@dxos/echo 0.9.1-main.c7dcc2e112 → 0.10.0

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/neutral/Annotation.mjs +22 -8
  2. package/dist/lib/neutral/Database.mjs +3 -3
  3. package/dist/lib/neutral/Entity.mjs +11 -11
  4. package/dist/lib/neutral/Err.mjs +7 -3
  5. package/dist/lib/neutral/Feed.mjs +15 -19
  6. package/dist/lib/neutral/Filter.mjs +13 -11
  7. package/dist/lib/neutral/Format.mjs +3 -7
  8. package/dist/lib/neutral/JsonSchema.mjs +7 -7
  9. package/dist/lib/neutral/Migration.mjs +11 -11
  10. package/dist/lib/neutral/Obj.mjs +15 -13
  11. package/dist/lib/neutral/Order.mjs +1 -1
  12. package/dist/lib/neutral/Query.mjs +17 -17
  13. package/dist/lib/neutral/QueryResult.mjs +1 -1
  14. package/dist/lib/neutral/Ref.mjs +6 -6
  15. package/dist/lib/neutral/Registry.mjs +1 -1
  16. package/dist/lib/neutral/Relation.mjs +12 -12
  17. package/dist/lib/neutral/Tag.mjs +15 -15
  18. package/dist/lib/neutral/Text.mjs +25 -0
  19. package/dist/lib/neutral/Text.mjs.map +7 -0
  20. package/dist/lib/neutral/Type.mjs +10 -10
  21. package/dist/lib/neutral/chunk-2HKGAQM4.mjs +1394 -0
  22. package/dist/lib/neutral/chunk-2HKGAQM4.mjs.map +7 -0
  23. package/dist/lib/neutral/{chunk-O6BH7EPN.mjs → chunk-2ZHECXUR.mjs} +5 -4
  24. package/dist/lib/neutral/chunk-2ZHECXUR.mjs.map +7 -0
  25. package/dist/lib/neutral/{chunk-AFANXSGV.mjs → chunk-3KTE2Y7Q.mjs} +20 -20
  26. package/dist/lib/neutral/chunk-3KTE2Y7Q.mjs.map +7 -0
  27. package/dist/lib/neutral/{chunk-WZ32BF53.mjs → chunk-3TN2VMI4.mjs} +49 -37
  28. package/dist/lib/neutral/chunk-3TN2VMI4.mjs.map +7 -0
  29. package/dist/lib/neutral/{chunk-E7IOTOZD.mjs → chunk-42L4HJ3Z.mjs} +11 -15
  30. package/dist/lib/neutral/chunk-42L4HJ3Z.mjs.map +7 -0
  31. package/dist/lib/neutral/{chunk-K3S7D5PR.mjs → chunk-4A2NWKRJ.mjs} +5 -5
  32. package/dist/lib/neutral/chunk-ACFAOTKJ.mjs +1531 -0
  33. package/dist/lib/neutral/chunk-ACFAOTKJ.mjs.map +7 -0
  34. package/dist/lib/neutral/{chunk-Z3WMRRAZ.mjs → chunk-BEFR7LEA.mjs} +11 -9
  35. package/dist/lib/neutral/{chunk-Z3WMRRAZ.mjs.map → chunk-BEFR7LEA.mjs.map} +1 -1
  36. package/dist/lib/neutral/{chunk-6M2Z6WBH.mjs → chunk-BXIKXT7Q.mjs} +2 -6
  37. package/dist/lib/neutral/{chunk-6M2Z6WBH.mjs.map → chunk-BXIKXT7Q.mjs.map} +1 -1
  38. package/dist/lib/neutral/{chunk-3JXQUMUI.mjs → chunk-C57XMMBY.mjs} +21 -20
  39. package/dist/lib/neutral/chunk-C57XMMBY.mjs.map +7 -0
  40. package/dist/lib/neutral/{chunk-JZ6LFQU5.mjs → chunk-C62JYFAD.mjs} +82 -8
  41. package/dist/lib/neutral/chunk-C62JYFAD.mjs.map +7 -0
  42. package/dist/lib/neutral/{chunk-TF5BSLF6.mjs → chunk-FSGX2R62.mjs} +3 -3
  43. package/dist/lib/neutral/chunk-FSGX2R62.mjs.map +7 -0
  44. package/dist/lib/neutral/{chunk-6CEISA3V.mjs → chunk-HBXT3GSB.mjs} +29 -15
  45. package/dist/lib/neutral/chunk-HBXT3GSB.mjs.map +7 -0
  46. package/dist/lib/neutral/{chunk-WU3GIANS.mjs → chunk-IQR45HAD.mjs} +1 -1
  47. package/dist/lib/neutral/chunk-IQR45HAD.mjs.map +7 -0
  48. package/dist/lib/neutral/{chunk-TFEWTY5A.mjs → chunk-KCBW4BR6.mjs} +4 -8
  49. package/dist/lib/neutral/chunk-KCBW4BR6.mjs.map +7 -0
  50. package/dist/lib/neutral/{chunk-4TPQZPLY.mjs → chunk-L3E5IXAE.mjs} +198 -30
  51. package/dist/lib/neutral/chunk-L3E5IXAE.mjs.map +7 -0
  52. package/dist/lib/neutral/{chunk-EVK6XBXO.mjs → chunk-PRC4FEKJ.mjs} +2 -2
  53. package/dist/lib/neutral/chunk-PRC4FEKJ.mjs.map +7 -0
  54. package/dist/lib/neutral/{chunk-NOL7O3BL.mjs → chunk-SCT6FFCR.mjs} +9 -4
  55. package/dist/lib/neutral/chunk-SCT6FFCR.mjs.map +7 -0
  56. package/dist/lib/neutral/{chunk-BBFJWWAV.mjs → chunk-TOCVVFK2.mjs} +26 -2
  57. package/dist/lib/neutral/chunk-TOCVVFK2.mjs.map +7 -0
  58. package/dist/lib/neutral/{chunk-A53NTBHE.mjs → chunk-U53LSQQA.mjs} +3 -3
  59. package/dist/lib/neutral/{chunk-A53NTBHE.mjs.map → chunk-U53LSQQA.mjs.map} +1 -1
  60. package/dist/lib/neutral/{chunk-PLDMJJJC.mjs → chunk-UGFFV2FR.mjs} +2 -2
  61. package/dist/lib/neutral/{chunk-35INCYOE.mjs → chunk-UWLP4RFM.mjs} +1 -1
  62. package/dist/lib/neutral/chunk-UWLP4RFM.mjs.map +7 -0
  63. package/dist/lib/neutral/{chunk-SZ5WB6HF.mjs → chunk-UZHCQ6NI.mjs} +13 -11
  64. package/dist/lib/neutral/{chunk-SZ5WB6HF.mjs.map → chunk-UZHCQ6NI.mjs.map} +3 -3
  65. package/dist/lib/neutral/{chunk-L3OBWBV2.mjs → chunk-WKIT2YY5.mjs} +2 -2
  66. package/dist/lib/neutral/{chunk-HGWQR5KH.mjs → chunk-XB6ISRAO.mjs} +27 -15
  67. package/dist/lib/neutral/chunk-XB6ISRAO.mjs.map +7 -0
  68. package/dist/lib/neutral/chunk-XXL42WCB.mjs +84 -0
  69. package/dist/lib/neutral/chunk-XXL42WCB.mjs.map +7 -0
  70. package/dist/lib/neutral/{chunk-5GAIWRQB.mjs → chunk-XXWK4J53.mjs} +27 -42
  71. package/dist/lib/neutral/chunk-XXWK4J53.mjs.map +7 -0
  72. package/dist/lib/neutral/index.mjs +30 -26
  73. package/dist/lib/neutral/internal/index.mjs +91 -69
  74. package/dist/lib/neutral/meta.json +1 -1
  75. package/dist/lib/neutral/testing/index.mjs +79 -60
  76. package/dist/lib/neutral/testing/index.mjs.map +3 -3
  77. package/dist/types/src/Annotation.d.ts +35 -1
  78. package/dist/types/src/Annotation.d.ts.map +1 -1
  79. package/dist/types/src/Collection.d.ts +9 -8
  80. package/dist/types/src/Collection.d.ts.map +1 -1
  81. package/dist/types/src/Database.d.ts +16 -0
  82. package/dist/types/src/Database.d.ts.map +1 -1
  83. package/dist/types/src/Dataset.d.ts +1 -16
  84. package/dist/types/src/Dataset.d.ts.map +1 -1
  85. package/dist/types/src/Err.d.ts +67 -0
  86. package/dist/types/src/Err.d.ts.map +1 -1
  87. package/dist/types/src/Feed.d.ts +56 -80
  88. package/dist/types/src/Feed.d.ts.map +1 -1
  89. package/dist/types/src/Filter.d.ts +9 -1
  90. package/dist/types/src/Filter.d.ts.map +1 -1
  91. package/dist/types/src/Hypergraph.d.ts +2 -9
  92. package/dist/types/src/Hypergraph.d.ts.map +1 -1
  93. package/dist/types/src/Migration.d.ts +1 -1
  94. package/dist/types/src/Migration.d.ts.map +1 -1
  95. package/dist/types/src/Obj.d.ts +31 -6
  96. package/dist/types/src/Obj.d.ts.map +1 -1
  97. package/dist/types/src/Order.d.ts +1 -1
  98. package/dist/types/src/Order.d.ts.map +1 -1
  99. package/dist/types/src/Query.d.ts +1 -1
  100. package/dist/types/src/Query.d.ts.map +1 -1
  101. package/dist/types/src/QueryResult.d.ts +2 -2
  102. package/dist/types/src/QueryResult.d.ts.map +1 -1
  103. package/dist/types/src/Ref.d.ts +8 -4
  104. package/dist/types/src/Ref.d.ts.map +1 -1
  105. package/dist/types/src/Registry.d.ts +1 -1
  106. package/dist/types/src/Relation.d.ts.map +1 -1
  107. package/dist/types/src/Tag.d.ts +11 -13
  108. package/dist/types/src/Tag.d.ts.map +1 -1
  109. package/dist/types/src/Text.d.ts +41 -0
  110. package/dist/types/src/Text.d.ts.map +1 -0
  111. package/dist/types/src/Text.test.d.ts +2 -0
  112. package/dist/types/src/Text.test.d.ts.map +1 -0
  113. package/dist/types/src/Type.d.ts +33 -20
  114. package/dist/types/src/Type.d.ts.map +1 -1
  115. package/dist/types/src/View.d.ts +8 -9
  116. package/dist/types/src/View.d.ts.map +1 -1
  117. package/dist/types/src/index.d.ts +1 -0
  118. package/dist/types/src/index.d.ts.map +1 -1
  119. package/dist/types/src/internal/Annotation/annotations.d.ts +9 -0
  120. package/dist/types/src/internal/Annotation/annotations.d.ts.map +1 -1
  121. package/dist/types/src/internal/Annotation/atoms.d.ts +14 -0
  122. package/dist/types/src/internal/Annotation/atoms.d.ts.map +1 -0
  123. package/dist/types/src/internal/Annotation/entity-dictionary.d.ts +13 -0
  124. package/dist/types/src/internal/Annotation/entity-dictionary.d.ts.map +1 -1
  125. package/dist/types/src/internal/Entity/api.d.ts.map +1 -1
  126. package/dist/types/src/internal/Entity/guard.d.ts.map +1 -1
  127. package/dist/types/src/internal/Entity/model.d.ts +1 -1
  128. package/dist/types/src/internal/Entity/model.d.ts.map +1 -1
  129. package/dist/types/src/internal/Entity/object.d.ts +5 -1
  130. package/dist/types/src/internal/Entity/object.d.ts.map +1 -1
  131. package/dist/types/src/internal/Entity/relation.d.ts +7 -0
  132. package/dist/types/src/internal/Entity/relation.d.ts.map +1 -1
  133. package/dist/types/src/internal/Entity/type-uri.d.ts.map +1 -1
  134. package/dist/types/src/internal/Filter/index.d.ts +2 -0
  135. package/dist/types/src/internal/Filter/index.d.ts.map +1 -0
  136. package/dist/types/src/internal/Filter/match.d.ts +22 -0
  137. package/dist/types/src/internal/Filter/match.d.ts.map +1 -0
  138. package/dist/types/src/internal/Format/format.d.ts +0 -2
  139. package/dist/types/src/internal/Format/format.d.ts.map +1 -1
  140. package/dist/types/src/internal/Format/index.d.ts +2 -2
  141. package/dist/types/src/internal/Format/index.d.ts.map +1 -1
  142. package/dist/types/src/internal/Format/string.d.ts +0 -8
  143. package/dist/types/src/internal/Format/string.d.ts.map +1 -1
  144. package/dist/types/src/internal/Format/types.d.ts +2 -1
  145. package/dist/types/src/internal/Format/types.d.ts.map +1 -1
  146. package/dist/types/src/internal/Obj/atoms.d.ts +7 -3
  147. package/dist/types/src/internal/Obj/atoms.d.ts.map +1 -1
  148. package/dist/types/src/internal/Obj/clone.d.ts +2 -1
  149. package/dist/types/src/internal/Obj/clone.d.ts.map +1 -1
  150. package/dist/types/src/internal/Obj/create-object.d.ts +3 -4
  151. package/dist/types/src/internal/Obj/create-object.d.ts.map +1 -1
  152. package/dist/types/src/internal/Obj/json-serializer.d.ts.map +1 -1
  153. package/dist/types/src/internal/Query/index.d.ts +1 -0
  154. package/dist/types/src/internal/Query/index.d.ts.map +1 -1
  155. package/dist/types/src/internal/Query/query-result-effect.d.ts +9 -0
  156. package/dist/types/src/internal/Query/query-result-effect.d.ts.map +1 -0
  157. package/dist/types/src/internal/Ref/atoms.d.ts +1 -2
  158. package/dist/types/src/internal/Ref/atoms.d.ts.map +1 -1
  159. package/dist/types/src/internal/Ref/ref.d.ts +13 -6
  160. package/dist/types/src/internal/Ref/ref.d.ts.map +1 -1
  161. package/dist/types/src/internal/common/atom-snapshot.d.ts +8 -0
  162. package/dist/types/src/internal/common/atom-snapshot.d.ts.map +1 -0
  163. package/dist/types/src/internal/common/proxy/make-object.d.ts.map +1 -1
  164. package/dist/types/src/internal/common/proxy/proxy-types.d.ts +12 -0
  165. package/dist/types/src/internal/common/proxy/proxy-types.d.ts.map +1 -1
  166. package/dist/types/src/internal/common/proxy/proxy-utils.d.ts +25 -0
  167. package/dist/types/src/internal/common/proxy/proxy-utils.d.ts.map +1 -1
  168. package/dist/types/src/internal/common/proxy/schema-validator.d.ts +5 -0
  169. package/dist/types/src/internal/common/proxy/schema-validator.d.ts.map +1 -1
  170. package/dist/types/src/internal/common/proxy/typed-handler.d.ts +17 -0
  171. package/dist/types/src/internal/common/proxy/typed-handler.d.ts.map +1 -1
  172. package/dist/types/src/internal/common/types/entity.d.ts +4 -4
  173. package/dist/types/src/internal/common/types/entity.d.ts.map +1 -1
  174. package/dist/types/src/internal/common/types/meta.d.ts +2 -4
  175. package/dist/types/src/internal/common/types/meta.d.ts.map +1 -1
  176. package/dist/types/src/internal/index.d.ts +1 -0
  177. package/dist/types/src/internal/index.d.ts.map +1 -1
  178. package/dist/types/src/testing/test-schema.d.ts +42 -171
  179. package/dist/types/src/testing/test-schema.d.ts.map +1 -1
  180. package/dist/types/tsconfig.tsbuildinfo +1 -1
  181. package/package.json +20 -13
  182. package/src/Annotation.test.ts +257 -10
  183. package/src/Annotation.ts +55 -6
  184. package/src/Collection.ts +10 -12
  185. package/src/Database.ts +22 -20
  186. package/src/Err.ts +22 -0
  187. package/src/Feed.ts +114 -141
  188. package/src/Filter.test.ts +38 -0
  189. package/src/Filter.ts +17 -3
  190. package/src/Hypergraph.ts +2 -11
  191. package/src/Migration.ts +1 -1
  192. package/src/Obj.test.ts +92 -12
  193. package/src/Obj.ts +36 -5
  194. package/src/Order.ts +3 -3
  195. package/src/Query.test.ts +2 -2
  196. package/src/Query.ts +16 -16
  197. package/src/QueryResult.ts +2 -2
  198. package/src/Ref.ts +12 -4
  199. package/src/Registry.ts +1 -1
  200. package/src/Relation.ts +1 -1
  201. package/src/Tag.ts +12 -16
  202. package/src/Text.test.ts +186 -0
  203. package/src/Text.ts +116 -0
  204. package/src/Type.test.ts +52 -24
  205. package/src/Type.ts +69 -31
  206. package/src/View.ts +22 -30
  207. package/src/index.ts +1 -1
  208. package/src/internal/Annotation/annotations.ts +15 -2
  209. package/src/internal/Annotation/atoms.ts +107 -0
  210. package/src/internal/Annotation/entity-dictionary.ts +40 -37
  211. package/src/internal/Entity/api.ts +1 -1
  212. package/src/internal/Entity/guard.ts +2 -1
  213. package/src/internal/Entity/model.ts +2 -2
  214. package/src/internal/Entity/object.ts +14 -1
  215. package/src/internal/Entity/relation.ts +19 -0
  216. package/src/internal/Entity/type-uri.ts +3 -1
  217. package/src/internal/Filter/index.ts +5 -0
  218. package/src/internal/Filter/match.ts +284 -0
  219. package/src/internal/Format/format.ts +0 -2
  220. package/src/internal/Format/index.ts +2 -2
  221. package/src/internal/Format/string.ts +0 -12
  222. package/src/internal/Format/types.ts +4 -2
  223. package/src/internal/JsonSchema/json-schema.test.ts +82 -54
  224. package/src/internal/Obj/atoms.ts +31 -18
  225. package/src/internal/Obj/clone.ts +61 -20
  226. package/src/internal/Obj/create-object.test.ts +3 -3
  227. package/src/internal/Obj/create-object.ts +3 -4
  228. package/src/internal/Obj/inspect.ts +1 -1
  229. package/src/internal/Obj/json-serializer.test.ts +7 -5
  230. package/src/internal/Obj/json-serializer.ts +1 -1
  231. package/src/internal/Obj/schema-validator.test.ts +53 -0
  232. package/src/internal/Obj/set-value.test.ts +29 -19
  233. package/src/internal/Query/index.ts +1 -0
  234. package/src/internal/Query/query-result-effect.ts +35 -0
  235. package/src/internal/Ref/atoms.ts +23 -3
  236. package/src/internal/Ref/ref.test.ts +8 -8
  237. package/src/internal/Ref/ref.ts +26 -20
  238. package/src/internal/common/atom-snapshot.ts +19 -0
  239. package/src/internal/common/proxy/make-object.ts +1 -1
  240. package/src/internal/common/proxy/proxy-types.ts +20 -0
  241. package/src/internal/common/proxy/proxy-utils.ts +37 -1
  242. package/src/internal/common/proxy/reactive-array.ts +3 -3
  243. package/src/internal/common/proxy/reactive.test.ts +1 -1
  244. package/src/internal/common/proxy/schema-validator.ts +181 -8
  245. package/src/internal/common/proxy/typed-handler.ts +254 -76
  246. package/src/internal/common/types/entity.ts +15 -7
  247. package/src/internal/index.ts +1 -0
  248. package/src/testing/test-schema.ts +75 -90
  249. package/dist/lib/neutral/chunk-35INCYOE.mjs.map +0 -7
  250. package/dist/lib/neutral/chunk-3JXQUMUI.mjs.map +0 -7
  251. package/dist/lib/neutral/chunk-4TPQZPLY.mjs.map +0 -7
  252. package/dist/lib/neutral/chunk-5GAIWRQB.mjs.map +0 -7
  253. package/dist/lib/neutral/chunk-6CEISA3V.mjs.map +0 -7
  254. package/dist/lib/neutral/chunk-AFANXSGV.mjs.map +0 -7
  255. package/dist/lib/neutral/chunk-BBFJWWAV.mjs.map +0 -7
  256. package/dist/lib/neutral/chunk-E7IOTOZD.mjs.map +0 -7
  257. package/dist/lib/neutral/chunk-EVK6XBXO.mjs.map +0 -7
  258. package/dist/lib/neutral/chunk-HGWQR5KH.mjs.map +0 -7
  259. package/dist/lib/neutral/chunk-JZ6LFQU5.mjs.map +0 -7
  260. package/dist/lib/neutral/chunk-NOL7O3BL.mjs.map +0 -7
  261. package/dist/lib/neutral/chunk-O6BH7EPN.mjs.map +0 -7
  262. package/dist/lib/neutral/chunk-TF5BSLF6.mjs.map +0 -7
  263. package/dist/lib/neutral/chunk-TFEWTY5A.mjs.map +0 -7
  264. package/dist/lib/neutral/chunk-VQYL2OQF.mjs +0 -2353
  265. package/dist/lib/neutral/chunk-VQYL2OQF.mjs.map +0 -7
  266. package/dist/lib/neutral/chunk-WU3GIANS.mjs.map +0 -7
  267. package/dist/lib/neutral/chunk-WZ32BF53.mjs.map +0 -7
  268. package/dist/lib/neutral/chunk-X3NUUON4.mjs +0 -184
  269. package/dist/lib/neutral/chunk-X3NUUON4.mjs.map +0 -7
  270. package/dist/types/src/exemplars.test.d.ts +0 -2
  271. package/dist/types/src/exemplars.test.d.ts.map +0 -1
  272. package/dist/types/src/hierarchy.test.d.ts +0 -2
  273. package/dist/types/src/hierarchy.test.d.ts.map +0 -1
  274. package/src/exemplars.test.ts +0 -21
  275. package/src/hierarchy.test.ts +0 -33
  276. /package/dist/lib/neutral/{chunk-K3S7D5PR.mjs.map → chunk-4A2NWKRJ.mjs.map} +0 -0
  277. /package/dist/lib/neutral/{chunk-PLDMJJJC.mjs.map → chunk-UGFFV2FR.mjs.map} +0 -0
  278. /package/dist/lib/neutral/{chunk-L3OBWBV2.mjs.map → chunk-WKIT2YY5.mjs.map} +0 -0
@@ -0,0 +1,186 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { describe, test } from 'vitest';
6
+
7
+ import * as Obj from './Obj';
8
+ import { TestSchema } from './testing';
9
+ import * as Text from './Text';
10
+
11
+ describe('Text', () => {
12
+ describe('update', () => {
13
+ test('replaces the value; readable via getValue', ({ expect }) => {
14
+ const obj = Obj.make(TestSchema.Person, { name: 'hello' });
15
+ Obj.update(obj, () => {
16
+ Text.update(obj, 'name', 'goodbye');
17
+ });
18
+ expect(Obj.getValue(obj, ['name'])).toBe('goodbye');
19
+ });
20
+
21
+ test('accepts a key-path array', ({ expect }) => {
22
+ const obj = Obj.make(TestSchema.Person, { name: 'hello' });
23
+ Obj.update(obj, () => {
24
+ Text.update(obj, ['name'], 'world');
25
+ });
26
+ expect(Obj.getValue(obj, ['name'])).toBe('world');
27
+ });
28
+
29
+ test('updates a nested string field', ({ expect }) => {
30
+ const obj = Obj.make(TestSchema.Person, { name: 'a', address: { city: 'Paris', coordinates: {} } });
31
+ Obj.update(obj, () => {
32
+ Text.update(obj, ['address', 'city'], 'London');
33
+ });
34
+ expect(Obj.getValue(obj, ['address', 'city'])).toBe('London');
35
+ });
36
+ });
37
+
38
+ describe('splice', () => {
39
+ test('inserts at an index', ({ expect }) => {
40
+ const obj = Obj.make(TestSchema.Person, { name: 'helloworld' });
41
+ Obj.update(obj, () => {
42
+ const removed = Text.splice(obj, 'name', 5, 0, ' ');
43
+ expect(removed).toBe('');
44
+ });
45
+ expect(Obj.getValue(obj, ['name'])).toBe('hello world');
46
+ });
47
+
48
+ test('deletes and returns the removed substring', ({ expect }) => {
49
+ const obj = Obj.make(TestSchema.Person, { name: 'hello world' });
50
+ Obj.update(obj, () => {
51
+ const removed = Text.splice(obj, 'name', 5, 6);
52
+ expect(removed).toBe(' world');
53
+ });
54
+ expect(Obj.getValue(obj, ['name'])).toBe('hello');
55
+ });
56
+
57
+ test('replaces in place', ({ expect }) => {
58
+ const obj = Obj.make(TestSchema.Person, { name: 'hello world' });
59
+ Obj.update(obj, () => {
60
+ const removed = Text.splice(obj, 'name', 6, 5, 'there');
61
+ expect(removed).toBe('world');
62
+ });
63
+ expect(Obj.getValue(obj, ['name'])).toBe('hello there');
64
+ });
65
+
66
+ test('start past end appends', ({ expect }) => {
67
+ const obj = Obj.make(TestSchema.Person, { name: 'abc' });
68
+ Obj.update(obj, () => {
69
+ const removed = Text.splice(obj, 'name', 100, 0, 'def');
70
+ expect(removed).toBe('');
71
+ });
72
+ expect(Obj.getValue(obj, ['name'])).toBe('abcdef');
73
+ });
74
+
75
+ test('mirrors Array.splice index semantics', ({ expect }) => {
76
+ const reference = 'hello world';
77
+ const obj = Obj.make(TestSchema.Person, { name: reference });
78
+ let removed = '';
79
+ Obj.update(obj, () => {
80
+ removed = Text.splice(obj, 'name', 0, 5, 'HELLO');
81
+ });
82
+ // Mirror the same operation on a character array.
83
+ const chars = [...reference];
84
+ const removedChars = chars.splice(0, 5, ...'HELLO');
85
+ expect(removed).toBe(removedChars.join(''));
86
+ expect(Obj.getValue(obj, ['name'])).toBe(chars.join(''));
87
+ });
88
+ });
89
+
90
+ describe('apply', () => {
91
+ test('replaces the first occurrence', ({ expect }) => {
92
+ const obj = Obj.make(TestSchema.Person, { name: 'foo foo foo' });
93
+ Obj.update(obj, () => {
94
+ Text.apply(obj, 'name', [{ oldString: 'foo', newString: 'bar' }]);
95
+ });
96
+ expect(Obj.getValue(obj, ['name'])).toBe('bar foo foo');
97
+ });
98
+
99
+ test('replaces all occurrences', ({ expect }) => {
100
+ const obj = Obj.make(TestSchema.Person, { name: 'foo foo foo' });
101
+ Obj.update(obj, () => {
102
+ Text.apply(obj, 'name', [{ oldString: 'foo', newString: 'bar', replaceAll: true }]);
103
+ });
104
+ expect(Obj.getValue(obj, ['name'])).toBe('bar bar bar');
105
+ });
106
+
107
+ test('replaceAll does not re-match inserted text', ({ expect }) => {
108
+ const obj = Obj.make(TestSchema.Person, { name: 'a a a' });
109
+ Obj.update(obj, () => {
110
+ Text.apply(obj, 'name', [{ oldString: 'a', newString: 'aa', replaceAll: true }]);
111
+ });
112
+ expect(Obj.getValue(obj, ['name'])).toBe('aa aa aa');
113
+ });
114
+
115
+ test('appends when oldString is missing', ({ expect }) => {
116
+ const obj = Obj.make(TestSchema.Person, { name: 'hello' });
117
+ Obj.update(obj, () => {
118
+ Text.apply(obj, 'name', [{ newString: ' world' }]);
119
+ });
120
+ expect(Obj.getValue(obj, ['name'])).toBe('hello world');
121
+ });
122
+
123
+ test('appends when oldString is empty', ({ expect }) => {
124
+ const obj = Obj.make(TestSchema.Person, { name: 'hello' });
125
+ Obj.update(obj, () => {
126
+ Text.apply(obj, 'name', [{ oldString: '', newString: '!' }]);
127
+ });
128
+ expect(Obj.getValue(obj, ['name'])).toBe('hello!');
129
+ });
130
+
131
+ test('applies edits sequentially and returns the final text', ({ expect }) => {
132
+ const obj = Obj.make(TestSchema.Person, { name: 'hello' });
133
+ let result = '';
134
+ Obj.update(obj, () => {
135
+ result = Text.apply(obj, 'name', [{ oldString: 'hello', newString: 'hi' }, { newString: ' there' }]);
136
+ });
137
+ expect(result).toBe('hi there');
138
+ expect(Obj.getValue(obj, ['name'])).toBe('hi there');
139
+ });
140
+
141
+ test('throws when a non-replaceAll oldString is not found', ({ expect }) => {
142
+ const obj = Obj.make(TestSchema.Person, { name: 'hello' });
143
+ expect(() =>
144
+ Obj.update(obj, () => {
145
+ Text.apply(obj, 'name', [{ oldString: 'missing', newString: 'x' }]);
146
+ }),
147
+ ).toThrow();
148
+ });
149
+ });
150
+
151
+ describe('strict mode', () => {
152
+ test('update throws outside Obj.update', ({ expect }) => {
153
+ const obj = Obj.make(TestSchema.Person, { name: 'hello' });
154
+ expect(() => Text.update(obj, 'name', 'x')).toThrow(/Obj\.update/);
155
+ });
156
+
157
+ test('splice throws outside Obj.update', ({ expect }) => {
158
+ const obj = Obj.make(TestSchema.Person, { name: 'hello' });
159
+ expect(() => Text.splice(obj, 'name', 0, 1, 'x')).toThrow(/Obj\.update/);
160
+ });
161
+
162
+ test('apply throws outside Obj.update', ({ expect }) => {
163
+ const obj = Obj.make(TestSchema.Person, { name: 'hello' });
164
+ expect(() => Text.apply(obj, 'name', [{ oldString: 'h', newString: 'H' }])).toThrow(/Obj\.update/);
165
+ });
166
+ });
167
+
168
+ describe('reactivity', () => {
169
+ test('a subscriber fires once per Obj.update batch', ({ expect }) => {
170
+ const obj = Obj.make(TestSchema.Person, { name: 'hello' });
171
+ let count = 0;
172
+ const unsubscribe = Obj.subscribe(obj, () => {
173
+ count++;
174
+ });
175
+
176
+ Obj.update(obj, () => {
177
+ Text.splice(obj, 'name', 0, 5, 'goodbye');
178
+ Text.splice(obj, 'name', 0, 0, '!');
179
+ });
180
+
181
+ expect(count).toBe(1);
182
+ expect(Obj.getValue(obj, ['name'])).toBe('!goodbye');
183
+ unsubscribe();
184
+ });
185
+ });
186
+ });
package/src/Text.ts ADDED
@@ -0,0 +1,116 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ // @import-as-namespace
6
+
7
+ import * as Err from './Err';
8
+ import * as internal from './internal';
9
+ import * as Obj from './Obj';
10
+
11
+ /**
12
+ * A key path addressing a string value within an object, mirroring `Obj.getValue` / `Obj.setValue`.
13
+ */
14
+ export type KeyPath = readonly (string | number)[];
15
+
16
+ /**
17
+ * A single find/replace edit applied to a string field. When `oldString` is missing or empty the
18
+ * `newString` is appended to the end of the current value.
19
+ */
20
+ export type Edit = {
21
+ oldString?: string;
22
+ newString: string;
23
+ replaceAll?: boolean;
24
+ };
25
+
26
+ /**
27
+ * Replace the string CRDT value at `path` with `newText`.
28
+ *
29
+ * For database-backed objects the change is applied as a minimal Automerge diff (preserving cursors,
30
+ * anchors, and merging cleanly with concurrent edits); for in-memory objects it is a plain assignment.
31
+ *
32
+ * Must be called inside `Obj.update(obj, () => { ... })`.
33
+ */
34
+ export const update = (obj: Obj.Unknown, path: KeyPath | string | number, newText: string): void => {
35
+ const { handler, target } = resolve(obj, 'update');
36
+ if (typeof handler.textUpdate !== 'function') {
37
+ throw new Err.TextNotSupportedError('update');
38
+ }
39
+ handler.textUpdate(target, normalizePath(path), newText);
40
+ };
41
+
42
+ /**
43
+ * Splice the string CRDT value at `path` by character index, mirroring `Array.prototype.splice`:
44
+ * remove `deleteCount` characters at `start` and insert `insert`. Returns the removed substring.
45
+ *
46
+ * Must be called inside `Obj.update(obj, () => { ... })`.
47
+ */
48
+ export const splice = (
49
+ obj: Obj.Unknown,
50
+ path: KeyPath | string | number,
51
+ start: number,
52
+ deleteCount: number,
53
+ insert = '',
54
+ ): string => {
55
+ const { handler, target } = resolve(obj, 'splice');
56
+ if (typeof handler.textSplice !== 'function') {
57
+ throw new Err.TextNotSupportedError('splice');
58
+ }
59
+ return handler.textSplice(target, normalizePath(path), start, deleteCount, insert);
60
+ };
61
+
62
+ /**
63
+ * Apply a sequence of find/replace {@link Edit}s to the string value at `path`, returning the resulting
64
+ * full string. Implemented on top of {@link splice} so it works for both backends.
65
+ *
66
+ * An edit with a missing or empty `oldString` appends its `newString`. A non-`replaceAll` edit whose
67
+ * `oldString` is not found throws {@link Err.TextEditNotFoundError}.
68
+ *
69
+ * Must be called inside `Obj.update(obj, () => { ... })`.
70
+ */
71
+ export const apply = (obj: Obj.Unknown, path: KeyPath | string | number, edits: readonly Edit[]): string => {
72
+ const keyPath = normalizePath(path);
73
+ for (const edit of edits) {
74
+ const text: string = Obj.getValue(obj, keyPath) ?? '';
75
+ if (edit.oldString == null || edit.oldString.length === 0) {
76
+ // Append: an empty match would loop forever in `replaceAll`, so treat it as an append instead.
77
+ splice(obj, keyPath, text.length, 0, edit.newString);
78
+ continue;
79
+ }
80
+
81
+ if (edit.replaceAll) {
82
+ let idx = text.indexOf(edit.oldString);
83
+ while (idx !== -1) {
84
+ splice(obj, keyPath, idx, edit.oldString.length, edit.newString);
85
+ const updated: string = Obj.getValue(obj, keyPath) ?? '';
86
+ // Resume past the inserted text so a `newString` that contains `oldString` is not re-matched.
87
+ idx = updated.indexOf(edit.oldString, idx + edit.newString.length);
88
+ }
89
+ } else {
90
+ const idx = text.indexOf(edit.oldString);
91
+ if (idx === -1) {
92
+ throw new Err.TextEditNotFoundError(edit.oldString);
93
+ }
94
+ splice(obj, keyPath, idx, edit.oldString.length, edit.newString);
95
+ }
96
+ }
97
+
98
+ return Obj.getValue(obj, keyPath) ?? '';
99
+ };
100
+
101
+ const normalizePath = (path: KeyPath | string | number): KeyPath =>
102
+ typeof path === 'string' || typeof path === 'number' ? [path] : path;
103
+
104
+ /**
105
+ * Resolve the reactive handler and raw target for `obj`. The handler is shared between the in-memory
106
+ * and database-backed proxy systems, so this is the single dispatch seam for both backends. Snapshots
107
+ * and non-reactive values are not proxies and cannot be edited.
108
+ */
109
+ const resolve = (obj: Obj.Unknown, operation: string) => {
110
+ if (!internal.isProxy(obj)) {
111
+ throw new Err.TextNotSupportedError(operation);
112
+ }
113
+ const handler = internal.getProxyHandler(obj);
114
+ const target = internal.getProxyTarget(obj);
115
+ return { handler, target };
116
+ };
package/src/Type.test.ts CHANGED
@@ -8,8 +8,10 @@ import { describe, test } from 'vitest';
8
8
  import { DXN, EntityId } from '@dxos/keys';
9
9
 
10
10
  import * as Entity from './Entity';
11
+ import * as Filter from './Filter';
11
12
  import * as JsonSchema from './JsonSchema';
12
13
  import * as Obj from './Obj';
14
+ import * as Ref from './Ref';
13
15
  import * as Relation from './Relation';
14
16
  import { TestSchema } from './testing';
15
17
  import * as Type from './Type';
@@ -205,53 +207,79 @@ describe('Type', () => {
205
207
  const worksForDxn = DXN.make('com.example.type.deterministic.worksFor', '0.1.0');
206
208
 
207
209
  test('Type.makeObject with the same DXN twice yields entities with identical ids', ({ expect }) => {
208
- const a = Schema.Struct({ name: Schema.String }).pipe(Type.makeObject(personDxn));
209
- const b = Schema.Struct({ name: Schema.String }).pipe(Type.makeObject(personDxn));
210
+ const a = Type.makeObject(personDxn)(Schema.Struct({ name: Schema.String }));
211
+ const b = Type.makeObject(personDxn)(Schema.Struct({ name: Schema.String }));
210
212
  expect(a.id).toBe(b.id);
211
213
  expect(a.id).toBe(EntityId.deterministic('com.example.type.deterministic.person', '0.1.0'));
212
214
  });
213
215
 
214
216
  test('different versions produce different ids', ({ expect }) => {
215
- const v1 = Schema.Struct({ name: Schema.String }).pipe(
216
- Type.makeObject(DXN.make('com.example.type.deterministic.person', '0.1.0')),
217
+ const v1 = Type.makeObject(DXN.make('com.example.type.deterministic.person', '0.1.0'))(
218
+ Schema.Struct({ name: Schema.String }),
217
219
  );
218
- const v2 = Schema.Struct({ name: Schema.String }).pipe(
219
- Type.makeObject(DXN.make('com.example.type.deterministic.person', '0.2.0')),
220
+ const v2 = Type.makeObject(DXN.make('com.example.type.deterministic.person', '0.2.0'))(
221
+ Schema.Struct({ name: Schema.String }),
220
222
  );
221
223
  expect(v1.id).not.toBe(v2.id);
222
224
  });
223
225
 
224
226
  test('Type.makeObject({ id }) override is honoured', ({ expect }) => {
225
227
  const explicit = EntityId.random();
226
- const entity = Schema.Struct({ name: Schema.String }).pipe(Type.makeObject(personDxn, { id: explicit }));
228
+ const entity = Type.makeObject(personDxn, { id: explicit })(Schema.Struct({ name: Schema.String }));
227
229
  expect(entity.id).toBe(explicit);
228
230
  // And the default still kicks in when no override is supplied.
229
- const defaultEntity = Schema.Struct({ name: Schema.String }).pipe(Type.makeObject(personDxn));
231
+ const defaultEntity = Type.makeObject(personDxn)(Schema.Struct({ name: Schema.String }));
230
232
  expect(defaultEntity.id).not.toBe(explicit);
231
233
  });
232
234
 
233
235
  test('Type.makeRelation defaults id to deterministic(typename, version)', ({ expect }) => {
234
- const a = Schema.Struct({ since: Schema.Number }).pipe(
235
- Type.makeRelation({ dxn: worksForDxn, source: TestSchema.Person, target: TestSchema.Organization }),
236
- );
237
- const b = Schema.Struct({ since: Schema.Number }).pipe(
238
- Type.makeRelation({ dxn: worksForDxn, source: TestSchema.Person, target: TestSchema.Organization }),
239
- );
240
- expect(a.id).toBe(b.id);
241
- expect(a.id).toBe(EntityId.deterministic('com.example.type.deterministic.worksFor', '0.1.0'));
236
+ class RelationA extends Type.makeRelation<RelationA>(worksForDxn)({
237
+ source: TestSchema.Person,
238
+ target: TestSchema.Organization,
239
+ })(Schema.Struct({ since: Schema.Number })) {}
240
+
241
+ class RelationB extends Type.makeRelation<RelationB>(worksForDxn)({
242
+ source: TestSchema.Person,
243
+ target: TestSchema.Organization,
244
+ })(Schema.Struct({ since: Schema.Number })) {}
245
+
246
+ expect(RelationA.id).toBe(RelationB.id);
247
+ expect(RelationA.id).toBe(EntityId.deterministic('com.example.type.deterministic.worksFor', '0.1.0'));
242
248
  });
243
249
 
244
250
  test('Type.makeRelation({ id }) override is honoured', ({ expect }) => {
245
251
  const explicit = EntityId.random();
246
- const entity = Schema.Struct({ since: Schema.Number }).pipe(
247
- Type.makeRelation({
248
- dxn: worksForDxn,
249
- source: TestSchema.Person,
250
- target: TestSchema.Organization,
251
- id: explicit,
252
- }),
253
- );
252
+ const entity = Type.makeRelation(worksForDxn)({
253
+ source: TestSchema.Person,
254
+ target: TestSchema.Organization,
255
+ id: explicit,
256
+ })(Schema.Struct({ since: Schema.Number }));
254
257
  expect(entity.id).toBe(explicit);
255
258
  });
259
+
260
+ test('Type.makeObject class syntax', ({ expect }) => {
261
+ class Person extends Type.makeObject<Person>(DXN.make('com.example.type.person', '0.1.0'))(
262
+ Schema.Struct({ name: Schema.String }),
263
+ ) {}
264
+ expect(Person.jsonSchema).toBeDefined();
265
+ });
266
+
267
+ test('Ref of Type.makeObject class', ({ expect }) => {
268
+ class Person extends Type.makeObject<Person>(DXN.make('com.example.type.person', '0.1.0'))(
269
+ Schema.Struct({ name: Schema.String }),
270
+ ) {}
271
+
272
+ const refSchema = Ref.Ref(Person);
273
+ expect(refSchema).toBeDefined();
274
+ });
275
+
276
+ test('Filter of Type.makeObject class', ({ expect }) => {
277
+ class Person extends Type.makeObject<Person>(DXN.make('com.example.type.person', '0.1.0'))(
278
+ Schema.Struct({ name: Schema.String }),
279
+ ) {}
280
+
281
+ const filter = Filter.type(Person);
282
+ expect(filter).toBeDefined();
283
+ });
256
284
  });
257
285
  });
package/src/Type.ts CHANGED
@@ -73,11 +73,9 @@ interface BaseTypeEntity<A> {
73
73
  *
74
74
  * @example
75
75
  * ```ts
76
- * const Person = Schema.Struct({
77
- * name: Schema.String,
78
- * }).pipe(Type.makeObject(DXN.make('com.example.type.person', '0.1.0')));
79
- *
80
- * type Person = Type.InstanceType<typeof Person>;
76
+ * class Person extends Type.makeObject<Person>(DXN.make('com.example.type.person', '0.1.0'))(
77
+ * Schema.Struct({ name: Schema.String }),
78
+ * ) {}
81
79
  * ```
82
80
  */
83
81
  export interface Obj<T, Fields extends Schema.Struct.Fields = Schema.Struct.Fields> extends BaseTypeEntity<
@@ -96,6 +94,15 @@ export interface Obj<T, Fields extends Schema.Struct.Fields = Schema.Struct.Fiel
96
94
  readonly fields: Fields;
97
95
  }
98
96
 
97
+ /**
98
+ * Return type for {@link makeObject}.
99
+ *
100
+ * Not to be used directly, but must be exported for typescript to infer the type.
101
+ */
102
+ export interface ObjClass<Self, T, Fields extends Schema.Struct.Fields> extends Obj<Self, Fields> {
103
+ new (_: never): T & EntityModule.OfKind<typeof EntityModule.Kind.Object>;
104
+ }
105
+
99
106
  /**
100
107
  * Type that represents any ECHO object type — a `Type.Type` entity branded
101
108
  * with the object entity kind, i.e. what `Type.makeObject(dxn)` produces.
@@ -115,19 +122,26 @@ export type AnyObj = Obj<unknown>;
115
122
  * scope. Pass `{ id }` to override (e.g. with `EntityId.random()` from a
116
123
  * request handler).
117
124
  *
125
+ * Giving the type a nominal class name keeps its inferred type portable for
126
+ * declaration emit: structural expansion (which can pull in unnameable union
127
+ * members from referenced schemas) is replaced by a reference to the class.
128
+ *
118
129
  * @example
119
130
  * ```ts
120
- * const Person = Schema.Struct({
131
+ * export class Person extends Type.makeObject<Person>(DXN.make('com.example.type.myType', '0.1.0'))(Schema.Struct({
121
132
  * name: Schema.String,
122
- * }).pipe(Type.makeObject(DXN.make('com.example.type.person', '0.1.0')));
133
+ * })) {}
123
134
  * ```
124
135
  */
136
+ // TODO(burdon): Require { typename, version } in options.
125
137
  export const makeObject: {
126
- (
138
+ <Self>(
127
139
  dxn: DXN.DXN,
128
140
  options?: { id?: EntityId },
129
- ): <Self extends Schema.Schema.Any>(self: Self) => Obj<Schema.Schema.Type<Self>>;
130
- } = internal.EchoObjectSchema as any;
141
+ ): <_Schema extends Schema.Schema.Any>(schema: _Schema) => ObjClass<Self, Schema.Schema.Type<_Schema>, {}>;
142
+ // Boundary cast: overload implementation bodies cannot access outer generic params (`Self`),
143
+ // so TypeScript cannot verify that makeObjectType's return matches the declared ObjClass<Self,…>.
144
+ } = (dxn, options) => (schema) => internal.makeObjectType(dxn, schema, options) as any;
131
145
 
132
146
  //
133
147
  // Type — the ECHO entity that holds a schema and metadata.
@@ -255,6 +269,20 @@ export interface Relation<
255
269
  readonly fields: Fields;
256
270
  }
257
271
 
272
+ /**
273
+ * Return type for {@link makeRelation}.
274
+ *
275
+ * Not to be used directly, but must be exported for typescript to infer the type.
276
+ */
277
+ export interface RelationClass<Self, T, Source, Target, Fields extends Schema.Struct.Fields> extends Relation<
278
+ Self,
279
+ Source,
280
+ Target,
281
+ Fields
282
+ > {
283
+ new (_: never): RelationModule.Endpoints<Source, Target> & T & EntityModule.OfKind<typeof EntityModule.Kind.Relation>;
284
+ }
285
+
258
286
  /**
259
287
  * Type that represents any ECHO relation type — a `Type.Type` entity branded
260
288
  * with the relation entity kind, i.e. what `Type.makeRelation(...)` produces.
@@ -267,18 +295,13 @@ export type AnyRelation = Relation<unknown, unknown, unknown>;
267
295
  *
268
296
  * @example
269
297
  * ```ts
270
- * const WorksFor = Schema.Struct({
271
- * role: Schema.String,
272
- * }).pipe(Type.makeRelation({
273
- * dxn: DXN.make('com.example.type.worksFor', '0.1.0'),
274
- * source: Person,
275
- * target: Company,
276
- * }));
298
+ * class WorksFor extends Type.makeRelation<WorksFor>(DXN.make('com.example.type.worksFor', '0.1.0'))(
299
+ * { source: Person, target: Company },
300
+ * )(Schema.Struct({ role: Schema.String })) {}
277
301
  * ```
278
302
  */
279
303
  export const makeRelation: {
280
- <SourceInstance, TargetInstance>(opts: {
281
- dxn: DXN.DXN;
304
+ <Self>(dxn: DXN.DXN): <SourceInstance, TargetInstance>(opts: {
282
305
  source: Obj<SourceInstance, any> | internal.UnknownTypeSchema<SourceInstance, typeof EntityModule.Kind.Object>;
283
306
  target: Obj<TargetInstance, any> | internal.UnknownTypeSchema<TargetInstance, typeof EntityModule.Kind.Object>;
284
307
  /**
@@ -286,14 +309,25 @@ export const makeRelation: {
286
309
  * see `Type.makeObject` for the workerd motivation.
287
310
  */
288
311
  id?: EntityId;
289
- }): <Self extends Schema.Schema.Any>(
290
- self: Self,
291
- ) => Relation<
292
- Schema.Schema.Type<Self>,
312
+ }) => <_Schema extends Schema.Schema.Any>(
313
+ schema: _Schema,
314
+ ) => RelationClass<
315
+ Self,
316
+ Schema.Schema.Type<_Schema>,
293
317
  SourceInstance & EntityModule.OfKind<typeof EntityModule.Kind.Object>,
294
- TargetInstance & EntityModule.OfKind<typeof EntityModule.Kind.Object>
318
+ TargetInstance & EntityModule.OfKind<typeof EntityModule.Kind.Object>,
319
+ {}
295
320
  >;
296
- } = internal.EchoRelationSchema as any;
321
+ } = (dxn) => (opts) => (schema) =>
322
+ // Boundary cast: makeRelationType erases generic params since the constructor/prototype
323
+ // wiring cannot thread them through the type system.
324
+ internal.makeRelationType({
325
+ dxn,
326
+ source: opts.source,
327
+ target: opts.target,
328
+ schema,
329
+ id: opts.id,
330
+ }) as any;
297
331
 
298
332
  /**
299
333
  * Type that represents any ECHO type-kind entity — a `Type.Type` meta-schema
@@ -611,13 +645,17 @@ export interface Type<A = unknown> extends BaseTypeEntity<A & EntityModule.OfKin
611
645
  * - `Type<A>` → `A & OfKind<Type>`
612
646
  */
613
647
  export type InstanceType<T extends AnyEntity> =
614
- T extends Relation<infer Props, infer Source, infer Target, any>
648
+ T extends RelationClass<any, infer Props, infer Source, infer Target, any>
615
649
  ? RelationModule.Endpoints<Source, Target> & Props & EntityModule.OfKind<typeof EntityModule.Kind.Relation>
616
- : T extends Obj<infer A, any>
617
- ? A & EntityModule.OfKind<typeof EntityModule.Kind.Object>
618
- : T extends Type<infer A>
619
- ? A & EntityModule.OfKind<typeof EntityModule.Kind.Type>
620
- : never;
650
+ : T extends Relation<infer Props, infer Source, infer Target, any>
651
+ ? RelationModule.Endpoints<Source, Target> & Props & EntityModule.OfKind<typeof EntityModule.Kind.Relation>
652
+ : T extends ObjClass<any, infer A, any>
653
+ ? A & EntityModule.OfKind<typeof EntityModule.Kind.Object>
654
+ : T extends Obj<infer A, any>
655
+ ? A & EntityModule.OfKind<typeof EntityModule.Kind.Object>
656
+ : T extends Type<infer A>
657
+ ? A & EntityModule.OfKind<typeof EntityModule.Kind.Type>
658
+ : never;
621
659
 
622
660
  /**
623
661
  * Returns the Effect Schema for a type entity.
package/src/View.ts CHANGED
@@ -60,36 +60,28 @@ export type Projection = Schema.Schema.Type<typeof Projection>;
60
60
  * Views are generated or user-defined projections of a schema's properties.
61
61
  * They are used to configure the visual representation of the data.
62
62
  */
63
- const ViewSchema = Schema.Struct({
64
- /**
65
- * Query used to retrieve data.
66
- * Can be a user-provided query grammar string or a query AST.
67
- */
68
- query: Schema.Struct({
69
- raw: Schema.optional(Schema.String),
70
- ast: QueryAST.Query,
71
- }),
72
-
73
- /**
74
- * Projection of the data returned from the query.
75
- */
76
- projection: Projection,
77
- }).pipe(
78
- internal.HiddenAnnotation.set(true),
79
- Annotation.IconAnnotation.set({ icon: 'ph--funnel--regular', hue: 'green' }),
80
- Type.makeObject(DXN.make('org.dxos.type.view', '0.1.0')),
81
- );
82
-
83
- // NOTE: Declared as a named interface and the `View` const is annotated `Type.Obj<View>` so
84
- // downstream consumers see a named `View` type instead of the inlined `QueryAST.Query` union.
85
- // Without the named interface, embedding `Type.getSchema(View.View)` in a downstream
86
- // `Schema.Struct` triggers TS2742 portability errors (e.g. plugin-kanban, plugin-table).
87
- // TODO(wittjosiah): Find a better solution that doesn't require manually keeping the interface in sync.
88
- export interface View extends Type.InstanceType<typeof ViewSchema> {}
89
-
90
- export const View: Type.Obj<View> = ViewSchema as any;
91
-
92
- export const make = (props: Partial<Obj.MakeProps<typeof View>>): View => {
63
+ export class View extends Type.makeObject<View>(DXN.make('org.dxos.type.view', '0.1.0'))(
64
+ Schema.Struct({
65
+ /**
66
+ * Query used to retrieve data.
67
+ * Can be a user-provided query grammar string or a query AST.
68
+ */
69
+ query: Schema.Struct({
70
+ raw: Schema.optional(Schema.String),
71
+ ast: QueryAST.Query,
72
+ }),
73
+
74
+ /**
75
+ * Projection of the data returned from the query.
76
+ */
77
+ projection: Projection,
78
+ }).pipe(
79
+ internal.HiddenAnnotation.set(true),
80
+ Annotation.IconAnnotation.set({ icon: 'ph--funnel--regular', hue: 'green' }),
81
+ ),
82
+ ) {}
83
+
84
+ export const make = (props: Partial<Obj.MakeProps<typeof View>>): Type.InstanceType<typeof View> => {
93
85
  return Obj.make(View, {
94
86
  query: { ast: Query.select(Filter.nothing()).ast },
95
87
  projection: { fields: [] },
package/src/index.ts CHANGED
@@ -8,7 +8,6 @@ export { DXN, EID, URI } from '@dxos/keys';
8
8
  export * as Annotation from './Annotation';
9
9
  export * as Database from './Database';
10
10
  export * as Entity from './Entity';
11
- // TODO(burdon): Rename to Error (less problematic than Obj/Object).
12
11
  export * as Err from './Err';
13
12
  export * as Feed from './Feed';
14
13
  export * as Filter from './Filter';
@@ -27,6 +26,7 @@ export * as Registry from './Registry';
27
26
  export * as Relation from './Relation';
28
27
  export * as Scope from './Scope';
29
28
  export * as Tag from './Tag';
29
+ export * as Text from './Text';
30
30
  export * as Type from './Type';
31
31
  export * as Collection from './Collection';
32
32
  export * as View from './View';