@dxos/echo 0.8.4-main.5ea62a8 → 0.8.4-main.66e292d

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 (328) hide show
  1. package/dist/lib/browser/chunk-7GH6RXJ3.mjs +3683 -0
  2. package/dist/lib/browser/chunk-7GH6RXJ3.mjs.map +7 -0
  3. package/dist/lib/browser/chunk-E4UTVJNF.mjs +1111 -0
  4. package/dist/lib/browser/chunk-E4UTVJNF.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +24 -1
  6. package/dist/lib/browser/internal/index.mjs +336 -0
  7. package/dist/lib/browser/internal/index.mjs.map +7 -0
  8. package/dist/lib/browser/meta.json +1 -1
  9. package/dist/lib/browser/testing/index.mjs +289 -31
  10. package/dist/lib/browser/testing/index.mjs.map +4 -4
  11. package/dist/lib/node-esm/chunk-JE5RXM2I.mjs +1111 -0
  12. package/dist/lib/node-esm/chunk-JE5RXM2I.mjs.map +7 -0
  13. package/dist/lib/node-esm/chunk-M4B6BMD2.mjs +3683 -0
  14. package/dist/lib/node-esm/chunk-M4B6BMD2.mjs.map +7 -0
  15. package/dist/lib/node-esm/index.mjs +24 -1
  16. package/dist/lib/node-esm/internal/index.mjs +336 -0
  17. package/dist/lib/node-esm/internal/index.mjs.map +7 -0
  18. package/dist/lib/node-esm/meta.json +1 -1
  19. package/dist/lib/node-esm/testing/index.mjs +289 -31
  20. package/dist/lib/node-esm/testing/index.mjs.map +4 -4
  21. package/dist/types/src/Annotation.d.ts +2 -0
  22. package/dist/types/src/Annotation.d.ts.map +1 -0
  23. package/dist/types/src/Database.d.ts +137 -0
  24. package/dist/types/src/Database.d.ts.map +1 -0
  25. package/dist/types/src/Entity.d.ts +36 -0
  26. package/dist/types/src/Entity.d.ts.map +1 -0
  27. package/dist/types/src/Format.d.ts +4 -0
  28. package/dist/types/src/Format.d.ts.map +1 -0
  29. package/dist/types/src/JsonSchema.d.ts +9 -0
  30. package/dist/types/src/JsonSchema.d.ts.map +1 -0
  31. package/dist/types/src/Key.d.ts +1 -0
  32. package/dist/types/src/Key.d.ts.map +1 -1
  33. package/dist/types/src/Obj.d.ts +121 -50
  34. package/dist/types/src/Obj.d.ts.map +1 -1
  35. package/dist/types/src/Ref.d.ts +10 -10
  36. package/dist/types/src/Ref.d.ts.map +1 -1
  37. package/dist/types/src/Relation.d.ts +18 -14
  38. package/dist/types/src/Relation.d.ts.map +1 -1
  39. package/dist/types/src/Tag.d.ts +17 -0
  40. package/dist/types/src/Tag.d.ts.map +1 -0
  41. package/dist/types/src/Type.d.ts +39 -50
  42. package/dist/types/src/Type.d.ts.map +1 -1
  43. package/dist/types/src/errors.d.ts +68 -0
  44. package/dist/types/src/errors.d.ts.map +1 -0
  45. package/dist/types/src/index.d.ts +9 -3
  46. package/dist/types/src/index.d.ts.map +1 -1
  47. package/dist/types/src/internal/annotations/annotations.d.ts +174 -0
  48. package/dist/types/src/internal/annotations/annotations.d.ts.map +1 -0
  49. package/dist/types/src/internal/annotations/annotations.test.d.ts +2 -0
  50. package/dist/types/src/internal/annotations/annotations.test.d.ts.map +1 -0
  51. package/dist/types/src/internal/annotations/index.d.ts +3 -0
  52. package/dist/types/src/internal/annotations/index.d.ts.map +1 -0
  53. package/dist/types/src/internal/annotations/util.d.ts +26 -0
  54. package/dist/types/src/internal/annotations/util.d.ts.map +1 -0
  55. package/dist/types/src/internal/entities/entity.d.ts +10 -0
  56. package/dist/types/src/internal/entities/entity.d.ts.map +1 -0
  57. package/dist/types/src/internal/entities/expando.d.ts +16 -0
  58. package/dist/types/src/internal/entities/expando.d.ts.map +1 -0
  59. package/dist/types/src/internal/entities/index.d.ts +6 -0
  60. package/dist/types/src/internal/entities/index.d.ts.map +1 -0
  61. package/dist/types/src/internal/entities/model.d.ts +70 -0
  62. package/dist/types/src/internal/entities/model.d.ts.map +1 -0
  63. package/dist/types/src/internal/entities/object.d.ts +11 -0
  64. package/dist/types/src/internal/entities/object.d.ts.map +1 -0
  65. package/dist/types/src/internal/entities/relation.d.ts +55 -0
  66. package/dist/types/src/internal/entities/relation.d.ts.map +1 -0
  67. package/dist/types/src/internal/entities/util.d.ts +2 -0
  68. package/dist/types/src/internal/entities/util.d.ts.map +1 -0
  69. package/dist/types/src/internal/formats/date.d.ts +63 -0
  70. package/dist/types/src/internal/formats/date.d.ts.map +1 -0
  71. package/dist/types/src/internal/formats/date.test.d.ts +2 -0
  72. package/dist/types/src/internal/formats/date.test.d.ts.map +1 -0
  73. package/dist/types/src/internal/formats/format.d.ts +32 -0
  74. package/dist/types/src/internal/formats/format.d.ts.map +1 -0
  75. package/dist/types/src/internal/formats/format.test.d.ts +2 -0
  76. package/dist/types/src/internal/formats/format.test.d.ts.map +1 -0
  77. package/dist/types/src/internal/formats/index.d.ts +8 -0
  78. package/dist/types/src/internal/formats/index.d.ts.map +1 -0
  79. package/dist/types/src/internal/formats/number.d.ts +31 -0
  80. package/dist/types/src/internal/formats/number.d.ts.map +1 -0
  81. package/dist/types/src/internal/formats/object.d.ts +35 -0
  82. package/dist/types/src/internal/formats/object.d.ts.map +1 -0
  83. package/dist/types/src/internal/formats/select.d.ts +11 -0
  84. package/dist/types/src/internal/formats/select.d.ts.map +1 -0
  85. package/dist/types/src/internal/formats/string.d.ts +38 -0
  86. package/dist/types/src/internal/formats/string.d.ts.map +1 -0
  87. package/dist/types/src/internal/formats/types.d.ts +68 -0
  88. package/dist/types/src/internal/formats/types.d.ts.map +1 -0
  89. package/dist/types/src/internal/index.d.ts +11 -0
  90. package/dist/types/src/internal/index.d.ts.map +1 -0
  91. package/dist/types/src/internal/json-schema/annotations.d.ts +19 -0
  92. package/dist/types/src/internal/json-schema/annotations.d.ts.map +1 -0
  93. package/dist/types/src/internal/json-schema/effect-schema.test.d.ts +2 -0
  94. package/dist/types/src/internal/json-schema/effect-schema.test.d.ts.map +1 -0
  95. package/dist/types/src/internal/json-schema/index.d.ts +5 -0
  96. package/dist/types/src/internal/json-schema/index.d.ts.map +1 -0
  97. package/dist/types/src/internal/json-schema/json-schema-normalize.d.ts +7 -0
  98. package/dist/types/src/internal/json-schema/json-schema-normalize.d.ts.map +1 -0
  99. package/dist/types/src/internal/json-schema/json-schema-type.d.ts +250 -0
  100. package/dist/types/src/internal/json-schema/json-schema-type.d.ts.map +1 -0
  101. package/dist/types/src/internal/json-schema/json-schema.d.ts +29 -0
  102. package/dist/types/src/internal/json-schema/json-schema.d.ts.map +1 -0
  103. package/dist/types/src/internal/json-schema/json-schema.test.d.ts +2 -0
  104. package/dist/types/src/internal/json-schema/json-schema.test.d.ts.map +1 -0
  105. package/dist/types/src/internal/object/common.d.ts +18 -0
  106. package/dist/types/src/internal/object/common.d.ts.map +1 -0
  107. package/dist/types/src/internal/object/create-object.d.ts +39 -0
  108. package/dist/types/src/internal/object/create-object.d.ts.map +1 -0
  109. package/dist/types/src/internal/object/create-object.test.d.ts +2 -0
  110. package/dist/types/src/internal/object/create-object.test.d.ts.map +1 -0
  111. package/dist/types/src/internal/object/deleted.d.ts +6 -0
  112. package/dist/types/src/internal/object/deleted.d.ts.map +1 -0
  113. package/dist/types/src/internal/object/ids.d.ts +6 -0
  114. package/dist/types/src/internal/object/ids.d.ts.map +1 -0
  115. package/dist/types/src/internal/object/index.d.ts +8 -0
  116. package/dist/types/src/internal/object/index.d.ts.map +1 -0
  117. package/dist/types/src/internal/object/inspect.d.ts +2 -0
  118. package/dist/types/src/internal/object/inspect.d.ts.map +1 -0
  119. package/dist/types/src/internal/object/json-serializer.d.ts +31 -0
  120. package/dist/types/src/internal/object/json-serializer.d.ts.map +1 -0
  121. package/dist/types/src/internal/object/json-serializer.test.d.ts +2 -0
  122. package/dist/types/src/internal/object/json-serializer.test.d.ts.map +1 -0
  123. package/dist/types/src/internal/object/schema-validator.d.ts +15 -0
  124. package/dist/types/src/internal/object/schema-validator.d.ts.map +1 -0
  125. package/dist/types/src/internal/object/schema-validator.test.d.ts +2 -0
  126. package/dist/types/src/internal/object/schema-validator.test.d.ts.map +1 -0
  127. package/dist/types/src/internal/object/typed-object.d.ts +31 -0
  128. package/dist/types/src/internal/object/typed-object.d.ts.map +1 -0
  129. package/dist/types/src/internal/object/typed-object.test.d.ts +2 -0
  130. package/dist/types/src/internal/object/typed-object.test.d.ts.map +1 -0
  131. package/dist/types/src/internal/proxy/handler.test.d.ts +2 -0
  132. package/dist/types/src/internal/proxy/handler.test.d.ts.map +1 -0
  133. package/dist/types/src/internal/proxy/index.d.ts +3 -0
  134. package/dist/types/src/internal/proxy/index.d.ts.map +1 -0
  135. package/dist/types/src/internal/proxy/make-object.d.ts +16 -0
  136. package/dist/types/src/internal/proxy/make-object.d.ts.map +1 -0
  137. package/dist/types/src/internal/proxy/schema.test.d.ts +2 -0
  138. package/dist/types/src/internal/proxy/schema.test.d.ts.map +1 -0
  139. package/dist/types/src/internal/proxy/typed-handler.d.ts +44 -0
  140. package/dist/types/src/internal/proxy/typed-handler.d.ts.map +1 -0
  141. package/dist/types/src/internal/proxy/typed-handler.test.d.ts +2 -0
  142. package/dist/types/src/internal/proxy/typed-handler.test.d.ts.map +1 -0
  143. package/dist/types/src/internal/proxy/typed-object.test.d.ts +2 -0
  144. package/dist/types/src/internal/proxy/typed-object.test.d.ts.map +1 -0
  145. package/dist/types/src/internal/ref/index.d.ts +3 -0
  146. package/dist/types/src/internal/ref/index.d.ts.map +1 -0
  147. package/dist/types/src/internal/ref/ref-array.d.ts +21 -0
  148. package/dist/types/src/internal/ref/ref-array.d.ts.map +1 -0
  149. package/dist/types/src/internal/ref/ref.d.ts +209 -0
  150. package/dist/types/src/internal/ref/ref.d.ts.map +1 -0
  151. package/dist/types/src/internal/ref/ref.test.d.ts +2 -0
  152. package/dist/types/src/internal/ref/ref.test.d.ts.map +1 -0
  153. package/dist/types/src/internal/schema/compose.d.ts +6 -0
  154. package/dist/types/src/internal/schema/compose.d.ts.map +1 -0
  155. package/dist/types/src/internal/schema/compose.test.d.ts +2 -0
  156. package/dist/types/src/internal/schema/compose.test.d.ts.map +1 -0
  157. package/dist/types/src/internal/schema/echo-schema.d.ts +168 -0
  158. package/dist/types/src/internal/schema/echo-schema.d.ts.map +1 -0
  159. package/dist/types/src/internal/schema/index.d.ts +6 -0
  160. package/dist/types/src/internal/schema/index.d.ts.map +1 -0
  161. package/dist/types/src/internal/schema/manipulation.d.ts +10 -0
  162. package/dist/types/src/internal/schema/manipulation.d.ts.map +1 -0
  163. package/dist/types/src/internal/schema/persistent-schema.d.ts +18 -0
  164. package/dist/types/src/internal/schema/persistent-schema.d.ts.map +1 -0
  165. package/dist/types/src/internal/schema/runtime-schema-registry.d.ts +18 -0
  166. package/dist/types/src/internal/schema/runtime-schema-registry.d.ts.map +1 -0
  167. package/dist/types/src/internal/schema/snapshot.d.ts +6 -0
  168. package/dist/types/src/internal/schema/snapshot.d.ts.map +1 -0
  169. package/dist/types/src/internal/types/base.d.ts +37 -0
  170. package/dist/types/src/internal/types/base.d.ts.map +1 -0
  171. package/dist/types/src/internal/types/entity.d.ts +12 -0
  172. package/dist/types/src/internal/types/entity.d.ts.map +1 -0
  173. package/dist/types/src/internal/types/index.d.ts +6 -0
  174. package/dist/types/src/internal/types/index.d.ts.map +1 -0
  175. package/dist/types/src/internal/types/meta.d.ts +40 -0
  176. package/dist/types/src/internal/types/meta.d.ts.map +1 -0
  177. package/dist/types/src/internal/types/typename.d.ts +13 -0
  178. package/dist/types/src/internal/types/typename.d.ts.map +1 -0
  179. package/dist/types/src/internal/types/version.d.ts +15 -0
  180. package/dist/types/src/internal/types/version.d.ts.map +1 -0
  181. package/dist/types/src/query/filter.d.ts +167 -0
  182. package/dist/types/src/query/filter.d.ts.map +1 -0
  183. package/dist/types/src/query/index.d.ts +4 -1
  184. package/dist/types/src/query/index.d.ts.map +1 -1
  185. package/dist/types/src/query/order.d.ts +12 -0
  186. package/dist/types/src/query/order.d.ts.map +1 -0
  187. package/dist/types/src/query/query.d.ts +112 -0
  188. package/dist/types/src/query/query.d.ts.map +1 -0
  189. package/dist/types/src/query/query.test.d.ts +2 -0
  190. package/dist/types/src/query/query.test.d.ts.map +1 -0
  191. package/dist/types/src/query/testing.d.ts +51 -0
  192. package/dist/types/src/query/testing.d.ts.map +1 -0
  193. package/dist/types/src/query/types.d.ts +17 -0
  194. package/dist/types/src/query/types.d.ts.map +1 -0
  195. package/dist/types/src/query/util.d.ts +8 -0
  196. package/dist/types/src/query/util.d.ts.map +1 -0
  197. package/dist/types/src/{test → testing}/api.test.d.ts.map +1 -1
  198. package/dist/types/src/testing/index.d.ts +3 -1
  199. package/dist/types/src/testing/index.d.ts.map +1 -1
  200. package/dist/types/src/testing/test-data.d.ts +18 -0
  201. package/dist/types/src/testing/test-data.d.ts.map +1 -0
  202. package/dist/types/src/testing/test-schema.d.ts +337 -0
  203. package/dist/types/src/testing/test-schema.d.ts.map +1 -0
  204. package/dist/types/src/testing/util.d.ts +16 -0
  205. package/dist/types/src/testing/util.d.ts.map +1 -0
  206. package/dist/types/tsconfig.tsbuildinfo +1 -1
  207. package/package.json +49 -29
  208. package/src/Annotation.ts +17 -0
  209. package/src/Database.ts +189 -0
  210. package/src/Entity.ts +51 -0
  211. package/src/Format.ts +11 -0
  212. package/src/JsonSchema.ts +16 -0
  213. package/src/Key.ts +3 -0
  214. package/src/Obj.ts +353 -107
  215. package/src/Ref.ts +9 -10
  216. package/src/Relation.ts +68 -43
  217. package/src/Tag.ts +40 -0
  218. package/src/Type.ts +97 -85
  219. package/src/errors.ts +18 -0
  220. package/src/index.ts +13 -4
  221. package/src/internal/README.md +83 -0
  222. package/src/internal/annotations/annotations.test.ts +96 -0
  223. package/src/internal/annotations/annotations.ts +463 -0
  224. package/src/internal/annotations/index.ts +6 -0
  225. package/src/internal/annotations/util.ts +70 -0
  226. package/src/internal/entities/entity.ts +109 -0
  227. package/src/internal/entities/expando.ts +23 -0
  228. package/src/internal/entities/index.ts +9 -0
  229. package/src/internal/entities/model.ts +129 -0
  230. package/src/internal/entities/object.ts +45 -0
  231. package/src/internal/entities/relation.ts +155 -0
  232. package/src/internal/entities/util.ts +33 -0
  233. package/src/internal/formats/date.test.ts +56 -0
  234. package/src/internal/formats/date.ts +217 -0
  235. package/src/internal/formats/format.test.ts +77 -0
  236. package/src/internal/formats/format.ts +55 -0
  237. package/src/internal/formats/index.ts +12 -0
  238. package/src/internal/formats/number.ts +89 -0
  239. package/src/internal/formats/object.ts +80 -0
  240. package/src/internal/formats/select.ts +16 -0
  241. package/src/internal/formats/string.ts +76 -0
  242. package/src/internal/formats/types.ts +180 -0
  243. package/src/internal/index.ts +38 -0
  244. package/src/internal/json-schema/annotations.ts +50 -0
  245. package/src/internal/json-schema/effect-schema.test.ts +143 -0
  246. package/src/internal/json-schema/index.ts +8 -0
  247. package/src/internal/json-schema/json-schema-normalize.ts +109 -0
  248. package/src/internal/json-schema/json-schema-type.ts +404 -0
  249. package/src/internal/json-schema/json-schema.test.ts +859 -0
  250. package/src/internal/json-schema/json-schema.ts +528 -0
  251. package/src/internal/object/common.ts +75 -0
  252. package/src/internal/object/create-object.test.ts +116 -0
  253. package/src/internal/object/create-object.ts +95 -0
  254. package/src/internal/object/deleted.ts +19 -0
  255. package/src/internal/object/ids.ts +12 -0
  256. package/src/internal/object/index.ts +11 -0
  257. package/src/internal/object/inspect.ts +46 -0
  258. package/src/internal/object/json-serializer.test.ts +94 -0
  259. package/src/internal/object/json-serializer.ts +230 -0
  260. package/src/internal/object/schema-validator.test.ts +186 -0
  261. package/src/internal/object/schema-validator.ts +242 -0
  262. package/src/internal/object/typed-object.test.ts +34 -0
  263. package/src/internal/object/typed-object.ts +94 -0
  264. package/src/internal/proxy/handler.test.ts +173 -0
  265. package/src/internal/proxy/index.ts +6 -0
  266. package/src/internal/proxy/make-object.ts +113 -0
  267. package/src/internal/proxy/schema.test.ts +137 -0
  268. package/src/internal/proxy/typed-handler.test.ts +102 -0
  269. package/src/internal/proxy/typed-handler.ts +233 -0
  270. package/src/internal/proxy/typed-object.test.ts +105 -0
  271. package/src/internal/ref/index.ts +6 -0
  272. package/src/internal/ref/ref-array.ts +39 -0
  273. package/src/internal/ref/ref.test.ts +101 -0
  274. package/src/internal/ref/ref.ts +525 -0
  275. package/src/internal/schema/compose.test.ts +42 -0
  276. package/src/internal/schema/compose.ts +37 -0
  277. package/src/internal/schema/echo-schema.ts +385 -0
  278. package/src/internal/schema/index.ts +9 -0
  279. package/src/internal/schema/manipulation.ts +92 -0
  280. package/src/internal/schema/persistent-schema.ts +28 -0
  281. package/src/internal/schema/runtime-schema-registry.ts +78 -0
  282. package/src/internal/schema/snapshot.ts +25 -0
  283. package/src/internal/types/base.ts +58 -0
  284. package/src/internal/types/entity.ts +23 -0
  285. package/src/internal/types/index.ts +9 -0
  286. package/src/internal/types/meta.ts +76 -0
  287. package/src/internal/types/typename.ts +45 -0
  288. package/src/internal/types/version.ts +20 -0
  289. package/src/query/filter.ts +455 -0
  290. package/src/query/index.ts +5 -1
  291. package/src/query/order.ts +34 -0
  292. package/src/query/query.test.ts +334 -0
  293. package/src/query/query.ts +303 -0
  294. package/src/query/testing.ts +64 -0
  295. package/src/query/types.ts +23 -0
  296. package/src/query/util.ts +25 -0
  297. package/src/testing/api.test.ts +100 -0
  298. package/src/testing/index.ts +3 -1
  299. package/src/testing/test-data.ts +130 -0
  300. package/src/testing/test-schema.ts +213 -0
  301. package/src/testing/util.ts +78 -0
  302. package/dist/lib/browser/chunk-2KNTYMIW.mjs +0 -697
  303. package/dist/lib/browser/chunk-2KNTYMIW.mjs.map +0 -7
  304. package/dist/lib/node-esm/chunk-EXNNC62J.mjs +0 -697
  305. package/dist/lib/node-esm/chunk-EXNNC62J.mjs.map +0 -7
  306. package/dist/types/src/experimental/database.d.ts +0 -8
  307. package/dist/types/src/experimental/database.d.ts.map +0 -1
  308. package/dist/types/src/experimental/index.d.ts +0 -1
  309. package/dist/types/src/experimental/index.d.ts.map +0 -1
  310. package/dist/types/src/experimental/queue.d.ts +0 -8
  311. package/dist/types/src/experimental/queue.d.ts.map +0 -1
  312. package/dist/types/src/experimental/space.d.ts +0 -8
  313. package/dist/types/src/experimental/space.d.ts.map +0 -1
  314. package/dist/types/src/query/dsl.d.ts +0 -218
  315. package/dist/types/src/query/dsl.d.ts.map +0 -1
  316. package/dist/types/src/query/dsl.test.d.ts +0 -2
  317. package/dist/types/src/query/dsl.test.d.ts.map +0 -1
  318. package/dist/types/src/testing/types.d.ts +0 -113
  319. package/dist/types/src/testing/types.d.ts.map +0 -1
  320. package/src/experimental/database.ts +0 -11
  321. package/src/experimental/index.ts +0 -7
  322. package/src/experimental/queue.ts +0 -11
  323. package/src/experimental/space.ts +0 -11
  324. package/src/query/dsl.test.ts +0 -324
  325. package/src/query/dsl.ts +0 -646
  326. package/src/test/api.test.ts +0 -173
  327. package/src/testing/types.ts +0 -91
  328. /package/dist/types/src/{test → testing}/api.test.d.ts +0 -0
@@ -0,0 +1,186 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import * as Schema from 'effect/Schema';
6
+ import { describe, expect, test } from 'vitest';
7
+
8
+ import { getDeep } from '@dxos/util';
9
+
10
+ import { SchemaValidator } from './schema-validator';
11
+
12
+ describe('schema-validator', () => {
13
+ describe('validateSchema', () => {
14
+ test('throws on ambiguous discriminated type union', () => {
15
+ const TestSchema = Schema.Struct({
16
+ union: Schema.Union(Schema.Struct({ a: Schema.Number }), Schema.Struct({ b: Schema.String })),
17
+ });
18
+
19
+ expect(() => SchemaValidator.validateSchema(TestSchema)).to.throw();
20
+ });
21
+ });
22
+
23
+ describe('hasPropertyAnnotation', () => {
24
+ test('has annotation', () => {
25
+ const annotationId = Symbol('foo');
26
+ const annotationValue = 'bar';
27
+ const TestSchema: Schema.Schema.AnyNoContext = Schema.Struct({
28
+ name: Schema.String.annotations({ [annotationId]: annotationValue }),
29
+ parent: Schema.optional(Schema.suspend(() => TestSchema.annotations({ [annotationId]: annotationValue }))),
30
+ friends: Schema.suspend(() =>
31
+ Schema.mutable(Schema.Array(TestSchema.annotations({ [annotationId]: annotationValue }))),
32
+ ),
33
+ });
34
+ expect(SchemaValidator.hasTypeAnnotation(TestSchema, 'name', annotationId)).to.be.true;
35
+ expect(SchemaValidator.hasTypeAnnotation(TestSchema, 'parent', annotationId)).to.be.true;
36
+ expect(SchemaValidator.hasTypeAnnotation(TestSchema, 'friends', annotationId)).to.be.true;
37
+ });
38
+
39
+ test('no annotation', () => {
40
+ const annotationId = Symbol('foo');
41
+ const Person: Schema.Schema.AnyNoContext = Schema.Struct({
42
+ name: Schema.String,
43
+ parent: Schema.optional(Schema.suspend(() => Person)),
44
+ friends: Schema.suspend(() => Schema.mutable(Schema.Array(Person))),
45
+ });
46
+ expect(SchemaValidator.hasTypeAnnotation(Person, 'name', annotationId)).to.be.false;
47
+ expect(SchemaValidator.hasTypeAnnotation(Person, 'parent', annotationId)).to.be.false;
48
+ expect(SchemaValidator.hasTypeAnnotation(Person, 'friends', annotationId)).to.be.false;
49
+ });
50
+ });
51
+
52
+ describe('getPropertySchema', () => {
53
+ const validateValueToAssign = (args: {
54
+ schema: Schema.Schema.AnyNoContext;
55
+ target: any;
56
+ path: string[];
57
+ valueToAssign: any;
58
+ expectToThrow?: boolean;
59
+ }) => {
60
+ const expectation = expect(() => {
61
+ const nestedSchema = SchemaValidator.getPropertySchema(args.schema, args.path, (path) => {
62
+ return getDeep(args.target, path);
63
+ });
64
+ Schema.validateSync(nestedSchema)(args.valueToAssign);
65
+ });
66
+ if (args.expectToThrow) {
67
+ expectation.to.throw();
68
+ } else {
69
+ expectation.not.to.throw();
70
+ }
71
+ };
72
+
73
+ test('basic', () => {
74
+ for (const value of [42, '42']) {
75
+ validateValueToAssign({
76
+ schema: Schema.Struct({ object: Schema.Struct({ field: Schema.Number }) }),
77
+ target: {},
78
+ path: ['object', 'field'],
79
+ valueToAssign: value,
80
+ expectToThrow: typeof value !== 'number',
81
+ });
82
+ }
83
+ });
84
+
85
+ test('preserves annotations', () => {
86
+ const annotationId = Symbol('foo');
87
+ const annotationValue = 'bar';
88
+ const Person: Schema.Schema.AnyNoContext = Schema.Struct({
89
+ parent: Schema.optional(Schema.suspend(() => Person.annotations({ [annotationId]: annotationValue }))),
90
+ friends: Schema.suspend(() =>
91
+ Schema.mutable(Schema.Array(Person.annotations({ [annotationId]: annotationValue }))),
92
+ ),
93
+ });
94
+ expect(SchemaValidator.getPropertySchema(Person, ['parent']).ast.annotations[annotationId]).to.eq(
95
+ annotationValue,
96
+ );
97
+ expect(SchemaValidator.getPropertySchema(Person, ['friends', '0']).ast.annotations[annotationId]).to.eq(
98
+ annotationValue,
99
+ );
100
+ });
101
+
102
+ test('discriminated union', () => {
103
+ const square = Schema.Struct({ type: Schema.Literal('square'), side: Schema.Number });
104
+ const circle = Schema.Struct({ type: Schema.Literal('circle'), radius: Schema.Number });
105
+ const shape = Schema.Union(square, circle);
106
+ validateValueToAssign({
107
+ schema: shape,
108
+ target: { type: 'square' },
109
+ path: ['side'],
110
+ valueToAssign: 1,
111
+ });
112
+ validateValueToAssign({
113
+ schema: shape,
114
+ target: { type: 'circle' },
115
+ path: ['side'],
116
+ valueToAssign: 1,
117
+ expectToThrow: true,
118
+ });
119
+ validateValueToAssign({
120
+ schema: shape,
121
+ target: { type: 'square' },
122
+ path: ['radius'],
123
+ valueToAssign: 1,
124
+ expectToThrow: true,
125
+ });
126
+ });
127
+
128
+ test('any', () => {
129
+ validateValueToAssign({
130
+ schema: Schema.Any,
131
+ target: { field: { nested: { value: Schema.Number } } },
132
+ path: ['field', 'nested'],
133
+ valueToAssign: { any: 'value' },
134
+ });
135
+ });
136
+ test('index signatures', () => {
137
+ for (const value of [42, '42']) {
138
+ validateValueToAssign({
139
+ schema: Schema.Struct({ field: Schema.String }, { key: Schema.String, value: Schema.Number }),
140
+ target: {},
141
+ path: ['unknownField'],
142
+ valueToAssign: value,
143
+ expectToThrow: typeof value !== 'number',
144
+ });
145
+ }
146
+ });
147
+
148
+ test('index signature from optional record', () => {
149
+ for (const value of [42, '42']) {
150
+ validateValueToAssign({
151
+ schema: Schema.Struct({
152
+ field: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Number })),
153
+ }),
154
+ target: {},
155
+ path: ['field', 'unknownField'],
156
+ valueToAssign: value,
157
+ expectToThrow: typeof value !== 'number',
158
+ });
159
+ }
160
+ });
161
+
162
+ test('suspend', () => {
163
+ const schemaWithSuspend = Schema.Struct({
164
+ array: Schema.optional(Schema.suspend(() => Schema.Array(Schema.Union(Schema.Null, Schema.Number)))),
165
+ object: Schema.optional(
166
+ Schema.suspend(() => Schema.Union(Schema.Null, Schema.Struct({ field: Schema.Number }))),
167
+ ),
168
+ });
169
+ const target: any = { array: [1, 2, null], object: { field: 3 } };
170
+ for (const value of [42, '42']) {
171
+ for (const path of [
172
+ ['array', '0'],
173
+ ['object', 'field'],
174
+ ]) {
175
+ validateValueToAssign({
176
+ schema: schemaWithSuspend,
177
+ target,
178
+ path,
179
+ valueToAssign: value,
180
+ expectToThrow: typeof value !== 'number',
181
+ });
182
+ }
183
+ }
184
+ });
185
+ });
186
+ });
@@ -0,0 +1,242 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import * as Schema from 'effect/Schema';
6
+ import * as SchemaAST from 'effect/SchemaAST';
7
+
8
+ import { invariant } from '@dxos/invariant';
9
+
10
+ import { SchemaId } from '../types';
11
+
12
+ // TODO(burdon): Reconcile with @dxos/effect visit().
13
+
14
+ export class SchemaValidator {
15
+ /**
16
+ * Recursively check that schema specifies constructions we can handle.
17
+ * Validates there are no ambiguous discriminated union types.
18
+ */
19
+ public static validateSchema(schema: Schema.Schema.AnyNoContext): void {
20
+ const visitAll = (nodes: SchemaAST.AST[]) => nodes.forEach((node) => this.validateSchema(Schema.make(node)));
21
+ if (SchemaAST.isUnion(schema.ast)) {
22
+ const typeAstList = schema.ast.types.filter((type) => SchemaAST.isTypeLiteral(type)) as SchemaAST.TypeLiteral[];
23
+ // Check we can handle a discriminated union.
24
+ if (typeAstList.length > 1) {
25
+ getTypeDiscriminators(typeAstList);
26
+ }
27
+ visitAll(typeAstList);
28
+ } else if (SchemaAST.isTupleType(schema.ast)) {
29
+ const positionalTypes = schema.ast.elements.map((t) => t.type);
30
+ const allTypes = positionalTypes.concat(schema.ast.rest.map((t) => t.type));
31
+ visitAll(allTypes);
32
+ } else if (SchemaAST.isTypeLiteral(schema.ast)) {
33
+ visitAll(SchemaAST.getPropertySignatures(schema.ast).map((p) => p.type));
34
+ }
35
+ }
36
+
37
+ public static hasTypeAnnotation(
38
+ rootObjectSchema: Schema.Schema.AnyNoContext,
39
+ property: string,
40
+ annotation: symbol,
41
+ ): boolean {
42
+ try {
43
+ let type = this.getPropertySchema(rootObjectSchema, [property]);
44
+ if (SchemaAST.isTupleType(type.ast)) {
45
+ type = this.getPropertySchema(rootObjectSchema, [property, '0']);
46
+ }
47
+
48
+ return type.ast.annotations[annotation] != null;
49
+ } catch {
50
+ return false;
51
+ }
52
+ }
53
+
54
+ public static getPropertySchema(
55
+ rootObjectSchema: Schema.Schema.AnyNoContext,
56
+ propertyPath: KeyPath,
57
+ getProperty: (path: KeyPath) => any = () => null,
58
+ ): Schema.Schema.AnyNoContext {
59
+ let schema: Schema.Schema.AnyNoContext = rootObjectSchema;
60
+ for (let i = 0; i < propertyPath.length; i++) {
61
+ const propertyName = propertyPath[i];
62
+ const tupleAst = unwrapArray(schema.ast);
63
+ if (tupleAst != null) {
64
+ schema = getArrayElementSchema(tupleAst, propertyName);
65
+ } else {
66
+ const propertyType = getPropertyType(schema.ast, propertyName.toString(), (propertyName) =>
67
+ getProperty([...propertyPath.slice(0, i), propertyName]),
68
+ );
69
+ if (propertyType == null) {
70
+ throw new TypeError(`unknown property: ${String(propertyName)} on object. Path: ${propertyPath}`);
71
+ }
72
+
73
+ schema = Schema.make(propertyType).annotations(propertyType.annotations);
74
+ }
75
+ }
76
+
77
+ return schema;
78
+ }
79
+
80
+ public static getTargetPropertySchema(target: any, prop: string | symbol): Schema.Schema.AnyNoContext {
81
+ const schema: Schema.Schema.AnyNoContext | undefined = (target as any)[SchemaId];
82
+ invariant(schema, 'target has no schema');
83
+ const arrayAst = unwrapArray(schema.ast);
84
+ if (arrayAst != null) {
85
+ return getArrayElementSchema(arrayAst, prop);
86
+ }
87
+
88
+ const propertyType = getPropertyType(schema.ast, prop.toString(), (prop) => target[prop]);
89
+ if (propertyType == null) {
90
+ return Schema.Any; // TODO(burdon): HACK.
91
+ }
92
+
93
+ invariant(propertyType, `invalid property: ${prop.toString()}`);
94
+ return Schema.make(propertyType);
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Tuple AST is used both for:
100
+ * fixed-length tuples ([string, number]) in which case AST will be { elements: [Schema.String, Schema.Number] }
101
+ * variable-length arrays (Array<string | number>) in which case AST will be { rest: [Schema.Union(Schema.String, Schema.Number)] }
102
+ */
103
+ const getArrayElementSchema = (
104
+ tupleAst: SchemaAST.TupleType,
105
+ property: string | symbol | number,
106
+ ): Schema.Schema.AnyNoContext => {
107
+ const elementIndex = typeof property === 'string' ? parseInt(property, 10) : Number.NaN;
108
+ if (Number.isNaN(elementIndex)) {
109
+ invariant(property === 'length', `invalid array property: ${String(property)}`);
110
+ return Schema.Number;
111
+ }
112
+ if (elementIndex < tupleAst.elements.length) {
113
+ const elementType = tupleAst.elements[elementIndex].type;
114
+ return Schema.make(elementType).annotations(elementType.annotations);
115
+ }
116
+
117
+ const restType = tupleAst.rest;
118
+ return Schema.make(restType[0].type).annotations(restType[0].annotations);
119
+ };
120
+
121
+ const flattenUnion = (typeAst: SchemaAST.AST): SchemaAST.AST[] =>
122
+ SchemaAST.isUnion(typeAst) ? typeAst.types.flatMap(flattenUnion) : [typeAst];
123
+
124
+ const getProperties = (
125
+ typeAst: SchemaAST.AST,
126
+ getTargetPropertyFn: (propertyName: string) => any,
127
+ ): SchemaAST.PropertySignature[] => {
128
+ const astCandidates = flattenUnion(typeAst);
129
+ const typeAstList = astCandidates.filter((type) => SchemaAST.isTypeLiteral(type)) as SchemaAST.TypeLiteral[];
130
+ if (typeAstList.length === 0) {
131
+ return [];
132
+ }
133
+ if (typeAstList.length === 1) {
134
+ return SchemaAST.getPropertySignatures(typeAstList[0]);
135
+ }
136
+
137
+ const typeDiscriminators = getTypeDiscriminators(typeAstList);
138
+ const targetPropertyValue = getTargetPropertyFn(String(typeDiscriminators[0].name));
139
+ const typeIndex = typeDiscriminators.findIndex((p) => targetPropertyValue === (p.type as SchemaAST.Literal).literal);
140
+ invariant(typeIndex !== -1, 'discriminator field not set on target');
141
+ return SchemaAST.getPropertySignatures(typeAstList[typeIndex]);
142
+ };
143
+
144
+ const getPropertyType = (
145
+ ast: SchemaAST.AST,
146
+ propertyName: string,
147
+ getTargetPropertyFn: (propertyName: string) => any,
148
+ ): SchemaAST.AST | null => {
149
+ const anyOrObject = unwrapAst(
150
+ ast,
151
+ (candidate) => SchemaAST.isAnyKeyword(candidate) || SchemaAST.isObjectKeyword(candidate),
152
+ );
153
+ if (anyOrObject != null) {
154
+ return ast;
155
+ }
156
+
157
+ const typeOrDiscriminatedUnion = unwrapAst(ast, (t) => {
158
+ return SchemaAST.isTypeLiteral(t) || (SchemaAST.isUnion(t) && t.types.some((t) => SchemaAST.isTypeLiteral(t)));
159
+ });
160
+ if (typeOrDiscriminatedUnion == null) {
161
+ return null;
162
+ }
163
+
164
+ const targetProperty = getProperties(typeOrDiscriminatedUnion, getTargetPropertyFn).find(
165
+ (p) => p.name === propertyName,
166
+ );
167
+ if (targetProperty != null) {
168
+ return unwrapAst(targetProperty.type);
169
+ }
170
+
171
+ const indexSignatureType = unwrapAst(ast, SchemaAST.isTypeLiteral);
172
+ if (
173
+ indexSignatureType &&
174
+ SchemaAST.isTypeLiteral(indexSignatureType) &&
175
+ indexSignatureType.indexSignatures.length > 0
176
+ ) {
177
+ return unwrapAst(indexSignatureType.indexSignatures[0].type);
178
+ }
179
+
180
+ return null;
181
+ };
182
+
183
+ const getTypeDiscriminators = (typeAstList: SchemaAST.TypeLiteral[]): SchemaAST.PropertySignature[] => {
184
+ const discriminatorPropCandidates = typeAstList
185
+ .flatMap(SchemaAST.getPropertySignatures)
186
+ .filter((p) => SchemaAST.isLiteral(p.type));
187
+ const propertyName = discriminatorPropCandidates[0].name;
188
+ const isValidDiscriminator = discriminatorPropCandidates.every((p) => p.name === propertyName && !p.isOptional);
189
+ const everyTypeHasDiscriminator = discriminatorPropCandidates.length === typeAstList.length;
190
+ const isDiscriminatedUnion = isValidDiscriminator && everyTypeHasDiscriminator;
191
+ invariant(isDiscriminatedUnion, 'type ambiguity: every type in a union must have a single unique-literal field');
192
+ return discriminatorPropCandidates;
193
+ };
194
+
195
+ /**
196
+ * Used to check that rootAst is for a type matching the provided predicate.
197
+ * That's not always straightforward because types of optionality and recursive types.
198
+ * const Task = Schema.Struct({
199
+ * ...,
200
+ * previous?: Schema.optional(Schema.suspend(() => Task)),
201
+ * });
202
+ * Here the AST for `previous` field is going to be Union(Suspend(Type), Undefined).
203
+ * SchemaAST.isTypeLiteral(field) will return false, but unwrapAst(field, (ast) => SchemaAST.isTypeLiteral(ast))
204
+ * will return true.
205
+ */
206
+ const unwrapAst = (rootAst: SchemaAST.AST, predicate?: (ast: SchemaAST.AST) => boolean): SchemaAST.AST | null => {
207
+ let ast: SchemaAST.AST | undefined = rootAst;
208
+ while (ast != null) {
209
+ if (predicate?.(ast)) {
210
+ return ast;
211
+ }
212
+
213
+ if (SchemaAST.isUnion(ast)) {
214
+ const next: any = ast.types.find((t) => (predicate != null && predicate(t)) || SchemaAST.isSuspend(t));
215
+ if (next != null) {
216
+ ast = next;
217
+ continue;
218
+ }
219
+ }
220
+
221
+ if (SchemaAST.isSuspend(ast)) {
222
+ ast = ast.f();
223
+ } else {
224
+ return predicate == null ? ast : null;
225
+ }
226
+ }
227
+
228
+ return null;
229
+ };
230
+
231
+ const unwrapArray = (ast: SchemaAST.AST) => unwrapAst(ast, SchemaAST.isTupleType) as SchemaAST.TupleType | null;
232
+
233
+ export const checkIdNotPresentOnSchema = (schema: Schema.Schema<any, any, any>) => {
234
+ invariant(SchemaAST.isTypeLiteral(schema.ast));
235
+ const idProperty = SchemaAST.getPropertySignatures(schema.ast).find((prop) => prop.name === 'id');
236
+ if (idProperty != null) {
237
+ throw new Error('"id" property name is reserved');
238
+ }
239
+ };
240
+
241
+ // TODO(burdon): Reconcile with JsonPath.
242
+ type KeyPath = readonly (string | number)[];
@@ -0,0 +1,34 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import * as Schema from 'effect/Schema';
6
+ import * as SchemaAST from 'effect/SchemaAST';
7
+ import { describe, expect, test } from 'vitest';
8
+
9
+ import { TypedObject } from './typed-object';
10
+
11
+ class Organization extends TypedObject({
12
+ typename: 'example.com/type/Organization',
13
+ version: '0.1.0',
14
+ })({
15
+ name: Schema.String,
16
+ }) {}
17
+
18
+ describe('EchoObjectSchema class DSL', () => {
19
+ test('type is a valid schema', async () => {
20
+ expect(Schema.isSchema(Organization)).to.be.true;
21
+ });
22
+
23
+ test('static typename accessor', async () => {
24
+ expect(Organization.typename).to.eq('example.com/type/Organization');
25
+ });
26
+
27
+ test('expect constructor to throw', async () => {
28
+ expect(() => new Organization()).to.throw();
29
+ });
30
+
31
+ test('expect schema', async () => {
32
+ expect(SchemaAST.isTypeLiteral(Organization.ast)).to.be.true;
33
+ });
34
+ });
@@ -0,0 +1,94 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import * as Schema from 'effect/Schema';
6
+ import * as SchemaAST from 'effect/SchemaAST';
7
+
8
+ import { invariant } from '@dxos/invariant';
9
+
10
+ import {
11
+ type TypeAnnotation,
12
+ TypeAnnotationId,
13
+ type TypeMeta,
14
+ TypenameSchema,
15
+ VersionSchema,
16
+ makeTypeJsonSchemaAnnotation,
17
+ } from '../annotations';
18
+ import { EntityKind, type HasId } from '../types';
19
+
20
+ import { type TypedObjectFields, type TypedObjectOptions, makeTypedEntityClass } from './common';
21
+
22
+ /**
23
+ * Definition for an object type that can be stored in an ECHO database.
24
+ * Implements effect schema to define object properties.
25
+ * Has a typename and version.
26
+ *
27
+ * In contrast to {@link EchoSchema} this definition is not recorded in the database.
28
+ */
29
+ export interface TypedObject<A = any, I = any> extends TypeMeta, Schema.Schema<A, I> {}
30
+
31
+ /**
32
+ * Typed object that could be used as a prototype in class definitions.
33
+ * This is an internal API type.
34
+ * Use {@link TypedObject} for the common use-cases.
35
+ */
36
+ export interface TypedObjectPrototype<A = any, I = any> extends TypedObject<A, I> {
37
+ /** Type constructor. */
38
+ new (): HasId & A;
39
+ }
40
+
41
+ export type TypedObjectProps = TypeMeta & {
42
+ // TODO(dmaretskyi): Remove after all legacy types has been removed.
43
+ disableValidation?: boolean;
44
+ };
45
+
46
+ /**
47
+ * Base class factory for typed objects.
48
+ * @deprecated Use Function.pipe(Type.Obj) instead.
49
+ */
50
+ export const TypedObject = ({
51
+ typename: typenameParam,
52
+ version: versionParam,
53
+ disableValidation,
54
+ }: TypedObjectProps) => {
55
+ const typename = TypenameSchema.make(typenameParam, { disableValidation });
56
+ const version = VersionSchema.make(versionParam, { disableValidation });
57
+
58
+ /**
59
+ * Return class definition factory.
60
+ */
61
+ return <SchemaFields extends Schema.Struct.Fields, Options extends TypedObjectOptions>(
62
+ fields: SchemaFields,
63
+ options?: Options,
64
+ ): TypedObjectPrototype<TypedObjectFields<SchemaFields, Options>, Schema.Struct.Encoded<SchemaFields>> => {
65
+ // Create schema from fields.
66
+ const schema: Schema.Schema.All = options?.record
67
+ ? Schema.Struct(fields, { key: Schema.String, value: Schema.Any })
68
+ : Schema.Struct(fields);
69
+
70
+ // Set ECHO object id property.
71
+ const typeSchema = Schema.extend(
72
+ Schema.mutable(options?.partial ? Schema.partial(schema) : schema),
73
+ Schema.Struct({ id: Schema.String }),
74
+ );
75
+
76
+ // Set ECHO annotations.
77
+ invariant(typeof EntityKind.Object === 'string');
78
+ const annotatedSchema = typeSchema.annotations({
79
+ [TypeAnnotationId]: { kind: EntityKind.Object, typename, version } satisfies TypeAnnotation,
80
+ [SchemaAST.JSONSchemaAnnotationId]: makeTypeJsonSchemaAnnotation({
81
+ kind: EntityKind.Object,
82
+ typename,
83
+ version,
84
+ }),
85
+ });
86
+
87
+ /**
88
+ * Return class definition.
89
+ * NOTE: Actual reactive ECHO objects must be created via the `live(Type)` function.
90
+ */
91
+ // TODO(burdon): This is missing fields required by TypedObject (e.g., Type, Encoded, Context)?
92
+ return class TypedObject extends makeTypedEntityClass(typename, version, annotatedSchema as any) {} as any;
93
+ };
94
+ };