@forgecart/cli 2.202608221935.0 → 2.202608300703.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/src/cli.js CHANGED
@@ -1,28 +1,9 @@
1
- import * as fs from 'node:fs';
2
- import * as path from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
1
  import { Command } from 'commander';
5
2
  import { createInitCommand } from './commands/init.js';
6
3
  import { createDeleteCommand, createDeployCommand, createEnvCommand, createInitApplicationCommand, createListCommand, createLogsCommand, createScaleCommand, createStatusCommand, createWhoamiCommand, } from './commands/placeholders.js';
4
+ import { createRefreshCommand } from './commands/refresh.js';
7
5
  import { printError } from './output.js';
8
- /**
9
- * Resolve the package manifest from this module's location. The depth differs
10
- * by layout — `src/cli.ts` (dev) sits one level below the package root,
11
- * `dist/src/cli.js` (published) two — and `dist/` never contains a
12
- * `package.json`, so the first existing candidate is always the right one.
13
- */
14
- function readPackageVersion() {
15
- const here = path.dirname(fileURLToPath(import.meta.url));
16
- const candidates = [
17
- path.join(here, '..', 'package.json'),
18
- path.join(here, '..', '..', 'package.json'),
19
- ];
20
- const manifest = candidates.find((candidate) => fs.existsSync(candidate));
21
- if (!manifest) {
22
- throw new Error(`package.json not found near ${here}; cannot resolve the CLI version.`);
23
- }
24
- return JSON.parse(fs.readFileSync(manifest, 'utf-8')).version;
25
- }
6
+ import { readPackageVersion } from './version.js';
26
7
  /**
27
8
  * CLI version, read from `package.json` (the single source of truth) so the
28
9
  * `--version` output can never drift from the published version.
@@ -42,6 +23,7 @@ export function createProgram() {
42
23
  .description('CLI for scaffolding and operating ForgeCart channel storefronts')
43
24
  .version(CLI_VERSION);
44
25
  program.addCommand(createInitCommand());
26
+ program.addCommand(createRefreshCommand());
45
27
  program.addCommand(createInitApplicationCommand());
46
28
  program.addCommand(createDeployCommand());
47
29
  program.addCommand(createListCommand());
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,gBAAgB,EAChB,4BAA4B,EAC5B,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,kBAAkB,EAAE,CAAC;AAEhD;;;;;;GAMG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,WAAW,CAAC;SACjB,WAAW,CAAC,iEAAiE,CAAC;SAC9E,OAAO,CAAC,WAAW,CAAC,CAAC;IAExB,OAAO,CAAC,UAAU,CAAC,iBAAiB,EAAE,CAAC,CAAC;IACxC,OAAO,CAAC,UAAU,CAAC,oBAAoB,EAAE,CAAC,CAAC;IAC3C,OAAO,CAAC,UAAU,CAAC,4BAA4B,EAAE,CAAC,CAAC;IACnD,OAAO,CAAC,UAAU,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAC1C,OAAO,CAAC,UAAU,CAAC,iBAAiB,EAAE,CAAC,CAAC;IACxC,OAAO,CAAC,UAAU,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAC1C,OAAO,CAAC,UAAU,CAAC,iBAAiB,EAAE,CAAC,CAAC;IACxC,OAAO,CAAC,UAAU,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAC1C,OAAO,CAAC,UAAU,CAAC,kBAAkB,EAAE,CAAC,CAAC;IACzC,OAAO,CAAC,UAAU,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACvC,OAAO,CAAC,UAAU,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAE1C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,OAAiB,OAAO,CAAC,IAAI;IACrD,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;IAChC,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,UAAU,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC;QAChC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Shared black-box harness for the CLI command specs.
3
+ *
4
+ * We run the BUILT CLI (the artifact that ships to npm) against a temp dir and
5
+ * assert on its outputs, rather than importing the source: the package is ESM
6
+ * (`import.meta.url`, `.js` import specifiers) and the repo's jest transform is
7
+ * CJS, so importing the source would break. Spawning the real binary also
8
+ * verifies what users actually get. The `test` target dependsOn `build`, so
9
+ * `dist/bin/forgecart.js` exists when these run.
10
+ */
11
+ export declare const CLI: string;
12
+ /** The bundled storefront template the built CLI scaffolds from. */
13
+ export declare const TEMPLATE_SRC: string;
14
+ export interface CliRun {
15
+ status: number | null;
16
+ /** Combined stdout+stderr, for message assertions. */
17
+ out: string;
18
+ /** stdout alone, for `--json` report parsing. */
19
+ stdout: string;
20
+ }
21
+ export declare function runCli(args: string[], cwd: string, envOverrides?: Record<string, string>): CliRun;
@@ -0,0 +1,29 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { resolve } from 'node:path';
3
+ /**
4
+ * Shared black-box harness for the CLI command specs.
5
+ *
6
+ * We run the BUILT CLI (the artifact that ships to npm) against a temp dir and
7
+ * assert on its outputs, rather than importing the source: the package is ESM
8
+ * (`import.meta.url`, `.js` import specifiers) and the repo's jest transform is
9
+ * CJS, so importing the source would break. Spawning the real binary also
10
+ * verifies what users actually get. The `test` target dependsOn `build`, so
11
+ * `dist/bin/forgecart.js` exists when these run.
12
+ */
13
+ export const CLI = resolve(__dirname, '..', '..', '..', 'dist', 'bin', 'forgecart.js');
14
+ /** The bundled storefront template the built CLI scaffolds from. */
15
+ export const TEMPLATE_SRC = resolve(__dirname, '..', '..', '..', 'templates', 'storefront');
16
+ export function runCli(args, cwd, envOverrides = {}) {
17
+ // Hermetic by default: a WORKSPACE_TEMPLATE_DIR exported in the developer's
18
+ // shell must not flip the cold-path tests into the warmed path.
19
+ const env = { ...process.env };
20
+ delete env.WORKSPACE_TEMPLATE_DIR;
21
+ Object.assign(env, envOverrides);
22
+ const result = spawnSync(process.execPath, [CLI, ...args], { cwd, encoding: 'utf8', env });
23
+ return {
24
+ status: result.status,
25
+ out: `${result.stdout ?? ''}${result.stderr ?? ''}`,
26
+ stdout: result.stdout ?? '',
27
+ };
28
+ }
29
+ //# sourceMappingURL=cli-harness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-harness.js","sourceRoot":"","sources":["../../../../src/commands/__test__/cli-harness.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC;AAEvF,oEAAoE;AACpE,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;AAU5F,MAAM,UAAU,MAAM,CACpB,IAAc,EACd,GAAW,EACX,eAAuC,EAAE;IAEzC,4EAA4E;IAC5E,gEAAgE;IAChE,MAAM,GAAG,GAAuC,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IACnE,OAAO,GAAG,CAAC,sBAAsB,CAAC;IAClC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IAEjC,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3F,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,GAAG,EAAE,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE;QACnD,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,EAAE;KAC5B,CAAC;AACJ,CAAC"}
@@ -51,6 +51,13 @@ export interface InitOptions {
51
51
  */
52
52
  configOnly?: boolean;
53
53
  }
54
+ /**
55
+ * Absolute path to the bundled storefront scaffold.
56
+ *
57
+ * At runtime this file lives at `<pkg>/dist/src/commands/init.js`, so the
58
+ * `templates/` directory shipped alongside `dist/` is three levels up.
59
+ */
60
+ export declare function getStorefrontTemplateDir(): string;
54
61
  /**
55
62
  * Run `forgecart init`.
56
63
  *
@@ -3,6 +3,8 @@ import { dirname, join, resolve } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { Command } from 'commander';
5
5
  import { print } from '../output.js';
6
+ import { readPackageVersion } from '../version.js';
7
+ import { writeTemplateManifest } from './template-manifest.js';
6
8
  /** Default shop API base. Overridable via `--api-url`. */
7
9
  export const DEFAULT_SHOP_API_URL = 'https://api.forgecart.com';
8
10
  /**
@@ -28,7 +30,7 @@ export const DEPS_READY_MARKER_PATH = '.forgecart/deps-ready.json';
28
30
  * At runtime this file lives at `<pkg>/dist/src/commands/init.js`, so the
29
31
  * `templates/` directory shipped alongside `dist/` is three levels up.
30
32
  */
31
- function getStorefrontTemplateDir() {
33
+ export function getStorefrontTemplateDir() {
32
34
  const here = dirname(fileURLToPath(import.meta.url));
33
35
  return resolve(here, '..', '..', '..', 'templates', 'storefront');
34
36
  }
@@ -123,35 +125,99 @@ function buildConfig(channelToken, options) {
123
125
  storefrontPath: '.',
124
126
  };
125
127
  }
126
- /** Build the `.env` contents for the storefront SDK clients. */
127
- function buildEnv(channelToken, options) {
128
- const lines = [
129
- `FORGECART_CHANNEL_TOKEN=${channelToken}`,
130
- `FORGECART_SHOP_API_URL=${options.apiUrl}`,
131
- ];
128
+ /**
129
+ * The platform-owned `.env` keys this invocation manages, in canonical render
130
+ * order. Conditional keys (browser URL, admin secret) enter the map only when
131
+ * their flag is present — an existing line for an unprovided key is
132
+ * workspace-owned for THIS run and survives untouched (#1123: the prewarmed
133
+ * config-only path re-runs init on every provisioning drive, and a drive that
134
+ * omits `--admin-secret` must not strip a secret an earlier drive wrote).
135
+ */
136
+ function buildPlatformEnv(channelToken, options) {
137
+ const entries = new Map();
138
+ entries.set('FORGECART_CHANNEL_TOKEN', channelToken);
139
+ entries.set('FORGECART_SHOP_API_URL', options.apiUrl);
132
140
  const browserApiUrl = options.browserApiUrl?.trim();
133
141
  if (browserApiUrl) {
134
- lines.push(`FORGECART_SHOP_API_BROWSER_URL=${browserApiUrl}`);
142
+ entries.set('FORGECART_SHOP_API_BROWSER_URL', browserApiUrl);
135
143
  }
136
144
  const secret = options.adminSecret?.trim();
137
145
  if (secret) {
138
- lines.push(`FORGECART_ADMIN_SECRET=${secret}`);
146
+ entries.set('FORGECART_ADMIN_SECRET', secret);
147
+ }
148
+ return entries;
149
+ }
150
+ /**
151
+ * Merge the platform-owned keys into an existing `.env`, preserving every
152
+ * other line — merchant keys, comments, ordering — verbatim (#1123: a
153
+ * wholesale rewrite wiped the demo store's DEMO_* config on three
154
+ * provisioning drives). Platform keys are refreshed in place at their
155
+ * existing position; duplicates collapse onto the first occurrence; managed
156
+ * keys with no existing line append in canonical order. Trailing blank lines
157
+ * normalize to the single final newline. An empty `existing` renders the
158
+ * canonical fresh `.env`, byte-identical to the pre-merge scaffold output.
159
+ */
160
+ function mergeEnv(existing, channelToken, options) {
161
+ const platform = buildPlatformEnv(channelToken, options);
162
+ const refreshed = new Set();
163
+ const merged = [];
164
+ for (const line of existing.split('\n')) {
165
+ const out = refreshPlatformLine(line, platform, refreshed);
166
+ if (out !== null) {
167
+ merged.push(out);
168
+ }
169
+ }
170
+ while (merged.length > 0 && merged[merged.length - 1] === '') {
171
+ merged.pop();
172
+ }
173
+ for (const [key, value] of platform) {
174
+ if (!refreshed.has(key)) {
175
+ merged.push(`${key}=${value}`);
176
+ }
177
+ }
178
+ merged.push('');
179
+ return merged.join('\n');
180
+ }
181
+ /**
182
+ * Map one existing `.env` line through the merge: workspace-owned lines pass
183
+ * through verbatim, a managed platform key refreshes in place on first sight,
184
+ * and its later duplicates drop (null).
185
+ */
186
+ function refreshPlatformLine(line, platform, refreshed) {
187
+ const key = /^([A-Z][A-Z0-9_]*)=/.exec(line)?.[1];
188
+ if (!key || !platform.has(key))
189
+ return line;
190
+ if (refreshed.has(key))
191
+ return null;
192
+ refreshed.add(key);
193
+ return `${key}=${platform.get(key) ?? ''}`;
194
+ }
195
+ /** Read a file that may legitimately not exist yet; any other failure propagates. */
196
+ async function readFileIfPresent(path) {
197
+ try {
198
+ return await readFile(path, 'utf8');
199
+ }
200
+ catch (error) {
201
+ if (error.code === 'ENOENT')
202
+ return '';
203
+ throw error;
139
204
  }
140
- lines.push('');
141
- return lines.join('\n');
142
205
  }
143
206
  /**
144
207
  * Write the per-channel config the storefront reads at runtime: `.forgecart/
145
208
  * config.json` (channel token, api url, app flag) and `.env` (the SDK
146
209
  * client env). Shared by the full scaffold and the `--config-only` serve-in-place
147
210
  * path, so both write byte-identical config — the only difference is whether the
148
- * template tree was copied first.
211
+ * template tree was copied first. `config.json` is pure platform state and is
212
+ * rewritten wholesale; `.env` is merge-preserved (#1123 — see mergeEnv).
149
213
  */
150
214
  async function writeChannelConfig(targetDir, channelToken, options) {
151
215
  const configDir = join(targetDir, '.forgecart');
152
216
  await mkdir(configDir, { recursive: true });
153
217
  await writeFile(join(configDir, 'config.json'), `${JSON.stringify(buildConfig(channelToken, options), null, 2)}\n`, 'utf8');
154
- await writeFile(join(targetDir, '.env'), buildEnv(channelToken, options), 'utf8');
218
+ const envPath = join(targetDir, '.env');
219
+ const existingEnv = await readFileIfPresent(envPath);
220
+ await writeFile(envPath, mergeEnv(existingEnv, channelToken, options), 'utf8');
155
221
  }
156
222
  /** Print the post-scaffold next steps. */
157
223
  function printNextSteps(targetDir, application, depsInstalled) {
@@ -224,6 +290,9 @@ export async function runInit(name, options) {
224
290
  }
225
291
  await writeChannelConfig(targetDir, channelToken, options);
226
292
  await writeDepsReadyMarker(targetDir, plan);
293
+ // Inventory the render for `forgecart refresh` (#1116) — hashed from the
294
+ // plan's template dir, the exact bytes the copy above landed.
295
+ await writeTemplateManifest(targetDir, plan.templateDir, readPackageVersion());
227
296
  printNextSteps(name ?? '.', options.application ?? false, plan.source === 'warmed');
228
297
  }
229
298
  /** Build the `init [name]` command. */
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/commands/init.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/commands/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import { Command } from 'commander';
2
+ /**
3
+ * `forgecart refresh` (#1116) — re-render the platform-owned template files
4
+ * of an EXISTING workspace from the CLI's bundled template, under the
5
+ * file-ownership partition:
6
+ *
7
+ * - a platform file whose content still matches its manifest hash (pristine
8
+ * since generation) re-renders to the current template;
9
+ * - a platform file that DIVERGED from its manifest hash was edited in the
10
+ * workspace — conflict: skipped and flagged, never clobbered;
11
+ * - a file the template does not ship is workspace-owned and never touched;
12
+ * - `.env` and `.forgecart/` are outside refresh's jurisdiction entirely
13
+ * (channel config rides `init`'s own write paths; `.env` rides the #1123
14
+ * preserve-and-merge).
15
+ *
16
+ * Workspaces generated before the manifest existed cannot prove pristineness,
17
+ * so every divergent file refuses by default; `--adopt <relpath>` takes the
18
+ * template version for explicitly reviewed paths. The refresh always (re)writes
19
+ * the manifest, so a pre-manifest workspace becomes precise after its first
20
+ * refresh. The JSON report (`--json`) is the machine seam the provisioning
21
+ * orchestrator parses.
22
+ */
23
+ interface RefreshOptions {
24
+ json?: boolean;
25
+ adopt?: string[];
26
+ }
27
+ /** Per-file verdicts, in report shape. */
28
+ export interface RefreshReport {
29
+ rendered: string[];
30
+ conflicts: string[];
31
+ adopted: string[];
32
+ upToDate: string[];
33
+ /** True when package.json was re-rendered — the driver must re-install. */
34
+ dependenciesChanged: boolean;
35
+ }
36
+ /** Run `forgecart refresh`. */
37
+ export declare function runRefresh(name: string | undefined, options: RefreshOptions): Promise<void>;
38
+ /** Build the `refresh [name]` command. */
39
+ export declare function createRefreshCommand(): Command;
40
+ export {};
@@ -0,0 +1,147 @@
1
+ import { mkdir, readFile, stat, writeFile } from 'node:fs/promises';
2
+ import { dirname, join, resolve } from 'node:path';
3
+ import { Command } from 'commander';
4
+ import { print } from '../output.js';
5
+ import { readPackageVersion } from '../version.js';
6
+ import { getStorefrontTemplateDir } from './init.js';
7
+ import { TEMPLATE_MANIFEST_RELPATH, TEMPLATE_MANIFEST_SCHEMA_VERSION, hashContent, listTemplateFiles, readTemplateManifest, } from './template-manifest.js';
8
+ /** True if the path exists and is a regular file. */
9
+ async function isFile(path) {
10
+ try {
11
+ return (await stat(path)).isFile();
12
+ }
13
+ catch {
14
+ return false;
15
+ }
16
+ }
17
+ /** Read file bytes; null when absent (any other failure propagates). */
18
+ async function readBytesIfPresent(path) {
19
+ try {
20
+ return await readFile(path);
21
+ }
22
+ catch (error) {
23
+ if (error.code === 'ENOENT')
24
+ return null;
25
+ throw error;
26
+ }
27
+ }
28
+ /**
29
+ * Decide one file's fate. `recordedHash` is the manifest's inventory entry
30
+ * (undefined on pre-manifest workspaces and template-added files).
31
+ */
32
+ function decideFile(workspaceHash, templateHash, recordedHash, adopt) {
33
+ if (workspaceHash === null)
34
+ return 'rendered';
35
+ if (workspaceHash === templateHash)
36
+ return 'upToDate';
37
+ if (adopt)
38
+ return 'adopted';
39
+ if (recordedHash !== undefined && workspaceHash === recordedHash)
40
+ return 'rendered';
41
+ return 'conflict';
42
+ }
43
+ /**
44
+ * Judge one template file against the workspace and apply the write when the
45
+ * verdict says so. Returns the verdict plus the template hash the manifest
46
+ * update needs.
47
+ */
48
+ async function refreshTemplateFile(targetDir, templateDir, relpath, recordedHash, adopt) {
49
+ const templateBytes = await readFile(join(templateDir, ...relpath.split('/')));
50
+ const templateHash = hashContent(templateBytes);
51
+ const workspacePath = join(targetDir, ...relpath.split('/'));
52
+ const workspaceBytes = await readBytesIfPresent(workspacePath);
53
+ const verdict = decideFile(workspaceBytes === null ? null : hashContent(workspaceBytes), templateHash, recordedHash, adopt);
54
+ if (verdict === 'rendered' || verdict === 'adopted') {
55
+ await mkdir(dirname(workspacePath), { recursive: true });
56
+ await writeFile(workspacePath, templateBytes);
57
+ }
58
+ return { verdict, templateHash };
59
+ }
60
+ /**
61
+ * Fold one file's verdict into the report and the next manifest: bucket the
62
+ * relpath, flip the install flag when package.json was (re)written, and
63
+ * record the inventory entry.
64
+ */
65
+ function recordVerdict(report, nextFiles, relpath, verdict, templateHash, recordedHash) {
66
+ if (relpath === 'package.json' && (verdict === 'rendered' || verdict === 'adopted')) {
67
+ report.dependenciesChanged = true;
68
+ }
69
+ report[verdict === 'conflict' ? 'conflicts' : verdict].push(relpath);
70
+ const nextHash = nextManifestHash(verdict, templateHash, recordedHash);
71
+ if (nextHash !== undefined) {
72
+ nextFiles[relpath] = nextHash;
73
+ }
74
+ }
75
+ /**
76
+ * The next manifest entry for a file: written/current files record the
77
+ * template hash; a conflict keeps its previous inventory (or stays
78
+ * uninventoried), so it keeps conflicting until adopted.
79
+ */
80
+ function nextManifestHash(verdict, templateHash, recordedHash) {
81
+ if (verdict === 'conflict')
82
+ return recordedHash;
83
+ return templateHash;
84
+ }
85
+ /** Run `forgecart refresh`. */
86
+ export async function runRefresh(name, options) {
87
+ const targetDir = name ? resolve(process.cwd(), name) : process.cwd();
88
+ if (!(await isFile(join(targetDir, 'package.json')))) {
89
+ throw new Error(`refresh requires an existing storefront at ${targetDir} (no package.json found).`);
90
+ }
91
+ const templateDir = getStorefrontTemplateDir();
92
+ const manifest = await readTemplateManifest(targetDir);
93
+ const adoptSet = new Set(options.adopt ?? []);
94
+ const report = {
95
+ rendered: [],
96
+ conflicts: [],
97
+ adopted: [],
98
+ upToDate: [],
99
+ dependenciesChanged: false,
100
+ };
101
+ const nextFiles = {};
102
+ for (const relpath of await listTemplateFiles(templateDir)) {
103
+ const recordedHash = manifest?.files[relpath];
104
+ const { verdict, templateHash } = await refreshTemplateFile(targetDir, templateDir, relpath, recordedHash, adoptSet.has(relpath));
105
+ recordVerdict(report, nextFiles, relpath, verdict, templateHash, recordedHash);
106
+ }
107
+ const nextManifest = {
108
+ schemaVersion: TEMPLATE_MANIFEST_SCHEMA_VERSION,
109
+ templateVersion: readPackageVersion(),
110
+ generatedAt: new Date().toISOString(),
111
+ files: nextFiles,
112
+ };
113
+ await mkdir(join(targetDir, '.forgecart'), { recursive: true });
114
+ await writeFile(join(targetDir, ...TEMPLATE_MANIFEST_RELPATH.split('/')), `${JSON.stringify(nextManifest, null, 2)}\n`, 'utf8');
115
+ if (options.json) {
116
+ print(JSON.stringify(report));
117
+ return;
118
+ }
119
+ printSummary(report);
120
+ }
121
+ function printSummary(report) {
122
+ print(`Refreshed ${String(report.rendered.length)} platform file(s).`);
123
+ if (report.adopted.length > 0) {
124
+ print(`Adopted ${String(report.adopted.length)} file(s) onto the template version.`);
125
+ }
126
+ if (report.conflicts.length > 0) {
127
+ print('Conflicts (workspace-edited platform files, left untouched):');
128
+ for (const relpath of report.conflicts) {
129
+ print(` ${relpath} (re-run with --adopt ${relpath} to take the template version)`);
130
+ }
131
+ }
132
+ if (report.dependenciesChanged) {
133
+ print('package.json changed — run your package manager install before the next dev boot.');
134
+ }
135
+ }
136
+ /** Build the `refresh [name]` command. */
137
+ export function createRefreshCommand() {
138
+ return new Command('refresh')
139
+ .description('Re-render platform-owned template files onto an existing storefront (workspace-owned files survive; edited platform files flag as conflicts)')
140
+ .argument('[name]', 'Target directory name (defaults to the current directory)')
141
+ .option('--adopt <relpath...>', 'Take the template version of specific flagged files (reviewed adoption)')
142
+ .option('--json', 'Print a machine-readable JSON report instead of the human summary')
143
+ .action(async (name, options) => {
144
+ await runRefresh(name, options);
145
+ });
146
+ }
147
+ //# sourceMappingURL=refresh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh.js","sourceRoot":"","sources":["../../../src/commands/refresh.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The template manifest — the file-ownership partition's source of truth
3
+ * (#1116). Written by `forgecart init` when it renders the template, read by
4
+ * `forgecart refresh` to distinguish platform-owned files (inventoried here;
5
+ * re-render when unmodified since generation) from workspace-owned state
6
+ * (everything else; never touched) and from agent-EDITED platform files
7
+ * (inventoried but diverged; conflict — skip and flag, never clobber).
8
+ */
9
+ export declare const TEMPLATE_MANIFEST_RELPATH = ".forgecart/template-manifest.json";
10
+ export interface TemplateManifest {
11
+ schemaVersion: number;
12
+ /** The CLI version whose bundled template rendered this workspace. */
13
+ templateVersion: string;
14
+ generatedAt: string;
15
+ /** posix relpath → sha256 hex of the rendered content. */
16
+ files: Record<string, string>;
17
+ }
18
+ export declare const TEMPLATE_MANIFEST_SCHEMA_VERSION = 1;
19
+ /** sha256 hex of file content — the manifest's and refresh's shared hash. */
20
+ export declare function hashContent(content: Buffer | string): string;
21
+ /**
22
+ * List the template's files as posix relpaths, depth-first, with the ignore
23
+ * set applied to top-level names (both shipped entries are top-level dirs;
24
+ * nested paths inherit their root's verdict).
25
+ */
26
+ export declare function listTemplateFiles(templateDir: string): Promise<string[]>;
27
+ /**
28
+ * Inventory the just-rendered template into the workspace's manifest. Hashes
29
+ * the TEMPLATE side of the copy — the bytes that actually landed — so a
30
+ * later refresh can prove per-file pristineness.
31
+ */
32
+ export declare function writeTemplateManifest(targetDir: string, templateDir: string, templateVersion: string): Promise<void>;
33
+ /**
34
+ * Read a workspace's manifest. Absent OR unreadable → null: the workspace is
35
+ * treated as pre-manifest, where refresh refuses every divergent file until
36
+ * explicitly adopted — the safe direction for a corrupt inventory too.
37
+ */
38
+ export declare function readTemplateManifest(targetDir: string): Promise<TemplateManifest | null>;
@@ -0,0 +1,116 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { readFile, readdir, writeFile } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ /**
5
+ * The template manifest — the file-ownership partition's source of truth
6
+ * (#1116). Written by `forgecart init` when it renders the template, read by
7
+ * `forgecart refresh` to distinguish platform-owned files (inventoried here;
8
+ * re-render when unmodified since generation) from workspace-owned state
9
+ * (everything else; never touched) and from agent-EDITED platform files
10
+ * (inventoried but diverged; conflict — skip and flag, never clobber).
11
+ */
12
+ export const TEMPLATE_MANIFEST_RELPATH = '.forgecart/template-manifest.json';
13
+ export const TEMPLATE_MANIFEST_SCHEMA_VERSION = 1;
14
+ /**
15
+ * Top-level names never inventoried: dependency trees and build caches are
16
+ * runtime state, `.forgecart/` and `.env` are per-channel config owned by
17
+ * `init`'s own write paths (`.env` by the #1123 merge), never by refresh.
18
+ */
19
+ const ALWAYS_IGNORED = ['node_modules', '.next', '.forgecart', '.env'];
20
+ /** sha256 hex of file content — the manifest's and refresh's shared hash. */
21
+ export function hashContent(content) {
22
+ return createHash('sha256').update(content).digest('hex');
23
+ }
24
+ /**
25
+ * The template's exclusion set: `.forgecartignore` entries (one top-level
26
+ * name per line, `#` comments) unioned with the always-ignored names. The
27
+ * ignore file predates this module; parsing it here makes it live.
28
+ */
29
+ async function readIgnoreSet(templateDir) {
30
+ const ignored = new Set(ALWAYS_IGNORED);
31
+ let raw = '';
32
+ try {
33
+ raw = await readFile(join(templateDir, '.forgecartignore'), 'utf8');
34
+ }
35
+ catch (error) {
36
+ if (error.code !== 'ENOENT')
37
+ throw error;
38
+ }
39
+ for (const line of raw.split('\n')) {
40
+ const entry = line.trim();
41
+ if (entry && !entry.startsWith('#')) {
42
+ ignored.add(entry);
43
+ }
44
+ }
45
+ return ignored;
46
+ }
47
+ /**
48
+ * List the template's files as posix relpaths, depth-first, with the ignore
49
+ * set applied to top-level names (both shipped entries are top-level dirs;
50
+ * nested paths inherit their root's verdict).
51
+ */
52
+ export async function listTemplateFiles(templateDir) {
53
+ const ignored = await readIgnoreSet(templateDir);
54
+ const files = [];
55
+ await walk(templateDir, '', ignored, files);
56
+ return files.sort((a, b) => a.localeCompare(b));
57
+ }
58
+ async function walk(dir, relPrefix, ignored, out) {
59
+ const entries = await readdir(dir, { withFileTypes: true });
60
+ for (const entry of entries) {
61
+ if (relPrefix === '' && ignored.has(entry.name))
62
+ continue;
63
+ const rel = relPrefix === '' ? entry.name : `${relPrefix}/${entry.name}`;
64
+ if (entry.isDirectory()) {
65
+ await walk(join(dir, entry.name), rel, ignored, out);
66
+ continue;
67
+ }
68
+ if (entry.isFile()) {
69
+ out.push(rel);
70
+ }
71
+ }
72
+ }
73
+ /**
74
+ * Inventory the just-rendered template into the workspace's manifest. Hashes
75
+ * the TEMPLATE side of the copy — the bytes that actually landed — so a
76
+ * later refresh can prove per-file pristineness.
77
+ */
78
+ export async function writeTemplateManifest(targetDir, templateDir, templateVersion) {
79
+ const files = {};
80
+ for (const relpath of await listTemplateFiles(templateDir)) {
81
+ files[relpath] = hashContent(await readFile(join(templateDir, ...relpath.split('/'))));
82
+ }
83
+ const manifest = {
84
+ schemaVersion: TEMPLATE_MANIFEST_SCHEMA_VERSION,
85
+ templateVersion,
86
+ generatedAt: new Date().toISOString(),
87
+ files,
88
+ };
89
+ await writeFile(join(targetDir, ...TEMPLATE_MANIFEST_RELPATH.split('/')), `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
90
+ }
91
+ /**
92
+ * Read a workspace's manifest. Absent OR unreadable → null: the workspace is
93
+ * treated as pre-manifest, where refresh refuses every divergent file until
94
+ * explicitly adopted — the safe direction for a corrupt inventory too.
95
+ */
96
+ export async function readTemplateManifest(targetDir) {
97
+ let raw;
98
+ try {
99
+ raw = await readFile(join(targetDir, ...TEMPLATE_MANIFEST_RELPATH.split('/')), 'utf8');
100
+ }
101
+ catch (error) {
102
+ if (error.code === 'ENOENT')
103
+ return null;
104
+ throw error;
105
+ }
106
+ try {
107
+ const parsed = JSON.parse(raw);
108
+ if (parsed.schemaVersion !== TEMPLATE_MANIFEST_SCHEMA_VERSION)
109
+ return null;
110
+ return parsed;
111
+ }
112
+ catch {
113
+ return null;
114
+ }
115
+ }
116
+ //# sourceMappingURL=template-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-manifest.js","sourceRoot":"","sources":["../../../src/commands/template-manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Read the CLI's own version from `package.json` — the single source of truth,
3
+ * so `--version` output and the template manifest's `templateVersion` can
4
+ * never drift from the published version.
5
+ *
6
+ * Resolution: at runtime this module lives at `<pkg>/dist/<file>.js` (or one
7
+ * directory deeper for `dist/commands/*`), and the tsc build does not copy
8
+ * `package.json`, so the first existing candidate is always the right one.
9
+ */
10
+ export declare function readPackageVersion(): string;
@@ -0,0 +1,25 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ /**
5
+ * Read the CLI's own version from `package.json` — the single source of truth,
6
+ * so `--version` output and the template manifest's `templateVersion` can
7
+ * never drift from the published version.
8
+ *
9
+ * Resolution: at runtime this module lives at `<pkg>/dist/<file>.js` (or one
10
+ * directory deeper for `dist/commands/*`), and the tsc build does not copy
11
+ * `package.json`, so the first existing candidate is always the right one.
12
+ */
13
+ export function readPackageVersion() {
14
+ const here = path.dirname(fileURLToPath(import.meta.url));
15
+ const candidates = [
16
+ path.join(here, '..', 'package.json'),
17
+ path.join(here, '..', '..', 'package.json'),
18
+ ];
19
+ const manifest = candidates.find((candidate) => fs.existsSync(candidate));
20
+ if (!manifest) {
21
+ throw new Error(`package.json not found near ${here}; cannot resolve the CLI version.`);
22
+ }
23
+ return JSON.parse(fs.readFileSync(manifest, 'utf-8')).version;
24
+ }
25
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,MAAM,UAAU,GAAG;QACjB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC;QACrC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC;KAC5C,CAAC;IACF,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;IAC1E,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,+BAA+B,IAAI,mCAAmC,CAAC,CAAC;IAC1F,CAAC;IACD,OAAQ,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAyB,CAAC,OAAO,CAAC;AACzF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgecart/cli",
3
- "version": "2.202608221935.0",
3
+ "version": "2.202608300703.0",
4
4
  "type": "module",
5
5
  "description": "CLI for scaffolding and operating ForgeCart channel storefronts",
6
6
  "bin": {
@@ -42,21 +42,34 @@ export interface ShopSessionConfig {
42
42
 
43
43
  const SESSION_MIRROR_COOKIE = 'forgecart-session-client';
44
44
 
45
+ /**
46
+ * Browser-readable mirror of the httpOnly `forgecart-visitor` cookie,
47
+ * minted by the middleware (#1079 P1). Its value rides the SDK's
48
+ * `forgecart-visitor` WS connection-params header so visitor-subject
49
+ * experiment goals stamp server-side.
50
+ */
51
+ const VISITOR_MIRROR_COOKIE = 'forgecart-visitor-client';
52
+
45
53
  let config: ShopSessionConfig | null = null;
46
54
  let client: ForgeCartShopClient | null = null;
47
55
  let syncedToken: string | null = null;
48
56
 
49
- /** Read the session mirror cookie (null outside the browser or when absent). */
50
- export function readMirrorSession(): string | null {
57
+ /** Read a JS-visible mirror cookie (null outside the browser or when absent). */
58
+ function readMirrorCookie(name: string): string | null {
51
59
  if (typeof document === 'undefined') return null;
52
60
  const entry = document.cookie
53
61
  .split('; ')
54
- .find((candidate) => candidate.startsWith(`${SESSION_MIRROR_COOKIE}=`));
62
+ .find((candidate) => candidate.startsWith(`${name}=`));
55
63
  if (!entry) return null;
56
- const value = decodeURIComponent(entry.slice(SESSION_MIRROR_COOKIE.length + 1));
64
+ const value = decodeURIComponent(entry.slice(name.length + 1));
57
65
  return value.length > 0 ? value : null;
58
66
  }
59
67
 
68
+ /** Read the session mirror cookie (null outside the browser or when absent). */
69
+ export function readMirrorSession(): string | null {
70
+ return readMirrorCookie(SESSION_MIRROR_COOKIE);
71
+ }
72
+
60
73
  /** Arm the singleton with the channel coordinates (idempotent; provider mount). */
61
74
  export function initShopSession(next: ShopSessionConfig): void {
62
75
  config = next;
@@ -77,6 +90,11 @@ function ensureClient(): ForgeCartShopClient {
77
90
  client = new ForgeCartShopClient({
78
91
  endpoint: config.apiUrl,
79
92
  channelToken: config.channelToken,
93
+ // The visitor mirror is minted by the middleware before any page can
94
+ // mount the provider, so it is present on every real navigation; when
95
+ // it is somehow absent the client simply sends no visitor header and
96
+ // visitor-subject goals stay unstamped (never guess — #1079).
97
+ visitorId: readMirrorCookie(VISITOR_MIRROR_COOKIE) ?? undefined,
80
98
  });
81
99
  const existing = readMirrorSession();
82
100
  if (existing) {
@@ -15,6 +15,16 @@ import { uuidv7 } from 'uuidv7';
15
15
  * - Mint ONLY when the cookie is absent. An identified visitor's response
16
16
  * carries NO `Set-Cookie` at all — re-sending it on every response would
17
17
  * make every page uncacheable for shared caches.
18
+ * - The key is DUAL-HOMED (#1079 P1, deliberate posture change): the
19
+ * canonical `forgecart-visitor` cookie stays httpOnly (server reads:
20
+ * `getVariant()`'s assignment, header forwarding), and a byte-identical
21
+ * NON-httpOnly mirror `forgecart-visitor-client` (the
22
+ * `forgecart-session-client` pattern) exposes it to browser JS so the
23
+ * SDK can send it in WS connection params — visitor-subject goal events
24
+ * are unattributable without it. The id is a UUIDv7, not a secret, and
25
+ * the httpOnly original stays authoritative: a missing or tampered
26
+ * mirror is re-synced FROM it (one extra `Set-Cookie` response per
27
+ * legacy visitor, then the steady state is Set-Cookie-free again).
18
28
  * - Zero fetches, zero environment reads: the workspace-pod prewarm boots
19
29
  * this dev server with no `.env`, and the middleware must be inert
20
30
  * to that (it never crashes a request, env or not).
@@ -48,6 +58,14 @@ import { uuidv7 } from 'uuidv7';
48
58
 
49
59
  const VISITOR_COOKIE_NAME = 'forgecart-visitor';
50
60
 
61
+ /**
62
+ * Browser-readable mirror of {@link VISITOR_COOKIE_NAME} (#1079 P1) — same
63
+ * value, same lifetime, NOT httpOnly. Read by the shop-session SDK
64
+ * bootstrap and sent as the `forgecart-visitor` WS connection-params
65
+ * header. Never read server-side: the httpOnly original is authoritative.
66
+ */
67
+ const VISITOR_MIRROR_COOKIE_NAME = 'forgecart-visitor-client';
68
+
51
69
  /** 400 days — Chrome's upper bound on cookie lifetime (RFC 6265bis). */
52
70
  const VISITOR_COOKIE_MAX_AGE_SECONDS = 400 * 24 * 60 * 60;
53
71
 
@@ -149,8 +167,15 @@ function mergeForcedEntries(request: NextRequest): string | null {
149
167
 
150
168
  export function middleware(request: NextRequest): NextResponse {
151
169
  const forcedEntryValue = mergeForcedEntries(request);
152
- const mintVisitor = !request.cookies.has(VISITOR_COOKIE_NAME);
153
- if (!forcedEntryValue && !mintVisitor) {
170
+ const storedVisitorId = request.cookies.get(VISITOR_COOKIE_NAME)?.value;
171
+ const mintVisitor = !storedVisitorId;
172
+ // Mirror sync (#1079 P1): absent on every pre-mirror visitor's first
173
+ // request back, and re-synced whenever it disagrees with the httpOnly
174
+ // original (client JS can rewrite it — the original is authoritative).
175
+ // `mintVisitor` implies a sync: a fresh id is written to BOTH homes.
176
+ const syncMirror =
177
+ mintVisitor || request.cookies.get(VISITOR_MIRROR_COOKIE_NAME)?.value !== storedVisitorId;
178
+ if (!forcedEntryValue && !syncMirror) {
154
179
  return NextResponse.next();
155
180
  }
156
181
 
@@ -175,14 +200,27 @@ export function middleware(request: NextRequest): NextResponse {
175
200
  response = NextResponse.next();
176
201
  }
177
202
 
178
- if (mintVisitor) {
179
- // `secure` is honored on https (deployed storefronts) and on localhost;
180
- // browsers may drop it on other plain-http dev hosts — acceptable, since a
181
- // stable visitor identity is a production concern, not a preview one.
203
+ if (syncMirror) {
204
+ const visitorId = storedVisitorId ?? uuidv7();
205
+ if (mintVisitor) {
206
+ // `secure` is honored on https (deployed storefronts) and on localhost;
207
+ // browsers may drop it on other plain-http dev hosts — acceptable, since a
208
+ // stable visitor identity is a production concern, not a preview one.
209
+ response.cookies.set({
210
+ name: VISITOR_COOKIE_NAME,
211
+ value: visitorId,
212
+ httpOnly: true,
213
+ secure: true,
214
+ sameSite: 'lax',
215
+ maxAge: VISITOR_COOKIE_MAX_AGE_SECONDS,
216
+ path: '/',
217
+ });
218
+ }
219
+ // Deliberately NOT httpOnly — see VISITOR_MIRROR_COOKIE_NAME.
182
220
  response.cookies.set({
183
- name: VISITOR_COOKIE_NAME,
184
- value: uuidv7(),
185
- httpOnly: true,
221
+ name: VISITOR_MIRROR_COOKIE_NAME,
222
+ value: visitorId,
223
+ httpOnly: false,
186
224
  secure: true,
187
225
  sameSite: 'lax',
188
226
  maxAge: VISITOR_COOKIE_MAX_AGE_SECONDS,