@stacksjs/scheduler 0.70.88 → 0.70.91

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.
@@ -0,0 +1,4 @@
1
+ // export { Schedule } from './schedule'
2
+ export * from './run';
3
+ export * from './schedule';
4
+ export * from './types';
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ var _=import.meta.require;import{ok as x}from"@stacksjs/error-handling";import{path as P}from"@stacksjs/path";import{schedule as z}from"@stacksjs/scheduler";import{globSync as g}from"@stacksjs/storage";import{snakeCase as S}from"@stacksjs/strings";import{Every as C}from"@stacksjs/types";async function GU(){let U=g([P.appPath("Jobs/*.ts")],{absolute:!0});for(let V of U)try{let Z=(await import(V)).default,$=S(y(Z,V));if(Z.rate)j($,Z.rate)}catch(W){console.error(W)}return await N(),x("Schedules ran successfully")}async function N(){let U=P.appPath("Scheduler.ts");try{let V=await import(U);if(typeof V.default==="function")V.default();else console.warn(`Scheduler file ${U} does not export a default function`)}catch(V){console.warn(`Could not load scheduler file ${U}:`,V)}}function j(U,V){switch(V){case C.Minute:z.job(U).everyMinute();break;case C.TwoMinutes:z.job(U).everyTwoMinutes();break;case C.FiveMinutes:z.job(U).everyFiveMinutes();break;case C.TenMinutes:z.job(U).everyTenMinutes();break;case C.ThirtyMinutes:z.job(U).everyThirtyMinutes();break;case C.HalfHour:z.job(U).everyThirtyMinutes();break;case C.Hour:z.job(U).everyHour();break;case C.Day:z.job(U).everyDay();break;case C.Week:z.job(U).weekly();break;case C.Month:z.job(U).monthly();break;case C.Year:z.job(U).yearly();break;default:throw Error(`Unsupported rate: ${V}`)}}function y(U,V){if(U.name)return U.name;return P.basename(V).replace(/\.ts$/,"")}import{spawn as l}from"child_process";import{join as o}from"path";import{runAction as t}from"@stacksjs/actions";import{log as H,runCommand as r}from"@stacksjs/cli";import{parse as K}from"@stacksjs/cron";import{runJob as a}from"@stacksjs/queue";import{createHash as D}from"crypto";import{existsSync as b,mkdirSync as k,readFileSync as p,statSync as h,unlinkSync as m,writeFileSync as T}from"fs";import{join as R}from"path";import w from"process";var A="stacks_scheduler";function u(U){let V=Date.now()-U.acquiredAt;if(V<0)return 0;return V}async function f(U,V,W,Z,$){let Q=$??R(w.cwd(),"storage","framework","locks"),G=R(Q,`${U}.lock`);if(!b(Q))k(Q,{recursive:!0});let X=!1,B=null;if(Z&&(W==="postgres"||W==="mysql")){if(!await i(U,W,Z))return null;B=async()=>{if(X)return;X=!0,await c(U,W,Z).catch(()=>{})}}let q={acquiredAt:Date.now(),monotonicStart:Math.round(performance.now()),holder:w.pid};try{T(G,JSON.stringify(q),{flag:"wx"})}catch{let O=!1;try{let J=p(G,"utf8"),L=JSON.parse(J);if(u(L)>=V)O=!0}catch{try{let J=h(G);if(Math.max(0,Date.now()-J.mtimeMs)>=V)O=!0}catch{O=!0}}if(!O){if(B)await B();return null}try{T(G,JSON.stringify(q))}catch{if(B)await B();return null}}return{release:async()=>{try{m(G)}catch{}if(B)await B()}}}function E(U){let V=D("sha256").update(`${A}:${U}`).digest();return{key1:V.readInt32BE(0),key2:V.readInt32BE(4)}}async function i(U,V,W){if(V==="postgres"){let{key1:Q,key2:G}=E(U),X=await W.unsafe(`SELECT pg_try_advisory_lock(${Q}, ${G}) AS acquired`);return n(X,"acquired")}let Z=v(U),$=await W.unsafe(`SELECT GET_LOCK('${Z}', 0) AS acquired`);return d($,"acquired")===1}async function c(U,V,W){if(V==="postgres"){let{key1:$,key2:Q}=E(U);await W.unsafe(`SELECT pg_advisory_unlock(${$}, ${Q})`);return}let Z=v(U);await W.unsafe(`SELECT RELEASE_LOCK('${Z}')`)}function v(U){let V=`${A}_${U}`.replace(/[^A-Za-z0-9_]/g,"_");if(V.length<=64)return V;let W=D("sha256").update(U).digest("hex").slice(0,32);return`${A}_${W}`}function n(U,V){let W=F(U);if(!W)return!1;let Z=W[V];return Z===!0||Z===1||Z==="1"||Z==="t"}function d(U,V){let W=F(U);if(!W)return null;let Z=W[V];if(typeof Z==="number")return Z;if(typeof Z==="string"&&/^-?\d+$/.test(Z))return Number.parseInt(Z,10);return null}function F(U){if(!U)return null;if(Array.isArray(U))return U[0];if(typeof U==="object"&&"rows"in U&&Array.isArray(U.rows))return U.rows[0];return null}class Y{static jobs=new Map;static scheduledKeys=new Map;cronPattern="";intervalMs=null;timezone="America/Los_Angeles";task;static lockDir=o(process.cwd(),"storage","framework","locks");static activeLocks=new Map;shouldPreventOverlap=!1;overlapExpiresAfterMinutes=1440;shouldRunOnOneServer=!1;shouldRunInBackground=!1;outputPath=null;outputAppend=!1;options={};constructor(U){this.task=U,queueMicrotask(()=>{try{this.start()}catch(V){H.error(`Failed to start scheduled task: ${V}`)}})}get pattern(){return this.cronPattern}everySecond(){return this.intervalMs=1000,this.cronPattern="@every_second",this}everyMinute(){return this.cronPattern="* * * * *",this}everyTwoMinutes(){return this.cronPattern="*/2 * * * *",this}everyFiveMinutes(){return this.cronPattern="*/5 * * * *",this}everyTenMinutes(){return this.cronPattern="*/10 * * * *",this}everyThirtyMinutes(){return this.cronPattern="*/30 * * * *",this}everyHour(){return this.cronPattern="0 * * * *",this}everyDay(){return this.cronPattern="0 0 * * *",this}hourly(){return this.cronPattern="0 * * * *",this}daily(){return this.cronPattern="0 0 * * *",this}weekly(){return this.cronPattern="0 0 * * 0",this}monthly(){return this.cronPattern="0 0 1 * *",this}yearly(){return this.cronPattern="0 0 1 1 *",this}annually(){return this.cronPattern="0 0 1 1 *",this}onDays(U){if(!Array.isArray(U)||U.length===0)throw Error(`onDays() requires a non-empty array; got ${JSON.stringify(U)}`);for(let V of U)if(!Number.isInteger(V)||V<0||V>6)throw Error(`onDays(): each day must be an integer 0-6 (Sun-Sat). Got ${V}`);return this.cronPattern=`0 0 * * ${U.join(",")}`,this}at(U){let V=U.split(":");if(V.length!==2||V[0]===""||V[1]==="")throw Error(`Invalid time format "${U}". Expected "HH:MM" (e.g., "14:30")`);let[W,Z]=V.map(Number);if(W===void 0||Z===void 0||Number.isNaN(W)||Number.isNaN(Z)||W<0||W>23||Z<0||Z>59)throw Error(`Invalid time "${U}". Hour must be 0-23, minute must be 0-59`);return this.cronPattern=`${Z} ${W} * * *`,this}setTimeZone(U){return this.timezone=U,this.options.timezone=U,this}withErrorHandler(U){return this.options.catch=U,this}withMaxRuns(U){return this.options.maxRuns=U,this}withProtection(U){return this.options.protect=U||!0,this}withName(U){return this.options.name=U,this}withContext(U){return this.options.context=U,this}withInterval(U){return this.options.interval=U,this}between(U,V){return this.options.startAt=U,this.options.stopAt=V,this}withoutOverlapping(U){if(this.shouldPreventOverlap=!0,U!==void 0)this.overlapExpiresAfterMinutes=U;return this}onOneServer(){return this.shouldRunOnOneServer=!0,this}runInBackground(){return this.shouldRunInBackground=!0,this}sendOutputTo(U){return this.outputPath=U,this.outputAppend=!1,this}appendOutputTo(U){return this.outputPath=U,this.outputAppend=!0,this}async runMissed(U){if(!this.cronPattern)return H.warn("[scheduler] runMissed() requires a cron-based schedule; interval-based tasks (everySecond) have no concept of missed slots"),0;let V=U.max??100,W=U.since instanceof Date?U.since.getTime():U.since,Z=Date.now();if(W>=Z)return 0;let $=[],Q=W;while($.length<V+1){let X=K(this.cronPattern,Q);if(!X||X.getTime()>Z)break;$.push(X),Q=X.getTime()}if($.length>V)H.warn(`[scheduler] runMissed: ${$.length} missed slots since ${new Date(W).toISOString()}, capping at ${V} \u2014 older runs dropped`),$.splice(0,$.length-V);let G=0;for(let X of $)try{let B=this.task();if(B&&typeof B.then==="function")await B;G++}catch(B){if(H.error(`[scheduler] runMissed slot ${X.toISOString()} failed: ${B instanceof Error?B.message:String(B)}`),this.options.catch)this.options.catch(B)}return G}static listJobs(){let U=[];for(let[V,W]of Y.jobs)U.push({name:V,pattern:W.pattern,timezone:W.timezone,nextRun:W.nextRun?W.nextRun():null});return U}static listLocks(){return Array.from(Y.activeLocks.keys())}async acquireLock(U){if(Y.activeLocks.has(U))return!1;let V=this.overlapExpiresAfterMinutes*60*1000,W=null,Z=null;if(this.shouldRunOnOneServer)try{let{db:Q}=await import("@stacksjs/database");Z=Q;let G=(await import("@stacksjs/env")).env.DB_CONNECTION||"sqlite";if(G==="postgres"||G==="mysql"||G==="sqlite")W=G}catch(Q){H.warn(`[scheduler] onOneServer() couldn't reach DB; falling back to file-only lock (which only serializes within this process): ${Q instanceof Error?Q.message:String(Q)}`)}let $=await f(U,V,W,Z,Y.lockDir);if(!$)return!1;return Y.activeLocks.set(U,$),!0}async releaseLock(U){let V=Y.activeLocks.get(U);if(!V)return;Y.activeLocks.delete(U),await V.release()}wrapTask(U){let V=this.options.name||"unnamed-task",W=U;if(this.shouldPreventOverlap||this.shouldRunOnOneServer){let Z=this,$=W;W=()=>{(async()=>{if(!await Z.acquireLock(V)){H.info(`Skipping overlapping task: ${V}`);return}try{let G=$();if(G&&typeof G==="object"&&typeof G.finally==="function")await G}catch(G){H.error(`[scheduler] task ${V} threw: ${G instanceof Error?G.message:String(G)}`)}finally{await Z.releaseLock(V)}})()}}if(this.shouldRunInBackground){let Z=W,$=this.outputPath,Q=this.outputAppend;W=()=>{let G="inherit",X="inherit",B=null;if($)try{let{openSync:O}=_("fs"),J=O($,Q?"a":"w");G=J,X=J,B=J}catch(O){H.warn(`[scheduler] couldn't open ${$} for background task ${V}: ${O instanceof Error?O.message:String(O)}; falling back to inherit`)}let q=l(process.execPath,["-e",`(${Z.toString()})()`],{detached:!0,stdio:["ignore",G,X]});q.on("error",(O)=>{if(H.error(`Background task ${V} failed to spawn: ${O.message}`),B!==null)try{let{closeSync:J}=_("fs");J(B)}catch{}}),q.on("exit",(O,J)=>{if(O!==0&&O!==null)H.error(`[scheduler] background task ${V} (pid: ${q.pid}) exited with code ${O}`);else if(J)H.warn(`[scheduler] background task ${V} (pid: ${q.pid}) terminated by signal ${J}`);if(B!==null)try{let{closeSync:L}=_("fs");L(B)}catch{}}),q.unref(),H.info(`Task ${V} spawned in background (pid: ${q.pid})`)}}return W}getNextRunTime(){if(!this.cronPattern||this.intervalMs!==null)return null;if(!this.timezone||this.timezone==="UTC")return K(this.cronPattern);return K(this.cronPattern,void 0,{tz:this.timezone})}start(){if(!this.cronPattern&&this.intervalMs===null)return{stop:()=>{},nextRun:()=>null};let U=this.options.name?`${this.options.name.toLowerCase().replace(/[^a-z0-9]+/g,"")}::${this.cronPattern||`every:${this.intervalMs}`}`:null;if(U){let O=Y.scheduledKeys.get(U);if(O)return H.debug(`[scheduler] duplicate schedule for "${this.options.name}" (${this.cronPattern||`every ${this.intervalMs}ms`}) skipped`),O}let V=this.wrapTask(this.task),W=!1,Z=null,$=0,Q=()=>{if(W=!0,Z!==null){if(this.intervalMs!==null)clearInterval(Z);else clearTimeout(Z);Z=null}},X={stop:Q,nextRun:()=>{return this.getNextRunTime()},pattern:this.intervalMs!==null?`every ${this.intervalMs/1000}s`:this.cronPattern,timezone:this.timezone,name:this.options.name},B=this.options.name||"unnamed-task",q=()=>{if(this.options.maxRuns&&$>=this.options.maxRuns){Q();return}$++;try{let O=V();if(O&&typeof O.then==="function")O.catch((J)=>{if(this.options.catch)this.options.catch(J);else H.error(`[scheduler] task ${B} async error (no .catch handler installed): ${J instanceof Error?J.message:String(J)}`)})}catch(O){if(this.options.catch)this.options.catch(O);else H.error(`[scheduler] task ${B} sync error (no .catch handler installed): ${O instanceof Error?O.message:String(O)}`)}};if(this.intervalMs!==null)Z=setInterval(()=>{if(W)return;q()},this.intervalMs),Z.unref?.();else if(this.cronPattern){let J=()=>{if(W)return;if(this.options.maxRuns&&$>=this.options.maxRuns)return;let L=this.getNextRunTime();if(!L)return;let I=Math.max(L.getTime()-Date.now(),0);if(I>2147483647)Z=setTimeout(()=>{if(W)return;J()},2147483647);else Z=setTimeout(()=>{if(W)return;q(),J()},I)};J()}if(this.options.name)Y.jobs.set(this.options.name,X);if(U)Y.scheduledKeys.set(U,X);return H.info(`Scheduled task with pattern: ${this.cronPattern} in timezone: ${this.timezone}`),X}static job(U){return new Y(async()=>{H.info(`Running job: ${U}`);try{await a(U)}catch(V){throw H.error(`Job ${U} failed:`,V),V}}).withName(U)}static action(U){return new Y(async()=>{H.info(`Running action: ${U}`);try{await t(U)}catch(V){throw H.error(`Action ${U} failed:`,V),V}}).withName(U)}static command(U){let V=null;return V=new Y(async()=>{let Z=V?.outputPath??null,$=V?.outputAppend??!1;try{if(H.info(`Executing command: ${U}`),Z){let{spawn:G}=await import("child_process"),{openSync:X,closeSync:B}=await import("fs"),O=X(Z,$?"a":"w");try{await new Promise((J,L)=>{let I=G(U,{shell:!0,stdio:["ignore",O,O]});I.on("error",L),I.on("exit",(M)=>{if(M===0)J();else L(Error(`Command '${U}' exited with code ${M}`))})})}finally{B(O)}return}let Q=await r(U);if(Q.isErr)throw H.error(Q.error),Q.error}catch(Q){throw H.error(`Command execution failed: ${Q}`),Q}}),V.withName(`command-${U}`)}static notification(U,V,W,Z){return new Y(async()=>{try{let Q=(await import("@stacksjs/notifications").catch(()=>null))?.notify;if(!Q){H.error("schedule.notification: @stacksjs/notifications.notify is not available \u2014 install / wire the notifications package.");return}await Q(U,V,W,Z)}catch($){throw H.error("Scheduled notification failed:",$),$}}).withName("notification")}static async gracefulShutdown(){H.info("Gracefully shutting down scheduled jobs...");for(let[U,V]of Y.jobs)H.info(`Stopping job: ${U}`),V.stop();Y.jobs.clear(),H.info("All jobs have been stopped")}}class s extends Y{}function e(U){if(U instanceof Date)return U>new Date?U:null;return K(U)}function AU(U){let V=e(U);if(!V)return-1;return V.getTime()-Date.now()}var MU=Y;export{AU as timeout,e as sendAt,MU as schedule,GU as runScheduler,Y as Schedule,s as Queue};
package/dist/run.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import type { Err, Ok } from '@stacksjs/error-handling';
2
+ export declare function runScheduler(): Promise<Ok<string, never> | Err<string, any>>;
@@ -0,0 +1,79 @@
1
+ import type { CatchCallbackFn } from '@stacksjs/cron';
2
+ import type { ScheduledJob, TimedSchedule, Timezone, UntimedSchedule } from './types';
3
+ /**
4
+ * Get the next run time for a cron expression.
5
+ *
6
+ * @param cronExpression - Standard 5-field cron expression or nickname
7
+ * @returns The next Date the expression matches, or null
8
+ */
9
+ export declare function sendAt(cronExpression: string | Date): Date | null;
10
+ /**
11
+ * Get the number of milliseconds until the next run of a cron expression.
12
+ *
13
+ * @param cronExpression - Standard 5-field cron expression or nickname
14
+ * @returns Milliseconds until next run, or -1 if no upcoming run
15
+ */
16
+ export declare function timeout(cronExpression: string | Date): number;
17
+ export declare const schedule: Scheduler;
18
+ export type Scheduler = typeof Schedule;
19
+ /**
20
+ * Schedule class for creating and managing scheduled tasks.
21
+ *
22
+ * Uses standard 5-field cron patterns (minute hour day month weekday)
23
+ * powered by `@stacksjs/cron` — auto-upgrades to native `Bun.cron.parse()`
24
+ * when available.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * import { schedule } from '@stacksjs/scheduler'
29
+ *
30
+ * schedule.job('Inspire').hourly().setTimeZone('America/Los_Angeles')
31
+ * schedule.action('CleanupTempFiles').everyFiveMinutes()
32
+ * schedule.command('echo "maintenance"').daily()
33
+ * ```
34
+ */
35
+ export declare class Schedule implements UntimedSchedule {
36
+ constructor(task: () => void);
37
+ get pattern(): string;
38
+ everySecond(): TimedSchedule;
39
+ everyMinute(): TimedSchedule;
40
+ everyTwoMinutes(): TimedSchedule;
41
+ everyFiveMinutes(): TimedSchedule;
42
+ everyTenMinutes(): TimedSchedule;
43
+ everyThirtyMinutes(): TimedSchedule;
44
+ everyHour(): TimedSchedule;
45
+ everyDay(): TimedSchedule;
46
+ hourly(): TimedSchedule;
47
+ daily(): TimedSchedule;
48
+ weekly(): TimedSchedule;
49
+ monthly(): TimedSchedule;
50
+ yearly(): TimedSchedule;
51
+ annually(): TimedSchedule;
52
+ onDays(days: number[]): TimedSchedule;
53
+ at(time: string): TimedSchedule;
54
+ setTimeZone(timezone: Timezone): this;
55
+ withErrorHandler(handler: CatchCallbackFn): this;
56
+ withMaxRuns(runs: number): this;
57
+ withProtection(callback?: (job: ScheduledJob) => void): this;
58
+ withName(name: string): this;
59
+ withContext(context: any): this;
60
+ withInterval(seconds: number): this;
61
+ between(startAt: string | Date, stopAt: string | Date): this;
62
+ withoutOverlapping(expiresAfterMinutes?: number): this;
63
+ onOneServer(): this;
64
+ runInBackground(): this;
65
+ sendOutputTo(path: string): this;
66
+ appendOutputTo(path: string): this;
67
+ runMissed(opts: { since: Date | number, max?: number }): Promise<number>;
68
+ static listJobs(): Array<{ name: string, pattern?: string, timezone?: Timezone, nextRun: Date | null }>;
69
+ static listLocks(): string[];
70
+ static job(name: string): UntimedSchedule;
71
+ static action(name: string): UntimedSchedule;
72
+ static command(cmd: string): UntimedSchedule;
73
+ static notification(recipient: unknown, payload: unknown, channels?: unknown, options?: unknown): UntimedSchedule;
74
+ static gracefulShutdown(): Promise<void>;
75
+ }
76
+ export declare class Queue extends Schedule {
77
+
78
+ }
79
+ export default Schedule;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Try to acquire a distributed scheduler lock for `taskName`. The
3
+ * lock is two-layered:
4
+ * 1. **DB advisory lock** (PG/MySQL) — serializes across the cluster.
5
+ * 2. **File lock** — serializes within the local process and
6
+ * provides a fallback when the DB connection isn't available.
7
+ *
8
+ * Returns a handle whose `release()` MUST be called in a finally so
9
+ * the lock doesn't leak. Returns `null` (not an error) when the lock
10
+ * can't be acquired — scheduler tasks SKIP rather than throw on
11
+ * lock contention.
12
+ *
13
+ * @param taskName - human-readable name (logged + used in lock key)
14
+ * @param expiryMs - file-lock staleness threshold; matches the
15
+ * pre-fix overlapExpiresAfterMinutes semantics
16
+ * @param dialect - SQL dialect from `getDialect()`; when 'sqlite' or
17
+ * `null` the function uses file-lock-only mode
18
+ * @param adminDb - bun-query-builder connection for advisory SQL;
19
+ * `null` falls back to file-lock-only
20
+ * @param lockDir - file-lock directory; default is `storage/framework/locks`
21
+ */
22
+ export declare function acquireSchedulerLock(taskName: string, expiryMs: number, dialect: Dialect | null, adminDb: { unsafe: (sql: string) => Promise<unknown> } | null, lockDir?: string): Promise<SchedulerLockHandle | null>;
23
+ export declare interface SchedulerLockHandle {
24
+ release: () => Promise<void>
25
+ }
26
+ export type Dialect = 'sqlite' | 'mysql' | 'postgres';
@@ -0,0 +1,110 @@
1
+ import type { CatchCallbackFn } from '@stacksjs/cron';
2
+ export declare interface ScheduledJob {
3
+ stop: () => void
4
+ nextRun: () => Date | null
5
+ pattern?: string
6
+ timezone?: Timezone
7
+ name?: string
8
+ }
9
+ // Base interface for common methods
10
+ export declare interface BaseSchedule {
11
+ withErrorHandler: (handler: CatchCallbackFn) => this
12
+ withMaxRuns: (runs: number) => this
13
+ withProtection: (callback?: (job: ScheduledJob) => void) => this
14
+ withName: (name: string) => this
15
+ withContext: (context: any) => this
16
+ withInterval: (seconds: number) => this
17
+ between: (startAt: string | Date, stopAt: string | Date) => this
18
+ setTimeZone: (timezone: Timezone) => this
19
+ withoutOverlapping: (expiresAfterMinutes?: number) => this
20
+ onOneServer: () => this
21
+ runInBackground: () => this
22
+ }
23
+ // Interface for schedule after timing is set
24
+ export declare interface TimedSchedule extends BaseSchedule {
25
+ }
26
+ // Interface for schedule before timing is set
27
+ export declare interface UntimedSchedule extends BaseSchedule {
28
+ everySecond: () => TimedSchedule
29
+ everyMinute: () => TimedSchedule
30
+ everyTwoMinutes: () => TimedSchedule
31
+ everyFiveMinutes: () => TimedSchedule
32
+ everyTenMinutes: () => TimedSchedule
33
+ everyThirtyMinutes: () => TimedSchedule
34
+ everyHour: () => TimedSchedule
35
+ everyDay: () => TimedSchedule
36
+ hourly: () => TimedSchedule
37
+ daily: () => TimedSchedule
38
+ weekly: () => TimedSchedule
39
+ monthly: () => TimedSchedule
40
+ yearly: () => TimedSchedule
41
+ annually: () => TimedSchedule
42
+ onDays: (days: number[]) => TimedSchedule
43
+ at: (time: string) => TimedSchedule
44
+ }
45
+ // IANA Timezone
46
+ export type Timezone = | 'Africa/Abidjan'
47
+ | 'Africa/Accra'
48
+ | 'Africa/Addis_Ababa'
49
+ | 'Africa/Algiers'
50
+ | 'Africa/Cairo'
51
+ | 'Africa/Casablanca'
52
+ | 'Africa/Lagos'
53
+ | 'Africa/Nairobi'
54
+ | 'America/Anchorage'
55
+ | 'America/Argentina/Buenos_Aires'
56
+ | 'America/Bogota'
57
+ | 'America/Caracas'
58
+ | 'America/Chicago'
59
+ | 'America/Denver'
60
+ | 'America/Detroit'
61
+ | 'America/Halifax'
62
+ | 'America/Los_Angeles'
63
+ | 'America/Mexico_City'
64
+ | 'America/New_York'
65
+ | 'America/Phoenix'
66
+ | 'America/Santiago'
67
+ | 'America/Sao_Paulo'
68
+ | 'America/Toronto'
69
+ | 'America/Vancouver'
70
+ | 'Asia/Bangkok'
71
+ | 'Asia/Dubai'
72
+ | 'Asia/Hong_Kong'
73
+ | 'Asia/Istanbul'
74
+ | 'Asia/Jakarta'
75
+ | 'Asia/Jerusalem'
76
+ | 'Asia/Karachi'
77
+ | 'Asia/Kolkata'
78
+ | 'Asia/Kuwait'
79
+ | 'Asia/Manila'
80
+ | 'Asia/Shanghai'
81
+ | 'Asia/Singapore'
82
+ | 'Asia/Tokyo'
83
+ | 'Australia/Melbourne'
84
+ | 'Australia/Perth'
85
+ | 'Australia/Sydney'
86
+ | 'Europe/Amsterdam'
87
+ | 'Europe/Athens'
88
+ | 'Europe/Belgrade'
89
+ | 'Europe/Berlin'
90
+ | 'Europe/Brussels'
91
+ | 'Europe/Budapest'
92
+ | 'Europe/Copenhagen'
93
+ | 'Europe/Dublin'
94
+ | 'Europe/Helsinki'
95
+ | 'Europe/Istanbul'
96
+ | 'Europe/Lisbon'
97
+ | 'Europe/London'
98
+ | 'Europe/Madrid'
99
+ | 'Europe/Moscow'
100
+ | 'Europe/Paris'
101
+ | 'Europe/Prague'
102
+ | 'Europe/Rome'
103
+ | 'Europe/Stockholm'
104
+ | 'Europe/Vienna'
105
+ | 'Europe/Warsaw'
106
+ | 'Europe/Zurich'
107
+ | 'Pacific/Auckland'
108
+ | 'Pacific/Fiji'
109
+ | 'Pacific/Honolulu'
110
+ | 'UTC';
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/scheduler",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.70.88",
5
+ "version": "0.70.91",
6
6
  "description": "The Stacks scheduler.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -53,8 +53,8 @@
53
53
  "prepublishOnly": "bun run build"
54
54
  },
55
55
  "devDependencies": {
56
- "@stacksjs/actions": "0.70.88",
57
- "@stacksjs/cron": "0.70.88",
56
+ "@stacksjs/actions": "0.70.91",
57
+ "@stacksjs/cron": "0.70.91",
58
58
  "better-dx": "^0.2.16"
59
59
  }
60
60
  }