@getxflow/cli 1.4.0 → 1.5.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/bin.js CHANGED
@@ -180,15 +180,11 @@ async function run(args) {
180
180
  await (0, db_1.dbExec)({ ...args, words: args.words.slice(2) });
181
181
  return;
182
182
  }
183
- if (second === 'move') {
184
- await (0, db_1.dbMove)(rest);
185
- return;
186
- }
187
183
  if (second === undefined || second === 'status') {
188
184
  await (0, db_1.dbStatus)({ ...args, words: args.words.slice(2) });
189
185
  return;
190
186
  }
191
- throw new errors_1.CliError(`Unknown command: db ${second}`, 'Available: status, list, schema, query, exec, migrate and move');
187
+ throw new errors_1.CliError(`Unknown command: db ${second}`, 'Available: status, list, schema, query, exec and migrate');
192
188
  case 'storage':
193
189
  if (second === 'push') {
194
190
  await (0, storage_1.storagePush)(rest);
@@ -5,7 +5,6 @@ exports.dbList = dbList;
5
5
  exports.dbSchema = dbSchema;
6
6
  exports.dbQuery = dbQuery;
7
7
  exports.dbExec = dbExec;
8
- exports.dbMove = dbMove;
9
8
  exports.dbMigrate = dbMigrate;
10
9
  exports.migrateBeforeDeploy = migrateBeforeDeploy;
11
10
  const node_fs_1 = require("node:fs");
@@ -20,41 +19,6 @@ const ui_1 = require("../ui");
20
19
  const sources_1 = require("./sources");
21
20
  /** Migration files live in the repo; the applied history lives in the database. */
22
21
  const MIGRATIONS_DIR = 'migrations';
23
- function serverLabel(server) {
24
- if (server === undefined)
25
- return '-';
26
- if (server === null)
27
- return 'platform';
28
- return server.status === 'active' ? `own "${server.name}"` : `own "${server.name}", ${server.status}`;
29
- }
30
- const moving = (move) => move?.status === 'copying' || move?.status === 'switching';
31
- /** Asked apart from the history: during a move the database does not answer, this does. */
32
- async function projectDatabase(client, projectId) {
33
- try {
34
- return await (0, api_1.apiJson)(client, `/api/v1/projects/${projectId}/db/move`);
35
- }
36
- catch (e) {
37
- if (e instanceof api_1.ApiError && e.code === 'not_deployed')
38
- return null;
39
- throw e;
40
- }
41
- }
42
- function printPlace(history, place) {
43
- (0, ui_1.out)(`Schema: ${(0, ui_1.bold)(history.schema)}`);
44
- if (history.server !== undefined)
45
- (0, ui_1.out)(`Server: ${serverLabel(history.server)}`);
46
- if (place?.move)
47
- (0, ui_1.out)(`Move: ${moveLine(place.move)}`);
48
- (0, ui_1.out)('');
49
- }
50
- /** Mid-move the history cannot be read, so the answer is the move itself. */
51
- function printMoving(place) {
52
- if (place.database)
53
- (0, ui_1.out)(`Database: ${(0, ui_1.bold)(place.database.name)}`);
54
- (0, ui_1.out)(`Server: ${serverLabel(place.server)}`);
55
- (0, ui_1.out)(`Move: ${place.move?.note ?? ''}`);
56
- (0, ui_1.note)((0, ui_1.dim)(' Migrations and queries wait until it is over; the platform finishes it on its own'));
57
- }
58
22
  const STATE_LABEL = {
59
23
  pending: 'waiting',
60
24
  applied: 'applied',
@@ -86,11 +50,9 @@ async function dbStatus(args) {
86
50
  // is the applied history alone, which does not depend on any working copy.
87
51
  if ((0, args_1.flagString)(args, 'project') !== undefined) {
88
52
  const { projectId, client } = await (0, session_1.projectTarget)(args);
89
- const place = await projectDatabase(client, projectId);
90
- if (place && moving(place.move))
91
- return printMoving(place);
92
53
  const history = await (0, api_1.apiJson)(client, `/api/v1/projects/${projectId}/db/migrate`);
93
- printPlace(history, place);
54
+ (0, ui_1.out)(`Schema: ${(0, ui_1.bold)(history.schema)}`);
55
+ (0, ui_1.out)('');
94
56
  if (history.applied.length === 0) {
95
57
  (0, ui_1.note)('No migrations have been applied yet');
96
58
  return;
@@ -102,15 +64,13 @@ async function dbStatus(args) {
102
64
  const { root, config } = (0, config_1.requireProject)();
103
65
  const client = await (0, session_1.connectProject)(config.projectId, root, config);
104
66
  const local = readMigrations(root);
105
- const place = await projectDatabase(client, config.projectId);
106
- if (place && moving(place.move))
107
- return printMoving(place);
108
67
  const status = await (0, api_1.apiJson)(client, `/api/v1/projects/${config.projectId}/db/status`, {
109
68
  method: 'POST',
110
69
  body: { migrations: local },
111
70
  timeoutMs: 60_000,
112
71
  });
113
- printPlace({ schema: status.schema, server: status.server, applied: [] }, place);
72
+ (0, ui_1.out)(`Schema: ${(0, ui_1.bold)(status.schema)}`);
73
+ (0, ui_1.out)('');
114
74
  if (status.migrations.length === 0) {
115
75
  (0, ui_1.note)(`No migrations. The first one: ${MIGRATIONS_DIR}/0001_init.sql`);
116
76
  return;
@@ -121,65 +81,23 @@ async function dbStatus(args) {
121
81
  (0, ui_1.warn)('A changed file is never applied again and db migrate refuses it: write a new migration');
122
82
  }
123
83
  }
124
- function serverRow(server) {
125
- const room = server.used_bytes !== null && server.space_bytes !== null
126
- ? `${(0, ui_1.formatBytes)(server.used_bytes)} of ${(0, ui_1.formatBytes)(server.space_bytes)} used`
127
- : '';
128
- return [
129
- server.id === null ? 'platform' : `own "${server.name}"`,
130
- server.id === null ? 'plan space' : server.tier ?? '',
131
- server.status === 'active' ? '' : server.status,
132
- room,
133
- `${server.databases} ${server.databases === 1 ? 'database' : 'databases'}`,
134
- ];
135
- }
136
- /** The databases of the projects of the organization, and the servers they can live on. */
84
+ /** The databases of the projects of the organization, largest first. */
137
85
  async function dbList() {
138
86
  const client = (0, session_1.connect)((0, config_1.localConfig)());
139
87
  const data = await (0, api_1.apiJson)(client, '/api/v1/org/databases');
140
88
  if (data.databases.length === 0) {
141
89
  (0, ui_1.note)('The organization has no databases');
142
90
  (0, ui_1.note)((0, ui_1.dim)(' One is created with the first project: xflow init'));
91
+ return;
143
92
  }
144
- else {
145
- printDatabases(data.databases, data.total, data.truncated);
146
- }
147
- printServers(data.servers);
148
- }
149
- function moveLine(move) {
150
- return move.finished_at ? `${move.note} (${(0, ui_1.formatAge)(move.finished_at)})` : move.note;
151
- }
152
- function printDatabases(databases, total, truncated) {
153
- (0, ui_1.table)(databases.map((database) => [
93
+ (0, ui_1.table)(data.databases.map((database) => [
154
94
  database.name,
155
95
  database.id,
156
- database.schema ?? '-',
157
- serverLabel(database.server),
158
- typeof database.bytes === 'number' ? (0, ui_1.formatBytes)(database.bytes) : '',
96
+ database.schema_name,
97
+ database.bytes === null ? '' : (0, ui_1.formatBytes)(database.bytes),
159
98
  ]));
160
- if (truncated)
161
- (0, ui_1.note)((0, ui_1.dim)(` Shown ${databases.length} of ${total}`));
162
- for (const database of databases) {
163
- if (database.move)
164
- (0, ui_1.note)((0, ui_1.dim)(` ${database.name}: ${moveLine(database.move)}`));
165
- }
166
- const down = new Map();
167
- for (const { server } of databases)
168
- if (server && server.status !== 'active')
169
- down.set(server.id, server);
170
- for (const server of down.values()) {
171
- (0, ui_1.note)((0, ui_1.dim)(` Server "${server.name}" is not running (${server.status}): its databases do not answer until it is back. ` +
172
- 'Not fixable from the code: tell the person, the server state is in the «Базы данных» (Databases) section of the web interface'));
173
- }
174
- }
175
- /** Where a new or a moved database can go, with the room on each. Only the platform: no choice to show. */
176
- function printServers(servers) {
177
- if (!servers || servers.length < 2)
178
- return;
179
- (0, ui_1.out)('');
180
- (0, ui_1.out)((0, ui_1.bold)('Servers'));
181
- (0, ui_1.table)(servers.map(serverRow));
182
- (0, ui_1.note)((0, ui_1.dim)(' Without --server a new project keeps its database on the platform server. To put it on another: xflow init --server <name>, or xflow db move --to <name> for one that already exists'));
99
+ if (data.truncated)
100
+ (0, ui_1.note)((0, ui_1.dim)(` Shown ${data.databases.length} of ${data.total}`));
183
101
  }
184
102
  /** Long values would tear the columns apart; the cut is marked, never silent. */
185
103
  const CELL_LIMIT = 60;
@@ -328,64 +246,6 @@ async function dbExec(args) {
328
246
  (0, ui_1.note)((0, ui_1.dim)(` The first ${data.rows.length} returned rows: the rest are saved but not sent back`));
329
247
  }
330
248
  }
331
- const MOVE_POLL_MS = 3_000;
332
- /** Past this the command lets go: the move carries on, db status follows it. */
333
- const MOVE_FOLLOW_MS = 10 * 60_000;
334
- const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
335
- /**
336
- * Moving the project database to another server of the organization. The platform
337
- * runs the move by itself; this command starts it and follows it to the end.
338
- */
339
- async function dbMove(args) {
340
- const to = (0, args_1.flagString)(args, 'to');
341
- if (!to) {
342
- throw new errors_1.CliError('Name the server: --to <name>', 'The servers and their names: xflow db list ("platform" is the common one)');
343
- }
344
- const { projectId, client } = await (0, session_1.projectTarget)(args);
345
- const path = `/api/v1/projects/${projectId}/db/move`;
346
- let place;
347
- // The platform takes the project name as the yes; --yes looks it up.
348
- const confirm = (0, args_1.flagBool)(args, 'yes')
349
- ? (await (0, api_1.apiJson)(client, `/api/v1/projects/${projectId}`)).name
350
- : undefined;
351
- try {
352
- place = await (0, api_1.apiJson)(client, path, {
353
- method: 'POST',
354
- body: { to, confirm },
355
- timeoutMs: 60_000,
356
- });
357
- }
358
- catch (e) {
359
- // Said in the words of this command: the flag, and the name that goes in it.
360
- if (e instanceof api_1.ApiError && e.code === 'confirm_required') {
361
- throw new errors_1.CliError(e.message, `To move it: xflow db move --to "${to}" --yes`);
362
- }
363
- throw e;
364
- }
365
- (0, ui_1.note)((0, ui_1.dim)(' The platform carries the move through by itself: stopping this command does not stop it'));
366
- const deadline = Date.now() + MOVE_FOLLOW_MS;
367
- let shown = '';
368
- while (moving(place.move)) {
369
- const line = place.move?.note ?? '';
370
- if (line !== shown)
371
- (0, ui_1.step)(line);
372
- shown = line;
373
- if (Date.now() > deadline) {
374
- (0, ui_1.note)('Still going: xflow db status follows it');
375
- return;
376
- }
377
- await sleep(MOVE_POLL_MS);
378
- place = await (0, api_1.apiJson)(client, path);
379
- }
380
- const { move } = place;
381
- if (move?.status === 'failed')
382
- throw new errors_1.CliError(move.note);
383
- if (move && (move.functions_done ?? 0) < (move.functions_total ?? 0)) {
384
- (0, ui_1.warn)(move.note);
385
- return;
386
- }
387
- (0, ui_1.ok)(move?.note ?? `The database is on ${serverLabel(place.server)}`);
388
- }
389
249
  async function dbMigrate(args) {
390
250
  const { root, config } = (0, config_1.requireProject)();
391
251
  const client = await (0, session_1.connectProject)(config.projectId, root, config);
@@ -83,7 +83,7 @@ async function init(args) {
83
83
  (0, ui_1.step)(orgName ? `Creating the project in "${orgName}"` : 'Creating the project on the platform');
84
84
  const project = await (0, api_1.apiJson)(client, '/api/v1/projects', {
85
85
  method: 'POST',
86
- body: { name, app: folder ? { name } : null, server: (0, args_1.flagString)(args, 'server') ?? null },
86
+ body: { name, app: folder ? { name } : null },
87
87
  timeoutMs: 60_000,
88
88
  });
89
89
  try {
@@ -105,8 +105,6 @@ async function init(args) {
105
105
  }
106
106
  (0, skills_1.installSkillQuietly)(target);
107
107
  (0, ui_1.ok)(`Project "${project.name}" created`);
108
- if (project.server)
109
- (0, ui_1.note)((0, ui_1.dim)(` Its database goes to the own server "${project.server.name}"`));
110
108
  (0, ui_1.out)('');
111
109
  (0, ui_1.out)(` ${(0, ui_1.bold)('Next:')}`);
112
110
  (0, ui_1.out)(` cd ${(0, node_path_1.basename)(target)}`);
@@ -265,7 +263,7 @@ async function get(args) {
265
263
  if (card.description)
266
264
  (0, ui_1.out)(card.description);
267
265
  (0, ui_1.out)('');
268
- (0, ui_1.out)(`Database: ${card.database.schema}${card.database.server_id ? ' (own server)' : ''}`);
266
+ (0, ui_1.out)(`Database: ${card.database.schema}`);
269
267
  (0, ui_1.out)('');
270
268
  (0, ui_1.out)('Apps:');
271
269
  if (card.apps.length === 0)
package/dist/flags.js CHANGED
@@ -44,7 +44,7 @@ exports.KNOWN = {
44
44
  'org list': [],
45
45
  'org members': [],
46
46
  'org switch': [],
47
- init: ['name', 'app', 'no-app', 'server', 'org'],
47
+ init: ['name', 'app', 'no-app', 'org'],
48
48
  link: [],
49
49
  projects: [],
50
50
  'projects list': [],
@@ -81,7 +81,6 @@ exports.KNOWN = {
81
81
  'db query': ['json', 'project'],
82
82
  'db exec': ['allow-destructive', 'json', 'project'],
83
83
  'db migrate': ['dry-run', 'allow-destructive'],
84
- 'db move': ['to', 'yes', 'project'],
85
84
  storage: ['json', 'project'],
86
85
  'storage ls': ['json', 'project'],
87
86
  'storage list': ['json', 'project'],
package/dist/help.js CHANGED
@@ -91,14 +91,12 @@ ${(0, ui_1.bold)('Functions')}
91
91
 
92
92
  ${(0, ui_1.bold)('Database')}
93
93
  xflow db status which migrations are applied and which are waiting
94
- xflow db list databases of the projects and the servers of the organization
94
+ xflow db list databases of the projects of the organization, with sizes
95
95
  xflow db schema [table] tables of the project, or the columns of one
96
96
  xflow db query 'select ...' read data, in a read-only transaction
97
97
  xflow db exec 'update ...' a one-off data change, outside the migration history
98
98
  xflow db migrate [--dry-run] [--allow-destructive]
99
99
  apply the migrations from migrations/*.sql
100
- xflow db move --to <server> --yes
101
- move the project database to another server
102
100
 
103
101
  ${(0, ui_1.bold)('Pages')}
104
102
  xflow pages public sites of the organization at <name>.xflowpage.ru
@@ -173,7 +171,7 @@ no working copy of its own:
173
171
  xflow db query --project 3f2a… 'select id, title from tasks where done = false'
174
172
  xflow functions invoke kanban --project 3f2a… --data-file card.json
175
173
 
176
- Which commands: ${(0, ui_1.bold)('db status')}, ${(0, ui_1.bold)('db query')}, ${(0, ui_1.bold)('db exec')}, ${(0, ui_1.bold)('db schema')}, ${(0, ui_1.bold)('db move')}, ${(0, ui_1.bold)('functions list')},
174
+ Which commands: ${(0, ui_1.bold)('db status')}, ${(0, ui_1.bold)('db query')}, ${(0, ui_1.bold)('db exec')}, ${(0, ui_1.bold)('db schema')}, ${(0, ui_1.bold)('functions list')},
177
175
  ${(0, ui_1.bold)('functions invoke')}, ${(0, ui_1.bold)('functions logs')}, ${(0, ui_1.bold)('logs')}, ${(0, ui_1.bold)('env')}, ${(0, ui_1.bold)('env rm')},
178
176
  ${(0, ui_1.bold)('connections')}, ${(0, ui_1.bold)('connections link')}, ${(0, ui_1.bold)('connections unlink')}, ${(0, ui_1.bold)('schedules')},
179
177
  ${(0, ui_1.bold)('schedules set')}, ${(0, ui_1.bold)('schedules rm')}, ${(0, ui_1.bold)('storage ls')}, ${(0, ui_1.bold)('storage push')},
@@ -336,8 +334,8 @@ is a hard gate on a changed contract, and until you run this command nothing els
336
334
  will work.`,
337
335
  db: `${(0, ui_1.bold)('xflow db')}: schema, data, migrations
338
336
 
339
- xflow db status what is applied, what is waiting, the server
340
- xflow db list databases of the projects, the servers of the organization
337
+ xflow db status what is applied, what is waiting
338
+ xflow db list databases of the projects of the organization, with sizes
341
339
  xflow db schema [table] tables of the project, or the columns of one
342
340
  xflow db query 'select ...' read data (rows: the query's own limit, 50 without
343
341
  one, 300 at most; --json prints the values whole)
@@ -345,26 +343,10 @@ will work.`,
345
343
  xflow db exec 'update ...' a one-off data change: one INSERT, UPDATE, DELETE or MERGE
346
344
  (or <file>.sql; --allow-destructive, --json)
347
345
  xflow db migrate store the project root, then apply migrations/*.sql
348
- xflow db move --to <server> --yes
349
- move the project database to another server
350
346
 
351
347
  Every project has exactly one database of its own, shared by all of its apps and functions,
352
348
  and every command here works from any folder of the project. ${(0, ui_1.bold)('list')} is about the
353
- organization: one database per project, and where each lives: ${(0, ui_1.bold)('platform')} is the common
354
- server, ${(0, ui_1.bold)('own "<name>"')} an own server of the organization. While an own server is not
355
- running, its databases do not answer, and that is not fixable from the code. An organization
356
- with own servers also gets them listed, with the room used on each: that is the choice for
357
- ${(0, ui_1.bold)('xflow init --server')} and ${(0, ui_1.bold)('db move')}.
358
-
359
- ${(0, ui_1.bold)('move')} takes the database of this project to another server of the organization:
360
- ${(0, ui_1.bold)('platform')} or an own server by name (or id, when two share a name). The platform copies
361
- the tables, switches the functions of the project and removes the old copy; nothing needs a
362
- deploy. While it copies, the apps read data but cannot save anything new, usually for a
363
- couple of minutes, which is why it asks for ${(0, ui_1.bold)('--yes')}. It also needs the
364
- right to move databases on the key: off by default, only its owner turns it on, in the
365
- platform settings where the key permissions live. The command follows the move to the end;
366
- stopping it does not stop the move, and ${(0, ui_1.bold)('xflow db status')} shows where it stands. A move
367
- that fails is rolled back. Buying, paying for and deleting servers stay with the person.
349
+ organization: one database per project, largest first, to see which project takes the room.
368
350
 
369
351
  ${(0, ui_1.bold)('schema')} answers a different question than the ${(0, ui_1.bold)('migrations/')} directory: the files say
370
352
  what you did, the schema says what is actually in there. ${(0, ui_1.bold)('query')} runs inside a READ ONLY transaction, so
@@ -776,8 +758,6 @@ change them, restyle them or delete what you do not use.
776
758
  --name <name> project name (the folder name by default)
777
759
  --app <folder> the folder of the first app (made from the name by default)
778
760
  --no-app a project without apps: its functions are workflows
779
- --server <name> the database of the project, right away on that server:
780
- platform, or an own server of the organization (xflow db list)
781
761
  --org <name> the organization that gets the project; required when you are
782
762
  signed in to several (xflow org)
783
763
 
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.4.0';
5
+ exports.CLI_VERSION = '1.5.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.4.0",
3
+ "version": "1.5.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": {