libcosense
https://jsr.io/@bsahd/libcosense
https://github.com/bsahd/libcosense/pkgs/npm/libcosense
https://github.com/bsahd/libcosense
目標
100%クラス指向のcosense rest/websocket wrapper
ブラウザ/deno/node/bunの全てで動作
主に非ブラウザ向け
数々の便利機能
全ページ一覧の非同期イテレーターとか
セキュリティ重視
依存関係なし
scrapbox-userscript-stdよく使われる関数を集めるというボトムアップ
こっちはトップダウンで一貫した設計にする
今後
CosenseClientをsingletonにすべきだろうか
roadmap for 1.5
型安全性をどうにかする
非nullアサーションを消す
fetchで帰ってきたオブジェクトをしっかりとバリデーションする
どれか1つが失敗すればすぐ例外行き
オブジェクトはキーと型を定義して適当にバリデートさせる...?
全APIのバリデーションを書く
Projectクラス OK
PageListItem OK
LatestPages OK
SearchResult OK
Page OK
すっ飛ばして2.0いく
そういえばまだログイン状態のテストしてなかった
ログイン時の挙動がようやくまともになった
これまではCookie設定がエラーになってた
1.4.1
CosenseClientにgetProjectを生やし、これまでできなかった既存のクライアントを使ってプロジェクトを作成することができるように
というかそっちであるべき
Project.new()をdeprecatedにした
https://gyazo.com/f6d4ae1b84e08ef7f74ca2b746cf077d
コメント率高い
ちょうどCosenseClientというクラスを@cosense/stdで実装しようとしてたところtakker.icon
名前被っちゃった
まあimport {CosenseClient as なんとか}すればいいしbsahd.icon
2025/03/15(1.3.2)
JSR Score 100%達成
1.3
APIのベースURLを設定できるようにした
オンプレ版対応のため
related pagesにgetDetailを生やした
1.2
継承をコンポジションにした
そうか...非同期constructorないのか...
でもconstructorをprivateにして別のnew静的メソッドを作ればいい!
Project.new()みたいにね
たぶんこれでもいけるtakker.icon
code:ts
class Class {
constructor(private value: unknown) {};
}
export const create = async (): Promise<Class> => Promise.resolve(new Class(Math.random()));
export type { Class };
Classの型定義だけexportして実体がexportされないので、外部でnew Classできなくなるはず
脱線: async関数内でPromise.resolve返すのは冗長では...bsahd.icon
async functionの例として適当に書いただけです。スルーしてくださいtakker.icon
手ごろなのがすぐ思いつかなかった
100%クラス指向と書いてるので、Java形式の厳格なオブジェクト指向にしたいという点もある
というか私が型定義がエクスポートされてなくて困ってたことがあるので、全クラスエクスポートしてるんですよね......
private-ref-type警告がでるやつだtakker.icon
(内部で使われている型を引き出すのがめんどいし冗長的になる)
直接の実体エクスポートはCosenseClientだけにして、他全ては型のみエクスポートにすべきかな〜
Project.new()形式のAPIとの互換性はどうすべきかな〜
というか結構雑なアサーションとか使いまくってるのでどうにかしたい
非nullアサーションが至るところで使われているのがわかるだろう
Cosense側が内部APIの仕様を変えたらすぐ型安全性が崩壊する
現状では新しいプロパティが生えることはあっても、すでにあるプロパティが消えることは珍しいかな
そもそも更新が古いscrapboxlab側の型定義を元にしてるので
あるべきものがあるか正しくバリデートして互換性がなくなったら例外を出すべきだ
1.5でバリデーション機能つけるか
プロパティの型があってなければ、例外を返す
予想外のプロパティがあるのは許容する
その頃にはstrictとnoImplicitAnyを付けたい
@cosense/typesとは微妙に型の互換性がない
こっちはscrapboxlab側の定義を元にしてるので
@cosense/stdの問題点
認証情報の取り回しがめんどくさい
どこが面倒かよくわからなかったtakker.icon
APIを呼ぶたびにオプションを設定する必要があるbsahd.icon
クラス指向なら一度認証を設定したProjectから短いコードでPageを読み書きできる
内部実装はバケツリレーだが
page listからpageを取得するのがめんどい
追加予定takker.icon
現状libcosenseだとPageListItemクラスにgetDetail()が生えててそれを呼ぶとPageクラスが帰ってくるbsahd.icon
全ページ一覧の取得がむずい
サンプル
Cosense Code Cloneで使ってる
minimum example
code:ts
import * as libcosense from "jsr:@bsahd/libcosense"
const pj = libcosense.Project.new("villagepump",{sessionid:"SESSION_ID"})
for await (const item of pj.pageList()) {
const page = await item.getDetail();
console.log(page)
}
これだけで全ページの内容を処理できる
ちなみに@cosense/stdでやろうとすると
code:ts
SESSIONID="SESSION_ID"
const pagecount = await cosenseStd.listPages("villagepump", {sid:SESSIONID}).count;
for (const skipnum=0;skipnum < pagecount;skipnum += 100) {
const pglistw = await cosenseStd.listPages("villagepump", {
skip: skipnum,
sort: "updated",
sid:SESSIONID
});
if (!pglistw.ok) {
continue;
}
for (const item of pglistw.pages) {
const page = await cosenseStd.getPage("villagepump", item,{sid:SESSIONID});
console.log(page);
}
}
前提
例外のタイプはダックタイピングでどうにかする
cf. Deno(Nodeでも)で外部モジュールのクラスやSymbolを使用するときは少し慎重になる必要がある
例外ベースに変えようかちょうど検討してたとこだったtakker.icon
単独のREST APIで構成されているものは例外ベースにする予定
https://github.com/takker99/scrapbox-userscript-std/pull/226
例外ベースにしたいというより、Responseを加工する処理を削って軽量化&Responseを直に使えるようにすることが目的
uploadToGCSなど、複数のAPIの組み合わせはResult typeかなー
@cosense/std側はそのままでこちら側を例外ベースにしたいかなbsahd.icon
目についた時にやりたいのでピン留め 3/11