@dxos/echo 0.8.4-staging.ac66bdf99f → 0.9.0

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 (437) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +7 -7
  3. package/dist/lib/neutral/Annotation.mjs +37 -6
  4. package/dist/lib/neutral/Database.mjs +6 -17
  5. package/dist/lib/neutral/Entity.mjs +31 -20
  6. package/dist/lib/neutral/Err.mjs +3 -3
  7. package/dist/lib/neutral/Feed.mjs +23 -19
  8. package/dist/lib/neutral/Filter.mjs +13 -15
  9. package/dist/lib/neutral/Format.mjs +23 -3
  10. package/dist/lib/neutral/JsonSchema.mjs +7 -8
  11. package/dist/lib/neutral/Key.mjs +9 -5
  12. package/dist/lib/neutral/Migration.mjs +11 -10
  13. package/dist/lib/neutral/Obj.mjs +29 -20
  14. package/dist/lib/neutral/Order.mjs +7 -3
  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 +10 -9
  18. package/dist/lib/neutral/Registry.mjs +14 -0
  19. package/dist/lib/neutral/Relation.mjs +28 -25
  20. package/dist/lib/neutral/Scope.mjs +12 -0
  21. package/dist/lib/neutral/Tag.mjs +17 -14
  22. package/dist/lib/neutral/Type.mjs +54 -26
  23. package/dist/lib/neutral/{chunk-OMUPQMLR.mjs → chunk-35INCYOE.mjs} +1 -1
  24. package/dist/lib/neutral/chunk-35INCYOE.mjs.map +7 -0
  25. package/dist/lib/neutral/chunk-3PBP4V4O.mjs +101 -0
  26. package/dist/lib/neutral/chunk-3PBP4V4O.mjs.map +7 -0
  27. package/dist/lib/neutral/chunk-4ZUHOTCG.mjs +184 -0
  28. package/dist/lib/neutral/chunk-4ZUHOTCG.mjs.map +7 -0
  29. package/dist/lib/neutral/chunk-5SMDBFVB.mjs +108 -0
  30. package/dist/lib/neutral/chunk-5SMDBFVB.mjs.map +7 -0
  31. package/dist/lib/neutral/{chunk-OS35K56T.mjs → chunk-5SUJPHAE.mjs} +3 -3
  32. package/dist/lib/neutral/{chunk-OS35K56T.mjs.map → chunk-5SUJPHAE.mjs.map} +2 -2
  33. package/dist/lib/neutral/{chunk-GZQTCRJB.mjs → chunk-6M2Z6WBH.mjs} +22 -2
  34. package/dist/lib/neutral/chunk-6M2Z6WBH.mjs.map +7 -0
  35. package/dist/lib/neutral/{chunk-V36VO5SS.mjs → chunk-6YDI3J37.mjs} +32 -40
  36. package/dist/lib/neutral/chunk-6YDI3J37.mjs.map +7 -0
  37. package/dist/lib/neutral/{chunk-MOR5ERFM.mjs → chunk-7FPIAJIV.mjs} +701 -1256
  38. package/dist/lib/neutral/chunk-7FPIAJIV.mjs.map +7 -0
  39. package/dist/lib/neutral/{chunk-ANHVGJI4.mjs → chunk-7LOUAPYZ.mjs} +9 -5
  40. package/dist/lib/neutral/chunk-7LOUAPYZ.mjs.map +7 -0
  41. package/dist/lib/neutral/{chunk-JUXPFOEI.mjs → chunk-7PI7C4EF.mjs} +48 -88
  42. package/dist/lib/neutral/chunk-7PI7C4EF.mjs.map +7 -0
  43. package/dist/lib/neutral/{chunk-UBEZSGXY.mjs → chunk-BBFJWWAV.mjs} +6 -6
  44. package/dist/lib/neutral/chunk-BBFJWWAV.mjs.map +7 -0
  45. package/dist/lib/neutral/{chunk-UI6MWK5W.mjs → chunk-EVK6XBXO.mjs} +16 -2
  46. package/dist/lib/neutral/chunk-EVK6XBXO.mjs.map +7 -0
  47. package/dist/lib/neutral/{chunk-7RO7CPBZ.mjs → chunk-IGK6FN65.mjs} +2 -2
  48. package/dist/lib/neutral/{chunk-HBUZJNZO.mjs → chunk-LWXVKPPW.mjs} +94 -99
  49. package/dist/lib/neutral/chunk-LWXVKPPW.mjs.map +7 -0
  50. package/dist/lib/neutral/{chunk-BVOFLCVF.mjs → chunk-MZ7K3MLL.mjs} +9 -6
  51. package/dist/lib/neutral/chunk-MZ7K3MLL.mjs.map +7 -0
  52. package/dist/lib/neutral/{chunk-TBKX6JQO.mjs → chunk-O6BH7EPN.mjs} +30 -3
  53. package/dist/lib/neutral/chunk-O6BH7EPN.mjs.map +7 -0
  54. package/dist/lib/neutral/{chunk-EAMSSLZC.mjs → chunk-QQIYS74I.mjs} +83 -46
  55. package/dist/lib/neutral/chunk-QQIYS74I.mjs.map +7 -0
  56. package/dist/lib/neutral/chunk-R5W6DXR4.mjs +678 -0
  57. package/dist/lib/neutral/chunk-R5W6DXR4.mjs.map +7 -0
  58. package/dist/lib/neutral/{chunk-WAK4DMFV.mjs → chunk-RIVWNMSF.mjs} +12 -7
  59. package/dist/lib/neutral/chunk-RIVWNMSF.mjs.map +7 -0
  60. package/dist/lib/neutral/{chunk-T6W2LEZU.mjs → chunk-SBVFRTST.mjs} +73 -38
  61. package/dist/lib/neutral/chunk-SBVFRTST.mjs.map +7 -0
  62. package/dist/lib/neutral/chunk-T6E37YIP.mjs +251 -0
  63. package/dist/lib/neutral/chunk-T6E37YIP.mjs.map +7 -0
  64. package/dist/lib/neutral/{chunk-DQYLD2RB.mjs → chunk-TFEWTY5A.mjs} +155 -129
  65. package/dist/lib/neutral/chunk-TFEWTY5A.mjs.map +7 -0
  66. package/dist/lib/neutral/{chunk-B4BASU6W.mjs → chunk-TYGKCRMK.mjs} +85 -76
  67. package/dist/lib/neutral/chunk-TYGKCRMK.mjs.map +7 -0
  68. package/dist/lib/neutral/{chunk-4OIBYSXE.mjs → chunk-UUP46KUQ.mjs} +78 -32
  69. package/dist/lib/neutral/chunk-UUP46KUQ.mjs.map +7 -0
  70. package/dist/lib/neutral/chunk-WISOH2XH.mjs +36 -0
  71. package/dist/lib/neutral/chunk-WISOH2XH.mjs.map +7 -0
  72. package/dist/lib/neutral/{chunk-G3IQMKF7.mjs → chunk-WTQJHC75.mjs} +111 -112
  73. package/dist/lib/neutral/chunk-WTQJHC75.mjs.map +7 -0
  74. package/dist/lib/neutral/chunk-WU3GIANS.mjs +31 -0
  75. package/dist/lib/neutral/chunk-WU3GIANS.mjs.map +7 -0
  76. package/dist/lib/neutral/{chunk-TU3GW67D.mjs → chunk-ZGNNFYHS.mjs} +40 -40
  77. package/dist/lib/neutral/chunk-ZGNNFYHS.mjs.map +7 -0
  78. package/dist/lib/neutral/index.mjs +47 -41
  79. package/dist/lib/neutral/internal/index.mjs +137 -72
  80. package/dist/lib/neutral/meta.json +1 -1
  81. package/dist/lib/neutral/testing/index.mjs +261 -178
  82. package/dist/lib/neutral/testing/index.mjs.map +4 -4
  83. package/dist/types/src/Annotation.d.ts +108 -4
  84. package/dist/types/src/Annotation.d.ts.map +1 -1
  85. package/dist/types/src/Annotation.test.d.ts +2 -0
  86. package/dist/types/src/Annotation.test.d.ts.map +1 -0
  87. package/dist/types/src/Collection.d.ts +2 -3
  88. package/dist/types/src/Collection.d.ts.map +1 -1
  89. package/dist/types/src/Database.d.ts +56 -49
  90. package/dist/types/src/Database.d.ts.map +1 -1
  91. package/dist/types/src/Dataset.d.ts +16 -6
  92. package/dist/types/src/Dataset.d.ts.map +1 -1
  93. package/dist/types/src/Entity.d.ts +101 -28
  94. package/dist/types/src/Entity.d.ts.map +1 -1
  95. package/dist/types/src/Err.d.ts +27 -27
  96. package/dist/types/src/Err.d.ts.map +1 -1
  97. package/dist/types/src/Feed.d.ts +66 -19
  98. package/dist/types/src/Feed.d.ts.map +1 -1
  99. package/dist/types/src/Filter.d.ts +38 -12
  100. package/dist/types/src/Filter.d.ts.map +1 -1
  101. package/dist/types/src/Format.d.ts +0 -2
  102. package/dist/types/src/Format.d.ts.map +1 -1
  103. package/dist/types/src/Hypergraph.d.ts +14 -9
  104. package/dist/types/src/Hypergraph.d.ts.map +1 -1
  105. package/dist/types/src/Json.d.ts +33 -0
  106. package/dist/types/src/Json.d.ts.map +1 -0
  107. package/dist/types/src/Json.test.d.ts +2 -0
  108. package/dist/types/src/Json.test.d.ts.map +1 -0
  109. package/dist/types/src/JsonSchema.d.ts +2 -2
  110. package/dist/types/src/JsonSchema.d.ts.map +1 -1
  111. package/dist/types/src/Key.d.ts +1 -1
  112. package/dist/types/src/Key.d.ts.map +1 -1
  113. package/dist/types/src/Migration.d.ts +26 -11
  114. package/dist/types/src/Migration.d.ts.map +1 -1
  115. package/dist/types/src/Obj.d.ts +104 -61
  116. package/dist/types/src/Obj.d.ts.map +1 -1
  117. package/dist/types/src/Order.d.ts +10 -0
  118. package/dist/types/src/Order.d.ts.map +1 -1
  119. package/dist/types/src/Query.d.ts +34 -12
  120. package/dist/types/src/Query.d.ts.map +1 -1
  121. package/dist/types/src/QueryResult.d.ts +21 -0
  122. package/dist/types/src/QueryResult.d.ts.map +1 -1
  123. package/dist/types/src/Ref.d.ts +15 -7
  124. package/dist/types/src/Ref.d.ts.map +1 -1
  125. package/dist/types/src/Registry.d.ts +131 -0
  126. package/dist/types/src/Registry.d.ts.map +1 -0
  127. package/dist/types/src/Relation.d.ts +73 -41
  128. package/dist/types/src/Relation.d.ts.map +1 -1
  129. package/dist/types/src/Scope.d.ts +35 -0
  130. package/dist/types/src/Scope.d.ts.map +1 -0
  131. package/dist/types/src/Tag.d.ts +21 -5
  132. package/dist/types/src/Tag.d.ts.map +1 -1
  133. package/dist/types/src/Type.d.ts +362 -95
  134. package/dist/types/src/Type.d.ts.map +1 -1
  135. package/dist/types/src/View.d.ts +9 -12
  136. package/dist/types/src/View.d.ts.map +1 -1
  137. package/dist/types/src/exemplars.test.d.ts +2 -0
  138. package/dist/types/src/exemplars.test.d.ts.map +1 -0
  139. package/dist/types/src/index.d.ts +4 -3
  140. package/dist/types/src/index.d.ts.map +1 -1
  141. package/dist/types/src/internal/Annotation/annotations.d.ts +79 -38
  142. package/dist/types/src/internal/Annotation/annotations.d.ts.map +1 -1
  143. package/dist/types/src/internal/Annotation/dictionary.d.ts +24 -0
  144. package/dist/types/src/internal/Annotation/dictionary.d.ts.map +1 -0
  145. package/dist/types/src/internal/Annotation/entity-dictionary.d.ts +14 -0
  146. package/dist/types/src/internal/Annotation/entity-dictionary.d.ts.map +1 -0
  147. package/dist/types/src/internal/Annotation/index.d.ts +4 -2
  148. package/dist/types/src/internal/Annotation/index.d.ts.map +1 -1
  149. package/dist/types/src/internal/Annotation/sorting.d.ts.map +1 -1
  150. package/dist/types/src/internal/Annotation/util.d.ts +14 -5
  151. package/dist/types/src/internal/Annotation/util.d.ts.map +1 -1
  152. package/dist/types/src/internal/Entity/api.d.ts +17 -3
  153. package/dist/types/src/internal/Entity/api.d.ts.map +1 -1
  154. package/dist/types/src/internal/Entity/entity.d.ts +72 -8
  155. package/dist/types/src/internal/Entity/entity.d.ts.map +1 -1
  156. package/dist/types/src/internal/Entity/guard.d.ts +10 -0
  157. package/dist/types/src/internal/Entity/guard.d.ts.map +1 -0
  158. package/dist/types/src/internal/Entity/index.d.ts +2 -0
  159. package/dist/types/src/internal/Entity/index.d.ts.map +1 -1
  160. package/dist/types/src/internal/Entity/model.d.ts +21 -17
  161. package/dist/types/src/internal/Entity/model.d.ts.map +1 -1
  162. package/dist/types/src/internal/Entity/object.d.ts +3 -3
  163. package/dist/types/src/internal/Entity/object.d.ts.map +1 -1
  164. package/dist/types/src/internal/Entity/relation.d.ts +30 -7
  165. package/dist/types/src/internal/Entity/relation.d.ts.map +1 -1
  166. package/dist/types/src/internal/Entity/type-kind.d.ts +24 -0
  167. package/dist/types/src/internal/Entity/type-kind.d.ts.map +1 -0
  168. package/dist/types/src/internal/Entity/type-uri.d.ts +24 -0
  169. package/dist/types/src/internal/Entity/type-uri.d.ts.map +1 -0
  170. package/dist/types/src/internal/Entity/version.d.ts.map +1 -1
  171. package/dist/types/src/internal/Format/date.d.ts.map +1 -1
  172. package/dist/types/src/internal/Format/format.d.ts +3 -2
  173. package/dist/types/src/internal/Format/format.d.ts.map +1 -1
  174. package/dist/types/src/internal/Format/index.d.ts +2 -2
  175. package/dist/types/src/internal/Format/index.d.ts.map +1 -1
  176. package/dist/types/src/internal/Format/number.d.ts.map +1 -1
  177. package/dist/types/src/internal/Format/object.d.ts +3 -1
  178. package/dist/types/src/internal/Format/object.d.ts.map +1 -1
  179. package/dist/types/src/internal/Format/types.d.ts.map +1 -1
  180. package/dist/types/src/internal/JsonSchema/json-schema-normalize.d.ts.map +1 -1
  181. package/dist/types/src/internal/JsonSchema/json-schema-type.d.ts +34 -34
  182. package/dist/types/src/internal/JsonSchema/json-schema-type.d.ts.map +1 -1
  183. package/dist/types/src/internal/JsonSchema/json-schema.d.ts +3 -2
  184. package/dist/types/src/internal/JsonSchema/json-schema.d.ts.map +1 -1
  185. package/dist/types/src/internal/Obj/atoms.d.ts +38 -0
  186. package/dist/types/src/internal/Obj/atoms.d.ts.map +1 -0
  187. package/dist/types/src/internal/Obj/clone.d.ts.map +1 -1
  188. package/dist/types/src/internal/Obj/common.d.ts.map +1 -1
  189. package/dist/types/src/internal/Obj/create-object.d.ts +12 -12
  190. package/dist/types/src/internal/Obj/create-object.d.ts.map +1 -1
  191. package/dist/types/src/internal/Obj/deleted.d.ts.map +1 -1
  192. package/dist/types/src/internal/Obj/index.d.ts +1 -1
  193. package/dist/types/src/internal/Obj/index.d.ts.map +1 -1
  194. package/dist/types/src/internal/Obj/json-serializer.d.ts +8 -8
  195. package/dist/types/src/internal/Obj/json-serializer.d.ts.map +1 -1
  196. package/dist/types/src/internal/Obj/set-value.d.ts +1 -1
  197. package/dist/types/src/internal/Obj/set-value.d.ts.map +1 -1
  198. package/dist/types/src/internal/Obj/snapshot.d.ts.map +1 -1
  199. package/dist/types/src/internal/Obj/typed-object.d.ts +1 -1
  200. package/dist/types/src/internal/Query/index.d.ts +2 -0
  201. package/dist/types/src/internal/Query/index.d.ts.map +1 -0
  202. package/dist/types/src/internal/{Query.d.ts → Query/pretty.d.ts} +1 -1
  203. package/dist/types/src/internal/Query/pretty.d.ts.map +1 -0
  204. package/dist/types/src/internal/Ref/atoms.d.ts +10 -0
  205. package/dist/types/src/internal/Ref/atoms.d.ts.map +1 -0
  206. package/dist/types/src/internal/Ref/ref-array.d.ts +2 -2
  207. package/dist/types/src/internal/Ref/ref.d.ts +50 -19
  208. package/dist/types/src/internal/Ref/ref.d.ts.map +1 -1
  209. package/dist/types/src/internal/Ref/utils.d.ts +8 -0
  210. package/dist/types/src/internal/Ref/utils.d.ts.map +1 -0
  211. package/dist/types/src/internal/Type/compose.d.ts.map +1 -1
  212. package/dist/types/src/internal/Type/index.d.ts +1 -2
  213. package/dist/types/src/internal/Type/index.d.ts.map +1 -1
  214. package/dist/types/src/internal/Type/manipulation.d.ts +0 -1
  215. package/dist/types/src/internal/Type/manipulation.d.ts.map +1 -1
  216. package/dist/types/src/internal/Type/type-schema.d.ts +52 -0
  217. package/dist/types/src/internal/Type/type-schema.d.ts.map +1 -0
  218. package/dist/types/src/internal/common/api/meta.d.ts +14 -11
  219. package/dist/types/src/internal/common/api/meta.d.ts.map +1 -1
  220. package/dist/types/src/internal/common/proxy/change-context.d.ts +1 -1
  221. package/dist/types/src/internal/common/proxy/change-context.d.ts.map +1 -1
  222. package/dist/types/src/internal/common/proxy/define-hidden-property.d.ts.map +1 -1
  223. package/dist/types/src/internal/common/proxy/errors.d.ts +1 -1
  224. package/dist/types/src/internal/common/proxy/errors.d.ts.map +1 -1
  225. package/dist/types/src/internal/common/proxy/event-batch.d.ts.map +1 -1
  226. package/dist/types/src/internal/common/proxy/json-serializer.d.ts.map +1 -1
  227. package/dist/types/src/internal/common/proxy/make-object.d.ts +11 -5
  228. package/dist/types/src/internal/common/proxy/make-object.d.ts.map +1 -1
  229. package/dist/types/src/internal/common/proxy/ownership.d.ts.map +1 -1
  230. package/dist/types/src/internal/common/proxy/proxy-utils.d.ts.map +1 -1
  231. package/dist/types/src/internal/common/proxy/reactive-array.d.ts +1 -1
  232. package/dist/types/src/internal/common/proxy/reactive.d.ts +1 -1
  233. package/dist/types/src/internal/common/proxy/reactive.d.ts.map +1 -1
  234. package/dist/types/src/internal/common/proxy/reactive.test.d.ts +2 -0
  235. package/dist/types/src/internal/common/proxy/reactive.test.d.ts.map +1 -0
  236. package/dist/types/src/internal/common/proxy/schema-validator.d.ts.map +1 -1
  237. package/dist/types/src/internal/common/proxy/typed-handler.d.ts +18 -2
  238. package/dist/types/src/internal/common/proxy/typed-handler.d.ts.map +1 -1
  239. package/dist/types/src/internal/common/types/base.d.ts +4 -4
  240. package/dist/types/src/internal/common/types/base.d.ts.map +1 -1
  241. package/dist/types/src/internal/common/types/entity.d.ts +62 -5
  242. package/dist/types/src/internal/common/types/entity.d.ts.map +1 -1
  243. package/dist/types/src/internal/common/types/index.d.ts +1 -1
  244. package/dist/types/src/internal/common/types/index.d.ts.map +1 -1
  245. package/dist/types/src/internal/common/types/meta.d.ts +33 -12
  246. package/dist/types/src/internal/common/types/meta.d.ts.map +1 -1
  247. package/dist/types/src/internal/common/types/model-symbols.d.ts +15 -4
  248. package/dist/types/src/internal/common/types/model-symbols.d.ts.map +1 -1
  249. package/dist/types/src/internal/common/types/typename.d.ts +7 -0
  250. package/dist/types/src/internal/common/types/typename.d.ts.map +1 -1
  251. package/dist/types/src/internal/common/types/version.d.ts +1 -1
  252. package/dist/types/src/internal/common/types/well-known-types.d.ts +11 -0
  253. package/dist/types/src/internal/common/types/well-known-types.d.ts.map +1 -0
  254. package/dist/types/src/internal/index.d.ts +2 -2
  255. package/dist/types/src/internal/index.d.ts.map +1 -1
  256. package/dist/types/src/testing/index.d.ts +1 -0
  257. package/dist/types/src/testing/index.d.ts.map +1 -1
  258. package/dist/types/src/testing/registry.d.ts +9 -0
  259. package/dist/types/src/testing/registry.d.ts.map +1 -0
  260. package/dist/types/src/testing/test-data.d.ts +8 -8
  261. package/dist/types/src/testing/test-data.d.ts.map +1 -1
  262. package/dist/types/src/testing/test-schema.d.ts +83 -89
  263. package/dist/types/src/testing/test-schema.d.ts.map +1 -1
  264. package/dist/types/src/testing/util.d.ts +5 -3
  265. package/dist/types/src/testing/util.d.ts.map +1 -1
  266. package/dist/types/tsconfig.tsbuildinfo +1 -1
  267. package/package.json +26 -24
  268. package/src/Annotation.test.ts +439 -0
  269. package/src/Annotation.ts +158 -4
  270. package/src/Collection.ts +5 -9
  271. package/src/Database.ts +93 -100
  272. package/src/Dataset.ts +10 -2
  273. package/src/Entity.test.ts +116 -6
  274. package/src/Entity.ts +134 -32
  275. package/src/Err.ts +4 -4
  276. package/src/Feed.ts +92 -44
  277. package/src/Filter.ts +70 -40
  278. package/src/Format.ts +0 -4
  279. package/src/Hypergraph.ts +14 -9
  280. package/src/Json.test.ts +175 -0
  281. package/src/Json.ts +103 -0
  282. package/src/Key.ts +1 -1
  283. package/src/Migration.ts +39 -19
  284. package/src/Obj.test.ts +122 -20
  285. package/src/Obj.ts +168 -91
  286. package/src/Order.ts +22 -0
  287. package/src/Query.test.ts +183 -154
  288. package/src/Query.ts +172 -85
  289. package/src/QueryResult.ts +26 -0
  290. package/src/Ref.ts +22 -4
  291. package/src/Registry.ts +155 -0
  292. package/src/Relation.test.ts +10 -10
  293. package/src/Relation.ts +116 -69
  294. package/src/Scope.ts +50 -0
  295. package/src/Tag.md +88 -0
  296. package/src/Tag.ts +49 -6
  297. package/src/Type.test.ts +223 -18
  298. package/src/Type.ts +609 -131
  299. package/src/View.ts +14 -23
  300. package/src/exemplars.test.ts +21 -0
  301. package/src/index.ts +4 -4
  302. package/src/internal/Annotation/annotations.test.ts +31 -11
  303. package/src/internal/Annotation/annotations.ts +143 -111
  304. package/src/internal/Annotation/dictionary.ts +47 -0
  305. package/src/internal/Annotation/entity-dictionary.ts +74 -0
  306. package/src/internal/Annotation/index.ts +4 -2
  307. package/src/internal/Annotation/util.ts +17 -8
  308. package/src/internal/Entity/api.ts +54 -7
  309. package/src/internal/Entity/entity.ts +196 -47
  310. package/src/internal/Entity/guard.ts +26 -0
  311. package/src/internal/Entity/index.ts +2 -0
  312. package/src/internal/Entity/model.ts +38 -28
  313. package/src/internal/Entity/object.ts +21 -5
  314. package/src/internal/Entity/relation.ts +68 -34
  315. package/src/internal/Entity/type-kind.ts +75 -0
  316. package/src/internal/Entity/type-uri.ts +92 -0
  317. package/src/internal/Entity/util.ts +9 -9
  318. package/src/internal/Format/date.ts +0 -4
  319. package/src/internal/Format/format.test.ts +21 -0
  320. package/src/internal/Format/index.ts +2 -3
  321. package/src/internal/Format/object.ts +21 -4
  322. package/src/internal/Format/types.ts +1 -1
  323. package/src/internal/JsonSchema/annotations.ts +1 -1
  324. package/src/internal/JsonSchema/json-schema-type.ts +4 -4
  325. package/src/internal/JsonSchema/json-schema.test.ts +71 -145
  326. package/src/internal/JsonSchema/json-schema.ts +49 -35
  327. package/src/internal/Obj/atoms.ts +244 -0
  328. package/src/internal/Obj/clone.ts +9 -4
  329. package/src/internal/Obj/create-object.test.ts +12 -10
  330. package/src/internal/Obj/create-object.ts +68 -22
  331. package/src/internal/Obj/index.ts +1 -1
  332. package/src/internal/Obj/inspect.ts +5 -3
  333. package/src/internal/Obj/json-serializer.test.ts +101 -22
  334. package/src/internal/Obj/json-serializer.ts +89 -33
  335. package/src/internal/Obj/set-value.test.ts +22 -45
  336. package/src/internal/Obj/set-value.ts +12 -19
  337. package/src/internal/Obj/snapshot.ts +13 -4
  338. package/src/internal/Obj/typed-object.test.ts +9 -11
  339. package/src/internal/Obj/typed-object.ts +1 -1
  340. package/src/internal/Query/index.ts +5 -0
  341. package/src/internal/{Query.ts → Query/pretty.ts} +40 -12
  342. package/src/internal/Ref/atoms.ts +20 -0
  343. package/src/internal/Ref/ref-array.ts +3 -3
  344. package/src/internal/Ref/ref.test.ts +18 -27
  345. package/src/internal/Ref/ref.ts +137 -59
  346. package/src/internal/Ref/utils.ts +45 -0
  347. package/src/internal/Type/compose.test.ts +3 -1
  348. package/src/internal/Type/index.ts +1 -2
  349. package/src/internal/Type/manipulation.ts +0 -25
  350. package/src/internal/Type/type-schema.ts +60 -0
  351. package/src/internal/common/README.md +2 -2
  352. package/src/internal/common/api/meta.ts +19 -17
  353. package/src/internal/common/proxy/change-context.ts +1 -1
  354. package/src/internal/common/proxy/change.test.ts +91 -83
  355. package/src/internal/common/proxy/errors.ts +2 -2
  356. package/src/internal/common/proxy/handler.test.ts +1 -1
  357. package/src/internal/common/proxy/json-serializer.ts +27 -16
  358. package/src/internal/common/proxy/make-object.ts +44 -20
  359. package/src/internal/common/proxy/ownership.ts +2 -2
  360. package/src/internal/common/proxy/reactive-array.ts +1 -1
  361. package/src/internal/common/proxy/reactive.test.ts +54 -0
  362. package/src/internal/common/proxy/reactive.ts +11 -2
  363. package/src/internal/common/proxy/schema.test.ts +48 -86
  364. package/src/internal/common/proxy/typed-handler.test.ts +12 -11
  365. package/src/internal/common/proxy/typed-handler.ts +78 -16
  366. package/src/internal/common/proxy/typed-object.test.ts +16 -28
  367. package/src/internal/common/types/base.ts +4 -4
  368. package/src/internal/common/types/entity.ts +80 -1
  369. package/src/internal/common/types/index.ts +6 -1
  370. package/src/internal/common/types/meta.ts +62 -20
  371. package/src/internal/common/types/model-symbols.ts +24 -4
  372. package/src/internal/common/types/typename.ts +39 -3
  373. package/src/internal/common/types/well-known-types.ts +15 -0
  374. package/src/internal/index.ts +6 -4
  375. package/src/testing/api.test.ts +15 -9
  376. package/src/testing/index.ts +1 -0
  377. package/src/testing/registry.ts +44 -0
  378. package/src/testing/test-data.ts +159 -99
  379. package/src/testing/test-schema.ts +22 -58
  380. package/src/testing/util.ts +14 -11
  381. package/dist/lib/neutral/Extension.mjs +0 -18
  382. package/dist/lib/neutral/SchemaRegistry.mjs +0 -2
  383. package/dist/lib/neutral/chunk-2KHZ36F5.mjs +0 -361
  384. package/dist/lib/neutral/chunk-2KHZ36F5.mjs.map +0 -7
  385. package/dist/lib/neutral/chunk-4OIBYSXE.mjs.map +0 -7
  386. package/dist/lib/neutral/chunk-4P3IXBLT.mjs +0 -45
  387. package/dist/lib/neutral/chunk-4P3IXBLT.mjs.map +0 -7
  388. package/dist/lib/neutral/chunk-ANHVGJI4.mjs.map +0 -7
  389. package/dist/lib/neutral/chunk-B4BASU6W.mjs.map +0 -7
  390. package/dist/lib/neutral/chunk-BNCCGLJN.mjs +0 -7
  391. package/dist/lib/neutral/chunk-BNCCGLJN.mjs.map +0 -7
  392. package/dist/lib/neutral/chunk-BVOFLCVF.mjs.map +0 -7
  393. package/dist/lib/neutral/chunk-DQYLD2RB.mjs.map +0 -7
  394. package/dist/lib/neutral/chunk-EAMSSLZC.mjs.map +0 -7
  395. package/dist/lib/neutral/chunk-G3IQMKF7.mjs.map +0 -7
  396. package/dist/lib/neutral/chunk-GZQTCRJB.mjs.map +0 -7
  397. package/dist/lib/neutral/chunk-HBUZJNZO.mjs.map +0 -7
  398. package/dist/lib/neutral/chunk-JUXPFOEI.mjs.map +0 -7
  399. package/dist/lib/neutral/chunk-MOR5ERFM.mjs.map +0 -7
  400. package/dist/lib/neutral/chunk-OMUPQMLR.mjs.map +0 -7
  401. package/dist/lib/neutral/chunk-PHU22NLC.mjs +0 -136
  402. package/dist/lib/neutral/chunk-PHU22NLC.mjs.map +0 -7
  403. package/dist/lib/neutral/chunk-ROG4RXXL.mjs +0 -97
  404. package/dist/lib/neutral/chunk-ROG4RXXL.mjs.map +0 -7
  405. package/dist/lib/neutral/chunk-T6W2LEZU.mjs.map +0 -7
  406. package/dist/lib/neutral/chunk-TBKX6JQO.mjs.map +0 -7
  407. package/dist/lib/neutral/chunk-TU3GW67D.mjs.map +0 -7
  408. package/dist/lib/neutral/chunk-UBEZSGXY.mjs.map +0 -7
  409. package/dist/lib/neutral/chunk-UI6MWK5W.mjs.map +0 -7
  410. package/dist/lib/neutral/chunk-V36VO5SS.mjs.map +0 -7
  411. package/dist/lib/neutral/chunk-WAK4DMFV.mjs.map +0 -7
  412. package/dist/lib/neutral/chunk-YAHXAYOW.mjs +0 -56
  413. package/dist/lib/neutral/chunk-YAHXAYOW.mjs.map +0 -7
  414. package/dist/lib/neutral/chunk-YS6Q3XAD.mjs +0 -50
  415. package/dist/lib/neutral/chunk-YS6Q3XAD.mjs.map +0 -7
  416. package/dist/types/src/Extension.d.ts +0 -80
  417. package/dist/types/src/Extension.d.ts.map +0 -1
  418. package/dist/types/src/Extension.test.d.ts +0 -2
  419. package/dist/types/src/Extension.test.d.ts.map +0 -1
  420. package/dist/types/src/SchemaRegistry.d.ts +0 -84
  421. package/dist/types/src/SchemaRegistry.d.ts.map +0 -1
  422. package/dist/types/src/internal/Obj/ids.d.ts +0 -6
  423. package/dist/types/src/internal/Obj/ids.d.ts.map +0 -1
  424. package/dist/types/src/internal/Query.d.ts.map +0 -1
  425. package/dist/types/src/internal/Type/echo-schema.d.ts +0 -181
  426. package/dist/types/src/internal/Type/echo-schema.d.ts.map +0 -1
  427. package/dist/types/src/internal/Type/persistent-schema.d.ts +0 -20
  428. package/dist/types/src/internal/Type/persistent-schema.d.ts.map +0 -1
  429. package/src/Extension.test.ts +0 -235
  430. package/src/Extension.ts +0 -122
  431. package/src/SchemaRegistry.ts +0 -106
  432. package/src/internal/Obj/ids.ts +0 -12
  433. package/src/internal/Type/echo-schema.ts +0 -423
  434. package/src/internal/Type/persistent-schema.ts +0 -33
  435. /package/dist/lib/neutral/{Extension.mjs.map → Registry.mjs.map} +0 -0
  436. /package/dist/lib/neutral/{SchemaRegistry.mjs.map → Scope.mjs.map} +0 -0
  437. /package/dist/lib/neutral/{chunk-7RO7CPBZ.mjs.map → chunk-IGK6FN65.mjs.map} +0 -0
package/src/Filter.ts CHANGED
@@ -11,11 +11,13 @@ import type * as Types from 'effect/Types';
11
11
 
12
12
  import { type ForeignKey, type QueryAST } from '@dxos/echo-protocol';
13
13
  import { assertArgument } from '@dxos/invariant';
14
- import { DXN, ObjectId } from '@dxos/keys';
14
+ import { EID, EntityId, type URI } from '@dxos/keys';
15
15
 
16
16
  import * as internal from './internal';
17
17
  import type * as Obj from './Obj';
18
18
  import * as Ref from './Ref';
19
+ // eslint-disable-next-line @dxos/rules/import-as-namespace
20
+ import type * as Type$ from './Type';
19
21
 
20
22
  export interface Filter<T> {
21
23
  // TODO(dmaretskyi): See new effect-schema approach to variance.
@@ -77,11 +79,11 @@ export const nothing = (): FilterClass => {
77
79
  };
78
80
 
79
81
  /*
80
- * Filter by ObjectId.
82
+ * Filter by EntityId.
81
83
  */
82
- export const id = (...ids: ObjectId[]): Any => {
84
+ export const id = (...ids: EntityId[]): Any => {
83
85
  assertArgument(
84
- ids.every((id) => ObjectId.isValid(id)),
86
+ ids.every((id) => EntityId.isValid(id)),
85
87
  'ids',
86
88
  'ids must be valid',
87
89
  );
@@ -100,61 +102,89 @@ export const id = (...ids: ObjectId[]): Any => {
100
102
 
101
103
  /**
102
104
  * Filter by type.
105
+ *
106
+ * Accepts a `Type.Type` entity (the value produced by `Type.makeObject` /
107
+ * `Type.makeRelation`), a `Schema.Union` of such entities (for filtering across a
108
+ * union of ECHO types), or a fully-qualified type URI — an `echo:` EID (stored schema)
109
+ * or a `dxn:` DXN (static schema). To filter by a bare typename, wrap it: `DXN.make(typename)`.
103
110
  */
104
- export const type = <S extends Schema.Schema.All>(
105
- schema: S | string,
106
- props?: Props<Schema.Schema.Type<S>>,
107
- ): Filter<Schema.Schema.Type<S>> => {
108
- if (Schema.isSchema(schema) && SchemaAST.isUnion(schema.ast)) {
109
- const typenames = schema.ast.types.map((type) => internal.getTypeDXNFromSpecifier(Schema.make(type)));
111
+ export const type: {
112
+ <T extends Type$.AnyEntity>(type: T, props?: Props<Type$.InstanceType<T>>): Filter<Type$.InstanceType<T>>;
113
+ // Schema-side overload restricted to the well-known unknown schemas and to
114
+ // `Schema.Union(...)` of `Type.Type` entities (for filtering across a union
115
+ // of ECHO types). Other raw schemas are rejected.
116
+ <S extends internal.UnknownTypeSchema<any, any>>(
117
+ schema: S,
118
+ props?: Props<Schema.Schema.Type<S>>,
119
+ ): Filter<Schema.Schema.Type<S>>;
120
+ <S extends Schema.Union<readonly Schema.Schema.AnyNoContext[]>>(
121
+ union: S,
122
+ props?: Props<Schema.Schema.Type<S>>,
123
+ ): Filter<Schema.Schema.Type<S>>;
124
+ (uri: URI.URI, props?: Props<unknown>): Filter<any>;
125
+ // Passthrough overload for callers that hold a `Type.AnyEntity | URI.URI` union
126
+ // (e.g. Query.type / Query.sourceOf / Query.targetOf impls). Listed last so the
127
+ // typed overloads above still win for monomorphic inputs.
128
+ (input: Type$.AnyEntity | URI.URI, props?: Props<unknown>): Filter<unknown>;
129
+ } = (input: Type$.AnyEntity | Schema.Schema.AnyNoContext | URI.URI, props?: Props<unknown>): any => {
130
+ if (Schema.isSchema(input) && SchemaAST.isUnion(input.ast)) {
131
+ const typenames = input.ast.types.map((t) => internal.getTypeURIFromSpecifier(Schema.make(t)));
110
132
  return new FilterClass({
111
133
  type: 'or',
112
134
  filters: typenames.map((typename) => ({
113
135
  type: 'object',
114
- typename: typename.toString(),
136
+ typename,
115
137
  props: {},
116
138
  })),
117
139
  });
118
140
  }
119
141
 
120
- const dxn = internal.getTypeDXNFromSpecifier(schema);
142
+ const uri = internal.getTypeURIFromSpecifier(input);
121
143
  return new FilterClass({
122
144
  type: 'object',
123
- typename: dxn.toString(),
145
+ typename: uri,
124
146
  ...propsFilterToAst(props ?? {}),
125
147
  });
126
148
  };
127
149
 
128
150
  /**
129
- * Filter by non-qualified typename.
151
+ * Filter by tag.
130
152
  */
131
- export const typename = (typename: string): Any => {
132
- assertArgument(!typename.startsWith('dxn:'), 'typename', 'Typename must no be qualified');
153
+ export const tag = (tag: string): Any => {
133
154
  return new FilterClass({
134
- type: 'object',
135
- typename: DXN.fromTypename(typename).toString(),
136
- props: {},
155
+ type: 'tag',
156
+ tag,
137
157
  });
138
158
  };
139
159
 
140
160
  /**
141
- * Filter by fully qualified type DXN.
161
+ * Options for {@link key} filter.
142
162
  */
143
- export const typeDXN = (dxn: DXN): Any => {
144
- return new FilterClass({
145
- type: 'object',
146
- typename: dxn.toString(),
147
- props: {},
148
- });
163
+ export type KeyFilterOptions = {
164
+ /**
165
+ * Optional semver range expression (e.g. `^1.2.3`, `~2.0.0`, `>=1.0.0 <2.0.0`).
166
+ * Matches the object's meta `version` field against the range.
167
+ * If omitted, matches any version (including objects with no version).
168
+ */
169
+ version?: string;
149
170
  };
150
171
 
151
172
  /**
152
- * Filter by tag.
173
+ * Filter by registry key stored in object meta.
174
+ *
175
+ * @example
176
+ * ```ts
177
+ * Filter.key('org.example.type.foo');
178
+ * Filter.key('org.example.type.foo', { version: '^1.2.3' });
179
+ * ```
153
180
  */
154
- export const tag = (tag: string): Any => {
181
+ export const key = (key: string, options?: KeyFilterOptions): Any => {
155
182
  return new FilterClass({
156
- type: 'tag',
157
- tag,
183
+ type: 'object',
184
+ typename: null,
185
+ props: {},
186
+ metaKey: key,
187
+ metaVersion: options?.version,
158
188
  });
159
189
  };
160
190
 
@@ -192,14 +222,14 @@ export const text = (
192
222
  /**
193
223
  * Filter by foreign keys.
194
224
  */
195
- export const foreignKeys = <S extends Schema.Schema.All>(
196
- schema: S | string,
225
+ export const foreignKeys = <S extends Type$.AnyEntity | URI.URI>(
226
+ schema: S,
197
227
  keys: ForeignKey[],
198
- ): Filter<Schema.Schema.Type<S>> => {
199
- const dxn = internal.getTypeDXNFromSpecifier(schema);
228
+ ): Filter<S extends Type$.AnyEntity ? Type$.InstanceType<S> : unknown> => {
229
+ const uri = internal.getTypeURIFromSpecifier(schema);
200
230
  return new FilterClass({
201
231
  type: 'object',
202
- typename: dxn.toString(),
232
+ typename: uri,
203
233
  props: {},
204
234
  foreignKeys: keys,
205
235
  });
@@ -347,7 +377,7 @@ export type ChildOfOptions = {
347
377
  /**
348
378
  * Filter objects that are children of the specified parent(s).
349
379
  * Accepts ECHO objects, Refs, or arrays of either.
350
- * Refs are resolved to DXNs without loading; objects use {@link Obj.getDXN}.
380
+ * Refs are resolved to DXNs without loading; objects use {@link Obj.getURI}.
351
381
  * With transitive=true (default), also matches grandchildren and beyond.
352
382
  */
353
383
  export const childOf = (
@@ -357,9 +387,9 @@ export const childOf = (
357
387
  const items = Array.isArray(parents) ? parents : [parents];
358
388
  const dxns = items.map((item) => {
359
389
  if (Ref.isRef(item)) {
360
- return item.dxn.toString();
390
+ return EID.parse(item.uri);
361
391
  }
362
- return internal.getDXN(item).toString();
392
+ return EID.parse(internal.getUri(item));
363
393
  });
364
394
  return new FilterClass({
365
395
  type: 'child-of',
@@ -401,7 +431,7 @@ export const or = <Filters extends readonly Any[]>(...filters: Filters): Filter<
401
431
  // TODO(dmaretskyi): Add `Filter.match` to support pattern matching on string props.
402
432
 
403
433
  const propsFilterToAst = (predicates: Props<any>): Pick<QueryAST.FilterObject, 'id' | 'props'> => {
404
- let idFilter: readonly ObjectId[] | undefined;
434
+ let idFilter: readonly EntityId[] | undefined;
405
435
  if ('id' in predicates) {
406
436
  assertArgument(
407
437
  typeof predicates.id === 'string' || Array.isArray(predicates.id),
@@ -409,7 +439,7 @@ const propsFilterToAst = (predicates: Props<any>): Pick<QueryAST.FilterObject, '
409
439
  'invalid id filter',
410
440
  );
411
441
  idFilter = typeof predicates.id === 'string' ? [predicates.id] : predicates.id;
412
- Schema.Array(ObjectId).pipe(Schema.validateSync)(idFilter);
442
+ Schema.Array(EntityId).pipe(Schema.validateSync)(idFilter);
413
443
  }
414
444
 
415
445
  return {
package/src/Format.ts CHANGED
@@ -2,8 +2,4 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import type * as internal from './internal';
6
-
7
5
  export * from './internal/Format';
8
-
9
- export type Format = internal.TypeFormat;
package/src/Hypergraph.ts CHANGED
@@ -2,14 +2,14 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { type DXN } from '@dxos/keys';
5
+ import { type URI } from '@dxos/keys';
6
6
 
7
7
  import type * as Database from './Database';
8
8
  import type * as Entity from './Entity';
9
9
  import type * as internal from './internal';
10
10
  import type * as Key from './Key';
11
11
  import type * as Ref from './Ref';
12
- import type * as SchemaRegistry from './SchemaRegistry';
12
+ import type * as Registry from './Registry';
13
13
 
14
14
  /**
15
15
  * Resolution context.
@@ -22,10 +22,10 @@ export interface RefResolutionContext {
22
22
  space?: Key.SpaceId;
23
23
 
24
24
  /**
25
- * Queue that the resolution is happening from.
26
- * This queue will be searched first, and then the space it belongs to.
25
+ * Feed that the resolution is happening from.
26
+ * This feed will be searched first, and then the space it belongs to.
27
27
  */
28
- queue?: DXN;
28
+ feed?: URI.URI;
29
29
  }
30
30
 
31
31
  export interface RefResolverOptions {
@@ -46,7 +46,12 @@ export interface RefResolverOptions {
46
46
  * Manages cross-space database interactions.
47
47
  */
48
48
  export interface Hypergraph extends Database.Queryable {
49
- get schemaRegistry(): SchemaRegistry.SchemaRegistry;
49
+ /**
50
+ * In-process registry of keyed objects and static schema types.
51
+ * Populated at startup via `registry.add(objects)` / `registry.add(schemas)`.
52
+ * Queries that include no explicit from() clause will fan out to this registry automatically.
53
+ */
54
+ get registry(): Registry.Registry;
50
55
 
51
56
  /**
52
57
  * Query objects.
@@ -57,11 +62,11 @@ export interface Hypergraph extends Database.Queryable {
57
62
  * Creates a reference to an existing object in the database.
58
63
  *
59
64
  * NOTE: The reference may be dangling if the object is not present in the database.
60
- * NOTE: Difference from `Ref.fromDXN`
61
- * `Ref.fromDXN(dxn)` returns an unhydrated reference. The `.load` and `.target` APIs will not work.
65
+ * NOTE: Difference from `Ref.fromURI`
66
+ * `Ref.fromURI(dxn)` returns an unhydrated reference. The `.load` and `.target` APIs will not work.
62
67
  * `db.makeRef(dxn)` is preferable in cases with access to the database.
63
68
  */
64
- makeRef<T extends Entity.Unknown = Entity.Unknown>(dxn: DXN): Ref.Ref<T>;
69
+ makeRef<T extends Entity.Unknown = Entity.Unknown>(uri: URI.URI): Ref.Ref<T>;
65
70
 
66
71
  /**
67
72
  * @param hostDb Host database for reference resolution.
@@ -0,0 +1,175 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { describe, expect, test } from 'vitest';
6
+
7
+ import { DXN, EID, EntityId } from '@dxos/keys';
8
+ import { safeStringify } from '@dxos/util';
9
+
10
+ import * as Database from './Database';
11
+ import * as Json from './Json';
12
+
13
+ /** Mint a random ECHO object id usable as both a stub-db key and a DXN payload. */
14
+ const newId = (): string => EntityId.random();
15
+
16
+ /** Build a fake encoded ref for a local-space object id. */
17
+ const encodeRef = (id: string): { '/': string } => ({ '/': EID.make({ entityId: id }) });
18
+
19
+ /** Minimal stub: `createRefReplacer` only touches `db.getObjectById`. */
20
+ const makeStubDb = (objects: Record<string, unknown>): Database.Database => {
21
+ return {
22
+ getObjectById: (id: string) => objects[id],
23
+ } as unknown as Database.Database;
24
+ };
25
+
26
+ /**
27
+ * Run a value through the replacer's public contract — `JSON.stringify(value, replacer)` —
28
+ * then re-parse the result so tests can assert on plain JS shapes. This mirrors the way
29
+ * the JSON highlighter invokes the replacer in production.
30
+ */
31
+ const stringifyWith = (replacer: Json.JsonReplacer, value: unknown): unknown =>
32
+ JSON.parse(JSON.stringify(value, replacer));
33
+
34
+ describe('createRefReplacer', () => {
35
+ test('passes plain values through unchanged', () => {
36
+ const replacer = Json.createRefReplacer({ db: makeStubDb({}) });
37
+ const subject = { a: 1, b: 'two', c: [3, { d: 4 }] };
38
+ expect(stringifyWith(replacer, subject)).toEqual(subject);
39
+ });
40
+
41
+ test('inlines refs at default depth (1)', () => {
42
+ const id = newId();
43
+ const target = { name: 'inlined' };
44
+ const replacer = Json.createRefReplacer({ db: makeStubDb({ [id]: target }) });
45
+ const subject = { ref: encodeRef(id) };
46
+ expect(stringifyWith(replacer, subject)).toEqual({ ref: target });
47
+ });
48
+
49
+ test('does not follow refs when depth is 0', () => {
50
+ const id = newId();
51
+ const target = { name: 'inlined' };
52
+ const ref = encodeRef(id);
53
+ const replacer = Json.createRefReplacer({ db: makeStubDb({ [id]: target }), depth: 0 });
54
+ expect(stringifyWith(replacer, { ref })).toEqual({ ref });
55
+ });
56
+
57
+ test('inlines refs across multiple levels up to depth', () => {
58
+ const innerId = newId();
59
+ const middleId = newId();
60
+ const inner = { name: 'inner' };
61
+ const middle = { ref: encodeRef(innerId) };
62
+ const outer = { ref: encodeRef(middleId) };
63
+ const db = makeStubDb({ [innerId]: inner, [middleId]: middle });
64
+
65
+ expect(stringifyWith(Json.createRefReplacer({ db, depth: 1 }), outer)).toEqual({
66
+ ref: { ref: encodeRef(innerId) },
67
+ });
68
+
69
+ expect(stringifyWith(Json.createRefReplacer({ db, depth: 2 }), outer)).toEqual({
70
+ ref: { ref: inner },
71
+ });
72
+ });
73
+
74
+ test('leaves refs encoded when the target is missing in the db', () => {
75
+ const ref = encodeRef(newId());
76
+ const replacer = Json.createRefReplacer({ db: makeStubDb({}) });
77
+ expect(stringifyWith(replacer, { ref })).toEqual({ ref });
78
+ });
79
+
80
+ test('leaves non-DXN single-key { "/": string } objects untouched', () => {
81
+ // Same `{ '/': string }` shape is used by other IPLD-style refs (e.g. CIDs); those should
82
+ // not crash the replacer and should pass through verbatim.
83
+ const cidLike = { '/': 'bafybeibwzifw7izxykxz' };
84
+ const replacer = Json.createRefReplacer({ db: makeStubDb({}) });
85
+ expect(stringifyWith(replacer, { ref: cidLike })).toEqual({ ref: cidLike });
86
+ });
87
+
88
+ test('leaves malformed dxn strings untouched', () => {
89
+ const malformed = { '/': 'dxn:not-a-real-dxn' };
90
+ const replacer = Json.createRefReplacer({ db: makeStubDb({}) });
91
+ expect(stringifyWith(replacer, { ref: malformed })).toEqual({ ref: malformed });
92
+ });
93
+
94
+ test('leaves non-echo dxns untouched (e.g. type DXN)', () => {
95
+ // Type DXNs share the `dxn:` prefix but `asEchoDXN()` returns undefined.
96
+ const typeRef = { '/': DXN.make('com.example.Thing') };
97
+ const replacer = Json.createRefReplacer({ db: makeStubDb({}) });
98
+ expect(stringifyWith(replacer, { ref: typeRef })).toEqual({ ref: typeRef });
99
+ });
100
+
101
+ test('inlines refs inside arrays', () => {
102
+ const idA = newId();
103
+ const idB = newId();
104
+ const a = { name: 'a' };
105
+ const b = { name: 'b' };
106
+ const replacer = Json.createRefReplacer({ db: makeStubDb({ [idA]: a, [idB]: b }) });
107
+ expect(stringifyWith(replacer, { items: [encodeRef(idA), encodeRef(idB), { plain: true }] })).toEqual({
108
+ items: [a, b, { plain: true }],
109
+ });
110
+ });
111
+
112
+ test('walks nested objects recursively', () => {
113
+ const innerId = newId();
114
+ const inner = { name: 'inner' };
115
+ const replacer = Json.createRefReplacer({ db: makeStubDb({ [innerId]: inner }) });
116
+ const subject = { outer: { mid: { ref: encodeRef(innerId) } } };
117
+ expect(stringifyWith(replacer, subject)).toEqual({ outer: { mid: { ref: inner } } });
118
+ });
119
+
120
+ test('a single replacer invocation does not recurse on its own', () => {
121
+ // The replacer is per-call; JSON.stringify drives the tree walk. Calling the replacer
122
+ // directly on a cyclic input must therefore return without touching the cycle.
123
+ const replacer = Json.createRefReplacer({ db: makeStubDb({}) });
124
+ const node: any = { name: 'self' };
125
+ node.self = node;
126
+
127
+ expect(() => replacer('', node)).not.toThrow();
128
+ expect(replacer('', node)).toBe(node);
129
+ });
130
+
131
+ test('invokes `toJSON` on resolved targets so refs in the target are re-walked', () => {
132
+ // Simulates the ECHO-object branch: `db.getObjectById` returns a live proxy, the replacer
133
+ // calls `.toJSON()` to get the encoded form, then continues walking that form. A ref nested
134
+ // inside the target should be inlined when there's depth budget remaining.
135
+ const outerId = newId();
136
+ const innerId = newId();
137
+ const inner = { name: 'inner' };
138
+ const target = {
139
+ toJSON: () => ({ nestedRef: encodeRef(innerId) }),
140
+ };
141
+ const replacer = Json.createRefReplacer({ db: makeStubDb({ [outerId]: target, [innerId]: inner }), depth: 2 });
142
+ expect(stringifyWith(replacer, { ref: encodeRef(outerId) })).toEqual({
143
+ ref: { nestedRef: inner },
144
+ });
145
+ });
146
+
147
+ test('depth budget counts ref hops, not tree depth — a ref deep in a plain tree still resolves', () => {
148
+ // A ref nested under arbitrarily many plain objects is one ref hop from the root, so
149
+ // `depth: 1` resolves it. `depth: 0` leaves it encoded.
150
+ const innerId = newId();
151
+ const inner = { name: 'inner' };
152
+ const subject = { a: { b: { c: { d: { ref: encodeRef(innerId) } } } } };
153
+
154
+ const inlining = Json.createRefReplacer({ db: makeStubDb({ [innerId]: inner }), depth: 1 });
155
+ expect(stringifyWith(inlining, subject)).toEqual({ a: { b: { c: { d: { ref: inner } } } } });
156
+
157
+ const passthrough = Json.createRefReplacer({ db: makeStubDb({ [innerId]: inner }), depth: 0 });
158
+ expect(stringifyWith(passthrough, subject)).toEqual(subject);
159
+ });
160
+
161
+ test('inlines refs when invoked through safeStringify (production path)', () => {
162
+ // `JsonHighlighter` runs the replacer through `@dxos/util/safeStringify`, whose inner
163
+ // wrapper short-circuits the root call without forwarding it to the user's filter. The
164
+ // replacer must therefore work on a per-call basis — not as a one-shot root tree walk.
165
+ // This regression-tests that integration: the `content` ref must inline.
166
+ const targetId = newId();
167
+ const target = { toJSON: () => ({ name: 'README content' }) };
168
+ const document = { id: '01ABC', name: 'README', content: encodeRef(targetId) };
169
+
170
+ const replacer = Json.createRefReplacer({ db: makeStubDb({ [targetId]: target }) });
171
+ const out = JSON.parse(safeStringify(document, replacer, 0)!);
172
+
173
+ expect(out).toEqual({ id: '01ABC', name: 'README', content: { name: 'README content' } });
174
+ });
175
+ });
package/src/Json.ts ADDED
@@ -0,0 +1,103 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { EID } from '@dxos/keys';
6
+
7
+ import * as Database from './Database';
8
+ import * as Obj from './Obj';
9
+
10
+ /**
11
+ * `JSON.stringify` replacer signature.
12
+ *
13
+ * Defined here (rather than re-imported from a UI package) so other ECHO-aware utilities can
14
+ * share a stable signature without creating a dependency edge into the UI tree.
15
+ */
16
+ export type JsonReplacer = (key: string, value: any) => any;
17
+
18
+ export type CreateRefReplacerOptions = {
19
+ db: Database.Database;
20
+ /** How many ref hops to follow. `0` leaves all refs as-is. Default: `1`. */
21
+ depth?: number;
22
+ };
23
+
24
+ const isEncodedRef = (value: unknown): value is { '/': string } =>
25
+ typeof value === 'object' &&
26
+ value !== null &&
27
+ Object.keys(value as object).length === 1 &&
28
+ typeof (value as { '/': unknown })['/'] === 'string';
29
+
30
+ const toJson = (obj: Obj.Any): unknown => (typeof (obj as any).toJSON === 'function' ? (obj as any).toJSON() : obj);
31
+
32
+ /**
33
+ * Returns a {@link JsonReplacer} that inlines ECHO ref objects (`{ "/": "echo:..." }`) up to
34
+ * `depth` ref hops. Beyond that depth refs are left in their encoded form.
35
+ *
36
+ * Implemented as a per-call `JSON.stringify` replacer (not a one-shot tree walk at root) so it
37
+ * composes with wrappers like `safeStringify` that intercept the root call. JSON.stringify
38
+ * already drives the recursion; we only need to (a) detect a ref at the current callback,
39
+ * (b) resolve and return the target if hop budget remains, and (c) tag the returned object
40
+ * with its hop count so children know how far in they are.
41
+ *
42
+ * The hop count is tracked per-object via a `WeakMap`: a ref-resolved target's children inherit
43
+ * `parentHops + 1`; a regular intermediate object's children inherit `parentHops`. This makes the
44
+ * budget count *ref hops*, not tree depth — a ref deep in a tree still resolves once when
45
+ * `depth >= 1`.
46
+ *
47
+ * Note: ECHO objects' `toJSON` runs before the replacer is invoked, so by the time we see a
48
+ * value refs are already encoded as `{ "/": "dxn:..." }`.
49
+ */
50
+ export const createRefReplacer = ({ db, depth = 1 }: CreateRefReplacerOptions): JsonReplacer => {
51
+ // Per-object hop count. Set when we return an object (via ref resolution or pass-through) so
52
+ // the child callbacks (which carry that object as `this`) can read it.
53
+ const hops = new WeakMap<object, number>();
54
+
55
+ return function (this: any, key: string, value: any) {
56
+ // Hop count for this call: hops at the parent, or 0 for the root.
57
+ const parentHops = this && typeof this === 'object' ? (hops.get(this) ?? 0) : 0;
58
+ if (isEncodedRef(value)) {
59
+ if (parentHops >= depth) {
60
+ return value;
61
+ }
62
+
63
+ // The `{ '/': string }` shape is shared with non-DXN IPLD-style refs (e.g. CIDs);
64
+ // an unparseable string would otherwise crash the whole `JSON.stringify`.
65
+ // Treat any parse miss as "leave as-is" rather than propagating.
66
+ const dxnString = value['/'];
67
+ if (!dxnString.startsWith('dxn:') && !dxnString.startsWith('echo:')) {
68
+ return value;
69
+ }
70
+
71
+ let echoUri: string | undefined;
72
+ try {
73
+ const parsed = EID.tryParse(dxnString);
74
+ echoUri = parsed ? EID.getEntityId(parsed) : undefined;
75
+ } catch {
76
+ return value;
77
+ }
78
+
79
+ if (!echoUri) {
80
+ return value;
81
+ }
82
+ const target = db.getObjectById(echoUri);
83
+ if (!target) {
84
+ return value;
85
+ }
86
+
87
+ const encoded = toJson(target);
88
+ if (encoded != null && typeof encoded === 'object') {
89
+ // Children of the resolved target are one hop deeper.
90
+ hops.set(encoded as object, parentHops + 1);
91
+ }
92
+ return encoded;
93
+ }
94
+
95
+ // Pass-through object: children inherit the parent's hop count (this branch doesn't burn
96
+ // budget).
97
+ if (value != null && typeof value === 'object') {
98
+ hops.set(value, parentHops);
99
+ }
100
+
101
+ return value;
102
+ };
103
+ };
package/src/Key.ts CHANGED
@@ -5,4 +5,4 @@
5
5
  // TODO(wittjosiah): Should this be a subset of Type?
6
6
 
7
7
  export { ForeignKey } from '@dxos/echo-protocol';
8
- export { SpaceId, ObjectId } from '@dxos/keys';
8
+ export { EID, EntityId, SpaceId, URI } from '@dxos/keys';
package/src/Migration.ts CHANGED
@@ -6,23 +6,41 @@
6
6
 
7
7
  import type * as Schema from 'effect/Schema';
8
8
 
9
- import { type DXN } from '@dxos/keys';
9
+ import { type URI } from '@dxos/keys';
10
10
 
11
11
  import type * as Database from './Database';
12
12
  import type * as Entity from './Entity';
13
- import { getSchemaDXN } from './internal';
13
+ import { MetaId, type EntityMeta, getSchemaURI } from './internal';
14
+ import * as Type from './Type';
14
15
 
15
- type DefineObjectMigrationOptions<From extends Schema.Schema.AnyNoContext, To extends Schema.Schema.AnyNoContext> = {
16
+ /**
17
+ * Result returned by a migration's `transform` callback.
18
+ * The data shape matches the target schema; the optional `[Obj.Meta]` symbol key lets the
19
+ * transform update the object's meta (e.g. `key` / `version`) atomically with the data swap.
20
+ */
21
+ type MigrationSchemaInput = Type.AnyEntity;
22
+
23
+ type MigrationInstanceType<S> = S extends Type.AnyEntity
24
+ ? Type.InstanceType<S>
25
+ : S extends Schema.Schema.AnyNoContext
26
+ ? Schema.Schema.Type<S>
27
+ : never;
28
+
29
+ export type TransformResult<To> = Omit<MigrationInstanceType<To>, 'id' | Entity.KindId> & {
30
+ [MetaId]?: Partial<EntityMeta>;
31
+ };
32
+
33
+ type DefineObjectMigrationOptions<From extends MigrationSchemaInput, To extends MigrationSchemaInput> = {
16
34
  from: From;
17
35
  to: To;
18
36
  /**
19
37
  * Pure function that converts the old object data to the new object data.
38
+ *
39
+ * The returned object may include an optional `[Obj.Meta]` entry to update the object's meta
40
+ * (e.g. registry `key` / `version`) atomically with the data swap.
20
41
  */
21
42
  // TODO(dmaretskyi): `id` should not be a part of the schema.
22
- transform: (
23
- from: Schema.Schema.Type<From>,
24
- context: ObjectMigrationContext,
25
- ) => Promise<Omit<Schema.Schema.Type<To>, 'id' | Entity.KindId>>;
43
+ transform: (from: MigrationInstanceType<From>, context: ObjectMigrationContext) => Promise<TransformResult<To>>;
26
44
 
27
45
  /**
28
46
  * Callback that is called after the object is migrated. Called for every object that is migrated.
@@ -30,7 +48,7 @@ type DefineObjectMigrationOptions<From extends Schema.Schema.AnyNoContext, To ex
30
48
  * NOTE: Database mutations performed in this callback are not guaranteed to be idempotent.
31
49
  * If multiple peers run the migration separately, the effects may be applied multiple times.
32
50
  */
33
- onMigration: (params: OnMigrateProps<From, To>) => Promise<void>;
51
+ onMigration?: (params: OnMigrateProps<From, To>) => Promise<void>;
34
52
  };
35
53
 
36
54
  /**
@@ -40,9 +58,9 @@ export type ObjectMigrationContext = {
40
58
  db: Database.Database;
41
59
  };
42
60
 
43
- type OnMigrateProps<From extends Schema.Schema.AnyNoContext, To extends Schema.Schema.AnyNoContext> = {
44
- before: Schema.Schema.Type<From>;
45
- object: Schema.Schema.Type<To>;
61
+ type OnMigrateProps<From, To> = {
62
+ before: MigrationInstanceType<From>;
63
+ object: MigrationInstanceType<To>;
46
64
  db: Database.Database;
47
65
  };
48
66
 
@@ -50,12 +68,12 @@ type OnMigrateProps<From extends Schema.Schema.AnyNoContext, To extends Schema.S
50
68
  * Definition of a migration from one object schema version to another.
51
69
  */
52
70
  export type ObjectMigration = {
53
- fromType: DXN;
54
- toType: DXN;
71
+ fromType: URI.URI;
72
+ toType: URI.URI;
55
73
  fromSchema: Schema.Schema.AnyNoContext;
56
74
  toSchema: Schema.Schema.AnyNoContext;
57
75
  transform: (from: unknown, context: ObjectMigrationContext) => Promise<unknown>;
58
- onMigration: (params: OnMigrateProps<any, any>) => Promise<void>;
76
+ onMigration?: (params: OnMigrateProps<any, any>) => Promise<void>;
59
77
  };
60
78
 
61
79
  /**
@@ -71,14 +89,16 @@ export type ObjectMigration = {
71
89
  * });
72
90
  * ```
73
91
  */
74
- export const define = <From extends Schema.Schema.AnyNoContext, To extends Schema.Schema.AnyNoContext>(
92
+ export const define = <From extends MigrationSchemaInput, To extends MigrationSchemaInput>(
75
93
  options: DefineObjectMigrationOptions<From, To>,
76
94
  ): ObjectMigration => {
77
- const fromType = getSchemaDXN(options.from);
95
+ const fromSchema = Type.getSchema(options.from);
96
+ const toSchema = Type.getSchema(options.to);
97
+ const fromType = getSchemaURI(fromSchema);
78
98
  if (!fromType) {
79
99
  throw new Error('Invalid from schema');
80
100
  }
81
- const toType = getSchemaDXN(options.to);
101
+ const toType = getSchemaURI(toSchema);
82
102
  if (!toType) {
83
103
  throw new Error('Invalid to schema');
84
104
  }
@@ -86,8 +106,8 @@ export const define = <From extends Schema.Schema.AnyNoContext, To extends Schem
86
106
  return {
87
107
  fromType,
88
108
  toType,
89
- fromSchema: options.from,
90
- toSchema: options.to,
109
+ fromSchema,
110
+ toSchema,
91
111
  transform: options.transform as any,
92
112
  onMigration: options.onMigration as any,
93
113
  };