@mosetta/react 0.0.12 → 0.0.14
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/AUTHORING.md +34 -0
- package/CHANGELOG.md +28 -0
- package/package.json +6 -6
- package/src/hooks/NodeContext.tsx +3 -0
- package/src/serverAction.ts +15 -3
package/AUTHORING.md
CHANGED
|
@@ -171,6 +171,35 @@ const booking = useServerAction({ token: bookToken, start: EServerActionStart.ST
|
|
|
171
171
|
never in the code or the environment. The server side of a tile is named like its component
|
|
172
172
|
(`booking.server.ts` beside `booking.component.tsx`).
|
|
173
173
|
|
|
174
|
+
### The block a call comes from
|
|
175
|
+
|
|
176
|
+
The input is whatever the browser sent — anybody can send anything. What the site's author set on
|
|
177
|
+
the block (the hours a booking form offers, the address a contact form writes to) is given to the
|
|
178
|
+
action separately, read by the host from the page itself: `block` beside `input`, in `action` and in
|
|
179
|
+
`authorize`.
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
async action({ input, block }: IServerActionPayload<IBookingInput>) {
|
|
183
|
+
const hours = (block?.settings.hours ?? []) as string[];
|
|
184
|
+
if (!hours.includes(input.slot)) return { ok: false }; // not a time this form offers
|
|
185
|
+
// … write the booking
|
|
186
|
+
return { ok: true };
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
- `block` is `{ page, node, reference, settings, draft }`: the page's name, the block's id on it, the
|
|
191
|
+
block's tile, its settings as stored, and whether it was read from a draft. A translated text in
|
|
192
|
+
the settings stays a reference (`{ $ref: … }`), as the page stores it.
|
|
193
|
+
- The component side does nothing for it: `useServerAction` says which block on which page calls.
|
|
194
|
+
The host finds that block on the page as published; in a draft's preview, in the draft — for the
|
|
195
|
+
site's editors only, everybody else gets the published page.
|
|
196
|
+
- `block` is `null` when the call comes from no page, when the block is not on the page as published
|
|
197
|
+
(a stale tab, a new block not published yet), or when it is not a tile of this palette at the
|
|
198
|
+
action's own version. An action that needs the block's settings refuses then: never fall back to
|
|
199
|
+
something the input says.
|
|
200
|
+
- Inside a prefab, the block's id is `<prefab's id on the page>/<id in the prefab>` and its settings
|
|
201
|
+
are as rendered, the instance's fields filled. A block inside a library's prefab is not read.
|
|
202
|
+
|
|
174
203
|
## Rules of a project's palette on the platform
|
|
175
204
|
|
|
176
205
|
- Its brand is the project's own; it holds the project's tiles (and the packages they import), every
|
|
@@ -181,3 +210,8 @@ const booking = useServerAction({ token: bookToken, start: EServerActionStart.ST
|
|
|
181
210
|
error comes back as the checker's line.
|
|
182
211
|
- Every release is the next patch version. Pages keep the version they name; update a block on a
|
|
183
212
|
page to take the new one.
|
|
213
|
+
- A **draft** is a build that is no release: no version spent, the site unchanged. Any page of the
|
|
214
|
+
site opened with `?previewOverride=<brand>.<block>:<draft>` shows that block from the draft
|
|
215
|
+
(`<brand>:<draft>` — every block of it), whatever version the page names — its server side
|
|
216
|
+
included — for the site's editors, or for anybody given a shared link. Release once it is right.
|
|
217
|
+
The palette keeps its last 10 drafts.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @mosetta/react
|
|
2
2
|
|
|
3
|
+
## 0.0.14
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
- @mosetta/core@0.0.14
|
|
9
|
+
- @mosetta/server@0.0.14
|
|
10
|
+
- @mosetta/di@0.0.14
|
|
11
|
+
- @mosetta/runtime@0.0.14
|
|
12
|
+
- @mosetta/server-action@0.0.14
|
|
13
|
+
|
|
14
|
+
## 0.0.13
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- A server action learns the block it is called from: `block` — the page, the node, the tile and its
|
|
19
|
+
settings as the page stores them — beside `input`, in `action` and in `authorize` (and as
|
|
20
|
+
`BLOCK_TOKEN`). The browser only says which block on which page calls; the runner reads that block
|
|
21
|
+
off the published page itself (a draft's, for the site's editors), so the settings are the site
|
|
22
|
+
author's and can be trusted. `useServerAction` sends where the call comes from without the
|
|
23
|
+
component doing anything.
|
|
24
|
+
- Updated dependencies
|
|
25
|
+
- @mosetta/server-action@0.0.13
|
|
26
|
+
- @mosetta/server@0.0.13
|
|
27
|
+
- @mosetta/core@0.0.13
|
|
28
|
+
- @mosetta/di@0.0.13
|
|
29
|
+
- @mosetta/runtime@0.0.13
|
|
30
|
+
|
|
3
31
|
## 0.0.12
|
|
4
32
|
|
|
5
33
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mosetta/react",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"description": "React binding of the mosetta protocol: side definers, hooks, blueprint renderer, DI adapter, server-action hook, startAndMount. Subpath exports only (no barrel) — each entry ships as its own package tile; ./context is the internal state-singleton entry the others re-export from.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"react": "*",
|
|
15
15
|
"react-dom": "*",
|
|
16
|
-
"@mosetta/core": "0.0.
|
|
17
|
-
"@mosetta/di": "0.0.
|
|
18
|
-
"@mosetta/runtime": "0.0.
|
|
19
|
-
"@mosetta/server": "0.0.
|
|
20
|
-
"@mosetta/server
|
|
16
|
+
"@mosetta/core": "0.0.14",
|
|
17
|
+
"@mosetta/di": "0.0.14",
|
|
18
|
+
"@mosetta/runtime": "0.0.14",
|
|
19
|
+
"@mosetta/server-action": "0.0.14",
|
|
20
|
+
"@mosetta/server": "0.0.14"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/react": "*",
|
|
@@ -91,6 +91,9 @@ export const ProvideNode = NodeContext.Provide;
|
|
|
91
91
|
|
|
92
92
|
export const useNode = NodeContext.use;
|
|
93
93
|
|
|
94
|
+
/** the node a component renders in, or null outside a page's tree (a hosted mount, a tool) */
|
|
95
|
+
export const useNodeOptional = NodeContext.useOptional;
|
|
96
|
+
|
|
94
97
|
function useNodeState(): INodeState {
|
|
95
98
|
const { id } = useNode();
|
|
96
99
|
const di = useDi(DI_CONTAINER_TOKEN);
|
package/src/serverAction.ts
CHANGED
|
@@ -19,8 +19,10 @@
|
|
|
19
19
|
// conditional hooks (branches are value-level), no module-level shared state. Opt-in suspense throws
|
|
20
20
|
// the waiter promise — the legacy protocol both runtimes (and preact's renderToStringAsync) support.
|
|
21
21
|
import { useCallback, useState, useSyncExternalStore } from 'react';
|
|
22
|
-
import { useComponent } from '@mosetta/react';
|
|
22
|
+
import { useComponent, useDiOptional, useNodeOptional } from '@mosetta/react';
|
|
23
23
|
import { useDi } from '@mosetta/react';
|
|
24
|
+
import { PAGE_MANAGER_TOKEN } from '@mosetta/runtime/client';
|
|
25
|
+
import { blockOrigin } from '@mosetta/server-action/core';
|
|
24
26
|
import {
|
|
25
27
|
EServerActionStart,
|
|
26
28
|
EXECUTION_ENV_TOKEN,
|
|
@@ -82,6 +84,13 @@ export function useServerAction<TIn extends {}, TOut>(
|
|
|
82
84
|
ensureServerActionClient(di);
|
|
83
85
|
const manager = useDi(SERVER_DATA_MANAGER);
|
|
84
86
|
const { reference } = useComponent();
|
|
87
|
+
// where the call comes from — this node on the page shown now — for the host to read the
|
|
88
|
+
// block's settings itself (core.ts BLOCK_ORIGIN_HEADER); none outside a page. Asked again at
|
|
89
|
+
// every mutation: a block a move onto another page keeps is that page's block from then on
|
|
90
|
+
const pages = useDiOptional(PAGE_MANAGER_TOKEN);
|
|
91
|
+
const nodeId = useNodeOptional()?.id;
|
|
92
|
+
const originNow = (): string | null => blockOrigin(pages ? (pages.getCurrentPage() as { origin?: string }).origin : null, nodeId);
|
|
93
|
+
const origin = originNow();
|
|
85
94
|
|
|
86
95
|
const env = useDi(EXECUTION_ENV_TOKEN);
|
|
87
96
|
const recorder = useDi(SSR_ACTION_RECORDER_TOKEN);
|
|
@@ -116,18 +125,21 @@ export function useServerAction<TIn extends {}, TOut>(
|
|
|
116
125
|
actionName: token.actionName,
|
|
117
126
|
invalidates: mergeTags(token.invalidates, options.invalidates),
|
|
118
127
|
customSerializer: token.customSerializer,
|
|
128
|
+
originOf: originNow,
|
|
119
129
|
});
|
|
120
130
|
}
|
|
121
131
|
return null;
|
|
122
132
|
});
|
|
123
133
|
|
|
124
|
-
|
|
134
|
+
// two blocks of one tile asking with one input are two questions: the host answers each by its
|
|
135
|
+
// own block's settings
|
|
136
|
+
const key = localHandle ? null : `${fullId}/${token.actionName}#${stableStringify(options.input)}${origin ? `@${origin}` : ''}`;
|
|
125
137
|
if (key !== null) {
|
|
126
138
|
// render-phase, but idempotent by key (start-on-first-read); a new input → a new key → a new query
|
|
127
139
|
manager.ensureQuery({
|
|
128
140
|
key,
|
|
129
141
|
tags: mergeTags(token.tags, options.tags),
|
|
130
|
-
request: { fullId, actionName: token.actionName, input: options.input },
|
|
142
|
+
request: { fullId, actionName: token.actionName, input: options.input, ...(origin ? { origin } : {}) },
|
|
131
143
|
customSerializer: token.customSerializer,
|
|
132
144
|
});
|
|
133
145
|
// the head channel's counter waits for this query from the SAME render that started it
|