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/README.md +22 -2
- package/dist/index.js +483 -308
- package/dist/templates/README.md +8 -1
- package/package.json +1 -1
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.
|
|
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
|
|
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
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
1301
|
-
const filePath = join12(cwd,
|
|
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(
|
|
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
|
|
1987
|
-
import { join as
|
|
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
|
|
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(
|
|
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 (
|
|
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 =
|
|
2391
|
+
const dir = join17(filePath, "..");
|
|
2218
2392
|
const fileName = filePath.split("/").pop();
|
|
2219
2393
|
const watcher = watch(dir, (_event, name) => {
|
|
2220
|
-
if (name === fileName &&
|
|
2394
|
+
if (name === fileName && existsSync16(filePath)) {
|
|
2221
2395
|
watcher.close();
|
|
2222
2396
|
clearTimeout(timer);
|
|
2223
|
-
resolve4(
|
|
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 =
|
|
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 (!
|
|
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 =
|
|
2445
|
+
const packageJsonPath = join17(cwd, "package.json");
|
|
2271
2446
|
let hasNext = false;
|
|
2272
|
-
if (
|
|
2273
|
-
const packageJson = JSON.parse(
|
|
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 =
|
|
2277
|
-
const serverEntry =
|
|
2278
|
-
const watcherEntry =
|
|
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 =
|
|
2281
|
-
if (
|
|
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 =
|
|
2286
|
-
if (
|
|
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 =
|
|
2291
|
-
|
|
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
|
-
|
|
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
|
|
2579
|
-
import { join as
|
|
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 =
|
|
2781
|
+
const packageJsonPath = join18(cwd, "package.json");
|
|
2607
2782
|
let hasNext = false;
|
|
2608
|
-
if (
|
|
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 =
|
|
2615
|
-
const hasServer =
|
|
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 =
|
|
2832
|
+
const outputDir = join18(cwd, ".spfn", "server");
|
|
2658
2833
|
mkdirSync2(outputDir, { recursive: true });
|
|
2659
|
-
const serverDir2 =
|
|
2660
|
-
if (!
|
|
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 =
|
|
2869
|
+
const prodServerPath = join18(cwd, ".spfn", "prod-server.mjs");
|
|
2695
2870
|
const prodServerContent = renderProdServerEntry();
|
|
2696
|
-
|
|
2871
|
+
writeFileSync12(prodServerPath, prodServerContent);
|
|
2697
2872
|
spinner.succeed(`SPFN server build completed \u2192 .spfn/server`);
|
|
2698
|
-
const routesDir =
|
|
2699
|
-
if (
|
|
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
|
|
2756
|
-
import { join as
|
|
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 =
|
|
2940
|
+
const packageJsonPath = join19(cwd, "package.json");
|
|
2766
2941
|
let hasNext = false;
|
|
2767
|
-
if (
|
|
2768
|
-
const packageJson = JSON.parse(
|
|
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 =
|
|
2772
|
-
const hasBuiltServer =
|
|
2773
|
-
const nextBuildDir =
|
|
2774
|
-
const hasNextBuild =
|
|
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 =
|
|
2785
|
-
if (!
|
|
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
|
|
2868
|
-
import { join as
|
|
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) =>
|
|
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 =
|
|
2900
|
-
const runnerEntry =
|
|
3074
|
+
const tempDir = join20(cwd, ".spfn");
|
|
3075
|
+
const runnerEntry = join20(tempDir, "provision.mjs");
|
|
2901
3076
|
mkdirSync3(tempDir, { recursive: true });
|
|
2902
|
-
|
|
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
|
|
2946
|
-
import { join as
|
|
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 =
|
|
2951
|
-
if (
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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 &&
|
|
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
|
|
3743
|
-
import { existsSync as
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
4153
|
-
if (
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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 &&
|
|
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 &&
|
|
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
|
|
4721
|
-
import { join as
|
|
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 =
|
|
4744
|
-
if (!
|
|
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(
|
|
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 =
|
|
4766
|
-
const newSql =
|
|
4767
|
-
if (
|
|
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 =
|
|
4771
|
-
const newSnapshot =
|
|
4772
|
-
if (
|
|
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
|
-
|
|
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
|
|
4839
|
-
import { join as
|
|
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
|
|
4847
|
-
import { dirname as dirname4, join as
|
|
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:
|
|
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 =
|
|
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 =
|
|
5055
|
+
const templatesDir = join25(findTemplatesPath(), "vercel");
|
|
4881
5056
|
for (const file of FILES) {
|
|
4882
|
-
const targetPath =
|
|
4883
|
-
if (
|
|
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(
|
|
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 =
|
|
4934
|
-
const pkgJsonPath =
|
|
4935
|
-
if (!
|
|
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 (!
|
|
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(
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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 (!
|
|
5514
|
+
if (!existsSync28(filePath)) {
|
|
5340
5515
|
continue;
|
|
5341
5516
|
}
|
|
5342
|
-
const content =
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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 (!
|
|
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 (
|
|
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
|
-
|
|
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
|
|
6520
|
-
import { join as
|
|
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:
|
|
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 (
|
|
6704
|
+
if (existsSync30(join28(dir, filename))) {
|
|
6530
6705
|
return dir;
|
|
6531
6706
|
}
|
|
6532
|
-
const parent =
|
|
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 ?
|
|
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 =
|
|
6549
|
-
if (!
|
|
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) =>
|
|
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 =
|
|
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
|
|
6994
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync6 } from "fs";
|
|
6820
6995
|
import { homedir } from "os";
|
|
6821
|
-
import { dirname as dirname6, join as
|
|
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 ??
|
|
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 (
|
|
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
|
|
6872
|
-
import { join as
|
|
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 =
|
|
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
|
-
|
|
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 (!
|
|
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 (!
|
|
7105
|
+
if (!existsSync32(configPath)) {
|
|
6931
7106
|
return { creation_rules: [] };
|
|
6932
7107
|
}
|
|
6933
|
-
return parse3(
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
7078
|
-
import { dirname as dirname7, join as
|
|
7079
|
-
var CONFIG_REL_PATH =
|
|
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
|
|
7256
|
+
return join33(cwd, CONFIG_REL_PATH);
|
|
7082
7257
|
}
|
|
7083
7258
|
function readCloudConfig(cwd) {
|
|
7084
7259
|
const path5 = cloudConfigPath(cwd);
|
|
7085
|
-
if (!
|
|
7260
|
+
if (!existsSync33(path5)) {
|
|
7086
7261
|
return {};
|
|
7087
7262
|
}
|
|
7088
7263
|
let parsed;
|
|
7089
7264
|
try {
|
|
7090
|
-
parsed = JSON.parse(
|
|
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
|
-
|
|
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
|
|
7744
|
-
import { dirname as dirname8, join as
|
|
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 =
|
|
7945
|
+
const vercelJsonPath = join34(cwd, "vercel.json");
|
|
7771
7946
|
let vercelJson = {};
|
|
7772
|
-
if (
|
|
7947
|
+
if (existsSync34(vercelJsonPath)) {
|
|
7773
7948
|
let parsed;
|
|
7774
7949
|
try {
|
|
7775
|
-
parsed = JSON.parse(
|
|
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
|
-
|
|
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 =
|
|
7817
|
-
if (
|
|
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
|
-
|
|
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 =
|
|
7865
|
-
if (!
|
|
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
|
|
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 =
|
|
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(
|
|
7989
|
-
const serverEnv = parseEnvFile(
|
|
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
|
|
8146
|
-
import { join as
|
|
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
|
|
8224
|
-
import { dirname as dirname9, join as
|
|
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
|
|
8266
|
-
import { join as
|
|
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 =
|
|
8275
|
-
const operationsDir =
|
|
8276
|
-
const lockDir =
|
|
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:
|
|
8281
|
-
lockFile:
|
|
8455
|
+
licenseFile: join36(stateDir, "license.json"),
|
|
8456
|
+
lockFile: join36(stateDir, "kit-lock.json"),
|
|
8282
8457
|
operationsDir,
|
|
8283
|
-
activeJournal:
|
|
8284
|
-
historyDir:
|
|
8458
|
+
activeJournal: join36(operationsDir, "active.json"),
|
|
8459
|
+
historyDir: join36(operationsDir, "history"),
|
|
8285
8460
|
lockDir,
|
|
8286
|
-
lockOwnerFile:
|
|
8461
|
+
lockOwnerFile: join36(lockDir, "owner.json")
|
|
8287
8462
|
};
|
|
8288
8463
|
}
|
|
8289
8464
|
function ensureOperationsDir(root) {
|
|
8290
8465
|
const paths = kitPaths(root);
|
|
8291
|
-
const ignoreFile =
|
|
8466
|
+
const ignoreFile = join36(paths.stateDir, ".gitignore");
|
|
8292
8467
|
mkdirSync9(paths.stateDir, { recursive: true });
|
|
8293
|
-
if (!
|
|
8294
|
-
|
|
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(
|
|
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 (!
|
|
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(
|
|
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(
|
|
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 (!
|
|
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) =>
|
|
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 (!
|
|
8852
|
+
if (!existsSync36(file)) {
|
|
8678
8853
|
return null;
|
|
8679
8854
|
}
|
|
8680
8855
|
try {
|
|
8681
|
-
const parsed = JSON.parse(
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
8907
|
+
if (!existsSync37(paths.lockOwnerFile)) {
|
|
8733
8908
|
return null;
|
|
8734
8909
|
}
|
|
8735
8910
|
try {
|
|
8736
|
-
const parsed = JSON.parse(
|
|
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
|
-
|
|
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
|
|
8820
|
-
import { dirname as dirname10, join as
|
|
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
|
|
8997
|
+
return join38(kitPaths(root).operationsDir, "context.json");
|
|
8823
8998
|
}
|
|
8824
8999
|
function readOperationContext(root) {
|
|
8825
9000
|
const file = contextFile(root);
|
|
8826
|
-
if (!
|
|
9001
|
+
if (!existsSync38(file)) {
|
|
8827
9002
|
return null;
|
|
8828
9003
|
}
|
|
8829
9004
|
try {
|
|
8830
|
-
const parsed = JSON.parse(
|
|
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
|
-
|
|
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
|
|
9476
|
-
import { join as
|
|
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 =
|
|
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(
|
|
9972
|
+
sha256Digest(readFileSync21(absolute))
|
|
9798
9973
|
);
|
|
9799
9974
|
}
|
|
9800
9975
|
};
|
|
9801
|
-
if (
|
|
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
|
|
9859
|
-
import { dirname as dirname11, join as
|
|
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 =
|
|
10037
|
+
const file = join40(projectDir2, AGENT_PACK_RECORD_PATH);
|
|
9863
10038
|
mkdirSync14(dirname11(file), { recursive: true });
|
|
9864
|
-
|
|
10039
|
+
writeFileSync27(file, `${JSON.stringify(record, null, 4)}
|
|
9865
10040
|
`, "utf8");
|
|
9866
10041
|
}
|
|
9867
10042
|
function readAgentPackRecord(projectDir2) {
|
|
9868
|
-
const file =
|
|
9869
|
-
if (!
|
|
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(
|
|
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
|
|
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:
|
|
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 (!
|
|
10134
|
+
if (!existsSync41(file)) {
|
|
9960
10135
|
return null;
|
|
9961
10136
|
}
|
|
9962
10137
|
try {
|
|
9963
|
-
return JSON.parse(
|
|
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
|
-
|
|
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
|
|
9978
|
-
import { join as
|
|
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 =
|
|
9981
|
-
return
|
|
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
|
|
10013
|
-
import { dirname as dirname14, join as
|
|
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
|
|
10017
|
-
import { dirname as dirname13, join as
|
|
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 =
|
|
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 (
|
|
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
|
-
|
|
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(
|
|
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
|
|
10247
|
-
import { join as
|
|
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 =
|
|
10251
|
-
if (!
|
|
10425
|
+
const file = join43(projectDir2, "package.json");
|
|
10426
|
+
if (!existsSync44(file)) {
|
|
10252
10427
|
return [];
|
|
10253
10428
|
}
|
|
10254
|
-
const raw =
|
|
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
|
-
|
|
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 =
|
|
10302
|
-
if (!
|
|
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(
|
|
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 (!
|
|
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(
|
|
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 (
|
|
10430
|
-
const actual = sha256Digest(
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
10925
|
+
if (!existsSync46(join45(projectDir2, "node_modules"))) {
|
|
10751
10926
|
return { ok: false, reason: "dependencies-missing" };
|
|
10752
10927
|
}
|
|
10753
|
-
if (!
|
|
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
|
-
|
|
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
|
|
11161
|
-
import { dirname as dirname15, join as
|
|
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
|
|
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
|
-
|
|
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 (!
|
|
11391
|
+
if (!existsSync47(path5)) {
|
|
11217
11392
|
return null;
|
|
11218
11393
|
}
|
|
11219
11394
|
try {
|
|
11220
|
-
const parsed = JSON.parse(
|
|
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
|
|
11623
|
-
import { join as
|
|
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 (!
|
|
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
|
|
12149
|
-
import { dirname as dirname16, join as
|
|
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 =
|
|
12496
|
-
if (!
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
12691
|
+
const file = join48(targetDir, SCAFFOLD_RECORD_PATH);
|
|
12517
12692
|
mkdirSync19(dirname16(file), { recursive: true });
|
|
12518
|
-
|
|
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
|
|
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
|
|
13048
|
-
import { dirname as dirname17, join as
|
|
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 =
|
|
13134
|
-
if (
|
|
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
|
|
13148
|
-
import { join as
|
|
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 =
|
|
13200
|
-
if (!
|
|
13374
|
+
const file = join50(cwd, "package.json");
|
|
13375
|
+
if (!existsSync51(file)) {
|
|
13201
13376
|
return {};
|
|
13202
13377
|
}
|
|
13203
13378
|
try {
|
|
13204
|
-
const parsed = JSON.parse(
|
|
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
|
|
13217
|
-
import { join as
|
|
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 (
|
|
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(
|
|
13520
|
+
const resolve4 = createRequire2(join52(projectDir2, "package.json"));
|
|
13346
13521
|
return import(pathToFileURL4(resolve4.resolve(specifier)).href);
|
|
13347
13522
|
};
|
|
13348
13523
|
}
|