@b9g/platform-cloudflare 0.1.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/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@b9g/platform-cloudflare",
3
+ "version": "0.1.0",
4
+ "description": "Cloudflare Workers platform adapter for Shovel - already ServiceWorker-based!",
5
+ "keywords": [
6
+ "shovel",
7
+ "platform",
8
+ "cloudflare",
9
+ "workers",
10
+ "edge",
11
+ "serviceworker"
12
+ ],
13
+ "dependencies": {
14
+ "@b9g/platform": "workspace:*",
15
+ "@b9g/cache": "workspace:*",
16
+ "@b9g/assets": "workspace:*",
17
+ "@cloudflare/workers-types": "^4.20241218.0"
18
+ },
19
+ "devDependencies": {
20
+ "@b9g/libuild": "^0.1.10",
21
+ "bun-types": "latest"
22
+ },
23
+ "type": "module",
24
+ "types": "src/index.d.ts",
25
+ "module": "src/index.js",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./src/index.d.ts",
29
+ "import": "./src/index.js"
30
+ },
31
+ "./platform": {
32
+ "types": "./src/platform.d.ts",
33
+ "import": "./src/platform.js"
34
+ },
35
+ "./platform.js": {
36
+ "types": "./src/platform.d.ts",
37
+ "import": "./src/platform.js"
38
+ },
39
+ "./wrangler": {
40
+ "types": "./src/wrangler.d.ts",
41
+ "import": "./src/wrangler.js"
42
+ },
43
+ "./package.json": "./package.json",
44
+ "./index": {
45
+ "types": "./src/index.d.ts",
46
+ "import": "./src/index.js"
47
+ },
48
+ "./index.js": {
49
+ "types": "./src/index.d.ts",
50
+ "import": "./src/index.js"
51
+ },
52
+ "./wrangler.js": {
53
+ "types": "./src/wrangler.d.ts",
54
+ "import": "./src/wrangler.js"
55
+ },
56
+ "./wrapper": {
57
+ "types": "./src/wrapper.d.ts",
58
+ "import": "./src/wrapper.js"
59
+ },
60
+ "./wrapper.js": {
61
+ "types": "./src/wrapper.d.ts",
62
+ "import": "./src/wrapper.js"
63
+ }
64
+ }
65
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @b9g/platform-cloudflare - Cloudflare Workers platform adapter for Shovel
3
+ *
4
+ * Provides ServiceWorker-native deployment for Cloudflare Workers with KV/R2/D1 integration.
5
+ */
6
+ export { CloudflarePlatform, createCloudflarePlatform, type CloudflarePlatformOptions, } from "./platform.js";
7
+ export { createOptionsFromEnv, generateWranglerConfig } from "./wrangler.js";
8
+ export { cloudflareWorkerBanner, cloudflareWorkerFooter } from "./wrapper.js";
9
+ export type { Platform, CacheConfig, StaticConfig, Handler, Server, ServerOptions, ServiceWorkerOptions, ServiceWorkerInstance, } from "@b9g/platform";
package/src/index.js ADDED
@@ -0,0 +1,16 @@
1
+ /// <reference types="./index.d.ts" />
2
+ // src/index.ts
3
+ import {
4
+ CloudflarePlatform,
5
+ createCloudflarePlatform
6
+ } from "./platform.js";
7
+ import { createOptionsFromEnv, generateWranglerConfig } from "./wrangler.js";
8
+ import { cloudflareWorkerBanner, cloudflareWorkerFooter } from "./wrapper.js";
9
+ export {
10
+ CloudflarePlatform,
11
+ cloudflareWorkerBanner,
12
+ cloudflareWorkerFooter,
13
+ createCloudflarePlatform,
14
+ createOptionsFromEnv,
15
+ generateWranglerConfig
16
+ };
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Cloudflare Workers platform implementation for Shovel
3
+ *
4
+ * Uses bundled adapters to avoid dynamic imports in Workers environment
5
+ * Supports KV for caching and R2 for filesystem operations
6
+ */
7
+ import { BasePlatform, PlatformConfig, CacheConfig, Handler, Server, ServerOptions, ServiceWorkerOptions, ServiceWorkerInstance } from "@b9g/platform";
8
+ export interface CloudflarePlatformOptions extends PlatformConfig {
9
+ /** Cloudflare Workers environment (production, preview, dev) */
10
+ environment?: "production" | "preview" | "dev";
11
+ /** KV namespace bindings */
12
+ kvNamespaces?: Record<string, any>;
13
+ /** R2 bucket bindings */
14
+ r2Buckets?: Record<string, any>;
15
+ /** D1 database bindings */
16
+ d1Databases?: Record<string, any>;
17
+ /** Durable Object bindings */
18
+ durableObjects?: Record<string, any>;
19
+ }
20
+ /**
21
+ * Cloudflare Workers platform implementation
22
+ */
23
+ export declare class CloudflarePlatform extends BasePlatform {
24
+ readonly name = "cloudflare";
25
+ private options;
26
+ private _dist?;
27
+ constructor(options?: CloudflarePlatformOptions);
28
+ /**
29
+ * Build artifacts filesystem (not available in Workers runtime)
30
+ */
31
+ get distDir(): FileSystemDirectoryHandle;
32
+ /**
33
+ * Get platform-specific default cache configuration for Cloudflare Workers
34
+ */
35
+ protected getDefaultCacheConfig(): CacheConfig;
36
+ /**
37
+ * Override cache creation to use bundled KV adapter
38
+ */
39
+ createCaches(config?: CacheConfig): Promise<CacheStorage>;
40
+ /**
41
+ * Create "server" for Cloudflare Workers (which is really just the handler)
42
+ */
43
+ createServer(handler: Handler, _options?: ServerOptions): Server;
44
+ /**
45
+ * Load ServiceWorker-style entrypoint in Cloudflare Workers
46
+ *
47
+ * Cloudflare Workers are already ServiceWorker-based, so we can use
48
+ * the global environment directly in production
49
+ */
50
+ loadServiceWorker(entrypoint: string, options?: ServiceWorkerOptions): Promise<ServiceWorkerInstance>;
51
+ /**
52
+ * Get filesystem root for File System Access API
53
+ */
54
+ getFileSystemRoot(name?: string): Promise<FileSystemDirectoryHandle>;
55
+ /**
56
+ * Dispose of platform resources
57
+ */
58
+ dispose(): Promise<void>;
59
+ }
60
+ /**
61
+ * Create a Cloudflare platform instance
62
+ */
63
+ export declare function createCloudflarePlatform(options?: CloudflarePlatformOptions): CloudflarePlatform;
64
+ /**
65
+ * Default export for easy importing
66
+ */
67
+ export default createCloudflarePlatform;
@@ -0,0 +1,136 @@
1
+ /// <reference types="./platform.d.ts" />
2
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
+ }) : x)(function(x) {
5
+ if (typeof require !== "undefined")
6
+ return require.apply(this, arguments);
7
+ throw Error('Dynamic require of "' + x + '" is not supported');
8
+ });
9
+
10
+ // src/platform.ts
11
+ import {
12
+ BasePlatform
13
+ } from "@b9g/platform";
14
+ import { FileSystemRegistry, getFileSystemRoot, MemoryFileSystemAdapter } from "@b9g/filesystem";
15
+ var CloudflarePlatform = class extends BasePlatform {
16
+ name = "cloudflare";
17
+ options;
18
+ _dist;
19
+ constructor(options = {}) {
20
+ super(options);
21
+ this.options = {
22
+ environment: "production",
23
+ kvNamespaces: {},
24
+ r2Buckets: {},
25
+ d1Databases: {},
26
+ durableObjects: {},
27
+ ...options
28
+ };
29
+ FileSystemRegistry.register("memory", new MemoryFileSystemAdapter());
30
+ if (this.options.r2Buckets?.default) {
31
+ try {
32
+ const { R2FileSystemAdapter } = __require("@b9g/filesystem-r2");
33
+ FileSystemRegistry.register("r2", new R2FileSystemAdapter(this.options.r2Buckets.default));
34
+ } catch {
35
+ console.warn("[Cloudflare] R2 adapter not available, using memory filesystem");
36
+ }
37
+ }
38
+ }
39
+ /**
40
+ * Build artifacts filesystem (not available in Workers runtime)
41
+ */
42
+ get distDir() {
43
+ if (!this._dist) {
44
+ this._dist = new MemoryFileSystemAdapter().getFileSystemRoot("dist");
45
+ }
46
+ return this._dist;
47
+ }
48
+ /**
49
+ * Get platform-specific default cache configuration for Cloudflare Workers
50
+ */
51
+ getDefaultCacheConfig() {
52
+ return {
53
+ pages: { type: "cloudflare" },
54
+ // Use Cloudflare's native Cache API
55
+ api: { type: "cloudflare" },
56
+ // Use Cloudflare's native Cache API
57
+ static: { type: "cloudflare" }
58
+ // Static files handled by CDN
59
+ };
60
+ }
61
+ /**
62
+ * Override cache creation to use bundled KV adapter
63
+ */
64
+ async createCaches(config) {
65
+ return globalThis.caches;
66
+ }
67
+ /**
68
+ * Create "server" for Cloudflare Workers (which is really just the handler)
69
+ */
70
+ createServer(handler, _options = {}) {
71
+ return {
72
+ listen: () => {
73
+ console.info("[Cloudflare] Worker handler ready");
74
+ return Promise.resolve();
75
+ },
76
+ close: () => {
77
+ console.info("[Cloudflare] Worker handler stopped");
78
+ return Promise.resolve();
79
+ },
80
+ address: () => ({ port: 0, host: "cloudflare-workers" })
81
+ };
82
+ }
83
+ /**
84
+ * Load ServiceWorker-style entrypoint in Cloudflare Workers
85
+ *
86
+ * Cloudflare Workers are already ServiceWorker-based, so we can use
87
+ * the global environment directly in production
88
+ */
89
+ async loadServiceWorker(entrypoint, options = {}) {
90
+ const isCloudflareWorker = typeof globalThis.addEventListener === "function" && typeof globalThis.caches !== "undefined" && typeof globalThis.FetchEvent !== "undefined";
91
+ if (isCloudflareWorker) {
92
+ console.info("[Cloudflare] Running in native ServiceWorker environment");
93
+ const instance = {
94
+ runtime: globalThis,
95
+ handleRequest: async (request) => {
96
+ const event = new FetchEvent("fetch", { request });
97
+ globalThis.dispatchEvent(event);
98
+ return new Response("Worker handler", { status: 200 });
99
+ },
100
+ install: () => Promise.resolve(),
101
+ activate: () => Promise.resolve(),
102
+ collectStaticRoutes: async () => [],
103
+ // Not supported in Workers
104
+ get ready() {
105
+ return true;
106
+ },
107
+ dispose: async () => {
108
+ }
109
+ };
110
+ await import(entrypoint);
111
+ return instance;
112
+ } else {
113
+ throw new Error("Cloudflare platform development mode not yet implemented. Use Node platform for development.");
114
+ }
115
+ }
116
+ /**
117
+ * Get filesystem root for File System Access API
118
+ */
119
+ async getFileSystemRoot(name = "default") {
120
+ return await getFileSystemRoot(name);
121
+ }
122
+ /**
123
+ * Dispose of platform resources
124
+ */
125
+ async dispose() {
126
+ }
127
+ };
128
+ function createCloudflarePlatform(options) {
129
+ return new CloudflarePlatform(options);
130
+ }
131
+ var platform_default = createCloudflarePlatform;
132
+ export {
133
+ CloudflarePlatform,
134
+ createCloudflarePlatform,
135
+ platform_default as default
136
+ };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Wrangler integration utilities for Cloudflare Workers
3
+ */
4
+ import type { CloudflarePlatformOptions } from "./platform.js";
5
+ /**
6
+ * Create platform options from Wrangler environment
7
+ */
8
+ export declare function createOptionsFromEnv(env: any): CloudflarePlatformOptions;
9
+ /**
10
+ * Generate wrangler.toml configuration for a Shovel app from CLI flags
11
+ */
12
+ export declare function generateWranglerConfig(options: {
13
+ name: string;
14
+ entrypoint: string;
15
+ cacheAdapter?: string;
16
+ filesystemAdapter?: string;
17
+ kvNamespaces?: string[];
18
+ r2Buckets?: string[];
19
+ d1Databases?: string[];
20
+ }): string;
@@ -0,0 +1,95 @@
1
+ /// <reference types="./wrangler.d.ts" />
2
+ // src/wrangler.ts
3
+ function createOptionsFromEnv(env) {
4
+ return {
5
+ environment: env.ENVIRONMENT || "production",
6
+ kvNamespaces: extractKVNamespaces(env),
7
+ r2Buckets: extractR2Buckets(env),
8
+ d1Databases: extractD1Databases(env),
9
+ durableObjects: extractDurableObjects(env)
10
+ };
11
+ }
12
+ function extractKVNamespaces(env) {
13
+ const kvNamespaces = {};
14
+ for (const [key, value] of Object.entries(env)) {
15
+ if (key.endsWith("_KV") || key.includes("KV")) {
16
+ kvNamespaces[key] = value;
17
+ }
18
+ }
19
+ return kvNamespaces;
20
+ }
21
+ function extractR2Buckets(env) {
22
+ const r2Buckets = {};
23
+ for (const [key, value] of Object.entries(env)) {
24
+ if (key.endsWith("_R2") || key.includes("R2")) {
25
+ r2Buckets[key] = value;
26
+ }
27
+ }
28
+ return r2Buckets;
29
+ }
30
+ function extractD1Databases(env) {
31
+ const d1Databases = {};
32
+ for (const [key, value] of Object.entries(env)) {
33
+ if (key.endsWith("_D1") || key.includes("D1") || key.endsWith("_DB")) {
34
+ d1Databases[key] = value;
35
+ }
36
+ }
37
+ return d1Databases;
38
+ }
39
+ function extractDurableObjects(env) {
40
+ const durableObjects = {};
41
+ for (const [key, value] of Object.entries(env)) {
42
+ if (key.endsWith("_DO") || key.includes("DURABLE")) {
43
+ durableObjects[key] = value;
44
+ }
45
+ }
46
+ return durableObjects;
47
+ }
48
+ function generateWranglerConfig(options) {
49
+ const {
50
+ name,
51
+ entrypoint,
52
+ cacheAdapter,
53
+ filesystemAdapter,
54
+ kvNamespaces = [],
55
+ r2Buckets = [],
56
+ d1Databases = []
57
+ } = options;
58
+ const autoKVNamespaces = [];
59
+ const autoR2Buckets = filesystemAdapter === "r2" ? ["STORAGE_R2"] : [];
60
+ const allKVNamespaces = [.../* @__PURE__ */ new Set([...kvNamespaces, ...autoKVNamespaces])];
61
+ const allR2Buckets = [.../* @__PURE__ */ new Set([...r2Buckets, ...autoR2Buckets])];
62
+ return `# Generated wrangler.toml for Shovel app
63
+ name = "${name}"
64
+ main = "${entrypoint}"
65
+ compatibility_date = "2024-01-01"
66
+
67
+ # ServiceWorker format (since Shovel apps are ServiceWorker-style)
68
+ usage_model = "bundled"
69
+
70
+ # KV bindings${allKVNamespaces.length > 0 ? "\n" + allKVNamespaces.map(
71
+ (kv) => `[[kv_namespaces]]
72
+ binding = "${kv}"
73
+ id = "your-kv-namespace-id"
74
+ preview_id = "your-preview-kv-namespace-id"`
75
+ ).join("\n\n") : ""}
76
+
77
+ # R2 bindings${allR2Buckets.length > 0 ? "\n" + allR2Buckets.map(
78
+ (bucket) => `[[r2_buckets]]
79
+ binding = "${bucket}"
80
+ bucket_name = "your-bucket-name"`
81
+ ).join("\n\n") : ""}
82
+
83
+ # D1 bindings
84
+ ${d1Databases.map(
85
+ (db) => `[[d1_databases]]
86
+ binding = "${db}"
87
+ database_name = "your-database-name"
88
+ database_id = "your-database-id"`
89
+ ).join("\n\n")}
90
+ `;
91
+ }
92
+ export {
93
+ createOptionsFromEnv,
94
+ generateWranglerConfig
95
+ };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Cloudflare Workers ES Module wrapper
3
+ * Converts ServiceWorker code to ES Module format for Cloudflare Workers
4
+ */
5
+ /**
6
+ * Generate banner code for ServiceWorker → ES Module conversion
7
+ */
8
+ export declare const cloudflareWorkerBanner = "// Cloudflare Worker ES Module wrapper\nlet serviceWorkerGlobals = null;\n\n// Set up ServiceWorker environment\nif (typeof globalThis.self === 'undefined') {\n\tglobalThis.self = globalThis;\n}\n\n// Capture fetch event handlers\nconst fetchHandlers = [];\nconst originalAddEventListener = globalThis.addEventListener;\nglobalThis.addEventListener = function(type, handler, options) {\n\tif (type === 'fetch') {\n\t\tfetchHandlers.push(handler);\n\t} else {\n\t\toriginalAddEventListener?.call(this, type, handler, options);\n\t}\n};\n\n// Create a promise-based FetchEvent that can be awaited\nclass FetchEvent {\n\tconstructor(type, init) {\n\t\tthis.type = type;\n\t\tthis.request = init.request;\n\t\tthis._response = null;\n\t\tthis._responsePromise = new Promise((resolve) => {\n\t\t\tthis._resolveResponse = resolve;\n\t\t});\n\t}\n\t\n\trespondWith(response) {\n\t\tthis._response = response;\n\t\tthis._resolveResponse(response);\n\t}\n\t\n\tasync waitUntil(promise) {\n\t\tawait promise;\n\t}\n}";
9
+ /**
10
+ * Generate footer code for ServiceWorker → ES Module conversion
11
+ */
12
+ export declare const cloudflareWorkerFooter = "\n// Export ES Module for Cloudflare Workers\nexport default {\n\tasync fetch(request, env, ctx) {\n\t\ttry {\n\t\t\t// Set up ServiceWorker-like dirs API for bundled deployment\n\t\t\tif (!globalThis.self.dirs) {\n\t\t\t\t// For bundled deployment, assets are served via static middleware\n\t\t\t\t// not through the dirs API\n\t\t\t\tglobalThis.self.dirs = {\n\t\t\t\t\tasync open(directoryName) {\n\t\t\t\t\t\tif (directoryName === 'assets') {\n\t\t\t\t\t\t\t// Return a minimal interface that indicates no files available\n\t\t\t\t\t\t\t// The assets middleware will fall back to dev mode behavior\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tasync getFileHandle(fileName) {\n\t\t\t\t\t\t\t\t\tthrow new Error(`NotFoundError: ${fileName} not found in bundled assets`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthrow new Error(`Directory ${directoryName} not available in bundled deployment`);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t\t\n\t\t\t// Set up caches API\n\t\t\tif (!globalThis.self.caches) {\n\t\t\t\tglobalThis.self.caches = globalThis.caches;\n\t\t\t}\n\t\t\t\n\t\t\t// Ensure request.url is a string\n\t\t\tif (typeof request.url !== 'string') {\n\t\t\t\treturn new Response('Invalid request URL: ' + typeof request.url, { status: 500 });\n\t\t\t}\n\t\t\t\n\t\t\t// Create proper FetchEvent-like object\n\t\t\tlet responseReceived = null;\n\t\t\tconst event = { \n\t\t\t\trequest, \n\t\t\t\trespondWith: (response) => { responseReceived = response; }\n\t\t\t};\n\t\t\t\n\t\t\t// Dispatch to ServiceWorker fetch handlers\n\t\t\tfor (const handler of fetchHandlers) {\n\t\t\t\ttry {\n\t\t\t\t\tconsole.log('[Wrapper] Calling handler for:', request.url);\n\t\t\t\t\tawait handler(event);\n\t\t\t\t\tconsole.log('[Wrapper] Handler completed, response:', !!responseReceived);\n\t\t\t\t\tif (responseReceived) {\n\t\t\t\t\t\treturn responseReceived;\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error('[Wrapper] Handler error:', error);\n\t\t\t\t\tconsole.error('[Wrapper] Error stack:', error.stack);\n\t\t\t\t\t// Return detailed error in response body for debugging\n\t\t\t\t\treturn new Response(JSON.stringify({\n\t\t\t\t\t\terror: error.message,\n\t\t\t\t\t\tstack: error.stack,\n\t\t\t\t\t\tname: error.name,\n\t\t\t\t\t\turl: request.url\n\t\t\t\t\t}, null, 2), { \n\t\t\t\t\t\tstatus: 500,\n\t\t\t\t\t\theaders: { 'Content-Type': 'application/json' }\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\treturn new Response('No ServiceWorker handler', { status: 404 });\n\t\t} catch (topLevelError) {\n\t\t\tconsole.error('[Wrapper] Top-level error:', topLevelError);\n\t\t\treturn new Response(JSON.stringify({\n\t\t\t\terror: 'Top-level wrapper error: ' + topLevelError.message,\n\t\t\t\tstack: topLevelError.stack,\n\t\t\t\tname: topLevelError.name,\n\t\t\t\turl: request?.url || 'unknown'\n\t\t\t}, null, 2), { \n\t\t\t\tstatus: 500,\n\t\t\t\theaders: { 'Content-Type': 'application/json' }\n\t\t\t});\n\t\t}\n\t}\n};";
package/src/wrapper.js ADDED
@@ -0,0 +1,127 @@
1
+ /// <reference types="./wrapper.d.ts" />
2
+ // src/wrapper.ts
3
+ var cloudflareWorkerBanner = `// Cloudflare Worker ES Module wrapper
4
+ let serviceWorkerGlobals = null;
5
+
6
+ // Set up ServiceWorker environment
7
+ if (typeof globalThis.self === 'undefined') {
8
+ globalThis.self = globalThis;
9
+ }
10
+
11
+ // Capture fetch event handlers
12
+ const fetchHandlers = [];
13
+ const originalAddEventListener = globalThis.addEventListener;
14
+ globalThis.addEventListener = function(type, handler, options) {
15
+ if (type === 'fetch') {
16
+ fetchHandlers.push(handler);
17
+ } else {
18
+ originalAddEventListener?.call(this, type, handler, options);
19
+ }
20
+ };
21
+
22
+ // Create a promise-based FetchEvent that can be awaited
23
+ class FetchEvent {
24
+ constructor(type, init) {
25
+ this.type = type;
26
+ this.request = init.request;
27
+ this._response = null;
28
+ this._responsePromise = new Promise((resolve) => {
29
+ this._resolveResponse = resolve;
30
+ });
31
+ }
32
+
33
+ respondWith(response) {
34
+ this._response = response;
35
+ this._resolveResponse(response);
36
+ }
37
+
38
+ async waitUntil(promise) {
39
+ await promise;
40
+ }
41
+ }`;
42
+ var cloudflareWorkerFooter = `
43
+ // Export ES Module for Cloudflare Workers
44
+ export default {
45
+ async fetch(request, env, ctx) {
46
+ try {
47
+ // Set up ServiceWorker-like dirs API for bundled deployment
48
+ if (!globalThis.self.dirs) {
49
+ // For bundled deployment, assets are served via static middleware
50
+ // not through the dirs API
51
+ globalThis.self.dirs = {
52
+ async open(directoryName) {
53
+ if (directoryName === 'assets') {
54
+ // Return a minimal interface that indicates no files available
55
+ // The assets middleware will fall back to dev mode behavior
56
+ return {
57
+ async getFileHandle(fileName) {
58
+ throw new Error(\`NotFoundError: \${fileName} not found in bundled assets\`);
59
+ }
60
+ };
61
+ }
62
+ throw new Error(\`Directory \${directoryName} not available in bundled deployment\`);
63
+ }
64
+ };
65
+ }
66
+
67
+ // Set up caches API
68
+ if (!globalThis.self.caches) {
69
+ globalThis.self.caches = globalThis.caches;
70
+ }
71
+
72
+ // Ensure request.url is a string
73
+ if (typeof request.url !== 'string') {
74
+ return new Response('Invalid request URL: ' + typeof request.url, { status: 500 });
75
+ }
76
+
77
+ // Create proper FetchEvent-like object
78
+ let responseReceived = null;
79
+ const event = {
80
+ request,
81
+ respondWith: (response) => { responseReceived = response; }
82
+ };
83
+
84
+ // Dispatch to ServiceWorker fetch handlers
85
+ for (const handler of fetchHandlers) {
86
+ try {
87
+ console.log('[Wrapper] Calling handler for:', request.url);
88
+ await handler(event);
89
+ console.log('[Wrapper] Handler completed, response:', !!responseReceived);
90
+ if (responseReceived) {
91
+ return responseReceived;
92
+ }
93
+ } catch (error) {
94
+ console.error('[Wrapper] Handler error:', error);
95
+ console.error('[Wrapper] Error stack:', error.stack);
96
+ // Return detailed error in response body for debugging
97
+ return new Response(JSON.stringify({
98
+ error: error.message,
99
+ stack: error.stack,
100
+ name: error.name,
101
+ url: request.url
102
+ }, null, 2), {
103
+ status: 500,
104
+ headers: { 'Content-Type': 'application/json' }
105
+ });
106
+ }
107
+ }
108
+
109
+ return new Response('No ServiceWorker handler', { status: 404 });
110
+ } catch (topLevelError) {
111
+ console.error('[Wrapper] Top-level error:', topLevelError);
112
+ return new Response(JSON.stringify({
113
+ error: 'Top-level wrapper error: ' + topLevelError.message,
114
+ stack: topLevelError.stack,
115
+ name: topLevelError.name,
116
+ url: request?.url || 'unknown'
117
+ }, null, 2), {
118
+ status: 500,
119
+ headers: { 'Content-Type': 'application/json' }
120
+ });
121
+ }
122
+ }
123
+ };`;
124
+ export {
125
+ cloudflareWorkerBanner,
126
+ cloudflareWorkerFooter
127
+ };