@agentshouse/mdruntime 0.1.0-ce531b51ff088 → 0.1.0-cebcd51f70ed1

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 (70) hide show
  1. package/AGENTS.md +57 -20
  2. package/README.md +10 -10
  3. package/dist/adapter.d.ts +52 -25
  4. package/dist/adapter.d.ts.map +1 -1
  5. package/dist/adapter.js +1 -9
  6. package/dist/attached-model.d.ts +11 -0
  7. package/dist/attached-model.d.ts.map +1 -0
  8. package/dist/attached-model.js +34 -0
  9. package/dist/bootstrap.d.ts +5 -3
  10. package/dist/bootstrap.d.ts.map +1 -1
  11. package/dist/bootstrap.js +3 -2
  12. package/dist/client.d.ts +3 -2
  13. package/dist/client.d.ts.map +1 -1
  14. package/dist/client.js +3 -10
  15. package/dist/connection.d.ts +10 -12
  16. package/dist/connection.d.ts.map +1 -1
  17. package/dist/connection.js +501 -512
  18. package/dist/conversation.d.ts +18 -6
  19. package/dist/conversation.d.ts.map +1 -1
  20. package/dist/conversation.js +179 -78
  21. package/dist/host.d.ts +7 -1
  22. package/dist/host.d.ts.map +1 -1
  23. package/dist/host.js +34 -31
  24. package/dist/index.d.ts +3 -3
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/memory.d.ts +11 -5
  27. package/dist/memory.d.ts.map +1 -1
  28. package/dist/memory.js +78 -26
  29. package/dist/model.d.ts +4 -11
  30. package/dist/model.d.ts.map +1 -1
  31. package/dist/model.js +4 -3
  32. package/dist/observation.d.ts +2 -0
  33. package/dist/observation.d.ts.map +1 -1
  34. package/dist/observation.js +24 -14
  35. package/dist/paths.d.ts +0 -1
  36. package/dist/paths.d.ts.map +1 -1
  37. package/dist/paths.js +0 -16
  38. package/dist/query.d.ts +8 -3
  39. package/dist/query.d.ts.map +1 -1
  40. package/dist/query.js +104 -36
  41. package/dist/reach.d.ts +40 -0
  42. package/dist/reach.d.ts.map +1 -0
  43. package/dist/reach.js +270 -0
  44. package/dist/react.d.ts +32 -0
  45. package/dist/react.d.ts.map +1 -0
  46. package/dist/react.js +62 -0
  47. package/dist/read-side.d.ts +4 -0
  48. package/dist/read-side.d.ts.map +1 -1
  49. package/dist/read-side.js +116 -54
  50. package/dist/refresh.d.ts +2 -1
  51. package/dist/refresh.d.ts.map +1 -1
  52. package/dist/refresh.js +7 -3
  53. package/dist/replica.d.ts +4 -4
  54. package/dist/replica.d.ts.map +1 -1
  55. package/dist/replica.js +12 -13
  56. package/dist/room-draft.d.ts +3 -0
  57. package/dist/room-draft.d.ts.map +1 -1
  58. package/dist/room-draft.js +6 -4
  59. package/dist/rows.d.ts +2 -1
  60. package/dist/rows.d.ts.map +1 -1
  61. package/dist/rows.js +75 -38
  62. package/dist/save.d.ts +1 -2
  63. package/dist/save.d.ts.map +1 -1
  64. package/dist/save.js +90 -48
  65. package/dist/store.d.ts +1 -1
  66. package/dist/store.d.ts.map +1 -1
  67. package/dist/wire.d.ts +51 -16
  68. package/dist/wire.d.ts.map +1 -1
  69. package/dist/wire.js +50 -59
  70. package/package.json +14 -2
package/dist/query.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { ARRAY_INDEX, itemFacts, memberFacts, pointerSegments, } from '@agentshouse/mdmodel';
2
+ import { referenceField } from './model.js';
1
3
  import { normalize } from './paths.js';
2
4
  export class QueryError extends Error {
3
5
  constructor(code, at, problem) {
@@ -11,6 +13,7 @@ const RELATED = ['types', 'where', 'pathPrefix', 'field'];
11
13
  const PARTS = ['fields', 'sections', 'preamble', 'markdown', 'backlinks', 'change'];
12
14
  const ORDER = ['by', 'direction', 'nulls', 'stringSort', 'locale', 'localeOptions'];
13
15
  const CALENDAR_DATE = /^(\d{4})-(\d{2})-(\d{2})$/;
16
+ const STRAY_ESCAPE = /~(?![01])/u;
14
17
  export function invalid(at, problem) {
15
18
  throw new QueryError('invalid-query', at, problem);
16
19
  }
@@ -68,23 +71,29 @@ function calendarDate(value) {
68
71
  const date = new Date(Date.UTC(year, month - 1, day));
69
72
  return (date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day);
70
73
  }
71
- function fits(spec, value) {
72
- const item = spec.kind === 'list' ? spec.of : spec;
73
- if (item.kind === 'number')
74
+ function itemOf(facts) {
75
+ return facts.type === 'array' ? facts.items : facts;
76
+ }
77
+ function fits(facts, value) {
78
+ const item = itemOf(facts);
79
+ if (item?.type === undefined)
80
+ return true;
81
+ if (item.values !== undefined)
82
+ return item.values.includes(value);
83
+ if (item.type === 'number')
74
84
  return typeof value === 'number';
75
- if (typeof value !== 'string')
85
+ if (item.type === 'integer')
86
+ return Number.isSafeInteger(value);
87
+ if (item.type === 'boolean')
88
+ return typeof value === 'boolean';
89
+ if (item.type !== 'string' || typeof value !== 'string')
76
90
  return false;
77
- if (item.kind === 'enum')
78
- return item.values.includes(value);
79
- if (item.kind === 'date')
80
- return calendarDate(value);
81
- if (item.kind === 'reference')
91
+ if (item.reference !== undefined)
82
92
  return value !== '' && normalize(value) === value;
93
+ if (item.format === 'date')
94
+ return calendarDate(value);
83
95
  return true;
84
96
  }
85
- function reference(spec) {
86
- return spec.kind === 'reference' || (spec.kind === 'list' && spec.of.kind === 'reference');
87
- }
88
97
  function typing(facts, rooms, types, at) {
89
98
  const read = rooms === null ? facts.rooms : facts.rooms.filter((room) => rooms.includes(room.locator));
90
99
  const acquiring = !facts.listed &&
@@ -102,22 +111,75 @@ function typing(facts, rooms, types, at) {
102
111
  types: types === null ? declared : declared.filter((type) => types.includes(type.name)),
103
112
  };
104
113
  }
105
- function specs(typed, field, at) {
106
- const declared = typed.types.flatMap((type) => Object.hasOwn(type.fields, field) ? [type.fields[field]] : []);
114
+ function pointer(input, at) {
115
+ if (typeof input !== 'string' || !input.startsWith('/') || STRAY_ESCAPE.test(input)) {
116
+ invalid(at, 'expects a JSON Pointer');
117
+ }
118
+ return input;
119
+ }
120
+ function stepped({ facts, through, conditional }, token) {
121
+ if (facts.type === 'array' && ARRAY_INDEX.test(token)) {
122
+ const item = itemFacts(facts, Number(token));
123
+ return item === undefined ? undefined : { facts: item, through, conditional };
124
+ }
125
+ let reached = facts;
126
+ let across = through;
127
+ while (reached?.type === 'array') {
128
+ reached = reached.items;
129
+ across = true;
130
+ }
131
+ const member = reached === undefined ? undefined : memberFacts(reached, token);
132
+ return member === undefined
133
+ ? undefined
134
+ : { facts: member, through: across, conditional: conditional || member.conditional === true };
135
+ }
136
+ function specAt(type, field) {
137
+ let spec = {
138
+ facts: { ...type.frontmatter, properties: type.fields },
139
+ through: false,
140
+ conditional: false,
141
+ };
142
+ for (const token of pointerSegments(field)) {
143
+ if (spec === undefined)
144
+ return undefined;
145
+ spec = stepped(spec, token);
146
+ }
147
+ return spec;
148
+ }
149
+ function specs(typed, input, at) {
150
+ const field = pointer(input, at);
151
+ const declared = typed.types.flatMap((type) => {
152
+ const spec = specAt(type, field);
153
+ return spec === undefined ? [] : [spec];
154
+ });
107
155
  if (declared.length === 0 && !typed.untyped) {
108
156
  throw new QueryError('unknown-field', at, `no MD Type it reads declares ${field}`);
109
157
  }
110
- return declared;
158
+ if (!declared.some((spec) => spec.conditional))
159
+ return declared;
160
+ return declared.map((spec) => ({
161
+ facts: { prose: false },
162
+ through: several(spec),
163
+ conditional: true,
164
+ }));
165
+ }
166
+ function several(spec) {
167
+ return spec.through || spec.facts.type === 'array';
168
+ }
169
+ function single(spec) {
170
+ return spec.facts.type !== undefined && spec.facts.type !== 'array';
171
+ }
172
+ function every(declared, holds) {
173
+ return declared.length > 0 && declared.every(holds);
111
174
  }
112
175
  function kind(declared, checked, at) {
113
- if (checked === null || declared.length === 0 || declared.some((spec) => fits(spec, checked))) {
176
+ if (checked === null ||
177
+ declared.length === 0 ||
178
+ declared.some((spec) => fits(spec.facts, checked))) {
114
179
  return;
115
180
  }
116
181
  throw new QueryError('wrong-kind', at, `${JSON.stringify(checked)} is not of its field's kind`);
117
182
  }
118
- function listed(declared, list) {
119
- return declared.length > 0 && declared.every((spec) => (spec.kind === 'list') === list);
120
- }
121
183
  function test(declared, input, at) {
122
184
  if (!plain(input)) {
123
185
  const equal = value(input, at);
@@ -131,8 +193,11 @@ function test(declared, input, at) {
131
193
  const stray = operators.find((operator) => !RANGE.includes(operator));
132
194
  if (stray !== undefined)
133
195
  invalid(place(at, stray), 'cannot join a range');
134
- if (listed(declared, true))
196
+ if (every(declared, (spec) => spec.facts.type === 'array'))
135
197
  invalid(at, 'a range on a list');
198
+ if (every(declared, (spec) => spec.facts.type === 'boolean' || spec.facts.type === 'object')) {
199
+ throw new QueryError('wrong-kind', at, 'its field does not compare in order');
200
+ }
136
201
  return {
137
202
  op: 'range',
138
203
  bounds: RANGE.filter((operator) => operators.includes(operator)).map((operator) => {
@@ -165,7 +230,7 @@ function test(declared, input, at) {
165
230
  return { op: 'nin', values: compared };
166
231
  if (compared.includes(null))
167
232
  invalid(where, 'a list holds no null');
168
- if (listed(declared, false))
233
+ if (every(declared, single))
169
234
  invalid(where, 'the field is not a list');
170
235
  return { op: 'all', values: compared };
171
236
  }
@@ -173,7 +238,7 @@ function test(declared, input, at) {
173
238
  if (!Number.isSafeInteger(operand) || operand < 0) {
174
239
  invalid(where, 'expects a nonnegative integer');
175
240
  }
176
- if (listed(declared, false))
241
+ if (every(declared, single))
177
242
  invalid(where, 'the field is not a list');
178
243
  return { op: 'size', size: operand };
179
244
  }
@@ -186,7 +251,7 @@ function condition(typed, input, at) {
186
251
  invalid(at, 'expects an object of field predicates');
187
252
  const conditions = [];
188
253
  for (const name of Object.keys(input).sort()) {
189
- const where = place(at, name);
254
+ const where = name.startsWith('/') ? `${at}${name}` : place(at, name);
190
255
  const entry = input[name];
191
256
  if (name === '$and' || name === '$or') {
192
257
  const nested = list(entry, where).map((item, index) => condition(typed, item, `${where}[${index}]`));
@@ -215,19 +280,19 @@ function scope(facts, rooms, input, at) {
215
280
  pathPrefix: input.pathPrefix === undefined ? null : directory(input.pathPrefix, place(at, 'pathPrefix')),
216
281
  };
217
282
  }
218
- function referenceField(typed, field, at) {
219
- if (typeof field !== 'string')
220
- invalid(at, 'expects a field name');
283
+ function referenceName(typed, field, at) {
221
284
  const declared = specs(typed, field, at);
222
- if (declared.length > 0 && !declared.some(reference))
285
+ if (declared.length > 0 &&
286
+ !declared.some((spec) => spec.conditional || referenceField(spec.facts))) {
223
287
  invalid(at, 'not a reference field');
288
+ }
224
289
  return field;
225
290
  }
226
291
  function hopOf(facts, rooms, root, input) {
227
292
  if (!plain(input))
228
293
  invalid('hop', 'expects an object');
229
294
  known(input, RELATED, 'hop');
230
- const field = referenceField(root, input.field, 'hop.field');
295
+ const field = referenceName(root, input.field, 'hop.field');
231
296
  const { types, where, pathPrefix } = scope(facts, rooms, input, 'hop');
232
297
  return { types, where, pathPrefix, field };
233
298
  }
@@ -236,14 +301,16 @@ function backlinksOf(facts, rooms, input) {
236
301
  invalid('backlinks', 'expects an object');
237
302
  known(input, RELATED, 'backlinks');
238
303
  const { typed, types, where, pathPrefix } = scope(facts, rooms, input, 'backlinks');
239
- const field = input.field === undefined ? null : referenceField(typed, input.field, 'backlinks.field');
304
+ const field = input.field === undefined ? null : referenceName(typed, input.field, 'backlinks.field');
240
305
  return { types, where, pathPrefix, field };
241
306
  }
242
307
  function keyPlan(typed, input, at, path) {
243
308
  if (input === 'room' || input === 'type' || (path && input === 'path'))
244
309
  return { of: input };
245
- if (plain(input) && typeof input.field === 'string' && Object.keys(input).length === 1) {
246
- specs(typed, input.field, place(at, 'field'));
310
+ if (plain(input) && Object.hasOwn(input, 'field') && Object.keys(input).length === 1) {
311
+ if (specs(typed, input.field, place(at, 'field')).some(several)) {
312
+ invalid(place(at, 'field'), 'a path that can hold several values');
313
+ }
247
314
  return { of: 'field', field: input.field };
248
315
  }
249
316
  return invalid(at, path ? 'expects room, path, type or a field' : 'expects room, type or a field');
@@ -301,10 +368,10 @@ function parts(typed, input, at, fields) {
301
368
  return 'all';
302
369
  const named = list(input, at);
303
370
  named.forEach((name, index) => {
304
- if (typeof name !== 'string')
305
- invalid(`${at}[${index}]`, 'expects a name');
306
371
  if (fields)
307
372
  specs(typed, name, `${at}[${index}]`);
373
+ else if (typeof name !== 'string')
374
+ invalid(`${at}[${index}]`, 'expects a name');
308
375
  });
309
376
  return named;
310
377
  }
@@ -372,7 +439,7 @@ function groupShape(typed, input) {
372
439
  });
373
440
  return { kind: 'groups', groupBy, orderBy };
374
441
  }
375
- export function planOf(input, facts) {
442
+ export function planOf(input, facts, reach = null) {
376
443
  if (!plain(input))
377
444
  invalid('query', 'expects a query object');
378
445
  const grouped = input.groupBy !== undefined;
@@ -386,6 +453,7 @@ export function planOf(input, facts) {
386
453
  const { typed, ...root } = scope(facts, rooms, input, '');
387
454
  return {
388
455
  rooms,
456
+ reach,
389
457
  root,
390
458
  hop: input.hop === undefined ? null : hopOf(facts, rooms, typed, input.hop),
391
459
  backlinks: input.backlinks === undefined ? null : backlinksOf(facts, rooms, input.backlinks),
@@ -401,11 +469,11 @@ export function limitOf(input) {
401
469
  }
402
470
  return input;
403
471
  }
404
- export function filterOf(input, facts) {
472
+ export function filterOf(input, facts, reach = null) {
405
473
  if (!plain(input))
406
474
  invalid('documents', 'expects a filter object');
407
475
  known(input, FILTER, '');
408
- return planOf({ ...input, select: {} }, facts);
476
+ return planOf({ ...input, select: {} }, facts, reach);
409
477
  }
410
478
  function canonical(value) {
411
479
  if (Array.isArray(value))
@@ -0,0 +1,40 @@
1
+ import type { MdRuntime, RuntimeTopic } from './connection.js';
2
+ import type { Plan, PlanReach, QueryResult } from './query.js';
3
+ import type { SearchResult } from './search.js';
4
+ export type ReachedDocument = {
5
+ readonly room: string;
6
+ readonly path: string;
7
+ };
8
+ export type PortReach = {
9
+ readonly rooms: readonly string[];
10
+ readonly documents: readonly ReachedDocument[];
11
+ readonly conversations: boolean;
12
+ };
13
+ export type Deliver = (message: unknown) => void;
14
+ export type Watch = (topic: RuntimeTopic, input: readonly unknown[], deliver: Deliver) => () => void;
15
+ export type RuntimeView = {
16
+ md: MdRuntime;
17
+ watch: Watch;
18
+ };
19
+ export type NarrowedView = RuntimeView & {
20
+ narrow(reach: PortReach | null): void;
21
+ };
22
+ export type Reachable = RuntimeView & {
23
+ plan(input: unknown, reach: PortReach | null): Plan;
24
+ query(plan: Plan): Promise<QueryResult>;
25
+ watchPlan(plan: Plan, deliver: (message: unknown) => void): () => void;
26
+ search(input: unknown, reach: PortReach | null): Promise<SearchResult>;
27
+ };
28
+ export declare function holds(reach: PortReach | null, room: string): boolean;
29
+ export declare function governs(reach: PortReach | null, room: {
30
+ readonly locator: string;
31
+ readonly typedSelection: {
32
+ roots: readonly string[];
33
+ } | null;
34
+ }): {
35
+ readonly collection: boolean;
36
+ readonly selection: boolean;
37
+ };
38
+ export declare function planReach(reach: PortReach | null): PlanReach | null;
39
+ export declare function narrowed(runtime: Reachable, given: PortReach | null): NarrowedView;
40
+ //# sourceMappingURL=reach.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reach.d.ts","sourceRoot":"","sources":["../src/reach.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,SAAS,EAAe,YAAY,EAAc,MAAM,iBAAiB,CAAC;AAMjG,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAI/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,MAAM,MAAM,eAAe,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/E,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;IAC/C,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;AAEjD,MAAM,MAAM,KAAK,GAAG,CAClB,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE,SAAS,OAAO,EAAE,EACzB,OAAO,EAAE,OAAO,KACb,MAAM,IAAI,CAAC;AAEhB,MAAM,MAAM,WAAW,GAAG;IAAE,EAAE,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,CAAC;AAE1D,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG;IAAE,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,IAAI,CAAA;CAAE,CAAC;AAEnF,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG;IACpC,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,IAAI,CAAC;IACpD,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACxC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACvE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CACxE,CAAC;AA2BF,wBAAgB,KAAK,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAMpE;AAMD,wBAAgB,OAAO,CACrB,KAAK,EAAE,SAAS,GAAG,IAAI,EACvB,IAAI,EAAE;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,cAAc,EAAE;QAAE,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI,CAAA;CAAE,GAC/F;IAAE,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAU/D;AAcD,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CASnE;AA8ED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,YAAY,CAgKlF"}
package/dist/reach.js ADDED
@@ -0,0 +1,270 @@
1
+ import { unheldView } from './conversation.js';
2
+ import { modelTypes, NO_MODEL } from './model.js';
3
+ import { applied, observing } from './observation.js';
4
+ import { beneath } from './paths.js';
5
+ import { reconciled } from './read-side.js';
6
+ import { moveTarget, readParts } from './room-draft.js';
7
+ import { documentKey } from './rows.js';
8
+ const UNHELD_TYPES = { collection: modelTypes(NO_MODEL), typedSelection: null };
9
+ const UNDECLARED = { kind: 'undeclared' };
10
+ function undeclared() {
11
+ return Promise.resolve(UNDECLARED);
12
+ }
13
+ const NO_CONVERSATION = {
14
+ list: () => Promise.resolve([]),
15
+ read: (conversation) => Promise.resolve(unheldView(conversation)),
16
+ create: undeclared,
17
+ send: undeclared,
18
+ answer: undeclared,
19
+ interrupt: undeclared,
20
+ kill: undeclared,
21
+ cancel: undeclared,
22
+ delete: undeclared,
23
+ };
24
+ const NO_CHAT_DRAFT = {
25
+ write: () => Promise.resolve(),
26
+ discard: () => Promise.resolve(),
27
+ };
28
+ export function holds(reach, room) {
29
+ return (reach === null ||
30
+ reach.rooms.includes(room) ||
31
+ reach.documents.some((document) => document.room === room));
32
+ }
33
+ function wholly(reach, room) {
34
+ return reach === null || reach.rooms.includes(room);
35
+ }
36
+ export function governs(reach, room) {
37
+ if (wholly(reach, room.locator)) {
38
+ return { collection: true, selection: room.typedSelection !== null };
39
+ }
40
+ const selected = (path) => room.typedSelection?.roots.some((root) => beneath(root, path)) ?? false;
41
+ const paths = reach.documents
42
+ .filter((document) => document.room === room.locator)
43
+ .map(({ path }) => path);
44
+ return { collection: paths.some((path) => !selected(path)), selection: paths.some(selected) };
45
+ }
46
+ function covers(reach, room, path) {
47
+ return (reach === null ||
48
+ reach.rooms.includes(room) ||
49
+ reach.documents.some((document) => document.room === room && document.path === path));
50
+ }
51
+ function conversing(reach) {
52
+ return reach === null || reach.conversations;
53
+ }
54
+ export function planReach(reach) {
55
+ return reach === null
56
+ ? null
57
+ : {
58
+ rooms: [...new Set(reach.rooms)].sort(),
59
+ documents: [
60
+ ...new Set(reach.documents.map(({ room, path }) => documentKey(room, path))),
61
+ ].sort(),
62
+ };
63
+ }
64
+ function roomsWithin(reach, rooms) {
65
+ return rooms.filter((room) => wholly(reach, room.locator));
66
+ }
67
+ function typesWithin(reach, room, types) {
68
+ if (room === undefined)
69
+ return structuredClone(UNHELD_TYPES);
70
+ const governed = governs(reach, room);
71
+ return {
72
+ collection: governed.collection ? types.collection : modelTypes(NO_MODEL),
73
+ typedSelection: governed.selection ? types.typedSelection : null,
74
+ };
75
+ }
76
+ function moves(reach, room, move) {
77
+ return move === undefined || covers(reach, room, moveTarget(move));
78
+ }
79
+ function draftWithin(reach, draft) {
80
+ return covers(reach, draft.room, draft.path) && moves(reach, draft.room, draft.parts.move);
81
+ }
82
+ function draftsWithin(reach, drafts) {
83
+ return drafts.filter((draft) => draftWithin(reach, draft));
84
+ }
85
+ function shownDraft(reach, draft) {
86
+ return draft !== null && draftWithin(reach, draft) ? draft : null;
87
+ }
88
+ function writeWithin(reach, write) {
89
+ return write.outcome === 'written'
90
+ ? { outcome: 'written', roomDraft: shownDraft(reach, write.roomDraft) }
91
+ : { outcome: 'stale', current: shownDraft(reach, write.current) };
92
+ }
93
+ function savedWithin(reach, room, result) {
94
+ if (reach === null || result.outcome === 'saved')
95
+ return result;
96
+ const { file, location, referrers, ...refusal } = result;
97
+ const hidden = file !== undefined && !covers(reach, room, file);
98
+ return {
99
+ ...refusal,
100
+ ...(file === undefined || hidden ? {} : { file }),
101
+ ...(location === undefined || hidden ? {} : { location }),
102
+ ...(referrers === undefined
103
+ ? {}
104
+ : { referrers: referrers.filter((path) => covers(reach, room, path)) }),
105
+ };
106
+ }
107
+ function statusWithin(reach, status) {
108
+ return {
109
+ ...status,
110
+ attentionRequired: status.attentionRequired.filter((entry) => covers(reach, entry.room, entry.path)),
111
+ };
112
+ }
113
+ function queueWithin(reach, room, entries) {
114
+ return entries.filter(({ batch }) => batch.changes.every((change) => covers(reach, room, change.path) &&
115
+ (change.kind !== 'move' || covers(reach, room, change.to))));
116
+ }
117
+ function unreached(room) {
118
+ return new Error(`No Local Replica for ${room}`);
119
+ }
120
+ export function narrowed(runtime, given) {
121
+ const { md, watch } = runtime;
122
+ let reach = given;
123
+ const open = new Set();
124
+ function following(begin) {
125
+ let stop = begin();
126
+ const restart = () => {
127
+ stop();
128
+ stop = begin();
129
+ };
130
+ open.add(restart);
131
+ return () => {
132
+ open.delete(restart);
133
+ stop();
134
+ };
135
+ }
136
+ const within = (topic, input, deliver) => {
137
+ if (topic === 'rooms') {
138
+ return following(() => watch(topic, input, (rooms) => deliver(roomsWithin(reach, rooms))));
139
+ }
140
+ if (topic === 'sync-status') {
141
+ return following(() => watch(topic, input, (status) => deliver(statusWithin(reach, status))));
142
+ }
143
+ if (topic === 'room-drafts') {
144
+ return following(() => watch(topic, input, (drafts) => deliver(draftsWithin(reach, drafts))));
145
+ }
146
+ if (topic === 'queue') {
147
+ const room = input[0];
148
+ return following(() => {
149
+ if (!holds(reach, room)) {
150
+ deliver([]);
151
+ return () => { };
152
+ }
153
+ return watch(topic, input, (entries) => deliver(queueWithin(reach, room, entries)));
154
+ });
155
+ }
156
+ if (topic === 'query') {
157
+ const plan = runtime.plan(input[0], reach);
158
+ let held = null;
159
+ return following(() => {
160
+ let first = true;
161
+ return runtime.watchPlan({ ...plan, reach: planReach(reach) }, (message) => {
162
+ if (!first) {
163
+ held = applied(held, message);
164
+ deliver(message);
165
+ return;
166
+ }
167
+ first = false;
168
+ if (held === null) {
169
+ held = message;
170
+ deliver(message);
171
+ return;
172
+ }
173
+ const next = reconciled(held, message);
174
+ if (next === null)
175
+ return;
176
+ held = next.keyed;
177
+ deliver(next.change);
178
+ });
179
+ });
180
+ }
181
+ if (topic === 'conversation') {
182
+ return following(() => {
183
+ if (conversing(reach))
184
+ return watch(topic, input, deliver);
185
+ deliver(unheldView(input[0]));
186
+ return () => { };
187
+ });
188
+ }
189
+ if (topic === 'conversations') {
190
+ return following(() => {
191
+ if (conversing(reach))
192
+ return watch(topic, input, deliver);
193
+ deliver([]);
194
+ return () => { };
195
+ });
196
+ }
197
+ return watch(topic, input, deliver);
198
+ };
199
+ const conversation = () => (conversing(reach) ? md.conversation : NO_CONVERSATION);
200
+ const chatDraft = () => (conversing(reach) ? md.chatDraft : NO_CHAT_DRAFT);
201
+ const view = {
202
+ ...md,
203
+ rooms: () => roomsWithin(reach, md.rooms()),
204
+ query: ((input) => runtime.query(runtime.plan(input, reach))),
205
+ search: (input) => runtime.search(input, reach),
206
+ types(room) {
207
+ if (wholly(reach, room))
208
+ return md.types(room);
209
+ if (!holds(reach, room))
210
+ return structuredClone(UNHELD_TYPES);
211
+ const listed = md.rooms().find(({ locator }) => locator === room);
212
+ return typesWithin(reach, listed, md.types(room));
213
+ },
214
+ async save(room, roomDrafts, writtenBy) {
215
+ if (!holds(reach, room))
216
+ throw unreached(room);
217
+ const outside = roomDrafts.find(({ path }) => !covers(reach, room, path) ||
218
+ !moves(reach, room, md.roomDraft.read(room, path)?.parts.move));
219
+ if (outside !== undefined)
220
+ return { outcome: 'refused', rule: 'stale', file: outside.path };
221
+ return savedWithin(reach, room, await md.save(room, roomDrafts, writtenBy));
222
+ },
223
+ history: (room, path, query) => covers(reach, room, path)
224
+ ? md.history(room, path, query)
225
+ : Promise.resolve({ kind: 'room-not-readable', locator: room }),
226
+ reached: (room, roomVersion, path) => (path === undefined ? wholly(reach, room) : covers(reach, room, path)) &&
227
+ md.reached(room, roomVersion, path),
228
+ log: () => (reach === null ? md.log() : ''),
229
+ roomDraft: {
230
+ list: () => md.roomDraft.list().then((drafts) => draftsWithin(reach, drafts)),
231
+ read: (room, path) => covers(reach, room, path) ? shownDraft(reach, md.roomDraft.read(room, path)) : null,
232
+ async write(room, path, revision, parts) {
233
+ if (!covers(reach, room, path) || !moves(reach, room, readParts(parts).move)) {
234
+ throw unreached(room);
235
+ }
236
+ return writeWithin(reach, await md.roomDraft.write(room, path, revision, parts));
237
+ },
238
+ async discard(room, path, revision) {
239
+ if (!covers(reach, room, path))
240
+ return { outcome: 'stale', current: null };
241
+ return writeWithin(reach, await md.roomDraft.discard(room, path, revision));
242
+ },
243
+ },
244
+ conversation: {
245
+ list: () => conversation().list(),
246
+ read: (id, options) => conversation().read(id, options),
247
+ create: (input) => conversation().create(input),
248
+ send: (id) => conversation().send(id),
249
+ answer: (id, answer) => conversation().answer(id, answer),
250
+ interrupt: (id) => conversation().interrupt(id),
251
+ kill: (id) => conversation().kill(id),
252
+ cancel: (id, turn) => conversation().cancel(id, turn),
253
+ delete: (id) => conversation().delete(id),
254
+ },
255
+ chatDraft: {
256
+ write: (id, draft) => chatDraft().write(id, draft),
257
+ discard: (id) => chatDraft().discard(id),
258
+ },
259
+ observe: observing(within),
260
+ };
261
+ return {
262
+ md: view,
263
+ watch: within,
264
+ narrow(next) {
265
+ reach = next;
266
+ for (const restart of [...open])
267
+ restart();
268
+ },
269
+ };
270
+ }
@@ -0,0 +1,32 @@
1
+ import type { MdRuntimeClient } from './client.js';
2
+ import type { AppRoom, ConnectionRefusal, SaveResult } from './connection.js';
3
+ import type { ServiceMode } from './adapter.js';
4
+ import type { ConversationEntry, ConversationOptions, ConversationView } from './conversation.js';
5
+ import type { QueueEntry } from './outcome.js';
6
+ import type { DocumentQuery, DocumentResult, GroupQuery, GroupResult } from './query.js';
7
+ import type { RoomDraft, RoomDraftParts } from './room-draft.js';
8
+ import type { SyncStatus } from './status.js';
9
+ type Client = MdRuntimeClient | null;
10
+ export declare function useObserve(client: Client, topic: 'deployment-facts'): unknown;
11
+ export declare function useObserve(client: Client, topic: 'rooms'): readonly AppRoom[] | undefined;
12
+ export declare function useObserve(client: Client, topic: 'connection-refusal'): ConnectionRefusal | null | undefined;
13
+ export declare function useObserve(client: Client, topic: 'storage-loss'): string | null | undefined;
14
+ export declare function useObserve(client: Client, topic: 'sync-status'): SyncStatus | undefined;
15
+ export declare function useObserve(client: Client, topic: 'service-mode'): ServiceMode | undefined;
16
+ export declare function useObserve(client: Client, topic: 'query', query: DocumentQuery): DocumentResult | undefined;
17
+ export declare function useObserve(client: Client, topic: 'query', query: GroupQuery): GroupResult | undefined;
18
+ export declare function useObserve(client: Client, topic: 'queue', locator: string): readonly QueueEntry[] | undefined;
19
+ export declare function useObserve(client: Client, topic: 'room-drafts'): readonly RoomDraft[] | undefined;
20
+ export declare function useObserve(client: Client, topic: 'presentation'): unknown;
21
+ export declare function useObserve(client: Client, topic: 'conversation', conversation: string, options: ConversationOptions): ConversationView | undefined;
22
+ export declare function useObserve(client: Client, topic: 'conversations'): readonly ConversationEntry[] | undefined;
23
+ export type Saving = {
24
+ readonly busy: boolean;
25
+ readonly outcome: SaveResult | null;
26
+ readonly failure: Error | null;
27
+ save(room: string, roomDrafts: ReadonlyMap<string, RoomDraftParts>): Promise<SaveResult>;
28
+ };
29
+ export declare function saveRoomDrafts(client: MdRuntimeClient, room: string, roomDrafts: ReadonlyMap<string, RoomDraftParts>): Promise<SaveResult>;
30
+ export declare function useSave(client: MdRuntimeClient): Saving;
31
+ export {};
32
+ //# sourceMappingURL=react.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"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"}