@dxos/echo 0.8.4-main.21d9917 → 0.8.4-main.2244d791bb

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 (278) hide show
  1. package/dist/lib/{browser → neutral}/Annotation.mjs +7 -5
  2. package/dist/lib/neutral/Database.mjs +45 -0
  3. package/dist/lib/{browser → neutral}/Entity.mjs +8 -9
  4. package/dist/lib/{browser → neutral}/Err.mjs +4 -3
  5. package/dist/lib/{browser → neutral}/Filter.mjs +9 -10
  6. package/dist/lib/{browser → neutral}/Format.mjs +4 -5
  7. package/dist/lib/neutral/JsonSchema.mjs +19 -0
  8. package/dist/lib/{browser → neutral}/Key.mjs +2 -3
  9. package/dist/lib/{node-esm → neutral}/Obj.mjs +23 -10
  10. package/dist/lib/{browser → neutral}/Order.mjs +2 -3
  11. package/dist/lib/neutral/Query.mjs +26 -0
  12. package/dist/lib/neutral/QueryResult.mjs +2 -0
  13. package/dist/lib/neutral/Ref.mjs +22 -0
  14. package/dist/lib/{browser → neutral}/Relation.mjs +9 -10
  15. package/dist/lib/neutral/SchemaRegistry.mjs +2 -0
  16. package/dist/lib/neutral/Tag.mjs +25 -0
  17. package/dist/lib/{browser → neutral}/Type.mjs +8 -9
  18. package/dist/lib/{browser/chunk-W5D2GWAW.mjs → neutral/chunk-36GDMODZ.mjs} +5 -6
  19. package/dist/lib/{browser/chunk-W5D2GWAW.mjs.map → neutral/chunk-36GDMODZ.mjs.map} +1 -1
  20. package/dist/lib/{browser/chunk-ZHXZGIXD.mjs → neutral/chunk-4L6DOFXP.mjs} +1 -3
  21. package/dist/lib/{browser/chunk-ZHXZGIXD.mjs.map → neutral/chunk-4L6DOFXP.mjs.map} +1 -1
  22. package/dist/lib/{browser/chunk-NOPVNWPT.mjs → neutral/chunk-5MGEVDUW.mjs} +73 -8
  23. package/dist/lib/neutral/chunk-5MGEVDUW.mjs.map +7 -0
  24. package/dist/lib/{browser/chunk-FRDT7RA4.mjs → neutral/chunk-7WE7SBA2.mjs} +4 -5
  25. package/dist/lib/neutral/chunk-7WE7SBA2.mjs.map +7 -0
  26. package/dist/lib/{browser/chunk-INHXFXY5.mjs → neutral/chunk-ANHVGJI4.mjs} +2 -3
  27. package/dist/lib/{node-esm/chunk-AJEMYSIR.mjs.map → neutral/chunk-ANHVGJI4.mjs.map} +1 -1
  28. package/dist/lib/neutral/chunk-B4FLOYG7.mjs +130 -0
  29. package/dist/lib/neutral/chunk-B4FLOYG7.mjs.map +7 -0
  30. package/dist/lib/{browser/chunk-U2J7TA7K.mjs → neutral/chunk-BHR4XVJK.mjs} +5 -6
  31. package/dist/lib/{node-esm/chunk-DMR7OAFK.mjs.map → neutral/chunk-BHR4XVJK.mjs.map} +1 -1
  32. package/dist/lib/{browser/chunk-ZAGAOZVY.mjs → neutral/chunk-CH3HX4Z5.mjs} +4 -5
  33. package/dist/lib/{node-esm/chunk-QYR67VBV.mjs.map → neutral/chunk-CH3HX4Z5.mjs.map} +1 -1
  34. package/dist/lib/{browser/chunk-CGS2ULMK.mjs → neutral/chunk-J5LGTIGS.mjs} +1 -2
  35. package/dist/lib/{browser/chunk-YKTSSMDS.mjs → neutral/chunk-KB7RIVLK.mjs} +1 -3
  36. package/dist/lib/neutral/chunk-KB7RIVLK.mjs.map +7 -0
  37. package/dist/lib/{browser/chunk-2SBB7OWV.mjs → neutral/chunk-KSFI2POM.mjs} +11 -10
  38. package/dist/lib/neutral/chunk-KSFI2POM.mjs.map +7 -0
  39. package/dist/lib/{browser/chunk-6L5HHUVU.mjs → neutral/chunk-LKQBTRJX.mjs} +18 -5
  40. package/dist/lib/neutral/chunk-LKQBTRJX.mjs.map +7 -0
  41. package/dist/lib/{browser/chunk-CJ5YELTO.mjs → neutral/chunk-MXQJZCPY.mjs} +15 -3
  42. package/dist/lib/neutral/chunk-MXQJZCPY.mjs.map +7 -0
  43. package/dist/lib/{browser/chunk-PQZW3S6L.mjs → neutral/chunk-N2QNHMT5.mjs} +3 -4
  44. package/dist/lib/{node-esm/chunk-Y75VU7LB.mjs.map → neutral/chunk-N2QNHMT5.mjs.map} +1 -1
  45. package/dist/lib/{browser/chunk-IXVWLTG7.mjs → neutral/chunk-OMUPQMLR.mjs} +1 -3
  46. package/dist/lib/{browser/chunk-IXVWLTG7.mjs.map → neutral/chunk-OMUPQMLR.mjs.map} +1 -1
  47. package/dist/lib/{node-esm/chunk-YOLH5KS4.mjs → neutral/chunk-RIMHNJ3E.mjs} +17 -4
  48. package/dist/lib/neutral/chunk-RIMHNJ3E.mjs.map +7 -0
  49. package/dist/lib/{browser/chunk-JMKVF2YQ.mjs → neutral/chunk-ROKO4RKJ.mjs} +2 -3
  50. package/dist/lib/{node-esm/chunk-K37NA7PO.mjs.map → neutral/chunk-ROKO4RKJ.mjs.map} +1 -1
  51. package/dist/lib/{browser/chunk-KQQGVHFN.mjs → neutral/chunk-SHLKRGRB.mjs} +5 -6
  52. package/dist/lib/{node-esm/chunk-NRN3ZW2T.mjs.map → neutral/chunk-SHLKRGRB.mjs.map} +1 -1
  53. package/dist/lib/{browser/chunk-22JMFST2.mjs → neutral/chunk-UW4T7PAL.mjs} +3 -4
  54. package/dist/lib/{browser/chunk-22JMFST2.mjs.map → neutral/chunk-UW4T7PAL.mjs.map} +1 -1
  55. package/dist/lib/{browser/chunk-XDIUHAAX.mjs → neutral/chunk-WSPASOLU.mjs} +3 -4
  56. package/dist/lib/{node-esm/chunk-QLI2EIJ2.mjs.map → neutral/chunk-WSPASOLU.mjs.map} +1 -1
  57. package/dist/lib/{browser/chunk-MYCCGG2T.mjs → neutral/chunk-X2MPMYYN.mjs} +1 -3
  58. package/dist/lib/{node-esm/chunk-MOWUEW5P.mjs.map → neutral/chunk-X2MPMYYN.mjs.map} +1 -1
  59. package/dist/lib/{browser/chunk-FPOISFQK.mjs → neutral/chunk-XAJMXQ4H.mjs} +7 -4
  60. package/dist/lib/neutral/chunk-XAJMXQ4H.mjs.map +7 -0
  61. package/dist/lib/{browser/chunk-BJPE6CIC.mjs → neutral/chunk-ZUG3PNXU.mjs} +146 -83
  62. package/dist/lib/neutral/chunk-ZUG3PNXU.mjs.map +7 -0
  63. package/dist/lib/{browser → neutral}/index.mjs +23 -24
  64. package/dist/lib/{browser → neutral}/internal/index.mjs +13 -8
  65. package/dist/lib/neutral/meta.json +1 -0
  66. package/dist/lib/{browser → neutral}/testing/index.mjs +23 -24
  67. package/dist/lib/{node-esm → neutral}/testing/index.mjs.map +1 -1
  68. package/dist/types/src/Annotation.d.ts +22 -0
  69. package/dist/types/src/Annotation.d.ts.map +1 -1
  70. package/dist/types/src/Database.d.ts +72 -50
  71. package/dist/types/src/Database.d.ts.map +1 -1
  72. package/dist/types/src/Err.d.ts +43 -0
  73. package/dist/types/src/Err.d.ts.map +1 -1
  74. package/dist/types/src/Obj.d.ts +84 -4
  75. package/dist/types/src/Obj.d.ts.map +1 -1
  76. package/dist/types/src/Query.d.ts +10 -0
  77. package/dist/types/src/Query.d.ts.map +1 -1
  78. package/dist/types/src/Relation.d.ts +2 -2
  79. package/dist/types/src/Relation.d.ts.map +1 -1
  80. package/dist/types/src/hierarchy.test.d.ts +2 -0
  81. package/dist/types/src/hierarchy.test.d.ts.map +1 -0
  82. package/dist/types/src/internal/annotations/annotations.d.ts +13 -6
  83. package/dist/types/src/internal/annotations/annotations.d.ts.map +1 -1
  84. package/dist/types/src/internal/annotations/util.d.ts +12 -0
  85. package/dist/types/src/internal/annotations/util.d.ts.map +1 -1
  86. package/dist/types/src/internal/api/meta.d.ts +2 -2
  87. package/dist/types/src/internal/api/meta.d.ts.map +1 -1
  88. package/dist/types/src/internal/entities/model.d.ts +3 -1
  89. package/dist/types/src/internal/entities/model.d.ts.map +1 -1
  90. package/dist/types/src/internal/entities/object.d.ts.map +1 -1
  91. package/dist/types/src/internal/entities/relation.d.ts.map +1 -1
  92. package/dist/types/src/internal/formats/select.d.ts +2 -2
  93. package/dist/types/src/internal/formats/select.d.ts.map +1 -1
  94. package/dist/types/src/internal/json-schema/json-schema-normalize.d.ts.map +1 -1
  95. package/dist/types/src/internal/json-schema/json-schema-type.d.ts +130 -29
  96. package/dist/types/src/internal/json-schema/json-schema-type.d.ts.map +1 -1
  97. package/dist/types/src/internal/json-schema/json-schema.d.ts +2 -1
  98. package/dist/types/src/internal/json-schema/json-schema.d.ts.map +1 -1
  99. package/dist/types/src/internal/object/json-serializer.d.ts.map +1 -1
  100. package/dist/types/src/internal/object/snapshot.d.ts.map +1 -1
  101. package/dist/types/src/internal/proxy/make-object.d.ts.map +1 -1
  102. package/dist/types/src/internal/proxy/typed-handler.d.ts +2 -1
  103. package/dist/types/src/internal/proxy/typed-handler.d.ts.map +1 -1
  104. package/dist/types/src/internal/ref/ref.d.ts +16 -2
  105. package/dist/types/src/internal/ref/ref.d.ts.map +1 -1
  106. package/dist/types/src/internal/schema/compose.d.ts +2 -1
  107. package/dist/types/src/internal/schema/compose.d.ts.map +1 -1
  108. package/dist/types/src/internal/schema/echo-schema.d.ts +2 -1
  109. package/dist/types/src/internal/schema/echo-schema.d.ts.map +1 -1
  110. package/dist/types/src/internal/schema/persistent-schema.d.ts.map +1 -1
  111. package/dist/types/src/internal/types/meta.d.ts +5 -5
  112. package/dist/types/src/internal/types/meta.d.ts.map +1 -1
  113. package/dist/types/src/internal/types/typename.d.ts +8 -0
  114. package/dist/types/src/internal/types/typename.d.ts.map +1 -1
  115. package/dist/types/tsconfig.tsbuildinfo +1 -1
  116. package/package.json +34 -113
  117. package/src/Annotation.ts +27 -0
  118. package/src/Database.ts +141 -124
  119. package/src/Err.ts +20 -0
  120. package/src/Obj.test.ts +56 -0
  121. package/src/Obj.ts +140 -11
  122. package/src/Query.ts +28 -0
  123. package/src/Relation.ts +5 -3
  124. package/src/hierarchy.test.ts +33 -0
  125. package/src/internal/annotations/annotations.ts +28 -1
  126. package/src/internal/annotations/util.ts +13 -0
  127. package/src/internal/api/meta.ts +3 -3
  128. package/src/internal/entities/model.ts +4 -0
  129. package/src/internal/entities/object.ts +1 -3
  130. package/src/internal/entities/relation.ts +1 -3
  131. package/src/internal/formats/format.test.ts +1 -1
  132. package/src/internal/formats/select.ts +1 -1
  133. package/src/internal/formats/types.ts +1 -1
  134. package/src/internal/json-schema/json-schema-normalize.ts +4 -2
  135. package/src/internal/json-schema/json-schema-type.ts +30 -31
  136. package/src/internal/json-schema/json-schema.ts +10 -6
  137. package/src/internal/object/json-serializer.test.ts +26 -2
  138. package/src/internal/object/json-serializer.ts +25 -9
  139. package/src/internal/object/schema-validator.test.ts +3 -7
  140. package/src/internal/object/set-value.test.ts +9 -9
  141. package/src/internal/object/snapshot.ts +29 -6
  142. package/src/internal/proxy/make-object.ts +12 -2
  143. package/src/internal/proxy/typed-handler.test.ts +8 -12
  144. package/src/internal/proxy/typed-handler.ts +9 -1
  145. package/src/internal/proxy/typed-object.test.ts +11 -11
  146. package/src/internal/ref/ref.test.ts +1 -1
  147. package/src/internal/ref/ref.ts +26 -2
  148. package/src/internal/schema/compose.ts +13 -8
  149. package/src/internal/schema/echo-schema.ts +3 -2
  150. package/src/internal/schema/persistent-schema.ts +2 -1
  151. package/src/internal/types/meta.ts +12 -13
  152. package/src/internal/types/typename.ts +10 -0
  153. package/dist/lib/browser/Database.mjs +0 -18
  154. package/dist/lib/browser/JsonSchema.mjs +0 -20
  155. package/dist/lib/browser/Obj.mjs +0 -91
  156. package/dist/lib/browser/Query.mjs +0 -27
  157. package/dist/lib/browser/QueryResult.mjs +0 -3
  158. package/dist/lib/browser/Ref.mjs +0 -23
  159. package/dist/lib/browser/SchemaRegistry.mjs +0 -3
  160. package/dist/lib/browser/Tag.mjs +0 -26
  161. package/dist/lib/browser/chunk-2SBB7OWV.mjs.map +0 -7
  162. package/dist/lib/browser/chunk-6L5HHUVU.mjs.map +0 -7
  163. package/dist/lib/browser/chunk-7STIBCP7.mjs +0 -133
  164. package/dist/lib/browser/chunk-7STIBCP7.mjs.map +0 -7
  165. package/dist/lib/browser/chunk-BJPE6CIC.mjs.map +0 -7
  166. package/dist/lib/browser/chunk-CJ5YELTO.mjs.map +0 -7
  167. package/dist/lib/browser/chunk-FPOISFQK.mjs.map +0 -7
  168. package/dist/lib/browser/chunk-FRDT7RA4.mjs.map +0 -7
  169. package/dist/lib/browser/chunk-INHXFXY5.mjs.map +0 -7
  170. package/dist/lib/browser/chunk-JMKVF2YQ.mjs.map +0 -7
  171. package/dist/lib/browser/chunk-KQQGVHFN.mjs.map +0 -7
  172. package/dist/lib/browser/chunk-MYCCGG2T.mjs.map +0 -7
  173. package/dist/lib/browser/chunk-NOPVNWPT.mjs.map +0 -7
  174. package/dist/lib/browser/chunk-PQZW3S6L.mjs.map +0 -7
  175. package/dist/lib/browser/chunk-RK4Z4JUZ.mjs +0 -283
  176. package/dist/lib/browser/chunk-RK4Z4JUZ.mjs.map +0 -7
  177. package/dist/lib/browser/chunk-U2J7TA7K.mjs.map +0 -7
  178. package/dist/lib/browser/chunk-XDIUHAAX.mjs.map +0 -7
  179. package/dist/lib/browser/chunk-YKTSSMDS.mjs.map +0 -7
  180. package/dist/lib/browser/chunk-ZAGAOZVY.mjs.map +0 -7
  181. package/dist/lib/browser/meta.json +0 -1
  182. package/dist/lib/browser/testing/index.mjs.map +0 -7
  183. package/dist/lib/node-esm/Annotation.mjs +0 -33
  184. package/dist/lib/node-esm/Annotation.mjs.map +0 -7
  185. package/dist/lib/node-esm/Database.mjs +0 -18
  186. package/dist/lib/node-esm/Database.mjs.map +0 -7
  187. package/dist/lib/node-esm/Entity.mjs +0 -52
  188. package/dist/lib/node-esm/Entity.mjs.map +0 -7
  189. package/dist/lib/node-esm/Err.mjs +0 -11
  190. package/dist/lib/node-esm/Err.mjs.map +0 -7
  191. package/dist/lib/node-esm/Filter.mjs +0 -62
  192. package/dist/lib/node-esm/Filter.mjs.map +0 -7
  193. package/dist/lib/node-esm/Format.mjs +0 -67
  194. package/dist/lib/node-esm/Format.mjs.map +0 -7
  195. package/dist/lib/node-esm/JsonSchema.mjs +0 -20
  196. package/dist/lib/node-esm/JsonSchema.mjs.map +0 -7
  197. package/dist/lib/node-esm/Key.mjs +0 -13
  198. package/dist/lib/node-esm/Key.mjs.map +0 -7
  199. package/dist/lib/node-esm/Obj.mjs.map +0 -7
  200. package/dist/lib/node-esm/Order.mjs +0 -13
  201. package/dist/lib/node-esm/Order.mjs.map +0 -7
  202. package/dist/lib/node-esm/Query.mjs +0 -27
  203. package/dist/lib/node-esm/Query.mjs.map +0 -7
  204. package/dist/lib/node-esm/QueryResult.mjs +0 -3
  205. package/dist/lib/node-esm/QueryResult.mjs.map +0 -7
  206. package/dist/lib/node-esm/Ref.mjs +0 -23
  207. package/dist/lib/node-esm/Ref.mjs.map +0 -7
  208. package/dist/lib/node-esm/Relation.mjs +0 -85
  209. package/dist/lib/node-esm/Relation.mjs.map +0 -7
  210. package/dist/lib/node-esm/SchemaRegistry.mjs +0 -3
  211. package/dist/lib/node-esm/SchemaRegistry.mjs.map +0 -7
  212. package/dist/lib/node-esm/Tag.mjs +0 -26
  213. package/dist/lib/node-esm/Tag.mjs.map +0 -7
  214. package/dist/lib/node-esm/Type.mjs +0 -48
  215. package/dist/lib/node-esm/Type.mjs.map +0 -7
  216. package/dist/lib/node-esm/chunk-4PNXQA64.mjs +0 -250
  217. package/dist/lib/node-esm/chunk-4PNXQA64.mjs.map +0 -7
  218. package/dist/lib/node-esm/chunk-5OBN7GZW.mjs +0 -158
  219. package/dist/lib/node-esm/chunk-5OBN7GZW.mjs.map +0 -7
  220. package/dist/lib/node-esm/chunk-AJEMYSIR.mjs +0 -22
  221. package/dist/lib/node-esm/chunk-ANLVLWME.mjs +0 -98
  222. package/dist/lib/node-esm/chunk-ANLVLWME.mjs.map +0 -7
  223. package/dist/lib/node-esm/chunk-DMR7OAFK.mjs +0 -57
  224. package/dist/lib/node-esm/chunk-FWTSPIFF.mjs +0 -133
  225. package/dist/lib/node-esm/chunk-FWTSPIFF.mjs.map +0 -7
  226. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
  227. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +0 -7
  228. package/dist/lib/node-esm/chunk-ILMLLM4R.mjs +0 -204
  229. package/dist/lib/node-esm/chunk-ILMLLM4R.mjs.map +0 -7
  230. package/dist/lib/node-esm/chunk-K37NA7PO.mjs +0 -43
  231. package/dist/lib/node-esm/chunk-LT3H4JOX.mjs +0 -3847
  232. package/dist/lib/node-esm/chunk-LT3H4JOX.mjs.map +0 -7
  233. package/dist/lib/node-esm/chunk-MOLNWFNL.mjs +0 -9
  234. package/dist/lib/node-esm/chunk-MOLNWFNL.mjs.map +0 -7
  235. package/dist/lib/node-esm/chunk-MOWUEW5P.mjs +0 -15
  236. package/dist/lib/node-esm/chunk-NBWL7UCZ.mjs +0 -40
  237. package/dist/lib/node-esm/chunk-NBWL7UCZ.mjs.map +0 -7
  238. package/dist/lib/node-esm/chunk-NRN3ZW2T.mjs +0 -143
  239. package/dist/lib/node-esm/chunk-QLI2EIJ2.mjs +0 -41
  240. package/dist/lib/node-esm/chunk-QYR67VBV.mjs +0 -288
  241. package/dist/lib/node-esm/chunk-UKGVOINP.mjs +0 -9
  242. package/dist/lib/node-esm/chunk-UKGVOINP.mjs.map +0 -7
  243. package/dist/lib/node-esm/chunk-W6QIEBTQ.mjs +0 -403
  244. package/dist/lib/node-esm/chunk-W6QIEBTQ.mjs.map +0 -7
  245. package/dist/lib/node-esm/chunk-XHJRMQZD.mjs +0 -69
  246. package/dist/lib/node-esm/chunk-XHJRMQZD.mjs.map +0 -7
  247. package/dist/lib/node-esm/chunk-Y75VU7LB.mjs +0 -74
  248. package/dist/lib/node-esm/chunk-YOLH5KS4.mjs.map +0 -7
  249. package/dist/lib/node-esm/chunk-YQ2NWGL5.mjs +0 -39
  250. package/dist/lib/node-esm/chunk-YQ2NWGL5.mjs.map +0 -7
  251. package/dist/lib/node-esm/chunk-ZBB67AKD.mjs +0 -24
  252. package/dist/lib/node-esm/chunk-ZBB67AKD.mjs.map +0 -7
  253. package/dist/lib/node-esm/index.mjs +0 -85
  254. package/dist/lib/node-esm/index.mjs.map +0 -7
  255. package/dist/lib/node-esm/internal/index.mjs +0 -457
  256. package/dist/lib/node-esm/internal/index.mjs.map +0 -7
  257. package/dist/lib/node-esm/meta.json +0 -1
  258. package/dist/lib/node-esm/testing/index.mjs +0 -367
  259. /package/dist/lib/{browser → neutral}/Annotation.mjs.map +0 -0
  260. /package/dist/lib/{browser → neutral}/Database.mjs.map +0 -0
  261. /package/dist/lib/{browser → neutral}/Entity.mjs.map +0 -0
  262. /package/dist/lib/{browser → neutral}/Err.mjs.map +0 -0
  263. /package/dist/lib/{browser → neutral}/Filter.mjs.map +0 -0
  264. /package/dist/lib/{browser → neutral}/Format.mjs.map +0 -0
  265. /package/dist/lib/{browser → neutral}/JsonSchema.mjs.map +0 -0
  266. /package/dist/lib/{browser → neutral}/Key.mjs.map +0 -0
  267. /package/dist/lib/{browser → neutral}/Obj.mjs.map +0 -0
  268. /package/dist/lib/{browser → neutral}/Order.mjs.map +0 -0
  269. /package/dist/lib/{browser → neutral}/Query.mjs.map +0 -0
  270. /package/dist/lib/{browser → neutral}/QueryResult.mjs.map +0 -0
  271. /package/dist/lib/{browser → neutral}/Ref.mjs.map +0 -0
  272. /package/dist/lib/{browser → neutral}/Relation.mjs.map +0 -0
  273. /package/dist/lib/{browser → neutral}/SchemaRegistry.mjs.map +0 -0
  274. /package/dist/lib/{browser → neutral}/Tag.mjs.map +0 -0
  275. /package/dist/lib/{browser → neutral}/Type.mjs.map +0 -0
  276. /package/dist/lib/{browser/chunk-CGS2ULMK.mjs.map → neutral/chunk-J5LGTIGS.mjs.map} +0 -0
  277. /package/dist/lib/{browser → neutral}/index.mjs.map +0 -0
  278. /package/dist/lib/{browser → neutral}/internal/index.mjs.map +0 -0
@@ -7,6 +7,7 @@ import * as Schema from 'effect/Schema';
7
7
  import { JsonPath, type JsonProp } from '@dxos/effect';
8
8
 
9
9
  import { FormatAnnotation, TypeFormat } from '../formats';
10
+ import { type Mutable } from '../proxy';
10
11
  import { EntityKindSchema } from '../types';
11
12
 
12
13
  //
@@ -18,7 +19,7 @@ const SimpleTypes = Schema.Literal('array', 'boolean', 'integer', 'null', 'numbe
18
19
 
19
20
  const NonNegativeInteger = Schema.Number.pipe(Schema.greaterThanOrEqualTo(0));
20
21
 
21
- const StringArray = Schema.Array(Schema.String).pipe(Schema.mutable);
22
+ const StringArray = Schema.Array(Schema.String);
22
23
 
23
24
  const JsonSchemaOrBoolean = Schema.Union(
24
25
  Schema.suspend(() => JsonSchemaType),
@@ -48,7 +49,7 @@ export const JsonSchemaEchoAnnotations = Schema.Struct({
48
49
  Schema.Record({
49
50
  key: Schema.String,
50
51
  value: Schema.Any,
51
- }).pipe(Schema.mutable),
52
+ }),
52
53
  ),
53
54
 
54
55
  /**
@@ -62,7 +63,7 @@ export const JsonSchemaEchoAnnotations = Schema.Struct({
62
63
 
63
64
  // Not used.
64
65
  schemaId: Schema.optional(Schema.String),
65
- }).pipe(Schema.mutable),
66
+ }),
66
67
  ),
67
68
 
68
69
  /**
@@ -72,9 +73,9 @@ export const JsonSchemaEchoAnnotations = Schema.Struct({
72
73
  Schema.Record({
73
74
  key: Schema.String,
74
75
  value: Schema.Any,
75
- }).pipe(Schema.mutable),
76
+ }),
76
77
  ),
77
- }).pipe(Schema.mutable);
78
+ });
78
79
  export type JsonSchemaEchoAnnotations = Schema.Schema.Type<typeof JsonSchemaEchoAnnotations>;
79
80
 
80
81
  /**
@@ -257,23 +258,21 @@ const _JsonSchemaType = Schema.Struct({
257
258
  Schema.Record({
258
259
  key: Schema.String,
259
260
  value: Schema.suspend(() => JsonSchemaType),
260
- }).pipe(Schema.mutable),
261
+ }),
261
262
  ),
262
263
  patternProperties: Schema.optional(
263
264
  Schema.Record({
264
265
  key: Schema.String,
265
266
  value: Schema.suspend(() => JsonSchemaType),
266
- }).pipe(Schema.mutable),
267
+ }),
267
268
  ),
268
269
  propertyNames: Schema.optional(Schema.suspend(() => JsonSchemaType)),
269
270
 
270
271
  definitions: Schema.optional(
271
- Schema.mutable(
272
- Schema.Record({
273
- key: Schema.String,
274
- value: Schema.suspend(() => JsonSchemaType),
275
- }),
276
- ),
272
+ Schema.Record({
273
+ key: Schema.String,
274
+ value: Schema.suspend(() => JsonSchemaType),
275
+ }),
277
276
  ),
278
277
  dependencies: Schema.optional(
279
278
  Schema.Record({
@@ -296,12 +295,10 @@ const _JsonSchemaType = Schema.Struct({
296
295
  oneOf: Schema.optional(Schema.Array(Schema.suspend(() => JsonSchemaType))),
297
296
  not: Schema.optional(Schema.suspend(() => JsonSchemaType)),
298
297
  $defs: Schema.optional(
299
- Schema.mutable(
300
- Schema.Record({
301
- key: Schema.String,
302
- value: Schema.suspend(() => JsonSchemaType),
303
- }),
304
- ),
298
+ Schema.Record({
299
+ key: Schema.String,
300
+ value: Schema.suspend(() => JsonSchemaType),
301
+ }),
305
302
  ),
306
303
 
307
304
  //
@@ -311,25 +308,23 @@ const _JsonSchemaType = Schema.Struct({
311
308
  currency: Schema.optional(Schema.String),
312
309
 
313
310
  reference: Schema.optional(
314
- Schema.mutable(
315
- Schema.Struct({
316
- schema: Schema.suspend(() => JsonSchemaType),
317
- schemaVersion: Schema.optional(Schema.String),
318
- schemaObject: Schema.optional(Schema.String),
319
- }),
320
- ),
311
+ Schema.Struct({
312
+ schema: Schema.suspend(() => JsonSchemaType),
313
+ schemaVersion: Schema.optional(Schema.String),
314
+ schemaObject: Schema.optional(Schema.String),
315
+ }),
321
316
  ),
322
317
 
323
318
  /**
324
319
  * ECHO-specific annotations.
325
320
  */
326
321
  // TODO(dmaretskyi): Since we are adding a lot of new extensions to the JSON Schema, it is safer to namespace them here.
327
- annotations: Schema.optional(Schema.mutable(JsonSchemaEchoAnnotations)),
322
+ annotations: Schema.optional(JsonSchemaEchoAnnotations),
328
323
 
329
324
  /**
330
325
  * @deprecated Use `annotations` instead.
331
326
  */
332
- echo: Schema.optional(Schema.mutable(JsonSchemaEchoAnnotations)),
327
+ echo: Schema.optional(JsonSchemaEchoAnnotations),
333
328
  }).annotations({ identifier: 'jsonSchema', description: 'JSON Schema' });
334
329
 
335
330
  export const JsonSchemaFields = Object.keys(_JsonSchemaType.fields);
@@ -338,9 +333,9 @@ export const JsonSchemaFields = Object.keys(_JsonSchemaType.fields);
338
333
  * https://json-schema.org/draft-07/schema
339
334
  */
340
335
  // TODO(burdon): Reconcile with @effect/Schema/JSONSchema
341
- export interface JsonSchemaType extends Schema.Schema.Type<Schema.mutable<typeof _JsonSchemaType>> {}
336
+ export interface JsonSchemaType extends Schema.Schema.Type<typeof _JsonSchemaType> {}
342
337
 
343
- export const JsonSchemaType: Schema.Schema<JsonSchemaType> = _JsonSchemaType.pipe(Schema.mutable);
338
+ export const JsonSchemaType: Schema.Schema<JsonSchemaType> = _JsonSchemaType;
344
339
 
345
340
  // TODO(burdon): Factor out JSON schema utils.
346
341
 
@@ -349,7 +344,11 @@ export const getSchemaProperty = (schema: JsonSchemaType, property: JsonProp): J
349
344
  };
350
345
 
351
346
  // TODO(burdon): Properties should be ordered.
352
- export const setSchemaProperty = (schema: JsonSchemaType, property: JsonProp, value: JsonSchemaType) => {
347
+ export const setSchemaProperty = (
348
+ schema: Mutable<JsonSchemaType>,
349
+ property: JsonProp,
350
+ value: Mutable<JsonSchemaType>,
351
+ ) => {
353
352
  schema.properties ??= {};
354
353
  schema.properties[property] = value;
355
354
  return schema;
@@ -80,7 +80,11 @@ export type JsonSchemaOptions = {
80
80
  */
81
81
  // TODO(burdon): Reconcile with possibly extending @effect/Schema/JSONSchema
82
82
  // We add additional propertyOrder (but the object properties ARE ordered); and type "string" for literals.
83
- export const toJsonSchema = (schema: Schema.Schema.All, options: JsonSchemaOptions = {}): JsonSchemaType => {
83
+ // TODO(wittjosiah): This is mutable because its a pojo, perhaps should be left as readonly at type level though?
84
+ export const toJsonSchema = (
85
+ schema: Schema.Schema.All,
86
+ options: JsonSchemaOptions = {},
87
+ ): Types.DeepMutable<JsonSchemaType> => {
84
88
  assertArgument(Schema.isSchema(schema), 'schema');
85
89
  let jsonSchema = _toJsonSchemaAST(schema.ast);
86
90
  if (options.strict) {
@@ -103,11 +107,11 @@ export const toJsonSchema = (schema: Schema.Schema.All, options: JsonSchemaOptio
103
107
  return jsonSchema;
104
108
  };
105
109
 
106
- const _toJsonSchemaAST = (ast: SchemaAST.AST): JsonSchemaType => {
110
+ const _toJsonSchemaAST = (ast: SchemaAST.AST): Types.DeepMutable<JsonSchemaType> => {
107
111
  const withRefinements = withEchoRefinements(ast, '#');
108
112
  const jsonSchema = JSONSchema.fromAST(withRefinements, {
109
113
  definitions: {},
110
- }) as JsonSchemaType;
114
+ }) as Types.DeepMutable<JsonSchemaType>;
111
115
 
112
116
  return normalizeJsonSchema(jsonSchema);
113
117
  };
@@ -326,7 +330,7 @@ const objectToEffectSchema = (root: JsonSchemaType, defs: JsonSchemaType['$defs'
326
330
  }
327
331
 
328
332
  if (immutableIdField) {
329
- schema = Schema.extend(Schema.mutable(schema), Schema.Struct({ id: immutableIdField }));
333
+ schema = Schema.extend(schema, Schema.Struct({ id: immutableIdField }));
330
334
  }
331
335
 
332
336
  const annotations = jsonSchemaFieldsToAnnotations(root);
@@ -377,7 +381,7 @@ const refToEffectSchema = (root: any): Schema.Schema.AnyNoContext => {
377
381
  const annotations_toJsonSchemaFields = (annotations: SchemaAST.Annotations): Record<symbol, any> => {
378
382
  const schemaFields: Record<string, any> = {};
379
383
 
380
- const echoAnnotations: JsonSchemaEchoAnnotations = {};
384
+ const echoAnnotations: Types.Mutable<JsonSchemaEchoAnnotations> = {};
381
385
  for (const [key, annotationId] of Object.entries(EchoAnnotations)) {
382
386
  if (annotations[annotationId] != null) {
383
387
  echoAnnotations[key as keyof JsonSchemaEchoAnnotations] = annotations[annotationId] as any;
@@ -495,7 +499,7 @@ const addJsonSchemaFields = (ast: SchemaAST.AST, schema: JsonSchemaType): Schema
495
499
  * Fixes field order.
496
500
  * Sets `$schema` prop.
497
501
  */
498
- const normalizeJsonSchema = (jsonSchema: JsonSchemaType): JsonSchemaType => {
502
+ const normalizeJsonSchema = (jsonSchema: Types.DeepMutable<JsonSchemaType>): Types.DeepMutable<JsonSchemaType> => {
499
503
  if (jsonSchema.properties && 'id' in jsonSchema.properties) {
500
504
  jsonSchema.properties = orderKeys(jsonSchema.properties, ['id']); // Put id first.
501
505
  }
@@ -9,10 +9,11 @@ import { DXN } from '@dxos/keys';
9
9
  import * as Obj from '../../Obj';
10
10
  import { TestSchema } from '../../testing';
11
11
  import { getSchemaDXN, getSchemaTypename, getTypeDXN, getTypename } from '../annotations';
12
+ import { getMetaChecked } from '../api';
12
13
  import { RelationSourceId, RelationTargetId, getObjectDXN } from '../entities';
13
14
  import { makeObject } from '../proxy';
14
15
  import { Ref, StaticRefResolver } from '../ref';
15
- import { ATTR_TYPE, EntityKind, KindId, MetaId, TypeId, getMeta, getSchema } from '../types';
16
+ import { ATTR_TYPE, EntityKind, KindId, MetaId, TypeId, getSchema } from '../types';
16
17
 
17
18
  import { createObject } from './create-object';
18
19
  import { objectFromJSON, objectToJSON } from './json-serializer';
@@ -21,7 +22,7 @@ describe('Object JSON serializer', () => {
21
22
  test('should serialize and deserialize object', async () => {
22
23
  const contact = makeObject(TestSchema.Person, { name: 'Alice' });
23
24
  Obj.change(contact, (c) => {
24
- getMeta(c).keys.push({ id: '12345', source: 'example.com' });
25
+ getMetaChecked(c).keys.push({ id: '12345', source: 'example.com' });
25
26
  });
26
27
 
27
28
  const task = createObject(TestSchema.Task, {
@@ -94,4 +95,27 @@ describe('Object JSON serializer', () => {
94
95
  expect(getObjectDXN(contactFromJson)).toEqual(getObjectDXN(contact));
95
96
  expect(getTypeDXN(contactFromJson)).toEqual(getSchemaDXN(TestSchema.Person));
96
97
  });
98
+
99
+ test('deserializes expando without leaking internal json keys', async () => {
100
+ const expando = Obj.make(TestSchema.Expando, { message: 'local-only' });
101
+ const expandoJson = objectToJSON(expando);
102
+
103
+ const refResolver = new StaticRefResolver().addSchema(TestSchema.Expando);
104
+ const expandoFromJson = (await objectFromJSON(expandoJson, { refResolver })) as TestSchema.Expando;
105
+
106
+ expect(expandoFromJson.id).toBe(expando.id);
107
+ expect(expandoFromJson.message).toBe('local-only');
108
+ expect((expandoFromJson as any)[ATTR_TYPE]).toBeUndefined();
109
+ });
110
+
111
+ test('deserializes expando without schema resolver and without leaking internal json keys', async () => {
112
+ const expando = Obj.make(TestSchema.Expando, { message: 'local-only' });
113
+ const expandoJson = objectToJSON(expando);
114
+
115
+ const expandoFromJson = (await objectFromJSON(expandoJson)) as TestSchema.Expando;
116
+
117
+ expect(expandoFromJson.id).toBe(expando.id);
118
+ expect(expandoFromJson.message).toBe('local-only');
119
+ expect((expandoFromJson as any)[ATTR_TYPE]).toBeUndefined();
120
+ });
97
121
  });
@@ -29,12 +29,14 @@ import { attachTypedJsonSerializer, defineHiddenProperty, typedJsonSerializer }
29
29
  import { Ref, type RefResolver, refFromEncodedReference, setRefResolver } from '../ref';
30
30
  import {
31
31
  ATTR_META,
32
+ ATTR_PARENT,
32
33
  ATTR_TYPE,
33
34
  type AnyEntity,
34
35
  EntityKind,
35
36
  KindId,
36
37
  MetaId,
37
38
  ObjectMetaSchema,
39
+ ParentId,
38
40
  setSchema,
39
41
  } from '../types';
40
42
 
@@ -81,15 +83,16 @@ export const objectFromJSON = async (
81
83
  const type = DXN.parse(jsonData[ATTR_TYPE]);
82
84
  const schema = await refResolver?.resolveSchema(type);
83
85
  invariant(schema === undefined || Schema.isSchema(schema));
86
+ const decodedInput = stripInternalJsonKeys(jsonData);
84
87
 
85
88
  let obj: any;
86
89
  if (schema != null) {
87
- obj = await schema.pipe(Schema.decodeUnknownPromise)(jsonData);
90
+ obj = await schema.pipe(Schema.decodeUnknownPromise)(decodedInput);
88
91
  if (refResolver) {
89
92
  setRefResolverOnData(obj, refResolver);
90
93
  }
91
94
  } else {
92
- obj = decodeGeneric(jsonData, { refResolver });
95
+ obj = decodeGeneric(decodedInput, { refResolver });
93
96
  }
94
97
 
95
98
  invariant(ObjectId.isValid(obj.id), 'Invalid object id');
@@ -126,6 +129,12 @@ export const objectFromJSON = async (
126
129
  });
127
130
  }
128
131
 
132
+ if (jsonData[ATTR_PARENT]) {
133
+ const parentDxn = DXN.parse(jsonData[ATTR_PARENT]);
134
+ const parent = (await refResolver?.resolve(parentDxn)) as Obj.Unknown | undefined;
135
+ defineHiddenProperty(obj, ParentId, parent);
136
+ }
137
+
129
138
  if (dxn) {
130
139
  defineHiddenProperty(obj, SelfDXNId, dxn);
131
140
  }
@@ -141,6 +150,18 @@ export const objectFromJSON = async (
141
150
  };
142
151
 
143
152
  const decodeGeneric = (jsonData: unknown, options: { refResolver?: RefResolver }) => {
153
+ const props = stripInternalJsonKeys(jsonData);
154
+
155
+ return deepMapValues(props, (value, visitor) => {
156
+ if (isEncodedReference(value)) {
157
+ return refFromEncodedReference(value, options.refResolver);
158
+ }
159
+
160
+ return visitor(value);
161
+ });
162
+ };
163
+
164
+ const stripInternalJsonKeys = (jsonData: unknown) => {
144
165
  const {
145
166
  [ATTR_TYPE]: _type,
146
167
  [ATTR_META]: _meta,
@@ -151,13 +172,7 @@ const decodeGeneric = (jsonData: unknown, options: { refResolver?: RefResolver }
151
172
  ...props
152
173
  } = jsonData as any;
153
174
 
154
- return deepMapValues(props, (value, visitor) => {
155
- if (isEncodedReference(value)) {
156
- return refFromEncodedReference(value, options.refResolver);
157
- }
158
-
159
- return visitor(value);
160
- });
175
+ return props;
161
176
  };
162
177
 
163
178
  export const setRefResolverOnData = (obj: AnyEntity, refResolver: RefResolver) => {
@@ -182,6 +197,7 @@ export const objectStructureToJson = (objectId: string, structure: ObjectStructu
182
197
  id: objectId,
183
198
  [ATTR_TYPE]: (ObjectStructure.getTypeReference(structure)?.['/'] ?? '') as DXN.String,
184
199
  [ATTR_DELETED]: ObjectStructure.isDeleted(structure),
200
+ [ATTR_PARENT]: ObjectStructure.getParent(structure)?.['/'] as DXN.String | undefined,
185
201
  [ATTR_RELATION_SOURCE]: ObjectStructure.getRelationSource(structure)?.['/'] as DXN.String | undefined,
186
202
  [ATTR_RELATION_TARGET]: ObjectStructure.getRelationTarget(structure)?.['/'] as DXN.String | undefined,
187
203
  };
@@ -27,9 +27,7 @@ describe('schema-validator', () => {
27
27
  const TestSchema: Schema.Schema.AnyNoContext = Schema.Struct({
28
28
  name: Schema.String.annotations({ [annotationId]: annotationValue }),
29
29
  parent: Schema.optional(Schema.suspend(() => TestSchema.annotations({ [annotationId]: annotationValue }))),
30
- friends: Schema.suspend(() =>
31
- Schema.mutable(Schema.Array(TestSchema.annotations({ [annotationId]: annotationValue }))),
32
- ),
30
+ friends: Schema.suspend(() => Schema.Array(TestSchema.annotations({ [annotationId]: annotationValue }))),
33
31
  });
34
32
  expect(SchemaValidator.hasTypeAnnotation(TestSchema, 'name', annotationId)).to.be.true;
35
33
  expect(SchemaValidator.hasTypeAnnotation(TestSchema, 'parent', annotationId)).to.be.true;
@@ -41,7 +39,7 @@ describe('schema-validator', () => {
41
39
  const Person: Schema.Schema.AnyNoContext = Schema.Struct({
42
40
  name: Schema.String,
43
41
  parent: Schema.optional(Schema.suspend(() => Person)),
44
- friends: Schema.suspend(() => Schema.mutable(Schema.Array(Person))),
42
+ friends: Schema.suspend(() => Schema.Array(Person)),
45
43
  });
46
44
  expect(SchemaValidator.hasTypeAnnotation(Person, 'name', annotationId)).to.be.false;
47
45
  expect(SchemaValidator.hasTypeAnnotation(Person, 'parent', annotationId)).to.be.false;
@@ -87,9 +85,7 @@ describe('schema-validator', () => {
87
85
  const annotationValue = 'bar';
88
86
  const Person: Schema.Schema.AnyNoContext = Schema.Struct({
89
87
  parent: Schema.optional(Schema.suspend(() => Person.annotations({ [annotationId]: annotationValue }))),
90
- friends: Schema.suspend(() =>
91
- Schema.mutable(Schema.Array(Person.annotations({ [annotationId]: annotationValue }))),
92
- ),
88
+ friends: Schema.suspend(() => Schema.Array(Person.annotations({ [annotationId]: annotationValue }))),
93
89
  });
94
90
  expect(SchemaValidator.getPropertySchema(Person, ['parent']).ast.annotations[annotationId]).to.eq(
95
91
  annotationValue,
@@ -55,11 +55,11 @@ describe('Obj.setValue', () => {
55
55
  test('sets multiple nested array elements', ({ expect }) => {
56
56
  const Item = Schema.Struct({
57
57
  value: Schema.optional(Schema.Number),
58
- }).pipe(Schema.mutable);
58
+ });
59
59
 
60
60
  const Container = Schema.Struct({
61
61
  name: Schema.String,
62
- items: Schema.optional(Schema.mutable(Schema.Array(Item))),
62
+ items: Schema.optional(Schema.Array(Item)),
63
63
  }).pipe(
64
64
  Type.object({
65
65
  typename: 'test.com/Container',
@@ -150,7 +150,7 @@ describe('Obj.setValue', () => {
150
150
 
151
151
  test('handles two-dimensional arrays', ({ expect }) => {
152
152
  const Matrix = Schema.Struct({
153
- values: Schema.optional(Schema.mutable(Schema.Array(Schema.mutable(Schema.Array(Schema.Number))))),
153
+ values: Schema.optional(Schema.Array(Schema.Array(Schema.Number))),
154
154
  }).pipe(
155
155
  Type.object({
156
156
  typename: 'test.com/Matrix',
@@ -200,11 +200,11 @@ describe('Obj.setValue', () => {
200
200
  test('handles numeric keys as strings', ({ expect }) => {
201
201
  const Item = Schema.Struct({
202
202
  value: Schema.optional(Schema.Number),
203
- }).pipe(Schema.mutable);
203
+ });
204
204
 
205
205
  const Container = Schema.Struct({
206
206
  name: Schema.String,
207
- items: Schema.optional(Schema.mutable(Schema.Array(Item))),
207
+ items: Schema.optional(Schema.Array(Item)),
208
208
  }).pipe(
209
209
  Type.object({
210
210
  typename: 'test.com/Container',
@@ -227,11 +227,11 @@ describe('Obj.setValue', () => {
227
227
  id: Schema.String, // Required field.
228
228
  title: Schema.optional(Schema.String),
229
229
  completed: Schema.optional(Schema.Boolean),
230
- }).pipe(Schema.mutable);
230
+ });
231
231
 
232
232
  const TodoList = Schema.Struct({
233
233
  name: Schema.String,
234
- tasks: Schema.optional(Schema.mutable(Schema.Array(Task))),
234
+ tasks: Schema.optional(Schema.Array(Task)),
235
235
  }).pipe(
236
236
  Type.object({
237
237
  typename: 'test.com/TodoList',
@@ -256,11 +256,11 @@ describe('Obj.setValue', () => {
256
256
  count: Schema.Number, // Required - should default to 0
257
257
  active: Schema.Boolean, // Required - should default to false
258
258
  label: Schema.optional(Schema.String), // Optional - should not be initialized
259
- }).pipe(Schema.mutable);
259
+ });
260
260
 
261
261
  const Container = Schema.Struct({
262
262
  name: Schema.String,
263
- items: Schema.optional(Schema.mutable(Schema.Array(Item))),
263
+ items: Schema.optional(Schema.Array(Item)),
264
264
  }).pipe(
265
265
  Type.object({
266
266
  typename: 'test.com/Container',
@@ -4,18 +4,35 @@
4
4
 
5
5
  import { deepMapValues } from '@dxos/util';
6
6
 
7
+ import { ObjectDatabaseId } from '../entities/model';
7
8
  import { RelationSourceDXNId, RelationSourceId, RelationTargetDXNId, RelationTargetId } from '../entities/relation';
8
9
  import { KindId, SnapshotKindId } from '../types/entity';
9
10
  import { MetaId } from '../types/meta';
10
11
  import { SchemaId, TypeId } from '../types/typename';
11
12
 
12
13
  /**
13
- * Copy a Symbol-keyed property from source to target if it exists.
14
+ * Copy a Symbol-keyed property from source to target if it has a defined value.
15
+ * Handles both plain objects (symbol in source) and proxies where get returns a value
16
+ * but has/in returns false (e.g. ObjectDatabaseId on echo-db proxies).
17
+ * Optional transform clones or mutates the value before assignment (e.g. for MetaId).
14
18
  */
15
- const copySymbolProperty = (source: any, target: any, symbol: symbol): void => {
16
- if (symbol in source) {
19
+ const copySymbolProperty = (
20
+ source: any,
21
+ target: any,
22
+ symbol: symbol,
23
+ transform?: (value: unknown) => unknown,
24
+ ): void => {
25
+ let value: unknown;
26
+ // Echo-db proxy getters (e.g. RelationSourceId, RelationTargetId) can throw in certain states.
27
+ try {
28
+ value = source[symbol];
29
+ } catch {
30
+ return;
31
+ }
32
+ if (value !== undefined) {
33
+ const finalValue = transform ? transform(value) : value;
17
34
  Object.defineProperty(target, symbol, {
18
- value: source[symbol],
35
+ value: finalValue,
19
36
  writable: false,
20
37
  enumerable: false,
21
38
  configurable: false,
@@ -56,8 +73,14 @@ export const getSnapshot = <T extends object>(obj: T): T => {
56
73
  copySymbolProperty(source, snapshot, TypeId);
57
74
  copySymbolProperty(source, snapshot, SchemaId);
58
75
 
59
- // Metadata symbol.
60
- copySymbolProperty(source, snapshot, MetaId);
76
+ // Database reference (required for Obj.getDatabase to work on snapshots).
77
+ copySymbolProperty(source, snapshot, ObjectDatabaseId);
78
+
79
+ // Metadata symbol. Copy arrays so the snapshot is not affected by mutations to the live meta's keys/tags.
80
+ copySymbolProperty(source, snapshot, MetaId, (meta: any) => ({
81
+ keys: [...(meta?.keys ?? [])],
82
+ tags: [...(meta?.tags ?? [])],
83
+ }));
61
84
 
62
85
  // Relation endpoint symbols.
63
86
  copySymbolProperty(source, snapshot, RelationSourceDXNId);
@@ -7,7 +7,7 @@ import type * as Schema from 'effect/Schema';
7
7
  import { ObjectId } from '@dxos/keys';
8
8
 
9
9
  import { getTypeAnnotation } from '../annotations';
10
- import { type AnyProperties, KindId, MetaId, type ObjectMeta, ObjectMetaSchema } from '../types';
10
+ import { type AnyProperties, KindId, MetaId, type ObjectMeta, ObjectMetaSchema, ParentId } from '../types';
11
11
 
12
12
  import { defineHiddenProperty } from './define-hidden-property';
13
13
  import { attachTypedJsonSerializer } from './json-serializer';
@@ -35,7 +35,8 @@ export const makeObject: {
35
35
  meta?: ObjectMeta,
36
36
  ): T;
37
37
  } = <T extends AnyProperties>(schema: Schema.Schema<T, any>, obj: MakeObjectProps<T>, meta?: ObjectMeta): T => {
38
- return createReactiveObject<T>({ ...obj } as T, meta, schema);
38
+ // Use Object.assign to copy symbol properties (like ParentId) that spread operator doesn't copy.
39
+ return createReactiveObject<T>(Object.assign({}, obj) as T, meta, schema);
39
40
  };
40
41
 
41
42
  const createReactiveObject = <T extends AnyProperties>(obj: T, meta?: ObjectMeta, schema?: Schema.Schema<T>): T => {
@@ -47,12 +48,21 @@ const createReactiveObject = <T extends AnyProperties>(obj: T, meta?: ObjectMeta
47
48
  throw new Error('Schema is required for reactive objects. Use Atom for untyped reactive state.');
48
49
  }
49
50
 
51
+ // Extract parent from props (can be set via [Obj.Parent]).
52
+ const parent = (obj as any)[ParentId];
53
+ if (parent !== undefined) {
54
+ delete (obj as any)[ParentId];
55
+ }
56
+
50
57
  const annotation = getTypeAnnotation(schema);
51
58
  if (annotation) {
52
59
  setIdOnTarget(obj);
53
60
  defineHiddenProperty(obj, KindId, annotation.kind);
54
61
  }
55
62
  initMeta(obj, meta);
63
+ if (parent !== undefined) {
64
+ defineHiddenProperty(obj, ParentId, parent);
65
+ }
56
66
  prepareTypedTarget(obj, schema);
57
67
  attachTypedJsonSerializer(obj);
58
68
  const proxy = createProxy<T>(obj, TypedReactiveHandler.instance);
@@ -94,7 +94,7 @@ describe('complex schema validations', () => {
94
94
  });
95
95
 
96
96
  test('subscribe', () => {
97
- const TestSchema = Schema.mutable(Schema.Struct({ field: Schema.String })).pipe(
97
+ const TestSchema = Schema.Struct({ field: Schema.String }).pipe(
98
98
  EchoObjectSchema({ typename: 'Test', version: '0.1.0' }),
99
99
  );
100
100
  const object = makeObject(TestSchema, { field: 'value' });
@@ -117,12 +117,10 @@ describe('complex schema validations', () => {
117
117
  });
118
118
 
119
119
  describe('object structure restrictions', () => {
120
- const NestedSchema = Schema.mutable(
121
- Schema.Struct({
122
- data: Schema.optional(Schema.Any),
123
- nested: Schema.optional(Schema.Any),
124
- }),
125
- );
120
+ const NestedSchema = Schema.Struct({
121
+ data: Schema.optional(Schema.Any),
122
+ nested: Schema.optional(Schema.Any),
123
+ });
126
124
 
127
125
  test('prevents direct cycles', () => {
128
126
  const obj = makeObject(NestedSchema, { data: null });
@@ -226,11 +224,9 @@ describe('object structure restrictions', () => {
226
224
  });
227
225
 
228
226
  test('array elements are owned by root ECHO object', () => {
229
- const ArraySchema = Schema.mutable(
230
- Schema.Struct({
231
- items: Schema.mutable(Schema.Array(Schema.Any)),
232
- }),
233
- );
227
+ const ArraySchema = Schema.Struct({
228
+ items: Schema.Array(Schema.Any),
229
+ });
234
230
 
235
231
  const obj1 = makeObject(ArraySchema, {
236
232
  items: [{ id: 1 }, { id: 2 }, { id: 3 }],
@@ -13,7 +13,7 @@ import { invariant } from '@dxos/invariant';
13
13
 
14
14
  import { getSchemaDXN } from '../annotations';
15
15
  import { ObjectDeletedId } from '../entities';
16
- import { SchemaId, TypeId } from '../types';
16
+ import { ParentId, SchemaId, TypeId } from '../types';
17
17
 
18
18
  import { executeChange, isInChangeContext, queueNotification } from './change-context';
19
19
  import { defineHiddenProperty } from './define-hidden-property';
@@ -47,6 +47,7 @@ type ProxyTarget = {
47
47
  * Schema for the root.
48
48
  */
49
49
  [SchemaId]: Schema.Schema.AnyNoContext;
50
+ [ParentId]?: any;
50
51
 
51
52
  /**
52
53
  * For modifications.
@@ -320,6 +321,10 @@ export class TypedReactiveHandler implements ReactiveHandler<ProxyTarget> {
320
321
  ): { echoRoot: object; preparedValue: any } {
321
322
  const echoRoot = getEchoRoot(target);
322
323
 
324
+ if (prop === ParentId) {
325
+ return { echoRoot, preparedValue: value }; // Short-circuit for parent assignment.
326
+ }
327
+
323
328
  // Check for cycles before assignment.
324
329
  if (isValidProxyTarget(value) || isProxy(value)) {
325
330
  if (wouldCreateCycle(echoRoot, value)) {
@@ -363,6 +368,9 @@ export class TypedReactiveHandler implements ReactiveHandler<ProxyTarget> {
363
368
  }
364
369
 
365
370
  private _validateValue(target: any, prop: string | symbol, value: any) {
371
+ if (prop === ParentId) {
372
+ return value;
373
+ }
366
374
  const schema = SchemaValidator.getTargetPropertySchema(target, prop);
367
375
  const _ = Schema.asserts(schema)(value);
368
376
  if (isValidProxyTarget(value)) {
@@ -3,6 +3,7 @@
3
3
  //
4
4
 
5
5
  import * as Schema from 'effect/Schema';
6
+ import type * as Types from 'effect/Types';
6
7
  import { describe, expect, test } from 'vitest';
7
8
 
8
9
  import { EchoObjectSchema } from '../entities';
@@ -61,15 +62,13 @@ describe('EchoObjectSchema class DSL', () => {
61
62
  });
62
63
 
63
64
  test('record', () => {
64
- const schema = Schema.mutable(
65
- Schema.Struct({
66
- meta: Schema.optional(Schema.mutable(Schema.Any)),
67
- // NOTE: Schema.Record only supports shallow values.
68
- // https://www.npmjs.com/package/@effect/schema#mutable-records
69
- // meta: Schema.optional(Schema.mutable(Schema.Record({ key: Schema.String, value: Schema.Any }))),
70
- // meta: Schema.optional(Schema.mutable(Schema.object)),
71
- }),
72
- );
65
+ const schema = Schema.Struct({
66
+ meta: Schema.optional(Schema.Any),
67
+ // NOTE: Schema.Record only supports shallow values.
68
+ // https://www.npmjs.com/package/@effect/schema#mutable-records
69
+ // meta: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Any })),
70
+ // meta: Schema.optional(Schema.object),
71
+ });
73
72
 
74
73
  {
75
74
  const object = makeObject(schema, {});
@@ -89,7 +88,8 @@ describe('EchoObjectSchema class DSL', () => {
89
88
 
90
89
  {
91
90
  // Plain object (not a reactive proxy) - doesn't need Obj.change.
92
- type Test1 = Schema.Schema.Type<typeof schema>;
91
+ // Note: Schema.Schema.Type generates readonly types, so we cast to mutable for plain objects.
92
+ type Test1 = Types.Mutable<Schema.Schema.Type<typeof schema>>;
93
93
 
94
94
  const object: Test1 = {};
95
95
  (object.meta ??= {}).test = 100;
@@ -98,7 +98,7 @@ describe('EchoObjectSchema class DSL', () => {
98
98
 
99
99
  {
100
100
  const Test2 = Schema.Struct({
101
- meta: Schema.optional(Schema.mutable(Schema.Record({ key: Schema.String, value: Schema.Any }))),
101
+ meta: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Any })),
102
102
  }).pipe(
103
103
  EchoObjectSchema({
104
104
  typename: 'dxos.org/type/FunctionTrigger',