@seanhogg/builderforce-webcontainers 2026.10.6 → 2026.10.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets.js +3 -3
- package/dist/check/worker.js +8 -8
- package/dist/host.d.ts +25 -9
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +39 -25
- package/dist/host.js.map +1 -1
- package/dist/index.d.ts +9 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/node/index.d.ts +5 -9
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +1 -25
- package/dist/node/index.js.map +1 -1
- package/dist/node/portResponder.d.ts +45 -0
- package/dist/node/portResponder.d.ts.map +1 -0
- package/dist/node/portResponder.js +136 -0
- package/dist/node/portResponder.js.map +1 -0
- package/dist/node/processHost.d.ts +26 -2
- package/dist/node/processHost.d.ts.map +1 -1
- package/dist/node/processHost.js +67 -16
- package/dist/node/processHost.js.map +1 -1
- package/dist/node/protocol.d.ts +42 -9
- package/dist/node/protocol.d.ts.map +1 -1
- package/dist/node/protocol.js.map +1 -1
- package/dist/node/responseWire.d.ts +27 -0
- package/dist/node/responseWire.d.ts.map +1 -0
- package/dist/node/responseWire.js +99 -0
- package/dist/node/responseWire.js.map +1 -0
- package/dist/node/worker.js +143 -127
- package/dist/node/workerProcess.d.ts +3 -1
- package/dist/node/workerProcess.d.ts.map +1 -1
- package/dist/node/workerProcess.js +49 -19
- package/dist/node/workerProcess.js.map +1 -1
- package/dist/previewBridge.d.ts +37 -0
- package/dist/previewBridge.d.ts.map +1 -0
- package/dist/previewBridge.js +116 -0
- package/dist/previewBridge.js.map +1 -0
- package/dist/previewMessages.d.ts +24 -0
- package/dist/previewMessages.d.ts.map +1 -0
- package/dist/previewMessages.js +59 -0
- package/dist/previewMessages.js.map +1 -0
- package/dist/process-worker.js +143 -127
- package/dist/protocol.d.ts +23 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +8 -0
- package/dist/protocol.js.map +1 -1
- package/dist/relay.html +1 -1
- package/dist/snapshotStore.d.ts +30 -0
- package/dist/snapshotStore.d.ts.map +1 -0
- package/dist/snapshotStore.js +115 -0
- package/dist/snapshotStore.js.map +1 -0
- package/dist/sw.js +1 -1
- package/dist/swRouter.d.ts +8 -1
- package/dist/swRouter.d.ts.map +1 -1
- package/dist/swRouter.js +40 -5
- package/dist/swRouter.js.map +1 -1
- package/package.json +3 -2
- package/src/host.ts +62 -31
- package/src/index.ts +8 -3
- package/src/node/index.ts +5 -24
- package/src/node/portResponder.ts +175 -0
- package/src/node/processHost.ts +92 -15
- package/src/node/protocol.ts +25 -4
- package/src/node/responseWire.ts +97 -0
- package/src/node/workerProcess.ts +52 -21
- package/src/previewBridge.ts +148 -0
- package/src/previewMessages.ts +79 -0
- package/src/protocol.ts +35 -1
- package/src/snapshotStore.ts +135 -0
- package/src/sw.ts +2 -0
- package/src/swRouter.ts +35 -6
package/dist/protocol.d.ts
CHANGED
|
@@ -54,8 +54,28 @@ export interface WireResponse {
|
|
|
54
54
|
reqId: number;
|
|
55
55
|
status: number;
|
|
56
56
|
headers: Record<string, string>;
|
|
57
|
-
body:
|
|
57
|
+
/** The whole body — or, with `streaming`, null: the body follows as `chunk` messages until `end`. */
|
|
58
|
+
body: string | ArrayBuffer | null;
|
|
59
|
+
streaming?: boolean;
|
|
58
60
|
}
|
|
61
|
+
/** page → worker: the next piece of a streaming response's body. */
|
|
62
|
+
export interface WireChunk {
|
|
63
|
+
type: 'chunk';
|
|
64
|
+
reqId: number;
|
|
65
|
+
data: ArrayBuffer;
|
|
66
|
+
}
|
|
67
|
+
/** page → worker: a streaming response is complete (`error`: it broke off, e.g. the server process exited). */
|
|
68
|
+
export interface WireEnd {
|
|
69
|
+
type: 'end';
|
|
70
|
+
reqId: number;
|
|
71
|
+
error?: string;
|
|
72
|
+
}
|
|
73
|
+
/** worker → page: the preview stopped reading a streaming response (closed an EventSource, navigated away). */
|
|
74
|
+
export interface WireCancel {
|
|
75
|
+
type: 'cancel';
|
|
76
|
+
reqId: number;
|
|
77
|
+
}
|
|
78
|
+
export type WireReply = WireResponse | WireChunk | WireEnd;
|
|
59
79
|
/**
|
|
60
80
|
* The URL prefix previews live under, for a worker scope. Serving the worker from
|
|
61
81
|
* `/__bfwc/sw.js` (scope `/__bfwc/`) keeps it off every other page of the host app;
|
|
@@ -65,4 +85,6 @@ export declare function previewPrefix(scopePath: string): string;
|
|
|
65
85
|
export declare function previewBase(scopePath: string, id: string): string;
|
|
66
86
|
export declare function isWireRequest(value: unknown): value is WireRequest;
|
|
67
87
|
export declare function isWireResponse(value: unknown): value is WireResponse;
|
|
88
|
+
export declare function isWireReply(value: unknown): value is WireReply;
|
|
89
|
+
export declare function isWireCancel(value: unknown): value is WireCancel;
|
|
68
90
|
//# sourceMappingURL=protocol.d.ts.map
|
package/dist/protocol.d.ts.map
CHANGED
|
@@ -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;;;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;
|
|
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,qGAAqG;IACrG,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAAC;IAClC,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,oEAAoE;AACpE,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,WAAW,CAAC;CACnB;AAED,+GAA+G;AAC/G,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,KAAK,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,+GAA+G;AAC/G,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,SAAS,GAAG,YAAY,GAAG,SAAS,GAAG,OAAO,CAAC;AAE3D;;;;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;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAG9D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAGhE"}
|
package/dist/protocol.js
CHANGED
|
@@ -53,4 +53,12 @@ export function isWireResponse(value) {
|
|
|
53
53
|
const v = value;
|
|
54
54
|
return !!v && v.type === 'response' && typeof v.reqId === 'number';
|
|
55
55
|
}
|
|
56
|
+
export function isWireReply(value) {
|
|
57
|
+
const v = value;
|
|
58
|
+
return !!v && (v.type === 'response' || v.type === 'chunk' || v.type === 'end') && typeof v.reqId === 'number';
|
|
59
|
+
}
|
|
60
|
+
export function isWireCancel(value) {
|
|
61
|
+
const v = value;
|
|
62
|
+
return !!v && v.type === 'cancel' && typeof v.reqId === 'number';
|
|
63
|
+
}
|
|
56
64
|
//# sourceMappingURL=protocol.js.map
|
package/dist/protocol.js.map
CHANGED
|
@@ -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;;;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;
|
|
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;AAkCD;;;;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;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,MAAM,CAAC,GAAG,KAAyB,CAAC;IACpC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,IAAI,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC;AACjH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,MAAM,CAAC,GAAG,KAA0B,CAAC;IACrC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC;AACnE,CAAC"}
|
package/dist/relay.html
CHANGED
|
@@ -1,2 +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",
|
|
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",s="bfwc:relay-ready",p="bfwc:relay-attach",l="bfwc:relay-reattach",f="bfwc:relay-reload",d="bfwc:relay-spawn";function m(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 u="process-worker.js";function i(e){return`bfwc:reload:${e}`}function g(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 n=window.parent;async function y(){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 g(t),e.addEventListener("message",r=>{r.data?.type===c&&typeof r.data.id=="string"&&n.postMessage({type:l,id:r.data.id},"*")}),addEventListener("message",r=>{if(r.source!==n)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===d&&r.ports[0]&&m(r.ports[0],new Worker(new URL(`./${u}`,location.href),{name:"bfwc-process"}))}),n.postMessage({type:s},"*")}y().catch(e=>{n.postMessage({type:s,error:e instanceof Error?e.message:String(e)},"*")});})();
|
|
2
2
|
</script></body></html>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapshots that outlive the page: `exportZip` bytes kept in IndexedDB under a
|
|
3
|
+
* key the host chooses (say, the project id plus a hash of package.json), so a
|
|
4
|
+
* reload can `importZip` an installed `node_modules` instead of reinstalling.
|
|
5
|
+
*
|
|
6
|
+
* Bounded: snapshots over `maxBytes` are not stored, and only the
|
|
7
|
+
* `maxEntries` most recently used are kept. Every operation fails soft —
|
|
8
|
+
* storage blocked (private windows) or full means a miss, never a throw.
|
|
9
|
+
*/
|
|
10
|
+
export interface SnapshotStoreOptions {
|
|
11
|
+
/** IndexedDB database name. Default `bfwc-snapshots`. */
|
|
12
|
+
name?: string;
|
|
13
|
+
/** Snapshots kept, least recently used dropped first. Default 4. */
|
|
14
|
+
maxEntries?: number;
|
|
15
|
+
/** Largest snapshot stored, in bytes. Default 256 MB. */
|
|
16
|
+
maxBytes?: number;
|
|
17
|
+
/** Injected for tests; defaults to `globalThis.indexedDB`. */
|
|
18
|
+
indexedDB?: IDBFactory;
|
|
19
|
+
}
|
|
20
|
+
export interface SnapshotStore {
|
|
21
|
+
/** The snapshot saved under `key`, or undefined. */
|
|
22
|
+
load(key: string): Promise<Uint8Array | undefined>;
|
|
23
|
+
/** Save (replacing any snapshot under `key`); false when it was not stored (too large, storage unavailable). */
|
|
24
|
+
save(key: string, zip: Uint8Array): Promise<boolean>;
|
|
25
|
+
remove(key: string): Promise<void>;
|
|
26
|
+
/** Keys held, most recently used first. */
|
|
27
|
+
keys(): Promise<string[]>;
|
|
28
|
+
}
|
|
29
|
+
export declare function createSnapshotStore(options?: SnapshotStoreOptions): SnapshotStore;
|
|
30
|
+
//# sourceMappingURL=snapshotStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshotStore.d.ts","sourceRoot":"","sources":["../src/snapshotStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,oBAAoB;IACnC,yDAAyD;IACzD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,UAAU,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,oDAAoD;IACpD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;IACnD,gHAAgH;IAChH,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,2CAA2C;IAC3C,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAC3B;AAyBD,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,oBAAyB,GAAG,aAAa,CAgFrF"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapshots that outlive the page: `exportZip` bytes kept in IndexedDB under a
|
|
3
|
+
* key the host chooses (say, the project id plus a hash of package.json), so a
|
|
4
|
+
* reload can `importZip` an installed `node_modules` instead of reinstalling.
|
|
5
|
+
*
|
|
6
|
+
* Bounded: snapshots over `maxBytes` are not stored, and only the
|
|
7
|
+
* `maxEntries` most recently used are kept. Every operation fails soft —
|
|
8
|
+
* storage blocked (private windows) or full means a miss, never a throw.
|
|
9
|
+
*/
|
|
10
|
+
const STORE = 'snapshots';
|
|
11
|
+
function request(req) {
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
req.onsuccess = () => resolve(req.result);
|
|
14
|
+
req.onerror = () => reject(req.error);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function done(tx) {
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
tx.oncomplete = () => resolve();
|
|
20
|
+
tx.onerror = () => reject(tx.error);
|
|
21
|
+
tx.onabort = () => reject(tx.error);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
export function createSnapshotStore(options = {}) {
|
|
25
|
+
const maxEntries = options.maxEntries ?? 4;
|
|
26
|
+
const maxBytes = options.maxBytes ?? 256 * 1024 * 1024;
|
|
27
|
+
let opening;
|
|
28
|
+
const open = () => (opening ??= (async () => {
|
|
29
|
+
const factory = options.indexedDB ?? globalThis.indexedDB;
|
|
30
|
+
if (!factory)
|
|
31
|
+
return undefined;
|
|
32
|
+
try {
|
|
33
|
+
const req = factory.open(options.name ?? 'bfwc-snapshots', 1);
|
|
34
|
+
req.onupgradeneeded = () => {
|
|
35
|
+
if (!req.result.objectStoreNames.contains(STORE))
|
|
36
|
+
req.result.createObjectStore(STORE, { keyPath: 'key' });
|
|
37
|
+
};
|
|
38
|
+
return await request(req);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
})());
|
|
44
|
+
const rows = async (db) => {
|
|
45
|
+
const all = (await request(db.transaction(STORE, 'readonly').objectStore(STORE).getAll()));
|
|
46
|
+
return all.sort((a, b) => b.usedAt - a.usedAt);
|
|
47
|
+
};
|
|
48
|
+
return {
|
|
49
|
+
async load(key) {
|
|
50
|
+
const db = await open();
|
|
51
|
+
if (!db)
|
|
52
|
+
return undefined;
|
|
53
|
+
try {
|
|
54
|
+
const tx = db.transaction(STORE, 'readwrite');
|
|
55
|
+
const store = tx.objectStore(STORE);
|
|
56
|
+
const row = (await request(store.get(key)));
|
|
57
|
+
if (row)
|
|
58
|
+
store.put({ ...row, usedAt: Date.now() });
|
|
59
|
+
await done(tx);
|
|
60
|
+
return row?.zip;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
async save(key, zip) {
|
|
67
|
+
if (zip.byteLength > maxBytes)
|
|
68
|
+
return false;
|
|
69
|
+
const db = await open();
|
|
70
|
+
if (!db)
|
|
71
|
+
return false;
|
|
72
|
+
try {
|
|
73
|
+
const tx = db.transaction(STORE, 'readwrite');
|
|
74
|
+
tx.objectStore(STORE).put({ key, zip, usedAt: Date.now() });
|
|
75
|
+
await done(tx);
|
|
76
|
+
const stale = (await rows(db)).slice(maxEntries);
|
|
77
|
+
if (stale.length) {
|
|
78
|
+
const prune = db.transaction(STORE, 'readwrite');
|
|
79
|
+
for (const row of stale)
|
|
80
|
+
prune.objectStore(STORE).delete(row.key);
|
|
81
|
+
await done(prune);
|
|
82
|
+
}
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
async remove(key) {
|
|
90
|
+
const db = await open();
|
|
91
|
+
if (!db)
|
|
92
|
+
return;
|
|
93
|
+
try {
|
|
94
|
+
const tx = db.transaction(STORE, 'readwrite');
|
|
95
|
+
tx.objectStore(STORE).delete(key);
|
|
96
|
+
await done(tx);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// storage unavailable: nothing to remove
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
async keys() {
|
|
103
|
+
const db = await open();
|
|
104
|
+
if (!db)
|
|
105
|
+
return [];
|
|
106
|
+
try {
|
|
107
|
+
return (await rows(db)).map((row) => row.key);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=snapshotStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshotStore.js","sourceRoot":"","sources":["../src/snapshotStore.ts"],"names":[],"mappings":"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"}
|
package/dist/sw.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";(()=>{var
|
|
1
|
+
"use strict";(()=>{var b="__bfwc",W=/^[A-Za-z0-9_-]+$/,x="bfwc:attach",A="bfwc:reattach";var D="__port";function g(n){return n.startsWith(`/${D}/`)}function E(n){let r=n.endsWith("/")?n:n+"/";return r.endsWith(`/${b}/`)?r:`${r}${b}/`}function P(n){let r=n;return!!r&&(r.type==="response"||r.type==="chunk"||r.type==="end")&&typeof r.reqId=="number"}var k={"content-type":"text/plain; charset=utf-8","cache-control":"no-store"};function T(n){let r=E(n.scopePath),c=n.timeoutMs??3e4,h=n.reattachWaitMs??3e3,u=new Map,a=new Map,p=new Map,l=new Map,w=0;function R(t,s){u.set(t,s),s.onmessage=i=>{let e=i.data;if(!P(e))return;if(e.type==="response"){p.get(e.reqId)?.(e),p.delete(e.reqId);return}let o=l.get(e.reqId);if(o)try{if(e.type==="chunk")return o.enqueue(new Uint8Array(e.data));l.delete(e.reqId),e.error?o.error(new Error(e.error)):o.close()}catch{l.delete(e.reqId)}};for(let i of a.get(t)??[])i(s);a.delete(t)}function v(t){if(!t.pathname.startsWith(r))return null;let s=t.pathname.slice(r.length),i=s.indexOf("/"),e=i<0?s:s.slice(0,i);return!e||!W.test(e)?null:{id:e,path:i<0?"/":s.slice(i),search:t.search}}async function L(t){let s=u.get(t);if(s)return s;let i=new Promise(e=>{let o=a.get(t)??[];o.push(e),a.set(t,o),setTimeout(()=>e(void 0),h)});return await n.requestReattach(t),i}let y=new Map;function C(t,s){if(!t||!g(s.path))return;let i=/^\/__port\/\d+/.exec(s.path)[0];y.set(t,{id:s.id,prefix:i}),y.size>200&&y.delete(y.keys().next().value)}function I(t,s,i){let e=v(t);if(e||!s||t.origin!==i)return e;let o=y.get(s);return o?{id:o.id,path:o.prefix+t.pathname,search:t.search}:null}async function M(t,s){let i=await L(t.id);if(!i)return{status:503,headers:k,body:"This preview is not running in any open tab."};let e=++w;return new Promise(o=>{let _=setTimeout(()=>{p.delete(e),u.get(t.id)===i&&u.delete(t.id),o({status:504,headers:k,body:"The preview did not answer in time."})},c);p.set(e,f=>{if(clearTimeout(_),!f.streaming)return o({status:f.status,headers:f.headers,body:f.body});let S=new ReadableStream({start:U=>void l.set(e,U),cancel:()=>{l.delete(e)&&i.postMessage({type:"cancel",reqId:e})}});o({status:f.status,headers:f.headers,body:S})}),i.postMessage({type:"request",reqId:e,path:t.path,search:t.search,...s})})}return{attach:R,match:v,matchForClient:I,rememberClient:C,respond:M,detach:t=>u.delete(t)}}var d=globalThis,m=T({scopePath:new URL(d.registration.scope).pathname,async requestReattach(n){let r=await d.clients.matchAll({type:"window",includeUncontrolled:!0});for(let c of r)c.postMessage({type:A,id:n})}});d.addEventListener("install",()=>{d.skipWaiting()});d.addEventListener("activate",n=>{n.waitUntil(d.clients.claim())});d.addEventListener("message",n=>{let r=n.data,c=n.ports[0];r?.type===x&&typeof r.id=="string"&&c&&m.attach(r.id,c)});var q=new Set([101,204,205,304]);d.addEventListener("fetch",n=>{let{request:r}=n,c=m.matchForClient(new URL(r.url),n.clientId,new URL(d.registration.scope).origin);if(!c)return;let h=g(c.path);r.method!=="GET"&&!h||(h&&m.rememberClient(n.resultingClientId,c),n.respondWith((async()=>{let u;if(h){let p={};r.headers.forEach((w,R)=>p[R]=w);let l=r.method==="GET"||r.method==="HEAD"?null:await r.arrayBuffer();u={method:r.method,headers:p,body:l}}let a=await m.respond(c,u);return q.has(a.status)&&a.body instanceof ReadableStream&&a.body.cancel(),new Response(q.has(a.status)?null:a.body,{status:a.status,headers:a.headers})})()))});})();
|
package/dist/swRouter.d.ts
CHANGED
|
@@ -19,7 +19,8 @@ export interface RequestDetails {
|
|
|
19
19
|
export interface RoutedResponse {
|
|
20
20
|
status: number;
|
|
21
21
|
headers: Record<string, string>;
|
|
22
|
-
body:
|
|
22
|
+
/** A stream when the page streams the body (a virtual server's response): chunks arrive as the server writes them. */
|
|
23
|
+
body: string | ArrayBuffer | ReadableStream<Uint8Array> | null;
|
|
23
24
|
}
|
|
24
25
|
export interface SwRouterOptions {
|
|
25
26
|
/** The worker registration's scope path (`/__bfwc/` or `/`). */
|
|
@@ -39,6 +40,12 @@ export interface SwRouterOptions {
|
|
|
39
40
|
* router asks open pages to re-attach and waits briefly. A port that stops
|
|
40
41
|
* answering (its tab closed) is dropped on timeout, so the NEXT request goes
|
|
41
42
|
* through the re-attach path instead of timing out forever.
|
|
43
|
+
*
|
|
44
|
+
* A virtual server's response streams: the page sends the head (`streaming`),
|
|
45
|
+
* then `chunk` messages and an `end`, and the router answers the fetch with a
|
|
46
|
+
* ReadableStream fed by them. The timeout covers only the wait for the head (an
|
|
47
|
+
* event stream may stay open indefinitely); cancelling the stream (the preview
|
|
48
|
+
* closed its EventSource) tells the page to hang up on the server.
|
|
42
49
|
*/
|
|
43
50
|
export declare function createSwRouter(options: SwRouterOptions): {
|
|
44
51
|
attach: (id: string, port: PortLike) => void;
|
package/dist/swRouter.d.ts.map
CHANGED
|
@@ -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,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;
|
|
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,sHAAsH;IACtH,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,cAAc,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;CAChE;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;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe;iBAUjC,MAAM,QAAQ,QAAQ,KAAG,IAAI;iBAyB7B,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;iBAyBlB,MAAM;EACrF;AAED,MAAM,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC"}
|
package/dist/swRouter.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PREVIEW_ID, isPortPath,
|
|
1
|
+
import { PREVIEW_ID, isPortPath, isWireReply, 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.
|
|
@@ -8,6 +8,12 @@ const TEXT = { 'content-type': 'text/plain; charset=utf-8', 'cache-control': 'no
|
|
|
8
8
|
* router asks open pages to re-attach and waits briefly. A port that stops
|
|
9
9
|
* answering (its tab closed) is dropped on timeout, so the NEXT request goes
|
|
10
10
|
* through the re-attach path instead of timing out forever.
|
|
11
|
+
*
|
|
12
|
+
* A virtual server's response streams: the page sends the head (`streaming`),
|
|
13
|
+
* then `chunk` messages and an `end`, and the router answers the fetch with a
|
|
14
|
+
* ReadableStream fed by them. The timeout covers only the wait for the head (an
|
|
15
|
+
* event stream may stay open indefinitely); cancelling the stream (the preview
|
|
16
|
+
* closed its EventSource) tells the page to hang up on the server.
|
|
11
17
|
*/
|
|
12
18
|
export function createSwRouter(options) {
|
|
13
19
|
const prefix = previewPrefix(options.scopePath);
|
|
@@ -16,14 +22,34 @@ export function createSwRouter(options) {
|
|
|
16
22
|
const ports = new Map();
|
|
17
23
|
const waiters = new Map();
|
|
18
24
|
const pending = new Map();
|
|
25
|
+
const streams = new Map();
|
|
19
26
|
let nextId = 0;
|
|
20
27
|
function attach(id, port) {
|
|
21
28
|
ports.set(id, port);
|
|
22
29
|
port.onmessage = (event) => {
|
|
23
|
-
|
|
30
|
+
const reply = event.data;
|
|
31
|
+
if (!isWireReply(reply))
|
|
32
|
+
return;
|
|
33
|
+
if (reply.type === 'response') {
|
|
34
|
+
pending.get(reply.reqId)?.(reply);
|
|
35
|
+
pending.delete(reply.reqId);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const controller = streams.get(reply.reqId);
|
|
39
|
+
if (!controller)
|
|
24
40
|
return;
|
|
25
|
-
|
|
26
|
-
|
|
41
|
+
try {
|
|
42
|
+
if (reply.type === 'chunk')
|
|
43
|
+
return controller.enqueue(new Uint8Array(reply.data));
|
|
44
|
+
streams.delete(reply.reqId);
|
|
45
|
+
if (reply.error)
|
|
46
|
+
controller.error(new Error(reply.error));
|
|
47
|
+
else
|
|
48
|
+
controller.close();
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
streams.delete(reply.reqId); // the reader already went away
|
|
52
|
+
}
|
|
27
53
|
};
|
|
28
54
|
for (const wake of waiters.get(id) ?? [])
|
|
29
55
|
wake(port);
|
|
@@ -90,7 +116,16 @@ export function createSwRouter(options) {
|
|
|
90
116
|
}, timeoutMs);
|
|
91
117
|
pending.set(reqId, (response) => {
|
|
92
118
|
clearTimeout(timer);
|
|
93
|
-
|
|
119
|
+
if (!response.streaming)
|
|
120
|
+
return resolve({ status: response.status, headers: response.headers, body: response.body });
|
|
121
|
+
const body = new ReadableStream({
|
|
122
|
+
start: (controller) => void streams.set(reqId, controller),
|
|
123
|
+
cancel: () => {
|
|
124
|
+
if (streams.delete(reqId))
|
|
125
|
+
port.postMessage({ type: 'cancel', reqId });
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
resolve({ status: response.status, headers: response.headers, body });
|
|
94
129
|
});
|
|
95
130
|
port.postMessage({ type: 'request', reqId, path: target.path, search: target.search, ...details });
|
|
96
131
|
});
|
package/dist/swRouter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swRouter.js","sourceRoot":"","sources":["../src/swRouter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"swRouter.js","sourceRoot":"","sources":["../src/swRouter.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seanhogg/builderforce-webcontainers",
|
|
3
|
-
"version": "2026.10.
|
|
3
|
+
"version": "2026.10.8",
|
|
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",
|
|
@@ -65,11 +65,12 @@
|
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"esbuild-wasm": "^0.24.0",
|
|
68
|
-
"@seanhogg/builderforce-webcontainers-core": "^2026.10.
|
|
68
|
+
"@seanhogg/builderforce-webcontainers-core": "^2026.10.8"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@vue/compiler-sfc": "^3.5.0",
|
|
72
72
|
"esbuild": "^0.24.0",
|
|
73
|
+
"fake-indexeddb": "^6.0.0",
|
|
73
74
|
"svelte": "^5.0.0",
|
|
74
75
|
"typescript": "^5.5.0",
|
|
75
76
|
"vitest": "^2.1.0"
|
package/src/host.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DevServer,
|
|
3
|
-
|
|
3
|
+
PortCookieJars,
|
|
4
4
|
VirtualFileSystem,
|
|
5
5
|
buildProject,
|
|
6
|
+
exportZip,
|
|
7
|
+
importZip,
|
|
6
8
|
type BuildResult,
|
|
7
9
|
type Bundler,
|
|
8
10
|
type ComponentCompilers,
|
|
@@ -14,9 +16,12 @@ import {
|
|
|
14
16
|
type RuntimeProcess,
|
|
15
17
|
type ServerAddress,
|
|
16
18
|
type Transformer,
|
|
19
|
+
type ZipExportOptions,
|
|
17
20
|
} from '@seanhogg/builderforce-webcontainers-core';
|
|
18
|
-
import { PREVIEW_ID, isPortPath, isWireRequest, previewBase, type
|
|
19
|
-
import {
|
|
21
|
+
import { PREVIEW_ID, isPortPath, isWireCancel, isWireRequest, previewBase, type WireReply, type WireResponse } from './protocol.js';
|
|
22
|
+
import { createPortResponder, createProcessHost, type NodeRuntimeOptions, type ProcessInfo } from './node/index.js';
|
|
23
|
+
import { listenToPreview, type PreviewConsoleMessage, type PreviewError } from './previewMessages.js';
|
|
24
|
+
import { listenToPreviewBridge } from './previewBridge.js';
|
|
20
25
|
import { relayTransport, sameOriginTransport } from './transport.js';
|
|
21
26
|
import { createEsbuildWasmBundler, createEsbuildWasmTransformer } from './esbuildWasm.js';
|
|
22
27
|
import { createCdnComponentCompilers } from './componentCompilers.js';
|
|
@@ -59,6 +64,8 @@ export interface CommonRuntimeOptions {
|
|
|
59
64
|
attribution?: boolean;
|
|
60
65
|
/** Reload open previews when files change. Default true. */
|
|
61
66
|
liveReload?: boolean;
|
|
67
|
+
/** Your scripts (JavaScript source), run in every preview page before the app — analytics, test hooks, a mock `fetch`. */
|
|
68
|
+
previewScripts?: string[];
|
|
62
69
|
/** The Node runtime behind `run` (process workers, npm registry). */
|
|
63
70
|
node?: NodeRuntimeOptions;
|
|
64
71
|
}
|
|
@@ -66,12 +73,6 @@ export interface CommonRuntimeOptions {
|
|
|
66
73
|
/** Edits arrive in bursts (a save, an agent writing several files): reload once per burst. */
|
|
67
74
|
const RELOAD_DEBOUNCE_MS = 120;
|
|
68
75
|
|
|
69
|
-
export interface PreviewError {
|
|
70
|
-
message: string;
|
|
71
|
-
stack?: string;
|
|
72
|
-
href: string;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
76
|
export interface RuntimeBuildOptions {
|
|
76
77
|
/** Where the site will be served from. Default `./` (works from any directory). */
|
|
77
78
|
base?: string;
|
|
@@ -85,8 +86,14 @@ export interface PreviewRuntime {
|
|
|
85
86
|
readonly url: string;
|
|
86
87
|
readonly fs: VirtualFileSystem;
|
|
87
88
|
readonly server: DevServer;
|
|
88
|
-
/** Write files into the runtime (nested or flat; see `FileTree`). Open previews reload. */
|
|
89
|
-
writeTree(files: FileTree): void;
|
|
89
|
+
/** Write files into the runtime (nested or flat; see `FileTree`), under `at` (default `/`). Open previews reload. */
|
|
90
|
+
writeTree(files: FileTree, at?: string): void;
|
|
91
|
+
/** The project as a zip — "Download project" with `exclude: ['node_modules']`, or a full snapshot to restore later. */
|
|
92
|
+
exportZip(options?: ZipExportOptions): Uint8Array;
|
|
93
|
+
/** Write a zip's contents (an `exportZip` snapshot, a downloaded template) into the runtime, under `at`. */
|
|
94
|
+
importZip(zip: Uint8Array, at?: string): void;
|
|
95
|
+
/** Reload every frame showing this preview now (live reload does this on edits by itself). */
|
|
96
|
+
reload(): void;
|
|
90
97
|
/** Whether this runtime can serve the mounted project, and why not. */
|
|
91
98
|
profile(): ProjectProfile;
|
|
92
99
|
/**
|
|
@@ -97,6 +104,10 @@ export interface PreviewRuntime {
|
|
|
97
104
|
build(options?: RuntimeBuildOptions): Promise<BuildResult>;
|
|
98
105
|
/** Uncaught errors and rejections raised inside the preview. */
|
|
99
106
|
onError(listener: (error: PreviewError) => void): () => void;
|
|
107
|
+
/** The preview's `console.log/info/warn/error/debug` output (first 1,000 messages per page load). */
|
|
108
|
+
onPreviewConsole(listener: (message: PreviewConsoleMessage) => void): () => void;
|
|
109
|
+
/** Replace the scripts run in preview pages; open previews reload to pick them up. */
|
|
110
|
+
setPreviewScripts(scripts: string[]): void;
|
|
100
111
|
/**
|
|
101
112
|
* Run a command — `node server.js`, `npm install`, `npm run dev`, `bfsh` for a
|
|
102
113
|
* terminal — in its own Web Worker. Pass `{ terminal: { cols, rows } }` when
|
|
@@ -108,6 +119,14 @@ export interface PreviewRuntime {
|
|
|
108
119
|
onServerListening(listener: (server: ServerAddress) => void): () => void;
|
|
109
120
|
/** A virtual server opened or closed its port. */
|
|
110
121
|
onPortChange(listener: (change: PortChange) => void): () => void;
|
|
122
|
+
/** Processes still running, with the ports their servers listen on. */
|
|
123
|
+
listProcesses(): ProcessInfo[];
|
|
124
|
+
/**
|
|
125
|
+
* Cookies virtual servers set, per port. A service worker cannot set cookies,
|
|
126
|
+
* so the runtime keeps them and sends them back on later requests (and
|
|
127
|
+
* WebSocket handshakes); inspect or `clear()` them here.
|
|
128
|
+
*/
|
|
129
|
+
readonly cookies: PortCookieJars;
|
|
111
130
|
dispose(): void;
|
|
112
131
|
}
|
|
113
132
|
|
|
@@ -142,10 +161,12 @@ export async function startPreviewRuntime(options: PreviewRuntimeOptions): Promi
|
|
|
142
161
|
const base = previewBase(transport.scope, id);
|
|
143
162
|
const fs = new VirtualFileSystem();
|
|
144
163
|
const components = options.components ?? createCdnComponentCompilers();
|
|
164
|
+
/** Run in every preview page: dev-server documents and pages virtual servers serve alike. */
|
|
165
|
+
let previewScripts: string[] = [...(options.previewScripts ?? [])];
|
|
145
166
|
let server: DevServer;
|
|
146
167
|
try {
|
|
147
168
|
const transformer = options.transformer ?? (await createEsbuildWasmTransformer());
|
|
148
|
-
server = new DevServer({ fs, transformer, base, cdn: options.cdn, components, attribution: options.attribution });
|
|
169
|
+
server = new DevServer({ fs, transformer, base, cdn: options.cdn, components, attribution: options.attribution, previewScripts });
|
|
149
170
|
} catch (error) {
|
|
150
171
|
transport.dispose();
|
|
151
172
|
throw error;
|
|
@@ -156,15 +177,22 @@ export async function startPreviewRuntime(options: PreviewRuntimeOptions): Promi
|
|
|
156
177
|
const node = { ...options.node, createWorker: options.node?.createWorker ?? transport.createWorker?.bind(transport) };
|
|
157
178
|
const processes = createProcessHost({ fs, base, origin: transport.origin, previewUrl, options: node });
|
|
158
179
|
|
|
159
|
-
const
|
|
160
|
-
|
|
180
|
+
const cookies = new PortCookieJars();
|
|
181
|
+
let responder: ReturnType<typeof createPortResponder> | undefined;
|
|
161
182
|
|
|
162
183
|
const attach = () => {
|
|
163
184
|
const channel = new MessageChannel();
|
|
185
|
+
const reply = (message: WireReply, transfer: Transferable[]) => channel.port1.postMessage(message, transfer);
|
|
186
|
+
// Virtual servers' responses stream (one responder per channel: request ids are per worker).
|
|
187
|
+
responder?.dispose();
|
|
188
|
+
const ports = (responder = createPortResponder(processes, { cookies, scripts: () => previewScripts }));
|
|
164
189
|
channel.port1.onmessage = async (event) => {
|
|
190
|
+
if (isWireCancel(event.data)) return ports.cancel(event.data.reqId);
|
|
165
191
|
if (!isWireRequest(event.data)) return;
|
|
166
|
-
const
|
|
167
|
-
|
|
192
|
+
const request = event.data;
|
|
193
|
+
if (isPortPath(request.path)) return ports.serve(request, reply);
|
|
194
|
+
const { message, transfer } = toWire(request.reqId, await server.handle(request.path, request.search));
|
|
195
|
+
reply(message, transfer);
|
|
168
196
|
};
|
|
169
197
|
transport.attach(id, channel.port2);
|
|
170
198
|
};
|
|
@@ -182,38 +210,41 @@ export async function startPreviewRuntime(options: PreviewRuntimeOptions): Promi
|
|
|
182
210
|
reloadTimer = setTimeout(() => transport.reload(base), RELOAD_DEBOUNCE_MS);
|
|
183
211
|
});
|
|
184
212
|
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
if (data?.type !== ERROR_MESSAGE_TYPE || typeof data.href !== 'string') return;
|
|
189
|
-
if (event.origin !== transport.origin || !new URL(data.href).pathname.startsWith(base)) return; // another runtime's preview
|
|
190
|
-
const error: PreviewError = { message: String(data.message ?? ''), stack: data.stack, href: data.href };
|
|
191
|
-
for (const listener of errorListeners) listener(error);
|
|
192
|
-
};
|
|
193
|
-
window.addEventListener('message', onWindowMessage);
|
|
213
|
+
const previewMessages = listenToPreview(window, transport.origin, base);
|
|
214
|
+
// WebSockets and document.cookie for virtual servers, which a service worker cannot carry.
|
|
215
|
+
const bridge = listenToPreviewBridge(window, transport.origin, base, { upgrade: (port, request) => processes.upgrade(port, request), cookies });
|
|
194
216
|
|
|
195
217
|
return {
|
|
196
218
|
id,
|
|
197
219
|
url: previewUrl,
|
|
198
220
|
fs,
|
|
199
221
|
server,
|
|
200
|
-
writeTree: (files) => fs.writeTree(files),
|
|
222
|
+
writeTree: (files, at) => fs.writeTree(files, at),
|
|
223
|
+
exportZip: (zipOptions) => exportZip(fs, zipOptions),
|
|
224
|
+
importZip: (zip, at) => importZip(fs, zip, at),
|
|
225
|
+
reload: () => transport.reload(base),
|
|
201
226
|
profile: () => server.profile(),
|
|
202
227
|
async build(buildOptions = {}) {
|
|
203
228
|
const bundler = options.bundler ?? (await createEsbuildWasmBundler());
|
|
204
229
|
return buildProject({ files: fs, bundler, cdn: options.cdn, components, base: buildOptions.base, minify: buildOptions.minify });
|
|
205
230
|
},
|
|
206
|
-
onError(listener)
|
|
207
|
-
|
|
208
|
-
|
|
231
|
+
onError: (listener) => previewMessages.onError(listener),
|
|
232
|
+
onPreviewConsole: (listener) => previewMessages.onConsole(listener),
|
|
233
|
+
setPreviewScripts(scripts) {
|
|
234
|
+
previewScripts = [...scripts];
|
|
235
|
+
server.setPreviewScripts(previewScripts);
|
|
236
|
+
transport.reload(base);
|
|
209
237
|
},
|
|
210
238
|
run: (command: string, args?: string[] | ProcessOptions, runOptions?: ProcessOptions) => processes.run(command, args as string[], runOptions),
|
|
211
239
|
onServerListening: (listener) => processes.servers.onListening(listener),
|
|
212
240
|
onPortChange: (listener) => processes.servers.onPortChange(listener),
|
|
241
|
+
listProcesses: () => processes.list(),
|
|
242
|
+
cookies,
|
|
213
243
|
dispose() {
|
|
244
|
+
bridge.dispose();
|
|
245
|
+
responder?.dispose();
|
|
214
246
|
processes.dispose();
|
|
215
|
-
|
|
216
|
-
errorListeners.clear();
|
|
247
|
+
previewMessages.dispose();
|
|
217
248
|
clearTimeout(reloadTimer);
|
|
218
249
|
stopWatching?.();
|
|
219
250
|
stopReattach();
|