@agoric/xsnap 0.14.3-dev-dcc80f2.0.dcc80f2 → 0.14.3-dev-4b83f24.0.4b83f24

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 (131) hide show
  1. package/README.md +33 -0
  2. package/package.json +8 -18
  3. package/scripts/use-github-release-binary.sh +160 -0
  4. package/src/install-prebuilt.js +358 -0
  5. package/src/prebuilt-manifest-sha256.js +3 -0
  6. package/src/xsnap.js +41 -18
  7. package/build.env +0 -4
  8. package/moddable/modules/data/base64/base64.js +0 -28
  9. package/moddable/modules/data/base64/manifest.json +0 -11
  10. package/moddable/modules/data/base64/modBase64.c +0 -188
  11. package/moddable/modules/data/binaryMessage/BinaryMessage.js +0 -106
  12. package/moddable/modules/data/crc/crc.c +0 -205
  13. package/moddable/modules/data/crc/crc.js +0 -36
  14. package/moddable/modules/data/crc/manifest.json +0 -8
  15. package/moddable/modules/data/hex/hex.js +0 -28
  16. package/moddable/modules/data/hex/manifest.json +0 -11
  17. package/moddable/modules/data/hex/modHex.c +0 -139
  18. package/moddable/modules/data/logical/logical.js +0 -32
  19. package/moddable/modules/data/logical/modLogical.c +0 -98
  20. package/moddable/modules/data/qrcode/manifest.json +0 -9
  21. package/moddable/modules/data/qrcode/qrcode.c +0 -93
  22. package/moddable/modules/data/qrcode/qrcode.js +0 -23
  23. package/moddable/modules/data/qrcode/qrcodegen.c +0 -1025
  24. package/moddable/modules/data/qrcode/qrcodegen.h +0 -267
  25. package/moddable/modules/data/text/decoder/manifest.json +0 -8
  26. package/moddable/modules/data/text/decoder/textdecoder.c +0 -480
  27. package/moddable/modules/data/text/decoder/textdecoder.js +0 -27
  28. package/moddable/modules/data/text/encoder/manifest.json +0 -8
  29. package/moddable/modules/data/text/encoder/textencoder.c +0 -232
  30. package/moddable/modules/data/text/encoder/textencoder.js +0 -24
  31. package/moddable/modules/data/tinyint/tinyint.c +0 -150
  32. package/moddable/modules/data/tinyint/tinyint.js +0 -53
  33. package/moddable/modules/data/url/manifest.json +0 -17
  34. package/moddable/modules/data/url/url.c +0 -1959
  35. package/moddable/modules/data/url/url.js +0 -210
  36. package/moddable/modules/data/wavreader/manifest.json +0 -8
  37. package/moddable/modules/data/wavreader/wavreader.js +0 -128
  38. package/moddable/modules/data/zlib/deflate.c +0 -161
  39. package/moddable/modules/data/zlib/deflate.js +0 -63
  40. package/moddable/modules/data/zlib/inflate.c +0 -145
  41. package/moddable/modules/data/zlib/inflate.js +0 -66
  42. package/moddable/modules/data/zlib/manifest_deflate.json +0 -9
  43. package/moddable/modules/data/zlib/manifest_inflate.json +0 -9
  44. package/moddable/modules/data/zlib/miniz.c +0 -4924
  45. package/moddable/xs/includes/xs.d.ts +0 -73
  46. package/moddable/xs/includes/xs.h +0 -1533
  47. package/moddable/xs/includes/xsmc.h +0 -206
  48. package/moddable/xs/makefiles/lin/makefile +0 -33
  49. package/moddable/xs/makefiles/lin/xsc.mk +0 -118
  50. package/moddable/xs/makefiles/lin/xsid.mk +0 -90
  51. package/moddable/xs/makefiles/lin/xsl.mk +0 -168
  52. package/moddable/xs/makefiles/lin/xst.mk +0 -201
  53. package/moddable/xs/makefiles/mac/makefile +0 -33
  54. package/moddable/xs/makefiles/mac/xsc.mk +0 -130
  55. package/moddable/xs/makefiles/mac/xsid.mk +0 -102
  56. package/moddable/xs/makefiles/mac/xsl.mk +0 -177
  57. package/moddable/xs/makefiles/mac/xst.mk +0 -203
  58. package/moddable/xs/makefiles/mac/xst_no_asan.txt +0 -52
  59. package/moddable/xs/makefiles/win/build.bat +0 -26
  60. package/moddable/xs/makefiles/win/xsc.mak +0 -142
  61. package/moddable/xs/makefiles/win/xsid.mak +0 -113
  62. package/moddable/xs/makefiles/win/xsl.mak +0 -186
  63. package/moddable/xs/makefiles/win/xst.mak +0 -195
  64. package/moddable/xs/platforms/lin_xs.h +0 -99
  65. package/moddable/xs/platforms/mac_xs.h +0 -97
  66. package/moddable/xs/platforms/wasm_xs.h +0 -79
  67. package/moddable/xs/platforms/win_xs.h +0 -104
  68. package/moddable/xs/platforms/xsHost.h +0 -63
  69. package/moddable/xs/platforms/xsPlatform.h +0 -618
  70. package/moddable/xs/sources/xsAPI.c +0 -2555
  71. package/moddable/xs/sources/xsAll.c +0 -294
  72. package/moddable/xs/sources/xsAll.h +0 -2741
  73. package/moddable/xs/sources/xsArguments.c +0 -222
  74. package/moddable/xs/sources/xsArray.c +0 -2657
  75. package/moddable/xs/sources/xsAtomics.c +0 -844
  76. package/moddable/xs/sources/xsBigInt.c +0 -1859
  77. package/moddable/xs/sources/xsBoolean.c +0 -109
  78. package/moddable/xs/sources/xsCode.c +0 -4493
  79. package/moddable/xs/sources/xsCommon.c +0 -1710
  80. package/moddable/xs/sources/xsCommon.h +0 -1142
  81. package/moddable/xs/sources/xsDataView.c +0 -2890
  82. package/moddable/xs/sources/xsDate.c +0 -1541
  83. package/moddable/xs/sources/xsDebug.c +0 -2710
  84. package/moddable/xs/sources/xsDefaults.c +0 -134
  85. package/moddable/xs/sources/xsError.c +0 -353
  86. package/moddable/xs/sources/xsFunction.c +0 -776
  87. package/moddable/xs/sources/xsGenerator.c +0 -865
  88. package/moddable/xs/sources/xsGlobal.c +0 -839
  89. package/moddable/xs/sources/xsJSON.c +0 -1091
  90. package/moddable/xs/sources/xsLexical.c +0 -1969
  91. package/moddable/xs/sources/xsLockdown.c +0 -933
  92. package/moddable/xs/sources/xsMapSet.c +0 -1649
  93. package/moddable/xs/sources/xsMarshall.c +0 -1020
  94. package/moddable/xs/sources/xsMath.c +0 -624
  95. package/moddable/xs/sources/xsMemory.c +0 -1941
  96. package/moddable/xs/sources/xsModule.c +0 -3101
  97. package/moddable/xs/sources/xsNumber.c +0 -560
  98. package/moddable/xs/sources/xsObject.c +0 -1102
  99. package/moddable/xs/sources/xsPlatforms.c +0 -480
  100. package/moddable/xs/sources/xsProfile.c +0 -577
  101. package/moddable/xs/sources/xsPromise.c +0 -1199
  102. package/moddable/xs/sources/xsProperty.c +0 -636
  103. package/moddable/xs/sources/xsProxy.c +0 -1014
  104. package/moddable/xs/sources/xsRegExp.c +0 -1168
  105. package/moddable/xs/sources/xsRun.c +0 -4889
  106. package/moddable/xs/sources/xsScope.c +0 -1293
  107. package/moddable/xs/sources/xsScript.c +0 -288
  108. package/moddable/xs/sources/xsScript.h +0 -1186
  109. package/moddable/xs/sources/xsSnapshot.c +0 -2161
  110. package/moddable/xs/sources/xsSnapshot.h +0 -51
  111. package/moddable/xs/sources/xsSourceMap.c +0 -218
  112. package/moddable/xs/sources/xsString.c +0 -3332
  113. package/moddable/xs/sources/xsSymbol.c +0 -503
  114. package/moddable/xs/sources/xsSyntaxical.c +0 -4193
  115. package/moddable/xs/sources/xsTree.c +0 -1893
  116. package/moddable/xs/sources/xsType.c +0 -1488
  117. package/moddable/xs/sources/xsdtoa.c +0 -6672
  118. package/moddable/xs/sources/xsmc.c +0 -340
  119. package/moddable/xs/sources/xsre.c +0 -7578
  120. package/src/build.js +0 -481
  121. package/xsnap-native/xsnap/makefiles/lin/makefile +0 -10
  122. package/xsnap-native/xsnap/makefiles/lin/xsnap-worker.mk +0 -156
  123. package/xsnap-native/xsnap/makefiles/lin/xsnap.mk +0 -144
  124. package/xsnap-native/xsnap/makefiles/mac/makefile +0 -10
  125. package/xsnap-native/xsnap/makefiles/mac/xsnap-worker.mk +0 -165
  126. package/xsnap-native/xsnap/makefiles/mac/xsnap.mk +0 -153
  127. package/xsnap-native/xsnap/sources/xsnap-worker.c +0 -1024
  128. package/xsnap-native/xsnap/sources/xsnap.c +0 -717
  129. package/xsnap-native/xsnap/sources/xsnap.h +0 -142
  130. package/xsnap-native/xsnap/sources/xsnapPlatform.c +0 -1501
  131. package/xsnap-native/xsnap/sources/xsnapPlatform.h +0 -105
package/README.md CHANGED
@@ -18,6 +18,39 @@ await fs.writeFile('bootstrap.xss', worker.makeSnapshotStream());
18
18
  await worker.close();
19
19
  ```
20
20
 
21
+ ## Using CI-built binaries from `xsnap-worker-binaries`
22
+
23
+ Before npm platform packages are wired, CI can consume binaries from GitHub
24
+ releases in `Agoric/xsnap-worker-binaries`.
25
+
26
+ From `packages/xsnap/`:
27
+
28
+ ```bash
29
+ ./scripts/use-github-release-binary.sh 0.0.0-dev /tmp/xsnap-release-assets
30
+ source /tmp/xsnap-release-assets/xsnap-binary.env
31
+ ```
32
+
33
+ This sets:
34
+
35
+ - `XSNAP_WORKER` for release mode
36
+ - `XSNAP_WORKER_DEBUG` for debug mode
37
+
38
+ The script verifies SHA256 digests against the release manifest before writing
39
+ the env file.
40
+
41
+ ## Package install behavior
42
+
43
+ `@agoric/xsnap` postinstall now installs prebuilt binaries from GitHub releases
44
+ instead of compiling native sources locally.
45
+
46
+ Optional environment overrides:
47
+
48
+ - `XSNAP_BINARY_VERSION` (default: package version)
49
+ - `XSNAP_BINARY_REPO` (default: `Agoric/xsnap-worker-binaries`)
50
+ - `XSNAP_BINARY_BASE_URL` (advanced override)
51
+ - `XSNAP_BINARY_MANIFEST_SHA256` (required trust anchor for unpinned versions)
52
+ - `XSNAP_CACHE_DIR` (advanced override for cached downloads)
53
+
21
54
  Some time later, possibly on a different computer…
22
55
 
23
56
  ```js
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agoric/xsnap",
3
- "version": "0.14.3-dev-dcc80f2.0.dcc80f2",
3
+ "version": "0.14.3-dev-4b83f24.0.4b83f24",
4
4
  "description": "Snapshotting VM worker based on Moddable's XS Javascript engine",
5
5
  "author": "Agoric",
6
6
  "license": "Apache-2.0",
@@ -12,12 +12,10 @@
12
12
  },
13
13
  "scripts": {
14
14
  "repl": "node src/xsrepl.js",
15
- "build:bin": "/bin/sh -c 'if test -d ./test; then node src/build.js; else yarn build:from-env; fi'",
16
- "build:env": "/bin/sh -c 'node src/build.js --show-env > build.env.new && mv build.env.new build.env'",
17
- "build:from-env": "{ cat build.env; echo node src/build.js; } | xargs env",
18
- "build": "yarn build:bin && yarn build:env",
15
+ "install:prebuilt": "node src/install-prebuilt.js",
16
+ "build": "yarn install:prebuilt",
19
17
  "check-version": "/bin/sh -c 'if test \"${npm_package_version}\" != \"$(./scripts/get_xsnap_version.sh)\"; then echo \"xsnap version mismatch; expected '${npm_package_version}'\"; exit 1; fi'",
20
- "postinstall": "npm run build:from-env",
18
+ "postinstall": "npm run install:prebuilt",
21
19
  "clean": "rm -rf xsnap-native/xsnap/build",
22
20
  "lint": "yarn run -T run-s --continue-on-error 'lint:*'",
23
21
  "lint:eslint": "yarn run -T eslint 'src/**/*.js' 'test/**/*.js' api.js",
@@ -28,8 +26,8 @@
28
26
  "test:xs": "exit 0"
29
27
  },
30
28
  "dependencies": {
31
- "@agoric/internal": "0.3.3-dev-dcc80f2.0.dcc80f2",
32
- "@agoric/xsnap-lockdown": "0.14.1-dev-dcc80f2.0.dcc80f2",
29
+ "@agoric/internal": "0.3.3-dev-4b83f24.0.4b83f24",
30
+ "@agoric/xsnap-lockdown": "0.14.1-dev-4b83f24.0.4b83f24",
33
31
  "@endo/bundle-source": "^4.1.2",
34
32
  "@endo/errors": "^1.2.13",
35
33
  "@endo/eventual-send": "^1.3.4",
@@ -51,16 +49,8 @@
51
49
  "files": [
52
50
  "LICENSE*",
53
51
  "api.js",
54
- "build.env",
55
- "moddable/modules/data/**",
56
- "moddable/xs/includes/**",
57
- "moddable/xs/makefiles/**",
58
- "moddable/xs/sources/**",
59
- "moddable/xs/platforms/*.h",
60
52
  "scripts/**",
61
- "src/**",
62
- "xsnap-native/xsnap/makefiles/**",
63
- "xsnap-native/xsnap/sources/**"
53
+ "src/**"
64
54
  ],
65
55
  "publishConfig": {
66
56
  "access": "public"
@@ -80,5 +70,5 @@
80
70
  "engines": {
81
71
  "node": "^20.9 || ^22.11"
82
72
  },
83
- "gitHead": "dcc80f24250a2f7221c8ffbc913d6f289c366313"
73
+ "gitHead": "4b83f243d627e8214b1b33338566dff420259339"
84
74
  }
@@ -0,0 +1,160 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -euo pipefail
4
+
5
+ version="${1:-}"
6
+ out_dir="${2:-}"
7
+ repo="${XSNAP_BINARY_REPO:-Agoric/xsnap-worker-binaries}"
8
+
9
+ if [[ -z "$version" || -z "$out_dir" ]]; then
10
+ echo "Usage: $0 <version> <output-dir>" >&2
11
+ echo "Environment:" >&2
12
+ echo " XSNAP_BINARY_REPO (default: Agoric/xsnap-worker-binaries)" >&2
13
+ echo " XSNAP_ENV_FILE (default: <output-dir>/xsnap-binary.env)" >&2
14
+ exit 1
15
+ fi
16
+
17
+ mkdir -p "$out_dir"
18
+ tag="v$version"
19
+ tarball="xsnap-worker-binaries-$version.tar.gz"
20
+ manifest_name="xsnap-worker-manifest-$version.json"
21
+ env_file="${XSNAP_ENV_FILE:-$out_dir/xsnap-binary.env}"
22
+
23
+ case "$(uname -s)" in
24
+ Linux) os="linux" ;;
25
+ Darwin) os="darwin" ;;
26
+ *)
27
+ echo "Unsupported host OS: $(uname -s)" >&2
28
+ exit 1
29
+ ;;
30
+ esac
31
+
32
+ case "$(uname -m)" in
33
+ x86_64|amd64) arch="x64" ;;
34
+ arm64|aarch64) arch="arm64" ;;
35
+ *)
36
+ echo "Unsupported host architecture: $(uname -m)" >&2
37
+ exit 1
38
+ ;;
39
+ esac
40
+
41
+ target="$os-$arch"
42
+
43
+ gh release download "$tag" \
44
+ --repo "$repo" \
45
+ --pattern "$tarball" \
46
+ --pattern "$manifest_name" \
47
+ --dir "$out_dir"
48
+
49
+ release_entry="dist/$target/release/xsnap-worker"
50
+ debug_entry="dist/$target/debug/xsnap-worker"
51
+ found_release=false
52
+ found_debug=false
53
+
54
+ mapfile -t tar_entries < <(tar -tvzf "$out_dir/$tarball")
55
+ for line in "${tar_entries[@]}"; do
56
+ [[ -n "$line" ]] || continue
57
+ type="${line:0:1}"
58
+ entry="${line##* }"
59
+ entry="${entry%/}"
60
+ if [[ -z "$entry" || "$entry" = /* || "$entry" == *'\'* ]]; then
61
+ echo "Refusing to extract unsafe archive path: $entry" >&2
62
+ exit 1
63
+ fi
64
+ IFS='/' read -r -a parts <<<"$entry"
65
+ for part in "${parts[@]}"; do
66
+ if [[ "$part" == ".." ]]; then
67
+ echo "Refusing to extract archive path traversal: $entry" >&2
68
+ exit 1
69
+ fi
70
+ done
71
+ allowed=false
72
+ for expected in "${expected_entries[@]}"; do
73
+ if [[ "$entry" == "$expected" ]]; then
74
+ allowed=true
75
+ break
76
+ fi
77
+ done
78
+ if [[ "$allowed" != true ]]; then
79
+ echo "Refusing to extract unexpected archive path: $entry" >&2
80
+ exit 1
81
+ fi
82
+ if [[ "$type" != "-" && "$type" != "d" ]]; then
83
+ echo "Refusing to extract non-regular archive entry: $entry" >&2
84
+ exit 1
85
+ fi
86
+
87
+ if [[ "$entry" == "$release_entry" ]]; then
88
+ found_release=true
89
+ fi
90
+ if [[ "$entry" == "$debug_entry" ]]; then
91
+ found_debug=true
92
+ fi
93
+ done
94
+
95
+ if [[ "$found_release" != true || "$found_debug" != true ]]; then
96
+ echo "Missing binaries for target $target in release bundle" >&2
97
+ exit 1
98
+ fi
99
+
100
+ tar -xzf "$out_dir/$tarball" -C "$out_dir" "$release_entry" "$debug_entry"
101
+
102
+ release_bin="$out_dir/dist/$target/release/xsnap-worker"
103
+ debug_bin="$out_dir/dist/$target/debug/xsnap-worker"
104
+
105
+ if [[ ! -f "$release_bin" || ! -f "$debug_bin" ]]; then
106
+ echo "Missing binaries for target $target in release bundle" >&2
107
+ exit 1
108
+ fi
109
+
110
+ chmod +x "$release_bin" "$debug_bin"
111
+
112
+ manifest_path="$out_dir/$manifest_name"
113
+
114
+ release_expected="$(
115
+ node -e '
116
+ const fs = require("fs");
117
+ const manifest = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
118
+ const target = process.argv[2];
119
+ process.stdout.write(manifest.targets[target].release.sha256);
120
+ ' "$manifest_path" "$target"
121
+ )"
122
+
123
+ debug_expected="$(
124
+ node -e '
125
+ const fs = require("fs");
126
+ const manifest = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
127
+ const target = process.argv[2];
128
+ process.stdout.write(manifest.targets[target].debug.sha256);
129
+ ' "$manifest_path" "$target"
130
+ )"
131
+
132
+ sha256_file() {
133
+ local path="$1"
134
+ if command -v sha256sum >/dev/null 2>&1; then
135
+ sha256sum "$path" | awk '{print $1}'
136
+ else
137
+ shasum -a 256 "$path" | awk '{print $1}'
138
+ fi
139
+ }
140
+
141
+ release_actual="$(sha256_file "$release_bin")"
142
+ debug_actual="$(sha256_file "$debug_bin")"
143
+
144
+ if [[ "$release_expected" != "$release_actual" ]]; then
145
+ echo "Release binary hash mismatch for $target" >&2
146
+ exit 1
147
+ fi
148
+
149
+ if [[ "$debug_expected" != "$debug_actual" ]]; then
150
+ echo "Debug binary hash mismatch for $target" >&2
151
+ exit 1
152
+ fi
153
+
154
+ cat >"$env_file" <<EOF
155
+ XSNAP_WORKER=$release_bin
156
+ XSNAP_WORKER_DEBUG=$debug_bin
157
+ EOF
158
+
159
+ echo "Prepared xsnap binaries for $target"
160
+ echo "Env file: $env_file"
@@ -0,0 +1,358 @@
1
+ #!/usr/bin/env node
2
+ /* eslint-env node */
3
+
4
+ import { createHash } from 'crypto';
5
+ import fsTop from 'fs';
6
+ import osTop from 'os';
7
+ import pathTop from 'path';
8
+ import { fileURLToPath } from 'url';
9
+ import * as childProcess from 'child_process';
10
+ import { manifestHashes } from './prebuilt-manifest-sha256.js';
11
+
12
+ /**
13
+ * @import {promises as fsPromises} from 'fs';
14
+ * @import {spawn} from 'child_process';
15
+ * @import {ReadStream} from 'fs';
16
+ */
17
+
18
+ /** @param {string} path */
19
+ const asset = path => fileURLToPath(new URL(path, import.meta.url));
20
+
21
+ /**
22
+ * @param {string} osType
23
+ * @param {string} arch
24
+ */
25
+ export const mapOsArchToTarget = (osType, arch) => {
26
+ const osName = {
27
+ Linux: 'linux',
28
+ Darwin: 'darwin',
29
+ }[osType];
30
+ if (!osName) {
31
+ throw Error(`Unsupported OS for prebuilt xsnap: ${osType}`);
32
+ }
33
+
34
+ const archName = {
35
+ x64: 'x64',
36
+ arm64: 'arm64',
37
+ amd64: 'x64',
38
+ aarch64: 'arm64',
39
+ }[arch];
40
+ if (!archName) {
41
+ throw Error(`Unsupported architecture for prebuilt xsnap: ${arch}`);
42
+ }
43
+
44
+ return `${osName}-${archName}`;
45
+ };
46
+
47
+ /** @param {string} target */
48
+ export const targetToBuildPlatform = target => {
49
+ if (target.startsWith('linux-')) {
50
+ return 'lin';
51
+ }
52
+ if (target.startsWith('darwin-')) {
53
+ return 'mac';
54
+ }
55
+ throw Error(`Unsupported target for build platform mapping: ${target}`);
56
+ };
57
+
58
+ /**
59
+ * @param {string} command
60
+ * @param {string[]} args
61
+ * @param {{ cwd?: string }} [opts]
62
+ */
63
+ const run = (command, args, opts = {}) =>
64
+ new Promise((resolve, reject) => {
65
+ const child = childProcess.spawn(command, args, {
66
+ cwd: opts.cwd || '.',
67
+ stdio: ['ignore', 'inherit', 'inherit'],
68
+ });
69
+ child.once('error', reject);
70
+ child.once('exit', code => {
71
+ if (code === 0) {
72
+ resolve(undefined);
73
+ } else {
74
+ reject(Error(`${command} exited with code ${code}`));
75
+ }
76
+ });
77
+ });
78
+
79
+ /**
80
+ * @param {string} command
81
+ * @param {string[]} args
82
+ * @param {{ cwd?: string }} [opts]
83
+ * @returns {Promise<string>}
84
+ */
85
+ const capture = (command, args, opts = {}) =>
86
+ new Promise((resolve, reject) => {
87
+ const child = childProcess.spawn(command, args, {
88
+ cwd: opts.cwd || '.',
89
+ stdio: ['ignore', 'pipe', 'inherit'],
90
+ });
91
+ let stdout = '';
92
+ child.stdout.setEncoding('utf8');
93
+ child.stdout.on('data', chunk => {
94
+ stdout += chunk;
95
+ });
96
+ child.once('error', reject);
97
+ child.once('exit', code => {
98
+ if (code === 0) {
99
+ resolve(stdout);
100
+ } else {
101
+ reject(Error(`${command} exited with code ${code}`));
102
+ }
103
+ });
104
+ });
105
+
106
+ /**
107
+ * @param {string} url
108
+ * @param {string} destPath
109
+ */
110
+ const downloadFile = async (url, destPath) => {
111
+ const res = await fetch(url);
112
+ if (!res.ok) {
113
+ throw Error(`Download failed for ${url}: HTTP ${res.status}`);
114
+ }
115
+ if (!res.body) {
116
+ throw Error(`Download failed for ${url}: missing response body`);
117
+ }
118
+
119
+ await fsTop.promises.mkdir(pathTop.dirname(destPath), { recursive: true });
120
+ await fsTop.promises.writeFile(
121
+ destPath,
122
+ Buffer.from(await res.arrayBuffer()),
123
+ );
124
+ };
125
+
126
+ /**
127
+ * @param {string} path
128
+ * @returns {Promise<string>}
129
+ */
130
+ const sha256File = async path => {
131
+ const hash = createHash('sha256');
132
+ await new Promise((resolve, reject) => {
133
+ const stream = fsTop.createReadStream(path);
134
+ stream.once('error', reject);
135
+ stream.on('data', chunk => hash.update(chunk));
136
+ stream.once('end', resolve);
137
+ });
138
+ return hash.digest('hex');
139
+ };
140
+
141
+ /**
142
+ * @param {string} manifestPath
143
+ * @param {string} target
144
+ * @returns {Promise<{ release: string, debug: string }>}
145
+ */
146
+ const readExpectedHashes = async (manifestPath, target) => {
147
+ const text = await fsTop.promises.readFile(manifestPath, 'utf8');
148
+ const manifest = JSON.parse(text);
149
+ const entry = manifest?.targets?.[target];
150
+ if (!entry?.release?.sha256 || !entry?.debug?.sha256) {
151
+ throw Error(`Manifest missing hashes for target ${target}`);
152
+ }
153
+ return {
154
+ release: entry.release.sha256,
155
+ debug: entry.debug.sha256,
156
+ };
157
+ };
158
+
159
+ const getDefaultCacheRoot = () => {
160
+ if (process.platform === 'darwin') {
161
+ return pathTop.join(osTop.homedir(), 'Library', 'Caches', 'agoric-sdk');
162
+ }
163
+ return pathTop.join(
164
+ process.env.XDG_CACHE_HOME || pathTop.join(osTop.homedir(), '.cache'),
165
+ 'agoric-sdk',
166
+ );
167
+ };
168
+
169
+ /** @param {string} entry */
170
+ const isSafeTarEntry = entry =>
171
+ entry !== '' &&
172
+ !pathTop.isAbsolute(entry) &&
173
+ !entry.split('/').includes('..') &&
174
+ !entry.includes('\\');
175
+
176
+ /**
177
+ * @param {string} tarballPath
178
+ * @param {string[]} expectedFiles
179
+ */
180
+ const validateTarball = async (tarballPath, expectedFiles) => {
181
+ const remaining = new Set(expectedFiles);
182
+ const listing = await capture('tar', ['-tvzf', tarballPath]);
183
+ for (const line of listing.split('\n')) {
184
+ if (!line) {
185
+ continue;
186
+ }
187
+ const type = line[0];
188
+ const rawEntry = line.split(/\s+/).at(-1) || '';
189
+ const entry = rawEntry.replace(/\/$/, '');
190
+ if (!isSafeTarEntry(entry)) {
191
+ throw Error(`Refusing to extract unsafe archive path: ${rawEntry}`);
192
+ }
193
+ if (type !== 'd' && type !== '-') {
194
+ throw Error(`Refusing to extract non-regular archive entry: ${rawEntry}`);
195
+ }
196
+ remaining.delete(entry);
197
+ }
198
+ if (remaining.size > 0) {
199
+ throw Error(
200
+ `Archive missing expected xsnap binaries: ${Array.from(remaining).join(', ')}`,
201
+ );
202
+ }
203
+ };
204
+
205
+ /**
206
+ * @param {string} version
207
+ * @param {NodeJS.ProcessEnv} env
208
+ */
209
+ const getExpectedManifestHash = (version, env) => {
210
+ const expected = env.XSNAP_BINARY_MANIFEST_SHA256 || manifestHashes[version];
211
+ if (!expected) {
212
+ throw Error(
213
+ `Missing trusted manifest hash for xsnap binary version ${version}; set XSNAP_BINARY_MANIFEST_SHA256 for explicit overrides`,
214
+ );
215
+ }
216
+ return expected;
217
+ };
218
+
219
+ const main = async () => {
220
+ const env = process.env;
221
+ if (env.XSNAP_WORKER) {
222
+ console.log('XSNAP_WORKER is set; skipping prebuilt binary install');
223
+ return;
224
+ }
225
+
226
+ const version = env.XSNAP_BINARY_VERSION || env.npm_package_version;
227
+ if (!version) {
228
+ throw Error(
229
+ 'Missing XSNAP_BINARY_VERSION and npm_package_version; cannot resolve prebuilt release',
230
+ );
231
+ }
232
+
233
+ const repo = env.XSNAP_BINARY_REPO || 'Agoric/xsnap-worker-binaries';
234
+ const base =
235
+ env.XSNAP_BINARY_BASE_URL ||
236
+ `https://github.com/${repo}/releases/download/v${version}`;
237
+
238
+ const target = mapOsArchToTarget(osTop.type(), osTop.arch());
239
+ const platform = targetToBuildPlatform(target);
240
+
241
+ const tarballName = `xsnap-worker-binaries-${version}.tar.gz`;
242
+ const manifestName = `xsnap-worker-manifest-${version}.json`;
243
+
244
+ const cacheRoot = pathTop.join(
245
+ env.XSNAP_CACHE_DIR || getDefaultCacheRoot(),
246
+ 'xsnap',
247
+ 'prebuilt',
248
+ version,
249
+ );
250
+ const tarballPath = pathTop.join(cacheRoot, tarballName);
251
+ const manifestPath = pathTop.join(cacheRoot, manifestName);
252
+ const extractRoot = pathTop.join(cacheRoot, 'bundle');
253
+ const expectedManifestHash = getExpectedManifestHash(version, env);
254
+
255
+ const releaseSrc = pathTop.join(
256
+ extractRoot,
257
+ `dist/${target}/release/xsnap-worker`,
258
+ );
259
+ const debugSrc = pathTop.join(
260
+ extractRoot,
261
+ `dist/${target}/debug/xsnap-worker`,
262
+ );
263
+ const expectedArchiveFiles = [
264
+ `dist/${target}/release/xsnap-worker`,
265
+ `dist/${target}/debug/xsnap-worker`,
266
+ ];
267
+
268
+ await fsTop.promises.mkdir(cacheRoot, { recursive: true });
269
+
270
+ const releaseDest = asset(
271
+ `../xsnap-native/xsnap/build/bin/${platform}/release/xsnap-worker`,
272
+ );
273
+ const debugDest = asset(
274
+ `../xsnap-native/xsnap/build/bin/${platform}/debug/xsnap-worker`,
275
+ );
276
+
277
+ const [manifestAvailable, releaseAvailable, debugAvailable] =
278
+ await Promise.allSettled([
279
+ sha256File(manifestPath),
280
+ sha256File(releaseDest),
281
+ sha256File(debugDest),
282
+ ]);
283
+ if (
284
+ manifestAvailable.status === 'fulfilled' &&
285
+ manifestAvailable.value === expectedManifestHash &&
286
+ releaseAvailable.status === 'fulfilled' &&
287
+ debugAvailable.status === 'fulfilled'
288
+ ) {
289
+ const { release: releaseExpected, debug: debugExpected } =
290
+ await readExpectedHashes(manifestPath, target);
291
+ if (
292
+ releaseAvailable.value === releaseExpected &&
293
+ debugAvailable.value === debugExpected
294
+ ) {
295
+ console.log(
296
+ `Using cached prebuilt xsnap binaries for ${target} (${version})`,
297
+ );
298
+ return;
299
+ }
300
+ }
301
+
302
+ await downloadFile(`${base}/${manifestName}`, manifestPath);
303
+ const manifestHash = await sha256File(manifestPath);
304
+ if (manifestHash !== expectedManifestHash) {
305
+ throw Error(`Manifest hash mismatch for xsnap binary version ${version}`);
306
+ }
307
+
308
+ await downloadFile(`${base}/${tarballName}`, tarballPath);
309
+ await validateTarball(tarballPath, expectedArchiveFiles);
310
+
311
+ await fsTop.promises.rm(extractRoot, { recursive: true, force: true });
312
+ await fsTop.promises.mkdir(extractRoot, { recursive: true });
313
+ await run('tar', [
314
+ '-xzf',
315
+ tarballPath,
316
+ '-C',
317
+ extractRoot,
318
+ ...expectedArchiveFiles,
319
+ ]);
320
+
321
+ const [
322
+ { release: releaseExpected, debug: debugExpected },
323
+ releaseActual,
324
+ debugActual,
325
+ ] = await Promise.all([
326
+ readExpectedHashes(manifestPath, target),
327
+ sha256File(releaseSrc),
328
+ sha256File(debugSrc),
329
+ ]);
330
+
331
+ if (releaseExpected !== releaseActual) {
332
+ throw Error(`Release binary hash mismatch for ${target}`);
333
+ }
334
+ if (debugExpected !== debugActual) {
335
+ throw Error(`Debug binary hash mismatch for ${target}`);
336
+ }
337
+
338
+ await fsTop.promises.mkdir(pathTop.dirname(releaseDest), { recursive: true });
339
+ await fsTop.promises.mkdir(pathTop.dirname(debugDest), { recursive: true });
340
+ await fsTop.promises.copyFile(releaseSrc, releaseDest);
341
+ await fsTop.promises.copyFile(debugSrc, debugDest);
342
+ await fsTop.promises.chmod(releaseDest, 0o755);
343
+ await fsTop.promises.chmod(debugDest, 0o755);
344
+
345
+ console.log(`Installed prebuilt xsnap binaries for ${target} (${version})`);
346
+ };
347
+
348
+ const isMain =
349
+ process.argv[1] &&
350
+ pathTop.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
351
+
352
+ if (isMain) {
353
+ main().catch(err => {
354
+ console.error('Failed to install prebuilt xsnap binaries');
355
+ console.error(err);
356
+ process.exit(1);
357
+ });
358
+ }
@@ -0,0 +1,3 @@
1
+ export const manifestHashes = {
2
+ '0.14.2': '377030e37b10554e73d0aff655639051d33281c9e17e7da98275fb9efcaae6f2',
3
+ };
package/src/xsnap.js CHANGED
@@ -44,6 +44,46 @@ const { freeze } = Object;
44
44
 
45
45
  const noop = freeze(() => {});
46
46
 
47
+ /**
48
+ * Resolve the xsnap worker binary path.
49
+ *
50
+ * `XSNAP_WORKER` may override both modes.
51
+ * `XSNAP_WORKER_DEBUG` overrides debug mode only.
52
+ *
53
+ * @param {{
54
+ * os: string,
55
+ * debug: boolean,
56
+ * env: Record<string, string | undefined>,
57
+ * }} options
58
+ */
59
+ export function resolveXsnapWorkerPath({ os, debug, env }) {
60
+ const platform = {
61
+ Linux: 'lin',
62
+ Darwin: 'mac',
63
+ // Windows_NT: 'win', // One can dream.
64
+ }[os];
65
+
66
+ if (platform === undefined) {
67
+ throw Error(`xsnap does not support platform ${os}`);
68
+ }
69
+
70
+ if (debug && env.XSNAP_WORKER_DEBUG) {
71
+ return env.XSNAP_WORKER_DEBUG;
72
+ }
73
+ if (env.XSNAP_WORKER) {
74
+ return env.XSNAP_WORKER;
75
+ }
76
+
77
+ return fileURLToPath(
78
+ new URL(
79
+ `../xsnap-native/xsnap/build/bin/${platform}/${
80
+ debug ? 'debug' : 'release'
81
+ }/xsnap-worker`,
82
+ import.meta.url,
83
+ ),
84
+ );
85
+ }
86
+
47
87
  /**
48
88
  * @param {Uint8Array} arg
49
89
  * @returns {Uint8Array}
@@ -169,24 +209,7 @@ export async function xsnap(options) {
169
209
  env = process.env,
170
210
  } = options;
171
211
 
172
- const platform = {
173
- Linux: 'lin',
174
- Darwin: 'mac',
175
- // Windows_NT: 'win', // One can dream.
176
- }[os];
177
-
178
- if (platform === undefined) {
179
- throw Error(`xsnap does not support platform ${os}`);
180
- }
181
-
182
- let bin = fileURLToPath(
183
- new URL(
184
- `../xsnap-native/xsnap/build/bin/${platform}/${
185
- debug ? 'debug' : 'release'
186
- }/xsnap-worker`,
187
- import.meta.url,
188
- ),
189
- );
212
+ let bin = resolveXsnapWorkerPath({ os, debug, env });
190
213
 
191
214
  /** @type {PromiseKit<void>} */
192
215
  const vatExit = makePromiseKit();
package/build.env DELETED
@@ -1,4 +0,0 @@
1
- MODDABLE_URL=https://github.com/agoric-labs/moddable.git
2
- MODDABLE_COMMIT_HASH=f6c5951fc055e4ca592b9166b9ae3cbb9cca6bf0
3
- XSNAP_NATIVE_URL=https://github.com/agoric-labs/xsnap-pub
4
- XSNAP_NATIVE_COMMIT_HASH=105bc6862050695b1723fa76df91564fe8111a37