@weave-apps/sdk 0.1.7 → 0.1.9

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/README.md CHANGED
@@ -11,10 +11,68 @@ Official SDK for building third-party applications for the Weave Platform.
11
11
  - ✅ **DOM API** - Secure parent page DOM manipulation
12
12
  - ✅ **Shadow DOM Isolation** - Scoped styles and encapsulation
13
13
  - ✅ **Background Services** - Run code without user interaction
14
+ - ✅ **State Persistence** - Survive page reloads with auto-persist
15
+ - ✅ **Multi-Page Flows** - Pending operations across navigations
14
16
  - ✅ **Form Integration** - Auto-fill forms with extracted data
15
17
  - ✅ **AI Integration** - Leverage AI for smart form filling
16
18
  - ✅ **Data Persistence** - Store app data with the Weave API
17
19
 
20
+
21
+ <!-- START doctoc generated TOC please keep comment here to allow auto update -->
22
+ <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
23
+ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
24
+
25
+ - [Quick Start](#quick-start)
26
+ - [1. Initialize a New App](#1-initialize-a-new-app)
27
+ - [2. Develop Your App](#2-develop-your-app)
28
+ - [3. Build Your App](#3-build-your-app)
29
+ - [4. Upload to Weave](#4-upload-to-weave)
30
+ - [API Reference](#api-reference)
31
+ - [WeaveBaseApp](#weavebaseapp)
32
+ - [Constructor](#constructor)
33
+ - [Settings & State](#settings--state)
34
+ - [Methods to Implement](#methods-to-implement)
35
+ - [`render(): void | Promise<void>`](#render-void--promisevoid)
36
+ - [`setupEventListeners(): void` (Optional)](#setupeventlisteners-void-optional)
37
+ - [`onBackgroundService(): void` (Optional)](#onbackgroundservice-void-optional)
38
+ - [`onUrlChange(newUrl: string): void` (Optional)](#onurlchangenewurl-string-void-optional)
39
+ - [`cleanup(): void` (Optional)](#cleanup-void-optional)
40
+ - [Helper Methods](#helper-methods)
41
+ - [`renderHTML(html: string): void`](#renderhtmlhtml-string-void)
42
+ - [`setState(updates: object): void`](#setstateupdates-object-void)
43
+ - [`this.background` (Property)](#thisbackground-property)
44
+ - [`query<T>(selector: string): T | null`](#querytselector-string-t--null)
45
+ - [`queryAll<T>(selector: string): NodeListOf<T>`](#queryalltselector-string-nodelistoft)
46
+ - [WeaveDOMAPI](#weavedomapi)
47
+ - [URL & Navigation](#url--navigation)
48
+ - [Read Operations](#read-operations)
49
+ - [Write Operations](#write-operations)
50
+ - [DOM Injection](#dom-injection)
51
+ - [Event Listeners](#event-listeners)
52
+ - [WeaveAPIClient](#weaveapiclient)
53
+ - [App Data Management](#app-data-management)
54
+ - [AI Integration](#ai-integration)
55
+ - [Utilities](#utilities)
56
+ - [Advanced Topics](#advanced-topics)
57
+ - [State Persistence (Survive Page Reloads)](#state-persistence-survive-page-reloads)
58
+ - [Auto-Persist (Recommended)](#auto-persist-recommended)
59
+ - [Multi-Page Flows (Pending Operations)](#multi-page-flows-pending-operations)
60
+ - [State TTL](#state-ttl)
61
+ - [Settings & Configuration](#settings--configuration)
62
+ - [Error Handling](#error-handling)
63
+ - [Performance Tips](#performance-tips)
64
+ - [Build Process](#build-process)
65
+ - [Security](#security)
66
+ - [Project Structure](#project-structure)
67
+ - [Troubleshooting](#troubleshooting)
68
+ - ["TypeScript not found in SDK"](#typescript-not-found-in-sdk)
69
+ - [Form not filling](#form-not-filling)
70
+ - [Button not injecting](#button-not-injecting)
71
+ - [State not persisting across page reloads](#state-not-persisting-across-page-reloads)
72
+ - [State not persisting long-term](#state-not-persisting-long-term)
73
+
74
+ <!-- END doctoc generated TOC please keep comment here to allow auto update -->
75
+
18
76
  ## Quick Start
19
77
 
20
78
  ### 1. Initialize a New App
@@ -124,6 +182,8 @@ constructor(appInfo: WeaveAppInfo)
124
182
  - `description` (string) - Detailed description
125
183
  - `author` (string) - Author name
126
184
  - `tags` (string[])` - Optional tags
185
+ - `persistState` (boolean) - Enable auto-persist across page reloads (default: false)
186
+ - `persistDebounce` (number) - Debounce delay in ms for state saves (default: 100)
127
187
 
128
188
  #### Settings & State
129
189
 
@@ -223,12 +283,35 @@ this.renderHTML('<h1>Hello</h1>');
223
283
  ```
224
284
 
225
285
  ##### `setState(updates: object): void`
226
- Update app state (shallow merge).
286
+ Update app state (shallow merge). If `persistState: true`, state is automatically saved to background service.
227
287
 
228
288
  ```typescript
229
289
  this.setState({ isLoading: true, count: 5 });
230
290
  ```
231
291
 
292
+ ##### `this.background` (Property)
293
+ Access the background API for state persistence and pending operations.
294
+
295
+ ```typescript
296
+ // Save state manually
297
+ await this.background?.saveState({ count: 5 });
298
+
299
+ // Load state manually
300
+ const state = await this.background?.loadState();
301
+
302
+ // Set pending operation before navigation
303
+ await this.background?.setPendingOperation({
304
+ type: 'fill-form',
305
+ data: { content: '...' }
306
+ });
307
+
308
+ // Get pending operation after navigation
309
+ const pending = await this.background?.getPendingOperation();
310
+
311
+ // Clear pending operation
312
+ await this.background?.clearPendingOperation();
313
+ ```
314
+
232
315
  ##### `query<T>(selector: string): T | null`
233
316
  Query a single element in shadow root.
234
317
 
@@ -378,6 +461,85 @@ const html = window.weaveAPI.utils.markdownToHtml(markdownString);
378
461
 
379
462
  ## Advanced Topics
380
463
 
464
+ ### State Persistence (Survive Page Reloads)
465
+
466
+ Apps can persist state across page reloads using the background state service. There are two approaches:
467
+
468
+ #### Auto-Persist (Recommended)
469
+
470
+ Simply set `persistState: true` in your app config. State is automatically saved on every `setState()` call and restored when the app initializes after a page reload.
471
+
472
+ ```typescript
473
+ class MyApp extends WeaveBaseApp<Settings, State> {
474
+ constructor() {
475
+ super({
476
+ id: 'my-app',
477
+ name: 'My App',
478
+ version: '1.0.0',
479
+ category: 'utility',
480
+ description: 'App with persistent state',
481
+ author: 'Your Name',
482
+ persistState: true, // Enable auto-persist
483
+ persistDebounce: 100, // Optional: debounce delay (default 100ms)
484
+ });
485
+
486
+ this.state = { count: 0, items: [] };
487
+ }
488
+
489
+ async onBackgroundService() {
490
+ // State is already restored automatically!
491
+ console.log('Restored count:', this.state.count);
492
+ }
493
+
494
+ handleIncrement() {
495
+ // Automatically saved to background service
496
+ this.setState({ count: this.state.count + 1 });
497
+ }
498
+ }
499
+ ```
500
+
501
+ #### Multi-Page Flows (Pending Operations)
502
+
503
+ For operations that span multiple pages (e.g., extract data on page A, fill form on page B), use pending operations:
504
+
505
+ ```typescript
506
+ class DataTransferApp extends WeaveBaseApp {
507
+ async handleTransferClick() {
508
+ // Save pending operation BEFORE navigation
509
+ await this.background?.setPendingOperation({
510
+ type: 'fill-form',
511
+ data: {
512
+ content: this.state.extractedContent,
513
+ targetForm: 'patient-notes'
514
+ },
515
+ targetUrl: 'https://target-system.com/form'
516
+ });
517
+
518
+ // Navigate to target page
519
+ window.open('https://target-system.com/form', '_self');
520
+ }
521
+
522
+ async onBackgroundService() {
523
+ // Check for pending operation after page loads
524
+ const pending = await this.background?.getPendingOperation();
525
+
526
+ if (pending?.type === 'fill-form') {
527
+ const pageUrl = await window.weaveDOM.getPageUrl();
528
+
529
+ // Verify we're on the right page
530
+ if (pageUrl.includes('target-system.com/form')) {
531
+ await this.fillForm(pending.data);
532
+ await this.background?.clearPendingOperation();
533
+ }
534
+ }
535
+ }
536
+ }
537
+ ```
538
+
539
+ #### State TTL
540
+
541
+ Persisted state expires after **5 minutes** by default. This prevents stale state from accumulating.
542
+
381
543
  ### Settings & Configuration
382
544
 
383
545
  Settings are injected from the Enterprise Console and displayed as auto-extracted form fields:
@@ -479,6 +641,9 @@ Check that selectors match the actual form structure. Use browser DevTools to in
479
641
  ### Button not injecting
480
642
  Verify the target selector exists before injection attempt. Check browser console for errors.
481
643
 
482
- ### State not persisting
483
- Use `this.weaveAPI.appData.*` methods to persist data to backend. In-memory state is lost on refresh.
644
+ ### State not persisting across page reloads
645
+ Enable `persistState: true` in your app config. State will automatically save on `setState()` and restore on page reload.
646
+
647
+ ### State not persisting long-term
648
+ Use `this.weaveAPI.appData.*` methods to persist data to backend. Background state only lasts 5 minutes.
484
649
 
@@ -0,0 +1,81 @@
1
+ /**
2
+ * WeaveBackgroundAPI
3
+ *
4
+ * Client-side API for Weave apps to interact with the background state service.
5
+ * Allows apps to persist state across page reloads and manage multi-page operations.
6
+ *
7
+ * Usage:
8
+ * ```typescript
9
+ * // Save state (survives page reload)
10
+ * await weaveAPI.background.saveState({ count: 5, items: [...] });
11
+ *
12
+ * // Load state after page reload
13
+ * const state = await weaveAPI.background.loadState();
14
+ *
15
+ * // Set pending operation before navigation
16
+ * await weaveAPI.background.setPendingOperation({
17
+ * type: 'fill-form',
18
+ * data: { content: '...' },
19
+ * targetUrl: 'https://example.com/form'
20
+ * });
21
+ *
22
+ * // Check for pending operation after navigation
23
+ * const pending = await weaveAPI.background.getPendingOperation();
24
+ * ```
25
+ */
26
+ /**
27
+ * Pending operation for multi-page flows
28
+ */
29
+ export interface PendingOperation {
30
+ type: string;
31
+ data: any;
32
+ targetUrl?: string;
33
+ }
34
+ /**
35
+ * WeaveBackgroundAPI class
36
+ *
37
+ * Provides methods for apps to interact with background state service.
38
+ * Each app instance gets its own API instance with its appId.
39
+ */
40
+ export declare class WeaveBackgroundAPI {
41
+ private appId;
42
+ constructor(appId: string);
43
+ /**
44
+ * Save app state to background service
45
+ * State survives page reloads within the browser session
46
+ *
47
+ * @param state - The state object to save
48
+ */
49
+ saveState(state: any): Promise<void>;
50
+ /**
51
+ * Load app state from background service
52
+ * Returns null if no state exists or if it has expired
53
+ *
54
+ * @returns The saved state or null
55
+ */
56
+ loadState<T = any>(): Promise<T | null>;
57
+ /**
58
+ * Clear app state from background service
59
+ */
60
+ clearState(): Promise<void>;
61
+ /**
62
+ * Set a pending operation for multi-page flows
63
+ * Use this before triggering navigation to save context
64
+ *
65
+ * @param operation - The operation to save
66
+ */
67
+ setPendingOperation(operation: PendingOperation): Promise<void>;
68
+ /**
69
+ * Get pending operation after navigation
70
+ * Returns null if no pending operation exists
71
+ *
72
+ * @returns The pending operation or null
73
+ */
74
+ getPendingOperation(): Promise<PendingOperation | null>;
75
+ /**
76
+ * Clear pending operation
77
+ * Call this after successfully handling the pending operation
78
+ */
79
+ clearPendingOperation(): Promise<void>;
80
+ }
81
+ //# sourceMappingURL=WeaveBackgroundAPI.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WeaveBackgroundAPI.d.ts","sourceRoot":"","sources":["../src/WeaveBackgroundAPI.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAWH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,GAAG,CAAC;IACV,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA4FD;;;;;GAKG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,KAAK,CAAS;gBAEV,KAAK,EAAE,MAAM;IAIzB;;;;;OAKG;IACG,SAAS,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAO1C;;;;;OAKG;IACG,SAAS,CAAC,CAAC,GAAG,GAAG,KAAK,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAO7C;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC;;;;;OAKG;IACG,mBAAmB,CAAC,SAAS,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAOrE;;;;;OAKG;IACG,mBAAmB,IAAI,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAO7D;;;OAGG;IACG,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;CAK7C"}
@@ -0,0 +1,165 @@
1
+ /**
2
+ * WeaveBackgroundAPI
3
+ *
4
+ * Client-side API for Weave apps to interact with the background state service.
5
+ * Allows apps to persist state across page reloads and manage multi-page operations.
6
+ *
7
+ * Usage:
8
+ * ```typescript
9
+ * // Save state (survives page reload)
10
+ * await weaveAPI.background.saveState({ count: 5, items: [...] });
11
+ *
12
+ * // Load state after page reload
13
+ * const state = await weaveAPI.background.loadState();
14
+ *
15
+ * // Set pending operation before navigation
16
+ * await weaveAPI.background.setPendingOperation({
17
+ * type: 'fill-form',
18
+ * data: { content: '...' },
19
+ * targetUrl: 'https://example.com/form'
20
+ * });
21
+ *
22
+ * // Check for pending operation after navigation
23
+ * const pending = await weaveAPI.background.getPendingOperation();
24
+ * ```
25
+ */
26
+ // Request timeout in ms
27
+ const REQUEST_TIMEOUT = 5000;
28
+ // Counter for unique request IDs
29
+ let requestIdCounter = 0;
30
+ // Pending requests waiting for response
31
+ const pendingRequests = new Map();
32
+ // Flag to track if listener is set up
33
+ let listenerInitialized = false;
34
+ /**
35
+ * Initialize the response listener (called once)
36
+ */
37
+ function initializeListener() {
38
+ if (listenerInitialized)
39
+ return;
40
+ listenerInitialized = true;
41
+ window.addEventListener('message', (event) => {
42
+ const message = event.data;
43
+ // Only handle background state responses
44
+ if (message?.type !== 'BG_STATE_RESPONSE')
45
+ return;
46
+ const pending = pendingRequests.get(message.requestId);
47
+ if (!pending)
48
+ return;
49
+ // Clear timeout and remove from pending
50
+ clearTimeout(pending.timeout);
51
+ pendingRequests.delete(message.requestId);
52
+ if (message.success) {
53
+ pending.resolve({
54
+ state: message.state,
55
+ operation: message.operation,
56
+ });
57
+ }
58
+ else {
59
+ pending.reject(new Error(message.error || 'Unknown error'));
60
+ }
61
+ });
62
+ }
63
+ /**
64
+ * Send a message to the background state service via content script
65
+ */
66
+ async function sendMessage(type, payload) {
67
+ // Initialize listener on first use
68
+ initializeListener();
69
+ const requestId = `bg-${++requestIdCounter}-${Date.now()}`;
70
+ return new Promise((resolve, reject) => {
71
+ // Set up timeout
72
+ const timeout = setTimeout(() => {
73
+ pendingRequests.delete(requestId);
74
+ reject(new Error(`Background state request timed out: ${type}`));
75
+ }, REQUEST_TIMEOUT);
76
+ // Store pending request
77
+ pendingRequests.set(requestId, { resolve, reject, timeout });
78
+ // Send message to content script (which forwards to background)
79
+ window.parent.postMessage({
80
+ type,
81
+ requestId,
82
+ payload,
83
+ }, '*');
84
+ });
85
+ }
86
+ /**
87
+ * WeaveBackgroundAPI class
88
+ *
89
+ * Provides methods for apps to interact with background state service.
90
+ * Each app instance gets its own API instance with its appId.
91
+ */
92
+ export class WeaveBackgroundAPI {
93
+ constructor(appId) {
94
+ this.appId = appId;
95
+ }
96
+ /**
97
+ * Save app state to background service
98
+ * State survives page reloads within the browser session
99
+ *
100
+ * @param state - The state object to save
101
+ */
102
+ async saveState(state) {
103
+ await sendMessage('BG_SAVE_STATE', {
104
+ appId: this.appId,
105
+ state,
106
+ });
107
+ }
108
+ /**
109
+ * Load app state from background service
110
+ * Returns null if no state exists or if it has expired
111
+ *
112
+ * @returns The saved state or null
113
+ */
114
+ async loadState() {
115
+ const response = await sendMessage('BG_LOAD_STATE', {
116
+ appId: this.appId,
117
+ });
118
+ return response.state;
119
+ }
120
+ /**
121
+ * Clear app state from background service
122
+ */
123
+ async clearState() {
124
+ await sendMessage('BG_CLEAR_STATE', {
125
+ appId: this.appId,
126
+ });
127
+ }
128
+ /**
129
+ * Set a pending operation for multi-page flows
130
+ * Use this before triggering navigation to save context
131
+ *
132
+ * @param operation - The operation to save
133
+ */
134
+ async setPendingOperation(operation) {
135
+ await sendMessage('BG_SET_PENDING_OP', {
136
+ appId: this.appId,
137
+ operation,
138
+ });
139
+ }
140
+ /**
141
+ * Get pending operation after navigation
142
+ * Returns null if no pending operation exists
143
+ *
144
+ * @returns The pending operation or null
145
+ */
146
+ async getPendingOperation() {
147
+ const response = await sendMessage('BG_GET_PENDING_OP', {
148
+ appId: this.appId,
149
+ });
150
+ return response.operation;
151
+ }
152
+ /**
153
+ * Clear pending operation
154
+ * Call this after successfully handling the pending operation
155
+ */
156
+ async clearPendingOperation() {
157
+ await sendMessage('BG_CLEAR_PENDING_OP', {
158
+ appId: this.appId,
159
+ });
160
+ }
161
+ }
162
+ // Export for global usage
163
+ if (typeof window !== 'undefined') {
164
+ window.WeaveBackgroundAPI = WeaveBackgroundAPI;
165
+ }
@@ -10,7 +10,6 @@
10
10
  * interface MyAppSettings {
11
11
  * apiKey: string;
12
12
  * endpoint: string;
13
- * autoSave?: boolean;
14
13
  * }
15
14
  *
16
15
  * interface MyAppState {
@@ -28,7 +27,9 @@
28
27
  * category: 'utility',
29
28
  * description: 'My custom app',
30
29
  * author: 'Your Name',
31
- * tags: ['custom']
30
+ * tags: ['custom'],
31
+ * // Enable auto-persist to survive page reloads
32
+ * persistState: true,
32
33
  * });
33
34
  *
34
35
  * // Settings are now type-safe!
@@ -47,7 +48,7 @@
47
48
  * }
48
49
  *
49
50
  * setupEventListeners() {
50
- * // setState is also type-safe
51
+ * // setState is also type-safe and auto-persists if enabled
51
52
  * this.setState({ count: this.state.count + 1 }); // ✅ Type-safe
52
53
  * }
53
54
  * }
@@ -55,6 +56,8 @@
55
56
  * customElements.define('my-app', MyApp);
56
57
  * ```
57
58
  */
59
+ import { WeaveBackgroundAPI } from './WeaveBackgroundAPI';
60
+ export type { PendingOperation } from './WeaveBackgroundAPI';
58
61
  /**
59
62
  * App metadata configuration
60
63
  */
@@ -73,6 +76,19 @@ export interface WeaveAppInfo {
73
76
  author: string;
74
77
  /** Additional tags for search and categorization */
75
78
  tags?: string[];
79
+ /**
80
+ * Enable automatic state persistence across page reloads.
81
+ * When true, state is automatically saved to background service on each setState() call.
82
+ * State is restored when the app initializes after a page reload.
83
+ * @default false
84
+ */
85
+ persistState?: boolean;
86
+ /**
87
+ * Debounce delay for state persistence in milliseconds.
88
+ * Prevents excessive saves when state changes rapidly.
89
+ * @default 100
90
+ */
91
+ persistDebounce?: number;
76
92
  }
77
93
  /**
78
94
  * Base class for Weave apps
@@ -90,11 +106,24 @@ export declare abstract class WeaveBaseApp<TSettings = Record<string, any>, TSta
90
106
  private _shadowRoot;
91
107
  /** App-specific API client instance (prevents app ID conflicts) */
92
108
  protected weaveAPI: any;
109
+ /** Background API for state persistence */
110
+ private _backgroundAPI;
111
+ /** Debounced persist function */
112
+ private _debouncedPersist;
113
+ /** Flag to track if state has been restored */
114
+ private _stateRestored;
115
+ /** App UUID from registry */
116
+ private _appUuid;
93
117
  /**
94
118
  * Creates a new Weave app
95
119
  * @param appInfo - App metadata configuration
96
120
  */
97
121
  constructor(appInfo: WeaveAppInfo);
122
+ /**
123
+ * Get the background API for state persistence and pending operations
124
+ * Use this for manual state management or multi-page flows
125
+ */
126
+ protected get background(): WeaveBackgroundAPI | null;
98
127
  /**
99
128
  * Get shadow root (override native property)
100
129
  */
@@ -159,8 +188,19 @@ export declare abstract class WeaveBaseApp<TSettings = Record<string, any>, TSta
159
188
  setAppConfig(_config: Record<string, any>): void;
160
189
  /**
161
190
  * Helper: Update app state
191
+ * If persistState is enabled, state is automatically saved to background service
162
192
  */
163
193
  protected setState(updates: Partial<TState>): void;
194
+ /**
195
+ * Persist current state to background service
196
+ * Called automatically when persistState is enabled
197
+ */
198
+ private _persistState;
199
+ /**
200
+ * Restore state from background service
201
+ * Called by BackgroundAppManager during initialization
202
+ */
203
+ restoreState(): Promise<boolean>;
164
204
  /**
165
205
  * Helper: Render HTML into shadow root
166
206
  */
@@ -1 +1 @@
1
- {"version":3,"file":"WeaveBaseApp.d.ts","sourceRoot":"","sources":["../src/WeaveBaseApp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AAEH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,EAAE,EAAE,MAAM,CAAC;IACX,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;GAIG;AACH,8BAAsB,YAAY,CAChC,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/B,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAC5B,SAAQ,WAAW;IACnB,mBAAmB;IACnB,SAAS,CAAC,OAAO,EAAE,YAAY,CAAC;IAEhC,2CAA2C;IAC3C,SAAS,CAAC,WAAW,CAAC,EAAE,SAAS,CAAC;IAElC,gDAAgD;IAChD,SAAS,CAAC,KAAK,EAAE,MAAM,CAAgB;IAEvC,sCAAsC;IACtC,OAAO,CAAC,WAAW,CAAa;IAEhC,mEAAmE;IACnE,SAAS,CAAC,QAAQ,EAAE,GAAG,CAAC;IAExB;;;OAGG;gBACS,OAAO,EAAE,YAAY;IAyCjC;;OAEG;IACH,IAAI,UAAU,IAAI,UAAU,CAE3B;IAED;;;;OAIG;IACH,SAAS,CAAC,mBAAmB,CAAC,IAAI,IAAI;IAEtC;;;;OAIG;IACH,SAAS,CAAC,WAAW,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAEzC;;;OAGG;IACH,SAAS,CAAC,MAAM,IAAI,IAAI;IAIxB;;OAEG;IACH,SAAS,CAAC,mBAAmB,IAAI,IAAI;IAIrC;;;OAGG;IACI,2BAA2B,IAAI,IAAI;IAO1C;;;OAGG;IACI,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAMzC;;;OAGG;IACH,iBAAiB,IAAI,IAAI;IAoBzB;;OAEG;IACH,oBAAoB,IAAI,IAAI;IAI5B;;;OAGG;IACH,SAAS,CAAC,OAAO,IAAI,IAAI;IAIzB;;OAEG;IACI,UAAU,IAAI,YAAY;IAIjC;;OAEG;IACI,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAOzC;;;OAGG;IAEI,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAIvD;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI;IAIlD;;OAEG;IACH,SAAS,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAQxC;;OAEG;IACH,SAAS,CAAC,KAAK,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI;IAIxE;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC;CAGjF"}
1
+ {"version":3,"file":"WeaveBaseApp.d.ts","sourceRoot":"","sources":["../src/WeaveBaseApp.ts"],"names":[],"mappings":"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"}
@@ -10,7 +10,6 @@
10
10
  * interface MyAppSettings {
11
11
  * apiKey: string;
12
12
  * endpoint: string;
13
- * autoSave?: boolean;
14
13
  * }
15
14
  *
16
15
  * interface MyAppState {
@@ -28,7 +27,9 @@
28
27
  * category: 'utility',
29
28
  * description: 'My custom app',
30
29
  * author: 'Your Name',
31
- * tags: ['custom']
30
+ * tags: ['custom'],
31
+ * // Enable auto-persist to survive page reloads
32
+ * persistState: true,
32
33
  * });
33
34
  *
34
35
  * // Settings are now type-safe!
@@ -47,7 +48,7 @@
47
48
  * }
48
49
  *
49
50
  * setupEventListeners() {
50
- * // setState is also type-safe
51
+ * // setState is also type-safe and auto-persists if enabled
51
52
  * this.setState({ count: this.state.count + 1 }); // ✅ Type-safe
52
53
  * }
53
54
  * }
@@ -55,6 +56,22 @@
55
56
  * customElements.define('my-app', MyApp);
56
57
  * ```
57
58
  */
59
+ import { WeaveBackgroundAPI } from './WeaveBackgroundAPI';
60
+ /**
61
+ * Simple debounce utility
62
+ */
63
+ function debounce(fn, delay) {
64
+ let timeoutId = null;
65
+ return (...args) => {
66
+ if (timeoutId) {
67
+ clearTimeout(timeoutId);
68
+ }
69
+ timeoutId = setTimeout(() => {
70
+ fn(...args);
71
+ timeoutId = null;
72
+ }, delay);
73
+ };
74
+ }
58
75
  /**
59
76
  * Base class for Weave apps
60
77
  * @template TSettings - Type for app settings, defaults to an empty object
@@ -69,6 +86,14 @@ export class WeaveBaseApp extends HTMLElement {
69
86
  super();
70
87
  /** App-specific state (override in subclass) */
71
88
  this.state = {};
89
+ /** Background API for state persistence */
90
+ this._backgroundAPI = null;
91
+ /** Debounced persist function */
92
+ this._debouncedPersist = null;
93
+ /** Flag to track if state has been restored */
94
+ this._stateRestored = false;
95
+ /** App UUID from registry */
96
+ this._appUuid = null;
72
97
  // Validate required fields
73
98
  if (!appInfo.id || !appInfo.name || !appInfo.version) {
74
99
  throw new Error('WeaveBaseApp: id, name, and version are required');
@@ -85,14 +110,24 @@ export class WeaveBaseApp extends HTMLElement {
85
110
  if (registryData?.settings) {
86
111
  this.appSettings = registryData.settings;
87
112
  }
113
+ // Store app UUID for background API
114
+ this._appUuid = registryData?.uuid || null;
88
115
  // Set app UUID on the global API client so it's included in all requests
89
116
  // This allows background services to make API calls without the app being open
90
117
  if (window.weaveAPI) {
91
- const appUuid = registryData?.uuid;
92
- if (appUuid) {
118
+ if (this._appUuid) {
93
119
  // Create a new API client instance for THIS app
94
120
  this.weaveAPI = new window.WeaveAPIClient();
95
- this.weaveAPI.setAppId(appUuid);
121
+ this.weaveAPI.setAppId(this._appUuid);
122
+ // Create background API instance for state persistence
123
+ this._backgroundAPI = new WeaveBackgroundAPI(this._appUuid);
124
+ // Set up debounced persist if enabled
125
+ if (appInfo.persistState) {
126
+ const delay = appInfo.persistDebounce ?? 100;
127
+ this._debouncedPersist = debounce(() => {
128
+ this._persistState();
129
+ }, delay);
130
+ }
96
131
  }
97
132
  else {
98
133
  console.warn('⚠️ App UUID not found in registry - API calls may fail. App:', appInfo.id);
@@ -101,6 +136,13 @@ export class WeaveBaseApp extends HTMLElement {
101
136
  }
102
137
  }
103
138
  }
139
+ /**
140
+ * Get the background API for state persistence and pending operations
141
+ * Use this for manual state management or multi-page flows
142
+ */
143
+ get background() {
144
+ return this._backgroundAPI;
145
+ }
104
146
  /**
105
147
  * Get shadow root (override native property)
106
148
  */
@@ -195,9 +237,53 @@ export class WeaveBaseApp extends HTMLElement {
195
237
  }
196
238
  /**
197
239
  * Helper: Update app state
240
+ * If persistState is enabled, state is automatically saved to background service
198
241
  */
199
242
  setState(updates) {
200
243
  this.state = { ...this.state, ...updates };
244
+ // Auto-persist if enabled
245
+ if (this.appInfo.persistState && this._debouncedPersist) {
246
+ this._debouncedPersist();
247
+ }
248
+ }
249
+ /**
250
+ * Persist current state to background service
251
+ * Called automatically when persistState is enabled
252
+ */
253
+ async _persistState() {
254
+ if (!this._backgroundAPI)
255
+ return;
256
+ try {
257
+ await this._backgroundAPI.saveState(this.state);
258
+ }
259
+ catch (error) {
260
+ console.error('❌ Failed to persist state:', error);
261
+ }
262
+ }
263
+ /**
264
+ * Restore state from background service
265
+ * Called by BackgroundAppManager during initialization
266
+ */
267
+ async restoreState() {
268
+ if (!this._backgroundAPI || !this.appInfo.persistState) {
269
+ return false;
270
+ }
271
+ if (this._stateRestored) {
272
+ return true; // Already restored
273
+ }
274
+ try {
275
+ const savedState = await this._backgroundAPI.loadState();
276
+ if (savedState) {
277
+ this.state = { ...this.state, ...savedState };
278
+ this._stateRestored = true;
279
+ console.log('✅ State restored for app:', this.appInfo.id);
280
+ return true;
281
+ }
282
+ }
283
+ catch (error) {
284
+ console.error('❌ Failed to restore state:', error);
285
+ }
286
+ return false;
201
287
  }
202
288
  /**
203
289
  * Helper: Render HTML into shadow root
package/dist/global.d.ts CHANGED
@@ -7,6 +7,7 @@
7
7
  import { WeaveBaseApp } from './WeaveBaseApp';
8
8
  import { WeaveDOMAPI } from './WeaveDOMAPI';
9
9
  import { WeaveAPIClient } from './WeaveAPIClient';
10
+ import { WeaveBackgroundAPI } from './WeaveBackgroundAPI';
10
11
  declare global {
11
12
  interface Window {
12
13
  WeaveBaseApp: typeof WeaveBaseApp;
@@ -14,6 +15,7 @@ declare global {
14
15
  weaveDOM: WeaveDOMAPI;
15
16
  WeaveAPIClient: typeof WeaveAPIClient;
16
17
  weaveAPI: WeaveAPIClient;
18
+ WeaveBackgroundAPI: typeof WeaveBackgroundAPI;
17
19
  }
18
20
  }
19
21
  //# sourceMappingURL=global.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"global.d.ts","sourceRoot":"","sources":["../src/global.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAIlD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,YAAY,EAAE,OAAO,YAAY,CAAC;QAClC,WAAW,EAAE,OAAO,WAAW,CAAC;QAChC,QAAQ,EAAE,WAAW,CAAC;QACtB,cAAc,EAAE,OAAO,cAAc,CAAC;QACtC,QAAQ,EAAE,cAAc,CAAC;KAAG;CAC/B"}
1
+ {"version":3,"file":"global.d.ts","sourceRoot":"","sources":["../src/global.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAG1D,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,YAAY,EAAE,OAAO,YAAY,CAAC;QAClC,WAAW,EAAE,OAAO,WAAW,CAAC;QAChC,QAAQ,EAAE,WAAW,CAAC;QACtB,cAAc,EAAE,OAAO,cAAc,CAAC;QACtC,QAAQ,EAAE,cAAc,CAAC;QACzB,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;KAAG;CACpD"}
package/dist/global.js CHANGED
@@ -9,9 +9,11 @@ import { WeaveDOMAPI } from './WeaveDOMAPI';
9
9
  import weavekDOM from './WeaveDOMAPI';
10
10
  import { WeaveAPIClient } from './WeaveAPIClient';
11
11
  import weaveAPI from './WeaveAPIClient';
12
+ import { WeaveBackgroundAPI } from './WeaveBackgroundAPI';
12
13
  // Make SDK available globally
13
14
  window.WeaveBaseApp = WeaveBaseApp;
14
15
  window.WeaveDOMAPI = WeaveDOMAPI;
15
16
  window.weaveDOM = weavekDOM;
16
17
  window.WeaveAPIClient = WeaveAPIClient;
17
18
  window.weaveAPI = weaveAPI;
19
+ window.WeaveBackgroundAPI = WeaveBackgroundAPI;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weave-apps/sdk",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "SDK for building Weave Micro Apps",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -15,7 +15,7 @@
15
15
  },
16
16
  "scripts": {
17
17
  "copy-sdk": "node scripts/copy-sdk-files.js",
18
- "prebuild": "npm run copy-sdk",
18
+ "prebuild": "npm run copy-sdk && npx doctoc README.md",
19
19
  "build": "tsc",
20
20
  "dev": "tsc --watch",
21
21
  "prepare": "npm run build",
@@ -16,6 +16,7 @@ const filesToCopy = [
16
16
  'WeaveBaseApp.ts',
17
17
  'WeaveDOMAPI.ts',
18
18
  'WeaveAPIClient.ts',
19
+ 'WeaveBackgroundAPI.ts',
19
20
  'global.ts',
20
21
  'app-template.js',
21
22
  'README.md',
@@ -1083,12 +1083,14 @@ customElements.define('smart-form-assistant', SmartFormAssistant);
1083
1083
  ### Key Takeaways
1084
1084
 
1085
1085
  1. **Single Instance Architecture**: One app instance serves both background and foreground
1086
- 2. **Persistent State**: `this.state` persists across open/close cycles
1087
- 3. **Background Service**: `onBackgroundService()` runs immediately, before DOM attachment
1088
- 4. **URL Monitoring**: `onUrlChange()` detects SPA navigation automatically
1089
- 5. **Shared Context**: Background and foreground share the same instance and state
1090
- 6. **DOM Check**: Use `this.isConnected` to check if app is attached to DOM
1091
- 7. **No User Interaction Required**: Apps can run and inject UI automatically
1086
+ 2. **Persistent State**: `this.state` persists across open/close cycles (within session)
1087
+ 3. **Page Reload Survival**: Enable `persistState: true` to survive page reloads
1088
+ 4. **Multi-Page Flows**: Use `this.background?.setPendingOperation()` for cross-page operations
1089
+ 5. **Background Service**: `onBackgroundService()` runs immediately, before DOM attachment
1090
+ 6. **URL Monitoring**: `onUrlChange()` detects SPA navigation automatically
1091
+ 7. **Shared Context**: Background and foreground share the same instance and state
1092
+ 8. **DOM Check**: Use `this.isConnected` to check if app is attached to DOM
1093
+ 9. **No User Interaction Required**: Apps can run and inject UI automatically
1092
1094
 
1093
1095
  ### Helper Methods (Available in `this`)
1094
1096
 
@@ -1102,7 +1104,7 @@ this.query<T>(selector: string): T | null
1102
1104
  // Query all elements in shadow root
1103
1105
  this.queryAll<T>(selector: string): NodeListOf<T>
1104
1106
 
1105
- // Update app state
1107
+ // Update app state (auto-persists if persistState: true)
1106
1108
  this.setState(updates: object): void
1107
1109
 
1108
1110
  // Access current state
@@ -1113,8 +1115,263 @@ this.appInfo: WeaveAppInfo
1113
1115
 
1114
1116
  // Access shadow root
1115
1117
  this.shadowRoot: ShadowRoot
1118
+
1119
+ // Access background API for state persistence
1120
+ this.background: WeaveBackgroundAPI | null
1116
1121
  ```
1117
1122
 
1123
+ ## State Persistence (Survive Page Reloads)
1124
+
1125
+ ### Overview
1126
+
1127
+ Apps can persist state across **page reloads** using the background state service. This is different from `weaveAPI.appData` which persists to the backend database - background state is stored in the browser extension's memory and survives page navigations within the same browser session.
1128
+
1129
+ **Use Cases:**
1130
+ - 🔄 **Survive page reloads** - State persists when user refreshes the page
1131
+ - 📋 **Multi-page flows** - Extract data on page A, fill form on page B
1132
+ - 🔀 **Handle redirects** - Continue operation after OAuth redirect or form submission
1133
+ - 💾 **Temporary state** - Store data that doesn't need long-term persistence
1134
+
1135
+ ### Auto-Persist (Recommended)
1136
+
1137
+ The simplest approach is to enable `persistState: true` in your app config. State is automatically:
1138
+ - **Saved** on every `setState()` call (debounced)
1139
+ - **Restored** when the app initializes after a page reload
1140
+
1141
+ ```typescript
1142
+ class PersistentCounterApp extends WeaveBaseApp {
1143
+ constructor() {
1144
+ super({
1145
+ id: 'persistent-counter',
1146
+ name: 'Persistent Counter',
1147
+ version: '1.0.0',
1148
+ category: 'utility',
1149
+ description: 'Counter that survives page reloads',
1150
+ author: 'Your Name',
1151
+
1152
+ // ✅ Enable auto-persist
1153
+ persistState: true,
1154
+
1155
+ // Optional: debounce delay in ms (default: 100)
1156
+ persistDebounce: 100,
1157
+ });
1158
+
1159
+ this.state = {
1160
+ count: 0,
1161
+ lastUpdated: null
1162
+ };
1163
+ }
1164
+
1165
+ async onBackgroundService() {
1166
+ // ✅ State is already restored automatically!
1167
+ console.log('Restored count:', this.state.count);
1168
+ console.log('Last updated:', this.state.lastUpdated);
1169
+ }
1170
+
1171
+ handleIncrement() {
1172
+ // ✅ Automatically saved to background service
1173
+ this.setState({
1174
+ count: this.state.count + 1,
1175
+ lastUpdated: new Date().toISOString()
1176
+ });
1177
+
1178
+ if (this.isConnected) {
1179
+ this.render();
1180
+ }
1181
+ }
1182
+
1183
+ render() {
1184
+ this.renderHTML(`
1185
+ <div class="p-4">
1186
+ <p>Count: <strong>${this.state.count}</strong></p>
1187
+ <p>Last updated: ${this.state.lastUpdated || 'Never'}</p>
1188
+ <button id="increment" class="bg-blue-500 text-white px-4 py-2 rounded">
1189
+ Increment
1190
+ </button>
1191
+ <p class="text-gray-500 text-sm mt-4">
1192
+ Refresh the page - count will persist!
1193
+ </p>
1194
+ </div>
1195
+ `);
1196
+ }
1197
+
1198
+ setupEventListeners() {
1199
+ this.query('#increment')?.addEventListener('click', () => {
1200
+ this.handleIncrement();
1201
+ });
1202
+ }
1203
+ }
1204
+
1205
+ customElements.define('persistent-counter', PersistentCounterApp);
1206
+ ```
1207
+
1208
+ ### Manual State Management
1209
+
1210
+ For more control, use the `this.background` API directly:
1211
+
1212
+ ```typescript
1213
+ class ManualStateApp extends WeaveBaseApp {
1214
+ constructor() {
1215
+ super({
1216
+ id: 'manual-state-app',
1217
+ name: 'Manual State App',
1218
+ version: '1.0.0',
1219
+ category: 'utility',
1220
+ description: 'Manual state management',
1221
+ author: 'Your Name',
1222
+ // Note: persistState is false (default)
1223
+ });
1224
+
1225
+ this.state = { data: null };
1226
+ }
1227
+
1228
+ async saveImportantData(data: any) {
1229
+ // Manually save state
1230
+ await this.background?.saveState({ importantData: data });
1231
+ }
1232
+
1233
+ async loadSavedData() {
1234
+ // Manually load state
1235
+ const saved = await this.background?.loadState();
1236
+ if (saved?.importantData) {
1237
+ this.state.data = saved.importantData;
1238
+ }
1239
+ }
1240
+
1241
+ async clearSavedData() {
1242
+ // Clear saved state
1243
+ await this.background?.clearState();
1244
+ }
1245
+ }
1246
+ ```
1247
+
1248
+ ### Multi-Page Flows (Pending Operations)
1249
+
1250
+ For operations that span multiple pages, use **pending operations**. This is perfect for:
1251
+ - Extract data on page A → Fill form on page B
1252
+ - Click button → Handle redirect → Continue operation
1253
+ - OAuth flows → Return and continue
1254
+
1255
+ ```typescript
1256
+ class DataTransferApp extends WeaveBaseApp {
1257
+ constructor() {
1258
+ super({
1259
+ id: 'data-transfer-app',
1260
+ name: 'Data Transfer App',
1261
+ version: '1.0.0',
1262
+ category: 'integration',
1263
+ description: 'Transfer data between pages',
1264
+ author: 'Your Name',
1265
+ });
1266
+
1267
+ this.state = {
1268
+ extractedContent: null,
1269
+ transferInProgress: false
1270
+ };
1271
+ }
1272
+
1273
+ async onBackgroundService() {
1274
+ // Check for pending operation after page loads
1275
+ const pending = await this.background?.getPendingOperation();
1276
+
1277
+ if (pending?.type === 'fill-form') {
1278
+ const pageUrl = await window.weaveDOM.getPageUrl();
1279
+
1280
+ // Verify we're on the target page
1281
+ if (pageUrl.includes(pending.targetUrl || '')) {
1282
+ console.log('✅ On target page, filling form...');
1283
+ await this.fillFormWithData(pending.data);
1284
+ await this.background?.clearPendingOperation();
1285
+ }
1286
+ }
1287
+ }
1288
+
1289
+ async handleTransferClick() {
1290
+ if (!this.state.extractedContent) {
1291
+ console.error('No content to transfer');
1292
+ return;
1293
+ }
1294
+
1295
+ // Save pending operation BEFORE navigation
1296
+ await this.background?.setPendingOperation({
1297
+ type: 'fill-form',
1298
+ data: {
1299
+ content: this.state.extractedContent,
1300
+ timestamp: Date.now()
1301
+ },
1302
+ targetUrl: 'target-system.com/form'
1303
+ });
1304
+
1305
+ // Navigate to target page
1306
+ window.open('https://target-system.com/form', '_self');
1307
+ // Page will reload, but pending operation is saved!
1308
+ }
1309
+
1310
+ async fillFormWithData(data: any) {
1311
+ // Fill form fields with transferred data
1312
+ await window.weaveDOM.setFormFieldValue('#notes', data.content);
1313
+ console.log('✅ Form filled with transferred data');
1314
+ }
1315
+ }
1316
+
1317
+ customElements.define('data-transfer-app', DataTransferApp);
1318
+ ```
1319
+
1320
+ ### Pending Operation Structure
1321
+
1322
+ ```typescript
1323
+ interface PendingOperation {
1324
+ type: string; // Operation type (e.g., 'fill-form', 'continue-auth')
1325
+ data: any; // Operation-specific data
1326
+ targetUrl?: string; // Optional: URL where operation should resume
1327
+ }
1328
+ ```
1329
+
1330
+ ### Background API Reference
1331
+
1332
+ ```typescript
1333
+ // Available via this.background
1334
+
1335
+ // Save state (survives page reload)
1336
+ await this.background?.saveState(state: any): Promise<void>
1337
+
1338
+ // Load state
1339
+ await this.background?.loadState<T>(): Promise<T | null>
1340
+
1341
+ // Clear state
1342
+ await this.background?.clearState(): Promise<void>
1343
+
1344
+ // Set pending operation (for multi-page flows)
1345
+ await this.background?.setPendingOperation(op: PendingOperation): Promise<void>
1346
+
1347
+ // Get pending operation
1348
+ await this.background?.getPendingOperation(): Promise<PendingOperation | null>
1349
+
1350
+ // Clear pending operation
1351
+ await this.background?.clearPendingOperation(): Promise<void>
1352
+ ```
1353
+
1354
+ ### State TTL (Time To Live)
1355
+
1356
+ Persisted state expires after **5 minutes** by default. This prevents stale state from accumulating.
1357
+
1358
+ For long-term persistence, use `this.weaveAPI.appData.*` methods instead.
1359
+
1360
+ ### Best Practices
1361
+
1362
+ #### ✅ DO:
1363
+ - Use `persistState: true` for simple state persistence
1364
+ - Use pending operations for multi-page flows
1365
+ - Clear pending operations after handling them
1366
+ - Check `this.background` exists before calling methods
1367
+ - Use background state for temporary/session data
1368
+
1369
+ #### ❌ DON'T:
1370
+ - Don't use background state for long-term data (use `weaveAPI.appData`)
1371
+ - Don't store sensitive data in background state
1372
+ - Don't forget to clear pending operations
1373
+ - Don't assume state will persist forever (5 min TTL)
1374
+
1118
1375
  ## Weave Backend API
1119
1376
 
1120
1377
  ### ⚠️ CRITICAL: API Access Restrictions