gemi 0.111.0 → 0.112.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/Agent.d.ts +54 -1
- package/dist/ai/Agent.d.ts.map +1 -1
- package/dist/ai/AgentController.d.ts +41 -0
- package/dist/ai/AgentController.d.ts.map +1 -1
- package/dist/ai/client/index.d.ts +1 -1
- package/dist/ai/client/index.d.ts.map +1 -1
- package/dist/ai/contextCompaction.d.ts +247 -0
- package/dist/ai/contextCompaction.d.ts.map +1 -0
- package/dist/ai/contextWindow.d.ts +18 -0
- package/dist/ai/contextWindow.d.ts.map +1 -1
- package/dist/ai/index.d.ts +5 -1
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai/index.js +93 -14
- package/dist/ai/index.js.map +16 -10
- package/dist/ai/jobs/AgentJob.d.ts +259 -0
- package/dist/ai/jobs/AgentJob.d.ts.map +1 -0
- package/dist/ai/jobs/AgentJobDeadlineSweep.d.ts +18 -0
- package/dist/ai/jobs/AgentJobDeadlineSweep.d.ts.map +1 -0
- package/dist/ai/jobs/AgentJobStore.d.ts +141 -0
- package/dist/ai/jobs/AgentJobStore.d.ts.map +1 -0
- package/dist/ai/jobs/DatabaseAgentJobStore.d.ts +82 -0
- package/dist/ai/jobs/DatabaseAgentJobStore.d.ts.map +1 -0
- package/dist/ai/jobs/contextBlock.d.ts +31 -0
- package/dist/ai/jobs/contextBlock.d.ts.map +1 -0
- package/dist/ai/jobs/index.d.ts +6 -0
- package/dist/ai/jobs/index.d.ts.map +1 -0
- package/dist/ai/jobs/overlay.d.ts +49 -0
- package/dist/ai/jobs/overlay.d.ts.map +1 -0
- package/dist/ai/providers/request.d.ts.map +1 -1
- package/dist/ai/store/Attachments.d.ts +6 -0
- package/dist/ai/store/Attachments.d.ts.map +1 -1
- package/dist/ai/store/MemoryAgentStore.d.ts +3 -0
- package/dist/ai/store/MemoryAgentStore.d.ts.map +1 -1
- package/dist/ai/types.d.ts +28 -0
- package/dist/ai/types.d.ts.map +1 -1
- package/dist/app/index.js +1 -1
- package/dist/bin/gemi.js +13 -2
- package/dist/bin/gemi.js.map +4 -4
- package/dist/bun/plugin.js +1 -1
- package/dist/bun/preload.js +1 -1
- package/dist/{chunk-30xjcyzh.js → chunk-0z2h35d7.js} +2 -2
- package/dist/{chunk-30xjcyzh.js.map → chunk-0z2h35d7.js.map} +1 -1
- package/dist/{chunk-358sdcx9.js → chunk-15a1mrdv.js} +2 -2
- package/dist/{chunk-358sdcx9.js.map → chunk-15a1mrdv.js.map} +1 -1
- package/dist/{chunk-hf7a51s3.js → chunk-18c3y8m3.js} +2 -2
- package/dist/{chunk-hf7a51s3.js.map → chunk-18c3y8m3.js.map} +1 -1
- package/dist/{chunk-tq85n8zh.js → chunk-1pvkw2pq.js} +2 -2
- package/dist/{chunk-tq85n8zh.js.map → chunk-1pvkw2pq.js.map} +1 -1
- package/dist/{chunk-8afxrmdc.js → chunk-2taa0brj.js} +2 -2
- package/dist/{chunk-8afxrmdc.js.map → chunk-2taa0brj.js.map} +1 -1
- package/dist/{chunk-5cygrbxv.js → chunk-30yg31sz.js} +2 -2
- package/dist/{chunk-5cygrbxv.js.map → chunk-30yg31sz.js.map} +1 -1
- package/dist/{chunk-3y8q8x11.js → chunk-3z9vhjd7.js} +2 -2
- package/dist/{chunk-3y8q8x11.js.map → chunk-3z9vhjd7.js.map} +1 -1
- package/dist/chunk-4sb1484x.js +46 -0
- package/dist/{chunk-h3864a8z.js.map → chunk-4sb1484x.js.map} +13 -4
- package/dist/{chunk-0sjh5v1n.js → chunk-53bek7sp.js} +1 -1
- package/dist/{chunk-xkc7ftnq.js → chunk-57edag57.js} +3 -3
- package/dist/{chunk-xkc7ftnq.js.map → chunk-57edag57.js.map} +1 -1
- package/dist/{chunk-ddr4qvvt.js → chunk-5az0mq4p.js} +3 -3
- package/dist/{chunk-ddr4qvvt.js.map → chunk-5az0mq4p.js.map} +1 -1
- package/dist/{chunk-nsvktvp4.js → chunk-5waxjsvv.js} +2 -2
- package/dist/{chunk-nsvktvp4.js.map → chunk-5waxjsvv.js.map} +1 -1
- package/dist/{chunk-rfcygmf5.js → chunk-6w79zajj.js} +3 -3
- package/dist/{chunk-rfcygmf5.js.map → chunk-6w79zajj.js.map} +1 -1
- package/dist/{chunk-gb37722w.js → chunk-75ptmewf.js} +3 -3
- package/dist/{chunk-gb37722w.js.map → chunk-75ptmewf.js.map} +1 -1
- package/dist/{chunk-pyj7cd9t.js → chunk-851k0b26.js} +2 -2
- package/dist/{chunk-pyj7cd9t.js.map → chunk-851k0b26.js.map} +1 -1
- package/dist/{chunk-m6ystvtk.js → chunk-8reepafh.js} +2 -2
- package/dist/{chunk-m6ystvtk.js.map → chunk-8reepafh.js.map} +1 -1
- package/dist/{chunk-nvv9wej6.js → chunk-9hr0bcfy.js} +3 -3
- package/dist/{chunk-nvv9wej6.js.map → chunk-9hr0bcfy.js.map} +1 -1
- package/dist/{chunk-yqavgpwd.js → chunk-a9q2cr4w.js} +2 -2
- package/dist/{chunk-yqavgpwd.js.map → chunk-a9q2cr4w.js.map} +1 -1
- package/dist/{chunk-cy5qp6mz.js → chunk-amvy4r59.js} +2 -2
- package/dist/{chunk-cy5qp6mz.js.map → chunk-amvy4r59.js.map} +1 -1
- package/dist/{chunk-twnn7r66.js → chunk-b1mk12xg.js} +2 -2
- package/dist/{chunk-twnn7r66.js.map → chunk-b1mk12xg.js.map} +1 -1
- package/dist/{chunk-a6453d9s.js → chunk-b7cqwcbm.js} +2 -2
- package/dist/{chunk-a6453d9s.js.map → chunk-b7cqwcbm.js.map} +1 -1
- package/dist/{chunk-nncz308h.js → chunk-b7qjp7r1.js} +2 -2
- package/dist/{chunk-nncz308h.js.map → chunk-b7qjp7r1.js.map} +1 -1
- package/dist/chunk-beks65me.js +19 -0
- package/dist/{chunk-mb7q7s0a.js.map → chunk-beks65me.js.map} +3 -4
- package/dist/chunk-bf1ets45.js +5 -0
- package/dist/{chunk-11fsn5mr.js.map → chunk-bf1ets45.js.map} +4 -3
- package/dist/{chunk-9rchedr4.js → chunk-bhw7k8vp.js} +2 -2
- package/dist/{chunk-9rchedr4.js.map → chunk-bhw7k8vp.js.map} +1 -1
- package/dist/{chunk-k1vw22t2.js → chunk-ck0v2kzw.js} +2 -2
- package/dist/{chunk-k1vw22t2.js.map → chunk-ck0v2kzw.js.map} +1 -1
- package/dist/{chunk-1bkr5wnt.js → chunk-cptzbdn6.js} +2 -2
- package/dist/{chunk-1bkr5wnt.js.map → chunk-cptzbdn6.js.map} +1 -1
- package/dist/{chunk-6vr7f5fq.js → chunk-csbc1fxa.js} +3 -3
- package/dist/{chunk-6vr7f5fq.js.map → chunk-csbc1fxa.js.map} +1 -1
- package/dist/{chunk-kf71mjxa.js → chunk-d0trs736.js} +3 -3
- package/dist/{chunk-kf71mjxa.js.map → chunk-d0trs736.js.map} +1 -1
- package/dist/{chunk-zhs1r1z0.js → chunk-d8ekyng4.js} +3 -3
- package/dist/{chunk-zhs1r1z0.js.map → chunk-d8ekyng4.js.map} +1 -1
- package/dist/chunk-d9an2nra.js +5 -0
- package/dist/{chunk-hn67spz6.js.map → chunk-d9an2nra.js.map} +1 -1
- package/dist/{chunk-0d9qsmx2.js → chunk-dan79jab.js} +2 -2
- package/dist/{chunk-0d9qsmx2.js.map → chunk-dan79jab.js.map} +1 -1
- package/dist/{chunk-1yx2e0bh.js → chunk-ddad3902.js} +2 -2
- package/dist/{chunk-1yx2e0bh.js.map → chunk-ddad3902.js.map} +1 -1
- package/dist/{chunk-3cg7y62b.js → chunk-dm59ze55.js} +2 -2
- package/dist/{chunk-3cg7y62b.js.map → chunk-dm59ze55.js.map} +1 -1
- package/dist/{chunk-wvsv1jgg.js → chunk-dx7wpz5b.js} +2 -2
- package/dist/{chunk-wvsv1jgg.js.map → chunk-dx7wpz5b.js.map} +1 -1
- package/dist/{chunk-q9s7kwgt.js → chunk-eazqda9z.js} +3 -3
- package/dist/{chunk-q9s7kwgt.js.map → chunk-eazqda9z.js.map} +1 -1
- package/dist/{chunk-td8tha3j.js → chunk-es487acc.js} +1 -1
- package/dist/{chunk-9s851qz8.js → chunk-et3vhkbf.js} +2 -2
- package/dist/{chunk-9s851qz8.js.map → chunk-et3vhkbf.js.map} +1 -1
- package/dist/{chunk-qd2v93y4.js → chunk-f1y8eggf.js} +2 -2
- package/dist/{chunk-qd2v93y4.js.map → chunk-f1y8eggf.js.map} +1 -1
- package/dist/{chunk-1pem26zc.js → chunk-ga7c7tq0.js} +3 -3
- package/dist/{chunk-1pem26zc.js.map → chunk-ga7c7tq0.js.map} +1 -1
- package/dist/{chunk-c098fzvk.js → chunk-gcs9ydjv.js} +2 -2
- package/dist/{chunk-c098fzvk.js.map → chunk-gcs9ydjv.js.map} +1 -1
- package/dist/{chunk-bn5yn731.js → chunk-hfyxvdjj.js} +2 -2
- package/dist/{chunk-bn5yn731.js.map → chunk-hfyxvdjj.js.map} +1 -1
- package/dist/chunk-j0sncgxc.js +14 -0
- package/dist/{chunk-mn5xw79b.js.map → chunk-j0sncgxc.js.map} +6 -5
- package/dist/{chunk-p7r2w4qw.js → chunk-j9jqxv1d.js} +3 -3
- package/dist/{chunk-p7r2w4qw.js.map → chunk-j9jqxv1d.js.map} +1 -1
- package/dist/{chunk-jbrwt3cs.js → chunk-jdtn0zk2.js} +4 -4
- package/dist/{chunk-jbrwt3cs.js.map → chunk-jdtn0zk2.js.map} +1 -1
- package/dist/{chunk-s6jjy4y9.js → chunk-jn59bsww.js} +2 -2
- package/dist/{chunk-s6jjy4y9.js.map → chunk-jn59bsww.js.map} +1 -1
- package/dist/{chunk-he7z7y9j.js → chunk-kk502yk2.js} +2 -2
- package/dist/{chunk-he7z7y9j.js.map → chunk-kk502yk2.js.map} +1 -1
- package/dist/{chunk-wsttx9rg.js → chunk-mrdarm2q.js} +2 -2
- package/dist/{chunk-wsttx9rg.js.map → chunk-mrdarm2q.js.map} +1 -1
- package/dist/chunk-mvtgx1mr.js +4 -0
- package/dist/{chunk-x78jrs4y.js.map → chunk-mvtgx1mr.js.map} +2 -2
- package/dist/{chunk-et4pv1b1.js → chunk-mx9y9mtx.js} +1 -1
- package/dist/chunk-nv7mv51r.js +6 -0
- package/dist/{chunk-tdsfke9q.js.map → chunk-nv7mv51r.js.map} +2 -2
- package/dist/{chunk-cttmy1jp.js → chunk-p2fhtf5r.js} +3 -3
- package/dist/{chunk-cttmy1jp.js.map → chunk-p2fhtf5r.js.map} +1 -1
- package/dist/{chunk-w7jsh3xd.js → chunk-qtbf3w6w.js} +2 -2
- package/dist/{chunk-w7jsh3xd.js.map → chunk-qtbf3w6w.js.map} +1 -1
- package/dist/chunk-qx87cdfg.js +5 -0
- package/dist/chunk-qx87cdfg.js.map +10 -0
- package/dist/{chunk-1kr25rcg.js → chunk-qyp652jz.js} +3 -3
- package/dist/{chunk-1kr25rcg.js.map → chunk-qyp652jz.js.map} +1 -1
- package/dist/{chunk-8b2tw2yp.js → chunk-rend2sbk.js} +1 -1
- package/dist/chunk-rx2shb3t.js +25 -0
- package/dist/chunk-rx2shb3t.js.map +19 -0
- package/dist/{chunk-mwqg079k.js → chunk-ryzcfxq7.js} +2 -2
- package/dist/{chunk-mwqg079k.js.map → chunk-ryzcfxq7.js.map} +1 -1
- package/dist/{chunk-7tx5y712.js → chunk-s9rfgxj5.js} +2 -2
- package/dist/{chunk-7tx5y712.js.map → chunk-s9rfgxj5.js.map} +1 -1
- package/dist/{chunk-1c1d0e60.js → chunk-scbvv2e4.js} +3 -3
- package/dist/{chunk-1c1d0e60.js.map → chunk-scbvv2e4.js.map} +1 -1
- package/dist/chunk-sj8jp4p9.js +24 -0
- package/dist/{chunk-wqvvjd5r.js.map → chunk-sj8jp4p9.js.map} +2 -2
- package/dist/{chunk-pwmgqq7z.js → chunk-teny6xkw.js} +2 -2
- package/dist/{chunk-pwmgqq7z.js.map → chunk-teny6xkw.js.map} +1 -1
- package/dist/{chunk-09ypnhjv.js → chunk-tmrmwthr.js} +2 -2
- package/dist/{chunk-09ypnhjv.js.map → chunk-tmrmwthr.js.map} +1 -1
- package/dist/{chunk-h4sw6nt3.js → chunk-vgdcz7tk.js} +3 -3
- package/dist/{chunk-h4sw6nt3.js.map → chunk-vgdcz7tk.js.map} +1 -1
- package/dist/{chunk-xk930yaw.js → chunk-wexs11yy.js} +2 -2
- package/dist/{chunk-xk930yaw.js.map → chunk-wexs11yy.js.map} +1 -1
- package/dist/{chunk-k1xt2tw7.js → chunk-xs4c2nvz.js} +2 -2
- package/dist/{chunk-k1xt2tw7.js.map → chunk-xs4c2nvz.js.map} +1 -1
- package/dist/{chunk-1va15sfq.js → chunk-xv74rhay.js} +2 -2
- package/dist/{chunk-1va15sfq.js.map → chunk-xv74rhay.js.map} +1 -1
- package/dist/{chunk-sq2wkynk.js → chunk-yv1psptb.js} +2 -2
- package/dist/{chunk-sq2wkynk.js.map → chunk-yv1psptb.js.map} +1 -1
- package/dist/{chunk-tk5q8hkv.js → chunk-zfter1x0.js} +4 -4
- package/dist/{chunk-tk5q8hkv.js.map → chunk-zfter1x0.js.map} +1 -1
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +214 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/useSubscription.d.ts +70 -0
- package/dist/client/useSubscription.d.ts.map +1 -0
- package/dist/config/index.js +1 -1
- package/dist/console/run.js +2 -2
- package/dist/console/run.js.map +2 -2
- 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/ChangeFeed.d.ts +42 -0
- package/dist/facades/ChangeFeed.d.ts.map +1 -0
- package/dist/facades/index.d.ts +1 -0
- package/dist/facades/index.d.ts.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/ResourcePolicy.d.ts +141 -0
- package/dist/http/ResourcePolicy.d.ts.map +1 -0
- package/dist/http/errors.d.ts +13 -0
- package/dist/http/errors.d.ts.map +1 -1
- package/dist/http/index.d.ts +2 -1
- 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 +2 -2
- package/dist/kernel/index.js +3 -3
- package/dist/kernel/index.js.map +3 -3
- package/dist/kernel/providers.d.ts.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/change-feed/ChangeFeedDriver.d.ts +94 -0
- package/dist/services/change-feed/ChangeFeedDriver.d.ts.map +1 -0
- package/dist/services/change-feed/ChangeFeedManager.d.ts +166 -0
- package/dist/services/change-feed/ChangeFeedManager.d.ts.map +1 -0
- package/dist/services/change-feed/ChangeFeedServiceProvider.d.ts +11 -0
- package/dist/services/change-feed/ChangeFeedServiceProvider.d.ts.map +1 -0
- package/dist/services/change-feed/DatabaseChangeFeedDriver.d.ts +84 -0
- package/dist/services/change-feed/DatabaseChangeFeedDriver.d.ts.map +1 -0
- package/dist/services/change-feed/MemoryChangeFeedDriver.d.ts +26 -0
- package/dist/services/change-feed/MemoryChangeFeedDriver.d.ts.map +1 -0
- package/dist/services/change-feed/changeFeedDriverContract.d.ts +8 -0
- package/dist/services/change-feed/changeFeedDriverContract.d.ts.map +1 -0
- package/dist/services/change-feed/config.d.ts +42 -0
- package/dist/services/change-feed/config.d.ts.map +1 -0
- package/dist/services/change-feed/cursor.d.ts +17 -0
- package/dist/services/change-feed/cursor.d.ts.map +1 -0
- package/dist/services/change-feed/postgresListen.d.ts +30 -0
- package/dist/services/change-feed/postgresListen.d.ts.map +1 -0
- package/dist/services/change-feed/stream.d.ts +16 -0
- package/dist/services/change-feed/stream.d.ts.map +1 -0
- package/dist/services/change-feed/wire.d.ts +20 -0
- package/dist/services/change-feed/wire.d.ts.map +1 -0
- package/dist/services/index.d.ts +9 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +3 -3
- package/dist/services/index.js.map +2 -2
- package/dist/services/queue/work.js +2 -2
- package/dist/services/queue/work.js.map +1 -1
- package/dist/services/router/ViewRouteDispatcher.d.ts +16 -0
- package/dist/services/router/ViewRouteDispatcher.d.ts.map +1 -1
- package/dist/stats/route-table.js +1 -1
- package/dist/support/index.js +2 -2
- package/dist/support/index.js.map +1 -1
- package/dist/vitest/index.js +1 -1
- package/package.json +6 -2
- package/skills/gemi-react-best-practices/rules/controller-authorize-every-tenant-read.md +16 -1
- package/dist/chunk-11fsn5mr.js +0 -5
- package/dist/chunk-h3864a8z.js +0 -17
- package/dist/chunk-hn67spz6.js +0 -5
- package/dist/chunk-mb7q7s0a.js +0 -19
- package/dist/chunk-mn5xw79b.js +0 -9
- package/dist/chunk-tdsfke9q.js +0 -6
- package/dist/chunk-wqvvjd5r.js +0 -24
- package/dist/chunk-x78jrs4y.js +0 -4
- /package/dist/{chunk-0sjh5v1n.js.map → chunk-53bek7sp.js.map} +0 -0
- /package/dist/{chunk-td8tha3j.js.map → chunk-es487acc.js.map} +0 -0
- /package/dist/{chunk-et4pv1b1.js.map → chunk-mx9y9mtx.js.map} +0 -0
- /package/dist/{chunk-8b2tw2yp.js.map → chunk-rend2sbk.js.map} +0 -0
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../console/Command.ts", "../console/config.ts", "../console/builder.ts", "../services/cron/
|
|
3
|
+
"sources": ["../console/Command.ts", "../console/config.ts", "../console/builder.ts", "../services/cron/config.ts", "../services/events/config.ts", "../services/events/Listener.ts", "../services/discovery.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"/**\n * A command an operator runs by hand: `gemi run backfill-avatars --dry-run`.\n *\n * This file is the *runtime* shape — the base class discovery walks the\n * prototype chain for, and the statics the runner reads. It is not the\n * authoring surface. Applications write commands with `defineCommand` in\n * `./builder`, which produces one of these; see that file for why the authoring\n * surface is a builder and not a class body.\n *\n * ### Why the schema is descriptors and not a signature string\n *\n * Laravel spells this `protected $signature = 'mail:send {user} {--queue}'`, and\n * the string is the whole schema. It reads beautifully and TypeScript cannot see\n * a word of it: a brace in the wrong place is a parse error the first time\n * somebody runs the command, months after it was written, and the parser is a\n * piece of framework surface with its own failure modes. The descriptors below\n * say the same thing in a shape the compiler checks — and, through the builder,\n * in a shape that types the handler's own body.\n *\n * ### Why the name is declared and never falls back to the class name\n *\n * `Job` keys off `static name`, which shadows `Function.name`. That is the right\n * trade there: a job's name *is* its class name, and the shadow is what makes it\n * a string literal that survives minification. Here it is the wrong one twice\n * over. A command's name is a CLI token (`backfill-avatars`, `db:seed`) with no\n * relationship to the class name, so an implicit fallback would only ever be\n * wrong; and shadowing `Function.name` costs the one error anybody will actually\n * hit — \"two commands claim `db:seed`\" — the ability to say *which two*.\n *\n * ### The minification hazard does not reach here\n *\n * `services/discovery.ts` warns about a job that never declared `static name`,\n * because two halves have to agree on one string across a bundler: the\n * dispatcher, minified into `dist/server/server.mjs`, and discovery, reading the\n * source. A command has one half — a human types the name and it is matched\n * against discovery-on-source. Nothing in the server bundle names a command, and\n * `gemi build` keeps `app/commands` out of the bundle anyway. So there is no\n * `warnIfNameWillNotSurviveTheBuild` equivalent below, and adding one by analogy\n * would be noise.\n *\n * If a `Command.call(\"db:seed\")` reachable from a controller is ever added, that\n * changes — and `commandName` being a declared string literal already covers it,\n * which is one more reason not to fall back to the class binding.\n */\n\n/** A positional argument. `variadic` collects the rest and must come last. */\nexport interface ArgSpec {\n description?: string;\n /**\n * Missing it is a usage error rather than an `undefined` in the handler.\n * Mutually exclusive with `default` — an argument that is supplied when absent\n * cannot also be absent, and declaring both is refused rather than silently\n * resolved in one direction.\n */\n required?: boolean;\n /** Collects every remaining positional. Only legal on the last argument. */\n variadic?: boolean;\n /** Supplied when the argument is absent, which makes it always present. */\n default?: string;\n}\n\n/**\n * A flag. The `type` decides both how the value is parsed and what the handler\n * sees, which is why it is required rather than defaulted to `string`: a\n * `--dry-run` declared without a type would arrive as the string `\"\"`, and every\n * `if (options.dryRun)` over it would be wrong in the one direction nobody\n * tests.\n */\nexport type OptionSpec =\n | {\n type: \"boolean\";\n description?: string;\n /** Single character, without the dash. */\n alias?: string;\n default?: boolean;\n }\n | {\n type: \"string\";\n description?: string;\n alias?: string;\n default?: string;\n required?: boolean;\n }\n | {\n type: \"number\";\n description?: string;\n alias?: string;\n default?: number;\n required?: boolean;\n };\n\n/** An argument as stored on the class: the spec, plus the name it was given. */\nexport type CommandArgument = ArgSpec & { name: string };\n\n/** An option as stored on the class: the spec, plus the name it was given. */\nexport type CommandOption = OptionSpec & { name: string };\n\n/** What a handler may return. `void` and `0` mean the same thing. */\nexport type CommandResult = Promise<number | void> | number | void;\n\n/**\n * A command that failed on purpose.\n *\n * The distinction `bin/check-models.ts` draws between `CheckModelsError` and\n * everything else, in the one place an application can reach it: this is a\n * sentence written for the terminal and is printed without a stack, while\n * anything else that escapes a handler is a bug and keeps its stack.\n */\nexport class CommandFailed extends Error {\n constructor(\n message: string,\n readonly code: number = 1,\n ) {\n super(message);\n this.name = \"CommandFailed\";\n }\n}\n\n/**\n * The base class. Discovery finds subclasses of this; the runner reads the\n * statics and calls `run`.\n *\n * Subclassing it by hand works and is what `defineCommand` does under the hood,\n * but it gives up the typing that is the whole point of the builder — `run`'s\n * parameter cannot be inferred from statics declared on the subclass, because\n * TypeScript does not contextually type an overriding method's parameters. Write\n * commands with `defineCommand`.\n */\nexport class Command {\n /**\n * The name typed on the command line. Mandatory: there is no sensible default\n * and the class name is not one.\n */\n static commandName = \"unset\";\n\n /** One line, printed by `gemi run` with no name and at the top of `--help`. */\n static description = \"\";\n\n /** Positional arguments, in order. A variadic one must be last. */\n static args: CommandArgument[] = [];\n\n /** Flags. Declared in camelCase; `dryRun` is spelled `--dry-run` on the CLI. */\n static options: CommandOption[] = [];\n\n /**\n * The body.\n *\n * Runs inside `kernel.run()`, so `app(MailManager)` and a model read resolve\n * exactly as they do in a request handler. Resolve services *here* rather than\n * at module load: a command file is imported by discovery before the container\n * has finished booting, and a service captured at the top of the file is\n * captured from a container that is not ready.\n */\n run(_ctx: any): CommandResult {}\n}\n\n/** The type of a class `defineCommand(...).handle(...)` produces. */\nexport type CommandClass = typeof Command;\n",
|
|
6
6
|
"import type { CommandClass } from \"./Command\";\n\n// Config key: `command`. Consumed by `gemi run`, and by nothing at boot.\n//\n// Named `command` rather than `console` on purpose. An app declares its slices\n// by importing them into `app/kernel/Kernel.ts`, and a file called\n// `app/config/console.ts` arrives there as `import console from\n// \"../config/console\"` — which shadows the global `console` for the rest of that\n// module. Every application would inherit that, silently. `command` also matches\n// the singular slice names already in use: `route`, `queue`, `schedule`, `log`.\nexport interface CommandConfig {\n /**\n * The `Command` classes this application can run, or nothing.\n *\n * ### The rule, exactly\n *\n * **Declared wins, and `[]` is declared.** A `commands` that is present is\n * used verbatim and no directory is read — the escape hatch for an app whose\n * commands live somewhere the walk cannot reach, and for a deploy that ships\n * no source. An empty array means an app with no commands and says so; it does\n * not mean \"find some\".\n *\n * **Absent or `undefined` discovers.** `undefined` counts as absent for the\n * same reason `withDefaults` treats it that way everywhere else: a key spread\n * in from an optional value is an omission, not an instruction.\n *\n * ### One way this differs from `queue` and `schedule`\n *\n * There, the cost of the list and the directory disagreeing is silence — a\n * dispatch dropped with a line on stderr nobody reads, a report that stops\n * arriving. Here it is a person typing `gemi run thing` and being told, in\n * those words, that there is no command called `thing`. The rule is kept for\n * consistency and for the source-less-deploy case, not because the stakes are\n * the same.\n */\n commands?: CommandClass[];\n\n /**\n * Where to look when `commands` was not declared. Relative to the project\n * root, or absolute.\n *\n * Every `.ts`/`.tsx` file underneath is imported when a command is run, so\n * this wants to be a directory of command declarations rather than a directory\n * that merely contains some.\n */\n commandsDir?: string;\n}\n\nexport function defineCommandConfig(config: CommandConfig): CommandConfig {\n return config;\n}\n\nexport function commandConfigDefaults(): Required<CommandConfig> {\n return {\n commands: [],\n commandsDir: \"app/commands\",\n };\n}\n",
|
|
7
7
|
"import {\n Command,\n type ArgSpec,\n type CommandArgument,\n type CommandClass,\n type CommandOption,\n type CommandResult,\n type OptionSpec,\n} from \"./Command\";\nimport type { CommandContext } from \"./context\";\n\n/**\n * Declaring a command, in the one shape that can type its own body.\n *\n * ### Why a builder and not a class\n *\n * The point of declaring arguments and options at all is that the handler then\n * knows what it has. A class body cannot deliver that, and the reason is a hard\n * TypeScript rule rather than a matter of arrangement: **TypeScript does not\n * contextually type a method's parameters from the base-class method it\n * overrides.** So both class-shaped designs fail in the same place:\n *\n * ```ts\n * class Backfill extends Command {\n * static args = [{ name: \"since\", required: true }];\n * async run(ctx) {} // implicit any\n * }\n *\n * class Backfill extends Command.define({ since: { required: true } }) {\n * async run(ctx) {} // still implicit any — the\n * } // configured base's signature does\n * // not flow into the override\n * ```\n *\n * The author's only way out is to restate the type by hand, which is the\n * ceremony the descriptors existed to remove — and which nothing enforces, so a\n * wrong restatement compiles.\n *\n * A **function expression passed as an argument is contextually typed**. That is\n * the entire mechanism here, and it is why this is a builder rather than sugar:\n * `.handle(fn)` carries the accumulated argument and option types into `fn`'s\n * parameter with nothing written by hand and nothing to keep in step.\n *\n * ### What it produces\n *\n * A `Command` subclass, so nothing downstream knows the difference: discovery\n * finds it by prototype chain, and the registry, parser and `--help` read the\n * same statics they would read off a hand-written class. The builder is an\n * authoring surface over one runtime shape, not a second mechanism.\n */\n\n/**\n * Marks a builder that was never finished. `discoverCommands` looks for this on\n * the exports it is about to discard — see the note on `handle` below.\n */\nexport const BUILDER_BRAND: unique symbol = Symbol.for(\"gemi.console.builder\");\n\n/** Flattens an accumulated intersection so editor hovers stay readable. */\ntype Simplify<T> = { [K in keyof T]: T[K] } & {};\n\n/**\n * What the handler sees for one argument.\n *\n * The order matters: `variadic` first because it wins over everything, then the\n * two ways an argument is guaranteed present. Anything else can be absent and\n * says so.\n */\nexport type ArgValue<S> = S extends { variadic: true }\n ? string[]\n : S extends { required: true }\n ? string\n : S extends { default: string }\n ? string\n : string | undefined;\n\n/** What the handler sees for one option. */\nexport type OptionValue<S> = S extends { type: \"boolean\" }\n ? boolean\n : S extends { type: \"number\" }\n ? S extends { default: number }\n ? number\n : S extends { required: true }\n ? number\n : number | undefined\n : S extends { default: string }\n ? string\n : S extends { required: true }\n ? string\n : string | undefined;\n\nexport interface CommandBuilder<\n A extends Record<string, unknown>,\n O extends Record<string, unknown>,\n> {\n /** One line, shown in the listing and at the top of `--help`. */\n describe(text: string): CommandBuilder<A, O>;\n\n /**\n * A positional argument. Declared in the order it is read from the command\n * line; a `variadic` one must be last.\n */\n arg<N extends string, const S extends ArgSpec = {}>(\n name: N,\n spec?: S,\n ): CommandBuilder<A & { [K in N]: ArgValue<S> }, O>;\n\n /**\n * A flag. Declared in camelCase and spelled in kebab-case on the command line,\n * so `.option(\"dryRun\", …)` is `--dry-run` for the operator and\n * `options.dryRun` in the handler — no string key nobody can autocomplete, and\n * no CLI flag with a capital letter in it.\n */\n option<N extends string, const S extends OptionSpec>(\n name: N,\n spec: S,\n ): CommandBuilder<A, O & { [K in N]: OptionValue<S> }>;\n\n /**\n * The body, and the terminal of the chain.\n *\n * A builder that never reaches here is not a class, so discovery would skip it\n * and the command would simply vanish from `gemi run` with nothing raised —\n * the silence #322 and #323 exist to remove, reintroduced by a new authoring\n * surface. `discoverCommands` therefore looks for `BUILDER_BRAND` among the\n * exports it discards and refuses the file by name.\n */\n handle(\n fn: (ctx: CommandContext<Simplify<A>, Simplify<O>>) => CommandResult,\n ): CommandClass;\n}\n\n/**\n * Refused early, where the author is looking.\n *\n * The whitespace and brace checks are aimed at one specific reader: somebody who\n * knows Laravel and reaches for `defineCommand(\"mail:send {user} {--queue}\")`.\n * Without this they get a command registered under a name containing a space,\n * which can never be typed and never runs, and no explanation.\n */\nfunction assertUsableName(name: string): void {\n if (name.trim() === \"\") {\n throw new Error(\"A command name cannot be empty.\");\n }\n\n if (/[{}]/.test(name) || /\\s/.test(name)) {\n throw new Error(\n `Invalid command name ${JSON.stringify(name)}. gemi does not parse a ` +\n `signature string; the name is the whole of it. Declare arguments ` +\n `with .arg() and flags with .option():\\n\\n` +\n ` defineCommand(\"mail:send\")\\n` +\n ` .arg(\"user\", { required: true })\\n` +\n ` .option(\"queue\", { type: \"boolean\" })\\n` +\n ` .handle(async ({ args, options }) => { ... })`,\n );\n }\n}\n\nexport function defineCommand(name: string): CommandBuilder<{}, {}> {\n assertUsableName(name);\n\n const args: CommandArgument[] = [];\n const options: CommandOption[] = [];\n let description = \"\";\n\n const builder = {\n [BUILDER_BRAND]: name,\n\n describe(text: string) {\n description = text;\n return builder;\n },\n\n arg(argName: string, spec: ArgSpec = {}) {\n // Checked here rather than at parse time because both of these are\n // mistakes in the declaration, and the declaration is what the author is\n // looking at right now. At parse time they would surface as a confusing\n // usage error in front of an operator who cannot fix them.\n if (spec.required && spec.default !== undefined) {\n throw new Error(\n `Argument \"${argName}\" of command \"${name}\" is both required and has ` +\n `a default. An argument that is supplied when absent cannot also be ` +\n `absent — drop one.`,\n );\n }\n\n // A variadic collects whatever is left, so there is no \"absent\" for a\n // default to fill — the empty list is the answer. Refused rather than\n // ignored, for the same reason as the pair above: a declaration the parser\n // silently drops reads, to whoever wrote it, exactly like one it honours.\n if (spec.variadic && spec.default !== undefined) {\n throw new Error(\n `Argument \"${argName}\" of command \"${name}\" is variadic and has a ` +\n `default. A variadic argument collects the remaining values and is ` +\n `an empty list when there are none, so the default could never ` +\n `apply — drop it, or drop \\`variadic\\`.`,\n );\n }\n\n const variadic = args.find((existing) => existing.variadic);\n if (variadic) {\n throw new Error(\n `Argument \"${argName}\" of command \"${name}\" is declared after the ` +\n `variadic argument \"${variadic.name}\", which collects everything ` +\n `that follows, so \"${argName}\" could never receive a value. A ` +\n `variadic argument must be declared last.`,\n );\n }\n\n if (args.some((existing) => existing.name === argName)) {\n throw new Error(\n `Command \"${name}\" declares the argument \"${argName}\" twice.`,\n );\n }\n\n args.push({ ...spec, name: argName });\n return builder;\n },\n\n option(optionName: string, spec: OptionSpec) {\n if (options.some((existing) => existing.name === optionName)) {\n throw new Error(\n `Command \"${name}\" declares the option \"${optionName}\" twice.`,\n );\n }\n\n const alias = \"alias\" in spec ? spec.alias : undefined;\n const clash =\n alias === undefined\n ? undefined\n : options.find(\n (existing) => \"alias\" in existing && existing.alias === alias,\n );\n if (clash) {\n throw new Error(\n `Options \"${clash.name}\" and \"${optionName}\" of command \"${name}\" ` +\n `both use the alias \"-${alias}\".`,\n );\n }\n\n options.push({ ...spec, name: optionName });\n return builder;\n },\n\n handle(fn: (ctx: any) => CommandResult) {\n const commandName = name;\n const commandDescription = description;\n\n const cls = class extends Command {\n static commandName = commandName;\n static description = commandDescription;\n // Copied, not aliased. The builder goes on owning `args`/`options` and\n // mutating them on every subsequent `.arg()`/`.option()`, so storing the\n // instances themselves let a chain reach back into a class it had\n // already produced — and `--help` and the parser both read these\n // statics, so the printed usage and the accepted arguments would change\n // under a command that was finished.\n static args = [...args];\n static options = [...options];\n\n run(ctx: any) {\n return fn(ctx);\n }\n };\n\n // This chain produced a command, so it is no longer unfinished. The brand\n // exists to catch a file that exports a builder and never calls `.handle()`\n // — see `refuseUnfinishedBuilders` in `services/discovery.ts`. Leaving it\n // set meant the natural way to share declarations,\n //\n // export const base = defineCommand(\"reindex\").option(\"verbose\", ...);\n // export default base.handle(async () => {});\n //\n // left a branded object among the file's exports and aborted the whole of\n // `gemi run` — every command in the project unrunnable, under a message\n // that was untrue for that file.\n delete (builder as Record<PropertyKey, unknown>)[BUILDER_BRAND];\n\n // An anonymous class expression reports `name === \"\"`, and the duplicate-\n // name error's whole job is to say *which two*. Give it the one name it\n // has.\n Object.defineProperty(cls, \"name\", {\n value: commandName,\n configurable: true,\n });\n\n return cls;\n },\n };\n\n return builder as unknown as CommandBuilder<{}, {}>;\n}\n\n/** True for a builder that never called `.handle()`. */\nexport function isUnfinishedBuilder(\n value: unknown,\n): value is { [BUILDER_BRAND]: string } {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as Record<PropertyKey, unknown>)[BUILDER_BRAND] === \"string\"\n );\n}\n",
|
|
8
|
-
"// A cron expression accepted by `Bun.cron`: a 5-field expression\n// (`minute hour day-of-month month day-of-week`, interpreted in UTC) or one of\n// the nicknames below. The `string & {}` member keeps arbitrary expressions\n// assignable while still surfacing the nicknames in editor autocomplete.\nexport type CronExpression =\n | \"@yearly\"\n | \"@annually\"\n | \"@monthly\"\n | \"@weekly\"\n | \"@daily\"\n | \"@midnight\"\n | \"@hourly\"\n | (string & {});\n\nimport type { HeldLock } from \"../lock/LockManager\";\n\nexport type WithoutOverlapping =\n | boolean\n | {\n /**\n * The lock's lease, in milliseconds: how long it outlives an instance\n * that died holding it. Renewed every third of this while the tick\n * runs, so it does not limit how long a tick may take. Default five\n * minutes.\n */\n expiresAfter?: number;\n };\n\nexport class CronJob {\n name: string;\n cron: CronExpression;\n\n /**\n * Skips a tick while an earlier tick of this job is still running, on this\n * instance or any other sharing the queue's storage. With the database\n * queue driver that is a lock row in `gemi_locks`; with the memory driver\n * it only covers this process.\n *\n * The lock is held across `onTick`, `callback` and `onComplete`, and handed\n * to `callback` as its argument. If its lease is lost while the tick runs\n * (the instance stalled past `expiresAfter`), `lock.lost` aborts and the\n * scheduler logs it. Writes that must not land after that go through\n * `lock.fence(...)`.\n *\n * It does not stop a second instance from running the same tick after the\n * first one finished; `onOneServer` does.\n */\n withoutOverlapping: WithoutOverlapping = false;\n\n /**\n * Runs each tick on one instance only, across every instance sharing the\n * queue's storage: the first to claim the tick's minute runs it, the others\n * skip it. Assumes the instances' clocks agree to well within a minute.\n */\n onOneServer = false;\n\n /**\n * Decides whether this tick happens at all. Evaluated once per tick, before\n * anything else; returning `false` skips `onTick`, `callback` and\n * `onComplete` alike.\n *\n * ### Why this is a member and not a line the job writes for itself\n *\n * A job that reports outward — mails a digest, opens a Sentry issue, pings a\n * channel — must not fire from a laptop, and the guard for that has to cover\n * the whole tick. There was nowhere to write it. gemi calls `onTick` and\n * `onComplete` *outside* `callback`, so a guard placed in a lifecycle hook\n * only stops the hook it lives in: an early `return` at the top of `callback`\n * leaves the job still announcing that it started and still announcing that\n * it finished, which for an outward-reporting job is most of the damage.\n *\n * The only construct that covered every hook was an abstract base class that\n * shadowed `callback` with the guard and renamed the real body to `run()` —\n * and that costs the app the framework's own vocabulary. A job then reads\n * `async run()` while every page of the documentation reads `async\n * callback()`, so the next person to follow the docs writes a `callback` the\n * base class never calls: no error, no output, a job that silently does\n * nothing. Forgetting *this* member fails the other way round — the job runs,\n * which is what it did before anyone asked for a gate.\n *\n * ### What it gates\n *\n * Work, not registration. A job whose gate returns `false` is still\n * scheduled, still holds its `Bun.cron` handle, and still appears in\n * `app(Scheduler).jobs` — \"is it scheduled\" and \"will it do anything\" are\n * different questions and the second one is answered per tick. That is also\n * why this is a method: a field would be read once, when the scheduler\n * constructs the job at boot, and would freeze whatever it saw then.\n *\n * A gate that throws is logged and treated as `false`. Cron work recurs, so\n * a skipped tick is recovered by the next one, while an email sent from the\n * wrong machine is not recovered at all.\n *\n * ### Composing with a base class\n *\n * Nothing here calls `super` for you. A subclass that overrides `shouldRun`\n * for its own reason silently replaces whatever its base was gating on, so\n * narrow rather than redefine:\n *\n * ```typescript\n * async shouldRun() {\n * return (await super.shouldRun()) && this.hasSubscribers();\n * }\n * ```\n *\n * A shared base should annotate its return type as written here rather than\n * let it be inferred: a base returning a plain `boolean` narrows the\n * signature its subclasses inherit, and the `await super` above then does not\n * typecheck against it.\n */\n shouldRun(): Promise<boolean> | boolean {\n return true;\n }\n\n /**\n * The tick's work. `lock` is the `withoutOverlapping` lock when the job\n * declares one.\n */\n callback(_lock?: HeldLock): Promise<void> | void {}\n onTick(): Promise<void> | void {}\n onComplete(): Promise<void> | void {}\n\n static exp(expression: CronExpression) {\n return expression;\n }\n}\n",
|
|
9
8
|
"import type { CronJob } from \"./CronJob\";\n\n// Config key: `schedule`. Consumed by `ScheduleServiceProvider`.\nexport interface ScheduleConfig {\n /**\n * The `CronJob` subclasses this application runs on a schedule, or nothing.\n *\n * ### Why \"or nothing\" is the point\n *\n * A cron job that is written and not listed here never fires, and unlike a\n * dropped dispatch there is no caller left waiting to notice: nothing is\n * downstream of a tick that did not happen. The report is simply not sent,\n * for as long as it takes somebody to ask why they stopped seeing it. That is\n * the whole failure — the file exists, the expression is right, and the class\n * was never in the module graph.\n *\n * Leaving this out spells the schedule once: the jobs are the classes under\n * `jobsDir`.\n *\n * ### The rule, exactly\n *\n * **Declared wins, and `[]` is declared.** A `jobs` that is present is used\n * verbatim and no directory is read — the escape hatch for an app whose jobs\n * live somewhere this cannot walk, for one that runs a deliberate subset, and\n * for a deploy that ships no source. An empty array means an app with nothing\n * scheduled and says so; it does not mean \"find some\".\n *\n * **Absent or `undefined` discovers.** `undefined` counts as absent for the\n * same reason `withDefaults` treats it that way everywhere else: a key spread\n * in from an optional value is an omission, not an instruction.\n *\n * Either way the set the scheduler ended up with is readable afterwards, as\n * `app(Scheduler).jobs` — an app that used to import this array to assert\n * something about its own schedule asks the scheduler instead.\n */\n jobs?: Array<new () => CronJob>;\n\n /**\n * Where to look when `jobs` was not declared. Relative to the project root,\n * or absolute.\n *\n * Every `.ts`/`.tsx` file underneath it is imported at boot and every exported\n * class extending `CronJob` is scheduled, so this wants to be a directory of\n * cron declarations rather than a directory that merely contains some. Moving\n * the jobs is what this field is for; pointing it at `app/` is not.\n */\n jobsDir?: string;\n}\n\nexport function defineScheduleConfig(config: ScheduleConfig): ScheduleConfig {\n return config;\n}\n\nexport function scheduleConfigDefaults(): Required<ScheduleConfig> {\n return {\n jobs: [],\n jobsDir: \"app/cron\",\n };\n}\n",
|
|
10
9
|
"import type { ListenerClass } from \"./Listener\";\n\n// Config key: `events`. Derived from `EventServiceProvider`.\nexport interface EventConfig {\n /**\n * The `Listener` subclasses this application registers, or nothing.\n *\n * ### Why \"or nothing\" is the point\n *\n * A dispatch fans out to whatever the `EventManager` holds for the event's\n * name, and an event nothing is registered for is not an error — it is the\n * ordinary state of an application that has not written that listener yet.\n * So this list is a second spelling of `app/listeners`, kept in step by hand,\n * and what the two disagreeing costs is a side effect that stops happening,\n * with a development-only warning as the only trace. Nothing fails, nothing\n * is dropped from a log: the welcome email simply is not sent.\n *\n * Leaving this out spells it once: the listeners are the classes under\n * `listenersDir`.\n *\n * ### The rule, exactly\n *\n * **Declared wins, and `[]` is declared.** A `listeners` that is present is\n * used verbatim and no directory is read — that is the escape hatch for an\n * app whose listeners live somewhere this cannot walk, for one that\n * deliberately registers a subset, and for a deploy that ships no source. An\n * empty array means an app with no listeners and says so; it does not mean\n * \"find some\".\n *\n * **Absent or `undefined` discovers.** `undefined` counts as absent for the\n * same reason `withDefaults` treats it that way everywhere else: a key spread\n * in from an optional value is an omission, not an instruction.\n */\n listeners?: ListenerClass[];\n\n /**\n * Where to look when `listeners` was not declared. Relative to the project\n * root, or absolute.\n *\n * Every `.ts`/`.tsx` file underneath it is imported at boot and every\n * exported class extending `Listener` is registered, so this wants to be a\n * directory of listener declarations rather than a directory that merely\n * contains some. That includes an abstract base a few listeners share: the\n * walk excludes the framework's `Listener` and nothing else, so a base\n * sitting here is registered alongside its subclasses and its `handle` runs\n * on every dispatch of whatever event it declares. Keep shared bases outside\n * this directory.\n *\n * There is no `eventsDir` to go with it. Event classes are never discovered\n * — each is imported by the listener that binds to it and by the code that\n * dispatches it, so nothing needs to walk them and a walk that imported them\n * anyway would only be a boot cost.\n */\n listenersDir?: string;\n}\n\nexport function defineEventConfig(config: EventConfig): EventConfig {\n return config;\n}\n\nexport function eventConfigDefaults(): Required<EventConfig> {\n return {\n listeners: [],\n listenersDir: \"app/listeners\",\n };\n}\n",
|
|
11
10
|
"import type { Event, EventClass } from \"./Event\";\n\n/**\n * One side effect of one event, in its own file.\n *\n * ```typescript\n * // app/listeners/SendWelcomeEmail.ts\n * export class SendWelcomeEmail extends Listener {\n * static name = \"SendWelcomeEmail\";\n * static event = UserRegistered;\n *\n * async handle(event: UserRegistered) {\n * await Mail.send(event.email, ...);\n * }\n * }\n * ```\n *\n * That is the whole of what the subsystem buys: adding a fourth side effect to\n * a registration is adding a file, rather than editing the controller that\n * already has three.\n *\n * ### Why the binding lives here and not on the event\n *\n * The listener is the thing with an opinion about what it cares about; an event\n * has no business knowing who is watching. An event listing its listeners would\n * also put the property back the way it was — adding a side effect would edit\n * an existing file.\n *\n * ### Why the binding is a value at all\n *\n * Laravel binds a listener to an event by reflecting on the type-hint of\n * `handle(UserRegistered $event)`. TypeScript erases types at runtime, so that\n * mechanism cannot exist here and the binding has to be carried as a value.\n * That constraint turns out to be a favourable one: it is also what makes the\n * payload types flow without a generated registry.\n *\n * ### The seam, written down rather than hidden\n *\n * **Nothing checks that `static event` and the annotation on `handle` agree.**\n * A static and an instance member cannot reference each other's types, so the\n * compiler sees `static event = UserRegistered` and `handle(event: OrderPaid)`\n * as two unrelated declarations. Copy a listener, change the static, forget the\n * annotation, and TypeScript is satisfied while the listener receives something\n * else.\n *\n * It is accepted because the failure is local and immediate — that one listener\n * reads a field that is not there, in its own stack — rather than the\n * misroute-shaped failures the rest of this subsystem is arranged against. The\n * `Listener.test-d.ts` beside this file pins the two halves the compiler *does*\n * enforce, so a later refactor reaching for convenience cannot widen them to\n * `any` unnoticed.\n */\nexport abstract class Listener {\n /**\n * The name this listener is reported and de-duplicated under. Required.\n *\n * Two listener classes claiming one name is refused at registration, and a\n * discovery walk makes that ordinary: `auth/NotifyAdmins.ts` beside\n * `billing/NotifyAdmins.ts` is a natural thing to write, and nothing forces\n * the import alias a hand-written list would have demanded.\n *\n * As on `Event`, the `\"unset\"` default is a floor and not the check — a class\n * declaration always shadows it with its own implicit binding, which is the\n * one a minifier renames. `discoverListeners` reads the property descriptor\n * to tell a declared name from an implicit one.\n *\n * For a **queued** listener it is more than a label: the queue is keyed by\n * name, the listener is registered under `listener:<name>`, and that string\n * is what a queued dispatch carries. So a queued listener whose name is the\n * implicit class binding is refused at registration rather than warned about\n * — the two ends of the queue can be two different module graphs, and a name\n * only one of them minified is a side effect that stops happening in\n * production and reports success.\n */\n static name = \"unset\";\n\n /**\n * The event class this listener handles. Exactly one, required.\n *\n * It holds the **class**, not its name. The name is read off it once, at\n * registration, inside `EventManager.useListeners` — and that read happens in\n * the module graph that declared the class, so the name it yields is the\n * source one. Nothing downstream keeps the class object, because a registry\n * keyed by class identity is wrong in production only.\n *\n * There is no `static events = [A, B]`, deliberately. A listener bound to two\n * events has to discriminate inside `handle`, and the natural way to write\n * that is `if (event instanceof UserRegistered)` — which is `true` in every\n * test and `false` in a production build, for the reason `Event`'s own doc\n * comment gives. Two events wanting the same side effect are two small\n * listeners calling one shared function.\n *\n * Declared as required, and still checked at runtime: every subclass inherits\n * this declaration whether or not it assigns to it, so the compiler cannot\n * see a listener that left it out. `EventManager` refuses one out loud.\n */\n static event: EventClass;\n\n /**\n * Where this listener runs. **A context boundary, not a performance dial.**\n *\n * Left `false`, the listener runs inline, inside the dispatcher's\n * `kernelContext` and `ormContext`: it has the request's `app()`, the\n * authenticated user through `currentActor()`, and it joins the ambient\n * transaction.\n *\n * **Unless the event declares `static afterCommit`.** That flag releases its\n * listeners after the commit and outside the transaction's scope, so a sync\n * listener bound to one runs with `currentTransaction()` undefined and\n * commits on its own. The listener kept sync *because* it writes a row that\n * has to roll back with the write it describes — an audit trail, a ledger\n * entry — starts committing separately the day someone sets that flag in the\n * event's file, and nothing on this side says so: no error, no warning, and\n * a `queued = false` that still reads as \"inside the transaction\".\n *\n * Set `true`, it is handed to the `QueueManager` instead and run from a\n * drain, on the queue's terms. What crosses is the event's name and its\n * constructor arguments as JSON, and nothing else — not the instance the sync\n * listeners share, and not a line of the request. With `worker = true` it is\n * a different thread with a cloned application.\n *\n * The part that catches people is that the context is not reliably *gone*\n * either: the queue is in-process, so a drain that happens to start from\n * `push` is still standing in the dispatcher's context and `app()` there\n * resolves the request's application. Nothing about that is promised. A\n * queued listener that reads the current actor, or writes expecting to join\n * the ambient transaction, works until the day the queue was already busy —\n * and then reads different rows, or commits separately, with no error either\n * way. So: a listener that needs the request's context has to stay sync, and\n * a listener that only needs the payload is free to queue.\n *\n * What it buys, in exchange, is the queue's whole retry path: `maxAttempts`,\n * `onFail`-style re-queueing and dead-lettering, none of which a sync\n * listener has. `dispatchAndWait` does **not** wait for it.\n */\n queued = false;\n\n /**\n * Attempts before the queue gives up, counting the first. `Job`'s field and\n * `Job`'s meaning, because it is forwarded to one.\n *\n * **Ignored unless `queued` is true.** A sync listener has no retry path at\n * all — its throw is logged and the next listener runs — so a `maxAttempts`\n * beside `queued = false` is a line that does nothing, which is why it is\n * said here rather than in a note somewhere else.\n */\n maxAttempts = 3;\n\n /**\n * Milliseconds to wait before each retry: a number for every retry, or an\n * array with one entry per retry, its last repeating. `Job`'s field and\n * `Job`'s meaning, because it is forwarded to one.\n *\n * **Ignored unless `queued` is true**, for the reason `maxAttempts` is.\n */\n backoff: number | number[] = 0;\n\n /**\n * Runs `handle` in a Worker thread with its own cloned application, for a\n * queued listener whose work is CPU-bound. `Job`'s field and `Job`'s\n * meaning, because it is forwarded to one.\n *\n * **Ignored unless `queued` is true.** A sync listener runs on the\n * dispatcher's stack by definition; there is no thread to move it to.\n */\n worker = false;\n\n /**\n * The side effect. Runs inside the dispatcher's context when `queued` is\n * false: a sync listener has the request's `app()`, its authenticated user,\n * and — unless the event declares `static afterCommit` — its ambient\n * transaction. A queued one can rely on none of them — see `queued`.\n *\n * Annotate the parameter with the event named in `static event` — narrowing\n * the base's `Event` here is legal because method parameters are bivariant,\n * which is what lets this class avoid a generic parameter. Nothing checks\n * that the two agree; see the note on the class.\n *\n * Abstract, so a listener that forgets it fails to compile rather than\n * silently handling nothing.\n *\n * A throw is caught, logged with both names, and the next listener still\n * runs. Listeners are independent side effects by construction, and their\n * order is a filesystem walk's — letting one cancel the rest would make that\n * order load-bearing, which is the exact coupling this subsystem exists to\n * remove.\n */\n abstract handle(event: Event): void | Promise<void>;\n}\n\n/**\n * A `Listener` subclass, as the registry and the `events` config slice hold\n * one.\n *\n * Zero-argument, because the manager constructs one per dispatch and has\n * nothing to pass it. The `event` member is typed as present even though\n * `EventManager` checks for it at runtime: an inherited declaration is\n * indistinguishable from an assignment to the type system, so this is the\n * shape, and the runtime check is what covers the difference.\n */\nexport type ListenerClass = (new () => Listener) & {\n name: string;\n event: EventClass;\n};\n",
|
|
12
11
|
"import { existsSync } from \"node:fs\";\nimport { dirname, isAbsolute, join } from \"node:path\";\n\nimport {\n discoverClasses,\n DiscoveryError,\n projectRoot,\n} from \"../support/discover\";\nimport { Command, type CommandClass } from \"../console/Command\";\nimport { commandConfigDefaults } from \"../console/config\";\nimport { isUnfinishedBuilder } from \"../console/builder\";\nimport { CronJob } from \"./cron/CronJob\";\nimport { scheduleConfigDefaults } from \"./cron/config\";\nimport { eventConfigDefaults } from \"./events/config\";\nimport type { EventClass } from \"./events/Event\";\nimport { Listener, type ListenerClass } from \"./events/Listener\";\nimport { Job } from \"./queue/Job\";\nimport { queueConfigDefaults } from \"./queue/config\";\n\n/**\n * Reading an application's jobs off the filesystem, for the two subsystems that\n * were asking every app to write its job list twice.\n *\n * Both functions here are the same call to `discoverClasses` with a different\n * base class, which is deliberate: one walk, one set of skip rules, one answer\n * to \"what does this app have\". They are exported from `gemi/services` rather\n * than kept private to the providers because the list stopped being something\n * an app could import — a test that used to walk the exported `jobs` array\n * needs somewhere to ask the same question, and so does a script.\n *\n * What the providers add on top of these is the decision of *whether* to ask,\n * which is the config's to make and not this file's.\n */\n\n/**\n * A directory the app named, resolved the way the app meant it.\n *\n * Relative paths hang off the project root rather than `process.cwd()` at the\n * moment of the call, because a config slice saying `app/jobs` means the app's\n * `app/jobs` and not whatever directory somebody happened to launch a script\n * from. Absolute paths are taken as given, which is what makes these callable\n * from a test that has no project around it.\n */\nfunction resolveDir(dir: string): string {\n return isAbsolute(dir) ? dir : join(projectRoot(), dir);\n}\n\n/**\n * Says something when the directory is missing *and so is the tree it lives in*.\n *\n * A missing directory has two readings and they want opposite responses. An app\n * that has never written a cron job has no `app/cron`, and warning about that on\n * every boot for the rest of its life is noise — the answer \"you have no cron\n * jobs\" is correct and uninteresting. But a deploy that ships `dist/` and\n * `node_modules/` without the source has no `app/cron` *and no `app/`*, and\n * there discovery quietly returns nothing while the app very much does have\n * jobs: the exact silence #322 and #323 exist to remove, reintroduced one layer\n * down and in production only.\n *\n * The parent directory is what tells them apart. If neither the directory nor\n * the one above it is there, the tree this was pointed into is not there\n * either, and that is worth a sentence.\n */\nfunction warnIfSourceIsMissing(\n resolved: string,\n subject: string,\n slice: string,\n) {\n if (existsSync(resolved) || existsSync(dirname(resolved))) return;\n\n console.warn(\n `No ${subject} were discovered: neither ${resolved} nor the directory ` +\n `above it exists, so this process cannot see the application's source. ` +\n `If this is a deploy that ships only the build output, list the classes ` +\n `explicitly in app/config/${slice}.ts so they travel with it.`,\n );\n}\n\n/**\n * The `Job` subclasses under `dir`, defaulting to the config slice's `app/jobs`.\n *\n * This is what a `queue` config slice with no `jobs` key resolves to. Call it\n * directly to ask the question a test used to answer by importing the array:\n * every job this application would register, in the order it would register\n * them.\n *\n * Every file underneath is imported, so calling this runs the app's job modules\n * — the cost `discoverClasses` documents at length, paid once per call.\n */\nexport async function discoverJobs(\n dir: string = queueConfigDefaults().jobsDir,\n): Promise<Array<new () => Job>> {\n const resolved = resolveDir(dir);\n warnIfSourceIsMissing(resolved, \"queue jobs\", \"queue\");\n const jobs = await discoverClasses(resolved, Job);\n warnIfNameWillNotSurviveTheBuild(jobs);\n return jobs;\n}\n\n/**\n * Says something about a discovered job that never declared `static name`.\n *\n * This is the one hazard discovery introduces rather than removes, and it is\n * production-only. `gemi build` minifies the server entry, and app code\n * reachable from it — a controller, and every job class that controller\n * imports to dispatch — is bundled and minified with it. Minification renames\n * the class binding, and a class's implicit `.name` is that binding: a\n * `TestJob` in the bundle reports `\"D\"`. Discovery, meanwhile, imports\n * `app/jobs/TestJob.ts` from source at runtime, where it is still `\"TestJob\"`.\n * The queue is keyed by that name, so the two halves stop agreeing and the\n * dispatch is dropped — in production, and only there.\n *\n * A declared `static name = \"TestJob\"` is a string literal, which survives\n * minification intact, and the two halves agree again. `docs/jobs-and-queues.md`\n * has always called it required; before discovery an app could get away without\n * it, because the explicit `jobs` array was bundled too and both sides were\n * wrong in the same way.\n *\n * The check is exact rather than a guess: a `static name = \"...\"` class field\n * defines a *writable* own property, while the implicit class name is\n * non-writable. Cron is unaffected — `CronJob.name` is an instance field\n * holding a string literal, not the class binding.\n */\nfunction warnIfNameWillNotSurviveTheBuild(jobs: Array<new () => Job>) {\n for (const job of jobs) {\n if (Object.getOwnPropertyDescriptor(job, \"name\")?.writable) continue;\n\n console.warn(\n `Queued job ${job.name} does not declare \\`static name\\`, so it is ` +\n `registered under its class name. A production build minifies the ` +\n `class that dispatches it and renames it, while discovery reads this ` +\n `file from source and does not — so the dispatch would be dropped in ` +\n `production and nowhere else. Add: static name = \"${job.name}\";`,\n );\n }\n}\n\n/**\n * The `CronJob` subclasses under `dir`, defaulting to the config slice's\n * `app/cron`.\n *\n * This is what a `schedule` config slice with no `jobs` key resolves to. Call it\n * directly to ask the question a test used to answer by importing the array —\n * though after boot `app(Scheduler).jobs` is the better one, because it is what\n * the scheduler actually took rather than what this would find if asked again.\n *\n * Every file underneath is imported, so calling this runs the app's cron\n * modules — the cost `discoverClasses` documents at length, paid once per call.\n */\nexport async function discoverCronJobs(\n dir: string = scheduleConfigDefaults().jobsDir,\n): Promise<Array<new () => CronJob>> {\n const resolved = resolveDir(dir);\n warnIfSourceIsMissing(resolved, \"cron jobs\", \"schedule\");\n return await discoverClasses(resolved, CronJob);\n}\n\n/**\n * The `Command` subclasses under `dir`, defaulting to the config slice's\n * `app/commands`.\n *\n * This is what a `commands`-less `command` slice resolves to, and what\n * `gemi run` lists. Unlike the two above it is not called during boot — a\n * command registry has one consumer, and importing every file under\n * `app/commands` into every server process would be a cost paid on every start\n * for code no request reaches.\n *\n * Every file underneath is imported, so calling this runs the app's command\n * modules — the cost `discoverClasses` documents at length, paid once per call.\n */\nexport async function discoverCommands(\n dir: string = commandConfigDefaults().commandsDir,\n): Promise<CommandClass[]> {\n const resolved = resolveDir(dir);\n warnIfSourceIsMissing(resolved, \"commands\", \"command\");\n\n const unfinished: string[] = [];\n\n const commands = await discoverClasses(\n resolved,\n Command,\n [],\n (value, file) => {\n if (isUnfinishedBuilder(value)) unfinished.push(file);\n },\n );\n\n refuseUnfinishedBuilders(unfinished);\n\n return commands as CommandClass[];\n}\n\n/**\n * Stops the walk for a builder chain that was never finished.\n *\n * This is the one silence the builder introduces, and it is the exact shape\n * #322 and #323 exist to remove. `defineCommand(...).arg(...)` without a\n * trailing `.handle(...)` is a plain object rather than a class, so\n * `discoverClasses` discards it along with every constant and helper a command\n * file happens to export — and the command disappears from `gemi run` with\n * nothing raised. The author sees a listing containing every command except the\n * one they just wrote.\n *\n * A throw rather than a warning, because there is no reading of an unfinished\n * builder under which the author meant it: the chain builds a description of a\n * command and then discards it.\n *\n * \"Unfinished\" is decided by `.handle()` clearing the brand, not by the export\n * still being a builder object. Those differ for the natural way to share\n * declarations —\n *\n * export const base = defineCommand(\"reindex\").option(\"verbose\", …);\n * export default base.handle(async () => {});\n *\n * — where `base` is a builder that *did* produce a command. Keying off the shape\n * alone made that file abort the whole of `gemi run`, taking every other command\n * in the project down with it, under a message that was untrue for it.\n */\nfunction refuseUnfinishedBuilders(files: string[]): void {\n if (files.length === 0) return;\n\n const subject =\n files.length === 1\n ? `${files[0]} exports a command builder that never called \\`.handle()\\``\n : `${files.length} files export a command builder that never called ` +\n `\\`.handle()\\`:\\n ${files.join(\"\\n \")}`;\n\n throw new DiscoveryError(\n `${subject}, so there is no command to run. \\`.handle()\\` is what turns ` +\n `the chain into a command — finish it:\\n\\n` +\n ` export default defineCommand(\"my-command\")\\n` +\n ` .handle(async ({ line }) => { line(\"hello\") })`,\n );\n}\n\n/**\n * The `Listener` subclasses under `dir`, defaulting to the config slice's\n * `app/listeners`.\n *\n * This is what an `events` config slice with no `listeners` key resolves to.\n * Call it directly to ask what an application would register: every listener,\n * in the order it would register them.\n *\n * Only listeners are walked. Event classes are never discovered — each is\n * imported by the listener that binds to it and by the code that dispatches it,\n * so there is nothing a walk over them would find that is not already in the\n * module graph.\n *\n * **Known residual, matching jobs and cron.** `discoverClasses` excludes the\n * `base` it is handed and not intermediates, so an abstract listener base\n * written in `listenersDir` and extended by its siblings is discovered and\n * registered alongside them — constructed, with its `handle` called, on every\n * dispatch of whatever event it declares. Keep shared bases outside the\n * directory.\n *\n * Every file underneath is imported, so calling this runs the app's listener\n * modules — the cost `discoverClasses` documents at length, paid once per call.\n *\n * @throws DiscoveryError if a discovered listener declares no `static event`.\n */\nexport async function discoverListeners(\n dir: string = eventConfigDefaults().listenersDir,\n): Promise<ListenerClass[]> {\n const resolved = resolveDir(dir);\n warnIfSourceIsMissing(resolved, \"event listeners\", \"events\");\n\n const listeners = (await discoverClasses(\n resolved,\n Listener,\n )) as ListenerClass[];\n\n refuseListenersWithNoEvent(listeners);\n warnIfListenerNameWillNotSurviveTheBuild(listeners);\n warnIfEventNameWillNotSurviveTheBuild(listeners);\n\n return listeners;\n}\n\n/**\n * Stops the walk for a listener that binds to nothing.\n *\n * `static event` is declared as required on the base and the compiler still\n * cannot see it missing: every subclass inherits the declaration whether or not\n * it assigns to one, so `typeof SomeListener` type-checks either way. What the\n * author gets instead is a file that is imported, registered under no name, and\n * never called — the silence this whole module is arranged against.\n *\n * A throw rather than a warning, because there is no reading of a listener with\n * no event under which the author meant it. The usual cause is the residual\n * above: an abstract base sharing a gate between two listeners, sitting in\n * `listenersDir` where the walk finds it. Moving it out is the fix, and the\n * message says so — the alternative, excluding it by some guess about\n * abstractness, is how a discovery pass ends up quietly covering less than the\n * directory holds.\n */\nfunction refuseListenersWithNoEvent(listeners: ListenerClass[]): void {\n const orphans = listeners.filter((listener) => !listener.event);\n if (orphans.length === 0) return;\n\n throw new DiscoveryError(\n `${orphans\n .map((listener) => listener.name)\n .join(\", \")} declare no \\`static event\\`, so there is no event name to ` +\n `register them under and their \\`handle\\` would never run. Every ` +\n `listener binds to exactly one event:\\n\\n` +\n ` static event = UserRegistered;\\n\\n` +\n `If this is an abstract base its subclasses extend, move it out of the ` +\n `listeners directory — the walk excludes gemi's \\`Listener\\` and ` +\n `nothing else, so a base sitting beside its subclasses is registered ` +\n `and run like one of them.`,\n );\n}\n\n/**\n * Says something about a discovered listener that never declared `static name`.\n *\n * The hazard is `warnIfNameWillNotSurviveTheBuild`'s, one subsystem over: a\n * `static name = \"...\"` class field defines a *writable* own property while the\n * implicit class binding does not, and a minifier renames the binding while\n * leaving the string literal alone. The two spellings are indistinguishable by\n * value — both read `\"SendWelcomeEmail\"` in development — so the descriptor is\n * the only thing that tells them apart, and the difference only becomes visible\n * in a production build.\n *\n * A warning rather than a refusal, because a *sync* listener's name has no\n * second half to disagree with: every reader of it comes from this same\n * source-side walk, so a minified build and this walk cannot produce two\n * different strings for it. A **queued** listener does have one — the name is\n * the queue's key and it crosses a JSON boundary — and that case is refused\n * outright, by `jobForListener`, at the point the listener is registered with\n * the queue rather than here. This walk cannot tell the two apart without\n * constructing every listener it found; the registry constructs them anyway.\n */\nfunction warnIfListenerNameWillNotSurviveTheBuild(listeners: ListenerClass[]) {\n for (const listener of listeners) {\n if (Object.getOwnPropertyDescriptor(listener, \"name\")?.writable) continue;\n\n console.warn(\n `Event listener ${listener.name} does not declare \\`static name\\`, so ` +\n `it is registered under its class name, which a production build ` +\n `renames. Nothing reads that name across a build boundary while the ` +\n `listener is sync, which is why this is a warning — but adding ` +\n `\\`queued = true\\` to it is refused at boot for the same reason, ` +\n `because the queue's key is that string. Add: static name = ` +\n `\"${listener.name}\";`,\n );\n }\n}\n\n/**\n * Says something about the *event* a discovered listener binds to, when that\n * event's name is the implicit class binding.\n *\n * This is the check that matters from the first dispatch, and it is the reason\n * `discoverListeners` reads `static event` at all. The registry is keyed by the\n * event's name; the dispatcher supplies that name from the class it constructs.\n * A production build minifies the server entry and the app code reachable from\n * it — the controller, and the event class it imports in order to dispatch —\n * while discovery imports `app/listeners/*.ts` from source, and those files\n * import `app/events/*.ts` from source too. Two module graphs, two class\n * objects, and an implicit `.name` that reads `\"UserRegistered\"` on one side\n * and `\"D\"` on the other. The dispatch then finds no listeners, which is legal,\n * normal, and completely silent.\n *\n * A declared `static name` is a string literal, which survives minification\n * intact, and the two halves agree again.\n *\n * Checked through the descriptor rather than the string, and not by comparing\n * `event.name` to anything: in development the two spellings produce the same\n * value, so a check on the string would be a tautology that passes forever.\n */\nfunction warnIfEventNameWillNotSurviveTheBuild(listeners: ListenerClass[]) {\n const warned = new Set<EventClass>();\n\n for (const { event } of listeners) {\n if (warned.has(event)) continue;\n if (Object.getOwnPropertyDescriptor(event, \"name\")?.writable) continue;\n warned.add(event);\n\n console.warn(\n `Event ${event.name} does not declare \\`static name\\`, so it is ` +\n `dispatched under its class name. A production build minifies the ` +\n `code that dispatches it and renames the class, while discovery reads ` +\n `the listener's copy from source and does not — so the dispatch would ` +\n `reach no listener in production and nowhere else. Add: ` +\n `static name = \"${event.name}\";`,\n );\n }\n}\n"
|
|
13
12
|
],
|
|
14
|
-
"mappings": "
|
|
15
|
-
"debugId": "
|
|
13
|
+
"mappings": "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",
|
|
14
|
+
"debugId": "F4BF85548D6425F064756E2164756E21",
|
|
16
15
|
"names": []
|
|
17
16
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{Kb as e}from"./chunk-3z9vhjd7.js";import{Vb as l,Xb as g,Zb as YZ,_b as UZ,ac as _Z}from"./chunk-beks65me.js";import{cc as WZ,fc as OZ,hc as HZ,ic as zZ,jc as q,kc as VZ,lc as KZ,nc as XZ}from"./chunk-ga7c7tq0.js";import{qc as I}from"./chunk-f1y8eggf.js";import{Cd as f,Hc as xZ,Kd as o,Lc as a,Md as m,Qd as r,Sc as PZ,Yd as y,ae as R,be as u,ce as d,nd as j,xd as CZ,yd as s}from"./chunk-4sb1484x.js";import{Ae as i,ze as BZ}from"./chunk-9hr0bcfy.js";import{Sf as h,gg as t,ig as v,jg as w,kg as k}from"./chunk-j0sncgxc.js";import{xg as GZ,zg as P}from"./chunk-b1mk12xg.js";import{Ag as B}from"./chunk-hfyxvdjj.js";import{Gg as NZ}from"./chunk-57edag57.js";import{uh as n}from"./chunk-6w79zajj.js";import{Ah as JZ,Dh as b}from"./chunk-eazqda9z.js";async function kZ(Z,$={}){try{if(!await Z.shouldRun.call(Z))return}catch(W){console.error(`Error evaluating shouldRun for cron job ${Z.name}:`,W);return}let G=MZ(Z.withoutOverlapping);if(!Z.onOneServer&&G===void 0){await p(Z);return}let H;try{if(H=$.locks?.()??yZ(),Z.onOneServer){let W=Math.floor(($.now??Date.now())/60000);if(!await H.store.advance(`gemi:cron-tick:${Z.name}`,W))return}}catch(W){console.error(`Could not claim the tick of cron job ${Z.name}; skipped it:`,W);return}if(G===void 0){await p(Z);return}try{await H.run(`gemi:cron:${Z.name}`,{ttl:G},(W)=>p(Z,W))}catch(W){if(W instanceof GZ)console.error(`Cron job ${Z.name} lost its withoutOverlapping lock while running: another tick may have started alongside it.`,W);else console.error(`Could not take the withoutOverlapping lock of cron job ${Z.name}:`,W)}}var ZZ=300000;function MZ(Z){if(!Z)return;if(Z===!0)return ZZ;return Z.expiresAfter??ZZ}var $Z;function yZ(){return $Z??=new P(new WZ),$Z}async function p(Z,$){try{await Z.onTick.call(Z)}catch(G){console.error(`Error executing cron job ${Z.name}:`,G)}try{await Z.callback.call(Z,$)}catch(G){console.error(`Error in cron job ${Z.name}:`,G)}try{await Z.onComplete.call(Z)}catch(G){console.error(`Error completing cron job ${Z.name}:`,G)}}class F{static token="scheduler";handles=[];resolved;inFlight=new Map;stopped=!1;locks;constructor(Z){this.resolved=Z.jobs}get jobs(){return[...this.resolved]}useJobs(Z){this.resolved=Z}useLocks(Z){this.locks=Z}start(Z=($)=>$()){if(this.stopped=!1,this.resolved.length===0)return;let $=globalThis.__gemiCronJobs??=new Map,G=new Map;for(let H of this.resolved){let W=new H;if(!W.name){console.error(`Cron job must have a name \u2014 ${H.name||"an anonymous class"} has none, so it is not scheduled. If this is a base class that other jobs extend, move it out of the discovered directory: \`abstract\` does not survive to runtime, so a class under app/cron is indistinguishable from a job.`);continue}if(!W.cron){console.error(`Cron job must have an expression. Job: ${W.name}`);continue}let z=G.get(W.name);if(z!==void 0){console.error(`Two cron jobs claim the name "${W.name}" \u2014 ${z} is scheduled and ${H.name||"an anonymous class"} is not. A name is the scheduler's key, so only one job can hold it. Rename one.`);continue}G.set(W.name,H.name||"an anonymous class"),$.get(W.name)?.stop();let Y=Bun.cron(W.cron,()=>{if(this.stopped)return;let V=this.locks;return this.track(W.name,()=>Z(()=>kZ(W,{locks:V})))});$.set(W.name,Y),this.handles.push(Y)}}stop(){this.stopped=!0;for(let Z of this.handles)Z.stop();this.handles=[]}async drain(Z=1/0){this.stop();let $,G=new Promise((H)=>{if(Z!==1/0)$=setTimeout(H,Z)});return await Promise.race([Promise.allSettled([...this.inFlight.values()].map((H)=>H.done)),G]),clearTimeout($),{unfinished:[...this.inFlight.values()].map((H)=>({...H.tick}))}}get running(){return this.inFlight.size}track(Z,$){let G={},H=Promise.resolve().then($).finally(()=>this.inFlight.delete(G));return this.inFlight.set(G,{tick:{name:Z,startedAt:new Date},done:H}),H}}import{isMainThread as wZ}from"worker_threads";var fZ=100,bZ=(Z)=>Math.min(fZ,Math.floor(Z/10));class gZ extends I{register(){this.app.singleton(q,()=>new q(this.app.config.get("queue",{}),{application:this.app})),this.app.singleton(P,()=>this.app.make(q).locks)}async boot(){let Z=this.app.config.get("queue",{});if(Z.jobs!==void 0)return;let{jobsDir:$}=B(HZ(),Z);this.app.make(q).useJobs(await YZ($))}async shutdown(Z){if(!this.app.resolved(q))return;let $=this.app.make(q);if($.running>0)console.log(`[gemi] Shutting down: waiting for ${$.running} running queued job(s).`);let G=Z?.timeoutMs,{unfinished:H}=await $.drain(G===void 0?1/0:Math.max(0,G-bZ(G)));if(H.length>0)return console.error("[gemi] Queued jobs still running at shutdown: "+H.map((W)=>`${W.name} (${W.id})`).join(", ")),{abandoned:!0}}}function T3(Z){if(mZ(Z),Bun.env.NODE_ENV!=="production")return hZ(Z);if(IZ(Z))return;if(!VZ())return;if(!Z.bound(q))return;Z.make(q).start()}function IZ(Z){let $=Z.config.get("queue",{}).driver??"memory";return $==="memory"||$ instanceof zZ}function vZ(Z){if(IZ(Z))return!0;if(typeof Z.config.get("queue",{}).driver!=="function"||!Z.bound(q))return!1;try{return!Z.make(q).durable}catch{return!1}}function mZ(Z){let $=process.env.GEMI_QUEUE_CLAIM;if($===void 0||XZ())return;if(process.env.ROOT_DIR===void 0||!wZ)return;if(KZ()){if(!vZ(Z))return;console.warn('[gemi] GEMI_QUEUE_CLAIM=off is ignored: the memory queue driver keeps jobs in the process that dispatched them, so no worker could run them. This server runs its own jobs. Use a driver other processes can read, such as "database", to hand jobs to `gemi queue:work`.')}else if($.trim().toLowerCase()!=="on")console.warn(`[gemi] GEMI_QUEUE_CLAIM="${$}" is not "off" or "on", so this server claims jobs. Set it to "off" to leave them to \`gemi queue:work\`.`)}function hZ(Z){let $=globalThis.__gemiDevQueue;if(!$)return;if(globalThis.__gemiDevQueue=void 0,$.stop(),Z.bound(q))Z.make(q).start()}class cZ extends I{register(){this.app.singleton(j,()=>new j(this.app.config.get("changeFeed",{}),{application:this.app}))}async shutdown(){if(!this.app.resolved(j))return;await this.app.make(j).close()}}class uZ extends I{register(){this.app.singleton(d,()=>new d(this.app.config.get("auth",{})))}}var pZ=100,dZ=(Z)=>Math.min(pZ,Math.floor(Z/10));class lZ extends I{register(){this.app.singleton(F,()=>new F(B(l(),this.app.config.get("schedule",{}))))}async boot(){let Z=this.app.make(F),$=this.app.config.get("schedule",{});if($.jobs===void 0){let{jobsDir:H}=B(l(),$);Z.useJobs(await UZ(H))}if(process.env.GEMI_NO_SCHEDULE==="1")return;let G=this.app;if(G.bound(P))Z.useLocks(()=>G.make(P));Z.start((H)=>JZ.run(G,H))}async shutdown(Z){if(!this.app.resolved(F))return;let $=this.app.make(F);if($.running>0)console.log(`[gemi] Shutting down: waiting for ${$.running} running cron tick(s).`);let G=Z?.timeoutMs,{unfinished:H}=await $.drain(G===void 0?1/0:Math.max(0,G-dZ(G)));if(H.length>0)return console.error("[gemi] Cron jobs still running at shutdown: "+H.map((W)=>`${W.name} (started ${W.startedAt.toISOString()})`).join(", ")),{abandoned:!0}}}class T extends CZ{modelName;constructor(Z="FeatureFlag"){super();this.modelName=Z}get available(){return BZ(this.modelName)}async load(){if(!this.available)throw new s(this.modelName);let Z=i(this.modelName);return await Z.asSystem(()=>Z.findMany())}async write(Z,$,G){if(!this.available)throw new s(this.modelName);let H=i(this.modelName),W=this.hasActorColumn(H),z={active:$};if(W)z.updatedBy=G.actor;return await H.asSystem(()=>H.upsert({where:{key:Z},create:{key:Z,...z},update:z})),{actorRecorded:W}}hasActorColumn(Z){let $=Z?.$schema?.fields?.updatedBy;return $!==void 0&&$.type==="String"}}function i3(Z){return Z}function qZ(){return{features:void 0,enabled:!0,source:new T,model:"FeatureFlag",ttl:30,context:void 0,onEvaluate:void 0,maxClientFlags:200}}class iZ extends I{register(){this.app.singleton(f,()=>{let Z=this.app.config.get("features",{}),$=B(qZ(),Z);if(Z?.model!==void 0&&Z?.source===void 0)$.source=new T(Z.model);else if(Z?.model!==void 0&&Z.source instanceof T&&Z.source.modelName!==Z.model)w.warning(`app/config/features.ts sets \`model: "${Z.model}"\` and a source reading "${Z.source.modelName}". The source wins; drop \`model\` to silence this.`);return new f($,(G)=>w.warning(G))})}async boot(){let Z=this.app.make(f);if(!Z.config.enabled)return;try{await Z.refresh()}catch($){w.error(`Could not warm the feature flag cache at boot. ${$ instanceof Error?$.message:String($)}`)}}}class sZ extends I{register(){this.app.singleton(n,()=>new n(this.app.config.get("mail",{})))}}function nZ(Z){return`listener:${Z}`}function QZ(Z,$){tZ(Z);let{maxAttempts:G,backoff:H,worker:W}=$,z=Z.event.name,Y=class extends OZ{maxAttempts=G;backoff=H;worker=W;async run(...V){let[K,J]=oZ(V),C=b(L).rehydrate(K,J);await new Z().handle(C)}onFail(V){console.error(`The listener ${Z.name} threw while handling ${z} off the queue. The queue will retry it up to ${G} times in total; nothing the dispatcher did is waiting on it.`,V)}onDeadletter(V){console.error(`The listener ${Z.name} failed every one of its ${G} attempts at ${z} and has been dead-lettered, so this side effect did not happen and will not be retried.`,V)}};return Object.defineProperty(Y,"name",{value:nZ(Z.name),writable:!0}),Y}function tZ(Z){if(Object.getOwnPropertyDescriptor(Z,"name")?.writable)return;throw Error(`The listener ${Z.name} sets \`queued = true\` but does not declare \`static name\`, so it would be registered with the queue `+"under its class name \u2014 which a production build renames while the "+`payload that names it does not. Add: static name = "${Z.name}";`)}function oZ(Z){return typeof Z[0]==="string"?Z:Z[0]}class L{static token="events";listeners={};events={};warnedFor=new Set;warnedAboutWaiting=new Set;config;constructor(Z={}){this.config=B(g(),Z),this.useListeners(this.config.listeners)}useListeners(Z){this.config.listeners=Z,this.listeners={},this.events={};let $=new Set;for(let G of Z){if(!G.event){console.error(`Listener ${G.name} does not declare \`static event\`, so there is no event name to register it under and its handle will never run. Add: static event = SomeEvent;`);continue}if($.has(G.name)){console.error(`Two event listeners are named "${G.name}" \u2014 the first is `+"registered and this one is not. A listener's name identifies it across the framework, so only one class can hold it. Rename one.");continue}$.add(G.name);let H=G.event.name,W=new G;this.events[H]??=G.event,(this.listeners[H]??=[]).push({listener:G,job:W.queued?QZ(G,W):null})}}get queuedListenerJobs(){return Object.values(this.listeners).flat().flatMap(({job:Z})=>Z?[Z]:[])}get registeredListeners(){return[...this.config.listeners]}dispatch(Z,$){if(this.deferToCommit(Z,$,!1))return;this.runListeners(Z,$)}async dispatchAndWait(Z,$){if(this.deferToCommit(Z,$,!0))return;return this.runListeners(Z,$)}deferToCommit(Z,$,G){let{name:H,afterCommit:W}=Z.constructor;if(!W)return!1;let z=NZ(()=>this.runListeners(Z,$));if(z&&G)this.warnNothingIsWaitedFor(H);return z}async runListeners(Z,$){let G=Z.constructor.name,H=this.listeners[G];if(!H?.length){this.warnNothingIsListening(G);return}for(let{listener:W,job:z}of H){if(z){this.enqueue(z,W,G,$);continue}try{await new W().handle(Z)}catch(Y){console.error(`The listener ${W.name} threw while handling ${G}. The `+"listeners after it still ran \u2014 a listener's failure is its own.",Y)}}}rehydrate(Z,$=[]){let G=this.events[Z];if(!G)throw Error(`Cannot rebuild the event "${Z}" that came off the queue: no registered listener binds to an event of that name, so there is no class to construct. A queued listener runs in an application that `+"discovered its own listeners \u2014 check that this process sees the "+`same app/listeners as the one that dispatched, and that the event declares \`static name = "${Z}"\`.`);return new G(...$)}enqueue(Z,$,G,H){if(H===void 0){console.error(`The listener ${$.name} is queued, but ${G} reached the dispatcher without its constructor arguments, so there is nothing for the queue to rebuild it from and the listener was skipped. Dispatch through ${G}.dispatch(...) or ${G}.dispatchAndWait(...), which carry them.`);return}let W=(z)=>console.error(`The listener ${$.name} could not be queued for ${G}, so it did not run and will not be retried. The listeners after it still ran.`,z);try{b(q).push(Z,JSON.stringify([G,H]),{reportFailure:!1}).catch(W)}catch(z){W(z)}}warnNothingIsWaitedFor(Z){if(Bun.env.NODE_ENV==="production")return;if(this.warnedAboutWaiting.has(Z))return;this.warnedAboutWaiting.add(Z),console.warn(`[gemi] ${Z}.dispatchAndWait() was called inside a transaction and ${Z} declares \`static afterCommit = true\`, so it resolved `+"immediately with no listener having run \u2014 they run when the "+"transaction commits. Await the transaction instead, or drop afterCommit if the caller needs the side effects first. Development only.")}warnNothingIsListening(Z){if(Bun.env.NODE_ENV==="production")return;if(this.warnedFor.has(Z))return;this.warnedFor.add(Z),console.warn(`[gemi] ${Z} was dispatched and nothing is listening for it. A listener registers under the name its event declares, so check that app/listeners holds a Listener with \`static event = ${Z}\` and that ${Z} declares \`static name = "${Z}"\`. Development only.`)}}class rZ extends I{register(){this.app.singleton(L,()=>new L(this.app.config.get("events",{})))}async boot(){let Z=this.app.make(L),$=this.app.config.get("events",{});if($.listeners===void 0){let{listenersDir:W}=B(g(),$);Z.useListeners(await _Z(W))}let G=Z.queuedListenerJobs;if(G.length===0)return;let H=this.app.make(q);for(let W of G)H.registerJob(W)}}class aZ extends I{register(){this.app.singleton(t,()=>new t(this.app.config.get("filesystem",{})))}}class eZ extends I{register(){this.app.singleton(o,()=>new o(this.app.config.get("image",{})))}}class Z3 extends I{register(){this.app.instance(r,new r)}}function M4(Z){return Z}function AZ(){return{maxFileSize:10485760,onLogFileClosed:()=>{},onLogCreated:()=>{}}}class $3 extends I{register(){this.app.singleton(v,()=>new v(B(AZ(),this.app.config.get("log",{}))))}async boot(){if(!process.env.ROOT_DIR)return;await this.app.make(v).boot()}}class G3 extends I{register(){this.app.singleton(m,()=>new m(B(xZ(),this.app.config.get("middleware",{}))))}boot(){this.app.make(m).assertGlobalMiddleware()}}function l4(Z){return Z}function FZ(){return{driver:new PZ,limit:1000,window:60}}class W3 extends I{register(){this.app.singleton(a,()=>new a(B(FZ(),this.app.config.get("ratelimiter",{}))))}}function a4(Z){return Z}function LZ(){return{url:process.env.REDIS_URL,options:void 0}}class H3 extends I{register(){this.app.singleton(k,()=>new k(B(LZ(),this.app.config.get("redis",{}))))}shutdown(){if(!this.app.resolved(k))return;this.app.make(k).close()}}class N extends e{status;constructor(Z,$){super(Z);this.status=$;this.name="McpToolError"}}var z3=/^[A-Za-z0-9_-]{1,64}$/,V3=/:([A-Za-z_][A-Za-z0-9_]*)([?*+]?)/g,SZ=4000,E=1e5,K3=/^(text\/|application\/([\w.+-]*\+)?(json|xml))/i;class M{router;dispatcher;static token="router.mcp";plans=new Map;constructor(Z,$){this.router=Z;this.dispatcher=$;for(let[G,H]of Object.entries(Z.routes??{}))this.plans.set(G,this.plan(G,H))}list(Z,$){return TZ(Z),this.descriptors($)}descriptors(Z){let $=[];for(let{descriptor:G}of this.plans.values()){if(Z?.names&&!Z.names.includes(G.name))continue;if(Z?.tags&&!G.tags.some((H)=>Z.tags.includes(H)))continue;$.push(G)}return $}async execute(Z,$,G,H){TZ(Z);let W=this.plans.get($);if(!W)throw Error(`McpRegistry: there is no tool named "${$}".`);let{descriptor:z}=W,Y=z.inputSchema.safeParse(G);if(Y.ok===!1)throw new N(`Invalid arguments for "${$}": ${Y.errors.join(", ")}`);let V=Y.value,K={caller:Z,req:Z.req,tool:z,input:V,ctx:H},J=await this.fillPath(W,K),{path:C}=this.dispatcher.getRouteHandlerAndParams(new Request(`http://gemi.internal/api${J}`));if(C!==z.url)throw new N(`"${$}" has nothing at ${J}.`,404);let U={};for(let _ of W.jsonKeys)if(V[_]!==void 0)U[_]=V[_];let Q,A="";if(W.fileFields.length>0)Q=await this.formData(W,U,K);else if(z.method==="GET")A=U3(U);else if(W.jsonKeys.length>0)Q=U;let D=this.router.credentials?await this.bind(W,"the credentials",()=>this.router.credentials(K)):void 0,S;try{let _=A?`${J}?${A}`:J;S=D?await this.dispatcher.dispatchAs(Z.req,z.method,_,Q,{credentials:D}):await this.dispatcher.dispatchAs(Z.req,z.method,_,Q)}catch(_){throw console.error(`[gemi/mcp] Dispatching "${$}" failed:`,_),new N(`"${$}" failed on the server.`,500)}if(!W.result&&!z.outputSchema)return await DZ($,S);return await this.project(W,K,S)}plan(Z,$){if(!z3.test(Z))throw Error(`McpRouter: "${Z}" is not a valid tool name. A routes key is the tool's name as a model and an MCP client see it, used verbatim \u2014 letters, digits, "_" and "-", at most 64.`);let G=`McpRouter: "${Z}" (${$?.method} ${$?.url})`;if(!($ instanceof Object)||$.__internal_brand!=="McpRoute")throw Error(`McpRouter: "${Z}" is not a route. Declare it with this.fromApiRoute(...).`);let{method:H,url:W,meta:z}=$;if(W.startsWith("/__gemi__"))throw Error(`${G} is a framework route, not an app route.`);let Y=this.dispatcher.flatRoutes[W]?.[H];if(!Y)throw Error(`${G} names no route of this app. Expose a route by the path and verb it is mounted at.`);let V=[...W.matchAll(V3)].map(([,X,O])=>({name:X,modifier:O})),K=z.params??{};for(let X of V)if(!(X.name in K))throw Error(`${G}: the path param "${X.name}" must be bound or declared "input".`);for(let[X,O]of Object.entries(K)){if(!V.some((x)=>x.name===X))throw Error(`${G}: "${X}" in params is not a param of the url.`);if(RZ(O))X3(G,`params.${X}`,O);else EZ(G,`params.${X}`,O)}let J=[],C;if(z.input){if(C=z.input.toJSONSchema(),C.type!=="object"||!C.properties)throw Error(`${G}: input must be an s.object(...), the fields of the request body.`);J=Object.keys(C.properties)}let U=Object.entries(z.files??{}).map(([X,O])=>({name:X,binder:O}));for(let X of U)EZ(G,`files.${X.name}`,X.binder);if(z.result!==void 0&&typeof z.result!=="function")throw Error(`${G}: result must be a function.`);if(z.output!==void 0&&typeof z.output?.safeParse!=="function")throw Error(`${G}: output must be a schema, built with s.`);if(U.length>0&&H==="GET")throw Error(`${G}: a GET has no body to carry a file.`);let Q={},A=(X,O)=>{if(X in Q)throw Error(`${G}: "${X}" is the input key of two params or files, and the model can only send one.`);Q[X]=O},D=new Map,S=new Map;for(let X of V){let O=K[X.name];if(O==="input"){let x=h.string().describe(`The ":${X.name}" segment of the url.`);A(X.name,X.modifier==="?"||X.modifier==="*"?x.optional():x)}else if(RZ(O)){let x=O.as??X.name;A(x,O.input),S.set(X.name,{key:x,param:O})}else D.set(X.name,O)}for(let X of U)if(X.binder==="input")A(X.name,h.string().describe("The id of an attachment the user uploaded or a tool produced (gemi_att_\u2026), whose file is sent as this field."));let _=[...Object.keys(Q),...U.map((X)=>X.name)];for(let X of new Set(_))if(J.includes(X))throw Error(`${G}: "${X}" is both an input field and a param or file, and the model can only send one.`);return{descriptor:Object.freeze({name:Z,description:z.description,method:H,url:W,inputSchema:I3(z.input,C,h.object(Q)),...z.output?{outputSchema:z.output}:{},annotations:Y3(H),tags:Object.freeze([...z.tags??[]]),requiresApproval:z.requiresApproval===!0,...Y.source?{source:Y.source}:{}}),params:V,paramBinders:D,modelParams:S,fileFields:U,jsonKeys:J,...z.result?{result:z.result}:{}}}async fillPath(Z,$){let{input:G,req:H}=$,W=Z.descriptor.url;for(let z of Z.params){let Y=Z.paramBinders.get(z.name),V=Z.modelParams.get(z.name),K;if(Y||V){if(K=V?await this.bind(Z,`the param "${z.name}"`,()=>V.param.bind(G[V.key],H,$),{refusals:!0}):await this.bind(Z,`the param "${z.name}"`,()=>Y(H,$)),K===void 0||K===null||K==="")throw console.error(`[gemi/mcp] The ${V?"resolver":"binder"} for "${z.name}" of "${Z.descriptor.name}" returned ${JSON.stringify(K)}.`),new N(`"${Z.descriptor.name}" failed on the server.`,500)}else K=G[z.name];let J=`:${z.name}${z.modifier}`;if(K===void 0||K===null||K===""){if(z.modifier!=="?"&&z.modifier!=="*")throw new N(`"${z.name}" of "${Z.descriptor.name}" must not be empty.`);W=W.replace(`/${J}`,"");continue}W=W.replace(J,J3(Z.descriptor.name,z,String(K)))}return W}async formData(Z,$,G){let{input:H,ctx:W}=G,z=Z.descriptor.name;if(!W)throw Error(`McpRegistry: "${z}" sends a file, which is resolved through the tool context's attachments, and none was passed to execute().`);let Y=new FormData;for(let[V,K]of Object.entries($))jZ(Y,V,K);for(let V of Z.fileFields){let K=V.binder==="input"?H[V.name]:await this.bind(Z,`the file "${V.name}"`,()=>V.binder(W,G));if(typeof K!=="string"||K==="")throw new N(`"${z}" needs a file for "${V.name}", and this turn has none. Ask the user to attach one.`);Y.append(V.name,await W.attachments.file(K))}return Y}async project(Z,$,G){let{name:H,outputSchema:W}=Z.descriptor;if(G.status<200||G.status>=300)return await DZ(H,G);let z=(U,Q)=>{if(Q===void 0)console.error(`[gemi/mcp] "${H}" ${U}`);else console.error(`[gemi/mcp] "${H}" ${U}`,Q);return new N(`"${H}" failed on the server.`,500)},Y=G.headers.get("Content-Type"),V=await G.text(),K=null;if(V!=="")try{K=JSON.parse(V)}catch{throw z(`answered ${Y??"a body with no content-type"} that is not JSON, and its result or output needs JSON.`)}let J=K;if(Z.result)try{J=await Z.result(K,$)}catch(U){throw z("failed in its result projection:",U)}if(W){let U=W.safeParse(J);if(U.ok===!1)throw z(`answered what its output schema refuses: ${U.errors.join(", ")}`);J=U.value}if(J===void 0)return null;let C=JSON.stringify(J);if(C!==void 0&&C.length>E)return`${C.slice(0,E)}\u2026 [cut at ${E} of ${C.length} characters]`;return J}async bind(Z,$,G,H={}){try{return await G()}catch(W){if(H.refusals&&W instanceof e)throw W;throw console.error(`[gemi/mcp] Binding ${$} of "${Z.descriptor.name}" failed:`,W),new N(`"${Z.descriptor.name}" failed on the server.`,500)}}}function TZ(Z){if(Z?.kind!=="local")throw Error("McpRegistry: only local callers are implemented. A remote caller needs a bearer-token resolver, which v1 does not have \u2014 and until it does there is no remote surface to call from.")}function EZ(Z,$,G){if(G!=="input"&&typeof G!=="function")throw Error(`${Z}: ${$} must be a function or "input".`)}function RZ(Z){return Z instanceof Object&&Z.__internal_brand==="McpModelParam"}function X3(Z,$,G){if(G.as!==void 0&&(typeof G.as!=="string"||G.as===""))throw Error(`${Z}: ${$}.as must be a non-empty string.`);let H=G.input;if(typeof H?.safeParse!=="function"||typeof H.toJSONSchema!=="function")throw Error(`${Z}: ${$}.input must be a schema, built with s.`);if(typeof G.bind!=="function")throw Error(`${Z}: ${$}.bind must be a function.`)}function Y3(Z){switch(Z){case"GET":return Object.freeze({readOnlyHint:!0});case"DELETE":return Object.freeze({destructiveHint:!0});case"PUT":return Object.freeze({idempotentHint:!0});default:return Object.freeze({})}}function I3(Z,$,G){let H=G.toJSONSchema(),W={...$??{},type:"object",properties:{...$?.properties??{},...H.properties},required:[...$?.required??[],...H.required??[]],additionalProperties:!1},z=(Y)=>{if(typeof Y!=="object"||Y===null||Array.isArray(Y))return{ok:!1,errors:["expected an object"]};let V=[],K={};if(Z){let C=Z.safeParse(Y);if(C.ok===!1)V.push(...C.errors);else K={...C.value}}let J=G.safeParse(Y);if(J.ok===!1)V.push(...J.errors);else K={...K,...J.value};return V.length>0?{ok:!1,errors:V}:{ok:!0,value:K}};return{toJSONSchema:()=>W,safeParse:z,parse(Y){let V=z(Y);if(V.ok===!1)throw Error(V.errors.join(", "));return V.value}}}function J3(Z,$,G){let H=$.modifier==="*"||$.modifier==="+"?G.split("/"):[G];if(H.some((W)=>W==="."||W===".."))throw new N(`"${G}" is not a valid value for "${$.name}" of "${Z}".`);return H.map(encodeURIComponent).join("/")}function jZ(Z,$,G){if(G===void 0||G===null)return;if(Array.isArray(G)){for(let H of G)jZ(Z,$,H);return}Z.append($,typeof G==="object"?JSON.stringify(G):String(G))}function U3(Z){let $=new URLSearchParams,G=(H,W)=>{if(W===void 0||W===null)return;if(Array.isArray(W)){for(let z of W)G(H,z);return}$.append(H,typeof W==="object"?JSON.stringify(W):String(W))};for(let[H,W]of Object.entries(Z))G(H,W);return $.toString()}async function DZ(Z,$){if($.status>=200&&$.status<300){let H=$.headers.get("Content-Type");if(H&&!K3.test(H)){let z=(await $.arrayBuffer()).byteLength;return`"${Z}" answered with ${z} bytes of ${H}, which is not shown.`}let W=await $.text();if(W==="")return null;if(W.length>E)return`${W.slice(0,E)}\u2026 [cut at ${E} of ${W.length} characters]`;try{return JSON.parse(W)}catch{return W}}let G=await $.text();if($.status>=400&&$.status<500){let H=G.length>SZ?`${G.slice(0,SZ)}\u2026`:G;throw new N(`"${Z}" was refused with ${$.status}${H?`: ${H}`:"."}`,$.status)}if($.status<500)console.error(`[gemi/mcp] "${Z}" answered ${$.status}${$.headers.get("Location")?` to ${$.headers.get("Location")}`:""}, which a tool call cannot follow.`);throw new N(`"${Z}" failed on the server.`,$.status)}function c(Z,$){let G=Z.get($);if(!G)throw Error(`Missing "${$}" configuration. Route dispatching needs a root router \u2014 declare it in "app/config/route.ts".`);return G}class B3 extends I{register(){this.app.singleton(R,()=>new R(c(this.app.config,"route.api"))),this.app.singleton(y,()=>new y(c(this.app.config,"route.view"))),this.app.singleton(u,()=>new u({api:this.app.make(R),view:this.app.make(y)},{api:c(this.app.config,"route.api"),view:c(this.app.config,"route.view"),domains:this.app.config.get("route.domains")})),this.app.singleton(M,()=>{let Z=this.app.config.get("route.mcp");if(!Z?.router)throw Error('No MCP router is configured. Declare one as `mcp: { router }` in "app/config/route.ts".');return new M(new Z.router,this.app.make(R))})}boot(){if(this.app.make(R),this.app.make(y),this.app.make(u),this.app.config.get("route.mcp"))this.app.make(M)}}
|
|
3
|
+
export{F as hb,gZ as ib,T3 as jb,cZ as kb,uZ as lb,lZ as mb,T as nb,i3 as ob,qZ as pb,iZ as qb,sZ as rb,L as sb,rZ as tb,aZ as ub,eZ as vb,Z3 as wb,M4 as xb,AZ as yb,$3 as zb,G3 as Ab,l4 as Bb,FZ as Cb,W3 as Db,a4 as Eb,LZ as Fb,H3 as Gb,N as Hb,M as Ib,B3 as Jb};
|
|
4
|
+
|
|
5
|
+
//# debugId=E5CD51F5C6B715EF64756E2164756E21
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../services/cron/Scheduler.ts", "../services/queue/QueueServiceProvider.ts", "../auth/AuthServiceProvider.ts", "../services/cron/ScheduleServiceProvider.ts", "../services/features/sources/DatabaseFeatureFlagSource.ts", "../services/features/config.ts", "../services/features/FeaturesServiceProvider.ts", "../services/email/MailServiceProvider.ts", "../services/events/listenerJob.ts", "../services/events/EventManager.ts", "../services/events/EventServiceProvider.ts", "../services/file-storage/FilesystemServiceProvider.ts", "../services/image-optimization/ImageServiceProvider.ts", "../services/kernel-id/KernelIdServiceProvider.ts", "../services/logging/config.ts", "../services/logging/LogServiceProvider.ts", "../services/middleware/MiddlewareServiceProvider.ts", "../services/rate-limiter/config.ts", "../services/rate-limiter/RateLimiterServiceProvider.ts", "../services/redis/config.ts", "../services/redis/RedisServiceProvider.ts", "../services/mcp/McpRegistry.ts", "../services/router/RouteServiceProvider.ts"],
|
|
3
|
+
"sources": ["../services/cron/Scheduler.ts", "../services/queue/QueueServiceProvider.ts", "../services/change-feed/ChangeFeedServiceProvider.ts", "../auth/AuthServiceProvider.ts", "../services/cron/ScheduleServiceProvider.ts", "../services/features/sources/DatabaseFeatureFlagSource.ts", "../services/features/config.ts", "../services/features/FeaturesServiceProvider.ts", "../services/email/MailServiceProvider.ts", "../services/events/listenerJob.ts", "../services/events/EventManager.ts", "../services/events/EventServiceProvider.ts", "../services/file-storage/FilesystemServiceProvider.ts", "../services/image-optimization/ImageServiceProvider.ts", "../services/kernel-id/KernelIdServiceProvider.ts", "../services/logging/config.ts", "../services/logging/LogServiceProvider.ts", "../services/middleware/MiddlewareServiceProvider.ts", "../services/rate-limiter/config.ts", "../services/rate-limiter/RateLimiterServiceProvider.ts", "../services/redis/config.ts", "../services/redis/RedisServiceProvider.ts", "../services/mcp/McpRegistry.ts", "../services/router/RouteServiceProvider.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"import { type HeldLock, LockManager } from \"../lock/LockManager\";\nimport { LockLostError } from \"../lock/LockStore\";\nimport { MemoryLockStore } from \"../lock/MemoryLockStore\";\nimport type { CronJob } from \"./CronJob\";\nimport type { ScheduleConfig } from \"./config\";\n\n// The handle the in-process `Bun.cron(schedule, callback)` overload returns\n// (has `.stop()`). `ReturnType<typeof Bun.cron>` can't be used — it resolves to\n// the module-path overload's `Promise<void>` instead.\ntype BunCronHandle = Bun.CronJob;\n\n// Runs a tick inside whatever async context the framework needs, so a job body\n// can resolve services exactly like a request handler can.\nexport type ScheduleRunner = <T>(cb: () => T | Promise<T>) => Promise<T> | T;\n\ndeclare global {\n // Registered cron jobs keyed by name, kept on `globalThis` so it survives\n // `bun --hot` reloads. Re-registering a name stops the previous schedule\n // first, so a reload *updates* the job in place instead of stacking a\n // duplicate. (Bun also stops all in-process cron jobs before re-evaluating on\n // hot reload; this makes the intent explicit and covers any re-construction.)\n var __gemiCronJobs: Map<string, BunCronHandle> | undefined;\n}\n\n/**\n * One tick of one job: the gate, then the three hooks in order.\n *\n * Named and exported rather than left inline in `start()` because otherwise the\n * only way to reach it is to wait — the tightest schedule `Bun.cron` accepts is\n * once a minute, which is not a thing a test can honestly sit through. What a\n * caller gives up by calling this directly is Bun's clock; what it keeps is\n * everything the scheduler puts between the clock and the job, which is the\n * part that has behaviour.\n *\n * The three hooks each catch and log, so one throwing does not stop the next —\n * `onComplete` in particular runs whether `callback` returned or threw, closer\n * to a `finally` than to a success handler.\n */\nexport async function runTick(job: CronJob, options: TickOptions = {}): Promise<void> {\n try {\n // Plain truthiness, not `!== false`. The sloppy gate an app writes is\n // `if (!isProduction) return false;`, which returns `undefined` on the path\n // that was meant to run — and between the two readings of that, the one\n // that skips a recurring tick is recoverable and the one that mails a\n // customer from a developer's laptop is not. The declared return type makes\n // that method a compile error anyway, so the type system catches it while\n // it is still being written and the runtime stays on the safe side.\n if (!(await job.shouldRun.call(job))) {\n return;\n }\n } catch (error) {\n // Fail closed, for the same reason. A gate that throws has told us nothing\n // about whether the job may run, and the framework already refuses to\n // schedule a job whose name or expression it cannot read rather than run\n // something half-declared. The throw is logged, so this is loud; the missed\n // tick comes back on the next one.\n console.error(\n `Error evaluating shouldRun for cron job ${job.name}:`,\n error,\n );\n return;\n }\n\n const overlap = overlapLease(job.withoutOverlapping);\n if (!job.onOneServer && overlap === undefined) {\n await runHooks(job);\n return;\n }\n\n // Fail closed, like the gate: a tick that cannot find out whether it may\n // run is skipped, and the next one tries again.\n let locks: LockManager;\n try {\n locks = options.locks?.() ?? processLocks();\n if (job.onOneServer) {\n const minute = Math.floor((options.now ?? Date.now()) / 60_000);\n if (!(await locks.store.advance(`gemi:cron-tick:${job.name}`, minute))) return;\n }\n } catch (error) {\n console.error(`Could not claim the tick of cron job ${job.name}; skipped it:`, error);\n return;\n }\n if (overlap === undefined) {\n await runHooks(job);\n return;\n }\n\n try {\n await locks.run(`gemi:cron:${job.name}`, { ttl: overlap }, (lock) => runHooks(job, lock));\n } catch (error) {\n if (error instanceof LockLostError) {\n console.error(\n `Cron job ${job.name} lost its withoutOverlapping lock while running: ` +\n `another tick may have started alongside it.`,\n error,\n );\n } else {\n console.error(`Could not take the withoutOverlapping lock of cron job ${job.name}:`, error);\n }\n }\n}\n\n/** What `runTick` needs beyond the job. */\nexport interface TickOptions {\n /**\n * The locks `withoutOverlapping` and `onOneServer` take. The scheduler\n * passes the queue's, so they hold across instances sharing its storage;\n * without one they hold within this process only.\n */\n locks?: () => LockManager;\n /** The tick's time, for `onOneServer`. Default now. */\n now?: number;\n}\n\nconst DEFAULT_OVERLAP_LEASE = 5 * 60_000;\n\nfunction overlapLease(option: CronJob[\"withoutOverlapping\"]): number | undefined {\n if (!option) return undefined;\n if (option === true) return DEFAULT_OVERLAP_LEASE;\n return option.expiresAfter ?? DEFAULT_OVERLAP_LEASE;\n}\n\nlet fallbackLocks: LockManager | undefined;\nfunction processLocks() {\n fallbackLocks ??= new LockManager(new MemoryLockStore());\n return fallbackLocks;\n}\n\nasync function runHooks(job: CronJob, lock?: HeldLock) {\n try {\n await job.onTick.call(job);\n } catch (error) {\n console.error(`Error executing cron job ${job.name}:`, error);\n }\n try {\n await job.callback.call(job, lock);\n } catch (error) {\n console.error(`Error in cron job ${job.name}:`, error);\n }\n try {\n await job.onComplete.call(job);\n } catch (error) {\n console.error(`Error completing cron job ${job.name}:`, error);\n }\n}\n\n/** A tick `drain` could not wait out. */\nexport interface UnfinishedTick {\n /** The job's `name`. */\n name: string;\n /**\n * When this tick started. A job whose tick outlasts its own interval has\n * two ticks running at once, and the name alone would list it twice with\n * nothing to tell the two apart.\n */\n startedAt: Date;\n}\n\n/** What `drain` could not wait out. */\nexport interface ScheduleDrainResult {\n /**\n * Ticks still running when the timeout elapsed. They keep running — a tick\n * has no way to be told to stop — until they finish or the process exits.\n */\n unfinished: UnfinishedTick[];\n}\n\nexport class Scheduler {\n static token = \"scheduler\";\n\n private handles: BunCronHandle[] = [];\n private resolved: Array<new () => CronJob>;\n\n /**\n * Every tick this scheduler has started and not seen settle, keyed by an\n * object of its own so two overlapping ticks of one job are two entries.\n * Without this there is nothing for a shutdown to wait on, and a tick the\n * process exits under is cut off halfway through its `callback`.\n */\n private readonly inFlight = new Map<object, { tick: UnfinishedTick; done: Promise<unknown> }>();\n\n /**\n * Set by `stop()`, cleared by `start()`. `handle.stop()` is what keeps Bun\n * from firing again, and this is the second lock on the same door: a\n * callback Bun had already dispatched when `stop()` ran, or a handle some\n * other scheduler left in the hot-reload registry, still finds nothing to\n * start once the drain has begun.\n */\n private stopped = false;\n\n private locks: (() => LockManager) | undefined;\n\n constructor(config: Required<ScheduleConfig>) {\n this.resolved = config.jobs;\n }\n\n /**\n * The jobs this scheduler will run, or ran — declared in the config slice or\n * discovered under `app/cron`, whichever the slice asked for.\n *\n * #323 turned an application's schedule from an array it could import into a\n * directory it cannot, and an app that had a test walking that array —\n * asserting every report has an owner, that no two share an expression — was\n * about to lose it. This is where that test goes instead, and it is the\n * better question anyway: the config slice says what was asked for, this says\n * what the scheduler took.\n *\n * It lists what `start()` iterates, not what `Bun.cron` accepted. A job with\n * no name, no expression, or a name another job already claimed is logged and\n * skipped, and still appears here — a test walking this should see the\n * problem rather than have it tidied away.\n *\n * A copy, so that walk cannot edit the set the scheduler is running from.\n */\n get jobs(): ReadonlyArray<new () => CronJob> {\n return [...this.resolved];\n }\n\n /**\n * Replaces the job set, for the provider to hand over what it found under\n * `app/cron` before it calls `start()`.\n *\n * The scheduler is constructed in `register()`, where reading a directory and\n * importing what is in it is not allowed to happen — that phase is\n * synchronous and resolves nothing. So it is built from whatever the slice\n * declared and corrected in `boot()`, one line before it starts. Calling this\n * after `start()` changes what `jobs` reports and nothing else; the schedules\n * are already registered.\n */\n useJobs(jobs: Array<new () => CronJob>) {\n this.resolved = jobs;\n }\n\n /**\n * Where `withoutOverlapping` and `onOneServer` take their locks. The\n * provider hands over the queue's, resolved only when a tick needs them.\n */\n useLocks(locks: () => LockManager) {\n this.locks = locks;\n }\n\n /**\n * Schedules every configured job. Called from `ScheduleServiceProvider.boot()`\n * — never from the constructor — so a job body may resolve any service the\n * container holds by the time it first ticks. That ordering is what replaced\n * the old `kernel.waitForBoot()` handshake.\n */\n start(run: ScheduleRunner = (cb) => cb()) {\n this.stopped = false;\n if (this.resolved.length === 0) {\n return;\n }\n\n const registry = (globalThis.__gemiCronJobs ??= new Map());\n\n // Names claimed during *this* pass, which is a different question from what\n // `registry` holds. `registry` spans hot reloads and is supposed to already\n // contain the previous reload's handle under the same name; this starts\n // empty every time and is how a name claimed twice in one schedule is told\n // apart from the same job coming back after an edit.\n const claimed = new Map<string, string>();\n\n for (const Job of this.resolved) {\n const job = new Job();\n if (!job.name) {\n // Named by its class, because under discovery nobody wrote this job\n // down anywhere and `JSON.stringify(job)` on a nameless one prints\n // `{}`. The overwhelmingly common cause is the second sentence: a\n // shared base class sitting in the walked directory. `abstract` is\n // erased before this code runs, so there is nothing here that could\n // tell a base class from a job — only the message can.\n console.error(\n `Cron job must have a name — ${Job.name || \"an anonymous class\"} ` +\n `has none, so it is not scheduled. If this is a base class that ` +\n `other jobs extend, move it out of the discovered directory: ` +\n `\\`abstract\\` does not survive to runtime, so a class under ` +\n `app/cron is indistinguishable from a job.`,\n );\n continue;\n }\n if (!job.cron) {\n console.error(`Cron job must have an expression. Job: ${job.name}`);\n continue;\n }\n const taken = claimed.get(job.name);\n if (taken !== undefined) {\n // Refusing the second one, rather than letting it through to the\n // `registry.get(...).stop()` below — which is what used to happen, and\n // it stopped the *first* job's schedule while leaving both classes in\n // `jobs`. That is the failure #323 exists to remove wearing a disguise:\n // a report that silently stops arriving, and a test walking `jobs` that\n // sees two healthy entries and passes.\n console.error(\n `Two cron jobs claim the name \"${job.name}\" — ${taken} is ` +\n `scheduled and ${Job.name || \"an anonymous class\"} is not. A name ` +\n `is the scheduler's key, so only one job can hold it. Rename one.`,\n );\n continue;\n }\n claimed.set(job.name, Job.name || \"an anonymous class\");\n\n // Update in place: stop any schedule previously registered under this name\n // before re-registering, so a hot reload replaces rather than duplicates.\n registry.get(job.name)?.stop();\n\n // Inside `run`, gate included: a `shouldRun` that resolves a service to\n // decide is the ordinary case, and evaluating it out here would leave it\n // without the application context every other line of the tick has.\n const handle = Bun.cron(job.cron, () => {\n if (this.stopped) return;\n const locks = this.locks;\n return this.track(job.name, () => run(() => runTick(job, { locks })));\n });\n\n registry.set(job.name, handle);\n this.handles.push(handle);\n }\n }\n\n /**\n * Stops every schedule, so no new tick starts. A tick already running is\n * left alone — `drain` is how to wait for it.\n */\n stop() {\n this.stopped = true;\n for (const handle of this.handles) {\n handle.stop();\n }\n this.handles = [];\n }\n\n /**\n * Stops every schedule, waits up to `timeoutMs` for the ticks already\n * running, and reports the ones that did not finish.\n *\n * Stopping comes first, so a tick that falls due while this waits does not\n * start: a drain that kept the clock running would never be sure it had\n * waited for everything. Nothing is cancelled — a job has no way to be told\n * — so an unfinished tick keeps running after this resolves, until it ends\n * or the process does.\n *\n * A later `start()` schedules again.\n */\n async drain(timeoutMs = Infinity): Promise<ScheduleDrainResult> {\n this.stop();\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 await Promise.race([\n Promise.allSettled([...this.inFlight.values()].map((e) => e.done)),\n deadline,\n ]);\n clearTimeout(timer);\n\n return {\n unfinished: [...this.inFlight.values()].map((e) => ({ ...e.tick })),\n };\n }\n\n /** How many ticks this scheduler is running right now. */\n get running(): number {\n return this.inFlight.size;\n }\n\n private track(name: string, tick: () => unknown): Promise<unknown> {\n const key = {};\n // `Promise.resolve().then(...)` rather than calling `tick` directly, so a\n // runner that throws synchronously lands in the same promise as one that\n // rejects, and the entry below is removed either way.\n const done = Promise.resolve()\n .then(tick)\n .finally(() => this.inFlight.delete(key));\n this.inFlight.set(key, { tick: { name, startedAt: new Date() }, done });\n return done;\n }\n}\n",
|
|
6
6
|
"import { isMainThread } from \"node:worker_threads\";\n\nimport type { Application } from \"../../foundation/Application\";\nimport { ServiceProvider } from \"../../support/ServiceProvider\";\nimport { discoverJobs } from \"../discovery\";\nimport { withDefaults } from \"../../support/withDefaults\";\nimport { queueConfigDefaults, type QueueConfig } from \"./config\";\nimport { LockManager } from \"../lock/LockManager\";\nimport { MemoryQueueDriver } from \"./MemoryQueueDriver\";\nimport {\n claimingTurnedOff,\n claimsInThisProcess,\n isQueueWorker,\n QueueManager,\n} from \"./QueueManager\";\n\n/**\n * How far inside the provider deadline the drain stops, so there is time left\n * to write the list of jobs it abandoned before `Application` stops waiting\n * for this provider and the process exits. A tenth of the budget, capped, so\n * that a deadline of a few milliseconds still spends most of itself waiting\n * rather than handing the whole of it to the margin.\n */\nconst REPORT_MARGIN_MS = 100;\n\nconst reportMargin = (timeoutMs: number) =>\n Math.min(REPORT_MARGIN_MS, Math.floor(timeoutMs / 10));\n\nexport class QueueServiceProvider extends ServiceProvider {\n register() {\n this.app.singleton(\n QueueManager,\n () =>\n new QueueManager(this.app.config.get<QueueConfig>(\"queue\", {}), {\n application: this.app,\n }),\n );\n // The `Lock` facade's binding: the queue's locks, over its storage.\n this.app.singleton(LockManager, () => this.app.make(QueueManager).locks);\n }\n\n /**\n * Fills in the job registry when the app did not declare one.\n *\n * ### Why the raw slice and not the manager's config\n *\n * The decision here is whether the app said anything, and by the time the\n * manager holds a config it can no longer tell: `withDefaults` treats an\n * absent key and an `undefined` one alike and substitutes the default `[]`,\n * which is the same value an app writes when it means \"no jobs, and I mean\n * it\". Reading the slice before defaults are applied is the only place the\n * difference still exists.\n *\n * So: `jobs` present, including `jobs: []`, is used verbatim and no directory\n * is read. Absent or `undefined`, the classes under `jobsDir` are.\n *\n * ### Why phase two\n *\n * Discovery imports every file it walks, which is asynchronous, and\n * `register()` is not. It also has to happen before the first dispatch rather\n * than before the first tick — there is no equivalent of the scheduler's\n * `start()` to hang it off, so the registry has to be complete by the end of\n * boot.\n */\n async boot() {\n const slice = this.app.config.get<QueueConfig>(\"queue\", {});\n if (slice.jobs !== undefined) return;\n\n const { jobsDir } = withDefaults(queueConfigDefaults(), slice);\n this.app.make(QueueManager).useJobs(await discoverJobs(jobsDir));\n }\n\n /**\n * Stops claiming and waits for the jobs this process is running, on the way\n * out of a server told to stop. Runs after the request drain, and before the\n * database provider's, so the jobs still have their connection.\n *\n * A job still running at the deadline is abandoned with the process. With\n * the memory driver that job is lost; with one that outlives the process it\n * is claimed again elsewhere once its lease runs out. With such a driver\n * claiming already stopped when the signal arrived, so the jobs here are\n * only the ones that were running then; the memory driver kept claiming\n * until now, so a job dispatched by a request still draining ran here too.\n *\n * The drain is bounded by a little less than what is left of the shared\n * provider deadline, so that this is the thing that names the jobs it gave\n * up on. Draining without a timeout of its own read better but could not\n * work: `drain()` then resolves only once every job has finished, by which\n * point nothing is unfinished and the list below is always empty — and the\n * operator who needed it got `Application`'s generic \"did not finish within\n * the provider shutdown deadline\" instead, which names no job at all. *\n * A drain that left jobs running resolves `{ abandoned: true }`, so\n * `Application` reports this provider in `timedOut` and the process exits 1:\n * the jobs are named in the log, and the exit code shows the stop was not\n * clean (#580).\n */\n async shutdown(options?: { timeoutMs: number }) {\n // Resolving the manager now would build a driver only to stop it.\n if (!this.app.resolved(QueueManager)) return;\n const queue = this.app.make(QueueManager);\n if (queue.running > 0) {\n console.log(\n `[gemi] Shutting down: waiting for ${queue.running} running queued job(s).`,\n );\n }\n const budget = options?.timeoutMs;\n const { unfinished } = await queue.drain(\n budget === undefined ? Infinity : Math.max(0, budget - reportMargin(budget)),\n );\n if (unfinished.length > 0) {\n console.error(\n `[gemi] Queued jobs still running at shutdown: ` +\n unfinished.map((job) => `${job.name} (${job.id})`).join(\", \"),\n );\n return { abandoned: true };\n }\n }\n}\n\n/**\n * Starts claiming once the application has booted, in a production server\n * whose driver keeps jobs outside the process, rather than at its first\n * dispatch. That is the recovery half of a durable driver: the jobs a previous\n * process left behind are claimed by whichever one is up, and a replica that\n * serves no dispatch of its own would otherwise never look.\n *\n * Called by the kernel after every provider has booted, not from `boot()`\n * above: queued listeners are registered as jobs by the event provider, which\n * boots after this one, and a job claimed before its name is registered is\n * dead-lettered for it.\n *\n * Not the memory driver, which never has anything left behind. Not a console\n * command or a migration, which boot the same providers and would claim jobs\n * only to exit under them — `ROOT_DIR` is set by `Server.start()` and the\n * `gemi queue:work` worker alone. Not a server started with\n * `GEMI_QUEUE_CLAIM=off`, which leaves its jobs to workers. Not\n * a `worker` job's thread, which clones the application. And not development,\n * where `bun --hot` boots a fresh application on every save: there the queue\n * starts at the first dispatch, and a reload hands a running loop over — see\n * `takeOverDevQueue`.\n */\nexport function startClaimingIfServing(application: Application) {\n warnAboutClaimSwitch(application);\n if (process.env.NODE_ENV !== \"production\") return takeOverDevQueue(application);\n if (configuresMemory(application)) return;\n if (!claimsInThisProcess()) return;\n if (!application.bound(QueueManager)) return;\n application.make(QueueManager).start();\n}\n\nfunction configuresMemory(application: Application) {\n const driver = application.config.get<QueueConfig>(\"queue\", {}).driver ?? \"memory\";\n return driver === \"memory\" || driver instanceof MemoryQueueDriver;\n}\n\n/**\n * Whether the queue's jobs live only in this process, for the warning below.\n * The config says so for `\"memory\"` or a `MemoryQueueDriver`, but a factory\n * only says once it has run, so for one the manager is built — which is the\n * same driver `durable` and `push()` go by. A factory that throws here is\n * left to throw at the first dispatch, as it would have without the switch,\n * rather than failing the boot over a warning.\n */\nfunction keepsJobsInProcess(application: Application) {\n if (configuresMemory(application)) return true;\n const driver = application.config.get<QueueConfig>(\"queue\", {}).driver;\n if (typeof driver !== \"function\" || !application.bound(QueueManager)) return false;\n try {\n return !application.make(QueueManager).durable;\n } catch {\n return false;\n }\n}\n\n/**\n * Says so at boot when a server's `GEMI_QUEUE_CLAIM` will not do what it\n * reads as. Only in a server's main thread: a console command or a `worker`\n * job's thread shares the server's environment and claims nothing anyway.\n *\n * - `off` with the memory driver is ignored, because a memory queue's jobs\n * exist only in the process that dispatched them. Honoured, it would leave\n * every dispatch waiting in a `Map` that no worker can see, for good.\n * - Any value but `off` or `on` claims. `false` or `0` read as \"off\" and are\n * not it, and a web process that quietly keeps claiming is the failure a\n * deploy that set one would never notice.\n */\nfunction warnAboutClaimSwitch(application: Application) {\n const value = process.env.GEMI_QUEUE_CLAIM;\n if (value === undefined || isQueueWorker()) return;\n if (process.env.ROOT_DIR === undefined || !isMainThread) return;\n if (claimingTurnedOff()) {\n if (!keepsJobsInProcess(application)) return;\n console.warn(\n `[gemi] GEMI_QUEUE_CLAIM=off is ignored: the memory queue driver keeps ` +\n `jobs in the process that dispatched them, so no worker could run ` +\n `them. This server runs its own jobs. Use a driver other processes ` +\n `can read, such as \"database\", to hand jobs to \\`gemi queue:work\\`.`,\n );\n } else if (value.trim().toLowerCase() !== \"on\") {\n console.warn(\n `[gemi] GEMI_QUEUE_CLAIM=\"${value}\" is not \"off\" or \"on\", so this ` +\n `server claims jobs. Set it to \"off\" to leave them to \\`gemi queue:work\\`.`,\n );\n }\n}\n\n/**\n * Under `gemi dev`, stops the loop the previous application started over a\n * shared driver, and starts this application's in its place.\n *\n * A `bun --hot` reload boots a new application in the same process, and the\n * old application's loop — started lazily by a dispatch — is a live closure\n * that nothing stopped. Over the database driver it went on claiming rows\n * from the table and running them with the code from before the save, and\n * after a few saves several generations of it were doing so side by side.\n *\n * Stopped rather than drained: `stop()` claims nothing more and returns, and\n * a job the old loop is running finishes on its old code and reports as\n * usual, where waiting for it would hold up the reload behind a job. Its row\n * stays claimed until then, so the new loop cannot take it twice.\n *\n * The new loop is started only when an old one was running, which keeps the\n * rule that development claims nothing until something is dispatched, while\n * a job waiting out a retry when the file was saved still gets its retry —\n * on the new code, which is the point of the reload. A memory queue is never\n * recorded, so it is never handed over: only its own application feeds it,\n * and stopping it would drop whatever was still waiting there.\n *\n * Runs from every boot. `waitForBoot` twice on one application stops its own\n * loop and starts it again at once, which changes nothing. The `bound` guard\n * is for an application built without the queue provider, which still stops\n * the stale loop.\n */\nfunction takeOverDevQueue(application: Application) {\n const previous = globalThis.__gemiDevQueue;\n if (!previous) return;\n globalThis.__gemiDevQueue = undefined;\n void previous.stop();\n if (application.bound(QueueManager)) application.make(QueueManager).start();\n}\n",
|
|
7
|
+
"import { ServiceProvider } from \"../../support/ServiceProvider\";\nimport type { ChangeFeedConfig } from \"./config\";\nimport { ChangeFeedManager } from \"./ChangeFeedManager\";\n\nexport class ChangeFeedServiceProvider extends ServiceProvider {\n register() {\n this.app.singleton(\n ChangeFeedManager,\n () =>\n new ChangeFeedManager(this.app.config.get<ChangeFeedConfig>(\"changeFeed\", {}), {\n application: this.app,\n }),\n );\n }\n\n /**\n * Ends every subscription and closes the `LISTEN` connection. Streams have\n * already ended by then: each stops at its next keepalive once the server\n * starts shutting down, so the request drain does not wait on them.\n */\n async shutdown() {\n if (!this.app.resolved(ChangeFeedManager)) return;\n await this.app.make(ChangeFeedManager).close();\n }\n}\n",
|
|
7
8
|
"import { ServiceProvider } from \"../support/ServiceProvider\";\nimport type { AuthConfig } from \"./config\";\nimport { AuthManager } from \"./AuthManager\";\n\nexport class AuthServiceProvider extends ServiceProvider {\n register() {\n this.app.singleton(\n AuthManager,\n () => new AuthManager(this.app.config.get<AuthConfig>(\"auth\", {})),\n );\n }\n}\n",
|
|
8
9
|
"import { kernelContext } from \"../../kernel/context\";\nimport { LockManager } from \"../lock/LockManager\";\nimport { ServiceProvider } from \"../../support/ServiceProvider\";\nimport { withDefaults } from \"../../support/withDefaults\";\nimport { discoverCronJobs } from \"../discovery\";\nimport { scheduleConfigDefaults, type ScheduleConfig } from \"./config\";\nimport { Scheduler } from \"./Scheduler\";\n\n/**\n * How far inside the provider deadline the drain stops, so there is time left\n * to name the ticks it abandoned before `Application` stops waiting for this\n * provider. The same margin as the queue's drain, for the same reason: a\n * tenth of the budget, capped, so a deadline of a few milliseconds still\n * spends most of itself waiting.\n */\nconst REPORT_MARGIN_MS = 100;\n\nconst reportMargin = (timeoutMs: number) => Math.min(REPORT_MARGIN_MS, Math.floor(timeoutMs / 10));\n\nexport class ScheduleServiceProvider extends ServiceProvider {\n register() {\n this.app.singleton(\n Scheduler,\n () =>\n new Scheduler(\n withDefaults(\n scheduleConfigDefaults(),\n this.app.config.get<ScheduleConfig>(\"schedule\", {}),\n ),\n ),\n );\n }\n\n /**\n * Resolves the schedule, then starts it.\n *\n * Cron jobs are scheduled in phase two, not in `register()`: a job body is\n * free to resolve any service, and by the time `boot()` runs every provider\n * has registered. Each tick re-enters the application context so the job sees\n * the same container a request handler would.\n *\n * ### Why the raw slice and not the scheduler's config\n *\n * The decision here is whether the app said anything, and by the time the\n * scheduler holds a config it can no longer tell: `withDefaults` treats an\n * absent key and an `undefined` one alike and substitutes the default `[]`,\n * which is the same value an app writes when it means \"nothing scheduled, and\n * I mean it\". Reading the slice before defaults are applied is the only place\n * that difference still exists.\n *\n * So: `jobs` present, including `jobs: []`, is used verbatim and no directory\n * is read. Absent or `undefined` — which includes an app with no `schedule`\n * slice at all — the classes under `jobsDir` are, which is the case #323 is\n * about: a cron job that is written and never listed fires never, and nothing\n * downstream is waiting to notice.\n */\n async boot() {\n const scheduler = this.app.make(Scheduler);\n const slice = this.app.config.get<ScheduleConfig>(\"schedule\", {});\n\n if (slice.jobs === undefined) {\n const { jobsDir } = withDefaults(scheduleConfigDefaults(), slice);\n scheduler.useJobs(await discoverCronJobs(jobsDir));\n }\n\n // Resolved, listed, and not started.\n //\n // `gemi run` sets this on the process it spawns. Booting the application is\n // how a console command reaches the container, and starting the schedule is\n // a side effect of that which nobody asked for: a `gemi run backfill` that\n // takes four minutes would otherwise fire the application's whole cron\n // schedule in a process no operator is watching, and `Bun.cron` handles hold\n // the loop open besides.\n //\n // Deliberately above `start` and below `useJobs`, so `app(Scheduler).jobs`\n // still answers honestly — a command that wants to fire a tick by hand can,\n // and a test asking what this application schedules gets the same answer\n // either way.\n //\n // An environment variable rather than a config field, because this is a\n // property of *this process* and not of the application: the same variable\n // is how a deploy runs one cron dyno beside several web ones, and a config\n // slice would have to know which command started it.\n if (process.env.GEMI_NO_SCHEDULE === \"1\") return;\n\n const app = this.app;\n // The queue's locks, so `withoutOverlapping` and `onOneServer` hold across\n // the instances sharing its storage. Resolved per tick that asks.\n if (app.bound(LockManager)) scheduler.useLocks(() => app.make(LockManager));\n scheduler.start((cb) => kernelContext.run(app, cb));\n }\n\n /**\n * Stops the schedule and waits for the ticks already running, on the way out\n * of a server told to stop.\n *\n * Stopping first is the point. Without it `Bun.cron` kept firing for the\n * whole drain window, starting ticks the process was about to exit under,\n * and a tick already running when the process exited was cut off halfway\n * through its `callback` — on a platform that recycles replicas on every\n * release, that is every release.\n *\n * The wait is bounded a little inside what is left of the shared provider\n * deadline, like the queue's, so that this is what names the ticks it gave\n * up on rather than `Application`'s generic \"did not finish within the\n * provider shutdown deadline\", which names no job at all. *\n * Ticks left running resolve `{ abandoned: true }`, so `Application`\n * reports this provider in `timedOut` and the process exits 1, as it does\n * for a queued job cut off at the deadline (#580).\n */\n async shutdown(options?: { timeoutMs: number }) {\n // `boot()` always resolves it; this is for an application that shuts down\n // without having booted, where building a scheduler would be pointless.\n if (!this.app.resolved(Scheduler)) return;\n const scheduler = this.app.make(Scheduler);\n if (scheduler.running > 0) {\n console.log(`[gemi] Shutting down: waiting for ${scheduler.running} running cron tick(s).`);\n }\n const budget = options?.timeoutMs;\n const { unfinished } = await scheduler.drain(\n budget === undefined ? Infinity : Math.max(0, budget - reportMargin(budget)),\n );\n if (unfinished.length > 0) {\n console.error(\n `[gemi] Cron jobs still running at shutdown: ` +\n unfinished\n .map((tick) => `${tick.name} (started ${tick.startedAt.toISOString()})`)\n .join(\", \"),\n );\n return { abandoned: true };\n }\n }\n}\n",
|
|
9
10
|
"import * as registry from \"../../../orm/registry\";\nimport {\n FeatureFlagSource,\n FeatureModelMissingError,\n type FeatureWriteMeta,\n type FeatureWriteResult,\n} from \"./FeatureFlagSource\";\n\n/**\n * Reads the application's `FeatureFlag` table — one row per feature, carrying\n * one meaningful column.\n *\n * The model is resolved from the ORM registry **by name, at call time**, the way\n * `auth/UserProvider` resolves `User`. The framework cannot import a class the\n * application generates, and resolving lazily also means constructing this\n * source does not require the app's models to have been imported yet — only\n * loading does.\n */\nexport class DatabaseFeatureFlagSource extends FeatureFlagSource {\n constructor(readonly modelName: string = \"FeatureFlag\") {\n super();\n }\n\n /** Whether the application has registered the model at all. */\n get available(): boolean {\n return registry.has(this.modelName);\n }\n\n async load(): Promise<Record<string, unknown>[]> {\n if (!this.available) {\n throw new FeatureModelMissingError(this.modelName);\n }\n\n const model = registry.get<any>(this.modelName);\n\n // `asSystem` for the same reason auth uses it: features are read before\n // there is a user — on an anonymous page load, in a cron tick — and a policy\n // that denies by default would turn \"nobody is signed in\" into \"no rows\",\n // which silently switches every feature off for logged-out traffic.\n return await model.asSystem(() => model.findMany());\n }\n\n /**\n * Upserts the row for `key`.\n *\n * `upsert`, not `update`: a feature that has been deployed but never switched\n * on has no row at all, and that is exactly the state an admin screen exists\n * to change.\n *\n * The actor goes into an `updatedBy` column **when the model has one**, and is\n * otherwise not recorded. The column is optional so that adding `Features.set`\n * did not become a migration every app owes. When it exists it is always\n * written — `null` for a write with no actor — because leaving the previous\n * value in place would credit the last named person with a change they did\n * not make.\n */\n async write(key: string, active: boolean, meta: FeatureWriteMeta): Promise<FeatureWriteResult> {\n if (!this.available) {\n throw new FeatureModelMissingError(this.modelName);\n }\n\n const model = registry.get<any>(this.modelName);\n const actorRecorded = this.hasActorColumn(model);\n const data: Record<string, unknown> = { active };\n if (actorRecorded) data.updatedBy = meta.actor;\n\n // `asSystem` for the same reason `load()` uses it: the table is global\n // configuration, and a policy that denied the write would leave the admin\n // with an error about tenancy on a row that has none.\n await model.asSystem(() =>\n model.upsert({\n where: { key },\n create: { key, ...data },\n update: data,\n }),\n );\n\n return { actorRecorded };\n }\n\n private hasActorColumn(model: any): boolean {\n const field = model?.$schema?.fields?.updatedBy;\n return field !== undefined && field.type === \"String\";\n }\n}\n",
|
|
@@ -26,7 +27,7 @@
|
|
|
26
27
|
"import type { ToolContext } from \"../../ai/Agent\";\nimport { ToolError } from \"../../ai/redact\";\nimport { s, type AnySchema, type JSONSchema, type Schema } from \"../../ai/Schema\";\nimport type { RouteSource } from \"../../http/ApiRouter\";\nimport type { HttpRequest } from \"../../http/HttpRequest\";\nimport type {\n McpFileBinder,\n McpMethod,\n McpModelParam,\n McpParamBinder,\n McpResultProjection,\n McpRouteDeclaration,\n McpRouter,\n} from \"../../http/McpRouter\";\nimport type { ApiRouteDispatcher, DispatchCredentials } from \"../router/ApiRouteDispatcher\";\n\n/**\n * Who a tool call runs as. Always an argument to the registry, never read from\n * ambient state by it.\n *\n * `local` is an agent running in this server, and `req` is the request that\n * started its run — read by `toAgentTools` at the moment the tool executes,\n * not when the tool was built, which is what lets one set of tools serve every\n * user's run.\n *\n * That producer *does* read ambient state, since a run no longer carries a\n * request (see `AgentContext`), and the boundary is what keeps that from\n * mattering here: the registry is handed a caller and dispatches as that\n * caller, so a second producer — a job, a test, v2's remote client — supplies\n * one however it likes, and a run with no ambient request is refused by\n * `toAgentTools` before the registry is reached rather than dispatching as\n * nobody.\n * The call is dispatched with that request's credentials, so it runs as that\n * user, through that route's middleware.\n *\n * `remote` is v2's MCP client with a bearer token. It is typed so the seam\n * exists and every entry point already takes it; each of them refuses it,\n * because a token resolver is what makes it safe and there is none yet.\n * Reading the caller from `RequestContext` instead would have been shorter,\n * and would have left v2 a registry that only works inside a gemi request.\n */\nexport type McpCaller =\n | { kind: \"local\"; req: HttpRequest<any, any> }\n | { kind: \"remote\"; token: string };\n\n/**\n * One tool call, as the app's hooks see it: who is calling, the request their\n * identity comes from, which tool, its parsed arguments, and — for a call an\n * agent run makes — the run's tool context.\n *\n * `req` is `caller.req`, repeated so a hook that only wants the request does\n * not have to narrow the caller. `ctx` is absent when `McpRegistry.execute` is\n * called without one (a test, a script dispatching a tool directly).\n */\nexport type McpCallContext = {\n caller: Extract<McpCaller, { kind: \"local\" }>;\n req: HttpRequest<any, any>;\n tool: McpToolDescriptor;\n input: Record<string, unknown>;\n ctx?: ToolContext;\n};\n\n/**\n * What `McpRouter.credentials` answers for a call: the app's own headers and\n * cookies, sent beside gemi's access token. See `DispatchCredentials`.\n */\nexport type McpCredentials = DispatchCredentials;\n\n/**\n * MCP's tool annotations, from the verb. They drive confirmation prompts in\n * remote clients, which is why they are set now rather than when v2 lands:\n * every app would otherwise have to revisit its MCP file to get them.\n *\n * Only what the verb says is set. POST and PATCH carry nothing, and MCP's\n * defaults for an unannotated tool — not read-only, possibly destructive — are\n * the honest answer for them.\n */\nexport type McpToolAnnotations = {\n readOnlyHint?: true;\n destructiveHint?: true;\n idempotentHint?: true;\n};\n\n/**\n * One tool, described without committing to who consumes it.\n *\n * The registry's native output. An in-process agent projects it to an\n * `AgentTool` (`toAgentTools`); v2's `tools/list` will project it to JSON.\n * Emitting `AgentTool`s here instead would leave v2 reversing JSON Schema back\n * out of them.\n */\nexport interface McpToolDescriptor {\n /** The `routes` key, verbatim. See `McpRouter`. */\n readonly name: string;\n readonly description: string;\n readonly method: McpMethod;\n /** The route's path as declared, params unfilled. */\n readonly url: string;\n /**\n * What the model fills in: the declared `input`, plus a string for every\n * `\"input\"` path param and every `\"input\"` file field, and the model-facing\n * field of every `this.param(...)` (#767), under its own name and schema.\n * Bound params and bound files are not in it at all, and neither is the raw\n * name of a param the model names in its own terms.\n */\n readonly inputSchema: Schema<Record<string, unknown>>;\n /**\n * The route meta's `output`: the shape of what the tool answers, after\n * `result`. Absent when the meta declares none. v2's `tools/list` emits it\n * as `outputSchema`.\n */\n readonly outputSchema?: AnySchema;\n readonly annotations: McpToolAnnotations;\n readonly tags: readonly string[];\n readonly requiresApproval: boolean;\n /** The controller method mounted at the route, when there is one (#502). */\n readonly source?: RouteSource;\n}\n\n/**\n * Narrows `list`. Data rather than a predicate, because v2's per-token\n * visibility will come from a token's grants, which are data. Both fields\n * narrow: `names` keeps only those tools, `tags` keeps tools carrying at least\n * one of them.\n */\nexport type McpToolFilter = {\n names?: readonly string[];\n tags?: readonly string[];\n};\n\n/**\n * A failure the model is meant to read: a 4xx from the route, a missing file,\n * a bad argument. v2 turns it into `isError: true` rather than a protocol\n * error. Anything else thrown from `execute` is the caller's mistake or the\n * server's, not the model's.\n */\nexport class McpToolError extends ToolError {\n constructor(\n message: string,\n readonly status?: number,\n ) {\n super(message);\n this.name = \"McpToolError\";\n }\n}\n\n/** Tool names as OpenAI's function tools and MCP clients both accept them. */\nconst TOOL_NAME = /^[A-Za-z0-9_-]{1,64}$/;\n\n/** `:name`, with URLPattern's modifiers. */\nconst PATH_PARAM = /:([A-Za-z_][A-Za-z0-9_]*)([?*+]?)/g;\n\n/**\n * A 4xx body goes to the model verbatim, so a validation error can be fixed on\n * the next step — but a route that answers a 4xx with a page of HTML should not\n * be able to fill the context window with it.\n */\nconst MAX_ERROR_BODY = 4000;\n\n/**\n * The same limit for a success, set higher: a list route answering every row,\n * or a route serving a large text file, would otherwise land in the context\n * window whole. Past it the model gets the text cut short and told so, rather\n * than an error — the route has already done what it does, and a model told it\n * failed would do it again.\n */\nconst MAX_RESULT_BODY = 100_000;\n\n/** A body a model can read. A missing content-type is read as text. */\nconst READABLE = /^(text\\/|application\\/([\\w.+-]*\\+)?(json|xml))/i;\n\ntype PathParam = { name: string; modifier: string };\n\ntype Plan = {\n descriptor: McpToolDescriptor;\n params: PathParam[];\n paramBinders: Map<string, McpParamBinder>;\n /** Params the model names in its own terms: the input key, and the resolver. */\n modelParams: Map<string, { key: string; param: McpModelParam<any, any> }>;\n fileFields: { name: string; binder: McpFileBinder | \"input\" }[];\n jsonKeys: string[];\n result?: McpResultProjection;\n};\n\n/**\n * The tools an app's `McpRouter` declares, resolved against the api route\n * table, and the one place a call to them is dispatched.\n *\n * Built once, at boot, from the dispatcher's flat route table — so a route\n * that was renamed or unmounted fails the boot here, and at compile time\n * before that, rather than the first model that calls it.\n *\n * THE INVARIANT: a tool call can do nothing its caller could not do with a\n * direct HTTP request. `execute` builds a request and hands it to\n * `ApiRouteDispatcher.dispatchAs`, which runs the route's middleware, auth and\n * policies exactly as for a client. It never reaches for a flat entry's\n * `exec`, which is the handler alone — no auth, no policies, no rate limit —\n * and is the shorter path for exactly that reason.\n */\nexport class McpRegistry<R extends McpRouter<any> = McpRouter<any>> {\n static token = \"router.mcp\";\n\n /**\n * Types only, never set: the app's router, which a typed `toAgentTools`\n * reads its tools' names, inputs and outputs from (#771). The container\n * answers an untyped registry; name the router where the tools are built:\n * `app(McpRegistry) as McpRegistry<SiteMcpRouter>`.\n */\n declare readonly __router?: R;\n\n private readonly plans = new Map<string, Plan>();\n\n constructor(\n private readonly router: McpRouter<any>,\n private readonly dispatcher: Pick<\n ApiRouteDispatcher,\n \"flatRoutes\" | \"dispatchAs\" | \"getRouteHandlerAndParams\"\n >,\n ) {\n for (const [name, declaration] of Object.entries(router.routes ?? {})) {\n this.plans.set(name, this.plan(name, declaration));\n }\n }\n\n /**\n * The tools `caller` may see. v1 has only local callers, and every declared\n * tool is visible to every one of them; `filter` is taken now so the shape\n * v2's per-token visibility needs is the shape callers already use.\n */\n list(caller: McpCaller, filter?: McpToolFilter): McpToolDescriptor[] {\n assertLocal(caller);\n return this.descriptors(filter);\n }\n\n /**\n * The same listing without a caller, for a projection built once before any\n * caller exists — `toAgentTools`. It is not per-caller visibility, and a v2\n * remote listing must go through `list`.\n */\n descriptors(filter?: McpToolFilter): McpToolDescriptor[] {\n const out: McpToolDescriptor[] = [];\n for (const { descriptor } of this.plans.values()) {\n if (filter?.names && !filter.names.includes(descriptor.name)) continue;\n if (filter?.tags && !descriptor.tags.some((tag) => filter.tags.includes(tag))) continue;\n out.push(descriptor);\n }\n return out;\n }\n\n /**\n * Calls the tool `name` as `caller`, and answers the route's JSON.\n *\n * Before dispatching, the router's `credentials` hook, when it has one, is\n * asked for the app's own headers and cookies for this call; they ride\n * beside the access token (see `McpRouter.credentials`). A throw from it is a\n * server failure, logged for the app, like a binder's.\n *\n * `ctx` is the tool context of the agent call, and only files need it:\n * `\"input\"` files resolve through `ctx.attachments`, the handle already\n * scoped to the caller, and bound files are chosen by a binder given `ctx`.\n * Nothing here reads an attachment any other way — the store has no\n * unscoped lookup, and this must not become one.\n *\n * A 2xx answers its JSON, or its text, cut at `MAX_RESULT_BODY`; a body\n * that is neither — a file a route serves — is described, not shown. A tool\n * whose meta has a `result` or an `output` answers its JSON put through\n * them instead, and is cut after that, not before (see `project`).\n * A 4xx throws an `McpToolError` carrying the route's body, so a validation\n * error reaches the model word for word. Anything else, or a route that\n * throws, throws an `McpToolError` that says only that the server failed.\n * The app gets the reason in its log, and a stack trace or a database\n * message is not something to hand a model that may be steered by whoever\n * wrote the document it is reading.\n */\n async execute(\n caller: McpCaller,\n name: string,\n args: unknown,\n ctx?: ToolContext,\n ): Promise<unknown> {\n assertLocal(caller);\n const plan = this.plans.get(name);\n if (!plan) {\n throw new Error(`McpRegistry: there is no tool named \"${name}\".`);\n }\n const { descriptor } = plan;\n\n const parsed = descriptor.inputSchema.safeParse(args);\n if (parsed.ok === false) {\n throw new McpToolError(`Invalid arguments for \"${name}\": ${parsed.errors.join(\", \")}`);\n }\n const input = parsed.value;\n\n const call: McpCallContext = { caller, req: caller.req, tool: descriptor, input, ctx };\n const path = await this.fillPath(plan, call);\n\n // The dispatcher routes `path` afresh and takes the first route that\n // matches it, so a model's \"archive-all\" for `/products/:id` would reach a\n // `/products/archive-all` declared above it — a route the app never\n // exposed, behind none of this tool's approval or annotations.\n const { path: matched } = this.dispatcher.getRouteHandlerAndParams(\n new Request(`http://gemi.internal/api${path}`),\n );\n if (matched !== descriptor.url) {\n throw new McpToolError(`\"${name}\" has nothing at ${path}.`, 404);\n }\n\n const json: Record<string, unknown> = {};\n for (const key of plan.jsonKeys) {\n if (input[key] !== undefined) json[key] = input[key];\n }\n\n let body: FormData | Record<string, unknown> | undefined;\n let query = \"\";\n if (plan.fileFields.length > 0) {\n body = await this.formData(plan, json, call);\n } else if (descriptor.method === \"GET\") {\n query = toQuery(json);\n } else if (plan.jsonKeys.length > 0) {\n body = json;\n }\n\n const credentials = this.router.credentials\n ? await this.bind(plan, \"the credentials\", () => this.router.credentials!(call))\n : undefined;\n\n let response: Response;\n try {\n const target = query ? `${path}?${query}` : path;\n response = credentials\n ? await this.dispatcher.dispatchAs(caller.req, descriptor.method, target, body, {\n credentials,\n })\n : await this.dispatcher.dispatchAs(caller.req, descriptor.method, target, body);\n } catch (error) {\n // What a client would get as a 500. A handler's throw is logged by the\n // dispatcher too, but dispatchAs's own refusal of the path is not.\n console.error(`[gemi/mcp] Dispatching \"${name}\" failed:`, error);\n throw new McpToolError(`\"${name}\" failed on the server.`, 500);\n }\n if (!plan.result && !descriptor.outputSchema) {\n return await readResponse(name, response);\n }\n return await this.project(plan, call, response);\n }\n\n // --- building ------------------------------------------------------------\n\n private plan(name: string, declaration: McpRouteDeclaration): Plan {\n if (!TOOL_NAME.test(name)) {\n throw new Error(\n `McpRouter: \"${name}\" is not a valid tool name. A routes key is the tool's name as a model and an MCP client see it, used verbatim — letters, digits, \"_\" and \"-\", at most 64.`,\n );\n }\n const where = `McpRouter: \"${name}\" (${declaration?.method} ${declaration?.url})`;\n if (!(declaration instanceof Object) || declaration.__internal_brand !== \"McpRoute\") {\n throw new Error(\n `McpRouter: \"${name}\" is not a route. Declare it with this.fromApiRoute(...).`,\n );\n }\n const { method, url, meta } = declaration;\n if (url.startsWith(\"/__gemi__\")) {\n throw new Error(`${where} is a framework route, not an app route.`);\n }\n\n const entry = this.dispatcher.flatRoutes[url]?.[method];\n if (!entry) {\n throw new Error(\n `${where} names no route of this app. Expose a route by the path and verb it is mounted at.`,\n );\n }\n\n // Mirrors the compile-time check, for a router written in JavaScript or\n // behind a cast: a param left undeclared would otherwise be the model's.\n const params: PathParam[] = [...url.matchAll(PATH_PARAM)].map(([, param, modifier]) => ({\n name: param,\n modifier,\n }));\n const declared = meta.params ?? {};\n for (const param of params) {\n if (!(param.name in declared)) {\n throw new Error(\n `${where}: the path param \"${param.name}\" must be bound or declared \"input\".`,\n );\n }\n }\n for (const [key, binder] of Object.entries(declared)) {\n if (!params.some((param) => param.name === key)) {\n throw new Error(`${where}: \"${key}\" in params is not a param of the url.`);\n }\n if (isModelParam(binder)) {\n assertModelParam(where, `params.${key}`, binder);\n } else {\n assertBinder(where, `params.${key}`, binder);\n }\n }\n\n let jsonKeys: string[] = [];\n let base: JSONSchema | undefined;\n if (meta.input) {\n base = meta.input.toJSONSchema();\n if (base.type !== \"object\" || !base.properties) {\n throw new Error(\n `${where}: input must be an s.object(...), the fields of the request body.`,\n );\n }\n jsonKeys = Object.keys(base.properties);\n }\n\n const fileFields = Object.entries(meta.files ?? {}).map(([field, binder]) => ({\n name: field,\n binder,\n }));\n for (const file of fileFields) {\n assertBinder(where, `files.${file.name}`, file.binder);\n }\n if (meta.result !== undefined && typeof meta.result !== \"function\") {\n throw new Error(`${where}: result must be a function.`);\n }\n if (meta.output !== undefined && typeof meta.output?.safeParse !== \"function\") {\n throw new Error(`${where}: output must be a schema, built with s.`);\n }\n if (fileFields.length > 0 && method === \"GET\") {\n throw new Error(`${where}: a GET has no body to carry a file.`);\n }\n\n const extras: Record<string, AnySchema> = {};\n // Two params, or a param and a file, can name the same input key only by\n // a `this.param` `as`; the model could send one value for both.\n const addExtra = (key: string, schema: AnySchema) => {\n if (key in extras) {\n throw new Error(\n `${where}: \"${key}\" is the input key of two params or files, and the model can only send one.`,\n );\n }\n extras[key] = schema;\n };\n const paramBinders = new Map<string, McpParamBinder>();\n const modelParams = new Map<string, { key: string; param: McpModelParam<any, any> }>();\n for (const param of params) {\n const binder = declared[param.name];\n if (binder === \"input\") {\n const field = s.string().describe(`The \":${param.name}\" segment of the url.`);\n addExtra(\n param.name,\n param.modifier === \"?\" || param.modifier === \"*\" ? field.optional() : field,\n );\n } else if (isModelParam(binder)) {\n const key = binder.as ?? param.name;\n addExtra(key, binder.input);\n modelParams.set(param.name, { key, param: binder });\n } else {\n paramBinders.set(param.name, binder);\n }\n }\n for (const file of fileFields) {\n if (file.binder === \"input\") {\n addExtra(\n file.name,\n s\n .string()\n .describe(\n \"The id of an attachment the user uploaded or a tool produced (gemi_att_…), whose file is sent as this field.\",\n ),\n );\n }\n }\n // A bound file is not the model's, but it shares the form with the input,\n // so it collides all the same.\n const bodyKeys = [...Object.keys(extras), ...fileFields.map((file) => file.name)];\n for (const key of new Set(bodyKeys)) {\n if (jsonKeys.includes(key)) {\n throw new Error(\n `${where}: \"${key}\" is both an input field and a param or file, and the model can only send one.`,\n );\n }\n }\n\n return {\n descriptor: Object.freeze({\n name,\n description: meta.description,\n method,\n url,\n inputSchema: combineSchemas(meta.input, base, s.object(extras)),\n ...(meta.output ? { outputSchema: meta.output } : {}),\n annotations: annotationsFor(method),\n tags: Object.freeze([...(meta.tags ?? [])]),\n requiresApproval: meta.requiresApproval === true,\n ...(entry.source ? { source: entry.source } : {}),\n }),\n params,\n paramBinders,\n modelParams,\n fileFields,\n jsonKeys,\n ...(meta.result ? { result: meta.result } : {}),\n };\n }\n\n // --- calling -------------------------------------------------------------\n\n private async fillPath(plan: Plan, call: McpCallContext): Promise<string> {\n const { input, req } = call;\n let path = plan.descriptor.url;\n for (const param of plan.params) {\n const binder = plan.paramBinders.get(param.name);\n const modelParam = plan.modelParams.get(param.name);\n let value: unknown;\n if (binder || modelParam) {\n value = modelParam\n ? await this.bind(\n plan,\n `the param \"${param.name}\"`,\n () => modelParam.param.bind(input[modelParam.key], req, call),\n { refusals: true },\n )\n : await this.bind(plan, `the param \"${param.name}\"`, () => binder!(req, call));\n if (value === undefined || value === null || value === \"\") {\n console.error(\n `[gemi/mcp] The ${modelParam ? \"resolver\" : \"binder\"} for \"${param.name}\" of \"${plan.descriptor.name}\" returned ${JSON.stringify(value)}.`,\n );\n throw new McpToolError(`\"${plan.descriptor.name}\" failed on the server.`, 500);\n }\n } else {\n value = input[param.name];\n }\n\n const token = `:${param.name}${param.modifier}`;\n if (value === undefined || value === null || value === \"\") {\n if (param.modifier !== \"?\" && param.modifier !== \"*\") {\n throw new McpToolError(`\"${param.name}\" of \"${plan.descriptor.name}\" must not be empty.`);\n }\n path = path.replace(`/${token}`, \"\");\n continue;\n }\n path = path.replace(token, encodeSegment(plan.descriptor.name, param, String(value)));\n }\n return path;\n }\n\n private async formData(\n plan: Plan,\n json: Record<string, unknown>,\n call: McpCallContext,\n ): Promise<FormData> {\n const { input, ctx } = call;\n const name = plan.descriptor.name;\n if (!ctx) {\n throw new Error(\n `McpRegistry: \"${name}\" sends a file, which is resolved through the tool context's attachments, and none was passed to execute().`,\n );\n }\n const form = new FormData();\n for (const [key, value] of Object.entries(json)) {\n appendField(form, key, value);\n }\n for (const field of plan.fileFields) {\n const id =\n field.binder === \"input\"\n ? (input[field.name] as string)\n : await this.bind(plan, `the file \"${field.name}\"`, () =>\n (field.binder as McpFileBinder)(ctx, call),\n );\n if (typeof id !== \"string\" || id === \"\") {\n throw new McpToolError(\n `\"${name}\" needs a file for \"${field.name}\", and this turn has none. Ask the user to attach one.`,\n );\n }\n // Scoped to the caller: someone else's id is a not-found, worded like an\n // invented one, and that error is what the model reads.\n form.append(field.name, await ctx.attachments.file(id));\n }\n return form;\n }\n\n /**\n * A 2xx put through the meta's `result` and `output`, for a tool that has\n * either.\n *\n * The whole body is parsed and projected before anything is cut: trimming a\n * large answer down is what a projection is for, and cutting first would\n * hand `result` half a JSON document. The projected value is then held to\n * `MAX_RESULT_BODY` the way any answer is.\n *\n * Every failure here is the app's, not the model's — a route that answered\n * something other than JSON, a `result` that threw, an answer `output` does\n * not describe — so each is logged and the model is told only that the\n * tool failed. Telling it more would invite a retry of a call that already\n * did what it does.\n */\n private async project(plan: Plan, call: McpCallContext, response: Response): Promise<unknown> {\n const { name, outputSchema } = plan.descriptor;\n if (response.status < 200 || response.status >= 300) {\n return await readResponse(name, response);\n }\n const failed = (why: string, error?: unknown) => {\n if (error === undefined) console.error(`[gemi/mcp] \"${name}\" ${why}`);\n else console.error(`[gemi/mcp] \"${name}\" ${why}`, error);\n return new McpToolError(`\"${name}\" failed on the server.`, 500);\n };\n\n const type = response.headers.get(\"Content-Type\");\n const text = await response.text();\n let data: unknown = null;\n if (text !== \"\") {\n try {\n data = JSON.parse(text);\n } catch {\n throw failed(\n `answered ${type ?? \"a body with no content-type\"} that is not JSON, and its result or output needs JSON.`,\n );\n }\n }\n\n let value = data;\n if (plan.result) {\n try {\n value = await plan.result(data, call);\n } catch (error) {\n throw failed(\"failed in its result projection:\", error);\n }\n }\n if (outputSchema) {\n const parsed = outputSchema.safeParse(value);\n if (parsed.ok === false) {\n throw failed(`answered what its output schema refuses: ${parsed.errors.join(\", \")}`);\n }\n value = parsed.value;\n }\n\n if (value === undefined) return null;\n const serialised = JSON.stringify(value);\n if (serialised !== undefined && serialised.length > MAX_RESULT_BODY) {\n return `${serialised.slice(0, MAX_RESULT_BODY)}… [cut at ${MAX_RESULT_BODY} of ${serialised.length} characters]`;\n }\n return value;\n }\n\n /**\n * Runs an app's binder. Its failure is the server's, not the model's, so the\n * model is told only that; the app gets the error in its log.\n *\n * With `refusals`, a `ToolError` (an `McpToolError` is one) is the app\n * refusing what the model sent, and is passed on for the model to read. Only\n * a model-facing param's resolver gets that: it is translating the model's\n * own words, so \"there is no such page\" is something the model can fix. A\n * plain binder reads nothing of the model's, and its throw stays the\n * server's.\n */\n private async bind<T>(\n plan: Plan,\n what: string,\n fn: () => T | Promise<T>,\n options: { refusals?: boolean } = {},\n ): Promise<T> {\n try {\n return await fn();\n } catch (error) {\n if (options.refusals && error instanceof ToolError) throw error;\n console.error(`[gemi/mcp] Binding ${what} of \"${plan.descriptor.name}\" failed:`, error);\n throw new McpToolError(`\"${plan.descriptor.name}\" failed on the server.`, 500);\n }\n }\n}\n\nfunction assertLocal(caller: McpCaller): asserts caller is Extract<McpCaller, { kind: \"local\" }> {\n if (caller?.kind !== \"local\") {\n throw new Error(\n \"McpRegistry: only local callers are implemented. A remote caller needs a bearer-token resolver, which v1 does not have — and until it does there is no remote surface to call from.\",\n );\n }\n}\n\n/** A binder is a function or `\"input\"`; anything else would leave the value to nobody. */\nfunction assertBinder(where: string, at: string, binder: unknown) {\n if (binder !== \"input\" && typeof binder !== \"function\") {\n throw new Error(`${where}: ${at} must be a function or \"input\".`);\n }\n}\n\n/**\n * By brand, as a declaration is, rather than `instanceof`: an app that ends up\n * with two copies of gemi would otherwise see its params as broken binders.\n */\nfunction isModelParam(value: unknown): value is McpModelParam<any, any> {\n return value instanceof Object && (value as McpModelParam).__internal_brand === \"McpModelParam\";\n}\n\n/** A `this.param(...)`, checked for a router written in JavaScript or behind a cast. */\nfunction assertModelParam(where: string, at: string, param: McpModelParam<any, any>) {\n if (param.as !== undefined && (typeof param.as !== \"string\" || param.as === \"\")) {\n throw new Error(`${where}: ${at}.as must be a non-empty string.`);\n }\n const schema = param.input;\n if (typeof schema?.safeParse !== \"function\" || typeof schema.toJSONSchema !== \"function\") {\n throw new Error(`${where}: ${at}.input must be a schema, built with s.`);\n }\n if (typeof param.bind !== \"function\") {\n throw new Error(`${where}: ${at}.bind must be a function.`);\n }\n}\n\nfunction annotationsFor(method: McpMethod): McpToolAnnotations {\n switch (method) {\n case \"GET\":\n return Object.freeze({ readOnlyHint: true as const });\n case \"DELETE\":\n return Object.freeze({ destructiveHint: true as const });\n case \"PUT\":\n return Object.freeze({ idempotentHint: true as const });\n default:\n return Object.freeze({});\n }\n}\n\n/**\n * One schema out of two: the app's `input`, and the strings for `\"input\"`\n * params and files.\n *\n * Built by hand, like `questionSchema` in `ai/Agent.ts`, rather than by\n * widening `s` with a merge: `Schema.ts` is deliberately the strict\n * structured-output subset and owns no operation on a finished schema. Both\n * halves are real `s` schemas, each parses the whole value and drops the keys\n * that are not its own — which is also where a bound param the model sent\n * anyway disappears, since neither half declares it.\n */\nfunction combineSchemas(\n input: AnySchema | undefined,\n base: JSONSchema | undefined,\n extras: AnySchema,\n): Schema<Record<string, unknown>> {\n const extra = extras.toJSONSchema();\n const json: JSONSchema = {\n ...(base ?? {}),\n type: \"object\",\n properties: { ...(base?.properties ?? {}), ...extra.properties },\n required: [...(base?.required ?? []), ...(extra.required ?? [])],\n additionalProperties: false,\n };\n\n const safeParse = (value: unknown) => {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return { ok: false as const, errors: [\"expected an object\"] };\n }\n const errors: string[] = [];\n let out: Record<string, unknown> = {};\n if (input) {\n const own = input.safeParse(value);\n if (own.ok === false) errors.push(...own.errors);\n else out = { ...own.value };\n }\n const more = extras.safeParse(value);\n if (more.ok === false) errors.push(...more.errors);\n else out = { ...out, ...more.value };\n return errors.length > 0 ? { ok: false as const, errors } : { ok: true as const, value: out };\n };\n\n return {\n toJSONSchema: () => json,\n safeParse,\n parse(value: unknown) {\n const result = safeParse(value);\n if (result.ok === false) throw new Error(result.errors.join(\", \"));\n return result.value;\n },\n } as unknown as Schema<Record<string, unknown>>;\n}\n\n/**\n * A model-supplied param, as one encoded path segment. `.` and `..` are\n * refused rather than encoded: they encode to themselves, and URL parsing\n * would walk them out of the route.\n */\nfunction encodeSegment(tool: string, param: PathParam, value: string): string {\n const parts = param.modifier === \"*\" || param.modifier === \"+\" ? value.split(\"/\") : [value];\n if (parts.some((part) => part === \".\" || part === \"..\")) {\n throw new McpToolError(`\"${value}\" is not a valid value for \"${param.name}\" of \"${tool}\".`);\n }\n return parts.map(encodeURIComponent).join(\"/\");\n}\n\n/**\n * A JSON field as a multipart form field. A form has only strings, so the\n * route reads `\"12\"` where a JSON body would have carried `12` — what a\n * browser form sends the same route. An array becomes the key repeated, which\n * `HttpRequest` reads back as an array.\n */\nfunction appendField(form: FormData, key: string, value: unknown) {\n if (value === undefined || value === null) return;\n if (Array.isArray(value)) {\n for (const item of value) appendField(form, key, item);\n return;\n }\n form.append(key, typeof value === \"object\" ? JSON.stringify(value) : String(value));\n}\n\n/** A GET's input, as the query string `HttpRequest.search` reads. */\nfunction toQuery(json: Record<string, unknown>): string {\n const search = new URLSearchParams();\n const add = (key: string, value: unknown) => {\n if (value === undefined || value === null) return;\n if (Array.isArray(value)) {\n for (const item of value) add(key, item);\n return;\n }\n search.append(key, typeof value === \"object\" ? JSON.stringify(value) : String(value));\n };\n for (const [key, value] of Object.entries(json)) add(key, value);\n return search.toString();\n}\n\nasync function readResponse(name: string, response: Response): Promise<unknown> {\n if (response.status >= 200 && response.status < 300) {\n const type = response.headers.get(\"Content-Type\");\n if (type && !READABLE.test(type)) {\n const size = (await response.arrayBuffer()).byteLength;\n return `\"${name}\" answered with ${size} bytes of ${type}, which is not shown.`;\n }\n const text = await response.text();\n if (text === \"\") return null;\n if (text.length > MAX_RESULT_BODY) {\n return `${text.slice(0, MAX_RESULT_BODY)}… [cut at ${MAX_RESULT_BODY} of ${text.length} characters]`;\n }\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n }\n const text = await response.text();\n if (response.status >= 400 && response.status < 500) {\n const shown = text.length > MAX_ERROR_BODY ? `${text.slice(0, MAX_ERROR_BODY)}…` : text;\n throw new McpToolError(\n `\"${name}\" was refused with ${response.status}${shown ? `: ${shown}` : \".\"}`,\n response.status,\n );\n }\n if (response.status < 500) {\n // A redirect, or a 1xx: nothing the model can follow, and nothing the\n // dispatcher logged.\n console.error(\n `[gemi/mcp] \"${name}\" answered ${response.status}${response.headers.get(\"Location\") ? ` to ${response.headers.get(\"Location\")}` : \"\"}, which a tool call cannot follow.`,\n );\n }\n throw new McpToolError(`\"${name}\" failed on the server.`, response.status);\n}\n",
|
|
27
28
|
"import { ServiceProvider } from \"../../support/ServiceProvider\";\nimport { McpRegistry } from \"../mcp/McpRegistry\";\nimport { ApiRouteDispatcher } from \"./ApiRouteDispatcher\";\nimport { DomainRouter } from \"./DomainRouter\";\nimport { ViewRouteDispatcher } from \"./ViewRouteDispatcher\";\nimport type { ApiRouteConfig, DomainsConfig, McpRouteConfig, ViewRouteConfig } from \"./config\";\nimport type { Repository } from \"../../support/Repository\";\n\nfunction readConfig<T>(config: Repository, key: string): T {\n const slice = config.get<T>(key);\n if (!slice) {\n throw new Error(\n `Missing \"${key}\" configuration. Route dispatching needs a root router — declare it in \"app/config/route.ts\".`,\n );\n }\n return slice;\n}\n\n/**\n * Owns both route dispatchers. Laravel ships a single RouteServiceProvider for\n * every route file it loads; the api/view split is a config slice, not a\n * separate provider.\n */\nexport class RouteServiceProvider extends ServiceProvider {\n register() {\n this.app.singleton(\n ApiRouteDispatcher,\n () =>\n new ApiRouteDispatcher(\n readConfig<ApiRouteConfig>(this.app.config, \"route.api\"),\n ),\n );\n\n this.app.singleton(\n ViewRouteDispatcher,\n () =>\n new ViewRouteDispatcher(\n readConfig<ViewRouteConfig>(this.app.config, \"route.view\"),\n ),\n );\n\n // The root pair above is the root group's; the other `route.domains`\n // groups get their own only when they bring their own routers.\n this.app.singleton(\n DomainRouter,\n () =>\n new DomainRouter(\n { api: this.app.make(ApiRouteDispatcher), view: this.app.make(ViewRouteDispatcher) },\n {\n api: readConfig<ApiRouteConfig>(this.app.config, \"route.api\"),\n view: readConfig<ViewRouteConfig>(this.app.config, \"route.view\"),\n domains: this.app.config.get<DomainsConfig | undefined>(\"route.domains\"),\n },\n ),\n );\n\n // Bound whether or not the app declares one, so asking for it without\n // `route.mcp` fails with a sentence rather than an unbound token.\n this.app.singleton(McpRegistry, () => {\n const config = this.app.config.get<McpRouteConfig | undefined>(\"route.mcp\");\n if (!config?.router) {\n throw new Error(\n 'No MCP router is configured. Declare one as `mcp: { router }` in \"app/config/route.ts\".',\n );\n }\n return new McpRegistry(new config.router(), this.app.make(ApiRouteDispatcher));\n });\n }\n\n /**\n * The dispatchers of every host group, and the MCP registry when an app declares one, must\n * not be built lazily: constructing the dispatchers flattens the route\n * tables and runs `assertNoReservedRoutePaths`, and constructing the registry\n * resolves every tool against those tables. A missing `route` config, a\n * route under a reserved prefix or a tool naming a route that is gone has to\n * fail the boot rather than the first request, or the first tool call, that\n * happens to hit it.\n */\n boot() {\n this.app.make(ApiRouteDispatcher);\n this.app.make(ViewRouteDispatcher);\n // Builds every host group's dispatchers and validates `route.domains`.\n this.app.make(DomainRouter);\n if (this.app.config.get(\"route.mcp\")) {\n this.app.make(McpRegistry);\n }\n }\n}\n"
|
|
28
29
|
],
|
|
29
|
-
"mappings": "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",
|
|
30
|
-
"debugId": "
|
|
30
|
+
"mappings": ";uzBAsCA,SAAsB,JAAO,LAAC,JAAc,JAAuB,LAAC,JAAkB,LACpF,HAAI,LAQF,HAAI,CAAE,MAAM,EAAI,UAAU,KAAK,CAAG,EAChC,OAEF,MAAO,EAAO,CAMd,QAAQ,MACN,2CAA2C,EAAI,QAC/C,CACF,EACA,OAGF,IAAM,EAAU,GAAa,EAAI,kBAAkB,EACnD,GAAI,CAAC,EAAI,aAAe,IAAY,OAAW,CAC7C,MAAM,EAAS,CAAG,EAClB,OAKF,IAAI,EACJ,GAAI,CAEF,GADA,EAAQ,EAAQ,QAAQ,GAAK,GAAa,EACtC,EAAI,YAAa,CACnB,IAAM,EAAS,KAAK,OAAO,EAAQ,KAAO,KAAK,IAAI,GAAK,KAAM,EAC9D,GAAI,CAAE,MAAM,EAAM,MAAM,QAAQ,kBAAkB,EAAI,OAAQ,CAAM,EAAI,QAE1E,MAAO,EAAO,CACd,QAAQ,MAAM,wCAAwC,EAAI,oBAAqB,CAAK,EACpF,OAEF,GAAI,IAAY,OAAW,CACzB,MAAM,EAAS,CAAG,EAClB,OAGF,GAAI,CACF,MAAM,EAAM,IAAI,aAAa,EAAI,OAAQ,CAAE,IAAK,CAAQ,EAAG,CAAC,IAAS,EAAS,EAAK,CAAI,CAAC,EACxF,MAAO,EAAO,CACd,GAAI,aAAiB,GACnB,QAAQ,MACN,YAAY,EAAI,mGAEhB,CACF,EAEA,aAAQ,MAAM,0DAA0D,EAAI,QAAS,CAAK,GAiBhG,IAAM,GAAwB,OAE9B,SAAS,EAAY,CAAC,EAA2D,CAC/E,GAAI,CAAC,EAAQ,OACb,GAAI,IAAW,GAAM,OAAO,GAC5B,OAAO,EAAO,cAAgB,GAGhC,IAAI,GACJ,SAAS,EAAY,EAAG,CAEtB,OADA,KAAkB,IAAI,EAAY,IAAI,EAAiB,EAChD,GAGT,eAAe,CAAQ,CAAC,EAAc,EAAiB,CACrD,GAAI,CACF,MAAM,EAAI,OAAO,KAAK,CAAG,EACzB,MAAO,EAAO,CACd,QAAQ,MAAM,4BAA4B,EAAI,QAAS,CAAK,EAE9D,GAAI,CACF,MAAM,EAAI,SAAS,KAAK,EAAK,CAAI,EACjC,MAAO,EAAO,CACd,QAAQ,MAAM,qBAAqB,EAAI,QAAS,CAAK,EAEvD,GAAI,CACF,MAAM,EAAI,WAAW,KAAK,CAAG,EAC7B,MAAO,EAAO,CACd,QAAQ,MAAM,6BAA6B,EAAI,QAAS,CAAK,GAyB1D,MAAM,CAAU,OACd,OAAQ,YAEP,QAA2B,CAAC,EAC5B,SAQS,SAAW,IAAI,IASxB,QAAU,GAEV,MAER,WAAW,CAAC,EAAkC,CAC5C,KAAK,SAAW,EAAO,QAqBrB,KAAI,EAAqC,CAC3C,MAAO,CAAC,GAAG,KAAK,QAAQ,EAc1B,OAAO,CAAC,EAAgC,CACtC,KAAK,SAAW,EAOlB,QAAQ,CAAC,EAA0B,CACjC,KAAK,MAAQ,EASf,KAAK,CAAC,EAAsB,CAAC,IAAO,EAAG,EAAG,CAExC,GADA,KAAK,QAAU,GACX,KAAK,SAAS,SAAW,EAC3B,OAGF,IAAM,EAAY,WAAW,iBAAmB,IAAI,IAO9C,EAAU,IAAI,IAEpB,QAAW,KAAO,KAAK,SAAU,CAC/B,IAAM,EAAM,IAAI,EAChB,GAAI,CAAC,EAAI,KAAM,CAOb,QAAQ,MACN,oCAA8B,EAAI,MAAQ,sPAK5C,EACA,SAEF,GAAI,CAAC,EAAI,KAAM,CACb,QAAQ,MAAM,0CAA0C,EAAI,MAAM,EAClE,SAEF,IAAM,EAAQ,EAAQ,IAAI,EAAI,IAAI,EAClC,GAAI,IAAU,OAAW,CAOvB,QAAQ,MACN,iCAAiC,EAAI,gBAAU,sBAC5B,EAAI,MAAQ,sGAEjC,EACA,SAEF,EAAQ,IAAI,EAAI,KAAM,EAAI,MAAQ,oBAAoB,EAItD,EAAS,IAAI,EAAI,IAAI,GAAG,KAAK,EAK7B,IAAM,EAAS,IAAI,KAAK,EAAI,KAAM,IAAM,CACtC,GAAI,KAAK,QAAS,OAClB,IAAM,EAAQ,KAAK,MACnB,OAAO,KAAK,MAAM,EAAI,KAAM,IAAM,EAAI,IAAM,GAAQ,EAAK,CAAE,OAAM,CAAC,CAAC,CAAC,EACrE,EAED,EAAS,IAAI,EAAI,KAAM,CAAM,EAC7B,KAAK,QAAQ,KAAK,CAAM,GAQ5B,IAAI,EAAG,CACL,KAAK,QAAU,GACf,QAAW,KAAU,KAAK,QACxB,EAAO,KAAK,EAEd,KAAK,QAAU,CAAC,OAeZ,MAAK,CAAC,EAAY,IAAwC,CAC9D,KAAK,KAAK,EAEV,IAAI,EACE,EAAW,IAAI,QAAc,CAAC,IAAY,CAC9C,GAAI,IAAc,IAAU,EAAQ,WAAW,EAAS,CAAS,EAClE,EAOD,OANA,MAAM,QAAQ,KAAK,CACjB,QAAQ,WAAW,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC,EAAE,IAAI,CAAC,IAAM,EAAE,IAAI,CAAC,EACjE,CACF,CAAC,EACD,aAAa,CAAK,EAEX,CACL,WAAY,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC,EAAE,IAAI,CAAC,KAAO,IAAK,EAAE,IAAK,EAAE,CACpE,KAIE,QAAO,EAAW,CACpB,OAAO,KAAK,SAAS,KAGf,KAAK,CAAC,EAAc,EAAuC,CACjE,IAAM,EAAM,CAAC,EAIP,EAAO,QAAQ,QAAQ,EAC1B,KAAK,CAAI,EACT,QAAQ,IAAM,KAAK,SAAS,OAAO,CAAG,CAAC,EAE1C,OADA,KAAK,SAAS,IAAI,EAAK,CAAE,KAAM,CAAE,OAAM,UAAW,IAAI,IAAO,EAAG,MAAK,CAAC,EAC/D,EAEX,CCzXA,uBAAS,wBAuBT,IAAM,GAAmB,IAEnB,GAAe,CAAC,IACpB,KAAK,IAAI,GAAkB,KAAK,MAAM,EAAY,EAAE,CAAC,EAEhD,MAAM,WAA6B,CAAgB,CACxD,QAAQ,EAAG,CACT,KAAK,IAAI,UACP,EACA,IACE,IAAI,EAAa,KAAK,IAAI,OAAO,IAAiB,QAAS,CAAC,CAAC,EAAG,CAC9D,YAAa,KAAK,GACpB,CAAC,CACL,EAEA,KAAK,IAAI,UAAU,EAAa,IAAM,KAAK,IAAI,KAAK,CAAY,EAAE,KAAK,OA0BnE,KAAI,EAAG,CACX,IAAM,EAAQ,KAAK,IAAI,OAAO,IAAiB,QAAS,CAAC,CAAC,EAC1D,GAAI,EAAM,OAAS,OAAW,OAE9B,IAAQ,WAAY,EAAa,GAAoB,EAAG,CAAK,EAC7D,KAAK,IAAI,KAAK,CAAY,EAAE,QAAQ,MAAM,GAAa,CAAO,CAAC,OA2B3D,SAAQ,CAAC,EAAiC,CAE9C,GAAI,CAAC,KAAK,IAAI,SAAS,CAAY,EAAG,OACtC,IAAM,EAAQ,KAAK,IAAI,KAAK,CAAY,EACxC,GAAI,EAAM,QAAU,EAClB,QAAQ,IACN,qCAAqC,EAAM,gCAC7C,EAEF,IAAM,EAAS,GAAS,WAChB,cAAe,MAAM,EAAM,MACjC,IAAW,OAAY,IAAW,KAAK,IAAI,EAAG,EAAS,GAAa,CAAM,CAAC,CAC7E,EACA,GAAI,EAAW,OAAS,EAKtB,OAJA,QAAQ,MACN,iDACE,EAAW,IAAI,CAAC,IAAQ,GAAG,EAAI,SAAS,EAAI,KAAK,EAAE,KAAK,IAAI,CAChE,EACO,CAAE,UAAW,EAAK,EAG/B,CAwBO,SAAS,EAAsB,CAAC,EAA0B,CAE/D,GADA,GAAqB,CAAW,EAC5B,mBAAyB,aAAc,OAAO,GAAiB,CAAW,EAC9E,GAAI,GAAiB,CAAW,EAAG,OACnC,GAAI,CAAC,GAAoB,EAAG,OAC5B,GAAI,CAAC,EAAY,MAAM,CAAY,EAAG,OACtC,EAAY,KAAK,CAAY,EAAE,MAAM,EAGvC,SAAS,EAAgB,CAAC,EAA0B,CAClD,IAAM,EAAS,EAAY,OAAO,IAAiB,QAAS,CAAC,CAAC,EAAE,QAAU,SAC1E,OAAO,IAAW,UAAY,aAAkB,GAWlD,SAAS,EAAkB,CAAC,EAA0B,CACpD,GAAI,GAAiB,CAAW,EAAG,MAAO,GAE1C,GAAI,OADW,EAAY,OAAO,IAAiB,QAAS,CAAC,CAAC,EAAE,SAC1C,YAAc,CAAC,EAAY,MAAM,CAAY,EAAG,MAAO,GAC7E,GAAI,CACF,MAAO,CAAC,EAAY,KAAK,CAAY,EAAE,QACvC,KAAM,CACN,MAAO,IAgBX,SAAS,EAAoB,CAAC,EAA0B,CACtD,IAAM,EAAQ,QAAQ,IAAI,iBAC1B,GAAI,IAAU,QAAa,GAAc,EAAG,OAC5C,GAAI,QAAQ,IAAI,WAAa,QAAa,CAAC,GAAc,OACzD,GAAI,GAAkB,EAAG,CACvB,GAAI,CAAC,GAAmB,CAAW,EAAG,OACtC,QAAQ,KACN,2QAIF,EACK,QAAI,EAAM,KAAK,EAAE,YAAY,IAAM,KACxC,QAAQ,KACN,4BAA4B,4GAE9B,EA+BJ,SAAS,EAAgB,CAAC,EAA0B,CAClD,IAAM,EAAW,WAAW,eAC5B,GAAI,CAAC,EAAU,OAGf,GAFA,WAAW,eAAiB,OACvB,EAAS,KAAK,EACf,EAAY,MAAM,CAAY,EAAG,EAAY,KAAK,CAAY,EAAE,MAAM,EC1OrE,MAAM,WAAkC,CAAgB,CAC7D,QAAQ,EAAG,CACT,KAAK,IAAI,UACP,EACA,IACE,IAAI,EAAkB,KAAK,IAAI,OAAO,IAAsB,aAAc,CAAC,CAAC,EAAG,CAC7E,YAAa,KAAK,GACpB,CAAC,CACL,OAQI,SAAQ,EAAG,CACf,GAAI,CAAC,KAAK,IAAI,SAAS,CAAiB,EAAG,OAC3C,MAAM,KAAK,IAAI,KAAK,CAAiB,EAAE,MAAM,EAEjD,CCpBO,MAAM,WAA4B,CAAgB,CACvD,QAAQ,EAAG,CACT,KAAK,IAAI,UACP,EACA,IAAM,IAAI,EAAY,KAAK,IAAI,OAAO,IAAgB,OAAQ,CAAC,CAAC,CAAC,CACnE,EAEJ,CCIA,IAAM,GAAmB,IAEnB,GAAe,CAAC,IAAsB,KAAK,IAAI,GAAkB,KAAK,MAAM,EAAY,EAAE,CAAC,EAE1F,MAAM,WAAgC,CAAgB,CAC3D,QAAQ,EAAG,CACT,KAAK,IAAI,UACP,EACA,IACE,IAAI,EACF,EACE,EAAuB,EACvB,KAAK,IAAI,OAAO,IAAoB,WAAY,CAAC,CAAC,CACpD,CACF,CACJ,OA0BI,KAAI,EAAG,CACX,IAAM,EAAY,KAAK,IAAI,KAAK,CAAS,EACnC,EAAQ,KAAK,IAAI,OAAO,IAAoB,WAAY,CAAC,CAAC,EAEhE,GAAI,EAAM,OAAS,OAAW,CAC5B,IAAQ,WAAY,EAAa,EAAuB,EAAG,CAAK,EAChE,EAAU,QAAQ,MAAM,GAAiB,CAAO,CAAC,EAqBnD,GAAI,QAAQ,IAAI,mBAAqB,IAAK,OAE1C,IAAM,EAAM,KAAK,IAGjB,GAAI,EAAI,MAAM,CAAW,EAAG,EAAU,SAAS,IAAM,EAAI,KAAK,CAAW,CAAC,EAC1E,EAAU,MAAM,CAAC,IAAO,GAAc,IAAI,EAAK,CAAE,CAAC,OAqB9C,SAAQ,CAAC,EAAiC,CAG9C,GAAI,CAAC,KAAK,IAAI,SAAS,CAAS,EAAG,OACnC,IAAM,EAAY,KAAK,IAAI,KAAK,CAAS,EACzC,GAAI,EAAU,QAAU,EACtB,QAAQ,IAAI,qCAAqC,EAAU,+BAA+B,EAE5F,IAAM,EAAS,GAAS,WAChB,cAAe,MAAM,EAAU,MACrC,IAAW,OAAY,IAAW,KAAK,IAAI,EAAG,EAAS,GAAa,CAAM,CAAC,CAC7E,EACA,GAAI,EAAW,OAAS,EAOtB,OANA,QAAQ,MACN,+CACE,EACG,IAAI,CAAC,IAAS,GAAG,EAAK,iBAAiB,EAAK,UAAU,YAAY,IAAI,EACtE,KAAK,IAAI,CAChB,EACO,CAAE,UAAW,EAAK,EAG/B,CClHO,MAAM,UAAkC,EAAkB,CAC1C,UAArB,WAAW,CAAU,EAAoB,cAAe,CACtD,MAAM,EADa,oBAKjB,UAAS,EAAY,CACvB,OAAgB,GAAI,KAAK,SAAS,OAG9B,KAAI,EAAuC,CAC/C,GAAI,CAAC,KAAK,UACR,MAAM,IAAI,EAAyB,KAAK,SAAS,EAGnD,IAAM,EAAiB,EAAS,KAAK,SAAS,EAM9C,OAAO,MAAM,EAAM,SAAS,IAAM,EAAM,SAAS,CAAC,OAiB9C,MAAK,CAAC,EAAa,EAAiB,EAAqD,CAC7F,GAAI,CAAC,KAAK,UACR,MAAM,IAAI,EAAyB,KAAK,SAAS,EAGnD,IAAM,EAAiB,EAAS,KAAK,SAAS,EACxC,EAAgB,KAAK,eAAe,CAAK,EACzC,EAAgC,CAAE,QAAO,EAC/C,GAAI,EAAe,EAAK,UAAY,EAAK,MAazC,OARA,MAAM,EAAM,SAAS,IACnB,EAAM,OAAO,CACX,MAAO,CAAE,KAAI,EACb,OAAQ,CAAE,SAAQ,CAAK,EACvB,OAAQ,CACV,CAAC,CACH,EAEO,CAAE,eAAc,EAGjB,cAAc,CAAC,EAAqB,CAC1C,IAAM,EAAQ,GAAO,SAAS,QAAQ,UACtC,OAAO,IAAU,QAAa,EAAM,OAAS,SAEjD,CCxBO,SAAS,EAAoB,CAAC,EAAwC,CAC3E,OAAO,EAGF,SAAS,EAAsB,EAMpC,CACA,MAAO,CACL,SAAU,OACV,QAAS,GACT,OAAQ,IAAI,EACZ,MAAO,cACP,IAAK,GACL,QAAS,OACT,WAAY,OACZ,eAAgB,GAClB,ECzEK,MAAM,WAAgC,CAAgB,CAC3D,QAAQ,EAAG,CACT,KAAK,IAAI,UAAU,EAAgB,IAAM,CACvC,IAAM,EAAW,KAAK,IAAI,OAAO,IAAoB,WAAY,CAAC,CAAC,EAC7D,EAAS,EAAa,GAAuB,EAAG,CAAQ,EAkB9D,GAAI,GAAU,QAAU,QAAa,GAAU,SAAW,OACxD,EAAO,OAAS,IAAI,EAA0B,EAAS,KAAK,EACvD,QACL,GAAU,QAAU,QACpB,EAAS,kBAAkB,GAC3B,EAAS,OAAO,YAAc,EAAS,MAEvC,EAAI,QACF,yCAAyC,EAAS,kCAAkC,EAAS,OAAO,8DACtG,EAGF,OAAO,IAAI,EAAe,EAAQ,CAAC,IAAY,EAAI,QAAQ,CAAO,CAAC,EACpE,OAGG,KAAI,EAAG,CACX,IAAM,EAAW,KAAK,IAAI,KAAK,CAAc,EAC7C,GAAI,CAAC,EAAS,OAAO,QAAS,OAM9B,GAAI,CACF,MAAM,EAAS,QAAQ,EACvB,MAAO,EAAO,CACd,EAAI,MACF,kDACE,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,GAEzD,GAGN,CC3DO,MAAM,WAA4B,CAAgB,CACvD,QAAQ,EAAG,CACT,KAAK,IAAI,UACP,EACA,IAAM,IAAI,EAAY,KAAK,IAAI,OAAO,IAAgB,OAAQ,CAAC,CAAC,CAAC,CACnE,EAEJ,CCiBO,SAAS,EAAe,CAAC,EAA8B,CAC5D,MAAO,YAAY,IAmBd,SAAS,EAAc,CAC5B,EACA,EACe,CACf,GAAmB,CAAQ,EAE3B,IAA0B,YAApB,EACkB,QAAlB,EACoB,OAApB,GADS,EAET,EAAU,EAAS,MAAM,KAEzB,EAAM,cAAc,EAAI,CAC5B,YAAc,EACd,QAAU,EACV,OAAS,OAEH,IAAG,IAAI,EAAoB,CAC/B,IAAO,EAAW,GAAQ,GAAY,CAAO,EACvC,EAAQ,EAAI,CAAY,EAAE,UAAU,EAAW,CAAI,EACzD,MAAM,IAAI,EAAS,EAAE,OAAO,CAAK,EA6BnC,MAAM,CAAC,EAAc,CACnB,QAAQ,MACN,gBAAgB,EAAS,6BAA6B,kDACR,iEAE9C,CACF,EAGF,YAAY,CAAC,EAAc,CACzB,QAAQ,MACN,gBAAgB,EAAS,gCAAgC,iBACxC,4FAEjB,CACF,EAEJ,EAoBA,OALA,OAAO,eAAe,EAAK,OAAQ,CACjC,MAAO,GAAgB,EAAS,IAAI,EACpC,SAAU,EACZ,CAAC,EAEM,EAoBT,SAAS,EAAkB,CAAC,EAA+B,CACzD,GAAI,OAAO,yBAAyB,EAAU,MAAM,GAAG,SAAU,OAEjE,MAAU,MACR,gBAAgB,EAAS,8GAEvB,0EACA,uDAAuD,EAAS,QACpE,EAmBF,SAAS,EAAW,CAAC,EAAyC,CAC5D,OAAQ,OAAO,EAAQ,KAAO,SAAW,EAAU,EAAQ,GClHtD,MAAM,CAAa,OACjB,OAAQ,SAGP,UAA4C,CAAC,EAW7C,OAAqC,CAAC,EAMtC,UAAY,IAAI,IAShB,mBAAqB,IAAI,IAExB,OAET,WAAW,CAAC,EAAsB,CAAC,EAAG,CACpC,KAAK,OAAS,EAAa,EAAoB,EAAG,CAAM,EACxD,KAAK,aAAa,KAAK,OAAO,SAAS,EA0BzC,YAAY,CAAC,EAA4B,CACvC,KAAK,OAAO,UAAY,EAExB,KAAK,UAAY,CAAC,EAClB,KAAK,OAAS,CAAC,EACf,IAAM,EAAU,IAAI,IAEpB,QAAW,KAAY,EAAW,CAOhC,GAAI,CAAC,EAAS,MAAO,CACnB,QAAQ,MACN,YAAY,EAAS,sJAGvB,EACA,SASF,GAAI,EAAQ,IAAI,EAAS,IAAI,EAAG,CAC9B,QAAQ,MACN,kCAAkC,EAAS,6BACzC,kIAEJ,EACA,SAEF,EAAQ,IAAI,EAAS,IAAI,EAUzB,IAAM,EAAO,EAAS,MAAM,KACtB,EAAW,IAAI,EAErB,KAAK,OAAO,KAAU,EAAS,OAC9B,KAAK,UAAU,KAAU,CAAC,GAAG,KAAK,CACjC,WACA,IAAK,EAAS,OAAS,GAAe,EAAU,CAAQ,EAAI,IAC9D,CAAC,MAcD,mBAAkB,EAAiC,CACrD,OAAO,OAAO,OAAO,KAAK,SAAS,EAChC,KAAK,EACL,QAAQ,EAAG,SAAW,EAAM,CAAC,CAAG,EAAI,CAAC,CAAE,KAcxC,oBAAmB,EAAiC,CACtD,MAAO,CAAC,GAAG,KAAK,OAAO,SAAS,EAgBlC,QAAQ,CAAC,EAAc,EAAiC,CACtD,GAAI,KAAK,cAAc,EAAO,EAAM,EAAK,EAAG,OACvC,KAAK,aAAa,EAAO,CAAI,OAa9B,gBAAe,CACnB,EACA,EACe,CACf,GAAI,KAAK,cAAc,EAAO,EAAM,EAAI,EAAG,OAC3C,OAAO,KAAK,aAAa,EAAO,CAAI,EAwB9B,aAAa,CACnB,EACA,EACA,EACS,CAKT,IAAQ,OAAM,eAAgB,EAAM,YAIpC,GAAI,CAAC,EAAa,MAAO,GAEzB,IAAM,EAAW,GAAiB,IAAM,KAAK,aAAa,EAAO,CAAI,CAAC,EACtE,GAAI,GAAY,EAAS,KAAK,uBAAuB,CAAI,EACzD,OAAO,OAkEO,aAAY,CAC1B,EACA,EACe,CACf,IAAM,EAAQ,EAAM,YAAiC,KAC/C,EAAW,KAAK,UAAU,GAEhC,GAAI,CAAC,GAAU,OAAQ,CACrB,KAAK,uBAAuB,CAAI,EAChC,OAGF,QAAa,SAAU,EAAU,SAAS,EAAU,CAClD,GAAI,EAAK,CACP,KAAK,QAAQ,EAAK,EAAU,EAAM,CAAI,EACtC,SAGF,GAAI,CACF,MAAM,IAAI,EAAS,EAAE,OAAO,CAAK,EACjC,MAAO,EAAO,CACd,QAAQ,MACN,gBAAgB,EAAS,6BAA6B,UACpD,uEACF,CACF,IAsBN,SAAS,CAAC,EAAmB,EAA2B,CAAC,EAAU,CACjE,IAAM,EAAa,KAAK,OAAO,GAE/B,GAAI,CAAC,EACH,MAAU,MACR,6BAA6B,uKAG3B,wEACA,+FAC6B,OACjC,EAGF,OAAO,IAAI,EAAW,GAAG,CAAI,EAkBvB,OAAO,CACb,EACA,EACA,EACA,EACA,CACA,GAAI,IAAS,OAAW,CACtB,QAAQ,MACN,gBAAgB,EAAS,uBAAuB,mKAGjB,sBAC1B,2CACP,EACA,OAGF,IAAM,EAAS,CAAC,IACd,QAAQ,MACN,gBAAgB,EAAS,gCAAgC,kFAGzD,CACF,EAKF,GAAI,CAIF,EAAI,CAAY,EACb,KAAK,EAAK,KAAK,UAAU,CAAC,EAAW,CAAI,CAAC,EAAG,CAAE,cAAe,EAAM,CAAC,EACrE,MAAM,CAAM,EACf,MAAO,EAAO,CACd,EAAO,CAAK,GAmBR,sBAAsB,CAAC,EAAc,CAC3C,GAAI,mBAAyB,aAAc,OAC3C,GAAI,KAAK,mBAAmB,IAAI,CAAI,EAAG,OACvC,KAAK,mBAAmB,IAAI,CAAI,EAEhC,QAAQ,KACN,UAAU,2DACL,4DACH,oEACA,uIAGJ,EAGM,sBAAsB,CAAC,EAAc,CAC3C,GAAI,mBAAyB,aAAc,OAC3C,GAAI,KAAK,UAAU,IAAI,CAAI,EAAG,OAC9B,KAAK,UAAU,IAAI,CAAI,EAEvB,QAAQ,KACN,UAAU,gLAEgD,gBAChD,+BAAkC,yBAC9C,EAEJ,CChgBO,MAAM,WAA6B,CAAgB,CACxD,QAAQ,EAAG,CACT,KAAK,IAAI,UACP,EACA,IAAM,IAAI,EAAa,KAAK,IAAI,OAAO,IAAiB,SAAU,CAAC,CAAC,CAAC,CACvE,OA2CI,KAAI,EAAG,CACX,IAAM,EAAU,KAAK,IAAI,KAAK,CAAY,EACpC,EAAQ,KAAK,IAAI,OAAO,IAAiB,SAAU,CAAC,CAAC,EAE3D,GAAI,EAAM,YAAc,OAAW,CACjC,IAAQ,gBAAiB,EAAa,EAAoB,EAAG,CAAK,EAClE,EAAQ,aAAa,MAAM,GAAkB,CAAY,CAAC,EAM5D,IAAM,EAAO,EAAQ,mBACrB,GAAI,EAAK,SAAW,EAAG,OAEvB,IAAM,EAAQ,KAAK,IAAI,KAAK,CAAY,EACxC,QAAW,KAAO,EAChB,EAAM,YAAY,CAAG,EAG3B,CCvEO,MAAM,WAAkC,CAAgB,CAC7D,QAAQ,EAAG,CACT,KAAK,IAAI,UACP,EACA,IACE,IAAI,EACF,KAAK,IAAI,OAAO,IAAsB,aAAc,CAAC,CAAC,CACxD,CACJ,EAEJ,CCVO,MAAM,WAA6B,CAAgB,CACxD,QAAQ,EAAG,CACT,KAAK,IAAI,UACP,EACA,IAAM,IAAI,EAAa,KAAK,IAAI,OAAO,IAAiB,QAAS,CAAC,CAAC,CAAC,CACtE,EAEJ,CCPO,MAAM,WAAgC,CAAgB,CAC3D,QAAQ,EAAG,CACT,KAAK,IAAI,SAAS,EAAU,IAAI,CAAU,EAE9C,CCEO,SAAS,EAAe,CAAC,EAA8B,CAC5D,OAAO,EAGF,SAAS,EAAiB,EAAwB,CACvD,MAAO,CACL,YAAa,SACb,gBAAiB,IAAM,GACvB,aAAc,IAAM,EACtB,ECdK,MAAM,WAA2B,CAAgB,CACtD,QAAQ,EAAG,CACT,KAAK,IAAI,UACP,EACA,IACE,IAAI,EACF,EACE,GAAkB,EAClB,KAAK,IAAI,OAAO,IAAe,MAAO,CAAC,CAAC,CAC1C,CACF,CACJ,OAoBI,KAAI,EAAG,CACX,GAAI,CAAC,QAAQ,IAAI,SACf,OAEF,MAAM,KAAK,IAAI,KAAK,CAAU,EAAE,KAAK,EAEzC,CClCO,MAAM,WAAkC,CAAgB,CAC7D,QAAQ,EAAG,CACT,KAAK,IAAI,UACP,EACA,IACE,IAAI,EACF,EACE,GAAyB,EACzB,KAAK,IAAI,OAAO,IAAsB,aAAc,CAAC,CAAC,CACxD,CACF,CACJ,EAGF,IAAI,EAAG,CAGL,KAAK,IAAI,KAAK,CAAkB,EAAE,uBAAuB,EAE7D,CCTO,SAAS,EAAuB,CACrC,EACmB,CACnB,OAAO,EAGF,SAAS,EAAyB,EAAgC,CACvE,MAAO,CACL,OAAQ,IAAI,GACZ,MAAO,KACP,OAAQ,EACV,ECxBK,MAAM,WAAmC,CAAgB,CAC9D,QAAQ,EAAG,CACT,KAAK,IAAI,UACP,EACA,IACE,IAAI,EACF,EACE,GAA0B,EAC1B,KAAK,IAAI,OAAO,IAAuB,cAAe,CAAC,CAAC,CAC1D,CACF,CACJ,EAEJ,CCLO,SAAS,EAAiB,CAAC,EAAkC,CAClE,OAAO,EAGF,SAAS,EAAmB,EAAgB,CACjD,MAAO,CACL,IAAK,QAAQ,IAAI,UACjB,QAAS,MACX,EChBK,MAAM,WAA6B,CAAgB,CACxD,QAAQ,EAAG,CACT,KAAK,IAAI,UACP,EACA,IACE,IAAI,EACF,EACE,GAAoB,EACpB,KAAK,IAAI,OAAO,IAAiB,QAAS,CAAC,CAAC,CAC9C,CACF,CACJ,EAQF,QAAQ,EAAG,CACT,GAAI,CAAC,KAAK,IAAI,SAAS,CAAY,EAAG,OACtC,KAAK,IAAI,KAAK,CAAY,EAAE,MAAM,EAEtC,CC4GO,MAAM,UAAqB,CAAU,CAG/B,OAFX,WAAW,CACT,EACS,EACT,CACA,MAAM,CAAO,EAFJ,cAGT,KAAK,KAAO,eAEhB,CAGA,IAAM,GAAY,wBAGZ,GAAa,qCAOb,GAAiB,KASjB,EAAkB,IAGlB,GAAW,kDA8BV,MAAM,CAAuD,CAc/C,OACA,iBAdZ,OAAQ,aAUE,MAAQ,IAAI,IAE7B,WAAW,CACQ,EACA,EAIjB,CALiB,cACA,kBAKjB,QAAY,EAAM,KAAgB,OAAO,QAAQ,EAAO,QAAU,CAAC,CAAC,EAClE,KAAK,MAAM,IAAI,EAAM,KAAK,KAAK,EAAM,CAAW,CAAC,EASrD,IAAI,CAAC,EAAmB,EAA6C,CAEnE,OADA,GAAY,CAAM,EACX,KAAK,YAAY,CAAM,EAQhC,WAAW,CAAC,EAA6C,CACvD,IAAM,EAA2B,CAAC,EAClC,QAAa,gBAAgB,KAAK,MAAM,OAAO,EAAG,CAChD,GAAI,GAAQ,OAAS,CAAC,EAAO,MAAM,SAAS,EAAW,IAAI,EAAG,SAC9D,GAAI,GAAQ,MAAQ,CAAC,EAAW,KAAK,KAAK,CAAC,IAAQ,EAAO,KAAK,SAAS,CAAG,CAAC,EAAG,SAC/E,EAAI,KAAK,CAAU,EAErB,OAAO,OA4BH,QAAO,CACX,EACA,EACA,EACA,EACkB,CAClB,GAAY,CAAM,EAClB,IAAM,EAAO,KAAK,MAAM,IAAI,CAAI,EAChC,GAAI,CAAC,EACH,MAAU,MAAM,wCAAwC,KAAQ,EAElE,IAAQ,cAAe,EAEjB,EAAS,EAAW,YAAY,UAAU,CAAI,EACpD,GAAI,EAAO,KAAO,GAChB,MAAM,IAAI,EAAa,0BAA0B,OAAU,EAAO,OAAO,KAAK,IAAI,GAAG,EAEvF,IAAM,EAAQ,EAAO,MAEf,EAAuB,CAAE,SAAQ,IAAK,EAAO,IAAK,KAAM,EAAY,QAAO,KAAI,EAC/E,EAAO,MAAM,KAAK,SAAS,EAAM,CAAI,GAMnC,KAAM,GAAY,KAAK,WAAW,yBACxC,IAAI,QAAQ,2BAA2B,GAAM,CAC/C,EACA,GAAI,IAAY,EAAW,IACzB,MAAM,IAAI,EAAa,IAAI,qBAAwB,KAAS,GAAG,EAGjE,IAAM,EAAgC,CAAC,EACvC,QAAW,KAAO,EAAK,SACrB,GAAI,EAAM,KAAS,OAAW,EAAK,GAAO,EAAM,GAGlD,IAAI,EACA,EAAQ,GACZ,GAAI,EAAK,WAAW,OAAS,EAC3B,EAAO,MAAM,KAAK,SAAS,EAAM,EAAM,CAAI,EACtC,QAAI,EAAW,SAAW,MAC/B,EAAQ,GAAQ,CAAI,EACf,QAAI,EAAK,SAAS,OAAS,EAChC,EAAO,EAGT,IAAM,EAAc,KAAK,OAAO,YAC5B,MAAM,KAAK,KAAK,EAAM,kBAAmB,IAAM,KAAK,OAAO,YAAa,CAAI,CAAC,EAC7E,OAEA,EACJ,GAAI,CACF,IAAM,EAAS,EAAQ,GAAG,KAAQ,IAAU,EAC5C,EAAW,EACP,MAAM,KAAK,WAAW,WAAW,EAAO,IAAK,EAAW,OAAQ,EAAQ,EAAM,CAC5E,aACF,CAAC,EACD,MAAM,KAAK,WAAW,WAAW,EAAO,IAAK,EAAW,OAAQ,EAAQ,CAAI,EAChF,MAAO,EAAO,CAId,MADA,QAAQ,MAAM,2BAA2B,aAAiB,CAAK,EACzD,IAAI,EAAa,IAAI,2BAA+B,GAAG,EAE/D,GAAI,CAAC,EAAK,QAAU,CAAC,EAAW,aAC9B,OAAO,MAAM,GAAa,EAAM,CAAQ,EAE1C,OAAO,MAAM,KAAK,QAAQ,EAAM,EAAM,CAAQ,EAKxC,IAAI,CAAC,EAAc,EAAwC,CACjE,GAAI,CAAC,GAAU,KAAK,CAAI,EACtB,MAAU,MACR,eAAe,kKACjB,EAEF,IAAM,EAAQ,eAAe,OAAU,GAAa,UAAU,GAAa,OAC3E,GAAI,EAAE,aAAuB,SAAW,EAAY,mBAAqB,WACvE,MAAU,MACR,eAAe,4DACjB,EAEF,IAAQ,SAAQ,MAAK,QAAS,EAC9B,GAAI,EAAI,WAAW,WAAW,EAC5B,MAAU,MAAM,GAAG,2CAA+C,EAGpE,IAAM,EAAQ,KAAK,WAAW,WAAW,KAAO,GAChD,GAAI,CAAC,EACH,MAAU,MACR,GAAG,qFACL,EAKF,IAAM,EAAsB,CAAC,GAAG,EAAI,SAAS,EAAU,CAAC,EAAE,IAAI,GAAI,EAAO,MAAe,CACtF,KAAM,EACN,UACF,EAAE,EACI,EAAW,EAAK,QAAU,CAAC,EACjC,QAAW,KAAS,EAClB,GAAI,EAAE,EAAM,QAAQ,GAClB,MAAU,MACR,GAAG,sBAA0B,EAAM,0CACrC,EAGJ,QAAY,EAAK,KAAW,OAAO,QAAQ,CAAQ,EAAG,CACpD,GAAI,CAAC,EAAO,KAAK,CAAC,IAAU,EAAM,OAAS,CAAG,EAC5C,MAAU,MAAM,GAAG,OAAW,yCAA2C,EAE3E,GAAI,GAAa,CAAM,EACrB,GAAiB,EAAO,UAAU,IAAO,CAAM,EAE/C,QAAa,EAAO,UAAU,IAAO,CAAM,EAI/C,IAAI,EAAqB,CAAC,EACtB,EACJ,GAAI,EAAK,MAAO,CAEd,GADA,EAAO,EAAK,MAAM,aAAa,EAC3B,EAAK,OAAS,UAAY,CAAC,EAAK,WAClC,MAAU,MACR,GAAG,oEACL,EAEF,EAAW,OAAO,KAAK,EAAK,UAAU,EAGxC,IAAM,EAAa,OAAO,QAAQ,EAAK,OAAS,CAAC,CAAC,EAAE,IAAI,EAAE,EAAO,MAAa,CAC5E,KAAM,EACN,QACF,EAAE,EACF,QAAW,KAAQ,EACjB,GAAa,EAAO,SAAS,EAAK,OAAQ,EAAK,MAAM,EAEvD,GAAI,EAAK,SAAW,QAAa,OAAO,EAAK,SAAW,WACtD,MAAU,MAAM,GAAG,+BAAmC,EAExD,GAAI,EAAK,SAAW,QAAa,OAAO,EAAK,QAAQ,YAAc,WACjE,MAAU,MAAM,GAAG,2CAA+C,EAEpE,GAAI,EAAW,OAAS,GAAK,IAAW,MACtC,MAAU,MAAM,GAAG,uCAA2C,EAGhE,IAAM,EAAoC,CAAC,EAGrC,EAAW,CAAC,EAAa,IAAsB,CACnD,GAAI,KAAO,EACT,MAAU,MACR,GAAG,OAAW,8EAChB,EAEF,EAAO,GAAO,GAEV,EAAe,IAAI,IACnB,EAAc,IAAI,IACxB,QAAW,KAAS,EAAQ,CAC1B,IAAM,EAAS,EAAS,EAAM,MAC9B,GAAI,IAAW,QAAS,CACtB,IAAM,EAAQ,EAAE,OAAO,EAAE,SAAS,SAAS,EAAM,2BAA2B,EAC5E,EACE,EAAM,KACN,EAAM,WAAa,KAAO,EAAM,WAAa,IAAM,EAAM,SAAS,EAAI,CACxE,EACK,QAAI,GAAa,CAAM,EAAG,CAC/B,IAAM,EAAM,EAAO,IAAM,EAAM,KAC/B,EAAS,EAAK,EAAO,KAAK,EAC1B,EAAY,IAAI,EAAM,KAAM,CAAE,MAAK,MAAO,CAAO,CAAC,EAElD,OAAa,IAAI,EAAM,KAAM,CAAM,EAGvC,QAAW,KAAQ,EACjB,GAAI,EAAK,SAAW,QAClB,EACE,EAAK,KACL,EACG,OAAO,EACP,SACC,mHACF,CACJ,EAKJ,IAAM,EAAW,CAAC,GAAG,OAAO,KAAK,CAAM,EAAG,GAAG,EAAW,IAAI,CAAC,IAAS,EAAK,IAAI,CAAC,EAChF,QAAW,KAAO,IAAI,IAAI,CAAQ,EAChC,GAAI,EAAS,SAAS,CAAG,EACvB,MAAU,MACR,GAAG,OAAW,iFAChB,EAIJ,MAAO,CACL,WAAY,OAAO,OAAO,CACxB,OACA,YAAa,EAAK,YAClB,SACA,MACA,YAAa,GAAe,EAAK,MAAO,EAAM,EAAE,OAAO,CAAM,CAAC,KAC1D,EAAK,OAAS,CAAE,aAAc,EAAK,MAAO,EAAI,CAAC,EACnD,YAAa,GAAe,CAAM,EAClC,KAAM,OAAO,OAAO,CAAC,GAAI,EAAK,MAAQ,CAAC,CAAE,CAAC,EAC1C,iBAAkB,EAAK,mBAAqB,MACxC,EAAM,OAAS,CAAE,OAAQ,EAAM,MAAO,EAAI,CAAC,CACjD,CAAC,EACD,SACA,eACA,cACA,aACA,cACI,EAAK,OAAS,CAAE,OAAQ,EAAK,MAAO,EAAI,CAAC,CAC/C,OAKY,SAAQ,CAAC,EAAY,EAAuC,CACxE,IAAQ,QAAO,OAAQ,EACnB,EAAO,EAAK,WAAW,IAC3B,QAAW,KAAS,EAAK,OAAQ,CAC/B,IAAM,EAAS,EAAK,aAAa,IAAI,EAAM,IAAI,EACzC,EAAa,EAAK,YAAY,IAAI,EAAM,IAAI,EAC9C,EACJ,GAAI,GAAU,GASZ,GARA,EAAQ,EACJ,MAAM,KAAK,KACT,EACA,cAAc,EAAM,QACpB,IAAM,EAAW,MAAM,KAAK,EAAM,EAAW,KAAM,EAAK,CAAI,EAC5D,CAAE,SAAU,EAAK,CACnB,EACA,MAAM,KAAK,KAAK,EAAM,cAAc,EAAM,QAAS,IAAM,EAAQ,EAAK,CAAI,CAAC,EAC3E,IAAU,QAAa,IAAU,MAAQ,IAAU,GAIrD,MAHA,QAAQ,MACN,kBAAkB,EAAa,WAAa,iBAAiB,EAAM,aAAa,EAAK,WAAW,kBAAkB,KAAK,UAAU,CAAK,IACxI,EACM,IAAI,EAAa,IAAI,EAAK,WAAW,8BAA+B,GAAG,EAG/E,OAAQ,EAAM,EAAM,MAGtB,IAAM,EAAQ,IAAI,EAAM,OAAO,EAAM,WACrC,GAAI,IAAU,QAAa,IAAU,MAAQ,IAAU,GAAI,CACzD,GAAI,EAAM,WAAa,KAAO,EAAM,WAAa,IAC/C,MAAM,IAAI,EAAa,IAAI,EAAM,aAAa,EAAK,WAAW,0BAA0B,EAE1F,EAAO,EAAK,QAAQ,IAAI,IAAS,EAAE,EACnC,SAEF,EAAO,EAAK,QAAQ,EAAO,GAAc,EAAK,WAAW,KAAM,EAAO,OAAO,CAAK,CAAC,CAAC,EAEtF,OAAO,OAGK,SAAQ,CACpB,EACA,EACA,EACmB,CACnB,IAAQ,QAAO,OAAQ,EACjB,EAAO,EAAK,WAAW,KAC7B,GAAI,CAAC,EACH,MAAU,MACR,iBAAiB,8GACnB,EAEF,IAAM,EAAO,IAAI,SACjB,QAAY,EAAK,KAAU,OAAO,QAAQ,CAAI,EAC5C,GAAY,EAAM,EAAK,CAAK,EAE9B,QAAW,KAAS,EAAK,WAAY,CACnC,IAAM,EACJ,EAAM,SAAW,QACZ,EAAM,EAAM,MACb,MAAM,KAAK,KAAK,EAAM,aAAa,EAAM,QAAS,IAC/C,EAAM,OAAyB,EAAK,CAAI,CAC3C,EACN,GAAI,OAAO,IAAO,UAAY,IAAO,GACnC,MAAM,IAAI,EACR,IAAI,wBAA2B,EAAM,4DACvC,EAIF,EAAK,OAAO,EAAM,KAAM,MAAM,EAAI,YAAY,KAAK,CAAE,CAAC,EAExD,OAAO,OAkBK,QAAO,CAAC,EAAY,EAAsB,EAAsC,CAC5F,IAAQ,OAAM,gBAAiB,EAAK,WACpC,GAAI,EAAS,OAAS,KAAO,EAAS,QAAU,IAC9C,OAAO,MAAM,GAAa,EAAM,CAAQ,EAE1C,IAAM,EAAS,CAAC,EAAa,IAAoB,CAC/C,GAAI,IAAU,OAAW,QAAQ,MAAM,eAAe,MAAS,GAAK,EAC/D,aAAQ,MAAM,eAAe,MAAS,IAAO,CAAK,EACvD,OAAO,IAAI,EAAa,IAAI,2BAA+B,GAAG,GAG1D,EAAO,EAAS,QAAQ,IAAI,cAAc,EAC1C,EAAO,MAAM,EAAS,KAAK,EAC7B,EAAgB,KACpB,GAAI,IAAS,GACX,GAAI,CACF,EAAO,KAAK,MAAM,CAAI,EACtB,KAAM,CACN,MAAM,EACJ,YAAY,GAAQ,sFACtB,EAIJ,IAAI,EAAQ,EACZ,GAAI,EAAK,OACP,GAAI,CACF,EAAQ,MAAM,EAAK,OAAO,EAAM,CAAI,EACpC,MAAO,EAAO,CACd,MAAM,EAAO,mCAAoC,CAAK,EAG1D,GAAI,EAAc,CAChB,IAAM,EAAS,EAAa,UAAU,CAAK,EAC3C,GAAI,EAAO,KAAO,GAChB,MAAM,EAAO,4CAA4C,EAAO,OAAO,KAAK,IAAI,GAAG,EAErF,EAAQ,EAAO,MAGjB,GAAI,IAAU,OAAW,OAAO,KAChC,IAAM,EAAa,KAAK,UAAU,CAAK,EACvC,GAAI,IAAe,QAAa,EAAW,OAAS,EAClD,MAAO,GAAG,EAAW,MAAM,EAAG,CAAe,mBAAa,QAAsB,EAAW,qBAE7F,OAAO,OAcK,KAAO,CACnB,EACA,EACA,EACA,EAAkC,CAAC,EACvB,CACZ,GAAI,CACF,OAAO,MAAM,EAAG,EAChB,MAAO,EAAO,CACd,GAAI,EAAQ,UAAY,aAAiB,EAAW,MAAM,EAE1D,MADA,QAAQ,MAAM,sBAAsB,SAAY,EAAK,WAAW,gBAAiB,CAAK,EAChF,IAAI,EAAa,IAAI,EAAK,WAAW,8BAA+B,GAAG,GAGnF,CAEA,SAAS,EAAW,CAAC,EAA4E,CAC/F,GAAI,GAAQ,OAAS,QACnB,MAAU,MACR,0LACF,EAKJ,SAAS,EAAY,CAAC,EAAe,EAAY,EAAiB,CAChE,GAAI,IAAW,SAAW,OAAO,IAAW,WAC1C,MAAU,MAAM,GAAG,MAAU,kCAAmC,EAQpE,SAAS,EAAY,CAAC,EAAkD,CACtE,OAAO,aAAiB,QAAW,EAAwB,mBAAqB,gBAIlF,SAAS,EAAgB,CAAC,EAAe,EAAY,EAAgC,CACnF,GAAI,EAAM,KAAO,SAAc,OAAO,EAAM,KAAO,UAAY,EAAM,KAAO,IAC1E,MAAU,MAAM,GAAG,MAAU,kCAAmC,EAElE,IAAM,EAAS,EAAM,MACrB,GAAI,OAAO,GAAQ,YAAc,YAAc,OAAO,EAAO,eAAiB,WAC5E,MAAU,MAAM,GAAG,MAAU,yCAA0C,EAEzE,GAAI,OAAO,EAAM,OAAS,WACxB,MAAU,MAAM,GAAG,MAAU,4BAA6B,EAI9D,SAAS,EAAc,CAAC,EAAuC,CAC7D,OAAQ,OACD,MACH,OAAO,OAAO,OAAO,CAAE,aAAc,EAAc,CAAC,MACjD,SACH,OAAO,OAAO,OAAO,CAAE,gBAAiB,EAAc,CAAC,MACpD,MACH,OAAO,OAAO,OAAO,CAAE,eAAgB,EAAc,CAAC,UAEtD,OAAO,OAAO,OAAO,CAAC,CAAC,GAe7B,SAAS,EAAc,CACrB,EACA,EACA,EACiC,CACjC,IAAM,EAAQ,EAAO,aAAa,EAC5B,EAAmB,IACnB,GAAQ,CAAC,EACb,KAAM,SACN,WAAY,IAAM,GAAM,YAAc,CAAC,KAAO,EAAM,UAAW,EAC/D,SAAU,CAAC,GAAI,GAAM,UAAY,CAAC,EAAI,GAAI,EAAM,UAAY,CAAC,CAAE,EAC/D,qBAAsB,EACxB,EAEM,EAAY,CAAC,IAAmB,CACpC,GAAI,OAAO,IAAU,UAAY,IAAU,MAAQ,MAAM,QAAQ,CAAK,EACpE,MAAO,CAAE,GAAI,GAAgB,OAAQ,CAAC,oBAAoB,CAAE,EAE9D,IAAM,EAAmB,CAAC,EACtB,EAA+B,CAAC,EACpC,GAAI,EAAO,CACT,IAAM,EAAM,EAAM,UAAU,CAAK,EACjC,GAAI,EAAI,KAAO,GAAO,EAAO,KAAK,GAAG,EAAI,MAAM,EAC1C,OAAM,IAAK,EAAI,KAAM,EAE5B,IAAM,EAAO,EAAO,UAAU,CAAK,EACnC,GAAI,EAAK,KAAO,GAAO,EAAO,KAAK,GAAG,EAAK,MAAM,EAC5C,OAAM,IAAK,KAAQ,EAAK,KAAM,EACnC,OAAO,EAAO,OAAS,EAAI,CAAE,GAAI,GAAgB,QAAO,EAAI,CAAE,GAAI,GAAe,MAAO,CAAI,GAG9F,MAAO,CACL,aAAc,IAAM,EACpB,YACA,KAAK,CAAC,EAAgB,CACpB,IAAM,EAAS,EAAU,CAAK,EAC9B,GAAI,EAAO,KAAO,GAAO,MAAU,MAAM,EAAO,OAAO,KAAK,IAAI,CAAC,EACjE,OAAO,EAAO,MAElB,EAQF,SAAS,EAAa,CAAC,EAAc,EAAkB,EAAuB,CAC5E,IAAM,EAAQ,EAAM,WAAa,KAAO,EAAM,WAAa,IAAM,EAAM,MAAM,GAAG,EAAI,CAAC,CAAK,EAC1F,GAAI,EAAM,KAAK,CAAC,IAAS,IAAS,KAAO,IAAS,IAAI,EACpD,MAAM,IAAI,EAAa,IAAI,gCAAoC,EAAM,aAAa,KAAQ,EAE5F,OAAO,EAAM,IAAI,kBAAkB,EAAE,KAAK,GAAG,EAS/C,SAAS,EAAW,CAAC,EAAgB,EAAa,EAAgB,CAChE,GAAI,IAAU,QAAa,IAAU,KAAM,OAC3C,GAAI,MAAM,QAAQ,CAAK,EAAG,CACxB,QAAW,KAAQ,EAAO,GAAY,EAAM,EAAK,CAAI,EACrD,OAEF,EAAK,OAAO,EAAK,OAAO,IAAU,SAAW,KAAK,UAAU,CAAK,EAAI,OAAO,CAAK,CAAC,EAIpF,SAAS,EAAO,CAAC,EAAuC,CACtD,IAAM,EAAS,IAAI,gBACb,EAAM,CAAC,EAAa,IAAmB,CAC3C,GAAI,IAAU,QAAa,IAAU,KAAM,OAC3C,GAAI,MAAM,QAAQ,CAAK,EAAG,CACxB,QAAW,KAAQ,EAAO,EAAI,EAAK,CAAI,EACvC,OAEF,EAAO,OAAO,EAAK,OAAO,IAAU,SAAW,KAAK,UAAU,CAAK,EAAI,OAAO,CAAK,CAAC,GAEtF,QAAY,EAAK,KAAU,OAAO,QAAQ,CAAI,EAAG,EAAI,EAAK,CAAK,EAC/D,OAAO,EAAO,SAAS,EAGzB,eAAe,EAAY,CAAC,EAAc,EAAsC,CAC9E,GAAI,EAAS,QAAU,KAAO,EAAS,OAAS,IAAK,CACnD,IAAM,EAAO,EAAS,QAAQ,IAAI,cAAc,EAChD,GAAI,GAAQ,CAAC,GAAS,KAAK,CAAI,EAAG,CAChC,IAAM,GAAQ,MAAM,EAAS,YAAY,GAAG,WAC5C,MAAO,IAAI,oBAAuB,cAAiB,yBAErD,IAAM,EAAO,MAAM,EAAS,KAAK,EACjC,GAAI,IAAS,GAAI,OAAO,KACxB,GAAI,EAAK,OAAS,EAChB,MAAO,GAAG,EAAK,MAAM,EAAG,CAAe,mBAAa,QAAsB,EAAK,qBAEjF,GAAI,CACF,OAAO,KAAK,MAAM,CAAI,EACtB,KAAM,CACN,OAAO,GAGX,IAAM,EAAO,MAAM,EAAS,KAAK,EACjC,GAAI,EAAS,QAAU,KAAO,EAAS,OAAS,IAAK,CACnD,IAAM,EAAQ,EAAK,OAAS,GAAiB,GAAG,EAAK,MAAM,EAAG,EAAc,UAAM,EAClF,MAAM,IAAI,EACR,IAAI,uBAA0B,EAAS,SAAS,EAAQ,KAAK,IAAU,MACvE,EAAS,MACX,EAEF,GAAI,EAAS,OAAS,IAGpB,QAAQ,MACN,eAAe,eAAkB,EAAS,SAAS,EAAS,QAAQ,IAAI,UAAU,EAAI,OAAO,EAAS,QAAQ,IAAI,UAAU,IAAM,sCACpI,EAEF,MAAM,IAAI,EAAa,IAAI,2BAA+B,EAAS,MAAM,ECr0B3E,SAAS,CAAa,CAAC,EAAoB,EAAgB,CACzD,IAAM,EAAQ,EAAO,IAAO,CAAG,EAC/B,GAAI,CAAC,EACH,MAAU,MACR,YAAY,qGACd,EAEF,OAAO,EAQF,MAAM,WAA6B,CAAgB,CACxD,QAAQ,EAAG,CACT,KAAK,IAAI,UACP,EACA,IACE,IAAI,EACF,EAA2B,KAAK,IAAI,OAAQ,WAAW,CACzD,CACJ,EAEA,KAAK,IAAI,UACP,EACA,IACE,IAAI,EACF,EAA4B,KAAK,IAAI,OAAQ,YAAY,CAC3D,CACJ,EAIA,KAAK,IAAI,UACP,EACA,IACE,IAAI,EACF,CAAE,IAAK,KAAK,IAAI,KAAK,CAAkB,EAAG,KAAM,KAAK,IAAI,KAAK,CAAmB,CAAE,EACnF,CACE,IAAK,EAA2B,KAAK,IAAI,OAAQ,WAAW,EAC5D,KAAM,EAA4B,KAAK,IAAI,OAAQ,YAAY,EAC/D,QAAS,KAAK,IAAI,OAAO,IAA+B,eAAe,CACzE,CACF,CACJ,EAIA,KAAK,IAAI,UAAU,EAAa,IAAM,CACpC,IAAM,EAAS,KAAK,IAAI,OAAO,IAAgC,WAAW,EAC1E,GAAI,CAAC,GAAQ,OACX,MAAU,MACR,yFACF,EAEF,OAAO,IAAI,EAAY,IAAI,EAAO,OAAU,KAAK,IAAI,KAAK,CAAkB,CAAC,EAC9E,EAYH,IAAI,EAAG,CAKL,GAJA,KAAK,IAAI,KAAK,CAAkB,EAChC,KAAK,IAAI,KAAK,CAAmB,EAEjC,KAAK,IAAI,KAAK,CAAY,EACtB,KAAK,IAAI,OAAO,IAAI,WAAW,EACjC,KAAK,IAAI,KAAK,CAAW,EAG/B",
|
|
31
|
+
"debugId": "E5CD51F5C6B715EF64756E2164756E21",
|
|
31
32
|
"names": []
|
|
32
33
|
}
|