@substrat-run/control-plane-api 0.142.0 → 0.143.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 CHANGED
@@ -339,6 +339,14 @@ type Vars = {
339
339
  host: ScopeHost;
340
340
  /** `host.admin` of the same request — the spelling every route writes through. */
341
341
  admin: HostAdmin;
342
+ /**
343
+ * The error this request answered with, when a route answered it itself instead of throwing
344
+ * (`failedWith`) or `onError` did. The 5xx backstop reads it to attribute the row, and to skip
345
+ * one a step already recorded.
346
+ */
347
+ failure?: unknown;
348
+ /** A step recorded this request's failure with no error object to mark (a literal 5xx). */
349
+ failureRecorded?: true;
342
350
  };
343
351
  /**
344
352
  * The K-35 denial-log filter (#867). Bounded by default like every other HTTP read
package/dist/api.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
package/dist/api.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { resolveObservabilityWindow } from './observability-window.js';
2
2
  import { Hono } from 'hono';
3
- import { parseHostname, withLabel, RESERVED_LABEL_SEPARATOR, adminAction, ownerTransferInput, memberInviteInput, memberRoleInput, ASSET_PART_PREFIX, assetHash, channelName, connectLinkId, connectionActivity, connectionActivitySource, connectionCredential, connectionCertificate, connectionFilter, connectionHealthState, connectionProvider, connectorDispatchKind, PROVISION_SIBLING_KIND, ARCHIVE_SCOPE_KIND, PROVISION_TENANT_KIND, SET_ENTITLEMENTS_KIND, MODEL_USAGE_KIND, SWEEP_RUNS_KIND, toConnectionHealthEntry, CONNECTION_EXPIRY_WARNING_DAYS, CONNECTION_STALE_DAYS, connectionProbe, createTenantInput, entitlementGrantInput, hostname as hostnameSchema, hostnameRegion, hostnameStatus, identityLink, errorCode, issueStatus, issueStatusInput, findingKind, findingRuleInput, findingStatus, findingStatusInput, listPageQuery, matchesOutboundHost, pageOf, platformRequestFilter, principalId as principalIdSchema, impersonationStamp, promotionAcknowledgement, bindAcknowledgement, provisionableJurisdiction, publishVersionInput, queryScopeInput, readScopeTableInput, entityHistoryInput, eventFacetInput, eventCauseInput, eventEffectsInput, invocationEventsInput, deadLettersInput, lifecycleFlowInput, operationSeriesCount, operationSeriesInput, delegatedReadParams, createOrgInput, roleKey as roleKeySchema, peerSwitch, verticalSlug, systemSwitch, DEFAULT_DENIAL_LIMIT, DENIAL_LIMIT_MAX, denialGroupBy, registerVerticalInput, platformScriptCollision, platformScriptRefusal, PLATFORM_SCRIPT_NAMES, serviceDimensions, sweepRunKind, sweepRunOutcome, scopeDump, dataSubjectId as dataSubjectIdSchema, scopeId as scopeIdSchema, scopeStatus, storageShape, surfaceName, tenantId as tenantIdSchema, tenantStatus, versionOrigin, z, PROBLEM_CONTENT_TYPE, toProblem, redrainEventsInput, importCursorMove, importCursorAcknowledgementMissing, REDRAIN_BATCH, migrationsOnTop, errorCodeOf, capabilityFilterQuery, PLATFORM_FEATURES_HEADER, PLATFORM_FEATURE_SCOPE_SWEEPER, } from '@substrat-run/contracts';
3
+ import { parseHostname, withLabel, RESERVED_LABEL_SEPARATOR, adminAction, ownerTransferInput, memberInviteInput, memberRoleInput, ASSET_PART_PREFIX, assetHash, channelName, connectLinkId, connectionActivity, connectionActivitySource, connectionCredential, connectionCertificate, connectionFilter, connectionHealthState, connectionProvider, connectorDispatchKind, PROVISION_SIBLING_KIND, ARCHIVE_SCOPE_KIND, PROVISION_TENANT_KIND, SET_ENTITLEMENTS_KIND, MODEL_USAGE_KIND, SWEEP_RUNS_KIND, toConnectionHealthEntry, CONNECTION_EXPIRY_WARNING_DAYS, CONNECTION_STALE_DAYS, connectionProbe, createTenantInput, entitlementGrantInput, hostname as hostnameSchema, hostnameRegion, hostnameStatus, identityLink, errorCode, issueStatus, issueStatusInput, findingKind, findingRuleInput, findingStatus, findingStatusInput, listPageQuery, matchesOutboundHost, pageOf, platformRequestFilter, principalId as principalIdSchema, impersonationStamp, promotionAcknowledgement, bindAcknowledgement, provisionableJurisdiction, publishVersionInput, queryScopeInput, readScopeTableInput, entityHistoryInput, eventFacetInput, eventCauseInput, eventEffectsInput, invocationEventsInput, deadLettersInput, lifecycleFlowInput, operationSeriesCount, operationSeriesInput, delegatedReadParams, createOrgInput, roleKey as roleKeySchema, peerSwitch, verticalSlug, systemSwitch, DEFAULT_DENIAL_LIMIT, DENIAL_LIMIT_MAX, denialGroupBy, registerVerticalInput, platformScriptCollision, platformScriptRefusal, PLATFORM_SCRIPT_NAMES, serviceDimensions, sweepRunKind, sweepRunOutcome, scopeDump, dataSubjectId as dataSubjectIdSchema, scopeId as scopeIdSchema, scopeStatus, storageShape, surfaceName, tenantId as tenantIdSchema, tenantStatus, versionOrigin, z, PROBLEM_CONTENT_TYPE, toProblem, redrainEventsInput, importCursorMove, importCursorAcknowledgementMissing, REDRAIN_BATCH, migrationsOnTop, errorCodeOf, capabilityFilterQuery, capabilityId as capabilityIdSchema, PLATFORM_FEATURES_HEADER, PLATFORM_FEATURE_SCOPE_SWEEPER, } from '@substrat-run/contracts';
4
4
  import { SCOPE_COPY_LEASE_MS, copyRestoreFence } from '@substrat-run/kernel';
5
5
  import { attributeFailure } from './failure-attribution.js';
6
6
  import { auditedChange, withAuditedOutcomes } from './audited-change.js';
@@ -11,7 +11,7 @@ import { connectionGrantsForScope } from './vertical-client.js';
11
11
  import { assertNoUnreachableScopeCopies, listAllScopeScriptCopies, reapScopeScriptCopies, recordStrandedStorage } from './scope-copy-cleanup.js';
12
12
  import { BACKFILL_PAGE_MAX, backfillScopeScriptCopies } from './scope-copy-backfill.js';
13
13
  import { oidcCallbackUrl, retireClientsOfReapedScope, wirePreviewAuth } from './preview-auth.js';
14
- import { versionReachedAt } from './scope-deployment.js';
14
+ import { scopeDeployment, versionReachedAt } from './scope-deployment.js';
15
15
  import { reconcileConnectionGrants } from './connection-grants.js';
16
16
  import { reconcileThenReassert, reviewedEntityGrants, switchCarryFor } from './reconcile.js';
17
17
  import { ConnectionRelayError, relayConnectionUpsert } from './connection-relay.js';
@@ -773,6 +773,18 @@ const namedTenants = (c) => {
773
773
  ];
774
774
  return named.filter((v) => typeof v === 'string' && v.length > 0);
775
775
  };
776
+ function messageOfThrow(err) {
777
+ return err instanceof Error ? err.message : String(err);
778
+ }
779
+ /** What a 5xx answered with no error object said: the JSON body's `error` (and `detail`), else the status. */
780
+ async function answeredMessage(res) {
781
+ const body = (await res.clone().json().catch(() => null));
782
+ const error = typeof body?.['error'] === 'string' ? body['error'] : typeof body?.['title'] === 'string' ? body['title'] : null;
783
+ const detail = typeof body?.['detail'] === 'string' ? body['detail'] : null;
784
+ if (error && detail)
785
+ return `${error}: ${detail}`;
786
+ return error ?? detail ?? `HTTP ${res.status}`;
787
+ }
776
788
  /**
777
789
  * The audited HTTP surface over `HostAdmin` (control-plane.md §4.5).
778
790
  *
@@ -804,6 +816,48 @@ export function createControlPlaneApi(options) {
804
816
  const { host, authenticate, authenticateBuilder, authenticateTenantService } = options;
805
817
  const copyLeaseMs = options.copyLeaseMs ?? SCOPE_COPY_LEASE_MS;
806
818
  const app = new Hono();
819
+ // The 5xx backstop: every 5xx this API answers lands an ops-failure row, however it was
820
+ // answered. A 5xx is the PLATFORM failing (an unmapped throw, or a downstream vertical's own
821
+ // 5xx passing through, #559), so the console must be able to list it and a `reference = <id>`
822
+ // search must find it. It used to be recorded only in `onError`, which never sees the routes
823
+ // that catch a ControlPlaneError and answer it themselves: a preview re-push whose bind failed
824
+ // after a clean carry answered 502 with a reference and left no row behind.
825
+ //
826
+ // Registered first, so it wraps every other middleware and route. 501 stays out: an honest
827
+ // not-implemented is a capability statement, not a failure. 4xx stay out too: they are
828
+ // refusals the caller can already read. Actor is unset only when the request failed before
829
+ // authentication, where nothing worth recording refuses. A throw a step already recorded,
830
+ // with its stage and scope, is not recorded again.
831
+ app.use('*', async (c, next) => {
832
+ await next();
833
+ const status = c.res.status;
834
+ const actor = c.get('actor');
835
+ if (status >= 500 && status !== 501 && actor && !c.get('failureRecorded')) {
836
+ const cause = c.get('failure');
837
+ const marked = typeof cause === 'object' && cause !== null && recordedCauses.has(cause);
838
+ if (!marked) {
839
+ const { vertical, tenantId } = await failureSubject(c);
840
+ recordFailure({
841
+ actor,
842
+ operation: `${c.req.method} ${c.req.routePath}`,
843
+ vertical,
844
+ tenantId,
845
+ scopeId: c.req.param('scopeId') ?? null,
846
+ status,
847
+ message: cause !== undefined ? messageOfThrow(cause) : await answeredMessage(c.res),
848
+ }, cause === undefined ? NO_CAUSE : cause);
849
+ }
850
+ }
851
+ if (inflightRecords.size) {
852
+ // `executionCtx` throws outside a Worker (tests, node); the write then simply runs on.
853
+ try {
854
+ c.executionCtx.waitUntil(Promise.allSettled([...inflightRecords]));
855
+ }
856
+ catch {
857
+ // no execution context
858
+ }
859
+ }
860
+ });
807
861
  // The CLI version advisory (#971), stamped on EVERY response — including the 401 the
808
862
  // auth middleware below answers with, since a CLI too old to authenticate is exactly
809
863
  // the one that needs to hear it. Registered first so nothing can return around it.
@@ -1027,20 +1081,62 @@ export function createControlPlaneApi(options) {
1027
1081
  // `undefined` (`throw undefined`, a bare rejection), and that throw deserves an
1028
1082
  // `unknown` attribution, not a skipped one. Only true omission skips.
1029
1083
  const NO_CAUSE = Symbol('no-cause');
1084
+ // The throws a step already recorded, so the 5xx backstop below does not record them twice.
1085
+ const recordedCauses = new WeakSet();
1086
+ // The writes still in flight. A Worker may end once its response is sent, and a write nobody
1087
+ // awaits goes with it, so the backstop hands these to `waitUntil`.
1088
+ const inflightRecords = new Set();
1030
1089
  const recordFailure = (entry, cause = NO_CAUSE) => {
1090
+ if (typeof cause === 'object' && cause !== null)
1091
+ recordedCauses.add(cause);
1031
1092
  // The error SHAPE rides beside the prose (#1233): attributed here — one place,
1032
1093
  // from the throw itself — so a fingerprint groups on a column and never has to
1033
1094
  // regex a message. A caller with no throw in hand leaves the columns null,
1034
1095
  // which reads as "nobody classified this" rather than a guess nobody made.
1035
1096
  const attributed = cause === NO_CAUSE ? undefined : attributeFailure(cause);
1036
- void host.admin
1097
+ const write = host.admin
1037
1098
  .recordOpsFailure({
1038
1099
  ...entry,
1039
1100
  origin: entry.origin ?? attributed?.origin ?? null,
1040
1101
  code: entry.code ?? attributed?.code ?? null,
1041
1102
  reference: entry.reference ?? UPSTREAM_REFERENCE.exec(entry.message)?.[1] ?? null,
1042
1103
  })
1043
- .catch(() => undefined);
1104
+ .catch(() => undefined)
1105
+ .finally(() => inflightRecords.delete(write));
1106
+ inflightRecords.add(write);
1107
+ };
1108
+ /**
1109
+ * Whose failure the backstop is recording, so the dashboard's tenant-pinned failure view lists
1110
+ * it. The path's tenant when it names one; otherwise the credential's own (a CI push token, a
1111
+ * builder); otherwise, on a vertical route, the vertical's owner. The slug is resolved the way
1112
+ * the route resolves it, so a CI push's bare `todo` is recorded as the full slug the
1113
+ * dashboard filters on. Best effort: a lookup that fails still records the row, unattributed.
1114
+ */
1115
+ const failureSubject = async (c) => {
1116
+ // Already decoded by Hono, and handed on as-is the way the routes hand it to
1117
+ // `resolveVerticalId`. Decoding again would throw on a slug carrying `%25`, and a throw here
1118
+ // replaces the 5xx being recorded with onError's answer.
1119
+ const raw = c.req.param('slug') ?? null;
1120
+ const pathTenant = c.req.param('tenantId') ?? null;
1121
+ const principal = c.get('principal');
1122
+ const confined = principal ? confinedTenant(principal) : null;
1123
+ if (!raw)
1124
+ return { vertical: null, tenantId: pathTenant ?? confined };
1125
+ try {
1126
+ const vertical = await resolveVerticalId(c, raw);
1127
+ const owner = (pathTenant ?? confined)
1128
+ ? null
1129
+ : ((await verticalOf(c.get('actor'), vertical))?.ownerTenant ?? null);
1130
+ return { vertical, tenantId: pathTenant ?? confined ?? owner };
1131
+ }
1132
+ catch {
1133
+ return { vertical: raw, tenantId: pathTenant ?? confined };
1134
+ }
1135
+ };
1136
+ /** The status of a ControlPlaneError a route answers itself, with the error marked for the backstop. */
1137
+ const failedWith = (c, e) => {
1138
+ c.set('failure', e);
1139
+ return e.status;
1044
1140
  };
1045
1141
  // Rides out a transient downstream window: the install path's binding-attach →
1046
1142
  // script-settings propagation race (#424 case 2), and a one-shot DO storage blip
@@ -1084,25 +1180,8 @@ export function createControlPlaneApi(options) {
1084
1180
  return c.json({ error: err.message, carrySourceChanged: { scopeId: err.scopeId, from: err.from, to: err.to, attempts: err.attempts } }, 409);
1085
1181
  }
1086
1182
  const { status, body } = mapError(err);
1087
- // A 5xx is the PLATFORM failing — an unmapped throw, or a downstream vertical's
1088
- // own 5xx passing through (a DO storage fault during a preview restore is the
1089
- // founding case, #559) — so it lands a durable ops-failure row the console can
1090
- // list and a `reference = <id>` search can find. 501 stays out: an honest
1091
- // not-implemented is a capability statement, not a failure. 4xx stay out too:
1092
- // they are refusals the caller can already read. Actor is unset only when the
1093
- // throw happened before authentication — nothing worth recording refuses there.
1094
- const actor = c.get('actor');
1095
- if (status >= 500 && status !== 501 && actor) {
1096
- recordFailure({
1097
- actor,
1098
- operation: `${c.req.method} ${c.req.routePath}`,
1099
- vertical: c.req.param('slug') ? decodeURIComponent(c.req.param('slug')) : null,
1100
- tenantId: c.req.param('tenantId') ?? null,
1101
- scopeId: c.req.param('scopeId') ?? null,
1102
- status,
1103
- message: err instanceof Error ? err.message : String(err),
1104
- }, err);
1105
- }
1183
+ // The 5xx backstop (registered first, above) records it, with this throw as the cause.
1184
+ c.set('failure', err);
1106
1185
  // A 500 is, by definition, a throw whose message `mapError` did not recognise — so the
1107
1186
  // client gets a GENERIC body that discloses nothing, and until now nothing recorded WHAT
1108
1187
  // threw either. That left every unmapped failure (e.g. a raw SQLite constraint from a
@@ -1213,7 +1292,7 @@ export function createControlPlaneApi(options) {
1213
1292
  }
1214
1293
  catch (e) {
1215
1294
  if (e instanceof ControlPlaneError) {
1216
- return c.json({ error: e.message }, e.status);
1295
+ return c.json({ error: e.message }, failedWith(c, e));
1217
1296
  }
1218
1297
  throw e;
1219
1298
  }
@@ -1885,7 +1964,7 @@ export function createControlPlaneApi(options) {
1885
1964
  }
1886
1965
  catch (e) {
1887
1966
  if (e instanceof ControlPlaneError)
1888
- return c.json({ error: e.message }, e.status);
1967
+ return c.json({ error: e.message }, failedWith(c, e));
1889
1968
  throw e;
1890
1969
  }
1891
1970
  });
@@ -1941,42 +2020,14 @@ export function createControlPlaneApi(options) {
1941
2020
  * served version's hook ran when the bound one's did. `versionReachedAt(via, …)` turns
1942
2021
  * this answer into the version, so the choice and the receipt have one source.
1943
2022
  */
1944
- const deploymentForScope = async (c, scope) => {
1945
- if (!scope.vertical)
1946
- return undefined;
1947
- const actor = c.get('actor');
1948
- // A scope on the stable serving script (#286) is reached THERE — that script holds
1949
- // its DOs regardless of what the bound version or the prod channel say.
1950
- if (scope.servingRef && options.resolveVerticalRef) {
1951
- const serving = await options.resolveVerticalRef(scope.servingRef);
1952
- if (serving)
1953
- return { client: serving, via: 'serving-script' };
1954
- }
1955
- const bySlug = async (slug) => {
1956
- if (scope.verticalVersionId && options.resolveVerticalVersion) {
1957
- const bound = await options.resolveVerticalVersion(slug, scope.verticalVersionId, actor);
1958
- if (bound)
1959
- return { client: bound, via: 'bound-version' };
1960
- }
1961
- const bySlugClient = options.verticals?.[slug] ?? (await options.resolveVertical?.(slug, actor));
1962
- return bySlugClient ? { client: bySlugClient, via: 'slug' } : undefined;
1963
- };
1964
- const direct = await bySlug(scope.vertical);
1965
- if (direct)
1966
- return direct;
1967
- // Miss path only (#417) — a resolved scope never pays for these registry reads: a
1968
- // scope bound to a BARE slug that is not registered, while the owning tenant's
1969
- // prefixed registration of the same name exists, is addressing the prefixed lineage
1970
- // under its bare spelling. Retry once under the registry id; anything else still
1971
- // misses and surfaces through `diagnoseUnboundScope` as before.
1972
- if (scope.tenantId && !scope.vertical.includes('/') && (await ownerOf(actor, scope.vertical)) === undefined) {
1973
- const tenant = await c.var.admin.getTenant(actor, scope.tenantId).catch(() => null);
1974
- const prefixed = tenant ? `${tenant.slug}/${scope.vertical}` : null;
1975
- if (prefixed && (await ownerOf(actor, prefixed)) !== undefined)
1976
- return bySlug(prefixed);
1977
- }
1978
- return undefined;
1979
- };
2023
+ const deploymentForScope = (c, scope) => scopeDeployment({
2024
+ verticals: options.verticals,
2025
+ resolveVertical: options.resolveVertical,
2026
+ resolveVerticalVersion: options.resolveVerticalVersion,
2027
+ resolveVerticalRef: options.resolveVerticalRef,
2028
+ ownerOf,
2029
+ tenantSlugOf: async (actor, tenantId) => (await c.var.admin.getTenant(actor, tenantId).catch(() => null))?.slug ?? null,
2030
+ }, c.get('actor'), scope);
1980
2031
  /**
1981
2032
  * Why a scope's vertical did not resolve to a running deployment — an ACTIONABLE 501
1982
2033
  * body, computed only on the miss path, in place of the bare "no deployment is bound"
@@ -3143,7 +3194,7 @@ export function createControlPlaneApi(options) {
3143
3194
  // unbound store is exactly what makes a vertical refuse its own reconcile), so
3144
3195
  // it must not be the half that gets dropped.
3145
3196
  const detail = storeErrors.length ? `${e.message} (stores: ${storeErrors.join('; ')})` : e.message;
3146
- return c.json({ error: detail }, e.status);
3197
+ return c.json({ error: detail }, failedWith(c, e));
3147
3198
  }
3148
3199
  throw e;
3149
3200
  }
@@ -3440,6 +3491,23 @@ export function createControlPlaneApi(options) {
3440
3491
  colocated: () => c.var.admin.listCapabilities(c.get('actor'), tenantId, scopeId, filter),
3441
3492
  }, (r) => r.entries.length));
3442
3493
  });
3494
+ // The operator's revoke of one capability (#1686): the lever for a leaked link, beside the
3495
+ // read above and gated the same way, in the handler as well as by the builder default-deny.
3496
+ // `HostAdmin.revokeCapability` reaches the deployment serving a hosted scope (its
3497
+ // `capabilityDelegation`) and audits it on this side, intent then outcome; the record names the
3498
+ // actor as its revoker. Idempotent: revoking a revoked capability answers 204 again. A capability
3499
+ // the scope does not hold is 404; a deployment built before the far end answers 501 "redeploy".
3500
+ app.post('/tenants/:tenantId/scopes/:scopeId/capabilities/:capabilityId/revoke', async (c) => {
3501
+ if (confinedTenant(c.get('principal')) !== null) {
3502
+ return c.json({ error: 'forbidden: revoking a capability is staff-only' }, 403);
3503
+ }
3504
+ const tenantId = tenantIdSchema.parse(c.req.param('tenantId'));
3505
+ const scopeId = scopeIdSchema.parse(c.req.param('scopeId'));
3506
+ const capability = capabilityIdSchema.parse(c.req.param('capabilityId'));
3507
+ // An unknown scope, another tenant's included, is the verb's own `not_found`: a 404.
3508
+ await c.var.admin.revokeCapability(c.get('actor'), tenantId, scopeId, capability);
3509
+ return c.body(null, 204);
3510
+ });
3443
3511
  // The owner seat (#925). Both reads go to the VERTICAL — the seat lives in its identity
3444
3512
  // directory, in its deployment — after the same K-3 cross-check every scope-addressed read
3445
3513
  // here makes. A co-located scope (contract-test host, self-host running no vertical code)
@@ -3465,7 +3533,7 @@ export function createControlPlaneApi(options) {
3465
3533
  }
3466
3534
  catch (e) {
3467
3535
  if (e instanceof ControlPlaneError)
3468
- return c.json({ error: e.message }, e.status);
3536
+ return c.json({ error: e.message }, failedWith(c, e));
3469
3537
  throw e;
3470
3538
  }
3471
3539
  });
@@ -3505,7 +3573,7 @@ export function createControlPlaneApi(options) {
3505
3573
  }
3506
3574
  catch (e) {
3507
3575
  if (e instanceof ControlPlaneError)
3508
- return c.json({ error: e.message }, e.status);
3576
+ return c.json({ error: e.message }, failedWith(c, e));
3509
3577
  throw e;
3510
3578
  }
3511
3579
  });
@@ -3551,7 +3619,7 @@ export function createControlPlaneApi(options) {
3551
3619
  const { operationId } = done;
3552
3620
  if ('error' in done) {
3553
3621
  if (done.error instanceof ControlPlaneError) {
3554
- return c.json({ error: done.error.message, operationId }, done.error.status);
3622
+ return c.json({ error: done.error.message, operationId }, failedWith(c, done.error));
3555
3623
  }
3556
3624
  throw done.error;
3557
3625
  }
@@ -3590,7 +3658,7 @@ export function createControlPlaneApi(options) {
3590
3658
  }
3591
3659
  catch (e) {
3592
3660
  if (e instanceof ControlPlaneError)
3593
- return c.json({ error: e.message }, e.status);
3661
+ return c.json({ error: e.message }, failedWith(c, e));
3594
3662
  throw e;
3595
3663
  }
3596
3664
  });
@@ -3699,7 +3767,7 @@ export function createControlPlaneApi(options) {
3699
3767
  }
3700
3768
  catch (e) {
3701
3769
  if (e instanceof ControlPlaneError) {
3702
- return c.json({ error: e.message }, e.status);
3770
+ return c.json({ error: e.message }, failedWith(c, e));
3703
3771
  }
3704
3772
  throw e;
3705
3773
  }
@@ -3755,6 +3823,7 @@ export function createControlPlaneApi(options) {
3755
3823
  status: 503,
3756
3824
  message: error,
3757
3825
  }, failure.cause);
3826
+ c.set('failureRecorded', true);
3758
3827
  return c.json({ error }, 503);
3759
3828
  }
3760
3829
  }
@@ -3884,7 +3953,7 @@ export function createControlPlaneApi(options) {
3884
3953
  }
3885
3954
  catch (e) {
3886
3955
  if (e instanceof ControlPlaneError) {
3887
- return c.json({ error: e.message }, e.status);
3956
+ return c.json({ error: e.message }, failedWith(c, e));
3888
3957
  }
3889
3958
  throw e;
3890
3959
  }
@@ -3969,7 +4038,7 @@ export function createControlPlaneApi(options) {
3969
4038
  }
3970
4039
  catch (e) {
3971
4040
  if (e instanceof ControlPlaneError) {
3972
- return c.json({ error: e.message }, e.status);
4041
+ return c.json({ error: e.message }, failedWith(c, e));
3973
4042
  }
3974
4043
  throw e;
3975
4044
  }
@@ -4060,7 +4129,7 @@ export function createControlPlaneApi(options) {
4060
4129
  }
4061
4130
  catch (e) {
4062
4131
  if (e instanceof ControlPlaneError) {
4063
- return c.json({ error: e.message }, e.status);
4132
+ return c.json({ error: e.message }, failedWith(c, e));
4064
4133
  }
4065
4134
  throw e;
4066
4135
  }
@@ -4561,7 +4630,7 @@ export function createControlPlaneApi(options) {
4561
4630
  }
4562
4631
  catch (e) {
4563
4632
  if (e instanceof ControlPlaneError) {
4564
- return c.json({ error: e.message }, e.status);
4633
+ return c.json({ error: e.message }, failedWith(c, e));
4565
4634
  }
4566
4635
  return c.json({
4567
4636
  error: 'backup failed — the scope was NOT reaped and its data is intact; ' +
@@ -4582,7 +4651,7 @@ export function createControlPlaneApi(options) {
4582
4651
  }
4583
4652
  catch (e) {
4584
4653
  if (e instanceof ControlPlaneError) {
4585
- return c.json({ error: e.message }, e.status);
4654
+ return c.json({ error: e.message }, failedWith(c, e));
4586
4655
  }
4587
4656
  throw e;
4588
4657
  }
@@ -4629,7 +4698,7 @@ export function createControlPlaneApi(options) {
4629
4698
  }
4630
4699
  catch (e) {
4631
4700
  if (e instanceof ControlPlaneError) {
4632
- return c.json({ error: e.message }, e.status);
4701
+ return c.json({ error: e.message }, failedWith(c, e));
4633
4702
  }
4634
4703
  throw e;
4635
4704
  }
@@ -4744,7 +4813,7 @@ export function createControlPlaneApi(options) {
4744
4813
  }
4745
4814
  catch (e) {
4746
4815
  if (e instanceof ControlPlaneError) {
4747
- return c.json({ error: e.message }, e.status);
4816
+ return c.json({ error: e.message }, failedWith(c, e));
4748
4817
  }
4749
4818
  throw e;
4750
4819
  }
@@ -4810,7 +4879,7 @@ export function createControlPlaneApi(options) {
4810
4879
  }
4811
4880
  catch (e) {
4812
4881
  if (e instanceof ControlPlaneError) {
4813
- return c.json({ error: e.message }, e.status);
4882
+ return c.json({ error: e.message }, failedWith(c, e));
4814
4883
  }
4815
4884
  // A restore throw is driven by the caller-supplied dump (a shape the target cannot
4816
4885
  // load, a DDL the engine rejects), so DISCLOSE it as an actionable 422 rather than
@@ -4890,7 +4959,7 @@ export function createControlPlaneApi(options) {
4890
4959
  }
4891
4960
  catch (e) {
4892
4961
  if (e instanceof ControlPlaneError) {
4893
- return c.json({ error: e.message }, e.status);
4962
+ return c.json({ error: e.message }, failedWith(c, e));
4894
4963
  }
4895
4964
  throw e;
4896
4965
  }
@@ -4910,7 +4979,7 @@ export function createControlPlaneApi(options) {
4910
4979
  }
4911
4980
  catch (e) {
4912
4981
  if (e instanceof ControlPlaneError) {
4913
- return c.json(exportBreakBody(e), e.status);
4982
+ return c.json(exportBreakBody(e), failedWith(c, e));
4914
4983
  }
4915
4984
  throw e;
4916
4985
  }
@@ -4948,7 +5017,7 @@ export function createControlPlaneApi(options) {
4948
5017
  }
4949
5018
  catch (e) {
4950
5019
  if (e instanceof ControlPlaneError) {
4951
- return c.json({ ...exportBreakBody(e), adopted, alreadyAdopted }, e.status);
5020
+ return c.json({ ...exportBreakBody(e), adopted, alreadyAdopted }, failedWith(c, e));
4952
5021
  }
4953
5022
  throw e;
4954
5023
  }
@@ -4980,7 +5049,7 @@ export function createControlPlaneApi(options) {
4980
5049
  }
4981
5050
  catch (e) {
4982
5051
  if (e instanceof ControlPlaneError) {
4983
- return c.json(exportBreakBody(e), e.status);
5052
+ return c.json(exportBreakBody(e), failedWith(c, e));
4984
5053
  }
4985
5054
  throw e;
4986
5055
  }
@@ -5465,7 +5534,7 @@ export function createControlPlaneApi(options) {
5465
5534
  message: e.message,
5466
5535
  }, e);
5467
5536
  }
5468
- return c.json({ error: e.message }, e.status);
5537
+ return c.json({ error: e.message }, failedWith(c, e));
5469
5538
  }
5470
5539
  throw e;
5471
5540
  }
@@ -6616,6 +6685,7 @@ export function createControlPlaneApi(options) {
6616
6685
  status: rejected ? 422 : 502,
6617
6686
  message: detail,
6618
6687
  }, e);
6688
+ c.set('failureRecorded', true);
6619
6689
  return rejected
6620
6690
  ? c.json({ error: 'deploy rejected', detail }, 422)
6621
6691
  : c.json({ error: 'deploy upload failed', detail }, 502);
@@ -7926,7 +7996,7 @@ export function createControlPlaneApi(options) {
7926
7996
  }
7927
7997
  catch (e) {
7928
7998
  if (e instanceof ControlPlaneError)
7929
- return c.json({ error: e.message }, e.status);
7999
+ return c.json({ error: e.message }, failedWith(c, e));
7930
8000
  throw e;
7931
8001
  }
7932
8002
  });
@@ -7977,7 +8047,7 @@ export function createControlPlaneApi(options) {
7977
8047
  }
7978
8048
  catch (e) {
7979
8049
  if (e instanceof ControlPlaneError)
7980
- return c.json({ error: e.message }, e.status);
8050
+ return c.json({ error: e.message }, failedWith(c, e));
7981
8051
  throw e;
7982
8052
  }
7983
8053
  });