@adcp/sdk 15.3.0 → 15.5.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 (130) hide show
  1. package/dist/lib/advanced.d.mts +2 -2
  2. package/dist/lib/advanced.d.ts +2 -2
  3. package/dist/lib/advanced.js +8 -2
  4. package/dist/lib/advanced.mjs +8 -2
  5. package/dist/lib/core/AgentClient.d.mts +4 -0
  6. package/dist/lib/core/AgentClient.d.ts +4 -0
  7. package/dist/lib/core/AgentClient.js +8 -0
  8. package/dist/lib/core/AgentClient.mjs +8 -0
  9. package/dist/lib/core/SingleAgentClient.d.mts +154 -0
  10. package/dist/lib/core/SingleAgentClient.d.ts +154 -0
  11. package/dist/lib/core/SingleAgentClient.js +661 -23
  12. package/dist/lib/core/SingleAgentClient.mjs +666 -23
  13. package/dist/lib/core/agent-discovery-cache.d.mts +103 -0
  14. package/dist/lib/core/agent-discovery-cache.d.ts +103 -0
  15. package/dist/lib/core/agent-discovery-cache.js +121 -0
  16. package/dist/lib/core/agent-discovery-cache.mjs +92 -0
  17. package/dist/lib/index.d.mts +5 -3
  18. package/dist/lib/index.d.ts +5 -3
  19. package/dist/lib/index.js +7 -0
  20. package/dist/lib/index.mjs +6 -0
  21. package/dist/lib/protocols/index.d.mts +2 -2
  22. package/dist/lib/protocols/index.d.ts +2 -2
  23. package/dist/lib/protocols/index.js +1 -1
  24. package/dist/lib/protocols/index.mjs +1 -1
  25. package/dist/lib/protocols/mcp-call-context.d.mts +109 -0
  26. package/dist/lib/protocols/mcp-call-context.d.ts +109 -0
  27. package/dist/lib/protocols/mcp-call-context.js +221 -0
  28. package/dist/lib/protocols/mcp-call-context.mjs +186 -0
  29. package/dist/lib/protocols/mcp-modern.d.mts +9 -1
  30. package/dist/lib/protocols/mcp-modern.d.ts +9 -1
  31. package/dist/lib/protocols/mcp-modern.js +180 -104
  32. package/dist/lib/protocols/mcp-modern.mjs +193 -104
  33. package/dist/lib/protocols/mcp-scope.d.mts +15 -0
  34. package/dist/lib/protocols/mcp-scope.d.ts +15 -0
  35. package/dist/lib/protocols/mcp.d.mts +4 -2
  36. package/dist/lib/protocols/mcp.d.ts +4 -2
  37. package/dist/lib/protocols/mcp.js +178 -154
  38. package/dist/lib/protocols/mcp.mjs +189 -153
  39. package/dist/lib/protocols/transportDiagnostics.d.mts +33 -1
  40. package/dist/lib/protocols/transportDiagnostics.d.ts +33 -1
  41. package/dist/lib/protocols/transportDiagnostics.js +128 -12
  42. package/dist/lib/protocols/transportDiagnostics.mjs +128 -12
  43. package/dist/lib/reporting/gcs/index.d.mts +2 -0
  44. package/dist/lib/reporting/gcs/index.d.ts +2 -0
  45. package/dist/lib/reporting/gcs/index.js +3 -0
  46. package/dist/lib/reporting/gcs/index.mjs +2 -0
  47. package/dist/lib/reporting/gcs/row-provider.d.mts +32 -0
  48. package/dist/lib/reporting/gcs/row-provider.d.ts +32 -0
  49. package/dist/lib/reporting/gcs/row-provider.js +205 -0
  50. package/dist/lib/reporting/gcs/row-provider.mjs +188 -0
  51. package/dist/lib/reporting/ledger/change-feed.d.mts +41 -0
  52. package/dist/lib/reporting/ledger/change-feed.d.ts +41 -0
  53. package/dist/lib/reporting/ledger/change-feed.js +101 -0
  54. package/dist/lib/reporting/ledger/change-feed.mjs +74 -0
  55. package/dist/lib/reporting/ledger/handler.d.mts +6 -0
  56. package/dist/lib/reporting/ledger/handler.d.ts +6 -0
  57. package/dist/lib/reporting/ledger/handler.js +51 -8
  58. package/dist/lib/reporting/ledger/handler.mjs +50 -8
  59. package/dist/lib/reporting/ledger/index.d.mts +5 -0
  60. package/dist/lib/reporting/ledger/index.d.ts +5 -0
  61. package/dist/lib/reporting/ledger/index.js +11 -1
  62. package/dist/lib/reporting/ledger/index.mjs +5 -0
  63. package/dist/lib/reporting/ledger/lifecycle.js +3 -2
  64. package/dist/lib/reporting/ledger/lifecycle.mjs +3 -2
  65. package/dist/lib/reporting/ledger/managed-postgres.d.mts +2 -0
  66. package/dist/lib/reporting/ledger/managed-postgres.d.ts +2 -0
  67. package/dist/lib/reporting/ledger/managed-postgres.js +26 -6
  68. package/dist/lib/reporting/ledger/managed-postgres.mjs +26 -6
  69. package/dist/lib/reporting/ledger/managed.d.mts +9 -0
  70. package/dist/lib/reporting/ledger/managed.d.ts +9 -0
  71. package/dist/lib/reporting/ledger/managed.js +18 -0
  72. package/dist/lib/reporting/ledger/managed.mjs +18 -0
  73. package/dist/lib/reporting/ledger/postgres.d.mts +179 -1
  74. package/dist/lib/reporting/ledger/postgres.d.ts +179 -1
  75. package/dist/lib/reporting/ledger/postgres.js +882 -19
  76. package/dist/lib/reporting/ledger/postgres.mjs +890 -19
  77. package/dist/lib/reporting/ledger/producer.js +13 -3
  78. package/dist/lib/reporting/ledger/producer.mjs +13 -3
  79. package/dist/lib/reporting/ledger/revision-metadata.d.mts +17 -0
  80. package/dist/lib/reporting/ledger/revision-metadata.d.ts +17 -0
  81. package/dist/lib/reporting/ledger/revision-metadata.js +71 -0
  82. package/dist/lib/reporting/ledger/revision-metadata.mjs +44 -0
  83. package/dist/lib/reporting/ledger/row-encoding.d.mts +128 -0
  84. package/dist/lib/reporting/ledger/row-encoding.d.ts +128 -0
  85. package/dist/lib/reporting/ledger/row-encoding.js +355 -0
  86. package/dist/lib/reporting/ledger/row-encoding.mjs +315 -0
  87. package/dist/lib/reporting/ledger/row-storage-errors.d.mts +7 -0
  88. package/dist/lib/reporting/ledger/row-storage-errors.d.ts +7 -0
  89. package/dist/lib/reporting/ledger/row-storage-errors.js +41 -0
  90. package/dist/lib/reporting/ledger/row-storage-errors.mjs +16 -0
  91. package/dist/lib/reporting/ledger/row-storage-migration.d.mts +41 -0
  92. package/dist/lib/reporting/ledger/row-storage-migration.d.ts +41 -0
  93. package/dist/lib/reporting/ledger/row-storage-migration.js +234 -0
  94. package/dist/lib/reporting/ledger/row-storage-migration.mjs +208 -0
  95. package/dist/lib/reporting/ledger/row-storage-object.d.mts +112 -0
  96. package/dist/lib/reporting/ledger/row-storage-object.d.ts +112 -0
  97. package/dist/lib/reporting/ledger/row-storage-object.js +281 -0
  98. package/dist/lib/reporting/ledger/row-storage-object.mjs +239 -0
  99. package/dist/lib/reporting/ledger/row-storage-provider-io.d.mts +51 -0
  100. package/dist/lib/reporting/ledger/row-storage-provider-io.d.ts +51 -0
  101. package/dist/lib/reporting/ledger/row-storage-provider-io.js +152 -0
  102. package/dist/lib/reporting/ledger/row-storage-provider-io.mjs +119 -0
  103. package/dist/lib/reporting/ledger/row-storage.d.mts +224 -0
  104. package/dist/lib/reporting/ledger/row-storage.d.ts +224 -0
  105. package/dist/lib/reporting/ledger/row-storage.js +692 -0
  106. package/dist/lib/reporting/ledger/row-storage.mjs +680 -0
  107. package/dist/lib/reporting/ledger/types.d.mts +45 -0
  108. package/dist/lib/reporting/ledger/types.d.ts +45 -0
  109. package/dist/lib/reporting/service/index.d.mts +41 -1
  110. package/dist/lib/reporting/service/index.d.ts +41 -1
  111. package/dist/lib/reporting/service/index.js +68 -1
  112. package/dist/lib/reporting/service/index.mjs +70 -1
  113. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  114. package/dist/lib/signing/agent-resolver/fetch-helpers.js +1 -1
  115. package/dist/lib/signing/agent-resolver/fetch-helpers.mjs +1 -1
  116. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  117. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  118. package/dist/lib/version.d.mts +3 -3
  119. package/dist/lib/version.d.ts +3 -3
  120. package/dist/lib/version.js +3 -3
  121. package/dist/lib/version.mjs +3 -3
  122. package/docs/README.md +1 -1
  123. package/docs/TYPE-SUMMARY.md +35 -2
  124. package/docs/guides/DISCOVERY-CACHE-AND-SESSION-REUSE.md +142 -0
  125. package/docs/guides/EXISTING-PLATFORM.md +43 -12
  126. package/docs/guides/REPORTING-LEDGER.md +209 -0
  127. package/docs/llms.txt +4 -2
  128. package/docs/migration-14.x-rc-worksheet.md +4 -4
  129. package/docs/recipes/verifying-inbound-webhooks.md +8 -0
  130. package/package.json +1 -1
@@ -0,0 +1,239 @@
1
+ import { createHash, randomUUID } from "crypto";
2
+ import { promises as fs } from "fs";
3
+ import * as path from "path";
4
+ import { canonicalize } from "../../utils/jcs.mjs";
5
+ import { ReportingRowStoreError } from "./row-storage-errors.mjs";
6
+ const REPORTING_ROW_KEY_PLACEHOLDERS = Object.freeze([
7
+ "prefix",
8
+ "namespace_key",
9
+ "account_key",
10
+ "period_date",
11
+ "finality",
12
+ "revision_key",
13
+ "content_sha256",
14
+ "chunk_index"
15
+ ]);
16
+ const REPORTING_ROW_DEFAULT_KEY_TEMPLATE = "{prefix}/{namespace_key}/{account_key}/{period_date}/{finality}/{revision_key}/{content_sha256}/{chunk_index}";
17
+ const PREFIX = /^[A-Za-z0-9_.=-]+(?:\/[A-Za-z0-9_.=-]+)*$/;
18
+ const KEY = /^[A-Za-z0-9_.=-]+(?:\/[A-Za-z0-9_.=-]+)*$/;
19
+ const PLACEHOLDER = /\{([a-z0-9_]+)\}/g;
20
+ function assertReportingRowKeyTemplateV1(template) {
21
+ if (!template.startsWith("{prefix}/{namespace_key}/")) {
22
+ throw new ReportingRowStoreError("INVALID_INPUT", "key templates must begin with {prefix}/{namespace_key}/");
23
+ }
24
+ for (const required of ["{content_sha256}", "{chunk_index}"]) {
25
+ if (!template.includes(required)) {
26
+ throw new ReportingRowStoreError("INVALID_INPUT", `key templates must include ${required}`);
27
+ }
28
+ }
29
+ const unknown = [...template.matchAll(PLACEHOLDER)].map((match) => match[1]).filter((name) => !REPORTING_ROW_KEY_PLACEHOLDERS.includes(name));
30
+ if (unknown.length > 0) {
31
+ throw new ReportingRowStoreError("INVALID_INPUT", `unknown key template placeholders: ${unknown.join(", ")}`);
32
+ }
33
+ const literal = template.replace(PLACEHOLDER, "x");
34
+ if (!KEY.test(literal) || literal.split("/").some((segment) => segment === "." || segment === "..")) {
35
+ throw new ReportingRowStoreError("INVALID_INPUT", "key template contains unsafe characters or segments");
36
+ }
37
+ }
38
+ function assertReportingRowPrefixV1(prefix) {
39
+ if (!PREFIX.test(prefix) || prefix.length > 512 || prefix.split("/").some((segment) => segment === "." || segment === "..")) {
40
+ throw new ReportingRowStoreError("INVALID_INPUT", "row binding prefix is invalid");
41
+ }
42
+ }
43
+ function reportingRowAccountKeyV1(namespaceKey, accountId) {
44
+ return sha256Hex(canonicalize(["adcp.account_key.v1", namespaceKey, accountId]));
45
+ }
46
+ function reportingRowRevisionKeyV1(rowSetId) {
47
+ return sha256Hex(canonicalize(["adcp.revision_key.v1", rowSetId]));
48
+ }
49
+ function renderReportingRowObjectKeyV1(input) {
50
+ const values = {
51
+ prefix: input.prefix,
52
+ namespace_key: input.namespaceKey,
53
+ account_key: reportingRowAccountKeyV1(input.namespaceKey, input.accountId),
54
+ period_date: periodDate(input.periodStart),
55
+ finality: input.finality ?? "adjustment",
56
+ revision_key: reportingRowRevisionKeyV1(input.rowSetId),
57
+ content_sha256: input.contentSha256,
58
+ chunk_index: String(input.chunkIndex).padStart(6, "0")
59
+ };
60
+ const key = input.template.replace(PLACEHOLDER, (_, name) => values[name]) + (input.compression === "gzip" ? ".jsonl.gz" : ".jsonl");
61
+ if (!KEY.test(key) || key.split("/").some((segment) => segment === "." || segment === "..") || !key.startsWith(`${input.prefix}/${input.namespaceKey}/`)) {
62
+ throw new ReportingRowStoreError("INVALID_INPUT", "rendered object key escapes the binding prefix");
63
+ }
64
+ return key;
65
+ }
66
+ function periodDate(periodStart) {
67
+ if (!periodStart) return "undated";
68
+ const parsed = new Date(periodStart);
69
+ if (Number.isNaN(parsed.getTime())) return "undated";
70
+ return parsed.toISOString().slice(0, 10).replace(/-/g, "/");
71
+ }
72
+ function createFilesystemReportingRowObjectProviderV1(options) {
73
+ const roots = /* @__PURE__ */ new Map();
74
+ for (const [name, directory] of Object.entries(options.roots)) {
75
+ if (!/^[A-Za-z0-9_.-]{1,64}$/.test(name) || !path.isAbsolute(directory)) {
76
+ throw new ReportingRowStoreError("INVALID_INPUT", "filesystem roots need a simple name and an absolute path");
77
+ }
78
+ roots.set(name, path.resolve(directory));
79
+ }
80
+ const resolve = (location, key) => {
81
+ const root = roots.get(location.root ?? "");
82
+ if (!root) throw new ReportingRowStoreError("UNSAFE_BINDING", "filesystem root is not configured");
83
+ const target = path.resolve(root, key);
84
+ if (!target.startsWith(root + path.sep)) {
85
+ throw new ReportingRowStoreError("INVALID_INPUT", "object key escapes the filesystem root");
86
+ }
87
+ return target;
88
+ };
89
+ const version = async (target) => {
90
+ const stat = await fs.stat(target);
91
+ return `${stat.ino}:${stat.mtimeMs}:${stat.size}`;
92
+ };
93
+ return {
94
+ name: "filesystem",
95
+ rangedReads: true,
96
+ validateLocation(location) {
97
+ const keys = Object.keys(location);
98
+ if (keys.length !== 1 || keys[0] !== "root" || !roots.has(location.root)) {
99
+ throw new ReportingRowStoreError("INVALID_INPUT", "filesystem locations name exactly one configured root");
100
+ }
101
+ },
102
+ async probe(input) {
103
+ const root = roots.get(input.location.root ?? "");
104
+ if (!root) throw new ReportingRowStoreError("UNSAFE_BINDING", "filesystem root is not configured");
105
+ await fs.mkdir(root, { recursive: true });
106
+ },
107
+ async putIfAbsent(input, context) {
108
+ context.signal.throwIfAborted();
109
+ const target = resolve(input.location, input.key);
110
+ await fs.mkdir(path.dirname(target), { recursive: true });
111
+ const temporary = `${target}.${randomUUID()}.tmp`;
112
+ const handle = await fs.open(temporary, "wx");
113
+ try {
114
+ await handle.writeFile(input.bytes);
115
+ await handle.sync();
116
+ } finally {
117
+ await handle.close();
118
+ }
119
+ try {
120
+ await fs.link(temporary, target);
121
+ return { created: true, nativeVersion: await version(target) };
122
+ } catch (error) {
123
+ if (error.code === "EEXIST") {
124
+ return { created: false, nativeVersion: await version(target) };
125
+ }
126
+ throw new ReportingRowStoreError("PROVIDER_UNAVAILABLE", "filesystem write failed");
127
+ } finally {
128
+ await fs.rm(temporary, { force: true });
129
+ }
130
+ },
131
+ async get(input, context) {
132
+ context.signal.throwIfAborted();
133
+ const target = resolve(input.location, input.key);
134
+ let handle;
135
+ try {
136
+ handle = await fs.open(target, "r");
137
+ } catch (error) {
138
+ if (error.code === "ENOENT") return null;
139
+ throw new ReportingRowStoreError("PROVIDER_UNAVAILABLE", "filesystem read failed");
140
+ }
141
+ try {
142
+ const stat = await handle.stat();
143
+ if (`${stat.ino}:${stat.mtimeMs}:${stat.size}` !== input.nativeVersion) return null;
144
+ const offset = input.range?.offset ?? 0;
145
+ const length = input.range?.length ?? stat.size - offset;
146
+ if (offset < 0 || length < 0 || offset + length > stat.size) {
147
+ throw new ReportingRowStoreError("ROWS_INTEGRITY_FAILED", "requested range exceeds the stored object");
148
+ }
149
+ if (length > input.maxBytes) {
150
+ throw new ReportingRowStoreError("ROWS_INTEGRITY_FAILED", "stored object exceeds its recorded size");
151
+ }
152
+ const buffer = Buffer.alloc(length);
153
+ await handle.read(buffer, 0, length, offset);
154
+ return buffer;
155
+ } finally {
156
+ await handle.close();
157
+ }
158
+ },
159
+ async delete(input, context) {
160
+ context.signal.throwIfAborted();
161
+ const target = resolve(input.location, input.key);
162
+ try {
163
+ if (await version(target) !== input.nativeVersion) return "absent";
164
+ await fs.unlink(target);
165
+ return "deleted";
166
+ } catch (error) {
167
+ if (error.code === "ENOENT") return "absent";
168
+ throw new ReportingRowStoreError("PROVIDER_UNAVAILABLE", "filesystem delete failed");
169
+ }
170
+ }
171
+ };
172
+ }
173
+ async function runReportingRowObjectProviderConformanceV1(provider, input) {
174
+ assertReportingRowPrefixV1(input.prefix);
175
+ provider.validateLocation(input.location);
176
+ const context = { signal: AbortSignal.timeout(6e4) };
177
+ const passed = [];
178
+ const key = `${input.prefix}/conformance/${randomUUID()}.jsonl`;
179
+ const first = Buffer.from('{"a":1}\n{"b":2}\n');
180
+ const metadata = { "adcp-installation": "conformance", "adcp-intent": "conformance" };
181
+ const base = { location: input.location, ...input.credentialRef ? { credentialRef: input.credentialRef } : {} };
182
+ await provider.probe(input, context);
183
+ passed.push("probe");
184
+ const created = await provider.putIfAbsent(
185
+ { ...base, key, bytes: first, contentType: "application/x-ndjson", metadata },
186
+ context
187
+ );
188
+ if (!created.created || !created.nativeVersion) throw new Error("putIfAbsent did not create a new object");
189
+ passed.push("create");
190
+ const conflict = await provider.putIfAbsent(
191
+ { ...base, key, bytes: Buffer.from('{"c":3}\n'), contentType: "application/x-ndjson", metadata },
192
+ context
193
+ );
194
+ if (conflict.created || conflict.nativeVersion !== created.nativeVersion) {
195
+ throw new Error("putIfAbsent replaced or re-versioned an existing object");
196
+ }
197
+ const stored = await provider.get({ ...base, key, nativeVersion: created.nativeVersion, maxBytes: 1024 }, context);
198
+ if (!stored || !Buffer.from(stored).equals(first))
199
+ throw new Error("create-only write did not preserve the first bytes");
200
+ passed.push("create-only");
201
+ if (provider.rangedReads) {
202
+ const ranged = await provider.get(
203
+ { ...base, key, nativeVersion: created.nativeVersion, range: { offset: 8, length: 8 }, maxBytes: 1024 },
204
+ context
205
+ );
206
+ if (!ranged || Buffer.from(ranged).toString("utf8") !== '{"b":2}\n')
207
+ throw new Error("ranged read returned wrong bytes");
208
+ passed.push("ranged-read");
209
+ }
210
+ if (await provider.get({ ...base, key, nativeVersion: "not-a-version", maxBytes: 1024 }, context) !== null) {
211
+ throw new Error("get returned bytes for a version that does not exist");
212
+ }
213
+ passed.push("version-pinned-read");
214
+ if (await provider.delete({ ...base, key, nativeVersion: "not-a-version" }, context) !== "absent") {
215
+ throw new Error("delete removed an object at a different version");
216
+ }
217
+ if (await provider.delete({ ...base, key, nativeVersion: created.nativeVersion }, context) !== "deleted") {
218
+ throw new Error("delete did not remove the exact version");
219
+ }
220
+ if (await provider.delete({ ...base, key, nativeVersion: created.nativeVersion }, context) !== "absent") {
221
+ throw new Error("repeated delete was not idempotent");
222
+ }
223
+ passed.push("exact-version-delete");
224
+ return passed;
225
+ }
226
+ function sha256Hex(value) {
227
+ return createHash("sha256").update(value).digest("hex");
228
+ }
229
+ export {
230
+ REPORTING_ROW_DEFAULT_KEY_TEMPLATE,
231
+ REPORTING_ROW_KEY_PLACEHOLDERS,
232
+ assertReportingRowKeyTemplateV1,
233
+ assertReportingRowPrefixV1,
234
+ createFilesystemReportingRowObjectProviderV1,
235
+ renderReportingRowObjectKeyV1,
236
+ reportingRowAccountKeyV1,
237
+ reportingRowRevisionKeyV1,
238
+ runReportingRowObjectProviderConformanceV1
239
+ };
@@ -0,0 +1,51 @@
1
+ import { ReportingRowStoreError } from './row-storage-errors.mjs';
2
+ /**
3
+ * Internal helpers shared by the bundled cloud row-object providers (GCS, S3,
4
+ * Azure Blob). Not part of the public `reporting/ledger` surface.
5
+ */
6
+ /** Stable code for an aborted operation: deadline expiry versus caller cancellation. */
7
+ export declare function reportingRowAbortError(signal: AbortSignal): ReportingRowStoreError;
8
+ /**
9
+ * Run one provider operation under the caller's signal. Settles as soon as the
10
+ * signal aborts, even when the official client ignores cancellation. Raw
11
+ * provider errors never escape: anything that is not already a
12
+ * `ReportingRowStoreError` becomes `PROVIDER_UNAVAILABLE`.
13
+ */
14
+ export declare function runReportingRowProviderOperation<T>(signal: AbortSignal, work: () => Promise<T>): Promise<T>;
15
+ /**
16
+ * Drain a byte stream, aborting once it exceeds `maxBytes`
17
+ * (`ROWS_INTEGRITY_FAILED`) or the signal fires. The stream is always destroyed.
18
+ */
19
+ export declare function readReportingRowStream(stream: AsyncIterable<unknown> & {
20
+ destroy?: (error?: Error) => unknown;
21
+ }, maxBytes: number, signal: AbortSignal): Promise<Buffer>;
22
+ /** Validate a get request's range and cap before any provider I/O. */
23
+ export declare function assertReportingRowReadBounds(input: {
24
+ range?: {
25
+ offset: number;
26
+ length: number;
27
+ };
28
+ maxBytes: number;
29
+ }): void;
30
+ /**
31
+ * Pick the host-injected client for a binding. `credentialRef` selects from a
32
+ * closed map; without one, the default client is used. Unknown references fail
33
+ * closed with `UNSAFE_BINDING` and never echo the reference.
34
+ */
35
+ export declare function selectReportingRowClient<T>(defaultClient: T | undefined, clients: ReadonlyMap<string, T>, credentialRef: string | undefined): T;
36
+ /** Copy a host client map into a closed, frozen lookup. */
37
+ export declare function reportingRowClientMap<T>(clients: Readonly<Record<string, T>> | undefined): ReadonlyMap<string, T>;
38
+ /** Throw `INVALID_INPUT` unless `location` has exactly the allowed keys (and every required one). */
39
+ export declare function assertReportingRowLocationKeys(location: Readonly<Record<string, string>>, required: readonly string[], optional?: readonly string[]): void;
40
+ /**
41
+ * Whether an object-store prefix filter can match keys under the binding's
42
+ * owned prefix. An absent or empty filter matches everything.
43
+ */
44
+ export declare function reportingRowPrefixFilterOverlaps(filter: string | undefined, bindingPrefix: string): boolean;
45
+ /**
46
+ * S3 and Azure record a version ID when the store is versioned, else the
47
+ * ETag. ETags are always kept quoted, which version IDs never are, so the two
48
+ * cannot be confused when the version is read back.
49
+ */
50
+ export declare function reportingRowEtagVersion(etag: string): string;
51
+ export declare function isReportingRowEtagVersion(nativeVersion: string): boolean;
@@ -0,0 +1,51 @@
1
+ import { ReportingRowStoreError } from './row-storage-errors';
2
+ /**
3
+ * Internal helpers shared by the bundled cloud row-object providers (GCS, S3,
4
+ * Azure Blob). Not part of the public `reporting/ledger` surface.
5
+ */
6
+ /** Stable code for an aborted operation: deadline expiry versus caller cancellation. */
7
+ export declare function reportingRowAbortError(signal: AbortSignal): ReportingRowStoreError;
8
+ /**
9
+ * Run one provider operation under the caller's signal. Settles as soon as the
10
+ * signal aborts, even when the official client ignores cancellation. Raw
11
+ * provider errors never escape: anything that is not already a
12
+ * `ReportingRowStoreError` becomes `PROVIDER_UNAVAILABLE`.
13
+ */
14
+ export declare function runReportingRowProviderOperation<T>(signal: AbortSignal, work: () => Promise<T>): Promise<T>;
15
+ /**
16
+ * Drain a byte stream, aborting once it exceeds `maxBytes`
17
+ * (`ROWS_INTEGRITY_FAILED`) or the signal fires. The stream is always destroyed.
18
+ */
19
+ export declare function readReportingRowStream(stream: AsyncIterable<unknown> & {
20
+ destroy?: (error?: Error) => unknown;
21
+ }, maxBytes: number, signal: AbortSignal): Promise<Buffer>;
22
+ /** Validate a get request's range and cap before any provider I/O. */
23
+ export declare function assertReportingRowReadBounds(input: {
24
+ range?: {
25
+ offset: number;
26
+ length: number;
27
+ };
28
+ maxBytes: number;
29
+ }): void;
30
+ /**
31
+ * Pick the host-injected client for a binding. `credentialRef` selects from a
32
+ * closed map; without one, the default client is used. Unknown references fail
33
+ * closed with `UNSAFE_BINDING` and never echo the reference.
34
+ */
35
+ export declare function selectReportingRowClient<T>(defaultClient: T | undefined, clients: ReadonlyMap<string, T>, credentialRef: string | undefined): T;
36
+ /** Copy a host client map into a closed, frozen lookup. */
37
+ export declare function reportingRowClientMap<T>(clients: Readonly<Record<string, T>> | undefined): ReadonlyMap<string, T>;
38
+ /** Throw `INVALID_INPUT` unless `location` has exactly the allowed keys (and every required one). */
39
+ export declare function assertReportingRowLocationKeys(location: Readonly<Record<string, string>>, required: readonly string[], optional?: readonly string[]): void;
40
+ /**
41
+ * Whether an object-store prefix filter can match keys under the binding's
42
+ * owned prefix. An absent or empty filter matches everything.
43
+ */
44
+ export declare function reportingRowPrefixFilterOverlaps(filter: string | undefined, bindingPrefix: string): boolean;
45
+ /**
46
+ * S3 and Azure record a version ID when the store is versioned, else the
47
+ * ETag. ETags are always kept quoted, which version IDs never are, so the two
48
+ * cannot be confused when the version is read back.
49
+ */
50
+ export declare function reportingRowEtagVersion(etag: string): string;
51
+ export declare function isReportingRowEtagVersion(nativeVersion: string): boolean;
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var row_storage_provider_io_exports = {};
20
+ __export(row_storage_provider_io_exports, {
21
+ assertReportingRowLocationKeys: () => assertReportingRowLocationKeys,
22
+ assertReportingRowReadBounds: () => assertReportingRowReadBounds,
23
+ isReportingRowEtagVersion: () => isReportingRowEtagVersion,
24
+ readReportingRowStream: () => readReportingRowStream,
25
+ reportingRowAbortError: () => reportingRowAbortError,
26
+ reportingRowClientMap: () => reportingRowClientMap,
27
+ reportingRowEtagVersion: () => reportingRowEtagVersion,
28
+ reportingRowPrefixFilterOverlaps: () => reportingRowPrefixFilterOverlaps,
29
+ runReportingRowProviderOperation: () => runReportingRowProviderOperation,
30
+ selectReportingRowClient: () => selectReportingRowClient
31
+ });
32
+ module.exports = __toCommonJS(row_storage_provider_io_exports);
33
+ var import_row_storage_errors = require('./row-storage-errors.js');
34
+ function reportingRowAbortError(signal) {
35
+ const reason = signal.reason;
36
+ if ((0, import_row_storage_errors.isReportingRowStoreError)(reason)) return reason;
37
+ return new import_row_storage_errors.ReportingRowStoreError(reason?.name === "TimeoutError" ? "DEADLINE_EXCEEDED" : "ABORTED");
38
+ }
39
+ async function runReportingRowProviderOperation(signal, work) {
40
+ if (!(signal instanceof AbortSignal)) throw new import_row_storage_errors.ReportingRowStoreError("INVALID_INPUT", "a signal is required");
41
+ if (signal.aborted) throw reportingRowAbortError(signal);
42
+ let onAbort;
43
+ try {
44
+ return await Promise.race([
45
+ work(),
46
+ new Promise((_, reject) => {
47
+ onAbort = () => reject(reportingRowAbortError(signal));
48
+ signal.addEventListener("abort", onAbort, { once: true });
49
+ })
50
+ ]);
51
+ } catch (error) {
52
+ if (signal.aborted) throw reportingRowAbortError(signal);
53
+ if ((0, import_row_storage_errors.isReportingRowStoreError)(error)) throw error;
54
+ throw new import_row_storage_errors.ReportingRowStoreError("PROVIDER_UNAVAILABLE");
55
+ } finally {
56
+ if (onAbort) signal.removeEventListener("abort", onAbort);
57
+ }
58
+ }
59
+ async function readReportingRowStream(stream, maxBytes, signal) {
60
+ const abort = () => stream.destroy?.(reportingRowAbortError(signal));
61
+ signal.addEventListener("abort", abort, { once: true });
62
+ const chunks = [];
63
+ let count = 0;
64
+ try {
65
+ if (signal.aborted) throw reportingRowAbortError(signal);
66
+ for await (const chunk of stream) {
67
+ if (signal.aborted) throw reportingRowAbortError(signal);
68
+ const bytes = typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk);
69
+ count += bytes.byteLength;
70
+ if (count > maxBytes) {
71
+ throw new import_row_storage_errors.ReportingRowStoreError("ROWS_INTEGRITY_FAILED", "stored object exceeds its recorded size");
72
+ }
73
+ chunks.push(bytes);
74
+ }
75
+ if (signal.aborted) throw reportingRowAbortError(signal);
76
+ return Buffer.concat(chunks);
77
+ } finally {
78
+ signal.removeEventListener("abort", abort);
79
+ stream.destroy?.();
80
+ }
81
+ }
82
+ function assertReportingRowReadBounds(input) {
83
+ if (!Number.isSafeInteger(input.maxBytes) || input.maxBytes < 0) {
84
+ throw new import_row_storage_errors.ReportingRowStoreError("INVALID_INPUT", "maxBytes must be a non-negative integer");
85
+ }
86
+ if (input.range === void 0) return;
87
+ const { offset, length } = input.range;
88
+ if (!Number.isSafeInteger(offset) || !Number.isSafeInteger(length) || offset < 0 || length < 0) {
89
+ throw new import_row_storage_errors.ReportingRowStoreError("INVALID_INPUT", "range must use non-negative integers");
90
+ }
91
+ if (length > input.maxBytes) {
92
+ throw new import_row_storage_errors.ReportingRowStoreError("ROWS_INTEGRITY_FAILED", "requested range exceeds the read cap");
93
+ }
94
+ }
95
+ function selectReportingRowClient(defaultClient, clients, credentialRef) {
96
+ const client = credentialRef === void 0 ? defaultClient : clients.get(credentialRef);
97
+ if (client === void 0) {
98
+ throw new import_row_storage_errors.ReportingRowStoreError("UNSAFE_BINDING", "no client is configured for this binding");
99
+ }
100
+ return client;
101
+ }
102
+ function reportingRowClientMap(clients) {
103
+ const map = /* @__PURE__ */ new Map();
104
+ if (clients === void 0) return map;
105
+ if (!clients || typeof clients !== "object") {
106
+ throw new import_row_storage_errors.ReportingRowStoreError("INVALID_INPUT", "clients must map credential references to clients");
107
+ }
108
+ for (const [ref, client] of Object.entries(clients)) {
109
+ if (!/^[A-Za-z0-9_.:-]{1,128}$/.test(ref) || !client) {
110
+ throw new import_row_storage_errors.ReportingRowStoreError("INVALID_INPUT", "clients must map credential references to clients");
111
+ }
112
+ map.set(ref, client);
113
+ }
114
+ return map;
115
+ }
116
+ function assertReportingRowLocationKeys(location, required, optional = []) {
117
+ if (!location || typeof location !== "object" || Array.isArray(location)) {
118
+ throw new import_row_storage_errors.ReportingRowStoreError("INVALID_INPUT", "location must be an object");
119
+ }
120
+ const keys = Object.keys(location);
121
+ const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
122
+ if (keys.some((key) => !allowed.has(key) || typeof location[key] !== "string") || required.some((key) => !keys.includes(key))) {
123
+ throw new import_row_storage_errors.ReportingRowStoreError(
124
+ "INVALID_INPUT",
125
+ `location accepts only ${[...required, ...optional.map((key) => `${key}?`)].join(", ")}`
126
+ );
127
+ }
128
+ }
129
+ function reportingRowPrefixFilterOverlaps(filter, bindingPrefix) {
130
+ if (!filter) return true;
131
+ const owned = `${bindingPrefix}/`;
132
+ return owned.startsWith(filter) || filter.startsWith(owned);
133
+ }
134
+ function reportingRowEtagVersion(etag) {
135
+ return etag.startsWith('"') && etag.endsWith('"') && etag.length >= 2 ? etag : `"${etag}"`;
136
+ }
137
+ function isReportingRowEtagVersion(nativeVersion) {
138
+ return nativeVersion.length >= 2 && nativeVersion.startsWith('"') && nativeVersion.endsWith('"');
139
+ }
140
+ // Annotate the CommonJS export names for ESM import in node:
141
+ 0 && (module.exports = {
142
+ assertReportingRowLocationKeys,
143
+ assertReportingRowReadBounds,
144
+ isReportingRowEtagVersion,
145
+ readReportingRowStream,
146
+ reportingRowAbortError,
147
+ reportingRowClientMap,
148
+ reportingRowEtagVersion,
149
+ reportingRowPrefixFilterOverlaps,
150
+ runReportingRowProviderOperation,
151
+ selectReportingRowClient
152
+ });
@@ -0,0 +1,119 @@
1
+ import { ReportingRowStoreError, isReportingRowStoreError } from "./row-storage-errors.mjs";
2
+ function reportingRowAbortError(signal) {
3
+ const reason = signal.reason;
4
+ if (isReportingRowStoreError(reason)) return reason;
5
+ return new ReportingRowStoreError(reason?.name === "TimeoutError" ? "DEADLINE_EXCEEDED" : "ABORTED");
6
+ }
7
+ async function runReportingRowProviderOperation(signal, work) {
8
+ if (!(signal instanceof AbortSignal)) throw new ReportingRowStoreError("INVALID_INPUT", "a signal is required");
9
+ if (signal.aborted) throw reportingRowAbortError(signal);
10
+ let onAbort;
11
+ try {
12
+ return await Promise.race([
13
+ work(),
14
+ new Promise((_, reject) => {
15
+ onAbort = () => reject(reportingRowAbortError(signal));
16
+ signal.addEventListener("abort", onAbort, { once: true });
17
+ })
18
+ ]);
19
+ } catch (error) {
20
+ if (signal.aborted) throw reportingRowAbortError(signal);
21
+ if (isReportingRowStoreError(error)) throw error;
22
+ throw new ReportingRowStoreError("PROVIDER_UNAVAILABLE");
23
+ } finally {
24
+ if (onAbort) signal.removeEventListener("abort", onAbort);
25
+ }
26
+ }
27
+ async function readReportingRowStream(stream, maxBytes, signal) {
28
+ const abort = () => stream.destroy?.(reportingRowAbortError(signal));
29
+ signal.addEventListener("abort", abort, { once: true });
30
+ const chunks = [];
31
+ let count = 0;
32
+ try {
33
+ if (signal.aborted) throw reportingRowAbortError(signal);
34
+ for await (const chunk of stream) {
35
+ if (signal.aborted) throw reportingRowAbortError(signal);
36
+ const bytes = typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk);
37
+ count += bytes.byteLength;
38
+ if (count > maxBytes) {
39
+ throw new ReportingRowStoreError("ROWS_INTEGRITY_FAILED", "stored object exceeds its recorded size");
40
+ }
41
+ chunks.push(bytes);
42
+ }
43
+ if (signal.aborted) throw reportingRowAbortError(signal);
44
+ return Buffer.concat(chunks);
45
+ } finally {
46
+ signal.removeEventListener("abort", abort);
47
+ stream.destroy?.();
48
+ }
49
+ }
50
+ function assertReportingRowReadBounds(input) {
51
+ if (!Number.isSafeInteger(input.maxBytes) || input.maxBytes < 0) {
52
+ throw new ReportingRowStoreError("INVALID_INPUT", "maxBytes must be a non-negative integer");
53
+ }
54
+ if (input.range === void 0) return;
55
+ const { offset, length } = input.range;
56
+ if (!Number.isSafeInteger(offset) || !Number.isSafeInteger(length) || offset < 0 || length < 0) {
57
+ throw new ReportingRowStoreError("INVALID_INPUT", "range must use non-negative integers");
58
+ }
59
+ if (length > input.maxBytes) {
60
+ throw new ReportingRowStoreError("ROWS_INTEGRITY_FAILED", "requested range exceeds the read cap");
61
+ }
62
+ }
63
+ function selectReportingRowClient(defaultClient, clients, credentialRef) {
64
+ const client = credentialRef === void 0 ? defaultClient : clients.get(credentialRef);
65
+ if (client === void 0) {
66
+ throw new ReportingRowStoreError("UNSAFE_BINDING", "no client is configured for this binding");
67
+ }
68
+ return client;
69
+ }
70
+ function reportingRowClientMap(clients) {
71
+ const map = /* @__PURE__ */ new Map();
72
+ if (clients === void 0) return map;
73
+ if (!clients || typeof clients !== "object") {
74
+ throw new ReportingRowStoreError("INVALID_INPUT", "clients must map credential references to clients");
75
+ }
76
+ for (const [ref, client] of Object.entries(clients)) {
77
+ if (!/^[A-Za-z0-9_.:-]{1,128}$/.test(ref) || !client) {
78
+ throw new ReportingRowStoreError("INVALID_INPUT", "clients must map credential references to clients");
79
+ }
80
+ map.set(ref, client);
81
+ }
82
+ return map;
83
+ }
84
+ function assertReportingRowLocationKeys(location, required, optional = []) {
85
+ if (!location || typeof location !== "object" || Array.isArray(location)) {
86
+ throw new ReportingRowStoreError("INVALID_INPUT", "location must be an object");
87
+ }
88
+ const keys = Object.keys(location);
89
+ const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
90
+ if (keys.some((key) => !allowed.has(key) || typeof location[key] !== "string") || required.some((key) => !keys.includes(key))) {
91
+ throw new ReportingRowStoreError(
92
+ "INVALID_INPUT",
93
+ `location accepts only ${[...required, ...optional.map((key) => `${key}?`)].join(", ")}`
94
+ );
95
+ }
96
+ }
97
+ function reportingRowPrefixFilterOverlaps(filter, bindingPrefix) {
98
+ if (!filter) return true;
99
+ const owned = `${bindingPrefix}/`;
100
+ return owned.startsWith(filter) || filter.startsWith(owned);
101
+ }
102
+ function reportingRowEtagVersion(etag) {
103
+ return etag.startsWith('"') && etag.endsWith('"') && etag.length >= 2 ? etag : `"${etag}"`;
104
+ }
105
+ function isReportingRowEtagVersion(nativeVersion) {
106
+ return nativeVersion.length >= 2 && nativeVersion.startsWith('"') && nativeVersion.endsWith('"');
107
+ }
108
+ export {
109
+ assertReportingRowLocationKeys,
110
+ assertReportingRowReadBounds,
111
+ isReportingRowEtagVersion,
112
+ readReportingRowStream,
113
+ reportingRowAbortError,
114
+ reportingRowClientMap,
115
+ reportingRowEtagVersion,
116
+ reportingRowPrefixFilterOverlaps,
117
+ runReportingRowProviderOperation,
118
+ selectReportingRowClient
119
+ };