@superbee/core 0.2.0-pre.1 → 0.2.0-pre.10

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 (215) hide show
  1. package/README.md +127 -0
  2. package/dist/artifact-contract.d.ts +90 -0
  3. package/dist/artifact-contract.d.ts.map +1 -0
  4. package/dist/artifact-contract.js +205 -0
  5. package/dist/artifact-contract.js.map +1 -0
  6. package/dist/backend.d.ts +16 -1
  7. package/dist/backend.d.ts.map +1 -1
  8. package/dist/backend.js +85 -42
  9. package/dist/backend.js.map +1 -1
  10. package/dist/bundle-ops.d.ts +1 -1
  11. package/dist/bundle-ops.d.ts.map +1 -1
  12. package/dist/bundle-ops.js +2 -2
  13. package/dist/bundle-ops.js.map +1 -1
  14. package/dist/bundle.d.ts +2 -1
  15. package/dist/bundle.d.ts.map +1 -1
  16. package/dist/bundle.js +3 -3
  17. package/dist/bundle.js.map +1 -1
  18. package/dist/document-codec.d.ts +6 -1
  19. package/dist/document-codec.d.ts.map +1 -1
  20. package/dist/document-codec.js +8 -4
  21. package/dist/document-codec.js.map +1 -1
  22. package/dist/document-field-actions.d.ts +13 -1
  23. package/dist/document-field-actions.d.ts.map +1 -1
  24. package/dist/document-field-actions.js +47 -6
  25. package/dist/document-field-actions.js.map +1 -1
  26. package/dist/document-mutation.d.ts +15 -1
  27. package/dist/document-mutation.d.ts.map +1 -1
  28. package/dist/document-mutation.js +42 -11
  29. package/dist/document-mutation.js.map +1 -1
  30. package/dist/document-write-policy.d.ts.map +1 -1
  31. package/dist/document-write-policy.js +4 -2
  32. package/dist/document-write-policy.js.map +1 -1
  33. package/dist/engine.d.ts +1 -1
  34. package/dist/engine.d.ts.map +1 -1
  35. package/dist/engine.js +4 -3
  36. package/dist/engine.js.map +1 -1
  37. package/dist/file-journaled-backend-internal.d.ts +19 -0
  38. package/dist/file-journaled-backend-internal.d.ts.map +1 -0
  39. package/dist/file-journaled-backend-internal.js +10 -0
  40. package/dist/file-journaled-backend-internal.js.map +1 -0
  41. package/dist/file-journaled-backend.d.ts +162 -0
  42. package/dist/file-journaled-backend.d.ts.map +1 -0
  43. package/dist/file-journaled-backend.js +1113 -0
  44. package/dist/file-journaled-backend.js.map +1 -0
  45. package/dist/filesystem-host.d.ts +15 -0
  46. package/dist/filesystem-host.d.ts.map +1 -0
  47. package/dist/filesystem-host.js +20 -0
  48. package/dist/filesystem-host.js.map +1 -0
  49. package/dist/filesystem-identity.d.ts +86 -4
  50. package/dist/filesystem-identity.d.ts.map +1 -1
  51. package/dist/filesystem-identity.js +338 -102
  52. package/dist/filesystem-identity.js.map +1 -1
  53. package/dist/filesystem-lock.d.ts +16 -3
  54. package/dist/filesystem-lock.d.ts.map +1 -1
  55. package/dist/filesystem-lock.js +323 -77
  56. package/dist/filesystem-lock.js.map +1 -1
  57. package/dist/filesystem-push-role.d.ts +77 -0
  58. package/dist/filesystem-push-role.d.ts.map +1 -0
  59. package/dist/filesystem-push-role.js +165 -0
  60. package/dist/filesystem-push-role.js.map +1 -0
  61. package/dist/filesystem.d.ts +17 -0
  62. package/dist/filesystem.d.ts.map +1 -0
  63. package/dist/filesystem.js +18 -0
  64. package/dist/filesystem.js.map +1 -0
  65. package/dist/frontmatter.d.ts +8 -0
  66. package/dist/frontmatter.d.ts.map +1 -1
  67. package/dist/frontmatter.js +14 -4
  68. package/dist/frontmatter.js.map +1 -1
  69. package/dist/governed-body-write.d.ts +106 -0
  70. package/dist/governed-body-write.d.ts.map +1 -0
  71. package/dist/governed-body-write.js +251 -0
  72. package/dist/governed-body-write.js.map +1 -0
  73. package/dist/heads-digest.d.ts +32 -0
  74. package/dist/heads-digest.d.ts.map +1 -0
  75. package/dist/heads-digest.js +37 -0
  76. package/dist/heads-digest.js.map +1 -0
  77. package/dist/host-policy-snapshot.d.ts +9 -0
  78. package/dist/host-policy-snapshot.d.ts.map +1 -0
  79. package/dist/host-policy-snapshot.js +33 -0
  80. package/dist/host-policy-snapshot.js.map +1 -0
  81. package/dist/host-text.d.ts +31 -0
  82. package/dist/host-text.d.ts.map +1 -0
  83. package/dist/host-text.js +77 -0
  84. package/dist/host-text.js.map +1 -0
  85. package/dist/hosted-transport/answer-rows.d.ts +216 -0
  86. package/dist/hosted-transport/answer-rows.d.ts.map +1 -0
  87. package/dist/hosted-transport/answer-rows.js +396 -0
  88. package/dist/hosted-transport/answer-rows.js.map +1 -0
  89. package/dist/hosted-transport/carrier.d.ts +142 -0
  90. package/dist/hosted-transport/carrier.d.ts.map +1 -0
  91. package/dist/hosted-transport/carrier.js +241 -0
  92. package/dist/hosted-transport/carrier.js.map +1 -0
  93. package/dist/hosted-transport/history-pages.d.ts +55 -0
  94. package/dist/hosted-transport/history-pages.d.ts.map +1 -0
  95. package/dist/hosted-transport/history-pages.js +69 -0
  96. package/dist/hosted-transport/history-pages.js.map +1 -0
  97. package/dist/hosted-transport/index.d.ts +18 -0
  98. package/dist/hosted-transport/index.d.ts.map +1 -0
  99. package/dist/hosted-transport/index.js +18 -0
  100. package/dist/hosted-transport/index.js.map +1 -0
  101. package/dist/hosted-transport/operations.d.ts +85 -0
  102. package/dist/hosted-transport/operations.d.ts.map +1 -0
  103. package/dist/hosted-transport/operations.js +128 -0
  104. package/dist/hosted-transport/operations.js.map +1 -0
  105. package/dist/hosted-transport/paged-reads.d.ts +87 -0
  106. package/dist/hosted-transport/paged-reads.d.ts.map +1 -0
  107. package/dist/hosted-transport/paged-reads.js +238 -0
  108. package/dist/hosted-transport/paged-reads.js.map +1 -0
  109. package/dist/hosted-transport/read-adapter.d.ts +250 -0
  110. package/dist/hosted-transport/read-adapter.d.ts.map +1 -0
  111. package/dist/hosted-transport/read-adapter.js +557 -0
  112. package/dist/hosted-transport/read-adapter.js.map +1 -0
  113. package/dist/hosted-transport/root-write.d.ts +114 -0
  114. package/dist/hosted-transport/root-write.d.ts.map +1 -0
  115. package/dist/hosted-transport/root-write.js +171 -0
  116. package/dist/hosted-transport/root-write.js.map +1 -0
  117. package/dist/hosted-transport/whole-document-transport.d.ts +174 -0
  118. package/dist/hosted-transport/whole-document-transport.d.ts.map +1 -0
  119. package/dist/hosted-transport/whole-document-transport.js +411 -0
  120. package/dist/hosted-transport/whole-document-transport.js.map +1 -0
  121. package/dist/index.d.ts +22 -8
  122. package/dist/index.d.ts.map +1 -1
  123. package/dist/index.js +15 -5
  124. package/dist/index.js.map +1 -1
  125. package/dist/indexeddb-backend.d.ts +38 -6
  126. package/dist/indexeddb-backend.d.ts.map +1 -1
  127. package/dist/indexeddb-backend.js +511 -46
  128. package/dist/indexeddb-backend.js.map +1 -1
  129. package/dist/journaled-backend.d.ts +296 -7
  130. package/dist/journaled-backend.d.ts.map +1 -1
  131. package/dist/journaled-backend.js +312 -0
  132. package/dist/journaled-backend.js.map +1 -1
  133. package/dist/kind-coverage.d.ts +99 -0
  134. package/dist/kind-coverage.d.ts.map +1 -0
  135. package/dist/kind-coverage.js +186 -0
  136. package/dist/kind-coverage.js.map +1 -0
  137. package/dist/kinds.d.ts +31 -1
  138. package/dist/kinds.d.ts.map +1 -1
  139. package/dist/kinds.js +83 -16
  140. package/dist/kinds.js.map +1 -1
  141. package/dist/links.d.ts +1 -1
  142. package/dist/links.d.ts.map +1 -1
  143. package/dist/links.js +5 -10
  144. package/dist/links.js.map +1 -1
  145. package/dist/managed-document-metadata.d.ts +2 -0
  146. package/dist/managed-document-metadata.d.ts.map +1 -0
  147. package/dist/managed-document-metadata.js +9 -0
  148. package/dist/managed-document-metadata.js.map +1 -0
  149. package/dist/memory-backend.d.ts +2 -2
  150. package/dist/memory-backend.d.ts.map +1 -1
  151. package/dist/memory-backend.js +32 -16
  152. package/dist/memory-backend.js.map +1 -1
  153. package/dist/paths.d.ts +6 -2
  154. package/dist/paths.d.ts.map +1 -1
  155. package/dist/paths.js +35 -33
  156. package/dist/paths.js.map +1 -1
  157. package/dist/platform.d.ts +84 -6
  158. package/dist/platform.d.ts.map +1 -1
  159. package/dist/platform.js +9 -4
  160. package/dist/platform.js.map +1 -1
  161. package/dist/portable-export.d.ts +119 -0
  162. package/dist/portable-export.d.ts.map +1 -0
  163. package/dist/portable-export.js +501 -0
  164. package/dist/portable-export.js.map +1 -0
  165. package/dist/publication-filesystem.d.ts +1 -1
  166. package/dist/publication-filesystem.d.ts.map +1 -1
  167. package/dist/publication-filesystem.js +1 -1
  168. package/dist/publication-filesystem.js.map +1 -1
  169. package/dist/query-filter.d.ts +7 -2
  170. package/dist/query-filter.d.ts.map +1 -1
  171. package/dist/query-filter.js +24 -1
  172. package/dist/query-filter.js.map +1 -1
  173. package/dist/query-order.d.ts +22 -0
  174. package/dist/query-order.d.ts.map +1 -0
  175. package/dist/query-order.js +42 -0
  176. package/dist/query-order.js.map +1 -0
  177. package/dist/remote-backend.d.ts +91 -27
  178. package/dist/remote-backend.d.ts.map +1 -1
  179. package/dist/remote-backend.js +217 -49
  180. package/dist/remote-backend.js.map +1 -1
  181. package/dist/remote-document-codec.d.ts +15 -0
  182. package/dist/remote-document-codec.d.ts.map +1 -0
  183. package/dist/remote-document-codec.js +103 -0
  184. package/dist/remote-document-codec.js.map +1 -0
  185. package/dist/remote-error.d.ts +38 -0
  186. package/dist/remote-error.d.ts.map +1 -0
  187. package/dist/remote-error.js +59 -0
  188. package/dist/remote-error.js.map +1 -0
  189. package/dist/remote-operations.d.ts +6 -3
  190. package/dist/remote-operations.d.ts.map +1 -1
  191. package/dist/remote-operations.js +20 -5
  192. package/dist/remote-operations.js.map +1 -1
  193. package/dist/remote-parsers.d.ts +86 -0
  194. package/dist/remote-parsers.d.ts.map +1 -0
  195. package/dist/remote-parsers.js +181 -0
  196. package/dist/remote-parsers.js.map +1 -0
  197. package/dist/remote.d.ts +7 -2
  198. package/dist/remote.d.ts.map +1 -1
  199. package/dist/remote.js +6 -1
  200. package/dist/remote.js.map +1 -1
  201. package/dist/storage.d.ts +4 -2
  202. package/dist/storage.d.ts.map +1 -1
  203. package/dist/storage.js +4 -1
  204. package/dist/storage.js.map +1 -1
  205. package/dist/types.d.ts +28 -5
  206. package/dist/types.d.ts.map +1 -1
  207. package/dist/uncertain-write.d.ts +49 -13
  208. package/dist/uncertain-write.d.ts.map +1 -1
  209. package/dist/uncertain-write.js +36 -12
  210. package/dist/uncertain-write.js.map +1 -1
  211. package/dist/versioning.d.ts +9 -4
  212. package/dist/versioning.d.ts.map +1 -1
  213. package/dist/versioning.js +9 -4
  214. package/dist/versioning.js.map +1 -1
  215. package/package.json +34 -2
@@ -0,0 +1,103 @@
1
+ import { InvalidInputError } from "./errors.js";
2
+ /** Metadata cannot be represented by the JSON document transport without loss. */
3
+ export class RemoteDocumentValueError extends InvalidInputError {
4
+ path;
5
+ constructor(path, reason) {
6
+ super(`Remote document ${path}: ${reason}. Supply JSON-compatible metadata explicitly.`);
7
+ this.name = "RemoteDocumentValueError";
8
+ this.path = path;
9
+ }
10
+ }
11
+ /** Capture the wire value once, without invoking accessors or custom serialization hooks. */
12
+ export function captureRemoteFrontmatter(frontmatter) {
13
+ const ancestors = new Set();
14
+ const refuse = (path, reason) => { throw new RemoteDocumentValueError(path, reason); };
15
+ const visit = (value, path, depth) => {
16
+ if (depth > 512)
17
+ return refuse(path, "metadata nesting exceeds the supported depth");
18
+ if (value === null || typeof value === "string" || typeof value === "boolean")
19
+ return value;
20
+ if (typeof value === "number") {
21
+ if (!Number.isFinite(value) || Object.is(value, -0))
22
+ return refuse(path, "number would change during JSON encoding");
23
+ return value;
24
+ }
25
+ if (typeof value !== "object")
26
+ return refuse(path, `${typeof value} is not a JSON value`);
27
+ if (ancestors.has(value))
28
+ return refuse(path, "cyclic metadata cannot be represented in JSON");
29
+ const prototype = Object.getPrototypeOf(value);
30
+ const descriptors = Object.getOwnPropertyDescriptors(value);
31
+ const hook = descriptors.toJSON;
32
+ if (hook && (!("value" in hook) || typeof hook.value === "function")) {
33
+ return refuse(`${path}["toJSON"]`, "custom serialization hooks are not supported");
34
+ }
35
+ for (const key of Reflect.ownKeys(descriptors)) {
36
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
37
+ if (typeof key === "symbol" && descriptor.enumerable)
38
+ return refuse(path, "enumerable symbol keys would be omitted");
39
+ }
40
+ if (prototype === Date.prototype) {
41
+ if (Object.values(descriptors).some(descriptor => descriptor.enumerable))
42
+ return refuse(path, "Date properties would be omitted");
43
+ let instant;
44
+ try {
45
+ instant = Date.prototype.getTime.call(value);
46
+ }
47
+ catch {
48
+ return refuse(path, "object has no Date value");
49
+ }
50
+ if (!Number.isFinite(instant))
51
+ return refuse(path, "invalid Date would become null");
52
+ return Date.prototype.toISOString.call(value);
53
+ }
54
+ const array = Array.isArray(value);
55
+ if (array ? prototype !== Array.prototype && prototype !== null : prototype !== Object.prototype && prototype !== null) {
56
+ return refuse(path, "only plain records, arrays, and valid Dates are supported");
57
+ }
58
+ ancestors.add(value);
59
+ try {
60
+ if (array) {
61
+ const length = value.length;
62
+ const result = [];
63
+ // Keep prototype serialization hooks out of the captured payload as well.
64
+ Object.setPrototypeOf(result, null);
65
+ for (const key of Object.keys(descriptors)) {
66
+ if (descriptors[key].enumerable && !(String(Number(key)) === key && Number.isInteger(Number(key)) && Number(key) >= 0 && Number(key) < length)) {
67
+ return refuse(`${path}[${JSON.stringify(key)}]`, "extra array properties would be omitted");
68
+ }
69
+ }
70
+ for (let index = 0; index < length; index++) {
71
+ const descriptor = descriptors[String(index)];
72
+ if (!descriptor)
73
+ return refuse(`${path}[${index}]`, "array holes would become null");
74
+ if (!("value" in descriptor))
75
+ return refuse(`${path}[${index}]`, "accessors are not supported");
76
+ result[index] = visit(descriptor.value, `${path}[${index}]`, depth + 1);
77
+ }
78
+ return result;
79
+ }
80
+ const result = Object.create(null);
81
+ for (const key of Object.keys(descriptors)) {
82
+ const descriptor = descriptors[key];
83
+ if (!descriptor.enumerable)
84
+ continue;
85
+ const field = `${path}[${JSON.stringify(key)}]`;
86
+ if (!("value" in descriptor))
87
+ return refuse(field, "accessors are not supported");
88
+ result[key] = visit(descriptor.value, field, depth + 1);
89
+ }
90
+ return result;
91
+ }
92
+ finally {
93
+ ancestors.delete(value);
94
+ }
95
+ };
96
+ return visit(frontmatter, "frontmatter", 0);
97
+ }
98
+ /** Encode only the captured value, never the caller's live metadata a second time. */
99
+ export function encodeRemoteDocument(frontmatter, body) {
100
+ const payload = Object.assign(Object.create(null), { frontmatter: captureRemoteFrontmatter(frontmatter), body });
101
+ return JSON.stringify(payload);
102
+ }
103
+ //# sourceMappingURL=remote-document-codec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-document-codec.js","sourceRoot":"","sources":["../src/remote-document-codec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The typed rejection of the wire client, shared by {@link RemoteBackend} and the heads and
3
+ * snapshot parsers in `remote-parsers.ts`, which the backend calls; a module of its own so the
4
+ * two need no import cycle. Runtime-neutral: nothing from Node.
5
+ */
6
+ /**
7
+ * A non-2xx wire response that is neither a `404` (ENOENT-shaped) nor a `412`
8
+ * ({@link VersionConflict}): the generic case, carrying the error envelope's `code` alongside
9
+ * the raw HTTP `status`, so a caller can distinguish e.g.
10
+ * `AUTH_REQUIRED` (401, an unauthenticated/misconfigured `--remote`) from `RUNTIME` (5xx, a
11
+ * genuine server-side bug) instead of both collapsing into a generically-classified `Error`.
12
+ */
13
+ export declare class RemoteError extends Error {
14
+ /** The envelope's `code` field, or a status-derived guess when the envelope is missing/unparseable. */
15
+ readonly code: string;
16
+ /** The raw HTTP status that produced this error. */
17
+ readonly status: number;
18
+ constructor(message: string, code: string, status: number, cause?: unknown);
19
+ }
20
+ /**
21
+ * The code of an answer the authority produced that the client's contract does not admit. It is
22
+ * deterministic (the same request gets the same answer), so it is never retried and never read
23
+ * as truncation or as the host being unavailable. The status stays `502` so a write whose answer
24
+ * is malformed is still an unknown outcome to the uncertain-write primitive, not a refusal.
25
+ */
26
+ export declare const MALFORMED_ANSWER = "MALFORMED_ANSWER";
27
+ /** A {@link MALFORMED_ANSWER} rejection, naming the route that answered when the reader knows it. */
28
+ export declare class MalformedAnswer extends RemoteError {
29
+ route: string | undefined;
30
+ constructor(message: string, route?: string);
31
+ }
32
+ /** A wire payload the authority produced but the contract does not admit: not retried, not truncation. */
33
+ export declare function malformed(message: string, route?: string): MalformedAnswer;
34
+ /** True when `error` is a {@link MalformedAnswer}. */
35
+ export declare function isMalformedAnswer(error: unknown): error is MalformedAnswer;
36
+ /** Run `read`, naming `route` on a malformed answer it rejects with that does not name one yet. */
37
+ export declare function onRoute<T>(route: string, read: () => Promise<T>): Promise<T>;
38
+ //# sourceMappingURL=remote-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-error.d.ts","sourceRoot":"","sources":["../src/remote-error.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;GAMG;AACH,qBAAa,WAAY,SAAQ,KAAK;IACpC,uGAAuG;IACvG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAM3E;AAED;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,qBAAqB,CAAC;AAEnD,qGAAqG;AACrG,qBAAa,eAAgB,SAAQ,WAAW;IAC9C,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;gBACd,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;CAI5C;AAED,0GAA0G;AAC1G,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,eAAe,CAE1E;AAED,sDAAsD;AACtD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CAE1E;AAED,mGAAmG;AACnG,wBAAsB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAOlF"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * The typed rejection of the wire client, shared by {@link RemoteBackend} and the heads and
3
+ * snapshot parsers in `remote-parsers.ts`, which the backend calls; a module of its own so the
4
+ * two need no import cycle. Runtime-neutral: nothing from Node.
5
+ */
6
+ /**
7
+ * A non-2xx wire response that is neither a `404` (ENOENT-shaped) nor a `412`
8
+ * ({@link VersionConflict}): the generic case, carrying the error envelope's `code` alongside
9
+ * the raw HTTP `status`, so a caller can distinguish e.g.
10
+ * `AUTH_REQUIRED` (401, an unauthenticated/misconfigured `--remote`) from `RUNTIME` (5xx, a
11
+ * genuine server-side bug) instead of both collapsing into a generically-classified `Error`.
12
+ */
13
+ export class RemoteError extends Error {
14
+ /** The envelope's `code` field, or a status-derived guess when the envelope is missing/unparseable. */
15
+ code;
16
+ /** The raw HTTP status that produced this error. */
17
+ status;
18
+ constructor(message, code, status, cause) {
19
+ super(message, cause === undefined ? undefined : { cause });
20
+ this.name = "RemoteError";
21
+ this.code = code;
22
+ this.status = status;
23
+ }
24
+ }
25
+ /**
26
+ * The code of an answer the authority produced that the client's contract does not admit. It is
27
+ * deterministic (the same request gets the same answer), so it is never retried and never read
28
+ * as truncation or as the host being unavailable. The status stays `502` so a write whose answer
29
+ * is malformed is still an unknown outcome to the uncertain-write primitive, not a refusal.
30
+ */
31
+ export const MALFORMED_ANSWER = "MALFORMED_ANSWER";
32
+ /** A {@link MALFORMED_ANSWER} rejection, naming the route that answered when the reader knows it. */
33
+ export class MalformedAnswer extends RemoteError {
34
+ route;
35
+ constructor(message, route) {
36
+ super(message, MALFORMED_ANSWER, 502);
37
+ this.route = route;
38
+ }
39
+ }
40
+ /** A wire payload the authority produced but the contract does not admit: not retried, not truncation. */
41
+ export function malformed(message, route) {
42
+ return new MalformedAnswer(message, route);
43
+ }
44
+ /** True when `error` is a {@link MalformedAnswer}. */
45
+ export function isMalformedAnswer(error) {
46
+ return error instanceof MalformedAnswer;
47
+ }
48
+ /** Run `read`, naming `route` on a malformed answer it rejects with that does not name one yet. */
49
+ export async function onRoute(route, read) {
50
+ try {
51
+ return await read();
52
+ }
53
+ catch (error) {
54
+ if (error instanceof MalformedAnswer && error.route === undefined)
55
+ error.route = route;
56
+ throw error;
57
+ }
58
+ }
59
+ //# sourceMappingURL=remote-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-error.js","sourceRoot":"","sources":["../src/remote-error.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;GAMG;AACH,MAAM,OAAO,WAAY,SAAQ,KAAK;IACpC,uGAAuG;IAC9F,IAAI,CAAS;IACtB,oDAAoD;IAC3C,MAAM,CAAS;IAExB,YAAY,OAAe,EAAE,IAAY,EAAE,MAAc,EAAE,KAAe;QACxE,KAAK,CAAC,OAAO,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5D,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AAEnD,qGAAqG;AACrG,MAAM,OAAO,eAAgB,SAAQ,WAAW;IAC9C,KAAK,CAAqB;IAC1B,YAAY,OAAe,EAAE,KAAc;QACzC,KAAK,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,CAAC,CAAC;QACtC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;CACF;AAED,0GAA0G;AAC1G,MAAM,UAAU,SAAS,CAAC,OAAe,EAAE,KAAc;IACvD,OAAO,IAAI,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;AAC7C,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,OAAO,KAAK,YAAY,eAAe,CAAC;AAC1C,CAAC;AAED,mGAAmG;AACnG,MAAM,CAAC,KAAK,UAAU,OAAO,CAAI,KAAa,EAAE,IAAsB;IACpE,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,EAAE,CAAC;IACtB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,eAAe,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;YAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;QACvF,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -12,7 +12,10 @@
12
12
  * carrier failure and a 5xx response both propagate unchanged: a 502 or 504 from an intermediary,
13
13
  * or the authority's own runtime failure, says nothing about whether the write was applied, so
14
14
  * the primitive classifies it as unknown and resolves it by lookup, which is the whole reason
15
- * the identity exists. Only a 4xx is final: the authority answered and declined.
15
+ * the identity exists. A 4xx is final because the authority answered and declined. A local
16
+ * refusal is also final because no PUT was sent, not because the authority recorded an outcome:
17
+ * an intent kind other than `document.write`, content that does not parse, or metadata the wire
18
+ * cannot encode is refused `USAGE` without a PUT.
16
19
  *
17
20
  * The transport is only sound against an authority that implements identity. A host that
18
21
  * predates it ignores the header, applies every retry as a fresh write, and answers the lookup
@@ -30,8 +33,8 @@
30
33
  * the verdict visible uses {@link openRemoteOperationTransport} or reads `wireCapabilities()`
31
34
  * itself. And the check is memoized on success: a host downgraded afterwards is not detected,
32
35
  * so one unidentified resubmission can reach it. Because every identified write is guarded by
33
- * its base, that exposure is bounded to a spurious conflict at a moved head, never a double
34
- * application.
36
+ * its base, that exposure is bounded to a spurious conflict at a moved head, or a second
37
+ * application when the head has returned to the base's exact content.
35
38
  *
36
39
  * This module imports nothing from Node so a browser working copy and a Node consumer share
37
40
  * one transport over one client adapter.
@@ -1 +1 @@
1
- {"version":3,"file":"remote-operations.d.ts","sourceRoot":"","sources":["../src/remote-operations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAIH,OAAO,EAAE,aAAa,EAAe,MAAM,qBAAqB,CAAC;AACjE,OAAO,KAAK,EAAmB,kBAAkB,EAAW,MAAM,sBAAsB,CAAC;AAGzF,MAAM,WAAW,+BAA+B;IAC9C,0GAA0G;IAC1G,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,kFAAkF;AAClF,qBAAa,0BAA2B,SAAQ,KAAK;IACnD,QAAQ,CAAC,IAAI,4BAA4B;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AAED;;;GAGG;AACH,wBAAsB,4BAA4B,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,GAAE,+BAAoC,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAIpJ;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,GAAE,+BAAoC,GAAG,kBAAkB,CAEvI"}
1
+ {"version":3,"file":"remote-operations.d.ts","sourceRoot":"","sources":["../src/remote-operations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAIH,OAAO,EAAE,aAAa,EAAe,MAAM,qBAAqB,CAAC;AAEjE,OAAO,KAAK,EAAmB,kBAAkB,EAAW,MAAM,sBAAsB,CAAC;AAGzF,MAAM,WAAW,+BAA+B;IAC9C,0GAA0G;IAC1G,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,kFAAkF;AAClF,qBAAa,0BAA2B,SAAQ,KAAK;IACnD,QAAQ,CAAC,IAAI,4BAA4B;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AAED;;;GAGG;AACH,wBAAsB,4BAA4B,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,GAAE,+BAAoC,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAIpJ;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,GAAE,+BAAoC,GAAG,kBAAkB,CAEvI"}
@@ -12,7 +12,10 @@
12
12
  * carrier failure and a 5xx response both propagate unchanged: a 502 or 504 from an intermediary,
13
13
  * or the authority's own runtime failure, says nothing about whether the write was applied, so
14
14
  * the primitive classifies it as unknown and resolves it by lookup, which is the whole reason
15
- * the identity exists. Only a 4xx is final: the authority answered and declined.
15
+ * the identity exists. A 4xx is final because the authority answered and declined. A local
16
+ * refusal is also final because no PUT was sent, not because the authority recorded an outcome:
17
+ * an intent kind other than `document.write`, content that does not parse, or metadata the wire
18
+ * cannot encode is refused `USAGE` without a PUT.
16
19
  *
17
20
  * The transport is only sound against an authority that implements identity. A host that
18
21
  * predates it ignores the header, applies every retry as a fresh write, and answers the lookup
@@ -30,8 +33,8 @@
30
33
  * the verdict visible uses {@link openRemoteOperationTransport} or reads `wireCapabilities()`
31
34
  * itself. And the check is memoized on success: a host downgraded afterwards is not detected,
32
35
  * so one unidentified resubmission can reach it. Because every identified write is guarded by
33
- * its base, that exposure is bounded to a spurious conflict at a moved head, never a double
34
- * application.
36
+ * its base, that exposure is bounded to a spurious conflict at a moved head, or a second
37
+ * application when the head has returned to the base's exact content.
35
38
  *
36
39
  * This module imports nothing from Node so a browser working copy and a Node consumer share
37
40
  * one transport over one client adapter.
@@ -39,6 +42,7 @@
39
42
  import { parseMarkdown } from "./frontmatter.js";
40
43
  import { pathFromConceptId } from "./paths.js";
41
44
  import { RemoteBackend, RemoteError } from "./remote-backend.js";
45
+ import { RemoteDocumentValueError } from "./remote-document-codec.js";
42
46
  import { VersionConflict } from "./version-transport.js";
43
47
  /** The authority behind a remote does not record outcomes by request identity. */
44
48
  export class OperationsUnsupportedError extends Error {
@@ -76,10 +80,19 @@ function buildTransport(remote, options) {
76
80
  };
77
81
  const transport = {
78
82
  async submit(intent) {
83
+ // Refused rather than thrown: the primitive classifies a throw as unknown, and an intent
84
+ // that can never be sent would then stay pending as possibly delivered on every push.
79
85
  if (intent.kind !== "document.write") {
80
- throw new Error(`remote operation transport: unsupported intent kind '${intent.kind}'`);
86
+ return { kind: "refused", code: "USAGE", message: `remote operation transport: unsupported intent kind '${intent.kind}'` };
81
87
  }
82
- const { frontmatter, body } = parseMarkdown(intent.content, pathFromConceptId(intent.target));
88
+ let parsed;
89
+ try {
90
+ parsed = parseMarkdown(intent.content, pathFromConceptId(intent.target));
91
+ }
92
+ catch (error) {
93
+ return { kind: "refused", code: "USAGE", message: error instanceof Error ? error.message : String(error) };
94
+ }
95
+ const { frontmatter, body } = parsed;
83
96
  const writeOptions = {
84
97
  expectedVersion: intent.base,
85
98
  requestId: intent.requestId,
@@ -93,6 +106,8 @@ function buildTransport(remote, options) {
93
106
  return { kind: "committed", version };
94
107
  }
95
108
  catch (error) {
109
+ if (error instanceof RemoteDocumentValueError)
110
+ return { kind: "refused", code: "USAGE", message: error.message };
96
111
  if (error instanceof VersionConflict)
97
112
  return { kind: "conflict", actual: error.actual };
98
113
  if (error instanceof RemoteError && error.status < 500)
@@ -1 +1 @@
1
- {"version":3,"file":"remote-operations.js","sourceRoot":"","sources":["../src/remote-operations.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"remote-operations.js","sourceRoot":"","sources":["../src/remote-operations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * The client side of the wire's heads and snapshot grammar (`docs/WIRE-PROTOCOL.md`, "Heads and
3
+ * snapshot"): what a heads answer must carry before its rows may be diffed as deletions, and
4
+ * how a snapshot body is read so that iterating it to completion is the completeness signal.
5
+ * {@link RemoteBackend.heads} and {@link RemoteBackend.snapshot} are thin callers over these
6
+ * two functions; a host that serves the same grammar through routes of its own validates the
7
+ * answers with the same functions rather than a second parser, since this grammar decides what
8
+ * a working copy deletes locally.
9
+ *
10
+ * Only `TextDecoder` and the Web Streams reader are used, so the same code runs in a browser, a
11
+ * Worker, and Node.
12
+ */
13
+ import { type DocumentHead } from "./heads-digest.js";
14
+ import type { ConceptId, Frontmatter, Version } from "./types.js";
15
+ /** A snapshot body that ended, or failed, before its terminator arrived with the announced count. */
16
+ export declare const SNAPSHOT_TRUNCATED = "SNAPSHOT_TRUNCATED";
17
+ /**
18
+ * A snapshot whose body arrived whole, with its terminator and the announced count, but whose
19
+ * rows digest to something other than the header announced. Not truncation: re-requesting will
20
+ * not necessarily repair it, since the authority contradicted itself. A consumer discards the
21
+ * documents' claim to the header digest either way.
22
+ */
23
+ export declare const SNAPSHOT_DIGEST_MISMATCH = "SNAPSHOT_DIGEST_MISMATCH";
24
+ /** A `200` from `GET /heads`: every document id and version, and the digest over them. */
25
+ export interface HeadsResult {
26
+ digest: string;
27
+ heads: DocumentHead[];
28
+ }
29
+ /** The snapshot's first line: how many documents follow and the digest `heads` would return for this state. */
30
+ export interface SnapshotHeader {
31
+ count: number;
32
+ digest: string;
33
+ }
34
+ /** One `doc` line of a snapshot. */
35
+ export interface SnapshotDocument {
36
+ id: ConceptId;
37
+ version: Version;
38
+ frontmatter: Frontmatter;
39
+ body: string;
40
+ }
41
+ /**
42
+ * A snapshot as {@link readSnapshotStream} hands it over: the header, already parsed, and the
43
+ * documents as they stream. Iterating `docs` to completion is the completeness signal: the
44
+ * loop ends only after the `end` line arrived with the announced count and the digest recomputed
45
+ * over the received `{ id, version }` rows equals the header's, and it throws
46
+ * `SNAPSHOT_TRUNCATED` (the body ended or failed first) or `SNAPSHOT_DIGEST_MISMATCH` (the body
47
+ * was whole but describes a state the header did not announce) otherwise, so a consumer that
48
+ * writes batches as they arrive has one control path and never needs to inspect a terminator
49
+ * or a digest itself. A consumer records the header digest as matched only after the loop
50
+ * ended normally.
51
+ */
52
+ export interface RemoteSnapshot {
53
+ header: SnapshotHeader;
54
+ docs: AsyncIterable<SnapshotDocument>;
55
+ }
56
+ /** Options for {@link readSnapshotStream}. */
57
+ export interface ReadSnapshotStreamOptions {
58
+ /**
59
+ * The HTTP status the response carried, reported as `RemoteError.status` by the truncation and
60
+ * digest-mismatch rejections; a caller that has already checked `res.ok` passes `res.status`.
61
+ * Defaults to `200`, the only status a snapshot body is read under.
62
+ */
63
+ status?: number;
64
+ }
65
+ /**
66
+ * The `200` body of `GET /heads`, admitted or refused as one: a well-formed digest, a `heads`
67
+ * array of `{ id, version }` rows, a `count` equal to the number of rows, and rows that digest
68
+ * by the documented recipe to the digest served. Anything else rejects as malformed (a
69
+ * {@link MalformedAnswer}, code `MALFORMED_ANSWER`) rather than being trusted. The
70
+ * recomputation is what stops a listing that is whole by its own count but not the state its
71
+ * digest names (a shortened listing under the real digest) from being diffed as a mass
72
+ * deletion. Rows keep the order served: the recipe sorts for itself, so order does not decide
73
+ * admission.
74
+ */
75
+ export declare function parseHeadsAnswer(payload: unknown): HeadsResult;
76
+ /**
77
+ * Read a `GET /snapshot` body by the documented grammar. The header line is parsed before this
78
+ * resolves, so a body that ends before it rejects here with `SNAPSHOT_TRUNCATED` and a first
79
+ * line that is not the header rejects as malformed; the documents then stream through
80
+ * {@link RemoteSnapshot.docs}, which ends normally only for a whole body whose rows digest to
81
+ * the header's announcement (see {@link RemoteSnapshot}). Obtaining the response, checking its
82
+ * status, and re-requesting a truncated snapshot are the caller's: this function reads one body
83
+ * once and cancels the reader when the iteration is abandoned or fails.
84
+ */
85
+ export declare function readSnapshotStream(body: ReadableStream<Uint8Array>, options?: ReadSnapshotStreamOptions): Promise<RemoteSnapshot>;
86
+ //# sourceMappingURL=remote-parsers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-parsers.d.ts","sourceRoot":"","sources":["../src/remote-parsers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAA8B,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAElF,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAElE,qGAAqG;AACrG,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AACvD;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,6BAA6B,CAAC;AAEnE,0FAA0F;AAC1F,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,+GAA+G;AAC/G,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,oCAAoC;AACpC,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,SAAS,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC;CACvC;AAED,8CAA8C;AAC9C,MAAM,WAAW,yBAAyB;IACxC;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAQD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,WAAW,CAe9D;AA6GD;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,EAAE,OAAO,GAAE,yBAA8B,GAAG,OAAO,CAAC,cAAc,CAAC,CAa3I"}
@@ -0,0 +1,181 @@
1
+ /**
2
+ * The client side of the wire's heads and snapshot grammar (`docs/WIRE-PROTOCOL.md`, "Heads and
3
+ * snapshot"): what a heads answer must carry before its rows may be diffed as deletions, and
4
+ * how a snapshot body is read so that iterating it to completion is the completeness signal.
5
+ * {@link RemoteBackend.heads} and {@link RemoteBackend.snapshot} are thin callers over these
6
+ * two functions; a host that serves the same grammar through routes of its own validates the
7
+ * answers with the same functions rather than a second parser, since this grammar decides what
8
+ * a working copy deletes locally.
9
+ *
10
+ * Only `TextDecoder` and the Web Streams reader are used, so the same code runs in a browser, a
11
+ * Worker, and Node.
12
+ */
13
+ import { headsDigest, isHeadsDigest } from "./heads-digest.js";
14
+ import { RemoteError, malformed } from "./remote-error.js";
15
+ /** A snapshot body that ended, or failed, before its terminator arrived with the announced count. */
16
+ export const SNAPSHOT_TRUNCATED = "SNAPSHOT_TRUNCATED";
17
+ /**
18
+ * A snapshot whose body arrived whole, with its terminator and the announced count, but whose
19
+ * rows digest to something other than the header announced. Not truncation: re-requesting will
20
+ * not necessarily repair it, since the authority contradicted itself. A consumer discards the
21
+ * documents' claim to the header digest either way.
22
+ */
23
+ export const SNAPSHOT_DIGEST_MISMATCH = "SNAPSHOT_DIGEST_MISMATCH";
24
+ function isDocumentHead(value) {
25
+ if (typeof value !== "object" || value === null)
26
+ return false;
27
+ const head = value;
28
+ return typeof head.id === "string" && typeof head.version === "string";
29
+ }
30
+ /**
31
+ * The `200` body of `GET /heads`, admitted or refused as one: a well-formed digest, a `heads`
32
+ * array of `{ id, version }` rows, a `count` equal to the number of rows, and rows that digest
33
+ * by the documented recipe to the digest served. Anything else rejects as malformed (a
34
+ * {@link MalformedAnswer}, code `MALFORMED_ANSWER`) rather than being trusted. The
35
+ * recomputation is what stops a listing that is whole by its own count but not the state its
36
+ * digest names (a shortened listing under the real digest) from being diffed as a mass
37
+ * deletion. Rows keep the order served: the recipe sorts for itself, so order does not decide
38
+ * admission.
39
+ */
40
+ export function parseHeadsAnswer(payload) {
41
+ const answer = payload;
42
+ if (!isHeadsDigest(answer?.digest))
43
+ throw malformed("wire heads answered without a well-formed digest");
44
+ if (!Array.isArray(answer.heads) || !answer.heads.every(isDocumentHead)) {
45
+ throw malformed("wire heads answered without a heads array of { id, version } rows");
46
+ }
47
+ if (answer.count !== answer.heads.length) {
48
+ throw malformed(`wire heads count ${String(answer.count)} disagrees with its ${answer.heads.length} row(s)`);
49
+ }
50
+ const heads = answer.heads.map(({ id, version }) => ({ id, version }));
51
+ const recomputed = headsDigest(heads);
52
+ if (recomputed !== answer.digest) {
53
+ throw malformed(`wire heads served digest ${answer.digest}, but its ${heads.length} row(s) digest to ${recomputed}`);
54
+ }
55
+ return { digest: answer.digest, heads };
56
+ }
57
+ /**
58
+ * Split a response body into its NDJSON lines as they arrive, decoding UTF-8 across chunk
59
+ * boundaries. A partial trailing line at the end of the body is a cut line and is not yielded; a
60
+ * transport failure while reading is reported as truncation, since either way the terminator
61
+ * never arrived. Leaving the loop early cancels the reader so the connection is released.
62
+ */
63
+ async function* ndjsonLines(body, status) {
64
+ const reader = body.getReader();
65
+ const decoder = new TextDecoder();
66
+ let buffered = "";
67
+ try {
68
+ for (;;) {
69
+ let chunk;
70
+ try {
71
+ chunk = await reader.read();
72
+ }
73
+ catch (cause) {
74
+ throw new RemoteError("snapshot body failed before its end line arrived", SNAPSHOT_TRUNCATED, status, cause);
75
+ }
76
+ buffered += decoder.decode(chunk.value, { stream: !chunk.done });
77
+ let start = 0;
78
+ for (let newline = buffered.indexOf("\n"); newline !== -1; newline = buffered.indexOf("\n", start)) {
79
+ yield buffered.slice(start, newline);
80
+ start = newline + 1;
81
+ }
82
+ buffered = buffered.slice(start);
83
+ if (chunk.done)
84
+ return;
85
+ }
86
+ }
87
+ finally {
88
+ await reader.cancel().catch(() => undefined);
89
+ }
90
+ }
91
+ function parseSnapshotLine(line) {
92
+ let record;
93
+ try {
94
+ record = JSON.parse(line);
95
+ }
96
+ catch {
97
+ throw malformed("snapshot line is not JSON");
98
+ }
99
+ if (typeof record !== "object" || record === null)
100
+ throw malformed("snapshot line is not a JSON object");
101
+ return record;
102
+ }
103
+ function parseSnapshotHeader(line) {
104
+ const record = parseSnapshotLine(line);
105
+ if (record.kind !== "snapshot")
106
+ throw malformed("snapshot did not begin with its header line");
107
+ if (typeof record.count !== "number" || !Number.isInteger(record.count) || record.count < 0) {
108
+ throw malformed("snapshot header carries no document count");
109
+ }
110
+ if (!isHeadsDigest(record.digest))
111
+ throw malformed("snapshot header carries no well-formed digest");
112
+ return { count: record.count, digest: record.digest };
113
+ }
114
+ /**
115
+ * The document lines of a snapshot, ending normally only at an `end` line whose count equals
116
+ * both the header's announcement and the documents actually seen, and only when the digest
117
+ * recomputed over the received heads equals the header's. Anything else is a rejection: a body
118
+ * that ends first is `SNAPSHOT_TRUNCATED`; a whole body whose heads digest differently is
119
+ * `SNAPSHOT_DIGEST_MISMATCH`; a line the contract does not admit is malformed. The digest is the
120
+ * client's own check of the listing it is about to trust: a count and a terminator say the body
121
+ * is whole, only the recipe says it is the state the header named.
122
+ */
123
+ async function* snapshotDocuments(lines, header, status) {
124
+ let seen = 0;
125
+ const received = [];
126
+ for await (const line of lines) {
127
+ const record = parseSnapshotLine(line);
128
+ if (record.kind === "doc") {
129
+ if (typeof record.id !== "string" || typeof record.version !== "string" || typeof record.body !== "string") {
130
+ throw malformed("snapshot doc line lacks id, version, or body");
131
+ }
132
+ if (typeof record.frontmatter !== "object" || record.frontmatter === null || Array.isArray(record.frontmatter)) {
133
+ throw malformed("snapshot doc line lacks a frontmatter object");
134
+ }
135
+ seen += 1;
136
+ if (seen > header.count)
137
+ throw malformed(`snapshot delivered more than the ${header.count} announced document(s)`);
138
+ received.push({ id: record.id, version: record.version });
139
+ yield { id: record.id, version: record.version, frontmatter: record.frontmatter, body: record.body };
140
+ continue;
141
+ }
142
+ if (record.kind === "end") {
143
+ if (record.count !== seen || seen !== header.count) {
144
+ throw new RemoteError(`snapshot end line counts ${String(record.count)} document(s), but ${seen} of ${header.count} announced arrived`, SNAPSHOT_TRUNCATED, status);
145
+ }
146
+ const recomputed = headsDigest(received);
147
+ if (recomputed !== header.digest) {
148
+ throw new RemoteError(`snapshot header announced digest ${header.digest}, but its ${seen} document(s) digest to ${recomputed}`, SNAPSHOT_DIGEST_MISMATCH, status);
149
+ }
150
+ return;
151
+ }
152
+ throw malformed(`snapshot line has unexpected kind ${JSON.stringify(record.kind)}`);
153
+ }
154
+ throw new RemoteError(`snapshot ended after ${seen} of ${header.count} document(s) without its end line`, SNAPSHOT_TRUNCATED, status);
155
+ }
156
+ /**
157
+ * Read a `GET /snapshot` body by the documented grammar. The header line is parsed before this
158
+ * resolves, so a body that ends before it rejects here with `SNAPSHOT_TRUNCATED` and a first
159
+ * line that is not the header rejects as malformed; the documents then stream through
160
+ * {@link RemoteSnapshot.docs}, which ends normally only for a whole body whose rows digest to
161
+ * the header's announcement (see {@link RemoteSnapshot}). Obtaining the response, checking its
162
+ * status, and re-requesting a truncated snapshot are the caller's: this function reads one body
163
+ * once and cancels the reader when the iteration is abandoned or fails.
164
+ */
165
+ export async function readSnapshotStream(body, options = {}) {
166
+ const status = options.status ?? 200;
167
+ const lines = ndjsonLines(body, status);
168
+ const first = await lines.next();
169
+ if (first.done)
170
+ throw new RemoteError("snapshot ended before its header line", SNAPSHOT_TRUNCATED, status);
171
+ let header;
172
+ try {
173
+ header = parseSnapshotHeader(first.value);
174
+ }
175
+ catch (err) {
176
+ await lines.return(undefined);
177
+ throw err;
178
+ }
179
+ return { header, docs: snapshotDocuments(lines, header, status) };
180
+ }
181
+ //# sourceMappingURL=remote-parsers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-parsers.js","sourceRoot":"","sources":["../src/remote-parsers.ts"],"names":[],"mappings":"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"}
package/dist/remote.d.ts CHANGED
@@ -1,4 +1,9 @@
1
1
  /** Browser-safe client adapter for the Superbee wire protocol. */
2
- export { RemoteBackend, RemoteError } from "./remote-backend.js";
3
- export type { FetchLike, RemoteBackendOptions } from "./remote-backend.js";
2
+ export { RemoteBackend } from "./remote-backend.js";
3
+ export type { FetchLike, HeadsOptions, RemoteBackendOptions, WireCapabilities } from "./remote-backend.js";
4
+ export { RemoteError } from "./remote-error.js";
5
+ export { SNAPSHOT_DIGEST_MISMATCH, SNAPSHOT_TRUNCATED, parseHeadsAnswer, readSnapshotStream } from "./remote-parsers.js";
6
+ export type { HeadsResult, ReadSnapshotStreamOptions, RemoteSnapshot, SnapshotDocument, SnapshotHeader } from "./remote-parsers.js";
7
+ export { headsDigest, isHeadsDigest, sortHeads } from "./heads-digest.js";
8
+ export type { DocumentHead } from "./heads-digest.js";
4
9
  //# sourceMappingURL=remote.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../src/remote.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAElE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACjE,YAAY,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../src/remote.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAElE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC3G,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,OAAO,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzH,YAAY,EAAE,WAAW,EAAE,yBAAyB,EAAE,cAAc,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACpI,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC1E,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}