@getxflow/cli 0.2.0 → 0.3.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
@@ -69,10 +69,6 @@ async function run(args) {
69
69
  }
70
70
  throw new errors_1.CliError(`Unknown command: mcp ${second}`, 'Available: install');
71
71
  case 'functions':
72
- // The command is gone, but silence would leave the habit unexplained.
73
- if (second === 'deploy') {
74
- throw new errors_1.CliError('Functions are deployed by the build now', 'Run xflow deploy: it ships the functions and then builds the application with their addresses');
75
- }
76
72
  if (second === 'invoke') {
77
73
  await (0, functions_1.functionsInvoke)(rest);
78
74
  return;
@@ -63,7 +63,7 @@ async function init(args) {
63
63
  }
64
64
  (0, ui_1.step)('Fetching the template');
65
65
  const archive = await (0, api_1.apiBinary)(client, `/api/v1/templates/${templateId}/archive`);
66
- const files = (0, zip_1.zipRead)(archive);
66
+ const files = (0, zip_1.dropDirectoryEntries)((0, zip_1.zipRead)(archive));
67
67
  (0, ui_1.step)('Creating the project on the platform');
68
68
  const project = await (0, api_1.apiJson)(client, '/api/v1/projects', {
69
69
  method: 'POST',
@@ -47,7 +47,7 @@ async function confirmForce(client, projectId, server, local) {
47
47
  (0, ui_1.warn)(`The server holds revision ${server.revision} from ${(0, ui_1.formatAge)(server.created_at)}, ${server.file_count} files`);
48
48
  try {
49
49
  const info = await (0, api_1.apiJson)(client, `/api/v1/projects/${projectId}/pull`);
50
- const serverPaths = new Set((0, zip_1.zipRead)(await (0, api_1.downloadUrl)(info.download_url)).map((e) => e.path));
50
+ const serverPaths = new Set((0, zip_1.dropDirectoryEntries)((0, zip_1.zipRead)(await (0, api_1.downloadUrl)(info.download_url))).map((e) => e.path));
51
51
  const localPaths = new Set(local.map((f) => f.path));
52
52
  const disappearing = [...serverPaths].filter((p) => !localPaths.has(p));
53
53
  if (disappearing.length > 0) {
@@ -132,7 +132,7 @@ async function pull(args) {
132
132
  (0, ui_1.warn)('The hash of the downloaded tree did not match the server one: the contents may have changed in transit');
133
133
  }
134
134
  (0, node_fs_1.mkdirSync)(target, { recursive: true });
135
- for (const entry of entries) {
135
+ for (const entry of (0, zip_1.dropDirectoryEntries)(entries)) {
136
136
  const path = (0, node_path_1.join)(target, entry.path);
137
137
  (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(path), { recursive: true });
138
138
  (0, node_fs_1.writeFileSync)(path, entry.content);
package/dist/template.js CHANGED
@@ -25,7 +25,7 @@ xflow publish # show the dev version to visitors
25
25
  \`\`\`
26
26
 
27
27
  The interface is built on the template components: \`src/components/ui\` (buttons, fields,
28
- dialogs) and \`src/components/blocks\` (table, form, filters, kanban, charts). Layout built
28
+ dialogs) and \`src/components/blocks\` (a data table and charts). Layout built
29
29
  on top of them looks like the rest of the platform, while custom colors in place of the
30
30
  tokens from \`src/index.css\` break that consistency.
31
31
 
package/dist/tree.js CHANGED
@@ -19,6 +19,9 @@ const DEFAULT_IGNORE = [
19
19
  '.env.*',
20
20
  '.DS_Store',
21
21
  'Thumbs.db',
22
+ // Compiler cache: rewritten by every local build, so it would make a new
23
+ // revision out of nothing.
24
+ 'tsconfig.tsbuildinfo',
22
25
  ];
23
26
  const IGNORE_FILE = '.xflowignore';
24
27
  function globToRegExpSource(pattern) {
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.2.0';
5
+ exports.CLI_VERSION = '0.3.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/dist/zip.js CHANGED
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ZipError = void 0;
4
4
  exports.zipCreate = zipCreate;
5
+ exports.dropDirectoryEntries = dropDirectoryEntries;
5
6
  exports.zipRead = zipRead;
6
7
  const node_zlib_1 = require("node:zlib");
7
8
  class ZipError extends Error {
@@ -122,6 +123,25 @@ function isSafeEntryPath(path) {
122
123
  }
123
124
  return path.split('/').every((part) => part !== '' && part !== '.' && part !== '..');
124
125
  }
126
+ /**
127
+ * Drop entries whose name is a directory elsewhere in the same archive: old
128
+ * revisions carry folders as empty files, and unpacking them collides with the
129
+ * directory of the same name. Call it before writing to disk, not inside
130
+ * zipRead: the tree hash is checked against the archive as the server made it.
131
+ */
132
+ function dropDirectoryEntries(entries) {
133
+ const directories = new Set();
134
+ for (const entry of entries) {
135
+ const segments = entry.path.split('/');
136
+ segments.pop();
137
+ let current = '';
138
+ for (const segment of segments) {
139
+ current = current ? `${current}/${segment}` : segment;
140
+ directories.add(current);
141
+ }
142
+ }
143
+ return entries.filter((entry) => !directories.has(entry.path));
144
+ }
125
145
  function zipRead(buffer) {
126
146
  if (buffer.length < 22)
127
147
  throw new ZipError('The file is too small to be a zip');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getxflow/cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "CLI for the XFlow platform: source sync, deployment and publishing of applications",
5
5
  "license": "UNLICENSED",
6
6
  "engines": {
@@ -103,6 +103,30 @@ Interface rules, checked outside `src/components/ui` and `src/components/blocks`
103
103
  `react-hot-toast`, `react-dnd`) must have it mounted in `App.tsx`. Missing providers
104
104
  build fine and give visitors a white screen.
105
105
 
106
+ Template integrity. The app grows out of the platform template, and part of that template
107
+ is not yours to change. The reference is a snapshot of the project itself, taken when the
108
+ platform first looked at it, so these rules never argue with work that was already there:
109
+
110
+ - Platform files must stay byte for byte as they arrived: `src/lib/theme-sync.ts`,
111
+ `src/lib/platform-auth.ts`, `src/contexts/platform-auth-context.tsx`,
112
+ `src/hooks/use-platform-auth.ts`, `src/utils/error-logger.ts`, `src/lib/xflow.ts`.
113
+ They wire the app to the platform, and every way they break is a silent one. Build what
114
+ you need around them, never inside them.
115
+ - The entry point keeps calling `initThemeSync()`, `initPlatformAuth()` and
116
+ `initErrorLogger()`, keeps importing `index.css` and keeps mounting `ThemeProvider`.
117
+ How the file is written is up to you.
118
+ - `index.html` keeps the element with `id="root"` and the script that loads `src/main`.
119
+ - Theme token names in `src/index.css` stay declared, in `:root` and in `.dark` alike, and
120
+ the Tailwind config keeps mapping them. Change the values as much as the design needs:
121
+ it is the names that components paint with.
122
+ - The Tailwind `content` globs keep covering `src/**`. Narrow them and Tailwind strips
123
+ every class the app uses.
124
+ - Files under `src/components/ui` and `src/components/blocks` may be edited freely but
125
+ not deleted.
126
+
127
+ A rejection names the file and the revision to take the original from:
128
+ `xflow pull --revision N --into ./original`, then copy the file back.
129
+
106
130
  ## Cloud functions
107
131
 
108
132
  Server-side code lives in `functions/<name>/index.ts` and exports `handler`. There is no
@@ -209,8 +233,7 @@ The platform design system already ships inside the project, and the app is supp
209
233
  to look like a part of the platform:
210
234
 
211
235
  - `src/components/ui` for primitives: buttons, inputs, dialogs, tables, menus
212
- - `src/components/blocks` for composed blocks: `data-table`, `entity-form`,
213
- `filter-bar`, `detail-panel`, `kanban`, `charts`, `user-picker`, `relation-picker`
236
+ - `src/components/blocks` for composed blocks: `data-table` and `charts`
214
237
  - `src/index.css` for color tokens
215
238
 
216
239
  Before writing your own component, check whether a block already covers it: props are
@@ -221,7 +244,7 @@ the design system sits in the project.
221
244
 
222
245
  The app runs inside the platform in an iframe and receives the theme and the current
223
246
  user from it. The `usePlatformAuth()` hook gives the name, role, permissions and the
224
- list of organization members: that is what `user-picker` is built on.
247
+ list of organization members.
225
248
 
226
249
  ## Errors from a deployed app
227
250