doxum 0.1.20 → 0.1.22

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 (65) hide show
  1. package/README.md +31 -4
  2. package/dist/{access-B9XDWjHy.js → access-CZXv_DIF.js} +238 -54
  3. package/dist/access-CZXv_DIF.js.map +1 -0
  4. package/dist/{access-EQ-XiFyl.cjs → access-D64MFRSm.cjs} +287 -55
  5. package/dist/access-D64MFRSm.cjs.map +1 -0
  6. package/dist/{advanced-Bcle5C2M.js → advanced-BTStm2cW.js} +29 -25
  7. package/dist/advanced-BTStm2cW.js.map +1 -0
  8. package/dist/{advanced-Du3O8O1A.d.ts → advanced-CqrR_4dh.d.ts} +3 -3
  9. package/dist/{advanced-CSjF-u_k.d.cts → advanced-CyR5o3Xr.d.cts} +3 -3
  10. package/dist/{advanced-BIGP86NL.cjs → advanced-GKZ-CZ6a.cjs} +29 -25
  11. package/dist/advanced-GKZ-CZ6a.cjs.map +1 -0
  12. package/dist/advanced.cjs +1 -1
  13. package/dist/advanced.d.cts +1 -1
  14. package/dist/advanced.d.ts +1 -1
  15. package/dist/advanced.js +1 -1
  16. package/dist/{contract-BlEl8Ibo.d.ts → contract-CRrZ-3sF.d.cts} +68 -36
  17. package/dist/{contract-BDQth1pe.d.cts → contract-CxiNcqMn.d.ts} +68 -36
  18. package/dist/{driver-DRoWiadc.cjs → driver-BC06FEMF.cjs} +2 -2
  19. package/dist/{driver-DRoWiadc.cjs.map → driver-BC06FEMF.cjs.map} +1 -1
  20. package/dist/{driver-DiN-0Mnc.js → driver-BqRjwFmU.js} +2 -2
  21. package/dist/{driver-DiN-0Mnc.js.map → driver-BqRjwFmU.js.map} +1 -1
  22. package/dist/index.cjs +482 -119
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.cts +2 -2
  25. package/dist/index.d.ts +2 -2
  26. package/dist/index.js +480 -117
  27. package/dist/index.js.map +1 -1
  28. package/dist/{integration-DL6dLuu1.cjs → integration-BqDw7e2w.cjs} +3 -4
  29. package/dist/{integration-DL6dLuu1.cjs.map → integration-BqDw7e2w.cjs.map} +1 -1
  30. package/dist/{integration-DHxVpbxc.js → integration-CSXaF8HP.js} +2 -3
  31. package/dist/{integration-DHxVpbxc.js.map → integration-CSXaF8HP.js.map} +1 -1
  32. package/dist/integration.cjs +5 -4
  33. package/dist/integration.d.cts +1 -1
  34. package/dist/integration.d.ts +1 -1
  35. package/dist/integration.js +4 -3
  36. package/dist/{issue-DVaGQGeP.js → issue-9U6YLYz3.js} +91 -20
  37. package/dist/issue-9U6YLYz3.js.map +1 -0
  38. package/dist/{issue-DhrNdQNg.cjs → issue-Bo0FIO3b.cjs} +116 -27
  39. package/dist/issue-Bo0FIO3b.cjs.map +1 -0
  40. package/dist/local-sync.cjs +2 -2
  41. package/dist/local-sync.d.cts +1 -1
  42. package/dist/local-sync.d.ts +1 -1
  43. package/dist/local-sync.js +2 -2
  44. package/dist/{notification-AXT94XYC.js → notification-CrZHVx3a.js} +3 -59
  45. package/dist/notification-CrZHVx3a.js.map +1 -0
  46. package/dist/{notification-DGv-_G9t.cjs → notification-DFoA9bQY.cjs} +3 -77
  47. package/dist/notification-DFoA9bQY.cjs.map +1 -0
  48. package/dist/react.cjs +4 -3
  49. package/dist/react.cjs.map +1 -1
  50. package/dist/react.js +3 -2
  51. package/dist/react.js.map +1 -1
  52. package/package.json +1 -1
  53. package/skills/doxum-runtime/SKILL.md +7 -0
  54. package/skills/doxum-runtime/references/guide.en.md +14 -2
  55. package/skills/doxum-runtime/references/guide.zh-CN.md +13 -2
  56. package/skills/doxum-runtime/references/projections.en.md +5 -0
  57. package/skills/doxum-runtime/references/projections.zh-CN.md +5 -0
  58. package/dist/access-B9XDWjHy.js.map +0 -1
  59. package/dist/access-EQ-XiFyl.cjs.map +0 -1
  60. package/dist/advanced-BIGP86NL.cjs.map +0 -1
  61. package/dist/advanced-Bcle5C2M.js.map +0 -1
  62. package/dist/issue-DVaGQGeP.js.map +0 -1
  63. package/dist/issue-DhrNdQNg.cjs.map +0 -1
  64. package/dist/notification-AXT94XYC.js.map +0 -1
  65. package/dist/notification-DGv-_G9t.cjs.map +0 -1
package/README.md CHANGED
@@ -100,9 +100,9 @@ capabilities while retaining selection, subscription and projection support.
100
100
  - `map(field(...))`, `map(object(...))`, `map(variant(...))` expose
101
101
  `get/has/ids/put/remove/replace`. `put` is an upsert; removing a missing key is a no-op.
102
102
  - `table(object(...))` retains `{ ids, byId }` data and exposes
103
- `get/has/ids/create/remove/move/replace`.
103
+ `get/has/ids/create/remove/move/reorder/replace`.
104
104
  - `list(field(...), { keyOf })` retains a plain array and exposes
105
- `get/has/ids/insert/remove/move/replace`.
105
+ `get/has/ids/insert/remove/move/reorder/replace`.
106
106
  - `tree(field(...))` retains `{ rootId?, nodes }` and exposes topology reads plus
107
107
  `insert/remove/move/replace`.
108
108
  - `variant(tag, branches)` has a readonly discriminant and whole-value branch replacement.
@@ -116,6 +116,20 @@ Doxum; it does not detect validator mutation or conversion. `parse(model, unknow
116
116
  validates and copies schema structure while sharing readonly payloads. Strict parsing
117
117
  requires validators for atomic fields; typed in-memory fields can omit them.
118
118
 
119
+ A tree may be empty, but payload presence follows the payload field schema exactly.
120
+ `tree(field<T>())` requires every existing node to own a `value` property;
121
+ `tree(optional(field<T>()))` is the explicit sparse-payload form. This is independent
122
+ from `optional(tree(...))`, which controls whether the whole tree member may be absent.
123
+ Missing and present-`undefined` payloads remain distinct when the payload field is optional.
124
+
125
+ Ordered table/list drafts accept either one key or a key selection in
126
+ `move(key | readonly key[], anchor?)`. A selection is moved as one block while
127
+ preserving its current canonical relative order; the anchor is resolved after the
128
+ selection is removed. Use `reorder(keys)` when the caller already owns the complete
129
+ final order: it requires an exact permutation of current membership and changes no
130
+ member values. Whole-collection `replace(value)` remains a distinct replacement
131
+ operation and is not an order primitive.
132
+
119
133
  Collection `replace` has two forms: `replace(id, value)` replaces one existing
120
134
  table/list/tree member without changing order or topology, while `replace(value)`
121
135
  replaces the entire map/table/list/tree. Use top-level `replace(parent, key, value)`
@@ -269,8 +283,21 @@ runtime.update(overrides, draft => {
269
283
  and eventful external sources. External sources declare `kind: 'value'` or
270
284
  `kind: 'collection'`; collection invalidation remains keyed internally. Schema
271
285
  `map`, `table`, and `list(field, { keyOf })` paths are all observed as keyed
272
- collection projections. A schema `list` uses its `keyOf` identity and publishes
273
- in document order; ordinary array-valued `field(...)` nodes remain scalar values.
286
+ collection projections. Trees expose structural observation without a second
287
+ projection protocol: `path.tree.rootId` is a scalar source,
288
+ `path.tree.nodes` is a keyed collection source, and
289
+ `path.tree.nodes.item(id)` is a single-node value source. A schema `list` uses its
290
+ `keyOf` identity and publishes in document order; ordinary array-valued `field(...)`
291
+ nodes remain scalar values.
292
+
293
+ Document projection sources route committed groups directly to their observed
294
+ targets, merge affected locations for the current projection batch, then combine the
295
+ previously published snapshot with the canonical final value. A touched tree node
296
+ replaces only that node structure and the necessary ancestor containers; unchanged
297
+ tree-node snapshots keep their identity. Whole aggregate tree values still use the
298
+ public `{ rootId?, nodes: Record }` shape, so changing a node may shallow-copy the
299
+ `nodes` record, while native `tree.nodes` observation remains keyed and touches only
300
+ changed nodes.
274
301
 
275
302
  For retained state, reverse indexes and keyed patches, use the isolated advanced
276
303
  entry point. Whole-value processors use `incremental(...)`; keyed collection
@@ -1,4 +1,4 @@
1
- import { A as insert$2, C as validate, D as equal, E as afterRemove, I as valid, L as profile, M as keys, O as index, P as move$2, T as isRecord, a as compiledShape, b as contains, g as resolveLocated, k as indexedKeys, m as resolveChild, n as fail, u as nodeAt, w as isPlainObject, x as parent, y as children } from "./issue-DVaGQGeP.js";
1
+ import { B as profile, C as validate, D as index, E as equal, F as matches, I as moveSelection, M as installList, N as keys, O as indexedKeys, P as listSequence, T as isRecord, a as compiledShape, b as contains, g as resolveLocated, i as AddressIndex, j as installKeys, k as insert$2, m as resolveChild, n as fail, u as nodeAt, w as isPlainObject, x as parent, y as children, z as valid } from "./issue-9U6YLYz3.js";
2
2
  //#region core/src/schema-value.ts
3
3
  var ParseError = class extends TypeError {
4
4
  issues;
@@ -103,14 +103,29 @@ const checkValue = (node, value, address = [], strict = false) => {
103
103
  return;
104
104
  }
105
105
  if (!validate(value)) return issue(address, "Invalid tree structure.", "invalid-tree");
106
- if (isRecord(value) && isRecord(value.nodes)) for (const id of Object.keys(value.nodes)) {
107
- const entry = value.nodes[id];
108
- if (isRecord(entry) && Object.prototype.hasOwnProperty.call(entry, "value")) {
109
- const failure = checkValue(node.value, entry.value, [...address, id], strict);
110
- if (failure) return failure;
111
- }
106
+ for (const id of Object.keys(value.nodes)) {
107
+ const failure = checkTreePayload(node.value, value.nodes[id], [...address, id], strict);
108
+ if (failure) return failure;
112
109
  }
113
110
  };
111
+ const checkTreePayload = (node, value, address, strict = false) => {
112
+ if (!isRecord(value)) return issue(address, "Invalid tree node.", "invalid-tree");
113
+ const present = Object.prototype.hasOwnProperty.call(value, "value");
114
+ if (!present && !node.optional) return issue(address, "Tree node value is required.");
115
+ return present ? checkValue(node, value.value, address, strict) : void 0;
116
+ };
117
+ const copyTreeNode = (value) => {
118
+ profile.copy.treeNode();
119
+ return {
120
+ ...value,
121
+ children: [...value.children]
122
+ };
123
+ };
124
+ const equalTreeNode = (left, right) => {
125
+ if (left === right) return true;
126
+ if (!left || !right) return false;
127
+ return left.parentId === right.parentId && equal(left.children, right.children) && Object.hasOwn(left, "value") === Object.hasOwn(right, "value") && Object.is(left.value, right.value);
128
+ };
114
129
  /** Copy editable structure; atomic payloads retain their immutable ownership contract. */
115
130
  const copyValue = (node, value) => {
116
131
  if (value === void 0 || node.kind === "field") return value;
@@ -139,10 +154,7 @@ const copyValue = (node, value) => {
139
154
  const entries = value.nodes;
140
155
  return {
141
156
  ...value,
142
- nodes: Object.fromEntries(Object.entries(entries).map(([id, n]) => [id, {
143
- ...n,
144
- children: [...n.children]
145
- }]))
157
+ nodes: Object.fromEntries(Object.entries(entries).map(([id, n]) => [id, copyTreeNode(n)]))
146
158
  };
147
159
  }
148
160
  return value;
@@ -166,7 +178,7 @@ const equalValue = (node, left, right) => {
166
178
  if (node.kind === "tree" && isRecord(left) && isRecord(right)) {
167
179
  if (left.rootId !== right.rootId) return false;
168
180
  const a = left.nodes, b = right.nodes;
169
- return Object.keys(a).length === Object.keys(b).length && Object.keys(a).every((id) => Object.hasOwn(b, id) && a[id].parentId === b[id].parentId && equal(a[id].children, b[id].children) && Object.hasOwn(a[id], "value") === Object.hasOwn(b[id], "value") && equalValue(node.value, a[id].value, b[id].value));
181
+ return Object.keys(a).length === Object.keys(b).length && Object.keys(a).every((id) => Object.hasOwn(b, id) && equalTreeNode(a[id], b[id]));
170
182
  }
171
183
  if (node.kind === "map" && isRecord(left) && isRecord(right)) {
172
184
  const keys = Object.keys(left);
@@ -235,12 +247,12 @@ function insert$1(session, container, value, position) {
235
247
  session.validate(node.value, value, at);
236
248
  const id = node.keyOf(value);
237
249
  if (typeof id !== "string") return fail(at, "invalid-list-key", "List key must be a string.");
238
- const items = current, keys$2 = keys(items, node.keyOf);
239
- if (keys$2.index(id) >= 0) return fail(at, "duplicate-list-item", "List key already exists.");
240
- if (!valid(keys$2, position)) return fail(at, "invalid-anchor", "Unknown list anchor.");
250
+ const items = current, keys$1 = keys(items, node.keyOf);
251
+ if (keys$1.index(id) >= 0) return fail(at, "duplicate-list-item", "List key already exists.");
252
+ if (!valid(keys$1, position)) return fail(at, "invalid-anchor", "Unknown list anchor.");
241
253
  session.recorder.order(container);
242
254
  session.recorder.member(container, id, session.definition(container, id), -1);
243
- insert$2(items, index(keys$2, position), value);
255
+ insert$2(items, index(keys$1, position), value);
244
256
  session.invalidate();
245
257
  }
246
258
  function replace$2(session, container, id, value) {
@@ -338,21 +350,185 @@ const move$1 = (session, container, id, position) => {
338
350
  };
339
351
  //#endregion
340
352
  //#region core/src/mutation/operations/order.ts
341
- function move(session, container, id, position) {
342
- const { at, node, value } = container;
353
+ const assertOrdered = (container) => {
354
+ const { at, node } = container;
343
355
  if (node.kind !== "table" && node.kind !== "list") return fail(at, "invalid-collection", "Expected an ordered container.");
344
- const items = node.kind === "table" ? value.ids : value;
345
- const keys$1 = node.kind === "table" ? items : keys(items, node.keyOf);
346
- const index = node.kind === "table" ? items.indexOf(id) : keys$1.index(id);
347
- if (index < 0) return fail(at, "missing-entity", "Ordered key does not exist.");
348
- if (!valid(keys$1, position)) return fail(at, "invalid-anchor", "Unknown order anchor.");
349
- const next = afterRemove(keys$1, index, position);
350
- if (next === index) return;
351
- session.recorder.order(container);
352
- move$2(items, index, next);
356
+ return node;
357
+ };
358
+ const plannedOrder = (at, current, selection, position) => {
359
+ const plan = moveSelection(current, selection, position);
360
+ switch (plan.kind) {
361
+ case "ok": return plan.order;
362
+ case "duplicate": return fail(at, "invalid-key", "Moved keys must be unique.");
363
+ case "missing": return fail(at, "missing-entity", "Ordered key does not exist.");
364
+ case "invalid-anchor": return fail(at, "invalid-anchor", "Unknown order anchor.");
365
+ }
366
+ };
367
+ function move(session, container, ids, position) {
368
+ const node = assertOrdered(container);
369
+ const selection = typeof ids === "string" ? [ids] : ids;
370
+ if (!selection.length) return;
371
+ if (node.kind === "table") {
372
+ const order = container.value.ids;
373
+ const next = plannedOrder(container.at, order, selection, position);
374
+ if (equal(order, next)) return;
375
+ session.recorder.order(container);
376
+ installKeys(order, next);
377
+ } else {
378
+ const items = container.value;
379
+ const sequence = listSequence(items, node.keyOf);
380
+ const next = plannedOrder(container.at, sequence.order, selection, position);
381
+ if (equal(sequence.order, next)) return;
382
+ session.recorder.order(container);
383
+ installList(items, node.keyOf, sequence, next);
384
+ }
385
+ session.invalidate();
386
+ }
387
+ function reorder(session, container, next) {
388
+ const node = assertOrdered(container);
389
+ if (node.kind === "table") {
390
+ const current = container.value.ids;
391
+ if (!matches(next, current)) return fail(container.at, "invalid-collection", "Order must contain every key exactly once.");
392
+ if (equal(current, next)) return;
393
+ session.recorder.order(container);
394
+ installKeys(current, next);
395
+ } else {
396
+ const items = container.value;
397
+ const sequence = listSequence(items, node.keyOf);
398
+ if (!matches(next, sequence.order)) return fail(container.at, "invalid-collection", "Order must contain every key exactly once.");
399
+ if (equal(sequence.order, next)) return;
400
+ session.recorder.order(container);
401
+ installList(items, node.keyOf, sequence, next);
402
+ }
353
403
  session.invalidate();
354
404
  }
355
405
  //#endregion
406
+ //#region core/src/impact-target.ts
407
+ const treeRootSegment = "rootId";
408
+ const treeNodesSegment = "nodes";
409
+ const ordinaryNamespace = "\0value";
410
+ const treeNamespace = "\0tree";
411
+ const tree = (at, selection) => ({
412
+ kind: "value",
413
+ at,
414
+ tree: selection
415
+ });
416
+ const address = (target) => "at" in target ? target.at : target.address;
417
+ const id = (target) => target.kind === "collection" && "id" in target ? target.id : void 0;
418
+ const belongs = (target, schema) => !("schema" in target) || target.schema === schema;
419
+ const treeTarget = (target) => "tree" in target ? target.tree : void 0;
420
+ const sameTreeTarget = (left, right) => {
421
+ if (!left || !right) return left === right;
422
+ if (left.kind !== right.kind) return false;
423
+ return left.kind !== "node" || right.kind === "node" && left.id === right.id;
424
+ };
425
+ const same = (left, right) => {
426
+ if ("schema" in left && "schema" in right && left.schema !== right.schema) return false;
427
+ if (!sameTreeTarget(treeTarget(left), treeTarget(right))) return false;
428
+ if (left.kind !== right.kind) return false;
429
+ const leftAddress = address(left);
430
+ const rightAddress = address(right);
431
+ if (leftAddress.length !== rightAddress.length) return false;
432
+ for (let index = 0; index < leftAddress.length; index += 1) if (leftAddress[index] !== rightAddress[index]) return false;
433
+ return left.kind !== "collection" || id(left) === id(right);
434
+ };
435
+ const indexedAddress = (target) => {
436
+ const tree = treeTarget(target);
437
+ if (tree?.kind === "root") return [
438
+ treeNamespace,
439
+ ...address(target),
440
+ treeRootSegment
441
+ ];
442
+ if (tree?.kind === "nodes") return [
443
+ treeNamespace,
444
+ ...address(target),
445
+ treeNodesSegment
446
+ ];
447
+ if (tree?.kind === "node") return [
448
+ treeNamespace,
449
+ ...address(target),
450
+ treeNodesSegment,
451
+ tree.id
452
+ ];
453
+ const key = id(target);
454
+ return key === void 0 ? [ordinaryNamespace, ...address(target)] : [
455
+ ordinaryNamespace,
456
+ ...address(target),
457
+ key
458
+ ];
459
+ };
460
+ const visitChangedLocations = (changes, value, order) => {
461
+ for (const change of changes.changes) {
462
+ if (change.kind === "reset") continue;
463
+ if (change.kind === "members") {
464
+ for (const member of change.members) {
465
+ value([
466
+ ordinaryNamespace,
467
+ ...change.at,
468
+ member.key
469
+ ]);
470
+ value([
471
+ treeNamespace,
472
+ ...change.at,
473
+ member.key
474
+ ]);
475
+ }
476
+ if (change.order) order([ordinaryNamespace, ...change.at]);
477
+ continue;
478
+ }
479
+ value([ordinaryNamespace, ...change.at]);
480
+ if (change.before !== change.after) value([
481
+ treeNamespace,
482
+ ...change.at,
483
+ treeRootSegment
484
+ ]);
485
+ for (const node of change.nodes) value([
486
+ treeNamespace,
487
+ ...change.at,
488
+ treeNodesSegment,
489
+ node.id
490
+ ]);
491
+ }
492
+ };
493
+ const affected = (target, values, orders) => {
494
+ const at = indexedAddress(target);
495
+ return id(target) !== void 0 ? values.hasAncestor(at) : values.overlaps(at) || orders.hasDescendant(at);
496
+ };
497
+ /** Targets are classified once; commit matching never builds a reverse impact index. */
498
+ var SubscriptionIndex = class {
499
+ ordinary = new AddressIndex();
500
+ membership = new AddressIndex();
501
+ constructor(schema) {
502
+ this.schema = schema;
503
+ }
504
+ add(target, value) {
505
+ if (!belongs(target, this.schema)) return;
506
+ (id(target) === void 0 ? this.ordinary : this.membership).add(indexedAddress(target), value);
507
+ }
508
+ delete(target, value) {
509
+ if (!belongs(target, this.schema)) return;
510
+ (id(target) === void 0 ? this.ordinary : this.membership).delete(indexedAddress(target), value);
511
+ }
512
+ collect(changes, visit) {
513
+ const values = this.ordinary.query(visit);
514
+ const members = this.membership.query(visit, "descendants");
515
+ const orders = this.ordinary.query(visit, "ancestors");
516
+ if (changes.changes.some((change) => change.kind === "reset")) {
517
+ values([]);
518
+ members([]);
519
+ return;
520
+ }
521
+ visitChangedLocations(changes, (at) => {
522
+ values(at);
523
+ members(at);
524
+ }, orders);
525
+ }
526
+ clear() {
527
+ this.ordinary.clear();
528
+ this.membership.clear();
529
+ }
530
+ };
531
+ //#endregion
356
532
  //#region core/src/access/scope.ts
357
533
  const locations = /* @__PURE__ */ new WeakMap();
358
534
  const locationOf = (value) => locations.get(value);
@@ -374,6 +550,7 @@ const collect = (context, at, kind = "value", id) => {
374
550
  ...id === void 0 ? {} : { id }
375
551
  });
376
552
  };
553
+ const collectTree = (context, at, selection) => context.dependencies?.record(tree(at, selection));
377
554
  const snapshot = (value) => {
378
555
  const location = value && typeof value === "object" ? locationOf(value) : void 0;
379
556
  if (!location) return value;
@@ -546,9 +723,13 @@ const createAccess = (context, initial = []) => {
546
723
  if (!location || !Object.hasOwn(location.parent, location.key)) return void 0;
547
724
  return childAccess(target, id, location.node, location.parent[location.key]);
548
725
  };
549
- if (property === "move") return (id, position) => {
726
+ if (property === "move") return (ids, position) => {
550
727
  const writable = writableCollection(target, node);
551
- move(context.session, writable, id, position);
728
+ move(context.session, writable, ids, position);
729
+ };
730
+ if (property === "reorder") return (ids) => {
731
+ const writable = writableCollection(target, node);
732
+ reorder(context.session, writable, ids);
552
733
  };
553
734
  };
554
735
  const tableMethod = (target, property, node) => {
@@ -608,18 +789,38 @@ const createAccess = (context, initial = []) => {
608
789
  const writable = writableTree(target, node);
609
790
  replace$1(context.session, writable, valueOrId, value);
610
791
  };
611
- const current = () => {
612
- collect(context, at);
613
- return collectionValue(target, node);
792
+ const current = () => collectionValue(target, node);
793
+ if (property === "rootId") return () => {
794
+ collectTree(context, at, { kind: "root" });
795
+ return current().rootId;
614
796
  };
615
- if (property === "rootId") return () => current().rootId;
616
797
  if (property === "get") return (id) => {
798
+ collectTree(context, at, {
799
+ kind: "node",
800
+ id
801
+ });
617
802
  const value = current();
618
803
  return contains(value, id) ? value.nodes[id].value : void 0;
619
804
  };
620
- if (property === "has") return (id) => contains(current(), id);
621
- if (property === "parent") return (id) => parent(current(), id);
805
+ if (property === "has") return (id) => {
806
+ collectTree(context, at, {
807
+ kind: "node",
808
+ id
809
+ });
810
+ return contains(current(), id);
811
+ };
812
+ if (property === "parent") return (id) => {
813
+ collectTree(context, at, {
814
+ kind: "node",
815
+ id
816
+ });
817
+ return parent(current(), id);
818
+ };
622
819
  if (property === "children") return (id) => {
820
+ collectTree(context, at, {
821
+ kind: "node",
822
+ id
823
+ });
623
824
  const children$1 = children(current(), id);
624
825
  return children$1 ? [...children$1] : void 0;
625
826
  };
@@ -699,23 +900,6 @@ const createAccess = (context, initial = []) => {
699
900
  }
700
901
  return access(root);
701
902
  };
702
- const collectionAccess = (context, at) => {
703
- const value = createAccess(context, at);
704
- if (value === void 0) return {
705
- get: () => {
706
- assertActive(context);
707
- },
708
- has: () => {
709
- assertActive(context);
710
- return false;
711
- },
712
- ids: () => {
713
- assertActive(context);
714
- return [];
715
- }
716
- };
717
- return value;
718
- };
719
903
  //#endregion
720
904
  //#region core/src/runtime/contract.ts
721
905
  var DocumentReentrancyError = class extends Error {
@@ -769,6 +953,6 @@ const readWith = (runtime, run, dependencies) => {
769
953
  }
770
954
  };
771
955
  //#endregion
772
- export { DocumentReentrancyError as a, createAccess as c, ParseError as d, checkKey as f, parse as g, equalValue as h, DocumentDisposedError as i, replace as l, copyValue as m, bindRuntimeAccess as n, TransactionRejected as o, checkValue as p, readWith as r, collectionAccess as s, accessOf as t, snapshot as u };
956
+ export { equalValue as C, equalTreeNode as S, checkKey as _, DocumentReentrancyError as a, copyTreeNode as b, replace as c, affected as d, belongs as f, ParseError as g, visitChangedLocations as h, DocumentDisposedError as i, snapshot as l, same as m, bindRuntimeAccess as n, TransactionRejected as o, indexedAddress as p, readWith as r, createAccess as s, accessOf as t, SubscriptionIndex as u, checkTreePayload as v, parse as w, copyValue as x, checkValue as y };
773
957
 
774
- //# sourceMappingURL=access-B9XDWjHy.js.map
958
+ //# sourceMappingURL=access-CZXv_DIF.js.map