@emdash-cms/cloudflare 0.0.1

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 (67) hide show
  1. package/dist/auth/index.d.mts +81 -0
  2. package/dist/auth/index.mjs +147 -0
  3. package/dist/cache/config.d.mts +52 -0
  4. package/dist/cache/config.mjs +55 -0
  5. package/dist/cache/runtime.d.mts +40 -0
  6. package/dist/cache/runtime.mjs +191 -0
  7. package/dist/d1-introspector-bZf0_ylK.mjs +57 -0
  8. package/dist/db/d1.d.mts +43 -0
  9. package/dist/db/d1.mjs +74 -0
  10. package/dist/db/do.d.mts +96 -0
  11. package/dist/db/do.mjs +489 -0
  12. package/dist/db/playground-middleware.d.mts +20 -0
  13. package/dist/db/playground-middleware.mjs +533 -0
  14. package/dist/db/playground.d.mts +39 -0
  15. package/dist/db/playground.mjs +26 -0
  16. package/dist/do-class-DY2Ba2RJ.mjs +174 -0
  17. package/dist/do-class-x5Xh_G62.d.mts +73 -0
  18. package/dist/do-dialect-BhFcRSFQ.mjs +58 -0
  19. package/dist/do-playground-routes-CmwFeGwJ.mjs +49 -0
  20. package/dist/do-types-CY0G0oyh.d.mts +14 -0
  21. package/dist/images-4RT9Ag8_.d.mts +76 -0
  22. package/dist/index.d.mts +200 -0
  23. package/dist/index.mjs +214 -0
  24. package/dist/media/images-runtime.d.mts +10 -0
  25. package/dist/media/images-runtime.mjs +215 -0
  26. package/dist/media/stream-runtime.d.mts +10 -0
  27. package/dist/media/stream-runtime.mjs +218 -0
  28. package/dist/plugins/index.d.mts +32 -0
  29. package/dist/plugins/index.mjs +163 -0
  30. package/dist/sandbox/index.d.mts +255 -0
  31. package/dist/sandbox/index.mjs +945 -0
  32. package/dist/storage/r2.d.mts +31 -0
  33. package/dist/storage/r2.mjs +116 -0
  34. package/dist/stream-DdbcvKi0.d.mts +78 -0
  35. package/package.json +109 -0
  36. package/src/auth/cloudflare-access.ts +303 -0
  37. package/src/auth/index.ts +16 -0
  38. package/src/cache/config.ts +81 -0
  39. package/src/cache/runtime.ts +328 -0
  40. package/src/cloudflare.d.ts +31 -0
  41. package/src/db/d1-introspector.ts +120 -0
  42. package/src/db/d1.ts +112 -0
  43. package/src/db/do-class.ts +275 -0
  44. package/src/db/do-dialect.ts +125 -0
  45. package/src/db/do-playground-routes.ts +65 -0
  46. package/src/db/do-preview-routes.ts +48 -0
  47. package/src/db/do-preview-sign.ts +100 -0
  48. package/src/db/do-preview.ts +268 -0
  49. package/src/db/do-types.ts +12 -0
  50. package/src/db/do.ts +62 -0
  51. package/src/db/playground-middleware.ts +340 -0
  52. package/src/db/playground-toolbar.ts +341 -0
  53. package/src/db/playground.ts +49 -0
  54. package/src/db/preview-toolbar.ts +220 -0
  55. package/src/index.ts +285 -0
  56. package/src/media/images-runtime.ts +353 -0
  57. package/src/media/images.ts +114 -0
  58. package/src/media/stream-runtime.ts +392 -0
  59. package/src/media/stream.ts +118 -0
  60. package/src/plugins/index.ts +7 -0
  61. package/src/plugins/vectorize-search.ts +393 -0
  62. package/src/sandbox/bridge.ts +1008 -0
  63. package/src/sandbox/index.ts +13 -0
  64. package/src/sandbox/runner.ts +357 -0
  65. package/src/sandbox/types.ts +181 -0
  66. package/src/sandbox/wrapper.ts +238 -0
  67. package/src/storage/r2.ts +200 -0
@@ -0,0 +1,174 @@
1
+ import { DurableObject } from "cloudflare:workers";
2
+
3
+ //#region src/db/do-class.ts
4
+ /**
5
+ * EmDashPreviewDB — Durable Object for preview databases
6
+ *
7
+ * Each preview session gets its own DO with isolated SQLite storage.
8
+ * The DO is populated from a snapshot of the source EmDash site
9
+ * and serves read-only queries until its TTL expires.
10
+ *
11
+ * Not used in production — preview only.
12
+ */
13
+ /** Default TTL for preview data (1 hour) */
14
+ const DEFAULT_TTL_MS = 3600 * 1e3;
15
+ /** Valid identifier pattern for snapshot table/column names */
16
+ const SAFE_IDENTIFIER = /^[a-z_][a-z0-9_]*$/;
17
+ /** SQL command prefixes that indicate read-only statements */
18
+ const READ_PREFIXES = [
19
+ "SELECT",
20
+ "PRAGMA",
21
+ "EXPLAIN",
22
+ "WITH"
23
+ ];
24
+ var EmDashPreviewDB = class extends DurableObject {
25
+ /**
26
+ * Execute a single SQL statement.
27
+ *
28
+ * Called via RPC from the Kysely driver connection.
29
+ */
30
+ query(sql, params) {
31
+ const cursor = params?.length ? this.ctx.storage.sql.exec(sql, ...params) : this.ctx.storage.sql.exec(sql);
32
+ const rows = [];
33
+ for (const row of cursor) rows.push(row);
34
+ return {
35
+ rows,
36
+ changes: READ_PREFIXES.some((p) => sql.trimStart().toUpperCase().startsWith(p)) ? void 0 : cursor.rowsWritten
37
+ };
38
+ }
39
+ /**
40
+ * Execute multiple statements in a single synchronous transaction.
41
+ *
42
+ * Used for snapshot import.
43
+ */
44
+ batch(statements) {
45
+ this.ctx.storage.transactionSync(() => {
46
+ for (const stmt of statements) if (stmt.params?.length) this.ctx.storage.sql.exec(stmt.sql, ...stmt.params);
47
+ else this.ctx.storage.sql.exec(stmt.sql);
48
+ });
49
+ }
50
+ /**
51
+ * Invalidate the cached snapshot so the next populateFromSnapshot call
52
+ * re-fetches from the source site.
53
+ */
54
+ invalidateSnapshot() {
55
+ try {
56
+ this.ctx.storage.sql.exec("DELETE FROM _emdash_do_meta WHERE key = 'snapshot_fetched_at'");
57
+ } catch {}
58
+ }
59
+ /**
60
+ * Get snapshot metadata (generated-at timestamp).
61
+ * Returns null if the DO has no snapshot loaded.
62
+ */
63
+ getSnapshotMeta() {
64
+ try {
65
+ const value = this.ctx.storage.sql.exec("SELECT value FROM _emdash_do_meta WHERE key = 'snapshot_generated_at'").one().value;
66
+ if (typeof value !== "string") return null;
67
+ return { generatedAt: value };
68
+ } catch {
69
+ return null;
70
+ }
71
+ }
72
+ /**
73
+ * Populate from a snapshot (preview mode).
74
+ *
75
+ * Fetches content from a source EmDash site and loads it into
76
+ * this DO's SQLite. Sets a TTL alarm for cleanup.
77
+ */
78
+ async populateFromSnapshot(sourceUrl, signature, options) {
79
+ const ttlMs = (options?.ttl ?? DEFAULT_TTL_MS / 1e3) * 1e3;
80
+ try {
81
+ const meta = this.ctx.storage.sql.exec("SELECT value FROM _emdash_do_meta WHERE key = 'snapshot_fetched_at'").one();
82
+ const fetchedAt = Number(meta.value);
83
+ if (Date.now() - fetchedAt < ttlMs) {
84
+ this.ctx.storage.setAlarm(Date.now() + ttlMs);
85
+ const gen = this.ctx.storage.sql.exec("SELECT value FROM _emdash_do_meta WHERE key = 'snapshot_generated_at'").one();
86
+ return { generatedAt: String(gen.value) };
87
+ }
88
+ } catch (error) {
89
+ if (!(error instanceof Error) || !error.message.includes("no such table")) throw error;
90
+ }
91
+ const url = `${sourceUrl}/_emdash/api/snapshot${options?.drafts ? "?drafts=true" : ""}`;
92
+ const response = await fetch(url, {
93
+ headers: { "X-Preview-Signature": signature },
94
+ signal: AbortSignal.timeout(1e4)
95
+ });
96
+ if (!response.ok) {
97
+ const body = await response.text().catch(() => "");
98
+ throw new Error(`Snapshot fetch failed: ${response.status} ${response.statusText}${body ? ` — ${body}` : ""}`);
99
+ }
100
+ const snapshot = await response.json();
101
+ this.ctx.storage.transactionSync(() => {
102
+ this.dropAllTables();
103
+ this.applySnapshot(snapshot);
104
+ });
105
+ this.ctx.storage.setAlarm(Date.now() + ttlMs);
106
+ return { generatedAt: snapshot.generatedAt };
107
+ }
108
+ /**
109
+ * Set a cleanup alarm after the given number of seconds.
110
+ *
111
+ * Used by the playground middleware to set TTL after initialization
112
+ * is complete (initialization runs on the Worker side via RPC).
113
+ */
114
+ setTtlAlarm(ttlSeconds) {
115
+ this.ctx.storage.setAlarm(Date.now() + ttlSeconds * 1e3);
116
+ }
117
+ /**
118
+ * Alarm handler — clean up expired preview/playground data.
119
+ *
120
+ * Drops all user tables to reclaim storage.
121
+ */
122
+ alarm() {
123
+ this.dropAllTables();
124
+ }
125
+ /**
126
+ * Drop all user tables in the DO's SQLite database.
127
+ * Preserves SQLite and Cloudflare internal tables.
128
+ */
129
+ dropAllTables() {
130
+ const tables = [...this.ctx.storage.sql.exec("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '_cf_%'")];
131
+ for (const row of tables) {
132
+ const name = String(row.name);
133
+ if (!SAFE_IDENTIFIER.test(name)) continue;
134
+ this.ctx.storage.sql.exec(`DROP TABLE IF EXISTS "${name}"`);
135
+ }
136
+ }
137
+ applySnapshot(snapshot) {
138
+ const validateSnapshotIdentifier = (name, context) => {
139
+ if (!SAFE_IDENTIFIER.test(name)) throw new Error(`Invalid ${context} in snapshot: ${JSON.stringify(name)}`);
140
+ };
141
+ this.ctx.storage.sql.exec(`
142
+ CREATE TABLE IF NOT EXISTS _emdash_do_meta (key TEXT PRIMARY KEY, value TEXT)
143
+ `);
144
+ for (const [tableName, rows] of Object.entries(snapshot.tables)) {
145
+ if (tableName === "_emdash_do_meta") continue;
146
+ if (!rows.length) continue;
147
+ validateSnapshotIdentifier(tableName, "table name");
148
+ const schemaInfo = snapshot.schema?.[tableName];
149
+ const columns = schemaInfo?.columns ?? Object.keys(rows[0]);
150
+ columns.forEach((c) => validateSnapshotIdentifier(c, `column name in ${tableName}`));
151
+ const colDefs = columns.map((c) => {
152
+ const colType = schemaInfo?.types?.[c] ?? "TEXT";
153
+ return `"${c}" ${[
154
+ "TEXT",
155
+ "INTEGER",
156
+ "REAL",
157
+ "BLOB",
158
+ "JSON"
159
+ ].includes(colType.toUpperCase()) ? colType.toUpperCase() : "TEXT"}`;
160
+ }).join(", ");
161
+ this.ctx.storage.sql.exec(`CREATE TABLE IF NOT EXISTS "${tableName}" (${colDefs})`);
162
+ const placeholders = columns.map(() => "?").join(", ");
163
+ const insertSql = `INSERT INTO "${tableName}" (${columns.map((c) => `"${c}"`).join(", ")}) VALUES (${placeholders})`;
164
+ for (const row of rows) {
165
+ const values = columns.map((c) => row[c] ?? null);
166
+ this.ctx.storage.sql.exec(insertSql, ...values);
167
+ }
168
+ }
169
+ this.ctx.storage.sql.exec(`INSERT OR REPLACE INTO _emdash_do_meta VALUES ('snapshot_fetched_at', ?), ('snapshot_generated_at', ?)`, String(Date.now()), snapshot.generatedAt);
170
+ }
171
+ };
172
+
173
+ //#endregion
174
+ export { EmDashPreviewDB as t };
@@ -0,0 +1,73 @@
1
+ import { DurableObject } from "cloudflare:workers";
2
+
3
+ //#region src/db/do-class.d.ts
4
+ /** Result shape returned by query() */
5
+ interface QueryResult {
6
+ rows: Record<string, unknown>[];
7
+ /** Number of rows written. Undefined for read-only queries. */
8
+ changes?: number;
9
+ }
10
+ /** A single statement for batch execution */
11
+ interface BatchStatement {
12
+ sql: string;
13
+ params?: unknown[];
14
+ }
15
+ declare class EmDashPreviewDB extends DurableObject {
16
+ /**
17
+ * Execute a single SQL statement.
18
+ *
19
+ * Called via RPC from the Kysely driver connection.
20
+ */
21
+ query(sql: string, params?: unknown[]): QueryResult;
22
+ /**
23
+ * Execute multiple statements in a single synchronous transaction.
24
+ *
25
+ * Used for snapshot import.
26
+ */
27
+ batch(statements: BatchStatement[]): void;
28
+ /**
29
+ * Invalidate the cached snapshot so the next populateFromSnapshot call
30
+ * re-fetches from the source site.
31
+ */
32
+ invalidateSnapshot(): void;
33
+ /**
34
+ * Get snapshot metadata (generated-at timestamp).
35
+ * Returns null if the DO has no snapshot loaded.
36
+ */
37
+ getSnapshotMeta(): {
38
+ generatedAt: string;
39
+ } | null;
40
+ /**
41
+ * Populate from a snapshot (preview mode).
42
+ *
43
+ * Fetches content from a source EmDash site and loads it into
44
+ * this DO's SQLite. Sets a TTL alarm for cleanup.
45
+ */
46
+ populateFromSnapshot(sourceUrl: string, signature: string, options?: {
47
+ drafts?: boolean;
48
+ ttl?: number;
49
+ }): Promise<{
50
+ generatedAt: string;
51
+ }>;
52
+ /**
53
+ * Set a cleanup alarm after the given number of seconds.
54
+ *
55
+ * Used by the playground middleware to set TTL after initialization
56
+ * is complete (initialization runs on the Worker side via RPC).
57
+ */
58
+ setTtlAlarm(ttlSeconds: number): void;
59
+ /**
60
+ * Alarm handler — clean up expired preview/playground data.
61
+ *
62
+ * Drops all user tables to reclaim storage.
63
+ */
64
+ alarm(): void;
65
+ /**
66
+ * Drop all user tables in the DO's SQLite database.
67
+ * Preserves SQLite and Cloudflare internal tables.
68
+ */
69
+ private dropAllTables;
70
+ private applySnapshot;
71
+ }
72
+ //#endregion
73
+ export { EmDashPreviewDB as t };
@@ -0,0 +1,58 @@
1
+ import { t as D1Introspector } from "./d1-introspector-bZf0_ylK.mjs";
2
+ import { SqliteAdapter, SqliteQueryCompiler } from "kysely";
3
+
4
+ //#region src/db/do-dialect.ts
5
+ var PreviewDODialect = class {
6
+ #config;
7
+ constructor(config) {
8
+ this.#config = config;
9
+ }
10
+ createAdapter() {
11
+ return new SqliteAdapter();
12
+ }
13
+ createDriver() {
14
+ return new PreviewDODriver(this.#config);
15
+ }
16
+ createQueryCompiler() {
17
+ return new SqliteQueryCompiler();
18
+ }
19
+ createIntrospector(db) {
20
+ return new D1Introspector(db);
21
+ }
22
+ };
23
+ var PreviewDODriver = class {
24
+ #config;
25
+ constructor(config) {
26
+ this.#config = config;
27
+ }
28
+ async init() {}
29
+ async acquireConnection() {
30
+ return new PreviewDOConnection(this.#config.getStub());
31
+ }
32
+ async beginTransaction() {}
33
+ async commitTransaction() {}
34
+ async rollbackTransaction() {}
35
+ async releaseConnection() {}
36
+ async destroy() {}
37
+ };
38
+ var PreviewDOConnection = class {
39
+ #stub;
40
+ constructor(stub) {
41
+ this.#stub = stub;
42
+ }
43
+ async executeQuery(compiledQuery) {
44
+ const sqlText = compiledQuery.sql;
45
+ const params = compiledQuery.parameters;
46
+ const result = await this.#stub.query(sqlText, params);
47
+ return {
48
+ rows: result.rows,
49
+ numAffectedRows: result.changes !== void 0 ? BigInt(result.changes) : void 0
50
+ };
51
+ }
52
+ async *streamQuery() {
53
+ throw new Error("Preview DO dialect does not support streaming");
54
+ }
55
+ };
56
+
57
+ //#endregion
58
+ export { PreviewDODialect as t };
@@ -0,0 +1,49 @@
1
+ //#region src/db/do-playground-routes.ts
2
+ /**
3
+ * Playground mode route gating.
4
+ *
5
+ * Unlike preview mode (which blocks everything except read-only API routes),
6
+ * playground mode allows most routes including the admin UI and write APIs.
7
+ * Only auth, setup, and abuse-prone routes are blocked.
8
+ *
9
+ * Pure function -- no Worker or Cloudflare dependencies.
10
+ */
11
+ /**
12
+ * Routes blocked in playground mode.
13
+ *
14
+ * These are either security-sensitive (auth, setup, tokens, OAuth),
15
+ * abuse-prone (media upload, plugin install), or pointless in a
16
+ * temporary playground (snapshot export, user management).
17
+ */
18
+ /**
19
+ * Auth routes that ARE allowed in playground mode.
20
+ * /auth/me is needed by the admin UI to identify the current user.
21
+ */
22
+ const AUTH_ALLOWLIST = new Set(["/_emdash/api/auth/me"]);
23
+ const BLOCKED_PREFIXES = [
24
+ "/_emdash/api/auth/",
25
+ "/_emdash/api/setup/",
26
+ "/_emdash/api/oauth/",
27
+ "/_emdash/api/tokens/",
28
+ "/_emdash/api/users/invite",
29
+ "/_emdash/api/plugins/install",
30
+ "/_emdash/api/plugins/marketplace",
31
+ "/_emdash/api/media/upload",
32
+ "/_emdash/api/snapshot"
33
+ ];
34
+ /**
35
+ * Check whether a request should be blocked in playground mode.
36
+ *
37
+ * Playground allows most CMS functionality: content CRUD, schema editing,
38
+ * taxonomies, menus, widgets, search, settings, and the full admin UI.
39
+ * Only auth, setup, user management, media uploads, and plugin
40
+ * installation are blocked.
41
+ */
42
+ function isBlockedInPlayground(pathname) {
43
+ if (AUTH_ALLOWLIST.has(pathname)) return false;
44
+ for (const prefix of BLOCKED_PREFIXES) if (pathname === prefix || pathname.startsWith(prefix)) return true;
45
+ return false;
46
+ }
47
+
48
+ //#endregion
49
+ export { isBlockedInPlayground as t };
@@ -0,0 +1,14 @@
1
+ //#region src/db/do-types.d.ts
2
+ /**
3
+ * Shared Durable Object config types (preview-only)
4
+ *
5
+ * Imported by both the config-time entry (index.ts) and the runtime entry (do.ts).
6
+ * This module must NOT import from cloudflare:workers so it stays safe at config time.
7
+ */
8
+ /** Durable Object preview database configuration */
9
+ interface PreviewDOConfig {
10
+ /** Wrangler binding name for the DO namespace */
11
+ binding: string;
12
+ }
13
+ //#endregion
14
+ export { PreviewDOConfig as t };
@@ -0,0 +1,76 @@
1
+ import { MediaProviderDescriptor } from "emdash/media";
2
+
3
+ //#region src/media/images.d.ts
4
+ /**
5
+ * Cloudflare Images configuration
6
+ */
7
+ interface CloudflareImagesConfig {
8
+ /**
9
+ * Cloudflare Account ID (for API calls)
10
+ * If not provided, reads from accountIdEnvVar at runtime
11
+ */
12
+ accountId?: string;
13
+ /**
14
+ * Environment variable name containing the Account ID
15
+ * @default "CF_ACCOUNT_ID"
16
+ */
17
+ accountIdEnvVar?: string;
18
+ /**
19
+ * Cloudflare Images Account Hash (for delivery URLs)
20
+ * This is different from the Account ID - find it in the Cloudflare dashboard
21
+ * under Images > Overview > "Account Hash"
22
+ * If not provided, reads from accountHashEnvVar at runtime
23
+ */
24
+ accountHash?: string;
25
+ /**
26
+ * Environment variable name containing the Account Hash
27
+ * @default "CF_IMAGES_ACCOUNT_HASH"
28
+ */
29
+ accountHashEnvVar?: string;
30
+ /**
31
+ * API Token with Images permissions
32
+ * If not provided, reads from apiTokenEnvVar at runtime
33
+ * Should have "Cloudflare Images: Read" and "Cloudflare Images: Edit" permissions
34
+ */
35
+ apiToken?: string;
36
+ /**
37
+ * Environment variable name containing the API token
38
+ * @default "CF_IMAGES_TOKEN"
39
+ */
40
+ apiTokenEnvVar?: string;
41
+ /**
42
+ * Custom delivery domain (optional)
43
+ * If not specified, uses imagedelivery.net
44
+ * @example "images.example.com"
45
+ */
46
+ deliveryDomain?: string;
47
+ /**
48
+ * Default variant to use for display
49
+ * @default "public"
50
+ */
51
+ defaultVariant?: string;
52
+ }
53
+ /**
54
+ * Cloudflare Images media provider
55
+ *
56
+ * @example
57
+ * ```ts
58
+ * import { cloudflareImages } from "@emdash-cms/cloudflare";
59
+ *
60
+ * emdash({
61
+ * mediaProviders: [
62
+ * // Uses CF_ACCOUNT_ID and CF_IMAGES_TOKEN env vars by default
63
+ * cloudflareImages({}),
64
+ *
65
+ * // Or with custom env var names
66
+ * cloudflareImages({
67
+ * accountIdEnvVar: "MY_CF_ACCOUNT",
68
+ * apiTokenEnvVar: "MY_CF_IMAGES_KEY",
69
+ * }),
70
+ * ],
71
+ * })
72
+ * ```
73
+ */
74
+ declare function cloudflareImages(config: CloudflareImagesConfig): MediaProviderDescriptor<CloudflareImagesConfig>;
75
+ //#endregion
76
+ export { cloudflareImages as n, CloudflareImagesConfig as t };
@@ -0,0 +1,200 @@
1
+ import { CloudflareCacheConfig } from "./cache/runtime.mjs";
2
+ import { cloudflareCache } from "./cache/config.mjs";
3
+ import { t as PreviewDOConfig } from "./do-types-CY0G0oyh.mjs";
4
+ import { n as cloudflareImages, t as CloudflareImagesConfig } from "./images-4RT9Ag8_.mjs";
5
+ import { n as cloudflareStream, t as CloudflareStreamConfig } from "./stream-DdbcvKi0.mjs";
6
+ import { AuthDescriptor, DatabaseDescriptor, StorageDescriptor } from "emdash";
7
+
8
+ //#region src/index.d.ts
9
+ /**
10
+ * D1 configuration
11
+ */
12
+ interface D1Config {
13
+ /**
14
+ * Name of the D1 binding in wrangler.toml
15
+ */
16
+ binding: string;
17
+ /**
18
+ * Read replication session mode.
19
+ *
20
+ * - `"disabled"` — No sessions. All queries go to primary. (default)
21
+ * - `"auto"` — Automatic session management. Anonymous requests use
22
+ * `"first-unconstrained"` (nearest replica). Authenticated requests
23
+ * use bookmark cookies for read-your-writes consistency.
24
+ * - `"primary-first"` — Like `"auto"`, but the first query in every
25
+ * session goes to the primary. Use this if your site has very
26
+ * frequent writes and you need stronger consistency guarantees
27
+ * at the cost of higher read latency.
28
+ *
29
+ * Read replication must also be enabled on the D1 database itself
30
+ * (via dashboard or REST API).
31
+ */
32
+ session?: "disabled" | "auto" | "primary-first";
33
+ /**
34
+ * Cookie name for storing the session bookmark.
35
+ * Only used when session is `"auto"` or `"primary-first"`.
36
+ *
37
+ * @default "__ec_d1_bookmark"
38
+ */
39
+ bookmarkCookie?: string;
40
+ }
41
+ /**
42
+ * R2 storage configuration
43
+ */
44
+ interface R2StorageConfig {
45
+ /**
46
+ * Name of the R2 binding in wrangler.toml
47
+ */
48
+ binding: string;
49
+ /**
50
+ * Public URL for accessing files (optional CDN)
51
+ */
52
+ publicUrl?: string;
53
+ }
54
+ /**
55
+ * Configuration for Cloudflare Access authentication
56
+ */
57
+ interface AccessConfig {
58
+ /**
59
+ * Your Cloudflare Access team domain
60
+ * @example "myteam.cloudflareaccess.com"
61
+ */
62
+ teamDomain: string;
63
+ /**
64
+ * Application Audience (AUD) tag from Access application settings.
65
+ * For Cloudflare Workers, use `audienceEnvVar` instead to read at runtime.
66
+ */
67
+ audience?: string;
68
+ /**
69
+ * Environment variable name containing the audience tag.
70
+ * Read at runtime from environment.
71
+ * @default "CF_ACCESS_AUDIENCE"
72
+ */
73
+ audienceEnvVar?: string;
74
+ /**
75
+ * Automatically create EmDash users on first login
76
+ * @default true
77
+ */
78
+ autoProvision?: boolean;
79
+ /**
80
+ * Role level for users not matching any group in roleMapping
81
+ * @default 30 (Editor)
82
+ */
83
+ defaultRole?: number;
84
+ /**
85
+ * Update user's role on each login based on current IdP groups
86
+ * When false, role is only set on first provisioning
87
+ * @default false
88
+ */
89
+ syncRoles?: boolean;
90
+ /**
91
+ * Map IdP group names to EmDash role levels
92
+ * First match wins if user is in multiple groups
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * roleMapping: {
97
+ * "Admins": 50, // Admin
98
+ * "Developers": 40, // Developer
99
+ * "Content Team": 30, // Editor
100
+ * }
101
+ * ```
102
+ */
103
+ roleMapping?: Record<string, number>;
104
+ }
105
+ /**
106
+ * Cloudflare D1 database adapter
107
+ *
108
+ * For Cloudflare Workers with D1 binding.
109
+ * Migrations run automatically at setup time - no need for manual SQL files.
110
+ *
111
+ * Uses a custom introspector that works around D1's restriction on
112
+ * cross-joins with pragma_table_info().
113
+ *
114
+ * @example
115
+ * ```ts
116
+ * database: d1({ binding: "DB" })
117
+ * ```
118
+ */
119
+ declare function d1(config: D1Config): DatabaseDescriptor;
120
+ /**
121
+ * Durable Object preview database adapter
122
+ *
123
+ * Each preview session gets an isolated SQLite database inside a DO,
124
+ * populated from a snapshot of the source EmDash site.
125
+ *
126
+ * Not for production use — preview only.
127
+ *
128
+ * @example
129
+ * ```ts
130
+ * database: previewDatabase({ binding: "PREVIEW_DB" })
131
+ * ```
132
+ */
133
+ declare function previewDatabase(config: PreviewDOConfig): DatabaseDescriptor;
134
+ /**
135
+ * Durable Object playground database adapter
136
+ *
137
+ * Each playground session gets an isolated SQLite database inside a DO,
138
+ * populated from a seed file with migrations run at init time.
139
+ * Unlike preview, playground is writable and has admin access.
140
+ *
141
+ * Not for production use -- playground/demo only.
142
+ *
143
+ * @example
144
+ * ```ts
145
+ * database: playgroundDatabase({ binding: "PLAYGROUND_DB" })
146
+ * ```
147
+ */
148
+ declare function playgroundDatabase(config: PreviewDOConfig): DatabaseDescriptor;
149
+ /**
150
+ * Cloudflare R2 binding adapter
151
+ *
152
+ * Uses R2 bindings directly when running on Cloudflare Workers.
153
+ * Does NOT support signed upload URLs (use s3() with R2 credentials instead).
154
+ *
155
+ * Requires R2 binding in wrangler.toml:
156
+ * ```toml
157
+ * [[r2_buckets]]
158
+ * binding = "MEDIA"
159
+ * bucket_name = "my-media-bucket"
160
+ * ```
161
+ *
162
+ * @example
163
+ * ```ts
164
+ * storage: r2({ binding: "MEDIA" })
165
+ * ```
166
+ */
167
+ declare function r2(config: R2StorageConfig): StorageDescriptor;
168
+ /**
169
+ * Cloudflare Access authentication adapter
170
+ *
171
+ * Use this to configure EmDash to authenticate via Cloudflare Access.
172
+ * When Access is configured, passkey auth is disabled.
173
+ *
174
+ * @example
175
+ * ```ts
176
+ * auth: access({
177
+ * teamDomain: "myteam.cloudflareaccess.com",
178
+ * audience: "abc123...",
179
+ * roleMapping: {
180
+ * "Admins": 50,
181
+ * "Editors": 30,
182
+ * },
183
+ * })
184
+ * ```
185
+ */
186
+ declare function access(config: AccessConfig): AuthDescriptor;
187
+ /**
188
+ * Cloudflare Worker Loader sandbox adapter
189
+ *
190
+ * Returns the module path for the Cloudflare sandbox runner.
191
+ * Use this in the `sandboxRunner` config option.
192
+ *
193
+ * @example
194
+ * ```ts
195
+ * sandboxRunner: sandbox()
196
+ * ```
197
+ */
198
+ declare function sandbox(): string;
199
+ //#endregion
200
+ export { AccessConfig, type CloudflareCacheConfig, type CloudflareImagesConfig, type CloudflareStreamConfig, D1Config, type PreviewDOConfig, R2StorageConfig, access, cloudflareCache, cloudflareImages, cloudflareStream, d1, playgroundDatabase, previewDatabase, r2, sandbox };