@base44/functions-compiler 0.1.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.
Files changed (81) hide show
  1. package/README.md +137 -0
  2. package/lib/dist/activation-shim.mjs +237 -0
  3. package/lib/dist/actor.mjs +1129 -0
  4. package/lib/dist/deno-shim.mjs +2347 -0
  5. package/lib/src/actor-compat.d.ts +24 -0
  6. package/lib/src/actor-compat.js +302 -0
  7. package/lib/src/actor-compat.js.map +1 -0
  8. package/lib/src/bundler.d.ts +51 -0
  9. package/lib/src/bundler.js +321 -0
  10. package/lib/src/bundler.js.map +1 -0
  11. package/lib/src/contracts.d.ts +108 -0
  12. package/lib/src/contracts.js +39 -0
  13. package/lib/src/contracts.js.map +1 -0
  14. package/lib/src/deno-bundle.d.ts +30 -0
  15. package/lib/src/deno-bundle.js +146 -0
  16. package/lib/src/deno-bundle.js.map +1 -0
  17. package/lib/src/errors.d.ts +16 -0
  18. package/lib/src/errors.js +11 -0
  19. package/lib/src/errors.js.map +1 -0
  20. package/lib/src/esbuild/deno-resolver.d.ts +26 -0
  21. package/lib/src/esbuild/deno-resolver.js +436 -0
  22. package/lib/src/esbuild/deno-resolver.js.map +1 -0
  23. package/lib/src/esbuild/node-builtin-require.d.ts +2 -0
  24. package/lib/src/esbuild/node-builtin-require.js +33 -0
  25. package/lib/src/esbuild/node-builtin-require.js.map +1 -0
  26. package/lib/src/esbuild/private-data-sources-virtual.d.ts +3 -0
  27. package/lib/src/esbuild/private-data-sources-virtual.js +127 -0
  28. package/lib/src/esbuild/private-data-sources-virtual.js.map +1 -0
  29. package/lib/src/esbuild/runtime-context-virtual.d.ts +3 -0
  30. package/lib/src/esbuild/runtime-context-virtual.js +42 -0
  31. package/lib/src/esbuild/runtime-context-virtual.js.map +1 -0
  32. package/lib/src/esbuild/runtime-virtual.d.ts +2 -0
  33. package/lib/src/esbuild/runtime-virtual.js +52 -0
  34. package/lib/src/esbuild/runtime-virtual.js.map +1 -0
  35. package/lib/src/esbuild/user-files.d.ts +15 -0
  36. package/lib/src/esbuild/user-files.js +73 -0
  37. package/lib/src/esbuild/user-files.js.map +1 -0
  38. package/lib/src/fetch-guard.d.ts +26 -0
  39. package/lib/src/fetch-guard.js +67 -0
  40. package/lib/src/fetch-guard.js.map +1 -0
  41. package/lib/src/index.d.ts +12 -0
  42. package/lib/src/index.js +11 -0
  43. package/lib/src/index.js.map +1 -0
  44. package/lib/src/log.d.ts +7 -0
  45. package/lib/src/log.js +39 -0
  46. package/lib/src/log.js.map +1 -0
  47. package/lib/src/private-data-sources/build-http.ts +51 -0
  48. package/lib/src/private-data-sources/elasticsearch.ts +8 -0
  49. package/lib/src/private-data-sources/http-url.ts +23 -0
  50. package/lib/src/private-data-sources/http.ts +6 -0
  51. package/lib/src/private-data-sources/hyperdrive.ts +48 -0
  52. package/lib/src/private-data-sources/ioredis-adapter.ts +229 -0
  53. package/lib/src/private-data-sources/manifest.ts +150 -0
  54. package/lib/src/private-data-sources/mariadb.ts +8 -0
  55. package/lib/src/private-data-sources/mongodb.ts +70 -0
  56. package/lib/src/private-data-sources/mysql.ts +8 -0
  57. package/lib/src/private-data-sources/node-net-adapter.ts +244 -0
  58. package/lib/src/private-data-sources/postgres.ts +8 -0
  59. package/lib/src/private-data-sources/redis-auth.ts +83 -0
  60. package/lib/src/private-data-sources/redis.ts +47 -0
  61. package/lib/src/private-data-sources/runtime-environment.ts +4 -0
  62. package/lib/src/private-data-sources/runtime-manifest-store.ts +32 -0
  63. package/lib/src/private-data-sources/sqlserver.ts +22 -0
  64. package/lib/src/private-data-sources/tcp.ts +86 -0
  65. package/lib/src/private-data-sources/tedious-adapter.ts +67 -0
  66. package/lib/src/private-data-sources/types.ts +29 -0
  67. package/lib/src/runtime/index.ts +30 -0
  68. package/lib/src/runtime-context.ts +29 -0
  69. package/lib/src/static-egress-marker.d.ts +1 -0
  70. package/lib/src/static-egress-marker.js +7 -0
  71. package/lib/src/static-egress-marker.js.map +1 -0
  72. package/lib/src/telemetry.d.ts +32 -0
  73. package/lib/src/telemetry.js +84 -0
  74. package/lib/src/telemetry.js.map +1 -0
  75. package/lib/src/tracing.d.ts +9 -0
  76. package/lib/src/tracing.js +21 -0
  77. package/lib/src/tracing.js.map +1 -0
  78. package/lib/src/worker-entry.d.ts +36 -0
  79. package/lib/src/worker-entry.js +320 -0
  80. package/lib/src/worker-entry.js.map +1 -0
  81. package/package.json +43 -0
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Bundle engine. Compiles a prepared worker tree (user files + injected shim +
3
+ * generated entry) into a single workerd ESM module using Deno's own resolver
4
+ * via the `deno-resolver` adapter over `@deno/loader`: `npm:`, `jsr:`, and
5
+ * `https:` specifiers resolve exactly as Deno resolves them (multiple versions,
6
+ * nested deps, correct exports).
7
+ *
8
+ * The loader reads source from a real filesystem, so the tree is materialized
9
+ * into a per-request temp dir, compiled, and removed. `node:` builtins stay
10
+ * external (workerd's `nodejs_compat` provides them) and `cloudflare:*` stays
11
+ * external (provided by the runtime).
12
+ */
13
+ import { mkdtemp, rm, writeFile } from "node:fs/promises";
14
+ import { tmpdir } from "node:os";
15
+ import path from "node:path";
16
+ import { build } from "esbuild";
17
+ import { denoResolverPlugin } from "./esbuild/deno-resolver.js";
18
+ import { nodeBuiltinRequirePlugin } from "./esbuild/node-builtin-require.js";
19
+ import { privateDataSourcesVirtualPlugin } from "./esbuild/private-data-sources-virtual.js";
20
+ import { runtimeContextVirtualPlugin } from "./esbuild/runtime-context-virtual.js";
21
+ import { runtimeVirtualPlugin } from "./esbuild/runtime-virtual.js";
22
+ import { USER_NAMESPACE, userFilesPlugin } from "./esbuild/user-files.js";
23
+ /**
24
+ * Compile `prepared` to a single ESM module string. Resolution/compile failures
25
+ * (user-attributable) return `{ ok: false }`; infrastructure failures (temp dir,
26
+ * missing output) throw.
27
+ */
28
+ export async function bundleToModule(prepared, nodeModulesDir = "none") {
29
+ const dir = await mkdtemp(path.join(tmpdir(), "b44-bundle-"));
30
+ try {
31
+ // Only deno.json (the loader's config) and the materialized node_modules
32
+ // touch disk — user source is served from memory by userFilesPlugin, so a
33
+ // malicious import can't traverse to another build's files.
34
+ await writeFile(path.join(dir, "deno.json"), JSON.stringify({ nodeModulesDir }));
35
+ let result;
36
+ try {
37
+ result = await build({
38
+ entryPoints: [prepared.entry],
39
+ absWorkingDir: dir,
40
+ bundle: true,
41
+ write: false,
42
+ format: "esm",
43
+ platform: "browser",
44
+ target: "es2022",
45
+ // "node" is active because workerd runs with nodejs_compat: packages
46
+ // like unicorn-magic gate their full API behind the "node" exports
47
+ // condition and ship a stripped default entry, so without it consumers
48
+ // (npm-run-path) import names that don't exist and the bundle fails.
49
+ // Conditions are a set — when a package lists both "workerd" and
50
+ // "node", its own exports-map key order still decides which wins.
51
+ conditions: ["workerd", "worker", "browser", "module", "node"],
52
+ external: ["cloudflare:*"],
53
+ // workerd ESM lacks __dirname/__filename; define rewrites only free refs
54
+ // (npm/Emscripten glue), leaving loader-bound CJS locals intact.
55
+ define: { __dirname: '"/"', __filename: '"/index.js"' },
56
+ minify: true,
57
+ sourcemap: false,
58
+ // Errors are surfaced structurally (return value / BuildFailure); keep
59
+ // esbuild from dumping diagnostics to the service's stderr.
60
+ logLevel: "silent",
61
+ plugins: [
62
+ // CJS require() of a node builtin → static re-export (workerd throws
63
+ // on esbuild's lowered __require). Runs before the Deno resolver.
64
+ nodeBuiltinRequirePlugin(),
65
+ userFilesPlugin(prepared.files, prepared.entry),
66
+ privateDataSourcesVirtualPlugin(),
67
+ runtimeContextVirtualPlugin(),
68
+ runtimeVirtualPlugin(),
69
+ denoResolverPlugin({ configPath: path.join(dir, "deno.json") }),
70
+ ],
71
+ });
72
+ }
73
+ catch (e) {
74
+ const errors = buildFailureErrors(e, dir);
75
+ if (errors)
76
+ return { ok: false, errors };
77
+ throw e;
78
+ }
79
+ const outputs = result.outputFiles ?? [];
80
+ if (outputs.length !== 1) {
81
+ // bundle:true with code-splitting off yields exactly one module; anything
82
+ // else means a plugin claimed the entry or emitted extra files — a bug.
83
+ throw new Error(`expected single-module output, got ${outputs.length} file(s)`);
84
+ }
85
+ return {
86
+ ok: true,
87
+ module: outputs[0].text,
88
+ warnings: result.warnings.map((w) => w.text),
89
+ };
90
+ }
91
+ finally {
92
+ await rm(dir, { recursive: true, force: true });
93
+ }
94
+ }
95
+ /** Flatten an esbuild `BuildFailure` into compile diagnostics, rewriting the
96
+ * temp-dir-absolute file paths back to the paths the user typed. Returns null
97
+ * if `e` is not an esbuild failure (so the caller rethrows it). */
98
+ function buildFailureErrors(e, dir) {
99
+ if (typeof e !== "object" || e === null || !("errors" in e))
100
+ return null;
101
+ const raw = e.errors;
102
+ if (!Array.isArray(raw))
103
+ return null;
104
+ return raw.map((m) => normalizeError(m, dir));
105
+ }
106
+ function normalizeError(m, dir) {
107
+ const message = m.text ?? "bundle error";
108
+ const item = { message };
109
+ // The Deno loader transpiles before esbuild sees the source, so a syntax
110
+ // error's real location lives in the message text
111
+ // (`… at file:///<abs>/main.ts:LINE:COL`) while esbuild's structured
112
+ // `location` points at the importing entry. Prefer the embedded one so the
113
+ // diagnostic points at the user's file.
114
+ const embedded = message.match(/file:\/\/(\/[^\s:]+):(\d+):(\d+)/);
115
+ if (embedded) {
116
+ item.file = relativizeFile(embedded[1], dir);
117
+ item.line = Number(embedded[2]);
118
+ item.column = Number(embedded[3]);
119
+ }
120
+ const loc = m.location;
121
+ if (item.file === undefined && loc?.file) {
122
+ item.file = relativizeFile(loc.file, dir);
123
+ }
124
+ if (item.line === undefined && typeof loc?.line === "number") {
125
+ item.line = loc.line;
126
+ }
127
+ if (item.column === undefined && typeof loc?.column === "number") {
128
+ item.column = loc.column;
129
+ }
130
+ if (loc?.lineText)
131
+ item.lineText = loc.lineText;
132
+ if (loc?.suggestion)
133
+ item.suggestion = loc.suggestion;
134
+ return item;
135
+ }
136
+ /** Normalize the file in a diagnostic to the path the user typed. User files are
137
+ * virtual keys esbuild prefixes with the namespace (`user:main.ts`); deps are
138
+ * temp-dir-absolute (`<dir>/node_modules/foo/…`). Strip either prefix. */
139
+ function relativizeFile(file, dir) {
140
+ if (file.startsWith(`${USER_NAMESPACE}:`)) {
141
+ return file.slice(USER_NAMESPACE.length + 1);
142
+ }
143
+ const prefix = dir.endsWith(path.sep) ? dir : dir + path.sep;
144
+ return file.startsWith(prefix) ? file.slice(prefix.length) : file;
145
+ }
146
+ //# sourceMappingURL=deno-bundle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deno-bundle.js","sourceRoot":"","sources":["../../src/deno-bundle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /** A user-code problem the bundler can't translate; `file` is attached to the
2
+ * diagnostic when known. Surfaced to the caller as an `ok:false` compile error. */
3
+ export declare class DenoCompatError extends Error {
4
+ readonly file?: string;
5
+ constructor(message: string, file?: string);
6
+ }
7
+ /** One flattened compile diagnostic. Shape is part of the HTTP contract — the
8
+ * Python `BundlerClient` and the builder agent read these fields. */
9
+ export interface BundleErrorItem {
10
+ message: string;
11
+ file?: string;
12
+ line?: number;
13
+ column?: number;
14
+ lineText?: string;
15
+ suggestion?: string;
16
+ }
@@ -0,0 +1,11 @@
1
+ /** A user-code problem the bundler can't translate; `file` is attached to the
2
+ * diagnostic when known. Surfaced to the caller as an `ok:false` compile error. */
3
+ export class DenoCompatError extends Error {
4
+ file;
5
+ constructor(message, file) {
6
+ super(message);
7
+ this.name = "DenoCompatError";
8
+ this.file = file;
9
+ }
10
+ }
11
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAAA;oFACoF;AACpF,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAC/B,IAAI,CAAU;IACvB,YAAY,OAAe,EAAE,IAAa;QACxC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * esbuild resolver/loader over `@deno/loader` (Deno's own resolver crates,
3
+ * compiled to WASM). A thin in-repo adapter — the published
4
+ * `@deno/esbuild-plugin` is the same idea, but it throws the build away on
5
+ * unresolved *optional* dependencies and reports transpile errors against the
6
+ * importer. We own the glue so we can fix both:
7
+ *
8
+ * - Optional deps that aren't installed are left external (the import survives
9
+ * to runtime, where the author's try/catch handles the miss) instead of
10
+ * failing the bundle. This covers declared optional deps such as axios's
11
+ * `follow-redirects` -> `debug` and undeclared guarded requires such as
12
+ * mysql2 -> `cardinal`, matching the old resolver's leniency.
13
+ * - Transpile/syntax errors are reported with the offending file + position,
14
+ * not the importing entry.
15
+ *
16
+ * The heavy lifting (npm/jsr/https resolution, multi-version, exports maps) stays
17
+ * in `@deno/loader`.
18
+ */
19
+ import type { Plugin } from "esbuild";
20
+ interface DenoResolverOptions {
21
+ /** Path to the deno.json controlling resolution (we write one per bundle with
22
+ * `nodeModulesDir: auto` so the loader auto-fetches npm deps under Node). */
23
+ configPath?: string;
24
+ }
25
+ export declare function denoResolverPlugin(options?: DenoResolverOptions): Plugin;
26
+ export {};
@@ -0,0 +1,436 @@
1
+ /**
2
+ * esbuild resolver/loader over `@deno/loader` (Deno's own resolver crates,
3
+ * compiled to WASM). A thin in-repo adapter — the published
4
+ * `@deno/esbuild-plugin` is the same idea, but it throws the build away on
5
+ * unresolved *optional* dependencies and reports transpile errors against the
6
+ * importer. We own the glue so we can fix both:
7
+ *
8
+ * - Optional deps that aren't installed are left external (the import survives
9
+ * to runtime, where the author's try/catch handles the miss) instead of
10
+ * failing the bundle. This covers declared optional deps such as axios's
11
+ * `follow-redirects` -> `debug` and undeclared guarded requires such as
12
+ * mysql2 -> `cardinal`, matching the old resolver's leniency.
13
+ * - Transpile/syntax errors are reported with the offending file + position,
14
+ * not the importing entry.
15
+ *
16
+ * The heavy lifting (npm/jsr/https resolution, multi-version, exports maps) stays
17
+ * in `@deno/loader`.
18
+ */
19
+ import { readFileSync, realpathSync, statSync } from "node:fs";
20
+ import { isBuiltin } from "node:module";
21
+ import { homedir } from "node:os";
22
+ import path from "node:path";
23
+ import { fileURLToPath, pathToFileURL } from "node:url";
24
+ import { MediaType, RequestedModuleType, ResolutionMode, ResolveError, Workspace, } from "@deno/loader";
25
+ import { logEvent } from "../log.js";
26
+ import { USER_NAMESPACE } from "./user-files.js";
27
+ // Schemes the loader resolves to; each becomes an esbuild namespace so imports
28
+ // inside a loaded module re-enter resolution with the scheme as context.
29
+ const NAMESPACES = ["file", "http", "https", "data", "npm", "jsr"];
30
+ // We also resolve imports *from* in-memory user files (served by the user-files
31
+ // plugin) but never load them — that plugin owns their contents.
32
+ const RESOLVE_NAMESPACES = [...NAMESPACES, USER_NAMESPACE];
33
+ export function denoResolverPlugin(options = {}) {
34
+ return {
35
+ name: "deno-resolver",
36
+ async setup(build) {
37
+ // Security: stop a dependency or data: module from reading host files.
38
+ // Allow file: reads only from where deps live — the global cache, or this
39
+ // build's temp node_modules in "auto" mode — and resolve symlinks first so
40
+ // a crafted link can't point out of those folders.
41
+ const baseDir = options.configPath
42
+ ? path.dirname(options.configPath)
43
+ : undefined;
44
+ const depRoots = [denoCacheRoot(), baseDir]
45
+ .filter((d) => Boolean(d))
46
+ .map(canonicalPath);
47
+ const pathUnderDeps = (absPath) => {
48
+ const p = canonicalPath(absPath);
49
+ return depRoots.some((root) => {
50
+ const rel = path.relative(root, p);
51
+ return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
52
+ });
53
+ };
54
+ const workspace = new Workspace({
55
+ platform: "browser",
56
+ nodeConditions: build.initialOptions.conditions,
57
+ configPath: options.configPath,
58
+ });
59
+ const dispose = (createdLoader) => {
60
+ createdLoader?.[Symbol.dispose]?.();
61
+ workspace[Symbol.dispose]?.();
62
+ };
63
+ let loader;
64
+ try {
65
+ // createLoader() is async and can reject; the WASM can also trap with
66
+ // "unreachable" (a Rust panic in the loader). esbuild does NOT run
67
+ // onDispose for a setup() that rejected, so on any failure here we must
68
+ // dispose the Workspace ourselves — otherwise its WASM allocation stays
69
+ // resident in the still-alive worker until GC.
70
+ loader = await workspace.createLoader();
71
+ }
72
+ catch (err) {
73
+ dispose();
74
+ logEvent("error", "base44.bundler.workspace_disposed_on_setup_failure", {
75
+ phase: "create_loader",
76
+ wasm_trap: errMessage(err) === "unreachable",
77
+ });
78
+ throw err;
79
+ }
80
+ build.onDispose(() => dispose(loader));
81
+ const externals = (build.initialOptions.external ?? []).map(toRegex);
82
+ const onResolve = async (args) => {
83
+ // node: builtins (and configured externals) stay external — workerd's
84
+ // nodejs_compat provides the builtins. require()-of-builtin is claimed
85
+ // earlier by node-builtin-require, so this is the import-kind path.
86
+ if (isBuiltin(args.path) ||
87
+ externals.some((re) => re.test(args.path))) {
88
+ return { path: args.path, external: true };
89
+ }
90
+ const mode = args.kind === "require-call" || args.kind === "require-resolve"
91
+ ? ResolutionMode.Require
92
+ : ResolutionMode.Import;
93
+ const importer = args.namespace === USER_NAMESPACE && baseDir
94
+ ? path.join(baseDir, args.importer)
95
+ : args.importer;
96
+ try {
97
+ const resolved = await loader.resolve(args.path, importer, mode);
98
+ // A non-builtin specifier that still resolves to a builtin (rare) —
99
+ // externalize it like any other node: import.
100
+ if (resolved.startsWith("node:")) {
101
+ return { path: resolved, external: true };
102
+ }
103
+ if (resolved.startsWith("file:") &&
104
+ !pathUnderDeps(fileURLToPath(resolved))) {
105
+ return { errors: [fileOutsideCacheError(args.path)] };
106
+ }
107
+ return toEsbuildPath(resolved);
108
+ }
109
+ catch (err) {
110
+ // DELTA: an uninstalled *optional* dependency is not a build failure.
111
+ // Leave it external so the import survives to runtime (where the
112
+ // author's try/catch turns the missing module into a no-op).
113
+ if (isOptionalDependency(err)) {
114
+ return { path: args.path, external: true };
115
+ }
116
+ // Restore the main/module lookup the loader skips (see
117
+ // resolveEntryFromPackageJson). Before the externalize branch: a
118
+ // resolvable package must be bundled, not left as a broken require().
119
+ const fallbackUrl = resolveEntryFromPackageJson(err, args.path, mode, pathUnderDeps);
120
+ if (fallbackUrl) {
121
+ logEvent("info", "base44.bundler.package_entry_fallback", {
122
+ specifier: args.path,
123
+ });
124
+ if (fallbackUrl.startsWith("file:") &&
125
+ !pathUnderDeps(fileURLToPath(fallbackUrl))) {
126
+ return { errors: [fileOutsideCacheError(args.path)] };
127
+ }
128
+ return toEsbuildPath(fallbackUrl);
129
+ }
130
+ // Some published packages guard undeclared optional deps with
131
+ // try/catch instead of listing them in package.json. The loader
132
+ // cannot identify those as optional, so preserve the old resolver's
133
+ // runtime fallback only for catchable references originating inside
134
+ // deps. `dynamic-import` is the ESM spelling of the same guarded
135
+ // pattern (`await import("x")`, often tagged webpackIgnore/
136
+ // @vite-ignore — pragmas esbuild does not honor). Static imports must
137
+ // still fail the build.
138
+ //
139
+ // Two shapes of "package not found": a typed ResolveError with
140
+ // ERR_MODULE_NOT_FOUND, or a bare Error reading "Could not find
141
+ // package X from referrer Y" (what @mastra/core -> @ast-grep/napi
142
+ // hits — neither the type nor the code). Match exactly those; other
143
+ // ResolveError codes (exports-map, version conflicts) must keep
144
+ // failing the build with their attributed diagnostic.
145
+ if ((isMissingDependency(err) ||
146
+ MISSING_PACKAGE.test(errMessage(err))) &&
147
+ (args.kind === "require-call" ||
148
+ args.kind === "require-resolve" ||
149
+ args.kind === "dynamic-import") &&
150
+ isBareSpecifier(args.path) &&
151
+ args.namespace !== USER_NAMESPACE &&
152
+ pathUnderDeps(importer)) {
153
+ logEvent("info", "base44.bundler.undeclared_optional_externalized", {
154
+ specifier: args.path,
155
+ referrer_package: packageFromImporter(importer),
156
+ });
157
+ return { path: args.path, external: true };
158
+ }
159
+ // These mean "genuinely not a dependency" — let esbuild report a
160
+ // normal "could not resolve" error with the importer location.
161
+ if (NOT_A_DEP.test(errMessage(err))) {
162
+ return null;
163
+ }
164
+ throw err;
165
+ }
166
+ };
167
+ build.onResolve({ filter: /.*/ }, onResolve);
168
+ for (const namespace of RESOLVE_NAMESPACES) {
169
+ build.onResolve({ filter: /.*/, namespace }, onResolve);
170
+ }
171
+ const onLoad = async (args) => {
172
+ // Defense in depth if a file: path slips past onResolve.
173
+ if (args.namespace === "file" && !pathUnderDeps(args.path)) {
174
+ return { errors: [fileOutsideCacheError(args.path)] };
175
+ }
176
+ const url = isUrlScheme(args.path)
177
+ ? args.path
178
+ : pathToFileURL(args.path).toString();
179
+ try {
180
+ const res = await loader.load(url, moduleType(args));
181
+ if (res.kind === "external") {
182
+ return undefined;
183
+ }
184
+ return { contents: res.code, loader: mediaToLoader(res.mediaType) };
185
+ }
186
+ catch (err) {
187
+ // DELTA: the loader transpiles here, so syntax errors surface as a
188
+ // throw. Report a located error against the file being loaded rather
189
+ // than letting esbuild attribute it to the importing entry.
190
+ //
191
+ // A WASM "unreachable" trap (a Rust panic in @deno/loader) also
192
+ // surfaces here as a throw and would otherwise be indistinguishable
193
+ // from a user syntax error — swallowed into a compile_error and never
194
+ // counted as a trap. Emit an observability signal for it (the
195
+ // onResolve path rethrows, so resolve-phase traps already surface as
196
+ // base44.bundler.crash with wasm_trap:true) while keeping the
197
+ // user-facing located-error return unchanged.
198
+ if (errMessage(err) === "unreachable") {
199
+ logEvent("error", "base44.bundler.load_wasm_trap", {
200
+ phase: "load",
201
+ });
202
+ }
203
+ return { errors: [locatedError(err, args.path)] };
204
+ }
205
+ };
206
+ for (const namespace of NAMESPACES) {
207
+ build.onLoad({ filter: /.*/, namespace }, onLoad);
208
+ }
209
+ },
210
+ };
211
+ }
212
+ function toEsbuildPath(resolved) {
213
+ if (resolved.startsWith("file:")) {
214
+ return { path: fileURLToPath(resolved), namespace: "file" };
215
+ }
216
+ for (const scheme of ["http", "https", "data", "npm", "jsr"]) {
217
+ if (resolved.startsWith(`${scheme}:`)) {
218
+ return { path: resolved, namespace: scheme };
219
+ }
220
+ }
221
+ return { path: resolved };
222
+ }
223
+ function isUrlScheme(p) {
224
+ return ["http:", "https:", "data:", "npm:", "jsr:"].some((s) => p.startsWith(s));
225
+ }
226
+ function moduleType(args) {
227
+ switch (args.with?.type) {
228
+ case "text":
229
+ return RequestedModuleType.Text;
230
+ case "bytes":
231
+ return RequestedModuleType.Bytes;
232
+ case "json":
233
+ return RequestedModuleType.Json;
234
+ default:
235
+ return args.path.endsWith(".json")
236
+ ? RequestedModuleType.Json
237
+ : RequestedModuleType.Default;
238
+ }
239
+ }
240
+ function mediaToLoader(type) {
241
+ switch (type) {
242
+ case MediaType.Jsx:
243
+ return "jsx";
244
+ case MediaType.Tsx:
245
+ return "tsx";
246
+ case MediaType.TypeScript:
247
+ case MediaType.Mts:
248
+ case MediaType.Cts:
249
+ return "ts";
250
+ case MediaType.Json:
251
+ return "json";
252
+ case MediaType.Css:
253
+ return "css";
254
+ case MediaType.Wasm:
255
+ return "binary";
256
+ case MediaType.JavaScript:
257
+ case MediaType.Mjs:
258
+ case MediaType.Cjs:
259
+ return "js";
260
+ default:
261
+ return "js";
262
+ }
263
+ }
264
+ // esbuild passes configured externals to plugins; match them here too.
265
+ function toRegex(external) {
266
+ return new RegExp("^" +
267
+ external.replace(/[-/\\^$+?.()|[\]{}]/g, "\\$&").replace(/\*/g, ".*") +
268
+ "$");
269
+ }
270
+ const NOT_A_DEP = /not a dependency and not in import map|Relative import path ".*?" not prefixed with/;
271
+ // An npm package the loader could not locate at all. Some shapes arrive as a
272
+ // typed ResolveError; this family arrives as a bare Error carrying only text.
273
+ const MISSING_PACKAGE = /Could not find package .*? from referrer/;
274
+ function isBareSpecifier(specifier) {
275
+ return (!specifier.startsWith(".") &&
276
+ !specifier.startsWith("/") &&
277
+ !specifier.startsWith("#") &&
278
+ !specifier.includes("\\") &&
279
+ !/^[a-zA-Z][a-zA-Z\d+.-]*:/.test(specifier));
280
+ }
281
+ function packageFromImporter(importer) {
282
+ const parts = canonicalPath(importer).split(path.sep);
283
+ const nodeModulesIndex = parts.lastIndexOf("node_modules");
284
+ const registryIndex = parts.lastIndexOf("registry.npmjs.org");
285
+ const markerIndex = Math.max(nodeModulesIndex, registryIndex);
286
+ if (markerIndex < 0)
287
+ return undefined;
288
+ const packageIndex = markerIndex + 1;
289
+ const first = parts[packageIndex];
290
+ if (!first)
291
+ return undefined;
292
+ return first.startsWith("@") && parts[packageIndex + 1]
293
+ ? `${first}/${parts[packageIndex + 1]}`
294
+ : first;
295
+ }
296
+ function errMessage(err) {
297
+ return err instanceof Error ? err.message : String(err);
298
+ }
299
+ // Same wording as the user-files plugin so the message is consistent.
300
+ function fileOutsideCacheError(spec) {
301
+ return {
302
+ text: `Cannot import "${spec}": filesystem imports are not allowed`,
303
+ };
304
+ }
305
+ // Where the loader caches npm; prod sets DENO_DIR, else Deno's per-OS default.
306
+ function denoCacheRoot() {
307
+ if (process.env.DENO_DIR)
308
+ return path.resolve(process.env.DENO_DIR);
309
+ const home = homedir();
310
+ switch (process.platform) {
311
+ case "darwin":
312
+ return path.join(home, "Library", "Caches", "deno");
313
+ case "win32":
314
+ return path.join(process.env.LOCALAPPDATA ?? path.join(home, "AppData", "Local"), "deno");
315
+ default:
316
+ return path.join(process.env.XDG_CACHE_HOME ?? path.join(home, ".cache"), "deno");
317
+ }
318
+ }
319
+ // Resolve symlinks before the containment check so a link can't escape an
320
+ // allowed root; fall back to the normalized path when it doesn't exist yet.
321
+ function canonicalPath(p) {
322
+ try {
323
+ return realpathSync(path.resolve(p));
324
+ }
325
+ catch {
326
+ return path.resolve(p);
327
+ }
328
+ }
329
+ // `@deno/loader` sets `isOptionalDependency` on ResolveError when an optional
330
+ // npm dependency can't be found (ERR_MODULE_NOT_FOUND).
331
+ function isOptionalDependency(err) {
332
+ return err instanceof ResolveError && err.isOptionalDependency === true;
333
+ }
334
+ function isMissingDependency(err) {
335
+ return err instanceof ResolveError && err.code === "ERR_MODULE_NOT_FOUND";
336
+ }
337
+ function locatedError(err, file) {
338
+ const text = errMessage(err);
339
+ // The loader embeds the position in the message (`… :LINE:COL`).
340
+ const m = text.match(/:(\d+):(\d+)\b/);
341
+ return {
342
+ text,
343
+ location: m ? { file, line: Number(m[1]), column: Number(m[2]) } : { file },
344
+ };
345
+ }
346
+ const ENTRY_EXTENSIONS = [".js", ".mjs", ".cjs", ".json"];
347
+ const INDEX_BASENAMES = ["index.js", "index.mjs", "index.cjs", "index.json"];
348
+ function isFile(p) {
349
+ try {
350
+ return statSync(p).isFile();
351
+ }
352
+ catch {
353
+ return false;
354
+ }
355
+ }
356
+ /** Resolve a package.json entry (a module ID) as Node does: literal path, then
357
+ * appended extensions, then directory index. Returns the file or null. */
358
+ function resolveNodeEntry(pkgDir, entry) {
359
+ const target = path.resolve(pkgDir, entry);
360
+ if (isFile(target))
361
+ return target;
362
+ for (const ext of ENTRY_EXTENSIONS) {
363
+ if (isFile(target + ext))
364
+ return target + ext;
365
+ }
366
+ for (const index of INDEX_BASENAMES) {
367
+ const candidate = path.join(target, index);
368
+ if (isFile(candidate))
369
+ return candidate;
370
+ }
371
+ return null;
372
+ }
373
+ /** @deno/loader hardcodes IsCjsResolutionMode::ExplicitTypeCommonJs, so a
374
+ * package with no `"type"` (CommonJS by Node's default) is treated as ESM;
375
+ * lacking an `exports` map, its `main` field is skipped and it resolves to a
376
+ * non-existent `<pkg>/index.js`. The Deno CLI (ImplicitTypeCommonJs) reads
377
+ * `main` instead. On that ERR_MODULE_NOT_FOUND, read package.json and return
378
+ * the real entry as a `file://` URL, or null when not this case / unrecoverable. */
379
+ function resolveEntryFromPackageJson(err, specifier, mode, pathUnderDeps) {
380
+ if (!(err instanceof ResolveError) || err.code !== "ERR_MODULE_NOT_FOUND") {
381
+ return null;
382
+ }
383
+ const msg = errMessage(err);
384
+ const m = msg.match(/Cannot find module '(file:\/\/\/.+?)'/);
385
+ if (!m)
386
+ return null;
387
+ let attempted;
388
+ try {
389
+ attempted = fileURLToPath(m[1]);
390
+ }
391
+ catch {
392
+ return null;
393
+ }
394
+ const base = path.basename(attempted);
395
+ if (base !== "index.js" && base !== "index.mjs")
396
+ return null;
397
+ // Only recover the loader's *synthesized* package-root probe. An explicit
398
+ // `pkg/index.js` (or `./index.js`) import that resolves to this same path was
399
+ // a deliberate request for that file — redirecting it to main/module would
400
+ // silently load a different entry, so let the missing-file error stand.
401
+ if (specifier.endsWith(`/${base}`))
402
+ return null;
403
+ const pkgDir = path.dirname(attempted);
404
+ // Fail closed: never touch a package.json (or its entries) outside the cache.
405
+ if (!pathUnderDeps(pkgDir))
406
+ return null;
407
+ const pkgJsonPath = path.join(pkgDir, "package.json");
408
+ try {
409
+ const pkg = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
410
+ // An `exports` map governs resolution; the loader would have used it, so a
411
+ // miss here is a genuine error — don't override the contract via legacy fields.
412
+ if (pkg.exports != null)
413
+ return null;
414
+ // Deno's ImplicitTypeCommonJs classifies these type-less packages as CJS and
415
+ // runs `main`, so prefer it for parity. A string `browser` entry wins first
416
+ // (the workerd/browser-targeted build, avoids bundling a Node entry). require()
417
+ // never falls through to `module` — an ESM/browser entry is not require-safe.
418
+ const browser = typeof pkg.browser === "string" ? pkg.browser : undefined;
419
+ const fields = mode === ResolutionMode.Require
420
+ ? [browser, pkg.main]
421
+ : [browser, pkg.main, pkg.module];
422
+ for (const entry of fields) {
423
+ if (typeof entry !== "string" || !entry)
424
+ continue;
425
+ const resolved = resolveNodeEntry(pkgDir, entry);
426
+ if (resolved && pathUnderDeps(resolved)) {
427
+ return pathToFileURL(resolved).toString();
428
+ }
429
+ }
430
+ return null;
431
+ }
432
+ catch {
433
+ return null;
434
+ }
435
+ }
436
+ //# sourceMappingURL=deno-resolver.js.map