@slowcook-ai/cli 0.16.0-alpha.1 → 0.16.0-alpha.10
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 +10 -0
- package/dist/cli.js +18 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/brew/agent.d.ts.map +1 -1
- package/dist/commands/brew/agent.js +26 -3
- package/dist/commands/brew/agent.js.map +1 -1
- package/dist/commands/init/mock.js +14 -6
- package/dist/commands/init/mock.js.map +1 -1
- package/dist/commands/plate/classify.d.ts +65 -0
- package/dist/commands/plate/classify.d.ts.map +1 -0
- package/dist/commands/plate/classify.js +194 -0
- package/dist/commands/plate/classify.js.map +1 -0
- package/dist/commands/plate/index.d.ts.map +1 -1
- package/dist/commands/plate/index.js +121 -17
- package/dist/commands/plate/index.js.map +1 -1
- package/dist/commands/port/index.d.ts +21 -0
- package/dist/commands/port/index.d.ts.map +1 -0
- package/dist/commands/port/index.js +203 -0
- package/dist/commands/port/index.js.map +1 -0
- package/dist/commands/port/transform.d.ts +51 -0
- package/dist/commands/port/transform.d.ts.map +1 -0
- package/dist/commands/port/transform.js +102 -0
- package/dist/commands/port/transform.js.map +1 -0
- package/dist/commands/preview/config.d.ts +73 -0
- package/dist/commands/preview/config.d.ts.map +1 -0
- package/dist/commands/preview/config.js +200 -0
- package/dist/commands/preview/config.js.map +1 -0
- package/dist/commands/preview/deploy.d.ts +35 -0
- package/dist/commands/preview/deploy.d.ts.map +1 -0
- package/dist/commands/preview/deploy.js +247 -0
- package/dist/commands/preview/deploy.js.map +1 -0
- package/dist/commands/preview/index.d.ts +9 -0
- package/dist/commands/preview/index.d.ts.map +1 -0
- package/dist/commands/preview/index.js +67 -0
- package/dist/commands/preview/index.js.map +1 -0
- package/dist/commands/preview/ssh.d.ts +49 -0
- package/dist/commands/preview/ssh.d.ts.map +1 -0
- package/dist/commands/preview/ssh.js +99 -0
- package/dist/commands/preview/ssh.js.map +1 -0
- package/dist/commands/preview/teardown.d.ts +25 -0
- package/dist/commands/preview/teardown.d.ts.map +1 -0
- package/dist/commands/preview/teardown.js +164 -0
- package/dist/commands/preview/teardown.js.map +1 -0
- package/dist/commands/refine/proposals-synth.js +17 -1
- package/dist/commands/refine/proposals-synth.js.map +1 -1
- package/dist/commands/refine/spec-yaml.d.ts +100 -100
- package/dist/commands/vibe/emit.d.ts +6 -0
- package/dist/commands/vibe/emit.d.ts.map +1 -1
- package/dist/commands/vibe/emit.js +12 -0
- package/dist/commands/vibe/emit.js.map +1 -1
- package/dist/commands/vibe/index.d.ts.map +1 -1
- package/dist/commands/vibe/index.js +143 -44
- package/dist/commands/vibe/index.js.map +1 -1
- package/package.json +6 -5
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `slowcook port` — 0.16.0-α.8.
|
|
3
|
+
*
|
|
4
|
+
* Deterministic copy step from mock/ → src/. No LLM. Same input →
|
|
5
|
+
* same output. Runs as a CI step before brew on the brew PR's branch.
|
|
6
|
+
*
|
|
7
|
+
* Walks `mock/src/components/` + `mock/src/app/`, copies each file to
|
|
8
|
+
* the mirrored src/ path, applying small import + hook rewrites so the
|
|
9
|
+
* production component reads via `@/lib/data#useDataDomain` instead of
|
|
10
|
+
* the mock-runtime hook. Brew (α.9) writes the actual data layer.
|
|
11
|
+
*
|
|
12
|
+
* Excluded from the copy: `mock/scenarios/`, `mock/src/lib/scenario-
|
|
13
|
+
* registry.ts`, anything mock-only infrastructure.
|
|
14
|
+
*
|
|
15
|
+
* What "deterministic" buys us: the diff is auditable in the PR, brew
|
|
16
|
+
* has a clear scope (anything outside src/lib/data + src/app/api +
|
|
17
|
+
* supabase/migrations is off-limits), and a future tooling pass could
|
|
18
|
+
* re-run port to detect drift.
|
|
19
|
+
*/
|
|
20
|
+
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync, readdirSync } from "node:fs";
|
|
21
|
+
import { dirname, join, relative } from "node:path";
|
|
22
|
+
import { transformForPort, mockPathToSrcPath } from "./transform.js";
|
|
23
|
+
function parseArgs(argv) {
|
|
24
|
+
const args = {
|
|
25
|
+
story: "",
|
|
26
|
+
repoRoot: process.cwd(),
|
|
27
|
+
dryRun: false,
|
|
28
|
+
force: false,
|
|
29
|
+
};
|
|
30
|
+
for (let i = 0; i < argv.length; i++) {
|
|
31
|
+
const a = argv[i];
|
|
32
|
+
const next = argv[i + 1];
|
|
33
|
+
if (a === "--story" && next) {
|
|
34
|
+
args.story = next;
|
|
35
|
+
i++;
|
|
36
|
+
}
|
|
37
|
+
else if (a === "--cwd" && next) {
|
|
38
|
+
args.repoRoot = next;
|
|
39
|
+
i++;
|
|
40
|
+
}
|
|
41
|
+
else if (a === "--dry-run") {
|
|
42
|
+
args.dryRun = true;
|
|
43
|
+
}
|
|
44
|
+
else if (a === "--force") {
|
|
45
|
+
args.force = true;
|
|
46
|
+
}
|
|
47
|
+
else if (a === "--help" || a === "-h") {
|
|
48
|
+
printHelp();
|
|
49
|
+
process.exit(0);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (!args.story) {
|
|
53
|
+
console.error("--story <id> is required.");
|
|
54
|
+
printHelp();
|
|
55
|
+
process.exit(64);
|
|
56
|
+
}
|
|
57
|
+
return args;
|
|
58
|
+
}
|
|
59
|
+
function printHelp() {
|
|
60
|
+
console.log(`
|
|
61
|
+
slowcook port — deterministic mock → src copy (0.16-α.8)
|
|
62
|
+
|
|
63
|
+
Walks mock/src/components/ + mock/src/app/, copies each file to the
|
|
64
|
+
mirrored src/ path, and applies small import + hook rewrites so the
|
|
65
|
+
production component reads from @/lib/data#useDataDomain instead of
|
|
66
|
+
the mock-runtime hook. No LLM; same input → same output.
|
|
67
|
+
|
|
68
|
+
Usage:
|
|
69
|
+
slowcook port --story <id> [--cwd <path>] [--dry-run] [--force]
|
|
70
|
+
|
|
71
|
+
Options:
|
|
72
|
+
--story <id> Story id (e.g. 017). Used in the port-provenance header.
|
|
73
|
+
--cwd <path> Repo root (default: cwd).
|
|
74
|
+
--dry-run Print planned actions; don't write.
|
|
75
|
+
--force Overwrite existing src/ files even if they don't
|
|
76
|
+
carry the @slowcook-port-from marker. Default: refuse
|
|
77
|
+
so a hand-edited file isn't accidentally clobbered.
|
|
78
|
+
|
|
79
|
+
What's NOT ported:
|
|
80
|
+
mock/scenarios/ (scenario fixtures — mock-only)
|
|
81
|
+
mock/src/lib/scenario-registry.ts (consumer-managed)
|
|
82
|
+
mock/Dockerfile, mock/package.json (mock-app shell — mock-only)
|
|
83
|
+
|
|
84
|
+
What's transformed:
|
|
85
|
+
import { useScenarioFixture } from "@slowcook-ai/mock-runtime";
|
|
86
|
+
→ import { useDataDomain } from "@/lib/data";
|
|
87
|
+
useScenarioFixture<T>("domain")
|
|
88
|
+
→ useDataDomain<T>("domain")
|
|
89
|
+
// @slowcook-mock-only lines stripped
|
|
90
|
+
port-provenance header prepended (// @slowcook-port-from mock/ (story-N))
|
|
91
|
+
|
|
92
|
+
Exit codes:
|
|
93
|
+
0 success (or dry-run completed; or no files to port)
|
|
94
|
+
2 refusing to overwrite a non-port file (use --force)
|
|
95
|
+
`);
|
|
96
|
+
}
|
|
97
|
+
export async function port(argv, _cliVersion) {
|
|
98
|
+
const args = parseArgs(argv);
|
|
99
|
+
const mockSrcDir = join(args.repoRoot, "mock/src");
|
|
100
|
+
if (!existsSync(mockSrcDir)) {
|
|
101
|
+
console.log(`No mock/src/ directory at ${mockSrcDir}. Run \`slowcook init mock\` first; nothing to port.`);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const files = walkFiles(mockSrcDir);
|
|
105
|
+
const actions = [];
|
|
106
|
+
for (const absPath of files) {
|
|
107
|
+
const relMockPath = relative(args.repoRoot, absPath).replace(/\\/g, "/");
|
|
108
|
+
const destRel = mockPathToSrcPath(relMockPath);
|
|
109
|
+
if (!destRel)
|
|
110
|
+
continue;
|
|
111
|
+
const inputBody = readFileSync(absPath, "utf8");
|
|
112
|
+
const { output, rewrites } = transformForPort(inputBody, { storyId: args.story });
|
|
113
|
+
void output; // populated below in writePhase
|
|
114
|
+
actions.push(buildPlanned(args.repoRoot, relMockPath, destRel, inputBody, rewrites, args.force, args.story));
|
|
115
|
+
}
|
|
116
|
+
if (actions.length === 0) {
|
|
117
|
+
console.log("Nothing to port (no eligible mock/src/ files).");
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
console.log(`slowcook port · story-${args.story} · cwd: ${relative(process.cwd(), args.repoRoot) || "."}`);
|
|
121
|
+
for (const a of actions)
|
|
122
|
+
printAction(a);
|
|
123
|
+
if (args.dryRun) {
|
|
124
|
+
console.log("\n--dry-run: no files written.");
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
// 0.16.0-α.8 — refuse if any action is blocked. Better to surface
|
|
128
|
+
// the conflict than overwrite a hand-edited production file.
|
|
129
|
+
const blocked = actions.filter((a) => a.kind === "blocked");
|
|
130
|
+
if (blocked.length > 0) {
|
|
131
|
+
console.error(`\nRefusing to port: ${blocked.length} file(s) at the destination ` +
|
|
132
|
+
`path don't carry the @slowcook-port-from marker. Pass --force to overwrite, ` +
|
|
133
|
+
`or hand-merge first.`);
|
|
134
|
+
process.exit(2);
|
|
135
|
+
}
|
|
136
|
+
let written = 0;
|
|
137
|
+
let unchanged = 0;
|
|
138
|
+
for (const a of actions) {
|
|
139
|
+
if (a.kind === "no-op") {
|
|
140
|
+
unchanged += 1;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const inputBody = readFileSync(join(args.repoRoot, a.src), "utf8");
|
|
144
|
+
const { output } = transformForPort(inputBody, { storyId: args.story });
|
|
145
|
+
const destAbs = join(args.repoRoot, a.dest);
|
|
146
|
+
mkdirSync(dirname(destAbs), { recursive: true });
|
|
147
|
+
writeFileSync(destAbs, output, "utf8");
|
|
148
|
+
written += 1;
|
|
149
|
+
}
|
|
150
|
+
console.log(`\nDone. ${written} file(s) written; ${unchanged} unchanged.`);
|
|
151
|
+
}
|
|
152
|
+
function buildPlanned(repoRoot, src, dest, inputBody, rewrites, force, story) {
|
|
153
|
+
const destAbs = join(repoRoot, dest);
|
|
154
|
+
const { output } = transformForPort(inputBody, { storyId: story });
|
|
155
|
+
const destExists = existsSync(destAbs);
|
|
156
|
+
if (!destExists) {
|
|
157
|
+
return { src, dest, kind: "create", rewrites };
|
|
158
|
+
}
|
|
159
|
+
const existing = readFileSync(destAbs, "utf8");
|
|
160
|
+
if (existing === output) {
|
|
161
|
+
return { src, dest, kind: "no-op", rewrites: [] };
|
|
162
|
+
}
|
|
163
|
+
// Allow overwrite when the existing file is a previously-ported file
|
|
164
|
+
// (carries the marker) OR --force is set.
|
|
165
|
+
if (existing.includes("@slowcook-port-from") || force) {
|
|
166
|
+
return { src, dest, kind: "update", rewrites };
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
src,
|
|
170
|
+
dest,
|
|
171
|
+
kind: "blocked",
|
|
172
|
+
rewrites: [],
|
|
173
|
+
reason: "destination file exists, doesn't carry @slowcook-port-from marker, --force not set",
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function printAction(a) {
|
|
177
|
+
const tag = a.kind === "create" ? "CREATE" :
|
|
178
|
+
a.kind === "update" ? "UPDATE" :
|
|
179
|
+
a.kind === "blocked" ? "BLOCK " :
|
|
180
|
+
"NO-OP ";
|
|
181
|
+
console.log(` ${tag} ${a.src} → ${a.dest}`);
|
|
182
|
+
if (a.reason)
|
|
183
|
+
console.log(` ${a.reason}`);
|
|
184
|
+
for (const r of a.rewrites)
|
|
185
|
+
console.log(` · ${r}`);
|
|
186
|
+
}
|
|
187
|
+
function walkFiles(dir, acc = []) {
|
|
188
|
+
const entries = readdirSync(dir);
|
|
189
|
+
for (const name of entries) {
|
|
190
|
+
if (name.startsWith("."))
|
|
191
|
+
continue;
|
|
192
|
+
const full = join(dir, name);
|
|
193
|
+
const st = statSync(full);
|
|
194
|
+
if (st.isDirectory()) {
|
|
195
|
+
walkFiles(full, acc);
|
|
196
|
+
}
|
|
197
|
+
else if (/\.(tsx?|jsx?)$/.test(name)) {
|
|
198
|
+
acc.push(full);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return acc;
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/port/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Port-time source transforms — 0.16.0-α.8.
|
|
3
|
+
*
|
|
4
|
+
* `slowcook port` is the deterministic mock → src copy step. It walks
|
|
5
|
+
* mock/src/components/ + mock/src/app/, copies each file to the
|
|
6
|
+
* mirrored src/ path, and applies a small set of import + hook
|
|
7
|
+
* rewrites so the production component reads its data from the real
|
|
8
|
+
* Supabase layer instead of the mock-runtime scenarios.
|
|
9
|
+
*
|
|
10
|
+
* The transform is intentionally narrow:
|
|
11
|
+
*
|
|
12
|
+
* import { useScenarioFixture } from "@slowcook-ai/mock-runtime";
|
|
13
|
+
* ↓
|
|
14
|
+
* import { useDataDomain } from "@/lib/data";
|
|
15
|
+
*
|
|
16
|
+
* const x = useScenarioFixture<T>("pins");
|
|
17
|
+
* ↓
|
|
18
|
+
* const x = useDataDomain<T>("pins");
|
|
19
|
+
*
|
|
20
|
+
* The new `@/lib/data#useDataDomain` is brew's territory (α.9 writes
|
|
21
|
+
* the real implementation against Supabase). At port-time, all that
|
|
22
|
+
* exists in src/lib/data/ may be a stub `useDataDomain` — brew fills
|
|
23
|
+
* the body. That keeps tests red until brew's done.
|
|
24
|
+
*
|
|
25
|
+
* Pure functions over strings. No fs access. Same input → same output.
|
|
26
|
+
*/
|
|
27
|
+
export interface PortTransformResult {
|
|
28
|
+
/** New file body. */
|
|
29
|
+
output: string;
|
|
30
|
+
/** Each rewrite that fired (for the audit-trail commit message). */
|
|
31
|
+
rewrites: string[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Apply all port-time source transforms to a single file's contents.
|
|
35
|
+
* Returns the transformed body + the list of rewrites that fired.
|
|
36
|
+
*/
|
|
37
|
+
export declare function transformForPort(input: string, opts?: {
|
|
38
|
+
storyId?: string;
|
|
39
|
+
}): PortTransformResult;
|
|
40
|
+
/**
|
|
41
|
+
* Translate a `mock/<rest>` path into the corresponding `src/<rest>`
|
|
42
|
+
* path. Used by the file walker.
|
|
43
|
+
*
|
|
44
|
+
* Examples:
|
|
45
|
+
* mock/src/components/rewo/RewoCard.tsx → src/components/rewo/RewoCard.tsx
|
|
46
|
+
* mock/src/app/(main)/u/[handle]/page.tsx → src/app/(main)/u/[handle]/page.tsx
|
|
47
|
+
* mock/src/lib/scenario-registry.ts → null (mock-only; never ports)
|
|
48
|
+
* mock/scenarios/story-017.ts → null (mock-only; never ports)
|
|
49
|
+
*/
|
|
50
|
+
export declare function mockPathToSrcPath(mockPath: string): string | null;
|
|
51
|
+
//# sourceMappingURL=transform.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../../src/commands/port/transform.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,MAAM,WAAW,mBAAmB;IAClC,qBAAqB;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAOD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,mBAAmB,CAqDhG;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAajE"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Port-time source transforms — 0.16.0-α.8.
|
|
3
|
+
*
|
|
4
|
+
* `slowcook port` is the deterministic mock → src copy step. It walks
|
|
5
|
+
* mock/src/components/ + mock/src/app/, copies each file to the
|
|
6
|
+
* mirrored src/ path, and applies a small set of import + hook
|
|
7
|
+
* rewrites so the production component reads its data from the real
|
|
8
|
+
* Supabase layer instead of the mock-runtime scenarios.
|
|
9
|
+
*
|
|
10
|
+
* The transform is intentionally narrow:
|
|
11
|
+
*
|
|
12
|
+
* import { useScenarioFixture } from "@slowcook-ai/mock-runtime";
|
|
13
|
+
* ↓
|
|
14
|
+
* import { useDataDomain } from "@/lib/data";
|
|
15
|
+
*
|
|
16
|
+
* const x = useScenarioFixture<T>("pins");
|
|
17
|
+
* ↓
|
|
18
|
+
* const x = useDataDomain<T>("pins");
|
|
19
|
+
*
|
|
20
|
+
* The new `@/lib/data#useDataDomain` is brew's territory (α.9 writes
|
|
21
|
+
* the real implementation against Supabase). At port-time, all that
|
|
22
|
+
* exists in src/lib/data/ may be a stub `useDataDomain` — brew fills
|
|
23
|
+
* the body. That keeps tests red until brew's done.
|
|
24
|
+
*
|
|
25
|
+
* Pure functions over strings. No fs access. Same input → same output.
|
|
26
|
+
*/
|
|
27
|
+
const SCENARIO_FIXTURE_IMPORT_RE = /import\s+\{\s*useScenarioFixture\s*(?:,\s*([^}]*))?\}\s+from\s+["']@slowcook-ai\/mock-runtime["'];?\s*\n/g;
|
|
28
|
+
const SCENARIO_USE_RE = /\buseScenarioFixture\b/g;
|
|
29
|
+
/**
|
|
30
|
+
* Apply all port-time source transforms to a single file's contents.
|
|
31
|
+
* Returns the transformed body + the list of rewrites that fired.
|
|
32
|
+
*/
|
|
33
|
+
export function transformForPort(input, opts) {
|
|
34
|
+
let output = input;
|
|
35
|
+
const rewrites = [];
|
|
36
|
+
// 1) Rewrite mock-runtime imports → src/lib/data import.
|
|
37
|
+
if (SCENARIO_FIXTURE_IMPORT_RE.test(output)) {
|
|
38
|
+
SCENARIO_FIXTURE_IMPORT_RE.lastIndex = 0;
|
|
39
|
+
output = output.replace(SCENARIO_FIXTURE_IMPORT_RE, (_m, otherImports) => {
|
|
40
|
+
if (otherImports && otherImports.trim()) {
|
|
41
|
+
// Other named imports stayed too — preserve them as a separate
|
|
42
|
+
// mock-runtime import line. Conservative: rare in mock components
|
|
43
|
+
// (most use only useScenarioFixture).
|
|
44
|
+
return (`import { useDataDomain } from "@/lib/data";\n` +
|
|
45
|
+
`import { ${otherImports.trim()} } from "@slowcook-ai/mock-runtime";\n`);
|
|
46
|
+
}
|
|
47
|
+
return `import { useDataDomain } from "@/lib/data";\n`;
|
|
48
|
+
});
|
|
49
|
+
rewrites.push("rewrote @slowcook-ai/mock-runtime → @/lib/data import");
|
|
50
|
+
}
|
|
51
|
+
// 2) Rewrite useScenarioFixture call sites → useDataDomain.
|
|
52
|
+
if (SCENARIO_USE_RE.test(output)) {
|
|
53
|
+
SCENARIO_USE_RE.lastIndex = 0;
|
|
54
|
+
output = output.replace(SCENARIO_USE_RE, "useDataDomain");
|
|
55
|
+
rewrites.push("rewrote useScenarioFixture(...) → useDataDomain(...)");
|
|
56
|
+
}
|
|
57
|
+
// 3) Strip the `// @slowcook-mock-only` markers so the production
|
|
58
|
+
// file doesn't carry mock-only annotations (vibe sometimes adds
|
|
59
|
+
// these to flag temporary structures).
|
|
60
|
+
if (/^\s*\/\/\s*@slowcook-mock-only\b.*$/m.test(output)) {
|
|
61
|
+
output = output.replace(/^\s*\/\/\s*@slowcook-mock-only\b.*$\n?/gm, "");
|
|
62
|
+
rewrites.push("stripped // @slowcook-mock-only markers");
|
|
63
|
+
}
|
|
64
|
+
// 4) Insert a port-provenance header so future readers know which
|
|
65
|
+
// story shipped this file. Skipped when one already exists.
|
|
66
|
+
if (opts?.storyId && !/@slowcook-port-from\b/.test(output)) {
|
|
67
|
+
const header = `// @slowcook-port-from mock/ (story-${opts.storyId})\n` +
|
|
68
|
+
`// Copied by \`slowcook port\` from the mock app.\n` +
|
|
69
|
+
`// UI shape is the design contract; brew (--mode plate) writes the\n` +
|
|
70
|
+
`// data layer + handlers but does NOT touch this file.\n`;
|
|
71
|
+
output = output.replace(/^("use client";?\s*\n)?/, (m) => (m ?? "") + header);
|
|
72
|
+
rewrites.push(`prepended port-provenance header (story-${opts.storyId})`);
|
|
73
|
+
}
|
|
74
|
+
return { output, rewrites };
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Translate a `mock/<rest>` path into the corresponding `src/<rest>`
|
|
78
|
+
* path. Used by the file walker.
|
|
79
|
+
*
|
|
80
|
+
* Examples:
|
|
81
|
+
* mock/src/components/rewo/RewoCard.tsx → src/components/rewo/RewoCard.tsx
|
|
82
|
+
* mock/src/app/(main)/u/[handle]/page.tsx → src/app/(main)/u/[handle]/page.tsx
|
|
83
|
+
* mock/src/lib/scenario-registry.ts → null (mock-only; never ports)
|
|
84
|
+
* mock/scenarios/story-017.ts → null (mock-only; never ports)
|
|
85
|
+
*/
|
|
86
|
+
export function mockPathToSrcPath(mockPath) {
|
|
87
|
+
if (!mockPath.startsWith("mock/"))
|
|
88
|
+
return null;
|
|
89
|
+
// Mock-only files: scenarios + scenario registry never port.
|
|
90
|
+
if (mockPath.startsWith("mock/scenarios/"))
|
|
91
|
+
return null;
|
|
92
|
+
if (mockPath === "mock/src/lib/scenario-registry.ts")
|
|
93
|
+
return null;
|
|
94
|
+
if (mockPath.startsWith("mock/src/lib/scenario-registry/"))
|
|
95
|
+
return null;
|
|
96
|
+
// mock/src/* → src/* ; mock/<other>/* → don't port (only src/ maps cleanly).
|
|
97
|
+
if (mockPath.startsWith("mock/src/")) {
|
|
98
|
+
return "src/" + mockPath.slice("mock/src/".length);
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=transform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transform.js","sourceRoot":"","sources":["../../../src/commands/port/transform.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `.brewing/preview.yaml` schema + parser — 0.16.0-α.5.
|
|
3
|
+
*
|
|
4
|
+
* Lightweight config that tells `slowcook preview deploy/teardown` how
|
|
5
|
+
* to ssh into the consumer's box, where to put files, what port range
|
|
6
|
+
* to allocate from, and what URL pattern the box's reverse proxy serves.
|
|
7
|
+
*
|
|
8
|
+
* Slowcook is stateless re: hosting. Each consumer provides their own
|
|
9
|
+
* SSH-reachable box (Docker engine + reverse proxy with wildcard cert);
|
|
10
|
+
* this config tells slowcook how to reach it.
|
|
11
|
+
*
|
|
12
|
+
* Hand-parsed (no yaml dep) because the schema is small + flat. If we
|
|
13
|
+
* ever need anchors / multi-doc / etc, switch to the workspace's `yaml`
|
|
14
|
+
* package — it's already a transitive dep via cli/dependencies.
|
|
15
|
+
*/
|
|
16
|
+
export interface PreviewConfig {
|
|
17
|
+
type: "ssh";
|
|
18
|
+
host: string;
|
|
19
|
+
user: string;
|
|
20
|
+
/** GitHub Actions secret NAME holding the SSH private key. */
|
|
21
|
+
keySecret: string;
|
|
22
|
+
port: number;
|
|
23
|
+
/** Inclusive range to allocate Docker host ports from (e.g. [4000, 4099]). */
|
|
24
|
+
portRange: [number, number];
|
|
25
|
+
/** URL template; `{port}` is substituted. e.g. https://mock-{port}.preview.example.com */
|
|
26
|
+
urlTemplate: string;
|
|
27
|
+
/** Absolute path on the box where slowcook stages PR builds. */
|
|
28
|
+
remoteRoot: string;
|
|
29
|
+
/** Path within the consumer's repo to the mock app. Default: "mock". */
|
|
30
|
+
mockDir: string;
|
|
31
|
+
}
|
|
32
|
+
export declare class PreviewConfigError extends Error {
|
|
33
|
+
constructor(message: string);
|
|
34
|
+
}
|
|
35
|
+
export declare const PREVIEW_CONFIG_PATH = ".brewing/preview.yaml";
|
|
36
|
+
/**
|
|
37
|
+
* Parse `.brewing/preview.yaml`. Returns the typed config or throws
|
|
38
|
+
* `PreviewConfigError` with a precise message identifying the missing
|
|
39
|
+
* or malformed field.
|
|
40
|
+
*
|
|
41
|
+
* Accepts either:
|
|
42
|
+
*
|
|
43
|
+
* preview:
|
|
44
|
+
* type: ssh
|
|
45
|
+
* host: ...
|
|
46
|
+
* ...
|
|
47
|
+
*
|
|
48
|
+
* or top-level keys (the `preview:` wrapper is optional but
|
|
49
|
+
* recommended; consumers will likely add a `box:` or other top-level
|
|
50
|
+
* sections later).
|
|
51
|
+
*/
|
|
52
|
+
export declare function parsePreviewConfig(yamlText: string): PreviewConfig;
|
|
53
|
+
export declare function readPreviewConfig(repoRoot: string): PreviewConfig;
|
|
54
|
+
/**
|
|
55
|
+
* Build the preview URL for a given allocated port.
|
|
56
|
+
*/
|
|
57
|
+
export declare function urlForPort(cfg: PreviewConfig, port: number): string;
|
|
58
|
+
/**
|
|
59
|
+
* Container name for a given PR. Single source of truth so deploy +
|
|
60
|
+
* teardown agree on the name.
|
|
61
|
+
*/
|
|
62
|
+
export declare function containerNameForPr(pr: number): string;
|
|
63
|
+
/**
|
|
64
|
+
* Image tag for a given PR. We rebuild per-PR (each PR has its own
|
|
65
|
+
* scenario set), so tags don't collide.
|
|
66
|
+
*/
|
|
67
|
+
export declare function imageTagForPr(pr: number): string;
|
|
68
|
+
/**
|
|
69
|
+
* Remote staging directory for a given PR. Build artifacts + the tar
|
|
70
|
+
* extract live here.
|
|
71
|
+
*/
|
|
72
|
+
export declare function remoteDirForPr(cfg: PreviewConfig, pr: number): string;
|
|
73
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/commands/preview/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,KAAK,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,8DAA8D;IAC9D,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,0FAA0F;IAC1F,WAAW,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,eAAO,MAAM,mBAAmB,0BAA0B,CAAC;AAE3D;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CAwHlE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CAUjE;AAgBD;;GAEG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAErE"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `.brewing/preview.yaml` schema + parser — 0.16.0-α.5.
|
|
3
|
+
*
|
|
4
|
+
* Lightweight config that tells `slowcook preview deploy/teardown` how
|
|
5
|
+
* to ssh into the consumer's box, where to put files, what port range
|
|
6
|
+
* to allocate from, and what URL pattern the box's reverse proxy serves.
|
|
7
|
+
*
|
|
8
|
+
* Slowcook is stateless re: hosting. Each consumer provides their own
|
|
9
|
+
* SSH-reachable box (Docker engine + reverse proxy with wildcard cert);
|
|
10
|
+
* this config tells slowcook how to reach it.
|
|
11
|
+
*
|
|
12
|
+
* Hand-parsed (no yaml dep) because the schema is small + flat. If we
|
|
13
|
+
* ever need anchors / multi-doc / etc, switch to the workspace's `yaml`
|
|
14
|
+
* package — it's already a transitive dep via cli/dependencies.
|
|
15
|
+
*/
|
|
16
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
export class PreviewConfigError extends Error {
|
|
19
|
+
constructor(message) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = "PreviewConfigError";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export const PREVIEW_CONFIG_PATH = ".brewing/preview.yaml";
|
|
25
|
+
/**
|
|
26
|
+
* Parse `.brewing/preview.yaml`. Returns the typed config or throws
|
|
27
|
+
* `PreviewConfigError` with a precise message identifying the missing
|
|
28
|
+
* or malformed field.
|
|
29
|
+
*
|
|
30
|
+
* Accepts either:
|
|
31
|
+
*
|
|
32
|
+
* preview:
|
|
33
|
+
* type: ssh
|
|
34
|
+
* host: ...
|
|
35
|
+
* ...
|
|
36
|
+
*
|
|
37
|
+
* or top-level keys (the `preview:` wrapper is optional but
|
|
38
|
+
* recommended; consumers will likely add a `box:` or other top-level
|
|
39
|
+
* sections later).
|
|
40
|
+
*/
|
|
41
|
+
export function parsePreviewConfig(yamlText) {
|
|
42
|
+
const lines = yamlText.split(/\r?\n/);
|
|
43
|
+
// Track inside-`preview:` block by indentation; allow flat top-level too.
|
|
44
|
+
const flat = {};
|
|
45
|
+
let inPreview = false;
|
|
46
|
+
let baseIndent = -1;
|
|
47
|
+
for (const raw of lines) {
|
|
48
|
+
const line = raw.replace(/#.*$/, ""); // strip comments
|
|
49
|
+
if (line.trim() === "")
|
|
50
|
+
continue;
|
|
51
|
+
const indentMatch = line.match(/^(\s*)/);
|
|
52
|
+
const indent = indentMatch ? indentMatch[1].length : 0;
|
|
53
|
+
const content = line.slice(indent);
|
|
54
|
+
if (indent === 0 && content.startsWith("preview:")) {
|
|
55
|
+
inPreview = true;
|
|
56
|
+
baseIndent = -1;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (indent === 0 && content.includes(":") && !content.startsWith("preview:")) {
|
|
60
|
+
// top-level key outside preview — could be a sibling block we don't parse
|
|
61
|
+
inPreview = false;
|
|
62
|
+
const m = content.match(/^([a-zA-Z_][\w-]*)\s*:\s*(.*)$/);
|
|
63
|
+
if (m && m[2]?.trim()) {
|
|
64
|
+
flat[m[1]] = stripQuotes(m[2].trim());
|
|
65
|
+
}
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (inPreview) {
|
|
69
|
+
if (baseIndent === -1)
|
|
70
|
+
baseIndent = indent;
|
|
71
|
+
if (indent < baseIndent) {
|
|
72
|
+
inPreview = false;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const m = content.match(/^([a-zA-Z_][\w-]*)\s*:\s*(.*)$/);
|
|
76
|
+
if (m) {
|
|
77
|
+
flat[m[1]] = stripQuotes(m[2].trim());
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const required = [
|
|
82
|
+
"type",
|
|
83
|
+
"host",
|
|
84
|
+
"user",
|
|
85
|
+
"keySecret",
|
|
86
|
+
"urlTemplate",
|
|
87
|
+
"remoteRoot",
|
|
88
|
+
];
|
|
89
|
+
// Map snake_case YAML to camelCase keys.
|
|
90
|
+
const aliasMap = {
|
|
91
|
+
type: "type",
|
|
92
|
+
host: "host",
|
|
93
|
+
user: "user",
|
|
94
|
+
key_secret: "keySecret",
|
|
95
|
+
keysecret: "keySecret",
|
|
96
|
+
url_template: "urlTemplate",
|
|
97
|
+
urltemplate: "urlTemplate",
|
|
98
|
+
remote_root: "remoteRoot",
|
|
99
|
+
remoteroot: "remoteRoot",
|
|
100
|
+
port: "port",
|
|
101
|
+
port_range: "portRange",
|
|
102
|
+
portrange: "portRange",
|
|
103
|
+
mock_dir: "mockDir",
|
|
104
|
+
mockdir: "mockDir",
|
|
105
|
+
};
|
|
106
|
+
const cfg = {};
|
|
107
|
+
for (const [k, v] of Object.entries(flat)) {
|
|
108
|
+
const camel = aliasMap[k.toLowerCase()];
|
|
109
|
+
if (!camel)
|
|
110
|
+
continue;
|
|
111
|
+
if (camel === "port") {
|
|
112
|
+
cfg.port = parseInt(v, 10);
|
|
113
|
+
}
|
|
114
|
+
else if (camel === "portRange") {
|
|
115
|
+
const m = v.match(/^\[?\s*(\d+)\s*,\s*(\d+)\s*\]?$/);
|
|
116
|
+
if (!m) {
|
|
117
|
+
throw new PreviewConfigError(`preview.port_range: expected "[lo, hi]"; got ${JSON.stringify(v)}`);
|
|
118
|
+
}
|
|
119
|
+
const lo = parseInt(m[1], 10);
|
|
120
|
+
const hi = parseInt(m[2], 10);
|
|
121
|
+
if (lo > hi || lo <= 0 || hi > 65535) {
|
|
122
|
+
throw new PreviewConfigError(`preview.port_range: ${lo}..${hi} is not a valid port range`);
|
|
123
|
+
}
|
|
124
|
+
cfg.portRange = [lo, hi];
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
// string fields
|
|
128
|
+
cfg[camel] = v;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
for (const k of required) {
|
|
132
|
+
if (cfg[k] === undefined || cfg[k] === "") {
|
|
133
|
+
throw new PreviewConfigError(`preview.${snakeOf(k)} is required in ${PREVIEW_CONFIG_PATH}.`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (cfg.type !== "ssh") {
|
|
137
|
+
throw new PreviewConfigError(`preview.type must be "ssh" (got ${JSON.stringify(cfg.type)}). Other deploy types may ship later.`);
|
|
138
|
+
}
|
|
139
|
+
if (!cfg.urlTemplate.includes("{port}")) {
|
|
140
|
+
throw new PreviewConfigError(`preview.url_template must contain the literal "{port}" placeholder; got ${JSON.stringify(cfg.urlTemplate)}.`);
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
type: "ssh",
|
|
144
|
+
host: cfg.host,
|
|
145
|
+
user: cfg.user,
|
|
146
|
+
keySecret: cfg.keySecret,
|
|
147
|
+
port: cfg.port ?? 22,
|
|
148
|
+
portRange: cfg.portRange ?? [4000, 4099],
|
|
149
|
+
urlTemplate: cfg.urlTemplate,
|
|
150
|
+
remoteRoot: cfg.remoteRoot,
|
|
151
|
+
mockDir: cfg.mockDir ?? "mock",
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
export function readPreviewConfig(repoRoot) {
|
|
155
|
+
const p = join(repoRoot, PREVIEW_CONFIG_PATH);
|
|
156
|
+
if (!existsSync(p)) {
|
|
157
|
+
throw new PreviewConfigError(`${PREVIEW_CONFIG_PATH} not found at ${p}. ` +
|
|
158
|
+
`See docs/operating-guide.md for the schema + box setup steps.`);
|
|
159
|
+
}
|
|
160
|
+
const text = readFileSync(p, "utf8");
|
|
161
|
+
return parsePreviewConfig(text);
|
|
162
|
+
}
|
|
163
|
+
function stripQuotes(s) {
|
|
164
|
+
if ((s.startsWith('"') && s.endsWith('"')) ||
|
|
165
|
+
(s.startsWith("'") && s.endsWith("'"))) {
|
|
166
|
+
return s.slice(1, -1);
|
|
167
|
+
}
|
|
168
|
+
return s;
|
|
169
|
+
}
|
|
170
|
+
function snakeOf(camel) {
|
|
171
|
+
return camel.replace(/([A-Z])/g, "_$1").toLowerCase();
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Build the preview URL for a given allocated port.
|
|
175
|
+
*/
|
|
176
|
+
export function urlForPort(cfg, port) {
|
|
177
|
+
return cfg.urlTemplate.replace("{port}", String(port));
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Container name for a given PR. Single source of truth so deploy +
|
|
181
|
+
* teardown agree on the name.
|
|
182
|
+
*/
|
|
183
|
+
export function containerNameForPr(pr) {
|
|
184
|
+
return `slowcook-mock-pr-${pr}`;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Image tag for a given PR. We rebuild per-PR (each PR has its own
|
|
188
|
+
* scenario set), so tags don't collide.
|
|
189
|
+
*/
|
|
190
|
+
export function imageTagForPr(pr) {
|
|
191
|
+
return `slowcook-mock-pr-${pr}:latest`;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Remote staging directory for a given PR. Build artifacts + the tar
|
|
195
|
+
* extract live here.
|
|
196
|
+
*/
|
|
197
|
+
export function remoteDirForPr(cfg, pr) {
|
|
198
|
+
return `${cfg.remoteRoot.replace(/\/+$/, "")}/pr-${pr}`;
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/commands/preview/config.ts"],"names":[],"mappings":"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"}
|