@getxflow/cli 0.16.0 → 0.17.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/deploy.js +4 -12
- package/dist/commands/projects.js +4 -7
- package/dist/commands/sources.js +18 -20
- package/dist/config.js +3 -1
- package/dist/template.js +10 -13
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/skills/xflow/SKILL.md +5 -4
package/dist/commands/deploy.js
CHANGED
|
@@ -4,8 +4,6 @@ exports.deploy = deploy;
|
|
|
4
4
|
exports.publish = publish;
|
|
5
5
|
exports.rollback = rollback;
|
|
6
6
|
exports.deployments = deployments;
|
|
7
|
-
const node_fs_1 = require("node:fs");
|
|
8
|
-
const node_path_1 = require("node:path");
|
|
9
7
|
const api_1 = require("../api");
|
|
10
8
|
const args_1 = require("../args");
|
|
11
9
|
const config_1 = require("../config");
|
|
@@ -47,21 +45,15 @@ async function waitForBuild(client, projectId, started) {
|
|
|
47
45
|
throw new errors_1.CliError('The build is taking longer than expected', `To check the state: xflow deployments. Version ${started.deploy_id}`);
|
|
48
46
|
}
|
|
49
47
|
/**
|
|
50
|
-
*
|
|
48
|
+
* Point the local .env at this project, function addresses included.
|
|
51
49
|
*
|
|
52
50
|
* The build bakes its own copy of the map into the bundle, this one is for
|
|
53
51
|
* `npm run dev`: without it the local run calls a function that it has no
|
|
54
52
|
* address for.
|
|
55
53
|
*/
|
|
56
|
-
async function
|
|
54
|
+
async function refreshPlatformEnv(root, client, projectId) {
|
|
57
55
|
const card = await (0, api_1.apiJson)(client, `/api/v1/projects/${projectId}`);
|
|
58
|
-
|
|
59
|
-
if (!(0, node_fs_1.existsSync)((0, node_path_1.join)(root, '.env')) && card.project_token) {
|
|
60
|
-
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(root, '.env'), (0, template_1.envFile)(card.project_token, client.apiUrl, card.functions), 'utf-8');
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
(0, template_1.writeEnvValue)(root, template_1.FUNCTIONS_ENV_KEY, (0, template_1.functionsEnvValue)(card.functions));
|
|
64
|
-
}
|
|
56
|
+
(0, template_1.writePlatformEnv)(root, card.project_token, client.apiUrl, card.functions);
|
|
65
57
|
return card.functions.filter((fn) => fn.invoke_url).map((fn) => fn.name);
|
|
66
58
|
}
|
|
67
59
|
/**
|
|
@@ -180,7 +172,7 @@ async function deploy(args) {
|
|
|
180
172
|
// The functions were shipped by the build itself, so their addresses are known
|
|
181
173
|
// only now. A failure here does not undo a finished build: warn and stop there.
|
|
182
174
|
try {
|
|
183
|
-
const functions = await
|
|
175
|
+
const functions = await refreshPlatformEnv(root, client, config.projectId);
|
|
184
176
|
if (functions.length > 0) {
|
|
185
177
|
(0, ui_1.note)((0, ui_1.dim)(` Functions in .env: ${functions.join(', ')}`));
|
|
186
178
|
}
|
|
@@ -77,7 +77,7 @@ async function init(args) {
|
|
|
77
77
|
write(target, file.path, titleFromProject(file.path, file.content, name));
|
|
78
78
|
for (const file of (0, template_1.scaffoldFiles)(name))
|
|
79
79
|
write(target, file.path, file.content);
|
|
80
|
-
|
|
80
|
+
(0, template_1.writePlatformEnv)(target, project.project_token, client.apiUrl);
|
|
81
81
|
const config = {
|
|
82
82
|
projectId: project.id,
|
|
83
83
|
build: { command: 'npm run build', dir: 'dist' },
|
|
@@ -159,11 +159,8 @@ async function link(args) {
|
|
|
159
159
|
(0, config_1.ignoreStateInGit)(dir);
|
|
160
160
|
if (client.organizationId)
|
|
161
161
|
(0, config_1.writeState)(dir, { organizationId: client.organizationId });
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
write(dir, '.env', (0, template_1.envFile)(card.project_token, client.apiUrl, card.functions));
|
|
165
|
-
(0, ui_1.note)((0, ui_1.dim)(' Created .env with the project token and the function addresses'));
|
|
166
|
-
}
|
|
162
|
+
(0, template_1.writePlatformEnv)(dir, card.project_token, client.apiUrl, card.functions);
|
|
163
|
+
(0, ui_1.note)((0, ui_1.dim)(' .env now points at this project'));
|
|
167
164
|
const skillReady = (0, skills_1.installSkillQuietly)(dir);
|
|
168
165
|
(0, ui_1.ok)(`The folder is linked to the project "${card.name}"`);
|
|
169
166
|
(0, ui_1.note)((0, ui_1.dim)(` ${(0, node_path_1.join)(dir, config_1.CONFIG_FILE)}`));
|
|
@@ -176,7 +173,7 @@ async function link(args) {
|
|
|
176
173
|
// compare against and stops.
|
|
177
174
|
try {
|
|
178
175
|
const { info, files } = await (0, sources_1.downloadRevision)(client, card.id, dir);
|
|
179
|
-
(0, config_1.writeState)(dir, {
|
|
176
|
+
(0, config_1.writeState)(dir, { treeHash: info.tree_hash });
|
|
180
177
|
(0, ui_1.ok)(`Revision ${info.revision}: ${files} files`);
|
|
181
178
|
}
|
|
182
179
|
catch (e) {
|
package/dist/commands/sources.js
CHANGED
|
@@ -74,33 +74,32 @@ async function confirmForce(client, projectId, server, local) {
|
|
|
74
74
|
async function pushSources(root, config, client, options) {
|
|
75
75
|
const tree = prepareTree(root, config);
|
|
76
76
|
const state = (0, config_1.readState)(root);
|
|
77
|
-
(0, ui_1.step)(`Sending ${tree.files.length} files (${(0, ui_1.formatBytes)(tree.archive.length)})`);
|
|
78
77
|
const server = await latestRevision(client, config.projectId);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
throw new errors_1.CliError(`The server holds revision ${server.revision} while this folder does not remember which version it worked from`, 'Fetch the server copy alongside and compare: xflow pull --into ./server-copy');
|
|
78
|
+
if (server && server.tree_hash === tree.hash) {
|
|
79
|
+
(0, config_1.writeState)(root, {
|
|
80
|
+
treeHash: server.tree_hash,
|
|
81
|
+
organizationId: client.organizationId ?? undefined,
|
|
82
|
+
});
|
|
83
|
+
(0, ui_1.ok)(`No changes, revision ${server.revision}`);
|
|
84
|
+
return { revision: server.revision, status: 'unchanged' };
|
|
85
|
+
}
|
|
86
|
+
// Asked before the upload, so a doomed archive never leaves the machine.
|
|
87
|
+
if (server && server.tree_hash !== state.treeHash && !options.force) {
|
|
88
|
+
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. ' +
|
|
89
|
+
'Fetch the server copy alongside and compare: xflow pull --into ./server-copy');
|
|
92
90
|
}
|
|
93
91
|
if (options.force && server && server.tree_hash !== tree.hash) {
|
|
94
92
|
await confirmForce(client, config.projectId, server, tree.files);
|
|
95
93
|
}
|
|
94
|
+
(0, ui_1.step)(`Sending ${tree.files.length} files (${(0, ui_1.formatBytes)(tree.archive.length)})`);
|
|
96
95
|
const headers = {};
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
// Just read from this project: the header only guards the gap until the upload lands.
|
|
97
|
+
if (server)
|
|
98
|
+
headers['X-Base-Revision'] = String(server.revision);
|
|
99
99
|
if (options.force)
|
|
100
100
|
headers['X-Force'] = 'true';
|
|
101
101
|
const result = await (0, api_1.apiUpload)(client, `/api/v1/projects/${config.projectId}/push`, tree.archive, headers);
|
|
102
102
|
(0, config_1.writeState)(root, {
|
|
103
|
-
revision: result.revision,
|
|
104
103
|
treeHash: result.tree_hash,
|
|
105
104
|
organizationId: client.organizationId ?? undefined,
|
|
106
105
|
});
|
|
@@ -165,7 +164,6 @@ async function pull(args) {
|
|
|
165
164
|
// Only the working folder tracks revisions; a side copy (--into) must not reset them.
|
|
166
165
|
if (!into) {
|
|
167
166
|
(0, config_1.writeState)(root, {
|
|
168
|
-
revision: info.revision,
|
|
169
167
|
treeHash: info.tree_hash,
|
|
170
168
|
organizationId: client.organizationId ?? undefined,
|
|
171
169
|
});
|
|
@@ -196,8 +194,8 @@ async function status() {
|
|
|
196
194
|
if (server.tree_hash === tree.hash) {
|
|
197
195
|
(0, ui_1.out)('State: matches the server');
|
|
198
196
|
}
|
|
199
|
-
else if (
|
|
200
|
-
(0, ui_1.out)(`State: ${(0, ui_1.bold)('diverged')}, the server
|
|
197
|
+
else if (server.tree_hash !== state.treeHash) {
|
|
198
|
+
(0, ui_1.out)(`State: ${(0, ui_1.bold)('diverged')}, the server holds a version this folder has not seen`);
|
|
201
199
|
(0, ui_1.note)((0, ui_1.dim)(' Fetch the server copy alongside: xflow pull --into ./server-copy'));
|
|
202
200
|
}
|
|
203
201
|
else {
|
package/dist/config.js
CHANGED
|
@@ -82,7 +82,9 @@ function readState(root) {
|
|
|
82
82
|
if (!(0, node_fs_1.existsSync)(path))
|
|
83
83
|
return {};
|
|
84
84
|
try {
|
|
85
|
-
|
|
85
|
+
// Read by field, so that keys older versions wrote leave the file on the next write.
|
|
86
|
+
const { treeHash, organizationId } = JSON.parse((0, node_fs_1.readFileSync)(path, 'utf-8'));
|
|
87
|
+
return { treeHash, organizationId };
|
|
86
88
|
}
|
|
87
89
|
catch {
|
|
88
90
|
return {};
|
package/dist/template.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.functionsEnvValue = functionsEnvValue;
|
|
5
|
-
exports.envFile = envFile;
|
|
6
|
-
exports.writeEnvValue = writeEnvValue;
|
|
3
|
+
exports.writePlatformEnv = writePlatformEnv;
|
|
7
4
|
exports.scaffoldFiles = scaffoldFiles;
|
|
8
5
|
const node_fs_1 = require("node:fs");
|
|
9
6
|
const node_path_1 = require("node:path");
|
|
@@ -32,7 +29,7 @@ tokens from \`src/index.css\` break that consistency.
|
|
|
32
29
|
Useful: \`xflow status\` for what is on the server, \`xflow deployments\` for version history.
|
|
33
30
|
`;
|
|
34
31
|
/** Function name → URL map, one JSON line. */
|
|
35
|
-
|
|
32
|
+
const FUNCTIONS_ENV_KEY = 'VITE_XFLOW_FUNCTIONS';
|
|
36
33
|
function functionsEnvValue(functions) {
|
|
37
34
|
const map = {};
|
|
38
35
|
for (const fn of functions) {
|
|
@@ -41,14 +38,14 @@ function functionsEnvValue(functions) {
|
|
|
41
38
|
}
|
|
42
39
|
return JSON.stringify(map);
|
|
43
40
|
}
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Point .env at the current project: written on every link and deploy, so that `npm run dev`
|
|
43
|
+
* never keeps talking to the previous one. Variables of your own are left alone.
|
|
44
|
+
*/
|
|
45
|
+
function writePlatformEnv(root, projectToken, apiUrl, functions = []) {
|
|
46
|
+
writeEnvValue(root, 'VITE_XFLOW_PROJECT_TOKEN', projectToken ?? '');
|
|
47
|
+
writeEnvValue(root, 'VITE_XFLOW_API_URL', apiUrl.replace(/^https?:\/\//, ''));
|
|
48
|
+
writeEnvValue(root, FUNCTIONS_ENV_KEY, functionsEnvValue(functions));
|
|
52
49
|
}
|
|
53
50
|
/** Update one variable in .env without touching the rest of the file. */
|
|
54
51
|
function writeEnvValue(root, key, value) {
|
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.17.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
|
@@ -24,7 +24,7 @@ contains the fix.
|
|
|
24
24
|
|
|
25
25
|
## Keeping these instructions current
|
|
26
26
|
|
|
27
|
-
These instructions ship with xflow CLI 0.
|
|
27
|
+
These instructions ship with xflow CLI 0.17.0. They travel inside the package, so the copy
|
|
28
28
|
you are reading can be older than the CLI answering your commands, and nothing about that
|
|
29
29
|
is visible in the text itself.
|
|
30
30
|
|
|
@@ -122,9 +122,10 @@ the very first version of a project: it publishes automatically, since there is
|
|
|
122
122
|
live version to protect yet.
|
|
123
123
|
|
|
124
124
|
`xflow status` says how the local copy differs from the server revision. When a deploy is
|
|
125
|
-
rejected before the build starts, the server
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
rejected before the build starts, the server holds a version this folder has never seen:
|
|
126
|
+
somebody deployed first, or the folder was linked to a different project along the way.
|
|
127
|
+
Fetch the server work next to yours (`xflow pull --into ./server-copy`), merge it in git,
|
|
128
|
+
deploy again. `--force` destroys that work, a last resort rather than a way around the error.
|
|
128
129
|
|
|
129
130
|
**Only pages are versioned.** Cloud functions and the database are one per project: they
|
|
130
131
|
are not versioned, and dev and live share them. So `xflow deploy` changes the running
|