gemi 0.75.0 → 0.77.0
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/ai/AgentController.d.ts +47 -0
- package/dist/ai/AgentController.d.ts.map +1 -1
- package/dist/ai/index.js +10 -10
- package/dist/ai/index.js.map +7 -5
- package/dist/ai/providers/request.d.ts.map +1 -1
- package/dist/ai/store/TurnJournal.d.ts +97 -0
- package/dist/ai/store/TurnJournal.d.ts.map +1 -0
- package/dist/ai/types.d.ts +29 -3
- package/dist/ai/types.d.ts.map +1 -1
- package/dist/app/index.js +1 -1
- package/dist/broadcasting/index.js +1 -1
- package/dist/bun/plugin.js +1 -1
- package/dist/bun/preload.js +1 -1
- package/dist/{chunk-fy2f8sq8.js → chunk-0472aphj.js} +2 -2
- package/dist/{chunk-fy2f8sq8.js.map → chunk-0472aphj.js.map} +1 -1
- package/dist/{chunk-bm97dwbp.js → chunk-0da5t7vs.js} +2 -2
- package/dist/{chunk-bm97dwbp.js.map → chunk-0da5t7vs.js.map} +1 -1
- package/dist/{chunk-17q5kheg.js → chunk-0pqsp84p.js} +2 -2
- package/dist/{chunk-17q5kheg.js.map → chunk-0pqsp84p.js.map} +1 -1
- package/dist/{chunk-8ef9n5pk.js → chunk-1712rv0r.js} +2 -2
- package/dist/{chunk-8ef9n5pk.js.map → chunk-1712rv0r.js.map} +1 -1
- package/dist/{chunk-9gn3x603.js → chunk-1ap69pez.js} +1 -1
- package/dist/{chunk-a1n0thja.js → chunk-1qwkhbc4.js} +2 -2
- package/dist/{chunk-a1n0thja.js.map → chunk-1qwkhbc4.js.map} +1 -1
- package/dist/{chunk-qn7rr4bw.js → chunk-27pnn95c.js} +2 -2
- package/dist/{chunk-qn7rr4bw.js.map → chunk-27pnn95c.js.map} +1 -1
- package/dist/{chunk-q60vevbt.js → chunk-34s3tfgk.js} +3 -3
- package/dist/{chunk-q60vevbt.js.map → chunk-34s3tfgk.js.map} +1 -1
- package/dist/{chunk-9h0cz1f0.js → chunk-3hfwbp2m.js} +2 -2
- package/dist/{chunk-9h0cz1f0.js.map → chunk-3hfwbp2m.js.map} +1 -1
- package/dist/{chunk-3dd2gwrj.js → chunk-3jyz4378.js} +2 -2
- package/dist/{chunk-3dd2gwrj.js.map → chunk-3jyz4378.js.map} +1 -1
- package/dist/{chunk-t20b1xn4.js → chunk-443yr2km.js} +3 -3
- package/dist/{chunk-t20b1xn4.js.map → chunk-443yr2km.js.map} +1 -1
- package/dist/{chunk-xrd9xx3c.js → chunk-4h7z32zp.js} +3 -3
- package/dist/{chunk-xrd9xx3c.js.map → chunk-4h7z32zp.js.map} +1 -1
- package/dist/{chunk-3nxkn558.js → chunk-5ddn8mfj.js} +2 -2
- package/dist/{chunk-3nxkn558.js.map → chunk-5ddn8mfj.js.map} +1 -1
- package/dist/{chunk-pxfw8dg1.js → chunk-6h5gvcs8.js} +2 -2
- package/dist/{chunk-pxfw8dg1.js.map → chunk-6h5gvcs8.js.map} +1 -1
- package/dist/{chunk-7e4rhaqs.js → chunk-7yhsy039.js} +2 -2
- package/dist/{chunk-7e4rhaqs.js.map → chunk-7yhsy039.js.map} +1 -1
- package/dist/{chunk-zat41156.js → chunk-989rzqd6.js} +3 -3
- package/dist/{chunk-zat41156.js.map → chunk-989rzqd6.js.map} +1 -1
- package/dist/{chunk-hny56r47.js → chunk-9z3y20ms.js} +2 -2
- package/dist/{chunk-hny56r47.js.map → chunk-9z3y20ms.js.map} +1 -1
- package/dist/{chunk-yqb9cpkx.js → chunk-axxbkm30.js} +2 -2
- package/dist/{chunk-yqb9cpkx.js.map → chunk-axxbkm30.js.map} +1 -1
- package/dist/{chunk-mc7nbgct.js → chunk-b941c34r.js} +2 -2
- package/dist/{chunk-mc7nbgct.js.map → chunk-b941c34r.js.map} +1 -1
- package/dist/{chunk-62pzr6vf.js → chunk-bdfdjnht.js} +3 -3
- package/dist/{chunk-62pzr6vf.js.map → chunk-bdfdjnht.js.map} +1 -1
- package/dist/chunk-cr9f0s09.js +5 -0
- package/dist/{chunk-krjkgg9v.js.map → chunk-cr9f0s09.js.map} +1 -1
- package/dist/{chunk-0yfgptrq.js → chunk-dgqqv3pb.js} +2 -2
- package/dist/{chunk-0yfgptrq.js.map → chunk-dgqqv3pb.js.map} +1 -1
- package/dist/{chunk-j9r5w8xf.js → chunk-dmdmt3gw.js} +2 -2
- package/dist/{chunk-j9r5w8xf.js.map → chunk-dmdmt3gw.js.map} +1 -1
- package/dist/chunk-e8z3nyd5.js +19 -0
- package/dist/{chunk-ytt205yt.js.map → chunk-e8z3nyd5.js.map} +8 -7
- package/dist/{chunk-8rqdeyhv.js → chunk-e97kq9tn.js} +2 -2
- package/dist/{chunk-8rqdeyhv.js.map → chunk-e97kq9tn.js.map} +1 -1
- package/dist/{chunk-evbvpfv6.js → chunk-eekg7k6d.js} +1 -1
- package/dist/{chunk-26rqzgsr.js → chunk-ezae18wm.js} +2 -2
- package/dist/{chunk-26rqzgsr.js.map → chunk-ezae18wm.js.map} +1 -1
- package/dist/{chunk-ppscsvv8.js → chunk-f2hkhw76.js} +2 -2
- package/dist/{chunk-ppscsvv8.js.map → chunk-f2hkhw76.js.map} +1 -1
- package/dist/{chunk-d6dz2706.js → chunk-fd0n8sav.js} +2 -2
- package/dist/{chunk-1x8gzkpq.js.map → chunk-fd0n8sav.js.map} +1 -1
- package/dist/{chunk-3sdrj0ma.js → chunk-fmhkn266.js} +2 -2
- package/dist/{chunk-3sdrj0ma.js.map → chunk-fmhkn266.js.map} +1 -1
- package/dist/{chunk-9ecgk1c5.js → chunk-fms4fcd1.js} +2 -2
- package/dist/{chunk-9ecgk1c5.js.map → chunk-fms4fcd1.js.map} +1 -1
- package/dist/{chunk-f90k29w1.js → chunk-fvn78g6k.js} +2 -2
- package/dist/{chunk-f90k29w1.js.map → chunk-fvn78g6k.js.map} +1 -1
- package/dist/{chunk-8ypzxfvx.js → chunk-gzs2y282.js} +2 -2
- package/dist/{chunk-8ypzxfvx.js.map → chunk-gzs2y282.js.map} +1 -1
- package/dist/{chunk-gpys09rg.js → chunk-j1w19cec.js} +3 -3
- package/dist/{chunk-gpys09rg.js.map → chunk-j1w19cec.js.map} +1 -1
- package/dist/{chunk-8jgj7vdp.js → chunk-j8nbjpkg.js} +2 -2
- package/dist/{chunk-8jgj7vdp.js.map → chunk-j8nbjpkg.js.map} +1 -1
- package/dist/{chunk-achgw308.js → chunk-jcq0tbdv.js} +2 -2
- package/dist/{chunk-achgw308.js.map → chunk-jcq0tbdv.js.map} +1 -1
- package/dist/{chunk-9sqw5bjy.js → chunk-m2bmwre5.js} +2 -2
- package/dist/{chunk-9sqw5bjy.js.map → chunk-m2bmwre5.js.map} +1 -1
- package/dist/{chunk-pp9c3f5m.js → chunk-mmhf39tr.js} +2 -2
- package/dist/{chunk-pp9c3f5m.js.map → chunk-mmhf39tr.js.map} +1 -1
- package/dist/{chunk-15gc8m24.js → chunk-n03frt02.js} +2 -2
- package/dist/{chunk-15gc8m24.js.map → chunk-n03frt02.js.map} +1 -1
- package/dist/{chunk-wtwkzgec.js → chunk-njh95es7.js} +2 -2
- package/dist/{chunk-wtwkzgec.js.map → chunk-njh95es7.js.map} +1 -1
- package/dist/{chunk-ngq4n2b3.js → chunk-q0nqypca.js} +2 -2
- package/dist/{chunk-ngq4n2b3.js.map → chunk-q0nqypca.js.map} +1 -1
- package/dist/{chunk-jpej3172.js → chunk-q623tx9p.js} +2 -2
- package/dist/{chunk-jpej3172.js.map → chunk-q623tx9p.js.map} +1 -1
- package/dist/{chunk-dz7b35tn.js → chunk-qgvg10ad.js} +3 -3
- package/dist/{chunk-dz7b35tn.js.map → chunk-qgvg10ad.js.map} +1 -1
- package/dist/{chunk-9p5vwn4d.js → chunk-qjgfrsw7.js} +2 -2
- package/dist/{chunk-9p5vwn4d.js.map → chunk-qjgfrsw7.js.map} +1 -1
- package/dist/{chunk-371tt28q.js → chunk-rkx6qhwg.js} +4 -4
- package/dist/{chunk-371tt28q.js.map → chunk-rkx6qhwg.js.map} +1 -1
- package/dist/{chunk-1k0gv92y.js → chunk-rv9k5bqn.js} +2 -2
- package/dist/{chunk-1k0gv92y.js.map → chunk-rv9k5bqn.js.map} +1 -1
- package/dist/{chunk-407t1q1d.js → chunk-s7hv9qgp.js} +2 -2
- package/dist/{chunk-407t1q1d.js.map → chunk-s7hv9qgp.js.map} +1 -1
- package/dist/{chunk-wbxmwzdb.js → chunk-sbqy6r29.js} +1 -1
- package/dist/{chunk-a82m12q4.js → chunk-sp4wwg7s.js} +3 -3
- package/dist/{chunk-a82m12q4.js.map → chunk-sp4wwg7s.js.map} +1 -1
- package/dist/{chunk-3eejq348.js → chunk-sqvw7rv2.js} +3 -3
- package/dist/{chunk-3eejq348.js.map → chunk-sqvw7rv2.js.map} +1 -1
- package/dist/{chunk-1yr8qejy.js → chunk-ss7ymqfm.js} +2 -2
- package/dist/{chunk-1yr8qejy.js.map → chunk-ss7ymqfm.js.map} +1 -1
- package/dist/{chunk-cr2wpeh5.js → chunk-sww4kgk4.js} +2 -2
- package/dist/{chunk-cr2wpeh5.js.map → chunk-sww4kgk4.js.map} +1 -1
- package/dist/{chunk-4z5qqq1t.js → chunk-tnt0ybj9.js} +2 -2
- package/dist/{chunk-4z5qqq1t.js.map → chunk-tnt0ybj9.js.map} +1 -1
- package/dist/{chunk-pjcdekm1.js → chunk-tpdsfr2k.js} +2 -2
- package/dist/{chunk-pjcdekm1.js.map → chunk-tpdsfr2k.js.map} +1 -1
- package/dist/{chunk-ra1ccnd5.js → chunk-vr84mhmq.js} +2 -2
- package/dist/{chunk-ra1ccnd5.js.map → chunk-vr84mhmq.js.map} +1 -1
- package/dist/{chunk-y3hmzww0.js → chunk-waax3j1k.js} +2 -2
- package/dist/{chunk-y3hmzww0.js.map → chunk-waax3j1k.js.map} +1 -1
- package/dist/{chunk-1x8gzkpq.js → chunk-xzp2ptj5.js} +2 -2
- package/dist/{chunk-d6dz2706.js.map → chunk-xzp2ptj5.js.map} +1 -1
- package/dist/{chunk-ywf0kz71.js → chunk-yaz93haz.js} +2 -2
- package/dist/{chunk-ywf0kz71.js.map → chunk-yaz93haz.js.map} +1 -1
- package/dist/{chunk-1rnzaqpm.js → chunk-yernnzqs.js} +2 -2
- package/dist/{chunk-1rnzaqpm.js.map → chunk-yernnzqs.js.map} +1 -1
- package/dist/{chunk-a4xxmmct.js → chunk-zjg31baf.js} +2 -2
- package/dist/{chunk-a4xxmmct.js.map → chunk-zjg31baf.js.map} +1 -1
- package/dist/{chunk-14pybfjk.js → chunk-zkty5j42.js} +2 -2
- package/dist/{chunk-14pybfjk.js.map → chunk-zkty5j42.js.map} +1 -1
- package/dist/{chunk-g2hwd2ts.js → chunk-zzh52t0y.js} +2 -2
- package/dist/{chunk-g2hwd2ts.js.map → chunk-zzh52t0y.js.map} +1 -1
- package/dist/client/MutationError.d.ts +16 -1
- package/dist/client/MutationError.d.ts.map +1 -1
- package/dist/client/index.js +94 -76
- package/dist/client/index.js.map +1 -1
- package/dist/client/useMutation.d.ts +1 -1
- package/dist/client/useMutation.d.ts.map +1 -1
- package/dist/config/index.js +1 -1
- package/dist/console/run.js +2 -2
- package/dist/console/run.js.map +1 -1
- package/dist/container/index.js +2 -2
- package/dist/container/index.js.map +1 -1
- package/dist/database/index.js +2 -2
- package/dist/database/index.js.map +1 -1
- package/dist/email/index.js +2 -2
- package/dist/email/index.js.map +1 -1
- package/dist/facades/index.js +2 -2
- package/dist/facades/index.js.map +1 -1
- package/dist/foundation/index.js +2 -2
- package/dist/foundation/index.js.map +1 -1
- package/dist/http/ApiRouter.d.ts +3 -2
- package/dist/http/ApiRouter.d.ts.map +1 -1
- package/dist/http/HttpResponse.d.ts +83 -0
- package/dist/http/HttpResponse.d.ts.map +1 -0
- package/dist/http/ViewRouter.d.ts.map +1 -1
- package/dist/http/index.d.ts +1 -0
- package/dist/http/index.d.ts.map +1 -1
- package/dist/http/index.js +2 -2
- package/dist/http/index.js.map +1 -1
- package/dist/i18n/dictionaryRuntime.js +2 -2
- package/dist/i18n/dictionaryRuntime.js.map +1 -1
- package/dist/i18n/index.js +2 -2
- package/dist/i18n/index.js.map +1 -1
- package/dist/kernel/index.js +2 -2
- package/dist/kernel/index.js.map +1 -1
- package/dist/orm/index.js +2 -2
- package/dist/orm/index.js.map +1 -1
- package/dist/server/index.js +2 -2
- package/dist/server/index.js.map +1 -1
- package/dist/services/index.js +2 -2
- package/dist/services/index.js.map +1 -1
- package/dist/services/queue/queueDriverContract.d.ts +6 -2
- package/dist/services/queue/queueDriverContract.d.ts.map +1 -1
- package/dist/services/queue/work.js +2 -2
- package/dist/services/queue/work.js.map +1 -1
- package/dist/services/router/ApiRouteDispatcher.d.ts.map +1 -1
- package/dist/services/router/serverQueryFetcher.d.ts.map +1 -1
- package/dist/support/index.js +2 -2
- package/dist/support/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-krjkgg9v.js +0 -5
- package/dist/chunk-ytt205yt.js +0 -19
- /package/dist/{chunk-9gn3x603.js.map → chunk-1ap69pez.js.map} +0 -0
- /package/dist/{chunk-evbvpfv6.js.map → chunk-eekg7k6d.js.map} +0 -0
- /package/dist/{chunk-wbxmwzdb.js.map → chunk-sbqy6r29.js.map} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{
|
|
2
|
+
import{vf as I}from"./chunk-1712rv0r.js";import{Bf as A,Cf as U,xf as W,yf as S}from"./chunk-bdfdjnht.js";import{Yf as C}from"./chunk-jcq0tbdv.js";import{rg as E}from"./chunk-0da5t7vs.js";import{ug as N}from"./chunk-e97kq9tn.js";class K{table;sql;dialect;connection;busyTimeout;configured;constructor(z,G={}){let V=G.table??"gemi_jobs";if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(V))throw Error(`The queue table name "${V}" is not a plain identifier. Use letters, digits and underscores.`);let Y=G.busyTimeout??1000;if(!Number.isInteger(Y)||Y<0)throw Error(`The queue's busyTimeout must be a whole number of milliseconds, 0 or more; got ${Y}.`);this.table=V,this.sql=z.sql,this.dialect=z.dialect,this.connection=z.name,this.busyTimeout=Y}configure(){if(this.dialect!=="sqlite"||this.busyTimeout===0)return Promise.resolve();return this.configured??=(async()=>{let[z]=await this.sql.unsafe("PRAGMA busy_timeout");if(Number(z?.timeout??0)!==0)return;await this.sql.unsafe(`PRAGMA busy_timeout = ${this.busyTimeout}`)})().catch((z)=>{throw this.configured=void 0,z}),this.configured}async createTable(){await this.configure();for(let z of R(this.dialect,this.table))await this.sql.unsafe(z)}enqueue(z){let G=this.transaction(),V=this.insert(G??this.sql,z);if(G)U(V);return V}async insert(z,{name:G,args:V,delayMs:Y=0,id:Z=Bun.randomUUIDv7()}){await this.configure();let $=this.now(z);return await z`
|
|
3
3
|
INSERT INTO ${this.name(z)}
|
|
4
4
|
(id, name, payload, status, attempts, available_at, created_at, updated_at)
|
|
5
5
|
VALUES
|
|
@@ -139,4 +139,4 @@ import{uf as I}from"./chunk-8ef9n5pk.js";import{Af as A,Bf as U,wf as W,xf as S}
|
|
|
139
139
|
`],"worker.ts"),Z=URL.createObjectURL(Y);return new Worker(Z)}async function T(z,G){let V=M();return V.postMessage({jobName:z,args:G}),await new Promise((Y,Z)=>{V.onmessage=($)=>{let B=$.data;if("error"in B)Z(B.error);else Y(B.result)}})}var D=new P("gemi.queue"),k=60000;class x{static token="queue";jobs={};config;driver;application;inFlight=new Map;state="idle";looping=!1;claiming;woken=!1;claimFailures=0;resume;unsubscribe;heartbeatTimer;releasedNames=new Set;constructor(z={},G={}){this.config=I(_(),z),this.application=G.application,this.driver=q(this.config.driver,this.application),this.useJobs(this.config.jobs)}useJobs(z){this.config.jobs=z,this.jobs={};for(let G of z)this.claim(G)}registerJob(z){this.config.jobs=[...this.config.jobs,z],this.claim(z)}claim(z){if(this.jobs[z.name]){console.error(`Two queued jobs are named "${z.name}" \u2014 the first is registered `+"and this one is not, so dispatching either would have run one of them. A class name is the queue's key, so only one job can hold it. Rename one.");return}this.jobs[z.name]=z}get registeredJobs(){return[...this.config.jobs]}dispatchJob(z,G){if(this.jobs[z])return new this.jobs[z]().run(JSON.parse(G))}push(z,G,V={}){let Y=this.driver.joinsTransaction?.()===!0;if(!Y){let Z=Bun.randomUUIDv7();if(A(()=>this.record(z,G,{id:Z,reportFailure:!0,committed:!0}).then(()=>{},()=>{})))return Promise.resolve(Z)}return this.record(z,G,{reportFailure:V.reportFailure,joins:Y})}record(z,G,V){let Y=this.driver.enqueue({name:z.name,args:G,...V.id===void 0?{}:{id:V.id}});this.startIfIdle();let Z=()=>!this.driver.subscribe&&this.state==="running"&&this.wake(),$=V.joins===!0&&A(Z);return Y.then(()=>$||Z(),(B)=>{if(V.reportFailure===!1)return;console.error(`[gemi] The queue driver could not record ${z.name}`+(V.committed?", which was held until its transaction committed. The transaction stays committed; the job did not run and will not be retried.":", so it did not run and will not be retried."),B)}),Y}async retryDead(z){if(!this.driver.retryDead)throw Error("This queue's driver keeps nothing of a dead-lettered job, so there is nothing to retry. The database driver keeps them.");let G=await this.driver.retryDead(z);if(G)this.claimSoon();return G}startIfIdle(){if(this.state==="idle"&&(!this.durable||this.mayClaimHere()))this.start()}claimSoon(){if(this.startIfIdle(),!this.driver.subscribe&&this.state==="running")this.wake()}start(){if(this.state==="running")return;if(this.state="running",this.durable&&this.application&&this.mayClaimHere()&&!v())globalThis.__gemiDevQueue=this;if(this.unsubscribe=this.driver.subscribe?.(()=>this.wake()),this.looping)return this.wake();D.runInAsyncScope(()=>void this.loop())}async drain(z=1/0){if(this.state="stopped",globalThis.__gemiDevQueue===this)globalThis.__gemiDevQueue=void 0;this.unsubscribe?.(),this.unsubscribe=void 0,this.wake();let G,V=new Promise((Z)=>{if(z!==1/0)G=setTimeout(Z,z)}),Y=(async()=>{await this.claiming?.catch(()=>{}),await Promise.allSettled([...this.inFlight.values()].map((Z)=>Z.done))})();return await Promise.race([Y,V]),clearTimeout(G),{unfinished:[...this.inFlight.values()].map((Z)=>({...Z.job}))}}stop(){return this.drain(0)}get running(){return this.inFlight.size}async loop(){this.looping=!0;try{await this.claimWhileRunning()}finally{this.looping=!1}}async claimWhileRunning(){while(this.state==="running"){let z=this.durable&&N()?0:this.config.concurrency-this.inFlight.size;if(z>0){this.woken=!1;let G=this.driver.claim(z,{...this.lease(),registered:{names:Object.keys(this.jobs),graceMs:this.config.unknownJobGrace}});this.claiming=G;let V=[];try{V=await G,this.claimFailures=0}catch(Y){this.claimFailures++,console.error(`The queue could not claim jobs from its driver; it will try again in ${this.retryDelay()}ms.`,Y)}finally{this.claiming=void 0}for(let Y of V)this.execute(Y);if(V.length===z)continue;if(this.woken&&this.inFlight.size<this.config.concurrency)continue}await this.sleep()}}sleep(){return new Promise((z)=>{let G,V=this.claimFailures>0?this.retryDelay():this.driver.subscribe?void 0:this.config.pollInterval;if(V!==void 0)G=setTimeout(()=>this.wake(),V),G.unref?.();this.resume=()=>{clearTimeout(G),z()}})}retryDelay(){let z=Math.min(Math.max(0,this.claimFailures-1),30);return Math.min(this.config.pollInterval*2**z,k)}wake(){this.woken=!0;let z=this.resume;this.resume=void 0,z?.()}get durable(){return!(this.driver instanceof X)}mayClaimHere(){return!this.application||g()}lease(){return{visibilityTimeoutMs:this.config.visibilityTimeout}}execute(z){let G=`${z.id}:${z.attempt}`,V=()=>this.run(z),Y=(this.application?E.run(this.application,V):V()).catch((Z)=>{console.error(`The queue could not record the outcome of ${z.name}.`,Z)}).finally(()=>{this.inFlight.delete(G),this.wake()});this.inFlight.set(G,{job:z,done:Y}),this.heartbeat()}heartbeat(){let z=this.driver;if(!z.heartbeat||this.heartbeatTimer)return;this.heartbeatTimer=setInterval(()=>{if(this.inFlight.size===0){clearInterval(this.heartbeatTimer),this.heartbeatTimer=void 0;return}let G=[...this.inFlight.values()].map((V)=>V.job);z.heartbeat(G,this.lease()).catch((V)=>{console.error("The queue could not extend its job leases.",V)})},Math.max(1,Math.floor(this.config.visibilityTimeout/3))),this.heartbeatTimer.unref?.()}async run(z){let G=this.jobs[z.name];if(!G){let Z=Date.now()-z.createdAt;if(this.durable&&Z<this.config.unknownJobGrace){if(!this.releasedNames.has(z.name))this.releasedNames.add(z.name),console.error(`Left a queued "${z.name}" for another replica: nothing is registered here under that name. It is dead-lettered if no replica has run it within unknownJobGrace (${this.config.unknownJobGrace}ms) of its dispatch.`);await this.driver.release(z,{retryInMs:Math.round(this.config.pollInterval*(1+Math.random()))});return}console.error(`Dropped a queued job: nothing is registered under the name "${z.name}". If the class exists, it was not `+"discovered \u2014 check that it is under the queue slice's jobsDir "+"(app/jobs by default), or list it in app/config/queue.ts."),await this.driver.fail(z,{error:`No job is registered under the name "${z.name}".`,retryInMs:null});return}let V;try{V=new G}catch(Z){console.error(`Dropped a queued ${z.name}: it could not be constructed.`,Z),await this.driver.fail(z,{error:`The job could not be constructed: ${String(Z)}`,retryInMs:null});return}let Y;try{Y=JSON.parse(z.args)}catch(Z){console.error(`Dropped a queued ${z.name}: its arguments are not JSON.`,Z),await this.driver.fail(z,{error:`The arguments are not JSON: ${String(Z)}`,retryInMs:null});return}if(z.attempt>Math.max(1,V.maxAttempts)){let Z=Error(`${z.name} was claimed for attempt ${z.attempt} of ${V.maxAttempts}: an earlier attempt never finished, most likely because the process running it exited. Dead-lettered without running it again.`);L(`${z.name}.onDeadletter`,()=>V.onDeadletter(Z,...Y)),await this.driver.fail(z,{error:Z.message,retryInMs:null});return}try{let Z=await(V.worker?T(z.name,z.args):V.run(...Y));V.onSuccess(Z,...Y)}catch(Z){let $=Z,B=String($?.stack??$);if(L(`${z.name}.onFail`,()=>V.onFail($,...Y)),z.attempt>=V.maxAttempts)L(`${z.name}.onDeadletter`,()=>V.onDeadletter($,...Y)),await this.driver.fail(z,{error:B,retryInMs:null});else await this.driver.fail(z,{error:B,retryInMs:f(V.backoff,z.attempt)});return}await this.driver.complete(z)}}function L(z,G){try{G()}catch(V){console.error(`${z} threw; the job's claim was ended anyway.`,V)}}function v(){return Bun.env.NODE_ENV==="production"}function g(){if(!Q)return!1;if(h())return!0;return process.env.ROOT_DIR!==void 0&&!y()}function y(z=process.env){return(z.GEMI_QUEUE_CLAIM??"").trim().toLowerCase()==="off"}var O=Symbol.for("gemi.queue.worker");function o(z=!0){globalThis[O]=z}function h(){return globalThis[O]===!0}function f(z,G){let V=Array.isArray(z)?z[Math.min(G,z.length)-1]:z;return Math.max(0,V??0)}function q(z,G){if(z==="memory")return new X;if(z==="database"||typeof z==="function"){if(!G&&(z==="database"||z.length>0))throw Error("The queue driver needs the application it belongs to, and this QueueManager was built without one. Pass { application }.");if(z==="database")return new K(G.make(C));return z(G)}if(typeof z==="string")throw Error(`Unknown queue driver "${z}". The queue slice's driver is "memory", "database", a QueueDriver, or a function returning one.`);return z}
|
|
140
140
|
export{K as Yb,j as Zb,_ as _b,X as $b,x as ac,g as bc,y as cc,o as dc,h as ec};
|
|
141
141
|
|
|
142
|
-
//# debugId=
|
|
142
|
+
//# debugId=749CF7005C17653964756E2164756E21
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
"import { AsyncResource } from \"node:async_hooks\";\nimport { isMainThread } from \"node:worker_threads\";\n\nimport type { Application } from \"../../foundation/Application\";\nimport { DatabaseManager } from \"../../database/DatabaseManager\";\nimport { kernelContext } from \"../../kernel/context\";\nimport { deferUntilCommit } from \"../../orm/context\";\nimport { isShuttingDown } from \"../../server/shutdown\";\nimport { DatabaseQueueDriver } from \"./DatabaseQueueDriver\";\nimport { Job } from \"./Job\";\nimport { queueConfigDefaults, type QueueConfig } from \"./config\";\nimport { MemoryQueueDriver } from \"./MemoryQueueDriver\";\nimport type { ClaimOptions, ClaimedJob, QueueDriver } from \"./QueueDriver\";\nimport { withDefaults } from \"../../support/withDefaults\";\n\n/**\n * The thread a `worker = true` job runs in.\n *\n * The `await` on `dispatchJob` is load-bearing and its absence was a hang.\n * `postMessage` structured-clones its argument and a Promise is not cloneable,\n * so an un-awaited `dispatchJob` posted the pending promise, threw\n * `DataCloneError` inside this handler, and posted nothing at all. `runInWorker`\n * then never settled: `run` below never returned, its concurrency slot was\n * never decremented, and neither the retry path nor `onDeadletter` ever fired —\n * after `concurrency` such jobs the queue sits at its limit forever, with no\n * error anywhere. Any job whose `run` is `async` hit it, which is every job\n * that touches IO; a queued listener hits it unconditionally, because the\n * synthetic job's `run` awaits `handle`.\n *\n * The await earns two more things. A rejecting job now reaches the `catch` and\n * comes back as `{error}`, where the queue's ordinary retry-then-dead-letter\n * path can have it, instead of being lost with the promise. And `destroy()` now\n * runs after the job rather than under it, so the cloned kernel — its database\n * connections included — outlives the work it was cloned for.\n */\nfunction createWorker() {\n const APP_DIR = process.env.APP_DIR;\n const ROOT_DIR = process.env.ROOT_DIR;\n\n const appPath =\n process.env.NODE_ENV === \"production\"\n ? `${ROOT_DIR}/dist/server/bootstrap.mjs`\n : `${APP_DIR}/bootstrap.ts`;\n\n const file = new File(\n [\n `\n import { app } from \"${appPath}\"\n self.onmessage = async (event) => {\n const clone = app.clone()\n let result = null;\n let error = null;\n try {\n result = await clone.dispatchJob(event.data.jobName, event.data.args)\n } catch (err) {\n error = err\n }\n clone.destroy()\n if(error) {\n self.postMessage({error});\n } else {\n self.postMessage({result});\n }\n };\n `,\n ],\n \"worker.ts\",\n );\n const url = URL.createObjectURL(file);\n return new Worker(url);\n}\n\n// TODO: terminate worker after the job is done\nasync function runInWorker(jobName: string, args: string) {\n const worker = createWorker();\n worker.postMessage({ jobName, args });\n return await new Promise((resolve, reject) => {\n worker.onmessage = (e) => {\n const data = e.data;\n if (\"error\" in data) {\n reject(data.error);\n } else {\n resolve(data.result);\n }\n };\n });\n}\n\n/**\n * The async context every job runs in: the one this module was evaluated in,\n * which is boot, outside any request.\n *\n * The worker loop is long-lived and started lazily, by the first dispatch, and\n * an async function keeps the context it was started in across every `await`.\n * Started from a request, it would run every later job — dispatched by any\n * request at all — inside the first one's `RequestContext`, and with its ORM\n * transaction handle if it had one open: one user's identity leaking into\n * another's work. Captured here, where no request exists, and entered by `start()`, a job\n * sees no request at all; the Application is entered separately, per job.\n */\nconst bootScope = new AsyncResource(\"gemi.queue\");\n\n/**\n * The longest the loop waits between retries while `claim` keeps rejecting.\n *\n * Long enough that a database which is properly down is not asked once a\n * second by every worker process for as long as it stays down, short enough\n * that the queue is claiming again within a minute of it coming back.\n */\nconst maxClaimBackoff = 60_000;\n\ndeclare global {\n /**\n * The queue loop a `gemi dev` server is running over a driver that outlives\n * the process, kept where a `bun --hot` reload cannot lose it. A reload\n * re-evaluates every module and boots a new application, but the previous\n * application's loop is a live closure and keeps claiming — from the same\n * table, with the code it was loaded with. This is how the next\n * application finds it to stop it; see `startClaimingIfServing`.\n *\n * Typed by what is called on it, not as `QueueManager`: after a reload the\n * one held here is an instance of the previous module graph's class.\n */\n var __gemiDevQueue: { stop(): Promise<unknown> } | undefined;\n}\n\n/** What `drain` could not wait out. */\nexport type DrainResult = {\n /**\n * Jobs still running when the timeout elapsed. They keep running, and keep\n * their leases, until they settle or the process exits; with a driver that\n * outlives the process, an exit leaves them to be reclaimed once their lease\n * runs out. Waiting jobs are not listed — they were never taken, and are\n * wherever the driver keeps them.\n */\n unfinished: ClaimedJob[];\n};\n\nexport class QueueManager {\n static token = \"queue\";\n\n jobs: Record<string, new () => Job> = {};\n\n readonly config: Required<QueueConfig>;\n readonly driver: QueueDriver;\n\n private readonly application: Application | undefined;\n /**\n * Keyed by claim, `id:attempt`, not by job id. A lease that lapses while\n * its job is still running here — heartbeats failing through a database\n * blip, or an event loop blocked past the visibility timeout — lets this\n * same process claim the job again. Both runs are then really running, so\n * both take a slot. Keyed by id alone, the second claim overwrote the first\n * and the first one's `finally` then deleted it: the live run stopped being\n * counted, drained or heartbeated, so its lease lapsed in turn and the job\n * was claimed again into a slot that was never free.\n */\n private readonly inFlight = new Map<\n string,\n { job: ClaimedJob; done: Promise<void> }\n >();\n private state: \"idle\" | \"running\" | \"stopped\" = \"idle\";\n private looping = false;\n private claiming: Promise<unknown> | undefined;\n private woken = false;\n /** Consecutive `claim` rejections; see `sleep`. Cleared by a claim that returns. */\n private claimFailures = 0;\n private resume: (() => void) | undefined;\n private unsubscribe: (() => void) | undefined;\n private heartbeatTimer: ReturnType<typeof setInterval> | undefined;\n /** Unknown names already reported as left for another replica; see `run`. */\n private readonly releasedNames = new Set<string>();\n\n /**\n * `application` is entered around every job, so `app()` inside one resolves\n * to it rather than to whichever application happens to be the static\n * fallback. The provider always passes it; a manager built bare, as the unit\n * tests do, runs jobs with no application entered.\n */\n constructor(\n config: QueueConfig = {},\n options: { application?: Application } = {},\n ) {\n this.config = withDefaults(queueConfigDefaults(), config);\n this.application = options.application;\n this.driver = resolveDriver(this.config.driver, this.application);\n this.useJobs(this.config.jobs);\n }\n\n /**\n * Replaces the registered set, for the provider to hand over what it found\n * under `app/jobs`.\n *\n * This exists because the two phases disagree about when the answer is\n * knowable. The manager is constructed in `register()`, which is synchronous\n * and must resolve nothing; reading a directory and importing what is in it is\n * neither. So the manager is built from whatever the config slice declared —\n * nothing, when the app left `jobs` out — and the discovered set arrives in\n * `boot()`.\n *\n * The consequence worth knowing: anything that constructs an application and\n * skips phase two sees an empty registry, and every dispatch against an empty\n * registry is dropped — loudly on stderr, but after `Job.dispatch` has already\n * returned, so no caller finds out. On a durable driver not even that is\n * immediate: the job is left for another replica, and the stderr line comes\n * only once `unknownJobGrace` has passed. An app that lists its jobs explicitly is\n * unaffected, because that list is already in place by the end of `register()`.\n */\n useJobs(jobs: Array<new () => Job>) {\n this.config.jobs = jobs;\n\n // Built one at a time rather than by `Object.fromEntries`, which resolves a\n // repeated key by keeping the last and saying nothing.\n //\n // Two classes with one name is the worst failure in this subsystem, and it\n // is worse than the dropped dispatch the rest of this file is about: the\n // registry is keyed by class name, a dispatch carries a class name, so\n // `SendEmail.dispatch(...)` on the one under `app/jobs/auth` would run the\n // body of the one under `app/jobs/billing`. Nothing is dropped and nothing\n // errors — the wrong work happens and reports success.\n //\n // Discovery is what makes it ordinary. A hand-written list forces an import\n // alias in one visible file the moment two names clash; a directory walk\n // does not, and `auth/SendEmail.ts` beside `billing/SendEmail.ts` is a\n // perfectly natural thing to write. So the first claim wins and the second\n // is refused out loud — the same rule `Scheduler.start` applies to a cron\n // name, and the reverse of the silent last-wins this replaces.\n this.jobs = {};\n for (const job of jobs) {\n this.claim(job);\n }\n }\n\n /**\n * Registers one more job beside whatever `useJobs` already took.\n *\n * This exists for the queued-listener adapter, which registers a synthetic\n * job per queued listener from `EventServiceProvider.boot()` — after the\n * queue's own `boot()` has filled the registry. `useJobs` cannot serve that\n * caller: it *replaces* the registry, so a second call would discard every\n * job the app wrote and leave the queue holding listeners alone. Reading\n * `registeredJobs` back, concatenating and calling `useJobs` again avoids\n * that and is still the wrong shape — it re-runs the collision check over\n * jobs that already passed it, so a duplicate the author has already been\n * told about is reported a second time on every boot.\n *\n * Same rule as `useJobs`, deliberately: the first claim on a name wins and\n * the second is refused out loud. And recorded in `config.jobs` either way,\n * so `registeredJobs` keeps reporting everything the manager was handed\n * rather than what it accepted — a refused job is visible to a test there.\n *\n * A new array rather than a `push`, because `useJobs` keeps the caller's\n * array by reference and appending to it would edit the config slice or the\n * discovered list under whoever else is holding it.\n */\n registerJob(job: new () => Job) {\n this.config.jobs = [...this.config.jobs, job];\n this.claim(job);\n }\n\n /**\n * Puts one job in the registry, or refuses it because the name is taken.\n *\n * The rule and the reason are in `useJobs` above; this is only the shape both\n * entry points share, so that a job arriving one at a time cannot be admitted\n * on terms the batch would have refused.\n */\n private claim(job: new () => Job) {\n if (this.jobs[job.name]) {\n console.error(\n `Two queued jobs are named \"${job.name}\" — the first is registered ` +\n `and this one is not, so dispatching either would have run one of ` +\n `them. A class name is the queue's key, so only one job can hold ` +\n `it. Rename one.`,\n );\n return;\n }\n this.jobs[job.name] = job;\n }\n\n /**\n * What the manager was handed, discovered or declared.\n *\n * The registry is keyed by name, and a name is exactly what a dispatch\n * carries, so \"is this job registered?\" is a question with a silent wrong\n * answer — `run()` drops an unknown name long after the caller moved on.\n * This is where a test asks it out loud.\n *\n * It reports what came in, not what the registry accepted, so a name claimed\n * twice appears twice here — deliberately, the same way `Scheduler.jobs`\n * does. A test walking this should see the collision rather than have it\n * tidied away. A copy, so that walk cannot edit the registry underneath it.\n */\n get registeredJobs(): ReadonlyArray<new () => Job> {\n return [...this.config.jobs];\n }\n\n dispatchJob(jobName: string, args: string) {\n if (this.jobs[jobName]) {\n const job = new this.jobs[jobName]();\n return job.run(JSON.parse(args));\n }\n }\n\n /**\n * Queues a job and resolves to its id once the driver has recorded it, then\n * makes sure the worker loop is running — unless `drain` stopped it, in which\n * case the job waits in the driver for whichever process claims next. With\n * the memory driver that is nobody; see `drain`. A durable driver's job also\n * waits there when this process does not claim: it is not a server, or it is\n * one started with `GEMI_QUEUE_CLAIM=off`; see `claimsInThisProcess`.\n *\n * Inside an open ORM transaction the job belongs to the transaction. A\n * driver that can write it on the transaction does (`joinsTransaction`);\n * for any other, it is held and recorded once the transaction commits, and\n * never if it rolls back. Either way no claimer can see the job before the\n * data it was dispatched about exists. A held dispatch resolves at once, to\n * the id the job will be recorded under: the transaction does not commit\n * until its callback returns, so a caller awaiting a promise that waited for\n * the commit would never return.\n */\n push(\n job: new () => Job,\n args: string,\n options: {\n /**\n * Whether a driver that cannot record the job is reported from here.\n * On by default, because the usual caller is a fire-and-forget\n * `Job.dispatch(...)` whose promise nobody reads. A caller with\n * something better to say — `EventManager`, which knows the event and\n * the listener — turns it off and says that instead. A held dispatch is\n * reported here whatever this says, because its promise has already\n * resolved and the caller has nothing left to catch.\n */\n reportFailure?: boolean;\n } = {},\n ): Promise<string> {\n // Asked before anything is written, and in the caller's async context,\n // which is where the transaction is. The held branch is the fallback\n // rather than the rule because it loses the job to a crash between the\n // commit and the enqueue; written on the transaction, the job commits\n // with the rows or not at all.\n const joins = this.driver.joinsTransaction?.() === true;\n if (!joins) {\n // `deferUntilCommit` is the ORM's commit hook, and the same one an\n // `afterCommit` event waits on: a savepoint that rolls back takes its\n // held dispatches with it, and the callback runs outside the\n // transaction, so the enqueue cannot be pulled back into it.\n const id = Bun.randomUUIDv7();\n const held = deferUntilCommit(() =>\n // Awaited, so the transaction's own promise waits for the enqueue and\n // a script that exits after its last transaction does not exit under\n // it. The rejection is swallowed only because `record` has already\n // reported it; rethrown, the drain would report it a second time.\n this.record(job, args, {\n id,\n reportFailure: true,\n committed: true,\n }).then(\n () => {},\n () => {},\n ),\n );\n if (held) return Promise.resolve(id);\n }\n return this.record(job, args, {\n reportFailure: options.reportFailure,\n joins,\n });\n }\n\n /** `push`, once it is known that the job is recorded now. */\n private record(\n job: new () => Job,\n args: string,\n options: {\n id?: string;\n reportFailure?: boolean;\n /** Written on the caller's open transaction; see `push`. */\n joins?: boolean;\n /** Held until a transaction committed; changes only the error line. */\n committed?: boolean;\n },\n ): Promise<string> {\n const id = this.driver.enqueue({\n name: job.name,\n args,\n ...(options.id === undefined ? {} : { id: options.id }),\n });\n // A driver that outlives the process is shared with every other one, so\n // a script or console command that dispatches would otherwise claim up to\n // `concurrency` of the table's jobs — other replicas' included — and exit\n // under them, costing each an attempt and a lease's wait. There the job\n // waits in the driver for a server or a worker. A manager built by hand,\n // with no application, is its caller's to run and keeps starting.\n this.startIfIdle();\n // A driver without `subscribe` is only polled, so without the wake a job\n // dispatched here would wait up to `pollInterval` in a queue with room.\n // A spurious wake just claims nothing.\n //\n // A job written on the caller's transaction is not claimable until that\n // commits, so its wake waits for the commit too — woken now, the loop\n // would claim nothing and then sleep a whole `pollInterval` past it.\n // Registered here, synchronously, because the commit hook is found\n // through the caller's async context, and this is the one place that is\n // certainly still current.\n const wake = () =>\n !this.driver.subscribe && this.state === \"running\" && this.wake();\n const wakeAtCommit = options.joins === true && deferUntilCommit(wake);\n\n // The rejection is reported here unless the caller says it will do it\n // itself, and whatever the driver is. It used to depend on both, and was\n // lost either way: a polled driver got an empty rejection arm, which marks\n // `id` handled — and `id` is what `push` returns — so a fire-and-forget\n // `SendReceiptJob.dispatch(...)` whose INSERT failed (a failover, a pool\n // timeout, a table not migrated yet) produced no unhandled rejection and\n // no line anywhere, and the job simply never ran. A subscribing driver got\n // no arm at all, so the same failure surfaced as a bare unhandled\n // rejection. A caller that awaits still gets the rejection either way and\n // can still decide what to do about it.\n id.then(\n () => wakeAtCommit || wake(),\n (error: unknown) => {\n if (options.reportFailure === false) return;\n console.error(\n `[gemi] The queue driver could not record ${job.name}` +\n (options.committed\n ? `, which was held until its transaction committed. The ` +\n `transaction stays committed; the job did not run and will ` +\n `not be retried.`\n : `, so it did not run and will not be retried.`),\n error,\n );\n },\n );\n return id;\n }\n\n /**\n * Runs a dead-lettered job again, from attempt 1 with its whole\n * `maxAttempts`, and resolves to whether a dead job was found under `id`.\n * `false` for an id that is waiting, running, finished or unknown: only a\n * dead job is brought back, so this cannot restart one that is running\n * somewhere.\n *\n * The job is claimed the way a dispatch's is. On a server it is run here as\n * soon as there is room; from a console command it waits in the driver for\n * a server or a worker, which with a polling driver means up to\n * `pollInterval`.\n *\n * Refused for a driver without `retryDead`. The memory driver keeps nothing\n * of a dead job, so there is no id it could bring back, and resolving\n * `false` would read as \"no such job\" to someone holding an id they just saw\n * in a log.\n */\n async retryDead(id: string): Promise<boolean> {\n if (!this.driver.retryDead) {\n throw new Error(\n `This queue's driver keeps nothing of a dead-lettered job, so there ` +\n `is nothing to retry. The database driver keeps them.`,\n );\n }\n const found = await this.driver.retryDead(id);\n if (found) this.claimSoon();\n return found;\n }\n\n /**\n * Starts the loop unless `drain` stopped it or this process should leave a\n * shared driver's jobs to a server — the rule `push` explains.\n */\n private startIfIdle() {\n if (this.state === \"idle\" && (!this.durable || this.mayClaimHere())) {\n this.start();\n }\n }\n\n /**\n * For a job the driver has just made claimable: start the loop, or wake a\n * running one that only polls, as `push` does for a dispatch.\n */\n private claimSoon() {\n this.startIfIdle();\n if (!this.driver.subscribe && this.state === \"running\") this.wake();\n }\n\n /**\n * Starts claiming. Idempotent, and called by the first `push`, so an app\n * does not need to; it is public for a process that should run jobs it\n * never dispatched — ones a driver kept across a restart — and for resuming\n * after `drain`.\n */\n start() {\n if (this.state === \"running\") return;\n this.state = \"running\";\n // Only a development server's loop over a shared driver: a memory queue\n // is only ever fed by its own application, and a hand-built manager or a\n // test's is its caller's to stop.\n if (this.durable && this.application && this.mayClaimHere() && !isProduction()) {\n globalThis.__gemiDevQueue = this;\n }\n this.unsubscribe = this.driver.subscribe?.(() => this.wake());\n // A loop a `drain` has not yet seen off picks the new state up itself;\n // starting a second would claim twice per wake.\n if (this.looping) return this.wake();\n bootScope.runInAsyncScope(() => void this.loop());\n }\n\n /**\n * Stops claiming, waits up to `timeoutMs` for the jobs already running, and\n * reports the ones that did not finish.\n *\n * Nothing is cancelled — a job has no way to be told — so an unfinished job\n * keeps running after this resolves. What happens to it then is the\n * driver's: the memory driver loses it with the process, along with every\n * job still waiting; one that outlives the process lets another claim it\n * once the lease runs out.\n *\n * A later `push` records its job but does not restart the loop. `start()`\n * does.\n */\n async drain(timeoutMs = Infinity): Promise<DrainResult> {\n this.state = \"stopped\";\n if (globalThis.__gemiDevQueue === this) globalThis.__gemiDevQueue = undefined;\n this.unsubscribe?.();\n this.unsubscribe = undefined;\n this.wake();\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n const deadline = new Promise<void>((resolve) => {\n if (timeoutMs !== Infinity) timer = setTimeout(resolve, timeoutMs);\n });\n // The claim in progress first, because what it returns is run: those jobs\n // are leased to this process already, and handing them back would cost\n // each of them an attempt it never made.\n const settled = (async () => {\n await this.claiming?.catch(() => {});\n await Promise.allSettled([...this.inFlight.values()].map((e) => e.done));\n })();\n await Promise.race([settled, deadline]);\n clearTimeout(timer);\n\n return {\n unfinished: [...this.inFlight.values()].map((e) => ({ ...e.job })),\n };\n }\n\n /** `drain(0)`: stop claiming, and report what is running without waiting. */\n stop(): Promise<DrainResult> {\n return this.drain(0);\n }\n\n /** How many jobs this process is running right now. */\n get running(): number {\n return this.inFlight.size;\n }\n\n /**\n * Claims while there is room, runs what it claims, and otherwise sleeps\n * until something changes: a job finishing frees a slot, or the driver's\n * `subscribe` says there is work. A driver without `subscribe` is polled\n * every `pollInterval` instead. This replaces a one-second timer that\n * spun while the queue was full.\n *\n * `woken` records a wake that arrived while a claim was in flight, when\n * there was nothing yet to resume; without it that job would wait for the\n * next unrelated wake.\n */\n private async loop() {\n this.looping = true;\n try {\n await this.claimWhileRunning();\n } finally {\n this.looping = false;\n }\n }\n\n private async claimWhileRunning() {\n while (this.state === \"running\") {\n // Once the server has been told to stop, a driver that outlives the\n // process gets nothing new taken from it: a job claimed now would likely\n // be cut off by the exit, and cost an attempt and a lease's wait, when\n // another replica can run it instead. The memory driver keeps claiming\n // until the provider's `drain()` stops it, because nobody else can run\n // its jobs — one waiting at the signal, or dispatched by a request\n // still draining, is run here or not at all.\n const room = this.durable && isShuttingDown()\n ? 0\n : this.config.concurrency - this.inFlight.size;\n\n if (room > 0) {\n this.woken = false;\n const claim = this.driver.claim(room, {\n ...this.lease(),\n registered: {\n names: Object.keys(this.jobs),\n graceMs: this.config.unknownJobGrace,\n },\n });\n this.claiming = claim;\n let claimed: ClaimedJob[] = [];\n try {\n claimed = await claim;\n this.claimFailures = 0;\n } catch (error) {\n this.claimFailures++;\n console.error(\n `The queue could not claim jobs from its driver; it will try ` +\n `again in ${this.retryDelay()}ms.`,\n error,\n );\n } finally {\n this.claiming = undefined;\n }\n for (const job of claimed) this.execute(job);\n\n // A full batch may have left more behind; the next pass either claims\n // it or, with no room left, sleeps until a slot frees.\n if (claimed.length === room) continue;\n if (this.woken && this.inFlight.size < this.config.concurrency) {\n continue;\n }\n }\n\n await this.sleep();\n }\n }\n\n /**\n * Waits for the next thing worth waking for: a slot freeing, a `subscribe`\n * driver saying there is work, or the poll timer a driver without\n * `subscribe` gets.\n *\n * A claim that rejected arms a timer whatever the driver does, and that is\n * the whole reason this is not just the `subscribe` test. The wake that\n * prompted the lost claim has already been consumed, so a driver that only\n * speaks up on an enqueue — LISTEN/NOTIFY, say — has nothing left to\n * announce: an idle worker whose one claim died on a connection reset would\n * sit there, with every job already waiting, until some unrelated process\n * enqueued again. On a busy worker an in-flight job's `wake` covers it; an\n * idle one has nothing to be rescued by.\n *\n * The delay doubles per consecutive failure so that storage which is\n * properly down is not asked once a `pollInterval` for as long as it stays\n * down, and the first claim that returns clears the count.\n */\n private sleep() {\n return new Promise<void>((resolve) => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const delay = this.claimFailures > 0\n ? this.retryDelay()\n : this.driver.subscribe\n ? undefined\n : this.config.pollInterval;\n if (delay !== undefined) {\n // Unref'd: a polling queue alone should not hold the process open.\n timer = setTimeout(() => this.wake(), delay);\n timer.unref?.();\n }\n this.resume = () => {\n clearTimeout(timer);\n resolve();\n };\n });\n }\n\n /** `pollInterval`, doubled once per consecutive claim failure, capped. */\n private retryDelay() {\n const doublings = Math.min(Math.max(0, this.claimFailures - 1), 30);\n return Math.min(this.config.pollInterval * 2 ** doublings, maxClaimBackoff);\n }\n\n private wake() {\n this.woken = true;\n const resume = this.resume;\n this.resume = undefined;\n resume?.();\n }\n\n /**\n * Whether the driver keeps jobs past this process: anything but memory.\n * Only such a queue can be fed by one process and drained by another.\n */\n get durable() {\n return !(this.driver instanceof MemoryQueueDriver);\n }\n\n /** See `claimsInThisProcess`. A manager built by hand always may. */\n private mayClaimHere() {\n return !this.application || claimsInThisProcess();\n }\n\n private lease(): ClaimOptions {\n return { visibilityTimeoutMs: this.config.visibilityTimeout };\n }\n\n private execute(claimed: ClaimedJob) {\n const key = `${claimed.id}:${claimed.attempt}`;\n const attempt = () => this.run(claimed);\n const done = (\n this.application\n ? kernelContext.run(this.application, attempt)\n : attempt()\n )\n .catch((error) => {\n // Only the driver's own reports reach here. The claim was not ended,\n // so its lease runs out and the job is claimed again.\n console.error(\n `The queue could not record the outcome of ${claimed.name}.`,\n error,\n );\n })\n .finally(() => {\n this.inFlight.delete(key);\n this.wake();\n });\n this.inFlight.set(key, { job: claimed, done });\n this.heartbeat();\n }\n\n /**\n * Keeps every running job's lease alive, at a third of the visibility\n * timeout so one late tick does not lose it. Stops itself once nothing is\n * running, and is unref'd, like the poll.\n */\n private heartbeat() {\n const driver = this.driver;\n if (!driver.heartbeat || this.heartbeatTimer) return;\n this.heartbeatTimer = setInterval(\n () => {\n if (this.inFlight.size === 0) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = undefined;\n return;\n }\n const jobs = [...this.inFlight.values()].map((e) => e.job);\n driver.heartbeat!(jobs, this.lease()).catch((error) => {\n console.error(`The queue could not extend its job leases.`, error);\n });\n },\n Math.max(1, Math.floor(this.config.visibilityTimeout / 3)),\n );\n this.heartbeatTimer.unref?.();\n }\n\n /**\n * One attempt at one claimed job, ending the claim either way.\n *\n * `onSuccess` is inside the `try`, as it always was, so a throw from it is a\n * failed attempt and is retried. `onFail` and `onDeadletter` are not: a\n * throw from either is logged and the claim still ends, where it used to\n * escape the queue with the job's slot still counted as taken.\n */\n private async run(claimed: ClaimedJob) {\n const Job = this.jobs[claimed.name];\n\n if (!Job) {\n // A driver other processes share may have handed this out because it\n // cannot filter by name, and while a blue/green ramp has both releases\n // claiming from one table, a name this replica lacks is usually one only\n // the other release has. So it goes back, without spending an attempt,\n // for a replica that has the class — until `unknownJobGrace` has passed\n // since the dispatch, after which the name is taken to be gone. Measured\n // from `createdAt` because that is all a claim carries; it is the\n // driver's clock against this one, and the skew is seconds against a\n // window of minutes. A driver that does filter only hands out an unknown\n // name once it has waited out the same window, so it normally lands\n // below; with this clock a few seconds behind the driver's, one handed\n // out right at the boundary is released instead, and — its window now\n // restarting from the release — waits one more `unknownJobGrace`. Late,\n // not lost, so no slack is subtracted for it.\n const age = Date.now() - claimed.createdAt;\n if (this.durable && age < this.config.unknownJobGrace) {\n // Once per name: with a driver that cannot filter, every poll can\n // claim the same job again, and a line per poll would bury the one\n // that matters — a replica whose discovery missed a job, say.\n if (!this.releasedNames.has(claimed.name)) {\n this.releasedNames.add(claimed.name);\n console.error(\n `Left a queued \"${claimed.name}\" for another replica: nothing is ` +\n `registered here under that name. It is dead-lettered if no ` +\n `replica has run it within unknownJobGrace ` +\n `(${this.config.unknownJobGrace}ms) of its dispatch.`,\n );\n }\n // Off this replica's own rhythm, and differently each time. It claims\n // again the moment the release lands, finds nothing due, and then\n // polls every `pollInterval`; a delay of exactly one interval made the\n // job due at the instant of that next poll, so the replica that could\n // not run it won the race for it every time. Any fixed fraction has\n // the same flaw for a replica whose polls happen to fall just before\n // the due moment each cycle — it loses every race, for good. Random\n // within one interval, every other replica's next poll has a chance.\n await this.driver.release(claimed, {\n retryInMs: Math.round(\n this.config.pollInterval * (1 + Math.random()),\n ),\n });\n return;\n }\n\n // The one place this is observable. A dispatch carries a name, the\n // registry is keyed by name, and a name nobody registered matches\n // nothing — which is exactly the silence #322 is about, except here it\n // has already happened and the work is gone. Saying so is all that is\n // left to do about it. Dead-lettered, which for the memory driver is the\n // drop it always was and for a durable one leaves a record to find.\n console.error(\n `Dropped a queued job: nothing is registered under the name ` +\n `\"${claimed.name}\". If the class exists, it was not ` +\n `discovered — check that it is under the queue slice's jobsDir ` +\n `(app/jobs by default), or list it in app/config/queue.ts.`,\n );\n await this.driver.fail(claimed, {\n error: `No job is registered under the name \"${claimed.name}\".`,\n retryInMs: null,\n });\n return;\n }\n\n let job: InstanceType<typeof Job>;\n try {\n job = new Job();\n } catch (error) {\n // A constructor or a class field that throws — `private mailer =\n // app(Mailer)` with nothing bound, say. Every other failure in here is\n // reported against the instance, and this one has no instance to report\n // against, which is exactly why it has to be caught here rather than\n // left to the generic path: that path never ends the claim, so the lease\n // lapses, the job is claimed again, and the constructor throws again,\n // once per visibility timeout, forever. The `maxAttempts` guard below\n // would normally stop that, and it cannot — answering \"how many attempts\n // does this job get?\" needs the instance that cannot be built. So it is\n // dead-lettered unrun, like a job whose arguments are not JSON: no\n // attempt of it can ever do anything but this.\n console.error(\n `Dropped a queued ${claimed.name}: it could not be constructed.`,\n error,\n );\n await this.driver.fail(claimed, {\n error: `The job could not be constructed: ${String(error)}`,\n retryInMs: null,\n });\n return;\n }\n\n let args: any[];\n try {\n args = JSON.parse(claimed.args);\n } catch (error) {\n // Not something `Job.dispatch` writes, so a driver changed it. Left\n // alone, the claim would never end and the job would be reclaimed,\n // unparseable, forever.\n console.error(\n `Dropped a queued ${claimed.name}: its arguments are not JSON.`,\n error,\n );\n await this.driver.fail(claimed, {\n error: `The arguments are not JSON: ${String(error)}`,\n retryInMs: null,\n });\n return;\n }\n\n // Claimed past its last attempt: an earlier claim used that attempt up and\n // never reported, which is what a process exiting mid-run looks like from\n // here. Running it again would exceed `maxAttempts`, so it is dead-lettered\n // unrun. `Math.max` keeps a `maxAttempts` below one meaning what it always\n // did — one attempt.\n if (claimed.attempt > Math.max(1, job.maxAttempts)) {\n const error = new Error(\n `${claimed.name} was claimed for attempt ${claimed.attempt} of ` +\n `${job.maxAttempts}: an earlier attempt never finished, most likely ` +\n `because the process running it exited. Dead-lettered without ` +\n `running it again.`,\n );\n hook(`${claimed.name}.onDeadletter`, () =>\n job.onDeadletter(error, ...args),\n );\n await this.driver.fail(claimed, {\n error: error.message,\n retryInMs: null,\n });\n return;\n }\n\n try {\n const result = await (job.worker\n ? runInWorker(claimed.name, claimed.args)\n : job.run(...args));\n\n job.onSuccess(result, ...args);\n } catch (err) {\n const error = err as Error;\n const recorded = String(error?.stack ?? error);\n hook(`${claimed.name}.onFail`, () => job.onFail(error, ...args));\n\n if (claimed.attempt >= job.maxAttempts) {\n hook(`${claimed.name}.onDeadletter`, () =>\n job.onDeadletter(error, ...args),\n );\n await this.driver.fail(claimed, { error: recorded, retryInMs: null });\n } else {\n await this.driver.fail(claimed, {\n error: recorded,\n retryInMs: backoffFor(job.backoff, claimed.attempt),\n });\n }\n return;\n }\n\n await this.driver.complete(claimed);\n }\n}\n\nfunction hook(name: string, fn: () => void) {\n try {\n fn();\n } catch (error) {\n console.error(`${name} threw; the job's claim was ended anyway.`, error);\n }\n}\n\nfunction isProduction() {\n return process.env.NODE_ENV === \"production\";\n}\n\n/**\n * Whether this process should claim from a driver shared with other\n * processes: a `gemi queue:work` worker, or a server's main thread unless\n * `GEMI_QUEUE_CLAIM=off` hands its jobs to workers. `ROOT_DIR` is set only by\n * a starting server or worker, so a console command, a seed\n * or a migration — which boot the same providers — is not one, and neither is\n * a `worker` job's thread, which clones the application and exits after it.\n *\n * A worker claims whatever `GEMI_QUEUE_CLAIM` says, because the variable is\n * for the web process, and a deploy that sets it once for every container\n * would otherwise leave nothing claiming at all.\n */\nexport function claimsInThisProcess() {\n if (!isMainThread) return false;\n if (isQueueWorker()) return true;\n return process.env.ROOT_DIR !== undefined && !claimingTurnedOff();\n}\n\n/**\n * `GEMI_QUEUE_CLAIM=off` (any case): this server dispatches into a shared\n * driver and leaves the claiming to `gemi queue:work` processes. Anything else\n * claims, as before. An environment variable rather than a config field for\n * the reason `GEMI_NO_SCHEDULE` is one: the web and the worker run the same\n * build with the same config, and only the process knows which it is.\n */\nexport function claimingTurnedOff(env: Record<string, string | undefined> = process.env) {\n return (env.GEMI_QUEUE_CLAIM ?? \"\").trim().toLowerCase() === \"off\";\n}\n\n// On `globalThis` under a registry symbol, like the shutdown flag, so the mark\n// the worker entry sets is the one this module reads if two copies of it are\n// loaded. Not an environment variable: a job that spawns a child process\n// would hand it on, and the child, a script, would start claiming.\nconst QUEUE_WORKER = Symbol.for(\"gemi.queue.worker\");\n\n/** Marks this process as a `gemi queue:work` worker; see `claimsInThisProcess`. */\nexport function markQueueWorker(worker = true) {\n (globalThis as { [QUEUE_WORKER]?: boolean })[QUEUE_WORKER] = worker;\n}\n\nexport function isQueueWorker(): boolean {\n return (globalThis as { [QUEUE_WORKER]?: boolean })[QUEUE_WORKER] === true;\n}\n\n/**\n * The delay before the retry that follows attempt `attempt`: the number\n * itself, or the array's entry for that retry with its last entry repeated.\n */\nexport function backoffFor(backoff: number | number[], attempt: number) {\n const delay = Array.isArray(backoff)\n ? backoff[Math.min(attempt, backoff.length) - 1]\n : backoff;\n return Math.max(0, delay ?? 0);\n}\n\nfunction resolveDriver(\n driver: Required<QueueConfig>[\"driver\"],\n application: Application | undefined,\n): QueueDriver {\n if (driver === \"memory\") return new MemoryQueueDriver();\n if (driver === \"database\" || typeof driver === \"function\") {\n // Only a manager built by hand has no application. A factory that takes\n // one would otherwise fail on `undefined` with a TypeError that names\n // neither the queue nor what is missing.\n if (!application && (driver === \"database\" || driver.length > 0)) {\n throw new Error(\n `The queue driver needs the application it belongs to, and this ` +\n `QueueManager was built without one. Pass { application }.`,\n );\n }\n if (driver === \"database\") {\n return new DatabaseQueueDriver(application!.make(DatabaseManager));\n }\n return driver(application!);\n }\n if (typeof driver === \"string\") {\n throw new Error(\n `Unknown queue driver \"${driver}\". The queue slice's driver is ` +\n `\"memory\", \"database\", a QueueDriver, or a function returning one.`,\n );\n }\n return driver;\n}\n"
|
|
9
9
|
],
|
|
10
10
|
"mappings": "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",
|
|
11
|
-
"debugId": "
|
|
11
|
+
"debugId": "749CF7005C17653964756E2164756E21",
|
|
12
12
|
"names": []
|
|
13
13
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{
|
|
2
|
+
import{Mf as X,Tf as K,Wf as W}from"./chunk-jcq0tbdv.js";import{AsyncLocalStorage as U}from"async_hooks";var v=new U;function h(){return v.getStore()?.tx}function S(){return v.getStore()?.connection??K}function E(j,q){let z=v.getStore();if(S()===j)return q();return y(j),v.run({...z,depth:z?.depth??0,connection:j===K?void 0:j},q)}function y(j){let q=v.getStore();if(q?.tx===void 0)return;let z=q.connection??K;if(z===j)return;throw new W(z,j)}function g(j){let q=v.getStore();if(q?.tx===void 0||q.afterCommit===void 0)return!1;return q.afterCommit.push(j),!0}function k(j){let q=v.getStore();if(q?.tx===void 0||q.commitDependsOn===void 0)return!1;return q.commitDependsOn.push(j),!0}class F extends Error{constructor(j){super(`A statement this transaction depended on failed after being issued on it, so the transaction rolled back instead of committing: ${j instanceof Error?j.message:String(j)}`,{cause:j});this.name="TransactionDependencyError"}}async function R(j,q){if(j.length===q)return;let B=(await Promise.allSettled(j.slice(q))).find((J)=>J.status==="rejected");if(B)throw new F(B.reason)}function O(){let j=v.getStore();return j?.tx===void 0?null:j.depth}function N(){return v.getStore()?.system===!0}function x(j){let q=v.getStore();return v.run({...q,depth:q?.depth??0,system:!0},j)}function T(){return v.getStore()?.actor}function p(j,q){let z=v.getStore();return v.run({...z,depth:z?.depth??0,system:!1,actor:{user:j??null}},q)}function L(j){if(Bun.env.NODE_ENV!=="development")return null;if(j===!1)return null;if(j===void 0)return X;if(!Number.isFinite(j)||j<=0)return X;return j}function _(j){let q=L(j);if(q===null)return()=>{};let z=Error("transaction opened here"),B=setTimeout(()=>{let J=z.stack?.split(`
|
|
3
3
|
`).slice(1).join(`
|
|
4
4
|
`)??"";console.warn(`[gemi] A database transaction has not settled after ${q}ms. It `+"reserves a pooled connection until it does \u2014 check for network or "+`filesystem I/O inside the callback, and move it outside the transaction if you find any.
|
|
5
5
|
${J}
|
|
6
6
|
(development only; set database.slowTransactionThreshold to change the threshold, or false to switch this off)`)},q);return B.unref?.(),()=>clearTimeout(B)}async function b(j,q){if(j.length===0)return;await v.run({...q,tx:void 0,afterCommit:void 0,commitDependsOn:void 0,depth:q?.depth??0},async()=>{for(let z of j)try{await z()}catch(B){console.error("An after-commit callback threw. The transaction it was waiting on had already committed and stays committed, and the callbacks after it still ran.",B)}})}function l(j,q,z){let B=v.getStore(),J=z?.connection??K;if(y(J),B?.tx){let G=B.afterCommit,Q=G?.length??0,P=B.commitDependsOn,M=P?.length??0;return B.tx.savepoint((V)=>{let H=V;return v.run({...B,tx:H,depth:B.depth+1},async()=>{let I=await q(H);if(P)await R(P,M);return I})}).catch((V)=>{if(G)G.length=Q;if(P)P.length=M;throw V})}let Y=_(z?.slowTransactionThreshold),Z=[],$=[];try{return j.begin((G)=>v.run({...B,tx:G,depth:0,connection:J===K?void 0:J,afterCommit:Z,commitDependsOn:$},async()=>{let Q=await q(G);return await R($,0),Q})).finally(Y).then(async(G)=>{return await b(Z,B),G})}catch(G){throw Y(),G}}
|
|
7
|
-
export{v as
|
|
7
|
+
export{v as wf,h as xf,S as yf,E as zf,y as Af,g as Bf,k as Cf,F as Df,O as Ef,N as Ff,x as Gf,T as Hf,p as If,l as Jf};
|
|
8
8
|
|
|
9
|
-
//# debugId=
|
|
9
|
+
//# debugId=55FE6868B1E8699064756E2164756E21
|
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
"import type { SQL, TransactionSQL } from \"bun\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { SLOW_TRANSACTION_THRESHOLD } from \"../database/config\";\nimport {\n CrossConnectionTransactionError,\n DEFAULT_CONNECTION,\n} from \"../database/Connection\";\n\n/**\n * The ambient transaction: the ORM's own `AsyncLocalStorage`, holding nothing\n * but the open handle and how deep the nesting is.\n *\n * One store per scope with one owner, which is the pattern the framework\n * already follows — `kernel/context.ts` holds the Application,\n * `http/requestContext.ts` the request and its user,\n * `services/pubsub/BroadcastManager.ts` a socket's headers. The reasoning for\n * not folding this into the kernel's is written up beside it, and the short\n * version is that a transaction scope has to *nest inside* an Application scope\n * without replacing it, and `foundation/app.ts` must keep reading exactly one\n * shape out of the kernel store or resolving a service becomes a transaction\n * concern.\n *\n * What makes an ALS the right home rather than a field somewhere: two concurrent\n * requests each in their own transaction must never see each other's handle.\n * Anything reachable from the Application — which is one object shared by every\n * in-flight request — fails that by construction, and fails it as silently\n * written data, not as an error. \"two concurrent transactions never see each\n * other's handle\" in `context.test.ts` is the test that would catch it.\n */\nexport interface OrmScope {\n /**\n * Bun's transaction handle. Every ORM statement in scope runs on it, which\n * means they all run on one reserved connection — so nothing here may issue\n * two statements concurrently. `Model.$exec` already runs its nested writes\n * and its relation reads sequentially, for exactly this reason.\n */\n tx?: TransactionSQL;\n /** 0 for the outermost transaction, 1+ for each savepoint inside it. */\n depth: number;\n /**\n * Which named connection this subtree's queries run on, and — when `tx` is\n * set — which connection the open transaction belongs to.\n *\n * Ambient rather than an argument threaded through `$exec`, for the same\n * reason the handle above is: a relation read resolves its target through the\n * registry and calls that class's `$exec`, so an argument would have to be\n * carried by every intermediate that has no interest in it, and the one that\n * forgot would silently read a nested `include` off the *other* pool. There\n * is no error that shape produces; there are just rows from the wrong place.\n *\n * `undefined` means the default connection. Kept as absent-rather-than-\n * `\"default\"` so that entering a scope on the default connection is\n * indistinguishable from not entering one, which is what makes the\n * cross-connection check below cost a comparison of two `undefined`s on every\n * application that never declares a second connection.\n */\n connection?: string;\n /**\n * Whether policies are suspended for this scope. Set only by\n * `Model.asSystem`, and never by anything ambient — the whole point is that\n * unscoped access is a decision visible at the call site, not the accidental\n * result of a missing user.\n */\n system?: boolean;\n /**\n * The user policies should scope to, when there is no request to read one\n * from. Set only by `Model.asUser`.\n *\n * A sentinel wrapper rather than the bare value, so that \"a scope was entered\n * with `null`\" stays distinguishable from \"no scope was entered\" — the two\n * mean different things under deny-by-default.\n */\n actor?: { user: unknown };\n /**\n * Work that must not happen unless this transaction commits — an event whose\n * `static afterCommit` is set, today, and nothing else.\n *\n * **Created by the outermost `withTransaction` and never by a savepoint.** A\n * savepoint's scope is a spread of its parent's (`{ ...current, tx: nested }`),\n * so the array arrives here already shared by reference with the transaction\n * that opened it — which is the behaviour wanted rather than a leak. A\n * dispatch made inside a savepoint that commits, in a transaction that then\n * rolls back, must not run: rolling back the outermost transaction discards\n * the list whole, savepoint entries included, because nothing ever drains it.\n *\n * The inverse — a savepoint that rolls back under a transaction that commits\n * — is the case sharing gets wrong on its own, and `withTransaction`'s\n * savepoint branch is where it is put right.\n *\n * Absent, rather than an empty array, on every scope that is not a\n * transaction: `runAsSystem`, `runAsUser` and `runOnConnection` spread\n * whatever they are given, so \"there is a list here\" and \"there is an open\n * transaction here\" have to stay the same question. `deferUntilCommit`\n * answers `false` when either half is missing and the caller runs the work\n * now, which is what keeps a hand-built scope carrying a `tx` — the shape\n * tests write — from swallowing a dispatch into a list nothing drains.\n */\n afterCommit?: AfterCommitCallback[];\n /**\n * Statements written on `tx` that the commit must wait for and must not\n * outlive — today, a job row the database queue driver wrote on the\n * transaction (#563), and nothing else. See `commitDependsOn`.\n *\n * Created and shared exactly as `afterCommit` is, and truncated with it\n * when a savepoint rolls back.\n */\n commitDependsOn?: Promise<unknown>[];\n}\n\n/**\n * Work deferred to a transaction's commit.\n *\n * Awaited when it is drained, and its rejection is caught there: a callback is\n * a side effect that the transaction is not waiting on and cannot be told\n * about, so the only thing left to do with a failure is report it.\n *\n * Not exported: a caller passes a lambda and never names this, and an export\n * nothing outside this file uses is what `orm/architecture.test.ts` is pointed\n * at. The declaration emit carries it along regardless, so `OrmScope` stays\n * readable from a `.d.ts`.\n */\ntype AfterCommitCallback = () => void | Promise<void>;\n\n/**\n * One ambient scope for the ORM, not one per feature.\n *\n * Transactions (iteration 5) and system access (iteration 6) are both\n * \"something is true for this async subtree and every query in it\", so they\n * share a store rather than minting a second `AsyncLocalStorage` each. The\n * inventory note in `kernel/context.ts` sets the bar for adding one; two\n * booleans-worth of ORM state does not clear it.\n */\nexport const ormContext = new AsyncLocalStorage<OrmScope>();\n\n/**\n * The open transaction, or `undefined` outside one. This is what `Model.$exec`\n * consults to decide which connection a statement runs on.\n *\n * Exported as the escape hatch for code that genuinely needs the handle — a raw\n * query that must join the surrounding transaction, say. **Do not store what it\n * returns.** Bun's handle stays callable after its `begin` resolves: queries on\n * it then run on the pooled connection, outside any transaction, and succeed.\n * Verified against both dialects, and it is the reason `Model.transaction`'s\n * callback takes no argument.\n */\nexport function currentTransaction(): TransactionSQL | undefined {\n return ormContext.getStore()?.tx;\n}\n\n/**\n * The connection every query in this scope runs on unless it names another.\n *\n * `\"default\"` outside any scope, so callers get a name rather than a\n * `string | undefined` to normalise themselves — the store keeps it absent,\n * which is not the same distinction and is nobody else's business.\n */\nexport function currentConnectionName(): string {\n return ormContext.getStore()?.connection ?? DEFAULT_CONNECTION;\n}\n\n/**\n * Run `fn` with `name` as the ambient connection.\n *\n * Merged into the current scope rather than replacing it, exactly as\n * `runAsSystem` and `runAsUser` are: naming a connection must not drop an open\n * transaction handle or re-enable policies underneath it.\n *\n * Deliberately absent from `orm/index.ts`, so it is not part of what an\n * application can reach — `Model.on(name)` and `DB.connection(name)` are the\n * two doors, and both funnel through here so that a nested relation read,\n * which resolves its own class out of the registry and calls its `$exec`,\n * inherits the connection without anything having to pass it along.\n */\nexport function runOnConnection<T>(\n name: string,\n fn: () => Promise<T>,\n): Promise<T> {\n const current = ormContext.getStore();\n if (currentConnectionName() === name) return fn();\n\n // ENTERING A CONNECTION IS WHERE THE CROSS-CONNECTION CHECK BELONGS, not at\n // each door — and it has to be *here*, before the scope is entered, because\n // entering it is what makes the check impossible afterwards.\n //\n // The scope carries `tx` and `connection` together, so overwriting the name\n // while an open handle stays in place produces a store that says \"a\n // transaction on analytics\" while holding the *default* connection's handle.\n // Every check downstream then agrees with itself and the statement runs on\n // the wrong connection's transaction. `Model.save` found this: it routes to\n // the row's own connection, so it is the one caller that switches connections\n // with a transaction already open.\n assertConnectionUsable(name);\n\n return ormContext.run(\n {\n ...current,\n depth: current?.depth ?? 0,\n connection: name === DEFAULT_CONNECTION ? undefined : name,\n },\n fn,\n );\n}\n\n/**\n * Refuse a statement that names one connection while a transaction is open on\n * another.\n *\n * The check `withTransaction`'s single-connection note asked for, and the\n * reasoning for refusing rather than routing is on\n * `CrossConnectionTransactionError`. Both doors call it — `Model.$exec` for\n * every model operation, `DB` for a raw fragment — because either one alone\n * leaves the other free to straddle the two connections, and a raw `DB.execute`\n * escaping a transaction is the more damaging of the two.\n *\n * Only ever fires on a query that *names* a connection: an unqualified one\n * resolves to the ambient name, which is the transaction's own.\n */\nexport function assertConnectionUsable(name: string): void {\n const store = ormContext.getStore();\n if (store?.tx === undefined) return;\n\n const open = store.connection ?? DEFAULT_CONNECTION;\n if (open === name) return;\n\n throw new CrossConnectionTransactionError(open, name);\n}\n\n/**\n * Hold `callback` until the open transaction commits, and say whether that\n * happened.\n *\n * `false` means there was nothing to wait for — no transaction is open, or the\n * scope was entered by hand rather than by `withTransaction` — and the caller\n * must run the work itself, now. That return value is the whole contract: a\n * caller that ignores it turns \"no transaction here\" into a side effect queued\n * on a list nothing will ever drain, which is a dispatch that silently never\n * happens.\n *\n * `true` means the callback is on the outermost scope's list and will run after\n * `begin` resolves, outside the transaction, whatever the caller does next. It\n * will not run at all if the transaction rolls back, or if a savepoint it was\n * registered inside rolls back — both of which are the point.\n *\n * Nothing here is ordered against anything but the other deferred callbacks:\n * they run in registration order, one at a time, after the commit round-trip.\n */\nexport function deferUntilCommit(callback: AfterCommitCallback): boolean {\n const store = ormContext.getStore();\n if (store?.tx === undefined || store.afterCommit === undefined) return false;\n\n store.afterCommit.push(callback);\n return true;\n}\n\n/**\n * Make the open transaction's commit depend on `write`, a statement issued on\n * its handle: the transaction does not commit until `write` settles, and\n * rolls back — rejecting with `TransactionDependencyError` — if it rejected.\n *\n * For a write the caller may not await. A queued listener's dispatch never is\n * (`EventManager` pushes it and moves on), and on Postgres a statement that\n * fails aborts the whole transaction: every statement after it is refused and\n * Bun's `COMMIT` quietly becomes a rollback. Without this, the callback's\n * promise resolved, `begin` resolved, and the rows the caller wrote were gone\n * with nothing to say so. MySQL does not abort on a failed statement, and is\n * failed here anyway, so that a joined write means the same thing on both:\n * it commits with the transaction or the transaction does not commit.\n *\n * `false` when there is no transaction to depend on, as for\n * `deferUntilCommit`.\n */\nexport function commitDependsOn(write: Promise<unknown>): boolean {\n const store = ormContext.getStore();\n if (store?.tx === undefined || store.commitDependsOn === undefined) {\n return false;\n }\n store.commitDependsOn.push(write);\n return true;\n}\n\n/**\n * A write the transaction's commit depended on failed, so the transaction —\n * or the savepoint the write was made in — rolled back. `cause` is the write's\n * own error.\n */\nexport class TransactionDependencyError extends Error {\n constructor(cause: unknown) {\n super(\n `A statement this transaction depended on failed after being issued on ` +\n `it, so the transaction rolled back instead of committing: ` +\n `${cause instanceof Error ? cause.message : String(cause)}`,\n { cause },\n );\n this.name = \"TransactionDependencyError\";\n }\n}\n\n/**\n * Wait for the writes registered from `mark` on, and throw if one failed.\n *\n * Called at the end of a transaction's or savepoint's callback, *inside*\n * `begin`/`savepoint`, so a throw is what makes Bun roll back rather than\n * commit — after `begin` has resolved it is too late to do anything but\n * report. Every write is settled before throwing, so none is still running on\n * the handle when the rollback is issued.\n */\nasync function awaitDependencies(\n writes: Promise<unknown>[],\n mark: number,\n): Promise<void> {\n if (writes.length === mark) return;\n const settled = await Promise.allSettled(writes.slice(mark));\n const failed = settled.find(\n (outcome): outcome is PromiseRejectedResult => outcome.status === \"rejected\",\n );\n if (failed) throw new TransactionDependencyError(failed.reason);\n}\n\n/** How deeply nested the current transaction is; `null` outside one. */\nexport function transactionDepth(): number | null {\n const store = ormContext.getStore();\n return store?.tx === undefined ? null : store.depth;\n}\n\n/** Whether policies are suspended for the current async scope. */\nexport function isSystemScope(): boolean {\n return ormContext.getStore()?.system === true;\n}\n\n/**\n * Run `fn` with policies suspended.\n *\n * The escape hatch the deny-by-default rule needs to be usable: a cron tick, a\n * queue worker or a seed script has no user, and under deny-by-default every\n * policied model would refuse it. This makes that intent explicit *at the call\n * site*, which is the property that matters — unscoped access must never be\n * what happens when a user simply fails to turn up.\n *\n * It nests inside a transaction and a transaction nests inside it: the scope is\n * merged rather than replaced, so `asSystem` does not silently drop an open\n * handle.\n */\nexport function runAsSystem<T>(fn: () => Promise<T>): Promise<T> {\n const current = ormContext.getStore();\n return ormContext.run({ ...current, depth: current?.depth ?? 0, system: true }, fn);\n}\n\n/** The explicitly-set actor, or `undefined` when none was. */\nexport function currentActor(): { user: unknown } | undefined {\n return ormContext.getStore()?.actor;\n}\n\n/**\n * Run `fn` with policies scoped to `user`, for code with no request to read one\n * from.\n *\n * The counterpart to `runAsSystem`, and the reason that one is not the only\n * escape hatch. A queue worker processing \"send the invoice for organisation 7\"\n * genuinely acts *as somebody*; giving it `asSystem` would suspend policies\n * entirely and leave it hand-scoping every query — which is precisely the\n * unscoped-by-accident failure the deny-by-default rule exists to prevent. The\n * narrow tool should be the easy one.\n *\n * It is also what makes policies testable without reaching into\n * `RequestContext`'s internals.\n *\n * Takes precedence over the request store, so a job that says who it is acting\n * as is not quietly overridden by an ambient request that happens to enclose it.\n *\n * It also **clears `system`**, which is the more surprising half. Acting as a\n * user is strictly narrower than acting as the system, so naming one inside an\n * `asSystem` block has to narrow back — and the spread would otherwise carry\n * `system: true` forward, leaving `$exec` to skip policies entirely while\n * `currentUser()` cheerfully returned the actor. Silently unscoped, from the\n * realistic shape: a worker enters `asSystem` to read its job queue, then\n * narrows to the job's owner for the work itself. The reverse nesting —\n * `asSystem` inside `asUser` — suspends policies, which is what it should do.\n */\nexport function runAsUser<T>(user: unknown, fn: () => Promise<T>): Promise<T> {\n const current = ormContext.getStore();\n return ormContext.run(\n {\n ...current,\n depth: current?.depth ?? 0,\n system: false,\n actor: { user: user ?? null },\n },\n fn,\n );\n}\n\n/**\n * The warning threshold in milliseconds, or `null` when the warning is off.\n *\n * `configured` is `database.slowTransactionThreshold` — see `DatabaseConfig`.\n * It arrives as a value rather than being read from here so that this file\n * keeps knowing nothing about the container: `withTransaction` is reachable\n * with a bare pool, and its tests use one.\n *\n * Exported for its own tests. The behaviour worth pinning is the fallback —\n * that a malformed threshold lands on the default rather than disabling — and\n * that cannot be observed through `withTransaction`: \"no warning fired\" is what\n * a disabled warning looks like too, so a behavioural test of it passes just as\n * happily when the fallback is gone. Asserting the number directly is the only\n * form of that test that can fail.\n */\nexport function slowTransactionThreshold(\n configured?: number | false,\n): number | null {\n // Read per call, never cached at module scope. `scripts/build.ts` rewrites\n // `process.env.NODE_ENV` to `Bun.env.NODE_ENV` precisely so mode stays a\n // runtime question in the built framework; caching it here would undo that.\n if (process.env.NODE_ENV !== \"development\") return null;\n\n // The one way to switch it off, and it has to be spelled. Everything else\n // falls back, so the warning is never lost to a mistyped value — only to an\n // explicit `false`.\n if (configured === false) return null;\n\n if (configured === undefined) return SLOW_TRANSACTION_THRESHOLD;\n if (!Number.isFinite(configured) || configured <= 0) {\n return SLOW_TRANSACTION_THRESHOLD;\n }\n return configured;\n}\n\n/**\n * Warn, in development only, about a transaction that has been open too long.\n *\n * The risk is structural rather than occasional: an open transaction holds one\n * reserved connection for as long as its callback runs, and nothing about\n * `Model.transaction(async () => { ... })` stops that callback from awaiting a\n * `fetch`, an S3 upload or a slow queue push. Under concurrency that drains the\n * pool, and the symptom — every unrelated query in the process blocking on\n * connection acquisition — names neither the callback nor the I/O in it.\n *\n * A timer rather than measuring elapsed time on the way out, for one reason:\n * the worst case is the transaction that *never* settles (a hung request, a\n * lock wait, a deadlock). After-the-fact measurement is silent for exactly that\n * case; a timer fires while it is still open and still holding the connection.\n *\n * The `Error` is constructed up front and read only if the timer fires — V8\n * formats `.stack` lazily on first access, so an ordinary fast transaction pays\n * for an object allocation and nothing else.\n *\n * The clock starts at the `begin` call, not at the moment a connection is\n * acquired, and stops when `begin` settles rather than when the callback\n * returns — so it spans the pool wait and the commit round-trip as well as the\n * callback. That is deliberate: all three are time this transaction is in\n * flight, and a warning that only covered the callback would stay silent for a\n * transaction stuck waiting on an exhausted pool, which is the exact situation\n * a long transaction elsewhere creates. Hence \"not settled\" rather than \"open\n * for\" — the message must not claim a connection was held for the whole span.\n */\nfunction watchForSlowTransaction(configured?: number | false): () => void {\n const threshold = slowTransactionThreshold(configured);\n if (threshold === null) return () => {};\n\n const site = new Error(\"transaction opened here\");\n\n const timer = setTimeout(() => {\n const stack = site.stack?.split(\"\\n\").slice(1).join(\"\\n\") ?? \"\";\n console.warn(\n `[gemi] A database transaction has not settled after ${threshold}ms. It ` +\n `reserves a pooled connection until it does — check for network or ` +\n `filesystem I/O inside the callback, and move it outside the ` +\n `transaction if you find any.\\n${stack}\\n` +\n `(development only; set database.slowTransactionThreshold to change ` +\n `the threshold, or false to switch this off)`,\n );\n }, threshold);\n\n // Otherwise the warning timer is itself a reason the process stays alive,\n // which would turn a diagnostic into a hang in short-lived commands and\n // scripts.\n timer.unref?.();\n\n return () => clearTimeout(timer);\n}\n\n/**\n * Runs what a committed transaction deferred, outside the transaction it was\n * deferred by.\n *\n * `outside` is the scope that was ambient before `begin` — an `asSystem` block,\n * a named connection, or nothing at all — and re-entering it explicitly is the\n * guarantee rather than a tidy-up. The alternative is relying on where a `.then`\n * happens to have captured its context, and the failure that produces is the\n * one the whole feature is about: a listener that runs \"after commit\" while\n * still holding the closed transaction's handle joins nothing, because Bun's\n * handle stays callable and simply runs on the pool. So the scope is rebuilt\n * with `tx` and the drained list cleared, and a listener that opens its own\n * transaction gets a real one.\n *\n * Errors are caught per callback and the next one still runs, for the reason\n * the event subsystem catches per listener: these are independent side effects\n * whose order came from a filesystem walk. And the transaction is not failed by\n * one — it committed, the database kept the rows, and reporting a rejection to\n * the caller would have it roll back or retry work that is already durable.\n * stderr is all that is left, and it is enough: the caller has no decision to\n * make here.\n *\n * The transaction's own promise does not resolve until this has finished, which\n * is the one thing about `afterCommit` that a caller can feel. Deliberate: the\n * alternative is a floating promise whose rejection nobody sees and whose work\n * a short-lived process — a command, a seed — exits out from under.\n */\nasync function drainAfterCommit(\n callbacks: AfterCommitCallback[],\n outside: OrmScope | undefined,\n): Promise<void> {\n if (callbacks.length === 0) return;\n\n await ormContext.run(\n {\n ...outside,\n tx: undefined,\n afterCommit: undefined,\n commitDependsOn: undefined,\n depth: outside?.depth ?? 0,\n },\n async () => {\n for (const callback of callbacks) {\n try {\n await callback();\n } catch (error) {\n console.error(\n `An after-commit callback threw. The transaction it was waiting ` +\n `on had already committed and stays committed, and the ` +\n `callbacks after it still ran.`,\n error,\n );\n }\n }\n },\n );\n}\n\n/**\n * Run `fn` inside a transaction, entering the ambient scope for its whole async\n * subtree.\n *\n * The single implementation behind both `Model.transaction` and\n * `DB.transaction`, so the two cannot become transaction systems that ignore\n * each other: a `Model.create` inside a `DB.transaction` joins it, and a raw\n * `DB.sql` inside a `Model.transaction` can join it through\n * `currentTransaction()`.\n *\n * Nesting becomes a savepoint. Not a preference — Bun refuses the alternative\n * outright (`cannot call begin inside a transaction use savepoint() instead`),\n * and a savepoint is the semantics worth having anyway: an inner failure rolls\n * back to it and leaves the outer transaction usable, which a caller that\n * catches can rely on. Verified on SQLite and Postgres.\n *\n * Note Bun hands the savepoint callback the *same* handle object as the outer\n * transaction (`sp === tx`), so the depth counter is the only thing that\n * actually changes on the way in. The callback's argument is still what gets\n * stored, so this keeps working if that ever stops being true. The cast is\n * because Bun types a savepoint handle as plain `SQL` — without `savepoint` on\n * it — while the runtime object does carry the method, which is what makes\n * three levels of nesting work.\n *\n * Anything `deferUntilCommit` queued is drained once `begin` resolves, outside\n * the transaction and only on the outermost call — a savepoint has committed\n * nothing durable, so draining there would run work that a later rollback of\n * the enclosing transaction was meant to have prevented. See `drainAfterCommit`\n * for what that costs the caller.\n *\n * In development, an outermost transaction that stays open past\n * `options.slowTransactionThreshold` (2s by default, `false` to disable) warns\n * — see `watchForSlowTransaction`. Nothing here *prevents* a long transaction;\n * the warning exists because the cost of one is paid by unrelated queries\n * elsewhere in the process, which makes it hard to trace back.\n *\n * The threshold is passed in rather than read from `app(DatabaseManager)` here.\n * Every caller already holds the manager — it is where `pool` came from — and\n * taking it as an argument keeps this file free of the container, so a bare\n * `SQL` is still enough to call it. Its tests rely on that.\n *\n * `options.connection` is the name `pool` belongs to, and follows the same rule\n * for the same reason: the pool cannot be asked what it is called. Passing it\n * is what lets a nested `withTransaction` tell \"the same connection, so take a\n * savepoint\" from \"a different one, so refuse\" — omitting it means the default\n * connection, which is what a two-argument call always meant.\n *\n * The return type is asserted rather than inferred for one reason worth\n * knowing: Bun's `begin` unwraps a callback that resolves to an *array of\n * promises*, awaiting each. So `Model.transaction(async () => [a, b])` where\n * `a` and `b` are promises resolves to their values, not to the promises. That\n * is Bun's behaviour and is left alone; it is only surprising if unmentioned.\n */\nexport function withTransaction<T>(\n pool: SQL,\n fn: (tx: TransactionSQL) => Promise<T>,\n options?: { slowTransactionThreshold?: number | false; connection?: string },\n): Promise<T> {\n const current = ormContext.getStore();\n\n // THE SINGLE-CONNECTION ASSUMPTION, WHICH IS NOW CHECKED RATHER THAN PINNED.\n //\n // The savepoint branch below ignores `pool` entirely. That was correct while\n // there was one `DatabaseManager` and one `SQL` — the ambient handle and\n // whatever pool the caller passed were necessarily the same database — and\n // the note that stood here said what would happen when a second connection\n // became configurable (#327): a `DB.transaction` on B inside a\n // `Model.transaction` on A would savepoint on **A** and hand the callback A's\n // handle, so statements landed in the wrong database with no error at all.\n //\n // So the name is compared before the branch is taken, and a mismatch raises.\n // Refusing is the whole answer rather than half of one — there is no way to\n // make one transaction span two pools, and the alternatives are covered on\n // `CrossConnectionTransactionError`.\n //\n // A caller that passes no `connection` is treated as naming the default,\n // which is what `withTransaction(pool, fn)` meant before this argument\n // existed.\n const name = options?.connection ?? DEFAULT_CONNECTION;\n assertConnectionUsable(name);\n\n // `current?.tx` rather than `current`: since iteration 6 a scope can exist\n // with no open transaction — `Model.asSystem` enters one — and a savepoint\n // needs an actual handle, not merely a store.\n if (current?.tx) {\n // WHERE THE SHARED AFTER-COMMIT LIST IS PUT RIGHT.\n //\n // The savepoint's scope is a spread of this one, so it appends to the\n // transaction's list rather than to a list of its own — which is what makes\n // \"the outer transaction rolled back, so nothing runs\" free. The case that\n // is not free is this one: the savepoint rolls back and the transaction\n // goes on to commit. Its entries have to go, or a dispatch made inside a\n // block the database threw away runs anyway, which is the same wrong answer\n // as before with an extra step.\n //\n // A mark on the way in and a truncate on the way out, rather than tagging\n // each entry with its depth and dropping everything at or below the failing\n // one. Depth cannot tell two *siblings* apart: a savepoint that committed\n // and one that rolled back both left entries at the same depth, adjacent in\n // the list, and dropping by depth would discard the committed sibling's\n // work as well. The index is exact because the ORM issues one statement at\n // a time on a transaction handle — see `OrmScope.tx` — so nothing appends\n // between the mark and the failure but this savepoint's own subtree.\n const deferred = current.afterCommit;\n const mark = deferred?.length ?? 0;\n // The same mark for the writes the commit depends on. The savepoint waits\n // for its own before it releases, so one that failed rolls back only the\n // savepoint — which a caller that catches can survive, exactly as it\n // survives a statement that threw inside it — and is then forgotten\n // rather than failing the transaction a second time.\n const dependencies = current.commitDependsOn;\n const dependencyMark = dependencies?.length ?? 0;\n\n return current.tx\n .savepoint((sp) => {\n const nested = sp as TransactionSQL;\n return ormContext.run(\n { ...current, tx: nested, depth: current.depth + 1 },\n async () => {\n const result = await fn(nested);\n if (dependencies) {\n await awaitDependencies(dependencies, dependencyMark);\n }\n return result;\n },\n );\n })\n .catch((error: unknown) => {\n if (deferred) deferred.length = mark;\n if (dependencies) dependencies.length = dependencyMark;\n throw error;\n }) as Promise<T>;\n }\n\n // Only the outermost scope is watched. A savepoint reserves no connection of\n // its own — its lifetime is bounded by the transaction it sits in, which is\n // already being timed — so warning per depth would report one slow block as\n // several warnings and point at the innermost frame rather than the one\n // holding the connection.\n const stopWatching = watchForSlowTransaction(\n options?.slowTransactionThreshold,\n );\n\n // Built out here rather than inside the `begin` callback, because the drain\n // below needs it *after* that callback's scope is gone, and reading it off\n // the store then is exactly what is no longer possible.\n const deferred: AfterCommitCallback[] = [];\n const dependencies: Promise<unknown>[] = [];\n\n // The `try` covers a *synchronous* throw from `begin` — a closed pool, say.\n // `.finally` alone would not: it is only attached once `begin` has returned a\n // promise, so a synchronous throw escapes with the timer still armed and\n // eventually warns about a transaction that never opened. `unref` keeps that\n // from holding the process, which makes it cosmetic rather than a leak, but\n // it is the same gap the `.finally` below exists to close.\n try {\n // Spread rather than replace: a `Model.transaction` inside a\n // `Model.asSystem` must not silently re-enable policies for its subtree.\n //\n // The connection goes into the scope alongside the handle, and the pair is\n // what makes the check at the top of this function possible: \"which\n // connection is this transaction on\" has to be answerable from the store,\n // because the handle itself does not say. It is also what makes an\n // unqualified query inside `DB.connection(\"analytics\").transaction(...)`\n // *join* the transaction instead of being refused by it — the query\n // inherits the name rather than defaulting to the hot path.\n return (\n pool\n .begin((tx) =>\n ormContext.run(\n {\n ...current,\n tx,\n depth: 0,\n connection: name === DEFAULT_CONNECTION ? undefined : name,\n // A fresh list per outermost transaction, assigned rather than\n // left to the spread. `current` holds no list today — the drain\n // clears the field before it runs anything — and assigning is\n // what keeps that from being load-bearing: a transaction opened\n // by a callback during a drain gets its own list either way,\n // rather than appending to the array it is being run out of.\n afterCommit: deferred,\n commitDependsOn: dependencies,\n },\n async () => {\n const result = await fn(tx);\n await awaitDependencies(dependencies, 0);\n return result;\n },\n ),\n )\n // `finally` and not a `then`/`catch` pair: the connection is released on\n // rollback exactly as it is on commit, so a throwing callback must clear\n // the timer too or every failed transaction leaves a warning armed\n // against a connection that has already gone back to the pool.\n //\n // Before the drain, so that a slow after-commit listener is not\n // reported as a transaction that will not settle. The connection went\n // back to the pool at the commit; the warning is about holding it.\n .finally(stopWatching)\n // Only here, on the fulfilled path. A rejected `begin` rolled back, so\n // the scope and everything queued on it are discarded unread — which is\n // the whole of what `afterCommit` promises.\n .then(async (result) => {\n await drainAfterCommit(deferred, current);\n return result;\n }) as Promise<T>\n );\n } catch (error) {\n stopWatching();\n throw error;\n }\n}\n"
|
|
6
6
|
],
|
|
7
7
|
"mappings": "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",
|
|
8
|
-
"debugId": "
|
|
8
|
+
"debugId": "55FE6868B1E8699064756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{gc as o}from"./chunk-0kpvbnz0.js";import{hc as i}from"./chunk-9hbwvcxn.js";import{vf as t}from"./chunk-1712rv0r.js";import{ng as r}from"./chunk-qjgfrsw7.js";class n extends i{register(){this.app.singleton(r,()=>new r(t(o(),this.app.config.get("translation",{}))))}}
|
|
3
|
+
export{n as Wa};
|
|
4
|
+
|
|
5
|
+
//# debugId=32B2E4D1B7C6734464756E2164756E21
|
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
"import { ServiceProvider } from \"../support/ServiceProvider\";\nimport { withDefaults } from \"../support/withDefaults\";\nimport { Translator } from \"./Translator\";\nimport { translationConfigDefaults, type TranslationConfig } from \"./config\";\n\nexport class TranslationServiceProvider extends ServiceProvider {\n register() {\n this.app.singleton(\n Translator,\n () =>\n new Translator(\n withDefaults(\n translationConfigDefaults(),\n this.app.config.get<TranslationConfig>(\"translation\", {}),\n ),\n ),\n );\n }\n}\n"
|
|
6
6
|
],
|
|
7
7
|
"mappings": ";sLAKO,AAAM,IAAmC,LAAgB,CAC9D,QAAQ,EAAG,CACT,KAAK,IAAI,UACP,EACA,IACE,IAAI,EACF,EACE,EAA0B,EAC1B,KAAK,IAAI,OAAO,IAAuB,cAAe,CAAC,CAAC,CAC1D,CACF,CACJ,EAEJ",
|
|
8
|
-
"debugId": "
|
|
8
|
+
"debugId": "32B2E4D1B7C6734464756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{L as B,N as p,T as _}from"./chunk-
|
|
2
|
+
import{L as B,N as p,T as _}from"./chunk-sqvw7rv2.js";import{U as v}from"./chunk-zjg31baf.js";import{V as D}from"./chunk-tnt0ybj9.js";import"./chunk-6h5gvcs8.js";import"./chunk-m2bmwre5.js";import"./chunk-dmdmt3gw.js";import{$ as d}from"./chunk-3hfwbp2m.js";import"./chunk-zzh52t0y.js";import{Gg as n}from"./chunk-xzp2ptj5.js";var x=n(D(),1),E=n(B(),1),i=n(d(),1);import M from"fs/promises";var C=n(d(),1);var A="169.254.170.2",O="169.254.170.23",b="[fd00:ec2::23]",S=(e,a)=>{if(e.protocol==="https:")return;if(e.hostname===A||e.hostname===O||e.hostname===b)return;if(e.hostname.includes("[")){if(e.hostname==="[::1]"||e.hostname==="[0000:0000:0000:0000:0000:0000:0000:0001]")return}else{if(e.hostname==="localhost")return;let c=e.hostname.split("."),r=(t)=>{let s=parseInt(t,10);return 0<=s&&s<=255};if(c[0]==="127"&&r(c[1])&&r(c[2])&&r(c[3])&&c.length===4)return}throw new C.CredentialsProviderError(`URL not accepted. It must either be HTTPS or match one of the following:
|
|
3
3
|
- loopback CIDR 127.0.0.0/8 or [::1/128]
|
|
4
4
|
- ECS container host 169.254.170.2
|
|
5
5
|
- EKS container host 169.254.170.23 or [fd00:ec2::23]`,{logger:a})};var o=n(d(),1),g=n(v(),1),w=n(_(),1),I=n(p(),1);function P(e){return new g.HttpRequest({protocol:e.protocol,hostname:e.hostname,port:Number(e.port),path:e.pathname,query:Array.from(e.searchParams.entries()).reduce((a,[c,r])=>{return a[c]=r,a},{}),fragment:e.hash})}async function K(e,a){let r=await I.sdkStreamMixin(e.body).transformToString();if(e.statusCode===200){let t=JSON.parse(r);if(typeof t.AccessKeyId!=="string"||typeof t.SecretAccessKey!=="string"||typeof t.Token!=="string"||typeof t.Expiration!=="string")throw new o.CredentialsProviderError("HTTP credential provider response not of the required format, an object matching: { AccessKeyId: string, SecretAccessKey: string, Token: string, Expiration: string(rfc3339) }",{logger:a});return{accessKeyId:t.AccessKeyId,secretAccessKey:t.SecretAccessKey,sessionToken:t.Token,expiration:w.parseRfc3339DateTime(t.Expiration)}}if(e.statusCode>=400&&e.statusCode<500){let t={};try{t=JSON.parse(r)}catch(s){}throw Object.assign(new o.CredentialsProviderError(`Server responded with status: ${e.statusCode}`,{logger:a}),{Code:t.Code,Message:t.Message})}throw new o.CredentialsProviderError(`Server responded with status: ${e.statusCode}`,{logger:a})}var T=(e,a,c)=>{return async()=>{for(let r=0;r<a;++r)try{return await e()}catch(t){await new Promise((s)=>setTimeout(s,c))}return await e()}};var N="AWS_CONTAINER_CREDENTIALS_RELATIVE_URI",j="http://169.254.170.2",H="AWS_CONTAINER_CREDENTIALS_FULL_URI",J="AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE",$="AWS_CONTAINER_AUTHORIZATION_TOKEN",G=(e={})=>{e.logger?.debug("@aws-sdk/credential-provider-http - fromHttp");let a,c=e.awsContainerCredentialsRelativeUri??process.env[N],r=e.awsContainerCredentialsFullUri??process.env[H],t=e.awsContainerAuthorizationToken??process.env[$],s=e.awsContainerAuthorizationTokenFile??process.env[J],m=e.logger?.constructor?.name==="NoOpLogger"||!e.logger?.warn?console.warn:e.logger.warn.bind(e.logger);if(c&&r)m("@aws-sdk/credential-provider-http: you have set both awsContainerCredentialsRelativeUri and awsContainerCredentialsFullUri."),m("awsContainerCredentialsFullUri will take precedence.");if(t&&s)m("@aws-sdk/credential-provider-http: you have set both awsContainerAuthorizationToken and awsContainerAuthorizationTokenFile."),m("awsContainerAuthorizationToken will take precedence.");if(r)a=r;else if(c)a=`${j}${c}`;else throw new i.CredentialsProviderError(`No HTTP credential provider host provided.
|
|
6
6
|
Set AWS_CONTAINER_CREDENTIALS_FULL_URI or AWS_CONTAINER_CREDENTIALS_RELATIVE_URI.`,{logger:e.logger});let y=new URL(a);S(y,e.logger);let R=E.NodeHttpHandler.create({requestTimeout:e.timeout??1000,connectionTimeout:e.timeout??1000});return T(async()=>{let f=P(y);if(t)f.headers.Authorization=t;else if(s)f.headers.Authorization=(await M.readFile(s)).toString();try{let h=await R.handle(f);return K(h.response).then((u)=>x.setCredentialFeature(u,"CREDENTIALS_HTTP","z"))}catch(h){throw new i.CredentialsProviderError(String(h),{logger:e.logger})}},e.maxRetries??3,e.timeout??1000)};export{G as fromHttp};
|
|
7
7
|
|
|
8
|
-
//# debugId=
|
|
8
|
+
//# debugId=FAD10B589FF1A6C264756E2164756E21
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
"export const retryWrapper = (toRetry, maxRetries, delayMs) => {\n return async () => {\n for (let i = 0; i < maxRetries; ++i) {\n try {\n return await toRetry();\n }\n catch (e) {\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n }\n }\n return await toRetry();\n };\n};\n"
|
|
9
9
|
],
|
|
10
10
|
"mappings": "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",
|
|
11
|
-
"debugId": "
|
|
11
|
+
"debugId": "FAD10B589FF1A6C264756E2164756E21",
|
|
12
12
|
"names": []
|
|
13
13
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{
|
|
2
|
+
import{Hg as h}from"./chunk-xzp2ptj5.js";var A=h((f)=>{f.HttpAuthLocation=void 0;(function(t){t.HEADER="header",t.QUERY="query"})(f.HttpAuthLocation||(f.HttpAuthLocation={}));f.HttpApiKeyAuthLocation=void 0;(function(t){t.HEADER="header",t.QUERY="query"})(f.HttpApiKeyAuthLocation||(f.HttpApiKeyAuthLocation={}));f.EndpointURLScheme=void 0;(function(t){t.HTTP="http",t.HTTPS="https"})(f.EndpointURLScheme||(f.EndpointURLScheme={}));f.AlgorithmId=void 0;(function(t){t.MD5="md5",t.CRC32="crc32",t.CRC32C="crc32c",t.SHA1="sha1",t.SHA256="sha256"})(f.AlgorithmId||(f.AlgorithmId={}));var x=(t)=>{let o=[];if(t.sha256!==void 0)o.push({algorithmId:()=>f.AlgorithmId.SHA256,checksumConstructor:()=>t.sha256});if(t.md5!=null)o.push({algorithmId:()=>f.AlgorithmId.MD5,checksumConstructor:()=>t.md5});return{addChecksumAlgorithm(s){o.push(s)},checksumAlgorithms(){return o}}},d=(t)=>{let o={};return t.checksumAlgorithms().forEach((s)=>{o[s.algorithmId()]=s.checksumConstructor()}),o},a=(t)=>{return x(t)},E=(t)=>{return d(t)};f.FieldPosition=void 0;(function(t){t[t.HEADER=0]="HEADER",t[t.TRAILER=1]="TRAILER"})(f.FieldPosition||(f.FieldPosition={}));var m="__smithy_context";f.IniSectionType=void 0;(function(t){t.PROFILE="profile",t.SSO_SESSION="sso-session",t.SERVICES="services"})(f.IniSectionType||(f.IniSectionType={}));f.RequestHandlerProtocol=void 0;(function(t){t.HTTP_0_9="http/0.9",t.HTTP_1_0="http/1.0",t.TDS_8_0="tds/8.0"})(f.RequestHandlerProtocol||(f.RequestHandlerProtocol={}));f.SMITHY_CONTEXT_KEY=m;f.getDefaultClientConfiguration=a;f.resolveDefaultRuntimeConfig=E});
|
|
3
3
|
export{A as _};
|
|
4
4
|
|
|
5
|
-
//# debugId=
|
|
5
|
+
//# debugId=D98CC478C66B587464756E2164756E21
|
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
"'use strict';\n\nexports.HttpAuthLocation = void 0;\n(function (HttpAuthLocation) {\n HttpAuthLocation[\"HEADER\"] = \"header\";\n HttpAuthLocation[\"QUERY\"] = \"query\";\n})(exports.HttpAuthLocation || (exports.HttpAuthLocation = {}));\n\nexports.HttpApiKeyAuthLocation = void 0;\n(function (HttpApiKeyAuthLocation) {\n HttpApiKeyAuthLocation[\"HEADER\"] = \"header\";\n HttpApiKeyAuthLocation[\"QUERY\"] = \"query\";\n})(exports.HttpApiKeyAuthLocation || (exports.HttpApiKeyAuthLocation = {}));\n\nexports.EndpointURLScheme = void 0;\n(function (EndpointURLScheme) {\n EndpointURLScheme[\"HTTP\"] = \"http\";\n EndpointURLScheme[\"HTTPS\"] = \"https\";\n})(exports.EndpointURLScheme || (exports.EndpointURLScheme = {}));\n\nexports.AlgorithmId = void 0;\n(function (AlgorithmId) {\n AlgorithmId[\"MD5\"] = \"md5\";\n AlgorithmId[\"CRC32\"] = \"crc32\";\n AlgorithmId[\"CRC32C\"] = \"crc32c\";\n AlgorithmId[\"SHA1\"] = \"sha1\";\n AlgorithmId[\"SHA256\"] = \"sha256\";\n})(exports.AlgorithmId || (exports.AlgorithmId = {}));\nconst getChecksumConfiguration = (runtimeConfig) => {\n const checksumAlgorithms = [];\n if (runtimeConfig.sha256 !== undefined) {\n checksumAlgorithms.push({\n algorithmId: () => exports.AlgorithmId.SHA256,\n checksumConstructor: () => runtimeConfig.sha256,\n });\n }\n if (runtimeConfig.md5 != undefined) {\n checksumAlgorithms.push({\n algorithmId: () => exports.AlgorithmId.MD5,\n checksumConstructor: () => runtimeConfig.md5,\n });\n }\n return {\n addChecksumAlgorithm(algo) {\n checksumAlgorithms.push(algo);\n },\n checksumAlgorithms() {\n return checksumAlgorithms;\n },\n };\n};\nconst resolveChecksumRuntimeConfig = (clientConfig) => {\n const runtimeConfig = {};\n clientConfig.checksumAlgorithms().forEach((checksumAlgorithm) => {\n runtimeConfig[checksumAlgorithm.algorithmId()] = checksumAlgorithm.checksumConstructor();\n });\n return runtimeConfig;\n};\n\nconst getDefaultClientConfiguration = (runtimeConfig) => {\n return getChecksumConfiguration(runtimeConfig);\n};\nconst resolveDefaultRuntimeConfig = (config) => {\n return resolveChecksumRuntimeConfig(config);\n};\n\nexports.FieldPosition = void 0;\n(function (FieldPosition) {\n FieldPosition[FieldPosition[\"HEADER\"] = 0] = \"HEADER\";\n FieldPosition[FieldPosition[\"TRAILER\"] = 1] = \"TRAILER\";\n})(exports.FieldPosition || (exports.FieldPosition = {}));\n\nconst SMITHY_CONTEXT_KEY = \"__smithy_context\";\n\nexports.IniSectionType = void 0;\n(function (IniSectionType) {\n IniSectionType[\"PROFILE\"] = \"profile\";\n IniSectionType[\"SSO_SESSION\"] = \"sso-session\";\n IniSectionType[\"SERVICES\"] = \"services\";\n})(exports.IniSectionType || (exports.IniSectionType = {}));\n\nexports.RequestHandlerProtocol = void 0;\n(function (RequestHandlerProtocol) {\n RequestHandlerProtocol[\"HTTP_0_9\"] = \"http/0.9\";\n RequestHandlerProtocol[\"HTTP_1_0\"] = \"http/1.0\";\n RequestHandlerProtocol[\"TDS_8_0\"] = \"tds/8.0\";\n})(exports.RequestHandlerProtocol || (exports.RequestHandlerProtocol = {}));\n\nexports.SMITHY_CONTEXT_KEY = SMITHY_CONTEXT_KEY;\nexports.getDefaultClientConfiguration = getDefaultClientConfiguration;\nexports.resolveDefaultRuntimeConfig = resolveDefaultRuntimeConfig;\n"
|
|
6
6
|
],
|
|
7
7
|
"mappings": ";uDAEQ,mBAAwB,QAC/B,QAAS,CAAC,EAAkB,CACzB,EAAiB,OAAY,SAC7B,EAAiB,MAAW,UACrB,qBAA6B,mBAAmB,CAAC,EAAE,EAEtD,yBAA8B,QACrC,QAAS,CAAC,EAAwB,CAC/B,EAAuB,OAAY,SACnC,EAAuB,MAAW,UAC3B,2BAAmC,yBAAyB,CAAC,EAAE,EAElE,oBAAyB,QAChC,QAAS,CAAC,EAAmB,CAC1B,EAAkB,KAAU,OAC5B,EAAkB,MAAW,UACtB,sBAA8B,oBAAoB,CAAC,EAAE,EAExD,cAAmB,QAC1B,QAAS,CAAC,EAAa,CACpB,EAAY,IAAS,MACrB,EAAY,MAAW,QACvB,EAAY,OAAY,SACxB,EAAY,KAAU,OACtB,EAAY,OAAY,WACjB,gBAAwB,cAAc,CAAC,EAAE,EACpD,IAAM,EAA2B,CAAC,IAAkB,CAChD,IAAM,EAAqB,CAAC,EAC5B,GAAI,EAAc,SAAW,OACzB,EAAmB,KAAK,CACpB,YAAa,IAAc,cAAY,OACvC,oBAAqB,IAAM,EAAc,MAC7C,CAAC,EAEL,GAAI,EAAc,KAAO,KACrB,EAAmB,KAAK,CACpB,YAAa,IAAc,cAAY,IACvC,oBAAqB,IAAM,EAAc,GAC7C,CAAC,EAEL,MAAO,CACH,oBAAoB,CAAC,EAAM,CACvB,EAAmB,KAAK,CAAI,GAEhC,kBAAkB,EAAG,CACjB,OAAO,EAEf,GAEE,EAA+B,CAAC,IAAiB,CACnD,IAAM,EAAgB,CAAC,EAIvB,OAHA,EAAa,mBAAmB,EAAE,QAAQ,CAAC,IAAsB,CAC7D,EAAc,EAAkB,YAAY,GAAK,EAAkB,oBAAoB,EAC1F,EACM,GAGL,EAAgC,CAAC,IAAkB,CACrD,OAAO,EAAyB,CAAa,GAE3C,EAA8B,CAAC,IAAW,CAC5C,OAAO,EAA6B,CAAM,GAGtC,gBAAqB,QAC5B,QAAS,CAAC,EAAe,CACtB,EAAc,EAAc,OAAY,GAAK,SAC7C,EAAc,EAAc,QAAa,GAAK,YACvC,kBAA0B,gBAAgB,CAAC,EAAE,EAExD,IAAM,EAAqB,mBAEnB,iBAAsB,QAC7B,QAAS,CAAC,EAAgB,CACvB,EAAe,QAAa,UAC5B,EAAe,YAAiB,cAChC,EAAe,SAAc,aACtB,mBAA2B,iBAAiB,CAAC,EAAE,EAElD,yBAA8B,QACrC,QAAS,CAAC,EAAwB,CAC/B,EAAuB,SAAc,WACrC,EAAuB,SAAc,WACrC,EAAuB,QAAa,YAC7B,2BAAmC,yBAAyB,CAAC,EAAE,EAElE,qBAAqB,EACrB,gCAAgC,EAChC,8BAA8B",
|
|
8
|
-
"debugId": "
|
|
8
|
+
"debugId": "D98CC478C66B587464756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|