@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 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.12",
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.12",
17
- "@mosetta/di": "0.0.12",
18
- "@mosetta/runtime": "0.0.12",
19
- "@mosetta/server": "0.0.12",
20
- "@mosetta/server-action": "0.0.12"
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);
@@ -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
- const key = localHandle ? null : `${fullId}/${token.actionName}#${stableStringify(options.input)}`;
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