@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 +21 -1
- package/dist/nextjs/index.d.mts +1 -1
- package/dist/nextjs/index.mjs +1 -1
- package/package.json +1 -1
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
|
-
|
|
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
|
+
|
package/dist/nextjs/index.d.mts
CHANGED
|
@@ -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
|
*
|
package/dist/nextjs/index.mjs
CHANGED
|
@@ -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.
|
|
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" } }
|