@getxflow/cli 1.10.0 → 1.12.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/commands/db.js +14 -3
- package/dist/commands/deploy.js +6 -2
- package/dist/help.js +6 -4
- package/dist/moment.js +6 -4
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/skills/xflow/SKILL.md +11 -5
package/dist/commands/db.js
CHANGED
|
@@ -295,6 +295,9 @@ async function migrate(client, projectId, migrations, dryRun, allowDestructive,
|
|
|
295
295
|
(0, ui_1.note)((0, ui_1.dim)(` ${hit}`));
|
|
296
296
|
}
|
|
297
297
|
if (result.dry_run) {
|
|
298
|
+
if (result.failed) {
|
|
299
|
+
throw new errors_1.CliError(`${result.failed.name}: ${result.failed.error}`, 'Checked on a copy of the database: the database is unchanged');
|
|
300
|
+
}
|
|
298
301
|
if (result.pending.length === 0) {
|
|
299
302
|
(0, ui_1.ok)('Nothing to apply: every migration is already in the database');
|
|
300
303
|
return;
|
|
@@ -303,12 +306,18 @@ async function migrate(client, projectId, migrations, dryRun, allowDestructive,
|
|
|
303
306
|
(0, ui_1.out)(`Will be applied (${result.pending.length}):`);
|
|
304
307
|
for (const name of result.pending)
|
|
305
308
|
(0, ui_1.out)(` ${name}`);
|
|
306
|
-
(0, ui_1.note)((0, ui_1.dim)(
|
|
309
|
+
(0, ui_1.note)((0, ui_1.dim)(result.checked
|
|
310
|
+
? ' They ran on a copy of the database as it is now; the database was not changed'
|
|
311
|
+
: result.checked === false
|
|
312
|
+
? ' Not checked: a copy of the database could not be made; the database was not changed'
|
|
313
|
+
: ' Dry run: the database was not changed'));
|
|
307
314
|
return;
|
|
308
315
|
}
|
|
309
316
|
for (const dump of result.dumps) {
|
|
310
317
|
(0, ui_1.note)((0, ui_1.dim)(` Dump of ${dump.table}: ${dump.rows} rows${dump.truncated ? ' (cut at the cap)' : ''}, kept for ${result.dump_retention_days} days`));
|
|
311
318
|
}
|
|
319
|
+
if (result.checked)
|
|
320
|
+
(0, ui_1.note)((0, ui_1.dim)(' Checked on a copy of the database first'));
|
|
312
321
|
if (result.applied.length > 0) {
|
|
313
322
|
(0, ui_1.ok)(`Applied: ${result.applied.join(', ')}`);
|
|
314
323
|
}
|
|
@@ -318,7 +327,7 @@ async function migrate(client, projectId, migrations, dryRun, allowDestructive,
|
|
|
318
327
|
if (result.failed) {
|
|
319
328
|
throw new errors_1.CliError(`${result.failed.name}: ${result.failed.error}`, result.applied.length > 0
|
|
320
329
|
? `What ran before it stays in the database (${result.applied.join(', ')}), the rest did not run`
|
|
321
|
-
: 'The database is unchanged:
|
|
330
|
+
: 'The database is unchanged: nothing was applied');
|
|
322
331
|
}
|
|
323
332
|
}
|
|
324
333
|
/** Time a branch has left, `5h` or `3d`. */
|
|
@@ -360,7 +369,9 @@ async function dbBranch(args) {
|
|
|
360
369
|
(0, ui_1.out)(JSON.stringify(data.branch, null, 2));
|
|
361
370
|
return;
|
|
362
371
|
}
|
|
363
|
-
(0, ui_1.ok)(`Branch ${name}: the project database ${data.branch.at ? `as of ${
|
|
372
|
+
(0, ui_1.ok)(`Branch ${name}: the project database ${data.branch.at ? `as of ${data.branch.at}` : 'as it is now'}`);
|
|
373
|
+
if (at && !data.branch.at)
|
|
374
|
+
(0, ui_1.note)((0, ui_1.dim)(` --at ${at} is within the last seconds: that is the database as it is now`));
|
|
364
375
|
(0, ui_1.note)((0, ui_1.dim)(` Work in it: xflow db query --branch ${name} '...', db exec and db migrate take --branch too`));
|
|
365
376
|
(0, ui_1.note)((0, ui_1.dim)(` Rows back into the project database: xflow db restore --from ${name} --table <table>`));
|
|
366
377
|
(0, ui_1.note)((0, ui_1.dim)(` Deleted on its own in ${left(data.branch.expires_at)}; more time: xflow db branch extend ${name}`));
|
package/dist/commands/deploy.js
CHANGED
|
@@ -109,7 +109,7 @@ function printNotes(started) {
|
|
|
109
109
|
(0, ui_1.note)(` Unfinished: ${item.message}${item.file ? (0, ui_1.dim)(` (${item.file})`) : ''}`);
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
|
-
async function finishFunctions(client, base, started) {
|
|
112
|
+
async function finishFunctions(client, base, started, rollback) {
|
|
113
113
|
printNotes(started);
|
|
114
114
|
const build = await waitForBuild(client, base, started);
|
|
115
115
|
if (build.phase === 'failed')
|
|
@@ -118,6 +118,9 @@ async function finishFunctions(client, base, started) {
|
|
|
118
118
|
for (const line of openLines(build.log_tail))
|
|
119
119
|
(0, ui_1.warn)(line);
|
|
120
120
|
(0, ui_1.note)((0, ui_1.dim)(' Every app of the project calls the new code already: there is no publish step for functions'));
|
|
121
|
+
if (rollback) {
|
|
122
|
+
(0, ui_1.note)((0, ui_1.dim)(` The database is not rolled back. As it was when this revision first went live: xflow db branch create rev-${build.revision} --at r${build.revision}`));
|
|
123
|
+
}
|
|
121
124
|
}
|
|
122
125
|
async function finishApp(client, base, started) {
|
|
123
126
|
printNotes(started);
|
|
@@ -165,6 +168,7 @@ function targetsHint(projectRoot) {
|
|
|
165
168
|
async function deployTarget(target, args, comment, revision) {
|
|
166
169
|
const base = (0, sources_1.streamPath)(target);
|
|
167
170
|
const { client } = target;
|
|
171
|
+
const rollback = revision !== undefined;
|
|
168
172
|
if (revision === undefined) {
|
|
169
173
|
if (!target.root)
|
|
170
174
|
throw new errors_1.CliError('Nothing to send without a folder', 'Build a revision the platform holds: --revision <n>');
|
|
@@ -189,7 +193,7 @@ async function deployTarget(target, args, comment, revision) {
|
|
|
189
193
|
if (target.kind === 'app')
|
|
190
194
|
await finishApp(client, base, started);
|
|
191
195
|
else
|
|
192
|
-
await finishFunctions(client, base, started);
|
|
196
|
+
await finishFunctions(client, base, started, rollback);
|
|
193
197
|
}
|
|
194
198
|
async function deploy(args) {
|
|
195
199
|
const stated = statedRevision((0, args_1.flagNumber)(args, 'revision'), (0, args_1.flagBool)(args, 'force'));
|
package/dist/help.js
CHANGED
|
@@ -295,8 +295,9 @@ will work.`,
|
|
|
295
295
|
with their SQL (cut to 2 KB), dropped connections; 7 days,
|
|
296
296
|
1 on the Free plan (--limit, --cursor for older)
|
|
297
297
|
xflow db branch [list] branches of the database (--json)
|
|
298
|
-
xflow db branch create <name> a branch as the database is now; --at 2h, --at 14:05 or
|
|
299
|
-
--at 2026-10-30T14:05+03:00 for a moment of its history
|
|
298
|
+
xflow db branch create <name> a branch as the database is now; --at 30s, --at 2h, --at 14:05 or
|
|
299
|
+
--at 2026-10-30T14:05+03:00 for a moment of its history,
|
|
300
|
+
--at r12 for when functions revision 12 went live;
|
|
300
301
|
--ttl 12h or --ttl 3d to delete it sooner than the plan does
|
|
301
302
|
xflow db branch extend <name> more time from now: the plan's longest, or --ttl
|
|
302
303
|
xflow db branch delete <name> the branch goes, the database is unchanged
|
|
@@ -349,9 +350,10 @@ ${(0, ui_1.bold)('migrations/0002_orders.sql')} and so on). The order comes from
|
|
|
349
350
|
is why the leading number is required. The command first stores the project root as a
|
|
350
351
|
revision, so the migrations stay in its history and a clone gets them, then applies what is
|
|
351
352
|
new. Every migration runs in its own transaction, the history is kept in the database
|
|
352
|
-
itself, and anything already applied is not run again.
|
|
353
|
+
itself, and anything already applied is not run again. New migrations run first, all of them,
|
|
354
|
+
on a copy of the database as it is now: if one fails there, nothing is applied.
|
|
353
355
|
|
|
354
|
-
--dry-run
|
|
356
|
+
--dry-run run the new migrations on that copy only; the database is not touched
|
|
355
357
|
--allow-destructive allow operations that destroy data
|
|
356
358
|
|
|
357
359
|
About destructive ones. ${(0, ui_1.bold)('DROP TABLE')}, ${(0, ui_1.bold)('DROP COLUMN')}, ${(0, ui_1.bold)('TRUNCATE')} and deleting rows (judged by what
|
package/dist/moment.js
CHANGED
|
@@ -3,10 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.parseMoment = parseMoment;
|
|
4
4
|
exports.parseTtl = parseTtl;
|
|
5
5
|
const errors_1 = require("./errors");
|
|
6
|
-
const UNIT_MS = { m: 60_000, h: 3_600_000, d: 86_400_000 };
|
|
7
|
-
/** `30m`/`2h`/`1d` ago, `14:05` local (yesterday if still ahead),
|
|
6
|
+
const UNIT_MS = { s: 1_000, m: 60_000, h: 3_600_000, d: 86_400_000 };
|
|
7
|
+
/** `30s`/`30m`/`2h`/`1d` ago, `14:05` local (yesterday if still ahead), an ISO date, or `r12` (the platform resolves it). */
|
|
8
8
|
function parseMoment(text, now = new Date()) {
|
|
9
|
-
|
|
9
|
+
if (/^r\d{1,6}$/.test(text))
|
|
10
|
+
return text;
|
|
11
|
+
const ago = /^(\d{1,5})([smhd])$/.exec(text);
|
|
10
12
|
if (ago)
|
|
11
13
|
return new Date(now.getTime() - Number(ago[1]) * UNIT_MS[ago[2]]).toISOString();
|
|
12
14
|
const clock = /^(\d{1,2}):(\d{2})$/.exec(text);
|
|
@@ -19,7 +21,7 @@ function parseMoment(text, now = new Date()) {
|
|
|
19
21
|
}
|
|
20
22
|
const at = new Date(text);
|
|
21
23
|
if (!/^\d{4}-\d{2}-\d{2}/.test(text) || Number.isNaN(at.getTime())) {
|
|
22
|
-
throw new errors_1.CliError(`--at ${text} is not a moment`, 'For example: --at 2h (two hours ago), --at 14:05 (today), --at 2026-10-30T14:05+03:00');
|
|
24
|
+
throw new errors_1.CliError(`--at ${text} is not a moment`, 'For example: --at 30s, --at 2h (two hours ago), --at 14:05 (today), --at 2026-10-30T14:05+03:00, --at r12 (when functions revision 12 went live)');
|
|
23
25
|
}
|
|
24
26
|
return at.toISOString();
|
|
25
27
|
}
|
package/dist/version.js
CHANGED
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DEFAULT_API_URL = exports.CLI_VERSION = void 0;
|
|
4
4
|
/** Keep in sync with cli/package.json. */
|
|
5
|
-
exports.CLI_VERSION = '1.
|
|
5
|
+
exports.CLI_VERSION = '1.12.0';
|
|
6
6
|
/** Overridden by XFLOW_API_URL or the `api` field in xflow.json. */
|
|
7
7
|
exports.DEFAULT_API_URL = 'https://app.getxflow.com';
|
package/package.json
CHANGED
package/skills/xflow/SKILL.md
CHANGED
|
@@ -28,7 +28,7 @@ instead of guessing; give the person https://docs.getxflow.com when they ask how
|
|
|
28
28
|
|
|
29
29
|
## Keeping these instructions current
|
|
30
30
|
|
|
31
|
-
These instructions ship with xflow CLI 1.
|
|
31
|
+
These instructions ship with xflow CLI 1.12.0, so the copy you are reading can be older than
|
|
32
32
|
the CLI answering your commands.
|
|
33
33
|
|
|
34
34
|
When the CLI says this file does not match it, run `xflow skills --refresh` and then **read
|
|
@@ -553,7 +553,8 @@ There is no undo: read the rows with `db query` first.
|
|
|
553
553
|
|
|
554
554
|
A branch is a database of its own, as the project database is now or was at a moment of its
|
|
555
555
|
history (6 hours back on Free, 3 days on Start, 7 days on Business), made in seconds without
|
|
556
|
-
copying: `xflow db branch create try-orders [--at 2h|14:05|<ISO date
|
|
556
|
+
copying: `xflow db branch create try-orders [--at 30s|2h|14:05|<ISO date>|r12]` (`r12`: when functions
|
|
557
|
+
revision 12 went live; rolling functions back with `--revision` leaves the database as it is). Try a migration or a fix
|
|
557
558
|
there first (`db migrate`, `db exec`, `db query` take `--branch <name>`); nothing reaches the
|
|
558
559
|
project database. After a data mistake, do not roll anything back: branch as of a moment before
|
|
559
560
|
it, check the rows with `db query --branch`, then `xflow db restore --from <branch> --table <t>
|
|
@@ -562,8 +563,8 @@ it, check the rows with `db query --branch`, then `xflow db restore --from <bran
|
|
|
562
563
|
5, 15 by plan; delete them when done (`xflow db branch delete <name>`). A branch is deleted on its
|
|
563
564
|
own when its lifetime ends: at most 1, 7 or 30 days by plan, shorter with `--ttl 12h`, more from
|
|
564
565
|
now with `xflow db branch extend <name>`. A branch sleeps after a quiet minute, has no functions,
|
|
565
|
-
and a branch of a branch is not made. People see the branches in Databases, open them there
|
|
566
|
-
|
|
566
|
+
and a branch of a branch is not made. People see the branches in Databases, open them there, read
|
|
567
|
+
their data and bring missing rows back.
|
|
567
568
|
|
|
568
569
|
Anything that destroys data
|
|
569
570
|
(`DROP TABLE`, `DROP COLUMN`, `TRUNCATE`, `DELETE`) needs both the `--allow-destructive` flag and
|
|
@@ -573,7 +574,12 @@ or deleted rows is rolled back, however the SQL was written. `BEGIN`/`COMMIT` in
|
|
|
573
574
|
refused: each one already runs in its own transaction. When the refusal is about the right, the flag changes nothing: say what
|
|
574
575
|
needs deleting and why, and let the person decide. With both in place the affected tables are
|
|
575
576
|
dumped first (up to 50 000 rows each) and kept for 7 days; getting a dump back is a request to
|
|
576
|
-
the person, no command does it.
|
|
577
|
+
the person, no command does it.
|
|
578
|
+
|
|
579
|
+
New migrations run first, all of them, on a copy of the database as it is now: if one fails
|
|
580
|
+
there, nothing is applied and the database is unchanged, so a half-applied set does not happen.
|
|
581
|
+
`--dry-run` runs only that check. A copy that could not be made is skipped, and the migration goes
|
|
582
|
+
straight to the database as before.
|
|
577
583
|
|
|
578
584
|
Pass `DATABASE_URL` as is, without an `ssl` option. The database is PostgreSQL 18, so do not
|
|
579
585
|
tune it from migrations:
|