@plitzi/sdk-server 0.37.9 → 0.37.10
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 +589 -0
- package/README.md +17 -0
- package/dist/adapters/cloudAdapters.d.ts +5 -3
- package/dist/adapters/cloudAdapters.js +120 -34
- package/dist/adapters/space/types.d.ts +3 -0
- package/dist/core/configSeam.d.ts +14 -0
- package/dist/core/configSeam.js +114 -0
- package/dist/core/configSeam.test.d.ts +1 -0
- package/dist/core/createServer.js +11 -5
- package/dist/core/createServer.publicData.test.d.ts +1 -0
- package/dist/core/freePort.d.ts +10 -0
- package/dist/core/freePort.js +36 -0
- package/dist/core/freePort.test.d.ts +1 -0
- package/dist/core/http/stages/images.d.ts +6 -0
- package/dist/core/http/stages/images.js +47 -0
- package/dist/core/http/stages/images.test.d.ts +1 -0
- package/dist/core/server/fleet/channel.js +1 -1
- package/dist/core/server/fleet/relay.js +1 -1
- package/dist/core/services/action.js +5 -4
- package/dist/core/services/registry.js +2 -0
- package/dist/helpers/cache/keys.d.ts +23 -10
- package/dist/helpers/cache/keys.js +16 -2
- package/dist/helpers/flagOverrides.d.ts +27 -0
- package/dist/helpers/flagOverrides.js +25 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -1
- package/dist/modules/actions/moduleFor.js +5 -3
- package/dist/modules/actions/runtime/flags.d.ts +16 -0
- package/dist/modules/actions/runtime/flags.js +49 -0
- package/dist/modules/actions/runtime/flags.test.d.ts +1 -0
- package/dist/modules/actions/runtime/kvList.js +1 -1
- package/dist/modules/actions/runtime/runAction.js +2 -0
- package/dist/modules/actions/transport/callHandler.js +8 -0
- package/dist/modules/actions/types.d.ts +14 -1
- package/dist/modules/connectors/engine.js +1 -1
- package/dist/modules/functions/capabilities.js +1 -1
- package/dist/modules/functions/load.js +2 -2
- package/dist/modules/functions/manifest.js +1 -1
- package/dist/modules/functions/runner/messages.js +1 -1
- package/dist/modules/images/allowlist.d.ts +5 -0
- package/dist/modules/images/allowlist.js +18 -0
- package/dist/modules/images/imageProxy.d.ts +36 -0
- package/dist/modules/images/imageProxy.js +273 -0
- package/dist/modules/images/imageProxy.test.d.ts +1 -0
- package/dist/modules/images/transform.d.ts +10 -0
- package/dist/modules/images/transform.js +28 -0
- package/dist/modules/images/transform.test.d.ts +1 -0
- package/dist/modules/rsc/connectorRscData.d.ts +12 -7
- package/dist/modules/rsc/connectorRscData.js +12 -8
- package/dist/modules/rsc/handler.js +18 -4
- package/dist/modules/rsc/publicFileResolver.d.ts +13 -0
- package/dist/modules/rsc/publicFileResolver.js +39 -0
- package/dist/modules/rsc/publicFileResolver.test.d.ts +1 -0
- package/dist/modules/rsc/resolveRscData.d.ts +4 -2
- package/dist/modules/rsc/resolveRscData.js +10 -2
- package/dist/modules/runtime/bundle.js +2 -2
- package/dist/modules/ssr/Component.d.ts +5 -1
- package/dist/modules/ssr/Component.js +3 -1
- package/dist/modules/ssr/prepareRender.js +36 -8
- package/package.json +9 -4
- package/skills/plitzi-authoring/CHEATSHEET.md +135 -0
- package/skills/plitzi-authoring/SKILL.md +71 -71
- package/skills/plitzi-authoring/recipes/carousel.ts +114 -0
- package/skills/plitzi-authoring/recipes/detail-page.ts +70 -0
- package/skills/plitzi-authoring/recipes/embed-and-svg.ts +48 -0
- package/skills/plitzi-authoring/recipes/every-few-seconds.ts +119 -0
- package/skills/plitzi-authoring/recipes/feature-flag.ts +24 -0
- package/skills/plitzi-authoring/recipes/filter-a-list.ts +63 -0
- package/skills/plitzi-authoring/recipes/forms-and-modals.ts +64 -0
- package/skills/plitzi-authoring/recipes/from-tailwind.ts +52 -0
- package/skills/plitzi-authoring/recipes/link-to-a-section.ts +35 -0
- package/skills/plitzi-authoring/recipes/link-with-data.ts +40 -0
- package/skills/plitzi-authoring/recipes/marquee.ts +57 -0
- package/skills/plitzi-authoring/recipes/plugin.ts +53 -0
- package/skills/plitzi-authoring/recipes/scroll-a-row.ts +79 -0
- package/skills/plitzi-authoring/recipes/show-data.ts +73 -0
- package/skills/plitzi-authoring/recipes/style-a-page.ts +45 -0
- package/skills/plitzi-authoring/recipes/typed-data.ts +64 -0
- package/skills/plitzi-authoring/recipes/usable-without-sight.ts +39 -0
- package/skills/plitzi-authoring/reference/accessibility.md +2 -2
- package/skills/plitzi-authoring/reference/authoring-errors.md +195 -53
- package/skills/plitzi-authoring/reference/colours-and-motion.md +63 -0
- package/skills/plitzi-authoring/reference/components.md +9 -1
- package/skills/plitzi-authoring/reference/data-and-visibility.md +29 -29
- package/skills/plitzi-authoring/reference/efficiency.md +53 -0
- package/skills/plitzi-authoring/reference/elements-and-styles.md +39 -24
- package/skills/plitzi-authoring/reference/feature-flags.md +78 -0
- package/skills/plitzi-authoring/reference/flows.md +25 -36
- package/skills/plitzi-authoring/reference/kept-state.md +28 -0
- package/skills/plitzi-authoring/reference/layouts.md +20 -3
- package/skills/plitzi-authoring/reference/lists.md +45 -16
- package/skills/plitzi-authoring/reference/plugins.md +15 -0
- package/skills/plitzi-authoring/reference/realtime.md +35 -0
- package/skills/plitzi-authoring/reference/review-checklist.md +8 -3
- package/skills/plitzi-authoring/reference/{templates-and-export.md → snippets-and-export.md} +9 -8
- package/skills/plitzi-authoring/reference/structure.md +29 -20
- package/skills/plitzi-authoring/reference/tailwind.md +19 -0
- package/skills/plitzi-authoring/reference/templates.md +6 -5
- package/skills/plitzi-authoring/reference/testing.md +11 -0
- package/skills/plitzi-authoring/reference/typed-sources.md +20 -0
- package/skills/plitzi-authoring/reference/validation.md +6 -2
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# Structuring a space that grows
|
|
2
2
|
|
|
3
|
-
A one-screen space fits in `src/space.ts`. Past a few pages, split it by what changes together —
|
|
4
|
-
repeated thing in exactly one place.
|
|
3
|
+
A one-screen space fits in `src/space.ts`. Past a few pages, split it by what changes together — a file per part, short
|
|
4
|
+
enough to read whole — and keep every repeated thing in exactly one place. `npx @plitzi/cli create shop --template
|
|
5
|
+
catalog` writes a complete small site in this shape (tokens, a layout, a card component, typed data, a page per file)
|
|
6
|
+
to read before writing one.
|
|
5
7
|
|
|
6
8
|
```text
|
|
7
9
|
src/space/
|
|
@@ -20,29 +22,36 @@ src/space/
|
|
|
20
22
|
|
|
21
23
|
## Components are functions
|
|
22
24
|
|
|
23
|
-
The same block in two places is a function with parameters.
|
|
24
|
-
|
|
25
|
+
The same block in two places is a function with parameters. Ids are one namespace for the whole space, so the
|
|
26
|
+
function builds inside `scope()`: every `id` given in it is prefixed by the scope's name, and `ref()` names one of them
|
|
27
|
+
in full for a binding, a step's target or a template:
|
|
25
28
|
|
|
26
29
|
```ts
|
|
27
30
|
export const panel = (params: { id: string; title: string; sub?: string; link?: { href: string; label: string }; children: ElementSpec[] }): ElementSpec =>
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
31
|
+
scope(params.id, () =>
|
|
32
|
+
container({
|
|
33
|
+
id: 'panel',
|
|
34
|
+
class: panelCard,
|
|
35
|
+
children: [
|
|
36
|
+
container({
|
|
37
|
+
id: 'head',
|
|
38
|
+
class: panelHead,
|
|
39
|
+
children: [
|
|
40
|
+
heading(params.title, { id: 'title', subType: 'h3', class: panelTitle }),
|
|
41
|
+
...(params.sub ? [text(params.sub, { id: 'sub', class: panelSub })] : []),
|
|
42
|
+
...(params.link ? [link({ id: 'link', href: params.link.href, class: panelLink, children: [text(params.link.label)] })] : [])
|
|
43
|
+
]
|
|
44
|
+
}),
|
|
45
|
+
...params.children
|
|
46
|
+
]
|
|
47
|
+
})
|
|
48
|
+
);
|
|
44
49
|
```
|
|
45
50
|
|
|
51
|
+
`panel({ id: 'hm-traffic', … })` writes `hm-traffic-panel`, `hm-traffic-title`… Whatever names one of them from inside
|
|
52
|
+
takes it from the function's argument — `scope(params.id, ref => …)`, then `scrollBy(ref('row'), …)` or
|
|
53
|
+
`` `{{ list_${ref('row')}.index }}` ``. Scopes nest; a list's `row` already reads its list by its full name.
|
|
54
|
+
|
|
46
55
|
A page then reads as what it shows: `panel({ id: 'hm-traffic', title: 'Traffic', link: { href: 'analytics', label:
|
|
47
56
|
'Open analytics →' }, children: [chart] })`.
|
|
48
57
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# From Tailwind classes
|
|
2
|
+
|
|
3
|
+
`tw()` turns Tailwind classes into the rules `styles()` takes, when the space is written — nothing of Tailwind runs in
|
|
4
|
+
the page, and the class is an ordinary one the builder edits:
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
const pill = styles('nav-pill', tw('inline-flex items-center gap-2 px-5 py-2 rounded-full bg-slate-950/90 hover:scale-105 md:text-sm'));
|
|
8
|
+
const t = createTw({ colors: tokens(variables) }); // bg-surface → var(--surface), text-ink/60 → a tint of it
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- Tailwind v4's scales and palette, its arbitrary values (`w-[220px]`, `shadow-[0_0_20px_rgb(6_182_212/0.35)]`), CSS
|
|
12
|
+
variables (`bg-(--accent)`) and any property (`[mask-type:luminance]`).
|
|
13
|
+
- Breakpoints become Plitzi's ranges: `md:` is tablet and desktop, `lg:` desktop, `max-md:` mobile, `max-lg:` tablet and
|
|
14
|
+
mobile. A property only a wider range sets is `revert` below it.
|
|
15
|
+
- `hover:`, `focus-visible:`, `active:`… are the class's states; `group-hover/card:` is how it looks inside an
|
|
16
|
+
ancestor wearing the class `card`.
|
|
17
|
+
- What has no exact equivalent is refused with what to write instead (`tw-no-equivalent`): `sm:`/`xl:`, `dark:` (a
|
|
18
|
+
token with both values), `space-x-*` (`gap`), `animate-*` (keyframes in `customCss`). An unknown class is
|
|
19
|
+
`tw-unknown-class` with the closest one; two classes that tie on a property are `tw-class-conflict`.
|
|
@@ -16,7 +16,7 @@ An attribute holding a condition — `href: "/x/{{ on ? 'a' : 'b' }}"` — is us
|
|
|
16
16
|
`template-never-resolved`). Move the expression into a binding with `bindTemplate`:
|
|
17
17
|
|
|
18
18
|
```ts
|
|
19
|
-
link({ href: '/',
|
|
19
|
+
link({ href: '/', bind: [bindTemplate('href', 'state.workspace.id', "{{ source ? '/workspace/' ~ source : '/' }}")] })
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
A value that came from a BINDING is data, never a template: a comment reading `{{ auth.accessToken }}` prints those
|
|
@@ -39,8 +39,8 @@ A text template feeding `items` is refused: the list would receive the array wri
|
|
|
39
39
|
## Naming sources inside a template
|
|
40
40
|
|
|
41
41
|
Always the full name — the element's kind, an underscore, its id: `apiContainer_stats`, `list_rows`,
|
|
42
|
-
`modalContainer_credits`. The globals (`state`, `auth`, `navigation`, `variables`, `host`, `theme`, `computed`) are
|
|
43
|
-
as themselves. In a flow, an earlier step is read by the name given with `named('quote', …)`: `{{ quote.output.total }}`.
|
|
42
|
+
`modalContainer_credits`. The globals (`state`, `auth`, `navigation`, `variables`, `host`, `theme`, `flags`, `computed`) are
|
|
43
|
+
named as themselves — `{{ flags.newCheckout }}` is `true` or `false`, see [feature flags](feature-flags.md). In a flow, an earlier step is read by the name given with `named('quote', …)`: `{{ quote.output.total }}`.
|
|
44
44
|
|
|
45
45
|
An id with a hyphen is one name inside a template — `{{ apiContainer_tn-data.data.total }}` reads that provider. So
|
|
46
46
|
subtraction is written with spaces: `{{ total - used }}`, never `total-used`.
|
|
@@ -69,7 +69,7 @@ outside the element that publishes it — each with the name it should have been
|
|
|
69
69
|
interpreter would read past, rather than letting a template render a value nobody wrote.
|
|
70
70
|
|
|
71
71
|
Filters: `default upper lower trim capitalize title camelize kebab snake ltrim rtrim pad padRight replace slice split
|
|
72
|
-
join reverse length first last contains startswith endswith number number_format round abs format sort batch chunk map
|
|
72
|
+
join reverse length first last contains startswith endswith number number_format currency percent round abs format sort batch chunk map
|
|
73
73
|
reduce merge keys values filter column find pluck unique flatten sum without only index_by group_by url_encode nl2br
|
|
74
74
|
striptags spaceless json_encode to_json object_as_json raw date base64_encode base64_decode md5 random`.
|
|
75
75
|
|
|
@@ -93,7 +93,8 @@ A lookup across two sources — a row joined to the stats around it:
|
|
|
93
93
|
step's params and an action's output, `"{{ text }}"` in quotes is safe too — a value inside a string literal of a
|
|
94
94
|
param written as a JSON document is escaped for it. Anywhere else (an attribute, a binding's template) it is not:
|
|
95
95
|
encode.
|
|
96
|
-
- **Numbers**: `number_format(decimals, point, thousands)`; `round(precision)
|
|
96
|
+
- **Numbers**: `number_format(decimals, point, thousands)`; `round(precision)`; `currency('USD', 'en', { trimZeros: true })`
|
|
97
|
+
(`$2,040.57`, `$440`); `percent(1)` (`0.256` → `25.6%`). Locale `en` unless named, so server and browser agree.
|
|
97
98
|
- **A step param that is one `{{ expression }}` is that value, with its type**: a number stays a number, and a
|
|
98
99
|
string stays a string however it looks — a password typed `1234` arrives as `"1234"`. Nothing is guessed from the
|
|
99
100
|
text: to convert, declare the type where the value lands (`setState`'s `type`, the action's input field). Text around
|
|
@@ -57,6 +57,13 @@ for it; over `max`, `problems` says so. It reads the SDK's render tracing, which
|
|
|
57
57
|
the space with it (the e2e harness: `renderSpace(page, space, { debugMode: true })`). What to do with the answer is in
|
|
58
58
|
[performance](performance.md).
|
|
59
59
|
|
|
60
|
+
## Asking the page what it holds
|
|
61
|
+
|
|
62
|
+
`plitzi check` lists every flow that failed while the page loaded; `--state` adds the state and every source by its
|
|
63
|
+
full name, `--element <id>` one element — what it reads, its own state, whether it is on screen. They come from the
|
|
64
|
+
page's dev tools (debug mode, any development server). In a test, `readDevTools(page, { state: true })` from
|
|
65
|
+
`@plitzi/sdk-authoring` answers the same.
|
|
66
|
+
|
|
60
67
|
## Spaces written for a test
|
|
61
68
|
|
|
62
69
|
Author them like any space — never as JSON: the validator is what tells a fixture that tests something from one that
|
|
@@ -112,6 +119,10 @@ expect(await page.evaluate(() => (window as unknown as { __painted: string[] }).
|
|
|
112
119
|
|
|
113
120
|
## Screenshots and scrolling
|
|
114
121
|
|
|
122
|
+
A browser holds back CSS animations and transitions in a tab that is not in view, and `onInterval` stops ticking there:
|
|
123
|
+
check motion with `getComputedStyle(element).animationName` (and `animationPlayState`), or two screenshots taken
|
|
124
|
+
apart, in a page that is in front — not a background tab.
|
|
125
|
+
|
|
115
126
|
The page scrolls the document in development exactly as in production while the dev-tools panel is folded away (the
|
|
116
127
|
badge). With the panel OPEN, the page shares the window with it and scrolls inside its own pane — close the panel, or
|
|
117
128
|
run with `debugMode: false`, before a `fullPage` screenshot or a scroll measurement. For a screenshot at a width and a
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Data typed by a sample
|
|
2
|
+
|
|
3
|
+
`source('site', home)` names a provider's source from a sample of its answer — the JSON file it reads, imported
|
|
4
|
+
(`import home from '../../public/data/home.json' with { type: 'json' }`). Every path is then completed by the editor
|
|
5
|
+
and checked: `site.data.hero.titel` is a type error, and refused `source-field-unknown` if it gets past the types.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
const site = source('site', home);
|
|
9
|
+
apiContainer({ id: site.id, query: '/data/home.json', children: [
|
|
10
|
+
heading({ from: site.data.hero.title }),
|
|
11
|
+
text({ content: twig`{{ ${site.data.products}|length }} products` }),
|
|
12
|
+
list({ id: 'products', items: site.data.products, row: p => text({ from: p.item.title }) })
|
|
13
|
+
] })
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
- A path is the source's full name (`apiContainer_site.data.hero.title`), so it goes in `from`, `items`, `bind` and
|
|
17
|
+
`visible` as it is, and into a template through `twig` (a plain template literal writes the same text, but a
|
|
18
|
+
type-checked lint refuses an object in one).
|
|
19
|
+
- A list fed by a path hands its `row` the item typed: every field any item of the sample has.
|
|
20
|
+
- An item of a list is read by position: `site.data.sections[1]`.
|
|
@@ -36,15 +36,19 @@ bar.
|
|
|
36
36
|
import { space } from './src/space.ts';
|
|
37
37
|
|
|
38
38
|
try {
|
|
39
|
-
const { schema, warnings } = authorSpace(space, { plugins: [] /* your declarations */ });
|
|
39
|
+
const { schema, warnings, suggestions } = authorSpace(space, { plugins: [] /* your declarations */ });
|
|
40
40
|
console.log(`${Object.keys(schema.flat).length} elements, ${warnings.length} warnings`);
|
|
41
41
|
warnings.forEach(warning => console.log(`[${warning.code}] ${warning.message}`));
|
|
42
|
+
suggestions.forEach(({ code, message, saves }) => console.log(`[suggest] ${code} (saves ${saves}): ${message}`));
|
|
42
43
|
} catch (error) {
|
|
43
44
|
console.log((error as Error).message);
|
|
44
45
|
}
|
|
45
46
|
```
|
|
46
47
|
|
|
47
|
-
2. **Fix, re-run, until it prints zero warnings.**
|
|
48
|
+
2. **Fix, re-run, until it prints zero warnings.** What has one fix (`author` says how many) is written for you: `npx
|
|
49
|
+
plitzi fix` shows it as a diff of your source, `--write` writes it and keeps it only if the space authors with each
|
|
50
|
+
fix gone and nothing new — a fix it cannot place, or that would add a problem, is said with the reason. Then
|
|
51
|
+
typecheck the project.
|
|
48
52
|
3. **Look at it** — in a browser, at desktop and phone width, in both themes (see [testing](testing.md)). Stage 3 is
|
|
49
53
|
thorough about MEANING and blind to what follows.
|
|
50
54
|
|