@weave-apps/sdk 0.1.12 → 0.1.14

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
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weave-apps/sdk",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "SDK for building Weave Micro Apps",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -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. **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
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