@dxos/echo 0.8.4-main.8360d9e660 → 0.8.4-main.8baae0fced

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 (292) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +3 -3
  3. package/dist/lib/neutral/Annotation.mjs +5 -3
  4. package/dist/lib/neutral/Database.mjs +8 -4
  5. package/dist/lib/neutral/Entity.mjs +16 -14
  6. package/dist/lib/neutral/Err.mjs +1 -1
  7. package/dist/lib/neutral/Extension.mjs +18 -0
  8. package/dist/lib/neutral/Extension.mjs.map +7 -0
  9. package/dist/lib/neutral/Feed.mjs +23 -18
  10. package/dist/lib/neutral/Filter.mjs +23 -13
  11. package/dist/lib/neutral/Format.mjs +3 -3
  12. package/dist/lib/neutral/JsonSchema.mjs +8 -8
  13. package/dist/lib/neutral/Key.mjs +1 -1
  14. package/dist/lib/neutral/Migration.mjs +17 -0
  15. package/dist/lib/neutral/Migration.mjs.map +7 -0
  16. package/dist/lib/neutral/Obj.mjs +16 -13
  17. package/dist/lib/neutral/Order.mjs +1 -1
  18. package/dist/lib/neutral/Query.mjs +19 -17
  19. package/dist/lib/neutral/QueryResult.mjs +1 -1
  20. package/dist/lib/neutral/Ref.mjs +9 -7
  21. package/dist/lib/neutral/Relation.mjs +15 -14
  22. package/dist/lib/neutral/SchemaRegistry.mjs +1 -1
  23. package/dist/lib/neutral/Tag.mjs +14 -13
  24. package/dist/lib/neutral/Type.mjs +10 -10
  25. package/dist/lib/neutral/{chunk-FZO7LQO7.mjs → chunk-44HT3MEC.mjs} +2 -2
  26. package/dist/lib/neutral/{chunk-ROAGDPV7.mjs → chunk-4A2GS5LQ.mjs} +12 -8
  27. package/dist/lib/neutral/chunk-4A2GS5LQ.mjs.map +7 -0
  28. package/dist/lib/neutral/{chunk-OENWMTE6.mjs → chunk-5SL5LDLD.mjs} +4 -2
  29. package/dist/lib/neutral/chunk-5SL5LDLD.mjs.map +7 -0
  30. package/dist/lib/neutral/{chunk-ANHVGJI4.mjs → chunk-7RVZT53K.mjs} +1 -1
  31. package/dist/lib/neutral/{chunk-YWXWXIE5.mjs → chunk-APHSOTIX.mjs} +2 -2
  32. package/dist/lib/neutral/{chunk-YWXWXIE5.mjs.map → chunk-APHSOTIX.mjs.map} +2 -2
  33. package/dist/lib/neutral/{chunk-6DNYDXCV.mjs → chunk-B2P7IVG3.mjs} +61 -14
  34. package/dist/lib/neutral/chunk-B2P7IVG3.mjs.map +7 -0
  35. package/dist/lib/neutral/{chunk-BNCCGLJN.mjs → chunk-BICZKPQG.mjs} +1 -1
  36. package/dist/lib/neutral/{chunk-43Y5DOS6.mjs → chunk-BMB7IHGB.mjs} +16 -66
  37. package/dist/lib/neutral/chunk-BMB7IHGB.mjs.map +7 -0
  38. package/dist/lib/neutral/{chunk-6K2MVI2O.mjs → chunk-BUBEC474.mjs} +4 -4
  39. package/dist/lib/neutral/{chunk-YMNSMKKW.mjs → chunk-C4PSESGN.mjs} +6 -22
  40. package/dist/lib/neutral/{chunk-YMNSMKKW.mjs.map → chunk-C4PSESGN.mjs.map} +3 -3
  41. package/dist/lib/neutral/chunk-FIWO2FZK.mjs +36 -0
  42. package/dist/lib/neutral/chunk-FIWO2FZK.mjs.map +7 -0
  43. package/dist/lib/neutral/chunk-GWFFC34K.mjs +50 -0
  44. package/dist/lib/neutral/chunk-GWFFC34K.mjs.map +7 -0
  45. package/dist/lib/neutral/{chunk-NXMFBIT5.mjs → chunk-HKETO4L4.mjs} +72 -6
  46. package/dist/lib/neutral/chunk-HKETO4L4.mjs.map +7 -0
  47. package/dist/lib/neutral/{chunk-NEGC54NE.mjs → chunk-I2DARWPX.mjs} +17 -19
  48. package/dist/lib/neutral/chunk-I2DARWPX.mjs.map +7 -0
  49. package/dist/lib/neutral/{chunk-7VNVH63N.mjs → chunk-IVSI7QO6.mjs} +50 -20
  50. package/dist/lib/neutral/chunk-IVSI7QO6.mjs.map +7 -0
  51. package/dist/lib/neutral/{chunk-BOZZPUXE.mjs → chunk-MLS7U7AT.mjs} +12 -65
  52. package/dist/lib/neutral/chunk-MLS7U7AT.mjs.map +7 -0
  53. package/dist/lib/neutral/{chunk-WYOKA6AE.mjs → chunk-N4B7FHQT.mjs} +2 -2
  54. package/dist/lib/neutral/{chunk-WYOKA6AE.mjs.map → chunk-N4B7FHQT.mjs.map} +1 -1
  55. package/dist/lib/neutral/{chunk-FXEG7EOK.mjs → chunk-N7VOEPSV.mjs} +6 -3
  56. package/dist/lib/neutral/{chunk-FXEG7EOK.mjs.map → chunk-N7VOEPSV.mjs.map} +3 -3
  57. package/dist/lib/neutral/{chunk-UTBRYVQC.mjs → chunk-QRZ2I3ZM.mjs} +2 -2
  58. package/dist/lib/neutral/{chunk-SEMVAGBM.mjs → chunk-TNBK56IN.mjs} +19 -24
  59. package/dist/lib/neutral/chunk-TNBK56IN.mjs.map +7 -0
  60. package/dist/lib/neutral/{chunk-B5OXLWZL.mjs → chunk-TRPZU2HV.mjs} +2 -2
  61. package/dist/lib/neutral/{chunk-UI6MWK5W.mjs → chunk-TTCSATUD.mjs} +1 -1
  62. package/dist/lib/neutral/{chunk-OMUPQMLR.mjs → chunk-V72DY6LU.mjs} +1 -1
  63. package/dist/lib/neutral/{chunk-6GPU7XC3.mjs → chunk-VW42HESL.mjs} +54 -17
  64. package/dist/lib/neutral/chunk-VW42HESL.mjs.map +7 -0
  65. package/dist/lib/neutral/{chunk-4JRI2ZJI.mjs → chunk-X3356HPV.mjs} +120 -12
  66. package/dist/lib/neutral/chunk-X3356HPV.mjs.map +7 -0
  67. package/dist/lib/neutral/{chunk-W47JKR3X.mjs → chunk-XEXM5HWQ.mjs} +18 -46
  68. package/dist/lib/neutral/chunk-XEXM5HWQ.mjs.map +7 -0
  69. package/dist/lib/neutral/{chunk-C4JZK4J7.mjs → chunk-Z5GKP74O.mjs} +231 -479
  70. package/dist/lib/neutral/chunk-Z5GKP74O.mjs.map +7 -0
  71. package/dist/lib/neutral/{chunk-UBEZSGXY.mjs → chunk-ZISMEVKD.mjs} +1 -1
  72. package/dist/lib/neutral/{chunk-UBEZSGXY.mjs.map → chunk-ZISMEVKD.mjs.map} +2 -2
  73. package/dist/lib/neutral/index.mjs +38 -28
  74. package/dist/lib/neutral/internal/index.mjs +15 -9
  75. package/dist/lib/neutral/meta.json +1 -1
  76. package/dist/lib/neutral/testing/index.mjs +185 -129
  77. package/dist/lib/neutral/testing/index.mjs.map +3 -3
  78. package/dist/types/src/Annotation.d.ts +1 -1
  79. package/dist/types/src/Annotation.d.ts.map +1 -1
  80. package/dist/types/src/Collection.d.ts.map +1 -1
  81. package/dist/types/src/Database.d.ts +14 -2
  82. package/dist/types/src/Database.d.ts.map +1 -1
  83. package/dist/types/src/Dataset.d.ts +2 -1
  84. package/dist/types/src/Dataset.d.ts.map +1 -1
  85. package/dist/types/src/Entity.d.ts +17 -11
  86. package/dist/types/src/Entity.d.ts.map +1 -1
  87. package/dist/types/src/Err.d.ts +18 -18
  88. package/dist/types/src/Err.d.ts.map +1 -1
  89. package/dist/types/src/Extension.d.ts +80 -0
  90. package/dist/types/src/Extension.d.ts.map +1 -0
  91. package/dist/types/src/Extension.test.d.ts +2 -0
  92. package/dist/types/src/Extension.test.d.ts.map +1 -0
  93. package/dist/types/src/Feed.d.ts +62 -21
  94. package/dist/types/src/Feed.d.ts.map +1 -1
  95. package/dist/types/src/Filter.d.ts +54 -4
  96. package/dist/types/src/Filter.d.ts.map +1 -1
  97. package/dist/types/src/Filter.test.d.ts +2 -0
  98. package/dist/types/src/Filter.test.d.ts.map +1 -0
  99. package/dist/types/src/Hypergraph.d.ts +3 -3
  100. package/dist/types/src/Hypergraph.d.ts.map +1 -1
  101. package/dist/types/src/Json.d.ts +33 -0
  102. package/dist/types/src/Json.d.ts.map +1 -0
  103. package/dist/types/src/Json.test.d.ts +2 -0
  104. package/dist/types/src/Json.test.d.ts.map +1 -0
  105. package/dist/types/src/JsonSchema.d.ts +1 -1
  106. package/dist/types/src/Migration.d.ts +69 -0
  107. package/dist/types/src/Migration.d.ts.map +1 -0
  108. package/dist/types/src/Obj.d.ts +42 -28
  109. package/dist/types/src/Obj.d.ts.map +1 -1
  110. package/dist/types/src/Order.d.ts.map +1 -1
  111. package/dist/types/src/Query.d.ts +13 -2
  112. package/dist/types/src/Query.d.ts.map +1 -1
  113. package/dist/types/src/Ref.d.ts +1 -0
  114. package/dist/types/src/Ref.d.ts.map +1 -1
  115. package/dist/types/src/Relation.d.ts +17 -18
  116. package/dist/types/src/Relation.d.ts.map +1 -1
  117. package/dist/types/src/Tag.d.ts +2 -2
  118. package/dist/types/src/Tag.d.ts.map +1 -1
  119. package/dist/types/src/Type.d.ts +3 -3
  120. package/dist/types/src/Type.d.ts.map +1 -1
  121. package/dist/types/src/View.d.ts +1 -1
  122. package/dist/types/src/View.d.ts.map +1 -1
  123. package/dist/types/src/exemplars.test.d.ts +2 -0
  124. package/dist/types/src/exemplars.test.d.ts.map +1 -0
  125. package/dist/types/src/index.d.ts +3 -0
  126. package/dist/types/src/index.d.ts.map +1 -1
  127. package/dist/types/src/internal/Annotation/annotations.d.ts +12 -2
  128. package/dist/types/src/internal/Annotation/annotations.d.ts.map +1 -1
  129. package/dist/types/src/internal/Annotation/sorting.d.ts.map +1 -1
  130. package/dist/types/src/internal/Annotation/util.d.ts +1 -1
  131. package/dist/types/src/internal/Annotation/util.d.ts.map +1 -1
  132. package/dist/types/src/internal/Entity/api.d.ts.map +1 -1
  133. package/dist/types/src/internal/Entity/model.d.ts +2 -0
  134. package/dist/types/src/internal/Entity/model.d.ts.map +1 -1
  135. package/dist/types/src/internal/Entity/object.d.ts.map +1 -1
  136. package/dist/types/src/internal/Entity/relation.d.ts.map +1 -1
  137. package/dist/types/src/internal/Entity/version.d.ts.map +1 -1
  138. package/dist/types/src/internal/Format/date.d.ts.map +1 -1
  139. package/dist/types/src/internal/Format/format.d.ts.map +1 -1
  140. package/dist/types/src/internal/Format/number.d.ts.map +1 -1
  141. package/dist/types/src/internal/Format/object.d.ts.map +1 -1
  142. package/dist/types/src/internal/Format/types.d.ts.map +1 -1
  143. package/dist/types/src/internal/JsonSchema/json-schema-normalize.d.ts.map +1 -1
  144. package/dist/types/src/internal/JsonSchema/json-schema-type.d.ts +28 -28
  145. package/dist/types/src/internal/JsonSchema/json-schema-type.d.ts.map +1 -1
  146. package/dist/types/src/internal/JsonSchema/json-schema.d.ts +1 -1
  147. package/dist/types/src/internal/JsonSchema/json-schema.d.ts.map +1 -1
  148. package/dist/types/src/internal/Obj/clone.d.ts.map +1 -1
  149. package/dist/types/src/internal/Obj/common.d.ts.map +1 -1
  150. package/dist/types/src/internal/Obj/create-object.d.ts.map +1 -1
  151. package/dist/types/src/internal/Obj/deleted.d.ts.map +1 -1
  152. package/dist/types/src/internal/Obj/ids.d.ts +1 -1
  153. package/dist/types/src/internal/Obj/ids.d.ts.map +1 -1
  154. package/dist/types/src/internal/Obj/json-serializer.d.ts +4 -3
  155. package/dist/types/src/internal/Obj/json-serializer.d.ts.map +1 -1
  156. package/dist/types/src/internal/Obj/set-value.d.ts +1 -1
  157. package/dist/types/src/internal/Obj/set-value.d.ts.map +1 -1
  158. package/dist/types/src/internal/Obj/snapshot.d.ts.map +1 -1
  159. package/dist/types/src/internal/Query.d.ts +10 -0
  160. package/dist/types/src/internal/Query.d.ts.map +1 -0
  161. package/dist/types/src/internal/Ref/ref-array.d.ts.map +1 -1
  162. package/dist/types/src/internal/Ref/ref.d.ts +14 -1
  163. package/dist/types/src/internal/Ref/ref.d.ts.map +1 -1
  164. package/dist/types/src/internal/Type/compose.d.ts.map +1 -1
  165. package/dist/types/src/internal/Type/echo-schema.d.ts +2 -2
  166. package/dist/types/src/internal/Type/echo-schema.d.ts.map +1 -1
  167. package/dist/types/src/internal/Type/manipulation.d.ts.map +1 -1
  168. package/dist/types/src/internal/common/api/meta.d.ts +3 -3
  169. package/dist/types/src/internal/common/api/meta.d.ts.map +1 -1
  170. package/dist/types/src/internal/common/proxy/change-context.d.ts +1 -1
  171. package/dist/types/src/internal/common/proxy/change-context.d.ts.map +1 -1
  172. package/dist/types/src/internal/common/proxy/define-hidden-property.d.ts.map +1 -1
  173. package/dist/types/src/internal/common/proxy/errors.d.ts +1 -1
  174. package/dist/types/src/internal/common/proxy/errors.d.ts.map +1 -1
  175. package/dist/types/src/internal/common/proxy/event-batch.d.ts.map +1 -1
  176. package/dist/types/src/internal/common/proxy/json-serializer.d.ts.map +1 -1
  177. package/dist/types/src/internal/common/proxy/make-object.d.ts.map +1 -1
  178. package/dist/types/src/internal/common/proxy/ownership.d.ts.map +1 -1
  179. package/dist/types/src/internal/common/proxy/proxy-utils.d.ts.map +1 -1
  180. package/dist/types/src/internal/common/proxy/reactive-array.d.ts +1 -1
  181. package/dist/types/src/internal/common/proxy/reactive.d.ts +1 -1
  182. package/dist/types/src/internal/common/proxy/reactive.d.ts.map +1 -1
  183. package/dist/types/src/internal/common/proxy/reactive.test.d.ts +2 -0
  184. package/dist/types/src/internal/common/proxy/reactive.test.d.ts.map +1 -0
  185. package/dist/types/src/internal/common/proxy/schema-validator.d.ts.map +1 -1
  186. package/dist/types/src/internal/common/proxy/typed-handler.d.ts.map +1 -1
  187. package/dist/types/src/internal/common/types/base.d.ts.map +1 -1
  188. package/dist/types/src/internal/common/types/entity.d.ts +4 -4
  189. package/dist/types/src/internal/common/types/entity.d.ts.map +1 -1
  190. package/dist/types/src/internal/common/types/meta.d.ts +10 -0
  191. package/dist/types/src/internal/common/types/meta.d.ts.map +1 -1
  192. package/dist/types/src/internal/common/types/version.d.ts +1 -1
  193. package/dist/types/src/internal/index.d.ts +1 -0
  194. package/dist/types/src/internal/index.d.ts.map +1 -1
  195. package/dist/types/src/testing/test-data.d.ts +8 -8
  196. package/dist/types/src/testing/test-data.d.ts.map +1 -1
  197. package/dist/types/src/testing/test-schema.d.ts +53 -53
  198. package/dist/types/src/testing/test-schema.d.ts.map +1 -1
  199. package/dist/types/src/testing/util.d.ts.map +1 -1
  200. package/dist/types/tsconfig.tsbuildinfo +1 -1
  201. package/package.json +25 -15
  202. package/src/Annotation.ts +1 -0
  203. package/src/Collection.ts +2 -2
  204. package/src/Database.ts +50 -15
  205. package/src/Entity.ts +18 -12
  206. package/src/Extension.test.ts +235 -0
  207. package/src/Extension.ts +122 -0
  208. package/src/Feed.ts +107 -34
  209. package/src/Filter.test.ts +90 -0
  210. package/src/Filter.ts +97 -3
  211. package/src/Hypergraph.ts +3 -3
  212. package/src/Json.test.ts +175 -0
  213. package/src/Json.ts +102 -0
  214. package/src/Migration.ts +106 -0
  215. package/src/Obj.test.ts +105 -13
  216. package/src/Obj.ts +154 -33
  217. package/src/Query.test.ts +199 -9
  218. package/src/Query.ts +58 -8
  219. package/src/Ref.ts +2 -0
  220. package/src/Relation.ts +24 -20
  221. package/src/Type.ts +1 -1
  222. package/src/View.ts +1 -1
  223. package/src/exemplars.test.ts +21 -0
  224. package/src/index.ts +4 -0
  225. package/src/internal/Annotation/annotations.test.ts +51 -2
  226. package/src/internal/Annotation/annotations.ts +33 -14
  227. package/src/internal/Annotation/sorting.ts +0 -1
  228. package/src/internal/Entity/api.ts +0 -1
  229. package/src/internal/Entity/model.ts +2 -0
  230. package/src/internal/Entity/object.ts +0 -1
  231. package/src/internal/Entity/version.ts +0 -1
  232. package/src/internal/Format/date.test.ts +0 -1
  233. package/src/internal/Format/format.test.ts +0 -1
  234. package/src/internal/JsonSchema/json-schema-type.ts +1 -1
  235. package/src/internal/JsonSchema/json-schema.test.ts +1 -2
  236. package/src/internal/JsonSchema/json-schema.ts +1 -2
  237. package/src/internal/Obj/clone.ts +1 -1
  238. package/src/internal/Obj/create-object.test.ts +2 -4
  239. package/src/internal/Obj/create-object.ts +2 -3
  240. package/src/internal/Obj/deleted.ts +1 -1
  241. package/src/internal/Obj/ids.ts +1 -1
  242. package/src/internal/Obj/json-serializer.test.ts +49 -5
  243. package/src/internal/Obj/json-serializer.ts +47 -25
  244. package/src/internal/Obj/set-value.test.ts +24 -24
  245. package/src/internal/Obj/set-value.ts +1 -1
  246. package/src/internal/Query.ts +156 -0
  247. package/src/internal/Ref/ref-array.ts +0 -1
  248. package/src/internal/Ref/ref.test.ts +0 -1
  249. package/src/internal/Ref/ref.ts +18 -1
  250. package/src/internal/Type/compose.test.ts +0 -1
  251. package/src/internal/Type/echo-schema.ts +3 -4
  252. package/src/internal/common/README.md +1 -1
  253. package/src/internal/common/api/meta.ts +3 -3
  254. package/src/internal/common/proxy/change-context.ts +1 -1
  255. package/src/internal/common/proxy/change.test.ts +94 -94
  256. package/src/internal/common/proxy/errors.ts +2 -2
  257. package/src/internal/common/proxy/handler.test.ts +0 -2
  258. package/src/internal/common/proxy/json-serializer.ts +4 -1
  259. package/src/internal/common/proxy/make-object.ts +0 -1
  260. package/src/internal/common/proxy/ownership.ts +0 -1
  261. package/src/internal/common/proxy/reactive-array.ts +1 -1
  262. package/src/internal/common/proxy/reactive.test.ts +54 -0
  263. package/src/internal/common/proxy/reactive.ts +11 -3
  264. package/src/internal/common/proxy/typed-handler.test.ts +0 -1
  265. package/src/internal/common/proxy/typed-handler.ts +8 -10
  266. package/src/internal/common/proxy/typed-object.test.ts +2 -3
  267. package/src/internal/common/types/entity.ts +1 -1
  268. package/src/internal/common/types/meta.ts +12 -1
  269. package/src/internal/index.ts +1 -0
  270. package/src/testing/api.test.ts +0 -1
  271. package/src/testing/test-data.ts +157 -98
  272. package/dist/lib/neutral/chunk-43Y5DOS6.mjs.map +0 -7
  273. package/dist/lib/neutral/chunk-4JRI2ZJI.mjs.map +0 -7
  274. package/dist/lib/neutral/chunk-6DNYDXCV.mjs.map +0 -7
  275. package/dist/lib/neutral/chunk-6GPU7XC3.mjs.map +0 -7
  276. package/dist/lib/neutral/chunk-7VNVH63N.mjs.map +0 -7
  277. package/dist/lib/neutral/chunk-BOZZPUXE.mjs.map +0 -7
  278. package/dist/lib/neutral/chunk-C4JZK4J7.mjs.map +0 -7
  279. package/dist/lib/neutral/chunk-NEGC54NE.mjs.map +0 -7
  280. package/dist/lib/neutral/chunk-NXMFBIT5.mjs.map +0 -7
  281. package/dist/lib/neutral/chunk-OENWMTE6.mjs.map +0 -7
  282. package/dist/lib/neutral/chunk-ROAGDPV7.mjs.map +0 -7
  283. package/dist/lib/neutral/chunk-SEMVAGBM.mjs.map +0 -7
  284. package/dist/lib/neutral/chunk-W47JKR3X.mjs.map +0 -7
  285. /package/dist/lib/neutral/{chunk-FZO7LQO7.mjs.map → chunk-44HT3MEC.mjs.map} +0 -0
  286. /package/dist/lib/neutral/{chunk-ANHVGJI4.mjs.map → chunk-7RVZT53K.mjs.map} +0 -0
  287. /package/dist/lib/neutral/{chunk-BNCCGLJN.mjs.map → chunk-BICZKPQG.mjs.map} +0 -0
  288. /package/dist/lib/neutral/{chunk-6K2MVI2O.mjs.map → chunk-BUBEC474.mjs.map} +0 -0
  289. /package/dist/lib/neutral/{chunk-UTBRYVQC.mjs.map → chunk-QRZ2I3ZM.mjs.map} +0 -0
  290. /package/dist/lib/neutral/{chunk-B5OXLWZL.mjs.map → chunk-TRPZU2HV.mjs.map} +0 -0
  291. /package/dist/lib/neutral/{chunk-UI6MWK5W.mjs.map → chunk-TTCSATUD.mjs.map} +0 -0
  292. /package/dist/lib/neutral/{chunk-OMUPQMLR.mjs.map → chunk-V72DY6LU.mjs.map} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/echo",
3
- "version": "0.8.4-main.8360d9e660",
3
+ "version": "0.8.4-main.8baae0fced",
4
4
  "description": "ECHO API",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -8,7 +8,7 @@
8
8
  "type": "git",
9
9
  "url": "https://github.com/dxos/dxos"
10
10
  },
11
- "license": "MIT",
11
+ "license": "FSL-1.1-Apache-2.0",
12
12
  "author": "info@dxos.org",
13
13
  "sideEffects": false,
14
14
  "type": "module",
@@ -73,6 +73,11 @@
73
73
  "types": "./dist/types/src/Key.d.ts",
74
74
  "default": "./dist/lib/neutral/Key.mjs"
75
75
  },
76
+ "./Migration": {
77
+ "source": "./src/Migration.ts",
78
+ "types": "./dist/types/src/Migration.d.ts",
79
+ "default": "./dist/lib/neutral/Migration.mjs"
80
+ },
76
81
  "./Obj": {
77
82
  "source": "./src/Obj.ts",
78
83
  "types": "./dist/types/src/Obj.d.ts",
@@ -117,6 +122,11 @@
117
122
  "source": "./src/Type.ts",
118
123
  "types": "./dist/types/src/Type.d.ts",
119
124
  "default": "./dist/lib/neutral/Type.mjs"
125
+ },
126
+ "./Extension": {
127
+ "source": "./src/Extension.ts",
128
+ "types": "./dist/types/src/Extension.d.ts",
129
+ "default": "./dist/lib/neutral/Extension.mjs"
120
130
  }
121
131
  },
122
132
  "files": [
@@ -124,19 +134,19 @@
124
134
  "src"
125
135
  ],
126
136
  "dependencies": {
127
- "effect": "3.19.16",
128
- "@dxos/context": "0.8.4-main.8360d9e660",
129
- "@dxos/debug": "0.8.4-main.8360d9e660",
130
- "@dxos/echo-protocol": "0.8.4-main.8360d9e660",
131
- "@dxos/invariant": "0.8.4-main.8360d9e660",
132
- "@dxos/async": "0.8.4-main.8360d9e660",
133
- "@dxos/effect": "0.8.4-main.8360d9e660",
134
- "@dxos/errors": "0.8.4-main.8360d9e660",
135
- "@dxos/node-std": "0.8.4-main.8360d9e660",
136
- "@dxos/keys": "0.8.4-main.8360d9e660",
137
- "@dxos/protocols": "0.8.4-main.8360d9e660",
138
- "@dxos/log": "0.8.4-main.8360d9e660",
139
- "@dxos/util": "0.8.4-main.8360d9e660"
137
+ "effect": "3.20.0",
138
+ "@dxos/async": "0.8.4-main.8baae0fced",
139
+ "@dxos/context": "0.8.4-main.8baae0fced",
140
+ "@dxos/debug": "0.8.4-main.8baae0fced",
141
+ "@dxos/echo-protocol": "0.8.4-main.8baae0fced",
142
+ "@dxos/effect": "0.8.4-main.8baae0fced",
143
+ "@dxos/errors": "0.8.4-main.8baae0fced",
144
+ "@dxos/invariant": "0.8.4-main.8baae0fced",
145
+ "@dxos/keys": "0.8.4-main.8baae0fced",
146
+ "@dxos/log": "0.8.4-main.8baae0fced",
147
+ "@dxos/node-std": "0.8.4-main.8baae0fced",
148
+ "@dxos/util": "0.8.4-main.8baae0fced",
149
+ "@dxos/protocols": "0.8.4-main.8baae0fced"
140
150
  },
141
151
  "publishConfig": {
142
152
  "access": "public"
package/src/Annotation.ts CHANGED
@@ -18,6 +18,7 @@ export {
18
18
  setDescriptionWithSchema,
19
19
  setLabelWithSchema,
20
20
  IconAnnotation,
21
+ IconFromRefAnnotation,
21
22
  } from './internal/Annotation';
22
23
 
23
24
  import type * as Schema from 'effect/Schema';
package/src/Collection.ts CHANGED
@@ -6,8 +6,8 @@
6
6
 
7
7
  import * as Schema from 'effect/Schema';
8
8
 
9
- import * as internal from './internal';
10
9
  import * as Annotation from './Annotation';
10
+ import * as internal from './internal';
11
11
  import * as Obj from './Obj';
12
12
  import * as Ref from './Ref';
13
13
  import * as Type from './Type';
@@ -25,7 +25,7 @@ export const Collection = Schema.Struct({
25
25
  }),
26
26
  Annotation.IconAnnotation.set({
27
27
  icon: 'ph--folder--regular',
28
- hue: 'neutral',
28
+ hue: 'amber',
29
29
  }),
30
30
  );
31
31
 
package/src/Database.ts CHANGED
@@ -20,8 +20,8 @@ import * as Err from './Err';
20
20
  import type * as Filter from './Filter';
21
21
  import type * as Hypergraph from './Hypergraph';
22
22
  import { isInstanceOf } from './internal/Annotation';
23
- import type { Ref } from './internal/Ref/ref';
24
23
  import { type AnyProperties } from './internal/common/types';
24
+ import type { Ref } from './internal/Ref/ref';
25
25
  import type * as Obj from './Obj';
26
26
  import type * as Query from './Query';
27
27
  import type * as QueryResult from './QueryResult';
@@ -38,7 +38,7 @@ export interface QueryFn {
38
38
  }
39
39
 
40
40
  /**
41
- * Common interface for Database and Queue.
41
+ * Common interface for Database, Feed, and Hypergraph.
42
42
  */
43
43
  export interface Queryable {
44
44
  query: QueryFn;
@@ -229,24 +229,28 @@ export const resolve: {
229
229
  }
230
230
  invariant(!schema || isInstanceOf(schema, object), 'Object type mismatch.');
231
231
  return object as any;
232
- })) as any;
232
+ }).pipe(Effect.withSpan('Database.resolve'))) as any;
233
233
 
234
234
  /**
235
235
  * Loads an object reference.
236
236
  */
237
- export const load: <T>(ref: Ref<T>) => Effect.Effect<T, Err.ObjectNotFoundError, never> = Effect.fn(function* (ref) {
238
- const object = yield* promiseWithCauseCapture(() => ref.tryLoad());
239
- if (!object) {
240
- return yield* Effect.fail(new Err.ObjectNotFoundError(ref.dxn));
241
- }
242
- return object;
243
- });
237
+ export const load: <T>(ref: Ref<T>) => Effect.Effect<T, Err.ObjectNotFoundError, never> = Effect.fn('Database.load')(
238
+ function* (ref) {
239
+ const object = yield* promiseWithCauseCapture(() => ref.tryLoad());
240
+ if (!object) {
241
+ return yield* Effect.fail(new Err.ObjectNotFoundError(ref.dxn));
242
+ }
243
+ return object;
244
+ },
245
+ );
244
246
 
245
247
  /**
246
248
  * Loads an object reference option.
247
249
  */
248
250
  // TODO(dmaretskyi): Do we need this -- you can just use `Effect.catchTag` in calling code instead.
249
- export const loadOption: <T>(ref: Ref<T>) => Effect.Effect<Option.Option<T>, never, never> = Effect.fn(function* (ref) {
251
+ export const loadOption: <T>(ref: Ref<T>) => Effect.Effect<Option.Option<T>, never, never> = Effect.fn(
252
+ 'Database.loadOption',
253
+ )(function* (ref) {
250
254
  const object = yield* load(ref).pipe(Effect.catchTag('ObjectNotFoundError', () => Effect.succeed(undefined)));
251
255
 
252
256
  return Option.fromNullable(object);
@@ -257,21 +261,23 @@ export const loadOption: <T>(ref: Ref<T>) => Effect.Effect<Option.Option<T>, nev
257
261
  * @see {@link Database.add}
258
262
  */
259
263
  export const add = <T extends Entity.Unknown>(obj: T): Effect.Effect<T, never, Service> =>
260
- Service.pipe(Effect.map(({ db }) => db.add(obj)));
264
+ Service.pipe(Effect.map(({ db }) => db.add(obj))).pipe(Effect.withSpan('Database.add'));
261
265
 
262
266
  /**
263
267
  * Removes an object from the database.
264
268
  * @see {@link Database.remove}
265
269
  */
266
270
  export const remove = <T extends Entity.Unknown>(obj: T): Effect.Effect<void, never, Service> =>
267
- Service.pipe(Effect.map(({ db }) => db.remove(obj)));
271
+ Service.pipe(Effect.map(({ db }) => db.remove(obj))).pipe(Effect.withSpan('Database.remove'));
268
272
 
269
273
  /**
270
274
  * Flushes pending changes to disk.
271
275
  * @see {@link Database.flush}
272
276
  */
273
277
  export const flush = (opts?: FlushOptions) =>
274
- Service.pipe(Effect.flatMap(({ db }) => promiseWithCauseCapture(() => db.flush(opts))));
278
+ Service.pipe(Effect.flatMap(({ db }) => promiseWithCauseCapture(() => db.flush(opts)))).pipe(
279
+ Effect.withSpan('Database.flush'),
280
+ );
275
281
 
276
282
  /**
277
283
  * Creates a `QueryResult` object that can be subscribed to.
@@ -292,7 +298,36 @@ export const runQuery: {
292
298
  <Q extends Query.Any>(query: Q): Effect.Effect<Query.Type<Q>[], never, Service>;
293
299
  <F extends Filter.Any>(filter: F): Effect.Effect<Filter.Type<F>[], never, Service>;
294
300
  } = (queryOrFilter: Query.Any | Filter.Any) =>
295
- query(queryOrFilter as any).pipe(Effect.flatMap((queryResult) => promiseWithCauseCapture(() => queryResult.run())));
301
+ query(queryOrFilter as any).pipe(
302
+ Effect.flatMap((queryResult) => promiseWithCauseCapture(() => queryResult.run())),
303
+ Effect.withSpan('Database.runQuery'),
304
+ );
305
+
306
+ /**
307
+ * Executes the query once and returns the first result as or None.
308
+ */
309
+ export const runQueryFirst: {
310
+ <Q extends Query.Any>(query: Q): Effect.Effect<Option.Option<Query.Type<Q>>, never, Service>;
311
+ <F extends Filter.Any>(filter: F): Effect.Effect<Option.Option<Filter.Type<F>>, never, Service>;
312
+ } = (queryOrFilter: Query.Any | Filter.Any) =>
313
+ query(queryOrFilter as any).pipe(
314
+ Effect.flatMap((queryResult) =>
315
+ promiseWithCauseCapture(async () => Option.fromNullable(await queryResult.firstOrUndefined())),
316
+ ),
317
+ Effect.withSpan('Database.runQueryFirst'),
318
+ );
319
+
320
+ /**
321
+ * Persists schemas in the database so they replicate to other clients.
322
+ * @see {@link SchemaRegistry.SchemaRegistry.register}
323
+ */
324
+ export const registerSchema = (
325
+ input: SchemaRegistry.RegisterSchemaInput[],
326
+ ): Effect.Effect<Type.RuntimeType[], never, Service> =>
327
+ Service.pipe(
328
+ Effect.flatMap(({ db }) => promiseWithCauseCapture(() => db.schemaRegistry.register(input))),
329
+ Effect.withSpan('Database.registerSchema'),
330
+ );
296
331
 
297
332
  /**
298
333
  * Creates a schema query result that can be subscribed to.
package/src/Entity.ts CHANGED
@@ -7,9 +7,9 @@
7
7
  import type { ForeignKey } from '@dxos/echo-protocol';
8
8
  import type { DXN, ObjectId } from '@dxos/keys';
9
9
 
10
- import * as entityInternal from './internal/Entity';
11
10
  import * as internal from './internal';
12
11
  import type * as Relation from './Relation';
12
+ import type * as Type from './Type';
13
13
 
14
14
  // Re-export KindId and SnapshotKindId from internal.
15
15
  export const KindId = internal.KindId;
@@ -132,6 +132,12 @@ export const getDXN = (entity: Unknown | Snapshot): DXN => internal.getDXN(entit
132
132
  */
133
133
  export const getTypeDXN = internal.getTypeDXN;
134
134
 
135
+ /**
136
+ * Get the schema of an entity.
137
+ * Returns the branded ECHO schema used to create the entity.
138
+ */
139
+ export const getSchema: (entity: Unknown | Snapshot) => Type.AnyEntity | undefined = internal.getSchema as any;
140
+
135
141
  /**
136
142
  * Get the typename of an entity's type.
137
143
  */
@@ -192,7 +198,7 @@ export const subscribe = (entity: Unknown, callback: () => void): (() => void) =
192
198
  //
193
199
 
194
200
  /**
195
- * Used to provide a mutable view of an entity within `Entity.change`.
201
+ * Used to provide a mutable view of an entity within `Entity.update`.
196
202
  */
197
203
  export type Mutable<T> = internal.Mutable<T>;
198
204
 
@@ -200,7 +206,7 @@ export type Mutable<T> = internal.Mutable<T>;
200
206
  * Perform mutations on an entity (object or relation) within a change context.
201
207
  *
202
208
  * Entities are read-only by default. Mutations are batched and notifications fire
203
- * when the callback completes. Direct mutations outside of `Entity.change` will throw
209
+ * when the callback completes. Direct mutations outside of `Entity.update` will throw
204
210
  * at runtime.
205
211
  *
206
212
  * @param entity - The echo entity (object or relation) to mutate.
@@ -208,30 +214,30 @@ export type Mutable<T> = internal.Mutable<T>;
208
214
  *
209
215
  * @example
210
216
  * ```typescript
211
- * // Mutate within Entity.change
212
- * Entity.change(entity, (e) => {
213
- * e.name = 'Updated';
214
- * e.count = 42;
217
+ * // Mutate within Entity.update
218
+ * Entity.update(entity, (obj) => {
219
+ * obj.name = 'Updated';
220
+ * obj.count = 42;
215
221
  * });
216
222
  *
217
223
  * // Direct mutation throws
218
- * entity.name = 'Bob'; // Error: Cannot modify outside Entity.change()
224
+ * entity.name = 'Bob'; // Error: Cannot modify outside Entity.update()
219
225
  * ```
220
226
  *
221
- * Note: For type-specific operations, prefer `Obj.change` or `Relation.change`.
227
+ * Note: For type-specific operations, prefer `Obj.update` or `Relation.update`.
222
228
  */
223
- export const change = <T extends Unknown>(entity: T, callback: internal.ChangeCallback<T>): void => {
229
+ export const update = <T extends Unknown>(entity: T, callback: internal.ChangeCallback<T>): void => {
224
230
  internal.change(entity, callback);
225
231
  };
226
232
 
227
233
  /**
228
234
  * Add a tag to an entity.
229
- * Must be called within an `Entity.change`, `Obj.change`, or `Relation.change` callback.
235
+ * Must be called within an `Entity.update`, `Obj.update`, or `Relation.update` callback.
230
236
  */
231
237
  export const addTag = (entity: Mutable<Unknown>, tag: string): void => internal.addTag(entity, tag);
232
238
 
233
239
  /**
234
240
  * Remove a tag from an entity.
235
- * Must be called within an `Entity.change`, `Obj.change`, or `Relation.change` callback.
241
+ * Must be called within an `Entity.update`, `Obj.update`, or `Relation.update` callback.
236
242
  */
237
243
  export const removeTag = (entity: Mutable<Unknown>, tag: string): void => internal.removeTag(entity, tag);
@@ -0,0 +1,235 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import * as Option from 'effect/Option';
6
+ import * as Schema from 'effect/Schema';
7
+ import { describe, test } from 'vitest';
8
+
9
+ import * as Extension from './Extension';
10
+
11
+ describe('Extension', () => {
12
+ describe('make', () => {
13
+ test('creates an extension with a string schema', ({ expect }) => {
14
+ const ColorExtension = Extension.make('color', Schema.String);
15
+
16
+ expect(ColorExtension.key).toBe('color');
17
+ expect(ColorExtension.valueSchema).toBe(Schema.String);
18
+ expect(ColorExtension[Extension.TypeId]).toBeDefined();
19
+ });
20
+
21
+ test('creates an extension with a number schema', ({ expect }) => {
22
+ const PriorityExtension = Extension.make('priority', Schema.Number);
23
+
24
+ expect(PriorityExtension.key).toBe('priority');
25
+ expect(PriorityExtension.valueSchema).toBe(Schema.Number);
26
+ });
27
+
28
+ test('creates an extension with a complex schema', ({ expect }) => {
29
+ const MetadataSchema = Schema.Struct({
30
+ author: Schema.String,
31
+ version: Schema.Number,
32
+ tags: Schema.Array(Schema.String),
33
+ });
34
+ const MetadataExtension = Extension.make('metadata', MetadataSchema);
35
+
36
+ expect(MetadataExtension.key).toBe('metadata');
37
+ expect(MetadataExtension.valueSchema).toBe(MetadataSchema);
38
+ });
39
+ });
40
+
41
+ describe('get and set', () => {
42
+ test('set and get a string value', ({ expect }) => {
43
+ const ColorExtension = Extension.make('color', Schema.String);
44
+ const values: Extension.Values = {};
45
+
46
+ Extension.set(values, ColorExtension, 'red');
47
+ const result = Extension.get(values, ColorExtension);
48
+
49
+ expect(Option.isSome(result)).toBe(true);
50
+ expect(Option.getOrThrow(result)).toBe('red');
51
+ });
52
+
53
+ test('set and get a number value', ({ expect }) => {
54
+ const PriorityExtension = Extension.make('priority', Schema.Number);
55
+ const values: Extension.Values = {};
56
+
57
+ Extension.set(values, PriorityExtension, 42);
58
+ const result = Extension.get(values, PriorityExtension);
59
+
60
+ expect(Option.isSome(result)).toBe(true);
61
+ expect(Option.getOrThrow(result)).toBe(42);
62
+ });
63
+
64
+ test('set and get a boolean value', ({ expect }) => {
65
+ const EnabledExtension = Extension.make('enabled', Schema.Boolean);
66
+ const values: Extension.Values = {};
67
+
68
+ Extension.set(values, EnabledExtension, true);
69
+ const result = Extension.get(values, EnabledExtension);
70
+
71
+ expect(Option.isSome(result)).toBe(true);
72
+ expect(Option.getOrThrow(result)).toBe(true);
73
+ });
74
+
75
+ test('set and get a complex object value', ({ expect }) => {
76
+ const ConfigSchema = Schema.Struct({
77
+ theme: Schema.String,
78
+ fontSize: Schema.Number,
79
+ darkMode: Schema.Boolean,
80
+ });
81
+ const ConfigExtension = Extension.make('config', ConfigSchema);
82
+ const values: Extension.Values = {};
83
+
84
+ const config = { theme: 'modern', fontSize: 14, darkMode: true };
85
+ Extension.set(values, ConfigExtension, config);
86
+ const result = Extension.get(values, ConfigExtension);
87
+
88
+ expect(Option.isSome(result)).toBe(true);
89
+ expect(Option.getOrThrow(result)).toEqual(config);
90
+ });
91
+
92
+ test('set and get an array value', ({ expect }) => {
93
+ const TagsExtension = Extension.make('tags', Schema.Array(Schema.String));
94
+ const values: Extension.Values = {};
95
+
96
+ const tags = ['important', 'urgent', 'review'];
97
+ Extension.set(values, TagsExtension, tags);
98
+ const result = Extension.get(values, TagsExtension);
99
+
100
+ expect(Option.isSome(result)).toBe(true);
101
+ expect(Option.getOrThrow(result)).toEqual(tags);
102
+ });
103
+
104
+ test('get returns None for missing extension', ({ expect }) => {
105
+ const ColorExtension = Extension.make('color', Schema.String);
106
+ const values: Extension.Values = {};
107
+
108
+ const result = Extension.get(values, ColorExtension);
109
+
110
+ expect(Option.isNone(result)).toBe(true);
111
+ });
112
+
113
+ test('overwriting an extension value', ({ expect }) => {
114
+ const ColorExtension = Extension.make('color', Schema.String);
115
+ const values: Extension.Values = {};
116
+
117
+ Extension.set(values, ColorExtension, 'red');
118
+ Extension.set(values, ColorExtension, 'blue');
119
+ const result = Extension.get(values, ColorExtension);
120
+
121
+ expect(Option.isSome(result)).toBe(true);
122
+ expect(Option.getOrThrow(result)).toBe('blue');
123
+ });
124
+
125
+ test('multiple extensions on the same values object', ({ expect }) => {
126
+ const ColorExtension = Extension.make('color', Schema.String);
127
+ const PriorityExtension = Extension.make('priority', Schema.Number);
128
+ const EnabledExtension = Extension.make('enabled', Schema.Boolean);
129
+ const values: Extension.Values = {};
130
+
131
+ Extension.set(values, ColorExtension, 'green');
132
+ Extension.set(values, PriorityExtension, 5);
133
+ Extension.set(values, EnabledExtension, false);
134
+
135
+ expect(Option.getOrThrow(Extension.get(values, ColorExtension))).toBe('green');
136
+ expect(Option.getOrThrow(Extension.get(values, PriorityExtension))).toBe(5);
137
+ expect(Option.getOrThrow(Extension.get(values, EnabledExtension))).toBe(false);
138
+ });
139
+ });
140
+
141
+ describe('curried forms', () => {
142
+ test('get curried form', ({ expect }) => {
143
+ const ColorExtension = Extension.make('color', Schema.String);
144
+ const values: Extension.Values = {};
145
+ Extension.set(values, ColorExtension, 'purple');
146
+
147
+ const getColor = Extension.get(ColorExtension);
148
+ const result = getColor(values);
149
+
150
+ expect(Option.isSome(result)).toBe(true);
151
+ expect(Option.getOrThrow(result)).toBe('purple');
152
+ });
153
+
154
+ test('set curried form', ({ expect }) => {
155
+ const ColorExtension = Extension.make('color', Schema.String);
156
+ const values: Extension.Values = {};
157
+
158
+ const setOrange = Extension.set(ColorExtension, 'orange');
159
+ setOrange(values);
160
+ const result = Extension.get(values, ColorExtension);
161
+
162
+ expect(Option.isSome(result)).toBe(true);
163
+ expect(Option.getOrThrow(result)).toBe('orange');
164
+ });
165
+
166
+ test('curried forms work in pipelines', ({ expect }) => {
167
+ const ColorExtension = Extension.make('color', Schema.String);
168
+ const values: Extension.Values = {};
169
+
170
+ Extension.set(ColorExtension, 'cyan')(values);
171
+ const result = Extension.get(ColorExtension)(values);
172
+
173
+ expect(Option.isSome(result)).toBe(true);
174
+ expect(Option.getOrThrow(result)).toBe('cyan');
175
+ });
176
+ });
177
+
178
+ describe('Key', () => {
179
+ test('Key.make creates a branded key', ({ expect }) => {
180
+ const key = Extension.Key.make('my-extension');
181
+
182
+ expect(key).toBe('my-extension');
183
+ expect(typeof key).toBe('string');
184
+ });
185
+ });
186
+
187
+ describe('Values schema', () => {
188
+ test('Values can be used as a schema', ({ expect }) => {
189
+ const PersonWithExtensions = Schema.Struct({
190
+ name: Schema.String,
191
+ extensions: Extension.Values,
192
+ });
193
+
194
+ const person = Schema.decodeUnknownSync(PersonWithExtensions)({
195
+ name: 'Alice',
196
+ extensions: {},
197
+ });
198
+
199
+ expect(person.name).toBe('Alice');
200
+ expect(person.extensions).toEqual({});
201
+ });
202
+
203
+ test('Values schema accepts extension data', ({ expect }) => {
204
+ const ColorExtension = Extension.make('color', Schema.String);
205
+ const values: Extension.Values = {};
206
+ Extension.set(values, ColorExtension, 'red');
207
+
208
+ const PersonWithExtensions = Schema.Struct({
209
+ name: Schema.String,
210
+ extensions: Extension.Values,
211
+ });
212
+
213
+ const person = Schema.decodeUnknownSync(PersonWithExtensions)({
214
+ name: 'Bob',
215
+ extensions: values,
216
+ });
217
+
218
+ expect(person.name).toBe('Bob');
219
+ const color = Extension.get(person.extensions, ColorExtension);
220
+ expect(Option.getOrThrow(color)).toBe('red');
221
+ });
222
+ });
223
+
224
+ describe('TypeId', () => {
225
+ test('TypeId is a unique symbol-like string', ({ expect }) => {
226
+ expect(Extension.TypeId).toBe('~@dxos/echo/Extension');
227
+ });
228
+
229
+ test('extensions have TypeId property', ({ expect }) => {
230
+ const ColorExtension = Extension.make('color', Schema.String);
231
+
232
+ expect(Extension.TypeId in ColorExtension).toBe(true);
233
+ });
234
+ });
235
+ });
@@ -0,0 +1,122 @@
1
+ // @import-as-namespace
2
+ //
3
+ // Copyright 2026 DXOS.org
4
+ //
5
+
6
+ import * as Function from 'effect/Function';
7
+ import * as Option from 'effect/Option';
8
+ import * as Schema from 'effect/Schema';
9
+ import type * as Types from 'effect/Types';
10
+
11
+ // @import-as-namespace
12
+
13
+ /**
14
+ * Extensions allow objects to contain typed properties that are not part of the schema.
15
+ */
16
+
17
+ export const TypeId = '~@dxos/echo/Extension' as const;
18
+ export type TypeId = typeof TypeId;
19
+
20
+ export interface Extension<T> extends Record<
21
+ TypeId,
22
+ {
23
+ _Type: T;
24
+ }
25
+ > {
26
+ readonly [TypeId]: {
27
+ _Type: T;
28
+ };
29
+
30
+ readonly key: Key;
31
+ readonly valueSchema: Schema.Schema<T>;
32
+ }
33
+
34
+ /**
35
+ * Create a new typed extension.
36
+ *
37
+ * ```ts
38
+ * const ColorExtension = Extension.make('color', Schema.String);
39
+ *
40
+ * const obj = Obj.make(Person, {
41
+ * [Obj.Meta]: { keys: [{ source: 'external', id: '123' }] },
42
+ * name: 'John',
43
+ * email: 'john@example.com',
44
+ * });
45
+ *
46
+ * Obj.update(obj, (obj) => {
47
+ * Extension.set(obj.extensions, ColorExtension, 'red');
48
+ * });
49
+ *
50
+ * console.log(Extension.get(obj.extensions, ColorExtension)); // 'red'
51
+ * ```
52
+ */
53
+ export const make = <S extends Schema.Schema.AnyNoContext>(
54
+ key: string,
55
+ valueSchema: S,
56
+ ): Extension<Schema.Schema.Type<S>> => {
57
+ return {
58
+ [TypeId]: {} as any,
59
+ key: Key.make(key),
60
+ valueSchema,
61
+ };
62
+ };
63
+
64
+ /**
65
+ * Unique identifier for an extension.
66
+ */
67
+ // TODO(dmaretskyi): filter to be fully qualified: (e.g., org.dxos.extension.color)
68
+ export const Key = Schema.String.pipe(Schema.brand('~@dxos/echo/ExtensionKey'));
69
+ export type Key = Schema.Schema.Type<typeof Key>;
70
+
71
+ /**
72
+ * Set of extension values.
73
+ *
74
+ * Can be used inside an object/relation schema:
75
+ *
76
+ * ```ts
77
+ * const Person = Schema.Struct({
78
+ * name: Schema.String,
79
+ * extensions: Extension.Values,
80
+ * });
81
+ * ```
82
+ */
83
+ export const Values = Schema.Record({ key: Key, value: Schema.Unknown });
84
+ export interface Values extends Schema.Schema.Type<typeof Values> {}
85
+
86
+ /**
87
+ * Get the value of an extension from a set of values.
88
+ */
89
+ export const get: {
90
+ <T>(extension: Extension<T>): (values: Values) => Option.Option<T>;
91
+ <T>(values: Values, extension: Extension<T>): Option.Option<T>;
92
+ } = Function.dual<
93
+ <T>(extension: Extension<T>) => (values: Values) => Option.Option<T>,
94
+ <T>(values: Values, extension: Extension<T>) => Option.Option<T>
95
+ >(2, (values, extension) => {
96
+ if (!(extension.key in values)) {
97
+ return Option.none();
98
+ }
99
+
100
+ return Function.pipe(values[extension.key], Schema.decodeUnknownSync(extension.valueSchema), Option.some);
101
+ });
102
+
103
+ /**
104
+ * Set the value of an extension in a set of values.
105
+ *
106
+ * Can also be used within Obj.update callback:
107
+ *
108
+ * ```ts
109
+ * Obj.update(obj, (obj) => {
110
+ * Extension.set(obj.extensions, ColorExtension, 'red');
111
+ * });
112
+ * ```
113
+ */
114
+ export const set: {
115
+ <T>(extension: Extension<T>, value: T): (values: Values) => void;
116
+ <T>(values: Types.Mutable<Values>, extension: Extension<T>, value: T): void;
117
+ } = Function.dual<
118
+ <T>(extension: Extension<T>, value: T) => (values: Values) => void,
119
+ <T>(values: Types.Mutable<Values>, extension: Extension<T>, value: T) => void
120
+ >(3, (values, extension, value) => {
121
+ values[extension.key] = Schema.encodeSync(extension.valueSchema)(value);
122
+ });