@substrat-run/control-plane-api 0.113.0 → 0.116.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.map +1 -1
- package/dist/api.js +86 -3
- package/dist/api.js.map +1 -1
- package/dist/client.d.ts +35 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +47 -3
- package/dist/client.js.map +1 -1
- package/dist/deploy.d.ts +3 -2
- package/dist/deploy.d.ts.map +1 -1
- package/dist/deploy.js +5 -4
- package/dist/deploy.js.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +32 -11
- package/dist/errors.js.map +1 -1
- package/dist/identity-tenants.d.ts +30 -0
- package/dist/identity-tenants.d.ts.map +1 -0
- package/dist/identity-tenants.js +23 -0
- package/dist/identity-tenants.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/vertical-client.d.ts +12 -3
- package/dist/vertical-client.d.ts.map +1 -1
- package/dist/vertical-client.js +26 -1
- package/dist/vertical-client.js.map +1 -1
- package/package.json +6 -6
package/dist/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/api.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
import { parseHostname, withLabel, RESERVED_LABEL_SEPARATOR, adminAction, ASSET_PART_PREFIX, assetHash, channelName, connectionActivity, connectionActivitySource, connectionCredential, connectionFilter, connectionProbe, createTenantInput, entitlementGrantInput, hostname as hostnameSchema, hostnameRegion, hostnameStatus, identityLink, errorCode, issueStatus, issueStatusInput, listPageQuery, matchesOutboundHost, pageOf, platformRequestFilter, principalId as principalIdSchema, promotionAcknowledgement, provisionableJurisdiction, publishVersionInput, queryScopeInput, readScopeTableInput, entityHistoryInput, eventFacetInput, eventCauseInput, eventEffectsInput, invocationEventsInput, delegatedReadParams, createOrgInput, roleKey as roleKeySchema, DEFAULT_DENIAL_LIMIT, DENIAL_LIMIT_MAX, denialGroupBy, registerVerticalInput, serviceDimensions, sweepRunKind, sweepRunOutcome, scopeDump, dataSubjectId as dataSubjectIdSchema, scopeId as scopeIdSchema, scopeStatus, storageShape, surfaceName, tenantId as tenantIdSchema, tenantStatus, versionOrigin, z, PROBLEM_CONTENT_TYPE, toProblem, redrainEventsInput, REDRAIN_BATCH, } from '@substrat-run/contracts';
|
|
2
|
+
import { parseHostname, withLabel, RESERVED_LABEL_SEPARATOR, adminAction, ASSET_PART_PREFIX, assetHash, channelName, connectionActivity, connectionActivitySource, connectionCredential, connectionFilter, connectionProbe, createTenantInput, entitlementGrantInput, hostname as hostnameSchema, hostnameRegion, hostnameStatus, identityLink, errorCode, issueStatus, issueStatusInput, listPageQuery, matchesOutboundHost, pageOf, platformRequestFilter, principalId as principalIdSchema, promotionAcknowledgement, provisionableJurisdiction, publishVersionInput, queryScopeInput, readScopeTableInput, entityHistoryInput, eventFacetInput, eventCauseInput, eventEffectsInput, invocationEventsInput, deadLettersInput, delegatedReadParams, createOrgInput, roleKey as roleKeySchema, DEFAULT_DENIAL_LIMIT, DENIAL_LIMIT_MAX, denialGroupBy, registerVerticalInput, serviceDimensions, sweepRunKind, sweepRunOutcome, scopeDump, dataSubjectId as dataSubjectIdSchema, scopeId as scopeIdSchema, scopeStatus, storageShape, surfaceName, tenantId as tenantIdSchema, tenantStatus, versionOrigin, z, PROBLEM_CONTENT_TYPE, toProblem, redrainEventsInput, REDRAIN_BATCH, } from '@substrat-run/contracts';
|
|
3
3
|
import { attributeFailure } from './failure-attribution.js';
|
|
4
4
|
import { migrationProgress, ulid } from '@substrat-run/kernel';
|
|
5
5
|
import { TENANT_HEADER } from './auth.js';
|
|
@@ -1660,6 +1660,23 @@ export function createControlPlaneApi(options) {
|
|
|
1660
1660
|
colocated: () => admin.invocationEvents(c.get('actor'), tenantId, scopeId, input),
|
|
1661
1661
|
}, (r) => r.events.length));
|
|
1662
1662
|
});
|
|
1663
|
+
// #1525: every delivery in the scope that gave up — the list the walks can only reach
|
|
1664
|
+
// one event at a time. Delegated like them; the row names no subject, so it logs none.
|
|
1665
|
+
app.get('/tenants/:tenantId/scopes/:scopeId/dead-letters', async (c) => {
|
|
1666
|
+
const tenantId = tenantIdSchema.parse(c.req.param('tenantId'));
|
|
1667
|
+
const scopeId = scopeIdSchema.parse(c.req.param('scopeId'));
|
|
1668
|
+
const input = deadLettersInput.parse({
|
|
1669
|
+
limit: c.req.query('limit') ? Number(c.req.query('limit')) : undefined,
|
|
1670
|
+
cursor: c.req.query('cursor') ?? undefined,
|
|
1671
|
+
});
|
|
1672
|
+
const scope = await admin.getScopeRecord(c.get('actor'), tenantId, scopeId);
|
|
1673
|
+
if (!scope)
|
|
1674
|
+
return c.json({ error: `unknown scope for tenant: (${tenantId}, ${scopeId})` }, 404);
|
|
1675
|
+
return c.json(await delegatedRead(c, tenantId, scopeId, scope, 'deadLetters', input, {
|
|
1676
|
+
viaVertical: (v) => v.deadLetters(scopeId, input),
|
|
1677
|
+
colocated: () => admin.deadLetters(c.get('actor'), tenantId, scopeId, input),
|
|
1678
|
+
}, (r) => r.entries.length));
|
|
1679
|
+
});
|
|
1663
1680
|
// #1235: one record's event history — the per-entity pivot of the audit spine.
|
|
1664
1681
|
// Delegated exactly like the table reads: through the vertical that holds the
|
|
1665
1682
|
// scope's data when one resolves, the co-located host otherwise.
|
|
@@ -2208,15 +2225,68 @@ export function createControlPlaneApi(options) {
|
|
|
2208
2225
|
'already shipped to the rebuilt table. Pass the instant the old table stopped receiving.',
|
|
2209
2226
|
}, 400);
|
|
2210
2227
|
}
|
|
2228
|
+
// The input schema also carries `countOnly` (#1545), and this route does not honour it:
|
|
2229
|
+
// its answer (`redrained`, `more`) is a statement that rows moved. Ignoring the field
|
|
2230
|
+
// silently would be the exact failure the separate count route exists to prevent — a
|
|
2231
|
+
// caller asking for a rehearsal and getting a reopen — so it is REFUSED here and the
|
|
2232
|
+
// caller is told where counting lives. Refused whatever its value: on this route the
|
|
2233
|
+
// field has no meaning at all, and "accepted when false" is a rule nobody can rely on.
|
|
2234
|
+
if (parsed.data.countOnly !== undefined) {
|
|
2235
|
+
return c.json({ error: 'countOnly is not honoured here — POST the same body to …/redrain-count for a read-only count' }, 400);
|
|
2236
|
+
}
|
|
2211
2237
|
const scope = await admin.getScopeRecord(actor, tenantId, scopeId);
|
|
2212
2238
|
if (!scope)
|
|
2213
2239
|
return c.json({ error: `unknown scope for tenant: (${tenantId}, ${scopeId})` }, 404);
|
|
2214
|
-
|
|
2240
|
+
// The instant alone, never `parsed.data`, so the field cannot reach the adapter from
|
|
2241
|
+
// this door even if the refusal above is ever relaxed.
|
|
2242
|
+
const redrained = await admin.redrainEvents(actor, tenantId, scopeId, {
|
|
2243
|
+
drainedBefore: parsed.data.drainedBefore,
|
|
2244
|
+
});
|
|
2215
2245
|
// `more` rather than silence: the verb reopens a bounded batch, so a caller that posts
|
|
2216
2246
|
// once can be holding a partial reopen. Saying so is what stops it reading as a finished
|
|
2217
2247
|
// window — the operator (or `pnpm lake:redrain`) posts again until `redrained` is 0.
|
|
2218
2248
|
return c.json({ redrained, more: redrained >= REDRAIN_BATCH, drainedBefore: parsed.data.drainedBefore });
|
|
2219
2249
|
});
|
|
2250
|
+
// #1545 — how many rows the route above WOULD reopen, reopening none of them: what a dry
|
|
2251
|
+
// run is run to learn, and what nothing could answer before. Staff/service only for the
|
|
2252
|
+
// same reason as the reopen (absent from BUILDER_ROUTES, which is default-deny), even
|
|
2253
|
+
// though it changes nothing: the number is a fact about one tenant's history.
|
|
2254
|
+
//
|
|
2255
|
+
// Its own path rather than a `countOnly` field on the POST above, which is how the flag
|
|
2256
|
+
// travels everywhere BELOW this door. `scripts/lake-redrain.mjs` runs from a checkout
|
|
2257
|
+
// against a control plane deployed on its own clock, so the script is routinely newer
|
|
2258
|
+
// than the route. A field an older route does not know is stripped by its Zod parse —
|
|
2259
|
+
// and that route would reopen the window and answer `{ redrained: N }`, which a dry run
|
|
2260
|
+
// would print as its count. A path it does not serve 404s, and nothing moves.
|
|
2261
|
+
//
|
|
2262
|
+
// No `more`: the count is unbounded (an aggregate materialises no rows), so it answers
|
|
2263
|
+
// for the whole window in one call and there is no second page to hint at.
|
|
2264
|
+
app.post('/tenants/:tenantId/scopes/:scopeId/redrain-count', async (c) => {
|
|
2265
|
+
const tenantId = tenantIdSchema.parse(c.req.param('tenantId'));
|
|
2266
|
+
const scopeId = scopeIdSchema.parse(c.req.param('scopeId'));
|
|
2267
|
+
const actor = c.get('actor');
|
|
2268
|
+
const parsed = redrainEventsInput.safeParse(await c.req.json().catch(() => ({})));
|
|
2269
|
+
if (!parsed.success) {
|
|
2270
|
+
return c.json({ error: 'body must be { drainedBefore: <ISO 8601 instant> } — the window to count' }, 400);
|
|
2271
|
+
}
|
|
2272
|
+
// The same future refusal as the reopen. A count of a window that has not closed yet
|
|
2273
|
+
// is not dangerous, it is meaningless — and a dry run whose instant this door accepts
|
|
2274
|
+
// and the real run rejects would be the worst kind of rehearsal.
|
|
2275
|
+
if (Date.parse(parsed.data.drainedBefore) > Date.now()) {
|
|
2276
|
+
return c.json({
|
|
2277
|
+
error: `drainedBefore ${parsed.data.drainedBefore} is in the future — it would count rows ` +
|
|
2278
|
+
'already shipped to the rebuilt table. Pass the instant the old table stopped receiving.',
|
|
2279
|
+
}, 400);
|
|
2280
|
+
}
|
|
2281
|
+
const scope = await admin.getScopeRecord(actor, tenantId, scopeId);
|
|
2282
|
+
if (!scope)
|
|
2283
|
+
return c.json({ error: `unknown scope for tenant: (${tenantId}, ${scopeId})` }, 404);
|
|
2284
|
+
const redrainable = await admin.redrainEvents(actor, tenantId, scopeId, {
|
|
2285
|
+
drainedBefore: parsed.data.drainedBefore,
|
|
2286
|
+
countOnly: true,
|
|
2287
|
+
});
|
|
2288
|
+
return c.json({ redrainable, drainedBefore: parsed.data.drainedBefore });
|
|
2289
|
+
});
|
|
2220
2290
|
app.post('/tenants/:tenantId/scopes/:scopeId/reap', async (c) => {
|
|
2221
2291
|
const tenantId = tenantIdSchema.parse(c.req.param('tenantId'));
|
|
2222
2292
|
const scopeId = scopeIdSchema.parse(c.req.param('scopeId'));
|
|
@@ -2876,7 +2946,20 @@ export function createControlPlaneApi(options) {
|
|
|
2876
2946
|
const p = c.get('principal');
|
|
2877
2947
|
// A builder sees only what it owns; staff see the whole registry. The narrowing
|
|
2878
2948
|
// runs above the adapter, so the page is sliced here — over the narrowed list.
|
|
2879
|
-
|
|
2949
|
+
//
|
|
2950
|
+
// A staff/service caller may ask for one tenant's slice instead of the registry:
|
|
2951
|
+
// `ownerTenant` is what that tenant owns, `visibleTo` adds the published tier (what
|
|
2952
|
+
// its install catalog shows). Both only ever NARROW, and both are ignored for a
|
|
2953
|
+
// builder, whose view is fixed by its auth — `visibleTo` would otherwise widen it.
|
|
2954
|
+
// The dashboard's tenant-narrowed seam sends them so it stops paging the whole
|
|
2955
|
+
// registry to keep a handful of rows; it still filters what comes back, so a plane
|
|
2956
|
+
// that predates the params (the two ship separately) changes nothing but the size.
|
|
2957
|
+
const ownerTenant = c.req.query('ownerTenant');
|
|
2958
|
+
const visibleTo = c.req.query('visibleTo');
|
|
2959
|
+
const visible = p.kind === 'builder'
|
|
2960
|
+
? all.filter((v) => v.ownerTenant === p.tenantId)
|
|
2961
|
+
: all.filter((v) => (ownerTenant === undefined || v.ownerTenant === ownerTenant) &&
|
|
2962
|
+
(visibleTo === undefined || v.listed || v.ownerTenant === visibleTo));
|
|
2880
2963
|
return c.json(pageSlice(visible, page, (v) => v.slug));
|
|
2881
2964
|
});
|
|
2882
2965
|
// -- the service→dimensions resolver (#1231's last item) -----------------------
|