@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.
- package/LICENSE +21 -0
- package/README.md +2 -2
- package/dist/add-C3DPTVH3.mjs +140 -0
- package/dist/{add-KRIIKAS4.mjs → add-JX2GXWXU.mjs} +4 -4
- package/dist/ai-HUZTVJIQ.mjs +17 -0
- package/dist/{assets-CWB72PZG.mjs → assets-MPESJSUV.mjs} +12 -7
- package/dist/{auth-step-up-Z6KOTTV7.mjs → auth-step-up-VJAFGSW6.mjs} +5 -5
- package/dist/build-E7RRQWVF.mjs +616 -0
- package/dist/{cancel-ZHVVILOU.mjs → cancel-UMKR274A.mjs} +4 -4
- package/dist/capture-TK4NIUYM.mjs +69 -0
- package/dist/chunk-25ZPFHLF.mjs +51 -0
- package/dist/{chunk-OLZKNUMK.mjs → chunk-3LJG22FS.mjs} +6 -6
- package/dist/chunk-454RQ6NR.mjs +10614 -0
- package/dist/{chunk-O2TO2J4I.mjs → chunk-6CQMRZ2W.mjs} +2 -2
- package/dist/chunk-6KKLF5KG.mjs +32 -0
- package/dist/chunk-74QZQSCL.mjs +84 -0
- package/dist/chunk-BGM7CH5G.mjs +81 -0
- package/dist/chunk-BTNVYAC4.mjs +5557 -0
- package/dist/{chunk-I2UP7YTG.mjs → chunk-ECGDRU56.mjs} +1 -1
- package/dist/{chunk-UTD34C2H.mjs → chunk-EOOOXVHR.mjs} +2 -4
- package/dist/chunk-EYEMPIMP.mjs +74 -0
- package/dist/{chunk-3RPR33MO.mjs → chunk-FV273ECI.mjs} +11 -7
- package/dist/{chunk-MLE4MVYC.mjs → chunk-GGPN4QYQ.mjs} +2 -2
- package/dist/chunk-HQPZQIWW.mjs +1456 -0
- package/dist/{chunk-3QUK5BPY.mjs → chunk-I7GEHLDD.mjs} +16 -1
- package/dist/chunk-ITDAICBY.mjs +363 -0
- package/dist/chunk-JKECUAAV.mjs +2169 -0
- package/dist/chunk-JNCEKW2Y.mjs +41 -0
- package/dist/chunk-JTYVGORV.mjs +101 -0
- package/dist/chunk-KQNZKV6E.mjs +535 -0
- package/dist/chunk-LJDIQ4PF.mjs +6463 -0
- package/dist/{chunk-2MKZKUVH.mjs → chunk-LLRUOIMU.mjs} +1 -2
- package/dist/chunk-MJMRIMR7.mjs +86 -0
- package/dist/chunk-N2WZJX5P.mjs +1989 -0
- package/dist/chunk-NEB372WD.mjs +1152 -0
- package/dist/chunk-OPX7KPOU.mjs +68 -0
- package/dist/chunk-PHUBMRAS.mjs +623 -0
- package/dist/chunk-PQBALZUH.mjs +1941 -0
- package/dist/chunk-PVW5D5AW.mjs +42 -0
- package/dist/{chunk-LZEG3FEY.mjs → chunk-QQWYQ2HS.mjs} +7 -7
- package/dist/chunk-QVR6V6QP.mjs +46 -0
- package/dist/chunk-SGT5IJVW.mjs +28 -0
- package/dist/chunk-SZXO6YSX.mjs +502 -0
- package/dist/chunk-THATLMEX.mjs +1227 -0
- package/dist/chunk-TYR4Q37L.mjs +391 -0
- package/dist/{chunk-DMLQBAGC.mjs → chunk-V3R2NYQR.mjs} +1 -1
- package/dist/chunk-VHK7WBO3.mjs +189 -0
- package/dist/{chunk-TH4XENKH.mjs → chunk-XASDB7YS.mjs} +1 -1
- package/dist/{chunk-NYXA6X56.mjs → chunk-XBQ4LO2N.mjs} +31 -11
- package/dist/chunk-XF3MGADK.mjs +2946 -0
- package/dist/{chunk-5KYR6WJB.mjs → chunk-XQAHREKO.mjs} +17 -1
- package/dist/chunk-XZG3DTXV.mjs +696 -0
- package/dist/chunk-Y4YGR2NY.mjs +52 -0
- package/dist/{chunk-LJEKKFC7.mjs → chunk-YS2JUNSQ.mjs} +19 -11
- package/dist/chunk-ZG7NJLXN.mjs +104 -0
- package/dist/create-3TGG7B3X.mjs +10 -0
- package/dist/credits-MEG7V5DI.mjs +33 -0
- package/dist/{deploy-STWZ3OSM.mjs → deploy-CNRORHJ7.mjs} +11 -11
- package/dist/dev-RXSBY7U4.mjs +8 -0
- package/dist/dev-YLYC6B7R.mjs +401 -0
- package/dist/dispatcher.mjs +131 -30
- package/dist/{doctor-3RW3OGZJ.mjs → doctor-XRTNG2IT.mjs} +13 -21
- package/dist/{domains-FTNK4IKY.mjs → domains-JIHFKDWR.mjs} +6 -6
- package/dist/engine-runtime-fetch-QVKJVC3W.mjs +10 -0
- package/dist/{env-TNCYUAMT.mjs → env-LJ3DCAA5.mjs} +7 -7
- package/dist/env-R72AHBT2.mjs +19 -0
- package/dist/explain-32RSXEXC.mjs +251 -0
- package/dist/inspect-PYLYN45K.mjs +86 -0
- package/dist/{inspect-X3M7WSTL.mjs → inspect-ZOLZHNSZ.mjs} +28 -13
- package/dist/introspect-27MMTACH.mjs +9 -0
- package/dist/kernel-IC4ZC54Z.mjs +662 -0
- package/dist/kernel-plugin.js +6862 -0
- package/dist/keys-WKOQ4KQE.mjs +71 -0
- package/dist/lines-I6ZB2IOU.mjs +56 -0
- package/dist/{link-76Z6V6CJ.mjs → link-TE2FUX74.mjs} +6 -6
- package/dist/{lint-DDZ7HUQN.mjs → lint-735IPMC6.mjs} +2 -2
- package/dist/{list-2MFOQKKQ.mjs → list-CHYY2P4T.mjs} +7 -11
- package/dist/lock-XVR3RRZ6.mjs +57 -0
- package/dist/{login-RLMDEWAL.mjs → login-LW7CHZDZ.mjs} +10 -8
- package/dist/{logout-Z57X6HPE.mjs → logout-6U6OP5DM.mjs} +2 -2
- package/dist/{logs-TNDM4RFE.mjs → logs-HKH4H6PT.mjs} +5 -5
- package/dist/logs-SCQC5HNO.mjs +44 -0
- package/dist/mcp-VRHQOEGG.mjs +1077 -0
- package/dist/{mock-EVER34IX.mjs → mock-5H3CVPHC.mjs} +2 -2
- package/dist/outdated-T7QW6F3Z.mjs +58 -0
- package/dist/port-24R7ZNP4.mjs +2408 -0
- package/dist/{projects-YL4T24VV.mjs → projects-HYITVEXS.mjs} +6 -6
- package/dist/publish-DZRR5A5X.mjs +20 -0
- package/dist/remix-4HVXRIEB.mjs +125 -0
- package/dist/repo-FG3CMZQS.mjs +9 -0
- package/dist/report-M7F4ZYP7.mjs +2 -0
- package/dist/rollback-H7Q4XYGX.mjs +43 -0
- package/dist/{rollback-47VQ4HJK.mjs → rollback-OM64O67F.mjs} +6 -6
- package/dist/scene-QTHT3ZPL.mjs +324 -0
- package/dist/{status-TWPOMWNA.mjs → status-5D4RR3MM.mjs} +5 -5
- package/dist/status-AKI6E3JK.mjs +18 -0
- package/dist/test-W6MURUGZ.mjs +233 -0
- package/dist/testers-EI32JB5F.mjs +49 -0
- package/dist/{unlink-H3BO3DOQ.mjs → unlink-BND47IWW.mjs} +2 -2
- package/dist/{update-FLY46XI3.mjs → update-XKKFREKH.mjs} +4 -4
- package/dist/{whoami-CQPPARRS.mjs → whoami-WHLGCVRC.mjs} +3 -3
- package/dist/workshop-F2T4IDOU.mjs +1703 -0
- package/package.json +11 -7
- package/templates/storefront-auto/app/products/[slug]/page.tsx +1 -1
- package/templates/storefront-auto/package.json +1 -1
- package/templates/storefront-bakery/package.json +1 -1
- package/templates/storefront-fashion/app/products/[slug]/page.tsx +1 -1
- package/templates/storefront-fashion/package.json +1 -1
- package/templates/storefront-grocery/package.json +1 -1
- package/templates/storefront-pharmacy/app/products/[slug]/page.tsx +1 -1
- package/templates/storefront-pharmacy/package.json +1 -1
- package/templates/storefront-restaurant/package.json +1 -1
- package/templates/storefront-retail/app/products/[slug]/page.tsx +1 -1
- package/templates/storefront-retail/package.json +1 -1
- package/templates/storefront-services/package.json +1 -1
- package/dist/chunk-ARIDYKLO.mjs +0 -6463
- package/dist/dev-7O65HY6C.mjs +0 -8
- package/dist/explain-DLRUUCNS.mjs +0 -251
- package/dist/introspect-RC265ACF.mjs +0 -9
- 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 };
|