@plitzi/sdk-server 0.38.6 → 0.38.7

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.
Files changed (79) hide show
  1. package/CHANGELOG.md +201 -0
  2. package/README.md +100 -14
  3. package/dist/adapters/jsonAdapters.d.ts +8 -4
  4. package/dist/adapters/jsonAdapters.js +5 -9
  5. package/dist/core/auth/api.d.ts +4 -1
  6. package/dist/core/auth/api.js +14 -5
  7. package/dist/core/auth/createAuth.d.ts +7 -1
  8. package/dist/core/auth/createAuth.js +14 -0
  9. package/dist/core/auth/credentials.d.ts +2 -0
  10. package/dist/core/auth/credentials.js +4 -2
  11. package/dist/core/auth/session.d.ts +2 -0
  12. package/dist/core/auth/session.js +6 -5
  13. package/dist/core/createServer.js +25 -4
  14. package/dist/core/http/stages/devReload.d.ts +2 -2
  15. package/dist/core/http/stages/devReload.js +2 -2
  16. package/dist/env.d.ts +1 -0
  17. package/dist/env.js +19 -0
  18. package/dist/env.test.d.ts +1 -0
  19. package/dist/functions-api.d.ts +5 -0
  20. package/dist/helpers/buildServerInfo.js +6 -0
  21. package/dist/index.d.ts +1 -1
  22. package/dist/modules/actions/runtime/projectData.d.ts +18 -0
  23. package/dist/modules/actions/runtime/projectData.js +69 -0
  24. package/dist/modules/actions/runtime/taskContext.d.ts +1 -1
  25. package/dist/modules/actions/runtime/taskContext.js +6 -3
  26. package/dist/modules/actions/types.d.ts +7 -1
  27. package/dist/modules/functions/build.js +10 -0
  28. package/dist/modules/functions/capabilities.js +5 -0
  29. package/dist/modules/functions/context.js +10 -2
  30. package/dist/modules/functions/contract.d.ts +5 -0
  31. package/dist/modules/functions/driver.js +4 -0
  32. package/dist/modules/functions/local.d.ts +3 -1
  33. package/dist/modules/functions/local.js +4 -2
  34. package/dist/modules/functions/protocol.d.ts +3 -0
  35. package/dist/modules/functions/scope.d.ts +1 -1
  36. package/dist/modules/images/imageProxy.js +1 -1
  37. package/dist/modules/mysql/accounts.js +1 -5
  38. package/dist/modules/project/plugins.d.ts +31 -0
  39. package/dist/modules/project/plugins.js +162 -0
  40. package/dist/modules/project/serveProject.d.ts +78 -0
  41. package/dist/modules/project/serveProject.js +200 -0
  42. package/dist/modules/project/serveProject.test.d.ts +1 -0
  43. package/dist/modules/project/watchSpace.d.ts +14 -0
  44. package/dist/modules/project/watchSpace.js +77 -0
  45. package/dist/modules/realtime/failure.d.ts +6 -0
  46. package/dist/modules/realtime/failure.js +11 -0
  47. package/dist/modules/realtime/handlers.js +13 -3
  48. package/dist/modules/realtime/hub.js +4 -1
  49. package/dist/modules/realtime/socket.js +3 -2
  50. package/dist/modules/rsc/connectorRscData.d.ts +7 -3
  51. package/dist/modules/rsc/connectorRscData.js +3 -4
  52. package/dist/modules/rsc/handler.d.ts +1 -0
  53. package/dist/modules/rsc/handler.js +11 -1
  54. package/dist/modules/rsc/publicFileResolver.d.ts +4 -8
  55. package/dist/modules/rsc/publicFileResolver.js +6 -10
  56. package/dist/modules/runtime/bundle.js +1 -1
  57. package/dist/modules/ssr/prepareRender.js +10 -2
  58. package/dist/modules/ssr/registerExternalPlugins.js +1 -1
  59. package/dist/modules/ssr/vendor.test.d.ts +1 -0
  60. package/dist/modules/ssr/views/template.ejs +7 -3
  61. package/dist/plugins/compile.js +1 -1
  62. package/dist/plugins/copy.js +1 -1
  63. package/dist/plugins/manager.js +1 -1
  64. package/dist/project.d.ts +9 -0
  65. package/dist/project.js +2 -0
  66. package/package.json +16 -4
  67. package/skills/plitzi-authoring/CHEATSHEET.md +2 -2
  68. package/skills/plitzi-authoring/SKILL.md +3 -2
  69. package/skills/plitzi-authoring/reference/accessibility.md +2 -2
  70. package/skills/plitzi-authoring/reference/auth.md +75 -0
  71. package/skills/plitzi-authoring/reference/authoring-errors.md +18 -4
  72. package/skills/plitzi-authoring/reference/colours-and-motion.md +3 -2
  73. package/skills/plitzi-authoring/reference/efficiency.md +5 -2
  74. package/skills/plitzi-authoring/reference/elements-and-styles.md +23 -23
  75. package/skills/plitzi-authoring/reference/flows.md +11 -6
  76. package/skills/plitzi-authoring/reference/layouts.md +15 -18
  77. package/skills/plitzi-authoring/reference/plugins.md +1 -1
  78. package/skills/plitzi-authoring/reference/review-checklist.md +2 -1
  79. package/skills/plitzi-authoring/reference/structure.md +3 -2
@@ -18,7 +18,7 @@ container([hero, grid]) // an array is the children
18
18
  | `id` | the ONE name this element answers to — its key in the document, a binding's source, a step's target. Unique across the WHOLE space |
19
19
  | `class` | a shared class: a `styles()` declaration or a name from the space's `classes`; a list for several. Exclusive with `css` |
20
20
  | `css` | rules of this element's own: one set, or one per breakpoint (`{ desktop, tablet, mobile }`) |
21
- | `states` | `hover`, `focus-visible`, `current` (a link to the page shown), `hidden`… beside the element's own `css` |
21
+ | `states` | `hover`, `focus-visible`, `current` (the chosen link, toggle, tab), `expanded`, `first`/`last`/`odd`/`even`, `hidden`… |
22
22
  | `variant` | a variant of the class it wears (when the class declares it) or of its type |
23
23
  | `slots` | a class for one of the element's OTHER selectors — a form control's `input` |
24
24
  | `bind` | where a value comes from |
@@ -73,13 +73,9 @@ indented with space below it: `{ margin: '0px', padding: '0px' }` on its class t
73
73
  children sit in the parent's layout and a `class` on it styles nothing (`STYLE_WITHOUT_TAG`). Put the layout on the
74
74
  parent, or give it `attributes: { subType: 'div' }`.
75
75
 
76
- A type this SDK does not ship — a plugin — is authored the same way: `defineElement<Props>({ type, … })`,
77
- `element<Props>('type', props)`, or `elementsFromManifest(manifest)`. See [plugins](plugins.md).
76
+ A plugin's type is authored the same way: `defineElement<Props>({ type, … })` — see [plugins](plugins.md).
78
77
 
79
- **What a type takes, answered by the package** instead of the 7,000-line `.d.ts`: every catalogue is exported, keyed by
80
- type — `elementCatalog` (what it is for), `elementDefaultAttributes`, `elementAttributeNames`, `elementTriggers`,
81
- `elementCallbacks`, `elementSlots`, `elementSourceTypes`, `elementLeafTypes`, plus `BUILTIN_TRANSFORMERS` — and
82
- `npx @plitzi/cli explain formControl` prints them for one type.
78
+ **What a type takes:** `npx @plitzi/cli explain formControl`, or the exported catalogues ([validation.md](validation.md)).
83
79
 
84
80
  ## CSS
85
81
 
@@ -111,15 +107,20 @@ const card = styles('card', {
111
107
  container({ class: card, children: [ … ] });
112
108
  ```
113
109
 
114
- - A flat object is the base rules; `{ css, states, variants, ancestors }` when there is more. Each part may be written
110
+ - A flat object is the base rules; `{ css, states, variants, ancestors, pseudos, conditions }` when there is more. Each part may be written
115
111
  per breakpoint.
116
- - **Share a rule as a class, never as a spread.** Spreading a `const` into each element's `css` shares the source and
117
- duplicates the document: one selector per element, so restyling one card in the builder restyles one card.
112
+ - **Share a rule as a class, never as a spread** into each element's `css`: that is one selector per element, so
113
+ restyling one card in the builder restyles one card.
118
114
  - **Build a family from a plain object**, not by layering classes: `styles('button-primary', { ...buttonBase, … })`.
115
+ - Of two classes on an element, the one the stylesheet writes later wins (first met; a breakpoint's after the base),
116
+ not the list's last: write the longhands a class means (`class-overrides-class`).
119
117
  - One name declared twice with rules that disagree is refused. Declare a class once, in the module that owns it, and
120
118
  import it.
121
119
  - `ancestors: { [sidebar.name]: { variants: { collapsed: { display: 'none' } } } }` styles an element by the state or
122
- variant of an ancestor class — the way to react to a parent's hover or collapse without a flow.
120
+ variant of an ancestor class — the way to react to a parent's hover or collapse without a flow; `'>'` is the parent,
121
+ whatever it wears (a component's part: `{ '>': { states: { expanded: … } } }`).
122
+ - `pseudos: { after: { content: '"→"' } }` (`before`/`after` need a quoted `content`; also `selection`, `marker`,
123
+ `placeholder`, `first-letter`, `first-line`). `conditions: { 'motion-reduce': …, 'container (max-width: 30rem)': … }`.
123
124
  - **`hidden` is how an element looks while its `visible` says no** — where it goes as it hides, and where it comes
124
125
  from as it shows. Give the class a transition that includes `display` with `allow-discrete`, and a panel fades or
125
126
  slides instead of blinking; the transition on the base is the way in, the one in `hidden` the way out:
@@ -142,16 +143,15 @@ container({ class: card, children: [ … ] });
142
143
  What is inside follows its container with `ancestors: { [panel.name]: { states: { hidden: { … } } } }` — a
143
144
  different `transition-delay` per item staggers them. A container whose children mount only while it shows
144
145
  (`loadStrategy: 'visible'`) empties before it can leave: use `'lazy'` on one that animates out.
145
- - **The navigation of a site goes in a layout, once.** A link to the page being shown marks itself, and its class
146
- dresses it with `states: { current: { … } }` — never a class picked per page, which forces a copy of the header into
147
- every page.
148
- - **`:hover` is part of the selector, not a rule in `customCss`.** `customCss` is for what no class can say:
149
- keyframes, pseudo-elements, a rule across two unrelated elements, the inside of a third-party widget.
150
- - Per element TYPE defaults go in `elements: { heading: { base, states, variants, slots } }` — a whole type at once.
151
- - **Slots** are an element's other selectors: a `formControl`'s `input`, `label`, `error`; a `modalContainer`'s
152
- `backgroundContainer`, `rootContainer`, `headerContainer`, `headerTitle`, `headerCloseButton`, `bodyContainer`; a
153
- `dialogContainer`'s footer and buttons. `elementSlots` lists them all by type. Style one element's with `slots`, a
154
- whole type's with `elements.<type>.slots`.
146
+ - **The navigation goes in a layout, once:** a link to the page shown dresses itself with its class's
147
+ `states: { current: { … } }` — never a class picked per page, which copies the header into every page.
148
+ - **`:hover` is part of the selector, not a rule in `customCss`.** `customCss` is for what no class can say: a rule
149
+ across two unrelated elements, the inside of a third-party widget.
150
+ - Per element TYPE defaults go in `elements: { heading: { base, states, variants, pseudos, slots } }` — a whole type.
151
+ - **Slots** are an element's parts (not `customCss`): a `formControl`'s `input` (the box), `field` (its `<input>`),
152
+ `label`, `requiredMark`, `error`; a `markdown`'s or `richText`'s `heading`, `heading2`, `link`, `codeBlockFrame`…;
153
+ a `pagination`'s `page` (the one shown: its `current` state). `plitzi explain <type>` lists them. Style one
154
+ element's with `slots`, a type's with `elements.<type>.slots`.
155
155
  - A `modalContainer` / `dialogContainer` is fixed to the viewport, as tall as its content up to the screen, and 500px
156
156
  wide up to the screen; theme its look through its slots.
157
157
 
@@ -159,7 +159,7 @@ container({ class: card, children: [ … ] });
159
159
  already speak in variant names — `variantFrom(pill, 'runs.item.status', { template: "{{ source == 'completed' ?
160
160
  'ok' : 'failed' }}" })`. The binding's key names the CLASS; never write it by hand. A variant while a condition holds
161
161
  — the dot of the slide on screen — is `activeWhen(dot, '{{ list_dots.index == state.slide }}')` (`active`, else
162
- `idle`); for "the current page", `activeOn` in [layouts.md](layouts.md).
162
+ `idle`); for "the current page", `current` in [layouts.md](layouts.md).
163
163
 
164
164
  ## From Tailwind classes
165
165
 
@@ -169,4 +169,4 @@ A design written in Tailwind classes becomes classes the builder edits with `sty
169
169
  ## Colours, themes and motion
170
170
 
171
171
  Every colour is a token with a light and a dark value, read as `var(--name)` (or `tokens(variables)`); keyframes go in
172
- `customCss` — see [colours-and-motion.md](colours-and-motion.md).
172
+ the space's `keyframes` — see [colours-and-motion.md](colours-and-motion.md).
@@ -48,7 +48,7 @@ Name the trigger (`named('changed', on('onChange'))`) and read its payload as `{
48
48
 
49
49
  | Trigger | Payload |
50
50
  | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------- |
51
- | `onChange` (formControl) | `value` (a boolean for a checkbox), `name` |
51
+ | `onChange` (formControl) | `value` (a boolean for a checkbox or switch), `name` |
52
52
  | `onSubmit` (form) | `values` (by control `name`), `actionUrl`, `method` |
53
53
  | `onPageLoad` (page) | `pageId`, `routeParams`, `queryParams` |
54
54
  | `onApiSuccess` / `onApiError` (apiContainer) | `url`, `method`, `status`, `data` |
@@ -61,6 +61,10 @@ Name the trigger (`named('changed', on('onChange'))`) and read its payload as `{
61
61
  `named('quote', step)` is how a later step reads an earlier one: `{{ quote.output.total }}`. The trigger too:
62
62
  `named('submitted', onSubmit())` → `{{ submitted.values.email }}`. Unnamed steps get ids nothing can refer to.
63
63
 
64
+ `whenSucceeded` / `whenFailed` read a server action's `status` (`runServerAction` only); any other step is asked what
65
+ it publishes (`npx @plitzi/cli explain login` lists it) — after `named('signedIn', authLogin(…))`,
66
+ `when({ field: 'signedIn.ok', operator: '=', value: true }, …)`.
67
+
64
68
  A step's params are templates, evaluated in full (conditions, loops, filters). A source in them is named in full:
65
69
  a row's button posts `{ jobId: '{{ list_jobRows.item.id }}' }` — the row that was clicked; the short name is refused.
66
70
 
@@ -75,7 +79,7 @@ so `when({ field: 'sent.values.code', operator: '=', value: '' }, …)` never ho
75
79
  ## Writes and what they refresh
76
80
 
77
81
  `webHook` (not GET) refreshes every request to its own site when it succeeds; `runServerAction` refreshes
78
- everything. Say what it should refresh — `invalidateQueries: 'elements', invalidateElements: ['orders']` — or
82
+ everything, server-driven providers included. Say what it should refresh — `invalidateQueries: 'elements', invalidateElements: ['orders']` — or
79
83
  `'none'` for a step that only reads, or when a refresh would make the page act on the new answer mid-flow.
80
84
 
81
85
  ## Triggers belong to the element that fires them
@@ -134,10 +138,11 @@ without a word.
134
138
 
135
139
  `addNotification({ content: 'Saved', appearance: 'success' })` — `appearance` is `success`, `danger`, `warning` or
136
140
  `info`. They follow the page's theme; how they look is the space's `notifications` — colours (`background`, `text`,
137
- `success`, `danger`, `warning`, `info`) and shape (`radius`, `font`, `fontSize`, `border`, `shadow`, `padding`):
138
- `notifications: { background: 'var(--card)', text: 'var(--foreground)', success: 'var(--accent)', radius: '12px',
139
- font: 'var(--font-sans)', border: '1px solid var(--border)', shadow: 'var(--shadow-lg)' }`. The toast carries no class
140
- of the space's, so this — not a `.Toastify__toast` rule in `customCss` — is where it is dressed.
141
+ `success`, `danger`, `warning`, `info`), shape (`radius`, `font`, `fontSize`, `fontWeight`, `lineHeight`, `minHeight`,
142
+ `border`, `shadow`, `padding`) and the parts inside (`iconSize`, `iconGap`, `closeColor`, `closeOpacity`,
143
+ `progressHeight`), one CSS value each: `notifications: { background: 'var(--card)', radius: '12px', iconSize: '18px' }`.
144
+ The toast carries no class of the space's, so this — not a `.Toastify__*` rule in `customCss` — is where it is
145
+ dressed.
141
146
 
142
147
  ## Modals, dropdowns, tabs
143
148
 
@@ -74,7 +74,6 @@ const navGroup = (group: DocsGroup): ElementSpec =>
74
74
  id: `docs-nav-${entry.id}`,
75
75
  href: entry.id,
76
76
  class: navLink,
77
- bind: [activeOn(navLink, entry.id)],
78
77
  children: [text(entry.title, { id: `docs-nav-label-${entry.id}` })]
79
78
  })
80
79
  )
@@ -83,33 +82,31 @@ const navGroup = (group: DocsGroup): ElementSpec =>
83
82
 
84
83
  Adding a page is one entry in the list: the menu, the index and the neighbours' "next" follow.
85
84
 
86
- ## The current entry: `activeOn`
85
+ ## The current entry
87
86
 
88
- A menu in a layout is the same nodes on every page, so it cannot have the right entry styled by hand. It asks which
89
- page is showing:
87
+ A menu in a layout is the same nodes on every page, so it cannot have the right entry styled by hand — and does not
88
+ need to. A link to the page being shown marks itself: `aria-current="page"`, which a screen reader announces, and its
89
+ class's `current` state dresses it:
90
90
 
91
91
  ```ts
92
- const navLink = styles('nav-link', {
93
- css: { color: 'var(--muted)' },
94
- variants: { active: { color: 'var(--foreground)', 'font-weight': '600' } }
95
- });
92
+ const navLink = styles('nav-link', { css: { color: 'var(--muted)' }, states: { current: { color: 'var(--foreground)' } } });
96
93
 
97
- link({ href: 'spaces', class: navLink, bind: [activeOn(navLink, ['spaces', 'space-record'])] });
94
+ link({ href: '/pricing', mode: 'internal', class: navLink });
98
95
  ```
99
96
 
100
- A link to ONE page needs nothing of this: it marks itself — `aria-current="page"`, which a screen reader announces — and
101
- its class dresses it with the `current` state:
97
+ An entry lit for a SECTION — a journal and its articles, `/automations/runs` while on `/automations/runs/42` — says
98
+ `current: 'section'`: current on its page and on every page under its path, at a `/` (`/runs` holds `/runs/42`, not
99
+ `/runsx`), announced there as the current entry (`aria-current="true"`); the same `current` state dresses it. A link to
100
+ `/` holds every page, so authoring warns (`link-current-section`):
102
101
 
103
102
  ```ts
104
- const navLink = styles('nav-link', { css: { color: 'var(--muted)' }, states: { current: { color: 'var(--foreground)' } } });
105
-
106
- link({ href: '/pricing', mode: 'internal', class: navLink });
103
+ link({ href: 'runs', class: navLink, current: 'section' });
107
104
  ```
108
105
 
109
- `activeOn(class, pageIds, { variant?, slot? })` is for an entry lit on SEVERAL pages — a section and the pages under it,
110
- a journal and its articles: it binds the class's variant to `navigation.currentPageId`, the `active` variant on those
111
- pages and `idle` everywhere else. Do not write the binding by hand with a `when` rule per page; that is the long form
112
- this replaces.
106
+ `activeOn(class, pageIds, { variant?, slot? })` is for an entry lit on pages NOT under its path — `['spaces',
107
+ 'space-record']` when a record lives at `/s/:id`: it binds the class's variant to `navigation.currentPageId`, the
108
+ `active` variant on those pages and `idle` everywhere else. Do not write the binding by hand with a `when` rule per
109
+ page; that is the long form this replaces.
113
110
 
114
111
  ## Pages from a factory
115
112
 
@@ -96,7 +96,7 @@ const route = usePluginRoute('board'); // from '@plitzi/plitzi-sdk'
96
96
  const url = route('/layout'); // undefined where no server runs code (the canvas)
97
97
  ```
98
98
 
99
- Its `ctx.kv` is the plugin's own; the space's credentials and channels are not.
99
+ Its `ctx.kv` is the plugin's own; the space's credentials, channels and data are not.
100
100
 
101
101
  ## Talking to other pages
102
102
 
@@ -50,7 +50,8 @@ Screen readers and browser agents (Claude in Chrome) work a page through its acc
50
50
 
51
51
  ## No copies
52
52
 
53
- - [ ] Chrome shared by pages is a layout; the current menu entry comes from `activeOn`, not per-page styling.
53
+ - [ ] Chrome shared by pages is a layout; the current menu entry marks itself (`current` state, `current: 'section'`),
54
+ not per-page styling.
54
55
  - [ ] A look used twice is a class; a tree used twice is a function or a `map` over data; a list of pages, links or
55
56
  plans is ONE array everything reads.
56
57
  - [ ] No positional ids (`container-45`) in what you wrote; every referenced element is named, and a helper that
@@ -108,7 +108,7 @@ the record is the answer instead: one page, as many addresses as records.
108
108
  per page. It renders; it is not maintainable. When you work on such a file, leave it better:
109
109
 
110
110
  - repeated `css` blocks → one class;
111
- - a nav with a hand-styled current entry per page → a layout and `activeOn`;
111
+ - a nav with a hand-styled current entry per page → a layout whose links mark themselves (`current: 'section'`);
112
112
  - the same tree pasted per item → a function and a `map`;
113
113
  - `container-45` → a name.
114
114
 
@@ -120,4 +120,5 @@ the rest.
120
120
  `npm run lint:space` (`plitzi lint`) reads the source for what this page asks — a file past 400 lines, pages in one
121
121
  file, rows of data inline, a colour that is not a token, CSS copied three times, a row singled out by its id inside a
122
122
  `map`, a minted id, a file nothing imports — at file:line, beside authoring's suggestions. Leave it clean; a departure
123
- on purpose says why where it is: `// plitzi-lint-disable-next-line <code> -- why`.
123
+ on purpose says why where it is: `// plitzi-lint-disable-next-line <code> -- why` for lint's own codes. A suggestion of
124
+ authoring's is quieted on its element instead — `quiet: ['repeated-shape']` — where the builder and the MCP read it.