Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/loud-pandas-repeat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@stll/stdnum": patch
---

Say which package is missing when the native binding cannot load. The platform
binaries are optional dependencies, so an installer that declines them leaves
the package importable and dead, throwing only on first use. The previous error
listed every supported target, including the one it was running on, which reads
as a portability problem rather than a missing install.
Comment on lines +5 to +9

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use clearer release-note wording.

“Missing install” is awkward here; use “missing installation” or “missing optional package” to describe the failure precisely.

🧰 Tools
🪛 LanguageTool

[grammar] ~9-~9: The word ‘install’ is not a noun.
Context: ...rtability problem rather than a missing install.

(A_INSTALL)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.changeset/loud-pandas-repeat.md around lines 5 - 9, Update the release-note
wording in the changeset to replace “missing install” with “missing
installation” or “missing optional package,” while preserving the explanation
that the native binding fails because an optional platform package was not
installed.

Source: Linters/SAST tools

110 changes: 110 additions & 0 deletions packages/stdnum/src/__test__/native-node.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/**
* The loader had no tests, which is how a real failure stayed invisible: the
* platform binaries are optionalDependencies, so an installer that declines
* them leaves this package importable and dead, throwing only on first use.
* The message that surfaced then said "supported targets: …, linux-x64-gnu,
* …" while failing ON linux-x64-gnu, which points the reader at a
* portability problem that does not exist.
*
* Every input the loader consults is injectable, so both branches are
* testable without touching the filesystem or the real platform.
*/

import { describe, expect, test } from "bun:test";

import type { NativeStdnumBinding } from "../native";
import { loadNativeStdnumBinding } from "../native-node";

/** Nothing resolves: the shape an install with no platform package has. */
const requireNothing = (specifier: string): unknown => {
throw new Error(`Cannot find module '${specifier}'`);
};

describe("loadNativeStdnumBinding", () => {
test("names the uninstalled package when the platform IS a build target", () => {
expect(() =>
loadNativeStdnumBinding({
platform: "linux",
arch: "x64",
libc: "gnu",
env: {},
requireModule: requireNothing,
}),
).toThrow(/@stll\/stdnum-linux-x64-gnu/u);
});

test("says the target exists rather than blaming the platform", () => {
let message = "";
try {
loadNativeStdnumBinding({
platform: "darwin",
arch: "arm64",
env: {},
requireModule: requireNothing,
});
} catch (error) {
message =
error instanceof Error
? error.message
: String(error);
}

expect(message).toContain("IS a build target");
expect(message).toContain("optionalDependency");
// The old message listed every supported target and nothing else, which
// read as "your platform is unsupported" for a platform that is.
expect(message).not.toMatch(
/^Unable to load native stdnum binding.*supported targets/u,
);
});

test("reports an unsupported platform as unsupported", () => {
let message = "";
try {
loadNativeStdnumBinding({
platform: "linux",
arch: "x64",
libc: "musl",
env: {},
requireModule: requireNothing,
});
} catch (error) {
message =
error instanceof Error
? error.message
: String(error);
}

expect(message).toContain(
"not among the build targets",
);
expect(message).toContain("linux-x64-musl");
});

test("prefers an explicit library path over the platform package", () => {
// Every member the binding contract requires; a partial object is
// rejected as "does not match the stdnum binding contract".
const binding = new Proxy(
{},
{ get: () => () => undefined },
) as NativeStdnumBinding;
Comment on lines +87 to +90

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Document why this cast is sound.

Add a // SAFETY: comment explaining that the Proxy returns functions for every runtime contract check performed by asNativeBinding.

As per coding guidelines, “Avoid as casts; narrow with type guards… If unavoidable, add a // SAFETY: comment explaining why it is sound.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/stdnum/src/__test__/native-node.test.ts` around lines 77 - 80,
Document the unavoidable NativeStdnumBinding cast in the Proxy setup by adding a
// SAFETY: comment explaining that the Proxy returns functions for every runtime
contract check performed by asNativeBinding. Keep the existing Proxy behavior
and cast unchanged.

Source: Coding guidelines

const loaded = loadNativeStdnumBinding({
platform: "linux",
arch: "x64",
libc: "gnu",
env: {
STELLA_STDNUM_NATIVE_LIBRARY_PATH:
"/tmp/custom.node",
},
requireModule: (specifier) => {
if (specifier === "/tmp/custom.node")
return binding;
throw new Error(
`Cannot find module '${specifier}'`,
);
},
});

expect(loaded).toBe(binding);
});
});
23 changes: 21 additions & 2 deletions packages/stdnum/src/native-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,31 @@ export const loadNativeStdnumBinding = (
);
}
}
const current = [platform, arch, libc]
.filter(Boolean)
.join("-");
const supported = targets
.map(([p, a, l]) => [p, a, l].filter(Boolean).join("-"))
.join(", ");

// Distinguish "we do not build for this platform" from "we do, but the
// package holding the binary is not installed". They read identically in a
// stack trace and have completely different fixes, and the second is the
// common one: the platform packages are optionalDependencies, so anything
// that declines to install them — `--no-optional`, an install-time version
// gate such as a release-age policy, a partial lockfile — leaves this
// package importable and non-functional, with no error until first use.
const reason =
match === undefined
? `This platform is not among the build targets (${supported}).`
: `This platform IS a build target, so ${match[3]} exists but was not ` +
`installed. It is an optionalDependency: check that optional ` +
`dependencies are enabled, that ${match[3]} is not excluded by an ` +
`install policy, and that it appears in your lockfile.`;
Comment on lines +86 to +92

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not equate a target match with an uninstalled package.

match proves only that this target has a package entry. requireModule(match[3]) can also fail when the package is installed but cannot initialise (for example, a broken native binary), or when it fails the binding contract. Describe it as “could not be loaded” and leave the specific cause to Tried:.

Proposed fix
-      : `This platform IS a build target, so ${match[3]} exists but was not ` +
-        `installed. It is an optionalDependency: check that optional ` +
+      : `This platform is a build target, but ${match[3]} could not be ` +
+        `loaded. It is an optionalDependency: check that optional ` +
         `dependencies are enabled, that ${match[3]} is not excluded by an ` +
         `install policy, and that it appears in your lockfile.`;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const reason =
match === undefined
? `This platform is not among the build targets (${supported}).`
: `This platform IS a build target, so ${match[3]} exists but was not ` +
`installed. It is an optionalDependency: check that optional ` +
`dependencies are enabled, that ${match[3]} is not excluded by an ` +
`install policy, and that it appears in your lockfile.`;
const reason =
match === undefined
? `This platform is not among the build targets (${supported}).`
: `This platform is a build target, but ${match[3]} could not be ` +
`loaded. It is an optionalDependency: check that optional ` +
`dependencies are enabled, that ${match[3]} is not excluded by an ` +
`install policy, and that it appears in your lockfile.`;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/stdnum/src/native-node.ts` around lines 86 - 92, Update the reason
construction in the native module loading path to describe a matched package as
one that “could not be loaded,” rather than asserting it was not installed. Keep
the unsupported-platform message for match === undefined, and leave the specific
requireModule failure cause to the existing “Tried:” details.

Comment on lines +89 to +92

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don't report installed native packages as missing

When the platform package is installed but fails to load, for example because its binary is incompatible, a shared library is unavailable, or its exports fail asNativeBinding, match is still defined and this message falsely states that the package was not installed. The collected errors already distinguish these cases, so the missing-package diagnosis should only be used after confirming that resolving the platform package itself failed; otherwise users are directed toward optional-dependency and lockfile fixes that cannot resolve the actual load failure.

Useful? React with 👍 / 👎.


throw new Error(
`Unable to load native stdnum binding for ${[platform, arch, libc].filter(Boolean).join("-")}; ` +
`supported targets: ${supported}.\n${errors.join("\n")}`,
`Unable to load the native stdnum binding for ${current}. ${reason}\n` +
`Tried:\n${errors.map((line) => ` ${line}`).join("\n")}`,
);
};

Expand Down
Loading