@substrat-run/control-plane-api 0.49.0 → 0.51.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/api.d.ts +47 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +368 -4
- package/dist/api.js.map +1 -1
- package/dist/backups.d.ts +93 -0
- package/dist/backups.d.ts.map +1 -0
- package/dist/backups.js +30 -0
- package/dist/backups.js.map +1 -0
- package/dist/directory-backup.d.ts +57 -0
- package/dist/directory-backup.d.ts.map +1 -0
- package/dist/directory-backup.js +68 -0
- package/dist/directory-backup.js.map +1 -0
- package/dist/do-namespaces.d.ts +64 -0
- package/dist/do-namespaces.d.ts.map +1 -0
- package/dist/do-namespaces.js +101 -0
- package/dist/do-namespaces.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/mask.d.ts +15 -0
- package/dist/mask.d.ts.map +1 -1
- package/dist/mask.js +25 -1
- package/dist/mask.js.map +1 -1
- package/dist/platform-runtime.d.ts +22 -0
- package/dist/platform-runtime.d.ts.map +1 -0
- package/dist/platform-runtime.js +2 -0
- package/dist/platform-runtime.js.map +1 -0
- package/dist/r2-backups.d.ts +24 -0
- package/dist/r2-backups.d.ts.map +1 -0
- package/dist/r2-backups.js +175 -0
- package/dist/r2-backups.js.map +1 -0
- package/package.json +7 -7
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The scope-backup write seam (#493) — where a reap's recoverable copy goes.
|
|
3
|
+
*
|
|
4
|
+
* A reap frees a scope's Durable Object storage because Cloudflare never garbage-
|
|
5
|
+
* collects a DO, and it is irreversible. Before #493 the operator had to REMEMBER to
|
|
6
|
+
* take a copy first, from a different surface; this seam is what makes "a reap always
|
|
7
|
+
* leaves a recoverable copy" a property of the route instead of a habit.
|
|
8
|
+
*
|
|
9
|
+
* Why a dump and not a snapshot fork. The obvious cheap move — `orchestratedSnapshot`
|
|
10
|
+
* before the reap — does not survive the case the flow exists for. A fork is provisioned
|
|
11
|
+
* INSIDE the vertical's own deployment and activated, so (a) its bytes live in the very
|
|
12
|
+
* deployment a retirement is about to delete, and (b) it counts as a live scope in
|
|
13
|
+
* `countScopesForVertical`, which means it re-blocks the `deleteVertical` the reap was
|
|
14
|
+
* clearing. A dump leaves the deployment entirely, and `POST …/restore` already loads one
|
|
15
|
+
* back, so export→store→restore is the round trip that actually closes.
|
|
16
|
+
*
|
|
17
|
+
* Provider-neutral by the same posture as `ObservabilityReader` and `DeployVerticalFn`:
|
|
18
|
+
* the contract lives here, an implementation lives in its own module (`r2-backups.ts` for
|
|
19
|
+
* Cloudflare R2), and the host injects one. A backup is addressed by (tenant, scope,
|
|
20
|
+
* capturedAt) rather than by a store-shaped key, so the key scheme stays the store's
|
|
21
|
+
* private business and no caller can build a path into someone else's tenant.
|
|
22
|
+
*
|
|
23
|
+
* Fidelity is deliberately FULL, never masked (#493). A masked dump has its PII redacted,
|
|
24
|
+
* so restoring one produces a structurally-valid but factually wrong scope — a backup that
|
|
25
|
+
* cannot restore is a false promise, and the promise is the whole point. This is not the
|
|
26
|
+
* §6 governed pull: the bytes go platform→platform and are never handed to a caller, which
|
|
27
|
+
* is why the export route's masking default does not apply here.
|
|
28
|
+
*/
|
|
29
|
+
import type { DirectoryBackup, DirectoryDump, ScopeBackup, ScopeDump } from '@substrat-run/contracts';
|
|
30
|
+
export type { ScopeBackup, DirectoryBackup };
|
|
31
|
+
export interface ScopeBackupStore {
|
|
32
|
+
/**
|
|
33
|
+
* Store one full-fidelity dump and return its metadata. Must be durable before it
|
|
34
|
+
* returns: the reap's whole guarantee is that this resolved before any byte was wiped.
|
|
35
|
+
*/
|
|
36
|
+
put(input: {
|
|
37
|
+
vertical: string | null;
|
|
38
|
+
dump: ScopeDump;
|
|
39
|
+
}): Promise<ScopeBackup>;
|
|
40
|
+
/** Every backup held for one scope, newest first. */
|
|
41
|
+
list(input: {
|
|
42
|
+
tenantId: string;
|
|
43
|
+
scopeId: string;
|
|
44
|
+
}): Promise<ScopeBackup[]>;
|
|
45
|
+
/** One backup's dump, by its capture time. Null when no such copy is held. */
|
|
46
|
+
get(input: {
|
|
47
|
+
tenantId: string;
|
|
48
|
+
scopeId: string;
|
|
49
|
+
capturedAt: string;
|
|
50
|
+
}): Promise<ScopeDump | null>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The DIRECTORY-backup store (#40) — the platform's own copy, not a tenant's.
|
|
54
|
+
*
|
|
55
|
+
* A sibling seam rather than a widened `ScopeBackupStore`, because the two are the same
|
|
56
|
+
* mechanism serving different jobs and the difference is worth keeping in the types.
|
|
57
|
+
* A scope backup is taken at a MOMENT — before a reap, before a migration — and is
|
|
58
|
+
* addressed by the scope it came from. A directory backup is taken on a SCHEDULE, has
|
|
59
|
+
* no tenant to be addressed by, and is pruned to a retention window. Only this one
|
|
60
|
+
* needs `delete`, and only this one is ever written by a sweep with no operator behind
|
|
61
|
+
* it.
|
|
62
|
+
*
|
|
63
|
+
* What it defends against is also different, and the distinction is the whole design
|
|
64
|
+
* (#40's own analysis). Per-scope point-in-time recovery already covers corruption
|
|
65
|
+
* inside the account — ~30 days, continuous, and strictly better than a daily copy, so
|
|
66
|
+
* scheduled per-scope backups are deliberately NOT built. The directory is the case PITR
|
|
67
|
+
* cannot answer: it is one Durable Object, and a bug that deletes it outright leaves
|
|
68
|
+
* nothing to rewind. Copies live outside the DO for that reason.
|
|
69
|
+
*
|
|
70
|
+
* Honest scoping: with the store bound in the platform's OWN account (the shape shipped
|
|
71
|
+
* today), this survives losing the directory, not losing the account. The seam is
|
|
72
|
+
* provider-neutral so an off-account target is a drop-in — see control-plane.md §4.9.
|
|
73
|
+
*/
|
|
74
|
+
export interface DirectoryBackupStore {
|
|
75
|
+
/**
|
|
76
|
+
* Store one full-fidelity directory dump and return its metadata. Durable before it
|
|
77
|
+
* returns, like the scope store's `put`.
|
|
78
|
+
*/
|
|
79
|
+
put(input: {
|
|
80
|
+
dump: DirectoryDump;
|
|
81
|
+
}): Promise<DirectoryBackup>;
|
|
82
|
+
/** Every directory copy held, newest first. */
|
|
83
|
+
list(): Promise<DirectoryBackup[]>;
|
|
84
|
+
/** One copy's dump, by its capture time. Null when no such copy is held. */
|
|
85
|
+
get(input: {
|
|
86
|
+
capturedAt: string;
|
|
87
|
+
}): Promise<DirectoryDump | null>;
|
|
88
|
+
/** Drop one copy — how the retention window is enforced. Missing is not an error. */
|
|
89
|
+
delete(input: {
|
|
90
|
+
capturedAt: string;
|
|
91
|
+
}): Promise<void>;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=backups.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backups.d.ts","sourceRoot":"","sources":["../src/backups.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAKtG,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,CAAC;AAE7C,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,GAAG,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,EAAE,SAAS,CAAA;KAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAC/E,qDAAqD;IACrD,IAAI,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC3E,8EAA8E;IAC9E,GAAG,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;CAClG;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,GAAG,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,aAAa,CAAA;KAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAC9D,+CAA+C;IAC/C,IAAI,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IACnC,4EAA4E;IAC5E,GAAG,CAAC,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAClE,qFAAqF;IACrF,MAAM,CAAC,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtD"}
|
package/dist/backups.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The scope-backup write seam (#493) — where a reap's recoverable copy goes.
|
|
3
|
+
*
|
|
4
|
+
* A reap frees a scope's Durable Object storage because Cloudflare never garbage-
|
|
5
|
+
* collects a DO, and it is irreversible. Before #493 the operator had to REMEMBER to
|
|
6
|
+
* take a copy first, from a different surface; this seam is what makes "a reap always
|
|
7
|
+
* leaves a recoverable copy" a property of the route instead of a habit.
|
|
8
|
+
*
|
|
9
|
+
* Why a dump and not a snapshot fork. The obvious cheap move — `orchestratedSnapshot`
|
|
10
|
+
* before the reap — does not survive the case the flow exists for. A fork is provisioned
|
|
11
|
+
* INSIDE the vertical's own deployment and activated, so (a) its bytes live in the very
|
|
12
|
+
* deployment a retirement is about to delete, and (b) it counts as a live scope in
|
|
13
|
+
* `countScopesForVertical`, which means it re-blocks the `deleteVertical` the reap was
|
|
14
|
+
* clearing. A dump leaves the deployment entirely, and `POST …/restore` already loads one
|
|
15
|
+
* back, so export→store→restore is the round trip that actually closes.
|
|
16
|
+
*
|
|
17
|
+
* Provider-neutral by the same posture as `ObservabilityReader` and `DeployVerticalFn`:
|
|
18
|
+
* the contract lives here, an implementation lives in its own module (`r2-backups.ts` for
|
|
19
|
+
* Cloudflare R2), and the host injects one. A backup is addressed by (tenant, scope,
|
|
20
|
+
* capturedAt) rather than by a store-shaped key, so the key scheme stays the store's
|
|
21
|
+
* private business and no caller can build a path into someone else's tenant.
|
|
22
|
+
*
|
|
23
|
+
* Fidelity is deliberately FULL, never masked (#493). A masked dump has its PII redacted,
|
|
24
|
+
* so restoring one produces a structurally-valid but factually wrong scope — a backup that
|
|
25
|
+
* cannot restore is a false promise, and the promise is the whole point. This is not the
|
|
26
|
+
* §6 governed pull: the bytes go platform→platform and are never handed to a caller, which
|
|
27
|
+
* is why the export route's masking default does not apply here.
|
|
28
|
+
*/
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=backups.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backups.js","sourceRoot":"","sources":["../src/backups.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The scheduled directory backup (#40) — the platform's own disaster-recovery pass.
|
|
3
|
+
*
|
|
4
|
+
* `control-plane.md` names the stake without resolving it: *"The directory becomes a real
|
|
5
|
+
* database, with its own migrations and backup story… Losing it is losing the platform,
|
|
6
|
+
* not losing a cache."* Every OTHER database the platform holds is a scope, protected by
|
|
7
|
+
* ~30-day Durable Object point-in-time recovery — which is continuous, per-scope, and
|
|
8
|
+
* strictly better than any daily copy, so scheduled per-scope backups are deliberately not
|
|
9
|
+
* built here. The directory is the one database PITR cannot answer for: it is a single DO,
|
|
10
|
+
* and a control-plane bug that deletes it outright leaves nothing to rewind. So a copy
|
|
11
|
+
* lives OUTSIDE the DO, and this is what takes it.
|
|
12
|
+
*
|
|
13
|
+
* Driven by the same cron that drives the platform sweep, and the cadence is enforced HERE
|
|
14
|
+
* rather than by a second trigger. A quarter-hourly sweep asking "is a copy due?" and answering
|
|
15
|
+
* from the store is idempotent, survives a missed tick (the next pass takes it, late
|
|
16
|
+
* rather than never), and needs no durable "last run" state of its own — the newest stored
|
|
17
|
+
* copy IS that state. A dedicated daily trigger would have to be right about time zones,
|
|
18
|
+
* deploys and restarts to do the same job worse.
|
|
19
|
+
*/
|
|
20
|
+
import type { DirectoryBackup, PlatformActorId } from '@substrat-run/contracts';
|
|
21
|
+
import type { HostAdmin } from '@substrat-run/kernel';
|
|
22
|
+
import type { DirectoryBackupStore } from './backups.js';
|
|
23
|
+
export interface DirectoryBackupOptions {
|
|
24
|
+
admin: HostAdmin;
|
|
25
|
+
store: DirectoryBackupStore;
|
|
26
|
+
/** The platform actor the export is audited under. */
|
|
27
|
+
actor: PlatformActorId;
|
|
28
|
+
/** Hours between copies (default 24). */
|
|
29
|
+
intervalHours?: number;
|
|
30
|
+
/** How many copies to keep (default 30). Older ones are deleted after a successful put. */
|
|
31
|
+
retain?: number;
|
|
32
|
+
/** Take a copy regardless of cadence — what the manual "back up now" route passes. */
|
|
33
|
+
force?: boolean;
|
|
34
|
+
/** Injected clock, for tests. */
|
|
35
|
+
now?: () => Date;
|
|
36
|
+
}
|
|
37
|
+
export interface DirectoryBackupResult {
|
|
38
|
+
/** The copy taken this pass, or null when the cadence guard skipped it. */
|
|
39
|
+
taken: DirectoryBackup | null;
|
|
40
|
+
/** When nothing was taken: the copy that was still fresh enough to skip for. */
|
|
41
|
+
skippedFor: string | null;
|
|
42
|
+
/** Copies deleted by the retention window this pass. */
|
|
43
|
+
pruned: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Take a directory backup if one is due, then prune to the retention window.
|
|
47
|
+
*
|
|
48
|
+
* Order matters and is the inverse of the reap's (#493). A reap stores BEFORE it wipes,
|
|
49
|
+
* because the wipe is what the copy protects against. Here the prune runs AFTER a
|
|
50
|
+
* successful put, so a failed capture can never be the thing that deletes the last good
|
|
51
|
+
* copy — the worst case is an extra copy kept, never a missing one.
|
|
52
|
+
*
|
|
53
|
+
* Throws whatever the export or the store threw. The caller (a cron pass) decides what a
|
|
54
|
+
* failure means; it must not be silent, which is why nothing is swallowed here.
|
|
55
|
+
*/
|
|
56
|
+
export declare function backupDirectoryIfDue(options: DirectoryBackupOptions): Promise<DirectoryBackupResult>;
|
|
57
|
+
//# sourceMappingURL=directory-backup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directory-backup.d.ts","sourceRoot":"","sources":["../src/directory-backup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAChF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAUzD,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,oBAAoB,CAAC;IAC5B,sDAAsD;IACtD,KAAK,EAAE,eAAe,CAAC;IACvB,yCAAyC;IACzC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2FAA2F;IAC3F,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sFAAsF;IACtF,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,2EAA2E;IAC3E,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IAC9B,gFAAgF;IAChF,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,qBAAqB,CAAC,CA+BhC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The scheduled directory backup (#40) — the platform's own disaster-recovery pass.
|
|
3
|
+
*
|
|
4
|
+
* `control-plane.md` names the stake without resolving it: *"The directory becomes a real
|
|
5
|
+
* database, with its own migrations and backup story… Losing it is losing the platform,
|
|
6
|
+
* not losing a cache."* Every OTHER database the platform holds is a scope, protected by
|
|
7
|
+
* ~30-day Durable Object point-in-time recovery — which is continuous, per-scope, and
|
|
8
|
+
* strictly better than any daily copy, so scheduled per-scope backups are deliberately not
|
|
9
|
+
* built here. The directory is the one database PITR cannot answer for: it is a single DO,
|
|
10
|
+
* and a control-plane bug that deletes it outright leaves nothing to rewind. So a copy
|
|
11
|
+
* lives OUTSIDE the DO, and this is what takes it.
|
|
12
|
+
*
|
|
13
|
+
* Driven by the same cron that drives the platform sweep, and the cadence is enforced HERE
|
|
14
|
+
* rather than by a second trigger. A quarter-hourly sweep asking "is a copy due?" and answering
|
|
15
|
+
* from the store is idempotent, survives a missed tick (the next pass takes it, late
|
|
16
|
+
* rather than never), and needs no durable "last run" state of its own — the newest stored
|
|
17
|
+
* copy IS that state. A dedicated daily trigger would have to be right about time zones,
|
|
18
|
+
* deploys and restarts to do the same job worse.
|
|
19
|
+
*/
|
|
20
|
+
/** One copy a day; the sweep runs far more often and skips in between. */
|
|
21
|
+
const DEFAULT_INTERVAL_HOURS = 24;
|
|
22
|
+
/**
|
|
23
|
+
* Thirty copies — a month of daily history, matching the DO PITR horizon so the two
|
|
24
|
+
* defences expire together rather than leaving a window covered by neither.
|
|
25
|
+
*/
|
|
26
|
+
const DEFAULT_RETAIN = 30;
|
|
27
|
+
/**
|
|
28
|
+
* Take a directory backup if one is due, then prune to the retention window.
|
|
29
|
+
*
|
|
30
|
+
* Order matters and is the inverse of the reap's (#493). A reap stores BEFORE it wipes,
|
|
31
|
+
* because the wipe is what the copy protects against. Here the prune runs AFTER a
|
|
32
|
+
* successful put, so a failed capture can never be the thing that deletes the last good
|
|
33
|
+
* copy — the worst case is an extra copy kept, never a missing one.
|
|
34
|
+
*
|
|
35
|
+
* Throws whatever the export or the store threw. The caller (a cron pass) decides what a
|
|
36
|
+
* failure means; it must not be silent, which is why nothing is swallowed here.
|
|
37
|
+
*/
|
|
38
|
+
export async function backupDirectoryIfDue(options) {
|
|
39
|
+
const now = options.now?.() ?? new Date();
|
|
40
|
+
const retain = options.retain ?? DEFAULT_RETAIN;
|
|
41
|
+
const intervalMs = (options.intervalHours ?? DEFAULT_INTERVAL_HOURS) * 3_600_000;
|
|
42
|
+
// The store IS the schedule's memory: newest copy, newest first (the store sorts).
|
|
43
|
+
const existing = await options.store.list();
|
|
44
|
+
const newest = existing[0];
|
|
45
|
+
if (!options.force && newest) {
|
|
46
|
+
const age = now.getTime() - new Date(newest.capturedAt).getTime();
|
|
47
|
+
// `age < 0` — a copy stamped in the future, from a clock skew or a hand-uploaded
|
|
48
|
+
// object — counts as fresh. Skipping is the conservative read: taking a copy is
|
|
49
|
+
// cheap, but treating a future timestamp as "infinitely overdue" would take one on
|
|
50
|
+
// every single pass until real time caught up.
|
|
51
|
+
if (age < intervalMs)
|
|
52
|
+
return { taken: null, skippedFor: newest.capturedAt, pruned: 0 };
|
|
53
|
+
}
|
|
54
|
+
const dump = await options.admin.exportDirectory(options.actor);
|
|
55
|
+
const taken = await options.store.put({ dump });
|
|
56
|
+
// Prune from a re-read list, not from `existing`: the copy just written has to be in
|
|
57
|
+
// the reckoning, or a retention window of N would keep N+1 forever.
|
|
58
|
+
let pruned = 0;
|
|
59
|
+
if (retain > 0) {
|
|
60
|
+
const all = await options.store.list();
|
|
61
|
+
for (const stale of all.slice(retain)) {
|
|
62
|
+
await options.store.delete({ capturedAt: stale.capturedAt });
|
|
63
|
+
pruned += 1;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return { taken, skippedFor: null, pruned };
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=directory-backup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directory-backup.js","sourceRoot":"","sources":["../src/directory-backup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAMH,0EAA0E;AAC1E,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAClC;;;GAGG;AACH,MAAM,cAAc,GAAG,EAAE,CAAC;AA0B1B;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,OAA+B;IAE/B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC;IAC1C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,cAAc,CAAC;IAChD,MAAM,UAAU,GAAG,CAAC,OAAO,CAAC,aAAa,IAAI,sBAAsB,CAAC,GAAG,SAAS,CAAC;IAEjF,mFAAmF;IACnF,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IAC5C,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC3B,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,MAAM,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC;QAClE,iFAAiF;QACjF,gFAAgF;QAChF,mFAAmF;QACnF,+CAA+C;QAC/C,IAAI,GAAG,GAAG,UAAU;YAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IACzF,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAChE,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IAEhD,qFAAqF;IACrF,oEAAoE;IACpE,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACvC,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YACtC,MAAM,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;YAC7D,MAAM,IAAI,CAAC,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC7C,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolving a scope's Durable Object to something an operator can OPEN.
|
|
3
|
+
*
|
|
4
|
+
* A scope's data lives in one Durable Object, named after the scope id inside a namespace
|
|
5
|
+
* (a class × a script). The console has always been able to say the name; the dashboard,
|
|
6
|
+
* however, addresses a namespace by an id Cloudflare assigns — not by the class or the
|
|
7
|
+
* script — so without that id the best a link could do was the account-wide namespace
|
|
8
|
+
* list, which on a real fleet is one entry per pushed script.
|
|
9
|
+
*
|
|
10
|
+
* This is the seam that closes the gap: given the script a scope serves from, hand back
|
|
11
|
+
* the namespaces defined in it. Injected by the host like every other Cloudflare read
|
|
12
|
+
* (D-34: this package holds no SDK and no credential), and entirely optional — absent, the
|
|
13
|
+
* console falls back to the namespace list exactly as before.
|
|
14
|
+
*/
|
|
15
|
+
/** One Durable Object namespace as Cloudflare describes it. */
|
|
16
|
+
export interface DoNamespaceRecord {
|
|
17
|
+
/** The dashboard's address for this namespace — the whole point of the read. */
|
|
18
|
+
id: string;
|
|
19
|
+
/** The exported class the namespace is defined by (e.g. `ScopeDO`). */
|
|
20
|
+
className: string;
|
|
21
|
+
/** The script defining it — a dispatch-namespace script name for a pushed vertical. */
|
|
22
|
+
script: string | null;
|
|
23
|
+
name: string | null;
|
|
24
|
+
useSqlite: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface DoNamespaceReader {
|
|
27
|
+
/** Every Durable Object namespace on the account. Filtering by script happens above:
|
|
28
|
+
* Cloudflare's list endpoint takes no filter, and a reader that pretended otherwise
|
|
29
|
+
* would hide the pagination cost from the one place that can cache it. */
|
|
30
|
+
list(): Promise<DoNamespaceRecord[]>;
|
|
31
|
+
}
|
|
32
|
+
export interface CfDoNamespaceOptions {
|
|
33
|
+
accountId: string;
|
|
34
|
+
apiToken: string;
|
|
35
|
+
/** Injected for tests. */
|
|
36
|
+
fetchImpl?: typeof fetch;
|
|
37
|
+
/**
|
|
38
|
+
* How long a listing stays fresh. The mapping changes only when a script is pushed or
|
|
39
|
+
* deleted, and the read is one-or-more paginated API calls that would otherwise run on
|
|
40
|
+
* every scope-detail open — so the default trades a few minutes of staleness (worst
|
|
41
|
+
* case: a just-pushed script's namespace is not linkable yet) for not hammering the API.
|
|
42
|
+
*/
|
|
43
|
+
ttlMs?: number;
|
|
44
|
+
/** Hard cap on pages walked, so an account with a very large fleet degrades to a slow
|
|
45
|
+
* read rather than an unbounded one. */
|
|
46
|
+
maxPages?: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* A {@link DoNamespaceReader} over Cloudflare's account-wide list endpoint, with a small
|
|
50
|
+
* TTL cache. Pure web-standard `fetch`, like every other Cloudflare seam in this package.
|
|
51
|
+
*/
|
|
52
|
+
export declare function createCfDoNamespaceReader(opts: CfDoNamespaceOptions): DoNamespaceReader;
|
|
53
|
+
/**
|
|
54
|
+
* The namespaces defined in one script, most-likely-scope-class first.
|
|
55
|
+
*
|
|
56
|
+
* A vertical's script defines several classes (the scope DO, an identity DO, sweepers), so
|
|
57
|
+
* ORDER is the useful part: the console links the first entry, and a scope's data is in the
|
|
58
|
+
* scope class. Matching is on the class name because that is the only stable signal — the
|
|
59
|
+
* namespace's `name` is Cloudflare's, and the binding name lives in the script's settings,
|
|
60
|
+
* not here. Every namespace of the script is still returned, so a vertical that named its
|
|
61
|
+
* class something else is one click away rather than unreachable.
|
|
62
|
+
*/
|
|
63
|
+
export declare function namespacesForScript(rows: DoNamespaceRecord[], script: string): DoNamespaceRecord[];
|
|
64
|
+
//# sourceMappingURL=do-namespaces.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"do-namespaces.d.ts","sourceRoot":"","sources":["../src/do-namespaces.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,+DAA+D;AAC/D,MAAM,WAAW,iBAAiB;IAChC,gFAAgF;IAChF,EAAE,EAAE,MAAM,CAAC;IACX,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAC;IAClB,uFAAuF;IACvF,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC;;+EAE2E;IAC3E,IAAI,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,0BAA0B;IAC1B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;6CACyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAID;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,oBAAoB,GAAG,iBAAiB,CAiEvF;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,iBAAiB,EAAE,CASlG"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolving a scope's Durable Object to something an operator can OPEN.
|
|
3
|
+
*
|
|
4
|
+
* A scope's data lives in one Durable Object, named after the scope id inside a namespace
|
|
5
|
+
* (a class × a script). The console has always been able to say the name; the dashboard,
|
|
6
|
+
* however, addresses a namespace by an id Cloudflare assigns — not by the class or the
|
|
7
|
+
* script — so without that id the best a link could do was the account-wide namespace
|
|
8
|
+
* list, which on a real fleet is one entry per pushed script.
|
|
9
|
+
*
|
|
10
|
+
* This is the seam that closes the gap: given the script a scope serves from, hand back
|
|
11
|
+
* the namespaces defined in it. Injected by the host like every other Cloudflare read
|
|
12
|
+
* (D-34: this package holds no SDK and no credential), and entirely optional — absent, the
|
|
13
|
+
* console falls back to the namespace list exactly as before.
|
|
14
|
+
*/
|
|
15
|
+
const PER_PAGE = 100;
|
|
16
|
+
/**
|
|
17
|
+
* A {@link DoNamespaceReader} over Cloudflare's account-wide list endpoint, with a small
|
|
18
|
+
* TTL cache. Pure web-standard `fetch`, like every other Cloudflare seam in this package.
|
|
19
|
+
*/
|
|
20
|
+
export function createCfDoNamespaceReader(opts) {
|
|
21
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
22
|
+
const ttlMs = opts.ttlMs ?? 5 * 60_000;
|
|
23
|
+
const maxPages = opts.maxPages ?? 50;
|
|
24
|
+
const base = `https://api.cloudflare.com/client/v4/accounts/${opts.accountId}/workers/durable_objects/namespaces`;
|
|
25
|
+
let cached = null;
|
|
26
|
+
// Concurrent scope-detail opens must not each start their own walk; they share the
|
|
27
|
+
// in-flight promise and settle together.
|
|
28
|
+
let inFlight = null;
|
|
29
|
+
async function walk() {
|
|
30
|
+
const rows = [];
|
|
31
|
+
for (let page = 1; page <= maxPages; page++) {
|
|
32
|
+
const res = await fetchImpl(`${base}?page=${page}&per_page=${PER_PAGE}`, {
|
|
33
|
+
headers: { authorization: `Bearer ${opts.apiToken}` },
|
|
34
|
+
});
|
|
35
|
+
if (!res.ok) {
|
|
36
|
+
throw new Error(`Cloudflare Durable Object namespace list failed (${res.status})`);
|
|
37
|
+
}
|
|
38
|
+
const body = (await res.json());
|
|
39
|
+
if (body.success === false) {
|
|
40
|
+
throw new Error(`Cloudflare Durable Object namespace list failed: ${body.errors?.map((e) => e.message).join('; ') || 'unknown error'}`);
|
|
41
|
+
}
|
|
42
|
+
const batch = body.result ?? [];
|
|
43
|
+
for (const r of batch) {
|
|
44
|
+
// An id-less row cannot be linked to, which is this read's whole purpose.
|
|
45
|
+
if (!r.id || !r.class)
|
|
46
|
+
continue;
|
|
47
|
+
rows.push({
|
|
48
|
+
id: r.id,
|
|
49
|
+
className: r.class,
|
|
50
|
+
script: r.script ?? null,
|
|
51
|
+
name: r.name ?? null,
|
|
52
|
+
useSqlite: r.use_sqlite === true,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
if (batch.length < PER_PAGE)
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
return rows;
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
async list() {
|
|
62
|
+
const now = Date.now();
|
|
63
|
+
if (cached && now - cached.at < ttlMs)
|
|
64
|
+
return cached.rows;
|
|
65
|
+
if (inFlight)
|
|
66
|
+
return inFlight;
|
|
67
|
+
inFlight = walk()
|
|
68
|
+
.then((rows) => {
|
|
69
|
+
cached = { at: Date.now(), rows };
|
|
70
|
+
return rows;
|
|
71
|
+
})
|
|
72
|
+
.finally(() => {
|
|
73
|
+
inFlight = null;
|
|
74
|
+
});
|
|
75
|
+
return inFlight;
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The namespaces defined in one script, most-likely-scope-class first.
|
|
81
|
+
*
|
|
82
|
+
* A vertical's script defines several classes (the scope DO, an identity DO, sweepers), so
|
|
83
|
+
* ORDER is the useful part: the console links the first entry, and a scope's data is in the
|
|
84
|
+
* scope class. Matching is on the class name because that is the only stable signal — the
|
|
85
|
+
* namespace's `name` is Cloudflare's, and the binding name lives in the script's settings,
|
|
86
|
+
* not here. Every namespace of the script is still returned, so a vertical that named its
|
|
87
|
+
* class something else is one click away rather than unreachable.
|
|
88
|
+
*/
|
|
89
|
+
export function namespacesForScript(rows, script) {
|
|
90
|
+
const mine = rows.filter((r) => r.script === script);
|
|
91
|
+
const rank = (r) => {
|
|
92
|
+
const c = r.className.toLowerCase();
|
|
93
|
+
if (c === 'scopedo')
|
|
94
|
+
return 0;
|
|
95
|
+
if (c.includes('scope'))
|
|
96
|
+
return 1;
|
|
97
|
+
return 2;
|
|
98
|
+
};
|
|
99
|
+
return mine.sort((a, b) => rank(a) - rank(b) || a.className.localeCompare(b.className));
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=do-namespaces.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"do-namespaces.js","sourceRoot":"","sources":["../src/do-namespaces.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,13 @@ export { blobStoreBindings, collectBlobStoreHandles, collectTenantStoreHandles,
|
|
|
16
16
|
export { createCustomHostnameProvisioner, mapCfStatus, extractRecords, reconcilePendingHostnames, isCustomHostname, validateBindableHostname, } from './custom-hostnames.js';
|
|
17
17
|
export type { CustomHostnameProvisioner, CustomHostnameProvisionerOptions, CustomHostnameIssuance, ReconcileHostnamesResult, } from './custom-hostnames.js';
|
|
18
18
|
export type { ObservabilityReader, ServiceMetricsRow, RecentLogEvent } from './observability.js';
|
|
19
|
+
export type { PlatformRuntime } from './platform-runtime.js';
|
|
20
|
+
export { createCfDoNamespaceReader, namespacesForScript } from './do-namespaces.js';
|
|
21
|
+
export type { DoNamespaceReader, DoNamespaceRecord, CfDoNamespaceOptions } from './do-namespaces.js';
|
|
19
22
|
export { createCfObservabilityReader } from './cf-observability.js';
|
|
20
23
|
export type { CfObservabilityOptions } from './cf-observability.js';
|
|
24
|
+
export type { ScopeBackup, ScopeBackupStore, DirectoryBackup, DirectoryBackupStore, } from './backups.js';
|
|
25
|
+
export { createR2BackupStore, createR2DirectoryBackupStore } from './r2-backups.js';
|
|
26
|
+
export { backupDirectoryIfDue } from './directory-backup.js';
|
|
27
|
+
export type { DirectoryBackupOptions, DirectoryBackupResult } from './directory-backup.js';
|
|
21
28
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACjD,YAAY,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,2BAA2B,EAC3B,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACrG,YAAY,EACV,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EACf,WAAW,EACX,SAAS,GACV,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACpE,YAAY,EAAE,yBAAyB,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,EACrB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,EACtB,sBAAsB,EACtB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,GAC5B,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACrG,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAChG,YAAY,EACV,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EACjB,aAAa,EACb,aAAa,GACd,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,+BAA+B,EAC/B,WAAW,EACX,cAAc,EACd,yBAAyB,EACzB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,yBAAyB,EACzB,gCAAgC,EAChC,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACjG,OAAO,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AACpE,YAAY,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -8,5 +8,11 @@ export { assertSandboxContract, deployManifest, deploymentRefFor, stableDeployme
|
|
|
8
8
|
export { createWfpUploader, createWfpModulesFetcher, createWfpBindingsPatcher } from './wfp.js';
|
|
9
9
|
export { blobStoreBindings, collectBlobStoreHandles, collectTenantStoreHandles, tenantStoreBindings, } from './tenant-stores.js';
|
|
10
10
|
export { createCustomHostnameProvisioner, mapCfStatus, extractRecords, reconcilePendingHostnames, isCustomHostname, validateBindableHostname, } from './custom-hostnames.js';
|
|
11
|
+
export { createCfDoNamespaceReader, namespacesForScript } from './do-namespaces.js';
|
|
11
12
|
export { createCfObservabilityReader } from './cf-observability.js';
|
|
13
|
+
export { createR2BackupStore, createR2DirectoryBackupStore } from './r2-backups.js';
|
|
14
|
+
// #40 — the scheduled directory copy. Exported as a function the CP worker's cron calls,
|
|
15
|
+
// not as a route with a timer behind it: this package stays library-only, exactly as the
|
|
16
|
+
// platform-request drain does (the recurrence has to come from a deployment, #444).
|
|
17
|
+
export { backupDirectoryIfDue } from './directory-backup.js';
|
|
12
18
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAEjD,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,2BAA2B,EAC3B,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAUrG,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEpE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,EACrB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,EACtB,sBAAsB,GAUvB,MAAM,qBAAqB,CAAC;AAO7B,OAAO,EACL,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAQhG,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,+BAA+B,EAC/B,WAAW,EACX,cAAc,EACd,yBAAyB,EACzB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAEjD,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,2BAA2B,EAC3B,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAUrG,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEpE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,EACrB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,EACtB,sBAAsB,GAUvB,MAAM,qBAAqB,CAAC;AAO7B,OAAO,EACL,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAQhG,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,+BAA+B,EAC/B,WAAW,EACX,cAAc,EACd,yBAAyB,EACzB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAS/B,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEpF,OAAO,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AAQpE,OAAO,EAAE,mBAAmB,EAAE,4BAA4B,EAAE,MAAM,iBAAiB,CAAC;AACpF,yFAAyF;AACzF,yFAAyF;AACzF,oFAAoF;AACpF,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/mask.d.ts
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
import type { ScopeDumpTable } from '@substrat-run/contracts';
|
|
2
2
|
/** Mask one dump in place-shape (returns new arrays; never mutates the input). */
|
|
3
3
|
export declare function maskDump(tables: ScopeDumpTable[]): ScopeDumpTable[];
|
|
4
|
+
/**
|
|
5
|
+
* The same heuristic applied to plain JSON records — the directory half of a tenant
|
|
6
|
+
* export (#36).
|
|
7
|
+
*
|
|
8
|
+
* A tenant export carries two kinds of thing: scope databases (table-shaped, masked by
|
|
9
|
+
* `maskDump` above) and directory records (object-shaped — a tenant's display name, an
|
|
10
|
+
* org's name, an identity link's external id, which is usually an email). Both halves
|
|
11
|
+
* must be masked by the SAME rule, or the default-masked promise is only half true and
|
|
12
|
+
* the leak is in the half nobody looked at.
|
|
13
|
+
*
|
|
14
|
+
* So this reuses `maskJsonValue` rather than growing a second heuristic: one PII column
|
|
15
|
+
* list, one recursive sweep, two entry points. Ids and timestamps pass through — the
|
|
16
|
+
* sweep targets free text, and ids are what keep an export intelligible.
|
|
17
|
+
*/
|
|
18
|
+
export declare function maskRecords<T>(records: readonly T[]): T[];
|
|
4
19
|
//# sourceMappingURL=mask.d.ts.map
|
package/dist/mask.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mask.d.ts","sourceRoot":"","sources":["../src/mask.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"mask.d.ts","sourceRoot":"","sources":["../src/mask.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AA2D9D,kFAAkF;AAClF,wBAAgB,QAAQ,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CAqBnE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,EAAE,CAEzD"}
|
package/dist/mask.js
CHANGED
|
@@ -22,7 +22,14 @@
|
|
|
22
22
|
*/
|
|
23
23
|
// Free-text/PII column names. `name` is included on purpose: entity names
|
|
24
24
|
// (customers, properties, contacts) are customer data even when they look benign.
|
|
25
|
-
|
|
25
|
+
//
|
|
26
|
+
// `external_id` earns its place from the platform's OWN schema (#36): an identity link's
|
|
27
|
+
// `externalId` is the provider's subject, which in practice is very often the person's
|
|
28
|
+
// email address — so a tenant export that swept only the obvious columns would hand out
|
|
29
|
+
// the one field most likely to name a human. It is the deliberately lossy direction of
|
|
30
|
+
// the trade: an opaque third-party id (a document ref, an upstream order number) gets
|
|
31
|
+
// masked too, which costs a masked pull some fidelity and costs a leak nothing.
|
|
32
|
+
const PII_COLUMN = /(^|_)(email|e?mail_address|phone|mobile|tel|address|street|city|postal|zip|ssn|personnummer|name|first_name|last_name|full_name|contact|external_id|note|notes|comment|comments|message|subject|body|description)($|_)/i;
|
|
26
33
|
// Columns that carry JSON documents worth sweeping by key rather than blanking.
|
|
27
34
|
const JSON_COLUMN = /(^|_)(payload|detail|details|data|before|after)($|_)/i;
|
|
28
35
|
const MASKED = '[masked]';
|
|
@@ -68,4 +75,21 @@ export function maskDump(tables) {
|
|
|
68
75
|
return { ...t, rows };
|
|
69
76
|
});
|
|
70
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* The same heuristic applied to plain JSON records — the directory half of a tenant
|
|
80
|
+
* export (#36).
|
|
81
|
+
*
|
|
82
|
+
* A tenant export carries two kinds of thing: scope databases (table-shaped, masked by
|
|
83
|
+
* `maskDump` above) and directory records (object-shaped — a tenant's display name, an
|
|
84
|
+
* org's name, an identity link's external id, which is usually an email). Both halves
|
|
85
|
+
* must be masked by the SAME rule, or the default-masked promise is only half true and
|
|
86
|
+
* the leak is in the half nobody looked at.
|
|
87
|
+
*
|
|
88
|
+
* So this reuses `maskJsonValue` rather than growing a second heuristic: one PII column
|
|
89
|
+
* list, one recursive sweep, two entry points. Ids and timestamps pass through — the
|
|
90
|
+
* sweep targets free text, and ids are what keep an export intelligible.
|
|
91
|
+
*/
|
|
92
|
+
export function maskRecords(records) {
|
|
93
|
+
return records.map((r) => maskJsonValue(r, false));
|
|
94
|
+
}
|
|
71
95
|
//# sourceMappingURL=mask.js.map
|
package/dist/mask.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mask.js","sourceRoot":"","sources":["../src/mask.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,0EAA0E;AAC1E,kFAAkF;AAClF,MAAM,UAAU,GAAG,
|
|
1
|
+
{"version":3,"file":"mask.js","sourceRoot":"","sources":["../src/mask.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where this platform's compute and per-tenant stores physically live (#537-adjacent ops
|
|
3
|
+
* ergonomics): the coordinates a staff surface turns into a link into the provider's own
|
|
4
|
+
* console — this scope's serving script, this tenant's database, this tenant's bucket.
|
|
5
|
+
*
|
|
6
|
+
* Substrate-agnostic on purpose. The control plane already knows a scope's `servingRef`,
|
|
7
|
+
* a store's `ref`, and a version's `deploymentRef`; what it has never told anyone is
|
|
8
|
+
* WHERE those refs resolve. That is one descriptor, not a Cloudflare dependency: this
|
|
9
|
+
* package still holds no provider SDK and no credential (D-34), and the pure adapter
|
|
10
|
+
* simply has no runtime to describe (`provider` absent ⇒ the console shows plain ids).
|
|
11
|
+
*/
|
|
12
|
+
export interface PlatformRuntime {
|
|
13
|
+
/** The substrate these refs resolve in. Only Cloudflare exists today; the tag is what
|
|
14
|
+
* lets a consumer refuse to build links for a substrate it does not know. */
|
|
15
|
+
provider: 'cloudflare';
|
|
16
|
+
/** The account the platform's scripts, databases and buckets belong to. */
|
|
17
|
+
accountId: string;
|
|
18
|
+
/** The dispatch namespace pushed verticals run in (WfP) — where a `servingRef` or a
|
|
19
|
+
* version's `deploymentRef` is a script name. */
|
|
20
|
+
dispatchNamespace: string;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=platform-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform-runtime.d.ts","sourceRoot":"","sources":["../src/platform-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,eAAe;IAC9B;kFAC8E;IAC9E,QAAQ,EAAE,YAAY,CAAC;IACvB,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAC;IAClB;sDACkD;IAClD,iBAAiB,EAAE,MAAM,CAAC;CAC3B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform-runtime.js","sourceRoot":"","sources":["../src/platform-runtime.ts"],"names":[],"mappings":""}
|