@pylonsync/sdk 0.3.287 → 0.3.289

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.ts +62 -0
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.3.287",
6
+ "version": "0.3.289",
7
7
  "type": "module",
8
8
  "main": "src/index.ts",
9
9
  "types": "src/index.ts",
package/src/index.ts CHANGED
@@ -650,6 +650,21 @@ export interface ManifestPolicy {
650
650
 
651
651
  export const MANIFEST_VERSION = 1;
652
652
 
653
+ /** A recurring job: run a function on a cron schedule. Declared in the
654
+ * manifest via `cron(schedule, functionName)`; the runtime fires the named
655
+ * function with anonymous auth every time the schedule matches. Server-side
656
+ * `ctx.db.*` is trusted, so a maintenance handler reads/writes its entities
657
+ * directly without elevating. */
658
+ export interface ManifestCron {
659
+ /** Standard 5-field cron expression, e.g. `"0 * * * *"` (every hour). */
660
+ schedule: string;
661
+ /** Name of the function (query/mutation/action) to run. Should be an
662
+ * `internal: true` function so it isn't also reachable over HTTP. */
663
+ function: string;
664
+ /** Optional human description, surfaced by `pylon status` / tooling. */
665
+ description?: string;
666
+ }
667
+
653
668
  export interface AppManifest {
654
669
  manifest_version: number;
655
670
  name: string;
@@ -665,6 +680,49 @@ export interface AppManifest {
665
680
  /** Declared OAuth integrations. Auto-creates the `_Connection`
666
681
  * entity at runtime boot. */
667
682
  connections?: ManifestConnection[];
683
+ /** Recurring jobs — run a function on a cron schedule. */
684
+ crons?: ManifestCron[];
685
+ }
686
+
687
+ /**
688
+ * Declare a recurring job. Runs the named function every time the cron
689
+ * `schedule` matches (the runtime checks once a minute).
690
+ *
691
+ * ```ts
692
+ * buildManifest({
693
+ * // ...
694
+ * crons: [
695
+ * cron("0 * * * *", "hourlyRollup"), // every hour, on the hour
696
+ * cron("15 3 * * *", "nightlyCleanup"), // daily at 03:15
697
+ * ],
698
+ * });
699
+ * ```
700
+ *
701
+ * `functionName` is a function in `functions/` — make it `internal: true`
702
+ * so it isn't also exposed over HTTP. It runs with anonymous auth, and a
703
+ * function's own `ctx.db.*` calls are server-side (not policy-gated), so a
704
+ * maintenance handler writes directly. Only call
705
+ * `ctx.auth.elevate({ admin: true, reason: "..." })` if it chains an
706
+ * `internal: true` function via `ctx.scheduler`, or you run with
707
+ * `PYLON_STRICT_FN_POLICIES=1`. The `reason` is mandatory (audited).
708
+ *
709
+ * Multiple replicas: each Pylon process runs its own scheduler. On a SHARED
710
+ * datastore (Postgres — the cloud default), the runtime takes a per-minute
711
+ * lease so each cron fires exactly ONCE per tick across all replicas. On
712
+ * per-replica SQLite there's no shared lease, so each replica fires — keep
713
+ * handlers idempotent there (most maintenance work naturally is). A
714
+ * single-machine app always fires exactly once.
715
+ */
716
+ export function cron(
717
+ schedule: string,
718
+ functionName: string,
719
+ opts?: { description?: string }
720
+ ): ManifestCron {
721
+ return {
722
+ schedule,
723
+ function: functionName,
724
+ ...(opts?.description ? { description: opts.description } : {}),
725
+ };
668
726
  }
669
727
 
670
728
  export function entitiesToManifest(
@@ -1352,6 +1410,7 @@ export function buildManifest(options: {
1352
1410
  auth?: ManifestAuthConfig;
1353
1411
  llm?: ManifestLlmConfig;
1354
1412
  connections?: ManifestConnection[];
1413
+ crons?: ManifestCron[];
1355
1414
  }): AppManifest {
1356
1415
  // Pull policies attached via the fluent `e.entity().policies(...)`
1357
1416
  // chain onto the top-level policies list. Without this, fluent
@@ -1396,6 +1455,9 @@ export function buildManifest(options: {
1396
1455
  ...(options.connections && options.connections.length > 0
1397
1456
  ? { connections: options.connections }
1398
1457
  : {}),
1458
+ ...(options.crons && options.crons.length > 0
1459
+ ? { crons: options.crons }
1460
+ : {}),
1399
1461
  };
1400
1462
  }
1401
1463