@geekmidas/constructs 10.0.0-alpha.4 → 10.0.0-alpha.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +223 -158
- package/dist/{Construct-BD5neF7w.mjs → Construct-BO1X-kt9.mjs} +32 -33
- package/dist/Construct-BO1X-kt9.mjs.map +1 -0
- package/dist/Construct-BQALFCh2.d.cts +230 -0
- package/dist/Construct-BQALFCh2.d.cts.map +1 -0
- package/dist/{Construct-CyS3RpTL.cjs → Construct-DmvXceVY.cjs} +52 -47
- package/dist/Construct-DmvXceVY.cjs.map +1 -0
- package/dist/Construct-QXnl3VqT.d.mts +230 -0
- package/dist/Construct-QXnl3VqT.d.mts.map +1 -0
- package/dist/{Cron-B2OFsfDM.d.cts → Cron-CMEGfRs8.d.cts} +21 -8
- package/dist/Cron-CMEGfRs8.d.cts.map +1 -0
- package/dist/{Cron-ByxdOfpE.d.mts → Cron-D6X4-xjB.d.mts} +21 -8
- package/dist/Cron-D6X4-xjB.d.mts.map +1 -0
- package/dist/CronBuilder-CEK7aOqD.d.cts +46 -0
- package/dist/CronBuilder-CEK7aOqD.d.cts.map +1 -0
- package/dist/CronBuilder-DB35T5dL.cjs +116 -0
- package/dist/CronBuilder-DB35T5dL.cjs.map +1 -0
- package/dist/CronBuilder-Dv0IlZs0.mjs +104 -0
- package/dist/CronBuilder-Dv0IlZs0.mjs.map +1 -0
- package/dist/CronBuilder-O_gvButp.d.mts +46 -0
- package/dist/CronBuilder-O_gvButp.d.mts.map +1 -0
- package/dist/{Endpoint-Dij_wOcc.mjs → Endpoint-CQAeh1Rh.mjs} +117 -109
- package/dist/Endpoint-CQAeh1Rh.mjs.map +1 -0
- package/dist/{Endpoint-DODn1RhN.cjs → Endpoint-D1L9qu5C.cjs} +145 -131
- package/dist/Endpoint-D1L9qu5C.cjs.map +1 -0
- package/dist/{Endpoint-2Wg7T7Cq.d.mts → Endpoint-Dp5AKIXl.d.cts} +69 -93
- package/dist/Endpoint-Dp5AKIXl.d.cts.map +1 -0
- package/dist/{Endpoint-DrD8a1yH.d.cts → Endpoint-DtI7heFm.d.mts} +70 -94
- package/dist/Endpoint-DtI7heFm.d.mts.map +1 -0
- package/dist/{EndpointFactory-b4ituq-s.mjs → EndpointFactory-BBdNeaGJ.mjs} +39 -74
- package/dist/EndpointFactory-BBdNeaGJ.mjs.map +1 -0
- package/dist/{EndpointFactory-BHFMmFkV.cjs → EndpointFactory-BZOCOaK6.cjs} +75 -110
- package/dist/EndpointFactory-BZOCOaK6.cjs.map +1 -0
- package/dist/{index-BxHDdy6a.d.mts → EndpointFactory-CgMDvERK.d.mts} +63 -133
- package/dist/EndpointFactory-CgMDvERK.d.mts.map +1 -0
- package/dist/{index-B8g8POkr.d.cts → EndpointFactory-FeFRmlDZ.d.cts} +62 -132
- package/dist/EndpointFactory-FeFRmlDZ.d.cts.map +1 -0
- package/dist/{HonoEndpointAdaptor-BM2jbWht.mjs → HonoEndpointAdaptor-Aui-x9Lg.mjs} +41 -47
- package/dist/HonoEndpointAdaptor-Aui-x9Lg.mjs.map +1 -0
- package/dist/{HonoEndpointAdaptor-De6IlBZe.cjs → HonoEndpointAdaptor-Cq99r9H7.cjs} +50 -53
- package/dist/HonoEndpointAdaptor-Cq99r9H7.cjs.map +1 -0
- package/dist/Queue-C1uDgjxt.mjs +100 -0
- package/dist/Queue-C1uDgjxt.mjs.map +1 -0
- package/dist/Queue-Ckmudcm8.cjs +105 -0
- package/dist/Queue-Ckmudcm8.cjs.map +1 -0
- package/dist/{Queue-D7ZuwBhy.d.cts → Queue-CpQIGB_2.d.cts} +28 -40
- package/dist/Queue-CpQIGB_2.d.cts.map +1 -0
- package/dist/{Queue-gVGfi2Yk.d.mts → Queue-YmvN2vEP.d.mts} +28 -40
- package/dist/Queue-YmvN2vEP.d.mts.map +1 -0
- package/dist/QueueBuilder-CJNcv4dy.cjs +112 -0
- package/dist/QueueBuilder-CJNcv4dy.cjs.map +1 -0
- package/dist/QueueBuilder-CM0WUxfw.d.mts +62 -0
- package/dist/QueueBuilder-CM0WUxfw.d.mts.map +1 -0
- package/dist/QueueBuilder-XEnnOWGe.d.cts +62 -0
- package/dist/QueueBuilder-XEnnOWGe.d.cts.map +1 -0
- package/dist/QueueBuilder-XuPKZwKH.mjs +95 -0
- package/dist/QueueBuilder-XuPKZwKH.mjs.map +1 -0
- package/dist/{Subscriber-2mEi0ImH.d.cts → Subscriber-B91YSkZt.d.cts} +14 -16
- package/dist/Subscriber-B91YSkZt.d.cts.map +1 -0
- package/dist/{Subscriber-xTJ9ayPN.d.mts → Subscriber-BjHYe5hK.d.mts} +14 -16
- package/dist/Subscriber-BjHYe5hK.d.mts.map +1 -0
- package/dist/SubscriberBuilder-BSpQX1JT.d.cts +55 -0
- package/dist/SubscriberBuilder-BSpQX1JT.d.cts.map +1 -0
- package/dist/SubscriberBuilder-BhMphk4z.d.mts +55 -0
- package/dist/SubscriberBuilder-BhMphk4z.d.mts.map +1 -0
- package/dist/{SubscriberBuilder-4OPehf2R.cjs → SubscriberBuilder-Duk028pD.cjs} +34 -37
- package/dist/SubscriberBuilder-Duk028pD.cjs.map +1 -0
- package/dist/{SubscriberBuilder-BjkbeT9l.mjs → SubscriberBuilder-DzQUWE08.mjs} +23 -25
- package/dist/SubscriberBuilder-DzQUWE08.mjs.map +1 -0
- package/dist/Topic-CkxOTrMv.cjs +104 -0
- package/dist/Topic-CkxOTrMv.cjs.map +1 -0
- package/dist/Topic-hPffOsot.mjs +99 -0
- package/dist/Topic-hPffOsot.mjs.map +1 -0
- package/dist/adaptors/aws.cjs +171 -108
- package/dist/adaptors/aws.cjs.map +1 -1
- package/dist/adaptors/aws.d.cts +75 -43
- package/dist/adaptors/aws.d.cts.map +1 -1
- package/dist/adaptors/aws.d.mts +76 -44
- package/dist/adaptors/aws.d.mts.map +1 -1
- package/dist/adaptors/aws.mjs +143 -83
- package/dist/adaptors/aws.mjs.map +1 -1
- package/dist/adaptors/hono.cjs +3 -10
- package/dist/adaptors/hono.d.cts +7 -16
- package/dist/adaptors/hono.d.cts.map +1 -1
- package/dist/adaptors/hono.d.mts +8 -17
- package/dist/adaptors/hono.d.mts.map +1 -1
- package/dist/adaptors/hono.mjs +2 -10
- package/dist/adaptors/testing.cjs +515 -87
- package/dist/adaptors/testing.cjs.map +1 -1
- package/dist/adaptors/testing.d.cts +250 -48
- package/dist/adaptors/testing.d.cts.map +1 -1
- package/dist/adaptors/testing.d.mts +250 -48
- package/dist/adaptors/testing.d.mts.map +1 -1
- package/dist/adaptors/testing.mjs +484 -68
- package/dist/adaptors/testing.mjs.map +1 -1
- package/dist/auth-CMwml9QN.d.mts +188 -0
- package/dist/auth-CMwml9QN.d.mts.map +1 -0
- package/dist/auth-rVRDjIw_.d.cts +188 -0
- package/dist/auth-rVRDjIw_.d.cts.map +1 -0
- package/dist/auth.cjs +171 -31
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +2 -108
- package/dist/auth.d.mts +2 -108
- package/dist/auth.mjs +160 -24
- package/dist/auth.mjs.map +1 -1
- package/dist/{cache-BrRz3fEB.d.mts → cache-C9a_hVCc.d.cts} +4 -7
- package/dist/cache-C9a_hVCc.d.cts.map +1 -0
- package/dist/{cache-BtZY_tHe.d.cts → cache-CeTMI8LR.d.mts} +4 -7
- package/dist/cache-CeTMI8LR.d.mts.map +1 -0
- package/dist/cache.cjs +68 -2
- package/dist/cache.cjs.map +1 -0
- package/dist/cache.d.cts +1 -2
- package/dist/cache.d.mts +1 -2
- package/dist/cache.mjs +67 -2
- package/dist/cache.mjs.map +1 -0
- package/dist/{clone-DblgEZZa.mjs → clone-EKNovYHu.mjs} +3 -3
- package/dist/clone-EKNovYHu.mjs.map +1 -0
- package/dist/{clone-Dy0cR2SZ.cjs → clone-UGJpvQnL.cjs} +7 -8
- package/dist/clone-UGJpvQnL.cjs.map +1 -0
- package/dist/{construct-interface-g5GM5pwR.d.mts → construct-interface-_3zgxzXl.d.mts} +20 -11
- package/dist/construct-interface-_3zgxzXl.d.mts.map +1 -0
- package/dist/{construct-interface-D9d4Gm4p.d.cts → construct-interface-_xi4BPN8.d.cts} +20 -11
- package/dist/construct-interface-_xi4BPN8.d.cts.map +1 -0
- package/dist/construct-interface.cjs +87 -7
- package/dist/construct-interface.cjs.map +1 -0
- package/dist/construct-interface.d.cts +2 -2
- package/dist/construct-interface.d.mts +2 -2
- package/dist/construct-interface.mjs +81 -2
- package/dist/construct-interface.mjs.map +1 -0
- package/dist/credential.cjs +63 -16
- package/dist/credential.cjs.map +1 -1
- package/dist/credential.d.cts +16 -9
- package/dist/credential.d.cts.map +1 -1
- package/dist/credential.d.mts +16 -9
- package/dist/credential.d.mts.map +1 -1
- package/dist/credential.mjs +58 -12
- package/dist/credential.mjs.map +1 -1
- package/dist/crons/index.cjs +68 -10
- package/dist/crons/index.cjs.map +1 -1
- package/dist/crons/index.d.cts +32 -14
- package/dist/crons/index.d.cts.map +1 -1
- package/dist/crons/index.d.mts +32 -14
- package/dist/crons/index.d.mts.map +1 -1
- package/dist/crons/index.mjs +66 -10
- package/dist/crons/index.mjs.map +1 -1
- package/dist/database/kysely.cjs +40 -15
- package/dist/database/kysely.cjs.map +1 -1
- package/dist/database/kysely.d.cts +2 -152
- package/dist/database/kysely.d.mts +2 -152
- package/dist/database/kysely.mjs +28 -5
- package/dist/database/kysely.mjs.map +1 -1
- package/dist/email.cjs +25 -12
- package/dist/email.cjs.map +1 -1
- package/dist/email.d.cts +3 -7
- package/dist/email.d.cts.map +1 -1
- package/dist/email.d.mts +3 -7
- package/dist/email.d.mts.map +1 -1
- package/dist/email.mjs +16 -3
- package/dist/email.mjs.map +1 -1
- package/dist/endpoints/index.cjs +8 -10
- package/dist/endpoints/index.d.cts +51 -7
- package/dist/endpoints/index.d.cts.map +1 -0
- package/dist/endpoints/index.d.mts +51 -7
- package/dist/endpoints/index.d.mts.map +1 -0
- package/dist/endpoints/index.mjs +6 -10
- package/dist/external-api-DS3mujT5.d.mts +86 -0
- package/dist/external-api-DS3mujT5.d.mts.map +1 -0
- package/dist/external-api-LBfE-q3g.d.cts +86 -0
- package/dist/external-api-LBfE-q3g.d.cts.map +1 -0
- package/dist/external-api.cjs +124 -0
- package/dist/external-api.cjs.map +1 -0
- package/dist/external-api.d.cts +2 -0
- package/dist/external-api.d.mts +2 -0
- package/dist/external-api.mjs +121 -0
- package/dist/external-api.mjs.map +1 -0
- package/dist/file-server.cjs +42 -10
- package/dist/file-server.cjs.map +1 -1
- package/dist/file-server.d.cts +6 -10
- package/dist/file-server.d.cts.map +1 -1
- package/dist/file-server.d.mts +6 -10
- package/dist/file-server.d.mts.map +1 -1
- package/dist/file-server.mjs +34 -2
- package/dist/file-server.mjs.map +1 -1
- package/dist/functions/index.cjs +2 -6
- package/dist/functions/index.d.cts +2 -4
- package/dist/functions/index.d.mts +2 -4
- package/dist/functions/index.mjs +2 -6
- package/dist/{functions-Ck21Uyq0.mjs → functions-DCIfCRR-.mjs} +371 -241
- package/dist/functions-DCIfCRR-.mjs.map +1 -0
- package/dist/{functions-DJJA7VGc.cjs → functions-ZH60FE7H.cjs} +409 -273
- package/dist/functions-ZH60FE7H.cjs.map +1 -0
- package/dist/{index-AIfVzkhi.d.cts → index-B0kxstwf.d.cts} +49 -42
- package/dist/index-B0kxstwf.d.cts.map +1 -0
- package/dist/index-CeEtxDmK.d.cts +79 -0
- package/dist/index-CeEtxDmK.d.cts.map +1 -0
- package/dist/index-CeEtxDmK.d.mts +79 -0
- package/dist/index-CeEtxDmK.d.mts.map +1 -0
- package/dist/{index-CubPzHd9.d.mts → index-CjVEjOw0.d.mts} +49 -42
- package/dist/index-CjVEjOw0.d.mts.map +1 -0
- package/dist/index.cjs +3 -3
- package/dist/index.d.cts +4 -4
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +2 -3
- package/dist/kysely-BJ8IQ_1P.d.mts +159 -0
- package/dist/kysely-BJ8IQ_1P.d.mts.map +1 -0
- package/dist/kysely-DVF8wOT4.d.cts +159 -0
- package/dist/kysely-DVF8wOT4.d.cts.map +1 -0
- package/dist/{lazyAccessors-BoJN3SMd.mjs → lazyAccessors-CFanua0O.mjs} +20 -60
- package/dist/lazyAccessors-CFanua0O.mjs.map +1 -0
- package/dist/{lazyAccessors-e3j2oE2m.cjs → lazyAccessors-hJKGv8Ay.cjs} +60 -113
- package/dist/lazyAccessors-hJKGv8Ay.cjs.map +1 -0
- package/dist/mobile-app.cjs +70 -0
- package/dist/mobile-app.cjs.map +1 -0
- package/dist/mobile-app.d.cts +61 -0
- package/dist/mobile-app.d.cts.map +1 -0
- package/dist/mobile-app.d.mts +61 -0
- package/dist/mobile-app.d.mts.map +1 -0
- package/dist/mobile-app.mjs +69 -0
- package/dist/mobile-app.mjs.map +1 -0
- package/dist/object-storage.cjs +18 -10
- package/dist/object-storage.cjs.map +1 -1
- package/dist/object-storage.d.cts +3 -7
- package/dist/object-storage.d.cts.map +1 -1
- package/dist/object-storage.d.mts +3 -7
- package/dist/object-storage.d.mts.map +1 -1
- package/dist/object-storage.mjs +11 -3
- package/dist/object-storage.mjs.map +1 -1
- package/dist/{processAudits-Dmts9H_b.mjs → processAudits-BpRlPQMW.mjs} +16 -19
- package/dist/processAudits-BpRlPQMW.mjs.map +1 -0
- package/dist/{processAudits-CGqttuXm.cjs → processAudits-D_2W5x8k.cjs} +26 -30
- package/dist/processAudits-D_2W5x8k.cjs.map +1 -0
- package/dist/queue/index.cjs +6 -189
- package/dist/queue/index.d.cts +3 -60
- package/dist/queue/index.d.mts +3 -60
- package/dist/queue/index.mjs +3 -186
- package/dist/rest-api.cjs +87 -63
- package/dist/rest-api.cjs.map +1 -1
- package/dist/rest-api.d.cts +62 -61
- package/dist/rest-api.d.cts.map +1 -1
- package/dist/rest-api.d.mts +62 -61
- package/dist/rest-api.d.mts.map +1 -1
- package/dist/rest-api.mjs +79 -55
- package/dist/rest-api.mjs.map +1 -1
- package/dist/{chunk-DWy1uDak.cjs → rolldown-runtime-y5ARvAq5.cjs} +14 -18
- package/dist/site.cjs +33 -9
- package/dist/site.cjs.map +1 -1
- package/dist/site.d.cts +11 -22
- package/dist/site.d.cts.map +1 -1
- package/dist/site.d.mts +11 -22
- package/dist/site.d.mts.map +1 -1
- package/dist/site.mjs +29 -5
- package/dist/site.mjs.map +1 -1
- package/dist/subscribers/index.cjs +2 -11
- package/dist/subscribers/index.d.cts +3 -14
- package/dist/subscribers/index.d.mts +3 -14
- package/dist/subscribers/index.mjs +2 -11
- package/dist/surfaceEnv-CNimK76p.cjs +56 -0
- package/dist/surfaceEnv-CNimK76p.cjs.map +1 -0
- package/dist/surfaceEnv-wMT5QzgZ.mjs +45 -0
- package/dist/surfaceEnv-wMT5QzgZ.mjs.map +1 -0
- package/dist/{telemetry-C3L0iS28.d.cts → telemetry-DGcFlVpB.d.cts} +2 -5
- package/dist/telemetry-DGcFlVpB.d.cts.map +1 -0
- package/dist/{telemetry-D7bVFLGi.d.mts → telemetry-DGcFlVpB.d.mts} +2 -5
- package/dist/telemetry-DGcFlVpB.d.mts.map +1 -0
- package/dist/topic/index.cjs +3 -137
- package/dist/topic/index.d.cts +2 -32
- package/dist/topic/index.d.mts +2 -32
- package/dist/topic/index.mjs +2 -134
- package/dist/{types-BlAkSuGI.d.cts → types-DMuSQeLL.d.cts} +3 -5
- package/dist/types-DMuSQeLL.d.cts.map +1 -0
- package/dist/{types-aKksMEWL.d.mts → types-DMuSQeLL.d.mts} +3 -5
- package/dist/types-DMuSQeLL.d.mts.map +1 -0
- package/dist/types.d.cts +1 -1
- package/dist/types.d.mts +1 -1
- package/dist/types.mjs +1 -0
- package/dist/worker.cjs +118 -35
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.d.cts +95 -42
- package/dist/worker.d.cts.map +1 -1
- package/dist/worker.d.mts +95 -42
- package/dist/worker.d.mts.map +1 -1
- package/dist/worker.mjs +113 -30
- package/dist/worker.mjs.map +1 -1
- package/package.json +66 -41
- package/dist/Construct-BD5neF7w.mjs.map +0 -1
- package/dist/Construct-C0ijr2qp.d.cts +0 -117
- package/dist/Construct-C0ijr2qp.d.cts.map +0 -1
- package/dist/Construct-Ct8nalEi.d.mts +0 -117
- package/dist/Construct-Ct8nalEi.d.mts.map +0 -1
- package/dist/Construct-CyS3RpTL.cjs.map +0 -1
- package/dist/Cron-B2OFsfDM.d.cts.map +0 -1
- package/dist/Cron-ByxdOfpE.d.mts.map +0 -1
- package/dist/CronBuilder-BL0AP6fs.d.mts +0 -44
- package/dist/CronBuilder-BL0AP6fs.d.mts.map +0 -1
- package/dist/CronBuilder-CdibiHNB.mjs +0 -84
- package/dist/CronBuilder-CdibiHNB.mjs.map +0 -1
- package/dist/CronBuilder-DyUr6nYz.cjs +0 -96
- package/dist/CronBuilder-DyUr6nYz.cjs.map +0 -1
- package/dist/CronBuilder-LSJMfqnq.d.cts +0 -44
- package/dist/CronBuilder-LSJMfqnq.d.cts.map +0 -1
- package/dist/Endpoint-2Wg7T7Cq.d.mts.map +0 -1
- package/dist/Endpoint-DODn1RhN.cjs.map +0 -1
- package/dist/Endpoint-Dij_wOcc.mjs.map +0 -1
- package/dist/Endpoint-DrD8a1yH.d.cts.map +0 -1
- package/dist/EndpointFactory-BHFMmFkV.cjs.map +0 -1
- package/dist/EndpointFactory-b4ituq-s.mjs.map +0 -1
- package/dist/HonoEndpointAdaptor-BM2jbWht.mjs.map +0 -1
- package/dist/HonoEndpointAdaptor-De6IlBZe.cjs.map +0 -1
- package/dist/Queue-D7ZuwBhy.d.cts.map +0 -1
- package/dist/Queue-gVGfi2Yk.d.mts.map +0 -1
- package/dist/Subscriber-2mEi0ImH.d.cts.map +0 -1
- package/dist/Subscriber-xTJ9ayPN.d.mts.map +0 -1
- package/dist/SubscriberBuilder-4OPehf2R.cjs.map +0 -1
- package/dist/SubscriberBuilder-BjkbeT9l.mjs.map +0 -1
- package/dist/SubscriberBuilder-Dptmo-1A.d.mts +0 -62
- package/dist/SubscriberBuilder-Dptmo-1A.d.mts.map +0 -1
- package/dist/SubscriberBuilder-waNhzjSM.d.cts +0 -62
- package/dist/SubscriberBuilder-waNhzjSM.d.cts.map +0 -1
- package/dist/Topic-19QeHBIR.d.cts +0 -91
- package/dist/Topic-19QeHBIR.d.cts.map +0 -1
- package/dist/Topic-DuFeqkBh.d.mts +0 -91
- package/dist/Topic-DuFeqkBh.d.mts.map +0 -1
- package/dist/auth.d.cts.map +0 -1
- package/dist/auth.d.mts.map +0 -1
- package/dist/cache-0ANvRQnD.mjs +0 -51
- package/dist/cache-0ANvRQnD.mjs.map +0 -1
- package/dist/cache-3WSvAulx.cjs +0 -57
- package/dist/cache-3WSvAulx.cjs.map +0 -1
- package/dist/cache-BrRz3fEB.d.mts.map +0 -1
- package/dist/cache-BtZY_tHe.d.cts.map +0 -1
- package/dist/clone-DblgEZZa.mjs.map +0 -1
- package/dist/clone-Dy0cR2SZ.cjs.map +0 -1
- package/dist/construct-interface-BfjcOD_E.cjs +0 -117
- package/dist/construct-interface-BfjcOD_E.cjs.map +0 -1
- package/dist/construct-interface-D9d4Gm4p.d.cts.map +0 -1
- package/dist/construct-interface-Z4CpS8tK.mjs +0 -81
- package/dist/construct-interface-Z4CpS8tK.mjs.map +0 -1
- package/dist/construct-interface-g5GM5pwR.d.mts.map +0 -1
- package/dist/database/kysely.d.cts.map +0 -1
- package/dist/database/kysely.d.mts.map +0 -1
- package/dist/functions-Ck21Uyq0.mjs.map +0 -1
- package/dist/functions-DJJA7VGc.cjs.map +0 -1
- package/dist/index-AIfVzkhi.d.cts.map +0 -1
- package/dist/index-B8g8POkr.d.cts.map +0 -1
- package/dist/index-BxHDdy6a.d.mts.map +0 -1
- package/dist/index-CubPzHd9.d.mts.map +0 -1
- package/dist/lazyAccessors-BoJN3SMd.mjs.map +0 -1
- package/dist/lazyAccessors-e3j2oE2m.cjs.map +0 -1
- package/dist/processAudits-CGqttuXm.cjs.map +0 -1
- package/dist/processAudits-Dmts9H_b.mjs.map +0 -1
- package/dist/queue/index.cjs.map +0 -1
- package/dist/queue/index.d.cts.map +0 -1
- package/dist/queue/index.d.mts.map +0 -1
- package/dist/queue/index.mjs.map +0 -1
- package/dist/subscribers/index.cjs.map +0 -1
- package/dist/subscribers/index.d.cts.map +0 -1
- package/dist/subscribers/index.d.mts.map +0 -1
- package/dist/subscribers/index.mjs.map +0 -1
- package/dist/surfaceEnv-BFXEms2f.cjs +0 -39
- package/dist/surfaceEnv-BFXEms2f.cjs.map +0 -1
- package/dist/surfaceEnv-C3Ktahea.mjs +0 -27
- package/dist/surfaceEnv-C3Ktahea.mjs.map +0 -1
- package/dist/telemetry-C3L0iS28.d.cts.map +0 -1
- package/dist/telemetry-D7bVFLGi.d.mts.map +0 -1
- package/dist/topic/index.cjs.map +0 -1
- package/dist/topic/index.d.cts.map +0 -1
- package/dist/topic/index.d.mts.map +0 -1
- package/dist/topic/index.mjs.map +0 -1
- package/dist/types-BlAkSuGI.d.cts.map +0 -1
- package/dist/types-aKksMEWL.d.mts.map +0 -1
package/README.md
CHANGED
|
@@ -26,21 +26,20 @@ pnpm add @geekmidas/constructs
|
|
|
26
26
|
|
|
27
27
|
```typescript
|
|
28
28
|
// The HTTP surface. Endpoints are built from it — `api.get(…)`, or
|
|
29
|
-
// `api.
|
|
29
|
+
// `api.database(db)` for a branch that a group of endpoints shares.
|
|
30
30
|
import { RestApi } from '@geekmidas/constructs/rest-api';
|
|
31
31
|
|
|
32
32
|
// Endpoint internals: the factory a surface hands you, and the builder it
|
|
33
33
|
// returns. There is no free-standing `e` — an endpoint belongs to a surface.
|
|
34
34
|
import { EndpointBuilder, EndpointFactory } from '@geekmidas/constructs/endpoints';
|
|
35
35
|
|
|
36
|
-
//
|
|
37
|
-
|
|
36
|
+
// The process with no port. Crons, topic subscribers, queues and functions
|
|
37
|
+
// are built from it — `worker.cron(…)`, `worker.topic(…)`, `worker.queue(…)`,
|
|
38
|
+
// `worker.input(…)` — and carry its logger.
|
|
39
|
+
import { Worker } from '@geekmidas/constructs/worker';
|
|
38
40
|
|
|
39
|
-
//
|
|
40
|
-
import {
|
|
41
|
-
|
|
42
|
-
// Subscribers
|
|
43
|
-
import { s, SubscriberBuilder } from '@geekmidas/constructs/subscribers';
|
|
41
|
+
// A declared event contract. Its publisher is its `service`.
|
|
42
|
+
import { Topic } from '@geekmidas/constructs/topic';
|
|
44
43
|
```
|
|
45
44
|
|
|
46
45
|
## Quick Start
|
|
@@ -57,6 +56,7 @@ import { RestApi } from '@geekmidas/constructs/rest-api';
|
|
|
57
56
|
import { logger } from './logger';
|
|
58
57
|
|
|
59
58
|
export const api = new RestApi('Api', {
|
|
59
|
+
path: 'apps/api',
|
|
60
60
|
// Required, and `'none'` is a valid answer that has to be typed out. An API
|
|
61
61
|
// that ships open because a field was left off is the one default worth
|
|
62
62
|
// refusing to have.
|
|
@@ -125,15 +125,31 @@ export const searchUsers = api
|
|
|
125
125
|
});
|
|
126
126
|
```
|
|
127
127
|
|
|
128
|
+
### The Worker
|
|
129
|
+
|
|
130
|
+
Everything that is not an HTTP route is built from a `Worker`: the process
|
|
131
|
+
that runs a schedule, drains a queue or consumes a topic. It takes no
|
|
132
|
+
authorizer, because nothing reaches it from outside.
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
// constructs/worker.ts
|
|
136
|
+
import { Worker } from '@geekmidas/constructs/worker';
|
|
137
|
+
import { database } from './database';
|
|
138
|
+
import { logger } from './logger';
|
|
139
|
+
|
|
140
|
+
// `.database(…)` is where a server target keeps cron schedules.
|
|
141
|
+
export const worker = new Worker('Jobs', { logger }).database(database);
|
|
142
|
+
```
|
|
143
|
+
|
|
128
144
|
### Cloud Functions
|
|
129
145
|
|
|
130
|
-
Create
|
|
146
|
+
Create functions with input/output validation:
|
|
131
147
|
|
|
132
148
|
```typescript
|
|
133
|
-
import {
|
|
149
|
+
import { worker } from '../constructs/worker';
|
|
134
150
|
import { z } from 'zod';
|
|
135
151
|
|
|
136
|
-
export const processOrder =
|
|
152
|
+
export const processOrder = worker
|
|
137
153
|
.input(z.object({
|
|
138
154
|
orderId: z.string(),
|
|
139
155
|
items: z.array(z.object({
|
|
@@ -144,13 +160,12 @@ export const processOrder = f
|
|
|
144
160
|
.output(z.object({
|
|
145
161
|
orderId: z.string(),
|
|
146
162
|
status: z.enum(['processing', 'completed', 'failed']),
|
|
147
|
-
processedAt: z.
|
|
163
|
+
processedAt: z.iso.datetime()
|
|
148
164
|
}))
|
|
149
165
|
.timeout(300000) // 5 minutes
|
|
150
166
|
.handle(async ({ input, logger }) => {
|
|
151
167
|
logger.info(`Processing order ${input.orderId}`);
|
|
152
168
|
|
|
153
|
-
// Process order logic
|
|
154
169
|
for (const item of input.items) {
|
|
155
170
|
logger.info(`Processing item ${item.id}, quantity: ${item.quantity}`);
|
|
156
171
|
}
|
|
@@ -165,87 +180,99 @@ export const processOrder = f
|
|
|
165
180
|
|
|
166
181
|
### Scheduled Tasks (Crons)
|
|
167
182
|
|
|
168
|
-
Define cron jobs with schedules:
|
|
169
|
-
|
|
170
183
|
```typescript
|
|
171
|
-
import {
|
|
184
|
+
import { worker } from '../constructs/worker';
|
|
172
185
|
|
|
173
186
|
// Daily report at 9 AM UTC
|
|
174
|
-
export const dailyReport =
|
|
175
|
-
.
|
|
187
|
+
export const dailyReport = worker
|
|
188
|
+
.cron('cron(0 9 * * ? *)')
|
|
176
189
|
.timeout(600000) // 10 minutes
|
|
177
190
|
.handle(async ({ logger }) => {
|
|
178
|
-
logger.info('Generating daily report');
|
|
179
|
-
|
|
180
|
-
const reportDate = new Date().toISOString().split('T')[0];
|
|
181
191
|
const reportData = {
|
|
182
|
-
date:
|
|
192
|
+
date: new Date().toISOString().split('T')[0],
|
|
183
193
|
totalOrders: 150,
|
|
184
|
-
totalRevenue: 12500.00
|
|
185
194
|
};
|
|
186
195
|
|
|
187
|
-
logger.info('Daily report generated'
|
|
196
|
+
logger.info(reportData, 'Daily report generated');
|
|
188
197
|
return reportData;
|
|
189
198
|
});
|
|
190
199
|
|
|
191
200
|
// Hourly cleanup
|
|
192
|
-
export const hourlyCleanup =
|
|
193
|
-
.
|
|
194
|
-
.
|
|
195
|
-
.handle(async ({ logger }) => {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
201
|
+
export const hourlyCleanup = worker
|
|
202
|
+
.cron('rate(1 hour)')
|
|
203
|
+
.dependsOn([database])
|
|
204
|
+
.handle(async ({ services, logger }) => {
|
|
205
|
+
const result = await services.database
|
|
206
|
+
.deleteFrom('sessions')
|
|
207
|
+
.where('expires_at', '<', new Date())
|
|
208
|
+
.executeTakeFirst();
|
|
200
209
|
|
|
201
|
-
|
|
210
|
+
logger.info({ deleted: Number(result.numDeletedRows) }, 'Cleaned up');
|
|
202
211
|
});
|
|
203
212
|
```
|
|
204
213
|
|
|
205
|
-
|
|
214
|
+
On AWS a cron is an EventBridge rule; on a server the worker schedules it in
|
|
215
|
+
the database it was given.
|
|
216
|
+
|
|
217
|
+
### Topics and Subscribers
|
|
206
218
|
|
|
207
|
-
|
|
219
|
+
A topic declares the event contract once. A subscriber binds to it from the
|
|
220
|
+
worker and is typed by it:
|
|
208
221
|
|
|
209
222
|
```typescript
|
|
210
|
-
|
|
223
|
+
// constructs/topics.ts
|
|
224
|
+
import { Topic } from '@geekmidas/constructs/topic';
|
|
211
225
|
import { z } from 'zod';
|
|
212
226
|
|
|
213
|
-
export const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}))
|
|
220
|
-
.handle(async ({ events, logger }) => {
|
|
221
|
-
logger.info(
|
|
222
|
-
{ eventCount: events.length },
|
|
223
|
-
'Processing user events'
|
|
224
|
-
);
|
|
227
|
+
export const users = new Topic('Users', {
|
|
228
|
+
events: {
|
|
229
|
+
'user.created': z.object({ userId: z.string(), email: z.email() }),
|
|
230
|
+
'user.updated': z.object({ userId: z.string(), changes: z.array(z.string()) }),
|
|
231
|
+
},
|
|
232
|
+
});
|
|
225
233
|
|
|
234
|
+
// subscribers/userEvents.ts
|
|
235
|
+
export const userEventsSubscriber = worker
|
|
236
|
+
.topic(users)
|
|
237
|
+
.subscribe(['user.created', 'user.updated'])
|
|
238
|
+
.handle(async ({ events, logger }) => {
|
|
226
239
|
for (const event of events) {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
case 'user.created':
|
|
230
|
-
logger.info({ userId: event.data.userId }, 'User created');
|
|
231
|
-
break;
|
|
232
|
-
case 'user.updated':
|
|
233
|
-
logger.info({ userId: event.data.userId }, 'User updated');
|
|
234
|
-
break;
|
|
235
|
-
case 'user.deleted':
|
|
236
|
-
logger.info({ userId: event.data.userId }, 'User deleted');
|
|
237
|
-
break;
|
|
238
|
-
}
|
|
239
|
-
} catch (error) {
|
|
240
|
-
logger.error({ error, event }, 'Failed to process event');
|
|
241
|
-
throw error;
|
|
242
|
-
}
|
|
240
|
+
// event.type narrows event.payload to that event's schema
|
|
241
|
+
logger.info({ type: event.type, userId: event.payload.userId }, 'User event');
|
|
243
242
|
}
|
|
243
|
+
});
|
|
244
|
+
```
|
|
244
245
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
246
|
+
A subscriber is bound, not handed a publisher. One that emits follow-ups
|
|
247
|
+
depends on the topic it emits to: `.dependsOn([notifications])`.
|
|
248
|
+
|
|
249
|
+
### Queues
|
|
250
|
+
|
|
251
|
+
A queue and its one consumer are one construct. Producers send by depending on
|
|
252
|
+
it:
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
// queues/emails.ts
|
|
256
|
+
export const emails = worker
|
|
257
|
+
.queue('Emails')
|
|
258
|
+
.message(z.object({ to: z.email(), template: z.enum(['welcome']) }))
|
|
259
|
+
.dependsOn([mail])
|
|
260
|
+
.handle(async ({ messages, services }) => {
|
|
261
|
+
for (const { to, template } of messages) {
|
|
262
|
+
await services.mail.sendTemplate(template, { to, subject: 'Welcome', props: {} });
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// endpoints/invites.ts
|
|
267
|
+
export const invite = api
|
|
268
|
+
.post('/invites')
|
|
269
|
+
.body(z.object({ email: z.email() }))
|
|
270
|
+
.dependsOn([emails])
|
|
271
|
+
.handle(async ({ body, services }) => {
|
|
272
|
+
// `type` is the queue's name exactly as written
|
|
273
|
+
await services.emails.publish([
|
|
274
|
+
{ type: 'Emails', payload: { to: body.email, template: 'welcome' } },
|
|
275
|
+
]);
|
|
249
276
|
});
|
|
250
277
|
```
|
|
251
278
|
|
|
@@ -288,7 +315,7 @@ For custom authentication methods, use `.securitySchemes()` on the surface's fac
|
|
|
288
315
|
```typescript
|
|
289
316
|
import { api } from '../constructs/api';
|
|
290
317
|
|
|
291
|
-
const router = api
|
|
318
|
+
const router = api
|
|
292
319
|
.securitySchemes({
|
|
293
320
|
// Custom OAuth2 with specific flows
|
|
294
321
|
oauth2: {
|
|
@@ -327,7 +354,7 @@ Set a default authorizer for every endpoint built from one branch:
|
|
|
327
354
|
// A branch of the surface's factory, with a default every endpoint on it
|
|
328
355
|
// inherits. (The surface's own `default` does the same thing for every
|
|
329
356
|
// endpoint it serves — this narrows it for one group.)
|
|
330
|
-
const router = api.
|
|
357
|
+
const router = api.authorizer('jwt');
|
|
331
358
|
|
|
332
359
|
// Protected by default
|
|
333
360
|
export const getProfile = router
|
|
@@ -343,99 +370,69 @@ export const getHealth = router
|
|
|
343
370
|
.handle(async () => ({ status: 'ok' }));
|
|
344
371
|
```
|
|
345
372
|
|
|
346
|
-
###
|
|
373
|
+
### Dependencies
|
|
347
374
|
|
|
348
|
-
|
|
375
|
+
`.dependsOn([...])` takes constructs. Each one's client lands in the handler's
|
|
376
|
+
`services` under the construct's own id, and the edge is what the manifest
|
|
377
|
+
records — so the environment, the runtime client and the cloud access all come
|
|
378
|
+
from the one line:
|
|
349
379
|
|
|
350
380
|
```typescript
|
|
351
381
|
import { api } from '../constructs/api';
|
|
352
|
-
import
|
|
353
|
-
import type { EnvironmentParser } from '@geekmidas/envkit';
|
|
354
|
-
import { Kysely } from 'kysely';
|
|
382
|
+
import { database } from '../constructs/database';
|
|
355
383
|
import { z } from 'zod';
|
|
356
384
|
|
|
357
|
-
// Define a database service
|
|
358
|
-
const databaseService = {
|
|
359
|
-
serviceName: 'database' as const,
|
|
360
|
-
async register(envParser: EnvironmentParser<{}>) {
|
|
361
|
-
const config = envParser.create((get) => ({
|
|
362
|
-
url: get('DATABASE_URL').string()
|
|
363
|
-
})).parse();
|
|
364
|
-
|
|
365
|
-
const db = new Kysely({ /* config */ });
|
|
366
|
-
return db;
|
|
367
|
-
}
|
|
368
|
-
} satisfies Service<'database', Kysely<Database>>;
|
|
369
|
-
|
|
370
|
-
// Use service in endpoint
|
|
371
385
|
export const getUserFromDb = api
|
|
372
386
|
.get('/users/:id')
|
|
373
387
|
.params(z.object({ id: z.string() }))
|
|
374
|
-
.
|
|
388
|
+
.dependsOn([database])
|
|
375
389
|
.handle(async ({ params, services }) => {
|
|
376
|
-
// services.database is
|
|
377
|
-
|
|
390
|
+
// services.database is a Kysely client typed by the construct's schema
|
|
391
|
+
return services.database
|
|
378
392
|
.selectFrom('users')
|
|
379
393
|
.where('id', '=', params.id)
|
|
380
394
|
.selectAll()
|
|
381
395
|
.executeTakeFirst();
|
|
382
|
-
|
|
383
|
-
return user;
|
|
384
396
|
});
|
|
385
397
|
```
|
|
386
398
|
|
|
399
|
+
A hand-written `Service` still works through `.services()` on every builder,
|
|
400
|
+
for code that has not moved to a construct yet. It records no edge, so a deploy
|
|
401
|
+
target cannot grant it anything.
|
|
402
|
+
|
|
387
403
|
### Event Publishing
|
|
388
404
|
|
|
389
|
-
|
|
405
|
+
An endpoint, function or cron publishes to a topic with `.event(topic, …)`,
|
|
406
|
+
once its handler has succeeded:
|
|
390
407
|
|
|
391
408
|
```typescript
|
|
392
|
-
import {
|
|
393
|
-
import type { Service } from '@geekmidas/services';
|
|
394
|
-
import type { EventPublisher, PublishableMessage } from '@geekmidas/events';
|
|
395
|
-
import { z } from 'zod';
|
|
396
|
-
|
|
397
|
-
// Define event types
|
|
398
|
-
type UserEvents =
|
|
399
|
-
| PublishableMessage<'user.created', { userId: string; email: string }>
|
|
400
|
-
| PublishableMessage<'user.updated', { userId: string }>;
|
|
401
|
-
|
|
402
|
-
// Create event publisher service
|
|
403
|
-
const userEventPublisher = {
|
|
404
|
-
serviceName: 'userEventPublisher' as const,
|
|
405
|
-
async register(envParser: EnvironmentParser<{}>) {
|
|
406
|
-
const config = envParser.create((get) => ({
|
|
407
|
-
publisherUrl: get('EVENT_PUBLISHER_URL').string()
|
|
408
|
-
})).parse();
|
|
409
|
+
import { users } from '../constructs/topics';
|
|
409
410
|
|
|
410
|
-
|
|
411
|
-
return Publisher.fromConnectionString<UserEvents>(config.publisherUrl);
|
|
412
|
-
}
|
|
413
|
-
} satisfies Service<'userEventPublisher', EventPublisher<UserEvents>>;
|
|
414
|
-
|
|
415
|
-
// Use in endpoint with event publishing
|
|
416
|
-
export const createUser = api
|
|
411
|
+
export const createUser = router
|
|
417
412
|
.post('/users')
|
|
418
|
-
.body(z.object({
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
}))
|
|
427
|
-
.handle(async ({ body, publish }) => {
|
|
428
|
-
const user = {
|
|
429
|
-
id: crypto.randomUUID(),
|
|
430
|
-
name: body.name,
|
|
431
|
-
email: body.email
|
|
432
|
-
};
|
|
433
|
-
|
|
434
|
-
// Events are automatically published after successful execution
|
|
435
|
-
return user;
|
|
436
|
-
});
|
|
413
|
+
.body(z.object({ name: z.string(), email: z.email() }))
|
|
414
|
+
.output(z.object({ id: z.string(), name: z.string(), email: z.email() }))
|
|
415
|
+
.event(users, {
|
|
416
|
+
type: 'user.created',
|
|
417
|
+
payload: (user) => ({ userId: user.id, email: user.email }),
|
|
418
|
+
when: (user) => !user.email.endsWith('@example.com'), // optional
|
|
419
|
+
})
|
|
420
|
+
.handle(async ({ body }) => ({ id: crypto.randomUUID(), ...body }));
|
|
437
421
|
```
|
|
438
422
|
|
|
423
|
+
- `type` is checked against the topic's events, and `payload` against that
|
|
424
|
+
event's schema.
|
|
425
|
+
- `.event()` is repeatable across topics. Two calls to two topics publish to
|
|
426
|
+
each, every event through its own topic's publisher.
|
|
427
|
+
- It also puts the topic's publisher in the handler as `services.users`, as
|
|
428
|
+
`.dependsOn([users])` would, for events the handler decides on:
|
|
429
|
+
`await services.users.publish([{ type: 'user.updated', payload }])`.
|
|
430
|
+
- A failed publish is logged, never thrown — the handler already succeeded.
|
|
431
|
+
|
|
432
|
+
The publisher reads `USERS_PUBLISHER_CONNECTION_STRING`, which only constructs
|
|
433
|
+
that publish to the topic are given, and picks its transport from the protocol:
|
|
434
|
+
`pgboss://` locally, `sns://` deployed.
|
|
435
|
+
|
|
439
436
|
### Database Context
|
|
440
437
|
|
|
441
438
|
Inject a database instance directly into the handler context using `.database()`:
|
|
@@ -632,10 +629,11 @@ For complex scenarios, use `ctx.auditor` to record audits manually within your h
|
|
|
632
629
|
|
|
633
630
|
```typescript
|
|
634
631
|
export const processOrder = api
|
|
635
|
-
.
|
|
636
|
-
.database(databaseService)
|
|
637
|
-
.services([paymentService])
|
|
632
|
+
.database(database)
|
|
638
633
|
.auditor(auditStorageService)
|
|
634
|
+
.post('/orders')
|
|
635
|
+
// `payments` is an ExternalApi whose client has `charge()`
|
|
636
|
+
.dependsOn([payments])
|
|
639
637
|
.actor(({ session }) => ({ id: session.userId, type: 'user' }))
|
|
640
638
|
.handle(async ({ body, db, services, auditor }) => {
|
|
641
639
|
// db is automatically the transaction when auditor uses KyselyAuditStorage
|
|
@@ -646,7 +644,7 @@ export const processOrder = api
|
|
|
646
644
|
.executeTakeFirstOrThrow();
|
|
647
645
|
|
|
648
646
|
// Manual audit for payment (external service call)
|
|
649
|
-
const payment = await services.
|
|
647
|
+
const payment = await services.payments.charge(order.total);
|
|
650
648
|
auditor.audit('payment.processed', {
|
|
651
649
|
orderId: order.id,
|
|
652
650
|
amount: order.total,
|
|
@@ -907,6 +905,69 @@ export const sendMessage = api
|
|
|
907
905
|
});
|
|
908
906
|
```
|
|
909
907
|
|
|
908
|
+
## Feature Tests
|
|
909
|
+
|
|
910
|
+
A feature test drives an app the way it runs deployed: a browser signs in and
|
|
911
|
+
calls the API, the API asks the auth server who is calling, each over its URL,
|
|
912
|
+
and every database is in its own transaction, rolled back after the test.
|
|
913
|
+
Nothing runs as a server — each URL an app's constructs answer on is served
|
|
914
|
+
in-process through MSW, from the real handler, for the test that made the
|
|
915
|
+
request. Postgres, Mailpit and MinIO are the real local ones `gkm test` starts.
|
|
916
|
+
|
|
917
|
+
Nothing is declared in test code. `gkm test` writes `.gkm/test/` into each app:
|
|
918
|
+
|
|
919
|
+
- `manifest.json` — which construct and endpoint is exported where, and the
|
|
920
|
+
test stage's environment, keyed as the constructs derive their keys;
|
|
921
|
+
- `clients/<surface>.ts` — each surface's typed client, from the same generator
|
|
922
|
+
`gkm build` uses;
|
|
923
|
+
- `index.ts` — a `Browser` with a client per surface (`Api` → `browser.api`)
|
|
924
|
+
and per auth server (`browser.auth`, its better-auth plugins paired with
|
|
925
|
+
their client plugins), and the configured `it`.
|
|
926
|
+
|
|
927
|
+
Map it once in the app's `package.json` — `"imports": { "#test":
|
|
928
|
+
"./.gkm/test/index.ts" }` — and a test is:
|
|
929
|
+
|
|
930
|
+
```typescript
|
|
931
|
+
import { it } from '#test';
|
|
932
|
+
|
|
933
|
+
it('shows the signed-in user their orders', async ({ browser, db, factories }) => {
|
|
934
|
+
const factory = await factories.get('database');
|
|
935
|
+
await factory.insert('orders', { email: 'ada@shop.test' });
|
|
936
|
+
|
|
937
|
+
await browser.signIn('ada@shop.test');
|
|
938
|
+
const { orders } = await browser.api.get('/orders');
|
|
939
|
+
expect(orders).toHaveLength(1);
|
|
940
|
+
|
|
941
|
+
const app = await db.get('database');
|
|
942
|
+
expect(await app.selectFrom('orders').selectAll().execute()).toHaveLength(1);
|
|
943
|
+
});
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
- **`browser`** — already the global `fetch`; its cookie jar follows the
|
|
947
|
+
browser's rules, so a session cookie set for the wrong domain never reaches
|
|
948
|
+
the API.
|
|
949
|
+
- **`browser.signIn(email)`** — when the auth server has the magic-link plugin
|
|
950
|
+
and the app sends mail: the link requested, read from the inbox and
|
|
951
|
+
followed, as a person signs in. Returns the session the auth server reports.
|
|
952
|
+
- **`db.get(name)`** — the app's own databases by service name
|
|
953
|
+
(`db.get('database')`), each this test's transaction, typed by its schema.
|
|
954
|
+
Opened on first use by whatever reaches it first — the test, a factory, an
|
|
955
|
+
endpoint — and the same one for all of them. A tenant an auth server owns is
|
|
956
|
+
not among them: it is reached through the auth server, as the app reaches
|
|
957
|
+
it. Nor is a reader.
|
|
958
|
+
- **`factories.get(name)`** — each of those databases' factory, from
|
|
959
|
+
`test/factories/<construct>.ts` at the project root, on the same
|
|
960
|
+
transaction: `(await factories.get('database')).insert('users', …)`.
|
|
961
|
+
- **`mailbox(address)`** — the mail actually sent, read from Mailpit.
|
|
962
|
+
- **`published(topic | queue)`** — what the test published or enqueued.
|
|
963
|
+
Nothing is delivered: a subscriber or a worker is run on its own with
|
|
964
|
+
**`subscriber(s).invoke({ events })`** / **`queue(q).invoke({ messages })`**,
|
|
965
|
+
on the test's services and transactions.
|
|
966
|
+
- Each database construct — the app's and each schema tenant — gets its own
|
|
967
|
+
transaction on its own connection, as deployed.
|
|
968
|
+
- A request that belongs to no running test is refused with
|
|
969
|
+
`UnknownTestContext` rather than reaching another test's data.
|
|
970
|
+
|
|
910
971
|
## AWS Lambda Adapters
|
|
911
972
|
|
|
912
973
|
Deploy your constructs to AWS Lambda:
|
|
@@ -1033,19 +1094,19 @@ Every construct has an async `getEnvironment()` method that returns the environm
|
|
|
1033
1094
|
|
|
1034
1095
|
```typescript
|
|
1035
1096
|
import { api } from '../constructs/api';
|
|
1036
|
-
import {
|
|
1037
|
-
import {
|
|
1097
|
+
import { cache } from '../constructs/cache';
|
|
1098
|
+
import { database } from '../constructs/database';
|
|
1038
1099
|
|
|
1039
1100
|
const endpoint = api
|
|
1040
1101
|
.get('/users')
|
|
1041
|
-
.
|
|
1102
|
+
.dependsOn([database, cache])
|
|
1042
1103
|
.handle(async ({ services }) => {
|
|
1043
1104
|
// Implementation
|
|
1044
1105
|
});
|
|
1045
1106
|
|
|
1046
1107
|
// Detect required environment variables
|
|
1047
1108
|
const envVars = await endpoint.getEnvironment();
|
|
1048
|
-
// Returns: ['
|
|
1109
|
+
// Returns: ['DATABASE_URL', 'SESSIONS_URL'] (sorted alphabetically)
|
|
1049
1110
|
```
|
|
1050
1111
|
|
|
1051
1112
|
### How It Works
|
|
@@ -1133,33 +1194,37 @@ This manifest can then be used by infrastructure-as-code tools (Terraform, CDK,
|
|
|
1133
1194
|
- **Deduplication**: Each variable listed once even if used by multiple services
|
|
1134
1195
|
- **Sorted Output**: Variables always returned in alphabetical order
|
|
1135
1196
|
- **Error Resilient**: Parse failures don't affect detection
|
|
1136
|
-
- **
|
|
1197
|
+
- **Topic Support**: A topic named by `.event(topic, …)` contributes its `<ID>_PUBLISHER_CONNECTION_STRING`
|
|
1137
1198
|
|
|
1138
1199
|
### Example with Multiple Services
|
|
1139
1200
|
|
|
1140
1201
|
```typescript
|
|
1141
1202
|
import { api } from '../constructs/api';
|
|
1142
|
-
import {
|
|
1143
|
-
import {
|
|
1144
|
-
import {
|
|
1203
|
+
import { cache } from '../constructs/cache';
|
|
1204
|
+
import { database } from '../constructs/database';
|
|
1205
|
+
import { mail } from '../constructs/email';
|
|
1206
|
+
import { users } from '../constructs/topics';
|
|
1145
1207
|
|
|
1146
1208
|
const endpoint = api
|
|
1147
1209
|
.post('/users')
|
|
1148
|
-
.
|
|
1210
|
+
.dependsOn([database, cache, mail])
|
|
1211
|
+
.output(userSchema)
|
|
1212
|
+
.event(users, {
|
|
1213
|
+
type: 'user.created',
|
|
1214
|
+
payload: (user) => ({ userId: user.id, email: user.email }),
|
|
1215
|
+
})
|
|
1149
1216
|
.handle(async ({ services }) => {
|
|
1150
1217
|
// Create user
|
|
1151
1218
|
});
|
|
1152
1219
|
|
|
1153
|
-
//
|
|
1220
|
+
// Detects the keys of every construct it names
|
|
1154
1221
|
const envVars = await endpoint.getEnvironment();
|
|
1155
1222
|
// Returns: [
|
|
1156
|
-
// 'CACHE_URL',
|
|
1157
1223
|
// 'DATABASE_URL',
|
|
1158
|
-
// '
|
|
1159
|
-
// '
|
|
1160
|
-
// '
|
|
1161
|
-
// '
|
|
1162
|
-
// 'SMTP_PASS'
|
|
1224
|
+
// 'MAIL_FROM',
|
|
1225
|
+
// 'MAIL_URL',
|
|
1226
|
+
// 'SESSIONS_URL',
|
|
1227
|
+
// 'USERS_PUBLISHER_CONNECTION_STRING'
|
|
1163
1228
|
// ]
|
|
1164
1229
|
```
|
|
1165
1230
|
|