@objectstack/types 17.0.0-rc.3 → 17.0.0-rc.5
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/CHANGELOG.md +376 -2
- package/dist/index.d.mts +289 -14
- package/dist/index.d.ts +289 -14
- package/dist/index.js +131 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +117 -1
- package/dist/index.mjs.map +1 -1
- package/dist/node.d.mts +120 -6
- package/dist/node.d.ts +120 -6
- package/dist/node.js +132 -8
- package/dist/node.js.map +1 -1
- package/dist/node.mjs +125 -7
- package/dist/node.mjs.map +1 -1
- package/package.json +2 -2
package/dist/node.js
CHANGED
|
@@ -20,33 +20,157 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/node.ts
|
|
21
21
|
var node_exports = {};
|
|
22
22
|
__export(node_exports, {
|
|
23
|
+
HOST_DECLARATION_FIELDS: () => HOST_DECLARATION_FIELDS,
|
|
24
|
+
HOST_IMPORT_FAILURE_KIND: () => HOST_IMPORT_FAILURE_KIND,
|
|
23
25
|
createHostImporter: () => createHostImporter,
|
|
24
|
-
createHostRequire: () => createHostRequire
|
|
26
|
+
createHostRequire: () => createHostRequire,
|
|
27
|
+
hostImportFailureKind: () => hostImportFailureKind,
|
|
28
|
+
isDeclaredByHost: () => isDeclaredByHost,
|
|
29
|
+
packageNameFromSpecifier: () => packageNameFromSpecifier,
|
|
30
|
+
readHostDeclaration: () => readHostDeclaration
|
|
25
31
|
});
|
|
26
32
|
module.exports = __toCommonJS(node_exports);
|
|
33
|
+
var import_node_fs = require("fs");
|
|
27
34
|
var import_node_module = require("module");
|
|
28
35
|
var import_node_path = require("path");
|
|
29
36
|
var import_node_url = require("url");
|
|
37
|
+
|
|
38
|
+
// src/module-not-found.ts
|
|
39
|
+
function isModuleNotFoundError(err) {
|
|
40
|
+
const code = err?.code;
|
|
41
|
+
if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") return true;
|
|
42
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
43
|
+
return msg.includes("Cannot find module") || msg.includes("Cannot find package");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/node.ts
|
|
30
47
|
function createHostRequire(hostRoot = process.cwd()) {
|
|
31
48
|
return (0, import_node_module.createRequire)((0, import_node_path.join)(hostRoot, "package.json"));
|
|
32
49
|
}
|
|
33
|
-
|
|
50
|
+
var HOST_DECLARATION_FIELDS = [
|
|
51
|
+
"dependencies",
|
|
52
|
+
"devDependencies",
|
|
53
|
+
"optionalDependencies",
|
|
54
|
+
"peerDependencies"
|
|
55
|
+
];
|
|
56
|
+
function packageNameFromSpecifier(specifier) {
|
|
57
|
+
if (!specifier || specifier.startsWith(".") || specifier.startsWith("/")) return void 0;
|
|
58
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(specifier)) return void 0;
|
|
59
|
+
const segments = specifier.split("/");
|
|
60
|
+
if (specifier.startsWith("@")) {
|
|
61
|
+
if (segments.length < 2 || !segments[0] || !segments[1]) return void 0;
|
|
62
|
+
return `${segments[0]}/${segments[1]}`;
|
|
63
|
+
}
|
|
64
|
+
return segments[0] || void 0;
|
|
65
|
+
}
|
|
66
|
+
function readHostDeclaration(specifier, hostRoot = process.cwd()) {
|
|
67
|
+
const packageName = packageNameFromSpecifier(specifier) ?? specifier;
|
|
68
|
+
const base = { packageName, hostRoot, declared: false };
|
|
69
|
+
let manifest;
|
|
70
|
+
try {
|
|
71
|
+
manifest = JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path.join)(hostRoot, "package.json"), "utf8"));
|
|
72
|
+
} catch {
|
|
73
|
+
return { ...base, manifestMissing: true };
|
|
74
|
+
}
|
|
75
|
+
for (const field of HOST_DECLARATION_FIELDS) {
|
|
76
|
+
const entries = manifest[field];
|
|
77
|
+
if (!entries || typeof entries !== "object") continue;
|
|
78
|
+
const specifierValue = entries[packageName];
|
|
79
|
+
if (specifierValue === void 0) continue;
|
|
80
|
+
return { ...base, declared: true, field, specifier: String(specifierValue) };
|
|
81
|
+
}
|
|
82
|
+
return base;
|
|
83
|
+
}
|
|
84
|
+
function isDeclaredByHost(specifier, hostRoot) {
|
|
85
|
+
return readHostDeclaration(specifier, hostRoot).declared;
|
|
86
|
+
}
|
|
87
|
+
var HOST_IMPORT_FAILURE_KIND = "objectstackHostImportFailureKind";
|
|
88
|
+
function hostImportFailureKind(err) {
|
|
89
|
+
const kind = err?.[HOST_IMPORT_FAILURE_KIND];
|
|
90
|
+
return kind === "undeclared" || kind === "declared-unresolvable" ? kind : void 0;
|
|
91
|
+
}
|
|
92
|
+
function hostImportError(kind, message, cause) {
|
|
93
|
+
const err = new Error(message);
|
|
94
|
+
return Object.assign(err, {
|
|
95
|
+
cause,
|
|
96
|
+
code: "MODULE_NOT_FOUND",
|
|
97
|
+
[HOST_IMPORT_FAILURE_KIND]: kind
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
function undeclaredMessage(declaration, cause) {
|
|
101
|
+
const { packageName, hostRoot, manifestMissing } = declaration;
|
|
102
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
103
|
+
return `Cannot find package '${packageName}': the host app does not declare it.
|
|
104
|
+
host app: ${hostRoot}
|
|
105
|
+
` + (manifestMissing ? " no readable package.json was found there \u2014 nothing can be declared\n" : ` checked: ${HOST_DECLARATION_FIELDS.join(", ")}
|
|
106
|
+
`) + `
|
|
107
|
+
Declare it in that app's package.json and install it, e.g.
|
|
108
|
+
cd ${hostRoot} && pnpm add ${packageName}
|
|
109
|
+
|
|
110
|
+
Being merely REACHABLE is not enough and is rejected on purpose (#4719):
|
|
111
|
+
a package hoisted into a workspace store \u2014 which is what NODE_PATH points
|
|
112
|
+
at in every pnpm bin shim \u2014 used to resolve here regardless of the app's
|
|
113
|
+
package.json, so the same app booted or refused depending on how the
|
|
114
|
+
process was launched. The declaration is the contract.
|
|
115
|
+
(fallback resolution also failed: ${detail})`;
|
|
116
|
+
}
|
|
117
|
+
function unresolvableMessage(declaration, cause) {
|
|
118
|
+
const { packageName, hostRoot, field, specifier } = declaration;
|
|
119
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
120
|
+
return `Cannot find module '${packageName}': the host app DECLARES it (${field}: ${JSON.stringify(specifier)}) but it could not be resolved.
|
|
121
|
+
host app: ${hostRoot}
|
|
122
|
+
|
|
123
|
+
This is an INSTALL problem, not a declaration problem \u2014 the declaration is
|
|
124
|
+
already there, so re-reading the package.json will not help. Check:
|
|
125
|
+
\u2022 dependencies never installed, or installed before the declaration was added \u2192 run \`pnpm install\` in ${hostRoot}
|
|
126
|
+
\u2022 a production prune / filtered deploy dropped it (devDependencies and
|
|
127
|
+
optionalDependencies go first)
|
|
128
|
+
\u2022 it IS installed but its "main"/"exports" points at a dist that was never built
|
|
129
|
+
(resolver: ${detail})`;
|
|
130
|
+
}
|
|
131
|
+
function createHostImporter(hostRoot = process.cwd()) {
|
|
132
|
+
const hostRequire = createHostRequire(hostRoot);
|
|
34
133
|
return async (pkg) => {
|
|
35
|
-
|
|
36
|
-
try {
|
|
37
|
-
resolved = hostRequire.resolve(pkg);
|
|
38
|
-
} catch {
|
|
134
|
+
if (packageNameFromSpecifier(pkg) === void 0) {
|
|
39
135
|
return import(
|
|
40
136
|
/* webpackIgnore: true */
|
|
41
137
|
pkg
|
|
42
138
|
);
|
|
43
139
|
}
|
|
44
|
-
|
|
140
|
+
const declaration = readHostDeclaration(pkg, hostRoot);
|
|
141
|
+
if (declaration.declared) {
|
|
142
|
+
let resolved;
|
|
143
|
+
try {
|
|
144
|
+
resolved = hostRequire.resolve(pkg);
|
|
145
|
+
} catch (cause) {
|
|
146
|
+
throw hostImportError(
|
|
147
|
+
"declared-unresolvable",
|
|
148
|
+
unresolvableMessage(declaration, cause),
|
|
149
|
+
cause
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
return import((0, import_node_url.pathToFileURL)(resolved).href);
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
return await import(
|
|
156
|
+
/* webpackIgnore: true */
|
|
157
|
+
pkg
|
|
158
|
+
);
|
|
159
|
+
} catch (cause) {
|
|
160
|
+
if (!isModuleNotFoundError(cause)) throw cause;
|
|
161
|
+
throw hostImportError("undeclared", undeclaredMessage(declaration, cause), cause);
|
|
162
|
+
}
|
|
45
163
|
};
|
|
46
164
|
}
|
|
47
165
|
// Annotate the CommonJS export names for ESM import in node:
|
|
48
166
|
0 && (module.exports = {
|
|
167
|
+
HOST_DECLARATION_FIELDS,
|
|
168
|
+
HOST_IMPORT_FAILURE_KIND,
|
|
49
169
|
createHostImporter,
|
|
50
|
-
createHostRequire
|
|
170
|
+
createHostRequire,
|
|
171
|
+
hostImportFailureKind,
|
|
172
|
+
isDeclaredByHost,
|
|
173
|
+
packageNameFromSpecifier,
|
|
174
|
+
readHostDeclaration
|
|
51
175
|
});
|
|
52
176
|
//# sourceMappingURL=node.js.map
|
package/dist/node.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/node.ts"],"sourcesContent":["// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * `@objectstack/types/node` — the **node-only** slice of the shared utilities.\n *\n * WHY A SUBPATH AND NOT THE ROOT EXPORT. `@objectstack/types` is a dependency of\n * `@objectstack/hono`, whose whole reason to exist is \"edge-compatible REST API\n * server for Cloudflare Workers, Deno, Bun, and Node\" — and of the plugin/service\n * layer a `LiteKernel` boots on Workers. The root entry (`src/index.ts`) reaches\n * **zero** `node:` builtins today, and that is a property those consumers depend\n * on: a Workers bundle that pulls in `node:module` fails to build (or dies at\n * first call) even when nothing ever invokes it. Everything here needs\n * `node:module` / `node:url` by definition — it exists to drive Node's own\n * resolver — so it lives behind its own entry point instead.\n *\n * The isolation is structural, not conventional: `tsup` builds `src/index.ts` and\n * `src/node.ts` as separate entries with `splitting: false`, so the root bundle\n * contains no reference to this file, and `node-isolation.test.ts` fails the\n * build if anything reachable from the root ever imports a `node:` builtin. Same\n * arrangement `@objectstack/metadata` already ships for `./node`.\n *\n * ── What lives here ──────────────────────────────────────────────────────────\n *\n * Resolving optional packages from the **host app**, not from the framework\n * package doing the importing.\n *\n * Node ESM resolves a bare `import('pkg')` against the **importer's own\n * realpath**. Framework packages (the CLI, `@objectstack/verify`,\n * `@objectstack/dogfood`) are reached through `link:`/workspace dependencies, so\n * their realpath is inside the *framework* workspace — a bare import from any of\n * them can only ever see packages installed in the framework's own\n * `node_modules`. Every package that lives OUTSIDE that workspace and is supplied\n * by the app being served, verified or tested — a cloud-private package such as\n * `@objectstack/organizations` or `@objectstack/service-ai-studio`, or anything a\n * customer installs into their own project — is therefore invisible to a bare\n * import, no matter what the host app declares in its `package.json`\n * (cloud#1013: `objectstack serve` could never load the enterprise multi-org\n * runtime, so every self-hosted walled-posture deployment hit the ADR-0093 D5\n * fail-fast and exited 1; framework#4700: `bootStack({ multiTenant: true })` told\n * apps to install a package they had already installed, and the dogfood\n * multi-org probes were constant-false).\n *\n * The fix is to resolve from the host app's root and import the resolved\n * absolute path. The importing package's own resolution stays as the fallback,\n * for the framework-owned packages it depends on and the host does not declare.\n *\n * Resolution failure is the ONLY thing that falls back. A package the host\n * resolves but that throws while it evaluates is a genuine crash and propagates\n * unchanged: re-importing it bare would replace the real cause with a\n * `MODULE_NOT_FOUND`, which every caller here classifies as \"not installed\" —\n * turning a broken package into a silent skip (or, on the organizations path,\n * into a fatal message telling the operator to install what is already there).\n */\n\nimport { createRequire } from 'node:module';\nimport { join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\n/**\n * Imports a package as the host app would see it.\n *\n * `any` is the module namespace of a package this repo does not compile against\n * (it is not a dependency of the importing package at all) — every call site\n * reads an export off it dynamically, exactly as the bare `import()` it replaces\n * did.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type HostImporter = (pkg: string) => Promise<any>;\n\n/**\n * A `require` anchored at the **host app's** `package.json` — i.e. the project\n * `objectstack serve` was invoked in, or the app `bootStack` is verifying, whose\n * `node_modules` carries the packages it declares.\n *\n * @param hostRoot Directory holding the host app's `package.json` (default: the\n * process CWD, which is where the CLI reads `objectstack.config.ts` from too).\n */\nexport function createHostRequire(hostRoot: string = process.cwd()): NodeRequire {\n return createRequire(join(hostRoot, 'package.json'));\n}\n\n/**\n * Build an importer that resolves from the host app first, then falls back to\n * the importing package's own resolution.\n *\n * @param hostRequire Reuse an existing host `require` (callers usually also need\n * it to read the host `package.json`); defaults to one anchored at the CWD.\n */\nexport function createHostImporter(\n hostRequire: NodeRequire = createHostRequire(),\n): HostImporter {\n return async (pkg: string): Promise<any> => {\n let resolved: string;\n try {\n resolved = hostRequire.resolve(pkg);\n } catch {\n // Invisible to the host app — try the importing package's own\n // dependencies. A package neither can see throws MODULE_NOT_FOUND from\n // here, which is what the callers' \"missing vs crashed\" classification\n // expects.\n return import(/* webpackIgnore: true */ pkg);\n }\n return import(pathToFileURL(resolved).href);\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsDA,yBAA8B;AAC9B,uBAAqB;AACrB,sBAA8B;AAqBvB,SAAS,kBAAkB,WAAmB,QAAQ,IAAI,GAAgB;AAC/E,aAAO,sCAAc,uBAAK,UAAU,cAAc,CAAC;AACrD;AASO,SAAS,mBACd,cAA2B,kBAAkB,GAC/B;AACd,SAAO,OAAO,QAA8B;AAC1C,QAAI;AACJ,QAAI;AACF,iBAAW,YAAY,QAAQ,GAAG;AAAA,IACpC,QAAQ;AAKN,aAAO;AAAA;AAAA,QAAiC;AAAA;AAAA,IAC1C;AACA,WAAO,WAAO,+BAAc,QAAQ,EAAE;AAAA,EACxC;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/node.ts","../src/module-not-found.ts"],"sourcesContent":["// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * `@objectstack/types/node` — the **node-only** slice of the shared utilities.\n *\n * WHY A SUBPATH AND NOT THE ROOT EXPORT. `@objectstack/types` is a dependency of\n * `@objectstack/hono`, whose whole reason to exist is \"edge-compatible REST API\n * server for Cloudflare Workers, Deno, Bun, and Node\" — and of the plugin/service\n * layer a `LiteKernel` boots on Workers. The root entry (`src/index.ts`) reaches\n * **zero** `node:` builtins today, and that is a property those consumers depend\n * on: a Workers bundle that pulls in `node:module` fails to build (or dies at\n * first call) even when nothing ever invokes it. Everything here needs\n * `node:module` / `node:url` by definition — it exists to drive Node's own\n * resolver — so it lives behind its own entry point instead.\n *\n * The isolation is structural, not conventional: `tsup` builds `src/index.ts` and\n * `src/node.ts` as separate entries with `splitting: false`, so the root bundle\n * contains no reference to this file, and `node-isolation.test.ts` fails the\n * build if anything reachable from the root ever imports a `node:` builtin. Same\n * arrangement `@objectstack/metadata` already ships for `./node`.\n *\n * ── What lives here ──────────────────────────────────────────────────────────\n *\n * Resolving optional packages from the **host app**, not from the framework\n * package doing the importing.\n *\n * Node ESM resolves a bare `import('pkg')` against the **importer's own\n * realpath**. Framework packages (the CLI, `@objectstack/verify`,\n * `@objectstack/dogfood`) are reached through `link:`/workspace dependencies, so\n * their realpath is inside the *framework* workspace — a bare import from any of\n * them can only ever see packages installed in the framework's own\n * `node_modules`. Every package that lives OUTSIDE that workspace and is supplied\n * by the app being served, verified or tested — a cloud-private package such as\n * `@objectstack/organizations` or `@objectstack/service-ai-studio`, or anything a\n * customer installs into their own project — is therefore invisible to a bare\n * import, no matter what the host app declares in its `package.json`\n * (cloud#1013: `objectstack serve` could never load the enterprise multi-org\n * runtime, so every self-hosted walled-posture deployment hit the ADR-0093 D5\n * fail-fast and exited 1; framework#4700: `bootStack({ multiTenant: true })` told\n * apps to install a package they had already installed, and the dogfood\n * multi-org probes were constant-false).\n *\n * The fix is to resolve from the host app's root and import the resolved\n * absolute path. The importing package's own resolution stays as the fallback,\n * for the framework-owned packages it depends on and the host does not declare.\n *\n * Resolution failure is the ONLY thing that falls back. A package the host\n * resolves but that throws while it evaluates is a genuine crash and propagates\n * unchanged: re-importing it bare would replace the real cause with a\n * `MODULE_NOT_FOUND`, which every caller here classifies as \"not installed\" —\n * turning a broken package into a silent skip (or, on the organizations path,\n * into a fatal message telling the operator to install what is already there).\n *\n * ── #4719: the host's DECLARATION gates the lookup, not its resolvability ────\n *\n * \"Resolve from the host app\" was implemented as a CJS `createRequire` anchored\n * at the host's `package.json`, and **CJS resolution honours `NODE_PATH`**\n * (`Module.globalPaths`). The first thing a pnpm-generated bin shim does is\n *\n * export NODE_PATH=\"<workspace>/node_modules/.pnpm/node_modules\"\n *\n * and every `serve` / `dev` child process inherits it. Everything any package in\n * the workspace transitively depends on lives in that hoisted store, so\n * `hostRequire.resolve(pkg)` succeeded for packages the host app had never\n * declared — the answer depended on HOW THE PROCESS WAS LAUNCHED, not on the\n * app. Measured on cloud's `apps/objectos-ee`, which did not declare\n * `@objectstack/organizations`: `pnpm start` (through the shim) booted with the\n * organizations plugin mounted and ADR-0093 D5 silent, while\n * `node node_modules/@objectstack/cli/bin/run.js serve` (no shim, no NODE_PATH)\n * hit the D5 fail-fast and exited 1. Same app, same `package.json`, same\n * posture. D5's own message told operators to \"declare it in the app's\n * package.json\" — the one thing the CLI never checked.\n *\n * So the host lookup is now gated on the host's **declaration**: a package name\n * is looked up in the host's `node_modules` only when it appears in the host\n * `package.json` (see {@link HOST_DECLARATION_FIELDS}). Reachability through a\n * hoisted store or `NODE_PATH` is deliberately not accepted — it is precisely\n * the accident that made the contract unenforced. This is the \"declared =\n * enforced\" shape the rest of the repo uses (Prime Directive #10): the\n * declaration is a deliberate authoring act, machine-checkable at the moment of\n * boot, and independent of launcher, package manager and hoist layout.\n *\n * The two failures it separates were, until now, one indistinguishable\n * `MODULE_NOT_FOUND`, with opposite remedies:\n *\n * - **undeclared** — the app never asked for this package. Remedy: declare it\n * in the app's `package.json` and install.\n * - **declared but unresolvable** — the app asked for it and the install is\n * broken/pruned/unbuilt. Remedy: fix the install. Re-reading the\n * `package.json` is wasted effort; the declaration is right there.\n *\n * {@link hostImportFailureKind} exposes that classification to callers so their\n * fail-fast text can say which one it is (`packages/cli` ADR-0093 D5,\n * `packages/verify` `bootStack`, `packages/qa/dogfood`'s enterprise probe).\n */\n\nimport { readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { isModuleNotFoundError } from './module-not-found.js';\n\n/**\n * Imports a package as the host app would see it.\n *\n * `any` is the module namespace of a package this repo does not compile against\n * (it is not a dependency of the importing package at all) — every call site\n * reads an export off it dynamically, exactly as the bare `import()` it replaces\n * did.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type HostImporter = (pkg: string) => Promise<any>;\n\n/**\n * A `require` anchored at the **host app's** `package.json` — i.e. the project\n * `objectstack serve` was invoked in, or the app `bootStack` is verifying, whose\n * `node_modules` carries the packages it declares.\n *\n * @param hostRoot Directory holding the host app's `package.json` (default: the\n * process CWD, which is where the CLI reads `objectstack.config.ts` from too).\n */\nexport function createHostRequire(hostRoot: string = process.cwd()): NodeRequire {\n return createRequire(join(hostRoot, 'package.json'));\n}\n\n/**\n * The `package.json` fields whose KEYS count as a host-app declaration (#4719).\n *\n * All four are deliberate authoring acts in the app's own manifest that name the\n * package, which is the signal this gate is built on — not \"is it reachable\".\n * Why each is in:\n *\n * - `dependencies` — the obvious one: the app runs with it.\n * - `devDependencies` — the app being served / verified / dogfooded IS the\n * project, not a library someone else consumes, so its dev deps are installed\n * in exactly the environment this resolver runs in.\n * - `optionalDependencies` — npm/pnpm install them and tolerate an install\n * failure. \"Installed ⇒ declared\" holds; and if it did NOT install, the\n * declared-but-unresolvable branch says so precisely instead of pretending the\n * app never asked.\n * - `peerDependencies` — an app is nobody's peer, so this is an unusual place to\n * put an enterprise add-on; but it still NAMES the package on purpose, and\n * `packages/cli`'s own edition gate (`serve`'s AI-service opt-in, #1597) has\n * read all four since it was written. Accepting three here and four there\n * would fork \"declared\" into two dialects for one question — the shape Prime\n * Directive #12 exists to prevent. That gate now delegates to this list, so\n * there is one owner and one answer.\n *\n * `bundleDependencies` is absent on purpose: it is an array of names that must\n * ALSO appear in `dependencies`, so it can never be the only declaration.\n */\nexport const HOST_DECLARATION_FIELDS = [\n 'dependencies',\n 'devDependencies',\n 'optionalDependencies',\n 'peerDependencies',\n] as const;\n\nexport type HostDeclarationField = (typeof HOST_DECLARATION_FIELDS)[number];\n\n/** What the host app's `package.json` says about one package name. */\nexport interface HostDeclaration {\n /** Bare package name the specifier belongs to (subpath stripped). */\n packageName: string;\n /** Directory whose `package.json` was consulted. */\n hostRoot: string;\n /** True when {@link packageName} is a key of one of {@link HOST_DECLARATION_FIELDS}. */\n declared: boolean;\n /** Which field carried it (first match, in {@link HOST_DECLARATION_FIELDS} order). */\n field?: HostDeclarationField;\n /** The version range AS WRITTEN — `^1.2.3`, `workspace:*`, `npm:@acme/x@1`, `link:../x`. */\n specifier?: string;\n /** True when `hostRoot` has no readable / parseable `package.json` at all. */\n manifestMissing?: boolean;\n}\n\n/**\n * The package a bare specifier belongs to, or `undefined` when the specifier is\n * not a bare package name at all (a relative/absolute path, a `file:`/`data:`\n * URL, or a `node:`-prefixed builtin). Those bypass the declaration gate: they\n * are not things a `package.json` can declare.\n *\n * Subpaths are stripped, so `@objectstack/platform-objects/plugin` is declared\n * by `\"@objectstack/platform-objects\"`, which is the only key that can exist.\n * Scoped names keep both segments.\n *\n * Alias dependencies need no special case, and that is the point: with\n * `\"foo\": \"npm:bar@1\"` the importable specifier is `foo` and the manifest key is\n * `foo`, so keying on the KEY (never the value) is exactly right — `import('bar')`\n * correctly reads as undeclared unless `bar` is itself a key. Same for\n * `workspace:` / `link:` / `file:` specifiers: the key is the name, the value is\n * the package manager's business.\n */\nexport function packageNameFromSpecifier(specifier: string): string | undefined {\n if (!specifier || specifier.startsWith('.') || specifier.startsWith('/')) return undefined;\n // A URL-ish or protocol-prefixed specifier (`node:fs`, `file:///…`, `data:…`).\n if (/^[a-z][a-z0-9+.-]*:/i.test(specifier)) return undefined;\n const segments = specifier.split('/');\n if (specifier.startsWith('@')) {\n if (segments.length < 2 || !segments[0] || !segments[1]) return undefined;\n return `${segments[0]}/${segments[1]}`;\n }\n return segments[0] || undefined;\n}\n\n/**\n * Read what the host app's `package.json` declares about `specifier`.\n *\n * Deliberately a plain manifest READ, never a resolution attempt: resolvability\n * is the property #4719 proved unreliable (it moved with `NODE_PATH` and the\n * hoist layout), while the manifest is the same fact in every launcher.\n */\nexport function readHostDeclaration(\n specifier: string,\n hostRoot: string = process.cwd(),\n): HostDeclaration {\n const packageName = packageNameFromSpecifier(specifier) ?? specifier;\n const base: HostDeclaration = { packageName, hostRoot, declared: false };\n\n let manifest: Record<string, unknown>;\n try {\n manifest = JSON.parse(readFileSync(join(hostRoot, 'package.json'), 'utf8')) as Record<\n string,\n unknown\n >;\n } catch {\n // No manifest ⇒ nothing is declared. Recorded rather than swallowed so the\n // failure text can say \"there is no package.json here\" instead of the\n // misleading \"you did not declare it\".\n return { ...base, manifestMissing: true };\n }\n\n for (const field of HOST_DECLARATION_FIELDS) {\n const entries = manifest[field];\n if (!entries || typeof entries !== 'object') continue;\n const specifierValue = (entries as Record<string, unknown>)[packageName];\n if (specifierValue === undefined) continue;\n return { ...base, declared: true, field, specifier: String(specifierValue) };\n }\n return base;\n}\n\n/** Convenience predicate over {@link readHostDeclaration}. */\nexport function isDeclaredByHost(specifier: string, hostRoot?: string): boolean {\n return readHostDeclaration(specifier, hostRoot).declared;\n}\n\n/**\n * Why a {@link HostImporter} could not produce a module.\n *\n * - `undeclared` — the host app's `package.json` never names the package, and\n * the importing framework package cannot supply it either. Remedy: DECLARE it\n * in the app and install.\n * - `declared-unresolvable` — the app declares it and it still would not\n * resolve. Remedy: fix the INSTALL. Re-reading the manifest is wasted effort.\n *\n * An evaluation crash is neither: it propagates untouched and carries no kind.\n */\nexport type HostImportFailureKind = 'undeclared' | 'declared-unresolvable';\n\n/**\n * Property carrying {@link HostImportFailureKind} on a thrown error.\n *\n * A string property, read by {@link hostImportFailureKind} — never `instanceof`.\n * `serve` loads plugins through this importer, so CLI and package can hold\n * different module instances of anything class-shaped; the #4818 comment in\n * `serve.ts` names that trap explicitly.\n */\nexport const HOST_IMPORT_FAILURE_KIND = 'objectstackHostImportFailureKind';\n\n/** The classification on an error thrown by a {@link HostImporter}, if any. */\nexport function hostImportFailureKind(err: unknown): HostImportFailureKind | undefined {\n const kind = (err as Record<string, unknown> | null | undefined)?.[HOST_IMPORT_FAILURE_KIND];\n return kind === 'undeclared' || kind === 'declared-unresolvable' ? kind : undefined;\n}\n\nfunction hostImportError(\n kind: HostImportFailureKind,\n message: string,\n cause: unknown,\n): Error {\n // `cause` is assigned rather than passed to the constructor: this package\n // compiles against a lib without the ES2022 `ErrorOptions` overload.\n const err = new Error(message);\n // Every caller classifies \"missing vs crashed\" through\n // `isModuleNotFoundError`; both of these ARE the missing case, just with\n // different remedies, so they must keep answering true to it.\n return Object.assign(err, {\n cause,\n code: 'MODULE_NOT_FOUND',\n [HOST_IMPORT_FAILURE_KIND]: kind,\n });\n}\n\nfunction undeclaredMessage(declaration: HostDeclaration, cause: unknown): string {\n const { packageName, hostRoot, manifestMissing } = declaration;\n const detail = cause instanceof Error ? cause.message : String(cause);\n return (\n `Cannot find package '${packageName}': the host app does not declare it.\\n` +\n ` host app: ${hostRoot}\\n` +\n (manifestMissing\n ? ' no readable package.json was found there — nothing can be declared\\n'\n : ` checked: ${HOST_DECLARATION_FIELDS.join(', ')}\\n`) +\n `\\n Declare it in that app's package.json and install it, e.g.\\n` +\n ` cd ${hostRoot} && pnpm add ${packageName}\\n` +\n '\\n Being merely REACHABLE is not enough and is rejected on purpose (#4719):\\n' +\n ' a package hoisted into a workspace store — which is what NODE_PATH points\\n' +\n \" at in every pnpm bin shim — used to resolve here regardless of the app's\\n\" +\n ' package.json, so the same app booted or refused depending on how the\\n' +\n ' process was launched. The declaration is the contract.\\n' +\n ` (fallback resolution also failed: ${detail})`\n );\n}\n\nfunction unresolvableMessage(declaration: HostDeclaration, cause: unknown): string {\n const { packageName, hostRoot, field, specifier } = declaration;\n const detail = cause instanceof Error ? cause.message : String(cause);\n return (\n `Cannot find module '${packageName}': the host app DECLARES it ` +\n `(${field}: ${JSON.stringify(specifier)}) but it could not be resolved.\\n` +\n ` host app: ${hostRoot}\\n` +\n '\\n This is an INSTALL problem, not a declaration problem — the declaration is\\n' +\n ' already there, so re-reading the package.json will not help. Check:\\n' +\n ` • dependencies never installed, or installed before the declaration was added → run \\`pnpm install\\` in ${hostRoot}\\n` +\n ' • a production prune / filtered deploy dropped it (devDependencies and\\n' +\n ' optionalDependencies go first)\\n' +\n ' • it IS installed but its \"main\"/\"exports\" points at a dist that was never built\\n' +\n ` (resolver: ${detail})`\n );\n}\n\n/**\n * Build an importer that loads a package **as the host app declares it**, and\n * otherwise falls back to the importing package's own resolution.\n *\n * Order of operations, and why (#4719):\n *\n * 1. The host `package.json` is READ. Only a declared name is looked up in the\n * host's `node_modules`. An undeclared name never reaches the host resolver,\n * so no amount of `NODE_PATH` / hoisting can make it appear to be the app's.\n * 2. Declared but unresolvable is reported AS SUCH — the app asked for it and\n * the install is broken. It is not retried bare: falling back there would\n * reintroduce exactly the \"some other package happens to supply it\" accident\n * this gate closes, and would report an install problem as an absence.\n * 3. Undeclared falls back to the importing package's own resolution, which is\n * what keeps every framework-owned load working (`serve`'s plugin-auth /\n * service-i18n path, `bootStack`'s service plugins). Bare `import()` is ESM,\n * and ESM does not honour `NODE_PATH`, so the fallback cannot re-open the\n * hole either. Only when that fails as module-not-found does the undeclared\n * error surface; a package that RESOLVES and then throws while evaluating is\n * a genuine crash and propagates untouched, as before.\n *\n * @param hostRoot Directory holding the host app's `package.json` (default: the\n * process CWD, which is where the CLI reads `objectstack.config.ts` from too).\n * Note this used to take a pre-built `NodeRequire`; it needs the ROOT now,\n * because a `NodeRequire` cannot be asked where it was anchored and the manifest\n * has to be read from there.\n */\nexport function createHostImporter(hostRoot: string = process.cwd()): HostImporter {\n const hostRequire = createHostRequire(hostRoot);\n return async (pkg: string): Promise<any> => {\n // Not a bare package name (a path, a URL, a `node:` builtin) — nothing a\n // manifest could declare. Hand it to the normal resolver untouched.\n if (packageNameFromSpecifier(pkg) === undefined) {\n return import(/* webpackIgnore: true */ pkg);\n }\n\n const declaration = readHostDeclaration(pkg, hostRoot);\n\n if (declaration.declared) {\n let resolved: string;\n try {\n resolved = hostRequire.resolve(pkg);\n } catch (cause) {\n throw hostImportError(\n 'declared-unresolvable',\n unresolvableMessage(declaration, cause),\n cause,\n );\n }\n return import(pathToFileURL(resolved).href);\n }\n\n try {\n return await import(/* webpackIgnore: true */ pkg);\n } catch (cause) {\n // A package that resolved and then exploded is a crash, not an absence.\n if (!isModuleNotFoundError(cause)) throw cause;\n throw hostImportError('undeclared', undeclaredMessage(declaration, cause), cause);\n }\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * True when a dynamic `import()` / `require.resolve()` failed because the\n * module is simply NOT INSTALLED — as opposed to the module being present but\n * throwing while it loads (a real crash). Checking `err.code` FIRST matters:\n * ESM reports a missing package as `err.code === 'ERR_MODULE_NOT_FOUND'` with\n * the human message `Cannot find package '...'`; matching only the older\n * `Cannot find module` string mis-classifies that as a crash (framework#1595).\n *\n * Single shared owner for this classification (framework#3265): the CLI's\n * optional-plugin guards and `requires` capability resolver delegate here, and\n * cloud's `objectos-runtime` capability loader is expected to adopt it at its\n * next framework pin bump — so the parallel loaders cannot drift apart and\n * re-introduce the #1595 false-alarm class.\n */\nexport function isModuleNotFoundError(err: unknown): boolean {\n const code = (err as { code?: string } | null | undefined)?.code;\n if (code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND') return true;\n const msg = err instanceof Error ? err.message : String(err);\n return msg.includes('Cannot find module') || msg.includes('Cannot find package');\n}\n"],"mappings":"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","names":[]}
|
package/dist/node.mjs
CHANGED
|
@@ -1,26 +1,144 @@
|
|
|
1
1
|
// src/node.ts
|
|
2
|
+
import { readFileSync } from "fs";
|
|
2
3
|
import { createRequire } from "module";
|
|
3
4
|
import { join } from "path";
|
|
4
5
|
import { pathToFileURL } from "url";
|
|
6
|
+
|
|
7
|
+
// src/module-not-found.ts
|
|
8
|
+
function isModuleNotFoundError(err) {
|
|
9
|
+
const code = err?.code;
|
|
10
|
+
if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") return true;
|
|
11
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
12
|
+
return msg.includes("Cannot find module") || msg.includes("Cannot find package");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// src/node.ts
|
|
5
16
|
function createHostRequire(hostRoot = process.cwd()) {
|
|
6
17
|
return createRequire(join(hostRoot, "package.json"));
|
|
7
18
|
}
|
|
8
|
-
|
|
19
|
+
var HOST_DECLARATION_FIELDS = [
|
|
20
|
+
"dependencies",
|
|
21
|
+
"devDependencies",
|
|
22
|
+
"optionalDependencies",
|
|
23
|
+
"peerDependencies"
|
|
24
|
+
];
|
|
25
|
+
function packageNameFromSpecifier(specifier) {
|
|
26
|
+
if (!specifier || specifier.startsWith(".") || specifier.startsWith("/")) return void 0;
|
|
27
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(specifier)) return void 0;
|
|
28
|
+
const segments = specifier.split("/");
|
|
29
|
+
if (specifier.startsWith("@")) {
|
|
30
|
+
if (segments.length < 2 || !segments[0] || !segments[1]) return void 0;
|
|
31
|
+
return `${segments[0]}/${segments[1]}`;
|
|
32
|
+
}
|
|
33
|
+
return segments[0] || void 0;
|
|
34
|
+
}
|
|
35
|
+
function readHostDeclaration(specifier, hostRoot = process.cwd()) {
|
|
36
|
+
const packageName = packageNameFromSpecifier(specifier) ?? specifier;
|
|
37
|
+
const base = { packageName, hostRoot, declared: false };
|
|
38
|
+
let manifest;
|
|
39
|
+
try {
|
|
40
|
+
manifest = JSON.parse(readFileSync(join(hostRoot, "package.json"), "utf8"));
|
|
41
|
+
} catch {
|
|
42
|
+
return { ...base, manifestMissing: true };
|
|
43
|
+
}
|
|
44
|
+
for (const field of HOST_DECLARATION_FIELDS) {
|
|
45
|
+
const entries = manifest[field];
|
|
46
|
+
if (!entries || typeof entries !== "object") continue;
|
|
47
|
+
const specifierValue = entries[packageName];
|
|
48
|
+
if (specifierValue === void 0) continue;
|
|
49
|
+
return { ...base, declared: true, field, specifier: String(specifierValue) };
|
|
50
|
+
}
|
|
51
|
+
return base;
|
|
52
|
+
}
|
|
53
|
+
function isDeclaredByHost(specifier, hostRoot) {
|
|
54
|
+
return readHostDeclaration(specifier, hostRoot).declared;
|
|
55
|
+
}
|
|
56
|
+
var HOST_IMPORT_FAILURE_KIND = "objectstackHostImportFailureKind";
|
|
57
|
+
function hostImportFailureKind(err) {
|
|
58
|
+
const kind = err?.[HOST_IMPORT_FAILURE_KIND];
|
|
59
|
+
return kind === "undeclared" || kind === "declared-unresolvable" ? kind : void 0;
|
|
60
|
+
}
|
|
61
|
+
function hostImportError(kind, message, cause) {
|
|
62
|
+
const err = new Error(message);
|
|
63
|
+
return Object.assign(err, {
|
|
64
|
+
cause,
|
|
65
|
+
code: "MODULE_NOT_FOUND",
|
|
66
|
+
[HOST_IMPORT_FAILURE_KIND]: kind
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function undeclaredMessage(declaration, cause) {
|
|
70
|
+
const { packageName, hostRoot, manifestMissing } = declaration;
|
|
71
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
72
|
+
return `Cannot find package '${packageName}': the host app does not declare it.
|
|
73
|
+
host app: ${hostRoot}
|
|
74
|
+
` + (manifestMissing ? " no readable package.json was found there \u2014 nothing can be declared\n" : ` checked: ${HOST_DECLARATION_FIELDS.join(", ")}
|
|
75
|
+
`) + `
|
|
76
|
+
Declare it in that app's package.json and install it, e.g.
|
|
77
|
+
cd ${hostRoot} && pnpm add ${packageName}
|
|
78
|
+
|
|
79
|
+
Being merely REACHABLE is not enough and is rejected on purpose (#4719):
|
|
80
|
+
a package hoisted into a workspace store \u2014 which is what NODE_PATH points
|
|
81
|
+
at in every pnpm bin shim \u2014 used to resolve here regardless of the app's
|
|
82
|
+
package.json, so the same app booted or refused depending on how the
|
|
83
|
+
process was launched. The declaration is the contract.
|
|
84
|
+
(fallback resolution also failed: ${detail})`;
|
|
85
|
+
}
|
|
86
|
+
function unresolvableMessage(declaration, cause) {
|
|
87
|
+
const { packageName, hostRoot, field, specifier } = declaration;
|
|
88
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
89
|
+
return `Cannot find module '${packageName}': the host app DECLARES it (${field}: ${JSON.stringify(specifier)}) but it could not be resolved.
|
|
90
|
+
host app: ${hostRoot}
|
|
91
|
+
|
|
92
|
+
This is an INSTALL problem, not a declaration problem \u2014 the declaration is
|
|
93
|
+
already there, so re-reading the package.json will not help. Check:
|
|
94
|
+
\u2022 dependencies never installed, or installed before the declaration was added \u2192 run \`pnpm install\` in ${hostRoot}
|
|
95
|
+
\u2022 a production prune / filtered deploy dropped it (devDependencies and
|
|
96
|
+
optionalDependencies go first)
|
|
97
|
+
\u2022 it IS installed but its "main"/"exports" points at a dist that was never built
|
|
98
|
+
(resolver: ${detail})`;
|
|
99
|
+
}
|
|
100
|
+
function createHostImporter(hostRoot = process.cwd()) {
|
|
101
|
+
const hostRequire = createHostRequire(hostRoot);
|
|
9
102
|
return async (pkg) => {
|
|
10
|
-
|
|
11
|
-
try {
|
|
12
|
-
resolved = hostRequire.resolve(pkg);
|
|
13
|
-
} catch {
|
|
103
|
+
if (packageNameFromSpecifier(pkg) === void 0) {
|
|
14
104
|
return import(
|
|
15
105
|
/* webpackIgnore: true */
|
|
16
106
|
pkg
|
|
17
107
|
);
|
|
18
108
|
}
|
|
19
|
-
|
|
109
|
+
const declaration = readHostDeclaration(pkg, hostRoot);
|
|
110
|
+
if (declaration.declared) {
|
|
111
|
+
let resolved;
|
|
112
|
+
try {
|
|
113
|
+
resolved = hostRequire.resolve(pkg);
|
|
114
|
+
} catch (cause) {
|
|
115
|
+
throw hostImportError(
|
|
116
|
+
"declared-unresolvable",
|
|
117
|
+
unresolvableMessage(declaration, cause),
|
|
118
|
+
cause
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
return import(pathToFileURL(resolved).href);
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
return await import(
|
|
125
|
+
/* webpackIgnore: true */
|
|
126
|
+
pkg
|
|
127
|
+
);
|
|
128
|
+
} catch (cause) {
|
|
129
|
+
if (!isModuleNotFoundError(cause)) throw cause;
|
|
130
|
+
throw hostImportError("undeclared", undeclaredMessage(declaration, cause), cause);
|
|
131
|
+
}
|
|
20
132
|
};
|
|
21
133
|
}
|
|
22
134
|
export {
|
|
135
|
+
HOST_DECLARATION_FIELDS,
|
|
136
|
+
HOST_IMPORT_FAILURE_KIND,
|
|
23
137
|
createHostImporter,
|
|
24
|
-
createHostRequire
|
|
138
|
+
createHostRequire,
|
|
139
|
+
hostImportFailureKind,
|
|
140
|
+
isDeclaredByHost,
|
|
141
|
+
packageNameFromSpecifier,
|
|
142
|
+
readHostDeclaration
|
|
25
143
|
};
|
|
26
144
|
//# sourceMappingURL=node.mjs.map
|
package/dist/node.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/node.ts"],"sourcesContent":["// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * `@objectstack/types/node` — the **node-only** slice of the shared utilities.\n *\n * WHY A SUBPATH AND NOT THE ROOT EXPORT. `@objectstack/types` is a dependency of\n * `@objectstack/hono`, whose whole reason to exist is \"edge-compatible REST API\n * server for Cloudflare Workers, Deno, Bun, and Node\" — and of the plugin/service\n * layer a `LiteKernel` boots on Workers. The root entry (`src/index.ts`) reaches\n * **zero** `node:` builtins today, and that is a property those consumers depend\n * on: a Workers bundle that pulls in `node:module` fails to build (or dies at\n * first call) even when nothing ever invokes it. Everything here needs\n * `node:module` / `node:url` by definition — it exists to drive Node's own\n * resolver — so it lives behind its own entry point instead.\n *\n * The isolation is structural, not conventional: `tsup` builds `src/index.ts` and\n * `src/node.ts` as separate entries with `splitting: false`, so the root bundle\n * contains no reference to this file, and `node-isolation.test.ts` fails the\n * build if anything reachable from the root ever imports a `node:` builtin. Same\n * arrangement `@objectstack/metadata` already ships for `./node`.\n *\n * ── What lives here ──────────────────────────────────────────────────────────\n *\n * Resolving optional packages from the **host app**, not from the framework\n * package doing the importing.\n *\n * Node ESM resolves a bare `import('pkg')` against the **importer's own\n * realpath**. Framework packages (the CLI, `@objectstack/verify`,\n * `@objectstack/dogfood`) are reached through `link:`/workspace dependencies, so\n * their realpath is inside the *framework* workspace — a bare import from any of\n * them can only ever see packages installed in the framework's own\n * `node_modules`. Every package that lives OUTSIDE that workspace and is supplied\n * by the app being served, verified or tested — a cloud-private package such as\n * `@objectstack/organizations` or `@objectstack/service-ai-studio`, or anything a\n * customer installs into their own project — is therefore invisible to a bare\n * import, no matter what the host app declares in its `package.json`\n * (cloud#1013: `objectstack serve` could never load the enterprise multi-org\n * runtime, so every self-hosted walled-posture deployment hit the ADR-0093 D5\n * fail-fast and exited 1; framework#4700: `bootStack({ multiTenant: true })` told\n * apps to install a package they had already installed, and the dogfood\n * multi-org probes were constant-false).\n *\n * The fix is to resolve from the host app's root and import the resolved\n * absolute path. The importing package's own resolution stays as the fallback,\n * for the framework-owned packages it depends on and the host does not declare.\n *\n * Resolution failure is the ONLY thing that falls back. A package the host\n * resolves but that throws while it evaluates is a genuine crash and propagates\n * unchanged: re-importing it bare would replace the real cause with a\n * `MODULE_NOT_FOUND`, which every caller here classifies as \"not installed\" —\n * turning a broken package into a silent skip (or, on the organizations path,\n * into a fatal message telling the operator to install what is already there).\n */\n\nimport { createRequire } from 'node:module';\nimport { join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\n/**\n * Imports a package as the host app would see it.\n *\n * `any` is the module namespace of a package this repo does not compile against\n * (it is not a dependency of the importing package at all) — every call site\n * reads an export off it dynamically, exactly as the bare `import()` it replaces\n * did.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type HostImporter = (pkg: string) => Promise<any>;\n\n/**\n * A `require` anchored at the **host app's** `package.json` — i.e. the project\n * `objectstack serve` was invoked in, or the app `bootStack` is verifying, whose\n * `node_modules` carries the packages it declares.\n *\n * @param hostRoot Directory holding the host app's `package.json` (default: the\n * process CWD, which is where the CLI reads `objectstack.config.ts` from too).\n */\nexport function createHostRequire(hostRoot: string = process.cwd()): NodeRequire {\n return createRequire(join(hostRoot, 'package.json'));\n}\n\n/**\n * Build an importer that resolves from the host app first, then falls back to\n * the importing package's own resolution.\n *\n * @param hostRequire Reuse an existing host `require` (callers usually also need\n * it to read the host `package.json`); defaults to one anchored at the CWD.\n */\nexport function createHostImporter(\n hostRequire: NodeRequire = createHostRequire(),\n): HostImporter {\n return async (pkg: string): Promise<any> => {\n let resolved: string;\n try {\n resolved = hostRequire.resolve(pkg);\n } catch {\n // Invisible to the host app — try the importing package's own\n // dependencies. A package neither can see throws MODULE_NOT_FOUND from\n // here, which is what the callers' \"missing vs crashed\" classification\n // expects.\n return import(/* webpackIgnore: true */ pkg);\n }\n return import(pathToFileURL(resolved).href);\n };\n}\n"],"mappings":";AAsDA,SAAS,qBAAqB;AAC9B,SAAS,YAAY;AACrB,SAAS,qBAAqB;AAqBvB,SAAS,kBAAkB,WAAmB,QAAQ,IAAI,GAAgB;AAC/E,SAAO,cAAc,KAAK,UAAU,cAAc,CAAC;AACrD;AASO,SAAS,mBACd,cAA2B,kBAAkB,GAC/B;AACd,SAAO,OAAO,QAA8B;AAC1C,QAAI;AACJ,QAAI;AACF,iBAAW,YAAY,QAAQ,GAAG;AAAA,IACpC,QAAQ;AAKN,aAAO;AAAA;AAAA,QAAiC;AAAA;AAAA,IAC1C;AACA,WAAO,OAAO,cAAc,QAAQ,EAAE;AAAA,EACxC;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/node.ts","../src/module-not-found.ts"],"sourcesContent":["// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * `@objectstack/types/node` — the **node-only** slice of the shared utilities.\n *\n * WHY A SUBPATH AND NOT THE ROOT EXPORT. `@objectstack/types` is a dependency of\n * `@objectstack/hono`, whose whole reason to exist is \"edge-compatible REST API\n * server for Cloudflare Workers, Deno, Bun, and Node\" — and of the plugin/service\n * layer a `LiteKernel` boots on Workers. The root entry (`src/index.ts`) reaches\n * **zero** `node:` builtins today, and that is a property those consumers depend\n * on: a Workers bundle that pulls in `node:module` fails to build (or dies at\n * first call) even when nothing ever invokes it. Everything here needs\n * `node:module` / `node:url` by definition — it exists to drive Node's own\n * resolver — so it lives behind its own entry point instead.\n *\n * The isolation is structural, not conventional: `tsup` builds `src/index.ts` and\n * `src/node.ts` as separate entries with `splitting: false`, so the root bundle\n * contains no reference to this file, and `node-isolation.test.ts` fails the\n * build if anything reachable from the root ever imports a `node:` builtin. Same\n * arrangement `@objectstack/metadata` already ships for `./node`.\n *\n * ── What lives here ──────────────────────────────────────────────────────────\n *\n * Resolving optional packages from the **host app**, not from the framework\n * package doing the importing.\n *\n * Node ESM resolves a bare `import('pkg')` against the **importer's own\n * realpath**. Framework packages (the CLI, `@objectstack/verify`,\n * `@objectstack/dogfood`) are reached through `link:`/workspace dependencies, so\n * their realpath is inside the *framework* workspace — a bare import from any of\n * them can only ever see packages installed in the framework's own\n * `node_modules`. Every package that lives OUTSIDE that workspace and is supplied\n * by the app being served, verified or tested — a cloud-private package such as\n * `@objectstack/organizations` or `@objectstack/service-ai-studio`, or anything a\n * customer installs into their own project — is therefore invisible to a bare\n * import, no matter what the host app declares in its `package.json`\n * (cloud#1013: `objectstack serve` could never load the enterprise multi-org\n * runtime, so every self-hosted walled-posture deployment hit the ADR-0093 D5\n * fail-fast and exited 1; framework#4700: `bootStack({ multiTenant: true })` told\n * apps to install a package they had already installed, and the dogfood\n * multi-org probes were constant-false).\n *\n * The fix is to resolve from the host app's root and import the resolved\n * absolute path. The importing package's own resolution stays as the fallback,\n * for the framework-owned packages it depends on and the host does not declare.\n *\n * Resolution failure is the ONLY thing that falls back. A package the host\n * resolves but that throws while it evaluates is a genuine crash and propagates\n * unchanged: re-importing it bare would replace the real cause with a\n * `MODULE_NOT_FOUND`, which every caller here classifies as \"not installed\" —\n * turning a broken package into a silent skip (or, on the organizations path,\n * into a fatal message telling the operator to install what is already there).\n *\n * ── #4719: the host's DECLARATION gates the lookup, not its resolvability ────\n *\n * \"Resolve from the host app\" was implemented as a CJS `createRequire` anchored\n * at the host's `package.json`, and **CJS resolution honours `NODE_PATH`**\n * (`Module.globalPaths`). The first thing a pnpm-generated bin shim does is\n *\n * export NODE_PATH=\"<workspace>/node_modules/.pnpm/node_modules\"\n *\n * and every `serve` / `dev` child process inherits it. Everything any package in\n * the workspace transitively depends on lives in that hoisted store, so\n * `hostRequire.resolve(pkg)` succeeded for packages the host app had never\n * declared — the answer depended on HOW THE PROCESS WAS LAUNCHED, not on the\n * app. Measured on cloud's `apps/objectos-ee`, which did not declare\n * `@objectstack/organizations`: `pnpm start` (through the shim) booted with the\n * organizations plugin mounted and ADR-0093 D5 silent, while\n * `node node_modules/@objectstack/cli/bin/run.js serve` (no shim, no NODE_PATH)\n * hit the D5 fail-fast and exited 1. Same app, same `package.json`, same\n * posture. D5's own message told operators to \"declare it in the app's\n * package.json\" — the one thing the CLI never checked.\n *\n * So the host lookup is now gated on the host's **declaration**: a package name\n * is looked up in the host's `node_modules` only when it appears in the host\n * `package.json` (see {@link HOST_DECLARATION_FIELDS}). Reachability through a\n * hoisted store or `NODE_PATH` is deliberately not accepted — it is precisely\n * the accident that made the contract unenforced. This is the \"declared =\n * enforced\" shape the rest of the repo uses (Prime Directive #10): the\n * declaration is a deliberate authoring act, machine-checkable at the moment of\n * boot, and independent of launcher, package manager and hoist layout.\n *\n * The two failures it separates were, until now, one indistinguishable\n * `MODULE_NOT_FOUND`, with opposite remedies:\n *\n * - **undeclared** — the app never asked for this package. Remedy: declare it\n * in the app's `package.json` and install.\n * - **declared but unresolvable** — the app asked for it and the install is\n * broken/pruned/unbuilt. Remedy: fix the install. Re-reading the\n * `package.json` is wasted effort; the declaration is right there.\n *\n * {@link hostImportFailureKind} exposes that classification to callers so their\n * fail-fast text can say which one it is (`packages/cli` ADR-0093 D5,\n * `packages/verify` `bootStack`, `packages/qa/dogfood`'s enterprise probe).\n */\n\nimport { readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { isModuleNotFoundError } from './module-not-found.js';\n\n/**\n * Imports a package as the host app would see it.\n *\n * `any` is the module namespace of a package this repo does not compile against\n * (it is not a dependency of the importing package at all) — every call site\n * reads an export off it dynamically, exactly as the bare `import()` it replaces\n * did.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type HostImporter = (pkg: string) => Promise<any>;\n\n/**\n * A `require` anchored at the **host app's** `package.json` — i.e. the project\n * `objectstack serve` was invoked in, or the app `bootStack` is verifying, whose\n * `node_modules` carries the packages it declares.\n *\n * @param hostRoot Directory holding the host app's `package.json` (default: the\n * process CWD, which is where the CLI reads `objectstack.config.ts` from too).\n */\nexport function createHostRequire(hostRoot: string = process.cwd()): NodeRequire {\n return createRequire(join(hostRoot, 'package.json'));\n}\n\n/**\n * The `package.json` fields whose KEYS count as a host-app declaration (#4719).\n *\n * All four are deliberate authoring acts in the app's own manifest that name the\n * package, which is the signal this gate is built on — not \"is it reachable\".\n * Why each is in:\n *\n * - `dependencies` — the obvious one: the app runs with it.\n * - `devDependencies` — the app being served / verified / dogfooded IS the\n * project, not a library someone else consumes, so its dev deps are installed\n * in exactly the environment this resolver runs in.\n * - `optionalDependencies` — npm/pnpm install them and tolerate an install\n * failure. \"Installed ⇒ declared\" holds; and if it did NOT install, the\n * declared-but-unresolvable branch says so precisely instead of pretending the\n * app never asked.\n * - `peerDependencies` — an app is nobody's peer, so this is an unusual place to\n * put an enterprise add-on; but it still NAMES the package on purpose, and\n * `packages/cli`'s own edition gate (`serve`'s AI-service opt-in, #1597) has\n * read all four since it was written. Accepting three here and four there\n * would fork \"declared\" into two dialects for one question — the shape Prime\n * Directive #12 exists to prevent. That gate now delegates to this list, so\n * there is one owner and one answer.\n *\n * `bundleDependencies` is absent on purpose: it is an array of names that must\n * ALSO appear in `dependencies`, so it can never be the only declaration.\n */\nexport const HOST_DECLARATION_FIELDS = [\n 'dependencies',\n 'devDependencies',\n 'optionalDependencies',\n 'peerDependencies',\n] as const;\n\nexport type HostDeclarationField = (typeof HOST_DECLARATION_FIELDS)[number];\n\n/** What the host app's `package.json` says about one package name. */\nexport interface HostDeclaration {\n /** Bare package name the specifier belongs to (subpath stripped). */\n packageName: string;\n /** Directory whose `package.json` was consulted. */\n hostRoot: string;\n /** True when {@link packageName} is a key of one of {@link HOST_DECLARATION_FIELDS}. */\n declared: boolean;\n /** Which field carried it (first match, in {@link HOST_DECLARATION_FIELDS} order). */\n field?: HostDeclarationField;\n /** The version range AS WRITTEN — `^1.2.3`, `workspace:*`, `npm:@acme/x@1`, `link:../x`. */\n specifier?: string;\n /** True when `hostRoot` has no readable / parseable `package.json` at all. */\n manifestMissing?: boolean;\n}\n\n/**\n * The package a bare specifier belongs to, or `undefined` when the specifier is\n * not a bare package name at all (a relative/absolute path, a `file:`/`data:`\n * URL, or a `node:`-prefixed builtin). Those bypass the declaration gate: they\n * are not things a `package.json` can declare.\n *\n * Subpaths are stripped, so `@objectstack/platform-objects/plugin` is declared\n * by `\"@objectstack/platform-objects\"`, which is the only key that can exist.\n * Scoped names keep both segments.\n *\n * Alias dependencies need no special case, and that is the point: with\n * `\"foo\": \"npm:bar@1\"` the importable specifier is `foo` and the manifest key is\n * `foo`, so keying on the KEY (never the value) is exactly right — `import('bar')`\n * correctly reads as undeclared unless `bar` is itself a key. Same for\n * `workspace:` / `link:` / `file:` specifiers: the key is the name, the value is\n * the package manager's business.\n */\nexport function packageNameFromSpecifier(specifier: string): string | undefined {\n if (!specifier || specifier.startsWith('.') || specifier.startsWith('/')) return undefined;\n // A URL-ish or protocol-prefixed specifier (`node:fs`, `file:///…`, `data:…`).\n if (/^[a-z][a-z0-9+.-]*:/i.test(specifier)) return undefined;\n const segments = specifier.split('/');\n if (specifier.startsWith('@')) {\n if (segments.length < 2 || !segments[0] || !segments[1]) return undefined;\n return `${segments[0]}/${segments[1]}`;\n }\n return segments[0] || undefined;\n}\n\n/**\n * Read what the host app's `package.json` declares about `specifier`.\n *\n * Deliberately a plain manifest READ, never a resolution attempt: resolvability\n * is the property #4719 proved unreliable (it moved with `NODE_PATH` and the\n * hoist layout), while the manifest is the same fact in every launcher.\n */\nexport function readHostDeclaration(\n specifier: string,\n hostRoot: string = process.cwd(),\n): HostDeclaration {\n const packageName = packageNameFromSpecifier(specifier) ?? specifier;\n const base: HostDeclaration = { packageName, hostRoot, declared: false };\n\n let manifest: Record<string, unknown>;\n try {\n manifest = JSON.parse(readFileSync(join(hostRoot, 'package.json'), 'utf8')) as Record<\n string,\n unknown\n >;\n } catch {\n // No manifest ⇒ nothing is declared. Recorded rather than swallowed so the\n // failure text can say \"there is no package.json here\" instead of the\n // misleading \"you did not declare it\".\n return { ...base, manifestMissing: true };\n }\n\n for (const field of HOST_DECLARATION_FIELDS) {\n const entries = manifest[field];\n if (!entries || typeof entries !== 'object') continue;\n const specifierValue = (entries as Record<string, unknown>)[packageName];\n if (specifierValue === undefined) continue;\n return { ...base, declared: true, field, specifier: String(specifierValue) };\n }\n return base;\n}\n\n/** Convenience predicate over {@link readHostDeclaration}. */\nexport function isDeclaredByHost(specifier: string, hostRoot?: string): boolean {\n return readHostDeclaration(specifier, hostRoot).declared;\n}\n\n/**\n * Why a {@link HostImporter} could not produce a module.\n *\n * - `undeclared` — the host app's `package.json` never names the package, and\n * the importing framework package cannot supply it either. Remedy: DECLARE it\n * in the app and install.\n * - `declared-unresolvable` — the app declares it and it still would not\n * resolve. Remedy: fix the INSTALL. Re-reading the manifest is wasted effort.\n *\n * An evaluation crash is neither: it propagates untouched and carries no kind.\n */\nexport type HostImportFailureKind = 'undeclared' | 'declared-unresolvable';\n\n/**\n * Property carrying {@link HostImportFailureKind} on a thrown error.\n *\n * A string property, read by {@link hostImportFailureKind} — never `instanceof`.\n * `serve` loads plugins through this importer, so CLI and package can hold\n * different module instances of anything class-shaped; the #4818 comment in\n * `serve.ts` names that trap explicitly.\n */\nexport const HOST_IMPORT_FAILURE_KIND = 'objectstackHostImportFailureKind';\n\n/** The classification on an error thrown by a {@link HostImporter}, if any. */\nexport function hostImportFailureKind(err: unknown): HostImportFailureKind | undefined {\n const kind = (err as Record<string, unknown> | null | undefined)?.[HOST_IMPORT_FAILURE_KIND];\n return kind === 'undeclared' || kind === 'declared-unresolvable' ? kind : undefined;\n}\n\nfunction hostImportError(\n kind: HostImportFailureKind,\n message: string,\n cause: unknown,\n): Error {\n // `cause` is assigned rather than passed to the constructor: this package\n // compiles against a lib without the ES2022 `ErrorOptions` overload.\n const err = new Error(message);\n // Every caller classifies \"missing vs crashed\" through\n // `isModuleNotFoundError`; both of these ARE the missing case, just with\n // different remedies, so they must keep answering true to it.\n return Object.assign(err, {\n cause,\n code: 'MODULE_NOT_FOUND',\n [HOST_IMPORT_FAILURE_KIND]: kind,\n });\n}\n\nfunction undeclaredMessage(declaration: HostDeclaration, cause: unknown): string {\n const { packageName, hostRoot, manifestMissing } = declaration;\n const detail = cause instanceof Error ? cause.message : String(cause);\n return (\n `Cannot find package '${packageName}': the host app does not declare it.\\n` +\n ` host app: ${hostRoot}\\n` +\n (manifestMissing\n ? ' no readable package.json was found there — nothing can be declared\\n'\n : ` checked: ${HOST_DECLARATION_FIELDS.join(', ')}\\n`) +\n `\\n Declare it in that app's package.json and install it, e.g.\\n` +\n ` cd ${hostRoot} && pnpm add ${packageName}\\n` +\n '\\n Being merely REACHABLE is not enough and is rejected on purpose (#4719):\\n' +\n ' a package hoisted into a workspace store — which is what NODE_PATH points\\n' +\n \" at in every pnpm bin shim — used to resolve here regardless of the app's\\n\" +\n ' package.json, so the same app booted or refused depending on how the\\n' +\n ' process was launched. The declaration is the contract.\\n' +\n ` (fallback resolution also failed: ${detail})`\n );\n}\n\nfunction unresolvableMessage(declaration: HostDeclaration, cause: unknown): string {\n const { packageName, hostRoot, field, specifier } = declaration;\n const detail = cause instanceof Error ? cause.message : String(cause);\n return (\n `Cannot find module '${packageName}': the host app DECLARES it ` +\n `(${field}: ${JSON.stringify(specifier)}) but it could not be resolved.\\n` +\n ` host app: ${hostRoot}\\n` +\n '\\n This is an INSTALL problem, not a declaration problem — the declaration is\\n' +\n ' already there, so re-reading the package.json will not help. Check:\\n' +\n ` • dependencies never installed, or installed before the declaration was added → run \\`pnpm install\\` in ${hostRoot}\\n` +\n ' • a production prune / filtered deploy dropped it (devDependencies and\\n' +\n ' optionalDependencies go first)\\n' +\n ' • it IS installed but its \"main\"/\"exports\" points at a dist that was never built\\n' +\n ` (resolver: ${detail})`\n );\n}\n\n/**\n * Build an importer that loads a package **as the host app declares it**, and\n * otherwise falls back to the importing package's own resolution.\n *\n * Order of operations, and why (#4719):\n *\n * 1. The host `package.json` is READ. Only a declared name is looked up in the\n * host's `node_modules`. An undeclared name never reaches the host resolver,\n * so no amount of `NODE_PATH` / hoisting can make it appear to be the app's.\n * 2. Declared but unresolvable is reported AS SUCH — the app asked for it and\n * the install is broken. It is not retried bare: falling back there would\n * reintroduce exactly the \"some other package happens to supply it\" accident\n * this gate closes, and would report an install problem as an absence.\n * 3. Undeclared falls back to the importing package's own resolution, which is\n * what keeps every framework-owned load working (`serve`'s plugin-auth /\n * service-i18n path, `bootStack`'s service plugins). Bare `import()` is ESM,\n * and ESM does not honour `NODE_PATH`, so the fallback cannot re-open the\n * hole either. Only when that fails as module-not-found does the undeclared\n * error surface; a package that RESOLVES and then throws while evaluating is\n * a genuine crash and propagates untouched, as before.\n *\n * @param hostRoot Directory holding the host app's `package.json` (default: the\n * process CWD, which is where the CLI reads `objectstack.config.ts` from too).\n * Note this used to take a pre-built `NodeRequire`; it needs the ROOT now,\n * because a `NodeRequire` cannot be asked where it was anchored and the manifest\n * has to be read from there.\n */\nexport function createHostImporter(hostRoot: string = process.cwd()): HostImporter {\n const hostRequire = createHostRequire(hostRoot);\n return async (pkg: string): Promise<any> => {\n // Not a bare package name (a path, a URL, a `node:` builtin) — nothing a\n // manifest could declare. Hand it to the normal resolver untouched.\n if (packageNameFromSpecifier(pkg) === undefined) {\n return import(/* webpackIgnore: true */ pkg);\n }\n\n const declaration = readHostDeclaration(pkg, hostRoot);\n\n if (declaration.declared) {\n let resolved: string;\n try {\n resolved = hostRequire.resolve(pkg);\n } catch (cause) {\n throw hostImportError(\n 'declared-unresolvable',\n unresolvableMessage(declaration, cause),\n cause,\n );\n }\n return import(pathToFileURL(resolved).href);\n }\n\n try {\n return await import(/* webpackIgnore: true */ pkg);\n } catch (cause) {\n // A package that resolved and then exploded is a crash, not an absence.\n if (!isModuleNotFoundError(cause)) throw cause;\n throw hostImportError('undeclared', undeclaredMessage(declaration, cause), cause);\n }\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * True when a dynamic `import()` / `require.resolve()` failed because the\n * module is simply NOT INSTALLED — as opposed to the module being present but\n * throwing while it loads (a real crash). Checking `err.code` FIRST matters:\n * ESM reports a missing package as `err.code === 'ERR_MODULE_NOT_FOUND'` with\n * the human message `Cannot find package '...'`; matching only the older\n * `Cannot find module` string mis-classifies that as a crash (framework#1595).\n *\n * Single shared owner for this classification (framework#3265): the CLI's\n * optional-plugin guards and `requires` capability resolver delegate here, and\n * cloud's `objectos-runtime` capability loader is expected to adopt it at its\n * next framework pin bump — so the parallel loaders cannot drift apart and\n * re-introduce the #1595 false-alarm class.\n */\nexport function isModuleNotFoundError(err: unknown): boolean {\n const code = (err as { code?: string } | null | undefined)?.code;\n if (code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND') return true;\n const msg = err instanceof Error ? err.message : String(err);\n return msg.includes('Cannot find module') || msg.includes('Cannot find package');\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/types",
|
|
3
|
-
"version": "17.0.0-rc.
|
|
3
|
+
"version": "17.0.0-rc.5",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Shared interfaces describing the ObjectStack Runtime environment",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@objectstack/spec": "17.0.0-rc.
|
|
21
|
+
"@objectstack/spec": "17.0.0-rc.5"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"typescript": "^6.0.3",
|