@substrat-run/control-plane-api 0.136.0 → 0.137.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.
@@ -0,0 +1,234 @@
1
+ /**
2
+ * The one sanctioned reading of `outputFields` (#1923 part c): per-(operation, field) counts for
3
+ * ONE tenant, from the stamped invocation lines its app's scripts wrote, joined to the router's
4
+ * own line for each request.
5
+ *
6
+ * ## Why the reading needs rules at all
7
+ *
8
+ * The report on a line is written by the vertical's own code, like `operation` and
9
+ * `problemCode` on the router's header. A vertical can mislabel its own responses, and it can
10
+ * `console.log` a line that looks exactly like the platform's, naming any tenant id it likes.
11
+ * So nothing on a line is taken as given except what the reader resolved itself:
12
+ *
13
+ * - **The tenant is the caller's, never the line's.** `tenantId` is required, and there is no
14
+ * "every tenant" spelling.
15
+ * - **The tenant and app a report counts for are the ROUTER's.** A report counts only when
16
+ * its `fieldCoverageId` — the dispatch id the router armed the walk with — matches the
17
+ * router's own line for that request, and that line names this tenant and this app. Router
18
+ * lines are read from the router's scripts only (`ROUTER_SCRIPT_NAMES`, by
19
+ * `$metadata.service`, which the log platform stamps). A vertical cannot log under those
20
+ * names: a slug that would deploy under any platform script name is refused at
21
+ * registration and at deploy (`platformScriptCollision`), from the same list. Workers Logs
22
+ * carry no field that tells a dispatch-namespace script from a top-level worker that this
23
+ * reader could check instead, so the reserved names are the whole of that guarantee. A vertical serving tenants A and
24
+ * B that writes a report naming B during A's request has only A's id to give it, which joins
25
+ * to A; an invented id joins to nothing. Each id counts once.
26
+ * - **The script is the app's.** A vertical line counts only if one of the app's own script
27
+ * families wrote it, which keeps one app's lines out of another's tally before the join.
28
+ * - **Silence about other tenants.** A line that does not join is dropped without trace —
29
+ * not counted as refused — since a count of someone else's lines is a fact about them.
30
+ *
31
+ * What remains is the accepted residual: a vertical can mislabel the report of a response it
32
+ * actually served for that tenant, the same way it can mislabel `operation`.
33
+ * - **Names are bounded, and declared when the caller can say so.** A report is refused whole
34
+ * when it is not three string arrays, names a field twice, names more than the declared
35
+ * half's cap, or names a field longer than an operation name may be. Given the operation's
36
+ * declared output (`declared`), a report naming anything outside it is refused too, so a
37
+ * vertical cannot grow the tally with names its own declaration never had.
38
+ *
39
+ * ## Why aggregate only
40
+ *
41
+ * One report is a fact about one response, and the line it rides also carries a path that
42
+ * can name a record (`/customers/{id}`): "this customer's `phone` is null". The question field
43
+ * coverage asks is "is this field ever returned", and a count answers it without holding a
44
+ * fact about any one record. So the tally keeps counts per (operation, field) and the number
45
+ * of responses behind them, and nothing per request: no path, scope, time, invocation id or
46
+ * value. It does not pass any of those through, so a view built on it cannot either.
47
+ *
48
+ * Every count is per response serialisation, never per row: a paged read of 200 rows is one
49
+ * response (#1331).
50
+ */
51
+ import { DECLARED_OUTPUT_FIELDS_MAX, FIELD_COVERAGE_ID_FIELD, INVOCATION_RECORD_FIELD_MAX, ROUTER_SCRIPT_NAMES } from '@substrat-run/contracts';
52
+ import { ownsInvocation } from './cf-observability.js';
53
+ import { serviceFamilyMatcher } from './service-family.js';
54
+ /** A dispatch id: the ULID the router mints for a sampled request. */
55
+ const DISPATCH_ID = /^[0-9A-HJKMNP-TV-Z]{26}$/;
56
+ /** The service a raw event was logged by, as the log platform stamped it. */
57
+ function serviceOf(event) {
58
+ const metadata = event['$metadata'];
59
+ return metadata && typeof metadata === 'object' ? metadata['service'] : undefined;
60
+ }
61
+ /**
62
+ * The dispatch ids the router minted for THIS tenant's requests to THIS app, from the
63
+ * router's own request lines (`router: 'request'`). Anything not logged by a router service is
64
+ * ignored, whatever it says.
65
+ */
66
+ function dispatchIdsFor(routerEvents, scope) {
67
+ const ids = new Set();
68
+ for (const event of routerEvents) {
69
+ if (event === null || typeof event !== 'object')
70
+ continue;
71
+ const service = serviceOf(event);
72
+ if (typeof service !== 'string' || !ROUTER_SCRIPT_NAMES.includes(service))
73
+ continue;
74
+ const source = event['source'];
75
+ if (source === null || typeof source !== 'object')
76
+ continue;
77
+ const line = source;
78
+ if (line['router'] !== 'request' || line['tenantId'] !== scope.tenantId || line['vertical'] !== scope.vertical)
79
+ continue;
80
+ const id = line[FIELD_COVERAGE_ID_FIELD];
81
+ if (typeof id === 'string' && DISPATCH_ID.test(id))
82
+ ids.add(id);
83
+ }
84
+ return ids;
85
+ }
86
+ /**
87
+ * How many distinct operations one tally keeps. Past it, a report on a new operation is
88
+ * refused: a vertical cannot grow the answer without bound by inventing names.
89
+ */
90
+ export const FIELD_COVERAGE_OPERATIONS_MAX = 1000;
91
+ /** The report's buckets — the writer's own shape, so a renamed bucket is a compile error here. */
92
+ const BUCKETS = ['present', 'empty', 'absent'];
93
+ /** A report's three name lists, or `undefined` when it is not a well-formed one. */
94
+ function namesOf(report, allowed) {
95
+ if (report === null || typeof report !== 'object' || Array.isArray(report))
96
+ return undefined;
97
+ const seen = new Set();
98
+ const out = { present: [], empty: [], absent: [] };
99
+ for (const bucket of BUCKETS) {
100
+ const list = report[bucket];
101
+ if (!Array.isArray(list))
102
+ return undefined;
103
+ for (const name of list) {
104
+ if (typeof name !== 'string' || name.length === 0 || name.length > INVOCATION_RECORD_FIELD_MAX)
105
+ return undefined;
106
+ if (seen.has(name))
107
+ return undefined;
108
+ if (allowed && !allowed.has(name))
109
+ return undefined;
110
+ seen.add(name);
111
+ out[bucket].push(name);
112
+ }
113
+ }
114
+ if (seen.size === 0 || seen.size > DECLARED_OUTPUT_FIELDS_MAX)
115
+ return undefined;
116
+ return out;
117
+ }
118
+ /**
119
+ * Tally the field coverage in `events` — raw Workers Logs events (`{ source, $metadata }`), as
120
+ * the telemetry query returns them — for one tenant and one app, counting only the reports
121
+ * whose dispatch id `routerEvents` (the router's own request lines) vouches for.
122
+ *
123
+ * The query that fetched them should already filter on both; this checks them again, because
124
+ * the tally is the thing a view trusts, and a query is one forgotten parameter from fleet-wide.
125
+ */
126
+ export function tallyFieldCoverage(events, routerEvents, scope) {
127
+ if (typeof scope.tenantId !== 'string' || scope.tenantId.length === 0) {
128
+ throw new TypeError('a field-coverage tally is for one tenant, and names it');
129
+ }
130
+ if (typeof scope.vertical !== 'string' || scope.vertical.length === 0) {
131
+ throw new TypeError('a field-coverage tally is for one app, and names it');
132
+ }
133
+ const ownService = serviceFamilyMatcher(scope.services);
134
+ // #1923: an app whose own scripts would be named like the router's has no provenance to
135
+ // join to — its lines and the router's are one set. Registration and deploy refuse such a
136
+ // slug; a registry entry that predates the rule is refused here rather than trusted.
137
+ const shadowed = ROUTER_SCRIPT_NAMES.find((name) => ownService(name));
138
+ if (shadowed) {
139
+ throw new TypeError(`a field-coverage tally cannot trust router lines for an app whose scripts are named like '${shadowed}'`);
140
+ }
141
+ const dispatched = dispatchIdsFor(routerEvents, scope);
142
+ const counted = new Set();
143
+ const operations = new Map();
144
+ // Each operation's declared names as a set, built the first time a report needs it.
145
+ const declaredSets = new Map();
146
+ let refused = 0;
147
+ for (const event of events) {
148
+ if (event === null || typeof event !== 'object')
149
+ continue;
150
+ // The same isolation predicate the tenant log reads use: a stamped line naming this tenant.
151
+ if (!ownsInvocation(event, { tenantId: scope.tenantId }))
152
+ continue;
153
+ const line = event.source;
154
+ if (!ownService(serviceOf(event)))
155
+ continue;
156
+ // A line with no report was not walked (unarmed, failed, or nothing declared): no data.
157
+ if (!('outputFields' in line))
158
+ continue;
159
+ // The provenance: the router minted this id for this tenant's request to this app. A line
160
+ // that does not join is not this tenant's as far as anything trusted says, and is dropped
161
+ // as silently as a line naming another tenant.
162
+ const id = line[FIELD_COVERAGE_ID_FIELD];
163
+ if (typeof id !== 'string' || !dispatched.has(id))
164
+ continue;
165
+ // From here the line is this tenant's, from this app: a bad report is the app's own fault,
166
+ // and is counted as refused rather than ignored. So is a second report on one dispatch id:
167
+ // one request is one response, and the first report on it is the one that counts.
168
+ if (counted.has(id)) {
169
+ refused++;
170
+ continue;
171
+ }
172
+ counted.add(id);
173
+ const operation = line['operation'];
174
+ // An async line (`kind`) has no response to report on, whatever it says.
175
+ if (line['kind'] !== undefined || typeof operation !== 'string' || operation.length === 0 || operation.length > INVOCATION_RECORD_FIELD_MAX) {
176
+ refused++;
177
+ continue;
178
+ }
179
+ let allowed;
180
+ if (scope.declared) {
181
+ if (!Object.hasOwn(scope.declared, operation)) {
182
+ refused++;
183
+ continue;
184
+ }
185
+ allowed = declaredSets.get(operation);
186
+ if (!allowed)
187
+ declaredSets.set(operation, (allowed = new Set(scope.declared[operation])));
188
+ }
189
+ const names = namesOf(line['outputFields'], allowed);
190
+ if (!names) {
191
+ refused++;
192
+ continue;
193
+ }
194
+ let entry = operations.get(operation);
195
+ if (!entry) {
196
+ if (operations.size >= FIELD_COVERAGE_OPERATIONS_MAX) {
197
+ refused++;
198
+ continue;
199
+ }
200
+ entry = { responses: 0, fields: new Map() };
201
+ }
202
+ // The field cap holds per operation across reports too, so a vertical naming a different
203
+ // field each time cannot grow one operation past what one declaration could hold.
204
+ let fresh = 0;
205
+ for (const bucket of BUCKETS)
206
+ for (const field of names[bucket])
207
+ if (!entry.fields.has(field))
208
+ fresh++;
209
+ if (entry.fields.size + fresh > DECLARED_OUTPUT_FIELDS_MAX) {
210
+ refused++;
211
+ continue;
212
+ }
213
+ operations.set(operation, entry);
214
+ entry.responses++;
215
+ for (const bucket of BUCKETS) {
216
+ for (const field of names[bucket]) {
217
+ let count = entry.fields.get(field);
218
+ if (!count) {
219
+ count = { field, present: 0, empty: 0, absent: 0 };
220
+ entry.fields.set(field, count);
221
+ }
222
+ count[bucket]++;
223
+ }
224
+ }
225
+ }
226
+ return {
227
+ tenantId: scope.tenantId,
228
+ operations: [...operations]
229
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
230
+ .map(([operation, e]) => ({ operation, responses: e.responses, fields: [...e.fields.values()] })),
231
+ refused,
232
+ };
233
+ }
234
+ //# sourceMappingURL=field-coverage-tally.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-coverage-tally.js","sourceRoot":"","sources":["../src/field-coverage-tally.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -37,6 +37,8 @@ export type { PlatformRuntime } from './platform-runtime.js';
37
37
  export { createCfDoNamespaceReader, namespacesForScript } from './do-namespaces.js';
38
38
  export type { DoNamespaceReader, DoNamespaceRecord, CfDoNamespaceOptions } from './do-namespaces.js';
39
39
  export { createCfObservabilityReader } from './cf-observability.js';
40
+ export { tallyFieldCoverage, FIELD_COVERAGE_OPERATIONS_MAX } from './field-coverage-tally.js';
41
+ export type { FieldCoverageTally, FieldCoverageScope, OperationFieldCoverage, FieldCoverageCount } from './field-coverage-tally.js';
40
42
  export { cachedSource, memoryCubeStore, grainFor, blockMsFor, CLOSE_LAG_MS } from './aggregate-source.js';
41
43
  export type { AggregateSource, Cube, CubeQuery, CubeStore, StoredBlock, RequestCubeRow, PatternCubeRow } from './aggregate-source.js';
42
44
  export { aggregateReads } from './aggregate-reads.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -29,6 +29,8 @@ export { TENANT_SERIES_SCOPE_CAP, TENANT_METRICS_LIMIT, REQUEST_FACET_KEYS, REQU
29
29
  export { STORAGE_PAGE_DEFAULT, STORAGE_PAGE_MAX, STORAGE_READ_CONCURRENCY, readStoragePage, } from './storage-meter.js';
30
30
  export { createCfDoNamespaceReader, namespacesForScript } from './do-namespaces.js';
31
31
  export { createCfObservabilityReader } from './cf-observability.js';
32
+ // #1923: the one sanctioned reading of a line's `outputFields` — tenant- and app-scoped, aggregate only.
33
+ export { tallyFieldCoverage, FIELD_COVERAGE_OPERATIONS_MAX } from './field-coverage-tally.js';
32
34
  // #1877: the aggregate seam under ObservabilityReader, and the store its cache keeps
33
35
  // closed blocks in (a Durable Object's SQL in production).
34
36
  export { cachedSource, memoryCubeStore, grainFor, blockMsFor, CLOSE_LAG_MS } from './aggregate-source.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,2BAA2B,EAC3B,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,EAChB,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AACjH,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAEzE,OAAO,EACL,yBAAyB,GAG1B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACrG,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAYxF,uFAAuF;AACvF,iFAAiF;AACjF,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,gGAAgG;AAChG,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,uFAAuF;AACvF,wEAAwE;AACxE,OAAO,EAAE,uBAAuB,EAAE,0BAA0B,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGxH,OAAO,EACL,0BAA0B,EAC1B,6BAA6B,EAC7B,qBAAqB,EACrB,uBAAuB,EACvB,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EACtB,sBAAsB,EACtB,iBAAiB,EACjB,gBAAgB,EAChB,wBAAwB,GAczB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAE5E,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACjF,qEAAqE;AACrE,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,mBAAmB,EACnB,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC;AAO7B,OAAO,EACL,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAUrB,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAChG,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAQ7B,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;AA2B/B,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,wBAAwB,EACxB,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEpF,OAAO,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AACpE,yGAAyG;AACzG,OAAO,EAAE,kBAAkB,EAAE,6BAA6B,EAAE,MAAM,2BAA2B,CAAC;AAE9F,qFAAqF;AACrF,2DAA2D;AAC3D,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1G,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAStE,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,4BAA4B;AAC5B,iFAAiF;AACjF,mFAAmF;AACnF,gFAAgF;AAChF,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AACzB,kFAAkF;AAClF,iFAAiF;AACjF,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,yFAAyF;AACzF,yFAAyF;AACzF,oFAAoF;AACpF,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAE7D,uFAAuF;AACvF,yFAAyF;AACzF,uFAAuF;AACvF,yFAAyF;AACzF,8EAA8E;AAC9E,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAG3E,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC"}
@@ -7,7 +7,7 @@
7
7
  export declare const PLATFORM_ENTRY_MODULE = "substrat-platform-entry.js";
8
8
  /** The import specifier the uploader replaces with `./<the bundle's entry>`. */
9
9
  export declare const PLATFORM_ENTRY_PLACEHOLDER = "__SUBSTRAT_VERTICAL_ENTRY__";
10
- /** The entry itself — 9378 bytes of ESM. */
10
+ /** The entry itself — 10039 bytes of ESM. */
11
11
  export declare const PLATFORM_ENTRY_SOURCE: string;
12
12
  /**
13
13
  * The scope sweeper the platform supplies (#1902) — uploaded beside the entry, which
@@ -1 +1 @@
1
- {"version":3,"file":"platform-entry.generated.d.ts","sourceRoot":"","sources":["../src/platform-entry.generated.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,+BAA+B,CAAC;AAElE,gFAAgF;AAChF,eAAO,MAAM,0BAA0B,gCAAgC,CAAC;AAExE,4CAA4C;AAC5C,eAAO,MAAM,qBAAqB,EAAE,MAA6iT,CAAC;AAEllT;;;GAGG;AACH,eAAO,MAAM,uBAAuB,iCAAiC,CAAC;AAEtE,8CAA8C;AAC9C,eAAO,MAAM,uBAAuB,EAAE,MAAuxT,CAAC"}
1
+ {"version":3,"file":"platform-entry.generated.d.ts","sourceRoot":"","sources":["../src/platform-entry.generated.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,+BAA+B,CAAC;AAElE,gFAAgF;AAChF,eAAO,MAAM,0BAA0B,gCAAgC,CAAC;AAExE,6CAA6C;AAC7C,eAAO,MAAM,qBAAqB,EAAE,MAAktU,CAAC;AAEvvU;;;GAGG;AACH,eAAO,MAAM,uBAAuB,iCAAiC,CAAC;AAEtE,8CAA8C;AAC9C,eAAO,MAAM,uBAAuB,EAAE,MAAuxT,CAAC"}
@@ -9,8 +9,8 @@
9
9
  export const PLATFORM_ENTRY_MODULE = 'substrat-platform-entry.js';
10
10
  /** The import specifier the uploader replaces with `./<the bundle's entry>`. */
11
11
  export const PLATFORM_ENTRY_PLACEHOLDER = '__SUBSTRAT_VERTICAL_ENTRY__';
12
- /** The entry itself — 9378 bytes of ESM. */
13
- export const PLATFORM_ENTRY_SOURCE = "// packages/contracts/dist/invocation-record.js\nvar INVOCATION_RECORD_HEADER = \"x-substrat-invocation-record\";\nvar INVOCATION_RECORD_FIELD_MAX = 128;\nvar FIELDS = [\"operation\", \"problemCode\", \"principalKind\"];\nfunction encodeInvocationRecord(record) {\n const params = new URLSearchParams();\n for (const key of FIELDS) {\n const v = record[key];\n if (typeof v === \"string\" && v !== \"\")\n params.set(key, v.slice(0, INVOCATION_RECORD_FIELD_MAX));\n }\n const value = params.toString();\n return value === \"\" ? null : value;\n}\nfunction invocationLevelOf(status, threw, problemCode) {\n if (threw || status === null || status >= 500)\n return \"error\";\n if (status >= 400)\n return \"warn\";\n if (problemCode)\n return \"warn\";\n return \"info\";\n}\n\n// packages/kernel/src/ulid.ts\nvar B32 = \"0123456789ABCDEFGHJKMNPQRSTVWXYZ\";\nvar MAX_ULID_TIME = 2 ** 48 - 1;\nvar ULID_SHAPE = /^[0-9A-HJKMNP-TV-Z]{26}$/;\nvar unencodable = (t) => `not an encodable ULID instant: ${t} (want 0..${MAX_ULID_TIME})`;\nfunction encodeTime(time) {\n let ts = \"\";\n let t = time;\n for (let i = 0; i < 10; i++) {\n ts = B32[t % 32] + ts;\n t = Math.floor(t / 32);\n }\n return ts;\n}\nfunction createUlid() {\n let lastTime = -1;\n const lastRand = new Array(16).fill(0);\n function freshRandom() {\n const bytes = crypto.getRandomValues(new Uint8Array(16));\n for (let i = 0; i < 16; i++) lastRand[i] = bytes[i] % 32;\n }\n function incrementRandom() {\n for (let i = 15; i >= 0; i--) {\n if (lastRand[i] < 31) {\n lastRand[i]++;\n return true;\n }\n lastRand[i] = 0;\n }\n return false;\n }\n const mint = ((now = Date.now()) => {\n if (!Number.isSafeInteger(now)) throw new RangeError(unencodable(now));\n let time = now;\n if (time <= lastTime) {\n time = lastTime;\n if (!incrementRandom()) {\n time = lastTime + 1;\n freshRandom();\n }\n } else {\n freshRandom();\n }\n if (time < 0 || time > MAX_ULID_TIME) throw new RangeError(unencodable(time));\n lastTime = time;\n let r = \"\";\n for (const d of lastRand) r += B32[d];\n return encodeTime(time) + r;\n });\n mint.seedFrom = (id) => {\n const time = ulidTime(id);\n if (time < lastTime) return;\n const rand = [];\n for (const ch of id.slice(10)) rand.push(B32.indexOf(ch));\n if (time === lastTime) {\n let ahead = false;\n for (let i = 0; i < 16; i++) {\n if (rand[i] !== lastRand[i]) {\n ahead = rand[i] > lastRand[i];\n break;\n }\n }\n if (!ahead) return;\n }\n lastTime = time;\n for (let i = 0; i < 16; i++) lastRand[i] = rand[i];\n };\n return mint;\n}\nvar processUlid = createUlid();\nfunction ulid(now = Date.now()) {\n return processUlid(now);\n}\nfunction ulidTime(id) {\n if (!ULID_SHAPE.test(id)) throw new Error(`not a ULID: ${id}`);\n let t = 0;\n for (const ch of id.slice(0, 10)) t = t * 32 + B32.indexOf(ch);\n if (t > MAX_ULID_TIME) throw new Error(`not a ULID: ${id}`);\n return t;\n}\n\n// packages/kernel/src/platform-call.ts\nfunction secretMatches(presented, expected) {\n if (!presented || presented.length !== expected.length) return false;\n let diff = 0;\n for (let i = 0; i < expected.length; i++) {\n diff |= presented.charCodeAt(i) ^ expected.charCodeAt(i);\n }\n return diff === 0;\n}\n\n// packages/kernel/src/routed-node.ts\nvar ROUTED_ID = /^[0-9A-HJKMNP-TV-Z]{26}$/;\nvar RouterAssertionError = class extends Error {\n};\nfunction readRoutedNode(headers, options = {}) {\n const rawTenant = headers.get(\"x-substrat-tenant\");\n const rawScope = headers.get(\"x-substrat-scope\");\n if (!rawTenant && !rawScope) return null;\n const { expectedSecret, allowUnsigned = false } = options;\n if (expectedSecret) {\n if (!secretMatches(headers.get(\"x-substrat-router\"), expectedSecret)) {\n throw new RouterAssertionError(\"router assertion is not signed by a known router\");\n }\n } else if (!allowUnsigned) {\n throw new RouterAssertionError(\n \"router assertion cannot be verified: this worker has no ROUTER_SECRET configured\"\n );\n }\n if (!rawTenant || !rawScope) {\n throw new RouterAssertionError(\"router assertion is incomplete\");\n }\n if (!ROUTED_ID.test(rawTenant) || !ROUTED_ID.test(rawScope)) {\n throw new RouterAssertionError(\"router assertion carries a malformed id\");\n }\n return {\n tenantId: rawTenant,\n scopeId: rawScope,\n surface: headers.get(\"x-substrat-surface\") ?? \"app\",\n verticalSlug: headers.get(\"x-substrat-vertical\")\n };\n}\n\n// packages/kernel/src/invocation-log.ts\nfunction invocationLine(f) {\n const emitted = f.emitted;\n return {\n substrat: \"invocation\",\n ...f.kind ? { kind: f.kind } : {},\n tenantId: f.tenantId,\n scopeId: f.scopeId,\n vertical: f.vertical ?? null,\n surface: f.surface ?? null,\n method: f.method ?? null,\n path: f.path ?? null,\n status: f.status ?? null,\n threw: f.threw,\n durationMs: f.durationMs,\n invocationId: f.invocationId,\n level: f.level,\n operation: f.operation ?? null,\n problemCode: f.problemCode ?? null,\n principalKind: f.principalKind ?? null,\n eventCount: emitted ? emitted.total : null,\n eventTypes: emitted ? distinct(emitted.events.map((e) => e.type)) : [],\n entities: emitted && f.withEntities !== false ? distinct(emitted.events.map((e) => e.entity)) : [],\n versionId: f.versionId ?? null,\n ...f.outputFields ? { outputFields: f.outputFields } : {},\n ...f.async ?? {}\n };\n}\nfunction distinct(values) {\n return [...new Set(values)];\n}\nfunction versionIdOf(env) {\n const v = env?.SUBSTRAT_VERSION_ID;\n return typeof v === \"string\" && v.length > 0 ? v : null;\n}\nfunction pathOf(url) {\n try {\n return new URL(url).pathname;\n } catch {\n return \"/\";\n }\n}\nvar STAMPS = /* @__PURE__ */ Symbol.for(\"substrat.invocation-stamp\");\nfunction stamps() {\n const g = globalThis;\n return g[STAMPS] ??= /* @__PURE__ */ new WeakMap();\n}\nfunction invocationStampOf(request) {\n return stamps().get(request);\n}\nfunction beginStamp(request) {\n const stamp = { invocationId: ulid(), record: {} };\n stamps().set(request, stamp);\n return stamp;\n}\nfunction writeInvocationLine(stamp, done, options) {\n try {\n writeLineOrThrow(stamp, done, options);\n } catch (e) {\n try {\n console.log(JSON.stringify({ substrat: \"invocation-log-fault\", detail: String(e) }));\n } catch {\n }\n }\n}\nfunction writeLineOrThrow(stamp, done, options) {\n const node = routedNodeOrNull(done.request.headers, done.env, options);\n if (!node) return;\n const { record, invocationId } = stamp;\n const line = invocationLine({\n tenantId: node.tenantId,\n scopeId: node.scopeId,\n vertical: node.verticalSlug,\n surface: node.surface,\n method: done.request.method,\n path: pathOf(done.request.url),\n status: done.status,\n threw: done.threw,\n durationMs: Date.now() - done.started,\n invocationId,\n level: invocationLevelOf(done.status, done.threw, record.problemCode),\n operation: record.operation,\n problemCode: record.problemCode,\n principalKind: record.principalKind,\n ...record.emitted ? { emitted: record.emitted } : {},\n versionId: versionIdOf(done.env),\n ...record.outputFields ? { outputFields: record.outputFields } : {}\n });\n console.log(JSON.stringify(line));\n}\nvar OTHER_HANDLERS = [\"scheduled\", \"queue\", \"email\", \"tail\", \"trace\", \"alarm\", \"test\"];\nfunction withInvocationLog(worker, options = {}) {\n const inner = worker?.fetch;\n if (typeof inner !== \"function\") return worker;\n const wrapped = {\n async fetch(request, env, ctx) {\n if (invocationStampOf(request)) return inner.call(worker, request, env, ctx);\n const started = Date.now();\n const stamp = beginStamp(request);\n let status = null;\n let threw = false;\n try {\n const response = await inner.call(worker, request, env, ctx);\n status = response.status;\n handRecordToRouter(response, stamp.record, request, env, options);\n return response;\n } catch (e) {\n threw = true;\n throw e;\n } finally {\n writeInvocationLine(stamp, { request, env, status, threw, started }, options);\n }\n }\n };\n for (const name of OTHER_HANDLERS) {\n const h = worker[name];\n if (typeof h === \"function\") wrapped[name] = h.bind(worker);\n }\n return wrapped;\n}\nfunction handRecordToRouter(response, record, request, env, options) {\n try {\n const value = encodeInvocationRecord(record);\n if (value === null || !routedNodeOrNull(request.headers, env, options)) return;\n response.headers?.set?.(\n INVOCATION_RECORD_HEADER,\n value\n );\n } catch {\n }\n}\nfunction routedNodeOrNull(headers, env, options) {\n try {\n return readRoutedNode(headers, {\n expectedSecret: options.routerSecret?.(env),\n allowUnsigned: options.allowUnsigned?.(env) ?? false\n });\n } catch (e) {\n if (!(e instanceof RouterAssertionError)) console.log(JSON.stringify({ substrat: \"invocation-log-fault\", detail: String(e) }));\n return null;\n }\n}\n\n// substrat-platform-entry.ts\nimport * as vertical from \"__SUBSTRAT_VERTICAL_ENTRY__\";\nexport * from \"__SUBSTRAT_VERTICAL_ENTRY__\";\nvar substrat_platform_entry_default = withInvocationLog(vertical.default, {\n routerSecret: (env) => env.ROUTER_SECRET,\n allowUnsigned: (env) => env.ALLOW_DEV_NODE === \"true\"\n});\nexport {\n substrat_platform_entry_default as default\n};\n";
12
+ /** The entry itself — 10039 bytes of ESM. */
13
+ export const PLATFORM_ENTRY_SOURCE = "// packages/contracts/dist/invocation-record.js\nvar INVOCATION_RECORD_HEADER = \"x-substrat-invocation-record\";\nvar INVOCATION_RECORD_FIELD_MAX = 128;\nvar FIELDS = [\"operation\", \"problemCode\", \"principalKind\"];\nfunction encodeInvocationRecord(record) {\n const params = new URLSearchParams();\n for (const key of FIELDS) {\n const v = record[key];\n if (typeof v === \"string\" && v !== \"\")\n params.set(key, v.slice(0, INVOCATION_RECORD_FIELD_MAX));\n }\n const value = params.toString();\n return value === \"\" ? null : value;\n}\nvar FIELD_COVERAGE_HEADER = \"x-substrat-field-coverage\";\nfunction invocationLevelOf(status, threw, problemCode) {\n if (threw || status === null || status >= 500)\n return \"error\";\n if (status >= 400)\n return \"warn\";\n if (problemCode)\n return \"warn\";\n return \"info\";\n}\n\n// packages/kernel/src/ulid.ts\nvar B32 = \"0123456789ABCDEFGHJKMNPQRSTVWXYZ\";\nvar MAX_ULID_TIME = 2 ** 48 - 1;\nvar ULID_SHAPE = /^[0-9A-HJKMNP-TV-Z]{26}$/;\nvar unencodable = (t) => `not an encodable ULID instant: ${t} (want 0..${MAX_ULID_TIME})`;\nfunction encodeTime(time) {\n let ts = \"\";\n let t = time;\n for (let i = 0; i < 10; i++) {\n ts = B32[t % 32] + ts;\n t = Math.floor(t / 32);\n }\n return ts;\n}\nfunction createUlid() {\n let lastTime = -1;\n const lastRand = new Array(16).fill(0);\n function freshRandom() {\n const bytes = crypto.getRandomValues(new Uint8Array(16));\n for (let i = 0; i < 16; i++) lastRand[i] = bytes[i] % 32;\n }\n function incrementRandom() {\n for (let i = 15; i >= 0; i--) {\n if (lastRand[i] < 31) {\n lastRand[i]++;\n return true;\n }\n lastRand[i] = 0;\n }\n return false;\n }\n const mint = ((now = Date.now()) => {\n if (!Number.isSafeInteger(now)) throw new RangeError(unencodable(now));\n let time = now;\n if (time <= lastTime) {\n time = lastTime;\n if (!incrementRandom()) {\n time = lastTime + 1;\n freshRandom();\n }\n } else {\n freshRandom();\n }\n if (time < 0 || time > MAX_ULID_TIME) throw new RangeError(unencodable(time));\n lastTime = time;\n let r = \"\";\n for (const d of lastRand) r += B32[d];\n return encodeTime(time) + r;\n });\n mint.seedFrom = (id) => {\n const time = ulidTime(id);\n if (time < lastTime) return;\n const rand = [];\n for (const ch of id.slice(10)) rand.push(B32.indexOf(ch));\n if (time === lastTime) {\n let ahead = false;\n for (let i = 0; i < 16; i++) {\n if (rand[i] !== lastRand[i]) {\n ahead = rand[i] > lastRand[i];\n break;\n }\n }\n if (!ahead) return;\n }\n lastTime = time;\n for (let i = 0; i < 16; i++) lastRand[i] = rand[i];\n };\n return mint;\n}\nvar processUlid = createUlid();\nfunction ulid(now = Date.now()) {\n return processUlid(now);\n}\nfunction ulidTime(id) {\n if (!ULID_SHAPE.test(id)) throw new Error(`not a ULID: ${id}`);\n let t = 0;\n for (const ch of id.slice(0, 10)) t = t * 32 + B32.indexOf(ch);\n if (t > MAX_ULID_TIME) throw new Error(`not a ULID: ${id}`);\n return t;\n}\n\n// packages/kernel/src/platform-call.ts\nfunction secretMatches(presented, expected) {\n if (!presented || presented.length !== expected.length) return false;\n let diff = 0;\n for (let i = 0; i < expected.length; i++) {\n diff |= presented.charCodeAt(i) ^ expected.charCodeAt(i);\n }\n return diff === 0;\n}\n\n// packages/kernel/src/routed-node.ts\nvar ROUTED_ID = /^[0-9A-HJKMNP-TV-Z]{26}$/;\nvar RouterAssertionError = class extends Error {\n};\nfunction readRoutedNode(headers, options = {}) {\n const rawTenant = headers.get(\"x-substrat-tenant\");\n const rawScope = headers.get(\"x-substrat-scope\");\n if (!rawTenant && !rawScope) return null;\n const { expectedSecret, allowUnsigned = false } = options;\n if (expectedSecret) {\n if (!secretMatches(headers.get(\"x-substrat-router\"), expectedSecret)) {\n throw new RouterAssertionError(\"router assertion is not signed by a known router\");\n }\n } else if (!allowUnsigned) {\n throw new RouterAssertionError(\n \"router assertion cannot be verified: this worker has no ROUTER_SECRET configured\"\n );\n }\n if (!rawTenant || !rawScope) {\n throw new RouterAssertionError(\"router assertion is incomplete\");\n }\n if (!ROUTED_ID.test(rawTenant) || !ROUTED_ID.test(rawScope)) {\n throw new RouterAssertionError(\"router assertion carries a malformed id\");\n }\n return {\n tenantId: rawTenant,\n scopeId: rawScope,\n surface: headers.get(\"x-substrat-surface\") ?? \"app\",\n verticalSlug: headers.get(\"x-substrat-vertical\")\n };\n}\n\n// packages/kernel/src/invocation-log.ts\nfunction invocationLine(f) {\n const emitted = f.emitted;\n return {\n substrat: \"invocation\",\n ...f.kind ? { kind: f.kind } : {},\n tenantId: f.tenantId,\n scopeId: f.scopeId,\n vertical: f.vertical ?? null,\n surface: f.surface ?? null,\n method: f.method ?? null,\n path: f.path ?? null,\n status: f.status ?? null,\n threw: f.threw,\n durationMs: f.durationMs,\n invocationId: f.invocationId,\n level: f.level,\n operation: f.operation ?? null,\n problemCode: f.problemCode ?? null,\n principalKind: f.principalKind ?? null,\n eventCount: emitted ? emitted.total : null,\n eventTypes: emitted ? distinct(emitted.events.map((e) => e.type)) : [],\n entities: emitted && f.withEntities !== false ? distinct(emitted.events.map((e) => e.entity)) : [],\n versionId: f.versionId ?? null,\n ...f.outputFields ? { outputFields: f.outputFields, ...f.fieldCoverageId ? { fieldCoverageId: f.fieldCoverageId } : {} } : {},\n ...f.async ?? {}\n };\n}\nfunction distinct(values) {\n return [...new Set(values)];\n}\nfunction versionIdOf(env) {\n const v = env?.SUBSTRAT_VERSION_ID;\n return typeof v === \"string\" && v.length > 0 ? v : null;\n}\nfunction pathOf(url) {\n try {\n return new URL(url).pathname;\n } catch {\n return \"/\";\n }\n}\nvar STAMPS = /* @__PURE__ */ Symbol.for(\"substrat.invocation-stamp\");\nfunction stamps() {\n const g = globalThis;\n return g[STAMPS] ??= /* @__PURE__ */ new WeakMap();\n}\nfunction invocationStampOf(request) {\n return stamps().get(request);\n}\nfunction beginStamp(request, env, options) {\n const stamp = { invocationId: ulid(), record: {} };\n const fieldCoverageId = armedFieldCoverageId(request.headers, env, options);\n if (fieldCoverageId) stamp.fieldCoverageId = fieldCoverageId;\n stamps().set(request, stamp);\n return stamp;\n}\nfunction armedFieldCoverageId(headers, env, options) {\n let id;\n try {\n id = headers.get(FIELD_COVERAGE_HEADER);\n } catch {\n return void 0;\n }\n if (id === null || !ROUTED_ID.test(id)) return void 0;\n return routedNodeOrNull(headers, env, options) !== null ? id : void 0;\n}\nfunction writeInvocationLine(stamp, done, options) {\n try {\n writeLineOrThrow(stamp, done, options);\n } catch (e) {\n try {\n console.log(JSON.stringify({ substrat: \"invocation-log-fault\", detail: String(e) }));\n } catch {\n }\n }\n}\nfunction writeLineOrThrow(stamp, done, options) {\n const node = routedNodeOrNull(done.request.headers, done.env, options);\n if (!node) return;\n const { record, invocationId } = stamp;\n const line = invocationLine({\n tenantId: node.tenantId,\n scopeId: node.scopeId,\n vertical: node.verticalSlug,\n surface: node.surface,\n method: done.request.method,\n path: pathOf(done.request.url),\n status: done.status,\n threw: done.threw,\n durationMs: Date.now() - done.started,\n invocationId,\n level: invocationLevelOf(done.status, done.threw, record.problemCode),\n operation: record.operation,\n problemCode: record.problemCode,\n principalKind: record.principalKind,\n ...record.emitted ? { emitted: record.emitted } : {},\n versionId: versionIdOf(done.env),\n ...record.outputFields ? { outputFields: record.outputFields } : {},\n ...stamp.fieldCoverageId ? { fieldCoverageId: stamp.fieldCoverageId } : {}\n });\n console.log(JSON.stringify(line));\n}\nvar OTHER_HANDLERS = [\"scheduled\", \"queue\", \"email\", \"tail\", \"trace\", \"alarm\", \"test\"];\nfunction withInvocationLog(worker, options = {}) {\n const inner = worker?.fetch;\n if (typeof inner !== \"function\") return worker;\n const wrapped = {\n async fetch(request, env, ctx) {\n if (invocationStampOf(request)) return inner.call(worker, request, env, ctx);\n const started = Date.now();\n const stamp = beginStamp(request, env, options);\n let status = null;\n let threw = false;\n try {\n const response = await inner.call(worker, request, env, ctx);\n status = response.status;\n handRecordToRouter(response, stamp.record, request, env, options);\n return response;\n } catch (e) {\n threw = true;\n throw e;\n } finally {\n writeInvocationLine(stamp, { request, env, status, threw, started }, options);\n }\n }\n };\n for (const name of OTHER_HANDLERS) {\n const h = worker[name];\n if (typeof h === \"function\") wrapped[name] = h.bind(worker);\n }\n return wrapped;\n}\nfunction handRecordToRouter(response, record, request, env, options) {\n try {\n const value = encodeInvocationRecord(record);\n if (value === null || !routedNodeOrNull(request.headers, env, options)) return;\n response.headers?.set?.(\n INVOCATION_RECORD_HEADER,\n value\n );\n } catch {\n }\n}\nfunction routedNodeOrNull(headers, env, options) {\n try {\n return readRoutedNode(headers, {\n expectedSecret: options.routerSecret?.(env),\n allowUnsigned: options.allowUnsigned?.(env) ?? false\n });\n } catch (e) {\n if (!(e instanceof RouterAssertionError)) console.log(JSON.stringify({ substrat: \"invocation-log-fault\", detail: String(e) }));\n return null;\n }\n}\n\n// substrat-platform-entry.ts\nimport * as vertical from \"__SUBSTRAT_VERTICAL_ENTRY__\";\nexport * from \"__SUBSTRAT_VERTICAL_ENTRY__\";\nvar substrat_platform_entry_default = withInvocationLog(vertical.default, {\n routerSecret: (env) => env.ROUTER_SECRET,\n allowUnsigned: (env) => env.ALLOW_DEV_NODE === \"true\"\n});\nexport {\n substrat_platform_entry_default as default\n};\n";
14
14
  /**
15
15
  * The scope sweeper the platform supplies (#1902) — uploaded beside the entry, which
16
16
  * re-exports its class, when a vertical declares schedules and brings no sweeper of its own.
@@ -1 +1 @@
1
- {"version":3,"file":"platform-entry.generated.js","sourceRoot":"","sources":["../src/platform-entry.generated.ts"],"names":[],"mappings":"AAAA,uKAAuK;AACvK,sEAAsE;AAEtE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,4BAA4B,CAAC;AAElE,gFAAgF;AAChF,MAAM,CAAC,MAAM,0BAA0B,GAAG,6BAA6B,CAAC;AAExE,4CAA4C;AAC5C,MAAM,CAAC,MAAM,qBAAqB,GAAW,oiTAAoiT,CAAC;AAEllT;;;GAGG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,8BAA8B,CAAC;AAEtE,8CAA8C;AAC9C,MAAM,CAAC,MAAM,uBAAuB,GAAW,8wTAA8wT,CAAC"}
1
+ {"version":3,"file":"platform-entry.generated.js","sourceRoot":"","sources":["../src/platform-entry.generated.ts"],"names":[],"mappings":"AAAA,uKAAuK;AACvK,sEAAsE;AAEtE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,4BAA4B,CAAC;AAElE,gFAAgF;AAChF,MAAM,CAAC,MAAM,0BAA0B,GAAG,6BAA6B,CAAC;AAExE,6CAA6C;AAC7C,MAAM,CAAC,MAAM,qBAAqB,GAAW,ysUAAysU,CAAC;AAEvvU;;;GAGG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,8BAA8B,CAAC;AAEtE,8CAA8C;AAC9C,MAAM,CAAC,MAAM,uBAAuB,GAAW,8wTAA8wT,CAAC"}
@@ -48,12 +48,19 @@ export declare function reconcilePayloadFor(admin: ReconcileGatherAdmin, actor:
48
48
  */
49
49
  export interface SwitchCarry {
50
50
  switchedOff?: ModuleId[];
51
+ /**
52
+ * Of `switchedOff`, the modules the tenant holds a live tenant-level grant for (#1823). Such a
53
+ * module has nothing in the scope's storage, so the deployment's in-unit OFF finds it held
54
+ * only because the platform says so. Absent when none is.
55
+ */
56
+ tenantHeld?: ModuleId[];
51
57
  }
52
58
  /**
53
59
  * Read the record for one scope, for `SwitchCarry`. The fleet read, narrowed to this scope
54
- * and `off`; access-logged like the gather's other reads.
60
+ * and `off`, and of those the modules the tenant holds a tenant-level grant for (#1823);
61
+ * access-logged like the gather's other reads.
55
62
  */
56
- export declare function switchCarryFor(admin: Pick<HostAdmin, 'listSystemSwitches'>, actor: PlatformActorId, node: {
63
+ export declare function switchCarryFor(admin: Pick<HostAdmin, 'listSystemSwitches' | 'tenantHeldSystemModules'>, actor: PlatformActorId, node: {
57
64
  tenantId: TenantId;
58
65
  scopeId: ScopeId;
59
66
  }): Promise<SwitchCarry>;
@@ -76,7 +83,7 @@ export declare function switchCarryFor(admin: Pick<HostAdmin, 'listSystemSwitche
76
83
  */
77
84
  export declare function reconcileThenReassert<T extends {
78
85
  switchedOff?: SwitchedOffInUnit[];
79
- } | 'unsupported'>(admin: Pick<HostAdmin, 'reassertSystemSwitches' | 'listSystemSwitches'>, actor: PlatformActorId, node: {
86
+ } | 'unsupported'>(admin: Pick<HostAdmin, 'reassertSystemSwitches' | 'listSystemSwitches' | 'tenantHeldSystemModules'>, actor: PlatformActorId, node: {
80
87
  tenantId: TenantId;
81
88
  scopeId: ScopeId;
82
89
  }, reconcile: (carry: SwitchCarry) => Promise<T>): Promise<T>;
@@ -1 +1 @@
1
- {"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../src/reconcile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EACV,QAAQ,EACR,eAAe,EACf,OAAO,EACP,iBAAiB,EACjB,QAAQ,EACT,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAGtD,4FAA4F;AAC5F,MAAM,WAAW,oBAAoB;IACnC,gBAAgB,EAAE,CAAC,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACrF,iBAAiB,EAAE,CACjB,KAAK,EAAE,eAAe,EACtB,QAAQ,EAAE,QAAQ,KACf,OAAO,CAAC;QAAE,QAAQ,EAAE,QAAQ,CAAA;KAAE,EAAE,CAAC,CAAC;IACvC,oBAAoB,EAAE,CAAC,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACzF,qBAAqB,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;CACrF;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,OAAO,EAAE,CAAC;IACxB,aAAa,EAAE,OAAO,EAAE,CAAC;IACzB,gBAAgB,EAAE,OAAO,EAAE,CAAC;IAC5B,cAAc,EAAE,OAAO,EAAE,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,oBAAoB,EAC3B,KAAK,EAAE,eAAe,EACtB,KAAK,EAAE;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAClE,OAAO,CAAC,gBAAgB,CAAC,CAuB3B;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,EAAE,QAAQ,EAAE,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,oBAAoB,CAAC,EAC5C,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAC7C,OAAO,CAAC,WAAW,CAAC,CAOtB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,qBAAqB,CAAC,CAAC,SAAS;IAAE,WAAW,CAAC,EAAE,iBAAiB,EAAE,CAAA;CAAE,GAAG,aAAa,EACzG,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,wBAAwB,GAAG,oBAAoB,CAAC,EACvE,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,EAC9C,SAAS,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GAC5C,OAAO,CAAC,CAAC,CAAC,CAMZ"}
1
+ {"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../src/reconcile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EACV,QAAQ,EACR,eAAe,EACf,OAAO,EACP,iBAAiB,EACjB,QAAQ,EACT,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAGtD,4FAA4F;AAC5F,MAAM,WAAW,oBAAoB;IACnC,gBAAgB,EAAE,CAAC,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACrF,iBAAiB,EAAE,CACjB,KAAK,EAAE,eAAe,EACtB,QAAQ,EAAE,QAAQ,KACf,OAAO,CAAC;QAAE,QAAQ,EAAE,QAAQ,CAAA;KAAE,EAAE,CAAC,CAAC;IACvC,oBAAoB,EAAE,CAAC,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACzF,qBAAqB,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;CACrF;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,OAAO,EAAE,CAAC;IACxB,aAAa,EAAE,OAAO,EAAE,CAAC;IACzB,gBAAgB,EAAE,OAAO,EAAE,CAAC;IAC5B,cAAc,EAAE,OAAO,EAAE,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,oBAAoB,EAC3B,KAAK,EAAE,eAAe,EACtB,KAAK,EAAE;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAClE,OAAO,CAAC,gBAAgB,CAAC,CAuB3B;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,EAAE,QAAQ,EAAE,CAAC;IACzB;;;;OAIG;IACH,UAAU,CAAC,EAAE,QAAQ,EAAE,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,oBAAoB,GAAG,yBAAyB,CAAC,EACxE,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAC7C,OAAO,CAAC,WAAW,CAAC,CAUtB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,qBAAqB,CAAC,CAAC,SAAS;IAAE,WAAW,CAAC,EAAE,iBAAiB,EAAE,CAAA;CAAE,GAAG,aAAa,EACzG,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,wBAAwB,GAAG,oBAAoB,GAAG,yBAAyB,CAAC,EACnG,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,EAC9C,SAAS,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GAC5C,OAAO,CAAC,CAAC,CAAC,CAMZ"}
package/dist/reconcile.js CHANGED
@@ -25,7 +25,8 @@ export async function reconcilePayloadFor(admin, actor, scope) {
25
25
  }
26
26
  /**
27
27
  * Read the record for one scope, for `SwitchCarry`. The fleet read, narrowed to this scope
28
- * and `off`; access-logged like the gather's other reads.
28
+ * and `off`, and of those the modules the tenant holds a tenant-level grant for (#1823);
29
+ * access-logged like the gather's other reads.
29
30
  */
30
31
  export async function switchCarryFor(admin, actor, node) {
31
32
  const rows = await admin.listSystemSwitches(actor, {
@@ -33,7 +34,11 @@ export async function switchCarryFor(admin, actor, node) {
33
34
  tenantId: node.tenantId,
34
35
  scopeId: node.scopeId,
35
36
  });
36
- return rows.length ? { switchedOff: rows.map((r) => r.moduleId) } : {};
37
+ if (!rows.length)
38
+ return {};
39
+ const switchedOff = rows.map((r) => r.moduleId);
40
+ const tenantHeld = await admin.tenantHeldSystemModules(actor, node.tenantId, switchedOff);
41
+ return { switchedOff, ...(tenantHeld.length ? { tenantHeld } : {}) };
37
42
  }
38
43
  /**
39
44
  * Run a hosted reconcile or provision, then put the directory's recorded OFF positions back
@@ -1 +1 @@
1
- {"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../src/reconcile.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAoBhE;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,KAA2B,EAC3B,KAAsB,EACtB,KAAmE;IAEnE,MAAM,YAAY,GAAG,MAAM,KAAK,CAAC,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IACzE,sFAAsF;IACtF,6EAA6E;IAC7E,MAAM,aAAa,GAAG,CAAC,MAAM,KAAK,CAAC,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAC9E,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAC3C,CAAC;IACF,MAAM,gBAAgB,GAAG,KAAK,CAAC,QAAQ;QACrC,CAAC,CAAC,wBAAwB,CACtB,CAAC,MAAM,KAAK,CAAC,oBAAoB,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAU,EAClE,KAAK,CAAC,QAAQ,EACd,KAAK,CAAC,EAAE,CACT;QACH,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,cAAc,GAAG,KAAK,CAAC,QAAQ;QACnC,CAAC,CAAC,MAAM,KAAK,CAAC,qBAAqB,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC;QACnE,CAAC,CAAC,EAAE,CAAC;IACP,OAAO;QACL,YAAY;QACZ,aAAa;QACb,gBAAgB,EAAE,gBAA6B;QAC/C,cAAc;KACf,CAAC;AACJ,CAAC;AAYD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAA4C,EAC5C,KAAsB,EACtB,IAA8C;IAE9C,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,kBAAkB,CAAC,KAAK,EAAE;QACjD,QAAQ,EAAE,KAAK;QACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,OAAO,EAAE,IAAI,CAAC,OAAO;KACtB,CAAC,CAAC;IACH,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACzE,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,KAAuE,EACvE,KAAsB,EACtB,IAA8C,EAC9C,SAA6C;IAE7C,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,MAAM,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;IACzE,sFAAsF;IACtF,MAAM,aAAa,GAAG,MAAM,KAAK,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC;IAChF,MAAM,KAAK,CAAC,sBAAsB,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,aAAa,EAAE,CAAC,CAAC;IACnE,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../src/reconcile.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAoBhE;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,KAA2B,EAC3B,KAAsB,EACtB,KAAmE;IAEnE,MAAM,YAAY,GAAG,MAAM,KAAK,CAAC,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IACzE,sFAAsF;IACtF,6EAA6E;IAC7E,MAAM,aAAa,GAAG,CAAC,MAAM,KAAK,CAAC,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAC9E,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAC3C,CAAC;IACF,MAAM,gBAAgB,GAAG,KAAK,CAAC,QAAQ;QACrC,CAAC,CAAC,wBAAwB,CACtB,CAAC,MAAM,KAAK,CAAC,oBAAoB,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAU,EAClE,KAAK,CAAC,QAAQ,EACd,KAAK,CAAC,EAAE,CACT;QACH,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,cAAc,GAAG,KAAK,CAAC,QAAQ;QACnC,CAAC,CAAC,MAAM,KAAK,CAAC,qBAAqB,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC;QACnE,CAAC,CAAC,EAAE,CAAC;IACP,OAAO;QACL,YAAY;QACZ,aAAa;QACb,gBAAgB,EAAE,gBAA6B;QAC/C,cAAc;KACf,CAAC;AACJ,CAAC;AAkBD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAAwE,EACxE,KAAsB,EACtB,IAA8C;IAE9C,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,kBAAkB,CAAC,KAAK,EAAE;QACjD,QAAQ,EAAE,KAAK;QACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,OAAO,EAAE,IAAI,CAAC,OAAO;KACtB,CAAC,CAAC;IACH,IAAI,CAAC,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAC5B,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAChD,MAAM,UAAU,GAAG,MAAM,KAAK,CAAC,uBAAuB,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAC1F,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AACvE,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,KAAmG,EACnG,KAAsB,EACtB,IAA8C,EAC9C,SAA6C;IAE7C,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,MAAM,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;IACzE,sFAAsF;IACtF,MAAM,aAAa,GAAG,MAAM,KAAK,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC;IAChF,MAAM,KAAK,CAAC,sBAAsB,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,aAAa,EAAE,CAAC,CAAC;IACnE,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * One vertical's script FAMILY (#1877), as the pattern `$metadata.service` is matched with —
3
+ * the field Workers Logs indexes every event by. The stem itself is the serving script;
4
+ * `<stem>-<ulid>` is a per-version script (previews, legacy scopes); `<stem>-eu` / `-us` a
5
+ * jurisdictional one (K-30).
6
+ *
7
+ * Anchored, so another vertical whose stem merely begins with this one (`ticket0` and
8
+ * `ticket0-crm`) is not in the family. One pattern for the telemetry query's filter and for a
9
+ * reader that checks the lines it was handed (#1923), so the two cannot disagree about which
10
+ * script a line came from.
11
+ */
12
+ /** The anchored pattern, as a string (the telemetry API's `regex` filter takes one). */
13
+ export declare function serviceFamilyPattern(stem: string): string;
14
+ /**
15
+ * A test for whether a service is a script of one of the families `stems` name, with the
16
+ * patterns compiled once. An empty list names none.
17
+ */
18
+ export declare function serviceFamilyMatcher(stems: readonly string[]): (service: unknown) => boolean;
19
+ //# sourceMappingURL=service-family.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-family.d.ts","sourceRoot":"","sources":["../src/service-family.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,wFAAwF;AACxF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAG5F"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * One vertical's script FAMILY (#1877), as the pattern `$metadata.service` is matched with —
3
+ * the field Workers Logs indexes every event by. The stem itself is the serving script;
4
+ * `<stem>-<ulid>` is a per-version script (previews, legacy scopes); `<stem>-eu` / `-us` a
5
+ * jurisdictional one (K-30).
6
+ *
7
+ * Anchored, so another vertical whose stem merely begins with this one (`ticket0` and
8
+ * `ticket0-crm`) is not in the family. One pattern for the telemetry query's filter and for a
9
+ * reader that checks the lines it was handed (#1923), so the two cannot disagree about which
10
+ * script a line came from.
11
+ */
12
+ /** A lowercased ULID, as it ends a per-version script name (`deploymentRefFor`). */
13
+ const SCRIPT_ULID = '[0-9a-hjkmnp-tv-z]{26}';
14
+ /** The anchored pattern, as a string (the telemetry API's `regex` filter takes one). */
15
+ export function serviceFamilyPattern(stem) {
16
+ const escaped = stem.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
17
+ return `^${escaped}(-${SCRIPT_ULID})?(-(eu|us))?$`;
18
+ }
19
+ /**
20
+ * A test for whether a service is a script of one of the families `stems` name, with the
21
+ * patterns compiled once. An empty list names none.
22
+ */
23
+ export function serviceFamilyMatcher(stems) {
24
+ const patterns = stems.map((stem) => new RegExp(serviceFamilyPattern(stem)));
25
+ return (service) => typeof service === 'string' && patterns.some((p) => p.test(service));
26
+ }
27
+ //# sourceMappingURL=service-family.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-family.js","sourceRoot":"","sources":["../src/service-family.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,oFAAoF;AACpF,MAAM,WAAW,GAAG,wBAAwB,CAAC;AAE7C,wFAAwF;AACxF,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;IAC5D,OAAO,IAAI,OAAO,KAAK,WAAW,gBAAgB,CAAC;AACrD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAwB;IAC3D,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7E,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,OAAO,KAAK,QAAQ,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;AAC3F,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { AttachmentRecord, ScopeLineage, ConnectionId, EntityRef, EntitlementGrant, ModuleId, PermissionKey, PlatformRequest, PlatformRequestFilter, PlatformRequestId, PlatformRequestStatus, PlatformRequestFailure, MintedPreviewClient, PreviewClientCheck, PreviewClientClaim, PreviewClientMint, PreviewClientRetire, PrincipalId, RetiredPreviewClients, PeerGrantsEntry, PeerSwitchOutcome, SystemSwitchOutcome, SystemScheduleEntry, ConnectionGrantRecord, ProjectedConnectionGrant, ProjectedConnectionKey, ProjectedIdentityLink, OwnerSeat, OwnerClaimLink, OwnerTransferResult, QueryScopeInput, ReadScopeTableInput, EntityHistoryInput, EventFacetInput, EventCauseInput, EventEffectsInput, EffectsTree, InvocationEventsInput, InvocationEvents, DeadLettersInput, DeadLetter, LifecycleFlowInput, LifecycleFlowResult, CauseChain, EventFacetResult, HistoryEntry, Page, ScopeDumpTable, ScopeId, ScopeQueryResult, DenialFilter, DenialSummary, PermissionDenial, ScopeTable, ScopeTablePage, TenantId, TenantStoreHandle, Visibility } from '@substrat-run/contracts';
1
+ import type { AttachmentRecord, ScopeLineage, ConnectionId, EntityRef, EntitlementGrant, ModuleId, PermissionKey, PlatformActorId, PlatformRequest, PlatformRequestFilter, PlatformRequestId, PlatformRequestStatus, PlatformRequestFailure, MintedPreviewClient, PreviewClientCheck, PreviewClientClaim, PreviewClientMint, PreviewClientRetire, PrincipalId, RetiredPreviewClients, PeerGrantsEntry, PeerSwitchOutcome, SystemSwitchOutcome, SystemScheduleEntry, ConnectionGrantRecord, ProjectedConnectionGrant, ProjectedConnectionKey, ProjectedIdentityLink, OwnerSeat, OwnerClaimLink, OwnerTransferResult, QueryScopeInput, ReadScopeTableInput, EntityHistoryInput, EventFacetInput, EventCauseInput, EventEffectsInput, EffectsTree, InvocationEventsInput, InvocationEvents, DeadLettersInput, DeadLetter, LifecycleFlowInput, LifecycleFlowResult, CauseChain, EventFacetResult, HistoryEntry, Page, ScopeDumpTable, ScopeId, ScopeQueryResult, DenialFilter, DenialSummary, PermissionDenial, ScopeTable, ScopeTablePage, TenantId, TenantStoreHandle, Visibility } from '@substrat-run/contracts';
2
2
  import { type CapabilityFilter, type CapabilityPage, type LifecycleDelivery, type ScopeLifecycle, type SwitchedOffInUnit, type ImportCursorMoveAt, type ImportCursorMoved, type ExportReadInput, type ExportedBatch, type ImportBatch, type ImportResult, type ImportState } from '@substrat-run/contracts';
3
3
  import type { KeptCopy, LoadMarker, OpenedAttachment, UndrainedEvents } from '@substrat-run/kernel';
4
4
  /**
@@ -113,6 +113,8 @@ export interface ProvisionInstanceInput {
113
113
  * A vertical that predates the field ignores it, and the re-assert after the call covers it.
114
114
  */
115
115
  switchedOff?: ModuleId[];
116
+ /** Of `switchedOff`, the modules held on this scope only by a tenant-level grant (#1823). */
117
+ tenantHeld?: ModuleId[];
116
118
  }
117
119
  export interface ConfigureInstanceInput {
118
120
  /** The scope's tenant — CP-less verticals shard identity/config storage per tenant
@@ -174,6 +176,8 @@ export interface ReconcileInstanceInput {
174
176
  tenantStores?: TenantStoreHandle[];
175
177
  /** The recorded-off modules, switched off in the reconcile's own unit (#1742) — as at provision. */
176
178
  switchedOff?: ModuleId[];
179
+ /** Of `switchedOff`, the modules held only by a tenant-level grant (#1823) — as at provision. */
180
+ tenantHeld?: ModuleId[];
177
181
  }
178
182
  /**
179
183
  * Materialize a tenant's directory-side connection grants for ONE scope (#592) — the
@@ -272,6 +276,8 @@ export declare class VerticalClient {
272
276
  tenantId: TenantId;
273
277
  scopeId: ScopeId;
274
278
  origin: string;
279
+ /** Who asked (#1686) — the vertical records it as the claim link's minter. */
280
+ actor?: PlatformActorId;
275
281
  }): Promise<OwnerClaimLink>;
276
282
  /**
277
283
  * Hand the scope's owner seat from `from` to `to` (#1665) — vertical-host's
@@ -370,6 +376,9 @@ export declare class VerticalClient {
370
376
  setLifecycle(input: {
371
377
  scopeId: ScopeId;
372
378
  lifecycle: ScopeLifecycle;
379
+ /** #2016: the tenant the directory holds the scope under; the deployment refuses a scope
380
+ * provisioned for another, and records it on a scope that predates its tenant receipt. */
381
+ tenantId?: TenantId;
373
382
  }): Promise<LifecycleDelivery>;
374
383
  /**
375
384
  * Move one module's schedule kill switch in the deployment serving the scope (#1666) —
@@ -393,6 +402,8 @@ export declare class VerticalClient {
393
402
  scopeId: ScopeId;
394
403
  moduleId: ModuleId;
395
404
  to: 'on' | 'off';
405
+ /** #1823: the platform holds a live tenant-level grant for the module — see the route's body. */
406
+ tenantHeld?: boolean;
396
407
  }): Promise<SystemSwitchOutcome>;
397
408
  /**
398
409
  * The read half of the schedule kill switch's status (#1674): every module the
@@ -475,6 +486,8 @@ export declare class VerticalClient {
475
486
  snapshotScope(input: {
476
487
  sourceScopeId: ScopeId;
477
488
  newScopeId: ScopeId;
489
+ /** #2016: the tenant snapshotted for — the source must be its, and the copy records it. */
490
+ tenantId?: TenantId;
478
491
  }): Promise<{
479
492
  tables: number;
480
493
  }>;
@@ -528,6 +541,7 @@ export declare class VerticalClient {
528
541
  * A vertical that predates the field ignores it. */
529
542
  opts?: {
530
543
  switchedOff?: ModuleId[];
544
+ tenantHeld?: ModuleId[];
531
545
  sourceScopeId?: ScopeId;
532
546
  exact?: boolean;
533
547
  loadStamp?: string;