@agentshouse/mdruntime 0.1.0-ccbb89b0ca151 → 0.1.0-ccc0e3407f107

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 (138) hide show
  1. package/README.md +1 -1
  2. package/dist/adapter.d.ts +74 -57
  3. package/dist/adapter.d.ts.map +1 -1
  4. package/dist/attached-model.d.ts +3 -10
  5. package/dist/attached-model.d.ts.map +1 -1
  6. package/dist/attached-model.js +29 -47
  7. package/dist/bootstrap.d.ts +11 -18
  8. package/dist/bootstrap.d.ts.map +1 -1
  9. package/dist/bootstrap.js +85 -108
  10. package/dist/chain.d.ts +2 -1
  11. package/dist/chain.d.ts.map +1 -1
  12. package/dist/client.d.ts +9 -5
  13. package/dist/client.d.ts.map +1 -1
  14. package/dist/client.js +2 -1
  15. package/dist/collection.d.ts +7 -4
  16. package/dist/collection.d.ts.map +1 -1
  17. package/dist/collection.js +23 -17
  18. package/dist/compile.d.ts +1 -6
  19. package/dist/compile.d.ts.map +1 -1
  20. package/dist/compile.js +91 -270
  21. package/dist/connection.d.ts +10 -9
  22. package/dist/connection.d.ts.map +1 -1
  23. package/dist/connection.js +1009 -1063
  24. package/dist/context.d.ts +2 -0
  25. package/dist/context.d.ts.map +1 -0
  26. package/dist/context.js +7 -0
  27. package/dist/conversation.d.ts +16 -13
  28. package/dist/conversation.d.ts.map +1 -1
  29. package/dist/conversation.js +229 -155
  30. package/dist/durability.d.ts +6 -1
  31. package/dist/durability.d.ts.map +1 -1
  32. package/dist/entry.d.ts +3 -2
  33. package/dist/entry.d.ts.map +1 -1
  34. package/dist/entry.js +17 -8
  35. package/dist/host.d.ts +3 -7
  36. package/dist/host.d.ts.map +1 -1
  37. package/dist/host.js +22 -27
  38. package/dist/identity.d.ts +0 -1
  39. package/dist/identity.d.ts.map +1 -1
  40. package/dist/identity.js +0 -3
  41. package/dist/index.d.ts +11 -7
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +4 -1
  44. package/dist/job.d.ts +14 -0
  45. package/dist/job.d.ts.map +1 -0
  46. package/dist/job.js +28 -0
  47. package/dist/live.d.ts +2 -1
  48. package/dist/live.d.ts.map +1 -1
  49. package/dist/live.js +10 -7
  50. package/dist/media.d.ts +1 -1
  51. package/dist/media.d.ts.map +1 -1
  52. package/dist/media.js +27 -4
  53. package/dist/memory.d.ts +11 -5
  54. package/dist/memory.d.ts.map +1 -1
  55. package/dist/memory.js +167 -84
  56. package/dist/messages.d.ts +2 -2
  57. package/dist/messages.d.ts.map +1 -1
  58. package/dist/messages.js +15 -16
  59. package/dist/model.d.ts +8 -8
  60. package/dist/model.d.ts.map +1 -1
  61. package/dist/model.js +6 -12
  62. package/dist/observation.d.ts +3 -2
  63. package/dist/observation.d.ts.map +1 -1
  64. package/dist/observation.js +8 -3
  65. package/dist/outcome.d.ts +0 -3
  66. package/dist/outcome.d.ts.map +1 -1
  67. package/dist/outcome.js +3 -1
  68. package/dist/paths.d.ts +1 -0
  69. package/dist/paths.d.ts.map +1 -1
  70. package/dist/paths.js +4 -0
  71. package/dist/query.d.ts +4 -209
  72. package/dist/query.d.ts.map +1 -1
  73. package/dist/query.js +3 -505
  74. package/dist/reach.d.ts +9 -8
  75. package/dist/reach.d.ts.map +1 -1
  76. package/dist/reach.js +37 -48
  77. package/dist/react.d.ts +3 -1
  78. package/dist/react.d.ts.map +1 -1
  79. package/dist/readiness.d.ts +3 -17
  80. package/dist/readiness.d.ts.map +1 -1
  81. package/dist/readiness.js +4 -10
  82. package/dist/recovery.d.ts +1 -0
  83. package/dist/recovery.d.ts.map +1 -1
  84. package/dist/recovery.js +26 -0
  85. package/dist/refresh.d.ts +7 -5
  86. package/dist/refresh.d.ts.map +1 -1
  87. package/dist/refresh.js +29 -5
  88. package/dist/replica.d.ts +10 -8
  89. package/dist/replica.d.ts.map +1 -1
  90. package/dist/replica.js +29 -14
  91. package/dist/room-draft.d.ts +21 -5
  92. package/dist/room-draft.d.ts.map +1 -1
  93. package/dist/room-draft.js +98 -51
  94. package/dist/rows.d.ts +14 -21
  95. package/dist/rows.d.ts.map +1 -1
  96. package/dist/rows.js +48 -184
  97. package/dist/save.d.ts +2 -0
  98. package/dist/save.d.ts.map +1 -1
  99. package/dist/save.js +20 -12
  100. package/dist/search.d.ts +12 -63
  101. package/dist/search.d.ts.map +1 -1
  102. package/dist/search.js +9 -52
  103. package/dist/shared.d.ts +0 -1
  104. package/dist/shared.d.ts.map +1 -1
  105. package/dist/shared.js +0 -10
  106. package/dist/status.d.ts +4 -3
  107. package/dist/status.d.ts.map +1 -1
  108. package/dist/status.js +3 -1
  109. package/dist/store.d.ts +21 -34
  110. package/dist/store.d.ts.map +1 -1
  111. package/dist/store.js +34 -77
  112. package/dist/stored.d.ts +11 -34
  113. package/dist/stored.d.ts.map +1 -1
  114. package/dist/stored.js +158 -241
  115. package/dist/tables.d.ts +23 -48
  116. package/dist/tables.d.ts.map +1 -1
  117. package/dist/tables.js +29 -66
  118. package/dist/transfer.d.ts +0 -5
  119. package/dist/transfer.d.ts.map +1 -1
  120. package/dist/validation.d.ts +3 -4
  121. package/dist/validation.d.ts.map +1 -1
  122. package/dist/validation.js +42 -100
  123. package/dist/windows.d.ts +1 -12
  124. package/dist/windows.d.ts.map +1 -1
  125. package/dist/windows.js +2 -75
  126. package/dist/wire.d.ts +9 -4
  127. package/dist/wire.d.ts.map +1 -1
  128. package/dist/wire.js +3 -1
  129. package/package.json +2 -2
  130. package/dist/fulltext.d.ts +0 -27
  131. package/dist/fulltext.d.ts.map +0 -1
  132. package/dist/fulltext.js +0 -368
  133. package/dist/reads.d.ts +0 -11
  134. package/dist/reads.d.ts.map +0 -1
  135. package/dist/reads.js +0 -100
  136. package/dist/words.d.ts +0 -19
  137. package/dist/words.d.ts.map +0 -1
  138. package/dist/words.js +0 -150
package/dist/compile.js CHANGED
@@ -1,119 +1,36 @@
1
- import { ARRAY_INDEX, parse, pointerSegments } from '@agentshouse/mdmodel';
1
+ import { ARRAY_INDEX, bodied, byCodePoint, compared, cursorOf, heldKind, parse, pointerSegments, sql, } from '@agentshouse/mdmodel';
2
2
  import { binaryRecord } from './binary.js';
3
- import { documentKey, kindOf, pathOfKey, placedOf, roomOfKey } from './rows.js';
4
- import { byCodePoint, compared, cursorOf, FROM_START, MAXIMUM_WINDOW, } from './windows.js';
3
+ import { documentKey, pathOfKey, roomOfKey } from './rows.js';
4
+ import { FROM_START, MAXIMUM_WINDOW } from './windows.js';
5
+ const { all, any, exists, fragment, grouped, joined, listed } = sql;
5
6
  const VISIBLE = (alias) => `${alias}.visible = 1 AND ${alias}.present = 1 AND ${alias}.queried = 1`;
6
- function fragment(sql, bind = []) {
7
- return { sql, bind };
7
+ function located(alias) {
8
+ return fragment(`(SELECT k.locator FROM replica_keys k WHERE k.key = ${alias}.key)`);
8
9
  }
9
- function joined(parts, separator) {
10
- return {
11
- sql: parts.map((part) => part.sql).join(separator),
12
- bind: parts.flatMap((part) => part.bind),
13
- };
14
- }
15
- function all(parts) {
16
- return parts.length === 0 ? fragment('1') : joined(parts.map(grouped), ' AND ');
17
- }
18
- function any(parts) {
19
- return parts.length === 0 ? fragment('0') : joined(parts.map(grouped), ' OR ');
20
- }
21
- function grouped(part) {
22
- return { sql: `(${part.sql})`, bind: part.bind };
23
- }
24
- function list(values) {
25
- return fragment(`(${values.map(() => '?').join(', ')})`, values);
26
- }
27
- function sqlValue(value) {
28
- if (value === null)
29
- return { kind: 'null', value: null };
30
- if (typeof value === 'boolean')
31
- return { kind: 'boolean', value: value ? 1 : 0 };
32
- if (typeof value === 'number')
33
- return { kind: 'number', value };
34
- return { kind: 'string', value };
35
- }
36
- function same(alias, value) {
37
- const held = sqlValue(value);
38
- return held.kind === 'null'
39
- ? fragment(`${alias}.kind = 'null'`)
40
- : fragment(`${alias}.kind = ? AND ${alias}.value = ?`, [held.kind, held.value]);
41
- }
42
- const BOUNDS = { gt: '>', gte: '>=', lt: '<', lte: '<=' };
43
- function pointed(alias, field, reach) {
44
- const { keys, places, glob } = placedOf(field, reach);
45
- return fragment(`${alias}.keys = ? AND ${alias}.places ${glob ? 'GLOB' : '='} ?`, [keys, places]);
46
- }
47
- function heldValues(doc, field, alias, where) {
48
- return joined([
49
- fragment(`SELECT 1 FROM replica_values ${alias} WHERE ${alias}.doc = ${doc}.id AND `),
50
- grouped(pointed(alias, field, 'value')),
51
- fragment(' AND '),
52
- grouped(where),
53
- ], '');
54
- }
55
- function exists(part) {
56
- return { sql: `EXISTS (${part.sql})`, bind: part.bind };
57
- }
58
- function itemOf(held, where) {
59
- return exists(joined([
60
- fragment(`SELECT 1 FROM replica_values i WHERE i.doc = ${held}.doc AND i.keys = ${held}.keys AND i.places = ${held}.places AND i.item >= 0 AND `),
61
- grouped(where),
62
- ], ''));
63
- }
64
- function matching(held, values) {
65
- return any(values.map((value) => any([
66
- same(held, value),
67
- all([fragment(`${held}.kind = 'array'`), itemOf(held, same('i', value))]),
68
- ])));
69
- }
70
- function tested(doc, field, test) {
71
- const reached = exists(heldValues(doc, field, 'r', fragment('r.item = -1')));
72
- if (test.op === 'exists')
73
- return test.exists ? reached : fragment(`NOT ${reached.sql}`, reached.bind);
74
- if (test.op === 'eq' || test.op === 'in') {
75
- const values = test.op === 'eq' ? [test.value] : test.values;
76
- return exists(heldValues(doc, field, 'v', any(values.map((value) => same('v', value)))));
77
- }
78
- if (test.op === 'ne' || test.op === 'nin') {
79
- const values = test.op === 'ne' ? [test.value] : test.values;
80
- const unmatched = matching('h', values);
81
- return any([
82
- fragment(`NOT ${reached.sql}`, reached.bind),
83
- exists(heldValues(doc, field, 'h', all([fragment('h.item = -1'), fragment(`NOT (${unmatched.sql})`, unmatched.bind)]))),
84
- ]);
85
- }
86
- if (test.op === 'range') {
87
- return exists(heldValues(doc, field, 'v', all([
88
- fragment('v.item = -1'),
89
- ...test.bounds.map(([operator, bound]) => {
90
- const held = sqlValue(bound);
91
- return fragment(`v.kind = ? AND v.value ${BOUNDS[operator]} ?`, [
92
- held.kind,
93
- held.value,
94
- ]);
95
- }),
96
- ])));
97
- }
98
- if (test.op === 'all') {
99
- return exists(heldValues(doc, field, 'h', all([
100
- fragment("h.item = -1 AND h.kind = 'array'"),
101
- ...test.values.map((value) => itemOf('h', same('i', value))),
102
- ])));
103
- }
104
- return exists(heldValues(doc, field, 'v', fragment("v.item = -1 AND v.kind = 'array' AND v.size = ?", [test.size])));
10
+ function within(alias, path) {
11
+ return fragment(`(${alias}.path = ? OR (${alias}.path >= ? AND ${alias}.path < ?))`, [
12
+ path,
13
+ `${path}/`,
14
+ `${path}0`,
15
+ ]);
105
16
  }
106
- function conditioned(doc, condition) {
107
- if ('every' in condition)
108
- return all(condition.every.map((one) => conditioned(doc, one)));
109
- if ('some' in condition)
110
- return any(condition.some.map((one) => conditioned(doc, one)));
111
- return tested(doc, condition.field, condition.test);
17
+ function ancestors(area) {
18
+ const depth = area.path.split('/').length;
19
+ return area.open.flatMap((open) => {
20
+ const segments = open.split('/');
21
+ return segments.slice(depth - 1, -1).map((_, at) => segments.slice(0, depth + at).join('/'));
22
+ });
112
23
  }
113
- function located(bound, alias) {
114
- if (bound.rooms.length === 0)
115
- return fragment("''");
116
- return fragment(`CASE ${alias}.key ${bound.rooms.map(() => 'WHEN ? THEN ?').join(' ')} END`, bound.rooms.flatMap(({ key, locator }) => [key, locator]));
24
+ function fenced(alias, key, area, folders) {
25
+ const opened = any([
26
+ ...area.open.map((open) => within(alias, open)),
27
+ ...(folders ? ancestors(area).map((path) => fragment(`${alias}.path = ?`, [path])) : []),
28
+ ]);
29
+ return all([
30
+ fragment(`${alias}.key = ?`, [key]),
31
+ within(alias, area.path),
32
+ fragment(`NOT ${grouped(opened).sql}`, opened.bind),
33
+ ]);
117
34
  }
118
35
  function reached(bound, alias) {
119
36
  const reach = bound.reach;
@@ -127,42 +44,20 @@ function reached(bound, alias) {
127
44
  const key = bound.keys.get(roomOfKey(held));
128
45
  return key === undefined ? [] : [[key, pathOfKey(held)]];
129
46
  });
130
- const areas = reach.areas.flatMap((area) => {
47
+ const outside = any(reach.areas.flatMap((area) => {
131
48
  const key = bound.keys.get(area.room);
132
- return key === undefined ? [] : [{ key, path: area.path, types: area.types }];
133
- });
134
- const beneath = (area) => fragment(`${alias}.key = ? AND (${alias}.path = ? OR (${alias}.path >= ? AND ${alias}.path < ?))`, [area.key, area.path, `${area.path}/`, `${area.path}0`]);
135
- const typed = (types) => fragment(`${alias}.type IN ${list(types).sql}`, types);
136
- const outside = any(areas.map((area) => area.types.length === 0
137
- ? beneath(area)
138
- : all([beneath(area), fragment(`NOT ${grouped(typed(area.types)).sql}`, area.types)])));
49
+ return key === undefined ? [] : [fenced(alias, key, area, false)];
50
+ }));
139
51
  return all([
140
52
  any([
141
- ...(rooms.length === 0 ? [] : [fragment(`${alias}.key IN ${list(rooms).sql}`, rooms)]),
53
+ ...(rooms.length === 0 ? [] : [fragment(`${alias}.key IN ${listed(rooms).sql}`, rooms)]),
142
54
  ...(documents.length === 0
143
55
  ? []
144
56
  : [
145
57
  fragment(`EXISTS (SELECT 1 FROM json_each(?) j WHERE j.value ->> 0 = ${alias}.key AND j.value ->> 1 = ${alias}.path)`, [JSON.stringify(documents)]),
146
58
  ]),
147
- ...areas.flatMap((area) => area.types.length === 0 ? [] : [all([beneath(area), typed(area.types)])]),
148
59
  ]),
149
- ...(areas.length === 0 ? [] : [fragment(`NOT ${grouped(outside).sql}`, outside.bind)]),
150
- ]);
151
- }
152
- function scoped(alias, scope) {
153
- return all([
154
- ...(scope.types === null
155
- ? []
156
- : [fragment(`${alias}.type IN ${list(scope.types).sql}`, scope.types)]),
157
- ...(scope.pathPrefix === null || scope.pathPrefix === ''
158
- ? []
159
- : [
160
- fragment(`${alias}.path >= ? AND ${alias}.path < ?`, [
161
- `${scope.pathPrefix}/`,
162
- `${scope.pathPrefix}0`,
163
- ]),
164
- ]),
165
- ...(scope.where === null ? [] : [conditioned(alias, scope.where)]),
60
+ ...(reach.areas.length === 0 ? [] : [fragment(`NOT ${grouped(outside).sql}`, outside.bind)]),
166
61
  ]);
167
62
  }
168
63
  function related(bound, plan) {
@@ -170,17 +65,17 @@ function related(bound, plan) {
170
65
  if (plan.hop !== null) {
171
66
  parts.push(exists(joined([
172
67
  fragment(`SELECT 1 FROM replica_links l JOIN replica_entries m ON m.key = d.key AND m.path = l.target AND ${VISIBLE('m')} WHERE l.doc = d.id AND l.kind = 'field' AND`),
173
- grouped(pointed('l', plan.hop.field, 'link')),
68
+ grouped(sql.placed('l', plan.hop.field, 'link')),
174
69
  fragment('AND'),
175
- grouped(all([scoped('m', plan.hop), reached(bound, 'm')])),
70
+ grouped(all([sql.scoped('m', plan.hop), reached(bound, 'm')])),
176
71
  ], ' ')));
177
72
  }
178
73
  if (plan.backlinks !== null) {
179
74
  const field = plan.backlinks.field;
180
75
  parts.push(exists(joined([
181
76
  fragment(`SELECT 1 FROM replica_links l JOIN replica_entries s ON s.id = l.doc AND ${VISIBLE('s')} WHERE l.key = d.key AND l.target = d.path AND l.kind = 'field' AND`),
182
- ...(field === null ? [] : [grouped(pointed('l', field, 'link')), fragment('AND')]),
183
- grouped(all([scoped('s', plan.backlinks), reached(bound, 's')])),
77
+ ...(field === null ? [] : [grouped(sql.placed('l', field, 'link')), fragment('AND')]),
78
+ grouped(all([sql.scoped('s', plan.backlinks), reached(bound, 's')])),
184
79
  ], ' ')));
185
80
  }
186
81
  return parts;
@@ -188,11 +83,9 @@ function related(bound, plan) {
188
83
  function filtered(bound, plan) {
189
84
  const keys = bound.rooms.map(({ key }) => key);
190
85
  return all([
191
- fragment(`d.key IN ${list(keys).sql} AND ${VISIBLE('d')}`, keys),
192
- ...(plan.paths === null ? [] : [fragment(`d.path IN ${list(plan.paths).sql}`, plan.paths)]),
193
- ...(plan.folder === null ? [] : [fragment('d.folder = ?', [plan.folder])]),
86
+ fragment(`d.key IN ${listed(keys).sql} AND ${VISIBLE('d')}`, keys),
194
87
  reached(bound, 'd'),
195
- scoped('d', plan.root),
88
+ sql.tests('d', plan),
196
89
  ...related(bound, plan),
197
90
  ]);
198
91
  }
@@ -223,103 +116,25 @@ const GROUP_TIES = {
223
116
  locale: undefined,
224
117
  localeOptions: undefined,
225
118
  };
226
- function classOf(value, ordering) {
227
- if (value === null || value === undefined) {
228
- return { placement: ordering.nulls === 'first' ? 0 : 2, klass: 0, scalar: null };
229
- }
230
- if (typeof value === 'boolean')
231
- return { placement: 1, klass: 1, scalar: value ? 1 : 0 };
232
- if (typeof value === 'number')
233
- return { placement: 1, klass: 1, scalar: value };
234
- if (typeof value === 'string')
235
- return { placement: 1, klass: 2, scalar: value };
236
- return { placement: 1, klass: 3, scalar: JSON.stringify(value) };
237
- }
238
- function placement(term) {
239
- const nulls = term.ordering.nulls === 'first' ? 0 : 2;
240
- return fragment(`CASE WHEN ${term.kind.sql} IS NULL OR ${term.kind.sql} = 'null' THEN ${nulls} ELSE 1 END`, [...term.kind.bind, ...term.kind.bind]);
241
- }
242
- function klass(term) {
243
- return fragment(`CASE ${term.kind.sql} WHEN 'boolean' THEN 1 WHEN 'number' THEN 1 WHEN 'string' THEN 2 WHEN 'array' THEN 3 ELSE 4 END`, term.kind.bind);
244
- }
245
- function classed(term, which) {
246
- const kinds = which === 1 ? "('boolean', 'number')" : which === 2 ? "('string')" : "('array')";
247
- return fragment(`(CASE WHEN ${term.kind.sql} IN ${kinds} THEN ${term.value.sql} END)`, [
248
- ...term.kind.bind,
249
- ...term.value.bind,
250
- ]);
251
- }
252
- function collated(bound, term, which) {
253
- if (which === 1)
254
- return '';
255
- const name = bound.collate(term.ordering, which === 3, term.total === true);
256
- return name === null ? '' : ` COLLATE ${name}`;
257
- }
258
- function sorted(bound, term) {
259
- const direction = term.ordering.direction === 'desc' ? 'DESC' : 'ASC';
260
- return joined([
261
- placement(term),
262
- fragment(`${klass(term).sql} ${direction}`, klass(term).bind),
263
- ...[1, 2, 3].map((which) => {
264
- const value = classed(term, which);
265
- return fragment(`${value.sql}${collated(bound, term, which)} ${direction}`, value.bind);
266
- }),
267
- ], ', ');
268
- }
269
- function beyond(bound, term, value) {
270
- const held = classOf(value, term.ordering);
271
- const place = placement(term);
272
- if (held.placement !== 1) {
273
- return {
274
- beyond: fragment(`${place.sql} > ${held.placement}`, place.bind),
275
- equal: fragment(`${place.sql} = ${held.placement}`, place.bind),
276
- };
277
- }
278
- const after = term.ordering.direction === 'desc' ? '<' : '>';
279
- const kind = klass(term);
280
- const scalar = classed(term, held.klass);
281
- const collation = collated(bound, term, held.klass);
282
- return {
283
- beyond: any([
284
- fragment(`${place.sql} > 1`, place.bind),
285
- all([
286
- fragment(`${place.sql} = 1`, place.bind),
287
- any([
288
- fragment(`${kind.sql} ${after} ${held.klass}`, kind.bind),
289
- all([
290
- fragment(`${kind.sql} = ${held.klass}`, kind.bind),
291
- fragment(`${scalar.sql} ${after} ?${collation}`, [...scalar.bind, held.scalar]),
292
- ]),
293
- ]),
294
- ]),
295
- ]),
296
- equal: all([
297
- fragment(`${place.sql} = 1 AND ${kind.sql} = ${held.klass}`, [...place.bind, ...kind.bind]),
298
- fragment(`${scalar.sql} = ?${collation}`, [...scalar.bind, held.scalar]),
299
- ]),
300
- };
301
- }
302
- function following(parts, tie) {
303
- let condition = tie;
304
- for (const part of [...parts].reverse()) {
305
- condition = any([part.beyond, all([part.equal, condition])]);
306
- }
307
- return condition;
308
- }
309
- function keyTerm(bound, key, ordering, index) {
119
+ function keyTerm(key, ordering, index) {
310
120
  if (key.of === 'field') {
311
121
  const alias = `o${index}`;
312
122
  return {
313
- term: { ordering, kind: fragment(`${alias}.kind`), value: fragment(`${alias}.value`) },
123
+ term: {
124
+ ordering,
125
+ kind: fragment(`${alias}.kind`),
126
+ value: fragment(`${alias}.value`),
127
+ instant: fragment(`${alias}.instant`),
128
+ },
314
129
  join: joined([
315
130
  fragment(`LEFT JOIN replica_values ${alias} ON ${alias}.doc = d.id AND `),
316
- grouped(pointed(alias, key.field, 'exact')),
131
+ grouped(sql.placed(alias, key.field, 'exact')),
317
132
  fragment(` AND ${alias}.item = -1 AND ${alias}.kind NOT IN ('object', 'undefined')`),
318
133
  ], ''),
319
134
  };
320
135
  }
321
136
  const expression = key.of === 'room'
322
- ? located(bound, 'd')
137
+ ? located('d')
323
138
  : key.of === 'path'
324
139
  ? fragment('d.path')
325
140
  : key.of === 'type'
@@ -345,13 +160,20 @@ function decoded(kind, value) {
345
160
  return JSON.parse(value);
346
161
  return null;
347
162
  }
163
+ function sortedValue(row, index) {
164
+ const kind = row[`k${index}`];
165
+ return sql.sortValue(kind, decoded(kind, row[`v${index}`]), row[`i${index}`]);
166
+ }
348
167
  function decodedKey(key, row, index) {
349
168
  if (key.of === 'field') {
350
169
  const kind = row[`k${index}`];
351
- return kind === null || kind === undefined ? undefined : decoded(kind, row[`v${index}`]);
170
+ return kind === null || kind === undefined ? undefined : sortedValue(row, index);
352
171
  }
353
172
  return row[`v${index}`] ?? null;
354
173
  }
174
+ function instantOf(term) {
175
+ return term.instant ?? fragment('NULL');
176
+ }
355
177
  const registered = new WeakMap();
356
178
  function hashed(text) {
357
179
  let hash = 0x811c9dc5;
@@ -401,9 +223,6 @@ function collating(connection) {
401
223
  return name;
402
224
  };
403
225
  }
404
- function bodied(shape) {
405
- return shape.sections !== null || shape.preamble || shape.markdown;
406
- }
407
226
  function masked(pointers) {
408
227
  const root = { whole: false, members: new Map() };
409
228
  for (const pointer of pointers) {
@@ -494,7 +313,7 @@ function documentOf(shape, room, row, linked) {
494
313
  ? {}
495
314
  : { sections: sectionsOf(body.sections, shape.sections) }),
496
315
  ...(shape.preamble && body !== null ? { preamble: body.preamble } : {}),
497
- ...(shape.markdown && body !== null ? { markdown: body.content } : {}),
316
+ ...(shape.markdown ? { markdown: row.content } : {}),
498
317
  ...(shape.backlinks ? { backlinks: linked.get(documentKey(room, path)) ?? [] } : {}),
499
318
  ...(shape.change
500
319
  ? {
@@ -528,10 +347,10 @@ function backlinksOf(connection, bound, rows) {
528
347
  return linked;
529
348
  }
530
349
  function documents(connection, bound, plan, shape, budget) {
531
- const terms = shape.orderBy.map(({ key, options }, index) => keyTerm(bound, key, orderingOf(options), index));
350
+ const terms = shape.orderBy.map(({ key, options }, index) => keyTerm(key, orderingOf(options), index));
532
351
  const filter = filtered(bound, plan);
533
352
  const cursor = plan.after?.values ?? null;
534
- const room = located(bound, 'd');
353
+ const room = located('d');
535
354
  const tie = cursor === null
536
355
  ? null
537
356
  : (() => {
@@ -547,19 +366,20 @@ function documents(connection, bound, plan, shape, budget) {
547
366
  })();
548
367
  const after = cursor === null || tie === null
549
368
  ? null
550
- : following(terms.map(({ term }, index) => beyond(bound, term, cursor[index])), tie);
369
+ : sql.following(terms.map(({ term }, index) => sql.beyond(bound.collate, term, cursor[index])), tie);
551
370
  const selected = joined([
552
371
  fragment('SELECT d.id, d.key, d.path, d.type, d.binary, d.revision, d.fields, d.change, d.file_id'),
553
372
  ...terms.map(({ term }, index) => joined([
554
373
  fragment(`, ${term.kind.sql} AS k${index}`, term.kind.bind),
555
374
  fragment(`, ${term.value.sql} AS v${index}`, term.value.bind),
375
+ fragment(`, ${instantOf(term).sql} AS i${index}`, instantOf(term).bind),
556
376
  ], '')),
557
377
  fragment(' FROM replica_entries d'),
558
378
  ...terms.flatMap(({ join }) => (join === null ? [] : [fragment(' '), join])),
559
379
  fragment(' WHERE '),
560
380
  after === null ? filter : all([filter, after]),
561
381
  fragment(' ORDER BY '),
562
- joined([...terms.map(({ term }) => sorted(bound, term)), room, fragment('d.path')], ', '),
382
+ joined([...terms.map(({ term }) => sql.sorted(bound.collate, term)), room, fragment('d.path')], ', '),
563
383
  ...(plan.limit === null ? [] : [fragment(' LIMIT ?', [plan.limit + 1])]),
564
384
  ], '');
565
385
  const rows = connection.rows(selected.sql, selected.bind);
@@ -598,7 +418,7 @@ function withContent(connection, rows, budget) {
598
418
  return rows;
599
419
  const ids = rows.map((row) => row.id);
600
420
  const sizes = new Map(connection
601
- .rows(`SELECT id, octet_length(content) AS size FROM replica_entries WHERE id IN ${list(ids).sql}`, ids)
421
+ .rows(`SELECT id, octet_length(content) AS size FROM replica_entries WHERE id IN ${listed(ids).sql}`, ids)
602
422
  .map((row) => [row.id, Number(row.size ?? 0)]));
603
423
  let cut = rows.length;
604
424
  let spent = 0;
@@ -613,34 +433,30 @@ function withContent(connection, rows, budget) {
613
433
  const kept = rows.slice(0, cut);
614
434
  const read = kept.map((row) => row.id);
615
435
  const held = new Map(connection
616
- .rows(`SELECT id, content FROM replica_entries WHERE id IN ${list(read).sql}`, read)
436
+ .rows(`SELECT id, content FROM replica_entries WHERE id IN ${listed(read).sql}`, read)
617
437
  .map((row) => [row.id, row.content]));
618
438
  return kept.map((row) => ({ ...row, content: held.get(row.id) ?? null }));
619
439
  }
620
440
  function storedFolders(bound, plan) {
621
- if (plan.paths !== null || plan.hop !== null || plan.backlinks !== null)
441
+ if (plan.paths !== null || plan.fileIds !== null)
442
+ return null;
443
+ if (plan.hop !== null || plan.backlinks !== null)
622
444
  return null;
623
445
  const whole = bound.rooms.filter(({ locator }) => bound.reach === null || bound.reach.rooms.includes(locator));
624
446
  if (whole.length === 0)
625
447
  return null;
626
448
  const keys = whole.map(({ key }) => key);
627
449
  const prefix = plan.root.pathPrefix;
628
- const fenced = (bound.reach?.areas ?? []).flatMap((area) => {
450
+ const outside = (bound.reach?.areas ?? []).flatMap((area) => {
629
451
  const key = bound.keys.get(area.room);
630
- return key === undefined
631
- ? []
632
- : [
633
- fragment('NOT (f.key = ? AND (f.path = ? OR (f.path >= ? AND f.path < ?)))', [
634
- key,
635
- area.path,
636
- `${area.path}/`,
637
- `${area.path}0`,
638
- ]),
639
- ];
452
+ if (key === undefined)
453
+ return [];
454
+ const fence = fenced('f', key, area, true);
455
+ return [fragment(`NOT ${grouped(fence).sql}`, fence.bind)];
640
456
  });
641
457
  return all([
642
- fragment(`f.key IN ${list(keys).sql} AND f.layer = 0 AND f.present = 1`, keys),
643
- ...fenced,
458
+ fragment(`f.key IN ${listed(keys).sql} AND f.layer = 0 AND f.present = 1`, keys),
459
+ ...outside,
644
460
  ...(prefix === null || prefix === ''
645
461
  ? []
646
462
  : [
@@ -655,7 +471,7 @@ function storedFolders(bound, plan) {
655
471
  }
656
472
  function groups(connection, bound, plan, shape) {
657
473
  const keyed = shape.groupBy.map((key, index) => {
658
- const { term, join } = keyTerm(bound, key, GROUP_ORDER, index);
474
+ const { term, join } = keyTerm(key, GROUP_ORDER, index);
659
475
  const kind = fragment(`COALESCE(${term.kind.sql}, 'null')`, term.kind.bind);
660
476
  return { term: { ...term, kind }, join };
661
477
  });
@@ -665,6 +481,7 @@ function groups(connection, bound, plan, shape) {
665
481
  joined(keyed.map(({ term }, index) => joined([
666
482
  fragment(`${term.kind.sql} AS k${index}`, term.kind.bind),
667
483
  fragment(`, ${term.value.sql} AS v${index}`, term.value.bind),
484
+ fragment(`, ${instantOf(term).sql} AS i${index}`, instantOf(term).bind),
668
485
  ], '')), ', '),
669
486
  fragment(', 1 AS n FROM replica_entries d'),
670
487
  ...keyed.flatMap(({ join }) => (join === null ? [] : [fragment(' '), join])),
@@ -678,10 +495,15 @@ function groups(connection, bound, plan, shape) {
678
495
  ? inner
679
496
  : joined([
680
497
  inner,
681
- fragment(" UNION ALL SELECT 'string' AS k0, f.path AS v0, 0 AS n FROM replica_folders f WHERE "),
498
+ fragment(" UNION ALL SELECT 'string' AS k0, f.path AS v0, NULL AS i0, 0 AS n FROM replica_folders f WHERE "),
682
499
  folders,
683
500
  ], '');
684
- const columns = keyed.map((_, index) => `k${index}, v${index}`).join(', ');
501
+ const columns = keyed
502
+ .map((_, index) => `k${index}, min(v${index}) AS v${index}, i${index}`)
503
+ .join(', ');
504
+ const held = keyed
505
+ .map((_, index) => `k${index}, i${index}, CASE WHEN i${index} IS NULL THEN v${index} END`)
506
+ .join(', ');
685
507
  const counted = {
686
508
  ordering: { ...GROUP_ORDER },
687
509
  kind: fragment("'number'"),
@@ -694,7 +516,7 @@ function groups(connection, bound, plan, shape) {
694
516
  });
695
517
  const ordered = shape.orderBy.map(({ by, direction }) => by === 'count'
696
518
  ? { ...counted, ordering: { ...GROUP_ORDER, direction } }
697
- : outer(by, { ...GROUP_ORDER, direction }));
519
+ : { ...outer(by, { ...GROUP_ORDER, direction }), instant: fragment(`i${by}`) });
698
520
  const ties = keyed.flatMap((_, index) => [
699
521
  { ...outer(index, GROUP_TIES), total: true },
700
522
  { ordering: GROUP_TIES, kind: fragment("'string'"), value: fragment(`k${index}`) },
@@ -704,19 +526,21 @@ function groups(connection, bound, plan, shape) {
704
526
  ? null
705
527
  : cursor[shape.orderBy.length].flatMap((value) => [
706
528
  value,
707
- kindOf(value ?? null),
529
+ heldKind(value ?? null),
708
530
  ]);
709
531
  const after = cursor === null || tieValues === null
710
532
  ? null
711
- : following(ordered.map((term, index) => beyond(bound, term, cursor[index])), following(ties.slice(0, -1).map((term, index) => beyond(bound, term, tieValues[index])), beyond(bound, ties.at(-1), tieValues.at(-1)).beyond));
712
- const grouping = joined([fragment(`SELECT ${columns}, sum(n) AS n FROM (`), source, fragment(`) GROUP BY ${columns}`)], '');
533
+ : sql.following(ordered.map((term, index) => sql.beyond(bound.collate, term, cursor[index])), sql.following(ties
534
+ .slice(0, -1)
535
+ .map((term, index) => sql.beyond(bound.collate, term, tieValues[index])), sql.beyond(bound.collate, ties.at(-1), tieValues.at(-1)).beyond));
536
+ const grouping = joined([fragment(`SELECT ${columns}, sum(n) AS n FROM (`), source, fragment(`) GROUP BY ${held}`)], '');
713
537
  const windowed = joined([
714
538
  fragment('SELECT * FROM ('),
715
539
  grouping,
716
540
  fragment(')'),
717
541
  ...(after === null ? [] : [fragment(' WHERE '), after]),
718
542
  fragment(' ORDER BY '),
719
- joined([...ordered, ...ties].map((term) => sorted(bound, term)), ', '),
543
+ joined([...ordered, ...ties].map((term) => sql.sorted(bound.collate, term)), ', '),
720
544
  ...(plan.limit === null ? [] : [fragment(' LIMIT ?', [plan.limit + 1])]),
721
545
  ], '');
722
546
  const rows = connection.rows(windowed.sql, windowed.bind);
@@ -729,7 +553,7 @@ function groups(connection, bound, plan, shape) {
729
553
  : last === undefined
730
554
  ? (plan.after?.text ?? FROM_START)
731
555
  : cursorOf([
732
- ...shape.orderBy.map(({ by }) => by === 'count' ? Number(last.n) : decoded(last[`k${by}`], last[`v${by}`])),
556
+ ...shape.orderBy.map(({ by }) => by === 'count' ? Number(last.n) : sortedValue(last, by)),
733
557
  keyOf(last),
734
558
  ]);
735
559
  const total = plan.total
@@ -749,9 +573,6 @@ function boundOf(connection, plan, rooms) {
749
573
  collate: collating(connection),
750
574
  };
751
575
  }
752
- export function selected(connection, plan, rooms) {
753
- return filtered(boundOf(connection, plan, rooms), plan);
754
- }
755
576
  export function answerQuery(connection, plan, rooms, budget = null) {
756
577
  const bound = boundOf(connection, plan, rooms);
757
578
  if (rooms.length === 0) {
@@ -1,6 +1,7 @@
1
+ import { type DocumentQuery, type GroupQuery } from '@agentshouse/mdmodel';
1
2
  import { type TypesResult } from './model.js';
2
3
  import { type Observation } from './observation.js';
3
- import { type DocumentQuery, type DocumentResult, type GroupQuery, type GroupResult, type PendingResult } from './query.js';
4
+ import { type DocumentResult, type GroupResult, type PendingResult } from './query.js';
4
5
  import { type EntrySeed } from './seed.js';
5
6
  import { type SearchRequest, type SearchResult } from './search.js';
6
7
  import { type Adapter, type Authorization, type Member, type ProviderRefusal, type ServiceMode } from './adapter.js';
@@ -12,8 +13,8 @@ import { type Recovery } from './recovery.js';
12
13
  import { type SaveRefusal } from './save.js';
13
14
  import { type RoomDraft, type RoomDraftParts, type RoomDraftWrite } from './room-draft.js';
14
15
  import { type SyncStatus } from './status.js';
15
- import { type RuntimeShell } from './durability.js';
16
- import { type CodeAvailability, type Persistence, type Readiness, type SearchObligation, type Space } from './readiness.js';
16
+ import { type RuntimeShell, type UnsentWork } from './durability.js';
17
+ import { type CodeAvailability, type Persistence, type Readiness } from './readiness.js';
17
18
  import type { RuntimeDatabase } from './tables.js';
18
19
  import { type NarrowedView, type PortReach, type RuntimeView } from './reach.js';
19
20
  export type AppTypedSelection = {
@@ -54,6 +55,7 @@ export type MdRuntimeOptions = {
54
55
  authorization?: Authorization;
55
56
  signal?: AbortSignal;
56
57
  recovery?: Recovery;
58
+ usableWhenHeld?: readonly string[];
57
59
  };
58
60
  export type SaveResult = {
59
61
  outcome: 'saved';
@@ -93,7 +95,7 @@ export type HistoryQuery = {
93
95
  };
94
96
  export type MdRuntime = {
95
97
  loaded(): Promise<void>;
96
- rooms(): readonly AppRoom[];
98
+ rooms(): Promise<readonly AppRoom[]>;
97
99
  query: {
98
100
  (query: DocumentQuery): Promise<DocumentResult | PendingResult>;
99
101
  (query: GroupQuery): Promise<GroupResult | PendingResult>;
@@ -115,6 +117,8 @@ export type MdRuntime = {
115
117
  persistence(): boolean;
116
118
  retry(): Promise<void>;
117
119
  cleanUp(): Promise<number>;
120
+ unsent(): Promise<readonly UnsentWork[]>;
121
+ reset(): Promise<void>;
118
122
  observe: {
119
123
  (topic: 'rooms'): Promise<Observation<readonly AppRoom[]>>;
120
124
  (topic: 'readiness'): Promise<Observation<Readiness>>;
@@ -123,6 +127,7 @@ export type MdRuntime = {
123
127
  (topic: 'sync-status'): Promise<Observation<SyncStatus>>;
124
128
  (topic: 'service-mode'): Promise<Observation<ServiceMode>>;
125
129
  (topic: 'member'): Promise<Observation<Member | null>>;
130
+ (topic: 'authority-changes'): Promise<Observation<number>>;
126
131
  (topic: 'query', query: DocumentQuery): Promise<Observation<DocumentResult | PendingResult>>;
127
132
  (topic: 'query', query: GroupQuery): Promise<Observation<GroupResult | PendingResult>>;
128
133
  (topic: 'queue', locator: string): Promise<Observation<readonly QueueEntry[]>>;
@@ -134,19 +139,15 @@ export type MdRuntime = {
134
139
  };
135
140
  reauthorize(): Promise<void>;
136
141
  };
137
- export type RuntimeTopic = 'rooms' | 'readiness' | 'connection-refusal' | 'storage-loss' | 'sync-status' | 'service-mode' | 'member' | 'query' | 'queue' | 'room-drafts' | 'presentation' | 'conversation' | 'conversations' | 'agents';
142
+ export type RuntimeTopic = 'rooms' | 'readiness' | 'connection-refusal' | 'storage-loss' | 'sync-status' | 'service-mode' | 'member' | 'authority-changes' | 'query' | 'queue' | 'room-drafts' | 'presentation' | 'conversation' | 'conversations' | 'agents';
138
143
  export type ReadinessPort = {
139
144
  readonly target: string;
140
145
  readonly code: CodeAvailability;
141
- readonly space: Space | null;
142
- readonly search: SearchObligation;
143
146
  };
144
147
  export type ComposedRuntime = RuntimeView & {
145
148
  seed(entry: EntrySeed): void;
146
149
  stop(): Promise<void>;
147
- lose(locators: readonly string[]): Promise<void>;
148
150
  persisted(answer: Persistence): void;
149
- shown(onScreen: boolean): void;
150
151
  within(reach: PortReach | null, port: ReadinessPort): NarrowedView;
151
152
  };
152
153
  export declare function composeRuntime(options: MdRuntimeOptions): ComposedRuntime;