@dxos/echo 0.9.1-main.c7dcc2e112 → 0.9.1-staging.ee54ba693a

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
package/src/Obj.test.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  // Copyright 2026 DXOS.org
3
3
  //
4
4
 
5
- import { describe, expectTypeOf, test, expect } from 'vitest';
5
+ import { describe, expect, expectTypeOf, test } from 'vitest';
6
6
 
7
7
  import { EID } from '@dxos/keys';
8
8
 
@@ -12,6 +12,7 @@ import * as Obj from './Obj';
12
12
  import * as Ref from './Ref';
13
13
  import * as Relation from './Relation';
14
14
  import { TestSchema } from './testing';
15
+ import type * as Type from './Type';
15
16
 
16
17
  describe('Obj', () => {
17
18
  describe('make', () => {
@@ -137,11 +138,11 @@ describe('Obj', () => {
137
138
 
138
139
  test("prefer: 'named' returns dxn: URI when meta.key is set", ({ expect }) => {
139
140
  const obj = Obj.make(TestSchema.Person, {
140
- [Obj.Meta]: { key: 'org.dxos.blueprint.webSearch' },
141
+ [Obj.Meta]: { key: 'org.dxos.skill.webSearch' },
141
142
  name: 'Alice',
142
143
  });
143
144
  const uri = Obj.getURI(obj, { prefer: 'named' });
144
- expect(uri).toBe('dxn:org.dxos.blueprint.webSearch');
145
+ expect(uri).toBe('dxn:org.dxos.skill.webSearch');
145
146
  });
146
147
 
147
148
  test("prefer: 'named' falls back to EID when meta.key is absent", ({ expect }) => {
@@ -152,12 +153,12 @@ describe('Obj', () => {
152
153
 
153
154
  test("prefer: 'named' handles key with hyphens (falls back to raw key URI)", ({ expect }) => {
154
155
  const obj = Obj.make(TestSchema.Person, {
155
- [Obj.Meta]: { key: 'org.dxos.blueprint.web-search' },
156
+ [Obj.Meta]: { key: 'org.dxos.skill.web-search' },
156
157
  name: 'Alice',
157
158
  });
158
159
  // Hyphens in the final DXN segment are invalid; falls back to the raw key as URI.
159
160
  const uri = Obj.getURI(obj, { prefer: 'named' });
160
- expect(uri).toBe('org.dxos.blueprint.web-search');
161
+ expect(uri).toBe('org.dxos.skill.web-search');
161
162
  });
162
163
 
163
164
  test("prefer: 'absolute' falls back to current EID when object has no database", ({ expect }) => {
@@ -328,7 +329,7 @@ describe('Obj', () => {
328
329
  employer: Ref.make(employer),
329
330
  });
330
331
 
331
- const cloned = Obj.clone(person, { deep: true });
332
+ const cloned = Obj.clone(person, { deep: 'all' });
332
333
 
333
334
  expect(cloned.employer).not.toBe(person.employer);
334
335
  expect(cloned.employer?.target).not.toBe(employer);
@@ -363,7 +364,7 @@ describe('Obj', () => {
363
364
  tasks: [Ref.make(task1), Ref.make(task2)],
364
365
  });
365
366
 
366
- const cloned = Obj.clone(person, { deep: true });
367
+ const cloned = Obj.clone(person, { deep: 'all' });
367
368
 
368
369
  expect(cloned.tasks).not.toBe(person.tasks);
369
370
  expect(cloned.tasks?.length).toBe(2);
@@ -393,7 +394,7 @@ describe('Obj', () => {
393
394
  employer: Ref.make(employer),
394
395
  });
395
396
 
396
- const cloned = Obj.clone(person, { deep: true });
397
+ const cloned = Obj.clone(person, { deep: 'all' });
397
398
 
398
399
  expect(cloned.employer).toBeDefined();
399
400
  expect(cloned.employer?.target).toBeDefined();
@@ -406,7 +407,7 @@ describe('Obj', () => {
406
407
  username: 'bob',
407
408
  });
408
409
 
409
- const clonedWithoutEmployer = Obj.clone(personWithoutEmployer, { deep: true });
410
+ const clonedWithoutEmployer = Obj.clone(personWithoutEmployer, { deep: 'all' });
410
411
  expect(clonedWithoutEmployer.employer).toBeUndefined();
411
412
  });
412
413
 
@@ -426,7 +427,7 @@ describe('Obj', () => {
426
427
  },
427
428
  });
428
429
 
429
- const cloned = Obj.clone(person, { deep: true });
430
+ const cloned = Obj.clone(person, { deep: 'all' });
430
431
 
431
432
  expect(cloned.address?.city).toBe('San Francisco');
432
433
  expect(cloned.address?.state).toBe('CA');
@@ -455,7 +456,7 @@ describe('Obj', () => {
455
456
  tasks: [Ref.make(task1), Ref.make(task2), Ref.make(task3)],
456
457
  });
457
458
 
458
- const cloned = Obj.clone(person, { deep: true });
459
+ const cloned = Obj.clone(person, { deep: 'all' });
459
460
 
460
461
  expect(cloned.tasks?.length).toBe(3);
461
462
  expect(cloned.tasks?.[0]?.target?.title).toBe('Task 1');
@@ -488,7 +489,7 @@ describe('Obj', () => {
488
489
  employer: Ref.make(employer),
489
490
  });
490
491
 
491
- const cloned = Obj.clone(person, { deep: true, retainId: true });
492
+ const cloned = Obj.clone(person, { deep: 'all', retainId: true });
492
493
 
493
494
  expect(cloned.id).toBe(person.id);
494
495
  expect(cloned.employer?.target).not.toBe(employer);
@@ -509,6 +510,48 @@ describe('Obj', () => {
509
510
  expect(Obj.instanceOf(TestSchema.Person, cloned)).toBe(true);
510
511
  expect(Obj.getType(cloned)).toBe(Obj.getType(person));
511
512
  });
513
+
514
+ test("deep: 'parent' clones owned children but shares unowned refs", ({ expect }) => {
515
+ // An owned task (parented to the person) is cloned; a shared task (no parent link) is referenced as-is.
516
+ const ownedTask = Obj.make(TestSchema.Task, { title: 'Owned' });
517
+ const sharedTask = Obj.make(TestSchema.Task, { title: 'Shared' });
518
+ const person = Obj.make(TestSchema.Person, {
519
+ name: 'Alice',
520
+ email: 'alice@example.com',
521
+ username: 'alice',
522
+ tasks: [Ref.make(ownedTask), Ref.make(sharedTask)],
523
+ });
524
+ Obj.setParent(ownedTask, person);
525
+
526
+ const cloned = Obj.clone(person, { deep: 'parent' });
527
+
528
+ // Owned child is a fresh copy.
529
+ expect(cloned.tasks?.[0]?.target).not.toBe(ownedTask);
530
+ expect(cloned.tasks?.[0]?.target?.id).not.toBe(ownedTask.id);
531
+ expect(cloned.tasks?.[0]?.target?.title).toBe('Owned');
532
+ // Unowned (shared) ref points at the original object.
533
+ expect(cloned.tasks?.[1]?.target).toBe(sharedTask);
534
+ });
535
+
536
+ test("deep: 'parent' follows transitive ownership", ({ expect }) => {
537
+ // grandchild ← child ← root: both are cloned because their parent chain reaches the clone root.
538
+ const grandchild = Obj.make(TestSchema.Task, { title: 'Grandchild' });
539
+ const child = Obj.make(TestSchema.Task, { title: 'Child', previous: Ref.make(grandchild) });
540
+ const person = Obj.make(TestSchema.Person, {
541
+ name: 'Alice',
542
+ email: 'alice@example.com',
543
+ username: 'alice',
544
+ tasks: [Ref.make(child)],
545
+ });
546
+ Obj.setParent(child, person);
547
+ Obj.setParent(grandchild, child);
548
+
549
+ const cloned = Obj.clone(person, { deep: 'parent' });
550
+
551
+ expect(cloned.tasks?.[0]?.target).not.toBe(child);
552
+ expect(cloned.tasks?.[0]?.target?.previous?.target).not.toBe(grandchild);
553
+ expect(cloned.tasks?.[0]?.target?.previous?.target?.title).toBe('Grandchild');
554
+ });
512
555
  });
513
556
 
514
557
  describe('type-level tests', () => {
@@ -638,4 +681,41 @@ describe('Obj', () => {
638
681
  expect(target.properties).toEqual({ x: '2' });
639
682
  });
640
683
  });
684
+
685
+ describe('exemplars', () => {
686
+ test('factory', ({ expect }) => {
687
+ const factory = <S extends Type.AnyObj>(schema: S) => {
688
+ return (props: Obj.MakeProps<S>) => Obj.make(schema, props);
689
+ };
690
+
691
+ const makePerson = factory(TestSchema.Person);
692
+ const person = makePerson({ name: 'John Doe' });
693
+ expect(person.name).toBe('John Doe');
694
+ });
695
+ });
696
+
697
+ describe('Hierarchy', () => {
698
+ test('setParent and getParent', async () => {
699
+ const parent = Obj.make(TestSchema.Organization, { name: 'parent' });
700
+ const child = Obj.make(TestSchema.Person, { name: 'child' });
701
+ expect(Obj.getParent(child)).toBeUndefined();
702
+
703
+ Obj.setParent(child, parent);
704
+ expect(Obj.getParent(child)).toBe(parent);
705
+
706
+ Obj.setParent(child, undefined);
707
+ expect(Obj.getParent(child)).toBeUndefined();
708
+ });
709
+
710
+ test('create object with Obj.Parent in props', () => {
711
+ const parent = Obj.make(TestSchema.Organization, { name: 'DXOS' });
712
+ const child = Obj.make(TestSchema.Person, {
713
+ [Obj.Parent]: parent,
714
+ name: 'John',
715
+ });
716
+
717
+ expect(child.name).toBe('John');
718
+ expect(Obj.getParent(child)).toBe(parent);
719
+ });
720
+ });
641
721
  });
package/src/Obj.ts CHANGED
@@ -6,6 +6,7 @@
6
6
 
7
7
  import * as Effect from 'effect/Effect';
8
8
  import * as Equal from 'effect/Equal';
9
+ import * as Exit from 'effect/Exit';
9
10
  import * as Function from 'effect/Function';
10
11
  import * as Option from 'effect/Option';
11
12
  import * as Schema from 'effect/Schema';
@@ -63,9 +64,9 @@ export interface Unknown extends BaseObj {}
63
64
  * if (Obj.isObject(unknownValue)) { ... }
64
65
  *
65
66
  * // Reference to any object type
66
- * const Collection = Schema.Struct({
67
- * objects: Schema.Array(Ref.Ref(Obj.Unknown)),
68
- * }).pipe(Type.makeObject(DXN.make('com.example.type.collection', '0.1.0')));
67
+ * class Collection extends Type.makeObject<Collection>(DXN.make('com.example.type.collection', '0.1.0'))(
68
+ * Schema.Struct({ objects: Schema.Array(Ref.Ref(Obj.Unknown)) }),
69
+ * ) {}
69
70
  * ```
70
71
  */
71
72
  // TODO(wittjosiah): Investigate if Schema.filter can validate KindId on ECHO instances.
@@ -299,6 +300,22 @@ export const getReactiveOption = <T extends Unknown>(snapshot: Snapshot<T>): Eff
299
300
  export const getReactiveOrThrow = <T extends Unknown>(snapshot: Snapshot<T>): T =>
300
301
  Effect.runSync(getReactive(snapshot));
301
302
 
303
+ /**
304
+ * Synchronous version of `Obj.getReactive` that returns `undefined` instead of throwing.
305
+ * Accepts `undefined` input so callers can pass the result of `useObject` directly.
306
+ *
307
+ * @param snapshot - A snapshot of the object (from `Obj.getSnapshot` or `useObject`), or `undefined`.
308
+ * @returns The reactive object, or `undefined` if the snapshot is `undefined` or unresolvable.
309
+ */
310
+ export const getReactiveOrUndefined = <T extends Unknown>(snapshot: Snapshot<T> | undefined): T | undefined => {
311
+ if (snapshot === undefined) {
312
+ return undefined;
313
+ }
314
+ return Effect.runSyncExit(getReactive(snapshot)).pipe(
315
+ Exit.match({ onSuccess: (value) => value, onFailure: () => undefined }),
316
+ );
317
+ };
318
+
302
319
  export type CloneOptions = {
303
320
  /**
304
321
  * Retain the original object's ID.
@@ -308,9 +325,12 @@ export type CloneOptions = {
308
325
 
309
326
  /**
310
327
  * Recursively clone referenced objects.
311
- * @default false
328
+ * - `'parent'`: clone only refs whose target is transitively parented under the clone root (children
329
+ * that cascade-delete with it); shared refs (registry operations, context objects, …) are copied as-is.
330
+ * - `'all'`: clone every referenced object recursively.
331
+ * Omit for a shallow clone (refs copied as-is).
312
332
  */
313
- deep?: boolean;
333
+ deep?: 'parent' | 'all';
314
334
  };
315
335
 
316
336
  /**
@@ -923,6 +943,17 @@ export const version = (entity: Unknown | Snapshot): Version => internal.version
923
943
  // Atoms
924
944
  //
925
945
 
946
+ /**
947
+ * Create a reactive snapshot atom for an ECHO object or ref.
948
+ * Use inside atom computations (e.g. `Atom.make((get) => get(Obj.atom(ref)))`) to subscribe
949
+ * to a ref's target — the atom re-fires when the target loads or changes.
950
+ *
951
+ * @idiom org.dxos.echo.objAtomReactive
952
+ * applies: Subscribing to a ref's target inside an atom computation or a non-React reactive context
953
+ * instead-of: `ref.target` — synchronous and not reactive; returns `undefined` when the target isn't loaded yet and never notifies when it becomes available
954
+ * uses: {@link atom}
955
+ * related: org.dxos.echo-react.useObjectReactive
956
+ */
926
957
  export const atom = objInternal.makeAtom;
927
958
  export const atomReactive = objInternal.makeWithReactive;
928
959
  export const atomProperty = objInternal.makeProperty;
package/src/Order.ts CHANGED
@@ -10,7 +10,7 @@ export interface Order<T> {
10
10
  // TODO(dmaretskyi): See new effect-schema approach to variance.
11
11
  '~Order': { value: T };
12
12
 
13
- ast: QueryAST.Order;
13
+ 'ast': QueryAST.Order;
14
14
  }
15
15
 
16
16
  export type Any = Order<any>;
@@ -18,11 +18,11 @@ export type Any = Order<any>;
18
18
  class OrderClass implements Order<any> {
19
19
  private static 'variance': Order<any>['~Order'] = {} as Order<any>['~Order'];
20
20
 
21
- static is(value: unknown): value is Order<any> {
21
+ static 'is'(value: unknown): value is Order<any> {
22
22
  return typeof value === 'object' && value !== null && '~Order' in value;
23
23
  }
24
24
 
25
- constructor(public readonly ast: QueryAST.Order) {}
25
+ 'constructor'(public readonly ast: QueryAST.Order) {}
26
26
 
27
27
  '~Order' = OrderClass.variance;
28
28
  }
package/src/Query.test.ts CHANGED
@@ -582,8 +582,8 @@ describe('query api', () => {
582
582
  const feed = (await Obj.fromJSON(
583
583
  {
584
584
  '@type': 'dxn:org.dxos.type.feed:0.1.0',
585
- id: feedId,
586
- name: 'test-feed',
585
+ 'id': feedId,
586
+ 'name': 'test-feed',
587
587
  },
588
588
  { uri: feedDxn },
589
589
  )) as Feed.Feed;
package/src/Query.ts CHANGED
@@ -50,7 +50,7 @@ export interface Query<T> {
50
50
  // TODO(dmaretskyi): See new effect-schema approach to variance.
51
51
  '~Query': { value: T };
52
52
 
53
- ast: QueryAST.Query;
53
+ 'ast': QueryAST.Query;
54
54
 
55
55
  /**
56
56
  * Filter the current selection based on a filter.
@@ -238,11 +238,11 @@ export type Type<Q extends Any> = Q extends Query<infer T> ? T : never;
238
238
  class QueryClass implements Any {
239
239
  private static 'variance': Any['~Query'] = {} as Any['~Query'];
240
240
 
241
- constructor(public readonly ast: QueryAST.Query) {}
241
+ 'constructor'(public readonly ast: QueryAST.Query) {}
242
242
 
243
243
  '~Query' = QueryClass.variance;
244
244
 
245
- select(filter: Filter.Any | Filter.Props<any>): Any {
245
+ 'select'(filter: Filter.Any | Filter.Props<any>): Any {
246
246
  if (Filter.is(filter)) {
247
247
  return new QueryClass({
248
248
  type: 'filter',
@@ -258,7 +258,7 @@ class QueryClass implements Any {
258
258
  }
259
259
  }
260
260
 
261
- reference(key: string): Any {
261
+ 'reference'(key: string): Any {
262
262
  return new QueryClass({
263
263
  type: 'reference-traversal',
264
264
  anchor: this.ast,
@@ -266,7 +266,7 @@ class QueryClass implements Any {
266
266
  });
267
267
  }
268
268
 
269
- referencedBy(target?: Type$.AnyEntity | URI.URI, key?: string): Any {
269
+ 'referencedBy'(target?: Type$.AnyEntity | URI.URI, key?: string): Any {
270
270
  const uri = target !== undefined ? internal.getTypeURIFromSpecifier(target) : null;
271
271
  return new QueryClass({
272
272
  type: 'incoming-references',
@@ -276,7 +276,7 @@ class QueryClass implements Any {
276
276
  });
277
277
  }
278
278
 
279
- sourceOf(relation?: Type$.AnyRelation | URI.URI, predicates?: Filter.Props<unknown> | undefined): Any {
279
+ 'sourceOf'(relation?: Type$.AnyRelation | URI.URI, predicates?: Filter.Props<unknown> | undefined): Any {
280
280
  return new QueryClass({
281
281
  type: 'relation',
282
282
  anchor: this.ast,
@@ -285,7 +285,7 @@ class QueryClass implements Any {
285
285
  });
286
286
  }
287
287
 
288
- targetOf(relation?: Type$.AnyRelation | URI.URI, predicates?: Filter.Props<unknown> | undefined): Any {
288
+ 'targetOf'(relation?: Type$.AnyRelation | URI.URI, predicates?: Filter.Props<unknown> | undefined): Any {
289
289
  return new QueryClass({
290
290
  type: 'relation',
291
291
  anchor: this.ast,
@@ -294,7 +294,7 @@ class QueryClass implements Any {
294
294
  });
295
295
  }
296
296
 
297
- source(): Any {
297
+ 'source'(): Any {
298
298
  return new QueryClass({
299
299
  type: 'relation-traversal',
300
300
  anchor: this.ast,
@@ -302,7 +302,7 @@ class QueryClass implements Any {
302
302
  });
303
303
  }
304
304
 
305
- target(): Any {
305
+ 'target'(): Any {
306
306
  return new QueryClass({
307
307
  type: 'relation-traversal',
308
308
  anchor: this.ast,
@@ -310,7 +310,7 @@ class QueryClass implements Any {
310
310
  });
311
311
  }
312
312
 
313
- parent(): Any {
313
+ 'parent'(): Any {
314
314
  return new QueryClass({
315
315
  type: 'hierarchy-traversal',
316
316
  anchor: this.ast,
@@ -318,7 +318,7 @@ class QueryClass implements Any {
318
318
  });
319
319
  }
320
320
 
321
- children(): Any {
321
+ 'children'(): Any {
322
322
  return new QueryClass({
323
323
  type: 'hierarchy-traversal',
324
324
  anchor: this.ast,
@@ -326,7 +326,7 @@ class QueryClass implements Any {
326
326
  });
327
327
  }
328
328
 
329
- orderBy(...order: Order.Order<any>[]): Any {
329
+ 'orderBy'(...order: Order.Order<any>[]): Any {
330
330
  return new QueryClass({
331
331
  type: 'order',
332
332
  query: this.ast,
@@ -334,7 +334,7 @@ class QueryClass implements Any {
334
334
  });
335
335
  }
336
336
 
337
- limit(limit: number): Any {
337
+ 'limit'(limit: number): Any {
338
338
  return new QueryClass({
339
339
  type: 'limit',
340
340
  query: this.ast,
@@ -342,7 +342,7 @@ class QueryClass implements Any {
342
342
  });
343
343
  }
344
344
 
345
- from(
345
+ 'from'(
346
346
  ...args:
347
347
  | [
348
348
  (
@@ -478,7 +478,7 @@ class QueryClass implements Any {
478
478
  });
479
479
  }
480
480
 
481
- options(options: QueryAST.QueryOptions): Any {
481
+ 'options'(options: QueryAST.QueryOptions): Any {
482
482
  return new QueryClass({
483
483
  type: 'options',
484
484
  query: this.ast,
@@ -486,7 +486,7 @@ class QueryClass implements Any {
486
486
  });
487
487
  }
488
488
 
489
- debugLabel(label: string): Any {
489
+ 'debugLabel'(label: string): Any {
490
490
  if (this.ast.type === 'options') {
491
491
  return new QueryClass({
492
492
  type: 'options',
@@ -3,8 +3,8 @@
3
3
  //
4
4
 
5
5
  import type * as Atom from '@effect-atom/atom/Atom';
6
- import * as Effect from 'effect/Effect';
7
- import * as Option from 'effect/Option';
6
+ import type * as Effect from 'effect/Effect';
7
+ import type * as Option from 'effect/Option';
8
8
 
9
9
  import { type CleanupFn } from '@dxos/async';
10
10
 
package/src/Ref.ts CHANGED
@@ -15,6 +15,7 @@ import type * as internal from './internal';
15
15
  import * as refInternal from './internal/Ref';
16
16
  import type * as JsonSchema from './JsonSchema';
17
17
  import type * as Obj from './Obj';
18
+ import type * as Relation from './Relation';
18
19
  // eslint-disable-next-line @dxos/rules/import-as-namespace
19
20
  import type * as Type$ from './Type';
20
21
 
@@ -45,13 +46,20 @@ export type Unknown = refInternal.Ref<Obj.Unknown>;
45
46
  *
46
47
  * @example
47
48
  * ```ts
48
- * const Task = Schema.Struct({
49
- * assignee: Ref.Ref(Person), // Creates a Ref schema
50
- * }).pipe(Type.makeObject(DXN.make('com.example.type.task', '0.1.0')));
49
+ * class Task extends Type.makeObject<Task>(DXN.make('com.example.type.task', '0.1.0'))(
50
+ * Schema.Struct({
51
+ * assignee: Ref.Ref(Person), // Creates a Ref schema
52
+ * }),
53
+ * ) {}
51
54
  * ```
52
55
  */
53
56
  export const Ref: {
54
- <S extends Type$.AnyObj | Type$.AnyRelation>(type: S): RefSchema<Type$.InstanceType<S> & Obj.Unknown>;
57
+ <S extends Type$.AnyObj>(type: S): RefSchema<Type$.InstanceType<S> & Obj.Unknown>;
58
+
59
+ // Relations are entities too and can be referenced. The instance type already
60
+ // carries the relation kind brand, so intersect with `Relation.Unknown` (not
61
+ // `Obj.Unknown`, whose object kind brand conflicts and collapses to `never`).
62
+ <S extends Type$.AnyRelation>(type: S): RefSchema<Type$.InstanceType<S> & Relation.Unknown>;
55
63
 
56
64
  // `Type.Type` entities (the meta-schema kind) can be referenced too — e.g. a
57
65
  // trigger that points to a stored function/workflow definition.
package/src/Registry.ts CHANGED
@@ -25,7 +25,7 @@ export type TypeId = typeof TypeId;
25
25
  * A registry may delegate to an optional upstream registry: results from the local
26
26
  * registry take precedence and upstream results fill in anything not found locally.
27
27
  *
28
- * Intended use cases include caches of schemas, operations, blueprints, routines, plugins,
28
+ * Intended use cases include caches of schemas, operations, skills, routines, plugins,
29
29
  * etc., sourced from 3rd-party plugins, local code, or local space objects.
30
30
  *
31
31
  * Types (schema-definition entities produced by `Type.makeObject` / `Type.makeRelation`) are
package/src/Relation.ts CHANGED
@@ -10,7 +10,7 @@ import { raise } from '@dxos/debug';
10
10
  import type { ForeignKey } from '@dxos/echo-protocol';
11
11
  import { SchemaEx } from '@dxos/effect';
12
12
  import { assertArgument, invariant } from '@dxos/invariant';
13
- import { EID, type EntityId, type URI, DXN } from '@dxos/keys';
13
+ import { DXN, EID, type EntityId, type URI } from '@dxos/keys';
14
14
  import { assumeType } from '@dxos/util';
15
15
 
16
16
  import type * as Database from './Database';
package/src/Tag.ts CHANGED
@@ -14,30 +14,26 @@ import * as internal from './internal';
14
14
  import * as Obj from './Obj';
15
15
  import * as Type from './Type';
16
16
 
17
- export const Tag = Schema.Struct({
18
- label: Schema.String,
19
- hue: Schema.optional(Schema.String), // TODO(burdon): Color name?
20
- }).pipe(
21
- internal.LabelAnnotation.set(['label']),
22
- internal.HiddenAnnotation.set(true),
23
- // Shared DXN so `meta.tags` (the `Ref<Tag>` schema) and this type stay in sync.
24
- Type.makeObject(internal.TagTypeDXN),
25
- );
26
-
27
- export type Tag = Type.InstanceType<typeof Tag>;
17
+ export class Tag extends Type.makeObject<Tag>(internal.TagTypeDXN)(
18
+ Schema.Struct({
19
+ label: Schema.String,
20
+ hue: Schema.optional(Schema.String), // TODO(burdon): Color name?
21
+ }).pipe(internal.LabelAnnotation.set(['label']), internal.HiddenAnnotation.set(true)),
22
+ ) {}
28
23
 
29
24
  export const make = (props: Obj.MakeProps<typeof Tag>) => Obj.make(Tag, props);
30
25
 
31
- export type Map = Record<string, Tag>;
26
+ export type Map = Record<string, Type.InstanceType<typeof Tag>>;
32
27
 
33
- export const sortTags = ({ label: a }: Tag, { label: b }: Tag) => a.localeCompare(b);
28
+ export const sortTags = ({ label: a }: Type.InstanceType<typeof Tag>, { label: b }: Type.InstanceType<typeof Tag>) =>
29
+ a.localeCompare(b);
34
30
 
35
- export const createTagList = (tags: Map): Tag[] =>
31
+ export const createTagList = (tags: Map): Type.InstanceType<typeof Tag>[] =>
36
32
  Object.entries(tags)
37
33
  .map(([id, tag]) => ({ ...tag, id }))
38
34
  .sort(sortTags);
39
35
 
40
- export const findTagByLabel = (tags: Map | undefined, name: string): Tag | undefined => {
36
+ export const findTagByLabel = (tags: Map | undefined, name: string): Type.InstanceType<typeof Tag> | undefined => {
41
37
  const entry = Object.entries(tags ?? {}).find(([_, tag]) => tag.label.toLowerCase() === name.toLowerCase());
42
38
  return entry ? { ...entry[1], id: entry[0] } : undefined;
43
39
  };
@@ -54,7 +50,7 @@ export const findTagByLabel = (tags: Map | undefined, name: string): Tag | undef
54
50
  export const findOrCreate = async (
55
51
  db: Pick<Database.Database, 'query' | 'add'>,
56
52
  options: { label: string; hue?: string; key?: ForeignKey },
57
- ): Promise<Tag> => {
53
+ ): Promise<Type.InstanceType<typeof Tag>> => {
58
54
  const { label, hue, key } = options;
59
55
  const withHue = hue ? { hue } : {};
60
56
  if (key) {