foldkit-remote 0.20.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 +987 -198
  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 +19 -14
  191. package/dist/index-BEvVAL76.d.mts +0 -4097
  192. package/dist/index-BEvVAL76.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-BDqn7Ocr.mjs +0 -6344
  198. package/dist/src-BDqn7Ocr.mjs.map +0 -1
@@ -0,0 +1,16 @@
1
+ import { i as PredictionMismatch } from "./diagnostics-DHhVYflV.js";
2
+ import { r as EntityStore } from "./store-ah2qjcJW.js";
3
+ import { o as OptimisticState } from "./optimistic-CECsfNGY.js";
4
+ import { a as Requirement } from "./requirement-D57S2LpH.js";
5
+ import { Option } from "effect";
6
+ /**
7
+ * Each row the request showed (its layer's patches, its insert overlays and
8
+ * the ones its answer confirmed) with the fields shown on it: those the layer
9
+ * patched and those `read` asks of it, minus what the store now holds.
10
+ *
11
+ * `before` is the optimistic state the answer settled, `after` the one it left;
12
+ * `visible` is the store as reads see it after; `read` is what the active
13
+ * Surfaces ask of it after, as planned.
14
+ */
15
+ export declare const answerGaps: (requestId: string, mutation: Option.Option<string>, before: OptimisticState, after: OptimisticState, visible: EntityStore, read: ReadonlyArray<Requirement>) => ReadonlyArray<PredictionMismatch>;
16
+ //# sourceMappingURL=answerGaps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"answerGaps.d.ts","names":[],"sources":["../src/answerGaps.ts"],"mappings":";;;;;;;;;;;;;;qBAuBa,aAAU,mBACJ,UACP,OAAO,gBAAc,QACvB,iBAAe,OAChB,iBAAe,SACb,aAAW,MACd,cAAc,iBACnB,cAAc"}
@@ -0,0 +1,60 @@
1
+ import { PredictionPart, predictionMismatch } from "./diagnostics.js";
2
+ import { patchedFields } from "./shown.js";
3
+ import { entityKey } from "./store.js";
4
+ // src/answerGaps.ts
5
+ /**
6
+ * Each row the request showed (its layer's patches, its insert overlays and
7
+ * the ones its answer confirmed) with the fields shown on it: those the layer
8
+ * patched and those `read` asks of it, minus what the store now holds.
9
+ *
10
+ * `before` is the optimistic state the answer settled, `after` the one it left;
11
+ * `visible` is the store as reads see it after; `read` is what the active
12
+ * Surfaces ask of it after, as planned.
13
+ */
14
+ const answerGaps = (requestId, mutation, before, after, visible, read) => {
15
+ const shown = /* @__PURE__ */ new Map();
16
+ const show = (entity, id, fields) => {
17
+ const key = entityKey(entity, id);
18
+ const row = shown.get(key) ?? {
19
+ entity,
20
+ id,
21
+ fields: /* @__PURE__ */ new Set()
22
+ };
23
+ for (const field of fields) row.fields.add(field);
24
+ shown.set(key, row);
25
+ };
26
+ for (const layer of before.layers) {
27
+ if (layer.id !== requestId) continue;
28
+ for (const patch of layer.patches) show(patch.entity, patch.id, patchedFields(patch));
29
+ }
30
+ const confirmed = `confirmed:${requestId}`;
31
+ for (const overlay of [...before.overlays, ...after.overlays]) {
32
+ if (overlay.position === "remove") continue;
33
+ if (overlay.id !== requestId && overlay.id !== confirmed) continue;
34
+ for (const edge of overlay.edges) show(edge.ref.entity, edge.ref.id, []);
35
+ }
36
+ const asked = /* @__PURE__ */ new Set();
37
+ for (const requirement of read) {
38
+ const key = entityKey(requirement.entity, requirement.id);
39
+ if (!shown.has(key)) continue;
40
+ asked.add(key);
41
+ show(requirement.entity, requirement.id, requirement.fields);
42
+ }
43
+ const gaps = [];
44
+ for (const [key, row] of shown) {
45
+ const entry = visible[key];
46
+ if (entry === void 0 ? !asked.has(key) : entry.tombstone) continue;
47
+ const fields = [...row.fields].filter((field) => entry === void 0 || !(entry.present.has(field) || entry.unavailable.has(field)));
48
+ if (fields.length > 0) gaps.push(predictionMismatch(PredictionPart.Gap({
49
+ requestId,
50
+ mutation,
51
+ entity: row.entity,
52
+ id: row.id,
53
+ fields
54
+ })));
55
+ }
56
+ return gaps;
57
+ };
58
+ export { answerGaps };
59
+
60
+ //# sourceMappingURL=answerGaps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"answerGaps.js","names":[],"sources":["../src/answerGaps.ts"],"sourcesContent":["/**\n * `verifyPredictions`' `Gap`: the fields a mutation's answer left out that the client\n * showed while it was pending. Releasing the layer leaves such a field to be\n * read again, so the row reads `Loading` for a frame, or nothing at all where\n * no Selection reads it. A check for development and tests, which changes\n * nothing it is given.\n */\nimport type { Option } from 'effect'\nimport { PredictionPart, predictionMismatch, type PredictionMismatch } from './diagnostics.js'\nimport type { OptimisticState } from './optimistic.js'\nimport { patchedFields } from './shown.js'\nimport type { Requirement } from './requirement.js'\nimport { entityKey, type EntityStore } from './store.js'\n\n/**\n * Each row the request showed (its layer's patches, its insert overlays and\n * the ones its answer confirmed) with the fields shown on it: those the layer\n * patched and those `read` asks of it, minus what the store now holds.\n *\n * `before` is the optimistic state the answer settled, `after` the one it left;\n * `visible` is the store as reads see it after; `read` is what the active\n * Surfaces ask of it after, as planned.\n */\nexport const answerGaps = (\n requestId: string,\n mutation: Option.Option<string>,\n before: OptimisticState,\n after: OptimisticState,\n visible: EntityStore,\n read: ReadonlyArray<Requirement>,\n): ReadonlyArray<PredictionMismatch> => {\n const shown = new Map<\n string,\n { readonly entity: string; readonly id: string; readonly fields: Set<string> }\n >()\n const show = (entity: string, id: string, fields: ReadonlyArray<string>) => {\n const key = entityKey(entity, id)\n const row = shown.get(key) ?? { entity, id, fields: new Set<string>() }\n for (const field of fields) row.fields.add(field)\n shown.set(key, row)\n }\n for (const layer of before.layers) {\n if (layer.id !== requestId) continue\n for (const patch of layer.patches) show(patch.entity, patch.id, patchedFields(patch))\n }\n const confirmed = `confirmed:${requestId}`\n for (const overlay of [...before.overlays, ...after.overlays]) {\n if (overlay.position === 'remove') continue\n if (overlay.id !== requestId && overlay.id !== confirmed) continue\n for (const edge of overlay.edges) show(edge.ref.entity, edge.ref.id, [])\n }\n const asked = new Set<string>()\n for (const requirement of read) {\n const key = entityKey(requirement.entity, requirement.id)\n if (!shown.has(key)) continue\n asked.add(key)\n show(requirement.entity, requirement.id, requirement.fields)\n }\n const gaps: Array<PredictionMismatch> = []\n for (const [key, row] of shown) {\n const entry = visible[key]\n // A row nothing holds or reads any longer is not shown: a temporary id\n // the answer replaced with the server's.\n if (entry === undefined ? !asked.has(key) : entry.tombstone) continue\n const fields = [...row.fields].filter(\n field => entry === undefined || !(entry.present.has(field) || entry.unavailable.has(field)),\n )\n if (fields.length > 0)\n gaps.push(\n predictionMismatch(\n PredictionPart.Gap({ requestId, mutation, entity: row.entity, id: row.id, fields }),\n ),\n )\n }\n return gaps\n}\n"],"mappings":";;;;;;;;;;;;;AAuBA,MAAa,cACX,WACA,UACA,QACA,OACA,SACA,SACsC;CACtC,MAAM,wBAAQ,IAAI,IAGhB;CACF,MAAM,QAAQ,QAAgB,IAAY,WAAkC;EAC1E,MAAM,MAAM,UAAU,QAAQ,EAAE;EAChC,MAAM,MAAM,MAAM,IAAI,GAAG,KAAK;GAAE;GAAQ;GAAI,wBAAQ,IAAI,IAAY;EAAE;EACtE,KAAK,MAAM,SAAS,QAAQ,IAAI,OAAO,IAAI,KAAK;EAChD,MAAM,IAAI,KAAK,GAAG;CACpB;CACA,KAAK,MAAM,SAAS,OAAO,QAAQ;EACjC,IAAI,MAAM,OAAO,WAAW;EAC5B,KAAK,MAAM,SAAS,MAAM,SAAS,KAAK,MAAM,QAAQ,MAAM,IAAI,cAAc,KAAK,CAAC;CACtF;CACA,MAAM,YAAY,aAAa;CAC/B,KAAK,MAAM,WAAW,CAAC,GAAG,OAAO,UAAU,GAAG,MAAM,QAAQ,GAAG;EAC7D,IAAI,QAAQ,aAAa,UAAU;EACnC,IAAI,QAAQ,OAAO,aAAa,QAAQ,OAAO,WAAW;EAC1D,KAAK,MAAM,QAAQ,QAAQ,OAAO,KAAK,KAAK,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC,CAAC;CACzE;CACA,MAAM,wBAAQ,IAAI,IAAY;CAC9B,KAAK,MAAM,eAAe,MAAM;EAC9B,MAAM,MAAM,UAAU,YAAY,QAAQ,YAAY,EAAE;EACxD,IAAI,CAAC,MAAM,IAAI,GAAG,GAAG;EACrB,MAAM,IAAI,GAAG;EACb,KAAK,YAAY,QAAQ,YAAY,IAAI,YAAY,MAAM;CAC7D;CACA,MAAM,OAAkC,CAAC;CACzC,KAAK,MAAM,CAAC,KAAK,QAAQ,OAAO;EAC9B,MAAM,QAAQ,QAAQ;EAGtB,IAAI,UAAU,KAAA,IAAY,CAAC,MAAM,IAAI,GAAG,IAAI,MAAM,WAAW;EAC7D,MAAM,SAAS,CAAC,GAAG,IAAI,MAAM,CAAC,CAAC,QAC7B,UAAS,UAAU,KAAA,KAAa,EAAE,MAAM,QAAQ,IAAI,KAAK,KAAK,MAAM,YAAY,IAAI,KAAK,EAC3F;EACA,IAAI,OAAO,SAAS,GAClB,KAAK,KACH,mBACE,eAAe,IAAI;GAAE;GAAW;GAAU,QAAQ,IAAI;GAAQ,IAAI,IAAI;GAAI;EAAO,CAAC,CACpF,CACF;CACJ;CACA,OAAO;AACT"}
@@ -0,0 +1,110 @@
1
+ import { i as RemoteError } from "./remoteData-mK1suYi1.js";
2
+ import { E as MutationDescriptor, t as ConnectionChange } from "./optimistic-CECsfNGY.js";
3
+ import { a as Requirement, n as RelationRequirement } from "./requirement-D57S2LpH.js";
4
+ import { A as RemoteMutationError, C as ReadBatch, D as RemoteForbidden, F as RemoteUnauthenticated, I as RemoteUnreachable, M as RemoteQueryError, N as RemoteReadError, O as RemoteLiveError, f as MutationRequest, i as LiveChange, j as RemoteProtocolError, k as RemoteLiveRefused, n as ConnectionChangeSchema, o as LiveRequirement, p as MutationResult, v as QueryRequest, w as ReadBatchResult, y as QueryResult } from "./wire-DthPcTM6.js";
5
+ import { n as CoalesceOptions } from "./coalesce-CLAr92Nk.js";
6
+ import { a as LiveEvent, i as LiveCursor } from "./live-BpTzlCnY.js";
7
+ import { Context, Effect, Layer, Schema, Stream } from "effect";
8
+ declare const RemoteClient_base: Context.ServiceClass<RemoteClient, "foldkit-remote/RemoteClient", {
9
+ readonly read: (batch: Schema.Schema.Type<typeof ReadBatch>) => Effect.Effect<Schema.Schema.Type<typeof ReadBatchResult>, RemoteReadError | RemoteProtocolError | RemoteUnauthenticated>;
10
+ readonly query: (request: Schema.Schema.Type<typeof QueryRequest>) => Effect.Effect<Schema.Schema.Type<typeof QueryResult>, RemoteQueryError | RemoteUnauthenticated>;
11
+ readonly mutate: (request: Schema.Schema.Type<typeof MutationRequest>) => Effect.Effect<Schema.Schema.Type<typeof MutationResult>, RemoteMutationError | RemoteForbidden | RemoteUnauthenticated | RemoteUnreachable>;
12
+ readonly live: (request: {
13
+ readonly requirements: ReadonlyArray<Requirement>;
14
+ /** The lists read live, each as its read sends it. */
15
+ readonly queries: ReadonlyArray<Schema.Schema.Type<typeof QueryRequest>>;
16
+ readonly after: LiveCursor;
17
+ }) => Stream.Stream<LiveEvent, RemoteLiveError | RemoteLiveRefused | RemoteProtocolError | RemoteUnauthenticated>;
18
+ }>;
19
+ /**
20
+ * The transport boundary. `foldkit-remote` never talks to a transport directly;
21
+ * it depends on this Effect service, which a later phase wires to Effect RPC.
22
+ */
23
+ declare class RemoteClient extends RemoteClient_base {}
24
+ /**
25
+ * The methods an Effect RPC client for `RemoteRpc` exposes. `R` is the
26
+ * environment a handler needs; it defaults to `never`, which is what a real
27
+ * transport client satisfies. `RemoteServer.handlers` returns this type with its
28
+ * own `R`, so the two sides cannot drift apart.
29
+ *
30
+ * `TransportError` is what the transport itself may fail with on top of the
31
+ * declared errors: `RpcClientError` for the client `RpcClient.make` builds,
32
+ * `never` for in-process handlers.
33
+ */
34
+ interface RemoteRpcClient<R = never, TransportError = never> {
35
+ readonly FoldkitRemoteRead: (payload: Schema.Schema.Type<typeof ReadBatch>) => Effect.Effect<Schema.Schema.Type<typeof ReadBatchResult>, RemoteReadError | RemoteProtocolError | RemoteUnauthenticated | TransportError, R>;
36
+ readonly FoldkitRemoteQuery: (payload: Schema.Schema.Type<typeof QueryRequest>) => Effect.Effect<Schema.Schema.Type<typeof QueryResult>, RemoteQueryError | RemoteUnauthenticated | TransportError, R>;
37
+ readonly FoldkitRemoteMutate: (payload: Schema.Schema.Type<typeof MutationRequest>) => Effect.Effect<Schema.Schema.Type<typeof MutationResult>, RemoteMutationError | RemoteForbidden | RemoteUnauthenticated | TransportError, R>;
38
+ readonly FoldkitRemoteLive: (payload: Schema.Schema.Type<typeof LiveRequirement>) => Stream.Stream<Schema.Schema.Type<typeof LiveChange>, RemoteLiveError | RemoteLiveRefused | RemoteProtocolError | RemoteUnauthenticated | TransportError, R>;
39
+ }
40
+ /**
41
+ * A client over a JSON transport (`Remote.json`, `Remote.http`): a mutation
42
+ * nothing answered fails `RemoteUnreachable`, its only error of its own.
43
+ */
44
+ type RemoteJsonClient = RemoteRpcClient<never, RemoteUnreachable>;
45
+ /** Reconstructs the client's `ConnectionChange` from the wire's flattened edge. */
46
+ declare const connectionChangeOf: (change: Schema.Schema.Type<typeof ConnectionChangeSchema>) => ConnectionChange;
47
+ /** Reconstructs the client's `LiveEvent` from the wire's flattened `LiveChange`. */
48
+ declare const liveEventOf: (change: Schema.Schema.Type<typeof LiveChange>) => LiveEvent;
49
+ declare const remoteError: (error: {
50
+ readonly _tag: string;
51
+ readonly message: string;
52
+ readonly refusal?: unknown;
53
+ readonly reason?: string | undefined;
54
+ }) => RemoteError;
55
+ declare const coalescedLayer: <E, R>(layer: Layer.Layer<RemoteClient, E, R>, options?: CoalesceOptions) => Layer.Layer<RemoteClient, E, R>;
56
+ /**
57
+ * A new operation's identity: random, so unique across clients and reloads.
58
+ * `randomUUID` exists only in a secure context, so a page served over plain
59
+ * HTTP draws 128 random bits from `getRandomValues`, which exists in all.
60
+ */
61
+ declare const mintOperation: () => string;
62
+ /**
63
+ * `Remote.mutate` as a standalone effect, so `Remote.mutateInto` can reuse it
64
+ * without the object literal referencing itself.
65
+ */
66
+ declare const mutateRemote: <Name extends string, Input, Output>(mutation: MutationDescriptor<Name, Input, Output, unknown>, input: Input, requestId: string, options?: {
67
+ readonly keys?: ReadonlyArray<string> | undefined;
68
+ /**
69
+ * The operation's identity across clients, the same on each retry of it:
70
+ * minted here when not given, so a retry must pass the one first sent.
71
+ */
72
+ readonly operation?: string | undefined;
73
+ /** What the client shows of each entity's rows, for the answer to carry (`Data.mutate` sends it). */
74
+ readonly shown?: ReadonlyArray<RelationRequirement> | undefined;
75
+ /** When the reader acted, as `Data.mutate` predicted it: sent for the server to take. */
76
+ readonly now?: number | undefined;
77
+ } | undefined) => Effect.Effect<{
78
+ output: Output;
79
+ entities: readonly {
80
+ readonly entity: string;
81
+ readonly id: string;
82
+ readonly values: {
83
+ readonly [x: string]: unknown;
84
+ };
85
+ }[];
86
+ connections: ConnectionChange[];
87
+ deleted: readonly {
88
+ readonly entity: string;
89
+ readonly id: string;
90
+ }[];
91
+ read: {
92
+ readonly requests: readonly Requirement[];
93
+ readonly result: {
94
+ readonly entities: readonly {
95
+ readonly entity: string;
96
+ readonly id: string;
97
+ readonly values: {
98
+ readonly [x: string]: unknown;
99
+ };
100
+ }[];
101
+ readonly settled: readonly {
102
+ readonly entity: string;
103
+ readonly id: string;
104
+ readonly fields: readonly string[];
105
+ }[];
106
+ };
107
+ } | undefined;
108
+ }, RemoteForbidden | RemoteMutationError | RemoteUnauthenticated | RemoteUnreachable, RemoteClient>;
109
+ export { connectionChangeOf as a, mutateRemote as c, coalescedLayer as i, remoteError as l, RemoteJsonClient as n, liveEventOf as o, RemoteRpcClient as r, mintOperation as s, RemoteClient as t };
110
+ //# sourceMappingURL=client-B055kQuP.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-B055kQuP.d.ts","names":[],"sources":["../src/client.ts"],"mappings":";;;;;;;;WAuCmB,OACb,OAAO,OAAO,OAAO,YAAY,eAC9B,OAAO,OACV,OAAO,OAAO,YAAY,kBAC1B,kBAAkB,sBAAsB;WAE1B,QACd,SAAS,OAAO,OAAO,YAAY,kBAChC,OAAO,OACV,OAAO,OAAO,YAAY,cAC1B,mBAAmB;WAEJ,SACf,SAAS,OAAO,OAAO,YAAY,qBAChC,OAAO,OACV,OAAO,OAAO,YAAY,iBAC1B,sBAAsB,kBAAkB,wBAAwB;WAEnD,OAAC;aACL,cAAc,cAAc;;aAE5B,SAAS,cAAc,OAAO,OAAO,YAAY;aACjD,OAAO;QACZ,OAAO,OACX,WACA,kBAAkB,oBAAoB,sBAAsB;;;;;;cA5BrD,qBAAqB;;;;;;;;;;;UA2CjB,gBAAgB,WAAW;WACjC,oBACP,SAAS,OAAO,OAAO,YAAY,eAChC,OAAO,OACV,OAAO,OAAO,YAAY,kBAC1B,kBAAkB,sBAAsB,wBAAwB,gBAChE;WAEO,qBACP,SAAS,OAAO,OAAO,YAAY,kBAChC,OAAO,OACV,OAAO,OAAO,YAAY,cAC1B,mBAAmB,wBAAwB,gBAC3C;WAEO,sBACP,SAAS,OAAO,OAAO,YAAY,qBAChC,OAAO,OACV,OAAO,OAAO,YAAY,iBAC1B,sBAAsB,kBAAkB,wBAAwB,gBAChE;WAEO,oBACP,SAAS,OAAO,OAAO,YAAY,qBAChC,OAAO,OACV,OAAO,OAAO,YAAY,aACxB,kBACA,oBACA,sBACA,wBACA,gBACF;;;;;;KAQQ,mBAAmB,uBAAuB;;cAGzC,qBAAkB,QACrB,OAAO,OAAO,YAAY,4BACjC;;cAQU,cAAW,QAAY,OAAO,OAAO,YAAY,gBAAc;cAoC/D,cAAW;WACb;WACA;WACA;WACA;MACP;cAOS,iBAAkB,GAAG,GAAC,OAC1B,MAAM,MAAM,cAAc,GAAG,IAAE,UAC7B,oBACR,MAAM,MAAM,cAAc,GAAG;;;;;;cAgBnB;;;;;cAWA,eACX,qBACA,OACA,QAAM,UAAA,mBAAA,MAAA,OAAA,kBAAA,OAAA,OAAA,mBAAA;WAMY,OAAA;;;;;WAKK;;WAEJ,QAAA,cAAc;;WAEhB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCjB,kBAAA,sBAAA,wBAAA,mBAAA"}
@@ -0,0 +1,2 @@
1
+ import { a as connectionChangeOf, c as mutateRemote, i as coalescedLayer, l as remoteError, n as RemoteJsonClient, o as liveEventOf, r as RemoteRpcClient, s as mintOperation, t as RemoteClient } from "./client-B055kQuP.js";
2
+ export { RemoteClient, RemoteJsonClient, RemoteRpcClient, coalescedLayer, connectionChangeOf, liveEventOf, mintOperation, mutateRemote, remoteError };
package/dist/client.js ADDED
@@ -0,0 +1,142 @@
1
+ import { RemoteMutationError } from "./wire.js";
2
+ import { coalesceQueries, coalesceReads } from "./coalesce.js";
3
+ import { Context, Effect, Layer, Schema } from "effect";
4
+ // src/client.ts
5
+ /**
6
+ * The transport seam: the `RemoteClient` Effect service, the RPC client it is
7
+ * built from, the wire-to-client reconstructions, and the mutation call.
8
+ */
9
+ /**
10
+ * The transport boundary. `foldkit-remote` never talks to a transport directly;
11
+ * it depends on this Effect service, which a later phase wires to Effect RPC.
12
+ */
13
+ var RemoteClient = class extends Context.Service()("foldkit-remote/RemoteClient") {};
14
+ /** Reconstructs the client's `ConnectionChange` from the wire's flattened edge. */
15
+ const connectionChangeOf = (change) => {
16
+ const edge = {
17
+ key: change.edge.key,
18
+ ref: {
19
+ entity: change.edge.entity,
20
+ id: change.edge.id
21
+ }
22
+ };
23
+ return change._tag === "Insert" ? {
24
+ _tag: "Insert",
25
+ connection: change.connection,
26
+ position: change.position,
27
+ edge
28
+ } : {
29
+ _tag: "Remove",
30
+ connection: change.connection,
31
+ edge
32
+ };
33
+ };
34
+ /** Reconstructs the client's `LiveEvent` from the wire's flattened `LiveChange`. */
35
+ const liveEventOf = (change) => {
36
+ switch (change._tag) {
37
+ case "EntityPatched": return {
38
+ _tag: "EntityPatched",
39
+ ref: {
40
+ entity: change.entity,
41
+ id: change.id
42
+ },
43
+ values: change.values,
44
+ changed: change.changed,
45
+ cursor: change.cursor
46
+ };
47
+ case "EntityDeleted": return {
48
+ _tag: "EntityDeleted",
49
+ ref: {
50
+ entity: change.entity,
51
+ id: change.id
52
+ },
53
+ cursor: change.cursor
54
+ };
55
+ case "ConnectionInsert": return {
56
+ _tag: "ConnectionInsert",
57
+ connection: change.connection,
58
+ position: change.position,
59
+ edge: {
60
+ key: change.edge.key,
61
+ ref: {
62
+ entity: change.edge.entity,
63
+ id: change.edge.id
64
+ }
65
+ },
66
+ cursor: change.cursor
67
+ };
68
+ case "ConnectionRemove": return {
69
+ _tag: "ConnectionRemove",
70
+ connection: change.connection,
71
+ edge: {
72
+ key: change.edge.key,
73
+ ref: {
74
+ entity: change.edge.entity,
75
+ id: change.edge.id
76
+ }
77
+ },
78
+ cursor: change.cursor
79
+ };
80
+ case "ConnectionInvalidate": return {
81
+ _tag: "ConnectionInvalidate",
82
+ connection: change.connection,
83
+ cursor: change.cursor
84
+ };
85
+ }
86
+ };
87
+ const remoteError = (error) => ({
88
+ _tag: error._tag,
89
+ message: error.message,
90
+ ...error.refusal === void 0 ? {} : { refusal: error.refusal },
91
+ ...error.reason === void 0 ? {} : { reason: error.reason }
92
+ });
93
+ const coalescedLayer = (layer, options = {}) => Layer.effect(RemoteClient, Effect.gen(function* () {
94
+ const client = yield* RemoteClient;
95
+ const read = yield* coalesceReads(client.read, options);
96
+ const query = yield* coalesceQueries(client.query, options);
97
+ return {
98
+ ...client,
99
+ read,
100
+ query
101
+ };
102
+ })).pipe(Layer.provide(layer));
103
+ /**
104
+ * A new operation's identity: random, so unique across clients and reloads.
105
+ * `randomUUID` exists only in a secure context, so a page served over plain
106
+ * HTTP draws 128 random bits from `getRandomValues`, which exists in all.
107
+ */
108
+ const mintOperation = () => typeof globalThis.crypto.randomUUID === "function" ? globalThis.crypto.randomUUID() : Array.from(globalThis.crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(16)), (byte) => byte.toString(16).padStart(2, "0")).join("");
109
+ /**
110
+ * `Remote.mutate` as a standalone effect, so `Remote.mutateInto` can reuse it
111
+ * without the object literal referencing itself.
112
+ */
113
+ const mutateRemote = Effect.fn("Remote.mutate")(function* (mutation, input, requestId, options = {}) {
114
+ const { keys, shown, now } = options;
115
+ const operation = options.operation ?? (yield* Effect.sync(mintOperation));
116
+ yield* Effect.annotateCurrentSpan({
117
+ mutation: mutation.name,
118
+ requestId,
119
+ operation
120
+ });
121
+ const client = yield* RemoteClient;
122
+ const encoded = yield* Schema.encodeUnknownEffect(mutation.Input)(input).pipe(Effect.catchTag("SchemaError", (error) => Effect.fail(new RemoteMutationError({ message: error.message }))));
123
+ const result = yield* client.mutate({
124
+ requestId,
125
+ operation,
126
+ mutation: mutation.name,
127
+ input: encoded,
128
+ ...keys === void 0 ? {} : { keys },
129
+ ...now === void 0 ? {} : { now },
130
+ ...shown === void 0 || shown.length === 0 ? {} : { shown }
131
+ });
132
+ return {
133
+ output: yield* Schema.decodeUnknownEffect(mutation.Output)(result.output).pipe(Effect.catchTag("SchemaError", (error) => Effect.fail(new RemoteMutationError({ message: error.message })))),
134
+ entities: result.entities,
135
+ connections: (result.connections ?? []).map(connectionChangeOf),
136
+ deleted: result.deleted ?? [],
137
+ read: result.read
138
+ };
139
+ });
140
+ export { RemoteClient, coalescedLayer, connectionChangeOf, liveEventOf, mintOperation, mutateRemote, remoteError };
141
+
142
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","names":[],"sources":["../src/client.ts"],"sourcesContent":["/**\n * The transport seam: the `RemoteClient` Effect service, the RPC client it is\n * built from, the wire-to-client reconstructions, and the mutation call.\n */\nimport { Context, Effect, Layer, Schema, Stream } from 'effect'\nimport type { RelationRequirement, Requirement } from './requirement.js'\nimport { coalesceQueries, coalesceReads, type CoalesceOptions } from './coalesce.js'\nimport type { LiveCursor, LiveEvent } from './live.js'\nimport type { MutationDescriptor } from './mutation.js'\nimport type { ConnectionChange } from './optimistic.js'\nimport type { RemoteError } from './remoteData.js'\nimport {\n MutationRequest,\n MutationResult,\n QueryRequest,\n QueryResult,\n ReadBatch,\n ReadBatchResult,\n type ConnectionChangeSchema,\n RemoteLiveError,\n RemoteLiveRefused,\n RemoteForbidden,\n RemoteUnauthenticated,\n RemoteMutationError,\n RemoteProtocolError,\n RemoteQueryError,\n RemoteReadError,\n RemoteUnreachable,\n type LiveChange,\n type LiveRequirement,\n} from './wire.js'\n\n/**\n * The transport boundary. `foldkit-remote` never talks to a transport directly;\n * it depends on this Effect service, which a later phase wires to Effect RPC.\n */\nexport class RemoteClient extends Context.Service<\n RemoteClient,\n {\n readonly read: (\n batch: Schema.Schema.Type<typeof ReadBatch>,\n ) => Effect.Effect<\n Schema.Schema.Type<typeof ReadBatchResult>,\n RemoteReadError | RemoteProtocolError | RemoteUnauthenticated\n >\n readonly query: (\n request: Schema.Schema.Type<typeof QueryRequest>,\n ) => Effect.Effect<\n Schema.Schema.Type<typeof QueryResult>,\n RemoteQueryError | RemoteUnauthenticated\n >\n readonly mutate: (\n request: Schema.Schema.Type<typeof MutationRequest>,\n ) => Effect.Effect<\n Schema.Schema.Type<typeof MutationResult>,\n RemoteMutationError | RemoteForbidden | RemoteUnauthenticated | RemoteUnreachable\n >\n readonly live: (request: {\n readonly requirements: ReadonlyArray<Requirement>\n /** The lists read live, each as its read sends it. */\n readonly queries: ReadonlyArray<Schema.Schema.Type<typeof QueryRequest>>\n readonly after: LiveCursor\n }) => Stream.Stream<\n LiveEvent,\n RemoteLiveError | RemoteLiveRefused | RemoteProtocolError | RemoteUnauthenticated\n >\n }\n>()('foldkit-remote/RemoteClient') {}\n\n/**\n * The methods an Effect RPC client for `RemoteRpc` exposes. `R` is the\n * environment a handler needs; it defaults to `never`, which is what a real\n * transport client satisfies. `RemoteServer.handlers` returns this type with its\n * own `R`, so the two sides cannot drift apart.\n *\n * `TransportError` is what the transport itself may fail with on top of the\n * declared errors: `RpcClientError` for the client `RpcClient.make` builds,\n * `never` for in-process handlers.\n */\nexport interface RemoteRpcClient<R = never, TransportError = never> {\n readonly FoldkitRemoteRead: (\n payload: Schema.Schema.Type<typeof ReadBatch>,\n ) => Effect.Effect<\n Schema.Schema.Type<typeof ReadBatchResult>,\n RemoteReadError | RemoteProtocolError | RemoteUnauthenticated | TransportError,\n R\n >\n readonly FoldkitRemoteQuery: (\n payload: Schema.Schema.Type<typeof QueryRequest>,\n ) => Effect.Effect<\n Schema.Schema.Type<typeof QueryResult>,\n RemoteQueryError | RemoteUnauthenticated | TransportError,\n R\n >\n readonly FoldkitRemoteMutate: (\n payload: Schema.Schema.Type<typeof MutationRequest>,\n ) => Effect.Effect<\n Schema.Schema.Type<typeof MutationResult>,\n RemoteMutationError | RemoteForbidden | RemoteUnauthenticated | TransportError,\n R\n >\n readonly FoldkitRemoteLive: (\n payload: Schema.Schema.Type<typeof LiveRequirement>,\n ) => Stream.Stream<\n Schema.Schema.Type<typeof LiveChange>,\n | RemoteLiveError\n | RemoteLiveRefused\n | RemoteProtocolError\n | RemoteUnauthenticated\n | TransportError,\n R\n >\n}\n\n/**\n * A client over a JSON transport (`Remote.json`, `Remote.http`): a mutation\n * nothing answered fails `RemoteUnreachable`, its only error of its own.\n */\nexport type RemoteJsonClient = RemoteRpcClient<never, RemoteUnreachable>\n\n/** Reconstructs the client's `ConnectionChange` from the wire's flattened edge. */\nexport const connectionChangeOf = (\n change: Schema.Schema.Type<typeof ConnectionChangeSchema>,\n): ConnectionChange => {\n const edge = { key: change.edge.key, ref: { entity: change.edge.entity, id: change.edge.id } }\n return change._tag === 'Insert'\n ? { _tag: 'Insert', connection: change.connection, position: change.position, edge }\n : { _tag: 'Remove', connection: change.connection, edge }\n}\n\n/** Reconstructs the client's `LiveEvent` from the wire's flattened `LiveChange`. */\nexport const liveEventOf = (change: Schema.Schema.Type<typeof LiveChange>): LiveEvent => {\n switch (change._tag) {\n case 'EntityPatched':\n return {\n _tag: 'EntityPatched',\n ref: { entity: change.entity, id: change.id },\n values: change.values,\n changed: change.changed,\n cursor: change.cursor,\n }\n case 'EntityDeleted':\n return {\n _tag: 'EntityDeleted',\n ref: { entity: change.entity, id: change.id },\n cursor: change.cursor,\n }\n case 'ConnectionInsert':\n return {\n _tag: 'ConnectionInsert',\n connection: change.connection,\n position: change.position,\n edge: { key: change.edge.key, ref: { entity: change.edge.entity, id: change.edge.id } },\n cursor: change.cursor,\n }\n case 'ConnectionRemove':\n return {\n _tag: 'ConnectionRemove',\n connection: change.connection,\n edge: { key: change.edge.key, ref: { entity: change.edge.entity, id: change.edge.id } },\n cursor: change.cursor,\n }\n case 'ConnectionInvalidate':\n return { _tag: 'ConnectionInvalidate', connection: change.connection, cursor: change.cursor }\n }\n}\n\nexport const remoteError = (error: {\n readonly _tag: string\n readonly message: string\n readonly refusal?: unknown\n readonly reason?: string | undefined\n}): RemoteError => ({\n _tag: error._tag,\n message: error.message,\n ...(error.refusal === undefined ? {} : { refusal: error.refusal }),\n ...(error.reason === undefined ? {} : { reason: error.reason }),\n})\n\nexport const coalescedLayer = <E, R>(\n layer: Layer.Layer<RemoteClient, E, R>,\n options: CoalesceOptions = {},\n): Layer.Layer<RemoteClient, E, R> =>\n Layer.effect(\n RemoteClient,\n Effect.gen(function* () {\n const client = yield* RemoteClient\n const read = yield* coalesceReads(client.read, options)\n const query = yield* coalesceQueries(client.query, options)\n return { ...client, read, query }\n }),\n ).pipe(Layer.provide(layer))\n\n/**\n * A new operation's identity: random, so unique across clients and reloads.\n * `randomUUID` exists only in a secure context, so a page served over plain\n * HTTP draws 128 random bits from `getRandomValues`, which exists in all.\n */\nexport const mintOperation = (): string =>\n typeof globalThis.crypto.randomUUID === 'function'\n ? globalThis.crypto.randomUUID()\n : Array.from(globalThis.crypto.getRandomValues(new Uint8Array(16)), byte =>\n byte.toString(16).padStart(2, '0'),\n ).join('')\n\n/**\n * `Remote.mutate` as a standalone effect, so `Remote.mutateInto` can reuse it\n * without the object literal referencing itself.\n */\nexport const mutateRemote = Effect.fn('Remote.mutate')(function* <\n Name extends string,\n Input,\n Output,\n>(\n mutation: MutationDescriptor<Name, Input, Output>,\n input: Input,\n requestId: string,\n options: {\n readonly keys?: ReadonlyArray<string> | undefined\n /**\n * The operation's identity across clients, the same on each retry of it:\n * minted here when not given, so a retry must pass the one first sent.\n */\n readonly operation?: string | undefined\n /** What the client shows of each entity's rows, for the answer to carry (`Data.mutate` sends it). */\n readonly shown?: ReadonlyArray<RelationRequirement> | undefined\n /** When the reader acted, as `Data.mutate` predicted it: sent for the server to take. */\n readonly now?: number | undefined\n } = {},\n) {\n const { keys, shown, now } = options\n const operation = options.operation ?? (yield* Effect.sync(mintOperation))\n yield* Effect.annotateCurrentSpan({ mutation: mutation.name, requestId, operation })\n const client = yield* RemoteClient\n const encoded = yield* Schema.encodeUnknownEffect(mutation.Input)(input).pipe(\n Effect.catchTag('SchemaError', error =>\n Effect.fail(new RemoteMutationError({ message: error.message })),\n ),\n )\n const result = yield* client.mutate({\n requestId,\n operation,\n mutation: mutation.name,\n input: encoded,\n ...(keys === undefined ? {} : { keys }),\n ...(now === undefined ? {} : { now }),\n ...(shown === undefined || shown.length === 0 ? {} : { shown }),\n })\n const output = yield* Schema.decodeUnknownEffect(mutation.Output)(result.output).pipe(\n Effect.catchTag('SchemaError', error =>\n Effect.fail(new RemoteMutationError({ message: error.message })),\n ),\n )\n return {\n output,\n entities: result.entities,\n connections: (result.connections ?? []).map(connectionChangeOf),\n deleted: result.deleted ?? [],\n read: result.read,\n }\n})\n"],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { a as Requirement } from "./requirement-D57S2LpH.js";
2
+ import { C as ReadBatch, F as RemoteUnauthenticated, M as RemoteQueryError, N as RemoteReadError, j as RemoteProtocolError, v as QueryRequest, w as ReadBatchResult, y as QueryResult } from "./wire-DthPcTM6.js";
3
+ import { Duration, Effect, Schema } from "effect";
4
+ type Batch = Schema.Schema.Type<typeof ReadBatch>;
5
+ type BatchResult = Schema.Schema.Type<typeof ReadBatchResult>;
6
+ type ReadError = RemoteReadError | RemoteProtocolError | RemoteUnauthenticated;
7
+ type QueryError = RemoteQueryError | RemoteUnauthenticated;
8
+ type BatchRead = (batch: Batch) => Effect.Effect<BatchResult, ReadError>;
9
+ type QueryRequestValue = Schema.Schema.Type<typeof QueryRequest>;
10
+ type QueryResultValue = Schema.Schema.Type<typeof QueryResult>;
11
+ type QueryRun = (request: QueryRequestValue) => Effect.Effect<QueryResultValue, QueryError>;
12
+ interface CoalesceOptions {
13
+ /**
14
+ * How long a batch waits to collect more requirements before it runs.
15
+ * Requirements issued concurrently coalesce even at the default of no wait.
16
+ */
17
+ readonly window?: Duration.Input | undefined;
18
+ }
19
+ /** A stable key for a requirement, so an equal requirement in flight is joined. */
20
+ declare const requirementKey: (requirement: Requirement) => string;
21
+ /**
22
+ * Wraps a raw query so that an identical query in flight (same query, input,
23
+ * and window) is joined rather than run again; distinct queries run
24
+ * concurrently. A page answers exactly one window, so nothing is batched.
25
+ */
26
+ declare const coalesceQueries: (query: QueryRun, options?: CoalesceOptions) => Effect.Effect<QueryRun>;
27
+ /**
28
+ * Wraps a raw batch read so that requirements are batched and deduplicated.
29
+ * Every waiter receives the whole batch result; `Remote.writeRead` is
30
+ * idempotent, so writing it more than once is harmless.
31
+ */
32
+ declare const coalesceReads: (read: BatchRead, options?: CoalesceOptions) => Effect.Effect<BatchRead>;
33
+ export { coalesceReads as a, coalesceQueries as i, CoalesceOptions as n, requirementKey as o, QueryRun as r, BatchRead as t };
34
+ //# sourceMappingURL=coalesce-CLAr92Nk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coalesce-CLAr92Nk.d.ts","names":[],"sources":["../src/coalesce.ts"],"mappings":";;;KAgCK,QAAQ,OAAO,OAAO,YAAY;KAClC,cAAc,OAAO,OAAO,YAAY;KACxC,YAAY,kBAAkB,sBAAsB;KACpD,aAAa,mBAAmB;KACzB,aAAa,OAAO,UAAU,OAAO,OAAO,aAAa;KAChE,oBAAoB,OAAO,OAAO,YAAY;KAC9C,mBAAmB,OAAO,OAAO,YAAY;KACtC,YAAY,SAAS,sBAAsB,OAAO,OAAO,kBAAkB;UAEtE;;;;;WAKN,SAAS,SAAS;;;cAIhB,iBAAc,aAAiB;;;;;;cAoD/B,kBAAe,OACnB,UAAQ,UACN,oBACR,OAAO,OAAO;;;;;;cA+CJ,gBAAa,MAClB,WAAS,UACN,oBACR,OAAO,OAAO"}
@@ -0,0 +1,2 @@
1
+ import { a as coalesceReads, i as coalesceQueries, n as CoalesceOptions, o as requirementKey, r as QueryRun, t as BatchRead } from "./coalesce-CLAr92Nk.js";
2
+ export { BatchRead, CoalesceOptions, QueryRun, coalesceQueries, coalesceReads, requirementKey };
@@ -0,0 +1,131 @@
1
+ import { Requirement } from "./requirement.js";
2
+ import { stableStringify } from "./query.js";
3
+ import "./wire.js";
4
+ import { Deferred, Duration, Effect, Request, RequestResolver } from "effect";
5
+ // src/coalesce.ts
6
+ /**
7
+ * Read coalescing: every requirement a client asks for goes through one
8
+ * `RequestResolver`, so requirements issued together become one `ReadBatch`
9
+ * (ids batched, fields unioned) and a requirement already in flight is joined
10
+ * rather than re-requested. The planner stays pure; this is the transport
11
+ * seam's concern, and the store is still the only cache.
12
+ */
13
+ /** A stable key for a requirement, so an equal requirement in flight is joined. */
14
+ const requirementKey = (requirement) => stableStringify([
15
+ requirement.entity,
16
+ requirement.id,
17
+ [...requirement.fields].sort(),
18
+ requirement.windows ?? null,
19
+ requirement.relations ?? null
20
+ ]);
21
+ /** Whether a requirement pages a relation anywhere in its graph. */
22
+ const hasWindows = (requirement) => requirement.windows !== void 0 || Object.values(requirement.relations ?? {}).some(hasWindows);
23
+ /**
24
+ * The reads a batch runs. Requirements that page nothing union into one read
25
+ * per entity and id, and every waiter may write its whole result, since
26
+ * plain values are the same whoever asked. A requirement that pages a
27
+ * relation reads alone: a page answers exactly one window, and a waiter
28
+ * cannot tell which of two pages for the same entity was its own.
29
+ */
30
+ const readsOf = (owned) => {
31
+ const plain = [...owned].filter(([, requirement]) => !hasWindows(requirement));
32
+ const paged = [...owned].filter(([, requirement]) => hasWindows(requirement));
33
+ return [...plain.length === 0 ? [] : [{
34
+ keys: plain.map(([key]) => key),
35
+ requests: Requirement.merge(plain.map(([, requirement]) => requirement))
36
+ }], ...paged.map(([key, requirement]) => ({
37
+ keys: [key],
38
+ requests: [requirement]
39
+ }))];
40
+ };
41
+ var QueryRead = class extends Request.Class {};
42
+ /**
43
+ * Wraps a raw query so that an identical query in flight (same query, input,
44
+ * and window) is joined rather than run again; distinct queries run
45
+ * concurrently. A page answers exactly one window, so nothing is batched.
46
+ */
47
+ const coalesceQueries = (query, options = {}) => Effect.gen(function* () {
48
+ const runAll = (entries) => Effect.gen(function* () {
49
+ const distinct = /* @__PURE__ */ new Map();
50
+ for (const entry of entries) distinct.set(entry.request.key, entry.request.request);
51
+ const results = /* @__PURE__ */ new Map();
52
+ yield* Effect.forEach(distinct, ([key, request]) => Effect.exit(query(request)).pipe(Effect.map((exit) => void results.set(key, exit))), {
53
+ concurrency: "unbounded",
54
+ discard: true
55
+ });
56
+ for (const entry of entries) yield* Request.completeEffect(entry, results.get(entry.request.key));
57
+ });
58
+ let resolver = RequestResolver.make(runAll);
59
+ if (options.window !== void 0) resolver = RequestResolver.setDelay(resolver, Duration.fromInputUnsafe(options.window));
60
+ return (request) => Effect.request(new QueryRead({
61
+ key: stableStringify([
62
+ request.query,
63
+ request.input,
64
+ request.window,
65
+ request.select ?? null
66
+ ]),
67
+ request
68
+ }), resolver);
69
+ });
70
+ var ReadRequirement = class extends Request.Class {};
71
+ /**
72
+ * Wraps a raw batch read so that requirements are batched and deduplicated.
73
+ * Every waiter receives the whole batch result; `Remote.writeRead` is
74
+ * idempotent, so writing it more than once is harmless.
75
+ */
76
+ const coalesceReads = (read, options = {}) => Effect.gen(function* () {
77
+ const inFlight = /* @__PURE__ */ new Map();
78
+ const runAll = (entries) => Effect.gen(function* () {
79
+ const joins = /* @__PURE__ */ new Map();
80
+ const own = /* @__PURE__ */ new Map();
81
+ for (const entry of entries) {
82
+ const key = entry.request.key;
83
+ if (joins.has(key)) continue;
84
+ const existing = inFlight.get(key);
85
+ if (existing !== void 0) {
86
+ joins.set(key, existing);
87
+ continue;
88
+ }
89
+ const deferred = yield* Deferred.make();
90
+ inFlight.set(key, deferred);
91
+ own.set(key, deferred);
92
+ joins.set(key, deferred);
93
+ }
94
+ if (own.size > 0) {
95
+ const owned = /* @__PURE__ */ new Map();
96
+ for (const entry of entries) if (own.has(entry.request.key)) owned.set(entry.request.key, entry.request.requirement);
97
+ yield* Effect.forEach(readsOf(owned), ({ keys, requests }) => Effect.gen(function* () {
98
+ const exit = yield* Effect.exit(read({
99
+ version: 5,
100
+ requests
101
+ }));
102
+ for (const key of keys) {
103
+ inFlight.delete(key);
104
+ yield* Deferred.done(own.get(key), exit);
105
+ }
106
+ }), {
107
+ concurrency: "unbounded",
108
+ discard: true
109
+ });
110
+ }
111
+ for (const entry of entries) yield* Request.completeEffect(entry, Deferred.await(joins.get(entry.request.key)));
112
+ });
113
+ let resolver = RequestResolver.make(runAll);
114
+ if (options.window !== void 0) resolver = RequestResolver.setDelay(resolver, Duration.fromInputUnsafe(options.window));
115
+ return (batch) => batch.requests.length === 0 ? Effect.succeed({
116
+ entities: [],
117
+ settled: []
118
+ }) : Effect.forEach(batch.requests, (requirement) => Effect.request(new ReadRequirement({
119
+ key: requirementKey(requirement),
120
+ requirement
121
+ }), resolver), { concurrency: "unbounded" }).pipe(Effect.map((results) => {
122
+ const distinct = [...new Set(results)];
123
+ return {
124
+ entities: distinct.flatMap((result) => result.entities),
125
+ settled: distinct.flatMap((result) => result.settled)
126
+ };
127
+ }));
128
+ });
129
+ export { coalesceQueries, coalesceReads, requirementKey };
130
+
131
+ //# sourceMappingURL=coalesce.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coalesce.js","names":[],"sources":["../src/coalesce.ts"],"sourcesContent":["/**\n * Read coalescing: every requirement a client asks for goes through one\n * `RequestResolver`, so requirements issued together become one `ReadBatch`\n * (ids batched, fields unioned) and a requirement already in flight is joined\n * rather than re-requested. The planner stays pure; this is the transport\n * seam's concern, and the store is still the only cache.\n */\nimport {\n Deferred,\n Duration,\n Effect,\n Request,\n RequestResolver,\n type Exit,\n type Schema,\n} from 'effect'\nimport { Requirement, type RelationRequirement } from './requirement.js'\nimport { stableStringify } from './query.js'\nimport {\n REMOTE_PROTOCOL_VERSION,\n type QueryRequest,\n type QueryResult,\n type ReadBatch,\n type ReadBatchResult,\n} from './wire.js'\nimport type {\n RemoteProtocolError,\n RemoteQueryError,\n RemoteReadError,\n RemoteUnauthenticated,\n} from './wire.js'\n\ntype Batch = Schema.Schema.Type<typeof ReadBatch>\ntype BatchResult = Schema.Schema.Type<typeof ReadBatchResult>\ntype ReadError = RemoteReadError | RemoteProtocolError | RemoteUnauthenticated\ntype QueryError = RemoteQueryError | RemoteUnauthenticated\nexport type BatchRead = (batch: Batch) => Effect.Effect<BatchResult, ReadError>\ntype QueryRequestValue = Schema.Schema.Type<typeof QueryRequest>\ntype QueryResultValue = Schema.Schema.Type<typeof QueryResult>\nexport type QueryRun = (request: QueryRequestValue) => Effect.Effect<QueryResultValue, QueryError>\n\nexport interface CoalesceOptions {\n /**\n * How long a batch waits to collect more requirements before it runs.\n * Requirements issued concurrently coalesce even at the default of no wait.\n */\n readonly window?: Duration.Input | undefined\n}\n\n/** A stable key for a requirement, so an equal requirement in flight is joined. */\nexport const requirementKey = (requirement: Requirement): string =>\n stableStringify([\n requirement.entity,\n requirement.id,\n [...requirement.fields].sort(),\n requirement.windows ?? null,\n requirement.relations ?? null,\n ])\n\n/** Whether a requirement pages a relation anywhere in its graph. */\nconst hasWindows = (requirement: RelationRequirement): boolean =>\n requirement.windows !== undefined || Object.values(requirement.relations ?? {}).some(hasWindows)\n\n/**\n * The reads a batch runs. Requirements that page nothing union into one read\n * per entity and id, and every waiter may write its whole result, since\n * plain values are the same whoever asked. A requirement that pages a\n * relation reads alone: a page answers exactly one window, and a waiter\n * cannot tell which of two pages for the same entity was its own.\n */\nconst readsOf = (\n owned: ReadonlyMap<string, Requirement>,\n): ReadonlyArray<{\n readonly keys: ReadonlyArray<string>\n readonly requests: ReadonlyArray<Requirement>\n}> => {\n const plain = [...owned].filter(([, requirement]) => !hasWindows(requirement))\n const paged = [...owned].filter(([, requirement]) => hasWindows(requirement))\n return [\n ...(plain.length === 0\n ? []\n : [\n {\n keys: plain.map(([key]) => key),\n requests: Requirement.merge(plain.map(([, requirement]) => requirement)),\n },\n ]),\n ...paged.map(([key, requirement]) => ({ keys: [key], requests: [requirement] })),\n ]\n}\n\nclass QueryRead extends Request.Class<\n { readonly key: string; readonly request: QueryRequestValue },\n QueryResultValue,\n QueryError\n> {}\n\n/**\n * Wraps a raw query so that an identical query in flight (same query, input,\n * and window) is joined rather than run again; distinct queries run\n * concurrently. A page answers exactly one window, so nothing is batched.\n */\nexport const coalesceQueries = (\n query: QueryRun,\n options: CoalesceOptions = {},\n): Effect.Effect<QueryRun> =>\n Effect.gen(function* () {\n const runAll = (entries: ReadonlyArray<Request.Entry<QueryRead>>) =>\n Effect.gen(function* () {\n const distinct = new Map<string, QueryRequestValue>()\n for (const entry of entries) distinct.set(entry.request.key, entry.request.request)\n const results = new Map<string, Exit.Exit<QueryResultValue, QueryError>>()\n yield* Effect.forEach(\n distinct,\n ([key, request]) =>\n Effect.exit(query(request)).pipe(Effect.map(exit => void results.set(key, exit))),\n { concurrency: 'unbounded', discard: true },\n )\n for (const entry of entries) {\n yield* Request.completeEffect(entry, results.get(entry.request.key)!)\n }\n })\n let resolver = RequestResolver.make<QueryRead>(runAll)\n if (options.window !== undefined) {\n resolver = RequestResolver.setDelay(resolver, Duration.fromInputUnsafe(options.window))\n }\n return request =>\n Effect.request(\n new QueryRead({\n key: stableStringify([\n request.query,\n request.input,\n request.window,\n request.select ?? null,\n ]),\n request,\n }),\n resolver,\n )\n })\n\nclass ReadRequirement extends Request.Class<\n { readonly key: string; readonly requirement: Requirement },\n BatchResult,\n ReadError\n> {}\n\n/**\n * Wraps a raw batch read so that requirements are batched and deduplicated.\n * Every waiter receives the whole batch result; `Remote.writeRead` is\n * idempotent, so writing it more than once is harmless.\n */\nexport const coalesceReads = (\n read: BatchRead,\n options: CoalesceOptions = {},\n): Effect.Effect<BatchRead> =>\n Effect.gen(function* () {\n // Requirements a batch is currently reading; a later equal requirement\n // joins the deferred instead of starting another read.\n const inFlight = new Map<string, Deferred.Deferred<BatchResult, ReadError>>()\n\n // A batch runs to completion even when every requester is interrupted, so\n // an in-flight requirement is always settled and released by its read.\n const runAll = (entries: ReadonlyArray<Request.Entry<ReadRequirement>>) =>\n Effect.gen(function* () {\n // Per requirement key: the deferred this batch's entries wait on. One\n // already in flight from an earlier batch is joined; the rest are\n // owned here and settled by this batch's read.\n const joins = new Map<string, Deferred.Deferred<BatchResult, ReadError>>()\n const own = new Map<string, Deferred.Deferred<BatchResult, ReadError>>()\n for (const entry of entries) {\n const key = entry.request.key\n if (joins.has(key)) continue\n const existing = inFlight.get(key)\n if (existing !== undefined) {\n joins.set(key, existing)\n continue\n }\n const deferred = yield* Deferred.make<BatchResult, ReadError>()\n inFlight.set(key, deferred)\n own.set(key, deferred)\n joins.set(key, deferred)\n }\n if (own.size > 0) {\n const owned = new Map<string, Requirement>()\n for (const entry of entries) {\n if (own.has(entry.request.key)) owned.set(entry.request.key, entry.request.requirement)\n }\n yield* Effect.forEach(\n readsOf(owned),\n ({ keys, requests }) =>\n Effect.gen(function* () {\n const exit = yield* Effect.exit(\n read({ version: REMOTE_PROTOCOL_VERSION, requests }),\n )\n // Settle exactly the requirements this read answered.\n for (const key of keys) {\n inFlight.delete(key)\n yield* Deferred.done(own.get(key)!, exit)\n }\n }),\n { concurrency: 'unbounded', discard: true },\n )\n }\n for (const entry of entries) {\n yield* Request.completeEffect(entry, Deferred.await(joins.get(entry.request.key)!))\n }\n })\n\n let resolver = RequestResolver.make<ReadRequirement>(runAll)\n if (options.window !== undefined) {\n resolver = RequestResolver.setDelay(resolver, Duration.fromInputUnsafe(options.window))\n }\n\n return batch =>\n batch.requests.length === 0\n ? Effect.succeed({ entities: [], settled: [] })\n : Effect.forEach(\n batch.requests,\n requirement =>\n Effect.request(\n new ReadRequirement({ key: requirementKey(requirement), requirement }),\n resolver,\n ),\n { concurrency: 'unbounded' },\n ).pipe(\n Effect.map(results => {\n const distinct = [...new Set(results)]\n return {\n entities: distinct.flatMap(result => result.entities),\n settled: distinct.flatMap(result => result.settled),\n }\n }),\n )\n })\n"],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ interface EdgeRef {
2
+ readonly entity: string;
3
+ readonly id: string;
4
+ }
5
+ /** `key` is the edge identity used for de-duplication. */
6
+ interface Edge {
7
+ readonly key: string;
8
+ readonly ref: EdgeRef;
9
+ }
10
+ declare const edge: (ref: EdgeRef, key?: string) => Edge;
11
+ type Cursor = string;
12
+ type Boundary = {
13
+ readonly _tag: 'Terminal';
14
+ } | {
15
+ readonly _tag: 'Cursor';
16
+ readonly cursor: Cursor;
17
+ } | {
18
+ readonly _tag: 'Unknown';
19
+ };
20
+ declare const terminal: Boundary;
21
+ declare const unknown: Boundary;
22
+ declare const cursor: (value: Cursor) => Boundary;
23
+ interface Segment {
24
+ readonly edges: readonly Edge[];
25
+ readonly start: Boundary;
26
+ readonly end: Boundary;
27
+ }
28
+ interface Connection {
29
+ readonly segments: readonly Segment[];
30
+ readonly stale: boolean;
31
+ }
32
+ declare const emptyConnection: Connection;
33
+ declare const segment: (edges: readonly Edge[], start: Boundary, end: Boundary) => Segment;
34
+ /** Adds a page to the connection, joining contiguous or overlapping segments. */
35
+ declare const merge: (current: Connection, page: Segment) => Connection;
36
+ /** Visible edges, segment by segment. */
37
+ declare const items: (connection: Connection) => ReadonlyArray<Edge>;
38
+ /** There is more before the known region. Derived from boundaries, never row count. */
39
+ declare const hasPrevious: (connection: Connection) => boolean;
40
+ /** There is more after the known region. Derived from boundaries, never row count. */
41
+ declare const hasNext: (connection: Connection) => boolean;
42
+ /** A connection with more than one segment has an explicit unknown gap. */
43
+ declare const isGapped: (connection: Connection) => boolean;
44
+ export { unknown as _, EdgeRef as a, edge as c, hasPrevious as d, isGapped as f, terminal as g, segment as h, Edge as i, emptyConnection as l, merge as m, Connection as n, Segment as o, items as p, Cursor as r, cursor as s, Boundary as t, hasNext as u };
45
+ //# sourceMappingURL=connection-BRyaJy89.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection-BRyaJy89.d.ts","names":[],"sources":["../src/connection.ts"],"mappings":"A,UAUiB;WACN;WACA;;;UAIM;WACN;WACA,KAAK;;cAGH,OAAI,KAAS,SAAO,iBAAgD;KAKrE;KAEA;WACG;;WACA;WAAyB,QAAQ;;WACjC;;cAEF,UAAU;cACV,SAAS;cACT,SAAM,OAAW,WAAS;UAEtB;WACN,gBAAgB;WAChB,OAAO;WACP,KAAK;;UAGC;WACN,mBAAmB;WACnB;;cAGE,iBAAiB;cAEjB,UAAO,gBAAoB,QAAM,OAAS,UAAQ,KAAO,aAAW;;cA+FpE,QAAK,SAAa,YAAU,MAAQ,YAAU;;cA4C9C,QAAK,YAAgB,eAAa,cAAc;;cAIhD,cAAW,YAAgB;;cAM3B,UAAO,YAAgB;;cAMvB,WAAQ,YAAgB"}
@@ -0,0 +1,2 @@
1
+ import { _ as unknown, a as EdgeRef, c as edge, d as hasPrevious, f as isGapped, g as terminal, h as segment, i as Edge, l as emptyConnection, m as merge, n as Connection, o as Segment, p as items, r as Cursor, s as cursor, t as Boundary, u as hasNext } from "./connection-BRyaJy89.js";
2
+ export { Boundary, Connection, Cursor, Edge, EdgeRef, Segment, cursor, edge, emptyConnection, hasNext, hasPrevious, isGapped, items, merge, segment, terminal, unknown };