@seanhogg/builderforce-webcontainers 2026.10.7 → 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/host.d.ts +7 -1
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +27 -8
- package/dist/host.js.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/node/index.d.ts +4 -8
- 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 +13 -2
- package/dist/node/processHost.d.ts.map +1 -1
- package/dist/node/processHost.js +62 -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 +141 -125
- 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 +5 -0
- package/dist/previewMessages.d.ts.map +1 -1
- package/dist/previewMessages.js +18 -11
- package/dist/previewMessages.js.map +1 -1
- package/dist/process-worker.js +141 -125
- 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 +31 -8
- package/src/index.ts +6 -2
- package/src/node/index.ts +4 -23
- package/src/node/portResponder.ts +175 -0
- package/src/node/processHost.ts +73 -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 +17 -9
- 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,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DevServer,
|
|
3
|
+
PortCookieJars,
|
|
3
4
|
VirtualFileSystem,
|
|
4
5
|
buildProject,
|
|
5
6
|
exportZip,
|
|
@@ -17,9 +18,10 @@ import {
|
|
|
17
18
|
type Transformer,
|
|
18
19
|
type ZipExportOptions,
|
|
19
20
|
} from '@seanhogg/builderforce-webcontainers-core';
|
|
20
|
-
import { PREVIEW_ID, isPortPath, isWireRequest, previewBase, type
|
|
21
|
-
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';
|
|
22
23
|
import { listenToPreview, type PreviewConsoleMessage, type PreviewError } from './previewMessages.js';
|
|
24
|
+
import { listenToPreviewBridge } from './previewBridge.js';
|
|
23
25
|
import { relayTransport, sameOriginTransport } from './transport.js';
|
|
24
26
|
import { createEsbuildWasmBundler, createEsbuildWasmTransformer } from './esbuildWasm.js';
|
|
25
27
|
import { createCdnComponentCompilers } from './componentCompilers.js';
|
|
@@ -119,6 +121,12 @@ export interface PreviewRuntime {
|
|
|
119
121
|
onPortChange(listener: (change: PortChange) => void): () => void;
|
|
120
122
|
/** Processes still running, with the ports their servers listen on. */
|
|
121
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;
|
|
122
130
|
dispose(): void;
|
|
123
131
|
}
|
|
124
132
|
|
|
@@ -153,10 +161,12 @@ export async function startPreviewRuntime(options: PreviewRuntimeOptions): Promi
|
|
|
153
161
|
const base = previewBase(transport.scope, id);
|
|
154
162
|
const fs = new VirtualFileSystem();
|
|
155
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 ?? [])];
|
|
156
166
|
let server: DevServer;
|
|
157
167
|
try {
|
|
158
168
|
const transformer = options.transformer ?? (await createEsbuildWasmTransformer());
|
|
159
|
-
server = new DevServer({ fs, transformer, base, cdn: options.cdn, components, attribution: options.attribution, previewScripts
|
|
169
|
+
server = new DevServer({ fs, transformer, base, cdn: options.cdn, components, attribution: options.attribution, previewScripts });
|
|
160
170
|
} catch (error) {
|
|
161
171
|
transport.dispose();
|
|
162
172
|
throw error;
|
|
@@ -167,15 +177,22 @@ export async function startPreviewRuntime(options: PreviewRuntimeOptions): Promi
|
|
|
167
177
|
const node = { ...options.node, createWorker: options.node?.createWorker ?? transport.createWorker?.bind(transport) };
|
|
168
178
|
const processes = createProcessHost({ fs, base, origin: transport.origin, previewUrl, options: node });
|
|
169
179
|
|
|
170
|
-
const
|
|
171
|
-
|
|
180
|
+
const cookies = new PortCookieJars();
|
|
181
|
+
let responder: ReturnType<typeof createPortResponder> | undefined;
|
|
172
182
|
|
|
173
183
|
const attach = () => {
|
|
174
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 }));
|
|
175
189
|
channel.port1.onmessage = async (event) => {
|
|
190
|
+
if (isWireCancel(event.data)) return ports.cancel(event.data.reqId);
|
|
176
191
|
if (!isWireRequest(event.data)) return;
|
|
177
|
-
const
|
|
178
|
-
|
|
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);
|
|
179
196
|
};
|
|
180
197
|
transport.attach(id, channel.port2);
|
|
181
198
|
};
|
|
@@ -194,6 +211,8 @@ export async function startPreviewRuntime(options: PreviewRuntimeOptions): Promi
|
|
|
194
211
|
});
|
|
195
212
|
|
|
196
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 });
|
|
197
216
|
|
|
198
217
|
return {
|
|
199
218
|
id,
|
|
@@ -212,14 +231,18 @@ export async function startPreviewRuntime(options: PreviewRuntimeOptions): Promi
|
|
|
212
231
|
onError: (listener) => previewMessages.onError(listener),
|
|
213
232
|
onPreviewConsole: (listener) => previewMessages.onConsole(listener),
|
|
214
233
|
setPreviewScripts(scripts) {
|
|
215
|
-
|
|
234
|
+
previewScripts = [...scripts];
|
|
235
|
+
server.setPreviewScripts(previewScripts);
|
|
216
236
|
transport.reload(base);
|
|
217
237
|
},
|
|
218
238
|
run: (command: string, args?: string[] | ProcessOptions, runOptions?: ProcessOptions) => processes.run(command, args as string[], runOptions),
|
|
219
239
|
onServerListening: (listener) => processes.servers.onListening(listener),
|
|
220
240
|
onPortChange: (listener) => processes.servers.onPortChange(listener),
|
|
221
241
|
listProcesses: () => processes.list(),
|
|
242
|
+
cookies,
|
|
222
243
|
dispose() {
|
|
244
|
+
bridge.dispose();
|
|
245
|
+
responder?.dispose();
|
|
223
246
|
processes.dispose();
|
|
224
247
|
previewMessages.dispose();
|
|
225
248
|
clearTimeout(reloadTimer);
|
package/src/index.ts
CHANGED
|
@@ -8,15 +8,19 @@ export type {
|
|
|
8
8
|
RuntimeBuildOptions,
|
|
9
9
|
} from './host.js';
|
|
10
10
|
export { listenToPreview } from './previewMessages.js';
|
|
11
|
+
export { createSnapshotStore } from './snapshotStore.js';
|
|
12
|
+
export type { SnapshotStore, SnapshotStoreOptions } from './snapshotStore.js';
|
|
11
13
|
export type { PreviewError, PreviewConsoleMessage, ConsoleLevel, PreviewMessages } from './previewMessages.js';
|
|
14
|
+
export { listenToPreviewBridge } from './previewBridge.js';
|
|
15
|
+
export type { PreviewBridge, PreviewBridgeOptions, BridgePort } from './previewBridge.js';
|
|
12
16
|
export type { PreviewTransport } from './transport.js';
|
|
13
17
|
export { createEsbuildWasmTransformer, createEsbuildWasmBundler, loadEsbuildWasm } from './esbuildWasm.js';
|
|
14
18
|
export type { EsbuildWasmOptions } from './esbuildWasm.js';
|
|
15
19
|
export { createCdnComponentCompilers, defaultVueCompilerUrl, defaultSvelteCompilerUrl } from './componentCompilers.js';
|
|
16
20
|
export type { CdnComponentCompilerOptions } from './componentCompilers.js';
|
|
17
21
|
export { createCacheStoragePackageCache } from './packageCache.js';
|
|
18
|
-
export { ProcessHost, createWorkerProcess } from './node/index.js';
|
|
19
|
-
export type { NodeRuntimeOptions, ProcessHostOptions, ProcessInfo, WorkerLike } from './node/index.js';
|
|
22
|
+
export { ProcessHost, createWorkerProcess, servePortRequest, createPortResponder, openPortResponse, injectBridge } from './node/index.js';
|
|
23
|
+
export type { NodeRuntimeOptions, ProcessHostOptions, ProcessInfo, WorkerLike, PortResponder, PortResponderOptions, OpenedPortResponse } from './node/index.js';
|
|
20
24
|
export { createSwRouter } from './swRouter.js';
|
|
21
25
|
export type { SwRouter, SwRouterOptions, PortLike, PreviewTarget, RoutedResponse, RequestDetails } from './swRouter.js';
|
|
22
26
|
export { previewBase, previewPrefix, PREVIEW_SEGMENT, PREVIEW_ID, PORT_SEGMENT, isPortPath } from './protocol.js';
|
package/src/node/index.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Glue between the preview runtime and the Node process host: options, the
|
|
3
3
|
* default Worker, and answering `/__port/<n>/…` preview requests from the
|
|
4
|
-
* virtual server listening on that port.
|
|
4
|
+
* virtual server listening on that port (portResponder.ts).
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
7
|
-
import type { WireRequest, WireResponse } from '../protocol.js';
|
|
6
|
+
import type { VirtualFileSystem } from '@seanhogg/builderforce-webcontainers-core';
|
|
8
7
|
import { ProcessHost, type WorkerLike } from './processHost.js';
|
|
9
8
|
|
|
10
9
|
export { ProcessHost } from './processHost.js';
|
|
11
10
|
export type { ProcessHostOptions, ProcessInfo, WorkerLike } from './processHost.js';
|
|
12
11
|
export { createWorkerProcess } from './workerProcess.js';
|
|
12
|
+
export { servePortRequest, createPortResponder, openPortResponse, injectBridge } from './portResponder.js';
|
|
13
|
+
export type { PortResponder, PortResponderOptions, OpenedPortResponse } from './portResponder.js';
|
|
13
14
|
|
|
14
15
|
export interface NodeRuntimeOptions {
|
|
15
16
|
/**
|
|
@@ -47,23 +48,3 @@ export function createProcessHost(args: { fs: VirtualFileSystem; base: string; o
|
|
|
47
48
|
killGraceMs: options.killGraceMs,
|
|
48
49
|
});
|
|
49
50
|
}
|
|
50
|
-
|
|
51
|
-
const TEXT = { 'content-type': 'text/plain; charset=utf-8', 'cache-control': 'no-store' };
|
|
52
|
-
|
|
53
|
-
/** Answer a preview request addressed to a virtual server. */
|
|
54
|
-
export async function servePortRequest(host: ProcessHost, request: WireRequest): Promise<{ message: WireResponse; transfer: Transferable[] }> {
|
|
55
|
-
const target = parsePortPath(request.path)!;
|
|
56
|
-
const response = await host.request(target.port, {
|
|
57
|
-
method: request.method ?? 'GET',
|
|
58
|
-
url: target.path + request.search,
|
|
59
|
-
headers: request.headers ?? {},
|
|
60
|
-
body: request.body ? new Uint8Array(request.body) : null,
|
|
61
|
-
});
|
|
62
|
-
if (!response) {
|
|
63
|
-
return { message: { type: 'response', reqId: request.reqId, status: 502, headers: TEXT, body: `Nothing is listening on port ${target.port}. Start a server (node server.js) first.` }, transfer: [] };
|
|
64
|
-
}
|
|
65
|
-
const headers: Record<string, string> = {};
|
|
66
|
-
for (const [key, value] of Object.entries(response.headers)) headers[key] = Array.isArray(value) ? value.join(', ') : String(value);
|
|
67
|
-
const body = response.body.slice().buffer as ArrayBuffer;
|
|
68
|
-
return { message: { type: 'response', reqId: request.reqId, status: response.status, headers, body }, transfer: [body] };
|
|
69
|
-
}
|