@dxos/echo 0.8.4-main.422d1c7879 → 0.8.4-main.4f23b4e393

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 (217) hide show
  1. package/README.md +2 -2
  2. package/dist/lib/neutral/Annotation.mjs +5 -3
  3. package/dist/lib/neutral/Database.mjs +4 -4
  4. package/dist/lib/neutral/Entity.mjs +14 -14
  5. package/dist/lib/neutral/Err.mjs +1 -1
  6. package/dist/lib/neutral/Extension.mjs +1 -1
  7. package/dist/lib/neutral/Feed.mjs +19 -17
  8. package/dist/lib/neutral/Filter.mjs +11 -11
  9. package/dist/lib/neutral/Format.mjs +3 -3
  10. package/dist/lib/neutral/JsonSchema.mjs +8 -8
  11. package/dist/lib/neutral/Key.mjs +1 -1
  12. package/dist/lib/neutral/Migration.mjs +10 -10
  13. package/dist/lib/neutral/Obj.mjs +14 -14
  14. package/dist/lib/neutral/Order.mjs +1 -1
  15. package/dist/lib/neutral/Query.mjs +17 -17
  16. package/dist/lib/neutral/QueryResult.mjs +1 -1
  17. package/dist/lib/neutral/Ref.mjs +9 -7
  18. package/dist/lib/neutral/Relation.mjs +15 -15
  19. package/dist/lib/neutral/SchemaRegistry.mjs +1 -1
  20. package/dist/lib/neutral/Tag.mjs +14 -14
  21. package/dist/lib/neutral/Type.mjs +10 -10
  22. package/dist/lib/neutral/{chunk-GZQTCRJB.mjs → chunk-44HT3MEC.mjs} +2 -2
  23. package/dist/lib/neutral/{chunk-4P3IXBLT.mjs → chunk-5SL5LDLD.mjs} +4 -2
  24. package/dist/lib/neutral/chunk-5SL5LDLD.mjs.map +7 -0
  25. package/dist/lib/neutral/{chunk-ANHVGJI4.mjs → chunk-7RVZT53K.mjs} +1 -1
  26. package/dist/lib/neutral/{chunk-A2WIDONZ.mjs → chunk-AHWSAJHU.mjs} +6 -22
  27. package/dist/lib/neutral/{chunk-A2WIDONZ.mjs.map → chunk-AHWSAJHU.mjs.map} +2 -2
  28. package/dist/lib/neutral/{chunk-XEU2Q5CI.mjs → chunk-APHSOTIX.mjs} +2 -2
  29. package/dist/lib/neutral/{chunk-BNCCGLJN.mjs → chunk-BICZKPQG.mjs} +1 -1
  30. package/dist/lib/neutral/{chunk-JPWH3QJD.mjs → chunk-BMB7IHGB.mjs} +10 -66
  31. package/dist/lib/neutral/{chunk-JPWH3QJD.mjs.map → chunk-BMB7IHGB.mjs.map} +2 -2
  32. package/dist/lib/neutral/{chunk-EJJFH3XB.mjs → chunk-C6LDQTNO.mjs} +10 -10
  33. package/dist/lib/neutral/chunk-C6LDQTNO.mjs.map +7 -0
  34. package/dist/lib/neutral/{chunk-GMYNDEMT.mjs → chunk-GKBXQFQK.mjs} +11 -27
  35. package/dist/lib/neutral/{chunk-GMYNDEMT.mjs.map → chunk-GKBXQFQK.mjs.map} +3 -3
  36. package/dist/lib/neutral/{chunk-YS6Q3XAD.mjs → chunk-GWFFC34K.mjs} +1 -1
  37. package/dist/lib/neutral/{chunk-YS6Q3XAD.mjs.map → chunk-GWFFC34K.mjs.map} +1 -1
  38. package/dist/lib/neutral/{chunk-PHU22NLC.mjs → chunk-I2DARWPX.mjs} +4 -12
  39. package/dist/lib/neutral/chunk-I2DARWPX.mjs.map +7 -0
  40. package/dist/lib/neutral/{chunk-RP37BQF7.mjs → chunk-KBZ4RAXR.mjs} +20 -16
  41. package/dist/lib/neutral/chunk-KBZ4RAXR.mjs.map +7 -0
  42. package/dist/lib/neutral/{chunk-QW7YUKSA.mjs → chunk-MLS7U7AT.mjs} +12 -65
  43. package/dist/lib/neutral/{chunk-QW7YUKSA.mjs.map → chunk-MLS7U7AT.mjs.map} +2 -2
  44. package/dist/lib/neutral/{chunk-TBKX6JQO.mjs → chunk-N4B7FHQT.mjs} +1 -1
  45. package/dist/lib/neutral/{chunk-H5F7C2UO.mjs → chunk-N7VOEPSV.mjs} +6 -3
  46. package/dist/lib/neutral/{chunk-H5F7C2UO.mjs.map → chunk-N7VOEPSV.mjs.map} +3 -3
  47. package/dist/lib/neutral/{chunk-ZUGFUCUK.mjs → chunk-NWXJZVWD.mjs} +8 -8
  48. package/dist/lib/neutral/{chunk-ZUGFUCUK.mjs.map → chunk-NWXJZVWD.mjs.map} +3 -3
  49. package/dist/lib/neutral/{chunk-JIN4EVUK.mjs → chunk-OMJN6VCR.mjs} +4 -4
  50. package/dist/lib/neutral/{chunk-EULNZHTP.mjs → chunk-PHOHAIEE.mjs} +4 -4
  51. package/dist/lib/neutral/{chunk-7I3YIAT2.mjs → chunk-QRZ2I3ZM.mjs} +2 -2
  52. package/dist/lib/neutral/{chunk-6ASBHRDG.mjs → chunk-SRX3EZY3.mjs} +38 -17
  53. package/dist/lib/neutral/chunk-SRX3EZY3.mjs.map +7 -0
  54. package/dist/lib/neutral/{chunk-WAK4DMFV.mjs → chunk-SUZMWP3Y.mjs} +2 -2
  55. package/dist/lib/neutral/{chunk-2KHZ36F5.mjs → chunk-TNBK56IN.mjs} +10 -21
  56. package/dist/lib/neutral/{chunk-2KHZ36F5.mjs.map → chunk-TNBK56IN.mjs.map} +3 -3
  57. package/dist/lib/neutral/{chunk-DQYLD2RB.mjs → chunk-TRPZU2HV.mjs} +2 -2
  58. package/dist/lib/neutral/{chunk-UI6MWK5W.mjs → chunk-TTCSATUD.mjs} +1 -1
  59. package/dist/lib/neutral/{chunk-OMUPQMLR.mjs → chunk-V72DY6LU.mjs} +1 -1
  60. package/dist/lib/neutral/{chunk-O26EPPBW.mjs → chunk-W7U2XGZ3.mjs} +11 -19
  61. package/dist/lib/neutral/{chunk-O26EPPBW.mjs.map → chunk-W7U2XGZ3.mjs.map} +3 -3
  62. package/dist/lib/neutral/{chunk-UBEZSGXY.mjs → chunk-ZISMEVKD.mjs} +1 -1
  63. package/dist/lib/neutral/{chunk-UBEZSGXY.mjs.map → chunk-ZISMEVKD.mjs.map} +2 -2
  64. package/dist/lib/neutral/{chunk-BQN7EDPH.mjs → chunk-ZVQ26DCR.mjs} +76 -478
  65. package/dist/lib/neutral/{chunk-BQN7EDPH.mjs.map → chunk-ZVQ26DCR.mjs.map} +3 -3
  66. package/dist/lib/neutral/index.mjs +28 -28
  67. package/dist/lib/neutral/internal/index.mjs +11 -9
  68. package/dist/lib/neutral/meta.json +1 -1
  69. package/dist/lib/neutral/testing/index.mjs +183 -129
  70. package/dist/lib/neutral/testing/index.mjs.map +3 -3
  71. package/dist/types/src/Annotation.d.ts +1 -1
  72. package/dist/types/src/Annotation.d.ts.map +1 -1
  73. package/dist/types/src/Collection.d.ts.map +1 -1
  74. package/dist/types/src/Database.d.ts +1 -1
  75. package/dist/types/src/Database.d.ts.map +1 -1
  76. package/dist/types/src/Dataset.d.ts +1 -1
  77. package/dist/types/src/Entity.d.ts +9 -9
  78. package/dist/types/src/Entity.d.ts.map +1 -1
  79. package/dist/types/src/Err.d.ts +18 -18
  80. package/dist/types/src/Err.d.ts.map +1 -1
  81. package/dist/types/src/Extension.d.ts +4 -4
  82. package/dist/types/src/Extension.d.ts.map +1 -1
  83. package/dist/types/src/Feed.d.ts +37 -13
  84. package/dist/types/src/Feed.d.ts.map +1 -1
  85. package/dist/types/src/Filter.d.ts.map +1 -1
  86. package/dist/types/src/Hypergraph.d.ts +3 -3
  87. package/dist/types/src/Hypergraph.d.ts.map +1 -1
  88. package/dist/types/src/Json.d.ts.map +1 -1
  89. package/dist/types/src/JsonSchema.d.ts +1 -1
  90. package/dist/types/src/Migration.d.ts.map +1 -1
  91. package/dist/types/src/Obj.d.ts +19 -19
  92. package/dist/types/src/Obj.d.ts.map +1 -1
  93. package/dist/types/src/Order.d.ts.map +1 -1
  94. package/dist/types/src/Query.d.ts +5 -1
  95. package/dist/types/src/Query.d.ts.map +1 -1
  96. package/dist/types/src/Ref.d.ts +1 -0
  97. package/dist/types/src/Ref.d.ts.map +1 -1
  98. package/dist/types/src/Relation.d.ts +15 -15
  99. package/dist/types/src/Relation.d.ts.map +1 -1
  100. package/dist/types/src/Tag.d.ts +2 -2
  101. package/dist/types/src/Tag.d.ts.map +1 -1
  102. package/dist/types/src/Type.d.ts.map +1 -1
  103. package/dist/types/src/View.d.ts +1 -1
  104. package/dist/types/src/View.d.ts.map +1 -1
  105. package/dist/types/src/exemplars.test.d.ts +2 -0
  106. package/dist/types/src/exemplars.test.d.ts.map +1 -0
  107. package/dist/types/src/internal/Annotation/annotations.d.ts +11 -2
  108. package/dist/types/src/internal/Annotation/annotations.d.ts.map +1 -1
  109. package/dist/types/src/internal/Annotation/sorting.d.ts.map +1 -1
  110. package/dist/types/src/internal/Annotation/util.d.ts +1 -1
  111. package/dist/types/src/internal/Annotation/util.d.ts.map +1 -1
  112. package/dist/types/src/internal/Entity/api.d.ts.map +1 -1
  113. package/dist/types/src/internal/Entity/relation.d.ts.map +1 -1
  114. package/dist/types/src/internal/Entity/version.d.ts.map +1 -1
  115. package/dist/types/src/internal/Format/date.d.ts.map +1 -1
  116. package/dist/types/src/internal/Format/format.d.ts.map +1 -1
  117. package/dist/types/src/internal/Format/number.d.ts.map +1 -1
  118. package/dist/types/src/internal/Format/object.d.ts.map +1 -1
  119. package/dist/types/src/internal/Format/types.d.ts.map +1 -1
  120. package/dist/types/src/internal/JsonSchema/json-schema-normalize.d.ts.map +1 -1
  121. package/dist/types/src/internal/JsonSchema/json-schema-type.d.ts +28 -28
  122. package/dist/types/src/internal/JsonSchema/json-schema-type.d.ts.map +1 -1
  123. package/dist/types/src/internal/JsonSchema/json-schema.d.ts +1 -1
  124. package/dist/types/src/internal/JsonSchema/json-schema.d.ts.map +1 -1
  125. package/dist/types/src/internal/Obj/clone.d.ts.map +1 -1
  126. package/dist/types/src/internal/Obj/common.d.ts.map +1 -1
  127. package/dist/types/src/internal/Obj/create-object.d.ts.map +1 -1
  128. package/dist/types/src/internal/Obj/deleted.d.ts.map +1 -1
  129. package/dist/types/src/internal/Obj/ids.d.ts +1 -1
  130. package/dist/types/src/internal/Obj/ids.d.ts.map +1 -1
  131. package/dist/types/src/internal/Obj/json-serializer.d.ts.map +1 -1
  132. package/dist/types/src/internal/Obj/set-value.d.ts +1 -1
  133. package/dist/types/src/internal/Obj/set-value.d.ts.map +1 -1
  134. package/dist/types/src/internal/Obj/snapshot.d.ts.map +1 -1
  135. package/dist/types/src/internal/Query.d.ts.map +1 -1
  136. package/dist/types/src/internal/Ref/ref.d.ts.map +1 -1
  137. package/dist/types/src/internal/Type/compose.d.ts.map +1 -1
  138. package/dist/types/src/internal/Type/echo-schema.d.ts +1 -1
  139. package/dist/types/src/internal/Type/echo-schema.d.ts.map +1 -1
  140. package/dist/types/src/internal/Type/manipulation.d.ts.map +1 -1
  141. package/dist/types/src/internal/common/api/meta.d.ts +3 -3
  142. package/dist/types/src/internal/common/api/meta.d.ts.map +1 -1
  143. package/dist/types/src/internal/common/proxy/change-context.d.ts +1 -1
  144. package/dist/types/src/internal/common/proxy/change-context.d.ts.map +1 -1
  145. package/dist/types/src/internal/common/proxy/define-hidden-property.d.ts.map +1 -1
  146. package/dist/types/src/internal/common/proxy/errors.d.ts +1 -1
  147. package/dist/types/src/internal/common/proxy/errors.d.ts.map +1 -1
  148. package/dist/types/src/internal/common/proxy/event-batch.d.ts.map +1 -1
  149. package/dist/types/src/internal/common/proxy/json-serializer.d.ts.map +1 -1
  150. package/dist/types/src/internal/common/proxy/ownership.d.ts.map +1 -1
  151. package/dist/types/src/internal/common/proxy/proxy-utils.d.ts.map +1 -1
  152. package/dist/types/src/internal/common/proxy/reactive-array.d.ts +1 -1
  153. package/dist/types/src/internal/common/proxy/reactive.d.ts +1 -1
  154. package/dist/types/src/internal/common/proxy/reactive.d.ts.map +1 -1
  155. package/dist/types/src/internal/common/proxy/schema-validator.d.ts.map +1 -1
  156. package/dist/types/src/internal/common/proxy/typed-handler.d.ts.map +1 -1
  157. package/dist/types/src/internal/common/types/base.d.ts.map +1 -1
  158. package/dist/types/src/internal/common/types/entity.d.ts +3 -3
  159. package/dist/types/src/internal/common/types/meta.d.ts.map +1 -1
  160. package/dist/types/src/internal/common/types/version.d.ts +1 -1
  161. package/dist/types/src/testing/test-data.d.ts +8 -8
  162. package/dist/types/src/testing/test-data.d.ts.map +1 -1
  163. package/dist/types/src/testing/test-schema.d.ts +53 -53
  164. package/dist/types/src/testing/test-schema.d.ts.map +1 -1
  165. package/dist/types/src/testing/util.d.ts.map +1 -1
  166. package/dist/types/tsconfig.tsbuildinfo +1 -1
  167. package/package.json +13 -13
  168. package/src/Annotation.ts +1 -0
  169. package/src/Collection.ts +1 -1
  170. package/src/Database.ts +1 -1
  171. package/src/Entity.ts +9 -9
  172. package/src/Extension.ts +3 -3
  173. package/src/Feed.ts +55 -24
  174. package/src/Hypergraph.ts +3 -3
  175. package/src/Obj.test.ts +12 -12
  176. package/src/Obj.ts +19 -19
  177. package/src/Query.test.ts +32 -16
  178. package/src/Query.ts +32 -7
  179. package/src/Ref.ts +2 -0
  180. package/src/Relation.ts +15 -15
  181. package/src/exemplars.test.ts +21 -0
  182. package/src/internal/Annotation/annotations.ts +16 -3
  183. package/src/internal/Obj/ids.ts +1 -1
  184. package/src/internal/Obj/json-serializer.test.ts +1 -1
  185. package/src/internal/Obj/json-serializer.ts +9 -9
  186. package/src/internal/Obj/set-value.test.ts +15 -15
  187. package/src/internal/Obj/set-value.ts +1 -1
  188. package/src/internal/Query.ts +7 -4
  189. package/src/internal/Type/echo-schema.ts +1 -1
  190. package/src/internal/common/README.md +1 -1
  191. package/src/internal/common/api/meta.ts +3 -3
  192. package/src/internal/common/proxy/change-context.ts +1 -1
  193. package/src/internal/common/proxy/change.test.ts +59 -59
  194. package/src/internal/common/proxy/errors.ts +2 -2
  195. package/src/internal/common/proxy/reactive-array.ts +1 -1
  196. package/src/internal/common/proxy/reactive.test.ts +3 -3
  197. package/src/internal/common/proxy/reactive.ts +1 -1
  198. package/src/internal/common/proxy/typed-handler.ts +7 -7
  199. package/src/internal/common/proxy/typed-object.test.ts +1 -1
  200. package/src/testing/test-data.ts +157 -97
  201. package/dist/lib/neutral/chunk-4P3IXBLT.mjs.map +0 -7
  202. package/dist/lib/neutral/chunk-6ASBHRDG.mjs.map +0 -7
  203. package/dist/lib/neutral/chunk-EJJFH3XB.mjs.map +0 -7
  204. package/dist/lib/neutral/chunk-PHU22NLC.mjs.map +0 -7
  205. package/dist/lib/neutral/chunk-RP37BQF7.mjs.map +0 -7
  206. /package/dist/lib/neutral/{chunk-GZQTCRJB.mjs.map → chunk-44HT3MEC.mjs.map} +0 -0
  207. /package/dist/lib/neutral/{chunk-ANHVGJI4.mjs.map → chunk-7RVZT53K.mjs.map} +0 -0
  208. /package/dist/lib/neutral/{chunk-XEU2Q5CI.mjs.map → chunk-APHSOTIX.mjs.map} +0 -0
  209. /package/dist/lib/neutral/{chunk-BNCCGLJN.mjs.map → chunk-BICZKPQG.mjs.map} +0 -0
  210. /package/dist/lib/neutral/{chunk-TBKX6JQO.mjs.map → chunk-N4B7FHQT.mjs.map} +0 -0
  211. /package/dist/lib/neutral/{chunk-JIN4EVUK.mjs.map → chunk-OMJN6VCR.mjs.map} +0 -0
  212. /package/dist/lib/neutral/{chunk-EULNZHTP.mjs.map → chunk-PHOHAIEE.mjs.map} +0 -0
  213. /package/dist/lib/neutral/{chunk-7I3YIAT2.mjs.map → chunk-QRZ2I3ZM.mjs.map} +0 -0
  214. /package/dist/lib/neutral/{chunk-WAK4DMFV.mjs.map → chunk-SUZMWP3Y.mjs.map} +0 -0
  215. /package/dist/lib/neutral/{chunk-DQYLD2RB.mjs.map → chunk-TRPZU2HV.mjs.map} +0 -0
  216. /package/dist/lib/neutral/{chunk-UI6MWK5W.mjs.map → chunk-TTCSATUD.mjs.map} +0 -0
  217. /package/dist/lib/neutral/{chunk-OMUPQMLR.mjs.map → chunk-V72DY6LU.mjs.map} +0 -0
package/src/Query.ts CHANGED
@@ -207,6 +207,11 @@ export interface Query<T> {
207
207
  * Add options to a query.
208
208
  */
209
209
  'options'(options: QueryAST.QueryOptions): Query<T>;
210
+
211
+ /**
212
+ * Attach a diagnostic label for logs and tooling (execution semantics unchanged).
213
+ */
214
+ 'debugLabel'(label: string): Query<T>;
210
215
  }
211
216
 
212
217
  export type Any = Query<any>;
@@ -354,7 +359,7 @@ class QueryClass implements Any {
354
359
  from: {
355
360
  _tag: 'scope',
356
361
  scope: {
357
- ...(options?.includeFeeds ? { allQueuesFromSpaces: true } : {}),
362
+ ...(options?.includeFeeds ? { allFeedsFromSpaces: true } : {}),
358
363
  },
359
364
  },
360
365
  });
@@ -379,7 +384,7 @@ class QueryClass implements Any {
379
384
  _tag: 'scope',
380
385
  scope: {
381
386
  spaceIds: databases.map((db) => db.spaceId),
382
- ...(options?.includeFeeds ? { allQueuesFromSpaces: true } : {}),
387
+ ...(options?.includeFeeds ? { allFeedsFromSpaces: true } : {}),
383
388
  },
384
389
  },
385
390
  });
@@ -405,10 +410,15 @@ class QueryClass implements Any {
405
410
  }
406
411
  }
407
412
 
408
- const feeds = items as Feed.Feed[];
409
- const queueDxns = feeds.flatMap((feed) => {
413
+ const feedItems = items as Feed.Feed[];
414
+ const feedDXNs = feedItems.map((feed) => {
410
415
  const dxn = Feed.getQueueDxn(feed);
411
- return dxn ? [dxn.toString()] : [];
416
+ if (!dxn) {
417
+ throw new TypeError(
418
+ `Query.from() expects persisted Feed objects with a queue DXN; got feed without a space (id=${Obj.getDXN(feed).toString()}).`,
419
+ );
420
+ }
421
+ return dxn.toString();
412
422
  });
413
423
  return new QueryClass({
414
424
  type: 'from',
@@ -416,7 +426,7 @@ class QueryClass implements Any {
416
426
  from: {
417
427
  _tag: 'scope',
418
428
  scope: {
419
- queues: queueDxns,
429
+ feeds: feedDXNs,
420
430
  },
421
431
  },
422
432
  });
@@ -429,6 +439,21 @@ class QueryClass implements Any {
429
439
  options,
430
440
  });
431
441
  }
442
+
443
+ debugLabel(label: string): Any {
444
+ if (this.ast.type === 'options') {
445
+ return new QueryClass({
446
+ type: 'options',
447
+ query: this.ast.query,
448
+ options: { ...this.ast.options, debugLabel: label },
449
+ });
450
+ }
451
+ return new QueryClass({
452
+ type: 'options',
453
+ query: this.ast,
454
+ options: { debugLabel: label },
455
+ });
456
+ }
432
457
  }
433
458
 
434
459
  export const is = (value: unknown): value is Any => {
@@ -531,7 +556,7 @@ export const from = (
531
556
  return wrapper.from(source as any, options);
532
557
  };
533
558
 
534
- const SCOPE_KEYS = new Set(['spaceIds', 'queues', 'allQueuesFromSpaces']);
559
+ const SCOPE_KEYS = new Set(['spaceIds', 'feeds', 'allFeedsFromSpaces']);
535
560
 
536
561
  /** Detect a raw Scope object (plain object with only Scope-valid keys). */
537
562
  const _isScope = (value: unknown): value is QueryAST.Scope => {
package/src/Ref.ts CHANGED
@@ -85,6 +85,8 @@ export const make = refInternal.Ref.make;
85
85
  // TODO(dmaretskyi): Consider just allowing `make` to accept DXN.
86
86
  export const fromDXN = refInternal.Ref.fromDXN;
87
87
 
88
+ export const hasObjectId = refInternal.Ref.hasObjectId;
89
+
88
90
  // TODO(wittjosiah): Factor out?
89
91
  export const isRefType = (ast: SchemaAST.AST): boolean => {
90
92
  return SchemaAST.getAnnotation<JsonSchema.JsonSchema>(ast, SchemaAST.JSONSchemaAnnotationId).pipe(
package/src/Relation.ts CHANGED
@@ -264,7 +264,7 @@ export const getTarget = <T extends Unknown | Snapshot>(relation: T): TargetOf<T
264
264
 
265
265
  /**
266
266
  * Makes all properties mutable recursively.
267
- * Used to provide a mutable view of a relation within `Relation.change`.
267
+ * Used to provide a mutable view of a relation within `Relation.update`.
268
268
  */
269
269
  export type Mutable<T> = internal.Mutable<T>;
270
270
 
@@ -272,10 +272,10 @@ export type Mutable<T> = internal.Mutable<T>;
272
272
  * Perform mutations on an echo relation within a controlled context.
273
273
  *
274
274
  * All mutations within the callback are batched and trigger a single notification
275
- * when the callback completes. Direct mutations outside of `Relation.change` will throw
275
+ * when the callback completes. Direct mutations outside of `Relation.update` will throw
276
276
  * an error for echo relations.
277
277
  *
278
- * @param relation - The echo relation to mutate. Use `Obj.change` for objects.
278
+ * @param relation - The echo relation to mutate. Use `Obj.update` for objects.
279
279
  * @param callback - The callback that performs mutations on the relation.
280
280
  *
281
281
  * @example
@@ -286,15 +286,15 @@ export type Mutable<T> = internal.Mutable<T>;
286
286
  * role: 'Engineer',
287
287
  * });
288
288
  *
289
- * // Mutate within Relation.change
290
- * Relation.change(worksFor, (obj) => {
289
+ * // Mutate within Relation.update
290
+ * Relation.update(worksFor, (obj) => {
291
291
  * obj.role = 'Senior Engineer';
292
292
  * });
293
293
  * ```
294
294
  *
295
- * Note: Only accepts relations. Use `Obj.change` for objects.
295
+ * Note: Only accepts relations. Use `Obj.update` for objects.
296
296
  */
297
- export const change = <T extends Unknown>(relation: T, callback: internal.ChangeCallback<T>): void => {
297
+ export const update = <T extends Unknown>(relation: T, callback: internal.ChangeCallback<T>): void => {
298
298
  internal.change(relation, callback);
299
299
  };
300
300
 
@@ -337,7 +337,7 @@ export const getValue = (rel: Unknown | Snapshot, path: readonly (string | numbe
337
337
 
338
338
  /**
339
339
  * Set a deeply nested property on a relation.
340
- * Must be called within a `Relation.change` callback.
340
+ * Must be called within a `Relation.update` callback.
341
341
  *
342
342
  * NOTE: TypeScript's structural typing allows readonly objects to be passed to `Mutable<T>`
343
343
  * parameters, so there is no compile-time error. Enforcement is runtime-only.
@@ -399,13 +399,13 @@ export const Meta = internal.MetaId;
399
399
  export type ReadonlyMeta = internal.ReadonlyMeta;
400
400
 
401
401
  /**
402
- * Mutable meta type returned by `Relation.getMeta` inside a `Relation.change` callback.
402
+ * Mutable meta type returned by `Relation.getMeta` inside a `Relation.update` callback.
403
403
  */
404
404
  export type Meta = internal.Meta;
405
405
 
406
406
  /**
407
407
  * Get the metadata for a relation.
408
- * Returns mutable meta when passed a mutable relation (inside `Relation.change` callback).
408
+ * Returns mutable meta when passed a mutable relation (inside `Relation.update` callback).
409
409
  * Returns read-only meta when passed a regular relation or snapshot.
410
410
  */
411
411
  // TODO(wittjosiah): When passed a Snapshot, should return a snapshot of meta, not the live meta proxy.
@@ -423,7 +423,7 @@ export const getKeys = (entity: Unknown | Snapshot, source: string): ForeignKey[
423
423
 
424
424
  /**
425
425
  * Delete all keys from the relation for the specified source.
426
- * Must be called within a `Relation.change` callback.
426
+ * Must be called within a `Relation.update` callback.
427
427
  *
428
428
  * NOTE: TypeScript's structural typing allows readonly objects to be passed to `Mutable<T>`
429
429
  * parameters, so there is no compile-time error. Enforcement is runtime-only.
@@ -432,7 +432,7 @@ export const deleteKeys = (entity: Mutable<Unknown>, source: string): void => in
432
432
 
433
433
  /**
434
434
  * Add a tag to the relation.
435
- * Must be called within a `Relation.change` callback.
435
+ * Must be called within a `Relation.update` callback.
436
436
  *
437
437
  * NOTE: TypeScript's structural typing allows readonly objects to be passed to `Mutable<T>`
438
438
  * parameters, so there is no compile-time error. Enforcement is runtime-only.
@@ -441,7 +441,7 @@ export const addTag = (entity: Mutable<Unknown>, tag: string): void => internal.
441
441
 
442
442
  /**
443
443
  * Remove a tag from the relation.
444
- * Must be called within a `Relation.change` callback.
444
+ * Must be called within a `Relation.update` callback.
445
445
  *
446
446
  * NOTE: TypeScript's structural typing allows readonly objects to be passed to `Mutable<T>`
447
447
  * parameters, so there is no compile-time error. Enforcement is runtime-only.
@@ -466,7 +466,7 @@ export const getLabel = (entity: Unknown | Snapshot): string | undefined => inte
466
466
 
467
467
  /**
468
468
  * Set the label of the relation.
469
- * Must be called within a `Relation.change` callback.
469
+ * Must be called within a `Relation.update` callback.
470
470
  *
471
471
  * NOTE: TypeScript's structural typing allows readonly objects to be passed to `Mutable<T>`
472
472
  * parameters, so there is no compile-time error. Enforcement is runtime-only.
@@ -481,7 +481,7 @@ export const getDescription = (entity: Unknown | Snapshot): string | undefined =
481
481
 
482
482
  /**
483
483
  * Set the description of the relation.
484
- * Must be called within a `Relation.change` callback.
484
+ * Must be called within a `Relation.update` callback.
485
485
  *
486
486
  * NOTE: TypeScript's structural typing allows readonly objects to be passed to `Mutable<T>`
487
487
  * parameters, so there is no compile-time error. Enforcement is runtime-only.
@@ -0,0 +1,21 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { describe, test } from 'vitest';
6
+
7
+ import * as Obj from './Obj';
8
+ import { TestSchema } from './testing';
9
+ import type * as Type from './Type';
10
+
11
+ describe('Exemplars', () => {
12
+ test('factory', ({ expect }) => {
13
+ const factory = <S extends Type.AnyObj>(schema: S) => {
14
+ return (props: Obj.MakeProps<S>) => Obj.make(schema, props);
15
+ };
16
+
17
+ const makePerson = factory(TestSchema.Person);
18
+ const person = makePerson({ name: 'John Doe' });
19
+ expect(person.name).toBe('John Doe');
20
+ });
21
+ });
@@ -481,7 +481,7 @@ export const makeUserAnnotation = <T>(props: MakeAnnoationsProps<T>): Annotation
481
481
 
482
482
  const getFromAst = (ast: SchemaAST.AST) =>
483
483
  SchemaAST.getAnnotation<PropertyMetaAnnotation>(PropertyMetaAnnotationId)(ast).pipe(
484
- Option.map((meta) => meta[props.id] as unknown),
484
+ Option.flatMap((meta) => Option.fromNullable(meta[props.id])),
485
485
  Option.map(Schema.decodeUnknownSync(props.schema)),
486
486
  );
487
487
 
@@ -532,6 +532,19 @@ export const IconAnnotation = makeUserAnnotation<IconAnnotation>({
532
532
  schema: IconAnnotationSchema,
533
533
  });
534
534
 
535
+ /**
536
+ * Indicates that this entity's icon should be resolved from a property whose value is a `Ref`
537
+ * to another entity. Consumers (e.g. graph node builders) resolve the ref target and use that
538
+ * target's schema `IconAnnotation` in place of the static one declared on this schema.
539
+ *
540
+ * Useful for wrapper schemas that delegate their visual identity to a referenced sub-entity
541
+ * (e.g. a generic `Game` whose icon should come from its `variant` ref's typed state).
542
+ */
543
+ export const IconFromRefAnnotation = makeUserAnnotation<string>({
544
+ id: 'org.dxos.annotation.icon.from-ref',
545
+ schema: Schema.String,
546
+ });
547
+
535
548
  /**
536
549
  * Get the label of an entity.
537
550
  * Accepts both reactive entities and snapshots.
@@ -545,7 +558,7 @@ export const getLabel = (entity: AnyProperties): string | undefined => {
545
558
 
546
559
  /**
547
560
  * Set the label of an entity.
548
- * Must be called within an Obj.change or Relation.change callback.
561
+ * Must be called within an Obj.update or Relation.update callback.
549
562
  */
550
563
  export const setLabel = (entity: Mutable<AnyProperties>, label: string) => {
551
564
  const schema = getSchema(entity);
@@ -567,7 +580,7 @@ export const getDescription = (entity: AnyProperties): string | undefined => {
567
580
 
568
581
  /**
569
582
  * Set the description of an entity.
570
- * Must be called within an Obj.change or Relation.change callback.
583
+ * Must be called within an Obj.update or Relation.update callback.
571
584
  */
572
585
  export const setDescription = (entity: Mutable<AnyProperties>, description: string) => {
573
586
  const schema = getSchema(entity);
@@ -5,7 +5,7 @@
5
5
  import { DXN, ObjectId, QueueSubspaceTags, SpaceId } from '@dxos/keys';
6
6
 
7
7
  /**
8
- * @deprecated Use `db.queues.create()`
8
+ * @deprecated Use `Feed.make(...)` + `db.add(feed)` then `Feed.getQueueDxn(feed)`.
9
9
  */
10
10
  // TODO(burdon): Move to @dxos/keys.
11
11
  export const createQueueDXN = (spaceId = SpaceId.random(), queueId = ObjectId.random()) =>
@@ -20,7 +20,7 @@ import { objectFromJSON, objectToJSON } from './json-serializer';
20
20
  describe('Object JSON serializer', () => {
21
21
  test('should serialize and deserialize object', async () => {
22
22
  const contact = makeObject(TestSchema.Person, { name: 'Alice' });
23
- Obj.change(contact, (contact) => {
23
+ Obj.update(contact, (contact) => {
24
24
  getMetaChecked(contact).keys.push({ id: '12345', source: 'example.com' });
25
25
  });
26
26
 
@@ -116,15 +116,15 @@ export const objectFromJSON = async (
116
116
  const isRelation =
117
117
  typeof jsonData[ATTR_RELATION_SOURCE] === 'string' || typeof jsonData[ATTR_RELATION_TARGET] === 'string';
118
118
  if (isRelation) {
119
- const sourceDxn: DXN = DXN.parse(jsonData[ATTR_RELATION_SOURCE] ?? raise(new TypeError('Missing relation source')));
120
- const targetDxn: DXN = DXN.parse(jsonData[ATTR_RELATION_TARGET] ?? raise(new TypeError('Missing relation target')));
119
+ const sourceDXN: DXN = DXN.parse(jsonData[ATTR_RELATION_SOURCE] ?? raise(new TypeError('Missing relation source')));
120
+ const targetDXN: DXN = DXN.parse(jsonData[ATTR_RELATION_TARGET] ?? raise(new TypeError('Missing relation target')));
121
121
 
122
- const source = (await refResolver?.resolve(sourceDxn)) as AnyEntity | undefined;
123
- const target = (await refResolver?.resolve(targetDxn)) as AnyEntity | undefined;
122
+ const source = (await refResolver?.resolve(sourceDXN)) as AnyEntity | undefined;
123
+ const target = (await refResolver?.resolve(targetDXN)) as AnyEntity | undefined;
124
124
 
125
125
  defineHiddenProperty(obj, KindId, EntityKind.Relation);
126
- defineHiddenProperty(obj, RelationSourceDXNId, sourceDxn);
127
- defineHiddenProperty(obj, RelationTargetDXNId, targetDxn);
126
+ defineHiddenProperty(obj, RelationSourceDXNId, sourceDXN);
127
+ defineHiddenProperty(obj, RelationTargetDXNId, targetDXN);
128
128
  defineHiddenProperty(obj, RelationSourceId, source);
129
129
  defineHiddenProperty(obj, RelationTargetId, target);
130
130
  } else {
@@ -142,8 +142,8 @@ export const objectFromJSON = async (
142
142
  }
143
143
 
144
144
  if (jsonData[ATTR_PARENT]) {
145
- const parentDxn = DXN.parse(jsonData[ATTR_PARENT]);
146
- const resolvedParent = (await refResolver?.resolve(parentDxn)) as Obj.Unknown | undefined;
145
+ const parentDXN = DXN.parse(jsonData[ATTR_PARENT]);
146
+ const resolvedParent = (await refResolver?.resolve(parentDXN)) as Obj.Unknown | undefined;
147
147
  defineHiddenProperty(obj, ParentId, resolvedParent);
148
148
  } else if (parent) {
149
149
  defineHiddenProperty(obj, ParentId, parent);
@@ -184,7 +184,7 @@ const stripInternalJsonKeys = (jsonData: unknown) => {
184
184
  [ATTR_TYPE]: _type,
185
185
  [ATTR_META]: _meta,
186
186
  [ATTR_DELETED]: _deleted,
187
- [ATTR_SELF_DXN]: _selfDxn,
187
+ [ATTR_SELF_DXN]: _selfDXN,
188
188
  [ATTR_RELATION_SOURCE]: _relationSource,
189
189
  [ATTR_RELATION_TARGET]: _relationTarget,
190
190
  ...props
@@ -17,7 +17,7 @@ describe('Obj.setValue', () => {
17
17
  email: 'john@example.com',
18
18
  });
19
19
 
20
- Obj.change(person, (person) => Obj.setValue(person, ['address', 'city'], 'NYC'));
20
+ Obj.update(person, (person) => Obj.setValue(person, ['address', 'city'], 'NYC'));
21
21
 
22
22
  expect(person.address).toBeDefined();
23
23
  expect(person.address?.city).toBe('NYC');
@@ -30,7 +30,7 @@ describe('Obj.setValue', () => {
30
30
  email: 'john@example.com',
31
31
  });
32
32
 
33
- Obj.change(person, (person) => Obj.setValue(person, ['fields', 0, 'label'], 'Phone'));
33
+ Obj.update(person, (person) => Obj.setValue(person, ['fields', 0, 'label'], 'Phone'));
34
34
 
35
35
  expect(Array.isArray(person.fields)).toBe(true);
36
36
  expect(person.fields?.[0].label).toBe('Phone');
@@ -45,7 +45,7 @@ describe('Obj.setValue', () => {
45
45
  email: 'john@example.com',
46
46
  });
47
47
 
48
- Obj.change(person, (person) => Obj.setValue(person, ['address', 'coordinates', 'lat'], 40.7128));
48
+ Obj.update(person, (person) => Obj.setValue(person, ['address', 'coordinates', 'lat'], 40.7128));
49
49
 
50
50
  expect(person.address).toBeDefined();
51
51
  expect(person.address?.coordinates).toBeDefined();
@@ -69,7 +69,7 @@ describe('Obj.setValue', () => {
69
69
 
70
70
  const container = Obj.make(Container, { name: 'box' });
71
71
 
72
- Obj.change(container, (container) => {
72
+ Obj.update(container, (container) => {
73
73
  Obj.setValue(container, ['items', 0, 'value'], 10);
74
74
  Obj.setValue(container, ['items', 1, 'value'], 20);
75
75
  Obj.setValue(container, ['items', 2, 'value'], 30);
@@ -88,7 +88,7 @@ describe('Obj.setValue', () => {
88
88
  email: 'john@example.com',
89
89
  });
90
90
 
91
- Obj.change(person, (person) => Obj.setValue(person, ['age'], 25));
91
+ Obj.update(person, (person) => Obj.setValue(person, ['age'], 25));
92
92
 
93
93
  expect(person.age).toBe(25);
94
94
  });
@@ -100,7 +100,7 @@ describe('Obj.setValue', () => {
100
100
  email: 'john@example.com',
101
101
  });
102
102
 
103
- Obj.change(person, (person) => Obj.setValue(person, ['address', 'city'], 'NYC'));
103
+ Obj.update(person, (person) => Obj.setValue(person, ['address', 'city'], 'NYC'));
104
104
 
105
105
  expect(person.address).toBeDefined();
106
106
  expect(person.address?.city).toBe('NYC');
@@ -114,7 +114,7 @@ describe('Obj.setValue', () => {
114
114
  });
115
115
 
116
116
  let result: any;
117
- Obj.change(person, (person) => {
117
+ Obj.update(person, (person) => {
118
118
  result = Obj.setValue(person, ['age'], 30);
119
119
  });
120
120
 
@@ -129,7 +129,7 @@ describe('Obj.setValue', () => {
129
129
  age: 25,
130
130
  });
131
131
 
132
- Obj.change(person, (person) => Obj.setValue(person, ['age'], 30));
132
+ Obj.update(person, (person) => Obj.setValue(person, ['age'], 30));
133
133
 
134
134
  expect(person.age).toBe(30);
135
135
  });
@@ -142,7 +142,7 @@ describe('Obj.setValue', () => {
142
142
  address: { city: 'Boston', state: 'MA', coordinates: {} },
143
143
  });
144
144
 
145
- Obj.change(person, (person) => Obj.setValue(person, ['address', 'city'], 'NYC'));
145
+ Obj.update(person, (person) => Obj.setValue(person, ['address', 'city'], 'NYC'));
146
146
 
147
147
  expect(person.address?.city).toBe('NYC');
148
148
  expect(person.address?.state).toBe('MA');
@@ -160,7 +160,7 @@ describe('Obj.setValue', () => {
160
160
 
161
161
  const matrix = Obj.make(Matrix, {});
162
162
 
163
- Obj.change(matrix, (matrix) => {
163
+ Obj.update(matrix, (matrix) => {
164
164
  Obj.setValue(matrix, ['values', 0, 0], 1);
165
165
  Obj.setValue(matrix, ['values', 0, 1], 2);
166
166
  Obj.setValue(matrix, ['values', 1, 0], 3);
@@ -180,7 +180,7 @@ describe('Obj.setValue', () => {
180
180
  email: 'john@example.com',
181
181
  });
182
182
 
183
- Obj.change(person, (person) => {
183
+ Obj.update(person, (person) => {
184
184
  expect(() => Obj.setValue(person, [], 'value')).toThrow('Path must not be empty');
185
185
  });
186
186
  });
@@ -192,7 +192,7 @@ describe('Obj.setValue', () => {
192
192
  email: 'john@example.com',
193
193
  });
194
194
 
195
- Obj.change(person, (person) => Obj.setValue(person, ['age'], 30));
195
+ Obj.update(person, (person) => Obj.setValue(person, ['age'], 30));
196
196
 
197
197
  expect(person.age).toBe(30);
198
198
  });
@@ -215,7 +215,7 @@ describe('Obj.setValue', () => {
215
215
  const container = Obj.make(Container, { name: 'box' });
216
216
 
217
217
  // Using string '0' for array index.
218
- Obj.change(container, (container) => Obj.setValue(container, ['items', '0', 'value'], 42));
218
+ Obj.update(container, (container) => Obj.setValue(container, ['items', '0', 'value'], 42));
219
219
 
220
220
  expect(container.items?.[0].value).toBe(42);
221
221
  });
@@ -243,7 +243,7 @@ describe('Obj.setValue', () => {
243
243
 
244
244
  // This should work: setting a nested property on an array element.
245
245
  // The required 'id' field should be initialized with a default value.
246
- Obj.change(todoList, (todoList) => Obj.setValue(todoList, ['tasks', 0, 'title'], 'Buy groceries'));
246
+ Obj.update(todoList, (todoList) => Obj.setValue(todoList, ['tasks', 0, 'title'], 'Buy groceries'));
247
247
 
248
248
  expect(todoList.tasks?.[0].id).toBe(''); // Default value for required String
249
249
  expect(todoList.tasks?.[0].title).toBe('Buy groceries');
@@ -270,7 +270,7 @@ describe('Obj.setValue', () => {
270
270
 
271
271
  const container = Obj.make(Container, { name: 'box' });
272
272
 
273
- Obj.change(container, (container) => Obj.setValue(container, ['items', 0, 'label'], 'First Item'));
273
+ Obj.update(container, (container) => Obj.setValue(container, ['items', 0, 'label'], 'First Item'));
274
274
 
275
275
  // All required primitive fields should have default values.
276
276
  expect(container.items?.[0].id).toBe('');
@@ -21,7 +21,7 @@ import { getSchema } from '../common/types';
21
21
 
22
22
  /**
23
23
  * Set a deeply nested property on an object.
24
- * Must be called within an Obj.change or Relation.change callback.
24
+ * Must be called within an Obj.update or Relation.update callback.
25
25
  */
26
26
  export const setValue = (obj: Mutable<any>, path: readonly (string | number)[], value: any): void => {
27
27
  invariant(path.length > 0, 'Path must not be empty');
@@ -109,6 +109,9 @@ export const prettyQuery = (query: QueryAST.Query): string => {
109
109
  if (opts.deleted !== undefined) {
110
110
  parts.push(`deleted: ${JSON.stringify(opts.deleted)}`);
111
111
  }
112
+ if (opts.debugLabel !== undefined) {
113
+ parts.push(`debugLabel: ${JSON.stringify(opts.debugLabel)}`);
114
+ }
112
115
  return `${prettyQuery(query.query)}.options({ ${parts.join(', ')} })`;
113
116
  }
114
117
  case 'from': {
@@ -118,11 +121,11 @@ export const prettyQuery = (query: QueryAST.Query): string => {
118
121
  if (scope.spaceIds !== undefined) {
119
122
  parts.push(`spaceIds: [${scope.spaceIds.map((s) => JSON.stringify(s)).join(', ')}]`);
120
123
  }
121
- if (scope.queues !== undefined) {
122
- parts.push(`queues: [${scope.queues.map(String).join(', ')}]`);
124
+ if (scope.feeds !== undefined) {
125
+ parts.push(`feeds: [${scope.feeds.map(String).join(', ')}]`);
123
126
  }
124
- if (scope.allQueuesFromSpaces !== undefined) {
125
- parts.push(`allQueuesFromSpaces: ${scope.allQueuesFromSpaces}`);
127
+ if (scope.allFeedsFromSpaces !== undefined) {
128
+ parts.push(`allFeedsFromSpaces: ${scope.allFeedsFromSpaces}`);
126
129
  }
127
130
  return `${prettyQuery(query.query)}.from({ ${parts.join(', ')} })`;
128
131
  }
@@ -372,7 +372,7 @@ export class EchoSchema<A = any, I = any> extends EchoSchemaConstructor() implem
372
372
  }
373
373
 
374
374
  /**
375
- * Wrapper for Obj.change that handles the change context for the persistent schema.
375
+ * Wrapper for Obj.update that handles the change context for the persistent schema.
376
376
  */
377
377
  private _change(callback: (schema: PersistentSchema) => void): void {
378
378
  const changeFn = (this._persistentSchema as any)[ChangeId];
@@ -99,4 +99,4 @@
99
99
  - [ ] ReactiveObject should specify id property? Reconcile AnyProperties, ReactiveObject, HasId, WithId, etc.
100
100
  - [ ] Can we us S.is(MyType) to detect objects with our types system? (Branding?)
101
101
  - [ ] Type.Expando doesn't work with AtomQuery result type (have to use Obj.Any instead).
102
- - [ ] Obj.Any doesn't work with Obj.change callback types (have to use `any` for the mutable parameter).
102
+ - [ ] Obj.Any doesn't work with Obj.update callback types (have to use `any` for the mutable parameter).
@@ -48,7 +48,7 @@ export const getKeys = (entity: AnyProperties, source: string): ForeignKey[] =>
48
48
 
49
49
  /**
50
50
  * Delete all keys from the entity for the specified source.
51
- * Must be called within an Obj.change or Relation.change callback.
51
+ * Must be called within an Obj.update or Relation.update callback.
52
52
  */
53
53
  export const deleteKeys = (entity: Mutable<AnyProperties>, source: string) => {
54
54
  const meta = getMetaChecked(entity);
@@ -62,7 +62,7 @@ export const deleteKeys = (entity: Mutable<AnyProperties>, source: string) => {
62
62
 
63
63
  /**
64
64
  * Add a tag to the entity.
65
- * Must be called within an Obj.change or Relation.change callback.
65
+ * Must be called within an Obj.update or Relation.update callback.
66
66
  */
67
67
  export const addTag = (entity: Mutable<AnyProperties>, tag: string) => {
68
68
  const meta = getMetaChecked(entity);
@@ -72,7 +72,7 @@ export const addTag = (entity: Mutable<AnyProperties>, tag: string) => {
72
72
 
73
73
  /**
74
74
  * Remove a tag from the entity.
75
- * Must be called within an Obj.change or Relation.change callback.
75
+ * Must be called within an Obj.update or Relation.update callback.
76
76
  */
77
77
  export const removeTag = (entity: Mutable<AnyProperties>, tag: string) => {
78
78
  const meta = getMetaChecked(entity);
@@ -35,7 +35,7 @@ const pendingOwnerNotifications = new Set<object>();
35
35
  /**
36
36
  * Enter a change context for the given key.
37
37
  * While in a change context, mutations are allowed on the associated object.
38
- * Nested Obj.change calls are not supported.
38
+ * Nested Obj.update calls are not supported.
39
39
  *
40
40
  * @param key - The key to enter the change context for (target object or ObjectCore).
41
41
  * @returns A cleanup function that exits the change context.