jam 0.7.0 → 0.7.2
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/README.md +35 -19
- package/lib/resolve-binary.js +13 -20
- package/lib/resolve-optional-package.js +43 -0
- package/package.json +11 -8
- package/install.js +0 -108
package/README.md
CHANGED
|
@@ -1,44 +1,60 @@
|
|
|
1
1
|
# jam
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Jam is an application server for isolated JavaScript. It runs JavaScript (and TypeScript) per request with isolated execution contexts, inspired by the PHP-FPM model.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Install
|
|
6
6
|
|
|
7
7
|
```sh
|
|
8
8
|
npm install -g jam
|
|
9
9
|
jam --help
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
Create a `scripts/app.ts` file:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
export default {
|
|
18
|
+
fetch(request: Request): Response {
|
|
19
|
+
return new Response("Hello from Jam");
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Start Jam, pointing it at the scripts folder:
|
|
13
25
|
|
|
14
26
|
```sh
|
|
15
|
-
|
|
27
|
+
jam ./scripts
|
|
16
28
|
```
|
|
17
29
|
|
|
18
|
-
|
|
30
|
+
Now make a request. Jam maps `/app` to `app.ts`.
|
|
19
31
|
|
|
20
|
-
|
|
32
|
+
```sh
|
|
33
|
+
curl http://localhost:3000/app
|
|
34
|
+
```
|
|
21
35
|
|
|
22
|
-
|
|
36
|
+
Edit the file and make the same request again. The next request runs the updated script immediately, with no rebuilds or restarts.
|
|
23
37
|
|
|
24
|
-
|
|
25
|
-
- `jam-darwin-x64`
|
|
26
|
-
- `jam-linux-x64`
|
|
27
|
-
- `jam-linux-arm64`
|
|
38
|
+
## Documentation
|
|
28
39
|
|
|
29
|
-
|
|
40
|
+
To learn more, see [the documentation](https://github.com/mjackson/jam/tree/main/docs).
|
|
41
|
+
|
|
42
|
+
## How platform binaries are downloaded
|
|
30
43
|
|
|
31
|
-
The
|
|
44
|
+
The `jam` package uses platform-specific optional dependencies. During install, npm picks the matching package for your OS/CPU target:
|
|
45
|
+
|
|
46
|
+
- [`jam-darwin-arm64`](https://www.npmjs.com/package/jam-darwin-arm64)
|
|
47
|
+
- [`jam-darwin-x64`](https://www.npmjs.com/package/jam-darwin-x64)
|
|
48
|
+
- [`jam-linux-x64`](https://www.npmjs.com/package/jam-linux-x64)
|
|
49
|
+
- [`jam-linux-arm64`](https://www.npmjs.com/package/jam-linux-arm64)
|
|
50
|
+
|
|
51
|
+
Current support is macOS (`darwin`) and Linux glibc builds. Linux musl and Windows targets are not published yet.
|
|
32
52
|
|
|
33
53
|
## Environment overrides
|
|
34
54
|
|
|
35
|
-
- `JAM_SKIP_DOWNLOAD=1`: skip binary download during install.
|
|
36
|
-
- `JAM_FORCE_DOWNLOAD=1`: force download even in the Jam source checkout.
|
|
37
|
-
- `JAM_BINARY_VERSION=<version>`: download a different Jam release version.
|
|
38
|
-
- `JAM_BINARY_BASE_URL=<url>`: override the release URL prefix.
|
|
39
55
|
- `JAM_LIBC=glibc|musl`: override Linux libc detection.
|
|
40
|
-
- `JAM_BINARY_PATH=<path>`: run a specific local binary instead of the
|
|
56
|
+
- `JAM_BINARY_PATH=<path>`: run a specific local binary instead of the optional dependency binary.
|
|
41
57
|
|
|
42
58
|
## Local development
|
|
43
59
|
|
|
44
|
-
|
|
60
|
+
Install dependencies with optional dependencies enabled so the matching platform package is available.
|
package/lib/resolve-binary.js
CHANGED
|
@@ -1,37 +1,30 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
let
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
let {
|
|
4
|
+
packageNameForTarget,
|
|
5
|
+
resolveOptionalPackageBinaryPath,
|
|
6
|
+
} = require("./resolve-optional-package");
|
|
6
7
|
let { getRuntimeDescriptor } = require("./platform");
|
|
7
8
|
|
|
8
|
-
function packageRoot() {
|
|
9
|
-
return path.join(__dirname, "..");
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function downloadedBinaryPath(runtimeDescriptor) {
|
|
13
|
-
let descriptor = runtimeDescriptor || getRuntimeDescriptor();
|
|
14
|
-
return path.join(packageRoot(), "downloaded", descriptor.binaryName);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
9
|
function resolveBinaryPath() {
|
|
18
10
|
let fromEnv = process.env.JAM_BINARY_PATH;
|
|
19
11
|
if (fromEnv) return fromEnv;
|
|
20
12
|
|
|
21
13
|
let descriptor = getRuntimeDescriptor();
|
|
22
|
-
let
|
|
23
|
-
if (
|
|
14
|
+
let optionalPath = resolveOptionalPackageBinaryPath(descriptor);
|
|
15
|
+
if (optionalPath) return optionalPath;
|
|
16
|
+
|
|
17
|
+
let packageName = packageNameForTarget(descriptor.target);
|
|
18
|
+
|
|
19
|
+
if (!packageName) {
|
|
20
|
+
throw new Error(`No optional dependency package is configured for ${descriptor.target}.`);
|
|
21
|
+
}
|
|
24
22
|
|
|
25
23
|
throw new Error(
|
|
26
|
-
|
|
27
|
-
`Jam binary not found for ${descriptor.target}.`,
|
|
28
|
-
`Expected: ${targetPath}`,
|
|
29
|
-
"Try reinstalling the package or run `npm rebuild jam`.",
|
|
30
|
-
].join("\n")
|
|
24
|
+
`Jam optional dependency package not installed for ${descriptor.target}: ${packageName}. Try reinstalling with optional dependencies enabled.`
|
|
31
25
|
);
|
|
32
26
|
}
|
|
33
27
|
|
|
34
28
|
module.exports = {
|
|
35
|
-
downloadedBinaryPath,
|
|
36
29
|
resolveBinaryPath,
|
|
37
30
|
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
let fs = require("node:fs");
|
|
4
|
+
let path = require("node:path");
|
|
5
|
+
|
|
6
|
+
let TARGET_TO_PACKAGE = {
|
|
7
|
+
"darwin-arm64": "jam-darwin-arm64",
|
|
8
|
+
"darwin-x64": "jam-darwin-x64",
|
|
9
|
+
"linux-x64": "jam-linux-x64",
|
|
10
|
+
"linux-arm64": "jam-linux-arm64",
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function packageNameForTarget(target) {
|
|
14
|
+
return TARGET_TO_PACKAGE[target] || null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function packageRootFromName(packageName) {
|
|
18
|
+
try {
|
|
19
|
+
let packageJsonPath = require.resolve(`${packageName}/package.json`, {
|
|
20
|
+
paths: [path.join(__dirname, "..")],
|
|
21
|
+
});
|
|
22
|
+
return path.dirname(packageJsonPath);
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function resolveOptionalPackageBinaryPath(runtimeDescriptor) {
|
|
29
|
+
let packageName = packageNameForTarget(runtimeDescriptor.target);
|
|
30
|
+
if (!packageName) return null;
|
|
31
|
+
|
|
32
|
+
let packageRoot = packageRootFromName(packageName);
|
|
33
|
+
if (!packageRoot) return null;
|
|
34
|
+
|
|
35
|
+
let binaryPath = path.join(packageRoot, "bin", runtimeDescriptor.binaryName);
|
|
36
|
+
if (!fs.existsSync(binaryPath)) return null;
|
|
37
|
+
return binaryPath;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = {
|
|
41
|
+
packageNameForTarget,
|
|
42
|
+
resolveOptionalPackageBinaryPath,
|
|
43
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jam",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "An application server for isolated JavaScript",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -16,18 +16,21 @@
|
|
|
16
16
|
"bin": {
|
|
17
17
|
"jam": "bin/jam.js"
|
|
18
18
|
},
|
|
19
|
+
"optionalDependencies": {
|
|
20
|
+
"jam-darwin-arm64": "0.7.2",
|
|
21
|
+
"jam-darwin-x64": "0.7.2",
|
|
22
|
+
"jam-linux-x64": "0.7.2",
|
|
23
|
+
"jam-linux-arm64": "0.7.2"
|
|
24
|
+
},
|
|
19
25
|
"files": [
|
|
20
26
|
"bin",
|
|
21
27
|
"lib",
|
|
22
|
-
"install.js",
|
|
23
28
|
"README.md"
|
|
24
29
|
],
|
|
25
|
-
"scripts": {
|
|
26
|
-
"postinstall": "node install.js",
|
|
27
|
-
"test": "node test/platform.test.js",
|
|
28
|
-
"prepublishOnly": "node test/platform.test.js"
|
|
29
|
-
},
|
|
30
30
|
"engines": {
|
|
31
31
|
"node": ">=18"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"test": "node test/platform.test.js && node test/resolve-optional-package.test.js"
|
|
32
35
|
}
|
|
33
|
-
}
|
|
36
|
+
}
|
package/install.js
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
let fs = require("node:fs");
|
|
5
|
-
let path = require("node:path");
|
|
6
|
-
let { Readable } = require("node:stream");
|
|
7
|
-
let { pipeline } = require("node:stream/promises");
|
|
8
|
-
|
|
9
|
-
let { getRuntimeDescriptor } = require("./lib/platform");
|
|
10
|
-
let { downloadedBinaryPath } = require("./lib/resolve-binary");
|
|
11
|
-
|
|
12
|
-
let MAX_REDIRECTS = 5;
|
|
13
|
-
let REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
|
|
14
|
-
|
|
15
|
-
function loadPackageVersion() {
|
|
16
|
-
let pkgPath = path.join(__dirname, "package.json");
|
|
17
|
-
let pkgJson = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
18
|
-
return pkgJson.version;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function shouldSkipInstall(version) {
|
|
22
|
-
if (process.env.JAM_SKIP_DOWNLOAD === "1") return true;
|
|
23
|
-
if (isJamSourceCheckout() && process.env.JAM_FORCE_DOWNLOAD !== "1") return true;
|
|
24
|
-
return version.startsWith("0.0.0-");
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function isJamSourceCheckout() {
|
|
28
|
-
let repoRoot = path.join(__dirname, "..", "..");
|
|
29
|
-
return (
|
|
30
|
-
fs.existsSync(path.join(repoRoot, "go.mod")) &&
|
|
31
|
-
fs.existsSync(path.join(repoRoot, "cmd", "jam", "main.go")) &&
|
|
32
|
-
fs.existsSync(path.join(repoRoot, "pnpm-workspace.yaml"))
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function buildDownloadUrl(target, binaryName, version) {
|
|
37
|
-
let releaseVersion = process.env.JAM_BINARY_VERSION || version;
|
|
38
|
-
let explicitBaseUrl = process.env.JAM_BINARY_BASE_URL;
|
|
39
|
-
let baseUrl =
|
|
40
|
-
explicitBaseUrl ||
|
|
41
|
-
`https://github.com/mjackson/jam/releases/download/v${releaseVersion}`;
|
|
42
|
-
return `${baseUrl.replace(/\/+$/, "")}/jam-${target}${binaryName.endsWith(".exe") ? ".exe" : ""}`;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async function fetchToFile(url, filePath, redirects) {
|
|
46
|
-
if (redirects > MAX_REDIRECTS) {
|
|
47
|
-
throw new Error(`Too many redirects while downloading ${url}`);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
let response = await fetch(url, {
|
|
51
|
-
headers: {
|
|
52
|
-
"user-agent": "jam-npm-installer",
|
|
53
|
-
},
|
|
54
|
-
redirect: "manual",
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
if (REDIRECT_STATUSES.has(response.status)) {
|
|
58
|
-
let location = response.headers.get("location");
|
|
59
|
-
if (!location) {
|
|
60
|
-
throw new Error(`Redirect missing location header: ${response.status} ${url}`);
|
|
61
|
-
}
|
|
62
|
-
let redirectUrl = new URL(location, url).toString();
|
|
63
|
-
await fetchToFile(redirectUrl, filePath, redirects + 1);
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (response.status !== 200) {
|
|
68
|
-
throw new Error(`Download failed: ${response.status} ${url}`);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (!response.body) {
|
|
72
|
-
throw new Error(`Download failed: empty response body for ${url}`);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
let out = fs.createWriteStream(filePath, { mode: 0o755 });
|
|
76
|
-
await pipeline(Readable.fromWeb(response.body), out);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
async function install() {
|
|
80
|
-
let version = loadPackageVersion();
|
|
81
|
-
if (shouldSkipInstall(version)) {
|
|
82
|
-
if (isJamSourceCheckout()) {
|
|
83
|
-
console.log("[jam] Skipping binary download in Jam source checkout.");
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
console.log("[jam] Skipping binary download.");
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
let descriptor = getRuntimeDescriptor();
|
|
91
|
-
let downloadUrl = buildDownloadUrl(descriptor.target, descriptor.binaryName, version);
|
|
92
|
-
let outputPath = downloadedBinaryPath(descriptor);
|
|
93
|
-
|
|
94
|
-
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
95
|
-
console.log(`[jam] Downloading ${descriptor.target} binary from ${downloadUrl}`);
|
|
96
|
-
await fetchToFile(downloadUrl, outputPath, 0);
|
|
97
|
-
|
|
98
|
-
if (descriptor.platform !== "win32") {
|
|
99
|
-
fs.chmodSync(outputPath, 0o755);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
console.log(`[jam] Installed binary to ${outputPath}`);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
install().catch((error) => {
|
|
106
|
-
console.error(`[jam] ${error.message}`);
|
|
107
|
-
process.exit(1);
|
|
108
|
-
});
|