@agentshouse/mdruntime 0.1.0-c037654ea7e04 → 0.1.0-c2647c1a232dd

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.
package/AGENTS.md CHANGED
@@ -79,22 +79,28 @@ data:
79
79
  - **Filter.** `rooms` names Room locators and defaults to every Room. `types`
80
80
  names MD Types, and `any` names untyped Markdown Documents. `pathPrefix` is a
81
81
  directory: `notes` keeps `notes/a.md` and not `notes2/a.md`.
82
- - **Fields.** `where` names frontmatter fields, ANDs them, and nests `$and` and
83
- `$or`. A bare value is equality; the operators are `$eq`, `$ne`, `$in`, `$nin`,
84
- `$gt`, `$gte`, `$lt`, `$lte`, `$all`, `$size` and `$exists`. Equality on a list
85
- asks whether it contains the value. Missing, null and empty differ:
82
+ - **Fields.** Every frontmatter value is named by JSON Pointer: `/status`,
83
+ `/shipping/city`, `~1` for a `/` and `~0` for a `~` in a key. A segment that
84
+ meets a list applies to every item and a number names one item. `where` names
85
+ pointers, ANDs them, and nests `$and` and `$or`; a pointer through a list holds
86
+ when any item satisfies it. A bare value is equality; the operators are `$eq`,
87
+ `$ne`, `$in`, `$nin`, `$gt`, `$gte`, `$lt`, `$lte`, `$all`, `$size` and
88
+ `$exists`. Equality on a list asks whether it contains the value. Missing, null and empty differ:
86
89
  `$exists: false` keeps a missing field, `null` an explicit null, `''` empty
87
90
  text and `$size: 0` an empty list. A date compares in calendar order and never
88
91
  with a date-time. A reference compares by the Room Path it resolves to.
89
- Untyped frontmatter is plain values. There is no text containment or regular
92
+ Untyped frontmatter, and a value under `oneOf` or a conditional, is plain
93
+ values. There is no text containment or regular
90
94
  expression.
91
95
  - **Relations.** `hop: { field, types, where, pathPrefix }` keeps Documents whose
92
- reference `field` points to a matching Document of the same Room.
93
- `backlinks: { field, types, where, pathPrefix }` keeps Documents that a
96
+ reference at pointer `field`, at any depth, points to a matching Document of the
97
+ same Room. `backlinks: { field, types, where, pathPrefix }` keeps Documents that a
94
98
  matching Document of the same Room references. Each keeps a Document once.
95
99
  - **Parts.** A Document query must `select`: `'all'`, or `fields`, `sections`,
96
- `preamble`, `markdown`, `backlinks` and `change`, where `fields` and `sections`
97
- are `'all'` or a list of names. Room and Path always return; a Markdown
100
+ `preamble`, `markdown`, `backlinks` and `change`, where `fields` is `'all'` or a
101
+ list of pointers and `sections` `'all'` or a list of names. Selected pointers
102
+ return the frontmatter holding only them, nested as written. A backlink names
103
+ the referring Path, MD Type and the pointer it came from as `field`. Room and Path always return; a Markdown
98
104
  Document also returns its MD Type. A non-Markdown Document or binary link
99
105
  target returns only Room and Path. An unselected part is absent. Select only
100
106
  what a screen shows: a list that selects the Markdown hears every edit of it.
@@ -108,8 +114,8 @@ data:
108
114
  - **Order and groups.** `orderBy` sorts by `room`, `path`, `type` or `{ field }`
109
115
  with TanStack DB's own options, and ties end with Room, then Path. `groupBy` by
110
116
  `room`, `type` or `{ field }` answers `{ groups: [{ key, count }] }`, counted
111
- over every match and ordered by `count` or a key. `limit` bounds the Documents
112
- or the groups.
117
+ over every match and ordered by `count` or a key. A sort or group takes only a
118
+ pointer that holds one value. `limit` bounds the Documents or the groups.
113
119
  - **Results and errors.** A result carries no Room Version: read freshness from
114
120
  Rooms and Sync Status. An invalid query fails with `invalid-query`,
115
121
  `unknown-type`, `unknown-field` or `wrong-kind`, naming where, and never
package/dist/query.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Definition } from '@agentshouse/mdmodel';
1
+ import { type Definition } from '@agentshouse/mdmodel';
2
2
  import type { HeadChange } from './adapter.js';
3
3
  export type Value = string | number | boolean | null;
4
4
  export type Predicate = Value | {
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
package/dist/query.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { ARRAY_INDEX, itemFacts, memberFacts, pointerSegments, } from '@agentshouse/mdmodel';
1
2
  import { referenceField } from './model.js';
2
3
  import { normalize } from './paths.js';
3
4
  export class QueryError extends Error {
@@ -12,6 +13,7 @@ const RELATED = ['types', 'where', 'pathPrefix', 'field'];
12
13
  const PARTS = ['fields', 'sections', 'preamble', 'markdown', 'backlinks', 'change'];
13
14
  const ORDER = ['by', 'direction', 'nulls', 'stringSort', 'locale', 'localeOptions'];
14
15
  const CALENDAR_DATE = /^(\d{4})-(\d{2})-(\d{2})$/;
16
+ const STRAY_ESCAPE = /~(?![01])/u;
15
17
  export function invalid(at, problem) {
16
18
  throw new QueryError('invalid-query', at, problem);
17
19
  }
@@ -109,23 +111,75 @@ function typing(facts, rooms, types, at) {
109
111
  types: types === null ? declared : declared.filter((type) => types.includes(type.name)),
110
112
  };
111
113
  }
112
- function specs(typed, field, at) {
113
- 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
+ });
114
155
  if (declared.length === 0 && !typed.untyped) {
115
156
  throw new QueryError('unknown-field', at, `no MD Type it reads declares ${field}`);
116
157
  }
117
- 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);
118
174
  }
119
175
  function kind(declared, checked, at) {
120
- 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))) {
121
179
  return;
122
180
  }
123
181
  throw new QueryError('wrong-kind', at, `${JSON.stringify(checked)} is not of its field's kind`);
124
182
  }
125
- function listed(declared, list) {
126
- return (declared.length > 0 &&
127
- declared.every((facts) => facts.type !== undefined && (facts.type === 'array') === list));
128
- }
129
183
  function test(declared, input, at) {
130
184
  if (!plain(input)) {
131
185
  const equal = value(input, at);
@@ -139,8 +193,11 @@ function test(declared, input, at) {
139
193
  const stray = operators.find((operator) => !RANGE.includes(operator));
140
194
  if (stray !== undefined)
141
195
  invalid(place(at, stray), 'cannot join a range');
142
- if (listed(declared, true))
196
+ if (every(declared, (spec) => spec.facts.type === 'array'))
143
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
+ }
144
201
  return {
145
202
  op: 'range',
146
203
  bounds: RANGE.filter((operator) => operators.includes(operator)).map((operator) => {
@@ -173,7 +230,7 @@ function test(declared, input, at) {
173
230
  return { op: 'nin', values: compared };
174
231
  if (compared.includes(null))
175
232
  invalid(where, 'a list holds no null');
176
- if (listed(declared, false))
233
+ if (every(declared, single))
177
234
  invalid(where, 'the field is not a list');
178
235
  return { op: 'all', values: compared };
179
236
  }
@@ -181,7 +238,7 @@ function test(declared, input, at) {
181
238
  if (!Number.isSafeInteger(operand) || operand < 0) {
182
239
  invalid(where, 'expects a nonnegative integer');
183
240
  }
184
- if (listed(declared, false))
241
+ if (every(declared, single))
185
242
  invalid(where, 'the field is not a list');
186
243
  return { op: 'size', size: operand };
187
244
  }
@@ -194,7 +251,7 @@ function condition(typed, input, at) {
194
251
  invalid(at, 'expects an object of field predicates');
195
252
  const conditions = [];
196
253
  for (const name of Object.keys(input).sort()) {
197
- const where = place(at, name);
254
+ const where = name.startsWith('/') ? `${at}${name}` : place(at, name);
198
255
  const entry = input[name];
199
256
  if (name === '$and' || name === '$or') {
200
257
  const nested = list(entry, where).map((item, index) => condition(typed, item, `${where}[${index}]`));
@@ -224,11 +281,11 @@ function scope(facts, rooms, input, at) {
224
281
  };
225
282
  }
226
283
  function referenceName(typed, field, at) {
227
- if (typeof field !== 'string')
228
- invalid(at, 'expects a field name');
229
284
  const declared = specs(typed, field, at);
230
- if (declared.length > 0 && !declared.some(referenceField))
285
+ if (declared.length > 0 &&
286
+ !declared.some((spec) => spec.conditional || referenceField(spec.facts))) {
231
287
  invalid(at, 'not a reference field');
288
+ }
232
289
  return field;
233
290
  }
234
291
  function hopOf(facts, rooms, root, input) {
@@ -250,8 +307,10 @@ function backlinksOf(facts, rooms, input) {
250
307
  function keyPlan(typed, input, at, path) {
251
308
  if (input === 'room' || input === 'type' || (path && input === 'path'))
252
309
  return { of: input };
253
- if (plain(input) && typeof input.field === 'string' && Object.keys(input).length === 1) {
254
- 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
+ }
255
314
  return { of: 'field', field: input.field };
256
315
  }
257
316
  return invalid(at, path ? 'expects room, path, type or a field' : 'expects room, type or a field');
@@ -309,10 +368,10 @@ function parts(typed, input, at, fields) {
309
368
  return 'all';
310
369
  const named = list(input, at);
311
370
  named.forEach((name, index) => {
312
- if (typeof name !== 'string')
313
- invalid(`${at}[${index}]`, 'expects a name');
314
371
  if (fields)
315
372
  specs(typed, name, `${at}[${index}]`);
373
+ else if (typeof name !== 'string')
374
+ invalid(`${at}[${index}]`, 'expects a name');
316
375
  });
317
376
  return named;
318
377
  }
@@ -1 +1 @@
1
- {"version":3,"file":"read-side.d.ts","sourceRoot":"","sources":["../src/read-side.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAU,KAAK,WAAW,EAAE,KAAK,WAAW,EAAe,MAAM,kBAAkB,CAAC;AAC3F,OAAO,KAAK,EAOV,IAAI,EAIJ,WAAW,EAIZ,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAqDpD,MAAM,MAAM,QAAQ,GAAG;IACrB,KAAK,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACxC,KAAK,CACH,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,CAAC,OAAO,EAAE,WAAW,GAAG,WAAW,KAAK,IAAI,GACpD,MAAM,IAAI,CAAC;IACd,SAAS,IAAI,SAAS,gBAAgB,EAAE,CAAC;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAAC;IAC9D,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AA8RF,wBAAgB,UAAU,CACxB,QAAQ,EAAE,WAAW,EACrB,IAAI,EAAE,WAAW,GAChB;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GAAG,IAAI,CA4BpD;AAED,wBAAgB,cAAc,IAAI,QAAQ,CA4HzC"}
1
+ {"version":3,"file":"read-side.d.ts","sourceRoot":"","sources":["../src/read-side.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAU,KAAK,WAAW,EAAE,KAAK,WAAW,EAAe,MAAM,kBAAkB,CAAC;AAC3F,OAAO,KAAK,EAOV,IAAI,EAIJ,WAAW,EAIZ,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAuDpD,MAAM,MAAM,QAAQ,GAAG;IACrB,KAAK,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACxC,KAAK,CACH,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,CAAC,OAAO,EAAE,WAAW,GAAG,WAAW,KAAK,IAAI,GACpD,MAAM,IAAI,CAAC;IACd,SAAS,IAAI,SAAS,gBAAgB,EAAE,CAAC;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAAC;IAC9D,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AA+UF,wBAAgB,UAAU,CACxB,QAAQ,EAAE,WAAW,EACrB,IAAI,EAAE,WAAW,GAChB;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GAAG,IAAI,CA4BpD;AAED,wBAAgB,cAAc,IAAI,QAAQ,CA4HzC"}
package/dist/read-side.js CHANGED
@@ -1,54 +1,58 @@
1
- import { and, BasicIndex, configureIndexDevMode, count, createCollection, createLiveQueryCollection, deepEquals, eq, gt, gte, inArray, isNull, isUndefined, lt, lte, not, or, toArray, } from '@tanstack/db';
1
+ import { and, BasicIndex, configureIndexDevMode, count, createCollection, createLiveQueryCollection, deepEquals, eq, inArray, or, toArray, } from '@tanstack/db';
2
+ import { ARRAY_INDEX, pointerSegments } from '@agentshouse/mdmodel';
2
3
  import { rowsOf } from './observation.js';
3
4
  import { documentKey } from './rows.js';
4
5
  function every(expressions) {
5
6
  const [first, second, ...rest] = expressions;
6
7
  return second === undefined ? first : and(first, second, ...rest);
7
8
  }
8
- function some(expressions) {
9
- const [first, second, ...rest] = expressions;
10
- return second === undefined ? first : or(first, second, ...rest);
9
+ function same(held, value) {
10
+ return Array.isArray(held) ? held.includes(value) : held === value;
11
11
  }
12
- function equal(field, value) {
13
- return or(eq(field, value), inArray(value, field));
12
+ function ordered(held, [operator, bound]) {
13
+ if (typeof held !== typeof bound || held === null || typeof held === 'object')
14
+ return false;
15
+ const left = held;
16
+ const right = bound;
17
+ if (operator === 'gt')
18
+ return left > right;
19
+ if (operator === 'gte')
20
+ return left >= right;
21
+ if (operator === 'lt')
22
+ return left < right;
23
+ return left <= right;
14
24
  }
15
- function tested(test, field, size, written) {
25
+ function satisfied(test, held) {
16
26
  if (test.op === 'eq')
17
- return test.value === null ? isNull(field) : equal(field, test.value);
18
- if (test.op === 'ne') {
19
- return test.value === null
20
- ? not(isNull(field))
21
- : or(isUndefined(field), isNull(field), not(equal(field, test.value)));
22
- }
23
- if (test.op === 'in' || test.op === 'nin') {
24
- const matches = test.values.length === 0
25
- ? inArray(field, [])
26
- : some(test.values.map((value) => (value === null ? isNull(field) : equal(field, value))));
27
- if (test.op === 'in')
28
- return matches;
29
- return some([
30
- isUndefined(field),
31
- ...(test.values.includes(null) ? [] : [isNull(field)]),
32
- not(matches),
33
- ]);
34
- }
35
- if (test.op === 'range') {
36
- const compare = { gt, gte, lt, lte };
37
- return every(test.bounds.map(([operator, bound]) => compare[operator](field, bound)));
38
- }
39
- if (test.op === 'all') {
40
- return every([not(isUndefined(size)), ...test.values.map((value) => inArray(value, field))]);
41
- }
42
- if (test.op === 'size')
43
- return eq(size, test.size);
44
- return test.exists ? not(isUndefined(written)) : isUndefined(written);
27
+ return same(held, test.value);
28
+ if (test.op === 'ne')
29
+ return !same(held, test.value);
30
+ if (test.op === 'in')
31
+ return test.values.some((value) => same(held, value));
32
+ if (test.op === 'nin')
33
+ return !test.values.some((value) => same(held, value));
34
+ if (test.op === 'range')
35
+ return test.bounds.every((bound) => ordered(held, bound));
36
+ if (test.op === 'all')
37
+ return Array.isArray(held) && test.values.every((value) => same(held, value));
38
+ return Array.isArray(held) && held.length === test.size;
39
+ }
40
+ function tested(test, reached) {
41
+ if (test.op === 'exists')
42
+ return test.exists === (reached !== undefined);
43
+ if (reached === undefined)
44
+ return test.op === 'ne' || test.op === 'nin';
45
+ return reached.some((held) => satisfied(test, held));
45
46
  }
46
47
  function conditioned(condition, row) {
47
48
  if ('every' in condition)
48
- return every(condition.every.map((item) => conditioned(item, row)));
49
+ return condition.every.every((item) => conditioned(item, row));
49
50
  if ('some' in condition)
50
- return some(condition.some.map((item) => conditioned(item, row)));
51
- return tested(condition.test, row.values[condition.field], row.sizes[condition.field], row.fields[condition.field]);
51
+ return condition.some.some((item) => conditioned(item, row));
52
+ return tested(condition.test, row.reached[condition.field]);
53
+ }
54
+ function narrowed(query, where, alias) {
55
+ return where === null ? query : query.fn.where((row) => conditioned(where, row[alias]));
52
56
  }
53
57
  function reached(reach, room, document) {
54
58
  return reach === null
@@ -62,7 +66,6 @@ function scoped(scope, row, rooms, reach) {
62
66
  ...reached(reach, row.room, row.key),
63
67
  ...(scope.types === null ? [] : [inArray(row.type, [...scope.types])]),
64
68
  ...(scope.pathPrefix === null ? [] : [inArray(scope.pathPrefix, row.directories)]),
65
- ...(scope.where === null ? [] : [conditioned(scope.where, row)]),
66
69
  ]);
67
70
  }
68
71
  function keyed(key, row) {
@@ -77,16 +80,17 @@ function live(query) {
77
80
  function related(q, rows, scope, field, kept, reach) {
78
81
  const matched = kept === 'source' ? 'target' : 'source';
79
82
  const from = q.from({ r: rows });
80
- return from
81
- .where(({ r }) => every([eq(r.kind, 'reference'), ...(field === null ? [] : [eq(r.field, field)])]))
83
+ const joined = from
84
+ .where(({ r }) => every([eq(r.kind, 'reference'), ...(field === null ? [] : [inArray(field, r.forms)])]))
82
85
  .innerJoin({ m: rows }, ({ r, m }) => eq(r[matched], m.key))
83
- .where(({ m }) => scoped(scope, m, null, reach))
86
+ .where(({ m }) => scoped(scope, m, null, reach));
87
+ return narrowed(joined, scope.where, 'm')
84
88
  .groupBy(({ r }) => r[kept])
85
89
  .select(({ r }) => ({ key: r[kept] }));
86
90
  }
87
91
  function filtered(q, rows, plan) {
88
92
  const from = q.from({ d: rows });
89
- let query = from.where(({ d }) => scoped(plan.root, d, plan.rooms, plan.reach));
93
+ let query = narrowed(from.where(({ d }) => scoped(plan.root, d, plan.rooms, plan.reach)), plan.root.where, 'd');
90
94
  if (plan.hop !== null) {
91
95
  const hop = related(q, rows, plan.hop, plan.hop.field, 'source', plan.reach);
92
96
  query = query.innerJoin({ h: hop }, ({ d, h }) => eq(d.key, h.key));
@@ -117,8 +121,7 @@ function documents(q, rows, plan, shape) {
117
121
  room: d.room,
118
122
  path: d.path,
119
123
  type: d.type,
120
- ...(shape.fields === 'all' ? { fields: d.fields } : {}),
121
- ...(Array.isArray(shape.fields) ? named('field', shape.fields, d.fields) : {}),
124
+ ...(shape.fields === null ? {} : { fields: d.fields }),
122
125
  ...(shape.sections === 'all' ? { sections: b.sections } : {}),
123
126
  ...(Array.isArray(shape.sections) ? named('section', shape.sections, b.sections) : {}),
124
127
  ...(shape.preamble ? { preamble: b.preamble } : {}),
@@ -176,6 +179,55 @@ function part(row, prefix, names, whole) {
176
179
  return whole;
177
180
  return present(names.map((name, index) => [name, row[`${prefix}${index}`]]));
178
181
  }
182
+ function masked(pointers) {
183
+ const root = { whole: false, members: new Map() };
184
+ for (const pointer of pointers) {
185
+ let mask = root;
186
+ for (const token of pointerSegments(pointer)) {
187
+ const next = mask.members.get(token) ?? { whole: false, members: new Map() };
188
+ mask.members.set(token, next);
189
+ mask = next;
190
+ }
191
+ mask.whole = true;
192
+ }
193
+ return root;
194
+ }
195
+ function merged(left, right) {
196
+ if (left === undefined)
197
+ return right;
198
+ const members = new Map(left.members);
199
+ for (const [token, mask] of right.members)
200
+ members.set(token, merged(members.get(token), mask));
201
+ return { whole: left.whole || right.whole, members };
202
+ }
203
+ function cut(value, mask) {
204
+ if (mask.whole)
205
+ return value;
206
+ if (Array.isArray(value)) {
207
+ const each = {
208
+ whole: false,
209
+ members: new Map([...mask.members].filter(([token]) => !ARRAY_INDEX.test(token))),
210
+ };
211
+ const kept = [];
212
+ value.forEach((item, index) => {
213
+ const part = cut(item, merged(mask.members.get(String(index)), each));
214
+ if (part !== undefined)
215
+ kept[index] = part;
216
+ });
217
+ return kept.length === 0 ? undefined : kept;
218
+ }
219
+ if (typeof value !== 'object' || value === null)
220
+ return undefined;
221
+ const kept = Object.entries(value).flatMap(([key, held]) => {
222
+ const member = mask.members.get(key);
223
+ const part = member === undefined ? undefined : cut(held, member);
224
+ return part === undefined ? [] : [[key, part]];
225
+ });
226
+ return kept.length === 0 ? undefined : Object.fromEntries(kept);
227
+ }
228
+ function selected(fields, pointers) {
229
+ return (cut(fields, masked(pointers)) ?? {});
230
+ }
179
231
  function documentOf(row, shape) {
180
232
  if (row.type === null)
181
233
  return { room: row.room, path: row.path };
@@ -185,7 +237,11 @@ function documentOf(row, shape) {
185
237
  type: row.type,
186
238
  ...(shape.fields === null
187
239
  ? {}
188
- : { fields: part(row, 'field', shape.fields, row.fields) }),
240
+ : {
241
+ fields: shape.fields === 'all'
242
+ ? row.fields
243
+ : selected(row.fields, shape.fields),
244
+ }),
189
245
  ...(shape.sections === null
190
246
  ? {}
191
247
  : {
package/dist/rows.d.ts CHANGED
@@ -10,7 +10,7 @@ export type DocumentRow = {
10
10
  type: string | null;
11
11
  fields: Fields;
12
12
  values: Fields;
13
- sizes: Readonly<Record<string, number>>;
13
+ reached: Readonly<Record<string, readonly unknown[]>>;
14
14
  directories: readonly string[];
15
15
  body: string;
16
16
  change: HeadChange | null;
@@ -31,6 +31,7 @@ export type ReferenceRow = {
31
31
  source: string;
32
32
  target: string;
33
33
  field: string;
34
+ forms: readonly string[];
34
35
  path: string;
35
36
  type: string;
36
37
  };
@@ -1 +1 @@
1
- {"version":3,"file":"rows.d.ts","sourceRoot":"","sources":["../src/rows.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAG9D,KAAK,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAEhD,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACxC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,GAAG,GAAG,WAAW,GAAG,OAAO,GAAG,YAAY,CAAC;AAMvD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9D;AAiBD,wBAAgB,MAAM,CACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,QAAQ,GAAG,IAAI,EACzB,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,UAAU,GAAG,IAAI,GACxB,SAAS,GAAG,EAAE,CAsEhB"}
1
+ {"version":3,"file":"rows.d.ts","sourceRoot":"","sources":["../src/rows.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,QAAQ,EACd,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAG9D,KAAK,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAEhD,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC,CAAC,CAAC;IACtD,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,GAAG,GAAG,WAAW,GAAG,OAAO,GAAG,YAAY,CAAC;AAMvD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9D;AAmFD,wBAAgB,MAAM,CACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,QAAQ,GAAG,IAAI,EACzB,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,UAAU,GAAG,IAAI,GACxB,SAAS,GAAG,EAAE,CAuEhB"}
package/dist/rows.js CHANGED
@@ -1,21 +1,65 @@
1
- import { target } from '@agentshouse/mdmodel';
1
+ import { ARRAY_INDEX, escapePointer, references, target, } from '@agentshouse/mdmodel';
2
2
  import { governing } from './collection.js';
3
- import { definitionOf, referenceField } from './model.js';
3
+ import { definitionOf } from './model.js';
4
4
  function keyOf(...parts) {
5
5
  return `${parts.join('\u0000')}\u0000`;
6
6
  }
7
7
  export function documentKey(room, path) {
8
8
  return keyOf('d', room, path);
9
9
  }
10
- function targetOf(path, value) {
11
- return typeof value === 'string' ? target(path, value) : null;
10
+ function plainObject(value) {
11
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
12
12
  }
13
- function resolved(path, value) {
14
- if (value === null)
15
- return null;
16
- if (!Array.isArray(value))
17
- return targetOf(path, value) ?? undefined;
18
- return value.map((item) => targetOf(path, item)).filter((item) => item !== null);
13
+ function compared(reading, exact, value) {
14
+ if (!reading.references.has(exact))
15
+ return value;
16
+ return (typeof value === 'string' ? target(reading.path, value) : null) ?? undefined;
17
+ }
18
+ function record(reading, node, forms, exact) {
19
+ const value = Array.isArray(node)
20
+ ? node.map((item, index) => compared(reading, `${exact}/${index}`, item))
21
+ : compared(reading, exact, node);
22
+ for (const form of forms) {
23
+ if (form.pending)
24
+ continue;
25
+ (reading.reached[form.text] ??= []).push(value);
26
+ if (!form.through && value !== undefined && !plainObject(value)) {
27
+ reading.values[form.text] = value;
28
+ }
29
+ }
30
+ if (Array.isArray(node) || plainObject(node) || !reading.references.has(exact))
31
+ return;
32
+ const found = compared(reading, exact, node);
33
+ if (typeof found !== 'string')
34
+ return;
35
+ const field = forms
36
+ .map((form) => form.text)
37
+ .reduce((left, right) => (right.length < left.length ? right : left));
38
+ const known = reading.targets.get(`${field}\u0000${found}`);
39
+ const kept = known ?? { field, target: found, forms: new Set() };
40
+ for (const form of forms)
41
+ kept.forms.add(form.text);
42
+ reading.targets.set(`${field}\u0000${found}`, kept);
43
+ }
44
+ function walk(reading, node, forms, exact) {
45
+ record(reading, node, forms, exact);
46
+ if (Array.isArray(node)) {
47
+ node.forEach((item, index) => walk(reading, item, forms.flatMap((form) => [
48
+ ...(form.pending
49
+ ? []
50
+ : [{ text: `${form.text}/${index}`, through: form.through, pending: false }]),
51
+ { text: form.text, through: true, pending: true },
52
+ ]), `${exact}/${index}`));
53
+ return;
54
+ }
55
+ if (!plainObject(node))
56
+ return;
57
+ for (const [key, value] of Object.entries(node)) {
58
+ const segment = escapePointer(key);
59
+ walk(reading, value, forms.flatMap((form) => form.pending && ARRAY_INDEX.test(key)
60
+ ? []
61
+ : [{ text: `${form.text}/${segment}`, through: form.through, pending: false }]), `${exact}/${segment}`);
62
+ }
19
63
  }
20
64
  function directoriesOf(path) {
21
65
  const parts = path.split('/').slice(0, -1);
@@ -34,7 +78,7 @@ export function rowsOf(room, path, document, composition, change) {
34
78
  type: null,
35
79
  fields: {},
36
80
  values: {},
37
- sizes: {},
81
+ reached: {},
38
82
  directories: directoriesOf(path),
39
83
  body,
40
84
  change,
@@ -43,32 +87,18 @@ export function rowsOf(room, path, document, composition, change) {
43
87
  }
44
88
  const type = document.type ?? 'any';
45
89
  const definition = definitionOf(governing(composition, path), document.type);
46
- const values = {};
47
- const sizes = {};
48
- const references = [];
90
+ const reading = {
91
+ path,
92
+ references: new Set(definition === undefined ? [] : references(document, definition).map((one) => one.pointer)),
93
+ values: {},
94
+ reached: {},
95
+ targets: new Map(),
96
+ };
49
97
  for (const [name, value] of Object.entries(document.fields)) {
50
- const reference = referenceField(definition?.fields[name]);
51
- const compared = reference ? resolved(path, value) : value;
52
- if (compared !== undefined)
53
- values[name] = compared;
54
- if (Array.isArray(value))
55
- sizes[name] = value.length;
56
- if (!reference)
57
- continue;
58
- const targets = [compared].flat().filter((found) => typeof found === 'string');
59
- for (const found of new Set(targets)) {
60
- references.push({
61
- key: keyOf('r', room, path, name, found),
62
- kind: 'reference',
63
- room,
64
- source: key,
65
- target: documentKey(room, found),
66
- field: name,
67
- path,
68
- type,
69
- });
70
- }
98
+ const segment = escapePointer(name);
99
+ walk(reading, value, [{ text: `/${segment}`, through: false, pending: false }], `/${segment}`);
71
100
  }
101
+ const { values, reached } = reading;
72
102
  return [
73
103
  {
74
104
  key,
@@ -78,7 +108,7 @@ export function rowsOf(room, path, document, composition, change) {
78
108
  type,
79
109
  fields: document.fields,
80
110
  values,
81
- sizes,
111
+ reached,
82
112
  directories: directoriesOf(path),
83
113
  body,
84
114
  change,
@@ -92,6 +122,16 @@ export function rowsOf(room, path, document, composition, change) {
92
122
  sections: document.sections,
93
123
  markdown: document.content,
94
124
  },
95
- ...references,
125
+ ...[...reading.targets.values()].map(({ field, target: found, forms }) => ({
126
+ key: keyOf('r', room, path, field, found),
127
+ kind: 'reference',
128
+ room,
129
+ source: key,
130
+ target: documentKey(room, found),
131
+ field,
132
+ forms: [...forms].sort(),
133
+ path,
134
+ type,
135
+ })),
96
136
  ];
97
137
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentshouse/mdruntime",
3
- "version": "0.1.0-c037654ea7e04",
3
+ "version": "0.1.0-c2647c1a232dd",
4
4
  "description": "The headless core of MD App",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -41,7 +41,7 @@
41
41
  "LICENSE"
42
42
  ],
43
43
  "dependencies": {
44
- "@agentshouse/mdmodel": "0.1.0-c2987f5d89720",
44
+ "@agentshouse/mdmodel": "0.1.0-c0bdbbc92d020",
45
45
  "@tanstack/db": "0.9.2",
46
46
  "minisearch": "7.2.0"
47
47
  },