@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.
Files changed (71) hide show
  1. package/dist/assets.js +3 -3
  2. package/dist/host.d.ts +7 -1
  3. package/dist/host.d.ts.map +1 -1
  4. package/dist/host.js +27 -8
  5. package/dist/host.js.map +1 -1
  6. package/dist/index.d.ts +6 -2
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +3 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/node/index.d.ts +4 -8
  11. package/dist/node/index.d.ts.map +1 -1
  12. package/dist/node/index.js +1 -25
  13. package/dist/node/index.js.map +1 -1
  14. package/dist/node/portResponder.d.ts +45 -0
  15. package/dist/node/portResponder.d.ts.map +1 -0
  16. package/dist/node/portResponder.js +136 -0
  17. package/dist/node/portResponder.js.map +1 -0
  18. package/dist/node/processHost.d.ts +13 -2
  19. package/dist/node/processHost.d.ts.map +1 -1
  20. package/dist/node/processHost.js +62 -16
  21. package/dist/node/processHost.js.map +1 -1
  22. package/dist/node/protocol.d.ts +42 -9
  23. package/dist/node/protocol.d.ts.map +1 -1
  24. package/dist/node/protocol.js.map +1 -1
  25. package/dist/node/responseWire.d.ts +27 -0
  26. package/dist/node/responseWire.d.ts.map +1 -0
  27. package/dist/node/responseWire.js +99 -0
  28. package/dist/node/responseWire.js.map +1 -0
  29. package/dist/node/worker.js +141 -125
  30. package/dist/node/workerProcess.d.ts +3 -1
  31. package/dist/node/workerProcess.d.ts.map +1 -1
  32. package/dist/node/workerProcess.js +49 -19
  33. package/dist/node/workerProcess.js.map +1 -1
  34. package/dist/previewBridge.d.ts +37 -0
  35. package/dist/previewBridge.d.ts.map +1 -0
  36. package/dist/previewBridge.js +116 -0
  37. package/dist/previewBridge.js.map +1 -0
  38. package/dist/previewMessages.d.ts +5 -0
  39. package/dist/previewMessages.d.ts.map +1 -1
  40. package/dist/previewMessages.js +18 -11
  41. package/dist/previewMessages.js.map +1 -1
  42. package/dist/process-worker.js +141 -125
  43. package/dist/protocol.d.ts +23 -1
  44. package/dist/protocol.d.ts.map +1 -1
  45. package/dist/protocol.js +8 -0
  46. package/dist/protocol.js.map +1 -1
  47. package/dist/relay.html +1 -1
  48. package/dist/snapshotStore.d.ts +30 -0
  49. package/dist/snapshotStore.d.ts.map +1 -0
  50. package/dist/snapshotStore.js +115 -0
  51. package/dist/snapshotStore.js.map +1 -0
  52. package/dist/sw.js +1 -1
  53. package/dist/swRouter.d.ts +8 -1
  54. package/dist/swRouter.d.ts.map +1 -1
  55. package/dist/swRouter.js +40 -5
  56. package/dist/swRouter.js.map +1 -1
  57. package/package.json +3 -2
  58. package/src/host.ts +31 -8
  59. package/src/index.ts +6 -2
  60. package/src/node/index.ts +4 -23
  61. package/src/node/portResponder.ts +175 -0
  62. package/src/node/processHost.ts +73 -15
  63. package/src/node/protocol.ts +25 -4
  64. package/src/node/responseWire.ts +97 -0
  65. package/src/node/workerProcess.ts +52 -21
  66. package/src/previewBridge.ts +148 -0
  67. package/src/previewMessages.ts +17 -9
  68. package/src/protocol.ts +35 -1
  69. package/src/snapshotStore.ts +135 -0
  70. package/src/sw.ts +2 -0
  71. package/src/swRouter.ts +35 -6
@@ -54,8 +54,28 @@ export interface WireResponse {
54
54
  reqId: number;
55
55
  status: number;
56
56
  headers: Record<string, string>;
57
- body: string | ArrayBuffer;
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
@@ -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;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,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
@@ -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;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"}
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",n="bfwc:relay-ready",p="bfwc:relay-attach",f="bfwc:relay-reattach",l="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 s=window.parent;async function x(){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"&&s.postMessage({type:f,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===l&&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"}))}),s.postMessage({type:n},"*")}x().catch(e=>{s.postMessage({type:n,error:e instanceof Error?e.message:String(e)},"*")});})();
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 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})})()))});})();
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})})()))});})();
@@ -19,7 +19,8 @@ export interface RequestDetails {
19
19
  export interface RoutedResponse {
20
20
  status: number;
21
21
  headers: Record<string, string>;
22
- body: string | ArrayBuffer;
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;
@@ -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;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"}
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, isWireResponse, previewPrefix } from './protocol.js';
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
- if (!isWireResponse(event.data))
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
- pending.get(event.data.reqId)?.(event.data);
26
- pending.delete(event.data.reqId);
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
- resolve({ status: response.status, headers: response.headers, body: response.body });
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
  });
@@ -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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seanhogg/builderforce-webcontainers",
3
- "version": "2026.10.7",
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.7"
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 WireRequest, type WireResponse } from './protocol.js';
21
- import { createProcessHost, servePortRequest, type NodeRuntimeOptions, type ProcessInfo } from './node/index.js';
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: options.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 answer = async (request: WireRequest) =>
171
- isPortPath(request.path) ? servePortRequest(processes, request) : toWire(request.reqId, await server.handle(request.path, request.search));
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 { message, transfer } = await answer(event.data);
178
- channel.port1.postMessage(message, transfer);
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
- server.setPreviewScripts(scripts);
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 { parsePortPath, type VirtualFileSystem } from '@seanhogg/builderforce-webcontainers-core';
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
- }