@dxos/echo 0.10.0 → 0.11.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 (274) hide show
  1. package/dist/lib/Annotation.mjs +3 -0
  2. package/dist/lib/Blob.mjs +160 -0
  3. package/dist/lib/Blob.mjs.map +1 -0
  4. package/dist/lib/Database.mjs +2 -0
  5. package/dist/lib/Entity.mjs +231 -0
  6. package/dist/lib/Entity.mjs.map +1 -0
  7. package/dist/lib/Err.mjs +105 -0
  8. package/dist/lib/Err.mjs.map +1 -0
  9. package/dist/lib/Feed.mjs +338 -0
  10. package/dist/lib/Feed.mjs.map +1 -0
  11. package/dist/lib/Filter.mjs +380 -0
  12. package/dist/lib/Filter.mjs.map +1 -0
  13. package/dist/lib/Format.mjs +47 -0
  14. package/dist/lib/Format.mjs.map +1 -0
  15. package/dist/lib/JsonSchema.mjs +21 -0
  16. package/dist/lib/JsonSchema.mjs.map +1 -0
  17. package/dist/lib/Key.mjs +15 -0
  18. package/dist/lib/Key.mjs.map +1 -0
  19. package/dist/lib/Migration.mjs +38 -0
  20. package/dist/lib/Migration.mjs.map +1 -0
  21. package/dist/lib/Obj.mjs +660 -0
  22. package/dist/lib/Obj.mjs.map +1 -0
  23. package/dist/lib/Order.mjs +65 -0
  24. package/dist/lib/Order.mjs.map +1 -0
  25. package/dist/lib/Query.mjs +336 -0
  26. package/dist/lib/Query.mjs.map +1 -0
  27. package/dist/lib/QueryResult.mjs +7 -0
  28. package/dist/lib/QueryResult.mjs.map +1 -0
  29. package/dist/lib/Ref.mjs +44 -0
  30. package/dist/lib/Ref.mjs.map +1 -0
  31. package/dist/lib/Registry.mjs +35 -0
  32. package/dist/lib/Registry.mjs.map +1 -0
  33. package/dist/lib/Relation.mjs +397 -0
  34. package/dist/lib/Relation.mjs.map +1 -0
  35. package/dist/lib/Scope.mjs +51 -0
  36. package/dist/lib/Scope.mjs.map +1 -0
  37. package/dist/lib/Tag.mjs +70 -0
  38. package/dist/lib/Tag.mjs.map +1 -0
  39. package/dist/lib/Text.mjs +82 -0
  40. package/dist/lib/Text.mjs.map +1 -0
  41. package/dist/lib/Type.mjs +371 -0
  42. package/dist/lib/Type.mjs.map +1 -0
  43. package/dist/lib/chunk-Annotation.mjs +221 -0
  44. package/dist/lib/chunk-Annotation.mjs.map +1 -0
  45. package/dist/lib/chunk-Database.mjs +918 -0
  46. package/dist/lib/chunk-Database.mjs.map +1 -0
  47. package/dist/lib/chunk-atom-snapshot.mjs +249 -0
  48. package/dist/lib/chunk-atom-snapshot.mjs.map +1 -0
  49. package/dist/lib/chunk-internal.mjs +2775 -0
  50. package/dist/lib/chunk-internal.mjs.map +1 -0
  51. package/dist/lib/chunk-json-schema.mjs +660 -0
  52. package/dist/lib/chunk-json-schema.mjs.map +1 -0
  53. package/dist/lib/chunk-number.mjs +164 -0
  54. package/dist/lib/chunk-number.mjs.map +1 -0
  55. package/dist/lib/chunk-ref-array.mjs +31 -0
  56. package/dist/lib/chunk-ref-array.mjs.map +1 -0
  57. package/dist/lib/chunk-ref.mjs +590 -0
  58. package/dist/lib/chunk-ref.mjs.map +1 -0
  59. package/dist/lib/chunk-rolldown-runtime.mjs +13 -0
  60. package/dist/lib/chunk-select.mjs +186 -0
  61. package/dist/lib/chunk-select.mjs.map +1 -0
  62. package/dist/lib/chunk-src.mjs +234 -0
  63. package/dist/lib/chunk-src.mjs.map +1 -0
  64. package/dist/lib/chunk-util.mjs +140 -0
  65. package/dist/lib/chunk-util.mjs.map +1 -0
  66. package/dist/lib/index.mjs +24 -0
  67. package/dist/lib/internal.mjs +10 -0
  68. package/dist/lib/testing.mjs +346 -0
  69. package/dist/lib/testing.mjs.map +1 -0
  70. package/dist/types/src/Aggregate.d.ts +86 -0
  71. package/dist/types/src/Aggregate.d.ts.map +1 -0
  72. package/dist/types/src/Blob.d.ts +153 -0
  73. package/dist/types/src/Blob.d.ts.map +1 -0
  74. package/dist/types/src/Blob.test.d.ts +2 -0
  75. package/dist/types/src/Blob.test.d.ts.map +1 -0
  76. package/dist/types/src/Database.d.ts +153 -4
  77. package/dist/types/src/Database.d.ts.map +1 -1
  78. package/dist/types/src/Err.d.ts +116 -0
  79. package/dist/types/src/Err.d.ts.map +1 -1
  80. package/dist/types/src/Feed.d.ts +116 -28
  81. package/dist/types/src/Feed.d.ts.map +1 -1
  82. package/dist/types/src/Feed.test.d.ts +2 -0
  83. package/dist/types/src/Feed.test.d.ts.map +1 -0
  84. package/dist/types/src/Filter.d.ts +14 -6
  85. package/dist/types/src/Filter.d.ts.map +1 -1
  86. package/dist/types/src/Hypergraph.d.ts +18 -0
  87. package/dist/types/src/Hypergraph.d.ts.map +1 -1
  88. package/dist/types/src/Json.d.ts.map +1 -1
  89. package/dist/types/src/Obj.d.ts +14 -1
  90. package/dist/types/src/Obj.d.ts.map +1 -1
  91. package/dist/types/src/Order.d.ts +9 -3
  92. package/dist/types/src/Order.d.ts.map +1 -1
  93. package/dist/types/src/Query.d.ts +107 -26
  94. package/dist/types/src/Query.d.ts.map +1 -1
  95. package/dist/types/src/Relation.d.ts +6 -0
  96. package/dist/types/src/Relation.d.ts.map +1 -1
  97. package/dist/types/src/Type.d.ts.map +1 -1
  98. package/dist/types/src/View.d.ts +1 -1
  99. package/dist/types/src/index.d.ts +2 -0
  100. package/dist/types/src/index.d.ts.map +1 -1
  101. package/dist/types/src/internal/Entity/api.d.ts +6 -0
  102. package/dist/types/src/internal/Entity/api.d.ts.map +1 -1
  103. package/dist/types/src/internal/Entity/model.d.ts +3 -2
  104. package/dist/types/src/internal/Entity/model.d.ts.map +1 -1
  105. package/dist/types/src/internal/Filter/match.d.ts.map +1 -1
  106. package/dist/types/src/internal/JsonSchema/json-schema-type.d.ts +34 -8
  107. package/dist/types/src/internal/JsonSchema/json-schema-type.d.ts.map +1 -1
  108. package/dist/types/src/internal/JsonSchema/json-schema.d.ts.map +1 -1
  109. package/dist/types/src/internal/Obj/atoms.d.ts.map +1 -1
  110. package/dist/types/src/internal/Obj/json-serializer.d.ts.map +1 -1
  111. package/dist/types/src/internal/Query/pretty.d.ts.map +1 -1
  112. package/dist/types/src/internal/Query/query-result-effect.d.ts.map +1 -1
  113. package/dist/types/src/internal/common/proxy/make-object.d.ts +13 -0
  114. package/dist/types/src/internal/common/proxy/make-object.d.ts.map +1 -1
  115. package/dist/types/src/internal/common/proxy/reactive.d.ts.map +1 -1
  116. package/dist/types/src/internal/common/proxy/symbols.d.ts.map +1 -1
  117. package/dist/types/src/internal/common/proxy/typed-handler.d.ts +12 -0
  118. package/dist/types/src/internal/common/proxy/typed-handler.d.ts.map +1 -1
  119. package/dist/types/src/internal/common/types/index.d.ts +1 -0
  120. package/dist/types/src/internal/common/types/index.d.ts.map +1 -1
  121. package/dist/types/src/internal/common/types/model-symbols.d.ts +5 -0
  122. package/dist/types/src/internal/common/types/model-symbols.d.ts.map +1 -1
  123. package/dist/types/src/internal/common/types/projection.d.ts +32 -0
  124. package/dist/types/src/internal/common/types/projection.d.ts.map +1 -0
  125. package/dist/types/tsconfig.tsbuildinfo +1 -1
  126. package/package.json +40 -35
  127. package/src/Aggregate.ts +105 -0
  128. package/src/Blob.test.ts +45 -0
  129. package/src/Blob.ts +205 -0
  130. package/src/Database.ts +207 -17
  131. package/src/Err.ts +36 -0
  132. package/src/Feed.test.ts +188 -0
  133. package/src/Feed.ts +238 -62
  134. package/src/Filter.ts +28 -10
  135. package/src/Hypergraph.ts +18 -0
  136. package/src/Json.ts +1 -0
  137. package/src/Obj.test.ts +2 -2
  138. package/src/Obj.ts +21 -1
  139. package/src/Order.ts +16 -9
  140. package/src/Query.test.ts +319 -2
  141. package/src/Query.ts +165 -51
  142. package/src/Relation.ts +14 -0
  143. package/src/Type.ts +5 -6
  144. package/src/index.ts +2 -0
  145. package/src/internal/Entity/api.ts +11 -1
  146. package/src/internal/Entity/model.ts +3 -0
  147. package/src/internal/Filter/match.ts +30 -1
  148. package/src/internal/JsonSchema/json-schema-type.ts +14 -2
  149. package/src/internal/JsonSchema/json-schema.test.ts +28 -1
  150. package/src/internal/JsonSchema/json-schema.ts +33 -6
  151. package/src/internal/Obj/atoms.ts +4 -2
  152. package/src/internal/Obj/json-serializer.test.ts +162 -2
  153. package/src/internal/Obj/json-serializer.ts +6 -0
  154. package/src/internal/Query/pretty.ts +19 -1
  155. package/src/internal/Query/query-result-effect.ts +2 -4
  156. package/src/internal/Ref/atoms.ts +1 -1
  157. package/src/internal/common/proxy/change-context.ts +1 -1
  158. package/src/internal/common/proxy/make-object.ts +42 -3
  159. package/src/internal/common/proxy/reactive.ts +5 -0
  160. package/src/internal/common/proxy/symbols.ts +1 -0
  161. package/src/internal/common/proxy/typed-handler.ts +71 -29
  162. package/src/internal/common/types/index.ts +1 -0
  163. package/src/internal/common/types/model-symbols.ts +6 -0
  164. package/src/internal/common/types/projection.ts +45 -0
  165. package/src/testing/test-data.ts +14 -14
  166. package/dist/lib/neutral/Annotation.mjs +0 -90
  167. package/dist/lib/neutral/Annotation.mjs.map +0 -7
  168. package/dist/lib/neutral/Database.mjs +0 -42
  169. package/dist/lib/neutral/Database.mjs.map +0 -7
  170. package/dist/lib/neutral/Entity.mjs +0 -76
  171. package/dist/lib/neutral/Entity.mjs.map +0 -7
  172. package/dist/lib/neutral/Err.mjs +0 -16
  173. package/dist/lib/neutral/Err.mjs.map +0 -7
  174. package/dist/lib/neutral/Feed.mjs +0 -50
  175. package/dist/lib/neutral/Feed.mjs.map +0 -7
  176. package/dist/lib/neutral/Filter.mjs +0 -72
  177. package/dist/lib/neutral/Filter.mjs.map +0 -7
  178. package/dist/lib/neutral/Format.mjs +0 -82
  179. package/dist/lib/neutral/Format.mjs.map +0 -7
  180. package/dist/lib/neutral/JsonSchema.mjs +0 -18
  181. package/dist/lib/neutral/JsonSchema.mjs.map +0 -7
  182. package/dist/lib/neutral/Key.mjs +0 -16
  183. package/dist/lib/neutral/Key.mjs.map +0 -7
  184. package/dist/lib/neutral/Migration.mjs +0 -18
  185. package/dist/lib/neutral/Migration.mjs.map +0 -7
  186. package/dist/lib/neutral/Obj.mjs +0 -128
  187. package/dist/lib/neutral/Obj.mjs.map +0 -7
  188. package/dist/lib/neutral/Order.mjs +0 -16
  189. package/dist/lib/neutral/Order.mjs.map +0 -7
  190. package/dist/lib/neutral/Query.mjs +0 -38
  191. package/dist/lib/neutral/Query.mjs.map +0 -7
  192. package/dist/lib/neutral/QueryResult.mjs +0 -2
  193. package/dist/lib/neutral/QueryResult.mjs.map +0 -7
  194. package/dist/lib/neutral/Ref.mjs +0 -25
  195. package/dist/lib/neutral/Ref.mjs.map +0 -7
  196. package/dist/lib/neutral/Registry.mjs +0 -14
  197. package/dist/lib/neutral/Registry.mjs.map +0 -7
  198. package/dist/lib/neutral/Relation.mjs +0 -97
  199. package/dist/lib/neutral/Relation.mjs.map +0 -7
  200. package/dist/lib/neutral/Scope.mjs +0 -12
  201. package/dist/lib/neutral/Scope.mjs.map +0 -7
  202. package/dist/lib/neutral/Tag.mjs +0 -32
  203. package/dist/lib/neutral/Tag.mjs.map +0 -7
  204. package/dist/lib/neutral/Text.mjs +0 -25
  205. package/dist/lib/neutral/Text.mjs.map +0 -7
  206. package/dist/lib/neutral/Type.mjs +0 -65
  207. package/dist/lib/neutral/Type.mjs.map +0 -7
  208. package/dist/lib/neutral/chunk-2HKGAQM4.mjs +0 -1394
  209. package/dist/lib/neutral/chunk-2HKGAQM4.mjs.map +0 -7
  210. package/dist/lib/neutral/chunk-2ZHECXUR.mjs +0 -95
  211. package/dist/lib/neutral/chunk-2ZHECXUR.mjs.map +0 -7
  212. package/dist/lib/neutral/chunk-3KTE2Y7Q.mjs +0 -336
  213. package/dist/lib/neutral/chunk-3KTE2Y7Q.mjs.map +0 -7
  214. package/dist/lib/neutral/chunk-3TN2VMI4.mjs +0 -714
  215. package/dist/lib/neutral/chunk-3TN2VMI4.mjs.map +0 -7
  216. package/dist/lib/neutral/chunk-42L4HJ3Z.mjs +0 -104
  217. package/dist/lib/neutral/chunk-42L4HJ3Z.mjs.map +0 -7
  218. package/dist/lib/neutral/chunk-4A2NWKRJ.mjs +0 -747
  219. package/dist/lib/neutral/chunk-4A2NWKRJ.mjs.map +0 -7
  220. package/dist/lib/neutral/chunk-7LOUAPYZ.mjs +0 -25
  221. package/dist/lib/neutral/chunk-7LOUAPYZ.mjs.map +0 -7
  222. package/dist/lib/neutral/chunk-ACFAOTKJ.mjs +0 -1531
  223. package/dist/lib/neutral/chunk-ACFAOTKJ.mjs.map +0 -7
  224. package/dist/lib/neutral/chunk-BEFR7LEA.mjs +0 -170
  225. package/dist/lib/neutral/chunk-BEFR7LEA.mjs.map +0 -7
  226. package/dist/lib/neutral/chunk-BXIKXT7Q.mjs +0 -89
  227. package/dist/lib/neutral/chunk-BXIKXT7Q.mjs.map +0 -7
  228. package/dist/lib/neutral/chunk-C57XMMBY.mjs +0 -206
  229. package/dist/lib/neutral/chunk-C57XMMBY.mjs.map +0 -7
  230. package/dist/lib/neutral/chunk-C62JYFAD.mjs +0 -183
  231. package/dist/lib/neutral/chunk-C62JYFAD.mjs.map +0 -7
  232. package/dist/lib/neutral/chunk-FSGX2R62.mjs +0 -48
  233. package/dist/lib/neutral/chunk-FSGX2R62.mjs.map +0 -7
  234. package/dist/lib/neutral/chunk-HBXT3GSB.mjs +0 -459
  235. package/dist/lib/neutral/chunk-HBXT3GSB.mjs.map +0 -7
  236. package/dist/lib/neutral/chunk-IQR45HAD.mjs +0 -31
  237. package/dist/lib/neutral/chunk-IQR45HAD.mjs.map +0 -7
  238. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +0 -10
  239. package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +0 -7
  240. package/dist/lib/neutral/chunk-KCBW4BR6.mjs +0 -424
  241. package/dist/lib/neutral/chunk-KCBW4BR6.mjs.map +0 -7
  242. package/dist/lib/neutral/chunk-L3E5IXAE.mjs +0 -575
  243. package/dist/lib/neutral/chunk-L3E5IXAE.mjs.map +0 -7
  244. package/dist/lib/neutral/chunk-PRC4FEKJ.mjs +0 -56
  245. package/dist/lib/neutral/chunk-PRC4FEKJ.mjs.map +0 -7
  246. package/dist/lib/neutral/chunk-SCT6FFCR.mjs +0 -351
  247. package/dist/lib/neutral/chunk-SCT6FFCR.mjs.map +0 -7
  248. package/dist/lib/neutral/chunk-TOCVVFK2.mjs +0 -75
  249. package/dist/lib/neutral/chunk-TOCVVFK2.mjs.map +0 -7
  250. package/dist/lib/neutral/chunk-U53LSQQA.mjs +0 -41
  251. package/dist/lib/neutral/chunk-U53LSQQA.mjs.map +0 -7
  252. package/dist/lib/neutral/chunk-UGFFV2FR.mjs +0 -27
  253. package/dist/lib/neutral/chunk-UGFFV2FR.mjs.map +0 -7
  254. package/dist/lib/neutral/chunk-UWLP4RFM.mjs +0 -7
  255. package/dist/lib/neutral/chunk-UWLP4RFM.mjs.map +0 -7
  256. package/dist/lib/neutral/chunk-UZHCQ6NI.mjs +0 -283
  257. package/dist/lib/neutral/chunk-UZHCQ6NI.mjs.map +0 -7
  258. package/dist/lib/neutral/chunk-WISOH2XH.mjs +0 -36
  259. package/dist/lib/neutral/chunk-WISOH2XH.mjs.map +0 -7
  260. package/dist/lib/neutral/chunk-WKIT2YY5.mjs +0 -34
  261. package/dist/lib/neutral/chunk-WKIT2YY5.mjs.map +0 -7
  262. package/dist/lib/neutral/chunk-XB6ISRAO.mjs +0 -271
  263. package/dist/lib/neutral/chunk-XB6ISRAO.mjs.map +0 -7
  264. package/dist/lib/neutral/chunk-XXL42WCB.mjs +0 -84
  265. package/dist/lib/neutral/chunk-XXL42WCB.mjs.map +0 -7
  266. package/dist/lib/neutral/chunk-XXWK4J53.mjs +0 -117
  267. package/dist/lib/neutral/chunk-XXWK4J53.mjs.map +0 -7
  268. package/dist/lib/neutral/index.mjs +0 -115
  269. package/dist/lib/neutral/index.mjs.map +0 -7
  270. package/dist/lib/neutral/internal/index.mjs +0 -581
  271. package/dist/lib/neutral/internal/index.mjs.map +0 -7
  272. package/dist/lib/neutral/meta.json +0 -1
  273. package/dist/lib/neutral/testing/index.mjs +0 -477
  274. package/dist/lib/neutral/testing/index.mjs.map +0 -7
@@ -0,0 +1,338 @@
1
+ import { t as __exportAll } from "./chunk-rolldown-runtime.mjs";
2
+ import { c as getKeys, l as getMetaChecked, m as foreignKey, s as deleteKeys } from "./chunk-atom-snapshot.mjs";
3
+ import { G as HiddenAnnotation, L as FormInputAnnotation, g as query$1, q as IconAnnotation, r as Service } from "./chunk-Database.mjs";
4
+ import { h as change } from "./chunk-ref.mjs";
5
+ import { makeObject } from "./Type.mjs";
6
+ import { ID, getURI, make as make$1 } from "./Obj.mjs";
7
+ import { is, select } from "./Query.mjs";
8
+ import { feed } from "./Scope.mjs";
9
+ import { KEY_QUEUE_POSITION } from "@dxos/echo-protocol";
10
+ import { DXN, EID, EntityId } from "@dxos/keys";
11
+ import * as Function from "effect/Function";
12
+ import * as Schema from "effect/Schema";
13
+ import { invariant } from "@dxos/invariant";
14
+ import * as Effect from "effect/Effect";
15
+ import * as Context from "effect/Context";
16
+ import * as Layer from "effect/Layer";
17
+ //#region src/Feed.ts
18
+ var Feed_exports = /* @__PURE__ */ __exportAll({
19
+ ContextFeedService: () => ContextFeedService,
20
+ Feed: () => Feed,
21
+ PARENT_KEY: () => PARENT_KEY,
22
+ POSITION_KEY: () => POSITION_KEY,
23
+ append: () => append,
24
+ cursor: () => cursor,
25
+ getFeedUri: () => getFeedUri,
26
+ getParent: () => getParent,
27
+ getPosition: () => getPosition,
28
+ getSyncState: () => getSyncState,
29
+ history: () => history,
30
+ make: () => make,
31
+ next: () => next,
32
+ nextOption: () => nextOption,
33
+ query: () => query,
34
+ remove: () => remove,
35
+ setParent: () => setParent,
36
+ setRetention: () => setRetention,
37
+ sync: () => sync
38
+ });
39
+ var __dxlog_file = "/__w/dxos/dxos/packages/core/echo/echo/src/Feed.ts";
40
+ /**
41
+ * Runtime schema for a Feed object.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * const feed = Obj.make(Feed.Feed, { name: 'notifications', kind: 'org.dxos.plugin.notifications.v1' });
46
+ * ```
47
+ */
48
+ var Feed = class extends makeObject(DXN.make("org.dxos.type.feed", "0.1.0"))(Schema.Struct({
49
+ /** User-facing display name. */
50
+ name: Schema.String.pipe(Schema.optional),
51
+ /** Identifier for the feed's kind (e.g., plugin id). */
52
+ kind: Schema.String.pipe(FormInputAnnotation.set(false), Schema.optional),
53
+ /**
54
+ * Feed namespace.
55
+ * Controls how feed data is stored and replicated.
56
+ * - `data`: Data feed (default).
57
+ * - `trace`: Trace feed.
58
+ */
59
+ namespace: Schema.optional(Schema.Literal("data", "trace")),
60
+ /**
61
+ * Earliest item a pending rewind discards — set when a soft fork is decided but not yet expressed,
62
+ * because the writer may be a different process than the one that decided it.
63
+ *
64
+ * Stored as the first *discarded* item rather than the new parent so that rewinding to the very
65
+ * first item needs no sentinel: nothing precedes it, so the continuation simply starts a new line.
66
+ * Readers show what precedes it; the next append parents to that and clears this.
67
+ *
68
+ * Transient intent, never a source of truth for history. The fork itself lives in item lineage
69
+ * (see {@link PARENT_KEY}), which is what {@link history} walks and what replicates in a defined
70
+ * order relative to the blocks.
71
+ */
72
+ rewindFrom: Schema.optional(ID.pipe(FormInputAnnotation.set(false)))
73
+ }).pipe(HiddenAnnotation.set(true), IconAnnotation.set({
74
+ icon: "ph--rows--regular",
75
+ hue: "yellow"
76
+ }))) {};
77
+ /**
78
+ * Effect service tag for injecting a Feed into effect operations.
79
+ * Used to provide a specific feed to operations that operate on it without threading
80
+ * the feed as an explicit parameter through every call site.
81
+ */
82
+ var ContextFeedService = class ContextFeedService extends Context.Tag("@dxos/echo/Feed/ContextFeedService")() {
83
+ static layer = (feed) => Layer.succeed(ContextFeedService, { feed });
84
+ };
85
+ /**
86
+ * Creates a new feed object.
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * const feed = Feed.make({ name: 'notifications', kind: 'org.dxos.plugin.notifications.v1' });
91
+ * ```
92
+ */
93
+ var make = (props = {}) => make$1(Feed, props);
94
+ /**
95
+ * Returns the feed object's EID when the feed is stored in a space.
96
+ *
97
+ * Private-ish and on track to be removed — prefer resolving feed scopes via higher-level APIs
98
+ * (e.g. `Feed.query`) rather than threading the raw queue URI. Used internally by the feed service
99
+ * layer.
100
+ */
101
+ var getFeedUri = (feed) => EID.tryParse(getURI(feed));
102
+ /**
103
+ * Appends items to a feed.
104
+ *
105
+ * Pass `options.parent` to soft-fork the feed: the first appended item continues from that item
106
+ * rather than from the feed's tip, so everything appended between the two becomes unreachable from
107
+ * {@link history}. Nothing is removed from the log.
108
+ *
109
+ * @example
110
+ * ```ts
111
+ * yield* Feed.append(feed, [Obj.make(Notification, { title: 'Hello' })]);
112
+ *
113
+ * // Continue from an earlier item, leaving what followed it unreachable.
114
+ * yield* Feed.append(feed, [Obj.make(Notification, { title: 'Take two' })], { parent: earlier });
115
+ * ```
116
+ */
117
+ var append = (feed, items, options) => Service.pipe(Effect.flatMap(({ db }) => Effect.promise(() => {
118
+ if (options?.parent !== void 0 && items.length > 0) setParent(items[0], options.parent);
119
+ return db.appendToFeed(feed, items);
120
+ }))).pipe(Effect.withSpan("Feed.append"));
121
+ /**
122
+ * Removes items from a feed.
123
+ *
124
+ * @example
125
+ * ```ts
126
+ * yield* Feed.remove(feed, [item]);
127
+ * ```
128
+ */
129
+ var remove = (feed, items) => Service.pipe(Effect.flatMap(({ db }) => Effect.promise(() => db.removeFeedItemsByIds(feed, items.map((item) => item.id))))).pipe(Effect.withSpan("Feed.remove"));
130
+ /**
131
+ * Foreign-key source for an item's explicit lineage parent within a feed.
132
+ *
133
+ * Lineage is carried in item `@meta` because feed items are arbitrary ECHO objects that the feed
134
+ * does not own and cannot extend; `KEY_QUEUE_POSITION` rides along with feed blocks the same way.
135
+ */
136
+ var PARENT_KEY = "org.dxos.key.feed-parent";
137
+ /**
138
+ * Tri-state read of the lineage key, distinguishing an absent key from a present but malformed one.
139
+ * A replicated id that does not parse must not read as "no parent" — that would resolve a fork as
140
+ * an implicit continuation and silently expose the items it abandoned.
141
+ */
142
+ var readParent = (item) => {
143
+ const id = getKeys(item, PARENT_KEY).at(0)?.id;
144
+ if (id === void 0) return { present: false };
145
+ return EntityId.isValid(id) ? {
146
+ present: true,
147
+ id
148
+ } : { present: true };
149
+ };
150
+ /**
151
+ * Foreign-key source for the global position a position authority assigned a feed item.
152
+ * Exposed alongside {@link getPosition} so callers can stamp or inspect it without reaching for
153
+ * `@dxos/protocols`.
154
+ */
155
+ var POSITION_KEY = KEY_QUEUE_POSITION;
156
+ /**
157
+ * The global position a feed item was assigned, or `+Infinity` when it has none — a block written
158
+ * locally and not yet acknowledged, which sorts last because it is the newest.
159
+ *
160
+ * Use this to put items into append order before calling {@link history}: a query returns an unordered
161
+ * set, and `created` is a wall clock that peers do not agree on.
162
+ *
163
+ * @example
164
+ * ```ts
165
+ * const inAppendOrder = Array.sort(messages, Order.mapInput(Order.number, Feed.getPosition));
166
+ * const { items } = Feed.history(inAppendOrder);
167
+ * ```
168
+ */
169
+ var getPosition = (item) => {
170
+ const key = getKeys(item, KEY_QUEUE_POSITION).at(0)?.id;
171
+ const position = key !== void 0 ? Number(key) : NaN;
172
+ return Number.isNaN(position) ? Number.POSITIVE_INFINITY : position;
173
+ };
174
+ /**
175
+ * Returns an item's explicit lineage parent, or `undefined` when it continues from the item that
176
+ * precedes it in append order (the default for every feed).
177
+ *
178
+ * Also `undefined` for a malformed stored id; {@link history} tells the two apart and reports
179
+ * a malformed parent as truncation.
180
+ */
181
+ var getParent = (item) => readParent(item).id;
182
+ /**
183
+ * Sets (or, with `undefined`, clears) an item's explicit lineage parent.
184
+ * Call before appending the item; {@link append}'s `parent` option does this for you.
185
+ */
186
+ var setParent = (item, parent) => {
187
+ change(item, (mutable) => {
188
+ deleteKeys(mutable, PARENT_KEY);
189
+ if (parent !== void 0) {
190
+ const id = typeof parent === "string" ? parent : parent.id;
191
+ getMetaChecked(mutable).keys.push(foreignKey(PARENT_KEY, id));
192
+ }
193
+ });
194
+ };
195
+ /**
196
+ * Returns the items reachable from a head by following lineage — a feed's `git log --first-parent`.
197
+ *
198
+ * Walks backwards from the head: an item with an explicit lineage parent jumps to that item, leaving
199
+ * everything appended in between unreachable; an item without one steps to its predecessor. So a
200
+ * feed appended as `M1, M2, M3, M4, M5(M3)` yields `M1, M2, M3, M5`, with `M4` unreachable but still
201
+ * present in the log — the log is the object store, this function is what HEAD reaches.
202
+ *
203
+ * `items` must be in **append order** — the walk is positional, and pre-sorting by a wall-clock
204
+ * field such as `created` would corrupt it. Lineage is resolved over exactly the list passed in, so
205
+ * a parent excluded by the caller's filter reads as absent (`shallow`), same as one that has not
206
+ * replicated yet or whose stored id does not parse.
207
+ *
208
+ * @example
209
+ * ```ts
210
+ * const messages = yield* Feed.query(feed, Filter.type(Message.Message)).run;
211
+ * const { items, shallow } = Feed.history(messages);
212
+ * ```
213
+ */
214
+ var history = (items, options) => {
215
+ const indexById = /* @__PURE__ */ new Map();
216
+ items.forEach((item, index) => indexById.set(item.id, index));
217
+ const head = options?.head;
218
+ const headIndex = head === void 0 ? items.length - 1 : indexById.get(typeof head === "string" ? head : head.id);
219
+ if (headIndex === void 0) return {
220
+ items: [],
221
+ shallow: true
222
+ };
223
+ let cursor = headIndex;
224
+ const reachable = [];
225
+ let shallow = false;
226
+ while (cursor >= 0) {
227
+ const item = items[cursor];
228
+ reachable.push(item);
229
+ const parent = readParent(item);
230
+ if (!parent.present) {
231
+ cursor -= 1;
232
+ continue;
233
+ }
234
+ if (parent.id === void 0) {
235
+ shallow = true;
236
+ break;
237
+ }
238
+ const parentIndex = indexById.get(parent.id);
239
+ if (parentIndex === void 0 || parentIndex >= cursor) {
240
+ shallow = true;
241
+ break;
242
+ }
243
+ cursor = parentIndex;
244
+ }
245
+ return {
246
+ items: reachable.reverse(),
247
+ shallow
248
+ };
249
+ };
250
+ /**
251
+ * Creates a reactive query over items in a feed.
252
+ *
253
+ * Returns a {@link QueryResult.QueryResultEffect}: yielding it produces a subscribable
254
+ * `QueryResult`, while its `.run` / `.first` shorthands execute the query once. This mirrors
255
+ * `Database.query` so feed and database queries chain identically.
256
+ *
257
+ * Supports both data-first and data-last (curried) forms; the latter composes with `pipe`.
258
+ *
259
+ * In non-Effect code, query a feed directly through the database with a feed scope:
260
+ * `db.query(Query.select(filter).from(Scope.feed(Feed.getFeedUri(feed))))`.
261
+ *
262
+ * @example
263
+ * ```ts
264
+ * const result = yield* Feed.query(feed, Filter.type(Person));
265
+ * result.subscribe(...);
266
+ *
267
+ * const objects = yield* Feed.query(feed, Filter.type(Person)).run;
268
+ * const object = yield* Feed.query(feed, Filter.type(Person)).first;
269
+ *
270
+ * // Data-last (curried) form composes with `pipe`:
271
+ * const objects = yield* pipe(feed, Feed.query(Filter.type(Person))).run;
272
+ * ```
273
+ */
274
+ var query = Function.dual(2, (feed$1, queryOrFilter) => {
275
+ const feedUri = getFeedUri(feed$1);
276
+ invariant(feedUri, "Feed must be stored in the database before accessing its contents", {
277
+ "~LogMeta": "~LogMeta",
278
+ F: __dxlog_file,
279
+ L: 432,
280
+ S: void 0,
281
+ A: ["feedUri", "'Feed must be stored in the database before accessing its contents'"]
282
+ });
283
+ return query$1((is(queryOrFilter) ? queryOrFilter : select(queryOrFilter)).from(feed(feedUri.toString())));
284
+ });
285
+ /**
286
+ * Syncs the feed with the server.
287
+ *
288
+ * @example
289
+ * ```ts
290
+ * yield* Feed.sync(feed);
291
+ * yield* Feed.sync(feed, { shouldPush: false });
292
+ * ```
293
+ */
294
+ var sync = (feed, options) => Service.pipe(Effect.flatMap(({ db }) => Effect.promise(() => db.syncFeed(feed, options)))).pipe(Effect.withSpan("Feed.sync"));
295
+ /**
296
+ * Returns the feed's replication backlog for its namespace.
297
+ *
298
+ * @example
299
+ * ```ts
300
+ * const { blocksToPull, blocksToPush } = yield* Feed.getSyncState(feed);
301
+ * ```
302
+ */
303
+ var getSyncState = (feed) => Service.pipe(Effect.flatMap(({ db }) => Effect.promise(() => db.getFeedSyncState(feed)))).pipe(Effect.withSpan("Feed.getSyncState"));
304
+ /**
305
+ * Creates a cursor for iterating over feed items.
306
+ * Currently stubbed — cursor operations are not yet implemented.
307
+ *
308
+ * @example
309
+ * ```ts
310
+ * const cursor = yield* Feed.cursor<Person>(feed);
311
+ * const item = yield* Feed.next(cursor);
312
+ * ```
313
+ */
314
+ var cursor = (_feed) => Effect.succeed({ _tag: "Cursor" });
315
+ /**
316
+ * Returns the next item from a feed cursor.
317
+ * Currently stubbed — cursor operations are not yet implemented.
318
+ */
319
+ var next = (_cursor) => Effect.die("Feed.next is not yet implemented");
320
+ /**
321
+ * Returns the next item from a feed cursor as an Option.
322
+ * Currently stubbed — cursor operations are not yet implemented.
323
+ */
324
+ var nextOption = (_cursor) => Effect.die("Feed.nextOption is not yet implemented");
325
+ /**
326
+ * Sets the local retention policy for a feed.
327
+ * Currently stubbed — feeds do not yet support retention.
328
+ *
329
+ * @example
330
+ * ```ts
331
+ * yield* Feed.setRetention(feed, { count: 1000 });
332
+ * ```
333
+ */
334
+ var setRetention = (_feed, _options) => Effect.void;
335
+ //#endregion
336
+ export { ContextFeedService, Feed, PARENT_KEY, POSITION_KEY, append, cursor, getFeedUri, getParent, getPosition, getSyncState, history, make, next, nextOption, query, remove, setParent, setRetention, sync, Feed_exports as t };
337
+
338
+ //# sourceMappingURL=Feed.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Feed.mjs","names":[],"sources":["../../src/Feed.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as Context from 'effect/Context';\nimport * as Effect from 'effect/Effect';\nimport * as Function from 'effect/Function';\nimport * as Layer from 'effect/Layer';\nimport type * as Option from 'effect/Option';\nimport * as Schema from 'effect/Schema';\n\nimport { KEY_QUEUE_POSITION } from '@dxos/echo-protocol';\nimport { invariant } from '@dxos/invariant';\nimport { DXN, EID, EntityId } from '@dxos/keys';\n\nimport * as Annotation from './Annotation';\nimport * as Database from './Database';\nimport type * as Entity from './Entity';\nimport type * as Filter from './Filter';\nimport * as internal from './internal';\nimport * as Obj from './Obj';\nimport * as Query from './Query';\nimport type * as QueryResult from './QueryResult';\nimport * as Scope from './Scope';\nimport * as Type from './Type';\n\n/**\n * Runtime schema for a Feed object.\n *\n * @example\n * ```ts\n * const feed = Obj.make(Feed.Feed, { name: 'notifications', kind: 'org.dxos.plugin.notifications.v1' });\n * ```\n */\nexport class Feed extends Type.makeObject<Feed>(DXN.make('org.dxos.type.feed', '0.1.0'))(\n Schema.Struct({\n /** User-facing display name. */\n name: Schema.String.pipe(Schema.optional),\n\n /** Identifier for the feed's kind (e.g., plugin id). */\n kind: Schema.String.pipe(internal.FormInputAnnotation.set(false), Schema.optional),\n\n /**\n * Feed namespace.\n * Controls how feed data is stored and replicated.\n * - `data`: Data feed (default).\n * - `trace`: Trace feed.\n */\n namespace: Schema.optional(Schema.Literal('data', 'trace')),\n\n /**\n * Earliest item a pending rewind discards — set when a soft fork is decided but not yet expressed,\n * because the writer may be a different process than the one that decided it.\n *\n * Stored as the first *discarded* item rather than the new parent so that rewinding to the very\n * first item needs no sentinel: nothing precedes it, so the continuation simply starts a new line.\n * Readers show what precedes it; the next append parents to that and clears this.\n *\n * Transient intent, never a source of truth for history. The fork itself lives in item lineage\n * (see {@link PARENT_KEY}), which is what {@link history} walks and what replicates in a defined\n * order relative to the blocks.\n */\n rewindFrom: Schema.optional(Obj.ID.pipe(internal.FormInputAnnotation.set(false))),\n }).pipe(\n internal.HiddenAnnotation.set(true),\n Annotation.IconAnnotation.set({ icon: 'ph--rows--regular', hue: 'yellow' }),\n ),\n) {}\n\n//\n// Types\n//\n\n/**\n * Opaque cursor for iterating over feed items.\n */\n// TODO(dmaretskyi): T needs to be referenced in the type structure for typescript to respect it during inference and type-checking.\nexport interface Cursor<T = Obj.Snapshot> {\n readonly _tag: 'Cursor';\n}\n\n/**\n * Retention options for a feed.\n *\n * Live feed objects persist every `Obj.update` as a whole-object re-append reusing the object's id\n * (see `EchoFeedCodec.encode`), so superseded blocks accumulate indefinitely — retention/compaction\n * of those superseded blocks (driven by these options, once implemented) is the reclaim mechanism.\n * TODO(wittjosiah): Implement when feed retention is supported (see `setRetention` below and\n * `FeedStore.appendLocal`).\n */\nexport interface RetentionOptions {\n /** Retain items after this cursor position. */\n // TODO(wittjosiah): Use FeedCursor from @dxos/feed?\n cursor?: string;\n}\n\n/**\n * Options for {@link append}.\n */\nexport interface AppendOptions {\n /**\n * Explicit lineage parent for the appended items — the soft-fork point.\n * Applies to the first item only; the rest of the batch chain implicitly in append order.\n */\n parent?: Entity.Unknown | Entity.Snapshot | EntityId;\n}\n\n/**\n * Options for {@link history}.\n */\nexport interface HistoryOptions {\n /**\n * Item to walk the history from, as git's `HEAD` selects which commits are reachable.\n * Defaults to the last item, so the most recently appended line of items wins.\n */\n head?: Entity.Unknown | Entity.Snapshot | EntityId;\n}\n\n/**\n * The items reachable from a head by following lineage — a feed's equivalent of\n * `git log --first-parent`.\n */\nexport interface History<T> {\n /** The reachable items, in append order. */\n items: T[];\n /**\n * A parent was referenced but could not be resolved, so the walk stopped at a boundary and\n * earlier history is missing — the same condition as a git shallow clone.\n */\n shallow: boolean;\n}\n\n/**\n * Sync options for a feed.\n */\nexport interface SyncOptions {\n /** Push local changes to the server. Defaults to true. */\n shouldPush?: boolean;\n /** Pull remote changes from the server. Defaults to true. */\n shouldPull?: boolean;\n}\n\n/**\n * Queue replication backlog for a feed namespace.\n * `0` / `0` means caught up on pull and push.\n */\nexport interface SyncState {\n /** Blocks still to pull from remote. */\n blocksToPull: number;\n /** Unpositioned blocks still to push to remote. */\n blocksToPush: number;\n /** Total blocks stored locally for the feed namespace. */\n totalBlocks: number;\n}\n\n/**\n * Effect service tag for injecting a Feed into effect operations.\n * Used to provide a specific feed to operations that operate on it without threading\n * the feed as an explicit parameter through every call site.\n */\nexport class ContextFeedService extends Context.Tag('@dxos/echo/Feed/ContextFeedService')<\n ContextFeedService,\n {\n readonly feed: Feed;\n }\n>() {\n static layer = (feed: Feed) => Layer.succeed(ContextFeedService, { feed });\n}\n\n//\n// Factory\n//\n\n/**\n * Creates a new feed object.\n *\n * @example\n * ```ts\n * const feed = Feed.make({ name: 'notifications', kind: 'org.dxos.plugin.notifications.v1' });\n * ```\n */\n// TODO(wittjosiah): How to control the feed namespace (data/trace)? Why do feeds have namespaces?\nexport const make = (props: Obj.MakeProps<typeof Feed> = {}): Feed => Obj.make(Feed, props);\n\n/**\n * Returns the feed object's EID when the feed is stored in a space.\n *\n * Private-ish and on track to be removed — prefer resolving feed scopes via higher-level APIs\n * (e.g. `Feed.query`) rather than threading the raw queue URI. Used internally by the feed service\n * layer.\n */\n// TODO(dmaretskyi): Remove — private-ish, prefer higher-level feed-scope APIs.\nexport const getFeedUri = (feed: Feed): EID.EID | undefined => EID.tryParse(Obj.getURI(feed));\n\n//\n// Operations\n//\n\n/**\n * Appends items to a feed.\n *\n * Pass `options.parent` to soft-fork the feed: the first appended item continues from that item\n * rather than from the feed's tip, so everything appended between the two becomes unreachable from\n * {@link history}. Nothing is removed from the log.\n *\n * @example\n * ```ts\n * yield* Feed.append(feed, [Obj.make(Notification, { title: 'Hello' })]);\n *\n * // Continue from an earlier item, leaving what followed it unreachable.\n * yield* Feed.append(feed, [Obj.make(Notification, { title: 'Take two' })], { parent: earlier });\n * ```\n */\nexport const append = (\n feed: Feed,\n items: Entity.Unknown[],\n options?: AppendOptions,\n): Effect.Effect<void, never, Database.Service> =>\n Database.Service.pipe(\n Effect.flatMap(({ db }) =>\n Effect.promise(() => {\n if (options?.parent !== undefined && items.length > 0) {\n setParent(items[0], options.parent);\n }\n return db.appendToFeed(feed, items);\n }),\n ),\n ).pipe(Effect.withSpan('Feed.append'));\n\n/**\n * Removes items from a feed.\n *\n * @example\n * ```ts\n * yield* Feed.remove(feed, [item]);\n * ```\n */\n// TODO(dmaretskyi): Should we allow snapshots here? - what does it mean to remove a snapshot?\nexport const remove = (\n feed: Feed,\n items: (Entity.Unknown | Obj.Snapshot)[],\n): Effect.Effect<void, never, Database.Service> =>\n Database.Service.pipe(\n Effect.flatMap(({ db }) =>\n Effect.promise(() =>\n db.removeFeedItemsByIds(\n feed,\n items.map((item) => item.id),\n ),\n ),\n ),\n ).pipe(Effect.withSpan('Feed.remove'));\n\n//\n// Lineage (soft fork)\n//\n\n/**\n * Foreign-key source for an item's explicit lineage parent within a feed.\n *\n * Lineage is carried in item `@meta` because feed items are arbitrary ECHO objects that the feed\n * does not own and cannot extend; `KEY_QUEUE_POSITION` rides along with feed blocks the same way.\n */\nexport const PARENT_KEY = 'org.dxos.key.feed-parent';\n\n/**\n * Tri-state read of the lineage key, distinguishing an absent key from a present but malformed one.\n * A replicated id that does not parse must not read as \"no parent\" — that would resolve a fork as\n * an implicit continuation and silently expose the items it abandoned.\n */\nconst readParent = (item: Entity.Unknown | Entity.Snapshot): { present: boolean; id?: EntityId } => {\n const id = internal.getKeys(item, PARENT_KEY).at(0)?.id;\n if (id === undefined) {\n return { present: false };\n }\n return EntityId.isValid(id) ? { present: true, id } : { present: true };\n};\n\n/**\n * Foreign-key source for the global position a position authority assigned a feed item.\n * Exposed alongside {@link getPosition} so callers can stamp or inspect it without reaching for\n * `@dxos/protocols`.\n */\nexport const POSITION_KEY = KEY_QUEUE_POSITION;\n\n/**\n * The global position a feed item was assigned, or `+Infinity` when it has none — a block written\n * locally and not yet acknowledged, which sorts last because it is the newest.\n *\n * Use this to put items into append order before calling {@link history}: a query returns an unordered\n * set, and `created` is a wall clock that peers do not agree on.\n *\n * @example\n * ```ts\n * const inAppendOrder = Array.sort(messages, Order.mapInput(Order.number, Feed.getPosition));\n * const { items } = Feed.history(inAppendOrder);\n * ```\n */\nexport const getPosition = (item: Entity.Unknown | Entity.Snapshot): number => {\n const key = internal.getKeys(item, KEY_QUEUE_POSITION).at(0)?.id;\n const position = key !== undefined ? Number(key) : Number.NaN;\n return Number.isNaN(position) ? Number.POSITIVE_INFINITY : position;\n};\n\n/**\n * Returns an item's explicit lineage parent, or `undefined` when it continues from the item that\n * precedes it in append order (the default for every feed).\n *\n * Also `undefined` for a malformed stored id; {@link history} tells the two apart and reports\n * a malformed parent as truncation.\n */\nexport const getParent = (item: Entity.Unknown | Entity.Snapshot): EntityId | undefined => readParent(item).id;\n\n/**\n * Sets (or, with `undefined`, clears) an item's explicit lineage parent.\n * Call before appending the item; {@link append}'s `parent` option does this for you.\n */\nexport const setParent = (\n item: Entity.Unknown,\n parent: Entity.Unknown | Entity.Snapshot | EntityId | undefined,\n): void => {\n internal.change(item, (mutable) => {\n internal.deleteKeys(mutable, PARENT_KEY);\n if (parent !== undefined) {\n const id = typeof parent === 'string' ? parent : parent.id;\n internal.getMetaChecked(mutable).keys.push(internal.foreignKey(PARENT_KEY, id));\n }\n });\n};\n\n/**\n * Returns the items reachable from a head by following lineage — a feed's `git log --first-parent`.\n *\n * Walks backwards from the head: an item with an explicit lineage parent jumps to that item, leaving\n * everything appended in between unreachable; an item without one steps to its predecessor. So a\n * feed appended as `M1, M2, M3, M4, M5(M3)` yields `M1, M2, M3, M5`, with `M4` unreachable but still\n * present in the log — the log is the object store, this function is what HEAD reaches.\n *\n * `items` must be in **append order** — the walk is positional, and pre-sorting by a wall-clock\n * field such as `created` would corrupt it. Lineage is resolved over exactly the list passed in, so\n * a parent excluded by the caller's filter reads as absent (`shallow`), same as one that has not\n * replicated yet or whose stored id does not parse.\n *\n * @example\n * ```ts\n * const messages = yield* Feed.query(feed, Filter.type(Message.Message)).run;\n * const { items, shallow } = Feed.history(messages);\n * ```\n */\nexport const history = <T extends Entity.Unknown | Entity.Snapshot>(\n items: readonly T[],\n options?: HistoryOptions,\n): History<T> => {\n const indexById = new Map<EntityId, number>();\n items.forEach((item, index) => indexById.set(item.id, index));\n\n const head = options?.head;\n const headIndex = head === undefined ? items.length - 1 : indexById.get(typeof head === 'string' ? head : head.id);\n if (headIndex === undefined) {\n return { items: [], shallow: true };\n }\n\n let cursor = headIndex;\n const reachable: T[] = [];\n let shallow = false;\n while (cursor >= 0) {\n const item = items[cursor];\n reachable.push(item);\n\n const parent = readParent(item);\n if (!parent.present) {\n cursor -= 1;\n continue;\n }\n if (parent.id === undefined) {\n // Key present but unparseable — a fork whose target cannot be identified, so stop rather than\n // fall through to the predecessor and make what this item superseded reachable again.\n shallow = true;\n break;\n }\n\n // A parent at or after its child is a cycle or a forward reference (possible with arbitrary\n // multi-writer data); requiring the cursor to strictly decrease also guarantees termination.\n const parentIndex = indexById.get(parent.id);\n if (parentIndex === undefined || parentIndex >= cursor) {\n shallow = true;\n break;\n }\n cursor = parentIndex;\n }\n\n return { items: reachable.reverse(), shallow };\n};\n\n/**\n * Creates a reactive query over items in a feed.\n *\n * Returns a {@link QueryResult.QueryResultEffect}: yielding it produces a subscribable\n * `QueryResult`, while its `.run` / `.first` shorthands execute the query once. This mirrors\n * `Database.query` so feed and database queries chain identically.\n *\n * Supports both data-first and data-last (curried) forms; the latter composes with `pipe`.\n *\n * In non-Effect code, query a feed directly through the database with a feed scope:\n * `db.query(Query.select(filter).from(Scope.feed(Feed.getFeedUri(feed))))`.\n *\n * @example\n * ```ts\n * const result = yield* Feed.query(feed, Filter.type(Person));\n * result.subscribe(...);\n *\n * const objects = yield* Feed.query(feed, Filter.type(Person)).run;\n * const object = yield* Feed.query(feed, Filter.type(Person)).first;\n *\n * // Data-last (curried) form composes with `pipe`:\n * const objects = yield* pipe(feed, Feed.query(Filter.type(Person))).run;\n * ```\n */\nexport const query: {\n <Q extends Query.Any>(feed: Feed, query: Q): QueryResult.QueryResultEffect<Query.Type<Q>, never, Database.Service>;\n <F extends Filter.Any>(feed: Feed, filter: F): QueryResult.QueryResultEffect<Filter.Type<F>, never, Database.Service>;\n <Q extends Query.Any>(\n query: Q,\n ): (feed: Feed) => QueryResult.QueryResultEffect<Query.Type<Q>, never, Database.Service>;\n <F extends Filter.Any>(\n filter: F,\n ): (feed: Feed) => QueryResult.QueryResultEffect<Filter.Type<F>, never, Database.Service>;\n} = Function.dual(2, (feed: Feed, queryOrFilter: Query.Any | Filter.Any) => {\n const feedUri = getFeedUri(feed);\n invariant(feedUri, 'Feed must be stored in the database before accessing its contents');\n const query = Query.is(queryOrFilter) ? queryOrFilter : Query.select(queryOrFilter);\n return Database.query(query.from(Scope.feed(feedUri.toString())));\n});\n\n/**\n * Syncs the feed with the server.\n *\n * @example\n * ```ts\n * yield* Feed.sync(feed);\n * yield* Feed.sync(feed, { shouldPush: false });\n * ```\n */\nexport const sync = (feed: Feed, options?: SyncOptions): Effect.Effect<void, never, Database.Service> =>\n Database.Service.pipe(Effect.flatMap(({ db }) => Effect.promise(() => db.syncFeed(feed, options)))).pipe(\n Effect.withSpan('Feed.sync'),\n );\n\n/**\n * Returns the feed's replication backlog for its namespace.\n *\n * @example\n * ```ts\n * const { blocksToPull, blocksToPush } = yield* Feed.getSyncState(feed);\n * ```\n */\nexport const getSyncState = (feed: Feed): Effect.Effect<SyncState, never, Database.Service> =>\n Database.Service.pipe(Effect.flatMap(({ db }) => Effect.promise(() => db.getFeedSyncState(feed)))).pipe(\n Effect.withSpan('Feed.getSyncState'),\n );\n\n/**\n * Creates a cursor for iterating over feed items.\n * Currently stubbed — cursor operations are not yet implemented.\n *\n * @example\n * ```ts\n * const cursor = yield* Feed.cursor<Person>(feed);\n * const item = yield* Feed.next(cursor);\n * ```\n */\n// TODO(wittjosiah): Implement cursor operations. Use Effect streams?\nexport const cursor = <T = Obj.Snapshot>(_feed: Feed): Effect.Effect<Cursor<T>, never, Database.Service> =>\n Effect.succeed({ _tag: 'Cursor' } as Cursor<T>);\n\n/**\n * Returns the next item from a feed cursor.\n * Currently stubbed — cursor operations are not yet implemented.\n */\nexport const next = <T = Obj.Snapshot>(_cursor: Cursor<T>): Effect.Effect<T, never, Database.Service> =>\n Effect.die('Feed.next is not yet implemented');\n\n/**\n * Returns the next item from a feed cursor as an Option.\n * Currently stubbed — cursor operations are not yet implemented.\n */\nexport const nextOption = <T = Obj.Snapshot>(\n _cursor: Cursor<T>,\n): Effect.Effect<Option.Option<T>, never, Database.Service> => Effect.die('Feed.nextOption is not yet implemented');\n\n/**\n * Sets the local retention policy for a feed.\n * Currently stubbed — feeds do not yet support retention.\n *\n * @example\n * ```ts\n * yield* Feed.setRetention(feed, { count: 1000 });\n * ```\n */\n// TODO(dmaretskyi): Implement when feed retention is supported.\nexport const setRetention = (_feed: Feed, _options: RetentionOptions): Effect.Effect<void, never, Database.Service> =>\n Effect.void;\n"],"mappings":"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"}