@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.
- package/README.md +137 -0
- package/lib/dist/activation-shim.mjs +237 -0
- package/lib/dist/actor.mjs +1129 -0
- package/lib/dist/deno-shim.mjs +2347 -0
- package/lib/src/actor-compat.d.ts +24 -0
- package/lib/src/actor-compat.js +302 -0
- package/lib/src/actor-compat.js.map +1 -0
- package/lib/src/bundler.d.ts +51 -0
- package/lib/src/bundler.js +321 -0
- package/lib/src/bundler.js.map +1 -0
- package/lib/src/contracts.d.ts +108 -0
- package/lib/src/contracts.js +39 -0
- package/lib/src/contracts.js.map +1 -0
- package/lib/src/deno-bundle.d.ts +30 -0
- package/lib/src/deno-bundle.js +146 -0
- package/lib/src/deno-bundle.js.map +1 -0
- package/lib/src/errors.d.ts +16 -0
- package/lib/src/errors.js +11 -0
- package/lib/src/errors.js.map +1 -0
- package/lib/src/esbuild/deno-resolver.d.ts +26 -0
- package/lib/src/esbuild/deno-resolver.js +436 -0
- package/lib/src/esbuild/deno-resolver.js.map +1 -0
- package/lib/src/esbuild/node-builtin-require.d.ts +2 -0
- package/lib/src/esbuild/node-builtin-require.js +33 -0
- package/lib/src/esbuild/node-builtin-require.js.map +1 -0
- package/lib/src/esbuild/private-data-sources-virtual.d.ts +3 -0
- package/lib/src/esbuild/private-data-sources-virtual.js +127 -0
- package/lib/src/esbuild/private-data-sources-virtual.js.map +1 -0
- package/lib/src/esbuild/runtime-context-virtual.d.ts +3 -0
- package/lib/src/esbuild/runtime-context-virtual.js +42 -0
- package/lib/src/esbuild/runtime-context-virtual.js.map +1 -0
- package/lib/src/esbuild/runtime-virtual.d.ts +2 -0
- package/lib/src/esbuild/runtime-virtual.js +52 -0
- package/lib/src/esbuild/runtime-virtual.js.map +1 -0
- package/lib/src/esbuild/user-files.d.ts +15 -0
- package/lib/src/esbuild/user-files.js +73 -0
- package/lib/src/esbuild/user-files.js.map +1 -0
- package/lib/src/fetch-guard.d.ts +26 -0
- package/lib/src/fetch-guard.js +67 -0
- package/lib/src/fetch-guard.js.map +1 -0
- package/lib/src/index.d.ts +12 -0
- package/lib/src/index.js +11 -0
- package/lib/src/index.js.map +1 -0
- package/lib/src/log.d.ts +7 -0
- package/lib/src/log.js +39 -0
- package/lib/src/log.js.map +1 -0
- package/lib/src/private-data-sources/build-http.ts +51 -0
- package/lib/src/private-data-sources/elasticsearch.ts +8 -0
- package/lib/src/private-data-sources/http-url.ts +23 -0
- package/lib/src/private-data-sources/http.ts +6 -0
- package/lib/src/private-data-sources/hyperdrive.ts +48 -0
- package/lib/src/private-data-sources/ioredis-adapter.ts +229 -0
- package/lib/src/private-data-sources/manifest.ts +150 -0
- package/lib/src/private-data-sources/mariadb.ts +8 -0
- package/lib/src/private-data-sources/mongodb.ts +70 -0
- package/lib/src/private-data-sources/mysql.ts +8 -0
- package/lib/src/private-data-sources/node-net-adapter.ts +244 -0
- package/lib/src/private-data-sources/postgres.ts +8 -0
- package/lib/src/private-data-sources/redis-auth.ts +83 -0
- package/lib/src/private-data-sources/redis.ts +47 -0
- package/lib/src/private-data-sources/runtime-environment.ts +4 -0
- package/lib/src/private-data-sources/runtime-manifest-store.ts +32 -0
- package/lib/src/private-data-sources/sqlserver.ts +22 -0
- package/lib/src/private-data-sources/tcp.ts +86 -0
- package/lib/src/private-data-sources/tedious-adapter.ts +67 -0
- package/lib/src/private-data-sources/types.ts +29 -0
- package/lib/src/runtime/index.ts +30 -0
- package/lib/src/runtime-context.ts +29 -0
- package/lib/src/static-egress-marker.d.ts +1 -0
- package/lib/src/static-egress-marker.js +7 -0
- package/lib/src/static-egress-marker.js.map +1 -0
- package/lib/src/telemetry.d.ts +32 -0
- package/lib/src/telemetry.js +84 -0
- package/lib/src/telemetry.js.map +1 -0
- package/lib/src/tracing.d.ts +9 -0
- package/lib/src/tracing.js +21 -0
- package/lib/src/tracing.js.map +1 -0
- package/lib/src/worker-entry.d.ts +36 -0
- package/lib/src/worker-entry.js +320 -0
- package/lib/src/worker-entry.js.map +1 -0
- 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
|