@kanf/wema-kake 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +123 -2
  3. package/bin/wema-kake.mjs +220 -0
  4. package/dist/lib/index.d.ts +13 -0
  5. package/dist/lib/index.js +14 -0
  6. package/dist/lib/shared/api.d.ts +97 -0
  7. package/dist/lib/shared/api.js +23 -0
  8. package/dist/lib/shared/delta.d.ts +24 -0
  9. package/dist/lib/shared/delta.js +19 -0
  10. package/dist/lib/shared/guide.d.ts +32 -0
  11. package/dist/lib/shared/guide.js +259 -0
  12. package/dist/lib/shared/hierarchy.d.ts +31 -0
  13. package/dist/lib/shared/hierarchy.js +40 -0
  14. package/dist/lib/shared/i18n.d.ts +23 -0
  15. package/dist/lib/shared/i18n.js +58 -0
  16. package/dist/lib/shared/note-html.d.ts +10 -0
  17. package/dist/lib/shared/note-html.js +28 -0
  18. package/dist/lib/shared/note-info.d.ts +20 -0
  19. package/dist/lib/shared/note-info.js +29 -0
  20. package/dist/lib/shared/note-text.d.ts +11 -0
  21. package/dist/lib/shared/note-text.js +118 -0
  22. package/dist/lib/shared/op-summary.d.ts +30 -0
  23. package/dist/lib/shared/op-summary.js +37 -0
  24. package/dist/lib/shared/protocol.d.ts +74 -0
  25. package/dist/lib/shared/protocol.js +12 -0
  26. package/dist/lib/shared/site.d.ts +16 -0
  27. package/dist/lib/shared/site.js +28 -0
  28. package/dist/lib/shared/slug.d.ts +4 -0
  29. package/dist/lib/shared/slug.js +6 -0
  30. package/dist/lib/shared/tools.d.ts +67 -0
  31. package/dist/lib/shared/tools.js +353 -0
  32. package/dist/lib/worker/access.d.ts +34 -0
  33. package/dist/lib/worker/access.js +114 -0
  34. package/dist/lib/worker/apply-ops.d.ts +75 -0
  35. package/dist/lib/worker/apply-ops.js +335 -0
  36. package/dist/lib/worker/assets.d.ts +4 -0
  37. package/dist/lib/worker/assets.js +35 -0
  38. package/dist/lib/worker/config.d.ts +52 -0
  39. package/dist/lib/worker/config.js +23 -0
  40. package/dist/lib/worker/first-page.d.ts +10 -0
  41. package/dist/lib/worker/first-page.js +30 -0
  42. package/dist/lib/worker/images.d.ts +3 -0
  43. package/dist/lib/worker/images.js +54 -0
  44. package/dist/lib/worker/index.d.ts +8 -0
  45. package/dist/lib/worker/index.js +72 -0
  46. package/dist/lib/worker/indexer.d.ts +42 -0
  47. package/dist/lib/worker/indexer.js +93 -0
  48. package/dist/lib/worker/mcp/board-access.d.ts +28 -0
  49. package/dist/lib/worker/mcp/board-access.js +67 -0
  50. package/dist/lib/worker/mcp/server.d.ts +9 -0
  51. package/dist/lib/worker/mcp/server.js +39 -0
  52. package/dist/lib/worker/page-do.d.ts +330 -0
  53. package/dist/lib/worker/page-do.js +1168 -0
  54. package/dist/lib/worker/page-queries.d.ts +28 -0
  55. package/dist/lib/worker/page-queries.js +88 -0
  56. package/dist/lib/worker/pages-api.d.ts +5 -0
  57. package/dist/lib/worker/pages-api.js +257 -0
  58. package/dist/lib/worker/plain-text.d.ts +22 -0
  59. package/dist/lib/worker/plain-text.js +113 -0
  60. package/dist/lib/worker/revert.d.ts +21 -0
  61. package/dist/lib/worker/revert.js +126 -0
  62. package/dist/lib/worker/sanitize.d.ts +4 -0
  63. package/dist/lib/worker/sanitize.js +135 -0
  64. package/dist/lib/worker/validate.d.ts +21 -0
  65. package/dist/lib/worker/validate.js +218 -0
  66. package/dist/web/assets/index-CVwMafiH.js +5 -0
  67. package/dist/web/assets/index-Cgi5orcJ.css +1 -0
  68. package/dist/web/favicon.svg +13 -0
  69. package/dist/web/index.html +14 -0
  70. package/migrations/0001_init.sql +18 -0
  71. package/migrations/0002_fts.sql +9 -0
  72. package/migrations/0003_hierarchy.sql +9 -0
  73. package/migrations/0004_settings.sql +12 -0
  74. package/migrations/0005_bookmarks.sql +8 -0
  75. package/migrations/0006_page_color.sql +5 -0
  76. package/package.json +74 -4
  77. package/template/.dev.vars.example +4 -0
  78. package/template/README.md +55 -0
  79. package/template/gitignore +8 -0
  80. package/template/package.json +25 -0
  81. package/template/src/config.ts +18 -0
  82. package/template/src/index.ts +9 -0
  83. package/template/tsconfig.json +16 -0
  84. package/template/wrangler.jsonc +57 -0
@@ -0,0 +1,330 @@
1
+ import { DurableObject } from 'cloudflare:workers';
2
+ import type { WemaKakeEnv } from './config';
3
+ import { type OpSummary, type PageSummary, type RevertOutcome } from '../shared/api';
4
+ import { type BoardContent, type HistoryDelta, type Snapshot } from '../shared/delta';
5
+ import type { BoardState } from '../shared/tools';
6
+ import { type ChildRejectCode } from '../shared/hierarchy';
7
+ /** Worker が WebSocket の接続要求を DO へ転送するときに、認証済みの主体を入れるヘッダー */
8
+ export declare const ACTOR_HEADER = "X-Wema-Actor";
9
+ /** 同じく、認証の期限(UNIX 秒)を入れるヘッダー。期限がなければ付けない */
10
+ export declare const AUTH_EXPIRES_HEADER = "X-Wema-Auth-Expires";
11
+ export interface ApplyInput {
12
+ /** 'user:<email>' / 'agent:<client>'。呼び出し側(Worker)が認証結果から決める */
13
+ actor: string;
14
+ clientId: string;
15
+ opId: string;
16
+ /** 外から来た値をそのまま渡す。検証は applyOps が行う */
17
+ deltas: unknown;
18
+ summary?: string;
19
+ /** ページがまだ作られていなければ、適用せずに断る(MCP のツールはページを作らない) */
20
+ mustExist?: boolean;
21
+ }
22
+ /** ページがないときの `reason`。Worker は、これを 404 にする */
23
+ export declare const REASON_PAGE_NOT_FOUND = "page not found";
24
+ export interface ReceiveInput extends Pick<ApplyInput, 'actor' | 'clientId' | 'opId'> {
25
+ /** 置く付箋と、その間の接続線。外から来た値をそのまま渡す。検証は receiveNotes が行う */
26
+ notes: unknown;
27
+ edges: unknown;
28
+ /** 付箋が元あったページ(操作の履歴の要約に出す) */
29
+ from: string;
30
+ }
31
+ export type ApplyResult = {
32
+ ok: true;
33
+ seq: number;
34
+ /** 適用して記録したデルタ。全員に配信する。すべて捨てた場合は空で、seq は進まない */
35
+ deltas: HistoryDelta[];
36
+ /** 送信元だけが追加で適用するデルタ(サニタイズで変わった text など) */
37
+ fixups: HistoryDelta[];
38
+ /**
39
+ * 接続中のクライアントへ配信したか。配信しないのは、同じ opId の再送(適用済み)と、
40
+ * デルタをすべて捨てた場合。WebSocket の送信元には、このとき個別に確定を返す
41
+ */
42
+ broadcast: boolean;
43
+ } | {
44
+ ok: false;
45
+ reason: string;
46
+ current?: BoardContent;
47
+ };
48
+ export interface RevertInput {
49
+ /** 取り消す操作の seq */
50
+ seq: number;
51
+ /** 取り消しを行う主体。呼び出し側(Worker)が認証結果から決める */
52
+ actor: string;
53
+ clientId: string;
54
+ opId: string;
55
+ /** 自分(同じ主体)の操作だけを取り消せるようにする。MCP の revert_operation で使う */
56
+ ownOnly?: boolean;
57
+ /** 一覧に出す説明。省略すると、取り消しを表す符号(src/shared/op-summary.ts) */
58
+ summary?: string;
59
+ }
60
+ /**
61
+ * 取り消せなかった理由の分類。
62
+ * - not-found: 対象の操作がない(間引かれた場合を含む)
63
+ * - conflict: すでに取り消されている
64
+ * - forbidden: `ownOnly` で、他の主体の操作を指定した
65
+ * - invalid: 入力が不正
66
+ */
67
+ export type RevertFailure = 'not-found' | 'conflict' | 'forbidden' | 'invalid';
68
+ export type RevertResult = ({
69
+ ok: true;
70
+ } & RevertOutcome) | {
71
+ ok: false;
72
+ code: RevertFailure;
73
+ reason: string;
74
+ };
75
+ /** ページ 1 つ分の付箋・接続線・操作履歴を持つ */
76
+ export declare class PageDO extends DurableObject<WemaKakeEnv> {
77
+ /** 適用済みのスキーマのバージョン。0 は一度も書き込まれていないページ */
78
+ private version;
79
+ /** ページを削除するたびに増える。削除の前に受け付けた書き込みを、削除の後に実行しないために使う */
80
+ private generation;
81
+ /** 実行中、または順番を待っている削除の数 */
82
+ private deleting;
83
+ /** 今まさに子にしようとしているページ(adoptChildren の実行中)。互いを同時に子にするのを断る */
84
+ private adopting;
85
+ /** 処理中の applyOps。次の呼び出しはこれが終わってから始める */
86
+ private applyQueue;
87
+ constructor(ctx: DurableObjectState, env: WemaKakeEnv);
88
+ private get sql();
89
+ /** スキーマがある(version > 0)ときだけ呼ぶ */
90
+ private getMeta;
91
+ private setMeta;
92
+ private readVersion;
93
+ /** スキーマを最新にする。書き込みの前に呼ぶ */
94
+ private migrate;
95
+ /** 表示名、ページの色、epoch を伝えるメッセージ。スキーマがある(version > 0)ときだけ呼ぶ */
96
+ private metaJson;
97
+ /** スキーマがある(version > 0)ときだけ呼ぶ */
98
+ private getSeq;
99
+ getSnapshot(): Snapshot;
100
+ /** agent(MCP)に渡すボード。付箋には作成者が付く。ページがまだ作られていなければ null */
101
+ getBoardState(): BoardState | null;
102
+ /**
103
+ * 表示名を変える。空文字なら未設定に戻す(表示はスラッグになる)。
104
+ * 付箋のデルタではないので ops には記録しない。
105
+ *
106
+ * まだ作られていないページに呼ぶと、ページができる。`mustExist` なら、作らずに断る
107
+ * (一覧の画面からの変更。古い一覧に残っている削除済みのページを、作り直さないため)
108
+ */
109
+ setTitle(input: unknown, options?: {
110
+ mustExist?: boolean;
111
+ }): {
112
+ ok: true;
113
+ title: string | null;
114
+ } | {
115
+ ok: false;
116
+ reason: 'invalid title' | typeof REASON_PAGE_NOT_FOUND;
117
+ };
118
+ /**
119
+ * ページの色を変える。null なら、付けていない状態に戻す。表示名と同じく、ops には記録しない。
120
+ * まだ作られていないページと、削除の途中のページには、付けない(色を選んだだけで、ページを
121
+ * 作らない。削除されたページを、古いタブから作り直さない)
122
+ */
123
+ setColor(input: unknown, actor?: string): Promise<{
124
+ ok: true;
125
+ color: string | null;
126
+ } | {
127
+ ok: false;
128
+ reason: 'invalid color' | typeof REASON_PAGE_NOT_FOUND;
129
+ }>;
130
+ /** 親ページにある自分の付箋を、このページの色にする。失敗しても、例外にしない */
131
+ private recolorOwnNote;
132
+ /**
133
+ * 色を変える。親ページにある付箋の色は、変えない(`setColor` との違い)。親ページの DO が、
134
+ * 子ページの付箋の色を変えた後に、子ページへ呼ぶ(matchChildColors)。書き込みの順番待ちに入れない
135
+ */
136
+ setOwnColor(input: unknown): {
137
+ ok: true;
138
+ color: string | null;
139
+ } | {
140
+ ok: false;
141
+ reason: 'invalid color' | typeof REASON_PAGE_NOT_FOUND;
142
+ };
143
+ /**
144
+ * 親をたどるための情報と、付箋の数。ページがなければ null。
145
+ * 索引(D1)への反映を待たずに読めるので、作ったばかりのページの概要にも使う
146
+ */
147
+ getPageRef(): {
148
+ title: string | null;
149
+ color: string | null;
150
+ parent: string | null;
151
+ noteCount: number;
152
+ } | null;
153
+ /**
154
+ * ページの概要(子ページの付箋の表示に使うもの)。ページがなければ null。
155
+ * 索引(D1)への反映を待たないので、変更の直後でも、今の付箋の数と配置が入る
156
+ */
157
+ getSummary(): Omit<PageSummary, 'name'> | null;
158
+ /** このページを `parent` の子にする。すでに別の親があるか、輪になるなら、理由を返す。置けたら null */
159
+ setParent(parent: string): ChildRejectCode | null;
160
+ /** `parent` の子であれば、ルートへ戻す */
161
+ clearParent(parent: string): void;
162
+ /** このページのスラッグ */
163
+ private slug;
164
+ /** 先祖のページ(親、その親、…の順)。上限を超えてたどらない。パンくずと、輪の検査に使う */
165
+ ancestors(): Promise<{
166
+ name: string;
167
+ title: string | null;
168
+ }[]>;
169
+ /**
170
+ * `wanted`(新しく置こうとしている子ページ)を、このページの子にする。
171
+ * 置けないものが 1 つでもあれば、すべて取りやめて、理由を返す。置けたら null を返す
172
+ */
173
+ private adoptChildren;
174
+ /**
175
+ * このページを根とする部分木が、`levels` 段に収まるか。子ページの DO を順にたどるので、
176
+ * 直前の変更も反映される(D1 の索引は、変更の数秒後にしか反映されない)
177
+ */
178
+ fitsWithin(levels: number): Promise<boolean>;
179
+ /**
180
+ * `candidates` のうち、子ページの付箋がもうないページを、ルートへ戻す。
181
+ * 失敗しても、書き込みは取り消さない。食い違いは、次の索引の更新(reconcileChildren)で直す
182
+ */
183
+ private releaseOrphans;
184
+ /**
185
+ * 子ページの付箋の増減を伴う書き込みを行う。`run` の前に、新しく置く子ページを子にし、
186
+ * 後に、付箋がなくなった子ページをルートへ戻す。置けなければ、`run` を呼ばずに `rejected` を返す
187
+ */
188
+ private withChildren;
189
+ /**
190
+ * 子ページの付箋の色と、子ページの色をそろえる。`applied` は、適用して記録したデルタ
191
+ * (断られた変更と、今と同じ色への変更は、入っていない)。
192
+ *
193
+ * - 子ページの付箋の色を変えたら、子ページの色を、その色にする
194
+ * - 子ページの付箋を置いたら(Undo や取り消しで戻したときも)、付箋の色を、子ページの色にする。
195
+ * 子ページに色が付いていなければ、付箋の色のままにする
196
+ *
197
+ * 失敗しても、書き込みは取り消さない。食い違いは、子ページの次の索引の更新か、子ページの色の
198
+ * 選び直しで直る(どちらも、付箋を子ページの色に合わせる)
199
+ */
200
+ private matchChildColors;
201
+ /**
202
+ * 子ページ `page` の付箋の色を、子ページの今の色にする。付箋の更新として記録するので、
203
+ * 開いているブラウザにも届く。色は、順番待ちに入ってから、子ページへ聞く(呼び出しが前後しても、
204
+ * 最後に子ページの今の色になる)
205
+ *
206
+ * @param resetUnset 子ページに色が付いていないときに、付箋を既定の色へ戻すか
207
+ */
208
+ recolorChildNotes(page: string, actor: string, resetUnset: boolean): Promise<void>;
209
+ private recolorChildNotesInOrder;
210
+ /**
211
+ * ページを削除する前に、階層から外す。親ページにある自分の付箋を消し、子ページはルートへ戻す
212
+ * (子孫ごとの削除はしない)。失敗しても削除は続ける。残った食い違いは、付箋の側が
213
+ * 「削除済み」と表示され、親ページの索引の更新(reconcileChildren)でも直る
214
+ */
215
+ private detachFromHierarchy;
216
+ /**
217
+ * 子ページ `page` の付箋を、このページから消す(子ページが削除されたとき)。
218
+ * 付箋の削除として記録するので、開いているブラウザにも届く
219
+ */
220
+ removeChildNotes(page: string, actor: string): Promise<void>;
221
+ /**
222
+ * ページを新しく作る。すでにあれば何もしない(既存のページの表示名を書き換えない)。
223
+ * 付箋を書き込めばページはできるので、これは表示名だけのページを先に作るためのもの。
224
+ */
225
+ createPage(input: unknown): {
226
+ ok: true;
227
+ } | {
228
+ ok: false;
229
+ reason: 'exists' | 'invalid title';
230
+ };
231
+ /**
232
+ * ページを、付箋と接続線を置いた状態で作り、D1 の索引にも反映する(最初のページ用。作った直後の
233
+ * 一覧に出すため、索引への反映を待つ)。すでにあれば何もせず、false を返す
234
+ */
235
+ createWithContent(title: string, deltas: HistoryDelta[]): Promise<boolean>;
236
+ /**
237
+ * ページを削除する。付箋、接続線、履歴、表示名と、D1 の索引を消す。取り消しはできない。
238
+ * 貼った画像は R2 に残る(どのページの画像かを記録していない)。
239
+ */
240
+ deletePage(actor?: string): Promise<void>;
241
+ /**
242
+ * 書き込みを順番待ちに入れる。待っている間にページの削除が始まるか済んでいたら、
243
+ * 実行せずに `deleted` を返す。
244
+ */
245
+ private write;
246
+ /**
247
+ * 変更から少し後に、まとめて D1 へ反映する(書き込みごとには反映しない)。
248
+ * alarm が設定済みなら何もしない。alarm の実行中は未設定に見えるので、反映中に届いた変更は
249
+ * 次の alarm で反映される。
250
+ */
251
+ private scheduleIndexing;
252
+ /** 失敗して例外で終わると、ランタイムが間隔を空けて再実行する */
253
+ alarm(): Promise<void>;
254
+ /**
255
+ * D1 の索引へ反映する。書き込みや削除と同じ順番待ちに入れる(索引の書き込みが、ページの削除と
256
+ * 前後しないようにする)
257
+ */
258
+ private reindexInOrder;
259
+ /** 保持期間を過ぎた ops を消す。直近の分は期間を過ぎていても残す */
260
+ private pruneOps;
261
+ private reindex;
262
+ /**
263
+ * 親ページがもうなければ、ルートへ戻る。親ページの削除のときに、clearParent が届かなかった場合に
264
+ * 効く(そのままだと、存在しない親を指したままで、一覧に出ない)。次の索引の更新で、D1 にも反映する
265
+ */
266
+ private healParent;
267
+ /**
268
+ * 索引の上ではこのページの子なのに、子ページの付箋がないページを、ルートへ戻す。
269
+ * 付箋を消した後の clearParent が失敗した場合の、食い違いを直す
270
+ */
271
+ private reconcileChildren;
272
+ /**
273
+ * 唯一の書き込み口。ブラウザ(WebSocket)からも MCP からも、これを通して変更する。
274
+ *
275
+ * サニタイズは非同期で、かかる時間が text によって違う。待っている間に後続の呼び出しが
276
+ * 先に保存まで進むと、同じクライアントの操作が送信順に適用されなくなる(作成より先に
277
+ * その付箋の更新が処理されるなど)ので、呼び出しを 1 つずつ順に処理する。
278
+ */
279
+ applyOps(input: ApplyInput): Promise<ApplyResult>;
280
+ /**
281
+ * 他のページから移す(写す)付箋と、その間の接続線を受け取って置く。互いの位置関係は保ち、
282
+ * 今ある付箋の下に置く(送る側は、このページの付箋の位置を知らない)。
283
+ * ページがまだ作られていなければ、作らずに断る。
284
+ */
285
+ receiveNotes(input: ReceiveInput): Promise<ApplyResult>;
286
+ /** 前の書き込みが終わってから `run` を始める */
287
+ private inOrder;
288
+ /** 同じ送信元の同じ操作が記録済みなら、その行を返す */
289
+ private findOp;
290
+ /**
291
+ * 適用したデルタを ops に記録し、seq を進める。`transactionSync` の中で呼ぶこと。
292
+ * 戻り値の `deliver` は、トランザクションが確定してから呼ぶ(接続中のクライアントへの配信)。
293
+ */
294
+ private recordOp;
295
+ /**
296
+ * @param prepare 検証の済んだデルタを、適用する前に書き換える(今あるボードに合わせた位置決めなど)。
297
+ * 同じ操作の再送(適用済み)では、呼ばない
298
+ * @param matchColors 適用した後に、子ページの色と、その付箋の色をそろえる(matchChildColors)
299
+ */
300
+ private applyOpsInOrder;
301
+ /**
302
+ * 最近の操作を新しい順に返す。`agentOnly` なら agent(MCP 経由)の操作に、
303
+ * `actor` を指定するとその主体の操作に絞る。
304
+ */
305
+ listOps(options?: {
306
+ limit?: number;
307
+ agentOnly?: boolean;
308
+ actor?: string;
309
+ }): OpSummary[];
310
+ /**
311
+ * 記録済みの操作を取り消す。逆向きのデルタを新しい操作として適用する。
312
+ * その後に変更された付箋や接続線は取り消さず、`skipped` で返す(部分適用)。
313
+ */
314
+ revert(input: RevertInput): Promise<RevertResult>;
315
+ private revertInOrder;
316
+ /** Worker が認証を済ませ、主体を ACTOR_HEADER に入れて転送してくる */
317
+ fetch(request: Request): Promise<Response>;
318
+ webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): Promise<void>;
319
+ /** 接続直後の同期。`lastSeq` 以降の ops が残っていれば差分を、なければスナップショットを返す */
320
+ private onHello;
321
+ /** `lastSeq` 以降の変更を、差分(ops の再送)で返せるか */
322
+ private canReplay;
323
+ private onOps;
324
+ /**
325
+ * hello を済ませた全接続へ送る。`sender` を指定すると、その送信元の接続にだけ
326
+ * `toSender` を送る(null なら送らない)。clientId はクライアントの自己申告なので、
327
+ * 送信元かどうかは主体も合わせて判定する
328
+ */
329
+ private broadcast;
330
+ }