@authhero/cloudflare-adapter 3.2.5 → 3.3.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.
@@ -1,5 +1,5 @@
1
1
  import { TenantsDataAdapter, TenantOperation } from '@authhero/adapter-interfaces';
2
- import { TenantOperationStores, StepConfig, StepRunner, TenantOperationExecutor } from '@authhero/multi-tenancy';
2
+ import { TenantOperationStores, StepConfig, StepRunner, SyncConfigFn, TenantOperationExecutor } from '@authhero/multi-tenancy';
3
3
 
4
4
  interface CfApiClientOptions {
5
5
  accountId: string;
@@ -133,8 +133,8 @@ interface TenantProvisionerSteps {
133
133
  interface TenantOperationWorkflowParams {
134
134
  operation_id: string;
135
135
  tenant_id: string;
136
- /** Widened beyond "provision" in later phases (upgrade, backup, …). */
137
- kind: "provision";
136
+ /** Widened beyond these in later phases (upgrade, backup, …). */
137
+ kind: "provision" | "sync-defaults";
138
138
  }
139
139
  /**
140
140
  * Instance status as reported by the engine. The exact value set has
@@ -220,6 +220,33 @@ interface ProvisionOperationDeps {
220
220
  */
221
221
  declare function runProvisionOperation(deps: ProvisionOperationDeps, params: TenantOperationWorkflowParams, step: StepRunner): Promise<void>;
222
222
 
223
+ type SyncDefaultsStepName = "mark-running" | "sync-defaults" | "mark-succeeded" | "mark-failed";
224
+ interface SyncDefaultsOperationDeps {
225
+ /** Control-plane operation log stores. */
226
+ stores: TenantOperationStores;
227
+ /**
228
+ * How the tenant's resolved config reaches its database, for example
229
+ * `createDispatchSyncConfig(...)`.
230
+ */
231
+ syncConfig: SyncConfigFn;
232
+ /** Per-step overrides of the retry/timeout defaults. */
233
+ stepConfig?: Partial<Record<SyncDefaultsStepName, StepConfig>>;
234
+ }
235
+ /**
236
+ * Durable `sync-defaults` operation: sync one tenant's resolved parent-chain
237
+ * config into its database as a Workflows instance, writing to the control-plane
238
+ * operation log at every step boundary (the same shape as
239
+ * {@link runProvisionOperation}, and the same step logic as the inline
240
+ * executor). The operation's `input` (`dry_run`, `force`) is read from its row,
241
+ * so nothing but ids crosses the Workflows boundary.
242
+ *
243
+ * A dry run records its diff in the operation's `result` and writes nothing to
244
+ * the tenant. Any failure runs `mark-failed` (event + failed row) and rethrows
245
+ * so the instance ends `errored`; if even that dies, the reconciler copies the
246
+ * engine's terminal state into the row.
247
+ */
248
+ declare function runSyncDefaultsOperation(deps: SyncDefaultsOperationDeps, params: TenantOperationWorkflowParams, step: StepRunner): Promise<void>;
249
+
223
250
  interface ProvisionVerifierOptions {
224
251
  client: CloudflareApiClient;
225
252
  /** Minimum number of signing keys the tenant D1 must hold. Default 1. */
@@ -388,5 +415,5 @@ interface WfpWorkflowProvisioningHookOptions {
388
415
  */
389
416
  declare function createWfpWorkflowProvisioningHook(options: WfpWorkflowProvisioningHookOptions): WfpTenantProvisioningHook;
390
417
 
391
- export { TENANT_OPERATION_ENGINE, TenantProvisionVerificationError, createCloudflareWorkflowsExecutor, createProvisionVerifier, createWfpWorkflowProvisioningHook, reconcileTenantOperations, runProvisionOperation };
392
- export type { CloudflareWorkflowsExecutorOptions, ProvisionOperationDeps, ProvisionStepName, ProvisionVerifierOptions, ReconcileTenantOperationsOptions, ReconcileTenantOperationsResult, TenantOperationWorkflowParams, WfpWorkflowProvisioningHookOptions, WorkflowInstanceHandle, WorkflowInstanceStatus, WorkflowsBinding };
418
+ export { TENANT_OPERATION_ENGINE, TenantProvisionVerificationError, createCloudflareWorkflowsExecutor, createProvisionVerifier, createWfpWorkflowProvisioningHook, reconcileTenantOperations, runProvisionOperation, runSyncDefaultsOperation };
419
+ export type { CloudflareWorkflowsExecutorOptions, ProvisionOperationDeps, ProvisionStepName, ProvisionVerifierOptions, ReconcileTenantOperationsOptions, ReconcileTenantOperationsResult, SyncDefaultsOperationDeps, SyncDefaultsStepName, TenantOperationWorkflowParams, WfpWorkflowProvisioningHookOptions, WorkflowInstanceHandle, WorkflowInstanceStatus, WorkflowsBinding };
@@ -1,14 +1,14 @@
1
- import { r as e, t } from "./sync-defaults-errors-BlLZh6Gt.mjs";
2
- import { buildEngineInstanceId as n, createOperationRecorder as r, isTerminalStatus as i } from "@authhero/multi-tenancy";
1
+ import { r as e, t } from "./sync-defaults-errors-CKdx0nzD.mjs";
2
+ import { buildEngineInstanceId as n, createOperationRecorder as r, isTerminalStatus as i, runSyncDefaultsStep as a } from "@authhero/multi-tenancy";
3
3
  //#region src/workflows/provision-operation.ts
4
- var a = {
4
+ var o = {
5
5
  retries: {
6
6
  limit: 5,
7
7
  delay: "5 seconds",
8
8
  backoff: "exponential"
9
9
  },
10
10
  timeout: "2 minutes"
11
- }, o = {
11
+ }, s = {
12
12
  retries: {
13
13
  limit: 8,
14
14
  delay: "10 seconds",
@@ -16,23 +16,23 @@ var a = {
16
16
  },
17
17
  timeout: "1 minute"
18
18
  };
19
- function s(e) {
19
+ function c(e) {
20
20
  return e instanceof Error ? e.message : String(e);
21
21
  }
22
- async function c(e, n, i) {
23
- let c = r(e.stores), l = e.shouldProvision ?? ((e) => e.deployment_type === "wfp"), u = (t) => e.stepConfig?.[t] ?? (t === "verify" ? o : a), d = n.operation_id, f = n.tenant_id, p = await i.do("mark-running", u("mark-running"), async () => {
22
+ async function l(e, n, i) {
23
+ let a = r(e.stores), l = e.shouldProvision ?? ((e) => e.deployment_type === "wfp"), u = (t) => e.stepConfig?.[t] ?? (t === "verify" ? s : o), d = n.operation_id, f = n.tenant_id, p = await i.do("mark-running", u("mark-running"), async () => {
24
24
  let t = await e.tenants.get(f);
25
- if (!t || !l(t)) return await c.appendEvent(d, {
25
+ if (!t || !l(t)) return await a.appendEvent(d, {
26
26
  step: "mark-running",
27
27
  outcome: "skipped",
28
28
  detail: { reason: t ? "tenant is not WFP-provisioned" : "tenant row missing (rolled back?)" }
29
- }), await c.markSucceeded(d), {
29
+ }), await a.markSucceeded(d), {
30
30
  skipped: !0,
31
31
  scriptName: "",
32
32
  databaseName: ""
33
33
  };
34
34
  let n = e.steps.validate(), r = e.steps.names(f);
35
- return await c.markRunning(d, "mark-running"), await c.appendEvent(d, {
35
+ return await a.markRunning(d, "mark-running"), await a.appendEvent(d, {
36
36
  step: "mark-running",
37
37
  outcome: "succeeded"
38
38
  }), {
@@ -50,9 +50,9 @@ async function c(e, n, i) {
50
50
  };
51
51
  try {
52
52
  let n = await i.do("create-database", u("create-database"), async () => {
53
- await c.setCurrentStep(d, "create-database");
53
+ await a.setCurrentStep(d, "create-database");
54
54
  let { id: t, created: n } = await e.steps.findOrCreateDatabase(p.databaseName);
55
- return await c.appendEvent(d, {
55
+ return await a.appendEvent(d, {
56
56
  step: "create-database",
57
57
  outcome: "succeeded",
58
58
  detail: {
@@ -65,29 +65,29 @@ async function c(e, n, i) {
65
65
  };
66
66
  });
67
67
  if (m.d1_database_id = n.databaseId, await i.do("apply-migrations", u("apply-migrations"), async () => {
68
- await c.setCurrentStep(d, "apply-migrations"), await e.steps.applyMigrations(n.databaseId, n.created), await c.appendEvent(d, {
68
+ await a.setCurrentStep(d, "apply-migrations"), await e.steps.applyMigrations(n.databaseId, n.created), await a.appendEvent(d, {
69
69
  step: "apply-migrations",
70
70
  outcome: "succeeded",
71
71
  detail: { database_version: p.databaseVersion }
72
72
  });
73
73
  }), await i.do("upload-script", u("upload-script"), async () => {
74
- await c.setCurrentStep(d, "upload-script"), await e.steps.uploadScript(p.scriptName, n.databaseId), await c.appendEvent(d, {
74
+ await a.setCurrentStep(d, "upload-script"), await e.steps.uploadScript(p.scriptName, n.databaseId), await a.appendEvent(d, {
75
75
  step: "upload-script",
76
76
  outcome: "succeeded",
77
77
  detail: { worker_version: p.workerVersion }
78
78
  });
79
79
  }), await i.do("upload-secrets", u("upload-secrets"), async () => {
80
- await c.setCurrentStep(d, "upload-secrets"), await e.steps.uploadSecrets(p.scriptName, f), await c.appendEvent(d, {
80
+ await a.setCurrentStep(d, "upload-secrets"), await e.steps.uploadSecrets(p.scriptName, f), await a.appendEvent(d, {
81
81
  step: "upload-secrets",
82
82
  outcome: "succeeded"
83
83
  });
84
84
  }), e.syncDefaults) {
85
85
  let n = e.syncDefaults;
86
86
  await i.do("seed-defaults", u("seed-defaults"), async () => {
87
- await c.setCurrentStep(d, "seed-defaults");
87
+ await a.setCurrentStep(d, "seed-defaults");
88
88
  let e = t(await n(f));
89
89
  if (e.length > 0) throw Error(`sync-defaults seed reported ${e.length} error(s): ${e.join("; ")}`);
90
- await c.appendEvent(d, {
90
+ await a.appendEvent(d, {
91
91
  step: "seed-defaults",
92
92
  outcome: "succeeded"
93
93
  });
@@ -96,26 +96,26 @@ async function c(e, n, i) {
96
96
  if (e.verify) {
97
97
  let t = e.verify;
98
98
  await i.do("verify", u("verify"), async () => {
99
- await c.setCurrentStep(d, "verify"), await t(n.databaseId, f), await c.appendEvent(d, {
99
+ await a.setCurrentStep(d, "verify"), await t(n.databaseId, f), await a.appendEvent(d, {
100
100
  step: "verify",
101
101
  outcome: "succeeded"
102
102
  });
103
103
  });
104
104
  }
105
105
  await i.do("mark-ready", u("mark-ready"), async () => {
106
- await c.setCurrentStep(d, "mark-ready"), await e.tenants.update(f, {
106
+ await a.setCurrentStep(d, "mark-ready"), await e.tenants.update(f, {
107
107
  ...m,
108
108
  provisioning_state: "ready",
109
109
  provisioning_error: void 0,
110
110
  provisioning_state_changed_at: (/* @__PURE__ */ new Date()).toISOString()
111
- }), await c.appendEvent(d, {
111
+ }), await a.appendEvent(d, {
112
112
  step: "mark-ready",
113
113
  outcome: "succeeded"
114
- }), await c.markSucceeded(d);
114
+ }), await a.markSucceeded(d);
115
115
  });
116
116
  } catch (t) {
117
117
  throw await i.do("mark-failed", u("mark-failed"), async () => {
118
- let n = s(t);
118
+ let n = c(t);
119
119
  try {
120
120
  await e.tenants.update(f, {
121
121
  ...m,
@@ -126,23 +126,68 @@ async function c(e, n, i) {
126
126
  } catch (t) {
127
127
  e.logger?.warn(`Failed to write provisioning_state="failed" for tenant ${f}:`, t);
128
128
  }
129
- await c.appendEvent(d, {
129
+ await a.appendEvent(d, {
130
130
  step: "mark-failed",
131
131
  outcome: "failed",
132
132
  detail: { message: n }
133
- }), await c.markFailed(d, t);
133
+ }), await a.markFailed(d, t);
134
134
  }), t;
135
135
  }
136
136
  }
137
137
  //#endregion
138
+ //#region src/workflows/sync-defaults-operation.ts
139
+ var u = {
140
+ retries: {
141
+ limit: 3,
142
+ delay: "5 seconds",
143
+ backoff: "exponential"
144
+ },
145
+ timeout: "2 minutes"
146
+ };
147
+ function d(e) {
148
+ return e instanceof Error ? e.message : String(e);
149
+ }
150
+ async function f(e, t, n) {
151
+ let i = r(e.stores), o = (t) => e.stepConfig?.[t] ?? u, s = t.operation_id;
152
+ try {
153
+ await n.do("mark-running", o("mark-running"), async () => {
154
+ await i.markRunning(s, "sync-defaults");
155
+ }), await n.do("sync-defaults", o("sync-defaults"), async () => {
156
+ await i.setCurrentStep(s, "sync-defaults");
157
+ let t = await e.stores.tenantOperations.get(s);
158
+ if (!t) throw Error(`Operation "${s}" not found`);
159
+ let n = await a({
160
+ operation: t,
161
+ syncConfig: e.syncConfig,
162
+ recorder: i
163
+ });
164
+ await i.appendEvent(s, {
165
+ step: "sync-defaults",
166
+ outcome: "succeeded",
167
+ detail: n
168
+ });
169
+ }), await n.do("mark-succeeded", o("mark-succeeded"), async () => {
170
+ await i.markSucceeded(s);
171
+ });
172
+ } catch (e) {
173
+ throw await n.do("mark-failed", o("mark-failed"), async () => {
174
+ await i.appendEvent(s, {
175
+ step: "mark-failed",
176
+ outcome: "failed",
177
+ detail: { message: d(e) }
178
+ }), await i.markFailed(s, e);
179
+ }), e;
180
+ }
181
+ }
182
+ //#endregion
138
183
  //#region src/workflows/verify.ts
139
- var l = class extends Error {
184
+ var p = class extends Error {
140
185
  checks;
141
186
  constructor(e, t) {
142
187
  super(e), this.name = "TenantProvisionVerificationError", this.checks = t;
143
188
  }
144
189
  };
145
- function u(e, t) {
190
+ function m(e, t) {
146
191
  for (let n of e) for (let e of n.results ?? []) if (!(!e || typeof e != "object")) {
147
192
  for (let [n, r] of Object.entries(e)) if (n === t) {
148
193
  if (typeof r == "number") return r;
@@ -151,11 +196,11 @@ function u(e, t) {
151
196
  }
152
197
  return 0;
153
198
  }
154
- function d(t) {
199
+ function h(t) {
155
200
  let n = t.minKeys ?? 1;
156
201
  return async (r, i) => {
157
- let a = await t.client.execD1(r, `SELECT (SELECT COUNT(*) FROM keys) AS key_count, (SELECT COUNT(*) FROM tenants WHERE id = '${e(i)}') AS tenant_count;`), o = u(a, "key_count"), s = u(a, "tenant_count"), c = [];
158
- if (o < n && c.push(`expected at least ${n} signing key(s) in "keys", found ${o}`), s < 1 && c.push(`tenant row "${i}" missing from "tenants"`), c.length > 0) throw new l(`Tenant D1 verification failed for "${i}": ${c.join("; ")}`, {
202
+ let a = await t.client.execD1(r, `SELECT (SELECT COUNT(*) FROM keys) AS key_count, (SELECT COUNT(*) FROM tenants WHERE id = '${e(i)}') AS tenant_count;`), o = m(a, "key_count"), s = m(a, "tenant_count"), c = [];
203
+ if (o < n && c.push(`expected at least ${n} signing key(s) in "keys", found ${o}`), s < 1 && c.push(`tenant row "${i}" missing from "tenants"`), c.length > 0) throw new p(`Tenant D1 verification failed for "${i}": ${c.join("; ")}`, {
159
204
  keyCount: o,
160
205
  tenantRowCount: s
161
206
  });
@@ -163,16 +208,16 @@ function d(t) {
163
208
  }
164
209
  //#endregion
165
210
  //#region src/workflows/executor.ts
166
- function f(e) {
211
+ function g(e) {
167
212
  let t = e instanceof Error ? e.message : String(e ?? "");
168
213
  return /already exists|already been created|duplicate/i.test(t);
169
214
  }
170
- function p(e) {
215
+ function _(e) {
171
216
  return {
172
217
  engine: "cloudflare-workflows",
173
218
  async start(t) {
174
- if (t.kind !== "provision") throw Error(`The Cloudflare Workflows executor does not support "${t.kind}" operations yet (phase 2 covers provision only)`);
175
- if (!t.tenant_id) throw Error("provision operations require a tenant_id");
219
+ if (t.kind !== "provision" && t.kind !== "sync-defaults") throw Error(`The Cloudflare Workflows executor does not support "${t.kind}" operations yet (it runs provision and sync-defaults)`);
220
+ if (!t.tenant_id) throw Error(`${t.kind} operations require a tenant_id`);
176
221
  let r = t.engine_instance_id ?? n(t);
177
222
  try {
178
223
  await e.binding.create({
@@ -184,20 +229,20 @@ function p(e) {
184
229
  }
185
230
  });
186
231
  } catch (e) {
187
- if (!f(e)) throw e;
232
+ if (!g(e)) throw e;
188
233
  }
189
234
  }
190
235
  };
191
236
  }
192
237
  //#endregion
193
238
  //#region src/workflows/types.ts
194
- var m = "cloudflare-workflows";
239
+ var v = "cloudflare-workflows";
195
240
  //#endregion
196
241
  //#region src/workflows/reconcile.ts
197
- async function h(e) {
242
+ async function y(e) {
198
243
  let { stores: t, tenants: a, binding: o } = e, s = r(t), c = e.minAgeMs ?? 600 * 1e3, l = e.limit ?? 100, u = new Date(Date.now() - c).toISOString(), { operations: d } = await t.tenantOperations.list({
199
244
  status: ["pending", "running"],
200
- engine: m,
245
+ engine: v,
201
246
  updated_before: u,
202
247
  per_page: l
203
248
  }), f = {
@@ -209,7 +254,7 @@ async function h(e) {
209
254
  errors: 0
210
255
  };
211
256
  for (let t of d) try {
212
- await h(t);
257
+ await m(t);
213
258
  } catch (n) {
214
259
  f.errors++, e.logger?.warn(`Failed to reconcile tenant operation ${t.id}:`, n);
215
260
  }
@@ -228,7 +273,7 @@ async function h(e) {
228
273
  provisioning_state_changed_at: (/* @__PURE__ */ new Date()).toISOString()
229
274
  });
230
275
  }
231
- async function h(e) {
276
+ async function m(e) {
232
277
  let r = await t.tenantOperations.get(e.id);
233
278
  if (!r || i(r.status)) return;
234
279
  let c = r.engine_instance_id ?? n(r), l;
@@ -245,6 +290,14 @@ async function h(e) {
245
290
  return;
246
291
  }
247
292
  if (u.status === "complete") {
293
+ if (r.kind === "sync-defaults") {
294
+ f.markedSucceeded++, await s.appendEvent(r.id, {
295
+ step: r.current_step ?? "reconcile",
296
+ outcome: "reconciled",
297
+ detail: { resolution: "succeeded" }
298
+ }), await s.markSucceeded(r.id);
299
+ return;
300
+ }
248
301
  (r.tenant_id ? await a.get(r.tenant_id) : null)?.provisioning_state === "ready" ? (f.markedSucceeded++, await s.appendEvent(r.id, {
249
302
  step: r.current_step ?? "reconcile",
250
303
  outcome: "reconciled",
@@ -257,7 +310,7 @@ async function h(e) {
257
310
  }
258
311
  //#endregion
259
312
  //#region src/workflows/enqueue-hook.ts
260
- function g(e) {
313
+ function b(e) {
261
314
  let t = e.shouldProvision ?? ((e) => e.deployment_type === "wfp");
262
315
  return {
263
316
  async onProvision(n, r) {
@@ -276,4 +329,4 @@ function g(e) {
276
329
  };
277
330
  }
278
331
  //#endregion
279
- export { m as TENANT_OPERATION_ENGINE, l as TenantProvisionVerificationError, p as createCloudflareWorkflowsExecutor, d as createProvisionVerifier, g as createWfpWorkflowProvisioningHook, h as reconcileTenantOperations, c as runProvisionOperation };
332
+ export { v as TENANT_OPERATION_ENGINE, p as TenantProvisionVerificationError, _ as createCloudflareWorkflowsExecutor, h as createProvisionVerifier, b as createWfpWorkflowProvisioningHook, y as reconcileTenantOperations, l as runProvisionOperation, f as runSyncDefaultsOperation };
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "type": "git",
12
12
  "url": "https://github.com/sesamyab/authhero"
13
13
  },
14
- "version": "3.2.5",
14
+ "version": "3.3.0",
15
15
  "files": [
16
16
  "dist"
17
17
  ],
@@ -47,8 +47,8 @@
47
47
  "typescript": "^5.9.3",
48
48
  "vite": "^8.0.14",
49
49
  "vitest": "^4.1.7",
50
- "@authhero/multi-tenancy": "15.2.0",
51
- "authhero": "9.25.0"
50
+ "@authhero/multi-tenancy": "15.3.0",
51
+ "authhero": "9.26.0"
52
52
  },
53
53
  "peerDependencies": {
54
54
  "@authhero/multi-tenancy": "^14.0.0",
@@ -67,8 +67,8 @@
67
67
  "dependencies": {
68
68
  "nanoid": "^5.1.11",
69
69
  "wretch": "^3.0.8",
70
- "@authhero/adapter-interfaces": "4.22.0",
71
- "@authhero/kysely-adapter": "12.16.0"
70
+ "@authhero/kysely-adapter": "12.17.0",
71
+ "@authhero/adapter-interfaces": "4.23.0"
72
72
  },
73
73
  "license": "AGPL-3.0-only",
74
74
  "scripts": {