@authhero/cloudflare-adapter 2.33.3 → 2.34.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/cloudflare-adapter.cjs +1 -1
- package/dist/cloudflare-adapter.d.ts +342 -3
- package/dist/cloudflare-adapter.mjs +1683 -1470
- package/dist/tsconfig.types.tsbuildinfo +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/wfp-provisioner/cf-api.d.ts +83 -0
- package/dist/types/wfp-provisioner/index.d.ts +6 -0
- package/dist/types/wfp-provisioner/provisioner.d.ts +47 -0
- package/dist/types/wfp-provisioner/tenant-hook.d.ts +68 -0
- package/dist/types/wfp-provisioner/tenant-worker.example.d.ts +92 -0
- package/dist/types/wfp-provisioner/types.d.ts +140 -0
- package/package.json +3 -3
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin Cloudflare REST API client for the WFP+D1 provisioner.
|
|
3
|
+
*
|
|
4
|
+
* Each method maps 1:1 to a documented CF endpoint and returns the parsed
|
|
5
|
+
* response body. Errors surface as `CloudflareApiError` carrying the HTTP
|
|
6
|
+
* status, endpoint, and (when JSON) the CF error array — making them easy
|
|
7
|
+
* to log without re-fetching the response.
|
|
8
|
+
*
|
|
9
|
+
* Idempotency is the caller's responsibility — the provisioner sequences
|
|
10
|
+
* calls and tolerates "already exists" / "not found" depending on the
|
|
11
|
+
* operation (see `provisioner.ts`).
|
|
12
|
+
*/
|
|
13
|
+
export declare class CloudflareApiError extends Error {
|
|
14
|
+
readonly status: number;
|
|
15
|
+
readonly endpoint: string;
|
|
16
|
+
readonly errors: unknown[];
|
|
17
|
+
readonly body: string;
|
|
18
|
+
constructor(status: number, endpoint: string, body: string, errors?: unknown[]);
|
|
19
|
+
}
|
|
20
|
+
export interface CfApiClientOptions {
|
|
21
|
+
accountId: string;
|
|
22
|
+
apiToken: string;
|
|
23
|
+
fetch?: typeof fetch;
|
|
24
|
+
timeoutMs?: number;
|
|
25
|
+
baseUrl?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface D1Database {
|
|
28
|
+
uuid: string;
|
|
29
|
+
name: string;
|
|
30
|
+
}
|
|
31
|
+
export interface D1QueryResult {
|
|
32
|
+
success: boolean;
|
|
33
|
+
meta?: Record<string, unknown>;
|
|
34
|
+
results?: unknown[];
|
|
35
|
+
}
|
|
36
|
+
export interface ScriptBinding {
|
|
37
|
+
type: "d1" | "plain_text" | "secret_text";
|
|
38
|
+
name: string;
|
|
39
|
+
id?: string;
|
|
40
|
+
text?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface ScriptUploadOptions {
|
|
43
|
+
/** Script source (JavaScript ES module). */
|
|
44
|
+
script: string;
|
|
45
|
+
/** Main module filename (must match part name in form data). */
|
|
46
|
+
mainModule: string;
|
|
47
|
+
/** Compatibility date, ISO yyyy-mm-dd. */
|
|
48
|
+
compatibilityDate: string;
|
|
49
|
+
/** Compatibility flags (e.g. `["nodejs_compat"]`). */
|
|
50
|
+
compatibilityFlags?: string[];
|
|
51
|
+
/** Bindings to attach (D1, plain_text, etc.). Secrets go via setSecret(). */
|
|
52
|
+
bindings?: ScriptBinding[];
|
|
53
|
+
/** Optional tags, stored on the script for operator-side lookup. */
|
|
54
|
+
tags?: string[];
|
|
55
|
+
}
|
|
56
|
+
export declare class CloudflareApiClient {
|
|
57
|
+
private readonly accountId;
|
|
58
|
+
private readonly apiToken;
|
|
59
|
+
private readonly fetchImpl;
|
|
60
|
+
private readonly timeoutMs;
|
|
61
|
+
private readonly baseUrl;
|
|
62
|
+
constructor(options: CfApiClientOptions);
|
|
63
|
+
createD1Database(name: string): Promise<D1Database>;
|
|
64
|
+
listD1Databases(name?: string): Promise<D1Database[]>;
|
|
65
|
+
deleteD1Database(databaseId: string): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Execute a single SQL statement (or batch of `;`-separated statements
|
|
68
|
+
* permitted by D1) against the given database. Use for applying
|
|
69
|
+
* migrations one file at a time — the per-call response size cap means
|
|
70
|
+
* very large single calls can fail; splitting per file keeps each call
|
|
71
|
+
* bounded by the migration author.
|
|
72
|
+
*/
|
|
73
|
+
execD1(databaseId: string, sql: string): Promise<D1QueryResult[]>;
|
|
74
|
+
uploadNamespacedScript(namespace: string, scriptName: string, options: ScriptUploadOptions): Promise<void>;
|
|
75
|
+
deleteNamespacedScript(namespace: string, scriptName: string): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Set a single secret on a namespaced script. The CF API replaces the
|
|
78
|
+
* value if a secret with that name already exists, so this is safely
|
|
79
|
+
* re-runnable.
|
|
80
|
+
*/
|
|
81
|
+
setNamespacedScriptSecret(namespace: string, scriptName: string, secretName: string, secretValue: string): Promise<void>;
|
|
82
|
+
private request;
|
|
83
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createCloudflareWfpD1Provisioner } from "./provisioner";
|
|
2
|
+
export type { CloudflareWfpD1Provisioner, CloudflareWfpD1ProvisionerOptions, ProvisionResult, ProvisionerMigration, TenantSecretsResolver, } from "./types";
|
|
3
|
+
export { createWfpTenantProvisioningHook } from "./tenant-hook";
|
|
4
|
+
export type { WfpTenantProvisioningHook, WfpTenantProvisioningHookOptions, } from "./tenant-hook";
|
|
5
|
+
export { CloudflareApiClient, CloudflareApiError } from "./cf-api";
|
|
6
|
+
export type { CfApiClientOptions, D1Database, D1QueryResult, ScriptBinding, ScriptUploadOptions, } from "./cf-api";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { CloudflareWfpD1Provisioner, CloudflareWfpD1ProvisionerOptions } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Construct the lifecycle hooks for provisioning + deprovisioning a tenant
|
|
4
|
+
* on Cloudflare Workers-for-Platforms backed by a per-tenant D1.
|
|
5
|
+
*
|
|
6
|
+
* Wiring on the control-plane authhero:
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* import createAdapters from "@authhero/cloudflare-adapter";
|
|
10
|
+
* import { createCloudflareWfpD1Provisioner } from "@authhero/cloudflare-adapter";
|
|
11
|
+
* import { initMultiTenant } from "@authhero/multi-tenancy";
|
|
12
|
+
* import tenantWorkerScript from "./tenant-worker.dist.js?raw";
|
|
13
|
+
* import migration0001 from "@authhero/drizzle/drizzle/sqlite/0000_initial.sql?raw";
|
|
14
|
+
*
|
|
15
|
+
* const provisioner = createCloudflareWfpD1Provisioner({
|
|
16
|
+
* accountId: env.CLOUDFLARE_ACCOUNT_ID,
|
|
17
|
+
* apiToken: env.CLOUDFLARE_API_TOKEN,
|
|
18
|
+
* dispatchNamespace: "authhero-tenants",
|
|
19
|
+
* controlPlaneBaseUrl: env.PUBLIC_BASE_URL,
|
|
20
|
+
* tenantWorkerScript,
|
|
21
|
+
* migrations: [{ name: "0000_initial.sql", sql: migration0001 }],
|
|
22
|
+
* secrets: async (tenantId) => ({
|
|
23
|
+
* ENCRYPTION_KEY: env.SHARED_ENCRYPTION_KEY,
|
|
24
|
+
* ISSUER: `https://${tenantId}.tokens.example.com`,
|
|
25
|
+
* }),
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* const { app } = initMultiTenant({
|
|
29
|
+
* dataAdapter,
|
|
30
|
+
* controlPlane: { tenantId: "main", clientId: "platform" },
|
|
31
|
+
* databaseIsolation: {
|
|
32
|
+
* getAdapters: async (tenantId) => { ... }, // resolve per-tenant adapter
|
|
33
|
+
* onProvision: provisioner.onProvision,
|
|
34
|
+
* onDeprovision: provisioner.onDeprovision,
|
|
35
|
+
* },
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* On tenant create, the management API row write fires
|
|
40
|
+
* `databaseIsolation.onProvision(tenantId)` which runs the full sequence
|
|
41
|
+
* below. If any step throws, the upstream `createProvisioningHooks` rolls
|
|
42
|
+
* back the tenant row — though side effects already taken (D1 created,
|
|
43
|
+
* partial migrations applied) are NOT rolled back. The operator should
|
|
44
|
+
* treat re-running `onProvision(tenantId)` as safe; each step is idempotent
|
|
45
|
+
* on "already exists".
|
|
46
|
+
*/
|
|
47
|
+
export declare function createCloudflareWfpD1Provisioner(options: CloudflareWfpD1ProvisionerOptions): CloudflareWfpD1Provisioner;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { TenantsDataAdapter } from "@authhero/adapter-interfaces";
|
|
2
|
+
import type { CloudflareWfpD1Provisioner } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* Adapt the provisioner to `@authhero/multi-tenancy`'s
|
|
5
|
+
* `databaseIsolation.onProvision` / `onDeprovision` contract by:
|
|
6
|
+
*
|
|
7
|
+
* 1. Looking up the tenant row first and gating on
|
|
8
|
+
* `tenant.deployment_type === "wfp"` — shared tenants short-circuit so
|
|
9
|
+
* the same control plane can host both kinds without code branches.
|
|
10
|
+
* 2. Running the provisioner sequence (D1 + script + secrets).
|
|
11
|
+
* 3. Writing the resulting `d1_database_id` + `worker_script_name` +
|
|
12
|
+
* `provisioning_state` back onto the tenant row so the admin UI can
|
|
13
|
+
* show real status, and so a redeploy / re-provision knows which
|
|
14
|
+
* resource ids to operate on.
|
|
15
|
+
* 4. On failure, marking `provisioning_state = "failed"` with the error
|
|
16
|
+
* message, then re-throwing — the multi-tenancy hook treats the throw
|
|
17
|
+
* as a signal to roll back the tenant row.
|
|
18
|
+
*
|
|
19
|
+
* Typical wiring on the control-plane authhero:
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { initMultiTenant } from "@authhero/multi-tenancy";
|
|
23
|
+
* import {
|
|
24
|
+
* createCloudflareWfpD1Provisioner,
|
|
25
|
+
* createWfpTenantProvisioningHook,
|
|
26
|
+
* } from "@authhero/cloudflare-adapter";
|
|
27
|
+
*
|
|
28
|
+
* const provisioner = createCloudflareWfpD1Provisioner({ ... });
|
|
29
|
+
* const hook = createWfpTenantProvisioningHook({
|
|
30
|
+
* provisioner,
|
|
31
|
+
* tenants: dataAdapter.tenants,
|
|
32
|
+
* });
|
|
33
|
+
*
|
|
34
|
+
* const { app } = initMultiTenant({
|
|
35
|
+
* dataAdapter,
|
|
36
|
+
* databaseIsolation: {
|
|
37
|
+
* getAdapters: async (tenantId) => { ... },
|
|
38
|
+
* onProvision: hook.onProvision,
|
|
39
|
+
* onDeprovision: hook.onDeprovision,
|
|
40
|
+
* },
|
|
41
|
+
* });
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export interface WfpTenantProvisioningHookOptions {
|
|
45
|
+
provisioner: CloudflareWfpD1Provisioner;
|
|
46
|
+
tenants: TenantsDataAdapter;
|
|
47
|
+
/**
|
|
48
|
+
* Optional override of "should this tenant be WFP-provisioned?". Defaults
|
|
49
|
+
* to `tenant.deployment_type === "wfp"`. Provide a custom predicate when
|
|
50
|
+
* the gating signal lives elsewhere (a feature flag, a config table, etc.).
|
|
51
|
+
*/
|
|
52
|
+
shouldProvision?: (tenant: {
|
|
53
|
+
id: string;
|
|
54
|
+
deployment_type?: string;
|
|
55
|
+
storage_kind?: string;
|
|
56
|
+
}) => boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Optional `console`-compatible logger for warnings emitted when the
|
|
59
|
+
* tenant row write-back fails after a successful provision. Defaults to
|
|
60
|
+
* a silent no-op so this module stays test-quiet.
|
|
61
|
+
*/
|
|
62
|
+
logger?: Pick<Console, "warn">;
|
|
63
|
+
}
|
|
64
|
+
export interface WfpTenantProvisioningHook {
|
|
65
|
+
onProvision(tenantId: string): Promise<void>;
|
|
66
|
+
onDeprovision(tenantId: string): Promise<void>;
|
|
67
|
+
}
|
|
68
|
+
export declare function createWfpTenantProvisioningHook(options: WfpTenantProvisioningHookOptions): WfpTenantProvisioningHook;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical tenant-worker entrypoint for a Workers-for-Platforms + D1
|
|
3
|
+
* authhero deployment. Reference only — copy into your own tenant-worker
|
|
4
|
+
* source tree and bundle from there (this file is not bundled into
|
|
5
|
+
* `@authhero/cloudflare-adapter`'s `dist`).
|
|
6
|
+
*
|
|
7
|
+
* The bundle the provisioner uploads must be a single self-contained ESM
|
|
8
|
+
* `.js`. Operators build it themselves so they can layer in custom hooks,
|
|
9
|
+
* code-executors, secrets, etc. Below is the minimal shape every operator
|
|
10
|
+
* needs.
|
|
11
|
+
*
|
|
12
|
+
* Env contract set by `createCloudflareWfpD1Provisioner.onProvision`:
|
|
13
|
+
*
|
|
14
|
+
* | Name | Source | Purpose |
|
|
15
|
+
* | ------------------------ | ------------ | ---------------------------------------------------- |
|
|
16
|
+
* | AUTH_DB | D1 binding | Per-tenant database |
|
|
17
|
+
* | CONTROL_PLANE_BASE_URL | plain_text | Target of `controlPlaneSync` outbox destination |
|
|
18
|
+
* | ENCRYPTION_KEY | secret_text | Encryption at rest (must be byte-stable across plane)|
|
|
19
|
+
* | ISSUER | secret_text | `iss` claim in JWTs |
|
|
20
|
+
* | …additional secrets… | secret_text | JWKS, mail creds, OAuth client secrets, etc. |
|
|
21
|
+
*
|
|
22
|
+
* Recommended bundler call:
|
|
23
|
+
*
|
|
24
|
+
* ```sh
|
|
25
|
+
* esbuild src/tenant-worker.ts \
|
|
26
|
+
* --bundle --format=esm --platform=neutral --target=es2022 \
|
|
27
|
+
* --conditions=workerd,worker,browser \
|
|
28
|
+
* --outfile=dist/tenant-worker.js
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* Wiring into the provisioner (control-plane authhero side):
|
|
32
|
+
*
|
|
33
|
+
* ```ts
|
|
34
|
+
* import tenantWorkerScript from "./tenant-worker.dist.js?raw";
|
|
35
|
+
* import { createCloudflareWfpD1Provisioner } from "@authhero/cloudflare-adapter";
|
|
36
|
+
*
|
|
37
|
+
* const provisioner = createCloudflareWfpD1Provisioner({
|
|
38
|
+
* accountId,
|
|
39
|
+
* apiToken,
|
|
40
|
+
* dispatchNamespace: "authhero-tenants",
|
|
41
|
+
* controlPlaneBaseUrl: env.PUBLIC_BASE_URL,
|
|
42
|
+
* tenantWorkerScript,
|
|
43
|
+
* migrations: [
|
|
44
|
+
* { name: "0000_initial.sql", sql: initialMigrationSql },
|
|
45
|
+
* // ...one entry per @authhero/drizzle migration file, in order
|
|
46
|
+
* ],
|
|
47
|
+
* secrets: async (tenantId) => ({
|
|
48
|
+
* ENCRYPTION_KEY: env.SHARED_ENCRYPTION_KEY, // byte-stable across all tenants
|
|
49
|
+
* ISSUER: `https://${tenantId}.tokens.example.com`,
|
|
50
|
+
* // …whichever else authhero reads from env
|
|
51
|
+
* }),
|
|
52
|
+
* });
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* Minimal tenant-worker source (copy and adapt):
|
|
56
|
+
*
|
|
57
|
+
* ```ts
|
|
58
|
+
* import { drizzle } from "drizzle-orm/d1";
|
|
59
|
+
* import createDataAdapters from "@authhero/drizzle";
|
|
60
|
+
* import { init } from "authhero";
|
|
61
|
+
*
|
|
62
|
+
* interface TenantWorkerEnv {
|
|
63
|
+
* AUTH_DB: D1Database;
|
|
64
|
+
* CONTROL_PLANE_BASE_URL: string;
|
|
65
|
+
* ENCRYPTION_KEY: string;
|
|
66
|
+
* ISSUER: string;
|
|
67
|
+
* // …your additional secrets
|
|
68
|
+
* }
|
|
69
|
+
*
|
|
70
|
+
* export default {
|
|
71
|
+
* async fetch(
|
|
72
|
+
* request: Request,
|
|
73
|
+
* env: TenantWorkerEnv,
|
|
74
|
+
* ctx: ExecutionContext,
|
|
75
|
+
* ): Promise<Response> {
|
|
76
|
+
* const db = drizzle(env.AUTH_DB);
|
|
77
|
+
* const dataAdapter = createDataAdapters(db);
|
|
78
|
+
*
|
|
79
|
+
* const { app } = init({
|
|
80
|
+
* dataAdapter,
|
|
81
|
+
* controlPlaneSync: {
|
|
82
|
+
* baseUrl: env.CONTROL_PLANE_BASE_URL,
|
|
83
|
+
* timeoutMs: 10_000,
|
|
84
|
+
* },
|
|
85
|
+
* });
|
|
86
|
+
*
|
|
87
|
+
* return app.fetch(request, env, ctx);
|
|
88
|
+
* },
|
|
89
|
+
* };
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export {};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public types for the Workers-for-Platforms + D1 tenant provisioner.
|
|
3
|
+
*
|
|
4
|
+
* Wire-up shape — the operator constructs a provisioner once at control-plane
|
|
5
|
+
* boot and passes its `onProvision` / `onDeprovision` to
|
|
6
|
+
* `@authhero/multi-tenancy`'s `databaseIsolation` config. Every tenant
|
|
7
|
+
* create/delete in the management API then drives a CF API call sequence
|
|
8
|
+
* that:
|
|
9
|
+
* 1. (create) provisions a per-tenant D1, applies migrations, deploys a
|
|
10
|
+
* namespaced worker bound to that D1, and uploads secrets to it.
|
|
11
|
+
* 2. (delete) removes the namespaced worker and its D1.
|
|
12
|
+
*
|
|
13
|
+
* Failures throw — the multi-tenancy hook wraps `onProvision` such that a
|
|
14
|
+
* thrown error rolls the tenant row back. Idempotency on retry is best-effort:
|
|
15
|
+
* each step checks for "already exists" and continues, but the operator
|
|
16
|
+
* should treat the provisioning sequence as restartable rather than
|
|
17
|
+
* transactional.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* SQL migration to run on every new tenant D1, in order.
|
|
21
|
+
*
|
|
22
|
+
* Operators bundle these from `@authhero/drizzle`'s `drizzle/sqlite/` files
|
|
23
|
+
* via their build tool of choice (vite's `?raw`, esbuild loader, webpack's
|
|
24
|
+
* raw-loader, etc.) — the provisioner is agnostic about how the SQL gets
|
|
25
|
+
* loaded.
|
|
26
|
+
*/
|
|
27
|
+
export interface ProvisionerMigration {
|
|
28
|
+
/** Filename, used only for error messages and audit logging. */
|
|
29
|
+
name: string;
|
|
30
|
+
/** Full SQL text of the migration. May contain multiple statements. */
|
|
31
|
+
sql: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Resolver that returns the secret values to upload onto a newly-provisioned
|
|
35
|
+
* tenant worker. Called once per tenant during `onProvision`.
|
|
36
|
+
*
|
|
37
|
+
* Implementations typically pull from a secret store (Vault, GCP Secret
|
|
38
|
+
* Manager, etc.) — the values must match the control-plane authhero's
|
|
39
|
+
* expectations for that tenant (notably `ENCRYPTION_KEY` and JWT signing
|
|
40
|
+
* material, which must be byte-stable to keep encrypted-at-rest data and
|
|
41
|
+
* issued JWTs valid).
|
|
42
|
+
*/
|
|
43
|
+
export type TenantSecretsResolver = (tenantId: string) => Promise<Record<string, string>>;
|
|
44
|
+
export interface CloudflareWfpD1ProvisionerOptions {
|
|
45
|
+
/** Cloudflare account id that owns the namespace, D1s, and tenant workers. */
|
|
46
|
+
accountId: string;
|
|
47
|
+
/**
|
|
48
|
+
* API token with at least these permissions on `accountId`:
|
|
49
|
+
* - Workers Scripts:Edit
|
|
50
|
+
* - D1:Edit
|
|
51
|
+
* - Workers for Platforms:Edit (for namespace ops)
|
|
52
|
+
*/
|
|
53
|
+
apiToken: string;
|
|
54
|
+
/** Name of the dispatch namespace tenant workers are deployed into. */
|
|
55
|
+
dispatchNamespace: string;
|
|
56
|
+
/**
|
|
57
|
+
* Base URL of the control-plane authhero. Passed to the tenant worker via
|
|
58
|
+
* the `CONTROL_PLANE_BASE_URL` env var so its `controlPlaneSync` destination
|
|
59
|
+
* knows where to POST `controlplane.sync.*` events.
|
|
60
|
+
*/
|
|
61
|
+
controlPlaneBaseUrl: string;
|
|
62
|
+
/**
|
|
63
|
+
* Full JavaScript bundle of the tenant worker. The operator builds this
|
|
64
|
+
* (typically via esbuild/vite of a thin wrapper that calls
|
|
65
|
+
* `authhero.init({ ... })`), and passes the resulting JS string here.
|
|
66
|
+
*
|
|
67
|
+
* The bundle MUST be self-contained — Cloudflare's script upload doesn't
|
|
68
|
+
* resolve npm dependencies. Use your bundler's `format: 'esm'` + `external`
|
|
69
|
+
* lists to inline `authhero`, `@authhero/drizzle`, and friends.
|
|
70
|
+
*/
|
|
71
|
+
tenantWorkerScript: string;
|
|
72
|
+
/**
|
|
73
|
+
* Optional script metadata override. Defaults to
|
|
74
|
+
* `{ main_module: "index.js", compatibility_date, compatibility_flags: ["nodejs_compat"] }`.
|
|
75
|
+
* Set `compatibility_date` to the same date the rest of your workers use.
|
|
76
|
+
*/
|
|
77
|
+
scriptMetadata?: {
|
|
78
|
+
main_module?: string;
|
|
79
|
+
compatibility_date?: string;
|
|
80
|
+
compatibility_flags?: string[];
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* SQL migrations applied in array order to every new tenant D1. Typically
|
|
84
|
+
* loaded from `@authhero/drizzle`'s shipped migrations via your build tool.
|
|
85
|
+
*/
|
|
86
|
+
migrations: ProvisionerMigration[];
|
|
87
|
+
/**
|
|
88
|
+
* Resolver that returns the secret values to set on the tenant worker.
|
|
89
|
+
* Called once per `onProvision`. The provisioner uploads each entry via
|
|
90
|
+
* the per-script secrets API.
|
|
91
|
+
*/
|
|
92
|
+
secrets: TenantSecretsResolver;
|
|
93
|
+
/**
|
|
94
|
+
* Naming convention for the namespaced script. Supports `{tenant_id}`
|
|
95
|
+
* placeholder. Defaults to `"{tenant_id}"`.
|
|
96
|
+
*
|
|
97
|
+
* Must match whatever the dispatcher synthesizes as `script_name` in its
|
|
98
|
+
* `dispatch_namespace` handler — otherwise the dispatcher can't reach the
|
|
99
|
+
* worker after provisioning.
|
|
100
|
+
*/
|
|
101
|
+
scriptNameTemplate?: string;
|
|
102
|
+
/**
|
|
103
|
+
* Naming convention for the per-tenant D1. Supports `{tenant_id}`.
|
|
104
|
+
* Defaults to `"tenant-{tenant_id}"`. CF accepts most ASCII names; keep
|
|
105
|
+
* it stable so a re-provision finds the existing D1.
|
|
106
|
+
*/
|
|
107
|
+
d1NameTemplate?: string;
|
|
108
|
+
/**
|
|
109
|
+
* Fetch override (tests only). Defaults to global `fetch`.
|
|
110
|
+
*/
|
|
111
|
+
fetch?: typeof fetch;
|
|
112
|
+
/**
|
|
113
|
+
* Per-request timeout (ms) on the CF API. Defaults to 30s. Individual
|
|
114
|
+
* D1 migrations can take a few seconds each; the upload of a multi-MB
|
|
115
|
+
* tenant bundle also takes a noticeable chunk of that.
|
|
116
|
+
*/
|
|
117
|
+
timeoutMs?: number;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Outcome of a successful `onProvision` — returned so the caller can persist
|
|
121
|
+
* the resource IDs back onto the tenant row (`tenants.d1_database_id`,
|
|
122
|
+
* `tenants.worker_script_name`). The control-plane authhero ships these
|
|
123
|
+
* fields in its schema; `createWfpTenantProvisioningHook` writes them
|
|
124
|
+
* automatically.
|
|
125
|
+
*/
|
|
126
|
+
export interface ProvisionResult {
|
|
127
|
+
d1DatabaseId: string;
|
|
128
|
+
scriptName: string;
|
|
129
|
+
d1Name: string;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* What `createCloudflareWfpD1Provisioner` returns — the two lifecycle
|
|
133
|
+
* callbacks that plug into `databaseIsolation` from `@authhero/multi-tenancy`
|
|
134
|
+
* via the `createWfpTenantProvisioningHook` wrapper (which handles
|
|
135
|
+
* deployment-type guarding and tenant-row writebacks).
|
|
136
|
+
*/
|
|
137
|
+
export interface CloudflareWfpD1Provisioner {
|
|
138
|
+
onProvision(tenantId: string): Promise<ProvisionResult>;
|
|
139
|
+
onDeprovision(tenantId: string): Promise<void>;
|
|
140
|
+
}
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"type": "git",
|
|
12
12
|
"url": "https://github.com/markusahlstrand/authhero"
|
|
13
13
|
},
|
|
14
|
-
"version": "2.
|
|
14
|
+
"version": "2.34.0",
|
|
15
15
|
"files": [
|
|
16
16
|
"dist"
|
|
17
17
|
],
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"nanoid": "^5.1.11",
|
|
46
46
|
"wretch": "^3.0.8",
|
|
47
|
-
"@authhero/adapter-interfaces": "3.1.
|
|
48
|
-
"@authhero/kysely-adapter": "11.8.
|
|
47
|
+
"@authhero/adapter-interfaces": "3.1.1",
|
|
48
|
+
"@authhero/kysely-adapter": "11.8.8"
|
|
49
49
|
},
|
|
50
50
|
"scripts": {
|
|
51
51
|
"build": "vite build && tsc -p tsconfig.types.json && rollup -c rollup.dts.config.mjs",
|