@adcp/sdk 15.4.0 → 15.6.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 (164) hide show
  1. package/README.md +15 -0
  2. package/dist/lib/advanced.d.mts +2 -2
  3. package/dist/lib/advanced.d.ts +2 -2
  4. package/dist/lib/advanced.js +8 -2
  5. package/dist/lib/advanced.mjs +8 -2
  6. package/dist/lib/auth/oauth/index.d.mts +1 -0
  7. package/dist/lib/auth/oauth/index.d.ts +1 -0
  8. package/dist/lib/auth/oauth/index.js +5 -0
  9. package/dist/lib/auth/oauth/index.mjs +3 -0
  10. package/dist/lib/auth/oauth/pending-web-flow.d.mts +16 -0
  11. package/dist/lib/auth/oauth/pending-web-flow.d.ts +16 -0
  12. package/dist/lib/auth/oauth/pending-web-flow.js +66 -0
  13. package/dist/lib/auth/oauth/pending-web-flow.mjs +41 -0
  14. package/dist/lib/auth/oauth/web-flow.js +3 -3
  15. package/dist/lib/auth/oauth/web-flow.mjs +3 -3
  16. package/dist/lib/core/AgentClient.d.mts +4 -0
  17. package/dist/lib/core/AgentClient.d.ts +4 -0
  18. package/dist/lib/core/AgentClient.js +8 -0
  19. package/dist/lib/core/AgentClient.mjs +8 -0
  20. package/dist/lib/core/SingleAgentClient.d.mts +154 -0
  21. package/dist/lib/core/SingleAgentClient.d.ts +154 -0
  22. package/dist/lib/core/SingleAgentClient.js +661 -23
  23. package/dist/lib/core/SingleAgentClient.mjs +666 -23
  24. package/dist/lib/core/agent-discovery-cache.d.mts +103 -0
  25. package/dist/lib/core/agent-discovery-cache.d.ts +103 -0
  26. package/dist/lib/core/agent-discovery-cache.js +121 -0
  27. package/dist/lib/core/agent-discovery-cache.mjs +92 -0
  28. package/dist/lib/index.d.mts +5 -3
  29. package/dist/lib/index.d.ts +5 -3
  30. package/dist/lib/index.js +7 -0
  31. package/dist/lib/index.mjs +6 -0
  32. package/dist/lib/protocols/index.d.mts +2 -2
  33. package/dist/lib/protocols/index.d.ts +2 -2
  34. package/dist/lib/protocols/index.js +1 -1
  35. package/dist/lib/protocols/index.mjs +1 -1
  36. package/dist/lib/protocols/mcp-call-context.d.mts +109 -0
  37. package/dist/lib/protocols/mcp-call-context.d.ts +109 -0
  38. package/dist/lib/protocols/mcp-call-context.js +221 -0
  39. package/dist/lib/protocols/mcp-call-context.mjs +186 -0
  40. package/dist/lib/protocols/mcp-modern.d.mts +9 -1
  41. package/dist/lib/protocols/mcp-modern.d.ts +9 -1
  42. package/dist/lib/protocols/mcp-modern.js +180 -104
  43. package/dist/lib/protocols/mcp-modern.mjs +193 -104
  44. package/dist/lib/protocols/mcp-scope.d.mts +15 -0
  45. package/dist/lib/protocols/mcp-scope.d.ts +15 -0
  46. package/dist/lib/protocols/mcp.d.mts +4 -2
  47. package/dist/lib/protocols/mcp.d.ts +4 -2
  48. package/dist/lib/protocols/mcp.js +178 -154
  49. package/dist/lib/protocols/mcp.mjs +189 -153
  50. package/dist/lib/protocols/transportDiagnostics.d.mts +33 -1
  51. package/dist/lib/protocols/transportDiagnostics.d.ts +33 -1
  52. package/dist/lib/protocols/transportDiagnostics.js +128 -12
  53. package/dist/lib/protocols/transportDiagnostics.mjs +128 -12
  54. package/dist/lib/registry/types.generated.d.mts +63 -1
  55. package/dist/lib/registry/types.generated.d.ts +63 -1
  56. package/dist/lib/reporting/azure/index.d.mts +26 -0
  57. package/dist/lib/reporting/azure/index.d.ts +26 -0
  58. package/dist/lib/reporting/azure/index.js +247 -0
  59. package/dist/lib/reporting/azure/index.mjs +230 -0
  60. package/dist/lib/reporting/bigquery/index.d.mts +127 -0
  61. package/dist/lib/reporting/bigquery/index.d.ts +127 -0
  62. package/dist/lib/reporting/bigquery/index.js +379 -0
  63. package/dist/lib/reporting/bigquery/index.mjs +341 -0
  64. package/dist/lib/reporting/gcs/index.d.mts +2 -0
  65. package/dist/lib/reporting/gcs/index.d.ts +2 -0
  66. package/dist/lib/reporting/gcs/index.js +3 -0
  67. package/dist/lib/reporting/gcs/index.mjs +2 -0
  68. package/dist/lib/reporting/gcs/row-provider.d.mts +32 -0
  69. package/dist/lib/reporting/gcs/row-provider.d.ts +32 -0
  70. package/dist/lib/reporting/gcs/row-provider.js +205 -0
  71. package/dist/lib/reporting/gcs/row-provider.mjs +188 -0
  72. package/dist/lib/reporting/ledger/change-feed.d.mts +41 -0
  73. package/dist/lib/reporting/ledger/change-feed.d.ts +41 -0
  74. package/dist/lib/reporting/ledger/change-feed.js +101 -0
  75. package/dist/lib/reporting/ledger/change-feed.mjs +74 -0
  76. package/dist/lib/reporting/ledger/handler.js +22 -8
  77. package/dist/lib/reporting/ledger/handler.mjs +22 -8
  78. package/dist/lib/reporting/ledger/index.d.mts +4 -0
  79. package/dist/lib/reporting/ledger/index.d.ts +4 -0
  80. package/dist/lib/reporting/ledger/index.js +9 -1
  81. package/dist/lib/reporting/ledger/index.mjs +4 -0
  82. package/dist/lib/reporting/ledger/managed-postgres.d.mts +2 -0
  83. package/dist/lib/reporting/ledger/managed-postgres.d.ts +2 -0
  84. package/dist/lib/reporting/ledger/managed-postgres.js +21 -1
  85. package/dist/lib/reporting/ledger/managed-postgres.mjs +21 -1
  86. package/dist/lib/reporting/ledger/managed.d.mts +9 -0
  87. package/dist/lib/reporting/ledger/managed.d.ts +9 -0
  88. package/dist/lib/reporting/ledger/managed.js +18 -0
  89. package/dist/lib/reporting/ledger/managed.mjs +18 -0
  90. package/dist/lib/reporting/ledger/postgres.d.mts +185 -0
  91. package/dist/lib/reporting/ledger/postgres.d.ts +185 -0
  92. package/dist/lib/reporting/ledger/postgres.js +872 -26
  93. package/dist/lib/reporting/ledger/postgres.mjs +880 -26
  94. package/dist/lib/reporting/ledger/producer.js +10 -1
  95. package/dist/lib/reporting/ledger/producer.mjs +10 -1
  96. package/dist/lib/reporting/ledger/row-storage-errors.d.mts +7 -0
  97. package/dist/lib/reporting/ledger/row-storage-errors.d.ts +7 -0
  98. package/dist/lib/reporting/ledger/row-storage-errors.js +41 -0
  99. package/dist/lib/reporting/ledger/row-storage-errors.mjs +16 -0
  100. package/dist/lib/reporting/ledger/row-storage-migration.d.mts +41 -0
  101. package/dist/lib/reporting/ledger/row-storage-migration.d.ts +41 -0
  102. package/dist/lib/reporting/ledger/row-storage-migration.js +234 -0
  103. package/dist/lib/reporting/ledger/row-storage-migration.mjs +208 -0
  104. package/dist/lib/reporting/ledger/row-storage-object.d.mts +112 -0
  105. package/dist/lib/reporting/ledger/row-storage-object.d.ts +112 -0
  106. package/dist/lib/reporting/ledger/row-storage-object.js +304 -0
  107. package/dist/lib/reporting/ledger/row-storage-object.mjs +262 -0
  108. package/dist/lib/reporting/ledger/row-storage-provider-io.d.mts +44 -0
  109. package/dist/lib/reporting/ledger/row-storage-provider-io.d.ts +44 -0
  110. package/dist/lib/reporting/ledger/row-storage-provider-io.js +142 -0
  111. package/dist/lib/reporting/ledger/row-storage-provider-io.mjs +111 -0
  112. package/dist/lib/reporting/ledger/row-storage.d.mts +224 -0
  113. package/dist/lib/reporting/ledger/row-storage.d.ts +224 -0
  114. package/dist/lib/reporting/ledger/row-storage.js +692 -0
  115. package/dist/lib/reporting/ledger/row-storage.mjs +680 -0
  116. package/dist/lib/reporting/ledger/types.d.mts +8 -0
  117. package/dist/lib/reporting/ledger/types.d.ts +8 -0
  118. package/dist/lib/reporting/s3/index.d.mts +29 -0
  119. package/dist/lib/reporting/s3/index.d.ts +29 -0
  120. package/dist/lib/reporting/s3/index.js +324 -0
  121. package/dist/lib/reporting/s3/index.mjs +317 -0
  122. package/dist/lib/reporting/service/index.d.mts +43 -1
  123. package/dist/lib/reporting/service/index.d.ts +43 -1
  124. package/dist/lib/reporting/service/index.js +69 -1
  125. package/dist/lib/reporting/service/index.mjs +71 -1
  126. package/dist/lib/schemas/browser.d.mts +14 -0
  127. package/dist/lib/schemas/browser.d.ts +14 -0
  128. package/dist/lib/schemas/browser.js +48 -0
  129. package/dist/lib/schemas/browser.mjs +20 -0
  130. package/dist/lib/schemas/index.d.mts +2 -120
  131. package/dist/lib/schemas/index.d.ts +2 -120
  132. package/dist/lib/schemas/index.js +3 -11
  133. package/dist/lib/schemas/index.mjs +1 -9
  134. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  135. package/dist/lib/testing/index.d.mts +1 -0
  136. package/dist/lib/testing/index.d.ts +1 -0
  137. package/dist/lib/testing/index.js +3 -0
  138. package/dist/lib/testing/index.mjs +2 -0
  139. package/dist/lib/testing/pending-web-flow-store.d.mts +20 -0
  140. package/dist/lib/testing/pending-web-flow-store.d.ts +20 -0
  141. package/dist/lib/testing/pending-web-flow-store.js +132 -0
  142. package/dist/lib/testing/pending-web-flow-store.mjs +98 -0
  143. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  144. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  145. package/dist/lib/validation/get-products-request.d.mts +122 -0
  146. package/dist/lib/validation/get-products-request.d.ts +122 -0
  147. package/dist/lib/validation/get-products-request.js +39 -0
  148. package/dist/lib/validation/get-products-request.mjs +14 -0
  149. package/dist/lib/version.d.mts +3 -3
  150. package/dist/lib/version.d.ts +3 -3
  151. package/dist/lib/version.js +3 -3
  152. package/dist/lib/version.mjs +3 -3
  153. package/docs/README.md +1 -1
  154. package/docs/TYPE-SUMMARY.md +35 -2
  155. package/docs/ZOD-SCHEMAS.md +13 -1
  156. package/docs/guides/DISCOVERY-CACHE-AND-SESSION-REUSE.md +142 -0
  157. package/docs/guides/EXISTING-PLATFORM.md +43 -12
  158. package/docs/guides/OAUTH-ISSUER-BINDING.md +6 -0
  159. package/docs/guides/REPORTING-LEDGER.md +374 -0
  160. package/docs/guides/REPORTING-OPERATIONS.md +76 -1
  161. package/docs/guides/WEB-OAUTH.md +48 -1
  162. package/docs/llms.txt +4 -2
  163. package/docs/migration-14.x-rc-worksheet.md +6 -4
  164. package/package.json +64 -1
@@ -0,0 +1,341 @@
1
+ import { createHash } from "crypto";
2
+ import { promises as fs } from "fs";
3
+ import { tmpdir } from "os";
4
+ import * as path from "path";
5
+ const REPORTING_WAREHOUSE_SINK_MIGRATION = `
6
+ CREATE TABLE IF NOT EXISTS adcp_reporting_warehouse_sinks (
7
+ sink_name TEXT PRIMARY KEY CHECK (sink_name ~ '^[A-Za-z0-9_.:-]{1,128}$'),
8
+ committed_cursor TEXT,
9
+ planned_from_cursor TEXT,
10
+ planned_to_cursor TEXT,
11
+ planned_revisions JSONB,
12
+ planned_rows_attempt INTEGER NOT NULL DEFAULT 0 CHECK (planned_rows_attempt >= 0),
13
+ planned_revisions_attempt INTEGER NOT NULL DEFAULT 0 CHECK (planned_revisions_attempt >= 0),
14
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
15
+ CHECK ((planned_to_cursor IS NULL) = (planned_revisions IS NULL))
16
+ );
17
+ `.trim();
18
+ const PROJECT = /^[a-z][a-z0-9-]{4,61}[a-z0-9]$/;
19
+ const DATASET_OR_TABLE = /^[A-Za-z_][A-Za-z0-9_]{0,1023}$/;
20
+ const NAME = /^[A-Za-z0-9_.:-]{1,128}$/;
21
+ function createBigQueryReportingWarehouseSinkV1(options) {
22
+ if (!NAME.test(options.name)) throw new TypeError("Warehouse sink name must be a bounded non-secret identifier");
23
+ if (options.projectId !== void 0 && !PROJECT.test(options.projectId)) {
24
+ throw new TypeError("BigQuery projectId is invalid");
25
+ }
26
+ for (const [label, value] of [
27
+ ["datasetId", options.datasetId],
28
+ ["rowsTableId", options.rowsTableId],
29
+ ["revisionsTableId", options.revisionsTableId]
30
+ ]) {
31
+ if (!DATASET_OR_TABLE.test(value)) throw new TypeError(`BigQuery ${label} is invalid`);
32
+ }
33
+ const loadTimeout = options.loadTimeoutMilliseconds ?? 6e5;
34
+ if (!Number.isSafeInteger(loadTimeout) || loadTimeout < 1e3 || loadTimeout > 6 * 36e5) {
35
+ throw new RangeError("loadTimeoutMilliseconds must be between 1 second and 6 hours");
36
+ }
37
+ const maxRevisions = options.maxRevisionsPerBatch ?? 200;
38
+ if (!Number.isSafeInteger(maxRevisions) || maxRevisions < 1 || maxRevisions > 1e4) {
39
+ throw new RangeError("maxRevisionsPerBatch must be between 1 and 10000");
40
+ }
41
+ const tables = {
42
+ projectId: options.projectId,
43
+ datasetId: options.datasetId,
44
+ rowsTableId: options.rowsTableId,
45
+ revisionsTableId: options.revisionsTableId
46
+ };
47
+ const dataset = options.bigquery.dataset(options.datasetId, {
48
+ ...options.projectId ? { projectId: options.projectId } : {},
49
+ ...options.location ? { location: options.location } : {}
50
+ });
51
+ async function loadOnce(tableId, jobId, lines, signal) {
52
+ signal?.throwIfAborted();
53
+ const directory = await fs.mkdtemp(path.join(tmpdir(), "adcp-warehouse-"));
54
+ const file = path.join(directory, `${jobId}.ndjson`);
55
+ try {
56
+ await fs.writeFile(file, lines.length ? lines.join("\n") + "\n" : "");
57
+ try {
58
+ await withinDeadline(
59
+ () => dataset.table(tableId).load(file, {
60
+ jobId,
61
+ sourceFormat: "NEWLINE_DELIMITED_JSON",
62
+ writeDisposition: "WRITE_APPEND",
63
+ jobTimeoutMs: String(loadTimeout),
64
+ ...options.location ? { location: options.location } : {},
65
+ ...options.labels ? { labels: { ...options.labels } } : {}
66
+ }),
67
+ loadTimeout,
68
+ signal
69
+ );
70
+ } catch (error) {
71
+ if (!isAlreadyExists(error)) throw error;
72
+ const metadata = await withinDeadline(
73
+ () => options.bigquery.job(jobId, options.location ? { location: options.location } : void 0).getMetadata(),
74
+ loadTimeout,
75
+ signal
76
+ );
77
+ assertJobSucceeded(metadata);
78
+ }
79
+ } finally {
80
+ await fs.rm(directory, { recursive: true, force: true });
81
+ }
82
+ }
83
+ return {
84
+ defaultTablesSql: reportingWarehouseDefaultTablesSqlV1(tables),
85
+ currentRowsViewSql: reportingWarehouseCurrentRowsViewSqlV1(tables),
86
+ async runOnce(runOptions = {}) {
87
+ const signal = runOptions.signal;
88
+ await options.db.query(
89
+ `INSERT INTO adcp_reporting_warehouse_sinks (sink_name) VALUES ($1) ON CONFLICT DO NOTHING`,
90
+ [options.name]
91
+ );
92
+ const state = (await options.db.query(
93
+ `SELECT committed_cursor, planned_from_cursor, planned_to_cursor, planned_revisions,
94
+ planned_rows_attempt, planned_revisions_attempt
95
+ FROM adcp_reporting_warehouse_sinks WHERE sink_name = $1`,
96
+ [options.name]
97
+ )).rows[0];
98
+ let plan = state.planned_to_cursor && state.planned_revisions ? {
99
+ from: state.planned_from_cursor,
100
+ to: state.planned_to_cursor,
101
+ revisions: state.planned_revisions,
102
+ rowsAttempt: state.planned_rows_attempt,
103
+ revisionsAttempt: state.planned_revisions_attempt
104
+ } : void 0;
105
+ if (!plan) {
106
+ const page = await options.store.changesAfter({
107
+ ...state.committed_cursor ? { cursor: state.committed_cursor } : {},
108
+ ...options.account_id ? { account_id: options.account_id } : {},
109
+ kinds: ["revision"],
110
+ limit: maxRevisions
111
+ });
112
+ if (page.records.length === 0) {
113
+ if (page.cursor !== state.committed_cursor) await commit(page.cursor);
114
+ return { idle: true, revisions: 0, rows: 0, missing: [] };
115
+ }
116
+ plan = {
117
+ from: state.committed_cursor,
118
+ to: page.cursor,
119
+ revisions: page.records.map((record) => ({ id: record.record_id, account_id: record.account_id })),
120
+ rowsAttempt: 0,
121
+ revisionsAttempt: 0
122
+ };
123
+ await options.db.query(
124
+ `UPDATE adcp_reporting_warehouse_sinks
125
+ SET planned_from_cursor = $2, planned_to_cursor = $3, planned_revisions = $4::jsonb,
126
+ planned_rows_attempt = 0, planned_revisions_attempt = 0, updated_at = clock_timestamp()
127
+ WHERE sink_name = $1`,
128
+ [options.name, plan.from, plan.to, JSON.stringify(plan.revisions)]
129
+ );
130
+ }
131
+ const rowLines = [];
132
+ const revisionLines = [];
133
+ const missing = [];
134
+ for (const planned of plan.revisions) {
135
+ signal?.throwIfAborted();
136
+ const revision = await options.store.getRevisionMetadata(planned.id, planned.account_id);
137
+ if (!revision) {
138
+ missing.push(planned.id);
139
+ continue;
140
+ }
141
+ const periodDate = sourceLocalDate(
142
+ revision.wireRevision.period?.start,
143
+ revision.wireRevision.period?.source_timezone
144
+ );
145
+ const base = {
146
+ reporting_revision_id: revision.reporting_revision_id,
147
+ reporting_obligation_id: revision.reporting_obligation_id,
148
+ account_id: planned.account_id,
149
+ revision_number: revision.revisionNumber,
150
+ finality: revision.finality,
151
+ period_date: periodDate
152
+ };
153
+ for (let offset = 0; offset < revision.binding.rowCount; ) {
154
+ const page = await options.store.readRevisionRows({
155
+ reporting_revision_id: revision.reporting_revision_id,
156
+ account_id: planned.account_id,
157
+ offset,
158
+ limit: 5e3,
159
+ ...signal ? { signal } : {}
160
+ });
161
+ if (!page || page.rows.length === 0) throw new Error("Reporting revision rows ended early");
162
+ page.rows.forEach((row, index) => {
163
+ const ordinal = offset + index;
164
+ const mapped = options.mapRow ? options.mapRow(row, { revision, ordinal }) : { row };
165
+ rowLines.push(JSON.stringify({ ...mapped, ...base, ordinal }));
166
+ });
167
+ offset += page.rows.length;
168
+ }
169
+ revisionLines.push(
170
+ JSON.stringify({
171
+ ...base,
172
+ supersedes_reporting_revision_id: revision.supersedes_reporting_revision_id ?? null,
173
+ period_start: revision.wireRevision.period?.start ?? null,
174
+ period_end: revision.wireRevision.period?.end ?? null,
175
+ source_timezone: revision.wireRevision.period?.source_timezone ?? null,
176
+ observed_at: revision.observedAt,
177
+ data_through: revision.dataThrough,
178
+ revision_content_sha256: revision.binding.sha256,
179
+ row_count: revision.binding.rowCount,
180
+ control_totals: revision.wireRevision.control_totals ?? []
181
+ })
182
+ );
183
+ }
184
+ const baseJobId = reportingWarehouseJobIdV1(options.name, plan.from, plan.to);
185
+ const suffix = (attempt) => attempt ? `_a${attempt}` : "";
186
+ const steps = [
187
+ { table: options.rowsTableId, kind: "rows", lines: rowLines, attempt: plan.rowsAttempt },
188
+ { table: options.revisionsTableId, kind: "revs", lines: revisionLines, attempt: plan.revisionsAttempt }
189
+ ];
190
+ for (const step of steps) {
191
+ if (!step.lines.length) continue;
192
+ try {
193
+ await loadOnce(step.table, `${baseJobId}_${step.kind}${suffix(step.attempt)}`, [...step.lines], signal);
194
+ } catch (error) {
195
+ if (isFinishedWithError(error)) {
196
+ const column = step.kind === "rows" ? "planned_rows_attempt" : "planned_revisions_attempt";
197
+ await options.db.query(
198
+ `UPDATE adcp_reporting_warehouse_sinks SET ${column} = ${column} + 1,
199
+ updated_at = clock_timestamp() WHERE sink_name = $1`,
200
+ [options.name]
201
+ );
202
+ }
203
+ throw error;
204
+ }
205
+ }
206
+ await commit(plan.to);
207
+ return { idle: false, revisions: revisionLines.length, rows: rowLines.length, missing };
208
+ }
209
+ };
210
+ async function commit(cursor) {
211
+ await options.db.query(
212
+ `UPDATE adcp_reporting_warehouse_sinks
213
+ SET committed_cursor = $2, planned_from_cursor = NULL, planned_to_cursor = NULL,
214
+ planned_revisions = NULL, planned_rows_attempt = 0, planned_revisions_attempt = 0,
215
+ updated_at = clock_timestamp()
216
+ WHERE sink_name = $1`,
217
+ [options.name, cursor]
218
+ );
219
+ await options.store.saveFeedConsumerCursor(options.name, cursor);
220
+ }
221
+ }
222
+ function reportingWarehouseJobIdV1(name, from, to) {
223
+ return `adcp_rr_${createHash("sha256").update(JSON.stringify([name, from, to])).digest("hex").slice(0, 40)}`;
224
+ }
225
+ function qualified(tables, table) {
226
+ return `\`${tables.projectId ? `${tables.projectId}.` : ""}${tables.datasetId}.${table}\``;
227
+ }
228
+ function reportingWarehouseDefaultTablesSqlV1(tables) {
229
+ return `
230
+ CREATE TABLE IF NOT EXISTS ${qualified(tables, tables.rowsTableId)} (
231
+ reporting_revision_id STRING NOT NULL,
232
+ reporting_obligation_id STRING NOT NULL,
233
+ account_id STRING NOT NULL,
234
+ revision_number INT64 NOT NULL,
235
+ finality STRING NOT NULL,
236
+ period_date DATE,
237
+ ordinal INT64 NOT NULL,
238
+ row JSON
239
+ )
240
+ PARTITION BY period_date
241
+ CLUSTER BY account_id, reporting_revision_id;
242
+
243
+ CREATE TABLE IF NOT EXISTS ${qualified(tables, tables.revisionsTableId)} (
244
+ reporting_revision_id STRING NOT NULL,
245
+ reporting_obligation_id STRING NOT NULL,
246
+ account_id STRING NOT NULL,
247
+ revision_number INT64 NOT NULL,
248
+ finality STRING NOT NULL,
249
+ period_date DATE,
250
+ supersedes_reporting_revision_id STRING,
251
+ period_start TIMESTAMP,
252
+ period_end TIMESTAMP,
253
+ source_timezone STRING,
254
+ observed_at TIMESTAMP,
255
+ data_through TIMESTAMP,
256
+ revision_content_sha256 STRING NOT NULL,
257
+ row_count INT64 NOT NULL,
258
+ control_totals JSON
259
+ )
260
+ PARTITION BY period_date
261
+ CLUSTER BY account_id, reporting_obligation_id;`.trim();
262
+ }
263
+ function reportingWarehouseCurrentRowsViewSqlV1(tables) {
264
+ const view = tables.viewId ?? "current_rows";
265
+ if (!DATASET_OR_TABLE.test(view)) throw new TypeError("BigQuery viewId is invalid");
266
+ return `
267
+ CREATE OR REPLACE VIEW ${qualified(tables, view)} AS
268
+ SELECT row_data.*
269
+ FROM ${qualified(tables, tables.rowsTableId)} AS row_data
270
+ JOIN (
271
+ SELECT reporting_obligation_id,
272
+ ARRAY_AGG(reporting_revision_id ORDER BY revision_number DESC LIMIT 1)[OFFSET(0)] AS reporting_revision_id
273
+ FROM ${qualified(tables, tables.revisionsTableId)}
274
+ GROUP BY reporting_obligation_id
275
+ ) AS current_revision
276
+ USING (reporting_obligation_id, reporting_revision_id);`.trim();
277
+ }
278
+ function sourceLocalDate(start, timeZone) {
279
+ if (!start) return null;
280
+ const instant = new Date(start);
281
+ if (Number.isNaN(instant.getTime())) return null;
282
+ try {
283
+ const parts = new Intl.DateTimeFormat("en-CA", {
284
+ timeZone: timeZone ?? "UTC",
285
+ year: "numeric",
286
+ month: "2-digit",
287
+ day: "2-digit"
288
+ }).format(instant);
289
+ return parts;
290
+ } catch {
291
+ return instant.toISOString().slice(0, 10);
292
+ }
293
+ }
294
+ async function withinDeadline(operation, milliseconds, signal) {
295
+ signal?.throwIfAborted();
296
+ let timer;
297
+ let onAbort;
298
+ try {
299
+ return await Promise.race([
300
+ operation(),
301
+ new Promise((_, reject) => {
302
+ timer = setTimeout(
303
+ () => reject(new Error("BigQuery call exceeded its deadline; the batch will be retried")),
304
+ milliseconds
305
+ );
306
+ if (signal) {
307
+ onAbort = () => reject(signal.reason ?? new Error("aborted"));
308
+ signal.addEventListener("abort", onAbort, { once: true });
309
+ }
310
+ })
311
+ ]);
312
+ } finally {
313
+ if (timer) clearTimeout(timer);
314
+ if (signal && onAbort) signal.removeEventListener("abort", onAbort);
315
+ }
316
+ }
317
+ function isAlreadyExists(error) {
318
+ const record = error;
319
+ return record?.code === 409 || !!record?.errors?.some((entry) => entry.reason === "duplicate");
320
+ }
321
+ function isFinishedWithError(error) {
322
+ return error?.adcpJobFailed === true;
323
+ }
324
+ function assertJobSucceeded(metadata) {
325
+ const record = Array.isArray(metadata) ? metadata[0] : metadata;
326
+ if (record?.status?.state !== "DONE") {
327
+ throw new Error("A BigQuery load job for this batch is still running; retry later");
328
+ }
329
+ if (record.status.errorResult) {
330
+ const failure = new Error("A BigQuery load job for this batch failed; it will be retried as a new attempt");
331
+ Object.defineProperty(failure, "adcpJobFailed", { value: true });
332
+ throw failure;
333
+ }
334
+ }
335
+ export {
336
+ REPORTING_WAREHOUSE_SINK_MIGRATION,
337
+ createBigQueryReportingWarehouseSinkV1,
338
+ reportingWarehouseCurrentRowsViewSqlV1,
339
+ reportingWarehouseDefaultTablesSqlV1,
340
+ reportingWarehouseJobIdV1
341
+ };
@@ -5,3 +5,5 @@ export { createGcsReportingResourceReaderV1, createGcsReportingReferenceResolver
5
5
  export type { GcsReportingReadScopeV1, GcsReportingReadAuthorizationV1, GcsReportingReaderOptionsV1 } from './reader.mjs';
6
6
  export { createGcsReportingManagedDeliveryAdapterV1 } from './adapter.mjs';
7
7
  export type { CreateGcsReportingManagedDeliveryAdapterOptionsV1 } from './adapter.mjs';
8
+ export { createGcsReportingRowObjectProviderV1 } from './row-provider.mjs';
9
+ export type { CreateGcsReportingRowObjectProviderOptionsV1 } from './row-provider.mjs';
@@ -5,3 +5,5 @@ export { createGcsReportingResourceReaderV1, createGcsReportingReferenceResolver
5
5
  export type { GcsReportingReadScopeV1, GcsReportingReadAuthorizationV1, GcsReportingReaderOptionsV1 } from './reader';
6
6
  export { createGcsReportingManagedDeliveryAdapterV1 } from './adapter';
7
7
  export type { CreateGcsReportingManagedDeliveryAdapterOptionsV1 } from './adapter';
8
+ export { createGcsReportingRowObjectProviderV1 } from './row-provider';
9
+ export type { CreateGcsReportingRowObjectProviderOptionsV1 } from './row-provider';
@@ -23,6 +23,7 @@ __export(gcs_exports, {
23
23
  createGcsReportingObjectFenceV1: () => import_fence.createGcsReportingObjectFenceV1,
24
24
  createGcsReportingReferenceResolverV1: () => import_reader.createGcsReportingReferenceResolverV1,
25
25
  createGcsReportingResourceReaderV1: () => import_reader.createGcsReportingResourceReaderV1,
26
+ createGcsReportingRowObjectProviderV1: () => import_row_provider.createGcsReportingRowObjectProviderV1,
26
27
  isReportingGcsFenceError: () => import_errors.isReportingGcsFenceError
27
28
  });
28
29
  module.exports = __toCommonJS(gcs_exports);
@@ -30,6 +31,7 @@ var import_fence = require('./fence.js');
30
31
  var import_errors = require('./errors.js');
31
32
  var import_reader = require('./reader.js');
32
33
  var import_adapter = require('./adapter.js');
34
+ var import_row_provider = require('./row-provider.js');
33
35
  // Annotate the CommonJS export names for ESM import in node:
34
36
  0 && (module.exports = {
35
37
  ReportingGcsFenceError,
@@ -37,5 +39,6 @@ var import_adapter = require('./adapter.js');
37
39
  createGcsReportingObjectFenceV1,
38
40
  createGcsReportingReferenceResolverV1,
39
41
  createGcsReportingResourceReaderV1,
42
+ createGcsReportingRowObjectProviderV1,
40
43
  isReportingGcsFenceError
41
44
  });
@@ -2,11 +2,13 @@ import { createGcsReportingObjectFenceV1 } from "./fence.mjs";
2
2
  import { ReportingGcsFenceError, isReportingGcsFenceError } from "./errors.mjs";
3
3
  import { createGcsReportingResourceReaderV1, createGcsReportingReferenceResolverV1 } from "./reader.mjs";
4
4
  import { createGcsReportingManagedDeliveryAdapterV1 } from "./adapter.mjs";
5
+ import { createGcsReportingRowObjectProviderV1 } from "./row-provider.mjs";
5
6
  export {
6
7
  ReportingGcsFenceError,
7
8
  createGcsReportingManagedDeliveryAdapterV1,
8
9
  createGcsReportingObjectFenceV1,
9
10
  createGcsReportingReferenceResolverV1,
10
11
  createGcsReportingResourceReaderV1,
12
+ createGcsReportingRowObjectProviderV1,
11
13
  isReportingGcsFenceError
12
14
  };
@@ -0,0 +1,32 @@
1
+ import type { BucketMetadata, Storage } from '@google-cloud/storage';
2
+ import type { ReportingRowObjectProviderV1 } from '../ledger/row-storage-object.mjs';
3
+ export interface CreateGcsReportingRowObjectProviderOptionsV1 {
4
+ /** Official client used by bindings without a `credentialRef`. */
5
+ storage?: Storage;
6
+ /**
7
+ * Official clients keyed by `credentialRef`. This map is the closed set of
8
+ * references a binding may name; an unknown reference fails `UNSAFE_BINDING`.
9
+ */
10
+ clients?: Readonly<Record<string, Storage>>;
11
+ }
12
+ /**
13
+ * Row-binding bucket policy (shared persistence spec §3.4). Unlike the Managed
14
+ * Delivery probe, versioning, soft delete and retention are allowed as
15
+ * backstops. Refused: buckets without uniform bucket-level access or enforced
16
+ * public access prevention, and any lifecycle `Delete` rule that can match a
17
+ * live object under the binding prefix (no prefix filter, or a prefix that
18
+ * overlaps it). Rules limited to noncurrent versions are allowed.
19
+ */
20
+ export declare function assertGcsReportingRowBucketPolicyV1(metadata: BucketMetadata, prefix: string): void;
21
+ /**
22
+ * Google Cloud Storage row-object provider (`@adcp/sdk/reporting/gcs`).
23
+ *
24
+ * Inject official `Storage` clients with host-owned, keyless credentials.
25
+ * Bindings use `location: { bucket }`; `credentialRef` selects an injected
26
+ * client. Writes are single-request uploads with `ifGenerationMatch: 0`, so an
27
+ * existing object is never replaced; the recorded native version is the
28
+ * object generation, and reads and deletes pin it. Objects are stored without
29
+ * `Content-Encoding`, and reads request raw stored bytes, so GCS never
30
+ * transcodes them.
31
+ */
32
+ export declare function createGcsReportingRowObjectProviderV1(options: CreateGcsReportingRowObjectProviderOptionsV1): ReportingRowObjectProviderV1;
@@ -0,0 +1,32 @@
1
+ import type { BucketMetadata, Storage } from '@google-cloud/storage';
2
+ import type { ReportingRowObjectProviderV1 } from '../ledger/row-storage-object';
3
+ export interface CreateGcsReportingRowObjectProviderOptionsV1 {
4
+ /** Official client used by bindings without a `credentialRef`. */
5
+ storage?: Storage;
6
+ /**
7
+ * Official clients keyed by `credentialRef`. This map is the closed set of
8
+ * references a binding may name; an unknown reference fails `UNSAFE_BINDING`.
9
+ */
10
+ clients?: Readonly<Record<string, Storage>>;
11
+ }
12
+ /**
13
+ * Row-binding bucket policy (shared persistence spec §3.4). Unlike the Managed
14
+ * Delivery probe, versioning, soft delete and retention are allowed as
15
+ * backstops. Refused: buckets without uniform bucket-level access or enforced
16
+ * public access prevention, and any lifecycle `Delete` rule that can match a
17
+ * live object under the binding prefix (no prefix filter, or a prefix that
18
+ * overlaps it). Rules limited to noncurrent versions are allowed.
19
+ */
20
+ export declare function assertGcsReportingRowBucketPolicyV1(metadata: BucketMetadata, prefix: string): void;
21
+ /**
22
+ * Google Cloud Storage row-object provider (`@adcp/sdk/reporting/gcs`).
23
+ *
24
+ * Inject official `Storage` clients with host-owned, keyless credentials.
25
+ * Bindings use `location: { bucket }`; `credentialRef` selects an injected
26
+ * client. Writes are single-request uploads with `ifGenerationMatch: 0`, so an
27
+ * existing object is never replaced; the recorded native version is the
28
+ * object generation, and reads and deletes pin it. Objects are stored without
29
+ * `Content-Encoding`, and reads request raw stored bytes, so GCS never
30
+ * transcodes them.
31
+ */
32
+ export declare function createGcsReportingRowObjectProviderV1(options: CreateGcsReportingRowObjectProviderOptionsV1): ReportingRowObjectProviderV1;
@@ -0,0 +1,205 @@
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_provider_exports = {};
20
+ __export(row_provider_exports, {
21
+ assertGcsReportingRowBucketPolicyV1: () => assertGcsReportingRowBucketPolicyV1,
22
+ createGcsReportingRowObjectProviderV1: () => createGcsReportingRowObjectProviderV1
23
+ });
24
+ module.exports = __toCommonJS(row_provider_exports);
25
+ var import_node_crypto = require("node:crypto");
26
+ var import_row_storage_errors = require('../ledger/row-storage-errors.js');
27
+ var import_row_storage_provider_io = require('../ledger/row-storage-provider-io.js');
28
+ const BUCKET = /^[a-z0-9][a-z0-9._-]{1,220}[a-z0-9]$/;
29
+ const GENERATION = /^[1-9][0-9]{0,15}$/;
30
+ const NONCURRENT_ONLY_CONDITIONS = ["numNewerVersions", "daysSinceNoncurrentTime", "noncurrentTimeBefore"];
31
+ function status(error) {
32
+ if (!error || typeof error !== "object" || !("code" in error)) return void 0;
33
+ const code = Number(error.code);
34
+ return Number.isInteger(code) ? code : void 0;
35
+ }
36
+ function generationOf(metadata) {
37
+ const generation = metadata?.generation === void 0 ? "" : String(metadata.generation);
38
+ if (!GENERATION.test(generation) || !Number.isSafeInteger(Number(generation))) {
39
+ throw new import_row_storage_errors.ReportingRowStoreError("PROVIDER_UNAVAILABLE", "object generation is missing");
40
+ }
41
+ return generation;
42
+ }
43
+ function pinnedGeneration(nativeVersion) {
44
+ return typeof nativeVersion === "string" && GENERATION.test(nativeVersion) && Number.isSafeInteger(Number(nativeVersion)) ? nativeVersion : null;
45
+ }
46
+ function assertGcsReportingRowBucketPolicyV1(metadata, prefix) {
47
+ if (!metadata || typeof metadata !== "object") {
48
+ throw new import_row_storage_errors.ReportingRowStoreError("UNSAFE_BINDING", "bucket policy is unavailable");
49
+ }
50
+ if (metadata.iamConfiguration?.uniformBucketLevelAccess?.enabled !== true) {
51
+ throw new import_row_storage_errors.ReportingRowStoreError("UNSAFE_BINDING", "bucket must enable uniform bucket-level access");
52
+ }
53
+ if (metadata.iamConfiguration?.publicAccessPrevention !== "enforced") {
54
+ throw new import_row_storage_errors.ReportingRowStoreError("UNSAFE_BINDING", "bucket must enforce public access prevention");
55
+ }
56
+ for (const rule of metadata.lifecycle?.rule ?? []) {
57
+ if (rule?.action?.type !== "Delete") continue;
58
+ const condition = rule.condition ?? {};
59
+ if (condition.isLive === false) continue;
60
+ if (NONCURRENT_ONLY_CONDITIONS.some((name) => condition[name] !== void 0 && condition[name] !== null)) continue;
61
+ const prefixes = condition.matchesPrefix ?? [];
62
+ if (prefixes.length === 0 || prefixes.some((filter) => (0, import_row_storage_provider_io.reportingRowPrefixFilterOverlaps)(filter, prefix))) {
63
+ throw new import_row_storage_errors.ReportingRowStoreError("UNSAFE_BINDING", "a bucket lifecycle rule can delete live row objects");
64
+ }
65
+ }
66
+ }
67
+ function createGcsReportingRowObjectProviderV1(options) {
68
+ if (!options || typeof options !== "object") {
69
+ throw new import_row_storage_errors.ReportingRowStoreError("INVALID_INPUT", "GCS row provider options are required");
70
+ }
71
+ const clients = (0, import_row_storage_provider_io.reportingRowClientMap)(options.clients);
72
+ const isStorage = (value) => !!value && typeof value === "object" && typeof value.bucket === "function";
73
+ if (options.storage !== void 0 && !isStorage(options.storage) || [...clients.values()].some((client) => !isStorage(client)) || options.storage === void 0 && clients.size === 0) {
74
+ throw new import_row_storage_errors.ReportingRowStoreError("INVALID_INPUT", "inject an official Storage client as storage or clients");
75
+ }
76
+ const validateLocation = (location) => {
77
+ (0, import_row_storage_provider_io.assertReportingRowLocationKeys)(location, ["bucket"]);
78
+ if (!BUCKET.test(location.bucket) || location.bucket.includes("..")) {
79
+ throw new import_row_storage_errors.ReportingRowStoreError("INVALID_INPUT", "GCS bucket name is invalid");
80
+ }
81
+ };
82
+ const bucketFor = (input) => {
83
+ validateLocation(input.location);
84
+ return (0, import_row_storage_provider_io.selectReportingRowClient)(options.storage, clients, input.credentialRef).bucket(input.location.bucket);
85
+ };
86
+ const assertKey = (key) => {
87
+ if (typeof key !== "string" || key.length === 0 || Buffer.byteLength(key) > 1024) {
88
+ throw new import_row_storage_errors.ReportingRowStoreError("INVALID_INPUT", "object key is invalid");
89
+ }
90
+ };
91
+ return {
92
+ name: "gcs",
93
+ rangedReads: true,
94
+ validateLocation,
95
+ probe(input, context) {
96
+ return (0, import_row_storage_provider_io.runReportingRowProviderOperation)(context.signal, async () => {
97
+ const bucket = bucketFor(input);
98
+ let metadata;
99
+ try {
100
+ [metadata] = await bucket.getMetadata();
101
+ } catch (error) {
102
+ const code = status(error);
103
+ if (code === 404) throw new import_row_storage_errors.ReportingRowStoreError("UNSAFE_BINDING", "bucket does not exist");
104
+ if (code === 401 || code === 403) {
105
+ throw new import_row_storage_errors.ReportingRowStoreError("UNSAFE_BINDING", "bucket policy cannot be read with this client");
106
+ }
107
+ throw error;
108
+ }
109
+ assertGcsReportingRowBucketPolicyV1(metadata, input.prefix);
110
+ });
111
+ },
112
+ putIfAbsent(input, context) {
113
+ return (0, import_row_storage_provider_io.runReportingRowProviderOperation)(context.signal, async () => {
114
+ const bucket = bucketFor(input);
115
+ assertKey(input.key);
116
+ const file = bucket.file(input.key);
117
+ try {
118
+ const bytes = Buffer.from(input.bytes);
119
+ await file.save(bytes, {
120
+ resumable: false,
121
+ gzip: false,
122
+ // GCS verifies the declared MD5 before creating the object, so a corrupted upload
123
+ // is refused server-side. Client-side validation is off because on mismatch the
124
+ // client deletes the key without a generation precondition.
125
+ validation: false,
126
+ contentType: input.contentType,
127
+ metadata: {
128
+ contentType: input.contentType,
129
+ md5Hash: (0, import_node_crypto.createHash)("md5").update(bytes).digest("base64"),
130
+ cacheControl: "no-store",
131
+ metadata: { ...input.metadata }
132
+ },
133
+ preconditionOpts: { ifGenerationMatch: 0 }
134
+ });
135
+ } catch (error) {
136
+ if (status(error) !== 412) throw error;
137
+ context.signal.throwIfAborted();
138
+ const [existing] = await bucket.file(input.key).getMetadata();
139
+ return { created: false, nativeVersion: generationOf(existing) };
140
+ }
141
+ if (file.metadata?.generation !== void 0) {
142
+ return { created: true, nativeVersion: generationOf(file.metadata) };
143
+ }
144
+ const [created] = await file.getMetadata();
145
+ return { created: true, nativeVersion: generationOf(created) };
146
+ });
147
+ },
148
+ get(input, context) {
149
+ return (0, import_row_storage_provider_io.runReportingRowProviderOperation)(context.signal, async () => {
150
+ const bucket = bucketFor(input);
151
+ assertKey(input.key);
152
+ (0, import_row_storage_provider_io.assertReportingRowReadBounds)(input);
153
+ const generation = pinnedGeneration(input.nativeVersion);
154
+ if (generation === null) return null;
155
+ const file = bucket.file(input.key, { generation });
156
+ try {
157
+ if (input.range?.length === 0) {
158
+ const [metadata] = await file.getMetadata();
159
+ if (input.range.offset > Number(metadata.size)) {
160
+ throw new import_row_storage_errors.ReportingRowStoreError("ROWS_INTEGRITY_FAILED", "requested range exceeds the stored object");
161
+ }
162
+ return Buffer.alloc(0);
163
+ }
164
+ const stream = file.createReadStream({
165
+ // The client always sends `Accept-Encoding: gzip`; with `decompress: false`
166
+ // neither GCS nor the client transcodes, so these are the stored bytes.
167
+ decompress: false,
168
+ // Digests recorded in PostgreSQL verify every byte returned.
169
+ validation: false,
170
+ ...input.range ? { start: input.range.offset, end: input.range.offset + input.range.length - 1 } : {}
171
+ });
172
+ return await (0, import_row_storage_provider_io.readReportingRowStream)(stream, input.maxBytes, context.signal);
173
+ } catch (error) {
174
+ const code = status(error);
175
+ if (code === 404) return null;
176
+ if (code === 416) {
177
+ throw new import_row_storage_errors.ReportingRowStoreError("ROWS_INTEGRITY_FAILED", "requested range exceeds the stored object");
178
+ }
179
+ throw error;
180
+ }
181
+ });
182
+ },
183
+ delete(input, context) {
184
+ return (0, import_row_storage_provider_io.runReportingRowProviderOperation)(context.signal, async () => {
185
+ const bucket = bucketFor(input);
186
+ assertKey(input.key);
187
+ const generation = pinnedGeneration(input.nativeVersion);
188
+ if (generation === null) return "absent";
189
+ try {
190
+ await bucket.file(input.key, { generation }).delete();
191
+ return "deleted";
192
+ } catch (error) {
193
+ const code = status(error);
194
+ if (code === 404 || code === 412) return "absent";
195
+ throw error;
196
+ }
197
+ });
198
+ }
199
+ };
200
+ }
201
+ // Annotate the CommonJS export names for ESM import in node:
202
+ 0 && (module.exports = {
203
+ assertGcsReportingRowBucketPolicyV1,
204
+ createGcsReportingRowObjectProviderV1
205
+ });