@seanhogg/builderforce-webcontainers-core 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/archive.d.ts +13 -0
- package/dist/archive.d.ts.map +1 -0
- package/dist/archive.js +53 -0
- package/dist/archive.js.map +1 -0
- package/dist/devServer.d.ts +5 -0
- package/dist/devServer.d.ts.map +1 -1
- package/dist/devServer.js +11 -2
- package/dist/devServer.js.map +1 -1
- package/dist/html.d.ts +12 -3
- package/dist/html.d.ts.map +1 -1
- package/dist/html.js +31 -5
- package/dist/html.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/installer/index.d.ts +2 -0
- package/dist/installer/index.d.ts.map +1 -1
- package/dist/installer/index.js +1 -0
- package/dist/installer/index.js.map +1 -1
- package/dist/installer/install.d.ts +3 -0
- package/dist/installer/install.d.ts.map +1 -1
- package/dist/installer/install.js +1 -1
- package/dist/installer/install.js.map +1 -1
- package/dist/installer/npmrc.d.ts +26 -0
- package/dist/installer/npmrc.d.ts.map +1 -0
- package/dist/installer/npmrc.js +55 -0
- package/dist/installer/npmrc.js.map +1 -0
- package/dist/installer/registry.d.ts +3 -0
- package/dist/installer/registry.d.ts.map +1 -1
- package/dist/installer/registry.js +10 -5
- package/dist/installer/registry.js.map +1 -1
- package/dist/node/http/client.d.ts +3 -1
- package/dist/node/http/client.d.ts.map +1 -1
- package/dist/node/http/client.js +74 -5
- package/dist/node/http/client.js.map +1 -1
- package/dist/node/http/index.d.ts.map +1 -1
- package/dist/node/http/index.js +2 -1
- package/dist/node/http/index.js.map +1 -1
- package/dist/node/http/messages.d.ts +87 -0
- package/dist/node/http/messages.d.ts.map +1 -0
- package/dist/node/http/messages.js +146 -0
- package/dist/node/http/messages.js.map +1 -0
- package/dist/node/http/server.d.ts +13 -98
- package/dist/node/http/server.d.ts.map +1 -1
- package/dist/node/http/server.js +34 -202
- package/dist/node/http/server.js.map +1 -1
- package/dist/node/http/serverResponse.d.ts +42 -0
- package/dist/node/http/serverResponse.d.ts.map +1 -0
- package/dist/node/http/serverResponse.js +89 -0
- package/dist/node/http/serverResponse.js.map +1 -0
- package/dist/node/http/upgradeSocket.d.ts +34 -0
- package/dist/node/http/upgradeSocket.d.ts.map +1 -0
- package/dist/node/http/upgradeSocket.js +73 -0
- package/dist/node/http/upgradeSocket.js.map +1 -0
- package/dist/node/runtime.js +2 -2
- package/dist/node/runtime.js.map +1 -1
- package/dist/previewBridge.d.ts +42 -0
- package/dist/previewBridge.d.ts.map +1 -0
- package/dist/previewBridge.js +192 -0
- package/dist/previewBridge.js.map +1 -0
- package/dist/shell/ast.d.ts +101 -0
- package/dist/shell/ast.d.ts.map +1 -0
- package/dist/shell/ast.js +7 -0
- package/dist/shell/ast.js.map +1 -0
- package/dist/shell/builtins.d.ts +14 -0
- package/dist/shell/builtins.d.ts.map +1 -1
- package/dist/shell/builtins.js +87 -7
- package/dist/shell/builtins.js.map +1 -1
- package/dist/shell/compound.d.ts +30 -0
- package/dist/shell/compound.d.ts.map +1 -0
- package/dist/shell/compound.js +73 -0
- package/dist/shell/compound.js.map +1 -0
- package/dist/shell/exec.d.ts +27 -6
- package/dist/shell/exec.d.ts.map +1 -1
- package/dist/shell/exec.js +127 -41
- package/dist/shell/exec.js.map +1 -1
- package/dist/shell/expand.d.ts +5 -1
- package/dist/shell/expand.d.ts.map +1 -1
- package/dist/shell/expand.js +7 -5
- package/dist/shell/expand.js.map +1 -1
- package/dist/shell/lineReader.d.ts +9 -0
- package/dist/shell/lineReader.d.ts.map +1 -0
- package/dist/shell/lineReader.js +42 -0
- package/dist/shell/lineReader.js.map +1 -0
- package/dist/shell/npm.d.ts.map +1 -1
- package/dist/shell/npm.js +5 -1
- package/dist/shell/npm.js.map +1 -1
- package/dist/shell/packageManagers.d.ts +20 -0
- package/dist/shell/packageManagers.d.ts.map +1 -0
- package/dist/shell/packageManagers.js +74 -0
- package/dist/shell/packageManagers.js.map +1 -0
- package/dist/shell/parse.d.ts +16 -66
- package/dist/shell/parse.d.ts.map +1 -1
- package/dist/shell/parse.js +244 -243
- package/dist/shell/parse.js.map +1 -1
- package/dist/shell/program.d.ts.map +1 -1
- package/dist/shell/program.js +2 -1
- package/dist/shell/program.js.map +1 -1
- package/dist/shell/testCommand.d.ts +9 -0
- package/dist/shell/testCommand.d.ts.map +1 -0
- package/dist/shell/testCommand.js +105 -0
- package/dist/shell/testCommand.js.map +1 -0
- package/dist/shell/tokenize.d.ts +25 -0
- package/dist/shell/tokenize.d.ts.map +1 -0
- package/dist/shell/tokenize.js +279 -0
- package/dist/shell/tokenize.js.map +1 -0
- package/dist/system/cookieJar.d.ts +59 -0
- package/dist/system/cookieJar.d.ts.map +1 -0
- package/dist/system/cookieJar.js +187 -0
- package/dist/system/cookieJar.js.map +1 -0
- package/dist/system/httpStream.d.ts +40 -0
- package/dist/system/httpStream.d.ts.map +1 -0
- package/dist/system/httpStream.js +156 -0
- package/dist/system/httpStream.js.map +1 -0
- package/dist/system/index.d.ts +9 -1
- package/dist/system/index.d.ts.map +1 -1
- package/dist/system/index.js +4 -0
- package/dist/system/index.js.map +1 -1
- package/dist/system/kernel.d.ts +10 -2
- package/dist/system/kernel.d.ts.map +1 -1
- package/dist/system/kernel.js +12 -3
- package/dist/system/kernel.js.map +1 -1
- package/dist/system/ports.d.ts +9 -4
- package/dist/system/ports.d.ts.map +1 -1
- package/dist/system/ports.js +21 -2
- package/dist/system/ports.js.map +1 -1
- package/dist/system/programs.d.ts +1 -1
- package/dist/system/programs.d.ts.map +1 -1
- package/dist/system/programs.js +2 -0
- package/dist/system/programs.js.map +1 -1
- package/dist/system/types.d.ts +36 -6
- package/dist/system/types.d.ts.map +1 -1
- package/dist/system/webSocketClient.d.ts +37 -0
- package/dist/system/webSocketClient.d.ts.map +1 -0
- package/dist/system/webSocketClient.js +152 -0
- package/dist/system/webSocketClient.js.map +1 -0
- package/dist/system/webSocketFrames.d.ts +39 -0
- package/dist/system/webSocketFrames.d.ts.map +1 -0
- package/dist/system/webSocketFrames.js +117 -0
- package/dist/system/webSocketFrames.js.map +1 -0
- package/dist/vfs.d.ts +16 -1
- package/dist/vfs.d.ts.map +1 -1
- package/dist/vfs.js +45 -3
- package/dist/vfs.js.map +1 -1
- package/package.json +1 -1
- package/src/archive.ts +63 -0
- package/src/devServer.ts +12 -2
- package/src/html.ts +34 -5
- package/src/index.ts +5 -1
- package/src/installer/index.ts +2 -0
- package/src/installer/install.ts +4 -1
- package/src/installer/npmrc.ts +69 -0
- package/src/installer/registry.ts +12 -5
- package/src/node/http/client.ts +69 -6
- package/src/node/http/index.ts +2 -1
- package/src/node/http/messages.ts +150 -0
- package/src/node/http/server.ts +37 -202
- package/src/node/http/serverResponse.ts +98 -0
- package/src/node/http/upgradeSocket.ts +79 -0
- package/src/node/runtime.ts +2 -2
- package/src/previewBridge.ts +194 -0
- package/src/shell/ast.ts +85 -0
- package/src/shell/builtins.ts +97 -7
- package/src/shell/compound.ts +89 -0
- package/src/shell/exec.ts +128 -40
- package/src/shell/expand.ts +8 -6
- package/src/shell/lineReader.ts +46 -0
- package/src/shell/npm.ts +5 -1
- package/src/shell/packageManagers.ts +75 -0
- package/src/shell/parse.ts +233 -236
- package/src/shell/program.ts +2 -1
- package/src/shell/testCommand.ts +105 -0
- package/src/shell/tokenize.ts +257 -0
- package/src/system/cookieJar.ts +187 -0
- package/src/system/httpStream.ts +168 -0
- package/src/system/index.ts +24 -1
- package/src/system/kernel.ts +16 -4
- package/src/system/ports.ts +23 -4
- package/src/system/programs.ts +3 -1
- package/src/system/types.ts +40 -6
- package/src/system/webSocketClient.ts +177 -0
- package/src/system/webSocketFrames.ts +118 -0
- package/src/vfs.ts +46 -3
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The socket an `http.Server` hands its `upgrade` listeners: a real Duplex
|
|
3
|
+
* over the upgraded connection's byte pipe, with the slice of net.Socket that
|
|
4
|
+
* WebSocket servers touch (`ws` writes its `101` reply and frames with
|
|
5
|
+
* `write(data, cb)`, corks, pauses, reads `_readableState`, and calls
|
|
6
|
+
* `setNoDelay`/`setTimeout`). Raw bytes only — framing is the package's job.
|
|
7
|
+
*/
|
|
8
|
+
import { Buffer } from '../buffer.js';
|
|
9
|
+
import { Duplex } from '../stream/index.js';
|
|
10
|
+
export class UpgradeSocket extends Duplex {
|
|
11
|
+
channel;
|
|
12
|
+
remoteAddress = '127.0.0.1';
|
|
13
|
+
remotePort = 50000 + Math.floor(Math.random() * 10000);
|
|
14
|
+
remoteFamily = 'IPv4';
|
|
15
|
+
localAddress = '127.0.0.1';
|
|
16
|
+
localPort;
|
|
17
|
+
encrypted = false;
|
|
18
|
+
connecting = false;
|
|
19
|
+
bytesRead = 0;
|
|
20
|
+
bytesWritten = 0;
|
|
21
|
+
release;
|
|
22
|
+
/** `hold` keeps the process alive while the connection is open, as a TCP socket would. */
|
|
23
|
+
constructor(channel, port, hold) {
|
|
24
|
+
super({ allowHalfOpen: false });
|
|
25
|
+
this.channel = channel;
|
|
26
|
+
this.localPort = port;
|
|
27
|
+
this.release = hold?.();
|
|
28
|
+
channel.onData((data) => {
|
|
29
|
+
this.bytesRead += data.byteLength;
|
|
30
|
+
this.push(Buffer.from(data.buffer, data.byteOffset, data.byteLength));
|
|
31
|
+
});
|
|
32
|
+
channel.onClose(() => {
|
|
33
|
+
this.push(null);
|
|
34
|
+
this.end();
|
|
35
|
+
});
|
|
36
|
+
this.once('close', () => {
|
|
37
|
+
this.release?.();
|
|
38
|
+
this.release = undefined;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
_read() {
|
|
42
|
+
// bytes are pushed as they arrive
|
|
43
|
+
}
|
|
44
|
+
_write(chunk, _encoding, callback) {
|
|
45
|
+
this.bytesWritten += chunk.byteLength;
|
|
46
|
+
this.channel.send(chunk);
|
|
47
|
+
callback();
|
|
48
|
+
}
|
|
49
|
+
_final(callback) {
|
|
50
|
+
this.channel.close();
|
|
51
|
+
this.push(null);
|
|
52
|
+
callback();
|
|
53
|
+
}
|
|
54
|
+
_destroy(error, callback) {
|
|
55
|
+
this.channel.close();
|
|
56
|
+
callback(error);
|
|
57
|
+
}
|
|
58
|
+
setTimeout(_ms, cb) {
|
|
59
|
+
if (cb)
|
|
60
|
+
this.once('timeout', cb);
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
setNoDelay() { return this; }
|
|
64
|
+
setKeepAlive() { return this; }
|
|
65
|
+
address() { return { address: this.localAddress, family: 'IPv4', port: this.localPort }; }
|
|
66
|
+
ref() { return this; }
|
|
67
|
+
unref() {
|
|
68
|
+
this.release?.();
|
|
69
|
+
this.release = undefined;
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=upgradeSocket.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgradeSocket.js","sourceRoot":"","sources":["../../../src/node/http/upgradeSocket.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAK5C,MAAM,OAAO,aAAc,SAAQ,MAAM;IAaV;IAZ7B,aAAa,GAAG,WAAW,CAAC;IAC5B,UAAU,GAAG,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC;IACvD,YAAY,GAAG,MAAM,CAAC;IACtB,YAAY,GAAG,WAAW,CAAC;IAC3B,SAAS,CAAS;IAClB,SAAS,GAAG,KAAK,CAAC;IAClB,UAAU,GAAG,KAAK,CAAC;IACnB,SAAS,GAAG,CAAC,CAAC;IACd,YAAY,GAAG,CAAC,CAAC;IACT,OAAO,CAAc;IAE7B,0FAA0F;IAC1F,YAA6B,OAAoB,EAAE,IAAY,EAAE,IAAuB;QACtF,KAAK,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,CAAC;QADL,YAAO,GAAP,OAAO,CAAa;QAE/C,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,EAAE,CAAC;QACxB,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;YACtB,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,UAAU,CAAC;YAClC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAqB,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QACvF,CAAC,CAAC,CAAC;QACH,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE;YACnB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAChB,IAAI,CAAC,GAAG,EAAE,CAAC;QACb,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;YACtB,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;YACjB,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QAC3B,CAAC,CAAC,CAAC;IACL,CAAC;IAEQ,KAAK;QACZ,kCAAkC;IACpC,CAAC;IAED,MAAM,CAAC,KAAiB,EAAE,SAAiB,EAAE,QAAkB;QAC7D,IAAI,CAAC,YAAY,IAAI,KAAK,CAAC,UAAU,CAAC;QACtC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzB,QAAQ,EAAE,CAAC;IACb,CAAC;IAED,MAAM,CAAC,QAAkB;QACvB,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChB,QAAQ,EAAE,CAAC;IACb,CAAC;IAEQ,QAAQ,CAAC,KAAmB,EAAE,QAAkB;QACvD,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClB,CAAC;IAED,UAAU,CAAC,GAAW,EAAE,EAAe;QACrC,IAAI,EAAE;YAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,UAAU,KAAW,OAAO,IAAI,CAAC,CAAC,CAAC;IACnC,YAAY,KAAW,OAAO,IAAI,CAAC,CAAC,CAAC;IACrC,OAAO,KAAK,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IAC1F,GAAG,KAAW,OAAO,IAAI,CAAC,CAAC,CAAC;IAC5B,KAAK;QACH,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
|
package/dist/node/runtime.js
CHANGED
|
@@ -16,8 +16,8 @@ export async function startNode(options) {
|
|
|
16
16
|
// Servers die with the process: wrap listen so exit can close what is left open.
|
|
17
17
|
const processSystem = {
|
|
18
18
|
...system,
|
|
19
|
-
listen(port, handler) {
|
|
20
|
-
const registration = system.listen(port, handler);
|
|
19
|
+
listen(port, handler, upgrade) {
|
|
20
|
+
const registration = system.listen(port, handler, upgrade);
|
|
21
21
|
const tracked = {
|
|
22
22
|
port: registration.port,
|
|
23
23
|
close() {
|
package/dist/node/runtime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/node/runtime.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAqB,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAoB,MAAM,cAAc,CAAC;AAC3F,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAiCvC,MAAM,YAAY,GAA2B,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AAEtG,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAoB;IAClD,MAAM,QAAQ,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC;IAC/B,IAAI,WAAoC,CAAC;IACzC,MAAM,IAAI,GAAG,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC;IACvE,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,MAAM,OAAO,GAAG,IAAI,GAAG,EAAqB,CAAC;IAE7C,iFAAiF;IACjF,MAAM,aAAa,GAAW;QAC5B,GAAG,MAAM;QACT,MAAM,CAAC,IAAI,EAAE,OAAO;
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/node/runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scripts injected into preview pages that let them reach virtual servers the
|
|
3
|
+
* way a page reaches a real one, where a service worker cannot help:
|
|
4
|
+
*
|
|
5
|
+
* • WebSockets. A service worker never sees a WebSocket, so the page's
|
|
6
|
+
* `WebSocket` is replaced by one that, for URLs naming a virtual server
|
|
7
|
+
* (`ws://localhost:3000/…`, `/__bfwc/<id>/__port/3000/…`, or a path on the
|
|
8
|
+
* page's own host from a page that server served), opens a MessageChannel
|
|
9
|
+
* to the runtime's host page (`parent`, else `opener`). The host connects
|
|
10
|
+
* to the server and relays frames both ways. Every other URL gets the real
|
|
11
|
+
* `WebSocket`, untouched.
|
|
12
|
+
* • `document.cookie` on pages a virtual server served. The host keeps those
|
|
13
|
+
* cookies (a service worker cannot set `Set-Cookie`); this mirrors the
|
|
14
|
+
* non-HttpOnly ones into `document.cookie` and sends assignments back.
|
|
15
|
+
*
|
|
16
|
+
* Messages on a socket's channel — page → host: `{ kind: 'send', data }`,
|
|
17
|
+
* `{ kind: 'close', code, reason }`; host → page: `{ kind: 'open', protocol }`,
|
|
18
|
+
* `{ kind: 'message', data }` (string or ArrayBuffer), `{ kind: 'close', code, reason, wasClean }`.
|
|
19
|
+
* On the cookie channel — page → host `{ kind: 'set-cookie', value }`, host → page `{ kind: 'cookies', value }`.
|
|
20
|
+
*/
|
|
21
|
+
/** page → host, with a MessagePort: "connect a WebSocket to `port` at `path`". */
|
|
22
|
+
export declare const SOCKET_MESSAGE_TYPE = "bfwc:socket";
|
|
23
|
+
/** page → host, with a MessagePort: "keep my `document.cookie` in sync" (pages served by a virtual server). */
|
|
24
|
+
export declare const COOKIE_MESSAGE_TYPE = "bfwc:cookies";
|
|
25
|
+
/** How long a page waits for the host to answer a socket before failing it (code 1006). */
|
|
26
|
+
export declare const SOCKET_CONNECT_TIMEOUT_MS = 15000;
|
|
27
|
+
/** The `WebSocket` replacement; runs in every preview page (dev-server documents and virtual-server HTML). */
|
|
28
|
+
export declare function previewSocketScript(): string;
|
|
29
|
+
/**
|
|
30
|
+
* `document.cookie` for a page a virtual server served: starts from the host's
|
|
31
|
+
* jar (`initial`: the non-HttpOnly cookies for this page's path), stays in sync
|
|
32
|
+
* over a channel, and sends assignments to the host's jar.
|
|
33
|
+
*/
|
|
34
|
+
export declare function previewCookieScript(initial: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Where to inject the bridge into a streamed document, from its first bytes:
|
|
37
|
+
* just after `<head …>`, else after `<html …>` or the doctype (never before the
|
|
38
|
+
* doctype, which would switch the page to quirks mode), else the start.
|
|
39
|
+
* Undefined while the prefix is too short to tell; pass `complete` at the end of the body.
|
|
40
|
+
*/
|
|
41
|
+
export declare function bridgeInsertionPoint(prefix: string, complete?: boolean): number | undefined;
|
|
42
|
+
//# sourceMappingURL=previewBridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"previewBridge.d.ts","sourceRoot":"","sources":["../src/previewBridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,kFAAkF;AAClF,eAAO,MAAM,mBAAmB,gBAAgB,CAAC;AACjD,+GAA+G;AAC/G,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAClD,2FAA2F;AAC3F,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAEhD,8GAA8G;AAC9G,wBAAgB,mBAAmB,IAAI,MAAM,CAkG5C;AAKD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAkC3D;AAQD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,UAAQ,GAAG,MAAM,GAAG,SAAS,CAQzF"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scripts injected into preview pages that let them reach virtual servers the
|
|
3
|
+
* way a page reaches a real one, where a service worker cannot help:
|
|
4
|
+
*
|
|
5
|
+
* • WebSockets. A service worker never sees a WebSocket, so the page's
|
|
6
|
+
* `WebSocket` is replaced by one that, for URLs naming a virtual server
|
|
7
|
+
* (`ws://localhost:3000/…`, `/__bfwc/<id>/__port/3000/…`, or a path on the
|
|
8
|
+
* page's own host from a page that server served), opens a MessageChannel
|
|
9
|
+
* to the runtime's host page (`parent`, else `opener`). The host connects
|
|
10
|
+
* to the server and relays frames both ways. Every other URL gets the real
|
|
11
|
+
* `WebSocket`, untouched.
|
|
12
|
+
* • `document.cookie` on pages a virtual server served. The host keeps those
|
|
13
|
+
* cookies (a service worker cannot set `Set-Cookie`); this mirrors the
|
|
14
|
+
* non-HttpOnly ones into `document.cookie` and sends assignments back.
|
|
15
|
+
*
|
|
16
|
+
* Messages on a socket's channel — page → host: `{ kind: 'send', data }`,
|
|
17
|
+
* `{ kind: 'close', code, reason }`; host → page: `{ kind: 'open', protocol }`,
|
|
18
|
+
* `{ kind: 'message', data }` (string or ArrayBuffer), `{ kind: 'close', code, reason, wasClean }`.
|
|
19
|
+
* On the cookie channel — page → host `{ kind: 'set-cookie', value }`, host → page `{ kind: 'cookies', value }`.
|
|
20
|
+
*/
|
|
21
|
+
/** page → host, with a MessagePort: "connect a WebSocket to `port` at `path`". */
|
|
22
|
+
export const SOCKET_MESSAGE_TYPE = 'bfwc:socket';
|
|
23
|
+
/** page → host, with a MessagePort: "keep my `document.cookie` in sync" (pages served by a virtual server). */
|
|
24
|
+
export const COOKIE_MESSAGE_TYPE = 'bfwc:cookies';
|
|
25
|
+
/** How long a page waits for the host to answer a socket before failing it (code 1006). */
|
|
26
|
+
export const SOCKET_CONNECT_TIMEOUT_MS = 15_000;
|
|
27
|
+
/** The `WebSocket` replacement; runs in every preview page (dev-server documents and virtual-server HTML). */
|
|
28
|
+
export function previewSocketScript() {
|
|
29
|
+
return `<script>(function(){
|
|
30
|
+
var Native = window.WebSocket;
|
|
31
|
+
if (!Native || Native.__bfwc) return;
|
|
32
|
+
var runtime = window.parent !== window ? window.parent : window.opener;
|
|
33
|
+
var m = /^(.*\\/__bfwc\\/[A-Za-z0-9_-]+\\/)(?:__port\\/(\\d{1,5})(?:\\/|$))?/.exec(location.pathname);
|
|
34
|
+
if (!runtime || !m || typeof MessageChannel === 'undefined') return;
|
|
35
|
+
var base = m[1], ownPort = m[2] ? Number(m[2]) : 0;
|
|
36
|
+
var LOCAL = /^(localhost|127\\.0\\.0\\.1|0\\.0\\.0\\.0|\\[::1\\])$/i;
|
|
37
|
+
function target(url) {
|
|
38
|
+
var u;
|
|
39
|
+
try { u = new URL(String(url), location.href); } catch (_) { return null; }
|
|
40
|
+
if (!/^(wss?|https?):$/.test(u.protocol)) return null;
|
|
41
|
+
if (u.host === location.host) {
|
|
42
|
+
if (u.pathname.indexOf(base) === 0) {
|
|
43
|
+
var p = /^__port\\/(\\d{1,5})(\\/.*)?$/.exec(u.pathname.slice(base.length));
|
|
44
|
+
return p ? { port: Number(p[1]), path: (p[2] || '/') + u.search } : null;
|
|
45
|
+
}
|
|
46
|
+
return ownPort ? { port: ownPort, path: u.pathname + u.search } : null;
|
|
47
|
+
}
|
|
48
|
+
if (LOCAL.test(u.hostname)) return { port: Number(u.port || (/^(wss|https):$/.test(u.protocol) ? 443 : 80)), path: u.pathname + u.search };
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
var CONNECTING = 0, OPEN = 1, CLOSING = 2, CLOSED = 3;
|
|
52
|
+
class VirtualWebSocket extends EventTarget {
|
|
53
|
+
constructor(url, protocols) {
|
|
54
|
+
var t = target(url);
|
|
55
|
+
if (!t) return protocols === undefined ? new Native(url) : new Native(url, protocols);
|
|
56
|
+
super();
|
|
57
|
+
var self = this, channel = new MessageChannel();
|
|
58
|
+
this._url = new URL(String(url), location.href).href.replace(/^http/, 'ws');
|
|
59
|
+
this._state = CONNECTING;
|
|
60
|
+
this._protocol = '';
|
|
61
|
+
this._binaryType = 'blob';
|
|
62
|
+
this._queue = Promise.resolve();
|
|
63
|
+
this._port = channel.port1;
|
|
64
|
+
this._port.onmessage = function (e) { self._receive(e.data); };
|
|
65
|
+
this._timer = setTimeout(function () { self._receive({ kind: 'close', code: 1006, reason: '', wasClean: false }); }, ${SOCKET_CONNECT_TIMEOUT_MS});
|
|
66
|
+
var list = protocols === undefined ? [] : typeof protocols === 'string' ? [protocols] : Array.from(protocols, String);
|
|
67
|
+
runtime.postMessage({ type: ${JSON.stringify(SOCKET_MESSAGE_TYPE)}, href: location.href, port: t.port, path: t.path, protocols: list }, '*', [channel.port2]);
|
|
68
|
+
}
|
|
69
|
+
get url() { return this._url; }
|
|
70
|
+
get readyState() { return this._state; }
|
|
71
|
+
get protocol() { return this._protocol; }
|
|
72
|
+
get extensions() { return ''; }
|
|
73
|
+
get bufferedAmount() { return 0; }
|
|
74
|
+
get binaryType() { return this._binaryType; }
|
|
75
|
+
set binaryType(v) { if (v === 'blob' || v === 'arraybuffer') this._binaryType = v; }
|
|
76
|
+
send(data) {
|
|
77
|
+
if (this._state === CONNECTING) throw new DOMException("Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.", 'InvalidStateError');
|
|
78
|
+
if (this._state !== OPEN) return;
|
|
79
|
+
var port = this._port;
|
|
80
|
+
var value = typeof data === 'string' ? data
|
|
81
|
+
: data instanceof Blob ? data.arrayBuffer()
|
|
82
|
+
: ArrayBuffer.isView(data) ? data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength)
|
|
83
|
+
: data instanceof ArrayBuffer ? data.slice(0) : String(data);
|
|
84
|
+
this._queue = this._queue.then(function () { return value; }).then(function (v) { port.postMessage({ kind: 'send', data: v }); });
|
|
85
|
+
}
|
|
86
|
+
close(code, reason) {
|
|
87
|
+
if (code !== undefined && code !== 1000 && (code < 3000 || code > 4999)) throw new DOMException("Failed to execute 'close' on 'WebSocket': The code must be either 1000, or between 3000 and 4999.", 'InvalidAccessError');
|
|
88
|
+
if (this._state === CLOSING || this._state === CLOSED) return;
|
|
89
|
+
this._state = CLOSING;
|
|
90
|
+
var port = this._port;
|
|
91
|
+
this._queue.then(function () { port.postMessage({ kind: 'close', code: code, reason: reason === undefined ? '' : String(reason) }); });
|
|
92
|
+
}
|
|
93
|
+
_receive(m) {
|
|
94
|
+
if (!m || this._state === CLOSED) return;
|
|
95
|
+
if (m.kind === 'open' && this._state === CONNECTING) {
|
|
96
|
+
clearTimeout(this._timer);
|
|
97
|
+
this._state = OPEN;
|
|
98
|
+
this._protocol = m.protocol || '';
|
|
99
|
+
this._emit(new Event('open'));
|
|
100
|
+
} else if (m.kind === 'message' && this._state !== CONNECTING) {
|
|
101
|
+
var d = m.data;
|
|
102
|
+
if (typeof d !== 'string' && this._binaryType === 'blob') d = new Blob([d]);
|
|
103
|
+
this._emit(new MessageEvent('message', { data: d, origin: new URL(this._url).origin }));
|
|
104
|
+
} else if (m.kind === 'close') {
|
|
105
|
+
clearTimeout(this._timer);
|
|
106
|
+
this._state = CLOSED;
|
|
107
|
+
this._port.close();
|
|
108
|
+
if (!m.wasClean) this._emit(new Event('error'));
|
|
109
|
+
this._emit(new CloseEvent('close', { code: m.code, reason: m.reason || '', wasClean: !!m.wasClean }));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
_emit(event) {
|
|
113
|
+
var handler = this['on' + event.type];
|
|
114
|
+
if (typeof handler === 'function') handler.call(this, event);
|
|
115
|
+
this.dispatchEvent(event);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
[['CONNECTING', CONNECTING], ['OPEN', OPEN], ['CLOSING', CLOSING], ['CLOSED', CLOSED]].forEach(function (c) {
|
|
119
|
+
Object.defineProperty(VirtualWebSocket, c[0], { value: c[1] });
|
|
120
|
+
Object.defineProperty(VirtualWebSocket.prototype, c[0], { value: c[1] });
|
|
121
|
+
});
|
|
122
|
+
Object.defineProperty(VirtualWebSocket, Symbol.hasInstance, { value: function (o) { return o instanceof Native || Function.prototype[Symbol.hasInstance].call(VirtualWebSocket, o); } });
|
|
123
|
+
VirtualWebSocket.__bfwc = true;
|
|
124
|
+
window.WebSocket = VirtualWebSocket;
|
|
125
|
+
})();</script>`;
|
|
126
|
+
}
|
|
127
|
+
/** `JSON.stringify`, safe inside an inline script. */
|
|
128
|
+
const scriptLiteral = (value) => JSON.stringify(value).replace(/</g, '\\u003c');
|
|
129
|
+
/**
|
|
130
|
+
* `document.cookie` for a page a virtual server served: starts from the host's
|
|
131
|
+
* jar (`initial`: the non-HttpOnly cookies for this page's path), stays in sync
|
|
132
|
+
* over a channel, and sends assignments to the host's jar.
|
|
133
|
+
*/
|
|
134
|
+
export function previewCookieScript(initial) {
|
|
135
|
+
return `<script>(function(){
|
|
136
|
+
var runtime = window.parent !== window ? window.parent : window.opener;
|
|
137
|
+
var cookies = ${scriptLiteral(initial)};
|
|
138
|
+
var port = null;
|
|
139
|
+
if (runtime && typeof MessageChannel !== 'undefined') {
|
|
140
|
+
var channel = new MessageChannel();
|
|
141
|
+
port = channel.port1;
|
|
142
|
+
port.onmessage = function (e) { if (e.data && e.data.kind === 'cookies') cookies = String(e.data.value); };
|
|
143
|
+
runtime.postMessage({ type: ${JSON.stringify(COOKIE_MESSAGE_TYPE)}, href: location.href }, '*', [channel.port2]);
|
|
144
|
+
}
|
|
145
|
+
function apply(value) {
|
|
146
|
+
var parts = value.split(';'), pair = parts[0], eq = pair.indexOf('=');
|
|
147
|
+
if (eq < 0) return;
|
|
148
|
+
var name = pair.slice(0, eq).trim(), remove = false;
|
|
149
|
+
if (!name) return;
|
|
150
|
+
for (var i = 1; i < parts.length; i++) {
|
|
151
|
+
var a = parts[i].trim(), k = a.split('=')[0].trim().toLowerCase(), v = a.slice(a.indexOf('=') + 1).trim();
|
|
152
|
+
if (k === 'httponly') return;
|
|
153
|
+
if (k === 'max-age' && /^-?\\d+$/.test(v) && Number(v) <= 0) remove = true;
|
|
154
|
+
if (k === 'expires' && Date.parse(v) <= Date.now()) remove = true;
|
|
155
|
+
}
|
|
156
|
+
var list = cookies ? cookies.split('; ').filter(function (c) { return c.slice(0, c.indexOf('=')) !== name; }) : [];
|
|
157
|
+
if (!remove) list.push(name + '=' + pair.slice(eq + 1).trim());
|
|
158
|
+
cookies = list.join('; ');
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
Object.defineProperty(document, 'cookie', {
|
|
162
|
+
configurable: true,
|
|
163
|
+
get: function () { return cookies; },
|
|
164
|
+
set: function (value) { value = String(value); apply(value); if (port) port.postMessage({ kind: 'set-cookie', value: value }); },
|
|
165
|
+
});
|
|
166
|
+
} catch (_) {}
|
|
167
|
+
})();</script>`;
|
|
168
|
+
}
|
|
169
|
+
const HEAD_OPEN = /<head(?:\s[^>]*)?>/i;
|
|
170
|
+
const HTML_OPEN = /<html(?:\s[^>]*)?>/i;
|
|
171
|
+
const DOCTYPE = /<!doctype[^>]*>/i;
|
|
172
|
+
/** Content past which `<head>` can no longer appear. */
|
|
173
|
+
const BODY_STARTED = /<(?:body|script|link|meta|style|title|div|p)[\s>/]/i;
|
|
174
|
+
/**
|
|
175
|
+
* Where to inject the bridge into a streamed document, from its first bytes:
|
|
176
|
+
* just after `<head …>`, else after `<html …>` or the doctype (never before the
|
|
177
|
+
* doctype, which would switch the page to quirks mode), else the start.
|
|
178
|
+
* Undefined while the prefix is too short to tell; pass `complete` at the end of the body.
|
|
179
|
+
*/
|
|
180
|
+
export function bridgeInsertionPoint(prefix, complete = false) {
|
|
181
|
+
const head = HEAD_OPEN.exec(prefix);
|
|
182
|
+
if (head)
|
|
183
|
+
return head.index + head[0].length;
|
|
184
|
+
if (!complete && !BODY_STARTED.test(prefix) && prefix.length < 4096)
|
|
185
|
+
return undefined;
|
|
186
|
+
const html = HTML_OPEN.exec(prefix);
|
|
187
|
+
if (html)
|
|
188
|
+
return html.index + html[0].length;
|
|
189
|
+
const doctype = DOCTYPE.exec(prefix);
|
|
190
|
+
return doctype ? doctype.index + doctype[0].length : 0;
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=previewBridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"previewBridge.js","sourceRoot":"","sources":["../src/previewBridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,kFAAkF;AAClF,MAAM,CAAC,MAAM,mBAAmB,GAAG,aAAa,CAAC;AACjD,+GAA+G;AAC/G,MAAM,CAAC,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAClD,2FAA2F;AAC3F,MAAM,CAAC,MAAM,yBAAyB,GAAG,MAAM,CAAC;AAEhD,8GAA8G;AAC9G,MAAM,UAAU,mBAAmB;IACjC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6HAoCoH,yBAAyB;;oCAElH,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eA0DxD,CAAC;AAChB,CAAC;AAED,sDAAsD;AACtD,MAAM,aAAa,GAAG,CAAC,KAAc,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;AAEzF;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAe;IACjD,OAAO;;kBAES,aAAa,CAAC,OAAO,CAAC;;;;;;kCAMN,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;eAwBtD,CAAC;AAChB,CAAC;AAED,MAAM,SAAS,GAAG,qBAAqB,CAAC;AACxC,MAAM,SAAS,GAAG,qBAAqB,CAAC;AACxC,MAAM,OAAO,GAAG,kBAAkB,CAAC;AACnC,wDAAwD;AACxD,MAAM,YAAY,GAAG,qDAAqD,CAAC;AAE3E;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAc,EAAE,QAAQ,GAAG,KAAK;IACnE,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACpC,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC7C,IAAI,CAAC,QAAQ,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,IAAI;QAAE,OAAO,SAAS,CAAC;IACtF,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACpC,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC7C,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACrC,OAAO,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shell's syntax tree. Words keep their quoting as segments, because
|
|
3
|
+
* expansion (variables, `$(…)`, `~`, globs, field splitting) happens at run
|
|
4
|
+
* time and depends on it.
|
|
5
|
+
*/
|
|
6
|
+
export type Segment = {
|
|
7
|
+
kind: 'text';
|
|
8
|
+
value: string;
|
|
9
|
+
quoted: boolean;
|
|
10
|
+
} | {
|
|
11
|
+
kind: 'var';
|
|
12
|
+
name: string;
|
|
13
|
+
quoted: boolean;
|
|
14
|
+
op?: ':-' | '-' | ':=' | ':+';
|
|
15
|
+
arg?: Word;
|
|
16
|
+
} | {
|
|
17
|
+
kind: 'cmd';
|
|
18
|
+
source: string;
|
|
19
|
+
quoted: boolean;
|
|
20
|
+
} | {
|
|
21
|
+
kind: 'tilde';
|
|
22
|
+
};
|
|
23
|
+
export type Word = Segment[];
|
|
24
|
+
export type RedirectOp = '>' | '>>' | '<' | '2>' | '2>>' | '&>' | '2>&1' | '>&2' | '<<' | '<<-';
|
|
25
|
+
export interface Redirect {
|
|
26
|
+
op: RedirectOp;
|
|
27
|
+
target?: Word;
|
|
28
|
+
/** A here-document's body; expanded like a double-quoted string unless the delimiter was quoted. */
|
|
29
|
+
heredoc?: {
|
|
30
|
+
body: Word;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export interface SimpleCommand {
|
|
34
|
+
type: 'simple';
|
|
35
|
+
assignments: Array<{
|
|
36
|
+
name: string;
|
|
37
|
+
value: Word;
|
|
38
|
+
}>;
|
|
39
|
+
words: Word[];
|
|
40
|
+
redirects: Redirect[];
|
|
41
|
+
}
|
|
42
|
+
export interface IfCommand {
|
|
43
|
+
type: 'if';
|
|
44
|
+
clauses: Array<{
|
|
45
|
+
condition: Script;
|
|
46
|
+
body: Script;
|
|
47
|
+
}>;
|
|
48
|
+
otherwise?: Script;
|
|
49
|
+
redirects: Redirect[];
|
|
50
|
+
}
|
|
51
|
+
export interface LoopCommand {
|
|
52
|
+
type: 'while' | 'until';
|
|
53
|
+
condition: Script;
|
|
54
|
+
body: Script;
|
|
55
|
+
redirects: Redirect[];
|
|
56
|
+
}
|
|
57
|
+
export interface ForCommand {
|
|
58
|
+
type: 'for';
|
|
59
|
+
name: string;
|
|
60
|
+
/** Absent: iterate the positional parameters (`for x; do …`). */
|
|
61
|
+
items?: Word[];
|
|
62
|
+
body: Script;
|
|
63
|
+
redirects: Redirect[];
|
|
64
|
+
}
|
|
65
|
+
export interface CaseCommand {
|
|
66
|
+
type: 'case';
|
|
67
|
+
subject: Word;
|
|
68
|
+
arms: Array<{
|
|
69
|
+
patterns: Word[];
|
|
70
|
+
body: Script;
|
|
71
|
+
}>;
|
|
72
|
+
redirects: Redirect[];
|
|
73
|
+
}
|
|
74
|
+
/** `{ list; }` runs in this shell; `( list )` in a copy of it. */
|
|
75
|
+
export interface GroupCommand {
|
|
76
|
+
type: 'group' | 'subshell';
|
|
77
|
+
body: Script;
|
|
78
|
+
redirects: Redirect[];
|
|
79
|
+
}
|
|
80
|
+
export interface FunctionDefinition {
|
|
81
|
+
type: 'function';
|
|
82
|
+
name: string;
|
|
83
|
+
body: CompoundCommand;
|
|
84
|
+
redirects: Redirect[];
|
|
85
|
+
}
|
|
86
|
+
export type CompoundCommand = IfCommand | LoopCommand | ForCommand | CaseCommand | GroupCommand;
|
|
87
|
+
export type Command = SimpleCommand | CompoundCommand | FunctionDefinition;
|
|
88
|
+
export interface Pipeline {
|
|
89
|
+
negate: boolean;
|
|
90
|
+
commands: Command[];
|
|
91
|
+
}
|
|
92
|
+
export interface AndOrList {
|
|
93
|
+
first: Pipeline;
|
|
94
|
+
rest: Array<{
|
|
95
|
+
op: '&&' | '||';
|
|
96
|
+
pipeline: Pipeline;
|
|
97
|
+
}>;
|
|
98
|
+
background: boolean;
|
|
99
|
+
}
|
|
100
|
+
export type Script = AndOrList[];
|
|
101
|
+
//# sourceMappingURL=ast.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../../src/shell/ast.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ast.js","sourceRoot":"","sources":["../../src/shell/ast.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
package/dist/shell/builtins.d.ts
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
* the command tables the executor dispatches on.
|
|
4
4
|
*/
|
|
5
5
|
import type { CommandContext, Command } from './context.js';
|
|
6
|
+
import type { Flow } from './compound.js';
|
|
7
|
+
/** A command started with `&`. */
|
|
8
|
+
export interface BackgroundJob {
|
|
9
|
+
id: number;
|
|
10
|
+
done: Promise<number>;
|
|
11
|
+
}
|
|
6
12
|
/** The slice of the shell a builtin may change. */
|
|
7
13
|
export interface ShellState {
|
|
8
14
|
cwd: string;
|
|
@@ -11,6 +17,14 @@ export interface ShellState {
|
|
|
11
17
|
lastStatus: number;
|
|
12
18
|
/** Set by `exit`: the shell stops after the current command. */
|
|
13
19
|
exitRequested?: number;
|
|
20
|
+
/** Set by `break`, `continue` and `return`; the loop or function it targets clears it. */
|
|
21
|
+
flow?: Flow;
|
|
22
|
+
/** `$1…`, `$@`; a function call swaps them for its arguments. */
|
|
23
|
+
positional: string[];
|
|
24
|
+
readonly functions: ReadonlyMap<string, unknown>;
|
|
25
|
+
readonly jobs: ReadonlyMap<number, BackgroundJob>;
|
|
26
|
+
/** One frame per running function (for `local`). */
|
|
27
|
+
readonly localFrames: Array<Map<string, string | undefined>>;
|
|
14
28
|
/** Run a script in this shell (for `source`). */
|
|
15
29
|
run(source: string): Promise<number>;
|
|
16
30
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../../src/shell/builtins.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../../src/shell/builtins.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAM5D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAE1C,kCAAkC;AAClC,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;CACvB;AAED,mDAAmD;AACnD,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0FAA0F;IAC1F,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,iEAAiE;IACjE,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjD,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAClD,oDAAoD;IACpD,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAC7D,iDAAiD;IACjD,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACtC;AAED,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAElF,wEAAwE;AACxE,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAK7C,CAAC;AAEF,oFAAoF;AACpF,eAAO,MAAM,eAAe,UAA+D,CAAC;AAe5F,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAuJ5C,CAAC"}
|
package/dist/shell/builtins.js
CHANGED
|
@@ -6,21 +6,27 @@ import { findExecutable } from '../system/commands.js';
|
|
|
6
6
|
import { resolvePath } from './context.js';
|
|
7
7
|
import { cat, cp, ls, mkdir, mv, pwd, rm, touch } from './fileCommands.js';
|
|
8
8
|
import { basename, clear, dirname, echo, env, falseCommand, grep, head, printf, sleep, tail, trueCommand, wc } from './textCommands.js';
|
|
9
|
+
import { bracket, test } from './testCommand.js';
|
|
10
|
+
import { readLine } from './lineReader.js';
|
|
9
11
|
/** Coreutils: plain commands, also spawnable as standalone programs. */
|
|
10
12
|
export const COREUTILS = {
|
|
11
|
-
ls, cat, mkdir, rm, cp, mv, touch, pwd, echo, printf, head, tail, grep, wc, env, clear, sleep, basename, dirname,
|
|
13
|
+
ls, cat, mkdir, rm, cp, mv, touch, pwd, echo, printf, head, tail, grep, wc, env, clear, sleep, basename, dirname, test,
|
|
14
|
+
'[': bracket,
|
|
12
15
|
true: trueCommand,
|
|
13
16
|
false: falseCommand,
|
|
14
17
|
};
|
|
15
18
|
/** Programs the runtime provides besides coreutils (reported by `which`/`type`). */
|
|
16
|
-
export const SYSTEM_PROGRAMS = ['node', 'npm', 'npx', 'bfsh', 'sh', 'bash'];
|
|
19
|
+
export const SYSTEM_PROGRAMS = ['node', 'npm', 'npx', 'pnpm', 'yarn', 'bfsh', 'sh', 'bash'];
|
|
17
20
|
const HELP = `bfsh — BuilderForce WebContainers shell
|
|
18
21
|
|
|
19
|
-
Builtins: cd, pwd, export, unset, exit, source, history, type, which, help
|
|
20
|
-
|
|
22
|
+
Builtins: cd, pwd, export, unset, exit, source, history, type, which, help,
|
|
23
|
+
read, local, shift, return, break, continue, jobs, wait
|
|
24
|
+
Files: ls [-la], cat, mkdir [-p], rm [-rf], cp [-r], mv, touch, test / [ … ]
|
|
21
25
|
Text: echo, printf, head, tail, grep, wc, env, clear, sleep, basename, dirname
|
|
22
|
-
Node: node <file>, npm install|ci|run|start|test, npx <bin
|
|
26
|
+
Node: node <file>, npm install|ci|run|start|test, npx <bin>, pnpm, yarn
|
|
23
27
|
Syntax: 'quotes' "\$VARS" a && b || c; a | b > out.txt >> log 2>&1 2> err; FOO=bar cmd; $(cmd); *.js
|
|
28
|
+
if …; then …; elif …; else …; fi while|until …; do …; done for x in …; do …; done
|
|
29
|
+
case $x in a|b) …;; *) …;; esac name() { …; } { …; } ( … ) cat <<EOF … EOF
|
|
24
30
|
|
|
25
31
|
`;
|
|
26
32
|
export const BUILTINS = {
|
|
@@ -103,10 +109,12 @@ export const BUILTINS = {
|
|
|
103
109
|
}
|
|
104
110
|
return status;
|
|
105
111
|
},
|
|
106
|
-
async type(ctx) {
|
|
112
|
+
async type(ctx, shell) {
|
|
107
113
|
let status = 0;
|
|
108
114
|
for (const name of ctx.args) {
|
|
109
|
-
if (name
|
|
115
|
+
if (shell.functions.has(name))
|
|
116
|
+
ctx.io.stdout(`${name} is a function\n`);
|
|
117
|
+
else if (name in BUILTINS)
|
|
110
118
|
ctx.io.stdout(`${name} is a shell builtin\n`);
|
|
111
119
|
else if (name in COREUTILS || SYSTEM_PROGRAMS.includes(name))
|
|
112
120
|
ctx.io.stdout(`${name} is /usr/local/bin/${name}\n`);
|
|
@@ -125,7 +133,79 @@ export const BUILTINS = {
|
|
|
125
133
|
async set() {
|
|
126
134
|
return 0; // options (-e, -x) are accepted and ignored
|
|
127
135
|
},
|
|
136
|
+
async break(ctx, shell) {
|
|
137
|
+
return loopControl(ctx, shell, 'break');
|
|
138
|
+
},
|
|
139
|
+
async continue(ctx, shell) {
|
|
140
|
+
return loopControl(ctx, shell, 'continue');
|
|
141
|
+
},
|
|
142
|
+
async return(ctx, shell) {
|
|
143
|
+
if (!shell.localFrames.length) {
|
|
144
|
+
ctx.io.stderr('return: can only `return\' from a function or sourced script\n');
|
|
145
|
+
return 1;
|
|
146
|
+
}
|
|
147
|
+
const status = ctx.args[0] === undefined ? shell.lastStatus : Number(ctx.args[0]) & 255;
|
|
148
|
+
shell.flow = { kind: 'return', status };
|
|
149
|
+
return status;
|
|
150
|
+
},
|
|
151
|
+
async local(ctx, shell) {
|
|
152
|
+
const frame = shell.localFrames[shell.localFrames.length - 1];
|
|
153
|
+
if (!frame) {
|
|
154
|
+
ctx.io.stderr('local: can only be used in a function\n');
|
|
155
|
+
return 1;
|
|
156
|
+
}
|
|
157
|
+
for (const arg of ctx.args) {
|
|
158
|
+
const eq = arg.indexOf('=');
|
|
159
|
+
const name = eq > 0 ? arg.slice(0, eq) : arg;
|
|
160
|
+
if (!frame.has(name))
|
|
161
|
+
frame.set(name, shell.env[name]);
|
|
162
|
+
shell.env[name] = eq > 0 ? arg.slice(eq + 1) : '';
|
|
163
|
+
}
|
|
164
|
+
return 0;
|
|
165
|
+
},
|
|
166
|
+
async shift(ctx, shell) {
|
|
167
|
+
const count = ctx.args[0] === undefined ? 1 : Number(ctx.args[0]);
|
|
168
|
+
if (!Number.isInteger(count) || count < 0 || count > shell.positional.length)
|
|
169
|
+
return 1;
|
|
170
|
+
shell.positional = shell.positional.slice(count);
|
|
171
|
+
return 0;
|
|
172
|
+
},
|
|
173
|
+
async read(ctx, shell) {
|
|
174
|
+
const raw = ctx.args.includes('-r');
|
|
175
|
+
const names = ctx.args.filter((a) => !a.startsWith('-'));
|
|
176
|
+
let line = await readLine(ctx.io.stdin, ctx.signal);
|
|
177
|
+
if (line === null)
|
|
178
|
+
return 1;
|
|
179
|
+
if (!raw)
|
|
180
|
+
line = line.replace(/\\(.)/g, '$1');
|
|
181
|
+
const targets = names.length ? names : ['REPLY'];
|
|
182
|
+
const fields = names.length ? line.trim().split(/\s+/) : [line];
|
|
183
|
+
targets.forEach((name, i) => {
|
|
184
|
+
shell.env[name] = i === targets.length - 1 ? fields.slice(i).join(' ') : fields[i] ?? '';
|
|
185
|
+
});
|
|
186
|
+
return 0;
|
|
187
|
+
},
|
|
188
|
+
async jobs(ctx, shell) {
|
|
189
|
+
for (const job of shell.jobs.values())
|
|
190
|
+
ctx.io.stdout(`[${job.id}] Running\n`);
|
|
191
|
+
return 0;
|
|
192
|
+
},
|
|
193
|
+
async wait(ctx, shell) {
|
|
194
|
+
const ids = ctx.args.map((a) => Number(a.replace(/^%/, '')));
|
|
195
|
+
const jobs = ids.length ? ids.map((id) => shell.jobs.get(id)).filter((j) => !!j) : [...shell.jobs.values()];
|
|
196
|
+
const statuses = await Promise.all(jobs.map((job) => job.done));
|
|
197
|
+
return statuses.length ? statuses[statuses.length - 1] : 0;
|
|
198
|
+
},
|
|
128
199
|
};
|
|
200
|
+
function loopControl(ctx, shell, kind) {
|
|
201
|
+
const levels = ctx.args[0] === undefined ? 1 : Number(ctx.args[0]);
|
|
202
|
+
if (!Number.isInteger(levels) || levels < 1) {
|
|
203
|
+
ctx.io.stderr(`${kind}: ${ctx.args[0]}: loop count out of range\n`);
|
|
204
|
+
return 1;
|
|
205
|
+
}
|
|
206
|
+
shell.flow = { kind, levels };
|
|
207
|
+
return 0;
|
|
208
|
+
}
|
|
129
209
|
BUILTINS['.'] = BUILTINS.source;
|
|
130
210
|
BUILTINS[':'] = async () => 0;
|
|
131
211
|
//# sourceMappingURL=builtins.js.map
|