@authhero/cloudflare-adapter 3.3.0 → 3.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.
- package/dist/tsconfig.types.tsbuildinfo +1 -1
- package/dist/types/wfp/dispatch-detach.d.ts +31 -0
- package/dist/types/wfp/dispatch-tenant-member-requests.d.ts +23 -0
- package/dist/types/wfp/index.d.ts +2 -0
- package/dist/types/wfp/tenant-app.d.ts +17 -1
- package/dist/types/workflows/detach-operation.d.ts +31 -0
- package/dist/types/workflows/entrypoint.example.d.ts +23 -0
- package/dist/types/workflows/index.d.ts +1 -0
- package/dist/types/workflows/types.d.ts +1 -1
- package/dist/wfp.cjs +1 -1
- package/dist/wfp.d.ts +70 -3
- package/dist/wfp.mjs +251 -69
- package/dist/workflows.cjs +1 -1
- package/dist/workflows.d.ts +34 -4
- package/dist/workflows.mjs +82 -31
- package/package.json +5 -5
package/dist/wfp.mjs
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { applyControlPlaneDefaultsPayload as e, applyResolvedConfigBlock as t,
|
|
2
|
-
import { Hono as
|
|
3
|
-
import { SigningCertificateRenewalError as
|
|
1
|
+
import { applyControlPlaneDefaultsPayload as e, applyResolvedConfigBlock as t, assertDetachEligible as n, buildControlPlaneDefaultsPayload as r, buildResolvedConfigBlock as i, createShadowReporter as a, detachTenantRows as o, emptyDetachResult as s, markTenantDetached as c, withRuntimeFallback as l } from "@authhero/multi-tenancy";
|
|
2
|
+
import { Hono as u } from "hono";
|
|
3
|
+
import { SigningCertificateRenewalError as d, TENANT_URI_PREFIX as f, createEncryptedDataAdapterWithKeyRing as p, ensureSigningKey as m, init as h, loadEncryptionKey as g, renewSigningCertificates as _ } from "authhero";
|
|
4
4
|
//#region src/wfp/dispatch-sync-defaults.ts
|
|
5
|
-
var
|
|
6
|
-
function
|
|
5
|
+
var v = "tenant-{tenant_id}-auth", y = 3e4, b = "/internal/sync-defaults";
|
|
6
|
+
function x(e, t) {
|
|
7
7
|
return e.replace(/\{tenant_id\}/g, t);
|
|
8
8
|
}
|
|
9
|
-
function
|
|
10
|
-
let { dispatcher: t, scriptNameTemplate:
|
|
9
|
+
function S(e) {
|
|
10
|
+
let { dispatcher: t, scriptNameTemplate: n = v, internalSecret: i, controlPlaneTenantId: a, controlPlaneAdapters: o, entities: s, timeoutMs: c = y } = e;
|
|
11
11
|
return async (e) => {
|
|
12
|
-
let l = await
|
|
12
|
+
let l = await r(o, a, s, e), u = x(n, e), d = new AbortController(), f = setTimeout(() => d.abort(), c), p;
|
|
13
13
|
try {
|
|
14
|
-
p = await t.get(u).fetch(`https://tenant.internal${
|
|
14
|
+
p = await t.get(u).fetch(`https://tenant.internal${b}`, {
|
|
15
15
|
method: "POST",
|
|
16
16
|
headers: {
|
|
17
17
|
"content-type": "application/json",
|
|
@@ -32,16 +32,54 @@ function _(e) {
|
|
|
32
32
|
}
|
|
33
33
|
//#endregion
|
|
34
34
|
//#region src/wfp/dispatch-sync-config.ts
|
|
35
|
-
var
|
|
36
|
-
function
|
|
37
|
-
let { dispatcher: t, scriptNameTemplate: n =
|
|
38
|
-
return async (e, { dryRun: l, force: u }) => {
|
|
39
|
-
|
|
35
|
+
var C = "/internal/sync-config";
|
|
36
|
+
function w(e) {
|
|
37
|
+
let { dispatcher: t, scriptNameTemplate: n = v, internalSecret: r, controlPlaneTenantId: a, controlPlaneAdapters: o, maxDepth: s, timeoutMs: c = y } = e;
|
|
38
|
+
return async (e, { dryRun: l, force: u, prune: d }) => {
|
|
39
|
+
if ((await o.tenants.get(e))?.config_sync === "detached") throw Error(`Tenant "${e}" is detached from its parent: set config_sync back to "inherit-on-read" or "synced" before syncing it`);
|
|
40
|
+
let f = await i(o, e, {
|
|
40
41
|
legacyControlPlane: a,
|
|
41
42
|
maxDepth: s
|
|
42
|
-
}),
|
|
43
|
+
}), p = x(n, e), m = new AbortController(), h = setTimeout(() => m.abort(), c), g;
|
|
43
44
|
try {
|
|
44
|
-
|
|
45
|
+
g = await t.get(p).fetch(`https://tenant.internal${C}`, {
|
|
46
|
+
method: "POST",
|
|
47
|
+
headers: {
|
|
48
|
+
"content-type": "application/json",
|
|
49
|
+
authorization: `Bearer ${r}`
|
|
50
|
+
},
|
|
51
|
+
body: JSON.stringify({
|
|
52
|
+
tenantId: e,
|
|
53
|
+
resolved: f,
|
|
54
|
+
dryRun: l,
|
|
55
|
+
force: u,
|
|
56
|
+
prune: d
|
|
57
|
+
}),
|
|
58
|
+
signal: m.signal
|
|
59
|
+
});
|
|
60
|
+
} finally {
|
|
61
|
+
clearTimeout(h);
|
|
62
|
+
}
|
|
63
|
+
if (!g.ok) {
|
|
64
|
+
let e = await g.text().catch(() => ""), t = g.headers.get("x-authhero-error");
|
|
65
|
+
throw Error(`sync-config push to "${p}" failed: ${g.status}${t ? ` (${t})` : ""} ${e.slice(0, 256)}`);
|
|
66
|
+
}
|
|
67
|
+
return await g.json();
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//#endregion
|
|
71
|
+
//#region src/wfp/dispatch-detach.ts
|
|
72
|
+
var T = "/internal/detach", E = 12e4;
|
|
73
|
+
function D(e) {
|
|
74
|
+
let { dispatcher: t, scriptNameTemplate: r = v, internalSecret: i, controlPlaneAdapters: a, timeoutMs: o = E } = e;
|
|
75
|
+
return async (e, { dryRun: l, clean: u }) => {
|
|
76
|
+
let d = await a.tenants.get(e);
|
|
77
|
+
if (!d) throw Error(`Tenant "${e}" not found`);
|
|
78
|
+
if (d.config_sync === "detached") return s("already-detached");
|
|
79
|
+
n(e, d);
|
|
80
|
+
let f = x(r, e), p = new AbortController(), m = setTimeout(() => p.abort(), o), h;
|
|
81
|
+
try {
|
|
82
|
+
h = await t.get(f).fetch(`https://tenant.internal${T}`, {
|
|
45
83
|
method: "POST",
|
|
46
84
|
headers: {
|
|
47
85
|
"content-type": "application/json",
|
|
@@ -49,9 +87,8 @@ function y(e) {
|
|
|
49
87
|
},
|
|
50
88
|
body: JSON.stringify({
|
|
51
89
|
tenantId: e,
|
|
52
|
-
resolved: d,
|
|
53
90
|
dryRun: l,
|
|
54
|
-
|
|
91
|
+
clean: u
|
|
55
92
|
}),
|
|
56
93
|
signal: p.signal
|
|
57
94
|
});
|
|
@@ -60,20 +97,78 @@ function y(e) {
|
|
|
60
97
|
}
|
|
61
98
|
if (!h.ok) {
|
|
62
99
|
let e = await h.text().catch(() => ""), t = h.headers.get("x-authhero-error");
|
|
63
|
-
throw Error(`
|
|
100
|
+
throw Error(`detach push to "${f}" failed: ${h.status}${t ? ` (${t})` : ""} ${e.slice(0, 256)}`);
|
|
101
|
+
}
|
|
102
|
+
let g = await h.json();
|
|
103
|
+
return !l && g.status === "detached" && await c(a, e), g;
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
//#endregion
|
|
107
|
+
//#region src/wfp/dispatch-tenant-member-requests.ts
|
|
108
|
+
var O = "/internal/tenant-member-requests", k = 15e3;
|
|
109
|
+
function A(e) {
|
|
110
|
+
let { dispatcher: t, scriptNameTemplate: n = v, internalSecret: r, timeoutMs: i = k } = e;
|
|
111
|
+
async function a(e, a) {
|
|
112
|
+
let o = x(n, e), s = new AbortController(), c = setTimeout(() => s.abort(), i);
|
|
113
|
+
try {
|
|
114
|
+
return await t.get(o).fetch(`https://tenant.internal${O}`, {
|
|
115
|
+
method: "POST",
|
|
116
|
+
headers: {
|
|
117
|
+
"content-type": "application/json",
|
|
118
|
+
authorization: `Bearer ${r}`
|
|
119
|
+
},
|
|
120
|
+
body: JSON.stringify({
|
|
121
|
+
tenantId: e,
|
|
122
|
+
...a
|
|
123
|
+
}),
|
|
124
|
+
signal: s.signal
|
|
125
|
+
});
|
|
126
|
+
} finally {
|
|
127
|
+
clearTimeout(c);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async function o(e, t) {
|
|
131
|
+
let n = await t.text().catch(() => ""), r = t.headers.get("x-authhero-error");
|
|
132
|
+
return /* @__PURE__ */ Error(`tenant member requests of "${e}" answered ${t.status}${r ? ` (${r})` : ""} ${n.slice(0, 256)}`);
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
async get(e, t) {
|
|
136
|
+
let n = await a(e, {
|
|
137
|
+
action: "get",
|
|
138
|
+
id: t
|
|
139
|
+
});
|
|
140
|
+
if (n.status === 404) return null;
|
|
141
|
+
if (!n.ok) throw await o(e, n);
|
|
142
|
+
return await n.json();
|
|
143
|
+
},
|
|
144
|
+
async report(e, t, n) {
|
|
145
|
+
let r = await a(e, {
|
|
146
|
+
action: "report",
|
|
147
|
+
id: t,
|
|
148
|
+
outcome: n
|
|
149
|
+
});
|
|
150
|
+
if (!r.ok) throw await o(e, r);
|
|
151
|
+
return (await r.json()).updated;
|
|
152
|
+
},
|
|
153
|
+
async listPending(e, { olderThan: t }) {
|
|
154
|
+
let n = await a(e, {
|
|
155
|
+
action: "list_pending",
|
|
156
|
+
olderThan: t
|
|
157
|
+
});
|
|
158
|
+
if (!n.ok) throw await o(e, n);
|
|
159
|
+
return (await n.json()).requests;
|
|
64
160
|
}
|
|
65
|
-
return await h.json();
|
|
66
161
|
};
|
|
67
162
|
}
|
|
68
163
|
//#endregion
|
|
69
164
|
//#region src/wfp/dispatch-renew-signing-certificates.ts
|
|
70
|
-
var
|
|
71
|
-
function
|
|
72
|
-
let { dispatcher: t, scriptNameTemplate: n =
|
|
165
|
+
var j = "/internal/renew-signing-certificates";
|
|
166
|
+
function M(e) {
|
|
167
|
+
let { dispatcher: t, scriptNameTemplate: n = v, internalSecret: r, timeoutMs: i = y } = e;
|
|
73
168
|
return async (e) => {
|
|
74
|
-
let a =
|
|
169
|
+
let a = x(n, e), o = new AbortController(), s = setTimeout(() => o.abort(), i);
|
|
75
170
|
try {
|
|
76
|
-
let e = await t.get(a).fetch(`https://tenant.internal${
|
|
171
|
+
let e = await t.get(a).fetch(`https://tenant.internal${j}`, {
|
|
77
172
|
method: "POST",
|
|
78
173
|
headers: { authorization: `Bearer ${r}` },
|
|
79
174
|
signal: o.signal
|
|
@@ -83,7 +178,7 @@ function x(e) {
|
|
|
83
178
|
throw Error(`signing-certificate renewal for "${a}" failed: ${e.status}${n ? ` (${n})` : ""} ${t.slice(0, 512)}`);
|
|
84
179
|
}
|
|
85
180
|
let n = await e.json();
|
|
86
|
-
if (!
|
|
181
|
+
if (!N(n)) throw Error(`signing-certificate renewal for "${a}" returned an unexpected body`);
|
|
87
182
|
return {
|
|
88
183
|
renewed: n.renewed,
|
|
89
184
|
notDue: n.notDue
|
|
@@ -93,13 +188,13 @@ function x(e) {
|
|
|
93
188
|
}
|
|
94
189
|
};
|
|
95
190
|
}
|
|
96
|
-
function
|
|
191
|
+
function N(e) {
|
|
97
192
|
return typeof e == "object" && !!e && "renewed" in e && "notDue" in e && Array.isArray(e.renewed) && e.renewed.every((e) => typeof e == "string") && typeof e.notDue == "number";
|
|
98
193
|
}
|
|
99
194
|
//#endregion
|
|
100
195
|
//#region src/wfp/tenant-app.ts
|
|
101
|
-
var
|
|
102
|
-
function
|
|
196
|
+
var P = "cp", F = "/internal/sync-defaults", I = "/internal/sync-config", L = "/internal/detach", R = "/internal/tenant-member-requests", z = "/internal/renew-signing-certificates";
|
|
197
|
+
function B(e, t, n, r) {
|
|
103
198
|
console.error(`[wfp-tenant] ${t}:`, n);
|
|
104
199
|
let i = n instanceof Error ? n.message : String(n);
|
|
105
200
|
return e.header("X-Authhero-Error", t), e.json({
|
|
@@ -108,60 +203,60 @@ function D(e, t, n, r) {
|
|
|
108
203
|
result: r
|
|
109
204
|
}, 500);
|
|
110
205
|
}
|
|
111
|
-
function
|
|
112
|
-
let t = `${
|
|
206
|
+
function V(e) {
|
|
207
|
+
let t = `${f}${e.tenantId}`;
|
|
113
208
|
return {
|
|
114
209
|
issuer: e.issuer,
|
|
115
210
|
validityDays: e.validityDays,
|
|
116
211
|
subjectUri: () => t
|
|
117
212
|
};
|
|
118
213
|
}
|
|
119
|
-
function
|
|
214
|
+
function H(e) {
|
|
120
215
|
return ({ tenant_id: t }) => t === e.CONTROL_PLANE_TENANT_ID ? [e.CONTROL_PLANE_ISSUER] : [];
|
|
121
216
|
}
|
|
122
|
-
async function
|
|
123
|
-
let
|
|
124
|
-
default:
|
|
125
|
-
keys: { [
|
|
126
|
-
} : { default:
|
|
127
|
-
dataAdapter:
|
|
128
|
-
...
|
|
129
|
-
additionalIssuers: (r.additionalIssuers ??
|
|
130
|
-
},
|
|
131
|
-
|
|
132
|
-
let
|
|
133
|
-
if (!
|
|
134
|
-
let
|
|
217
|
+
async function U(n, r) {
|
|
218
|
+
let i = n.CONTROL_PLANE_TENANT_ID, s = await g(n.ENCRYPTION_KEY), f = n.CONTROL_PLANE_ENCRYPTION_KEY ? {
|
|
219
|
+
default: s,
|
|
220
|
+
keys: { [P]: await g(n.CONTROL_PLANE_ENCRYPTION_KEY) }
|
|
221
|
+
} : { default: s }, v = r.createDataAdapter(n), y = p(v, f, { resolveEncryptKeyId: (e) => e === i ? P : void 0 }), b = l(y, { controlPlaneTenantId: i }), x = r.signingCertificateAuthority?.(n), S = x ? V(x) : void 0, C = {
|
|
222
|
+
dataAdapter: b,
|
|
223
|
+
...S ? { signingCertificateAuthority: S } : {},
|
|
224
|
+
additionalIssuers: (r.additionalIssuers ?? H)(n)
|
|
225
|
+
}, w = r.configure ? r.configure(C, n) : C, T = w.signingCertificateAuthority, { app: E } = h(w), D = new u();
|
|
226
|
+
if (D.post(F, async (t) => {
|
|
227
|
+
let a = n.WFP_INTERNAL_SYNC_SECRET, o = t.req.header("authorization");
|
|
228
|
+
if (!a || o !== `Bearer ${a}`) return t.json({ error: "unauthorized" }, 401);
|
|
229
|
+
let s;
|
|
135
230
|
try {
|
|
136
|
-
|
|
231
|
+
s = await t.req.json();
|
|
137
232
|
} catch {
|
|
138
233
|
return t.json({ error: "invalid JSON" }, 400);
|
|
139
234
|
}
|
|
140
235
|
let c;
|
|
141
236
|
try {
|
|
142
|
-
c = await e(
|
|
237
|
+
c = await e(s, y, i, { continueOnError: r.continueOnError ?? !0 });
|
|
143
238
|
} catch (e) {
|
|
144
|
-
return
|
|
239
|
+
return B(t, "sync_defaults_apply_failed", e);
|
|
145
240
|
}
|
|
146
|
-
let
|
|
241
|
+
let l;
|
|
147
242
|
try {
|
|
148
|
-
|
|
243
|
+
l = { created: (await m(y.keys, {
|
|
149
244
|
name: n.ISSUER,
|
|
150
|
-
certificateAuthority:
|
|
245
|
+
certificateAuthority: T
|
|
151
246
|
})).created };
|
|
152
247
|
} catch (e) {
|
|
153
|
-
return
|
|
248
|
+
return B(t, "sync_defaults_signing_key_failed", e, c);
|
|
154
249
|
}
|
|
155
250
|
if (r.onSyncResult) try {
|
|
156
251
|
await r.onSyncResult(c, n);
|
|
157
252
|
} catch (e) {
|
|
158
|
-
return
|
|
253
|
+
return B(t, "sync_defaults_on_result_failed", e, c);
|
|
159
254
|
}
|
|
160
255
|
return t.json({
|
|
161
256
|
...c,
|
|
162
|
-
signingKey:
|
|
257
|
+
signingKey: l
|
|
163
258
|
});
|
|
164
|
-
}),
|
|
259
|
+
}), D.post(I, async (e) => {
|
|
165
260
|
let i = n.WFP_INTERNAL_SYNC_SECRET, a = e.req.header("authorization");
|
|
166
261
|
if (!i || a !== `Bearer ${i}`) return e.json({ error: "unauthorized" }, 401);
|
|
167
262
|
let o;
|
|
@@ -173,10 +268,11 @@ async function A(n, r) {
|
|
|
173
268
|
if (!o.tenantId || !o.resolved) return e.json({ error: "tenantId and resolved are required" }, 400);
|
|
174
269
|
if (n.TENANT_ID && o.tenantId !== n.TENANT_ID) return console.error(`[wfp-tenant] sync_config_tenant_mismatch: push for "${o.tenantId}" reached the worker of "${n.TENANT_ID}"`), e.header("X-Authhero-Error", "sync_config_tenant_mismatch"), e.json({ error: "sync_config_tenant_mismatch" }, 403);
|
|
175
270
|
try {
|
|
176
|
-
let n = await t(o.resolved,
|
|
271
|
+
let n = await t(o.resolved, v, o.tenantId, {
|
|
177
272
|
dryRun: o.dryRun === !0,
|
|
178
273
|
force: o.force === !0,
|
|
179
|
-
|
|
274
|
+
prune: o.prune === !0,
|
|
275
|
+
keyRing: f,
|
|
180
276
|
continueOnError: r.continueOnError ?? !0
|
|
181
277
|
});
|
|
182
278
|
return e.json(n);
|
|
@@ -184,23 +280,87 @@ async function A(n, r) {
|
|
|
184
280
|
return t instanceof Error && /key ring cannot decrypt/.test(t.message) ? (console.error("[wfp-tenant] sync_config_key_ring:", t), e.header("X-Authhero-Error", "sync_config_key_ring"), e.json({
|
|
185
281
|
error: "sync_config_key_ring",
|
|
186
282
|
detail: t.message
|
|
187
|
-
}, 422)) :
|
|
283
|
+
}, 422)) : B(e, "sync_config_apply_failed", t);
|
|
284
|
+
}
|
|
285
|
+
}), D.post(L, async (e) => {
|
|
286
|
+
let t = n.WFP_INTERNAL_SYNC_SECRET, r = e.req.header("authorization");
|
|
287
|
+
if (!t || r !== `Bearer ${t}`) return e.json({ error: "unauthorized" }, 401);
|
|
288
|
+
let i;
|
|
289
|
+
try {
|
|
290
|
+
i = await e.req.json();
|
|
291
|
+
} catch {
|
|
292
|
+
return e.json({ error: "invalid JSON" }, 400);
|
|
293
|
+
}
|
|
294
|
+
if (!i.tenantId) return e.json({ error: "tenantId is required" }, 400);
|
|
295
|
+
if (n.TENANT_ID && i.tenantId !== n.TENANT_ID) return console.error(`[wfp-tenant] detach_tenant_mismatch: detach for "${i.tenantId}" reached the worker of "${n.TENANT_ID}"`), e.header("X-Authhero-Error", "detach_tenant_mismatch"), e.json({ error: "detach_tenant_mismatch" }, 403);
|
|
296
|
+
try {
|
|
297
|
+
let t = await o(v, i.tenantId, {
|
|
298
|
+
dryRun: i.dryRun === !0,
|
|
299
|
+
clean: i.clean === !0
|
|
300
|
+
});
|
|
301
|
+
return t.status === "detached" && await c(v, i.tenantId), e.json(t);
|
|
302
|
+
} catch (t) {
|
|
303
|
+
return B(e, "detach_failed", t);
|
|
188
304
|
}
|
|
189
|
-
}),
|
|
305
|
+
}), D.post(R, async (e) => {
|
|
190
306
|
let t = n.WFP_INTERNAL_SYNC_SECRET, r = e.req.header("authorization");
|
|
191
307
|
if (!t || r !== `Bearer ${t}`) return e.json({ error: "unauthorized" }, 401);
|
|
192
|
-
|
|
308
|
+
let i;
|
|
309
|
+
try {
|
|
310
|
+
i = await e.req.json();
|
|
311
|
+
} catch {
|
|
312
|
+
return e.json({ error: "invalid JSON" }, 400);
|
|
313
|
+
}
|
|
314
|
+
if (!i.tenantId) return e.json({ error: "tenantId is required" }, 400);
|
|
315
|
+
if (n.TENANT_ID && i.tenantId !== n.TENANT_ID) return console.error(`[wfp-tenant] member_requests_tenant_mismatch: a request for "${i.tenantId}" reached the worker of "${n.TENANT_ID}"`), e.header("X-Authhero-Error", "member_requests_tenant_mismatch"), e.json({ error: "member_requests_tenant_mismatch" }, 403);
|
|
316
|
+
let a = v.tenantMemberRequests;
|
|
317
|
+
if (!a) return e.json({ error: "tenant member requests are not available" }, 501);
|
|
193
318
|
try {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
319
|
+
switch (i.action) {
|
|
320
|
+
case "get": {
|
|
321
|
+
if (!i.id) return e.json({ error: "id is required" }, 400);
|
|
322
|
+
let t = await a.get(i.tenantId, i.id);
|
|
323
|
+
return t ? e.json(t) : e.json({ error: "not_found" }, 404);
|
|
324
|
+
}
|
|
325
|
+
case "report": {
|
|
326
|
+
let { status: t, result: n, error: r } = i.outcome ?? {};
|
|
327
|
+
if (!i.id || t !== "succeeded" && t !== "failed") return e.json({ error: "id and a final status are required" }, 400);
|
|
328
|
+
let o = await a.update(i.tenantId, i.id, {
|
|
329
|
+
status: t,
|
|
330
|
+
result: n ?? null,
|
|
331
|
+
error: r ?? null,
|
|
332
|
+
finished_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
333
|
+
}, "pending");
|
|
334
|
+
return e.json({ updated: o });
|
|
335
|
+
}
|
|
336
|
+
case "list_pending": {
|
|
337
|
+
let t = await a.list(i.tenantId, {
|
|
338
|
+
status: "pending",
|
|
339
|
+
created_before: i.olderThan,
|
|
340
|
+
per_page: 100
|
|
341
|
+
});
|
|
342
|
+
return e.json({ requests: t.requests });
|
|
343
|
+
}
|
|
344
|
+
default: return e.json({ error: "unknown action" }, 400);
|
|
345
|
+
}
|
|
346
|
+
} catch (t) {
|
|
347
|
+
return B(e, "member_requests_failed", t);
|
|
348
|
+
}
|
|
349
|
+
}), D.post(z, async (e) => {
|
|
350
|
+
let t = n.WFP_INTERNAL_SYNC_SECRET, r = e.req.header("authorization");
|
|
351
|
+
if (!t || r !== `Bearer ${t}`) return e.json({ error: "unauthorized" }, 401);
|
|
352
|
+
if (!T) return e.json({ error: "no signing certificate authority" }, 404);
|
|
353
|
+
try {
|
|
354
|
+
let t = await _({
|
|
355
|
+
dataAdapter: { keys: y.keys },
|
|
356
|
+
certificateAuthority: T
|
|
197
357
|
});
|
|
198
358
|
return e.json({
|
|
199
359
|
renewed: t.renewed,
|
|
200
360
|
notDue: t.notDue
|
|
201
361
|
});
|
|
202
362
|
} catch (t) {
|
|
203
|
-
return t instanceof
|
|
363
|
+
return t instanceof d ? (console.error("[wfp-tenant] signing_certificate_renewal_failed:", t), e.header("X-Authhero-Error", "signing_certificate_renewal_failed"), e.json({
|
|
204
364
|
error: "signing_certificate_renewal_failed",
|
|
205
365
|
renewed: t.result.renewed,
|
|
206
366
|
notDue: t.result.notDue,
|
|
@@ -208,16 +368,38 @@ async function A(n, r) {
|
|
|
208
368
|
kid: e,
|
|
209
369
|
detail: t instanceof Error ? t.message : String(t)
|
|
210
370
|
}))
|
|
211
|
-
}, 500)) :
|
|
371
|
+
}, 500)) : B(e, "signing_certificate_renewal_failed", t);
|
|
212
372
|
}
|
|
213
|
-
}),
|
|
373
|
+
}), D.route("/", E), r.inheritanceShadow) {
|
|
374
|
+
let e = a(y, r.inheritanceShadow === !0 ? {} : r.inheritanceShadow), t = n.TENANT_ID || new URL(n.ISSUER).hostname.split(".")[0], o, s = async () => {
|
|
375
|
+
let e = await y.tenants.get(t);
|
|
376
|
+
for (let t = 0; t < 5; t++) {
|
|
377
|
+
let t = e?.parent_tenant_id;
|
|
378
|
+
if (!t || t === i) return !0;
|
|
379
|
+
if (e = await y.tenants.get(t), !e) return !1;
|
|
380
|
+
}
|
|
381
|
+
return !1;
|
|
382
|
+
};
|
|
383
|
+
W.set(D, (n) => {
|
|
384
|
+
if (!new URL(n.url).pathname.startsWith("/internal/") && t !== i) return (async () => {
|
|
385
|
+
if (!await (o ??= s())) return;
|
|
386
|
+
let n = e.schedule(t, i);
|
|
387
|
+
n && await n;
|
|
388
|
+
})();
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
return D;
|
|
214
392
|
}
|
|
215
|
-
|
|
393
|
+
var W = /* @__PURE__ */ new WeakMap();
|
|
394
|
+
function G(e) {
|
|
216
395
|
let t = /* @__PURE__ */ new WeakMap();
|
|
217
396
|
return { fetch(n, r, i) {
|
|
218
397
|
let a = t.get(r);
|
|
219
|
-
return a || (a =
|
|
398
|
+
return a || (a = U(r, e), t.set(r, a)), a.then(async (e) => {
|
|
399
|
+
let t = await e.fetch(n, r, i), a = W.get(e)?.(n);
|
|
400
|
+
return a && i.waitUntil(a), t;
|
|
401
|
+
});
|
|
220
402
|
} };
|
|
221
403
|
}
|
|
222
404
|
//#endregion
|
|
223
|
-
export {
|
|
405
|
+
export { O as TENANT_MEMBER_REQUESTS_PATH, D as createDispatchDetach, M as createDispatchRenewSigningCertificates, w as createDispatchSyncConfig, S as createDispatchSyncDefaults, A as createDispatchTenantMemberRequestStore, G as createWfpTenantApp };
|
package/dist/workflows.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./sync-defaults-errors-pnzkP88r.js");let t=require("@authhero/multi-tenancy");var n={retries:{limit:5,delay:`5 seconds`,backoff:`exponential`},timeout:`2 minutes`},r={retries:{limit:8,delay:`10 seconds`,backoff:`exponential`},timeout:`1 minute`};function i(e){return e instanceof Error?e.message:String(e)}async function a(a,o,s){let c=(0,t.createOperationRecorder)(a.stores),l=a.shouldProvision??(e=>e.deployment_type===`wfp`),u=e=>a.stepConfig?.[e]??(e===`verify`?r:n),d=o.operation_id,f=o.tenant_id,p=await s.do(`mark-running`,u(`mark-running`),async()=>{let e=await a.tenants.get(f);if(!e||!l(e))return await c.appendEvent(d,{step:`mark-running`,outcome:`skipped`,detail:{reason:e?`tenant is not WFP-provisioned`:`tenant row missing (rolled back?)`}}),await c.markSucceeded(d),{skipped:!0,scriptName:``,databaseName:``};let t=a.steps.validate(),n=a.steps.names(f);return await c.markRunning(d,`mark-running`),await c.appendEvent(d,{step:`mark-running`,outcome:`succeeded`}),{skipped:!1,...n,...t}});if(p.skipped)return;let m={worker_script_name:p.scriptName,bundle_configuration:p.bundleConfiguration,worker_version:p.workerVersion,database_version:p.databaseVersion};try{let t=await s.do(`create-database`,u(`create-database`),async()=>{await c.setCurrentStep(d,`create-database`);let{id:e,created:t}=await a.steps.findOrCreateDatabase(p.databaseName);return await c.appendEvent(d,{step:`create-database`,outcome:`succeeded`,detail:{database_id:e,created:t}}),{databaseId:e,created:t}});if(m.d1_database_id=t.databaseId,await s.do(`apply-migrations`,u(`apply-migrations`),async()=>{await c.setCurrentStep(d,`apply-migrations`),await a.steps.applyMigrations(t.databaseId,t.created),await c.appendEvent(d,{step:`apply-migrations`,outcome:`succeeded`,detail:{database_version:p.databaseVersion}})}),await s.do(`upload-script`,u(`upload-script`),async()=>{await c.setCurrentStep(d,`upload-script`),await a.steps.uploadScript(p.scriptName,t.databaseId),await c.appendEvent(d,{step:`upload-script`,outcome:`succeeded`,detail:{worker_version:p.workerVersion}})}),await s.do(`upload-secrets`,u(`upload-secrets`),async()=>{await c.setCurrentStep(d,`upload-secrets`),await a.steps.uploadSecrets(p.scriptName,f),await c.appendEvent(d,{step:`upload-secrets`,outcome:`succeeded`})}),a.syncDefaults){let t=a.syncDefaults;await s.do(`seed-defaults`,u(`seed-defaults`),async()=>{await c.setCurrentStep(d,`seed-defaults`);let n=e.t(await t(f));if(n.length>0)throw Error(`sync-defaults seed reported ${n.length} error(s): ${n.join(`; `)}`);await c.appendEvent(d,{step:`seed-defaults`,outcome:`succeeded`})})}if(a.verify){let e=a.verify;await s.do(`verify`,u(`verify`),async()=>{await c.setCurrentStep(d,`verify`),await e(t.databaseId,f),await c.appendEvent(d,{step:`verify`,outcome:`succeeded`})})}await s.do(`mark-ready`,u(`mark-ready`),async()=>{await c.setCurrentStep(d,`mark-ready`),await a.tenants.update(f,{...m,provisioning_state:`ready`,provisioning_error:void 0,provisioning_state_changed_at:new Date().toISOString()}),await c.appendEvent(d,{step:`mark-ready`,outcome:`succeeded`}),await c.markSucceeded(d)})}catch(e){throw await s.do(`mark-failed`,u(`mark-failed`),async()=>{let t=i(e);try{await a.tenants.update(f,{...m,provisioning_state:`failed`,provisioning_error:t.slice(0,2048),provisioning_state_changed_at:new Date().toISOString()})}catch(e){a.logger?.warn(`Failed to write provisioning_state="failed" for tenant ${f}:`,e)}await c.appendEvent(d,{step:`mark-failed`,outcome:`failed`,detail:{message:t}}),await c.markFailed(d,e)}),e}}var o={retries:{limit:3,delay:`5 seconds`,backoff:`exponential`},timeout:`2 minutes`};function s(e){return e instanceof Error?e.message:String(e)}async function c(e,n,r){let i=(0,t.createOperationRecorder)(e.stores),a=t=>e.stepConfig?.[t]??o,c=n.operation_id;try{await r.do(`mark-running`,a(`mark-running`),async()=>{await i.markRunning(c,`sync-defaults`)}),await r.do(`sync-defaults`,a(`sync-defaults`),async()=>{await i.setCurrentStep(c,`sync-defaults`);let n=await e.stores.tenantOperations.get(c);if(!n)throw Error(`Operation "${c}" not found`);let r=await(0,t.runSyncDefaultsStep)({operation:n,syncConfig:e.syncConfig,recorder:i});await i.appendEvent(c,{step:`sync-defaults`,outcome:`succeeded`,detail:r})}),await r.do(`mark-succeeded`,a(`mark-succeeded`),async()=>{await i.markSucceeded(c)})}catch(e){throw await r.do(`mark-failed`,a(`mark-failed`),async()=>{await i.appendEvent(c,{step:`mark-failed`,outcome:`failed`,detail:{message:s(e)}}),await i.markFailed(c,e)}),e}}var l=class extends Error{checks;constructor(e,t){super(e),this.name=`TenantProvisionVerificationError`,this.checks=t}};function
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./sync-defaults-errors-pnzkP88r.js");let t=require("@authhero/multi-tenancy");var n={retries:{limit:5,delay:`5 seconds`,backoff:`exponential`},timeout:`2 minutes`},r={retries:{limit:8,delay:`10 seconds`,backoff:`exponential`},timeout:`1 minute`};function i(e){return e instanceof Error?e.message:String(e)}async function a(a,o,s){let c=(0,t.createOperationRecorder)(a.stores),l=a.shouldProvision??(e=>e.deployment_type===`wfp`),u=e=>a.stepConfig?.[e]??(e===`verify`?r:n),d=o.operation_id,f=o.tenant_id,p=await s.do(`mark-running`,u(`mark-running`),async()=>{let e=await a.tenants.get(f);if(!e||!l(e))return await c.appendEvent(d,{step:`mark-running`,outcome:`skipped`,detail:{reason:e?`tenant is not WFP-provisioned`:`tenant row missing (rolled back?)`}}),await c.markSucceeded(d),{skipped:!0,scriptName:``,databaseName:``};let t=a.steps.validate(),n=a.steps.names(f);return await c.markRunning(d,`mark-running`),await c.appendEvent(d,{step:`mark-running`,outcome:`succeeded`}),{skipped:!1,...n,...t}});if(p.skipped)return;let m={worker_script_name:p.scriptName,bundle_configuration:p.bundleConfiguration,worker_version:p.workerVersion,database_version:p.databaseVersion};try{let t=await s.do(`create-database`,u(`create-database`),async()=>{await c.setCurrentStep(d,`create-database`);let{id:e,created:t}=await a.steps.findOrCreateDatabase(p.databaseName);return await c.appendEvent(d,{step:`create-database`,outcome:`succeeded`,detail:{database_id:e,created:t}}),{databaseId:e,created:t}});if(m.d1_database_id=t.databaseId,await s.do(`apply-migrations`,u(`apply-migrations`),async()=>{await c.setCurrentStep(d,`apply-migrations`),await a.steps.applyMigrations(t.databaseId,t.created),await c.appendEvent(d,{step:`apply-migrations`,outcome:`succeeded`,detail:{database_version:p.databaseVersion}})}),await s.do(`upload-script`,u(`upload-script`),async()=>{await c.setCurrentStep(d,`upload-script`),await a.steps.uploadScript(p.scriptName,t.databaseId),await c.appendEvent(d,{step:`upload-script`,outcome:`succeeded`,detail:{worker_version:p.workerVersion}})}),await s.do(`upload-secrets`,u(`upload-secrets`),async()=>{await c.setCurrentStep(d,`upload-secrets`),await a.steps.uploadSecrets(p.scriptName,f),await c.appendEvent(d,{step:`upload-secrets`,outcome:`succeeded`})}),a.syncDefaults){let t=a.syncDefaults;await s.do(`seed-defaults`,u(`seed-defaults`),async()=>{await c.setCurrentStep(d,`seed-defaults`);let n=e.t(await t(f));if(n.length>0)throw Error(`sync-defaults seed reported ${n.length} error(s): ${n.join(`; `)}`);await c.appendEvent(d,{step:`seed-defaults`,outcome:`succeeded`})})}if(a.verify){let e=a.verify;await s.do(`verify`,u(`verify`),async()=>{await c.setCurrentStep(d,`verify`),await e(t.databaseId,f),await c.appendEvent(d,{step:`verify`,outcome:`succeeded`})})}await s.do(`mark-ready`,u(`mark-ready`),async()=>{await c.setCurrentStep(d,`mark-ready`),await a.tenants.update(f,{...m,provisioning_state:`ready`,provisioning_error:void 0,provisioning_state_changed_at:new Date().toISOString()}),await c.appendEvent(d,{step:`mark-ready`,outcome:`succeeded`}),await c.markSucceeded(d)})}catch(e){throw await s.do(`mark-failed`,u(`mark-failed`),async()=>{let t=i(e);try{await a.tenants.update(f,{...m,provisioning_state:`failed`,provisioning_error:t.slice(0,2048),provisioning_state_changed_at:new Date().toISOString()})}catch(e){a.logger?.warn(`Failed to write provisioning_state="failed" for tenant ${f}:`,e)}await c.appendEvent(d,{step:`mark-failed`,outcome:`failed`,detail:{message:t}}),await c.markFailed(d,e)}),e}}var o={retries:{limit:3,delay:`5 seconds`,backoff:`exponential`},timeout:`2 minutes`};function s(e){return e instanceof Error?e.message:String(e)}async function c(e,n,r){let i=(0,t.createOperationRecorder)(e.stores),a=t=>e.stepConfig?.[t]??o,c=n.operation_id;try{await r.do(`mark-running`,a(`mark-running`),async()=>{await i.markRunning(c,`sync-defaults`)}),await r.do(`sync-defaults`,a(`sync-defaults`),async()=>{await i.setCurrentStep(c,`sync-defaults`);let n=await e.stores.tenantOperations.get(c);if(!n)throw Error(`Operation "${c}" not found`);let r=await(0,t.runSyncDefaultsStep)({operation:n,syncConfig:e.syncConfig,recorder:i});await i.appendEvent(c,{step:`sync-defaults`,outcome:`succeeded`,detail:r})}),await r.do(`mark-succeeded`,a(`mark-succeeded`),async()=>{await i.markSucceeded(c)})}catch(e){throw await r.do(`mark-failed`,a(`mark-failed`),async()=>{await i.appendEvent(c,{step:`mark-failed`,outcome:`failed`,detail:{message:s(e)}}),await i.markFailed(c,e)}),e}}var l={retries:{limit:3,delay:`5 seconds`,backoff:`exponential`},timeout:`2 minutes`},u={retries:{limit:0,delay:`5 seconds`},timeout:`5 minutes`};function d(e){return e instanceof Error?e.message:String(e)}async function f(e,n,r){let i=(0,t.createOperationRecorder)(e.stores),a=t=>e.stepConfig?.[t]??(t===`detach`?u:l),o=n.operation_id;try{await r.do(`mark-running`,a(`mark-running`),async()=>{await i.markRunning(o,`detach`)}),await r.do(`detach`,a(`detach`),async()=>{await i.setCurrentStep(o,`detach`);let n=await e.stores.tenantOperations.get(o);if(!n)throw Error(`Operation "${o}" not found`);let r=await(0,t.runDetachStep)({operation:n,detachTenant:e.detachTenant,recorder:i});await i.appendEvent(o,{step:`detach`,outcome:`succeeded`,detail:r})}),await r.do(`mark-succeeded`,a(`mark-succeeded`),async()=>{await i.markSucceeded(o)})}catch(e){throw await r.do(`mark-failed`,a(`mark-failed`),async()=>{await i.appendEvent(o,{step:`mark-failed`,outcome:`failed`,detail:{message:d(e)}}),await i.markFailed(o,e)}),e}}var p=class extends Error{checks;constructor(e,t){super(e),this.name=`TenantProvisionVerificationError`,this.checks=t}};function m(e,t){for(let n of e)for(let e of n.results??[])if(!(!e||typeof e!=`object`)){for(let[n,r]of Object.entries(e))if(n===t){if(typeof r==`number`)return r;if(typeof r==`string`&&r!==``&&!isNaN(Number(r)))return Number(r)}}return 0}function h(t){let n=t.minKeys??1;return async(r,i)=>{let a=await t.client.execD1(r,`SELECT (SELECT COUNT(*) FROM keys) AS key_count, (SELECT COUNT(*) FROM tenants WHERE id = '${e.r(i)}') AS tenant_count;`),o=m(a,`key_count`),s=m(a,`tenant_count`),c=[];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(`; `)}`,{keyCount:o,tenantRowCount:s})}}function g(e){let t=e instanceof Error?e.message:String(e??``);return/already exists|already been created|duplicate/i.test(t)}function _(e){return{engine:`cloudflare-workflows`,async start(n){if(n.kind!==`provision`&&n.kind!==`sync-defaults`&&n.kind!==`detach`)throw Error(`The Cloudflare Workflows executor does not support "${n.kind}" operations yet (it runs provision, sync-defaults and detach)`);if(!n.tenant_id)throw Error(`${n.kind} operations require a tenant_id`);let r=n.engine_instance_id??(0,t.buildEngineInstanceId)(n);try{await e.binding.create({id:r,params:{operation_id:n.id,tenant_id:n.tenant_id,kind:n.kind}})}catch(e){if(!g(e))throw e}}}}var v=`cloudflare-workflows`;async function y(e){let{stores:n,tenants:r,binding:i}=e,a=(0,t.createOperationRecorder)(n),o=e.minAgeMs??600*1e3,s=e.limit??100,c=new Date(Date.now()-o).toISOString(),{operations:l}=await n.tenantOperations.list({status:[`pending`,`running`],engine:v,updated_before:c,per_page:s}),u={scanned:l.length,markedFailed:0,markedSucceeded:0,stillRunning:0,instanceMissing:0,errors:0};for(let t of l)try{await f(t)}catch(n){u.errors++,e.logger?.warn(`Failed to reconcile tenant operation ${t.id}:`,n)}return u;async function d(e,t){await a.appendEvent(e.id,{step:e.current_step??`reconcile`,outcome:`reconciled`,detail:{resolution:`failed`,reason:t}}),await a.markFailed(e.id,t),e.kind===`provision`&&e.tenant_id&&(await r.get(e.tenant_id))?.provisioning_state===`pending`&&await r.update(e.tenant_id,{provisioning_state:`failed`,provisioning_error:t.slice(0,2048),provisioning_state_changed_at:new Date().toISOString()})}async function f(e){let o=await n.tenantOperations.get(e.id);if(!o||(0,t.isTerminalStatus)(o.status))return;let s=o.engine_instance_id??(0,t.buildEngineInstanceId)(o),c;try{c=await i.get(s)}catch{u.instanceMissing++,u.markedFailed++,await d(o,`engine instance "${s}" not found (expired past retention or never started)`);return}let l=await c.status();if(l.status===`errored`||l.status===`terminated`){let e=typeof l.error==`string`?l.error:l.error?.message??`engine reported ${l.status}`;u.markedFailed++,await d(o,e);return}if(l.status===`complete`){if(o.kind===`sync-defaults`||o.kind===`detach`){u.markedSucceeded++,await a.appendEvent(o.id,{step:o.current_step??`reconcile`,outcome:`reconciled`,detail:{resolution:`succeeded`}}),await a.markSucceeded(o.id);return}(o.tenant_id?await r.get(o.tenant_id):null)?.provisioning_state===`ready`?(u.markedSucceeded++,await a.appendEvent(o.id,{step:o.current_step??`reconcile`,outcome:`reconciled`,detail:{resolution:`succeeded`}}),await a.markSucceeded(o.id)):(u.markedFailed++,await d(o,`engine instance completed but the operation was never finalized and the tenant is not ready`));return}u.stillRunning++}}function b(e){let t=e.shouldProvision??(e=>e.deployment_type===`wfp`);return{async onProvision(n,r){let i=await e.tenants.get(n);i&&t(i)&&await e.enqueueOperation({kind:`provision`,tenant_id:n})},async onUpgrade(t,n){await e.inline.onUpgrade(t,n)},async onDeprovision(t){await e.inline.onDeprovision(t)}}}exports.TENANT_OPERATION_ENGINE=v,exports.TenantProvisionVerificationError=p,exports.createCloudflareWorkflowsExecutor=_,exports.createProvisionVerifier=h,exports.createWfpWorkflowProvisioningHook=b,exports.reconcileTenantOperations=y,exports.runDetachOperation=f,exports.runProvisionOperation=a,exports.runSyncDefaultsOperation=c;
|
package/dist/workflows.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TenantsDataAdapter, TenantOperation } from '@authhero/adapter-interfaces';
|
|
2
|
-
import { TenantOperationStores, StepConfig, StepRunner, SyncConfigFn, TenantOperationExecutor } from '@authhero/multi-tenancy';
|
|
2
|
+
import { TenantOperationStores, StepConfig, StepRunner, SyncConfigFn, DetachConfigFn, TenantOperationExecutor } from '@authhero/multi-tenancy';
|
|
3
3
|
|
|
4
4
|
interface CfApiClientOptions {
|
|
5
5
|
accountId: string;
|
|
@@ -134,7 +134,7 @@ interface TenantOperationWorkflowParams {
|
|
|
134
134
|
operation_id: string;
|
|
135
135
|
tenant_id: string;
|
|
136
136
|
/** Widened beyond these in later phases (upgrade, backup, …). */
|
|
137
|
-
kind: "provision" | "sync-defaults";
|
|
137
|
+
kind: "provision" | "sync-defaults" | "detach";
|
|
138
138
|
}
|
|
139
139
|
/**
|
|
140
140
|
* Instance status as reported by the engine. The exact value set has
|
|
@@ -247,6 +247,36 @@ interface SyncDefaultsOperationDeps {
|
|
|
247
247
|
*/
|
|
248
248
|
declare function runSyncDefaultsOperation(deps: SyncDefaultsOperationDeps, params: TenantOperationWorkflowParams, step: StepRunner): Promise<void>;
|
|
249
249
|
|
|
250
|
+
type DetachStepName = "mark-running" | "detach" | "mark-succeeded" | "mark-failed";
|
|
251
|
+
interface DetachOperationDeps {
|
|
252
|
+
/** Control-plane operation log stores. */
|
|
253
|
+
stores: TenantOperationStores;
|
|
254
|
+
/**
|
|
255
|
+
* How the detach reaches the tenant's database, for example
|
|
256
|
+
* `createDispatchDetach(...)`.
|
|
257
|
+
*/
|
|
258
|
+
detachTenant: DetachConfigFn;
|
|
259
|
+
/**
|
|
260
|
+
* Per-step overrides of the retry/timeout defaults. The `detach` step has no
|
|
261
|
+
* automatic retries unless a host sets them.
|
|
262
|
+
*/
|
|
263
|
+
stepConfig?: Partial<Record<DetachStepName, StepConfig>>;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Durable `detach` operation: a synced tenant leaves its parent, as a Workflows
|
|
267
|
+
* instance that writes to the control-plane operation log at every step
|
|
268
|
+
* boundary (the same shape as {@link runSyncDefaultsOperation}, and the same
|
|
269
|
+
* step logic as the inline executor). The operation's `input` (`dry_run`,
|
|
270
|
+
* `clean`) is read from its row, so nothing but ids crosses the Workflows
|
|
271
|
+
* boundary.
|
|
272
|
+
*
|
|
273
|
+
* A dry run records what would change in the operation's `result` and writes
|
|
274
|
+
* nothing. Any failure runs `mark-failed` (event + failed row) and rethrows
|
|
275
|
+
* so the instance ends `errored`; if even that dies, the reconciler copies the
|
|
276
|
+
* engine's terminal state into the row.
|
|
277
|
+
*/
|
|
278
|
+
declare function runDetachOperation(deps: DetachOperationDeps, params: TenantOperationWorkflowParams, step: StepRunner): Promise<void>;
|
|
279
|
+
|
|
250
280
|
interface ProvisionVerifierOptions {
|
|
251
281
|
client: CloudflareApiClient;
|
|
252
282
|
/** Minimum number of signing keys the tenant D1 must hold. Default 1. */
|
|
@@ -415,5 +445,5 @@ interface WfpWorkflowProvisioningHookOptions {
|
|
|
415
445
|
*/
|
|
416
446
|
declare function createWfpWorkflowProvisioningHook(options: WfpWorkflowProvisioningHookOptions): WfpTenantProvisioningHook;
|
|
417
447
|
|
|
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 };
|
|
448
|
+
export { TENANT_OPERATION_ENGINE, TenantProvisionVerificationError, createCloudflareWorkflowsExecutor, createProvisionVerifier, createWfpWorkflowProvisioningHook, reconcileTenantOperations, runDetachOperation, runProvisionOperation, runSyncDefaultsOperation };
|
|
449
|
+
export type { CloudflareWorkflowsExecutorOptions, DetachOperationDeps, DetachStepName, ProvisionOperationDeps, ProvisionStepName, ProvisionVerifierOptions, ReconcileTenantOperationsOptions, ReconcileTenantOperationsResult, SyncDefaultsOperationDeps, SyncDefaultsStepName, TenantOperationWorkflowParams, WfpWorkflowProvisioningHookOptions, WorkflowInstanceHandle, WorkflowInstanceStatus, WorkflowsBinding };
|