relaxnative 0.1.0-beta.1

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.
Files changed (130) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +592 -0
  3. package/dist/chunk-22W76CYR.js +607 -0
  4. package/dist/chunk-24NXCU65.js +254 -0
  5. package/dist/chunk-2APMRURB.js +65 -0
  6. package/dist/chunk-2CHBHJPT.js +607 -0
  7. package/dist/chunk-2I4JHZI7.js +287 -0
  8. package/dist/chunk-2JOHYYQO.js +607 -0
  9. package/dist/chunk-3GW77EWF.js +505 -0
  10. package/dist/chunk-5J5CAKCD.js +266 -0
  11. package/dist/chunk-5NTDZ7YZ.js +377 -0
  12. package/dist/chunk-5TA6MROS.js +529 -0
  13. package/dist/chunk-5WVEBKMJ.js +1019 -0
  14. package/dist/chunk-6O5TIEEI.js +545 -0
  15. package/dist/chunk-6XU5DETO.js +896 -0
  16. package/dist/chunk-7BIZ6P3B.js +176 -0
  17. package/dist/chunk-7DKO777J.js +285 -0
  18. package/dist/chunk-7JYWUH4Y.js +268 -0
  19. package/dist/chunk-7NMCEP2V.js +756 -0
  20. package/dist/chunk-A7N4YBP2.js +379 -0
  21. package/dist/chunk-AZTCDV6R.js +572 -0
  22. package/dist/chunk-B34XEGM6.js +559 -0
  23. package/dist/chunk-BFHBLVXW.js +607 -0
  24. package/dist/chunk-BLOJ33LO.js +65 -0
  25. package/dist/chunk-BYPXCWTI.js +375 -0
  26. package/dist/chunk-C4KJD2AN.js +1044 -0
  27. package/dist/chunk-CJALJTRQ.js +814 -0
  28. package/dist/chunk-D4PK367Z.js +627 -0
  29. package/dist/chunk-DCWBZPEV.js +287 -0
  30. package/dist/chunk-DI7KSUEC.js +676 -0
  31. package/dist/chunk-DQ2KXIOO.js +665 -0
  32. package/dist/chunk-DV5STE3W.js +986 -0
  33. package/dist/chunk-EG5KNEKP.js +1027 -0
  34. package/dist/chunk-EOA2OWFA.js +1020 -0
  35. package/dist/chunk-ES3B6EZJ.js +56 -0
  36. package/dist/chunk-ETIXNPU5.js +741 -0
  37. package/dist/chunk-EUZBU2H7.js +824 -0
  38. package/dist/chunk-F6V7XDEB.js +150 -0
  39. package/dist/chunk-FNJKUFNF.js +1019 -0
  40. package/dist/chunk-FZB37DWL.js +453 -0
  41. package/dist/chunk-G3NDHZNZ.js +453 -0
  42. package/dist/chunk-G4YR34LE.js +410 -0
  43. package/dist/chunk-GU4XXISM.js +264 -0
  44. package/dist/chunk-GVPSQXGJ.js +1027 -0
  45. package/dist/chunk-HD5C4RNU.js +676 -0
  46. package/dist/chunk-HDIVY47T.js +287 -0
  47. package/dist/chunk-HFLRTDNK.js +985 -0
  48. package/dist/chunk-HGWRCVQ5.js +287 -0
  49. package/dist/chunk-HRG3SVKK.js +995 -0
  50. package/dist/chunk-HUGFULJ3.js +1027 -0
  51. package/dist/chunk-IDYSBXYS.js +344 -0
  52. package/dist/chunk-ISDDUQVI.js +1019 -0
  53. package/dist/chunk-IZ632ZCJ.js +286 -0
  54. package/dist/chunk-J5XI4L52.js +218 -0
  55. package/dist/chunk-JTIO7BUH.js +582 -0
  56. package/dist/chunk-JTWSFMF2.js +1020 -0
  57. package/dist/chunk-K5TV62T4.js +736 -0
  58. package/dist/chunk-K7MTG53V.js +985 -0
  59. package/dist/chunk-KGLZB3H2.js +676 -0
  60. package/dist/chunk-KYAB35P5.js +741 -0
  61. package/dist/chunk-KYDW3YVX.js +453 -0
  62. package/dist/chunk-L3MEMPRH.js +361 -0
  63. package/dist/chunk-LFTO3Z7N.js +757 -0
  64. package/dist/chunk-LLZ4I4OR.js +405 -0
  65. package/dist/chunk-LMRUM4U4.js +207 -0
  66. package/dist/chunk-LT5OGU6T.js +559 -0
  67. package/dist/chunk-LZQQOC3M.js +741 -0
  68. package/dist/chunk-LZYUNF6Q.js +1017 -0
  69. package/dist/chunk-MCTPVW4G.js +453 -0
  70. package/dist/chunk-MGLWXBIB.js +65 -0
  71. package/dist/chunk-MLKGABMK.js +9 -0
  72. package/dist/chunk-MTE6XDGC.js +541 -0
  73. package/dist/chunk-NDJUNDAE.js +676 -0
  74. package/dist/chunk-NG7SNFUD.js +1027 -0
  75. package/dist/chunk-ONVWKYK7.js +739 -0
  76. package/dist/chunk-OVMTFGE7.js +1042 -0
  77. package/dist/chunk-P5RQPRJ4.js +741 -0
  78. package/dist/chunk-PFABSW6Y.js +401 -0
  79. package/dist/chunk-PVVUJA2M.js +65 -0
  80. package/dist/chunk-Q3CDTGTX.js +676 -0
  81. package/dist/chunk-QKAKWDOQ.js +967 -0
  82. package/dist/chunk-QMPRDU6I.js +598 -0
  83. package/dist/chunk-R5U7XKVJ.js +16 -0
  84. package/dist/chunk-RB6RHB6C.js +254 -0
  85. package/dist/chunk-RD2K7ODW.js +175 -0
  86. package/dist/chunk-RHBHJND2.js +582 -0
  87. package/dist/chunk-RUP6POSE.js +453 -0
  88. package/dist/chunk-RYNSM23L.js +582 -0
  89. package/dist/chunk-S72S7XXS.js +255 -0
  90. package/dist/chunk-SDV5AAPW.js +213 -0
  91. package/dist/chunk-STXQPXUY.js +242 -0
  92. package/dist/chunk-TUSF5AEG.js +140 -0
  93. package/dist/chunk-V74SNTE6.js +736 -0
  94. package/dist/chunk-VHM5XETC.js +453 -0
  95. package/dist/chunk-VKQIXLNL.js +273 -0
  96. package/dist/chunk-VPG23Z7Y.js +545 -0
  97. package/dist/chunk-VSP234PR.js +627 -0
  98. package/dist/chunk-WH4JPUWF.js +598 -0
  99. package/dist/chunk-WLAUJL3K.js +409 -0
  100. package/dist/chunk-WXCN2QJ7.js +350 -0
  101. package/dist/chunk-X3JZKLJC.js +896 -0
  102. package/dist/chunk-X7SAP7FC.js +582 -0
  103. package/dist/chunk-XEH6PRYE.js +968 -0
  104. package/dist/chunk-XI65CAQV.js +211 -0
  105. package/dist/chunk-Y7OSHR6W.js +235 -0
  106. package/dist/chunk-YN4WUMVD.js +1020 -0
  107. package/dist/chunk-YUWJ2C4Y.js +1020 -0
  108. package/dist/chunk-YXLBPWNU.js +263 -0
  109. package/dist/chunk-YYJJHO7R.js +407 -0
  110. package/dist/chunk-Z2RBHUIH.js +757 -0
  111. package/dist/chunk-Z6G3KIOM.js +1027 -0
  112. package/dist/chunk-ZPPXCDSH.js +361 -0
  113. package/dist/chunk-ZRTY24SZ.js +582 -0
  114. package/dist/chunk-ZSDFBCQG.js +741 -0
  115. package/dist/cli.d.ts +1 -0
  116. package/dist/cli.js +339 -0
  117. package/dist/esmLoader.d.ts +10 -0
  118. package/dist/esmLoader.js +112 -0
  119. package/dist/index.d.ts +407 -0
  120. package/dist/index.js +126 -0
  121. package/dist/memory/memory.selftest.d.ts +2 -0
  122. package/dist/memory/memory.selftest.js +25 -0
  123. package/dist/worker/processEntry.d.ts +2 -0
  124. package/dist/worker/processEntry.js +160 -0
  125. package/dist/worker/workerEntry.d.ts +2 -0
  126. package/dist/worker/workerEntry.js +27 -0
  127. package/native/examples/add.c +6 -0
  128. package/native/examples/add_test.c +23 -0
  129. package/native/relaxnative_test.h +36 -0
  130. package/package.json +81 -0
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/cli.js ADDED
@@ -0,0 +1,339 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ benchmarkCompareSyncVsWorker,
4
+ benchmarkCompareTraditionalVsRelaxnative,
5
+ formatBenchmarkCompare,
6
+ formatBenchmarkTraditionalCompare,
7
+ formatNativeTestResults,
8
+ installPackageEnforcingTrust,
9
+ listPackages,
10
+ removePackage,
11
+ runNativeTests,
12
+ updateIndex
13
+ } from "./chunk-OVMTFGE7.js";
14
+ import {
15
+ detectCompilers,
16
+ getCacheRoot
17
+ } from "./chunk-LFTO3Z7N.js";
18
+ import "./chunk-HDIVY47T.js";
19
+ import "./chunk-LLZ4I4OR.js";
20
+ import "./chunk-MLKGABMK.js";
21
+
22
+ // src/cli.ts
23
+ import { existsSync, readdirSync, rmSync, statSync } from "fs";
24
+ import { join } from "path";
25
+ import { readFileSync } from "fs";
26
+ function isTraceEnabled() {
27
+ return process.env.RELAXNATIVE_TRACE === "1";
28
+ }
29
+ function trace(...args) {
30
+ if (!isTraceEnabled()) return;
31
+ console.log("[relaxnative:trace]", ...args);
32
+ }
33
+ function getFlagValue(argv, name) {
34
+ const idx = argv.indexOf(name);
35
+ if (idx === -1) return void 0;
36
+ return argv[idx + 1];
37
+ }
38
+ function hasFlag(argv, name) {
39
+ return argv.includes(name);
40
+ }
41
+ function usage() {
42
+ console.log(`relaxnative
43
+
44
+ Usage:
45
+ relaxnative add <package>
46
+ relaxnative list
47
+ relaxnative remove <package>
48
+ relaxnative test <nativeDir>
49
+ relaxnative bench <nativeFile> <fnName>
50
+ relaxnative doctor
51
+ relaxnative cache status
52
+ relaxnative cache clean
53
+
54
+ Examples:
55
+ npx relaxnative test native/
56
+ npx relaxnative bench native/examples/add.c add
57
+ npx relaxnative bench native/examples/add.c add --traditional
58
+ npx relaxnative doctor
59
+ npx relaxnative cache status
60
+
61
+ Notes:
62
+ - For deterministic offline installs, use: file:<path>
63
+ - Example: relaxnative add file:examples/registry/fast-matrix
64
+ - Trust levels: local (no prompts), community (warning + confirmation), verified (silent install, requires signature)
65
+ - Add: pass --yes to make the command non-interactive (community installs will fail unless previously trusted)
66
+ - Bench: pass --traditional to include a JS baseline when available (built-in for add)
67
+ `);
68
+ }
69
+ function fmtOk(msg) {
70
+ return `\u2713 ${msg}`;
71
+ }
72
+ function fmtFail(msg) {
73
+ return `\u2717 ${msg}`;
74
+ }
75
+ function folderSizeBytes(dir) {
76
+ let bytes = 0;
77
+ let newest = 0;
78
+ if (!existsSync(dir)) return { bytes: 0, newestMtimeMs: 0 };
79
+ for (const ent of readdirSync(dir, { withFileTypes: true })) {
80
+ const p = join(dir, ent.name);
81
+ try {
82
+ const st = statSync(p);
83
+ newest = Math.max(newest, st.mtimeMs);
84
+ if (ent.isDirectory()) {
85
+ const sub = folderSizeBytes(p);
86
+ bytes += sub.bytes;
87
+ newest = Math.max(newest, sub.newestMtimeMs);
88
+ } else if (ent.isFile()) {
89
+ bytes += st.size;
90
+ }
91
+ } catch {
92
+ }
93
+ }
94
+ return { bytes, newestMtimeMs: newest };
95
+ }
96
+ function humanBytes(bytes) {
97
+ const u = ["B", "KB", "MB", "GB"];
98
+ let b = bytes;
99
+ let i = 0;
100
+ while (b >= 1024 && i < u.length - 1) {
101
+ b /= 1024;
102
+ i++;
103
+ }
104
+ return `${b.toFixed(i === 0 ? 0 : 1)} ${u[i]}`;
105
+ }
106
+ async function main() {
107
+ process.on("uncaughtException", (err) => {
108
+ console.error("[relaxnative] uncaughtException", err);
109
+ });
110
+ process.on("unhandledRejection", (err) => {
111
+ console.error("[relaxnative] unhandledRejection", err);
112
+ });
113
+ const [, , cmd, arg] = process.argv;
114
+ if (!cmd || cmd === "-h" || cmd === "--help") {
115
+ usage();
116
+ process.exit(0);
117
+ }
118
+ if (cmd === "add") {
119
+ if (!arg) {
120
+ console.error("Missing package specifier");
121
+ usage();
122
+ process.exit(1);
123
+ }
124
+ const yes = hasFlag(process.argv, "--yes");
125
+ const res = await installPackageEnforcingTrust(arg, process.cwd(), { yes });
126
+ updateIndex(process.cwd());
127
+ console.log(`Installed ${res.pkg} (${res.trust}) at ${res.dir}`);
128
+ if (res.warnings.length) {
129
+ console.warn("\nStatic scan warnings:");
130
+ for (const w of res.warnings) {
131
+ console.warn(` - ${w}`);
132
+ }
133
+ }
134
+ process.exit(0);
135
+ }
136
+ if (cmd === "list") {
137
+ const pkgs = listPackages(process.cwd());
138
+ for (const p of pkgs) console.log(p);
139
+ process.exit(0);
140
+ }
141
+ if (cmd === "remove") {
142
+ if (!arg) {
143
+ console.error("Missing package name");
144
+ usage();
145
+ process.exit(1);
146
+ }
147
+ removePackage(arg, process.cwd());
148
+ updateIndex(process.cwd());
149
+ console.log(`Removed ${arg}`);
150
+ process.exit(0);
151
+ }
152
+ if (cmd === "test") {
153
+ if (!arg) {
154
+ console.error("Missing native directory (ex: native/)");
155
+ usage();
156
+ process.exit(1);
157
+ }
158
+ const isolation = getFlagValue(process.argv, "--isolation") ?? "in-process";
159
+ if (!["in-process", "worker", "process"].includes(isolation)) {
160
+ console.error("Invalid --isolation (expected: in-process|worker|process)");
161
+ process.exit(1);
162
+ }
163
+ const { results, exitCode } = await runNativeTests(arg, { isolation });
164
+ if (!results.length) {
165
+ console.log("No native tests found");
166
+ process.exit(0);
167
+ }
168
+ console.log(formatNativeTestResults(results));
169
+ process.exit(exitCode);
170
+ }
171
+ if (cmd === "bench") {
172
+ const nativeFile = arg;
173
+ const fnName = process.argv[4];
174
+ if (!nativeFile || !fnName) {
175
+ console.error("Usage: relaxnative bench <nativeFile> <fnName>");
176
+ usage();
177
+ process.exit(1);
178
+ }
179
+ const iterationsRaw = getFlagValue(process.argv, "--iterations");
180
+ const warmupRaw = getFlagValue(process.argv, "--warmup");
181
+ const argsRaw = getFlagValue(process.argv, "--args");
182
+ const json = hasFlag(process.argv, "--json");
183
+ const traditional = hasFlag(process.argv, "--traditional");
184
+ const confirm = hasFlag(process.argv, "--confirm");
185
+ trace("bench start", {
186
+ nativeFile,
187
+ fnName,
188
+ traditional,
189
+ iterationsRaw,
190
+ warmupRaw
191
+ });
192
+ let args;
193
+ if (argsRaw != null) {
194
+ try {
195
+ const parsed = JSON.parse(argsRaw);
196
+ if (!Array.isArray(parsed)) {
197
+ console.error('--args must be a JSON array (example: --args "[1,2]")');
198
+ process.exit(1);
199
+ }
200
+ args = parsed;
201
+ } catch {
202
+ console.error('Invalid JSON for --args (example: --args "[1,2]")');
203
+ process.exit(1);
204
+ }
205
+ }
206
+ const iterations = iterationsRaw ? Number(iterationsRaw) : 5e4;
207
+ const warmup = warmupRaw ? Number(warmupRaw) : 2e3;
208
+ trace("bench parsed flags", { iterations, warmup, hasArgs: Array.isArray(args) });
209
+ if (!Number.isFinite(iterations) || iterations <= 0) {
210
+ console.error("Invalid --iterations");
211
+ process.exit(1);
212
+ }
213
+ if (!Number.isFinite(warmup) || warmup < 0) {
214
+ console.error("Invalid --warmup");
215
+ process.exit(1);
216
+ }
217
+ const expensive = iterations > 25e4;
218
+ if (expensive && !confirm) {
219
+ console.error("Refusing to run: iterations > 250000. Re-run with --confirm to proceed.");
220
+ process.exit(2);
221
+ }
222
+ if (traditional) {
223
+ try {
224
+ trace("bench traditional: begin");
225
+ const res = await benchmarkCompareTraditionalVsRelaxnative(nativeFile, fnName, {
226
+ iterations,
227
+ warmup,
228
+ args
229
+ });
230
+ trace("bench traditional: done");
231
+ if (json) console.log(JSON.stringify(res, null, 2));
232
+ else console.log(formatBenchmarkTraditionalCompare(res));
233
+ } catch (e) {
234
+ console.error("[relaxnative] bench failed", {
235
+ nativeFile,
236
+ fnName,
237
+ traditional: true,
238
+ error: e?.message ?? String(e)
239
+ });
240
+ if (isTraceEnabled()) {
241
+ console.error(e);
242
+ }
243
+ process.exit(1);
244
+ }
245
+ } else {
246
+ try {
247
+ trace("bench compare: begin");
248
+ const res = await benchmarkCompareSyncVsWorker(nativeFile, fnName, { iterations, warmup, args });
249
+ trace("bench compare: done");
250
+ if (json) console.log(JSON.stringify(res, null, 2));
251
+ else console.log(formatBenchmarkCompare(res));
252
+ } catch (e) {
253
+ console.error("[relaxnative] bench failed", {
254
+ nativeFile,
255
+ fnName,
256
+ traditional: false,
257
+ error: e?.message ?? String(e)
258
+ });
259
+ if (isTraceEnabled()) {
260
+ console.error(e);
261
+ }
262
+ process.exit(1);
263
+ }
264
+ }
265
+ process.exit(0);
266
+ }
267
+ if (cmd === "doctor") {
268
+ const lines = [];
269
+ try {
270
+ const { c, rust } = detectCompilers();
271
+ if (c) lines.push(fmtOk(`C compiler detected (${c.vendor} ${c.version})`));
272
+ else lines.push(fmtFail("C compiler missing (install clang or gcc)"));
273
+ if (rust) lines.push(fmtOk(`Rust compiler detected (${rust.version})`));
274
+ else lines.push(fmtFail("Rust compiler missing (install rustc + cargo)"));
275
+ } catch (e) {
276
+ lines.push(fmtFail(`Compiler detection failed: ${e?.message ?? String(e)}`));
277
+ }
278
+ try {
279
+ await import("worker_threads");
280
+ lines.push(fmtOk("Worker threads supported"));
281
+ } catch {
282
+ lines.push(fmtFail("Worker threads not supported in this Node build"));
283
+ }
284
+ const root = getCacheRoot();
285
+ try {
286
+ const st = existsSync(root) ? statSync(root) : null;
287
+ if (!st) lines.push(fmtOk(`Cache directory will be created at ${root}`));
288
+ else if (st.isDirectory()) lines.push(fmtOk(`Cache directory OK (${root})`));
289
+ else lines.push(fmtFail(`Cache path is not a directory: ${root}`));
290
+ } catch (e) {
291
+ lines.push(fmtFail(`Cache directory not accessible: ${e?.message ?? String(e)}`));
292
+ }
293
+ lines.push(fmtOk("ESM loader supported (Node >= 18)"));
294
+ console.log(lines.join("\n"));
295
+ process.exit(lines.some((l) => l.startsWith("\u2717")) ? 1 : 0);
296
+ }
297
+ if (cmd === "cache") {
298
+ const sub = arg;
299
+ const root = getCacheRoot();
300
+ if (!sub || !["status", "clean"].includes(sub)) {
301
+ console.error("Usage: relaxnative cache <status|clean>");
302
+ process.exit(1);
303
+ }
304
+ if (sub === "status") {
305
+ if (!existsSync(root)) {
306
+ console.log(fmtOk(`Cache empty (missing dir: ${root})`));
307
+ process.exit(0);
308
+ }
309
+ const entries = readdirSync(root, { withFileTypes: true }).filter((d) => d.isDirectory());
310
+ const { bytes, newestMtimeMs } = folderSizeBytes(root);
311
+ const last = newestMtimeMs ? new Date(newestMtimeMs).toISOString() : "n/a";
312
+ let newestAccess = 0;
313
+ for (const ent of entries) {
314
+ const metaPath = join(root, ent.name, "meta.json");
315
+ try {
316
+ const raw = readFileSync(metaPath, "utf8");
317
+ const meta = JSON.parse(raw);
318
+ if (typeof meta.lastAccessAt === "number") {
319
+ newestAccess = Math.max(newestAccess, meta.lastAccessAt);
320
+ }
321
+ } catch {
322
+ }
323
+ }
324
+ const lastAccess = newestAccess ? new Date(newestAccess).toISOString() : "n/a";
325
+ console.log(fmtOk(`Cache entries: ${entries.length}`));
326
+ console.log(fmtOk(`Disk usage: ${humanBytes(bytes)}`));
327
+ console.log(fmtOk(`Last modified: ${last}`));
328
+ console.log(fmtOk(`Last access: ${lastAccess}`));
329
+ process.exit(0);
330
+ }
331
+ rmSync(root, { recursive: true, force: true });
332
+ console.log(fmtOk("Cache cleaned"));
333
+ process.exit(0);
334
+ }
335
+ console.error(`Unknown command: ${cmd}`);
336
+ usage();
337
+ process.exit(1);
338
+ }
339
+ main();
@@ -0,0 +1,10 @@
1
+ type ResolveContext = {
2
+ parentURL?: string;
3
+ };
4
+ type LoadContext = {
5
+ format?: string;
6
+ };
7
+ declare function resolve(specifier: string, context: ResolveContext, nextResolve: any): Promise<any>;
8
+ declare function load(url: string, context: LoadContext, nextLoad: any): Promise<any>;
9
+
10
+ export { load, resolve };
@@ -0,0 +1,112 @@
1
+ import "./chunk-MLKGABMK.js";
2
+
3
+ // src/esmLoader.ts
4
+ import { pathToFileURL, fileURLToPath } from "url";
5
+ import { dirname, extname, join, resolve as resolvePath } from "path";
6
+ import { existsSync, readdirSync, readFileSync } from "fs";
7
+ var NATIVE_EXTS = /* @__PURE__ */ new Set([".c", ".cpp", ".cc", ".cxx", ".rs"]);
8
+ function isNativePath(p) {
9
+ return NATIVE_EXTS.has(extname(p));
10
+ }
11
+ function encodeVirtual(fileUrl) {
12
+ const u = new URL(fileUrl);
13
+ u.searchParams.set("relaxnative", "1");
14
+ return u.href;
15
+ }
16
+ function isVirtual(url) {
17
+ try {
18
+ const u = new URL(url);
19
+ return u.protocol === "file:" && u.searchParams.get("relaxnative") === "1";
20
+ } catch {
21
+ return false;
22
+ }
23
+ }
24
+ function findProjectRoot(fromDir) {
25
+ let cur = fromDir;
26
+ while (true) {
27
+ if (existsSync(join(cur, "package.json"))) return cur;
28
+ const parent = dirname(cur);
29
+ if (parent === cur) return fromDir;
30
+ cur = parent;
31
+ }
32
+ }
33
+ function resolveRegistrySpecifier(spec, parentURL) {
34
+ const prefix = "relaxnative/";
35
+ const pkg = spec.slice(prefix.length);
36
+ const parentPath = parentURL?.startsWith("file:") ? dirname(fileURLToPath(parentURL)) : process.cwd();
37
+ const root = findProjectRoot(parentPath);
38
+ const pkgDir = join(root, "native", "registry", pkg);
39
+ if (!existsSync(pkgDir)) {
40
+ throw new Error(
41
+ `RelaxRegistry package not found: ${pkg} (expected at ${pkgDir})`
42
+ );
43
+ }
44
+ const relaxJsonPath = join(pkgDir, "relax.json");
45
+ if (existsSync(relaxJsonPath)) {
46
+ const raw = readFileSync(relaxJsonPath, "utf8");
47
+ let parsed;
48
+ try {
49
+ parsed = JSON.parse(raw);
50
+ } catch {
51
+ throw new Error(`Invalid relax.json: ${relaxJsonPath}`);
52
+ }
53
+ const exp = parsed?.exports?.[0];
54
+ if (!exp?.source) {
55
+ throw new Error(`relax.json missing exports[0].source: ${relaxJsonPath}`);
56
+ }
57
+ const entry = join(pkgDir, exp.source);
58
+ if (!existsSync(entry)) {
59
+ throw new Error(`Registry entry not found: ${entry}`);
60
+ }
61
+ return entry;
62
+ }
63
+ const candidates = readdirSync(pkgDir).filter((f) => isNativePath(f)).map((f) => join(pkgDir, f));
64
+ if (candidates.length !== 1) {
65
+ throw new Error(
66
+ `Cannot infer registry entry for ${spec}. Add relax.json or ensure exactly one native file exists in ${pkgDir}`
67
+ );
68
+ }
69
+ return candidates[0];
70
+ }
71
+ async function resolve(specifier, context, nextResolve) {
72
+ if (specifier.startsWith("relaxnative/")) {
73
+ const entryPath = resolveRegistrySpecifier(specifier, context.parentURL);
74
+ return {
75
+ url: encodeVirtual(pathToFileURL(entryPath).href),
76
+ shortCircuit: true
77
+ };
78
+ }
79
+ if (isNativePath(specifier)) {
80
+ const parentPath = context.parentURL?.startsWith("file:") ? dirname(fileURLToPath(context.parentURL)) : process.cwd();
81
+ const abs = resolvePath(parentPath, specifier);
82
+ return {
83
+ url: encodeVirtual(pathToFileURL(abs).href),
84
+ shortCircuit: true
85
+ };
86
+ }
87
+ return nextResolve(specifier, context);
88
+ }
89
+ async function load(url, context, nextLoad) {
90
+ if (isVirtual(url)) {
91
+ const u = new URL(url);
92
+ u.searchParams.delete("relaxnative");
93
+ const sourcePath = fileURLToPath(u);
94
+ const loaderDir = dirname(fileURLToPath(import.meta.url));
95
+ const distEntry = pathToFileURL(join(loaderDir, "index.js")).href;
96
+ const source = `import { loadNative } from ${JSON.stringify(distEntry)};
97
+
98
+ const __api = await loadNative(${JSON.stringify(sourcePath)});
99
+ export default __api;
100
+ `;
101
+ return {
102
+ format: "module",
103
+ source,
104
+ shortCircuit: true
105
+ };
106
+ }
107
+ return nextLoad(url, context);
108
+ }
109
+ export {
110
+ load,
111
+ resolve
112
+ };