@dxos/echo 0.8.4-main.d05539e30a → 0.8.4-main.d9fc60f731

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 (378) hide show
  1. package/README.md +4 -4
  2. package/dist/lib/neutral/Annotation.mjs +27 -6
  3. package/dist/lib/neutral/Database.mjs +6 -11
  4. package/dist/lib/neutral/Entity.mjs +25 -16
  5. package/dist/lib/neutral/Err.mjs +3 -3
  6. package/dist/lib/neutral/Feed.mjs +19 -17
  7. package/dist/lib/neutral/Filter.mjs +13 -13
  8. package/dist/lib/neutral/Format.mjs +23 -3
  9. package/dist/lib/neutral/JsonSchema.mjs +7 -8
  10. package/dist/lib/neutral/Key.mjs +9 -5
  11. package/dist/lib/neutral/Migration.mjs +11 -10
  12. package/dist/lib/neutral/Obj.mjs +21 -18
  13. package/dist/lib/neutral/Query.mjs +17 -17
  14. package/dist/lib/neutral/QueryResult.mjs +1 -1
  15. package/dist/lib/neutral/Ref.mjs +10 -11
  16. package/dist/lib/neutral/Registry.mjs +14 -0
  17. package/dist/lib/neutral/Relation.mjs +24 -23
  18. package/dist/lib/neutral/Scope.mjs +12 -0
  19. package/dist/lib/neutral/Tag.mjs +17 -14
  20. package/dist/lib/neutral/Type.mjs +52 -26
  21. package/dist/lib/neutral/chunk-3DKP2EIN.mjs +31 -0
  22. package/dist/lib/neutral/chunk-3DKP2EIN.mjs.map +7 -0
  23. package/dist/lib/neutral/chunk-4BB4MFCN.mjs +154 -0
  24. package/dist/lib/neutral/chunk-4BB4MFCN.mjs.map +7 -0
  25. package/dist/lib/neutral/{chunk-PT37DG2F.mjs → chunk-4SFPZJVA.mjs} +82 -74
  26. package/dist/lib/neutral/chunk-4SFPZJVA.mjs.map +7 -0
  27. package/dist/lib/neutral/{chunk-APHSOTIX.mjs → chunk-4Z6GET2X.mjs} +3 -3
  28. package/dist/lib/neutral/{chunk-APHSOTIX.mjs.map → chunk-4Z6GET2X.mjs.map} +2 -2
  29. package/dist/lib/neutral/{chunk-7RVZT53K.mjs → chunk-7LOUAPYZ.mjs} +9 -5
  30. package/dist/lib/neutral/chunk-7LOUAPYZ.mjs.map +7 -0
  31. package/dist/lib/neutral/{chunk-BMB7IHGB.mjs → chunk-7OAFN3OX.mjs} +70 -57
  32. package/dist/lib/neutral/chunk-7OAFN3OX.mjs.map +7 -0
  33. package/dist/lib/neutral/{chunk-MLS7U7AT.mjs → chunk-7PRCIDHH.mjs} +41 -28
  34. package/dist/lib/neutral/chunk-7PRCIDHH.mjs.map +7 -0
  35. package/dist/lib/neutral/{chunk-ZISMEVKD.mjs → chunk-BBFJWWAV.mjs} +6 -6
  36. package/dist/lib/neutral/chunk-BBFJWWAV.mjs.map +7 -0
  37. package/dist/lib/neutral/{chunk-ZFACXBY6.mjs → chunk-BDQGMDIX.mjs} +30 -34
  38. package/dist/lib/neutral/chunk-BDQGMDIX.mjs.map +7 -0
  39. package/dist/lib/neutral/chunk-CGTI5MXO.mjs +36 -0
  40. package/dist/lib/neutral/chunk-CGTI5MXO.mjs.map +7 -0
  41. package/dist/lib/neutral/{chunk-V72DY6LU.mjs → chunk-DKLUHO4T.mjs} +1 -1
  42. package/dist/lib/neutral/chunk-DKLUHO4T.mjs.map +7 -0
  43. package/dist/lib/neutral/chunk-DMN2CKPX.mjs +93 -0
  44. package/dist/lib/neutral/chunk-DMN2CKPX.mjs.map +7 -0
  45. package/dist/lib/neutral/{chunk-QRZ2I3ZM.mjs → chunk-G6CFWUYH.mjs} +2 -2
  46. package/dist/lib/neutral/chunk-H26LSXVJ.mjs +653 -0
  47. package/dist/lib/neutral/chunk-H26LSXVJ.mjs.map +7 -0
  48. package/dist/lib/neutral/{chunk-N7VOEPSV.mjs → chunk-J3MNM65T.mjs} +9 -9
  49. package/dist/lib/neutral/chunk-J3MNM65T.mjs.map +7 -0
  50. package/dist/lib/neutral/chunk-JE7AOSWF.mjs +247 -0
  51. package/dist/lib/neutral/chunk-JE7AOSWF.mjs.map +7 -0
  52. package/dist/lib/neutral/chunk-LAF3LSWI.mjs +379 -0
  53. package/dist/lib/neutral/chunk-LAF3LSWI.mjs.map +7 -0
  54. package/dist/lib/neutral/chunk-LMNRFOVB.mjs +108 -0
  55. package/dist/lib/neutral/chunk-LMNRFOVB.mjs.map +7 -0
  56. package/dist/lib/neutral/{chunk-N4B7FHQT.mjs → chunk-O6BH7EPN.mjs} +30 -3
  57. package/dist/lib/neutral/chunk-O6BH7EPN.mjs.map +7 -0
  58. package/dist/lib/neutral/{chunk-Q7ZL2P5H.mjs → chunk-OIYW7R2G.mjs} +62 -46
  59. package/dist/lib/neutral/chunk-OIYW7R2G.mjs.map +7 -0
  60. package/dist/lib/neutral/{chunk-G54OX4IX.mjs → chunk-OYUG4FHF.mjs} +527 -847
  61. package/dist/lib/neutral/chunk-OYUG4FHF.mjs.map +7 -0
  62. package/dist/lib/neutral/{chunk-J54QMAKF.mjs → chunk-Q5FKCVBC.mjs} +64 -59
  63. package/dist/lib/neutral/chunk-Q5FKCVBC.mjs.map +7 -0
  64. package/dist/lib/neutral/{chunk-FIWO2FZK.mjs → chunk-Q62IHV2N.mjs} +12 -7
  65. package/dist/lib/neutral/chunk-Q62IHV2N.mjs.map +7 -0
  66. package/dist/lib/neutral/{chunk-SCPFDS2E.mjs → chunk-QVJDDG44.mjs} +65 -34
  67. package/dist/lib/neutral/chunk-QVJDDG44.mjs.map +7 -0
  68. package/dist/lib/neutral/{chunk-TRPZU2HV.mjs → chunk-TFEWTY5A.mjs} +155 -129
  69. package/dist/lib/neutral/chunk-TFEWTY5A.mjs.map +7 -0
  70. package/dist/lib/neutral/{chunk-44HT3MEC.mjs → chunk-VKWOFXT2.mjs} +22 -2
  71. package/dist/lib/neutral/{chunk-44HT3MEC.mjs.map → chunk-VKWOFXT2.mjs.map} +1 -1
  72. package/dist/lib/neutral/{chunk-APJKDGFL.mjs → chunk-ZACMEAYV.mjs} +24 -24
  73. package/dist/lib/neutral/chunk-ZACMEAYV.mjs.map +7 -0
  74. package/dist/lib/neutral/index.mjs +50 -40
  75. package/dist/lib/neutral/internal/index.mjs +125 -64
  76. package/dist/lib/neutral/meta.json +1 -1
  77. package/dist/lib/neutral/testing/index.mjs +88 -99
  78. package/dist/lib/neutral/testing/index.mjs.map +3 -3
  79. package/dist/types/src/Annotation.d.ts +108 -4
  80. package/dist/types/src/Annotation.d.ts.map +1 -1
  81. package/dist/types/src/Annotation.test.d.ts +2 -0
  82. package/dist/types/src/Annotation.test.d.ts.map +1 -0
  83. package/dist/types/src/Collection.d.ts +2 -3
  84. package/dist/types/src/Collection.d.ts.map +1 -1
  85. package/dist/types/src/Database.d.ts +50 -30
  86. package/dist/types/src/Database.d.ts.map +1 -1
  87. package/dist/types/src/Dataset.d.ts +16 -6
  88. package/dist/types/src/Dataset.d.ts.map +1 -1
  89. package/dist/types/src/Entity.d.ts +89 -19
  90. package/dist/types/src/Entity.d.ts.map +1 -1
  91. package/dist/types/src/Err.d.ts +9 -9
  92. package/dist/types/src/Err.d.ts.map +1 -1
  93. package/dist/types/src/Feed.d.ts +35 -12
  94. package/dist/types/src/Feed.d.ts.map +1 -1
  95. package/dist/types/src/Filter.d.ts +21 -8
  96. package/dist/types/src/Filter.d.ts.map +1 -1
  97. package/dist/types/src/Hypergraph.d.ts +13 -8
  98. package/dist/types/src/Hypergraph.d.ts.map +1 -1
  99. package/dist/types/src/Json.d.ts.map +1 -1
  100. package/dist/types/src/JsonSchema.d.ts +1 -1
  101. package/dist/types/src/JsonSchema.d.ts.map +1 -1
  102. package/dist/types/src/Key.d.ts +1 -1
  103. package/dist/types/src/Key.d.ts.map +1 -1
  104. package/dist/types/src/Migration.d.ts +15 -12
  105. package/dist/types/src/Migration.d.ts.map +1 -1
  106. package/dist/types/src/Obj.d.ts +69 -40
  107. package/dist/types/src/Obj.d.ts.map +1 -1
  108. package/dist/types/src/Query.d.ts +24 -10
  109. package/dist/types/src/Query.d.ts.map +1 -1
  110. package/dist/types/src/QueryResult.d.ts +9 -0
  111. package/dist/types/src/QueryResult.d.ts.map +1 -1
  112. package/dist/types/src/Ref.d.ts +15 -8
  113. package/dist/types/src/Ref.d.ts.map +1 -1
  114. package/dist/types/src/Registry.d.ts +131 -0
  115. package/dist/types/src/Registry.d.ts.map +1 -0
  116. package/dist/types/src/Relation.d.ts +55 -26
  117. package/dist/types/src/Relation.d.ts.map +1 -1
  118. package/dist/types/src/Scope.d.ts +34 -0
  119. package/dist/types/src/Scope.d.ts.map +1 -0
  120. package/dist/types/src/StateMap.d.ts +32 -0
  121. package/dist/types/src/StateMap.d.ts.map +1 -0
  122. package/dist/types/src/StateMap.test.d.ts +2 -0
  123. package/dist/types/src/StateMap.test.d.ts.map +1 -0
  124. package/dist/types/src/Tag.d.ts +19 -3
  125. package/dist/types/src/Tag.d.ts.map +1 -1
  126. package/dist/types/src/TagIndex.d.ts +36 -0
  127. package/dist/types/src/TagIndex.d.ts.map +1 -0
  128. package/dist/types/src/TagIndex.test.d.ts +2 -0
  129. package/dist/types/src/TagIndex.test.d.ts.map +1 -0
  130. package/dist/types/src/Tagging.d.ts +31 -0
  131. package/dist/types/src/Tagging.d.ts.map +1 -0
  132. package/dist/types/src/Type.d.ts +356 -95
  133. package/dist/types/src/Type.d.ts.map +1 -1
  134. package/dist/types/src/View.d.ts +1 -4
  135. package/dist/types/src/View.d.ts.map +1 -1
  136. package/dist/types/src/index.d.ts +6 -3
  137. package/dist/types/src/index.d.ts.map +1 -1
  138. package/dist/types/src/internal/Annotation/annotations.d.ts +68 -36
  139. package/dist/types/src/internal/Annotation/annotations.d.ts.map +1 -1
  140. package/dist/types/src/internal/Annotation/dictionary.d.ts +24 -0
  141. package/dist/types/src/internal/Annotation/dictionary.d.ts.map +1 -0
  142. package/dist/types/src/internal/Annotation/entity-dictionary.d.ts +14 -0
  143. package/dist/types/src/internal/Annotation/entity-dictionary.d.ts.map +1 -0
  144. package/dist/types/src/internal/Annotation/index.d.ts +4 -2
  145. package/dist/types/src/internal/Annotation/index.d.ts.map +1 -1
  146. package/dist/types/src/internal/Annotation/util.d.ts +13 -4
  147. package/dist/types/src/internal/Annotation/util.d.ts.map +1 -1
  148. package/dist/types/src/internal/Entity/api.d.ts +3 -3
  149. package/dist/types/src/internal/Entity/api.d.ts.map +1 -1
  150. package/dist/types/src/internal/Entity/entity.d.ts +72 -8
  151. package/dist/types/src/internal/Entity/entity.d.ts.map +1 -1
  152. package/dist/types/src/internal/Entity/guard.d.ts +10 -0
  153. package/dist/types/src/internal/Entity/guard.d.ts.map +1 -0
  154. package/dist/types/src/internal/Entity/index.d.ts +2 -0
  155. package/dist/types/src/internal/Entity/index.d.ts.map +1 -1
  156. package/dist/types/src/internal/Entity/model.d.ts +18 -17
  157. package/dist/types/src/internal/Entity/model.d.ts.map +1 -1
  158. package/dist/types/src/internal/Entity/object.d.ts +3 -3
  159. package/dist/types/src/internal/Entity/object.d.ts.map +1 -1
  160. package/dist/types/src/internal/Entity/relation.d.ts +30 -7
  161. package/dist/types/src/internal/Entity/relation.d.ts.map +1 -1
  162. package/dist/types/src/internal/Entity/type-kind.d.ts +24 -0
  163. package/dist/types/src/internal/Entity/type-kind.d.ts.map +1 -0
  164. package/dist/types/src/internal/Entity/type-uri.d.ts +25 -0
  165. package/dist/types/src/internal/Entity/type-uri.d.ts.map +1 -0
  166. package/dist/types/src/internal/Format/date.d.ts.map +1 -1
  167. package/dist/types/src/internal/Format/format.d.ts +3 -2
  168. package/dist/types/src/internal/Format/format.d.ts.map +1 -1
  169. package/dist/types/src/internal/Format/index.d.ts +2 -2
  170. package/dist/types/src/internal/Format/index.d.ts.map +1 -1
  171. package/dist/types/src/internal/Format/object.d.ts +3 -1
  172. package/dist/types/src/internal/Format/object.d.ts.map +1 -1
  173. package/dist/types/src/internal/JsonSchema/json-schema.d.ts +2 -1
  174. package/dist/types/src/internal/JsonSchema/json-schema.d.ts.map +1 -1
  175. package/dist/types/src/internal/Obj/clone.d.ts.map +1 -1
  176. package/dist/types/src/internal/Obj/create-object.d.ts +11 -12
  177. package/dist/types/src/internal/Obj/create-object.d.ts.map +1 -1
  178. package/dist/types/src/internal/Obj/index.d.ts +0 -1
  179. package/dist/types/src/internal/Obj/index.d.ts.map +1 -1
  180. package/dist/types/src/internal/Obj/json-serializer.d.ts +8 -8
  181. package/dist/types/src/internal/Obj/json-serializer.d.ts.map +1 -1
  182. package/dist/types/src/internal/Obj/snapshot.d.ts.map +1 -1
  183. package/dist/types/src/internal/Obj/typed-object.d.ts +1 -1
  184. package/dist/types/src/internal/Query.d.ts.map +1 -1
  185. package/dist/types/src/internal/Ref/ref-array.d.ts +2 -2
  186. package/dist/types/src/internal/Ref/ref.d.ts +29 -19
  187. package/dist/types/src/internal/Ref/ref.d.ts.map +1 -1
  188. package/dist/types/src/internal/Type/index.d.ts +1 -2
  189. package/dist/types/src/internal/Type/index.d.ts.map +1 -1
  190. package/dist/types/src/internal/Type/manipulation.d.ts +0 -1
  191. package/dist/types/src/internal/Type/manipulation.d.ts.map +1 -1
  192. package/dist/types/src/internal/Type/type-schema.d.ts +52 -0
  193. package/dist/types/src/internal/Type/type-schema.d.ts.map +1 -0
  194. package/dist/types/src/internal/common/api/meta.d.ts +11 -8
  195. package/dist/types/src/internal/common/api/meta.d.ts.map +1 -1
  196. package/dist/types/src/internal/common/proxy/json-serializer.d.ts.map +1 -1
  197. package/dist/types/src/internal/common/proxy/make-object.d.ts +11 -5
  198. package/dist/types/src/internal/common/proxy/make-object.d.ts.map +1 -1
  199. package/dist/types/src/internal/common/proxy/typed-handler.d.ts +18 -2
  200. package/dist/types/src/internal/common/proxy/typed-handler.d.ts.map +1 -1
  201. package/dist/types/src/internal/common/types/base.d.ts +4 -4
  202. package/dist/types/src/internal/common/types/entity.d.ts +59 -2
  203. package/dist/types/src/internal/common/types/entity.d.ts.map +1 -1
  204. package/dist/types/src/internal/common/types/index.d.ts +1 -1
  205. package/dist/types/src/internal/common/types/index.d.ts.map +1 -1
  206. package/dist/types/src/internal/common/types/meta.d.ts +13 -7
  207. package/dist/types/src/internal/common/types/meta.d.ts.map +1 -1
  208. package/dist/types/src/internal/common/types/model-symbols.d.ts +8 -4
  209. package/dist/types/src/internal/common/types/model-symbols.d.ts.map +1 -1
  210. package/dist/types/src/internal/common/types/typename.d.ts +7 -0
  211. package/dist/types/src/internal/common/types/typename.d.ts.map +1 -1
  212. package/dist/types/src/internal/common/types/well-known-types.d.ts +11 -0
  213. package/dist/types/src/internal/common/types/well-known-types.d.ts.map +1 -0
  214. package/dist/types/src/internal/index.d.ts +1 -0
  215. package/dist/types/src/internal/index.d.ts.map +1 -1
  216. package/dist/types/src/testing/test-schema.d.ts +32 -38
  217. package/dist/types/src/testing/test-schema.d.ts.map +1 -1
  218. package/dist/types/src/testing/util.d.ts +5 -3
  219. package/dist/types/src/testing/util.d.ts.map +1 -1
  220. package/dist/types/tsconfig.tsbuildinfo +1 -1
  221. package/package.json +21 -23
  222. package/src/Annotation.test.ts +439 -0
  223. package/src/Annotation.ts +151 -4
  224. package/src/Collection.ts +5 -9
  225. package/src/Database.ts +86 -61
  226. package/src/Dataset.ts +10 -2
  227. package/src/Entity.test.ts +116 -6
  228. package/src/Entity.ts +116 -23
  229. package/src/Err.ts +4 -4
  230. package/src/Feed.ts +57 -40
  231. package/src/Filter.ts +49 -27
  232. package/src/Hypergraph.ts +13 -8
  233. package/src/Json.test.ts +4 -4
  234. package/src/Json.ts +7 -6
  235. package/src/Key.ts +1 -1
  236. package/src/Migration.ts +27 -19
  237. package/src/Obj.test.ts +19 -8
  238. package/src/Obj.ts +131 -70
  239. package/src/Query.test.ts +147 -151
  240. package/src/Query.ts +133 -75
  241. package/src/QueryResult.ts +11 -0
  242. package/src/Ref.ts +21 -5
  243. package/src/Registry.ts +155 -0
  244. package/src/Relation.test.ts +10 -10
  245. package/src/Relation.ts +89 -52
  246. package/src/Scope.ts +50 -0
  247. package/src/StateMap.test.ts +56 -0
  248. package/src/StateMap.ts +99 -0
  249. package/src/Tag.md +88 -0
  250. package/src/Tag.ts +49 -6
  251. package/src/TagIndex.test.ts +57 -0
  252. package/src/TagIndex.ts +110 -0
  253. package/src/Tagging.ts +83 -0
  254. package/src/Type.test.ts +223 -18
  255. package/src/Type.ts +601 -131
  256. package/src/View.ts +10 -19
  257. package/src/index.ts +6 -4
  258. package/src/internal/Annotation/annotations.test.ts +31 -11
  259. package/src/internal/Annotation/annotations.ts +122 -103
  260. package/src/internal/Annotation/dictionary.ts +47 -0
  261. package/src/internal/Annotation/entity-dictionary.ts +40 -0
  262. package/src/internal/Annotation/index.ts +4 -2
  263. package/src/internal/Annotation/util.ts +17 -8
  264. package/src/internal/Entity/api.ts +7 -7
  265. package/src/internal/Entity/entity.ts +196 -47
  266. package/src/internal/Entity/guard.ts +26 -0
  267. package/src/internal/Entity/index.ts +2 -0
  268. package/src/internal/Entity/model.ts +35 -28
  269. package/src/internal/Entity/object.ts +21 -5
  270. package/src/internal/Entity/relation.ts +68 -34
  271. package/src/internal/Entity/type-kind.ts +75 -0
  272. package/src/internal/Entity/type-uri.ts +87 -0
  273. package/src/internal/Entity/util.ts +10 -9
  274. package/src/internal/Format/date.ts +0 -4
  275. package/src/internal/Format/format.test.ts +21 -0
  276. package/src/internal/Format/index.ts +2 -3
  277. package/src/internal/Format/object.ts +21 -4
  278. package/src/internal/Format/types.ts +1 -1
  279. package/src/internal/JsonSchema/annotations.ts +1 -1
  280. package/src/internal/JsonSchema/json-schema.test.ts +54 -140
  281. package/src/internal/JsonSchema/json-schema.ts +44 -30
  282. package/src/internal/Obj/clone.ts +9 -4
  283. package/src/internal/Obj/create-object.test.ts +12 -10
  284. package/src/internal/Obj/create-object.ts +67 -22
  285. package/src/internal/Obj/index.ts +0 -1
  286. package/src/internal/Obj/inspect.ts +4 -3
  287. package/src/internal/Obj/json-serializer.test.ts +100 -21
  288. package/src/internal/Obj/json-serializer.ts +93 -38
  289. package/src/internal/Obj/set-value.test.ts +7 -30
  290. package/src/internal/Obj/snapshot.ts +11 -4
  291. package/src/internal/Obj/typed-object.test.ts +9 -11
  292. package/src/internal/Obj/typed-object.ts +1 -1
  293. package/src/internal/Query.ts +14 -11
  294. package/src/internal/Ref/ref-array.ts +3 -3
  295. package/src/internal/Ref/ref.test.ts +17 -26
  296. package/src/internal/Ref/ref.ts +107 -59
  297. package/src/internal/Type/compose.test.ts +3 -1
  298. package/src/internal/Type/index.ts +1 -2
  299. package/src/internal/Type/manipulation.ts +0 -25
  300. package/src/internal/Type/type-schema.ts +60 -0
  301. package/src/internal/common/README.md +1 -1
  302. package/src/internal/common/api/meta.ts +16 -14
  303. package/src/internal/common/proxy/change.test.ts +35 -27
  304. package/src/internal/common/proxy/handler.test.ts +1 -1
  305. package/src/internal/common/proxy/json-serializer.ts +26 -16
  306. package/src/internal/common/proxy/make-object.ts +43 -20
  307. package/src/internal/common/proxy/ownership.ts +2 -2
  308. package/src/internal/common/proxy/schema.test.ts +48 -86
  309. package/src/internal/common/proxy/typed-handler.test.ts +12 -11
  310. package/src/internal/common/proxy/typed-handler.ts +71 -9
  311. package/src/internal/common/proxy/typed-object.test.ts +15 -27
  312. package/src/internal/common/types/base.ts +4 -4
  313. package/src/internal/common/types/entity.ts +80 -1
  314. package/src/internal/common/types/index.ts +6 -1
  315. package/src/internal/common/types/meta.ts +37 -14
  316. package/src/internal/common/types/model-symbols.ts +16 -4
  317. package/src/internal/common/types/typename.ts +39 -3
  318. package/src/internal/common/types/well-known-types.ts +15 -0
  319. package/src/internal/index.ts +5 -0
  320. package/src/testing/api.test.ts +15 -9
  321. package/src/testing/test-data.ts +24 -24
  322. package/src/testing/test-schema.ts +22 -58
  323. package/src/testing/util.ts +14 -11
  324. package/dist/lib/neutral/Extension.mjs +0 -18
  325. package/dist/lib/neutral/SchemaRegistry.mjs +0 -2
  326. package/dist/lib/neutral/chunk-5SL5LDLD.mjs +0 -47
  327. package/dist/lib/neutral/chunk-5SL5LDLD.mjs.map +0 -7
  328. package/dist/lib/neutral/chunk-7RVZT53K.mjs.map +0 -7
  329. package/dist/lib/neutral/chunk-APJKDGFL.mjs.map +0 -7
  330. package/dist/lib/neutral/chunk-BICZKPQG.mjs +0 -7
  331. package/dist/lib/neutral/chunk-BICZKPQG.mjs.map +0 -7
  332. package/dist/lib/neutral/chunk-BMB7IHGB.mjs.map +0 -7
  333. package/dist/lib/neutral/chunk-FIWO2FZK.mjs.map +0 -7
  334. package/dist/lib/neutral/chunk-G54OX4IX.mjs.map +0 -7
  335. package/dist/lib/neutral/chunk-GWFFC34K.mjs +0 -50
  336. package/dist/lib/neutral/chunk-GWFFC34K.mjs.map +0 -7
  337. package/dist/lib/neutral/chunk-I2DARWPX.mjs +0 -128
  338. package/dist/lib/neutral/chunk-I2DARWPX.mjs.map +0 -7
  339. package/dist/lib/neutral/chunk-J54QMAKF.mjs.map +0 -7
  340. package/dist/lib/neutral/chunk-MGSQGHOD.mjs +0 -206
  341. package/dist/lib/neutral/chunk-MGSQGHOD.mjs.map +0 -7
  342. package/dist/lib/neutral/chunk-MLS7U7AT.mjs.map +0 -7
  343. package/dist/lib/neutral/chunk-N4B7FHQT.mjs.map +0 -7
  344. package/dist/lib/neutral/chunk-N7VOEPSV.mjs.map +0 -7
  345. package/dist/lib/neutral/chunk-PSZBLH53.mjs +0 -81
  346. package/dist/lib/neutral/chunk-PSZBLH53.mjs.map +0 -7
  347. package/dist/lib/neutral/chunk-PT37DG2F.mjs.map +0 -7
  348. package/dist/lib/neutral/chunk-Q2KKKJSV.mjs +0 -56
  349. package/dist/lib/neutral/chunk-Q2KKKJSV.mjs.map +0 -7
  350. package/dist/lib/neutral/chunk-Q7ZL2P5H.mjs.map +0 -7
  351. package/dist/lib/neutral/chunk-SCPFDS2E.mjs.map +0 -7
  352. package/dist/lib/neutral/chunk-TNBK56IN.mjs +0 -350
  353. package/dist/lib/neutral/chunk-TNBK56IN.mjs.map +0 -7
  354. package/dist/lib/neutral/chunk-TRPZU2HV.mjs.map +0 -7
  355. package/dist/lib/neutral/chunk-V72DY6LU.mjs.map +0 -7
  356. package/dist/lib/neutral/chunk-ZFACXBY6.mjs.map +0 -7
  357. package/dist/lib/neutral/chunk-ZISMEVKD.mjs.map +0 -7
  358. package/dist/types/src/Extension.d.ts +0 -80
  359. package/dist/types/src/Extension.d.ts.map +0 -1
  360. package/dist/types/src/Extension.test.d.ts +0 -2
  361. package/dist/types/src/Extension.test.d.ts.map +0 -1
  362. package/dist/types/src/SchemaRegistry.d.ts +0 -84
  363. package/dist/types/src/SchemaRegistry.d.ts.map +0 -1
  364. package/dist/types/src/internal/Obj/ids.d.ts +0 -6
  365. package/dist/types/src/internal/Obj/ids.d.ts.map +0 -1
  366. package/dist/types/src/internal/Type/echo-schema.d.ts +0 -181
  367. package/dist/types/src/internal/Type/echo-schema.d.ts.map +0 -1
  368. package/dist/types/src/internal/Type/persistent-schema.d.ts +0 -20
  369. package/dist/types/src/internal/Type/persistent-schema.d.ts.map +0 -1
  370. package/src/Extension.test.ts +0 -235
  371. package/src/Extension.ts +0 -122
  372. package/src/SchemaRegistry.ts +0 -106
  373. package/src/internal/Obj/ids.ts +0 -12
  374. package/src/internal/Type/echo-schema.ts +0 -423
  375. package/src/internal/Type/persistent-schema.ts +0 -33
  376. /package/dist/lib/neutral/{Extension.mjs.map → Registry.mjs.map} +0 -0
  377. /package/dist/lib/neutral/{SchemaRegistry.mjs.map → Scope.mjs.map} +0 -0
  378. /package/dist/lib/neutral/{chunk-QRZ2I3ZM.mjs.map → chunk-G6CFWUYH.mjs.map} +0 -0
package/src/Relation.ts CHANGED
@@ -10,7 +10,7 @@ import { raise } from '@dxos/debug';
10
10
  import type { ForeignKey } from '@dxos/echo-protocol';
11
11
  import { createJsonPath } from '@dxos/effect';
12
12
  import { assertArgument, invariant } from '@dxos/invariant';
13
- import { DXN, type ObjectId } from '@dxos/keys';
13
+ import { EID, type EntityId, type URI, DXN } from '@dxos/keys';
14
14
  import { assumeType } from '@dxos/util';
15
15
 
16
16
  import type * as Database from './Database';
@@ -18,7 +18,9 @@ import * as Entity from './Entity';
18
18
  import * as internal from './internal';
19
19
  import * as entityInternal from './internal/Entity';
20
20
  import * as Obj from './Obj';
21
- import type * as Type from './Type';
21
+ import type * as Ref from './Ref';
22
+ import type * as Tag from './Tag';
23
+ import * as Type from './Type';
22
24
 
23
25
  export type Endpoints<Source, Target> = {
24
26
  [Source]: Source;
@@ -59,24 +61,20 @@ export interface Unknown extends BaseRelation<Obj.Unknown, Obj.Unknown> {}
59
61
  * ```
60
62
  */
61
63
  // TODO(dmaretskyi): Change ObjModule.Any to ObjModule.Unknown to have stricter types.
62
- export const Unknown: Type.Relation<Unknown, Obj.Any, Obj.Any> = Schema.Struct({
64
+ export const Unknown: internal.UnknownTypeSchema<Unknown, typeof Entity.Kind.Relation> = Schema.Struct({
63
65
  id: Schema.String,
64
66
  }).pipe(
65
67
  Schema.extend(Schema.Record({ key: Schema.String, value: Schema.Unknown })),
66
- // TODO(dmaretskyi): Clean this up.
67
- // NOTE: The EchoRelationSchema annotation is required for Ref.Ref(Relation.Unknown) to work.
68
- // The typename/version/source/target only satisfy ECHO schema machinery for reference targets.
69
- internal.EchoRelationSchema({
70
- typename: 'org.dxos.schema.anyRelation',
71
- version: '0.0.0',
72
- source: Obj.Unknown,
73
- target: Obj.Unknown,
68
+ Schema.annotations({
69
+ [internal.TypeAnnotationId]: {
70
+ kind: Entity.Kind.Relation,
71
+ typename: 'org.dxos.schema.anyRelation',
72
+ version: '0.0.0',
73
+ sourceSchema: DXN.make(internal.ANY_OBJECT_TYPENAME, internal.ANY_OBJECT_VERSION),
74
+ targetSchema: DXN.make(internal.ANY_OBJECT_TYPENAME, internal.ANY_OBJECT_VERSION),
75
+ },
74
76
  }),
75
- (schema) =>
76
- Object.assign(schema, {
77
- [internal.SchemaKindId]: (schema as any)[internal.SchemaKindId],
78
- }) as unknown as Type.Relation<Unknown, Obj.Any, Obj.Any>,
79
- );
77
+ ) as unknown as internal.UnknownTypeSchema<Unknown, typeof Entity.Kind.Relation>;
80
78
 
81
79
  /**
82
80
  * Relation type with specific source and target types.
@@ -89,7 +87,7 @@ export type OfShape<Source extends Obj.Unknown, Target extends Obj.Unknown, Prop
89
87
  */
90
88
  interface BaseRelationSnapshot<Source, Target> extends internal.AnyEntity, Endpoints<Source, Target> {
91
89
  readonly [Entity.SnapshotKindId]: internal.EntityKind.Relation;
92
- readonly id: ObjectId;
90
+ readonly id: EntityId;
93
91
  }
94
92
 
95
93
  /**
@@ -129,18 +127,20 @@ export type TargetOf<A> = A extends Endpoints<infer _S, infer T> ? T : never;
129
127
  /**
130
128
  * Internal props type for relation instance creation.
131
129
  */
132
- type MakePropsInternal<T extends Unknown> = {
133
- id?: ObjectId;
134
- [Meta]?: internal.ObjectMeta;
130
+ type MakePropsInternal<T extends Endpoints<any, any>> = {
131
+ id?: EntityId;
132
+ [Meta]?: Partial<internal.EntityMeta>;
135
133
  [Source]: T[Source];
136
134
  [Target]: T[Target];
137
135
  } & Entity.Properties<T>;
138
136
 
139
137
  /**
140
- * Props type for relation creation with a given schema.
141
- * Takes a schema type (created with Type.Relation) and extracts the props type.
138
+ * Props type for relation creation with a given schema. Accepts a `Type.AnyRelation`
139
+ * entity (created with `Type.makeRelation`) and derives the props shape via
140
+ * `Type.InstanceType`. Object-kind entities are rejected at the type level —
141
+ * use `Obj.MakeProps` for those.
142
142
  */
143
- export type MakeProps<S extends Type.AnyRelation> = MakePropsInternal<Schema.Schema.Type<S>>;
143
+ export type MakeProps<S extends Type.AnyRelation> = MakePropsInternal<Type.InstanceType<S>>;
144
144
 
145
145
  /**
146
146
  * Creates new relation.
@@ -151,10 +151,11 @@ export type MakeProps<S extends Type.AnyRelation> = MakePropsInternal<Schema.Sch
151
151
  */
152
152
  // NOTE: Writing the definition this way (with generic over schema) makes typescript perfer to infer the type from the first param (this schema) rather than the second param (the props).
153
153
  // TODO(dmaretskyi): Move meta into props.
154
- export const make = <S extends Type.AnyRelation>(
155
- schema: S,
156
- props: NoInfer<MakeProps<S>>,
157
- ): Schema.Schema.Type<S> & Entity.OfKind<typeof Entity.Kind.Relation> => {
154
+ export const make = <T extends Type.AnyRelation>(
155
+ type: T,
156
+ props: NoInfer<MakeProps<T>>,
157
+ ): Type.InstanceType<T> & Entity.OfKind<typeof Entity.Kind.Relation> => {
158
+ const schema = Type.getSchema(type);
158
159
  assertArgument(
159
160
  internal.getTypeAnnotation(schema)?.kind === internal.EntityKind.Relation,
160
161
  'schema',
@@ -162,22 +163,48 @@ export const make = <S extends Type.AnyRelation>(
162
163
  );
163
164
  assertArgument(props[internal.ParentId] === undefined, 'props', 'Parent is not allowed for relations');
164
165
 
165
- let meta: internal.ObjectMeta | undefined = undefined;
166
+ let meta: internal.EntityMeta | undefined = undefined;
166
167
 
167
168
  if (props[internal.MetaId] != null) {
168
169
  meta = props[internal.MetaId] as any;
169
170
  delete props[internal.MetaId];
170
171
  }
171
172
 
172
- const sourceDXN = internal.getObjectDXN(props[Source]) ?? raise(new Error('Unresolved relation source'));
173
- const targetDXN = internal.getObjectDXN(props[Target]) ?? raise(new Error('Unresolved relation target'));
173
+ const sourceDXN = internal.getObjectEchoUri(props[Source]) ?? raise(new Error('Unresolved relation source'));
174
+ const targetDXN = internal.getObjectEchoUri(props[Target]) ?? raise(new Error('Unresolved relation target'));
174
175
 
175
176
  (props as any)[internal.RelationSourceDXNId] = sourceDXN;
176
177
  (props as any)[internal.RelationTargetDXNId] = targetDXN;
177
178
 
178
- return internal.makeObject<Schema.Schema.Type<S>>(schema, props as any, meta);
179
+ // Pass the type entity through as `typeSource` so the resulting instance
180
+ // carries a back-reference resolvable via `Relation.getType` / `Entity.getType`.
181
+ return internal.makeObject(schema as any, props as any, meta, type as any) as any;
179
182
  };
180
183
 
184
+ /**
185
+ * Test if a value is an instance of a given relation type.
186
+ *
187
+ * Mirrors `Obj.instanceOf` but only accepts `Type.AnyRelation` — use
188
+ * `Obj.instanceOf` for objects and `Type.isType` for `Type.Type` entities.
189
+ *
190
+ * @example
191
+ * ```ts
192
+ * const isEmployedBy = Relation.instanceOf(EmployedBy);
193
+ * if (isEmployedBy(relation)) {
194
+ * // relation is EmployedBy
195
+ * }
196
+ * ```
197
+ */
198
+ export const instanceOf: {
199
+ <S extends Type.AnyRelation>(schema: S): (value: unknown) => value is Type.InstanceType<S>;
200
+ <S extends Type.AnyRelation>(schema: S, value: unknown): value is Type.InstanceType<S>;
201
+ } = ((...args: [schema: Type.AnyRelation, value?: unknown]) => {
202
+ if (args.length === 1) {
203
+ return (entity: unknown) => internal.isInstanceOf(args[0], entity);
204
+ }
205
+ return internal.isInstanceOf(args[0], args[1]);
206
+ }) as any;
207
+
181
208
  /**
182
209
  * Type guard for relations.
183
210
  * Returns true for both reactive relations and relation snapshots.
@@ -203,29 +230,29 @@ export const isSnapshot = (value: unknown): value is Snapshot => {
203
230
  };
204
231
 
205
232
  /**
206
- * @returns Relation source DXN.
233
+ * @returns Relation source URI.
207
234
  * Accepts both reactive relations and snapshots.
208
235
  * @throws If the object is not a relation.
209
236
  */
210
- export const getSourceDXN = (value: Unknown | Snapshot): DXN => {
237
+ export const getSourceURI = (value: Unknown | Snapshot): EID.EID => {
211
238
  assertArgument(isRelation(value), 'Expected a relation');
212
239
  assumeType<internal.InternalObjectProps>(value);
213
- const dxn = (value as internal.InternalObjectProps)[internal.RelationSourceDXNId];
214
- invariant(dxn instanceof DXN);
215
- return dxn;
240
+ const uri = (value as internal.InternalObjectProps)[internal.RelationSourceDXNId];
241
+ invariant(EID.isEID(uri));
242
+ return uri;
216
243
  };
217
244
 
218
245
  /**
219
- * @returns Relation target DXN.
246
+ * @returns Relation target URI.
220
247
  * Accepts both reactive relations and snapshots.
221
248
  * @throws If the object is not a relation.
222
249
  */
223
- export const getTargetDXN = (value: Unknown | Snapshot): DXN => {
250
+ export const getTargetURI = (value: Unknown | Snapshot): EID.EID => {
224
251
  assertArgument(isRelation(value), 'Expected a relation');
225
252
  assumeType<internal.InternalObjectProps>(value);
226
- const dxn = (value as internal.InternalObjectProps)[internal.RelationTargetDXNId];
227
- invariant(dxn instanceof DXN);
228
- return dxn;
253
+ const uri = (value as internal.InternalObjectProps)[internal.RelationTargetDXNId];
254
+ invariant(EID.isEID(uri));
255
+ return uri;
229
256
  };
230
257
 
231
258
  /**
@@ -350,21 +377,27 @@ export const setValue: (rel: Mutable<Unknown>, path: readonly (string | number)[
350
377
  //
351
378
 
352
379
  /**
353
- * Get the DXN of the relation.
354
- * Accepts both reactive relations and snapshots.
380
+ * Get the canonical URI of the relation. Returns `URI.URI` — today always an EID,
381
+ * but future entity kinds may surface other URI schemes; narrow with `EID.parse(uri)`
382
+ * or `DXN.tryMake(uri)` at the point of use. Accepts both reactive relations and snapshots.
355
383
  */
356
- export const getDXN = (entity: Unknown | Snapshot): DXN => internal.getDXN(entity);
384
+ export const getURI = (entity: Unknown | Snapshot): URI.URI => internal.getUri(entity);
357
385
 
358
386
  /**
359
387
  * @returns The DXN of the relation's type.
360
388
  */
361
- export const getTypeDXN = internal.getTypeDXN;
389
+ export const getTypeURI: (obj: internal.AnyProperties) => URI.URI | undefined = internal.getTypeURI;
362
390
 
363
391
  /**
364
- * Get the schema of the relation.
365
- * Returns the branded ECHO schema used to create the relation.
392
+ * Get the type entity (`Type.AnyRelation`) the relation was created from.
393
+ *
394
+ * Returns `undefined` when the relation's type isn't registered in this
395
+ * runtime (e.g. a freshly deserialized snapshot whose type entity hasn't been
396
+ * wired up yet, or a relation loaded from storage before its schema is known).
397
+ * To get the Effect Schema from the returned entity, use `Type.getSchema(...)`.
366
398
  */
367
- export const getSchema: (rel: unknown | undefined) => Type.AnyEntity | undefined = internal.getSchema as any;
399
+ export const getType = (relation: Unknown | Snapshot): Type.AnyRelation | undefined =>
400
+ internal.getType(relation) as Type.AnyRelation | undefined;
368
401
 
369
402
  /**
370
403
  * @returns The typename of the relation's type.
@@ -394,7 +427,7 @@ export const getDatabase = (entity: Unknown | Snapshot): Database.Database | und
394
427
  export const Meta = internal.MetaId;
395
428
 
396
429
  /**
397
- * Deeply read-only version of ObjectMeta.
430
+ * Deeply read-only version of EntityMeta.
398
431
  */
399
432
  export type ReadonlyMeta = internal.ReadonlyMeta;
400
433
 
@@ -437,7 +470,7 @@ export const deleteKeys = (entity: Mutable<Unknown>, source: string): void => in
437
470
  * NOTE: TypeScript's structural typing allows readonly objects to be passed to `Mutable<T>`
438
471
  * parameters, so there is no compile-time error. Enforcement is runtime-only.
439
472
  */
440
- export const addTag = (entity: Mutable<Unknown>, tag: string): void => internal.addTag(entity, tag);
473
+ export const addTag = (entity: Mutable<Unknown>, tag: Ref.Ref<Tag.Tag>): void => internal.addTag(entity, tag);
441
474
 
442
475
  /**
443
476
  * Remove a tag from the relation.
@@ -446,7 +479,7 @@ export const addTag = (entity: Mutable<Unknown>, tag: string): void => internal.
446
479
  * NOTE: TypeScript's structural typing allows readonly objects to be passed to `Mutable<T>`
447
480
  * parameters, so there is no compile-time error. Enforcement is runtime-only.
448
481
  */
449
- export const removeTag = (entity: Mutable<Unknown>, tag: string): void => internal.removeTag(entity, tag);
482
+ export const removeTag = (entity: Mutable<Unknown>, tag: Ref.Ref<Tag.Tag>): void => internal.removeTag(entity, tag);
450
483
 
451
484
  /**
452
485
  * Check if the relation is deleted.
@@ -461,8 +494,12 @@ export const isDeleted = (entity: Unknown | Snapshot): boolean => internal.isDel
461
494
  /**
462
495
  * Get the label of the relation.
463
496
  * Accepts both reactive relations and snapshots.
497
+ *
498
+ * @param options.fallback `'typename'` returns the relation's typename when no
499
+ * label is set (e.g. `org.dxos.type.table`).
464
500
  */
465
- export const getLabel = (entity: Unknown | Snapshot): string | undefined => internal.getLabel(entity);
501
+ export const getLabel = (entity: Unknown | Snapshot, options?: internal.GetLabelOptions): string | undefined =>
502
+ internal.getLabel(entity, options);
466
503
 
467
504
  /**
468
505
  * Set the label of the relation.
package/src/Scope.ts ADDED
@@ -0,0 +1,50 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ // @import-as-namespace
6
+
7
+ import { type QueryAST } from '@dxos/echo-protocol';
8
+
9
+ /**
10
+ * Scope targeting a space's automerge documents.
11
+ *
12
+ * With no `spaceId`, targets the owning space — i.e. the space of whichever database
13
+ * executes the query — so callers can reference "this space" without looking up its id.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * db.query(Filter.type(Person).from(Scope.space())); // owning space
18
+ * db.query(Filter.type(Person).from(Scope.space(otherSpaceId))); // a specific space
19
+ * ```
20
+ */
21
+ export const space = (spaceId?: string, options?: { includeAllFeeds?: boolean }): QueryAST.SpaceScope => ({
22
+ _tag: 'space',
23
+ ...(spaceId !== undefined ? { spaceId } : {}),
24
+ ...(options?.includeAllFeeds ? { includeAllFeeds: true } : {}),
25
+ });
26
+
27
+ /**
28
+ * Scope targeting a code-shipped object/type registry.
29
+ *
30
+ * - `'local'` — the in-process registry attached to the hypergraph (default).
31
+ * - `'remote'` — a remote registry service (not yet implemented).
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * // Discover all types — persisted in the space and code-shipped in the registry.
36
+ * db.query(Filter.type(Type.Type).from(Scope.space(), Scope.registry()));
37
+ * ```
38
+ */
39
+ export const registry = (location: 'local' | 'remote' = 'local'): QueryAST.RegistryScope => ({
40
+ _tag: 'registry',
41
+ location,
42
+ });
43
+
44
+ /**
45
+ * Scope targeting a specific feed (by its underlying queue DXN).
46
+ */
47
+ export const feed = (feedUri: string): QueryAST.FeedScope => ({
48
+ _tag: 'feed',
49
+ feedUri,
50
+ });
@@ -0,0 +1,56 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import * as Schema from 'effect/Schema';
6
+ import { describe, expect, test } from 'vitest';
7
+
8
+ import { DXN } from '@dxos/keys';
9
+
10
+ import * as Obj from './Obj';
11
+ import * as StateMap from './StateMap';
12
+ import * as Type from './Type';
13
+
14
+ /** Per-item state stored in the side-map (the non-tag metadata for a feed item). */
15
+ const PostState = Schema.Struct({
16
+ readAt: Schema.optional(Schema.String),
17
+ imageUrl: Schema.optional(Schema.String),
18
+ });
19
+
20
+ /** A host object carrying a per-object state side-map alongside (a notional feed of) items. */
21
+ const Host = Schema.Struct({
22
+ postState: StateMap.field(PostState),
23
+ }).pipe(Type.makeObject(DXN.make('org.dxos.test.StateMapHost', '0.1.0')));
24
+
25
+ describe('StateMap', () => {
26
+ test('patches, reads, and removes per-object state', () => {
27
+ const host = Obj.make(Host, {});
28
+ const state = StateMap.bind(host, 'postState');
29
+
30
+ const itemA = 'item-a';
31
+ const itemB = 'item-b';
32
+
33
+ // Absent → empty object (no ?-chains at call sites).
34
+ expect(state.get(itemA)).toEqual({});
35
+
36
+ // Patch creates, then shallow-merges.
37
+ state.patch(itemA, { readAt: 't1' });
38
+ expect(state.get(itemA)).toEqual({ readAt: 't1' });
39
+ state.patch(itemA, { imageUrl: 'u1' });
40
+ expect(state.get(itemA)).toEqual({ readAt: 't1', imageUrl: 'u1' });
41
+ // Overwrite a field.
42
+ state.patch(itemA, { readAt: 't2' });
43
+ expect(state.get(itemA)).toEqual({ readAt: 't2', imageUrl: 'u1' });
44
+
45
+ state.patch(itemB, { imageUrl: 'u2' });
46
+
47
+ // Predicate-filtered ids.
48
+ expect(state.ids().sort()).toEqual([itemA, itemB]);
49
+ expect(state.ids((value) => Boolean(value.readAt))).toEqual([itemA]);
50
+
51
+ // Remove drops the entry.
52
+ state.remove(itemA);
53
+ expect(state.get(itemA)).toEqual({});
54
+ expect(state.ids()).toEqual([itemB]);
55
+ });
56
+ });
@@ -0,0 +1,99 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ // @import-as-namespace
6
+
7
+ import * as Schema from 'effect/Schema';
8
+
9
+ import { FormInputAnnotation } from './internal';
10
+ import * as Obj from './Obj';
11
+
12
+ /**
13
+ * A per-object state side-map: an in-document `Record<objectId, S>` holding small mutable metadata
14
+ * for each object, keyed by object id.
15
+ *
16
+ * The companion to immutable feed/queue objects: the objects are immutable, so their mutable
17
+ * per-item state (read-marker, hero image, snippet, …) lives here on a host object. Use this for
18
+ * non-tag metadata; boolean labels (starred, archived) belong in the tag model ({@link TagIndex} /
19
+ * {@link Tagging}) so they share the space-wide `Tag` registry.
20
+ */
21
+
22
+ /** Read/write accessor over a host object's state side-map field, bound to a single field key. */
23
+ export interface Accessor<S extends object> {
24
+ /** All object ids with an entry, optionally filtered by a predicate over their state. */
25
+ ids(predicate?: (state: Partial<S>, id: string) => boolean): string[];
26
+ /** The entry for an object id; `{}` when absent (so call sites read fields without `?` chains). */
27
+ get(id: string): Partial<S>;
28
+ /** All `[id, state]` entries. */
29
+ entries(): Array<[string, Partial<S>]>;
30
+ /** Shallow-merges a patch into an object's entry, creating it when absent. */
31
+ patch(id: string, patch: Partial<S>): void;
32
+ /** Removes an object's entry. */
33
+ remove(id: string): void;
34
+ }
35
+
36
+ /**
37
+ * Schema fragment for a state side-map field. Splice into a host Struct. Optional record, hidden
38
+ * from forms. Keyed by object id; the value is the per-object state struct `S`.
39
+ */
40
+ export const field = <S, I>(value: Schema.Schema<S, I>) =>
41
+ Schema.Record({ key: Schema.String, value }).pipe(FormInputAnnotation.set(false), Schema.optional);
42
+
43
+ /** Binds an {@link Accessor} over `host[key]`; all mutations go through `Obj.update`. */
44
+ export const bind = <S extends object = Record<string, unknown>>(host: Obj.Any, key: string): Accessor<S> => {
45
+ type Entry = Partial<S>;
46
+
47
+ // The field is addressed by a runtime key, so the ECHO object is viewed as a keyed record — a
48
+ // deliberate type-system boundary (dynamic field access — the field name is a parameter).
49
+ const asView = (obj: Obj.Any) => obj as unknown as Record<string, Record<string, Entry> | undefined>;
50
+ const read = (): Record<string, Entry> => asView(host)[key] ?? {};
51
+
52
+ // Mutate the live typed record in place (do NOT reassign the whole tree); assigning a detached
53
+ // plain object tree bypasses ECHO's schema-aware conversion. Mutating the typed proxy preserves it.
54
+ const write = (mutate: (record: Record<string, Entry>) => void): void => {
55
+ Obj.update(host, (host) => {
56
+ const view = asView(host);
57
+ if (view[key] === undefined) {
58
+ view[key] = {};
59
+ }
60
+ // Re-read after assignment: the assignment expression yields the plain RHS, not the
61
+ // ECHO-backed reactive record, so mutating that would not persist.
62
+ mutate(view[key]!);
63
+ });
64
+ };
65
+
66
+ // Existence check via `Object.keys` — ECHO's reactive record proxy auto-vivifies missing keys on
67
+ // direct access, so `record[id]` is not reliably `undefined` for absent ids.
68
+ const has = (record: Record<string, Entry>, id: string): boolean => Object.keys(record).includes(id);
69
+
70
+ return {
71
+ ids: (predicate) => {
72
+ const record = read();
73
+ const keys = Object.keys(record);
74
+ return predicate ? keys.filter((id) => predicate(record[id], id)) : keys;
75
+ },
76
+ get: (id) => {
77
+ const record = read();
78
+ return has(record, id) ? record[id] : {};
79
+ },
80
+ entries: () => {
81
+ const record = read();
82
+ return Object.keys(record).map((id) => [id, record[id]]);
83
+ },
84
+ patch: (id, patch) =>
85
+ write((record) => {
86
+ if (!has(record, id)) {
87
+ record[id] = { ...patch };
88
+ } else {
89
+ Object.assign(record[id], patch);
90
+ }
91
+ }),
92
+ remove: (id) =>
93
+ write((record) => {
94
+ if (has(record, id)) {
95
+ delete record[id];
96
+ }
97
+ }),
98
+ };
99
+ };
package/src/Tag.md ADDED
@@ -0,0 +1,88 @@
1
+ # Tag
2
+
3
+ `Tag` is the ECHO type for labelling other objects. The schema is tiny — `{ label, hue? }` — but the _attachment pattern_ varies by use case. This doc covers what's available and which pattern to use.
4
+
5
+ ## What `Tag` is
6
+
7
+ Definition: [`Tag.ts`](./src/Tag.ts) — `Schema.Struct({ label: String, hue?: String })` with DXN `org.dxos.type.tag@0.1.0`. Carries the `SystemTypeAnnotation` so it's treated as a built-in type by the ECHO runtime.
8
+
9
+ Helpers on the same module:
10
+
11
+ - `Tag.make(props)` — constructor.
12
+ - `Tag.Map` — `Record<string, Tag>` alias used by call-site indexes.
13
+ - `Tag.createTagList(tags)` — `Map → Tag[]`, sorted by label.
14
+ - `Tag.findTagByLabel(tags, name)` — case-insensitive label lookup.
15
+ - `Tag.sortTags(a, b)` — `localeCompare` on label.
16
+
17
+ ### Colour (`hue`)
18
+
19
+ `hue` is a Tailwind colour name (`red`, `orange`, `amber`, …). The canonical list lives in [`IconAnnotation`'s schema](./src/internal/Annotation/annotations.ts) (search `IconAnnotationSchema`). Surfaces that render chips read `hue` to colour the chip; rendering hashes the tag id as a fallback when `hue` is absent.
20
+
21
+ ## Attachment patterns
22
+
23
+ Three ways an object can carry tags. Pick the one that matches your storage shape:
24
+
25
+ ### 1. `Obj.getMeta(obj).tags` — DXN refs on the object's metadata
26
+
27
+ The canonical user-tagging path. Each tag lives as a standalone `Tag` object in the space; the _target_ object holds an array of DXN refs to those tags inside its metadata bag. The form layer's [`ObjectProperties`](../../ui/react-ui-form/src/components/ObjectProperties) writes these via the `pinnedTags` control.
28
+
29
+ Use when: the target object is mutable. Tags can be added/removed cheaply by editing the metadata.
30
+
31
+ ```ts
32
+ const tag = Obj.make(Tag.Tag, { label: 'important', hue: 'amber' });
33
+ db.add(tag);
34
+ Obj.update(target, (t) => {
35
+ Obj.getMeta(t).tags = [tag.dxn];
36
+ });
37
+ ```
38
+
39
+ ### 2. Container-owned tag map — inverse index on the parent
40
+
41
+ For containers that hold immutable-in-feed objects (mailboxes hold messages, magazines hold posts), the children can't carry tag refs because they're immutable. Instead, the container holds an inverted `tagId → Ref<child>[]` map. Tag _definitions_ live in the same map alongside the assignments.
42
+
43
+ Use when: the children are append-only feed records, OR you need a fast "all objects tagged X" query without scanning each child.
44
+
45
+ Reference shape (from plugin-inbox's [`Mailbox.tags`](../../plugins/plugin-inbox/src/types/Mailbox.ts)):
46
+
47
+ ```ts
48
+ Mailbox.tags: Record<TagId, {
49
+ label: string,
50
+ hue?: string,
51
+ source?: 'provider' | 'user',
52
+ messages: Ref<Message>[],
53
+ }>
54
+ ```
55
+
56
+ Tag-id strategy depends on origin:
57
+
58
+ | source | tag id | rationale |
59
+ | ----------------------------------- | --------------------------------- | ---------------------------------------------------------------------------------------- |
60
+ | provider-synced (e.g. Gmail labels) | external id (Gmail's `Label_123`) | preserves re-sync idempotence — same external id → same map entry, no duplicates |
61
+ | user-applied | random `EntityId` | stable across renames; case-insensitive label dedupe via `findTagByLabel` at create time |
62
+
63
+ Trade-offs considered and rejected:
64
+
65
+ - Slug from label (`kebab-case(label)`) — natural dedupe but renaming a tag breaks references that point at the old key.
66
+ - EntityId for everything — uniform but Gmail re-syncs would duplicate provider tags every run unless you also dedupe by label, which loses the external id.
67
+
68
+ ### 3. `HasSubject` relation (deprecated)
69
+
70
+ The older pattern: a relation with `Source = Tag, Target = Object`. Lives at [`@dxos/types/HasSubject`](../../sdk/types/src/types/HasSubject.ts) and is marked `@deprecated`.
71
+
72
+ Use when: never, for new code. Existing data is being migrated to one of the two patterns above. The shape is functionally identical to (1), but doing it via a separate relation creates a query overhead (walk all relations for the object) that (1) avoids by inlining the refs.
73
+
74
+ ## Hue conventions
75
+
76
+ Always store the Tailwind colour name (e.g. `'amber'`), not a hex. Surfaces translate via `data-hue` on the chip, which the design system maps to the right shade. See [`getHashStyles`](../../ui/react-ui-components) for the fallback hash when `hue` is unset.
77
+
78
+ ## plugin-inbox: how the inbox uses Tag
79
+
80
+ Single source of truth: [`Mailbox.tags`](../../plugins/plugin-inbox/src/types/Mailbox.ts). Pattern 2 (container map). Replaces both the older `Mailbox.labels` (Gmail provider dictionary) and the `HasSubject`-relation user-tagging path with one record.
81
+
82
+ - Provider labels: `syncLabels` writes `mailbox.tags[gmailLabelId] = { label, source: 'provider', messages: [...] }`. Gmail's id is the map key so re-sync is idempotent.
83
+ - Per-message provider assignments: at sync time, after appending a message to the feed, the sync loop pushes `Ref.make(message)` onto each `mailbox.tags[labelId].messages` it was given.
84
+ - User tags / extractor tags: `Mailbox.applyTag(mailbox, { label, hue? }, message)` find-or-creates a `source: 'user'` entry and pushes a Ref. Idempotent.
85
+ - UI: `Mailbox.buildMessageTagsIndex(mailbox)` inverts the map to `messageId → Tag[]` for `MessageStack` tile chips; `Mailbox.getTagsForMessage(mailbox, message)` for the single-message case in `MessageHeader`.
86
+ - Extractor tagging: extractors that want to tag the source message return `{ tags: [{ label, hue? }] }` in their `ExtractResult`; the `ExtractMessage` dispatcher applies them after persistence. See the trip extractor for an example (`tags: [{ label: 'trip', hue: 'sky' }]`).
87
+
88
+ Adopting the same pattern in a new plugin: pick this shape when your plugin owns a mutable container with immutable feed-stored children, OR when you want a fast "tagged-with-X" inverted lookup. Copy `applyTag` / `removeTag` / `buildMessageTagsIndex` / `getTagsForMessage` (≈ 80 lines, no plugin-specific logic) and you have a working tag system on day one.
package/src/Tag.ts CHANGED
@@ -6,6 +6,10 @@
6
6
 
7
7
  import * as Schema from 'effect/Schema';
8
8
 
9
+ import type { ForeignKey } from '@dxos/echo-protocol';
10
+
11
+ import type * as Database from './Database';
12
+ import * as Filter from './Filter';
9
13
  import * as internal from './internal';
10
14
  import * as Obj from './Obj';
11
15
  import * as Type from './Type';
@@ -14,15 +18,13 @@ export const Tag = Schema.Struct({
14
18
  label: Schema.String,
15
19
  hue: Schema.optional(Schema.String), // TODO(burdon): Color name?
16
20
  }).pipe(
17
- Type.object({
18
- typename: 'org.dxos.type.tag',
19
- version: '0.1.0',
20
- }),
21
21
  internal.LabelAnnotation.set(['label']),
22
- internal.SystemTypeAnnotation.set(true),
22
+ internal.HiddenAnnotation.set(true),
23
+ // Shared DXN so `meta.tags` (the `Ref<Tag>` schema) and this type stay in sync.
24
+ Type.makeObject(internal.TagTypeDXN),
23
25
  );
24
26
 
25
- export type Tag = Schema.Schema.Type<typeof Tag>;
27
+ export type Tag = Type.InstanceType<typeof Tag>;
26
28
 
27
29
  export const make = (props: Obj.MakeProps<typeof Tag>) => Obj.make(Tag, props);
28
30
 
@@ -39,3 +41,44 @@ export const findTagByLabel = (tags: Map | undefined, name: string): Tag | undef
39
41
  const entry = Object.entries(tags ?? {}).find(([_, tag]) => tag.label.toLowerCase() === name.toLowerCase());
40
42
  return entry ? { ...entry[1], id: entry[0] } : undefined;
41
43
  };
44
+
45
+ /**
46
+ * Finds or creates a {@link Tag} object in the database.
47
+ *
48
+ * - With a foreign `key` (system/provider tags): matched by that key; the label is kept current on
49
+ * re-sync. Use a stable key (e.g. `{ source: 'google.com/gmail/label', id }`) for tags whose
50
+ * identity is external or well-known.
51
+ * - Without a key (user tags): matched by case-insensitive label among tags that carry **no**
52
+ * foreign key, so it never collides with a keyed system/provider tag of the same label.
53
+ */
54
+ export const findOrCreate = async (
55
+ db: Pick<Database.Database, 'query' | 'add'>,
56
+ options: { label: string; hue?: string; key?: ForeignKey },
57
+ ): Promise<Tag> => {
58
+ const { label, hue, key } = options;
59
+ const withHue = hue ? { hue } : {};
60
+ if (key) {
61
+ const [existing] = await db.query(Filter.foreignKeys(Tag, [key])).run();
62
+ if (existing) {
63
+ // Keep label (and hue, when provided) current on re-sync. `hue` is only touched when supplied,
64
+ // so a label-only re-sync (e.g. Gmail) doesn't wipe a user-set colour.
65
+ if (existing.label !== label || (hue !== undefined && existing.hue !== hue)) {
66
+ Obj.update(existing, (existing) => {
67
+ existing.label = label;
68
+ if (hue !== undefined) {
69
+ existing.hue = hue;
70
+ }
71
+ });
72
+ }
73
+ return existing;
74
+ }
75
+ return db.add(Obj.make(Tag, { [Obj.Meta]: { keys: [key] }, label, ...withHue }));
76
+ }
77
+
78
+ const lowered = label.toLowerCase();
79
+ const candidates = await db.query(Filter.type(Tag)).run();
80
+ const existing = candidates.find(
81
+ (tag) => tag.label.toLowerCase() === lowered && (Obj.getMeta(tag).keys ?? []).length === 0,
82
+ );
83
+ return existing ?? db.add(make({ label, ...withHue }));
84
+ };