@seanhogg/builderforce-webcontainers 2026.10.1 → 2026.10.3

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 (98) hide show
  1. package/README.md +11 -0
  2. package/dist/assets.d.ts +6 -0
  3. package/dist/assets.js +6 -0
  4. package/dist/check/cachedFetch.d.ts +3 -0
  5. package/dist/check/cachedFetch.d.ts.map +1 -0
  6. package/dist/check/cachedFetch.js +39 -0
  7. package/dist/check/cachedFetch.js.map +1 -0
  8. package/dist/check/index.d.ts +41 -0
  9. package/dist/check/index.d.ts.map +1 -0
  10. package/dist/check/index.js +74 -0
  11. package/dist/check/index.js.map +1 -0
  12. package/dist/check/protocol.d.ts +29 -0
  13. package/dist/check/protocol.d.ts.map +1 -0
  14. package/dist/check/protocol.js +13 -0
  15. package/dist/check/protocol.js.map +1 -0
  16. package/dist/check/worker.js +20 -0
  17. package/dist/componentCompilers.d.ts +21 -0
  18. package/dist/componentCompilers.d.ts.map +1 -0
  19. package/dist/componentCompilers.js +35 -0
  20. package/dist/componentCompilers.js.map +1 -0
  21. package/dist/esbuildWasm.d.ts +10 -4
  22. package/dist/esbuildWasm.d.ts.map +1 -1
  23. package/dist/esbuildWasm.js +14 -6
  24. package/dist/esbuildWasm.js.map +1 -1
  25. package/dist/host.d.ts +61 -6
  26. package/dist/host.d.ts.map +1 -1
  27. package/dist/host.js +61 -42
  28. package/dist/host.js.map +1 -1
  29. package/dist/index.d.ts +10 -4
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +5 -2
  32. package/dist/index.js.map +1 -1
  33. package/dist/node/index.d.ts +42 -0
  34. package/dist/node/index.d.ts.map +1 -0
  35. package/dist/node/index.js +45 -0
  36. package/dist/node/index.js.map +1 -0
  37. package/dist/node/processHost.d.ts +65 -0
  38. package/dist/node/processHost.d.ts.map +1 -0
  39. package/dist/node/processHost.js +269 -0
  40. package/dist/node/processHost.js.map +1 -0
  41. package/dist/node/protocol.d.ts +139 -0
  42. package/dist/node/protocol.d.ts.map +1 -0
  43. package/dist/node/protocol.js +5 -0
  44. package/dist/node/protocol.js.map +1 -0
  45. package/dist/node/worker.js +197 -0
  46. package/dist/node/workerProcess.d.ts +22 -0
  47. package/dist/node/workerProcess.d.ts.map +1 -0
  48. package/dist/node/workerProcess.js +210 -0
  49. package/dist/node/workerProcess.js.map +1 -0
  50. package/dist/packageCache.d.ts +12 -0
  51. package/dist/packageCache.d.ts.map +1 -0
  52. package/dist/packageCache.js +28 -0
  53. package/dist/packageCache.js.map +1 -0
  54. package/dist/process-worker.js +197 -0
  55. package/dist/protocol.d.ts +27 -0
  56. package/dist/protocol.d.ts.map +1 -1
  57. package/dist/protocol.js +25 -0
  58. package/dist/protocol.js.map +1 -1
  59. package/dist/relay.html +2 -0
  60. package/dist/relayWorker.d.ts +19 -0
  61. package/dist/relayWorker.d.ts.map +1 -0
  62. package/dist/relayWorker.js +39 -0
  63. package/dist/relayWorker.js.map +1 -0
  64. package/dist/sw.js +1 -1
  65. package/dist/swRouter.d.ts +9 -1
  66. package/dist/swRouter.d.ts.map +1 -1
  67. package/dist/swRouter.js +29 -5
  68. package/dist/swRouter.js.map +1 -1
  69. package/dist/transport.d.ts +23 -0
  70. package/dist/transport.d.ts.map +1 -0
  71. package/dist/transport.js +112 -0
  72. package/dist/transport.js.map +1 -0
  73. package/dist/worker.d.ts +5 -0
  74. package/dist/worker.d.ts.map +1 -0
  75. package/dist/worker.js +29 -0
  76. package/dist/worker.js.map +1 -0
  77. package/package.json +17 -3
  78. package/src/check/cachedFetch.ts +47 -0
  79. package/src/check/index.ts +98 -0
  80. package/src/check/protocol.ts +34 -0
  81. package/src/check/worker.ts +49 -0
  82. package/src/componentCompilers.ts +66 -0
  83. package/src/esbuildWasm.ts +19 -7
  84. package/src/host.ts +125 -42
  85. package/src/index.ts +18 -4
  86. package/src/node/index.ts +69 -0
  87. package/src/node/processHost.ts +307 -0
  88. package/src/node/protocol.ts +63 -0
  89. package/src/node/worker.ts +46 -0
  90. package/src/node/workerProcess.ts +219 -0
  91. package/src/packageCache.ts +28 -0
  92. package/src/protocol.ts +33 -0
  93. package/src/relay.ts +44 -0
  94. package/src/relayWorker.ts +53 -0
  95. package/src/sw.ts +25 -6
  96. package/src/swRouter.ts +36 -5
  97. package/src/transport.ts +141 -0
  98. package/src/worker.ts +25 -0
@@ -4,10 +4,30 @@
4
4
  * state: every request it intercepts is forwarded over a MessagePort to the page.
5
5
  */
6
6
  export declare const PREVIEW_SEGMENT = "__bfwc";
7
+ /**
8
+ * What a preview id may contain. No dots, so a file served beside the worker
9
+ * (`sw.js`, `relay.html`) can never be mistaken for a preview.
10
+ */
11
+ export declare const PREVIEW_ID: RegExp;
7
12
  /** page → worker, with a MessagePort in `ports[0]`: "serve preview `id` over this port". */
8
13
  export declare const ATTACH = "bfwc:attach";
9
14
  /** worker → pages: "I lost the port for `id` (I was restarted) — attach again". */
10
15
  export declare const REATTACH = "bfwc:reattach";
16
+ /**
17
+ * The cross-origin relay (`relay.html`, served on the preview origin and framed,
18
+ * hidden, by the host). The host cannot talk to a worker on another origin, so the
19
+ * relay registers it and passes messages both ways.
20
+ */
21
+ /** relay → host: the worker is active; send ports. */
22
+ export declare const RELAY_READY = "bfwc:relay-ready";
23
+ /** host → relay, with a MessagePort: forward as ATTACH for `id`. */
24
+ export declare const RELAY_ATTACH = "bfwc:relay-attach";
25
+ /** relay → host: the worker asked for `id` again. */
26
+ export declare const RELAY_REATTACH = "bfwc:relay-reattach";
27
+ /** host → relay: reload every frame showing the preview at `base`. */
28
+ export declare const RELAY_RELOAD = "bfwc:relay-reload";
29
+ /** host → relay, with a MessagePort: start a process worker on the preview origin, bridged over the port. */
30
+ export declare const RELAY_SPAWN = "bfwc:relay-spawn";
11
31
  export interface AttachMessage {
12
32
  type: typeof ATTACH;
13
33
  id: string;
@@ -21,7 +41,14 @@ export interface WireRequest {
21
41
  reqId: number;
22
42
  path: string;
23
43
  search: string;
44
+ /** Present for requests to a virtual Node server (`/__port/<n>/…`), which see the full request. */
45
+ method?: string;
46
+ headers?: Record<string, string>;
47
+ body?: ArrayBuffer | null;
24
48
  }
49
+ /** The path segment that routes a preview request to a virtual server: `/__port/3000/…`. */
50
+ export declare const PORT_SEGMENT = "__port";
51
+ export declare function isPortPath(path: string): boolean;
25
52
  export interface WireResponse {
26
53
  type: 'response';
27
54
  reqId: number;
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,eAAe,WAAW,CAAC;AAExC,4FAA4F;AAC5F,eAAO,MAAM,MAAM,gBAAgB,CAAC;AACpC,mFAAmF;AACnF,eAAO,MAAM,QAAQ,kBAAkB,CAAC;AAExC,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,OAAO,MAAM,CAAC;IACpB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,QAAQ,CAAC;IACtB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAGlE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAGpE"}
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,eAAe,WAAW,CAAC;AAExC;;;GAGG;AACH,eAAO,MAAM,UAAU,QAAqB,CAAC;AAE7C,4FAA4F;AAC5F,eAAO,MAAM,MAAM,gBAAgB,CAAC;AACpC,mFAAmF;AACnF,eAAO,MAAM,QAAQ,kBAAkB,CAAC;AAExC;;;;GAIG;AACH,sDAAsD;AACtD,eAAO,MAAM,WAAW,qBAAqB,CAAC;AAC9C,oEAAoE;AACpE,eAAO,MAAM,YAAY,sBAAsB,CAAC;AAChD,qDAAqD;AACrD,eAAO,MAAM,cAAc,wBAAwB,CAAC;AACpD,sEAAsE;AACtE,eAAO,MAAM,YAAY,sBAAsB,CAAC;AAChD,6GAA6G;AAC7G,eAAO,MAAM,WAAW,qBAAqB,CAAC;AAE9C,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,OAAO,MAAM,CAAC;IACpB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,QAAQ,CAAC;IACtB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,mGAAmG;IACnG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CAC3B;AAED,4FAA4F;AAC5F,eAAO,MAAM,YAAY,WAAW,CAAC;AAErC,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEhD;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAGlE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAGpE"}
package/dist/protocol.js CHANGED
@@ -4,10 +4,35 @@
4
4
  * state: every request it intercepts is forwarded over a MessagePort to the page.
5
5
  */
6
6
  export const PREVIEW_SEGMENT = '__bfwc';
7
+ /**
8
+ * What a preview id may contain. No dots, so a file served beside the worker
9
+ * (`sw.js`, `relay.html`) can never be mistaken for a preview.
10
+ */
11
+ export const PREVIEW_ID = /^[A-Za-z0-9_-]+$/;
7
12
  /** page → worker, with a MessagePort in `ports[0]`: "serve preview `id` over this port". */
8
13
  export const ATTACH = 'bfwc:attach';
9
14
  /** worker → pages: "I lost the port for `id` (I was restarted) — attach again". */
10
15
  export const REATTACH = 'bfwc:reattach';
16
+ /**
17
+ * The cross-origin relay (`relay.html`, served on the preview origin and framed,
18
+ * hidden, by the host). The host cannot talk to a worker on another origin, so the
19
+ * relay registers it and passes messages both ways.
20
+ */
21
+ /** relay → host: the worker is active; send ports. */
22
+ export const RELAY_READY = 'bfwc:relay-ready';
23
+ /** host → relay, with a MessagePort: forward as ATTACH for `id`. */
24
+ export const RELAY_ATTACH = 'bfwc:relay-attach';
25
+ /** relay → host: the worker asked for `id` again. */
26
+ export const RELAY_REATTACH = 'bfwc:relay-reattach';
27
+ /** host → relay: reload every frame showing the preview at `base`. */
28
+ export const RELAY_RELOAD = 'bfwc:relay-reload';
29
+ /** host → relay, with a MessagePort: start a process worker on the preview origin, bridged over the port. */
30
+ export const RELAY_SPAWN = 'bfwc:relay-spawn';
31
+ /** The path segment that routes a preview request to a virtual server: `/__port/3000/…`. */
32
+ export const PORT_SEGMENT = '__port';
33
+ export function isPortPath(path) {
34
+ return path.startsWith(`/${PORT_SEGMENT}/`);
35
+ }
11
36
  /**
12
37
  * The URL prefix previews live under, for a worker scope. Serving the worker from
13
38
  * `/__bfwc/sw.js` (scope `/__bfwc/`) keeps it off every other page of the host app;
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC;AAExC,4FAA4F;AAC5F,MAAM,CAAC,MAAM,MAAM,GAAG,aAAa,CAAC;AACpC,mFAAmF;AACnF,MAAM,CAAC,MAAM,QAAQ,GAAG,eAAe,CAAC;AA2BxC;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,SAAiB;IAC7C,MAAM,KAAK,GAAG,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC;IACpE,OAAO,KAAK,CAAC,QAAQ,CAAC,IAAI,eAAe,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,eAAe,GAAG,CAAC;AACxF,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,SAAiB,EAAE,EAAU;IACvD,OAAO,GAAG,aAAa,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,MAAM,CAAC,GAAG,KAA2B,CAAC;IACtC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;AAClG,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,MAAM,CAAC,GAAG,KAA4B,CAAC;IACvC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC;AACrE,CAAC"}
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC;AAExC;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,kBAAkB,CAAC;AAE7C,4FAA4F;AAC5F,MAAM,CAAC,MAAM,MAAM,GAAG,aAAa,CAAC;AACpC,mFAAmF;AACnF,MAAM,CAAC,MAAM,QAAQ,GAAG,eAAe,CAAC;AAExC;;;;GAIG;AACH,sDAAsD;AACtD,MAAM,CAAC,MAAM,WAAW,GAAG,kBAAkB,CAAC;AAC9C,oEAAoE;AACpE,MAAM,CAAC,MAAM,YAAY,GAAG,mBAAmB,CAAC;AAChD,qDAAqD;AACrD,MAAM,CAAC,MAAM,cAAc,GAAG,qBAAqB,CAAC;AACpD,sEAAsE;AACtE,MAAM,CAAC,MAAM,YAAY,GAAG,mBAAmB,CAAC;AAChD,6GAA6G;AAC7G,MAAM,CAAC,MAAM,WAAW,GAAG,kBAAkB,CAAC;AAuB9C,4FAA4F;AAC5F,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;AAErC,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,YAAY,GAAG,CAAC,CAAC;AAC9C,CAAC;AAUD;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,SAAiB;IAC7C,MAAM,KAAK,GAAG,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC;IACpE,OAAO,KAAK,CAAC,QAAQ,CAAC,IAAI,eAAe,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,eAAe,GAAG,CAAC;AACxF,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,SAAiB,EAAE,EAAU;IACvD,OAAO,GAAG,aAAa,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,MAAM,CAAC,GAAG,KAA2B,CAAC;IACtC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;AAClG,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,MAAM,CAAC,GAAG,KAA4B,CAAC;IACvC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC;AACrE,CAAC"}
@@ -0,0 +1,2 @@
1
+ <!doctype html><html><head><meta charset="utf-8"><title>Preview relay</title></head><body><script>"use strict";(()=>{var a="bfwc:attach",c="bfwc:reattach",n="bfwc:relay-ready",p="bfwc:relay-attach",d="bfwc:relay-reattach",f="bfwc:relay-reload",m="bfwc:relay-spawn";function l(e,t){t.onmessage=r=>e.postMessage({kind:"message",data:r.data}),t.onerror=r=>e.postMessage({kind:"error",message:r.message||"The process worker failed."}),e.onmessage=r=>{let o=r.data;o?.kind==="message"?t.postMessage(o.data):o?.kind==="terminate"&&(t.terminate(),e.close())}}var g="process-worker.js";function i(e){return`bfwc:reload:${e}`}function u(e){if(e.active)return Promise.resolve(e.active);let t=e.installing??e.waiting;return t?new Promise((r,o)=>{t.addEventListener("statechange",()=>{t.state==="activated"&&e.active&&r(e.active),t.state==="redundant"&&o(new Error("The preview service worker failed to install."))})}):Promise.reject(new Error("The preview service worker did not install."))}function E(e){if(typeof BroadcastChannel>"u")return;let t=new BroadcastChannel(i(e));t.postMessage("reload"),t.close()}var s=window.parent;async function R(){let e=navigator.serviceWorker;if(!e)throw new Error("Service workers are unavailable on the preview origin.");let t=await e.register(new URL("./sw.js",location.href).href,{scope:"./"});await u(t),e.addEventListener("message",r=>{r.data?.type===c&&typeof r.data.id=="string"&&s.postMessage({type:d,id:r.data.id},"*")}),addEventListener("message",r=>{if(r.source!==s)return;let o=r.data;o?.type===p&&typeof o.id=="string"&&r.ports[0]?t.active?.postMessage({type:a,id:o.id},[r.ports[0]]):o?.type===f&&typeof o.base=="string"?E(o.base):o?.type===m&&r.ports[0]&&l(r.ports[0],new Worker(new URL(`./${g}`,location.href),{name:"bfwc-process"}))}),s.postMessage({type:n},"*")}R().catch(e=>{s.postMessage({type:n,error:e instanceof Error?e.message:String(e)},"*")});})();
2
+ </script></body></html>
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Process workers on the PREVIEW origin, driven from the host page.
3
+ *
4
+ * A process worker runs whatever the project runs — npm packages, the user's
5
+ * server, an AI-written script. A worker started by the host page shares the
6
+ * host's origin: its IndexedDB, Cache Storage and same-origin requests. In relay
7
+ * mode that is exactly what the preview origin exists to keep apart, so the
8
+ * relay frame starts each worker on ITS origin and this bridges the two over a
9
+ * MessageChannel. The host side is a `WorkerLike`, so the process host cannot
10
+ * tell the difference.
11
+ */
12
+ import type { WorkerLike } from './node/processHost.js';
13
+ /** Host side: a `WorkerLike` whose worker lives at the far end of `port`. */
14
+ export declare function workerOverPort(port: MessagePort): WorkerLike;
15
+ /** Relay side: run `worker`, relaying both ways over `port` until terminated. */
16
+ export declare function bridgeWorker(port: MessagePort, worker: Worker): void;
17
+ /** The process worker's file name beside `relay.html` on the preview origin. */
18
+ export declare const RELAY_PROCESS_WORKER = "process-worker.js";
19
+ //# sourceMappingURL=relayWorker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relayWorker.d.ts","sourceRoot":"","sources":["../src/relayWorker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAKxD,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,UAAU,CAkB5D;AAED,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAWpE;AAED,gFAAgF;AAChF,eAAO,MAAM,oBAAoB,sBAAsB,CAAC"}
@@ -0,0 +1,39 @@
1
+ /** Host side: a `WorkerLike` whose worker lives at the far end of `port`. */
2
+ export function workerOverPort(port) {
3
+ const worker = {
4
+ onmessage: null,
5
+ onerror: null,
6
+ postMessage(data) {
7
+ port.postMessage({ kind: 'message', data });
8
+ },
9
+ terminate() {
10
+ port.postMessage({ kind: 'terminate' });
11
+ port.close();
12
+ },
13
+ };
14
+ port.onmessage = (event) => {
15
+ const message = event.data;
16
+ if (message?.kind === 'message')
17
+ worker.onmessage?.({ data: message.data });
18
+ else if (message?.kind === 'error')
19
+ worker.onerror?.({ message: message.message });
20
+ };
21
+ return worker;
22
+ }
23
+ /** Relay side: run `worker`, relaying both ways over `port` until terminated. */
24
+ export function bridgeWorker(port, worker) {
25
+ worker.onmessage = (event) => port.postMessage({ kind: 'message', data: event.data });
26
+ worker.onerror = (event) => port.postMessage({ kind: 'error', message: event.message || 'The process worker failed.' });
27
+ port.onmessage = (event) => {
28
+ const message = event.data;
29
+ if (message?.kind === 'message')
30
+ worker.postMessage(message.data);
31
+ else if (message?.kind === 'terminate') {
32
+ worker.terminate();
33
+ port.close();
34
+ }
35
+ };
36
+ }
37
+ /** The process worker's file name beside `relay.html` on the preview origin. */
38
+ export const RELAY_PROCESS_WORKER = 'process-worker.js';
39
+ //# sourceMappingURL=relayWorker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relayWorker.js","sourceRoot":"","sources":["../src/relayWorker.ts"],"names":[],"mappings":"AAgBA,6EAA6E;AAC7E,MAAM,UAAU,cAAc,CAAC,IAAiB;IAC9C,MAAM,MAAM,GAAe;QACzB,SAAS,EAAE,IAAI;QACf,OAAO,EAAE,IAAI;QACb,WAAW,CAAC,IAAI;YACd,IAAI,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAqB,CAAC,CAAC;QACjE,CAAC;QACD,SAAS;YACP,IAAI,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,WAAW,EAAqB,CAAC,CAAC;YAC3D,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC;KACF,CAAC;IACF,IAAI,CAAC,SAAS,GAAG,CAAC,KAA+B,EAAE,EAAE;QACnD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC;QAC3B,IAAI,OAAO,EAAE,IAAI,KAAK,SAAS;YAAE,MAAM,CAAC,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;aACvE,IAAI,OAAO,EAAE,IAAI,KAAK,OAAO;YAAE,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACrF,CAAC,CAAC;IACF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,YAAY,CAAC,IAAiB,EAAE,MAAc;IAC5D,MAAM,CAAC,SAAS,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAuB,CAAC,CAAC;IAC3G,MAAM,CAAC,OAAO,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,4BAA4B,EAAuB,CAAC,CAAC;IAC7I,IAAI,CAAC,SAAS,GAAG,CAAC,KAA6B,EAAE,EAAE;QACjD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC;QAC3B,IAAI,OAAO,EAAE,IAAI,KAAK,SAAS;YAAE,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;aAC7D,IAAI,OAAO,EAAE,IAAI,KAAK,WAAW,EAAE,CAAC;YACvC,MAAM,CAAC,SAAS,EAAE,CAAC;YACnB,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,gFAAgF;AAChF,MAAM,CAAC,MAAM,oBAAoB,GAAG,mBAAmB,CAAC"}
package/dist/sw.js CHANGED
@@ -1 +1 @@
1
- "use strict";(()=>{var f="__bfwc",g="bfwc:attach",m="bfwc:reattach";function w(e){let n=e.endsWith("/")?e:e+"/";return n.endsWith(`/${f}/`)?n:`${n}${f}/`}function y(e){let n=e;return!!n&&n.type==="response"&&typeof n.reqId=="number"}var v={"content-type":"text/plain; charset=utf-8","cache-control":"no-store"};function R(e){let n=w(e.scopePath),i=e.timeoutMs??3e4,k=e.reattachWaitMs??3e3,c=new Map,d=new Map,p=new Map,T=0;function W(t,r){c.set(t,r),r.onmessage=s=>{y(s.data)&&(p.get(s.data.reqId)?.(s.data),p.delete(s.data.reqId))};for(let s of d.get(t)??[])s(r);d.delete(t)}function P(t){if(!t.pathname.startsWith(n))return null;let r=t.pathname.slice(n.length),s=r.indexOf("/"),o=s<0?r:r.slice(0,s);return o?{id:o,path:s<0?"/":r.slice(s),search:t.search}:null}async function b(t){let r=c.get(t);if(r)return r;let s=new Promise(o=>{let u=d.get(t)??[];u.push(o),d.set(t,u),setTimeout(()=>o(void 0),k)});return await e.requestReattach(t),s}async function x(t){let r=await b(t.id);if(!r)return{status:503,headers:v,body:"This preview is not running in any open tab."};let s=++T;return new Promise(o=>{let u=setTimeout(()=>{p.delete(s),c.get(t.id)===r&&c.delete(t.id),o({status:504,headers:v,body:"The preview did not answer in time."})},i);p.set(s,l=>{clearTimeout(u),o({status:l.status,headers:l.headers,body:l.body})}),r.postMessage({type:"request",reqId:s,path:t.path,search:t.search})})}return{attach:W,match:P,respond:x,detach:t=>c.delete(t)}}var a=globalThis,h=R({scopePath:new URL(a.registration.scope).pathname,async requestReattach(e){let n=await a.clients.matchAll({type:"window",includeUncontrolled:!0});for(let i of n)i.postMessage({type:m,id:e})}});a.addEventListener("install",()=>{a.skipWaiting()});a.addEventListener("activate",e=>{e.waitUntil(a.clients.claim())});a.addEventListener("message",e=>{let n=e.data,i=e.ports[0];n?.type===g&&typeof n.id=="string"&&i&&h.attach(n.id,i)});a.addEventListener("fetch",e=>{if(e.request.method!=="GET")return;let n=h.match(new URL(e.request.url));n&&e.respondWith(h.respond(n).then(i=>new Response(i.body,{status:i.status,headers:i.headers})))});})();
1
+ "use strict";(()=>{var v="__bfwc",x=/^[A-Za-z0-9_-]+$/,P="bfwc:attach",A="bfwc:reattach";var C="__port";function g(t){return t.startsWith(`/${C}/`)}function b(t){let r=t.endsWith("/")?t:t+"/";return r.endsWith(`/${v}/`)?r:`${r}${v}/`}function T(t){let r=t;return!!r&&r.type==="response"&&typeof r.reqId=="number"}var E={"content-type":"text/plain; charset=utf-8","cache-control":"no-store"};function L(t){let r=b(t.scopePath),o=t.timeoutMs??3e4,l=t.reattachWaitMs??3e3,p=new Map,a=new Map,u=new Map,w=0;function y(e,s){p.set(e,s),s.onmessage=n=>{T(n.data)&&(u.get(n.data.reqId)?.(n.data),u.delete(n.data.reqId))};for(let n of a.get(e)??[])n(s);a.delete(e)}function h(e){if(!e.pathname.startsWith(r))return null;let s=e.pathname.slice(r.length),n=s.indexOf("/"),i=n<0?s:s.slice(0,n);return!i||!x.test(i)?null:{id:i,path:n<0?"/":s.slice(n),search:e.search}}async function W(e){let s=p.get(e);if(s)return s;let n=new Promise(i=>{let c=a.get(e)??[];c.push(i),a.set(e,c),setTimeout(()=>i(void 0),l)});return await t.requestReattach(e),n}let f=new Map;function k(e,s){if(!e||!g(s.path))return;let n=/^\/__port\/\d+/.exec(s.path)[0];f.set(e,{id:s.id,prefix:n}),f.size>200&&f.delete(f.keys().next().value)}function q(e,s,n){let i=h(e);if(i||!s||e.origin!==n)return i;let c=f.get(s);return c?{id:c.id,path:c.prefix+e.pathname,search:e.search}:null}async function M(e,s){let n=await W(e.id);if(!n)return{status:503,headers:E,body:"This preview is not running in any open tab."};let i=++w;return new Promise(c=>{let _=setTimeout(()=>{u.delete(i),p.get(e.id)===n&&p.delete(e.id),c({status:504,headers:E,body:"The preview did not answer in time."})},o);u.set(i,R=>{clearTimeout(_),c({status:R.status,headers:R.headers,body:R.body})}),n.postMessage({type:"request",reqId:i,path:e.path,search:e.search,...s})})}return{attach:y,match:h,matchForClient:q,rememberClient:k,respond:M,detach:e=>p.delete(e)}}var d=globalThis,m=L({scopePath:new URL(d.registration.scope).pathname,async requestReattach(t){let r=await d.clients.matchAll({type:"window",includeUncontrolled:!0});for(let o of r)o.postMessage({type:A,id:t})}});d.addEventListener("install",()=>{d.skipWaiting()});d.addEventListener("activate",t=>{t.waitUntil(d.clients.claim())});d.addEventListener("message",t=>{let r=t.data,o=t.ports[0];r?.type===P&&typeof r.id=="string"&&o&&m.attach(r.id,o)});var I=new Set([101,204,205,304]);d.addEventListener("fetch",t=>{let{request:r}=t,o=m.matchForClient(new URL(r.url),t.clientId,new URL(d.registration.scope).origin);if(!o)return;let l=g(o.path);r.method!=="GET"&&!l||(l&&m.rememberClient(t.resultingClientId,o),t.respondWith((async()=>{let p;if(l){let u={};r.headers.forEach((y,h)=>u[h]=y);let w=r.method==="GET"||r.method==="HEAD"?null:await r.arrayBuffer();p={method:r.method,headers:u,body:w}}let a=await m.respond(o,p);return new Response(I.has(a.status)?null:a.body,{status:a.status,headers:a.headers})})()))});})();
@@ -10,6 +10,12 @@ export interface PreviewTarget {
10
10
  path: string;
11
11
  search: string;
12
12
  }
13
+ /** The parts of a request a virtual server needs (method, headers, body). */
14
+ export interface RequestDetails {
15
+ method: string;
16
+ headers: Record<string, string>;
17
+ body: ArrayBuffer | null;
18
+ }
13
19
  export interface RoutedResponse {
14
20
  status: number;
15
21
  headers: Record<string, string>;
@@ -37,7 +43,9 @@ export interface SwRouterOptions {
37
43
  export declare function createSwRouter(options: SwRouterOptions): {
38
44
  attach: (id: string, port: PortLike) => void;
39
45
  match: (url: URL) => PreviewTarget | null;
40
- respond: (target: PreviewTarget) => Promise<RoutedResponse>;
46
+ matchForClient: (url: URL, clientId: string | undefined, origin: string) => PreviewTarget | null;
47
+ rememberClient: (clientId: string | undefined, target: PreviewTarget) => void;
48
+ respond: (target: PreviewTarget, details?: RequestDetails) => Promise<RoutedResponse>;
41
49
  detach: (id: string) => boolean;
42
50
  };
43
51
  export type SwRouter = ReturnType<typeof createSwRouter>;
@@ -1 +1 @@
1
- {"version":3,"file":"swRouter.d.ts","sourceRoot":"","sources":["../src/swRouter.ts"],"names":[],"mappings":"AAEA,4FAA4F;AAC5F,MAAM,WAAW,QAAQ;IACvB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACpC,SAAS,EAAE,CAAC,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;CACxD;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAClD,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAID;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe;iBASjC,MAAM,QAAQ,QAAQ,KAAG,IAAI;iBAW7B,GAAG,KAAG,aAAa,GAAG,IAAI;sBAsBf,aAAa,KAAG,OAAO,CAAC,cAAc,CAAC;iBAkBxB,MAAM;EACrD;AAED,MAAM,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC"}
1
+ {"version":3,"file":"swRouter.d.ts","sourceRoot":"","sources":["../src/swRouter.ts"],"names":[],"mappings":"AAEA,4FAA4F;AAC5F,MAAM,WAAW,QAAQ;IACvB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACpC,SAAS,EAAE,CAAC,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;CACxD;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,6EAA6E;AAC7E,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,WAAW,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAClD,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAID;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe;iBASjC,MAAM,QAAQ,QAAQ,KAAG,IAAI;iBAW7B,GAAG,KAAG,aAAa,GAAG,IAAI;0BAuCjB,GAAG,YAAY,MAAM,GAAG,SAAS,UAAU,MAAM,KAAG,aAAa,GAAG,IAAI;+BAPnE,MAAM,GAAG,SAAS,UAAU,aAAa,KAAG,IAAI;sBAcnD,aAAa,YAAY,cAAc,KAAG,OAAO,CAAC,cAAc,CAAC;iBAkBlB,MAAM;EACrF;AAED,MAAM,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC"}
package/dist/swRouter.js CHANGED
@@ -1,4 +1,4 @@
1
- import { isWireResponse, previewPrefix } from './protocol.js';
1
+ import { PREVIEW_ID, isPortPath, isWireResponse, previewPrefix } from './protocol.js';
2
2
  const TEXT = { 'content-type': 'text/plain; charset=utf-8', 'cache-control': 'no-store' };
3
3
  /**
4
4
  * Request routing for the preview service worker, with no worker globals in it.
@@ -35,7 +35,9 @@ export function createSwRouter(options) {
35
35
  const rest = url.pathname.slice(prefix.length);
36
36
  const slash = rest.indexOf('/');
37
37
  const id = slash < 0 ? rest : rest.slice(0, slash);
38
- if (!id)
38
+ // Ids are `[A-Za-z0-9_-]`; a dotted segment is a real file beside the worker
39
+ // (`sw.js`, `relay.html`), which must load from the network.
40
+ if (!id || !PREVIEW_ID.test(id))
39
41
  return null;
40
42
  return { id, path: slash < 0 ? '/' : rest.slice(slash), search: url.search };
41
43
  }
@@ -52,7 +54,29 @@ export function createSwRouter(options) {
52
54
  await options.requestReattach(id);
53
55
  return arrived;
54
56
  }
55
- async function respond(target) {
57
+ /**
58
+ * Pages served by a virtual server (`/__bfwc/<id>/__port/3000/`) write absolute
59
+ * URLs (`/style.css`, `fetch('/api')`). A service worker sees every request its
60
+ * clients make, so a same-origin request from such a page that falls outside
61
+ * the preview prefix is mapped back under that server's path.
62
+ */
63
+ const clientServers = new Map();
64
+ function rememberClient(clientId, target) {
65
+ if (!clientId || !isPortPath(target.path))
66
+ return;
67
+ const prefix = /^\/__port\/\d+/.exec(target.path)[0];
68
+ clientServers.set(clientId, { id: target.id, prefix });
69
+ if (clientServers.size > 200)
70
+ clientServers.delete(clientServers.keys().next().value);
71
+ }
72
+ function matchForClient(url, clientId, origin) {
73
+ const direct = match(url);
74
+ if (direct || !clientId || url.origin !== origin)
75
+ return direct;
76
+ const server = clientServers.get(clientId);
77
+ return server ? { id: server.id, path: server.prefix + url.pathname, search: url.search } : null;
78
+ }
79
+ async function respond(target, details) {
56
80
  const port = await portFor(target.id);
57
81
  if (!port)
58
82
  return { status: 503, headers: TEXT, body: 'This preview is not running in any open tab.' };
@@ -68,9 +92,9 @@ export function createSwRouter(options) {
68
92
  clearTimeout(timer);
69
93
  resolve({ status: response.status, headers: response.headers, body: response.body });
70
94
  });
71
- port.postMessage({ type: 'request', reqId, path: target.path, search: target.search });
95
+ port.postMessage({ type: 'request', reqId, path: target.path, search: target.search, ...details });
72
96
  });
73
97
  }
74
- return { attach, match, respond, detach: (id) => ports.delete(id) };
98
+ return { attach, match, matchForClient, rememberClient, respond, detach: (id) => ports.delete(id) };
75
99
  }
76
100
  //# sourceMappingURL=swRouter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"swRouter.js","sourceRoot":"","sources":["../src/swRouter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"swRouter.js","sourceRoot":"","sources":["../src/swRouter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { WorkerLike } from './node/processHost.js';
2
+ export interface PreviewTransport {
3
+ /** The origin previews are served from. */
4
+ readonly origin: string;
5
+ /** The worker's scope path on that origin. */
6
+ readonly scope: string;
7
+ /** Hand the worker the port that serves preview `id`. */
8
+ attach(id: string, port: MessagePort): void;
9
+ /** The worker restarted and wants `id` again. */
10
+ onReattach(listener: (id: string) => void): () => void;
11
+ /** Reload every frame showing the preview at `base`. */
12
+ reload(base: string): void;
13
+ /**
14
+ * Start a process worker on the preview origin. Only the relay has one to
15
+ * offer; same-origin mode runs processes on the host's origin, which is the
16
+ * trust it already chose.
17
+ */
18
+ createWorker?(): WorkerLike;
19
+ dispose(): void;
20
+ }
21
+ export declare function sameOriginTransport(serviceWorkerUrl: string, scopeOption?: string): Promise<PreviewTransport>;
22
+ export declare function relayTransport(relayUrl: string): Promise<PreviewTransport>;
23
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAIxD,MAAM,WAAW,gBAAgB;IAC/B,2CAA2C;IAC3C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,yDAAyD;IACzD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5C,iDAAiD;IACjD,UAAU,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACvD,wDAAwD;IACxD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;;OAIG;IACH,YAAY,CAAC,IAAI,UAAU,CAAC;IAC5B,OAAO,IAAI,IAAI,CAAC;CACjB;AAKD,wBAAsB,mBAAmB,CAAC,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA6BnH;AAED,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA6DhF"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * How the host page reaches the preview service worker.
3
+ *
4
+ * Same origin: the page registers the worker itself. Simple, but the preview then
5
+ * runs on the host's origin, with the host's storage and session — fine for a
6
+ * page that only previews code its own user wrote and trusts.
7
+ *
8
+ * Relay: the worker lives on a SEPARATE preview origin. A hidden `relay.html` on
9
+ * that origin registers it and passes ports and reloads across. The preview can
10
+ * then never read the host's cookies, storage or DOM, which is what an editor
11
+ * running AI-written code or arbitrary npm packages needs.
12
+ */
13
+ import { ATTACH, REATTACH, RELAY_ATTACH, RELAY_READY, RELAY_REATTACH, RELAY_RELOAD, RELAY_SPAWN, } from './protocol.js';
14
+ import { workerOverPort } from './relayWorker.js';
15
+ import { broadcastReload, waitForActive } from './worker.js';
16
+ /** How long the relay has to register its worker before boot gives up. */
17
+ const RELAY_TIMEOUT_MS = 15_000;
18
+ export async function sameOriginTransport(serviceWorkerUrl, scopeOption) {
19
+ const container = navigator.serviceWorker;
20
+ if (!container)
21
+ throw new Error('Service workers are unavailable here (a secure context — https or localhost — is required).');
22
+ const scriptUrl = new URL(serviceWorkerUrl, location.href);
23
+ const scope = new URL(scopeOption ?? './', scriptUrl).pathname;
24
+ const registration = await container.register(scriptUrl.href, { scope });
25
+ await waitForActive(registration);
26
+ const listeners = new Set();
27
+ const onWorkerMessage = (event) => {
28
+ if (event.data?.type === REATTACH && typeof event.data.id === 'string')
29
+ for (const listener of listeners)
30
+ listener(event.data.id);
31
+ };
32
+ container.addEventListener('message', onWorkerMessage);
33
+ return {
34
+ origin: location.origin,
35
+ scope,
36
+ attach: (id, port) => registration.active?.postMessage({ type: ATTACH, id }, [port]),
37
+ onReattach(listener) {
38
+ listeners.add(listener);
39
+ return () => listeners.delete(listener);
40
+ },
41
+ reload: broadcastReload,
42
+ dispose() {
43
+ container.removeEventListener('message', onWorkerMessage);
44
+ listeners.clear();
45
+ },
46
+ };
47
+ }
48
+ export async function relayTransport(relayUrl) {
49
+ const url = new URL(relayUrl, location.href);
50
+ const frame = document.createElement('iframe');
51
+ frame.src = url.href;
52
+ frame.title = 'Preview relay';
53
+ frame.setAttribute('aria-hidden', 'true');
54
+ frame.tabIndex = -1;
55
+ frame.style.cssText = 'position:absolute;width:0;height:0;border:0;visibility:hidden';
56
+ const fromRelay = (event) => event.origin === url.origin && event.source === frame.contentWindow;
57
+ const ready = new Promise((resolve, reject) => {
58
+ const timer = setTimeout(() => {
59
+ window.removeEventListener('message', onReady);
60
+ reject(new Error(`The preview relay at ${url.origin} did not start.`));
61
+ }, RELAY_TIMEOUT_MS);
62
+ function onReady(event) {
63
+ if (!fromRelay(event) || event.data?.type !== RELAY_READY)
64
+ return;
65
+ clearTimeout(timer);
66
+ window.removeEventListener('message', onReady);
67
+ if (typeof event.data.error === 'string')
68
+ reject(new Error(event.data.error));
69
+ else
70
+ resolve();
71
+ }
72
+ window.addEventListener('message', onReady);
73
+ });
74
+ document.body.appendChild(frame);
75
+ try {
76
+ await ready;
77
+ }
78
+ catch (error) {
79
+ frame.remove();
80
+ throw error;
81
+ }
82
+ const listeners = new Set();
83
+ const onRelayMessage = (event) => {
84
+ if (!fromRelay(event) || event.data?.type !== RELAY_REATTACH || typeof event.data.id !== 'string')
85
+ return;
86
+ for (const listener of listeners)
87
+ listener(event.data.id);
88
+ };
89
+ window.addEventListener('message', onRelayMessage);
90
+ const post = (message, transfer = []) => frame.contentWindow?.postMessage(message, url.origin, transfer);
91
+ return {
92
+ origin: url.origin,
93
+ scope: new URL('./', url).pathname,
94
+ attach: (id, port) => post({ type: RELAY_ATTACH, id }, [port]),
95
+ onReattach(listener) {
96
+ listeners.add(listener);
97
+ return () => listeners.delete(listener);
98
+ },
99
+ reload: (base) => post({ type: RELAY_RELOAD, base }),
100
+ createWorker() {
101
+ const channel = new MessageChannel();
102
+ post({ type: RELAY_SPAWN }, [channel.port2]);
103
+ return workerOverPort(channel.port1);
104
+ },
105
+ dispose() {
106
+ window.removeEventListener('message', onRelayMessage);
107
+ listeners.clear();
108
+ frame.remove();
109
+ },
110
+ };
111
+ }
112
+ //# sourceMappingURL=transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /** Resolve once the registration has an active worker. */
2
+ export declare function waitForActive(registration: ServiceWorkerRegistration): Promise<ServiceWorker>;
3
+ /** Reload every frame (on this origin) showing the preview served at `base`. */
4
+ export declare function broadcastReload(base: string): void;
5
+ //# sourceMappingURL=worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAKA,0DAA0D;AAC1D,wBAAgB,aAAa,CAAC,YAAY,EAAE,yBAAyB,GAAG,OAAO,CAAC,aAAa,CAAC,CAU7F;AAED,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAKlD"}
package/dist/worker.js ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Small helpers shared by the page-side transports and the relay page.
3
+ */
4
+ import { reloadChannelName } from '@seanhogg/builderforce-webcontainers-core';
5
+ /** Resolve once the registration has an active worker. */
6
+ export function waitForActive(registration) {
7
+ if (registration.active)
8
+ return Promise.resolve(registration.active);
9
+ const installing = registration.installing ?? registration.waiting;
10
+ if (!installing)
11
+ return Promise.reject(new Error('The preview service worker did not install.'));
12
+ return new Promise((resolve, reject) => {
13
+ installing.addEventListener('statechange', () => {
14
+ if (installing.state === 'activated' && registration.active)
15
+ resolve(registration.active);
16
+ if (installing.state === 'redundant')
17
+ reject(new Error('The preview service worker failed to install.'));
18
+ });
19
+ });
20
+ }
21
+ /** Reload every frame (on this origin) showing the preview served at `base`. */
22
+ export function broadcastReload(base) {
23
+ if (typeof BroadcastChannel === 'undefined')
24
+ return;
25
+ const channel = new BroadcastChannel(reloadChannelName(base));
26
+ channel.postMessage('reload');
27
+ channel.close();
28
+ }
29
+ //# sourceMappingURL=worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.js","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAE9E,0DAA0D;AAC1D,MAAM,UAAU,aAAa,CAAC,YAAuC;IACnE,IAAI,YAAY,CAAC,MAAM;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IACrE,MAAM,UAAU,GAAG,YAAY,CAAC,UAAU,IAAI,YAAY,CAAC,OAAO,CAAC;IACnE,IAAI,CAAC,UAAU;QAAE,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC,CAAC;IACjG,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,UAAU,CAAC,gBAAgB,CAAC,aAAa,EAAE,GAAG,EAAE;YAC9C,IAAI,UAAU,CAAC,KAAK,KAAK,WAAW,IAAI,YAAY,CAAC,MAAM;gBAAE,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;YAC1F,IAAI,UAAU,CAAC,KAAK,KAAK,WAAW;gBAAE,MAAM,CAAC,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC,CAAC;QAC3G,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,IAAI,OAAO,gBAAgB,KAAK,WAAW;QAAE,OAAO;IACpD,MAAM,OAAO,GAAG,IAAI,gBAAgB,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9D,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC9B,OAAO,CAAC,KAAK,EAAE,CAAC;AAClB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seanhogg/builderforce-webcontainers",
3
- "version": "2026.10.1",
3
+ "version": "2026.10.3",
4
4
  "description": "BuilderForce WebContainers — browser runtime. Boots an in-browser dev server for a project held in memory and serves it to an iframe through a service worker. Open source, no COOP/COEP, no session caps.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -10,7 +10,19 @@
10
10
  "types": "./dist/index.d.ts",
11
11
  "import": "./dist/index.js"
12
12
  },
13
- "./sw.js": "./dist/sw.js"
13
+ "./check": {
14
+ "types": "./dist/check/index.d.ts",
15
+ "import": "./dist/check/index.js"
16
+ },
17
+ "./check/worker.js": "./dist/check/worker.js",
18
+ "./node/worker.js": "./dist/node/worker.js",
19
+ "./assets": {
20
+ "types": "./dist/assets.d.ts",
21
+ "import": "./dist/assets.js"
22
+ },
23
+ "./sw.js": "./dist/sw.js",
24
+ "./relay.html": "./dist/relay.html",
25
+ "./process-worker.js": "./dist/process-worker.js"
14
26
  },
15
27
  "type": "module",
16
28
  "sideEffects": false,
@@ -53,10 +65,12 @@
53
65
  },
54
66
  "dependencies": {
55
67
  "esbuild-wasm": "^0.24.0",
56
- "@seanhogg/builderforce-webcontainers-core": "^2026.10.1"
68
+ "@seanhogg/builderforce-webcontainers-core": "^2026.10.3"
57
69
  },
58
70
  "devDependencies": {
71
+ "@vue/compiler-sfc": "^3.5.0",
59
72
  "esbuild": "^0.24.0",
73
+ "svelte": "^5.0.0",
60
74
  "typescript": "^5.5.0",
61
75
  "vitest": "^2.1.0"
62
76
  },
@@ -0,0 +1,47 @@
1
+ import type { FetchLike, FetchResponseLike } from '@seanhogg/builderforce-webcontainers-core/check';
2
+
3
+ /**
4
+ * `fetch` for type definitions, backed by Cache Storage so a new tab (or a new
5
+ * worker) does not download lib.dom.d.ts and React's types again.
6
+ *
7
+ * Only immutable URLs are cached: lib files under a pinned TypeScript version and
8
+ * package files at an exact `name@x.y.z`. Range lookups (`react@^18`) always go to
9
+ * the network, so a new release is picked up. A cached entry keeps the final URL
10
+ * and the `X-TypeScript-Types` header, the two things acquisition reads besides
11
+ * the body.
12
+ */
13
+ const CACHE_NAME = 'bfwc-types-v1';
14
+ const EXACT_VERSION = /@\d+\.\d+\.\d+(?:[-+][\w.]+)?\//;
15
+ const FINAL_URL = 'x-bfwc-final-url';
16
+ const TYPES = 'x-typescript-types';
17
+
18
+ interface CacheLike {
19
+ match(url: string): Promise<Response | undefined>;
20
+ put(url: string, response: Response): Promise<void>;
21
+ }
22
+
23
+ function wrap(url: string, body: string, types: string | null): FetchResponseLike {
24
+ return { ok: true, url, headers: { get: (name) => (name.toLowerCase() === TYPES ? types : null) }, text: async () => body };
25
+ }
26
+
27
+ export function createCachedFetch(base: typeof fetch = fetch): FetchLike {
28
+ let cache: Promise<CacheLike | undefined> | undefined;
29
+ const open = () =>
30
+ (cache ??= typeof caches === 'undefined' ? Promise.resolve(undefined) : caches.open(CACHE_NAME).catch(() => undefined));
31
+
32
+ return async (url) => {
33
+ const cacheable = EXACT_VERSION.test(url);
34
+ const store = cacheable ? await open() : undefined;
35
+ const hit = await store?.match(url).catch(() => undefined);
36
+ if (hit) return wrap(hit.headers.get(FINAL_URL) ?? url, await hit.text(), hit.headers.get(TYPES));
37
+
38
+ const res = await base(url);
39
+ if (!res.ok || !store) return res;
40
+ const body = await res.text();
41
+ const types = res.headers.get(TYPES);
42
+ const headers: Record<string, string> = { [FINAL_URL]: res.url || url };
43
+ if (types) headers[TYPES] = types;
44
+ await store.put(url, new Response(body, { headers })).catch(() => undefined);
45
+ return wrap(res.url || url, body, types);
46
+ };
47
+ }