@dxos/echo 0.8.4-main.e098934 → 0.8.4-main.ead640a

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 (308) hide show
  1. package/dist/lib/browser/chunk-HKFCK2GL.mjs +175 -0
  2. package/dist/lib/browser/chunk-HKFCK2GL.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-EIXXFUN5.mjs → chunk-MAAYELT7.mjs} +477 -486
  4. package/dist/lib/browser/chunk-MAAYELT7.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-MB6MMNFP.mjs +3857 -0
  6. package/dist/lib/browser/chunk-MB6MMNFP.mjs.map +7 -0
  7. package/dist/lib/browser/index.mjs +12 -7
  8. package/dist/lib/browser/internal/index.mjs +332 -0
  9. package/dist/lib/browser/internal/index.mjs.map +7 -0
  10. package/dist/lib/browser/meta.json +1 -1
  11. package/dist/lib/browser/query/index.mjs +15 -0
  12. package/dist/lib/browser/query/index.mjs.map +7 -0
  13. package/dist/lib/browser/testing/index.mjs +260 -32
  14. package/dist/lib/browser/testing/index.mjs.map +4 -4
  15. package/dist/lib/node-esm/{chunk-TCY7IVTS.mjs → chunk-5NWDGIBT.mjs} +477 -486
  16. package/dist/lib/node-esm/chunk-5NWDGIBT.mjs.map +7 -0
  17. package/dist/lib/node-esm/chunk-AUAH4E2J.mjs +175 -0
  18. package/dist/lib/node-esm/chunk-AUAH4E2J.mjs.map +7 -0
  19. package/dist/lib/node-esm/chunk-BQRA4VLX.mjs +3857 -0
  20. package/dist/lib/node-esm/chunk-BQRA4VLX.mjs.map +7 -0
  21. package/dist/lib/node-esm/index.mjs +12 -7
  22. package/dist/lib/node-esm/internal/index.mjs +332 -0
  23. package/dist/lib/node-esm/internal/index.mjs.map +7 -0
  24. package/dist/lib/node-esm/meta.json +1 -1
  25. package/dist/lib/node-esm/query/index.mjs +15 -0
  26. package/dist/lib/node-esm/query/index.mjs.map +7 -0
  27. package/dist/lib/node-esm/testing/index.mjs +260 -32
  28. package/dist/lib/node-esm/testing/index.mjs.map +4 -4
  29. package/dist/types/src/Obj.d.ts +22 -5
  30. package/dist/types/src/Obj.d.ts.map +1 -1
  31. package/dist/types/src/Ref.d.ts +1 -1
  32. package/dist/types/src/Ref.d.ts.map +1 -1
  33. package/dist/types/src/Relation.d.ts +5 -4
  34. package/dist/types/src/Relation.d.ts.map +1 -1
  35. package/dist/types/src/Type.d.ts +16 -17
  36. package/dist/types/src/Type.d.ts.map +1 -1
  37. package/dist/types/src/errors.d.ts +8 -8
  38. package/dist/types/src/index.d.ts +2 -2
  39. package/dist/types/src/index.d.ts.map +1 -1
  40. package/dist/types/src/internal/ast/annotation-helper.d.ts +8 -0
  41. package/dist/types/src/internal/ast/annotation-helper.d.ts.map +1 -0
  42. package/dist/types/src/internal/ast/annotations.d.ts +125 -0
  43. package/dist/types/src/internal/ast/annotations.d.ts.map +1 -0
  44. package/dist/types/src/internal/ast/annotations.test.d.ts +2 -0
  45. package/dist/types/src/internal/ast/annotations.test.d.ts.map +1 -0
  46. package/dist/types/src/internal/ast/entity-kind.d.ts +10 -0
  47. package/dist/types/src/internal/ast/entity-kind.d.ts.map +1 -0
  48. package/dist/types/src/internal/ast/index.d.ts +5 -0
  49. package/dist/types/src/internal/ast/index.d.ts.map +1 -0
  50. package/dist/types/src/internal/ast/types.d.ts +6 -0
  51. package/dist/types/src/internal/ast/types.d.ts.map +1 -0
  52. package/dist/types/src/internal/formats/date.d.ts +63 -0
  53. package/dist/types/src/internal/formats/date.d.ts.map +1 -0
  54. package/dist/types/src/internal/formats/date.test.d.ts +2 -0
  55. package/dist/types/src/internal/formats/date.test.d.ts.map +1 -0
  56. package/dist/types/src/internal/formats/format.d.ts +30 -0
  57. package/dist/types/src/internal/formats/format.d.ts.map +1 -0
  58. package/dist/types/src/internal/formats/format.test.d.ts +2 -0
  59. package/dist/types/src/internal/formats/format.test.d.ts.map +1 -0
  60. package/dist/types/src/internal/formats/index.d.ts +8 -0
  61. package/dist/types/src/internal/formats/index.d.ts.map +1 -0
  62. package/dist/types/src/internal/formats/number.d.ts +31 -0
  63. package/dist/types/src/internal/formats/number.d.ts.map +1 -0
  64. package/dist/types/src/internal/formats/object.d.ts +35 -0
  65. package/dist/types/src/internal/formats/object.d.ts.map +1 -0
  66. package/dist/types/src/internal/formats/select.d.ts +11 -0
  67. package/dist/types/src/internal/formats/select.d.ts.map +1 -0
  68. package/dist/types/src/internal/formats/string.d.ts +38 -0
  69. package/dist/types/src/internal/formats/string.d.ts.map +1 -0
  70. package/dist/types/src/internal/formats/types.d.ts +68 -0
  71. package/dist/types/src/internal/formats/types.d.ts.map +1 -0
  72. package/dist/types/src/internal/index.d.ts +15 -0
  73. package/dist/types/src/internal/index.d.ts.map +1 -0
  74. package/dist/types/src/internal/json/annotations.d.ts +19 -0
  75. package/dist/types/src/internal/json/annotations.d.ts.map +1 -0
  76. package/dist/types/src/internal/json/effect-schema.test.d.ts +2 -0
  77. package/dist/types/src/internal/json/effect-schema.test.d.ts.map +1 -0
  78. package/dist/types/src/internal/json/index.d.ts +2 -0
  79. package/dist/types/src/internal/json/index.d.ts.map +1 -0
  80. package/dist/types/src/internal/json/json-schema.d.ts +28 -0
  81. package/dist/types/src/internal/json/json-schema.d.ts.map +1 -0
  82. package/dist/types/src/internal/json/json-schema.test.d.ts +2 -0
  83. package/dist/types/src/internal/json/json-schema.test.d.ts.map +1 -0
  84. package/dist/types/src/internal/json-schema/index.d.ts +3 -0
  85. package/dist/types/src/internal/json-schema/index.d.ts.map +1 -0
  86. package/dist/types/src/internal/json-schema/json-schema-normalize.d.ts +7 -0
  87. package/dist/types/src/internal/json-schema/json-schema-normalize.d.ts.map +1 -0
  88. package/dist/types/src/internal/json-schema/json-schema-type.d.ts +250 -0
  89. package/dist/types/src/internal/json-schema/json-schema-type.d.ts.map +1 -0
  90. package/dist/types/src/internal/object/accessors.d.ts +37 -0
  91. package/dist/types/src/internal/object/accessors.d.ts.map +1 -0
  92. package/dist/types/src/internal/object/common.d.ts +18 -0
  93. package/dist/types/src/internal/object/common.d.ts.map +1 -0
  94. package/dist/types/src/internal/object/create.d.ts +40 -0
  95. package/dist/types/src/internal/object/create.d.ts.map +1 -0
  96. package/dist/types/src/internal/object/create.test.d.ts +2 -0
  97. package/dist/types/src/internal/object/create.test.d.ts.map +1 -0
  98. package/dist/types/src/internal/object/deleted.d.ts +6 -0
  99. package/dist/types/src/internal/object/deleted.d.ts.map +1 -0
  100. package/dist/types/src/internal/object/entity.d.ts +33 -0
  101. package/dist/types/src/internal/object/entity.d.ts.map +1 -0
  102. package/dist/types/src/internal/object/expando.d.ts +14 -0
  103. package/dist/types/src/internal/object/expando.d.ts.map +1 -0
  104. package/dist/types/src/internal/object/ids.d.ts +6 -0
  105. package/dist/types/src/internal/object/ids.d.ts.map +1 -0
  106. package/dist/types/src/internal/object/index.d.ts +16 -0
  107. package/dist/types/src/internal/object/index.d.ts.map +1 -0
  108. package/dist/types/src/internal/object/inspect.d.ts +2 -0
  109. package/dist/types/src/internal/object/inspect.d.ts.map +1 -0
  110. package/dist/types/src/internal/object/json-serializer.d.ts +32 -0
  111. package/dist/types/src/internal/object/json-serializer.d.ts.map +1 -0
  112. package/dist/types/src/internal/object/json-serializer.test.d.ts +2 -0
  113. package/dist/types/src/internal/object/json-serializer.test.d.ts.map +1 -0
  114. package/dist/types/src/internal/object/meta.d.ts +31 -0
  115. package/dist/types/src/internal/object/meta.d.ts.map +1 -0
  116. package/dist/types/src/internal/object/model.d.ts +117 -0
  117. package/dist/types/src/internal/object/model.d.ts.map +1 -0
  118. package/dist/types/src/internal/object/relation.d.ts +17 -0
  119. package/dist/types/src/internal/object/relation.d.ts.map +1 -0
  120. package/dist/types/src/internal/object/schema-validator.d.ts +15 -0
  121. package/dist/types/src/internal/object/schema-validator.d.ts.map +1 -0
  122. package/dist/types/src/internal/object/schema-validator.test.d.ts +2 -0
  123. package/dist/types/src/internal/object/schema-validator.test.d.ts.map +1 -0
  124. package/dist/types/src/internal/object/typed-object.d.ts +31 -0
  125. package/dist/types/src/internal/object/typed-object.d.ts.map +1 -0
  126. package/dist/types/src/internal/object/typed-object.test.d.ts +2 -0
  127. package/dist/types/src/internal/object/typed-object.test.d.ts.map +1 -0
  128. package/dist/types/src/internal/object/typename.d.ts +15 -0
  129. package/dist/types/src/internal/object/typename.d.ts.map +1 -0
  130. package/dist/types/src/internal/object/version.d.ts +14 -0
  131. package/dist/types/src/internal/object/version.d.ts.map +1 -0
  132. package/dist/types/src/internal/projection/compose.d.ts +6 -0
  133. package/dist/types/src/internal/projection/compose.d.ts.map +1 -0
  134. package/dist/types/src/internal/projection/compose.test.d.ts +2 -0
  135. package/dist/types/src/internal/projection/compose.test.d.ts.map +1 -0
  136. package/dist/types/src/internal/projection/index.d.ts +2 -0
  137. package/dist/types/src/internal/projection/index.d.ts.map +1 -0
  138. package/dist/types/src/internal/proxy/handler.test.d.ts +2 -0
  139. package/dist/types/src/internal/proxy/handler.test.d.ts.map +1 -0
  140. package/dist/types/src/internal/proxy/reactive-object.d.ts +15 -0
  141. package/dist/types/src/internal/proxy/reactive-object.d.ts.map +1 -0
  142. package/dist/types/src/internal/proxy/schema.test.d.ts +2 -0
  143. package/dist/types/src/internal/proxy/schema.test.d.ts.map +1 -0
  144. package/dist/types/src/internal/proxy/typed-handler.d.ts +44 -0
  145. package/dist/types/src/internal/proxy/typed-handler.d.ts.map +1 -0
  146. package/dist/types/src/internal/proxy/typed-handler.test.d.ts +2 -0
  147. package/dist/types/src/internal/proxy/typed-handler.test.d.ts.map +1 -0
  148. package/dist/types/src/internal/proxy/typed-object.test.d.ts +2 -0
  149. package/dist/types/src/internal/proxy/typed-object.test.d.ts.map +1 -0
  150. package/dist/types/src/internal/query/index.d.ts +2 -0
  151. package/dist/types/src/internal/query/index.d.ts.map +1 -0
  152. package/dist/types/src/internal/query/query.d.ts +17 -0
  153. package/dist/types/src/internal/query/query.d.ts.map +1 -0
  154. package/dist/types/src/internal/ref/index.d.ts +3 -0
  155. package/dist/types/src/internal/ref/index.d.ts.map +1 -0
  156. package/dist/types/src/internal/ref/ref-array.d.ts +21 -0
  157. package/dist/types/src/internal/ref/ref-array.d.ts.map +1 -0
  158. package/dist/types/src/internal/ref/ref.d.ts +206 -0
  159. package/dist/types/src/internal/ref/ref.d.ts.map +1 -0
  160. package/dist/types/src/internal/ref/ref.test.d.ts +2 -0
  161. package/dist/types/src/internal/ref/ref.test.d.ts.map +1 -0
  162. package/dist/types/src/internal/schema/echo-schema.d.ts +168 -0
  163. package/dist/types/src/internal/schema/echo-schema.d.ts.map +1 -0
  164. package/dist/types/src/internal/schema/index.d.ts +7 -0
  165. package/dist/types/src/internal/schema/index.d.ts.map +1 -0
  166. package/dist/types/src/internal/schema/manipulation.d.ts +10 -0
  167. package/dist/types/src/internal/schema/manipulation.d.ts.map +1 -0
  168. package/dist/types/src/internal/schema/runtime-schema-registry.d.ts +18 -0
  169. package/dist/types/src/internal/schema/runtime-schema-registry.d.ts.map +1 -0
  170. package/dist/types/src/internal/schema/snapshot.d.ts +6 -0
  171. package/dist/types/src/internal/schema/snapshot.d.ts.map +1 -0
  172. package/dist/types/src/internal/schema/stored-schema.d.ts +13 -0
  173. package/dist/types/src/internal/schema/stored-schema.d.ts.map +1 -0
  174. package/dist/types/src/internal/testing/index.d.ts +3 -0
  175. package/dist/types/src/internal/testing/index.d.ts.map +1 -0
  176. package/dist/types/src/internal/testing/types.d.ts +455 -0
  177. package/dist/types/src/internal/testing/types.d.ts.map +1 -0
  178. package/dist/types/src/internal/testing/utils.d.ts +10 -0
  179. package/dist/types/src/internal/testing/utils.d.ts.map +1 -0
  180. package/dist/types/src/internal/types/index.d.ts +3 -0
  181. package/dist/types/src/internal/types/index.d.ts.map +1 -0
  182. package/dist/types/src/internal/types/types.d.ts +79 -0
  183. package/dist/types/src/internal/types/types.d.ts.map +1 -0
  184. package/dist/types/src/internal/types/types.test.d.ts +2 -0
  185. package/dist/types/src/internal/types/types.test.d.ts.map +1 -0
  186. package/dist/types/src/internal/types/util.d.ts +5 -0
  187. package/dist/types/src/internal/types/util.d.ts.map +1 -0
  188. package/dist/types/src/query/index.d.ts +2 -1
  189. package/dist/types/src/query/index.d.ts.map +1 -1
  190. package/dist/types/src/query/{dsl.d.ts → query.d.ts} +28 -23
  191. package/dist/types/src/query/query.d.ts.map +1 -0
  192. package/dist/types/src/query/query.test.d.ts +2 -0
  193. package/dist/types/src/query/query.test.d.ts.map +1 -0
  194. package/dist/types/src/query/tag.d.ts +17 -0
  195. package/dist/types/src/query/tag.d.ts.map +1 -0
  196. package/dist/types/src/testing/echo-schema.d.ts +7 -0
  197. package/dist/types/src/testing/echo-schema.d.ts.map +1 -0
  198. package/dist/types/src/testing/index.d.ts +2 -0
  199. package/dist/types/src/testing/index.d.ts.map +1 -1
  200. package/dist/types/src/testing/types.d.ts +18 -18
  201. package/dist/types/src/testing/types.d.ts.map +1 -1
  202. package/dist/types/tsconfig.tsbuildinfo +1 -1
  203. package/package.json +29 -18
  204. package/src/Obj.ts +77 -12
  205. package/src/Ref.ts +1 -2
  206. package/src/Relation.ts +15 -5
  207. package/src/Type.ts +29 -30
  208. package/src/index.ts +2 -2
  209. package/src/internal/ast/annotation-helper.ts +22 -0
  210. package/src/internal/ast/annotations.test.ts +98 -0
  211. package/src/internal/ast/annotations.ts +219 -0
  212. package/src/internal/ast/entity-kind.ts +15 -0
  213. package/src/internal/ast/index.ts +8 -0
  214. package/src/internal/ast/types.ts +17 -0
  215. package/src/internal/formats/date.test.ts +56 -0
  216. package/src/internal/formats/date.ts +217 -0
  217. package/src/internal/formats/format.test.ts +77 -0
  218. package/src/internal/formats/format.ts +52 -0
  219. package/src/internal/formats/index.ts +12 -0
  220. package/src/internal/formats/number.ts +89 -0
  221. package/src/internal/formats/object.ts +80 -0
  222. package/src/internal/formats/select.ts +16 -0
  223. package/src/internal/formats/string.ts +76 -0
  224. package/src/internal/formats/types.ts +175 -0
  225. package/src/internal/index.ts +22 -0
  226. package/src/internal/json/annotations.ts +50 -0
  227. package/src/internal/json/effect-schema.test.ts +143 -0
  228. package/src/internal/json/index.ts +5 -0
  229. package/src/internal/json/json-schema.test.ts +857 -0
  230. package/src/internal/json/json-schema.ts +519 -0
  231. package/src/internal/json-schema/index.ts +6 -0
  232. package/src/internal/json-schema/json-schema-normalize.ts +109 -0
  233. package/src/internal/json-schema/json-schema-type.ts +403 -0
  234. package/src/internal/object/accessors.ts +153 -0
  235. package/src/internal/object/common.ts +76 -0
  236. package/src/internal/object/create.test.ts +118 -0
  237. package/src/internal/object/create.ts +96 -0
  238. package/src/internal/object/deleted.ts +19 -0
  239. package/src/internal/object/entity.ts +248 -0
  240. package/src/internal/object/expando.ts +21 -0
  241. package/src/internal/object/ids.ts +12 -0
  242. package/src/internal/object/index.ts +19 -0
  243. package/src/internal/object/inspect.ts +48 -0
  244. package/src/internal/object/json-serializer.test.ts +99 -0
  245. package/src/internal/object/json-serializer.ts +225 -0
  246. package/src/internal/object/meta.ts +61 -0
  247. package/src/internal/object/model.ts +170 -0
  248. package/src/internal/object/relation.ts +24 -0
  249. package/src/internal/object/schema-validator.test.ts +186 -0
  250. package/src/internal/object/schema-validator.ts +241 -0
  251. package/src/internal/object/typed-object.test.ts +34 -0
  252. package/src/internal/object/typed-object.ts +88 -0
  253. package/src/internal/object/typename.ts +61 -0
  254. package/src/internal/object/version.ts +22 -0
  255. package/src/internal/projection/compose.test.ts +43 -0
  256. package/src/internal/projection/compose.ts +36 -0
  257. package/src/internal/projection/index.ts +5 -0
  258. package/src/internal/proxy/handler.test.ts +163 -0
  259. package/src/internal/proxy/reactive-object.ts +108 -0
  260. package/src/internal/proxy/schema.test.ts +136 -0
  261. package/src/internal/proxy/typed-handler.test.ts +102 -0
  262. package/src/internal/proxy/typed-handler.ts +228 -0
  263. package/src/internal/proxy/typed-object.test.ts +100 -0
  264. package/src/internal/query/index.ts +5 -0
  265. package/src/internal/query/query.ts +23 -0
  266. package/src/internal/ref/index.ts +6 -0
  267. package/src/internal/ref/ref-array.ts +39 -0
  268. package/src/internal/ref/ref.test.ts +100 -0
  269. package/src/internal/ref/ref.ts +521 -0
  270. package/src/internal/schema/echo-schema.ts +383 -0
  271. package/src/internal/schema/index.ts +10 -0
  272. package/src/internal/schema/manipulation.ts +92 -0
  273. package/src/internal/schema/runtime-schema-registry.ts +78 -0
  274. package/src/internal/schema/snapshot.ts +25 -0
  275. package/src/internal/schema/stored-schema.ts +26 -0
  276. package/src/internal/testing/index.ts +6 -0
  277. package/src/internal/testing/types.ts +211 -0
  278. package/src/internal/testing/utils.ts +54 -0
  279. package/src/internal/types/index.ts +6 -0
  280. package/src/internal/types/types.test.ts +48 -0
  281. package/src/internal/types/types.ts +176 -0
  282. package/src/internal/types/util.ts +9 -0
  283. package/src/query/index.ts +2 -1
  284. package/src/query/query.test.ts +401 -0
  285. package/src/query/{dsl.ts → query.ts} +82 -45
  286. package/src/query/tag.ts +37 -0
  287. package/src/test/api.test.ts +9 -9
  288. package/src/testing/echo-schema.ts +39 -0
  289. package/src/testing/index.ts +2 -0
  290. package/src/testing/types.ts +1 -1
  291. package/dist/lib/browser/chunk-EIXXFUN5.mjs.map +0 -7
  292. package/dist/lib/node-esm/chunk-TCY7IVTS.mjs.map +0 -7
  293. package/dist/types/src/experimental/database.d.ts +0 -8
  294. package/dist/types/src/experimental/database.d.ts.map +0 -1
  295. package/dist/types/src/experimental/index.d.ts +0 -1
  296. package/dist/types/src/experimental/index.d.ts.map +0 -1
  297. package/dist/types/src/experimental/queue.d.ts +0 -8
  298. package/dist/types/src/experimental/queue.d.ts.map +0 -1
  299. package/dist/types/src/experimental/space.d.ts +0 -8
  300. package/dist/types/src/experimental/space.d.ts.map +0 -1
  301. package/dist/types/src/query/dsl.d.ts.map +0 -1
  302. package/dist/types/src/query/dsl.test.d.ts +0 -2
  303. package/dist/types/src/query/dsl.test.d.ts.map +0 -1
  304. package/src/experimental/database.ts +0 -11
  305. package/src/experimental/index.ts +0 -7
  306. package/src/experimental/queue.ts +0 -11
  307. package/src/experimental/space.ts +0 -11
  308. package/src/query/dsl.test.ts +0 -362
@@ -0,0 +1,118 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { inspect } from 'util';
6
+
7
+ import * as Schema from 'effect/Schema';
8
+ import { describe, expect, test } from 'vitest';
9
+
10
+ import { DXN } from '@dxos/keys';
11
+
12
+ import { getSchemaDXN } from '../ast';
13
+ import { Testing } from '../testing';
14
+ import { isInstanceOf } from '../types';
15
+
16
+ import { getSchema } from './accessors';
17
+ import { create } from './create';
18
+ import { objectToJSON } from './json-serializer';
19
+ import { RelationSourceId, RelationTargetId } from './model';
20
+ import { getType } from './typename';
21
+
22
+ describe('create (static version)', () => {
23
+ test('defaults', ({ expect }) => {
24
+ const Contact = Schema.Struct({
25
+ name: Schema.String.pipe(
26
+ Schema.optional,
27
+ Schema.withConstructorDefault(() => 'Anonymous'),
28
+ ),
29
+ email: Schema.String.pipe(Schema.optional),
30
+ });
31
+
32
+ const obj = Contact.make({});
33
+ expect(obj.name).toBe('Anonymous');
34
+ });
35
+
36
+ test('create static object', () => {
37
+ const contact = create(Testing.Contact, {
38
+ name: 'Bot',
39
+ email: 'bot@example.com',
40
+ });
41
+
42
+ expect(contact.id).toBeDefined();
43
+ expect(contact.name).toBe('Bot');
44
+ expect(contact.email).toBe('bot@example.com');
45
+ expect((contact as any)['@type']).toBeUndefined();
46
+ expect(getType(contact)?.toString()).toBe(getSchemaDXN(Testing.Contact)!.toString());
47
+ expect(isInstanceOf(Testing.Contact, contact)).toBe(true);
48
+ });
49
+
50
+ test('JSON encoding', () => {
51
+ const contact = create(Testing.Contact, {
52
+ name: 'Bot',
53
+ email: 'bot@example.com',
54
+ });
55
+
56
+ const json = JSON.parse(JSON.stringify(contact));
57
+ expect(json).toEqual({
58
+ id: contact.id,
59
+ '@type': DXN.fromTypenameAndVersion(Testing.Contact.typename, Testing.Contact.version).toString(),
60
+ '@meta': {
61
+ keys: [],
62
+ },
63
+ name: 'Bot',
64
+ email: 'bot@example.com',
65
+ });
66
+ expect(objectToJSON(contact)).toStrictEqual(json);
67
+ });
68
+
69
+ test('JSON encoding with relation', () => {
70
+ const contactA = create(Testing.Contact, {
71
+ name: 'Bot',
72
+ email: 'bot@example.com',
73
+ });
74
+ const contactB = create(Testing.Contact, {
75
+ name: 'Bot',
76
+ email: 'bot@example.com',
77
+ });
78
+ const hasManager = create(Testing.HasManager, {
79
+ [RelationSourceId]: contactA,
80
+ [RelationTargetId]: contactB,
81
+ });
82
+
83
+ const json = JSON.parse(JSON.stringify(hasManager));
84
+ expect(json).toEqual({
85
+ id: hasManager.id,
86
+ '@type': DXN.fromTypenameAndVersion(Testing.HasManager.typename, Testing.HasManager.version).toString(),
87
+ '@relationSource': DXN.fromLocalObjectId(contactA.id).toString(),
88
+ '@relationTarget': DXN.fromLocalObjectId(contactB.id).toString(),
89
+ '@meta': {
90
+ keys: [],
91
+ },
92
+ });
93
+ });
94
+
95
+ test('getSchema', () => {
96
+ const contact = create(Testing.Contact, {
97
+ name: 'Bot',
98
+ email: 'bot@example.com',
99
+ });
100
+
101
+ expect(getSchema(contact)).toBe(Testing.Contact);
102
+ });
103
+
104
+ test('inspect', () => {
105
+ const contact = create(Testing.Contact, {
106
+ name: 'Bot',
107
+ email: 'bot@example.com',
108
+ });
109
+
110
+ // console.log(contact);
111
+
112
+ const text = inspect(contact);
113
+ expect(text).toContain('Bot');
114
+ expect(text).toContain('bot@example.com');
115
+ expect(text).toContain('example.com/type/Contact');
116
+ expect(text).toContain('0.1.0');
117
+ });
118
+ });
@@ -0,0 +1,96 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import type * as Schema from 'effect/Schema';
6
+
7
+ import { raise } from '@dxos/debug';
8
+ import { assertArgument, failedInvariant } from '@dxos/invariant';
9
+ import { ObjectId } from '@dxos/keys';
10
+ import { defineHiddenProperty } from '@dxos/live-object';
11
+
12
+ import { EntityKind, getSchemaDXN, getTypeAnnotation } from '../ast';
13
+
14
+ import { getObjectDXN, setSchema } from './accessors';
15
+ import { attachedTypedObjectInspector } from './inspect';
16
+ import { attachTypedJsonSerializer } from './json-serializer';
17
+ import {
18
+ EntityKindId,
19
+ MetaId,
20
+ RelationSourceDXNId,
21
+ RelationSourceId,
22
+ RelationTargetDXNId,
23
+ RelationTargetId,
24
+ assertObjectModelShape,
25
+ } from './model';
26
+ import { setTypename } from './typename';
27
+
28
+ // Make `id` optional.
29
+ type CreateData<T> = T extends { id: string } ? Omit<T, 'id' | typeof EntityKindId> & { id?: string } : T;
30
+
31
+ /**
32
+ * Creates a new object instance from a schema and data, without signal reactivity.
33
+ * This static version creates plain JavaScript objects that are not reactive/observable.
34
+ * For reactive objects that automatically update UI when changed, use the regular live() function.
35
+ *
36
+ * @param schema - The Effect schema that defines the object's structure and type, piped into EchoObject
37
+ * @param data - The data to initialize the object with. The id and @type fields are handled automatically.
38
+ * @returns A new non-reactive object instance conforming to the schema
39
+ * @throws {Error} If the schema is not an object schema
40
+ * @throws {TypeError} If data contains an @type field
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * const Contact = Schema.Struct({
45
+ * name: Schema.String,
46
+ * email: Schema.String,
47
+ * }).pipe(Type.Obj({
48
+ * typename: 'example.com/type/Contact',
49
+ * version: '0.1.0',
50
+ * }))
51
+ *
52
+ * // Creates a non-reactive contact object
53
+ * const contact = create(Contact, {
54
+ * name: "John",
55
+ * email: "john@example.com",
56
+ * })
57
+ * ```
58
+ */
59
+ // TODO(burdon): Rename make.
60
+ // TODO(burdon): Handle defaults (see Schema.make).
61
+ // TODO(dmaretskyi): Use `Obj.make` and `Relation.make` from '@dxos/echo' instead.
62
+ export const create = <S extends Schema.Schema.AnyNoContext>(
63
+ schema: S,
64
+ data: CreateData<Schema.Schema.Type<S>>,
65
+ ): CreateData<Schema.Schema.Type<S>> & { id: string } => {
66
+ const annotation = getTypeAnnotation(schema);
67
+ if (!annotation) {
68
+ throw new Error('Schema is not an object schema');
69
+ }
70
+ assertArgument(!('@type' in data), 'data', '@type is not allowed');
71
+ assertArgument(!(RelationSourceDXNId in data), 'data', 'Relation source DXN is not allowed in the constructor');
72
+ assertArgument(!(RelationTargetDXNId in data), 'data', 'Relation target DXN is not allowed in the constructor');
73
+ assertArgument(
74
+ RelationSourceId in data === RelationTargetId in data,
75
+ 'data',
76
+ 'Relation source and target must be provided together',
77
+ );
78
+
79
+ const obj = { ...data, id: data.id ?? ObjectId.random() };
80
+ const kind = RelationSourceId in data ? EntityKind.Relation : EntityKind.Object;
81
+ defineHiddenProperty(obj, EntityKindId, kind);
82
+ setTypename(obj, getSchemaDXN(schema) ?? failedInvariant('Missing schema DXN'));
83
+ setSchema(obj, schema);
84
+ attachTypedJsonSerializer(obj);
85
+ attachedTypedObjectInspector(obj);
86
+ defineHiddenProperty(obj, MetaId, { keys: [] });
87
+ if (kind === EntityKind.Relation) {
88
+ const sourceDXN = getObjectDXN(data[RelationSourceId]) ?? raise(new Error('Unresolved relation source'));
89
+ const targetDXN = getObjectDXN(data[RelationTargetId]) ?? raise(new Error('Unresolved relation target'));
90
+ defineHiddenProperty(obj, RelationSourceDXNId, sourceDXN);
91
+ defineHiddenProperty(obj, RelationTargetDXNId, targetDXN);
92
+ }
93
+
94
+ assertObjectModelShape(obj);
95
+ return obj;
96
+ };
@@ -0,0 +1,19 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import type { BaseObject } from '../types';
6
+
7
+ import { DeletedId } from './model';
8
+
9
+ /**
10
+ * @returns `true` if the object has been marked as deleted.
11
+ */
12
+ export const isDeleted = (obj: BaseObject): boolean => {
13
+ if ((obj as any)[DeletedId] === undefined) {
14
+ // TODO(dmaretskyi): Return to prior behavior of throwing.
15
+ // throw new Error('Object does not support deletion marker');
16
+ return false;
17
+ }
18
+ return (obj as any)[DeletedId] ?? false;
19
+ };
@@ -0,0 +1,248 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import * as Schema from 'effect/Schema';
6
+ import * as SchemaAST from 'effect/SchemaAST';
7
+ import type * as Types from 'effect/Types';
8
+
9
+ import { raise } from '@dxos/debug';
10
+ import { assertArgument, invariant } from '@dxos/invariant';
11
+ import { DXN } from '@dxos/keys';
12
+
13
+ import {
14
+ EntityKind,
15
+ type TypeAnnotation,
16
+ TypeAnnotationId,
17
+ type TypeMeta,
18
+ getEntityKind,
19
+ getSchemaTypename,
20
+ getTypeIdentifierAnnotation,
21
+ } from '../ast';
22
+ import { type HasId, type ToMutable } from '../types';
23
+
24
+ import { type RelationSourceTargetRefs } from './relation';
25
+
26
+ // TODO(burdon): Define Schema type for `typename` and use consistently for all DXN-like properties.
27
+
28
+ // type RequiredKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? never : K }[keyof T];
29
+ type EchoTypeSchemaProps<T, ExtraFields = {}> = Types.Simplify<HasId & ToMutable<T> & ExtraFields>;
30
+
31
+ export interface EchoTypeSchema<Self extends Schema.Schema.Any, ExtraFields = {}>
32
+ extends TypeMeta,
33
+ Schema.AnnotableClass<
34
+ EchoTypeSchema<Self, ExtraFields>,
35
+ EchoTypeSchemaProps<Schema.Schema.Type<Self>, ExtraFields>,
36
+ EchoTypeSchemaProps<Schema.Schema.Encoded<Self>, ExtraFields>,
37
+ Schema.Schema.Context<Self>
38
+ > {
39
+ // make(
40
+ // props: RequiredKeys<Schema.TypeLiteral.Constructor<Fields, []>> extends never
41
+ // ? void | Simplify<Schema.TypeLiteral.Constructor<Fields, []>>
42
+ // : Simplify<Schema.TypeLiteral.Constructor<Fields, []>>,
43
+ // options?: MakeOptions,
44
+ // ): Simplify<Schema.TypeLiteral.Type<Fields, []>>;
45
+
46
+ instanceOf(value: unknown): boolean;
47
+ }
48
+
49
+ /**
50
+ * Pipeable function to add ECHO object annotations to a schema.
51
+ */
52
+ // TODO(dmaretskyi): Rename EchoObjectSchema.
53
+ export const EchoObject: {
54
+ // TODO(burdon): Tighten Self type to Schema.TypeLiteral or Schema.Struct to facilitate definition of `make` method.
55
+ // (meta: TypeMeta): <Self extends Schema.Struct<Fields>, Fields extends Schema.Struct.Fields>(self: Self) => EchoObjectSchema<Self, Fields>;
56
+ (meta: TypeMeta): <Self extends Schema.Schema.Any>(self: Self) => EchoTypeSchema<Self>;
57
+ } = ({ typename, version }) => {
58
+ return <Self extends Schema.Schema.Any>(self: Self): EchoTypeSchema<Self> => {
59
+ invariant(typeof TypeAnnotationId === 'symbol', 'Sanity.');
60
+ invariant(SchemaAST.isTypeLiteral(self.ast), 'Schema must be a TypeLiteral.');
61
+
62
+ // TODO(dmaretskyi): Does `Schema.mutable` work for deep mutability here?
63
+ // TODO(dmaretskyi): Do not do mutable here.
64
+ const schemaWithId = Schema.extend(Schema.mutable(self), Schema.Struct({ id: Schema.String }));
65
+ const ast = SchemaAST.annotations(schemaWithId.ast, {
66
+ // TODO(dmaretskyi): `extend` kills the annotations.
67
+ ...self.ast.annotations,
68
+ [TypeAnnotationId]: { kind: EntityKind.Object, typename, version } satisfies TypeAnnotation,
69
+ // TODO(dmaretskyi): TypeIdentifierAnnotationId?
70
+ [SchemaAST.JSONSchemaAnnotationId]: makeTypeJsonSchemaAnnotation({
71
+ kind: EntityKind.Object,
72
+ typename,
73
+ version,
74
+ }),
75
+ });
76
+
77
+ return makeEchoObjectSchema<Self>(/* self.fields, */ ast, typename, version);
78
+ };
79
+ };
80
+
81
+ export type EchoRelationOptions<
82
+ TSource extends Schema.Schema.AnyNoContext,
83
+ TTarget extends Schema.Schema.AnyNoContext,
84
+ > = TypeMeta & {
85
+ source: TSource;
86
+ target: TTarget;
87
+ };
88
+
89
+ // TODO(dmaretskyi): Rename EchoRelationSchema.
90
+ export const EchoRelation = <Source extends Schema.Schema.AnyNoContext, Target extends Schema.Schema.AnyNoContext>(
91
+ options: EchoRelationOptions<Source, Target>,
92
+ ) => {
93
+ assertArgument(Schema.isSchema(options.source), 'source');
94
+ assertArgument(Schema.isSchema(options.target), 'target');
95
+ const sourceDXN = getDXNForRelationSchemaRef(options.source);
96
+ const targetDXN = getDXNForRelationSchemaRef(options.target);
97
+ if (getEntityKind(options.source) !== EntityKind.Object) {
98
+ raise(new Error('Source schema must be an echo object schema.'));
99
+ }
100
+ if (getEntityKind(options.target) !== EntityKind.Object) {
101
+ raise(new Error('Target schema must be an echo object schema.'));
102
+ }
103
+
104
+ return <Self extends Schema.Schema.Any>(
105
+ self: Self,
106
+ ): EchoTypeSchema<Self, RelationSourceTargetRefs<Schema.Schema.Type<Source>, Schema.Schema.Type<Target>>> => {
107
+ invariant(SchemaAST.isTypeLiteral(self.ast), 'Schema must be a TypeLiteral.');
108
+
109
+ // TODO(dmaretskyi): Does `Schema.mutable` work for deep mutability here?
110
+ // TODO(dmaretskyi): Do not do mutable here.
111
+ const schemaWithId = Schema.extend(Schema.mutable(self), Schema.Struct({ id: Schema.String }));
112
+ const ast = SchemaAST.annotations(schemaWithId.ast, {
113
+ // TODO(dmaretskyi): `extend` kills the annotations.
114
+ ...self.ast.annotations,
115
+ [TypeAnnotationId]: {
116
+ kind: EntityKind.Relation,
117
+ typename: options.typename,
118
+ version: options.version,
119
+ sourceSchema: sourceDXN,
120
+ targetSchema: targetDXN,
121
+ } satisfies TypeAnnotation,
122
+ // TODO(dmaretskyi): TypeIdentifierAnnotationId?
123
+
124
+ [SchemaAST.JSONSchemaAnnotationId]: makeTypeJsonSchemaAnnotation({
125
+ kind: EntityKind.Relation,
126
+ typename: options.typename,
127
+ version: options.version,
128
+ relationSource: sourceDXN,
129
+ relationTarget: targetDXN,
130
+ }),
131
+ });
132
+
133
+ return makeEchoObjectSchema<Self>(/* self.fields, */ ast, options.typename, options.version);
134
+ };
135
+ };
136
+
137
+ /**
138
+ * @returns JSON-schema annotation so that the schema can be serialized with correct parameters.
139
+ */
140
+ export const makeTypeJsonSchemaAnnotation = (options: {
141
+ identifier?: string;
142
+ kind: EntityKind;
143
+ typename: string;
144
+ version: string;
145
+ relationSource?: string;
146
+ relationTarget?: string;
147
+ }) => {
148
+ assertArgument(!!options.relationSource === (options.kind === EntityKind.Relation), 'relationSource');
149
+ assertArgument(!!options.relationTarget === (options.kind === EntityKind.Relation), 'relationTarget');
150
+
151
+ const obj = {
152
+ // TODO(dmaretskyi): Should this include the version?
153
+ $id: options.identifier ?? DXN.fromTypename(options.typename).toString(),
154
+ entityKind: options.kind,
155
+ version: options.version,
156
+ typename: options.typename,
157
+ } as any;
158
+ if (options.kind === EntityKind.Relation) {
159
+ obj.relationSource = { $ref: options.relationSource };
160
+ obj.relationTarget = { $ref: options.relationTarget };
161
+ }
162
+ return obj;
163
+ };
164
+
165
+ const getDXNForRelationSchemaRef = (schema: Schema.Schema.Any): string => {
166
+ assertArgument(Schema.isSchema(schema), 'schema');
167
+ const identifier = getTypeIdentifierAnnotation(schema);
168
+ if (identifier) {
169
+ return identifier;
170
+ }
171
+
172
+ const typename = getSchemaTypename(schema);
173
+ if (!typename) {
174
+ throw new Error('Schema must have a typename');
175
+ }
176
+
177
+ return DXN.fromTypename(typename).toString();
178
+ };
179
+
180
+ // type MakeOptions =
181
+ // | boolean
182
+ // | {
183
+ // readonly disableValidation?: boolean;
184
+ // };
185
+
186
+ // NOTE: Utils copied from Effect `Schema.ts`.
187
+ // const _ownKeys = (o: object): Array<PropertyKey> =>
188
+ // (Object.keys(o) as Array<PropertyKey>).concat(Object.getOwnPropertySymbols(o));
189
+
190
+ // const _lazilyMergeDefaults = (
191
+ // fields: Schema.Struct.Fields,
192
+ // out: Record<PropertyKey, unknown>,
193
+ // ): { [x: string | symbol]: unknown } => {
194
+ // const ownKeys = _ownKeys(fields);
195
+ // for (const key of ownKeys) {
196
+ // const field = fields[key];
197
+ // if (out[key] === undefined && Schema.isPropertySignature(field)) {
198
+ // const ast = field.ast;
199
+ // const defaultValue = ast._tag === 'PropertySignatureDeclaration' ? ast.defaultValue : ast.to.defaultValue;
200
+ // if (defaultValue !== undefined) {
201
+ // out[key] = defaultValue();
202
+ // }
203
+ // }
204
+ // }
205
+ // return out;
206
+ // };
207
+
208
+ // const _getDisableValidationMakeOption = (options: MakeOptions | undefined): boolean =>
209
+ // Predicate.isBoolean(options) ? options : options?.disableValidation ?? false;
210
+
211
+ const makeEchoObjectSchema = <Self extends Schema.Schema.Any>(
212
+ // fields: Fields,
213
+ ast: SchemaAST.AST,
214
+ typename: string,
215
+ version: string,
216
+ ): EchoTypeSchema<Self> => {
217
+ return class EchoObjectSchemaClass extends Schema.make<
218
+ EchoTypeSchemaProps<Schema.Schema.Type<Self>>,
219
+ EchoTypeSchemaProps<Schema.Schema.Encoded<Self>>,
220
+ Schema.Schema.Context<Self>
221
+ >(ast) {
222
+ static readonly typename = typename;
223
+ static readonly version = version;
224
+
225
+ static override annotations(
226
+ annotations: Schema.Annotations.GenericSchema<EchoTypeSchemaProps<Schema.Schema.Type<Self>>>,
227
+ ): EchoTypeSchema<Self> {
228
+ const schema = Schema.make<EchoTypeSchemaProps<Schema.Schema.Type<Self>>>(ast).annotations(annotations);
229
+ return makeEchoObjectSchema<Self>(/* fields, */ schema.ast, typename, version);
230
+ }
231
+
232
+ // static make(
233
+ // props: RequiredKeys<Schema.TypeLiteral.Constructor<Fields, []>> extends never
234
+ // ? void | Simplify<Schema.TypeLiteral.Constructor<Fields, []>>
235
+ // : Simplify<Schema.TypeLiteral.Constructor<Fields, []>>,
236
+ // options?: MakeOptions,
237
+ // ): Simplify<Schema.TypeLiteral.Type<Fields, []>> {
238
+ // const propsWithDefaults: any = _lazilyMergeDefaults(fields, { ...(props as any) });
239
+ // return _getDisableValidationMakeOption(options)
240
+ // ? propsWithDefaults
241
+ // : ParseResult.validateSync(this)(propsWithDefaults);
242
+ // }
243
+
244
+ static instanceOf(value: unknown): boolean {
245
+ return Schema.is(this)(value);
246
+ }
247
+ };
248
+ };
@@ -0,0 +1,21 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import * as Schema from 'effect/Schema';
6
+
7
+ import { EchoObject } from './entity';
8
+
9
+ export const EXPANDO_TYPENAME = 'dxos.org/type/Expando';
10
+
11
+ const ExpandoSchema = Schema.Struct({}, { key: Schema.String, value: Schema.Any }).pipe(
12
+ EchoObject({ typename: EXPANDO_TYPENAME, version: '0.1.0' }),
13
+ );
14
+
15
+ /**
16
+ * Expando object is an object with an arbitrary set of properties.
17
+ */
18
+ // TODO(dmaretskyi): Can we consider expando a top-type, i.e. have a ref to expando potentially be a valid ref to any object?
19
+ export interface Expando extends Schema.Schema.Type<typeof ExpandoSchema> {}
20
+
21
+ export const Expando: Schema.Schema<Expando> = ExpandoSchema;
@@ -0,0 +1,12 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { DXN, ObjectId, QueueSubspaceTags, SpaceId } from '@dxos/keys';
6
+
7
+ // TODO(burdon): Move to @dxos/keys once ObjectId is moved there.
8
+ /**
9
+ * @deprecated Use `db.queues.create()`
10
+ */
11
+ export const createQueueDXN = (spaceId = SpaceId.random(), queueId = ObjectId.random()) =>
12
+ new DXN(DXN.kind.QUEUE, [QueueSubspaceTags.DATA, spaceId, queueId]);
@@ -0,0 +1,19 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './common';
6
+ export * from './create';
7
+ export * from './entity';
8
+ export * from './expando';
9
+ export * from './ids';
10
+ export * from './json-serializer';
11
+ export * from './meta';
12
+ export type * from './relation';
13
+ export * from './typed-object';
14
+ export * from './typename';
15
+ export * from './deleted';
16
+ export * from './model';
17
+ export * from './accessors';
18
+ export * from './schema-validator';
19
+ export * from './version';
@@ -0,0 +1,48 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import type { InspectOptionsStylized, inspect as inspectFn } from 'node:util';
6
+
7
+ import { type CustomInspectFunction, inspectCustom } from '@dxos/debug';
8
+
9
+ import { type AnyEchoObject } from '../types';
10
+
11
+ import { ATTR_META, ATTR_TYPE, MetaId } from './model';
12
+ import { getType } from './typename';
13
+
14
+ /*
15
+ * @internal
16
+ */
17
+ export const attachedTypedObjectInspector = (obj: any) => {
18
+ const descriptor = Object.getOwnPropertyDescriptor(obj, inspectCustom);
19
+ if (descriptor) {
20
+ return;
21
+ }
22
+
23
+ Object.defineProperty(obj, inspectCustom, {
24
+ value: typedObjectInspectFunction,
25
+ writable: false,
26
+ enumerable: false,
27
+ configurable: true,
28
+ });
29
+ };
30
+
31
+ // NOTE: KEEP as function.
32
+ const typedObjectInspectFunction: CustomInspectFunction<AnyEchoObject> = function (
33
+ this: AnyEchoObject,
34
+ depth: number,
35
+ options: InspectOptionsStylized,
36
+ inspect: typeof inspectFn,
37
+ ) {
38
+ const { id, ...props } = this;
39
+ return inspect(
40
+ {
41
+ id,
42
+ [ATTR_TYPE]: getType(this),
43
+ ...props,
44
+ [ATTR_META]: (this as any)[MetaId], // TODO(dmaretskyi): Couldn't use getMeta since that throw's if the object has no meta.
45
+ },
46
+ options,
47
+ );
48
+ };
@@ -0,0 +1,99 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { describe, expect, test } from 'vitest';
6
+
7
+ import { DXN } from '@dxos/keys';
8
+
9
+ import { EntityKind, getSchemaDXN, getSchemaTypename } from '../ast';
10
+ import { Ref, StaticRefResolver } from '../ref';
11
+ import { Testing } from '../testing';
12
+
13
+ import { getObjectDXN, getSchema } from './accessors';
14
+ import { create } from './create';
15
+ import { objectFromJSON, objectToJSON } from './json-serializer';
16
+ import { getMeta } from './meta';
17
+ import { ATTR_TYPE, EntityKindId, MetaId, RelationSourceId, RelationTargetId, TypeId } from './model';
18
+ import { getType, getTypename } from './typename';
19
+
20
+ describe('Object JSON serializer', () => {
21
+ test('should serialize and deserialize object', async () => {
22
+ const contact = create(Testing.Contact, {
23
+ name: 'John Doe',
24
+ });
25
+ getMeta(contact).keys.push({ id: '12345', source: 'crm.example.com' });
26
+
27
+ const task = create(Testing.Task, {
28
+ title: 'Polish my shoes',
29
+ assignee: Ref.make(contact),
30
+ });
31
+
32
+ const contactJson = objectToJSON(contact);
33
+ const taskJson = objectToJSON(task);
34
+
35
+ expect(contactJson.id).toBe(contact.id);
36
+ expect(contactJson[ATTR_TYPE]).toEqual(getSchemaDXN(Testing.Contact)!.toString());
37
+ expect(contactJson.name).toEqual('John Doe');
38
+
39
+ expect(taskJson.id).toBe(task.id);
40
+ expect(taskJson[ATTR_TYPE]).toEqual(getSchemaDXN(Testing.Task)!.toString());
41
+ expect(taskJson.title).toEqual('Polish my shoes');
42
+ expect(taskJson.assignee).toEqual({ '/': DXN.fromLocalObjectId(contact.id).toString() });
43
+
44
+ const refResolver = new StaticRefResolver()
45
+ .addSchema(Testing.Contact)
46
+ .addSchema(Testing.Task)
47
+ .addObject(contact)
48
+ .addObject(task);
49
+
50
+ const contactFromJson = (await objectFromJSON(contactJson, { refResolver })) as Testing.Contact;
51
+ const taskFromJson = (await objectFromJSON(taskJson, { refResolver })) as Testing.Task;
52
+
53
+ expect(contactFromJson.id).toBe(contact.id);
54
+ expect(contactFromJson.name).toBe('John Doe');
55
+ expect((contactFromJson as any)[TypeId]).toEqual(getSchemaDXN(Testing.Contact));
56
+ expect((contactFromJson as any)[EntityKindId]).toBe(EntityKind.Object);
57
+ expect((contactFromJson as any)[RelationSourceId]).toBeUndefined();
58
+ expect((contactFromJson as any)[RelationTargetId]).toBeUndefined();
59
+ expect((contactFromJson as any)[MetaId]).toEqual({
60
+ keys: [
61
+ {
62
+ id: '12345',
63
+ source: 'crm.example.com',
64
+ },
65
+ ],
66
+ });
67
+ expect(getType(contactFromJson)?.toString()).toBe(getSchemaDXN(Testing.Contact)!.toString());
68
+ expect(getTypename(contactFromJson)).toBe(getSchemaTypename(Testing.Contact));
69
+ expect(getObjectDXN(contactFromJson)?.toString()).toEqual(getObjectDXN(contact)?.toString());
70
+ expect(getSchema(contactFromJson)).toEqual(Testing.Contact);
71
+
72
+ expect(taskFromJson.id).toBe(task.id);
73
+ expect(taskFromJson.title).toBe('Polish my shoes');
74
+ expect(taskFromJson.assignee!.dxn).toEqual(DXN.fromLocalObjectId(contact.id));
75
+ expect(taskFromJson.assignee!.target).toEqual(contact);
76
+ expect(await taskFromJson.assignee!.load()).toEqual(contact);
77
+ expect((taskFromJson as any)[TypeId]).toEqual(getSchemaDXN(Testing.Task));
78
+ expect((taskFromJson as any)[EntityKindId]).toBe(EntityKind.Object);
79
+ expect((taskFromJson as any)[RelationSourceId]).toBeUndefined();
80
+ expect((taskFromJson as any)[RelationTargetId]).toBeUndefined();
81
+ expect((taskFromJson as any)[MetaId]).toEqual({ keys: [] });
82
+ expect(getSchema(taskFromJson)).toEqual(Testing.Task);
83
+ });
84
+
85
+ test('serialize with unresolved schema', async () => {
86
+ const contact = create(Testing.Contact, {
87
+ name: 'John Doe',
88
+ });
89
+ const contactJson = objectToJSON(contact);
90
+ const contactFromJson: any = await objectFromJSON(contactJson);
91
+
92
+ expect(contactFromJson.id).toBe(contact.id);
93
+ expect(contactFromJson.name).toBe('John Doe');
94
+ expect(getSchema(contactFromJson)).toBeUndefined();
95
+ expect(getTypename(contactFromJson)).toEqual(getSchemaTypename(Testing.Contact));
96
+ expect(getObjectDXN(contactFromJson)).toEqual(getObjectDXN(contact));
97
+ expect(getType(contactFromJson)).toEqual(getSchemaDXN(Testing.Contact));
98
+ });
99
+ });