@cimplify/cli 0.15.3 → 0.17.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 (120) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2 -2
  3. package/dist/add-C3DPTVH3.mjs +140 -0
  4. package/dist/{add-KRIIKAS4.mjs → add-JX2GXWXU.mjs} +4 -4
  5. package/dist/ai-HUZTVJIQ.mjs +17 -0
  6. package/dist/{assets-CWB72PZG.mjs → assets-MPESJSUV.mjs} +12 -7
  7. package/dist/{auth-step-up-Z6KOTTV7.mjs → auth-step-up-VJAFGSW6.mjs} +5 -5
  8. package/dist/build-E7RRQWVF.mjs +616 -0
  9. package/dist/{cancel-ZHVVILOU.mjs → cancel-UMKR274A.mjs} +4 -4
  10. package/dist/capture-TK4NIUYM.mjs +69 -0
  11. package/dist/chunk-25ZPFHLF.mjs +51 -0
  12. package/dist/{chunk-OLZKNUMK.mjs → chunk-3LJG22FS.mjs} +6 -6
  13. package/dist/chunk-454RQ6NR.mjs +10614 -0
  14. package/dist/{chunk-O2TO2J4I.mjs → chunk-6CQMRZ2W.mjs} +2 -2
  15. package/dist/chunk-6KKLF5KG.mjs +32 -0
  16. package/dist/chunk-74QZQSCL.mjs +84 -0
  17. package/dist/chunk-BGM7CH5G.mjs +81 -0
  18. package/dist/chunk-BTNVYAC4.mjs +5557 -0
  19. package/dist/{chunk-I2UP7YTG.mjs → chunk-ECGDRU56.mjs} +1 -1
  20. package/dist/{chunk-UTD34C2H.mjs → chunk-EOOOXVHR.mjs} +2 -4
  21. package/dist/chunk-EYEMPIMP.mjs +74 -0
  22. package/dist/{chunk-3RPR33MO.mjs → chunk-FV273ECI.mjs} +11 -7
  23. package/dist/{chunk-MLE4MVYC.mjs → chunk-GGPN4QYQ.mjs} +2 -2
  24. package/dist/chunk-HQPZQIWW.mjs +1456 -0
  25. package/dist/{chunk-3QUK5BPY.mjs → chunk-I7GEHLDD.mjs} +16 -1
  26. package/dist/chunk-ITDAICBY.mjs +363 -0
  27. package/dist/chunk-JKECUAAV.mjs +2169 -0
  28. package/dist/chunk-JNCEKW2Y.mjs +41 -0
  29. package/dist/chunk-JTYVGORV.mjs +101 -0
  30. package/dist/chunk-KQNZKV6E.mjs +535 -0
  31. package/dist/chunk-LJDIQ4PF.mjs +6463 -0
  32. package/dist/{chunk-2MKZKUVH.mjs → chunk-LLRUOIMU.mjs} +1 -2
  33. package/dist/chunk-MJMRIMR7.mjs +86 -0
  34. package/dist/chunk-N2WZJX5P.mjs +1989 -0
  35. package/dist/chunk-NEB372WD.mjs +1152 -0
  36. package/dist/chunk-OPX7KPOU.mjs +68 -0
  37. package/dist/chunk-PHUBMRAS.mjs +623 -0
  38. package/dist/chunk-PQBALZUH.mjs +1941 -0
  39. package/dist/chunk-PVW5D5AW.mjs +42 -0
  40. package/dist/{chunk-LZEG3FEY.mjs → chunk-QQWYQ2HS.mjs} +7 -7
  41. package/dist/chunk-QVR6V6QP.mjs +46 -0
  42. package/dist/chunk-SGT5IJVW.mjs +28 -0
  43. package/dist/chunk-SZXO6YSX.mjs +502 -0
  44. package/dist/chunk-THATLMEX.mjs +1227 -0
  45. package/dist/chunk-TYR4Q37L.mjs +391 -0
  46. package/dist/{chunk-DMLQBAGC.mjs → chunk-V3R2NYQR.mjs} +1 -1
  47. package/dist/chunk-VHK7WBO3.mjs +189 -0
  48. package/dist/{chunk-TH4XENKH.mjs → chunk-XASDB7YS.mjs} +1 -1
  49. package/dist/{chunk-NYXA6X56.mjs → chunk-XBQ4LO2N.mjs} +31 -11
  50. package/dist/chunk-XF3MGADK.mjs +2946 -0
  51. package/dist/{chunk-5KYR6WJB.mjs → chunk-XQAHREKO.mjs} +17 -1
  52. package/dist/chunk-XZG3DTXV.mjs +696 -0
  53. package/dist/chunk-Y4YGR2NY.mjs +52 -0
  54. package/dist/{chunk-LJEKKFC7.mjs → chunk-YS2JUNSQ.mjs} +19 -11
  55. package/dist/chunk-ZG7NJLXN.mjs +104 -0
  56. package/dist/create-3TGG7B3X.mjs +10 -0
  57. package/dist/credits-MEG7V5DI.mjs +33 -0
  58. package/dist/{deploy-STWZ3OSM.mjs → deploy-CNRORHJ7.mjs} +11 -11
  59. package/dist/dev-RXSBY7U4.mjs +8 -0
  60. package/dist/dev-YLYC6B7R.mjs +401 -0
  61. package/dist/dispatcher.mjs +131 -30
  62. package/dist/{doctor-3RW3OGZJ.mjs → doctor-XRTNG2IT.mjs} +13 -21
  63. package/dist/{domains-FTNK4IKY.mjs → domains-JIHFKDWR.mjs} +6 -6
  64. package/dist/engine-runtime-fetch-QVKJVC3W.mjs +10 -0
  65. package/dist/{env-TNCYUAMT.mjs → env-LJ3DCAA5.mjs} +7 -7
  66. package/dist/env-R72AHBT2.mjs +19 -0
  67. package/dist/explain-32RSXEXC.mjs +251 -0
  68. package/dist/inspect-PYLYN45K.mjs +86 -0
  69. package/dist/{inspect-X3M7WSTL.mjs → inspect-ZOLZHNSZ.mjs} +28 -13
  70. package/dist/introspect-27MMTACH.mjs +9 -0
  71. package/dist/kernel-IC4ZC54Z.mjs +662 -0
  72. package/dist/kernel-plugin.js +6862 -0
  73. package/dist/keys-WKOQ4KQE.mjs +71 -0
  74. package/dist/lines-I6ZB2IOU.mjs +56 -0
  75. package/dist/{link-76Z6V6CJ.mjs → link-TE2FUX74.mjs} +6 -6
  76. package/dist/{lint-DDZ7HUQN.mjs → lint-735IPMC6.mjs} +2 -2
  77. package/dist/{list-2MFOQKKQ.mjs → list-CHYY2P4T.mjs} +7 -11
  78. package/dist/lock-XVR3RRZ6.mjs +57 -0
  79. package/dist/{login-RLMDEWAL.mjs → login-LW7CHZDZ.mjs} +10 -8
  80. package/dist/{logout-Z57X6HPE.mjs → logout-6U6OP5DM.mjs} +2 -2
  81. package/dist/{logs-TNDM4RFE.mjs → logs-HKH4H6PT.mjs} +5 -5
  82. package/dist/logs-SCQC5HNO.mjs +44 -0
  83. package/dist/mcp-VRHQOEGG.mjs +1077 -0
  84. package/dist/{mock-EVER34IX.mjs → mock-5H3CVPHC.mjs} +2 -2
  85. package/dist/outdated-T7QW6F3Z.mjs +58 -0
  86. package/dist/port-24R7ZNP4.mjs +2408 -0
  87. package/dist/{projects-YL4T24VV.mjs → projects-HYITVEXS.mjs} +6 -6
  88. package/dist/publish-DZRR5A5X.mjs +20 -0
  89. package/dist/remix-4HVXRIEB.mjs +125 -0
  90. package/dist/repo-FG3CMZQS.mjs +9 -0
  91. package/dist/report-M7F4ZYP7.mjs +2 -0
  92. package/dist/rollback-H7Q4XYGX.mjs +43 -0
  93. package/dist/{rollback-47VQ4HJK.mjs → rollback-OM64O67F.mjs} +6 -6
  94. package/dist/scene-QTHT3ZPL.mjs +324 -0
  95. package/dist/{status-TWPOMWNA.mjs → status-5D4RR3MM.mjs} +5 -5
  96. package/dist/status-AKI6E3JK.mjs +18 -0
  97. package/dist/test-W6MURUGZ.mjs +233 -0
  98. package/dist/testers-EI32JB5F.mjs +49 -0
  99. package/dist/{unlink-H3BO3DOQ.mjs → unlink-BND47IWW.mjs} +2 -2
  100. package/dist/{update-FLY46XI3.mjs → update-XKKFREKH.mjs} +4 -4
  101. package/dist/{whoami-CQPPARRS.mjs → whoami-WHLGCVRC.mjs} +3 -3
  102. package/dist/workshop-F2T4IDOU.mjs +1703 -0
  103. package/package.json +11 -7
  104. package/templates/storefront-auto/app/products/[slug]/page.tsx +1 -1
  105. package/templates/storefront-auto/package.json +1 -1
  106. package/templates/storefront-bakery/package.json +1 -1
  107. package/templates/storefront-fashion/app/products/[slug]/page.tsx +1 -1
  108. package/templates/storefront-fashion/package.json +1 -1
  109. package/templates/storefront-grocery/package.json +1 -1
  110. package/templates/storefront-pharmacy/app/products/[slug]/page.tsx +1 -1
  111. package/templates/storefront-pharmacy/package.json +1 -1
  112. package/templates/storefront-restaurant/package.json +1 -1
  113. package/templates/storefront-retail/app/products/[slug]/page.tsx +1 -1
  114. package/templates/storefront-retail/package.json +1 -1
  115. package/templates/storefront-services/package.json +1 -1
  116. package/dist/chunk-ARIDYKLO.mjs +0 -6463
  117. package/dist/dev-7O65HY6C.mjs +0 -8
  118. package/dist/explain-DLRUUCNS.mjs +0 -251
  119. package/dist/introspect-RC265ACF.mjs +0 -9
  120. package/dist/repo-UUT3AZ3L.mjs +0 -9
@@ -65,12 +65,14 @@ var CliError = class extends Error {
65
65
  code;
66
66
  exitCode;
67
67
  remediation;
68
+ details;
68
69
  constructor(code, message, options = {}) {
69
70
  super(message);
70
71
  this.name = "CliError";
71
72
  this.code = code;
72
73
  this.exitCode = options.exitCode ?? EXIT_CODE_FOR[code];
73
74
  this.remediation = options.remediation;
75
+ this.details = options.details;
74
76
  }
75
77
  };
76
78
  function isCliError(err) {
@@ -85,9 +87,12 @@ var RED_OPEN = "\x1B[31m";
85
87
  var GREEN_OPEN = "\x1B[32m";
86
88
  var YELLOW_OPEN = "\x1B[33m";
87
89
  var CYAN_OPEN = "\x1B[36m";
90
+ var HONEY_OPEN = "\x1B[38;5;214m";
91
+ var ORANGE_OPEN = "\x1B[38;5;208m";
88
92
  var PREFIX_STEP = "\u25B8";
89
93
  var PREFIX_SUCCESS = "\u2713";
90
94
  var PREFIX_FAILURE = "\u2717";
95
+ var PREFIX_WARN = "!";
91
96
  var OUTPUT_MODE = {
92
97
  HUMAN: "human",
93
98
  JSON: "json"
@@ -138,6 +143,12 @@ function yellow(value) {
138
143
  function cyan(value) {
139
144
  return isJsonMode() ? value : wrap(CYAN_OPEN, value);
140
145
  }
146
+ function honey(value) {
147
+ return isJsonMode() ? value : wrap(HONEY_OPEN, value);
148
+ }
149
+ function orange(value) {
150
+ return isJsonMode() ? value : wrap(ORANGE_OPEN, value);
151
+ }
141
152
  function step(message) {
142
153
  if (isJsonMode()) return;
143
154
  console.log(`${cyan(PREFIX_STEP)} ${message}`);
@@ -150,6 +161,10 @@ function failure(message) {
150
161
  if (isJsonMode()) return;
151
162
  console.error(`${red(PREFIX_FAILURE)} ${message}`);
152
163
  }
164
+ function warn(message) {
165
+ if (isJsonMode()) return;
166
+ console.error(`${orange(PREFIX_WARN)} ${message}`);
167
+ }
153
168
  function info(message) {
154
169
  if (isJsonMode()) return;
155
170
  console.log(message);
@@ -163,4 +178,4 @@ function result(data) {
163
178
  `);
164
179
  }
165
180
 
166
- export { CLI_ERROR_CODE, CliError, EXIT_CODE, OUTPUT_MODE, bold, configureOutput, dim, failure, green, info, isAutoYes, isCliError, isInteractive, isJsonMode, red, result, step, success, yellow };
181
+ export { CLI_ERROR_CODE, CliError, EXIT_CODE, OUTPUT_MODE, bold, configureOutput, dim, failure, green, honey, info, isAutoYes, isCliError, isInteractive, isJsonMode, orange, red, result, step, success, warn, yellow };
@@ -0,0 +1,363 @@
1
+ #!/usr/bin/env node
2
+ import { LOCAL_ENVIRONMENT, resetLocalData, localEnvironments, localDataDir } from './chunk-NEB372WD.mjs';
3
+ import { readInteractiveId, remoteCall, CHANNELS } from './chunk-JNCEKW2Y.mjs';
4
+ import { requireInteractiveProject } from './chunk-BTNVYAC4.mjs';
5
+ import { flagBool, flagString, parseArgs } from './chunk-XQAHREKO.mjs';
6
+ import { ApiClient } from './chunk-XBQ4LO2N.mjs';
7
+ import { readAuthOrNull } from './chunk-GGPN4QYQ.mjs';
8
+ import { bold, dim, info, success, warn, result, CliError, CLI_ERROR_CODE } from './chunk-I7GEHLDD.mjs';
9
+
10
+ // src/interactives/remote/environments.ts
11
+ var BASE = "/v1/interactives";
12
+ function segment(value) {
13
+ return encodeURIComponent(value);
14
+ }
15
+ function seedBody(seed) {
16
+ return { from: seed.from, at: seed.at, tables: [...seed.tables], kv_namespaces: [...seed.kvNamespaces] };
17
+ }
18
+ function change(body) {
19
+ return { change: body.change, table: body.table, column: body.column ?? null, index: body.index ?? null };
20
+ }
21
+ function environment(body) {
22
+ return {
23
+ name: body.name,
24
+ kind: body.kind,
25
+ status: body.status,
26
+ revision: body.revision_id,
27
+ previewToken: body.preview_token,
28
+ seed: body.seed === null ? null : { from: body.seed.from, at: body.seed.at, tables: body.seed.tables, kvNamespaces: body.seed.kv_namespaces },
29
+ schema: body.schema === null ? null : {
30
+ appliedVersion: body.schema.applied_version,
31
+ declaredVersion: body.schema.declared_version,
32
+ pending: body.schema.pending,
33
+ destructive: body.schema.destructive,
34
+ blocked: body.schema.blocked
35
+ },
36
+ createdAt: body.created_at,
37
+ lastActiveAt: body.last_active_at,
38
+ expiresAt: body.expires_at,
39
+ dataRollback: body.data_rollback
40
+ };
41
+ }
42
+ function diff(body) {
43
+ return {
44
+ environment: body.environment,
45
+ revision: body.revision_id,
46
+ fromVersion: body.from_version,
47
+ toVersion: body.to_version,
48
+ changes: body.changes.map(change),
49
+ destructive: body.destructive,
50
+ blocked: body.blocked,
51
+ reversible: body.reversible,
52
+ statements: body.statements
53
+ };
54
+ }
55
+ function migration(body) {
56
+ return {
57
+ id: body.id,
58
+ environment: body.environment,
59
+ revision: body.revision_id,
60
+ fromVersion: body.from_version,
61
+ toVersion: body.to_version,
62
+ changes: body.changes.map(change),
63
+ statements: body.statements,
64
+ reversible: body.reversible,
65
+ status: body.status,
66
+ appliedBy: body.applied_by,
67
+ appliedAt: body.applied_at,
68
+ rolledBackAt: body.rolled_back_at
69
+ };
70
+ }
71
+ var HttpEnvironments = class {
72
+ constructor(client) {
73
+ this.client = client;
74
+ }
75
+ client;
76
+ path(interactiveId, rest = "") {
77
+ return `${BASE}/${segment(interactiveId)}/environments${rest}`;
78
+ }
79
+ async list(interactiveId) {
80
+ return (await this.client.get(this.path(interactiveId))).environments.map(environment);
81
+ }
82
+ async get(interactiveId, name) {
83
+ return environment(await this.client.get(this.path(interactiveId, `/${segment(name)}`)));
84
+ }
85
+ async create(interactiveId, request) {
86
+ return environment(
87
+ await this.client.post(this.path(interactiveId), {
88
+ name: request.name,
89
+ revision_id: request.revision,
90
+ seed: request.seed === null ? null : seedBody(request.seed)
91
+ })
92
+ );
93
+ }
94
+ async remove(interactiveId, name) {
95
+ await this.client.delete(this.path(interactiveId, `/${segment(name)}`));
96
+ }
97
+ async deploy(interactiveId, name, revision) {
98
+ return environment(await this.client.put(this.path(interactiveId, `/${segment(name)}/revision`), { revision_id: revision }));
99
+ }
100
+ async diff(interactiveId, name, to) {
101
+ const query = to === null ? "" : `?to=${to}`;
102
+ return diff(await this.client.get(this.path(interactiveId, `/${segment(name)}/diff${query}`)));
103
+ }
104
+ async migrate(interactiveId, name, confirmDestructive) {
105
+ const body = await this.client.post(this.path(interactiveId, `/${segment(name)}/migrate`), { confirm_destructive: confirmDestructive });
106
+ return body === null ? null : migration(body);
107
+ }
108
+ async promote(interactiveId, name, to, confirmDestructive) {
109
+ const body = await this.client.post(this.path(interactiveId, `/${segment(name)}/promote`), {
110
+ to,
111
+ confirm_destructive: confirmDestructive
112
+ });
113
+ return { revision: body.revision_id, to: body.to, migrations: body.migrations.map(migration) };
114
+ }
115
+ async seed(interactiveId, seed) {
116
+ return environment(await this.client.post(this.path(interactiveId, "/test/seed"), seedBody(seed)));
117
+ }
118
+ async rollback(interactiveId, name, to) {
119
+ const body = await this.client.post(
120
+ this.path(interactiveId, `/${segment(name)}/rollback`),
121
+ { to }
122
+ );
123
+ return { environment: body.environment, to: body.to, revision: body.revision_id, migrationsRolledBack: body.migrations_rolled_back };
124
+ }
125
+ async migrations(interactiveId, limit) {
126
+ return (await this.client.get(`${BASE}/${segment(interactiveId)}/migrations?limit=${limit}`)).map(migration);
127
+ }
128
+ async rollbackMigration(interactiveId, migrationId, confirm) {
129
+ return migration(await this.client.post(`${BASE}/${segment(interactiveId)}/migrations/${segment(migrationId)}/rollback`, { confirm }));
130
+ }
131
+ };
132
+ async function environmentsFromEnv() {
133
+ const auth = await readAuthOrNull();
134
+ if (auth === null) {
135
+ throw new CliError(CLI_ERROR_CODE.NOT_LOGGED_IN, "environments live on the Cimplify registry and need a signed-in account", {
136
+ remediation: "Run `cimplify login`. Local data for `cimplify dev` needs no account: `cimplify env list --local`."
137
+ });
138
+ }
139
+ return new HttpEnvironments(ApiClient.fromAuth(auth));
140
+ }
141
+
142
+ // src/interactives/commands/env.ts
143
+ var USAGE = `usage:
144
+ cimplify env list [--local]
145
+ cimplify env create <name> [--revision <r>] [--seed-from live|test --seed-tables a,b --seed-kv a,b --seed-at <time>]
146
+ cimplify env delete <name>
147
+ cimplify env deploy <name> <revision>
148
+ cimplify env diff <name> [--to test|live]
149
+ cimplify env migrate <name> [--confirm]
150
+ cimplify env promote <name> --to test|live [--confirm]
151
+ cimplify env seed test --from live --tables a,b [--kv a,b] [--at <time>]
152
+ cimplify env rollback <name> --to <time>
153
+ cimplify env rollback --migration <id> [--confirm]
154
+ cimplify env migrations [--limit <n>]
155
+ cimplify env reset [<name>]`;
156
+ var NAME_PATTERN = /^[a-z](?:[a-z0-9-]{0,38}[a-z0-9])?$/;
157
+ var RESERVED = /* @__PURE__ */ new Set(["live", "test"]);
158
+ var DEFAULT_MIGRATIONS = 20;
159
+ function usage(detail) {
160
+ throw new CliError(CLI_ERROR_CODE.INVALID_INPUT, detail === void 0 ? USAGE : `${detail}
161
+ ${USAGE}`);
162
+ }
163
+ function localEnvironmentName(raw) {
164
+ if (raw === void 0) return LOCAL_ENVIRONMENT;
165
+ if (raw !== LOCAL_ENVIRONMENT && !NAME_PATTERN.test(raw)) usage(`'${raw}' is not an environment name: lowercase letters, digits and dashes, starting with a letter`);
166
+ return raw;
167
+ }
168
+ function environmentName(raw, allowChannels) {
169
+ if (raw === void 0) usage();
170
+ if (RESERVED.has(raw)) {
171
+ if (allowChannels) return raw;
172
+ usage(`'${raw}' is a channel; branch environments take another name`);
173
+ }
174
+ if (!NAME_PATTERN.test(raw) || raw === LOCAL_ENVIRONMENT) usage(`'${raw}' is not an environment name: lowercase letters, digits and dashes, starting with a letter`);
175
+ return raw;
176
+ }
177
+ function channel(raw, flag) {
178
+ if (raw === void 0 || !CHANNELS.includes(raw)) usage(`--${flag} must be live or test`);
179
+ return raw;
180
+ }
181
+ function list(raw) {
182
+ if (raw === void 0) return [];
183
+ return raw.split(",").map((part) => part.trim()).filter((part) => part.length > 0);
184
+ }
185
+ function time(raw, flag) {
186
+ if (raw === void 0) usage(`--${flag} needs a time, for example 2026-10-14T09:30:00Z`);
187
+ const parsed = new Date(raw);
188
+ if (Number.isNaN(parsed.getTime())) usage(`--${flag} is not a time: '${raw}'`);
189
+ return parsed.toISOString();
190
+ }
191
+ function seedFrom(args, prefix) {
192
+ const from = flagString(args, `${prefix}from`);
193
+ if (from === void 0) return null;
194
+ const at = flagString(args, `${prefix}at`);
195
+ return {
196
+ from: channel(from, `${prefix}from`),
197
+ at: at === void 0 ? null : time(at, `${prefix}at`),
198
+ tables: list(flagString(args, `${prefix}tables`)),
199
+ kvNamespaces: list(flagString(args, `${prefix}kv`))
200
+ };
201
+ }
202
+ function parseEnvCommand(args) {
203
+ const [action, first, second] = args.positional;
204
+ const confirm = flagBool(args, "confirm") || flagBool(args, "yes");
205
+ switch (action) {
206
+ case "list":
207
+ case "ls":
208
+ return { action: "list", local: flagBool(args, "local") };
209
+ case "create":
210
+ return { action: "create", name: environmentName(first, false), revision: flagString(args, "revision") ?? null, seed: seedFrom(args, "seed-") };
211
+ case "delete":
212
+ case "rm":
213
+ return { action: "delete", name: environmentName(first, false) };
214
+ case "deploy":
215
+ if (second === void 0) usage();
216
+ return { action: "deploy", name: environmentName(first, false), revision: second };
217
+ case "diff": {
218
+ const to = flagString(args, "to");
219
+ return { action: "diff", name: environmentName(first, true), to: to === void 0 ? null : channel(to, "to") };
220
+ }
221
+ case "migrate":
222
+ return { action: "migrate", name: environmentName(first, true), confirm };
223
+ case "promote":
224
+ return { action: "promote", name: environmentName(first, true), to: channel(flagString(args, "to"), "to"), confirm };
225
+ case "seed": {
226
+ if (first !== "test") usage("only test is seeded; a branch takes --seed-from when it is created");
227
+ const seed = seedFrom(args, "");
228
+ if (seed === null) usage("seed needs --from live");
229
+ return { action: "seed", seed };
230
+ }
231
+ case "rollback": {
232
+ const migration2 = flagString(args, "migration");
233
+ if (migration2 !== void 0) return { action: "undo", migration: migration2, confirm };
234
+ return { action: "rollback", name: environmentName(first, true), to: time(flagString(args, "to"), "to") };
235
+ }
236
+ case "migrations": {
237
+ const limit = Number(flagString(args, "limit") ?? DEFAULT_MIGRATIONS);
238
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) usage("--limit is 1 to 100");
239
+ return { action: "migrations", limit };
240
+ }
241
+ case "reset":
242
+ return { action: "reset", name: first === void 0 ? null : localEnvironmentName(first) };
243
+ default:
244
+ return usage();
245
+ }
246
+ }
247
+ async function runEnvCommand(root, remote, command) {
248
+ if (command.action === "reset") return { kind: "reset", removed: await resetLocalData(root, command.name) };
249
+ if (command.action === "list" && command.local) {
250
+ return { kind: "local", environments: localEnvironments(root).map((name) => ({ name, dir: localDataDir(root, name) })) };
251
+ }
252
+ const id = await readInteractiveId(root);
253
+ const environments = await remote();
254
+ switch (command.action) {
255
+ case "list":
256
+ return { kind: "environments", environments: await remoteCall(() => environments.list(id)) };
257
+ case "create":
258
+ return { kind: "environment", environment: await remoteCall(() => environments.create(id, command)) };
259
+ case "delete":
260
+ await remoteCall(() => environments.remove(id, command.name));
261
+ return { kind: "deleted", name: command.name };
262
+ case "deploy":
263
+ return { kind: "environment", environment: await remoteCall(() => environments.deploy(id, command.name, command.revision)) };
264
+ case "diff":
265
+ return { kind: "diff", diff: await remoteCall(() => environments.diff(id, command.name, command.to)) };
266
+ case "migrate": {
267
+ const planned = await remoteCall(() => environments.diff(id, command.name, null));
268
+ if (planned.destructive && !command.confirm) return { kind: "diff", diff: planned };
269
+ return { kind: "migration", migration: await remoteCall(() => environments.migrate(id, command.name, command.confirm)) };
270
+ }
271
+ case "promote": {
272
+ const planned = await remoteCall(() => environments.diff(id, command.name, command.to));
273
+ if (planned.destructive && !command.confirm) return { kind: "diff", diff: planned };
274
+ const promoted = await remoteCall(() => environments.promote(id, command.name, command.to, command.confirm));
275
+ return { kind: "promoted", revision: promoted.revision, to: promoted.to, migrations: promoted.migrations };
276
+ }
277
+ case "seed":
278
+ return { kind: "environment", environment: await remoteCall(() => environments.seed(id, command.seed)) };
279
+ case "rollback": {
280
+ const rolled = await remoteCall(() => environments.rollback(id, command.name, command.to));
281
+ return { kind: "rolled_back", environment: rolled.environment, to: rolled.to, revision: rolled.revision, migrations: rolled.migrationsRolledBack };
282
+ }
283
+ case "undo":
284
+ return { kind: "migration", migration: await remoteCall(() => environments.rollbackMigration(id, command.migration, command.confirm)) };
285
+ case "migrations":
286
+ return { kind: "migrations", migrations: await remoteCall(() => environments.migrations(id, command.limit)) };
287
+ }
288
+ }
289
+ function renderDiff(diff2) {
290
+ const lines = [`${bold(diff2.environment)} schema v${diff2.fromVersion} -> v${diff2.toVersion}${diff2.revision === null ? "" : dim(` (${diff2.revision})`)}`];
291
+ if (diff2.changes.length === 0) lines.push(dim(" no schema changes"));
292
+ for (const c of diff2.changes) {
293
+ const target = [c.table, c.column ?? c.index].filter((part) => part !== null).join(".");
294
+ const mark = c.change.startsWith("drop") || c.change === "retype_column" ? "-" : c.change === "rekey_table" || c.change === "add_required_column" ? "!" : "+";
295
+ lines.push(` ${mark} ${c.change.replaceAll("_", " ")} ${target}`);
296
+ }
297
+ for (const s of diff2.statements) lines.push(dim(` ${s}`));
298
+ if (diff2.blocked) lines.push(" blocked: a table changes its key or gains a required column; declare a new table or a nullable column");
299
+ else if (diff2.destructive) lines.push(" destructive: drops data; rerun with --confirm to apply");
300
+ else if (diff2.changes.length > 0) lines.push(dim(diff2.reversible ? " additive: can be rolled back with env rollback --migration" : " cannot be rolled back"));
301
+ return lines;
302
+ }
303
+ function renderEnvironment(e) {
304
+ const schema = e.schema === null ? dim("schema unknown") : `schema v${e.schema.appliedVersion}${e.schema.pending ? ` (v${e.schema.declaredVersion} pending${e.schema.destructive ? ", destructive" : ""})` : ""}`;
305
+ const preview = e.previewToken === null ? "" : ` preview ${e.previewToken}`;
306
+ const seed = e.seed === null ? "" : dim(` seeded from ${e.seed.from} at ${e.seed.at}`);
307
+ const expiry = e.expiresAt === null ? "" : dim(` idle expiry ${e.expiresAt}`);
308
+ return `${bold(e.name.padEnd(16))} ${e.kind.padEnd(6)} ${e.revision ?? dim("no revision")} ${schema}${preview}${seed}${expiry}`;
309
+ }
310
+ function renderMigration(m) {
311
+ return `${m.id} ${m.environment} v${m.fromVersion} -> v${m.toVersion} ${m.status}${m.reversible ? "" : dim(" (not reversible)")} ${dim(m.appliedAt)}`;
312
+ }
313
+ function renderEnvOutcome(outcome) {
314
+ switch (outcome.kind) {
315
+ case "environments":
316
+ for (const e of outcome.environments) info(renderEnvironment(e));
317
+ return;
318
+ case "local":
319
+ if (outcome.environments.length === 0) info(dim("no local data yet; cimplify dev creates it on first use"));
320
+ for (const e of outcome.environments) info(`${bold(e.name.padEnd(16))} ${dim(e.dir)}`);
321
+ return;
322
+ case "environment":
323
+ success(renderEnvironment(outcome.environment));
324
+ return;
325
+ case "deleted":
326
+ success(`${outcome.name} deleted with its data and preview link`);
327
+ return;
328
+ case "diff":
329
+ for (const line of renderDiff(outcome.diff)) info(line);
330
+ if (outcome.diff.destructive && !outcome.diff.blocked) warn("nothing was applied");
331
+ return;
332
+ case "migration":
333
+ if (outcome.migration === null) success("data already holds the declared schema");
334
+ else success(renderMigration(outcome.migration));
335
+ return;
336
+ case "migrations":
337
+ if (outcome.migrations.length === 0) info(dim("no migrations recorded"));
338
+ for (const m of outcome.migrations) info(renderMigration(m));
339
+ return;
340
+ case "promoted":
341
+ success(`${outcome.revision} is on ${outcome.to}`);
342
+ for (const m of outcome.migrations) info(` ${renderMigration(m)}`);
343
+ return;
344
+ case "rolled_back":
345
+ success(`${outcome.environment} code and data are back at ${outcome.to}${outcome.revision === null ? "" : ` on ${outcome.revision}`}`);
346
+ if (outcome.migrations.length > 0) info(dim(` migrations rolled back: ${outcome.migrations.join(", ")}`));
347
+ return;
348
+ case "reset":
349
+ if (outcome.removed.length === 0) info(dim("no local data to reset"));
350
+ for (const name of outcome.removed) success(`local ${name} data reset`);
351
+ return;
352
+ }
353
+ }
354
+ async function run(argv) {
355
+ const command = parseEnvCommand(parseArgs(argv));
356
+ const root = process.cwd();
357
+ requireInteractiveProject(root, "env");
358
+ const outcome = await runEnvCommand(root, environmentsFromEnv, command);
359
+ renderEnvOutcome(outcome);
360
+ result(outcome);
361
+ }
362
+
363
+ export { localEnvironmentName, parseEnvCommand, renderDiff, renderEnvOutcome, run, runEnvCommand };