@getxflow/cli 1.9.0 → 1.10.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.
@@ -321,6 +321,11 @@ async function migrate(client, projectId, migrations, dryRun, allowDestructive,
321
321
  : 'The database is unchanged: the migration rolled back as a whole');
322
322
  }
323
323
  }
324
+ /** Time a branch has left, `5h` or `3d`. */
325
+ function left(iso) {
326
+ const h = Math.max(0, Math.round((Date.parse(iso) - Date.now()) / 3_600_000));
327
+ return h < 48 ? `${h}h left` : `${Math.round(h / 24)}d left`;
328
+ }
324
329
  /** Branches of the project database: list, create, delete. */
325
330
  async function dbBranch(args) {
326
331
  const [sub, name] = args.words;
@@ -329,7 +334,7 @@ async function dbBranch(args) {
329
334
  if (sub === undefined || sub === 'list') {
330
335
  const data = await (0, api_1.apiJson)(client, path);
331
336
  if ((0, args_1.flagBool)(args, 'json')) {
332
- (0, ui_1.out)(JSON.stringify(data.branches, null, 2));
337
+ (0, ui_1.out)(JSON.stringify(data, null, 2));
333
338
  return;
334
339
  }
335
340
  if (data.branches.length === 0) {
@@ -337,16 +342,18 @@ async function dbBranch(args) {
337
342
  (0, ui_1.note)((0, ui_1.dim)(' A copy to try things on: xflow db branch create <name> [--at 2h]'));
338
343
  return;
339
344
  }
340
- (0, ui_1.table)(data.branches.map((b) => [b.name, b.at ? `as of ${new Date(b.at).toLocaleString()}` : 'as made', (0, ui_1.formatAge)(b.created_at)]));
345
+ (0, ui_1.table)(data.branches.map((b) => [b.name, b.at ? `as of ${new Date(b.at).toLocaleString()}` : 'as made', (0, ui_1.formatAge)(b.created_at), left(b.expires_at)]));
346
+ (0, ui_1.note)((0, ui_1.dim)(` ${data.branches.length} of ${data.plan.branches}; a branch lives up to ${data.plan.ttl_hours / 24}d on this plan (xflow db branch extend <name>)`));
341
347
  return;
342
348
  }
343
349
  if (!name)
344
350
  throw new errors_1.CliError(`xflow db branch ${sub} needs a name`, `For example: xflow db branch ${sub} fix-orders`);
345
351
  if (sub === 'create') {
346
352
  const at = (0, args_1.flagString)(args, 'at');
353
+ const ttl = (0, args_1.flagString)(args, 'ttl');
347
354
  const data = await (0, api_1.apiJson)(client, path, {
348
355
  method: 'POST',
349
- body: { name, at: at ? (0, moment_1.parseMoment)(at) : undefined },
356
+ body: { name, at: at ? (0, moment_1.parseMoment)(at) : undefined, ttl_hours: ttl ? (0, moment_1.parseTtl)(ttl) : undefined },
350
357
  timeoutMs: 180_000,
351
358
  });
352
359
  if ((0, args_1.flagBool)(args, 'json')) {
@@ -356,6 +363,20 @@ async function dbBranch(args) {
356
363
  (0, ui_1.ok)(`Branch ${name}: the project database ${data.branch.at ? `as of ${new Date(data.branch.at).toLocaleString()}` : 'as it is now'}`);
357
364
  (0, ui_1.note)((0, ui_1.dim)(` Work in it: xflow db query --branch ${name} '...', db exec and db migrate take --branch too`));
358
365
  (0, ui_1.note)((0, ui_1.dim)(` Rows back into the project database: xflow db restore --from ${name} --table <table>`));
366
+ (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}`));
367
+ return;
368
+ }
369
+ if (sub === 'extend') {
370
+ const ttl = (0, args_1.flagString)(args, 'ttl');
371
+ const data = await (0, api_1.apiJson)(client, `${path}/${encodeURIComponent(name)}`, {
372
+ method: 'PATCH',
373
+ body: { ttl_hours: ttl ? (0, moment_1.parseTtl)(ttl) : undefined },
374
+ });
375
+ if ((0, args_1.flagBool)(args, 'json')) {
376
+ (0, ui_1.out)(JSON.stringify(data.branch, null, 2));
377
+ return;
378
+ }
379
+ (0, ui_1.ok)(`Branch ${name}: ${left(data.branch.expires_at)}, until ${new Date(data.branch.expires_at).toLocaleString()}`);
359
380
  return;
360
381
  }
361
382
  if (sub === 'delete' || sub === 'rm') {
@@ -363,7 +384,7 @@ async function dbBranch(args) {
363
384
  (0, ui_1.ok)(`Branch ${name} deleted; the project database is unchanged`);
364
385
  return;
365
386
  }
366
- throw new errors_1.CliError(`Unknown command: db branch ${sub}`, 'Available: list, create and delete');
387
+ throw new errors_1.CliError(`Unknown command: db branch ${sub}`, 'Available: list, create, extend and delete');
367
388
  }
368
389
  /** Rows of a table from a branch back into the project database, by primary key. */
369
390
  async function dbRestore(args) {
package/dist/flags.js CHANGED
@@ -82,8 +82,8 @@ exports.KNOWN = {
82
82
  'db exec': ['allow-destructive', 'json', 'project', 'branch'],
83
83
  'db logs': ['limit', 'project', 'cursor'],
84
84
  'db migrate': ['dry-run', 'allow-destructive', 'branch'],
85
- 'db branch': ['at', 'json', 'project'],
86
- 'db branches': ['at', 'json', 'project'],
85
+ 'db branch': ['at', 'json', 'project', 'ttl'],
86
+ 'db branches': ['at', 'json', 'project', 'ttl'],
87
87
  'db restore': ['from', 'table', 'where', 'overwrite', 'json', 'project'],
88
88
  storage: ['json', 'project'],
89
89
  'storage ls': ['json', 'project'],
package/dist/help.js CHANGED
@@ -296,7 +296,9 @@ will work.`,
296
296
  1 on the Free plan (--limit, --cursor for older)
297
297
  xflow db branch [list] branches of the database (--json)
298
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
299
+ --at 2026-10-30T14:05+03:00 for a moment of its history;
300
+ --ttl 12h or --ttl 3d to delete it sooner than the plan does
301
+ xflow db branch extend <name> more time from now: the plan's longest, or --ttl
300
302
  xflow db branch delete <name> the branch goes, the database is unchanged
301
303
  xflow db restore --from <branch> --table <table> [--where '...'] [--overwrite]
302
304
  rows of a table from a branch back into the database
@@ -331,7 +333,9 @@ ${(0, ui_1.bold)('query')}, ${(0, ui_1.bold)('exec')} and ${(0, ui_1.bold)('migr
331
333
  reaches the project database. A branch sleeps after a quiet minute, so its first query takes a
332
334
  second or two; it has no functions and no history of its own, and a branch of a branch is not
333
335
  made. Branches per project: 2 on Free, 5 on Start, 15 on Business; delete one you no longer need.
334
- The history reaches back 6 hours on Free, 3 days on Start, 7 days on Business.
336
+ A branch is deleted on its own when its lifetime ends: at most 1 day on Free, 7 days on Start,
337
+ 30 days on Business, and ${(0, ui_1.bold)('extend')} gives it more from now within that. The history reaches
338
+ back 6 hours on Free, 3 days on Start, 7 days on Business.
335
339
 
336
340
  ${(0, ui_1.bold)('restore')} puts rows back after a mistake without rolling the whole database back: make a
337
341
  branch as of a moment before it, check the rows there with ${(0, ui_1.bold)('query --branch')}, then restore
package/dist/limits.js CHANGED
@@ -54,7 +54,7 @@ const QUOTA_LABELS = {
54
54
  people: 'People',
55
55
  space_mb: 'Space (databases and files together)',
56
56
  function_minutes: 'Function minutes',
57
- db_hours: 'Database hours',
57
+ db_hours: 'Database GB-hours',
58
58
  builds: 'Builds this month',
59
59
  };
60
60
  /** Decimal units, 1000 MB per GB: matches how volumes are sold. */
package/dist/moment.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseMoment = parseMoment;
4
+ exports.parseTtl = parseTtl;
4
5
  const errors_1 = require("./errors");
5
6
  const UNIT_MS = { m: 60_000, h: 3_600_000, d: 86_400_000 };
6
7
  /** `30m`/`2h`/`1d` ago, `14:05` local (yesterday if still ahead), or an ISO date. */
@@ -22,3 +23,10 @@ function parseMoment(text, now = new Date()) {
22
23
  }
23
24
  return at.toISOString();
24
25
  }
26
+ /** A branch lifetime in whole hours: `12h` or `3d`. */
27
+ function parseTtl(text) {
28
+ const m = /^(\d{1,4})([hd])$/.exec(text);
29
+ if (!m || Number(m[1]) < 1)
30
+ throw new errors_1.CliError(`--ttl ${text} is not a lifetime`, 'For example: --ttl 12h or --ttl 3d');
31
+ return Number(m[1]) * (m[2] === 'd' ? 24 : 1);
32
+ }
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.9.0';
5
+ exports.CLI_VERSION = '1.10.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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getxflow/cli",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "description": "CLI for the XFlow platform: projects with apps, functions and a database; source sync, deployment and publishing",
5
5
  "license": "UNLICENSED",
6
6
  "engines": {
@@ -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.9.0, so the copy you are reading can be older than
31
+ These instructions ship with xflow CLI 1.10.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
@@ -119,7 +119,8 @@ schedules may run, and which access levels an app may have. The number of functi
119
119
  and projects is not capped; at most 15 new projects a day (UTC) per organization, deleted ones count. Most are finite; a plan
120
120
  may leave one uncapped, and then `whoami` prints `no limit` instead of a ceiling.
121
121
  A paid plan comes in packages (Start S/M/L, Business M/L/XL): the package sets the space,
122
- database hours and function minutes, the plan sets the rest. More space is a bigger package
122
+ database GB-hours (1 GB of memory a database actually uses for an hour awake; a sleeping
123
+ one uses none) and function minutes, the plan sets the rest. More space is a bigger package
123
124
  chosen by a person in «Тариф» (Plan); there is no separate purchase of space.
124
125
  `xflow whoami` prints every one of them next to what is used, and the rights of your own key
125
126
  with them: destroying data (a migration or `db exec`), deleting files from storage, moving a database and
@@ -558,8 +559,11 @@ project database. After a data mistake, do not roll anything back: branch as of
558
559
  it, check the rows with `db query --branch`, then `xflow db restore --from <branch> --table <t>
559
560
  [--where '...']` puts the missing rows back by primary key and keeps all newer data
560
561
  (`--overwrite` replaces rows still there and needs the destroy right). Branches per project: 2,
561
- 5, 15 by plan; delete them when done (`xflow db branch delete <name>`). A branch sleeps after a
562
- quiet minute, has no functions, and a branch of a branch is not made.
562
+ 5, 15 by plan; delete them when done (`xflow db branch delete <name>`). A branch is deleted on its
563
+ own when its lifetime ends: at most 1, 7 or 30 days by plan, shorter with `--ttl 12h`, more from
564
+ 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 and
566
+ read their data.
563
567
 
564
568
  Anything that destroys data
565
569
  (`DROP TABLE`, `DROP COLUMN`, `TRUNCATE`, `DELETE`) needs both the `--allow-destructive` flag and