@mosetta/react 0.0.12 → 0.0.13

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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @mosetta/react
2
2
 
3
+ ## 0.0.13
4
+
5
+ ### Patch Changes
6
+
7
+ - A server action learns the block it is called from: `block` — the page, the node, the tile and its
8
+ settings as the page stores them — beside `input`, in `action` and in `authorize` (and as
9
+ `BLOCK_TOKEN`). The browser only says which block on which page calls; the runner reads that block
10
+ off the published page itself (a draft's, for the site's editors), so the settings are the site
11
+ author's and can be trusted. `useServerAction` sends where the call comes from without the
12
+ component doing anything.
13
+ - Updated dependencies
14
+ - @mosetta/server-action@0.0.13
15
+ - @mosetta/server@0.0.13
16
+ - @mosetta/core@0.0.13
17
+ - @mosetta/di@0.0.13
18
+ - @mosetta/runtime@0.0.13
19
+
3
20
  ## 0.0.12
4
21
 
5
22
  ### 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.13",
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.13",
17
+ "@mosetta/runtime": "0.0.13",
18
+ "@mosetta/server": "0.0.13",
19
+ "@mosetta/server-action": "0.0.13",
20
+ "@mosetta/di": "0.0.13"
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