@weave-apps/sdk 0.9.0 → 0.11.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.
Files changed (37) hide show
  1. package/dist/WeaveAPIClient.d.ts +49 -0
  2. package/dist/WeaveAPIClient.d.ts.map +1 -1
  3. package/dist/WeaveDOMAPI.d.ts +66 -0
  4. package/dist/WeaveDOMAPI.d.ts.map +1 -1
  5. package/dist/WeaveDOMAPI.js +51 -0
  6. package/dist/apis/api-mono/services/interfaces/WorkflowsTriggersWorkflowCompanyIdPostPost.d.ts +224 -0
  7. package/dist/apis/api-mono/services/interfaces/WorkflowsTriggersWorkflowCompanyIdPostPost.d.ts.map +1 -0
  8. package/dist/apis/api-mono/services/interfaces/WorkflowsTriggersWorkflowCompanyIdPostPost.js +66 -0
  9. package/dist/app-sdk/src/WeaveAPIClient.d.ts +370 -0
  10. package/dist/app-sdk/src/WeaveAPIClient.d.ts.map +1 -0
  11. package/dist/app-sdk/src/WeaveAPIClient.js +361 -0
  12. package/dist/app-sdk/src/WeaveAppInstanceAPI.d.ts +237 -0
  13. package/dist/app-sdk/src/WeaveAppInstanceAPI.d.ts.map +1 -0
  14. package/dist/app-sdk/src/WeaveAppInstanceAPI.js +395 -0
  15. package/dist/app-sdk/src/WeaveBackgroundAPI.d.ts +81 -0
  16. package/dist/app-sdk/src/WeaveBackgroundAPI.d.ts.map +1 -0
  17. package/dist/app-sdk/src/WeaveBackgroundAPI.js +165 -0
  18. package/dist/app-sdk/src/WeaveBaseApp.d.ts +318 -0
  19. package/dist/app-sdk/src/WeaveBaseApp.d.ts.map +1 -0
  20. package/dist/app-sdk/src/WeaveBaseApp.js +434 -0
  21. package/dist/app-sdk/src/WeaveCronAPI.d.ts +68 -0
  22. package/dist/app-sdk/src/WeaveCronAPI.d.ts.map +1 -0
  23. package/dist/app-sdk/src/WeaveCronAPI.js +172 -0
  24. package/dist/app-sdk/src/WeaveDOMAPI.d.ts +593 -0
  25. package/dist/app-sdk/src/WeaveDOMAPI.d.ts.map +1 -0
  26. package/dist/app-sdk/src/WeaveDOMAPI.js +774 -0
  27. package/dist/app-sdk/src/global.d.ts +25 -0
  28. package/dist/app-sdk/src/global.d.ts.map +1 -0
  29. package/dist/app-sdk/src/global.js +23 -0
  30. package/dist/app-sdk/src/index.d.ts +14 -0
  31. package/dist/app-sdk/src/index.d.ts.map +1 -0
  32. package/dist/app-sdk/src/index.js +14 -0
  33. package/dist/index.d.ts +1 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +1 -1
  36. package/package.json +3 -3
  37. package/templates/WEAVE_SPEC.md +417 -1
@@ -0,0 +1,434 @@
1
+ /**
2
+ * Weave Base App
3
+ *
4
+ * Base class for all Weave apps. Provides common functionality and lifecycle methods.
5
+ * Third-party developers should extend this class instead of HTMLElement directly.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * // Define your settings and state types
10
+ * interface MyAppSettings {
11
+ * apiKey: string;
12
+ * endpoint: string;
13
+ * }
14
+ *
15
+ * interface MyAppState {
16
+ * count: number;
17
+ * isLoading: boolean;
18
+ * data: any[];
19
+ * }
20
+ *
21
+ * class MyApp extends WeaveBaseApp<MyAppSettings, MyAppState> {
22
+ * constructor() {
23
+ * super({
24
+ * id: 'my-app',
25
+ * name: 'My App',
26
+ * version: '1.0.0',
27
+ * category: 'utility',
28
+ * description: 'My custom app',
29
+ * author: 'Your Name',
30
+ * tags: ['custom'],
31
+ * // Enable auto-persist to survive page reloads
32
+ * persistState: true,
33
+ * });
34
+ *
35
+ * // Settings are now type-safe!
36
+ * const apiKey = this.appSettings?.apiKey; // ✅ TypeScript knows this exists
37
+ *
38
+ * // Initialize state with proper typing
39
+ * this.state = {
40
+ * count: 0,
41
+ * isLoading: false,
42
+ * data: []
43
+ * };
44
+ * }
45
+ *
46
+ * render() {
47
+ * return `<div>My App Content</div>`;
48
+ * }
49
+ *
50
+ * setupEventListeners() {
51
+ * // setState is also type-safe and auto-persists if enabled
52
+ * this.setState({ count: this.state.count + 1 }); // ✅ Type-safe
53
+ * }
54
+ * }
55
+ *
56
+ * customElements.define('my-app', MyApp);
57
+ * ```
58
+ */
59
+ /**
60
+ * Simple debounce utility
61
+ */
62
+ function debounce(fn, delay) {
63
+ let timeoutId = null;
64
+ return (...args) => {
65
+ if (timeoutId) {
66
+ clearTimeout(timeoutId);
67
+ }
68
+ timeoutId = setTimeout(() => {
69
+ fn(...args);
70
+ timeoutId = null;
71
+ }, delay);
72
+ };
73
+ }
74
+ /**
75
+ * Base class for Weave apps
76
+ * @template TSettings - Type for app settings, defaults to an empty object
77
+ * @template TState - Type for app state, defaults to an empty object
78
+ */
79
+ export class WeaveBaseApp extends HTMLElement {
80
+ /**
81
+ * Creates a new Weave app
82
+ * @param appInfo - App metadata configuration
83
+ */
84
+ constructor(appInfo) {
85
+ super();
86
+ /** App-specific state (override in subclass) */
87
+ this.state = {};
88
+ /** Background API for state persistence */
89
+ this._backgroundAPI = null;
90
+ /** Cron API for scheduled tasks */
91
+ this._cronAPI = null;
92
+ /** App Instance API for cross-tab communication */
93
+ this._instancesAPI = null;
94
+ /** Debounced persist function */
95
+ this._debouncedPersist = null;
96
+ /** Flag to track if state has been restored */
97
+ this._stateRestored = false;
98
+ /** App UUID from registry */
99
+ this._appUuid = null;
100
+ /** User's company role (e.g., 'admin', 'member') */
101
+ this.companyRole = null;
102
+ /** Current user object */
103
+ this.currentUser = null;
104
+ /** Current company object */
105
+ this.currentCompany = null;
106
+ // Validate required fields
107
+ if (!appInfo.id || !appInfo.name || !appInfo.version) {
108
+ throw new Error('WeaveBaseApp: id, name, and version are required');
109
+ }
110
+ this.appInfo = appInfo;
111
+ // Create shadow DOM for style isolation and store reference
112
+ this._shadowRoot = this.attachShadow({ mode: 'open' });
113
+ // Read UUID and settings from global registry (set by BackgroundAppManager before element creation)
114
+ // This allows the constructor to access both UUID and settings immediately
115
+ const registry = window.__weaveAppRegistry;
116
+ const tagName = this.tagName.toLowerCase();
117
+ const registryData = registry?.[tagName];
118
+ // Set app settings if available in registry
119
+ if (registryData?.settings) {
120
+ this.appSettings = registryData.settings;
121
+ }
122
+ // Store app UUID for background API
123
+ this._appUuid = registryData?.uuid || null;
124
+ // Store user context for conditional rendering and personalization
125
+ this.currentUser = registryData?.currentUser || null;
126
+ this.currentCompany = registryData?.currentCompany || null;
127
+ this.companyRole = registryData?.companyRole || null;
128
+ // Set app UUID on the global API client so it's included in all requests
129
+ // This allows background services to make API calls without the app being open
130
+ if (window.weaveAPI) {
131
+ if (this._appUuid) {
132
+ // Create a new API client instance for THIS app
133
+ this.weaveAPI = new window.WeaveAPIClient();
134
+ this.weaveAPI.setAppId(this._appUuid);
135
+ // Create background API instance for state persistence
136
+ if (window.WeaveBackgroundAPI) {
137
+ this._backgroundAPI = new window.WeaveBackgroundAPI(this._appUuid);
138
+ }
139
+ // Create cron API instance for scheduled tasks
140
+ if (window.WeaveCronAPI) {
141
+ this._cronAPI = new window.WeaveCronAPI(this._appUuid);
142
+ this._cronAPI.initialize();
143
+ }
144
+ // Create app instance API for cross-tab communication
145
+ if (window.WeaveAppInstanceAPI) {
146
+ this._instancesAPI = new window.WeaveAppInstanceAPI(this._appUuid);
147
+ }
148
+ // Set up debounced persist if enabled
149
+ if (appInfo.persistState) {
150
+ const delay = appInfo.persistDebounce ?? 100;
151
+ this._debouncedPersist = debounce(() => {
152
+ this._persistState();
153
+ }, delay);
154
+ }
155
+ }
156
+ else {
157
+ console.warn('⚠️ App UUID not found in registry - API calls may fail. App:', appInfo.id);
158
+ // Fallback to global API client (will have wrong app ID, but better than nothing)
159
+ this.weaveAPI = window.weaveAPI;
160
+ }
161
+ }
162
+ else {
163
+ console.warn('⚠️ window.weaveAPI not available - API calls will fail. App:', appInfo.id);
164
+ }
165
+ }
166
+ /**
167
+ * Get the background API for state persistence and pending operations
168
+ * Use this for manual state management or multi-page flows
169
+ */
170
+ get background() {
171
+ return this._backgroundAPI;
172
+ }
173
+ /**
174
+ * Get the cron API for scheduled tasks
175
+ */
176
+ get cron() {
177
+ return this._cronAPI;
178
+ }
179
+ /**
180
+ * Get the app instance API for cross-tab communication
181
+ *
182
+ * Use this to:
183
+ * - Discover other instances of your app across browser tabs
184
+ * - Claim/release controller status (for automation coordination)
185
+ * - Send messages between instances
186
+ * - Listen for instance changes
187
+ *
188
+ * @example
189
+ * // Register this instance and check for others
190
+ * const instances = await this.instances.register();
191
+ * if (instances.length > 1) {
192
+ * // Other instances exist
193
+ * }
194
+ *
195
+ * // Claim controller status
196
+ * const claimed = await this.instances.claimController();
197
+ *
198
+ * // Listen for messages from other instances
199
+ * this.instances.onMessage((msg) => {
200
+ * if (msg.type === 'YIELD_CONTROL') {
201
+ * // Another tab wants control
202
+ * }
203
+ * });
204
+ */
205
+ get instances() {
206
+ return this._instancesAPI;
207
+ }
208
+ /**
209
+ * Register a cron job with a callback
210
+ *
211
+ * @param cronExpression - Standard cron expression (5 or 6 fields)
212
+ * - 5 fields: minute hour dayOfMonth month dayOfWeek
213
+ * - 6 fields: second minute hour dayOfMonth month dayOfWeek
214
+ * @param callback - Function to call when cron triggers
215
+ * @param jobName - Optional unique name for the job
216
+ * @returns Job ID if successful, null otherwise
217
+ *
218
+ * @example
219
+ * // Every second
220
+ * this.cronTab('* * * * * *', this.handleEverySecond);
221
+ *
222
+ * // Every minute
223
+ * this.cronTab('* * * * *', this.handleEveryMinute);
224
+ *
225
+ * // Every 5 seconds
226
+ * this.cronTab('0/5 * * * * *', this.handleEvery5Seconds);
227
+ *
228
+ * // Every hour at minute 0
229
+ * this.cronTab('0 * * * *', this.handleEveryHour);
230
+ */
231
+ async cronTab(cronExpression, callback, jobName) {
232
+ if (!this._cronAPI) {
233
+ console.error('Cron API not available - app UUID may be missing');
234
+ return null;
235
+ }
236
+ // Bind callback to this instance
237
+ const boundCallback = callback.bind(this);
238
+ return this._cronAPI.register(cronExpression, boundCallback, jobName);
239
+ }
240
+ /**
241
+ * Unregister a cron job by name
242
+ */
243
+ async cronUnregister(jobName) {
244
+ if (!this._cronAPI)
245
+ return false;
246
+ return this._cronAPI.unregister(jobName);
247
+ }
248
+ /**
249
+ * Unregister all cron jobs for this app
250
+ */
251
+ async cronUnregisterAll() {
252
+ if (!this._cronAPI)
253
+ return 0;
254
+ return this._cronAPI.unregisterAll();
255
+ }
256
+ /**
257
+ * Get shadow root (override native property)
258
+ */
259
+ get shadowRoot() {
260
+ return this._shadowRoot;
261
+ }
262
+ /**
263
+ * Render method - must be implemented by subclass
264
+ * Should return HTML string or directly manipulate shadowRoot
265
+ */
266
+ render() {
267
+ // Default implementation - subclasses should override
268
+ }
269
+ /**
270
+ * Setup event listeners - optional override in subclass
271
+ */
272
+ setupEventListeners() {
273
+ // Default implementation - subclasses can override
274
+ }
275
+ /**
276
+ * Initialize background service
277
+ * Called by BackgroundAppManager after instantiation
278
+ */
279
+ initializeBackgroundService() {
280
+ if (this.onBackgroundService) {
281
+ this.onBackgroundService();
282
+ }
283
+ }
284
+ /**
285
+ * Handle URL change
286
+ * Called by BackgroundAppManager when URL changes
287
+ */
288
+ handleUrlChange(url) {
289
+ if (this.onUrlChange) {
290
+ this.onUrlChange(url);
291
+ }
292
+ }
293
+ /**
294
+ * Handle server event
295
+ * Called by BackgroundAppManager when a WebSocket event is received for this app
296
+ */
297
+ handleServerEvent(event, data) {
298
+ if (this.onServerEvent) {
299
+ this.onServerEvent(event, data);
300
+ }
301
+ }
302
+ /**
303
+ * Lifecycle: Called when element is added to DOM
304
+ * This happens when user opens the app in the drawer
305
+ */
306
+ connectedCallback() {
307
+ // Render the app UI
308
+ this.render();
309
+ // Setup event listeners for UI
310
+ this.setupEventListeners();
311
+ // Notify Weave Platform that app UI is ready
312
+ this.dispatchEvent(new CustomEvent('weave-app-ready', {
313
+ detail: {
314
+ appInfo: this.appInfo,
315
+ timestamp: new Date().toISOString()
316
+ },
317
+ bubbles: true,
318
+ composed: true
319
+ }));
320
+ }
321
+ /**
322
+ * Lifecycle: Called when element is removed from DOM
323
+ */
324
+ disconnectedCallback() {
325
+ this.cleanup();
326
+ }
327
+ /**
328
+ * Cleanup method - optional override in subclass
329
+ * Called when app is disconnected from DOM
330
+ */
331
+ cleanup() {
332
+ // Default implementation - subclasses can override
333
+ }
334
+ /**
335
+ * Get app configuration (for Weave Platform)
336
+ */
337
+ getAppInfo() {
338
+ return this.appInfo;
339
+ }
340
+ /**
341
+ * Get current app state (for Weave Platform)
342
+ */
343
+ getAppState() {
344
+ return {
345
+ ...this.state,
346
+ lastUpdated: new Date().toISOString()
347
+ };
348
+ }
349
+ /**
350
+ * Set app configuration (for Weave Platform)
351
+ * Override this method to handle configuration updates
352
+ */
353
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
354
+ setAppConfig(_config) {
355
+ // Default implementation - subclasses can override
356
+ }
357
+ /**
358
+ * Helper: Update app state
359
+ * If persistState is enabled, state is automatically saved to background service
360
+ */
361
+ setState(updates) {
362
+ this.state = { ...this.state, ...updates };
363
+ // Auto-persist if enabled
364
+ if (this.appInfo.persistState && this._debouncedPersist) {
365
+ this._debouncedPersist();
366
+ }
367
+ }
368
+ /**
369
+ * Persist current state to background service
370
+ * Called automatically when persistState is enabled
371
+ */
372
+ async _persistState() {
373
+ if (!this._backgroundAPI)
374
+ return;
375
+ try {
376
+ await this._backgroundAPI.saveState(this.state);
377
+ }
378
+ catch (error) {
379
+ console.error('❌ Failed to persist state:', error);
380
+ }
381
+ }
382
+ /**
383
+ * Restore state from background service
384
+ * Called by BackgroundAppManager during initialization
385
+ */
386
+ async restoreState() {
387
+ if (!this._backgroundAPI || !this.appInfo.persistState) {
388
+ return false;
389
+ }
390
+ if (this._stateRestored) {
391
+ return true; // Already restored
392
+ }
393
+ try {
394
+ const savedState = await this._backgroundAPI.loadState();
395
+ if (savedState) {
396
+ this.state = { ...this.state, ...savedState };
397
+ this._stateRestored = true;
398
+ console.log('✅ State restored for app:', this.appInfo.id);
399
+ return true;
400
+ }
401
+ }
402
+ catch (error) {
403
+ console.error('❌ Failed to restore state:', error);
404
+ }
405
+ return false;
406
+ }
407
+ /**
408
+ * Helper: Render HTML into shadow root
409
+ */
410
+ renderHTML(html) {
411
+ if (this._shadowRoot) {
412
+ this._shadowRoot.innerHTML = html;
413
+ }
414
+ else {
415
+ console.error('❌ No shadow root available!');
416
+ }
417
+ }
418
+ /**
419
+ * Helper: Query element in shadow root
420
+ */
421
+ query(selector) {
422
+ return this._shadowRoot?.querySelector(selector) || null;
423
+ }
424
+ /**
425
+ * Helper: Query all elements in shadow root
426
+ */
427
+ queryAll(selector) {
428
+ return this._shadowRoot?.querySelectorAll(selector) || [];
429
+ }
430
+ }
431
+ // Export for global usage
432
+ if (typeof window !== 'undefined') {
433
+ window.WeaveBaseApp = WeaveBaseApp;
434
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * WeaveCronAPI - Client API for apps to register cron jobs
3
+ *
4
+ * Apps can register callbacks to be executed on a schedule using standard cron syntax.
5
+ * The browser extension's background script acts as the master clock.
6
+ *
7
+ * Cron syntax (5 or 6 fields):
8
+ * - 5 fields: minute hour dayOfMonth month dayOfWeek
9
+ * - 6 fields: second minute hour dayOfMonth month dayOfWeek
10
+ *
11
+ * Special characters: asterisk (any), comma (list), dash (range), slash (step)
12
+ */
13
+ interface CronJob {
14
+ id: string;
15
+ appId: string;
16
+ tabId: number;
17
+ cronExpression: string;
18
+ jobName: string;
19
+ createdAt: number;
20
+ }
21
+ type CronCallback = () => void | Promise<void>;
22
+ export declare class WeaveCronAPI {
23
+ private appId;
24
+ private callbacks;
25
+ private jobIds;
26
+ private pendingRequests;
27
+ private messageListener;
28
+ private isInitialized;
29
+ constructor(appId: string);
30
+ /**
31
+ * Initialize the cron API (sets up message listener)
32
+ */
33
+ initialize(): void;
34
+ /**
35
+ * Cleanup - unregister all jobs and remove listener
36
+ */
37
+ destroy(): Promise<void>;
38
+ /**
39
+ * Register a cron job
40
+ * @param cronExpression - Cron expression (5 or 6 fields)
41
+ * @param callback - Function to call when cron triggers
42
+ * @param jobName - Optional unique name for the job (defaults to callback name or random)
43
+ * @returns Job ID if successful
44
+ */
45
+ register(cronExpression: string, callback: CronCallback, jobName?: string): Promise<string | null>;
46
+ /**
47
+ * Unregister a cron job by name
48
+ */
49
+ unregister(jobName: string): Promise<boolean>;
50
+ /**
51
+ * Unregister all cron jobs for this app
52
+ */
53
+ unregisterAll(): Promise<number>;
54
+ /**
55
+ * List all cron jobs for this app
56
+ */
57
+ list(): Promise<CronJob[]>;
58
+ /**
59
+ * Handle incoming messages
60
+ */
61
+ private handleMessage;
62
+ /**
63
+ * Send request to content script bridge
64
+ */
65
+ private sendRequest;
66
+ }
67
+ export default WeaveCronAPI;
68
+ //# sourceMappingURL=WeaveCronAPI.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WeaveCronAPI.d.ts","sourceRoot":"","sources":["../../../src/WeaveCronAPI.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,UAAU,OAAO;IACf,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAcD,KAAK,YAAY,GAAG,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE/C,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,SAAS,CAAwC;IACzD,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,eAAe,CAA2F;IAClH,OAAO,CAAC,eAAe,CAAgD;IACvE,OAAO,CAAC,aAAa,CAAS;gBAElB,KAAK,EAAE,MAAM;IAIzB;;OAEG;IACH,UAAU,IAAI,IAAI;IAUlB;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAe9B;;;;;;OAMG;IACG,QAAQ,CACZ,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,YAAY,EACtB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAwBzB;;OAEG;IACG,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAkBnD;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;IActC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAQhC;;OAEG;IACH,OAAO,CAAC,aAAa;IAmCrB;;OAEG;IACH,OAAO,CAAC,WAAW;CAqBpB;AAED,eAAe,YAAY,CAAC"}
@@ -0,0 +1,172 @@
1
+ /**
2
+ * WeaveCronAPI - Client API for apps to register cron jobs
3
+ *
4
+ * Apps can register callbacks to be executed on a schedule using standard cron syntax.
5
+ * The browser extension's background script acts as the master clock.
6
+ *
7
+ * Cron syntax (5 or 6 fields):
8
+ * - 5 fields: minute hour dayOfMonth month dayOfWeek
9
+ * - 6 fields: second minute hour dayOfMonth month dayOfWeek
10
+ *
11
+ * Special characters: asterisk (any), comma (list), dash (range), slash (step)
12
+ */
13
+ export class WeaveCronAPI {
14
+ constructor(appId) {
15
+ this.callbacks = new Map();
16
+ this.jobIds = new Map(); // jobName -> jobId
17
+ this.pendingRequests = new Map();
18
+ this.messageListener = null;
19
+ this.isInitialized = false;
20
+ this.appId = appId;
21
+ }
22
+ /**
23
+ * Initialize the cron API (sets up message listener)
24
+ */
25
+ initialize() {
26
+ if (this.isInitialized)
27
+ return;
28
+ this.messageListener = (event) => {
29
+ this.handleMessage(event);
30
+ };
31
+ window.addEventListener('message', this.messageListener);
32
+ this.isInitialized = true;
33
+ }
34
+ /**
35
+ * Cleanup - unregister all jobs and remove listener
36
+ */
37
+ async destroy() {
38
+ // Unregister all jobs for this app
39
+ await this.unregisterAll();
40
+ // Remove message listener
41
+ if (this.messageListener) {
42
+ window.removeEventListener('message', this.messageListener);
43
+ this.messageListener = null;
44
+ }
45
+ this.callbacks.clear();
46
+ this.jobIds.clear();
47
+ this.isInitialized = false;
48
+ }
49
+ /**
50
+ * Register a cron job
51
+ * @param cronExpression - Cron expression (5 or 6 fields)
52
+ * @param callback - Function to call when cron triggers
53
+ * @param jobName - Optional unique name for the job (defaults to callback name or random)
54
+ * @returns Job ID if successful
55
+ */
56
+ async register(cronExpression, callback, jobName) {
57
+ const name = jobName || callback.name || `job_${Date.now()}`;
58
+ // Store callback locally
59
+ this.callbacks.set(name, callback);
60
+ // Send registration request to background
61
+ const response = await this.sendRequest('CRON_REGISTER', {
62
+ appId: this.appId,
63
+ cronExpression,
64
+ jobName: name,
65
+ });
66
+ if (response.success && response.jobId) {
67
+ this.jobIds.set(name, response.jobId);
68
+ return response.jobId;
69
+ }
70
+ // Registration failed, remove callback
71
+ this.callbacks.delete(name);
72
+ console.error(`Failed to register cron job: ${response.error}`);
73
+ return null;
74
+ }
75
+ /**
76
+ * Unregister a cron job by name
77
+ */
78
+ async unregister(jobName) {
79
+ const jobId = this.jobIds.get(jobName);
80
+ if (!jobId) {
81
+ return false;
82
+ }
83
+ const response = await this.sendRequest('CRON_UNREGISTER', {
84
+ jobId,
85
+ });
86
+ if (response.success) {
87
+ this.callbacks.delete(jobName);
88
+ this.jobIds.delete(jobName);
89
+ }
90
+ return response.success;
91
+ }
92
+ /**
93
+ * Unregister all cron jobs for this app
94
+ */
95
+ async unregisterAll() {
96
+ const response = await this.sendRequest('CRON_UNREGISTER_APP', {
97
+ appId: this.appId,
98
+ });
99
+ if (response.success) {
100
+ this.callbacks.clear();
101
+ this.jobIds.clear();
102
+ return response.count;
103
+ }
104
+ return 0;
105
+ }
106
+ /**
107
+ * List all cron jobs for this app
108
+ */
109
+ async list() {
110
+ const response = await this.sendRequest('CRON_LIST', {
111
+ appId: this.appId,
112
+ });
113
+ return response.jobs || [];
114
+ }
115
+ /**
116
+ * Handle incoming messages
117
+ */
118
+ handleMessage(event) {
119
+ const { data } = event;
120
+ // Handle cron tick
121
+ if (data?.type === 'CRON_TICK') {
122
+ const { appId, jobName } = data.payload;
123
+ // Only handle ticks for this app
124
+ if (appId !== this.appId)
125
+ return;
126
+ const callback = this.callbacks.get(jobName);
127
+ if (callback) {
128
+ try {
129
+ const result = callback();
130
+ // Handle async callbacks
131
+ if (result instanceof Promise) {
132
+ result.catch(err => console.error(`Cron job ${jobName} error:`, err));
133
+ }
134
+ }
135
+ catch (error) {
136
+ console.error(`Cron job ${jobName} error:`, error);
137
+ }
138
+ }
139
+ return;
140
+ }
141
+ // Handle responses
142
+ if (data?.type?.endsWith('_RESPONSE') && data.requestId) {
143
+ const pending = this.pendingRequests.get(data.requestId);
144
+ if (pending) {
145
+ this.pendingRequests.delete(data.requestId);
146
+ pending.resolve(data.payload);
147
+ }
148
+ }
149
+ }
150
+ /**
151
+ * Send request to content script bridge
152
+ */
153
+ sendRequest(type, payload) {
154
+ return new Promise((resolve, reject) => {
155
+ const requestId = `cron_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
156
+ this.pendingRequests.set(requestId, { resolve, reject });
157
+ // Timeout after 5 seconds
158
+ setTimeout(() => {
159
+ if (this.pendingRequests.has(requestId)) {
160
+ this.pendingRequests.delete(requestId);
161
+ reject(new Error('Cron request timeout'));
162
+ }
163
+ }, 5000);
164
+ window.parent.postMessage({
165
+ type,
166
+ requestId,
167
+ payload,
168
+ }, '*');
169
+ });
170
+ }
171
+ }
172
+ export default WeaveCronAPI;