@somewhere-tech/cli 0.11.0 → 0.12.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.
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Rendering for the worker's structured BUILD_ERROR payload (tsk_de47d1d7).
3
+ *
4
+ * The /deploy and /deploy/patch routes answer compile failures with
5
+ * { error: 'BUILD_ERROR', message, data: { file, line, column, error,
6
+ * frame, errors: [...], build_log }, hint? }
7
+ *
8
+ * The CLI has the source files on disk, so the code frame is rebuilt locally
9
+ * (wider context + caret) instead of trusting the server's copy — and falls
10
+ * back to data.frame / the raw message when the local file doesn't match.
11
+ */
12
+ import { existsSync, readFileSync } from 'node:fs';
13
+ import { isAbsolute, join } from 'node:path';
14
+ import { CliApiError } from './client.js';
15
+ import { bold, dim, error, info, red, teal, warn } from './output.js';
16
+ /** Pull file:line:col diagnostics out of esbuild-style bundle_error text. */
17
+ export function parseBundleErrorText(text) {
18
+ const out = [];
19
+ const re = /([^\s:]+(?:\/[^\s:]+)*):(\d+):(\d+): ERROR: ([^\n]+)/g;
20
+ let m;
21
+ while ((m = re.exec(text)) !== null) {
22
+ out.push({ file: m[1], line: Number(m[2]), column: Number(m[3]) + 1, message: m[4].trim() });
23
+ }
24
+ return out;
25
+ }
26
+ const FRAME_CONTEXT = 2;
27
+ /** Build a code frame (line ±2, caret under the column) from local source. */
28
+ export function localCodeFrame(source, line, column) {
29
+ const lines = source.split('\n');
30
+ if (line < 1 || line > lines.length)
31
+ return '';
32
+ const start = Math.max(1, line - FRAME_CONTEXT);
33
+ const end = Math.min(lines.length, line + FRAME_CONTEXT);
34
+ const width = String(end).length;
35
+ const out = [];
36
+ for (let n = start; n <= end; n++) {
37
+ const marker = n === line ? red('>') : ' ';
38
+ const num = String(n).padStart(width);
39
+ out.push(`${marker} ${dim(`${num} |`)} ${lines[n - 1]}`);
40
+ if (n === line && column !== undefined && column >= 1) {
41
+ out.push(` ${dim(`${' '.repeat(width)} |`)} ${' '.repeat(column - 1)}${red('^')}`);
42
+ }
43
+ }
44
+ return out.join('\n');
45
+ }
46
+ function frameFor(detail, baseDir, serverFrame) {
47
+ if (detail.file && detail.line !== undefined) {
48
+ const abs = isAbsolute(detail.file) ? detail.file : join(baseDir, detail.file);
49
+ if (existsSync(abs)) {
50
+ try {
51
+ const frame = localCodeFrame(readFileSync(abs, 'utf-8'), detail.line, detail.column);
52
+ if (frame)
53
+ return frame;
54
+ }
55
+ catch {
56
+ // unreadable — fall through to the server's frame
57
+ }
58
+ }
59
+ }
60
+ return serverFrame ?? '';
61
+ }
62
+ function location(detail) {
63
+ if (!detail.file)
64
+ return detail.message ?? 'unknown error';
65
+ const pos = detail.line !== undefined
66
+ ? `:${detail.line}${detail.column !== undefined ? `:${detail.column}` : ''}`
67
+ : '';
68
+ return `${detail.file}${pos}${detail.message ? ` — ${detail.message}` : ''}`;
69
+ }
70
+ /** True when the error carries a structured build payload this module renders:
71
+ * BUILD_ERROR's data.{file,line,errors}, or any error (DEPLOY_BLANK_PAGE)
72
+ * whose data.bundle_error names the compile failure. */
73
+ export function isBuildError(err) {
74
+ if (!(err instanceof CliApiError) || !err.data || typeof err.data !== 'object')
75
+ return false;
76
+ if (err.code === 'BUILD_ERROR')
77
+ return true;
78
+ return typeof err.data.bundle_error === 'string';
79
+ }
80
+ /**
81
+ * Print a structured build failure: file:line heading, local code frame,
82
+ * remaining errors, missing assets, hint. Returns true if it rendered
83
+ * (caller falls back to generic output otherwise).
84
+ */
85
+ export function renderBuildError(err, baseDir) {
86
+ const data = (err.data ?? {});
87
+ let details = Array.isArray(data.errors) && data.errors.length > 0
88
+ ? data.errors
89
+ : data.file !== undefined || data.error !== undefined
90
+ ? [{ file: data.file, line: data.line, column: data.column, message: data.error }]
91
+ : [];
92
+ if (details.length === 0 && typeof data.bundle_error === 'string') {
93
+ details = parseBundleErrorText(data.bundle_error);
94
+ }
95
+ if (details.length === 0 && !data.missing_assets?.length)
96
+ return false;
97
+ const first = details[0];
98
+ if (first) {
99
+ error(`${bold('Build failed')} — ${teal(location(first))}`);
100
+ const frame = frameFor(first, baseDir, data.frame);
101
+ if (frame) {
102
+ console.log('');
103
+ console.log(frame);
104
+ console.log('');
105
+ }
106
+ }
107
+ for (const extra of details.slice(1, 6)) {
108
+ error(location(extra));
109
+ const frame = frameFor(extra, baseDir);
110
+ if (frame) {
111
+ console.log('');
112
+ console.log(frame);
113
+ console.log('');
114
+ }
115
+ }
116
+ if (details.length > 6) {
117
+ info(dim(`…and ${details.length - 6} more build error(s)`));
118
+ }
119
+ if (data.missing_assets?.length) {
120
+ warn(`Missing assets referenced by the build: ${data.missing_assets.slice(0, 10).join(', ')}${data.missing_assets.length > 10 ? ', …' : ''}`);
121
+ }
122
+ if (err.hint)
123
+ info(dim(err.hint));
124
+ if (err.code === 'BUILD_ERROR') {
125
+ info(dim(`Nothing from this deploy is live. Fix the error and redeploy. [BUILD_ERROR, HTTP ${err.statusCode}]`));
126
+ }
127
+ else {
128
+ // Non-BUILD_ERROR codes (DEPLOY_BLANK_PAGE) carry context the frame
129
+ // doesn't repeat — rollback state, false-alarm guidance. Keep it.
130
+ info(dim(`${err.message} [${err.code}, HTTP ${err.statusCode}]`));
131
+ }
132
+ return true;
133
+ }
134
+ /** Yellow single-line summary for watch-mode (somewhere dev) reuse. */
135
+ export function buildErrorSummary(err) {
136
+ const data = (err.data ?? {});
137
+ let first = Array.isArray(data.errors) && data.errors.length > 0
138
+ ? data.errors[0]
139
+ : data.file !== undefined || data.error !== undefined
140
+ ? { file: data.file, line: data.line, column: data.column, message: data.error }
141
+ : undefined;
142
+ if (!first && typeof data.bundle_error === 'string') {
143
+ first = parseBundleErrorText(data.bundle_error)[0];
144
+ }
145
+ return first ? location(first) : err.message;
146
+ }
147
+ //# sourceMappingURL=build-errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-errors.js","sourceRoot":"","sources":["../../src/lib/build-errors.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,17 @@
1
+ import { Agent } from 'undici';
1
2
  const BASE_URL = 'https://api.somewhere.tech/v1';
3
+ /** Default request timeout. Long-running calls (deploy compiles, pulls)
4
+ * pass an explicit budget via opts.timeoutMs instead. */
5
+ const DEFAULT_TIMEOUT_MS = 60_000;
6
+ /** Budget for calls that run a real build server-side (npm install alone
7
+ * can take minutes): /deploy, /deploy/patch, pull/export. */
8
+ export const LONG_CALL_TIMEOUT_MS = 10 * 60_000;
2
9
  export class ApiClient {
3
10
  token;
4
11
  constructor(token) {
5
12
  this.token = token;
6
13
  }
7
- async call(method, path, body, query) {
14
+ async call(method, path, body, query, opts) {
8
15
  let url = `${BASE_URL}${path}`;
9
16
  if (query) {
10
17
  const params = new URLSearchParams();
@@ -25,7 +32,57 @@ export class ApiClient {
25
32
  headers['Content-Type'] = 'application/json';
26
33
  reqBody = JSON.stringify(body);
27
34
  }
28
- const res = await fetch(url, { method, headers, body: reqBody });
35
+ const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
36
+ let res;
37
+ try {
38
+ res = await fetch(url, {
39
+ method,
40
+ headers,
41
+ body: reqBody,
42
+ signal: AbortSignal.timeout(timeoutMs),
43
+ // Match undici's internal header/body timeouts to OUR budget. Node's
44
+ // global fetch defaults headersTimeout to ~300s — SHORTER than a long
45
+ // deploy's 10-min budget — so a cold first deploy (while the project is
46
+ // provisioned) tripped UND_ERR_HEADERS_TIMEOUT before our AbortSignal
47
+ // fired, and the failure got mislabeled as the user's network
48
+ // (tsk_896f9c7b). Now our AbortSignal is the only real deadline.
49
+ dispatcher: new Agent({ headersTimeout: timeoutMs, bodyTimeout: timeoutMs }),
50
+ });
51
+ }
52
+ catch (err) {
53
+ // AbortSignal.timeout fired — no bytes back within the budget. Name it
54
+ // so the user can tell a hang from a rejection (review F8/Q5: a bare
55
+ // fetch hung `somewhere deploy` forever on network silence).
56
+ if (err instanceof Error && (err.name === 'TimeoutError' || err.name === 'AbortError')) {
57
+ throw new CliApiError('TIMEOUT', `No response from ${method} ${url.split('?')[0]} after ${Math.round(timeoutMs / 1000)}s. ` +
58
+ 'The request may still be processing server-side — check `somewhere status` before retrying a deploy.', 0);
59
+ }
60
+ // Node's fetch rejects network-level failures with a bare
61
+ // "fetch failed" and buries the real reason (ENOTFOUND,
62
+ // ECONNRESET, UND_ERR_*) in err.cause. Surfacing only the bare
63
+ // message cost a customer an hour of guessing whether it was
64
+ // auth, payload, or network (pfb_70e9d140c5a0) — name the
65
+ // endpoint and the underlying cause.
66
+ const cause = err.cause;
67
+ const code = cause?.code;
68
+ // undici's own header/body timeout means the CONNECTION established but the
69
+ // server was slow to respond — NOT the user's network. Common on a first
70
+ // deploy while the project is provisioned (tsk_896f9c7b). With the matching
71
+ // dispatcher above this should rarely fire now, but if it does, say what's
72
+ // actually happening instead of blaming the network.
73
+ if (code === 'UND_ERR_HEADERS_TIMEOUT' || code === 'UND_ERR_BODY_TIMEOUT') {
74
+ throw new CliApiError('SERVER_SLOW', `${method} ${url.split('?')[0]} connected, but the server didn't finish responding in time. ` +
75
+ 'This is usually a first deploy while your project is being provisioned — the request may ' +
76
+ 'still be completing server-side. Check `somewhere status` before retrying ' +
77
+ '(retrying a first deploy is safe — it just creates a new version).', 0);
78
+ }
79
+ const detail = code ??
80
+ cause?.message ??
81
+ (err instanceof Error ? err.message : String(err));
82
+ throw new CliApiError('NETWORK_ERROR', `Could not reach ${method} ${url.split('?')[0]} — ${detail}. ` +
83
+ 'No HTTP response was received (this is a connection problem, not a server rejection). ' +
84
+ 'Check your network and retry; for large deploys a flaky connection can drop the upload mid-request.', 0);
85
+ }
29
86
  const text = await res.text();
30
87
  let parsed;
31
88
  try {
@@ -36,16 +93,23 @@ export class ApiClient {
36
93
  }
37
94
  if (parsed.ok === true)
38
95
  return parsed.data;
39
- throw new CliApiError(parsed.error ?? 'UNKNOWN', parsed.message ?? 'Unknown error', res.status);
96
+ const errBody = parsed;
97
+ throw new CliApiError(errBody.error ?? 'UNKNOWN', errBody.message ?? 'Unknown error', res.status, errBody.data, errBody.hint);
40
98
  }
41
99
  }
42
100
  export class CliApiError extends Error {
43
101
  code;
44
102
  statusCode;
45
- constructor(code, message, statusCode) {
103
+ data;
104
+ hint;
105
+ constructor(code, message, statusCode,
106
+ /** Structured payload some errors carry (e.g. BUILD_ERROR file/line/frame). */
107
+ data, hint) {
46
108
  super(message);
47
109
  this.code = code;
48
110
  this.statusCode = statusCode;
111
+ this.data = data;
112
+ this.hint = hint;
49
113
  this.name = 'CliApiError';
50
114
  }
51
115
  }
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/lib/client.ts"],"names":[],"mappings":"AAEA,MAAM,QAAQ,GAAG,+BAA+B,CAAC;AAEjD,MAAM,OAAO,SAAS;IACS;IAA7B,YAA6B,KAAa;QAAb,UAAK,GAAL,KAAK,CAAQ;IAAG,CAAC;IAE9C,KAAK,CAAC,IAAI,CACR,MAAc,EACd,IAAY,EACZ,IAAc,EACd,KAAmD;QAEnD,IAAI,GAAG,GAAG,GAAG,QAAQ,GAAG,IAAI,EAAE,CAAC;QAC/B,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;YACrC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3C,IAAI,CAAC,KAAK,SAAS;oBAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YACnD,CAAC;YACD,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;YAC7B,IAAI,EAAE;gBAAE,GAAG,IAAI,IAAI,EAAE,EAAE,CAAC;QAC1B,CAAC;QAED,MAAM,OAAO,GAA2B;YACtC,aAAa,EAAE,UAAU,IAAI,CAAC,KAAK,EAAE;YACrC,MAAM,EAAE,kBAAkB;SAC3B,CAAC;QAEF,IAAI,OAA2B,CAAC;QAChC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;YAC7C,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACjC,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QACjE,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,IAAI,MAAsB,CAAC;QAE3B,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAmB,CAAC;QAC9C,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,WAAW,CACnB,kBAAkB,EAClB,sBAAsB,GAAG,CAAC,MAAM,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAC1D,GAAG,CAAC,MAAM,CACX,CAAC;QACJ,CAAC;QAED,IAAI,MAAM,CAAC,EAAE,KAAK,IAAI;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC;QAE3C,MAAM,IAAI,WAAW,CAClB,MAA6B,CAAC,KAAK,IAAI,SAAS,EAChD,MAA+B,CAAC,OAAO,IAAI,eAAe,EAC3D,GAAG,CAAC,MAAM,CACX,CAAC;IACJ,CAAC;CACF;AAED,MAAM,OAAO,WAAY,SAAQ,KAAK;IAElB;IAEA;IAHlB,YACkB,IAAY,EAC5B,OAAe,EACC,UAAkB;QAElC,KAAK,CAAC,OAAO,CAAC,CAAC;QAJC,SAAI,GAAJ,IAAI,CAAQ;QAEZ,eAAU,GAAV,UAAU,CAAQ;QAGlC,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/lib/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Minimal .env parser for local dev. Values from .env / .env.local in the
3
+ * project root feed sw.env when running functions locally — these files are
4
+ * in the deploy IGNORE list, so nothing here ever leaves the machine.
5
+ */
6
+ import { existsSync, readFileSync } from 'node:fs';
7
+ import { join } from 'node:path';
8
+ export function parseEnvFile(content) {
9
+ const out = {};
10
+ for (const rawLine of content.split('\n')) {
11
+ const line = rawLine.trim();
12
+ if (!line || line.startsWith('#'))
13
+ continue;
14
+ const eq = line.indexOf('=');
15
+ if (eq <= 0)
16
+ continue;
17
+ const key = line.slice(0, eq).trim().replace(/^export\s+/, '');
18
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
19
+ continue;
20
+ let value = line.slice(eq + 1).trim();
21
+ if ((value.startsWith('"') && value.endsWith('"')) ||
22
+ (value.startsWith("'") && value.endsWith("'"))) {
23
+ value = value.slice(1, -1);
24
+ }
25
+ out[key] = value;
26
+ }
27
+ return out;
28
+ }
29
+ /** Load .env then .env.local (local wins), from the project directory. */
30
+ export function loadLocalEnv(dir) {
31
+ const merged = {};
32
+ for (const name of ['.env', '.env.local']) {
33
+ const path = join(dir, name);
34
+ if (!existsSync(path))
35
+ continue;
36
+ Object.assign(merged, parseEnvFile(readFileSync(path, 'utf-8')));
37
+ }
38
+ return merged;
39
+ }
40
+ //# sourceMappingURL=envfile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envfile.js","sourceRoot":"","sources":["../../src/local/envfile.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAC5C,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,EAAE,IAAI,CAAC;YAAE,SAAS;QACtB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;QAC/D,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,SAAS;QACpD,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACtC,IACE,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC9C,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAC9C,CAAC;YACD,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7B,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACnB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,IAAI,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC7B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,SAAS;QAChC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Module loading for the local function runtime.
3
+ *
4
+ * Two jobs the deployed runtime gets for free from the platform compiler:
5
+ *
6
+ * 1. Extensionless cross-file imports — `import { db } from '../_lib/db'`
7
+ * works deployed; Node ESM requires extensions. A resolve hook tries the
8
+ * same candidates the platform accepts (.ts/.tsx/.mts/.js/.mjs/.jsx and
9
+ * directory index files) for relative imports originating inside the
10
+ * project.
11
+ *
12
+ * 2. Hot reload — ESM has no cache invalidation, so every project-local file
13
+ * URL carries a ?gen=N query. Bumping the generation makes the next
14
+ * import() re-evaluate the whole project module graph; the hook propagates
15
+ * the parent's generation to children so nothing stale survives a save.
16
+ *
17
+ * TypeScript runs through Node's native type stripping (>= 22.18 / 23.6).
18
+ */
19
+ import { registerHooks } from 'node:module';
20
+ import { existsSync, statSync } from 'node:fs';
21
+ import { dirname, resolve as resolvePath } from 'node:path';
22
+ import { fileURLToPath, pathToFileURL } from 'node:url';
23
+ const EXT_CANDIDATES = ['.ts', '.tsx', '.mts', '.js', '.mjs', '.jsx'];
24
+ let generation = 1;
25
+ let installedRoot = null;
26
+ export function bumpGeneration() {
27
+ return ++generation;
28
+ }
29
+ /** file:// URL for a project entry file, stamped with the current generation. */
30
+ export function entryUrl(absPath) {
31
+ return `${pathToFileURL(absPath).href}?gen=${generation}`;
32
+ }
33
+ /**
34
+ * Verify the running Node supports what the local runtime needs:
35
+ * module.registerHooks + unflagged TypeScript type stripping.
36
+ */
37
+ export function assertNodeSupport() {
38
+ const [major, minor] = process.versions.node.split('.').map(Number);
39
+ const ok = major >= 24 || (major === 23 && minor >= 6) || (major === 22 && minor >= 18);
40
+ if (!ok) {
41
+ throw new Error(`somewhere dev --local needs Node >= 22.18 (or >= 23.6) for TypeScript support and module hooks — you're on ${process.versions.node}. Upgrade Node and retry.`);
42
+ }
43
+ }
44
+ function isFile(p) {
45
+ try {
46
+ return existsSync(p) && statSync(p).isFile();
47
+ }
48
+ catch {
49
+ return false;
50
+ }
51
+ }
52
+ function resolveWithExtensions(base) {
53
+ if (isFile(base))
54
+ return base;
55
+ for (const ext of EXT_CANDIDATES) {
56
+ if (isFile(base + ext))
57
+ return base + ext;
58
+ }
59
+ for (const ext of EXT_CANDIDATES) {
60
+ const idx = resolvePath(base, 'index' + ext);
61
+ if (isFile(idx))
62
+ return idx;
63
+ }
64
+ return null;
65
+ }
66
+ /**
67
+ * Install the resolve hook for project files under `projectRoot`.
68
+ * Safe to call once per process; subsequent calls just update the root.
69
+ */
70
+ export function installLoader(projectRoot) {
71
+ const root = resolvePath(projectRoot);
72
+ if (installedRoot !== null) {
73
+ installedRoot = root;
74
+ return;
75
+ }
76
+ installedRoot = root;
77
+ registerHooks({
78
+ resolve(specifier, context, nextResolve) {
79
+ const parent = context.parentURL;
80
+ if (!installedRoot ||
81
+ !parent ||
82
+ !parent.startsWith('file://') ||
83
+ (!specifier.startsWith('./') && !specifier.startsWith('../'))) {
84
+ return nextResolve(specifier, context);
85
+ }
86
+ const parentUrl = new URL(parent);
87
+ const parentPath = fileURLToPath(`file://${parentUrl.pathname}`);
88
+ if (!parentPath.startsWith(installedRoot)) {
89
+ return nextResolve(specifier, context);
90
+ }
91
+ const target = resolvePath(dirname(parentPath), specifier);
92
+ const found = resolveWithExtensions(target);
93
+ if (!found) {
94
+ // Let Node produce its standard (clear) ERR_MODULE_NOT_FOUND.
95
+ return nextResolve(specifier, context);
96
+ }
97
+ const gen = parentUrl.searchParams.get('gen');
98
+ const url = pathToFileURL(found).href + (gen ? `?gen=${gen}` : '');
99
+ return { url, shortCircuit: true };
100
+ },
101
+ });
102
+ }
103
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/local/loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * File-based routing for the local function runtime — a faithful port of the
3
+ * deploy pipeline's route compiler + matcher (worker/src/utils/function-bundle.ts:
4
+ * compileRoutePattern / routeSpecificityKey / matchRoute), so a URL resolves to
5
+ * the same function locally as it does deployed.
6
+ *
7
+ * api/hello.ts → /api/hello (static)
8
+ * api/sites/[id].ts → /api/sites/:id (param)
9
+ * api/files/[...path].ts → /api/files/*path (catch-all)
10
+ * [...path].ts (root) → /*path (root catch-all)
11
+ *
12
+ * Static beats param beats rest. `_lib/` and plain root files are bundled for
13
+ * imports but never routed.
14
+ */
15
+ export function compileRoutePattern(file) {
16
+ const noExt = file.replace(/\.(ts|tsx|mts|js|mjs|jsx)$/, '');
17
+ const parts = noExt.split('/');
18
+ const pattern = [];
19
+ const display = [];
20
+ parts.forEach((seg, i) => {
21
+ const rest = seg.match(/^\[\.\.\.([a-zA-Z_][a-zA-Z0-9_]*)\]$/);
22
+ if (rest) {
23
+ if (i !== parts.length - 1) {
24
+ throw new Error(`catch-all [...${rest[1]}] must be the last path segment: ${file}`);
25
+ }
26
+ pattern.push({ type: 'rest', value: rest[1] });
27
+ display.push(`*${rest[1]}`);
28
+ return;
29
+ }
30
+ const param = seg.match(/^\[([a-zA-Z_][a-zA-Z0-9_]*)\]$/);
31
+ if (param) {
32
+ pattern.push({ type: 'param', value: param[1] });
33
+ display.push(`:${param[1]}`);
34
+ return;
35
+ }
36
+ if (/[\[\]]/.test(seg)) {
37
+ throw new Error(`malformed parametric segment "${seg}" in ${file}. Use [name] or [...name].`);
38
+ }
39
+ pattern.push({ type: 'static', value: seg });
40
+ display.push(seg);
41
+ });
42
+ return { pattern, displayPath: '/' + display.join('/') };
43
+ }
44
+ function routeSpecificityKey(pattern) {
45
+ return pattern.map((s) => (s.type === 'static' ? '2' : s.type === 'param' ? '1' : '0')).join('');
46
+ }
47
+ function hasRestSegment(pattern) {
48
+ return pattern.some((s) => s.type === 'rest');
49
+ }
50
+ export function isRoutable(file) {
51
+ const isRootParametric = !file.includes('/') && /\[/.test(file);
52
+ return file.startsWith('api/') || isRootParametric;
53
+ }
54
+ /**
55
+ * Compile + sort routes most-specific first, mirroring buildFunctionBundle:
56
+ * non-rest beats rest; within a group, segment-wise static > param wins.
57
+ * Throws on two files compiling to the same route (same deploy-time error).
58
+ */
59
+ export function compileRoutes(files) {
60
+ const routes = [];
61
+ for (const { file, absPath } of files) {
62
+ if (!isRoutable(file))
63
+ continue;
64
+ const { pattern, displayPath } = compileRoutePattern(file);
65
+ routes.push({ file, absPath, pattern, displayPath });
66
+ }
67
+ const seen = new Map();
68
+ for (const r of routes) {
69
+ const prior = seen.get(r.displayPath);
70
+ if (prior) {
71
+ throw new Error(`Two functions compile to the same route "${r.displayPath}": ${prior} and ${r.file}`);
72
+ }
73
+ seen.set(r.displayPath, r.file);
74
+ }
75
+ routes.sort((a, b) => {
76
+ const aRest = hasRestSegment(a.pattern);
77
+ const bRest = hasRestSegment(b.pattern);
78
+ if (aRest !== bRest)
79
+ return aRest ? 1 : -1;
80
+ const ka = routeSpecificityKey(a.pattern);
81
+ const kb = routeSpecificityKey(b.pattern);
82
+ return kb.localeCompare(ka);
83
+ });
84
+ return routes;
85
+ }
86
+ /** Walk routes in order and return the first match — port of the shim's matchRoute. */
87
+ export function matchRoute(routes, pathname) {
88
+ const urlSegs = pathname.split('/').filter(Boolean);
89
+ outer: for (const route of routes) {
90
+ const params = {};
91
+ const pat = route.pattern;
92
+ for (let i = 0; i < pat.length; i++) {
93
+ const p = pat[i];
94
+ if (p.type === 'rest') {
95
+ params[p.value] = urlSegs.slice(i).map(decodeURIComponent).join('/');
96
+ return { route, params };
97
+ }
98
+ if (i >= urlSegs.length)
99
+ continue outer;
100
+ if (p.type === 'static') {
101
+ if (urlSegs[i] !== p.value)
102
+ continue outer;
103
+ }
104
+ else {
105
+ params[p.value] = decodeURIComponent(urlSegs[i]);
106
+ }
107
+ }
108
+ if (pat.length === urlSegs.length) {
109
+ return { route, params };
110
+ }
111
+ }
112
+ return null;
113
+ }
114
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/local/router.ts"],"names":[],"mappings":"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"}