@substrat-run/kernel 0.159.0 → 0.161.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/dist/billing-retention.d.ts +46 -0
- package/dist/billing-retention.d.ts.map +1 -0
- package/dist/billing-retention.js +78 -0
- package/dist/billing-retention.js.map +1 -0
- package/dist/byte-stream.d.ts +142 -0
- package/dist/byte-stream.d.ts.map +1 -0
- package/dist/byte-stream.js +274 -0
- package/dist/byte-stream.js.map +1 -0
- package/dist/connection-landings.d.ts +15 -3
- package/dist/connection-landings.d.ts.map +1 -1
- package/dist/connection-landings.js +10 -2
- package/dist/connection-landings.js.map +1 -1
- package/dist/deadline-fetch.d.ts +34 -0
- package/dist/deadline-fetch.d.ts.map +1 -0
- package/dist/deadline-fetch.js +231 -0
- package/dist/deadline-fetch.js.map +1 -0
- package/dist/deployment-wire-auth.d.ts +125 -0
- package/dist/deployment-wire-auth.d.ts.map +1 -0
- package/dist/deployment-wire-auth.js +161 -0
- package/dist/deployment-wire-auth.js.map +1 -0
- package/dist/entity-trash.d.ts +65 -8
- package/dist/entity-trash.d.ts.map +1 -1
- package/dist/entity-trash.js +80 -12
- package/dist/entity-trash.js.map +1 -1
- package/dist/export-break.d.ts +6 -0
- package/dist/export-break.d.ts.map +1 -1
- package/dist/export-break.js +11 -0
- package/dist/export-break.js.map +1 -1
- package/dist/index.d.ts +19 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -8
- package/dist/index.js.map +1 -1
- package/dist/meter-days.d.ts +107 -0
- package/dist/meter-days.d.ts.map +1 -0
- package/dist/meter-days.js +230 -0
- package/dist/meter-days.js.map +1 -0
- package/dist/meters.d.ts +19 -1
- package/dist/meters.d.ts.map +1 -1
- package/dist/meters.js +0 -0
- package/dist/meters.js.map +1 -1
- package/dist/ops-failure-reference.d.ts +43 -0
- package/dist/ops-failure-reference.d.ts.map +1 -0
- package/dist/ops-failure-reference.js +42 -0
- package/dist/ops-failure-reference.js.map +1 -0
- package/dist/platform-request-cap.d.ts +9 -0
- package/dist/platform-request-cap.d.ts.map +1 -0
- package/dist/platform-request-cap.js +42 -0
- package/dist/platform-request-cap.js.map +1 -0
- package/dist/platform-sweep.d.ts +187 -7
- package/dist/platform-sweep.d.ts.map +1 -1
- package/dist/platform-sweep.js +388 -45
- package/dist/platform-sweep.js.map +1 -1
- package/dist/promote-review.d.ts +6 -0
- package/dist/promote-review.d.ts.map +1 -1
- package/dist/promote-review.js +9 -0
- package/dist/promote-review.js.map +1 -1
- package/dist/request-rollup.d.ts +161 -0
- package/dist/request-rollup.d.ts.map +1 -0
- package/dist/request-rollup.js +273 -0
- package/dist/request-rollup.js.map +1 -0
- package/dist/reserved-roles.d.ts +192 -4
- package/dist/reserved-roles.d.ts.map +1 -1
- package/dist/reserved-roles.js +314 -2
- package/dist/reserved-roles.js.map +1 -1
- package/dist/schedule-state.d.ts +5 -0
- package/dist/schedule-state.d.ts.map +1 -1
- package/dist/scope-host.d.ts +317 -15
- package/dist/scope-host.d.ts.map +1 -1
- package/dist/scope-host.js +93 -2
- package/dist/scope-host.js.map +1 -1
- package/dist/scope-lifecycle.d.ts +7 -0
- package/dist/scope-lifecycle.d.ts.map +1 -1
- package/dist/scope-lifecycle.js +7 -0
- package/dist/scope-lifecycle.js.map +1 -1
- package/dist/scope-record.d.ts +16 -5
- package/dist/scope-record.d.ts.map +1 -1
- package/dist/scope-record.js +72 -7
- package/dist/scope-record.js.map +1 -1
- package/dist/spine-restore.d.ts +7 -3
- package/dist/spine-restore.d.ts.map +1 -1
- package/dist/spine-restore.js +14 -3
- package/dist/spine-restore.js.map +1 -1
- package/dist/storage-gauge.d.ts +47 -13
- package/dist/storage-gauge.d.ts.map +1 -1
- package/dist/storage-gauge.js +84 -27
- package/dist/storage-gauge.js.map +1 -1
- package/dist/subject-redaction.d.ts +9 -3
- package/dist/subject-redaction.d.ts.map +1 -1
- package/dist/subject-redaction.js +23 -12
- package/dist/subject-redaction.js.map +1 -1
- package/dist/vertical-owners.d.ts +69 -0
- package/dist/vertical-owners.d.ts.map +1 -0
- package/dist/vertical-owners.js +108 -0
- package/dist/vertical-owners.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { RedactionSql } from './subject-redaction.js';
|
|
2
|
+
/**
|
|
3
|
+
* How long the platform keeps its billing rollups (#2501): the compact per-(tenant, vertical)
|
|
4
|
+
* daily counts a later invoice, or a dispute about one, is answered from. Today that is the daily
|
|
5
|
+
* request rollup (`request-rollup.ts`) and the daily meter snapshot (`meter-days.ts`). The raw
|
|
6
|
+
* captures behind a rollup keep their own, shorter windows, and no existing retention follows
|
|
7
|
+
* this constant: the storage gauge's day rows stay `STORAGE_GAUGE_RETENTION_MONTHS` and model
|
|
8
|
+
* usage stays `MODEL_USAGE_RETENTION_DAYS`.
|
|
9
|
+
*
|
|
10
|
+
* `null` means the rollups are never pruned. That is a placeholder, not a decision: the horizon
|
|
11
|
+
* waits on the maintainer's bookkeeping, and no legal period is written here. Keeping
|
|
12
|
+
* everything is the reversible default, since a pruned row cannot be brought back while a
|
|
13
|
+
* horizon can be set later. The rollups hold counts and bytes only, never personal content.
|
|
14
|
+
*/
|
|
15
|
+
export declare const BILLING_ROLLUP_RETENTION_MONTHS: number | null;
|
|
16
|
+
/**
|
|
17
|
+
* The first day a billing retention of `months` keeps, or null when `months` is null (kept for
|
|
18
|
+
* good). A horizon is a whole number of months, at least one. Every billing rollup prunes by it.
|
|
19
|
+
*/
|
|
20
|
+
export declare function billingRollupHorizon(nowMs: number, months: number | null): string | null;
|
|
21
|
+
/**
|
|
22
|
+
* The UTC day `months` calendar months before `nowMs`. The day of the month is clamped to the
|
|
23
|
+
* target month's last day, so the 31st counts back to the 28th, 29th or 30th rather than rolling
|
|
24
|
+
* forward into the month after.
|
|
25
|
+
*/
|
|
26
|
+
export declare function utcDayMonthsBefore(nowMs: number, months: number): string;
|
|
27
|
+
/** The UTC day an instant falls on. */
|
|
28
|
+
export declare function utcDayOf(ms: number): string;
|
|
29
|
+
/** `day`, refused unless it is a UTC day that exists (`YYYY-MM-DD`). */
|
|
30
|
+
export declare function assertUtcDay(name: string, day: string): string;
|
|
31
|
+
/** Push an inclusive window of UTC days onto a `WHERE` list and its parameters, each day checked first. */
|
|
32
|
+
export declare function pushDayWindow(filter: {
|
|
33
|
+
since?: string;
|
|
34
|
+
until?: string;
|
|
35
|
+
}, where: string[], params: (string | number)[]): void;
|
|
36
|
+
/**
|
|
37
|
+
* Delete at most `limit` rows of a billing rollup's `rows` table older than a retention of
|
|
38
|
+
* `months`, oldest first, then the `records` of the days that are gone. `months` null deletes
|
|
39
|
+
* nothing. A record goes only once its day holds no row, so a day still being pruned stays marked
|
|
40
|
+
* done and is not rolled up again in between. Returns how many rows went.
|
|
41
|
+
*/
|
|
42
|
+
export declare function pruneBillingDays(sql: RedactionSql, tables: {
|
|
43
|
+
rows: string;
|
|
44
|
+
records: string;
|
|
45
|
+
}, nowMs: number, limit: number, months: number | null): number;
|
|
46
|
+
//# sourceMappingURL=billing-retention.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"billing-retention.d.ts","sourceRoot":"","sources":["../src/billing-retention.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAG3D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,+BAA+B,EAAE,MAAM,GAAG,IAAW,CAAC;AAEnE;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAMxF;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAMxE;AAED,uCAAuC;AACvC,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAG9D;AAED,2GAA2G;AAC3G,wBAAgB,aAAa,CAAC,MAAM,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,IAAI,CAS5H;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,YAAY,EACjB,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EACzC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GAAG,IAAI,GACpB,MAAM,CAWR"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { utcDay } from '@substrat-run/contracts';
|
|
2
|
+
import { assertRowLimit, boundedRetentionDelete } from './scope-host.js';
|
|
3
|
+
/**
|
|
4
|
+
* How long the platform keeps its billing rollups (#2501): the compact per-(tenant, vertical)
|
|
5
|
+
* daily counts a later invoice, or a dispute about one, is answered from. Today that is the daily
|
|
6
|
+
* request rollup (`request-rollup.ts`) and the daily meter snapshot (`meter-days.ts`). The raw
|
|
7
|
+
* captures behind a rollup keep their own, shorter windows, and no existing retention follows
|
|
8
|
+
* this constant: the storage gauge's day rows stay `STORAGE_GAUGE_RETENTION_MONTHS` and model
|
|
9
|
+
* usage stays `MODEL_USAGE_RETENTION_DAYS`.
|
|
10
|
+
*
|
|
11
|
+
* `null` means the rollups are never pruned. That is a placeholder, not a decision: the horizon
|
|
12
|
+
* waits on the maintainer's bookkeeping, and no legal period is written here. Keeping
|
|
13
|
+
* everything is the reversible default, since a pruned row cannot be brought back while a
|
|
14
|
+
* horizon can be set later. The rollups hold counts and bytes only, never personal content.
|
|
15
|
+
*/
|
|
16
|
+
export const BILLING_ROLLUP_RETENTION_MONTHS = null;
|
|
17
|
+
/**
|
|
18
|
+
* The first day a billing retention of `months` keeps, or null when `months` is null (kept for
|
|
19
|
+
* good). A horizon is a whole number of months, at least one. Every billing rollup prunes by it.
|
|
20
|
+
*/
|
|
21
|
+
export function billingRollupHorizon(nowMs, months) {
|
|
22
|
+
if (months === null)
|
|
23
|
+
return null;
|
|
24
|
+
if (!Number.isInteger(months) || months < 1) {
|
|
25
|
+
throw new Error(`a billing retention must be a whole number of months, at least one, or null, got ${String(months)}`);
|
|
26
|
+
}
|
|
27
|
+
return utcDayMonthsBefore(nowMs, months);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The UTC day `months` calendar months before `nowMs`. The day of the month is clamped to the
|
|
31
|
+
* target month's last day, so the 31st counts back to the 28th, 29th or 30th rather than rolling
|
|
32
|
+
* forward into the month after.
|
|
33
|
+
*/
|
|
34
|
+
export function utcDayMonthsBefore(nowMs, months) {
|
|
35
|
+
const now = new Date(nowMs);
|
|
36
|
+
const month = now.getUTCMonth() - months;
|
|
37
|
+
// Day 0 of the following month is the last day of the target month.
|
|
38
|
+
const lastDay = new Date(Date.UTC(now.getUTCFullYear(), month + 1, 0)).getUTCDate();
|
|
39
|
+
return new Date(Date.UTC(now.getUTCFullYear(), month, Math.min(now.getUTCDate(), lastDay))).toISOString().slice(0, 10);
|
|
40
|
+
}
|
|
41
|
+
/** The UTC day an instant falls on. */
|
|
42
|
+
export function utcDayOf(ms) {
|
|
43
|
+
return new Date(ms).toISOString().slice(0, 10);
|
|
44
|
+
}
|
|
45
|
+
/** `day`, refused unless it is a UTC day that exists (`YYYY-MM-DD`). */
|
|
46
|
+
export function assertUtcDay(name, day) {
|
|
47
|
+
if (!utcDay.safeParse(day).success)
|
|
48
|
+
throw new Error(`${name} must be a UTC day (YYYY-MM-DD), got ${JSON.stringify(day)}`);
|
|
49
|
+
return day;
|
|
50
|
+
}
|
|
51
|
+
/** Push an inclusive window of UTC days onto a `WHERE` list and its parameters, each day checked first. */
|
|
52
|
+
export function pushDayWindow(filter, where, params) {
|
|
53
|
+
if (filter.since !== undefined) {
|
|
54
|
+
where.push('day >= ?');
|
|
55
|
+
params.push(assertUtcDay('since', filter.since));
|
|
56
|
+
}
|
|
57
|
+
if (filter.until !== undefined) {
|
|
58
|
+
where.push('day <= ?');
|
|
59
|
+
params.push(assertUtcDay('until', filter.until));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Delete at most `limit` rows of a billing rollup's `rows` table older than a retention of
|
|
64
|
+
* `months`, oldest first, then the `records` of the days that are gone. `months` null deletes
|
|
65
|
+
* nothing. A record goes only once its day holds no row, so a day still being pruned stays marked
|
|
66
|
+
* done and is not rolled up again in between. Returns how many rows went.
|
|
67
|
+
*/
|
|
68
|
+
export function pruneBillingDays(sql, tables, nowMs, limit, months) {
|
|
69
|
+
const capped = assertRowLimit('limit', limit);
|
|
70
|
+
const horizon = billingRollupHorizon(nowMs, months);
|
|
71
|
+
if (horizon === null)
|
|
72
|
+
return 0;
|
|
73
|
+
const pruned = sql(boundedRetentionDelete(tables.rows, 'day'), [horizon, capped]).length;
|
|
74
|
+
sql(`DELETE FROM ${tables.records}
|
|
75
|
+
WHERE day < ? AND NOT EXISTS (SELECT 1 FROM ${tables.rows} d WHERE d.day = ${tables.records}.day)`, [horizon]);
|
|
76
|
+
return pruned;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=billing-retention.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"billing-retention.js","sourceRoot":"","sources":["../src/billing-retention.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bytes as a stream (#2498, `docs/rfc/file-bearing-sources.md` §6).
|
|
3
|
+
*
|
|
4
|
+
* The byte seams between a provider, a connector and the attachment store each took the whole
|
|
5
|
+
* body as one buffer, so the largest file a connector could land was whatever the worker had
|
|
6
|
+
* memory for. Each seam now also takes a {@link SizedByteStream}: a stream, plus the length its
|
|
7
|
+
* producer declares. The length is declared rather than counted because the stores need it
|
|
8
|
+
* before the first byte (R2 refuses a stream of unknown length), and it is enforced on the way
|
|
9
|
+
* through: a stream that carries more or fewer bytes than it declared errors instead of landing.
|
|
10
|
+
*
|
|
11
|
+
* Structural, for the reason `FetchLike` is: the kernel depends on no platform typings, and a
|
|
12
|
+
* web `ReadableStream<Uint8Array>` from Node, workerd or a browser satisfies {@link ByteStream}
|
|
13
|
+
* without either being required.
|
|
14
|
+
*/
|
|
15
|
+
/** One read from a {@link ByteStreamReader}: a chunk, or the end. */
|
|
16
|
+
export interface ByteStreamRead {
|
|
17
|
+
readonly done: boolean;
|
|
18
|
+
readonly value?: Uint8Array;
|
|
19
|
+
}
|
|
20
|
+
/** The reader a {@link ByteStream} hands out — the slice of `ReadableStreamDefaultReader` used here. */
|
|
21
|
+
export interface ByteStreamReader {
|
|
22
|
+
read(): Promise<ByteStreamRead>;
|
|
23
|
+
cancel(reason?: unknown): Promise<void>;
|
|
24
|
+
releaseLock(): void;
|
|
25
|
+
}
|
|
26
|
+
/** A web `ReadableStream<Uint8Array>`, structurally. */
|
|
27
|
+
export interface ByteStream {
|
|
28
|
+
getReader(): ByteStreamReader;
|
|
29
|
+
}
|
|
30
|
+
/** A byte stream with the length its producer declares. */
|
|
31
|
+
export interface SizedByteStream {
|
|
32
|
+
readonly stream: ByteStream;
|
|
33
|
+
readonly length: number;
|
|
34
|
+
}
|
|
35
|
+
/** A body a byte seam takes: the whole buffer, as before, or a sized stream. */
|
|
36
|
+
export type ByteBody = Uint8Array | SizedByteStream;
|
|
37
|
+
/**
|
|
38
|
+
* Bytes a connector already holds, in the shape a connector upload takes (#2538): the buffer itself
|
|
39
|
+
* up to {@link CONNECTOR_ATTACHMENT_BUFFERED_MAX_BYTES}, and above it a {@link SizedByteStream} of the
|
|
40
|
+
* same bytes, which the platform sends by the streamed route instead of refusing. It does not shrink
|
|
41
|
+
* what the connector holds; it keeps a file the connector could fetch landing end to end. The stream
|
|
42
|
+
* is one-shot: read it once, and call this again for another upload of the same bytes.
|
|
43
|
+
*/
|
|
44
|
+
export declare function connectorUploadBody(bytes: Uint8Array): ByteBody;
|
|
45
|
+
/**
|
|
46
|
+
* Bytes already held, as a one-shot {@link SizedByteStream} handed out 1 MiB at a time. It saves no
|
|
47
|
+
* memory, since the buffer is held; it lets a buffer go where a seam takes a stream. Each chunk is a
|
|
48
|
+
* copy, not a view of `bytes`, so a consumer that takes a chunk's buffer cannot detach the rest.
|
|
49
|
+
*/
|
|
50
|
+
export declare function bytesStream(bytes: Uint8Array): SizedByteStream;
|
|
51
|
+
/** Is `body` a sized stream rather than a buffer? */
|
|
52
|
+
export declare function isSizedByteStream(body: unknown): body is SizedByteStream;
|
|
53
|
+
/**
|
|
54
|
+
* A running SHA-256, fed one chunk at a time. The kernel holds no implementation: Web Crypto has
|
|
55
|
+
* no incremental digest and a hand-rolled one is not an option, so each adapter passes its
|
|
56
|
+
* runtime's own (workerd's `crypto.DigestStream`, Node's `createHash`).
|
|
57
|
+
*/
|
|
58
|
+
export interface IncrementalSha256 {
|
|
59
|
+
update(chunk: Uint8Array): void | Promise<void>;
|
|
60
|
+
/** The lowercase hex digest of everything fed so far. Called once, after the last chunk. */
|
|
61
|
+
hex(): Promise<string>;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* What makes the runtime's own running SHA-256, where it has one (workerd's `crypto.DigestStream`), or
|
|
65
|
+
* `undefined` where it has none (Node, a browser): the kernel finds the runtime's, it never holds one.
|
|
66
|
+
* A maker rather than a digest, so a caller can learn whether it can check bytes before it asks for
|
|
67
|
+
* any, and opens a digest only for bytes it has.
|
|
68
|
+
*/
|
|
69
|
+
export declare function runtimeSha256(): (() => IncrementalSha256) | undefined;
|
|
70
|
+
/** What a witnessed stream's bytes were, once they have all passed. */
|
|
71
|
+
export interface StreamWitness {
|
|
72
|
+
readonly sha256: string;
|
|
73
|
+
readonly size: number;
|
|
74
|
+
}
|
|
75
|
+
/** A sized stream that hashes and counts its bytes as its reader takes them. */
|
|
76
|
+
export interface WitnessedStream {
|
|
77
|
+
readonly body: SizedByteStream;
|
|
78
|
+
/**
|
|
79
|
+
* The bytes' sha256 and size. Throws until the stream has been read to its end, so a store
|
|
80
|
+
* that answered without consuming the body cannot have a record written for bytes it never saw.
|
|
81
|
+
*/
|
|
82
|
+
witness(): StreamWitness;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Hash and count `body` while it flows to wherever it is going. The digest is fed each chunk
|
|
86
|
+
* before the chunk moves on, and the final digest is taken before the stream reports its end, so
|
|
87
|
+
* once the consumer has read to the end — a store's `put` has resolved — {@link
|
|
88
|
+
* WitnessedStream.witness} answers.
|
|
89
|
+
*/
|
|
90
|
+
export declare function witnessedStream(body: SizedByteStream, digest: IncrementalSha256): WitnessedStream;
|
|
91
|
+
/**
|
|
92
|
+
* The largest chunk a {@link verifiedStream} hands on, and so the most it holds back at its end. A
|
|
93
|
+
* source chunk larger than this is handed on in pieces of this size.
|
|
94
|
+
*/
|
|
95
|
+
export declare const VERIFIED_STREAM_CHUNK_MAX_BYTES: number;
|
|
96
|
+
/**
|
|
97
|
+
* Stored bytes on their way out, held to the record that witnesses them (#2514): its `size` and its
|
|
98
|
+
* `sha256`, both checked as the bytes pass rather than before the first one, so nothing is held whole.
|
|
99
|
+
*
|
|
100
|
+
* **What a consumer sees when the bytes are wrong.** The digest is known only after the last byte, so
|
|
101
|
+
* the chunk that completes the declared size is held back until the source has ended and the digest
|
|
102
|
+
* matches. A mismatch, a source that ends short, or one that carries more errors the stream instead,
|
|
103
|
+
* and that last chunk is never handed on. So a consumer can never have received all `size` bytes of a
|
|
104
|
+
* file that did not verify. One that counts to `size` and stops reading is as safe as one that reads to
|
|
105
|
+
* the end. What it may have received before the error is a prefix, and it is told so by the error.
|
|
106
|
+
*
|
|
107
|
+
* The held-back chunk is at most {@link VERIFIED_STREAM_CHUNK_MAX_BYTES}: a source chunk larger than
|
|
108
|
+
* that is handed on in copies of that size, so the wait never holds more than one such piece beside the
|
|
109
|
+
* source chunk it came from. A source chunk within the cap is handed on as the source gave it.
|
|
110
|
+
*
|
|
111
|
+
* Throws before reading anything when the stream's length is not the record's `size`, or the record
|
|
112
|
+
* carries no sha256 to verify against, so a file is never handed on as verified when it could not be.
|
|
113
|
+
*/
|
|
114
|
+
export declare function verifiedStream(body: SizedByteStream, expected: {
|
|
115
|
+
readonly size: number;
|
|
116
|
+
readonly sha256: string;
|
|
117
|
+
}, digest: IncrementalSha256): SizedByteStream;
|
|
118
|
+
/**
|
|
119
|
+
* `body` as a stream of known length that the runtime takes, held to its declared length: a
|
|
120
|
+
* stream that carries more or fewer bytes errors whatever reads it. On workerd that is a
|
|
121
|
+
* `FixedLengthStream`, which a `fetch` sends with a `Content-Length` rather than chunked and
|
|
122
|
+
* which R2's `put` accepts (it refuses a stream of unknown length); the runtime enforces the
|
|
123
|
+
* length there, so a web stream is piped straight in. Elsewhere it is the stream passed through
|
|
124
|
+
* a relay that enforces the length.
|
|
125
|
+
*/
|
|
126
|
+
export declare function knownLengthStream(body: SizedByteStream): ByteStream;
|
|
127
|
+
/**
|
|
128
|
+
* A request init whose body is a sized stream, as the runtime's `fetch` takes it: the body
|
|
129
|
+
* as {@link knownLengthStream}, and off workerd the `Content-Length` header and `duplex: 'half'`
|
|
130
|
+
* a streamed request body needs there. `fetchLikeOf` sends through it, so a `FetchLike` does not
|
|
131
|
+
* call this itself: a runtime `fetch` becomes one by `fetchLikeOf`. A caller that holds a
|
|
132
|
+
* runtime `fetch` it does not make a `FetchLike` (the platform's vertical client) does.
|
|
133
|
+
*/
|
|
134
|
+
export declare function knownLengthInit<Init extends {
|
|
135
|
+
headers?: Record<string, string>;
|
|
136
|
+
body: SizedByteStream;
|
|
137
|
+
}>(init: Init): Omit<Init, 'body'> & {
|
|
138
|
+
body: ByteStream;
|
|
139
|
+
headers: Record<string, string>;
|
|
140
|
+
duplex?: 'half';
|
|
141
|
+
};
|
|
142
|
+
//# sourceMappingURL=byte-stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"byte-stream.d.ts","sourceRoot":"","sources":["../src/byte-stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,qEAAqE;AACrE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;CAC7B;AAED,wGAAwG;AACxG,MAAM,WAAW,gBAAgB;IAC/B,IAAI,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC;IAChC,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,WAAW,IAAI,IAAI,CAAC;CACrB;AAED,wDAAwD;AACxD,MAAM,WAAW,UAAU;IACzB,SAAS,IAAI,gBAAgB,CAAC;CAC/B;AAED,2DAA2D;AAC3D,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,gFAAgF;AAChF,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,eAAe,CAAC;AAEpD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,QAAQ,CAG/D;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,eAAe,CAkB9D;AAED,qDAAqD;AACrD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,eAAe,CAQxE;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,4FAA4F;IAC5F,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACxB;AAQD;;;;;GAKG;AACH,wBAAgB,aAAa,IAAI,CAAC,MAAM,iBAAiB,CAAC,GAAG,SAAS,CAcrE;AAED,uEAAuE;AACvE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,gFAAgF;AAChF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B;;;OAGG;IACH,OAAO,IAAI,aAAa,CAAC;CAC1B;AAmED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,iBAAiB,GAAG,eAAe,CAiBjG;AAED;;;GAGG;AACH,eAAO,MAAM,+BAA+B,QAAc,CAAC;AAI3D;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,eAAe,EACrB,QAAQ,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAC5D,MAAM,EAAE,iBAAiB,GACxB,eAAe,CAgEjB;AAUD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,UAAU,CAOnE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,SAAS;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,eAAe,CAAA;CAAE,EACtG,IAAI,EAAE,IAAI,GACT,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAS7F"}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bytes as a stream (#2498, `docs/rfc/file-bearing-sources.md` §6).
|
|
3
|
+
*
|
|
4
|
+
* The byte seams between a provider, a connector and the attachment store each took the whole
|
|
5
|
+
* body as one buffer, so the largest file a connector could land was whatever the worker had
|
|
6
|
+
* memory for. Each seam now also takes a {@link SizedByteStream}: a stream, plus the length its
|
|
7
|
+
* producer declares. The length is declared rather than counted because the stores need it
|
|
8
|
+
* before the first byte (R2 refuses a stream of unknown length), and it is enforced on the way
|
|
9
|
+
* through: a stream that carries more or fewer bytes than it declared errors instead of landing.
|
|
10
|
+
*
|
|
11
|
+
* Structural, for the reason `FetchLike` is: the kernel depends on no platform typings, and a
|
|
12
|
+
* web `ReadableStream<Uint8Array>` from Node, workerd or a browser satisfies {@link ByteStream}
|
|
13
|
+
* without either being required.
|
|
14
|
+
*/
|
|
15
|
+
import { CONNECTOR_ATTACHMENT_BUFFERED_MAX_BYTES } from '@substrat-run/contracts';
|
|
16
|
+
/**
|
|
17
|
+
* Bytes a connector already holds, in the shape a connector upload takes (#2538): the buffer itself
|
|
18
|
+
* up to {@link CONNECTOR_ATTACHMENT_BUFFERED_MAX_BYTES}, and above it a {@link SizedByteStream} of the
|
|
19
|
+
* same bytes, which the platform sends by the streamed route instead of refusing. It does not shrink
|
|
20
|
+
* what the connector holds; it keeps a file the connector could fetch landing end to end. The stream
|
|
21
|
+
* is one-shot: read it once, and call this again for another upload of the same bytes.
|
|
22
|
+
*/
|
|
23
|
+
export function connectorUploadBody(bytes) {
|
|
24
|
+
if (bytes.byteLength <= CONNECTOR_ATTACHMENT_BUFFERED_MAX_BYTES)
|
|
25
|
+
return bytes;
|
|
26
|
+
return bytesStream(bytes);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Bytes already held, as a one-shot {@link SizedByteStream} handed out 1 MiB at a time. It saves no
|
|
30
|
+
* memory, since the buffer is held; it lets a buffer go where a seam takes a stream. Each chunk is a
|
|
31
|
+
* copy, not a view of `bytes`, so a consumer that takes a chunk's buffer cannot detach the rest.
|
|
32
|
+
*/
|
|
33
|
+
export function bytesStream(bytes) {
|
|
34
|
+
const CHUNK = 1024 * 1024;
|
|
35
|
+
let at = 0;
|
|
36
|
+
const stream = new ReadableStream({
|
|
37
|
+
async pull(controller) {
|
|
38
|
+
if (at >= bytes.byteLength) {
|
|
39
|
+
controller.close();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
controller.enqueue(bytes.slice(at, Math.min(at + CHUNK, bytes.byteLength)));
|
|
43
|
+
at += CHUNK;
|
|
44
|
+
},
|
|
45
|
+
async cancel() { },
|
|
46
|
+
}, { highWaterMark: 0 });
|
|
47
|
+
return { stream, length: bytes.byteLength };
|
|
48
|
+
}
|
|
49
|
+
/** Is `body` a sized stream rather than a buffer? */
|
|
50
|
+
export function isSizedByteStream(body) {
|
|
51
|
+
return (typeof body === 'object' &&
|
|
52
|
+
body !== null &&
|
|
53
|
+
!ArrayBuffer.isView(body) &&
|
|
54
|
+
typeof body.stream?.getReader === 'function' &&
|
|
55
|
+
typeof body.length === 'number');
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* What makes the runtime's own running SHA-256, where it has one (workerd's `crypto.DigestStream`), or
|
|
59
|
+
* `undefined` where it has none (Node, a browser): the kernel finds the runtime's, it never holds one.
|
|
60
|
+
* A maker rather than a digest, so a caller can learn whether it can check bytes before it asks for
|
|
61
|
+
* any, and opens a digest only for bytes it has.
|
|
62
|
+
*/
|
|
63
|
+
export function runtimeSha256() {
|
|
64
|
+
const DigestStream = globalThis.crypto?.DigestStream;
|
|
65
|
+
if (!DigestStream)
|
|
66
|
+
return undefined;
|
|
67
|
+
return () => {
|
|
68
|
+
const digest = new DigestStream('SHA-256');
|
|
69
|
+
const writer = digest.getWriter();
|
|
70
|
+
return {
|
|
71
|
+
update: (chunk) => writer.write(chunk),
|
|
72
|
+
hex: async () => {
|
|
73
|
+
await writer.close();
|
|
74
|
+
return [...new Uint8Array(await digest.digest)].map((b) => b.toString(16).padStart(2, '0')).join('');
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** Refuse a declared length no stream can have. */
|
|
80
|
+
function assertDeclaredLength(length) {
|
|
81
|
+
if (!Number.isSafeInteger(length) || length < 0) {
|
|
82
|
+
throw new Error(`a byte stream's declared length must be a non-negative integer, not ${length}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* A new stream that passes `source` through chunk by chunk, holding it to `length`. Pull-driven
|
|
87
|
+
* with no queue of its own (`highWaterMark: 0`), so a chunk is read from the source only when the
|
|
88
|
+
* consumer asks for one: at most one chunk is in flight, whatever the body's size.
|
|
89
|
+
*/
|
|
90
|
+
function relay(source, length, onChunk, onEnd) {
|
|
91
|
+
assertDeclaredLength(length);
|
|
92
|
+
const reader = source.getReader();
|
|
93
|
+
let size = 0;
|
|
94
|
+
return new ReadableStream({
|
|
95
|
+
async pull(controller) {
|
|
96
|
+
try {
|
|
97
|
+
const { done, value } = await reader.read();
|
|
98
|
+
if (done) {
|
|
99
|
+
if (size !== length) {
|
|
100
|
+
throw new Error(`the byte stream ended after ${size} bytes; it declared ${length}`);
|
|
101
|
+
}
|
|
102
|
+
await onEnd?.();
|
|
103
|
+
controller.close();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
// A read that neither ends nor carries a chunk is a broken source: answering it by
|
|
107
|
+
// returning would leave the consumer's read waiting for a pull that never comes.
|
|
108
|
+
if (!value)
|
|
109
|
+
throw new Error('the byte stream yielded a read with neither a chunk nor its end');
|
|
110
|
+
size += value.byteLength;
|
|
111
|
+
if (size > length) {
|
|
112
|
+
throw new Error(`the byte stream carried more than the ${length} bytes it declared`);
|
|
113
|
+
}
|
|
114
|
+
await onChunk?.(value);
|
|
115
|
+
controller.enqueue(value);
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
controller.error(err);
|
|
119
|
+
await reader.cancel(err).catch(() => { });
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
cancel: (reason) => reader.cancel(reason),
|
|
123
|
+
}, { highWaterMark: 0 });
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Hash and count `body` while it flows to wherever it is going. The digest is fed each chunk
|
|
127
|
+
* before the chunk moves on, and the final digest is taken before the stream reports its end, so
|
|
128
|
+
* once the consumer has read to the end — a store's `put` has resolved — {@link
|
|
129
|
+
* WitnessedStream.witness} answers.
|
|
130
|
+
*/
|
|
131
|
+
export function witnessedStream(body, digest) {
|
|
132
|
+
let witnessed;
|
|
133
|
+
const stream = relay(body.stream, body.length, (chunk) => digest.update(chunk), async () => {
|
|
134
|
+
witnessed = { sha256: await digest.hex(), size: body.length };
|
|
135
|
+
});
|
|
136
|
+
return {
|
|
137
|
+
body: { stream, length: body.length },
|
|
138
|
+
witness: () => {
|
|
139
|
+
if (!witnessed)
|
|
140
|
+
throw new Error('the byte stream was not read to its end, so its bytes are unwitnessed');
|
|
141
|
+
return witnessed;
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* The largest chunk a {@link verifiedStream} hands on, and so the most it holds back at its end. A
|
|
147
|
+
* source chunk larger than this is handed on in pieces of this size.
|
|
148
|
+
*/
|
|
149
|
+
export const VERIFIED_STREAM_CHUNK_MAX_BYTES = 1024 * 1024;
|
|
150
|
+
const SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
151
|
+
/**
|
|
152
|
+
* Stored bytes on their way out, held to the record that witnesses them (#2514): its `size` and its
|
|
153
|
+
* `sha256`, both checked as the bytes pass rather than before the first one, so nothing is held whole.
|
|
154
|
+
*
|
|
155
|
+
* **What a consumer sees when the bytes are wrong.** The digest is known only after the last byte, so
|
|
156
|
+
* the chunk that completes the declared size is held back until the source has ended and the digest
|
|
157
|
+
* matches. A mismatch, a source that ends short, or one that carries more errors the stream instead,
|
|
158
|
+
* and that last chunk is never handed on. So a consumer can never have received all `size` bytes of a
|
|
159
|
+
* file that did not verify. One that counts to `size` and stops reading is as safe as one that reads to
|
|
160
|
+
* the end. What it may have received before the error is a prefix, and it is told so by the error.
|
|
161
|
+
*
|
|
162
|
+
* The held-back chunk is at most {@link VERIFIED_STREAM_CHUNK_MAX_BYTES}: a source chunk larger than
|
|
163
|
+
* that is handed on in copies of that size, so the wait never holds more than one such piece beside the
|
|
164
|
+
* source chunk it came from. A source chunk within the cap is handed on as the source gave it.
|
|
165
|
+
*
|
|
166
|
+
* Throws before reading anything when the stream's length is not the record's `size`, or the record
|
|
167
|
+
* carries no sha256 to verify against, so a file is never handed on as verified when it could not be.
|
|
168
|
+
*/
|
|
169
|
+
export function verifiedStream(body, expected, digest) {
|
|
170
|
+
assertDeclaredLength(body.length);
|
|
171
|
+
if (body.length !== expected.size) {
|
|
172
|
+
throw new Error(`the stored object is ${body.length} bytes; its record says ${expected.size}`);
|
|
173
|
+
}
|
|
174
|
+
if (!SHA256_HEX.test(expected.sha256)) {
|
|
175
|
+
throw new Error('the record carries no sha256 to verify the bytes against');
|
|
176
|
+
}
|
|
177
|
+
const length = body.length;
|
|
178
|
+
const reader = body.stream.getReader();
|
|
179
|
+
let rest;
|
|
180
|
+
let delivered = 0;
|
|
181
|
+
/** The next non-empty piece of the source, at most the cap, or `null` at its end. */
|
|
182
|
+
const nextPiece = async () => {
|
|
183
|
+
while (!rest || rest.byteLength === 0) {
|
|
184
|
+
const { done, value } = await reader.read();
|
|
185
|
+
if (done)
|
|
186
|
+
return null;
|
|
187
|
+
if (!value)
|
|
188
|
+
throw new Error('the byte stream yielded a read with neither a chunk nor its end');
|
|
189
|
+
rest = value;
|
|
190
|
+
}
|
|
191
|
+
if (rest.byteLength <= VERIFIED_STREAM_CHUNK_MAX_BYTES) {
|
|
192
|
+
const piece = rest;
|
|
193
|
+
rest = undefined;
|
|
194
|
+
return piece;
|
|
195
|
+
}
|
|
196
|
+
// A copy, not a view: a consumer may take the piece's buffer, which would take the rest with it.
|
|
197
|
+
const piece = rest.slice(0, VERIFIED_STREAM_CHUNK_MAX_BYTES);
|
|
198
|
+
rest = rest.subarray(VERIFIED_STREAM_CHUNK_MAX_BYTES);
|
|
199
|
+
return piece;
|
|
200
|
+
};
|
|
201
|
+
const stream = new ReadableStream({
|
|
202
|
+
async pull(controller) {
|
|
203
|
+
try {
|
|
204
|
+
const piece = delivered < length ? await nextPiece() : null;
|
|
205
|
+
if (delivered < length) {
|
|
206
|
+
if (!piece)
|
|
207
|
+
throw new Error(`the stored object ended after ${delivered} bytes; its record says ${length}`);
|
|
208
|
+
if (delivered + piece.byteLength > length) {
|
|
209
|
+
throw new Error(`the stored object carried more than the ${length} bytes its record says`);
|
|
210
|
+
}
|
|
211
|
+
await digest.update(piece);
|
|
212
|
+
delivered += piece.byteLength;
|
|
213
|
+
if (delivered < length) {
|
|
214
|
+
controller.enqueue(piece);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// The last bytes: handed on only once the source has ended and the digest matches.
|
|
219
|
+
if (await nextPiece())
|
|
220
|
+
throw new Error(`the stored object carried more than the ${length} bytes its record says`);
|
|
221
|
+
if ((await digest.hex()) !== expected.sha256)
|
|
222
|
+
throw new Error('the bytes do not match the recorded sha256');
|
|
223
|
+
if (piece)
|
|
224
|
+
controller.enqueue(piece);
|
|
225
|
+
controller.close();
|
|
226
|
+
}
|
|
227
|
+
catch (err) {
|
|
228
|
+
controller.error(err);
|
|
229
|
+
await reader.cancel(err).catch(() => { });
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
cancel: (reason) => reader.cancel(reason),
|
|
233
|
+
}, { highWaterMark: 0 });
|
|
234
|
+
return { stream, length };
|
|
235
|
+
}
|
|
236
|
+
/** The runtime's own `FixedLengthStream`, where it has one (workerd). */
|
|
237
|
+
function fixedLengthStream() {
|
|
238
|
+
return globalThis.FixedLengthStream;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* `body` as a stream of known length that the runtime takes, held to its declared length: a
|
|
242
|
+
* stream that carries more or fewer bytes errors whatever reads it. On workerd that is a
|
|
243
|
+
* `FixedLengthStream`, which a `fetch` sends with a `Content-Length` rather than chunked and
|
|
244
|
+
* which R2's `put` accepts (it refuses a stream of unknown length); the runtime enforces the
|
|
245
|
+
* length there, so a web stream is piped straight in. Elsewhere it is the stream passed through
|
|
246
|
+
* a relay that enforces the length.
|
|
247
|
+
*/
|
|
248
|
+
export function knownLengthStream(body) {
|
|
249
|
+
const Fixed = fixedLengthStream();
|
|
250
|
+
if (!Fixed)
|
|
251
|
+
return relay(body.stream, body.length);
|
|
252
|
+
assertDeclaredLength(body.length);
|
|
253
|
+
const piped = body.stream;
|
|
254
|
+
const source = typeof piped.pipeThrough === 'function' ? piped : relay(body.stream, body.length);
|
|
255
|
+
return source.pipeThrough(new Fixed(body.length));
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* A request init whose body is a sized stream, as the runtime's `fetch` takes it: the body
|
|
259
|
+
* as {@link knownLengthStream}, and off workerd the `Content-Length` header and `duplex: 'half'`
|
|
260
|
+
* a streamed request body needs there. `fetchLikeOf` sends through it, so a `FetchLike` does not
|
|
261
|
+
* call this itself: a runtime `fetch` becomes one by `fetchLikeOf`. A caller that holds a
|
|
262
|
+
* runtime `fetch` it does not make a `FetchLike` (the platform's vertical client) does.
|
|
263
|
+
*/
|
|
264
|
+
export function knownLengthInit(init) {
|
|
265
|
+
const length = init.body.length;
|
|
266
|
+
const onWorkerd = fixedLengthStream() !== undefined;
|
|
267
|
+
return {
|
|
268
|
+
...init,
|
|
269
|
+
body: knownLengthStream(init.body),
|
|
270
|
+
headers: { ...init.headers, ...(onWorkerd ? {} : { 'content-length': String(length) }) },
|
|
271
|
+
...(onWorkerd ? {} : { duplex: 'half' }),
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
//# sourceMappingURL=byte-stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"byte-stream.js","sourceRoot":"","sources":["../src/byte-stream.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Connection, ConnectionLandingOptions, ManifestConnectionLandings, PlatformActorId, ScopeId, TenantId } from '@substrat-run/contracts';
|
|
2
|
-
import type { ScopeHost } from './scope-host.js';
|
|
1
|
+
import type { Connection, ConnectionLandingOptions, ManifestConnectionLandings, PlatformActorId, Scope, ScopeId, TenantId } from '@substrat-run/contracts';
|
|
2
|
+
import type { ScopeFilter, ScopeHost } from './scope-host.js';
|
|
3
3
|
/**
|
|
4
4
|
* Connection landings, applied (#2316): the connector pass's step that turns a vertical's declared
|
|
5
5
|
* `connections` into a held key and a binding for every live connection of that provider.
|
|
@@ -94,9 +94,21 @@ export declare class LandingPass {
|
|
|
94
94
|
private readonly actor;
|
|
95
95
|
private readonly input;
|
|
96
96
|
private readonly concurrency;
|
|
97
|
+
/**
|
|
98
|
+
* How the pass reads a tenant's scopes: the sweep's own reader, so a directory row that does
|
|
99
|
+
* not read as a scope is stepped over here too and named once with the pass's others, rather
|
|
100
|
+
* than failing every landing in its tenant.
|
|
101
|
+
*/
|
|
102
|
+
private readonly listScopes;
|
|
97
103
|
private readonly targets;
|
|
98
104
|
private readonly held;
|
|
99
|
-
constructor(host: ScopeHost, actor: PlatformActorId, input: ConnectionLandingOptionsInput, concurrency: number
|
|
105
|
+
constructor(host: ScopeHost, actor: PlatformActorId, input: ConnectionLandingOptionsInput, concurrency: number,
|
|
106
|
+
/**
|
|
107
|
+
* How the pass reads a tenant's scopes: the sweep's own reader, so a directory row that does
|
|
108
|
+
* not read as a scope is stepped over here too and named once with the pass's others, rather
|
|
109
|
+
* than failing every landing in its tenant.
|
|
110
|
+
*/
|
|
111
|
+
listScopes: (filter: ScopeFilter) => Promise<Scope[]>);
|
|
100
112
|
/** Grant and bind what `connection`'s installs declare for its provider, and say what happened. */
|
|
101
113
|
apply(connection: Connection): Promise<LandingOutcome>;
|
|
102
114
|
/** Every primary, active install of `vertical` in `tenantId`, with what its running version declares. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection-landings.d.ts","sourceRoot":"","sources":["../src/connection-landings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,eAAe,
|
|
1
|
+
{"version":3,"file":"connection-landings.d.ts","sourceRoot":"","sources":["../src/connection-landings.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,13 +12,21 @@ export class LandingPass {
|
|
|
12
12
|
actor;
|
|
13
13
|
input;
|
|
14
14
|
concurrency;
|
|
15
|
+
listScopes;
|
|
15
16
|
targets = new Map();
|
|
16
17
|
held = new Map();
|
|
17
|
-
constructor(host, actor, input, concurrency
|
|
18
|
+
constructor(host, actor, input, concurrency,
|
|
19
|
+
/**
|
|
20
|
+
* How the pass reads a tenant's scopes: the sweep's own reader, so a directory row that does
|
|
21
|
+
* not read as a scope is stepped over here too and named once with the pass's others, rather
|
|
22
|
+
* than failing every landing in its tenant.
|
|
23
|
+
*/
|
|
24
|
+
listScopes) {
|
|
18
25
|
this.host = host;
|
|
19
26
|
this.actor = actor;
|
|
20
27
|
this.input = input;
|
|
21
28
|
this.concurrency = concurrency;
|
|
29
|
+
this.listScopes = listScopes;
|
|
22
30
|
}
|
|
23
31
|
/** Grant and bind what `connection`'s installs declare for its provider, and say what happened. */
|
|
24
32
|
async apply(connection) {
|
|
@@ -92,7 +100,7 @@ export class LandingPass {
|
|
|
92
100
|
let found = this.targets.get(key);
|
|
93
101
|
if (!found) {
|
|
94
102
|
found = (async () => {
|
|
95
|
-
const scopes = (await this.
|
|
103
|
+
const scopes = (await this.listScopes({ tenantId })).filter((s) => s.vertical === vertical && s.status === 'active' && isPrimaryScope(s));
|
|
96
104
|
if (scopes.length === 0)
|
|
97
105
|
return [];
|
|
98
106
|
const running = await runningFactsOf(this.host.admin, this.actor, scopes, (slug, versionId) => this.input.read(slug, versionId), (reason) => ({ kind: 'unreadable', reason }), this.concurrency);
|