gemi 0.122.0 → 0.123.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/index.js +2 -2
- package/dist/ai/index.js.map +1 -1
- package/dist/app/App.d.ts +9 -0
- package/dist/app/App.d.ts.map +1 -1
- package/dist/app/index.js +1 -1
- package/dist/auth/AuthController.d.ts.map +1 -1
- package/dist/bin/gemi.js +4 -1
- package/dist/bin/gemi.js.map +3 -3
- package/dist/bun/plugin.js +1 -1
- package/dist/bun/preload.js +1 -1
- package/dist/{chunk-dwedx1jb.js → chunk-08fcqny0.js} +3 -3
- package/dist/{chunk-dwedx1jb.js.map → chunk-08fcqny0.js.map} +1 -1
- package/dist/{chunk-vx7hvyvq.js → chunk-0qmc7snf.js} +2 -2
- package/dist/{chunk-vx7hvyvq.js.map → chunk-0qmc7snf.js.map} +1 -1
- package/dist/{chunk-qy8c0gzr.js → chunk-0tc0j3pf.js} +2 -2
- package/dist/{chunk-qy8c0gzr.js.map → chunk-0tc0j3pf.js.map} +1 -1
- package/dist/{chunk-meg8rtr6.js → chunk-1f1sb3w7.js} +2 -2
- package/dist/{chunk-meg8rtr6.js.map → chunk-1f1sb3w7.js.map} +1 -1
- package/dist/{chunk-d5f1m3d2.js → chunk-1kdga1b2.js} +2 -2
- package/dist/{chunk-d5f1m3d2.js.map → chunk-1kdga1b2.js.map} +1 -1
- package/dist/{chunk-48gmyaxc.js → chunk-2amj1eyp.js} +2 -2
- package/dist/{chunk-48gmyaxc.js.map → chunk-2amj1eyp.js.map} +1 -1
- package/dist/chunk-2vc72qez.js +9 -0
- package/dist/chunk-2vc72qez.js.map +10 -0
- package/dist/{chunk-9xyxbxgk.js → chunk-30xxc6r7.js} +2 -2
- package/dist/{chunk-9xyxbxgk.js.map → chunk-30xxc6r7.js.map} +1 -1
- package/dist/chunk-39dx7xrz.js +10 -0
- package/dist/{chunk-dyp0wg9e.js.map → chunk-39dx7xrz.js.map} +8 -4
- package/dist/{chunk-dhbgrv58.js → chunk-4qpypa3h.js} +1 -1
- package/dist/{chunk-wqe47c0j.js → chunk-52xkw2bs.js} +2 -2
- package/dist/{chunk-wqe47c0j.js.map → chunk-52xkw2bs.js.map} +1 -1
- package/dist/{chunk-3gsv1vv1.js → chunk-53hc0ah1.js} +2 -2
- package/dist/{chunk-3gsv1vv1.js.map → chunk-53hc0ah1.js.map} +1 -1
- package/dist/{chunk-qsz9241c.js → chunk-58ffbwdc.js} +2 -2
- package/dist/{chunk-qsz9241c.js.map → chunk-58ffbwdc.js.map} +1 -1
- package/dist/{chunk-0ejjgzwc.js → chunk-5a4qbtez.js} +2 -2
- package/dist/{chunk-0ejjgzwc.js.map → chunk-5a4qbtez.js.map} +1 -1
- package/dist/{chunk-7mmw0jr2.js → chunk-675kzd7z.js} +2 -2
- package/dist/{chunk-7mmw0jr2.js.map → chunk-675kzd7z.js.map} +1 -1
- package/dist/{chunk-dedqr5wb.js → chunk-6gjvqtcp.js} +2 -2
- package/dist/{chunk-dedqr5wb.js.map → chunk-6gjvqtcp.js.map} +1 -1
- package/dist/{chunk-6yhpprzs.js → chunk-6pxavyh3.js} +2 -2
- package/dist/{chunk-6yhpprzs.js.map → chunk-6pxavyh3.js.map} +1 -1
- package/dist/{chunk-h43jp8ws.js → chunk-73v34hw8.js} +2 -2
- package/dist/{chunk-h43jp8ws.js.map → chunk-73v34hw8.js.map} +1 -1
- package/dist/{chunk-wjkh2gem.js → chunk-8x73yyfs.js} +1 -1
- package/dist/{chunk-yds7zwc3.js → chunk-96a1459v.js} +2 -2
- package/dist/{chunk-yds7zwc3.js.map → chunk-96a1459v.js.map} +1 -1
- package/dist/{chunk-d2c3947c.js → chunk-9a9ef0f1.js} +2 -2
- package/dist/{chunk-d2c3947c.js.map → chunk-9a9ef0f1.js.map} +1 -1
- package/dist/{chunk-ep0baa9g.js → chunk-9p7eq7bz.js} +2 -2
- package/dist/{chunk-ep0baa9g.js.map → chunk-9p7eq7bz.js.map} +1 -1
- package/dist/chunk-9t08wbkd.js +58 -0
- package/dist/{chunk-3rvn9a76.js.map → chunk-9t08wbkd.js.map} +3 -3
- package/dist/{chunk-pn2x59ge.js → chunk-aafanq8n.js} +3 -3
- package/dist/{chunk-pn2x59ge.js.map → chunk-aafanq8n.js.map} +1 -1
- package/dist/{chunk-tewhms9w.js → chunk-ay8nat73.js} +2 -2
- package/dist/{chunk-tewhms9w.js.map → chunk-ay8nat73.js.map} +1 -1
- package/dist/{chunk-shfe0b22.js → chunk-bk2aheea.js} +2 -2
- package/dist/{chunk-shfe0b22.js.map → chunk-bk2aheea.js.map} +1 -1
- package/dist/{chunk-mr94nfp1.js → chunk-d0wr9jk4.js} +3 -3
- package/dist/{chunk-mr94nfp1.js.map → chunk-d0wr9jk4.js.map} +1 -1
- package/dist/chunk-d3ywmeb7.js +4 -0
- package/dist/chunk-d3ywmeb7.js.map +10 -0
- package/dist/{chunk-zefs2b6h.js → chunk-de7hxe73.js} +2 -2
- package/dist/{chunk-zefs2b6h.js.map → chunk-de7hxe73.js.map} +1 -1
- package/dist/{chunk-er8acmch.js → chunk-dmj6ysym.js} +2 -2
- package/dist/{chunk-er8acmch.js.map → chunk-dmj6ysym.js.map} +1 -1
- package/dist/{chunk-rhek27sj.js → chunk-ebx5hnqn.js} +3 -3
- package/dist/{chunk-rhek27sj.js.map → chunk-ebx5hnqn.js.map} +1 -1
- package/dist/chunk-eevna943.js +5 -0
- package/dist/chunk-eevna943.js.map +10 -0
- package/dist/{chunk-9rr5h818.js → chunk-enta97jb.js} +3 -3
- package/dist/{chunk-9rr5h818.js.map → chunk-enta97jb.js.map} +1 -1
- package/dist/{chunk-s5k3yq6t.js → chunk-f1thqe6j.js} +2 -2
- package/dist/{chunk-s5k3yq6t.js.map → chunk-f1thqe6j.js.map} +1 -1
- package/dist/{chunk-zrcd9hxr.js → chunk-fsy344zb.js} +2 -2
- package/dist/{chunk-zrcd9hxr.js.map → chunk-fsy344zb.js.map} +1 -1
- package/dist/{chunk-gemzbw7f.js → chunk-fxxbwyce.js} +2 -2
- package/dist/{chunk-gemzbw7f.js.map → chunk-fxxbwyce.js.map} +1 -1
- package/dist/{chunk-1wnnkde0.js → chunk-fyk07w01.js} +3 -3
- package/dist/{chunk-1wnnkde0.js.map → chunk-fyk07w01.js.map} +1 -1
- package/dist/chunk-gvp6brdt.js +57 -0
- package/dist/{chunk-3s21p9n2.js.map → chunk-gvp6brdt.js.map} +19 -9
- package/dist/{chunk-7g649j8h.js → chunk-gwnmgkq5.js} +2 -2
- package/dist/{chunk-7g649j8h.js.map → chunk-gwnmgkq5.js.map} +1 -1
- package/dist/{chunk-51w5ktv2.js → chunk-h76mddt3.js} +2 -2
- package/dist/{chunk-51w5ktv2.js.map → chunk-h76mddt3.js.map} +1 -1
- package/dist/{chunk-nz8z8jr1.js → chunk-hnrzm5pf.js} +3 -3
- package/dist/{chunk-nz8z8jr1.js.map → chunk-hnrzm5pf.js.map} +1 -1
- package/dist/{chunk-3wnghdsx.js → chunk-hs88b8gt.js} +2 -2
- package/dist/{chunk-3wnghdsx.js.map → chunk-hs88b8gt.js.map} +1 -1
- package/dist/{chunk-0k02tza1.js → chunk-j0cnsgjq.js} +1 -1
- package/dist/{chunk-pemhmjzp.js → chunk-j8vc3bze.js} +3 -3
- package/dist/{chunk-pemhmjzp.js.map → chunk-j8vc3bze.js.map} +1 -1
- package/dist/{chunk-471htqmz.js → chunk-k17wx7gz.js} +2 -2
- package/dist/{chunk-471htqmz.js.map → chunk-k17wx7gz.js.map} +1 -1
- package/dist/{chunk-m83zm86m.js → chunk-kc924zjz.js} +2 -2
- package/dist/{chunk-m83zm86m.js.map → chunk-kc924zjz.js.map} +1 -1
- package/dist/{chunk-5nj94s66.js → chunk-kdjhgtp9.js} +4 -4
- package/dist/{chunk-5nj94s66.js.map → chunk-kdjhgtp9.js.map} +1 -1
- package/dist/{chunk-32n8t8x6.js → chunk-kvas36p6.js} +2 -2
- package/dist/{chunk-32n8t8x6.js.map → chunk-kvas36p6.js.map} +1 -1
- package/dist/{chunk-4fx8p02c.js → chunk-kzstewtm.js} +2 -2
- package/dist/{chunk-4fx8p02c.js.map → chunk-kzstewtm.js.map} +1 -1
- package/dist/{chunk-gc2dz11k.js → chunk-mxbtcaed.js} +2 -2
- package/dist/{chunk-gc2dz11k.js.map → chunk-mxbtcaed.js.map} +1 -1
- package/dist/{chunk-cca0grq3.js → chunk-n1a2zj5z.js} +2 -2
- package/dist/{chunk-cca0grq3.js.map → chunk-n1a2zj5z.js.map} +1 -1
- package/dist/{chunk-0yj0s1pn.js → chunk-nf608sb1.js} +2 -2
- package/dist/{chunk-0yj0s1pn.js.map → chunk-nf608sb1.js.map} +1 -1
- package/dist/{chunk-19dpzp4y.js → chunk-nvngb4ed.js} +3 -3
- package/dist/{chunk-19dpzp4y.js.map → chunk-nvngb4ed.js.map} +1 -1
- package/dist/{chunk-cskqet01.js → chunk-pfm9ngch.js} +2 -2
- package/dist/{chunk-cskqet01.js.map → chunk-pfm9ngch.js.map} +1 -1
- package/dist/{chunk-fenc5tpv.js → chunk-q1e3q01h.js} +2 -2
- package/dist/{chunk-fenc5tpv.js.map → chunk-q1e3q01h.js.map} +1 -1
- package/dist/{chunk-2c0s0561.js → chunk-qen6587t.js} +2 -2
- package/dist/{chunk-2c0s0561.js.map → chunk-qen6587t.js.map} +1 -1
- package/dist/{chunk-r01aqf0q.js → chunk-r5tadf14.js} +2 -2
- package/dist/{chunk-r01aqf0q.js.map → chunk-r5tadf14.js.map} +1 -1
- package/dist/{chunk-cvr5624r.js → chunk-rjm1kj8n.js} +1 -1
- package/dist/{chunk-jaa6xkeh.js → chunk-rjtmgbay.js} +2 -2
- package/dist/{chunk-jaa6xkeh.js.map → chunk-rjtmgbay.js.map} +1 -1
- package/dist/{chunk-rhvc1axc.js → chunk-rkgsvsrj.js} +2 -2
- package/dist/{chunk-rhvc1axc.js.map → chunk-rkgsvsrj.js.map} +1 -1
- package/dist/{chunk-m53fgf20.js → chunk-s5jxc63j.js} +3 -3
- package/dist/{chunk-m53fgf20.js.map → chunk-s5jxc63j.js.map} +1 -1
- package/dist/{chunk-5tdxbwzg.js → chunk-sxa70wgn.js} +3 -3
- package/dist/{chunk-5tdxbwzg.js.map → chunk-sxa70wgn.js.map} +1 -1
- package/dist/chunk-tazvfppr.js +5 -0
- package/dist/{chunk-htgmy3x7.js.map → chunk-tazvfppr.js.map} +1 -1
- package/dist/{chunk-gy81nd9a.js → chunk-vd6zn8c4.js} +2 -2
- package/dist/{chunk-gy81nd9a.js.map → chunk-vd6zn8c4.js.map} +1 -1
- package/dist/chunk-vpgbn494.js +24 -0
- package/dist/{chunk-2yw4yezc.js.map → chunk-vpgbn494.js.map} +3 -3
- package/dist/{chunk-7ftj43vp.js → chunk-vwnd4jb5.js} +2 -2
- package/dist/{chunk-7ftj43vp.js.map → chunk-vwnd4jb5.js.map} +1 -1
- package/dist/{chunk-c0v61fp5.js → chunk-w0jfqnw9.js} +2 -2
- package/dist/{chunk-c0v61fp5.js.map → chunk-w0jfqnw9.js.map} +1 -1
- package/dist/{chunk-65beps2q.js → chunk-w61sphn5.js} +3 -3
- package/dist/{chunk-65beps2q.js.map → chunk-w61sphn5.js.map} +1 -1
- package/dist/{chunk-zg6hmc48.js → chunk-wqharhrp.js} +1 -1
- package/dist/{chunk-sx340k85.js → chunk-x0jahbqj.js} +2 -2
- package/dist/{chunk-sx340k85.js.map → chunk-x0jahbqj.js.map} +1 -1
- package/dist/{chunk-y6wt5b6t.js → chunk-xhmtph39.js} +1 -1
- package/dist/{chunk-c7qs1xp3.js → chunk-y67evsdy.js} +2 -2
- package/dist/{chunk-c7qs1xp3.js.map → chunk-y67evsdy.js.map} +1 -1
- package/dist/{chunk-krk7mcgm.js → chunk-ycsrpf47.js} +3 -3
- package/dist/{chunk-krk7mcgm.js.map → chunk-ycsrpf47.js.map} +1 -1
- package/dist/{chunk-9d2d08qm.js → chunk-ywgvxx35.js} +2 -2
- package/dist/{chunk-9d2d08qm.js.map → chunk-ywgvxx35.js.map} +1 -1
- package/dist/{chunk-4acpw92g.js → chunk-z23h4s1a.js} +3 -3
- package/dist/{chunk-4acpw92g.js.map → chunk-z23h4s1a.js.map} +1 -1
- package/dist/{chunk-tk135gmj.js → chunk-zjej5vqh.js} +3 -3
- package/dist/{chunk-tk135gmj.js.map → chunk-zjej5vqh.js.map} +1 -1
- package/dist/chunks/{ThemeProvider-BLI4zpLu.js → ThemeProvider-CgK2mmVV.js} +576 -2
- package/dist/chunks/ThemeProvider-CgK2mmVV.js.map +1 -0
- package/dist/client/auth/useEmailCode.d.ts.map +1 -1
- package/dist/client/auth/useSignIn.d.ts.map +1 -1
- package/dist/client/auth/useSignOut.d.ts.map +1 -1
- package/dist/client/index.d.ts +5 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +216 -8
- package/dist/client/index.js.map +1 -1
- package/dist/client/init.d.ts +8 -0
- package/dist/client/init.d.ts.map +1 -1
- package/dist/client/realtime/FakeSocket.d.ts +47 -0
- package/dist/client/realtime/FakeSocket.d.ts.map +1 -0
- package/dist/client/realtime/RealtimeClient.d.ts +162 -0
- package/dist/client/realtime/RealtimeClient.d.ts.map +1 -0
- package/dist/client/realtime/RealtimeContext.d.ts +8 -0
- package/dist/client/realtime/RealtimeContext.d.ts.map +1 -0
- package/dist/client/realtime/useChannelSubscription.d.ts +32 -0
- package/dist/client/realtime/useChannelSubscription.d.ts.map +1 -0
- package/dist/client/rpc.d.ts +11 -0
- package/dist/client/rpc.d.ts.map +1 -1
- package/dist/client/useChannel.d.ts +87 -0
- package/dist/client/useChannel.d.ts.map +1 -0
- package/dist/client/useMutation.d.ts.map +1 -1
- package/dist/client/useQuery.d.ts +13 -0
- package/dist/client/useQuery.d.ts.map +1 -1
- package/dist/config/index.js +1 -1
- package/dist/console/run.js +2 -2
- package/dist/console/run.js.map +1 -1
- package/dist/container/index.js +2 -2
- package/dist/container/index.js.map +1 -1
- package/dist/database/index.js +2 -2
- package/dist/database/index.js.map +1 -1
- package/dist/email/index.js +2 -2
- package/dist/email/index.js.map +1 -1
- package/dist/facades/Broadcast.d.ts +68 -0
- package/dist/facades/Broadcast.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/gemi.d.ts +19 -6
- package/dist/http/ChannelRouter.d.ts +171 -0
- package/dist/http/ChannelRouter.d.ts.map +1 -0
- package/dist/http/index.d.ts +1 -0
- package/dist/http/index.d.ts.map +1 -1
- package/dist/http/index.js +2 -2
- package/dist/http/index.js.map +1 -1
- package/dist/i18n/dictionaryRuntime.js +2 -2
- package/dist/i18n/dictionaryRuntime.js.map +1 -1
- package/dist/i18n/index.js +2 -2
- package/dist/i18n/index.js.map +1 -1
- package/dist/kernel/Kernel.d.ts +6 -0
- package/dist/kernel/Kernel.d.ts.map +1 -1
- package/dist/kernel/index.js +3 -3
- package/dist/kernel/index.js.map +5 -6
- package/dist/kernel/providers.d.ts +2 -2
- package/dist/kernel/providers.d.ts.map +1 -1
- package/dist/orm/context.d.ts.map +1 -1
- package/dist/orm/index.js +2 -2
- package/dist/orm/index.js.map +1 -1
- package/dist/server/Server.d.ts.map +1 -1
- package/dist/server/httpDev.d.ts +1 -2
- package/dist/server/httpDev.d.ts.map +1 -1
- package/dist/server/httpProd.d.ts +1 -1
- package/dist/server/httpProd.d.ts.map +1 -1
- package/dist/server/index.js +2 -2
- package/dist/server/index.js.map +3 -3
- package/dist/server/shutdown.d.ts +9 -0
- package/dist/server/shutdown.d.ts.map +1 -1
- package/dist/services/broadcast/BroadcastDriver.d.ts +73 -0
- package/dist/services/broadcast/BroadcastDriver.d.ts.map +1 -0
- package/dist/services/broadcast/BroadcastEvent.d.ts +67 -0
- package/dist/services/broadcast/BroadcastEvent.d.ts.map +1 -0
- package/dist/services/broadcast/BroadcastManager.d.ts +148 -0
- package/dist/services/broadcast/BroadcastManager.d.ts.map +1 -0
- package/dist/services/broadcast/BroadcastServiceProvider.d.ts +16 -0
- package/dist/services/broadcast/BroadcastServiceProvider.d.ts.map +1 -0
- package/dist/services/broadcast/FakeBroadcastManager.d.ts +45 -0
- package/dist/services/broadcast/FakeBroadcastManager.d.ts.map +1 -0
- package/dist/services/broadcast/MemoryBroadcastDriver.d.ts +23 -0
- package/dist/services/broadcast/MemoryBroadcastDriver.d.ts.map +1 -0
- package/dist/services/broadcast/SocketHub.d.ts +192 -0
- package/dist/services/broadcast/SocketHub.d.ts.map +1 -0
- package/dist/services/broadcast/brand.d.ts +16 -0
- package/dist/services/broadcast/brand.d.ts.map +1 -0
- package/dist/services/broadcast/channels.d.ts +77 -0
- package/dist/services/broadcast/channels.d.ts.map +1 -0
- package/dist/services/broadcast/config.d.ts +93 -0
- package/dist/services/broadcast/config.d.ts.map +1 -0
- package/dist/services/broadcast/protocol.d.ts +123 -0
- package/dist/services/broadcast/protocol.d.ts.map +1 -0
- package/dist/services/broadcast/socketTag.d.ts +17 -0
- package/dist/services/broadcast/socketTag.d.ts.map +1 -0
- package/dist/services/broadcast/types.d.ts +27 -0
- package/dist/services/broadcast/types.d.ts.map +1 -0
- package/dist/services/broadcast/wire.d.ts +35 -0
- package/dist/services/broadcast/wire.d.ts.map +1 -0
- package/dist/services/events/EventManager.d.ts +6 -0
- package/dist/services/events/EventManager.d.ts.map +1 -1
- package/dist/services/index.d.ts +9 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +9 -9
- package/dist/services/index.js.map +4 -3
- package/dist/services/queue/work.js +2 -2
- package/dist/services/queue/work.js.map +1 -1
- package/dist/services/router/config.d.ts +7 -0
- package/dist/services/router/config.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/testing/Page.d.ts +8 -0
- package/dist/testing/Page.d.ts.map +1 -1
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +174 -8
- package/dist/testing/index.js.map +1 -1
- package/dist/vitest/index.js +1 -1
- package/package.json +1 -1
- package/skills/gemi-react-best-practices/SKILL.md +3 -1
- package/skills/gemi-react-best-practices/rules/query-live-over-polling.md +56 -0
- package/dist/chunk-2n3pgfnb.js +0 -5
- package/dist/chunk-2n3pgfnb.js.map +0 -10
- package/dist/chunk-2yw4yezc.js +0 -24
- package/dist/chunk-3rvn9a76.js +0 -58
- package/dist/chunk-3s21p9n2.js +0 -57
- package/dist/chunk-dyp0wg9e.js +0 -10
- package/dist/chunk-htgmy3x7.js +0 -5
- package/dist/chunk-krtybgz1.js +0 -4
- package/dist/chunk-krtybgz1.js.map +0 -10
- package/dist/chunk-s07v65x6.js +0 -9
- package/dist/chunk-s07v65x6.js.map +0 -10
- package/dist/chunks/ThemeProvider-BLI4zpLu.js.map +0 -1
- /package/dist/{chunk-dhbgrv58.js.map → chunk-4qpypa3h.js.map} +0 -0
- /package/dist/{chunk-wjkh2gem.js.map → chunk-8x73yyfs.js.map} +0 -0
- /package/dist/{chunk-0k02tza1.js.map → chunk-j0cnsgjq.js.map} +0 -0
- /package/dist/{chunk-cvr5624r.js.map → chunk-rjm1kj8n.js.map} +0 -0
- /package/dist/{chunk-zg6hmc48.js.map → chunk-wqharhrp.js.map} +0 -0
- /package/dist/{chunk-y6wt5b6t.js.map → chunk-xhmtph39.js.map} +0 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Event } from "../events/Event";
|
|
2
|
+
import { type BroadcastableEvent } from "./brand";
|
|
3
|
+
import { type ChannelParamsArgs, type ChannelTarget } from "./channels";
|
|
4
|
+
export { BROADCAST_EVENT, isBroadcastEvent, type BroadcastableEvent } from "./brand";
|
|
5
|
+
/**
|
|
6
|
+
* An event that is also sent to the sockets on a channel, after its listeners
|
|
7
|
+
* have run.
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* export class SiteChanged extends BroadcastEvent<{ pages: string[] }, "changed"> {
|
|
11
|
+
* static name = "SiteChanged";
|
|
12
|
+
* static afterCommit = true;
|
|
13
|
+
* constructor(public site: { publicId: string }, public changes: { pages: string[] }) {
|
|
14
|
+
* super();
|
|
15
|
+
* }
|
|
16
|
+
* broadcastOn() {
|
|
17
|
+
* return this.channel("site.:siteId", { siteId: this.site.publicId });
|
|
18
|
+
* }
|
|
19
|
+
* broadcastAs() {
|
|
20
|
+
* return "changed" as const;
|
|
21
|
+
* }
|
|
22
|
+
* broadcastWith() {
|
|
23
|
+
* return this.changes;
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* SiteChanged.dispatch(site, { pages: ["/about"] });
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* It is an `Event`: it needs `static name`, listeners bind to it, `static
|
|
31
|
+
* afterCommit` holds both the listeners and the broadcast until the commit,
|
|
32
|
+
* and under `Event.fake()` it is recorded and **not** broadcast. Without
|
|
33
|
+
* `afterCommit`, a dispatch inside a transaction still runs its listeners at
|
|
34
|
+
* once, and the broadcast itself waits for the commit and is dropped on
|
|
35
|
+
* rollback, as every `Broadcast` emit does.
|
|
36
|
+
*
|
|
37
|
+
* `TPayload` is what `broadcastWith` returns and `TName` what `broadcastAs`
|
|
38
|
+
* returns. List the class on a channel with `.events(SiteChanged)` and the
|
|
39
|
+
* generated `BroadcastRPC` types the client's handler for `changed`.
|
|
40
|
+
*/
|
|
41
|
+
export declare abstract class BroadcastEvent<TPayload = undefined, TName extends string = string> extends Event implements BroadcastableEvent {
|
|
42
|
+
/**
|
|
43
|
+
* Type-only: what `.events(...)` on a channel reads to type `BroadcastRPC`.
|
|
44
|
+
* No field is emitted.
|
|
45
|
+
*/
|
|
46
|
+
readonly __broadcast: {
|
|
47
|
+
name: TName;
|
|
48
|
+
payload: TPayload;
|
|
49
|
+
};
|
|
50
|
+
constructor();
|
|
51
|
+
/**
|
|
52
|
+
* The channel or channels to send this event to: a `this.channel(...)`, a
|
|
53
|
+
* concrete topic such as `` `user.${id}` ``, or an array of them.
|
|
54
|
+
*/
|
|
55
|
+
abstract broadcastOn(): ChannelTarget | string | ReadonlyArray<ChannelTarget | string>;
|
|
56
|
+
/** The event name clients see. Defaults to the class's `static name`. */
|
|
57
|
+
broadcastAs(): TName;
|
|
58
|
+
/**
|
|
59
|
+
* The payload clients receive. Nothing by default: a payload is never
|
|
60
|
+
* derived from the event's fields, so nothing is broadcast by accident.
|
|
61
|
+
* Keep it to ids and a change hint, and never put secrets in it.
|
|
62
|
+
*/
|
|
63
|
+
broadcastWith(): TPayload;
|
|
64
|
+
/** The channel `pattern` names with `params`, for `broadcastOn`. */
|
|
65
|
+
protected channel<P extends string>(pattern: P, ...params: ChannelParamsArgs<P>): ChannelTarget;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=BroadcastEvent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BroadcastEvent.d.ts","sourceRoot":"","sources":["../../../services/broadcast/BroadcastEvent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxC,OAAO,EAAmB,KAAK,kBAAkB,EAAE,MAAM,SAAS,CAAC;AACnE,OAAO,EAAc,KAAK,iBAAiB,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAEpF,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,KAAK,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAErF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,8BAAsB,cAAc,CAAC,QAAQ,GAAG,SAAS,EAAE,KAAK,SAAS,MAAM,GAAG,MAAM,CACtF,SAAQ,KACR,YAAW,kBAAkB;IAE7B;;;OAGG;IACH,SAAiB,WAAW,EAAE;QAAE,IAAI,EAAE,KAAK,CAAC;QAAC,OAAO,EAAE,QAAQ,CAAA;KAAE,CAAC;;IAOjE;;;OAGG;IACH,QAAQ,CAAC,WAAW,IAAI,aAAa,GAAG,MAAM,GAAG,aAAa,CAAC,aAAa,GAAG,MAAM,CAAC;IAEtF,yEAAyE;IACzE,WAAW,IAAI,KAAK;IAIpB;;;;OAIG;IACH,aAAa,IAAI,QAAQ;IAIzB,oEAAoE;IACpE,SAAS,CAAC,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,GAAG,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,aAAa;CAGhG"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { Application } from "../../foundation/Application";
|
|
2
|
+
import type { ChannelRouter, ChannelRouterClass } from "../../http/ChannelRouter";
|
|
3
|
+
import type { BroadcastDeliver, BroadcastDriver, BroadcastDriverHooks, BroadcastRevocation } from "./BroadcastDriver";
|
|
4
|
+
import type { BroadcastableEvent } from "./brand";
|
|
5
|
+
import { type ChannelParamsArgs, type ChannelTarget } from "./channels";
|
|
6
|
+
import { type BroadcastConfig } from "./config";
|
|
7
|
+
import type { BroadcastDataArgs, BroadcastEventsFor, SentBroadcast } from "./types";
|
|
8
|
+
/** An emit whose frame is bigger than `maxEventBytes`. Nothing was sent. */
|
|
9
|
+
export declare class BroadcastPayloadTooLargeError extends Error {
|
|
10
|
+
readonly bytes: number;
|
|
11
|
+
readonly limit: number;
|
|
12
|
+
constructor(bytes: number, limit: number, event: string);
|
|
13
|
+
}
|
|
14
|
+
/** Something that carries the sender's socket id: a request, or the id. */
|
|
15
|
+
export type SocketSource = string | null | undefined | Request | {
|
|
16
|
+
rawRequest: Request;
|
|
17
|
+
} | {
|
|
18
|
+
headers: Headers;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Real-time, volatile fan-out to the WebSocket clients subscribed to a
|
|
22
|
+
* channel. Events are hints that something changed, not data: delivery is at
|
|
23
|
+
* most once, nothing is stored or replayed, and a client that may have missed
|
|
24
|
+
* something refetches over HTTP.
|
|
25
|
+
*
|
|
26
|
+
* ```ts
|
|
27
|
+
* Broadcast.to("site.:siteId", { siteId: site.publicId }).emit("changed", { pages: ["/about"] });
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* The manager builds and checks the frame at the call site, so a bad channel
|
|
31
|
+
* or a payload over the limit throws there, and then hands it to the driver,
|
|
32
|
+
* which decides which processes see it. Inside an ORM transaction the
|
|
33
|
+
* hand-off waits for the commit and is dropped on rollback.
|
|
34
|
+
*
|
|
35
|
+
* Delivery to sockets belongs to the transport, which calls `start` with the
|
|
36
|
+
* function that reaches this process's sockets. Until it does, an emit
|
|
37
|
+
* reaches no socket in this process.
|
|
38
|
+
*/
|
|
39
|
+
export declare class BroadcastManager {
|
|
40
|
+
private readonly options;
|
|
41
|
+
static token: string;
|
|
42
|
+
readonly config: Required<BroadcastConfig>;
|
|
43
|
+
readonly driver: BroadcastDriver;
|
|
44
|
+
private readonly warnedAboutSize;
|
|
45
|
+
private started;
|
|
46
|
+
private hooks;
|
|
47
|
+
private channelRouter;
|
|
48
|
+
constructor(config?: BroadcastConfig, options?: {
|
|
49
|
+
application?: Application;
|
|
50
|
+
/** The app's `ChannelRouter` (`route.channels`). */
|
|
51
|
+
channels?: ChannelRouterClass;
|
|
52
|
+
});
|
|
53
|
+
/**
|
|
54
|
+
* The app's `ChannelRouter`, built once, or `null` when `route.channels` is
|
|
55
|
+
* not set (and every subscription is refused). The transport authorizes
|
|
56
|
+
* each `sub` with its `authorize`.
|
|
57
|
+
*/
|
|
58
|
+
get channels(): ChannelRouter | null;
|
|
59
|
+
/**
|
|
60
|
+
* The channel `pattern` names with `params`, or a concrete topic, or a
|
|
61
|
+
* `ChannelTarget` (what `BroadcastEvent.channel()` returns).
|
|
62
|
+
*/
|
|
63
|
+
to<P extends string>(pattern: P | ChannelTarget, ...params: ChannelParamsArgs<P>): PendingBroadcast<BroadcastEventsFor<P>>;
|
|
64
|
+
/** A user's own channel, `user.<id>`: the one the router's `"user"` channel joins. */
|
|
65
|
+
toUser(user: {
|
|
66
|
+
id: unknown;
|
|
67
|
+
} | string | number): PendingBroadcast<BroadcastEventsFor<"user">>;
|
|
68
|
+
/**
|
|
69
|
+
* Skips the socket that made `source`, a request carrying the
|
|
70
|
+
* `X-Gemi-Socket` header (or the id itself), so the client that saved
|
|
71
|
+
* something does not refetch what it already has. Without a valid socket id
|
|
72
|
+
* nobody is skipped.
|
|
73
|
+
*/
|
|
74
|
+
toOthers(source: SocketSource): BroadcastScope;
|
|
75
|
+
/** Sends a `BroadcastEvent` to the channels its `broadcastOn` names. */
|
|
76
|
+
broadcastEvent(event: BroadcastableEvent): void;
|
|
77
|
+
/**
|
|
78
|
+
* Checks and encodes an emit, then hands it to the driver: now, or after
|
|
79
|
+
* the open transaction commits. Throws at the call site for a bad event
|
|
80
|
+
* name, a payload that is not JSON, or one over `maxEventBytes`.
|
|
81
|
+
*/
|
|
82
|
+
send(target: ChannelTarget, event: string, data: unknown, except: string | null): void;
|
|
83
|
+
private prepare;
|
|
84
|
+
private hand;
|
|
85
|
+
/**
|
|
86
|
+
* The hand-off to the driver, after the commit. Never throws: an emit is
|
|
87
|
+
* fire-and-forget, and a driver failure is logged with the topic and event,
|
|
88
|
+
* never the payload.
|
|
89
|
+
*/
|
|
90
|
+
protected publish(sent: SentBroadcast, frame: string): void;
|
|
91
|
+
/** Whether the transport has started delivering to this process's sockets. */
|
|
92
|
+
get isStarted(): boolean;
|
|
93
|
+
/**
|
|
94
|
+
* For the transport: this process starts receiving, and every frame for a
|
|
95
|
+
* topic it has sockets on reaches `deliver` (`server.publish`).
|
|
96
|
+
*/
|
|
97
|
+
start(deliver: BroadcastDeliver, hooks?: BroadcastDriverHooks): Promise<void>;
|
|
98
|
+
/** For the transport: the first local socket joined `topic`. */
|
|
99
|
+
topicAdded(topic: string): void | Promise<void>;
|
|
100
|
+
/** For the transport: the last local socket left `topic`. */
|
|
101
|
+
topicRemoved(topic: string): void | Promise<void>;
|
|
102
|
+
close(): Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* Closes subscriptions that may no longer be allowed: every socket of a
|
|
105
|
+
* user (`{ user }`: its sockets are closed, and its client reconnects and
|
|
106
|
+
* authorizes every channel again), or every subscription to a channel
|
|
107
|
+
* (`{ channel }`, a pattern with `params` or a topic: each is told
|
|
108
|
+
* `denied` with `revoked`). Sign-out calls it for the user.
|
|
109
|
+
*
|
|
110
|
+
* Inside an ORM transaction it waits for the commit, so the
|
|
111
|
+
* resubscriptions it causes are authorized against the committed state.
|
|
112
|
+
* Never throws for a transport problem; a bad channel throws at the call
|
|
113
|
+
* site.
|
|
114
|
+
*/
|
|
115
|
+
revoke(target: {
|
|
116
|
+
user: {
|
|
117
|
+
id: unknown;
|
|
118
|
+
} | string | number;
|
|
119
|
+
} | {
|
|
120
|
+
channel: string | ChannelTarget;
|
|
121
|
+
params?: Record<string, unknown>;
|
|
122
|
+
}): void;
|
|
123
|
+
protected sendRevocation(revocation: BroadcastRevocation): void;
|
|
124
|
+
private checkSize;
|
|
125
|
+
}
|
|
126
|
+
/** An emit with its channel chosen: `.emit(event, data)` sends it. */
|
|
127
|
+
export declare class PendingBroadcast<Events extends Record<string, unknown> = Record<string, unknown>> {
|
|
128
|
+
private readonly manager;
|
|
129
|
+
readonly target: ChannelTarget;
|
|
130
|
+
private readonly except;
|
|
131
|
+
constructor(manager: BroadcastManager, target: ChannelTarget, except: string | null);
|
|
132
|
+
/**
|
|
133
|
+
* Sends `event` with `data` (JSON) to every socket on the channel. Returns
|
|
134
|
+
* nothing: delivery is at most once and never acknowledged.
|
|
135
|
+
*/
|
|
136
|
+
emit<E extends keyof Events & string>(event: E, ...data: BroadcastDataArgs<Events[E]>): void;
|
|
137
|
+
}
|
|
138
|
+
/** `Broadcast.toOthers(...)`: the same `to`, with the sender's socket skipped. */
|
|
139
|
+
export declare class BroadcastScope {
|
|
140
|
+
private readonly manager;
|
|
141
|
+
readonly except: string | null;
|
|
142
|
+
constructor(manager: BroadcastManager, except: string | null);
|
|
143
|
+
to<P extends string>(pattern: P | ChannelTarget, ...params: ChannelParamsArgs<P>): PendingBroadcast<BroadcastEventsFor<P>>;
|
|
144
|
+
toUser(user: {
|
|
145
|
+
id: unknown;
|
|
146
|
+
} | string | number): PendingBroadcast<BroadcastEventsFor<"user">>;
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=BroadcastManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BroadcastManager.d.ts","sourceRoot":"","sources":["../../../services/broadcast/BroadcastManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ServiceProvider } from "../../support/ServiceProvider";
|
|
2
|
+
export declare class BroadcastServiceProvider extends ServiceProvider {
|
|
3
|
+
register(): void;
|
|
4
|
+
/**
|
|
5
|
+
* Fails the boot on a malformed channel pattern, rather than refusing every
|
|
6
|
+
* subscription to it at runtime.
|
|
7
|
+
*/
|
|
8
|
+
boot(): void;
|
|
9
|
+
/**
|
|
10
|
+
* Says `bye` to the sockets (the server's drain did already, in
|
|
11
|
+
* production; a dev reload retiring this application did not), then stops
|
|
12
|
+
* receiving and closes the driver's connections.
|
|
13
|
+
*/
|
|
14
|
+
shutdown(): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=BroadcastServiceProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BroadcastServiceProvider.d.ts","sourceRoot":"","sources":["../../../services/broadcast/BroadcastServiceProvider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAKhE,qBAAa,wBAAyB,SAAQ,eAAe;IAC3D,QAAQ;IAcR;;;OAGG;IACH,IAAI;IAKJ;;;;OAIG;IACG,QAAQ;CAKf"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Application } from "../../foundation/Application";
|
|
2
|
+
import { BroadcastManager } from "./BroadcastManager";
|
|
3
|
+
import type { BroadcastRevocation } from "./BroadcastDriver";
|
|
4
|
+
import type { SentBroadcast } from "./types";
|
|
5
|
+
/**
|
|
6
|
+
* What `Broadcast.fake()` installs: a `BroadcastManager` that records every
|
|
7
|
+
* emit instead of handing it to a driver.
|
|
8
|
+
*
|
|
9
|
+
* Everything before the hand-off is the real manager's: channels and params
|
|
10
|
+
* are checked, payloads encoded and size-checked, and inside an ORM
|
|
11
|
+
* transaction an emit is recorded only once the transaction commits (and
|
|
12
|
+
* never if it rolls back). So a test sees what clients would have been sent.
|
|
13
|
+
*
|
|
14
|
+
* `restore()` is not optional, for the reason it is not on `Event.fake()`.
|
|
15
|
+
*/
|
|
16
|
+
export declare class FakeBroadcastManager extends BroadcastManager {
|
|
17
|
+
private readonly application;
|
|
18
|
+
private readonly previous;
|
|
19
|
+
readonly sent: SentBroadcast[];
|
|
20
|
+
/** Every `Broadcast.revoke`, as it would travel: `{ user }` or `{ topic }`. */
|
|
21
|
+
readonly revoked: BroadcastRevocation[];
|
|
22
|
+
static install(application: Application): FakeBroadcastManager;
|
|
23
|
+
private constructor();
|
|
24
|
+
private readonly channelsFrom;
|
|
25
|
+
get channels(): import("gemi/http").ChannelRouter;
|
|
26
|
+
protected publish(sent: SentBroadcast): void;
|
|
27
|
+
protected sendRevocation(revocation: BroadcastRevocation): void;
|
|
28
|
+
/** Puts the real manager back. */
|
|
29
|
+
restore(): void;
|
|
30
|
+
/**
|
|
31
|
+
* Asserts something was sent to `channel`: a pattern (`"site.:siteId"`,
|
|
32
|
+
* matching every topic it builds), a concrete topic (`"site.abc123"`), or
|
|
33
|
+
* `"user"` (every `user.<id>`). `event` and `predicate` narrow it.
|
|
34
|
+
*/
|
|
35
|
+
assertSent(channel: string, event?: string, predicate?: (data: any, sent: SentBroadcast) => boolean): void;
|
|
36
|
+
/** Asserts nothing matching was sent. Same arguments as `assertSent`. */
|
|
37
|
+
assertNotSent(channel: string, event?: string, predicate?: (data: any, sent: SentBroadcast) => boolean): void;
|
|
38
|
+
/** Asserts exactly `times` matching emits. */
|
|
39
|
+
assertSentTimes(channel: string, times: number, event?: string, predicate?: (data: any, sent: SentBroadcast) => boolean): void;
|
|
40
|
+
/** Asserts nothing at all was broadcast. */
|
|
41
|
+
assertNothingSent(): void;
|
|
42
|
+
private matching;
|
|
43
|
+
private summary;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=FakeBroadcastManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FakeBroadcastManager.d.ts","sourceRoot":"","sources":["../../../services/broadcast/FakeBroadcastManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C;;;;;;;;;;GAUG;AACH,qBAAa,oBAAqB,SAAQ,gBAAgB;IAmBtD,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAnB3B,QAAQ,CAAC,IAAI,EAAE,aAAa,EAAE,CAAM;IACpC,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,EAAE,mBAAmB,EAAE,CAAM;IAE7C,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,WAAW,GAAG,oBAAoB;IAa9D,OAAO;IAiBP,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA+B;IAE5D,IAAa,QAAQ,sCAEpB;cAEkB,OAAO,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;cAIlC,cAAc,CAAC,UAAU,EAAE,mBAAmB,GAAG,IAAI;IAIxE,kCAAkC;IAClC,OAAO,IAAI,IAAI;IAQf;;;;OAIG;IACH,UAAU,CACR,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,aAAa,KAAK,OAAO,GACtD,IAAI;IAQP,yEAAyE;IACzE,aAAa,CACX,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,aAAa,KAAK,OAAO,GACtD,IAAI;IASP,8CAA8C;IAC9C,eAAe,CACb,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,aAAa,KAAK,OAAO,GACtD,IAAI;IAUP,4CAA4C;IAC5C,iBAAiB,IAAI,IAAI;IAQzB,OAAO,CAAC,QAAQ;IAgBhB,OAAO,CAAC,OAAO;CAIhB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { BroadcastDeliver, BroadcastDriver, BroadcastDriverHooks, BroadcastRevocation } from "./BroadcastDriver";
|
|
2
|
+
/**
|
|
3
|
+
* The default driver: an emit reaches the sockets of **this process only**.
|
|
4
|
+
*
|
|
5
|
+
* Right for one instance whose web processes also run the jobs and cron that
|
|
6
|
+
* emit. Wrong as soon as there are two replicas, or a `gemi queue:work`
|
|
7
|
+
* worker that emits: those frames reach nobody, with nothing to say so. Use
|
|
8
|
+
* the Redis driver there.
|
|
9
|
+
*
|
|
10
|
+
* Before the HTTP server starts the transport, there is nobody to deliver to,
|
|
11
|
+
* and `publish` drops the frame.
|
|
12
|
+
*/
|
|
13
|
+
export declare class MemoryBroadcastDriver implements BroadcastDriver {
|
|
14
|
+
private deliver;
|
|
15
|
+
private hooks;
|
|
16
|
+
/** Whether the transport has started this driver. */
|
|
17
|
+
get started(): boolean;
|
|
18
|
+
publish(topic: string, frame: string): void;
|
|
19
|
+
start(deliver: BroadcastDeliver, hooks?: BroadcastDriverHooks): void;
|
|
20
|
+
revoke(revocation: BroadcastRevocation): void;
|
|
21
|
+
close(): void;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=MemoryBroadcastDriver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MemoryBroadcastDriver.d.ts","sourceRoot":"","sources":["../../../services/broadcast/MemoryBroadcastDriver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,mBAAmB,CAAC;AAE3B;;;;;;;;;;GAUG;AACH,qBAAa,qBAAsB,YAAW,eAAe;IAC3D,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,KAAK,CAAmC;IAEhD,qDAAqD;IACrD,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAI3C,KAAK,CAAC,OAAO,EAAE,gBAAgB,EAAE,KAAK,CAAC,EAAE,oBAAoB,GAAG,IAAI;IAKpE,MAAM,CAAC,UAAU,EAAE,mBAAmB,GAAG,IAAI;IAI7C,KAAK,IAAI,IAAI;CAId"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import type { Server, ServerWebSocket, WebSocketHandler } from "bun";
|
|
2
|
+
import type { Application } from "../../foundation/Application";
|
|
3
|
+
import { type CarriedContext } from "../../http/requestContext";
|
|
4
|
+
import type { BroadcastRevocation } from "./BroadcastDriver";
|
|
5
|
+
import type { BroadcastManager } from "./BroadcastManager";
|
|
6
|
+
/** What the hub keeps on each of its sockets (`ws.data`). */
|
|
7
|
+
export interface GemiSocketData {
|
|
8
|
+
/** Marks a broadcast socket, as opposed to the dev server's HMR relay. */
|
|
9
|
+
readonly gemiSocket: true;
|
|
10
|
+
/** The hub that accepted it. After a dev reload that may not be the current one. */
|
|
11
|
+
readonly hub: SocketHub;
|
|
12
|
+
readonly socketId: string;
|
|
13
|
+
readonly tag: string;
|
|
14
|
+
/** The upgrade request, kept to rebuild a request context for every `sub`. */
|
|
15
|
+
readonly request: {
|
|
16
|
+
url: string;
|
|
17
|
+
headers: [string, string][];
|
|
18
|
+
};
|
|
19
|
+
/** What the global middleware resolved for the upgrade: its user. */
|
|
20
|
+
readonly carried: CarriedContext | null;
|
|
21
|
+
/** The signed-in user's id at the upgrade, for limits and `revoke({ user })`. */
|
|
22
|
+
readonly userId: string | null;
|
|
23
|
+
readonly ip: string | null;
|
|
24
|
+
/** Subscription id → its topic, `null` while it is being authorized. */
|
|
25
|
+
readonly subs: Map<string, Subscription>;
|
|
26
|
+
/** Topic → how many of this socket's subscriptions are on it. */
|
|
27
|
+
readonly topics: Map<string, number>;
|
|
28
|
+
rate: {
|
|
29
|
+
windowStart: number;
|
|
30
|
+
subs: number;
|
|
31
|
+
pings: number;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* The process, IP and user connection counts this socket holds. Taken
|
|
35
|
+
* before the upgrade's first `await`, so concurrent upgrades cannot
|
|
36
|
+
* overshoot the caps, and given back exactly once.
|
|
37
|
+
*/
|
|
38
|
+
reservation: Reservation | null;
|
|
39
|
+
/** The hub has closed it (backpressure, revoke, bye); its close is on the way. */
|
|
40
|
+
closing: boolean;
|
|
41
|
+
closed: boolean;
|
|
42
|
+
}
|
|
43
|
+
interface Subscription {
|
|
44
|
+
pattern: string;
|
|
45
|
+
params: Record<string, string | number>;
|
|
46
|
+
topic: string | null;
|
|
47
|
+
}
|
|
48
|
+
interface Reservation {
|
|
49
|
+
ip: string | null;
|
|
50
|
+
userId: string | null;
|
|
51
|
+
}
|
|
52
|
+
type GemiSocket = ServerWebSocket<GemiSocketData>;
|
|
53
|
+
/**
|
|
54
|
+
* The WebSocket side of broadcasting, one per HTTP server process: accepts
|
|
55
|
+
* upgrades on the configured path, speaks the `gemi.v1` protocol (see
|
|
56
|
+
* `protocol.ts`), authorizes every subscription through the app's
|
|
57
|
+
* `ChannelRouter`, and delivers what the driver hands it with
|
|
58
|
+
* `server.publish`.
|
|
59
|
+
*
|
|
60
|
+
* Nothing here keeps a queue or a log. A socket that falls behind is closed
|
|
61
|
+
* (`1013`), one whose user signs out is closed (`4001`), and a server that
|
|
62
|
+
* stops says `bye` (`1012`); in every case the client reconnects and
|
|
63
|
+
* refetches what it shows.
|
|
64
|
+
*/
|
|
65
|
+
export declare class SocketHub {
|
|
66
|
+
private readonly manager;
|
|
67
|
+
private readonly application;
|
|
68
|
+
static token: string;
|
|
69
|
+
private server;
|
|
70
|
+
private readonly sockets;
|
|
71
|
+
private readonly byTopic;
|
|
72
|
+
private readonly topicReady;
|
|
73
|
+
private readonly perIp;
|
|
74
|
+
private readonly perUser;
|
|
75
|
+
/** Sockets held or being upgraded: the process cap's count. */
|
|
76
|
+
private connections;
|
|
77
|
+
/** Bumped by every topic revocation; see `revokedAt`. */
|
|
78
|
+
private revocationSeq;
|
|
79
|
+
/** Topic → the `revocationSeq` of its last revocation. */
|
|
80
|
+
private readonly revokedAt;
|
|
81
|
+
private closing;
|
|
82
|
+
/** Resolves once every socket `shutdown` closed is gone, or was cut. */
|
|
83
|
+
private closed;
|
|
84
|
+
private onAllClosed;
|
|
85
|
+
private startPromise;
|
|
86
|
+
constructor(manager: BroadcastManager, application: Application);
|
|
87
|
+
private get config();
|
|
88
|
+
/** Whether `req` is for the socket endpoint (an upgrade or not). */
|
|
89
|
+
matches(req: Request): boolean;
|
|
90
|
+
/** Sockets open on this process. */
|
|
91
|
+
get size(): number;
|
|
92
|
+
/**
|
|
93
|
+
* Starts delivering: the driver's frames for this process go out with
|
|
94
|
+
* `server.publish`. Call once the server is listening.
|
|
95
|
+
*/
|
|
96
|
+
start(server: Server<any>): Promise<void>;
|
|
97
|
+
/**
|
|
98
|
+
* Answers a request for the socket endpoint: upgrades it, or refuses it
|
|
99
|
+
* with a status saying why. `undefined` once Bun has taken it over.
|
|
100
|
+
*/
|
|
101
|
+
upgrade(req: Request, server: Server<any>): Promise<Response | undefined>;
|
|
102
|
+
private tryUpgrade;
|
|
103
|
+
private authenticate;
|
|
104
|
+
/**
|
|
105
|
+
* `true`, or why the upgrade is refused. A browser always sends `Origin` on
|
|
106
|
+
* a WebSocket upgrade, and cookies ride along with it whatever the page
|
|
107
|
+
* that opened it, so an upgrade carrying cookies must come from an allowed
|
|
108
|
+
* origin and one without `Origin` is refused. A native client sends its
|
|
109
|
+
* token in a header and no cookies, and may leave `Origin` out.
|
|
110
|
+
*/
|
|
111
|
+
private checkOrigin;
|
|
112
|
+
/** Whether `origin` may open a socket. */
|
|
113
|
+
originAllowed(origin: string, req: Request): boolean;
|
|
114
|
+
private reserve;
|
|
115
|
+
private release;
|
|
116
|
+
/** `Bun.serve`'s `websocket` option. */
|
|
117
|
+
get websocket(): WebSocketHandler<GemiSocketData>;
|
|
118
|
+
/** The server-wide socket settings, also for a handler shared with HMR in dev. */
|
|
119
|
+
socketOptions(): {
|
|
120
|
+
readonly maxPayloadLength: number;
|
|
121
|
+
readonly idleTimeout: number;
|
|
122
|
+
readonly backpressureLimit: number;
|
|
123
|
+
readonly closeOnBackpressureLimit: true;
|
|
124
|
+
readonly sendPings: true;
|
|
125
|
+
readonly perMessageDeflate: false;
|
|
126
|
+
};
|
|
127
|
+
onOpen(ws: GemiSocket): void;
|
|
128
|
+
onMessage(ws: GemiSocket, message: string | Buffer): void;
|
|
129
|
+
onClose(ws: GemiSocket): void;
|
|
130
|
+
private rateWindow;
|
|
131
|
+
/**
|
|
132
|
+
* Counts a `sub`. Only `sub` frames count: an `unsub` costs the server
|
|
133
|
+
* nothing to speak of, and counting it would charge a component's every
|
|
134
|
+
* mount and unmount twice.
|
|
135
|
+
*/
|
|
136
|
+
private countSub;
|
|
137
|
+
/** `false` when the socket was closed for pinging too often. */
|
|
138
|
+
private countPing;
|
|
139
|
+
private subscribe;
|
|
140
|
+
/** Runs the channel's authorization for `sub`, as the socket's upgrade request. */
|
|
141
|
+
private authorize;
|
|
142
|
+
private unsubscribeFrame;
|
|
143
|
+
/** Puts `ws` on `topic`, and resolves once the driver delivers the topic here. */
|
|
144
|
+
private join;
|
|
145
|
+
private leave;
|
|
146
|
+
/**
|
|
147
|
+
* The driver's delivery for this process. A socket already over the
|
|
148
|
+
* backpressure limit is closed first (1013): it would only fall further
|
|
149
|
+
* behind, and its client recovers by reconnecting and refetching.
|
|
150
|
+
*/
|
|
151
|
+
deliver(topic: string, frame: string): void;
|
|
152
|
+
/** Applies a revocation that reached this process. */
|
|
153
|
+
applyRevocation(revocation: BroadcastRevocation): void;
|
|
154
|
+
/**
|
|
155
|
+
* After `revoke({ channel })`: every subscription `ws` has on `topic` is
|
|
156
|
+
* authorized again, as the socket's upgrade request. The ones refused are
|
|
157
|
+
* denied with `revoked`; the others stay, and get a fresh `subscribed` so
|
|
158
|
+
* the client resyncs whatever was published while the topic was paused.
|
|
159
|
+
* The socket receives nothing on the topic until the answers are in.
|
|
160
|
+
*/
|
|
161
|
+
private reauthorize;
|
|
162
|
+
private sendAll;
|
|
163
|
+
/**
|
|
164
|
+
* Stops accepting sockets and says `bye` to every open one, with a
|
|
165
|
+
* jittered `retryAfter` (1–5 s) so a restart's reconnects do not arrive at
|
|
166
|
+
* once, then closes them (1012). Sockets that have not closed after
|
|
167
|
+
* `terminateAfterMs` are cut. Idempotent.
|
|
168
|
+
*
|
|
169
|
+
* Resolves once every socket has closed, or at `terminateAfterMs` when the
|
|
170
|
+
* rest were cut: by the hub's own count, which the drain waits on instead
|
|
171
|
+
* of Bun's (on a loaded CI runner, Bun's graceful `stop()` was seen to wait
|
|
172
|
+
* on terminated sockets for good).
|
|
173
|
+
*/
|
|
174
|
+
shutdown(options?: {
|
|
175
|
+
terminateAfterMs?: number;
|
|
176
|
+
}): Promise<void>;
|
|
177
|
+
/** Whether `shutdown` ran. */
|
|
178
|
+
get isClosing(): boolean;
|
|
179
|
+
private sayBye;
|
|
180
|
+
/** In the application's scope, as a request is: facades resolve from it. */
|
|
181
|
+
private run;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Whether an `allowedOrigins` entry (or `APP_URL` / `HOST_NAME`) admits
|
|
185
|
+
* `url`. An entry is an origin (`https://app.example.com`), a bare host
|
|
186
|
+
* (`app.example.com`), or a wildcard for the subdomains of a host
|
|
187
|
+
* (`*.example.com`, `https://*.example.com`), which does not admit the host
|
|
188
|
+
* itself.
|
|
189
|
+
*/
|
|
190
|
+
export declare function originMatches(allowed: string, url: URL): boolean;
|
|
191
|
+
export {};
|
|
192
|
+
//# sourceMappingURL=SocketHub.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SocketHub.d.ts","sourceRoot":"","sources":["../../../services/broadcast/SocketHub.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ChannelTarget } from "./channels";
|
|
2
|
+
/**
|
|
3
|
+
* Marks a `BroadcastEvent` instance. A symbol in the global registry rather
|
|
4
|
+
* than `instanceof`, for the reason `Event` gives: a production build can hold
|
|
5
|
+
* two copies of an event class, and `instanceof` would tell them apart.
|
|
6
|
+
*/
|
|
7
|
+
export declare const BROADCAST_EVENT: unique symbol;
|
|
8
|
+
/** What `EventManager` needs of an event to broadcast it. */
|
|
9
|
+
export interface BroadcastableEvent {
|
|
10
|
+
broadcastOn(): ChannelTarget | string | ReadonlyArray<ChannelTarget | string>;
|
|
11
|
+
broadcastAs(): string;
|
|
12
|
+
broadcastWith(): unknown;
|
|
13
|
+
}
|
|
14
|
+
/** Whether `event` is a `BroadcastEvent`, from any copy of gemi. */
|
|
15
|
+
export declare function isBroadcastEvent(event: unknown): event is BroadcastableEvent;
|
|
16
|
+
//# sourceMappingURL=brand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brand.d.ts","sourceRoot":"","sources":["../../../services/broadcast/brand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAMhD;;;;GAIG;AACH,eAAO,MAAM,eAAe,eAAoC,CAAC;AAEjE,6DAA6D;AAC7D,MAAM,WAAW,kBAAkB;IACjC,WAAW,IAAI,aAAa,GAAG,MAAM,GAAG,aAAa,CAAC,aAAa,GAAG,MAAM,CAAC,CAAC;IAC9E,WAAW,IAAI,MAAM,CAAC;IACtB,aAAa,IAAI,OAAO,CAAC;CAC1B;AAED,oEAAoE;AACpE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,kBAAkB,CAM5E"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel names: patterns, the topics they build, and the checks both go
|
|
3
|
+
* through. Shared by the `Broadcast` facade (which builds the topic an emit
|
|
4
|
+
* goes to), `ChannelRouter` (which builds the topic a socket may join) and the
|
|
5
|
+
* fake (which matches a pattern against recorded topics), so the three can
|
|
6
|
+
* never disagree on what `site.:siteId` with `{ siteId: "abc" }` is called.
|
|
7
|
+
*
|
|
8
|
+
* A pattern is dot-separated segments, each either a literal (`site`) or a
|
|
9
|
+
* param (`:siteId`). A topic is the same with every param filled in:
|
|
10
|
+
* `site.abc123`. Param values are restricted to `[A-Za-z0-9_-]`, so a value
|
|
11
|
+
* can never contain a dot and add a segment, and a topic can always be read
|
|
12
|
+
* back as exactly one pattern's.
|
|
13
|
+
*/
|
|
14
|
+
/** The longest topic, in characters. */
|
|
15
|
+
export declare const MAX_TOPIC_LENGTH = 255;
|
|
16
|
+
/** The longest event name, in characters. */
|
|
17
|
+
export declare const MAX_EVENT_NAME_LENGTH = 64;
|
|
18
|
+
/** The names of a pattern's params: `"site.:siteId"` → `"siteId"`. */
|
|
19
|
+
export type ChannelParamNames<P extends string> = P extends `${infer Head}.${infer Rest}` ? SegmentParam<Head> | ChannelParamNames<Rest> : SegmentParam<P>;
|
|
20
|
+
type SegmentParam<S extends string> = S extends `:${infer Name}` ? Name : never;
|
|
21
|
+
/** The params a pattern takes: `"site.:siteId"` → `{ siteId: string | number }`. */
|
|
22
|
+
export type ChannelParams<P extends string> = {
|
|
23
|
+
[K in ChannelParamNames<P>]: string | number;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* `[]` for a pattern without params (or a concrete topic), `[params]` for one
|
|
27
|
+
* with them, so `Broadcast.to("status")` and `Broadcast.to("site.:siteId",
|
|
28
|
+
* { siteId })` both type-check and `Broadcast.to("site.:siteId")` does not.
|
|
29
|
+
*/
|
|
30
|
+
export type ChannelParamsArgs<P extends string> = [ChannelParamNames<P>] extends [never] ? [params?: Record<string, never>] : [params: ChannelParams<P>];
|
|
31
|
+
/**
|
|
32
|
+
* A resolved channel: the topic sockets subscribe to, and the pattern it came
|
|
33
|
+
* from when it was built from one. What `BroadcastEvent.channel()` returns and
|
|
34
|
+
* `Broadcast.to()` accepts.
|
|
35
|
+
*/
|
|
36
|
+
export interface ChannelTarget {
|
|
37
|
+
readonly topic: string;
|
|
38
|
+
readonly pattern?: string;
|
|
39
|
+
}
|
|
40
|
+
/** A pattern, topic or param was refused. The message says which and why. */
|
|
41
|
+
export declare class InvalidChannelError extends Error {
|
|
42
|
+
constructor(message: string);
|
|
43
|
+
}
|
|
44
|
+
/** Whether `value` is a `ChannelTarget` rather than a pattern string. */
|
|
45
|
+
export declare function isChannelTarget(value: unknown): value is ChannelTarget;
|
|
46
|
+
/** The segments of a pattern, checked. Throws `InvalidChannelError`. */
|
|
47
|
+
export declare function parsePattern(pattern: string): string[];
|
|
48
|
+
/** The param names of a pattern, in order. */
|
|
49
|
+
export declare function patternParams(pattern: string): string[];
|
|
50
|
+
/**
|
|
51
|
+
* The topic `pattern` names with `params` filled in. Every param must be
|
|
52
|
+
* given, and nothing else: a missing one, an extra one, or a value outside
|
|
53
|
+
* `[A-Za-z0-9_-]` throws `InvalidChannelError`, so a client can never steer a
|
|
54
|
+
* subscription onto a topic its pattern does not describe.
|
|
55
|
+
*/
|
|
56
|
+
export declare function buildTopic(pattern: string, params?: Record<string, unknown>): string;
|
|
57
|
+
/** Checks a concrete topic (no params). Throws `InvalidChannelError`. */
|
|
58
|
+
export declare function assertTopic(topic: string): string;
|
|
59
|
+
/** Checks an event name. Throws `InvalidChannelError`. */
|
|
60
|
+
export declare function assertEventName(event: string): string;
|
|
61
|
+
/**
|
|
62
|
+
* Whether `topic` is one `pattern` builds. A pattern without params matches
|
|
63
|
+
* only itself, except `"user"`, which also matches every `user.<id>`: that is
|
|
64
|
+
* the topic the router's `user` channel resolves to.
|
|
65
|
+
*/
|
|
66
|
+
export declare function topicMatches(pattern: string, topic: string): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* The channel `ChannelRouter` resolves from the session rather than from
|
|
69
|
+
* params: a socket that subscribes to `"user"` joins `user.<its user's id>`.
|
|
70
|
+
*/
|
|
71
|
+
export declare const USER_CHANNEL = "user";
|
|
72
|
+
/** The topic of a user's own channel. */
|
|
73
|
+
export declare function userTopic(user: {
|
|
74
|
+
id: unknown;
|
|
75
|
+
} | string | number): string;
|
|
76
|
+
export {};
|
|
77
|
+
//# sourceMappingURL=channels.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channels.d.ts","sourceRoot":"","sources":["../../../services/broadcast/channels.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,wCAAwC;AACxC,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC,6CAA6C;AAC7C,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAOxC,sEAAsE;AACtE,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,GAAG,MAAM,IAAI,IAAI,MAAM,IAAI,EAAE,GACrF,YAAY,CAAC,IAAI,CAAC,GAAG,iBAAiB,CAAC,IAAI,CAAC,GAC5C,YAAY,CAAC,CAAC,CAAC,CAAC;AAEpB,KAAK,YAAY,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,IAAI,MAAM,IAAI,EAAE,GAAG,IAAI,GAAG,KAAK,CAAC;AAEhF,oFAAoF;AACpF,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,MAAM,IAAI;KAC3C,CAAC,IAAI,iBAAiB,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,MAAM;CAC7C,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GACpF,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,GAChC,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;AAE/B;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,6EAA6E;AAC7E,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;CAI5B;AAED,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAMtE;AAED,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAqCtD;AAED,8CAA8C;AAC9C,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAIvD;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,MAAM,CAkCxF;AAED,yEAAyE;AACzE,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CASjD;AAED,0DAA0D;AAC1D,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAarD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAYpE;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,SAAS,CAAC;AAEnC,yCAAyC;AACzC,wBAAgB,SAAS,CAAC,IAAI,EAAE;IAAE,EAAE,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAGzE"}
|