foldkit-remote 0.21.0 → 0.22.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 (198) hide show
  1. package/README.md +977 -193
  2. package/dist/answerGaps.d.ts +16 -0
  3. package/dist/answerGaps.d.ts.map +1 -0
  4. package/dist/answerGaps.js +60 -0
  5. package/dist/answerGaps.js.map +1 -0
  6. package/dist/client-B055kQuP.d.ts +110 -0
  7. package/dist/client-B055kQuP.d.ts.map +1 -0
  8. package/dist/client.d.ts +2 -0
  9. package/dist/client.js +142 -0
  10. package/dist/client.js.map +1 -0
  11. package/dist/coalesce-CLAr92Nk.d.ts +34 -0
  12. package/dist/coalesce-CLAr92Nk.d.ts.map +1 -0
  13. package/dist/coalesce.d.ts +2 -0
  14. package/dist/coalesce.js +131 -0
  15. package/dist/coalesce.js.map +1 -0
  16. package/dist/connection-BRyaJy89.d.ts +45 -0
  17. package/dist/connection-BRyaJy89.d.ts.map +1 -0
  18. package/dist/connection.d.ts +2 -0
  19. package/dist/connection.js +170 -0
  20. package/dist/connection.js.map +1 -0
  21. package/dist/data.d.ts +7 -0
  22. package/dist/data.d.ts.map +1 -0
  23. package/dist/data.js +21 -0
  24. package/dist/data.js.map +1 -0
  25. package/dist/diagnostics-DHhVYflV.d.ts +125 -0
  26. package/dist/diagnostics-DHhVYflV.d.ts.map +1 -0
  27. package/dist/diagnostics.d.ts +2 -0
  28. package/dist/diagnostics.js +60 -0
  29. package/dist/diagnostics.js.map +1 -0
  30. package/dist/entity-CgsG5Uel.d.ts +196 -0
  31. package/dist/entity-CgsG5Uel.d.ts.map +1 -0
  32. package/dist/entity-DuDxaa3n.js +279 -0
  33. package/dist/entity-DuDxaa3n.js.map +1 -0
  34. package/dist/entity.d.ts +2 -0
  35. package/dist/entity.js +1 -0
  36. package/dist/held.d.ts +19 -0
  37. package/dist/held.d.ts.map +1 -0
  38. package/dist/held.js +70 -0
  39. package/dist/held.js.map +1 -0
  40. package/dist/impact.d.ts +79 -0
  41. package/dist/impact.d.ts.map +1 -0
  42. package/dist/impact.js +166 -0
  43. package/dist/impact.js.map +1 -0
  44. package/dist/index.d.ts +1542 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +2791 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/inspect-DtRQGCRP.d.ts +134 -0
  49. package/dist/inspect-DtRQGCRP.d.ts.map +1 -0
  50. package/dist/inspect.d.ts +2 -0
  51. package/dist/inspect.js +33 -0
  52. package/dist/inspect.js.map +1 -0
  53. package/dist/json-DowLl1eB.d.ts +52 -0
  54. package/dist/json-DowLl1eB.d.ts.map +1 -0
  55. package/dist/json.d.ts +2 -0
  56. package/dist/json.js +123 -0
  57. package/dist/json.js.map +1 -0
  58. package/dist/law.d.ts +56 -0
  59. package/dist/law.d.ts.map +1 -0
  60. package/dist/law.js +201 -0
  61. package/dist/law.js.map +1 -0
  62. package/dist/live-BpTzlCnY.d.ts +79 -0
  63. package/dist/live-BpTzlCnY.d.ts.map +1 -0
  64. package/dist/live.d.ts +2 -0
  65. package/dist/live.js +126 -0
  66. package/dist/live.js.map +1 -0
  67. package/dist/matching-DF5F9Us8.d.ts +100 -0
  68. package/dist/matching-DF5F9Us8.d.ts.map +1 -0
  69. package/dist/matching.d.ts +2 -0
  70. package/dist/matching.js +172 -0
  71. package/dist/matching.js.map +1 -0
  72. package/dist/model-B6zk1NMB.d.ts +710 -0
  73. package/dist/model-B6zk1NMB.d.ts.map +1 -0
  74. package/dist/model.d.ts +2 -0
  75. package/dist/model.js +1308 -0
  76. package/dist/model.js.map +1 -0
  77. package/dist/mutation.d.ts +2 -0
  78. package/dist/mutation.js +612 -0
  79. package/dist/mutation.js.map +1 -0
  80. package/dist/optimistic-CECsfNGY.d.ts +523 -0
  81. package/dist/optimistic-CECsfNGY.d.ts.map +1 -0
  82. package/dist/optimistic.d.ts +2 -0
  83. package/dist/optimistic.js +336 -0
  84. package/dist/optimistic.js.map +1 -0
  85. package/dist/outbox-DNuc4kZp.d.ts +259 -0
  86. package/dist/outbox-DNuc4kZp.d.ts.map +1 -0
  87. package/dist/outbox.d.ts +2 -0
  88. package/dist/outbox.js +326 -0
  89. package/dist/outbox.js.map +1 -0
  90. package/dist/patchValues.d.ts +10 -0
  91. package/dist/patchValues.d.ts.map +1 -0
  92. package/dist/patchValues.js +57 -0
  93. package/dist/patchValues.js.map +1 -0
  94. package/dist/persistence-CCMnvg7-.d.ts +114 -0
  95. package/dist/persistence-CCMnvg7-.d.ts.map +1 -0
  96. package/dist/persistence.d.ts +2 -0
  97. package/dist/persistence.js +205 -0
  98. package/dist/persistence.js.map +1 -0
  99. package/dist/placement-jZdsIpmj.d.ts +50 -0
  100. package/dist/placement-jZdsIpmj.d.ts.map +1 -0
  101. package/dist/placement.d.ts +2 -0
  102. package/dist/placement.js +116 -0
  103. package/dist/placement.js.map +1 -0
  104. package/dist/plan-D9cftxC_.d.ts +35 -0
  105. package/dist/plan-D9cftxC_.d.ts.map +1 -0
  106. package/dist/plan.d.ts +2 -0
  107. package/dist/plan.js +100 -0
  108. package/dist/plan.js.map +1 -0
  109. package/dist/policy-DsWHmIAq.d.ts +42 -0
  110. package/dist/policy-DsWHmIAq.d.ts.map +1 -0
  111. package/dist/policy.d.ts +2 -0
  112. package/dist/policy.js +32 -0
  113. package/dist/policy.js.map +1 -0
  114. package/dist/{port.d.mts → port.d.ts} +3 -2
  115. package/dist/port.d.ts.map +1 -0
  116. package/dist/{port.mjs → port.js} +3 -2
  117. package/dist/port.js.map +1 -0
  118. package/dist/query-BuIU93xA.d.ts +141 -0
  119. package/dist/query-BuIU93xA.d.ts.map +1 -0
  120. package/dist/query.d.ts +2 -0
  121. package/dist/query.js +0 -0
  122. package/dist/query.js.map +1 -0
  123. package/dist/record.d.ts +11 -0
  124. package/dist/record.d.ts.map +1 -0
  125. package/dist/record.js +21 -0
  126. package/dist/record.js.map +1 -0
  127. package/dist/relation-BLBGOZom.d.ts +59 -0
  128. package/dist/relation-BLBGOZom.d.ts.map +1 -0
  129. package/dist/relation.d.ts +2 -0
  130. package/dist/relation.js +93 -0
  131. package/dist/relation.js.map +1 -0
  132. package/dist/remoteData-mK1suYi1.d.ts +148 -0
  133. package/dist/remoteData-mK1suYi1.d.ts.map +1 -0
  134. package/dist/remoteData.d.ts +2 -0
  135. package/dist/remoteData.js +179 -0
  136. package/dist/remoteData.js.map +1 -0
  137. package/dist/reporting.d.ts +12 -0
  138. package/dist/reporting.d.ts.map +1 -0
  139. package/dist/reporting.js +45 -0
  140. package/dist/reporting.js.map +1 -0
  141. package/dist/requirement-D57S2LpH.d.ts +64 -0
  142. package/dist/requirement-D57S2LpH.d.ts.map +1 -0
  143. package/dist/requirement.d.ts +2 -0
  144. package/dist/requirement.js +114 -0
  145. package/dist/requirement.js.map +1 -0
  146. package/dist/resume-Ct5R6yli.d.ts +92 -0
  147. package/dist/resume-Ct5R6yli.d.ts.map +1 -0
  148. package/dist/resume.d.ts +2 -0
  149. package/dist/resume.js +222 -0
  150. package/dist/resume.js.map +1 -0
  151. package/dist/retain-BfNTvBRb.d.ts +48 -0
  152. package/dist/retain-BfNTvBRb.d.ts.map +1 -0
  153. package/dist/retain.d.ts +2 -0
  154. package/dist/retain.js +107 -0
  155. package/dist/retain.js.map +1 -0
  156. package/dist/selection.d.ts +2 -0
  157. package/dist/selection.js +1 -0
  158. package/dist/shown.d.ts +20 -0
  159. package/dist/shown.d.ts.map +1 -0
  160. package/dist/shown.js +40 -0
  161. package/dist/shown.js.map +1 -0
  162. package/dist/sse-C7LhdbiQ.d.ts +17 -0
  163. package/dist/sse-C7LhdbiQ.d.ts.map +1 -0
  164. package/dist/sse.d.ts +2 -0
  165. package/dist/sse.js +127 -0
  166. package/dist/sse.js.map +1 -0
  167. package/dist/store-ah2qjcJW.d.ts +100 -0
  168. package/dist/store-ah2qjcJW.d.ts.map +1 -0
  169. package/dist/store.d.ts +2 -0
  170. package/dist/store.js +218 -0
  171. package/dist/store.js.map +1 -0
  172. package/dist/testing.d.ts +133 -0
  173. package/dist/testing.d.ts.map +1 -0
  174. package/dist/testing.js +0 -0
  175. package/dist/testing.js.map +1 -0
  176. package/dist/why-kgp_Wr3z.d.ts +483 -0
  177. package/dist/why-kgp_Wr3z.d.ts.map +1 -0
  178. package/dist/why.d.ts +2 -0
  179. package/dist/why.js +293 -0
  180. package/dist/why.js.map +1 -0
  181. package/dist/whyReasons.d.ts +42 -0
  182. package/dist/whyReasons.d.ts.map +1 -0
  183. package/dist/whyReasons.js +211 -0
  184. package/dist/whyReasons.js.map +1 -0
  185. package/dist/wire-DthPcTM6.d.ts +787 -0
  186. package/dist/wire-DthPcTM6.d.ts.map +1 -0
  187. package/dist/wire.d.ts +2 -0
  188. package/dist/wire.js +329 -0
  189. package/dist/wire.js.map +1 -0
  190. package/package.json +18 -13
  191. package/dist/index-YyucgTYm.d.mts +0 -4115
  192. package/dist/index-YyucgTYm.d.mts.map +0 -1
  193. package/dist/index.d.mts +0 -2
  194. package/dist/index.mjs +0 -1
  195. package/dist/port.d.mts.map +0 -1
  196. package/dist/port.mjs.map +0 -1
  197. package/dist/src-lVP_7bf4.mjs +0 -6382
  198. package/dist/src-lVP_7bf4.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"optimistic-CECsfNGY.d.ts","names":[],"sources":["../src/mutation.ts","../src/optimistic.ts"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { S as visibleStore, _ as removeOverlay, a as OptimisticOperation, b as settleSuccess, c as addOverlay, d as emptyOptimistic, f as goneIn, g as removeLayer, h as pruneOverlays, i as EntityLayer, l as beginOptimistic, m as plannedStore, n as ConnectionIdentity, o as OptimisticState, p as heldOnlyByRequests, r as ConnectionOverlay, s as addLayer, t as ConnectionChange, u as connectionIdentity, v as resolvedPatches, x as visibleItems, y as settleFailure } from "./optimistic-CECsfNGY.js";
2
+ export { ConnectionChange, ConnectionIdentity, ConnectionOverlay, EntityLayer, OptimisticOperation, OptimisticState, addLayer, addOverlay, beginOptimistic, connectionIdentity, emptyOptimistic, goneIn, heldOnlyByRequests, plannedStore, pruneOverlays, removeLayer, removeOverlay, resolvedPatches, settleFailure, settleSuccess, visibleItems, visibleStore };
@@ -0,0 +1,336 @@
1
+ import { put } from "./record.js";
2
+ import { isPatch } from "./shown.js";
3
+ import { entityKey, isTombstone, tombstone, writeEntities } from "./store.js";
4
+ import { reconcileMutation } from "./mutation.js";
5
+ import { edge, items } from "./connection.js";
6
+ import { Option } from "effect";
7
+ import { Operators, QueryEvaluateError, Write } from "foldkit-entity";
8
+ // src/optimistic.ts
9
+ /**
10
+ * Optimistic layers. Pending changes are held as ordered layers over a base
11
+ * store and as overlays over a connection; the visible value is recomputed, never
12
+ * patched with inverses. Settling is remove-the-layer, so overlapping layers
13
+ * rebase for free.
14
+ */
15
+ const toOverlays = (id, changes) => changes.map((change) => ({
16
+ id,
17
+ connection: change.connection,
18
+ edges: [change.edge],
19
+ position: change._tag === "Insert" ? change.position : "remove"
20
+ }));
21
+ const connectionIdentity = (connection) => typeof connection === "string" ? connection : connection.identity;
22
+ const change = (connection, ref, position) => position === "remove" ? {
23
+ _tag: "Remove",
24
+ connection: connectionIdentity(connection),
25
+ edge: edge(ref)
26
+ } : {
27
+ _tag: "Insert",
28
+ connection: connectionIdentity(connection),
29
+ position,
30
+ edge: edge(ref)
31
+ };
32
+ /**
33
+ * Constructors for connection changes: what a request shows optimistically
34
+ * (`MutateOptions.optimistic`) and what a mutation source reports it made
35
+ * (`MutationOutcome.connections`).
36
+ */
37
+ const ConnectionChange = {
38
+ /** Show `ref` at the front of the connection until the request settles. */
39
+ prepend: (connection, ref) => change(connection, ref, "prepend"),
40
+ /** Show `ref` at the end of the connection until the request settles. */
41
+ append: (connection, ref) => change(connection, ref, "append"),
42
+ /** Hide `ref` from the connection until the request settles. */
43
+ remove: (connection, ref) => change(connection, ref, "remove")
44
+ };
45
+ /**
46
+ * Applies a request's operations: its patches and deletions become one layer
47
+ * and its connection changes become overlays, all owned by `requestId` so
48
+ * settling removes every one of them together.
49
+ */
50
+ const beginOptimistic = (optimistic, requestId, operations, viewer = Option.none()) => {
51
+ const patches = [];
52
+ const deletes = [];
53
+ const changes = [];
54
+ for (const operation of operations) if (isPatch(operation)) patches.push(operation);
55
+ else if (operation._tag === "Delete") deletes.push(operation);
56
+ else changes.push(operation);
57
+ if (patches.length === 0 && deletes.length === 0 && changes.length === 0) return optimistic;
58
+ return {
59
+ layers: patches.length === 0 && deletes.length === 0 ? optimistic.layers : [...optimistic.layers, {
60
+ id: requestId,
61
+ patches,
62
+ deletes,
63
+ viewer
64
+ }],
65
+ overlays: changes.length === 0 ? optimistic.overlays : [...optimistic.overlays, ...toOverlays(requestId, changes)]
66
+ };
67
+ };
68
+ /**
69
+ * Replaces a request's overlays with the server-confirmed changes, owned by
70
+ * `id`, in the position the request's overlays held, so a confirmed edge keeps
71
+ * its place among other pending inserts.
72
+ */
73
+ const confirm = (optimistic, requestId, id, changes) => {
74
+ const at = optimistic.overlays.findIndex((overlay) => overlay.id === requestId);
75
+ if (at === -1 && changes.length === 0) return optimistic;
76
+ const confirmed = toOverlays(id, changes);
77
+ const others = optimistic.overlays.filter((overlay) => overlay.id !== requestId);
78
+ return {
79
+ ...optimistic,
80
+ overlays: at === -1 ? [...others, ...confirmed] : [
81
+ ...others.slice(0, at),
82
+ ...confirmed,
83
+ ...others.slice(at)
84
+ ]
85
+ };
86
+ };
87
+ const emptyOptimistic = {
88
+ layers: [],
89
+ overlays: []
90
+ };
91
+ const addLayer = (optimistic, layer) => ({
92
+ ...optimistic,
93
+ layers: [...optimistic.layers, layer]
94
+ });
95
+ const removeLayer = (optimistic, id) => {
96
+ const layers = optimistic.layers.filter((layer) => layer.id !== id);
97
+ return layers.length === optimistic.layers.length ? optimistic : {
98
+ ...optimistic,
99
+ layers
100
+ };
101
+ };
102
+ const addOverlay = (optimistic, overlay) => ({
103
+ ...optimistic,
104
+ overlays: [...optimistic.overlays, overlay]
105
+ });
106
+ const removeOverlay = (optimistic, id) => {
107
+ const overlays = optimistic.overlays.filter((overlay) => overlay.id !== id);
108
+ return overlays.length === optimistic.overlays.length ? optimistic : {
109
+ ...optimistic,
110
+ overlays
111
+ };
112
+ };
113
+ /**
114
+ * Base store with every layer applied in order. Later layers win. A patch is
115
+ * not news from the server, so it keeps the date of the value under it; an
116
+ * entity only a request holds (a temporary id) is not the server's to age, and
117
+ * is dated `Infinity`, so freshness never plans or times it. A row a layer
118
+ * deletes is a tombstone over all of them. A patch over a row the server
119
+ * holds deleted shows it, as a create of a row known absent does; a patch a
120
+ * later request's delete settled is taken out of its layer (`settleSuccess`).
121
+ */
122
+ const visibleStore = (base, optimistic, operators = Operators.kernel) => [...pendingDeletes(optimistic)].reduce(tombstone, writeEntities(base, resolvedPatches(base, optimistic, operators).map((patch) => {
123
+ const key = entityKey(patch.entity, patch.id);
124
+ const under = base[key];
125
+ return {
126
+ key,
127
+ values: patch.values,
128
+ updatedAt: under === void 0 || under.tombstone ? Infinity : under.updatedAt
129
+ };
130
+ })));
131
+ const pendingDeletes = (optimistic) => new Set(optimistic.layers.flatMap((layer) => layer.deletes.map((gone) => entityKey(gone.entity, gone.id))));
132
+ /**
133
+ * The store a read plans over: the visible store, except that a row the
134
+ * server holds is planned by what the server holds of it, so a field only a
135
+ * layer shows is still fetched. Its values stay the visible ones, so a
136
+ * relation a layer points elsewhere is followed where it points. A row only a
137
+ * request holds and a row a layer deletes are planned as shown; the first is
138
+ * then left out of the read (`heldOnlyByRequests`).
139
+ */
140
+ const plannedStore = (base, visible, optimistic) => {
141
+ let next;
142
+ for (const layer of optimistic.layers) for (const patch of layer.patches) {
143
+ const key = entityKey(patch.entity, patch.id);
144
+ const under = base[key];
145
+ const shown = visible[key];
146
+ if (under === void 0 || under.tombstone || shown === void 0 || shown.tombstone) continue;
147
+ next ??= { ...visible };
148
+ next[key] = {
149
+ ...under,
150
+ values: shown.values
151
+ };
152
+ }
153
+ return next ?? visible;
154
+ };
155
+ /**
156
+ * The rows only a pending request holds: a layer shows each over a row the
157
+ * server holds nothing of (never read, or read as absent). A read of one is
158
+ * not sent. The server has nothing for it until the request lands, so it would
159
+ * answer "absent", which leaves the plan as it was. Once the answer lands
160
+ * the plan would also be unchanged, so the read entry would never restart and
161
+ * a field the answer omits (a guard's `may`) would never be fetched. Left out
162
+ * now, the row's missing fields enter the plan when its answer lands, and that
163
+ * change starts the read.
164
+ */
165
+ const heldOnlyByRequests = (base, optimistic) => {
166
+ const held = /* @__PURE__ */ new Set();
167
+ for (const layer of optimistic.layers) for (const patch of layer.patches) {
168
+ const key = entityKey(patch.entity, patch.id);
169
+ const under = base[key];
170
+ if (under === void 0 || under.tombstone) held.add(key);
171
+ }
172
+ return held;
173
+ };
174
+ /**
175
+ * Whether a list hides a row: the store holds it deleted, or a pending layer
176
+ * deletes it. Read from the server's store, not the visible one, where a
177
+ * pending patch over a deleted row clears its tombstone.
178
+ */
179
+ const goneIn = (store, optimistic) => {
180
+ const deleted = pendingDeletes(optimistic);
181
+ return (key) => deleted.has(key) || isTombstone(store, key);
182
+ };
183
+ /**
184
+ * Every pending patch in order, each amount added and each computed field
185
+ * worked out over the row under it (the store's, with earlier layers over
186
+ * it) by `Write.apply`, with the client's table (the kernel's when not
187
+ * given). A prediction, so only over
188
+ * what the client holds: a field it has not read, an amount over anything
189
+ * but a number, or a computation by an operator its table lacks keeps what
190
+ * it shows until the server answers.
191
+ */
192
+ const resolvedPatches = (base, optimistic, operators = Operators.kernel) => {
193
+ const shown = /* @__PURE__ */ new Map();
194
+ const unsure = /* @__PURE__ */ new Map();
195
+ return optimistic.layers.flatMap((layer) => layer.patches.map(({ adds, computes, ...patch }) => {
196
+ const key = entityKey(patch.entity, patch.id);
197
+ const earlier = shown.get(key) ?? {};
198
+ const unknown = new Set([...unsure.get(key) ?? []].filter((field) => !Object.hasOwn(patch.values, field)));
199
+ const values = { ...patch.values };
200
+ const row = Object.fromEntries(Object.entries({
201
+ ...base[key]?.values,
202
+ ...earlier
203
+ }).filter(([field]) => !unknown.has(field)));
204
+ const moved = {
205
+ values: {},
206
+ increments: adds ?? {},
207
+ assignments: computes ?? {}
208
+ };
209
+ const predicted = predicting(() => Write.apply(row, moved, { operators }));
210
+ for (const field of [...Object.keys(moved.increments), ...Object.keys(moved.assignments)]) if (predicted !== void 0 && !predicted.unknown.includes(field)) {
211
+ put(values, field, predicted.row[field]);
212
+ unknown.delete(field);
213
+ } else unknown.add(field);
214
+ shown.set(key, {
215
+ ...earlier,
216
+ ...values
217
+ });
218
+ unsure.set(key, unknown);
219
+ return {
220
+ ...patch,
221
+ values
222
+ };
223
+ }));
224
+ };
225
+ /** A prediction, or none where an operator refuses the row it is given: the server will answer. */
226
+ const predicting = (predict) => {
227
+ try {
228
+ return predict();
229
+ } catch (error) {
230
+ if (error instanceof QueryEvaluateError) return void 0;
231
+ throw error;
232
+ }
233
+ };
234
+ /**
235
+ * A fresh page is newer than any settled overlay it covers: an edge the page
236
+ * carries no longer needs an insert overlay, and a `remove` overlay hiding it
237
+ * is stale (the server says it is back). A pending request's overlays are
238
+ * left alone; they settle with the request.
239
+ */
240
+ const pruneOverlays = (optimistic, connection, covered, pending) => {
241
+ let changed = false;
242
+ const overlays = optimistic.overlays.flatMap((overlay) => {
243
+ if (overlay.connection !== connection || pending.has(overlay.id)) return [overlay];
244
+ const edges = overlay.edges.filter((edge) => !covered.has(edge.key));
245
+ if (edges.length > 0 && edges.length === overlay.edges.length) return [overlay];
246
+ changed = true;
247
+ return edges.length === 0 ? [] : [{
248
+ ...overlay,
249
+ edges
250
+ }];
251
+ });
252
+ return changed ? {
253
+ ...optimistic,
254
+ overlays
255
+ } : optimistic;
256
+ };
257
+ /** Everything a request owns: its layer and its overlays. */
258
+ const release = (optimistic, requestId) => removeOverlay(removeLayer(optimistic, requestId), requestId);
259
+ /**
260
+ * Applies a result idempotently and releases the request's layer and overlays:
261
+ * a later layer re-wins on rebase. Confirmed connection changes are recorded
262
+ * once per request, as overlays the request no longer owns.
263
+ */
264
+ const settleSuccess = (base, optimistic, state, requestId, answer) => {
265
+ const reconciled = reconcileMutation(base, state, requestId, answer);
266
+ return {
267
+ store: reconciled.store,
268
+ state: reconciled.state,
269
+ optimistic: state.applied.has(requestId) ? release(optimistic, requestId) : superseded(confirm(removeLayer(optimistic, requestId), requestId, `confirmed:${requestId}`, answer.connections ?? []), state, requestId, (answer.deleted ?? []).map((gone) => entityKey(gone.entity, gone.id)).filter((key) => isTombstone(reconciled.store, key)))
270
+ };
271
+ };
272
+ /**
273
+ * The layers without the patches of rows `requestId`'s answer deleted, in each
274
+ * request started before it: the server ran those first, so the delete is
275
+ * the row's last word, and their answers will not bring it back. Shown, such
276
+ * a patch would make the deleted row a stub that reads `Initial`.
277
+ */
278
+ const superseded = (optimistic, state, requestId, deleted) => {
279
+ const at = state.order.get(requestId);
280
+ if (at === void 0 || deleted.length === 0) return optimistic;
281
+ const gone = new Set(deleted);
282
+ let changed = false;
283
+ const layers = optimistic.layers.map((layer) => {
284
+ const started = state.order.get(layer.id);
285
+ if (started === void 0 || started > at) return layer;
286
+ const patches = layer.patches.filter((patch) => !gone.has(entityKey(patch.entity, patch.id)));
287
+ if (patches.length === layer.patches.length) return layer;
288
+ changed = true;
289
+ return {
290
+ ...layer,
291
+ patches
292
+ };
293
+ });
294
+ return changed ? {
295
+ ...optimistic,
296
+ layers
297
+ } : optimistic;
298
+ };
299
+ /** A failed request's layer and overlays are dropped; the base was never mutated. */
300
+ const settleFailure = (optimistic, requestId) => release(optimistic, requestId);
301
+ /**
302
+ * Visible edges for one connection: applicable overlays are placed outside the
303
+ * server-known segments and de-duplicated by edge identity (across overlays too),
304
+ * so a pending insert never corrupts server-known ordering; a `remove` overlay
305
+ * hides its edges wherever they are, and so does `gone` (`goneIn`) of the
306
+ * edge's target. A later prepend lands before an earlier one, a later append
307
+ * after, so pending inserts read in the order they were made. Boundaries still
308
+ * come from the connection's segments.
309
+ */
310
+ const visibleItems = (connection, connectionId, overlays, gone = () => false) => {
311
+ const applicable = overlays.filter((overlay) => overlay.connection === connectionId);
312
+ const hidden = /* @__PURE__ */ new Set();
313
+ for (const overlay of applicable) for (const edge of overlay.edges) if (overlay.position === "remove") hidden.add(edge.key);
314
+ else hidden.delete(edge.key);
315
+ const hides = (edge) => hidden.has(edge.key) || gone(entityKey(edge.ref.entity, edge.ref.id));
316
+ const known = items(connection).filter((edge) => !hides(edge));
317
+ const seen = new Set(known.map((edge) => edge.key));
318
+ const take = (position) => {
319
+ const edges = [];
320
+ const ordered = applicable.filter((value) => value.position === position);
321
+ for (const overlay of position === "prepend" ? [...ordered].reverse() : ordered) for (const edge of overlay.edges) {
322
+ if (seen.has(edge.key) || hides(edge)) continue;
323
+ seen.add(edge.key);
324
+ edges.push(edge);
325
+ }
326
+ return edges;
327
+ };
328
+ return [
329
+ ...take("prepend"),
330
+ ...known,
331
+ ...take("append")
332
+ ];
333
+ };
334
+ export { ConnectionChange, addLayer, addOverlay, beginOptimistic, connectionIdentity, emptyOptimistic, goneIn, heldOnlyByRequests, plannedStore, pruneOverlays, removeLayer, removeOverlay, resolvedPatches, settleFailure, settleSuccess, visibleItems, visibleStore };
335
+
336
+ //# sourceMappingURL=optimistic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"optimistic.js","names":[],"sources":["../src/optimistic.ts"],"sourcesContent":["/**\n * Optimistic layers. Pending changes are held as ordered layers over a base\n * store and as overlays over a connection; the visible value is recomputed, never\n * patched with inverses. Settling is remove-the-layer, so overlapping layers\n * rebase for free.\n */\nimport { Option } from 'effect'\nimport {\n Operators,\n QueryEvaluateError,\n Write,\n type EntityRef,\n type MemoryOperators,\n} from 'foldkit-entity'\nimport { type Connection, type Edge, edge, items } from './connection.js'\nimport {\n reconcileMutation,\n type MutationAnswer,\n type MutationState,\n type NormalizedPatch,\n type PendingDelete,\n type PendingPatch,\n} from './mutation.js'\nimport { put } from './record.js'\nimport { isPatch } from './shown.js'\nimport {\n entityKey,\n isTombstone,\n tombstone,\n writeEntities,\n type EntityEntry,\n type EntityKey,\n type EntityStore,\n} from './store.js'\n\nexport interface EntityLayer {\n readonly id: string\n readonly patches: ReadonlyArray<PendingPatch>\n /** Rows it deletes. A delete wins over every layer's patch of its row, as in an answer. */\n readonly deletes: ReadonlyArray<PendingDelete>\n /**\n * Who the operation was predicted for (`Remote.make`'s viewer when it\n * started): what a list body's `Expr.viewer` reads when the client places\n * the rows this layer changes. None for a preview with no operation.\n */\n readonly viewer: Option.Option<EntityRef>\n}\n\n/**\n * Edges placed outside a connection's server-known region (`prepend`,\n * `append`) or hidden from it (`remove`), owned by the request or live event\n * that produced them.\n */\nexport interface ConnectionOverlay {\n readonly id: string\n readonly connection: string\n readonly edges: ReadonlyArray<Edge>\n readonly position: 'prepend' | 'append' | 'remove'\n}\n\n/** A connection change a request makes optimistically or a mutation result confirms. */\nexport type ConnectionChange =\n | {\n readonly _tag: 'Insert'\n readonly connection: string\n readonly position: 'prepend' | 'append'\n readonly edge: Edge\n }\n | { readonly _tag: 'Remove'; readonly connection: string; readonly edge: Edge }\n\n/** What a request changes before the server answers: entity patches, deletions and connection changes. */\nexport type OptimisticOperation = PendingPatch | PendingDelete | ConnectionChange\n\nconst toOverlays = (id: string, changes: ReadonlyArray<ConnectionChange>): ConnectionOverlay[] =>\n changes.map(change => ({\n id,\n connection: change.connection,\n edges: [change.edge],\n position: change._tag === 'Insert' ? change.position : 'remove',\n }))\n\n/** A connection, by identity string or by anything that carries one (a `QueryRef`). */\nexport type ConnectionIdentity = string | { readonly identity: string }\n\nexport const connectionIdentity = (connection: ConnectionIdentity): string =>\n typeof connection === 'string' ? connection : connection.identity\n\nconst change = (\n connection: ConnectionIdentity,\n ref: { readonly entity: string; readonly id: string },\n position: 'prepend' | 'append' | 'remove',\n): ConnectionChange =>\n position === 'remove'\n ? { _tag: 'Remove', connection: connectionIdentity(connection), edge: edge(ref) }\n : { _tag: 'Insert', connection: connectionIdentity(connection), position, edge: edge(ref) }\n\n/**\n * Constructors for connection changes: what a request shows optimistically\n * (`MutateOptions.optimistic`) and what a mutation source reports it made\n * (`MutationOutcome.connections`).\n */\nexport const ConnectionChange = {\n /** Show `ref` at the front of the connection until the request settles. */\n prepend: (\n connection: ConnectionIdentity,\n ref: { readonly entity: string; readonly id: string },\n ): ConnectionChange => change(connection, ref, 'prepend'),\n\n /** Show `ref` at the end of the connection until the request settles. */\n append: (\n connection: ConnectionIdentity,\n ref: { readonly entity: string; readonly id: string },\n ): ConnectionChange => change(connection, ref, 'append'),\n\n /** Hide `ref` from the connection until the request settles. */\n remove: (\n connection: ConnectionIdentity,\n ref: { readonly entity: string; readonly id: string },\n ): ConnectionChange => change(connection, ref, 'remove'),\n}\n\n/**\n * Applies a request's operations: its patches and deletions become one layer\n * and its connection changes become overlays, all owned by `requestId` so\n * settling removes every one of them together.\n */\nexport const beginOptimistic = (\n optimistic: OptimisticState,\n requestId: string,\n operations: ReadonlyArray<OptimisticOperation>,\n viewer: Option.Option<EntityRef> = Option.none(),\n): OptimisticState => {\n const patches: PendingPatch[] = []\n const deletes: PendingDelete[] = []\n const changes: ConnectionChange[] = []\n for (const operation of operations) {\n if (isPatch(operation)) patches.push(operation)\n else if (operation._tag === 'Delete') deletes.push(operation)\n else changes.push(operation)\n }\n if (patches.length === 0 && deletes.length === 0 && changes.length === 0) return optimistic\n return {\n layers:\n patches.length === 0 && deletes.length === 0\n ? optimistic.layers\n : [...optimistic.layers, { id: requestId, patches, deletes, viewer }],\n overlays:\n changes.length === 0\n ? optimistic.overlays\n : [...optimistic.overlays, ...toOverlays(requestId, changes)],\n }\n}\n\n/**\n * Replaces a request's overlays with the server-confirmed changes, owned by\n * `id`, in the position the request's overlays held, so a confirmed edge keeps\n * its place among other pending inserts.\n */\nconst confirm = (\n optimistic: OptimisticState,\n requestId: string,\n id: string,\n changes: ReadonlyArray<ConnectionChange>,\n): OptimisticState => {\n const at = optimistic.overlays.findIndex(overlay => overlay.id === requestId)\n if (at === -1 && changes.length === 0) return optimistic\n const confirmed = toOverlays(id, changes)\n const others = optimistic.overlays.filter(overlay => overlay.id !== requestId)\n return {\n ...optimistic,\n overlays:\n at === -1\n ? [...others, ...confirmed]\n : [...others.slice(0, at), ...confirmed, ...others.slice(at)],\n }\n}\n\n/** Pending entity layers and connection overlays over the base store. */\nexport interface OptimisticState {\n readonly layers: ReadonlyArray<EntityLayer>\n readonly overlays: ReadonlyArray<ConnectionOverlay>\n}\n\nexport const emptyOptimistic: OptimisticState = { layers: [], overlays: [] }\n\nexport const addLayer = (optimistic: OptimisticState, layer: EntityLayer): OptimisticState => ({\n ...optimistic,\n layers: [...optimistic.layers, layer],\n})\n\nexport const removeLayer = (optimistic: OptimisticState, id: string): OptimisticState => {\n const layers = optimistic.layers.filter(layer => layer.id !== id)\n return layers.length === optimistic.layers.length ? optimistic : { ...optimistic, layers }\n}\n\nexport const addOverlay = (\n optimistic: OptimisticState,\n overlay: ConnectionOverlay,\n): OptimisticState => ({\n ...optimistic,\n overlays: [...optimistic.overlays, overlay],\n})\n\nexport const removeOverlay = (optimistic: OptimisticState, id: string): OptimisticState => {\n const overlays = optimistic.overlays.filter(overlay => overlay.id !== id)\n return overlays.length === optimistic.overlays.length ? optimistic : { ...optimistic, overlays }\n}\n\n/**\n * Base store with every layer applied in order. Later layers win. A patch is\n * not news from the server, so it keeps the date of the value under it; an\n * entity only a request holds (a temporary id) is not the server's to age, and\n * is dated `Infinity`, so freshness never plans or times it. A row a layer\n * deletes is a tombstone over all of them. A patch over a row the server\n * holds deleted shows it, as a create of a row known absent does; a patch a\n * later request's delete settled is taken out of its layer (`settleSuccess`).\n */\nexport const visibleStore = (\n base: EntityStore,\n optimistic: OptimisticState,\n operators: MemoryOperators = Operators.kernel,\n): EntityStore =>\n [...pendingDeletes(optimistic)].reduce(\n tombstone,\n writeEntities(\n base,\n resolvedPatches(base, optimistic, operators).map(patch => {\n const key = entityKey(patch.entity, patch.id)\n const under = base[key]\n return {\n key,\n values: patch.values,\n updatedAt: under === undefined || under.tombstone ? Infinity : under.updatedAt,\n }\n }),\n ),\n )\n\nconst pendingDeletes = (optimistic: OptimisticState): ReadonlySet<EntityKey> =>\n new Set(\n optimistic.layers.flatMap(layer => layer.deletes.map(gone => entityKey(gone.entity, gone.id))),\n )\n\n/**\n * The store a read plans over: the visible store, except that a row the\n * server holds is planned by what the server holds of it, so a field only a\n * layer shows is still fetched. Its values stay the visible ones, so a\n * relation a layer points elsewhere is followed where it points. A row only a\n * request holds and a row a layer deletes are planned as shown; the first is\n * then left out of the read (`heldOnlyByRequests`).\n */\nexport const plannedStore = (\n base: EntityStore,\n visible: EntityStore,\n optimistic: OptimisticState,\n): EntityStore => {\n let next: Record<EntityKey, EntityEntry> | undefined\n for (const layer of optimistic.layers)\n for (const patch of layer.patches) {\n const key = entityKey(patch.entity, patch.id)\n const under = base[key]\n const shown = visible[key]\n if (under === undefined || under.tombstone || shown === undefined || shown.tombstone) continue\n next ??= { ...visible }\n next[key] = { ...under, values: shown.values }\n }\n return next ?? visible\n}\n\n/**\n * The rows only a pending request holds: a layer shows each over a row the\n * server holds nothing of (never read, or read as absent). A read of one is\n * not sent. The server has nothing for it until the request lands, so it would\n * answer \"absent\", which leaves the plan as it was. Once the answer lands\n * the plan would also be unchanged, so the read entry would never restart and\n * a field the answer omits (a guard's `may`) would never be fetched. Left out\n * now, the row's missing fields enter the plan when its answer lands, and that\n * change starts the read.\n */\nexport const heldOnlyByRequests = (\n base: EntityStore,\n optimistic: OptimisticState,\n): ReadonlySet<EntityKey> => {\n const held = new Set<EntityKey>()\n for (const layer of optimistic.layers)\n for (const patch of layer.patches) {\n const key = entityKey(patch.entity, patch.id)\n const under = base[key]\n if (under === undefined || under.tombstone) held.add(key)\n }\n return held\n}\n\n/**\n * Whether a list hides a row: the store holds it deleted, or a pending layer\n * deletes it. Read from the server's store, not the visible one, where a\n * pending patch over a deleted row clears its tombstone.\n */\nexport const goneIn = (\n store: EntityStore,\n optimistic: OptimisticState,\n): ((key: EntityKey) => boolean) => {\n const deleted = pendingDeletes(optimistic)\n return key => deleted.has(key) || isTombstone(store, key)\n}\n\n/**\n * Every pending patch in order, each amount added and each computed field\n * worked out over the row under it (the store's, with earlier layers over\n * it) by `Write.apply`, with the client's table (the kernel's when not\n * given). A prediction, so only over\n * what the client holds: a field it has not read, an amount over anything\n * but a number, or a computation by an operator its table lacks keeps what\n * it shows until the server answers.\n */\nexport const resolvedPatches = (\n base: EntityStore,\n optimistic: OptimisticState,\n operators: MemoryOperators = Operators.kernel,\n): ReadonlyArray<NormalizedPatch> => {\n const shown = new Map<EntityKey, Record<string, unknown>>()\n // Fields an earlier layer could not work out: what the store holds for one is\n // not what the row before a later layer holds, so nothing reading it is predicted.\n const unsure = new Map<EntityKey, ReadonlySet<string>>()\n return optimistic.layers.flatMap(layer =>\n layer.patches.map(({ adds, computes, ...patch }) => {\n const key = entityKey(patch.entity, patch.id)\n const earlier = shown.get(key) ?? {}\n const unknown = new Set(\n [...(unsure.get(key) ?? [])].filter(field => !Object.hasOwn(patch.values, field)),\n )\n const values: Record<string, unknown> = { ...patch.values }\n // A tombstone holds no values; what the store holds, with what earlier layers show over it.\n const row = Object.fromEntries(\n Object.entries({ ...base[key]?.values, ...earlier }).filter(\n ([field]) => !unknown.has(field),\n ),\n )\n const moved = { values: {}, increments: adds ?? {}, assignments: computes ?? {} }\n const predicted = predicting(() => Write.apply(row, moved, { operators }))\n for (const field of [...Object.keys(moved.increments), ...Object.keys(moved.assignments)]) {\n if (predicted !== undefined && !predicted.unknown.includes(field)) {\n put(values, field, predicted.row[field])\n unknown.delete(field)\n } else {\n unknown.add(field)\n }\n }\n shown.set(key, { ...earlier, ...values })\n unsure.set(key, unknown)\n return { ...patch, values }\n }),\n )\n}\n\n/** A prediction, or none where an operator refuses the row it is given: the server will answer. */\nconst predicting = <A>(predict: () => A): A | undefined => {\n try {\n return predict()\n } catch (error) {\n if (error instanceof QueryEvaluateError) return undefined\n throw error\n }\n}\n\n/**\n * A fresh page is newer than any settled overlay it covers: an edge the page\n * carries no longer needs an insert overlay, and a `remove` overlay hiding it\n * is stale (the server says it is back). A pending request's overlays are\n * left alone; they settle with the request.\n */\nexport const pruneOverlays = (\n optimistic: OptimisticState,\n connection: string,\n covered: ReadonlySet<string>,\n pending: ReadonlySet<string>,\n): OptimisticState => {\n let changed = false\n const overlays = optimistic.overlays.flatMap(overlay => {\n if (overlay.connection !== connection || pending.has(overlay.id)) return [overlay]\n const edges = overlay.edges.filter(edge => !covered.has(edge.key))\n // An overlay a live remove emptied goes too: it shows nothing.\n if (edges.length > 0 && edges.length === overlay.edges.length) return [overlay]\n changed = true\n return edges.length === 0 ? [] : [{ ...overlay, edges }]\n })\n return changed ? { ...optimistic, overlays } : optimistic\n}\n\n/** Everything a request owns: its layer and its overlays. */\nconst release = (optimistic: OptimisticState, requestId: string): OptimisticState =>\n removeOverlay(removeLayer(optimistic, requestId), requestId)\n\n/**\n * Applies a result idempotently and releases the request's layer and overlays:\n * a later layer re-wins on rebase. Confirmed connection changes are recorded\n * once per request, as overlays the request no longer owns.\n */\nexport const settleSuccess = (\n base: EntityStore,\n optimistic: OptimisticState,\n state: MutationState,\n requestId: string,\n answer: MutationAnswer & {\n /** Connection changes the server confirmed; they replace the request's own. */\n readonly connections?: ReadonlyArray<ConnectionChange> | undefined\n },\n): {\n readonly store: EntityStore\n readonly state: MutationState\n readonly optimistic: OptimisticState\n} => {\n const reconciled = reconcileMutation(base, state, requestId, answer)\n return {\n store: reconciled.store,\n state: reconciled.state,\n optimistic: state.applied.has(requestId)\n ? release(optimistic, requestId)\n : superseded(\n confirm(\n removeLayer(optimistic, requestId),\n requestId,\n `confirmed:${requestId}`,\n answer.connections ?? [],\n ),\n state,\n requestId,\n (answer.deleted ?? [])\n .map(gone => entityKey(gone.entity, gone.id))\n .filter(key => isTombstone(reconciled.store, key)),\n ),\n }\n}\n\n/**\n * The layers without the patches of rows `requestId`'s answer deleted, in each\n * request started before it: the server ran those first, so the delete is\n * the row's last word, and their answers will not bring it back. Shown, such\n * a patch would make the deleted row a stub that reads `Initial`.\n */\nconst superseded = (\n optimistic: OptimisticState,\n state: MutationState,\n requestId: string,\n deleted: ReadonlyArray<EntityKey>,\n): OptimisticState => {\n const at = state.order.get(requestId)\n if (at === undefined || deleted.length === 0) return optimistic\n const gone = new Set(deleted)\n let changed = false\n const layers = optimistic.layers.map(layer => {\n const started = state.order.get(layer.id)\n if (started === undefined || started > at) return layer\n const patches = layer.patches.filter(patch => !gone.has(entityKey(patch.entity, patch.id)))\n if (patches.length === layer.patches.length) return layer\n changed = true\n return { ...layer, patches }\n })\n return changed ? { ...optimistic, layers } : optimistic\n}\n\n/** A failed request's layer and overlays are dropped; the base was never mutated. */\nexport const settleFailure = (optimistic: OptimisticState, requestId: string): OptimisticState =>\n release(optimistic, requestId)\n\n/**\n * Visible edges for one connection: applicable overlays are placed outside the\n * server-known segments and de-duplicated by edge identity (across overlays too),\n * so a pending insert never corrupts server-known ordering; a `remove` overlay\n * hides its edges wherever they are, and so does `gone` (`goneIn`) of the\n * edge's target. A later prepend lands before an earlier one, a later append\n * after, so pending inserts read in the order they were made. Boundaries still\n * come from the connection's segments.\n */\nexport const visibleItems = (\n connection: Connection,\n connectionId: string,\n overlays: ReadonlyArray<ConnectionOverlay>,\n gone: (key: EntityKey) => boolean = () => false,\n): ReadonlyArray<Edge> => {\n const applicable = overlays.filter(overlay => overlay.connection === connectionId)\n // Overlays are ordered evidence: the last word on an edge wins, so a remove\n // hides it only until a later insert brings it back.\n const hidden = new Set<string>()\n for (const overlay of applicable) {\n for (const edge of overlay.edges) {\n if (overlay.position === 'remove') hidden.add(edge.key)\n else hidden.delete(edge.key)\n }\n }\n const hides = (edge: Edge): boolean =>\n hidden.has(edge.key) || gone(entityKey(edge.ref.entity, edge.ref.id))\n const known = items(connection).filter(edge => !hides(edge))\n const seen = new Set(known.map(edge => edge.key))\n\n const take = (position: 'prepend' | 'append'): ReadonlyArray<Edge> => {\n const edges: Edge[] = []\n const ordered = applicable.filter(value => value.position === position)\n for (const overlay of position === 'prepend' ? [...ordered].reverse() : ordered) {\n for (const edge of overlay.edges) {\n if (seen.has(edge.key) || hides(edge)) continue\n seen.add(edge.key)\n edges.push(edge)\n }\n }\n return edges\n }\n\n return [...take('prepend'), ...known, ...take('append')]\n}\n"],"mappings":"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"}
@@ -0,0 +1,259 @@
1
+ import { i as RemoteError } from "./remoteData-mK1suYi1.js";
2
+ import { a as OptimisticOperation } from "./optimistic-CECsfNGY.js";
3
+ import { n as RelationRequirement } from "./requirement-D57S2LpH.js";
4
+ import { Context, Data, Duration, Effect, Option, Schema } from "effect";
5
+ import { KeyValueStore } from "effect/persistence";
6
+ /** Where a queued write stands, as the store keeps it. */
7
+ type QueuedState = {
8
+ readonly _tag: 'Waiting';
9
+ } | {
10
+ readonly _tag: 'Failed';
11
+ readonly error: RemoteError;
12
+ };
13
+ /**
14
+ * A durable write as the store keeps it: what a later page needs to show it
15
+ * and send it as itself. Its input is encoded by the Mutation's `Input`, so a
16
+ * page reads it back with the Mutation it has then.
17
+ */
18
+ declare const StoredWrite: Schema.Struct<{
19
+ /** Its operation id, which is also its request id. */
20
+ readonly operation: Schema.String;
21
+ readonly mutation: Schema.String;
22
+ readonly input: Schema.Unknown;
23
+ readonly keys: Schema.optionalKey<Schema.$Array<Schema.String>>;
24
+ readonly now: Schema.optionalKey<Schema.Number>;
25
+ /** The layer it shows until it settles. */
26
+ readonly optimistic: Schema.$Array<Schema.Codec<OptimisticOperation, OptimisticOperation, never, never>>;
27
+ /** Who its layer was predicted for. */
28
+ readonly viewer: Schema.optionalKey<Schema.Struct<{
29
+ readonly entity: Schema.String;
30
+ readonly id: Schema.String;
31
+ }>>;
32
+ /** What the reader was shown when they acted, for the answer to carry. */
33
+ readonly shown: Schema.$Array<Schema.Codec<RelationRequirement, RelationRequirement, never, never>>;
34
+ /** Moves on each change of `state`; of two copies, the higher wins. */
35
+ readonly revision: Schema.Number;
36
+ readonly state: Schema.Union<readonly [Schema.Struct<{
37
+ readonly _tag: Schema.Literal<"Waiting">;
38
+ }>, Schema.Struct<{
39
+ readonly _tag: Schema.Literal<"Failed">;
40
+ readonly error: Schema.Struct<{
41
+ readonly _tag: Schema.String;
42
+ readonly message: Schema.String;
43
+ readonly refusal: Schema.optional<Schema.Unknown>;
44
+ readonly reason: Schema.optional<Schema.String>;
45
+ }>;
46
+ }>]>;
47
+ }>;
48
+ type StoredWrite = typeof StoredWrite.Type;
49
+ /** A key's queue as stored, each entry as it was written, read or not. */
50
+ interface StoredOutbox {
51
+ readonly entries: ReadonlyArray<unknown>;
52
+ readonly done: ReadonlyArray<string>;
53
+ }
54
+ declare const emptyStoredOutbox: StoredOutbox;
55
+ /** `done` with `settled` after it, the newest `DONE_WINDOW`, each once. */
56
+ declare const withDone: (done: ReadonlyArray<string>, settled: ReadonlyArray<string>) => ReadonlyArray<string>;
57
+ /**
58
+ * Two copies of a queue as one: entries of both by operation, none that
59
+ * either settled, the higher revision of each, in the store's order and then
60
+ * mine. A join, so tabs that save at once lose nothing the next save does
61
+ * not put back.
62
+ */
63
+ declare const mergeStored: (store: StoredOutbox, mine: StoredOutbox) => StoredOutbox;
64
+ /** The record under `key` for `scope`: none when absent, of another scope or version, or not JSON. */
65
+ declare const readStored: (key: string, scope: string) => Effect.Effect<StoredOutbox, KeyValueStore.KeyValueStoreError, KeyValueStore.KeyValueStore>;
66
+ /**
67
+ * Whether the record under `key` holds writes waiting for another scope than
68
+ * `scope`: a key that is not one per principal. Writing over it would lose
69
+ * them, so it is not written.
70
+ */
71
+ declare const othersWaiting: (key: string, scope: string) => Effect.Effect<boolean, KeyValueStore.KeyValueStoreError, KeyValueStore.KeyValueStore>;
72
+ declare const storedText: (outbox: StoredOutbox, scope: string) => string;
73
+ /** A queue's text, as `storedText` wrote it, read back. */
74
+ declare const storedFromText: (text: string) => StoredOutbox;
75
+ /**
76
+ * Whether the store says `operation` is settled: done, or refused. A store
77
+ * that lacks it says nothing (it could not be written), and it is sent.
78
+ */
79
+ declare const settledIn: (outbox: StoredOutbox, operation: string) => boolean;
80
+ /** A queued write in the Model. */
81
+ type Queued = Data.TaggedEnum<{
82
+ /** One this version can send: shown, in the ledger, and sent in its turn. */
83
+ Readable: {
84
+ readonly write: StoredWrite;
85
+ /** Whether the store is known to hold this revision: only then is it sent. */
86
+ readonly stored: boolean;
87
+ /** Sends in a row that nothing answered, this page. */
88
+ readonly attempt: number;
89
+ /** Why the last of them failed. */
90
+ readonly delayed: Option.Option<RemoteError>;
91
+ };
92
+ /** One this version cannot read (a Mutation it lacks, an input that no longer decodes): kept, never sent. */
93
+ Unreadable: {
94
+ readonly operation: string;
95
+ readonly revision: number;
96
+ /** The entry as it was stored, written back as it is. */
97
+ readonly raw: unknown;
98
+ readonly reason: string;
99
+ readonly stored: boolean;
100
+ };
101
+ }>;
102
+ declare const Queued: Data.TaggedEnum.Constructor<{
103
+ readonly _tag: "Readable";
104
+ readonly write: StoredWrite;
105
+ readonly stored: boolean;
106
+ readonly attempt: number;
107
+ readonly delayed: Option.Option<RemoteError>;
108
+ } | {
109
+ readonly _tag: "Unreadable";
110
+ readonly operation: string;
111
+ readonly revision: number;
112
+ readonly raw: unknown;
113
+ readonly reason: string;
114
+ readonly stored: boolean;
115
+ }>;
116
+ /** The durable writes this page holds, and the store they are copied to. */
117
+ interface OutboxState {
118
+ /** Names this page load's operations; minted by the outbox's restore. */
119
+ readonly session: Option.Option<string>;
120
+ /** The store key the queue was read from; it is saved there and nowhere else. */
121
+ readonly key: Option.Option<string>;
122
+ /**
123
+ * Whose queue it is, as it was read: a page whose scope moves on sends none
124
+ * of it, and reads the new scope's instead.
125
+ */
126
+ readonly scope: Option.Option<string>;
127
+ /** Every tab's queued writes, in the order they are sent. */
128
+ readonly queue: ReadonlyArray<Queued>;
129
+ /** Operations settled lately, here or in another tab. */
130
+ readonly done: ReadonlyArray<string>;
131
+ }
132
+ declare const emptyOutbox: OutboxState;
133
+ declare const operationOf: (value: {
134
+ readonly _tag: "Readable";
135
+ readonly write: StoredWrite;
136
+ readonly stored: boolean;
137
+ readonly attempt: number;
138
+ readonly delayed: Option.Option<RemoteError>;
139
+ } | {
140
+ readonly _tag: "Unreadable";
141
+ readonly operation: string;
142
+ readonly revision: number;
143
+ readonly raw: unknown;
144
+ readonly reason: string;
145
+ readonly stored: boolean;
146
+ }) => string;
147
+ declare const revisionOf: (value: {
148
+ readonly _tag: "Readable";
149
+ readonly write: StoredWrite;
150
+ readonly stored: boolean;
151
+ readonly attempt: number;
152
+ readonly delayed: Option.Option<RemoteError>;
153
+ } | {
154
+ readonly _tag: "Unreadable";
155
+ readonly operation: string;
156
+ readonly revision: number;
157
+ readonly raw: unknown;
158
+ readonly reason: string;
159
+ readonly stored: boolean;
160
+ }) => number;
161
+ /** The queue as the store keeps it. */
162
+ declare const storedOf: (outbox: OutboxState) => StoredOutbox;
163
+ /** Whether every queued write is in the store as the Model holds it. */
164
+ declare const allStored: (outbox: OutboxState) => boolean;
165
+ /** The write sent next: the oldest one waiting that the store holds. */
166
+ declare const headOf: (outbox: OutboxState) => Option.Option<StoredWrite>;
167
+ /**
168
+ * A stored entry as a page reads it: a write it can send, with its input
169
+ * decoded, or one it cannot, with why.
170
+ */
171
+ type ReadEntry = {
172
+ readonly _tag: 'Readable';
173
+ readonly write: StoredWrite;
174
+ readonly input: unknown;
175
+ } | {
176
+ readonly _tag: 'Unreadable';
177
+ readonly operation: string;
178
+ readonly revision: number;
179
+ readonly raw: unknown;
180
+ readonly reason: string;
181
+ };
182
+ /** A stored queue as a page reads it, entry by entry: one bad entry never fails the rest. */
183
+ declare const readEntries: (outbox: StoredOutbox, inputOf: (mutation: string) => Option.Option<(input: unknown) => Option.Option<{
184
+ readonly decoded: unknown;
185
+ }>>) => ReadonlyArray<ReadEntry>;
186
+ /** One item of `Data.outboxStatus`. */
187
+ interface OutboxItem {
188
+ readonly requestId: string;
189
+ /** The Mutation it names; none where the stored entry names none this version can read. */
190
+ readonly mutation: Option.Option<string>;
191
+ readonly state: OutboxItemState;
192
+ }
193
+ type OutboxItemState = Data.TaggedEnum<{
194
+ /** Not answered yet: `attempt` sends nothing answered so far, `error` the last one's. */
195
+ Waiting: {
196
+ readonly attempt: number;
197
+ readonly error: Option.Option<RemoteError>;
198
+ };
199
+ /** The server answered no: `Data.retry` or `Data.dismiss` it. */
200
+ Failed: {
201
+ readonly error: RemoteError;
202
+ };
203
+ /** Stored by a version of the app this one cannot read: `Data.dismiss` it. */
204
+ Unreadable: {
205
+ readonly reason: string;
206
+ };
207
+ }>;
208
+ declare const OutboxItemState: Data.TaggedEnum.Constructor<{
209
+ readonly _tag: "Failed";
210
+ readonly error: RemoteError;
211
+ } | {
212
+ readonly _tag: "Unreadable";
213
+ readonly reason: string;
214
+ } | {
215
+ readonly _tag: "Waiting";
216
+ readonly attempt: number;
217
+ readonly error: Option.Option<RemoteError>;
218
+ }>;
219
+ /** What the outbox holds, for a view: "3 changes waiting". */
220
+ interface OutboxStatus {
221
+ readonly waiting: number;
222
+ readonly failed: number;
223
+ /** Every durable write not yet settled, in the order they are sent. */
224
+ readonly items: ReadonlyArray<OutboxItem>;
225
+ }
226
+ declare const outboxStatus: (outbox: OutboxState) => OutboxStatus;
227
+ /**
228
+ * Where the outbox takes its Web Locks: the browser's, where it has them. A
229
+ * trace, which is one tab, takes none.
230
+ */
231
+ declare const OutboxLocks: Context.Reference<Option.Option<LockManager>>;
232
+ /**
233
+ * Holds the Web Lock `name` while `effect` runs, where there are Web Locks;
234
+ * elsewhere runs it as it is (one tab, or a server).
235
+ */
236
+ declare const withLock: <A, E, R>(name: string, effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
237
+ /** The `BroadcastChannel` the tabs on one key speak on; none where there is none. */
238
+ declare const channelFor: (key: string) => Option.Option<BroadcastChannel>;
239
+ /** The lock the tabs on one key send under: one sends, the others wait. */
240
+ declare const sendLock: (key: string) => string;
241
+ /** The lock a write to the store is made under, so two tabs never write it at once. */
242
+ declare const storeLock: (key: string) => string;
243
+ /** How long the outbox waits before sending again after `attempt` sends nothing answered. */
244
+ interface RetryOptions {
245
+ /** The first wait; 1 second unless given. */
246
+ readonly base?: Duration.Input | undefined;
247
+ /** The longest; 30 seconds unless given. */
248
+ readonly max?: Duration.Input | undefined;
249
+ }
250
+ /** `base` doubled per attempt, up to `max`, ±20% so tabs that failed together do not retry together. */
251
+ declare const backoff: (attempt: number, options?: RetryOptions) => Effect.Effect<Duration.Duration>;
252
+ /**
253
+ * Waits until the browser says it is online again, where it can say so;
254
+ * never elsewhere. A wait for a retry races it, so a write goes as soon as
255
+ * the network is back.
256
+ */
257
+ declare const onlineAgain: Effect.Effect<void>;
258
+ export { storedOf as A, readEntries as C, settledIn as D, sendLock as E, withDone as M, withLock as N, storeLock as O, outboxStatus as S, revisionOf as T, headOf as _, OutboxStatus as a, operationOf as b, ReadEntry as c, StoredWrite as d, allStored as f, emptyStoredOutbox as g, emptyOutbox as h, OutboxState as i, storedText as j, storedFromText as k, RetryOptions as l, channelFor as m, OutboxItemState as n, Queued as o, backoff as p, OutboxLocks as r, QueuedState as s, OutboxItem as t, StoredOutbox as u, mergeStored as v, readStored as w, othersWaiting as x, onlineAgain as y };
259
+ //# sourceMappingURL=outbox-DNuc4kZp.d.ts.map