@objectstack/types 17.1.0 → 17.3.0

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/dist/node.d.mts CHANGED
@@ -7,6 +7,32 @@
7
7
  * did.
8
8
  */
9
9
  type HostImporter = (pkg: string) => Promise<any>;
10
+ /**
11
+ * The importing package's OWN dynamic import — write it literally, in the
12
+ * calling module:
13
+ *
14
+ * createHostImporter(hostRoot, { fallbackImport: (s) => import(s) })
15
+ *
16
+ * `any` for the same reason {@link HostImporter} uses it: the module namespace
17
+ * belongs to a package this repo does not compile against.
18
+ */
19
+ type FallbackImport = (specifier: string) => Promise<any>;
20
+ /** Options for {@link createHostImporter}. */
21
+ interface HostImporterOptions {
22
+ /**
23
+ * The resolution base for everything the host app does NOT declare — supplied
24
+ * as the caller's own `import()` rather than as a URL string, because a
25
+ * string base was MEASURED to be unimplementable without a regression. See
26
+ * {@link createHostImporter}'s "why a function" note for both measurements.
27
+ *
28
+ * Omitted ⇒ the fallback resolves from `@objectstack/types`, which sees only
29
+ * `@objectstack/types`'s own dependencies. That default is retained so an
30
+ * out-of-tree caller cannot be broken by this parameter's arrival, and the
31
+ * `undeclared` failure text names it explicitly so the gap reports itself
32
+ * instead of being rediscovered.
33
+ */
34
+ fallbackImport?: FallbackImport;
35
+ }
10
36
  /**
11
37
  * A `require` anchored at the **host app's** `package.json` — i.e. the project
12
38
  * `objectstack serve` was invoked in, or the app `bootStack` is verifying, whose
@@ -95,10 +121,19 @@ declare function isDeclaredByHost(specifier: string, hostRoot?: string): boolean
95
121
  * in the app and install.
96
122
  * - `declared-unresolvable` — the app declares it and it still would not
97
123
  * resolve. Remedy: fix the INSTALL. Re-reading the manifest is wasted effort.
124
+ * - `declared-no-loadable-entry` (#14041) — the app declares it, the install
125
+ * delivered it, and the package's own `exports` names NO entry Node can load
126
+ * for the requested subpath — no `require`-condition target (which is why the
127
+ * CJS resolution refused) and no `import`-condition one for the fallback
128
+ * either (a `types`-only or `browser`-only publish, or a subpath the map
129
+ * never names). Remedy: change the PACKAGE — neither the app's manifest nor
130
+ * its install can ever fix this, which is exactly why it must not share the
131
+ * `declared-unresolvable` INSTALL wording.
98
132
  *
99
- * An evaluation crash is neither: it propagates untouched and carries no kind.
133
+ * An evaluation crash is none of these: it propagates untouched and carries no
134
+ * kind.
100
135
  */
101
- type HostImportFailureKind = 'undeclared' | 'declared-unresolvable';
136
+ type HostImportFailureKind = 'undeclared' | 'declared-unresolvable' | 'declared-no-loadable-entry';
102
137
  /**
103
138
  * Property carrying {@link HostImportFailureKind} on a thrown error.
104
139
  *
@@ -123,20 +158,64 @@ declare function hostImportFailureKind(err: unknown): HostImportFailureKind | un
123
158
  * the install is broken. It is not retried bare: falling back there would
124
159
  * reintroduce exactly the "some other package happens to supply it" accident
125
160
  * this gate closes, and would report an install problem as an absence.
126
- * 3. Undeclared falls back to the importing package's own resolution, which is
127
- * what keeps every framework-owned load working (`serve`'s plugin-auth /
128
- * service-i18n path, `bootStack`'s service plugins). Bare `import()` is ESM,
129
- * and ESM does not honour `NODE_PATH`, so the fallback cannot re-open the
130
- * hole either. Only when that fails as module-not-found does the undeclared
131
- * error surface; a package that RESOLVES and then throws while evaluating is
132
- * a genuine crash and propagates untouched, as before.
161
+ * 3. Undeclared falls back to the CALLER's own resolution, which is what keeps
162
+ * every framework-owned load working (`serve`'s plugin-auth / service-i18n
163
+ * path, `bootStack`'s service plugins). Bare `import()` is ESM, and ESM does
164
+ * not honour `NODE_PATH`, so the fallback cannot re-open the hole either.
165
+ * Only when that fails as module-not-found does the undeclared error
166
+ * surface; a package that RESOLVES and then throws while evaluating is a
167
+ * genuine crash and propagates untouched, as before.
168
+ *
169
+ * ── The caller supplies that base, and why it is a FUNCTION (#10943) ─────────
170
+ *
171
+ * Step 3 said "the importing package's own resolution" long before anything
172
+ * made it true. The fallback was a bare `import()` written HERE, and ESM
173
+ * resolves a bare specifier against the module containing the call — so it
174
+ * resolved from `@objectstack/types`, which under a pnpm-isolated layout can
175
+ * see only `@objectstack/types`'s own dependencies. Measured on `main` from an
176
+ * app declaring nothing, `@objectstack/plugin-auth`, `@objectstack/plugin-audit`
177
+ * and `chalk` all resolve from `packages/cli` and all failed through this
178
+ * helper; `@objectstack/spec` — the one dependency this package declares — was
179
+ * the only name that came back OK, which is the whole pattern. Under a hoisted
180
+ * npm/yarn layout the same fallback usually DOES find the caller's
181
+ * dependencies, so the claim was green in some installs and absent in others:
182
+ * the layout-dependence class cloud#1013 and #10645 exist to close, one level
183
+ * up. A declared contract the implementation does not keep is the thing this
184
+ * repo fixes at the producer (Prime Directive #12), so the mechanism moved
185
+ * rather than the sentence.
186
+ *
187
+ * The base arrives as the caller's own `import()` and NOT as a `parentURL` /
188
+ * `import.meta.url` string. Both string spellings were measured on Node
189
+ * v22.22.2 and both are wrong:
190
+ *
191
+ * - `import.meta.resolve(specifier, parentURL)` — the parent argument is
192
+ * SILENTLY IGNORED without `--experimental-import-meta-resolve`. Measured:
193
+ * resolving `@objectstack/plugin-auth` against a `packages/types` parent
194
+ * returned `packages/cli/node_modules/...`, i.e. the caller's own answer,
195
+ * byte-identical to passing no parent at all. It would have compiled, run,
196
+ * and pinned green while ignoring the base — a phantom fix of exactly the
197
+ * kind this card is about.
198
+ * - `createRequire(parentURL).resolve(specifier)` — CJS resolution, which
199
+ * honours `NODE_PATH`. Measured against a store reachable only through
200
+ * `NODE_PATH`: the CJS resolve found it (with and without the `paths`
201
+ * option, since GLOBAL_FOLDERS are always appended) while the ESM bare
202
+ * `import()` did not. That is #4719's hole re-opened on the fallback path,
203
+ * and it would have falsified the "ESM does not honour NODE_PATH" sentence
204
+ * three lines above.
205
+ *
206
+ * A function written in the calling module is the only spelling that uses
207
+ * Node's real ESM resolver anchored where the caller actually lives: no flag,
208
+ * no `NODE_PATH`, no second resolution algorithm to drift from the first.
133
209
  *
134
210
  * @param hostRoot Directory holding the host app's `package.json` (default: the
135
211
  * process CWD, which is where the CLI reads `objectstack.config.ts` from too).
136
212
  * Note this used to take a pre-built `NodeRequire`; it needs the ROOT now,
137
213
  * because a `NodeRequire` cannot be asked where it was anchored and the manifest
138
214
  * has to be read from there.
215
+ * @param options {@link HostImporterOptions.fallbackImport} carries the caller's
216
+ * resolution base. Omitting it keeps the pre-#10943 behaviour (this package's
217
+ * own resolution) so no out-of-tree caller changes under its feet.
139
218
  */
140
- declare function createHostImporter(hostRoot?: string): HostImporter;
219
+ declare function createHostImporter(hostRoot?: string, options?: HostImporterOptions): HostImporter;
141
220
 
142
- export { HOST_DECLARATION_FIELDS, HOST_IMPORT_FAILURE_KIND, type HostDeclaration, type HostDeclarationField, type HostImportFailureKind, type HostImporter, createHostImporter, createHostRequire, hostImportFailureKind, isDeclaredByHost, packageNameFromSpecifier, readHostDeclaration };
221
+ export { type FallbackImport, HOST_DECLARATION_FIELDS, HOST_IMPORT_FAILURE_KIND, type HostDeclaration, type HostDeclarationField, type HostImportFailureKind, type HostImporter, type HostImporterOptions, createHostImporter, createHostRequire, hostImportFailureKind, isDeclaredByHost, packageNameFromSpecifier, readHostDeclaration };
package/dist/node.d.ts CHANGED
@@ -7,6 +7,32 @@
7
7
  * did.
8
8
  */
9
9
  type HostImporter = (pkg: string) => Promise<any>;
10
+ /**
11
+ * The importing package's OWN dynamic import — write it literally, in the
12
+ * calling module:
13
+ *
14
+ * createHostImporter(hostRoot, { fallbackImport: (s) => import(s) })
15
+ *
16
+ * `any` for the same reason {@link HostImporter} uses it: the module namespace
17
+ * belongs to a package this repo does not compile against.
18
+ */
19
+ type FallbackImport = (specifier: string) => Promise<any>;
20
+ /** Options for {@link createHostImporter}. */
21
+ interface HostImporterOptions {
22
+ /**
23
+ * The resolution base for everything the host app does NOT declare — supplied
24
+ * as the caller's own `import()` rather than as a URL string, because a
25
+ * string base was MEASURED to be unimplementable without a regression. See
26
+ * {@link createHostImporter}'s "why a function" note for both measurements.
27
+ *
28
+ * Omitted ⇒ the fallback resolves from `@objectstack/types`, which sees only
29
+ * `@objectstack/types`'s own dependencies. That default is retained so an
30
+ * out-of-tree caller cannot be broken by this parameter's arrival, and the
31
+ * `undeclared` failure text names it explicitly so the gap reports itself
32
+ * instead of being rediscovered.
33
+ */
34
+ fallbackImport?: FallbackImport;
35
+ }
10
36
  /**
11
37
  * A `require` anchored at the **host app's** `package.json` — i.e. the project
12
38
  * `objectstack serve` was invoked in, or the app `bootStack` is verifying, whose
@@ -95,10 +121,19 @@ declare function isDeclaredByHost(specifier: string, hostRoot?: string): boolean
95
121
  * in the app and install.
96
122
  * - `declared-unresolvable` — the app declares it and it still would not
97
123
  * resolve. Remedy: fix the INSTALL. Re-reading the manifest is wasted effort.
124
+ * - `declared-no-loadable-entry` (#14041) — the app declares it, the install
125
+ * delivered it, and the package's own `exports` names NO entry Node can load
126
+ * for the requested subpath — no `require`-condition target (which is why the
127
+ * CJS resolution refused) and no `import`-condition one for the fallback
128
+ * either (a `types`-only or `browser`-only publish, or a subpath the map
129
+ * never names). Remedy: change the PACKAGE — neither the app's manifest nor
130
+ * its install can ever fix this, which is exactly why it must not share the
131
+ * `declared-unresolvable` INSTALL wording.
98
132
  *
99
- * An evaluation crash is neither: it propagates untouched and carries no kind.
133
+ * An evaluation crash is none of these: it propagates untouched and carries no
134
+ * kind.
100
135
  */
101
- type HostImportFailureKind = 'undeclared' | 'declared-unresolvable';
136
+ type HostImportFailureKind = 'undeclared' | 'declared-unresolvable' | 'declared-no-loadable-entry';
102
137
  /**
103
138
  * Property carrying {@link HostImportFailureKind} on a thrown error.
104
139
  *
@@ -123,20 +158,64 @@ declare function hostImportFailureKind(err: unknown): HostImportFailureKind | un
123
158
  * the install is broken. It is not retried bare: falling back there would
124
159
  * reintroduce exactly the "some other package happens to supply it" accident
125
160
  * this gate closes, and would report an install problem as an absence.
126
- * 3. Undeclared falls back to the importing package's own resolution, which is
127
- * what keeps every framework-owned load working (`serve`'s plugin-auth /
128
- * service-i18n path, `bootStack`'s service plugins). Bare `import()` is ESM,
129
- * and ESM does not honour `NODE_PATH`, so the fallback cannot re-open the
130
- * hole either. Only when that fails as module-not-found does the undeclared
131
- * error surface; a package that RESOLVES and then throws while evaluating is
132
- * a genuine crash and propagates untouched, as before.
161
+ * 3. Undeclared falls back to the CALLER's own resolution, which is what keeps
162
+ * every framework-owned load working (`serve`'s plugin-auth / service-i18n
163
+ * path, `bootStack`'s service plugins). Bare `import()` is ESM, and ESM does
164
+ * not honour `NODE_PATH`, so the fallback cannot re-open the hole either.
165
+ * Only when that fails as module-not-found does the undeclared error
166
+ * surface; a package that RESOLVES and then throws while evaluating is a
167
+ * genuine crash and propagates untouched, as before.
168
+ *
169
+ * ── The caller supplies that base, and why it is a FUNCTION (#10943) ─────────
170
+ *
171
+ * Step 3 said "the importing package's own resolution" long before anything
172
+ * made it true. The fallback was a bare `import()` written HERE, and ESM
173
+ * resolves a bare specifier against the module containing the call — so it
174
+ * resolved from `@objectstack/types`, which under a pnpm-isolated layout can
175
+ * see only `@objectstack/types`'s own dependencies. Measured on `main` from an
176
+ * app declaring nothing, `@objectstack/plugin-auth`, `@objectstack/plugin-audit`
177
+ * and `chalk` all resolve from `packages/cli` and all failed through this
178
+ * helper; `@objectstack/spec` — the one dependency this package declares — was
179
+ * the only name that came back OK, which is the whole pattern. Under a hoisted
180
+ * npm/yarn layout the same fallback usually DOES find the caller's
181
+ * dependencies, so the claim was green in some installs and absent in others:
182
+ * the layout-dependence class cloud#1013 and #10645 exist to close, one level
183
+ * up. A declared contract the implementation does not keep is the thing this
184
+ * repo fixes at the producer (Prime Directive #12), so the mechanism moved
185
+ * rather than the sentence.
186
+ *
187
+ * The base arrives as the caller's own `import()` and NOT as a `parentURL` /
188
+ * `import.meta.url` string. Both string spellings were measured on Node
189
+ * v22.22.2 and both are wrong:
190
+ *
191
+ * - `import.meta.resolve(specifier, parentURL)` — the parent argument is
192
+ * SILENTLY IGNORED without `--experimental-import-meta-resolve`. Measured:
193
+ * resolving `@objectstack/plugin-auth` against a `packages/types` parent
194
+ * returned `packages/cli/node_modules/...`, i.e. the caller's own answer,
195
+ * byte-identical to passing no parent at all. It would have compiled, run,
196
+ * and pinned green while ignoring the base — a phantom fix of exactly the
197
+ * kind this card is about.
198
+ * - `createRequire(parentURL).resolve(specifier)` — CJS resolution, which
199
+ * honours `NODE_PATH`. Measured against a store reachable only through
200
+ * `NODE_PATH`: the CJS resolve found it (with and without the `paths`
201
+ * option, since GLOBAL_FOLDERS are always appended) while the ESM bare
202
+ * `import()` did not. That is #4719's hole re-opened on the fallback path,
203
+ * and it would have falsified the "ESM does not honour NODE_PATH" sentence
204
+ * three lines above.
205
+ *
206
+ * A function written in the calling module is the only spelling that uses
207
+ * Node's real ESM resolver anchored where the caller actually lives: no flag,
208
+ * no `NODE_PATH`, no second resolution algorithm to drift from the first.
133
209
  *
134
210
  * @param hostRoot Directory holding the host app's `package.json` (default: the
135
211
  * process CWD, which is where the CLI reads `objectstack.config.ts` from too).
136
212
  * Note this used to take a pre-built `NodeRequire`; it needs the ROOT now,
137
213
  * because a `NodeRequire` cannot be asked where it was anchored and the manifest
138
214
  * has to be read from there.
215
+ * @param options {@link HostImporterOptions.fallbackImport} carries the caller's
216
+ * resolution base. Omitting it keeps the pre-#10943 behaviour (this package's
217
+ * own resolution) so no out-of-tree caller changes under its feet.
139
218
  */
140
- declare function createHostImporter(hostRoot?: string): HostImporter;
219
+ declare function createHostImporter(hostRoot?: string, options?: HostImporterOptions): HostImporter;
141
220
 
142
- export { HOST_DECLARATION_FIELDS, HOST_IMPORT_FAILURE_KIND, type HostDeclaration, type HostDeclarationField, type HostImportFailureKind, type HostImporter, createHostImporter, createHostRequire, hostImportFailureKind, isDeclaredByHost, packageNameFromSpecifier, readHostDeclaration };
221
+ export { type FallbackImport, HOST_DECLARATION_FIELDS, HOST_IMPORT_FAILURE_KIND, type HostDeclaration, type HostDeclarationField, type HostImportFailureKind, type HostImporter, type HostImporterOptions, createHostImporter, createHostRequire, hostImportFailureKind, isDeclaredByHost, packageNameFromSpecifier, readHostDeclaration };
package/dist/node.js CHANGED
@@ -87,7 +87,7 @@ function isDeclaredByHost(specifier, hostRoot) {
87
87
  var HOST_IMPORT_FAILURE_KIND = "objectstackHostImportFailureKind";
88
88
  function hostImportFailureKind(err) {
89
89
  const kind = err?.[HOST_IMPORT_FAILURE_KIND];
90
- return kind === "undeclared" || kind === "declared-unresolvable" ? kind : void 0;
90
+ return kind === "undeclared" || kind === "declared-unresolvable" || kind === "declared-no-loadable-entry" ? kind : void 0;
91
91
  }
92
92
  function hostImportError(kind, message, cause) {
93
93
  const err = new Error(message);
@@ -97,9 +97,10 @@ function hostImportError(kind, message, cause) {
97
97
  [HOST_IMPORT_FAILURE_KIND]: kind
98
98
  });
99
99
  }
100
- function undeclaredMessage(declaration, cause) {
100
+ function undeclaredMessage(declaration, cause, callerBaseSupplied) {
101
101
  const { packageName, hostRoot, manifestMissing } = declaration;
102
102
  const detail = cause instanceof Error ? cause.message : String(cause);
103
+ const baseNote = callerBaseSupplied ? "" : "\n (the caller did not pass `fallbackImport`, so that fallback resolved from\n @objectstack/types, which can see only its own dependencies \u2014 a caller that\n needs its own resolution passes `{ fallbackImport: (s) => import(s) }`, #10943)";
103
104
  return `Cannot find package '${packageName}': the host app does not declare it.
104
105
  host app: ${hostRoot}
105
106
  ` + (manifestMissing ? " no readable package.json was found there \u2014 nothing can be declared\n" : ` checked: ${HOST_DECLARATION_FIELDS.join(", ")}
@@ -112,7 +113,7 @@ function undeclaredMessage(declaration, cause) {
112
113
  at in every pnpm bin shim \u2014 used to resolve here regardless of the app's
113
114
  package.json, so the same app booted or refused depending on how the
114
115
  process was launched. The declaration is the contract.
115
- (fallback resolution also failed: ${detail})`;
116
+ (fallback resolution also failed: ${detail})${baseNote}`;
116
117
  }
117
118
  function unresolvableMessage(declaration, cause) {
118
119
  const { packageName, hostRoot, field, specifier } = declaration;
@@ -128,8 +129,192 @@ function unresolvableMessage(declaration, cause) {
128
129
  \u2022 it IS installed but its "main"/"exports" points at a dist that was never built
129
130
  (resolver: ${detail})`;
130
131
  }
131
- function createHostImporter(hostRoot = process.cwd()) {
132
+ var ESM_IMPORT_CONDITIONS = /* @__PURE__ */ new Set([
133
+ "node-addons",
134
+ "node",
135
+ "import",
136
+ "default"
137
+ ]);
138
+ var CJS_REQUIRE_CONDITIONS = /* @__PURE__ */ new Set([
139
+ "node-addons",
140
+ "node",
141
+ "require",
142
+ "default"
143
+ ]);
144
+ function selectConditionTarget(node, conditions) {
145
+ if (typeof node === "string") return node;
146
+ if (Array.isArray(node)) {
147
+ for (const alternative of node) {
148
+ const hit = selectConditionTarget(alternative, conditions);
149
+ if (hit !== void 0) return hit;
150
+ }
151
+ return void 0;
152
+ }
153
+ if (node === null || typeof node !== "object") return void 0;
154
+ for (const entry of Object.entries(node)) {
155
+ if (!conditions.has(entry[0])) continue;
156
+ const hit = selectConditionTarget(entry[1], conditions);
157
+ if (hit !== void 0) return hit;
158
+ }
159
+ return void 0;
160
+ }
161
+ function resolveExportsSubpath(exportsField, subpath, conditions = ESM_IMPORT_CONDITIONS) {
162
+ if (exportsField === void 0) return void 0;
163
+ const keys = typeof exportsField === "object" && exportsField !== null && !Array.isArray(exportsField) ? Object.keys(exportsField) : void 0;
164
+ const isSubpathMap = keys !== void 0 && keys.length > 0 && keys.every((key) => key === "." || key.indexOf("./") === 0);
165
+ if (!isSubpathMap) {
166
+ return subpath === "." ? selectConditionTarget(exportsField, conditions) : void 0;
167
+ }
168
+ const map = exportsField;
169
+ if (Object.prototype.hasOwnProperty.call(map, subpath)) {
170
+ return selectConditionTarget(map[subpath], conditions);
171
+ }
172
+ let best;
173
+ for (const entry of Object.entries(map)) {
174
+ const star = entry[0].indexOf("*");
175
+ if (star < 0 || entry[0].indexOf("*", star + 1) >= 0) continue;
176
+ const prefix = entry[0].slice(0, star);
177
+ const suffix = entry[0].slice(star + 1);
178
+ if (subpath.indexOf(prefix) !== 0) continue;
179
+ if (suffix !== "" && subpath.slice(subpath.length - suffix.length) !== suffix) continue;
180
+ if (subpath.length < prefix.length + suffix.length) continue;
181
+ if (best !== void 0 && (best.prefix.length > prefix.length || best.prefix.length === prefix.length && best.suffix.length >= suffix.length)) {
182
+ continue;
183
+ }
184
+ best = { prefix, suffix, target: entry[1] };
185
+ }
186
+ if (best === void 0) return void 0;
187
+ const matched = subpath.slice(best.prefix.length, subpath.length - best.suffix.length);
188
+ const target = selectConditionTarget(best.target, conditions);
189
+ return target === void 0 ? void 0 : target.split("*").join(matched);
190
+ }
191
+ function exportsSubpathOf(specifier, packageName) {
192
+ return specifier === packageName ? "." : `.${specifier.slice(packageName.length)}`;
193
+ }
194
+ function packageRootOf(resolvedFile, packageName) {
195
+ let dir = (0, import_node_path.dirname)(resolvedFile);
196
+ for (let hop = 0; hop < 64; hop += 1) {
197
+ try {
198
+ const manifest = JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path.join)(dir, "package.json"), "utf8"));
199
+ if (manifest.name === packageName) return dir;
200
+ } catch {
201
+ }
202
+ const parent = (0, import_node_path.dirname)(dir);
203
+ if (parent === dir) return void 0;
204
+ dir = parent;
205
+ }
206
+ return void 0;
207
+ }
208
+ function esmEntryForDeclared(specifier, packageName, cjsResolved) {
209
+ const root = packageRootOf(cjsResolved, packageName);
210
+ if (root === void 0) return void 0;
211
+ let exportsField;
212
+ try {
213
+ exportsField = JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path.join)(root, "package.json"), "utf8")).exports;
214
+ } catch {
215
+ return void 0;
216
+ }
217
+ if (exportsField === void 0 || exportsField === null) return void 0;
218
+ const subpath = exportsSubpathOf(specifier, packageName);
219
+ const target = resolveExportsSubpath(exportsField, subpath);
220
+ if (typeof target !== "string" || target.indexOf("./") !== 0) return void 0;
221
+ const entry = (0, import_node_path.resolve)(root, target);
222
+ if (entry.indexOf(root + import_node_path.sep) !== 0) return void 0;
223
+ return (0, import_node_fs.existsSync)(entry) ? entry : void 0;
224
+ }
225
+ function hasInvalidExportsSubpathSegments(subpath) {
226
+ if (subpath === ".") return false;
227
+ return subpath.slice(2).split(/[/\\]/).some((raw) => {
228
+ const segment = raw.toLowerCase();
229
+ return segment === "" || segment === "." || segment === ".." || segment === "node_modules";
230
+ });
231
+ }
232
+ var ALIAS_DECLARATION_PROTOCOLS = [
233
+ { prefix: "npm:", rangeRequired: false },
234
+ { prefix: "workspace:", rangeRequired: true }
235
+ ];
236
+ function declaredManifestName(declaration) {
237
+ const { packageName, specifier } = declaration;
238
+ if (specifier === void 0) return packageName;
239
+ const protocol = ALIAS_DECLARATION_PROTOCOLS.find((p) => specifier.indexOf(p.prefix) === 0);
240
+ if (protocol === void 0) return packageName;
241
+ const value = specifier.slice(protocol.prefix.length);
242
+ const at = value.lastIndexOf("@");
243
+ if (at <= 0 && protocol.rangeRequired) return packageName;
244
+ const name = at > 0 ? value.slice(0, at) : value;
245
+ return packageNameFromSpecifier(name) === name ? name : packageName;
246
+ }
247
+ function hostInstalledPackageDir(declaration) {
248
+ const { packageName, hostRoot } = declaration;
249
+ const linked = (0, import_node_path.join)(hostRoot, "node_modules", ...packageName.split("/"));
250
+ try {
251
+ const manifest = JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path.join)(linked, "package.json"), "utf8"));
252
+ if (manifest.name !== declaredManifestName(declaration)) return void 0;
253
+ } catch {
254
+ return void 0;
255
+ }
256
+ try {
257
+ return (0, import_node_fs.realpathSync)(linked);
258
+ } catch {
259
+ return linked;
260
+ }
261
+ }
262
+ function declaredCjsResolveFallback(specifier, declaration) {
263
+ const { packageName } = declaration;
264
+ const packageDir = hostInstalledPackageDir(declaration);
265
+ if (packageDir === void 0) return { outcome: "absent" };
266
+ let exportsField;
267
+ try {
268
+ exportsField = JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path.join)(packageDir, "package.json"), "utf8")).exports;
269
+ } catch {
270
+ return { outcome: "absent" };
271
+ }
272
+ if (exportsField === void 0 || exportsField === null) return { outcome: "install-broken" };
273
+ const subpath = exportsSubpathOf(specifier, packageName);
274
+ if (hasInvalidExportsSubpathSegments(subpath)) return { outcome: "invalid-specifier" };
275
+ const importTarget = resolveExportsSubpath(exportsField, subpath, ESM_IMPORT_CONDITIONS);
276
+ if (typeof importTarget === "string" && importTarget.indexOf("./") === 0) {
277
+ const entry = (0, import_node_path.resolve)(packageDir, importTarget);
278
+ if (entry.indexOf(packageDir + import_node_path.sep) === 0 && (0, import_node_fs.existsSync)(entry)) {
279
+ return { outcome: "entry", entry };
280
+ }
281
+ return { outcome: "install-broken" };
282
+ }
283
+ const requireTarget = resolveExportsSubpath(exportsField, subpath, CJS_REQUIRE_CONDITIONS);
284
+ if (typeof requireTarget === "string" && requireTarget.indexOf("./") === 0) {
285
+ return { outcome: "install-broken" };
286
+ }
287
+ return { outcome: "no-loadable-entry", packageDir };
288
+ }
289
+ function noLoadableEntryMessage(declaration, packageDir, subpath, cause) {
290
+ const { packageName, hostRoot, field, specifier } = declaration;
291
+ const detail = cause instanceof Error ? cause.message : String(cause);
292
+ const subpathNote = subpath === "." ? 'its main entry (".")' : `the subpath '${subpath}'`;
293
+ return `Cannot load module '${packageName}': the host app DECLARES it (${field}: ${JSON.stringify(specifier)}) and it IS installed, but the package publishes no entry that Node can load.
294
+ host app: ${hostRoot}
295
+ installed at: ${packageDir}
296
+
297
+ This is a problem with the PACKAGE's own published shape, not with the app or
298
+ its install \u2014 the declaration is right and the package is on disk, so neither
299
+ re-reading package.json nor re-running \`pnpm install\` can change anything.
300
+ Measured from its manifest:
301
+ \u2022 its "exports" map names no \`require\`-condition entry for ${subpathNote},
302
+ so a CommonJS resolution cannot see it at all
303
+ \u2022 and no \`import\`-condition entry either, so there is nothing for the ESM
304
+ fallback to load
305
+ The remedy lives in the package: it must publish a runtime entry for this
306
+ subpath (an \`import\` condition suffices here; a dual build adds \`require\`).
307
+ A publish carrying only \`types\` / \`browser\`-style conditions cannot be loaded
308
+ by a Node host at all.
309
+ (resolver: ${detail})`;
310
+ }
311
+ function createHostImporter(hostRoot = process.cwd(), options = {}) {
132
312
  const hostRequire = createHostRequire(hostRoot);
313
+ const { fallbackImport } = options;
314
+ const importAsCaller = fallbackImport ?? ((specifier) => import(
315
+ /* webpackIgnore: true */
316
+ specifier
317
+ ));
133
318
  return async (pkg) => {
134
319
  if (packageNameFromSpecifier(pkg) === void 0) {
135
320
  return import(
@@ -143,22 +328,40 @@ function createHostImporter(hostRoot = process.cwd()) {
143
328
  try {
144
329
  resolved = hostRequire.resolve(pkg);
145
330
  } catch (cause) {
331
+ const fallback = declaredCjsResolveFallback(pkg, declaration);
332
+ if (fallback.outcome === "entry") {
333
+ return import((0, import_node_url.pathToFileURL)(fallback.entry).href);
334
+ }
335
+ if (fallback.outcome === "no-loadable-entry") {
336
+ throw hostImportError(
337
+ "declared-no-loadable-entry",
338
+ noLoadableEntryMessage(
339
+ declaration,
340
+ fallback.packageDir,
341
+ exportsSubpathOf(pkg, declaration.packageName),
342
+ cause
343
+ ),
344
+ cause
345
+ );
346
+ }
146
347
  throw hostImportError(
147
348
  "declared-unresolvable",
148
349
  unresolvableMessage(declaration, cause),
149
350
  cause
150
351
  );
151
352
  }
152
- return import((0, import_node_url.pathToFileURL)(resolved).href);
353
+ const entry = esmEntryForDeclared(pkg, declaration.packageName, resolved) ?? resolved;
354
+ return import((0, import_node_url.pathToFileURL)(entry).href);
153
355
  }
154
356
  try {
155
- return await import(
156
- /* webpackIgnore: true */
157
- pkg
158
- );
357
+ return await importAsCaller(pkg);
159
358
  } catch (cause) {
160
359
  if (!isModuleNotFoundError(cause)) throw cause;
161
- throw hostImportError("undeclared", undeclaredMessage(declaration, cause), cause);
360
+ throw hostImportError(
361
+ "undeclared",
362
+ undeclaredMessage(declaration, cause, fallbackImport !== void 0),
363
+ cause
364
+ );
162
365
  }
163
366
  };
164
367
  }