@lunora/scheduler 1.0.0-alpha.35 → 1.0.0-alpha.37
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/dist/index.d.mts
CHANGED
|
@@ -349,8 +349,12 @@ interface QueueWorkpool {
|
|
|
349
349
|
shardKey?: string;
|
|
350
350
|
}>, options?: QueueSendOptionsLike) => Promise<void>;
|
|
351
351
|
}
|
|
352
|
-
/**
|
|
353
|
-
|
|
352
|
+
/**
|
|
353
|
+
* Dispatches a single {@link QueueJob} — the consumer's per-message worker.
|
|
354
|
+
* `messageId` is the queue message's native id, threaded through so the
|
|
355
|
+
* dispatcher can attribute a failure to the exact message that caused it.
|
|
356
|
+
*/
|
|
357
|
+
type QueueDispatch = (job: QueueJob, messageId?: string) => Promise<void>;
|
|
354
358
|
/** Options for `createQueueConsumer`. */
|
|
355
359
|
interface QueueConsumerOptions {
|
|
356
360
|
/** How each job is executed; e.g. the `httpDispatcher`. */
|
|
@@ -364,6 +368,13 @@ interface HttpDispatcherOptions {
|
|
|
364
368
|
fetchImpl?: typeof fetch;
|
|
365
369
|
/** Origin where the Worker is mounted (the `/_lunora/scheduler/dispatch` endpoint). */
|
|
366
370
|
originUrl: string;
|
|
371
|
+
/**
|
|
372
|
+
* Abort a job's dispatch after this many ms; the abort is retryable, so the
|
|
373
|
+
* consumer retries the message. Defaults to 5 minutes — raise it for a
|
|
374
|
+
* workpool running jobs that legitimately run longer, lower it to fail a
|
|
375
|
+
* stuck origin faster.
|
|
376
|
+
*/
|
|
377
|
+
timeoutMs?: number;
|
|
367
378
|
}
|
|
368
379
|
/**
|
|
369
380
|
* Client-side scheduler — forwards `runAfter` / `runAt` / `cancel` calls to a
|
|
@@ -550,10 +561,16 @@ declare const createQueueWorkpool: (options: QueueWorkpoolOptions) => QueueWorkp
|
|
|
550
561
|
*/
|
|
551
562
|
declare const createQueueConsumer: (options: QueueConsumerOptions) => ((batch: MessageBatchLike) => Promise<void>);
|
|
552
563
|
/**
|
|
553
|
-
* Default {@link QueueDispatch}:
|
|
564
|
+
* Default {@link QueueDispatch}: dispatch each job to the Worker's
|
|
554
565
|
* `/_lunora/scheduler/dispatch` endpoint (the same path SchedulerDO dispatches
|
|
555
|
-
* through)
|
|
556
|
-
*
|
|
566
|
+
* through) via `@lunora/dispatch`'s `createDispatchRunner` — which bounds each
|
|
567
|
+
* job with {@link HttpDispatcherOptions.timeoutMs} (default
|
|
568
|
+
* {@link DEFAULT_JOB_TIMEOUT_MS}), so a hung origin no longer holds the whole
|
|
569
|
+
* `queue()` invocation open, and threads the queue message id through for
|
|
570
|
+
* failure attribution. Any dispatch failure throws so the consumer retries the
|
|
571
|
+
* message — including a 2xx carrying a non-empty non-JSON body, which is an
|
|
572
|
+
* intermediary's page rather than a function's return value and therefore no
|
|
573
|
+
* evidence the job ran. An empty 2xx is a normal success (a `void` function).
|
|
557
574
|
*/
|
|
558
575
|
declare const httpDispatcher: (options: HttpDispatcherOptions) => QueueDispatch;
|
|
559
576
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -349,8 +349,12 @@ interface QueueWorkpool {
|
|
|
349
349
|
shardKey?: string;
|
|
350
350
|
}>, options?: QueueSendOptionsLike) => Promise<void>;
|
|
351
351
|
}
|
|
352
|
-
/**
|
|
353
|
-
|
|
352
|
+
/**
|
|
353
|
+
* Dispatches a single {@link QueueJob} — the consumer's per-message worker.
|
|
354
|
+
* `messageId` is the queue message's native id, threaded through so the
|
|
355
|
+
* dispatcher can attribute a failure to the exact message that caused it.
|
|
356
|
+
*/
|
|
357
|
+
type QueueDispatch = (job: QueueJob, messageId?: string) => Promise<void>;
|
|
354
358
|
/** Options for `createQueueConsumer`. */
|
|
355
359
|
interface QueueConsumerOptions {
|
|
356
360
|
/** How each job is executed; e.g. the `httpDispatcher`. */
|
|
@@ -364,6 +368,13 @@ interface HttpDispatcherOptions {
|
|
|
364
368
|
fetchImpl?: typeof fetch;
|
|
365
369
|
/** Origin where the Worker is mounted (the `/_lunora/scheduler/dispatch` endpoint). */
|
|
366
370
|
originUrl: string;
|
|
371
|
+
/**
|
|
372
|
+
* Abort a job's dispatch after this many ms; the abort is retryable, so the
|
|
373
|
+
* consumer retries the message. Defaults to 5 minutes — raise it for a
|
|
374
|
+
* workpool running jobs that legitimately run longer, lower it to fail a
|
|
375
|
+
* stuck origin faster.
|
|
376
|
+
*/
|
|
377
|
+
timeoutMs?: number;
|
|
367
378
|
}
|
|
368
379
|
/**
|
|
369
380
|
* Client-side scheduler — forwards `runAfter` / `runAt` / `cancel` calls to a
|
|
@@ -550,10 +561,16 @@ declare const createQueueWorkpool: (options: QueueWorkpoolOptions) => QueueWorkp
|
|
|
550
561
|
*/
|
|
551
562
|
declare const createQueueConsumer: (options: QueueConsumerOptions) => ((batch: MessageBatchLike) => Promise<void>);
|
|
552
563
|
/**
|
|
553
|
-
* Default {@link QueueDispatch}:
|
|
564
|
+
* Default {@link QueueDispatch}: dispatch each job to the Worker's
|
|
554
565
|
* `/_lunora/scheduler/dispatch` endpoint (the same path SchedulerDO dispatches
|
|
555
|
-
* through)
|
|
556
|
-
*
|
|
566
|
+
* through) via `@lunora/dispatch`'s `createDispatchRunner` — which bounds each
|
|
567
|
+
* job with {@link HttpDispatcherOptions.timeoutMs} (default
|
|
568
|
+
* {@link DEFAULT_JOB_TIMEOUT_MS}), so a hung origin no longer holds the whole
|
|
569
|
+
* `queue()` invocation open, and threads the queue message id through for
|
|
570
|
+
* failure attribution. Any dispatch failure throws so the consumer retries the
|
|
571
|
+
* message — including a 2xx carrying a non-empty non-JSON body, which is an
|
|
572
|
+
* intermediary's page rather than a function's return value and therefore no
|
|
573
|
+
* evidence the job ran. An empty 2xx is a normal success (a `void` function).
|
|
557
574
|
*/
|
|
558
575
|
declare const httpDispatcher: (options: HttpDispatcherOptions) => QueueDispatch;
|
|
559
576
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{default as o}from"./packem_shared/createScheduler-DrXa1iXV.mjs";import{default as a}from"./packem_shared/createWorkpool-d3THj5gc.mjs";import{createCronTrigger as c}from"./packem_shared/createCronTrigger-DSuDZtqj.mjs";import{CRON_SCHEDULE_KINDS as f,compileCronSchedule as l,cronJobs as n}from"./packem_shared/CRON_SCHEDULE_KINDS-xyFk800s.mjs";import{createQueueConsumer as u,createQueueWorkpool as x,httpDispatcher as d}from"./packem_shared/createQueueConsumer-
|
|
1
|
+
import{default as o}from"./packem_shared/createScheduler-DrXa1iXV.mjs";import{default as a}from"./packem_shared/createWorkpool-d3THj5gc.mjs";import{createCronTrigger as c}from"./packem_shared/createCronTrigger-DSuDZtqj.mjs";import{CRON_SCHEDULE_KINDS as f,compileCronSchedule as l,cronJobs as n}from"./packem_shared/CRON_SCHEDULE_KINDS-xyFk800s.mjs";import{createQueueConsumer as u,createQueueWorkpool as x,httpDispatcher as d}from"./packem_shared/createQueueConsumer-DqxvbEwB.mjs";import{MAX_RETRY_ATTEMPTS as S,RETRY_BASE_DELAY_MS as E,SchedulerDO as C}from"./packem_shared/MAX_RETRY_ATTEMPTS-DU_w9jpi.mjs";import{createSchedulerHost as h}from"./packem_shared/createSchedulerHost-C4HUMyFs.mjs";import{isWorkflowReference as T}from"./packem_shared/isWorkflowReference-CT3tdefh.mjs";import{assertValidCronExpression as A,isValidCronExpression as g,warnIfSecondsLeading as k}from"./packem_shared/assertValidCronExpression-DnBtukpq.mjs";export{f as CRON_SCHEDULE_KINDS,S as MAX_RETRY_ATTEMPTS,E as RETRY_BASE_DELAY_MS,C as SchedulerDO,A as assertValidCronExpression,l as compileCronSchedule,c as createCronTrigger,u as createQueueConsumer,x as createQueueWorkpool,o as createScheduler,h as createSchedulerHost,a as createWorkpool,n as cronJobs,d as httpDispatcher,g as isValidCronExpression,T as isWorkflowReference,k as warnIfSecondsLeading};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as c}from"@lunora/errors";const A=(e,t,n)=>{if(t===void 0)return{dispose:()=>{},signal:e};const r=new AbortController,o=setTimeout(()=>{r.abort(n())},t);return{dispose:()=>{clearTimeout(o)},signal:r.signal}},b=e=>{let t="";for(let n=0;n<e.length;n+=32768)t+=String.fromCharCode(...e.subarray(n,n+32768));return btoa(t)},_=e=>b(e).replaceAll("+","-").replaceAll("/","_").replace(/=+$/,"");new TextDecoder;const I=new TextEncoder,m="=",O=e=>{for(let t=0;t<e.length;t+=1)if(e.charCodeAt(t)>255)return!1;return!0},E=e=>_(I.encode(JSON.stringify(e))),$=e=>!e.startsWith(m)&&O(e)?e:`${m}${_(I.encode(e))}`,L="/_lunora/scheduler/dispatch",S=3e4,v=e=>{let t=e.length;for(;t>0&&e[t-1]==="/";)t-=1;return e.slice(0,t)},U=Symbol("lunoraDispatchFailure"),D=Symbol("lunoraDispatchMessageId"),N=(e,t)=>(Object.defineProperty(e,U,{value:!0}),t!==void 0&&Object.defineProperty(e,D,{value:t}),e),K=(e,t,n,r)=>{try{const o=JSON.parse(n)?.error;if(typeof o=="object"&&o!==null&&typeof o.code=="string"){const{code:s,data:a,message:i}=o;return N(new c(s,typeof i=="string"?i:void 0,{data:a,status:t}),r)}}catch{}return N(new c("INTERNAL",`${e}: function dispatch failed (${String(t)}): ${n}`,{status:t}),r)},M=(e,t,n)=>new c("INTERNAL",`${e}: function dispatch to "${t}" timed out after ${String(n)}ms`,{status:503}),P=e=>{const{label:t}=e,n=globalThis.fetch,r=e.fetchImpl??(typeof n=="function"?n.bind(globalThis):void 0);return async(o,s,a={})=>{if(typeof r!="function")throw new TypeError(`${t}: no fetch implementation available — pass fetchImpl or run on a platform with global fetch`);const i=e.env.LUNORA_ORIGIN_URL;if(typeof i!="string"||i.length===0)throw new c("INTERNAL",`${t}: \`LUNORA_ORIGIN_URL\` must be set on the Worker env so a handler can call back into Lunora functions`);const h=e.env.LUNORA_ADMIN_TOKEN;if(typeof h!="string"||h.length===0)throw new c("INTERNAL",`${t}: \`LUNORA_ADMIN_TOKEN\` must be set on the Worker env to authenticate function dispatch`);const R=`${v(i)}${L}`,f={authorization:`Bearer ${h}`,"content-type":"application/json"};e.identity?.userId!==void 0&&(f["x-lunora-userid"]=$(e.identity.userId)),e.identity?.claims!==void 0&&(f["x-lunora-identity"]=E(e.identity.claims));const y=a.timeoutMs??S,g=A(void 0,y,()=>new DOMException(`dispatch timed out after ${String(y)}ms`,"TimeoutError")),p=u=>{throw u instanceof Error&&u.name==="TimeoutError"?M(t,o.__lunoraRef,y):u};let l;try{try{l=await r(R,{body:JSON.stringify({args:s??{},functionPath:o.__lunoraRef,id:a.dedupId,shardKey:a.shardKey}),headers:f,method:"POST",signal:g.signal})}catch(d){return p(d)}if(!l.ok){let d;try{d=await l.text()}catch(T){return p(T)}throw K(t,l.status,d,a.messageId)}let u;try{u=await l.text()}catch(d){return p(d)}if(u.length===0)return;try{return JSON.parse(u)}catch{throw new c("INTERNAL",`${t}: function dispatch returned a non-JSON body (${String(l.status)}): ${u}`,{status:l.status})}}finally{g.dispose()}}},w=100,x=3e5,k=e=>{if(!e.queue)throw new c("INTERNAL","@lunora/scheduler: `queue` (a Cloudflare Queue binding) is required");return{enqueue:async(r,o,s={})=>{const a={args:o,functionPath:r.__lunoraRef,shardKey:s.shardKey},i=s.delaySeconds===void 0?void 0:{delaySeconds:s.delaySeconds};await e.queue.send(a,i)},enqueueBatch:async(r,o)=>{if(r.length>w)throw new c("VALIDATION_ERROR",`@lunora/scheduler: enqueueBatch exceeds ${String(w)} (got ${String(r.length)}) — split across calls`);const s=r.map(a=>({body:{args:a.args,functionPath:a.ref.__lunoraRef,shardKey:a.shardKey}}));await e.queue.sendBatch(s,o)}}},q=e=>typeof e=="object"&&e!==null&&typeof e.functionPath=="string",B=e=>async t=>{await Promise.all(t.messages.map(async n=>{try{if(!q(n.body))throw new c("INTERNAL","@lunora/scheduler: queue message body is not a QueueJob (missing functionPath)");await e.dispatch(n.body,n.id),n.ack()}catch{n.retry()}}))},C=e=>{const t=P({env:{LUNORA_ADMIN_TOKEN:e.adminToken,LUNORA_ORIGIN_URL:e.originUrl},fetchImpl:e.fetchImpl,label:"@lunora/scheduler"}),n=e.timeoutMs??x;return async(r,o)=>{await t({__lunoraRef:r.functionPath},r.args,{messageId:o,shardKey:r.shardKey,timeoutMs:n})}};export{B as createQueueConsumer,k as createQueueWorkpool,C as httpDispatcher};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/scheduler",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.37",
|
|
4
4
|
"description": "Scheduling for Lunora: runAfter / runAt and Cron Triggers via SchedulerDO",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@lunora/errors": "1.0.0-alpha.22",
|
|
50
|
-
"@lunora/platform": "1.0.0-alpha.
|
|
50
|
+
"@lunora/platform": "1.0.0-alpha.17",
|
|
51
51
|
"cron-parser": "5.8.1"
|
|
52
52
|
},
|
|
53
53
|
"engines": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{LunoraError as c}from"@lunora/errors";const u=100,h=e=>{let t=e.length;for(;t>0&&e[t-1]==="/";)t-=1;return e.slice(0,t)},f=e=>{if(!e.queue)throw new c("INTERNAL","@lunora/scheduler: `queue` (a Cloudflare Queue binding) is required");return{enqueue:async(n,r,o={})=>{const s={args:r,functionPath:n.__lunoraRef,shardKey:o.shardKey},i=o.delaySeconds===void 0?void 0:{delaySeconds:o.delaySeconds};await e.queue.send(s,i)},enqueueBatch:async(n,r)=>{if(n.length>u)throw new c("VALIDATION_ERROR",`@lunora/scheduler: enqueueBatch exceeds ${String(u)} (got ${String(n.length)}) — split across calls`);const o=n.map(s=>({body:{args:s.args,functionPath:s.ref.__lunoraRef,shardKey:s.shardKey}}));await e.queue.sendBatch(o,r)}}},l=e=>typeof e=="object"&&e!==null&&typeof e.functionPath=="string",y=e=>async t=>{await Promise.all(t.messages.map(async a=>{try{if(!l(a.body))throw new c("INTERNAL","@lunora/scheduler: queue message body is not a QueueJob (missing functionPath)");await e.dispatch(a.body),a.ack()}catch{a.retry()}}))},p=e=>{const t=e.fetchImpl??globalThis.fetch;if(typeof t!="function")throw new TypeError("@lunora/scheduler: no fetch implementation available — pass fetchImpl or run on a platform with global fetch");const a=`${h(e.originUrl)}/_lunora/scheduler/dispatch`;return async n=>{const r=await t(a,{body:JSON.stringify({args:n.args??{},functionPath:n.functionPath,shardKey:n.shardKey}),headers:{authorization:`Bearer ${e.adminToken}`,"content-type":"application/json"},method:"POST"});if(!r.ok)throw new c("INTERNAL",`@lunora/scheduler: queue dispatch failed (${r.status.toString()}): ${await r.text()}`)}};export{y as createQueueConsumer,f as createQueueWorkpool,p as httpDispatcher};
|