@plitzi/sdk-shared 0.38.7 → 0.38.9

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/CHANGELOG.md CHANGED
@@ -1,5 +1,251 @@
1
1
  # @plitzi/sdk-shared
2
2
 
3
+ ## 0.38.9
4
+
5
+ ### Patch Changes
6
+
7
+ - 64f4cd6: - **`unusedPort()`** (`@plitzi/sdk-server`): a port the system has just handed out, for a test to listen on. The
8
+ package's own tests and `@plitzi/sdk-mcp`'s listened on fixed ports in the ephemeral range (393xx), which any
9
+ outgoing connection of a test running beside them could take: CI failed with `EADDRINUSE` on runs with nothing
10
+ wrong. Every one now listens on a port the system gives it.
11
+ - **Brotli at quality 5** (`@plitzi/sdk-server`): `compression.brotliQuality`, for a page rendered for one request, is
12
+ 5 (was 2). At 2 a 220 KB page came out larger than gzip — 36 KB against 31 — and at 5 it is 29 KB, for the CPU gzip
13
+ spends; on half a core ~0.1 ms a page.
14
+ - **`verify` checks the pages for signed-in visitors** (`@plitzi/cli`): a page that sends the browser to sign in is
15
+ checked again signed in as the account `.env` names — `PLITZI_CHECK_USER`, `PLITZI_CHECK_PASSWORD` (the environment
16
+ wins over `.env`). With none named, `verify` says how. `page check --as` reads the password from `.env` too, and
17
+ `page check` takes several pages, in one browser.
18
+ - **An address that shows nothing answers 404** (`@plitzi/sdk-shared`, `@plitzi/plitzi-sdk`, `@plitzi/sdk-server`):
19
+ one no page answers was sent to the home page with a 302 — every typo read as the home page. A page whose slug is
20
+ `'*'` is now the space's page for it, sent with status 404 (a folder may have its own; the deepest wins), and a page
21
+ gated off by a flag shows it too. Never kept in the page cache: an address asked for at random filled it. Only a slug
22
+ that IS `'*'`: a page answering a subtree by its own (`spaces/:spaceId/update/*`) stays a page like any other.
23
+ - **`authorSpace` gives a space with no `'*'` page a plain one** (`@plitzi/sdk-authoring`), in the home page's layout,
24
+ and says so: suggestion `not-found-page`. The project templates carry their own.
25
+ - **`notFound` on a server provider** (`@plitzi/sdk-elements`, `@plitzi/sdk-server`): an expression against its answer
26
+ (`{{ source.found == false }}`); when it is true the page is sent with status 404, rendered as written. Refused on a
27
+ browser provider (`not-found-in-browser`) and when it is not one expression (`not-found-not-a-template`).
28
+ - **`page check` and `verify` know a page sent with 404** (`@plitzi/cli`): it is said, not failed; the space's `'*'`
29
+ page is checked at an address no page has, and fails if it is not sent with 404.
30
+ - **A form field draws no background of its own** (`@plitzi/sdk-elements`): the `<input>` inside a form control's box
31
+ (`field`) took `background-color: inherit`, and painted a translucent box colour a second time — a darker box inside
32
+ the padding (Tremor's search). It is `transparent`: the box shows through it.
33
+ - **`space lint` offers `quiet` only where there is an element to put it on** (`@plitzi/cli`): a suggestion about what
34
+ the space lacks — `not-found-page` — is answered by writing it.
35
+ - **`feedback --previous`** (`@plitzi/cli`): the brief says what to do when an earlier report's link no longer opens —
36
+ say so, and ask the person for the highest id it had.
37
+ - **`start:prod` runs in production by itself** (`@plitzi/cli`): `NODE_ENV=production node …`. Without the variable a
38
+ deployed project was a development server — dev tools on, and every public action answering its `steps` and `trace`
39
+ (each step's result, what the `output` was meant to leave out). `doctor` says a `start:prod` that does not
40
+ (`start-prod-not-production`); `plitzi upgrade --write` rewrites the script.
41
+ - **A project's `ActionRefusal` reaches the flow** (`@plitzi/sdk-server`): a functions bundle carries its own copy of the
42
+ class, so `instanceof` never knew it and the run lost its reason (`{{ x.error }}` empty). Told apart by name now.
43
+ - **Plugins are not remounted after hydration** (`@plitzi/sdk-elements`): the registry wrapped each plugin again when
44
+ the host handed it over again, a new component type every time — the server's HTML thrown away, entrances replayed,
45
+ effects run twice. Each plugin component is wrapped once.
46
+ - **`start:dev` survives a space that does not author** (`@plitzi/sdk-server`): a restart while the space had an error
47
+ died, and fixing the space woke nothing. It comes up answering every page with what is wrong (503), and loads the
48
+ space the first time a save authors.
49
+ - **`page check` sees what is cut off at the screen's edge** (`@plitzi/sdk-authoring`, `@plitzi/cli`): a link or words an
50
+ ancestor hides past the viewport, which no sideways scroll shows (`cut-off`).
51
+ - **`page shot --click <element...> --sheet`** (`@plitzi/cli`): elements clicked in order — a name or a CSS selector,
52
+ inside a plugin too — pictured after each (`--frames`/`--every`), with how much changed, and every picture on one
53
+ contact sheet. The dev tools' badge is no longer in a picture: its shadow root is marked (`@plitzi/sdk-dev-tools`).
54
+ - **`formControl` is optional unless it says `required: true`** (`@plitzi/sdk-elements`), as an HTML field is. A field
55
+ left without `required` in an older space was required, and is not now.
56
+ - **`text({ decorative })`, `list({ label })`** (`@plitzi/sdk-elements`); **`writing-mode`, `text-orientation`** in the
57
+ CSS vocabulary (`@plitzi/sdk-shared`).
58
+ - **`unused-token` reads the plugins' stylesheets** (`@plitzi/sdk-authoring`): `authorSpace`'s `stylesheets`, which
59
+ `projectAuthoring` fills with every `.css` under `src/plugins/` — a token only a plugin used was said to be unused.
60
+ - **`id-taken` on a provider placed on two pages points at the layout they share** (`@plitzi/sdk-authoring`), not at
61
+ `scope()`, which renamed its source and every template reading it.
62
+ - **The welcome template's tokens keep their names** (`@plitzi/sdk-authoring`): `satisfies`, not an annotation, so
63
+ `tokens(variables).card` typechecks as the cheatsheet teaches.
64
+
65
+ ## 0.38.8
66
+
67
+ ### Patch Changes
68
+
69
+ - 1b45aae: ## The CLI's commands are named after what they act on
70
+
71
+ Every command sits under the thing it acts on — `plitzi element move`, never a bare `move` that could mean anything —
72
+ and only what acts on the project as a whole or the account stays at the top:
73
+
74
+ | Before | Now |
75
+ | -------------------------------------------- | ------------------------------------------------------------------ |
76
+ | `where`, `edit`, `remove`, `move` | `element where`, `element edit`, `element remove`, `element move` |
77
+ | `check`, `shot`, `import` | `page check`, `page shot`, `page import` |
78
+ | `space`, `pull`, `push`, `lint`, `fix` | `space use`, `space pull`, `space push`, `space lint`, `space fix` |
79
+ | `add plugin`, `pack plugin`, `upload plugin` | `plugin add`, `plugin pack`, `plugin upload` |
80
+ | `add runtime` | `runtime add` |
81
+ | `pack source` | `source pack` |
82
+ | `update` (alias of `upgrade`) | `upgrade` |
83
+
84
+ `create`, `verify`, `doctor`, `upgrade`, `explain`, `feedback`, `login`, `logout` and `whoami` stay at the top, beside
85
+ `functions`, `runtime`, `data` and `skills`. There are no aliases: a name asked for at the top that is a group's command
86
+ answers where it is — `There is no plitzi push: it is one of plitzi space push, plitzi runtime push, plitzi functions
87
+ push` — read off the commands themselves. A project's scripts (`lint:space`, `check`, `shot`) call the new names;
88
+ `plitzi upgrade --write` updates the ones the CLI wrote.
89
+
90
+ ## Agents load less to do the same (RFC 0025)
91
+ - **The MCP lists the operations vocabulary once.** A connection with a space listed ~69k tokens of tools before an
92
+ agent did anything — the operations schema five times. `plitzi_apply` carries it; `plitzi_render` lists only its
93
+ operations' types beside it (and keeps the whole schema on a guest connection, where it is alone). The listing is
94
+ now ~18k tokens.
95
+ - **`plitzi_validate` is `plitzi_apply`'s `dryRun`**, which already answered the same; the `validate` function, its
96
+ `validateShape` and `ValidateInput` are gone from `@plitzi/sdk-mcp`.
97
+ - **`plitzi_apply` looks at the page it would leave**: `look: 'html' | 'image' | 'accessibility' | 'both'` (with
98
+ `pageRef`, `viewport`, `fullPage`) renders it — with `dryRun`, as the batch would leave it, nothing saved; without, as
99
+ saved. Check, look and save with the operations written twice, not four times.
100
+ - **`plitzi_look` is the one way to see a saved page** — its accessibility outline (the default: text, cheap), its HTML
101
+ or a PNG; `plitzi_preview` and `plitzi_screenshot` are gone. It is always offered: without a browser service it answers
102
+ the HTML and says so.
103
+ - **`plitzi_describe_operation { type }`** answers one operation's schema, or every type there is; a type that does not
104
+ exist is answered with the nearest one.
105
+ - `closest` is exported from `@plitzi/sdk-authoring`: the nearest of a closed list of names.
106
+ - **`plitzi element where <id | class | words>`** answers where the project's code writes an element — the file, the line and
107
+ the call itself — asked of the code as it is now, so it follows an element wherever somebody moved it. A class is
108
+ found by its name or by the variable that holds it (`nav-link`, `navLink`).
109
+ - **`plitzi element edit <id> --set key=value --remove key`** writes attributes in that call (`content` where the factory takes
110
+ it first), keeping each one's kind, formatted as the project formats, and keeps the change only if the space still
111
+ authors with every value there. Behind both, `locateElements` in `@plitzi/sdk-authoring`: every element with the call
112
+ that wrote it.
113
+ - A generated project's `AGENTS.md` says to ask `where` instead of keeping a note of where things are.
114
+ - **`plitzi doctor` says one fact once**: packages installed from the same place, and packages behind the same version,
115
+ are one line each — on a project installed from local tarballs, ~1,050 tokens of output became ~330.
116
+ - **A mistake costs one line.** An operation type that does not exist is answered with the nearest one; a field an
117
+ operation, an element, a binding or a flow step does not have is refused naming the one meant — it used to be dropped,
118
+ and `prop` for `props` answered success having applied nothing. An element type spelt with other capitals
119
+ (`Heading`) is read as the catalog spells it and said in `warnings`.
120
+ - **The same batch refused twice is not run a third time** (`REPEATED_BATCH`), and the second refusal says so; `plitzi
121
+ edit` does the same, kept in the project's `tmp/refusals.json`.
122
+ - **`plitzi_apply`'s `look` renders a batch through the path a save takes** (`draftBatch`): a
123
+ `repeatElement` expanded, what has one reading read, the same refusals.
124
+ - **Each skill is a core an agent reads every time, and references it opens when the task names one.** The core —
125
+ what it is for, the rules that go wrong most, a table routing each task to its one file — is held to 1,500 tokens:
126
+ `plitzi-authoring` went from ~4,000 to ~920 (every rule kept in `reference/rules.md`, the recipes indexed in
127
+ `reference/recipes.md`, every reference in `reference/index.md`), `plitzi-cli` from ~4,000 to ~820 (projects,
128
+ plugins and troubleshooting are references of their own), `plitzi-render` from ~3,100 to ~1,050.
129
+ - **Intent tools on the MCP**: `plitzi_set_attributes`, `plitzi_set_classes` (classes added or removed, the rest
130
+ kept), `plitzi_bind_attribute`, `plitzi_place_component`, `plitzi_add_page` — a few parameters, the element by its ref alone (the
131
+ page is found, a ref that does not exist answered with the nearest), checked and saved as `plitzi_apply` saves, and
132
+ answered in a line with the next step.
133
+ - **An element may only wear a class the space has, or the batch declares**: `plitzi_apply` used to save one nothing
134
+ defines — rendered unstyled, said by nobody. It is refused naming the nearest class.
135
+ - **`plitzi explain` answers any export of `@plitzi/sdk-authoring`** — `pageFamily`, `styles`, `SpaceSpec` — with its
136
+ signature and the first paragraph of its doc, read from the `.d.ts` the project installed: a few dozen tokens where an
137
+ agent used to search ~180k of published types.
138
+ - **`plitzi element where --by id|class|text`** reads a query one way; without it, the first reading that matches is answered
139
+ and every other one that matched is said with its count and the command for it — a query that means two things is
140
+ never answered as one. `plitzi element edit` reads its element by id alone.
141
+ - **A helper written once and called for many elements** is told apart: `locateElements` answers each element's
142
+ `through` — the calls of the author's code that led to the one that wrote it — so `plitzi element where` says which other
143
+ elements the same call writes and the call that leads to this one alone, and `plitzi element edit` changes a value the helper
144
+ is handed where it is handed (`pageHead('about-head', 'About us')`), refuses an edit of a call that writes several
145
+ elements unless `--every` says so, and never writes `content` beside words given as the factory's first argument.
146
+ - `plitzi explain` of a name that is the project's own says where to read it.
147
+ - **Nothing a write does is silent.** `plitzi_apply` answers `effects`: every change the batch made, read off the space
148
+ before and after it (elements added or removed by subtree, attributes, classes, moves, styles, settings, connectors,
149
+ actions), with a note where a changed attribute is one a binding computes; a batch that changed nothing says so, and
150
+ a store with no persister leads the warnings as `NOT saved: …`. The intent tools answer those `effects`, whether it
151
+ was `saved` and what was already so, refuse to take off a class the element does not wear and to write a value under
152
+ a binding. `plitzi element edit` reads the whole space before and after the edit (`plitzi element readings`, in a fresh process),
153
+ prints every change, and puts the files back when anything changed that was not asked, naming it; it refuses an
154
+ attribute a binding computes, and `plitzi element where` marks those `Bound`. `canonicalJson` (`@plitzi/sdk-shared`) is
155
+ what every before-and-after comparison is made with. Authoring keeps deep enough a call stack (64 frames) that a
156
+ helper inside a helper is still told apart.
157
+ - `locateElements` answers the parts of every component too, in the component (`rootId` is its id): `plitzi element where`
158
+ and `plitzi element edit` reach an element inside a component as they reach one on a page or a layout.
159
+
160
+ ## Fixed
161
+ - **A layout no longer mounts twice when the page hydrates.** An element with `runtime: 'server'` was wrapped in its
162
+ static shell while hydrating and handed back without it — under another key — on the next render, so React tore
163
+ down everything under it and built it again. With a server provider around a layout (an `apiContainer` that draws
164
+ no markup of its own), the whole layout's DOM was replaced on every page, and every `motion` arrival in it played
165
+ twice: a visible flicker on load. The shell now stays around the element on both sides of hydration, under the same
166
+ key, and only stops freezing (`frozen`).
167
+ - **A component's instance is written somewhere.** `component(…)` — and each child it places in a slot — rebuilt its
168
+ spec with a spread and lost the marker of where the author wrote it: `plitzi element where` could not place an instance and
169
+ `plitzi element edit` refused it. The original marker is carried (`carryWrittenAt`); `where` finds the words an instance
170
+ hands its component, and `edit` writes an instance's props.
171
+ - `plitzi element edit` follows a value read off a list the call is repeated for (`item.question` in
172
+ `QUESTIONS.flatMap(item => …)`) to the one entry that holds it, in the file the list is written in; when the shared
173
+ value is not a literal it says where it comes from instead of offering `--every`.
174
+ - **`plitzi page check --element` says which class wins.** Each property more than one of the element's classes sets, at
175
+ rest: the value shown, the class it comes from and what the others say — or that they all set it so. Which wins is
176
+ asked of the page, each class taken off for a moment; a style change is verified in text, not with a picture.
177
+ - **`plitzi element where` reads more.** By words it finds every word an element says (`words` on `WrittenElement`: content,
178
+ `label`, `title`, `alt`, `placeholder`, binding templates, an instance's props); by class it says where the class
179
+ is declared (`locateClasses` in `@plitzi/sdk-authoring`); an element repeated over a list says which list, and its
180
+ file; a page or a layout is placed at the object it is declared as.
181
+ - **`plitzi element edit` edits a page** by the attributes `where` shows (`seoPageTitle` written as `seoTitle`;
182
+ `PAGE_SPEC_FIELDS` in `@plitzi/sdk-authoring`), refusing `layout` and `seoEnabled`, which are no field of their own;
183
+ and a value read in more than one place (a list entry a nav and a menu both draw) changes them all only with
184
+ `--every`, each named.
185
+ - **A component prop of a type that does not exist is refused** (`prop-type-unknown`, with the types there are):
186
+ `type: 'string'` was accepted at run time, offered by no editor and checked against nothing. `BUILTIN_PARAM_TYPES`
187
+ in `@plitzi/sdk-shared` is the list, and `BuiltinParamType` is derived from it.
188
+ - **`plitzi element remove <id>` and `plitzi element move <id> --before|--after <id>`** take an element out of the code that writes
189
+ it, or reorder it among its siblings — following a section a helper returns to the helper's call — checked as
190
+ `edit` is: a removal may take only the element and what it holds, a move only reorder its parent, or the file goes
191
+ back. Styles and imports only the removed call used go with it, named; a helper left unread is said.
192
+ - **`plitzi verify`** runs the project's checks — author (no warning), lint:space, typecheck, lint, format — and every
193
+ page with no parameter, and prints only what fails; a page it could not open is said as not checked. Generated
194
+ projects get `npm run verify`, and their `AGENTS.md` names it as the way to leave the project passing.
195
+ - **A component's refusal says where it is written** (by its root's call).
196
+ - **`check --element`** says a class changes nothing on the element only when every property it sets stays without
197
+ it, and names the other elements the class is on: changing the class changes them; taking it off this element does
198
+ not.
199
+ - **An element's generated selector and binding ids are named after its id**, under its parent's place, not after its
200
+ position among its siblings: a move renames nothing. Every space written in code gets new generated names once —
201
+ the same rules, so nothing a visitor sees changes.
202
+ - A change of an element's children is said as what came, went or moved (`faq moved — now after hero, before pricing`).
203
+ - **`plitzi page check --click <id>`** clicks one element and says what changed — flows run and how each step ended, the
204
+ page it went to, what scrolled, what is shown now and what no longer is, the state — or that nothing did, in those words; a flow that
205
+ succeeded while nothing on the page changed is said as that. A click that could not be made, or a flow that failed,
206
+ fails the check.
207
+ - **A refusal of the authored space's gate says where**: each of the validator's errors (`UNRESOLVED_INTERACTION_TARGET`,
208
+ `UNRESOLVED_BINDING_SOURCE`, …) carries the file and line of the element it is about, and a name nothing answers to is
209
+ offered the nearest element that answers the step it was for (`openModal('search')` → `search-modal`, not the
210
+ `search-q` field), or the few that do. `SchemaValidationError` gains `missing` and `wantedBy` (`@plitzi/sdk-schema`).
211
+ - **`plitzi element where` finds the words a list's rows show** when the list is handed them as data (`items:
212
+ [...PLANS]`), and says the list they are the entries of and its file (`Its rows are the entries of PLANS
213
+ (src/space/enterprise/content.ts)`).
214
+ - `plitzi page check --click` says what is **shown** now and what no longer is (drawn, wherever the page is scrolled),
215
+ not "on screen".
216
+ - `plitzi upgrade` says packages installed locally in one line past three, and a skill rewritten within the same
217
+ version as that, not `0.38.7 → 0.38.7`.
218
+ - **What `element edit`, `remove` and `move` read back goes through the gate `npm run author` does**: a change that
219
+ leaves the space refused — a flow still opening a modal that was removed — is put back, the refusal said with where.
220
+ `element remove` refuses before writing when another element's flows act on what it takes away, and says what it
221
+ took once, by the outermost element (`search-modal removed, with 18 inside`). Moving an element beside itself is
222
+ refused as nothing to do.
223
+ - **An id, a plugin folder or a data file written a letter off is offered the nearest one** (`element where`, `edit`,
224
+ `remove`, `move`, `plugin pack`, `data describe`).
225
+ - **A project file that does not load is said at its file and line** — `src/space/hero.ts:139: Expected ','` — by the
226
+ server, `npm run author` and every CLI command, never as a stack (`ProjectModuleError` and `moduleProblem` in
227
+ `@plitzi/sdk-authoring/node`).
228
+ - **`plitzi verify` stops at the first failure** — one broken file fails every later step the same way — and says the
229
+ rest as not run (`--keep-going` runs them anyway); `--no-pages` no longer fails a run that passed.
230
+ - **`plitzi space fix` no longer drops attributes written one level too deep**: `attributes: { value: 5 }` where
231
+ `value: 5` was meant is put in its place (a new `unwrap` fix), not removed with what it said.
232
+ - **`plitzi explain <type>` explains the project's own elements** (`src/plugins/`), and `plugin add` prints how to place
233
+ one with the attributes it was declared with.
234
+ - **`plitzi data describe` says an object keyed by data once**, as a map of one shape with how many keys, so a file of
235
+ five hundred articles reads as one.
236
+ - **`plitzi page check` says a path no page answers before opening it**, with the paths the pages do answer — never a
237
+ redirect home read as a page for signed-in visitors.
238
+ - **`copyToClipboard(text)`**, a utility step (`@plitzi/sdk-interactions`, built with `copyToClipboard` in
239
+ `@plitzi/sdk-authoring`): a "Copy link" button had no step to write with. A browser that gives the page no clipboard
240
+ fails the step, so a toast after it is never said of a copy that was not made.
241
+ - **`plitzi explain` lists the values a param takes only when it is a choice** (`select`): `setState`'s `value` read
242
+ as `'true' | 'false'`, the examples of a free value taken for its only ones.
243
+ - **`plitzi page check --click` says what the page said** — a toast or an alert, `said: "Link copied"` — and **a form
244
+ the browser held back**, with the field and the browser's reason, never as a click that changed nothing.
245
+ - **`plitzi page check --click <id> --fill <id>=<value>`** fills fields before the click as a visitor does (typed, an
246
+ option chosen, a box ticked), so a form's success flow is checked, not only its refusal when empty. A container of
247
+ several fields is refused with their ids; a field filled inside another element says whose it is.
248
+
3
249
  ## 0.38.7
4
250
 
5
251
  ### Patch Changes
@@ -64,7 +64,7 @@ export interface ElementDeclarationData {
64
64
  /**
65
65
  * Whether the element is behaviour rather than content — a clock that fires a flow, a listener — and draws nothing on
66
66
  * a page (at most a tag in the builder, to have something to select). Read by the page checks (`inspectPage`,
67
- * `plitzi check`), which would otherwise wait for it to be on screen and report it hidden.
67
+ * `plitzi page check`), which would otherwise wait for it to be on screen and report it hidden.
68
68
  */
69
69
  drawsNothing?: boolean;
70
70
  content?: {
@@ -1,6 +1,6 @@
1
1
  import { toBuilderParams as e, toInteractionCallback as t, toInteractionCallbacks as n } from "./builder.mjs";
2
2
  import { elementDeclaration as r, valuesOf as i } from "./declare.mjs";
3
- import { hiddenParams as a, invalidParams as o, missingRequiredParams as s, reconcileParams as c } from "./paramSpec.mjs";
4
- import { BUILTIN_ELEMENT_CALLBACKS as l, SCROLL_CALLBACKS as u, applyElementCallback as d, getElementCallback as f } from "./elementCallbacks.mjs";
5
- import { spaceCallbacks as p } from "./spaceCallbacks.mjs";
6
- export { l as BUILTIN_ELEMENT_CALLBACKS, u as SCROLL_CALLBACKS, d as applyElementCallback, r as elementDeclaration, f as getElementCallback, a as hiddenParams, o as invalidParams, s as missingRequiredParams, c as reconcileParams, p as spaceCallbacks, e as toBuilderParams, t as toInteractionCallback, n as toInteractionCallbacks, i as valuesOf };
3
+ import { BUILTIN_PARAM_TYPES as a, hiddenParams as o, invalidParams as s, missingRequiredParams as c, reconcileParams as l } from "./paramSpec.mjs";
4
+ import { BUILTIN_ELEMENT_CALLBACKS as u, SCROLL_CALLBACKS as d, applyElementCallback as f, getElementCallback as p } from "./elementCallbacks.mjs";
5
+ import { spaceCallbacks as m } from "./spaceCallbacks.mjs";
6
+ export { u as BUILTIN_ELEMENT_CALLBACKS, a as BUILTIN_PARAM_TYPES, d as SCROLL_CALLBACKS, f as applyElementCallback, r as elementDeclaration, p as getElementCallback, o as hiddenParams, s as invalidParams, c as missingRequiredParams, l as reconcileParams, m as spaceCallbacks, e as toBuilderParams, t as toInteractionCallback, n as toInteractionCallbacks, i as valuesOf };
@@ -1,5 +1,6 @@
1
1
  import { InteractionParamType } from '../types';
2
- export type BuiltinParamType = 'text' | 'textarea' | 'select' | 'boolean' | 'number' | 'scalar' | 'json' | 'elementIds';
2
+ export declare const BUILTIN_PARAM_TYPES: readonly ["text", "textarea", "select", "boolean", "number", "scalar", "json", "elementIds"];
3
+ export type BuiltinParamType = (typeof BUILTIN_PARAM_TYPES)[number];
3
4
  export interface BuiltinParam {
4
5
  type: BuiltinParamType;
5
6
  description: string;
@@ -1,17 +1,26 @@
1
1
  import { hasValidToken as e } from "../helpers/twigWrapper/tokens/hasValidToken.mjs";
2
2
  //#region src/authoring/paramSpec.ts
3
- var t = (e, t, n) => {
3
+ var t = [
4
+ "text",
5
+ "textarea",
6
+ "select",
7
+ "boolean",
8
+ "number",
9
+ "scalar",
10
+ "json",
11
+ "elementIds"
12
+ ], n = (e, t, n) => {
4
13
  let r = [];
5
14
  for (let [i, a] of Object.entries(n)) a.when && e[i] !== void 0 && !a.when(t) && r.push(i);
6
15
  return r;
7
- }, n = (e, t, n) => {
16
+ }, r = (e, t, n) => {
8
17
  let r = [];
9
18
  for (let [i, a] of Object.entries(n)) a.required && e[i] === void 0 && (!a.when || a.when(t)) && r.push(i);
10
19
  return r;
11
- }, r = (e) => e.trim() !== "" && Number.isFinite(Number(e)), i = (t) => typeof t == "string" && e(t), a = (e, t) => {
20
+ }, i = (e) => e.trim() !== "" && Number.isFinite(Number(e)), a = (t) => typeof t == "string" && e(t), o = (e, t) => {
12
21
  switch (t.type) {
13
22
  case "boolean": return typeof e == "boolean";
14
- case "number": return typeof e == "number" || typeof e == "string" && r(e);
23
+ case "number": return typeof e == "number" || typeof e == "string" && i(e);
15
24
  case "select": return typeof e == "string" && (!t.options || t.options.includes(e));
16
25
  case "scalar": return typeof e == "string" || typeof e == "number" || typeof e == "boolean";
17
26
  case "json": return typeof e == "string" || typeof e == "number" || typeof e == "boolean" || typeof e == "object" && !!e;
@@ -20,23 +29,23 @@ var t = (e, t, n) => {
20
29
  case "elementIds": return Array.isArray(e) && e.every((e) => typeof e == "string");
21
30
  default: return !0;
22
31
  }
23
- }, o = (e, t, n) => {
32
+ }, s = (e, t, n) => {
24
33
  let r = [];
25
- for (let [o, s] of Object.entries(n)) {
26
- let n = e[o];
27
- n === void 0 || i(n) || s.when && !s.when(t) || a(n, s) || r.push({
28
- key: o,
34
+ for (let [i, s] of Object.entries(n)) {
35
+ let n = e[i];
36
+ n === void 0 || a(n) || s.when && !s.when(t) || o(n, s) || r.push({
37
+ key: i,
29
38
  expected: s.type,
30
39
  got: n === null ? "null" : typeof n,
31
40
  ...s.options ? { options: s.options } : {}
32
41
  });
33
42
  }
34
43
  return r;
35
- }, s = (e, t, n) => {
44
+ }, c = (e, t, n) => {
36
45
  let r = {};
37
46
  for (let [i, a] of Object.entries(e)) (!n || i in t) && (r[i] = a);
38
47
  for (let [e, n] of Object.entries(t)) n.default === void 0 || r[e] !== void 0 || n.when && !n.when(r) || (r[e] = n.default);
39
48
  return r;
40
49
  };
41
50
  //#endregion
42
- export { t as hiddenParams, o as invalidParams, n as missingRequiredParams, s as reconcileParams };
51
+ export { t as BUILTIN_PARAM_TYPES, n as hiddenParams, s as invalidParams, r as missingRequiredParams, c as reconcileParams };
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The attribute every piece of the dev tools' own chrome carries — the badge, the panel — so whatever looks at the
3
- * page rather than at the tools can leave it out: `plitzi shot` and `plitzi check` hide it, and a picture of a page is
3
+ * page rather than at the tools can leave it out: `plitzi page shot` and `plitzi page check` hide it, and a picture of a page is
4
4
  * a picture of the page.
5
5
  */
6
6
  export declare const DEV_TOOLS_ATTRIBUTE = "data-plitzi-devtools";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * A value as JSON with every object's keys in order, so two values that say the same thing in another order read the
3
+ * same: what a before-and-after comparison is made with, where a key moved is not a change. `undefined` reads
4
+ * `undefined`.
5
+ */
6
+ export declare const canonicalJson: (value: unknown) => string;
@@ -0,0 +1,5 @@
1
+ import { isRecord as e } from "./isRecord.mjs";
2
+ //#region src/helpers/canonicalJson.ts
3
+ var t = (t) => t === void 0 ? "undefined" : JSON.stringify(t, (t, n) => e(n) ? Object.fromEntries(Object.keys(n).toSorted().map((e) => [e, n[e]])) : n);
4
+ //#endregion
5
+ export { t as canonicalJson };
package/dist/index.mjs CHANGED
@@ -70,27 +70,27 @@ import jn from "./schema/SchemaContext.mjs";
70
70
  import { ANCHOR_PATTERN as Mn, anchorOf as Nn, isAnchor as Pn, uniqueAnchor as Fn } from "./schema/anchor.mjs";
71
71
  import { COMPONENT_PROPS_SOURCE as In, EMPTY_SCHEMA as Ln, VARIABLE_REGEX as Rn } from "./schema/schemaConstants.mjs";
72
72
  import { findLayoutCycle as zn, resolveLayoutChain as Bn } from "./schema/layoutChain.mjs";
73
- import { collectServerElements as Vn, hasServerElements as Hn } from "./schema/serverElements.mjs";
74
- import { isSnippet as Un } from "./schema/snippet.mjs";
75
- import Wn from "./network/NetworkContext.mjs";
76
- import Gn from "./network/NetworkInternalContext.mjs";
77
- import { elementFromWire as Kn, schemaFromWire as qn, schemaToWire as Jn } from "./schema/wire.mjs";
78
- import { SpaceEvents as Yn, spaceEventSchemas as Xn, validateSpaceEvent as Zn } from "./network/spaceEvents.mjs";
79
- import Qn from "./network/graphql/builder/Mutations/index.mjs";
80
- import $n from "./network/graphql/builder/Queries/index.mjs";
81
- import er from "./network/graphql/builder/Subscriptions/index.mjs";
82
- import tr from "./network/graphql/sdk/Mutations/index.mjs";
83
- import nr from "./network/graphql/sdk/Queries/index.mjs";
84
- import rr, { RTEvent as ir, RTEventCloseCode as ar, getInstance as or, isRTEvent as sr, isRTEventCloseCode as cr, setInstance as lr } from "./websockets/RTCodec.mjs";
85
- import { THEME_CLASSES as ur, THEME_COOKIE_NAME as dr, applyThemeClass as fr, isTheme as pr, readThemeCookie as mr, themeFromCookies as hr, writeThemeCookie as gr } from "./theme/themeCookie.mjs";
86
- import _r, { createThemeState as vr, createThemeStore as yr, resolveScheme as br, setAreaTheme as xr, setMachineScheme as Sr, setThemeMode as Cr, themeFor as wr } from "./theme/themeStore.mjs";
87
- import { useThemeStore as Tr } from "./theme/ThemeScope.mjs";
88
- import Er from "./theme/ThemeProvider.mjs";
89
- import Dr, { SPACE_THEME_AREA as Or } from "./theme/useTheme.mjs";
90
- import kr from "./server/actions/useActionsSync.mjs";
91
- import Ar, { cancelRsc as jr, currentRscLocation as Mr, releaseRscRequests as Nr, rscDataPath as Pr } from "./server/rsc/refreshRsc.mjs";
92
- import Fr from "./server/rsc/useRscRefresh.mjs";
93
- import Ir from "./server/rsc/useRscSync.mjs";
94
- import { PAINTED_STATE_BUDGET as Lr, clearPaintedEntry as Rr, paintedEntryFromCookies as zr, paintedKeys as Br, paintedStateCookieName as Vr, paintedStateFor as Hr, pickPainted as Ur, readPaintedEntry as Wr, writePaintedEntry as Gr } from "./state/paintedState.mjs";
95
- import { runtimeStatePersist as Kr, stateOwner as qr } from "./state/runtimeStatePersist.mjs";
96
- export { Mn as ANCHOR_PATTERN, x as BODYLESS_METHODS, y as BuilderContext, Qn as BuilderMutations, $n as BuilderQueries, In as COMPONENT_PROPS_SOURCE, N as COMPUTED_GLOBALS, S as CONNECTOR_HTTP_METHODS, an as CONTENT_PSEUDOS, mn as CURRENT_SELECTOR, Me as ComponentContext, C as DEFAULT_READ_ENDPOINT, Te as DEV_TOOLS_ATTRIBUTE, St as DISPLAY_MODES, gt as DISPLAY_MODE_MIN_WIDTH, we as DevToolsContext, w as EMPTY_RESPONSE_METHODS, Ln as EMPTY_SCHEMA, ft as EMPTY_STYLE_SCHEMA, hn as EXPANDED_SELECTOR, Ne as FLAG_GATE_PATTERN, Ue as FLAG_LAYERS, Pe as FLAG_NAME_PATTERN, Ft as FontValidationError, on as GENERATED_PSEUDOS, P as GLOBAL_SOURCES, gn as HIDDEN_CLASS, Ee as HIDE_DEV_TOOLS_CSS, ee as INTERVAL_TRIGGER, zt as LAYOUT_GRIDS, m as MAX_PARAM_PASSES, te as MIN_INTERVAL_MS, Yt as MOTION_CONDITIONS, Wn as NetworkContext, Gn as NetworkInternalContext, Lr as PAINTED_STATE_BUDGET, _n as PARENT_ANCESTOR, yt as PLUGIN_CSS_LAYER, sn as PSEUDO_PROPERTIES, oe as ParamsFromURL, ke as PlitziConsole, ot as PlitziServiceContext, st as PlitziServiceProvider, T as READ_ENDPOINT_NAMES, rr as RTCodec, ir as RTEvent, ar as RTEventCloseCode, bt as RUNTIME_CSS_LAYER, Ht as RUNTIME_STYLE_ID, Or as SPACE_THEME_AREA, Xt as STYLE_CONDITION_LABELS, Zt as STYLE_MOTION_CONDITIONS, cn as STYLE_PSEUDOS, ln as STYLE_PSEUDO_LABELS, vn as STYLE_STATES, yn as STYLE_STATE_LABELS, Tt as SYSTEM_FONTS, jn as SchemaContext, tr as SdkMutations, nr as SdkQueries, er as SpaceEventSubscription, Yn as SpaceEvents, pt as StyleBindingsAllowed, lt as StyleVariableCategory, ur as THEME_CLASSES, dr as THEME_COOKIE_NAME, Er as ThemeProvider, Rn as VARIABLE_REGEX, a as VISITOR_NAME, ct as WHILE_RUNNING_MODES, E as WRITE_ENDPOINT_NAMES, Nn as anchorOf, fr as applyThemeClass, t as authFailureFromResponse, Et as availableFonts, mt as baseDefaultValue, jr as cancelRsc, Qt as canonicalCondition, o as checkVisitorRoles, Rr as clearPaintedEntry, Vn as collectServerElements, $t as conditionAtRule, en as conditionLabel, tn as conditionOfAtRule, k as connectorPresets, D as connectorTokens, b as contexts, B as cookieFromHeader, Ae as createStoreDevToolsLogger, vr as createThemeState, yr as createThemeStore, Mr as currentRscLocation, De as debugCookieName, se as delay, Xe as describeChange, $e as diffSchema, et as diffStyle, he as disableReactDevTools, _t as displayModeAt, vt as displayModeWidthRem, V as documentCookies, Kn as elementFromWire, H as elementOf, A as emptyManifest, ce as emptyObject, F as evaluateComputed, ae as evaluateRuleGroup, Lt as fallbackForFamily, Dt as familiesInCss, L as fetchManifest, Ze as fieldChanges, c as filters, zn as findLayoutCycle, We as flagUserFromSSR, Ge as flagValues, Le as flagsCookieName, Ot as fontFamilyStack, wt as fontLinkAssets, kt as fontUrlResolver, Rt as fontsFromCss, At as fontsToHead, Re as forcedFlagsFromCookies, K as formatDate, q as formatDateUTC, J as formatFromNow, Y as formatUTCToLocal, R as generateFacade, ve as getBindingsDetails, O as getConnectorTokens, le as getDisplayName, X as getDurationMs, or as getInstance, ue as getKeyDecoded, de as getPathsFromObeject, ye as getSourceName, jt as googleCss2Url, Mt as googleTextSubsetUrl, Ke as hasConditions, Hn as hasServerElements, f as hasTemplateSyntax, p as hasValidToken, fe as hexToRGB, bn as inCascadeOrder, xt as inPluginLayer, ht as inheritableAttributesBase, g as inspectTemplate, ne as intervalOf, Pn as isAnchor, j as isConnectorManifestDraft, un as isContentValue, re as isDate, Ct as isDisplayMode, U as isElementTarget, Fe as isFlagName, pe as isInViewport, Oe as isInteractionFlow, xn as isKnownState, W as isNodeTarget, Sn as isParentAncestor, sr as isRTEvent, cr as isRTEventCloseCode, e as isRecord, Nt as isSafeFontUrl, Ie as isSchemaFlag, Un as isSnippet, Cn as isStartingState, nn as isStyleCondition, dn as isStylePseudo, Tn as isStyleRules, ut as isStyleVariableCategory, pr as isTheme, ie as isUserEdit, Z as isValidFormat, tt as jsonCopy, Bt as layoutGridCss, Vt as layoutGridLook, I as liveSources, me as makeId, Ut as markStyleCache, Kt as mergeSnippetStyle, G as nodeOf, n as onAuthFailure, je as pConsole, zr as paintedEntryFromCookies, Br as paintedKeys, Vr as paintedStateCookieName, Hr as paintedStateFor, rn as parseContainerCondition, Q as parseDate, ze as parseFlagList, It as parseSpaceFont, qe as passesFlagGate, Ur as pickPainted, Pt as primaryFamily, l as processTwig, u as processTwigParam, d as processTwigValue, fn as pseudoHonours, pn as pseudoSuffix, Wr as readPaintedEntry, mr as readThemeCookie, Ar as refreshRsc, Nr as releaseRscRequests, r as reportAuthFailure, Je as resolveFlags, Bn as resolveLayoutChain, br as resolveScheme, h as resolveStepParam, Se as resolveVariables, Pr as rscDataPath, Kr as runtimeStatePersist, i as sameRegistrableDomain, qt as sameSelector, nt as sameValue, qn as schemaFromWire, Jn as schemaToWire, Jt as selectorsOf, Be as serializeFlagList, xr as setAreaTheme, En as setAtKeys, lr as setInstance, Sr as setMachineScheme, Cr as setThemeMode, Xn as spaceEventSchemas, qr as stateOwner, wn as stateSuffix, Wt as styleCacheFromDocument, Gt as styleCacheTravelsInDocument, dt as styleConstants, Dn as styleTargetKeys, On as styleTargetProblem, kn as styleTargetRules, z as syntaxHighlight, _ as templatePaths, v as templateRootNames, wr as themeFor, hr as themeFromCookies, _r as themeStore, $ as toUnixSeconds, Ye as undeclaredFlagOverrides, Fn as uniqueAnchor, Qe as uniqueLines, kr as useActionsSync, rt as useFontHead, it as useNetwork, at as usePlitziServiceContext, be as useRegisterSource, Ce as useResolvedVariables, Fr as useRscRefresh, Ir as useRscSync, xe as useStableValue, Dr as useTheme, Tr as useThemeStore, ge as utility, _e as utilityOptions, M as validateConnectorManifest, Zn as validateSpaceEvent, An as valueAtKeys, s as visitorAccess, Ve as withForcedFlag, He as writeForcedFlags, Gr as writePaintedEntry, gr as writeThemeCookie };
73
+ import { collectServerElements as Vn, hasServerElements as Hn, notFoundProvider as Un } from "./schema/serverElements.mjs";
74
+ import { isSnippet as Wn } from "./schema/snippet.mjs";
75
+ import Gn from "./network/NetworkContext.mjs";
76
+ import Kn from "./network/NetworkInternalContext.mjs";
77
+ import { elementFromWire as qn, schemaFromWire as Jn, schemaToWire as Yn } from "./schema/wire.mjs";
78
+ import { SpaceEvents as Xn, spaceEventSchemas as Zn, validateSpaceEvent as Qn } from "./network/spaceEvents.mjs";
79
+ import $n from "./network/graphql/builder/Mutations/index.mjs";
80
+ import er from "./network/graphql/builder/Queries/index.mjs";
81
+ import tr from "./network/graphql/builder/Subscriptions/index.mjs";
82
+ import nr from "./network/graphql/sdk/Mutations/index.mjs";
83
+ import rr from "./network/graphql/sdk/Queries/index.mjs";
84
+ import ir, { RTEvent as ar, RTEventCloseCode as or, getInstance as sr, isRTEvent as cr, isRTEventCloseCode as lr, setInstance as ur } from "./websockets/RTCodec.mjs";
85
+ import { THEME_CLASSES as dr, THEME_COOKIE_NAME as fr, applyThemeClass as pr, isTheme as mr, readThemeCookie as hr, themeFromCookies as gr, writeThemeCookie as _r } from "./theme/themeCookie.mjs";
86
+ import vr, { createThemeState as yr, createThemeStore as br, resolveScheme as xr, setAreaTheme as Sr, setMachineScheme as Cr, setThemeMode as wr, themeFor as Tr } from "./theme/themeStore.mjs";
87
+ import { useThemeStore as Er } from "./theme/ThemeScope.mjs";
88
+ import Dr from "./theme/ThemeProvider.mjs";
89
+ import Or, { SPACE_THEME_AREA as kr } from "./theme/useTheme.mjs";
90
+ import Ar from "./server/actions/useActionsSync.mjs";
91
+ import jr, { cancelRsc as Mr, currentRscLocation as Nr, releaseRscRequests as Pr, rscDataPath as Fr } from "./server/rsc/refreshRsc.mjs";
92
+ import Ir from "./server/rsc/useRscRefresh.mjs";
93
+ import Lr from "./server/rsc/useRscSync.mjs";
94
+ import { PAINTED_STATE_BUDGET as Rr, clearPaintedEntry as zr, paintedEntryFromCookies as Br, paintedKeys as Vr, paintedStateCookieName as Hr, paintedStateFor as Ur, pickPainted as Wr, readPaintedEntry as Gr, writePaintedEntry as Kr } from "./state/paintedState.mjs";
95
+ import { runtimeStatePersist as qr, stateOwner as Jr } from "./state/runtimeStatePersist.mjs";
96
+ export { Mn as ANCHOR_PATTERN, x as BODYLESS_METHODS, y as BuilderContext, $n as BuilderMutations, er as BuilderQueries, In as COMPONENT_PROPS_SOURCE, N as COMPUTED_GLOBALS, S as CONNECTOR_HTTP_METHODS, an as CONTENT_PSEUDOS, mn as CURRENT_SELECTOR, Me as ComponentContext, C as DEFAULT_READ_ENDPOINT, Te as DEV_TOOLS_ATTRIBUTE, St as DISPLAY_MODES, gt as DISPLAY_MODE_MIN_WIDTH, we as DevToolsContext, w as EMPTY_RESPONSE_METHODS, Ln as EMPTY_SCHEMA, ft as EMPTY_STYLE_SCHEMA, hn as EXPANDED_SELECTOR, Ne as FLAG_GATE_PATTERN, Ue as FLAG_LAYERS, Pe as FLAG_NAME_PATTERN, Ft as FontValidationError, on as GENERATED_PSEUDOS, P as GLOBAL_SOURCES, gn as HIDDEN_CLASS, Ee as HIDE_DEV_TOOLS_CSS, ee as INTERVAL_TRIGGER, zt as LAYOUT_GRIDS, m as MAX_PARAM_PASSES, te as MIN_INTERVAL_MS, Yt as MOTION_CONDITIONS, Gn as NetworkContext, Kn as NetworkInternalContext, Rr as PAINTED_STATE_BUDGET, _n as PARENT_ANCESTOR, yt as PLUGIN_CSS_LAYER, sn as PSEUDO_PROPERTIES, oe as ParamsFromURL, ke as PlitziConsole, ot as PlitziServiceContext, st as PlitziServiceProvider, T as READ_ENDPOINT_NAMES, ir as RTCodec, ar as RTEvent, or as RTEventCloseCode, bt as RUNTIME_CSS_LAYER, Ht as RUNTIME_STYLE_ID, kr as SPACE_THEME_AREA, Xt as STYLE_CONDITION_LABELS, Zt as STYLE_MOTION_CONDITIONS, cn as STYLE_PSEUDOS, ln as STYLE_PSEUDO_LABELS, vn as STYLE_STATES, yn as STYLE_STATE_LABELS, Tt as SYSTEM_FONTS, jn as SchemaContext, nr as SdkMutations, rr as SdkQueries, tr as SpaceEventSubscription, Xn as SpaceEvents, pt as StyleBindingsAllowed, lt as StyleVariableCategory, dr as THEME_CLASSES, fr as THEME_COOKIE_NAME, Dr as ThemeProvider, Rn as VARIABLE_REGEX, a as VISITOR_NAME, ct as WHILE_RUNNING_MODES, E as WRITE_ENDPOINT_NAMES, Nn as anchorOf, pr as applyThemeClass, t as authFailureFromResponse, Et as availableFonts, mt as baseDefaultValue, Mr as cancelRsc, Qt as canonicalCondition, o as checkVisitorRoles, zr as clearPaintedEntry, Vn as collectServerElements, $t as conditionAtRule, en as conditionLabel, tn as conditionOfAtRule, k as connectorPresets, D as connectorTokens, b as contexts, B as cookieFromHeader, Ae as createStoreDevToolsLogger, yr as createThemeState, br as createThemeStore, Nr as currentRscLocation, De as debugCookieName, se as delay, Xe as describeChange, $e as diffSchema, et as diffStyle, he as disableReactDevTools, _t as displayModeAt, vt as displayModeWidthRem, V as documentCookies, qn as elementFromWire, H as elementOf, A as emptyManifest, ce as emptyObject, F as evaluateComputed, ae as evaluateRuleGroup, Lt as fallbackForFamily, Dt as familiesInCss, L as fetchManifest, Ze as fieldChanges, c as filters, zn as findLayoutCycle, We as flagUserFromSSR, Ge as flagValues, Le as flagsCookieName, Ot as fontFamilyStack, wt as fontLinkAssets, kt as fontUrlResolver, Rt as fontsFromCss, At as fontsToHead, Re as forcedFlagsFromCookies, K as formatDate, q as formatDateUTC, J as formatFromNow, Y as formatUTCToLocal, R as generateFacade, ve as getBindingsDetails, O as getConnectorTokens, le as getDisplayName, X as getDurationMs, sr as getInstance, ue as getKeyDecoded, de as getPathsFromObeject, ye as getSourceName, jt as googleCss2Url, Mt as googleTextSubsetUrl, Ke as hasConditions, Hn as hasServerElements, f as hasTemplateSyntax, p as hasValidToken, fe as hexToRGB, bn as inCascadeOrder, xt as inPluginLayer, ht as inheritableAttributesBase, g as inspectTemplate, ne as intervalOf, Pn as isAnchor, j as isConnectorManifestDraft, un as isContentValue, re as isDate, Ct as isDisplayMode, U as isElementTarget, Fe as isFlagName, pe as isInViewport, Oe as isInteractionFlow, xn as isKnownState, W as isNodeTarget, Sn as isParentAncestor, cr as isRTEvent, lr as isRTEventCloseCode, e as isRecord, Nt as isSafeFontUrl, Ie as isSchemaFlag, Wn as isSnippet, Cn as isStartingState, nn as isStyleCondition, dn as isStylePseudo, Tn as isStyleRules, ut as isStyleVariableCategory, mr as isTheme, ie as isUserEdit, Z as isValidFormat, tt as jsonCopy, Bt as layoutGridCss, Vt as layoutGridLook, I as liveSources, me as makeId, Ut as markStyleCache, Kt as mergeSnippetStyle, G as nodeOf, Un as notFoundProvider, n as onAuthFailure, je as pConsole, Br as paintedEntryFromCookies, Vr as paintedKeys, Hr as paintedStateCookieName, Ur as paintedStateFor, rn as parseContainerCondition, Q as parseDate, ze as parseFlagList, It as parseSpaceFont, qe as passesFlagGate, Wr as pickPainted, Pt as primaryFamily, l as processTwig, u as processTwigParam, d as processTwigValue, fn as pseudoHonours, pn as pseudoSuffix, Gr as readPaintedEntry, hr as readThemeCookie, jr as refreshRsc, Pr as releaseRscRequests, r as reportAuthFailure, Je as resolveFlags, Bn as resolveLayoutChain, xr as resolveScheme, h as resolveStepParam, Se as resolveVariables, Fr as rscDataPath, qr as runtimeStatePersist, i as sameRegistrableDomain, qt as sameSelector, nt as sameValue, Jn as schemaFromWire, Yn as schemaToWire, Jt as selectorsOf, Be as serializeFlagList, Sr as setAreaTheme, En as setAtKeys, ur as setInstance, Cr as setMachineScheme, wr as setThemeMode, Zn as spaceEventSchemas, Jr as stateOwner, wn as stateSuffix, Wt as styleCacheFromDocument, Gt as styleCacheTravelsInDocument, dt as styleConstants, Dn as styleTargetKeys, On as styleTargetProblem, kn as styleTargetRules, z as syntaxHighlight, _ as templatePaths, v as templateRootNames, Tr as themeFor, gr as themeFromCookies, vr as themeStore, $ as toUnixSeconds, Ye as undeclaredFlagOverrides, Fn as uniqueAnchor, Qe as uniqueLines, Ar as useActionsSync, rt as useFontHead, it as useNetwork, at as usePlitziServiceContext, be as useRegisterSource, Ce as useResolvedVariables, Ir as useRscRefresh, Lr as useRscSync, xe as useStableValue, Or as useTheme, Er as useThemeStore, ge as utility, _e as utilityOptions, M as validateConnectorManifest, Qn as validateSpaceEvent, An as valueAtKeys, s as visitorAccess, Ve as withForcedFlag, He as writeForcedFlags, Kr as writePaintedEntry, _r as writeThemeCookie };
@@ -1,4 +1,4 @@
1
1
  export { matchPath } from './matchPath';
2
- export { getPageFullPath, getPaths, isAbsoluteUrl, isPageAuthored, matchRoutePath, getRouteParams, getSlugParams, navigationTarget } from './routes';
2
+ export { getPageFullPath, getPaths, isAbsoluteUrl, isPageAuthored, matchRoutePath, notFoundPageFor, getRouteParams, getSlugParams, navigationTarget, NOT_FOUND_SLUG } from './routes';
3
3
  export type { PathMatch, PathPattern } from './matchPath';
4
4
  export type { NavigationAccessLevel, NavigationAction, Path } from './routes';
@@ -1,3 +1,3 @@
1
1
  import { matchPath as e } from "./matchPath.mjs";
2
- import { getPageFullPath as t, getPaths as n, getRouteParams as r, getSlugParams as i, isAbsoluteUrl as a, isPageAuthored as o, matchRoutePath as s, navigationTarget as c } from "./routes.mjs";
3
- export { t as getPageFullPath, n as getPaths, r as getRouteParams, i as getSlugParams, a as isAbsoluteUrl, o as isPageAuthored, e as matchPath, s as matchRoutePath, c as navigationTarget };
2
+ import { NOT_FOUND_SLUG as t, getPageFullPath as n, getPaths as r, getRouteParams as i, getSlugParams as a, isAbsoluteUrl as o, isPageAuthored as s, matchRoutePath as c, navigationTarget as l, notFoundPageFor as u } from "./routes.mjs";
3
+ export { t as NOT_FOUND_SLUG, n as getPageFullPath, r as getPaths, i as getRouteParams, a as getSlugParams, o as isAbsoluteUrl, s as isPageAuthored, e as matchPath, c as matchRoutePath, l as navigationTarget, u as notFoundPageFor };
@@ -11,6 +11,12 @@ export type Path = {
11
11
  path: string;
12
12
  unauthorizedBehaviour?: NavigationAction;
13
13
  unauthorizedPageRedirect?: string;
14
+ /**
15
+ * A path of the space's "not found" page — a page whose slug is `*`, the page of every address under its folder that
16
+ * no other page answers — sent with status 404. Not every path ending in `*`: `update/*` is a page of its own that
17
+ * answers a whole subtree.
18
+ */
19
+ notFound?: true;
14
20
  };
15
21
  /**
16
22
  * Does this name another origin, rather than somewhere in this space?
@@ -20,6 +26,8 @@ export type Path = {
20
26
  * external would turn a typo into an off-site redirect.
21
27
  */
22
28
  export declare const isAbsoluteUrl: (url: string) => boolean;
29
+ /** The slug of the space's "not found" page: the page of every address under its folder that no other page answers. */
30
+ export declare const NOT_FOUND_SLUG = "*";
23
31
  declare function getPageFullPath(pages: Record<string, Element>, pageFolders: PageFolder[], pageId: string, asString: true): string;
24
32
  declare function getPageFullPath(pages: Record<string, Element>, pageFolders: PageFolder[], pageId: string, asString?: false): Record<string, string>;
25
33
  /**
@@ -31,7 +39,7 @@ declare function getPageFullPath(pages: Record<string, Element>, pageFolders: Pa
31
39
  */
32
40
  declare const navigationTarget: (pages: Record<string, Element>, pageFolders: PageFolder[], target: string) => string;
33
41
  declare const isPageAuthored: (accessLevel?: NavigationAccessLevel, authenticated?: boolean, previewMode?: boolean) => boolean;
34
- declare const getPaths: (pages?: Record<string, Element>, pageFolders?: PageFolder[], authenticated?: boolean, basePath?: string, previewMode?: boolean, strictMode?: boolean) => Path[];
42
+ declare const getPaths: (pages?: Record<string, Element>, pageFolders?: PageFolder[], authenticated?: boolean, basePath?: string, previewMode?: boolean) => Path[];
35
43
  declare const matchRoutePath: (paths: Path[], pathName: string, authenticated: boolean) => {
36
44
  action: {
37
45
  type: "authenticated" | "accessDenied" | "redirect" | "notFound" | "normal";
@@ -40,7 +48,9 @@ declare const matchRoutePath: (paths: Path[], pathName: string, authenticated: b
40
48
  pathMatch?: PathMatch;
41
49
  pageId?: string;
42
50
  };
51
+ /** The space's "not found" page for an address — its deepest folder's, or the space's — when it declares one. */
52
+ declare const notFoundPageFor: (paths: Path[], pathName: string) => string | undefined;
43
53
  declare const getRouteParams: (path: string) => string[];
44
54
  /** The params a page's slug declares, in either spelling a slug accepts: `post/{{slug}}` and `:spaceId/update`. */
45
55
  declare const getSlugParams: (slug: string) => string[];
46
- export { getPageFullPath, getPaths, matchRoutePath, isPageAuthored, getRouteParams, getSlugParams, navigationTarget };
56
+ export { getPageFullPath, getPaths, matchRoutePath, notFoundPageFor, isPageAuthored, getRouteParams, getSlugParams, navigationTarget };
@@ -1,76 +1,63 @@
1
1
  import { matchPath as e } from "./matchPath.mjs";
2
2
  //#region src/navigation/routes.ts
3
- var t = (e) => /^https?:\/\//i.test(e), n = (e) => t(e) || e.includes("{{"), r = (e) => e.replaceAll(/\/+/g, "/"), i = (e) => r(e.replace(/{{([a-zA-Z0-9-_:*/]+)}}/gi, ":$1")), a = (e, t) => {
3
+ var t = (e) => /^https?:\/\//i.test(e), n = "*", r = (e) => t(e) || e.includes("{{"), i = (e) => e.replaceAll(/\/+/g, "/"), a = (e) => i(e.replace(/{{([a-zA-Z0-9-_:*/]+)}}/gi, ":$1")), o = (e, t) => {
4
4
  if (!t || !e[t]) return "";
5
5
  let { slug: n, parentId: r } = e[t];
6
- return r ? `${a(e, r)}/${n}` : n;
6
+ return r ? `${o(e, r)}/${n}` : n;
7
7
  };
8
- function o(e, t, n, o = !1) {
8
+ function s(e, t, n, r = !1) {
9
9
  let s = n.replace(/^\/+/, ""), { slug: c = "", folder: l, default: u } = e[s]?.attributes ?? {
10
10
  slug: s,
11
11
  folder: "",
12
12
  default: !1
13
13
  };
14
- if (u && !o) return {
14
+ if (u && !r) return {
15
15
  "/": n,
16
16
  [`/${n}`]: n
17
17
  };
18
- if (u && o) return "/";
19
- if (!l && !o) return {
20
- [i(`/${c}`)]: n,
21
- [i(`/${n}`)]: n
18
+ if (u && r) return "/";
19
+ if (!l && !r) return {
20
+ [a(`/${c}`)]: n,
21
+ [a(`/${n}`)]: n
22
22
  };
23
- if (!l && o) return r(`/${c}`);
24
- if (!t.find((e) => e.id === l)) return o ? r(`/${c}`) : {
25
- [i(`/${c}`)]: n,
26
- [i(`/${n}`)]: n
23
+ if (!l && r) return i(`/${c}`);
24
+ if (!t.find((e) => e.id === l)) return r ? i(`/${c}`) : {
25
+ [a(`/${c}`)]: n,
26
+ [a(`/${n}`)]: n
27
27
  };
28
- let d = [a(t.reduce((e, t) => ({
28
+ let d = [o(t.reduce((e, t) => ({
29
29
  ...e,
30
30
  [t.id]: t
31
31
  }), {}), l), c].filter(Boolean).join("/");
32
- return o ? r(`/${d}`) : {
33
- [i(`/${d}`)]: n,
34
- [i(`/${n}`)]: n
32
+ return r ? i(`/${d}`) : {
33
+ [a(`/${d}`)]: n,
34
+ [a(`/${n}`)]: n
35
35
  };
36
36
  }
37
- var s = (e, t, n) => {
37
+ var c = (e, t, n) => {
38
38
  let r = n.indexOf("#");
39
- if (r > 0) return `${s(e, t, n.slice(0, r))}${n.slice(r)}`;
39
+ if (r > 0) return `${c(e, t, n.slice(0, r))}${n.slice(r)}`;
40
40
  let i = e[n];
41
41
  if (!i) return n;
42
- let { slug: a, default: c } = i.attributes;
43
- return typeof a == "string" ? o(e, t, n, !0) : c ? "/" : `/${n}`;
44
- }, c = (e, t, n = !0) => !e || !n || t === void 0 || t && e === "authenticated" || !t && e === "public" ? !0 : t && e === "public" ? !1 : !e, l = (e = {}, t = [], r, i = "", a = !0, s = !0) => {
45
- let l = Object.keys(e).reduce((s, l) => {
46
- let { attributes: { accessLevel: u, enabled: d = !0, unauthorizedBehaviour: f } } = e[l], p = e[l].attributes.unauthorizedPageRedirect;
47
- if (!d && a) return s;
48
- p &&= n(p) ? p : o(e, t, p, !0);
49
- let m = o(e, t, l), h = Object.keys(m).map((e) => ({
50
- pageId: l,
51
- path: `${i}${e}`.replaceAll("//", "/"),
52
- accessLevel: u,
53
- enabled: d,
54
- isRaw: `/${l}` === e,
55
- unauthorizedBehaviour: f,
56
- unauthorizedPageRedirect: p,
57
- hasAccess: c(u, r, a)
58
- }));
59
- return [...s, ...h];
60
- }, []).filter((e) => e.path !== "*" || e.hasAccess).sort((e, t) => e.path === t.path ? e.accessLevel === "authenticated" ? -1 : 1 : e.path > t.path ? -1 : 1);
61
- if (!l.find((e) => e.path === "*") && !s) {
62
- let e = l.find((e) => e.path === "/" && e.hasAccess);
63
- e && e.unauthorizedBehaviour === "redirect" && e.unauthorizedPageRedirect && l.push({
64
- ...e,
65
- path: "*",
66
- hasAccess: !1,
67
- isRaw: !0,
68
- unauthorizedBehaviour: "redirect",
69
- unauthorizedPageRedirect: "/"
70
- });
71
- }
72
- return l;
73
- }, u = (t, n, r) => {
42
+ let { slug: a, default: o } = i.attributes;
43
+ return typeof a == "string" ? s(e, t, n, !0) : o ? "/" : `/${n}`;
44
+ }, l = (e, t, n = !0) => !e || !n || t === void 0 || t && e === "authenticated" || !t && e === "public" ? !0 : t && e === "public" ? !1 : !e, u = (e = {}, t = [], n, i = "", a = !0) => Object.keys(e).reduce((o, c) => {
45
+ let { attributes: { accessLevel: u, enabled: d = !0, unauthorizedBehaviour: f } } = e[c], p = e[c].attributes.unauthorizedPageRedirect;
46
+ if (!d && a) return o;
47
+ p &&= r(p) ? p : s(e, t, p, !0);
48
+ let m = s(e, t, c), h = e[c].attributes.slug === "*", g = Object.keys(m).map((e) => ({
49
+ pageId: c,
50
+ path: `${i}${e}`.replaceAll("//", "/"),
51
+ accessLevel: u,
52
+ enabled: d,
53
+ isRaw: `/${c}` === e,
54
+ unauthorizedBehaviour: f,
55
+ unauthorizedPageRedirect: p,
56
+ hasAccess: l(u, n, a),
57
+ ...h ? { notFound: !0 } : {}
58
+ }));
59
+ return [...o, ...g];
60
+ }, []).sort((e, t) => !!e.notFound == !!t.notFound ? e.path === t.path ? e.accessLevel === "authenticated" ? -1 : 1 : e.path > t.path ? -1 : 1 : e.notFound ? 1 : -1), d = (t, n, r) => {
74
61
  if (!n) return {
75
62
  action: {
76
63
  type: "accessDenied",
@@ -90,35 +77,28 @@ var s = (e, t, n) => {
90
77
  pageId: t.pageId
91
78
  });
92
79
  });
93
- let a = i.find(({ path: { accessLevel: e } }) => e === "authenticated" && r || e === "public" && !r || !e);
94
- if (a ||= i.find(({ path: { accessLevel: e } }) => e === "authenticated" && !r || e === "public" && r), !a && t.find((e) => e.path === "/" && e.hasAccess)) return {
95
- action: {
96
- type: "redirect",
97
- path: "/"
98
- },
99
- pathMatch: void 0
100
- };
101
- if (!a) return {
80
+ let a = (e) => e.find(({ path: { accessLevel: e } }) => e === "authenticated" && r || e === "public" && !r || !e) ?? e.find(({ path: { accessLevel: e } }) => e === "authenticated" && !r || e === "public" && r), o = a(i.filter(({ path: e }) => !e.notFound)) ?? a(i.filter(({ path: e }) => e.notFound));
81
+ if (!o) return {
102
82
  action: {
103
83
  type: "notFound",
104
84
  path: void 0
105
85
  },
106
86
  pathMatch: void 0
107
87
  };
108
- let { pageId: o, matchResult: s, path: { hasAccess: c, unauthorizedBehaviour: l, unauthorizedPageRedirect: u } } = a;
109
- return !c && l === "redirect" && u ? {
88
+ let { pageId: s, matchResult: c, path: { hasAccess: l, unauthorizedBehaviour: u, unauthorizedPageRedirect: d, notFound: f } } = o;
89
+ return !l && u === "redirect" && d ? {
110
90
  action: {
111
91
  type: "redirect",
112
- path: u
92
+ path: d
113
93
  },
114
94
  pathMatch: void 0
115
- } : c ? {
95
+ } : l ? {
116
96
  action: {
117
- type: "normal",
97
+ type: f ? "notFound" : "normal",
118
98
  path: void 0
119
99
  },
120
- pathMatch: s,
121
- pageId: o
100
+ pathMatch: c,
101
+ pageId: s
122
102
  } : {
123
103
  action: {
124
104
  type: "accessDenied",
@@ -126,6 +106,9 @@ var s = (e, t, n) => {
126
106
  },
127
107
  pathMatch: void 0
128
108
  };
129
- }, d = (e) => !e || typeof e != "string" ? [] : (e.match(/:[a-zA-Z0-9-_:*]+/gim) || []).map((e) => e.replace(":", "")), f = (e) => d(i(e));
109
+ }, f = (t, n) => t.find((t) => t.hasAccess && t.notFound && e({
110
+ path: t.path,
111
+ end: !0
112
+ }, n))?.pageId, p = (e) => !e || typeof e != "string" ? [] : (e.match(/:[a-zA-Z0-9-_:*]+/gim) || []).map((e) => e.replace(":", "")), m = (e) => p(a(e));
130
113
  //#endregion
131
- export { o as getPageFullPath, l as getPaths, d as getRouteParams, f as getSlugParams, t as isAbsoluteUrl, c as isPageAuthored, u as matchRoutePath, s as navigationTarget };
114
+ export { n as NOT_FOUND_SLUG, s as getPageFullPath, u as getPaths, p as getRouteParams, m as getSlugParams, t as isAbsoluteUrl, l as isPageAuthored, d as matchRoutePath, c as navigationTarget, f as notFoundPageFor };
@@ -90,7 +90,7 @@ export declare const spaceEventSchemas: {
90
90
  }, z.core.$strip>;
91
91
  /**
92
92
  * A plugin installed — or installed again at a new address, which is how a new version arrives: from the builder,
93
- * or from `plitzi upload plugin` with no builder open. An open builder loads it without a reload.
93
+ * or from `plitzi plugin upload` with no builder open. An open builder loads it without a reload.
94
94
  */
95
95
  readonly SPACE_ADD_PLUGIN: z.ZodObject<{
96
96
  plugin: z.ZodObject<{
@@ -145,7 +145,7 @@ export interface ProjectLayoutOptions {
145
145
  export declare const pluginTypeOf: (folder: string) => string;
146
146
  /**
147
147
  * The file an element's folder is built from, wherever the folder is — its `index.ts`, or `index.tsx` — or nothing
148
- * when it has neither, or both: what `plitzi pack plugin` builds a folder from, as the server does.
148
+ * when it has neither, or both: what `plitzi plugin pack` builds a folder from, as the server does.
149
149
  */
150
150
  export declare const elementEntry: (dir: string) => string | undefined;
151
151
  /**
@@ -195,7 +195,7 @@ var v = {
195
195
  action: "move",
196
196
  from: `${n}/${c}`,
197
197
  to: `${n}/${a}`
198
- })] : [y("plugin-declaration-missing", `${n}/`, `${n}/ has no ${a}: ${s}.`, `Write ${n}/${a} — export default { type: '${o}', … } — as npx plitzi add plugin writes one beside the component it makes.`)];
198
+ })] : [y("plugin-declaration-missing", `${n}/`, `${n}/ has no ${a}: ${s}.`, `Write ${n}/${a} — export default { type: '${o}', … } — as npx plitzi plugin add writes one beside the component it makes.`)];
199
199
  }, B = (e, t) => {
200
200
  if (!x.test(t)) return [te(t)];
201
201
  let n = `${i}/${t}`, r = M(t), a = ne(S(m.join(e, n)).files, t);
@@ -205,14 +205,14 @@ var v = {
205
205
  accepted: o,
206
206
  code: "plugin-entry-missing",
207
207
  lost: `the server cannot build the plugin ${r}`,
208
- write: `Write ${n}/index.ts exporting the component by default${a ? ` — export { default } from './${a}';` : ""} — as npx plitzi add plugin writes it. Every folder of ${i}/ is a plugin: code several plugins share goes outside it (src/shared/).`
208
+ write: `Write ${n}/index.ts exporting the component by default${a ? ` — export { default } from './${a}';` : ""} — as npx plitzi plugin add writes it. Every folder of ${i}/ is a plugin: code several plugins share goes outside it (src/shared/).`
209
209
  }),
210
210
  ...R(e, {
211
211
  folder: `${n}/${s}`,
212
212
  accepted: ["index.ts"],
213
213
  code: "plugin-functions-entry-missing",
214
214
  lost: `none of the plugin's server half runs (its routes under /fn/plugins/${r}/)`,
215
- write: `Write ${n}/${s}/index.ts exporting defineFunctions({ … }) by default — as npx plitzi add plugin --server writes it.`
215
+ write: `Write ${n}/${s}/index.ts exporting defineFunctions({ … }) by default — as npx plitzi plugin add --server writes it.`
216
216
  }),
217
217
  ...z(e, t)
218
218
  ];
@@ -225,7 +225,7 @@ var v = {
225
225
  type: t.type,
226
226
  isMain: t.isMain === !0
227
227
  }] : []) : [], U = (t, n) => {
228
- let r = `${p}/${n}`, i = `${r}/${c}`, a = `npx plitzi pull brings it again — or delete ${r}/, and the space renders no ${n}.`;
228
+ let r = `${p}/${n}`, i = `${r}/${c}`, a = `npx plitzi space pull brings it again — or delete ${r}/, and the space renders no ${n}.`;
229
229
  if (!C(t, i, "file")) return { problem: y("vendor-manifest-missing", `${r}/`, `${r}/ has no ${c}: the server runs a built plugin by what its manifest names, so ${n} does not run.`, a) };
230
230
  let o;
231
231
  try {
@@ -247,7 +247,7 @@ var v = {
247
247
  }, W = (e) => {
248
248
  let { files: t, folders: n } = S(m.join(e, i)), r = t.filter((e) => !b.has(e) && e !== "declarations.ts").map((e) => {
249
249
  let { name: t } = m.parse(e), n = t !== "index" && N(t) || "<Name>";
250
- return y("plugin-file-loose", `${i}/${e}`, `${i}/${e} is a file, and every plugin is a folder of ${i}/: the server reads nothing of it.`, `Give it a folder with an index.ts that exports it by default — ${i}/${n}/${e} and ${i}/${n}/index.ts, as npx plitzi add plugin writes them — or move it out of ${i}/.`);
250
+ return y("plugin-file-loose", `${i}/${e}`, `${i}/${e} is a file, and every plugin is a folder of ${i}/: the server reads nothing of it.`, `Give it a folder with an index.ts that exports it by default — ${i}/${n}/${e} and ${i}/${n}/index.ts, as npx plitzi plugin add writes them — or move it out of ${i}/.`);
251
251
  }), a = S(m.join(e, p)).folders, o = new Set(n.filter((e) => x.test(e)).map(M)), s = a.filter((e) => o.has(e)).map((e) => y("plugin-shadowed", `${p}/${e}/`, `${e} is both ${p}/${e}/, as it was built, and a folder of ${i}/: the server runs the built one, and a change to the source shows nowhere.`, `Delete ${p}/${e}/: the source is the plugin now.`)), c = a.flatMap((t) => {
252
252
  let n = U(e, t);
253
253
  return "problem" in n ? [n.problem] : [];
@@ -337,7 +337,7 @@ var v = {
337
337
  ...s,
338
338
  ...c
339
339
  ];
340
- }, oe = (e) => w(e, n).filter((e) => !b.has(m.posix.basename(e)) && !e.endsWith(".json")).map((e) => y("data-not-json", e, `${e} is not JSON: a provider's /data/<file> and ctx.data read JSON files only, and plitzi push leaves it behind.`, `Keep it as JSON (${e.replace(/\.[^./]*$/, "")}.json), or move it out of ${n}/.`)), se = /^\.env(?:\..*)?$|\.(?:pem|key|p12|pfx|jks|keystore|sqlite3?|db|sql|dump)$|secret|credential|password|passwd|private[-_.]?key|id_rsa|id_ed25519|service[-_]?account/i, ce = (e, t) => {
340
+ }, oe = (e) => w(e, n).filter((e) => !b.has(m.posix.basename(e)) && !e.endsWith(".json")).map((e) => y("data-not-json", e, `${e} is not JSON: a provider's /data/<file> and ctx.data read JSON files only, and plitzi space push leaves it behind.`, `Keep it as JSON (${e.replace(/\.[^./]*$/, "")}.json), or move it out of ${n}/.`)), se = /^\.env(?:\..*)?$|\.(?:pem|key|p12|pfx|jks|keystore|sqlite3?|db|sql|dump)$|secret|credential|password|passwd|private[-_.]?key|id_rsa|id_ed25519|service[-_]?account/i, ce = (e, t) => {
341
341
  let r = w(e, l, /* @__PURE__ */ new Set(["node_modules"])), i = r.filter((e) => se.test(m.posix.basename(e))).map((e) => y("public-secret-file", e, `${e} is served to anyone who asks, as it is — everything in ${l}/ is on the internet once the project is deployed — and its name says it holds a secret, a key or a database.`, `Take it out of ${l}/: settings go in the root's .env${t === "server" ? `, data only the server reads in ${n}/` : ""}.`)), a = `${l}/data`, o = t === "server" ? r.filter((e) => e.startsWith(`${a}/`) && e.endsWith(".json")) : [], s = o.slice(0, 3).map((e) => m.posix.basename(e)), c = o.length > 0 ? [y("public-data", `${a}/`, `${a}/ holds ${String(o.length)} JSON file${o.length === 1 ? "" : "s"} (${s.join(", ")}${o.length > s.length ? ", …" : ""}), served to anyone who asks: every visitor can read all of ${o.length === 1 ? "it" : "them"}, not only what a page shows.`, `Data only the server should read goes in ${n}/ — a provider reads it on the server (runtime: 'server', query: '/data/<file>'), a function with ctx.data('<file>'), and neither serves the file. What every visitor may read can stay.`)] : [];
342
342
  return [...i, ...c];
343
343
  }, Z = (t) => {
@@ -365,7 +365,7 @@ var v = {
365
365
  accepted: ["index.ts"],
366
366
  code: "runtime-entry-missing",
367
367
  lost: "the space’s runtime does not run",
368
- write: `Write ${u}/index.ts exporting defineRuntime(…) by default — npx plitzi add runtime writes one.`
368
+ write: `Write ${u}/index.ts exporting defineRuntime(…) by default — npx plitzi runtime add writes one.`
369
369
  }),
370
370
  ...n.space === "local" ? re(e, i) : [],
371
371
  ...i,
@@ -39,7 +39,7 @@ export declare const PLUGINS_DIR = "src/plugins";
39
39
  * and one of them, never both.
40
40
  */
41
41
  export declare const PLUGIN_ENTRIES: readonly ["index.ts", "index.tsx"];
42
- /** What a plugin folder declares itself in — its type, attributes, triggers — as `plitzi add plugin` writes it. */
42
+ /** What a plugin folder declares itself in — its type, attributes, triggers — as `plitzi plugin add` writes it. */
43
43
  export declare const PLUGIN_DECLARATION_FILE = "declaration.ts";
44
44
  /** A plugin's server half, a folder of its own beside its component: built from its `index.ts`. */
45
45
  export declare const PLUGIN_FUNCTIONS_DIR = "functions";
@@ -48,7 +48,7 @@ export declare const PLUGIN_FUNCTIONS_DIR = "functions";
48
48
  * beside the manifest it was published with (`PLUGIN_MANIFEST_FILE`).
49
49
  */
50
50
  export declare const VENDOR_PLUGINS_DIR = "vendor/plugins";
51
- /** What `plitzi pack plugin` writes beside a plugin's bundle: its version, its files, and the elements it provides. */
51
+ /** What `plitzi plugin pack` writes beside a plugin's bundle: its version, its files, and the elements it provides. */
52
52
  export declare const PLUGIN_MANIFEST_FILE = "plugin-manifest.json";
53
53
  /** A runtime that came across built only (a project made from a space): run as it was built. */
54
54
  export declare const RUNTIME_BUNDLE = "vendor/runtime.bundle";
@@ -2,6 +2,6 @@ import e from "./SchemaContext.mjs";
2
2
  import { ANCHOR_PATTERN as t, anchorOf as n, isAnchor as r, uniqueAnchor as i } from "./anchor.mjs";
3
3
  import { COMPONENT_PROPS_SOURCE as a, EMPTY_SCHEMA as o, VARIABLE_REGEX as s } from "./schemaConstants.mjs";
4
4
  import { findLayoutCycle as c, resolveLayoutChain as l } from "./layoutChain.mjs";
5
- import { collectServerElements as u, hasServerElements as d } from "./serverElements.mjs";
6
- import { isSnippet as f } from "./snippet.mjs";
7
- export { t as ANCHOR_PATTERN, a as COMPONENT_PROPS_SOURCE, o as EMPTY_SCHEMA, e as SchemaContext, s as VARIABLE_REGEX, n as anchorOf, u as collectServerElements, c as findLayoutCycle, d as hasServerElements, r as isAnchor, f as isSnippet, l as resolveLayoutChain, i as uniqueAnchor };
5
+ import { collectServerElements as u, hasServerElements as d, notFoundProvider as f } from "./serverElements.mjs";
6
+ import { isSnippet as p } from "./snippet.mjs";
7
+ export { t as ANCHOR_PATTERN, a as COMPONENT_PROPS_SOURCE, o as EMPTY_SCHEMA, e as SchemaContext, s as VARIABLE_REGEX, n as anchorOf, u as collectServerElements, c as findLayoutCycle, d as hasServerElements, r as isAnchor, p as isSnippet, f as notFoundProvider, l as resolveLayoutChain, i as uniqueAnchor };
@@ -23,3 +23,14 @@ import { Element, Schema } from '../types';
23
23
  export declare const collectServerElements: (schema: Schema, pageId: string | undefined, ids?: string[], flags?: Record<string, boolean>) => Element[];
24
24
  /** Whether anything on this page consumes server data at all — the question both RSC gates actually ask. */
25
25
  export declare const hasServerElements: (schema: Schema, pageId: string | undefined, flags?: Record<string, boolean>) => boolean;
26
+ /**
27
+ * The server provider of a page whose answer says the address shows nothing — its `notFound`, a template against
28
+ * that answer (`{{ source.found == false }}`) — by id; the page is then sent with status 404, rendered as written.
29
+ *
30
+ * Only `true` counts: a template that does not evaluate comes back as its own text, and a typo must leave the page
31
+ * found rather than turn every visit into a 404.
32
+ */
33
+ export declare const notFoundProvider: (elements: Element[], serverData: Record<string, unknown>, navigation: {
34
+ routeParams: Record<string, unknown>;
35
+ queryParams: Record<string, unknown>;
36
+ }) => string | undefined;
@@ -1,20 +1,27 @@
1
- import { passesFlagGate as e } from "../flags/resolveFlags.mjs";
2
- import { resolveLayoutChain as t } from "./layoutChain.mjs";
1
+ import { processTwigValue as e } from "../helpers/twigWrapper/processTwig/processTwig.mjs";
2
+ import { passesFlagGate as t } from "../flags/resolveFlags.mjs";
3
+ import { resolveLayoutChain as n } from "./layoutChain.mjs";
3
4
  //#region src/schema/serverElements.ts
4
- var n = (n, r, i, a) => {
5
+ var r = (e, r, i, a) => {
5
6
  if (r === void 0) return [];
6
- let o = i ? new Set(i) : void 0, s = [], c = /* @__PURE__ */ new Set(), l = n.flat[r], u = (e) => typeof e == "string" ? e : "", d = [r, ...l ? t((e) => n.flat[e], u(l.attributes.layout), u(l.attributes.layoutContainer)).map((e) => e.layout) : []];
7
+ let o = i ? new Set(i) : void 0, s = [], c = /* @__PURE__ */ new Set(), l = e.flat[r], u = (e) => typeof e == "string" ? e : "", d = [r, ...l ? n((t) => e.flat[t], u(l.attributes.layout), u(l.attributes.layoutContainer)).map((e) => e.layout) : []];
7
8
  for (; d.length > 0;) {
8
- let t = d.pop();
9
- if (t === void 0 || c.has(t)) continue;
10
- c.add(t);
11
- let r = n.flat[t];
12
- if (!r || a && !e(r.definition.flag, a)) continue;
9
+ let n = d.pop();
10
+ if (n === void 0 || c.has(n)) continue;
11
+ c.add(n);
12
+ let r = e.flat[n];
13
+ if (!r || a && !t(r.definition.flag, a)) continue;
13
14
  r.definition.runtime === "server" && (!o || o.has(r.id)) && s.push(r);
14
15
  let { items: i } = r.definition;
15
16
  i && d.push(...i);
16
17
  }
17
18
  return s;
18
- }, r = (e, t, r) => n(e, t, void 0, r).length > 0;
19
+ }, i = (e, t, n) => r(e, t, void 0, n).length > 0, a = (t, n, r) => t.find((t) => {
20
+ let i = t.attributes.notFound;
21
+ return typeof i == "string" && i !== "" && Object.hasOwn(n, t.id) && e(i, {
22
+ source: n[t.id],
23
+ navigation: r
24
+ }) === !0;
25
+ })?.id;
19
26
  //#endregion
20
- export { n as collectServerElements, r as hasServerElements };
27
+ export { r as collectServerElements, i as hasServerElements, a as notFoundProvider };
@@ -29,7 +29,7 @@ export type SpaceExport = {
29
29
  /** The version taken out: the draft (`main`, revision 0), or a snapshot an environment holds. */
30
30
  version: Pick<SpaceVersionContents, 'environment' | 'revision' | 'snapshot'>;
31
31
  /**
32
- * Which state of the draft's documents this is — its schema, style, actions and connectors — what `plitzi push`
32
+ * Which state of the draft's documents this is — its schema, style, actions and connectors — what `plitzi space push`
33
33
  * checks the space's draft has not moved on from. `null` for a snapshot, which no push writes.
34
34
  */
35
35
  draft: string | null;
@@ -62,7 +62,7 @@ export type SpaceExport = {
62
62
  };
63
63
  /**
64
64
  * The space's own data, by path in `src/data/` (JSON its providers read on the server, never served), and the version
65
- * of it this is — what `plitzi push` checks the space's copy has not moved on from.
65
+ * of it this is — what `plitzi space push` checks the space's copy has not moved on from.
66
66
  */
67
67
  data: {
68
68
  version: string;
@@ -1,6 +1,6 @@
1
1
  import { Schema, Style } from '../types';
2
2
  /**
3
- * A project's space put back on Plitzi as the space's draft (docs/en/projects-from-spaces.md): what `plitzi push` sends
3
+ * A project's space put back on Plitzi as the space's draft (docs/en/projects-from-spaces.md): what `plitzi space push` sends
4
4
  * to `PUT /spaces/:spaceId/import`, and what it is answered. The way back of `SpaceExport` — one shape for both ends.
5
5
  */
6
6
  export declare const SPACE_IMPORT_FORMAT = 1;
@@ -10,7 +10,7 @@ export declare const PLUGIN_CSS_LAYER = "plitzi-sdk-plugin";
10
10
  export declare const RUNTIME_CSS_LAYER = "plitzi-sdk-runtime";
11
11
  /**
12
12
  * A plugin's stylesheet, as it ships: everything in it in {@link PLUGIN_CSS_LAYER}. Written by whatever builds a
13
- * plugin — a server compiling one, `plitzi pack plugin` — so every way the file later reaches a page (a `<link>` in the
13
+ * plugin — a server compiling one, `plitzi plugin pack` — so every way the file later reaches a page (a `<link>` in the
14
14
  * server's HTML, the SDK's assets, the builder's canvas) puts it in its place with no work of its own.
15
15
  *
16
16
  * Idempotent: a stylesheet already wrapped is returned as it is.
@@ -178,6 +178,8 @@ declare const styleConstants: {
178
178
  readonly TEXT_INDENT: "text-indent";
179
179
  readonly TEXT_TRANSFORM: "text-transform";
180
180
  readonly DIRECTION: "direction";
181
+ readonly WRITING_MODE: "writing-mode";
182
+ readonly TEXT_ORIENTATION: "text-orientation";
181
183
  readonly WHITE_SPACE: "white-space";
182
184
  readonly TEXT_WRAP: "text-wrap";
183
185
  readonly WORD_BREAK: "word-break";
@@ -199,8 +201,8 @@ declare const styleConstants: {
199
201
  };
200
202
  export declare const inheritableAttributesBase: StyleCategory[];
201
203
  export declare const StyleBindingsAllowed: {
202
- path: "color" | "cursor" | "filter" | "fill" | "display" | "flex-wrap" | "flex-direction" | "align-items" | "justify-items" | "justify-content" | "align-content" | "row-gap" | "column-gap" | "grid-row-gap" | "grid-column-gap" | "grid-template-areas" | "grid-template-columns" | "grid-template-rows" | "grid-auto-flow" | "grid-auto-rows" | "grid-auto-columns" | "grid-row-start" | "grid-column-start" | "grid-row-end" | "grid-column-end" | "align-self" | "justify-self" | "order" | "flex-grow" | "flex-shrink" | "flex-basis" | "list-style" | "list-style-type" | "list-style-position" | "list-style-image" | "background-color" | "background-image" | "background-position" | "background-size" | "background-repeat" | "background-origin" | "background-clip" | "background-attachment" | "background-blend-mode" | "mask-image" | "mask-size" | "mask-position" | "mask-repeat" | "mask-composite" | "-webkit-mask-image" | "border-top-style" | "border-top-width" | "border-top-color" | "border-bottom-style" | "border-bottom-width" | "border-bottom-color" | "border-left-style" | "border-left-width" | "border-left-color" | "border-right-style" | "border-right-width" | "border-right-color" | "border-top-left-radius" | "border-top-right-radius" | "border-bottom-left-radius" | "border-bottom-right-radius" | "border-collapse" | "border-spacing" | "table-layout" | "opacity" | "visibility" | "transition" | "transition-property" | "transition-duration" | "transition-timing-function" | "transition-delay" | "transition-behavior" | "animation" | "animation-name" | "animation-duration" | "animation-timing-function" | "animation-delay" | "animation-iteration-count" | "animation-direction" | "animation-fill-mode" | "animation-play-state" | "animation-timeline" | "animation-range" | "box-shadow" | "backdrop-filter" | "mix-blend-mode" | "isolation" | "clip-path" | "transform" | "transform-origin" | "translate" | "rotate" | "scale" | "transform-style" | "perspective" | "perspective-origin" | "backface-visibility" | "will-change" | "outline-width" | "outline-style" | "outline-color" | "outline-offset" | "pointer-events" | "user-select" | "touch-action" | "resize" | "appearance" | "scroll-behavior" | "overscroll-behavior" | "scroll-snap-type" | "scroll-snap-align" | "scroll-snap-stop" | "scroll-padding-top" | "scroll-margin-top" | "accent-color" | "caret-color" | "color-scheme" | "content" | "position" | "float" | "clear" | "z-index" | "top" | "bottom" | "left" | "right" | "box-sizing" | "aspect-ratio" | "width" | "height" | "min-width" | "min-height" | "max-width" | "max-height" | "overflow" | "overflow-x" | "overflow-y" | "scrollbar-width" | "scrollbar-color" | "container-type" | "container-name" | "object-fit" | "object-position" | "column-width" | "column-count" | "margin-top" | "margin-bottom" | "margin-left" | "margin-right" | "padding-top" | "padding-bottom" | "padding-left" | "padding-right" | "font-family" | "font-weight" | "font-size" | "line-height" | "text-align" | "font-style" | "font-variant" | "font-variant-numeric" | "font-feature-settings" | "text-decoration" | "text-decoration-line" | "text-decoration-color" | "text-decoration-style" | "text-decoration-thickness" | "text-underline-offset" | "letter-spacing" | "word-spacing" | "text-indent" | "text-transform" | "direction" | "white-space" | "text-wrap" | "word-break" | "overflow-wrap" | "hyphens" | "vertical-align" | "text-overflow" | "text-shadow" | "line-clamp" | "-webkit-line-clamp" | "-webkit-box-orient" | "-webkit-text-fill-color" | "-webkit-background-clip" | "box-decoration-break" | "-webkit-box-decoration-break" | "stroke" | "stroke-width";
203
- label: "color" | "cursor" | "filter" | "fill" | "display" | "flex-wrap" | "flex-direction" | "align-items" | "justify-items" | "justify-content" | "align-content" | "row-gap" | "column-gap" | "grid-row-gap" | "grid-column-gap" | "grid-template-areas" | "grid-template-columns" | "grid-template-rows" | "grid-auto-flow" | "grid-auto-rows" | "grid-auto-columns" | "grid-row-start" | "grid-column-start" | "grid-row-end" | "grid-column-end" | "align-self" | "justify-self" | "order" | "flex-grow" | "flex-shrink" | "flex-basis" | "list-style" | "list-style-type" | "list-style-position" | "list-style-image" | "background-color" | "background-image" | "background-position" | "background-size" | "background-repeat" | "background-origin" | "background-clip" | "background-attachment" | "background-blend-mode" | "mask-image" | "mask-size" | "mask-position" | "mask-repeat" | "mask-composite" | "-webkit-mask-image" | "border-top-style" | "border-top-width" | "border-top-color" | "border-bottom-style" | "border-bottom-width" | "border-bottom-color" | "border-left-style" | "border-left-width" | "border-left-color" | "border-right-style" | "border-right-width" | "border-right-color" | "border-top-left-radius" | "border-top-right-radius" | "border-bottom-left-radius" | "border-bottom-right-radius" | "border-collapse" | "border-spacing" | "table-layout" | "opacity" | "visibility" | "transition" | "transition-property" | "transition-duration" | "transition-timing-function" | "transition-delay" | "transition-behavior" | "animation" | "animation-name" | "animation-duration" | "animation-timing-function" | "animation-delay" | "animation-iteration-count" | "animation-direction" | "animation-fill-mode" | "animation-play-state" | "animation-timeline" | "animation-range" | "box-shadow" | "backdrop-filter" | "mix-blend-mode" | "isolation" | "clip-path" | "transform" | "transform-origin" | "translate" | "rotate" | "scale" | "transform-style" | "perspective" | "perspective-origin" | "backface-visibility" | "will-change" | "outline-width" | "outline-style" | "outline-color" | "outline-offset" | "pointer-events" | "user-select" | "touch-action" | "resize" | "appearance" | "scroll-behavior" | "overscroll-behavior" | "scroll-snap-type" | "scroll-snap-align" | "scroll-snap-stop" | "scroll-padding-top" | "scroll-margin-top" | "accent-color" | "caret-color" | "color-scheme" | "content" | "position" | "float" | "clear" | "z-index" | "top" | "bottom" | "left" | "right" | "box-sizing" | "aspect-ratio" | "width" | "height" | "min-width" | "min-height" | "max-width" | "max-height" | "overflow" | "overflow-x" | "overflow-y" | "scrollbar-width" | "scrollbar-color" | "container-type" | "container-name" | "object-fit" | "object-position" | "column-width" | "column-count" | "margin-top" | "margin-bottom" | "margin-left" | "margin-right" | "padding-top" | "padding-bottom" | "padding-left" | "padding-right" | "font-family" | "font-weight" | "font-size" | "line-height" | "text-align" | "font-style" | "font-variant" | "font-variant-numeric" | "font-feature-settings" | "text-decoration" | "text-decoration-line" | "text-decoration-color" | "text-decoration-style" | "text-decoration-thickness" | "text-underline-offset" | "letter-spacing" | "word-spacing" | "text-indent" | "text-transform" | "direction" | "white-space" | "text-wrap" | "word-break" | "overflow-wrap" | "hyphens" | "vertical-align" | "text-overflow" | "text-shadow" | "line-clamp" | "-webkit-line-clamp" | "-webkit-box-orient" | "-webkit-text-fill-color" | "-webkit-background-clip" | "box-decoration-break" | "-webkit-box-decoration-break" | "stroke" | "stroke-width";
204
+ path: "color" | "cursor" | "filter" | "fill" | "display" | "flex-wrap" | "flex-direction" | "align-items" | "justify-items" | "justify-content" | "align-content" | "row-gap" | "column-gap" | "grid-row-gap" | "grid-column-gap" | "grid-template-areas" | "grid-template-columns" | "grid-template-rows" | "grid-auto-flow" | "grid-auto-rows" | "grid-auto-columns" | "grid-row-start" | "grid-column-start" | "grid-row-end" | "grid-column-end" | "align-self" | "justify-self" | "order" | "flex-grow" | "flex-shrink" | "flex-basis" | "list-style" | "list-style-type" | "list-style-position" | "list-style-image" | "background-color" | "background-image" | "background-position" | "background-size" | "background-repeat" | "background-origin" | "background-clip" | "background-attachment" | "background-blend-mode" | "mask-image" | "mask-size" | "mask-position" | "mask-repeat" | "mask-composite" | "-webkit-mask-image" | "border-top-style" | "border-top-width" | "border-top-color" | "border-bottom-style" | "border-bottom-width" | "border-bottom-color" | "border-left-style" | "border-left-width" | "border-left-color" | "border-right-style" | "border-right-width" | "border-right-color" | "border-top-left-radius" | "border-top-right-radius" | "border-bottom-left-radius" | "border-bottom-right-radius" | "border-collapse" | "border-spacing" | "table-layout" | "opacity" | "visibility" | "transition" | "transition-property" | "transition-duration" | "transition-timing-function" | "transition-delay" | "transition-behavior" | "animation" | "animation-name" | "animation-duration" | "animation-timing-function" | "animation-delay" | "animation-iteration-count" | "animation-direction" | "animation-fill-mode" | "animation-play-state" | "animation-timeline" | "animation-range" | "box-shadow" | "backdrop-filter" | "mix-blend-mode" | "isolation" | "clip-path" | "transform" | "transform-origin" | "translate" | "rotate" | "scale" | "transform-style" | "perspective" | "perspective-origin" | "backface-visibility" | "will-change" | "outline-width" | "outline-style" | "outline-color" | "outline-offset" | "pointer-events" | "user-select" | "touch-action" | "resize" | "appearance" | "scroll-behavior" | "overscroll-behavior" | "scroll-snap-type" | "scroll-snap-align" | "scroll-snap-stop" | "scroll-padding-top" | "scroll-margin-top" | "accent-color" | "caret-color" | "color-scheme" | "content" | "position" | "float" | "clear" | "z-index" | "top" | "bottom" | "left" | "right" | "box-sizing" | "aspect-ratio" | "width" | "height" | "min-width" | "min-height" | "max-width" | "max-height" | "overflow" | "overflow-x" | "overflow-y" | "scrollbar-width" | "scrollbar-color" | "container-type" | "container-name" | "object-fit" | "object-position" | "column-width" | "column-count" | "margin-top" | "margin-bottom" | "margin-left" | "margin-right" | "padding-top" | "padding-bottom" | "padding-left" | "padding-right" | "font-family" | "font-weight" | "font-size" | "line-height" | "text-align" | "font-style" | "font-variant" | "font-variant-numeric" | "font-feature-settings" | "text-decoration" | "text-decoration-line" | "text-decoration-color" | "text-decoration-style" | "text-decoration-thickness" | "text-underline-offset" | "letter-spacing" | "word-spacing" | "text-indent" | "text-transform" | "direction" | "writing-mode" | "text-orientation" | "white-space" | "text-wrap" | "word-break" | "overflow-wrap" | "hyphens" | "vertical-align" | "text-overflow" | "text-shadow" | "line-clamp" | "-webkit-line-clamp" | "-webkit-box-orient" | "-webkit-text-fill-color" | "-webkit-background-clip" | "box-decoration-break" | "-webkit-box-decoration-break" | "stroke" | "stroke-width";
205
+ label: "color" | "cursor" | "filter" | "fill" | "display" | "flex-wrap" | "flex-direction" | "align-items" | "justify-items" | "justify-content" | "align-content" | "row-gap" | "column-gap" | "grid-row-gap" | "grid-column-gap" | "grid-template-areas" | "grid-template-columns" | "grid-template-rows" | "grid-auto-flow" | "grid-auto-rows" | "grid-auto-columns" | "grid-row-start" | "grid-column-start" | "grid-row-end" | "grid-column-end" | "align-self" | "justify-self" | "order" | "flex-grow" | "flex-shrink" | "flex-basis" | "list-style" | "list-style-type" | "list-style-position" | "list-style-image" | "background-color" | "background-image" | "background-position" | "background-size" | "background-repeat" | "background-origin" | "background-clip" | "background-attachment" | "background-blend-mode" | "mask-image" | "mask-size" | "mask-position" | "mask-repeat" | "mask-composite" | "-webkit-mask-image" | "border-top-style" | "border-top-width" | "border-top-color" | "border-bottom-style" | "border-bottom-width" | "border-bottom-color" | "border-left-style" | "border-left-width" | "border-left-color" | "border-right-style" | "border-right-width" | "border-right-color" | "border-top-left-radius" | "border-top-right-radius" | "border-bottom-left-radius" | "border-bottom-right-radius" | "border-collapse" | "border-spacing" | "table-layout" | "opacity" | "visibility" | "transition" | "transition-property" | "transition-duration" | "transition-timing-function" | "transition-delay" | "transition-behavior" | "animation" | "animation-name" | "animation-duration" | "animation-timing-function" | "animation-delay" | "animation-iteration-count" | "animation-direction" | "animation-fill-mode" | "animation-play-state" | "animation-timeline" | "animation-range" | "box-shadow" | "backdrop-filter" | "mix-blend-mode" | "isolation" | "clip-path" | "transform" | "transform-origin" | "translate" | "rotate" | "scale" | "transform-style" | "perspective" | "perspective-origin" | "backface-visibility" | "will-change" | "outline-width" | "outline-style" | "outline-color" | "outline-offset" | "pointer-events" | "user-select" | "touch-action" | "resize" | "appearance" | "scroll-behavior" | "overscroll-behavior" | "scroll-snap-type" | "scroll-snap-align" | "scroll-snap-stop" | "scroll-padding-top" | "scroll-margin-top" | "accent-color" | "caret-color" | "color-scheme" | "content" | "position" | "float" | "clear" | "z-index" | "top" | "bottom" | "left" | "right" | "box-sizing" | "aspect-ratio" | "width" | "height" | "min-width" | "min-height" | "max-width" | "max-height" | "overflow" | "overflow-x" | "overflow-y" | "scrollbar-width" | "scrollbar-color" | "container-type" | "container-name" | "object-fit" | "object-position" | "column-width" | "column-count" | "margin-top" | "margin-bottom" | "margin-left" | "margin-right" | "padding-top" | "padding-bottom" | "padding-left" | "padding-right" | "font-family" | "font-weight" | "font-size" | "line-height" | "text-align" | "font-style" | "font-variant" | "font-variant-numeric" | "font-feature-settings" | "text-decoration" | "text-decoration-line" | "text-decoration-color" | "text-decoration-style" | "text-decoration-thickness" | "text-underline-offset" | "letter-spacing" | "word-spacing" | "text-indent" | "text-transform" | "direction" | "writing-mode" | "text-orientation" | "white-space" | "text-wrap" | "word-break" | "overflow-wrap" | "hyphens" | "vertical-align" | "text-overflow" | "text-shadow" | "line-clamp" | "-webkit-line-clamp" | "-webkit-box-orient" | "-webkit-text-fill-color" | "-webkit-background-clip" | "box-decoration-break" | "-webkit-box-decoration-break" | "stroke" | "stroke-width";
204
206
  }[];
205
207
  export declare const baseDefaultValue: Record<StyleCategory, StyleValue>;
206
208
  export default styleConstants;
@@ -191,6 +191,8 @@ var e = {
191
191
  TEXT_INDENT: "text-indent",
192
192
  TEXT_TRANSFORM: "text-transform",
193
193
  DIRECTION: "direction",
194
+ WRITING_MODE: "writing-mode",
195
+ TEXT_ORIENTATION: "text-orientation",
194
196
  WHITE_SPACE: "white-space",
195
197
  TEXT_WRAP: "text-wrap",
196
198
  WORD_BREAK: "word-break",
@@ -413,6 +415,8 @@ var e = {
413
415
  [t.TEXT_INDENT]: "0px",
414
416
  [t.TEXT_TRANSFORM]: "none",
415
417
  [t.DIRECTION]: "ltr",
418
+ [t.WRITING_MODE]: "horizontal-tb",
419
+ [t.TEXT_ORIENTATION]: "mixed",
416
420
  [t.WHITE_SPACE]: "normal",
417
421
  [t.TEXT_WRAP]: "wrap",
418
422
  [t.WORD_BREAK]: "normal",
@@ -772,8 +772,9 @@ export type SSRCompressionConfig = {
772
772
  /** Responses smaller than this many bytes go out uncompressed. Default 1024. */
773
773
  threshold?: number;
774
774
  /**
775
- * Brotli quality, 0–11, for a body compressed on every request — a page rendered for this request alone. Default 2:
776
- * measured on a quarter core, going to 4 cost a tenth of the pages a second to save half a kilobyte on each.
775
+ * Brotli quality, 0–11, for a body compressed on every request — a page rendered for this request alone. Default 5:
776
+ * the first quality under gzip's size for the CPU gzip spends — 2 came out larger than gzip on a 220 KB page (36 KB
777
+ * against 31; 5: 29). On half a core it costs ~0.1 ms a page, 5–8% of the pages a second of a 6 KB page.
777
778
  */
778
779
  brotliQuality?: number;
779
780
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plitzi/sdk-shared",
3
- "version": "0.38.7",
3
+ "version": "0.38.9",
4
4
  "license": "AGPL-3.0",
5
5
  "files": [
6
6
  "dist",
@@ -318,6 +318,10 @@
318
318
  "types": "./dist/helpers/index.d.ts",
319
319
  "import": "./dist/helpers/index.mjs"
320
320
  },
321
+ "./helpers/canonicalJson": {
322
+ "types": "./dist/helpers/canonicalJson.d.ts",
323
+ "import": "./dist/helpers/canonicalJson.mjs"
324
+ },
321
325
  "./helpers/cookies": {
322
326
  "types": "./dist/helpers/cookies.d.ts",
323
327
  "import": "./dist/helpers/cookies.mjs"