@substrat-run/control-plane-api 0.131.0 → 0.133.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 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
@@ -508,6 +508,7 @@ const TENANT_ROUTES = [
508
508
  // Integrations (#605/#726): connections, their grants, the consent round's verify.
509
509
  { method: 'GET', re: /\/tenants\/[^/]+\/connection-grants$/, pin: 'path' },
510
510
  { method: 'GET', re: /\/tenants\/[^/]+\/connections$/, pin: 'path' },
511
+ { method: 'GET', re: /\/tenants\/[^/]+\/connections\/calls$/, pin: 'path' },
511
512
  { method: 'POST', re: /\/tenants\/[^/]+\/connections$/, pin: 'path' },
512
513
  { method: 'DELETE', re: /\/tenants\/[^/]+\/connections\/[^/]+$/, pin: 'path' },
513
514
  { method: 'GET', re: /\/tenants\/[^/]+\/connections\/[^/]+\/activity$/, pin: 'path' },
@@ -1078,7 +1079,7 @@ export function createControlPlaneApi(options) {
1078
1079
  const backup = wantsBackup === true ? await backupScope(c, tenantId, scope) : null;
1079
1080
  const vertical = await verticalForScope(c, scope);
1080
1081
  if (vertical)
1081
- await vertical.deleteScope({ scopeId: scope.id });
1082
+ await vertical.deleteScope({ tenantId, scopeId: scope.id });
1082
1083
  // Tenant teardown reaps every scope and releases every name by design — force past
1083
1084
  // the bound-hostname guard (which fences the interactive per-scope reap route below).
1084
1085
  await c.var.admin.reapScope(actor, tenantId, scope.id, {
@@ -1380,6 +1381,37 @@ export function createControlPlaneApi(options) {
1380
1381
  const buckets = await options.observability.connectorCallsSeries(input);
1381
1382
  return c.json({ hours: input.hours, buckets });
1382
1383
  });
1384
+ // The same dataset, with the tenant predicate enforced by the reader. The tenant
1385
+ // credential can reach only this path, never the fleet-wide sibling above.
1386
+ app.get('/tenants/:tenantId/connections/calls', async (c) => {
1387
+ if (!options.observability?.connectorCallsSeries) {
1388
+ return c.json({ error: 'connector-call analytics are not configured on this control plane' }, 501);
1389
+ }
1390
+ const input = z.object({
1391
+ tenantId: tenantIdSchema,
1392
+ hours: z.coerce.number().int().min(1).max(72).default(24),
1393
+ provider: z.string().regex(/^[A-Za-z0-9_\-.]{1,64}$/).optional(),
1394
+ since: z.string().datetime({ offset: true }).optional(),
1395
+ until: z.string().datetime({ offset: true }).optional(),
1396
+ }).parse({
1397
+ tenantId: c.req.param('tenantId'),
1398
+ hours: c.req.query('hours'),
1399
+ provider: c.req.query('provider') || undefined,
1400
+ since: c.req.query('since'),
1401
+ until: c.req.query('until'),
1402
+ });
1403
+ try {
1404
+ resolveObservabilityWindow(input);
1405
+ }
1406
+ catch (e) {
1407
+ throw new ControlPlaneError(400, e.message);
1408
+ }
1409
+ if (input.since !== undefined && !options.observability.absoluteTenantWindows) {
1410
+ throw new ControlPlaneError(501, 'absolute tenant telemetry windows are not supported by this backend');
1411
+ }
1412
+ const buckets = await options.observability.connectorCallsSeries(input);
1413
+ return c.json({ hours: input.hours, buckets });
1414
+ });
1383
1415
  // -- platform-request backlog, fleet-wide (#1690 §2) -----------------------
1384
1416
  //
1385
1417
  // "Is the platform's own intent drain keeping up" — composed from the SAME ops-failure
@@ -2881,11 +2913,11 @@ export function createControlPlaneApi(options) {
2881
2913
  * 5xx, a timeout) still aborts — an implemented wipe that failed is a retry, not a
2882
2914
  * shrug. Returns true when the storage was stranded.
2883
2915
  */
2884
- const deleteScopeStorageOrStrand = async (vertical, scopeId) => {
2916
+ const deleteScopeStorageOrStrand = async (vertical, tenantId, scopeId) => {
2885
2917
  if (!vertical)
2886
2918
  return false;
2887
2919
  try {
2888
- await vertical.deleteScope({ scopeId });
2920
+ await vertical.deleteScope({ tenantId, scopeId });
2889
2921
  return false;
2890
2922
  }
2891
2923
  catch (e) {
@@ -2910,7 +2942,7 @@ export function createControlPlaneApi(options) {
2910
2942
  // storage-before-row ordering deleteSnapshot itself keeps, so a crash
2911
2943
  // between the two converges on retry.
2912
2944
  const vertical = await verticalForScope(c, scope);
2913
- const storageStranded = await deleteScopeStorageOrStrand(vertical, scopeId);
2945
+ const storageStranded = await deleteScopeStorageOrStrand(vertical, tenantId, scopeId);
2914
2946
  await c.var.host.deleteSnapshot(actor, tenantId, scopeId);
2915
2947
  return c.json({ deleted: scopeId, ...(storageStranded ? { storageStranded: true } : {}) });
2916
2948
  }
@@ -3257,7 +3289,7 @@ export function createControlPlaneApi(options) {
3257
3289
  const vertical = await verticalForScope(c, scope);
3258
3290
  // By this point the backup contract has resolved (a copy landed, or the caller
3259
3291
  // explicitly declined one), so stranding is a bookkeeping fact, not data loss.
3260
- const storageStranded = await deleteScopeStorageOrStrand(vertical, scopeId);
3292
+ const storageStranded = await deleteScopeStorageOrStrand(vertical, tenantId, scopeId);
3261
3293
  await c.var.admin.reapScope(actor, tenantId, scopeId, {
3262
3294
  ...(backup ? { backupRef: backupRefOf(backup) } : {}),
3263
3295
  });
@@ -3692,12 +3724,15 @@ export function createControlPlaneApi(options) {
3692
3724
  // the new version's script before the pointer moves (#1710). The snapshot is taken
3693
3725
  // after a successful carry, before binding, in the script the data is leaving. A scope
3694
3726
  // on the serving script carries nothing, because its route does not follow the version pointer.
3727
+ // A legacy preview pinned there is the exception: its binding must move the data to the
3728
+ // version script and clear the pin, or the preview keeps serving production code (#1724).
3695
3729
  app.post('/tenants/:tenantId/scopes/:scopeId/version', async (c) => {
3696
3730
  const tenantId = tenantIdSchema.parse(c.req.param('tenantId'));
3697
3731
  const scopeId = scopeIdSchema.parse(c.req.param('scopeId'));
3698
3732
  const { versionId, snapshot, acknowledge } = bindScopeVersionBody.parse(await c.req.json());
3699
3733
  const actor = c.get('actor');
3700
3734
  const scope = await c.var.admin.getScopeRecord(actor, tenantId, scopeId);
3735
+ const repairPreviewPin = scope?.kind === 'preview' && Boolean(scope.servingRef);
3701
3736
  // #1756: the apps in this tenant the bind would break, asked BEFORE the carry and the
3702
3737
  // snapshot below, which move data. The host refuses too, whoever calls, but by then a
3703
3738
  // refusal would land after the bytes had moved. It refuses what the bind would refuse
@@ -3711,7 +3746,7 @@ export function createControlPlaneApi(options) {
3711
3746
  let carried = null;
3712
3747
  const carry = async () => {
3713
3748
  if (scope)
3714
- carried = await carryOntoVersion(c, scope, versionId);
3749
+ carried = await carryOntoVersion(c, scope, versionId, { dropServingRef: repairPreviewPin });
3715
3750
  };
3716
3751
  // #1674: after the bind, the scope may route to a different store (the carry's
3717
3752
  // destination); put the directory's recorded OFF positions back there. #1742: the carry
@@ -3723,7 +3758,14 @@ export function createControlPlaneApi(options) {
3723
3758
  };
3724
3759
  // The host's own refusal, should the impact have changed since it was read above (a
3725
3760
  // promote landing in between): relayed as the same 409, never as a 500.
3726
- const bind = (opts) => c.var.admin.bindScopeVersion(actor, tenantId, scopeId, versionId, opts).catch(relayHostRefusal);
3761
+ const bind = async (opts) => {
3762
+ await c.var.admin.bindScopeVersion(actor, tenantId, scopeId, versionId, opts).catch(relayHostRefusal);
3763
+ // Keep the old route until the bind succeeds. If clearing the pin fails, a retry
3764
+ // carries again from the still-serving script before it changes the route.
3765
+ if (repairPreviewPin) {
3766
+ await c.var.admin.setScopeServingRef(actor, tenantId, scopeId, null).catch(relayHostRefusal);
3767
+ }
3768
+ };
3727
3769
  if (snapshot) {
3728
3770
  if (!scope) {
3729
3771
  return c.json({ error: `unknown scope for tenant: (${tenantId}, ${scopeId})` }, 404);
@@ -5799,7 +5841,7 @@ export function createControlPlaneApi(options) {
5799
5841
  await retireClientsOfReapedScope(previewAuthDeps(c), preview);
5800
5842
  const vertical = await verticalForScope(c, preview);
5801
5843
  if (vertical)
5802
- await vertical.deleteScope({ scopeId: preview.id });
5844
+ await vertical.deleteScope({ tenantId: preview.tenantId, scopeId: preview.id });
5803
5845
  await c.var.host.deleteSnapshot(c.get('actor'), preview.tenantId, preview.id);
5804
5846
  };
5805
5847
  /** The preview-login seam (#1704, `preview-auth.ts`): the directory, and each issuer's deployment. */