fix: repair platform resolution in the built bundles
All checks were successful
NYX Security Scan / nyx-scan (pull_request) Successful in 5m53s
All checks were successful
NYX Security Scan / nyx-scan (pull_request) Successful in 5m53s
The published package could not be used at all. `require('nomyo-js')`
succeeded, but constructing a client threw:
Cannot find module './node'
Platform selection was done with a runtime require:
const NodeSecureMemory = require('./node').NodeSecureMemory;
Rollup flattens every module into one file, so './node' and './browser'
no longer exist at runtime — and because the calls sit inside function
bodies, rollup left them as literal runtime requires rather than
resolving them. The failure was therefore deferred to first use: module
load and Object.keys() both looked fine, so nothing noticed. The
SecureCompletionClient constructor calls createSecureMemory() and
createHttpClient(), which made every client unconstructable.
Confirmed present at 057ff6c, this branch's merge base, so the npm
package has never worked.
Platform implementations are now injected by the entry points, which is
what src/node.ts and src/browser.ts always claimed to do (they merely
re-exported ./index). createSecureMemory/createHttpClient consult a
registered factory and throw a directive error if none was registered.
No require fallback is kept: leaving one would put an unresolvable
relative require back in the bundle, and bundlers resolve requires
statically, so webpack/vite would fail on a path that does not exist in
dist/. Jest registers the platform via tests/setup.ts instead.
This also keeps the Node HTTP client and the optional native addon out
of the browser bundle, which previously carried both.
Second defect found while verifying: dist/esm/index.mjs contained 13
require() calls (crypto, fs, path, jose, nomyo-native) that the source
loads lazily. `require` does not exist in ES module scope, so an ESM
consumer crashed with "require is not defined" as soon as one ran —
using keyDir for key persistence would have hit it on every Node
version. Node 24 masked the crypto case by having a global crypto. The
ESM output now carries a createRequire shim.
tests/integration/bundle.test.ts covers the artefact that actually
ships: both bundles construct a client, expose the API, resolve the
platform layer, keep Node-only modules out of the browser build, and the
ESM entry is imported and used by a real spawned Node process. Every
other suite runs against src/ through ts-jest, where these paths resolve
normally — which is precisely why this went unnoticed.
Verified end to end by installing the packed tarball into a clean
project: CommonJS and ESM both construct a client and run fs-backed key
generation on Node 18.19.1 and 24.18.0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
111335d7ff
commit
659ef5a62b
8 changed files with 262 additions and 23 deletions
|
|
@ -1,6 +1,18 @@
|
|||
/**
|
||||
* Browser-specific entry point
|
||||
* Ensures browser-specific implementations are used
|
||||
* Browser-specific entry point.
|
||||
*
|
||||
* Registers the browser platform implementations before anything can ask for
|
||||
* them, so the bundled build never needs a runtime path lookup. Wiring them in
|
||||
* here also keeps the Node.js implementations — and their `https`/`fs` imports
|
||||
* and the optional native addon — out of the browser bundle entirely.
|
||||
*/
|
||||
|
||||
import { registerSecureMemory } from './core/memory/secure';
|
||||
import { registerHttpClient } from './core/http/client';
|
||||
import { BrowserSecureMemory } from './core/memory/browser';
|
||||
import { BrowserHttpClient } from './core/http/browser';
|
||||
|
||||
registerSecureMemory(() => new BrowserSecureMemory());
|
||||
registerHttpClient(() => new BrowserHttpClient());
|
||||
|
||||
export * from './index';
|
||||
|
|
|
|||
|
|
@ -20,17 +20,30 @@ export interface HttpClient {
|
|||
get(url: string, options?: Omit<HttpRequestOptions, 'body'>): Promise<HttpResponse>;
|
||||
}
|
||||
|
||||
export type HttpClientFactory = () => HttpClient;
|
||||
|
||||
let httpClientFactory: HttpClientFactory | null = null;
|
||||
|
||||
/**
|
||||
* Create an HTTP client for the current platform
|
||||
* Register the platform's HttpClient implementation.
|
||||
*
|
||||
* Called by the entry points (src/node.ts, src/browser.ts) at load time — see
|
||||
* registerSecureMemory for why a runtime require cannot survive bundling.
|
||||
*/
|
||||
export function registerHttpClient(factory: HttpClientFactory): void {
|
||||
httpClientFactory = factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an HTTP client for the current platform.
|
||||
*/
|
||||
export function createHttpClient(): HttpClient {
|
||||
if (typeof window !== 'undefined') {
|
||||
// Browser environment
|
||||
const BrowserHttpClient = require('./browser').BrowserHttpClient;
|
||||
return new BrowserHttpClient();
|
||||
} else {
|
||||
// Node.js environment
|
||||
const NodeHttpClient = require('./node').NodeHttpClient;
|
||||
return new NodeHttpClient();
|
||||
if (httpClientFactory === null) {
|
||||
throw new Error(
|
||||
'No HttpClient implementation registered. Import the package entry ' +
|
||||
"point ('nomyo-js', or src/node.ts / src/browser.ts) rather than " +
|
||||
'deep-importing core modules.'
|
||||
);
|
||||
}
|
||||
return httpClientFactory();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,17 +144,32 @@ export class SecureByteContext {
|
|||
}
|
||||
}
|
||||
|
||||
export type SecureMemoryFactory = () => SecureMemory;
|
||||
|
||||
let secureMemoryFactory: SecureMemoryFactory | null = null;
|
||||
|
||||
/**
|
||||
* Create a secure memory implementation for the current platform
|
||||
* Register the platform's SecureMemory implementation.
|
||||
*
|
||||
* The entry points (src/node.ts, src/browser.ts) call this at load time. That
|
||||
* matters for the bundled builds: rollup flattens every module into one file,
|
||||
* so requiring the platform module by relative path at runtime would point at a
|
||||
* path that no longer exists, and throw the moment platform code was needed.
|
||||
*/
|
||||
export function registerSecureMemory(factory: SecureMemoryFactory): void {
|
||||
secureMemoryFactory = factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a secure memory implementation for the current platform.
|
||||
*/
|
||||
export function createSecureMemory(): SecureMemory {
|
||||
if (typeof window !== 'undefined') {
|
||||
// Browser environment
|
||||
const BrowserSecureMemory = require('./browser').BrowserSecureMemory;
|
||||
return new BrowserSecureMemory();
|
||||
} else {
|
||||
// Node.js environment
|
||||
const NodeSecureMemory = require('./node').NodeSecureMemory;
|
||||
return new NodeSecureMemory();
|
||||
if (secureMemoryFactory === null) {
|
||||
throw new Error(
|
||||
'No SecureMemory implementation registered. Import the package entry ' +
|
||||
"point ('nomyo-js', or src/node.ts / src/browser.ts) rather than " +
|
||||
'deep-importing core modules.'
|
||||
);
|
||||
}
|
||||
return secureMemoryFactory();
|
||||
}
|
||||
|
|
|
|||
16
src/node.ts
16
src/node.ts
|
|
@ -1,6 +1,18 @@
|
|||
/**
|
||||
* Node.js-specific entry point
|
||||
* Ensures Node.js-specific implementations are used
|
||||
* Node.js-specific entry point.
|
||||
*
|
||||
* Registers the Node.js platform implementations before anything can ask for
|
||||
* them. This is what makes the bundled build work: rollup flattens all modules
|
||||
* into a single file, so the platform layer cannot be discovered at runtime by
|
||||
* relative path — it has to be wired in here, statically.
|
||||
*/
|
||||
|
||||
import { registerSecureMemory } from './core/memory/secure';
|
||||
import { registerHttpClient } from './core/http/client';
|
||||
import { NodeSecureMemory } from './core/memory/node';
|
||||
import { NodeHttpClient } from './core/http/node';
|
||||
|
||||
registerSecureMemory(() => new NodeSecureMemory());
|
||||
registerHttpClient(() => new NodeHttpClient());
|
||||
|
||||
export * from './index';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue