@adcp/sdk 15.5.0 → 15.7.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.
- package/README.md +15 -0
- package/dist/lib/auth/oauth/index.d.mts +1 -0
- package/dist/lib/auth/oauth/index.d.ts +1 -0
- package/dist/lib/auth/oauth/index.js +5 -0
- package/dist/lib/auth/oauth/index.mjs +3 -0
- package/dist/lib/auth/oauth/pending-web-flow.d.mts +16 -0
- package/dist/lib/auth/oauth/pending-web-flow.d.ts +16 -0
- package/dist/lib/auth/oauth/pending-web-flow.js +66 -0
- package/dist/lib/auth/oauth/pending-web-flow.mjs +41 -0
- package/dist/lib/auth/oauth/web-flow.js +3 -3
- package/dist/lib/auth/oauth/web-flow.mjs +3 -3
- package/dist/lib/registry/types.generated.d.mts +63 -1
- package/dist/lib/registry/types.generated.d.ts +63 -1
- package/dist/lib/reporting/azure/index.d.mts +26 -0
- package/dist/lib/reporting/azure/index.d.ts +26 -0
- package/dist/lib/reporting/azure/index.js +247 -0
- package/dist/lib/reporting/azure/index.mjs +230 -0
- package/dist/lib/reporting/bigquery/index.d.mts +127 -0
- package/dist/lib/reporting/bigquery/index.d.ts +127 -0
- package/dist/lib/reporting/bigquery/index.js +379 -0
- package/dist/lib/reporting/bigquery/index.mjs +341 -0
- package/dist/lib/reporting/ledger/handler.js +20 -8
- package/dist/lib/reporting/ledger/handler.mjs +20 -8
- package/dist/lib/reporting/ledger/postgres.d.mts +21 -0
- package/dist/lib/reporting/ledger/postgres.d.ts +21 -0
- package/dist/lib/reporting/ledger/postgres.js +35 -2
- package/dist/lib/reporting/ledger/postgres.mjs +35 -2
- package/dist/lib/reporting/ledger/row-storage-object.js +31 -8
- package/dist/lib/reporting/ledger/row-storage-object.mjs +32 -9
- package/dist/lib/reporting/ledger/row-storage-provider-io.d.mts +0 -7
- package/dist/lib/reporting/ledger/row-storage-provider-io.d.ts +0 -7
- package/dist/lib/reporting/ledger/row-storage-provider-io.js +0 -10
- package/dist/lib/reporting/ledger/row-storage-provider-io.mjs +0 -8
- package/dist/lib/reporting/s3/index.d.mts +29 -0
- package/dist/lib/reporting/s3/index.d.ts +29 -0
- package/dist/lib/reporting/s3/index.js +324 -0
- package/dist/lib/reporting/s3/index.mjs +317 -0
- package/dist/lib/reporting/service/index.d.mts +39 -22
- package/dist/lib/reporting/service/index.d.ts +39 -22
- package/dist/lib/reporting/service/index.js +75 -64
- package/dist/lib/reporting/service/index.mjs +79 -63
- package/dist/lib/reporting/service/maintenance.d.mts +91 -0
- package/dist/lib/reporting/service/maintenance.d.ts +91 -0
- package/dist/lib/reporting/service/maintenance.js +140 -0
- package/dist/lib/reporting/service/maintenance.mjs +114 -0
- package/dist/lib/schemas/browser.d.mts +14 -0
- package/dist/lib/schemas/browser.d.ts +14 -0
- package/dist/lib/schemas/browser.js +48 -0
- package/dist/lib/schemas/browser.mjs +20 -0
- package/dist/lib/schemas/index.d.mts +2 -120
- package/dist/lib/schemas/index.d.ts +2 -120
- package/dist/lib/schemas/index.js +3 -11
- package/dist/lib/schemas/index.mjs +1 -9
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/testing/index.d.mts +1 -0
- package/dist/lib/testing/index.d.ts +1 -0
- package/dist/lib/testing/index.js +3 -0
- package/dist/lib/testing/index.mjs +2 -0
- package/dist/lib/testing/pending-web-flow-store.d.mts +20 -0
- package/dist/lib/testing/pending-web-flow-store.d.ts +20 -0
- package/dist/lib/testing/pending-web-flow-store.js +132 -0
- package/dist/lib/testing/pending-web-flow-store.mjs +98 -0
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/validation/get-products-request.d.mts +122 -0
- package/dist/lib/validation/get-products-request.d.ts +122 -0
- package/dist/lib/validation/get-products-request.js +39 -0
- package/dist/lib/validation/get-products-request.mjs +14 -0
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.mjs +3 -3
- package/docs/TYPE-SUMMARY.md +1 -1
- package/docs/ZOD-SCHEMAS.md +13 -1
- package/docs/guides/OAUTH-ISSUER-BINDING.md +6 -0
- package/docs/guides/REPORTING-LEDGER.md +254 -0
- package/docs/guides/REPORTING-OPERATIONS.md +82 -1
- package/docs/guides/WEB-OAUTH.md +48 -1
- package/docs/llms.txt +1 -1
- package/docs/migration-14.x-rc-worksheet.md +6 -4
- package/package.json +64 -1
|
@@ -423,6 +423,18 @@ class PostgresReportingLedgerStore {
|
|
|
423
423
|
rowStorage;
|
|
424
424
|
rowStorageWrites;
|
|
425
425
|
changeFeed;
|
|
426
|
+
/** Whether this store writes the host change feed (`changeFeed: true`). */
|
|
427
|
+
get changeFeedEnabled() {
|
|
428
|
+
return this.changeFeed;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Delete expired cursor snapshots and changes checkpoints, a bounded batch
|
|
432
|
+
* at a time. The store-bound form of `sweepExpiredReportingLedgerState`, for
|
|
433
|
+
* hosts that hold the store but not the pool.
|
|
434
|
+
*/
|
|
435
|
+
async sweepExpiredState(limit = 1e3) {
|
|
436
|
+
return sweepExpiredReportingLedgerState(this.pool, limit);
|
|
437
|
+
}
|
|
426
438
|
/** Append one host change-feed row inside the caller's account-locked transaction. */
|
|
427
439
|
async appendChange(client, kind, accountId, recordId, obligationId) {
|
|
428
440
|
if (!this.changeFeed) return;
|
|
@@ -1288,6 +1300,15 @@ ${managedDueArm} )
|
|
|
1288
1300
|
*
|
|
1289
1301
|
* Planners skip retired periods, and `ledger_retained_from` advances past
|
|
1290
1302
|
* them because their obligations no longer exist.
|
|
1303
|
+
*
|
|
1304
|
+
* When the change-feed tables exist, a period that is not yet retiring is
|
|
1305
|
+
* also held while any live registered feed consumer (updated within
|
|
1306
|
+
* `maxFeedHoldDays`, default 7) has not passed that period's change rows in
|
|
1307
|
+
* the consumer's own feed order, the same rule `pruneChanges` applies. A
|
|
1308
|
+
* held period is simply not a candidate this pass. A consumer that stays
|
|
1309
|
+
* silent past `maxFeedHoldDays` stops holding, so retention is never
|
|
1310
|
+
* blocked forever; it then falls behind retention and a sink sees the
|
|
1311
|
+
* retired revisions as missing.
|
|
1291
1312
|
*/
|
|
1292
1313
|
async retireExpiredPeriods(input) {
|
|
1293
1314
|
if (!Number.isSafeInteger(input.statusRetentionDays) || input.statusRetentionDays < 1) {
|
|
@@ -1299,8 +1320,18 @@ ${managedDueArm} )
|
|
|
1299
1320
|
}
|
|
1300
1321
|
const limit = input.limit ?? 100;
|
|
1301
1322
|
positiveInteger(limit, "limit");
|
|
1323
|
+
const holdDays = input.maxFeedHoldDays ?? 7;
|
|
1324
|
+
positiveInteger(holdDays, "maxFeedHoldDays");
|
|
1302
1325
|
const tables = await this.optionalTables();
|
|
1303
1326
|
const materializationHold = tables.has("adcp_reporting_materializations") ? `AND NOT EXISTS (SELECT 1 FROM adcp_reporting_materializations hold WHERE hold.obligation_id = obligation.obligation_id)` : "";
|
|
1327
|
+
const feedHold = tables.has("adcp_reporting_changes") && tables.has("adcp_reporting_feed_consumers") ? `AND NOT EXISTS (
|
|
1328
|
+
SELECT 1 FROM adcp_reporting_changes change
|
|
1329
|
+
JOIN adcp_reporting_feed_consumers consumer
|
|
1330
|
+
ON consumer.updated_at > clock_timestamp() - ($4::integer * INTERVAL '1 day')
|
|
1331
|
+
WHERE change.obligation_id = obligation.obligation_id
|
|
1332
|
+
AND ((consumer.account_id IS NULL
|
|
1333
|
+
AND (change.xid, change.seq) > (consumer.cursor_xid, consumer.cursor_seq))
|
|
1334
|
+
OR (consumer.account_id = change.account_id AND change.seq > consumer.cursor_seq)))` : "";
|
|
1304
1335
|
const candidates = await this.query(
|
|
1305
1336
|
`SELECT obligation_id, account_id FROM (
|
|
1306
1337
|
SELECT tombstone.obligation_id, tombstone.account_id, 0 AS resume, tombstone.period_end
|
|
@@ -1321,10 +1352,11 @@ ${managedDueArm} )
|
|
|
1321
1352
|
AND NOT EXISTS (SELECT 1 FROM adcp_reporting_obligation_tombstones tombstone
|
|
1322
1353
|
WHERE tombstone.obligation_id = obligation.obligation_id)
|
|
1323
1354
|
${materializationHold}
|
|
1355
|
+
${feedHold}
|
|
1324
1356
|
) candidate
|
|
1325
1357
|
ORDER BY resume, period_end, obligation_id
|
|
1326
1358
|
LIMIT $1`,
|
|
1327
|
-
[limit, input.account_id ?? null, recordDays]
|
|
1359
|
+
feedHold ? [limit, input.account_id ?? null, recordDays, holdDays] : [limit, input.account_id ?? null, recordDays]
|
|
1328
1360
|
);
|
|
1329
1361
|
let retired = 0;
|
|
1330
1362
|
const failed = [];
|
|
@@ -1496,7 +1528,8 @@ ${managedDueArm} )
|
|
|
1496
1528
|
`SELECT table_name AS name FROM information_schema.tables
|
|
1497
1529
|
WHERE table_schema = current_schema()
|
|
1498
1530
|
AND table_name IN ('adcp_reporting_row_sets', 'adcp_reporting_row_write_intents',
|
|
1499
|
-
'adcp_reporting_materializations'
|
|
1531
|
+
'adcp_reporting_materializations', 'adcp_reporting_changes',
|
|
1532
|
+
'adcp_reporting_feed_consumers')`
|
|
1500
1533
|
);
|
|
1501
1534
|
return new Set(result.rows.map((row) => row.name));
|
|
1502
1535
|
}
|
|
@@ -127,10 +127,8 @@ function createFilesystemReportingRowObjectProviderV1(options) {
|
|
|
127
127
|
}
|
|
128
128
|
return target;
|
|
129
129
|
};
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
return `${stat.ino}:${stat.mtimeMs}:${stat.size}`;
|
|
133
|
-
};
|
|
130
|
+
const versionOf = (stat) => `${stat.ino}:${stat.birthtimeNs}:${stat.mtimeNs}:${stat.size}`;
|
|
131
|
+
const version = async (target) => versionOf(await import_fs.promises.stat(target, { bigint: true }));
|
|
134
132
|
return {
|
|
135
133
|
name: "filesystem",
|
|
136
134
|
rangedReads: true,
|
|
@@ -153,6 +151,8 @@ function createFilesystemReportingRowObjectProviderV1(options) {
|
|
|
153
151
|
const handle = await import_fs.promises.open(temporary, "wx");
|
|
154
152
|
try {
|
|
155
153
|
await handle.writeFile(input.bytes);
|
|
154
|
+
const mtime = Date.now() / 1e3 + (0, import_crypto.randomInt)(0, 1e3) / 1e6;
|
|
155
|
+
await handle.utimes(mtime, mtime);
|
|
156
156
|
await handle.sync();
|
|
157
157
|
} finally {
|
|
158
158
|
await handle.close();
|
|
@@ -180,11 +180,12 @@ function createFilesystemReportingRowObjectProviderV1(options) {
|
|
|
180
180
|
throw new import_row_storage_errors.ReportingRowStoreError("PROVIDER_UNAVAILABLE", "filesystem read failed");
|
|
181
181
|
}
|
|
182
182
|
try {
|
|
183
|
-
const stat = await handle.stat();
|
|
184
|
-
if (
|
|
183
|
+
const stat = await handle.stat({ bigint: true });
|
|
184
|
+
if (versionOf(stat) !== input.nativeVersion) return null;
|
|
185
|
+
const size = Number(stat.size);
|
|
185
186
|
const offset = input.range?.offset ?? 0;
|
|
186
|
-
const length = input.range?.length ??
|
|
187
|
-
if (offset < 0 || length < 0 || offset + length >
|
|
187
|
+
const length = input.range?.length ?? size - offset;
|
|
188
|
+
if (offset < 0 || length < 0 || offset + length > size) {
|
|
188
189
|
throw new import_row_storage_errors.ReportingRowStoreError("ROWS_INTEGRITY_FAILED", "requested range exceeds the stored object");
|
|
189
190
|
}
|
|
190
191
|
if (length > input.maxBytes) {
|
|
@@ -262,6 +263,28 @@ async function runReportingRowObjectProviderConformanceV1(provider, input) {
|
|
|
262
263
|
throw new Error("repeated delete was not idempotent");
|
|
263
264
|
}
|
|
264
265
|
passed.push("exact-version-delete");
|
|
266
|
+
const recreated = await provider.putIfAbsent(
|
|
267
|
+
{ ...base, key, bytes: first, contentType: "application/x-ndjson", metadata },
|
|
268
|
+
context
|
|
269
|
+
);
|
|
270
|
+
if (!recreated.created || !recreated.nativeVersion) throw new Error("putIfAbsent did not re-create a deleted object");
|
|
271
|
+
if (recreated.nativeVersion === created.nativeVersion) {
|
|
272
|
+
throw new Error("re-creating identical bytes produced the same native version; versions must be write-unique");
|
|
273
|
+
}
|
|
274
|
+
if (await provider.delete({ ...base, key, nativeVersion: created.nativeVersion }, context) !== "absent") {
|
|
275
|
+
throw new Error("delete of a stale version removed or reported a later write");
|
|
276
|
+
}
|
|
277
|
+
const survivor = await provider.get(
|
|
278
|
+
{ ...base, key, nativeVersion: recreated.nativeVersion, maxBytes: 1024 },
|
|
279
|
+
context
|
|
280
|
+
);
|
|
281
|
+
if (!survivor || !Buffer.from(survivor).equals(first)) {
|
|
282
|
+
throw new Error("delete of a stale version removed a later write with identical bytes");
|
|
283
|
+
}
|
|
284
|
+
if (await provider.delete({ ...base, key, nativeVersion: recreated.nativeVersion }, context) !== "deleted") {
|
|
285
|
+
throw new Error("delete did not remove the re-created version");
|
|
286
|
+
}
|
|
287
|
+
passed.push("write-unique-version");
|
|
265
288
|
return passed;
|
|
266
289
|
}
|
|
267
290
|
function sha256Hex(value) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createHash, randomUUID } from "crypto";
|
|
1
|
+
import { createHash, randomInt, randomUUID } from "crypto";
|
|
2
2
|
import { promises as fs } from "fs";
|
|
3
3
|
import * as path from "path";
|
|
4
4
|
import { canonicalize } from "../../utils/jcs.mjs";
|
|
@@ -86,10 +86,8 @@ function createFilesystemReportingRowObjectProviderV1(options) {
|
|
|
86
86
|
}
|
|
87
87
|
return target;
|
|
88
88
|
};
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
return `${stat.ino}:${stat.mtimeMs}:${stat.size}`;
|
|
92
|
-
};
|
|
89
|
+
const versionOf = (stat) => `${stat.ino}:${stat.birthtimeNs}:${stat.mtimeNs}:${stat.size}`;
|
|
90
|
+
const version = async (target) => versionOf(await fs.stat(target, { bigint: true }));
|
|
93
91
|
return {
|
|
94
92
|
name: "filesystem",
|
|
95
93
|
rangedReads: true,
|
|
@@ -112,6 +110,8 @@ function createFilesystemReportingRowObjectProviderV1(options) {
|
|
|
112
110
|
const handle = await fs.open(temporary, "wx");
|
|
113
111
|
try {
|
|
114
112
|
await handle.writeFile(input.bytes);
|
|
113
|
+
const mtime = Date.now() / 1e3 + randomInt(0, 1e3) / 1e6;
|
|
114
|
+
await handle.utimes(mtime, mtime);
|
|
115
115
|
await handle.sync();
|
|
116
116
|
} finally {
|
|
117
117
|
await handle.close();
|
|
@@ -139,11 +139,12 @@ function createFilesystemReportingRowObjectProviderV1(options) {
|
|
|
139
139
|
throw new ReportingRowStoreError("PROVIDER_UNAVAILABLE", "filesystem read failed");
|
|
140
140
|
}
|
|
141
141
|
try {
|
|
142
|
-
const stat = await handle.stat();
|
|
143
|
-
if (
|
|
142
|
+
const stat = await handle.stat({ bigint: true });
|
|
143
|
+
if (versionOf(stat) !== input.nativeVersion) return null;
|
|
144
|
+
const size = Number(stat.size);
|
|
144
145
|
const offset = input.range?.offset ?? 0;
|
|
145
|
-
const length = input.range?.length ??
|
|
146
|
-
if (offset < 0 || length < 0 || offset + length >
|
|
146
|
+
const length = input.range?.length ?? size - offset;
|
|
147
|
+
if (offset < 0 || length < 0 || offset + length > size) {
|
|
147
148
|
throw new ReportingRowStoreError("ROWS_INTEGRITY_FAILED", "requested range exceeds the stored object");
|
|
148
149
|
}
|
|
149
150
|
if (length > input.maxBytes) {
|
|
@@ -221,6 +222,28 @@ async function runReportingRowObjectProviderConformanceV1(provider, input) {
|
|
|
221
222
|
throw new Error("repeated delete was not idempotent");
|
|
222
223
|
}
|
|
223
224
|
passed.push("exact-version-delete");
|
|
225
|
+
const recreated = await provider.putIfAbsent(
|
|
226
|
+
{ ...base, key, bytes: first, contentType: "application/x-ndjson", metadata },
|
|
227
|
+
context
|
|
228
|
+
);
|
|
229
|
+
if (!recreated.created || !recreated.nativeVersion) throw new Error("putIfAbsent did not re-create a deleted object");
|
|
230
|
+
if (recreated.nativeVersion === created.nativeVersion) {
|
|
231
|
+
throw new Error("re-creating identical bytes produced the same native version; versions must be write-unique");
|
|
232
|
+
}
|
|
233
|
+
if (await provider.delete({ ...base, key, nativeVersion: created.nativeVersion }, context) !== "absent") {
|
|
234
|
+
throw new Error("delete of a stale version removed or reported a later write");
|
|
235
|
+
}
|
|
236
|
+
const survivor = await provider.get(
|
|
237
|
+
{ ...base, key, nativeVersion: recreated.nativeVersion, maxBytes: 1024 },
|
|
238
|
+
context
|
|
239
|
+
);
|
|
240
|
+
if (!survivor || !Buffer.from(survivor).equals(first)) {
|
|
241
|
+
throw new Error("delete of a stale version removed a later write with identical bytes");
|
|
242
|
+
}
|
|
243
|
+
if (await provider.delete({ ...base, key, nativeVersion: recreated.nativeVersion }, context) !== "deleted") {
|
|
244
|
+
throw new Error("delete did not remove the re-created version");
|
|
245
|
+
}
|
|
246
|
+
passed.push("write-unique-version");
|
|
224
247
|
return passed;
|
|
225
248
|
}
|
|
226
249
|
function sha256Hex(value) {
|
|
@@ -42,10 +42,3 @@ export declare function assertReportingRowLocationKeys(location: Readonly<Record
|
|
|
42
42
|
* owned prefix. An absent or empty filter matches everything.
|
|
43
43
|
*/
|
|
44
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;
|
|
@@ -42,10 +42,3 @@ export declare function assertReportingRowLocationKeys(location: Readonly<Record
|
|
|
42
42
|
* owned prefix. An absent or empty filter matches everything.
|
|
43
43
|
*/
|
|
44
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;
|
|
@@ -20,11 +20,9 @@ var row_storage_provider_io_exports = {};
|
|
|
20
20
|
__export(row_storage_provider_io_exports, {
|
|
21
21
|
assertReportingRowLocationKeys: () => assertReportingRowLocationKeys,
|
|
22
22
|
assertReportingRowReadBounds: () => assertReportingRowReadBounds,
|
|
23
|
-
isReportingRowEtagVersion: () => isReportingRowEtagVersion,
|
|
24
23
|
readReportingRowStream: () => readReportingRowStream,
|
|
25
24
|
reportingRowAbortError: () => reportingRowAbortError,
|
|
26
25
|
reportingRowClientMap: () => reportingRowClientMap,
|
|
27
|
-
reportingRowEtagVersion: () => reportingRowEtagVersion,
|
|
28
26
|
reportingRowPrefixFilterOverlaps: () => reportingRowPrefixFilterOverlaps,
|
|
29
27
|
runReportingRowProviderOperation: () => runReportingRowProviderOperation,
|
|
30
28
|
selectReportingRowClient: () => selectReportingRowClient
|
|
@@ -131,21 +129,13 @@ function reportingRowPrefixFilterOverlaps(filter, bindingPrefix) {
|
|
|
131
129
|
const owned = `${bindingPrefix}/`;
|
|
132
130
|
return owned.startsWith(filter) || filter.startsWith(owned);
|
|
133
131
|
}
|
|
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
132
|
// Annotate the CommonJS export names for ESM import in node:
|
|
141
133
|
0 && (module.exports = {
|
|
142
134
|
assertReportingRowLocationKeys,
|
|
143
135
|
assertReportingRowReadBounds,
|
|
144
|
-
isReportingRowEtagVersion,
|
|
145
136
|
readReportingRowStream,
|
|
146
137
|
reportingRowAbortError,
|
|
147
138
|
reportingRowClientMap,
|
|
148
|
-
reportingRowEtagVersion,
|
|
149
139
|
reportingRowPrefixFilterOverlaps,
|
|
150
140
|
runReportingRowProviderOperation,
|
|
151
141
|
selectReportingRowClient
|
|
@@ -99,20 +99,12 @@ function reportingRowPrefixFilterOverlaps(filter, bindingPrefix) {
|
|
|
99
99
|
const owned = `${bindingPrefix}/`;
|
|
100
100
|
return owned.startsWith(filter) || filter.startsWith(owned);
|
|
101
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
102
|
export {
|
|
109
103
|
assertReportingRowLocationKeys,
|
|
110
104
|
assertReportingRowReadBounds,
|
|
111
|
-
isReportingRowEtagVersion,
|
|
112
105
|
readReportingRowStream,
|
|
113
106
|
reportingRowAbortError,
|
|
114
107
|
reportingRowClientMap,
|
|
115
|
-
reportingRowEtagVersion,
|
|
116
108
|
reportingRowPrefixFilterOverlaps,
|
|
117
109
|
runReportingRowProviderOperation,
|
|
118
110
|
selectReportingRowClient
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type S3Client } from '@aws-sdk/client-s3';
|
|
2
|
+
import type { ReportingRowObjectProviderV1 } from '../ledger/row-storage-object.mjs';
|
|
3
|
+
export interface CreateS3ReportingRowObjectProviderOptionsV1 {
|
|
4
|
+
/** Official client used by bindings without a `credentialRef`. */
|
|
5
|
+
client?: S3Client;
|
|
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
|
+
* Endpoint, region, credentials and path style all come from these clients,
|
|
10
|
+
* never from binding locations.
|
|
11
|
+
*/
|
|
12
|
+
clients?: Readonly<Record<string, S3Client>>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Amazon S3 (and S3-compatible) row-object provider (`@adcp/sdk/reporting/s3`).
|
|
16
|
+
*
|
|
17
|
+
* Inject official `S3Client`s configured by the host (region, endpoint,
|
|
18
|
+
* credentials). Bindings use `location: { bucket, region? }`; when `region` is
|
|
19
|
+
* set the probe requires the selected client to be configured for it.
|
|
20
|
+
*
|
|
21
|
+
* Writes use `PutObject` with `If-None-Match: *`. Bucket versioning must be
|
|
22
|
+
* enabled: the recorded native version is the write-unique `VersionId`, and every
|
|
23
|
+
* read and delete is pinned to it, so a delayed delete can never remove a later
|
|
24
|
+
* write that re-created identical bytes at the same key (ETags are content-derived
|
|
25
|
+
* and cannot fence that). The probe refuses unversioned or suspended buckets.
|
|
26
|
+
* Because S3-compatible stores may silently ignore `If-None-Match`, the probe
|
|
27
|
+
* also proves create-only behaviour empirically before any binding is used.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createS3ReportingRowObjectProviderV1(options: CreateS3ReportingRowObjectProviderOptionsV1): ReportingRowObjectProviderV1;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type S3Client } from '@aws-sdk/client-s3';
|
|
2
|
+
import type { ReportingRowObjectProviderV1 } from '../ledger/row-storage-object';
|
|
3
|
+
export interface CreateS3ReportingRowObjectProviderOptionsV1 {
|
|
4
|
+
/** Official client used by bindings without a `credentialRef`. */
|
|
5
|
+
client?: S3Client;
|
|
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
|
+
* Endpoint, region, credentials and path style all come from these clients,
|
|
10
|
+
* never from binding locations.
|
|
11
|
+
*/
|
|
12
|
+
clients?: Readonly<Record<string, S3Client>>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Amazon S3 (and S3-compatible) row-object provider (`@adcp/sdk/reporting/s3`).
|
|
16
|
+
*
|
|
17
|
+
* Inject official `S3Client`s configured by the host (region, endpoint,
|
|
18
|
+
* credentials). Bindings use `location: { bucket, region? }`; when `region` is
|
|
19
|
+
* set the probe requires the selected client to be configured for it.
|
|
20
|
+
*
|
|
21
|
+
* Writes use `PutObject` with `If-None-Match: *`. Bucket versioning must be
|
|
22
|
+
* enabled: the recorded native version is the write-unique `VersionId`, and every
|
|
23
|
+
* read and delete is pinned to it, so a delayed delete can never remove a later
|
|
24
|
+
* write that re-created identical bytes at the same key (ETags are content-derived
|
|
25
|
+
* and cannot fence that). The probe refuses unversioned or suspended buckets.
|
|
26
|
+
* Because S3-compatible stores may silently ignore `If-None-Match`, the probe
|
|
27
|
+
* also proves create-only behaviour empirically before any binding is used.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createS3ReportingRowObjectProviderV1(options: CreateS3ReportingRowObjectProviderOptionsV1): ReportingRowObjectProviderV1;
|