@open-mercato/cli 0.6.4-develop.4382.1.6b4f656b77 → 0.6.4
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/.turbo/turbo-build.log +1 -1
- package/dist/agentic/guides/core.customers.md +1 -1
- package/dist/agentic/shared/AGENTS.md.template +56 -33
- package/dist/agentic/shared/ai/skills/{auto-continue-pr → om-auto-continue-pr}/SKILL.md +19 -19
- package/dist/agentic/shared/ai/skills/{auto-create-pr → om-auto-continue-pr}/STANDALONE.md +1 -1
- package/dist/agentic/shared/ai/skills/{auto-continue-pr-loop → om-auto-continue-pr-loop}/SKILL.md +32 -32
- package/dist/agentic/shared/ai/skills/{auto-create-pr → om-auto-create-pr}/SKILL.md +24 -24
- package/dist/agentic/shared/ai/skills/{auto-fix-github → om-auto-create-pr}/STANDALONE.md +1 -1
- package/dist/agentic/shared/ai/skills/{auto-create-pr-loop → om-auto-create-pr-loop}/SKILL.md +40 -40
- package/dist/agentic/shared/ai/skills/{auto-fix-github → om-auto-fix-github}/SKILL.md +3 -3
- package/dist/agentic/shared/ai/skills/{auto-continue-pr → om-auto-fix-github}/STANDALONE.md +1 -1
- package/dist/agentic/shared/ai/skills/{auto-review-pr → om-auto-review-pr}/SKILL.md +5 -5
- package/dist/agentic/shared/ai/skills/{auto-review-pr → om-auto-review-pr}/STANDALONE.md +1 -1
- package/dist/agentic/shared/ai/skills/{auto-upgrade-0.4.10-to-0.5.0 → om-auto-upgrade-0.4.10-to-0.5.0}/SKILL.md +1 -1
- package/dist/agentic/shared/ai/skills/{backend-ui-design → om-backend-ui-design}/SKILL.md +2 -2
- package/dist/agentic/shared/ai/skills/{code-review → om-code-review}/SKILL.md +1 -1
- package/dist/agentic/shared/ai/skills/{code-review → om-code-review}/references/review-checklist.md +3 -0
- package/dist/agentic/shared/ai/skills/{data-model-design → om-data-model-design}/SKILL.md +4 -2
- package/dist/agentic/shared/ai/skills/{eject-and-customize → om-eject-and-customize}/SKILL.md +2 -2
- package/dist/agentic/shared/ai/skills/{implement-spec → om-implement-spec}/SKILL.md +6 -6
- package/dist/agentic/shared/ai/skills/{integration-builder → om-integration-builder}/SKILL.md +1 -1
- package/dist/agentic/shared/ai/skills/{integration-tests → om-integration-tests}/SKILL.md +1 -1
- package/dist/agentic/shared/ai/skills/{module-scaffold → om-module-scaffold}/SKILL.md +5 -3
- package/dist/agentic/shared/ai/skills/{module-scaffold → om-module-scaffold}/references/naming-conventions.md +7 -8
- package/dist/agentic/shared/ai/skills/om-prepare-issue/SKILL.md +202 -0
- package/dist/agentic/shared/ai/skills/{spec-writing → om-spec-writing}/SKILL.md +6 -6
- package/dist/agentic/shared/ai/skills/{spec-writing → om-spec-writing}/references/spec-checklist.md +3 -3
- package/dist/agentic/shared/ai/skills/{system-extension → om-system-extension}/SKILL.md +1 -1
- package/dist/agentic/shared/ai/skills/{trim-unused-modules → om-trim-unused-modules}/SKILL.md +1 -1
- package/dist/agentic/shared/ai/skills/{troubleshooter → om-troubleshooter}/SKILL.md +1 -1
- package/dist/bin.js +1 -0
- package/dist/bin.js.map +2 -2
- package/dist/lib/__integration__/TC-INT-008.spec.js +1 -1
- package/dist/lib/__integration__/TC-INT-008.spec.js.map +2 -2
- package/dist/lib/agentic-setup.js +52 -51
- package/dist/lib/agentic-setup.js.map +2 -2
- package/dist/lib/deploy/railway/__integration__/TC-CLI-RAILWAY-001.meta.js +7 -0
- package/dist/lib/deploy/railway/__integration__/TC-CLI-RAILWAY-001.meta.js.map +7 -0
- package/dist/lib/deploy/railway/__integration__/TC-CLI-RAILWAY-001.spec.js +63 -0
- package/dist/lib/deploy/railway/__integration__/TC-CLI-RAILWAY-001.spec.js.map +7 -0
- package/dist/lib/deploy/railway/env.js +98 -0
- package/dist/lib/deploy/railway/env.js.map +7 -0
- package/dist/lib/deploy/railway/graphql-client.js +99 -0
- package/dist/lib/deploy/railway/graphql-client.js.map +7 -0
- package/dist/lib/deploy/railway/index.js +1115 -0
- package/dist/lib/deploy/railway/index.js.map +7 -0
- package/dist/lib/deploy/railway/operations.js +249 -0
- package/dist/lib/deploy/railway/operations.js.map +7 -0
- package/dist/lib/deploy/railway/options.js +155 -0
- package/dist/lib/deploy/railway/options.js.map +7 -0
- package/dist/lib/deploy/railway/railway-cli.js +61 -0
- package/dist/lib/deploy/railway/railway-cli.js.map +7 -0
- package/dist/lib/deploy/railway/redaction.js +88 -0
- package/dist/lib/deploy/railway/redaction.js.map +7 -0
- package/dist/lib/deploy/railway/source.js +128 -0
- package/dist/lib/deploy/railway/source.js.map +7 -0
- package/dist/lib/deploy/railway/state.js +41 -0
- package/dist/lib/deploy/railway/state.js.map +7 -0
- package/dist/lib/deploy/railway/token.js +46 -0
- package/dist/lib/deploy/railway/token.js.map +7 -0
- package/dist/lib/deploy/railway/types.js +1 -0
- package/dist/lib/deploy/railway/types.js.map +7 -0
- package/dist/lib/generators/openapi.js +4 -0
- package/dist/lib/generators/openapi.js.map +2 -2
- package/dist/lib/testing/integration.js +57 -26
- package/dist/lib/testing/integration.js.map +2 -2
- package/dist/lib/testing/runtime-utils.js +16 -8
- package/dist/lib/testing/runtime-utils.js.map +2 -2
- package/dist/mercato.js +23 -8
- package/dist/mercato.js.map +2 -2
- package/package.json +11 -12
- package/src/bin.ts +1 -0
- package/src/lib/__integration__/TC-INT-008.spec.ts +1 -1
- package/src/lib/agentic-setup.ts +52 -51
- package/src/lib/deploy/railway/__integration__/TC-CLI-RAILWAY-001.meta.ts +3 -0
- package/src/lib/deploy/railway/__integration__/TC-CLI-RAILWAY-001.spec.ts +71 -0
- package/src/lib/deploy/railway/__tests__/dry-run.test.ts +61 -0
- package/src/lib/deploy/railway/__tests__/env.test.ts +55 -0
- package/src/lib/deploy/railway/__tests__/graphql-client.test.ts +80 -0
- package/src/lib/deploy/railway/__tests__/operations.test.ts +21 -0
- package/src/lib/deploy/railway/__tests__/options.test.ts +78 -0
- package/src/lib/deploy/railway/__tests__/redaction.test.ts +62 -0
- package/src/lib/deploy/railway/__tests__/source.test.ts +130 -0
- package/src/lib/deploy/railway/__tests__/state.test.ts +24 -0
- package/src/lib/deploy/railway/__tests__/token.test.ts +39 -0
- package/src/lib/deploy/railway/env.ts +112 -0
- package/src/lib/deploy/railway/graphql-client.ts +128 -0
- package/src/lib/deploy/railway/index.ts +1454 -0
- package/src/lib/deploy/railway/operations.ts +248 -0
- package/src/lib/deploy/railway/options.ts +167 -0
- package/src/lib/deploy/railway/railway-cli.ts +65 -0
- package/src/lib/deploy/railway/redaction.ts +91 -0
- package/src/lib/deploy/railway/source.ts +143 -0
- package/src/lib/deploy/railway/state.ts +45 -0
- package/src/lib/deploy/railway/token.ts +49 -0
- package/src/lib/deploy/railway/types.ts +87 -0
- package/src/lib/generators/openapi.ts +3 -0
- package/src/lib/testing/__tests__/integration.test.ts +79 -0
- package/src/lib/testing/__tests__/runtime-utils.test.ts +45 -1
- package/src/lib/testing/integration.ts +74 -28
- package/src/lib/testing/runtime-utils.ts +17 -8
- package/src/mercato.ts +33 -8
- /package/dist/agentic/shared/ai/skills/{backend-ui-design → om-backend-ui-design}/references/ui-components.md +0 -0
- /package/dist/agentic/shared/ai/skills/{data-model-design → om-data-model-design}/references/mikro-orm-cheatsheet.md +0 -0
- /package/dist/agentic/shared/ai/skills/{integration-builder → om-integration-builder}/references/adapter-contracts.md +0 -0
- /package/dist/agentic/shared/ai/skills/{module-scaffold → om-module-scaffold}/references/navigation-patterns.md +0 -0
- /package/dist/agentic/shared/ai/skills/{spec-writing → om-spec-writing}/references/spec-template.md +0 -0
- /package/dist/agentic/shared/ai/skills/{system-extension → om-system-extension}/references/extension-contracts.md +0 -0
- /package/dist/agentic/shared/ai/skills/{troubleshooter → om-troubleshooter}/references/diagnostic-commands.md +0 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { redactText } from "./redaction.js";
|
|
3
|
+
function runRailway(args, cwd, token, secrets) {
|
|
4
|
+
const result = spawnSync("railway", args, {
|
|
5
|
+
cwd,
|
|
6
|
+
encoding: "utf8",
|
|
7
|
+
env: { ...process.env, RAILWAY_API_TOKEN: token },
|
|
8
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
9
|
+
});
|
|
10
|
+
const output = `${result.stdout ?? ""}
|
|
11
|
+
${result.stderr ?? ""}`.trim();
|
|
12
|
+
if (result.status !== 0) {
|
|
13
|
+
throw new Error(redactText(output || `railway ${args[0] ?? ""} failed`, secrets));
|
|
14
|
+
}
|
|
15
|
+
return output;
|
|
16
|
+
}
|
|
17
|
+
function uploadLocalSource(input) {
|
|
18
|
+
runRailway(
|
|
19
|
+
[
|
|
20
|
+
"link",
|
|
21
|
+
"--project",
|
|
22
|
+
input.projectId,
|
|
23
|
+
"--environment",
|
|
24
|
+
input.environmentId,
|
|
25
|
+
"--service",
|
|
26
|
+
input.serviceId
|
|
27
|
+
],
|
|
28
|
+
input.cwd,
|
|
29
|
+
input.token,
|
|
30
|
+
[input.token]
|
|
31
|
+
);
|
|
32
|
+
const output = runRailway(
|
|
33
|
+
[
|
|
34
|
+
"up",
|
|
35
|
+
"--environment",
|
|
36
|
+
input.environmentId,
|
|
37
|
+
"--service",
|
|
38
|
+
input.serviceName,
|
|
39
|
+
"--detach",
|
|
40
|
+
"--json"
|
|
41
|
+
],
|
|
42
|
+
input.cwd,
|
|
43
|
+
input.token,
|
|
44
|
+
[input.token]
|
|
45
|
+
);
|
|
46
|
+
try {
|
|
47
|
+
const parsed = JSON.parse(output);
|
|
48
|
+
if (parsed && typeof parsed === "object") {
|
|
49
|
+
const record = parsed;
|
|
50
|
+
const deploymentId = record.deploymentId ?? record.deployment_id ?? record.id;
|
|
51
|
+
return typeof deploymentId === "string" ? deploymentId : null;
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
export {
|
|
59
|
+
uploadLocalSource
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=railway-cli.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/lib/deploy/railway/railway-cli.ts"],
|
|
4
|
+
"sourcesContent": ["import { spawnSync } from 'node:child_process'\nimport { redactText } from './redaction'\n\nfunction runRailway(args: string[], cwd: string, token: string, secrets: string[]): string {\n const result = spawnSync('railway', args, {\n cwd,\n encoding: 'utf8',\n env: { ...process.env, RAILWAY_API_TOKEN: token },\n stdio: ['ignore', 'pipe', 'pipe'],\n })\n const output = `${result.stdout ?? ''}\\n${result.stderr ?? ''}`.trim()\n if (result.status !== 0) {\n throw new Error(redactText(output || `railway ${args[0] ?? ''} failed`, secrets))\n }\n return output\n}\n\nexport function uploadLocalSource(input: {\n cwd: string\n projectId: string\n environmentId: string\n serviceId: string\n serviceName: string\n token: string\n}): string | null {\n runRailway(\n [\n 'link',\n '--project',\n input.projectId,\n '--environment',\n input.environmentId,\n '--service',\n input.serviceId,\n ],\n input.cwd,\n input.token,\n [input.token],\n )\n const output = runRailway(\n [\n 'up',\n '--environment',\n input.environmentId,\n '--service',\n input.serviceName,\n '--detach',\n '--json',\n ],\n input.cwd,\n input.token,\n [input.token],\n )\n try {\n const parsed: unknown = JSON.parse(output)\n if (parsed && typeof parsed === 'object') {\n const record = parsed as Record<string, unknown>\n const deploymentId = record.deploymentId ?? record.deployment_id ?? record.id\n return typeof deploymentId === 'string' ? deploymentId : null\n }\n } catch {\n return null\n }\n return null\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAE3B,SAAS,WAAW,MAAgB,KAAa,OAAe,SAA2B;AACzF,QAAM,SAAS,UAAU,WAAW,MAAM;AAAA,IACxC;AAAA,IACA,UAAU;AAAA,IACV,KAAK,EAAE,GAAG,QAAQ,KAAK,mBAAmB,MAAM;AAAA,IAChD,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA,EAClC,CAAC;AACD,QAAM,SAAS,GAAG,OAAO,UAAU,EAAE;AAAA,EAAK,OAAO,UAAU,EAAE,GAAG,KAAK;AACrE,MAAI,OAAO,WAAW,GAAG;AACvB,UAAM,IAAI,MAAM,WAAW,UAAU,WAAW,KAAK,CAAC,KAAK,EAAE,WAAW,OAAO,CAAC;AAAA,EAClF;AACA,SAAO;AACT;AAEO,SAAS,kBAAkB,OAOhB;AAChB;AAAA,IACE;AAAA,MACE;AAAA,MACA;AAAA,MACA,MAAM;AAAA,MACN;AAAA,MACA,MAAM;AAAA,MACN;AAAA,MACA,MAAM;AAAA,IACR;AAAA,IACA,MAAM;AAAA,IACN,MAAM;AAAA,IACN,CAAC,MAAM,KAAK;AAAA,EACd;AACA,QAAM,SAAS;AAAA,IACb;AAAA,MACE;AAAA,MACA;AAAA,MACA,MAAM;AAAA,MACN;AAAA,MACA,MAAM;AAAA,MACN;AAAA,MACA;AAAA,IACF;AAAA,IACA,MAAM;AAAA,IACN,MAAM;AAAA,IACN,CAAC,MAAM,KAAK;AAAA,EACd;AACA,MAAI;AACF,UAAM,SAAkB,KAAK,MAAM,MAAM;AACzC,QAAI,UAAU,OAAO,WAAW,UAAU;AACxC,YAAM,SAAS;AACf,YAAM,eAAe,OAAO,gBAAgB,OAAO,iBAAiB,OAAO;AAC3E,aAAO,OAAO,iBAAiB,WAAW,eAAe;AAAA,IAC3D;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
const SENSITIVE_KEY_PATTERN = /(secret|token|password|private|credential|encryption|fallback|api[_-]?key|database_url|redis_url)/i;
|
|
3
|
+
const CREDENTIAL_PATTERNS = [
|
|
4
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----/,
|
|
5
|
+
/\bsk-[A-Za-z0-9_-]{12,}\b/,
|
|
6
|
+
/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/,
|
|
7
|
+
/\bgh[pousr]_[A-Za-z0-9]{20,}\b/,
|
|
8
|
+
/\bAKIA[0-9A-Z]{16}\b/
|
|
9
|
+
];
|
|
10
|
+
function fingerprintSecret(value) {
|
|
11
|
+
return `sha256:${createHash("sha256").update(value).digest("hex").slice(0, 8)}`;
|
|
12
|
+
}
|
|
13
|
+
function isSensitiveKey(key) {
|
|
14
|
+
return SENSITIVE_KEY_PATTERN.test(key);
|
|
15
|
+
}
|
|
16
|
+
function detectCredentialValue(value, railwayToken) {
|
|
17
|
+
if (railwayToken && value.includes(railwayToken)) return true;
|
|
18
|
+
return CREDENTIAL_PATTERNS.some((pattern) => pattern.test(value));
|
|
19
|
+
}
|
|
20
|
+
function assertSafeVariables(variables, options = {}) {
|
|
21
|
+
const allowedKeys = new Set(options.allowedKeys ?? []);
|
|
22
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
23
|
+
if (!allowedKeys.has(key) && detectCredentialValue(value, options.railwayToken)) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
`Refusing to upload credential-like value from ${key}. Pass --allow-secret-passthrough ${key} to allow this exact key.`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function formatVariableValue(key, value, railwayToken) {
|
|
31
|
+
if (isSensitiveKey(key) || detectCredentialValue(value, railwayToken)) {
|
|
32
|
+
return `<redacted> (${fingerprintSecret(value)})`;
|
|
33
|
+
}
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
function redactText(value, secrets = []) {
|
|
37
|
+
let redacted = value;
|
|
38
|
+
for (const secret of secrets.filter(Boolean).sort((left, right) => right.length - left.length)) {
|
|
39
|
+
redacted = redacted.split(secret).join("****");
|
|
40
|
+
}
|
|
41
|
+
for (const pattern of CREDENTIAL_PATTERNS) {
|
|
42
|
+
redacted = redacted.replace(new RegExp(pattern.source, pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`), "****");
|
|
43
|
+
}
|
|
44
|
+
return redacted.replace(/Bearer\s+[A-Za-z0-9._~+/-]+/gi, "Bearer ****");
|
|
45
|
+
}
|
|
46
|
+
function redactStructuredValue(value, secrets = []) {
|
|
47
|
+
if (typeof value === "string") return redactText(value, secrets);
|
|
48
|
+
if (Array.isArray(value)) {
|
|
49
|
+
return value.map((item) => redactStructuredValue(item, secrets));
|
|
50
|
+
}
|
|
51
|
+
if (!value || typeof value !== "object") return value;
|
|
52
|
+
return Object.fromEntries(
|
|
53
|
+
Object.entries(value).map(([key, nestedValue]) => [
|
|
54
|
+
key,
|
|
55
|
+
isSensitiveKey(key) ? "<redacted>" : redactStructuredValue(nestedValue, secrets)
|
|
56
|
+
])
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
function collectSensitiveStructuredValues(value) {
|
|
60
|
+
const collected = /* @__PURE__ */ new Set();
|
|
61
|
+
function visit(current) {
|
|
62
|
+
if (Array.isArray(current)) {
|
|
63
|
+
current.forEach(visit);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (!current || typeof current !== "object") return;
|
|
67
|
+
for (const [key, nestedValue] of Object.entries(current)) {
|
|
68
|
+
if (isSensitiveKey(key) && typeof nestedValue === "string" && nestedValue.length > 0) {
|
|
69
|
+
collected.add(nestedValue);
|
|
70
|
+
} else {
|
|
71
|
+
visit(nestedValue);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
visit(value);
|
|
76
|
+
return Array.from(collected);
|
|
77
|
+
}
|
|
78
|
+
export {
|
|
79
|
+
assertSafeVariables,
|
|
80
|
+
collectSensitiveStructuredValues,
|
|
81
|
+
detectCredentialValue,
|
|
82
|
+
fingerprintSecret,
|
|
83
|
+
formatVariableValue,
|
|
84
|
+
isSensitiveKey,
|
|
85
|
+
redactStructuredValue,
|
|
86
|
+
redactText
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=redaction.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/lib/deploy/railway/redaction.ts"],
|
|
4
|
+
"sourcesContent": ["import { createHash } from 'node:crypto'\n\nconst SENSITIVE_KEY_PATTERN = /(secret|token|password|private|credential|encryption|fallback|api[_-]?key|database_url|redis_url)/i\nconst CREDENTIAL_PATTERNS = [\n /-----BEGIN [A-Z ]*PRIVATE KEY-----/,\n /\\bsk-[A-Za-z0-9_-]{12,}\\b/,\n /\\bxox[baprs]-[A-Za-z0-9-]{10,}\\b/,\n /\\bgh[pousr]_[A-Za-z0-9]{20,}\\b/,\n /\\bAKIA[0-9A-Z]{16}\\b/,\n]\n\nexport function fingerprintSecret(value: string): string {\n return `sha256:${createHash('sha256').update(value).digest('hex').slice(0, 8)}`\n}\n\nexport function isSensitiveKey(key: string): boolean {\n return SENSITIVE_KEY_PATTERN.test(key)\n}\n\nexport function detectCredentialValue(value: string, railwayToken?: string): boolean {\n if (railwayToken && value.includes(railwayToken)) return true\n return CREDENTIAL_PATTERNS.some((pattern) => pattern.test(value))\n}\n\nexport function assertSafeVariables(\n variables: Record<string, string>,\n options: { railwayToken?: string; allowedKeys?: string[] } = {},\n): void {\n const allowedKeys = new Set(options.allowedKeys ?? [])\n for (const [key, value] of Object.entries(variables)) {\n if (!allowedKeys.has(key) && detectCredentialValue(value, options.railwayToken)) {\n throw new Error(\n `Refusing to upload credential-like value from ${key}. Pass --allow-secret-passthrough ${key} to allow this exact key.`,\n )\n }\n }\n}\n\nexport function formatVariableValue(key: string, value: string, railwayToken?: string): string {\n if (isSensitiveKey(key) || detectCredentialValue(value, railwayToken)) {\n return `<redacted> (${fingerprintSecret(value)})`\n }\n return value\n}\n\nexport function redactText(value: string, secrets: string[] = []): string {\n let redacted = value\n for (const secret of secrets.filter(Boolean).sort((left, right) => right.length - left.length)) {\n redacted = redacted.split(secret).join('****')\n }\n for (const pattern of CREDENTIAL_PATTERNS) {\n redacted = redacted.replace(new RegExp(pattern.source, pattern.flags.includes('g') ? pattern.flags : `${pattern.flags}g`), '****')\n }\n return redacted.replace(/Bearer\\s+[A-Za-z0-9._~+/-]+/gi, 'Bearer ****')\n}\n\nexport function redactStructuredValue(value: unknown, secrets: string[] = []): unknown {\n if (typeof value === 'string') return redactText(value, secrets)\n if (Array.isArray(value)) {\n return value.map((item) => redactStructuredValue(item, secrets))\n }\n if (!value || typeof value !== 'object') return value\n return Object.fromEntries(\n Object.entries(value as Record<string, unknown>).map(([key, nestedValue]) => [\n key,\n isSensitiveKey(key) ? '<redacted>' : redactStructuredValue(nestedValue, secrets),\n ]),\n )\n}\n\nexport function collectSensitiveStructuredValues(value: unknown): string[] {\n const collected = new Set<string>()\n\n function visit(current: unknown): void {\n if (Array.isArray(current)) {\n current.forEach(visit)\n return\n }\n if (!current || typeof current !== 'object') return\n for (const [key, nestedValue] of Object.entries(current as Record<string, unknown>)) {\n if (isSensitiveKey(key) && typeof nestedValue === 'string' && nestedValue.length > 0) {\n collected.add(nestedValue)\n } else {\n visit(nestedValue)\n }\n }\n }\n\n visit(value)\n return Array.from(collected)\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,kBAAkB;AAE3B,MAAM,wBAAwB;AAC9B,MAAM,sBAAsB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,SAAS,kBAAkB,OAAuB;AACvD,SAAO,UAAU,WAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC;AAC/E;AAEO,SAAS,eAAe,KAAsB;AACnD,SAAO,sBAAsB,KAAK,GAAG;AACvC;AAEO,SAAS,sBAAsB,OAAe,cAAgC;AACnF,MAAI,gBAAgB,MAAM,SAAS,YAAY,EAAG,QAAO;AACzD,SAAO,oBAAoB,KAAK,CAAC,YAAY,QAAQ,KAAK,KAAK,CAAC;AAClE;AAEO,SAAS,oBACd,WACA,UAA6D,CAAC,GACxD;AACN,QAAM,cAAc,IAAI,IAAI,QAAQ,eAAe,CAAC,CAAC;AACrD,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,SAAS,GAAG;AACpD,QAAI,CAAC,YAAY,IAAI,GAAG,KAAK,sBAAsB,OAAO,QAAQ,YAAY,GAAG;AAC/E,YAAM,IAAI;AAAA,QACR,iDAAiD,GAAG,qCAAqC,GAAG;AAAA,MAC9F;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,oBAAoB,KAAa,OAAe,cAA+B;AAC7F,MAAI,eAAe,GAAG,KAAK,sBAAsB,OAAO,YAAY,GAAG;AACrE,WAAO,eAAe,kBAAkB,KAAK,CAAC;AAAA,EAChD;AACA,SAAO;AACT;AAEO,SAAS,WAAW,OAAe,UAAoB,CAAC,GAAW;AACxE,MAAI,WAAW;AACf,aAAW,UAAU,QAAQ,OAAO,OAAO,EAAE,KAAK,CAAC,MAAM,UAAU,MAAM,SAAS,KAAK,MAAM,GAAG;AAC9F,eAAW,SAAS,MAAM,MAAM,EAAE,KAAK,MAAM;AAAA,EAC/C;AACA,aAAW,WAAW,qBAAqB;AACzC,eAAW,SAAS,QAAQ,IAAI,OAAO,QAAQ,QAAQ,QAAQ,MAAM,SAAS,GAAG,IAAI,QAAQ,QAAQ,GAAG,QAAQ,KAAK,GAAG,GAAG,MAAM;AAAA,EACnI;AACA,SAAO,SAAS,QAAQ,iCAAiC,aAAa;AACxE;AAEO,SAAS,sBAAsB,OAAgB,UAAoB,CAAC,GAAY;AACrF,MAAI,OAAO,UAAU,SAAU,QAAO,WAAW,OAAO,OAAO;AAC/D,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,IAAI,CAAC,SAAS,sBAAsB,MAAM,OAAO,CAAC;AAAA,EACjE;AACA,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,SAAO,OAAO;AAAA,IACZ,OAAO,QAAQ,KAAgC,EAAE,IAAI,CAAC,CAAC,KAAK,WAAW,MAAM;AAAA,MAC3E;AAAA,MACA,eAAe,GAAG,IAAI,eAAe,sBAAsB,aAAa,OAAO;AAAA,IACjF,CAAC;AAAA,EACH;AACF;AAEO,SAAS,iCAAiC,OAA0B;AACzE,QAAM,YAAY,oBAAI,IAAY;AAElC,WAAS,MAAM,SAAwB;AACrC,QAAI,MAAM,QAAQ,OAAO,GAAG;AAC1B,cAAQ,QAAQ,KAAK;AACrB;AAAA,IACF;AACA,QAAI,CAAC,WAAW,OAAO,YAAY,SAAU;AAC7C,eAAW,CAAC,KAAK,WAAW,KAAK,OAAO,QAAQ,OAAkC,GAAG;AACnF,UAAI,eAAe,GAAG,KAAK,OAAO,gBAAgB,YAAY,YAAY,SAAS,GAAG;AACpF,kBAAU,IAAI,WAAW;AAAA,MAC3B,OAAO;AACL,cAAM,WAAW;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,KAAK;AACX,SAAO,MAAM,KAAK,SAAS;AAC7B;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
const defaultRunner = (command, args, cwd) => execFileSync(command, args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
5
|
+
function tryRun(runner, command, args, cwd) {
|
|
6
|
+
try {
|
|
7
|
+
return runner(command, args, cwd);
|
|
8
|
+
} catch {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function normalizeGitRepository(remoteUrl) {
|
|
13
|
+
const trimmed = remoteUrl.trim().replace(/\.git$/, "");
|
|
14
|
+
const sshMatch = trimmed.match(/^git@github\.com:([^/]+\/[^/]+)$/);
|
|
15
|
+
if (sshMatch) return sshMatch[1] ?? null;
|
|
16
|
+
try {
|
|
17
|
+
const parsed = new URL(trimmed);
|
|
18
|
+
if (parsed.hostname !== "github.com") return null;
|
|
19
|
+
return parsed.pathname.replace(/^\/+/, "") || null;
|
|
20
|
+
} catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function inspectGitSource(cwd, runner = defaultRunner) {
|
|
25
|
+
const branch = tryRun(runner, "git", ["branch", "--show-current"], cwd);
|
|
26
|
+
const remoteUrl = tryRun(runner, "git", ["remote", "get-url", "origin"], cwd);
|
|
27
|
+
if (!branch || !remoteUrl) return null;
|
|
28
|
+
const repo = normalizeGitRepository(remoteUrl);
|
|
29
|
+
if (!repo) return null;
|
|
30
|
+
const commitSha = tryRun(runner, "git", ["rev-parse", "HEAD"], cwd) ?? void 0;
|
|
31
|
+
return {
|
|
32
|
+
mode: "git",
|
|
33
|
+
reason: `GitHub remote origin and branch ${branch} detected`,
|
|
34
|
+
repo,
|
|
35
|
+
branch,
|
|
36
|
+
commitSha
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function assertGitDeployReady(cwd, runner = defaultRunner) {
|
|
40
|
+
const status = tryRun(runner, "git", ["status", "--porcelain"], cwd);
|
|
41
|
+
if (status === null) throw new Error("Git is required for --source git.");
|
|
42
|
+
if (status.length > 0) throw new Error("Git-backed deploy requires a clean working tree.");
|
|
43
|
+
const upstream = tryRun(runner, "git", ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"], cwd);
|
|
44
|
+
if (!upstream) throw new Error("The current branch has no upstream. Push it before a Git-backed deploy.");
|
|
45
|
+
const ahead = tryRun(runner, "git", ["rev-list", "--count", "@{u}..HEAD"], cwd);
|
|
46
|
+
if (ahead === null || Number.parseInt(ahead, 10) > 0) {
|
|
47
|
+
throw new Error("The current branch has local commits that are not pushed to its upstream.");
|
|
48
|
+
}
|
|
49
|
+
const behind = tryRun(runner, "git", ["rev-list", "--count", "HEAD..@{u}"], cwd);
|
|
50
|
+
if (behind === null || Number.parseInt(behind, 10) > 0) {
|
|
51
|
+
throw new Error("The current branch is behind its upstream. Pull the latest commits before a Git-backed deploy.");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function isRailwayCliAvailable(cwd, runner = defaultRunner) {
|
|
55
|
+
return tryRun(runner, "railway", ["--version"], cwd) !== null;
|
|
56
|
+
}
|
|
57
|
+
function resolveRailwaySource(mode, cwd, runner = defaultRunner) {
|
|
58
|
+
const gitSource = inspectGitSource(cwd, runner);
|
|
59
|
+
if (mode === "git") {
|
|
60
|
+
if (!gitSource) {
|
|
61
|
+
throw new Error("--source git requires a GitHub origin remote and a current branch.");
|
|
62
|
+
}
|
|
63
|
+
assertGitDeployReady(cwd, runner);
|
|
64
|
+
return gitSource;
|
|
65
|
+
}
|
|
66
|
+
if (mode === "local") {
|
|
67
|
+
if (!isRailwayCliAvailable(cwd, runner)) {
|
|
68
|
+
throw new Error("--source local requires the Railway CLI. Install it from https://docs.railway.com/reference/cli-api.");
|
|
69
|
+
}
|
|
70
|
+
return { mode: "local", reason: "Local upload explicitly requested" };
|
|
71
|
+
}
|
|
72
|
+
if (gitSource) {
|
|
73
|
+
try {
|
|
74
|
+
assertGitDeployReady(cwd, runner);
|
|
75
|
+
return gitSource;
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (!isRailwayCliAvailable(cwd, runner)) throw error;
|
|
78
|
+
const reason = error instanceof Error ? error.message : "Git source is not deployable";
|
|
79
|
+
return {
|
|
80
|
+
mode: "local",
|
|
81
|
+
reason: `${reason} Falling back to railway up.`
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (!isRailwayCliAvailable(cwd, runner)) {
|
|
86
|
+
throw new Error("No usable GitHub remote found and the Railway CLI is unavailable for local upload.");
|
|
87
|
+
}
|
|
88
|
+
return { mode: "local", reason: "No usable GitHub remote found; falling back to railway up" };
|
|
89
|
+
}
|
|
90
|
+
const REQUIRED_IGNORE_ENTRIES = [
|
|
91
|
+
{ label: ".env", matches: (line) => line === ".env" || line === ".env*" },
|
|
92
|
+
{ label: ".env.*", matches: (line) => line === ".env.*" || line === ".env*" },
|
|
93
|
+
{ label: "*.pem", matches: (line) => line === "*.pem" },
|
|
94
|
+
{ label: "*.key", matches: (line) => line === "*.key" },
|
|
95
|
+
{ label: "id_rsa", matches: (line) => line === "id_*" || line === "id_rsa" },
|
|
96
|
+
{ label: "id_ed25519", matches: (line) => line === "id_*" || line === "id_ed25519" },
|
|
97
|
+
{ label: ".git", matches: (line) => line === ".git" },
|
|
98
|
+
{ label: ".railway", matches: (line) => line === ".railway" },
|
|
99
|
+
{ label: "node_modules", matches: (line) => line === "node_modules" },
|
|
100
|
+
{ label: ".yarn/cache", matches: (line) => line === ".yarn/cache" },
|
|
101
|
+
{ label: ".next", matches: (line) => line === ".next" },
|
|
102
|
+
{ label: ".turbo", matches: (line) => line === ".turbo" },
|
|
103
|
+
{ label: "*.db", matches: (line) => line === "*.db" },
|
|
104
|
+
{ label: "*.sqlite", matches: (line) => line === "*.sqlite" || line === "*.sqlite*" },
|
|
105
|
+
{ label: "*.sqlite3", matches: (line) => line === "*.sqlite3" || line === "*.sqlite*" },
|
|
106
|
+
{ label: ".mercato/railway.json", matches: (line) => line === ".mercato/railway.json" || line === ".mercato/railway.json*" },
|
|
107
|
+
{ label: ".mercato/railway.json.local", matches: (line) => line === ".mercato/railway.json.local" || line === ".mercato/railway.json*" }
|
|
108
|
+
];
|
|
109
|
+
function assertLocalUploadSafe(cwd) {
|
|
110
|
+
const ignorePath = resolve(cwd, ".railwayignore");
|
|
111
|
+
if (!existsSync(ignorePath)) {
|
|
112
|
+
throw new Error("Local Railway upload requires a .railwayignore file.");
|
|
113
|
+
}
|
|
114
|
+
const entries = readFileSync(ignorePath, "utf8").split(/\r?\n/).map((line) => line.trim().replace(/^\/+|\/+$/g, "")).filter((line) => line.length > 0 && !line.startsWith("#") && !line.startsWith("!"));
|
|
115
|
+
const missing = REQUIRED_IGNORE_ENTRIES.filter((requirement) => !entries.some(requirement.matches)).map((requirement) => requirement.label);
|
|
116
|
+
if (missing.length > 0) {
|
|
117
|
+
throw new Error(`.railwayignore is missing required safety entries: ${missing.join(", ")}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
export {
|
|
121
|
+
assertGitDeployReady,
|
|
122
|
+
assertLocalUploadSafe,
|
|
123
|
+
inspectGitSource,
|
|
124
|
+
isRailwayCliAvailable,
|
|
125
|
+
normalizeGitRepository,
|
|
126
|
+
resolveRailwaySource
|
|
127
|
+
};
|
|
128
|
+
//# sourceMappingURL=source.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/lib/deploy/railway/source.ts"],
|
|
4
|
+
"sourcesContent": ["import { execFileSync } from 'node:child_process'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport type { RailwaySource, RailwaySourceMode } from './types'\n\ntype CommandRunner = (command: string, args: string[], cwd: string) => string\n\nconst defaultRunner: CommandRunner = (command, args, cwd) =>\n execFileSync(command, args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim()\n\nfunction tryRun(runner: CommandRunner, command: string, args: string[], cwd: string): string | null {\n try {\n return runner(command, args, cwd)\n } catch {\n return null\n }\n}\n\nexport function normalizeGitRepository(remoteUrl: string): string | null {\n const trimmed = remoteUrl.trim().replace(/\\.git$/, '')\n const sshMatch = trimmed.match(/^git@github\\.com:([^/]+\\/[^/]+)$/)\n if (sshMatch) return sshMatch[1] ?? null\n try {\n const parsed = new URL(trimmed)\n if (parsed.hostname !== 'github.com') return null\n return parsed.pathname.replace(/^\\/+/, '') || null\n } catch {\n return null\n }\n}\n\nexport function inspectGitSource(cwd: string, runner: CommandRunner = defaultRunner): RailwaySource | null {\n const branch = tryRun(runner, 'git', ['branch', '--show-current'], cwd)\n const remoteUrl = tryRun(runner, 'git', ['remote', 'get-url', 'origin'], cwd)\n if (!branch || !remoteUrl) return null\n const repo = normalizeGitRepository(remoteUrl)\n if (!repo) return null\n const commitSha = tryRun(runner, 'git', ['rev-parse', 'HEAD'], cwd) ?? undefined\n return {\n mode: 'git',\n reason: `GitHub remote origin and branch ${branch} detected`,\n repo,\n branch,\n commitSha,\n }\n}\n\nexport function assertGitDeployReady(cwd: string, runner: CommandRunner = defaultRunner): void {\n const status = tryRun(runner, 'git', ['status', '--porcelain'], cwd)\n if (status === null) throw new Error('Git is required for --source git.')\n if (status.length > 0) throw new Error('Git-backed deploy requires a clean working tree.')\n const upstream = tryRun(runner, 'git', ['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}'], cwd)\n if (!upstream) throw new Error('The current branch has no upstream. Push it before a Git-backed deploy.')\n const ahead = tryRun(runner, 'git', ['rev-list', '--count', '@{u}..HEAD'], cwd)\n if (ahead === null || Number.parseInt(ahead, 10) > 0) {\n throw new Error('The current branch has local commits that are not pushed to its upstream.')\n }\n const behind = tryRun(runner, 'git', ['rev-list', '--count', 'HEAD..@{u}'], cwd)\n if (behind === null || Number.parseInt(behind, 10) > 0) {\n throw new Error('The current branch is behind its upstream. Pull the latest commits before a Git-backed deploy.')\n }\n}\n\nexport function isRailwayCliAvailable(cwd: string, runner: CommandRunner = defaultRunner): boolean {\n return tryRun(runner, 'railway', ['--version'], cwd) !== null\n}\n\nexport function resolveRailwaySource(\n mode: RailwaySourceMode,\n cwd: string,\n runner: CommandRunner = defaultRunner,\n): RailwaySource {\n const gitSource = inspectGitSource(cwd, runner)\n if (mode === 'git') {\n if (!gitSource) {\n throw new Error('--source git requires a GitHub origin remote and a current branch.')\n }\n assertGitDeployReady(cwd, runner)\n return gitSource\n }\n if (mode === 'local') {\n if (!isRailwayCliAvailable(cwd, runner)) {\n throw new Error('--source local requires the Railway CLI. Install it from https://docs.railway.com/reference/cli-api.')\n }\n return { mode: 'local', reason: 'Local upload explicitly requested' }\n }\n if (gitSource) {\n try {\n assertGitDeployReady(cwd, runner)\n return gitSource\n } catch (error) {\n if (!isRailwayCliAvailable(cwd, runner)) throw error\n const reason = error instanceof Error ? error.message : 'Git source is not deployable'\n return {\n mode: 'local',\n reason: `${reason} Falling back to railway up.`,\n }\n }\n }\n if (!isRailwayCliAvailable(cwd, runner)) {\n throw new Error('No usable GitHub remote found and the Railway CLI is unavailable for local upload.')\n }\n return { mode: 'local', reason: 'No usable GitHub remote found; falling back to railway up' }\n}\n\nconst REQUIRED_IGNORE_ENTRIES = [\n { label: '.env', matches: (line: string) => line === '.env' || line === '.env*' },\n { label: '.env.*', matches: (line: string) => line === '.env.*' || line === '.env*' },\n { label: '*.pem', matches: (line: string) => line === '*.pem' },\n { label: '*.key', matches: (line: string) => line === '*.key' },\n { label: 'id_rsa', matches: (line: string) => line === 'id_*' || line === 'id_rsa' },\n { label: 'id_ed25519', matches: (line: string) => line === 'id_*' || line === 'id_ed25519' },\n { label: '.git', matches: (line: string) => line === '.git' },\n { label: '.railway', matches: (line: string) => line === '.railway' },\n { label: 'node_modules', matches: (line: string) => line === 'node_modules' },\n { label: '.yarn/cache', matches: (line: string) => line === '.yarn/cache' },\n { label: '.next', matches: (line: string) => line === '.next' },\n { label: '.turbo', matches: (line: string) => line === '.turbo' },\n { label: '*.db', matches: (line: string) => line === '*.db' },\n { label: '*.sqlite', matches: (line: string) => line === '*.sqlite' || line === '*.sqlite*' },\n { label: '*.sqlite3', matches: (line: string) => line === '*.sqlite3' || line === '*.sqlite*' },\n { label: '.mercato/railway.json', matches: (line: string) =>\n line === '.mercato/railway.json' || line === '.mercato/railway.json*' },\n { label: '.mercato/railway.json.local', matches: (line: string) =>\n line === '.mercato/railway.json.local' || line === '.mercato/railway.json*' },\n]\n\nexport function assertLocalUploadSafe(cwd: string): void {\n const ignorePath = resolve(cwd, '.railwayignore')\n if (!existsSync(ignorePath)) {\n throw new Error('Local Railway upload requires a .railwayignore file.')\n }\n const entries = readFileSync(ignorePath, 'utf8')\n .split(/\\r?\\n/)\n .map((line) => line.trim().replace(/^\\/+|\\/+$/g, ''))\n .filter((line) => line.length > 0 && !line.startsWith('#') && !line.startsWith('!'))\n const missing = REQUIRED_IGNORE_ENTRIES\n .filter((requirement) => !entries.some(requirement.matches))\n .map((requirement) => requirement.label)\n if (missing.length > 0) {\n throw new Error(`.railwayignore is missing required safety entries: ${missing.join(', ')}`)\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
function railwayStatePath(cwd, track) {
|
|
4
|
+
return resolve(cwd, ".mercato", track ? "railway.json" : "railway.json.local");
|
|
5
|
+
}
|
|
6
|
+
function isRailwayState(value) {
|
|
7
|
+
if (!value || typeof value !== "object") return false;
|
|
8
|
+
const record = value;
|
|
9
|
+
return record.schemaVersion === 1 && record.provider === "railway" && typeof record.projectName === "string" && typeof record.environments === "object" && record.environments !== null;
|
|
10
|
+
}
|
|
11
|
+
function loadRailwayState(path) {
|
|
12
|
+
if (!existsSync(path)) return null;
|
|
13
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
14
|
+
if (!isRailwayState(parsed)) {
|
|
15
|
+
throw new Error(`Unsupported or invalid Railway state file: ${path}`);
|
|
16
|
+
}
|
|
17
|
+
return parsed;
|
|
18
|
+
}
|
|
19
|
+
function createRailwayState(projectName, cliVersion) {
|
|
20
|
+
return {
|
|
21
|
+
schemaVersion: 1,
|
|
22
|
+
provider: "railway",
|
|
23
|
+
projectName,
|
|
24
|
+
environments: {},
|
|
25
|
+
writtenBy: { cliVersion }
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function saveRailwayState(path, state) {
|
|
29
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
30
|
+
const temporaryPath = `${path}.tmp`;
|
|
31
|
+
writeFileSync(temporaryPath, `${JSON.stringify(state, null, 2)}
|
|
32
|
+
`, { mode: 384 });
|
|
33
|
+
renameSync(temporaryPath, path);
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
createRailwayState,
|
|
37
|
+
loadRailwayState,
|
|
38
|
+
railwayStatePath,
|
|
39
|
+
saveRailwayState
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/lib/deploy/railway/state.ts"],
|
|
4
|
+
"sourcesContent": ["import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport type { RailwayState } from './types'\n\nexport function railwayStatePath(cwd: string, track: boolean): string {\n return resolve(cwd, '.mercato', track ? 'railway.json' : 'railway.json.local')\n}\n\nfunction isRailwayState(value: unknown): value is RailwayState {\n if (!value || typeof value !== 'object') return false\n const record = value as Record<string, unknown>\n return (\n record.schemaVersion === 1 &&\n record.provider === 'railway' &&\n typeof record.projectName === 'string' &&\n typeof record.environments === 'object' &&\n record.environments !== null\n )\n}\n\nexport function loadRailwayState(path: string): RailwayState | null {\n if (!existsSync(path)) return null\n const parsed: unknown = JSON.parse(readFileSync(path, 'utf8'))\n if (!isRailwayState(parsed)) {\n throw new Error(`Unsupported or invalid Railway state file: ${path}`)\n }\n return parsed\n}\n\nexport function createRailwayState(projectName: string, cliVersion: string): RailwayState {\n return {\n schemaVersion: 1,\n provider: 'railway',\n projectName,\n environments: {},\n writtenBy: { cliVersion },\n }\n}\n\nexport function saveRailwayState(path: string, state: RailwayState): void {\n mkdirSync(dirname(path), { recursive: true })\n const temporaryPath = `${path}.tmp`\n writeFileSync(temporaryPath, `${JSON.stringify(state, null, 2)}\\n`, { mode: 0o600 })\n renameSync(temporaryPath, path)\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,YAAY,WAAW,cAAc,YAAY,qBAAqB;AAC/E,SAAS,SAAS,eAAe;AAG1B,SAAS,iBAAiB,KAAa,OAAwB;AACpE,SAAO,QAAQ,KAAK,YAAY,QAAQ,iBAAiB,oBAAoB;AAC/E;AAEA,SAAS,eAAe,OAAuC;AAC7D,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,SAAS;AACf,SACE,OAAO,kBAAkB,KACzB,OAAO,aAAa,aACpB,OAAO,OAAO,gBAAgB,YAC9B,OAAO,OAAO,iBAAiB,YAC/B,OAAO,iBAAiB;AAE5B;AAEO,SAAS,iBAAiB,MAAmC;AAClE,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO;AAC9B,QAAM,SAAkB,KAAK,MAAM,aAAa,MAAM,MAAM,CAAC;AAC7D,MAAI,CAAC,eAAe,MAAM,GAAG;AAC3B,UAAM,IAAI,MAAM,8CAA8C,IAAI,EAAE;AAAA,EACtE;AACA,SAAO;AACT;AAEO,SAAS,mBAAmB,aAAqB,YAAkC;AACxF,SAAO;AAAA,IACL,eAAe;AAAA,IACf,UAAU;AAAA,IACV;AAAA,IACA,cAAc,CAAC;AAAA,IACf,WAAW,EAAE,WAAW;AAAA,EAC1B;AACF;AAEO,SAAS,iBAAiB,MAAc,OAA2B;AACxE,YAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5C,QAAM,gBAAgB,GAAG,IAAI;AAC7B,gBAAc,eAAe,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA,GAAM,EAAE,MAAM,IAAM,CAAC;AACnF,aAAW,eAAe,IAAI;AAChC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
function railwayTokenConfigPath(environment = process.env, platform = process.platform) {
|
|
5
|
+
if (platform === "win32") {
|
|
6
|
+
return join(environment.APPDATA || join(homedir(), "AppData", "Roaming"), "open-mercato", "railway.json");
|
|
7
|
+
}
|
|
8
|
+
return join(environment.XDG_CONFIG_HOME || join(homedir(), ".config"), "open-mercato", "railway.json");
|
|
9
|
+
}
|
|
10
|
+
function readCachedRailwayToken(path, platform = process.platform) {
|
|
11
|
+
if (!existsSync(path)) return null;
|
|
12
|
+
if (platform !== "win32") {
|
|
13
|
+
const mode = statSync(path).mode & 511;
|
|
14
|
+
if ((mode & 63) !== 0) {
|
|
15
|
+
throw new Error(`Refusing to read ${path}: permissions must be 0600.`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
19
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
20
|
+
const token = parsed.token;
|
|
21
|
+
return typeof token === "string" && token.trim() ? token.trim() : null;
|
|
22
|
+
}
|
|
23
|
+
function writeCachedRailwayToken(path, token) {
|
|
24
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
25
|
+
writeFileSync(path, `${JSON.stringify({ token }, null, 2)}
|
|
26
|
+
`, { mode: 384 });
|
|
27
|
+
if (process.platform !== "win32") chmodSync(path, 384);
|
|
28
|
+
}
|
|
29
|
+
function resolveRailwayToken(input) {
|
|
30
|
+
const environment = input.environment ?? process.env;
|
|
31
|
+
if (input.flagToken?.trim()) return { token: input.flagToken.trim(), source: "flag" };
|
|
32
|
+
if (environment.RAILWAY_API_TOKEN?.trim()) {
|
|
33
|
+
return { token: environment.RAILWAY_API_TOKEN.trim(), source: "environment" };
|
|
34
|
+
}
|
|
35
|
+
const configPath = input.configPath ?? railwayTokenConfigPath(environment);
|
|
36
|
+
const cached = readCachedRailwayToken(configPath);
|
|
37
|
+
if (cached) return { token: cached, source: "cache" };
|
|
38
|
+
return { token: null, source: null };
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
railwayTokenConfigPath,
|
|
42
|
+
readCachedRailwayToken,
|
|
43
|
+
resolveRailwayToken,
|
|
44
|
+
writeCachedRailwayToken
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=token.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/lib/deploy/railway/token.ts"],
|
|
4
|
+
"sourcesContent": ["import { chmodSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { dirname, join } from 'node:path'\n\nexport function railwayTokenConfigPath(\n environment: NodeJS.ProcessEnv = process.env,\n platform: NodeJS.Platform = process.platform,\n): string {\n if (platform === 'win32') {\n return join(environment.APPDATA || join(homedir(), 'AppData', 'Roaming'), 'open-mercato', 'railway.json')\n }\n return join(environment.XDG_CONFIG_HOME || join(homedir(), '.config'), 'open-mercato', 'railway.json')\n}\n\nexport function readCachedRailwayToken(path: string, platform: NodeJS.Platform = process.platform): string | null {\n if (!existsSync(path)) return null\n if (platform !== 'win32') {\n const mode = statSync(path).mode & 0o777\n if ((mode & 0o077) !== 0) {\n throw new Error(`Refusing to read ${path}: permissions must be 0600.`)\n }\n }\n const parsed: unknown = JSON.parse(readFileSync(path, 'utf8'))\n if (!parsed || typeof parsed !== 'object') return null\n const token = (parsed as Record<string, unknown>).token\n return typeof token === 'string' && token.trim() ? token.trim() : null\n}\n\nexport function writeCachedRailwayToken(path: string, token: string): void {\n mkdirSync(dirname(path), { recursive: true })\n writeFileSync(path, `${JSON.stringify({ token }, null, 2)}\\n`, { mode: 0o600 })\n if (process.platform !== 'win32') chmodSync(path, 0o600)\n}\n\nexport function resolveRailwayToken(input: {\n flagToken?: string\n environment?: NodeJS.ProcessEnv\n configPath?: string\n}): { token: string | null; source: 'flag' | 'environment' | 'cache' | null } {\n const environment = input.environment ?? process.env\n if (input.flagToken?.trim()) return { token: input.flagToken.trim(), source: 'flag' }\n if (environment.RAILWAY_API_TOKEN?.trim()) {\n return { token: environment.RAILWAY_API_TOKEN.trim(), source: 'environment' }\n }\n const configPath = input.configPath ?? railwayTokenConfigPath(environment)\n const cached = readCachedRailwayToken(configPath)\n if (cached) return { token: cached, source: 'cache' }\n return { token: null, source: null }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,WAAW,YAAY,WAAW,cAAc,UAAU,qBAAqB;AACxF,SAAS,eAAe;AACxB,SAAS,SAAS,YAAY;AAEvB,SAAS,uBACd,cAAiC,QAAQ,KACzC,WAA4B,QAAQ,UAC5B;AACR,MAAI,aAAa,SAAS;AACxB,WAAO,KAAK,YAAY,WAAW,KAAK,QAAQ,GAAG,WAAW,SAAS,GAAG,gBAAgB,cAAc;AAAA,EAC1G;AACA,SAAO,KAAK,YAAY,mBAAmB,KAAK,QAAQ,GAAG,SAAS,GAAG,gBAAgB,cAAc;AACvG;AAEO,SAAS,uBAAuB,MAAc,WAA4B,QAAQ,UAAyB;AAChH,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO;AAC9B,MAAI,aAAa,SAAS;AACxB,UAAM,OAAO,SAAS,IAAI,EAAE,OAAO;AACnC,SAAK,OAAO,QAAW,GAAG;AACxB,YAAM,IAAI,MAAM,oBAAoB,IAAI,6BAA6B;AAAA,IACvE;AAAA,EACF;AACA,QAAM,SAAkB,KAAK,MAAM,aAAa,MAAM,MAAM,CAAC;AAC7D,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,QAAM,QAAS,OAAmC;AAClD,SAAO,OAAO,UAAU,YAAY,MAAM,KAAK,IAAI,MAAM,KAAK,IAAI;AACpE;AAEO,SAAS,wBAAwB,MAAc,OAAqB;AACzE,YAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5C,gBAAc,MAAM,GAAG,KAAK,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;AAAA,GAAM,EAAE,MAAM,IAAM,CAAC;AAC9E,MAAI,QAAQ,aAAa,QAAS,WAAU,MAAM,GAAK;AACzD;AAEO,SAAS,oBAAoB,OAI0C;AAC5E,QAAM,cAAc,MAAM,eAAe,QAAQ;AACjD,MAAI,MAAM,WAAW,KAAK,EAAG,QAAO,EAAE,OAAO,MAAM,UAAU,KAAK,GAAG,QAAQ,OAAO;AACpF,MAAI,YAAY,mBAAmB,KAAK,GAAG;AACzC,WAAO,EAAE,OAAO,YAAY,kBAAkB,KAAK,GAAG,QAAQ,cAAc;AAAA,EAC9E;AACA,QAAM,aAAa,MAAM,cAAc,uBAAuB,WAAW;AACzE,QAAM,SAAS,uBAAuB,UAAU;AAChD,MAAI,OAAQ,QAAO,EAAE,OAAO,QAAQ,QAAQ,QAAQ;AACpD,SAAO,EAAE,OAAO,MAAM,QAAQ,KAAK;AACrC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -394,6 +394,10 @@ process.stdout.write(JSON.stringify(deepClone(doc), (_, v) =>
|
|
|
394
394
|
}
|
|
395
395
|
return null;
|
|
396
396
|
} finally {
|
|
397
|
+
try {
|
|
398
|
+
await esbuild.stop();
|
|
399
|
+
} catch {
|
|
400
|
+
}
|
|
397
401
|
for (const file of ["_openapi-register.mjs", "_openapi-loader.mjs", "_next-stub.cjs"]) {
|
|
398
402
|
try {
|
|
399
403
|
fs.unlinkSync(path.join(cacheDir, file));
|