@forgecart/cli 2.202610130131.0 → 2.202610130322.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.
Files changed (61) hide show
  1. package/dist/src/admin-api/admin-api-refusal.error.d.ts +2 -2
  2. package/dist/src/admin-api/admin-api-refusal.error.js +2 -2
  3. package/dist/src/admin-api/admin-api.client.d.ts +8 -4
  4. package/dist/src/admin-api/admin-api.client.js +11 -9
  5. package/dist/src/admin-api/admin-api.client.js.map +1 -1
  6. package/dist/src/admin-api/admin-api.result.d.ts +4 -0
  7. package/dist/src/admin-api/admin-api.result.js +17 -0
  8. package/dist/src/admin-api/admin-api.result.js.map +1 -1
  9. package/dist/src/authorization/cli-authorization-flow.d.ts +21 -14
  10. package/dist/src/authorization/cli-authorization-flow.js +40 -31
  11. package/dist/src/authorization/cli-authorization-flow.js.map +1 -1
  12. package/dist/src/authorization/cli-credential-check.d.ts +6 -13
  13. package/dist/src/authorization/cli-credential-check.js +6 -23
  14. package/dist/src/authorization/cli-credential-check.js.map +1 -1
  15. package/dist/src/authorization/cli-credential-step.d.ts +74 -0
  16. package/dist/src/authorization/cli-credential-step.js +154 -0
  17. package/dist/src/authorization/cli-credential-step.js.map +1 -0
  18. package/dist/src/authorization/cli-unauthorized-refusal.d.ts +18 -8
  19. package/dist/src/authorization/cli-unauthorized-refusal.js +26 -11
  20. package/dist/src/authorization/cli-unauthorized-refusal.js.map +1 -1
  21. package/dist/src/authorization/type/cli-authorization.types.d.ts +36 -4
  22. package/dist/src/authorization/type/cli-authorization.types.js +16 -3
  23. package/dist/src/authorization/type/cli-authorization.types.js.map +1 -1
  24. package/dist/src/commands/init.d.ts +0 -10
  25. package/dist/src/commands/init.js +17 -42
  26. package/dist/src/commands/init.js.map +1 -1
  27. package/dist/src/commands/sync.js +2 -2
  28. package/dist/src/commands/sync.js.map +1 -1
  29. package/dist/src/deploy/deploy.runner.d.ts +15 -8
  30. package/dist/src/deploy/deploy.runner.js +19 -10
  31. package/dist/src/deploy/deploy.runner.js.map +1 -1
  32. package/dist/src/sync/sync-dotenv.file.d.ts +30 -22
  33. package/dist/src/sync/sync-dotenv.file.js +49 -54
  34. package/dist/src/sync/sync-dotenv.file.js.map +1 -1
  35. package/dist/src/sync/sync-env-channel.d.ts +47 -0
  36. package/dist/src/sync/sync-env-channel.js +106 -0
  37. package/dist/src/sync/sync-env-channel.js.map +1 -0
  38. package/dist/src/sync/sync-env.runner.d.ts +26 -10
  39. package/dist/src/sync/sync-env.runner.js +73 -40
  40. package/dist/src/sync/sync-env.runner.js.map +1 -1
  41. package/dist/src/sync/sync-record.file.d.ts +8 -6
  42. package/dist/src/sync/sync-record.file.js +18 -5
  43. package/dist/src/sync/sync-record.file.js.map +1 -1
  44. package/dist/src/sync/sync-round.d.ts +23 -13
  45. package/dist/src/sync/sync-round.js +73 -37
  46. package/dist/src/sync/sync-round.js.map +1 -1
  47. package/dist/src/sync/sync-watch.d.ts +2 -1
  48. package/dist/src/sync/sync-watch.js +2 -1
  49. package/dist/src/sync/sync-watch.js.map +1 -1
  50. package/dist/src/sync/sync.runner.d.ts +16 -6
  51. package/dist/src/sync/sync.runner.js +29 -19
  52. package/dist/src/sync/sync.runner.js.map +1 -1
  53. package/dist/src/sync/type/sync.types.d.ts +49 -16
  54. package/dist/src/sync/type/sync.types.js.map +1 -1
  55. package/dist/src/sync/workspace-sync.client.d.ts +14 -8
  56. package/dist/src/sync/workspace-sync.client.js +40 -17
  57. package/dist/src/sync/workspace-sync.client.js.map +1 -1
  58. package/dist/src/util/dotenv.util.d.ts +19 -5
  59. package/dist/src/util/dotenv.util.js +42 -5
  60. package/dist/src/util/dotenv.util.js.map +1 -1
  61. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import { resolve } from 'node:path';
2
2
  import { AdminApiClient } from '../admin-api/admin-api.client.js';
3
- import { CliCredentialCheck } from '../authorization/cli-credential-check.js';
3
+ import { CliCredentialStep } from '../authorization/cli-credential-step.js';
4
4
  import { CliExitLadder, CliRefusal } from '../exit-ladder.js';
5
5
  import { print } from '../output.js';
6
6
  import { DeployFollowBound } from './deploy-follow-bound.js';
@@ -8,14 +8,19 @@ import { DeploymentFollow } from './deployment-follow.js';
8
8
  import { DeploymentClient } from './deployment.client.js';
9
9
  import { DeploymentEndStatus, } from './type/deploy.types.js';
10
10
  /**
11
- * `forgecart deploy [name]` (#411): publish what the board serves. Before
12
- * anything leaves the machine the run settles its four credentials — the
13
- * environment, then the storefront's `.env`, then its `.forgecart/config.json`,
14
- * exactly as `sync` reads them; a key in no source exits 3 with nothing sent.
15
- * Then it sends ONE `publishDeployment(type: storefront)` — the server builds
16
- * and ships; the CLI builds nothing — and follows the deployment to its end:
17
- * RUNNING prints its URL and exits 0, FAILED prints its error and exits 1.
18
- * Ctrl-C and `--timeout` end the following, never the publish.
11
+ * `forgecart deploy [name]` (#411): publish what the board serves. The run
12
+ * reads its four credentials through the credential step (launch#62 N3), from
13
+ * the environment, then the storefront's `.env`, then its
14
+ * `.forgecart/config.json`, exactly as `sync` reads them. When a key is in no
15
+ * source, the step authorizes this machine at `/authorize` and writes the four
16
+ * delivered keys into `.env` before any deploy request; when the admin API
17
+ * refuses the credentials, it authorizes the same way and deploys again on the
18
+ * delivered keys. Without a terminal the step prints the URL and the code,
19
+ * waits for the authorization's window, and exits 3. Then the run sends ONE
20
+ * `publishDeployment(type: storefront)` — the server builds and ships; the CLI
21
+ * builds nothing — and follows the deployment to its end: RUNNING prints its
22
+ * URL and exits 0, FAILED prints its error and exits 1. Ctrl-C and `--timeout`
23
+ * end the following, never the publish.
19
24
  */
20
25
  export class DeployRunner {
21
26
  name;
@@ -30,7 +35,11 @@ export class DeployRunner {
30
35
  }
31
36
  async run() {
32
37
  const storefront = this.name ? resolve(process.cwd(), this.name) : process.cwd();
33
- const credentials = await CliCredentialCheck.settle(storefront);
38
+ const step = new CliCredentialStep({ targetDir: storefront });
39
+ await step.run(async (credentials) => this.deploy(credentials));
40
+ }
41
+ /** ONE publish on the settled credentials, followed to its end. */
42
+ async deploy(credentials) {
34
43
  const client = new DeploymentClient(new AdminApiClient(credentials, this.options.timeout));
35
44
  const bound = new DeployFollowBound(this.options.timeout);
36
45
  bound.listen();
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.runner.js","sourceRoot":"","sources":["../../../src/deploy/deploy.runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAErC,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EACL,mBAAmB,GAGpB,MAAM,wBAAwB,CAAC;AAEhC;;;;;;;;;GASG;AACH,MAAM,OAAO,YAAY;IAMJ;IACA;IANnB;;;OAGG;IACH,YACmB,IAAwB,EACxB,OAAsB;QADtB,SAAI,GAAJ,IAAI,CAAoB;QACxB,YAAO,GAAP,OAAO,CAAe;IACtC,CAAC;IAEJ,KAAK,CAAC,GAAG;QACP,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QACjF,MAAM,WAAW,GAAG,MAAM,kBAAkB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAChE,MAAM,MAAM,GAAG,IAAI,gBAAgB,CAAC,IAAI,cAAc,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;QAC3F,MAAM,KAAK,GAAG,IAAI,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC1D,KAAK,CAAC,MAAM,EAAE,CAAC;QACf,IAAI,CAAC;YACH,KAAK,CAAC,oBAAoB,IAAI,CAAC,IAAI,IAAI,GAAG,EAAE,CAAC,CAAC;YAC9C,KAAK,CAAC,+EAA+E,CAAC,CAAC;YACvF,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;YACzC,KAAK,CAAC,UAAU,EAAE,CAAC;YACnB,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,gBAAgB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;QACxE,CAAC;gBAAS,CAAC;YACT,KAAK,CAAC,OAAO,EAAE,CAAC;QAClB,CAAC;IACH,CAAC;IAED,qGAAqG;IAC7F,MAAM,CAAC,KAA2B;QACxC,IAAI,KAAK,CAAC,MAAM,KAAK,mBAAmB,CAAC,MAAM,EAAE,CAAC;YAChD,MAAM,IAAI,UAAU,CAAC,aAAa,CAAC,KAAK,EAAE;gBACxC,0BAA0B,KAAK,CAAC,YAAY,IAAI,yCAAyC,EAAE;aAC5F,CAAC,CAAC;QACL,CAAC;QACD,KAAK,CAAC,gBAAgB,KAAK,CAAC,GAAG,IAAI,wDAAwD,EAAE,CAAC,CAAC;IACjG,CAAC;CACF"}
1
+ {"version":3,"file":"deploy.runner.js","sourceRoot":"","sources":["../../../src/deploy/deploy.runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAE5E,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAErC,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EACL,mBAAmB,GAGpB,MAAM,wBAAwB,CAAC;AAEhC;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,YAAY;IAMJ;IACA;IANnB;;;OAGG;IACH,YACmB,IAAwB,EACxB,OAAsB;QADtB,SAAI,GAAJ,IAAI,CAAoB;QACxB,YAAO,GAAP,OAAO,CAAe;IACtC,CAAC;IAEJ,KAAK,CAAC,GAAG;QACP,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QACjF,MAAM,IAAI,GAAG,IAAI,iBAAiB,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC;QAC9D,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,mEAAmE;IAC3D,KAAK,CAAC,MAAM,CAAC,WAA2B;QAC9C,MAAM,MAAM,GAAG,IAAI,gBAAgB,CAAC,IAAI,cAAc,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;QAC3F,MAAM,KAAK,GAAG,IAAI,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC1D,KAAK,CAAC,MAAM,EAAE,CAAC;QACf,IAAI,CAAC;YACH,KAAK,CAAC,oBAAoB,IAAI,CAAC,IAAI,IAAI,GAAG,EAAE,CAAC,CAAC;YAC9C,KAAK,CAAC,+EAA+E,CAAC,CAAC;YACvF,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;YACzC,KAAK,CAAC,UAAU,EAAE,CAAC;YACnB,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,gBAAgB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;QACxE,CAAC;gBAAS,CAAC;YACT,KAAK,CAAC,OAAO,EAAE,CAAC;QAClB,CAAC;IACH,CAAC;IAED,qGAAqG;IAC7F,MAAM,CAAC,KAA2B;QACxC,IAAI,KAAK,CAAC,MAAM,KAAK,mBAAmB,CAAC,MAAM,EAAE,CAAC;YAChD,MAAM,IAAI,UAAU,CAAC,aAAa,CAAC,KAAK,EAAE;gBACxC,0BAA0B,KAAK,CAAC,YAAY,IAAI,yCAAyC,EAAE;aAC5F,CAAC,CAAC;QACL,CAAC;QACD,KAAK,CAAC,gBAAgB,KAAK,CAAC,GAAG,IAAI,wDAAwD,EAAE,CAAC,CAAC;IACjG,CAAC;CACF"}
@@ -1,36 +1,44 @@
1
- import { type CliCredentials } from '../authorization/type/cli-authorization.types.js';
2
- import type { SyncOutgoingEnv, SyncTakenEnv } from './type/sync.types.js';
1
+ import { type StorefrontEnvFile } from '../util/dotenv.util.js';
2
+ import type { SyncDotenvOutgoing, SyncDotenvTaken } from './type/sync.types.js';
3
3
  /**
4
- * The laptop's `.env`, as `forgecart sync` carries it (#2781). Only merchant
5
- * lines travel — the platform's `FORGECART_*` keys never cross — and they
6
- * travel under init's merge rule: a key is refreshed in place, every other line
7
- * is kept verbatim, nothing is deleted.
4
+ * One of the laptop's env files, as `forgecart sync` carries it (#2781). Only
5
+ * merchant lines travel — the platform's `FORGECART_*` keys never cross — and
6
+ * they travel under init's merge rule: a key is refreshed in place, every other
7
+ * line is kept verbatim, nothing is deleted.
8
8
  *
9
9
  * A push carries the merchant lines changed since they last travelled, so a
10
10
  * key the workspace changed meanwhile is not overwritten by a stale copy; a
11
- * pull takes the workspace's lines for every key the laptop has not changed
12
- * since, beside the run's four credentials — the `.env` a fresh pull writes.
13
- * What travelled is recorded as a digest per key, never the value.
11
+ * pull takes the workspace's lines of the same file for every key the laptop
12
+ * has not changed since. What travelled is recorded as a digest per key, never
13
+ * the value. A `recorded` of null is a first sync — no key counts as changed —
14
+ * and an empty one a file that never travelled.
14
15
  */
15
16
  export declare class SyncDotenvFile {
17
+ readonly file: StorefrontEnvFile;
16
18
  private readonly path;
17
- constructor(storefront: string);
18
- /** The merchant lines a push carries — every one changed since `recorded` — and the digests to record once they land. */
19
- outgoing(recorded: Readonly<Record<string, string>> | null): Promise<SyncOutgoingEnv>;
20
- /** How many of the workspace's merchant lines a pull would take — every key the laptop has not changed since `recorded`. */
21
- takeable(workspaceLines: readonly string[], recorded: Readonly<Record<string, string>> | null): Promise<number>;
19
+ /**
20
+ * @param storefront the storefront directory.
21
+ * @param file the env file this is.
22
+ */
23
+ constructor(storefront: string, file: StorefrontEnvFile);
24
+ /**
25
+ * The merchant lines a push carries from this file — every one changed since
26
+ * `recorded` — and the digests to record once they land; null when the
27
+ * storefront holds no such file.
28
+ */
29
+ outgoing(recorded: Readonly<Record<string, string>> | null): Promise<SyncDotenvOutgoing | null>;
30
+ /** The workspace's merchant lines a pull would take into this file — every key the laptop has not changed since `recorded`. */
31
+ incoming(workspaceLines: readonly string[], recorded: Readonly<Record<string, string>> | null): Promise<string[]>;
22
32
  /**
23
33
  * Take the workspace's merchant lines — for every key the laptop has not
24
- * changed since `recorded` — and the run's four credentials into `.env`,
25
- * under init's merge rule. A key the laptop changed keeps the laptop's value
26
- * and the digest it last travelled with, so the next push still carries it.
34
+ * changed since `recorded` — and the `platform` keys into this file, under
35
+ * init's merge rule; a file the laptop lacks is created. A key the laptop
36
+ * changed keeps the laptop's value and the digest it last travelled with, so
37
+ * the next push still carries it, and a key that did not travel keeps the
38
+ * digest it had — none for a key that never travelled.
27
39
  */
28
- take(workspaceLines: readonly string[], credentials: CliCredentials, recorded: Readonly<Record<string, string>> | null): Promise<SyncTakenEnv>;
40
+ take(workspaceLines: readonly string[], platform: ReadonlyMap<string, string>, recorded: Readonly<Record<string, string>> | null): Promise<SyncDotenvTaken>;
29
41
  /** The keys whose line differs from the one that last travelled — none before a first sync. */
30
42
  private changedSince;
31
- /** The four credentials as `.env` assignments, in the order init writes them. */
32
- private credentialEntries;
33
43
  private digestsOf;
34
- /** The `.env` text — empty when the storefront has none; any other failure propagates. */
35
- private read;
36
44
  }
@@ -1,65 +1,80 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { readFile, writeFile } from 'node:fs/promises';
2
+ import { writeFile } from 'node:fs/promises';
3
3
  import { join } from 'node:path';
4
- import { CliCredentialKey, } from '../authorization/type/cli-authorization.types.js';
5
- import { dotenvValue, mergeDotenv, merchantAssignments } from '../util/dotenv.util.js';
4
+ import { dotenvValue, mergeDotenv, merchantAssignments, readDotenvFile, } from '../util/dotenv.util.js';
6
5
  /**
7
- * The laptop's `.env`, as `forgecart sync` carries it (#2781). Only merchant
8
- * lines travel — the platform's `FORGECART_*` keys never cross — and they
9
- * travel under init's merge rule: a key is refreshed in place, every other line
10
- * is kept verbatim, nothing is deleted.
6
+ * One of the laptop's env files, as `forgecart sync` carries it (#2781). Only
7
+ * merchant lines travel — the platform's `FORGECART_*` keys never cross — and
8
+ * they travel under init's merge rule: a key is refreshed in place, every other
9
+ * line is kept verbatim, nothing is deleted.
11
10
  *
12
11
  * A push carries the merchant lines changed since they last travelled, so a
13
12
  * key the workspace changed meanwhile is not overwritten by a stale copy; a
14
- * pull takes the workspace's lines for every key the laptop has not changed
15
- * since, beside the run's four credentials — the `.env` a fresh pull writes.
16
- * What travelled is recorded as a digest per key, never the value.
13
+ * pull takes the workspace's lines of the same file for every key the laptop
14
+ * has not changed since. What travelled is recorded as a digest per key, never
15
+ * the value. A `recorded` of null is a first sync — no key counts as changed —
16
+ * and an empty one a file that never travelled.
17
17
  */
18
18
  export class SyncDotenvFile {
19
+ file;
19
20
  path;
20
- constructor(storefront) {
21
- this.path = join(storefront, '.env');
21
+ /**
22
+ * @param storefront the storefront directory.
23
+ * @param file the env file this is.
24
+ */
25
+ constructor(storefront, file) {
26
+ this.file = file;
27
+ this.path = join(storefront, file);
22
28
  }
23
- /** The merchant lines a push carries — every one changed since `recorded` — and the digests to record once they land. */
29
+ /**
30
+ * The merchant lines a push carries from this file — every one changed since
31
+ * `recorded` — and the digests to record once they land; null when the
32
+ * storefront holds no such file.
33
+ */
24
34
  async outgoing(recorded) {
25
- const assignments = merchantAssignments((await this.read()).split('\n'));
35
+ const content = await readDotenvFile(this.path);
36
+ if (content === null)
37
+ return null;
38
+ const assignments = merchantAssignments(content.split('\n'));
26
39
  const digests = this.digestsOf(assignments);
27
40
  const lines = [...assignments]
28
41
  .filter(([key]) => recorded?.[key] !== digests[key])
29
42
  .map(([, line]) => line);
30
43
  return { lines, digests };
31
44
  }
32
- /** How many of the workspace's merchant lines a pull would take — every key the laptop has not changed since `recorded`. */
33
- async takeable(workspaceLines, recorded) {
34
- const changed = this.changedSince(await this.read(), recorded);
35
- const keys = [...merchantAssignments(workspaceLines).keys()];
36
- return keys.filter((key) => !changed.has(key)).length;
45
+ /** The workspace's merchant lines a pull would take into this file — every key the laptop has not changed since `recorded`. */
46
+ async incoming(workspaceLines, recorded) {
47
+ const changed = this.changedSince((await readDotenvFile(this.path)) ?? '', recorded);
48
+ return [...merchantAssignments(workspaceLines)]
49
+ .filter(([key]) => !changed.has(key))
50
+ .map(([, line]) => line);
37
51
  }
38
52
  /**
39
53
  * Take the workspace's merchant lines — for every key the laptop has not
40
- * changed since `recorded` — and the run's four credentials into `.env`,
41
- * under init's merge rule. A key the laptop changed keeps the laptop's value
42
- * and the digest it last travelled with, so the next push still carries it.
54
+ * changed since `recorded` — and the `platform` keys into this file, under
55
+ * init's merge rule; a file the laptop lacks is created. A key the laptop
56
+ * changed keeps the laptop's value and the digest it last travelled with, so
57
+ * the next push still carries it, and a key that did not travel keeps the
58
+ * digest it had — none for a key that never travelled.
43
59
  */
44
- async take(workspaceLines, credentials, recorded) {
45
- const existing = await this.read();
60
+ async take(workspaceLines, platform, recorded) {
61
+ const existing = (await readDotenvFile(this.path)) ?? '';
46
62
  const changed = this.changedSince(existing, recorded);
47
- const taken = [...merchantAssignments(workspaceLines)].filter(([key]) => !changed.has(key));
48
- const managed = new Map([
49
- ...this.credentialEntries(credentials),
50
- ...taken.map(([key, line]) => [key, dotenvValue(line, key)]),
51
- ]);
52
- const next = mergeDotenv(existing, managed);
63
+ const taken = new Map([...merchantAssignments(workspaceLines)]
64
+ .filter(([key]) => !changed.has(key))
65
+ .map(([key, line]) => [key, dotenvValue(line, key)]));
66
+ const managed = new Map([...platform, ...taken]);
67
+ const next = managed.size === 0 ? existing : mergeDotenv(existing, managed);
53
68
  if (next !== existing)
54
69
  await writeFile(this.path, next, 'utf8');
55
70
  const merged = this.digestsOf(merchantAssignments(next.split('\n')));
56
71
  const digests = {};
57
72
  for (const [key, digest] of Object.entries(merged)) {
58
- const recordedDigest = changed.has(key) ? recorded?.[key] : digest;
59
- if (recordedDigest !== undefined)
60
- digests[key] = recordedDigest;
73
+ const travelled = taken.has(key) ? digest : recorded?.[key];
74
+ if (travelled !== undefined)
75
+ digests[key] = travelled;
61
76
  }
62
- return { taken: taken.length, digests };
77
+ return { taken: taken.size, digests };
63
78
  }
64
79
  /** The keys whose line differs from the one that last travelled — none before a first sync. */
65
80
  changedSince(content, recorded) {
@@ -68,28 +83,8 @@ export class SyncDotenvFile {
68
83
  const digests = this.digestsOf(merchantAssignments(content.split('\n')));
69
84
  return new Set(Object.keys(digests).filter((key) => recorded[key] !== digests[key]));
70
85
  }
71
- /** The four credentials as `.env` assignments, in the order init writes them. */
72
- credentialEntries(credentials) {
73
- return [
74
- [CliCredentialKey.CHANNEL_TOKEN, credentials.channelToken],
75
- [CliCredentialKey.SHOP_API_URL, credentials.shopApiUrl],
76
- [CliCredentialKey.ADMIN_SECRET, credentials.adminSecret],
77
- [CliCredentialKey.ADMIN_API_URL, credentials.adminApiUrl],
78
- ];
79
- }
80
86
  digestsOf(assignments) {
81
87
  return Object.fromEntries([...assignments].map(([key, line]) => [key, createHash('sha256').update(line).digest('hex')]));
82
88
  }
83
- /** The `.env` text — empty when the storefront has none; any other failure propagates. */
84
- async read() {
85
- try {
86
- return await readFile(this.path, 'utf8');
87
- }
88
- catch (error) {
89
- if (error.code === 'ENOENT')
90
- return '';
91
- throw error;
92
- }
93
- }
94
89
  }
95
90
  //# sourceMappingURL=sync-dotenv.file.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sync-dotenv.file.js","sourceRoot":"","sources":["../../../src/sync/sync-dotenv.file.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sync-dotenv.file.js","sourceRoot":"","sources":["../../../src/sync/sync-dotenv.file.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import { type CliCredentials } from '../authorization/type/cli-authorization.types.js';
2
+ import type { SyncEnvDigests, SyncEnvFileLines, SyncOutgoingEnv, SyncTakenEnv } from './type/sync.types.js';
3
+ /**
4
+ * The laptop's side of `forgecart sync`'s env channel (#2781): EVERY env file a
5
+ * storefront reads — `.env`, `.env.local`, `.env.development`,
6
+ * `.env.development.local`, `.env.production`, `.env.production.local` — carried
7
+ * file for file, each one's merchant lines into the other side's file of the
8
+ * same name, so every key resolves in the workspace as it does on the laptop.
9
+ * The files are never flattened into one, and the platform's `FORGECART_*`
10
+ * keys never cross in any of them. What travelled is recorded per file and per
11
+ * key. A pull also writes the run's four credentials into `.env` — the `.env` a
12
+ * fresh pull writes.
13
+ */
14
+ export declare class SyncEnvChannel {
15
+ private readonly files;
16
+ /** @param storefront the storefront directory. */
17
+ constructor(storefront: string);
18
+ /**
19
+ * The merchant lines a push carries — one entry per env file the laptop
20
+ * holds, its lines changed since `recorded` — and the digests to record once
21
+ * they land.
22
+ */
23
+ outgoing(recorded: SyncEnvDigests | null): Promise<SyncOutgoingEnv>;
24
+ /**
25
+ * The workspace's merchant lines a pull would take — one entry per env file
26
+ * the workspace holds, its lines for every key the laptop has not changed
27
+ * since `recorded`.
28
+ */
29
+ incoming(workspaceFiles: readonly SyncEnvFileLines[], recorded: SyncEnvDigests | null): Promise<SyncEnvFileLines[]>;
30
+ /**
31
+ * Take each workspace env file's merchant lines into the laptop file of the
32
+ * same name — created when the laptop lacks it — and the four credentials
33
+ * into `.env`. A file the workspace does not hold is left as it is, with the
34
+ * digests it last travelled with.
35
+ */
36
+ take(workspaceFiles: readonly SyncEnvFileLines[], credentials: CliCredentials, recorded: SyncEnvDigests | null): Promise<SyncTakenEnv>;
37
+ /** Take into one file — null for a file with neither workspace lines nor credentials to take. */
38
+ private takeInto;
39
+ /**
40
+ * The platform keys a pull writes into `file`: the four credentials in
41
+ * `.env`, in the order init writes them; none elsewhere.
42
+ */
43
+ private platformEntries;
44
+ /** The digests `file` last travelled with: null before a first sync, none for a file that never travelled. */
45
+ private recordOf;
46
+ private linesByFile;
47
+ }
@@ -0,0 +1,106 @@
1
+ import { CliCredentialKey, } from '../authorization/type/cli-authorization.types.js';
2
+ import { STOREFRONT_ENV_FILES, StorefrontEnvFile } from '../util/dotenv.util.js';
3
+ import { SyncDotenvFile } from './sync-dotenv.file.js';
4
+ /**
5
+ * The laptop's side of `forgecart sync`'s env channel (#2781): EVERY env file a
6
+ * storefront reads — `.env`, `.env.local`, `.env.development`,
7
+ * `.env.development.local`, `.env.production`, `.env.production.local` — carried
8
+ * file for file, each one's merchant lines into the other side's file of the
9
+ * same name, so every key resolves in the workspace as it does on the laptop.
10
+ * The files are never flattened into one, and the platform's `FORGECART_*`
11
+ * keys never cross in any of them. What travelled is recorded per file and per
12
+ * key. A pull also writes the run's four credentials into `.env` — the `.env` a
13
+ * fresh pull writes.
14
+ */
15
+ export class SyncEnvChannel {
16
+ files;
17
+ /** @param storefront the storefront directory. */
18
+ constructor(storefront) {
19
+ this.files = STOREFRONT_ENV_FILES.map((file) => new SyncDotenvFile(storefront, file));
20
+ }
21
+ /**
22
+ * The merchant lines a push carries — one entry per env file the laptop
23
+ * holds, its lines changed since `recorded` — and the digests to record once
24
+ * they land.
25
+ */
26
+ async outgoing(recorded) {
27
+ const files = [];
28
+ const digests = {};
29
+ for (const envFile of this.files) {
30
+ const outgoing = await envFile.outgoing(this.recordOf(recorded, envFile.file));
31
+ if (outgoing === null)
32
+ continue;
33
+ files.push({ file: envFile.file, lines: outgoing.lines });
34
+ digests[envFile.file] = outgoing.digests;
35
+ }
36
+ return { files, digests };
37
+ }
38
+ /**
39
+ * The workspace's merchant lines a pull would take — one entry per env file
40
+ * the workspace holds, its lines for every key the laptop has not changed
41
+ * since `recorded`.
42
+ */
43
+ async incoming(workspaceFiles, recorded) {
44
+ const workspace = this.linesByFile(workspaceFiles);
45
+ const files = [];
46
+ for (const envFile of this.files) {
47
+ const workspaceLines = workspace.get(envFile.file);
48
+ if (workspaceLines === undefined)
49
+ continue;
50
+ const lines = await envFile.incoming(workspaceLines, this.recordOf(recorded, envFile.file));
51
+ files.push({ file: envFile.file, lines });
52
+ }
53
+ return files;
54
+ }
55
+ /**
56
+ * Take each workspace env file's merchant lines into the laptop file of the
57
+ * same name — created when the laptop lacks it — and the four credentials
58
+ * into `.env`. A file the workspace does not hold is left as it is, with the
59
+ * digests it last travelled with.
60
+ */
61
+ async take(workspaceFiles, credentials, recorded) {
62
+ const workspace = this.linesByFile(workspaceFiles);
63
+ const digests = {};
64
+ let taken = 0;
65
+ for (const envFile of this.files) {
66
+ const workspaceLines = workspace.get(envFile.file);
67
+ const result = await this.takeInto(envFile, workspaceLines, credentials, recorded);
68
+ const fileDigests = result?.digests ?? recorded?.[envFile.file];
69
+ if (fileDigests !== undefined)
70
+ digests[envFile.file] = fileDigests;
71
+ taken += result?.taken ?? 0;
72
+ }
73
+ return { taken, digests };
74
+ }
75
+ /** Take into one file — null for a file with neither workspace lines nor credentials to take. */
76
+ async takeInto(envFile, workspaceLines, credentials, recorded) {
77
+ const platform = this.platformEntries(envFile.file, credentials);
78
+ if (workspaceLines === undefined && platform.size === 0)
79
+ return null;
80
+ return envFile.take(workspaceLines ?? [], platform, this.recordOf(recorded, envFile.file));
81
+ }
82
+ /**
83
+ * The platform keys a pull writes into `file`: the four credentials in
84
+ * `.env`, in the order init writes them; none elsewhere.
85
+ */
86
+ platformEntries(file, credentials) {
87
+ if (file !== StorefrontEnvFile.BASE)
88
+ return new Map();
89
+ return new Map([
90
+ [CliCredentialKey.CHANNEL_TOKEN, credentials.channelToken],
91
+ [CliCredentialKey.SHOP_API_URL, credentials.shopApiUrl],
92
+ [CliCredentialKey.ADMIN_SECRET, credentials.adminSecret],
93
+ [CliCredentialKey.ADMIN_API_URL, credentials.adminApiUrl],
94
+ ]);
95
+ }
96
+ /** The digests `file` last travelled with: null before a first sync, none for a file that never travelled. */
97
+ recordOf(recorded, file) {
98
+ if (recorded === null)
99
+ return null;
100
+ return recorded[file] ?? {};
101
+ }
102
+ linesByFile(files) {
103
+ return new Map(files.map(({ file, lines }) => [file, lines]));
104
+ }
105
+ }
106
+ //# sourceMappingURL=sync-env-channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-env-channel.js","sourceRoot":"","sources":["../../../src/sync/sync-env-channel.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,16 @@
1
1
  import type { SyncEnvOptions } from './type/sync.types.js';
2
2
  /**
3
3
  * `forgecart sync-env [name]` (#2781) — the workspace's side of `forgecart
4
- * sync`, which the admin API runs in the pod: it reads the merchant lines of the
5
- * storefront's `.env`, or, with `--merge`, merges the lines
6
- * FORGECART_SYNC_ENV_LINES carries under init's merge rule, and prints ONE JSON
7
- * report — the merchant lines as they stand, and how many keys the merge
8
- * carried in. The platform's own `FORGECART_*` keys are never read out and
9
- * never written: a laptop's never land, the pod's never leave.
4
+ * sync`, which the admin API runs in the pod. It reads EVERY env file the
5
+ * storefront holds — `.env`, `.env.local`, `.env.development`,
6
+ * `.env.development.local`, `.env.production`, `.env.production.local` — or,
7
+ * with `--merge`, merges the lines FORGECART_SYNC_ENV_FILES carries for each
8
+ * file into the storefront file of that name under init's merge rule, creating
9
+ * one the storefront lacks. The files stay apart, so the dev-server resolves
10
+ * every key as the laptop does. It prints ONE JSON report: one entry per env
11
+ * file the storefront holds — its merchant lines as they stand, and how many
12
+ * keys the merge carried in. The platform's own `FORGECART_*` keys are never
13
+ * read out and never written: a laptop's never land, the pod's never leave.
10
14
  */
11
15
  export declare class SyncEnvRunner {
12
16
  private readonly name;
@@ -17,13 +21,25 @@ export declare class SyncEnvRunner {
17
21
  */
18
22
  constructor(name: string | undefined, options: SyncEnvOptions);
19
23
  run(): Promise<void>;
20
- /** The merchant keys the variable carries, keyed to their values — platform keys and other lines dropped. */
24
+ /**
25
+ * One env file once the run is done: the merchant keys carried for it merged
26
+ * in — the file created when the storefront lacks it — and its merchant lines
27
+ * as they then stand; null for a file the storefront does not hold.
28
+ */
29
+ private settle;
30
+ /** A file nothing was merged into, as it stands — null when the storefront does not hold it. */
31
+ private held;
32
+ /**
33
+ * The merchant keys a merge carries for each env file, keyed to their values
34
+ * — platform keys and other lines dropped; none for a read.
35
+ */
21
36
  private incoming;
22
- private parseLines;
37
+ private managedOf;
38
+ private parseEntries;
39
+ /** One entry: an env file the storefront reads — never a path outside them — and its lines. */
40
+ private parseEntry;
23
41
  private malformedMessage;
24
42
  private merchantLines;
25
43
  /** The report the admin API parses: ONE line of JSON on stdout. */
26
44
  private report;
27
- /** The `.env` text — empty when the storefront has none; any other failure propagates. */
28
- private read;
29
45
  }