@getxflow/cli 1.4.0 → 1.6.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
@@ -25,7 +25,6 @@ const schedules_1 = require("./commands/schedules");
25
25
  const skills_1 = require("./commands/skills");
26
26
  const sources_1 = require("./commands/sources");
27
27
  const storage_1 = require("./commands/storage");
28
- const pages_1 = require("./commands/pages");
29
28
  const deploy_1 = require("./commands/deploy");
30
29
  const dev_1 = require("./commands/dev");
31
30
  const update_1 = require("./commands/update");
@@ -180,15 +179,11 @@ async function run(args) {
180
179
  await (0, db_1.dbExec)({ ...args, words: args.words.slice(2) });
181
180
  return;
182
181
  }
183
- if (second === 'move') {
184
- await (0, db_1.dbMove)(rest);
185
- return;
186
- }
187
182
  if (second === undefined || second === 'status') {
188
183
  await (0, db_1.dbStatus)({ ...args, words: args.words.slice(2) });
189
184
  return;
190
185
  }
191
- throw new errors_1.CliError(`Unknown command: db ${second}`, 'Available: status, list, schema, query, exec, migrate and move');
186
+ throw new errors_1.CliError(`Unknown command: db ${second}`, 'Available: status, list, schema, query, exec and migrate');
192
187
  case 'storage':
193
188
  if (second === 'push') {
194
189
  await (0, storage_1.storagePush)(rest);
@@ -203,32 +198,6 @@ async function run(args) {
203
198
  return;
204
199
  }
205
200
  throw new errors_1.CliError(`Unknown command: storage ${second}`, 'Available: ls, push and rm');
206
- case 'pages':
207
- if (second === 'create') {
208
- await (0, pages_1.pagesCreate)(rest);
209
- return;
210
- }
211
- if (second === 'deploy') {
212
- await (0, pages_1.pagesDeploy)(rest);
213
- return;
214
- }
215
- if (second === 'status') {
216
- await (0, pages_1.pagesStatus)(rest);
217
- return;
218
- }
219
- if (second === 'rollback') {
220
- await (0, pages_1.pagesRollback)(rest);
221
- return;
222
- }
223
- if (second === 'rm' || second === 'remove') {
224
- await (0, pages_1.pagesRemove)(rest);
225
- return;
226
- }
227
- if (second === undefined || second === 'list') {
228
- await (0, pages_1.pagesList)(rest);
229
- return;
230
- }
231
- throw new errors_1.CliError(`Unknown command: pages ${second}`, 'Available: list, create, deploy, status, rollback and rm');
232
201
  case 'status':
233
202
  await (0, sources_1.status)();
234
203
  return;
@@ -338,7 +307,7 @@ async function main() {
338
307
  // XFLOW_TOKEN is taken as given and no organization is looked for, so a
339
308
  // project of another one answers "not found" with nothing else to say.
340
309
  if (process.env.XFLOW_TOKEN?.trim()) {
341
- (0, ui_1.fatalNote)((0, ui_1.dim)(' XFLOW_TOKEN is set: it is the key of one organization and is used as it comes, so a project or a page of another one is not found'));
310
+ (0, ui_1.fatalNote)((0, ui_1.dim)(' XFLOW_TOKEN is set: it is the key of one organization and is used as it comes, so a project of another one is not found'));
342
311
  }
343
312
  else if (e.apiUrl && (0, credentials_1.listOrgs)(e.apiUrl).length > 1) {
344
313
  (0, ui_1.fatalNote)((0, ui_1.dim)(' Several organizations are signed in: the answer may live in another one, see xflow org'));
@@ -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);
@@ -1,6 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.versionComment = versionComment;
4
3
  exports.statedRevision = statedRevision;
5
4
  exports.deploy = deploy;
6
5
  exports.publish = publish;
@@ -70,7 +69,7 @@ async function startBuild(client, base, revision, allowRemovals, comment) {
70
69
  const MAX_COMMENT_LENGTH = 200;
71
70
  /** Control and bidi-override characters: they spoof terminals, not comments. */
72
71
  const COMMENT_CONTROL_CHARS = /[\u0000-\u001f\u007f-\u009f\u200e\u200f\u202a-\u202e\u2066-\u2069]/g;
73
- /** The version comment as typed after -m, cleaned; empty when there is none. Pages take the same. */
72
+ /** The version comment as typed after -m, cleaned; empty when there is none. */
74
73
  function versionComment(raw) {
75
74
  const comment = (raw ?? '').replace(/\s+/g, ' ').replace(COMMENT_CONTROL_CHARS, '').trim();
76
75
  if (comment.length > MAX_COMMENT_LENGTH) {
@@ -8,14 +8,12 @@ exports.envRemove = envRemove;
8
8
  const node_fs_1 = require("node:fs");
9
9
  const node_path_1 = require("node:path");
10
10
  const api_1 = require("../api");
11
- const args_1 = require("../args");
12
11
  const config_1 = require("../config");
13
12
  const errors_1 = require("../errors");
14
13
  const session_1 = require("../session");
15
14
  const ui_1 = require("../ui");
16
15
  const SCOPE_LABEL = {
17
- organization: 'the whole organization',
18
- project: 'this project only',
16
+ project: 'this project',
19
17
  connection: 'from a connected service, read-only',
20
18
  };
21
19
  const FUNCTIONS_DIR = 'functions';
@@ -101,7 +99,7 @@ async function envList(args) {
101
99
  (0, ui_1.note)((0, ui_1.dim)(' To store one: xflow env set SMTP_PASSWORD=secret'));
102
100
  return;
103
101
  }
104
- (0, ui_1.table)(rows.map((row) => [row.name, SCOPE_LABEL[row.scope] ?? SCOPE_LABEL.organization]));
102
+ (0, ui_1.table)(rows.map((row) => [row.name, SCOPE_LABEL[row.scope]]));
105
103
  (0, ui_1.note)((0, ui_1.dim)(' The platform never returns values: they are visible only inside the function'));
106
104
  }
107
105
  /** Check that every referenced variable is stored. */
@@ -172,12 +170,9 @@ async function envSet(args) {
172
170
  const value = pair.slice(pair.indexOf('=') + 1);
173
171
  const result = await (0, api_1.apiJson)(client, `/api/v1/projects/${config.projectId}/env`, {
174
172
  method: 'POST',
175
- // The word goes to the platform as typed: it knows the two scopes and
176
- // refuses a third. Guessing here would have made --scope proj mean the
177
- // whole organization, which is the opposite of what such a typo means.
178
- body: { name, value, scope: (0, args_1.flagString)(args, 'scope') },
173
+ body: { name, value },
179
174
  });
180
- (0, ui_1.ok)(`${(0, ui_1.bold)(result.name)} stored (${result.scope === 'project' ? 'this project only' : 'the whole organization'})`);
175
+ (0, ui_1.ok)(`${(0, ui_1.bold)(result.name)} stored`);
181
176
  // Values reach a function on its next deploy.
182
177
  const users = referencedByFunctions(root).needed.get(name);
183
178
  if (users && users.length > 0) {
@@ -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)
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.contentTypeFor = contentTypeFor;
4
- exports.collectMedia = collectMedia;
5
4
  exports.planBatches = planBatches;
6
5
  exports.caseCollisions = caseCollisions;
7
6
  exports.storageList = storageList;
@@ -75,7 +74,7 @@ function contentTypeFor(name) {
75
74
  * what one is asked to put there so it stays out of the 10 MB archive.
76
75
  *
77
76
  * Dot entries and symlinks are skipped: pointing push at a working copy should
78
- * not upload .git, and a link is not ours to follow. Pages walk a site the same way.
77
+ * not upload .git, and a link is not ours to follow.
79
78
  */
80
79
  function collectMedia(root, prefix = '') {
81
80
  const found = [];
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': [],
@@ -60,7 +60,7 @@ exports.KNOWN = {
60
60
  logs: ['limit', 'project', 'app'],
61
61
  env: ['project'],
62
62
  'env list': ['project'],
63
- 'env set': ['scope'],
63
+ 'env set': [],
64
64
  'env rm': ['project'],
65
65
  'env remove': ['project'],
66
66
  'env check': [],
@@ -81,21 +81,12 @@ 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'],
88
87
  'storage push': ['to', 'replace', 'json', 'project'],
89
88
  'storage rm': ['folder', 'yes', 'project'],
90
89
  'storage remove': ['folder', 'yes', 'project'],
91
- pages: ['json'],
92
- 'pages list': ['json'],
93
- 'pages create': ['slug'],
94
- 'pages deploy': ['message'],
95
- 'pages status': ['json'],
96
- 'pages rollback': [],
97
- 'pages rm': ['yes'],
98
- 'pages remove': ['yes'],
99
90
  status: [],
100
91
  dev: [],
101
92
  pull: ['into', 'force', 'revision'],
@@ -145,7 +136,6 @@ const NAMESPACES = new Set([
145
136
  'schedules',
146
137
  'db',
147
138
  'storage',
148
- 'pages',
149
139
  ]);
150
140
  /** The command these words name, as far as the table knows it. */
151
141
  function commandOf(words) {
package/dist/help.js CHANGED
@@ -91,24 +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
-
103
- ${(0, ui_1.bold)('Pages')}
104
- xflow pages public sites of the organization at <name>.xflowpage.ru
105
- xflow pages create <title> <folder|.zip|.html> [--slug name]
106
- a new page with the site on it, live at once
107
- xflow pages deploy <page> <folder|.zip|.html> [-m "what changed"]
108
- upload the site: live at once, there is no publish step
109
- xflow pages status <page> the page and its versions
110
- xflow pages rollback <page> <N> put an earlier version back on the address
111
- xflow pages rm <page> --yes delete the page for good: the address empties
112
100
 
113
101
  ${(0, ui_1.bold)('Reference')}
114
102
  xflow logs [--limit N] browser errors of the published apps
@@ -173,7 +161,7 @@ no working copy of its own:
173
161
  xflow db query --project 3f2a… 'select id, title from tasks where done = false'
174
162
  xflow functions invoke kanban --project 3f2a… --data-file card.json
175
163
 
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')},
164
+ 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
165
  ${(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
166
  ${(0, ui_1.bold)('connections')}, ${(0, ui_1.bold)('connections link')}, ${(0, ui_1.bold)('connections unlink')}, ${(0, ui_1.bold)('schedules')},
179
167
  ${(0, ui_1.bold)('schedules set')}, ${(0, ui_1.bold)('schedules rm')}, ${(0, ui_1.bold)('storage ls')}, ${(0, ui_1.bold)('storage push')},
@@ -264,53 +252,6 @@ how they differ, ${(0, ui_1.bold)('xflow pull --into ./server-copy')} fetches th
264
252
 
265
253
  In a folder that is already linked, the command points xflow.json at the other project and
266
254
  keeps the rest of the file. A new project instead of an existing one: ${(0, ui_1.bold)('xflow init')}.`,
267
- pages: `${(0, ui_1.bold)('xflow pages')}: public sites of the organization
268
-
269
- A page is a static site at https://<name>.xflowpage.ru: a landing page, a presentation, an
270
- invoice for people outside the organization. It opens by its address for anyone, without
271
- signing in, and has no build, no publish step and no database. An app is the opposite: code
272
- the platform builds, with versions, access levels and a database through functions.
273
-
274
- xflow pages the pages, their state and address
275
- xflow pages create <title> <path> a new page with its first version: there are no
276
- empty pages
277
- --slug <name> the name in the address; without it the name is made
278
- from the title, with a random tail
279
- xflow pages deploy <page> <path> upload a new version
280
- -m <text> what changed, one short line: it names the version
281
- xflow pages status <page> the page and its versions
282
- xflow pages rollback <page> <N> put version N back on the address
283
- xflow pages rm <page> delete the page
284
- --yes without it the command names what goes and stops
285
-
286
- ${(0, ui_1.bold)('<path>')} is a folder with index.html at the top, its .zip, or one .html file.
287
- ${(0, ui_1.bold)('<page>')} is the name from the address or the id. ${(0, ui_1.bold)('--json')} on list and status
288
- prints the answer as JSON. A page belongs to the organization, not to a project, so there is
289
- no ${(0, ui_1.bold)('--project')}: the key is picked as for every other command (${(0, ui_1.bold)('xflow help org')}).
290
-
291
- ${(0, ui_1.bold)('An upload is live at once.')} There is no draft and no publish step: anyone with the
292
- address sees the new version within seconds. Uploading the same files again changes nothing.
293
- The files of the last 5 versions are kept, so a rollback takes seconds and needs no upload.
294
-
295
- What goes up is the built site: HTML, CSS, JS, images, fonts, PDF, audio and video. No code
296
- runs on the platform side, and a folder with package.json is refused as the sources of a site:
297
- build it and upload what the build writes. Dot entries are skipped. At most 5 MB zipped, 25 MB
298
- unpacked and 1000 files.
299
-
300
- The page is public, so a file holding a secret key is refused whole: every visitor would read
301
- it. A page has no backend: a form or a lookup belongs in an app with access "anyone". The one
302
- key a page may hold is a function key, and it opens that function to every visitor: an HTML
303
- form can post to its webhook address with ${(0, ui_1.bold)('?key=')}, while fetch cannot read the answer
304
- (no CORS). Search engines are asked not to index pages, so do not promise SEO.
305
-
306
- ${(0, ui_1.bold)('Deleting is final.')} The address goes empty and every version of the page is gone: there
307
- is no undo and no copy on the platform side. The name stays reserved for the organization, so
308
- the same ${(0, ui_1.bold)('--slug')} is free for the next page of yours and taken for everyone else. A
309
- throwaway page put up to try something is worth deleting the same way it was made.
310
-
311
- A page takes the key of the whole organization, the one ${(0, ui_1.bold)('xflow login')} issues: a key
312
- scoped to projects is refused. A page is deleted by whoever made it, or by the owner of the
313
- organization. Switching a page off for a while is for a person, in the platform interface.`,
314
255
  update: `${(0, ui_1.bold)('xflow update')}: bring the CLI up to date
315
256
 
316
257
  Installs the published version and, if anything changed, rewrites the platform
@@ -336,8 +277,8 @@ is a hard gate on a changed contract, and until you run this command nothing els
336
277
  will work.`,
337
278
  db: `${(0, ui_1.bold)('xflow db')}: schema, data, migrations
338
279
 
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
280
+ xflow db status what is applied, what is waiting
281
+ xflow db list databases of the projects of the organization, with sizes
341
282
  xflow db schema [table] tables of the project, or the columns of one
342
283
  xflow db query 'select ...' read data (rows: the query's own limit, 50 without
343
284
  one, 300 at most; --json prints the values whole)
@@ -345,26 +286,10 @@ will work.`,
345
286
  xflow db exec 'update ...' a one-off data change: one INSERT, UPDATE, DELETE or MERGE
346
287
  (or <file>.sql; --allow-destructive, --json)
347
288
  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
289
 
351
290
  Every project has exactly one database of its own, shared by all of its apps and functions,
352
291
  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.
292
+ organization: one database per project, largest first, to see which project takes the room.
368
293
 
369
294
  ${(0, ui_1.bold)('schema')} answers a different question than the ${(0, ui_1.bold)('migrations/')} directory: the files say
370
295
  what you did, the schema says what is actually in there. ${(0, ui_1.bold)('query')} runs inside a READ ONLY transaction, so
@@ -481,11 +406,7 @@ the function. So keep your own copy wherever you got it from.
481
406
  xflow env set SMTP_PASSWORD=… store
482
407
  xflow env rm SMTP_PASSWORD delete
483
408
 
484
- --scope project visible to this project only
485
- --scope organization visible to every project of the organization (the default)
486
-
487
- Those two words are the whole list: a third is refused rather than read as the default,
488
- because a value meant for one project would have gone out to all of them.
409
+ A variable belongs to this project: functions of other projects never see it.
489
410
 
490
411
  ${(0, ui_1.bold)('check')} reads the sources in ${(0, ui_1.bold)('functions/')} and looks for ${(0, ui_1.bold)('process.env.NAME')}
491
412
  references. The same rule applies on deploy: a function receives only the variables it
@@ -627,8 +548,7 @@ Who may call a function is its callers, in the root xflow.json:
627
548
 
628
549
  { "projectId": "…", "functions": { "book-slot": { "callers": ["zapis"] } } }
629
550
 
630
- A caller is an app folder of this project (apps/zapis is "zapis"); pages are static and call
631
- nothing. A function nobody lists has no callers: a workflow, woken by a schedule, by a service
551
+ A caller is an app folder of this project (apps/zapis is "zapis"). A function nobody lists has no callers: a workflow, woken by a schedule, by a service
632
552
  holding a key a person issued, or by ${(0, ui_1.bold)('invoke')}. ${(0, ui_1.bold)('open')} in the list means a caller is an app
633
553
  anyone with the link opens, so anyone on the internet can call it. The address in the note
634
554
  under the table is for webhooks and other functions, with a key; apps call ${(0, ui_1.bold)('/_fn/<name>')} on
@@ -776,8 +696,6 @@ change them, restyle them or delete what you do not use.
776
696
  --name <name> project name (the folder name by default)
777
697
  --app <folder> the folder of the first app (made from the name by default)
778
698
  --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
699
  --org <name> the organization that gets the project; required when you are
782
700
  signed in to several (xflow org)
783
701
 
package/dist/limits.js CHANGED
@@ -4,12 +4,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.limitLine = limitLine;
5
5
  exports.quotaRows = quotaRows;
6
6
  const DENIAL_LABELS = {
7
- limit_projects: 'projects, the platform cap',
7
+ limit_projects: 'new projects today, the platform cap (resets at 00:00 UTC)',
8
8
  limit_apps: 'apps',
9
9
  plan_app_access: 'access levels of apps',
10
10
  functions_stopped: 'function minutes, the functions of the organization are stopped',
11
11
  limit_connections: 'active connector connections',
12
- limit_pages: 'public pages',
13
12
  limit_functions: 'cloud functions',
14
13
  limit_builds: 'builds this month',
15
14
  limit_builds_concurrent: 'builds running at once',
@@ -32,12 +31,12 @@ function limitLine(detail) {
32
31
  : detail.code === 'limit_schedule_interval'
33
32
  ? `, every ${detail.used} min against a ${detail.limit} min minimum`
34
33
  : `, ${detail.used} of ${detail.limit} used`;
35
- return `Plan limit: ${label}${numbers}. Repeating the same command will not help.`;
34
+ const tail = detail.code === 'limit_projects' ? 'Repeating it before then will not help.' : 'Repeating the same command will not help.';
35
+ return `Plan limit: ${label}${numbers}. ${tail}`;
36
36
  }
37
37
  const QUOTA_ORDER = [
38
38
  'apps',
39
39
  'connections',
40
- 'pages',
41
40
  'functions',
42
41
  'schedules',
43
42
  'people',
@@ -49,7 +48,6 @@ const QUOTA_ORDER = [
49
48
  const QUOTA_LABELS = {
50
49
  apps: 'Apps',
51
50
  connections: 'Active connections',
52
- pages: 'Public pages',
53
51
  functions: 'Cloud functions',
54
52
  schedules: 'Schedules',
55
53
  people: 'People',
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.6.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.6.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": {