@fias/arche-sdk 1.6.0 → 1.8.0

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/dist/index.mjs CHANGED
@@ -353,6 +353,36 @@ function useBridge() {
353
353
  }
354
354
  return bridge;
355
355
  }
356
+ var PERSIST_DEBOUNCE_WAIT_MS = 250;
357
+ var PERSIST_DEBOUNCE_MAX_WAIT_MS = 1e3;
358
+ function createDebouncedWriter(bridge) {
359
+ let pending = null;
360
+ function flush() {
361
+ if (!pending) return;
362
+ const { path, content, timerId } = pending;
363
+ if (timerId !== null) clearTimeout(timerId);
364
+ pending = null;
365
+ bridge.request("storage_write", { path, content }).catch(() => {
366
+ });
367
+ }
368
+ function schedule(path, content) {
369
+ const now = Date.now();
370
+ if (pending && pending.path !== path) {
371
+ flush();
372
+ }
373
+ if (!pending) {
374
+ pending = { path, content, timerId: null, firstScheduledAt: now };
375
+ } else {
376
+ pending.content = content;
377
+ }
378
+ if (pending.timerId !== null) clearTimeout(pending.timerId);
379
+ const elapsed = now - pending.firstScheduledAt;
380
+ const remainingMax = Math.max(0, PERSIST_DEBOUNCE_MAX_WAIT_MS - elapsed);
381
+ const delay = Math.min(PERSIST_DEBOUNCE_WAIT_MS, remainingMax);
382
+ pending.timerId = setTimeout(flush, delay);
383
+ }
384
+ return { schedule, flush };
385
+ }
356
386
  function useFiasUser() {
357
387
  const bridge = useBridge();
358
388
  const [user, setUser] = useState2(null);
@@ -482,41 +512,87 @@ function usePersistentState(key, initialValue) {
482
512
  const bridge = useBridge();
483
513
  const [value, setValueInternal] = useState2(initialValue);
484
514
  const initializedRef = useRef(false);
515
+ const valueRef = useRef(initialValue);
516
+ const writer = useMemo2(() => createDebouncedWriter(bridge), [bridge]);
485
517
  useEffect2(() => {
486
518
  bridge.request("storage_read", { path: `__state/${key}` }).then((result) => {
487
519
  if (result.exists && result.content !== null) {
488
520
  try {
489
- setValueInternal(JSON.parse(result.content));
521
+ const parsed = JSON.parse(result.content);
522
+ valueRef.current = parsed;
523
+ setValueInternal(parsed);
490
524
  } catch {
491
525
  }
492
526
  }
493
527
  initializedRef.current = true;
494
528
  });
495
- }, [bridge, key]);
529
+ return () => {
530
+ writer.flush();
531
+ };
532
+ }, [bridge, key, writer]);
496
533
  const setValue = useCallback(
497
534
  (next) => {
498
- setValueInternal((prev) => {
499
- const resolved = typeof next === "function" ? next(prev) : next;
500
- bridge.request("storage_write", {
501
- path: `__state/${key}`,
502
- content: JSON.stringify(resolved)
503
- }).catch(() => {
504
- });
505
- return resolved;
506
- });
535
+ const resolved = typeof next === "function" ? next(valueRef.current) : next;
536
+ valueRef.current = resolved;
537
+ setValueInternal(resolved);
538
+ writer.schedule(`__state/${key}`, JSON.stringify(resolved));
507
539
  },
508
- [bridge, key]
540
+ [key, writer]
509
541
  );
510
542
  return [value, setValue];
511
543
  }
512
- function useStepNavigation(initialStep) {
544
+ function useStepNavigation(initialStep, options) {
513
545
  const bridge = useBridge();
514
- const [currentStep, setCurrentStep] = useState2(initialStep ?? null);
546
+ const persistKey = options?.persistKey;
547
+ const [currentStep, setCurrentStepInternal] = useState2(initialStep ?? null);
548
+ const initializedRef = useRef(false);
549
+ const writer = useMemo2(() => createDebouncedWriter(bridge), [bridge]);
550
+ useEffect2(() => {
551
+ if (!persistKey) {
552
+ initializedRef.current = true;
553
+ return;
554
+ }
555
+ let cancelled = false;
556
+ bridge.request("storage_read", {
557
+ path: `__state/${persistKey}`
558
+ }).then((result) => {
559
+ if (cancelled || initializedRef.current) return;
560
+ if (result.exists && result.content !== null) {
561
+ try {
562
+ const parsed = JSON.parse(result.content);
563
+ if (typeof parsed === "string" || parsed === null) {
564
+ setCurrentStepInternal(parsed);
565
+ }
566
+ } catch {
567
+ }
568
+ }
569
+ initializedRef.current = true;
570
+ }).catch(() => {
571
+ initializedRef.current = true;
572
+ });
573
+ return () => {
574
+ cancelled = true;
575
+ writer.flush();
576
+ };
577
+ }, [bridge, persistKey, writer]);
515
578
  useEffect2(() => {
516
579
  return bridge.onStepNavigationUpdate((nodeId) => {
517
- setCurrentStep(nodeId);
580
+ setCurrentStepInternal(nodeId);
581
+ if (persistKey) {
582
+ writer.schedule(`__state/${persistKey}`, JSON.stringify(nodeId));
583
+ }
518
584
  });
519
- }, [bridge]);
585
+ }, [bridge, persistKey, writer]);
586
+ const setCurrentStep = useCallback(
587
+ (nodeId) => {
588
+ initializedRef.current = true;
589
+ setCurrentStepInternal(nodeId);
590
+ if (persistKey) {
591
+ writer.schedule(`__state/${persistKey}`, JSON.stringify(nodeId));
592
+ }
593
+ },
594
+ [persistKey, writer]
595
+ );
520
596
  return { currentStep, setCurrentStep };
521
597
  }
522
598
  function useFiasDataStore() {
package/dist/types.d.ts CHANGED
@@ -81,7 +81,11 @@ export interface EntityInvocationApi {
81
81
  isLoading: boolean;
82
82
  result: EntityInvocationResult | null;
83
83
  error: Error | null;
84
- /** Partial text received so far during a streaming invocation. */
84
+ /**
85
+ * Tokens accumulated during a streaming invocation. Persists after the
86
+ * call completes (not reset to ''), so render either `streamingText` OR
87
+ * `result.output` — never both, or you'll show the response twice.
88
+ */
85
89
  streamingText: string;
86
90
  }
87
91
  export interface EntityInvocationParams {
@@ -152,6 +156,21 @@ export interface StepNavigationApi {
152
156
  currentStep: string | null;
153
157
  setCurrentStep: (step: string | null) => void;
154
158
  }
159
+ /**
160
+ * Options for useStepNavigation().
161
+ */
162
+ export interface StepNavigationOptions {
163
+ /**
164
+ * When set, persists currentStep to bridge storage under `__state/<persistKey>`.
165
+ * The persisted value is read on mount (overriding initialStep if present) and
166
+ * written on every setCurrentStep call and incoming host step_navigate message.
167
+ *
168
+ * Use this instead of wrapping currentStep in usePersistentState separately —
169
+ * two independent step-state sources cause a bidirectional useEffect sync loop
170
+ * that spams storage and flashes the UI.
171
+ */
172
+ persistKey?: string;
173
+ }
155
174
  /**
156
175
  * Message types sent from the parent frame to the plugin iframe.
157
176
  */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fias/arche-sdk",
3
- "version": "1.6.0",
3
+ "version": "1.8.0",
4
4
  "description": "SDK for building FIAS platform plugin arches",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -2,7 +2,7 @@
2
2
 
3
3
  This project is a FIAS platform plugin — a React application that runs in a sandboxed iframe within the FIAS marketplace. This file provides the context AI coding assistants need to build, test, and submit plugins effectively.
4
4
 
5
- For other AI tool instruction files, see `AGENTS.md` (identical content).
5
+ For other AI tool instruction files, see `CLAUDE.md` (identical content).
6
6
 
7
7
  ## Project Structure
8
8
 
@@ -183,13 +183,17 @@ function AISummarizer() {
183
183
  });
184
184
  }
185
185
 
186
+ // streamingText holds the accumulated tokens during the call AND continues to
187
+ // hold the full text after the call completes — it is NOT cleared. result.output
188
+ // also contains the full text once invoke() resolves. Render ONE slot only —
189
+ // streamingText while loading, result.output afterwards. Showing both renders
190
+ // the response twice.
186
191
  return (
187
192
  <div>
188
193
  <button onClick={() => summarize('...')} disabled={isLoading}>
189
194
  Summarize
190
195
  </button>
191
- {isLoading && <p>{streamingText}</p>}
192
- {result && <p>{result.output}</p>}
196
+ <p>{isLoading ? streamingText : result?.output}</p>
193
197
  {error && <p>Error: {error.message}</p>}
194
198
  </div>
195
199
  );
@@ -249,12 +253,22 @@ navigateTo('/settings');
249
253
 
250
254
  **Returns:** `StepNavigationApi`
251
255
 
256
+ Call **once** in the top-level App component. Share `currentStep` and `setCurrentStep` with step components via React context — do not call `useStepNavigation` from step components (each call creates an independent state).
257
+
252
258
  ```tsx
253
259
  import { useStepNavigation } from '@fias/arche-sdk';
254
260
 
261
+ // Without persistence — currentStep resets on preview rebuild
255
262
  const { currentStep, setCurrentStep } = useStepNavigation('step-1');
263
+
264
+ // With persistence — currentStep survives preview rebuilds (SDK ≥ 1.7.0)
265
+ const { currentStep, setCurrentStep } = useStepNavigation('step-1', {
266
+ persistKey: 'currentStep',
267
+ });
256
268
  ```
257
269
 
270
+ **Do NOT wrap `currentStep` in `usePersistentState`.** Two independent step-state sources create a bidirectional `useEffect` sync loop that spams `storage_write` and flashes the UI between steps. `persistKey` is the only correct way to persist the current step.
271
+
258
272
  ### `usePersistentState()` — Auto-saving state
259
273
 
260
274
  **Permission:** `storage:sandbox`
@@ -268,6 +282,12 @@ const [count, setCount] = usePersistentState<number>('counter', 0);
268
282
  // Automatically persists to storage on change
269
283
  ```
270
284
 
285
+ Use for domain data (form inputs, selections, AI results). **Do not use for `currentStep`** — use `useStepNavigation({ persistKey })` instead.
286
+
287
+ **Writes are debounced (SDK ≥ 1.8.0).** The in-memory value updates synchronously on every setter call, but the underlying `storage_write` is coalesced with a 250 ms trailing-edge debounce and a 1 s max-wait, and flushed on unmount. This makes the hook safe to call from `requestAnimationFrame`, `mousemove`, and other high-frequency handlers — bursts no longer trip the `storage_write` rate limit.
288
+
289
+ Trade-off: reading the same key via `useFiasStorage().readFile('__state/foo')` immediately after a `setValue` can see a stale value for up to ~250 ms. Read through the hook instead of bypassing it.
290
+
271
291
  ### `fias` — Imperative utilities
272
292
 
273
293
  ```tsx
@@ -338,6 +358,8 @@ These are hard limits enforced by the platform. Code that violates these will fa
338
358
  - `storage_read`: 300/minute
339
359
  - `storage_list`, `storage_delete`: 60/minute
340
360
 
361
+ A separate **runaway-loop detector** also fires if any method is called >50 times in 5 s and blocks that method for 10 s. Errors include the target for diagnostics, e.g. `Runaway loop detected: "storage_write" (path: __state/gameState) called 51 times in 5s.` — if you see this, look at the identified path and debounce the updates at the source.
362
+
341
363
  ### Security Rules (enforced during review)
342
364
 
343
365
  - No `eval()`, `Function()`, `innerHTML`, or dynamic code execution
@@ -418,6 +440,84 @@ npm run submit
418
440
  # First listing: 5000 credits ($50). Re-submissions: 100 credits ($1).
419
441
  ```
420
442
 
443
+ ## Common Pitfalls
444
+
445
+ These are real bugs that have shipped from AI-generated plugins. Avoid them.
446
+
447
+ ### Don't navigate from a `useEffect` that watches derived state
448
+
449
+ If a step component derives a value from context state and uses `useEffect` to redirect when that derived value is missing, the redirect can fire mid-update and yank the user away from a working screen. Common shape:
450
+
451
+ ```tsx
452
+ // ❌ BUG: redirects whenever conversations changes between renders
453
+ const currentConversation = conversations.find((c) => c.id === currentConversationId);
454
+
455
+ useEffect(() => {
456
+ if (!currentConversation) {
457
+ setCurrentStep('list');
458
+ }
459
+ }, [currentConversation, setCurrentStep]); // fires on every conversations change
460
+ ```
461
+
462
+ `currentConversation` is a _derived_ value — its reference changes every time `conversations` changes. The effect re-runs and may redirect during a state transition (e.g., right after the user sends a message and the component is mid-update). The user reports "I sent a message and got bounced back" or "the response never showed up".
463
+
464
+ ```tsx
465
+ // ✅ FIX: gate on the *id* (which is durable) and render a loading state when
466
+ // the conversation can't be found yet — let the user navigate back manually
467
+ // instead of forcing it.
468
+ useEffect(() => {
469
+ if (!currentConversationId) setCurrentStep('list');
470
+ }, [currentConversationId, setCurrentStep]);
471
+
472
+ if (!currentConversation) return <div>Loading conversation…</div>;
473
+ ```
474
+
475
+ ### Use functional updaters when an `await` sits between reads and writes
476
+
477
+ When you read state, `await` something, then write back, the closure captures the _stale_ state. Use the functional form so the setter receives the latest value at the time of the update:
478
+
479
+ ```tsx
480
+ // ❌ BUG: `messages` here is the snapshot from before await
481
+ const messages = currentConversation.messages;
482
+ const response = await invoke({ entityId, input });
483
+ setConversations(
484
+ conversations.map((c) => (c.id === id ? { ...c, messages: [...messages, response] } : c)),
485
+ );
486
+
487
+ // ✅ FIX: functional updater reads fresh state at update time
488
+ await invoke({ entityId, input });
489
+ setConversations((prev) =>
490
+ prev.map((c) => (c.id === id ? { ...c, messages: [...c.messages, response] } : c)),
491
+ );
492
+ ```
493
+
494
+ This matters most for `usePersistentState` on lists: the user can fire many updates quickly, and lost-update bugs are common with snapshot-style updates.
495
+
496
+ ### Render `streamingText` OR `result.output` — never both
497
+
498
+ Already covered in the `useEntityInvocation` section above, but worth repeating: `streamingText` is _not_ cleared after the call completes — it keeps holding the full final text. Showing `{streamingText && ...}` AND `{result && ...}` in separate JSX nodes prints the response twice as soon as `result` arrives. Use one slot: `{isLoading ? streamingText : result?.output}`.
499
+
500
+ ### Verify visually when the user reports a UI bug
501
+
502
+ If a user says "I don't see the response" or "the layout's broken", don't rely solely on `console.log` and `useFiasStorage` reads. Inspect the actual rendered output. The platform exposes `get_preview_screenshot` to AI builders for exactly this reason.
503
+
504
+ ### Don't persist per-frame state
505
+
506
+ For state that updates every frame (game position, drag coordinates, animated values), use plain `useState` — not `usePersistentState`. Persisting every ball-position tick writes unbounded data to durable storage and doesn't survive reloads in any useful way anyway (the ball will be somewhere different when the player resumes).
507
+
508
+ Hook-level debouncing (SDK ≥ 1.8.0) makes `usePersistentState` safe under bursts, but you still shouldn't persist what you'll discard on reload. Keep ephemeral state in `useState` and persist only meaningful checkpoints (final score, selected difficulty, high-score list).
509
+
510
+ ```tsx
511
+ // ❌ Persisting ball position every frame — meaningless on reload
512
+ const [gameState, setGameState] = usePersistentState<GameState>('gameState', initial);
513
+ requestAnimationFrame(() => setGameState(advance(gameState)));
514
+
515
+ // ✅ Ephemeral for live gameplay, persisted for results
516
+ const [gameState, setGameState] = useState<GameState>(initial);
517
+ const [highScores, setHighScores] = usePersistentState<Score[]>('highScores', []);
518
+ // persist highScores only when a run completes
519
+ ```
520
+
421
521
  ## Common Patterns
422
522
 
423
523
  ### Theme-Aware Card Component
@@ -183,13 +183,17 @@ function AISummarizer() {
183
183
  });
184
184
  }
185
185
 
186
+ // streamingText holds the accumulated tokens during the call AND continues to
187
+ // hold the full text after the call completes — it is NOT cleared. result.output
188
+ // also contains the full text once invoke() resolves. Render ONE slot only —
189
+ // streamingText while loading, result.output afterwards. Showing both renders
190
+ // the response twice.
186
191
  return (
187
192
  <div>
188
193
  <button onClick={() => summarize('...')} disabled={isLoading}>
189
194
  Summarize
190
195
  </button>
191
- {isLoading && <p>{streamingText}</p>}
192
- {result && <p>{result.output}</p>}
196
+ <p>{isLoading ? streamingText : result?.output}</p>
193
197
  {error && <p>Error: {error.message}</p>}
194
198
  </div>
195
199
  );
@@ -249,12 +253,22 @@ navigateTo('/settings');
249
253
 
250
254
  **Returns:** `StepNavigationApi`
251
255
 
256
+ Call **once** in the top-level App component. Share `currentStep` and `setCurrentStep` with step components via React context — do not call `useStepNavigation` from step components (each call creates an independent state).
257
+
252
258
  ```tsx
253
259
  import { useStepNavigation } from '@fias/arche-sdk';
254
260
 
261
+ // Without persistence — currentStep resets on preview rebuild
255
262
  const { currentStep, setCurrentStep } = useStepNavigation('step-1');
263
+
264
+ // With persistence — currentStep survives preview rebuilds (SDK ≥ 1.7.0)
265
+ const { currentStep, setCurrentStep } = useStepNavigation('step-1', {
266
+ persistKey: 'currentStep',
267
+ });
256
268
  ```
257
269
 
270
+ **Do NOT wrap `currentStep` in `usePersistentState`.** Two independent step-state sources create a bidirectional `useEffect` sync loop that spams `storage_write` and flashes the UI between steps. `persistKey` is the only correct way to persist the current step.
271
+
258
272
  ### `usePersistentState()` — Auto-saving state
259
273
 
260
274
  **Permission:** `storage:sandbox`
@@ -268,6 +282,12 @@ const [count, setCount] = usePersistentState<number>('counter', 0);
268
282
  // Automatically persists to storage on change
269
283
  ```
270
284
 
285
+ Use for domain data (form inputs, selections, AI results). **Do not use for `currentStep`** — use `useStepNavigation({ persistKey })` instead.
286
+
287
+ **Writes are debounced (SDK ≥ 1.8.0).** The in-memory value updates synchronously on every setter call, but the underlying `storage_write` is coalesced with a 250 ms trailing-edge debounce and a 1 s max-wait, and flushed on unmount. This makes the hook safe to call from `requestAnimationFrame`, `mousemove`, and other high-frequency handlers — bursts no longer trip the `storage_write` rate limit.
288
+
289
+ Trade-off: reading the same key via `useFiasStorage().readFile('__state/foo')` immediately after a `setValue` can see a stale value for up to ~250 ms. Read through the hook instead of bypassing it.
290
+
271
291
  ### `fias` — Imperative utilities
272
292
 
273
293
  ```tsx
@@ -338,6 +358,8 @@ These are hard limits enforced by the platform. Code that violates these will fa
338
358
  - `storage_read`: 300/minute
339
359
  - `storage_list`, `storage_delete`: 60/minute
340
360
 
361
+ A separate **runaway-loop detector** also fires if any method is called >50 times in 5 s and blocks that method for 10 s. Errors include the target for diagnostics, e.g. `Runaway loop detected: "storage_write" (path: __state/gameState) called 51 times in 5s.` — if you see this, look at the identified path and debounce the updates at the source.
362
+
341
363
  ### Security Rules (enforced during review)
342
364
 
343
365
  - No `eval()`, `Function()`, `innerHTML`, or dynamic code execution
@@ -418,6 +440,84 @@ npm run submit
418
440
  # First listing: 5000 credits ($50). Re-submissions: 100 credits ($1).
419
441
  ```
420
442
 
443
+ ## Common Pitfalls
444
+
445
+ These are real bugs that have shipped from AI-generated plugins. Avoid them.
446
+
447
+ ### Don't navigate from a `useEffect` that watches derived state
448
+
449
+ If a step component derives a value from context state and uses `useEffect` to redirect when that derived value is missing, the redirect can fire mid-update and yank the user away from a working screen. Common shape:
450
+
451
+ ```tsx
452
+ // ❌ BUG: redirects whenever conversations changes between renders
453
+ const currentConversation = conversations.find((c) => c.id === currentConversationId);
454
+
455
+ useEffect(() => {
456
+ if (!currentConversation) {
457
+ setCurrentStep('list');
458
+ }
459
+ }, [currentConversation, setCurrentStep]); // fires on every conversations change
460
+ ```
461
+
462
+ `currentConversation` is a _derived_ value — its reference changes every time `conversations` changes. The effect re-runs and may redirect during a state transition (e.g., right after the user sends a message and the component is mid-update). The user reports "I sent a message and got bounced back" or "the response never showed up".
463
+
464
+ ```tsx
465
+ // ✅ FIX: gate on the *id* (which is durable) and render a loading state when
466
+ // the conversation can't be found yet — let the user navigate back manually
467
+ // instead of forcing it.
468
+ useEffect(() => {
469
+ if (!currentConversationId) setCurrentStep('list');
470
+ }, [currentConversationId, setCurrentStep]);
471
+
472
+ if (!currentConversation) return <div>Loading conversation…</div>;
473
+ ```
474
+
475
+ ### Use functional updaters when an `await` sits between reads and writes
476
+
477
+ When you read state, `await` something, then write back, the closure captures the _stale_ state. Use the functional form so the setter receives the latest value at the time of the update:
478
+
479
+ ```tsx
480
+ // ❌ BUG: `messages` here is the snapshot from before await
481
+ const messages = currentConversation.messages;
482
+ const response = await invoke({ entityId, input });
483
+ setConversations(
484
+ conversations.map((c) => (c.id === id ? { ...c, messages: [...messages, response] } : c)),
485
+ );
486
+
487
+ // ✅ FIX: functional updater reads fresh state at update time
488
+ await invoke({ entityId, input });
489
+ setConversations((prev) =>
490
+ prev.map((c) => (c.id === id ? { ...c, messages: [...c.messages, response] } : c)),
491
+ );
492
+ ```
493
+
494
+ This matters most for `usePersistentState` on lists: the user can fire many updates quickly, and lost-update bugs are common with snapshot-style updates.
495
+
496
+ ### Render `streamingText` OR `result.output` — never both
497
+
498
+ Already covered in the `useEntityInvocation` section above, but worth repeating: `streamingText` is _not_ cleared after the call completes — it keeps holding the full final text. Showing `{streamingText && ...}` AND `{result && ...}` in separate JSX nodes prints the response twice as soon as `result` arrives. Use one slot: `{isLoading ? streamingText : result?.output}`.
499
+
500
+ ### Verify visually when the user reports a UI bug
501
+
502
+ If a user says "I don't see the response" or "the layout's broken", don't rely solely on `console.log` and `useFiasStorage` reads. Inspect the actual rendered output. The platform exposes `get_preview_screenshot` to AI builders for exactly this reason.
503
+
504
+ ### Don't persist per-frame state
505
+
506
+ For state that updates every frame (game position, drag coordinates, animated values), use plain `useState` — not `usePersistentState`. Persisting every ball-position tick writes unbounded data to durable storage and doesn't survive reloads in any useful way anyway (the ball will be somewhere different when the player resumes).
507
+
508
+ Hook-level debouncing (SDK ≥ 1.8.0) makes `usePersistentState` safe under bursts, but you still shouldn't persist what you'll discard on reload. Keep ephemeral state in `useState` and persist only meaningful checkpoints (final score, selected difficulty, high-score list).
509
+
510
+ ```tsx
511
+ // ❌ Persisting ball position every frame — meaningless on reload
512
+ const [gameState, setGameState] = usePersistentState<GameState>('gameState', initial);
513
+ requestAnimationFrame(() => setGameState(advance(gameState)));
514
+
515
+ // ✅ Ephemeral for live gameplay, persisted for results
516
+ const [gameState, setGameState] = useState<GameState>(initial);
517
+ const [highScores, setHighScores] = usePersistentState<Score[]>('highScores', []);
518
+ // persist highScores only when a run completes
519
+ ```
520
+
421
521
  ## Common Patterns
422
522
 
423
523
  ### Theme-Aware Card Component
@@ -0,0 +1,13 @@
1
+ # Multi-Step FIAS Plugin Template
2
+
3
+ Canonical pattern for multi-step plugins. The important bits live in:
4
+
5
+ - `src/App.tsx` — calls `useStepNavigation('step-one', { persistKey: 'currentStep' })` **once**. Single source of truth for the current step.
6
+ - `src/context/AppContext.tsx` — exposes `currentStep` and `setCurrentStep` to step components, plus any domain data via `usePersistentState`.
7
+ - `src/steps/<step-id>/<StepName>.tsx` — step components read/write via context. They do not call `useStepNavigation` themselves.
8
+
9
+ ## Why not `usePersistentState('currentStep', ...)`?
10
+
11
+ That used to look tempting, but it creates two independent step-state sources (one from `useStepNavigation`, one from `usePersistentState`). Any `useEffect` that syncs them turns into a bidirectional ping-pong loop that spams `storage_write` and flashes the UI between steps.
12
+
13
+ Since SDK 1.7.0, `useStepNavigation` handles persistence directly via `{ persistKey }`. That is the only correct way to persist step state. Domain data (form inputs, AI-generated content, user selections) can still use `usePersistentState` — just not `currentStep`.
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "{{name}}",
3
+ "version": "1.0.0",
4
+ "description": "A multi-step FIAS plugin arche",
5
+ "main": "src/index.tsx",
6
+ "archeType": "tool",
7
+ "tags": [],
8
+ "pricing": { "model": "free", "currency": "usd" },
9
+ "permissions": ["theme:read", "storage:sandbox"],
10
+ "sdk": "^1.7.0"
11
+ }
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>FIAS Plugin</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/index.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "{{name}}",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "start": "vite & sleep 2 && fias-dev",
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "validate": "tsc --noEmit",
10
+ "dev:harness": "fias-dev",
11
+ "submit": "fias-dev submit"
12
+ },
13
+ "dependencies": {
14
+ "@fias/arche-sdk": "^1.7.0",
15
+ "react": "^19.0.0",
16
+ "react-dom": "^19.0.0"
17
+ },
18
+ "devDependencies": {
19
+ "@fias/plugin-dev-harness": "^1.0.0",
20
+ "@types/react": "^19.0.0",
21
+ "@types/react-dom": "^19.0.0",
22
+ "@vitejs/plugin-react": "^4.0.0",
23
+ "typescript": "^5.3.3",
24
+ "vite": "^6.0.0"
25
+ }
26
+ }
@@ -0,0 +1,38 @@
1
+ import { useFiasTheme, useStepNavigation } from '@fias/arche-sdk';
2
+ import { AppProvider } from './context/AppContext';
3
+ import { StepOne } from './steps/step-one/StepOne';
4
+ import { StepTwo } from './steps/step-two/StepTwo';
5
+
6
+ function AppContent() {
7
+ const theme = useFiasTheme();
8
+
9
+ // Single source of truth for step navigation.
10
+ // persistKey makes currentStep survive preview rebuilds via bridge storage.
11
+ // Do NOT also wrap currentStep in usePersistentState — two sources create
12
+ // a bidirectional useEffect sync loop that spams storage and flashes the UI.
13
+ const { currentStep, setCurrentStep } = useStepNavigation('step-one', {
14
+ persistKey: 'currentStep',
15
+ });
16
+
17
+ if (!theme) return null;
18
+
19
+ return (
20
+ <AppProvider currentStep={currentStep} setCurrentStep={setCurrentStep}>
21
+ <div
22
+ style={{
23
+ minHeight: '100vh',
24
+ backgroundColor: theme.colors.background,
25
+ color: theme.colors.text,
26
+ fontFamily: theme.fonts.body,
27
+ padding: theme.spacing.lg,
28
+ }}
29
+ >
30
+ {currentStep === 'step-two' ? <StepTwo /> : <StepOne />}
31
+ </div>
32
+ </AppProvider>
33
+ );
34
+ }
35
+
36
+ export function App() {
37
+ return <AppContent />;
38
+ }