@authhero/multi-tenancy 15.2.0 → 15.4.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.
Files changed (74) hide show
  1. package/dist/multi-tenancy.cjs +1 -1
  2. package/dist/multi-tenancy.mjs +2968 -1176
  3. package/dist/types/index.d.ts +5 -2
  4. package/dist/types/index.d.ts.map +1 -1
  5. package/dist/types/inheritance/legacy.d.ts.map +1 -1
  6. package/dist/types/inheritance/merge.d.ts +1 -0
  7. package/dist/types/inheritance/merge.d.ts.map +1 -1
  8. package/dist/types/inheritance/read.d.ts.map +1 -1
  9. package/dist/types/inheritance/report.d.ts +6 -2
  10. package/dist/types/inheritance/report.d.ts.map +1 -1
  11. package/dist/types/inheritance/types.d.ts +3 -1
  12. package/dist/types/inheritance/types.d.ts.map +1 -1
  13. package/dist/types/init.d.ts +84 -1
  14. package/dist/types/init.d.ts.map +1 -1
  15. package/dist/types/middleware/config-sync-mode.d.ts +39 -0
  16. package/dist/types/middleware/config-sync-mode.d.ts.map +1 -0
  17. package/dist/types/middleware/index.d.ts +3 -1
  18. package/dist/types/middleware/index.d.ts.map +1 -1
  19. package/dist/types/middleware/protect-synced.d.ts +1 -1
  20. package/dist/types/middleware/protect-synced.d.ts.map +1 -1
  21. package/dist/types/middleware/settings-inheritance.d.ts +20 -2
  22. package/dist/types/middleware/settings-inheritance.d.ts.map +1 -1
  23. package/dist/types/operations/detach.d.ts +85 -0
  24. package/dist/types/operations/detach.d.ts.map +1 -0
  25. package/dist/types/operations/enqueue.d.ts +8 -1
  26. package/dist/types/operations/enqueue.d.ts.map +1 -1
  27. package/dist/types/operations/index.d.ts +7 -1
  28. package/dist/types/operations/index.d.ts.map +1 -1
  29. package/dist/types/operations/recorder.d.ts +2 -0
  30. package/dist/types/operations/recorder.d.ts.map +1 -1
  31. package/dist/types/operations/sync-defaults.d.ts +58 -0
  32. package/dist/types/operations/sync-defaults.d.ts.map +1 -0
  33. package/dist/types/operations/tenant-members-request.d.ts +97 -0
  34. package/dist/types/operations/tenant-members-request.d.ts.map +1 -0
  35. package/dist/types/operations/types.d.ts +2 -0
  36. package/dist/types/operations/types.d.ts.map +1 -1
  37. package/dist/types/rollout/index.d.ts +2 -0
  38. package/dist/types/rollout/index.d.ts.map +1 -1
  39. package/dist/types/rollout/payload.d.ts +15 -0
  40. package/dist/types/rollout/payload.d.ts.map +1 -1
  41. package/dist/types/rollout/resolved-sync.d.ts +112 -0
  42. package/dist/types/rollout/resolved-sync.d.ts.map +1 -0
  43. package/dist/types/rollouts/advance.d.ts +44 -0
  44. package/dist/types/rollouts/advance.d.ts.map +1 -0
  45. package/dist/types/rollouts/config.d.ts +54 -0
  46. package/dist/types/rollouts/config.d.ts.map +1 -0
  47. package/dist/types/rollouts/controller.d.ts +29 -0
  48. package/dist/types/rollouts/controller.d.ts.map +1 -0
  49. package/dist/types/rollouts/enumerate.d.ts +25 -0
  50. package/dist/types/rollouts/enumerate.d.ts.map +1 -0
  51. package/dist/types/rollouts/hashes.d.ts +10 -0
  52. package/dist/types/rollouts/hashes.d.ts.map +1 -0
  53. package/dist/types/rollouts/index.d.ts +21 -0
  54. package/dist/types/rollouts/index.d.ts.map +1 -0
  55. package/dist/types/rollouts/plan.d.ts +29 -0
  56. package/dist/types/rollouts/plan.d.ts.map +1 -0
  57. package/dist/types/rollouts/reconcile.d.ts +38 -0
  58. package/dist/types/rollouts/reconcile.d.ts.map +1 -0
  59. package/dist/types/rollouts/status.d.ts +28 -0
  60. package/dist/types/rollouts/status.d.ts.map +1 -0
  61. package/dist/types/rollouts/trigger.d.ts +52 -0
  62. package/dist/types/rollouts/trigger.d.ts.map +1 -0
  63. package/dist/types/rollouts/types.d.ts +25 -0
  64. package/dist/types/rollouts/types.d.ts.map +1 -0
  65. package/dist/types/rollouts/verify.d.ts +50 -0
  66. package/dist/types/rollouts/verify.d.ts.map +1 -0
  67. package/dist/types/routes/tenants.d.ts.map +1 -1
  68. package/dist/types/types.d.ts +20 -0
  69. package/dist/types/types.d.ts.map +1 -1
  70. package/dist/types/utils/tenant-settings.d.ts +13 -0
  71. package/dist/types/utils/tenant-settings.d.ts.map +1 -0
  72. package/dist/types/utils/tenant-tree.d.ts +2 -1
  73. package/dist/types/utils/tenant-tree.d.ts.map +1 -1
  74. package/package.json +5 -5
@@ -1,9 +1,10 @@
1
1
  import { Hono as e } from "hono";
2
- import { LogTypes as t, MANAGEMENT_API_AUDIENCE as n, MANAGEMENT_API_SCOPES as r, auth0QuerySchema as i, brandingSchema as a, connectionInsertSchema as o, connectionOptionsSchema as s, connectionSchema as c, deepMergePatch as l, emailProviderSchema as u, escapeLuceneValue as d, fetchAll as f, hookInsertSchema as p, init as m, isInteractiveClient as h, listControlPlaneKeys as g, promptSettingSchema as _, provisionDefaultClients as v, resourceServerInsertSchema as y, tenantInsertSchema as b, tenantSchema as x } from "authhero";
3
- import { OpenAPIHono as S, createRoute as C, z as w } from "@hono/zod-openapi";
4
- import { HTTPException as T } from "hono/http-exception";
2
+ import { LogTypes as t, MANAGEMENT_API_AUDIENCE as n, MANAGEMENT_API_SCOPES as r, TenantMemberRequestRejected as i, actionInsertSchema as a, auth0QuerySchema as o, brandingSchema as s, canDecrypt as c, connectionInsertSchema as l, connectionOptionsSchema as u, connectionSchema as d, deepMergePatch as f, emailProviderSchema as p, escapeLuceneValue as m, executeTenantMemberRequest as h, fetchAll as g, hookCodeInsertSchema as _, hookInsertSchema as v, init as y, isInteractiveClient as b, isSyncedRowId as x, listControlPlaneKeys as S, promptSettingSchema as C, provisionDefaultClients as w, resourceServerInsertSchema as T, tenantInsertSchema as E, tenantSchema as D } from "authhero";
3
+ import { HTTPException as O } from "hono/http-exception";
4
+ import { OpenAPIHono as k, createRoute as A, z as j } from "@hono/zod-openapi";
5
+ import { escapeLuceneValue as ee } from "@authhero/adapter-interfaces";
5
6
  //#region src/hooks/access-control.ts
6
- function E(e) {
7
+ function M(e) {
7
8
  let { controlPlaneTenantId: t, requireOrganizationMatch: n = !0 } = e;
8
9
  return { async onTenantAccessValidation(e, r) {
9
10
  if (r === t) return !0;
@@ -14,14 +15,14 @@ function E(e) {
14
15
  return !0;
15
16
  } };
16
17
  }
17
- function D(e, t, n, r) {
18
+ function N(e, t, n, r) {
18
19
  if (t === n) return !0;
19
20
  let i = r || e;
20
21
  return i ? i.toLowerCase() === t.toLowerCase() : !1;
21
22
  }
22
23
  //#endregion
23
24
  //#region src/hooks/database.ts
24
- function ee(e) {
25
+ function te(e) {
25
26
  return { async resolveDataAdapters(t) {
26
27
  try {
27
28
  return await e.getAdapters(t);
@@ -33,18 +34,18 @@ function ee(e) {
33
34
  }
34
35
  //#endregion
35
36
  //#region src/operations/instance-id.ts
36
- var O = 64;
37
- function k(e) {
37
+ var P = 64;
38
+ function ne(e) {
38
39
  return e.replace(/[^a-zA-Z0-9_-]/g, "-");
39
40
  }
40
- function A(e) {
41
- let t = k(e.id), n = Math.max(0, O - (5 + t.length)), r = k(e.kind).slice(0, n), i = 3 + r.length + 2 + t.length, a = Math.max(0, O - i);
42
- return `op-${r}-${k(e.tenant_id ?? "fleet").slice(0, a)}-${t}`.slice(0, O);
41
+ function re(e) {
42
+ let t = ne(e.id), n = Math.max(0, P - (5 + t.length)), r = ne(e.kind).slice(0, n), i = 3 + r.length + 2 + t.length, a = Math.max(0, P - i);
43
+ return `op-${r}-${ne(e.tenant_id ?? "fleet").slice(0, a)}-${t}`.slice(0, P);
43
44
  }
44
45
  //#endregion
45
46
  //#region src/operations/recorder.ts
46
- var te = 2048;
47
- function j(e) {
47
+ var ie = 2048;
48
+ function F(e) {
48
49
  let t;
49
50
  if (e instanceof Error) t = e.message;
50
51
  else try {
@@ -52,9 +53,9 @@ function j(e) {
52
53
  } catch {
53
54
  t = Object.prototype.toString.call(e);
54
55
  }
55
- return t.slice(0, te);
56
+ return t.slice(0, ie);
56
57
  }
57
- function M(e) {
58
+ function I(e) {
58
59
  let { tenantOperations: t, tenantOperationEvents: n } = e;
59
60
  return {
60
61
  async markRunning(e, n) {
@@ -75,6 +76,9 @@ function M(e) {
75
76
  attempt: t.attempt ?? 1
76
77
  });
77
78
  },
79
+ async setResult(e, n) {
80
+ await t.update(e, { result: n });
81
+ },
78
82
  async markSucceeded(e) {
79
83
  await t.update(e, {
80
84
  status: "succeeded",
@@ -85,24 +89,24 @@ function M(e) {
85
89
  async markFailed(e, n) {
86
90
  await t.update(e, {
87
91
  status: "failed",
88
- error: j(n),
92
+ error: F(n),
89
93
  finished_at: (/* @__PURE__ */ new Date()).toISOString()
90
94
  });
91
95
  }
92
96
  };
93
97
  }
94
- function ne(e) {
98
+ function L(e) {
95
99
  return e === "succeeded" || e === "failed" || e === "cancelled";
96
100
  }
97
101
  //#endregion
98
102
  //#region src/operations/inline-executor.ts
99
- var re = { async do(e, t, n) {
103
+ var ae = { async do(e, t, n) {
100
104
  let r = typeof t == "function" ? t : n;
101
105
  if (!r) throw Error("StepRunner.do called without a function");
102
106
  return r();
103
107
  } };
104
- function ie(e) {
105
- let t = M(e.stores);
108
+ function oe(e) {
109
+ let t = I(e.stores);
106
110
  return {
107
111
  engine: "inline",
108
112
  async start(n) {
@@ -121,7 +125,7 @@ function ie(e) {
121
125
  try {
122
126
  let r = await e.run({
123
127
  operation: n,
124
- step: re
128
+ step: ae
125
129
  });
126
130
  await t.appendEvent(n.id, {
127
131
  step: e.name,
@@ -132,7 +136,7 @@ function ie(e) {
132
136
  throw await t.appendEvent(n.id, {
133
137
  step: e.name,
134
138
  outcome: "failed",
135
- detail: { message: j(r) }
139
+ detail: { message: F(r) }
136
140
  }), await t.markFailed(n.id, r), r;
137
141
  }
138
142
  }
@@ -142,7 +146,7 @@ function ie(e) {
142
146
  }
143
147
  //#endregion
144
148
  //#region src/operations/enqueue.ts
145
- async function ae(e, t, n) {
149
+ async function se(e, t, n) {
146
150
  let r = await e.tenantOperations.create({
147
151
  tenant_id: t.tenant_id,
148
152
  rollout_id: t.rollout_id,
@@ -150,13 +154,14 @@ async function ae(e, t, n) {
150
154
  engine: n,
151
155
  initiated_by: t.initiated_by,
152
156
  target_worker_version: t.target_worker_version,
153
- target_database_version: t.target_database_version
154
- }), i = A(r);
157
+ target_database_version: t.target_database_version,
158
+ input: t.input
159
+ }), i = re(r);
155
160
  try {
156
161
  await e.tenantOperations.update(r.id, { engine_instance_id: i });
157
162
  } catch (t) {
158
163
  try {
159
- await M(e).markFailed(r.id, t);
164
+ await I(e).markFailed(r.id, t);
160
165
  } catch {}
161
166
  throw t;
162
167
  }
@@ -165,713 +170,633 @@ async function ae(e, t, n) {
165
170
  engine_instance_id: i
166
171
  };
167
172
  }
168
- async function oe(e, t, n) {
169
- let r = M(e), i = await ae(e, n, t.engine);
173
+ async function ce(e, t, n, r = {}) {
174
+ let i = I(e), a = await se(e, n, t.engine);
170
175
  try {
171
- await t.start(i);
176
+ await t.start(a);
172
177
  } catch (t) {
178
+ let n = null;
173
179
  try {
174
- let n = await e.tenantOperations.get(i.id);
175
- n && !ne(n.status) && await r.markFailed(i.id, t);
180
+ let r = await e.tenantOperations.get(a.id);
181
+ r && !L(r.status) && await i.markFailed(a.id, t), n = await e.tenantOperations.get(a.id);
176
182
  } catch (e) {
177
183
  console.warn("Failed to record tenant operation failure:", e);
178
184
  }
185
+ if (r.returnFailed && n) return n;
179
186
  throw t;
180
187
  }
181
188
  try {
182
- return await e.tenantOperations.get(i.id) ?? i;
189
+ return await e.tenantOperations.get(a.id) ?? a;
183
190
  } catch (e) {
184
- return console.warn("Failed to re-fetch tenant operation after start:", e), i;
191
+ return console.warn("Failed to re-fetch tenant operation after start:", e), a;
185
192
  }
186
193
  }
187
- //#endregion
188
- //#region src/operations/record-provision.ts
189
- function se(e) {
190
- if (e.tenantOperations && e.tenantOperationEvents) return {
191
- tenantOperations: e.tenantOperations,
192
- tenantOperationEvents: e.tenantOperationEvents
193
- };
194
- }
195
- async function ce(e, t, n) {
196
- let r = se(e);
197
- if (!r) {
198
- await n(void 0);
199
- return;
194
+ async function le(e) {
195
+ let { tenantId: t, parentTenantId: n, getTenant: r, getChildren: i } = e, a = e.maxDepth ?? 2;
196
+ if (!n) return;
197
+ if (t && n === t) throw new O(400, { message: "A tenant cannot be its own parent" });
198
+ let o = new Set(t ? [t] : []), s = 0, c = n;
199
+ for (; c;) {
200
+ if (o.has(c)) throw new O(400, { message: `Setting parent_tenant_id to '${n}' would create a cycle` });
201
+ o.add(c);
202
+ let e = await r(c);
203
+ if (!e) throw new O(400, { message: `Parent tenant '${c}' not found` });
204
+ if (s += 1, s > a) throw new O(400, { message: `Tenant tree is limited to ${a} levels above a tenant` });
205
+ c = e.parent_tenant_id;
200
206
  }
201
- let i = M(r), a = async (e) => {
202
- try {
203
- await e();
204
- } catch (e) {
205
- console.warn("Failed to record tenant operation progress:", e);
207
+ if (t && i) {
208
+ let e = new Set([t]), r = 0, o = [t];
209
+ for (; o.length > 0;) {
210
+ let t = [];
211
+ for (let n of o) for (let r of await i(n)) e.has(r) || (e.add(r), t.push(r));
212
+ if (t.length > 0 && (r += 1, s + r > a)) throw new O(400, { message: `Moving tenant under '${n}' would make its descendant '${t[0]}' exceed the tenant tree limit of ${a} levels` });
213
+ o = t;
206
214
  }
207
- }, o;
208
- if (await a(async () => {
209
- let e = await ae(r, t, "inline");
210
- o = e.id, await i.markRunning(e.id);
211
- }), !o) {
212
- await n(void 0);
213
- return;
214
215
  }
215
- let s = o, c = !1, l = async (e, t, n) => {
216
- c = !0, await a(async () => {
217
- t === "started" && await i.setCurrentStep(s, e), await i.appendEvent(s, {
218
- step: e,
219
- outcome: t,
220
- detail: n
221
- });
222
- });
223
- };
224
- try {
225
- await n(l);
226
- } catch (e) {
227
- throw await a(async () => {
228
- c || await i.appendEvent(s, {
229
- step: t.kind,
230
- outcome: "failed",
231
- detail: { message: j(e) }
232
- }), await i.markFailed(s, e);
233
- }), e;
216
+ }
217
+ //#endregion
218
+ //#region src/inheritance/chain.ts
219
+ async function R(e) {
220
+ let { tenantId: t, getTenant: n, legacyControlPlane: r } = e, i = e.maxDepth ?? 2, a = await n(t);
221
+ if (!a?.parent_tenant_id) return r && r !== t ? [r] : [];
222
+ let o = [], s = new Set([t]), c = a.parent_tenant_id;
223
+ for (; c;) {
224
+ if (s.has(c)) throw new O(400, { message: `Tenant chain for '${t}' contains a cycle at '${c}'` });
225
+ if (s.add(c), o.length >= i) throw new O(400, { message: `Tenant chain for '${t}' is deeper than ${i}` });
226
+ let e = await n(c);
227
+ if (!e) break;
228
+ o.push(c), c = e.parent_tenant_id;
234
229
  }
235
- await a(async () => {
236
- c || await i.appendEvent(s, {
237
- step: t.kind,
238
- outcome: "succeeded"
239
- }), await i.markSucceeded(s);
240
- });
230
+ return o;
241
231
  }
242
232
  //#endregion
243
- //#region src/hooks/provisioning.ts
244
- function le(e) {
245
- return `urn:authhero:tenant:${e.toLowerCase()}`;
233
+ //#region src/inheritance/legacy.ts
234
+ function ue(e) {
235
+ let t = {};
236
+ for (let n of e ?? []) n.value !== void 0 && (t[n.name] = n.value);
237
+ return t;
246
238
  }
247
- function N(e) {
239
+ function de(e, t) {
240
+ if (!e.inherit) return e;
241
+ let n = t.find((t) => t.name === e.name && t.is_system);
242
+ if (!n) return e;
243
+ let r = {
244
+ ...ue(n.secrets),
245
+ ...ue(e.secrets)
246
+ };
248
247
  return {
249
- async beforeCreate(e, t) {
250
- return !t.audience && t.id ? {
251
- ...t,
252
- audience: le(t.id)
253
- } : t;
254
- },
255
- async afterCreate(t, i) {
256
- let { accessControl: a, databaseIsolation: o } = e;
257
- if (a && t.ctx && await ue(t, i, a), o?.onProvision) {
258
- let e = o.onProvision;
259
- o.recordProvisionOperations === !1 ? await e(i.id) : await ce(t.adapters, {
260
- kind: "provision",
261
- tenant_id: i.id,
262
- initiated_by: t.ctx?.var.user?.sub
263
- }, (t) => e(i.id, t));
264
- } else try {
265
- await v(t.adapters, i.id, {
266
- managementApiIdentifier: n,
267
- managementApiScopes: r
268
- });
269
- } catch (e) {
270
- throw console.warn(`Failed to provision default clients for tenant ${i.id}:`, e), e;
271
- }
272
- },
273
- async beforeDelete(t, n) {
274
- let { accessControl: r, databaseIsolation: i } = e;
275
- if (r) try {
276
- let e = (await t.adapters.organizations.list(r.controlPlaneTenantId)).organizations.find((e) => e.name === n);
277
- e && await t.adapters.organizations.remove(r.controlPlaneTenantId, e.id);
278
- } catch (e) {
279
- console.warn(`Failed to remove organization for tenant ${n}:`, e);
280
- }
281
- if (i?.onDeprovision) try {
282
- await i.onDeprovision(n);
283
- } catch (e) {
284
- console.warn(`Failed to deprovision database for tenant ${n}:`, e);
285
- }
286
- }
248
+ ...e,
249
+ code: n.code,
250
+ secrets: Object.entries(r).map(([e, t]) => ({
251
+ name: e,
252
+ value: t
253
+ }))
287
254
  };
288
255
  }
289
- async function ue(e, t, n) {
290
- let { controlPlaneTenantId: r, defaultPermissions: i, defaultRoles: a, issuer: o, adminRoleName: s = "Tenant Admin", adminRoleDescription: c = "Full access to all tenant management operations", addCreatorToOrganization: l = !0 } = n, u = await e.adapters.organizations.create(r, {
291
- name: t.id,
292
- display_name: t.friendly_name || t.id
293
- }), d;
294
- if (o && (d = await fe(e, r, s, c)), l && e.ctx) {
295
- let t = e.ctx.var.user;
296
- if (t?.sub && !await de(e, r, t.sub)) try {
297
- await e.adapters.userOrganizations.create(r, {
298
- user_id: t.sub,
299
- organization_id: u.id
300
- }), d && await e.adapters.userRoles.create(r, t.sub, d, u.id);
301
- } catch (e) {
302
- console.warn(`Failed to add creator ${t.sub} to organization ${u.id}:`, e);
303
- }
304
- }
305
- a && a.length > 0 && console.log(`Would assign roles ${a.join(", ")} to organization ${u.id}`), i && i.length > 0 && console.log(`Would grant permissions ${i.join(", ")} to organization ${u.id}`);
256
+ function fe(e, t) {
257
+ if (!t) return e;
258
+ let n = new Set(e.hooks.map((e) => e.hook_id)), r = t.hooks.filter((e) => He(e) && !n.has(e.hook_id));
259
+ return {
260
+ connections: e.connections.map((e) => Ne(e, t.connections)),
261
+ resourceServers: e.resourceServers.map((e) => e.is_system ? Ie(e, t.resourceServers.find((t) => t.id === e.id) ?? null) : e),
262
+ hooks: [...e.hooks, ...r],
263
+ hookCode: e.hookCode,
264
+ actions: e.actions.map((e) => de(e, t.actions)),
265
+ emailProvider: e.emailProvider ?? t.emailProvider,
266
+ branding: e.branding,
267
+ promptSettings: e.promptSettings
268
+ };
306
269
  }
307
- async function de(e, t, r) {
308
- let i = await e.adapters.userRoles.list(t, r, void 0, "");
309
- for (let r of i) if ((await e.adapters.rolePermissions.list(t, r.id, { per_page: 1e3 })).some((e) => e.permission_name === "admin:organizations" && e.resource_server_identifier === n)) return !0;
310
- return !1;
270
+ //#endregion
271
+ //#region src/rollout/defaults-projection.ts
272
+ function pe(e = {}) {
273
+ return {
274
+ connections: e.connections ?? !0,
275
+ resourceServers: e.resourceServers ?? !0,
276
+ hooks: e.hooks ?? !0,
277
+ emailProvider: e.emailProvider ?? !0,
278
+ branding: e.branding ?? !0,
279
+ promptSettings: e.promptSettings ?? !0
280
+ };
311
281
  }
312
- async function fe(e, t, i, a) {
313
- let o = (await e.adapters.roles.list(t, {})).roles.find((e) => e.name === i);
314
- if (o) return o.id;
315
- let s = await e.adapters.roles.create(t, {
316
- name: i,
317
- description: a
318
- }), c = n, l = r.map((e) => ({
319
- role_id: s.id,
320
- resource_server_identifier: c,
321
- permission_name: e.value
322
- }));
323
- return await e.adapters.rolePermissions.assign(t, s.id, l), s.id;
282
+ function z() {
283
+ return {
284
+ upserted: 0,
285
+ errors: []
286
+ };
324
287
  }
325
- //#endregion
326
- //#region src/hooks/sync.ts
327
- function pe(e, t, n = () => !0) {
328
- let { controlPlaneTenantId: r, getChildTenantIds: i, getAdapters: a } = e, o = /* @__PURE__ */ new Map();
329
- async function s(e, n, r) {
330
- return (await t(e).list(n, {
331
- q: `name:${d(r)}`,
332
- per_page: 1
333
- }))[0] ?? null;
334
- }
335
- async function c(e) {
336
- let n = await i(), o = t(await a(r));
337
- await Promise.all(n.map(async (n) => {
338
- try {
339
- let r = await a(n), i = t(r), c = {
340
- ...o.transform(e),
341
- is_system: !0
342
- }, l = await s(r, n, e.name), u = l ? i.getId(l) : void 0;
343
- if (l && u) {
344
- let e = i.preserveOnUpdate ? i.preserveOnUpdate(l, c) : c;
345
- await i.update(n, u, e);
346
- } else await i.create(n, c);
347
- } catch (t) {
348
- console.error(`Failed to sync ${o.listKey} "${e.name}" to tenant "${n}":`, t);
349
- }
350
- }));
351
- }
352
- async function l(e) {
353
- let n = await i();
354
- await Promise.all(n.map(async (n) => {
355
- try {
356
- let r = await a(n), i = t(r), o = await s(r, n, e), c = o ? i.getId(o) : void 0;
357
- o && c && await i.remove(n, c);
358
- } catch (t) {
359
- console.error(`Failed to delete entity "${e}" from tenant "${n}":`, t);
360
- }
361
- }));
362
- }
288
+ function me(e) {
363
289
  return {
364
- afterCreate: async (e, t) => {
365
- e.tenantId === r && n(t) && await c(t);
366
- },
367
- afterUpdate: async (e, t, i) => {
368
- e.tenantId === r && n(i) && await c(i);
369
- },
370
- beforeDelete: async (e, i) => {
371
- if (e.tenantId !== r) return;
372
- let a = await t(e.adapters).get(e.tenantId, i);
373
- a && n(a) && o.set(i, a);
374
- },
375
- afterDelete: async (e, t) => {
376
- if (e.tenantId !== r) return;
377
- let n = o.get(t);
378
- n && (o.delete(t), await l(n.name));
379
- }
290
+ tenantId: e,
291
+ connections: z(),
292
+ resourceServers: z(),
293
+ hooks: z(),
294
+ emailProvider: z(),
295
+ branding: z(),
296
+ promptSettings: z()
380
297
  };
381
298
  }
382
- function me(e, t, n = () => !0) {
383
- let { controlPlaneTenantId: r, getControlPlaneAdapters: i, getAdapters: a } = e;
384
- return { async afterCreate(e, o) {
385
- if (o.id !== r) try {
386
- let e = await i(), s = await a(o.id), c = t(e), l = t(s), u = await f((e) => c.listPaginated(r, e), c.listKey, {
387
- cursorField: "id",
388
- pageSize: 100
389
- });
390
- await Promise.all(u.filter((e) => n(e)).map(async (e) => {
391
- try {
392
- let t = c.transform(e);
393
- await l.create(o.id, {
394
- ...t,
395
- is_system: !0
396
- });
397
- } catch (e) {
398
- console.error(`Failed to sync entity to new tenant "${o.id}":`, e);
399
- }
400
- }));
401
- } catch (e) {
402
- console.error(`Failed to sync entities to new tenant "${o.id}":`, e);
403
- }
404
- } };
299
+ function he(e) {
300
+ let t = e.metadata;
301
+ return !!(t && t.inheritable === !0);
405
302
  }
406
- var he = (e) => ({
407
- list: async (t, n) => (await e.resourceServers.list(t, n)).resource_servers,
408
- listPaginated: (t, n) => e.resourceServers.list(t, n),
409
- get: (t, n) => e.resourceServers.get(t, n),
410
- create: (t, n) => e.resourceServers.create(t, n),
411
- update: (t, n, r) => e.resourceServers.update(t, n, r),
412
- remove: (t, n) => e.resourceServers.remove(t, n),
413
- listKey: "resource_servers",
414
- getId: (e) => e.id,
415
- transform: (e) => ({
416
- id: e.id,
417
- name: e.name,
418
- identifier: e.identifier,
419
- scopes: e.scopes,
420
- signing_alg: e.signing_alg,
421
- token_lifetime: e.token_lifetime,
422
- token_lifetime_for_web: e.token_lifetime_for_web
423
- })
424
- }), ge = (e) => ({
425
- list: async (t, n) => (await e.roles.list(t, n)).roles,
426
- listPaginated: (t, n) => e.roles.list(t, n),
427
- get: (t, n) => e.roles.get(t, n),
428
- create: (t, n) => e.roles.create(t, n),
429
- update: (t, n, r) => e.roles.update(t, n, r),
430
- remove: (t, n) => e.roles.remove(t, n),
431
- listKey: "roles",
432
- getId: (e) => e.id,
433
- transform: (e) => ({
434
- id: e.id,
435
- name: e.name,
436
- description: e.description
437
- })
438
- });
439
- function _e(e) {
440
- return e.metadata?.sync !== !1;
303
+ async function B(e, t, n, r) {
304
+ try {
305
+ await r();
306
+ } catch (r) {
307
+ let i = `${t}: ${r instanceof Error ? r.message : String(r)}`;
308
+ if (!n) throw Error(i, { cause: r });
309
+ e.errors.push(i);
310
+ }
441
311
  }
442
- function ve(e) {
443
- let { sync: t = {}, filters: n = {} } = e, r = t.resourceServers ?? !0, i = t.roles ?? !0, a = (e) => _e(e) ? n.resourceServers ? n.resourceServers(e) : !0 : !1, o = (e) => _e(e) ? n.roles ? n.roles(e) : !0 : !1, s = r ? pe(e, he, a) : void 0, c = i ? pe(e, ge, o) : void 0, l = r ? me(e, he, a) : void 0, u = i ? me(e, ge, o) : void 0, p = i ? { async afterCreate(t, r) {
444
- if (r.id !== e.controlPlaneTenantId) {
445
- await u?.afterCreate?.(t, r);
446
- try {
447
- let t = await e.getControlPlaneAdapters(), i = await e.getAdapters(r.id), a = await f((n) => t.roles.list(e.controlPlaneTenantId, n), "roles", {
448
- cursorField: "id",
449
- pageSize: 100
450
- }), o = /* @__PURE__ */ new Map();
451
- for (let e of a.filter((e) => n.roles?.(e) ?? !0)) {
452
- let t = await m(i, r.id, e.name);
453
- t && o.set(e.name, t.id);
454
- }
455
- for (let s of a.filter((e) => n.roles?.(e) ?? !0)) {
456
- let n = o.get(s.name);
457
- if (n) try {
458
- let a = await t.rolePermissions.list(e.controlPlaneTenantId, s.id, {});
459
- a.length > 0 && await i.rolePermissions.assign(r.id, n, a.map((e) => ({
460
- role_id: n,
461
- resource_server_identifier: e.resource_server_identifier,
462
- permission_name: e.permission_name
463
- })));
464
- } catch (e) {
465
- console.error(`Failed to sync permissions for role "${s.name}" to tenant "${r.id}":`, e);
466
- }
467
- }
468
- } catch (e) {
469
- console.error(`Failed to sync role permissions to tenant "${r.id}":`, e);
470
- }
471
- }
472
- } } : void 0;
473
- async function m(e, t, n) {
474
- return (await e.roles.list(t, {
475
- q: `name:${d(n)}`,
476
- per_page: 1
477
- })).roles[0] ?? null;
312
+ async function ge(e, t, n) {
313
+ return {
314
+ connections: n.connections ? await g((n) => e.connections.list(t, n), "connections", {
315
+ cursorField: "id",
316
+ pageSize: 100
317
+ }) : [],
318
+ resourceServers: n.resourceServers ? await g((n) => e.resourceServers.list(t, n), "resource_servers", {
319
+ cursorField: "id",
320
+ pageSize: 100
321
+ }) : [],
322
+ hooks: n.hooks ? await g((n) => e.hooks.list(t, n), "hooks", {
323
+ cursorField: "hook_id",
324
+ pageSize: 100
325
+ }) : [],
326
+ emailProvider: n.emailProvider ? await e.emailProviders.get(t) ?? null : null,
327
+ branding: n.branding ? await e.branding.get(t) ?? null : null,
328
+ promptSettings: n.promptSettings ? await e.promptSettings.get(t) ?? null : null
329
+ };
330
+ }
331
+ async function _e(e, t, n, r, i, a) {
332
+ if (r.connections) for (let r of e.connections) {
333
+ let e = r.id;
334
+ e && await B(a.connections, `connection ${e}`, i, async () => {
335
+ let i = l.parse(r);
336
+ await t.connections.get(n, e) ? await t.connections.update(n, e, i) : await t.connections.create(n, i), a.connections.upserted += 1;
337
+ });
338
+ }
339
+ if (r.resourceServers) for (let r of e.resourceServers) !r.is_system || !r.id || await B(a.resourceServers, `resource_server ${r.id}`, i, async () => {
340
+ let e = T.parse(r);
341
+ await t.resourceServers.get(n, r.id) ? await t.resourceServers.update(n, r.id, e) : await t.resourceServers.create(n, e), a.resourceServers.upserted += 1;
342
+ });
343
+ if (r.hooks) for (let r of e.hooks) !he(r) || !r.hook_id || await B(a.hooks, `hook ${r.hook_id}`, i, async () => {
344
+ let e = v.parse(r);
345
+ await t.hooks.get(n, r.hook_id) ? await t.hooks.update(n, r.hook_id, e) : await t.hooks.create(n, e), a.hooks.upserted += 1;
346
+ });
347
+ r.emailProvider && e.emailProvider && await B(a.emailProvider, "email_provider", i, async () => {
348
+ let r = e.emailProvider;
349
+ await t.emailProviders.get(n) ? await t.emailProviders.update(n, r) : await t.emailProviders.create(n, r), a.emailProvider.upserted += 1;
350
+ }), r.branding && e.branding && await B(a.branding, "branding", i, async () => {
351
+ await t.branding.set(n, e.branding), a.branding.upserted += 1;
352
+ }), r.promptSettings && e.promptSettings && await B(a.promptSettings, "prompt_settings", i, async () => {
353
+ await t.promptSettings.set(n, e.promptSettings), a.promptSettings.upserted += 1;
354
+ });
355
+ }
356
+ async function ve(e, t) {
357
+ let { controlPlaneTenantId: n, getControlPlaneAdapters: r, getAdapters: i, entities: a, continueOnError: o = !1 } = e, s = pe(a), c = await ge(await r(), n, s), l = await i(t), u = me(t);
358
+ return await _e(c, l, n, s, o, u), u;
359
+ }
360
+ //#endregion
361
+ //#region src/inheritance/read.ts
362
+ async function V(e, t) {
363
+ let n = await ge(e, t, {
364
+ connections: !0,
365
+ resourceServers: !0,
366
+ hooks: !0,
367
+ emailProvider: !0,
368
+ branding: !0,
369
+ promptSettings: !0
370
+ }), r = await g((n) => e.actions.list(t, n), "actions", {
371
+ cursorField: "id",
372
+ pageSize: 100
373
+ }), i = new Set(r.map((e) => e.id)), a = /* @__PURE__ */ new Set();
374
+ for (let e of n.hooks) {
375
+ let t = e.code_id;
376
+ t && !i.has(t) && a.add(t);
377
+ }
378
+ let o = [];
379
+ for (let n of a) {
380
+ let r = await e.hookCode.get(t, n);
381
+ r && o.push(r);
478
382
  }
479
383
  return {
480
- entityHooks: {
481
- resourceServers: s,
482
- roles: c
483
- },
484
- tenantHooks: { async afterCreate(e, t) {
485
- let n = [l?.afterCreate, p?.afterCreate ?? u?.afterCreate], r = [];
486
- for (let i of n) if (i) try {
487
- await i(e, t);
488
- } catch (e) {
489
- r.push(e instanceof Error ? e : Error(String(e)));
490
- }
491
- if (r.length === 1) throw r[0];
492
- if (r.length > 1) throw AggregateError(r, r.map((e) => e.message).join("; "));
493
- } }
384
+ ...n,
385
+ actions: r,
386
+ hookCode: o
494
387
  };
495
388
  }
496
389
  //#endregion
497
- //#region src/middleware/request-data.ts
498
- function P(e) {
499
- return e.var.data ?? e.env.data;
390
+ //#region src/inheritance/shadow.ts
391
+ var ye = /secret|password|private_key|api_key|token/i, be = new Set([
392
+ "id",
393
+ "tenant_id",
394
+ "created_at",
395
+ "updated_at",
396
+ "scope",
397
+ "override",
398
+ "enforce",
399
+ "enabled_clients"
400
+ ]);
401
+ async function H(e) {
402
+ let t = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(e));
403
+ return [...new Uint8Array(t)].map((e) => e.toString(16).padStart(2, "0")).join("").slice(0, 16);
500
404
  }
501
- async function ye(e) {
502
- let { tenantId: t, parentTenantId: n, getTenant: r, getChildren: i } = e, a = e.maxDepth ?? 2;
503
- if (!n) return;
504
- if (t && n === t) throw new T(400, { message: "A tenant cannot be its own parent" });
505
- let o = new Set(t ? [t] : []), s = 0, c = n;
506
- for (; c;) {
507
- if (o.has(c)) throw new T(400, { message: `Setting parent_tenant_id to '${n}' would create a cycle` });
508
- o.add(c);
509
- let e = await r(c);
510
- if (!e) throw new T(400, { message: `Parent tenant '${c}' not found` });
511
- if (s += 1, s > a) throw new T(400, { message: `Tenant tree is limited to ${a} levels above a tenant` });
512
- c = e.parent_tenant_id;
405
+ async function xe(e, t = "") {
406
+ if (Array.isArray(e)) {
407
+ if (t === "secrets") {
408
+ let t = {};
409
+ for (let n of e) t[n.name] = n.value === void 0 ? null : `sha256:${await H(n.value)}`;
410
+ return t;
411
+ }
412
+ return Promise.all(e.map((e) => xe(e, t)));
513
413
  }
514
- if (t && i) {
515
- let e = new Set([t]), r = 0, o = [t];
516
- for (; o.length > 0;) {
517
- let t = [];
518
- for (let n of o) for (let r of await i(n)) e.has(r) || (e.add(r), t.push(r));
519
- if (t.length > 0 && (r += 1, s + r > a)) throw new T(400, { message: `Moving tenant under '${n}' would make its descendant '${t[0]}' exceed the tenant tree limit of ${a} levels` });
520
- o = t;
414
+ if (e && typeof e == "object") {
415
+ let t = {};
416
+ for (let n of Object.keys(e).sort()) {
417
+ let r = e[n];
418
+ r != null && (t[n] = typeof r == "string" && ye.test(n) ? `sha256:${await H(r)}` : await xe(r, n));
521
419
  }
420
+ return t;
522
421
  }
422
+ return e;
523
423
  }
524
- //#endregion
525
- //#region src/routes/tenants.ts
526
- var be = w.object({
527
- sub: w.string(),
528
- tenant_id: w.string().optional(),
529
- org_id: w.string().optional(),
530
- scope: w.string().optional(),
531
- permissions: w.array(w.string()).optional()
532
- }).passthrough();
533
- function xe(e) {
534
- let t = be.safeParse(e);
535
- return t.success ? t.data : void 0;
424
+ async function Se(e) {
425
+ let t = {};
426
+ for (let [n, r] of Object.entries(e)) be.has(n) || (t[n] = r);
427
+ return await xe(t);
536
428
  }
537
- function Se(e) {
538
- let t = e.permissions ?? [], n = e.scope ? e.scope.split(" ").filter(Boolean) : [], r = new Set([...t, ...n]);
539
- return r.has("delete:tenants") || r.has("admin:organizations");
429
+ var Ce = {
430
+ connection: (e) => `${e.strategy}:${e.name}`,
431
+ resourceServer: (e) => e.identifier,
432
+ hook: (e) => e.hook_id,
433
+ action: (e) => e.name
434
+ }, we = new Set([
435
+ "connection",
436
+ "resourceServer",
437
+ "action",
438
+ "emailProvider",
439
+ "branding",
440
+ "promptSettings"
441
+ ]);
442
+ async function Te(e) {
443
+ let t = /* @__PURE__ */ new Map(), n = async (e, n, r) => {
444
+ let i = t.get(e) ?? /* @__PURE__ */ new Map();
445
+ i.set(n, await Se(r)), t.set(e, i);
446
+ };
447
+ for (let [t, r] of Object.entries(Ce)) {
448
+ let i = e[{
449
+ connection: "connections",
450
+ resourceServer: "resourceServers",
451
+ hook: "hooks",
452
+ action: "actions"
453
+ }[t]];
454
+ for (let e of i) await n(t, r(e), e);
455
+ }
456
+ for (let t of [
457
+ "emailProvider",
458
+ "branding",
459
+ "promptSettings"
460
+ ]) e[t] && await n(t, "singleton", e[t]);
461
+ return t;
540
462
  }
541
- function F(e, t) {
542
- let n = new S();
543
- return n.openapi(C({
544
- tags: ["tenants"],
545
- method: "get",
546
- path: "/",
547
- request: { query: i },
548
- security: [{ Bearer: [] }],
549
- responses: { 200: {
550
- content: { "application/json": { schema: w.object({
551
- tenants: w.array(x),
552
- start: w.number().optional(),
553
- limit: w.number().optional(),
554
- length: w.number().optional(),
555
- total: w.number().optional()
556
- }) } },
557
- description: "List of tenants"
558
- } }
559
- }), async (t) => {
560
- let { page: n, per_page: r, include_totals: i, q: a } = t.req.valid("query"), o = t.var.user, s = o?.permissions || [];
561
- if (!(o?.org_id ?? t.var.organization_id) && s.includes("admin:organizations")) {
562
- let e = await P(t).tenants.list({
563
- page: n,
564
- per_page: r,
565
- include_totals: i,
566
- q: a
463
+ async function Ee(e, t) {
464
+ let [n, r] = await Promise.all([Te(e), Te(t)]), i = [], a = new Set([...n.keys(), ...r.keys()]);
465
+ for (let e of a) {
466
+ let t = n.get(e) ?? /* @__PURE__ */ new Map(), a = r.get(e) ?? /* @__PURE__ */ new Map();
467
+ for (let n of new Set([...t.keys(), ...a.keys()])) {
468
+ let r = t.get(n), o = a.get(n);
469
+ if (r && !o) i.push({
470
+ entity: e,
471
+ key: n,
472
+ kind: "missing",
473
+ fields: [],
474
+ expected: !1
567
475
  });
568
- return i ? t.json({
569
- tenants: e.tenants,
570
- start: e.totals?.start ?? 0,
571
- limit: e.totals?.limit ?? r,
572
- length: e.tenants.length,
573
- total: e.totals?.length
574
- }) : t.json({ tenants: e.tenants });
575
- }
576
- let c = e.accessControl?.controlPlaneTenantId ?? P(t).multiTenancyConfig?.controlPlaneTenantId;
577
- if (c && !o?.sub) throw new T(403, { message: "Access denied: token has no subject" });
578
- if (c && o?.sub) {
579
- let e = (await f((e) => P(t).userOrganizations.listUserOrganizations(c, o.sub, e), "organizations")).map((e) => e.name);
580
- if (e.length === 0) return i ? t.json({
581
- tenants: [],
582
- start: 0,
583
- limit: r ?? 50,
584
- length: 0,
585
- total: 0
586
- }) : t.json({ tenants: [] });
587
- let s = e.length, l = n ?? 0, u = r ?? 50, d = l * u, p = e.slice(d, d + u);
588
- if (p.length === 0) return i ? t.json({
589
- tenants: [],
590
- start: d,
591
- limit: u,
592
- length: 0,
593
- total: s
594
- }) : t.json({ tenants: [] });
595
- let m = p.map((e) => `id:${e}`).join(" OR "), h = a ? `(${m}) AND (${a})` : m, g = await P(t).tenants.list({
596
- q: h,
597
- per_page: u,
598
- include_totals: !1
476
+ else if (!r && o) i.push({
477
+ entity: e,
478
+ key: n,
479
+ kind: "extra",
480
+ fields: [],
481
+ expected: we.has(e)
599
482
  });
600
- return i ? t.json({
601
- tenants: g.tenants,
602
- start: d,
603
- limit: u,
604
- length: g.tenants.length,
605
- total: s
606
- }) : t.json({ tenants: g.tenants });
607
- }
608
- let l = await P(t).tenants.list({
609
- page: n,
610
- per_page: r,
611
- include_totals: i,
612
- q: a
613
- });
614
- return i ? t.json({
615
- tenants: l.tenants,
616
- start: l.totals?.start ?? 0,
617
- limit: l.totals?.limit ?? r,
618
- length: l.tenants.length,
619
- total: l.totals?.length
620
- }) : t.json({ tenants: l.tenants });
621
- }), n.openapi(C({
622
- tags: ["tenants"],
623
- method: "post",
624
- path: "/",
625
- request: { body: { content: { "application/json": { schema: b } } } },
626
- security: [{ Bearer: [] }],
627
- responses: {
628
- 201: {
629
- content: { "application/json": { schema: x } },
630
- description: "Tenant created"
631
- },
632
- 400: { description: "Validation error" },
633
- 409: { description: "Tenant with this ID already exists" }
634
- }
635
- }), async (e) => {
636
- if (!e.var.user?.sub) throw new T(401, { message: "Authentication required to create tenants" });
637
- let n = e.req.valid("json"), r = {
638
- adapters: P(e),
639
- ctx: e
640
- };
641
- t.tenants?.beforeCreate && (n = await t.tenants.beforeCreate(r, n)), await ye({
642
- tenantId: n.id,
643
- parentTenantId: n.parent_tenant_id,
644
- getTenant: (t) => P(e).tenants.get(t)
645
- });
646
- let i = await P(e).tenants.create(n);
647
- return t.tenants?.afterCreate && await t.tenants.afterCreate(r, i), e.json(i, 201);
648
- }), n.openapi(C({
649
- tags: ["tenants"],
650
- method: "delete",
651
- path: "/{id}",
652
- request: { params: w.object({ id: w.string() }) },
653
- security: [{ Bearer: ["delete:tenants"] }],
654
- responses: {
655
- 204: { description: "Tenant deleted" },
656
- 403: { description: "Access denied or cannot delete the control plane" },
657
- 404: { description: "Tenant not found" }
658
- }
659
- }), async (n) => {
660
- let { id: r } = n.req.valid("param"), i = e.accessControl?.controlPlaneTenantId ?? P(n).multiTenancyConfig?.controlPlaneTenantId;
661
- if (i) {
662
- let e = xe(n.var.user);
663
- if (!e?.sub) throw new T(401, { message: "Authentication required" });
664
- if (r === i) throw new T(403, { message: "Cannot delete the control plane" });
665
- let t = n.var.org_name, a = r.toLowerCase(), o = !!t && t.toLowerCase() === a;
666
- if (!o) {
667
- let r = !!(e.org_id ?? n.var.organization_id ?? t), a = !e.tenant_id || e.tenant_id === i;
668
- !r && a && Se(e) && (o = !0);
669
- }
670
- if (o ||= (await f((t) => P(n).userOrganizations.listUserOrganizations(i, e.sub, t), "organizations")).some((e) => e.name?.toLowerCase() === a), !o) throw new T(403, { message: "Access denied to this tenant" });
671
- }
672
- if (!await P(n).tenants.get(r)) throw new T(404, { message: "Tenant not found" });
673
- let a = {
674
- adapters: P(n),
675
- ctx: n
676
- };
677
- return t.tenants?.beforeDelete && await t.tenants.beforeDelete(a, r), await P(n).tenants.remove(r), t.tenants?.afterDelete && await t.tenants.afterDelete(a, r), n.body(null, 204);
678
- }), n.openapi(C({
679
- tags: ["tenants", "settings"],
680
- method: "get",
681
- path: "/settings",
682
- request: { headers: w.object({ "tenant-id": w.string().optional() }) },
683
- security: [{ Bearer: ["read:tenants"] }],
684
- responses: { 200: {
685
- content: { "application/json": { schema: x } },
686
- description: "Current tenant settings"
687
- } }
688
- }), async (e) => {
689
- let t = await P(e).tenants.get(e.var.tenant_id);
690
- if (!t) throw new T(404, { message: "Tenant not found" });
691
- return e.json(t);
692
- }), n.openapi(C({
693
- tags: ["tenants", "settings"],
694
- method: "patch",
695
- path: "/settings",
696
- request: {
697
- headers: w.object({ "tenant-id": w.string().optional() }),
698
- body: { content: { "application/json": { schema: w.object(b.shape).partial() } } }
699
- },
700
- security: [{ Bearer: ["update:tenants"] }],
701
- responses: { 200: {
702
- content: { "application/json": { schema: x } },
703
- description: "Updated tenant settings"
704
- } }
705
- }), async (e) => {
706
- let { id: t, ...n } = e.req.valid("json"), r = await P(e).tenants.get(e.var.tenant_id);
707
- if (!r) throw new T(404, { message: "Tenant not found" });
708
- if ("default_client_id" in n) {
709
- let t = n.default_client_id;
710
- if (typeof t == "string" && t.length > 0) {
711
- let n = await P(e).clients.get(e.var.tenant_id, t);
712
- if (!n) throw new T(400, { message: `Client with id '${t}' not found` });
713
- if (!h(n)) throw new T(400, { message: `Client '${t}' is not an interactive client and cannot be used as the default client` });
483
+ else if (r && o) {
484
+ let t = JSON.stringify(r), a = JSON.stringify(o);
485
+ if (t === a) continue;
486
+ let s = [...new Set([...Object.keys(r), ...Object.keys(o)])].filter((e) => JSON.stringify(r[e]) !== JSON.stringify(o[e])).sort();
487
+ i.push({
488
+ entity: e,
489
+ key: n,
490
+ kind: "changed",
491
+ fields: s,
492
+ expected: !1,
493
+ legacy_hash: await H(t),
494
+ new_hash: await H(a)
495
+ });
714
496
  }
715
497
  }
716
- "parent_tenant_id" in n && await ye({
717
- tenantId: e.var.tenant_id,
718
- parentTenantId: n.parent_tenant_id,
719
- getTenant: (t) => P(e).tenants.get(t),
720
- getChildren: async (t) => (await f((t) => P(e).tenants.list(t), "tenants")).filter((e) => e.parent_tenant_id === t).map((e) => e.id)
721
- });
722
- let i = l(r, n);
723
- await P(e).tenants.update(e.var.tenant_id, i);
724
- let a = await P(e).tenants.get(e.var.tenant_id);
725
- if (!a) throw new T(500, { message: "Failed to retrieve updated tenant" });
726
- return e.json(a);
727
- }), n;
498
+ }
499
+ return i;
500
+ }
501
+ async function De(e) {
502
+ let { adapters: t, tenantId: n, legacyControlPlane: r } = e, i = await R({
503
+ tenantId: n,
504
+ getTenant: (e) => t.tenants.get(e),
505
+ legacyControlPlane: r,
506
+ maxDepth: e.maxDepth
507
+ }), a = await V(t, n), o = await Promise.all(i.map((e) => V(t, e))), s = r && r !== n ? await V(t, r) : void 0, { hidden: c, ...l } = $e(o, a), u = new Set(c);
508
+ return (await Ee(fe(a, s), l)).filter((e) => !u.has(`${e.entity}:${e.key}`));
728
509
  }
729
510
  //#endregion
730
- //#region src/middleware/protect-synced.ts
731
- function Ce(e) {
732
- for (let { pattern: t, type: n } of [
733
- {
734
- pattern: /\/api\/v2\/resource-servers\/([^/]+)$/,
735
- type: "resource_server"
736
- },
737
- {
738
- pattern: /\/api\/v2\/roles\/([^/]+)$/,
739
- type: "role"
740
- },
741
- {
742
- pattern: /\/api\/v2\/connections\/([^/]+)$/,
743
- type: "connection"
511
+ //#region src/inheritance/report.ts
512
+ var Oe = 3600 * 1e3, ke = 1e4;
513
+ function Ae(e, t) {
514
+ let n = t.sample ?? 1, r = t.intervalMs ?? 6e4, i = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Set(), o = /* @__PURE__ */ new Map();
515
+ async function s(n, r) {
516
+ let i = await De({
517
+ adapters: e,
518
+ tenantId: n,
519
+ legacyControlPlane: r
520
+ }), a = Date.now();
521
+ o.size > ke && o.clear();
522
+ for (let s of i) {
523
+ if (s.expected && !t.includeExpected) continue;
524
+ let i = await H([
525
+ n,
526
+ s.entity,
527
+ s.key,
528
+ s.kind,
529
+ s.fields.join(",")
530
+ ].join("|")), c = o.get(i);
531
+ c !== void 0 && a - c < Oe || (o.set(i, a), await je(e, n, s, r));
744
532
  }
745
- ]) {
746
- let r = e.match(t);
747
- if (r && r[1]) return {
748
- type: n,
749
- id: r[1]
750
- };
751
533
  }
752
- return null;
534
+ return { schedule(e, t) {
535
+ if (a.has(e) || n < 1 && Math.random() >= n) return;
536
+ let o = Date.now(), c = i.get(e);
537
+ if (!(c !== void 0 && o - c < r)) return i.set(e, o), a.add(e), s(e, t).catch(() => {}).finally(() => a.delete(e));
538
+ } };
753
539
  }
754
- async function we(e, t, n) {
755
- try {
756
- switch (n.type) {
757
- case "resource_server": return (await e.resourceServers.get(t, n.id))?.is_system === !0;
758
- case "role": return (await e.roles.get(t, n.id))?.is_system === !0;
759
- case "connection": return (await e.connections.get(t, n.id))?.is_system === !0;
760
- default: return !1;
540
+ async function je(e, n, r, i) {
541
+ await e.logs.create(n, {
542
+ type: t.CONFIG_INHERITANCE_DIFF,
543
+ date: (/* @__PURE__ */ new Date()).toISOString(),
544
+ isMobile: !1,
545
+ description: `${r.kind} ${r.entity} ${r.key}`,
546
+ details: {
547
+ shadow: "config-inheritance",
548
+ legacy_control_plane: i,
549
+ ...r
761
550
  }
762
- } catch {
763
- return !1;
764
- }
551
+ });
765
552
  }
766
- function Te(e) {
553
+ function Me(e) {
554
+ let { base: t, getTenant: n, controlPlaneTenantId: r } = e, i = e.ttlMs ?? 1e4, a = e.maxEntries ?? 1e3, o = /* @__PURE__ */ new Map();
555
+ async function s(e) {
556
+ if (e === r) return !1;
557
+ let t = o.get(e), s = Date.now();
558
+ if (t && t.expires > s) return t.synced;
559
+ try {
560
+ let t = await n(e), r = t?.config_sync === "synced" || t?.config_sync === "detached";
561
+ return o.size >= a && o.clear(), o.set(e, {
562
+ synced: r,
563
+ expires: s + i
564
+ }), r;
565
+ } catch {
566
+ return !1;
567
+ }
568
+ }
767
569
  return {
768
- resource_server: "resource server",
769
- role: "role",
770
- connection: "connection"
771
- }[e];
772
- }
773
- function Ee() {
774
- return async (e, t) => {
775
- if (![
776
- "PATCH",
777
- "PUT",
778
- "DELETE"
779
- ].includes(e.req.method)) return t();
780
- let n = Ce(e.req.path);
781
- if (!n) return t();
782
- let r = e.var.tenant_id || e.req.header("x-tenant-id") || e.req.header("tenant-id");
783
- if (!r) return t();
784
- if (await we(P(e), r, n)) throw new T(403, { message: `This ${Te(n.type)} is a system resource and cannot be modified. Make changes in the control plane instead.` });
785
- return t();
570
+ isSynced: s,
571
+ invalidate(e) {
572
+ e ? o.delete(e) : o.clear();
573
+ },
574
+ async resolve({ tenant_id: e }) {
575
+ let n = await t(e);
576
+ return !n || n.tenantId === e ? n : await s(e) ? void 0 : n;
577
+ }
786
578
  };
787
579
  }
788
580
  //#endregion
789
- //#region src/inheritance/chain.ts
790
- async function De(e) {
791
- let { tenantId: t, getTenant: n, legacyControlPlane: r } = e, i = e.maxDepth ?? 2, a = await n(t);
792
- if (!a?.parent_tenant_id) return r && r !== t ? [r] : [];
793
- let o = [], s = new Set([t]), c = a.parent_tenant_id;
794
- for (; c;) {
795
- if (s.has(c)) throw new T(400, { message: `Tenant chain for '${t}' contains a cycle at '${c}'` });
796
- if (s.add(c), o.length >= i) throw new T(400, { message: `Tenant chain for '${t}' is deeper than ${i}` });
797
- let e = await n(c);
798
- if (!e) break;
799
- o.push(c), c = e.parent_tenant_id;
800
- }
801
- return o;
581
+ //#region src/middleware/settings-inheritance.ts
582
+ function Ne(e, t) {
583
+ let n = t.find((t) => t.strategy === e.strategy);
584
+ if (!n?.options) return e;
585
+ let r = d.passthrough().parse({
586
+ ...n,
587
+ ...e
588
+ });
589
+ return r.options = u.passthrough().parse({
590
+ ...n.options || {},
591
+ ...e.options
592
+ }), r;
802
593
  }
803
- //#endregion
804
- //#region src/inheritance/legacy.ts
805
- function Oe(e) {
806
- let t = {};
807
- for (let n of e ?? []) n.value !== void 0 && (t[n.name] = n.value);
808
- return t;
594
+ function Pe(e, t) {
595
+ let n = [...t || [], ...e || []];
596
+ return [...new Set(n)];
809
597
  }
810
- function ke(e, t) {
811
- if (!e.inherit) return e;
812
- let n = t.find((t) => t.name === e.name && t.is_system);
813
- if (!n) return e;
814
- let r = {
815
- ...Oe(n.secrets),
816
- ...Oe(e.secrets)
598
+ function Fe(e, t) {
599
+ if (!t?.length) return e || [];
600
+ if (!e?.length) return t;
601
+ let n = /* @__PURE__ */ new Map();
602
+ for (let e of t) n.set(e.value, e);
603
+ for (let t of e) n.set(t.value, t);
604
+ return Array.from(n.values());
605
+ }
606
+ function Ie(e, t) {
607
+ return t ? {
608
+ ...e,
609
+ scopes: Fe(e.scopes, t.scopes)
610
+ } : e;
611
+ }
612
+ function Le(e, t) {
613
+ return t ? {
614
+ ...e,
615
+ callbacks: Pe(e.callbacks, t.callbacks),
616
+ web_origins: Pe(e.web_origins, t.web_origins),
617
+ allowed_logout_urls: Pe(e.allowed_logout_urls, t.allowed_logout_urls),
618
+ allowed_origins: Pe(e.allowed_origins, t.allowed_origins)
619
+ } : e;
620
+ }
621
+ function Re(e) {
622
+ let { controlPlaneTenantId: t, controlPlaneClientId: n, resolveControlPlane: r } = e;
623
+ if (r) return async (e) => r({ tenant_id: e });
624
+ if (!t) return async () => void 0;
625
+ let i = {
626
+ tenantId: t,
627
+ clientId: n
817
628
  };
629
+ return async () => i;
630
+ }
631
+ function ze(e, t) {
818
632
  return {
819
- ...e,
820
- code: n.code,
821
- secrets: Object.entries(r).map(([e, t]) => ({
822
- name: e,
823
- value: t
824
- }))
633
+ ...e.resourceServers,
634
+ get: async (n, r) => {
635
+ let i = await e.resourceServers.get(n, r);
636
+ if (!i) return i;
637
+ let a = await t(n);
638
+ return !a || n === a.tenantId || !i.is_system ? i : Ie(i, await e.resourceServers.get(a.tenantId, r));
639
+ },
640
+ list: async (n, r) => {
641
+ let i = await e.resourceServers.list(n, r), a = await t(n);
642
+ if (!a || n === a.tenantId) return i;
643
+ let o = a.tenantId, s = i.resource_servers.filter((e) => !!(e.is_system && e.id)).map((e) => e.id);
644
+ if (s.length === 0) return i;
645
+ let c = /* @__PURE__ */ new Map();
646
+ await Promise.all(s.map(async (t) => {
647
+ let n = await e.resourceServers.get(o, t);
648
+ n && c.set(t, n);
649
+ }));
650
+ let l = i.resource_servers.map((e) => e.is_system && e.id ? Ie(e, c.get(e.id) ?? null) : e);
651
+ return {
652
+ ...i,
653
+ resource_servers: l
654
+ };
655
+ }
825
656
  };
826
657
  }
827
- function Ae(e, t) {
828
- if (!t) return e;
829
- let n = new Set(e.hooks.map((e) => e.hook_id)), r = t.hooks.filter((e) => X(e) && !n.has(e.hook_id));
658
+ function Be(e, t) {
659
+ let n = Re({
660
+ controlPlaneTenantId: t.controlPlaneTenantId,
661
+ resolveControlPlane: t.resolveControlPlane
662
+ });
830
663
  return {
831
- connections: e.connections.map((e) => q(e, t.connections)),
832
- resourceServers: e.resourceServers.map((e) => e.is_system ? Y(e, t.resourceServers.find((t) => t.id === e.id) ?? null) : e),
833
- hooks: [...e.hooks, ...r],
834
- actions: e.actions.map((e) => ke(e, t.actions)),
835
- emailProvider: e.emailProvider ?? t.emailProvider,
836
- branding: e.branding,
837
- promptSettings: e.promptSettings
664
+ ...e,
665
+ resourceServers: ze(e, n)
838
666
  };
839
667
  }
840
- //#endregion
841
- //#region src/inheritance/types.ts
842
- var je = () => ({
843
- connections: [],
844
- resourceServers: [],
845
- hooks: [],
846
- actions: [],
668
+ function Ve(e, t) {
669
+ let { controlPlaneTenantId: n, controlPlaneClientId: r, resolveControlPlane: i, inheritanceShadow: a } = t, o = t.configSync ?? Me({
670
+ base: Re({
671
+ controlPlaneTenantId: n,
672
+ controlPlaneClientId: r,
673
+ resolveControlPlane: i
674
+ }),
675
+ getTenant: async (t) => await e.tenants.get(t) ?? null,
676
+ controlPlaneTenantId: n
677
+ }), s = (e) => o.resolve({ tenant_id: e }), c = a ? Ae(e, a === !0 ? {} : a) : void 0, l = c ? async (e) => {
678
+ let t = await s(e);
679
+ return await o.isSynced(e) || c.schedule(e, t?.tenantId), t;
680
+ } : s;
681
+ return {
682
+ ...e,
683
+ multiTenancyConfig: {
684
+ controlPlaneTenantId: n,
685
+ controlPlaneClientId: r,
686
+ resolveControlPlane: o.resolve
687
+ },
688
+ connections: {
689
+ ...e.connections,
690
+ get: async (t, n) => {
691
+ let r = await e.connections.get(t, n);
692
+ if (!r) return r;
693
+ let i = await l(t);
694
+ return !i || t === i.tenantId ? r : Ne(r, (await e.connections.list(i.tenantId)).connections || []);
695
+ },
696
+ list: async (t, n) => {
697
+ let r = await e.connections.list(t, n), i = await l(t);
698
+ if (!i || t === i.tenantId) return r;
699
+ let a = await e.connections.list(i.tenantId), o = r.connections.map((e) => Ne(e, a.connections || []));
700
+ return {
701
+ ...r,
702
+ connections: o
703
+ };
704
+ }
705
+ },
706
+ clientConnections: {
707
+ ...e.clientConnections,
708
+ listByClient: async (t, n) => {
709
+ let r = await e.clientConnections.listByClient(t, n);
710
+ r.length === 0 && (r = (await e.connections.list(t)).connections || []);
711
+ let i = await l(t);
712
+ if (!i || t === i.tenantId) return r;
713
+ let a = await e.connections.list(i.tenantId);
714
+ return r.map((e) => Ne(e, a.connections || []));
715
+ }
716
+ },
717
+ emailProviders: {
718
+ ...e.emailProviders,
719
+ get: async (t) => {
720
+ let n = await e.emailProviders.get(t);
721
+ if (n) return n;
722
+ let r = await l(t);
723
+ return !r || t === r.tenantId ? null : e.emailProviders.get(r.tenantId);
724
+ }
725
+ },
726
+ resourceServers: ze(e, l),
727
+ hooks: Ue(e, l)
728
+ };
729
+ }
730
+ function He(e) {
731
+ if (!e || typeof e != "object") return !1;
732
+ let t = e.metadata;
733
+ return !t || typeof t != "object" ? !1 : t.inheritable === !0;
734
+ }
735
+ function Ue(e, t) {
736
+ return {
737
+ ...e.hooks,
738
+ list: async (n, r) => {
739
+ let i = await e.hooks.list(n, r), a = await t(n);
740
+ if (!a || n === a.tenantId) return i;
741
+ let o = ((await e.hooks.list(a.tenantId, r)).hooks || []).filter(He);
742
+ if (o.length === 0) return i;
743
+ let s = new Set((i.hooks || []).map((e) => e.hook_id)), c = [];
744
+ for (let e of o) s.has(e.hook_id) || e.hook_id && s.has(await it("hook", n, e.hook_id)) || c.push(e);
745
+ return {
746
+ ...i,
747
+ hooks: [...i.hooks || [], ...c],
748
+ length: typeof i.length == "number" ? i.length + c.length : i.length
749
+ };
750
+ },
751
+ get: async (n, r) => {
752
+ let i = await e.hooks.get(n, r);
753
+ if (i) return i;
754
+ let a = await t(n);
755
+ if (!a || n === a.tenantId) return i;
756
+ let o = await e.hooks.get(a.tenantId, r);
757
+ return o && He(o) ? o : null;
758
+ }
759
+ };
760
+ }
761
+ function We(e, t) {
762
+ return Ve(e, t);
763
+ }
764
+ //#endregion
765
+ //#region src/inheritance/types.ts
766
+ var Ge = () => ({
767
+ connections: [],
768
+ resourceServers: [],
769
+ hooks: [],
770
+ hookCode: [],
771
+ actions: [],
847
772
  emailProvider: null,
848
773
  branding: null,
849
774
  promptSettings: null
850
775
  });
851
776
  //#endregion
852
777
  //#region src/inheritance/merge.ts
853
- function I(e, t) {
778
+ function U(e, t) {
854
779
  if (t.scope) return t.scope;
855
780
  switch (e) {
856
781
  case "connection":
857
782
  case "emailProvider": return "shared";
858
783
  case "resourceServer":
859
784
  case "action": return t.is_system ? "shared" : "local";
860
- case "hook": return X(t) ? "shared" : "local";
785
+ case "hook": return He(t) ? "shared" : "local";
861
786
  default: return "local";
862
787
  }
863
788
  }
864
- function L(e) {
789
+ function Ke(e) {
865
790
  return e.override ?? "fields";
866
791
  }
867
- var Me = /secret|password|private_key|api_key|token/i;
868
- function Ne(e, t) {
792
+ var qe = /secret|password|private_key|api_key|token/i;
793
+ function Je(e, t) {
869
794
  let n = { ...e ?? {} };
870
- for (let [e, r] of Object.entries(t ?? {})) Me.test(e) && (n[e] = r);
795
+ for (let [e, r] of Object.entries(t ?? {})) qe.test(e) && (n[e] = r);
871
796
  return n;
872
797
  }
873
- function R(e, t) {
874
- switch (L(e)) {
798
+ function Ye(e, t) {
799
+ switch (Ke(e)) {
875
800
  case "none": return {
876
801
  ...e,
877
802
  id: t.id
@@ -879,7 +804,7 @@ function R(e, t) {
879
804
  case "secrets": return {
880
805
  ...e,
881
806
  id: t.id,
882
- options: Ne(e.options, t.options)
807
+ options: Je(e.options, t.options)
883
808
  };
884
809
  default: return {
885
810
  ...e,
@@ -891,8 +816,8 @@ function R(e, t) {
891
816
  };
892
817
  }
893
818
  }
894
- function Pe(e, t) {
895
- switch (L(e)) {
819
+ function Xe(e, t) {
820
+ switch (Ke(e)) {
896
821
  case "none": return {
897
822
  ...e,
898
823
  id: t.id
@@ -904,46 +829,46 @@ function Pe(e, t) {
904
829
  };
905
830
  default: return {
906
831
  ...t,
907
- scopes: $e(t.scopes, e.scopes)
832
+ scopes: Fe(t.scopes, e.scopes)
908
833
  };
909
834
  }
910
835
  }
911
- function Fe(e) {
836
+ function Ze(e) {
912
837
  return new Map((e ?? []).map((e) => [e.name, e]));
913
838
  }
914
- function Ie(e, t) {
915
- let n = L(e);
839
+ function Qe(e, t) {
840
+ let n = Ke(e);
916
841
  if (n === "none") return {
917
842
  ...e,
918
843
  id: t.id
919
844
  };
920
- let r = Fe(e.secrets);
921
- if (n === "secrets" || n === "fields") for (let [e, n] of Fe(t.secrets)) r.set(e, n);
845
+ let r = Ze(e.secrets);
846
+ if (n === "secrets" || n === "fields") for (let [e, n] of Ze(t.secrets)) r.set(e, n);
922
847
  return {
923
848
  ...n === "fields" && !t.inherit ? t : e,
924
849
  id: t.id,
925
850
  secrets: [...r.values()]
926
851
  };
927
852
  }
928
- function Le(e, t) {
853
+ function $e(e, t) {
929
854
  let n = /* @__PURE__ */ new Set(), r = [...e].reverse().concat(t), i = (e, t, r, i) => r ? t : t.filter((t) => {
930
- let r = I(e, t);
855
+ let r = U(e, t);
931
856
  return r === "local" ? !1 : (r === "platform" && n.add(`${e}:${i(t)}`), !0);
932
- }), a = je(), o = (e) => `${e.strategy}:${e.name}`, s = (e) => e.identifier, c = (e) => e.hook_id ?? "", l = (e) => e.name, u = /* @__PURE__ */ new Map(), d = [], f = /* @__PURE__ */ new Map(), p = /* @__PURE__ */ new Map(), m = /* @__PURE__ */ new Map();
857
+ }), a = Ge(), o = (e) => `${e.strategy}:${e.name}`, s = (e) => e.identifier, c = (e) => e.hook_id ?? "", l = (e) => e.name, u = /* @__PURE__ */ new Map(), d = [], f = /* @__PURE__ */ new Map(), p = /* @__PURE__ */ new Map(), m = /* @__PURE__ */ new Map();
933
858
  r.forEach((e, t) => {
934
859
  let h = t === r.length - 1;
935
860
  for (let t of i("connection", e.connections, h, o)) {
936
861
  let e = u.get(t.strategy);
937
- h ? d.push(e ? R(e, t) : t) : u.set(t.strategy, e ? R(e, t) : t);
862
+ h ? d.push(e ? Ye(e, t) : t) : u.set(t.strategy, e ? Ye(e, t) : t);
938
863
  }
939
864
  for (let t of i("resourceServer", e.resourceServers, h, s)) {
940
865
  let e = f.get(s(t));
941
- f.set(s(t), e ? Pe(e, t) : t);
866
+ f.set(s(t), e ? Xe(e, t) : t);
942
867
  }
943
868
  for (let t of i("hook", e.hooks, h, c)) p.set(c(t), t);
944
869
  for (let t of i("action", e.actions, h, l)) {
945
870
  let e = m.get(l(t));
946
- m.set(l(t), e ? Ie(e, t) : t);
871
+ m.set(l(t), e ? Qe(e, t) : t);
947
872
  }
948
873
  for (let t of [
949
874
  "emailProvider",
@@ -951,485 +876,1662 @@ function Le(e, t) {
951
876
  "promptSettings"
952
877
  ]) {
953
878
  let r = e[t];
954
- r && (h || I(t, r) !== "local") && (!h && I(t, r) === "platform" && n.add(`${t}:singleton`), a[t] = r);
879
+ r && (h || U(t, r) !== "local") && (!h && U(t, r) === "platform" && n.add(`${t}:singleton`), a[t] = r);
955
880
  }
956
881
  });
957
882
  let h = new Set(r[r.length - 1].connections.map((e) => e.strategy));
958
- return a.connections = [...d, ...[...u.values()].filter((e) => !h.has(e.strategy))], a.resourceServers = [...f.values()], a.hooks = [...p.values()], a.actions = [...m.values()], {
883
+ a.connections = [...d, ...[...u.values()].filter((e) => !h.has(e.strategy))], a.resourceServers = [...f.values()], a.hooks = [...p.values()];
884
+ let g = new Set(a.hooks.map((e) => e.code_id).filter((e) => !!e)), _ = /* @__PURE__ */ new Map();
885
+ for (let e of [...r].reverse()) for (let t of e.hookCode) g.has(t.id) && !_.has(t.id) && _.set(t.id, t);
886
+ return a.hookCode = [..._.values()], a.actions = [...m.values()], {
959
887
  ...a,
960
888
  hidden: [...n]
961
889
  };
962
890
  }
963
891
  //#endregion
964
- //#region src/rollout/defaults-projection.ts
965
- function z(e = {}) {
966
- return {
967
- connections: e.connections ?? !0,
968
- resourceServers: e.resourceServers ?? !0,
969
- hooks: e.hooks ?? !0,
970
- emailProvider: e.emailProvider ?? !0,
971
- branding: e.branding ?? !0,
972
- promptSettings: e.promptSettings ?? !0
973
- };
892
+ //#region src/rollout/resolved-sync.ts
893
+ var et = [
894
+ "connection",
895
+ "resourceServer",
896
+ "hookCode",
897
+ "action",
898
+ "hook"
899
+ ];
900
+ function tt(e) {
901
+ return Array.isArray(e) ? `[${e.map(tt).join(",")}]` : e && typeof e == "object" ? `{${Object.entries(e).filter(([, e]) => e !== void 0).sort(([e], [t]) => e < t ? -1 : 1).map(([e, t]) => `${JSON.stringify(e)}:${tt(t)}`).join(",")}}` : JSON.stringify(e) ?? "null";
902
+ }
903
+ async function nt(e) {
904
+ let t = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(e));
905
+ return [...new Uint8Array(t)].map((e) => e.toString(16).padStart(2, "0")).join("");
906
+ }
907
+ var rt = {
908
+ connection: "con_",
909
+ resourceServer: "api_",
910
+ hook: "h_",
911
+ hookCode: "hc_",
912
+ action: "act_"
913
+ };
914
+ async function it(e, t, n) {
915
+ let r = await nt(`${t}|${e}|${n}`);
916
+ return `${rt[e]}sy${r.slice(0, 15)}`;
917
+ }
918
+ function W(e, t) {
919
+ let n = t;
920
+ switch (e) {
921
+ case "connection": return `${n.strategy}:${n.name}`;
922
+ case "resourceServer": return n.identifier;
923
+ case "action": return n.name;
924
+ case "hookCode": return n.id;
925
+ case "hook": return n.hook_id;
926
+ }
974
927
  }
975
- function B() {
928
+ async function at(e, t, n = {}) {
929
+ let r = await R({
930
+ tenantId: t,
931
+ getTenant: (t) => e.tenants.get(t),
932
+ legacyControlPlane: n.legacyControlPlane,
933
+ maxDepth: n.maxDepth
934
+ }), i = $e(await Promise.all(r.map((t) => V(e, t))), Ge()), a = {
935
+ version: 1,
936
+ chain: r,
937
+ connections: i.connections,
938
+ resourceServers: i.resourceServers,
939
+ hookCode: i.hookCode,
940
+ actions: i.actions,
941
+ hooks: i.hooks,
942
+ emailProvider: i.emailProvider,
943
+ branding: i.branding,
944
+ promptSettings: i.promptSettings,
945
+ hidden: i.hidden,
946
+ deletes: n.deletes ?? []
947
+ };
976
948
  return {
977
- upserted: 0,
978
- errors: []
949
+ ...a,
950
+ hash: await nt(tt(a))
979
951
  };
980
952
  }
981
- function Re(e) {
982
- return {
983
- tenantId: e,
984
- connections: B(),
985
- resourceServers: B(),
986
- hooks: B(),
987
- emailProvider: B(),
988
- branding: B(),
989
- promptSettings: B()
953
+ function ot(e, t) {
954
+ if (typeof e == "string") e.startsWith("enc:v1:") && t.add(e);
955
+ else if (Array.isArray(e)) for (let n of e) ot(n, t);
956
+ else if (e && typeof e == "object") for (let n of Object.values(e)) ot(n, t);
957
+ }
958
+ function st(e) {
959
+ let t = e.slice(7), n = t.indexOf(":");
960
+ return n === -1 ? "default" : t.slice(0, n);
961
+ }
962
+ async function ct(e, t) {
963
+ let n = /* @__PURE__ */ new Set();
964
+ ot(e, n);
965
+ let r = /* @__PURE__ */ new Set();
966
+ for (let e of n) await c(e, t) || r.add(st(e));
967
+ return [...r].sort();
968
+ }
969
+ var G = () => ({
970
+ added: 0,
971
+ changed: 0,
972
+ unchanged: 0,
973
+ deleted: 0,
974
+ errors: []
975
+ }), lt = (e) => tt(e);
976
+ async function ut(e, t, n, r = {}) {
977
+ let { dryRun: i = !1, continueOnError: o = !0 } = r;
978
+ if (r.prune && e.chain.length === 0) throw Error(`Cannot prune '${n}': the block resolved no ancestors, so every synced copy would look stale. Nothing was written.`);
979
+ let c = {
980
+ status: i ? "dry-run" : "applied",
981
+ hash: e.hash,
982
+ entities: {
983
+ connection: G(),
984
+ resourceServer: G(),
985
+ hookCode: G(),
986
+ action: G(),
987
+ hook: G(),
988
+ emailProvider: G(),
989
+ branding: G(),
990
+ promptSettings: G()
991
+ },
992
+ changes: [],
993
+ rows: []
994
+ }, u = await t.tenants.get(n), d = !!r.deletes && e.deletes.length > 0;
995
+ if (!i && !r.force && !d && !r.prune && u?.config_sync_hash === e.hash) return {
996
+ ...c,
997
+ status: "up-to-date"
998
+ };
999
+ if (r.keyRing) {
1000
+ let t = await ct(e, r.keyRing);
1001
+ if (t.length > 0) throw Error(`Cannot sync config to '${n}': its key ring cannot decrypt key id(s) ${t.map((e) => `"${e}"`).join(", ")} carried by the payload. Nothing was written.`);
1002
+ }
1003
+ let f = await V(t, n), m = (i || r.prune) && e.chain.length > 0 ? f : null, h = {
1004
+ connection: /* @__PURE__ */ new Set(),
1005
+ resourceServer: /* @__PURE__ */ new Set(),
1006
+ action: /* @__PURE__ */ new Set()
1007
+ };
1008
+ for (let e of f.connections) x(e.id) || h.connection.add(W("connection", e));
1009
+ for (let e of f.resourceServers) x(e.id) || h.resourceServer.add(W("resourceServer", e));
1010
+ for (let e of f.actions) x(e.id) || h.action.add(W("action", e));
1011
+ let y = async (e, t, n) => {
1012
+ let r = c.entities[e];
1013
+ try {
1014
+ let i = await n();
1015
+ r[i] += 1, i !== "unchanged" && (c.changes.push(`${e}:${t}`), c.rows.push({
1016
+ entity: e,
1017
+ key: t,
1018
+ action: i
1019
+ }));
1020
+ } catch (n) {
1021
+ let i = `${e} ${t}: ${n instanceof Error ? n.message : String(n)}`;
1022
+ if (!o) throw Error(i, { cause: n });
1023
+ r.errors.push(i);
1024
+ }
1025
+ }, b = async (e) => e.existing ? lt(e.view(e.existing)) === lt(e.view(e.desired)) ? "unchanged" : (i || await e.update(), "changed") : (i || await e.create(), "added"), S = (e, t) => it(e, n, t), w = /* @__PURE__ */ new Map();
1026
+ for (let t of e.hookCode) w.set(t.id, await S("hookCode", W("hookCode", t)));
1027
+ for (let t of e.actions) w.set(t.id, await S("action", W("action", t)));
1028
+ let E, D = async (e) => {
1029
+ if (!E) {
1030
+ E = [];
1031
+ let e = await g((e) => t.clients.list(n, e), "clients", {
1032
+ cursorField: "client_id",
1033
+ pageSize: 100
1034
+ });
1035
+ for (let r of e) (await t.clientConnections.listByClient(n, r.client_id)).length > 0 && E.push(r.client_id);
1036
+ }
1037
+ for (let r of E) await t.clientConnections.addClientToConnection(n, e, r);
1038
+ }, O = (e, t) => h[e].has(t) ? (c.changes.push(`skip ${e}:${t} (the tenant has its own)`), c.rows.push({
1039
+ entity: e,
1040
+ key: t,
1041
+ action: "skipped"
1042
+ }), !0) : !1;
1043
+ for (let r of e.connections) {
1044
+ let e = W("connection", r), i = await S("connection", e);
1045
+ await y("connection", e, async () => {
1046
+ if (O("connection", e)) return "unchanged";
1047
+ let { enabled_clients: a, ...o } = r, s = l.parse({
1048
+ ...o,
1049
+ id: i
1050
+ }), c = await t.connections.get(n, i);
1051
+ return b({
1052
+ existing: c ? l.parse(c) : null,
1053
+ desired: s,
1054
+ create: async () => {
1055
+ await t.connections.create(n, s);
1056
+ try {
1057
+ await D(i);
1058
+ } catch (e) {
1059
+ throw await t.connections.remove(n, i).catch(() => {}), e;
1060
+ }
1061
+ },
1062
+ update: () => t.connections.update(n, i, s),
1063
+ view: (e) => e
1064
+ });
1065
+ });
1066
+ }
1067
+ for (let r of e.resourceServers) {
1068
+ let e = W("resourceServer", r), i = await S("resourceServer", e);
1069
+ await y("resourceServer", e, async () => {
1070
+ if (O("resourceServer", e)) return "unchanged";
1071
+ let a = T.parse({
1072
+ ...r,
1073
+ id: i
1074
+ }), o = await t.resourceServers.get(n, i);
1075
+ return b({
1076
+ existing: o ? T.parse(o) : null,
1077
+ desired: a,
1078
+ create: () => t.resourceServers.create(n, a),
1079
+ update: () => t.resourceServers.update(n, i, a),
1080
+ view: (e) => e
1081
+ });
1082
+ });
1083
+ }
1084
+ for (let r of e.hookCode) {
1085
+ let e = W("hookCode", r), i = w.get(r.id);
1086
+ await y("hookCode", e, async () => {
1087
+ let e = _.parse(r), a = await t.hookCode.get(n, i);
1088
+ return b({
1089
+ existing: a ? _.parse(a) : null,
1090
+ desired: e,
1091
+ create: () => t.hookCode.create(n, e, { importMetadata: { id: i } }),
1092
+ update: () => t.hookCode.update(n, i, e),
1093
+ view: (e) => e
1094
+ });
1095
+ });
1096
+ }
1097
+ for (let r of e.actions) {
1098
+ let e = W("action", r), i = w.get(r.id);
1099
+ await y("action", e, async () => {
1100
+ if (O("action", e)) return "unchanged";
1101
+ let o = a.parse(r), s = await t.actions.get(n, i);
1102
+ return b({
1103
+ existing: s ? a.parse(s) : null,
1104
+ desired: o,
1105
+ create: () => t.actions.create(n, o, { importMetadata: { id: i } }),
1106
+ update: () => t.actions.update(n, i, o),
1107
+ view: (e) => e
1108
+ });
1109
+ });
1110
+ }
1111
+ for (let r of e.hooks) {
1112
+ let e = W("hook", r), i = await S("hook", e);
1113
+ await y("hook", e, async () => {
1114
+ let e = r.code_id, a = v.parse({
1115
+ ...r,
1116
+ hook_id: i,
1117
+ ...e ? { code_id: w.get(e) ?? e } : {}
1118
+ }), o = await t.hooks.get(n, i);
1119
+ return b({
1120
+ existing: o ? v.parse(o) : null,
1121
+ desired: a,
1122
+ create: () => t.hooks.create(n, a),
1123
+ update: () => t.hooks.update(n, i, a),
1124
+ view: (e) => e
1125
+ });
1126
+ });
1127
+ }
1128
+ let k = (e) => e.override === "none" || e.enforce === !0, A = (e, t, n) => lt(e.parse(t)) === lt(n), j = (e, t) => {
1129
+ let n = { ...t };
1130
+ for (let [t, r] of Object.entries(e)) r != null && n[t] === void 0 && (n[t] = null);
1131
+ return n;
990
1132
  };
1133
+ e.emailProvider && await y("emailProvider", "singleton", async () => {
1134
+ let r = p.parse(e.emailProvider), a = await t.emailProviders.get(n);
1135
+ if (!a) return i || await t.emailProviders.create(n, r), "added";
1136
+ if (!k(r) || A(p, a, r)) return "unchanged";
1137
+ if (!i) {
1138
+ let e = p.parse(a);
1139
+ await t.emailProviders.update(n, j(e, r));
1140
+ }
1141
+ return "changed";
1142
+ }), e.branding && await y("branding", "singleton", async () => {
1143
+ let r = s.parse(e.branding), a = await t.branding.get(n);
1144
+ if (!a) return i || await t.branding.set(n, r), "added";
1145
+ if (!k(r) || A(s, a, r)) return "unchanged";
1146
+ if (!i) {
1147
+ let e = s.parse(a), i = j(e, r);
1148
+ e.colors && !r.colors && (i.colors = { primary: null }), e.font && !r.font && (i.font = { url: null }), await t.branding.set(n, i);
1149
+ }
1150
+ return "changed";
1151
+ }), e.promptSettings && k(e.promptSettings) && await y("promptSettings", "singleton", async () => {
1152
+ let r = C.parse(e.promptSettings);
1153
+ return A(C, await t.promptSettings.get(n), r) ? "unchanged" : (i || await t.promptSettings.set(n, r), "changed");
1154
+ });
1155
+ for (let a of e.deletes) {
1156
+ let e = await S(a.entity, a.key), o = `${a.entity}:${a.key}`;
1157
+ try {
1158
+ if (!await pt(t, n, a.entity, e)) continue;
1159
+ c.changes.push(`delete ${o}`), c.rows.push({
1160
+ entity: a.entity,
1161
+ key: a.key,
1162
+ action: "delete"
1163
+ }), r.deletes && !i && (await mt(t, n, a.entity, e), c.entities[a.entity].deleted += 1);
1164
+ } catch (e) {
1165
+ c.entities[a.entity].errors.push(`delete ${o}: ${e instanceof Error ? e.message : e}`);
1166
+ }
1167
+ }
1168
+ let ee = () => Object.values(c.entities).some((e) => e.errors.length > 0);
1169
+ if (m && !ee()) {
1170
+ let a = /* @__PURE__ */ new Set();
1171
+ for (let t of et) for (let n of dt(e, t)) (t === "connection" || t === "resourceServer" || t === "action") && h[t].has(W(t, n)) || a.add(t === "hookCode" || t === "action" ? w.get(n.id) : await S(t, W(t, n)));
1172
+ for (let e of ft(m, a)) {
1173
+ let a = `${e.entity}:${e.label}`;
1174
+ try {
1175
+ c.changes.push(`delete ${a}`), c.rows.push({
1176
+ entity: e.entity,
1177
+ key: e.label,
1178
+ action: "delete"
1179
+ }), r.prune && !i && (await mt(t, n, e.entity, e.id), c.entities[e.entity].deleted += 1);
1180
+ } catch (t) {
1181
+ c.entities[e.entity].errors.push(`delete ${a}: ${t instanceof Error ? t.message : t}`);
1182
+ }
1183
+ }
1184
+ }
1185
+ let M = ee();
1186
+ return !i && !M && u && await t.tenants.update(n, {
1187
+ config_sync_hash: e.hash,
1188
+ config_synced_at: (/* @__PURE__ */ new Date()).toISOString()
1189
+ }), c;
1190
+ }
1191
+ function dt(e, t) {
1192
+ switch (t) {
1193
+ case "connection": return e.connections;
1194
+ case "resourceServer": return e.resourceServers;
1195
+ case "hookCode": return e.hookCode;
1196
+ case "action": return e.actions;
1197
+ case "hook": return e.hooks;
1198
+ }
991
1199
  }
992
- function ze(e) {
993
- let t = e.metadata;
994
- return !!(t && t.inheritable === !0);
1200
+ function ft(e, t) {
1201
+ let n = (e) => !!e && x(e) && !t.has(e);
1202
+ return [
1203
+ ...e.hooks.filter((e) => n(e.hook_id)).map((e) => ({
1204
+ entity: "hook",
1205
+ id: e.hook_id,
1206
+ label: e.hook_id
1207
+ })),
1208
+ ...e.actions.filter((e) => n(e.id)).map((e) => ({
1209
+ entity: "action",
1210
+ id: e.id,
1211
+ label: e.name
1212
+ })),
1213
+ ...e.hookCode.filter((e) => n(e.id)).map((e) => ({
1214
+ entity: "hookCode",
1215
+ id: e.id,
1216
+ label: e.id
1217
+ })),
1218
+ ...e.connections.filter((e) => n(e.id)).map((e) => ({
1219
+ entity: "connection",
1220
+ id: e.id,
1221
+ label: `${e.strategy}:${e.name}`
1222
+ })),
1223
+ ...e.resourceServers.filter((e) => n(e.id)).map((e) => ({
1224
+ entity: "resourceServer",
1225
+ id: e.id,
1226
+ label: e.identifier
1227
+ }))
1228
+ ];
1229
+ }
1230
+ async function pt(e, t, n, r) {
1231
+ switch (n) {
1232
+ case "connection": return !!await e.connections.get(t, r);
1233
+ case "resourceServer": return !!await e.resourceServers.get(t, r);
1234
+ case "hookCode": return !!await e.hookCode.get(t, r);
1235
+ case "action": return !!await e.actions.get(t, r);
1236
+ case "hook": return !!await e.hooks.get(t, r);
1237
+ }
995
1238
  }
996
- async function V(e, t, n, r) {
997
- try {
998
- await r();
999
- } catch (r) {
1000
- let i = `${t}: ${r instanceof Error ? r.message : String(r)}`;
1001
- if (!n) throw Error(i, { cause: r });
1002
- e.errors.push(i);
1239
+ async function mt(e, t, n, r) {
1240
+ switch (n) {
1241
+ case "connection":
1242
+ await e.connections.remove(t, r);
1243
+ return;
1244
+ case "resourceServer":
1245
+ await e.resourceServers.remove(t, r);
1246
+ return;
1247
+ case "hookCode":
1248
+ await e.hookCode.remove(t, r);
1249
+ return;
1250
+ case "action":
1251
+ await e.actions.remove(t, r);
1252
+ return;
1253
+ case "hook":
1254
+ await e.hooks.remove(t, r);
1255
+ return;
1003
1256
  }
1004
1257
  }
1005
- async function H(e, t, n) {
1258
+ //#endregion
1259
+ //#region src/operations/sync-defaults.ts
1260
+ function ht(e) {
1261
+ let t = e.input ?? {};
1006
1262
  return {
1007
- connections: n.connections ? await f((n) => e.connections.list(t, n), "connections", {
1008
- cursorField: "id",
1009
- pageSize: 100
1010
- }) : [],
1011
- resourceServers: n.resourceServers ? await f((n) => e.resourceServers.list(t, n), "resource_servers", {
1012
- cursorField: "id",
1013
- pageSize: 100
1014
- }) : [],
1015
- hooks: n.hooks ? await f((n) => e.hooks.list(t, n), "hooks", {
1016
- cursorField: "hook_id",
1017
- pageSize: 100
1018
- }) : [],
1019
- emailProvider: n.emailProvider ? await e.emailProviders.get(t) ?? null : null,
1020
- branding: n.branding ? await e.branding.get(t) ?? null : null,
1021
- promptSettings: n.promptSettings ? await e.promptSettings.get(t) ?? null : null
1263
+ dryRun: t.dry_run === !0,
1264
+ force: t.force === !0,
1265
+ prune: t.prune === !0
1022
1266
  };
1023
1267
  }
1024
- async function Be(e, t, n, r, i, a) {
1025
- if (r.connections) for (let r of e.connections) {
1026
- let e = r.id;
1027
- e && await V(a.connections, `connection ${e}`, i, async () => {
1028
- let i = o.parse(r);
1029
- await t.connections.get(n, e) ? await t.connections.update(n, e, i) : await t.connections.create(n, i), a.connections.upserted += 1;
1030
- });
1268
+ function gt(e) {
1269
+ return Object.values(e.entities).flatMap((e) => e.errors);
1270
+ }
1271
+ var _t = 20, vt = 300;
1272
+ function yt(e, t) {
1273
+ return {
1274
+ dry_run: t.dryRun,
1275
+ prune: t.prune,
1276
+ status: e.status,
1277
+ hash: e.hash,
1278
+ changes: e.changes,
1279
+ rows: e.rows ?? [],
1280
+ error_messages: gt(e).slice(0, _t).map((e) => e.slice(0, vt)),
1281
+ entities: Object.fromEntries(Object.entries(e.entities).map(([e, t]) => [e, {
1282
+ added: t.added,
1283
+ changed: t.changed,
1284
+ unchanged: t.unchanged,
1285
+ deleted: t.deleted,
1286
+ errors: t.errors.length
1287
+ }]))
1288
+ };
1289
+ }
1290
+ async function bt(e) {
1291
+ let { operation: t, syncConfig: n, recorder: r } = e;
1292
+ if (!t.tenant_id) throw Error("sync-defaults operations require a tenant_id");
1293
+ let i = ht(t), a = await n(t.tenant_id, i), o = yt(a, i);
1294
+ await r.setResult(t.id, o);
1295
+ let s = gt(a);
1296
+ if (s.length > 0) {
1297
+ let e = s.slice(0, 3).join("; ");
1298
+ throw Error(`sync-defaults reported ${s.length} error(s): ${e}${s.length > 3 ? "; ..." : ""}`);
1031
1299
  }
1032
- if (r.resourceServers) for (let r of e.resourceServers) !r.is_system || !r.id || await V(a.resourceServers, `resource_server ${r.id}`, i, async () => {
1033
- let e = y.parse(r);
1034
- await t.resourceServers.get(n, r.id) ? await t.resourceServers.update(n, r.id, e) : await t.resourceServers.create(n, e), a.resourceServers.upserted += 1;
1035
- });
1036
- if (r.hooks) for (let r of e.hooks) !ze(r) || !r.hook_id || await V(a.hooks, `hook ${r.hook_id}`, i, async () => {
1037
- let e = p.parse(r);
1038
- await t.hooks.get(n, r.hook_id) ? await t.hooks.update(n, r.hook_id, e) : await t.hooks.create(n, e), a.hooks.upserted += 1;
1039
- });
1040
- r.emailProvider && e.emailProvider && await V(a.emailProvider, "email_provider", i, async () => {
1041
- let r = e.emailProvider;
1042
- await t.emailProviders.get(n) ? await t.emailProviders.update(n, r) : await t.emailProviders.create(n, r), a.emailProvider.upserted += 1;
1043
- }), r.branding && e.branding && await V(a.branding, "branding", i, async () => {
1044
- await t.branding.set(n, e.branding), a.branding.upserted += 1;
1045
- }), r.promptSettings && e.promptSettings && await V(a.promptSettings, "prompt_settings", i, async () => {
1046
- await t.promptSettings.set(n, e.promptSettings), a.promptSettings.upserted += 1;
1047
- });
1300
+ return o;
1048
1301
  }
1049
- async function U(e, t) {
1050
- let { controlPlaneTenantId: n, getControlPlaneAdapters: r, getAdapters: i, entities: a, continueOnError: o = !1 } = e, s = z(a), c = await H(await r(), n, s), l = await i(t), u = Re(t);
1051
- return await Be(c, l, n, s, o, u), u;
1302
+ function xt(e, t) {
1303
+ return async (n, r) => {
1304
+ let i = await e.tenants.get(n);
1305
+ if (!i) throw Error(`Tenant "${n}" not found`);
1306
+ if (i.config_sync === "detached") throw Error(`Tenant "${n}" is detached from its parent: set config_sync back to "inherit-on-read" or "synced" before syncing it`);
1307
+ if (i.deployment_type === "wfp") throw Error(`Tenant "${n}" has its own database: configure a dispatch sync (syncDefaults) for it`);
1308
+ return ut(await at(e, n, {
1309
+ legacyControlPlane: (t.resolveControlPlane ? await t.resolveControlPlane({ tenant_id: n }) : { tenantId: t.controlPlaneTenantId })?.tenantId,
1310
+ maxDepth: t.maxDepth
1311
+ }), e, n, {
1312
+ dryRun: r.dryRun,
1313
+ force: r.force,
1314
+ prune: r.prune
1315
+ });
1316
+ };
1052
1317
  }
1053
1318
  //#endregion
1054
- //#region src/inheritance/read.ts
1055
- async function W(e, t) {
1056
- let n = await H(e, t, {
1057
- connections: !0,
1058
- resourceServers: !0,
1059
- hooks: !0,
1060
- emailProvider: !0,
1061
- branding: !0,
1062
- promptSettings: !0
1063
- }), r = await f((n) => e.actions.list(t, n), "actions", {
1064
- cursorField: "id",
1065
- pageSize: 100
1066
- });
1319
+ //#region src/operations/detach.ts
1320
+ function St(e) {
1321
+ let t = e.input ?? {};
1067
1322
  return {
1068
- ...n,
1069
- actions: r
1323
+ dryRun: t.dry_run === !0,
1324
+ clean: t.clean === !0
1070
1325
  };
1071
1326
  }
1072
- //#endregion
1073
- //#region src/inheritance/shadow.ts
1074
- var Ve = /secret|password|private_key|api_key|token/i, He = new Set([
1075
- "id",
1076
- "tenant_id",
1077
- "created_at",
1078
- "updated_at",
1079
- "scope",
1080
- "override",
1081
- "enforce",
1082
- "enabled_clients"
1083
- ]);
1084
- async function G(e) {
1085
- let t = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(e));
1086
- return [...new Uint8Array(t)].map((e) => e.toString(16).padStart(2, "0")).join("").slice(0, 16);
1327
+ var Ct = 20, wt = 300;
1328
+ function Tt(e) {
1329
+ return Object.entries(e.entities).flatMap(([e, t]) => t.errors.map((t) => `${e} ${t}`));
1087
1330
  }
1088
- async function K(e, t = "") {
1089
- if (Array.isArray(e)) {
1090
- if (t === "secrets") {
1091
- let t = {};
1092
- for (let n of e) t[n.name] = n.value === void 0 ? null : `sha256:${await G(n.value)}`;
1093
- return t;
1094
- }
1095
- return Promise.all(e.map((e) => K(e, t)));
1331
+ function Et(e, t) {
1332
+ return {
1333
+ dry_run: t.dryRun,
1334
+ clean: t.clean,
1335
+ status: e.status,
1336
+ changes: e.changes,
1337
+ rows: e.rows ?? [],
1338
+ error_messages: Tt(e).slice(0, Ct).map((e) => e.slice(0, wt)),
1339
+ entities: Object.fromEntries(Object.entries(e.entities).map(([e, t]) => [e, {
1340
+ converted: t.converted,
1341
+ removed: t.removed,
1342
+ skipped: t.skipped,
1343
+ errors: t.errors.length
1344
+ }]))
1345
+ };
1346
+ }
1347
+ async function Dt(e) {
1348
+ let { operation: t, detachTenant: n, recorder: r, onDetached: i } = e;
1349
+ if (!t.tenant_id) throw Error("detach operations require a tenant_id");
1350
+ let a = St(t), o = await n(t.tenant_id, a), s = Et(o, a);
1351
+ await r.setResult(t.id, s);
1352
+ let c = Tt(o);
1353
+ if (c.length === 0 && !a.dryRun && o.status === "detached" && i?.(t.tenant_id), c.length > 0) {
1354
+ let e = c.slice(0, 3).join("; ");
1355
+ throw Error(`detach reported ${c.length} error(s): ${e}${c.length > 3 ? "; ..." : ""}`);
1096
1356
  }
1097
- if (e && typeof e == "object") {
1098
- let t = {};
1099
- for (let n of Object.keys(e).sort()) {
1100
- let r = e[n];
1101
- r != null && (t[n] = typeof r == "string" && Ve.test(n) ? `sha256:${await G(r)}` : await K(r, n));
1357
+ return s;
1358
+ }
1359
+ var K = () => ({
1360
+ converted: 0,
1361
+ removed: 0,
1362
+ skipped: 0,
1363
+ errors: []
1364
+ });
1365
+ function q(e) {
1366
+ let { id: t, hook_id: n, scope: r, override: i, enforce: a, ...o } = e;
1367
+ return o;
1368
+ }
1369
+ function Ot(e) {
1370
+ return {
1371
+ status: e,
1372
+ changes: [],
1373
+ rows: [],
1374
+ entities: {
1375
+ connection: K(),
1376
+ resourceServer: K(),
1377
+ hookCode: K(),
1378
+ action: K(),
1379
+ hook: K()
1102
1380
  }
1103
- return t;
1104
- }
1105
- return e;
1381
+ };
1106
1382
  }
1107
- async function Ue(e) {
1108
- let t = {};
1109
- for (let [n, r] of Object.entries(e)) He.has(n) || (t[n] = r);
1110
- return await K(t);
1383
+ function kt(e, t) {
1384
+ if (t.config_sync !== "synced") throw Error(`Tenant "${e}" is not synced: sync its parent's config into it (config_sync "synced") before detaching, otherwise it would lose what it inherits`);
1111
1385
  }
1112
- var We = {
1113
- connection: (e) => `${e.strategy}:${e.name}`,
1114
- resourceServer: (e) => e.identifier,
1115
- hook: (e) => e.hook_id,
1116
- action: (e) => e.name
1117
- }, Ge = new Set([
1118
- "connection",
1119
- "resourceServer",
1120
- "action",
1121
- "emailProvider",
1122
- "branding",
1123
- "promptSettings"
1124
- ]);
1125
- async function Ke(e) {
1126
- let t = /* @__PURE__ */ new Map(), n = async (e, n, r) => {
1127
- let i = t.get(e) ?? /* @__PURE__ */ new Map();
1128
- i.set(n, await Ue(r)), t.set(e, i);
1129
- };
1130
- for (let [t, r] of Object.entries(We)) {
1131
- let i = e[{
1132
- connection: "connections",
1133
- resourceServer: "resourceServers",
1134
- hook: "hooks",
1135
- action: "actions"
1136
- }[t]];
1137
- for (let e of i) await n(t, r(e), e);
1138
- }
1139
- for (let t of [
1140
- "emailProvider",
1141
- "branding",
1142
- "promptSettings"
1143
- ]) e[t] && await n(t, "singleton", e[t]);
1144
- return t;
1386
+ async function At(e, t) {
1387
+ await e.tenants.update(t, {
1388
+ parent_tenant_id: null,
1389
+ config_sync: "detached",
1390
+ config_sync_hash: null,
1391
+ config_synced_at: null
1392
+ });
1145
1393
  }
1146
- async function qe(e, t) {
1147
- let [n, r] = await Promise.all([Ke(e), Ke(t)]), i = [], a = new Set([...n.keys(), ...r.keys()]);
1148
- for (let e of a) {
1149
- let t = n.get(e) ?? /* @__PURE__ */ new Map(), a = r.get(e) ?? /* @__PURE__ */ new Map();
1150
- for (let n of new Set([...t.keys(), ...a.keys()])) {
1151
- let r = t.get(n), o = a.get(n);
1152
- if (r && !o) i.push({
1153
- entity: e,
1154
- key: n,
1155
- kind: "missing",
1156
- fields: [],
1157
- expected: !1
1394
+ async function jt(e, t, n) {
1395
+ let r = Ot(n.dryRun ? "dry-run" : "detached"), { dryRun: i, clean: o } = n, s = await V(e, t), c = {
1396
+ connection: s.connections.filter((e) => x(e.id)),
1397
+ resourceServer: s.resourceServers.filter((e) => x(e.id)),
1398
+ hookCode: s.hookCode.filter((e) => x(e.id)),
1399
+ action: s.actions.filter((e) => x(e.id)),
1400
+ hook: s.hooks.filter((e) => x(e.hook_id))
1401
+ }, u = (e) => e.scope === "platform", d = /* @__PURE__ */ new Set();
1402
+ for (let e of c.hook) {
1403
+ let t = e.code_id;
1404
+ u(e) && t && d.add(t);
1405
+ }
1406
+ for (let e of c.action) u(e) && d.add(e.id);
1407
+ let f = (e, t, n, i = "") => {
1408
+ r.changes.push(`${t} ${e}:${n}${i}`), r.rows.push({
1409
+ entity: e,
1410
+ key: n,
1411
+ action: t === "convert" ? "converted" : t === "remove" ? "removed" : "kept"
1412
+ });
1413
+ }, p = async (e, t, n) => {
1414
+ try {
1415
+ await n();
1416
+ } catch (n) {
1417
+ r.entities[e].errors.push(`${t}: ${n instanceof Error ? n.message : String(n)}`);
1418
+ }
1419
+ }, m = (e, t) => {
1420
+ r.entities[e].skipped += 1, f(e, "skip", t, " (platform: stays a copy)");
1421
+ }, h = [];
1422
+ if (o) {
1423
+ let n = async (n) => {
1424
+ for (let r of await e.clientConnections.listByConnection(t, n)) await e.clientConnections.removeClientFromConnection(t, n, r);
1425
+ }, a = [
1426
+ ...c.hook.map((n) => [
1427
+ "hook",
1428
+ n.hook_id,
1429
+ () => e.hooks.remove(t, n.hook_id)
1430
+ ]),
1431
+ ...c.action.map((n) => [
1432
+ "action",
1433
+ n.name,
1434
+ () => e.actions.remove(t, n.id)
1435
+ ]),
1436
+ ...c.hookCode.map((n) => [
1437
+ "hookCode",
1438
+ n.id,
1439
+ () => e.hookCode.remove(t, n.id)
1440
+ ]),
1441
+ ...c.connection.map((r) => [
1442
+ "connection",
1443
+ `${r.strategy}:${r.name}`,
1444
+ async () => {
1445
+ await n(r.id), await e.connections.remove(t, r.id);
1446
+ }
1447
+ ]),
1448
+ ...c.resourceServer.map((n) => [
1449
+ "resourceServer",
1450
+ n.identifier,
1451
+ () => e.resourceServers.remove(t, n.id)
1452
+ ])
1453
+ ];
1454
+ for (let [e, t, n] of a) f(e, "remove", t), await p(e, t, async () => {
1455
+ i || await n(), r.entities[e].removed += 1;
1456
+ });
1457
+ } else {
1458
+ let n = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Set();
1459
+ for (let a of c.hookCode) {
1460
+ if (d.has(a.id)) {
1461
+ m("hookCode", a.id);
1462
+ continue;
1463
+ }
1464
+ f("hookCode", "convert", a.id), o.add(a.id), await p("hookCode", a.id, async () => {
1465
+ if (!i) {
1466
+ let r = await e.hookCode.create(t, _.parse(q(a)));
1467
+ n.set(a.id, r.id), h.push(async () => {
1468
+ await e.hookCode.remove(t, a.id);
1469
+ });
1470
+ }
1471
+ r.entities.hookCode.converted += 1;
1158
1472
  });
1159
- else if (!r && o) i.push({
1160
- entity: e,
1161
- key: n,
1162
- kind: "extra",
1163
- fields: [],
1164
- expected: Ge.has(e)
1473
+ }
1474
+ for (let s of c.action) {
1475
+ if (u(s)) {
1476
+ m("action", s.name);
1477
+ continue;
1478
+ }
1479
+ f("action", "convert", s.name), o.add(s.id), await p("action", s.name, async () => {
1480
+ if (!i) {
1481
+ let r = await e.actions.create(t, a.parse(q(s)));
1482
+ n.set(s.id, r.id), h.push(async () => {
1483
+ await e.actions.remove(t, s.id);
1484
+ });
1485
+ }
1486
+ r.entities.action.converted += 1;
1165
1487
  });
1166
- else if (r && o) {
1167
- let t = JSON.stringify(r), a = JSON.stringify(o);
1168
- if (t === a) continue;
1169
- let s = [...new Set([...Object.keys(r), ...Object.keys(o)])].filter((e) => JSON.stringify(r[e]) !== JSON.stringify(o[e])).sort();
1170
- i.push({
1171
- entity: e,
1172
- key: n,
1173
- kind: "changed",
1174
- fields: s,
1175
- expected: !1,
1176
- legacy_hash: await G(t),
1177
- new_hash: await G(a)
1178
- });
1488
+ }
1489
+ let s = i ? [] : await g((n) => e.organizations.list(t, n), "organizations", {
1490
+ cursorField: "id",
1491
+ pageSize: 100
1492
+ });
1493
+ for (let n of c.connection) {
1494
+ let a = `${n.strategy}:${n.name}`;
1495
+ if (u(n)) {
1496
+ m("connection", a);
1497
+ continue;
1498
+ }
1499
+ f("connection", "convert", a), await p("connection", a, async () => {
1500
+ if (!i) {
1501
+ let { enabled_clients: r, ...i } = q(n), a = await e.connections.create(t, l.parse(i));
1502
+ for (let r of await e.clientConnections.listByConnection(t, n.id)) await e.clientConnections.addClientToConnection(t, a.id, r), await e.clientConnections.removeClientFromConnection(t, n.id, r);
1503
+ for (let r of s) {
1504
+ let i = (await e.organizationConnections.list(t, r.id)).find((e) => e.connection_id === n.id);
1505
+ if (!i) continue;
1506
+ let { connection_id: o, organization_id: s, ...c } = i;
1507
+ await e.organizationConnections.create(t, r.id, {
1508
+ ...c,
1509
+ connection_id: a.id
1510
+ }), await e.organizationConnections.remove(t, r.id, n.id);
1511
+ }
1512
+ h.push(async () => {
1513
+ await e.connections.remove(t, n.id);
1514
+ });
1515
+ }
1516
+ r.entities.connection.converted += 1;
1517
+ });
1518
+ }
1519
+ for (let n of c.resourceServer) {
1520
+ if (u(n)) {
1521
+ m("resourceServer", n.identifier);
1522
+ continue;
1523
+ }
1524
+ f("resourceServer", "convert", n.identifier), await p("resourceServer", n.identifier, async () => {
1525
+ if (!i) {
1526
+ await e.resourceServers.remove(t, n.id);
1527
+ try {
1528
+ await e.resourceServers.create(t, T.parse(q(n)));
1529
+ } catch (r) {
1530
+ throw await e.resourceServers.create(t, T.parse(n)), r;
1531
+ }
1532
+ }
1533
+ r.entities.resourceServer.converted += 1;
1534
+ });
1535
+ }
1536
+ let y = [];
1537
+ for (let a of c.hook) {
1538
+ let s = a.hook_id, c = a.code_id;
1539
+ if (u(a)) {
1540
+ m("hook", s);
1541
+ continue;
1179
1542
  }
1543
+ if (c && !(i ? o.has(c) : n.has(c))) {
1544
+ r.entities.hook.skipped += 1, f("hook", "skip", s, " (its code is not converted)");
1545
+ continue;
1546
+ }
1547
+ f("hook", "convert", s), await p("hook", s, async () => {
1548
+ i || (await e.hooks.create(t, v.parse({
1549
+ ...q(a),
1550
+ ...c ? { code_id: n.get(c) } : {}
1551
+ })), y.push(async () => {
1552
+ await e.hooks.remove(t, s);
1553
+ })), r.entities.hook.converted += 1;
1554
+ });
1180
1555
  }
1556
+ h.unshift(...y);
1181
1557
  }
1182
- return i;
1558
+ if (Object.values(r.entities).some((e) => e.errors.length > 0)) return {
1559
+ ...r,
1560
+ status: "partial"
1561
+ };
1562
+ if (i) return r;
1563
+ for (let e of h) try {
1564
+ await e();
1565
+ } catch (e) {
1566
+ r.entities.hook.errors.push(`remove: ${e instanceof Error ? e.message : String(e)}`);
1567
+ }
1568
+ return Object.values(r.entities).some((e) => e.errors.length > 0) ? {
1569
+ ...r,
1570
+ status: "partial"
1571
+ } : r;
1183
1572
  }
1184
- async function Je(e) {
1185
- let { adapters: t, tenantId: n, legacyControlPlane: r } = e, i = await De({
1186
- tenantId: n,
1187
- getTenant: (e) => t.tenants.get(e),
1188
- legacyControlPlane: r,
1189
- maxDepth: e.maxDepth
1190
- }), a = await W(t, n), o = await Promise.all(i.map((e) => W(t, e))), s = r && r !== n ? await W(t, r) : void 0, { hidden: c, ...l } = Le(o, a), u = new Set(c);
1191
- return (await qe(Ae(a, s), l)).filter((e) => !u.has(`${e.entity}:${e.key}`));
1573
+ function Mt(e) {
1574
+ return async (t, n) => {
1575
+ let r = await e.tenants.get(t);
1576
+ if (!r) throw Error(`Tenant "${t}" not found`);
1577
+ if (r.deployment_type === "wfp") throw Error(`Tenant "${t}" has its own database: configure a dispatch detach for it`);
1578
+ if (r.config_sync === "detached") return Ot("already-detached");
1579
+ kt(t, r);
1580
+ let i = await jt(e, t, n);
1581
+ return i.status === "detached" && await At(e, t), i;
1582
+ };
1192
1583
  }
1193
1584
  //#endregion
1194
- //#region src/inheritance/report.ts
1195
- var Ye = 3600 * 1e3, Xe = 1e4;
1196
- function Ze(e, t) {
1197
- let n = t.sample ?? 1, r = t.intervalMs ?? 6e4, i = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Set(), o = /* @__PURE__ */ new Map();
1198
- async function s(n, r) {
1199
- let i = await Je({
1200
- adapters: e,
1201
- tenantId: n,
1202
- legacyControlPlane: r
1203
- }), a = Date.now();
1204
- o.size > Xe && o.clear();
1205
- for (let s of i) {
1206
- if (s.expected && !t.includeExpected) continue;
1207
- let i = await G([
1208
- n,
1209
- s.entity,
1210
- s.key,
1211
- s.kind,
1212
- s.fields.join(",")
1213
- ].join("|")), c = o.get(i);
1214
- c !== void 0 && a - c < Ye || (o.set(i, a), await Qe(e, n, s, r));
1215
- }
1216
- }
1217
- return { schedule(e, t) {
1218
- if (a.has(e) || n < 1 && Math.random() >= n) return;
1219
- let o = Date.now(), c = i.get(e);
1220
- c !== void 0 && o - c < r || (i.set(e, o), a.add(e), s(e, t).catch(() => {}).finally(() => a.delete(e)));
1221
- } };
1585
+ //#region src/operations/record-provision.ts
1586
+ function Nt(e) {
1587
+ if (e.tenantOperations && e.tenantOperationEvents) return {
1588
+ tenantOperations: e.tenantOperations,
1589
+ tenantOperationEvents: e.tenantOperationEvents
1590
+ };
1222
1591
  }
1223
- async function Qe(e, n, r, i) {
1224
- await e.logs.create(n, {
1225
- type: t.CONFIG_INHERITANCE_DIFF,
1226
- date: (/* @__PURE__ */ new Date()).toISOString(),
1227
- isMobile: !1,
1228
- description: `${r.kind} ${r.entity} ${r.key}`,
1229
- details: {
1230
- shadow: "config-inheritance",
1231
- legacy_control_plane: i,
1232
- ...r
1592
+ async function Pt(e, t, n) {
1593
+ let r = Nt(e);
1594
+ if (!r) {
1595
+ await n(void 0);
1596
+ return;
1597
+ }
1598
+ let i = I(r), a = async (e) => {
1599
+ try {
1600
+ await e();
1601
+ } catch (e) {
1602
+ console.warn("Failed to record tenant operation progress:", e);
1233
1603
  }
1604
+ }, o;
1605
+ if (await a(async () => {
1606
+ let e = await se(r, t, "inline");
1607
+ o = e.id, await i.markRunning(e.id);
1608
+ }), !o) {
1609
+ await n(void 0);
1610
+ return;
1611
+ }
1612
+ let s = o, c = !1, l = async (e, t, n) => {
1613
+ c = !0, await a(async () => {
1614
+ t === "started" && await i.setCurrentStep(s, e), await i.appendEvent(s, {
1615
+ step: e,
1616
+ outcome: t,
1617
+ detail: n
1618
+ });
1619
+ });
1620
+ };
1621
+ try {
1622
+ await n(l);
1623
+ } catch (e) {
1624
+ throw await a(async () => {
1625
+ c || await i.appendEvent(s, {
1626
+ step: t.kind,
1627
+ outcome: "failed",
1628
+ detail: { message: F(e) }
1629
+ }), await i.markFailed(s, e);
1630
+ }), e;
1631
+ }
1632
+ await a(async () => {
1633
+ c || await i.appendEvent(s, {
1634
+ step: t.kind,
1635
+ outcome: "succeeded"
1636
+ }), await i.markSucceeded(s);
1234
1637
  });
1235
1638
  }
1236
1639
  //#endregion
1237
- //#region src/middleware/settings-inheritance.ts
1238
- function q(e, t) {
1239
- let n = t.find((t) => t.strategy === e.strategy);
1240
- if (!n?.options) return e;
1241
- let r = c.passthrough().parse({
1242
- ...n,
1243
- ...e
1244
- });
1245
- return r.options = s.passthrough().parse({
1246
- ...n.options || {},
1247
- ...e.options
1248
- }), r;
1249
- }
1250
- function J(e, t) {
1251
- let n = [...t || [], ...e || []];
1252
- return [...new Set(n)];
1253
- }
1254
- function $e(e, t) {
1255
- if (!t?.length) return e || [];
1256
- if (!e?.length) return t;
1257
- let n = /* @__PURE__ */ new Map();
1258
- for (let e of t) n.set(e.value, e);
1259
- for (let t of e) n.set(t.value, t);
1260
- return Array.from(n.values());
1261
- }
1262
- function Y(e, t) {
1263
- return t ? {
1264
- ...e,
1265
- scopes: $e(e.scopes, t.scopes)
1266
- } : e;
1267
- }
1268
- function et(e, t) {
1269
- return t ? {
1270
- ...e,
1271
- callbacks: J(e.callbacks, t.callbacks),
1272
- web_origins: J(e.web_origins, t.web_origins),
1273
- allowed_logout_urls: J(e.allowed_logout_urls, t.allowed_logout_urls),
1274
- allowed_origins: J(e.allowed_origins, t.allowed_origins)
1275
- } : e;
1276
- }
1277
- function tt(e) {
1278
- let { controlPlaneTenantId: t, controlPlaneClientId: n, resolveControlPlane: r } = e;
1279
- if (r) return async (e) => r({ tenant_id: e });
1280
- if (!t) return async () => void 0;
1281
- let i = {
1282
- tenantId: t,
1283
- clientId: n
1640
+ //#region src/operations/tenant-members-request.ts
1641
+ function Ft(e) {
1642
+ let t = () => {
1643
+ if (!e.tenantMemberRequests) throw Error("The data adapter has no tenantMemberRequests adapter");
1644
+ return e.tenantMemberRequests;
1284
1645
  };
1285
- return async () => i;
1286
- }
1287
- function nt(e, t) {
1288
1646
  return {
1289
- ...e.resourceServers,
1290
- get: async (n, r) => {
1291
- let i = await e.resourceServers.get(n, r);
1292
- if (!i) return i;
1293
- let a = await t(n);
1294
- return !a || n === a.tenantId || !i.is_system ? i : Y(i, await e.resourceServers.get(a.tenantId, r));
1647
+ get: (e, n) => t().get(e, n),
1648
+ async report(e, n, r) {
1649
+ return t().update(e, n, {
1650
+ status: r.status,
1651
+ result: r.result ?? null,
1652
+ error: r.error ?? null,
1653
+ finished_at: (/* @__PURE__ */ new Date()).toISOString()
1654
+ }, "pending");
1295
1655
  },
1296
- list: async (n, r) => {
1297
- let i = await e.resourceServers.list(n, r), a = await t(n);
1298
- if (!a || n === a.tenantId) return i;
1299
- let o = a.tenantId, s = i.resource_servers.filter((e) => !!(e.is_system && e.id)).map((e) => e.id);
1300
- if (s.length === 0) return i;
1301
- let c = /* @__PURE__ */ new Map();
1302
- await Promise.all(s.map(async (t) => {
1303
- let n = await e.resourceServers.get(o, t);
1304
- n && c.set(t, n);
1305
- }));
1306
- let l = i.resource_servers.map((e) => e.is_system && e.id ? Y(e, c.get(e.id) ?? null) : e);
1307
- return {
1308
- ...i,
1309
- resource_servers: l
1310
- };
1656
+ async listPending(e, { olderThan: n }) {
1657
+ return (await t().list(e, {
1658
+ status: "pending",
1659
+ created_before: n,
1660
+ per_page: 100
1661
+ })).requests;
1311
1662
  }
1312
1663
  };
1313
1664
  }
1314
- function rt(e, t) {
1315
- let n = tt({
1316
- controlPlaneTenantId: t.controlPlaneTenantId,
1317
- resolveControlPlane: t.resolveControlPlane
1665
+ function It(e) {
1666
+ let t = e.input?.request_id;
1667
+ return typeof t == "string" && t ? t : void 0;
1668
+ }
1669
+ var Lt = 300;
1670
+ async function Rt(e) {
1671
+ let { operation: t, store: n, getBackend: r, recorder: a } = e, o = t.tenant_id, s = It(t);
1672
+ if (!o || !s) throw Error("tenant-members-request operations require a tenant_id and a request_id");
1673
+ let c = await n.get(o, s);
1674
+ if (!c) throw Error(`Request ${s} was not found for tenant ${o}`);
1675
+ if (c.status !== "pending") {
1676
+ let e = {
1677
+ request_id: s,
1678
+ outcome: "already_finished",
1679
+ status: c.status
1680
+ };
1681
+ return await a.setResult(t.id, e), e;
1682
+ }
1683
+ let l;
1684
+ try {
1685
+ l = {
1686
+ status: "succeeded",
1687
+ result: await h(await r(), o, c, e.executeOptions)
1688
+ };
1689
+ } catch (e) {
1690
+ if (!(e instanceof i)) throw e;
1691
+ l = {
1692
+ status: "failed",
1693
+ error: F(e).slice(0, Lt)
1694
+ };
1695
+ }
1696
+ let u = await n.report(o, s, l), d = {
1697
+ request_id: s,
1698
+ outcome: l.status,
1699
+ reported: u,
1700
+ ...l.status === "failed" ? { reason: l.error } : {}
1701
+ };
1702
+ return await a.setResult(t.id, d), d;
1703
+ }
1704
+ var zt = 5;
1705
+ async function Bt(e, t, n, r = {}) {
1706
+ let { operations: i } = await e.stores.tenantOperations.list({
1707
+ tenant_id: t,
1708
+ kind: "tenant-members-request",
1709
+ per_page: 100
1318
1710
  });
1711
+ return i.find((e) => It(e) === n && (e.status === "pending" || e.status === "running" || e.status === "succeeded")) || ce(e.stores, e.executor, {
1712
+ kind: "tenant-members-request",
1713
+ tenant_id: t,
1714
+ initiated_by: r.initiatedBy,
1715
+ input: { request_id: n }
1716
+ }, { returnFailed: !0 });
1717
+ }
1718
+ async function Vt(e) {
1719
+ let t = e.now?.() ?? /* @__PURE__ */ new Date(), n = new Date(t.getTime() - (e.graceMs ?? 5 * 6e4)).toISOString(), r = e.maxAttempts ?? 5, i = {
1720
+ checked: 0,
1721
+ enqueued: 0,
1722
+ gaveUp: 0,
1723
+ unreachable: []
1724
+ };
1725
+ for (let t of await e.tenantIds()) {
1726
+ let a;
1727
+ try {
1728
+ a = await e.store.listPending(t, { olderThan: n });
1729
+ } catch (e) {
1730
+ console.warn(`[tenant-members] could not read the requests of ${t}:`, e), i.unreachable.push(t);
1731
+ continue;
1732
+ }
1733
+ if (a.length === 0) continue;
1734
+ let { operations: o } = await e.stores.tenantOperations.list({
1735
+ tenant_id: t,
1736
+ kind: "tenant-members-request",
1737
+ per_page: 100
1738
+ });
1739
+ for (let n of a) {
1740
+ i.checked += 1;
1741
+ let a = o.filter((e) => It(e) === n.id);
1742
+ if (!a.some((e) => e.status === "pending" || e.status === "running")) {
1743
+ if (a.filter((e) => e.status === "failed").length >= r) {
1744
+ try {
1745
+ await e.store.report(t, n.id, {
1746
+ status: "failed",
1747
+ error: "The control plane could not carry out this request"
1748
+ }) && (i.gaveUp += 1);
1749
+ } catch (e) {
1750
+ console.warn(`[tenant-members] could not report request ${n.id} as failed:`, e);
1751
+ }
1752
+ continue;
1753
+ }
1754
+ try {
1755
+ await Bt(e, t, n.id), i.enqueued += 1;
1756
+ } catch (e) {
1757
+ console.warn(`[tenant-members] could not enqueue request ${n.id}:`, e);
1758
+ }
1759
+ }
1760
+ }
1761
+ }
1762
+ return i;
1763
+ }
1764
+ //#endregion
1765
+ //#region src/hooks/provisioning.ts
1766
+ function Ht(e) {
1767
+ return `urn:authhero:tenant:${e.toLowerCase()}`;
1768
+ }
1769
+ function Ut(e) {
1319
1770
  return {
1320
- ...e,
1321
- resourceServers: nt(e, n)
1322
- };
1323
- }
1324
- function it(e, t) {
1325
- let { controlPlaneTenantId: n, controlPlaneClientId: r, resolveControlPlane: i, inheritanceShadow: a } = t, o = tt({
1326
- controlPlaneTenantId: n,
1327
- controlPlaneClientId: r,
1328
- resolveControlPlane: i
1329
- }), s = a ? Ze(e, a === !0 ? {} : a) : void 0, c = s ? async (e) => {
1330
- let t = await o(e);
1331
- return s.schedule(e, t?.tenantId), t;
1332
- } : o;
1333
- return {
1334
- ...e,
1335
- multiTenancyConfig: {
1336
- controlPlaneTenantId: n,
1337
- controlPlaneClientId: r,
1338
- resolveControlPlane: i
1771
+ async beforeCreate(e, t) {
1772
+ return !t.audience && t.id ? {
1773
+ ...t,
1774
+ audience: Ht(t.id)
1775
+ } : t;
1339
1776
  },
1340
- connections: {
1341
- ...e.connections,
1342
- get: async (t, n) => {
1343
- let r = await e.connections.get(t, n);
1344
- if (!r) return r;
1345
- let i = await c(t);
1346
- return !i || t === i.tenantId ? r : q(r, (await e.connections.list(i.tenantId)).connections || []);
1347
- },
1348
- list: async (t, n) => {
1349
- let r = await e.connections.list(t, n), i = await c(t);
1350
- if (!i || t === i.tenantId) return r;
1351
- let a = await e.connections.list(i.tenantId), o = r.connections.map((e) => q(e, a.connections || []));
1352
- return {
1353
- ...r,
1354
- connections: o
1355
- };
1777
+ async afterCreate(t, i) {
1778
+ let { accessControl: a, databaseIsolation: o } = e;
1779
+ if (a && t.ctx && await Wt(t, i, a), o?.onProvision) {
1780
+ let e = o.onProvision;
1781
+ o.recordProvisionOperations === !1 ? await e(i.id) : await Pt(t.adapters, {
1782
+ kind: "provision",
1783
+ tenant_id: i.id,
1784
+ initiated_by: t.ctx?.var.user?.sub
1785
+ }, (t) => e(i.id, t));
1786
+ } else try {
1787
+ await w(t.adapters, i.id, {
1788
+ managementApiIdentifier: n,
1789
+ managementApiScopes: r
1790
+ });
1791
+ } catch (e) {
1792
+ throw console.warn(`Failed to provision default clients for tenant ${i.id}:`, e), e;
1356
1793
  }
1357
1794
  },
1358
- clientConnections: {
1359
- ...e.clientConnections,
1360
- listByClient: async (t, n) => {
1361
- let r = await e.clientConnections.listByClient(t, n);
1362
- r.length === 0 && (r = (await e.connections.list(t)).connections || []);
1363
- let i = await c(t);
1364
- if (!i || t === i.tenantId) return r;
1365
- let a = await e.connections.list(i.tenantId);
1366
- return r.map((e) => q(e, a.connections || []));
1795
+ async beforeDelete(t, n) {
1796
+ let { accessControl: r, databaseIsolation: i } = e;
1797
+ if (r) try {
1798
+ let e = (await t.adapters.organizations.list(r.controlPlaneTenantId)).organizations.find((e) => e.name === n);
1799
+ e && await t.adapters.organizations.remove(r.controlPlaneTenantId, e.id);
1800
+ } catch (e) {
1801
+ console.warn(`Failed to remove organization for tenant ${n}:`, e);
1802
+ }
1803
+ if (i?.onDeprovision) try {
1804
+ await i.onDeprovision(n);
1805
+ } catch (e) {
1806
+ console.warn(`Failed to deprovision database for tenant ${n}:`, e);
1807
+ }
1808
+ }
1809
+ };
1810
+ }
1811
+ async function Wt(e, t, n) {
1812
+ let { controlPlaneTenantId: r, defaultPermissions: i, defaultRoles: a, issuer: o, adminRoleName: s = "Tenant Admin", adminRoleDescription: c = "Full access to all tenant management operations", addCreatorToOrganization: l = !0 } = n, u = await e.adapters.organizations.create(r, {
1813
+ name: t.id,
1814
+ display_name: t.friendly_name || t.id
1815
+ }), d;
1816
+ if (o && (d = await Kt(e, r, s, c)), l && e.ctx) {
1817
+ let t = e.ctx.var.user;
1818
+ if (t?.sub && !await Gt(e, r, t.sub)) try {
1819
+ await e.adapters.userOrganizations.create(r, {
1820
+ user_id: t.sub,
1821
+ organization_id: u.id
1822
+ }), d && await e.adapters.userRoles.create(r, t.sub, d, u.id);
1823
+ } catch (e) {
1824
+ console.warn(`Failed to add creator ${t.sub} to organization ${u.id}:`, e);
1825
+ }
1826
+ }
1827
+ a && a.length > 0 && console.log(`Would assign roles ${a.join(", ")} to organization ${u.id}`), i && i.length > 0 && console.log(`Would grant permissions ${i.join(", ")} to organization ${u.id}`);
1828
+ }
1829
+ async function Gt(e, t, r) {
1830
+ let i = await e.adapters.userRoles.list(t, r, void 0, "");
1831
+ for (let r of i) if ((await e.adapters.rolePermissions.list(t, r.id, { per_page: 1e3 })).some((e) => e.permission_name === "admin:organizations" && e.resource_server_identifier === n)) return !0;
1832
+ return !1;
1833
+ }
1834
+ async function Kt(e, t, i, a) {
1835
+ let o = (await e.adapters.roles.list(t, {})).roles.find((e) => e.name === i);
1836
+ if (o) return o.id;
1837
+ let s = await e.adapters.roles.create(t, {
1838
+ name: i,
1839
+ description: a
1840
+ }), c = n, l = r.map((e) => ({
1841
+ role_id: s.id,
1842
+ resource_server_identifier: c,
1843
+ permission_name: e.value
1844
+ }));
1845
+ return await e.adapters.rolePermissions.assign(t, s.id, l), s.id;
1846
+ }
1847
+ //#endregion
1848
+ //#region src/hooks/sync.ts
1849
+ function qt(e, t, n = () => !0) {
1850
+ let { controlPlaneTenantId: r, getChildTenantIds: i, getAdapters: a } = e, o = /* @__PURE__ */ new Map();
1851
+ async function s(e, n, r) {
1852
+ return (await t(e).list(n, {
1853
+ q: `name:${m(r)}`,
1854
+ per_page: 1
1855
+ }))[0] ?? null;
1856
+ }
1857
+ async function c(e) {
1858
+ let n = await i(), o = t(await a(r));
1859
+ await Promise.all(n.map(async (n) => {
1860
+ try {
1861
+ let r = await a(n), i = t(r), c = {
1862
+ ...o.transform(e),
1863
+ is_system: !0
1864
+ }, l = await s(r, n, e.name), u = l ? i.getId(l) : void 0;
1865
+ if (l && u) {
1866
+ let e = i.preserveOnUpdate ? i.preserveOnUpdate(l, c) : c;
1867
+ await i.update(n, u, e);
1868
+ } else await i.create(n, c);
1869
+ } catch (t) {
1870
+ console.error(`Failed to sync ${o.listKey} "${e.name}" to tenant "${n}":`, t);
1367
1871
  }
1872
+ }));
1873
+ }
1874
+ async function l(e) {
1875
+ let n = await i();
1876
+ await Promise.all(n.map(async (n) => {
1877
+ try {
1878
+ let r = await a(n), i = t(r), o = await s(r, n, e), c = o ? i.getId(o) : void 0;
1879
+ o && c && await i.remove(n, c);
1880
+ } catch (t) {
1881
+ console.error(`Failed to delete entity "${e}" from tenant "${n}":`, t);
1882
+ }
1883
+ }));
1884
+ }
1885
+ return {
1886
+ afterCreate: async (e, t) => {
1887
+ e.tenantId === r && n(t) && await c(t);
1368
1888
  },
1369
- emailProviders: {
1370
- ...e.emailProviders,
1371
- get: async (t) => {
1372
- let n = await e.emailProviders.get(t);
1373
- if (n) return n;
1374
- let r = await c(t);
1375
- return !r || t === r.tenantId ? null : e.emailProviders.get(r.tenantId);
1889
+ afterUpdate: async (e, t, i) => {
1890
+ e.tenantId === r && n(i) && await c(i);
1891
+ },
1892
+ beforeDelete: async (e, i) => {
1893
+ if (e.tenantId !== r) return;
1894
+ let a = await t(e.adapters).get(e.tenantId, i);
1895
+ a && n(a) && o.set(i, a);
1896
+ },
1897
+ afterDelete: async (e, t) => {
1898
+ if (e.tenantId !== r) return;
1899
+ let n = o.get(t);
1900
+ n && (o.delete(t), await l(n.name));
1901
+ }
1902
+ };
1903
+ }
1904
+ function Jt(e, t, n = () => !0) {
1905
+ let { controlPlaneTenantId: r, getControlPlaneAdapters: i, getAdapters: a } = e;
1906
+ return { async afterCreate(e, o) {
1907
+ if (o.id !== r) try {
1908
+ let e = await i(), s = await a(o.id), c = t(e), l = t(s), u = await g((e) => c.listPaginated(r, e), c.listKey, {
1909
+ cursorField: "id",
1910
+ pageSize: 100
1911
+ });
1912
+ await Promise.all(u.filter((e) => n(e)).map(async (e) => {
1913
+ try {
1914
+ let t = c.transform(e);
1915
+ await l.create(o.id, {
1916
+ ...t,
1917
+ is_system: !0
1918
+ });
1919
+ } catch (e) {
1920
+ console.error(`Failed to sync entity to new tenant "${o.id}":`, e);
1921
+ }
1922
+ }));
1923
+ } catch (e) {
1924
+ console.error(`Failed to sync entities to new tenant "${o.id}":`, e);
1925
+ }
1926
+ } };
1927
+ }
1928
+ var Yt = (e) => ({
1929
+ list: async (t, n) => (await e.resourceServers.list(t, n)).resource_servers,
1930
+ listPaginated: (t, n) => e.resourceServers.list(t, n),
1931
+ get: (t, n) => e.resourceServers.get(t, n),
1932
+ create: (t, n) => e.resourceServers.create(t, n),
1933
+ update: (t, n, r) => e.resourceServers.update(t, n, r),
1934
+ remove: (t, n) => e.resourceServers.remove(t, n),
1935
+ listKey: "resource_servers",
1936
+ getId: (e) => e.id,
1937
+ transform: (e) => ({
1938
+ id: e.id,
1939
+ name: e.name,
1940
+ identifier: e.identifier,
1941
+ scopes: e.scopes,
1942
+ signing_alg: e.signing_alg,
1943
+ token_lifetime: e.token_lifetime,
1944
+ token_lifetime_for_web: e.token_lifetime_for_web
1945
+ })
1946
+ }), Xt = (e) => ({
1947
+ list: async (t, n) => (await e.roles.list(t, n)).roles,
1948
+ listPaginated: (t, n) => e.roles.list(t, n),
1949
+ get: (t, n) => e.roles.get(t, n),
1950
+ create: (t, n) => e.roles.create(t, n),
1951
+ update: (t, n, r) => e.roles.update(t, n, r),
1952
+ remove: (t, n) => e.roles.remove(t, n),
1953
+ listKey: "roles",
1954
+ getId: (e) => e.id,
1955
+ transform: (e) => ({
1956
+ id: e.id,
1957
+ name: e.name,
1958
+ description: e.description
1959
+ })
1960
+ });
1961
+ function Zt(e) {
1962
+ return e.metadata?.sync !== !1;
1963
+ }
1964
+ function Qt(e) {
1965
+ let { sync: t = {}, filters: n = {} } = e, r = t.resourceServers ?? !0, i = t.roles ?? !0, a = (e) => Zt(e) ? n.resourceServers ? n.resourceServers(e) : !0 : !1, o = (e) => Zt(e) ? n.roles ? n.roles(e) : !0 : !1, s = r ? qt(e, Yt, a) : void 0, c = i ? qt(e, Xt, o) : void 0, l = r ? Jt(e, Yt, a) : void 0, u = i ? Jt(e, Xt, o) : void 0, d = i ? { async afterCreate(t, r) {
1966
+ if (r.id !== e.controlPlaneTenantId) {
1967
+ await u?.afterCreate?.(t, r);
1968
+ try {
1969
+ let t = await e.getControlPlaneAdapters(), i = await e.getAdapters(r.id), a = await g((n) => t.roles.list(e.controlPlaneTenantId, n), "roles", {
1970
+ cursorField: "id",
1971
+ pageSize: 100
1972
+ }), o = /* @__PURE__ */ new Map();
1973
+ for (let e of a.filter((e) => n.roles?.(e) ?? !0)) {
1974
+ let t = await f(i, r.id, e.name);
1975
+ t && o.set(e.name, t.id);
1976
+ }
1977
+ for (let s of a.filter((e) => n.roles?.(e) ?? !0)) {
1978
+ let n = o.get(s.name);
1979
+ if (n) try {
1980
+ let a = await t.rolePermissions.list(e.controlPlaneTenantId, s.id, {});
1981
+ a.length > 0 && await i.rolePermissions.assign(r.id, n, a.map((e) => ({
1982
+ role_id: n,
1983
+ resource_server_identifier: e.resource_server_identifier,
1984
+ permission_name: e.permission_name
1985
+ })));
1986
+ } catch (e) {
1987
+ console.error(`Failed to sync permissions for role "${s.name}" to tenant "${r.id}":`, e);
1988
+ }
1989
+ }
1990
+ } catch (e) {
1991
+ console.error(`Failed to sync role permissions to tenant "${r.id}":`, e);
1992
+ }
1993
+ }
1994
+ } } : void 0;
1995
+ async function f(e, t, n) {
1996
+ return (await e.roles.list(t, {
1997
+ q: `name:${m(n)}`,
1998
+ per_page: 1
1999
+ })).roles[0] ?? null;
2000
+ }
2001
+ return {
2002
+ entityHooks: {
2003
+ resourceServers: s,
2004
+ roles: c
2005
+ },
2006
+ tenantHooks: { async afterCreate(e, t) {
2007
+ let n = [l?.afterCreate, d?.afterCreate ?? u?.afterCreate], r = [];
2008
+ for (let i of n) if (i) try {
2009
+ await i(e, t);
2010
+ } catch (e) {
2011
+ r.push(e instanceof Error ? e : Error(String(e)));
1376
2012
  }
2013
+ if (r.length === 1) throw r[0];
2014
+ if (r.length > 1) throw AggregateError(r, r.map((e) => e.message).join("; "));
2015
+ } }
2016
+ };
2017
+ }
2018
+ //#endregion
2019
+ //#region src/middleware/request-data.ts
2020
+ function J(e) {
2021
+ return e.var.data ?? e.env.data;
2022
+ }
2023
+ //#endregion
2024
+ //#region src/utils/tenant-settings.ts
2025
+ async function $t(e, t, n) {
2026
+ let r = !n || n === t, { totals: i } = await e.list({
2027
+ per_page: 1,
2028
+ include_totals: !0,
2029
+ ...r ? {} : { q: `parent_tenant_id:${ee(t)}` }
2030
+ });
2031
+ return {
2032
+ is_control_plane: r,
2033
+ has_children: (i?.length ?? 0) > +!!r
2034
+ };
2035
+ }
2036
+ //#endregion
2037
+ //#region src/routes/tenants.ts
2038
+ var en = j.object({
2039
+ sub: j.string(),
2040
+ tenant_id: j.string().optional(),
2041
+ org_id: j.string().optional(),
2042
+ scope: j.string().optional(),
2043
+ permissions: j.array(j.string()).optional()
2044
+ }).passthrough();
2045
+ function tn(e) {
2046
+ let t = en.safeParse(e);
2047
+ return t.success ? t.data : void 0;
2048
+ }
2049
+ function nn(e, t, n) {
2050
+ return t ? !e || n ? !1 : e.tenant_id === t : !0;
2051
+ }
2052
+ function rn(e) {
2053
+ let t = e.permissions ?? [], n = e.scope ? e.scope.split(" ").filter(Boolean) : [], r = new Set([...t, ...n]);
2054
+ return r.has("delete:tenants") || r.has("admin:organizations");
2055
+ }
2056
+ function an(e, t) {
2057
+ let n = new k(), r = (t) => {
2058
+ let n = tn(t.var.user);
2059
+ return nn(n, e.accessControl?.controlPlaneTenantId ?? J(t).multiTenancyConfig?.controlPlaneTenantId, !!(n?.org_id ?? t.var.organization_id ?? t.var.org_name));
2060
+ }, i = (t, n) => $t(J(t).tenants, n, e.accessControl?.controlPlaneTenantId ?? J(t).multiTenancyConfig?.controlPlaneTenantId);
2061
+ if (n.openapi(A({
2062
+ tags: ["tenants"],
2063
+ method: "get",
2064
+ path: "/",
2065
+ request: { query: o },
2066
+ security: [{ Bearer: [] }],
2067
+ responses: { 200: {
2068
+ content: { "application/json": { schema: j.object({
2069
+ tenants: j.array(D),
2070
+ start: j.number().optional(),
2071
+ limit: j.number().optional(),
2072
+ length: j.number().optional(),
2073
+ total: j.number().optional()
2074
+ }) } },
2075
+ description: "List of tenants"
2076
+ } }
2077
+ }), async (t) => {
2078
+ let { page: n, per_page: r, include_totals: i, q: a } = t.req.valid("query"), o = t.var.user, s = o?.permissions || [];
2079
+ if (!(o?.org_id ?? t.var.organization_id) && s.includes("admin:organizations")) {
2080
+ let e = await J(t).tenants.list({
2081
+ page: n,
2082
+ per_page: r,
2083
+ include_totals: i,
2084
+ q: a
2085
+ });
2086
+ return i ? t.json({
2087
+ tenants: e.tenants,
2088
+ start: e.totals?.start ?? 0,
2089
+ limit: e.totals?.limit ?? r,
2090
+ length: e.tenants.length,
2091
+ total: e.totals?.length
2092
+ }) : t.json({ tenants: e.tenants });
2093
+ }
2094
+ let c = e.accessControl?.controlPlaneTenantId ?? J(t).multiTenancyConfig?.controlPlaneTenantId;
2095
+ if (c && !o?.sub) throw new O(403, { message: "Access denied: token has no subject" });
2096
+ if (c && o?.sub) {
2097
+ let e = (await g((e) => J(t).userOrganizations.listUserOrganizations(c, o.sub, e), "organizations")).map((e) => e.name);
2098
+ if (e.length === 0) return i ? t.json({
2099
+ tenants: [],
2100
+ start: 0,
2101
+ limit: r ?? 50,
2102
+ length: 0,
2103
+ total: 0
2104
+ }) : t.json({ tenants: [] });
2105
+ let s = e.length, l = n ?? 0, u = r ?? 50, d = l * u, f = e.slice(d, d + u);
2106
+ if (f.length === 0) return i ? t.json({
2107
+ tenants: [],
2108
+ start: d,
2109
+ limit: u,
2110
+ length: 0,
2111
+ total: s
2112
+ }) : t.json({ tenants: [] });
2113
+ let p = f.map((e) => `id:${e}`).join(" OR "), m = a ? `(${p}) AND (${a})` : p, h = await J(t).tenants.list({
2114
+ q: m,
2115
+ per_page: u,
2116
+ include_totals: !1
2117
+ });
2118
+ return i ? t.json({
2119
+ tenants: h.tenants,
2120
+ start: d,
2121
+ limit: u,
2122
+ length: h.tenants.length,
2123
+ total: s
2124
+ }) : t.json({ tenants: h.tenants });
2125
+ }
2126
+ let l = await J(t).tenants.list({
2127
+ page: n,
2128
+ per_page: r,
2129
+ include_totals: i,
2130
+ q: a
2131
+ });
2132
+ return i ? t.json({
2133
+ tenants: l.tenants,
2134
+ start: l.totals?.start ?? 0,
2135
+ limit: l.totals?.limit ?? r,
2136
+ length: l.tenants.length,
2137
+ total: l.totals?.length
2138
+ }) : t.json({ tenants: l.tenants });
2139
+ }), n.openapi(A({
2140
+ tags: ["tenants"],
2141
+ method: "post",
2142
+ path: "/",
2143
+ request: { body: { content: { "application/json": { schema: E } } } },
2144
+ security: [{ Bearer: [] }],
2145
+ responses: {
2146
+ 201: {
2147
+ content: { "application/json": { schema: D } },
2148
+ description: "Tenant created"
2149
+ },
2150
+ 400: { description: "Validation error" },
2151
+ 409: { description: "Tenant with this ID already exists" }
2152
+ }
2153
+ }), async (n) => {
2154
+ if (!n.var.user?.sub) throw new O(401, { message: "Authentication required to create tenants" });
2155
+ let { config_sync_hash: i, config_synced_at: a, config_sync: o, ...s } = n.req.valid("json"), c = s;
2156
+ if (c.parent_tenant_id && !r(n)) throw new O(403, { message: "Only the control plane can set a tenant's parent" });
2157
+ o !== void 0 && r(n) && (c = {
2158
+ ...c,
2159
+ config_sync: o
2160
+ });
2161
+ let l = {
2162
+ adapters: J(n),
2163
+ ctx: n
2164
+ };
2165
+ t.tenants?.beforeCreate && (c = await t.tenants.beforeCreate(l, c)), await le({
2166
+ tenantId: c.id,
2167
+ parentTenantId: c.parent_tenant_id,
2168
+ getTenant: (e) => J(n).tenants.get(e)
2169
+ });
2170
+ let u = await J(n).tenants.create(c);
2171
+ return t.tenants?.afterCreate && await t.tenants.afterCreate(l, u), e.onConfigSyncChange?.(u.id), n.json(u, 201);
2172
+ }), n.openapi(A({
2173
+ tags: ["tenants"],
2174
+ method: "delete",
2175
+ path: "/{id}",
2176
+ request: { params: j.object({ id: j.string() }) },
2177
+ security: [{ Bearer: ["delete:tenants"] }],
2178
+ responses: {
2179
+ 204: { description: "Tenant deleted" },
2180
+ 403: { description: "Access denied or cannot delete the control plane" },
2181
+ 404: { description: "Tenant not found" }
2182
+ }
2183
+ }), async (n) => {
2184
+ let { id: r } = n.req.valid("param"), i = e.accessControl?.controlPlaneTenantId ?? J(n).multiTenancyConfig?.controlPlaneTenantId;
2185
+ if (i) {
2186
+ let e = tn(n.var.user);
2187
+ if (!e?.sub) throw new O(401, { message: "Authentication required" });
2188
+ if (r === i) throw new O(403, { message: "Cannot delete the control plane" });
2189
+ let t = n.var.org_name, a = r.toLowerCase(), o = !!t && t.toLowerCase() === a;
2190
+ if (!o) {
2191
+ let r = !!(e.org_id ?? n.var.organization_id ?? t), a = !e.tenant_id || e.tenant_id === i;
2192
+ !r && a && rn(e) && (o = !0);
2193
+ }
2194
+ if (o ||= (await g((t) => J(n).userOrganizations.listUserOrganizations(i, e.sub, t), "organizations")).some((e) => e.name?.toLowerCase() === a), !o) throw new O(403, { message: "Access denied to this tenant" });
2195
+ }
2196
+ if (!await J(n).tenants.get(r)) throw new O(404, { message: "Tenant not found" });
2197
+ let a = {
2198
+ adapters: J(n),
2199
+ ctx: n
2200
+ };
2201
+ return t.tenants?.beforeDelete && await t.tenants.beforeDelete(a, r), await J(n).tenants.remove(r), t.tenants?.afterDelete && await t.tenants.afterDelete(a, r), n.body(null, 204);
2202
+ }), e.verifyConfigSync) {
2203
+ let t = e.verifyConfigSync;
2204
+ n.openapi(A({
2205
+ tags: ["tenants"],
2206
+ method: "get",
2207
+ path: "/{id}/config-sync/verify",
2208
+ request: { params: j.object({ id: j.string() }) },
2209
+ security: [{ Bearer: ["read:tenants"] }],
2210
+ responses: {
2211
+ 200: {
2212
+ content: { "application/json": { schema: j.object({
2213
+ tenant_id: j.string(),
2214
+ config_sync: j.enum([
2215
+ "inherit-on-read",
2216
+ "synced",
2217
+ "detached"
2218
+ ]),
2219
+ method: j.enum([
2220
+ "read-time",
2221
+ "sync-dry-run",
2222
+ "not-applicable"
2223
+ ]),
2224
+ ok: j.boolean(),
2225
+ differences: j.array(j.object({
2226
+ entity: j.string(),
2227
+ key: j.string(),
2228
+ kind: j.enum([
2229
+ "missing",
2230
+ "extra",
2231
+ "changed",
2232
+ "stale"
2233
+ ]),
2234
+ fields: j.array(j.string()).optional()
2235
+ })),
2236
+ errors: j.array(j.string())
2237
+ }) } },
2238
+ description: "The tenant's config compared with what its parents publish"
2239
+ },
2240
+ 403: { description: "Not allowed to verify this tenant" },
2241
+ 404: { description: "Tenant not found" },
2242
+ 502: { description: "The tenant's database could not be reached" }
2243
+ }
2244
+ }), async (e) => {
2245
+ let { id: n } = e.req.valid("param");
2246
+ if (n !== e.var.tenant_id && !r(e)) throw new O(403, { message: "Config can only be verified for your own tenant" });
2247
+ let i;
2248
+ try {
2249
+ i = await t(n);
2250
+ } catch (e) {
2251
+ throw console.error(`[multi-tenancy] config verify failed for "${n}":`, e), new O(502, { message: "Could not verify: the tenant's config could not be checked" });
2252
+ }
2253
+ if (!i) throw new O(404, { message: "Tenant not found" });
2254
+ return e.json(i);
2255
+ });
2256
+ }
2257
+ if (e.configSyncStatus) {
2258
+ let t = e.configSyncStatus;
2259
+ n.openapi(A({
2260
+ tags: ["tenants"],
2261
+ method: "get",
2262
+ path: "/{id}/config-sync",
2263
+ request: { params: j.object({ id: j.string() }) },
2264
+ security: [{ Bearer: ["read:tenants"] }],
2265
+ responses: {
2266
+ 200: {
2267
+ content: { "application/json": { schema: j.object({
2268
+ tenant_id: j.string(),
2269
+ parent_tenant_id: j.string().nullable(),
2270
+ config_sync: j.enum([
2271
+ "inherit-on-read",
2272
+ "synced",
2273
+ "detached"
2274
+ ]),
2275
+ config_sync_hash: j.string().nullable(),
2276
+ config_synced_at: j.string().nullable(),
2277
+ expected_hash: j.string().nullable(),
2278
+ drifted: j.boolean(),
2279
+ chain: j.array(j.string()),
2280
+ inherited: j.record(j.string(), j.string())
2281
+ }) } },
2282
+ description: "Config sync status of the tenant"
2283
+ },
2284
+ 403: { description: "Not allowed to read this tenant" },
2285
+ 404: { description: "Tenant not found" }
2286
+ }
2287
+ }), async (e) => {
2288
+ let { id: n } = e.req.valid("param");
2289
+ if (n !== e.var.tenant_id && !r(e)) throw new O(403, { message: "Config sync status is only readable for your own tenant" });
2290
+ let i = await t(n);
2291
+ if (!i) throw new O(404, { message: "Tenant not found" });
2292
+ return e.json(i);
2293
+ });
2294
+ }
2295
+ return n.openapi(A({
2296
+ tags: ["tenants", "settings"],
2297
+ method: "get",
2298
+ path: "/settings",
2299
+ request: { headers: j.object({ "tenant-id": j.string().optional() }) },
2300
+ security: [{ Bearer: ["read:tenants"] }],
2301
+ responses: { 200: {
2302
+ content: { "application/json": { schema: D } },
2303
+ description: "Current tenant settings"
2304
+ } }
2305
+ }), async (e) => {
2306
+ let t = await J(e).tenants.get(e.var.tenant_id);
2307
+ if (!t) throw new O(404, { message: "Tenant not found" });
2308
+ return e.json({
2309
+ ...t,
2310
+ ...await i(e, t.id)
2311
+ });
2312
+ }), n.openapi(A({
2313
+ tags: ["tenants", "settings"],
2314
+ method: "patch",
2315
+ path: "/settings",
2316
+ request: {
2317
+ headers: j.object({ "tenant-id": j.string().optional() }),
2318
+ body: { content: { "application/json": { schema: j.object(E.shape).partial() } } }
2319
+ },
2320
+ security: [{ Bearer: ["update:tenants"] }],
2321
+ responses: { 200: {
2322
+ content: { "application/json": { schema: D } },
2323
+ description: "Updated tenant settings"
2324
+ } }
2325
+ }), async (t) => {
2326
+ let { id: n, config_sync_hash: a, config_synced_at: o, ...s } = t.req.valid("json"), c = await J(t).tenants.get(t.var.tenant_id);
2327
+ if (!c) throw new O(404, { message: "Tenant not found" });
2328
+ if ("config_sync" in s && (s.config_sync ?? "inherit-on-read") !== (c.config_sync ?? "inherit-on-read") && !r(t)) throw new O(403, { message: "Only the control plane can change a tenant's config_sync mode" });
2329
+ if ("default_client_id" in s) {
2330
+ let e = s.default_client_id;
2331
+ if (typeof e == "string" && e.length > 0) {
2332
+ let n = await J(t).clients.get(t.var.tenant_id, e);
2333
+ if (!n) throw new O(400, { message: `Client with id '${e}' not found` });
2334
+ if (!b(n)) throw new O(400, { message: `Client '${e}' is not an interactive client and cannot be used as the default client` });
2335
+ }
2336
+ }
2337
+ if ("parent_tenant_id" in s) {
2338
+ if ((s.parent_tenant_id ?? null) !== (c.parent_tenant_id ?? null) && !r(t)) throw new O(403, { message: "Only the control plane can change a tenant's parent" });
2339
+ await le({
2340
+ tenantId: t.var.tenant_id,
2341
+ parentTenantId: s.parent_tenant_id,
2342
+ getTenant: (e) => J(t).tenants.get(e),
2343
+ getChildren: async (e) => (await g((e) => J(t).tenants.list(e), "tenants")).filter((t) => t.parent_tenant_id === e).map((e) => e.id)
2344
+ });
2345
+ }
2346
+ let l = f(c, s);
2347
+ await J(t).tenants.update(t.var.tenant_id, l), "config_sync" in s && e.onConfigSyncChange?.(t.var.tenant_id);
2348
+ let u = await J(t).tenants.get(t.var.tenant_id);
2349
+ if (!u) throw new O(500, { message: "Failed to retrieve updated tenant" });
2350
+ return t.json({
2351
+ ...u,
2352
+ ...await i(t, u.id)
2353
+ });
2354
+ }), n;
2355
+ }
2356
+ //#endregion
2357
+ //#region src/middleware/protect-synced.ts
2358
+ function on(e) {
2359
+ for (let { pattern: t, type: n } of [
2360
+ {
2361
+ pattern: /\/api\/v2\/resource-servers\/([^/]+)$/,
2362
+ type: "resource_server"
2363
+ },
2364
+ {
2365
+ pattern: /\/api\/v2\/roles\/([^/]+)$/,
2366
+ type: "role"
1377
2367
  },
1378
- resourceServers: nt(e, c),
1379
- hooks: at(e, c)
1380
- };
2368
+ {
2369
+ pattern: /\/api\/v2\/connections\/([^/]+)$/,
2370
+ type: "connection"
2371
+ }
2372
+ ]) {
2373
+ let r = e.match(t);
2374
+ if (r && r[1]) return {
2375
+ type: n,
2376
+ id: r[1]
2377
+ };
2378
+ }
2379
+ return null;
1381
2380
  }
1382
- function X(e) {
1383
- if (!e || typeof e != "object") return !1;
1384
- let t = e.metadata;
1385
- return !t || typeof t != "object" ? !1 : t.inheritable === !0;
2381
+ async function sn(e, t, n) {
2382
+ try {
2383
+ switch (n.type) {
2384
+ case "resource_server": return (await e.resourceServers.get(t, n.id))?.is_system === !0;
2385
+ case "role": return (await e.roles.get(t, n.id))?.is_system === !0;
2386
+ case "connection": return (await e.connections.get(t, n.id))?.is_system === !0;
2387
+ default: return !1;
2388
+ }
2389
+ } catch {
2390
+ return !1;
2391
+ }
1386
2392
  }
1387
- function at(e, t) {
2393
+ function cn(e) {
1388
2394
  return {
1389
- ...e.hooks,
1390
- list: async (n, r) => {
1391
- let i = await e.hooks.list(n, r), a = await t(n);
1392
- if (!a || n === a.tenantId) return i;
1393
- let o = ((await e.hooks.list(a.tenantId, r)).hooks || []).filter(X);
1394
- if (o.length === 0) return i;
1395
- let s = new Set((i.hooks || []).map((e) => e.hook_id)), c = o.filter((e) => !s.has(e.hook_id));
1396
- return {
1397
- ...i,
1398
- hooks: [...i.hooks || [], ...c],
1399
- length: typeof i.length == "number" ? i.length + c.length : i.length
1400
- };
2395
+ resource_server: "resource server",
2396
+ role: "role",
2397
+ connection: "connection"
2398
+ }[e];
2399
+ }
2400
+ function ln(e) {
2401
+ for (let { pattern: t, type: n } of [
2402
+ {
2403
+ pattern: /\/api\/v2\/connections\/([^/]+)$/,
2404
+ type: "connection"
1401
2405
  },
1402
- get: async (n, r) => {
1403
- let i = await e.hooks.get(n, r);
1404
- if (i) return i;
1405
- let a = await t(n);
1406
- if (!a || n === a.tenantId) return i;
1407
- let o = await e.hooks.get(a.tenantId, r);
1408
- return o && X(o) ? o : null;
2406
+ {
2407
+ pattern: /\/api\/v2\/resource-servers\/([^/]+)$/,
2408
+ type: "resource_server"
2409
+ },
2410
+ {
2411
+ pattern: /\/api\/v2\/actions\/actions\/([^/]+)$/,
2412
+ type: "action"
2413
+ },
2414
+ {
2415
+ pattern: /\/api\/v2\/hooks\/([^/]+)$/,
2416
+ type: "hook"
2417
+ },
2418
+ {
2419
+ pattern: /\/api\/v2\/hook-code\/([^/]+)$/,
2420
+ type: "hook_code"
2421
+ }
2422
+ ]) {
2423
+ let r = e.match(t);
2424
+ if (r?.[1]) return {
2425
+ type: n,
2426
+ id: r[1]
2427
+ };
2428
+ }
2429
+ return null;
2430
+ }
2431
+ async function un(e, t, n) {
2432
+ try {
2433
+ switch (n.type) {
2434
+ case "connection": return await e.connections.get(t, n.id);
2435
+ case "resource_server": return await e.resourceServers.get(t, n.id);
2436
+ case "action": return await e.actions.get(t, n.id);
2437
+ case "hook": return await e.hooks.get(t, n.id);
2438
+ case "hook_code": return await e.hookCode.get(t, n.id);
1409
2439
  }
2440
+ } catch {
2441
+ return null;
2442
+ }
2443
+ }
2444
+ var dn = {
2445
+ connection: ["options"],
2446
+ resource_server: ["signing_secret"],
2447
+ action: ["secrets"],
2448
+ hook: [],
2449
+ hook_code: []
2450
+ }, fn = new Set([
2451
+ "client_secret",
2452
+ "app_secret",
2453
+ "twilio_token"
2454
+ ]);
2455
+ function pn(e, t) {
2456
+ let n = (e) => {
2457
+ let { client_secret: t, ...n } = e ?? {};
2458
+ return n;
1410
2459
  };
2460
+ return mn(n(e), n(t));
2461
+ }
2462
+ var mn = (e, t) => JSON.stringify(e) === JSON.stringify(t);
2463
+ function Y(e, t) {
2464
+ return new O(403, { message: `This ${e.type.replace("_", " ")} is synced from a parent tenant and ${t}. Make changes in the parent tenant instead.` });
2465
+ }
2466
+ async function hn(e, t, n) {
2467
+ let r = await un(J(e), t, n);
2468
+ if (!r) return;
2469
+ if (n.type === "hook_code") throw Y(n, "cannot be modified");
2470
+ if (e.req.method === "DELETE") {
2471
+ if (r.enforce === !0) throw Y(n, "cannot be deleted");
2472
+ return;
2473
+ }
2474
+ let i = r.override ?? "fields";
2475
+ if (i === "none") throw Y(n, "cannot be edited");
2476
+ let a = {};
2477
+ try {
2478
+ let t = await e.req.raw.clone().json();
2479
+ t && typeof t == "object" && (a = t);
2480
+ } catch {}
2481
+ for (let e of [
2482
+ "scope",
2483
+ "override",
2484
+ "enforce"
2485
+ ]) if (e in a && !mn(a[e] ?? null, r[e] ?? null)) throw Y(n, `has a locked ${e}`);
2486
+ if (r.enforce === !0 && a.enabled === !1) throw Y(n, "cannot be disabled");
2487
+ if (i === "secrets") {
2488
+ let e = dn[n.type];
2489
+ for (let [t, i] of Object.entries(a)) if (![
2490
+ "scope",
2491
+ "override",
2492
+ "enforce"
2493
+ ].includes(t) && !mn(i, r[t])) {
2494
+ if (!e.includes(t)) throw Y(n, `only allows its secrets to be changed (not ${t})`);
2495
+ if (t === "options" && i && typeof i == "object") {
2496
+ let e = r.options ?? {};
2497
+ for (let [t, r] of Object.entries(i)) if (!mn(r, e[t]) && !t.endsWith("_hint") && !(fn.has(t) || t === "configuration" && pn(r, e[t]))) throw Y(n, `only allows its secrets to be changed (not options.${t})`);
2498
+ }
2499
+ }
2500
+ }
1411
2501
  }
1412
- function Z(e, t) {
1413
- return it(e, t);
2502
+ function gn() {
2503
+ return async (e, t) => {
2504
+ if (![
2505
+ "PATCH",
2506
+ "PUT",
2507
+ "DELETE"
2508
+ ].includes(e.req.method)) return t();
2509
+ let n = on(e.req.path), r = ln(e.req.path);
2510
+ if (!n && !r) return t();
2511
+ let i = e.var.tenant_id || e.req.header("x-tenant-id") || e.req.header("tenant-id");
2512
+ if (!i) return t();
2513
+ if (n && await sn(J(e), i, n)) throw new O(403, { message: `This ${cn(n.type)} is a system resource and cannot be modified. Make changes in the control plane instead.` });
2514
+ return r && x(r.id) && await hn(e, i, r), t();
2515
+ };
1414
2516
  }
1415
2517
  //#endregion
1416
2518
  //#region src/middleware/index.ts
1417
- function ot(e) {
2519
+ function _n(e) {
1418
2520
  return async (t, n) => {
1419
2521
  let r = t.var.user;
1420
2522
  return r?.tenant_id === e && r.org_name && t.set("tenant_id", r.org_name), n();
1421
2523
  };
1422
2524
  }
1423
- function st(e) {
2525
+ function vn(e) {
1424
2526
  return async (t, r) => {
1425
2527
  if (!e.accessControl) return r();
1426
2528
  let { controlPlaneTenantId: i } = e.accessControl, a = t.var.org_name, o = t.var.organization_id, s = a || o, c = t.var.tenant_id, l = t.var.user, u = (l?.aud ? Array.isArray(l.aud) ? l.aud : [l.aud] : []).includes(n);
1427
- if (!c && s && u && (t.set("tenant_id", s), c = s), !c) throw new T(400, { message: "Tenant ID not found in request" });
1428
- if (!D(o, c, i, a)) throw new T(403, { message: `Access denied to tenant ${c}` });
2529
+ if (!c && s && u && (t.set("tenant_id", s), c = s), !c) throw new O(400, { message: "Tenant ID not found in request" });
2530
+ if (!N(o, c, i, a)) throw new O(403, { message: `Access denied to tenant ${c}` });
1429
2531
  return r();
1430
2532
  };
1431
2533
  }
1432
- function ct(e) {
2534
+ function yn(e) {
1433
2535
  return async (t, n) => {
1434
2536
  if (!e.subdomainRouting) return n();
1435
2537
  let { baseDomain: r, reservedSubdomains: i = [], resolveSubdomain: a } = e.subdomainRouting, o = t.req.header("x-forwarded-host") || t.req.header("host") || "", s = null;
@@ -1441,18 +2543,18 @@ function ct(e) {
1441
2543
  let c = null;
1442
2544
  if (a) c = await a(s);
1443
2545
  else if (e.subdomainRouting.useOrganizations !== !1 && e.accessControl) try {
1444
- let n = await P(t).organizations.get(e.accessControl.controlPlaneTenantId, s);
2546
+ let n = await J(t).organizations.get(e.accessControl.controlPlaneTenantId, s);
1445
2547
  n && (c = n.id);
1446
2548
  } catch {}
1447
- if (!c) throw new T(404, { message: `Tenant not found for subdomain: ${s}` });
2549
+ if (!c) throw new O(404, { message: `Tenant not found for subdomain: ${s}` });
1448
2550
  return t.set("tenant_id", c), n();
1449
2551
  };
1450
2552
  }
1451
- function lt(e) {
2553
+ function bn(e) {
1452
2554
  return async (t, n) => {
1453
2555
  if (!e.databaseIsolation) return n();
1454
2556
  let r = t.var.tenant_id;
1455
- if (!r) throw new T(400, { message: "Tenant ID not found in request" });
2557
+ if (!r) throw new O(400, { message: "Tenant ID not found in request" });
1456
2558
  try {
1457
2559
  let n = await e.databaseIsolation.getAdapters(r);
1458
2560
  t.set("baseData", n), t.env = {
@@ -1460,131 +2562,817 @@ function lt(e) {
1460
2562
  data: n
1461
2563
  };
1462
2564
  } catch (e) {
1463
- throw console.error(`Failed to resolve database for tenant ${r}:`, e), new T(500, { message: "Failed to resolve tenant database" });
2565
+ throw console.error(`Failed to resolve database for tenant ${r}:`, e), new O(500, { message: "Failed to resolve tenant database" });
1464
2566
  }
1465
2567
  return n();
1466
2568
  };
1467
2569
  }
1468
- function Q(e) {
1469
- let t = ct(e), n = st(e), r = lt(e);
2570
+ function xn(e) {
2571
+ let t = yn(e), n = vn(e), r = bn(e);
1470
2572
  return async (e, i) => (await t(e, async () => {}), await n(e, async () => {}), await r(e, async () => {}), i());
1471
2573
  }
1472
2574
  //#endregion
2575
+ //#region src/rollouts/config.ts
2576
+ var Sn = [
2577
+ 10,
2578
+ 50,
2579
+ 100
2580
+ ], X = {
2581
+ canary: 1,
2582
+ waves: Sn,
2583
+ failure_threshold: .1,
2584
+ max_concurrent: 25,
2585
+ debounce_ms: 6e4
2586
+ };
2587
+ function Z(e) {
2588
+ let t = e.config ?? {};
2589
+ return {
2590
+ ...X,
2591
+ trigger: "manual",
2592
+ root_tenant_id: "",
2593
+ ...t,
2594
+ waves: Array.isArray(t.waves) && t.waves.length > 0 ? t.waves : [...Sn]
2595
+ };
2596
+ }
2597
+ function Cn(e, t, n) {
2598
+ let r = Array(e).fill(0);
2599
+ if (e === 0) return r;
2600
+ let i = Math.min(Math.max(t, 0), e), a = [], o = i;
2601
+ for (let t of n) {
2602
+ let n = Math.min(e, Math.max(o, Math.ceil(e * t / 100)));
2603
+ n > o && (a.push(n), o = n);
2604
+ }
2605
+ o < e && a.push(e);
2606
+ let s = +(i > 0), c = i;
2607
+ for (let e of a) {
2608
+ for (; c < e; c++) r[c] = s;
2609
+ s++;
2610
+ }
2611
+ return r;
2612
+ }
2613
+ //#endregion
2614
+ //#region src/rollouts/enumerate.ts
2615
+ async function wn(e, t, n) {
2616
+ let r = n.maxDepth ?? 8, i = await g((t) => e.tenants.list(t), "tenants", {
2617
+ cursorField: "id",
2618
+ pageSize: 100
2619
+ }), a = new Map(i.map((e) => [e.id, e])), o = async (e) => e.parent_tenant_id ?? (e.id === t ? void 0 : await n.resolveLegacyParent(e.id)), s = [];
2620
+ for (let e of i) {
2621
+ if (e.id === t || e.config_sync !== "synced") continue;
2622
+ let n = 0, i = e, c = !1;
2623
+ for (; i && n <= r;) {
2624
+ let e = await o(i);
2625
+ if (!e) break;
2626
+ if (n++, e === t) {
2627
+ c = !0;
2628
+ break;
2629
+ }
2630
+ i = a.get(e);
2631
+ }
2632
+ c && s.push({
2633
+ tenantId: e.id,
2634
+ depth: n,
2635
+ appliedHash: e.config_sync_hash ?? null
2636
+ });
2637
+ }
2638
+ return s.sort((e, t) => e.depth - t.depth || (e.tenantId < t.tenantId ? -1 : 1));
2639
+ }
2640
+ //#endregion
2641
+ //#region src/rollouts/hashes.ts
2642
+ async function Q(e, t) {
2643
+ return e.resolveControlPlane ? (await e.resolveControlPlane({ tenant_id: t }))?.tenantId : e.controlPlaneTenantId;
2644
+ }
2645
+ function Tn(e) {
2646
+ let t = /* @__PURE__ */ new Map();
2647
+ return async (n) => {
2648
+ let r = await Q(e, n), i = (await R({
2649
+ tenantId: n,
2650
+ getTenant: (t) => e.adapters.tenants.get(t),
2651
+ legacyControlPlane: r,
2652
+ maxDepth: e.maxDepth
2653
+ })).join(">"), a = t.get(i);
2654
+ return a || (a = at(e.adapters, n, {
2655
+ legacyControlPlane: r,
2656
+ maxDepth: e.maxDepth
2657
+ }).then((e) => e.hash), t.set(i, a)), a;
2658
+ };
2659
+ }
2660
+ //#endregion
2661
+ //#region src/rollouts/plan.ts
2662
+ var En = [
2663
+ "pending",
2664
+ "canary",
2665
+ "rolling",
2666
+ "paused"
2667
+ ];
2668
+ function Dn(e) {
2669
+ let { tenantOperations: t, tenantOperationEvents: n } = e.adapters;
2670
+ if (!t || !n) throw Error("Fleet rollouts need the tenant operations adapters");
2671
+ return {
2672
+ tenantOperations: t,
2673
+ tenantOperationEvents: n
2674
+ };
2675
+ }
2676
+ function $(e) {
2677
+ if (!e.adapters.rollouts) throw Error("Fleet rollouts need the rollouts adapter");
2678
+ return e.adapters.rollouts;
2679
+ }
2680
+ async function On(e, t, n = {}) {
2681
+ let r = $(e), i = Dn(e), a = Z(t), o = await wn(e.adapters, a.root_tenant_id, {
2682
+ resolveLegacyParent: (t) => Q(e, t),
2683
+ maxDepth: e.maxDepth
2684
+ });
2685
+ if (n.tenantIds) {
2686
+ let e = new Set(n.tenantIds);
2687
+ o = o.filter((t) => e.has(t.tenantId));
2688
+ }
2689
+ let s = Tn(e), c = [];
2690
+ for (let e of o) (a.force || e.appliedHash !== await s(e.tenantId)) && c.push(e);
2691
+ let l = o.length - c.length;
2692
+ if (c.length === 0) return a.trigger === "write" ? await r.remove(t.id) : await r.update(t.id, {
2693
+ status: "done",
2694
+ finished_at: new Date(e.now?.() ?? Date.now()).toISOString(),
2695
+ config: {
2696
+ ...a,
2697
+ planned: !0,
2698
+ tenant_count: 0
2699
+ }
2700
+ }), {
2701
+ planned: 0,
2702
+ upToDate: l
2703
+ };
2704
+ let u = new Set((await kn(e, t.id)).map((e) => e.tenant_id)), d = Cn(c.length, a.canary, a.waves);
2705
+ for (let [n, r] of c.entries()) u.has(r.tenantId) || await se(i, {
2706
+ kind: "sync-defaults",
2707
+ tenant_id: r.tenantId,
2708
+ rollout_id: t.id,
2709
+ initiated_by: `rollout:${t.id}`,
2710
+ input: {
2711
+ wave: d[n],
2712
+ ...a.dry_run ? { dry_run: !0 } : {},
2713
+ ...a.force ? { force: !0 } : {}
2714
+ }
2715
+ }, e.executor.engine);
2716
+ return await r.update(t.id, {
2717
+ status: "canary",
2718
+ config: {
2719
+ ...a,
2720
+ planned: !0,
2721
+ tenant_count: c.length
2722
+ }
2723
+ }), {
2724
+ planned: c.length,
2725
+ upToDate: l
2726
+ };
2727
+ }
2728
+ async function kn(e, t) {
2729
+ let { tenantOperations: n } = Dn(e), r = [];
2730
+ for (let e = 0;; e++) {
2731
+ let { operations: i } = await n.list({
2732
+ rollout_id: t,
2733
+ page: e,
2734
+ per_page: 200
2735
+ });
2736
+ if (r.push(...i), i.length < 200) break;
2737
+ }
2738
+ return r.sort((e, t) => e.created_at === t.created_at ? e.id < t.id ? -1 : 1 : e.created_at < t.created_at ? -1 : 1);
2739
+ }
2740
+ //#endregion
2741
+ //#region src/rollouts/advance.ts
2742
+ var An = (e) => {
2743
+ let t = e.input?.wave;
2744
+ return typeof t == "number" ? t : 0;
2745
+ }, jn = (e) => e.status === "failed" || e.status === "cancelled";
2746
+ function Mn(e, t, n) {
2747
+ let r = t.filter((e) => An(e) === n), i = r.filter(jn).length, a = n === 0 && e.canary > 0, o = a ? 0 : e.failure_threshold;
2748
+ return r.length === 0 || i === 0 || i / r.length <= o ? null : `${a ? "canary" : `wave ${n}`} failed for ${i} of ${r.length} tenant(s)`;
2749
+ }
2750
+ async function Nn(e, t) {
2751
+ let n = $(e), r = e.now?.() ?? Date.now(), i = typeof t == "string" ? await n.get(t) : t;
2752
+ if (!i || i.kind !== "config-sync") return {
2753
+ rolloutId: String(i?.id ?? t),
2754
+ outcome: "skipped",
2755
+ started: 0
2756
+ };
2757
+ let a = i.id;
2758
+ if (!En.includes(i.status) || i.status === "paused") return {
2759
+ rolloutId: a,
2760
+ outcome: "skipped",
2761
+ started: 0
2762
+ };
2763
+ let o = Z(i);
2764
+ if (!o.planned) {
2765
+ if (r - Date.parse(i.updated_at) < o.debounce_ms) return {
2766
+ rolloutId: a,
2767
+ outcome: "waiting",
2768
+ started: 0
2769
+ };
2770
+ let { planned: t } = await On(e, i);
2771
+ if (t === 0) return {
2772
+ rolloutId: a,
2773
+ outcome: "planned-empty",
2774
+ started: 0
2775
+ };
2776
+ i = await n.get(a) ?? i, o = Z(i);
2777
+ }
2778
+ let s = await kn(e, a), c = s.filter((e) => !L(e.status));
2779
+ if (c.length === 0) {
2780
+ let e = Math.max(0, ...s.map(An)), t = s.length > 0 && e > (o.accepted_through ?? -1) ? Mn(o, s, e) : null;
2781
+ return await n.update(a, {
2782
+ status: t ? "failed" : "done",
2783
+ ...t ? { paused_reason: t } : {},
2784
+ finished_at: new Date(r).toISOString()
2785
+ }), {
2786
+ rolloutId: a,
2787
+ outcome: "done",
2788
+ started: 0
2789
+ };
2790
+ }
2791
+ let l = Math.min(...c.map(An)), u = l - 1;
2792
+ if (u >= 0 && u > (o.accepted_through ?? -1)) {
2793
+ let e = Mn(o, s, u);
2794
+ if (e) return await n.update(a, {
2795
+ status: "paused",
2796
+ paused_reason: e
2797
+ }), {
2798
+ rolloutId: a,
2799
+ outcome: "paused",
2800
+ started: 0,
2801
+ wave: l
2802
+ };
2803
+ }
2804
+ let d = l === 0 ? "canary" : "rolling";
2805
+ i.status !== d && await n.update(a, { status: d });
2806
+ let f = c.filter((e) => An(e) === l), p = f.filter((e) => e.status === "running").length, m = Math.max(0, o.max_concurrent - p), { tenantOperations: h, tenantOperationEvents: g } = Dn(e), _ = I({
2807
+ tenantOperations: h,
2808
+ tenantOperationEvents: g
2809
+ }), v = 0;
2810
+ for (let t of f.filter((e) => e.status === "pending").slice(0, m)) {
2811
+ try {
2812
+ await _.markRunning(t.id), await e.executor.start(t);
2813
+ } catch (e) {
2814
+ try {
2815
+ let n = await h.get(t.id);
2816
+ n && !L(n.status) && await _.markFailed(t.id, e);
2817
+ } catch {}
2818
+ }
2819
+ v++;
2820
+ }
2821
+ return {
2822
+ rolloutId: a,
2823
+ outcome: v > 0 ? "started" : "running",
2824
+ started: v,
2825
+ wave: l
2826
+ };
2827
+ }
2828
+ async function Pn(e, t = {}) {
2829
+ let { rollouts: n } = await $(e).list({
2830
+ kind: "config-sync",
2831
+ status: [
2832
+ "pending",
2833
+ "canary",
2834
+ "rolling"
2835
+ ],
2836
+ per_page: t.limit ?? 20
2837
+ }), r = [];
2838
+ for (let i of n) try {
2839
+ r.push(await Nn(e, i));
2840
+ } catch (e) {
2841
+ t.logger?.warn(`Advancing rollout ${i.id} failed:`, e);
2842
+ }
2843
+ return r;
2844
+ }
2845
+ async function Fn(e, t, n = "paused by an operator") {
2846
+ let r = $(e), i = await r.get(t);
2847
+ return !i || i.kind !== "config-sync" ? null : i.status === "done" || i.status === "failed" ? i : (await r.update(t, {
2848
+ status: "paused",
2849
+ paused_reason: n
2850
+ }), r.get(t));
2851
+ }
2852
+ async function In(e, t) {
2853
+ let n = $(e), r = await n.get(t);
2854
+ if (!r || r.kind !== "config-sync") return null;
2855
+ if (r.status !== "paused") return r;
2856
+ let i = Z(r), a = (await kn(e, t)).filter((e) => !L(e.status)), o = a.length > 0 ? Math.min(...a.map(An)) : 0;
2857
+ return await n.update(t, {
2858
+ status: o === 0 ? "canary" : "rolling",
2859
+ paused_reason: null,
2860
+ config: {
2861
+ ...i,
2862
+ accepted_through: Math.max(i.accepted_through ?? -1, o - 1)
2863
+ }
2864
+ }), n.get(t);
2865
+ }
2866
+ //#endregion
2867
+ //#region src/rollouts/verify.ts
2868
+ var Ln = {
2869
+ added: "missing",
2870
+ changed: "changed",
2871
+ delete: "stale"
2872
+ };
2873
+ function Rn(e) {
2874
+ return (e.rows ?? e.changes.flatMap((e) => {
2875
+ if (e.startsWith("skip ")) return [];
2876
+ let t = /^delete ([^:\s]+):(.*)$/.exec(e);
2877
+ if (t) return [{
2878
+ entity: t[1],
2879
+ key: t[2],
2880
+ action: "delete"
2881
+ }];
2882
+ let n = /^([^:\s]+):(.*)$/.exec(e);
2883
+ return n ? [{
2884
+ entity: n[1],
2885
+ key: n[2],
2886
+ action: "changed"
2887
+ }] : [];
2888
+ })).flatMap((e) => {
2889
+ let t = Ln[e.action];
2890
+ return t ? [{
2891
+ entity: e.entity,
2892
+ key: e.key,
2893
+ kind: t
2894
+ }] : [];
2895
+ });
2896
+ }
2897
+ async function zn(e, t) {
2898
+ let n = await e.adapters.tenants.get(t);
2899
+ if (!n) return null;
2900
+ let r = n.config_sync ?? "inherit-on-read", i = {
2901
+ tenant_id: t,
2902
+ config_sync: r,
2903
+ errors: []
2904
+ };
2905
+ if (r === "detached") return {
2906
+ ...i,
2907
+ method: "not-applicable",
2908
+ ok: !0,
2909
+ differences: []
2910
+ };
2911
+ if (r === "inherit-on-read") {
2912
+ let n = (await De({
2913
+ adapters: e.adapters,
2914
+ tenantId: t,
2915
+ legacyControlPlane: await Q(e, t),
2916
+ maxDepth: e.maxDepth
2917
+ })).filter((e) => !e.expected).map((e) => ({
2918
+ entity: e.entity,
2919
+ key: e.key,
2920
+ kind: e.kind,
2921
+ ...e.fields.length > 0 ? { fields: e.fields } : {}
2922
+ }));
2923
+ return {
2924
+ ...i,
2925
+ method: "read-time",
2926
+ ok: n.length === 0,
2927
+ differences: n
2928
+ };
2929
+ }
2930
+ let a = await e.syncConfig(t, {
2931
+ dryRun: !0,
2932
+ force: !1,
2933
+ prune: !1
2934
+ }), o = Rn(a), s = Object.entries(a.entities).filter(([, e]) => e.errors.length > 0).map(([e, t]) => `${e}: the sync dry run reported ${t.errors.length} error(s)`);
2935
+ return {
2936
+ ...i,
2937
+ method: "sync-dry-run",
2938
+ ok: o.length === 0 && s.length === 0,
2939
+ differences: o,
2940
+ errors: s.slice(0, 20)
2941
+ };
2942
+ }
2943
+ //#endregion
2944
+ //#region src/rollouts/status.ts
2945
+ async function Bn(e, t) {
2946
+ let n = await e.adapters.tenants.get(t);
2947
+ if (!n) return null;
2948
+ if (n.config_sync === "detached") return {
2949
+ tenant_id: t,
2950
+ parent_tenant_id: null,
2951
+ config_sync: "detached",
2952
+ config_sync_hash: null,
2953
+ config_synced_at: null,
2954
+ expected_hash: null,
2955
+ drifted: !1,
2956
+ chain: [],
2957
+ inherited: {}
2958
+ };
2959
+ let r = n.config_sync === "synced", i = await R({
2960
+ tenantId: t,
2961
+ getTenant: (t) => e.adapters.tenants.get(t),
2962
+ legacyControlPlane: await Q(e, t),
2963
+ maxDepth: e.maxDepth
2964
+ }), a = r ? await Tn(e)(t) : null, o = n.config_sync_hash ?? null, s = {};
2965
+ for (let n of [...i].reverse()) {
2966
+ let r = await V(e.adapters, n), i = (e, t) => U(e, t) === "shared", a = async (e, r, a) => {
2967
+ for (let o of r) {
2968
+ if (!i(a, o)) continue;
2969
+ let r = await it(e, t, W(e, o)), c = o.id ?? o.hook_id;
2970
+ c && x(c) && s[r] || (s[r] = n);
2971
+ }
2972
+ };
2973
+ await a("connection", r.connections, "connection"), await a("resourceServer", r.resourceServers, "resourceServer"), await a("action", r.actions, "action"), await a("hook", r.hooks, "hook"), r.emailProvider && i("emailProvider", r.emailProvider) && (s.emailProvider = n), r.branding && i("branding", r.branding) && (s.branding = n), r.promptSettings && i("promptSettings", r.promptSettings) && (s.promptSettings = n);
2974
+ }
2975
+ return {
2976
+ tenant_id: t,
2977
+ parent_tenant_id: n.parent_tenant_id ?? null,
2978
+ config_sync: r ? "synced" : "inherit-on-read",
2979
+ config_sync_hash: o,
2980
+ config_synced_at: n.config_synced_at ?? null,
2981
+ expected_hash: a,
2982
+ drifted: r && a !== o,
2983
+ chain: i,
2984
+ inherited: s
2985
+ };
2986
+ }
2987
+ //#endregion
2988
+ //#region src/rollouts/reconcile.ts
2989
+ async function Vn(e, n = {}) {
2990
+ let r = $(e), i = n.rootTenantId ?? e.controlPlaneTenantId, a = n.limit ?? 500, o = await wn(e.adapters, i, {
2991
+ resolveLegacyParent: (t) => Q(e, t),
2992
+ maxDepth: e.maxDepth
2993
+ }), s = Tn(e), c = [];
2994
+ for (let e of o) {
2995
+ if (c.length >= a) break;
2996
+ try {
2997
+ let t = await s(e.tenantId);
2998
+ e.appliedHash !== t && c.push({
2999
+ tenantId: e.tenantId,
3000
+ expected: t,
3001
+ applied: e.appliedHash
3002
+ });
3003
+ } catch (t) {
3004
+ n.logger?.warn(`Could not compute the expected hash of ${e.tenantId}:`, t);
3005
+ }
3006
+ }
3007
+ let l = {
3008
+ checked: o.length,
3009
+ drifted: c.length,
3010
+ alreadyCovered: 0,
3011
+ recentlyFailed: 0
3012
+ };
3013
+ if (c.length === 0) return l;
3014
+ let u = /* @__PURE__ */ new Set(), { rollouts: d } = await r.list({
3015
+ kind: "config-sync",
3016
+ status: [...En],
3017
+ per_page: 100
3018
+ });
3019
+ for (let t of d) for (let n of await kn(e, t.id)) n.tenant_id && !L(n.status) && u.add(n.tenant_id);
3020
+ for (let r of c) try {
3021
+ await e.adapters.logs.create(r.tenantId, {
3022
+ type: t.CONFIG_DRIFT,
3023
+ date: new Date(e.now?.() ?? Date.now()).toISOString(),
3024
+ isMobile: !1,
3025
+ description: r.applied ? "hash-mismatch: applied config differs from what the parent chain publishes" : "never-synced: no config has been applied to this synced tenant",
3026
+ details: {
3027
+ reason: r.applied ? "hash-mismatch" : "never-synced",
3028
+ applied_hash: r.applied,
3029
+ expected_hash: r.expected,
3030
+ covered_by_open_rollout: u.has(r.tenantId),
3031
+ trigger: "reconcile"
3032
+ }
3033
+ });
3034
+ } catch (e) {
3035
+ n.logger?.warn(`Could not record drift for ${r.tenantId}:`, e);
3036
+ }
3037
+ let f = c.filter((e) => !u.has(e.tenantId));
3038
+ l.alreadyCovered = c.length - f.length;
3039
+ let { tenantOperations: p } = Dn(e), m = e.now?.() ?? Date.now(), h = n.retryAfterMs ?? 36e5, g = [];
3040
+ for (let e of f) {
3041
+ try {
3042
+ let { operations: t } = await p.list({
3043
+ tenant_id: e.tenantId,
3044
+ kind: "sync-defaults",
3045
+ per_page: 5
3046
+ }), n = t.sort((e, t) => e.created_at === t.created_at ? e.id < t.id ? 1 : -1 : e.created_at < t.created_at ? 1 : -1)[0];
3047
+ if (n?.status === "failed" && m - Date.parse(n.updated_at) < h) {
3048
+ l.recentlyFailed++;
3049
+ continue;
3050
+ }
3051
+ } catch (t) {
3052
+ n.logger?.warn(`Could not read the latest sync of ${e.tenantId}:`, t);
3053
+ }
3054
+ g.push(e);
3055
+ }
3056
+ if (g.length === 0) return l;
3057
+ let _ = await r.create({
3058
+ kind: "config-sync",
3059
+ initiated_by: "reconcile",
3060
+ config: {
3061
+ ...X,
3062
+ trigger: "reconcile",
3063
+ root_tenant_id: i,
3064
+ planned: !1
3065
+ }
3066
+ });
3067
+ return await On(e, _, { tenantIds: g.map((e) => e.tenantId) }), l.rolloutId = _.id, l;
3068
+ }
3069
+ //#endregion
3070
+ //#region src/rollouts/trigger.ts
3071
+ function Hn(e, t) {
3072
+ return e === "hookCode" ? !0 : !t || typeof t != "object" ? !1 : U(e, t) !== "local";
3073
+ }
3074
+ async function Un(e, t, n) {
3075
+ let r = $(e), { rollouts: i } = await r.list({
3076
+ kind: "config-sync",
3077
+ status: "pending",
3078
+ per_page: 100
3079
+ }), a = i.find((e) => {
3080
+ let n = Z(e);
3081
+ return n.trigger === "write" && n.planned !== !0 && n.origin_tenant_id === t;
3082
+ });
3083
+ return a ? (await r.update(a.id, {}), await r.get(a.id) ?? a) : r.create({
3084
+ kind: "config-sync",
3085
+ initiated_by: n ?? `write:${t}`,
3086
+ config: {
3087
+ ...X,
3088
+ trigger: "write",
3089
+ root_tenant_id: t,
3090
+ origin_tenant_id: t,
3091
+ planned: !1
3092
+ }
3093
+ });
3094
+ }
3095
+ async function Wn(e, t) {
3096
+ if (t === e.controlPlaneTenantId) return !0;
3097
+ try {
3098
+ let { tenants: n } = await e.adapters.tenants.list({
3099
+ q: `parent_tenant_id:${t}`,
3100
+ per_page: 1
3101
+ });
3102
+ return n.some((e) => e.parent_tenant_id === t);
3103
+ } catch {
3104
+ return !0;
3105
+ }
3106
+ }
3107
+ function Gn(e, t = {}) {
3108
+ let n = async (n) => {
3109
+ try {
3110
+ await Wn(e, n) && await Un(e, n);
3111
+ } catch (e) {
3112
+ t.logger?.warn(`Could not record a config change for ${n}:`, e);
3113
+ }
3114
+ }, r = (e) => ({
3115
+ afterCreate: async (t, r) => {
3116
+ Hn(e, r) && await n(t.tenantId);
3117
+ },
3118
+ afterUpdate: async (t, r, i) => {
3119
+ Hn(e, i) && await n(t.tenantId);
3120
+ },
3121
+ afterDelete: async (e) => n(e.tenantId)
3122
+ });
3123
+ return {
3124
+ actions: r("action"),
3125
+ hooks: r("hook"),
3126
+ hookCode: r("hookCode"),
3127
+ connections: r("connection"),
3128
+ resourceServers: r("resourceServer")
3129
+ };
3130
+ }
3131
+ //#endregion
3132
+ //#region src/rollouts/controller.ts
3133
+ function Kn(e) {
3134
+ return {
3135
+ async create(t) {
3136
+ let n = {
3137
+ ...X,
3138
+ waves: t.waves ?? [...X.waves],
3139
+ canary: t.canary ?? X.canary,
3140
+ failure_threshold: t.failureThreshold ?? X.failure_threshold,
3141
+ max_concurrent: t.maxConcurrent ?? X.max_concurrent,
3142
+ trigger: "manual",
3143
+ root_tenant_id: t.rootTenantId ?? e.controlPlaneTenantId,
3144
+ dry_run: t.dryRun,
3145
+ force: t.force,
3146
+ planned: !1
3147
+ }, r = $(e), i = await r.create({
3148
+ kind: "config-sync",
3149
+ initiated_by: t.initiatedBy,
3150
+ config: n
3151
+ });
3152
+ return await On(e, i, { tenantIds: t.tenantIds }), await Nn(e, i.id), await r.get(i.id) ?? i;
3153
+ },
3154
+ pause: (t, n) => Fn(e, t, n),
3155
+ resume: (t) => In(e, t)
3156
+ };
3157
+ }
3158
+ //#endregion
1473
3159
  //#region src/init.ts
1474
- function ut(e) {
1475
- let { dataAdapter: t, controlPlane: n, controlPlane: { tenantId: r = "control_plane", clientId: i } = {}, resolveControlPlane: a, inheritanceShadow: o, sync: s = {
3160
+ function qn(e) {
3161
+ let { dataAdapter: t, controlPlane: n, controlPlane: { tenantId: r = "control_plane", clientId: i } = {}, resolveControlPlane: a, inheritanceShadow: o, syncDefaults: s, detachTenant: c, tenantMemberRequests: l, rolloutExecutor: u, sync: d = {
1476
3162
  resourceServers: !0,
1477
3163
  roles: !0
1478
- }, defaultPermissions: c = ["tenant:admin"], requireOrganizationMatch: l = !1, managementApiExtensions: u = [], entityHooks: d, getChildTenantIds: p, getAdapters: h, ...g } = e;
3164
+ }, defaultPermissions: f = ["tenant:admin"], requireOrganizationMatch: p = !1, managementApiExtensions: m = [], entityHooks: h, getChildTenantIds: _, getAdapters: v, ...b } = e;
1479
3165
  if (a && !n) throw Error("initMultiTenant: `resolveControlPlane` requires `controlPlane` to be set. The static `controlPlane.tenantId` is used for access control, sync direction, and tenant management routing; the resolver only overrides per-tenant runtime inheritance lookups on top of it.");
1480
- let _ = t, v = t;
1481
- n && (_ = Z(t, {
1482
- controlPlaneTenantId: r,
1483
- controlPlaneClientId: i,
1484
- resolveControlPlane: a,
1485
- inheritanceShadow: o
1486
- }), v = {
1487
- ...rt(t, {
3166
+ let x = t, S = t, C;
3167
+ if (n) {
3168
+ let n = Me({
3169
+ base: Re({
3170
+ controlPlaneTenantId: r,
3171
+ controlPlaneClientId: i,
3172
+ resolveControlPlane: a
3173
+ }),
3174
+ getTenant: async (e) => await t.tenants.get(e) ?? null,
1488
3175
  controlPlaneTenantId: r,
1489
- resolveControlPlane: a
1490
- }),
1491
- multiTenancyConfig: {
3176
+ ttlMs: e.configSyncCacheTtlMs
3177
+ });
3178
+ C = (e) => n.invalidate(e), x = We(t, {
1492
3179
  controlPlaneTenantId: r,
1493
3180
  controlPlaneClientId: i,
1494
- resolveControlPlane: a
1495
- }
1496
- });
1497
- let y = g.tenantOperationExecutor;
1498
- if (!y && t.tenantOperations && t.tenantOperationEvents && g.tenantUpgrade) {
3181
+ resolveControlPlane: a,
3182
+ inheritanceShadow: o,
3183
+ configSync: n
3184
+ }), S = {
3185
+ ...Be(t, {
3186
+ controlPlaneTenantId: r,
3187
+ resolveControlPlane: n.resolve
3188
+ }),
3189
+ multiTenancyConfig: {
3190
+ controlPlaneTenantId: r,
3191
+ controlPlaneClientId: i,
3192
+ resolveControlPlane: n.resolve
3193
+ }
3194
+ };
3195
+ }
3196
+ let w = b.tenantOperationExecutor, T, E;
3197
+ if (t.tenantOperations && t.tenantOperationEvents) {
1499
3198
  let e = {
1500
3199
  tenantOperations: t.tenantOperations,
1501
3200
  tenantOperationEvents: t.tenantOperationEvents
1502
- }, n = g.tenantUpgrade, r = ie({
3201
+ }, n = b.tenantUpgrade, i = I(e), o = s ?? xt(t, {
3202
+ controlPlaneTenantId: r,
3203
+ resolveControlPlane: a
3204
+ }), u = c ?? Mt(t), d = l ? l.store ?? Ft(t) : void 0, f = oe({
1503
3205
  stores: e,
1504
- definitions: { upgrade: (e) => [{
1505
- name: "upgrade",
1506
- async run() {
1507
- if (!e.tenant_id) throw Error("upgrade operations require a tenant_id");
1508
- await n(e.tenant_id);
1509
- }
1510
- }] }
3206
+ definitions: {
3207
+ detach: (e) => [{
3208
+ name: "detach",
3209
+ run: () => Dt({
3210
+ operation: e,
3211
+ detachTenant: u,
3212
+ recorder: i,
3213
+ onDetached: (e) => C?.(e)
3214
+ })
3215
+ }],
3216
+ ...n ? { upgrade: (e) => [{
3217
+ name: "upgrade",
3218
+ async run() {
3219
+ if (!e.tenant_id) throw Error("upgrade operations require a tenant_id");
3220
+ await n(e.tenant_id);
3221
+ }
3222
+ }] } : {},
3223
+ "sync-defaults": (e) => [{
3224
+ name: "sync-defaults",
3225
+ run: () => bt({
3226
+ operation: e,
3227
+ syncConfig: o,
3228
+ recorder: i
3229
+ })
3230
+ }],
3231
+ ...d && l ? { "tenant-members-request": (e) => [{
3232
+ name: "tenant-members-request",
3233
+ run: () => Rt({
3234
+ operation: e,
3235
+ store: d,
3236
+ getBackend: l.getBackend,
3237
+ recorder: i,
3238
+ executeOptions: { isRequesterAllowed: l.isRequesterAllowed }
3239
+ })
3240
+ }] } : {}
3241
+ }
1511
3242
  });
1512
- y = {
1513
- engine: r.engine,
1514
- enqueue: (t) => oe(e, r, {
3243
+ if (T = f, d) {
3244
+ let n = {
3245
+ stores: e,
3246
+ executor: f
3247
+ };
3248
+ E = {
3249
+ enqueue: (e, t) => Bt(n, e, t),
3250
+ reconcile: (e = {}) => Vt({
3251
+ ...n,
3252
+ store: d,
3253
+ tenantIds: e.tenantIds ?? (async () => (await g((e) => t.tenants.list(e), "tenants", {
3254
+ cursorField: "id",
3255
+ pageSize: 100
3256
+ })).map((e) => e.id).filter((e) => e !== r)),
3257
+ graceMs: e.graceMs,
3258
+ maxAttempts: e.maxAttempts
3259
+ })
3260
+ };
3261
+ }
3262
+ w ??= {
3263
+ engine: f.engine,
3264
+ kinds: n ? [
3265
+ "upgrade",
3266
+ "sync-defaults",
3267
+ "detach"
3268
+ ] : ["sync-defaults", "detach"],
3269
+ enqueue: (t) => ce(e, f, {
1515
3270
  ...t,
1516
3271
  tenant_id: t.tenant_id
1517
- })
3272
+ }, { returnFailed: t.kind === "sync-defaults" || t.kind === "detach" })
1518
3273
  };
1519
3274
  }
1520
- let b = s !== !1, x = b ? {
1521
- resourceServers: s.resourceServers ?? !0,
1522
- roles: s.roles ?? !0
3275
+ if (l && !E) throw Error("initMultiTenant: `tenantMemberRequests` requires the `tenantOperations` and `tenantOperationEvents` adapters on the data adapter, which run the `tenant-members-request` operation.");
3276
+ let D = u ?? T, O = D && t.rollouts && t.tenantOperations && t.tenantOperationEvents ? {
3277
+ adapters: t,
3278
+ executor: D,
3279
+ controlPlaneTenantId: r,
3280
+ resolveControlPlane: a
3281
+ } : void 0, k = O ? Kn(O) : void 0, A = O ? Gn(O, { logger: console }) : void 0, j = d !== !1, ee = j ? {
3282
+ resourceServers: d.resourceServers ?? !0,
3283
+ roles: d.roles ?? !0
1523
3284
  } : {
1524
3285
  resourceServers: !1,
1525
3286
  roles: !1
1526
- }, { entityHooks: S, tenantHooks: C } = ve({
3287
+ }, { entityHooks: M, tenantHooks: N } = Qt({
1527
3288
  controlPlaneTenantId: r,
1528
- getChildTenantIds: p ?? (async () => (await f((e) => _.tenants.list(e), "tenants", {
3289
+ getChildTenantIds: _ ?? (async () => (await g((e) => x.tenants.list(e), "tenants", {
1529
3290
  cursorField: "id",
1530
3291
  pageSize: 100
1531
3292
  })).filter((e) => e.id !== r).map((e) => e.id)),
1532
- getAdapters: h ?? (async () => _),
1533
- getControlPlaneAdapters: async () => _,
1534
- sync: x
1535
- }), w = {
1536
- resourceServers: [S.resourceServers, ...d?.resourceServers ?? []],
1537
- roles: [S.roles, ...d?.roles ?? []],
1538
- connections: d?.connections ?? [],
1539
- tenants: d?.tenants ?? [],
1540
- rolePermissions: d?.rolePermissions ?? []
1541
- }, T = N({ accessControl: {
3293
+ getAdapters: v ?? (async () => x),
3294
+ getControlPlaneAdapters: async () => x,
3295
+ sync: ee
3296
+ }), te = {
3297
+ resourceServers: [
3298
+ M.resourceServers,
3299
+ A?.resourceServers,
3300
+ ...h?.resourceServers ?? []
3301
+ ],
3302
+ roles: [M.roles, ...h?.roles ?? []],
3303
+ connections: [A?.connections, ...h?.connections ?? []],
3304
+ tenants: h?.tenants ?? [],
3305
+ rolePermissions: h?.rolePermissions ?? [],
3306
+ actions: [A?.actions, ...h?.actions ?? []],
3307
+ hooks: [A?.hooks, ...h?.hooks ?? []],
3308
+ hookCode: [A?.hookCode, ...h?.hookCode ?? []]
3309
+ }, P = Ut({ accessControl: {
1542
3310
  controlPlaneTenantId: r,
1543
- requireOrganizationMatch: l,
1544
- defaultPermissions: c
1545
- } }), E = F({ accessControl: {
1546
- controlPlaneTenantId: r,
1547
- requireOrganizationMatch: l,
1548
- defaultPermissions: c
1549
- } }, { tenants: {
3311
+ requireOrganizationMatch: p,
3312
+ defaultPermissions: f
3313
+ } }), ne = an({
3314
+ accessControl: {
3315
+ controlPlaneTenantId: r,
3316
+ requireOrganizationMatch: p,
3317
+ defaultPermissions: f
3318
+ },
3319
+ onConfigSyncChange: (e) => C?.(e),
3320
+ configSyncStatus: (e) => Bn({
3321
+ adapters: t,
3322
+ controlPlaneTenantId: r,
3323
+ resolveControlPlane: a
3324
+ }, e),
3325
+ verifyConfigSync: (e) => zn({
3326
+ adapters: t,
3327
+ controlPlaneTenantId: r,
3328
+ resolveControlPlane: a,
3329
+ syncConfig: s ?? xt(t, {
3330
+ controlPlaneTenantId: r,
3331
+ resolveControlPlane: a
3332
+ })
3333
+ }, e)
3334
+ }, { tenants: {
1550
3335
  async beforeCreate(e, t) {
1551
- return T.beforeCreate && (t = await T.beforeCreate(e, t)), C.beforeCreate && (t = await C.beforeCreate(e, t)), t;
3336
+ return P.beforeCreate && (t = await P.beforeCreate(e, t)), N.beforeCreate && (t = await N.beforeCreate(e, t)), t;
1552
3337
  },
1553
3338
  async afterCreate(e, t) {
1554
- await T.afterCreate?.(e, t), await C.afterCreate?.(e, t);
3339
+ await P.afterCreate?.(e, t), await N.afterCreate?.(e, t);
1555
3340
  },
1556
3341
  async beforeDelete(e, t) {
1557
- await T.beforeDelete?.(e, t), await C.beforeDelete?.(e, t);
1558
- }
1559
- } }), { app: D } = m({
1560
- dataAdapter: _,
1561
- managementDataAdapter: v,
1562
- ...g,
1563
- tenantOperationExecutor: y,
1564
- entityHooks: w,
1565
- managementApiExtensions: [...u, {
3342
+ await P.beforeDelete?.(e, t), await N.beforeDelete?.(e, t);
3343
+ }
3344
+ } }), { app: re } = y({
3345
+ dataAdapter: x,
3346
+ managementDataAdapter: S,
3347
+ ...b,
3348
+ tenantOperationExecutor: w,
3349
+ ...k ? { rolloutController: k } : {},
3350
+ entityHooks: te,
3351
+ managementApiExtensions: [...m, {
1566
3352
  path: "/tenants",
1567
- router: E
3353
+ router: ne
1568
3354
  }],
1569
- managementApiMiddleware: [...g.managementApiMiddleware ?? [], ...b ? [Ee()] : []]
3355
+ managementApiMiddleware: [...b.managementApiMiddleware ?? [], ...j ? [gn()] : []]
1570
3356
  });
1571
3357
  return {
1572
- app: D,
1573
- controlPlaneTenantId: r
3358
+ app: re,
3359
+ controlPlaneTenantId: r,
3360
+ rollouts: O,
3361
+ memberRequests: E
1574
3362
  };
1575
3363
  }
1576
3364
  //#endregion
1577
3365
  //#region src/rollout/payload.ts
1578
- function dt(e) {
3366
+ function Jn(e) {
1579
3367
  let { pkcs7: t, tenant_id: n, ...r } = e;
1580
3368
  return r;
1581
3369
  }
1582
- async function ft(e, t, n = {}, r) {
1583
- let i = z(n), a = n.signingKeys ?? !0, o = n.tenants ?? !0, s = await H(e, t, i), c = a ? (await g(e.keys)).map(dt) : [], l = o ? await pt(e, t, r) : [];
3370
+ async function Yn(e, t, n = {}, r) {
3371
+ let i = pe(n), a = n.signingKeys ?? !0, o = n.tenants ?? !0, s = await ge(e, t, i), c = a ? (await S(e.keys)).map(Jn) : [], l = o ? await Xn(e, t, r) : [];
1584
3372
  return {
1585
3373
  connections: s.connections,
1586
3374
  resourceServers: s.resourceServers.filter((e) => e.is_system),
1587
- hooks: s.hooks.filter(ze),
3375
+ hooks: s.hooks.filter(he),
1588
3376
  emailProvider: s.emailProvider,
1589
3377
  branding: s.branding,
1590
3378
  promptSettings: s.promptSettings,
@@ -1592,7 +3380,7 @@ async function ft(e, t, n = {}, r) {
1592
3380
  tenants: l
1593
3381
  };
1594
3382
  }
1595
- async function pt(e, t, n) {
3383
+ async function Xn(e, t, n) {
1596
3384
  let r = n && n !== t ? [t, n] : [t], i = [];
1597
3385
  for (let t of r) {
1598
3386
  let n = await e.tenants.get(t);
@@ -1603,19 +3391,19 @@ async function pt(e, t, n) {
1603
3391
  }
1604
3392
  return i;
1605
3393
  }
1606
- async function mt(e, t, n) {
1607
- let r = B();
3394
+ async function Zn(e, t, n) {
3395
+ let r = z();
1608
3396
  if (e.length === 0) return r;
1609
- let i = await g(t.keys), a = new Set(i.map((e) => e.kid));
1610
- for (let i of e) await V(r, `signing_key ${i.kid}`, n, async () => {
1611
- let e = dt(i);
3397
+ let i = await S(t.keys), a = new Set(i.map((e) => e.kid));
3398
+ for (let i of e) await B(r, `signing_key ${i.kid}`, n, async () => {
3399
+ let e = Jn(i);
1612
3400
  a.has(e.kid) || (await t.keys.create(e), a.add(e.kid), r.upserted += 1);
1613
3401
  });
1614
3402
  return r;
1615
3403
  }
1616
- async function ht(e, t, n) {
1617
- let r = B();
1618
- for (let i of e) await V(r, `tenant ${i.id}`, n, async () => {
3404
+ async function Qn(e, t, n) {
3405
+ let r = z();
3406
+ for (let i of e) await B(r, `tenant ${i.id}`, n, async () => {
1619
3407
  await t.tenants.get(i.id) || (await t.tenants.create({
1620
3408
  id: i.id,
1621
3409
  friendly_name: i.friendly_name
@@ -1623,46 +3411,50 @@ async function ht(e, t, n) {
1623
3411
  });
1624
3412
  return r;
1625
3413
  }
1626
- async function gt(e, t, n, r = {}) {
1627
- let i = r.continueOnError ?? !1, o = z(r.entities), s = r.entities?.signingKeys ?? !0, c = r.entities?.tenants ?? !0, l = {
3414
+ async function $n(e, t, n, r = {}) {
3415
+ let i = r.continueOnError ?? !1, a = pe(r.entities), o = r.entities?.signingKeys ?? !0, c = r.entities?.tenants ?? !0, l = {
1628
3416
  connections: e.connections,
1629
3417
  resourceServers: e.resourceServers,
1630
3418
  hooks: e.hooks,
1631
- emailProvider: e.emailProvider ? u.parse(e.emailProvider) : null,
1632
- branding: e.branding ? a.parse(e.branding) : null,
1633
- promptSettings: e.promptSettings ? _.parse(e.promptSettings) : null
1634
- }, d = c ? await ht(e.tenants ?? [], t, i) : B(), f = Re(n);
1635
- await Be(l, t, n, o, i, f);
1636
- let p = s ? await mt(e.signingKeys, t, i) : B();
3419
+ emailProvider: e.emailProvider ? p.parse(e.emailProvider) : null,
3420
+ branding: e.branding ? s.parse(e.branding) : null,
3421
+ promptSettings: e.promptSettings ? C.parse(e.promptSettings) : null
3422
+ }, u = c ? await Qn(e.tenants ?? [], t, i) : z(), d = me(n);
3423
+ await _e(l, t, n, a, i, d);
3424
+ let f = o ? await Zn(e.signingKeys, t, i) : z(), m = e.resolved && r.resolved ? await ut(e.resolved, t, r.resolved.tenantId, {
3425
+ continueOnError: i,
3426
+ ...r.resolved
3427
+ }) : void 0;
1637
3428
  return {
1638
- ...f,
1639
- signingKeys: p,
1640
- tenants: d
3429
+ ...d,
3430
+ signingKeys: f,
3431
+ tenants: u,
3432
+ ...m ? { resolved: m } : {}
1641
3433
  };
1642
3434
  }
1643
3435
  //#endregion
1644
3436
  //#region src/rollout/index.ts
1645
- function _t(e) {
3437
+ function er(e) {
1646
3438
  return {
1647
- syncDefaults: (t) => U(e, t),
3439
+ syncDefaults: (t) => ve(e, t),
1648
3440
  syncDefaultsToTenants: async (t) => {
1649
3441
  let n = [];
1650
- for (let r of t) n.push(await U(e, r));
3442
+ for (let r of t) n.push(await ve(e, r));
1651
3443
  return n;
1652
3444
  }
1653
3445
  };
1654
3446
  }
1655
3447
  //#endregion
1656
3448
  //#region src/plugin.ts
1657
- function vt(e) {
1658
- let t = $(e);
3449
+ function tr(e) {
3450
+ let t = nr(e);
1659
3451
  return {
1660
3452
  name: "multi-tenancy",
1661
- middleware: Q(e),
3453
+ middleware: xn(e),
1662
3454
  hooks: t,
1663
3455
  routes: [{
1664
3456
  path: "/management",
1665
- handler: F(e, t)
3457
+ handler: an(e, t)
1666
3458
  }],
1667
3459
  onRegister: async () => {
1668
3460
  console.log("Multi-tenancy plugin registered"), e.accessControl && console.log(` - Access control enabled (control plane: ${e.accessControl.controlPlaneTenantId})`), e.subdomainRouting && console.log(` - Subdomain routing enabled (base domain: ${e.subdomainRouting.baseDomain})`), e.databaseIsolation && console.log(" - Database isolation enabled");
@@ -1671,29 +3463,29 @@ function vt(e) {
1671
3463
  }
1672
3464
  //#endregion
1673
3465
  //#region src/index.ts
1674
- function $(e) {
1675
- let t = e.accessControl ? E(e.accessControl) : {}, n = e.databaseIsolation ? ee(e.databaseIsolation) : {}, r = N(e);
3466
+ function nr(e) {
3467
+ let t = e.accessControl ? M(e.accessControl) : {}, n = e.databaseIsolation ? te(e.databaseIsolation) : {}, r = Ut(e);
1676
3468
  return {
1677
3469
  ...t,
1678
3470
  ...n,
1679
3471
  tenants: r
1680
3472
  };
1681
3473
  }
1682
- function yt(t) {
1683
- let n = new e(), r = $(t);
1684
- return n.route("/tenants", F(t, r)), n;
3474
+ function rr(t) {
3475
+ let n = new e(), r = nr(t);
3476
+ return n.route("/tenants", an(t, r)), n;
1685
3477
  }
1686
- function bt(e) {
3478
+ function ir(e) {
1687
3479
  return {
1688
- hooks: $(e),
1689
- middleware: Q(e),
1690
- app: yt(e),
3480
+ hooks: nr(e),
3481
+ middleware: xn(e),
3482
+ app: rr(e),
1691
3483
  config: e,
1692
- wrapAdapters: (t, n) => Z(t, {
3484
+ wrapAdapters: (t, n) => We(t, {
1693
3485
  controlPlaneTenantId: e.accessControl?.controlPlaneTenantId,
1694
3486
  controlPlaneClientId: n?.controlPlaneClientId
1695
3487
  })
1696
3488
  };
1697
3489
  }
1698
3490
  //#endregion
1699
- export { gt as applyControlPlaneDefaultsPayload, ft as buildControlPlaneDefaultsPayload, A as buildEngineInstanceId, Je as compareShadow, Ae as computeLegacyConfig, E as createAccessControlHooks, st as createAccessControlMiddleware, ot as createControlPlaneTenantMiddleware, ee as createDatabaseHooks, lt as createDatabaseMiddleware, _t as createDirectRolloutAdapter, ie as createInlineExecutor, yt as createMultiTenancy, $ as createMultiTenancyHooks, Q as createMultiTenancyMiddleware, vt as createMultiTenancyPlugin, M as createOperationRecorder, Ee as createProtectSyncedMiddleware, N as createProvisioningHooks, it as createRuntimeFallbackAdapter, Ze as createShadowReporter, ct as createSubdomainMiddleware, ve as createSyncHooks, F as createTenantsOpenAPIRouter, qe as diffConfig, L as effectiveOverride, I as effectiveScope, oe as enqueueTenantOperation, j as errorToMessage, ut as initMultiTenant, re as inlineStepRunner, ne as isTerminalStatus, et as mergeClientWithFallback, Le as mergeConfig, U as projectControlPlaneDefaults, W as readConfigRows, De as resolveTenantChain, ce as runRecordedTenantOperation, bt as setupMultiTenancy, D as validateTenantAccess, Z as withRuntimeFallback, rt as withSystemResourceServerInheritance };
3491
+ export { X as DEFAULT_ROLLOUT_SETTINGS, Sn as DEFAULT_ROLLOUT_WAVES, zt as MAX_MEMBER_REQUEST_ATTEMPTS, Nn as advanceRollout, Pn as advanceRollouts, $n as applyControlPlaneDefaultsPayload, ut as applyResolvedConfigBlock, kt as assertDetachEligible, Cn as assignWaves, Yn as buildControlPlaneDefaultsPayload, re as buildEngineInstanceId, at as buildResolvedConfigBlock, De as compareShadow, fe as computeLegacyConfig, M as createAccessControlHooks, vn as createAccessControlMiddleware, Me as createConfigSyncResolver, _n as createControlPlaneTenantMiddleware, te as createDatabaseHooks, bn as createDatabaseMiddleware, er as createDirectRolloutAdapter, Tn as createExpectedHash, Mt as createInProcessDetach, Ft as createInProcessMemberRequestStore, xt as createInProcessSyncConfig, oe as createInlineExecutor, rr as createMultiTenancy, nr as createMultiTenancyHooks, xn as createMultiTenancyMiddleware, tr as createMultiTenancyPlugin, I as createOperationRecorder, se as createOperationRow, gn as createProtectSyncedMiddleware, Ut as createProvisioningHooks, Kn as createRolloutController, Gn as createRolloutWriteHooks, Ve as createRuntimeFallbackAdapter, Ae as createShadowReporter, yn as createSubdomainMiddleware, Qt as createSyncHooks, an as createTenantsOpenAPIRouter, Tt as detachErrors, St as detachOptionsOf, jt as detachTenantRows, Ee as diffConfig, Ke as effectiveOverride, U as effectiveScope, Ot as emptyDetachResult, Bt as enqueueTenantMemberRequest, ce as enqueueTenantOperation, F as errorToMessage, ct as findUndecryptableKeyIds, Bn as getConfigSyncStatus, qn as initMultiTenant, ae as inlineStepRunner, Hn as isPublishedRow, L as isTerminalStatus, kn as listRolloutOperations, wn as listSyncedDescendants, At as markTenantDetached, It as memberRequestIdOf, Le as mergeClientWithFallback, $e as mergeConfig, Fn as pauseRollout, On as planRollout, ve as projectControlPlaneDefaults, V as readConfigRows, Z as readRolloutConfig, Vn as reconcileConfigDrift, Vt as reconcileTenantMemberRequests, Un as requestRollout, R as resolveTenantChain, In as resumeRollout, Dt as runDetachStep, Pt as runRecordedTenantOperation, bt as runSyncDefaultsStep, Rt as runTenantMembersRequestStep, ir as setupMultiTenancy, Et as summarizeDetachResult, yt as summarizeSyncResult, gt as syncErrors, ht as syncOptionsOf, it as syncedRowId, N as validateTenantAccess, zn as verifyTenantConfig, We as withRuntimeFallback, Be as withSystemResourceServerInheritance };