@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.
- package/CHANGELOG.md +201 -0
- package/README.md +100 -14
- package/dist/adapters/jsonAdapters.d.ts +8 -4
- package/dist/adapters/jsonAdapters.js +5 -9
- package/dist/core/auth/api.d.ts +4 -1
- package/dist/core/auth/api.js +14 -5
- package/dist/core/auth/createAuth.d.ts +7 -1
- package/dist/core/auth/createAuth.js +14 -0
- package/dist/core/auth/credentials.d.ts +2 -0
- package/dist/core/auth/credentials.js +4 -2
- package/dist/core/auth/session.d.ts +2 -0
- package/dist/core/auth/session.js +6 -5
- package/dist/core/createServer.js +25 -4
- package/dist/core/http/stages/devReload.d.ts +2 -2
- package/dist/core/http/stages/devReload.js +2 -2
- package/dist/env.d.ts +1 -0
- package/dist/env.js +19 -0
- package/dist/env.test.d.ts +1 -0
- package/dist/functions-api.d.ts +5 -0
- package/dist/helpers/buildServerInfo.js +6 -0
- package/dist/index.d.ts +1 -1
- package/dist/modules/actions/runtime/projectData.d.ts +18 -0
- package/dist/modules/actions/runtime/projectData.js +69 -0
- package/dist/modules/actions/runtime/taskContext.d.ts +1 -1
- package/dist/modules/actions/runtime/taskContext.js +6 -3
- package/dist/modules/actions/types.d.ts +7 -1
- package/dist/modules/functions/build.js +10 -0
- package/dist/modules/functions/capabilities.js +5 -0
- package/dist/modules/functions/context.js +10 -2
- package/dist/modules/functions/contract.d.ts +5 -0
- package/dist/modules/functions/driver.js +4 -0
- package/dist/modules/functions/local.d.ts +3 -1
- package/dist/modules/functions/local.js +4 -2
- package/dist/modules/functions/protocol.d.ts +3 -0
- package/dist/modules/functions/scope.d.ts +1 -1
- package/dist/modules/images/imageProxy.js +1 -1
- package/dist/modules/mysql/accounts.js +1 -5
- package/dist/modules/project/plugins.d.ts +31 -0
- package/dist/modules/project/plugins.js +162 -0
- package/dist/modules/project/serveProject.d.ts +78 -0
- package/dist/modules/project/serveProject.js +200 -0
- package/dist/modules/project/serveProject.test.d.ts +1 -0
- package/dist/modules/project/watchSpace.d.ts +14 -0
- package/dist/modules/project/watchSpace.js +77 -0
- package/dist/modules/realtime/failure.d.ts +6 -0
- package/dist/modules/realtime/failure.js +11 -0
- package/dist/modules/realtime/handlers.js +13 -3
- package/dist/modules/realtime/hub.js +4 -1
- package/dist/modules/realtime/socket.js +3 -2
- package/dist/modules/rsc/connectorRscData.d.ts +7 -3
- package/dist/modules/rsc/connectorRscData.js +3 -4
- package/dist/modules/rsc/handler.d.ts +1 -0
- package/dist/modules/rsc/handler.js +11 -1
- package/dist/modules/rsc/publicFileResolver.d.ts +4 -8
- package/dist/modules/rsc/publicFileResolver.js +6 -10
- package/dist/modules/runtime/bundle.js +1 -1
- package/dist/modules/ssr/prepareRender.js +10 -2
- package/dist/modules/ssr/registerExternalPlugins.js +1 -1
- package/dist/modules/ssr/vendor.test.d.ts +1 -0
- package/dist/modules/ssr/views/template.ejs +7 -3
- package/dist/plugins/compile.js +1 -1
- package/dist/plugins/copy.js +1 -1
- package/dist/plugins/manager.js +1 -1
- package/dist/project.d.ts +9 -0
- package/dist/project.js +2 -0
- package/package.json +16 -4
- package/skills/plitzi-authoring/CHEATSHEET.md +2 -2
- package/skills/plitzi-authoring/SKILL.md +3 -2
- package/skills/plitzi-authoring/reference/accessibility.md +2 -2
- package/skills/plitzi-authoring/reference/auth.md +75 -0
- package/skills/plitzi-authoring/reference/authoring-errors.md +18 -4
- package/skills/plitzi-authoring/reference/colours-and-motion.md +3 -2
- package/skills/plitzi-authoring/reference/efficiency.md +5 -2
- package/skills/plitzi-authoring/reference/elements-and-styles.md +23 -23
- package/skills/plitzi-authoring/reference/flows.md +11 -6
- package/skills/plitzi-authoring/reference/layouts.md +15 -18
- package/skills/plitzi-authoring/reference/plugins.md +1 -1
- package/skills/plitzi-authoring/reference/review-checklist.md +2 -1
- 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` (
|
|
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
|
|
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
|
|
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
|
|
117
|
-
|
|
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
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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", `
|
|
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
|
-
`
|
|
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`)
|
|
138
|
-
`
|
|
139
|
-
|
|
140
|
-
of the space's, so this — not a `.
|
|
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
|
|
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
|
|
89
|
-
page
|
|
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: '
|
|
94
|
+
link({ href: '/pricing', mode: 'internal', class: navLink });
|
|
98
95
|
```
|
|
99
96
|
|
|
100
|
-
|
|
101
|
-
its
|
|
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
|
-
|
|
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
|
|
110
|
-
a
|
|
111
|
-
pages and `idle` everywhere else. Do not write the binding by hand with a `when` rule per
|
|
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
|
|
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
|
|
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
|
|
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.
|