@weave-apps/sdk 0.1.11 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -0
- package/dist/WeaveBaseApp.d.ts +47 -3
- package/dist/WeaveBaseApp.d.ts.map +1 -1
- package/dist/WeaveBaseApp.js +64 -2
- package/dist/WeaveCronAPI.d.ts +68 -0
- package/dist/WeaveCronAPI.d.ts.map +1 -0
- package/dist/WeaveCronAPI.js +172 -0
- package/dist/global.d.ts +2 -0
- package/dist/global.d.ts.map +1 -1
- package/dist/global.js +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/package.json +1 -1
- package/scripts/copy-sdk-files.js +1 -0
- package/templates/WEAVE_SPEC.md +265 -3
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@ Official SDK for building third-party applications for the Weave Platform.
|
|
|
12
12
|
- ✅ **Shadow DOM Isolation** - Scoped styles and encapsulation
|
|
13
13
|
- ✅ **Background Services** - Run code without user interaction
|
|
14
14
|
- ✅ **State Persistence** - Survive page reloads with auto-persist
|
|
15
|
+
- ✅ **Scheduled Tasks** - Cron jobs with standard cron syntax
|
|
15
16
|
- ✅ **Multi-Page Flows** - Pending operations across navigations
|
|
16
17
|
- ✅ **Form Integration** - Auto-fill forms with extracted data
|
|
17
18
|
- ✅ **AI Integration** - Leverage AI for smart form filling
|
|
@@ -58,6 +59,9 @@ Official SDK for building third-party applications for the Weave Platform.
|
|
|
58
59
|
- [Auto-Persist (Recommended)](#auto-persist-recommended)
|
|
59
60
|
- [Multi-Page Flows (Pending Operations)](#multi-page-flows-pending-operations)
|
|
60
61
|
- [State TTL](#state-ttl)
|
|
62
|
+
- [Scheduled Tasks (Cron Jobs)](#scheduled-tasks-cron-jobs)
|
|
63
|
+
- [🔮 Secret Hack: Persistent Cron](#-secret-hack-persistent-cron)
|
|
64
|
+
- [Cron Syntax](#cron-syntax)
|
|
61
65
|
- [Settings & Configuration](#settings--configuration)
|
|
62
66
|
- [Error Handling](#error-handling)
|
|
63
67
|
- [Performance Tips](#performance-tips)
|
|
@@ -540,6 +544,92 @@ class DataTransferApp extends WeaveBaseApp {
|
|
|
540
544
|
|
|
541
545
|
Persisted state expires after **5 minutes** by default. This prevents stale state from accumulating.
|
|
542
546
|
|
|
547
|
+
### Scheduled Tasks (Cron Jobs)
|
|
548
|
+
|
|
549
|
+
Apps can register scheduled tasks using standard cron syntax. The browser extension acts as the master clock.
|
|
550
|
+
|
|
551
|
+
```typescript
|
|
552
|
+
class PollingApp extends WeaveBaseApp {
|
|
553
|
+
constructor() {
|
|
554
|
+
super({
|
|
555
|
+
id: 'polling-app',
|
|
556
|
+
name: 'Polling App',
|
|
557
|
+
version: '1.0.0',
|
|
558
|
+
category: 'utility',
|
|
559
|
+
description: 'App with scheduled polling',
|
|
560
|
+
author: 'Your Name',
|
|
561
|
+
persistState: true, // Important for cron!
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
this.state = {
|
|
565
|
+
cronEnabled: false, // Track cron state
|
|
566
|
+
tickCount: 0
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
async onBackgroundService() {
|
|
571
|
+
// Re-register cron if it was enabled before page reload
|
|
572
|
+
if (this.state.cronEnabled) {
|
|
573
|
+
await this.startPolling();
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
async startPolling() {
|
|
578
|
+
// Register cron job - every 5 seconds
|
|
579
|
+
const jobId = await this.cronTab('*/5 * * * * *', this.handleTick, 'poller');
|
|
580
|
+
if (jobId) {
|
|
581
|
+
this.setState({ cronEnabled: true });
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
async stopPolling() {
|
|
586
|
+
await this.cronUnregisterAll();
|
|
587
|
+
this.setState({ cronEnabled: false });
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
handleTick() {
|
|
591
|
+
this.setState({ tickCount: this.state.tickCount + 1 });
|
|
592
|
+
console.log('Tick!', this.state.tickCount);
|
|
593
|
+
|
|
594
|
+
// Update UI if app is open
|
|
595
|
+
if (this.isConnected) {
|
|
596
|
+
this.render();
|
|
597
|
+
this.setupEventListeners();
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
#### 🔮 Secret Hack: Persistent Cron
|
|
604
|
+
|
|
605
|
+
Cron jobs don't survive page reloads by default. The **secret hack** is to combine `persistState: true` with a `cronEnabled` state flag:
|
|
606
|
+
|
|
607
|
+
1. **Track cron state** - Add `cronEnabled: boolean` to your state
|
|
608
|
+
2. **Set flag when starting** - `this.setState({ cronEnabled: true })`
|
|
609
|
+
3. **Re-register on reload** - Check `this.state.cronEnabled` in `onBackgroundService()`
|
|
610
|
+
|
|
611
|
+
This way, when the page reloads, your state is restored (including `cronEnabled: true`), and `onBackgroundService()` automatically re-registers the cron job!
|
|
612
|
+
|
|
613
|
+
#### Cron Syntax
|
|
614
|
+
|
|
615
|
+
```
|
|
616
|
+
┌───────────── second (0-59) - optional
|
|
617
|
+
│ ┌───────────── minute (0-59)
|
|
618
|
+
│ │ ┌───────────── hour (0-23)
|
|
619
|
+
│ │ │ ┌───────────── day of month (1-31)
|
|
620
|
+
│ │ │ │ ┌───────────── month (1-12)
|
|
621
|
+
│ │ │ │ │ ┌───────────── day of week (0-6)
|
|
622
|
+
│ │ │ │ │ │
|
|
623
|
+
* * * * * *
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
**Common patterns:**
|
|
627
|
+
- `* * * * * *` - Every second
|
|
628
|
+
- `*/5 * * * * *` - Every 5 seconds
|
|
629
|
+
- `*/30 * * * * *` - Every 30 seconds
|
|
630
|
+
- `0 * * * * *` - Every minute
|
|
631
|
+
- `0 */5 * * * *` - Every 5 minutes
|
|
632
|
+
|
|
543
633
|
### Settings & Configuration
|
|
544
634
|
|
|
545
635
|
Settings are injected from the Enterprise Console and displayed as auto-extracted form fields:
|
package/dist/WeaveBaseApp.d.ts
CHANGED
|
@@ -56,8 +56,14 @@
|
|
|
56
56
|
* customElements.define('my-app', MyApp);
|
|
57
57
|
* ```
|
|
58
58
|
*/
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Pending operation for multi-page flows
|
|
61
|
+
*/
|
|
62
|
+
export interface PendingOperation {
|
|
63
|
+
type: string;
|
|
64
|
+
data: any;
|
|
65
|
+
targetUrl?: string;
|
|
66
|
+
}
|
|
61
67
|
/**
|
|
62
68
|
* App metadata configuration
|
|
63
69
|
*/
|
|
@@ -108,6 +114,8 @@ export declare abstract class WeaveBaseApp<TSettings = Record<string, any>, TSta
|
|
|
108
114
|
protected weaveAPI: any;
|
|
109
115
|
/** Background API for state persistence */
|
|
110
116
|
private _backgroundAPI;
|
|
117
|
+
/** Cron API for scheduled tasks */
|
|
118
|
+
private _cronAPI;
|
|
111
119
|
/** Debounced persist function */
|
|
112
120
|
private _debouncedPersist;
|
|
113
121
|
/** Flag to track if state has been restored */
|
|
@@ -123,7 +131,43 @@ export declare abstract class WeaveBaseApp<TSettings = Record<string, any>, TSta
|
|
|
123
131
|
* Get the background API for state persistence and pending operations
|
|
124
132
|
* Use this for manual state management or multi-page flows
|
|
125
133
|
*/
|
|
126
|
-
protected get background():
|
|
134
|
+
protected get background(): any;
|
|
135
|
+
/**
|
|
136
|
+
* Get the cron API for scheduled tasks
|
|
137
|
+
*/
|
|
138
|
+
protected get cron(): any;
|
|
139
|
+
/**
|
|
140
|
+
* Register a cron job with a callback
|
|
141
|
+
*
|
|
142
|
+
* @param cronExpression - Standard cron expression (5 or 6 fields)
|
|
143
|
+
* - 5 fields: minute hour dayOfMonth month dayOfWeek
|
|
144
|
+
* - 6 fields: second minute hour dayOfMonth month dayOfWeek
|
|
145
|
+
* @param callback - Function to call when cron triggers
|
|
146
|
+
* @param jobName - Optional unique name for the job
|
|
147
|
+
* @returns Job ID if successful, null otherwise
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* // Every second
|
|
151
|
+
* this.cronTab('* * * * * *', this.handleEverySecond);
|
|
152
|
+
*
|
|
153
|
+
* // Every minute
|
|
154
|
+
* this.cronTab('* * * * *', this.handleEveryMinute);
|
|
155
|
+
*
|
|
156
|
+
* // Every 5 seconds
|
|
157
|
+
* this.cronTab('0/5 * * * * *', this.handleEvery5Seconds);
|
|
158
|
+
*
|
|
159
|
+
* // Every hour at minute 0
|
|
160
|
+
* this.cronTab('0 * * * *', this.handleEveryHour);
|
|
161
|
+
*/
|
|
162
|
+
protected cronTab(cronExpression: string, callback: () => void | Promise<void>, jobName?: string): Promise<string | null>;
|
|
163
|
+
/**
|
|
164
|
+
* Unregister a cron job by name
|
|
165
|
+
*/
|
|
166
|
+
protected cronUnregister(jobName: string): Promise<boolean>;
|
|
167
|
+
/**
|
|
168
|
+
* Unregister all cron jobs for this app
|
|
169
|
+
*/
|
|
170
|
+
protected cronUnregisterAll(): Promise<number>;
|
|
127
171
|
/**
|
|
128
172
|
* Get shadow root (override native property)
|
|
129
173
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WeaveBaseApp.d.ts","sourceRoot":"","sources":["../src/WeaveBaseApp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;
|
|
1
|
+
{"version":3,"file":"WeaveBaseApp.d.ts","sourceRoot":"","sources":["../src/WeaveBaseApp.ts"],"names":[],"mappings":"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"}
|
package/dist/WeaveBaseApp.js
CHANGED
|
@@ -56,7 +56,6 @@
|
|
|
56
56
|
* customElements.define('my-app', MyApp);
|
|
57
57
|
* ```
|
|
58
58
|
*/
|
|
59
|
-
import { WeaveBackgroundAPI } from './WeaveBackgroundAPI';
|
|
60
59
|
/**
|
|
61
60
|
* Simple debounce utility
|
|
62
61
|
*/
|
|
@@ -88,6 +87,8 @@ export class WeaveBaseApp extends HTMLElement {
|
|
|
88
87
|
this.state = {};
|
|
89
88
|
/** Background API for state persistence */
|
|
90
89
|
this._backgroundAPI = null;
|
|
90
|
+
/** Cron API for scheduled tasks */
|
|
91
|
+
this._cronAPI = null;
|
|
91
92
|
/** Debounced persist function */
|
|
92
93
|
this._debouncedPersist = null;
|
|
93
94
|
/** Flag to track if state has been restored */
|
|
@@ -120,7 +121,14 @@ export class WeaveBaseApp extends HTMLElement {
|
|
|
120
121
|
this.weaveAPI = new window.WeaveAPIClient();
|
|
121
122
|
this.weaveAPI.setAppId(this._appUuid);
|
|
122
123
|
// Create background API instance for state persistence
|
|
123
|
-
|
|
124
|
+
if (window.WeaveBackgroundAPI) {
|
|
125
|
+
this._backgroundAPI = new window.WeaveBackgroundAPI(this._appUuid);
|
|
126
|
+
}
|
|
127
|
+
// Create cron API instance for scheduled tasks
|
|
128
|
+
if (window.WeaveCronAPI) {
|
|
129
|
+
this._cronAPI = new window.WeaveCronAPI(this._appUuid);
|
|
130
|
+
this._cronAPI.initialize();
|
|
131
|
+
}
|
|
124
132
|
// Set up debounced persist if enabled
|
|
125
133
|
if (appInfo.persistState) {
|
|
126
134
|
const delay = appInfo.persistDebounce ?? 100;
|
|
@@ -143,6 +151,60 @@ export class WeaveBaseApp extends HTMLElement {
|
|
|
143
151
|
get background() {
|
|
144
152
|
return this._backgroundAPI;
|
|
145
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Get the cron API for scheduled tasks
|
|
156
|
+
*/
|
|
157
|
+
get cron() {
|
|
158
|
+
return this._cronAPI;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Register a cron job with a callback
|
|
162
|
+
*
|
|
163
|
+
* @param cronExpression - Standard cron expression (5 or 6 fields)
|
|
164
|
+
* - 5 fields: minute hour dayOfMonth month dayOfWeek
|
|
165
|
+
* - 6 fields: second minute hour dayOfMonth month dayOfWeek
|
|
166
|
+
* @param callback - Function to call when cron triggers
|
|
167
|
+
* @param jobName - Optional unique name for the job
|
|
168
|
+
* @returns Job ID if successful, null otherwise
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* // Every second
|
|
172
|
+
* this.cronTab('* * * * * *', this.handleEverySecond);
|
|
173
|
+
*
|
|
174
|
+
* // Every minute
|
|
175
|
+
* this.cronTab('* * * * *', this.handleEveryMinute);
|
|
176
|
+
*
|
|
177
|
+
* // Every 5 seconds
|
|
178
|
+
* this.cronTab('0/5 * * * * *', this.handleEvery5Seconds);
|
|
179
|
+
*
|
|
180
|
+
* // Every hour at minute 0
|
|
181
|
+
* this.cronTab('0 * * * *', this.handleEveryHour);
|
|
182
|
+
*/
|
|
183
|
+
async cronTab(cronExpression, callback, jobName) {
|
|
184
|
+
if (!this._cronAPI) {
|
|
185
|
+
console.error('Cron API not available - app UUID may be missing');
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
// Bind callback to this instance
|
|
189
|
+
const boundCallback = callback.bind(this);
|
|
190
|
+
return this._cronAPI.register(cronExpression, boundCallback, jobName);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Unregister a cron job by name
|
|
194
|
+
*/
|
|
195
|
+
async cronUnregister(jobName) {
|
|
196
|
+
if (!this._cronAPI)
|
|
197
|
+
return false;
|
|
198
|
+
return this._cronAPI.unregister(jobName);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Unregister all cron jobs for this app
|
|
202
|
+
*/
|
|
203
|
+
async cronUnregisterAll() {
|
|
204
|
+
if (!this._cronAPI)
|
|
205
|
+
return 0;
|
|
206
|
+
return this._cronAPI.unregisterAll();
|
|
207
|
+
}
|
|
146
208
|
/**
|
|
147
209
|
* Get shadow root (override native property)
|
|
148
210
|
*/
|
|
@@ -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;
|
package/dist/global.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { WeaveBaseApp } from './WeaveBaseApp';
|
|
|
8
8
|
import { WeaveDOMAPI } from './WeaveDOMAPI';
|
|
9
9
|
import { WeaveAPIClient } from './WeaveAPIClient';
|
|
10
10
|
import { WeaveBackgroundAPI } from './WeaveBackgroundAPI';
|
|
11
|
+
import { WeaveCronAPI } from './WeaveCronAPI';
|
|
11
12
|
declare global {
|
|
12
13
|
interface Window {
|
|
13
14
|
WeaveBaseApp: typeof WeaveBaseApp;
|
|
@@ -16,6 +17,7 @@ declare global {
|
|
|
16
17
|
WeaveAPIClient: typeof WeaveAPIClient;
|
|
17
18
|
weaveAPI: WeaveAPIClient;
|
|
18
19
|
WeaveBackgroundAPI: typeof WeaveBackgroundAPI;
|
|
20
|
+
WeaveCronAPI: typeof WeaveCronAPI;
|
|
19
21
|
}
|
|
20
22
|
}
|
|
21
23
|
//# sourceMappingURL=global.d.ts.map
|
package/dist/global.d.ts.map
CHANGED
|
@@ -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;AAElD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;
|
|
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;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C,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;QAC9C,YAAY,EAAE,OAAO,YAAY,CAAC;KAAG;CACxC"}
|
package/dist/global.js
CHANGED
|
@@ -10,6 +10,7 @@ import weavekDOM from './WeaveDOMAPI';
|
|
|
10
10
|
import { WeaveAPIClient } from './WeaveAPIClient';
|
|
11
11
|
import weaveAPI from './WeaveAPIClient';
|
|
12
12
|
import { WeaveBackgroundAPI } from './WeaveBackgroundAPI';
|
|
13
|
+
import { WeaveCronAPI } from './WeaveCronAPI';
|
|
13
14
|
// Make SDK available globally
|
|
14
15
|
window.WeaveBaseApp = WeaveBaseApp;
|
|
15
16
|
window.WeaveDOMAPI = WeaveDOMAPI;
|
|
@@ -17,3 +18,4 @@ window.weaveDOM = weavekDOM;
|
|
|
17
18
|
window.WeaveAPIClient = WeaveAPIClient;
|
|
18
19
|
window.weaveAPI = weaveAPI;
|
|
19
20
|
window.WeaveBackgroundAPI = WeaveBackgroundAPI;
|
|
21
|
+
window.WeaveCronAPI = WeaveCronAPI;
|
package/dist/index.d.ts
CHANGED
|
@@ -9,5 +9,6 @@ export { default as weaveDOM } from './WeaveDOMAPI';
|
|
|
9
9
|
export { WeaveAPIClient, type AIChatRequest, type AIChatResponse, type AppData, type CreateAppDataRequest, type UpdateAppDataRequest } from './WeaveAPIClient';
|
|
10
10
|
export { default as weaveAPI } from './WeaveAPIClient';
|
|
11
11
|
export { WeaveBackgroundAPI } from './WeaveBackgroundAPI';
|
|
12
|
+
export { WeaveCronAPI } from './WeaveCronAPI';
|
|
12
13
|
import './global';
|
|
13
14
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,KAAK,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACxF,OAAO,EAAE,WAAW,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AACvF,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EACL,cAAc,EACd,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,OAAO,EACZ,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EAC1B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,KAAK,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACxF,OAAO,EAAE,WAAW,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AACvF,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EACL,cAAc,EACd,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,OAAO,EACZ,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EAC1B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C,OAAO,UAAU,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -9,5 +9,6 @@ export { default as weaveDOM } from './WeaveDOMAPI';
|
|
|
9
9
|
export { WeaveAPIClient } from './WeaveAPIClient';
|
|
10
10
|
export { default as weaveAPI } from './WeaveAPIClient';
|
|
11
11
|
export { WeaveBackgroundAPI } from './WeaveBackgroundAPI';
|
|
12
|
+
export { WeaveCronAPI } from './WeaveCronAPI';
|
|
12
13
|
// Import global.ts to ensure Window interface augmentation is included
|
|
13
14
|
import './global';
|
package/package.json
CHANGED
package/templates/WEAVE_SPEC.md
CHANGED
|
@@ -1088,9 +1088,10 @@ customElements.define('smart-form-assistant', SmartFormAssistant);
|
|
|
1088
1088
|
4. **Multi-Page Flows**: Use `this.background?.setPendingOperation()` for cross-page operations
|
|
1089
1089
|
5. **Background Service**: `onBackgroundService()` runs immediately, before DOM attachment
|
|
1090
1090
|
6. **URL Monitoring**: `onUrlChange()` detects SPA navigation automatically
|
|
1091
|
-
7. **
|
|
1092
|
-
8. **
|
|
1093
|
-
9. **
|
|
1091
|
+
7. **Scheduled Tasks**: Use `this.cronTab()` to register cron jobs with standard cron syntax
|
|
1092
|
+
8. **Shared Context**: Background and foreground share the same instance and state
|
|
1093
|
+
9. **DOM Check**: Use `this.isConnected` to check if app is attached to DOM
|
|
1094
|
+
10. **No User Interaction Required**: Apps can run and inject UI automatically
|
|
1094
1095
|
|
|
1095
1096
|
### Helper Methods (Available in `this`)
|
|
1096
1097
|
|
|
@@ -1118,6 +1119,18 @@ this.shadowRoot: ShadowRoot
|
|
|
1118
1119
|
|
|
1119
1120
|
// Access background API for state persistence
|
|
1120
1121
|
this.background: WeaveBackgroundAPI | null
|
|
1122
|
+
|
|
1123
|
+
// Access cron API for scheduled tasks
|
|
1124
|
+
this.cron: WeaveCronAPI | null
|
|
1125
|
+
|
|
1126
|
+
// Register a cron job (shorthand)
|
|
1127
|
+
await this.cronTab(expression: string, callback: () => void, jobName?: string): Promise<string | null>
|
|
1128
|
+
|
|
1129
|
+
// Unregister a cron job by name
|
|
1130
|
+
await this.cronUnregister(jobName: string): Promise<boolean>
|
|
1131
|
+
|
|
1132
|
+
// Unregister all cron jobs for this app
|
|
1133
|
+
await this.cronUnregisterAll(): Promise<number>
|
|
1121
1134
|
```
|
|
1122
1135
|
|
|
1123
1136
|
## State Persistence (Survive Page Reloads)
|
|
@@ -1372,6 +1385,255 @@ For long-term persistence, use `this.weaveAPI.appData.*` methods instead.
|
|
|
1372
1385
|
- Don't forget to clear pending operations
|
|
1373
1386
|
- Don't assume state will persist forever (5 min TTL)
|
|
1374
1387
|
|
|
1388
|
+
## Scheduled Tasks (Cron Service)
|
|
1389
|
+
|
|
1390
|
+
### Overview
|
|
1391
|
+
|
|
1392
|
+
Apps can register **scheduled tasks** that run on a timer using standard cron syntax. The browser extension acts as the master clock, ticking every second and notifying apps when their cron expressions match.
|
|
1393
|
+
|
|
1394
|
+
**Use cases:**
|
|
1395
|
+
- ⏰ **Periodic polling** - Check for updates every N seconds
|
|
1396
|
+
- 🔄 **Auto-refresh** - Update UI or data at intervals
|
|
1397
|
+
- 📊 **Monitoring** - Track metrics over time
|
|
1398
|
+
- 🔔 **Reminders** - Trigger notifications on schedule
|
|
1399
|
+
|
|
1400
|
+
### Architecture
|
|
1401
|
+
|
|
1402
|
+
```
|
|
1403
|
+
[Background Script: CronService] ← Master clock (ticks every second)
|
|
1404
|
+
↓
|
|
1405
|
+
[Content Script: CronBridge] ← Routes tick messages
|
|
1406
|
+
↓
|
|
1407
|
+
[Iframe: App] ← Receives CRON_TICK, executes callback
|
|
1408
|
+
```
|
|
1409
|
+
|
|
1410
|
+
### Registering a Cron Job
|
|
1411
|
+
|
|
1412
|
+
Use `this.cronTab()` to register a cron job:
|
|
1413
|
+
|
|
1414
|
+
```typescript
|
|
1415
|
+
class MyApp extends WeaveBaseApp {
|
|
1416
|
+
constructor() {
|
|
1417
|
+
super({
|
|
1418
|
+
id: 'my-app',
|
|
1419
|
+
name: 'My App',
|
|
1420
|
+
version: '1.0.0',
|
|
1421
|
+
category: 'utility',
|
|
1422
|
+
description: 'App with scheduled tasks',
|
|
1423
|
+
tags: ['cron'],
|
|
1424
|
+
persistState: true, // Recommended for cron apps
|
|
1425
|
+
});
|
|
1426
|
+
|
|
1427
|
+
this.state = {
|
|
1428
|
+
tickCount: 0,
|
|
1429
|
+
cronEnabled: false
|
|
1430
|
+
};
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
async onBackgroundService() {
|
|
1434
|
+
// Re-register cron if it was enabled before page reload
|
|
1435
|
+
if (this.state.cronEnabled) {
|
|
1436
|
+
await this.startCron();
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
async startCron() {
|
|
1441
|
+
// Register a cron job - every 5 seconds
|
|
1442
|
+
const jobId = await this.cronTab('*/5 * * * * *', this.handleTick, 'myTicker');
|
|
1443
|
+
|
|
1444
|
+
if (jobId) {
|
|
1445
|
+
console.log('Cron registered:', jobId);
|
|
1446
|
+
this.setState({ cronEnabled: true });
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
async stopCron() {
|
|
1451
|
+
await this.cronUnregisterAll();
|
|
1452
|
+
this.setState({ cronEnabled: false });
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
// Cron callback - called every 5 seconds
|
|
1456
|
+
handleTick() {
|
|
1457
|
+
this.setState({ tickCount: this.state.tickCount + 1 });
|
|
1458
|
+
console.log('Tick!', this.state.tickCount);
|
|
1459
|
+
|
|
1460
|
+
// Update UI if app is open
|
|
1461
|
+
if (this.isConnected) {
|
|
1462
|
+
this.render();
|
|
1463
|
+
this.setupEventListeners();
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
```
|
|
1468
|
+
|
|
1469
|
+
### Cron Syntax
|
|
1470
|
+
|
|
1471
|
+
The cron service supports both **5-field** (standard) and **6-field** (with seconds) expressions:
|
|
1472
|
+
|
|
1473
|
+
#### 5-Field (Standard Cron)
|
|
1474
|
+
```
|
|
1475
|
+
┌───────────── minute (0-59)
|
|
1476
|
+
│ ┌───────────── hour (0-23)
|
|
1477
|
+
│ │ ┌───────────── day of month (1-31)
|
|
1478
|
+
│ │ │ ┌───────────── month (1-12)
|
|
1479
|
+
│ │ │ │ ┌───────────── day of week (0-6, Sunday=0)
|
|
1480
|
+
│ │ │ │ │
|
|
1481
|
+
* * * * *
|
|
1482
|
+
```
|
|
1483
|
+
|
|
1484
|
+
#### 6-Field (With Seconds)
|
|
1485
|
+
```
|
|
1486
|
+
┌───────────── second (0-59)
|
|
1487
|
+
│ ┌───────────── minute (0-59)
|
|
1488
|
+
│ │ ┌───────────── hour (0-23)
|
|
1489
|
+
│ │ │ ┌───────────── day of month (1-31)
|
|
1490
|
+
│ │ │ │ ┌───────────── month (1-12)
|
|
1491
|
+
│ │ │ │ │ ┌───────────── day of week (0-6, Sunday=0)
|
|
1492
|
+
│ │ │ │ │ │
|
|
1493
|
+
* * * * * *
|
|
1494
|
+
```
|
|
1495
|
+
|
|
1496
|
+
#### Special Characters
|
|
1497
|
+
|
|
1498
|
+
| Character | Description | Example |
|
|
1499
|
+
|-----------|-------------|---------|
|
|
1500
|
+
| `*` | Any value | `* * * * * *` = every second |
|
|
1501
|
+
| `,` | Value list | `0,30 * * * * *` = at 0 and 30 seconds |
|
|
1502
|
+
| `-` | Range | `0-10 * * * * *` = seconds 0-10 |
|
|
1503
|
+
| `/` | Step | `*/5 * * * * *` = every 5 seconds |
|
|
1504
|
+
|
|
1505
|
+
#### Common Examples
|
|
1506
|
+
|
|
1507
|
+
```typescript
|
|
1508
|
+
// Every second
|
|
1509
|
+
await this.cronTab('* * * * * *', callback);
|
|
1510
|
+
|
|
1511
|
+
// Every 5 seconds
|
|
1512
|
+
await this.cronTab('*/5 * * * * *', callback);
|
|
1513
|
+
|
|
1514
|
+
// Every 10 seconds
|
|
1515
|
+
await this.cronTab('*/10 * * * * *', callback);
|
|
1516
|
+
|
|
1517
|
+
// Every 30 seconds
|
|
1518
|
+
await this.cronTab('0,30 * * * * *', callback);
|
|
1519
|
+
|
|
1520
|
+
// Every minute (at second 0)
|
|
1521
|
+
await this.cronTab('0 * * * * *', callback);
|
|
1522
|
+
|
|
1523
|
+
// Every 5 minutes
|
|
1524
|
+
await this.cronTab('0 */5 * * * *', callback);
|
|
1525
|
+
|
|
1526
|
+
// Every hour at minute 0
|
|
1527
|
+
await this.cronTab('0 0 * * * *', callback);
|
|
1528
|
+
|
|
1529
|
+
// Standard 5-field: every minute
|
|
1530
|
+
await this.cronTab('* * * * *', callback);
|
|
1531
|
+
```
|
|
1532
|
+
|
|
1533
|
+
### Cron API Methods
|
|
1534
|
+
|
|
1535
|
+
```typescript
|
|
1536
|
+
// Register a cron job
|
|
1537
|
+
// Returns job ID if successful, null if failed
|
|
1538
|
+
await this.cronTab(
|
|
1539
|
+
cronExpression: string, // Cron expression (5 or 6 fields)
|
|
1540
|
+
callback: () => void, // Function to call on each tick
|
|
1541
|
+
jobName?: string // Optional unique name for the job
|
|
1542
|
+
): Promise<string | null>
|
|
1543
|
+
|
|
1544
|
+
// Unregister a specific job by name
|
|
1545
|
+
await this.cronUnregister(jobName: string): Promise<boolean>
|
|
1546
|
+
|
|
1547
|
+
// Unregister all cron jobs for this app
|
|
1548
|
+
await this.cronUnregisterAll(): Promise<number>
|
|
1549
|
+
|
|
1550
|
+
// Access cron API directly (advanced)
|
|
1551
|
+
this.cron?.register(expression, callback, name)
|
|
1552
|
+
this.cron?.unregister(jobName)
|
|
1553
|
+
this.cron?.unregisterAll()
|
|
1554
|
+
this.cron?.list()
|
|
1555
|
+
```
|
|
1556
|
+
|
|
1557
|
+
### Persisting Cron State Across Page Reloads
|
|
1558
|
+
|
|
1559
|
+
Cron jobs are **not automatically re-registered** after a page reload. Use `persistState: true` and re-register in `onBackgroundService()`:
|
|
1560
|
+
|
|
1561
|
+
```typescript
|
|
1562
|
+
class PersistentCronApp extends WeaveBaseApp {
|
|
1563
|
+
constructor() {
|
|
1564
|
+
super({
|
|
1565
|
+
id: 'persistent-cron',
|
|
1566
|
+
name: 'Persistent Cron',
|
|
1567
|
+
version: '1.0.0',
|
|
1568
|
+
category: 'utility',
|
|
1569
|
+
description: 'Cron that survives page reloads',
|
|
1570
|
+
tags: ['cron'],
|
|
1571
|
+
persistState: true, // ✅ Enable state persistence
|
|
1572
|
+
});
|
|
1573
|
+
|
|
1574
|
+
this.state = {
|
|
1575
|
+
cronEnabled: false,
|
|
1576
|
+
tickCount: 0
|
|
1577
|
+
};
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
async onBackgroundService() {
|
|
1581
|
+
// ✅ Re-register cron if it was enabled before reload
|
|
1582
|
+
if (this.state.cronEnabled) {
|
|
1583
|
+
await this.cronTab('*/5 * * * * *', this.handleTick, 'ticker');
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
handleTick() {
|
|
1588
|
+
this.setState({ tickCount: this.state.tickCount + 1 });
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
async toggleCron() {
|
|
1592
|
+
if (this.state.cronEnabled) {
|
|
1593
|
+
await this.cronUnregisterAll();
|
|
1594
|
+
this.setState({ cronEnabled: false });
|
|
1595
|
+
} else {
|
|
1596
|
+
await this.cronTab('*/5 * * * * *', this.handleTick, 'ticker');
|
|
1597
|
+
this.setState({ cronEnabled: true });
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
```
|
|
1602
|
+
|
|
1603
|
+
### Best Practices
|
|
1604
|
+
|
|
1605
|
+
#### ✅ DO:
|
|
1606
|
+
- Use `persistState: true` to remember cron state across reloads
|
|
1607
|
+
- Re-register cron jobs in `onBackgroundService()` if they were enabled
|
|
1608
|
+
- Give jobs meaningful names for easier management
|
|
1609
|
+
- Use appropriate intervals (don't poll every second unless necessary)
|
|
1610
|
+
- Update UI only if `this.isConnected` (app is open)
|
|
1611
|
+
- Clean up cron jobs in `cleanup()` method
|
|
1612
|
+
|
|
1613
|
+
#### ❌ DON'T:
|
|
1614
|
+
- Don't register cron jobs in the constructor (use `onBackgroundService()`)
|
|
1615
|
+
- Don't use very short intervals without good reason (battery/performance)
|
|
1616
|
+
- Don't assume cron jobs survive page reloads (they don't)
|
|
1617
|
+
- Don't forget to unregister jobs when no longer needed
|
|
1618
|
+
|
|
1619
|
+
### Cleanup
|
|
1620
|
+
|
|
1621
|
+
Cron jobs are automatically cleaned up when:
|
|
1622
|
+
- The tab is closed
|
|
1623
|
+
- The app explicitly unregisters them
|
|
1624
|
+
- The extension is reloaded
|
|
1625
|
+
|
|
1626
|
+
For manual cleanup:
|
|
1627
|
+
|
|
1628
|
+
```typescript
|
|
1629
|
+
class MyApp extends WeaveBaseApp {
|
|
1630
|
+
protected cleanup(): void {
|
|
1631
|
+
// Clean up cron jobs when app is removed from DOM
|
|
1632
|
+
this.cronUnregisterAll();
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
```
|
|
1636
|
+
|
|
1375
1637
|
## Weave Backend API
|
|
1376
1638
|
|
|
1377
1639
|
### ⚠️ CRITICAL: API Access Restrictions
|