@stigmer/cli 3.12.9 → 3.14.0-dev.20260910084630
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/auth/login.d.ts.map +1 -1
- package/auth/login.js +30 -5
- package/auth/login.js.map +1 -1
- package/auth/token.d.ts +10 -5
- package/auth/token.d.ts.map +1 -1
- package/auth/token.js +30 -21
- package/auth/token.js.map +1 -1
- package/commands/apikey/index.d.ts.map +1 -1
- package/commands/apikey/index.js +11 -5
- package/commands/apikey/index.js.map +1 -1
- package/commands/auth/index.d.ts.map +1 -1
- package/commands/auth/index.js +19 -8
- package/commands/auth/index.js.map +1 -1
- package/commands/config/backend.d.ts.map +1 -1
- package/commands/config/backend.js +134 -12
- package/commands/config/backend.js.map +1 -1
- package/commands/connect.d.ts.map +1 -1
- package/commands/connect.js +6 -3
- package/commands/connect.js.map +1 -1
- package/commands/share.d.ts.map +1 -1
- package/commands/share.js +8 -6
- package/commands/share.js.map +1 -1
- package/commands/up.d.ts.map +1 -1
- package/commands/up.js +22 -6
- package/commands/up.js.map +1 -1
- package/config/config.d.ts +56 -7
- package/config/config.d.ts.map +1 -1
- package/config/config.js +115 -16
- package/config/config.js.map +1 -1
- package/config/index.d.ts +1 -1
- package/config/index.d.ts.map +1 -1
- package/config/index.js +1 -1
- package/config/index.js.map +1 -1
- package/config/keys.d.ts +1 -1
- package/config/keys.d.ts.map +1 -1
- package/config/keys.js +29 -6
- package/config/keys.js.map +1 -1
- package/config/resolve.d.ts +32 -21
- package/config/resolve.d.ts.map +1 -1
- package/config/resolve.js +80 -39
- package/config/resolve.js.map +1 -1
- package/local/constants.d.ts +8 -3
- package/local/constants.d.ts.map +1 -1
- package/local/constants.js +8 -3
- package/local/constants.js.map +1 -1
- package/local/daemon/components.d.ts.map +1 -1
- package/local/daemon/components.js +4 -2
- package/local/daemon/components.js.map +1 -1
- package/local/daemon/env.d.ts +16 -3
- package/local/daemon/env.d.ts.map +1 -1
- package/local/daemon/env.js +16 -3
- package/local/daemon/env.js.map +1 -1
- package/local/daemon/launch.d.ts.map +1 -1
- package/local/daemon/launch.js +5 -4
- package/local/daemon/launch.js.map +1 -1
- package/local/daemon/process.d.ts.map +1 -1
- package/local/daemon/process.js +8 -5
- package/local/daemon/process.js.map +1 -1
- package/local/runtime/index.d.ts +2 -2
- package/local/runtime/index.d.ts.map +1 -1
- package/local/runtime/index.js +4 -2
- package/local/runtime/index.js.map +1 -1
- package/local/runtime/node.d.ts +8 -0
- package/local/runtime/node.d.ts.map +1 -1
- package/local/runtime/node.js +74 -23
- package/local/runtime/node.js.map +1 -1
- package/local/runtime/runner.d.ts +2 -1
- package/local/runtime/runner.d.ts.map +1 -1
- package/local/runtime/runner.js +4 -31
- package/local/runtime/runner.js.map +1 -1
- package/local/runtime/runtimes-install.d.ts +13 -0
- package/local/runtime/runtimes-install.d.ts.map +1 -0
- package/local/runtime/runtimes-install.js +60 -0
- package/local/runtime/runtimes-install.js.map +1 -0
- package/local/runtime/server.d.ts +26 -30
- package/local/runtime/server.d.ts.map +1 -1
- package/local/runtime/server.js +106 -108
- package/local/runtime/server.js.map +1 -1
- package/local/seedpack/content.d.ts +3 -3
- package/local/seedpack/content.d.ts.map +1 -1
- package/local/seedpack/content.js +19 -13
- package/local/seedpack/content.js.map +1 -1
- package/local/status.d.ts.map +1 -1
- package/local/status.js +4 -2
- package/local/status.js.map +1 -1
- package/local/webconsole/index.d.ts +2 -3
- package/local/webconsole/index.d.ts.map +1 -1
- package/local/webconsole/index.js +24 -10
- package/local/webconsole/index.js.map +1 -1
- package/package.json +5 -5
- package/resources/connect/connect.d.ts +4 -3
- package/resources/connect/connect.d.ts.map +1 -1
- package/resources/connect/connect.js +17 -5
- package/resources/connect/connect.js.map +1 -1
- package/resources/connect/oauth.d.ts +5 -2
- package/resources/connect/oauth.d.ts.map +1 -1
- package/resources/connect/oauth.js +5 -4
- package/resources/connect/oauth.js.map +1 -1
- package/src/auth/login.ts +54 -9
- package/src/auth/token.test.ts +69 -13
- package/src/auth/token.ts +46 -22
- package/src/client/client.test.ts +10 -2
- package/src/commands/apikey/index.ts +42 -11
- package/src/commands/auth/index.ts +52 -14
- package/src/commands/config/backend.test.ts +122 -0
- package/src/commands/config/backend.ts +195 -14
- package/src/commands/connect.test.ts +22 -4
- package/src/commands/connect.ts +37 -13
- package/src/commands/share.ts +36 -16
- package/src/commands/up.ts +24 -6
- package/src/config/config.test.ts +62 -6
- package/src/config/config.ts +165 -24
- package/src/config/index.ts +6 -0
- package/src/config/keys.test.ts +27 -4
- package/src/config/keys.ts +46 -9
- package/src/config/resolve.test.ts +135 -34
- package/src/config/resolve.ts +88 -38
- package/src/local/constants.ts +8 -4
- package/src/local/daemon/components.test.ts +9 -1
- package/src/local/daemon/components.ts +4 -2
- package/src/local/daemon/daemon.integration.test.ts +1 -1
- package/src/local/daemon/env.test.ts +15 -4
- package/src/local/daemon/env.ts +33 -5
- package/src/local/daemon/launch.ts +5 -4
- package/src/local/daemon/process.ts +7 -4
- package/src/local/runtime/index.ts +6 -5
- package/src/local/runtime/node.ts +99 -23
- package/src/local/runtime/runner.ts +5 -36
- package/src/local/runtime/runtime.test.ts +58 -14
- package/src/local/runtime/runtimes-install.test.ts +56 -0
- package/src/local/runtime/runtimes-install.ts +72 -0
- package/src/local/runtime/server.test.ts +178 -114
- package/src/local/runtime/server.ts +149 -129
- package/src/local/seedpack/content.ts +52 -21
- package/src/local/status.ts +4 -2
- package/src/local/webconsole/index.ts +27 -10
- package/src/resources/connect/connect.integration.test.ts +79 -26
- package/src/resources/connect/connect.ts +72 -19
- package/src/resources/connect/oauth.test.ts +38 -12
- package/src/resources/connect/oauth.ts +27 -11
- package/src/resources/share.test.ts +2 -2
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Shared npm-install mechanics for on-demand-acquired runtime packages
|
|
2
|
+
// (~/.stigmer/runtimes/<version>/). Extracted when the TS server became the
|
|
3
|
+
// second consumer (D4 #24) — the runner and the server acquire different
|
|
4
|
+
// packages into the SAME per-version install root, so the root bootstrap and
|
|
5
|
+
// the install invocation must not drift between them.
|
|
6
|
+
|
|
7
|
+
import { execFileSync } from "node:child_process";
|
|
8
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { CliExitError } from "../../errors/cli-exit-error.js";
|
|
11
|
+
import { ExitCode } from "../../errors/exit-codes.js";
|
|
12
|
+
|
|
13
|
+
/** The install implementation, injectable for tests. */
|
|
14
|
+
export type NpmInstall = (installDir: string, spec: string) => void;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Ensure the per-version install root exists with a stable package.json.
|
|
18
|
+
* A stable root makes installs deterministic and records the pinned
|
|
19
|
+
* dependencies rather than letting npm synthesize an ad-hoc root; both the
|
|
20
|
+
* runner and the server packages install into this one root, so the file is
|
|
21
|
+
* written once and shared.
|
|
22
|
+
*/
|
|
23
|
+
export function ensureRuntimesRoot(installDir: string): void {
|
|
24
|
+
mkdirSync(installDir, { recursive: true });
|
|
25
|
+
const rootManifest = join(installDir, "package.json");
|
|
26
|
+
if (!existsSync(rootManifest)) {
|
|
27
|
+
writeFileSync(
|
|
28
|
+
rootManifest,
|
|
29
|
+
`${JSON.stringify({ name: "stigmer-runtime", private: true, version: "0.0.0" }, null, 2)}\n`,
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Install a package (plus its platform-native optional dependency) into an
|
|
35
|
+
// isolated prefix. `--omit=dev` drops devDependencies while keeping the
|
|
36
|
+
// optional native package npm selects by os/cpu; output is inherited so the
|
|
37
|
+
// user sees the one-time download progress.
|
|
38
|
+
export function npmInstallIntoRuntimes(installDir: string, spec: string): void {
|
|
39
|
+
try {
|
|
40
|
+
execFileSync(
|
|
41
|
+
"npm",
|
|
42
|
+
[
|
|
43
|
+
"install",
|
|
44
|
+
spec,
|
|
45
|
+
"--prefix",
|
|
46
|
+
installDir,
|
|
47
|
+
"--omit=dev",
|
|
48
|
+
"--no-audit",
|
|
49
|
+
"--no-fund",
|
|
50
|
+
],
|
|
51
|
+
{
|
|
52
|
+
stdio: "inherit",
|
|
53
|
+
},
|
|
54
|
+
);
|
|
55
|
+
} catch (err) {
|
|
56
|
+
throw new CliExitError(`failed to install ${spec}`, ExitCode.General, [
|
|
57
|
+
`Command: npm install ${spec} --prefix ${installDir}`,
|
|
58
|
+
"Ensure npm is on PATH and the network is reachable.",
|
|
59
|
+
"If npm reported EBADENGINE, this Node line is outside the package's",
|
|
60
|
+
"supported range. If a previous install was interrupted, remove",
|
|
61
|
+
`${installDir} and retry.`,
|
|
62
|
+
String(err),
|
|
63
|
+
]);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// A source build reports "0.0.0-dev" and the dev npm channel stamps
|
|
68
|
+
// "<v>-dev.<stamp>" versions; neither publishes matching runtime packages,
|
|
69
|
+
// so they are not acquirable. Release and rc/next versions are.
|
|
70
|
+
export function isAcquirableRelease(version: string): boolean {
|
|
71
|
+
return !version.includes("-dev");
|
|
72
|
+
}
|
|
@@ -1,51 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// Pins the server resolution: exactly like the runner's (explicit dir →
|
|
2
|
+
// repo tree → acquired slim package).
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
mkdirSync,
|
|
6
|
+
mkdtempSync,
|
|
7
|
+
readFileSync,
|
|
8
|
+
writeFileSync,
|
|
9
|
+
} from "node:fs";
|
|
3
10
|
import { tmpdir } from "node:os";
|
|
4
11
|
import { join } from "node:path";
|
|
5
|
-
import { gzipSync } from "fflate";
|
|
6
12
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
// Build a minimal ustar archive with a single regular-file entry.
|
|
10
|
-
function makeTar(name: string, content: Buffer): Buffer {
|
|
11
|
-
const header = Buffer.alloc(512);
|
|
12
|
-
header.write(name, 0, "utf8");
|
|
13
|
-
header.write(content.length.toString(8).padStart(11, "0"), 124, "ascii");
|
|
14
|
-
header.write("0", 156, "ascii");
|
|
15
|
-
const data = Buffer.alloc(Math.ceil(content.length / 512) * 512);
|
|
16
|
-
content.copy(data);
|
|
17
|
-
const trailer = Buffer.alloc(1024);
|
|
18
|
-
return Buffer.concat([header, data, trailer]);
|
|
19
|
-
}
|
|
13
|
+
import { acquireServer, ensureServer, resolveServerTs } from "./server.js";
|
|
20
14
|
|
|
21
|
-
function
|
|
22
|
-
return
|
|
15
|
+
function tempDir(prefix: string): string {
|
|
16
|
+
return mkdtempSync(join(tmpdir(), prefix));
|
|
23
17
|
}
|
|
24
18
|
|
|
25
|
-
|
|
26
|
-
// shasum-format checksum for the `.sha256` URL. `corruptChecksum` flips the
|
|
27
|
-
// returned hash to exercise the verification failure path.
|
|
28
|
-
function fakeFetch(archiveGz: Uint8Array, opts: { corruptChecksum?: boolean; capturedUrls?: string[] } = {}): typeof fetch {
|
|
29
|
-
const checksum = opts.corruptChecksum === true ? "0".repeat(64) : sha256(archiveGz);
|
|
30
|
-
return (async (url: string) => {
|
|
31
|
-
opts.capturedUrls?.push(url);
|
|
32
|
-
if (url.endsWith(".sha256")) {
|
|
33
|
-
return { ok: true, status: 200, text: async () => `${checksum} archive.tar.gz\n` };
|
|
34
|
-
}
|
|
35
|
-
return {
|
|
36
|
-
ok: true,
|
|
37
|
-
status: 200,
|
|
38
|
-
arrayBuffer: async () => archiveGz.buffer.slice(archiveGz.byteOffset, archiveGz.byteOffset + archiveGz.byteLength),
|
|
39
|
-
};
|
|
40
|
-
}) as unknown as typeof fetch;
|
|
41
|
-
}
|
|
19
|
+
const fakeNode = (): string => "/usr/bin/node";
|
|
42
20
|
|
|
43
|
-
|
|
21
|
+
// Snapshot and restore the env vars the resolution reads, so tests don't leak.
|
|
22
|
+
const TOUCHED = [
|
|
23
|
+
"STIGMER_SERVER_DIR",
|
|
24
|
+
"STIGMER_NODE_BIN",
|
|
25
|
+
] as const;
|
|
44
26
|
let saved: Record<string, string | undefined>;
|
|
45
27
|
|
|
46
28
|
beforeEach(() => {
|
|
47
29
|
saved = {};
|
|
48
|
-
for (const key of TOUCHED)
|
|
30
|
+
for (const key of TOUCHED) {
|
|
31
|
+
saved[key] = process.env[key];
|
|
32
|
+
delete process.env[key];
|
|
33
|
+
}
|
|
49
34
|
});
|
|
50
35
|
|
|
51
36
|
afterEach(() => {
|
|
@@ -55,98 +40,177 @@ afterEach(() => {
|
|
|
55
40
|
}
|
|
56
41
|
});
|
|
57
42
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
43
|
+
function builtServerDir(): string {
|
|
44
|
+
const dir = tempDir("stigmer-server-");
|
|
45
|
+
writeFileSync(join(dir, "package.json"), "{}");
|
|
46
|
+
mkdirSync(join(dir, "dist"));
|
|
47
|
+
writeFileSync(join(dir, "dist", "main.js"), "//");
|
|
48
|
+
return dir;
|
|
49
|
+
}
|
|
64
50
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
arch: "arm64",
|
|
70
|
-
fetchImpl: fakeFetch(gz, { capturedUrls }),
|
|
71
|
-
});
|
|
51
|
+
describe("ensureServer", () => {
|
|
52
|
+
it("resolves the server from STIGMER_SERVER_DIR", () => {
|
|
53
|
+
const dir = builtServerDir();
|
|
54
|
+
process.env.STIGMER_SERVER_DIR = dir;
|
|
72
55
|
|
|
73
|
-
expect(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
);
|
|
78
|
-
expect(capturedUrls).toContain(
|
|
79
|
-
"https://github.com/stigmer/stigmer/releases/download/v0.5.0/stigmer-server-v0.5.0-darwin-arm64.tar.gz.sha256",
|
|
80
|
-
);
|
|
56
|
+
expect(ensureServer({ node: fakeNode })).toEqual({
|
|
57
|
+
nodeBin: "/usr/bin/node",
|
|
58
|
+
entryPath: join(dir, "dist", "main.js"),
|
|
59
|
+
appDir: dir,
|
|
60
|
+
});
|
|
81
61
|
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe("resolveServerTs", () => {
|
|
65
|
+
it("resolves a built server from STIGMER_SERVER_DIR", () => {
|
|
66
|
+
const dir = builtServerDir();
|
|
67
|
+
process.env.STIGMER_SERVER_DIR = dir;
|
|
82
68
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
await downloadServerBinary({
|
|
89
|
-
version: "0.5.0-rc.1",
|
|
90
|
-
binPath,
|
|
91
|
-
platform: "linux",
|
|
92
|
-
arch: "x64",
|
|
93
|
-
fetchImpl: fakeFetch(gz, { capturedUrls }),
|
|
69
|
+
expect(resolveServerTs(fakeNode)).toEqual({
|
|
70
|
+
nodeBin: "/usr/bin/node",
|
|
71
|
+
entryPath: join(dir, "dist", "main.js"),
|
|
72
|
+
appDir: dir,
|
|
94
73
|
});
|
|
74
|
+
});
|
|
95
75
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
);
|
|
76
|
+
it("errors with build guidance when dist/main.js is missing", () => {
|
|
77
|
+
const dir = tempDir("stigmer-server-");
|
|
78
|
+
writeFileSync(join(dir, "package.json"), "{}");
|
|
79
|
+
process.env.STIGMER_SERVER_DIR = dir;
|
|
80
|
+
|
|
81
|
+
expect(() => resolveServerTs(fakeNode)).toThrow(/not built/);
|
|
82
|
+
// Remediation rides in hints, not the message.
|
|
83
|
+
const hints = captureHints(() => resolveServerTs(fakeNode));
|
|
84
|
+
expect(hints.join("\n")).toMatch(/make build-server/);
|
|
99
85
|
});
|
|
100
86
|
|
|
101
|
-
it("
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
downloadServerBinary({
|
|
106
|
-
version: "0.5.0",
|
|
107
|
-
binPath,
|
|
108
|
-
platform: "darwin",
|
|
109
|
-
arch: "arm64",
|
|
110
|
-
fetchImpl: fakeFetch(gz, { corruptChecksum: true }),
|
|
111
|
-
}),
|
|
112
|
-
).rejects.toThrow(/checksum mismatch/);
|
|
87
|
+
it("rejects an override that is not a server package", () => {
|
|
88
|
+
const dir = tempDir("stigmer-server-");
|
|
89
|
+
process.env.STIGMER_SERVER_DIR = join(dir, "no-package-here"); // no package.json
|
|
90
|
+
expect(() => resolveServerTs(fakeNode)).toThrow(/not a server package/);
|
|
113
91
|
});
|
|
92
|
+
});
|
|
114
93
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
94
|
+
describe("acquireServer", () => {
|
|
95
|
+
it("installs the slim package and resolves its main.js entry", () => {
|
|
96
|
+
const home = tempDir("stigmer-home-");
|
|
97
|
+
const installed: Array<{ dir: string; spec: string }> = [];
|
|
98
|
+
const install = (dir: string, spec: string): void => {
|
|
99
|
+
installed.push({ dir, spec });
|
|
100
|
+
// Simulate the npm install laying down the slim package.
|
|
101
|
+
const pkgDir = join(dir, "node_modules", "@stigmer", "server-slim");
|
|
102
|
+
mkdirSync(pkgDir, { recursive: true });
|
|
103
|
+
writeFileSync(join(pkgDir, "main.js"), "// slim bundle");
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const launch = acquireServer({
|
|
107
|
+
home,
|
|
108
|
+
version: "0.5.0",
|
|
109
|
+
node: fakeNode,
|
|
110
|
+
install,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const installDir = join(home, ".stigmer", "runtimes", "0.5.0");
|
|
114
|
+
expect(launch).toEqual({
|
|
115
|
+
nodeBin: "/usr/bin/node",
|
|
116
|
+
entryPath: join(
|
|
117
|
+
installDir,
|
|
118
|
+
"node_modules",
|
|
119
|
+
"@stigmer",
|
|
120
|
+
"server-slim",
|
|
121
|
+
"main.js",
|
|
122
|
+
),
|
|
123
|
+
appDir: join(installDir, "node_modules", "@stigmer", "server-slim"),
|
|
124
|
+
});
|
|
125
|
+
expect(installed).toEqual([
|
|
126
|
+
{ dir: installDir, spec: "@stigmer/server-slim@0.5.0" },
|
|
127
|
+
]);
|
|
126
128
|
});
|
|
127
129
|
|
|
128
|
-
it("
|
|
129
|
-
const
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
130
|
+
it("reuses a prior install without reinstalling", () => {
|
|
131
|
+
const home = tempDir("stigmer-home-");
|
|
132
|
+
const pkgDir = join(
|
|
133
|
+
home,
|
|
134
|
+
".stigmer",
|
|
135
|
+
"runtimes",
|
|
136
|
+
"0.5.0",
|
|
137
|
+
"node_modules",
|
|
138
|
+
"@stigmer",
|
|
139
|
+
"server-slim",
|
|
140
|
+
);
|
|
141
|
+
mkdirSync(pkgDir, { recursive: true });
|
|
142
|
+
writeFileSync(join(pkgDir, "main.js"), "// slim bundle");
|
|
143
|
+
|
|
144
|
+
const install = vi.fn();
|
|
145
|
+
const launch = acquireServer({
|
|
146
|
+
home,
|
|
147
|
+
version: "0.5.0",
|
|
148
|
+
node: fakeNode,
|
|
149
|
+
install,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
expect(launch.nodeBin).toBe("/usr/bin/node");
|
|
153
|
+
expect(install).not.toHaveBeenCalled();
|
|
135
154
|
});
|
|
136
|
-
});
|
|
137
155
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
156
|
+
// The runner and the server share one per-version install root; acquiring
|
|
157
|
+
// the server after the runner must ADD to it, never clobber the root
|
|
158
|
+
// manifest the runner's install already wrote.
|
|
159
|
+
it("shares the runtimes install root with the runner without clobbering it", () => {
|
|
160
|
+
const home = tempDir("stigmer-home-");
|
|
161
|
+
const installDir = join(home, ".stigmer", "runtimes", "0.5.0");
|
|
162
|
+
mkdirSync(installDir, { recursive: true });
|
|
163
|
+
writeFileSync(
|
|
164
|
+
join(installDir, "package.json"),
|
|
165
|
+
'{"name":"stigmer-runtime","marker":"pre-existing"}\n',
|
|
166
|
+
);
|
|
145
167
|
|
|
146
|
-
const
|
|
147
|
-
|
|
168
|
+
const install = (dir: string): void => {
|
|
169
|
+
const pkgDir = join(dir, "node_modules", "@stigmer", "server-slim");
|
|
170
|
+
mkdirSync(pkgDir, { recursive: true });
|
|
171
|
+
writeFileSync(join(pkgDir, "main.js"), "// slim bundle");
|
|
172
|
+
};
|
|
173
|
+
acquireServer({ home, version: "0.5.0", node: fakeNode, install });
|
|
148
174
|
|
|
149
|
-
expect(
|
|
150
|
-
|
|
175
|
+
expect(readFileSync(join(installDir, "package.json"), "utf8")).toContain(
|
|
176
|
+
"pre-existing",
|
|
177
|
+
);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// The hunted npm failure shape: the install runs but the platform-native
|
|
181
|
+
// package was skipped (unsupported OS/arch), so the entry never appears.
|
|
182
|
+
it("errors actionably when the install produces no entry", () => {
|
|
183
|
+
const home = tempDir("stigmer-home-");
|
|
184
|
+
const install = vi.fn(); // runs, lays down nothing
|
|
185
|
+
const attempt = (): unknown => acquireServer({ home, version: "0.5.0", node: fakeNode, install });
|
|
186
|
+
expect(attempt).toThrow(/install did not produce/);
|
|
187
|
+
expect(install).toHaveBeenCalledOnce();
|
|
188
|
+
const hints = captureHints(attempt).join("\n");
|
|
189
|
+
expect(hints).toMatch(/platform-native/);
|
|
190
|
+
expect(hints).toMatch(/Remove .* and retry/);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it("refuses to acquire for a non-release (dev) build, naming the fallback", () => {
|
|
194
|
+
const home = tempDir("stigmer-home-");
|
|
195
|
+
const attempt = (): unknown =>
|
|
196
|
+
acquireServer({
|
|
197
|
+
home,
|
|
198
|
+
version: "0.0.0-dev",
|
|
199
|
+
node: fakeNode,
|
|
200
|
+
install: vi.fn(),
|
|
201
|
+
});
|
|
202
|
+
expect(attempt).toThrow(/non-release build/);
|
|
203
|
+
// The escape hatch rides in the hints: the explicit server dir.
|
|
204
|
+
const hints = captureHints(attempt);
|
|
205
|
+
expect(hints.join("\n")).toMatch(/STIGMER_SERVER_DIR/);
|
|
151
206
|
});
|
|
152
207
|
});
|
|
208
|
+
|
|
209
|
+
function captureHints(attempt: () => unknown): readonly string[] {
|
|
210
|
+
try {
|
|
211
|
+
attempt();
|
|
212
|
+
} catch (err) {
|
|
213
|
+
return (err as { hints?: readonly string[] }).hints ?? [];
|
|
214
|
+
}
|
|
215
|
+
throw new Error("expected the attempt to throw");
|
|
216
|
+
}
|