@upstash/qstash 2.7.0 → 2.7.1

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
@@ -131,9 +131,29 @@ const result = await client.publishJSON({
131
131
  See [the documentation](https://docs.upstash.com/qstash) for details.
132
132
 
133
133
  ## Contributing
134
+ ### Setup
135
+ This project requires [Bun](https://bun.sh/) to be installed. Please see the [Bun installation documentation](https://bun.sh/docs/installation) for further instructions.
134
136
 
135
- ### [Install Deno](https://deno.land/#installation)
137
+ Once you have cloned the project, you will need to install the dependencies and then you can run the project.
138
+ ```sh
139
+ bun install
140
+ bun run build
141
+ ```
136
142
 
143
+ ### Testing
144
+ To begin testing, environment variables will need to be setup. First, create a `.env` file in the root of the project. [`.env.template`](/.env.template) can be used as a template. Your values can be found in the [Qstash Console](https://console.upstash.com/qstash).
145
+ ```sh
146
+ bun run test
137
147
  ```
138
148
 
149
+ ### Committing
150
+ This project uses [commitlint](https://commitlint.js.org/). When committing, please ensure your commit message is formatted to include an appropriate prefix with the message.
151
+
152
+ #### Examples ####
139
153
  ```
154
+ fix: typescript bug
155
+ feat: use new logger
156
+ perf: refactor cache
157
+ ```
158
+ For a full list of options, please see the [`commitlint.config.js`](/commitlint.config.js) file.
159
+
@@ -1635,7 +1635,7 @@ type VerifySignatureConfig = {
1635
1635
  declare function verifySignature(handler: NextApiHandler, config?: VerifySignatureConfig): NextApiHandler;
1636
1636
  declare function verifySignatureEdge(handler: (request: NextRequest, nfe?: NextFetchEvent) => NextResponse | Promise<NextResponse>, config?: VerifySignatureConfig): (request: NextRequest, nfe: NextFetchEvent) => Promise<NextResponse<unknown>>;
1637
1637
  type VerifySignatureAppRouterResponse = NextResponse | Promise<NextResponse> | Response | Promise<Response>;
1638
- declare function verifySignatureAppRouter(handler: ((request: Request) => VerifySignatureAppRouterResponse) | ((request: NextRequest) => VerifySignatureAppRouterResponse), config?: VerifySignatureConfig): (request: NextRequest | Request) => Promise<Response>;
1638
+ declare function verifySignatureAppRouter(handler: ((request: Request, params?: unknown) => VerifySignatureAppRouterResponse) | ((request: NextRequest, params?: unknown) => VerifySignatureAppRouterResponse), config?: VerifySignatureConfig): (request: NextRequest | Request, params?: unknown) => Promise<Response>;
1639
1639
  /**
1640
1640
  * Serve method to serve a QStash workflow in a Nextjs project
1641
1641
  *
@@ -7,4 +7,4 @@ If you want to disable QStash Verification, you should clear env variables QSTAS
7
7
  > Step Names from the request: ${JSON.stringify(o)}
8
8
  Step Types from the request: ${JSON.stringify(i)}
9
9
  > Step Names expected: ${JSON.stringify(s)}
10
- Step Types expected: ${JSON.stringify(n)}`)}throw t}},ut=r=>{let e=t=>t.targetStep??t.stepId;return r.toSorted((t,s)=>e(t)-e(s))};var O=class{stepName;constructor(e){this.stepName=e}},ue=class extends O{stepFunction;stepType="Run";constructor(e,t){super(e),this.stepFunction=t}getPlanStep(e,t){return{stepId:0,stepName:this.stepName,stepType:this.stepType,concurrent:e,targetStep:t}}async getResultStep(e,t){let s=this.stepFunction();return s instanceof Promise&&(s=await s),{stepId:t,stepName:this.stepName,stepType:this.stepType,out:s,concurrent:e}}},pe=class extends O{sleep;stepType="SleepFor";constructor(e,t){super(e),this.sleep=t}getPlanStep(e,t){return{stepId:0,stepName:this.stepName,stepType:this.stepType,sleepFor:this.sleep,concurrent:e,targetStep:t}}async getResultStep(e,t){return await Promise.resolve({stepId:t,stepName:this.stepName,stepType:this.stepType,sleepFor:this.sleep,concurrent:e})}},ce=class extends O{sleepUntil;stepType="SleepUntil";constructor(e,t){super(e),this.sleepUntil=t}getPlanStep(e,t){return{stepId:0,stepName:this.stepName,stepType:this.stepType,sleepUntil:this.sleepUntil,concurrent:e,targetStep:t}}async getResultStep(e,t){return await Promise.resolve({stepId:t,stepName:this.stepName,stepType:this.stepType,sleepUntil:this.sleepUntil,concurrent:e})}},de=class extends O{url;method;body;headers;stepType="Call";constructor(e,t,s,n,o){super(e),this.url=t,this.method=s,this.body=n,this.headers=o}getPlanStep(e,t){return{stepId:0,stepName:this.stepName,stepType:this.stepType,concurrent:e,targetStep:t}}async getResultStep(e,t){return await Promise.resolve({stepId:t,stepName:this.stepName,stepType:this.stepType,concurrent:e,callUrl:this.url,callMethod:this.method,callBody:this.body,callHeaders:this.headers})}};var I=class{executor;steps;qstashClient;workflowRunId;url;failureUrl;requestPayload;headers;rawInitialPayload;env;constructor({qstashClient:e,workflowRunId:t,headers:s,steps:n,url:o,failureUrl:i,debug:a,initialPayload:l,rawInitialPayload:u,env:c}){this.qstashClient=e,this.workflowRunId=t,this.steps=n,this.url=o,this.failureUrl=i,this.headers=s,this.requestPayload=l,this.rawInitialPayload=u??JSON.stringify(this.requestPayload),this.env=c??{},this.executor=new le(this,this.steps,a)}async run(e,t){let s=()=>this.executor.wrapStep(e,t);return this.addStep(new ue(e,s))}async sleep(e,t){await this.addStep(new pe(e,t))}async sleepUntil(e,t){let s;typeof t=="number"?s=t:(t=typeof t=="string"?new Date(t):t,s=Math.round(t.getTime()/1e3)),await this.addStep(new ce(e,s))}async call(e,t,s,n,o){return await this.addStep(new de(e,t,s,n,o??{}))}async addStep(e){return await this.executor.addStep(e)}},he=class r extends I{static disabledMessage="disabled-qstash-worklfow-run";async addStep(e){throw new b(r.disabledMessage)}static async tryAuthentication(e,t){let s=new r({qstashClient:new N({baseUrl:"disabled-client",token:"disabled-client"}),workflowRunId:t.workflowRunId,headers:t.headers,steps:[],url:t.url,failureUrl:t.failureUrl,initialPayload:t.requestPayload,rawInitialPayload:t.rawInitialPayload,env:t.env});try{await e(s)}catch(n){return n instanceof b&&n.stepName===this.disabledMessage?m("step-found"):y(n)}return m("run-ended")}};var Ke=["DEBUG","INFO","SUBMIT","WARN","ERROR"],me=class r{logs=[];options;workflowRunId=void 0;constructor(e){this.options=e}async log(e,t,s){if(this.shouldLog(e)){let o={timestamp:Date.now(),workflowRunId:this.workflowRunId??"",logLevel:e,eventType:t,details:s};this.logs.push(o),this.options.logOutput==="console"&&this.writeToConsole(o),await new Promise(i=>setTimeout(i,100))}}setWorkflowRunId(e){this.workflowRunId=e}writeToConsole(e){console.log(JSON.stringify(e,void 0,2))}shouldLog(e){return Ke.indexOf(e)>=Ke.indexOf(this.options.logLevel)}static getLogger(e){return typeof e=="object"?e:e?new r({logLevel:"INFO",logOutput:"console"}):void 0}};var Me=async r=>{try{return await r.text()}catch{return}},pt=r=>{let[e,...t]=JSON.parse(r),s=atob(e.body),n={stepId:0,stepName:"init",stepType:"Initial",out:s,concurrent:1},i=t.filter(l=>l.callType==="step").map(l=>JSON.parse(atob(l.body))),a=[n,...i];return{rawInitialPayload:s,steps:a}},ct=r=>{let e=[],t=[],s=[];for(let n of r)n.stepId===0?e.includes(n.targetStep??0)||(s.push(n),e.push(n.targetStep??0)):t.includes(n.stepId)||(s.push(n),t.push(n.stepId));return s},dt=async(r,e)=>{if(r.length<2)return!1;let t=r.at(-1),s=t.stepId,n=t.targetStep;for(let o=0;o<r.length-1;o++){let i=r[o];if(i.stepId===s&&i.targetStep===n){let a=`QStash Workflow: The step '${i.stepName}' with id '${i.stepId}' has run twice during workflow execution. Rest of the workflow will continue running as usual.`;return await e?.log("WARN","RESPONSE_DEFAULT",a),console.warn(a),!0}}return!1},$e=r=>{let e=r.headers.get(oe),t=!e;if(!t&&e!==G)throw new h(`Incompatible workflow sdk protocol version. Expected ${G}, got ${e} from the request.`);let s=t?`wfr_${Ce()}`:r.headers.get(B)??"";if(s.length===0)throw new h("Couldn't get workflow id from header");return{isFirstInvocation:t,workflowRunId:s}},Te=async(r,e,t)=>{if(e)return{rawInitialPayload:r??"",steps:[],isLastDuplicate:!1};{if(!r)throw new h("Only first call can have an empty body");let{rawInitialPayload:s,steps:n}=pt(r),o=await dt(n,t),i=ct(n);return{rawInitialPayload:s,steps:i,isLastDuplicate:o}}},Qe=async(r,e,t,s,n,o)=>{if(r.headers.get(ne)!=="true")return m("not-failure-callback");if(!n)return y(new h("Workflow endpoint is called to handle a failure, but a failureFunction is not provided in serve options. Either provide a failureUrl or a failureFunction."));try{let{status:i,header:a,body:l,url:u,sourceHeader:c,sourceBody:d,workflowRunId:p}=JSON.parse(e),w=l?atob(l):"{}",f=JSON.parse(w),{rawInitialPayload:g,steps:S,isLastDuplicate:fe}=await Te(atob(d),!1,o),q=new I({qstashClient:t,workflowRunId:p,initialPayload:s(g),rawInitialPayload:g,headers:D(new Headers(c)),steps:S,url:u,failureUrl:u,debug:o});await n(q,i,f.message,a)}catch(i){return y(i)}return m("is-failure-callback")};var ht=r=>{let e=r?.env??(typeof process>"u"?{}:process.env),t=!!(e.QSTASH_CURRENT_SIGNING_KEY&&e.QSTASH_NEXT_SIGNING_KEY);return{qstashClient:new N({baseUrl:e.QSTASH_URL,token:e.QSTASH_TOKEN}),onStepFinish:(s,n)=>new Response(JSON.stringify({workflowRunId:s}),{status:200}),initialPayloadParser:s=>{if(s)try{return JSON.parse(s)}catch(n){if(n instanceof SyntaxError)return s;throw n}},receiver:t?new v({currentSigningKey:e.QSTASH_CURRENT_SIGNING_KEY,nextSigningKey:e.QSTASH_NEXT_SIGNING_KEY}):void 0,baseUrl:e.UPSTASH_WORKFLOW_URL,env:e,...r}},Je=(r,e)=>{let{qstashClient:t,onStepFinish:s,initialPayloadParser:n,url:o,verbose:i,receiver:a,failureUrl:l,failureFunction:u,baseUrl:c,env:d}=ht(e),p=me.getLogger(i),w=async f=>{let g=o??f.url,S=c?g.replace(/^(https?:\/\/[^/]+)(\/.*)?$/,(Q,mt,Ye)=>c+(Ye||"")):g;S!==g&&await p?.log("WARN","ENDPOINT_START",{warning:`QStash Workflow: replacing the base of the url with "${c}" and using it as workflow endpoint.`,originalURL:g,updatedURL:S});let fe=u?S:l,q=await Me(f)??"";await De(q,f.headers.get("upstash-signature"),a),await p?.log("INFO","ENDPOINT_START");let ye=await Qe(f,q,t,n,u);if(ye.isErr())throw ye.error;if(ye.value==="is-failure-callback")return await p?.log("WARN","RESPONSE_DEFAULT","failureFunction executed"),s("no-workflow-id","failure-callback");let{isFirstInvocation:Se,workflowRunId:ke}=$e(f);p?.setWorkflowRunId(ke);let{rawInitialPayload:ge,steps:je,isLastDuplicate:ze}=await Te(q,Se,p);if(ze)return s("no-workflow-id","duplicate-step");let L=new I({qstashClient:t,workflowRunId:ke,initialPayload:n(ge),rawInitialPayload:ge,headers:D(f.headers),steps:je,url:S,failureUrl:fe,debug:p,env:d}),M=await he.tryAuthentication(r,L);if(M.isErr())throw await p?.log("ERROR","ERROR",{error:M.error.message}),M.error;if(M.value==="run-ended")return s("no-workflow-id","auth-fail");let $=await Ge(f,ge,t,S,fe,p);if($.isErr())throw await p?.log("ERROR","SUBMIT_THIRD_PARTY_RESULT",{error:$.error.message}),$.error;if($.value==="continue-workflow"){let Q=Se?await He(L,p):await Fe({onStep:async()=>r(L),onCleanup:async()=>{await Be(L,p)}});if(Q.isErr())throw await p?.log("ERROR","ERROR",{error:Q.error.message}),Q.error;return await p?.log("INFO","RESPONSE_WORKFLOW"),s(L.workflowRunId,"success")}return await p?.log("INFO","RESPONSE_DEFAULT"),s("no-workflow-id","fromCallback")};return async f=>{try{return await w(f)}catch(g){return console.error(g),new Response(JSON.stringify(Ee(g)),{status:500})}}};var se=class{http;constructor(e){this.http=e}async cancel(e){return await this.http.request({path:["v2","workflows","runs",`${e}?cancel=true`],method:"DELETE",parseResponseAsJson:!1})??!0}};var Ve=400;function Hr(r,e){let t=e?.currentSigningKey??process.env.QSTASH_CURRENT_SIGNING_KEY;if(!t)throw new Error("currentSigningKey is required, either in the config or as env variable QSTASH_CURRENT_SIGNING_KEY");let s=e?.nextSigningKey??process.env.QSTASH_NEXT_SIGNING_KEY;if(!s)throw new Error("nextSigningKey is required, either in the config or as env variable QSTASH_NEXT_SIGNING_KEY");let n=new v({currentSigningKey:t,nextSigningKey:s});return async(o,i)=>{let a=o.headers["upstash-signature"];if(!a){i.status(Ve),i.send("`Upstash-Signature` header is missing"),i.end();return}if(typeof a!="string")throw new TypeError("`Upstash-Signature` header is not a string");let l=[];for await(let d of o)l.push(typeof d=="string"?Buffer.from(d):d);let u=Buffer.concat(l).toString("utf8");if(!await n.verify({signature:a,body:u,clockTolerance:e?.clockTolerance})){i.status(Ve),i.send("Invalid signature"),i.end();return}try{o.body=o.headers["content-type"]==="application/json"?JSON.parse(u):u}catch{o.body=u}return r(o,i)}}function Fr(r,e){let t=e?.currentSigningKey??process.env.QSTASH_CURRENT_SIGNING_KEY;if(!t)throw new Error("currentSigningKey is required, either in the config or as env variable QSTASH_CURRENT_SIGNING_KEY");let s=e?.nextSigningKey??process.env.QSTASH_NEXT_SIGNING_KEY;if(!s)throw new Error("nextSigningKey is required, either in the config or as env variable QSTASH_NEXT_SIGNING_KEY");let n=new v({currentSigningKey:t,nextSigningKey:s});return async(o,i)=>{let a=o.clone(),l=o.headers.get("upstash-signature");if(!l)return new K(new TextEncoder().encode("`Upstash-Signature` header is missing"),{status:403});if(typeof l!="string")throw new TypeError("`Upstash-Signature` header is not a string");let u=await a.text();return await n.verify({signature:l,body:u,clockTolerance:e?.clockTolerance})?r(o,i):new K(new TextEncoder().encode("invalid signature"),{status:403})}}function Br(r,e){let t=e?.currentSigningKey??process.env.QSTASH_CURRENT_SIGNING_KEY;if(!t)throw new Error("currentSigningKey is required, either in the config or as env variable QSTASH_CURRENT_SIGNING_KEY");let s=e?.nextSigningKey??process.env.QSTASH_NEXT_SIGNING_KEY;if(!s)throw new Error("nextSigningKey is required, either in the config or as env variable QSTASH_NEXT_SIGNING_KEY");let n=new v({currentSigningKey:t,nextSigningKey:s});return async o=>{let i=o.clone(),a=o.headers.get("upstash-signature");if(!a)return new K(new TextEncoder().encode("`Upstash-Signature` header is missing"),{status:403});if(typeof a!="string")throw new TypeError("`Upstash-Signature` header is not a string");let l=await i.text();return await n.verify({signature:a,body:l,clockTolerance:e?.clockTolerance})?r(o):new K(new TextEncoder().encode("invalid signature"),{status:403})}}var Gr=(r,e)=>{let t=Je(r,{onStepFinish:s=>new K(JSON.stringify({workflowRunId:s}),{status:200}),...e});return async s=>await t(s)};export{Gr as serve,Hr as verifySignature,Br as verifySignatureAppRouter,Fr as verifySignatureEdge};
10
+ Step Types expected: ${JSON.stringify(n)}`)}throw t}},ut=r=>{let e=t=>t.targetStep??t.stepId;return r.toSorted((t,s)=>e(t)-e(s))};var O=class{stepName;constructor(e){this.stepName=e}},ue=class extends O{stepFunction;stepType="Run";constructor(e,t){super(e),this.stepFunction=t}getPlanStep(e,t){return{stepId:0,stepName:this.stepName,stepType:this.stepType,concurrent:e,targetStep:t}}async getResultStep(e,t){let s=this.stepFunction();return s instanceof Promise&&(s=await s),{stepId:t,stepName:this.stepName,stepType:this.stepType,out:s,concurrent:e}}},pe=class extends O{sleep;stepType="SleepFor";constructor(e,t){super(e),this.sleep=t}getPlanStep(e,t){return{stepId:0,stepName:this.stepName,stepType:this.stepType,sleepFor:this.sleep,concurrent:e,targetStep:t}}async getResultStep(e,t){return await Promise.resolve({stepId:t,stepName:this.stepName,stepType:this.stepType,sleepFor:this.sleep,concurrent:e})}},ce=class extends O{sleepUntil;stepType="SleepUntil";constructor(e,t){super(e),this.sleepUntil=t}getPlanStep(e,t){return{stepId:0,stepName:this.stepName,stepType:this.stepType,sleepUntil:this.sleepUntil,concurrent:e,targetStep:t}}async getResultStep(e,t){return await Promise.resolve({stepId:t,stepName:this.stepName,stepType:this.stepType,sleepUntil:this.sleepUntil,concurrent:e})}},de=class extends O{url;method;body;headers;stepType="Call";constructor(e,t,s,n,o){super(e),this.url=t,this.method=s,this.body=n,this.headers=o}getPlanStep(e,t){return{stepId:0,stepName:this.stepName,stepType:this.stepType,concurrent:e,targetStep:t}}async getResultStep(e,t){return await Promise.resolve({stepId:t,stepName:this.stepName,stepType:this.stepType,concurrent:e,callUrl:this.url,callMethod:this.method,callBody:this.body,callHeaders:this.headers})}};var I=class{executor;steps;qstashClient;workflowRunId;url;failureUrl;requestPayload;headers;rawInitialPayload;env;constructor({qstashClient:e,workflowRunId:t,headers:s,steps:n,url:o,failureUrl:i,debug:a,initialPayload:l,rawInitialPayload:u,env:c}){this.qstashClient=e,this.workflowRunId=t,this.steps=n,this.url=o,this.failureUrl=i,this.headers=s,this.requestPayload=l,this.rawInitialPayload=u??JSON.stringify(this.requestPayload),this.env=c??{},this.executor=new le(this,this.steps,a)}async run(e,t){let s=()=>this.executor.wrapStep(e,t);return this.addStep(new ue(e,s))}async sleep(e,t){await this.addStep(new pe(e,t))}async sleepUntil(e,t){let s;typeof t=="number"?s=t:(t=typeof t=="string"?new Date(t):t,s=Math.round(t.getTime()/1e3)),await this.addStep(new ce(e,s))}async call(e,t,s,n,o){return await this.addStep(new de(e,t,s,n,o??{}))}async addStep(e){return await this.executor.addStep(e)}},he=class r extends I{static disabledMessage="disabled-qstash-worklfow-run";async addStep(e){throw new b(r.disabledMessage)}static async tryAuthentication(e,t){let s=new r({qstashClient:new N({baseUrl:"disabled-client",token:"disabled-client"}),workflowRunId:t.workflowRunId,headers:t.headers,steps:[],url:t.url,failureUrl:t.failureUrl,initialPayload:t.requestPayload,rawInitialPayload:t.rawInitialPayload,env:t.env});try{await e(s)}catch(n){return n instanceof b&&n.stepName===this.disabledMessage?m("step-found"):y(n)}return m("run-ended")}};var Ke=["DEBUG","INFO","SUBMIT","WARN","ERROR"],me=class r{logs=[];options;workflowRunId=void 0;constructor(e){this.options=e}async log(e,t,s){if(this.shouldLog(e)){let o={timestamp:Date.now(),workflowRunId:this.workflowRunId??"",logLevel:e,eventType:t,details:s};this.logs.push(o),this.options.logOutput==="console"&&this.writeToConsole(o),await new Promise(i=>setTimeout(i,100))}}setWorkflowRunId(e){this.workflowRunId=e}writeToConsole(e){console.log(JSON.stringify(e,void 0,2))}shouldLog(e){return Ke.indexOf(e)>=Ke.indexOf(this.options.logLevel)}static getLogger(e){return typeof e=="object"?e:e?new r({logLevel:"INFO",logOutput:"console"}):void 0}};var Me=async r=>{try{return await r.text()}catch{return}},pt=r=>{let[e,...t]=JSON.parse(r),s=atob(e.body),n={stepId:0,stepName:"init",stepType:"Initial",out:s,concurrent:1},i=t.filter(l=>l.callType==="step").map(l=>JSON.parse(atob(l.body))),a=[n,...i];return{rawInitialPayload:s,steps:a}},ct=r=>{let e=[],t=[],s=[];for(let n of r)n.stepId===0?e.includes(n.targetStep??0)||(s.push(n),e.push(n.targetStep??0)):t.includes(n.stepId)||(s.push(n),t.push(n.stepId));return s},dt=async(r,e)=>{if(r.length<2)return!1;let t=r.at(-1),s=t.stepId,n=t.targetStep;for(let o=0;o<r.length-1;o++){let i=r[o];if(i.stepId===s&&i.targetStep===n){let a=`QStash Workflow: The step '${i.stepName}' with id '${i.stepId}' has run twice during workflow execution. Rest of the workflow will continue running as usual.`;return await e?.log("WARN","RESPONSE_DEFAULT",a),console.warn(a),!0}}return!1},$e=r=>{let e=r.headers.get(oe),t=!e;if(!t&&e!==G)throw new h(`Incompatible workflow sdk protocol version. Expected ${G}, got ${e} from the request.`);let s=t?`wfr_${Ce()}`:r.headers.get(B)??"";if(s.length===0)throw new h("Couldn't get workflow id from header");return{isFirstInvocation:t,workflowRunId:s}},Te=async(r,e,t)=>{if(e)return{rawInitialPayload:r??"",steps:[],isLastDuplicate:!1};{if(!r)throw new h("Only first call can have an empty body");let{rawInitialPayload:s,steps:n}=pt(r),o=await dt(n,t),i=ct(n);return{rawInitialPayload:s,steps:i,isLastDuplicate:o}}},Qe=async(r,e,t,s,n,o)=>{if(r.headers.get(ne)!=="true")return m("not-failure-callback");if(!n)return y(new h("Workflow endpoint is called to handle a failure, but a failureFunction is not provided in serve options. Either provide a failureUrl or a failureFunction."));try{let{status:i,header:a,body:l,url:u,sourceHeader:c,sourceBody:d,workflowRunId:p}=JSON.parse(e),w=l?atob(l):"{}",f=JSON.parse(w),{rawInitialPayload:g,steps:S,isLastDuplicate:fe}=await Te(atob(d),!1,o),q=new I({qstashClient:t,workflowRunId:p,initialPayload:s(g),rawInitialPayload:g,headers:D(new Headers(c)),steps:S,url:u,failureUrl:u,debug:o});await n(q,i,f.message,a)}catch(i){return y(i)}return m("is-failure-callback")};var ht=r=>{let e=r?.env??(typeof process>"u"?{}:process.env),t=!!(e.QSTASH_CURRENT_SIGNING_KEY&&e.QSTASH_NEXT_SIGNING_KEY);return{qstashClient:new N({baseUrl:e.QSTASH_URL,token:e.QSTASH_TOKEN}),onStepFinish:(s,n)=>new Response(JSON.stringify({workflowRunId:s}),{status:200}),initialPayloadParser:s=>{if(s)try{return JSON.parse(s)}catch(n){if(n instanceof SyntaxError)return s;throw n}},receiver:t?new v({currentSigningKey:e.QSTASH_CURRENT_SIGNING_KEY,nextSigningKey:e.QSTASH_NEXT_SIGNING_KEY}):void 0,baseUrl:e.UPSTASH_WORKFLOW_URL,env:e,...r}},Je=(r,e)=>{let{qstashClient:t,onStepFinish:s,initialPayloadParser:n,url:o,verbose:i,receiver:a,failureUrl:l,failureFunction:u,baseUrl:c,env:d}=ht(e),p=me.getLogger(i),w=async f=>{let g=o??f.url,S=c?g.replace(/^(https?:\/\/[^/]+)(\/.*)?$/,(Q,mt,Ye)=>c+(Ye||"")):g;S!==g&&await p?.log("WARN","ENDPOINT_START",{warning:`QStash Workflow: replacing the base of the url with "${c}" and using it as workflow endpoint.`,originalURL:g,updatedURL:S});let fe=u?S:l,q=await Me(f)??"";await De(q,f.headers.get("upstash-signature"),a),await p?.log("INFO","ENDPOINT_START");let ye=await Qe(f,q,t,n,u);if(ye.isErr())throw ye.error;if(ye.value==="is-failure-callback")return await p?.log("WARN","RESPONSE_DEFAULT","failureFunction executed"),s("no-workflow-id","failure-callback");let{isFirstInvocation:Se,workflowRunId:ke}=$e(f);p?.setWorkflowRunId(ke);let{rawInitialPayload:ge,steps:je,isLastDuplicate:ze}=await Te(q,Se,p);if(ze)return s("no-workflow-id","duplicate-step");let L=new I({qstashClient:t,workflowRunId:ke,initialPayload:n(ge),rawInitialPayload:ge,headers:D(f.headers),steps:je,url:S,failureUrl:fe,debug:p,env:d}),M=await he.tryAuthentication(r,L);if(M.isErr())throw await p?.log("ERROR","ERROR",{error:M.error.message}),M.error;if(M.value==="run-ended")return s("no-workflow-id","auth-fail");let $=await Ge(f,ge,t,S,fe,p);if($.isErr())throw await p?.log("ERROR","SUBMIT_THIRD_PARTY_RESULT",{error:$.error.message}),$.error;if($.value==="continue-workflow"){let Q=Se?await He(L,p):await Fe({onStep:async()=>r(L),onCleanup:async()=>{await Be(L,p)}});if(Q.isErr())throw await p?.log("ERROR","ERROR",{error:Q.error.message}),Q.error;return await p?.log("INFO","RESPONSE_WORKFLOW"),s(L.workflowRunId,"success")}return await p?.log("INFO","RESPONSE_DEFAULT"),s("no-workflow-id","fromCallback")};return async f=>{try{return await w(f)}catch(g){return console.error(g),new Response(JSON.stringify(Ee(g)),{status:500})}}};var se=class{http;constructor(e){this.http=e}async cancel(e){return await this.http.request({path:["v2","workflows","runs",`${e}?cancel=true`],method:"DELETE",parseResponseAsJson:!1})??!0}};var Ve=400;function Hr(r,e){let t=e?.currentSigningKey??process.env.QSTASH_CURRENT_SIGNING_KEY;if(!t)throw new Error("currentSigningKey is required, either in the config or as env variable QSTASH_CURRENT_SIGNING_KEY");let s=e?.nextSigningKey??process.env.QSTASH_NEXT_SIGNING_KEY;if(!s)throw new Error("nextSigningKey is required, either in the config or as env variable QSTASH_NEXT_SIGNING_KEY");let n=new v({currentSigningKey:t,nextSigningKey:s});return async(o,i)=>{let a=o.headers["upstash-signature"];if(!a){i.status(Ve),i.send("`Upstash-Signature` header is missing"),i.end();return}if(typeof a!="string")throw new TypeError("`Upstash-Signature` header is not a string");let l=[];for await(let d of o)l.push(typeof d=="string"?Buffer.from(d):d);let u=Buffer.concat(l).toString("utf8");if(!await n.verify({signature:a,body:u,clockTolerance:e?.clockTolerance})){i.status(Ve),i.send("Invalid signature"),i.end();return}try{o.body=o.headers["content-type"]==="application/json"?JSON.parse(u):u}catch{o.body=u}return r(o,i)}}function Fr(r,e){let t=e?.currentSigningKey??process.env.QSTASH_CURRENT_SIGNING_KEY;if(!t)throw new Error("currentSigningKey is required, either in the config or as env variable QSTASH_CURRENT_SIGNING_KEY");let s=e?.nextSigningKey??process.env.QSTASH_NEXT_SIGNING_KEY;if(!s)throw new Error("nextSigningKey is required, either in the config or as env variable QSTASH_NEXT_SIGNING_KEY");let n=new v({currentSigningKey:t,nextSigningKey:s});return async(o,i)=>{let a=o.clone(),l=o.headers.get("upstash-signature");if(!l)return new K(new TextEncoder().encode("`Upstash-Signature` header is missing"),{status:403});if(typeof l!="string")throw new TypeError("`Upstash-Signature` header is not a string");let u=await a.text();return await n.verify({signature:l,body:u,clockTolerance:e?.clockTolerance})?r(o,i):new K(new TextEncoder().encode("invalid signature"),{status:403})}}function Br(r,e){let t=e?.currentSigningKey??process.env.QSTASH_CURRENT_SIGNING_KEY;if(!t)throw new Error("currentSigningKey is required, either in the config or as env variable QSTASH_CURRENT_SIGNING_KEY");let s=e?.nextSigningKey??process.env.QSTASH_NEXT_SIGNING_KEY;if(!s)throw new Error("nextSigningKey is required, either in the config or as env variable QSTASH_NEXT_SIGNING_KEY");let n=new v({currentSigningKey:t,nextSigningKey:s});return async(o,i)=>{let a=o.clone(),l=o.headers.get("upstash-signature");if(!l)return new K(new TextEncoder().encode("`Upstash-Signature` header is missing"),{status:403});if(typeof l!="string")throw new TypeError("`Upstash-Signature` header is not a string");let u=await a.text();return await n.verify({signature:l,body:u,clockTolerance:e?.clockTolerance})?r(o,i):new K(new TextEncoder().encode("invalid signature"),{status:403})}}var Gr=(r,e)=>{let t=Je(r,{onStepFinish:s=>new K(JSON.stringify({workflowRunId:s}),{status:200}),...e});return async s=>await t(s)};export{Gr as serve,Hr as verifySignature,Br as verifySignatureAppRouter,Fr as verifySignatureEdge};
package/package.json CHANGED
@@ -1 +1 @@
1
- { "version": "v2.7.0", "name": "@upstash/qstash", "description": "Official Typescript client for QStash", "author": "Andreas Thomas <dev@chronark.com>", "license": "MIT", "homepage": "https://github.com/upstash/sdk-qstash-ts#readme", "repository": { "type": "git", "url": "git+https://github.com/upstash/sdk-qstash-ts.git" }, "bugs": { "url": "https://github.com/upstash/sdk-qstash-ts/issues" }, "files": [ "dist" ], "main": "./dist/base/index.mjs", "module": "./dist/base/index.mjs", "types": "./dist/base/index.d.mts", "exports": { ".": { "types": "./dist/base/index.d.mts", "import": "./dist/base/index.mjs", "require": "./dist/base/index.js" }, "./nextjs": { "import": "./dist/nextjs/index.mjs" }, "./dist/nextjs": { "import": "./dist/nextjs/index.mjs" }, "./h3": { "types": "./dist/h3/index.d.mts", "import": "./dist/h3/index.mjs" }, "./nuxt": { "types": "./dist/h3/index.d.mts", "import": "./dist/h3/index.mjs" }, "./svelte": { "types": "./dist/svelte/index.d.mts", "import": "./dist/svelte/index.mjs" }, "./solidjs": { "types": "./dist/solidjs/index.d.mts", "import": "./dist/solidjs/index.mjs" }, "./workflow": { "types": "./dist/workflow/index.d.mts", "import": "./dist/workflow/index.mjs" }, "./hono": { "types": "./dist/hono/index.d.mts", "import": "./dist/hono/index.mjs" }, "./cloudflare": { "types": "./dist/cloudflare/index.d.mts", "import": "./dist/cloudflare/index.mjs" } }, "keywords": [ "qstash", "queue", "events", "serverless", "upstash" ], "scripts": { "build": "tsup", "test": "bun test src", "fmt": "prettier --write .", "lint": "tsc && eslint \"src/**/*.{js,ts,tsx}\" --quiet --fix", "prepare": "husky install" }, "devDependencies": { "@commitlint/cli": "^19.2.2", "@commitlint/config-conventional": "^19.2.2", "@types/bun": "^1.1.1", "@types/crypto-js": "^4.2.0", "@typescript-eslint/eslint-plugin": "^7.0.1", "@typescript-eslint/parser": "^7.0.1", "ai": "^3.1.28", "bun-types": "^1.1.7", "eslint": "^8", "eslint-plugin-unicorn": "^51.0.1", "husky": "^9.0.10", "neverthrow": "^7.0.1", "next": "^14.0.2", "prettier": "^3.2.5", "tsup": "latest", "typescript": "^5.4.5", "undici-types": "^6.16.0", "@solidjs/start": "^1.0.6", "@sveltejs/kit": "^2.5.18", "hono": "^4.5.8", "h3": "^1.12.0", "vitest": "latest" }, "dependencies": { "crypto-js": ">=4.2.0", "jose": "^ 5.2.3" } }
1
+ { "version": "v2.7.1", "name": "@upstash/qstash", "description": "Official Typescript client for QStash", "author": "Andreas Thomas <dev@chronark.com>", "license": "MIT", "homepage": "https://github.com/upstash/sdk-qstash-ts#readme", "repository": { "type": "git", "url": "git+https://github.com/upstash/sdk-qstash-ts.git" }, "bugs": { "url": "https://github.com/upstash/sdk-qstash-ts/issues" }, "files": [ "dist" ], "main": "./dist/base/index.mjs", "module": "./dist/base/index.mjs", "types": "./dist/base/index.d.mts", "exports": { ".": { "types": "./dist/base/index.d.mts", "import": "./dist/base/index.mjs", "require": "./dist/base/index.js" }, "./nextjs": { "import": "./dist/nextjs/index.mjs" }, "./dist/nextjs": { "import": "./dist/nextjs/index.mjs" }, "./h3": { "types": "./dist/h3/index.d.mts", "import": "./dist/h3/index.mjs" }, "./nuxt": { "types": "./dist/h3/index.d.mts", "import": "./dist/h3/index.mjs" }, "./svelte": { "types": "./dist/svelte/index.d.mts", "import": "./dist/svelte/index.mjs" }, "./solidjs": { "types": "./dist/solidjs/index.d.mts", "import": "./dist/solidjs/index.mjs" }, "./workflow": { "types": "./dist/workflow/index.d.mts", "import": "./dist/workflow/index.mjs" }, "./hono": { "types": "./dist/hono/index.d.mts", "import": "./dist/hono/index.mjs" }, "./cloudflare": { "types": "./dist/cloudflare/index.d.mts", "import": "./dist/cloudflare/index.mjs" } }, "keywords": [ "qstash", "queue", "events", "serverless", "upstash" ], "scripts": { "build": "tsup", "test": "bun test src", "fmt": "prettier --write .", "lint": "tsc && eslint \"src/**/*.{js,ts,tsx}\" --quiet --fix", "prepare": "husky" }, "devDependencies": { "@commitlint/cli": "^19.2.2", "@commitlint/config-conventional": "^19.2.2", "@types/bun": "^1.1.1", "@types/crypto-js": "^4.2.0", "@typescript-eslint/eslint-plugin": "^7.0.1", "@typescript-eslint/parser": "^7.0.1", "ai": "^3.1.28", "bun-types": "^1.1.7", "eslint": "^8", "eslint-plugin-unicorn": "^51.0.1", "husky": "^9.0.10", "neverthrow": "^7.0.1", "next": "^14.0.2", "prettier": "^3.2.5", "tsup": "latest", "typescript": "^5.4.5", "undici-types": "^6.16.0", "@solidjs/start": "^1.0.6", "@sveltejs/kit": "^2.5.18", "hono": "^4.5.8", "h3": "^1.12.0", "vitest": "latest" }, "dependencies": { "crypto-js": ">=4.2.0", "jose": "^ 5.2.3" } }