@plitzi/cli 0.38.4 → 0.38.6

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/README.md CHANGED
@@ -20,6 +20,12 @@ npx @plitzi/cli upload plugin # that zip, on the space you work
20
20
  - **The same flag means the same thing everywhere:** `-o, --out` where it writes, `-f, --force` to write over what is
21
21
  there, `-e, --environment` for which version of the space, `--width` for the widths a page is looked at (one, or
22
22
  several separated by commas), `--api` for the platform, `--json` for a tool or an agent.
23
+ - **`--dry-run` says what it would do, and does none of it** — on every command that writes or sends: `create`,
24
+ `add plugin`, `add runtime`, `pull`, `push`, `pack plugin`, `source`, `import`, `upload plugin`, `functions pull`/
25
+ `push`, `runtime push`/`start`/`stop`/`size`/`vars`, `skills update`, `doctor --fix`. Each file it would write (`+` new, `~` replaced, `-`
26
+ removed), what it would install or run, what it would send and where. It still reads what it needs to say so — the
27
+ project, the files it would send, the space it would pull, signing in for that. `upgrade` and `fix` only show until
28
+ `--write`.
23
29
 
24
30
  ## `create`
25
31
 
@@ -97,13 +103,14 @@ when an install fails the CLI says which setting names it.
97
103
 
98
104
  ## What lands in the project
99
105
 
100
- - **The space, as yours.** A local project gets `src/space.ts` — a _copy_ of the space Plitzi gives a new
101
- account, declared as a tree, some CSS and a palette rather than exported as a document. It is the same
102
- declaration the platform authors a new space from, so what you start with and what signing up gives you cannot
103
- come apart — and unlike a document, you can read and change it.
106
+ - **The space, as yours.** A local project gets `src/space/` — a _copy_ of the space Plitzi gives a new account,
107
+ declared as a tree, some CSS and a palette rather than exported as a document, a file per part: `index.ts` (the page
108
+ and the space), `tokens.ts`, `theme.ts`, `content.ts`. It is the same declaration the platform authors a new space
109
+ from, so what you start with and what signing up gives you cannot come apart — and unlike a document, you can read
110
+ and change it.
104
111
  - **A live loop.** In client mode a save is a hot module replacement: the space module is swapped and the tree
105
- remounted, so the page updates without reloading. In server mode `--watch` restarts the process and the next
106
- request renders the change.
112
+ remounted, so the page updates without reloading. In server mode `start:dev` re-authors a saved space and the open
113
+ page loads again, swaps a saved plugin where it is drawn, and restarts only for the server's own code.
107
114
  - **A plugin of the project's own.** `src/plugins/StatCard` is a React component the space renders through a
108
115
  `custom` element — the one thing about Plitzi a page of built-in elements cannot show. Every folder of
109
116
  `src/plugins` is registered by itself, under its name in camelCase, so `plitzi add plugin` is all a new one takes. Its props ARE the
@@ -130,18 +137,38 @@ when an install fails the CLI says which setting names it.
130
137
 
131
138
  | Folder | What it is | In git |
132
139
  | --- | --- | --- |
133
- | `public/` | Served to anyone who asks, as it is — data, pictures, a favicon. **It is on the internet**: never a secret, a key, a private document or data only some visitors may read | yes |
140
+ | `public/` | Served to anyone who asks, as it is — pictures, a favicon, and in client mode the data the browser fetches (`public/data/`). **It is on the internet**: never a secret, a key, a private document or data only some visitors may read | yes |
141
+ | `src/data/` | Server mode: the project's own data — JSON its server reads for a provider (`query: '/data/<file>'`, `runtime: 'server'`) and never serves (`dataDir`). What a provider reads is in the page it renders: data a page must not carry is a server action's to read | yes |
142
+ | `src/functions/` | Server mode: the project's own server code — tasks and `/fn/` routes (`defineFunctions`), built at boot | yes |
143
+ | `vendor/plugins/` | The plugins the project runs as they were built, with no source (a project made from a space gets them), each folder with its `plugin-manifest.json`. The server runs them, and `author`, `check`, `fix` and `push` know every element type each provides | yes |
134
144
  | `tmp/` | What the project writes for itself while it runs: the plugins the server builds (`tmp/.sdk-plugins`), resized pictures, the port it took (`tmp/dev-server.json`), the space as last authored, screenshots and test output. Rebuilt when missing | no |
145
+ | `state/` | Server mode: what the server keeps for the space — its `kv` in `state/kv.json` (`createFileKv`): saved layouts, counters, cached answers. The deployment's state: kept across restarts, never rebuilt. `action.kv` in `src/config/serverOptions.ts` keeps it elsewhere (`createSqliteKv` for several processes, or a database) | no |
135
146
  | `.plitzi/` | What the CLI records about the project: the space it came from (`space.json`), the functions' working copy, the files `create` wrote — what `pull`, `push` and `upgrade` stand on | yes |
136
147
 
148
+ **`plitzi/` is the CLI's; `src/` is yours — but `src/main.ts`.** `plitzi/` holds `author.ts`, the types plugins import
149
+ (`assets.d.ts`, server mode) or the page's base styles (`preflight.css`, client mode), and a `README.md` saying what
150
+ each folder of `src/` is. `src/main.ts`, the entry point, is the CLI's too, kept in `src/` where an entry point is
151
+ looked for; `upgrade` keeps all of them current, and the build compiles `src/` into `dist/main.js`. A plugin is declared by its folder: the server, `author` and `check` find every
152
+ `src/plugins/<Name>/declaration.ts` (`pluginDeclarations` from `@plitzi/sdk-authoring/node`). What the server does
153
+ besides serving the space is the project's own, in files it reads: `src/config/serverOptions.ts` (handed to `createServer` —
154
+ `images`, `action.limits`, `action.kv`, `rsc`) and, with `--source local`, `src/actions/index.ts` (the space's server
155
+ actions, one `defineAction` each, a file each as they grow).
156
+ What `main.ts` wires itself — where the space comes from, the plugins, `public/`, `src/data/`, `src/functions/`, the
157
+ actions' lookups — is left out of `serverOptions`' type, and comes after it, so an option there can never unwire it.
158
+ `src/functions/` holds the project's own server code; `start:dev` restarts on a change to any of them. A plugin is not
159
+ server code to restart for: a save to one is built again and swapped in the open pages where it is drawn — the rest of
160
+ the page, its state included, stays — its server half (`src/plugins/<Name>/functions/`) is loaded again in place, and a
161
+ new plugin folder is registered without a restart.
162
+
137
163
  ## `create --template blank` and `--template catalog`
138
164
 
139
165
  A space written in the project starts as the welcome tour, with a plugin of the project's own. `--template blank`
140
166
  starts it as tokens for both themes, a layout whose `site-main` the pages render in, and one empty page — with
141
- `public/data/` and no example plugin — for a project that is about to be a specific site. `--template catalog` starts
142
- it as a complete small shop to read and change: a layout with a menu, a product card component, the products in
143
- `public/data/products.json` read as a typed source, a catalog filtered by category, and a page per product — a file
144
- per part under `src/site/`. Both go with `--source local`.
167
+ a folder for its data and no example plugin — for a project that is about to be a specific site. `--template catalog`
168
+ starts it as a complete small shop to read and change: a layout with a menu, a product card component, the products
169
+ in `src/data/products.json` read on the server as a typed source (`public/data/products.json`, fetched by the browser,
170
+ in client mode), a catalog filtered by category, and a page per product — a file per part under `src/space/`. Both go
171
+ with `--source local`.
145
172
 
146
173
  ## `check` and `shot`
147
174
 
@@ -155,7 +182,9 @@ plitzi shot / --compare https://example.com --width 1440 # beside another site
155
182
  ```
156
183
 
157
184
  Both run on the project's own Playwright against its running server (`npm start`), and refuse a port that answers as
158
- another project. `check` reports every element the space owes the page that is missing or hidden (with why), broken
185
+ another project. They wait for the page to settle — loaded, then half a second with nothing asked for — counting no
186
+ stream that stays open, so a page with a realtime `channel` is checked like any other (`openPage` of
187
+ `@plitzi/sdk-authoring`, which the generated `npm run visual` uses too). `check` reports every element the space owes the page that is missing or hidden (with why), broken
159
188
  images, sideways scroll, text in the colour behind it, console errors, refused requests and failed flows — and the
160
189
  page's data: a binding that reads a path its provider's answer lacks (with the keys it has), a provider that failed,
161
190
  the rows each list rendered. A page's state in a few hundred tokens, where a screenshot costs thousands.
@@ -184,6 +213,43 @@ by the line and column the element remembers — and only where the value is wri
184
213
  listed with where it is and why it was left. `--write` authors the space again in a fresh process and keeps the edits
185
214
  only if every fix is gone and no problem was added; a fix that would add one is put back and said.
186
215
 
216
+ ## `lint`
217
+
218
+ ```bash
219
+ plitzi lint # the space's source, eslint's way: each practice to change at its file and line
220
+ plitzi lint --json # one object: { scope, notChecked, counts, findings }
221
+ plitzi lint --max-warnings 0 # more warnings than that fail (exit 1); --strict: any warning does
222
+ ```
223
+
224
+ How the space in `src/space/` is written — what only its source can say, and what grows hard to read and to change as
225
+ a space reaches thousands of lines. Each finding has a stable `code`, a severity, the file, line and column, what to
226
+ write instead, and `docs`: the reference of the project's skills that explains the practice. A project `create` writes
227
+ has it as `npm run lint:space`.
228
+
229
+ | Code | What it finds |
230
+ |---|---|
231
+ | `unused-file` | a file of `src/space/` nothing `src/space/index.ts` imports reaches |
232
+ | `file-too-long` | a file of more than 400 non-blank lines: split it by what changes together |
233
+ | `pages-in-one-file` | two pages or more (past 40 lines) written in `index.ts`, which only assembles the space; three or more (past 150 lines) in any other file — one file per page, or per page family, under `src/space/pages/` |
234
+ | `inline-records` | ten or more records written inline that a page renders (`.map`ped, or a list's `items`): rows of data for `src/data/` (`public/data/` without a server), read by a provider into one `list` |
235
+ | `repeated-css` | the same CSS — three declarations or more, in any order or spelling — written three times: one class |
236
+ | `special-case-in-map` | a `map` that singles out a row by its `id`, `key`, `slug`, `name`, `title` or `label`: the difference belongs in the row's data |
237
+ | `colour-not-token` | a hex, `rgb()`/`hsl()`/`oklch()`… or a named colour where a colour is all a property takes — outside the declared tokens (`variables`, `light`/`dark`), anchors, masks, markup and URLs |
238
+ | `positional-id` | an id minted for an element nobody named — `container-45`, `heading-a7k2` |
239
+ | `space-does-not-author` | error: the space does not author, so authoring's suggestions could not be read — `npm run author` says why |
240
+ | `source-unreadable` | error: the source could not be read (no TypeScript installed, a rule that could not finish) |
241
+
242
+ Beside them, every suggestion authoring makes about the space it authors to (`authorSpace(…).suggestions`) — whatever
243
+ its code, the day authoring adds it — as a warning at the line that wrote its element; `plitzi explain <code>` says what
244
+ each means. They are quieted on the element (`quiet: ['repeated-shape']`), where the builder and the MCP read it too.
245
+ A practice the source departs from on purpose is said where it is, eslint's way:
246
+ `// plitzi-lint-disable-next-line colour-not-token -- the partner's own red`, `-line` for its own line, and
247
+ `// plitzi-lint-disable <codes>` for the whole file.
248
+
249
+ It is not whether the space authors — what it refuses and warns of is `npm run author`'s — nor a page as it renders
250
+ (`check`), nor the project around the space (`doctor`); every report says so (`notChecked`). Exit code 1 while anything
251
+ is an error; with `--strict` a warning too, with `--max-warnings <n>` more than n of them.
252
+
187
253
  ## `import`
188
254
 
189
255
  ```bash
@@ -204,9 +270,11 @@ What lands in `--out`:
204
270
  - `assets.json`, `screens/<width>.png`, and `IMPORT.md`: what was not carried over (the text, states, scripts, fonts
205
271
  not on Google) and what to do next.
206
272
 
207
- It reads only a site that is yours: one served from this machine (a host that resolves to loopback), or one a verified
208
- domain of one of your spaces covers — the `_plitzi` TXT record a custom domain is verified by, under **Domains** in the
209
- dashboard, asked of the platform you are signed in to (`--api`). Anything else is refused before a page is opened.
273
+ It reads only a site that is yours: one served from this machine (a host that resolves to loopback), with nothing asked
274
+ of anybody; or, with `--account`, one a verified domain of one of your spaces covers — the `_plitzi` TXT record a custom
275
+ domain is verified by, under **Domains** in the dashboard, asked of your Plitzi account (`--api`), signing in when you
276
+ are not. Without `--account` it never reaches the platform: a site not served from this machine is refused, saying so.
277
+ Anything else is refused before a page is opened.
210
278
 
211
279
  It refuses an `--out` that already has files unless `--force`, and then names the files an earlier import left that it
212
280
  did not write again.
@@ -227,27 +295,81 @@ plitzi explain --list steps # every one of a kind: elements, steps, triggers
227
295
  What a name means when authoring, from the catalogues the checks themselves read — the answer to a question that would
228
296
  otherwise be a search through the SDK's types. `--json` answers in one object; over MCP it is `plitzi://explain/{name}`.
229
297
 
298
+ ## `doctor`
299
+
300
+ ```bash
301
+ plitzi doctor # every area, each problem with where it is and what fixes it
302
+ plitzi doctor --fix # repairs what is simple and safe, then checks again
303
+ plitzi doctor --fix --dry-run # what --fix would repair, nothing done
304
+ plitzi doctor --json # one object: { ok, counts, areas, findings, repaired, recommendations }
305
+ plitzi doctor --strict # warnings fail too — for a CI that keeps the project up to its CLI
306
+ ```
307
+
308
+ Whether the project is whole as the CLI sets it up, read from what it is now rather than from what the CLI once wrote:
309
+ a developer may change any file, and the checks are what each part must be for the project to install, start, build and
310
+ push. It is the CLI's check of the project, not of the space: what the space authors to and warns of is `npm run
311
+ author`'s, how it is written `lint`'s, and how a page renders `check`'s. Exit code 1 while anything is an error (with `--strict`, a warning).
312
+
313
+ Each finding has an area, a stable `code`, the file and its `fix`; the report ends with **what to run next** — the few
314
+ commands (`plitzi upgrade --write`, an install, `doctor --fix`) that clear most of the list, by how many each fixes.
315
+
316
+ **`--fix`** makes only what is simple and safe, then examines the project again: a layout an older CLI left, moved (every
317
+ import, `new URL(…, import.meta.url)` and script naming a moved file follows, and what the CLI recorded of its files
318
+ follows them); an older CLI's dead files and caches deleted; `.gitignore` lines added or taken out; `"type": "module"`
319
+ and `engines`; a folder `start:dev` watches; a `PLITZI_SIGNING_SECRET` where there is none (or one too short). What
320
+ replaces a file of the project's, installs, or touches git is recommended, never done — `upgrade --write` and the install
321
+ are a step of their own.
322
+
323
+ **A project an older CLI made** is the usual patient, and its own installed CLI may predate `doctor`: run the latest one
324
+ from it — `npx @plitzi/cli@latest doctor --fix`, then what it recommends. While its layout is an older one (the space in
325
+ `src/space.ts`, `author` in `src/`, `functions/` at the root), nothing else is checked — read against it, every other
326
+ area would only say what the move fixes — and `upgrade` writes no file until it is moved.
327
+
328
+ | Area | What is held |
329
+ |---|---|
330
+ | `layout` | where an older CLI kept what this one reads elsewhere (`src/space.ts`, `src/site/`, `src/actions.ts`, `src/author.ts`, `functions/`) and what it left behind (`src/plugins/declarations.ts`, `.sdk-plugins/`, `.plitzi/dev-server.json`) |
331
+ | `packages` | `"type": "module"`, the Node version; every package the project and its scripts need, declared and installed at a version its range allows; the SDK's packages at one version, no older than the CLI; one copy of the SDK and of React (none installed inside another); the scripts — gone, behind the CLI, or the project's own — the file each Node script starts and every folder `start:dev` watches; one lockfile |
332
+ | `machinery` | the CLI's files (`MACHINERY`), as `upgrade` sees them: gone, behind, or the project's own (said, never failed); the scaffold record |
333
+ | `config` | `tsconfig.json` reads `src/` and `plitzi/` and sets what Node's type stripping needs; `tsconfig.build.json` writes the file `start:prod` runs; `.gitignore` keeps `.env` out (and `node_modules`, `tmp`, `dist`, `state`) and `.plitzi/` in; `.env` not in git; the signing secret, as long as the project's own server wants it; a cloud project's key |
334
+ | `sources` | what Node runs as written — `src/main.ts`, `src/config/`, `src/actions/`, `src/space/`, `src/runtime/`, `plitzi/author.ts`, the plugins' declarations, and all they import: every relative import a file that is there, with its extension; JSON imported `with { type: 'json' }`; no JSX; every package declared (a devDependency, in what production runs, is said); what the server loads by name exported (`space`, `actions`, the runtime's default) |
335
+ | `plugins` | each folder of `src/plugins/`: an `index.ts` that builds and exports its component, its packages declared; a `declaration.ts` whose `type` is the folder's (`StatCard` → `statCard`); one folder a type; none shadowed by a built copy in `vendor/plugins/` |
336
+ | `data` | every file of `src/data/` and `public/data/` JSON — what the server answers with and `push` sends |
337
+ | `functions` | `src/functions/` built by the project's own `@plitzi/sdk-server` (`buildFunctions`), as every runner builds them |
338
+ | `records` | `.plitzi/space.json`, `scaffold.json` and `functions.json` readable, and of the same space |
339
+ | `skills` | `.claude/skills/plitzi-*` as the packages installed write them |
340
+
341
+ It loads the plugins' declarations and the project's own `@plitzi/sdk-server` to ask them — as the server does — and
342
+ never starts the server or the runtime. A check that cannot finish is said as an error
343
+ of its area, never a crash.
344
+
230
345
  ## `upgrade`
231
346
 
232
347
  ```bash
233
348
  plitzi upgrade # what this CLI would change in the project — shown, nothing written
234
349
  plitzi upgrade skills --write # only the skills, each replaced whole from the packages installed
235
- plitzi upgrade --write --take src/author.ts
350
+ plitzi upgrade --write --take plitzi/author.ts
236
351
  ```
237
352
 
238
353
  A project brought up to the CLI it has now, part by part: `files` (the machinery — `author.ts`, `main.ts`, the
239
354
  Playwright and lint configs, `AGENTS.md`), `packages` (`package.json` merged, `@plitzi/*` raised to this version, then
240
355
  the install), `skills` (`.claude/skills/plitzi-*`, whole, so a reference a skill no longer has goes with it) and
241
356
  `renames` (a name a version renamed, at its file and line). A file nobody changed since the CLI wrote it is replaced;
242
- one the project made its own is a diff, left unless `--take` names it (`all` for every one). `update` is the same
357
+ one the project made its own is a diff, left unless `--take` names it (`all` for every one). The generated
358
+ `.prettierignore` names them, so the project's `format` never turns one into a file `upgrade` believes was changed. A file of the project's own
359
+ that the machinery reads (`src/config/serverOptions.ts`, `src/actions/index.ts`) is written when the project has none and the
360
+ `main.ts` reading it is the CLI's, and never replaced. A script is the same: one the CLI wrote and nobody changed takes
361
+ today's command, one the project changed is left and said (`.plitzi/scaffold.json` records both, and the package
362
+ manager the files were written for — what a project not installed yet has no lockfile to say). In a project made from
363
+ a space, a file the space gave over one of the CLI's would be the space's, and `upgrade` would leave it to `plitzi pull` —
364
+ none does: `src/main.ts` runs whatever the space brought, from where it lands. `update` is the same
243
365
  command, and `plitzi skills update` is `upgrade skills --write`. `npm run author` says when the authoring skill is
244
366
  older than the `@plitzi/sdk-authoring` installed.
245
367
 
246
368
  ## `data describe`
247
369
 
248
370
  ```bash
249
- plitzi data describe public/data/products.json # its shape, and one row of its longest list
250
- plitzi data describe public/data/products.json --json # { shape, example }
371
+ plitzi data describe src/data/products.json # its shape, and one row of its longest list
372
+ plitzi data describe src/data/products.json --json # { shape, example }
251
373
  ```
252
374
 
253
375
  The fields of a JSON file, their types and which rows have them — `price?: number (in 812 of 879)` — so a page can
@@ -271,7 +393,7 @@ the draft, an environment's latest, or a pinned revision.
271
393
 
272
394
  ```bash
273
395
  plitzi push # at a terminal: tick what goes up — what changed is ticked already
274
- plitzi push space functions # only these: space, functions, runtime, plugins
396
+ plitzi push space functions # only these: space, functions, data, runtime, plugins, files
275
397
  plitzi push space --force # replace the draft even though it was edited in the builder since
276
398
  ```
277
399
 
@@ -281,9 +403,13 @@ that names come before what names them:
281
403
 
282
404
  1. `plugins` — every plugin whose source changed, packed and uploaded to the space's CDN (`--cdn`/`--bucket` when it
283
405
  has several public buckets);
284
- 2. `functions` — `functions/`, as `functions push`;
285
- 3. `runtime` — the runtime module, as `runtime push`;
286
- 4. `space` — `src/space.ts` authored, with the actions `src/actions.ts` serves and the connectors in `src/connectors/`.
406
+ 2. `files` — each changed file of `public/assets/`, put at the same path under the space's `assets/` on its CDN
407
+ (images, sounds, videos and JSON; any other type is named and stays here);
408
+ 3. `functions` — `src/functions/`, as `functions push`;
409
+ 4. `data` — `src/data/` whole, as the space's own data: kept privately, read by its server providers, frozen with each
410
+ publish — refused when the space's copy changed since the project last had it, unless `--force`;
411
+ 5. `runtime` — the runtime module, as `runtime push`;
412
+ 6. `space` — `src/space/` authored, with the actions `src/actions/index.ts` serves and the connectors in `src/connectors/`.
287
413
 
288
414
  The draft is never replaced unseen: when it was edited in the builder since the project's last pull or push, the push
289
415
  is refused — pull first, or `--force`. A project that never had the space (one not made with `create --from`) may take a
@@ -291,6 +417,12 @@ space nobody has worked on yet; one that holds work asks for `--force` too. Alwa
291
417
  which must be the one the project came from, and never a published environment: publish in the builder. Afterwards
292
418
  `.plitzi/space.json` records what was sent, so `pull` keeps working — on a project that started on its own too.
293
419
 
420
+ The space goes back with its files where Plitzi serves them: a path to a file of `public/assets/` (`/assets/a.png`) —
421
+ one `create --from` or `pull` wrote, or one the `files` part put on the CDN — is sent as that file's CDN address. Only
422
+ `public/assets/` goes to the CDN: the rest of `public/` is the project's own server's. What would not reach Plitzi is
423
+ said before anything is sent, with what to do: a provider reading a file `src/data/` does not hold, a file of
424
+ `public/assets/` the space names that is not on its CDN yet, and one of `public/` outside `assets/`.
425
+
294
426
  ## `add plugin`
295
427
 
296
428
  Adds elements of your own to the project you are in — one, several at once (`add plugin seat-picker legend`), or one at
@@ -300,10 +432,8 @@ adds), its `Settings.tsx` panel for the builder, and the `index.ts` that puts th
300
432
  what the builder shows, and what it is for, and checks every folder is free before writing any.
301
433
 
302
434
  - **In a project `plitzi create` wrote**, it goes in `src/plugins`, where the project already looks: nothing to
303
- register, and `start:dev` restarts onto it. Host it with `custom({ renderType: 'seatPicker' })` in `src/space.ts`
435
+ register, and a running `start:dev` picks it up without a restart. Host it with `custom({ renderType: 'seatPicker' })` in `src/space/`
304
436
  — or, when the space lives in Plitzi, with a Custom element in the builder.
305
- - **In a project written before plugins were found by folder**, it goes in `src/plugins` too, and prints the line to
306
- add to the `plugins` list in `src/main.ts`.
307
437
  - **In a plugin package**, it goes in `src/`, and is added to `src/elements.ts` and `src/declarations.ts`, from which
308
438
  the package publishes it.
309
439
  - **In any other project**, it asks which folder holds the project's components (`--dir` answers it) and prints how
@@ -318,11 +448,21 @@ plitzi add plugin ticker --prop interval:number=5000 --prop paused:boolean --tri
318
448
  ```
319
449
 
320
450
  `--prop name:type=default` (string, number or boolean) is an attribute: in the props, the declaration's defaults and
321
- `bindingsAllowed`, and a control in `Settings.tsx`. `--trigger onTick:count,at` is an event and what a flow started by
322
- it reads, fired with the `useTickerEvents()` hook it gets. `--callback reset` is an action a flow can call. `--headless`
323
- is an element with nothing to see: hidden on a page, a badge in the builder. The files are written as the project's
451
+ `bindingsAllowed`, and a control in `Settings.tsx`; `--prop rows:list` and `--prop meta:json` are data a binding fills
452
+ (`unknown[]`, `Record<string, unknown>`, empty until it does). `--trigger onTick:count,at` is an event and what a flow
453
+ started by it reads, fired with the `useTickerEvents()` hook it gets — never on the builder's canvas. `--callback reset`
454
+ is an action a flow can call. `--headless` is an element with nothing to see: hidden on a page, a badge in the builder,
455
+ and `drawsNothing` in its declaration, so a page check does not look for it. The files are written as the project's
324
456
  Prettier writes them.
325
457
 
458
+ `--server` gives the element a server half: `functions/index.ts` in its folder, written like a space's functions — its
459
+ routes answer under `/fn/plugins/<type>/` (`usePluginRoute(type)` in the component names them), its tasks are steps
460
+ `<type>.<action>`, and it runs with a plugin's own corner of `kv`, none of the space's credentials or channels. In a
461
+ server-mode project or a plugin package (which gets `@plitzi/sdk-server` for the types); a project that renders in the
462
+ browser alone has nothing to run it, and is refused. `pack plugin` carries it as `functions.source.json`, and the
463
+ platform builds and keeps it privately when the plugin is uploaded — see
464
+ [functions § a plugin's server half](../../docs/en/functions.md#10-a-plugins-server-half).
465
+
326
466
  ## `create --plugin`
327
467
 
328
468
  A plugin package: elements any space can load, with a Vite preview to write them in. It builds nothing itself —
@@ -352,9 +492,11 @@ plitzi pack plugin src/plugins/SeatPicker # an element of a self-hos
352
492
  plitzi pack plugin src/plugins/SeatPicker src/plugins/Legend # several in one plugin, the first its main
353
493
  ```
354
494
 
355
- It writes one ES module (esbuild; React and the SDK kept out — the page provides them; images and fonts inside, since
356
- a page imports the module from a blob URL), `plugin-manifest.json` written from the elements' declarations with each
357
- file's integrity hash, and the zip the builder takes: upload it under Resources, as a plugin. A package also gets its
495
+ It writes one ES module (esbuild; React and the SDK kept out — the page provides them; images, fonts and any file
496
+ imported whole — `worker.js?raw` for its text, `engine.wasm?inline` for a data URI — inside, since a page imports the
497
+ module from a blob URL), `plugin-manifest.json` written from the elements' declarations with each file's integrity
498
+ hash, and the zip the builder takes — its stylesheet in the `plitzi-sdk-plugin` cascade layer, below the space's
499
+ styles, so a space's classes and `customCss` win over it: upload it under Resources, as a plugin. A package also gets its
358
500
  type declarations, written with its own TypeScript. Or serve the build at a versioned address with CORS open, and list
359
501
  it in a space's plugins as `{ type, resource }`.
360
502
 
@@ -371,7 +513,7 @@ in the code keeps it from being written — said, and never stopping the build.
371
513
  ## `pack source`
372
514
 
373
515
  ```bash
374
- plitzi pack source src/runtime.ts --kind runtime --name runtime -o runtime.source.json.gz
516
+ plitzi pack source src/runtime/index.ts --kind runtime --name runtime -o runtime.source.json.gz
375
517
  ```
376
518
 
377
519
  What `upload plugin` and `runtime push` keep, written to a file to look at: the closure of the entries named, gzipped.
@@ -411,7 +553,7 @@ certificate authorities the system trusts, as the browser beside it does.
411
553
  ## `runtime`
412
554
 
413
555
  ```bash
414
- plitzi runtime push # pack src/runtime.ts and keep it as the space's draft runtime
556
+ plitzi runtime push # pack src/runtime/index.ts and keep it as the space's draft runtime
415
557
  plitzi runtime status # how each environment's runtime is, and its variables' names; --json
416
558
  printf %s "$URL" | plitzi runtime vars set REDIS_URL # a value from stdin stays out of the shell history
417
559
  plitzi runtime vars unset REDIS_URL
@@ -425,8 +567,9 @@ connection kept open, memory that outlives a request, Node and its packages (`do
425
567
 
426
568
  **Its code lives in a project of yours** — a folder, usually a repository — and nowhere else: the builder shows how a
427
569
  runtime runs and sets its variables, but it does not hold its code and cannot change it. The project has
428
- `src/runtime.ts` (or `--entry`), whose default export is `defineRuntime(…)`, and `@plitzi/sdk-server` installed: a
429
- project from `plitzi create` in server mode has it; `examples/self-hosting/10-runtime` is the smallest one to start
570
+ `src/runtime/index.ts` (or `--entry`), whose default export is `defineRuntime(…)`, and `@plitzi/sdk-server` installed:
571
+ `plitzi add runtime` writes it in a project from `plitzi create` in server mode, whose `src/main.ts` runs it too —
572
+ `/hello` answers on `npm start` — and whose `start:dev` restarts on it; `examples/self-hosting/10-runtime` is the smallest one to start
430
573
  from, and it runs as a server of its own too.
431
574
 
432
575
  `push` packs the module with the project's own `@plitzi/sdk-server` — the module and every package it imports, bar
@@ -446,21 +589,26 @@ running one would stop, and why a stopped one is.
446
589
  ## `functions`
447
590
 
448
591
  ```bash
449
- plitzi functions pull # the space's functions into functions/
450
- plitzi functions push # functions/ saved as the space's draft, built and checked
592
+ plitzi functions pull # the space's functions into src/functions/
593
+ plitzi functions push # src/functions/ saved as the space's draft, built and checked
451
594
  plitzi functions try seismic.feed --params '{"minMagnitude":"4"}' # one task of the saved draft, in the sandbox
452
595
  plitzi functions dev seismic.feed --params '{}' --watch # on this machine, as the platform runs it
453
596
  ```
454
597
 
455
- A space's own server code — tasks its actions run as steps, routes under `/fn/` — and `functions/` is a working copy
598
+ A space's own server code — tasks its actions run as steps, routes under `/fn/` — and `src/functions/` is a working copy
456
599
  of it: `.plitzi/functions.json` keeps what was pulled, so `pull` refuses to overwrite what is not pushed (`--force`
457
600
  throws it away) and `push` refuses when the space moved on since. A problem comes back as
458
- `functions/<file>:<line> <message>`. `dev` runs with the project's own `@plitzi/sdk-server` (`isolated-vm` and
601
+ `src/functions/<file>:<line> <message>`. `dev` runs with the project's own `@plitzi/sdk-server` (`isolated-vm` and
459
602
  `core-js` beside it); credentials come from `PLITZI_FUNCTIONS_CREDENTIALS`. See `docs/en/functions.md`.
460
603
 
461
604
  ## Credentials
462
605
 
463
- `create` never mints one. A cloud project's key comes from Credentials in the builder, is written to `.env`, and
464
- `.gitignore` is written in the same breath. Server and browser take **different** keys and the scaffold names
465
- them differently on purpose: a server gets the secret self-hosting key, a browser gets the public render key,
606
+ `create` never mints a key to Plitzi. A cloud project's key comes from Credentials in the builder, is written to
607
+ `.env`, and `.gitignore` is written in the same breath. Server and browser take **different** keys and the scaffold
608
+ names them differently on purpose: a server gets the secret self-hosting key, a browser gets the public render key,
466
609
  whose protection is the origin it is presenting from.
610
+
611
+ Every server project's `src/main.ts` reads `.env` itself (`process.loadEnvFile`), so `npm start` needs nothing else.
612
+ `create` gives each one a signing key there, made for it: `PLITZI_SIGNING_SECRET`, what `ctx.sign` and `ctx.verify`
613
+ sign with — at least 32 characters (`doctor --fix` writes one where it is missing). `PORT` is left commented out: 8080,
614
+ or the next free port while developing.