@getxflow/cli 1.1.0 → 1.1.1

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.
@@ -24,7 +24,7 @@ async function functionsList(args) {
24
24
  fn.last_called_at ? `called ${(0, ui_1.formatAge)(fn.last_called_at)}` : 'never called',
25
25
  fn.last_error_at ? `failed ${(0, ui_1.formatAge)(fn.last_error_at)}` : '',
26
26
  ]));
27
- (0, ui_1.note)((0, ui_1.dim)(' callers: the app folders and page names allowed to call it (functions in the root xflow.json)'));
27
+ (0, ui_1.note)((0, ui_1.dim)(' callers: the app folders allowed to call it (functions in the root xflow.json)'));
28
28
  (0, ui_1.note)((0, ui_1.dim)(' open: a caller is public, so anyone on the internet can call it; no callers: a workflow'));
29
29
  (0, ui_1.note)((0, ui_1.dim)(` Webhooks and other functions call ${functions[0].url.replace(/\/[^/]+$/, '/<name>')} with a key`));
30
30
  }
@@ -99,7 +99,6 @@ async function pagesCreate(args) {
99
99
  (0, ui_1.ok)(`Page "${result.page.title}" is live: ${result.file_count} file(s), ${(0, ui_1.formatBytes)(result.size_bytes)}`);
100
100
  (0, ui_1.out)(result.page.url);
101
101
  (0, ui_1.note)((0, ui_1.dim)(` Anyone with the address sees it within seconds. The next version: xflow pages deploy ${result.page.slug} <path>`));
102
- (0, ui_1.note)((0, ui_1.dim)(` A backend for it: name "${result.page.slug}" in functions.<name>.callers of a project, xflow deploy functions, then fetch /_fn/<name>`));
103
102
  }
104
103
  function contentTypeOf(upload) {
105
104
  return upload.kind === 'zip' ? 'application/zip' : 'text/html';
package/dist/help.js CHANGED
@@ -301,11 +301,10 @@ build it and upload what the build writes. Dot entries are skipped. At most 5 MB
301
301
  unpacked and 1000 files.
302
302
 
303
303
  The page is public, so a file holding a secret key is refused whole: every visitor would read
304
- it. A page that needs a backend (a form, a lookup) calls ${(0, ui_1.bold)('/_fn/<name>')} on its own address,
305
- and only a function whose callers in the root xflow.json of its project name the page answers:
306
- add the page name there and run ${(0, ui_1.bold)('xflow deploy functions')}. The page has to exist first.
307
- Such a function is open to the whole internet. Search engines are asked not to index pages,
308
- so do not promise SEO.
304
+ it. A page has no backend: a form or a lookup belongs in an app with access "anyone". The one
305
+ key a page may hold is a function key, and it opens that function to every visitor: an HTML
306
+ form can post to its webhook address with ${(0, ui_1.bold)('?key=')}, while fetch cannot read the answer
307
+ (no CORS). Search engines are asked not to index pages, so do not promise SEO.
309
308
 
310
309
  ${(0, ui_1.bold)('Deleting is final.')} The address goes empty and every version of the page is gone: there
311
310
  is no undo and no copy on the platform side. The name stays reserved for the organization, so
@@ -618,14 +617,14 @@ of the project runs the new code the moment it finishes; there is no publish ste
618
617
 
619
618
  Who may call a function is its callers, in the root xflow.json:
620
619
 
621
- { "projectId": "…", "functions": { "book-slot": { "callers": ["zapis", "clinic-landing"] } } }
620
+ { "projectId": "…", "functions": { "book-slot": { "callers": ["zapis"] } } }
622
621
 
623
- A caller is an app folder of this project (apps/zapis is "zapis") or the name of a page of
624
- the organization. A function nobody lists has no callers: a workflow, woken by a schedule,
625
- by a service holding a key a person issued, or by ${(0, ui_1.bold)('invoke')}. ${(0, ui_1.bold)('open')} in the list means a
626
- caller is public (a page, or an app anyone with the link opens), so anyone on the internet
627
- can call it. The address in the note under the table is for webhooks and other functions,
628
- with a key; apps and pages call ${(0, ui_1.bold)('/_fn/<name>')} on their own address.
622
+ A caller is an app folder of this project (apps/zapis is "zapis"); pages are static and call
623
+ nothing. A function nobody lists has no callers: a workflow, woken by a schedule, by a service
624
+ 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
625
+ anyone with the link opens, so anyone on the internet can call it. The address in the note
626
+ under the table is for webhooks and other functions, with a key; apps call ${(0, ui_1.bold)('/_fn/<name>')} on
627
+ their own address.
629
628
 
630
629
  ${(0, ui_1.bold)('invoke')} calls it as the person who owns the key: the platform checks the role, gives
631
630
  the function a one-minute ticket and calls it itself, so the function sees
package/dist/template.js CHANGED
@@ -13,7 +13,7 @@ const README = `# %NAME%
13
13
  A project on XFlow: one database, cloud functions and the apps that use them.
14
14
 
15
15
  \`\`\`
16
- xflow.json projectId, and which apps and pages may call each function
16
+ xflow.json projectId, and which apps may call each function
17
17
  package.json dependencies of the functions
18
18
  functions/<name>/ cloud functions, shared by every app
19
19
  migrations/ the database of the project
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.1.0';
5
+ exports.CLI_VERSION = '1.1.1';
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.1.0",
3
+ "version": "1.1.1",
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": {
@@ -23,7 +23,7 @@ contains the fix.
23
23
 
24
24
  ## Keeping these instructions current
25
25
 
26
- These instructions ship with xflow CLI 1.1.0, so the copy you are reading can be older than
26
+ These instructions ship with xflow CLI 1.1.1, so the copy you are reading can be older than
27
27
  the CLI answering your commands.
28
28
 
29
29
  When the CLI says this file does not match it, run `xflow skills --refresh` and then **read
@@ -48,10 +48,10 @@ the error points away from the cause. The sections below carry the details.
48
48
  3. Never delete a `functions/<name>/` directory unless the user asked for that
49
49
  function to go. The next deploy removes it from the cloud together with its keys and
50
50
  schedules, and that is final.
51
- 4. A function an app or a page calls needs that caller in its `callers` in the root
51
+ 4. A function an app calls needs that app in its `callers` in the root
52
52
  `xflow.json`. Without it the call answers 404 `not_bound`, and a new function is not
53
53
  reachable from anywhere.
54
- 5. Every function with a public caller (a page, an app open to anyone) can be called by
54
+ 5. Every function called by an app open to anyone can be called by
55
55
  anybody on the internet with curl. Write it as such, see **Your function is open to the
56
56
  whole internet**.
57
57
  6. An already applied migration is never re-run, and `xflow db migrate` refuses a
@@ -128,6 +128,9 @@ halfway through the work into a sentence said before it starts.
128
128
  - The cap on how long one function call may run is written into the function when it is
129
129
  deployed, so a plan change reaches the cloud only with the next `xflow deploy functions` of
130
130
  each project, in both directions.
131
+ - One function runs at most a plan-set number of calls at once: Free 5, Start 15, Business 35
132
+ per cloud zone (the cloud has three, the spread is not promised). A call over it gets 429 at
133
+ once: fan out in batches of that size, never all items of a list in parallel.
131
134
 
132
135
  A limit refusal is not a bad request. The CLI prints a line starting with `Plan limit:`, the
133
136
  API answers `code: "forbidden"` with a `limit` object (`code`, `used`, `limit`), MCP tools
@@ -210,8 +213,7 @@ An app:
210
213
 
211
214
  The project root: function names are lowercase latin letters, digits and hyphens, up to 41
212
215
  characters (`send-mail`, not `sendMail`); every name under `functions` in `xflow.json` has its
213
- `functions/<name>/index.ts`; every caller is an app folder of this project or an existing page
214
- of the organization (`unknown-caller`).
216
+ `functions/<name>/index.ts`; every caller is an app folder of this project (`unknown-caller`).
215
217
 
216
218
  Passes with a line `Unfinished:` instead of blocking: the template's page title, no
217
219
  `<link rel="icon">` in `index.html`, an `exports.access` left in a function (it does nothing).
@@ -263,14 +265,14 @@ The root `xflow.json` names the callers of each function:
263
265
  "projectId": "…",
264
266
  "functions": {
265
267
  "list-slots": { "callers": ["zapis", "priem"] },
266
- "book-slot": { "callers": ["zapis", "clinic-landing"] }
268
+ "book-slot": { "callers": ["zapis"] }
267
269
  }
268
270
  }
269
271
 
270
- - A caller is an app folder of this project (`apps/zapis` is `"zapis"`) or the address name of
271
- a page of the organization (`clinic` for `clinic.xflowpage.ru`). A function without callers is
272
- left out of `functions` or given `"callers": []`, the same thing.
273
- - A function nobody lists has no callers: no app and no page reaches it. That is a workflow,
272
+ - A caller is an app folder of this project (`apps/zapis` is `"zapis"`); a page is never
273
+ a caller. A function without callers is left out of `functions` or given `"callers": []`,
274
+ the same thing.
275
+ - A function nobody lists has no callers: no app reaches it. That is a workflow,
274
276
  run by a schedule, a key or `xflow functions invoke`.
275
277
  - Callers ship with `xflow deploy functions` and come back with its `--revision N`.
276
278
  - The app calls `xflow.functions.invoke('book-slot', { body })` from `src/lib/xflow.ts`. The call
@@ -281,15 +283,13 @@ The root `xflow.json` names the callers of each function:
281
283
  `invalid_credential` (401, the visitor has to sign in again), `no_access`, `role_too_low`,
282
284
  `functions_stopped` (403, minutes ran out or the plan is unpaid: a person settles it),
283
285
  `rate_limited` (429), `unavailable` (503, retry later). Branch on `code`, never on the message.
284
- - A page has to exist before a function can name it: create the page, then deploy functions.
285
286
 
286
287
  ## Your function is open to the whole internet
287
288
 
288
- A function is open when any of its callers is public: a page (always) or an app at "Anyone
289
- with the link". `xflow deploy functions` says so in a line of its own, `xflow functions list`
289
+ A function is open when an app at "Anyone with the link" is among its callers. `xflow deploy functions` says so in a line of its own, `xflow functions list`
290
290
  prints `open`, the web interface marks it.
291
291
 
292
- Open means anybody can call it with curl, without your page: the browser is not a gate.
292
+ Open means anybody can call it with curl, without your app: the browser is not a gate.
293
293
  Hiding the address, checking `Origin` or `Referer`, a token baked into the bundle: all of it is
294
294
  readable or forgeable and protects nothing. Write every open function as if the request came
295
295
  from a stranger, because it may:
@@ -346,14 +346,13 @@ At the closed levels do none of this: the platform already knows the person.
346
346
  is uploaded, no build, no publish step, no database. A landing page, a presentation, an invoice.
347
347
  - An app is source the platform builds: versions and publish, access levels, a database through
348
348
  functions. Anything people sign in to, enter data into or come back to.
349
- - A form open to strangers on a plan without "Anyone with the link" is a page: pages are public
350
- on every plan.
351
349
  - Keep the sources of a page outside the project folder, or list them in `.xflowignore`: the
352
350
  root travels with `deploy functions`.
353
- - A page that needs a backend (a form, a lookup) calls `/_fn/<name>` on its own address with the
354
- header `X-Xflow-Fn: 1` (without it 403 `bad_request`), and a function of a project answers only
355
- when its callers name the page. The function is then open. A refusal carries its code in
356
- `reason` of the JSON body, the same codes as above.
351
+ - A page has no backend. A form or a lookup for strangers is an app at "Anyone with the link"
352
+ (a paid plan): the platform checks every call to its functions.
353
+ - A function key on a page is allowed, and it opens that function to every visitor: an HTML
354
+ form posts to `https://fn.xflowpage.ru/<projectId>/<name>?key=...`, fetch cannot read the
355
+ answer (no CORS). Nothing guards it but the function's own checks: say so to the user.
357
356
 
358
357
  ## Cloud functions
359
358
 
@@ -397,12 +396,12 @@ The platform checks every call before your code runs and hands the answer over i
397
396
  ```js
398
397
  exports.handler = async (event) => {
399
398
  const { caller, slug, user, ip } = event.xflow
400
- // caller: 'app' (an app of this project), 'page', 'service' (another function of this
399
+ // caller: 'app' (an app of this project), 'service' (another function of this
401
400
  // project), 'external' (an outside service with a key), 'schedule',
402
401
  // 'platform' (a person through xflow functions invoke or the web interface)
403
- // slug: the address name of the calling app (<id>--<org>) or page, null otherwise
402
+ // slug: the address name of the calling app (<id>--<org>), null otherwise
404
403
  // user: { id, role, name, access } of a signed-in person, null for anonymous
405
- // visitors, pages, keys. access: 'view' | 'edit' on this project
404
+ // visitors and keys. access: 'view' | 'edit' on this project
406
405
  // ip: the visitor's address, null for a schedule
407
406
  }
408
407
  ```
@@ -429,7 +428,7 @@ exports.minRole = 'developer' // 'member' | 'developer' | 'owner'
429
428
  ```
430
429
 
431
430
  The platform refuses anything below that role before your code runs; it applies to people
432
- (apps, pages, `functions invoke`), not to keys and schedules. `member` is the people who only
431
+ (apps, `functions invoke`), not to keys and schedules. `member` is the people who only
433
432
  use the apps (clinic employees, managers), `developer` and `owner` build them. In the web
434
433
  interface these roles are called «Просмотр» (`member`), «Редактор» (`developer`) and «Админ»
435
434
  (`owner`). Without the line