@getxflow/cli 0.21.0 → 0.23.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 +21 -8
- package/dist/commands/deploy.js +9 -6
- package/dist/commands/dev.js +11 -0
- package/dist/commands/sources.js +32 -24
- package/dist/help.js +10 -3
- package/dist/json-arg.js +2 -5
- package/dist/template.js +12 -3
- package/dist/text-file.js +23 -0
- package/dist/tree.js +12 -0
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/skills/xflow/SKILL.md +10 -6
package/dist/commands/db.js
CHANGED
|
@@ -13,6 +13,7 @@ const args_1 = require("../args");
|
|
|
13
13
|
const config_1 = require("../config");
|
|
14
14
|
const errors_1 = require("../errors");
|
|
15
15
|
const session_1 = require("../session");
|
|
16
|
+
const text_file_1 = require("../text-file");
|
|
16
17
|
const ui_1 = require("../ui");
|
|
17
18
|
/** Migration files live in the repo; the applied history lives in the database. */
|
|
18
19
|
const MIGRATIONS_DIR = 'migrations';
|
|
@@ -92,12 +93,18 @@ async function dbStatus(args) {
|
|
|
92
93
|
(0, ui_1.warn)('A changed file will not be applied again: write a new migration');
|
|
93
94
|
}
|
|
94
95
|
}
|
|
96
|
+
function attachedProjects(database) {
|
|
97
|
+
const names = database.projects.map((project) => project.name);
|
|
98
|
+
if (database.hidden_projects > 0)
|
|
99
|
+
names.push(`+${database.hidden_projects} without access`);
|
|
100
|
+
return names.length === 0 ? 'no projects' : names.join(', ');
|
|
101
|
+
}
|
|
95
102
|
/**
|
|
96
103
|
* Logical databases of the organization, and who is attached to them.
|
|
97
104
|
*
|
|
98
105
|
* The identifier is what `xflow init --database <id>` asks for, and the list of
|
|
99
|
-
* projects is not decoration: a database
|
|
100
|
-
* one application lands in the others too.
|
|
106
|
+
* projects is not decoration: a database can be shared, so a migration written
|
|
107
|
+
* for one application lands in the others too.
|
|
101
108
|
*/
|
|
102
109
|
async function dbList() {
|
|
103
110
|
const client = (0, session_1.connect)((0, config_1.localConfig)());
|
|
@@ -111,13 +118,15 @@ async function dbList() {
|
|
|
111
118
|
database.name,
|
|
112
119
|
database.id,
|
|
113
120
|
database.schema ?? '-',
|
|
114
|
-
database
|
|
115
|
-
? 'no projects'
|
|
116
|
-
: database.projects.map((project) => project.name).join(', '),
|
|
121
|
+
attachedProjects(database),
|
|
117
122
|
]));
|
|
118
123
|
if (data.truncated)
|
|
119
124
|
(0, ui_1.note)((0, ui_1.dim)(` Shown ${data.databases.length} of ${data.total}`));
|
|
120
|
-
|
|
125
|
+
for (const database of data.databases) {
|
|
126
|
+
const attached = database.projects.length + database.hidden_projects;
|
|
127
|
+
if (attached > 1)
|
|
128
|
+
(0, ui_1.note)((0, ui_1.dim)(` ${database.name} is shared by ${attached} projects: a migration affects all of them`));
|
|
129
|
+
}
|
|
121
130
|
}
|
|
122
131
|
/** Long values would tear the columns apart; the cut is marked, never silent. */
|
|
123
132
|
const CELL_LIMIT = 60;
|
|
@@ -179,9 +188,13 @@ async function dbSchema(args) {
|
|
|
179
188
|
* database itself rather than by us guessing at the text of the SQL.
|
|
180
189
|
*/
|
|
181
190
|
async function dbQuery(args) {
|
|
182
|
-
const
|
|
191
|
+
const typed = args.words.join(' ').trim();
|
|
192
|
+
const sql = /\.sql$/i.test(typed)
|
|
193
|
+
? (0, text_file_1.readTextFile)(typed, 'An argument ending in .sql is read as a file: check the path, or end the query with ;')
|
|
194
|
+
: typed;
|
|
183
195
|
if (!sql) {
|
|
184
|
-
throw new errors_1.CliError('A query is required', 'For example: xflow db query "select * from orders order by created_at desc limit 20"'
|
|
196
|
+
throw new errors_1.CliError(typed ? `${typed} is empty` : 'A query is required', 'For example: xflow db query "select * from orders order by created_at desc limit 20", ' +
|
|
197
|
+
'or a long one from a file outside the project: xflow db query ../report.sql');
|
|
185
198
|
}
|
|
186
199
|
const { projectId, client } = await (0, session_1.projectTarget)(args);
|
|
187
200
|
const data = await (0, api_1.apiJson)(client, `/api/v1/projects/${projectId}/db/query`, {
|
package/dist/commands/deploy.js
CHANGED
|
@@ -171,17 +171,20 @@ async function deploy(args) {
|
|
|
171
171
|
(0, ui_1.out)(build.project_url);
|
|
172
172
|
// The functions were shipped by the build itself, so their addresses are known
|
|
173
173
|
// only now. A failure here does not undo a finished build: warn and stop there.
|
|
174
|
+
let functions = null;
|
|
174
175
|
try {
|
|
175
|
-
|
|
176
|
-
if (functions.length > 0) {
|
|
177
|
-
(0, ui_1.note)((0, ui_1.dim)(` Functions in .env: ${functions.join(', ')}`));
|
|
178
|
-
}
|
|
176
|
+
functions = await refreshPlatformEnv(root, client, config.projectId);
|
|
179
177
|
}
|
|
180
178
|
catch {
|
|
181
179
|
(0, ui_1.warn)('Could not refresh the function addresses in .env, the build itself is fine');
|
|
182
180
|
}
|
|
183
|
-
|
|
184
|
-
|
|
181
|
+
if (functions === null) {
|
|
182
|
+
(0, ui_1.note)((0, ui_1.dim)(' The cloud functions of this build are already live: publishing does not hold them back'));
|
|
183
|
+
}
|
|
184
|
+
else if (functions.length > 0) {
|
|
185
|
+
(0, ui_1.note)((0, ui_1.dim)(` Cloud functions live now: ${functions.join(', ')}`));
|
|
186
|
+
(0, ui_1.note)((0, ui_1.dim)(' There is one copy per project, publishing does not hold them back'));
|
|
187
|
+
}
|
|
185
188
|
(0, ui_1.note)((0, ui_1.dim)(' Show the new pages to visitors: xflow publish'));
|
|
186
189
|
}
|
|
187
190
|
async function publish(args) {
|
package/dist/commands/dev.js
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.dev = dev;
|
|
4
4
|
const node_os_1 = require("node:os");
|
|
5
|
+
const api_1 = require("../api");
|
|
5
6
|
const config_1 = require("../config");
|
|
6
7
|
const errors_1 = require("../errors");
|
|
7
8
|
const session_1 = require("../session");
|
|
8
9
|
const spawn_1 = require("../spawn");
|
|
9
10
|
const template_1 = require("../template");
|
|
11
|
+
const ui_1 = require("../ui");
|
|
10
12
|
/**
|
|
11
13
|
* The local dev server, handed the key this CLI picks for the project. The template
|
|
12
14
|
* proxy trades it for a pass on every function call, so which organization holds the
|
|
@@ -24,6 +26,15 @@ async function dev() {
|
|
|
24
26
|
// .env: pinned here, the key goes only to the platform that issued it.
|
|
25
27
|
VITE_XFLOW_API_URL: (0, template_1.envApiUrl)(client.apiUrl),
|
|
26
28
|
};
|
|
29
|
+
// .env only moves on link and deploy: a function someone else shipped, or a new
|
|
30
|
+
// project token, would reach this run only after one of them.
|
|
31
|
+
try {
|
|
32
|
+
const card = await (0, api_1.apiJson)(client, `/api/v1/projects/${config.projectId}`);
|
|
33
|
+
Object.assign(env, (0, template_1.platformEnv)(card.project_token, client.apiUrl, card.functions));
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
(0, ui_1.warn)(`The function addresses were not fetched (${e instanceof Error ? e.message : String(e)}): the ones in .env are used`);
|
|
37
|
+
}
|
|
27
38
|
// Ctrl+C reaches the server on its own; staying alive until it is gone is what
|
|
28
39
|
// hands its exit code back instead of leaving it to print over the prompt.
|
|
29
40
|
process.on('SIGINT', () => { });
|
package/dist/commands/sources.js
CHANGED
|
@@ -75,21 +75,27 @@ async function pushSources(root, config, client, options) {
|
|
|
75
75
|
const tree = prepareTree(root, config);
|
|
76
76
|
const state = (0, config_1.readState)(root);
|
|
77
77
|
const server = await latestRevision(client, config.projectId);
|
|
78
|
-
if (server
|
|
79
|
-
(0,
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
78
|
+
if (server) {
|
|
79
|
+
const relation = (0, tree_1.syncState)(tree.hash, server.tree_hash, state.treeHash);
|
|
80
|
+
if (relation === 'matches') {
|
|
81
|
+
(0, config_1.writeState)(root, {
|
|
82
|
+
treeHash: server.tree_hash,
|
|
83
|
+
organizationId: client.organizationId ?? undefined,
|
|
84
|
+
});
|
|
85
|
+
(0, ui_1.ok)(`No changes, revision ${server.revision}`);
|
|
86
|
+
return { revision: server.revision, status: 'unchanged' };
|
|
87
|
+
}
|
|
88
|
+
// Asked before the upload, so a doomed archive never leaves the machine.
|
|
89
|
+
if (relation === 'diverged' && !options.force) {
|
|
90
|
+
throw new errors_1.CliError(`The server holds revision ${server.revision}, and this folder does not stand on it`, 'Someone deployed before you, or this folder worked on a different project. ' +
|
|
91
|
+
'Fetch the server copy alongside and compare: xflow pull --into ./server-copy');
|
|
92
|
+
}
|
|
93
|
+
if (options.force) {
|
|
94
|
+
await confirmForce(client, config.projectId, server, tree.files);
|
|
95
|
+
}
|
|
96
|
+
else if (relation === 'unrecorded') {
|
|
97
|
+
(0, ui_1.warn)(`This folder has no record of the server revision: the code goes up on top of revision ${server.revision}`);
|
|
98
|
+
}
|
|
93
99
|
}
|
|
94
100
|
(0, ui_1.step)(`Sending ${tree.files.length} files (${(0, ui_1.formatBytes)(tree.archive.length)})`);
|
|
95
101
|
const headers = {};
|
|
@@ -161,13 +167,10 @@ async function pull(args) {
|
|
|
161
167
|
'xflow pull --into ./server-copy, or overwrite the folder completely: xflow pull --force');
|
|
162
168
|
}
|
|
163
169
|
const { info, files } = await downloadRevision(client, config.projectId, target, (0, args_1.flagNumber)(args, 'revision'));
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
organizationId: client.organizationId ?? undefined,
|
|
169
|
-
});
|
|
170
|
-
}
|
|
170
|
+
(0, config_1.writeState)(target, {
|
|
171
|
+
treeHash: info.tree_hash,
|
|
172
|
+
organizationId: client.organizationId ?? undefined,
|
|
173
|
+
});
|
|
171
174
|
(0, ui_1.ok)(`Revision ${info.revision}: ${files} files in ${target}`);
|
|
172
175
|
}
|
|
173
176
|
async function status() {
|
|
@@ -191,13 +194,18 @@ async function status() {
|
|
|
191
194
|
}
|
|
192
195
|
else {
|
|
193
196
|
(0, ui_1.out)(`On the server: revision ${server.revision}, ${server.file_count} files, ${(0, ui_1.formatAge)(server.created_at)}`);
|
|
194
|
-
|
|
197
|
+
const relation = (0, tree_1.syncState)(tree.hash, server.tree_hash, state.treeHash);
|
|
198
|
+
if (relation === 'matches') {
|
|
195
199
|
(0, ui_1.out)('State: matches the server');
|
|
196
200
|
}
|
|
197
|
-
else if (
|
|
201
|
+
else if (relation === 'diverged') {
|
|
198
202
|
(0, ui_1.out)(`State: ${(0, ui_1.bold)('diverged')}, the server holds a version this folder has not seen`);
|
|
199
203
|
(0, ui_1.note)((0, ui_1.dim)(' Fetch the server copy alongside: xflow pull --into ./server-copy'));
|
|
200
204
|
}
|
|
205
|
+
else if (relation === 'unrecorded') {
|
|
206
|
+
(0, ui_1.out)(`State: ${(0, ui_1.bold)('differs')}, this folder has no record of the server revision`);
|
|
207
|
+
(0, ui_1.note)((0, ui_1.dim)(` xflow deploy sends this code on top of revision ${server.revision}; to compare first: xflow pull --into ./server-copy`));
|
|
208
|
+
}
|
|
201
209
|
else {
|
|
202
210
|
(0, ui_1.out)(`State: ${(0, ui_1.bold)('local changes')}`);
|
|
203
211
|
(0, ui_1.note)((0, ui_1.dim)(' Send and build them: xflow deploy'));
|
package/dist/help.js
CHANGED
|
@@ -232,6 +232,7 @@ will work.`,
|
|
|
232
232
|
xflow db schema [table] tables of the project, or the columns of one
|
|
233
233
|
xflow db query "select ..." read data (--limit N: 50 by default, 300 at most;
|
|
234
234
|
--json prints the values whole)
|
|
235
|
+
xflow db query <file>.sql the same, the query read from a file
|
|
235
236
|
xflow db migrate apply migrations/*.sql
|
|
236
237
|
|
|
237
238
|
${(0, ui_1.bold)('list')} is about the organization, not about this project: it names the logical
|
|
@@ -241,7 +242,9 @@ attached to each one. That last column is the one to read before a migration.
|
|
|
241
242
|
${(0, ui_1.bold)('schema')} answers a different question than the ${(0, ui_1.bold)('migrations/')} directory: one logical
|
|
242
243
|
database is shared by several projects, so the files say what you did, and the schema
|
|
243
244
|
says what is actually in there. ${(0, ui_1.bold)('query')} runs inside a READ ONLY transaction, so
|
|
244
|
-
writes are rejected by the database itself, not by us reading your SQL.
|
|
245
|
+
writes are rejected by the database itself, not by us reading your SQL. A query with
|
|
246
|
+
double quotes goes in a .sql file outside the project: Windows PowerShell strips them from
|
|
247
|
+
arguments, and a file in migrations/ would be taken for a migration. Its table cuts
|
|
245
248
|
long values to keep the columns readable; ${(0, ui_1.bold)('--json')} prints them whole, together with
|
|
246
249
|
the applied limit and its ceiling. ${(0, ui_1.bold)('status')} named by id (${(0, ui_1.bold)('--project')}) answers with
|
|
247
250
|
the applied history alone: comparing against ${(0, ui_1.bold)('migrations/')} needs the folder.
|
|
@@ -546,7 +549,9 @@ calls fail, or go out under the key of whichever organization is active.
|
|
|
546
549
|
|
|
547
550
|
The platform address comes from the CLI too, not from .env, so the key goes to the
|
|
548
551
|
platform that issued it and nowhere else. It never reaches the browser: the page gets
|
|
549
|
-
only the ${(0, ui_1.bold)('VITE_')} variables
|
|
552
|
+
only the ${(0, ui_1.bold)('VITE_')} variables. The project token and the function addresses are
|
|
553
|
+
fetched on every start as well: a function someone else shipped answers here without a
|
|
554
|
+
deploy of yours.`,
|
|
550
555
|
pull: `${(0, ui_1.bold)('xflow pull')}: fetch the sources
|
|
551
556
|
|
|
552
557
|
By default it fetches the latest revision into the project folder and refuses to write
|
|
@@ -575,7 +580,9 @@ ignore field of xflow.json.
|
|
|
575
580
|
If the server holds a revision newer than the one you worked from, the sources are
|
|
576
581
|
rejected and the build does not start. That means somebody deployed before you: fetch
|
|
577
582
|
their changes alongside (${(0, ui_1.bold)('xflow pull --into ./server-copy')}), merge them in git on
|
|
578
|
-
your side, and retry.
|
|
583
|
+
your side, and retry. A folder with no record of the server revision (a fresh clone, a CI
|
|
584
|
+
checkout) is not rejected: its first deploy goes on top of the current revision and says
|
|
585
|
+
so, the check applies from the next one. ${(0, ui_1.bold)('--force')} overwrites the server revision, and before that the
|
|
579
586
|
CLI shows whose work you are about to destroy and asks for confirmation by typing the
|
|
580
587
|
project name. In a non-interactive run (CI, an agent) there is no way to confirm: a
|
|
581
588
|
version conflict has to fail the build rather than silently destroy somebody else's work.
|
package/dist/json-arg.js
CHANGED
|
@@ -5,9 +5,9 @@ exports.jsonHint = jsonHint;
|
|
|
5
5
|
exports.bodyArg = bodyArg;
|
|
6
6
|
exports.assertJson = assertJson;
|
|
7
7
|
exports.jsonArg = jsonArg;
|
|
8
|
-
const node_fs_1 = require("node:fs");
|
|
9
8
|
const args_1 = require("./args");
|
|
10
9
|
const errors_1 = require("./errors");
|
|
10
|
+
const text_file_1 = require("./text-file");
|
|
11
11
|
/**
|
|
12
12
|
* A JSON body typed on the command line: --data for a call, --payload for a
|
|
13
13
|
* schedule. Checked here, before it leaves: PowerShell strips the double quotes
|
|
@@ -59,10 +59,7 @@ function bodyArg(args, flag) {
|
|
|
59
59
|
throw new errors_1.CliError(`--${flag} and --${fileFlag} cannot be used together`, 'Give the body once');
|
|
60
60
|
}
|
|
61
61
|
if (path !== undefined) {
|
|
62
|
-
|
|
63
|
-
throw new errors_1.CliError(`No file ${path}`, `--${fileFlag} takes the path to a file with the body`);
|
|
64
|
-
}
|
|
65
|
-
return { text: (0, node_fs_1.readFileSync)(path, 'utf-8').trim(), fromFile: path };
|
|
62
|
+
return { text: (0, text_file_1.readTextFile)(path, `--${fileFlag} takes the path to a file with the body`), fromFile: path };
|
|
66
63
|
}
|
|
67
64
|
if (inline === undefined)
|
|
68
65
|
return undefined;
|
package/dist/template.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.envApiUrl = envApiUrl;
|
|
4
|
+
exports.platformEnv = platformEnv;
|
|
4
5
|
exports.writePlatformEnv = writePlatformEnv;
|
|
5
6
|
exports.scaffoldFiles = scaffoldFiles;
|
|
6
7
|
const node_fs_1 = require("node:fs");
|
|
@@ -43,14 +44,22 @@ function functionsEnvValue(functions) {
|
|
|
43
44
|
function envApiUrl(apiUrl) {
|
|
44
45
|
return apiUrl.replace(/^https?:\/\//, '');
|
|
45
46
|
}
|
|
47
|
+
/** The variables that point a local run at the project: written to .env, handed to xflow dev. */
|
|
48
|
+
function platformEnv(projectToken, apiUrl, functions = []) {
|
|
49
|
+
return {
|
|
50
|
+
VITE_XFLOW_PROJECT_TOKEN: projectToken ?? '',
|
|
51
|
+
VITE_XFLOW_API_URL: envApiUrl(apiUrl),
|
|
52
|
+
[FUNCTIONS_ENV_KEY]: functionsEnvValue(functions),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
46
55
|
/**
|
|
47
56
|
* Point .env at the current project: written on every link and deploy, so that `npm run dev`
|
|
48
57
|
* never keeps talking to the previous one. Variables of your own are left alone.
|
|
49
58
|
*/
|
|
50
59
|
function writePlatformEnv(root, projectToken, apiUrl, functions = []) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
60
|
+
for (const [key, value] of Object.entries(platformEnv(projectToken, apiUrl, functions))) {
|
|
61
|
+
writeEnvValue(root, key, value);
|
|
62
|
+
}
|
|
54
63
|
}
|
|
55
64
|
/** Update one variable in .env without touching the rest of the file. */
|
|
56
65
|
function writeEnvValue(root, key, value) {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readTextFile = readTextFile;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_util_1 = require("node:util");
|
|
6
|
+
const errors_1 = require("./errors");
|
|
7
|
+
/**
|
|
8
|
+
* A file the agent wrote, whatever the shell saved it in. Windows PowerShell 5.1 writes
|
|
9
|
+
* UTF-16 with `>` and Out-File, and the code page with Set-Content: read as UTF-8, the
|
|
10
|
+
* first is garbage and the second turns non-latin text into U+FFFD without a word.
|
|
11
|
+
*/
|
|
12
|
+
function readTextFile(path, missingHint) {
|
|
13
|
+
if (!(0, node_fs_1.statSync)(path, { throwIfNoEntry: false })?.isFile())
|
|
14
|
+
throw new errors_1.CliError(`No file ${path}`, missingHint);
|
|
15
|
+
const bytes = (0, node_fs_1.readFileSync)(path);
|
|
16
|
+
const encoding = bytes[0] === 0xff && bytes[1] === 0xfe ? 'utf-16le' : 'utf-8';
|
|
17
|
+
try {
|
|
18
|
+
return new node_util_1.TextDecoder(encoding, { fatal: true }).decode(bytes).trim();
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
throw new errors_1.CliError(`${path} is not UTF-8`, 'Save it as UTF-8, in Windows PowerShell: Set-Content -Encoding utf8');
|
|
22
|
+
}
|
|
23
|
+
}
|
package/dist/tree.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.isSkillCopy = isSkillCopy;
|
|
4
4
|
exports.loadIgnoreRules = loadIgnoreRules;
|
|
5
5
|
exports.collectFiles = collectFiles;
|
|
6
|
+
exports.syncState = syncState;
|
|
6
7
|
exports.treeHash = treeHash;
|
|
7
8
|
exports.heaviest = heaviest;
|
|
8
9
|
const node_crypto_1 = require("node:crypto");
|
|
@@ -130,6 +131,17 @@ function collectFiles(root, rules) {
|
|
|
130
131
|
files.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
|
131
132
|
return { files, skippedLinks };
|
|
132
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* How the folder stands against the server tree. A folder with no record (a fresh
|
|
136
|
+
* clone, a CI checkout) has not diverged: nothing is known about what it started from.
|
|
137
|
+
*/
|
|
138
|
+
function syncState(local, server, remembered) {
|
|
139
|
+
if (local === server)
|
|
140
|
+
return 'matches';
|
|
141
|
+
if (remembered === undefined)
|
|
142
|
+
return 'unrecorded';
|
|
143
|
+
return remembered === server ? 'local-changes' : 'diverged';
|
|
144
|
+
}
|
|
133
145
|
/** Computed identically on the server. */
|
|
134
146
|
function treeHash(files) {
|
|
135
147
|
const parts = files.map((file) => `${file.path}\0${(0, node_crypto_1.createHash)('sha256').update(file.content).digest('hex')}`);
|
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 = '0.
|
|
5
|
+
exports.CLI_VERSION = '0.23.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
|
@@ -22,7 +22,7 @@ contains the fix.
|
|
|
22
22
|
|
|
23
23
|
## Keeping these instructions current
|
|
24
24
|
|
|
25
|
-
These instructions ship with xflow CLI 0.
|
|
25
|
+
These instructions ship with xflow CLI 0.23.0, so the copy you are reading can be older than
|
|
26
26
|
the CLI answering your commands.
|
|
27
27
|
|
|
28
28
|
When the CLI says this file does not match it, run `xflow skills --refresh` and then **read
|
|
@@ -108,7 +108,9 @@ one exception is the very first version of a project, which publishes automatica
|
|
|
108
108
|
`xflow status` says how the local copy differs from the server revision. A deploy rejected
|
|
109
109
|
before the build starts means the server holds a version this folder has never seen: somebody
|
|
110
110
|
deployed first, or the folder was linked elsewhere. Fetch it next to yours (`xflow pull --into
|
|
111
|
-
./server-copy`), merge in git, deploy again. `--force` destroys that work.
|
|
111
|
+
./server-copy`), merge in git, deploy again. `--force` destroys that work. A folder with no
|
|
112
|
+
record of the server revision (a fresh clone, a CI checkout) is not rejected: its first deploy
|
|
113
|
+
goes on top of the current revision and says so, the check applies from the next one.
|
|
112
114
|
|
|
113
115
|
**Only pages are versioned.** Cloud functions and the database are one per project: they
|
|
114
116
|
are not versioned, and dev and live share them. So `xflow deploy` changes the running
|
|
@@ -270,7 +272,7 @@ the interface, not a check.
|
|
|
270
272
|
A function that changes data should say so instead of checking the role by hand:
|
|
271
273
|
|
|
272
274
|
```js
|
|
273
|
-
exports.minRole = '
|
|
275
|
+
exports.minRole = 'developer' // 'member' | 'developer' | 'owner'
|
|
274
276
|
```
|
|
275
277
|
|
|
276
278
|
The wrapper refuses anything below that role before your code runs. Without the line every
|
|
@@ -429,8 +431,9 @@ do not see: `xflow db status` lists applied migrations with no file in your repo
|
|
|
429
431
|
somebody else's project), and `xflow db list` names who else is on yours. For the same reason
|
|
430
432
|
`migrations/` is not the schema. It says what you did; `xflow db schema [table]` says what is in
|
|
431
433
|
the database right now, and `xflow db query "select ..."` reads it inside a READ ONLY
|
|
432
|
-
transaction (50 rows by default, `--limit` up to 300; `--json` prints long values whole).
|
|
433
|
-
|
|
434
|
+
transaction (50 rows by default, `--limit` up to 300; `--json` prints long values whole). A query
|
|
435
|
+
with double quotes goes in a `.sql` file outside the project, `xflow db query ../q.sql`: PowerShell
|
|
436
|
+
strips them from arguments. Look before you write a migration against a shared database.
|
|
434
437
|
|
|
435
438
|
## File storage
|
|
436
439
|
|
|
@@ -598,7 +601,8 @@ A local run does not report anything: these logs exist for what you cannot open
|
|
|
598
601
|
own devtools.
|
|
599
602
|
|
|
600
603
|
Run the app locally with `xflow dev`, not a bare `npm run dev`: it hands the dev server
|
|
601
|
-
the key of the project's organization, which cloud functions need to answer
|
|
604
|
+
the key of the project's organization, which cloud functions need to answer, and their
|
|
605
|
+
current addresses, including functions someone else deployed.
|
|
602
606
|
|
|
603
607
|
## Environment
|
|
604
608
|
|