@openworkers/adapter-static 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/README.md ADDED
@@ -0,0 +1,113 @@
1
+ # @openworkers/adapter-static
2
+
3
+ Static site adapter for OpenWorkers. Converts any static site into a deployable worker with optimized asset serving.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add -g @openworkers/adapter-static
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```bash
14
+ # Auto-detect input folder and generate worker
15
+ adapter-static
16
+
17
+ # Specify input and output directories
18
+ adapter-static ./build -o ./dist
19
+
20
+ # Custom 404 page
21
+ adapter-static --fallback /404.html
22
+ ```
23
+
24
+ ## CLI Options
25
+
26
+ | Option | Flag | Default | Description |
27
+ | --------- | ---------------- | ------------------ | --------------------------------------------------- |
28
+ | Input | positional | auto-detect | Source directory (`dist`, `build`, `out`, `public`) |
29
+ | Output | `-o, --out` | `dist-openworkers` | Output directory |
30
+ | Mode | `-m, --mode` | auto-detect | Routing mode: `directory` or `flat` |
31
+ | Fallback | `-f, --fallback` | none | Fallback file for unmatched routes |
32
+ | Immutable | `--immutable` | auto-detect | Comma-separated immutable path patterns |
33
+
34
+ ## Programmatic Usage
35
+
36
+ ```js
37
+ import { adapt } from '@openworkers/adapter-static';
38
+
39
+ await adapt({
40
+ input: 'build',
41
+ out: 'dist',
42
+ mode: 'flat',
43
+ fallback: '/404.html'
44
+ });
45
+ ```
46
+
47
+ ## Output Structure
48
+
49
+ ```
50
+ dist/
51
+ ├── worker.js # Worker serving files via ASSETS binding
52
+ ├── assets/ # Static files
53
+ └── routes.js # Route manifest
54
+ ```
55
+
56
+ ## Routing Modes
57
+
58
+ The adapter auto-detects the routing mode based on your file structure.
59
+
60
+ | Mode | URL | File |
61
+ | ------------- | -------- | ------------------- |
62
+ | **Directory** | `/about` | `/about/index.html` |
63
+ | **Flat** | `/about` | `/about.html` |
64
+
65
+ Directory mode is used by most static generators. Flat mode is common with SvelteKit static adapter.
66
+
67
+ ## Immutable Assets
68
+
69
+ Assets with hashed filenames are served with long cache headers (`max-age=31536000, immutable`).
70
+
71
+ Auto-detected patterns:
72
+
73
+ - `/_app/immutable/*` — SvelteKit
74
+ - `/assets/*` — Vite
75
+ - `/_next/static/*` — Next.js
76
+ - `/_astro/*` — Astro
77
+
78
+ ## Deploy to OpenWorkers
79
+
80
+ **Quick deploy** (existing worker):
81
+
82
+ ```bash
83
+ ow workers upload my-site ./dist
84
+ ```
85
+
86
+ **Full setup**:
87
+
88
+ ```bash
89
+ # 1. Create storage for assets
90
+ ow storage create my-site-assets --provider platform
91
+
92
+ # 2. Create environment
93
+ ow env create my-site-env
94
+
95
+ # 3. Bind storage to environment as ASSETS
96
+ ow env bind my-site-env ASSETS my-site-assets -t assets
97
+
98
+ # 4. Create worker
99
+ ow workers create my-site
100
+
101
+ # 5. Link environment to worker
102
+ ow workers link my-site my-site-env
103
+
104
+ # 6. Build and upload
105
+ adapter-static ./build -o ./dist
106
+ ow workers upload my-site ./dist
107
+ ```
108
+
109
+ Your site is live at `https://my-site.workers.rocks`
110
+
111
+ ## License
112
+
113
+ MIT
package/bin/cli.js ADDED
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { adapt } from '../index.js';
4
+
5
+ const args = process.argv.slice(2);
6
+
7
+ /** @type {import('../index.js').AdapterOptions} */
8
+ const options = {};
9
+
10
+ for (let i = 0; i < args.length; i++) {
11
+ const arg = args[i];
12
+
13
+ if (arg === '--input' || arg === '-i') {
14
+ options.input = args[++i];
15
+ } else if (arg === '--out' || arg === '-o') {
16
+ options.out = args[++i];
17
+ } else if (arg === '--mode' || arg === '-m') {
18
+ const mode = args[++i];
19
+
20
+ if (mode !== 'directory' && mode !== 'flat') {
21
+ console.error(`Invalid mode: ${mode}. Must be 'directory' or 'flat'.`);
22
+ process.exit(1);
23
+ }
24
+
25
+ options.mode = mode;
26
+ } else if (arg === '--fallback' || arg === '-f') {
27
+ options.fallback = args[++i];
28
+ } else if (arg === '--immutable') {
29
+ options.immutable = args[++i].split(',');
30
+ } else if (arg === '--help' || arg === '-h') {
31
+ printHelp();
32
+ process.exit(0);
33
+ } else if (arg === '--version' || arg === '-v') {
34
+ const pkg = await import('../package.json', { with: { type: 'json' } });
35
+ console.log(pkg.default.version);
36
+ process.exit(0);
37
+ } else if (!arg.startsWith('-')) {
38
+ // Positional argument = input directory
39
+ options.input = arg;
40
+ } else {
41
+ console.error(`Unknown option: ${arg}`);
42
+ printHelp();
43
+ process.exit(1);
44
+ }
45
+ }
46
+
47
+ try {
48
+ await adapt(options);
49
+ } catch (error) {
50
+ console.error('Error:', error instanceof Error ? error.message : error);
51
+ process.exit(1);
52
+ }
53
+
54
+ function printHelp() {
55
+ console.log(`
56
+ Usage: adapter-static [input] [options]
57
+
58
+ Build a static site for OpenWorkers.
59
+
60
+ Arguments:
61
+ input Input directory (default: dist, build, out, or public)
62
+
63
+ Options:
64
+ -o, --out <dir> Output directory (default: dist-openworkers)
65
+ -m, --mode <mode> Routing mode: 'directory' or 'flat' (default: auto-detect)
66
+ -f, --fallback <f> SPA fallback file (e.g., /index.html or /200.html)
67
+ --immutable <p> Comma-separated immutable patterns (e.g., /assets/*,/_app/*)
68
+ -h, --help Show this help message
69
+ -v, --version Show version
70
+
71
+ Examples:
72
+ adapter-static # Auto-detect input, output to dist-openworkers
73
+ adapter-static dist -o out # Build dist/ to out/
74
+ adapter-static --fallback /index.html # Enable SPA mode
75
+ adapter-static --mode flat # Use flat routing (/page -> /page.html)
76
+ `);
77
+ }
@@ -0,0 +1,549 @@
1
+ // node_modules/mime/dist/types/standard.js
2
+ var types = {
3
+ "application/andrew-inset": ["ez"],
4
+ "application/appinstaller": ["appinstaller"],
5
+ "application/applixware": ["aw"],
6
+ "application/appx": ["appx"],
7
+ "application/appxbundle": ["appxbundle"],
8
+ "application/atom+xml": ["atom"],
9
+ "application/atomcat+xml": ["atomcat"],
10
+ "application/atomdeleted+xml": ["atomdeleted"],
11
+ "application/atomsvc+xml": ["atomsvc"],
12
+ "application/atsc-dwd+xml": ["dwd"],
13
+ "application/atsc-held+xml": ["held"],
14
+ "application/atsc-rsat+xml": ["rsat"],
15
+ "application/automationml-aml+xml": ["aml"],
16
+ "application/automationml-amlx+zip": ["amlx"],
17
+ "application/bdoc": ["bdoc"],
18
+ "application/calendar+xml": ["xcs"],
19
+ "application/ccxml+xml": ["ccxml"],
20
+ "application/cdfx+xml": ["cdfx"],
21
+ "application/cdmi-capability": ["cdmia"],
22
+ "application/cdmi-container": ["cdmic"],
23
+ "application/cdmi-domain": ["cdmid"],
24
+ "application/cdmi-object": ["cdmio"],
25
+ "application/cdmi-queue": ["cdmiq"],
26
+ "application/cpl+xml": ["cpl"],
27
+ "application/cu-seeme": ["cu"],
28
+ "application/cwl": ["cwl"],
29
+ "application/dash+xml": ["mpd"],
30
+ "application/dash-patch+xml": ["mpp"],
31
+ "application/davmount+xml": ["davmount"],
32
+ "application/dicom": ["dcm"],
33
+ "application/docbook+xml": ["dbk"],
34
+ "application/dssc+der": ["dssc"],
35
+ "application/dssc+xml": ["xdssc"],
36
+ "application/ecmascript": ["ecma"],
37
+ "application/emma+xml": ["emma"],
38
+ "application/emotionml+xml": ["emotionml"],
39
+ "application/epub+zip": ["epub"],
40
+ "application/exi": ["exi"],
41
+ "application/express": ["exp"],
42
+ "application/fdf": ["fdf"],
43
+ "application/fdt+xml": ["fdt"],
44
+ "application/font-tdpfr": ["pfr"],
45
+ "application/geo+json": ["geojson"],
46
+ "application/gml+xml": ["gml"],
47
+ "application/gpx+xml": ["gpx"],
48
+ "application/gxf": ["gxf"],
49
+ "application/gzip": ["gz"],
50
+ "application/hjson": ["hjson"],
51
+ "application/hyperstudio": ["stk"],
52
+ "application/inkml+xml": ["ink", "inkml"],
53
+ "application/ipfix": ["ipfix"],
54
+ "application/its+xml": ["its"],
55
+ "application/java-archive": ["jar", "war", "ear"],
56
+ "application/java-serialized-object": ["ser"],
57
+ "application/java-vm": ["class"],
58
+ "application/javascript": ["*js"],
59
+ "application/json": ["json", "map"],
60
+ "application/json5": ["json5"],
61
+ "application/jsonml+json": ["jsonml"],
62
+ "application/ld+json": ["jsonld"],
63
+ "application/lgr+xml": ["lgr"],
64
+ "application/lost+xml": ["lostxml"],
65
+ "application/mac-binhex40": ["hqx"],
66
+ "application/mac-compactpro": ["cpt"],
67
+ "application/mads+xml": ["mads"],
68
+ "application/manifest+json": ["webmanifest"],
69
+ "application/marc": ["mrc"],
70
+ "application/marcxml+xml": ["mrcx"],
71
+ "application/mathematica": ["ma", "nb", "mb"],
72
+ "application/mathml+xml": ["mathml"],
73
+ "application/mbox": ["mbox"],
74
+ "application/media-policy-dataset+xml": ["mpf"],
75
+ "application/mediaservercontrol+xml": ["mscml"],
76
+ "application/metalink+xml": ["metalink"],
77
+ "application/metalink4+xml": ["meta4"],
78
+ "application/mets+xml": ["mets"],
79
+ "application/mmt-aei+xml": ["maei"],
80
+ "application/mmt-usd+xml": ["musd"],
81
+ "application/mods+xml": ["mods"],
82
+ "application/mp21": ["m21", "mp21"],
83
+ "application/mp4": ["*mp4", "*mpg4", "mp4s", "m4p"],
84
+ "application/msix": ["msix"],
85
+ "application/msixbundle": ["msixbundle"],
86
+ "application/msword": ["doc", "dot"],
87
+ "application/mxf": ["mxf"],
88
+ "application/n-quads": ["nq"],
89
+ "application/n-triples": ["nt"],
90
+ "application/node": ["cjs"],
91
+ "application/octet-stream": [
92
+ "bin",
93
+ "dms",
94
+ "lrf",
95
+ "mar",
96
+ "so",
97
+ "dist",
98
+ "distz",
99
+ "pkg",
100
+ "bpk",
101
+ "dump",
102
+ "elc",
103
+ "deploy",
104
+ "exe",
105
+ "dll",
106
+ "deb",
107
+ "dmg",
108
+ "iso",
109
+ "img",
110
+ "msi",
111
+ "msp",
112
+ "msm",
113
+ "buffer"
114
+ ],
115
+ "application/oda": ["oda"],
116
+ "application/oebps-package+xml": ["opf"],
117
+ "application/ogg": ["ogx"],
118
+ "application/omdoc+xml": ["omdoc"],
119
+ "application/onenote": [
120
+ "onetoc",
121
+ "onetoc2",
122
+ "onetmp",
123
+ "onepkg",
124
+ "one",
125
+ "onea"
126
+ ],
127
+ "application/oxps": ["oxps"],
128
+ "application/p2p-overlay+xml": ["relo"],
129
+ "application/patch-ops-error+xml": ["xer"],
130
+ "application/pdf": ["pdf"],
131
+ "application/pgp-encrypted": ["pgp"],
132
+ "application/pgp-keys": ["asc"],
133
+ "application/pgp-signature": ["sig", "*asc"],
134
+ "application/pics-rules": ["prf"],
135
+ "application/pkcs10": ["p10"],
136
+ "application/pkcs7-mime": ["p7m", "p7c"],
137
+ "application/pkcs7-signature": ["p7s"],
138
+ "application/pkcs8": ["p8"],
139
+ "application/pkix-attr-cert": ["ac"],
140
+ "application/pkix-cert": ["cer"],
141
+ "application/pkix-crl": ["crl"],
142
+ "application/pkix-pkipath": ["pkipath"],
143
+ "application/pkixcmp": ["pki"],
144
+ "application/pls+xml": ["pls"],
145
+ "application/postscript": ["ai", "eps", "ps"],
146
+ "application/provenance+xml": ["provx"],
147
+ "application/pskc+xml": ["pskcxml"],
148
+ "application/raml+yaml": ["raml"],
149
+ "application/rdf+xml": ["rdf", "owl"],
150
+ "application/reginfo+xml": ["rif"],
151
+ "application/relax-ng-compact-syntax": ["rnc"],
152
+ "application/resource-lists+xml": ["rl"],
153
+ "application/resource-lists-diff+xml": ["rld"],
154
+ "application/rls-services+xml": ["rs"],
155
+ "application/route-apd+xml": ["rapd"],
156
+ "application/route-s-tsid+xml": ["sls"],
157
+ "application/route-usd+xml": ["rusd"],
158
+ "application/rpki-ghostbusters": ["gbr"],
159
+ "application/rpki-manifest": ["mft"],
160
+ "application/rpki-roa": ["roa"],
161
+ "application/rsd+xml": ["rsd"],
162
+ "application/rss+xml": ["rss"],
163
+ "application/rtf": ["rtf"],
164
+ "application/sbml+xml": ["sbml"],
165
+ "application/scvp-cv-request": ["scq"],
166
+ "application/scvp-cv-response": ["scs"],
167
+ "application/scvp-vp-request": ["spq"],
168
+ "application/scvp-vp-response": ["spp"],
169
+ "application/sdp": ["sdp"],
170
+ "application/senml+xml": ["senmlx"],
171
+ "application/sensml+xml": ["sensmlx"],
172
+ "application/set-payment-initiation": ["setpay"],
173
+ "application/set-registration-initiation": ["setreg"],
174
+ "application/shf+xml": ["shf"],
175
+ "application/sieve": ["siv", "sieve"],
176
+ "application/smil+xml": ["smi", "smil"],
177
+ "application/sparql-query": ["rq"],
178
+ "application/sparql-results+xml": ["srx"],
179
+ "application/sql": ["sql"],
180
+ "application/srgs": ["gram"],
181
+ "application/srgs+xml": ["grxml"],
182
+ "application/sru+xml": ["sru"],
183
+ "application/ssdl+xml": ["ssdl"],
184
+ "application/ssml+xml": ["ssml"],
185
+ "application/swid+xml": ["swidtag"],
186
+ "application/tei+xml": ["tei", "teicorpus"],
187
+ "application/thraud+xml": ["tfi"],
188
+ "application/timestamped-data": ["tsd"],
189
+ "application/toml": ["toml"],
190
+ "application/trig": ["trig"],
191
+ "application/ttml+xml": ["ttml"],
192
+ "application/ubjson": ["ubj"],
193
+ "application/urc-ressheet+xml": ["rsheet"],
194
+ "application/urc-targetdesc+xml": ["td"],
195
+ "application/voicexml+xml": ["vxml"],
196
+ "application/wasm": ["wasm"],
197
+ "application/watcherinfo+xml": ["wif"],
198
+ "application/widget": ["wgt"],
199
+ "application/winhlp": ["hlp"],
200
+ "application/wsdl+xml": ["wsdl"],
201
+ "application/wspolicy+xml": ["wspolicy"],
202
+ "application/xaml+xml": ["xaml"],
203
+ "application/xcap-att+xml": ["xav"],
204
+ "application/xcap-caps+xml": ["xca"],
205
+ "application/xcap-diff+xml": ["xdf"],
206
+ "application/xcap-el+xml": ["xel"],
207
+ "application/xcap-ns+xml": ["xns"],
208
+ "application/xenc+xml": ["xenc"],
209
+ "application/xfdf": ["xfdf"],
210
+ "application/xhtml+xml": ["xhtml", "xht"],
211
+ "application/xliff+xml": ["xlf"],
212
+ "application/xml": ["xml", "xsl", "xsd", "rng"],
213
+ "application/xml-dtd": ["dtd"],
214
+ "application/xop+xml": ["xop"],
215
+ "application/xproc+xml": ["xpl"],
216
+ "application/xslt+xml": ["*xsl", "xslt"],
217
+ "application/xspf+xml": ["xspf"],
218
+ "application/xv+xml": ["mxml", "xhvml", "xvml", "xvm"],
219
+ "application/yang": ["yang"],
220
+ "application/yin+xml": ["yin"],
221
+ "application/zip": ["zip"],
222
+ "application/zip+dotlottie": ["lottie"],
223
+ "audio/3gpp": ["*3gpp"],
224
+ "audio/aac": ["adts", "aac"],
225
+ "audio/adpcm": ["adp"],
226
+ "audio/amr": ["amr"],
227
+ "audio/basic": ["au", "snd"],
228
+ "audio/midi": ["mid", "midi", "kar", "rmi"],
229
+ "audio/mobile-xmf": ["mxmf"],
230
+ "audio/mp3": ["*mp3"],
231
+ "audio/mp4": ["m4a", "mp4a", "m4b"],
232
+ "audio/mpeg": ["mpga", "mp2", "mp2a", "mp3", "m2a", "m3a"],
233
+ "audio/ogg": ["oga", "ogg", "spx", "opus"],
234
+ "audio/s3m": ["s3m"],
235
+ "audio/silk": ["sil"],
236
+ "audio/wav": ["wav"],
237
+ "audio/wave": ["*wav"],
238
+ "audio/webm": ["weba"],
239
+ "audio/xm": ["xm"],
240
+ "font/collection": ["ttc"],
241
+ "font/otf": ["otf"],
242
+ "font/ttf": ["ttf"],
243
+ "font/woff": ["woff"],
244
+ "font/woff2": ["woff2"],
245
+ "image/aces": ["exr"],
246
+ "image/apng": ["apng"],
247
+ "image/avci": ["avci"],
248
+ "image/avcs": ["avcs"],
249
+ "image/avif": ["avif"],
250
+ "image/bmp": ["bmp", "dib"],
251
+ "image/cgm": ["cgm"],
252
+ "image/dicom-rle": ["drle"],
253
+ "image/dpx": ["dpx"],
254
+ "image/emf": ["emf"],
255
+ "image/fits": ["fits"],
256
+ "image/g3fax": ["g3"],
257
+ "image/gif": ["gif"],
258
+ "image/heic": ["heic"],
259
+ "image/heic-sequence": ["heics"],
260
+ "image/heif": ["heif"],
261
+ "image/heif-sequence": ["heifs"],
262
+ "image/hej2k": ["hej2"],
263
+ "image/ief": ["ief"],
264
+ "image/jaii": ["jaii"],
265
+ "image/jais": ["jais"],
266
+ "image/jls": ["jls"],
267
+ "image/jp2": ["jp2", "jpg2"],
268
+ "image/jpeg": ["jpg", "jpeg", "jpe"],
269
+ "image/jph": ["jph"],
270
+ "image/jphc": ["jhc"],
271
+ "image/jpm": ["jpm", "jpgm"],
272
+ "image/jpx": ["jpx", "jpf"],
273
+ "image/jxl": ["jxl"],
274
+ "image/jxr": ["jxr"],
275
+ "image/jxra": ["jxra"],
276
+ "image/jxrs": ["jxrs"],
277
+ "image/jxs": ["jxs"],
278
+ "image/jxsc": ["jxsc"],
279
+ "image/jxsi": ["jxsi"],
280
+ "image/jxss": ["jxss"],
281
+ "image/ktx": ["ktx"],
282
+ "image/ktx2": ["ktx2"],
283
+ "image/pjpeg": ["jfif"],
284
+ "image/png": ["png"],
285
+ "image/sgi": ["sgi"],
286
+ "image/svg+xml": ["svg", "svgz"],
287
+ "image/t38": ["t38"],
288
+ "image/tiff": ["tif", "tiff"],
289
+ "image/tiff-fx": ["tfx"],
290
+ "image/webp": ["webp"],
291
+ "image/wmf": ["wmf"],
292
+ "message/disposition-notification": ["disposition-notification"],
293
+ "message/global": ["u8msg"],
294
+ "message/global-delivery-status": ["u8dsn"],
295
+ "message/global-disposition-notification": ["u8mdn"],
296
+ "message/global-headers": ["u8hdr"],
297
+ "message/rfc822": ["eml", "mime", "mht", "mhtml"],
298
+ "model/3mf": ["3mf"],
299
+ "model/gltf+json": ["gltf"],
300
+ "model/gltf-binary": ["glb"],
301
+ "model/iges": ["igs", "iges"],
302
+ "model/jt": ["jt"],
303
+ "model/mesh": ["msh", "mesh", "silo"],
304
+ "model/mtl": ["mtl"],
305
+ "model/obj": ["obj"],
306
+ "model/prc": ["prc"],
307
+ "model/step": ["step", "stp", "stpnc", "p21", "210"],
308
+ "model/step+xml": ["stpx"],
309
+ "model/step+zip": ["stpz"],
310
+ "model/step-xml+zip": ["stpxz"],
311
+ "model/stl": ["stl"],
312
+ "model/u3d": ["u3d"],
313
+ "model/vrml": ["wrl", "vrml"],
314
+ "model/x3d+binary": ["*x3db", "x3dbz"],
315
+ "model/x3d+fastinfoset": ["x3db"],
316
+ "model/x3d+vrml": ["*x3dv", "x3dvz"],
317
+ "model/x3d+xml": ["x3d", "x3dz"],
318
+ "model/x3d-vrml": ["x3dv"],
319
+ "text/cache-manifest": ["appcache", "manifest"],
320
+ "text/calendar": ["ics", "ifb"],
321
+ "text/coffeescript": ["coffee", "litcoffee"],
322
+ "text/css": ["css"],
323
+ "text/csv": ["csv"],
324
+ "text/html": ["html", "htm", "shtml"],
325
+ "text/jade": ["jade"],
326
+ "text/javascript": ["js", "mjs"],
327
+ "text/jsx": ["jsx"],
328
+ "text/less": ["less"],
329
+ "text/markdown": ["md", "markdown"],
330
+ "text/mathml": ["mml"],
331
+ "text/mdx": ["mdx"],
332
+ "text/n3": ["n3"],
333
+ "text/plain": ["txt", "text", "conf", "def", "list", "log", "in", "ini"],
334
+ "text/richtext": ["rtx"],
335
+ "text/rtf": ["*rtf"],
336
+ "text/sgml": ["sgml", "sgm"],
337
+ "text/shex": ["shex"],
338
+ "text/slim": ["slim", "slm"],
339
+ "text/spdx": ["spdx"],
340
+ "text/stylus": ["stylus", "styl"],
341
+ "text/tab-separated-values": ["tsv"],
342
+ "text/troff": ["t", "tr", "roff", "man", "me", "ms"],
343
+ "text/turtle": ["ttl"],
344
+ "text/uri-list": ["uri", "uris", "urls"],
345
+ "text/vcard": ["vcard"],
346
+ "text/vtt": ["vtt"],
347
+ "text/wgsl": ["wgsl"],
348
+ "text/xml": ["*xml"],
349
+ "text/yaml": ["yaml", "yml"],
350
+ "video/3gpp": ["3gp", "3gpp"],
351
+ "video/3gpp2": ["3g2"],
352
+ "video/h261": ["h261"],
353
+ "video/h263": ["h263"],
354
+ "video/h264": ["h264"],
355
+ "video/iso.segment": ["m4s"],
356
+ "video/jpeg": ["jpgv"],
357
+ "video/jpm": ["*jpm", "*jpgm"],
358
+ "video/mj2": ["mj2", "mjp2"],
359
+ "video/mp2t": ["ts", "m2t", "m2ts", "mts"],
360
+ "video/mp4": ["mp4", "mp4v", "mpg4"],
361
+ "video/mpeg": ["mpeg", "mpg", "mpe", "m1v", "m2v"],
362
+ "video/ogg": ["ogv"],
363
+ "video/quicktime": ["qt", "mov"],
364
+ "video/webm": ["webm"]
365
+ };
366
+ Object.freeze(types);
367
+ var standard_default = types;
368
+
369
+ // node_modules/mime/dist/src/Mime.js
370
+ var __classPrivateFieldGet = function(receiver, state, kind, f) {
371
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
372
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
373
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
374
+ };
375
+ var _Mime_extensionToType;
376
+ var _Mime_typeToExtension;
377
+ var _Mime_typeToExtensions;
378
+ var Mime = class {
379
+ constructor(...args) {
380
+ _Mime_extensionToType.set(this, /* @__PURE__ */ new Map());
381
+ _Mime_typeToExtension.set(this, /* @__PURE__ */ new Map());
382
+ _Mime_typeToExtensions.set(this, /* @__PURE__ */ new Map());
383
+ for (const arg of args) {
384
+ this.define(arg);
385
+ }
386
+ }
387
+ define(typeMap, force = false) {
388
+ for (let [type, extensions] of Object.entries(typeMap)) {
389
+ type = type.toLowerCase();
390
+ extensions = extensions.map((ext) => ext.toLowerCase());
391
+ if (!__classPrivateFieldGet(this, _Mime_typeToExtensions, "f").has(type)) {
392
+ __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").set(type, /* @__PURE__ */ new Set());
393
+ }
394
+ const allExtensions = __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").get(type);
395
+ let first = true;
396
+ for (let extension of extensions) {
397
+ const starred = extension.startsWith("*");
398
+ extension = starred ? extension.slice(1) : extension;
399
+ allExtensions?.add(extension);
400
+ if (first) {
401
+ __classPrivateFieldGet(this, _Mime_typeToExtension, "f").set(type, extension);
402
+ }
403
+ first = false;
404
+ if (starred)
405
+ continue;
406
+ const currentType = __classPrivateFieldGet(this, _Mime_extensionToType, "f").get(extension);
407
+ if (currentType && currentType != type && !force) {
408
+ throw new Error(`"${type} -> ${extension}" conflicts with "${currentType} -> ${extension}". Pass \`force=true\` to override this definition.`);
409
+ }
410
+ __classPrivateFieldGet(this, _Mime_extensionToType, "f").set(extension, type);
411
+ }
412
+ }
413
+ return this;
414
+ }
415
+ getType(path) {
416
+ if (typeof path !== "string")
417
+ return null;
418
+ const last = path.replace(/^.*[/\\]/s, "").toLowerCase();
419
+ const ext = last.replace(/^.*\./s, "").toLowerCase();
420
+ const hasPath = last.length < path.length;
421
+ const hasDot = ext.length < last.length - 1;
422
+ if (!hasDot && hasPath)
423
+ return null;
424
+ return __classPrivateFieldGet(this, _Mime_extensionToType, "f").get(ext) ?? null;
425
+ }
426
+ getExtension(type) {
427
+ if (typeof type !== "string")
428
+ return null;
429
+ type = type?.split?.(";")[0];
430
+ return (type && __classPrivateFieldGet(this, _Mime_typeToExtension, "f").get(type.trim().toLowerCase())) ?? null;
431
+ }
432
+ getAllExtensions(type) {
433
+ if (typeof type !== "string")
434
+ return null;
435
+ return __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").get(type.toLowerCase()) ?? null;
436
+ }
437
+ _freeze() {
438
+ this.define = () => {
439
+ throw new Error("define() not allowed for built-in Mime objects. See https://github.com/broofa/mime/blob/main/README.md#custom-mime-instances");
440
+ };
441
+ Object.freeze(this);
442
+ for (const extensions of __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").values()) {
443
+ Object.freeze(extensions);
444
+ }
445
+ return this;
446
+ }
447
+ _getTestState() {
448
+ return {
449
+ types: __classPrivateFieldGet(this, _Mime_extensionToType, "f"),
450
+ extensions: __classPrivateFieldGet(this, _Mime_typeToExtension, "f")
451
+ };
452
+ }
453
+ };
454
+ _Mime_extensionToType = /* @__PURE__ */ new WeakMap(), _Mime_typeToExtension = /* @__PURE__ */ new WeakMap(), _Mime_typeToExtensions = /* @__PURE__ */ new WeakMap();
455
+ var Mime_default = Mime;
456
+
457
+ // node_modules/mime/dist/src/index_lite.js
458
+ var index_lite_default = new Mime_default(standard_default)._freeze();
459
+
460
+ // src/worker.js
461
+ import { routes } from "ROUTES";
462
+ function isImmutable(pathname) {
463
+ for (const pattern of routes.immutable) {
464
+ if (pattern.endsWith("/*")) {
465
+ const prefix = pattern.slice(0, -1);
466
+ if (pathname.startsWith(prefix)) {
467
+ return true;
468
+ }
469
+ } else if (pathname === pattern) {
470
+ return true;
471
+ }
472
+ }
473
+ return false;
474
+ }
475
+ var worker_default = {
476
+ async fetch(req, env) {
477
+ const url = new URL(req.url);
478
+ let { pathname } = url;
479
+ try {
480
+ pathname = decodeURIComponent(pathname);
481
+ } catch {
482
+ }
483
+ if (pathname !== "/" && pathname.endsWith("/")) {
484
+ pathname = pathname.slice(0, -1);
485
+ }
486
+ let response = await tryServeFile(env, pathname);
487
+ if (response) {
488
+ return addHeaders(response, pathname);
489
+ }
490
+ if (pathname === "/" || routes.mode === "directory") {
491
+ const indexPath = pathname === "/" ? "/index.html" : pathname + "/index.html";
492
+ response = await tryServeFile(env, indexPath);
493
+ if (response) {
494
+ return addHeaders(response, indexPath);
495
+ }
496
+ }
497
+ if (routes.mode === "flat" && pathname !== "/") {
498
+ response = await tryServeFile(env, pathname + ".html");
499
+ if (response) {
500
+ return addHeaders(response, pathname + ".html");
501
+ }
502
+ }
503
+ if (routes.fallback) {
504
+ response = await tryServeFile(env, routes.fallback);
505
+ if (response) {
506
+ return addHeaders(response, routes.fallback);
507
+ }
508
+ }
509
+ response = await tryServeFile(env, "/404.html");
510
+ if (response) {
511
+ return new Response(response.body, {
512
+ status: 404,
513
+ headers: response.headers
514
+ });
515
+ }
516
+ return new Response("Not Found", { status: 404 });
517
+ }
518
+ };
519
+ async function tryServeFile(env, pathname) {
520
+ try {
521
+ const response = await env.ASSETS.fetch(pathname);
522
+ if (response.ok) {
523
+ return response;
524
+ }
525
+ return null;
526
+ } catch {
527
+ return null;
528
+ }
529
+ }
530
+ function addHeaders(response, pathname) {
531
+ const headers = new Headers(response.headers);
532
+ if (!headers.has("content-type")) {
533
+ headers.set("content-type", index_lite_default.getType(pathname) ?? "application/octet-stream");
534
+ }
535
+ if (isImmutable(pathname)) {
536
+ headers.set("cache-control", "public, max-age=31536000, immutable");
537
+ } else if (pathname.endsWith(".html")) {
538
+ headers.set("cache-control", "no-cache");
539
+ } else {
540
+ headers.set("cache-control", "public, max-age=3600");
541
+ }
542
+ return new Response(response.body, {
543
+ status: response.status,
544
+ headers
545
+ });
546
+ }
547
+ export {
548
+ worker_default as default
549
+ };
package/index.d.ts ADDED
@@ -0,0 +1,42 @@
1
+ export interface AdapterOptions {
2
+ /**
3
+ * Input directory containing the static files
4
+ * @default 'dist' | 'build' | 'out' | 'public' (auto-detected)
5
+ */
6
+ input?: string;
7
+
8
+ /**
9
+ * Output directory for the OpenWorkers build
10
+ * @default 'dist-openworkers'
11
+ */
12
+ out?: string;
13
+
14
+ /**
15
+ * Routing mode for serving files without extensions
16
+ * - 'directory': /page -> /page/index.html
17
+ * - 'flat': /page -> /page.html
18
+ * @default auto-detected based on file structure
19
+ */
20
+ mode?: 'directory' | 'flat';
21
+
22
+ /**
23
+ * SPA fallback file path (e.g., '/index.html' or '/200.html')
24
+ * When set, this file is served for all routes that don't match a file
25
+ * @default undefined (no fallback, returns 404)
26
+ */
27
+ fallback?: string;
28
+
29
+ /**
30
+ * Glob patterns for immutable assets (hashed filenames)
31
+ * These files get long cache headers
32
+ * @default auto-detected (/_app/immutable/*, /assets/*, etc.)
33
+ */
34
+ immutable?: string[];
35
+ }
36
+
37
+ /**
38
+ * Build static site for OpenWorkers
39
+ */
40
+ export function adapt(options?: AdapterOptions): Promise<void>;
41
+
42
+ export default adapt;
package/index.js ADDED
@@ -0,0 +1,213 @@
1
+ import { existsSync, writeFileSync, readFileSync, cpSync, readdirSync, statSync, mkdirSync, rmSync } from 'node:fs';
2
+ import { join, posix, resolve } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { build } from 'esbuild';
5
+
6
+ const pkg = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf-8'));
7
+ const name = '@openworkers/adapter-static';
8
+ const version = pkg.version;
9
+
10
+ /**
11
+ * @typedef {Object} AdapterOptions
12
+ * @property {string} [input] - Input directory (default: 'dist' or 'build')
13
+ * @property {string} [out] - Output directory (default: 'dist-openworkers')
14
+ * @property {'directory' | 'flat'} [mode] - Routing mode (default: auto-detect)
15
+ * @property {string} [fallback] - SPA fallback file (e.g., '/index.html' or '/200.html')
16
+ * @property {string[]} [immutable] - Glob patterns for immutable assets
17
+ */
18
+
19
+ /**
20
+ * Build static site for OpenWorkers
21
+ * @param {AdapterOptions} options
22
+ */
23
+ export async function adapt(options = {}) {
24
+ const input = options.input ?? findInputDir();
25
+ const out = options.out ?? 'dist-openworkers';
26
+ const fallback = options.fallback ?? null;
27
+
28
+ if (!existsSync(input)) {
29
+ throw new Error(`Input directory not found: ${input}`);
30
+ }
31
+
32
+ const tmp = resolve(out, '.tmp');
33
+
34
+ // Clean and create output directories
35
+ rmSync(out, { recursive: true, force: true });
36
+ mkdirSync(out, { recursive: true });
37
+ mkdirSync(join(out, 'assets'), { recursive: true });
38
+ mkdirSync(tmp, { recursive: true });
39
+
40
+ // Copy all files to assets/
41
+ cpSync(input, join(out, 'assets'), { recursive: true });
42
+
43
+ // Detect routing mode
44
+ const mode = options.mode ?? detectMode(join(out, 'assets'));
45
+
46
+ // Detect immutable patterns
47
+ const immutable = options.immutable ?? detectImmutable(join(out, 'assets'));
48
+
49
+ // Generate routes config
50
+ const routesConfig = {
51
+ mode,
52
+ fallback,
53
+ immutable
54
+ };
55
+
56
+ writeFileSync(join(tmp, 'routes.js'), `export const routes = ${JSON.stringify(routesConfig, null, 2)};\n`);
57
+
58
+ // Bundle worker with esbuild
59
+ await build({
60
+ entryPoints: [fileURLToPath(new URL('./src/worker.js', import.meta.url).href)],
61
+ bundle: true,
62
+ format: 'esm',
63
+ platform: 'browser',
64
+ outfile: resolve(out, 'worker.js'),
65
+ alias: {
66
+ ROUTES: resolve(tmp, 'routes.js')
67
+ },
68
+ minify: false,
69
+ banner: {
70
+ js: `// Generated by ${name} v${version}\n`
71
+ }
72
+ });
73
+
74
+ // Generate routes.js manifest for edge router
75
+ const allFiles = listFiles(join(out, 'assets'));
76
+ const staticFiles = allFiles.filter((f) => !isImmutablePath(f, immutable));
77
+
78
+ const manifest = {
79
+ immutable,
80
+ static: staticFiles.map((f) => '/' + f),
81
+ fallback
82
+ };
83
+
84
+ writeFileSync(
85
+ join(out, 'routes.js'),
86
+ `// Generated by ${name} v${version}\n` +
87
+ `// Used by OpenWorkers edge router for static content\n\n` +
88
+ `export default ${JSON.stringify(manifest, null, '\t')};\n`
89
+ );
90
+
91
+ // Cleanup tmp
92
+ rmSync(tmp, { recursive: true, force: true });
93
+
94
+ console.log(`Built static site for OpenWorkers:`);
95
+ console.log(` Input: ${input}`);
96
+ console.log(` Output: ${out}`);
97
+ console.log(` Mode: ${mode}`);
98
+ console.log(` Files: ${allFiles.length}`);
99
+
100
+ if (fallback) {
101
+ console.log(` SPA fallback: ${fallback}`);
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Find input directory (dist or build)
107
+ * @returns {string}
108
+ */
109
+ function findInputDir() {
110
+ if (existsSync('dist')) return 'dist';
111
+ if (existsSync('build')) return 'build';
112
+ if (existsSync('out')) return 'out';
113
+ if (existsSync('public')) return 'public';
114
+
115
+ throw new Error('Could not find input directory. Specify --input or create dist/build folder.');
116
+ }
117
+
118
+ /**
119
+ * Detect routing mode based on file structure
120
+ * @param {string} dir
121
+ * @returns {'directory' | 'flat'}
122
+ */
123
+ function detectMode(dir) {
124
+ // Check if there are index.html files in subdirectories (directory mode)
125
+ const files = listFiles(dir);
126
+ const hasNestedIndex = files.some((f) => f.includes('/index.html') && f !== 'index.html');
127
+
128
+ if (hasNestedIndex) {
129
+ return 'directory';
130
+ }
131
+
132
+ // Check if there are .html files at root level (flat mode)
133
+ const hasRootHtml = files.some((f) => !f.includes('/') && f.endsWith('.html') && f !== 'index.html');
134
+
135
+ if (hasRootHtml) {
136
+ return 'flat';
137
+ }
138
+
139
+ // Default to directory mode
140
+ return 'directory';
141
+ }
142
+
143
+ /**
144
+ * Detect immutable asset patterns
145
+ * @param {string} dir
146
+ * @returns {string[]}
147
+ */
148
+ function detectImmutable(dir) {
149
+ const patterns = [];
150
+
151
+ // Common patterns for hashed assets
152
+ const commonImmutable = [
153
+ '_app/immutable', // SvelteKit
154
+ 'assets', // Vite
155
+ '_next/static', // Next.js
156
+ '_astro' // Astro
157
+ ];
158
+
159
+ for (const pattern of commonImmutable) {
160
+ if (existsSync(join(dir, pattern))) {
161
+ patterns.push(`/${pattern}/*`);
162
+ }
163
+ }
164
+
165
+ return patterns;
166
+ }
167
+
168
+ /**
169
+ * Check if a path matches immutable patterns
170
+ * @param {string} filepath
171
+ * @param {string[]} patterns
172
+ * @returns {boolean}
173
+ */
174
+ function isImmutablePath(filepath, patterns) {
175
+ for (const pattern of patterns) {
176
+ if (pattern.endsWith('/*')) {
177
+ const prefix = pattern.slice(1, -2); // Remove leading / and trailing /*
178
+
179
+ if (filepath.startsWith(prefix)) {
180
+ return true;
181
+ }
182
+ }
183
+ }
184
+
185
+ return false;
186
+ }
187
+
188
+ /**
189
+ * List all files in a directory recursively
190
+ * @param {string} dir
191
+ * @param {string} [base='']
192
+ * @returns {string[]}
193
+ */
194
+ function listFiles(dir, base = '') {
195
+ const results = [];
196
+ const entries = readdirSync(dir);
197
+
198
+ for (const entry of entries) {
199
+ const fullPath = join(dir, entry);
200
+ const relativePath = base ? posix.join(base, entry) : entry;
201
+ const stat = statSync(fullPath);
202
+
203
+ if (stat.isDirectory()) {
204
+ results.push(...listFiles(fullPath, relativePath));
205
+ } else {
206
+ results.push(relativePath);
207
+ }
208
+ }
209
+
210
+ return results;
211
+ }
212
+
213
+ export default adapt;
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@openworkers/adapter-static",
3
+ "version": "0.1.0",
4
+ "description": "Static site adapter for OpenWorkers",
5
+ "keywords": [
6
+ "adapter",
7
+ "openworkers",
8
+ "edge",
9
+ "workers",
10
+ "static",
11
+ "hosting"
12
+ ],
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/openworkers/adapter-static.git"
16
+ },
17
+ "license": "MIT",
18
+ "type": "module",
19
+ "bin": {
20
+ "adapter-static": "./bin/cli.js"
21
+ },
22
+ "exports": {
23
+ ".": {
24
+ "types": "./index.d.ts",
25
+ "import": "./index.js"
26
+ },
27
+ "./package.json": "./package.json"
28
+ },
29
+ "types": "index.d.ts",
30
+ "files": [
31
+ "bin",
32
+ "files",
33
+ "index.js",
34
+ "index.d.ts"
35
+ ],
36
+ "scripts": {
37
+ "build": "esbuild src/worker.js --bundle --outfile=files/worker.js --external:ROUTES --format=esm",
38
+ "prepublishOnly": "bun run build",
39
+ "format": "prettier --write ."
40
+ },
41
+ "dependencies": {
42
+ "esbuild": "^0.24.0",
43
+ "mime": "^4.0.0"
44
+ },
45
+ "devDependencies": {
46
+ "@openworkers/workers-types": "^0.1.7",
47
+ "prettier": "^3.8.1"
48
+ },
49
+ "publishConfig": {
50
+ "access": "public",
51
+ "registry": "https://registry.npmjs.org/"
52
+ }
53
+ }