spfn 0.3.0-beta.6 → 0.3.0-beta.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -948,7 +948,7 @@ import { dirname as dirname2, join as join10 } from "path";
948
948
  import { fileURLToPath as fileURLToPath2 } from "url";
949
949
  function getCliVersion() {
950
950
  if (true) {
951
- return "0.3.0-beta.6";
951
+ return "0.3.0-beta.8";
952
952
  }
953
953
  return fromPackageJson ?? (fromPackageJson = readPackageVersion());
954
954
  }
@@ -1169,14 +1169,6 @@ import { randomBytes } from "crypto";
1169
1169
  import { spawnSync } from "child_process";
1170
1170
  import { join as join12 } from "path";
1171
1171
  import fse8 from "fs-extra";
1172
- function envExampleTemplate(mode) {
1173
- return withPlaceholderCreds(`# Example environment \u2014 committed reference for the variables SPFN uses.
1174
- # Real values live in .env.local (Next.js) and .env.server (backend secrets),
1175
- # both gitignored. This file documents the keys; it is not loaded by anything.
1176
-
1177
- ${ENV_LOCAL_TEMPLATE}${mode === "full" ? FULL_ENV_LOCAL_EXAMPLE : ""}
1178
- ${ENV_SERVER_TEMPLATE}${mode === "full" ? FULL_ENV_SERVER_EXAMPLE : ""}`);
1179
- }
1180
1172
  function envServerExampleTemplate(mode) {
1181
1173
  return withPlaceholderCreds(
1182
1174
  `${ENV_SERVER_TEMPLATE}${mode === "full" ? FULL_ENV_SERVER_EXAMPLE : ""}`
@@ -1190,11 +1182,14 @@ function envLocalTemplate(mode) {
1190
1182
  # Full scaffold: auth session encryption (Next.js server only; never NEXT_PUBLIC_*)
1191
1183
  SPFN_AUTH_SESSION_SECRET=${randomSecret("base64url")}
1192
1184
  SPFN_AUTH_SESSION_TTL=7d
1185
+ ${CSRF_ENV_LINES}
1193
1186
  NEXT_PUBLIC_SPFN_APP_URL=http://localhost:3790
1194
1187
  `;
1195
1188
  }
1196
1189
  function envLocalExampleTemplate(mode) {
1197
- return `${ENV_LOCAL_TEMPLATE}${mode === "full" ? FULL_ENV_LOCAL_EXAMPLE : ""}`;
1190
+ return withPlaceholderCreds(
1191
+ `${ENV_LOCAL_TEMPLATE}${mode === "full" ? FULL_ENV_LOCAL_EXAMPLE : ""}`
1192
+ );
1198
1193
  }
1199
1194
  function envServerTemplate(mode) {
1200
1195
  if (mode === "bare") {
@@ -1260,20 +1255,16 @@ export default defineConfig({
1260
1255
  updateTsconfig(cwd);
1261
1256
  }
1262
1257
  function generateEnvFiles(cwd, mode) {
1263
- writeLocalEnv(cwd, envLocalTemplate(mode), envLocalExampleTemplate(mode));
1264
- writeServerEnv(cwd, envServerTemplate(mode), envServerExampleTemplate(mode));
1265
- writeExampleEnv(cwd, envExampleTemplate(mode));
1258
+ writeReferenceEnv(cwd, ".env.local.example", envLocalExampleTemplate(mode));
1259
+ writeReferenceEnv(cwd, ".env.server.example", envServerExampleTemplate(mode));
1260
+ writeLocalEnv(cwd, envLocalTemplate(mode));
1261
+ writeServerEnv(cwd, envServerTemplate(mode));
1266
1262
  }
1267
- function writeLocalEnv(cwd, template, exampleTemplate) {
1263
+ function writeLocalEnv(cwd, template) {
1268
1264
  const filename = ".env.local";
1269
1265
  const filePath = join12(cwd, filename);
1270
1266
  if (isGitTracked(cwd, filename)) {
1271
- const referenceName = ".env.local.spfn.example";
1272
- const referencePath = join12(cwd, referenceName);
1273
- if (!existsSync12(referencePath)) {
1274
- writeFileSync8(referencePath, exampleTemplate);
1275
- }
1276
- logger.warn(`${filename} is tracked by Git \u2014 left it untouched; wrote ${referenceName}. Untrack ${filename}, then add the missing keys with freshly generated values`);
1267
+ logger.warn(`${filename} is tracked by Git \u2014 left it untouched; its keys are listed in .env.local.example. Untrack ${filename}, then add the missing keys with freshly generated values`);
1277
1268
  return;
1278
1269
  }
1279
1270
  writeEnvFile(cwd, filename, template);
@@ -1286,7 +1277,7 @@ function isGitTracked(cwd, filename) {
1286
1277
  });
1287
1278
  return result.status === 0;
1288
1279
  }
1289
- function writeServerEnv(cwd, template, exampleTemplate) {
1280
+ function writeServerEnv(cwd, template) {
1290
1281
  const filePath = join12(cwd, ".env.server");
1291
1282
  if (!existsSync12(filePath)) {
1292
1283
  writeFileSync8(filePath, template);
@@ -1294,16 +1285,15 @@ function writeServerEnv(cwd, template, exampleTemplate) {
1294
1285
  logger.success("Created .env.server");
1295
1286
  return;
1296
1287
  }
1297
- writeFileSync8(join12(cwd, ".env.server.example"), exampleTemplate);
1298
- logger.warn(".env.server already exists \u2014 left it untouched; wrote SPFN's reference to .env.server.example, add any missing keys manually");
1288
+ logger.warn(".env.server already exists \u2014 left it untouched; SPFN's reference is in .env.server.example, add any missing keys manually");
1299
1289
  }
1300
- function writeExampleEnv(cwd, content) {
1301
- const filePath = join12(cwd, ".env.example");
1290
+ function writeReferenceEnv(cwd, filename, content) {
1291
+ const filePath = join12(cwd, filename);
1302
1292
  if (existsSync12(filePath)) {
1303
1293
  return;
1304
1294
  }
1305
1295
  writeFileSync8(filePath, content);
1306
- logger.success("Created .env.example (committed reference)");
1296
+ logger.success(`Created ${filename} (committed reference)`);
1307
1297
  }
1308
1298
  function writeEnvFile(cwd, filename, content) {
1309
1299
  const filePath = join12(cwd, filename);
@@ -1361,8 +1351,11 @@ function pendingIgnoreRules(content) {
1361
1351
  if (!gitignoreCovers(lines, ".env.server")) {
1362
1352
  rules.push("\n# spfn server env (secrets)\n.env.server\n");
1363
1353
  }
1364
- if (!lines.some((line) => line.trim() === "!.env.example")) {
1365
- rules.push("\n# spfn env reference (committed)\n!.env.example\n");
1354
+ if (!lines.some((line) => line.trim() === "!.env.local.example")) {
1355
+ rules.push("\n# spfn env reference, Next.js keys (committed)\n!.env.local.example\n");
1356
+ }
1357
+ if (!lines.some((line) => line.trim() === "!.env.server.example")) {
1358
+ rules.push("\n# spfn env reference, backend keys (committed)\n!.env.server.example\n");
1366
1359
  }
1367
1360
  return rules;
1368
1361
  }
@@ -1386,7 +1379,7 @@ function updateTsconfig(cwd) {
1386
1379
  logger.warn('Could not update tsconfig.json (you can add "src/server" to exclude manually)');
1387
1380
  }
1388
1381
  }
1389
- var writeFileSync8, ENV_LOCAL_TEMPLATE, FULL_ENV_LOCAL_EXAMPLE, ENV_SERVER_TEMPLATE, FULL_ENV_SERVER_EXAMPLE;
1382
+ var writeFileSync8, ENV_LOCAL_TEMPLATE, CSRF_ENV_LINES, FULL_ENV_LOCAL_EXAMPLE, ENV_SERVER_TEMPLATE, FULL_ENV_SERVER_EXAMPLE;
1390
1383
  var init_config_files = __esm({
1391
1384
  "src/commands/init/steps/config-files.ts"() {
1392
1385
  "use strict";
@@ -1404,10 +1397,13 @@ NEXT_PUBLIC_SPFN_API_URL=http://localhost:8790
1404
1397
  # Next.js app URL (used by the SPFN server for CORS/redirects)
1405
1398
  SPFN_APP_URL=http://localhost:3790
1406
1399
  `;
1400
+ CSRF_ENV_LINES = `# CSRF for cookie-session mutations: off | warn | enforce (unset = warn)
1401
+ SPFN_AUTH_CSRF=enforce`;
1407
1402
  FULL_ENV_LOCAL_EXAMPLE = `
1408
1403
  # Full scaffold: auth session encryption (Next.js server only; never NEXT_PUBLIC_*)
1409
1404
  SPFN_AUTH_SESSION_SECRET=replace-with-a-random-secret-at-least-32-characters
1410
1405
  SPFN_AUTH_SESSION_TTL=7d
1406
+ ${CSRF_ENV_LINES}
1411
1407
  NEXT_PUBLIC_SPFN_APP_URL=http://localhost:3790
1412
1408
  `;
1413
1409
  ENV_SERVER_TEMPLATE = `# SPFN backend environment
@@ -1981,16 +1977,182 @@ init_init();
1981
1977
 
1982
1978
  // src/commands/dev.ts
1983
1979
  init_logger();
1984
- init_package_manager();
1985
1980
  import { Command as Command4 } from "commander";
1986
- import { existsSync as existsSync15, readFileSync as readFileSync8, writeFileSync as writeFileSync10, mkdirSync, unlinkSync, rmSync, watch } from "fs";
1987
- import { join as join16, relative, sep as sep2 } from "path";
1981
+ import { existsSync as existsSync16, readFileSync as readFileSync9, writeFileSync as writeFileSync11, mkdirSync, unlinkSync, rmSync, watch } from "fs";
1982
+ import { join as join17, relative, sep as sep2 } from "path";
1988
1983
  import { execa as execa6 } from "execa";
1989
1984
  import chokidar from "chokidar";
1990
1985
 
1986
+ // src/lib/agent-instructions.ts
1987
+ import { existsSync as existsSync15, readFileSync as readFileSync8, writeFileSync as writeFileSync10 } from "fs";
1988
+ import { join as join15 } from "path";
1989
+
1990
+ // src/lib/agent-instructions-template.ts
1991
+ init_version();
1992
+ var BEGIN_MARKER = "<!-- spfn:agent-instructions:begin \u2014 generated by spfn dev; edits inside are overwritten -->";
1993
+ var END_MARKER = "<!-- spfn:agent-instructions:end -->";
1994
+ function renderAgentInstructions(version = getCliVersion()) {
1995
+ return `${BEGIN_MARKER}
1996
+ ## SPFN conventions for coding agents
1997
+
1998
+ This is not the stack you know. An SPFN app looks like Next.js plus Drizzle, but
1999
+ its idioms are not the ecosystem defaults, and code written from general priors
2000
+ is wrong here. Read the docs below before writing server, data or auth code.
2001
+
2002
+ **Data access.** Every query goes through a repository extending
2003
+ \`BaseRepository\` \u2014 \`this.db\` writes, \`this.readDb\` reads. Handlers and
2004
+ services never import drizzle query builders directly.
2005
+
2006
+ \`\`\`typescript
2007
+ import { BaseRepository } from '@spfn/core/db';
2008
+ import { orders } from '../entities/order.entity';
2009
+
2010
+ export class OrderRepository extends BaseRepository
2011
+ {
2012
+ async findByCustomer(customerId: bigint)
2013
+ {
2014
+ return this._findMany(orders, { where: { customerId } });
2015
+ }
2016
+ }
2017
+ \`\`\`
2018
+
2019
+ The scaffold's \`src/server/repositories/\` shows the shape.
2020
+
2021
+ **Transactions.** Wrap the route in \`Transactional()\`, or call
2022
+ \`runInTransaction\` in a service or script. Never accept or pass a \`tx\`
2023
+ parameter: repositories pick the transaction up from the async context. Side
2024
+ effects that must not fire on a rollback go in \`onAfterCommit(...)\`.
2025
+
2026
+ **Auth.** Read the principal with \`getAuth(c)\` from \`@spfn/auth/server\`.
2027
+ Authorization is declared route middleware \u2014
2028
+ \`.use([authenticate, requirePermissions('order:write')])\` \u2014 never an ad-hoc
2029
+ check inside the handler body.
2030
+
2031
+ **Frontend data.** A page's initial data is awaited in a Server Component
2032
+ through the isomorphic \`api.\` client; cookies forward automatically. Fetching
2033
+ initial data from a \`'use client'\` component in \`useEffect\` is the
2034
+ anti-pattern. Mutations go through server actions, or the same \`api.\` client
2035
+ from a Client Component.
2036
+
2037
+ **Generated files.** Route maps under \`src/generated/\` come from
2038
+ \`pnpm codegen\` \u2014 never hand-edit them. Migrations come from
2039
+ \`spfn db generate\` off the entities; never hand-write migration SQL.
2040
+
2041
+ **Read before writing.** \`node_modules/@spfn/core/README.md\`,
2042
+ \`node_modules/@spfn/core/src/db/README.md\`, \`node_modules/@spfn/auth/README.md\`.
2043
+ Online: \`https://superfunction.xyz/docs/packages/core/<module>\` for cache,
2044
+ codegen, config, db, env, errors, event, job, logger, middleware, nextjs, route,
2045
+ security and server; \`https://superfunction.xyz/docs/guides/<guide>\` for
2046
+ authentication, custom-generators, deployment, file-upload, monitoring and
2047
+ websockets. Do not guess other paths \u2014 they 404.
2048
+
2049
+ <!-- spfn CLI v${version} -->
2050
+ ${END_MARKER}`;
2051
+ }
2052
+
2053
+ // src/lib/agent-instructions.ts
2054
+ var AGENTS_FILE = "AGENTS.md";
2055
+ var CLAUDE_FILE = "CLAUDE.md";
2056
+ function syncAgentInstructions(projectRoot, options = {}) {
2057
+ if (isOptedOut(options)) {
2058
+ return { action: "skipped" };
2059
+ }
2060
+ const agentsPath = join15(projectRoot, AGENTS_FILE);
2061
+ const block = renderAgentInstructions();
2062
+ if (!existsSync15(agentsPath)) {
2063
+ writeFileSync10(agentsPath, `${block}
2064
+ `, "utf8");
2065
+ ensureClaudeReference(projectRoot);
2066
+ return { action: "created" };
2067
+ }
2068
+ const content = withoutByteOrderMark(readFileSync8(agentsPath, "utf8"));
2069
+ const lines = content.split("\n");
2070
+ const location = locateBlock(lines);
2071
+ if (location.kind === "malformed") {
2072
+ return {
2073
+ action: "skipped",
2074
+ warning: `${AGENTS_FILE} has a malformed spfn block \u2014 expected exactly one "${BEGIN_MARKER}" line followed by one "${END_MARKER}" line. Fix or delete the markers; nothing was written.`
2075
+ };
2076
+ }
2077
+ ensureClaudeReference(projectRoot);
2078
+ if (location.kind === "absent") {
2079
+ const eol = content.includes("\r\n") ? "\r\n" : "\n";
2080
+ const existing = content.replace(/(\r?\n)+$/, "");
2081
+ const separator = existing.length > 0 ? `${eol}${eol}` : "";
2082
+ writeFileSync10(agentsPath, `${existing}${separator}${block.split("\n").join(eol)}${eol}`, "utf8");
2083
+ return { action: "appended" };
2084
+ }
2085
+ const carriageReturn = lines[location.begin].endsWith("\r") ? "\r" : "";
2086
+ const blockLines = block.split("\n").map((line) => line + carriageReturn);
2087
+ const currentLines = lines.slice(location.begin, location.end + 1);
2088
+ if (currentLines.length === blockLines.length && currentLines.every((line, index) => line === blockLines[index])) {
2089
+ return { action: "unchanged" };
2090
+ }
2091
+ lines.splice(location.begin, location.end - location.begin + 1, ...blockLines);
2092
+ writeFileSync10(agentsPath, lines.join("\n"), "utf8");
2093
+ return { action: "replaced" };
2094
+ }
2095
+ function isOptedOut(options) {
2096
+ const fromEnv = (process.env.SPFN_AGENT_FILES ?? "").toLowerCase();
2097
+ return options.agentFiles === false || fromEnv === "0" || fromEnv === "false";
2098
+ }
2099
+ function locateBlock(lines) {
2100
+ const fenced = fencedLines(lines);
2101
+ const begins = lineIndicesOf(lines, BEGIN_MARKER, fenced);
2102
+ const ends = lineIndicesOf(lines, END_MARKER, fenced);
2103
+ if (begins.length === 0 && ends.length === 0) {
2104
+ return { kind: "absent" };
2105
+ }
2106
+ if (begins.length !== 1 || ends.length !== 1 || ends[0] < begins[0]) {
2107
+ return { kind: "malformed" };
2108
+ }
2109
+ return { kind: "found", begin: begins[0], end: ends[0] };
2110
+ }
2111
+ function lineIndicesOf(lines, marker, fenced) {
2112
+ const found = [];
2113
+ lines.forEach((line, index) => {
2114
+ if (!fenced[index] && withoutCarriageReturn(line) === marker) {
2115
+ found.push(index);
2116
+ }
2117
+ });
2118
+ return found;
2119
+ }
2120
+ var FENCE_LINE = /^\s*(`{3,}|~{3,})(.*)$/;
2121
+ function fencedLines(lines) {
2122
+ let open = null;
2123
+ return lines.map((rawLine) => {
2124
+ const fence = FENCE_LINE.exec(withoutCarriageReturn(rawLine));
2125
+ if (open === null) {
2126
+ open = fence ? { character: fence[1][0], length: fence[1].length } : null;
2127
+ return false;
2128
+ }
2129
+ if (fence && fence[1][0] === open.character && fence[1].length >= open.length && fence[2].trim() === "") {
2130
+ open = null;
2131
+ }
2132
+ return true;
2133
+ });
2134
+ }
2135
+ function withoutCarriageReturn(line) {
2136
+ return line.endsWith("\r") ? line.slice(0, -1) : line;
2137
+ }
2138
+ function withoutByteOrderMark(content) {
2139
+ return content.startsWith("\uFEFF") ? content.slice(1) : content;
2140
+ }
2141
+ function ensureClaudeReference(projectRoot) {
2142
+ const claudePath = join15(projectRoot, CLAUDE_FILE);
2143
+ if (existsSync15(claudePath)) {
2144
+ return;
2145
+ }
2146
+ writeFileSync10(claudePath, `@${AGENTS_FILE}
2147
+ `, "utf8");
2148
+ }
2149
+
2150
+ // src/commands/dev.ts
2151
+ init_package_manager();
2152
+
1991
2153
  // src/utils/secret-store/index.ts
1992
2154
  init_env_file();
1993
- import { join as join15 } from "path";
2155
+ import { join as join16 } from "path";
1994
2156
 
1995
2157
  // src/utils/secret-store/macos.ts
1996
2158
  import { execa as execa4 } from "execa";
@@ -2177,7 +2339,7 @@ function keychainRef(key) {
2177
2339
  return `${KEYCHAIN_REF_PREFIX}${keychainName(key)}`;
2178
2340
  }
2179
2341
  async function resolveKeychainEnv(cwd) {
2180
- const parsed = parseEnvFile(join15(cwd, ".env.server"));
2342
+ const parsed = parseEnvFile(join16(cwd, ".env.server"));
2181
2343
  const refs = Object.entries(parsed).filter(([, value]) => value.startsWith(KEYCHAIN_REF_PREFIX));
2182
2344
  if (refs.length === 0) {
2183
2345
  return { env: {}, missing: [] };
@@ -2205,33 +2367,45 @@ import { loadAppConfig, resolvePorts, resolveHost } from "@spfn/core/app-config"
2205
2367
  function ignoreDotfilesUnder(root) {
2206
2368
  return (watchedPath) => relative(root, watchedPath).split(sep2).some((segment) => segment.startsWith(".") && segment !== "." && segment !== "..");
2207
2369
  }
2370
+ function writeAgentInstructions(cwd, agentFiles) {
2371
+ try {
2372
+ const { action, warning } = syncAgentInstructions(cwd, { agentFiles });
2373
+ if (warning) {
2374
+ logger.warn(`[SPFN] ${warning}`);
2375
+ } else if (action !== "unchanged" && action !== "skipped") {
2376
+ logger.info(`[SPFN] Agent instructions ${action} in AGENTS.md`);
2377
+ }
2378
+ } catch (error) {
2379
+ logger.warn(`[SPFN] Could not write the agent instruction files (AGENTS.md, CLAUDE.md): ${error instanceof Error ? error.message : String(error)}`);
2380
+ }
2381
+ }
2208
2382
  function waitForReadyFile(filePath, timeoutMs = 3e4) {
2209
2383
  return new Promise((resolve4, reject) => {
2210
- if (existsSync15(filePath)) {
2384
+ if (existsSync16(filePath)) {
2211
2385
  unlinkSync(filePath);
2212
2386
  }
2213
2387
  const timer = setTimeout(() => {
2214
2388
  watcher.close();
2215
2389
  reject(new Error(`Server did not become ready within ${timeoutMs / 1e3}s`));
2216
2390
  }, timeoutMs);
2217
- const dir = join16(filePath, "..");
2391
+ const dir = join17(filePath, "..");
2218
2392
  const fileName = filePath.split("/").pop();
2219
2393
  const watcher = watch(dir, (_event, name) => {
2220
- if (name === fileName && existsSync15(filePath)) {
2394
+ if (name === fileName && existsSync16(filePath)) {
2221
2395
  watcher.close();
2222
2396
  clearTimeout(timer);
2223
- resolve4(readFileSync8(filePath, "utf-8").trim());
2397
+ resolve4(readFileSync9(filePath, "utf-8").trim());
2224
2398
  }
2225
2399
  });
2226
2400
  });
2227
2401
  }
2228
- var devCommand = new Command4("dev").description("Start SPFN development server (detects and runs Next.js + Hono)").option("-p, --port <port>", "Server port").option("-H, --host <host>", "Server host").option("--server-only", "Run only Hono server (skip Next.js)").option("--watch", "Enable hot reload (watch mode)").option("--allow-pending-migrations", "Start even when migrations are pending (they are listed as a warning)").action(async (options) => {
2402
+ var devCommand = new Command4("dev").description("Start SPFN development server (detects and runs Next.js + Hono)").option("-p, --port <port>", "Server port").option("-H, --host <host>", "Server host").option("--server-only", "Run only Hono server (skip Next.js)").option("--watch", "Enable hot reload (watch mode)").option("--allow-pending-migrations", "Start even when migrations are pending (they are listed as a warning)").option("--no-agent-files", "Do not write the SPFN instruction block into AGENTS.md (same as SPFN_AGENT_FILES=0)").action(async (options) => {
2229
2403
  process.setMaxListeners(20);
2230
2404
  if (!process.env.NODE_ENV) {
2231
2405
  process.env.NODE_ENV = "development";
2232
2406
  }
2233
2407
  const cwd = process.cwd();
2234
- const serverDir = join16(cwd, "src", "server");
2408
+ const serverDir = join17(cwd, "src", "server");
2235
2409
  if (options.port) {
2236
2410
  process.env.SPFN_PORT = String(options.port);
2237
2411
  }
@@ -2241,11 +2415,12 @@ var devCommand = new Command4("dev").description("Start SPFN development server
2241
2415
  const appConfig = await loadAppConfig(cwd);
2242
2416
  const { next: nextPort, server: serverPort } = resolvePorts(appConfig);
2243
2417
  const serverHost = resolveHost(appConfig);
2244
- if (!existsSync15(serverDir)) {
2418
+ if (!existsSync16(serverDir)) {
2245
2419
  logger.error("src/server directory not found.");
2246
2420
  logger.info('Run "spfn init" first to initialize SPFN in your project.');
2247
2421
  process.exit(1);
2248
2422
  }
2423
+ writeAgentInstructions(cwd, options.agentFiles !== false);
2249
2424
  const { env: keychainEnv, missing: keychainMissing } = await resolveKeychainEnv(cwd);
2250
2425
  const injectedCount = Object.keys(keychainEnv).length;
2251
2426
  if (injectedCount > 0) {
@@ -2267,28 +2442,28 @@ var devCommand = new Command4("dev").description("Start SPFN development server
2267
2442
  if (migrationCheck.allowPending) {
2268
2443
  serverEnv.SPFN_ALLOW_PENDING_MIGRATIONS = "true";
2269
2444
  }
2270
- const packageJsonPath = join16(cwd, "package.json");
2445
+ const packageJsonPath = join17(cwd, "package.json");
2271
2446
  let hasNext = false;
2272
- if (existsSync15(packageJsonPath)) {
2273
- const packageJson = JSON.parse(readFileSync8(packageJsonPath, "utf-8"));
2447
+ if (existsSync16(packageJsonPath)) {
2448
+ const packageJson = JSON.parse(readFileSync9(packageJsonPath, "utf-8"));
2274
2449
  hasNext = !!(packageJson.dependencies?.next || packageJson.devDependencies?.next);
2275
2450
  }
2276
- const tempDir = join16(cwd, ".spfn");
2277
- const serverEntry = join16(tempDir, "server.mjs");
2278
- const watcherEntry = join16(tempDir, "watcher.mjs");
2451
+ const tempDir = join17(cwd, ".spfn");
2452
+ const serverEntry = join17(tempDir, "server.mjs");
2453
+ const watcherEntry = join17(tempDir, "watcher.mjs");
2279
2454
  mkdirSync(tempDir, { recursive: true });
2280
- const serverBuildDir = join16(tempDir, "server");
2281
- if (existsSync15(serverBuildDir)) {
2455
+ const serverBuildDir = join17(tempDir, "server");
2456
+ if (existsSync16(serverBuildDir)) {
2282
2457
  rmSync(serverBuildDir, { recursive: true, force: true });
2283
2458
  logger.info("[SPFN] Cleaned stale build cache (.spfn/server)");
2284
2459
  }
2285
- const readySignal = join16(tempDir, "server-ready");
2286
- if (existsSync15(readySignal)) {
2460
+ const readySignal = join17(tempDir, "server-ready");
2461
+ if (existsSync16(readySignal)) {
2287
2462
  unlinkSync(readySignal);
2288
2463
  }
2289
2464
  const configParts = ["debug: true"];
2290
- const readyFile = join16(tempDir, "server-ready");
2291
- writeFileSync10(serverEntry, `
2465
+ const readyFile = join17(tempDir, "server-ready");
2466
+ writeFileSync11(serverEntry, `
2292
2467
  import { writeFileSync } from 'fs';
2293
2468
 
2294
2469
  // Load environment variables FIRST (before any imports that depend on them)
@@ -2304,7 +2479,7 @@ const instance = await startServer({
2304
2479
  // Signal ready with actual port
2305
2480
  writeFileSync(${JSON.stringify(readyFile)}, String(instance.config.port));
2306
2481
  `);
2307
- writeFileSync10(watcherEntry, `
2482
+ writeFileSync11(watcherEntry, `
2308
2483
  // Load environment variables
2309
2484
  // const { loadEnvFiles } = await import('@spfn/core/server');
2310
2485
  // loadEnvFiles();
@@ -2575,8 +2750,8 @@ catch (error)
2575
2750
  init_logger();
2576
2751
  init_package_manager();
2577
2752
  import { Command as Command5 } from "commander";
2578
- import { existsSync as existsSync16, writeFileSync as writeFileSync11, mkdirSync as mkdirSync2, readdirSync } from "fs";
2579
- import { join as join17 } from "path";
2753
+ import { existsSync as existsSync17, writeFileSync as writeFileSync12, mkdirSync as mkdirSync2, readdirSync } from "fs";
2754
+ import { join as join18 } from "path";
2580
2755
  import { execa as execa7 } from "execa";
2581
2756
  import ora5 from "ora";
2582
2757
  import chalk7 from "chalk";
@@ -2603,16 +2778,16 @@ async function buildProject(options) {
2603
2778
  }
2604
2779
  const cwd = process.cwd();
2605
2780
  const pm = detectPackageManager(cwd);
2606
- const packageJsonPath = join17(cwd, "package.json");
2781
+ const packageJsonPath = join18(cwd, "package.json");
2607
2782
  let hasNext = false;
2608
- if (existsSync16(packageJsonPath)) {
2783
+ if (existsSync17(packageJsonPath)) {
2609
2784
  const packageJson = JSON.parse(await import("fs").then(
2610
2785
  (fs5) => fs5.promises.readFile(packageJsonPath, "utf-8")
2611
2786
  ));
2612
2787
  hasNext = !!(packageJson.dependencies?.next || packageJson.devDependencies?.next);
2613
2788
  }
2614
- const serverDir = join17(cwd, "src", "server");
2615
- const hasServer = existsSync16(serverDir);
2789
+ const serverDir = join18(cwd, "src", "server");
2790
+ const hasServer = existsSync17(serverDir);
2616
2791
  console.log(chalk7.blue.bold("\n\u{1F3D7}\uFE0F Building SPFN project for production...\n"));
2617
2792
  if (hasServer) {
2618
2793
  const spinner = ora5("Generating API client...").start();
@@ -2654,10 +2829,10 @@ async function buildProject(options) {
2654
2829
  if (hasServer && !options.nextOnly) {
2655
2830
  const spinner = ora5("Building SPFN server...").start();
2656
2831
  try {
2657
- const outputDir = join17(cwd, ".spfn", "server");
2832
+ const outputDir = join18(cwd, ".spfn", "server");
2658
2833
  mkdirSync2(outputDir, { recursive: true });
2659
- const serverDir2 = join17(cwd, "src", "server");
2660
- if (!existsSync16(serverDir2)) {
2834
+ const serverDir2 = join18(cwd, "src", "server");
2835
+ if (!existsSync17(serverDir2)) {
2661
2836
  spinner.fail("SPFN server build failed");
2662
2837
  logger.error("src/server/ directory not found");
2663
2838
  logger.error('Please run "spfn init" to initialize the project.');
@@ -2691,12 +2866,12 @@ async function buildProject(options) {
2691
2866
  onSuccess: async () => {
2692
2867
  }
2693
2868
  });
2694
- const prodServerPath = join17(cwd, ".spfn", "prod-server.mjs");
2869
+ const prodServerPath = join18(cwd, ".spfn", "prod-server.mjs");
2695
2870
  const prodServerContent = renderProdServerEntry();
2696
- writeFileSync11(prodServerPath, prodServerContent);
2871
+ writeFileSync12(prodServerPath, prodServerContent);
2697
2872
  spinner.succeed(`SPFN server build completed \u2192 .spfn/server`);
2698
- const routesDir = join17(cwd, ".spfn", "server", "routes");
2699
- if (existsSync16(routesDir)) {
2873
+ const routesDir = join18(cwd, ".spfn", "server", "routes");
2874
+ if (existsSync17(routesDir)) {
2700
2875
  console.log();
2701
2876
  console.log(chalk7.bold("Route (api)"));
2702
2877
  try {
@@ -2752,8 +2927,8 @@ var buildCommand = new Command5("build").description("Build SPFN project for pro
2752
2927
  init_logger();
2753
2928
  init_package_manager();
2754
2929
  import { Command as Command6 } from "commander";
2755
- import { existsSync as existsSync17, readFileSync as readFileSync9 } from "fs";
2756
- import { join as join18 } from "path";
2930
+ import { existsSync as existsSync18, readFileSync as readFileSync10 } from "fs";
2931
+ import { join as join19 } from "path";
2757
2932
  import { execa as execa8 } from "execa";
2758
2933
  import chalk8 from "chalk";
2759
2934
  import { loadAppConfig as loadAppConfig2, resolvePorts as resolvePorts2 } from "@spfn/core/app-config";
@@ -2762,16 +2937,16 @@ var startCommand = new Command6("start").description("Start SPFN production serv
2762
2937
  process.env.NODE_ENV = "production";
2763
2938
  }
2764
2939
  const cwd = process.cwd();
2765
- const packageJsonPath = join18(cwd, "package.json");
2940
+ const packageJsonPath = join19(cwd, "package.json");
2766
2941
  let hasNext = false;
2767
- if (existsSync17(packageJsonPath)) {
2768
- const packageJson = JSON.parse(readFileSync9(packageJsonPath, "utf-8"));
2942
+ if (existsSync18(packageJsonPath)) {
2943
+ const packageJson = JSON.parse(readFileSync10(packageJsonPath, "utf-8"));
2769
2944
  hasNext = !!(packageJson.dependencies?.next || packageJson.devDependencies?.next);
2770
2945
  }
2771
- const builtServerDir = join18(cwd, ".spfn", "server");
2772
- const hasBuiltServer = existsSync17(builtServerDir);
2773
- const nextBuildDir = join18(cwd, ".next");
2774
- const hasNextBuild = existsSync17(nextBuildDir);
2946
+ const builtServerDir = join19(cwd, ".spfn", "server");
2947
+ const hasBuiltServer = existsSync18(builtServerDir);
2948
+ const nextBuildDir = join19(cwd, ".next");
2949
+ const hasNextBuild = existsSync18(nextBuildDir);
2775
2950
  if (!options.nextOnly && !hasBuiltServer) {
2776
2951
  logger.error('.spfn/server directory not found. Please run "spfn build" first.');
2777
2952
  process.exit(1);
@@ -2781,8 +2956,8 @@ var startCommand = new Command6("start").description("Start SPFN production serv
2781
2956
  process.exit(1);
2782
2957
  }
2783
2958
  const pm = detectPackageManager(cwd);
2784
- const serverEntry = join18(cwd, ".spfn", "prod-server.mjs");
2785
- if (!existsSync17(serverEntry)) {
2959
+ const serverEntry = join19(cwd, ".spfn", "prod-server.mjs");
2960
+ if (!existsSync18(serverEntry)) {
2786
2961
  logger.error('.spfn/prod-server.mjs not found. Please run "spfn build" first.');
2787
2962
  process.exit(1);
2788
2963
  }
@@ -2864,8 +3039,8 @@ var startCommand = new Command6("start").description("Start SPFN production serv
2864
3039
  init_logger();
2865
3040
  init_package_manager();
2866
3041
  import { Command as Command7 } from "commander";
2867
- import { existsSync as existsSync18, mkdirSync as mkdirSync3, writeFileSync as writeFileSync12 } from "fs";
2868
- import { join as join19 } from "path";
3042
+ import { existsSync as existsSync19, mkdirSync as mkdirSync3, writeFileSync as writeFileSync13 } from "fs";
3043
+ import { join as join20 } from "path";
2869
3044
  import { pathToFileURL } from "url";
2870
3045
  import { execa as execa9 } from "execa";
2871
3046
  var CONFIG_FILE_PATHS = [
@@ -2875,7 +3050,7 @@ var CONFIG_FILE_PATHS = [
2875
3050
  "src/server/server.config.js"
2876
3051
  ];
2877
3052
  function findServerConfig(cwd) {
2878
- return CONFIG_FILE_PATHS.map((path5) => join19(cwd, path5)).find(existsSync18);
3053
+ return CONFIG_FILE_PATHS.map((path5) => join20(cwd, path5)).find(existsSync19);
2879
3054
  }
2880
3055
  var provisionCommand = new Command7("provision").description("Run deploy-time provisioning (seed/RBAC lifecycle hooks) \u2014 once per deploy, not per cold start").action(async () => {
2881
3056
  if (!process.env.NODE_ENV) {
@@ -2896,10 +3071,10 @@ var provisionCommand = new Command7("provision").description("Run deploy-time pr
2896
3071
  if (keychainMissing.length > 0) {
2897
3072
  logger.warn(`[SPFN] Could not resolve keychain secret(s): ${keychainMissing.join(", ")} \u2014 run \`spfn secret set <KEY>\``);
2898
3073
  }
2899
- const tempDir = join19(cwd, ".spfn");
2900
- const runnerEntry = join19(tempDir, "provision.mjs");
3074
+ const tempDir = join20(cwd, ".spfn");
3075
+ const runnerEntry = join20(tempDir, "provision.mjs");
2901
3076
  mkdirSync3(tempDir, { recursive: true });
2902
- writeFileSync12(runnerEntry, `
3077
+ writeFileSync13(runnerEntry, `
2903
3078
  if (!process.env.NODE_ENV)
2904
3079
  {
2905
3080
  process.env.NODE_ENV = 'production';
@@ -2942,13 +3117,13 @@ process.exit(0);
2942
3117
  // src/commands/codegen.ts
2943
3118
  init_logger();
2944
3119
  import { Command as Command8 } from "commander";
2945
- import { existsSync as existsSync19, writeFileSync as writeFileSync13 } from "fs";
2946
- import { join as join20 } from "path";
3120
+ import { existsSync as existsSync20, writeFileSync as writeFileSync14 } from "fs";
3121
+ import { join as join21 } from "path";
2947
3122
  import chalk9 from "chalk";
2948
3123
  async function initCodegen(options) {
2949
3124
  const cwd = process.cwd();
2950
- const rcPath = join20(cwd, ".spfnrc.ts");
2951
- if (existsSync19(rcPath)) {
3125
+ const rcPath = join21(cwd, ".spfnrc.ts");
3126
+ if (existsSync20(rcPath)) {
2952
3127
  logger.warn(".spfnrc.ts already exists");
2953
3128
  logger.info("Edit manually to add custom generators");
2954
3129
  process.exit(0);
@@ -2973,7 +3148,7 @@ export default defineConfig({
2973
3148
  ],
2974
3149
  });
2975
3150
  `;
2976
- writeFileSync13(rcPath, content);
3151
+ writeFileSync14(rcPath, content);
2977
3152
  console.log("\n" + chalk9.green.bold("\u2713 Created .spfnrc.ts\n"));
2978
3153
  console.log(chalk9.gray("Configured the @spfn/core:route-map generator."));
2979
3154
  if (options.withExample) {
@@ -3041,18 +3216,18 @@ codegenCommand.command("run").description("Run code generators once (no watch mo
3041
3216
  // src/commands/contract.ts
3042
3217
  init_logger();
3043
3218
  import { Command as Command9 } from "commander";
3044
- import { join as join21 } from "path";
3219
+ import { join as join22 } from "path";
3045
3220
  import chalk10 from "chalk";
3046
3221
  async function resolveContractsDir(cwd, override) {
3047
3222
  if (override) {
3048
- return join21(cwd, override);
3223
+ return join22(cwd, override);
3049
3224
  }
3050
3225
  const { loadCodegenConfig } = await import("@spfn/core/codegen");
3051
3226
  const config = loadCodegenConfig(cwd);
3052
3227
  const generator = config.generators?.find(
3053
3228
  (entry) => "name" in entry && entry.name === "@spfn/core:contract"
3054
3229
  );
3055
- return join21(cwd, generator?.outputDir ?? "./contracts");
3230
+ return join22(cwd, generator?.outputDir ?? "./contracts");
3056
3231
  }
3057
3232
  async function regenerate(cwd) {
3058
3233
  const { loadCodegenConfig, createGeneratorsFromConfig, CodegenOrchestrator } = await import("@spfn/core/codegen");
@@ -3320,7 +3495,7 @@ import { Command as Command11 } from "commander";
3320
3495
  import chalk13 from "chalk";
3321
3496
 
3322
3497
  // src/commands/db/utils/drizzle.ts
3323
- import { existsSync as existsSync20, writeFileSync as writeFileSync14, unlinkSync as unlinkSync2 } from "fs";
3498
+ import { existsSync as existsSync21, writeFileSync as writeFileSync15, unlinkSync as unlinkSync2 } from "fs";
3324
3499
  import { spawn } from "child_process";
3325
3500
  import { pathToFileURL as pathToFileURL2 } from "url";
3326
3501
  import chalk12 from "chalk";
@@ -3387,7 +3562,7 @@ function validateDatabasePrerequisites() {
3387
3562
  }
3388
3563
  }
3389
3564
  async function runDrizzleCommand(command) {
3390
- const hasUserConfig = existsSync20("./drizzle.config.ts");
3565
+ const hasUserConfig = existsSync21("./drizzle.config.ts");
3391
3566
  const tempConfigPath = `./drizzle.config.${process.pid}.${Date.now()}.temp.ts`;
3392
3567
  const configPath = hasUserConfig ? "./drizzle.config.ts" : tempConfigPath;
3393
3568
  if (!hasUserConfig) {
@@ -3406,7 +3581,7 @@ async function runDrizzleCommand(command) {
3406
3581
  expandGlobs: true,
3407
3582
  autoDetectSchemas: true
3408
3583
  });
3409
- writeFileSync14(tempConfigPath, configContent);
3584
+ writeFileSync15(tempConfigPath, configContent);
3410
3585
  console.log(chalk12.dim("Using auto-generated Drizzle config\n"));
3411
3586
  }
3412
3587
  const args = command.split(" ");
@@ -3419,7 +3594,7 @@ async function runDrizzleCommand(command) {
3419
3594
  env: shouldRelaxDbTls(env2.DATABASE_URL) ? { ...process.env, NODE_TLS_REJECT_UNAUTHORIZED: "0" } : { ...process.env }
3420
3595
  });
3421
3596
  const cleanup = () => {
3422
- if (!hasUserConfig && existsSync20(tempConfigPath)) {
3597
+ if (!hasUserConfig && existsSync21(tempConfigPath)) {
3423
3598
  unlinkSync2(tempConfigPath);
3424
3599
  }
3425
3600
  };
@@ -3739,8 +3914,8 @@ async function applyFunctionMigrations(plans) {
3739
3914
 
3740
3915
  // src/commands/db/migrate.ts
3741
3916
  import chalk21 from "chalk";
3742
- import { join as join22 } from "path";
3743
- import { existsSync as existsSync22 } from "fs";
3917
+ import { join as join23 } from "path";
3918
+ import { existsSync as existsSync23 } from "fs";
3744
3919
 
3745
3920
  // src/commands/db/backup.ts
3746
3921
  import { promises as fs3 } from "fs";
@@ -3833,7 +4008,7 @@ function formatTimestamp() {
3833
4008
 
3834
4009
  // src/commands/db/utils/backup-files.ts
3835
4010
  import { promises as fs2 } from "fs";
3836
- import { existsSync as existsSync21 } from "fs";
4011
+ import { existsSync as existsSync22 } from "fs";
3837
4012
  import path2 from "path";
3838
4013
  import chalk19 from "chalk";
3839
4014
 
@@ -3933,7 +4108,7 @@ async function ensureBackupInGitignore() {
3933
4108
  const gitignorePath = path2.join(process.cwd(), ".gitignore");
3934
4109
  try {
3935
4110
  let content = "";
3936
- let exists = existsSync21(gitignorePath);
4111
+ let exists = existsSync22(gitignorePath);
3937
4112
  if (exists) {
3938
4113
  content = await fs2.readFile(gitignorePath, "utf-8");
3939
4114
  }
@@ -3955,7 +4130,7 @@ async function ensureBackupDir() {
3955
4130
  try {
3956
4131
  await fs2.mkdir(backupDir, { recursive: true });
3957
4132
  const gitignorePath = path2.join(backupDir, ".gitignore");
3958
- const gitignoreExists = existsSync21(gitignorePath);
4133
+ const gitignoreExists = existsSync22(gitignorePath);
3959
4134
  if (!gitignoreExists) {
3960
4135
  await fs2.writeFile(gitignorePath, "# Ignore all backup files\n*.sql\n*.dump\n*.meta.json\n");
3961
4136
  }
@@ -4149,8 +4324,8 @@ async function dbMigrate(options = {}) {
4149
4324
  await executeFunctionMigrations2(loadFunctionMigrationPlans2(functions));
4150
4325
  console.log(chalk21.green("\u2705 Function migrations applied\n"));
4151
4326
  }
4152
- const projectMigrationsDir = join22(process.cwd(), "src/server/drizzle");
4153
- if (existsSync22(projectMigrationsDir)) {
4327
+ const projectMigrationsDir = join23(process.cwd(), "src/server/drizzle");
4328
+ if (existsSync23(projectMigrationsDir)) {
4154
4329
  const projConn = postgres.default(env5.DATABASE_URL, { max: 1 });
4155
4330
  const projDb = drizzle({ client: projConn });
4156
4331
  try {
@@ -4256,7 +4431,7 @@ function printReport(report2) {
4256
4431
 
4257
4432
  // src/commands/db/studio.ts
4258
4433
  import chalk23 from "chalk";
4259
- import { existsSync as existsSync23, writeFileSync as writeFileSync15, unlinkSync as unlinkSync3 } from "fs";
4434
+ import { existsSync as existsSync24, writeFileSync as writeFileSync16, unlinkSync as unlinkSync3 } from "fs";
4260
4435
  import { spawn as spawn3 } from "child_process";
4261
4436
  import { env as env6 } from "@spfn/core/config";
4262
4437
  async function dbStudio(requestedPort) {
@@ -4274,7 +4449,7 @@ async function dbStudio(requestedPort) {
4274
4449
  console.error(chalk23.red(error instanceof Error ? error.message : "Failed to find available port"));
4275
4450
  process.exit(1);
4276
4451
  }
4277
- const hasUserConfig = existsSync23("./drizzle.config.ts");
4452
+ const hasUserConfig = existsSync24("./drizzle.config.ts");
4278
4453
  const tempConfigPath = `./drizzle.config.${process.pid}.${Date.now()}.temp.ts`;
4279
4454
  try {
4280
4455
  const configPath = hasUserConfig ? "./drizzle.config.ts" : tempConfigPath;
@@ -4291,7 +4466,7 @@ async function dbStudio(requestedPort) {
4291
4466
  expandGlobs: true
4292
4467
  // Expand glob patterns for Studio compatibility
4293
4468
  });
4294
- writeFileSync15(tempConfigPath, configContent);
4469
+ writeFileSync16(tempConfigPath, configContent);
4295
4470
  console.log(chalk23.dim("Using auto-generated Drizzle config\n"));
4296
4471
  }
4297
4472
  const studioProcess = spawn3("drizzle-kit", ["studio", `--port=${port}`, `--config=${configPath}`], {
@@ -4300,7 +4475,7 @@ async function dbStudio(requestedPort) {
4300
4475
  env: shouldRelaxDbTls(env6.DATABASE_URL) ? { ...process.env, NODE_TLS_REJECT_UNAUTHORIZED: "0" } : { ...process.env }
4301
4476
  });
4302
4477
  const cleanup = () => {
4303
- if (!hasUserConfig && existsSync23(tempConfigPath)) {
4478
+ if (!hasUserConfig && existsSync24(tempConfigPath)) {
4304
4479
  unlinkSync3(tempConfigPath);
4305
4480
  }
4306
4481
  };
@@ -4330,7 +4505,7 @@ async function dbStudio(requestedPort) {
4330
4505
  process.exit(0);
4331
4506
  });
4332
4507
  } catch (error) {
4333
- if (!hasUserConfig && existsSync23(tempConfigPath)) {
4508
+ if (!hasUserConfig && existsSync24(tempConfigPath)) {
4334
4509
  unlinkSync3(tempConfigPath);
4335
4510
  }
4336
4511
  console.error(chalk23.red("\u274C Failed to start Drizzle Studio"));
@@ -4717,8 +4892,8 @@ async function dbBackupClean(options) {
4717
4892
  }
4718
4893
 
4719
4894
  // src/commands/db/reindex.ts
4720
- import { existsSync as existsSync24, readFileSync as readFileSync10, writeFileSync as writeFileSync16, renameSync, copyFileSync } from "fs";
4721
- import { join as join23 } from "path";
4895
+ import { existsSync as existsSync25, readFileSync as readFileSync11, writeFileSync as writeFileSync17, renameSync, copyFileSync } from "fs";
4896
+ import { join as join24 } from "path";
4722
4897
  import chalk29 from "chalk";
4723
4898
  import { loadEnv as loadEnv9 } from "@spfn/core/server";
4724
4899
  function isTimestampPrefix(tag) {
@@ -4740,13 +4915,13 @@ async function dbReindex(options = {}) {
4740
4915
  const { getDrizzleConfig } = await import("@spfn/core/db");
4741
4916
  const config = getDrizzleConfig({ disablePackageDiscovery: true });
4742
4917
  const outDir = config.out;
4743
- const journalPath = join23(outDir, "meta", "_journal.json");
4744
- if (!existsSync24(journalPath)) {
4918
+ const journalPath = join24(outDir, "meta", "_journal.json");
4919
+ if (!existsSync25(journalPath)) {
4745
4920
  console.error(chalk29.red("\u274C No _journal.json found at:"), journalPath);
4746
4921
  console.log(chalk29.yellow("\u{1F4A1} Run `spfn db generate` first to create migrations"));
4747
4922
  process.exit(1);
4748
4923
  }
4749
- const journal = JSON.parse(readFileSync10(journalPath, "utf-8"));
4924
+ const journal = JSON.parse(readFileSync11(journalPath, "utf-8"));
4750
4925
  if (journal.entries.length === 0) {
4751
4926
  console.log(chalk29.yellow("No migration entries found \u2014 nothing to reindex."));
4752
4927
  return;
@@ -4762,14 +4937,14 @@ async function dbReindex(options = {}) {
4762
4937
  const { prefix: oldPrefix, suffix } = parseTag(entry.tag);
4763
4938
  const newPrefix = String(entry.when);
4764
4939
  const newTag = suffix ? `${newPrefix}_${suffix}` : newPrefix;
4765
- const oldSql = join23(outDir, `${entry.tag}.sql`);
4766
- const newSql = join23(outDir, `${newTag}.sql`);
4767
- if (existsSync24(oldSql)) {
4940
+ const oldSql = join24(outDir, `${entry.tag}.sql`);
4941
+ const newSql = join24(outDir, `${newTag}.sql`);
4942
+ if (existsSync25(oldSql)) {
4768
4943
  renames.push({ type: "sql", from: oldSql, to: newSql });
4769
4944
  }
4770
- const oldSnapshot = join23(outDir, "meta", `${oldPrefix}_snapshot.json`);
4771
- const newSnapshot = join23(outDir, "meta", `${newPrefix}_snapshot.json`);
4772
- if (existsSync24(oldSnapshot)) {
4945
+ const oldSnapshot = join24(outDir, "meta", `${oldPrefix}_snapshot.json`);
4946
+ const newSnapshot = join24(outDir, "meta", `${newPrefix}_snapshot.json`);
4947
+ if (existsSync25(oldSnapshot)) {
4773
4948
  renames.push({ type: "snapshot", from: oldSnapshot, to: newSnapshot });
4774
4949
  }
4775
4950
  tagUpdates.push({ idx: entry.idx, oldTag: entry.tag, newTag });
@@ -4812,7 +4987,7 @@ async function dbReindex(options = {}) {
4812
4987
  entry.tag = update.newTag;
4813
4988
  }
4814
4989
  }
4815
- writeFileSync16(journalPath, JSON.stringify(journal, null, 2) + "\n");
4990
+ writeFileSync17(journalPath, JSON.stringify(journal, null, 2) + "\n");
4816
4991
  console.log(chalk29.green(`
4817
4992
  \u2705 Reindex complete \u2014 ${tagUpdates.length} migration(s) converted to timestamp prefix.`));
4818
4993
  }
@@ -4835,21 +5010,21 @@ dbCommand.command("reindex").description("Convert migration files from sequentia
4835
5010
  // src/commands/add.ts
4836
5011
  init_package_manager();
4837
5012
  import { Command as Command12 } from "commander";
4838
- import { existsSync as existsSync26, readFileSync as readFileSync12 } from "fs";
4839
- import { join as join25 } from "path";
5013
+ import { existsSync as existsSync27, readFileSync as readFileSync13 } from "fs";
5014
+ import { join as join26 } from "path";
4840
5015
  import { execa as execa10 } from "execa";
4841
5016
  import chalk31 from "chalk";
4842
5017
  import ora11 from "ora";
4843
5018
 
4844
5019
  // src/commands/add-vercel.ts
4845
5020
  init_templates();
4846
- import { copyFileSync as copyFileSync2, existsSync as existsSync25, mkdirSync as mkdirSync4, readFileSync as readFileSync11 } from "fs";
4847
- import { dirname as dirname4, join as join24 } from "path";
5021
+ import { copyFileSync as copyFileSync2, existsSync as existsSync26, mkdirSync as mkdirSync4, readFileSync as readFileSync12 } from "fs";
5022
+ import { dirname as dirname4, join as join25 } from "path";
4848
5023
  import chalk30 from "chalk";
4849
5024
  var FILES = [
4850
5025
  {
4851
5026
  template: "route.ts",
4852
- target: join24("src", "app", "api", "backend", "[[...route]]", "route.ts"),
5027
+ target: join25("src", "app", "api", "backend", "[[...route]]", "route.ts"),
4853
5028
  description: "hono/vercel adapter (mounts the SPFN app under /api/backend)"
4854
5029
  },
4855
5030
  {
@@ -4864,7 +5039,7 @@ var FILES = [
4864
5039
  }
4865
5040
  ];
4866
5041
  function registryEnvValue(templatesDir) {
4867
- const scopeLines = readFileSync11(join24(templatesDir, "npmrc"), "utf8").trim();
5042
+ const scopeLines = readFileSync12(join25(templatesDir, "npmrc"), "utf8").trim();
4868
5043
  const registryUrl = scopeLines.split("\n")[0].split("=")[1];
4869
5044
  const authKey = `${registryUrl.replace(/^https?:/, "").replace(/\/*$/, "/")}:_authToken`;
4870
5045
  return [
@@ -4877,15 +5052,15 @@ function registryEnvValue(templatesDir) {
4877
5052
  async function addVercel() {
4878
5053
  console.log(chalk30.blue("\n\u{1F4E6} Setting up the Vercel serverless target...\n"));
4879
5054
  const cwd = process.cwd();
4880
- const templatesDir = join24(findTemplatesPath(), "vercel");
5055
+ const templatesDir = join25(findTemplatesPath(), "vercel");
4881
5056
  for (const file of FILES) {
4882
- const targetPath = join24(cwd, file.target);
4883
- if (existsSync25(targetPath)) {
5057
+ const targetPath = join25(cwd, file.target);
5058
+ if (existsSync26(targetPath)) {
4884
5059
  console.log(chalk30.yellow(`\u23ED\uFE0F ${file.target} already exists \u2014 skipped (not overwritten)`));
4885
5060
  continue;
4886
5061
  }
4887
5062
  mkdirSync4(dirname4(targetPath), { recursive: true });
4888
- copyFileSync2(join24(templatesDir, file.template), targetPath);
5063
+ copyFileSync2(join25(templatesDir, file.template), targetPath);
4889
5064
  console.log(chalk30.green(`\u2705 ${file.target}`) + chalk30.gray(` \u2014 ${file.description}`));
4890
5065
  }
4891
5066
  console.log(chalk30.green("\n\u2705 Vercel target ready!\n"));
@@ -4930,9 +5105,9 @@ async function addPackage(packageName) {
4930
5105
  console.log(chalk31.blue(`
4931
5106
  \u{1F4E6} Setting up ${packageName}...
4932
5107
  `));
4933
- const pkgPath = join25(process.cwd(), "node_modules", ...packageName.split("/"));
4934
- const pkgJsonPath = join25(pkgPath, "package.json");
4935
- if (!existsSync26(pkgJsonPath)) {
5108
+ const pkgPath = join26(process.cwd(), "node_modules", ...packageName.split("/"));
5109
+ const pkgJsonPath = join26(pkgPath, "package.json");
5110
+ if (!existsSync27(pkgJsonPath)) {
4936
5111
  const pm = detectPackageManager(process.cwd());
4937
5112
  const installSpinner = ora11("Installing package...").start();
4938
5113
  try {
@@ -4946,11 +5121,11 @@ async function addPackage(packageName) {
4946
5121
  } else {
4947
5122
  console.log(chalk31.gray("\u2713 Package already installed (using local version)\n"));
4948
5123
  }
4949
- if (!existsSync26(pkgJsonPath)) {
5124
+ if (!existsSync27(pkgJsonPath)) {
4950
5125
  console.error(chalk31.red(`\u274C Package ${packageName} not found after installation`));
4951
5126
  process.exit(1);
4952
5127
  }
4953
- const pkgJson = JSON.parse(readFileSync12(pkgJsonPath, "utf-8"));
5128
+ const pkgJson = JSON.parse(readFileSync13(pkgJsonPath, "utf-8"));
4954
5129
  if (pkgJson.spfn?.migrations) {
4955
5130
  console.log(chalk31.blue(`
4956
5131
  \u{1F5C4}\uFE0F Setting up database for ${packageName}...
@@ -4996,7 +5171,7 @@ var addCommand = new Command12("add").description("Add a capability to this proj
4996
5171
  // src/commands/env.ts
4997
5172
  import { Command as Command13 } from "commander";
4998
5173
  import chalk32 from "chalk";
4999
- import { existsSync as existsSync27, readFileSync as readFileSync13, writeFileSync as writeFileSync17 } from "fs";
5174
+ import { existsSync as existsSync28, readFileSync as readFileSync14, writeFileSync as writeFileSync18 } from "fs";
5000
5175
  import { resolve as resolve2 } from "path";
5001
5176
  import { parse as parse2 } from "dotenv";
5002
5177
 
@@ -5287,11 +5462,11 @@ async function initEnvFiles(options) {
5287
5462
  }
5288
5463
  function writeEnvTemplate(cwd, file, vars, force) {
5289
5464
  const filePath = resolve2(cwd, file);
5290
- if (existsSync27(filePath) && !force) {
5465
+ if (existsSync28(filePath) && !force) {
5291
5466
  console.log(chalk32.yellow(` \u23ED\uFE0F ${file} already exists (use --force to overwrite)`));
5292
5467
  return;
5293
5468
  }
5294
- writeFileSync17(filePath, generateEnvFileContent(vars), "utf-8");
5469
+ writeFileSync18(filePath, generateEnvFileContent(vars), "utf-8");
5295
5470
  console.log(chalk32.green(` \u2705 ${file} (${vars.length} variables)`));
5296
5471
  }
5297
5472
  function generateEnvFileContent(vars) {
@@ -5336,10 +5511,10 @@ async function checkEnvFiles(options) {
5336
5511
  const warnings = [];
5337
5512
  for (const file of filesToCheck) {
5338
5513
  const filePath = resolve2(cwd, file);
5339
- if (!existsSync27(filePath)) {
5514
+ if (!existsSync28(filePath)) {
5340
5515
  continue;
5341
5516
  }
5342
- const content = readFileSync13(filePath, "utf-8");
5517
+ const content = readFileSync14(filePath, "utf-8");
5343
5518
  const parsed = parse2(content);
5344
5519
  for (const [key, value] of Object.entries(parsed)) {
5345
5520
  loadedEnv[key] = { value: value || "", file };
@@ -6046,12 +6221,12 @@ import chalk34 from "chalk";
6046
6221
 
6047
6222
  // src/utils/ops/auth-crypto.ts
6048
6223
  import { createRequire } from "module";
6049
- import { join as join26 } from "path";
6224
+ import { join as join27 } from "path";
6050
6225
  import { pathToFileURL as pathToFileURL3 } from "url";
6051
6226
  var CRYPTO_ENTRY_SINCE = "0.3.0-beta.2";
6052
6227
  var CRYPTO_ENTRY = "@spfn/auth/crypto";
6053
6228
  async function importFromApp(specifier) {
6054
- const requireFromApp = createRequire(join26(process.cwd(), "noop.js"));
6229
+ const requireFromApp = createRequire(join27(process.cwd(), "noop.js"));
6055
6230
  return await import(pathToFileURL3(requireFromApp.resolve(specifier)).href);
6056
6231
  }
6057
6232
  async function loadAuthCrypto() {
@@ -6468,12 +6643,12 @@ function resolveEnv(env9) {
6468
6643
 
6469
6644
  // src/commands/secret/store-value.ts
6470
6645
  init_logger();
6471
- import { join as join28 } from "path";
6646
+ import { join as join29 } from "path";
6472
6647
  init_env_file();
6473
6648
 
6474
6649
  // src/utils/sops.ts
6475
6650
  import { execa as execa11 } from "execa";
6476
- import { existsSync as existsSync28, mkdirSync as mkdirSync5, writeFileSync as writeFileSync18 } from "fs";
6651
+ import { existsSync as existsSync29, mkdirSync as mkdirSync5, writeFileSync as writeFileSync19 } from "fs";
6477
6652
  import { dirname as dirname5 } from "path";
6478
6653
  async function ensureSopsInstalled() {
6479
6654
  try {
@@ -6483,14 +6658,14 @@ async function ensureSopsInstalled() {
6483
6658
  }
6484
6659
  }
6485
6660
  async function sopsDecrypt(absFile) {
6486
- if (!existsSync28(absFile)) {
6661
+ if (!existsSync29(absFile)) {
6487
6662
  return {};
6488
6663
  }
6489
6664
  const { stdout } = await execa11("sops", ["--decrypt", "--output-type", "json", absFile]);
6490
6665
  return JSON.parse(stdout);
6491
6666
  }
6492
6667
  async function sopsSetValue(absFile, relFile, key, value) {
6493
- if (existsSync28(absFile)) {
6668
+ if (existsSync29(absFile)) {
6494
6669
  await execa11("sops", ["set", absFile, `["${key}"]`, JSON.stringify(value)]);
6495
6670
  return;
6496
6671
  }
@@ -6509,27 +6684,27 @@ async function sopsSetValue(absFile, relFile, key, value) {
6509
6684
  ],
6510
6685
  { input: JSON.stringify({ [key]: value }) }
6511
6686
  );
6512
- writeFileSync18(absFile, stdout);
6687
+ writeFileSync19(absFile, stdout);
6513
6688
  }
6514
6689
  async function sopsUpdateKeys(absFile) {
6515
6690
  await execa11("sops", ["updatekeys", "-y", absFile]);
6516
6691
  }
6517
6692
 
6518
6693
  // src/utils/secret-config.ts
6519
- import { existsSync as existsSync29, readdirSync as readdirSync2 } from "fs";
6520
- import { join as join27 } from "path";
6694
+ import { existsSync as existsSync30, readdirSync as readdirSync2 } from "fs";
6695
+ import { join as join28 } from "path";
6521
6696
  var SECRETS_DIR = "secrets";
6522
6697
  function getSopsFile(cwd, env9) {
6523
6698
  const relFile = `${SECRETS_DIR}/${env9}.enc.json`;
6524
- return { absFile: join27(cwd, relFile), relFile };
6699
+ return { absFile: join28(cwd, relFile), relFile };
6525
6700
  }
6526
6701
  function findUp(cwd, filename, maxDepth = 6) {
6527
6702
  let dir = cwd;
6528
6703
  for (let depth = 0; depth < maxDepth; depth++) {
6529
- if (existsSync29(join27(dir, filename))) {
6704
+ if (existsSync30(join28(dir, filename))) {
6530
6705
  return dir;
6531
6706
  }
6532
- const parent = join27(dir, "..");
6707
+ const parent = join28(dir, "..");
6533
6708
  if (parent === dir) {
6534
6709
  break;
6535
6710
  }
@@ -6539,17 +6714,17 @@ function findUp(cwd, filename, maxDepth = 6) {
6539
6714
  }
6540
6715
  function findSopsConfig(cwd) {
6541
6716
  const dir = findUp(cwd, ".sops.yaml");
6542
- return dir ? join27(dir, ".sops.yaml") : null;
6717
+ return dir ? join28(dir, ".sops.yaml") : null;
6543
6718
  }
6544
6719
  function hasSopsConfig(cwd) {
6545
6720
  return findSopsConfig(cwd) !== null;
6546
6721
  }
6547
6722
  function listSopsFiles(cwd) {
6548
- const dir = join27(cwd, SECRETS_DIR);
6549
- if (!existsSync29(dir)) {
6723
+ const dir = join28(cwd, SECRETS_DIR);
6724
+ if (!existsSync30(dir)) {
6550
6725
  return [];
6551
6726
  }
6552
- return readdirSync2(dir).filter((name) => name.endsWith(".enc.json")).map((name) => join27(dir, name));
6727
+ return readdirSync2(dir).filter((name) => name.endsWith(".enc.json")).map((name) => join28(dir, name));
6553
6728
  }
6554
6729
 
6555
6730
  // src/commands/secret/store-value.ts
@@ -6578,7 +6753,7 @@ async function storeSecret(cwd, env9, key, value) {
6578
6753
  );
6579
6754
  }
6580
6755
  await store.set(keychainName(key), value);
6581
- const serverEnvPath = join28(cwd, ".env.server");
6756
+ const serverEnvPath = join29(cwd, ".env.server");
6582
6757
  const result = upsertEnvVar(serverEnvPath, key, keychainRef(key));
6583
6758
  restrictEnvFilePerms(serverEnvPath);
6584
6759
  ensureGitignored(cwd, [{ pattern: ".env.server", comment: "spfn server env (secrets)" }]);
@@ -6816,12 +6991,12 @@ function requireEntry(schema, key) {
6816
6991
  // src/commands/secret/keygen.ts
6817
6992
  init_logger();
6818
6993
  import { execa as execa12 } from "execa";
6819
- import { existsSync as existsSync30, mkdirSync as mkdirSync6 } from "fs";
6994
+ import { existsSync as existsSync31, mkdirSync as mkdirSync6 } from "fs";
6820
6995
  import { homedir } from "os";
6821
- import { dirname as dirname6, join as join29 } from "path";
6996
+ import { dirname as dirname6, join as join30 } from "path";
6822
6997
  import chalk41 from "chalk";
6823
6998
  function ageKeyFile() {
6824
- return process.env.SOPS_AGE_KEY_FILE ?? join29(homedir(), ".config", "sops", "age", "keys.txt");
6999
+ return process.env.SOPS_AGE_KEY_FILE ?? join30(homedir(), ".config", "sops", "age", "keys.txt");
6825
7000
  }
6826
7001
  async function ensureAgeInstalled() {
6827
7002
  try {
@@ -6841,7 +7016,7 @@ async function secretKeygen() {
6841
7016
  process.exit(1);
6842
7017
  }
6843
7018
  const keyFile = ageKeyFile();
6844
- if (existsSync30(keyFile)) {
7019
+ if (existsSync31(keyFile)) {
6845
7020
  const { stdout } = await execa12("age-keygen", ["-y", keyFile]);
6846
7021
  logger.warn(`age key file already exists: ${keyFile}`);
6847
7022
  printPublicKeys(publicKeys(stdout));
@@ -6868,8 +7043,8 @@ function printPublicKeys(keys) {
6868
7043
 
6869
7044
  // src/commands/secret/recipients.ts
6870
7045
  init_logger();
6871
- import { existsSync as existsSync31, readFileSync as readFileSync14, writeFileSync as writeFileSync19 } from "fs";
6872
- import { join as join30 } from "path";
7046
+ import { existsSync as existsSync32, readFileSync as readFileSync15, writeFileSync as writeFileSync20 } from "fs";
7047
+ import { join as join31 } from "path";
6873
7048
  import { parse as parse3, stringify } from "yaml";
6874
7049
  import chalk42 from "chalk";
6875
7050
  var SOPS_CONFIG = ".sops.yaml";
@@ -6877,7 +7052,7 @@ var DEFAULT_PATH_REGEX = "secrets/.*\\.enc\\.json$";
6877
7052
  var AGE_RECIPIENT = /^age1[0-9a-z]+$/;
6878
7053
  async function secretRecipients(action, key, _options) {
6879
7054
  const cwd = process.cwd();
6880
- const configPath = join30(cwd, SOPS_CONFIG);
7055
+ const configPath = join31(cwd, SOPS_CONFIG);
6881
7056
  if (action === "list") {
6882
7057
  listRecipients(configPath);
6883
7058
  return;
@@ -6904,12 +7079,12 @@ async function secretRecipients(action, key, _options) {
6904
7079
  logger.warn("Removing a recipient does not revoke values they already decrypted \u2014 rotate those values.");
6905
7080
  }
6906
7081
  rule.age = [...recipients].join(",");
6907
- writeFileSync19(configPath, stringify(config));
7082
+ writeFileSync20(configPath, stringify(config));
6908
7083
  logger.success(`${action === "add" ? "Added" : "Removed"} recipient; updated ${SOPS_CONFIG}.`);
6909
7084
  await reencrypt(cwd);
6910
7085
  }
6911
7086
  function listRecipients(configPath) {
6912
- if (!existsSync31(configPath)) {
7087
+ if (!existsSync32(configPath)) {
6913
7088
  logger.info(`No ${SOPS_CONFIG} found.`);
6914
7089
  return;
6915
7090
  }
@@ -6927,10 +7102,10 @@ function listRecipients(configPath) {
6927
7102
  console.log();
6928
7103
  }
6929
7104
  function loadConfig(configPath) {
6930
- if (!existsSync31(configPath)) {
7105
+ if (!existsSync32(configPath)) {
6931
7106
  return { creation_rules: [] };
6932
7107
  }
6933
- return parse3(readFileSync14(configPath, "utf-8")) ?? { creation_rules: [] };
7108
+ return parse3(readFileSync15(configPath, "utf-8")) ?? { creation_rules: [] };
6934
7109
  }
6935
7110
  function ensureRule(config) {
6936
7111
  if (!config.creation_rules) {
@@ -6962,10 +7137,10 @@ async function reencrypt(cwd) {
6962
7137
 
6963
7138
  // src/commands/secret/check.ts
6964
7139
  init_logger();
6965
- import { join as join31 } from "path";
7140
+ import { join as join32 } from "path";
6966
7141
  import chalk43 from "chalk";
6967
7142
  init_env_file();
6968
- var COMMITTED_FILES = [".env", ".env.example"];
7143
+ var COMMITTED_FILES = [".env", ".env.example", ".env.local.example", ".env.server.example"];
6969
7144
  var PLACEHOLDER = /(your-|changeme|placeholder|example|<.*>)/i;
6970
7145
  async function secretCheck(options) {
6971
7146
  const cwd = process.cwd();
@@ -6981,14 +7156,14 @@ async function secretCheck(options) {
6981
7156
  const issues = [];
6982
7157
  const warnings = [];
6983
7158
  for (const file of COMMITTED_FILES) {
6984
- const parsed = parseEnvFile(join31(cwd, file));
7159
+ const parsed = parseEnvFile(join32(cwd, file));
6985
7160
  for (const [key, value] of Object.entries(parsed)) {
6986
7161
  if (secretKeys.has(key) && value.length > 0 && !PLACEHOLDER.test(value)) {
6987
7162
  issues.push(`${chalk43.cyan(key)} has a real value in committed ${chalk43.yellow(file)} \u2014 move it to the keychain/SOPS.`);
6988
7163
  }
6989
7164
  }
6990
7165
  }
6991
- const serverEnv = parseEnvFile(join31(cwd, ".env.server"));
7166
+ const serverEnv = parseEnvFile(join32(cwd, ".env.server"));
6992
7167
  for (const key of secretKeys) {
6993
7168
  const value = serverEnv[key];
6994
7169
  if (value && !value.startsWith(KEYCHAIN_REF_PREFIX)) {
@@ -7074,20 +7249,20 @@ async function deleteCloudToken(provider) {
7074
7249
 
7075
7250
  // src/utils/cloud/config.ts
7076
7251
  init_env_file();
7077
- import { existsSync as existsSync32, mkdirSync as mkdirSync7, readFileSync as readFileSync15, writeFileSync as writeFileSync20 } from "fs";
7078
- import { dirname as dirname7, join as join32 } from "path";
7079
- var CONFIG_REL_PATH = join32(".spfn", "cloud.json");
7252
+ import { existsSync as existsSync33, mkdirSync as mkdirSync7, readFileSync as readFileSync16, writeFileSync as writeFileSync21 } from "fs";
7253
+ import { dirname as dirname7, join as join33 } from "path";
7254
+ var CONFIG_REL_PATH = join33(".spfn", "cloud.json");
7080
7255
  function cloudConfigPath(cwd) {
7081
- return join32(cwd, CONFIG_REL_PATH);
7256
+ return join33(cwd, CONFIG_REL_PATH);
7082
7257
  }
7083
7258
  function readCloudConfig(cwd) {
7084
7259
  const path5 = cloudConfigPath(cwd);
7085
- if (!existsSync32(path5)) {
7260
+ if (!existsSync33(path5)) {
7086
7261
  return {};
7087
7262
  }
7088
7263
  let parsed;
7089
7264
  try {
7090
- parsed = JSON.parse(readFileSync15(path5, "utf-8"));
7265
+ parsed = JSON.parse(readFileSync16(path5, "utf-8"));
7091
7266
  } catch {
7092
7267
  throw new Error(`${path5} is not valid JSON \u2014 delete it and run \`spfn cloud link\` again.`);
7093
7268
  }
@@ -7099,7 +7274,7 @@ function readCloudConfig(cwd) {
7099
7274
  function writeCloudConfig(cwd, config) {
7100
7275
  const path5 = cloudConfigPath(cwd);
7101
7276
  mkdirSync7(dirname7(path5), { recursive: true });
7102
- writeFileSync20(path5, JSON.stringify(config, null, 4) + "\n", "utf-8");
7277
+ writeFileSync21(path5, JSON.stringify(config, null, 4) + "\n", "utf-8");
7103
7278
  ensureGitignored(cwd, [{ pattern: ".spfn/cloud.json", comment: "spfn cloud link state (per-user accounts)" }]);
7104
7279
  }
7105
7280
  function requireLinked(config, provider) {
@@ -7740,8 +7915,8 @@ function printMigrationPrompt(nearLimit) {
7740
7915
 
7741
7916
  // src/commands/cloud/keepalive.ts
7742
7917
  init_logger();
7743
- import { existsSync as existsSync33, mkdirSync as mkdirSync8, readFileSync as readFileSync16, writeFileSync as writeFileSync21 } from "fs";
7744
- import { dirname as dirname8, join as join33 } from "path";
7918
+ import { existsSync as existsSync34, mkdirSync as mkdirSync8, readFileSync as readFileSync17, writeFileSync as writeFileSync22 } from "fs";
7919
+ import { dirname as dirname8, join as join34 } from "path";
7745
7920
  import chalk49 from "chalk";
7746
7921
  var DEFAULT_HEALTH_PATH = "/api/backend/_core/health?detailed=true";
7747
7922
  var DEFAULT_SCHEDULE = "0 3 * * *";
@@ -7767,12 +7942,12 @@ async function cloudKeepalive(options) {
7767
7942
  logger.info("The DB ping needs detailed health checks: keep `healthCheck: { detailed: true }` in your server config.");
7768
7943
  }
7769
7944
  function setupVercelCron(cwd, healthPath) {
7770
- const vercelJsonPath = join33(cwd, "vercel.json");
7945
+ const vercelJsonPath = join34(cwd, "vercel.json");
7771
7946
  let vercelJson = {};
7772
- if (existsSync33(vercelJsonPath)) {
7947
+ if (existsSync34(vercelJsonPath)) {
7773
7948
  let parsed;
7774
7949
  try {
7775
- parsed = JSON.parse(readFileSync16(vercelJsonPath, "utf-8"));
7950
+ parsed = JSON.parse(readFileSync17(vercelJsonPath, "utf-8"));
7776
7951
  } catch {
7777
7952
  logger.error("vercel.json is not valid JSON (comments and trailing commas are not allowed) \u2014 fix it and re-run.");
7778
7953
  process.exit(1);
@@ -7793,7 +7968,7 @@ function setupVercelCron(cwd, healthPath) {
7793
7968
  logger.info(`Point the existing cron at ${chalk49.cyan(healthPath)} yourself, or use \`--github-actions --url <deployed-url>\`.`);
7794
7969
  process.exit(1);
7795
7970
  }
7796
- writeFileSync21(vercelJsonPath, JSON.stringify(vercelJson, null, 4) + "\n", "utf-8");
7971
+ writeFileSync22(vercelJsonPath, JSON.stringify(vercelJson, null, 4) + "\n", "utf-8");
7797
7972
  logger.success(`vercel.json: daily cron added (${DEFAULT_SCHEDULE} UTC \u2192 ${healthPath}). Deploy to activate.`);
7798
7973
  warnIfBackendRouteMissing(cwd);
7799
7974
  }
@@ -7813,8 +7988,8 @@ function setupGithubActions(cwd, healthPath, url) {
7813
7988
  logger.error("`--github-actions` needs `--url <deployed-url>` \u2014 the workflow runs outside your app and must know where it lives.");
7814
7989
  process.exit(1);
7815
7990
  }
7816
- const workflowPath = join33(cwd, ".github", "workflows", "spfn-keepalive.yml");
7817
- if (existsSync33(workflowPath)) {
7991
+ const workflowPath = join34(cwd, ".github", "workflows", "spfn-keepalive.yml");
7992
+ if (existsSync34(workflowPath)) {
7818
7993
  logger.success(`${workflowPath} already exists \u2014 nothing to do.`);
7819
7994
  return;
7820
7995
  }
@@ -7824,7 +7999,7 @@ function setupGithubActions(cwd, healthPath, url) {
7824
7999
  process.exit(1);
7825
8000
  }
7826
8001
  mkdirSync8(dirname8(workflowPath), { recursive: true });
7827
- writeFileSync21(workflowPath, githubWorkflow(new URL(healthPath, base).toString()), "utf-8");
8002
+ writeFileSync22(workflowPath, githubWorkflow(new URL(healthPath, base).toString()), "utf-8");
7828
8003
  logger.success(".github/workflows/spfn-keepalive.yml: daily keep-alive workflow added. Commit and push to activate.");
7829
8004
  }
7830
8005
  function normalizeDeployUrl(url) {
@@ -7861,8 +8036,8 @@ jobs:
7861
8036
  `;
7862
8037
  }
7863
8038
  function warnIfBackendRouteMissing(cwd) {
7864
- const routePath = join33(cwd, "src", "app", "api", "backend", "[[...route]]", "route.ts");
7865
- if (!existsSync33(routePath)) {
8039
+ const routePath = join34(cwd, "src", "app", "api", "backend", "[[...route]]", "route.ts");
8040
+ if (!existsSync34(routePath)) {
7866
8041
  logger.warn("src/app/api/backend/[[...route]]/route.ts not found \u2014 the cron path answers only after `spfn add vercel`.");
7867
8042
  }
7868
8043
  }
@@ -7870,7 +8045,7 @@ function warnIfBackendRouteMissing(cwd) {
7870
8045
  // src/commands/cloud/env.ts
7871
8046
  init_logger();
7872
8047
  init_env_file();
7873
- import { join as join34 } from "path";
8048
+ import { join as join35 } from "path";
7874
8049
  import prompts13 from "prompts";
7875
8050
  import chalk50 from "chalk";
7876
8051
  async function cloudEnvPull(options) {
@@ -7891,7 +8066,7 @@ async function cloudEnvPull(options) {
7891
8066
  logger.info("Values were written to files/keychain only \u2014 none were displayed.");
7892
8067
  }
7893
8068
  function writePublicValues(cwd, projectRef2, keys) {
7894
- const envLocalPath = join34(cwd, ".env.local");
8069
+ const envLocalPath = join35(cwd, ".env.local");
7895
8070
  ensureGitignored(cwd, [{ pattern: ".env.local", comment: "local env (per-machine values)" }]);
7896
8071
  upsertEnvVar(envLocalPath, "NEXT_PUBLIC_SUPABASE_URL", `https://${projectRef2}.supabase.co`);
7897
8072
  logger.success("NEXT_PUBLIC_SUPABASE_URL \u2192 .env.local");
@@ -7985,8 +8160,8 @@ async function cloudEnvPush(keys) {
7985
8160
  }
7986
8161
  }
7987
8162
  async function resolveLocalValues(cwd, keys) {
7988
- const publicEnv = { ...parseEnvFile(join34(cwd, ".env")), ...parseEnvFile(join34(cwd, ".env.local")) };
7989
- const serverEnv = parseEnvFile(join34(cwd, ".env.server"));
8163
+ const publicEnv = { ...parseEnvFile(join35(cwd, ".env")), ...parseEnvFile(join35(cwd, ".env.local")) };
8164
+ const serverEnv = parseEnvFile(join35(cwd, ".env.server"));
7990
8165
  const keychain = await resolveKeychainEnv(cwd);
7991
8166
  const vars = [];
7992
8167
  const missing = [];
@@ -8142,8 +8317,8 @@ async function resolveKitAdapters(request2) {
8142
8317
  // src/kit/operations/install.ts
8143
8318
  import { createHash as createHash2 } from "crypto";
8144
8319
  import { hostname as hostname2, tmpdir } from "os";
8145
- import { mkdtempSync, existsSync as existsSync45 } from "fs";
8146
- import { join as join44 } from "path";
8320
+ import { mkdtempSync, existsSync as existsSync46 } from "fs";
8321
+ import { join as join45 } from "path";
8147
8322
 
8148
8323
  // src/kit/events.ts
8149
8324
  import chalk51 from "chalk";
@@ -8220,8 +8395,8 @@ function nextLine(event) {
8220
8395
  }
8221
8396
 
8222
8397
  // src/kit/journal.ts
8223
- import { mkdirSync as mkdirSync10, readdirSync as readdirSync3, readFileSync as readFileSync17, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync23, existsSync as existsSync35, statSync } from "fs";
8224
- import { dirname as dirname9, join as join36 } from "path";
8398
+ import { mkdirSync as mkdirSync10, readdirSync as readdirSync3, readFileSync as readFileSync18, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync24, existsSync as existsSync36, statSync } from "fs";
8399
+ import { dirname as dirname9, join as join37 } from "path";
8225
8400
 
8226
8401
  // src/kit/digest.ts
8227
8402
  import { createHash } from "crypto";
@@ -8262,8 +8437,8 @@ function digestOfJson(value) {
8262
8437
  }
8263
8438
 
8264
8439
  // src/kit/paths.ts
8265
- import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync22, existsSync as existsSync34 } from "fs";
8266
- import { join as join35 } from "path";
8440
+ import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync23, existsSync as existsSync35 } from "fs";
8441
+ import { join as join36 } from "path";
8267
8442
  var KIT_STATE_DIR = ".spfn";
8268
8443
  var OPERATIONS_IGNORE = [
8269
8444
  "# Written by `spfn kit`. One machine's run is not part of the project.",
@@ -8271,27 +8446,27 @@ var OPERATIONS_IGNORE = [
8271
8446
  ""
8272
8447
  ].join("\n");
8273
8448
  function kitPaths(root) {
8274
- const stateDir = join35(root, KIT_STATE_DIR);
8275
- const operationsDir = join35(stateDir, "operations");
8276
- const lockDir = join35(operationsDir, "operation.lock");
8449
+ const stateDir = join36(root, KIT_STATE_DIR);
8450
+ const operationsDir = join36(stateDir, "operations");
8451
+ const lockDir = join36(operationsDir, "operation.lock");
8277
8452
  return {
8278
8453
  root,
8279
8454
  stateDir,
8280
- licenseFile: join35(stateDir, "license.json"),
8281
- lockFile: join35(stateDir, "kit-lock.json"),
8455
+ licenseFile: join36(stateDir, "license.json"),
8456
+ lockFile: join36(stateDir, "kit-lock.json"),
8282
8457
  operationsDir,
8283
- activeJournal: join35(operationsDir, "active.json"),
8284
- historyDir: join35(operationsDir, "history"),
8458
+ activeJournal: join36(operationsDir, "active.json"),
8459
+ historyDir: join36(operationsDir, "history"),
8285
8460
  lockDir,
8286
- lockOwnerFile: join35(lockDir, "owner.json")
8461
+ lockOwnerFile: join36(lockDir, "owner.json")
8287
8462
  };
8288
8463
  }
8289
8464
  function ensureOperationsDir(root) {
8290
8465
  const paths = kitPaths(root);
8291
- const ignoreFile = join35(paths.stateDir, ".gitignore");
8466
+ const ignoreFile = join36(paths.stateDir, ".gitignore");
8292
8467
  mkdirSync9(paths.stateDir, { recursive: true });
8293
- if (!existsSync34(ignoreFile)) {
8294
- writeFileSync22(ignoreFile, OPERATIONS_IGNORE, "utf8");
8468
+ if (!existsSync35(ignoreFile)) {
8469
+ writeFileSync23(ignoreFile, OPERATIONS_IGNORE, "utf8");
8295
8470
  }
8296
8471
  mkdirSync9(paths.operationsDir, { recursive: true });
8297
8472
  return paths.operationsDir;
@@ -8578,14 +8753,14 @@ var JournalStore = class {
8578
8753
  return readJournalFile(this.paths.activeJournal);
8579
8754
  }
8580
8755
  readHistory(operationId) {
8581
- return readJournalFile(join36(this.paths.historyDir, `${operationId}.json`));
8756
+ return readJournalFile(join37(this.paths.historyDir, `${operationId}.json`));
8582
8757
  }
8583
8758
  /** Every journal this project still has, newest update first. */
8584
8759
  listHistory() {
8585
- if (!existsSync35(this.paths.historyDir)) {
8760
+ if (!existsSync36(this.paths.historyDir)) {
8586
8761
  return [];
8587
8762
  }
8588
- return readdirSync3(this.paths.historyDir).filter((name) => name.endsWith(".json")).map((name) => readJournalFile(join36(this.paths.historyDir, name))).filter((journal) => journal !== null).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
8763
+ return readdirSync3(this.paths.historyDir).filter((name) => name.endsWith(".json")).map((name) => readJournalFile(join37(this.paths.historyDir, name))).filter((journal) => journal !== null).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
8589
8764
  }
8590
8765
  create(init) {
8591
8766
  const timestamp = this.now();
@@ -8644,15 +8819,15 @@ var JournalStore = class {
8644
8819
  }
8645
8820
  /** Move a finished operation into history and clear the active slot. */
8646
8821
  archive(journal) {
8647
- writeJsonAtomically(join36(this.paths.historyDir, `${journal.operationId}.json`), journal);
8822
+ writeJsonAtomically(join37(this.paths.historyDir, `${journal.operationId}.json`), journal);
8648
8823
  rmSync2(this.paths.activeJournal, { force: true });
8649
8824
  this.pruneHistory();
8650
8825
  }
8651
8826
  pruneHistory() {
8652
- if (!existsSync35(this.paths.historyDir)) {
8827
+ if (!existsSync36(this.paths.historyDir)) {
8653
8828
  return;
8654
8829
  }
8655
- const files = readdirSync3(this.paths.historyDir).filter((name) => name.endsWith(".json")).map((name) => join36(this.paths.historyDir, name)).sort((left, right) => statSync(right).mtimeMs - statSync(left).mtimeMs);
8830
+ const files = readdirSync3(this.paths.historyDir).filter((name) => name.endsWith(".json")).map((name) => join37(this.paths.historyDir, name)).sort((left, right) => statSync(right).mtimeMs - statSync(left).mtimeMs);
8656
8831
  for (const file of files.slice(this.historyLimit)) {
8657
8832
  rmSync2(file, { force: true });
8658
8833
  }
@@ -8674,11 +8849,11 @@ function upsertCheckpoint(checkpoints, entry) {
8674
8849
  return next;
8675
8850
  }
8676
8851
  function readJournalFile(file) {
8677
- if (!existsSync35(file)) {
8852
+ if (!existsSync36(file)) {
8678
8853
  return null;
8679
8854
  }
8680
8855
  try {
8681
- const parsed = JSON.parse(readFileSync17(file, "utf8"));
8856
+ const parsed = JSON.parse(readFileSync18(file, "utf8"));
8682
8857
  return validateOperationJournal(parsed).valid ? parsed : null;
8683
8858
  } catch {
8684
8859
  return null;
@@ -8687,13 +8862,13 @@ function readJournalFile(file) {
8687
8862
  function writeJsonAtomically(file, value) {
8688
8863
  mkdirSync10(dirname9(file), { recursive: true });
8689
8864
  const temporary = `${file}.tmp`;
8690
- writeFileSync23(temporary, `${JSON.stringify(value, null, 4)}
8865
+ writeFileSync24(temporary, `${JSON.stringify(value, null, 4)}
8691
8866
  `, { encoding: "utf8", mode: 384 });
8692
8867
  renameSync2(temporary, file);
8693
8868
  }
8694
8869
 
8695
8870
  // src/kit/lock.ts
8696
- import { mkdirSync as mkdirSync11, readFileSync as readFileSync18, rmSync as rmSync3, writeFileSync as writeFileSync24, existsSync as existsSync36 } from "fs";
8871
+ import { mkdirSync as mkdirSync11, readFileSync as readFileSync19, rmSync as rmSync3, writeFileSync as writeFileSync25, existsSync as existsSync37 } from "fs";
8697
8872
  import { hostname } from "os";
8698
8873
  function defaultIsProcessAlive(pid) {
8699
8874
  try {
@@ -8729,11 +8904,11 @@ function acquireOperationLock(options) {
8729
8904
  }
8730
8905
  function readLockOwner(root) {
8731
8906
  const paths = kitPaths(root);
8732
- if (!existsSync36(paths.lockOwnerFile)) {
8907
+ if (!existsSync37(paths.lockOwnerFile)) {
8733
8908
  return null;
8734
8909
  }
8735
8910
  try {
8736
- const parsed = JSON.parse(readFileSync18(paths.lockOwnerFile, "utf8"));
8911
+ const parsed = JSON.parse(readFileSync19(paths.lockOwnerFile, "utf8"));
8737
8912
  return typeof parsed?.pid === "number" && typeof parsed?.operationId === "string" ? parsed : null;
8738
8913
  } catch {
8739
8914
  return null;
@@ -8802,7 +8977,7 @@ function tryCreateLockDir(paths) {
8802
8977
  }
8803
8978
  }
8804
8979
  function writeOwner(paths, owner) {
8805
- writeFileSync24(paths.lockOwnerFile, `${JSON.stringify(owner, null, 4)}
8980
+ writeFileSync25(paths.lockOwnerFile, `${JSON.stringify(owner, null, 4)}
8806
8981
  `, "utf8");
8807
8982
  }
8808
8983
  function handle(paths, owner, reclaimed) {
@@ -8816,18 +8991,18 @@ function handle(paths, owner, reclaimed) {
8816
8991
  }
8817
8992
 
8818
8993
  // src/kit/operation-context.ts
8819
- import { mkdirSync as mkdirSync12, readFileSync as readFileSync19, renameSync as renameSync3, rmSync as rmSync4, writeFileSync as writeFileSync25, existsSync as existsSync37 } from "fs";
8820
- import { dirname as dirname10, join as join37 } from "path";
8994
+ import { mkdirSync as mkdirSync12, readFileSync as readFileSync20, renameSync as renameSync3, rmSync as rmSync4, writeFileSync as writeFileSync26, existsSync as existsSync38 } from "fs";
8995
+ import { dirname as dirname10, join as join38 } from "path";
8821
8996
  function contextFile(root) {
8822
- return join37(kitPaths(root).operationsDir, "context.json");
8997
+ return join38(kitPaths(root).operationsDir, "context.json");
8823
8998
  }
8824
8999
  function readOperationContext(root) {
8825
9000
  const file = contextFile(root);
8826
- if (!existsSync37(file)) {
9001
+ if (!existsSync38(file)) {
8827
9002
  return null;
8828
9003
  }
8829
9004
  try {
8830
- const parsed = JSON.parse(readFileSync19(file, "utf8"));
9005
+ const parsed = JSON.parse(readFileSync20(file, "utf8"));
8831
9006
  return parsed?.schemaVersion === 1 && typeof parsed.operationId === "string" ? parsed : null;
8832
9007
  } catch {
8833
9008
  return null;
@@ -8837,7 +9012,7 @@ function writeOperationContext(root, context) {
8837
9012
  const file = contextFile(root);
8838
9013
  mkdirSync12(dirname10(file), { recursive: true });
8839
9014
  const temporary = `${file}.tmp`;
8840
- writeFileSync25(temporary, `${JSON.stringify(context, null, 4)}
9015
+ writeFileSync26(temporary, `${JSON.stringify(context, null, 4)}
8841
9016
  `, "utf8");
8842
9017
  renameSync3(temporary, file);
8843
9018
  }
@@ -9472,8 +9647,8 @@ var KeychainKitCredentialStore = class {
9472
9647
  };
9473
9648
 
9474
9649
  // src/kit/tooling.ts
9475
- import { cpSync, mkdirSync as mkdirSync13, readdirSync as readdirSync4, readFileSync as readFileSync20, rmSync as rmSync5, statSync as statSync2, existsSync as existsSync38 } from "fs";
9476
- import { join as join38, relative as relative2, sep as sep3 } from "path";
9650
+ import { cpSync, mkdirSync as mkdirSync13, readdirSync as readdirSync4, readFileSync as readFileSync21, rmSync as rmSync5, statSync as statSync2, existsSync as existsSync39 } from "fs";
9651
+ import { join as join39, relative as relative2, sep as sep3 } from "path";
9477
9652
 
9478
9653
  // src/kit/manifest.ts
9479
9654
  var DEFAULT_AGENT_PACK_ROOT = ".spfn/agent-pack";
@@ -9784,7 +9959,7 @@ function snapshotTree(root, ignore = ["node_modules", ".git"]) {
9784
9959
  if (ignore.includes(entry.name)) {
9785
9960
  continue;
9786
9961
  }
9787
- const absolute = join38(directory, entry.name);
9962
+ const absolute = join39(directory, entry.name);
9788
9963
  if (entry.isDirectory()) {
9789
9964
  walk(absolute);
9790
9965
  continue;
@@ -9794,11 +9969,11 @@ function snapshotTree(root, ignore = ["node_modules", ".git"]) {
9794
9969
  }
9795
9970
  files.set(
9796
9971
  relative2(root, absolute).split(sep3).join("/"),
9797
- sha256Digest(readFileSync20(absolute))
9972
+ sha256Digest(readFileSync21(absolute))
9798
9973
  );
9799
9974
  }
9800
9975
  };
9801
- if (existsSync38(root) && statSync2(root).isDirectory()) {
9976
+ if (existsSync39(root) && statSync2(root).isDirectory()) {
9802
9977
  walk(root);
9803
9978
  }
9804
9979
  return { files };
@@ -9855,22 +10030,22 @@ function assertWritesWithinAllowlist(diff, allowlist, context) {
9855
10030
  }
9856
10031
 
9857
10032
  // src/kit/agent-pack.ts
9858
- import { existsSync as existsSync39, mkdirSync as mkdirSync14, readFileSync as readFileSync21, writeFileSync as writeFileSync26 } from "fs";
9859
- import { dirname as dirname11, join as join39 } from "path";
10033
+ import { existsSync as existsSync40, mkdirSync as mkdirSync14, readFileSync as readFileSync22, writeFileSync as writeFileSync27 } from "fs";
10034
+ import { dirname as dirname11, join as join40 } from "path";
9860
10035
  var AGENT_PACK_RECORD_PATH = ".spfn/agent-pack.json";
9861
10036
  function writeAgentPackRecord(projectDir2, record) {
9862
- const file = join39(projectDir2, AGENT_PACK_RECORD_PATH);
10037
+ const file = join40(projectDir2, AGENT_PACK_RECORD_PATH);
9863
10038
  mkdirSync14(dirname11(file), { recursive: true });
9864
- writeFileSync26(file, `${JSON.stringify(record, null, 4)}
10039
+ writeFileSync27(file, `${JSON.stringify(record, null, 4)}
9865
10040
  `, "utf8");
9866
10041
  }
9867
10042
  function readAgentPackRecord(projectDir2) {
9868
- const file = join39(projectDir2, AGENT_PACK_RECORD_PATH);
9869
- if (!existsSync39(file)) {
10043
+ const file = join40(projectDir2, AGENT_PACK_RECORD_PATH);
10044
+ if (!existsSync40(file)) {
9870
10045
  return null;
9871
10046
  }
9872
10047
  try {
9873
- const parsed = JSON.parse(readFileSync21(file, "utf8"));
10048
+ const parsed = JSON.parse(readFileSync22(file, "utf8"));
9874
10049
  return parsed.schemaVersion === 1 && typeof parsed.files === "object" && parsed.files !== null ? parsed : null;
9875
10050
  } catch {
9876
10051
  return null;
@@ -9878,7 +10053,7 @@ function readAgentPackRecord(projectDir2) {
9878
10053
  }
9879
10054
 
9880
10055
  // src/kit/installed-state.ts
9881
- import { mkdirSync as mkdirSync15, readFileSync as readFileSync22, renameSync as renameSync4, writeFileSync as writeFileSync27, existsSync as existsSync40 } from "fs";
10056
+ import { mkdirSync as mkdirSync15, readFileSync as readFileSync23, renameSync as renameSync4, writeFileSync as writeFileSync28, existsSync as existsSync41 } from "fs";
9882
10057
  import { dirname as dirname12 } from "path";
9883
10058
  function readLicenseFile(file) {
9884
10059
  const parsed = readJson(file);
@@ -9921,7 +10096,7 @@ function requireInstalledLock(file) {
9921
10096
  const lock = readInstalledLock(file);
9922
10097
  if (lock === null) {
9923
10098
  throw new KitError("KIT_LOCK_INVALID", "This project has no readable .spfn/kit-lock.json.", {
9924
- evidence: { file, exists: existsSync40(file) },
10099
+ evidence: { file, exists: existsSync41(file) },
9925
10100
  next: { command: "spfn kit recover --json", requiresHumanApproval: false }
9926
10101
  });
9927
10102
  }
@@ -9956,11 +10131,11 @@ function lockFromManifest(manifest, context) {
9956
10131
  };
9957
10132
  }
9958
10133
  function readJson(file) {
9959
- if (!existsSync40(file)) {
10134
+ if (!existsSync41(file)) {
9960
10135
  return null;
9961
10136
  }
9962
10137
  try {
9963
- return JSON.parse(readFileSync22(file, "utf8"));
10138
+ return JSON.parse(readFileSync23(file, "utf8"));
9964
10139
  } catch {
9965
10140
  return null;
9966
10141
  }
@@ -9968,17 +10143,17 @@ function readJson(file) {
9968
10143
  function writeJsonAtomically2(file, value) {
9969
10144
  mkdirSync15(dirname12(file), { recursive: true });
9970
10145
  const temporary = `${file}.tmp`;
9971
- writeFileSync27(temporary, `${JSON.stringify(value, null, 4)}
10146
+ writeFileSync28(temporary, `${JSON.stringify(value, null, 4)}
9972
10147
  `, "utf8");
9973
10148
  renameSync4(temporary, file);
9974
10149
  }
9975
10150
 
9976
10151
  // src/kit/drift.ts
9977
- import { readFileSync as readFileSync23, existsSync as existsSync41 } from "fs";
9978
- import { join as join40 } from "path";
10152
+ import { readFileSync as readFileSync24, existsSync as existsSync42 } from "fs";
10153
+ import { join as join41 } from "path";
9979
10154
  function fileDigest(root, relativePath) {
9980
- const file = join40(root, relativePath);
9981
- return existsSync41(file) ? sha256Digest(readFileSync23(file)) : null;
10155
+ const file = join41(root, relativePath);
10156
+ return existsSync42(file) ? sha256Digest(readFileSync24(file)) : null;
9982
10157
  }
9983
10158
  function agentPackTargets(root, lock) {
9984
10159
  if (lock.agentPack.root === void 0) {
@@ -10009,12 +10184,12 @@ function managedDigests(root, lock) {
10009
10184
  }
10010
10185
 
10011
10186
  // src/kit/operations/shared.ts
10012
- import { mkdirSync as mkdirSync17, readdirSync as readdirSync5, readFileSync as readFileSync26, writeFileSync as writeFileSync30, existsSync as existsSync44 } from "fs";
10013
- import { dirname as dirname14, join as join43 } from "path";
10187
+ import { mkdirSync as mkdirSync17, readdirSync as readdirSync5, readFileSync as readFileSync27, writeFileSync as writeFileSync31, existsSync as existsSync45 } from "fs";
10188
+ import { dirname as dirname14, join as join44 } from "path";
10014
10189
 
10015
10190
  // src/kit/expand.ts
10016
- import { mkdirSync as mkdirSync16, existsSync as existsSync42, readFileSync as readFileSync24, writeFileSync as writeFileSync28 } from "fs";
10017
- import { dirname as dirname13, join as join41 } from "path";
10191
+ import { mkdirSync as mkdirSync16, existsSync as existsSync43, readFileSync as readFileSync25, writeFileSync as writeFileSync29 } from "fs";
10192
+ import { dirname as dirname13, join as join42 } from "path";
10018
10193
 
10019
10194
  // src/kit/tar.ts
10020
10195
  var BLOCK = 512;
@@ -10116,26 +10291,26 @@ function expandArchive(bytes, request2) {
10116
10291
  let matched = 0;
10117
10292
  for (const entry of entries) {
10118
10293
  const relative3 = request2.root === void 0 || request2.root.length === 0 ? entry.path : `${request2.root.replace(/\/+$/, "")}/${entry.path}`;
10119
- const destination = join41(request2.targetDir, relative3);
10294
+ const destination = join42(request2.targetDir, relative3);
10120
10295
  if (entry.kind === "directory") {
10121
10296
  mkdirSync16(destination, { recursive: true });
10122
10297
  continue;
10123
10298
  }
10124
10299
  const digest = sha256Digest(entry.bytes);
10125
10300
  files[relative3] = digest;
10126
- if (existsSync42(destination) && request2.existing !== "replace") {
10301
+ if (existsSync43(destination) && request2.existing !== "replace") {
10127
10302
  assertSameBytes(destination, relative3, digest);
10128
10303
  matched += 1;
10129
10304
  continue;
10130
10305
  }
10131
10306
  mkdirSync16(dirname13(destination), { recursive: true });
10132
- writeFileSync28(destination, entry.bytes, { mode: entry.mode === 0 ? 420 : entry.mode });
10307
+ writeFileSync29(destination, entry.bytes, { mode: entry.mode === 0 ? 420 : entry.mode });
10133
10308
  written += 1;
10134
10309
  }
10135
10310
  return { files, written, matched };
10136
10311
  }
10137
10312
  function assertSameBytes(destination, relative3, expected) {
10138
- const actual = sha256Digest(readFileSync24(destination));
10313
+ const actual = sha256Digest(readFileSync25(destination));
10139
10314
  if (actual === expected) {
10140
10315
  return;
10141
10316
  }
@@ -10243,15 +10418,15 @@ function registryNpmrc(scopes, registryUrl) {
10243
10418
  }
10244
10419
 
10245
10420
  // src/kit/package-graph.ts
10246
- import { existsSync as existsSync43, readFileSync as readFileSync25, writeFileSync as writeFileSync29 } from "fs";
10247
- import { join as join42 } from "path";
10421
+ import { existsSync as existsSync44, readFileSync as readFileSync26, writeFileSync as writeFileSync30 } from "fs";
10422
+ import { join as join43 } from "path";
10248
10423
  var DEPENDENCY_BLOCKS = ["dependencies", "devDependencies", "optionalDependencies"];
10249
10424
  function applyManifestVersions(projectDir2, manifest) {
10250
- const file = join42(projectDir2, "package.json");
10251
- if (!existsSync43(file)) {
10425
+ const file = join43(projectDir2, "package.json");
10426
+ if (!existsSync44(file)) {
10252
10427
  return [];
10253
10428
  }
10254
- const raw = readFileSync25(file, "utf8");
10429
+ const raw = readFileSync26(file, "utf8");
10255
10430
  const document = JSON.parse(raw);
10256
10431
  const wanted = new Map(manifest.packages.map((entry) => [entry.name, entry.version]));
10257
10432
  const changes = [];
@@ -10270,7 +10445,7 @@ function applyManifestVersions(projectDir2, manifest) {
10270
10445
  }
10271
10446
  }
10272
10447
  if (changes.length > 0) {
10273
- writeFileSync29(file, `${JSON.stringify(document, null, indentationOf(raw))}${trailingNewline(raw)}`);
10448
+ writeFileSync30(file, `${JSON.stringify(document, null, indentationOf(raw))}${trailingNewline(raw)}`);
10274
10449
  }
10275
10450
  return changes;
10276
10451
  }
@@ -10298,12 +10473,12 @@ function installedGraphMismatches(projectDir2, manifest, skip = () => false) {
10298
10473
  return mismatches;
10299
10474
  }
10300
10475
  function installedVersion(projectDir2, name) {
10301
- const file = join42(projectDir2, "node_modules", ...name.split("/"), "package.json");
10302
- if (!existsSync43(file)) {
10476
+ const file = join43(projectDir2, "node_modules", ...name.split("/"), "package.json");
10477
+ if (!existsSync44(file)) {
10303
10478
  return null;
10304
10479
  }
10305
10480
  try {
10306
- const version = JSON.parse(readFileSync25(file, "utf8")).version;
10481
+ const version = JSON.parse(readFileSync26(file, "utf8")).version;
10307
10482
  return typeof version === "string" ? version : null;
10308
10483
  } catch {
10309
10484
  return null;
@@ -10390,7 +10565,7 @@ function latestActiveRelease(catalog) {
10390
10565
  return active[0];
10391
10566
  }
10392
10567
  function assertEmptyTarget(targetDir) {
10393
- if (!existsSync44(targetDir)) {
10568
+ if (!existsSync45(targetDir)) {
10394
10569
  return;
10395
10570
  }
10396
10571
  const entries = readdirSync5(targetDir).filter((name) => name !== ".DS_Store");
@@ -10421,13 +10596,13 @@ async function materializeTargets(adapters, projectDir2, targets, options = {})
10421
10596
  continue;
10422
10597
  }
10423
10598
  written[target.path] = digest;
10424
- writeManagedFile(join43(projectDir2, target.path), target.path, bytes, digest, options.existing);
10599
+ writeManagedFile(join44(projectDir2, target.path), target.path, bytes, digest, options.existing);
10425
10600
  }
10426
10601
  return written;
10427
10602
  }
10428
10603
  function writeManagedFile(file, path5, bytes, digest, existing = "verify") {
10429
- if (existsSync44(file) && existing !== "replace") {
10430
- const actual = sha256Digest(readFileSync26(file));
10604
+ if (existsSync45(file) && existing !== "replace") {
10605
+ const actual = sha256Digest(readFileSync27(file));
10431
10606
  if (actual === digest) {
10432
10607
  return;
10433
10608
  }
@@ -10437,7 +10612,7 @@ function writeManagedFile(file, path5, bytes, digest, existing = "verify") {
10437
10612
  });
10438
10613
  }
10439
10614
  mkdirSync17(dirname14(file), { recursive: true });
10440
- writeFileSync30(file, bytes);
10615
+ writeFileSync31(file, bytes);
10441
10616
  }
10442
10617
  async function installFrozenGraph(adapters, options) {
10443
10618
  const maxAttempts = options.allowSessionRetry === false ? 1 : 2;
@@ -10499,7 +10674,7 @@ function scopesOf(manifest) {
10499
10674
  return scopes.length === 0 ? ["@superfunction"] : [...new Set(scopes)];
10500
10675
  }
10501
10676
  function writeRegistryNpmrc(projectDir2, manifest, registryUrl) {
10502
- writeFileSync30(join43(projectDir2, ".npmrc"), registryNpmrc(scopesOf(manifest), registryUrl), "utf8");
10677
+ writeFileSync31(join44(projectDir2, ".npmrc"), registryNpmrc(scopesOf(manifest), registryUrl), "utf8");
10503
10678
  }
10504
10679
  function assertGraphInstalled(options) {
10505
10680
  if (options.expect === void 0) {
@@ -10747,10 +10922,10 @@ function installSteps(options) {
10747
10922
  return { kind: "done", evidence: { attempts: evidence.attempts, tooling: tooling.specifier } };
10748
10923
  },
10749
10924
  async verify() {
10750
- if (!existsSync45(join44(projectDir2, "node_modules"))) {
10925
+ if (!existsSync46(join45(projectDir2, "node_modules"))) {
10751
10926
  return { ok: false, reason: "dependencies-missing" };
10752
10927
  }
10753
- if (!existsSync45(paths.lockFile)) {
10928
+ if (!existsSync46(paths.lockFile)) {
10754
10929
  writeLock(options, projectDir2, adapters.clock.now());
10755
10930
  }
10756
10931
  return { ok: true };
@@ -10849,7 +11024,7 @@ async function verifyToolingPlan(options, projectDir2, operationId) {
10849
11024
  });
10850
11025
  const isolated = await runIsolated(
10851
11026
  projectDir2,
10852
- join44(mkdtempSync(join44(tmpdir(), "spfn-kit-")), operationId),
11027
+ join45(mkdtempSync(join45(tmpdir(), "spfn-kit-")), operationId),
10853
11028
  (workingCopy) => discovered.tooling.planInstall({
10854
11029
  projectDir: workingCopy,
10855
11030
  release: options.manifest.version
@@ -11157,8 +11332,8 @@ async function startRecovery(sink, adapters, activationId) {
11157
11332
  }
11158
11333
 
11159
11334
  // src/kit/customer-source.ts
11160
- import { existsSync as existsSync46, mkdirSync as mkdirSync18, readFileSync as readFileSync27, writeFileSync as writeFileSync31 } from "fs";
11161
- import { dirname as dirname15, join as join45 } from "path";
11335
+ import { existsSync as existsSync47, mkdirSync as mkdirSync18, readFileSync as readFileSync28, writeFileSync as writeFileSync32 } from "fs";
11336
+ import { dirname as dirname15, join as join46 } from "path";
11162
11337
  var IGNORED_DIRECTORIES = ["node_modules", ".git", ".next", ".turbo", ".vercel", "dist", "coverage"];
11163
11338
  var KIT_OWNED_FILES = ["pnpm-lock.yaml", "package.json", ".npmrc", "tsconfig.tsbuildinfo"];
11164
11339
  function declaredPaths(declaration) {
@@ -11203,21 +11378,21 @@ function compareCustomerSource(before, after) {
11203
11378
  return paths.filter((path5) => before[path5] !== after[path5]).map((path5) => ({ path: path5, before: before[path5] ?? null, after: after[path5] ?? null }));
11204
11379
  }
11205
11380
  function customerBaselinePath(projectDir2, operationId) {
11206
- return join45(projectDir2, ".spfn", "operations", `${operationId}.customer-source.json`);
11381
+ return join46(projectDir2, ".spfn", "operations", `${operationId}.customer-source.json`);
11207
11382
  }
11208
11383
  function writeCustomerBaseline(projectDir2, baseline) {
11209
11384
  const path5 = customerBaselinePath(projectDir2, baseline.operationId);
11210
11385
  mkdirSync18(dirname15(path5), { recursive: true });
11211
- writeFileSync31(path5, `${JSON.stringify(baseline, null, 4)}
11386
+ writeFileSync32(path5, `${JSON.stringify(baseline, null, 4)}
11212
11387
  `);
11213
11388
  }
11214
11389
  function readCustomerBaseline(projectDir2, operationId) {
11215
11390
  const path5 = customerBaselinePath(projectDir2, operationId);
11216
- if (!existsSync46(path5)) {
11391
+ if (!existsSync47(path5)) {
11217
11392
  return null;
11218
11393
  }
11219
11394
  try {
11220
- const parsed = JSON.parse(readFileSync27(path5, "utf8"));
11395
+ const parsed = JSON.parse(readFileSync28(path5, "utf8"));
11221
11396
  return parsed?.schemaVersion === 1 && typeof parsed.files === "object" ? parsed : null;
11222
11397
  } catch {
11223
11398
  return null;
@@ -11619,8 +11794,8 @@ function planResult(sink, plan, digest) {
11619
11794
  }
11620
11795
 
11621
11796
  // src/kit/operations/inspect.ts
11622
- import { existsSync as existsSync47 } from "fs";
11623
- import { join as join46 } from "path";
11797
+ import { existsSync as existsSync48 } from "fs";
11798
+ import { join as join47 } from "path";
11624
11799
  async function runStatus(request2, adapters) {
11625
11800
  const paths = kitPaths(request2.projectDir);
11626
11801
  const lock = readInstalledLock(paths.lockFile);
@@ -11733,7 +11908,7 @@ async function runCheck(request2, adapters) {
11733
11908
  fixCommand: `spfn kit resume ${journal.operationId} --json`
11734
11909
  });
11735
11910
  }
11736
- if (!existsSync47(join46(request2.projectDir, "node_modules"))) {
11911
+ if (!existsSync48(join47(request2.projectDir, "node_modules"))) {
11737
11912
  diagnostics.push({
11738
11913
  code: "KIT_GATE_FAILED",
11739
11914
  severity: "warning",
@@ -12145,8 +12320,8 @@ function parseKeys(raw) {
12145
12320
  }
12146
12321
 
12147
12322
  // src/kit/scaffold.ts
12148
- import { mkdirSync as mkdirSync19, existsSync as existsSync48, readFileSync as readFileSync28, writeFileSync as writeFileSync32 } from "fs";
12149
- import { dirname as dirname16, join as join47 } from "path";
12323
+ import { mkdirSync as mkdirSync19, existsSync as existsSync49, readFileSync as readFileSync29, writeFileSync as writeFileSync33 } from "fs";
12324
+ import { dirname as dirname16, join as join48 } from "path";
12150
12325
 
12151
12326
  // src/kit/http/registry.ts
12152
12327
  import { createHash as createHash3 } from "crypto";
@@ -12492,20 +12667,20 @@ var ArtifactScaffoldPort = class {
12492
12667
  }
12493
12668
  };
12494
12669
  function seedProjectName(targetDir, name) {
12495
- const file = join47(targetDir, "package.json");
12496
- if (!existsSync48(file)) {
12670
+ const file = join48(targetDir, "package.json");
12671
+ if (!existsSync49(file)) {
12497
12672
  return;
12498
12673
  }
12499
12674
  let parsed;
12500
12675
  try {
12501
- parsed = JSON.parse(readFileSync28(file, "utf8"));
12676
+ parsed = JSON.parse(readFileSync29(file, "utf8"));
12502
12677
  } catch {
12503
12678
  throw new KitError("KIT_MANIFEST_INVALID", "The release scaffold's package.json is not readable JSON.", {
12504
12679
  evidence: { path: "package.json" }
12505
12680
  });
12506
12681
  }
12507
12682
  parsed.name = packageNameOf(name);
12508
- writeFileSync32(file, `${JSON.stringify(parsed, null, 4)}
12683
+ writeFileSync33(file, `${JSON.stringify(parsed, null, 4)}
12509
12684
  `, "utf8");
12510
12685
  }
12511
12686
  function packageNameOf(name) {
@@ -12513,9 +12688,9 @@ function packageNameOf(name) {
12513
12688
  return cleaned.length === 0 ? "kit-project" : cleaned;
12514
12689
  }
12515
12690
  function writeScaffoldRecord(targetDir, record) {
12516
- const file = join47(targetDir, SCAFFOLD_RECORD_PATH);
12691
+ const file = join48(targetDir, SCAFFOLD_RECORD_PATH);
12517
12692
  mkdirSync19(dirname16(file), { recursive: true });
12518
- writeFileSync32(file, `${JSON.stringify(record, null, 4)}
12693
+ writeFileSync33(file, `${JSON.stringify(record, null, 4)}
12519
12694
  `, "utf8");
12520
12695
  }
12521
12696
 
@@ -12895,7 +13070,7 @@ function requireCatalogUrl(projectDir2) {
12895
13070
 
12896
13071
  // src/kit/local/index.ts
12897
13072
  import { createRequire as createRequire2 } from "module";
12898
- import { join as join51 } from "path";
13073
+ import { join as join52 } from "path";
12899
13074
  import { pathToFileURL as pathToFileURL4 } from "url";
12900
13075
 
12901
13076
  // src/kit/local/process.ts
@@ -13044,8 +13219,8 @@ ${result.stdout}`;
13044
13219
  }
13045
13220
 
13046
13221
  // src/kit/local/database.ts
13047
- import { existsSync as existsSync49 } from "fs";
13048
- import { dirname as dirname17, join as join48 } from "path";
13222
+ import { existsSync as existsSync50 } from "fs";
13223
+ import { dirname as dirname17, join as join49 } from "path";
13049
13224
  import { fileURLToPath as fileURLToPath3 } from "url";
13050
13225
  var DEFAULT_DATABASE_TIMEOUT_MS = 9e5;
13051
13226
  var SpfnDatabasePort = class {
@@ -13130,8 +13305,8 @@ function spfnCommand() {
13130
13305
  function resolveSpfnBin(from = fileURLToPath3(import.meta.url)) {
13131
13306
  let directory = dirname17(from);
13132
13307
  for (let depth = 0; depth < 8; depth += 1) {
13133
- const candidate = join48(directory, "bin", "spfn.js");
13134
- if (existsSync49(candidate)) {
13308
+ const candidate = join49(directory, "bin", "spfn.js");
13309
+ if (existsSync50(candidate)) {
13135
13310
  return candidate;
13136
13311
  }
13137
13312
  const parent = dirname17(directory);
@@ -13144,8 +13319,8 @@ function resolveSpfnBin(from = fileURLToPath3(import.meta.url)) {
13144
13319
  }
13145
13320
 
13146
13321
  // src/kit/local/gates.ts
13147
- import { existsSync as existsSync50, readFileSync as readFileSync29 } from "fs";
13148
- import { join as join49 } from "path";
13322
+ import { existsSync as existsSync51, readFileSync as readFileSync30 } from "fs";
13323
+ import { join as join50 } from "path";
13149
13324
  var DEFAULT_GATE_TIMEOUT_MS = 9e5;
13150
13325
  var GATE_PLANS = {
13151
13326
  "kit-check": { scripts: [], fallback: spfnFallback(["kit", "check", "--json"]) },
@@ -13196,12 +13371,12 @@ var CommandGatePort = class {
13196
13371
  }
13197
13372
  };
13198
13373
  function projectScripts(cwd) {
13199
- const file = join49(cwd, "package.json");
13200
- if (!existsSync50(file)) {
13374
+ const file = join50(cwd, "package.json");
13375
+ if (!existsSync51(file)) {
13201
13376
  return {};
13202
13377
  }
13203
13378
  try {
13204
- const parsed = JSON.parse(readFileSync29(file, "utf8"));
13379
+ const parsed = JSON.parse(readFileSync30(file, "utf8"));
13205
13380
  return parsed.scripts ?? {};
13206
13381
  } catch {
13207
13382
  return {};
@@ -13213,8 +13388,8 @@ function spfnFallback(args) {
13213
13388
  }
13214
13389
 
13215
13390
  // src/kit/local/git.ts
13216
- import { existsSync as existsSync51 } from "fs";
13217
- import { join as join50 } from "path";
13391
+ import { existsSync as existsSync52 } from "fs";
13392
+ import { join as join51 } from "path";
13218
13393
  var FALLBACK_AUTHOR_NAME = "spfn kit";
13219
13394
  var FALLBACK_AUTHOR_EMAIL = "kit@superfunction.xyz";
13220
13395
  var DEFAULT_GIT_TIMEOUT_MS = 12e4;
@@ -13225,7 +13400,7 @@ var SystemGitPort = class {
13225
13400
  }
13226
13401
  /** Start a repository, or leave the one that is already there alone. */
13227
13402
  async init(request2) {
13228
- if (existsSync51(join50(request2.cwd, ".git"))) {
13403
+ if (existsSync52(join51(request2.cwd, ".git"))) {
13229
13404
  return;
13230
13405
  }
13231
13406
  const result = await this.git(request2.cwd, ["init"]);
@@ -13342,7 +13517,7 @@ function createKitLocalPorts(options = {}) {
13342
13517
  }
13343
13518
  function createProjectModuleLoader() {
13344
13519
  return async (specifier, projectDir2) => {
13345
- const resolve4 = createRequire2(join51(projectDir2, "package.json"));
13520
+ const resolve4 = createRequire2(join52(projectDir2, "package.json"));
13346
13521
  return import(pathToFileURL4(resolve4.resolve(specifier)).href);
13347
13522
  };
13348
13523
  }