@agentshouse/mdruntime 0.1.0-c2c52158b7381 → 0.1.0-c2e8a6f99866d

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 (97) hide show
  1. package/AGENTS.md +14 -10
  2. package/dist/adapter.d.ts +14 -5
  3. package/dist/adapter.d.ts.map +1 -1
  4. package/dist/bootstrap.d.ts +13 -6
  5. package/dist/bootstrap.d.ts.map +1 -1
  6. package/dist/bootstrap.js +86 -88
  7. package/dist/chain.d.ts +11 -0
  8. package/dist/chain.d.ts.map +1 -0
  9. package/dist/chain.js +64 -0
  10. package/dist/change-set.d.ts +5 -9
  11. package/dist/change-set.d.ts.map +1 -1
  12. package/dist/change-set.js +10 -86
  13. package/dist/chat/list.d.ts.map +1 -1
  14. package/dist/chat/list.js +1 -1
  15. package/dist/chat/message.d.ts.map +1 -1
  16. package/dist/chat/message.js +8 -1
  17. package/dist/chat/messages.d.ts +1 -0
  18. package/dist/chat/messages.d.ts.map +1 -1
  19. package/dist/chat/messages.js +9 -1
  20. package/dist/chat/provider.d.ts +2 -0
  21. package/dist/chat/provider.d.ts.map +1 -1
  22. package/dist/chat/provider.js +17 -1
  23. package/dist/client.d.ts +1 -0
  24. package/dist/client.d.ts.map +1 -1
  25. package/dist/client.js +1 -0
  26. package/dist/collection.d.ts +5 -19
  27. package/dist/collection.d.ts.map +1 -1
  28. package/dist/collection.js +22 -360
  29. package/dist/compile.d.ts +11 -0
  30. package/dist/compile.d.ts.map +1 -0
  31. package/dist/compile.js +712 -0
  32. package/dist/connection.d.ts +2 -2
  33. package/dist/connection.d.ts.map +1 -1
  34. package/dist/connection.js +1396 -624
  35. package/dist/conversation.d.ts +1 -1
  36. package/dist/conversation.d.ts.map +1 -1
  37. package/dist/conversation.js +26 -17
  38. package/dist/entry.d.ts +2 -1
  39. package/dist/entry.d.ts.map +1 -1
  40. package/dist/entry.js +17 -23
  41. package/dist/index.d.ts +1 -1
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/live.d.ts +22 -0
  44. package/dist/live.d.ts.map +1 -0
  45. package/dist/live.js +123 -0
  46. package/dist/memory.d.ts +1 -0
  47. package/dist/memory.d.ts.map +1 -1
  48. package/dist/memory.js +12 -0
  49. package/dist/observation.d.ts +12 -0
  50. package/dist/observation.d.ts.map +1 -1
  51. package/dist/observation.js +84 -0
  52. package/dist/overlay.d.ts +0 -3
  53. package/dist/overlay.d.ts.map +1 -1
  54. package/dist/overlay.js +1 -151
  55. package/dist/reach.d.ts.map +1 -1
  56. package/dist/reach.js +15 -35
  57. package/dist/reads.js +1 -1
  58. package/dist/refresh.d.ts +10 -13
  59. package/dist/refresh.d.ts.map +1 -1
  60. package/dist/refresh.js +74 -70
  61. package/dist/replica.d.ts +55 -38
  62. package/dist/replica.d.ts.map +1 -1
  63. package/dist/replica.js +273 -214
  64. package/dist/rows.d.ts +29 -31
  65. package/dist/rows.d.ts.map +1 -1
  66. package/dist/rows.js +113 -69
  67. package/dist/save.d.ts +9 -10
  68. package/dist/save.d.ts.map +1 -1
  69. package/dist/save.js +59 -57
  70. package/dist/search.d.ts +2 -2
  71. package/dist/search.d.ts.map +1 -1
  72. package/dist/search.js +60 -28
  73. package/dist/shared.d.ts +0 -3
  74. package/dist/shared.d.ts.map +1 -1
  75. package/dist/shared.js +1 -31
  76. package/dist/store.d.ts +79 -62
  77. package/dist/store.d.ts.map +1 -1
  78. package/dist/store.js +164 -330
  79. package/dist/stored.d.ts +143 -0
  80. package/dist/stored.d.ts.map +1 -0
  81. package/dist/stored.js +561 -0
  82. package/dist/tables.d.ts +246 -168
  83. package/dist/tables.d.ts.map +1 -1
  84. package/dist/tables.js +156 -25
  85. package/dist/turn.d.ts +1 -1
  86. package/dist/turn.d.ts.map +1 -1
  87. package/dist/turn.js +28 -20
  88. package/dist/validation.d.ts +30 -0
  89. package/dist/validation.d.ts.map +1 -0
  90. package/dist/validation.js +162 -0
  91. package/dist/windows.d.ts +2 -1
  92. package/dist/windows.d.ts.map +1 -1
  93. package/dist/windows.js +30 -25
  94. package/package.json +2 -3
  95. package/dist/read-side.d.ts +0 -27
  96. package/dist/read-side.d.ts.map +0 -1
  97. package/dist/read-side.js +0 -1179
package/dist/tables.js CHANGED
@@ -1,3 +1,24 @@
1
+ import { answerQuery } from './compile.js';
2
+ import { abandon, release, commit, counts, ended, entries, erase, fenceRooms, files, opened, rederive, referrers, refreshing, REPLICA_TABLES, searched, stage, typed, typedWithin, unrefresh, walk, } from './stored.js';
3
+ export function sqliteConnection(engine, database) {
4
+ const decoder = new TextDecoder();
5
+ return {
6
+ run(sql, bind = []) {
7
+ database.exec({ sql, bind });
8
+ },
9
+ rows(sql, bind = []) {
10
+ return database.exec({ sql, bind, rowMode: 'object', returnValue: 'resultRows' });
11
+ },
12
+ collate(name, compare) {
13
+ const answered = engine.capi.sqlite3_create_collation_v2(database.pointer, name, engine.capi.SQLITE_UTF8, 0, (_argument, leftLength, left, rightLength, right) => {
14
+ const heap = engine.wasm.heap8u();
15
+ return Math.sign(compare(decoder.decode(heap.subarray(left, left + leftLength)), decoder.decode(heap.subarray(right, right + rightLength))));
16
+ }, 0);
17
+ if (answered !== 0)
18
+ throw new Error(`the database refused the collation ${name}`);
19
+ },
20
+ };
21
+ }
1
22
  export const FILE_PART_BYTES = 1_048_576;
2
23
  const TABLES = [
3
24
  ['authored_facts', 'name TEXT PRIMARY KEY, value TEXT NOT NULL'],
@@ -19,6 +40,7 @@ export const RUNTIME_TABLES = [
19
40
  'CREATE TABLE file_parts (key TEXT NOT NULL, part INTEGER NOT NULL, bytes BLOB NOT NULL, PRIMARY KEY (key, part))',
20
41
  'CREATE TABLE opened_files (key TEXT PRIMARY KEY, locator TEXT NOT NULL, path TEXT NOT NULL, revision TEXT NOT NULL, media_type TEXT NOT NULL, sha256 TEXT, byte_length INTEGER) WITHOUT ROWID',
21
42
  'CREATE INDEX opened_files_name ON opened_files (locator, path, revision)',
43
+ ...REPLICA_TABLES,
22
44
  ];
23
45
  const OWNERS = ['authored_pending', 'authored_room_drafts', 'authored_calls'];
24
46
  const AUTHORED_KEYS = OWNERS.map((table) => `SELECT value FROM ${table}, json_each(${table}.files)`).join(' UNION ');
@@ -31,22 +53,43 @@ const READS = new Set([
31
53
  'authored.fences',
32
54
  'authored.fact',
33
55
  'authored.file',
56
+ 'replica.open',
57
+ 'replica.query',
58
+ 'replica.entries',
59
+ 'replica.typed',
60
+ 'replica.referrers',
61
+ 'replica.walk',
62
+ 'replica.files',
63
+ 'replica.typedWithin',
64
+ 'replica.counts',
65
+ 'replica.searched',
66
+ 'replica.received',
67
+ 'replica.receivedParts',
34
68
  'opened.find',
35
69
  'file.part',
36
70
  ]);
37
71
  const KINDS = new Set([
38
72
  ...READS,
39
- 'authored.keep',
40
73
  'authored.amend',
41
74
  'authored.roomDraft',
42
75
  'authored.chatDraft',
43
76
  'authored.call',
44
- 'authored.fence',
45
- 'authored.unfence',
46
- 'authored.discard',
47
77
  'authored.remember',
48
78
  'authored.claim',
49
79
  'authored.end',
80
+ 'replica.stage',
81
+ 'replica.refresh',
82
+ 'replica.unrefresh',
83
+ 'replica.commit',
84
+ 'replica.fence',
85
+ 'replica.erase',
86
+ 'replica.abandon',
87
+ 'replica.release',
88
+ 'replica.rederive',
89
+ 'replica.fact',
90
+ 'replica.present',
91
+ 'replica.receive',
92
+ 'replica.unreceive',
50
93
  'opened.begin',
51
94
  'opened.part',
52
95
  'opened.keep',
@@ -143,7 +186,10 @@ function heldOrder(connection, locator) {
143
186
  const [held] = connection.rows('SELECT ids FROM authored_queues WHERE locator = ?', [locator]);
144
187
  return held === undefined ? [] : JSON.parse(text(held.ids));
145
188
  }
146
- function keptRows(connection, { locator, order, added, founded, roomDrafts, consumedRoomDrafts }) {
189
+ function keptRows(connection, { locator, order, added, founded, roomDrafts, consumedRoomDrafts, amendments }) {
190
+ for (const { id, amendment } of amendments ?? []) {
191
+ amended(connection, { kind: 'authored.amend', locator, id, amendment });
192
+ }
147
193
  for (const path of consumedRoomDrafts ?? []) {
148
194
  connection.run('DELETE FROM authored_room_drafts WHERE locator = ? AND path = ?', [
149
195
  locator,
@@ -176,12 +222,13 @@ function keptRows(connection, { locator, order, added, founded, roomDrafts, cons
176
222
  connection.run('INSERT OR REPLACE INTO authored_pending (locator, id, batch, state, files) VALUES (?, ?, ?, ?, ?)', [locator, id, batch, state, owned(files)]);
177
223
  }
178
224
  for (const { id, batch, files } of founded ?? []) {
179
- if (!before.has(id))
225
+ const [held] = connection.rows('SELECT files FROM authored_pending WHERE locator = ? AND id = ?', [locator, id]);
226
+ if (held === undefined || !before.has(id))
180
227
  continue;
181
- connection.run('UPDATE authored_pending SET batch = ?, files = ? WHERE locator = ? AND id = ?', [batch, owned(files), locator, id]);
228
+ connection.run('UPDATE authored_pending SET batch = ?, files = ? WHERE locator = ? AND id = ?', [batch, owned([...JSON.parse(text(held.files)), ...files]), locator, id]);
182
229
  }
183
230
  }
184
- async function kept(connection, { writes, files, whole }) {
231
+ async function kept(connection, { writes, files, whole, discards }) {
185
232
  const refused = writes.find(({ locator }) => fenced(connection, locator));
186
233
  if (refused !== undefined && whole)
187
234
  return refused.locator;
@@ -190,6 +237,8 @@ async function kept(connection, { writes, files, whole }) {
190
237
  if (!fenced(connection, write.locator))
191
238
  keptRows(connection, write);
192
239
  }
240
+ for (const locator of discards ?? [])
241
+ roomWork(connection, locator);
193
242
  unowned(connection);
194
243
  return refused?.locator ?? null;
195
244
  }
@@ -256,18 +305,14 @@ function fence(connection, locators) {
256
305
  connection.run('DELETE FROM opened_files WHERE locator = ?', [locator]);
257
306
  }
258
307
  unowned(connection);
259
- return null;
308
+ return fenceRooms(connection, locators);
260
309
  }
261
- function unfence(connection, locators) {
262
- for (const locator of locators) {
310
+ function erased(connection, { locator, limit }) {
311
+ const done = erase(connection, locator, limit);
312
+ if (locator !== null && !done.remaining) {
263
313
  connection.run('DELETE FROM authored_fences WHERE locator = ?', [locator]);
264
314
  }
265
- return null;
266
- }
267
- function discard(connection, locator) {
268
- roomWork(connection, locator);
269
- unowned(connection);
270
- return null;
315
+ return done;
271
316
  }
272
317
  function claim(connection, holder) {
273
318
  const held = fact(connection, 'holder');
@@ -277,6 +322,7 @@ function claim(connection, holder) {
277
322
  return true;
278
323
  }
279
324
  function end(connection, holder) {
325
+ ended(connection);
280
326
  if (holder !== null && claim(connection, holder))
281
327
  return true;
282
328
  for (const [table] of TABLES)
@@ -350,6 +396,38 @@ function cleaned(connection) {
350
396
  unowned(connection);
351
397
  return count(freed?.bytes);
352
398
  }
399
+ async function committed(connection, { authored, rooms }) {
400
+ const refused = authored === undefined ? null : await kept(connection, authored);
401
+ if (refused !== null && authored?.whole === true) {
402
+ return { fenced: refused, counts: {}, changed: {}, prepared: {} };
403
+ }
404
+ return {
405
+ fenced: refused,
406
+ ...commit(connection, rooms.filter(({ locator }) => !fenced(connection, locator))),
407
+ };
408
+ }
409
+ function receivedParts(connection, key, count) {
410
+ return connection
411
+ .rows('SELECT value FROM replica_received_parts WHERE key = ? AND part < ? ORDER BY part', [
412
+ key,
413
+ count,
414
+ ])
415
+ .map((row) => JSON.parse(text(row.value)));
416
+ }
417
+ function receive(connection, { key, head, part }) {
418
+ connection.run('INSERT OR REPLACE INTO replica_received (key, head) VALUES (?, ?)', [key, head]);
419
+ if (part !== undefined) {
420
+ connection.run('INSERT OR REPLACE INTO replica_received_parts (key, part, value) VALUES (?, ?, ?)', [key, part.index, part.value]);
421
+ }
422
+ return null;
423
+ }
424
+ function unreceive(connection, keys) {
425
+ for (const key of keys) {
426
+ connection.run('DELETE FROM replica_received WHERE key = ?', [key]);
427
+ connection.run('DELETE FROM replica_received_parts WHERE key = ?', [key]);
428
+ }
429
+ return null;
430
+ }
353
431
  export function answerRuntime(connection, request, spool) {
354
432
  switch (request.kind) {
355
433
  case 'authored.queues':
@@ -366,8 +444,6 @@ export function answerRuntime(connection, request, spool) {
366
444
  return fact(connection, request.name);
367
445
  case 'authored.file':
368
446
  return spooled(connection, spool, request);
369
- case 'authored.keep':
370
- return kept(connection, request);
371
447
  case 'authored.amend':
372
448
  return amended(connection, request);
373
449
  case 'authored.roomDraft':
@@ -376,18 +452,73 @@ export function answerRuntime(connection, request, spool) {
376
452
  return chatDraft(connection, request);
377
453
  case 'authored.call':
378
454
  return call(connection, request);
379
- case 'authored.fence':
380
- return fence(connection, request.locators);
381
- case 'authored.unfence':
382
- return unfence(connection, request.locators);
383
- case 'authored.discard':
384
- return discard(connection, request.locator);
385
455
  case 'authored.remember':
386
456
  return remember(connection, request.name, request.value);
387
457
  case 'authored.claim':
388
458
  return claim(connection, request.holder);
389
459
  case 'authored.end':
390
460
  return end(connection, request.holder);
461
+ case 'replica.open':
462
+ return opened(connection);
463
+ case 'replica.query':
464
+ return answerQuery(connection, request.plan, request.rooms, request.budget);
465
+ case 'replica.entries':
466
+ return entries(connection, request.key, request.view, request.paths);
467
+ case 'replica.typed':
468
+ return typed(connection, request.key, request.view, request.paths);
469
+ case 'replica.referrers':
470
+ return referrers(connection, request.key, request.view, request.targets, request.kinds);
471
+ case 'replica.walk':
472
+ return walk(connection, request.key, request.view, request.after, request.bytes);
473
+ case 'replica.files':
474
+ return files(connection, request.key, request.view, request.after, request.limit);
475
+ case 'replica.typedWithin':
476
+ return typedWithin(connection, request.key, request.view, request.roots, request.within);
477
+ case 'replica.counts':
478
+ return counts(connection, request.rooms);
479
+ case 'replica.searched':
480
+ return searched(connection, request.key, request.locator, request.after, request.limit, request.paths);
481
+ case 'replica.received':
482
+ return connection.rows('SELECT key, head FROM replica_received').map((row) => ({
483
+ key: text(row.key),
484
+ head: JSON.parse(text(row.head)),
485
+ }));
486
+ case 'replica.receivedParts':
487
+ return receivedParts(connection, request.key, request.count);
488
+ case 'replica.stage':
489
+ return fenced(connection, request.locator) ? null : stage(connection, request);
490
+ case 'replica.refresh':
491
+ return refreshing(connection, request);
492
+ case 'replica.unrefresh':
493
+ return unrefresh(connection, request.key);
494
+ case 'replica.commit':
495
+ return committed(connection, request);
496
+ case 'replica.fence':
497
+ return fence(connection, request.locators);
498
+ case 'replica.erase':
499
+ return erased(connection, request);
500
+ case 'replica.abandon':
501
+ return abandon(connection, request.locator, request.kept);
502
+ case 'replica.release':
503
+ return release(connection, request.key);
504
+ case 'replica.rederive':
505
+ return rederive(connection, request.key, request.limit);
506
+ case 'replica.fact':
507
+ connection.run('INSERT OR REPLACE INTO replica_facts (name, value) VALUES (?, ?)', [
508
+ request.name,
509
+ request.value,
510
+ ]);
511
+ return null;
512
+ case 'replica.present':
513
+ connection.run('INSERT OR REPLACE INTO replica_presentation (app, value) VALUES (?, ?)', [
514
+ request.app,
515
+ request.value,
516
+ ]);
517
+ return null;
518
+ case 'replica.receive':
519
+ return receive(connection, request);
520
+ case 'replica.unreceive':
521
+ return unreceive(connection, request.keys);
391
522
  case 'opened.find':
392
523
  return openedFile(connection, request);
393
524
  case 'opened.begin':
package/dist/turn.d.ts CHANGED
@@ -2,8 +2,8 @@ export declare function yielded(): Promise<void>;
2
2
  export declare const ROW_WEIGHT = 256;
3
3
  export type Later<T> = T | Promise<T>;
4
4
  export declare function after<T, R>(value: Later<T>, next: (value: T) => Later<R>): Later<R>;
5
+ export declare function paced(): (weight: number) => Later<void>;
5
6
  export declare function inUnits<T>(items: Iterable<T>, each: (item: T) => number, ended?: () => void, paused?: () => Promise<void>): Later<void>;
6
7
  export declare function finished<R>(steps: Generator<number, R, void>): R;
7
8
  export declare function stepped<R>(steps: Generator<number, R, void>): Later<R>;
8
- export declare function inChunks<T>(items: Iterable<T>, weigh: (item: T) => number, each: (chunk: readonly T[]) => Later<void>): Promise<void>;
9
9
  //# sourceMappingURL=turn.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"turn.d.ts","sourceRoot":"","sources":["../src/turn.ts"],"names":[],"mappings":"AAAA,wBAAgB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CASvC;AAID,eAAO,MAAM,UAAU,MAAM,CAAC;AAE9B,MAAM,MAAM,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAEtC,wBAAgB,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAEnF;AAqBD,wBAAgB,OAAO,CAAC,CAAC,EACvB,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAClB,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,EACzB,KAAK,GAAE,MAAM,IAAe,EAC5B,MAAM,GAAE,MAAM,OAAO,CAAC,IAAI,CAAW,GACpC,KAAK,CAAC,IAAI,CAAC,CAWb;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAIhE;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAStE;AAED,wBAAsB,QAAQ,CAAC,CAAC,EAC9B,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAClB,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,EAC1B,IAAI,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,KAAK,KAAK,CAAC,IAAI,CAAC,GACzC,OAAO,CAAC,IAAI,CAAC,CAWf"}
1
+ {"version":3,"file":"turn.d.ts","sourceRoot":"","sources":["../src/turn.ts"],"names":[],"mappings":"AAAA,wBAAgB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CASvC;AAOD,eAAO,MAAM,UAAU,MAAM,CAAC;AAE9B,MAAM,MAAM,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAEtC,wBAAgB,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAEnF;AAmBD,wBAAgB,KAAK,IAAI,CAAC,MAAM,EAAE,MAAM,KAAK,KAAK,CAAC,IAAI,CAAC,CAGvD;AAsBD,wBAAgB,OAAO,CAAC,CAAC,EACvB,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAClB,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,EACzB,KAAK,GAAE,MAAM,IAAe,EAC5B,MAAM,GAAE,MAAM,OAAO,CAAC,IAAI,CAAW,GACpC,KAAK,CAAC,IAAI,CAAC,CAUb;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAIhE;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAStE"}
package/dist/turn.js CHANGED
@@ -9,31 +9,51 @@ export function yielded() {
9
9
  });
10
10
  }
11
11
  const UNIT = 1 << 20;
12
+ // A unit of runtime work ends within one 60 Hz frame so a port's next message waits at most a frame.
13
+ const SLICE_MS = 16;
12
14
  export const ROW_WEIGHT = 256;
13
15
  export function after(value, next) {
14
16
  return value instanceof Promise ? value.then(next) : next(value);
15
17
  }
18
+ function spending() {
19
+ let spent = 0;
20
+ let since = performance.now();
21
+ return {
22
+ spend(weight) {
23
+ spent += weight;
24
+ return spent >= UNIT || performance.now() - since >= SLICE_MS;
25
+ },
26
+ restart() {
27
+ spent = 0;
28
+ since = performance.now();
29
+ },
30
+ };
31
+ }
32
+ export function paced() {
33
+ const spent = spending();
34
+ return (weight) => (spent.spend(weight) ? yielded().then(() => spent.restart()) : undefined);
35
+ }
16
36
  async function resumed(iterator, each, ended, paused) {
17
- for (let spent = UNIT;;) {
18
- if (spent >= UNIT) {
37
+ const spent = spending();
38
+ for (let due = true;;) {
39
+ if (due) {
19
40
  await paused();
20
- spent = 0;
41
+ spent.restart();
21
42
  }
22
43
  const next = iterator.next();
23
44
  if (next.done === true)
24
45
  break;
25
- spent += each(next.value);
26
- if (spent >= UNIT)
46
+ due = spent.spend(each(next.value));
47
+ if (due)
27
48
  ended();
28
49
  }
29
50
  ended();
30
51
  }
31
52
  export function inUnits(items, each, ended = () => { }, paused = yielded) {
32
53
  const iterator = items[Symbol.iterator]();
33
- let spent = 0;
54
+ const spent = spending();
34
55
  for (let next = iterator.next(); next.done !== true; next = iterator.next()) {
35
- spent += each(next.value);
36
- if (spent >= UNIT) {
56
+ if (spent.spend(each(next.value))) {
37
57
  ended();
38
58
  return resumed(iterator, each, ended, paused);
39
59
  }
@@ -53,15 +73,3 @@ export function stepped(steps) {
53
73
  };
54
74
  return after(inUnits(driven(), (weight) => weight), () => result);
55
75
  }
56
- export async function inChunks(items, weigh, each) {
57
- const iterator = items[Symbol.iterator]();
58
- for (let next = iterator.next(); next.done !== true;) {
59
- await yielded();
60
- const chunk = [];
61
- for (let spent = 0; next.done !== true && spent < UNIT; next = iterator.next()) {
62
- chunk.push(next.value);
63
- spent += weigh(next.value);
64
- }
65
- await each(chunk);
66
- }
67
- }
@@ -0,0 +1,30 @@
1
+ import { type Validation } from '@agentshouse/mdmodel';
2
+ import { type Composition } from './collection.js';
3
+ type Examined = {
4
+ readonly path: string;
5
+ readonly content: string | null;
6
+ };
7
+ export type Typed = {
8
+ readonly type: string | undefined;
9
+ readonly binary: boolean;
10
+ };
11
+ export type Walk = (after: string | null) => Promise<{
12
+ readonly entries: readonly Examined[];
13
+ readonly next: string | null;
14
+ }>;
15
+ export type Lookup = (paths: readonly string[]) => Promise<ReadonlyMap<string, Typed>>;
16
+ export declare function validatedCollection(walk: Walk, lookup: Lookup, composition: Composition): Promise<Validation>;
17
+ export type Changed = {
18
+ readonly path: string;
19
+ readonly was: string | null | undefined;
20
+ readonly now: string | null | undefined;
21
+ };
22
+ type CleanSource = {
23
+ readonly lookup: Lookup;
24
+ readonly texts: (paths: readonly string[]) => Promise<ReadonlyMap<string, string | null>>;
25
+ readonly referrers: ((targets: readonly string[]) => Promise<readonly string[]>) | null;
26
+ };
27
+ export declare function sameComposition(left: Composition, right: Composition): boolean;
28
+ export declare function changedCleanly(changes: readonly Changed[], previous: Composition, composition: Composition, source: CleanSource): Promise<boolean>;
29
+ export {};
30
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,UAAU,EAGhB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAMxE,KAAK,QAAQ,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAE3E,MAAM,MAAM,KAAK,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC;AAEpF,MAAM,MAAM,IAAI,GAAG,CACjB,KAAK,EAAE,MAAM,GAAG,IAAI,KACjB,OAAO,CAAC;IAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,QAAQ,EAAE,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAAC;AAEtF,MAAM,MAAM,MAAM,GAAG,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;AAyBvF,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,WAAW,GACvB,OAAO,CAAC,UAAU,CAAC,CAyCrB;AAED,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACzC,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;IAC1F,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;CACzF,CAAC;AAaF,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAQ9E;AAqCD,wBAAsB,cAAc,CAClC,OAAO,EAAE,SAAS,OAAO,EAAE,EAC3B,QAAQ,EAAE,WAAW,EACrB,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,OAAO,CAAC,CAsDlB"}
@@ -0,0 +1,162 @@
1
+ import { assess, checkDocument, parse, parseFrontmatter, resolves, verdicts, } from '@agentshouse/mdmodel';
2
+ import { governing, selected } from './collection.js';
3
+ import { sameTyping } from './model.js';
4
+ import { paced, ROW_WEIGHT, yielded } from './turn.js';
5
+ const NO_TYPES = { types: {} };
6
+ function modelOf(model) {
7
+ return model.status === 'unavailable' ? NO_TYPES : { types: model.types };
8
+ }
9
+ function typedText(content) {
10
+ const read = parse(content);
11
+ return read.ok && read.document.type !== undefined && read.document.type !== 'any';
12
+ }
13
+ function partVerdict(part) {
14
+ return part.unmodeled !== null
15
+ ? { verdict: 'refuse', rule: 'model-unavailable', file: part.unmodeled }
16
+ : part.judged.verdict();
17
+ }
18
+ export async function validatedCollection(walk, lookup, composition) {
19
+ const collection = { model: composition.model, judged: verdicts(), unmodeled: null };
20
+ const selection = composition.selection === null
21
+ ? null
22
+ : { model: composition.selection.model, judged: verdicts(), unmodeled: null };
23
+ const partOf = (path) => selection !== null && selected(composition, path) ? selection : collection;
24
+ const pace = paced();
25
+ for (let after = null;;) {
26
+ const { entries, next } = await walk(after);
27
+ const assessed = [];
28
+ for (const { path, content } of entries) {
29
+ if (content === null)
30
+ continue;
31
+ const part = partOf(path);
32
+ if (part.model.status === 'unavailable' && part.unmodeled === null && typedText(content)) {
33
+ part.unmodeled = path;
34
+ }
35
+ const assessment = assess(path, content, false, modelOf(part.model), { anyRoom: true });
36
+ assessed.push({ part, assessment });
37
+ await pace(content.length + assessment.targets.length * ROW_WEIGHT);
38
+ }
39
+ const targets = [...new Set(assessed.flatMap(({ assessment }) => assessment.targets))];
40
+ const known = await lookup(targets);
41
+ for (const { part, assessment } of assessed) {
42
+ const within = (path) => partOf(path) === part;
43
+ part.judged.add(assessment, (path) => (within(path) ? known.get(path)?.type : undefined), (path) => within(path) && known.get(path)?.binary === true);
44
+ await pace((assessment.targets.length + 1) * ROW_WEIGHT);
45
+ }
46
+ if (next === null)
47
+ break;
48
+ after = next;
49
+ await yielded();
50
+ }
51
+ const judged = [partVerdict(collection), ...(selection === null ? [] : [partVerdict(selection)])];
52
+ const refusals = judged.filter((verdict) => verdict.verdict === 'refuse');
53
+ if (refusals.length === 0)
54
+ return judged[0];
55
+ return refusals.find((refusal) => refusal.rule !== 'model-unavailable') ?? refusals[0];
56
+ }
57
+ function declared(content) {
58
+ if (content === undefined || content === null)
59
+ return undefined;
60
+ const read = parseFrontmatter(content);
61
+ return read.ok ? (read.frontmatter.type ?? 'any') : undefined;
62
+ }
63
+ function typedIn(content) {
64
+ const type = declared(content);
65
+ return type !== undefined && type !== 'any';
66
+ }
67
+ export function sameComposition(left, right) {
68
+ return (sameTyping(left.model, right.model) &&
69
+ (left.selection === null || right.selection === null
70
+ ? left.selection === right.selection
71
+ : sameTyping(left.selection.model, right.selection.model)) &&
72
+ JSON.stringify(left.selection?.roots ?? null) === JSON.stringify(right.selection?.roots ?? null));
73
+ }
74
+ function checked(path, content, composition) {
75
+ const model = governing(composition, path);
76
+ return checkDocument(path, content, modelOf(model), { anyRoom: true });
77
+ }
78
+ async function reachingAll(checks, composition, next) {
79
+ const targets = [
80
+ ...new Set(checks.flatMap(({ check }) => check.ok ? check.references.flatMap(({ target }) => (target === null ? [] : [target])) : [])),
81
+ ];
82
+ const known = await next(targets);
83
+ const pace = paced();
84
+ for (const { path, check } of checks) {
85
+ if (!check.ok)
86
+ return false;
87
+ const part = (other) => selected(composition, other) === selected(composition, path);
88
+ const resolved = check.references.every((reference) => resolves(reference, (other) => (part(other) ? known.get(other)?.type : undefined), (other) => part(other) && known.get(other)?.binary === true));
89
+ if (!resolved)
90
+ return false;
91
+ await pace((check.references.length + 1) * ROW_WEIGHT);
92
+ }
93
+ return true;
94
+ }
95
+ export async function changedCleanly(changes, previous, composition, source) {
96
+ if (!sameComposition(previous, composition))
97
+ return false;
98
+ const changedAt = new Map(changes.map((change) => [change.path, change]));
99
+ const next = async (paths) => {
100
+ const asked = paths.filter((path) => !changedAt.has(path));
101
+ const known = new Map(await source.lookup(asked));
102
+ for (const path of paths) {
103
+ const change = changedAt.get(path);
104
+ if (change === undefined)
105
+ continue;
106
+ if (change.now === undefined)
107
+ known.delete(path);
108
+ else
109
+ known.set(path, { type: declared(change.now), binary: change.now === null });
110
+ }
111
+ return known;
112
+ };
113
+ const shifted = [];
114
+ const checks = [];
115
+ const pace = paced();
116
+ for (const { path, was, now } of changes) {
117
+ await pace((now?.length ?? 0) + (was?.length ?? 0) + ROW_WEIGHT);
118
+ const modeled = governing(composition, path).status !== 'unavailable';
119
+ if ((now === undefined) !== (was === undefined) ||
120
+ declared(now) !== declared(was) ||
121
+ (now === null) !== (was === null)) {
122
+ shifted.push(path);
123
+ }
124
+ if (!modeled && typedIn(was) && !typedIn(now))
125
+ return false;
126
+ if (now === undefined)
127
+ continue;
128
+ if (now === null)
129
+ return false;
130
+ if (!modeled) {
131
+ if (declared(now) === undefined)
132
+ return false;
133
+ if (typedIn(now))
134
+ continue;
135
+ const check = checked(path, now, composition);
136
+ if (!check.ok)
137
+ return false;
138
+ checks.push({ path, check });
139
+ continue;
140
+ }
141
+ checks.push({ path, check: checked(path, now, composition) });
142
+ }
143
+ if (!(await reachingAll(checks, composition, next)))
144
+ return false;
145
+ if (shifted.length === 0)
146
+ return true;
147
+ if (source.referrers === null)
148
+ return false;
149
+ const files = (await source.referrers(shifted)).filter((file) => !changedAt.has(file));
150
+ if (files.length === 0)
151
+ return true;
152
+ const texts = await source.texts(files);
153
+ const rechecked = [];
154
+ for (const file of files) {
155
+ const content = texts.get(file);
156
+ if (content === undefined || content === null)
157
+ return false;
158
+ await pace(content.length + ROW_WEIGHT);
159
+ rechecked.push({ path: file, check: checked(file, content, composition) });
160
+ }
161
+ return reachingAll(rechecked, composition, next);
162
+ }
package/dist/windows.d.ts CHANGED
@@ -14,7 +14,8 @@ interface Placed {
14
14
  }
15
15
  export declare function messageOrder(left: Placed, right: Placed): number;
16
16
  export declare function precedes(message: Placed, before: readonly [string, string] | null): boolean;
17
- export declare function beyond(values: readonly unknown[], cursor: readonly unknown[], orderings: readonly Ordering[]): boolean;
17
+ export declare function byCodePoint(left: string, right: string): number;
18
+ export declare function compared(left: unknown, right: unknown, ordering: Ordering): number;
18
19
  export declare function cursorOf(values: readonly unknown[]): string;
19
20
  export declare function decodedCursor(text: string): readonly unknown[] | null;
20
21
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"windows.d.ts","sourceRoot":"","sources":["../src/windows.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,OAAO,CAAC;AAEnC,eAAO,MAAM,UAAU,OAAO,CAAC;AAE/B,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAE/D;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,SAAS,EAAE,KAAK,GAAG,MAAM,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,aAAa,EAAE,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;CAC1D,CAAC;AAEF,UAAU,MAAM;IACd,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAIhE;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,GAAG,OAAO,CAE3F;AA0CD,wBAAgB,MAAM,CACpB,MAAM,EAAE,SAAS,OAAO,EAAE,EAC1B,MAAM,EAAE,SAAS,OAAO,EAAE,EAC1B,SAAS,EAAE,SAAS,QAAQ,EAAE,GAC7B,OAAO,CAMT;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,CAE3D;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,OAAO,EAAE,GAAG,IAAI,CAOrE"}
1
+ {"version":3,"file":"windows.d.ts","sourceRoot":"","sources":["../src/windows.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,OAAO,CAAC;AAEnC,eAAO,MAAM,UAAU,OAAO,CAAC;AAE/B,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAE/D;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,SAAS,EAAE,KAAK,GAAG,MAAM,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,aAAa,EAAE,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;CAC1D,CAAC;AAEF,UAAU,MAAM;IACd,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAIhE;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,GAAG,OAAO,CAE3F;AAaD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAO/D;AA4BD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAMlF;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,CAE3D;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,OAAO,EAAE,GAAG,IAAI,CAOrE"}