t3-code-android-nightly/.repos/alchemy-effect/packages/alchemy/test/Bundle/InstalledPackages.test.ts
Julius Marminge 6f9cea00ae
chore(refs): sync Effect and Alchemy references to 4.0.1 and beta.80 (#16170)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 13:22:30 -07:00

1612 lines
53 KiB
TypeScript

import {
hashPackageInstallIdentity,
installPackages,
installResolvedPackages,
matchesPackageRoot,
normalizeInstallTargets,
npmInstallArgs,
npmLockfileArgs,
npmPlainInstallArgs,
parsePackageRoot,
parsePackageRootFromSpecifier,
resolveInstallTargets,
resolvePackageInstallIdentity,
} from "@/Bundle/InstalledPackages";
import * as NodeServices from "@effect/platform-node/NodeServices";
import { describe, expect, it } from "alchemy-test";
import * as Effect from "effect/Effect";
import * as FileSystem from "effect/FileSystem";
import * as Path from "effect/Path";
import { strFromU8, unzipSync } from "fflate";
import { spawnSync } from "node:child_process";
import { zipCode } from "@/Util/zip";
const integrationEnabled =
process.env.ALCHEMY_TEST_LAMBDA_EXTERNAL_PACKAGES === "1" &&
spawnSync("npm", ["--version"], { stdio: "ignore" }).status === 0;
const withLockfileFixture = <A, E, R>(
options: {
readonly prefix: string;
readonly packageJson: unknown;
readonly lockfileName: string;
readonly lockfileContent: string;
readonly workspace?: string;
},
use: (context: {
readonly root: string;
readonly cwd: string;
readonly fs: FileSystem.FileSystem;
readonly path: Path.Path;
}) => Effect.Effect<A, E, R>,
) =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({ prefix: options.prefix });
const cwd =
options.workspace === undefined
? root
: path.join(root, options.workspace);
try {
if (cwd !== root) yield* fs.makeDirectory(cwd, { recursive: true });
yield* fs.writeFileString(
path.join(cwd, "package.json"),
JSON.stringify(options.packageJson),
);
yield* fs.writeFileString(
path.join(root, options.lockfileName),
options.lockfileContent,
);
return yield* use({ root, cwd, fs, path });
} finally {
yield* fs.remove(root, { recursive: true }).pipe(Effect.ignore);
}
});
describe("Lambda external packages", { tags: ["unit", "local"] }, () => {
it("accepts only package roots, not subpaths", () => {
expect(parsePackageRoot("sharp")).toBe("sharp");
expect(parsePackageRoot("@img/tool")).toBe("@img/tool");
expect(parsePackageRoot("sharp/lib/index.js")).toBeUndefined();
expect(parsePackageRoot("@img/sharp-linux-arm64/lib")).toBeUndefined();
expect(parsePackageRoot("node:fs")).toBeUndefined();
expect(parsePackageRoot("./local.js")).toBeUndefined();
});
it("extracts package roots from externalized module ids", () => {
expect(parsePackageRootFromSpecifier("heic-convert")).toBe("heic-convert");
expect(parsePackageRootFromSpecifier("heic-convert/lib")).toBe(
"heic-convert",
);
expect(parsePackageRootFromSpecifier("@scope/pkg/subpath")).toBe(
"@scope/pkg",
);
expect(parsePackageRootFromSpecifier("node:fs")).toBeUndefined();
expect(parsePackageRootFromSpecifier("./local.js")).toBeUndefined();
expect(parsePackageRootFromSpecifier("")).toBeUndefined();
expect(parsePackageRootFromSpecifier("@scope")).toBeUndefined();
});
it("matches package roots and subpath imports", () => {
expect(matchesPackageRoot("sharp", "sharp")).toBe(true);
expect(matchesPackageRoot("sharp/lib/index.js", "sharp")).toBe(true);
expect(matchesPackageRoot("sharpish", "sharp")).toBe(false);
expect(matchesPackageRoot("@scope/pkg", "@scope/pkg")).toBe(true);
expect(matchesPackageRoot("@scope/pkg/sub", "@scope/pkg")).toBe(true);
});
it.effect("rejects subpaths in build.install", () =>
Effect.gen(function* () {
const error = yield* Effect.flip(normalizeInstallTargets(["sharp/lib"]));
expect(error.message).toMatch(/Invalid package name/);
}),
);
it.effect("normalizes and validates install targets", () =>
Effect.gen(function* () {
expect(yield* normalizeInstallTargets(["sharp", "pg-native"])).toEqual({
sharp: "*",
"pg-native": "*",
});
expect(yield* normalizeInstallTargets({ sharp: "^0.33.5" })).toEqual({
sharp: "^0.33.5",
});
}),
);
it("targets Linux and the Lambda architecture", () => {
expect(npmInstallArgs("arm64")).toEqual([
"ci",
"--force",
"--platform=linux",
"--os=linux",
"--arch=arm64",
"--cpu=arm64",
"--libc=glibc",
]);
expect(npmInstallArgs("x86_64")).toEqual([
"ci",
"--force",
"--platform=linux",
"--os=linux",
"--arch=x64",
"--cpu=x64",
"--libc=glibc",
]);
expect(npmLockfileArgs("arm64")).toEqual([
"install",
"--force",
"--platform=linux",
"--os=linux",
"--arch=arm64",
"--cpu=arm64",
"--libc=glibc",
"--package-lock-only",
"--ignore-scripts",
]);
expect(npmPlainInstallArgs("arm64")).toEqual([
"install",
"--force",
"--platform=linux",
"--os=linux",
"--arch=arm64",
"--cpu=arm64",
"--libc=glibc",
]);
});
it.effect("resolves catalog versions from pnpm-workspace.yaml", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-external-catalog-",
});
try {
yield* fs.writeFileString(
path.join(root, "pnpm-workspace.yaml"),
["packages:", " - packages/*", "catalog:", " sharp: ^0.33.5"].join(
"\n",
),
);
yield* fs.writeFileString(
path.join(root, "package.json"),
JSON.stringify({ dependencies: { sharp: "catalog:" } }),
);
const files = yield* installPackages({
cwd: root,
install: ["sharp"],
architecture: "arm64",
runNpmInstall: (directory) =>
Effect.gen(function* () {
const packageJson = JSON.parse(
yield* fs.readFileString(path.join(directory, "package.json")),
);
expect(packageJson.dependencies.sharp).toBe("^0.33.5");
}),
});
expect(files.map((file) => file.path)).toContain("package.json");
} finally {
yield* fs.remove(root, { recursive: true }).pipe(Effect.ignore);
}
}).pipe(Effect.provide(NodeServices.layer)),
);
it.effect("resolves named catalog versions from pnpm-workspace.yaml", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-external-pnpm-named-catalog-",
});
try {
yield* fs.writeFileString(
path.join(root, "pnpm-workspace.yaml"),
[
"packages:",
" - packages/*",
"catalogs:",
" native:",
" sharp: ^0.34.5",
].join("\n"),
);
yield* fs.writeFileString(
path.join(root, "package.json"),
JSON.stringify({ dependencies: { sharp: "catalog:native" } }),
);
const files = yield* installPackages({
cwd: root,
install: ["sharp"],
architecture: "arm64",
runNpmInstall: (directory) =>
Effect.gen(function* () {
const packageJson = JSON.parse(
yield* fs.readFileString(path.join(directory, "package.json")),
);
expect(packageJson.dependencies.sharp).toBe("^0.34.5");
}),
});
expect(files.map((file) => file.path)).toContain("package.json");
} finally {
yield* fs.remove(root, { recursive: true }).pipe(Effect.ignore);
}
}).pipe(Effect.provide(NodeServices.layer)),
);
it.effect("resolves catalog versions from Bun workspace metadata", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-external-bun-catalog-",
});
try {
yield* fs.writeFileString(
path.join(root, "package.json"),
JSON.stringify({
workspaces: {
packages: ["packages/*"],
catalogs: {
native: {
sharp: "^0.34.5",
},
},
},
dependencies: { sharp: "catalog:native" },
}),
);
const files = yield* installPackages({
cwd: root,
install: ["sharp"],
architecture: "arm64",
runNpmInstall: (directory) =>
Effect.gen(function* () {
const packageJson = JSON.parse(
yield* fs.readFileString(path.join(directory, "package.json")),
);
expect(packageJson.dependencies.sharp).toBe("^0.34.5");
}),
});
expect(files.map((file) => file.path)).toContain("package.json");
} finally {
yield* fs.remove(root, { recursive: true }).pipe(Effect.ignore);
}
}).pipe(Effect.provide(NodeServices.layer)),
);
it.effect("falls back to optional and dev dependency versions", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-external-dependency-sections-",
});
try {
yield* fs.writeFileString(
path.join(root, "package.json"),
JSON.stringify({
optionalDependencies: { sharp: "^0.34.5" },
devDependencies: { "pg-native": "^3.2.0" },
}),
);
const files = yield* installPackages({
cwd: root,
install: ["sharp", "pg-native"],
architecture: "x86_64",
runNpmInstall: (directory) =>
Effect.gen(function* () {
const packageJson = JSON.parse(
yield* fs.readFileString(path.join(directory, "package.json")),
);
expect(packageJson.dependencies).toEqual({
"pg-native": "^3.2.0",
sharp: "^0.34.5",
});
}),
});
expect(files.map((file) => file.path)).toContain("package.json");
} finally {
yield* fs.remove(root, { recursive: true }).pipe(Effect.ignore);
}
}).pipe(Effect.provide(NodeServices.layer)),
);
it.effect("rejects workspace and file dependency protocols", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-external-incompatible-protocol-",
});
try {
yield* fs.writeFileString(
path.join(root, "package.json"),
JSON.stringify({
dependencies: {
sharp: "workspace:*",
"pg-native": "file:../pg-native",
},
}),
);
const workspaceError = yield* installPackages({
cwd: root,
install: ["sharp"],
architecture: "arm64",
runNpmInstall: () => Effect.void,
}).pipe(Effect.flip);
expect(workspaceError.message).toContain("workspace:*");
const fileError = yield* installPackages({
cwd: root,
install: ["pg-native"],
architecture: "arm64",
runNpmInstall: () => Effect.void,
}).pipe(Effect.flip);
expect(fileError.message).toContain("file:../pg-native");
} finally {
yield* fs.remove(root, { recursive: true }).pipe(Effect.ignore);
}
}).pipe(Effect.provide(NodeServices.layer)),
);
it.effect("installs into an isolated artifact and returns its files", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-external-source-",
});
let installDirectory: string | undefined;
const installArgs: Array<ReadonlyArray<string>> = [];
let artifactPackageJson: unknown;
try {
yield* fs.writeFileString(
path.join(root, "package.json"),
JSON.stringify({ dependencies: { sharp: "^0.34.5" } }),
);
const files = yield* installPackages({
cwd: root,
install: ["sharp"],
architecture: "arm64",
runNpmInstall: (directory, args) =>
Effect.gen(function* () {
installDirectory = directory;
installArgs.push(args);
const manifest = yield* fs.readFileString(
path.join(directory, "package.json"),
);
artifactPackageJson = yield* Effect.try(() =>
JSON.parse(manifest),
);
const sharpRoot = path.join(directory, "node_modules", "sharp");
const sharpBinRoot = path.join(sharpRoot, "bin");
const binRoot = path.join(directory, "node_modules", ".bin");
const binaryRoot = path.join(
directory,
"node_modules",
"@img",
"sharp-linux-arm64",
"lib",
);
const libvipsRoot = path.join(
directory,
"node_modules",
"@img",
"sharp-libvips-linux-arm64",
"lib",
);
yield* fs.makeDirectory(sharpRoot, { recursive: true });
yield* fs.makeDirectory(sharpBinRoot, { recursive: true });
yield* fs.makeDirectory(binRoot, { recursive: true });
yield* fs.makeDirectory(binaryRoot, { recursive: true });
yield* fs.makeDirectory(libvipsRoot, { recursive: true });
yield* fs.writeFileString(
path.join(sharpRoot, "package.json"),
JSON.stringify({ name: "sharp", version: "0.34.5" }),
);
const executablePath = path.join(sharpBinRoot, "sharp-tool");
yield* fs.writeFileString(executablePath, "#!/bin/sh\n");
yield* fs.chmod(executablePath, 0o755);
yield* fs.symlink(
"../sharp/bin/sharp-tool",
path.join(binRoot, "sharp-tool"),
);
yield* fs.writeFile(
path.join(binaryRoot, "sharp.node"),
new Uint8Array([0, 1, 2, 3]),
);
yield* fs.writeFile(
path.join(libvipsRoot, "libvips.so"),
new Uint8Array([4, 5, 6, 7]),
);
yield* fs.writeFileString(
path.join(directory, "package-lock.json"),
"{}",
);
}),
});
expect(artifactPackageJson).toEqual({
private: true,
dependencies: { sharp: "^0.34.5" },
});
// No lockfile in the fixture means nothing to pin, so the install is a
// single plain `npm install` instead of the lockfile-generate + ci pair.
expect(installArgs).toEqual([npmPlainInstallArgs("arm64")]);
expect(files.map((file) => file.path)).toEqual(
expect.arrayContaining([
"package.json",
"package-lock.json",
"node_modules/sharp/package.json",
"node_modules/sharp/bin/sharp-tool",
"node_modules/.bin/sharp-tool",
"node_modules/@img/sharp-linux-arm64/lib/sharp.node",
"node_modules/@img/sharp-libvips-linux-arm64/lib/libvips.so",
]),
);
const executable = files.find(
(file) => file.path === "node_modules/sharp/bin/sharp-tool",
);
const symlink = files.find(
(file) => file.path === "node_modules/.bin/sharp-tool",
);
expect(executable?.mode && executable.mode & 0o111).toBe(0o111);
expect(symlink?.mode && symlink.mode & 0o170000).toBe(0o120000);
const archive = yield* zipCode(
"export const handler = () => {};",
files,
);
const zip = unzipSync(archive);
expect(
zip["node_modules/@img/sharp-linux-arm64/lib/sharp.node"],
).toBeDefined();
expect(
zip["node_modules/@img/sharp-libvips-linux-arm64/lib/libvips.so"],
).toBeDefined();
expect(strFromU8(zip["node_modules/.bin/sharp-tool"]!)).toBe(
"../sharp/bin/sharp-tool",
);
expect(installDirectory).toBeDefined();
expect(yield* fs.exists(installDirectory!)).toBe(false);
} finally {
yield* fs.remove(root, { recursive: true }).pipe(Effect.ignore);
}
}).pipe(Effect.provide(NodeServices.layer)),
);
it.effect("does not install package.json dependencies unless requested", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-external-unrequested-",
});
let installed = false;
try {
yield* fs.writeFileString(
path.join(root, "package.json"),
JSON.stringify({ dependencies: { "heic-convert": "^2.1.0" } }),
);
const files = yield* installPackages({
cwd: root,
architecture: "arm64",
runNpmInstall: () =>
Effect.sync(() => {
installed = true;
}),
});
expect(installed).toBe(false);
expect(files).toEqual([]);
} finally {
yield* fs.remove(root, { recursive: true }).pipe(Effect.ignore);
}
}).pipe(Effect.provide(NodeServices.layer)),
);
it.effect("returns an empty install identity when nothing is requested", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-external-empty-identity-",
});
try {
yield* fs.writeFileString(
path.join(root, "package.json"),
JSON.stringify({ dependencies: { sharp: "^0.34.5" } }),
);
yield* fs.writeFileString(
path.join(root, "package-lock.json"),
"sharp@0.34.5",
);
expect(
yield* resolvePackageInstallIdentity({
cwd: root,
requested: {},
}),
).toEqual({ resolved: {}, overrides: {} });
} finally {
yield* fs.remove(root, { recursive: true }).pipe(Effect.ignore);
}
}).pipe(Effect.provide(NodeServices.layer)),
);
it.effect("fails when the source package.json is missing", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-external-missing-manifest-",
});
try {
const error = yield* installPackages({
cwd: root,
install: ["sharp"],
architecture: "arm64",
runNpmInstall: () => Effect.void,
}).pipe(Effect.flip);
expect(error.message).toContain(
`Failed to read package.json for Lambda externals from '${root}'`,
);
} finally {
yield* fs.remove(root, { recursive: true }).pipe(Effect.ignore);
}
}).pipe(Effect.provide(NodeServices.layer)),
);
it.effect("fails when the nearest lockfile cannot be read", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-external-lockfile-read-error-",
});
try {
yield* fs.writeFileString(
path.join(root, "package.json"),
JSON.stringify({ dependencies: { sharp: "^0.34.5" } }),
);
yield* fs.makeDirectory(path.join(root, "package-lock.json"));
const error = yield* resolvePackageInstallIdentity({
cwd: root,
requested: { sharp: "*" },
}).pipe(Effect.flip);
expect(error.message).toContain(
`Failed to read package-manager lockfile for Lambda externals from '${root}'`,
);
} finally {
yield* fs.remove(root, { recursive: true }).pipe(Effect.ignore);
}
}).pipe(Effect.provide(NodeServices.layer)),
);
it.effect("resolves catalog versions from a top-level Bun catalog", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-external-bun-top-level-catalog-",
});
try {
yield* fs.writeFileString(
path.join(root, "package.json"),
JSON.stringify({
workspaces: ["packages/*"],
catalog: { sharp: "^0.33.5" },
dependencies: { sharp: "catalog:" },
}),
);
const files = yield* installPackages({
cwd: root,
install: ["sharp"],
architecture: "arm64",
runNpmInstall: (directory) =>
Effect.gen(function* () {
const packageJson = JSON.parse(
yield* fs.readFileString(path.join(directory, "package.json")),
);
expect(packageJson.dependencies.sharp).toBe("^0.33.5");
}),
});
expect(files.map((file) => file.path)).toContain("package.json");
} finally {
yield* fs.remove(root, { recursive: true }).pipe(Effect.ignore);
}
}).pipe(Effect.provide(NodeServices.layer)),
);
it.effect(
"resolves catalog versions from manifest catalogs with package-only workspaces",
() =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-external-bun-workspace-packages-only-",
});
try {
yield* fs.writeFileString(
path.join(root, "package.json"),
JSON.stringify({
workspaces: { packages: ["packages/*"] },
catalog: { sharp: "^0.34.5" },
dependencies: { sharp: "catalog:" },
}),
);
const files = yield* installPackages({
cwd: root,
install: ["sharp"],
architecture: "arm64",
runNpmInstall: (directory) =>
Effect.gen(function* () {
const packageJson = JSON.parse(
yield* fs.readFileString(
path.join(directory, "package.json"),
),
);
expect(packageJson.dependencies.sharp).toBe("^0.34.5");
}),
});
expect(files.map((file) => file.path)).toContain("package.json");
} finally {
yield* fs.remove(root, { recursive: true }).pipe(Effect.ignore);
}
}).pipe(Effect.provide(NodeServices.layer)),
);
it.effect("fails when a catalog reference cannot be resolved", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-external-unresolved-catalog-",
});
try {
yield* fs.writeFileString(
path.join(root, "package.json"),
JSON.stringify({ dependencies: { sharp: "catalog:" } }),
);
const error = yield* resolveInstallTargets({
cwd: root,
requested: { sharp: "*" },
}).pipe(Effect.flip);
expect(error.message).toContain(
"Could not resolve catalog version for 'sharp'",
);
} finally {
yield* fs.remove(root, { recursive: true }).pipe(Effect.ignore);
}
}).pipe(Effect.provide(NodeServices.layer)),
);
it.effect("fails when a pnpm catalog entry is missing", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-external-missing-pnpm-catalog-entry-",
});
try {
yield* fs.writeFileString(
path.join(root, "pnpm-workspace.yaml"),
["packages:", " - packages/*", "catalog:", " other: ^1.0.0"].join(
"\n",
),
);
yield* fs.writeFileString(
path.join(root, "package.json"),
JSON.stringify({ dependencies: { sharp: "catalog:" } }),
);
const error = yield* resolveInstallTargets({
cwd: root,
requested: { sharp: "*" },
}).pipe(Effect.flip);
expect(error.message).toContain(
"Could not resolve catalog version for 'sharp' (catalog:)",
);
expect(error.message).toContain("pnpm-workspace.yaml");
} finally {
yield* fs.remove(root, { recursive: true }).pipe(Effect.ignore);
}
}).pipe(Effect.provide(NodeServices.layer)),
);
it.effect("omits lockfile fingerprints when no lockfile exists", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-external-no-lockfile-",
});
try {
yield* fs.writeFileString(
path.join(root, "package.json"),
JSON.stringify({ dependencies: { sharp: "^0.34.5" } }),
);
expect(
yield* resolvePackageInstallIdentity({
cwd: root,
requested: { sharp: "*" },
}),
).toEqual({
resolved: { sharp: "^0.34.5" },
overrides: {},
lockfile: undefined,
});
} finally {
yield* fs.remove(root, { recursive: true }).pipe(Effect.ignore);
}
}).pipe(Effect.provide(NodeServices.layer)),
);
it.effect("fails when a Bun workspace catalog entry is missing", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-external-missing-bun-catalog-entry-",
});
try {
yield* fs.writeFileString(
path.join(root, "package.json"),
JSON.stringify({
workspaces: {
packages: ["packages/*"],
catalogs: {
native: {
other: "^1.0.0",
},
},
},
dependencies: { sharp: "catalog:native" },
}),
);
const error = yield* resolveInstallTargets({
cwd: root,
requested: { sharp: "*" },
}).pipe(Effect.flip);
expect(error.message).toContain(
"Could not resolve catalog version for 'sharp' (catalog:native)",
);
expect(error.message).toContain("package.json");
} finally {
yield* fs.remove(root, { recursive: true }).pipe(Effect.ignore);
}
}).pipe(Effect.provide(NodeServices.layer)),
);
describe.sequential("npm install failures", () => {
it.effect(
"fails when npm is missing from PATH",
() =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-external-missing-npm-",
});
const originalPath = process.env.PATH;
try {
process.env.PATH = "";
yield* fs.writeFileString(
path.join(root, "package.json"),
JSON.stringify({ dependencies: { sharp: "^0.34.5" } }),
);
const error = yield* installResolvedPackages({
resolved: { sharp: "^0.34.5" },
architecture: "arm64",
}).pipe(Effect.flip);
expect(error.message).toContain(
"Failed to run 'npm install' for build.install:",
);
expect(error.message).toMatch(/NotFound|ENOENT/);
} finally {
process.env.PATH = originalPath;
yield* fs.remove(root, { recursive: true }).pipe(Effect.ignore);
}
}).pipe(Effect.provide(NodeServices.layer)),
// Blanking process.env.PATH is process-global: every concurrently
// running test that spawns a child process would fail with ENOENT.
// `exclusive` takes the runner's whole-process write lock.
{ exclusive: true },
);
it.effect(
"fails when npm install exits non-zero",
() =>
Effect.gen(function* () {
const error = yield* installResolvedPackages({
resolved: {
"alchemy-nonexistent-external-package-xyz": "1.0.0",
},
architecture: "arm64",
}).pipe(Effect.flip);
expect(error.message).toMatch(
/npm install for build\.install failed with exit code \d+:/,
);
}).pipe(Effect.provide(NodeServices.layer)),
{ timeout: 120_000 },
);
});
// `chmod 0o000` cannot make a directory unreadable on Windows, so the
// failure under test is unreproducible there.
it.effect.skipIf(process.platform === "win32")(
"fails when installed package files cannot be read",
() =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-external-artifact-read-error-",
});
try {
yield* fs.writeFileString(
path.join(root, "package.json"),
JSON.stringify({ dependencies: { sharp: "^0.34.5" } }),
);
const error = yield* installPackages({
cwd: root,
install: ["sharp"],
architecture: "arm64",
runNpmInstall: (directory, args) =>
Effect.gen(function* () {
if (
args[0] === "install" &&
args.includes("--package-lock-only")
)
return;
yield* fs.writeFileString(
path.join(directory, "package.json"),
JSON.stringify({
private: true,
dependencies: { sharp: "^0.34.5" },
}),
);
yield* fs.writeFileString(
path.join(directory, "package-lock.json"),
"{}",
);
yield* fs.chmod(directory, 0o000);
}),
}).pipe(Effect.flip);
expect(error.message).toBe(
"Failed to read installed Lambda external packages",
);
} finally {
yield* fs.remove(root, { recursive: true }).pipe(Effect.ignore);
}
}).pipe(Effect.provide(NodeServices.layer)),
);
it.effect(
"pins requested packages to the versions in supported lockfiles",
() =>
Effect.gen(function* () {
const fixtures = [
{
name: "package-lock.json",
content: JSON.stringify({
name: "fixture",
lockfileVersion: 3,
packages: {
"": { dependencies: { sharp: "^0.34.0" } },
"node_modules/sharp": {
version: "0.34.5",
dependencies: { semver: "^7.0.0" },
},
"node_modules/semver": { version: "7.7.3" },
},
}),
},
{
name: "package-lock.json",
content: JSON.stringify({
lockfileVersion: 1,
dependencies: {
sharp: {
version: "0.34.5",
requires: { semver: "^7.0.0" },
},
semver: { version: "7.7.3" },
},
}),
},
{
name: "pnpm-lock.yaml",
content: [
"lockfileVersion: '9.0'",
"importers:",
" .:",
" dependencies:",
" sharp:",
" specifier: ^0.34.0",
" version: 0.34.5",
"snapshots:",
" sharp@0.34.5:",
" dependencies:",
" semver: 7.7.3",
" semver@7.7.3: {}",
].join("\n"),
},
{
name: "bun.lock",
content: [
"{",
" // Bun lockfiles use JSONC.",
' "lockfileVersion": 1,',
' "workspaces": {',
' "": { "dependencies": { "sharp": "^0.34.0" } },',
" },",
' "packages": {',
' "sharp": ["sharp@0.34.5", "", { "dependencies": { "semver": "^7.0.0" } }],',
' "semver": ["semver@7.7.3", "", {}],',
" },",
"}",
].join("\n"),
},
{
name: "yarn.lock",
content: [
"# yarn lockfile v1",
"",
"sharp@^0.34.0:",
' version "0.34.5"',
' resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.34.5.tgz"',
" dependencies:",
' semver "^7.0.0"',
"semver@^7.0.0:",
' version "7.7.3"',
].join("\n"),
},
{
name: "yarn.lock",
content: [
'"sharp@npm:^0.34.0":',
" version: 0.34.5",
' resolution: "sharp@npm:0.34.5"',
" dependencies:",
' semver: "npm:^7.0.0"',
'"semver@npm:^7.0.0":',
" version: 7.7.3",
' resolution: "semver@npm:7.7.3"',
].join("\n"),
},
{
// v1 with quoted multi-selector entry keys and quoted pair keys.
name: "yarn.lock",
content: [
"# yarn lockfile v1",
"",
'"sharp@^0.34.0", "sharp@~0.34.5":',
' version "0.34.5"',
' resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.34.5.tgz#abc123"',
" dependencies:",
' "semver" "^7.0.0"',
'"semver@^7.0.0", semver@^7.5.0:',
' version "7.7.3"',
].join("\n"),
},
] as const;
for (const fixture of fixtures) {
yield* withLockfileFixture(
{
prefix: `alchemy-external-pinned-${fixture.name}-`,
packageJson: { dependencies: { sharp: "^0.34.0" } },
lockfileName: fixture.name,
lockfileContent: fixture.content,
},
({ cwd }) =>
Effect.gen(function* () {
const identity = yield* resolvePackageInstallIdentity({
cwd,
requested: { sharp: "*" },
});
expect(identity.resolved, fixture.name).toEqual({
sharp: "0.34.5",
});
expect(identity.overrides, fixture.name).toEqual({
"sharp@0.34.5": { semver: "7.7.3" },
});
}),
);
}
}).pipe(Effect.provide(NodeServices.layer)),
);
it.effect(
"does not replace an explicit install version with a different lock entry",
() =>
withLockfileFixture(
{
prefix: "alchemy-external-explicit-version-",
packageJson: { dependencies: { sharp: "^0.34.0" } },
lockfileName: "package-lock.json",
lockfileContent: JSON.stringify({
name: "fixture",
lockfileVersion: 3,
packages: {
"": { dependencies: { sharp: "^0.34.0" } },
"node_modules/sharp": { version: "0.34.5" },
},
}),
},
({ cwd }) =>
Effect.gen(function* () {
expect(
yield* resolveInstallTargets({
cwd,
requested: { sharp: "0.33.5" },
}),
).toEqual({ sharp: "0.33.5" });
}),
).pipe(Effect.provide(NodeServices.layer)),
);
it.effect(
"pins aliases and transitive dependency edges from package-lock.json",
() =>
withLockfileFixture(
{
prefix: "alchemy-external-package-lock-plan-",
packageJson: {
dependencies: {
alias: "npm:real@^1.0.0",
parent: "^1.0.0",
},
},
lockfileName: "package-lock.json",
lockfileContent: JSON.stringify({
lockfileVersion: 3,
packages: {
"": {
dependencies: {
alias: "npm:real@^1.0.0",
parent: "^1.0.0",
},
},
"node_modules/alias": {
name: "real",
version: "1.2.3",
dependencies: { leaf: "^2.0.0" },
},
"node_modules/parent": {
version: "1.0.0",
dependencies: { leaf: "^2.0.0" },
},
"node_modules/leaf": { version: "2.4.0" },
},
}),
},
({ cwd }) =>
Effect.gen(function* () {
const identity = yield* resolvePackageInstallIdentity({
cwd,
requested: { alias: "*", parent: "*" },
});
expect(identity.resolved).toEqual({
alias: "npm:real@1.2.3",
parent: "1.0.0",
});
expect(identity.overrides).toEqual({
"real@1.2.3": { leaf: "2.4.0" },
"parent@1.0.0": { leaf: "2.4.0" },
});
}),
).pipe(Effect.provide(NodeServices.layer)),
);
it.effect("rejects a stale Bun importer entry", () =>
withLockfileFixture(
{
prefix: "alchemy-external-stale-bun-lock-",
packageJson: { dependencies: { sharp: "^0.35.0" } },
lockfileName: "bun.lock",
lockfileContent: JSON.stringify({
lockfileVersion: 1,
workspaces: {
"": { dependencies: { sharp: "^0.34.0" } },
},
packages: { sharp: ["sharp@0.34.5", "", {}] },
}),
},
({ cwd }) =>
Effect.gen(function* () {
const error = yield* resolveInstallTargets({
cwd,
requested: { sharp: "*" },
}).pipe(Effect.flip);
expect(error.message).toContain("Could not resolve a locked version");
}),
).pipe(Effect.provide(NodeServices.layer)),
);
it.effect("resolves Bun packages for the current workspace importer", () =>
withLockfileFixture(
{
prefix: "alchemy-external-bun-importer-",
workspace: "packages/app",
packageJson: { dependencies: { sharp: "^0.35.0" } },
lockfileName: "bun.lock",
lockfileContent: JSON.stringify({
lockfileVersion: 1,
workspaces: {
"packages/app": {
name: "app",
dependencies: { sharp: "^0.35.0" },
},
},
packages: {
sharp: ["sharp@0.34.5", "", {}],
"app/sharp": ["sharp@0.35.1", "", {}],
},
}),
},
({ cwd }) =>
Effect.gen(function* () {
expect(
yield* resolveInstallTargets({
cwd,
requested: { sharp: "*" },
}),
).toEqual({ sharp: "0.35.1" });
}),
).pipe(Effect.provide(NodeServices.layer)),
);
it.effect("preserves context-specific Bun dependency resolutions", () =>
withLockfileFixture(
{
prefix: "alchemy-external-bun-context-",
packageJson: { dependencies: { root: "^1.0.0" } },
lockfileName: "bun.lock",
lockfileContent: JSON.stringify({
lockfileVersion: 1,
workspaces: {
"": { dependencies: { root: "^1.0.0" } },
},
packages: {
root: [
"root@1.0.0",
"",
{ dependencies: { left: "^1", right: "^1" } },
],
"root/left": ["left@1.0.0", "", { dependencies: { shared: "^1" } }],
"root/right": [
"right@1.0.0",
"",
{ dependencies: { shared: "^1" } },
],
"root/left/shared": [
"shared@1.0.0",
"",
{ dependencies: { leaf: "^1" } },
],
"root/right/shared": [
"shared@1.0.0",
"",
{ dependencies: { leaf: "^1" } },
],
"root/left/shared/leaf": ["leaf@1.0.0", "", {}],
"root/right/shared/leaf": ["leaf@2.0.0", "", {}],
},
}),
},
({ cwd }) =>
Effect.gen(function* () {
const identity = yield* resolvePackageInstallIdentity({
cwd,
requested: { root: "*" },
});
// `shared` resolves uniquely under the root so one flat rule covers
// it; only the divergent `leaf` needs per-branch nesting.
expect(identity.overrides).toEqual({
"root@1.0.0": {
left: { ".": "1.0.0", leaf: "1.0.0" },
right: { ".": "1.0.0", leaf: "2.0.0" },
shared: "1.0.0",
},
});
}),
).pipe(Effect.provide(NodeServices.layer)),
);
it.effect(
"keeps overrides flat and compact for diamond-shaped graphs",
() => {
// 16 levels of diamonds = 2^16 distinct paths. A path-enumerating walk
// would take minutes and emit megabytes; the deduplicated walk must emit
// one flat rule per package.
const levels = 16;
const packages: Record<string, unknown> = {
"": { dependencies: { root: "^1.0.0" } },
"node_modules/root": {
version: "1.0.0",
dependencies: { a1: "^1.0.0", b1: "^1.0.0" },
},
};
for (let i = 1; i <= levels; i++) {
const dependencies =
i === levels
? {}
: { [`a${i + 1}`]: "^1.0.0", [`b${i + 1}`]: "^1.0.0" };
packages[`node_modules/a${i}`] = { version: "1.0.0", dependencies };
packages[`node_modules/b${i}`] = { version: "1.0.0", dependencies };
}
return withLockfileFixture(
{
prefix: "alchemy-external-diamond-",
packageJson: { dependencies: { root: "^1.0.0" } },
lockfileName: "package-lock.json",
lockfileContent: JSON.stringify({ lockfileVersion: 3, packages }),
},
({ cwd }) =>
Effect.gen(function* () {
const identity = yield* resolvePackageInstallIdentity({
cwd,
requested: { root: "*" },
});
const rootOverrides = identity.overrides["root@1.0.0"];
expect(typeof rootOverrides).toBe("object");
expect(Object.keys(rootOverrides as object)).toHaveLength(
levels * 2,
);
expect(
Object.values(rootOverrides as Record<string, unknown>).every(
(spec) => spec === "1.0.0",
),
).toBe(true);
}),
).pipe(Effect.provide(NodeServices.layer));
},
);
it.effect("disambiguates pnpm peer-variant resolutions without failing", () =>
withLockfileFixture(
{
prefix: "alchemy-external-pnpm-peer-variants-",
packageJson: { dependencies: { root: "^1.0.0" } },
lockfileName: "pnpm-lock.yaml",
lockfileContent: [
"lockfileVersion: '9.0'",
"importers:",
" .:",
" dependencies:",
" root:",
" specifier: ^1.0.0",
" version: 1.0.0",
"snapshots:",
" root@1.0.0:",
" dependencies:",
" m1: 1.0.0",
" m2: 1.0.0",
" m1@1.0.0:",
" dependencies:",
" p: 1.0.0(x@1.0.0)",
" m2@1.0.0:",
" dependencies:",
" p: 1.0.0(x@2.0.0)",
" p@1.0.0(x@1.0.0):",
" dependencies:",
" x: 1.0.0",
" p@1.0.0(x@2.0.0):",
" dependencies:",
" x: 2.0.0",
" x@1.0.0: {}",
" x@2.0.0: {}",
].join("\n"),
},
({ cwd }) =>
Effect.gen(function* () {
const identity = yield* resolvePackageInstallIdentity({
cwd,
requested: { root: "*" },
});
expect(identity.overrides).toEqual({
"root@1.0.0": {
m1: { ".": "1.0.0", x: "1.0.0" },
m2: { ".": "1.0.0", x: "2.0.0" },
p: "1.0.0",
},
});
}),
).pipe(Effect.provide(NodeServices.layer)),
);
it.effect(
"parses bun.lock JSONC with comments, trailing commas, and tricky strings",
() =>
withLockfileFixture(
{
prefix: "alchemy-external-bun-jsonc-",
packageJson: { dependencies: { weird: "^1.0.0" } },
lockfileName: "bun.lock",
lockfileContent: [
"{",
" /* block",
" comment */",
' "lockfileVersion": 1,',
' "workspaces": {',
' "": { "dependencies": { "weird": "^1.0.0" } }, // line comment',
" },",
' "packages": {',
' "weird": ["weird@1.2.3", "", { "bin": { "weird,]": "cli,}.js" } }],',
" },",
"}",
].join("\n"),
},
({ cwd }) =>
Effect.gen(function* () {
expect(
yield* resolveInstallTargets({
cwd,
requested: { weird: "*" },
}),
).toEqual({ weird: "1.2.3" });
}),
).pipe(Effect.provide(NodeServices.layer)),
);
it.effect(
"includes package-manager lockfiles in the external package identity",
() =>
Effect.gen(function* () {
const lockfileContent = (
lockfileName:
| "bun.lock"
| "package-lock.json"
| "pnpm-lock.yaml"
| "yarn.lock",
version: string,
) => {
switch (lockfileName) {
case "bun.lock":
return JSON.stringify({
lockfileVersion: 1,
workspaces: {
"": { dependencies: { sharp: "^0.34.5" } },
},
packages: { sharp: [`sharp@${version}`, "", {}] },
});
case "package-lock.json":
return JSON.stringify({
lockfileVersion: 3,
packages: {
"": { dependencies: { sharp: "^0.34.5" } },
"node_modules/sharp": { version },
},
});
case "pnpm-lock.yaml":
return [
"lockfileVersion: '9.0'",
"importers:",
" .:",
" dependencies:",
" sharp:",
" specifier: ^0.34.5",
` version: ${version}`,
].join("\n");
case "yarn.lock":
return [
"# yarn lockfile v1",
"",
`sharp@^0.34.5:`,
` version "${version}"`,
].join("\n");
}
};
for (const lockfileName of [
"bun.lock",
"package-lock.json",
"pnpm-lock.yaml",
"yarn.lock",
] as const) {
yield* withLockfileFixture(
{
prefix: "alchemy-external-lockfile-",
packageJson: { dependencies: { sharp: "^0.34.5" } },
lockfileName,
lockfileContent: lockfileContent(lockfileName, "0.34.5"),
},
({ cwd, fs, path }) =>
Effect.gen(function* () {
const first = yield* resolvePackageInstallIdentity({
cwd,
requested: { sharp: "*" },
});
const firstHash = yield* hashPackageInstallIdentity({
bundleHash: "bundle",
identity: first,
architecture: "arm64",
});
yield* fs.writeFileString(
path.join(cwd, lockfileName),
lockfileContent(lockfileName, "0.34.6"),
);
const second = yield* resolvePackageInstallIdentity({
cwd,
requested: { sharp: "*" },
});
const secondHash = yield* hashPackageInstallIdentity({
bundleHash: "bundle",
identity: second,
architecture: "arm64",
});
expect(first.resolved).not.toEqual(second.resolved);
expect(first.lockfile?.name).toBe(lockfileName);
expect(second.lockfile?.name).toBe(lockfileName);
expect(first.lockfile?.hash).not.toBe(second.lockfile?.hash);
expect(firstHash).not.toBe(secondHash);
}),
);
}
}).pipe(Effect.provide(NodeServices.layer)),
);
it.effect("does not fall back to node_modules for an invalid bun.lockb", () =>
withLockfileFixture(
{
prefix: "alchemy-external-invalid-lockb-",
packageJson: { dependencies: { sharp: "^0.34.5" } },
lockfileName: "bun.lockb",
lockfileContent: "invalid",
},
({ cwd, fs, path }) =>
Effect.gen(function* () {
const installedPackage = path.join(cwd, "node_modules", "sharp");
yield* fs.makeDirectory(installedPackage, { recursive: true });
yield* fs.writeFileString(
path.join(installedPackage, "package.json"),
JSON.stringify({ name: "sharp", version: "999.0.0" }),
);
const error = yield* resolveInstallTargets({
cwd,
requested: { sharp: "*" },
}).pipe(Effect.flip);
expect(error.message).toContain(
"Failed to inspect legacy Bun lockfile",
);
}),
).pipe(Effect.provide(NodeServices.layer)),
);
});
describe.skipIf(!integrationEnabled)(
"Lambda external packages integration",
{ tags: ["live"] },
() => {
it.effect(
"npm-installs aliases with pinned transitive dependencies",
() =>
Effect.gen(function* () {
const files = yield* installResolvedPackages({
resolved: { odd: "npm:is-odd@3.0.1" },
overrides: {
"is-odd@3.0.1": { "is-number": "6.0.0" },
},
architecture: "arm64",
});
const aliasManifest = files.find(
(file) => file.path === "node_modules/odd/package.json",
);
const transitiveManifest = files.find(
(file) =>
file.path === "node_modules/is-number/package.json" ||
file.path ===
"node_modules/odd/node_modules/is-number/package.json",
);
expect(aliasManifest).toBeDefined();
expect(transitiveManifest).toBeDefined();
const decoder = new TextDecoder();
const alias = yield* Effect.try(() =>
JSON.parse(decoder.decode(aliasManifest?.content)),
);
const transitive = yield* Effect.try(() =>
JSON.parse(decoder.decode(transitiveManifest?.content)),
);
expect(alias.name).toBe("is-odd");
expect(alias.version).toBe("3.0.1");
expect(transitive.version).toBe("6.0.0");
}).pipe(Effect.provide(NodeServices.layer)),
{ timeout: 120_000 },
);
it.effect(
"npm-installs sharp with linux arm64 native binaries",
() =>
withLockfileFixture(
{
prefix: "alchemy-external-sharp-",
packageJson: { dependencies: { sharp: "^0.33.5" } },
lockfileName: "package-lock.json",
lockfileContent: JSON.stringify({
lockfileVersion: 3,
packages: {
"": { dependencies: { sharp: "^0.33.5" } },
"node_modules/sharp": {
version: "0.33.5",
dependencies: { semver: "^7.5.4" },
},
"node_modules/semver": { version: "7.7.3" },
},
}),
},
({ cwd }) =>
Effect.gen(function* () {
const files = yield* installPackages({
cwd,
install: ["sharp"],
architecture: "arm64",
});
const paths = files.map((file) => file.path);
expect(paths).toContain("node_modules/sharp/package.json");
expect(
paths.some((filePath) =>
filePath.includes(
"node_modules/@img/sharp-linux-arm64/lib/sharp-linux-arm64.node",
),
),
).toBe(true);
expect(
paths.some((filePath) =>
filePath.includes(
"node_modules/@img/sharp-libvips-linux-arm64/lib/libvips",
),
),
).toBe(true);
}),
).pipe(Effect.provide(NodeServices.layer)),
{ timeout: 120_000 },
);
},
);