@geekmidas/constructs 10.0.0-alpha.12 → 10.0.0-alpha.121
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 +222 -158
- package/dist/Construct-B7AXn0xH.d.mts +230 -0
- package/dist/Construct-B7AXn0xH.d.mts.map +1 -0
- package/dist/Construct-CFW0zjdD.d.cts +230 -0
- package/dist/Construct-CFW0zjdD.d.cts.map +1 -0
- package/dist/{Construct-BD5neF7w.mjs → Construct-CKUA-CTA.mjs} +35 -35
- package/dist/Construct-CKUA-CTA.mjs.map +1 -0
- package/dist/{Construct-CyS3RpTL.cjs → Construct-DEOVauyA.cjs} +55 -49
- package/dist/Construct-DEOVauyA.cjs.map +1 -0
- package/dist/{Cron-DmQ8vQ4b.d.cts → Cron-bdGRSROY.d.cts} +15 -9
- package/dist/Cron-bdGRSROY.d.cts.map +1 -0
- package/dist/{Cron-dh4rUgib.d.mts → Cron-i4Uetezh.d.mts} +15 -9
- package/dist/Cron-i4Uetezh.d.mts.map +1 -0
- package/dist/CronBuilder-9H4BnD6W.d.mts +47 -0
- package/dist/CronBuilder-9H4BnD6W.d.mts.map +1 -0
- package/dist/{CronBuilder-Da0C0edR.cjs → CronBuilder-BXTG10u5.cjs} +47 -31
- package/dist/CronBuilder-BXTG10u5.cjs.map +1 -0
- package/dist/CronBuilder-B_24LZpn.d.cts +47 -0
- package/dist/CronBuilder-B_24LZpn.d.cts.map +1 -0
- package/dist/{CronBuilder-CYKT5RiO.mjs → CronBuilder-D3sF9tS_.mjs} +37 -21
- package/dist/CronBuilder-D3sF9tS_.mjs.map +1 -0
- package/dist/{Endpoint-BjWKinFA.d.mts → Endpoint-CeFkSwh5.d.mts} +129 -99
- package/dist/Endpoint-CeFkSwh5.d.mts.map +1 -0
- package/dist/{Endpoint-dav89W6-.d.cts → Endpoint-DgrpG15P.d.cts} +127 -97
- package/dist/Endpoint-DgrpG15P.d.cts.map +1 -0
- package/dist/{Endpoint-DCQPMdUX.mjs → Endpoint-gPXhHKhJ.mjs} +208 -100
- package/dist/Endpoint-gPXhHKhJ.mjs.map +1 -0
- package/dist/{Endpoint-DcWXE-SZ.cjs → Endpoint-yhR2T2Wt.cjs} +248 -122
- package/dist/Endpoint-yhR2T2Wt.cjs.map +1 -0
- package/dist/{EndpointFactory-CdWB7wPw.cjs → EndpointFactory-CSrAyazj.cjs} +361 -112
- package/dist/EndpointFactory-CSrAyazj.cjs.map +1 -0
- package/dist/{index-lAO9egTb.d.mts → EndpointFactory-Cc863XJS.d.cts} +190 -136
- package/dist/EndpointFactory-Cc863XJS.d.cts.map +1 -0
- package/dist/{index-Bd5FqCoe.d.cts → EndpointFactory-Dbw9ugkX.d.mts} +189 -135
- package/dist/EndpointFactory-Dbw9ugkX.d.mts.map +1 -0
- package/dist/{EndpointFactory-CjV8F_lJ.mjs → EndpointFactory-c2ZugGig.mjs} +283 -76
- package/dist/EndpointFactory-c2ZugGig.mjs.map +1 -0
- package/dist/Function-B0uZhZnU.d.cts +76 -0
- package/dist/Function-B0uZhZnU.d.cts.map +1 -0
- package/dist/Function-B_9R7Zy1.d.mts +76 -0
- package/dist/Function-B_9R7Zy1.d.mts.map +1 -0
- package/dist/{HonoEndpointAdaptor-DtL9j19R.cjs → HonoEndpointAdaptor-CN1jjUBJ.cjs} +115 -71
- package/dist/HonoEndpointAdaptor-CN1jjUBJ.cjs.map +1 -0
- package/dist/{HonoEndpointAdaptor-P1ahhZHu.mjs → HonoEndpointAdaptor-euH1oSdY.mjs} +105 -64
- package/dist/HonoEndpointAdaptor-euH1oSdY.mjs.map +1 -0
- package/dist/{Queue-D7ZuwBhy.d.cts → Queue-B-dDXyjF.d.mts} +48 -48
- package/dist/Queue-B-dDXyjF.d.mts.map +1 -0
- package/dist/Queue-BhuofM3k.cjs +109 -0
- package/dist/Queue-BhuofM3k.cjs.map +1 -0
- package/dist/{Queue-gVGfi2Yk.d.mts → Queue-JbIjVtTf.d.cts} +49 -49
- package/dist/Queue-JbIjVtTf.d.cts.map +1 -0
- package/dist/Queue-cmhqEBOB.mjs +104 -0
- package/dist/Queue-cmhqEBOB.mjs.map +1 -0
- package/dist/QueueBuilder-48W-q8In.d.mts +75 -0
- package/dist/QueueBuilder-48W-q8In.d.mts.map +1 -0
- package/dist/QueueBuilder-DXu1qxT1.mjs +118 -0
- package/dist/QueueBuilder-DXu1qxT1.mjs.map +1 -0
- package/dist/QueueBuilder-DdU2klPu.cjs +135 -0
- package/dist/QueueBuilder-DdU2klPu.cjs.map +1 -0
- package/dist/QueueBuilder-MfpkJ3R9.d.cts +75 -0
- package/dist/QueueBuilder-MfpkJ3R9.d.cts.map +1 -0
- package/dist/{Subscriber-2mEi0ImH.d.cts → Subscriber-CEt7kHkc.d.cts} +29 -20
- package/dist/Subscriber-CEt7kHkc.d.cts.map +1 -0
- package/dist/{Subscriber-xTJ9ayPN.d.mts → Subscriber-dsxc5LKa.d.mts} +29 -20
- package/dist/Subscriber-dsxc5LKa.d.mts.map +1 -0
- package/dist/SubscriberBuilder-BRa_nrmH.d.cts +67 -0
- package/dist/SubscriberBuilder-BRa_nrmH.d.cts.map +1 -0
- package/dist/SubscriberBuilder-D9bkrYzH.d.mts +67 -0
- package/dist/SubscriberBuilder-D9bkrYzH.d.mts.map +1 -0
- package/dist/{SubscriberBuilder-BjkbeT9l.mjs → SubscriberBuilder-DRXfxWn_.mjs} +50 -24
- package/dist/SubscriberBuilder-DRXfxWn_.mjs.map +1 -0
- package/dist/{SubscriberBuilder-4OPehf2R.cjs → SubscriberBuilder-M2EAgQhC.cjs} +61 -36
- package/dist/SubscriberBuilder-M2EAgQhC.cjs.map +1 -0
- package/dist/Topic-D2R0r24C.mjs +99 -0
- package/dist/Topic-D2R0r24C.mjs.map +1 -0
- package/dist/Topic-DwS3QO0m.cjs +104 -0
- package/dist/Topic-DwS3QO0m.cjs.map +1 -0
- package/dist/adaptors/aws.cjs +200 -246
- package/dist/adaptors/aws.cjs.map +1 -1
- package/dist/adaptors/aws.d.cts +58 -62
- package/dist/adaptors/aws.d.cts.map +1 -1
- package/dist/adaptors/aws.d.mts +59 -63
- package/dist/adaptors/aws.d.mts.map +1 -1
- package/dist/adaptors/aws.mjs +171 -219
- 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 +9 -18
- package/dist/adaptors/hono.d.mts.map +1 -1
- package/dist/adaptors/hono.mjs +2 -10
- package/dist/adaptors/testing.cjs +765 -99
- package/dist/adaptors/testing.cjs.map +1 -1
- package/dist/adaptors/testing.d.cts +351 -54
- package/dist/adaptors/testing.d.cts.map +1 -1
- package/dist/adaptors/testing.d.mts +351 -54
- package/dist/adaptors/testing.d.mts.map +1 -1
- package/dist/adaptors/testing.mjs +726 -78
- package/dist/adaptors/testing.mjs.map +1 -1
- package/dist/auth-BMCJN30Y.d.cts +268 -0
- package/dist/auth-BMCJN30Y.d.cts.map +1 -0
- package/dist/auth-D_0788uW.d.mts +268 -0
- package/dist/auth-D_0788uW.d.mts.map +1 -0
- package/dist/auth.cjs +301 -33
- 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 +289 -27
- package/dist/auth.mjs.map +1 -1
- package/dist/{cache-BrRz3fEB.d.mts → cache-5QZzh5Er.d.mts} +3 -6
- package/dist/cache-5QZzh5Er.d.mts.map +1 -0
- package/dist/{cache-BtZY_tHe.d.cts → cache-DnGDGly3.d.cts} +3 -6
- package/dist/cache-DnGDGly3.d.cts.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/cipher-C1lEFX63.mjs +225 -0
- package/dist/cipher-C1lEFX63.mjs.map +1 -0
- package/dist/cipher-CVxfwLjk.cjs +332 -0
- package/dist/cipher-CVxfwLjk.cjs.map +1 -0
- package/dist/cipher-DSDxPRIp.d.cts +106 -0
- package/dist/cipher-DSDxPRIp.d.cts.map +1 -0
- package/dist/cipher-DSDxPRIp.d.mts +106 -0
- package/dist/cipher-DSDxPRIp.d.mts.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-D9d4Gm4p.d.cts → construct-interface-B1AfqRl4.d.cts} +42 -11
- package/dist/construct-interface-B1AfqRl4.d.cts.map +1 -0
- package/dist/{construct-interface-g5GM5pwR.d.mts → construct-interface-ZvYQbivO.d.mts} +42 -11
- package/dist/construct-interface-ZvYQbivO.d.mts.map +1 -0
- package/dist/construct-interface.cjs +113 -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 +106 -2
- package/dist/construct-interface.mjs.map +1 -0
- package/dist/credential-DYymbp_7.cjs +257 -0
- package/dist/credential-DYymbp_7.cjs.map +1 -0
- package/dist/credential-DfSAOOdY.mjs +222 -0
- package/dist/credential-DfSAOOdY.mjs.map +1 -0
- package/dist/credential.cjs +8 -96
- package/dist/credential.d.cts +2 -61
- package/dist/credential.d.mts +2 -61
- package/dist/credential.mjs +2 -94
- package/dist/crons/index.cjs +129 -7
- package/dist/crons/index.cjs.map +1 -1
- package/dist/crons/index.d.cts +45 -11
- package/dist/crons/index.d.cts.map +1 -1
- package/dist/crons/index.d.mts +45 -11
- package/dist/crons/index.d.mts.map +1 -1
- package/dist/crons/index.mjs +127 -8
- package/dist/crons/index.mjs.map +1 -1
- package/dist/database/kysely.cjs +4 -181
- package/dist/database/kysely.d.cts +2 -152
- package/dist/database/kysely.d.mts +2 -152
- package/dist/database/kysely.mjs +2 -180
- 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 +4 -8
- package/dist/email.d.mts.map +1 -1
- package/dist/email.mjs +16 -3
- package/dist/email.mjs.map +1 -1
- package/dist/encryption/kms.cjs +95 -0
- package/dist/encryption/kms.cjs.map +1 -0
- package/dist/encryption/kms.d.cts +7 -0
- package/dist/encryption/kms.d.cts.map +1 -0
- package/dist/encryption/kms.d.mts +7 -0
- package/dist/encryption/kms.d.mts.map +1 -0
- package/dist/encryption/kms.mjs +93 -0
- package/dist/encryption/kms.mjs.map +1 -0
- package/dist/encryption.cjs +116 -0
- package/dist/encryption.cjs.map +1 -0
- package/dist/encryption.d.cts +37 -0
- package/dist/encryption.d.cts.map +1 -0
- package/dist/encryption.d.mts +37 -0
- package/dist/encryption.d.mts.map +1 -0
- package/dist/encryption.mjs +101 -0
- package/dist/encryption.mjs.map +1 -0
- package/dist/endpoints/index.cjs +27 -16
- package/dist/endpoints/index.d.cts +105 -7
- package/dist/endpoints/index.d.cts.map +1 -0
- package/dist/endpoints/index.d.mts +105 -7
- package/dist/endpoints/index.d.mts.map +1 -0
- package/dist/endpoints/index.mjs +6 -10
- package/dist/expire-C586zznE.d.cts +38 -0
- package/dist/expire-C586zznE.d.cts.map +1 -0
- package/dist/expire-C586zznE.d.mts +38 -0
- package/dist/expire-C586zznE.d.mts.map +1 -0
- package/dist/expire-CieftS01.cjs +67 -0
- package/dist/expire-CieftS01.cjs.map +1 -0
- package/dist/expire-CpzUAfr9.mjs +50 -0
- package/dist/expire-CpzUAfr9.mjs.map +1 -0
- package/dist/external-api.cjs +106 -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 +103 -0
- package/dist/external-api.mjs.map +1 -0
- package/dist/fake-B7rvi4Qp.d.cts +201 -0
- package/dist/fake-B7rvi4Qp.d.cts.map +1 -0
- package/dist/fake-JXbkdao8.d.mts +201 -0
- package/dist/fake-JXbkdao8.d.mts.map +1 -0
- package/dist/file-server.cjs +66 -11
- package/dist/file-server.cjs.map +1 -1
- package/dist/file-server.d.cts +25 -10
- package/dist/file-server.d.cts.map +1 -1
- package/dist/file-server.d.mts +25 -10
- package/dist/file-server.d.mts.map +1 -1
- package/dist/file-server.mjs +58 -4
- package/dist/file-server.mjs.map +1 -1
- package/dist/functions/index.cjs +3 -6
- package/dist/functions/index.d.cts +3 -4
- package/dist/functions/index.d.mts +3 -4
- package/dist/functions/index.mjs +2 -6
- package/dist/{functions-14lpEFL4.mjs → functions-CTI-hDem.mjs} +376 -238
- package/dist/{functions-14lpEFL4.mjs.map → functions-CTI-hDem.mjs.map} +1 -1
- package/dist/{functions-i_la-N5r.cjs → functions-D7O5SGVc.cjs} +414 -264
- package/dist/functions-D7O5SGVc.cjs.map +1 -0
- package/dist/index-Cd2w-DRL.d.cts +115 -0
- package/dist/index-Cd2w-DRL.d.cts.map +1 -0
- package/dist/index-DdRjYhiJ.d.cts +119 -0
- package/dist/index-DdRjYhiJ.d.cts.map +1 -0
- package/dist/index-DdRjYhiJ.d.mts +119 -0
- package/dist/index-DdRjYhiJ.d.mts.map +1 -0
- package/dist/index-aBIcAiH5.d.mts +115 -0
- package/dist/index-aBIcAiH5.d.mts.map +1 -0
- package/dist/index.cjs +6 -3
- package/dist/index.d.cts +27 -4
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +27 -4
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +3 -3
- package/dist/kysely-BHYV-WC-.d.cts +216 -0
- package/dist/kysely-BHYV-WC-.d.cts.map +1 -0
- package/dist/kysely-CAW3QSqp.d.mts +216 -0
- package/dist/kysely-CAW3QSqp.d.mts.map +1 -0
- package/dist/kysely-Cs4uPO8k.mjs +371 -0
- package/dist/kysely-Cs4uPO8k.mjs.map +1 -0
- package/dist/kysely-CukArHJ0.cjs +384 -0
- package/dist/kysely-CukArHJ0.cjs.map +1 -0
- package/dist/{telemetry-C3L0iS28.d.cts → lambdaTelemetry-8vnmVZQ5.d.cts} +3 -6
- package/dist/lambdaTelemetry-8vnmVZQ5.d.cts.map +1 -0
- package/dist/{telemetry-D7bVFLGi.d.mts → lambdaTelemetry-8vnmVZQ5.d.mts} +3 -6
- package/dist/lambdaTelemetry-8vnmVZQ5.d.mts.map +1 -0
- package/dist/mobile-app.cjs +74 -0
- package/dist/mobile-app.cjs.map +1 -0
- package/dist/mobile-app.d.cts +73 -0
- package/dist/mobile-app.d.cts.map +1 -0
- package/dist/mobile-app.d.mts +73 -0
- package/dist/mobile-app.d.mts.map +1 -0
- package/dist/mobile-app.mjs +73 -0
- package/dist/mobile-app.mjs.map +1 -0
- package/dist/object-storage.cjs +26 -11
- package/dist/object-storage.cjs.map +1 -1
- package/dist/object-storage.d.cts +12 -7
- package/dist/object-storage.d.cts.map +1 -1
- package/dist/object-storage.d.mts +12 -7
- package/dist/object-storage.d.mts.map +1 -1
- package/dist/object-storage.mjs +18 -5
- package/dist/object-storage.mjs.map +1 -1
- 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/random-password.cjs +86 -0
- package/dist/random-password.cjs.map +1 -0
- package/dist/random-password.d.cts +30 -0
- package/dist/random-password.d.cts.map +1 -0
- package/dist/random-password.d.mts +30 -0
- package/dist/random-password.d.mts.map +1 -0
- package/dist/random-password.mjs +84 -0
- package/dist/random-password.mjs.map +1 -0
- package/dist/{lazyAccessors-BoJN3SMd.mjs → readRequestBody-BTIl3k-D.mjs} +117 -60
- package/dist/readRequestBody-BTIl3k-D.mjs.map +1 -0
- package/dist/readRequestBody-Dej_L3c4.cjs +342 -0
- package/dist/readRequestBody-Dej_L3c4.cjs.map +1 -0
- package/dist/requestLogger-BZcXwdBs.cjs +207 -0
- package/dist/requestLogger-BZcXwdBs.cjs.map +1 -0
- package/dist/requestLogger-pA4gi3E1.mjs +178 -0
- package/dist/requestLogger-pA4gi3E1.mjs.map +1 -0
- package/dist/rest-api.cjs +90 -59
- package/dist/rest-api.cjs.map +1 -1
- package/dist/rest-api.d.cts +70 -42
- package/dist/rest-api.d.cts.map +1 -1
- package/dist/rest-api.d.mts +70 -42
- package/dist/rest-api.d.mts.map +1 -1
- package/dist/rest-api.mjs +82 -51
- package/dist/rest-api.mjs.map +1 -1
- package/dist/{chunk-DWy1uDak.cjs → rolldown-runtime-y5ARvAq5.cjs} +14 -18
- package/dist/runSubscriber-BHvJ8POr.mjs +109 -0
- package/dist/runSubscriber-BHvJ8POr.mjs.map +1 -0
- package/dist/runSubscriber-D8nLXIv1.cjs +126 -0
- package/dist/runSubscriber-D8nLXIv1.cjs.map +1 -0
- package/dist/shutdown-BMJmAhSt.cjs +38 -0
- package/dist/shutdown-BMJmAhSt.cjs.map +1 -0
- package/dist/shutdown-C7xa8x9S.mjs +27 -0
- package/dist/shutdown-C7xa8x9S.mjs.map +1 -0
- package/dist/site.cjs +33 -7
- package/dist/site.cjs.map +1 -1
- package/dist/site.d.cts +17 -7
- package/dist/site.d.cts.map +1 -1
- package/dist/site.d.mts +17 -7
- package/dist/site.d.mts.map +1 -1
- package/dist/site.mjs +29 -3
- package/dist/site.mjs.map +1 -1
- package/dist/stages-BSCF19o2.d.cts +56 -0
- package/dist/stages-BSCF19o2.d.cts.map +1 -0
- package/dist/stages-BSCF19o2.d.mts +56 -0
- package/dist/stages-BSCF19o2.d.mts.map +1 -0
- package/dist/subscribers/index.cjs +118 -6
- package/dist/subscribers/index.cjs.map +1 -0
- package/dist/subscribers/index.d.cts +51 -6
- package/dist/subscribers/index.d.cts.map +1 -0
- package/dist/subscribers/index.d.mts +51 -6
- package/dist/subscribers/index.d.mts.map +1 -0
- package/dist/subscribers/index.mjs +115 -5
- package/dist/subscribers/index.mjs.map +1 -0
- 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-B48RLjyk.d.cts +40 -0
- package/dist/telemetry-B48RLjyk.d.cts.map +1 -0
- package/dist/telemetry-BEoBn6GO.d.mts +40 -0
- package/dist/telemetry-BEoBn6GO.d.mts.map +1 -0
- package/dist/telemetry.cjs +56 -0
- package/dist/telemetry.cjs.map +1 -0
- package/dist/telemetry.d.cts +2 -0
- package/dist/telemetry.d.mts +2 -0
- package/dist/telemetry.mjs +55 -0
- package/dist/telemetry.mjs.map +1 -0
- package/dist/topic/index.cjs +3 -137
- package/dist/topic/index.d.cts +2 -4
- package/dist/topic/index.d.mts +2 -4
- package/dist/topic/index.mjs +2 -134
- package/dist/tracing-Bbj1uoFJ.mjs +127 -0
- package/dist/tracing-Bbj1uoFJ.mjs.map +1 -0
- package/dist/tracing-C0ooU0V2.cjs +150 -0
- package/dist/tracing-C0ooU0V2.cjs.map +1 -0
- 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 +59 -34
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.d.cts +70 -45
- package/dist/worker.d.cts.map +1 -1
- package/dist/worker.d.mts +71 -45
- package/dist/worker.d.mts.map +1 -1
- package/dist/worker.mjs +54 -29
- package/dist/worker.mjs.map +1 -1
- package/package.json +115 -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-DmQ8vQ4b.d.cts.map +0 -1
- package/dist/Cron-dh4rUgib.d.mts.map +0 -1
- package/dist/CronBuilder-BquXDbDI.d.cts +0 -44
- package/dist/CronBuilder-BquXDbDI.d.cts.map +0 -1
- package/dist/CronBuilder-CGgY2080.d.mts +0 -44
- package/dist/CronBuilder-CGgY2080.d.mts.map +0 -1
- package/dist/CronBuilder-CYKT5RiO.mjs.map +0 -1
- package/dist/CronBuilder-Da0C0edR.cjs.map +0 -1
- package/dist/Endpoint-BjWKinFA.d.mts.map +0 -1
- package/dist/Endpoint-DCQPMdUX.mjs.map +0 -1
- package/dist/Endpoint-DcWXE-SZ.cjs.map +0 -1
- package/dist/Endpoint-dav89W6-.d.cts.map +0 -1
- package/dist/EndpointFactory-CdWB7wPw.cjs.map +0 -1
- package/dist/EndpointFactory-CjV8F_lJ.mjs.map +0 -1
- package/dist/HonoEndpointAdaptor-DtL9j19R.cjs.map +0 -1
- package/dist/HonoEndpointAdaptor-P1ahhZHu.mjs.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/credential.cjs.map +0 -1
- package/dist/credential.d.cts.map +0 -1
- package/dist/credential.d.mts.map +0 -1
- package/dist/credential.mjs.map +0 -1
- package/dist/database/kysely.cjs.map +0 -1
- package/dist/database/kysely.d.cts.map +0 -1
- package/dist/database/kysely.d.mts.map +0 -1
- package/dist/database/kysely.mjs.map +0 -1
- package/dist/functions-i_la-N5r.cjs.map +0 -1
- package/dist/index-Bd5FqCoe.d.cts.map +0 -1
- package/dist/index-BknhM6Ux.d.mts +0 -31
- package/dist/index-BknhM6Ux.d.mts.map +0 -1
- package/dist/index-CBSfrowv.d.cts +0 -31
- package/dist/index-CBSfrowv.d.cts.map +0 -1
- package/dist/index-D24Xm79M.d.cts +0 -171
- package/dist/index-D24Xm79M.d.cts.map +0 -1
- package/dist/index-_ajboYxK.d.mts +0 -171
- package/dist/index-_ajboYxK.d.mts.map +0 -1
- package/dist/index-lAO9egTb.d.mts.map +0 -1
- package/dist/lazyAccessors-BoJN3SMd.mjs.map +0 -1
- package/dist/lazyAccessors-e3j2oE2m.cjs +0 -274
- package/dist/lazyAccessors-e3j2oE2m.cjs.map +0 -1
- package/dist/processAudits-CGqttuXm.cjs +0 -92
- package/dist/processAudits-CGqttuXm.cjs.map +0 -1
- package/dist/processAudits-Dmts9H_b.mjs +0 -80
- 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/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.mjs.map +0 -1
- package/dist/types-BlAkSuGI.d.cts.map +0 -1
- package/dist/types-aKksMEWL.d.mts.map +0 -1
|
@@ -1,26 +1,66 @@
|
|
|
1
|
-
|
|
2
|
-
require(
|
|
3
|
-
require(
|
|
4
|
-
const
|
|
5
|
-
require(
|
|
6
|
-
const require_Endpoint = require(
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_rolldown_runtime = require("../rolldown-runtime-y5ARvAq5.cjs");
|
|
3
|
+
const require_construct_interface = require("../construct-interface.cjs");
|
|
4
|
+
const require_auth = require("../auth.cjs");
|
|
5
|
+
const require_functions = require("../functions-D7O5SGVc.cjs");
|
|
6
|
+
const require_Endpoint = require("../Endpoint-yhR2T2Wt.cjs");
|
|
7
|
+
const require_Queue = require("../Queue-BhuofM3k.cjs");
|
|
8
|
+
const require_email = require("../email.cjs");
|
|
9
|
+
const require_kysely = require("../kysely-CukArHJ0.cjs");
|
|
10
|
+
const require_readRequestBody = require("../readRequestBody-Dej_L3c4.cjs");
|
|
11
|
+
const require_Topic = require("../Topic-DwS3QO0m.cjs");
|
|
12
|
+
const require_HonoEndpointAdaptor = require("../HonoEndpointAdaptor-CN1jjUBJ.cjs");
|
|
13
|
+
const require_requestLogger = require("../requestLogger-BZcXwdBs.cjs");
|
|
14
|
+
let _geekmidas_manifest = require("@geekmidas/manifest");
|
|
15
|
+
let node_crypto = require("node:crypto");
|
|
16
|
+
let _geekmidas_services = require("@geekmidas/services");
|
|
17
|
+
let _geekmidas_errors = require("@geekmidas/errors");
|
|
18
|
+
let _geekmidas_envkit = require("@geekmidas/envkit");
|
|
19
|
+
let hono = require("hono");
|
|
20
|
+
let _geekmidas_audit = require("@geekmidas/audit");
|
|
21
|
+
let _geekmidas_testkit_context = require("@geekmidas/testkit/context");
|
|
22
|
+
let msw = require("msw");
|
|
23
|
+
let _geekmidas_testkit_browser = require("@geekmidas/testkit/browser");
|
|
24
|
+
let _geekmidas_testkit_faker = require("@geekmidas/testkit/faker");
|
|
25
|
+
let _geekmidas_testkit_mailbox = require("@geekmidas/testkit/mailbox");
|
|
26
|
+
let _geekmidas_testkit_transactions = require("@geekmidas/testkit/transactions");
|
|
27
|
+
let msw_node = require("msw/node");
|
|
28
|
+
let vitest = require("vitest");
|
|
29
|
+
let node_fs = require("node:fs");
|
|
30
|
+
let node_tls = require("node:tls");
|
|
31
|
+
node_tls = require_rolldown_runtime.__toESM(node_tls, 1);
|
|
32
|
+
let node_url = require("node:url");
|
|
33
|
+
//#region src/testing/peer.ts
|
|
18
34
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
35
|
+
* The TCP peer a test's in-process requests arrive from.
|
|
36
|
+
*
|
|
37
|
+
* A feature test hands a request straight to a surface's Hono app — no
|
|
38
|
+
* socket, no server — so the app's bindings have no peer to read. Deployed,
|
|
39
|
+
* every request has one, and what a surface trusts follows from it: the auth
|
|
40
|
+
* server honours `x-gkm-client-ip` only from an internal caller, a loopback
|
|
41
|
+
* or private peer with no `Origin` and no forwarding header. With no peer at
|
|
42
|
+
* all the header was dropped, and every test's session check became the same
|
|
43
|
+
* client to Better Auth's rate limiter.
|
|
44
|
+
*
|
|
45
|
+
* So an in-process dispatch says where it came from, the way
|
|
46
|
+
* `@hono/node-server` does: its bindings carry `incoming.socket.remoteAddress`.
|
|
47
|
+
* The address is loopback because that is the truth — the request came from
|
|
48
|
+
* this process. A browser's own address still travels in its
|
|
49
|
+
* `x-forwarded-for`, which makes it outside traffic exactly as it would be
|
|
50
|
+
* behind the stack's proxy.
|
|
51
|
+
*
|
|
52
|
+
* Only a test's dispatch passes these. A runtime with no socket of its own
|
|
53
|
+
* (Lambda, Bun) still has no peer, and a request with no peer is still not an
|
|
54
|
+
* internal caller's.
|
|
22
55
|
*/
|
|
23
|
-
|
|
56
|
+
/** The address an in-process request presents as its peer. */
|
|
57
|
+
const IN_PROCESS_PEER_ADDRESS = "127.0.0.1";
|
|
58
|
+
/** Bindings for `app.fetch(request, bindings)` from inside this process. */
|
|
59
|
+
function inProcessBindings() {
|
|
60
|
+
return { incoming: { socket: { remoteAddress: IN_PROCESS_PEER_ADDRESS } } };
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/endpoints/MswEndpointAdaptor.ts
|
|
24
64
|
const MSW_HTTP_METHODS = [
|
|
25
65
|
"get",
|
|
26
66
|
"post",
|
|
@@ -52,8 +92,8 @@ function createMswHandlers(endpoints, options) {
|
|
|
52
92
|
* Creates a fresh ServiceDiscovery and pre-registers all provided services.
|
|
53
93
|
*/
|
|
54
94
|
function registerContext(id, ctxOptions) {
|
|
55
|
-
const envParser = new
|
|
56
|
-
const serviceDiscovery = new
|
|
95
|
+
const envParser = new _geekmidas_envkit.EnvironmentParser({});
|
|
96
|
+
const serviceDiscovery = new _geekmidas_services.ServiceDiscovery(envParser);
|
|
57
97
|
const serviceDefs = [];
|
|
58
98
|
if (ctxOptions.services) for (const [name, instance] of Object.entries(ctxOptions.services)) serviceDefs.push({
|
|
59
99
|
serviceName: name,
|
|
@@ -68,7 +108,6 @@ function createMswHandlers(endpoints, options) {
|
|
|
68
108
|
break;
|
|
69
109
|
}
|
|
70
110
|
}
|
|
71
|
-
if (ctxOptions.publisher) serviceDefs.push(ctxOptions.publisher);
|
|
72
111
|
if (ctxOptions.auditorStorage !== void 0) {
|
|
73
112
|
for (const endpoint of endpoints) if (endpoint.auditorStorageService) {
|
|
74
113
|
serviceDefs.push({
|
|
@@ -88,18 +127,18 @@ function createMswHandlers(endpoints, options) {
|
|
|
88
127
|
const mswUrl = `${baseURL}${endpoint.route}`;
|
|
89
128
|
const mswMethod = msw.http[method];
|
|
90
129
|
handlers.push(mswMethod(mswUrl, async ({ request }) => {
|
|
91
|
-
const contextId = request.headers.get(TEST_CONTEXT_HEADER);
|
|
130
|
+
const contextId = request.headers.get(_geekmidas_testkit_context.TEST_CONTEXT_HEADER);
|
|
92
131
|
const ctx = contextId ? contexts.get(contextId) : void 0;
|
|
93
132
|
if (!ctx) return new Response(JSON.stringify({
|
|
94
133
|
error: "Missing or unknown test context ID",
|
|
95
|
-
hint: `Set the '${TEST_CONTEXT_HEADER}' header to a registered context ID`
|
|
134
|
+
hint: `Set the '${_geekmidas_testkit_context.TEST_CONTEXT_HEADER}' header to a registered context ID`
|
|
96
135
|
}), {
|
|
97
136
|
status: 500,
|
|
98
137
|
headers: { "content-type": "application/json" }
|
|
99
138
|
});
|
|
100
139
|
const app = new hono.Hono();
|
|
101
140
|
require_HonoEndpointAdaptor.HonoEndpoint.addRoute(endpoint, ctx.serviceDiscovery, app);
|
|
102
|
-
const response = await app.request(request);
|
|
141
|
+
const response = await app.request(request, void 0, inProcessBindings());
|
|
103
142
|
return new Response(response.body, {
|
|
104
143
|
status: response.status,
|
|
105
144
|
statusText: response.statusText,
|
|
@@ -112,7 +151,6 @@ function createMswHandlers(endpoints, options) {
|
|
|
112
151
|
registerContext
|
|
113
152
|
};
|
|
114
153
|
}
|
|
115
|
-
|
|
116
154
|
//#endregion
|
|
117
155
|
//#region src/endpoints/TestEndpointAdaptor.ts
|
|
118
156
|
/**
|
|
@@ -132,8 +170,10 @@ function serializeCookie(name, value, options) {
|
|
|
132
170
|
return cookieString;
|
|
133
171
|
}
|
|
134
172
|
var TestEndpointAdaptor = class TestEndpointAdaptor {
|
|
173
|
+
endpoint;
|
|
174
|
+
serviceDiscovery;
|
|
135
175
|
static getDefaultServiceDiscover(_endpoint) {
|
|
136
|
-
return
|
|
176
|
+
return _geekmidas_services.ServiceDiscovery.getInstance(new _geekmidas_envkit.EnvironmentParser({}));
|
|
137
177
|
}
|
|
138
178
|
constructor(endpoint, serviceDiscovery = TestEndpointAdaptor.getDefaultServiceDiscover(endpoint)) {
|
|
139
179
|
this.endpoint = endpoint;
|
|
@@ -143,21 +183,21 @@ var TestEndpointAdaptor = class TestEndpointAdaptor {
|
|
|
143
183
|
const body = await this.endpoint.parseInput(ctx.body, "body");
|
|
144
184
|
const query = await this.endpoint.parseInput(ctx.query, "query");
|
|
145
185
|
const params = await this.endpoint.parseInput(ctx.params, "params");
|
|
146
|
-
const header =
|
|
147
|
-
const cookie =
|
|
186
|
+
const header = require_readRequestBody.createObjectHeaders(ctx.headers);
|
|
187
|
+
const cookie = require_readRequestBody.createCookieHeaderAccessor(ctx.headers.cookie);
|
|
148
188
|
const startTime = Date.now();
|
|
149
189
|
const requestId = ctx.requestId ?? ctx.headers["x-request-id"] ?? crypto.randomUUID();
|
|
150
|
-
|
|
190
|
+
let logger = require_requestLogger.requestLogger(this.endpoint, {
|
|
151
191
|
requestId,
|
|
152
|
-
|
|
153
|
-
host: ctx.headers.host,
|
|
154
|
-
method: this.endpoint.method
|
|
192
|
+
host: ctx.headers.host
|
|
155
193
|
});
|
|
156
194
|
const rawDb = ctx.database;
|
|
157
|
-
|
|
195
|
+
const operation = `${this.endpoint.method} ${this.endpoint.route}`;
|
|
196
|
+
return (0, _geekmidas_services.runWithRequestContext)({
|
|
158
197
|
logger,
|
|
159
198
|
requestId,
|
|
160
|
-
startTime
|
|
199
|
+
startTime,
|
|
200
|
+
operation
|
|
161
201
|
}, async () => {
|
|
162
202
|
const session = await this.endpoint.getSession({
|
|
163
203
|
logger,
|
|
@@ -166,7 +206,8 @@ var TestEndpointAdaptor = class TestEndpointAdaptor {
|
|
|
166
206
|
cookie,
|
|
167
207
|
...rawDb !== void 0 && { db: rawDb }
|
|
168
208
|
});
|
|
169
|
-
|
|
209
|
+
logger = (0, _geekmidas_services.currentRequestContext)()?.logger ?? logger;
|
|
210
|
+
if (!await this.endpoint.authorize({
|
|
170
211
|
header,
|
|
171
212
|
cookie,
|
|
172
213
|
services: ctx.services,
|
|
@@ -176,11 +217,18 @@ var TestEndpointAdaptor = class TestEndpointAdaptor {
|
|
|
176
217
|
body,
|
|
177
218
|
query,
|
|
178
219
|
params
|
|
179
|
-
})
|
|
180
|
-
if (!isAuthorized) {
|
|
220
|
+
})) {
|
|
181
221
|
logger.warn("Forbidden access attempt");
|
|
182
|
-
throw new
|
|
222
|
+
throw new _geekmidas_errors.ForbiddenError("Forbidden access to the endpoint", "You do not have permission to access this resource.");
|
|
183
223
|
}
|
|
224
|
+
const rateLimitHeaders = this.endpoint.rateLimit ? await require_requestLogger.enforceRateLimit(this.endpoint.rateLimit, {
|
|
225
|
+
header,
|
|
226
|
+
services: ctx.services,
|
|
227
|
+
logger,
|
|
228
|
+
session,
|
|
229
|
+
path: this.endpoint.route,
|
|
230
|
+
method: this.endpoint.method
|
|
231
|
+
}) : {};
|
|
184
232
|
const auditorStorage = ctx.auditorStorage;
|
|
185
233
|
let auditContext;
|
|
186
234
|
if (auditorStorage) {
|
|
@@ -199,24 +247,22 @@ var TestEndpointAdaptor = class TestEndpointAdaptor {
|
|
|
199
247
|
} catch (error) {
|
|
200
248
|
logger.error(error, "Failed to extract actor for audits");
|
|
201
249
|
}
|
|
202
|
-
const auditor = new __geekmidas_audit.DefaultAuditor({
|
|
203
|
-
actor,
|
|
204
|
-
storage: auditorStorage,
|
|
205
|
-
metadata: {
|
|
206
|
-
endpoint: this.endpoint.route,
|
|
207
|
-
method: this.endpoint.method
|
|
208
|
-
}
|
|
209
|
-
});
|
|
210
250
|
auditContext = {
|
|
211
|
-
auditor
|
|
251
|
+
auditor: new _geekmidas_audit.DefaultAuditor({
|
|
252
|
+
actor,
|
|
253
|
+
storage: auditorStorage,
|
|
254
|
+
metadata: {
|
|
255
|
+
endpoint: this.endpoint.route,
|
|
256
|
+
method: this.endpoint.method
|
|
257
|
+
}
|
|
258
|
+
}),
|
|
212
259
|
storage: auditorStorage
|
|
213
260
|
};
|
|
214
261
|
}
|
|
215
262
|
const audits = this.endpoint.audits;
|
|
216
263
|
if (!auditContext && audits?.length) logger.warn("No auditor storage service available");
|
|
217
|
-
const
|
|
218
|
-
const
|
|
219
|
-
const db = trx ?? rawDb;
|
|
264
|
+
const { output, metadata, redirect } = await require_requestLogger.executeWithAuditTransaction(auditContext, async (auditor) => {
|
|
265
|
+
const db = auditor?.getTransaction?.() ?? rawDb;
|
|
220
266
|
const responseBuilder = new require_Endpoint.ResponseBuilder();
|
|
221
267
|
const response = await this.endpoint.handler({
|
|
222
268
|
body,
|
|
@@ -231,56 +277,58 @@ var TestEndpointAdaptor = class TestEndpointAdaptor {
|
|
|
231
277
|
db
|
|
232
278
|
}, responseBuilder);
|
|
233
279
|
let data = response;
|
|
234
|
-
let metadata
|
|
280
|
+
let metadata = responseBuilder.getMetadata();
|
|
235
281
|
if (require_Endpoint.Endpoint.hasMetadata(response)) {
|
|
236
282
|
data = response.data;
|
|
237
|
-
metadata
|
|
283
|
+
metadata = response.metadata;
|
|
238
284
|
}
|
|
239
|
-
const
|
|
285
|
+
const redirect = require_Endpoint.Endpoint.isRedirectStatus(metadata.status ?? this.endpoint.status);
|
|
240
286
|
return {
|
|
241
|
-
output:
|
|
242
|
-
metadata
|
|
243
|
-
responseBuilder
|
|
287
|
+
output: redirect ? void 0 : await this.endpoint.parseOutput(data),
|
|
288
|
+
metadata,
|
|
289
|
+
responseBuilder,
|
|
290
|
+
redirect
|
|
244
291
|
};
|
|
245
|
-
}, async (result
|
|
246
|
-
if (!audits?.length) return;
|
|
292
|
+
}, async (result, auditor) => {
|
|
293
|
+
if (!audits?.length || result.redirect) return;
|
|
247
294
|
for (const audit of audits) {
|
|
248
|
-
if (audit.when && !audit.when(result
|
|
249
|
-
const payload = audit.payload(result
|
|
250
|
-
const entityId = audit.entityId?.(result
|
|
295
|
+
if (audit.when && !audit.when(result.output)) continue;
|
|
296
|
+
const payload = audit.payload(result.output);
|
|
297
|
+
const entityId = audit.entityId?.(result.output);
|
|
251
298
|
auditor.audit(audit.type, payload, {
|
|
252
299
|
table: audit.table,
|
|
253
300
|
entityId
|
|
254
301
|
});
|
|
255
302
|
}
|
|
256
303
|
}, { db: rawDb });
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
304
|
+
if (!redirect) await require_functions.publishConstructEvents(this.endpoint, output, this.serviceDiscovery, this.endpoint.logger, ctx.services);
|
|
305
|
+
const headers = {
|
|
306
|
+
...rateLimitHeaders,
|
|
307
|
+
...metadata.headers || {}
|
|
308
|
+
};
|
|
261
309
|
if (metadata.cookies && metadata.cookies.size > 0) {
|
|
262
310
|
const setCookieValues = [];
|
|
263
|
-
for (const [name, cookie
|
|
311
|
+
for (const [name, cookie] of metadata.cookies.entries()) setCookieValues.push(serializeCookie(name, cookie.value, cookie.options));
|
|
264
312
|
headers["set-cookie"] = setCookieValues;
|
|
265
313
|
}
|
|
266
314
|
return {
|
|
267
315
|
body: output,
|
|
268
|
-
status: metadata.status
|
|
316
|
+
status: metadata.status ?? this.endpoint.status,
|
|
269
317
|
headers
|
|
270
318
|
};
|
|
271
319
|
});
|
|
272
320
|
}
|
|
273
321
|
async request(ctx) {
|
|
274
|
-
|
|
275
|
-
return response.body;
|
|
322
|
+
return (await this.fullRequest(ctx)).body;
|
|
276
323
|
}
|
|
277
324
|
};
|
|
278
|
-
|
|
279
325
|
//#endregion
|
|
280
326
|
//#region src/functions/TestFunctionAdaptor.ts
|
|
281
327
|
var TestFunctionAdaptor = class TestFunctionAdaptor {
|
|
328
|
+
fn;
|
|
329
|
+
serviceDiscovery;
|
|
282
330
|
static getDefaultServiceDiscovery(_fn) {
|
|
283
|
-
return
|
|
331
|
+
return _geekmidas_services.ServiceDiscovery.getInstance(new _geekmidas_envkit.EnvironmentParser({}));
|
|
284
332
|
}
|
|
285
333
|
constructor(fn, serviceDiscovery = TestFunctionAdaptor.getDefaultServiceDiscovery(fn)) {
|
|
286
334
|
this.fn = fn;
|
|
@@ -294,16 +342,12 @@ var TestFunctionAdaptor = class TestFunctionAdaptor {
|
|
|
294
342
|
else services = await this.serviceDiscovery.register(this.fn.services);
|
|
295
343
|
let db;
|
|
296
344
|
if ("db" in ctx && ctx.db !== void 0) db = ctx.db;
|
|
297
|
-
else if (this.fn.databaseService)
|
|
298
|
-
const dbServices = await this.serviceDiscovery.register([this.fn.databaseService]);
|
|
299
|
-
db = dbServices[this.fn.databaseService.serviceName];
|
|
300
|
-
}
|
|
345
|
+
else if (this.fn.databaseService) db = (await this.serviceDiscovery.register([this.fn.databaseService]))[this.fn.databaseService.serviceName];
|
|
301
346
|
let auditor;
|
|
302
347
|
if ("auditor" in ctx && ctx.auditor !== void 0) auditor = ctx.auditor;
|
|
303
348
|
else if (this.fn.auditorStorageService) {
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
auditor = new __geekmidas_audit.DefaultAuditor({
|
|
349
|
+
const storage = (await this.serviceDiscovery.register([this.fn.auditorStorageService]))[this.fn.auditorStorageService.serviceName];
|
|
350
|
+
auditor = new _geekmidas_audit.DefaultAuditor({
|
|
307
351
|
actor: {
|
|
308
352
|
id: "system",
|
|
309
353
|
type: "system"
|
|
@@ -316,11 +360,10 @@ var TestFunctionAdaptor = class TestFunctionAdaptor {
|
|
|
316
360
|
});
|
|
317
361
|
}
|
|
318
362
|
const requestId = `test-${Date.now()}`;
|
|
319
|
-
|
|
320
|
-
return (0, __geekmidas_services.runWithRequestContext)({
|
|
363
|
+
return (0, _geekmidas_services.runWithRequestContext)({
|
|
321
364
|
logger,
|
|
322
365
|
requestId,
|
|
323
|
-
startTime
|
|
366
|
+
startTime: Date.now()
|
|
324
367
|
}, async () => {
|
|
325
368
|
const response = await this.fn.fn({
|
|
326
369
|
input: parsedInput,
|
|
@@ -337,26 +380,28 @@ var TestFunctionAdaptor = class TestFunctionAdaptor {
|
|
|
337
380
|
await auditor.flush();
|
|
338
381
|
}
|
|
339
382
|
}
|
|
340
|
-
await
|
|
383
|
+
await require_functions.publishEvents(logger, this.serviceDiscovery, this.fn.events, output, services);
|
|
341
384
|
return output;
|
|
342
385
|
});
|
|
343
386
|
}
|
|
344
387
|
};
|
|
345
|
-
|
|
346
388
|
//#endregion
|
|
347
389
|
//#region src/queue/TestQueueAdaptor.ts
|
|
348
390
|
/**
|
|
349
391
|
* In-memory test driver for a {@link Queue} worker. Hand it a batch of messages
|
|
350
392
|
* and it registers the queue's services, runs the handler in a request context,
|
|
351
|
-
* and returns the handler result — no SQS/pg-boss required.
|
|
393
|
+
* and returns the handler result — no SQS/pg-boss required. A queue with a
|
|
394
|
+
* database gets `db` registered from it, or the one the request passes.
|
|
352
395
|
*
|
|
353
396
|
* @example
|
|
354
397
|
* const adapter = new TestQueueAdaptor(ordersQueue);
|
|
355
398
|
* await adapter.invoke({ messages: [{ orderId: '123' }] });
|
|
356
399
|
*/
|
|
357
400
|
var TestQueueAdaptor = class TestQueueAdaptor {
|
|
401
|
+
queue;
|
|
402
|
+
serviceDiscovery;
|
|
358
403
|
static getDefaultServiceDiscovery() {
|
|
359
|
-
return
|
|
404
|
+
return _geekmidas_services.ServiceDiscovery.getInstance(new _geekmidas_envkit.EnvironmentParser({}));
|
|
360
405
|
}
|
|
361
406
|
constructor(queue, serviceDiscovery = TestQueueAdaptor.getDefaultServiceDiscovery()) {
|
|
362
407
|
this.queue = queue;
|
|
@@ -365,25 +410,35 @@ var TestQueueAdaptor = class TestQueueAdaptor {
|
|
|
365
410
|
async invoke(request) {
|
|
366
411
|
const logger = this.queue.logger.child({ test: true });
|
|
367
412
|
const services = request.services ?? await this.serviceDiscovery.register(this.queue.services);
|
|
413
|
+
const db = request.db !== void 0 ? request.db : await this.getDatabase();
|
|
368
414
|
const requestId = `test-${Date.now()}`;
|
|
369
415
|
const startTime = Date.now();
|
|
370
|
-
|
|
416
|
+
const operation = `queue ${this.queue.name}`;
|
|
417
|
+
return (0, _geekmidas_services.runWithRequestContext)({
|
|
371
418
|
logger,
|
|
372
419
|
requestId,
|
|
373
|
-
startTime
|
|
420
|
+
startTime,
|
|
421
|
+
operation
|
|
374
422
|
}, () => this.queue.handler({
|
|
375
423
|
messages: request.messages,
|
|
376
424
|
services,
|
|
377
|
-
logger
|
|
425
|
+
logger,
|
|
426
|
+
db
|
|
378
427
|
}));
|
|
379
428
|
}
|
|
429
|
+
async getDatabase() {
|
|
430
|
+
const service = this.queue.databaseService;
|
|
431
|
+
if (!service) return void 0;
|
|
432
|
+
return (await this.serviceDiscovery.register([service]))[service.serviceName];
|
|
433
|
+
}
|
|
380
434
|
};
|
|
381
|
-
|
|
382
435
|
//#endregion
|
|
383
436
|
//#region src/subscribers/TestSubscriberAdaptor.ts
|
|
384
437
|
var TestSubscriberAdaptor = class TestSubscriberAdaptor {
|
|
438
|
+
subscriber;
|
|
439
|
+
serviceDiscovery;
|
|
385
440
|
static getDefaultServiceDiscovery() {
|
|
386
|
-
return
|
|
441
|
+
return _geekmidas_services.ServiceDiscovery.getInstance(new _geekmidas_envkit.EnvironmentParser({}));
|
|
387
442
|
}
|
|
388
443
|
constructor(subscriber, serviceDiscovery = TestSubscriberAdaptor.getDefaultServiceDiscovery()) {
|
|
389
444
|
this.subscriber = subscriber;
|
|
@@ -394,19 +449,23 @@ var TestSubscriberAdaptor = class TestSubscriberAdaptor {
|
|
|
394
449
|
let services;
|
|
395
450
|
if (request.services) services = request.services;
|
|
396
451
|
else services = await this.serviceDiscovery.register(this.subscriber.services);
|
|
452
|
+
const db = request.db !== void 0 ? request.db : await this.getDatabase();
|
|
397
453
|
const filteredEvents = this.filterEvents(request.events);
|
|
398
454
|
if (filteredEvents.length === 0) return { batchItemFailures: [] };
|
|
399
455
|
const requestId = `test-${Date.now()}`;
|
|
400
456
|
const startTime = Date.now();
|
|
401
|
-
|
|
457
|
+
const operation = `subscriber ${this.subscriber.topicName ?? "test"}`;
|
|
458
|
+
return (0, _geekmidas_services.runWithRequestContext)({
|
|
402
459
|
logger,
|
|
403
460
|
requestId,
|
|
404
|
-
startTime
|
|
461
|
+
startTime,
|
|
462
|
+
operation
|
|
405
463
|
}, async () => {
|
|
406
464
|
const result = await this.subscriber.handler({
|
|
407
465
|
events: filteredEvents,
|
|
408
466
|
services,
|
|
409
|
-
logger
|
|
467
|
+
logger,
|
|
468
|
+
db
|
|
410
469
|
});
|
|
411
470
|
let output = result;
|
|
412
471
|
if (this.subscriber.outputSchema && result) {
|
|
@@ -414,21 +473,628 @@ var TestSubscriberAdaptor = class TestSubscriberAdaptor {
|
|
|
414
473
|
if (validationResult.issues) throw new Error("Subscriber output validation failed");
|
|
415
474
|
output = validationResult.value;
|
|
416
475
|
}
|
|
417
|
-
await require_lazyAccessors.publishEvents(logger, this.serviceDiscovery, this.subscriber.events, output, this.subscriber.publisherService);
|
|
418
476
|
return output;
|
|
419
477
|
});
|
|
420
478
|
}
|
|
479
|
+
async getDatabase() {
|
|
480
|
+
const service = this.subscriber.databaseService;
|
|
481
|
+
if (!service) return void 0;
|
|
482
|
+
return (await this.serviceDiscovery.register([service]))[service.serviceName];
|
|
483
|
+
}
|
|
421
484
|
filterEvents(events) {
|
|
422
485
|
if (!this.subscriber.subscribedEvents) return events;
|
|
423
486
|
return events.filter((event) => this.subscriber.subscribedEvents.includes(event.type));
|
|
424
487
|
}
|
|
425
488
|
};
|
|
426
|
-
|
|
427
489
|
//#endregion
|
|
428
|
-
|
|
490
|
+
//#region src/testing/manifest.ts
|
|
491
|
+
/**
|
|
492
|
+
* The test manifest: what `gkm test` knows about an app, written down for the
|
|
493
|
+
* suite it starts.
|
|
494
|
+
*
|
|
495
|
+
* `gkm test` discovers the constructs, reconciles the test stage and resolves
|
|
496
|
+
* its environment — then, until this existed, threw all of it away and left a
|
|
497
|
+
* test to declare it again by hand. The manifest keeps it: which construct and
|
|
498
|
+
* endpoint is exported where, and the environment the test stage resolved,
|
|
499
|
+
* under the keys the constructs themselves derive. A feature test is built from
|
|
500
|
+
* it, so nothing a test says can drift from what the app declares.
|
|
501
|
+
*/
|
|
502
|
+
/** The environment variable `gkm test` names the manifest in. */
|
|
503
|
+
const TEST_MANIFEST_ENV = "GKM_TEST_MANIFEST";
|
|
504
|
+
/**
|
|
505
|
+
* Read a test manifest: from a path, a `file:` URL, or — given nothing — the
|
|
506
|
+
* one `gkm test` named in `GKM_TEST_MANIFEST`.
|
|
507
|
+
*/
|
|
508
|
+
function loadTestManifest(from) {
|
|
509
|
+
const location = from ?? process.env["GKM_TEST_MANIFEST"];
|
|
510
|
+
if (!location) throw new NoTestManifest();
|
|
511
|
+
trustLocalEdge();
|
|
512
|
+
const path = location instanceof URL || location.startsWith("file:") ? (0, node_url.fileURLToPath)(location) : location;
|
|
513
|
+
return JSON.parse((0, node_fs.readFileSync)(path, "utf-8"));
|
|
514
|
+
}
|
|
515
|
+
let trusted = false;
|
|
516
|
+
/**
|
|
517
|
+
* Trust the local edge's certificate authority in this thread.
|
|
518
|
+
*
|
|
519
|
+
* `NODE_EXTRA_CA_CERTS` does it for a process started with it — which a
|
|
520
|
+
* suite started by `gkm test` is, and one started by plain `vitest` or an
|
|
521
|
+
* editor is not: its setup only learns the path once Vitest is running, and a
|
|
522
|
+
* worker *thread* shares that process and keeps a TLS store of its own. Every
|
|
523
|
+
* feature test loads the manifest, so this is the one place each worker passes
|
|
524
|
+
* through.
|
|
525
|
+
*/
|
|
526
|
+
function trustLocalEdge() {
|
|
527
|
+
const certificate = process.env.NODE_EXTRA_CA_CERTS;
|
|
528
|
+
if (trusted || !certificate || !(0, node_fs.existsSync)(certificate)) return;
|
|
529
|
+
trusted = true;
|
|
530
|
+
node_tls.default.setDefaultCACertificates([...node_tls.default.getCACertificates("default"), (0, node_fs.readFileSync)(certificate, "utf-8")]);
|
|
531
|
+
}
|
|
532
|
+
/** A feature test ran without a manifest to build itself from. */
|
|
533
|
+
var NoTestManifest = class extends Error {
|
|
534
|
+
constructor() {
|
|
535
|
+
super(`No test manifest: run the suite with \`gkm test\`, or add \`globalSetup: ['@geekmidas/cli/vitest']\` to the root vitest config — either writes one and names it in ${TEST_MANIFEST_ENV}.`);
|
|
536
|
+
this.name = "NoTestManifest";
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
//#endregion
|
|
540
|
+
//#region src/testing/featureTest.ts
|
|
541
|
+
/**
|
|
542
|
+
* `featureTest`: an app, driven the way it runs deployed.
|
|
543
|
+
*
|
|
544
|
+
* A browser signs in and calls the API; the API calls the auth server; each
|
|
545
|
+
* over its URL, each database in its own transaction. Nothing runs as a server:
|
|
546
|
+
* every URL an app's constructs answer on is served in-process through MSW,
|
|
547
|
+
* from the real handler, for the test that made the request. Everything else
|
|
548
|
+
* with a real local equivalent — Postgres, Mailpit, MinIO — is used directly.
|
|
549
|
+
*
|
|
550
|
+
* It is built from the test manifest `gkm test` writes — the constructs and
|
|
551
|
+
* endpoints the app exports, and the environment the test stage resolved — so
|
|
552
|
+
* a test declares none of it. `gkm test` also generates the configured `it`
|
|
553
|
+
* and a `Browser` with a typed client per surface:
|
|
554
|
+
*
|
|
555
|
+
* ```ts
|
|
556
|
+
* import { it } from '#test';
|
|
557
|
+
*
|
|
558
|
+
* it('shows the signed-in user their profile', async ({ browser, mailbox }) => {
|
|
559
|
+
* await browser.auth.signIn.magicLink({ email: 'ada@shop.test' });
|
|
560
|
+
* await browser.visit((await mailbox('ada@shop.test').last()).link!);
|
|
561
|
+
* expect(await browser.api.get('/profile')).toMatchObject({ email: 'ada@shop.test' });
|
|
562
|
+
* });
|
|
563
|
+
* ```
|
|
564
|
+
*/
|
|
565
|
+
/** Every test in flight in this process, by id. */
|
|
566
|
+
const contexts = /* @__PURE__ */ new Map();
|
|
567
|
+
/** Databases already resolving to test transactions — patched once each. */
|
|
568
|
+
const bound = /* @__PURE__ */ new WeakSet();
|
|
569
|
+
function featureTest(options = {}) {
|
|
570
|
+
const manifest = options.manifest ?? loadTestManifest();
|
|
571
|
+
const BrowserClass = options.browser ?? _geekmidas_testkit_browser.Browser;
|
|
572
|
+
let app;
|
|
573
|
+
let factoryDatabases = [];
|
|
574
|
+
let owned = [];
|
|
575
|
+
let network;
|
|
576
|
+
let restoreFetch = () => {};
|
|
577
|
+
(0, vitest.beforeAll)(async () => {
|
|
578
|
+
app = await load(manifest, options.modules ?? {});
|
|
579
|
+
owned = ownDatabases(app, manifest);
|
|
580
|
+
factoryDatabases = databasesFor(options.factories ?? {}, owned);
|
|
581
|
+
for (const database of app.databases) bindToTests(database);
|
|
582
|
+
network = (0, msw_node.setupServer)(...surfaceHandlers(app.endpoints, manifest.env), ...app.auths.map((auth) => authHandler(auth, manifest.env, app.envParser)), ...Object.entries(options.fakes ?? {}).flatMap(([id, fake]) => fakeHandler(id, fake, manifest.env)));
|
|
583
|
+
network.listen({ onUnhandledRequest(request, print) {
|
|
584
|
+
if (isLocal(new URL(request.url))) return;
|
|
585
|
+
print.error();
|
|
586
|
+
} });
|
|
587
|
+
const restoreStamp = (0, _geekmidas_testkit_context.installContextFetch)();
|
|
588
|
+
const stamped = globalThis.fetch;
|
|
589
|
+
globalThis.fetch = deliveringFetch(stamped, (id) => {
|
|
590
|
+
const state = contexts.get(id);
|
|
591
|
+
return state ? deliver(app, state, id) : Promise.resolve();
|
|
592
|
+
});
|
|
593
|
+
restoreFetch = () => {
|
|
594
|
+
globalThis.fetch = stamped;
|
|
595
|
+
restoreStamp();
|
|
596
|
+
};
|
|
597
|
+
});
|
|
598
|
+
(0, vitest.afterAll)(() => {
|
|
599
|
+
restoreFetch();
|
|
600
|
+
network?.close();
|
|
601
|
+
});
|
|
602
|
+
const run = (name, fn) => async () => {
|
|
603
|
+
const id = (0, node_crypto.randomUUID)();
|
|
604
|
+
_geekmidas_testkit_faker.faker.seed(seedOf(name));
|
|
605
|
+
const state = {
|
|
606
|
+
transactions: new _geekmidas_testkit_transactions.TransactionRegistry(),
|
|
607
|
+
discovery: new _geekmidas_services.ServiceDiscovery(app.envParser),
|
|
608
|
+
surfaces: /* @__PURE__ */ new Map(),
|
|
609
|
+
auth: /* @__PURE__ */ new Map(),
|
|
610
|
+
addresses: /* @__PURE__ */ new Set(),
|
|
611
|
+
published: /* @__PURE__ */ new Map(),
|
|
612
|
+
pending: [],
|
|
613
|
+
delivering: false
|
|
614
|
+
};
|
|
615
|
+
const readMail = app.readMail;
|
|
616
|
+
if (readMail) state.mailbox = (address) => {
|
|
617
|
+
state.addresses.add(address);
|
|
618
|
+
return readMail(address);
|
|
619
|
+
};
|
|
620
|
+
contexts.set(id, state);
|
|
621
|
+
await state.discovery.register(recorders(app.channels, state));
|
|
622
|
+
try {
|
|
623
|
+
await (0, _geekmidas_testkit_context.runInTestContext)(id, async () => {
|
|
624
|
+
const browser = new BrowserClass();
|
|
625
|
+
const restore = browser.install();
|
|
626
|
+
try {
|
|
627
|
+
const transaction = (database) => state.transactions.get(database.id, urlOf(database, app.envParser), database.clientConfig);
|
|
628
|
+
const db = { get: (name) => {
|
|
629
|
+
const database = owned.find((candidate) => candidate.service.serviceName === name);
|
|
630
|
+
if (!database) throw new UnknownDatabase(name, owned.map((candidate) => candidate.service.serviceName));
|
|
631
|
+
return transaction(database);
|
|
632
|
+
} };
|
|
633
|
+
const built = /* @__PURE__ */ new Map();
|
|
634
|
+
await fn({
|
|
635
|
+
browser,
|
|
636
|
+
db,
|
|
637
|
+
factories: { get: (name) => {
|
|
638
|
+
const database = factoryDatabases.find(([candidate]) => candidate === name)?.[1];
|
|
639
|
+
if (!database) throw new UnknownFactory(name, factoryDatabases.map(([candidate]) => candidate));
|
|
640
|
+
let factory = built.get(name);
|
|
641
|
+
if (!factory) {
|
|
642
|
+
factory = transaction(database).then(options.factories[name]);
|
|
643
|
+
built.set(name, factory);
|
|
644
|
+
}
|
|
645
|
+
return factory;
|
|
646
|
+
} },
|
|
647
|
+
faker: _geekmidas_testkit_faker.faker,
|
|
648
|
+
mailbox: (address) => {
|
|
649
|
+
if (!state.mailbox) throw new NoInbox();
|
|
650
|
+
return state.mailbox(address);
|
|
651
|
+
},
|
|
652
|
+
published: (channel) => [...state.published.get(channel.id) ?? []],
|
|
653
|
+
services: { get: (name) => resolveService(app, state, name) },
|
|
654
|
+
subscriber: (subscriber) => thenDeliver(new TestSubscriberAdaptor(subscriber, state.discovery), () => deliver(app, state, id)),
|
|
655
|
+
queue: (queue) => thenDeliver(new TestQueueAdaptor(queue, state.discovery), () => deliver(app, state, id))
|
|
656
|
+
});
|
|
657
|
+
} finally {
|
|
658
|
+
restore();
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
} finally {
|
|
662
|
+
contexts.delete(id);
|
|
663
|
+
await Promise.all([state.transactions.rollbackAll(), ...[...state.addresses].map((address) => app.readMail?.(address).clear())]);
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
const it = ((name, fn, timeout) => (0, vitest.test)(name, run(name, fn), timeout));
|
|
667
|
+
it.only = (name, fn, timeout) => vitest.test.only(name, run(name, fn), timeout);
|
|
668
|
+
it.skip = (name, fn, timeout) => vitest.test.skip(name, run(name, fn), timeout);
|
|
669
|
+
return it;
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* A test's faker seed, from its name: the same test draws the same data on
|
|
673
|
+
* every run, and two tests draw different data.
|
|
674
|
+
*/
|
|
675
|
+
function seedOf(name) {
|
|
676
|
+
let hash = 0;
|
|
677
|
+
for (const char of name) hash = Math.imul(hash, 31) + char.charCodeAt(0) | 0;
|
|
678
|
+
return hash >>> 0;
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Import what the manifest points at.
|
|
682
|
+
*
|
|
683
|
+
* The same modules the app runs, so a construct here *is* the app's construct —
|
|
684
|
+
* the one its endpoints were built from — never a copy declared again.
|
|
685
|
+
*/
|
|
686
|
+
async function load(manifest, modules) {
|
|
687
|
+
const envParser = new _geekmidas_envkit.EnvironmentParser({ ...manifest.env });
|
|
688
|
+
const imported = async ({ file, export: name }) => (modules[file] ?? await import(file))[name];
|
|
689
|
+
const constructs = await Promise.all(Object.values(manifest.constructs).map(({ source }) => imported(source)));
|
|
690
|
+
const endpoints = (await Promise.all(manifest.endpoints.map(({ source }) => imported(source)))).filter((value) => require_Endpoint.Endpoint.isEndpoint(value));
|
|
691
|
+
const subscribers = (await Promise.all((manifest.subscribers ?? []).map(async ({ source }) => ({
|
|
692
|
+
name: source.export,
|
|
693
|
+
subscriber: await imported(source)
|
|
694
|
+
})))).filter((entry) => entry.subscriber?.__IS_SUBSCRIBER__ === true);
|
|
695
|
+
const unique = (values) => [...new Set(values)];
|
|
696
|
+
const databases = unique(constructs.filter((value) => value instanceof require_kysely.KyselyDatabase));
|
|
697
|
+
const auths = unique(constructs.filter((value) => value instanceof require_auth.BetterAuth));
|
|
698
|
+
const emails = unique(constructs.filter((value) => value instanceof require_email.Email));
|
|
699
|
+
const consumables = unique(constructs.filter(require_construct_interface.isConsumable));
|
|
700
|
+
const channels = unique(constructs.filter((value) => value instanceof require_Topic.Topic || value instanceof require_Queue.Queue));
|
|
701
|
+
const inbox = emails.map((email) => manifest.env[(0, _geekmidas_manifest.provideKey)(email.id, "inboxUrl")]).find(Boolean);
|
|
702
|
+
return {
|
|
703
|
+
envParser,
|
|
704
|
+
endpoints,
|
|
705
|
+
databases,
|
|
706
|
+
auths,
|
|
707
|
+
emails,
|
|
708
|
+
channels,
|
|
709
|
+
consumables,
|
|
710
|
+
subscribers,
|
|
711
|
+
...inbox ? { readMail: (0, _geekmidas_testkit_mailbox.createMailbox)({ inbox }) } : {}
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* A publisher that records rather than sends, under the name a topic or queue
|
|
716
|
+
* is injected by — `services.users` — which is also the one `.event(users, …)`
|
|
717
|
+
* publishes through.
|
|
718
|
+
*/
|
|
719
|
+
function recorders(channels, state) {
|
|
720
|
+
return channels.map((channel) => {
|
|
721
|
+
const publisher = { async publish(messages) {
|
|
722
|
+
const sent = messages.map(({ type, payload }) => ({
|
|
723
|
+
type,
|
|
724
|
+
payload
|
|
725
|
+
}));
|
|
726
|
+
state.published.set(channel.id, [...state.published.get(channel.id) ?? [], ...sent]);
|
|
727
|
+
state.pending.push({
|
|
728
|
+
channel,
|
|
729
|
+
messages: sent
|
|
730
|
+
});
|
|
731
|
+
} };
|
|
732
|
+
return {
|
|
733
|
+
serviceName: channel.service.serviceName,
|
|
734
|
+
register: () => publisher
|
|
735
|
+
};
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* The databases a test is handed: the app's own. A schema tenant an auth server
|
|
740
|
+
* owns is reached through that server, as the app reaches it, and a reader is
|
|
741
|
+
* the same database through a read-only role — neither is another database to
|
|
742
|
+
* test against.
|
|
743
|
+
*/
|
|
744
|
+
function ownDatabases(app, manifest) {
|
|
745
|
+
const owners = new Set(app.auths.map((auth) => auth.databaseId));
|
|
746
|
+
return app.databases.filter((database) => !owners.has(database.id) && manifest.constructs[database.id]?.kind !== "database-reader");
|
|
747
|
+
}
|
|
748
|
+
/** Each factory's database, found by the service name it is keyed by. */
|
|
749
|
+
function databasesFor(factories, databases) {
|
|
750
|
+
return Object.keys(factories).map((name) => {
|
|
751
|
+
const database = databases.find((candidate) => candidate.service.serviceName === name);
|
|
752
|
+
if (!database) throw new UnknownFactory(name, databases.map((candidate) => candidate.service.serviceName));
|
|
753
|
+
return [name, database];
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Make a database resolve, inside a test, to that test's transaction.
|
|
758
|
+
*
|
|
759
|
+
* Everything reaches a database through its service — an endpoint through
|
|
760
|
+
* service discovery, the auth server through its tenant directly — so this is
|
|
761
|
+
* the one place a test's connection can be handed over. Outside a test it is
|
|
762
|
+
* the database it always was.
|
|
763
|
+
*/
|
|
764
|
+
/** Rounds of delivery before a chain of consumers is called a loop. */
|
|
765
|
+
const MAX_DELIVERY_ROUNDS = 25;
|
|
766
|
+
/**
|
|
767
|
+
* Deliver what this test published, in-process, as the broker would — and
|
|
768
|
+
* keep delivering what the consumers publish in turn, until nothing is left.
|
|
769
|
+
*
|
|
770
|
+
* - A queue's messages go to its one consumer, as a batch.
|
|
771
|
+
* - A topic's events go to every subscriber that named their type, and only
|
|
772
|
+
* those: fan-out, as the filter policy (SNS) or the per-subscriber queue
|
|
773
|
+
* (pg-boss) does it.
|
|
774
|
+
* - Each payload is checked against the consumer's schema first. Turning an
|
|
775
|
+
* SNS envelope or an SQS record into `{ type, payload }` is each adaptor's
|
|
776
|
+
* job and tested there; what is tested here is what the handler is handed.
|
|
777
|
+
* - Consumers run as the server side of this test, so their databases are
|
|
778
|
+
* this test's transactions and their writes roll back with it.
|
|
779
|
+
* - A consumer that throws fails the test. Deployed, the message would be
|
|
780
|
+
* retried; here a retry would only hide the bug.
|
|
781
|
+
*/
|
|
782
|
+
async function deliver(app, state, id) {
|
|
783
|
+
if (state.delivering) return;
|
|
784
|
+
state.delivering = true;
|
|
785
|
+
try {
|
|
786
|
+
for (let round = 0; state.pending.length > 0; round++) {
|
|
787
|
+
if (round === MAX_DELIVERY_ROUNDS) throw new DeliveryDidNotSettle(MAX_DELIVERY_ROUNDS, [...new Set(state.pending.map(({ channel }) => channel.id))]);
|
|
788
|
+
for (const { channel, messages } of state.pending.splice(0)) if (channel instanceof require_Queue.Queue) await toQueue(channel, messages, state, id);
|
|
789
|
+
else await toSubscribers(channel, messages, app, state, id);
|
|
790
|
+
}
|
|
791
|
+
} finally {
|
|
792
|
+
state.delivering = false;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
async function toQueue(queue, messages, state, id) {
|
|
796
|
+
const payloads = [];
|
|
797
|
+
for (const message of messages) payloads.push(await accepted(queue.messageSchema, message, `queue '${queue.name}'`));
|
|
798
|
+
await (0, _geekmidas_testkit_context.runAsServer)(id, () => new TestQueueAdaptor(queue, state.discovery).invoke({ messages: payloads })).catch((error) => {
|
|
799
|
+
throw new DeliveryFailed(`queue '${queue.name}'`, messages, error);
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
async function toSubscribers(topic, messages, app, state, id) {
|
|
803
|
+
for (const { name, subscriber } of app.subscribers) {
|
|
804
|
+
if (subscriber.topicName !== topic.name) continue;
|
|
805
|
+
const named = messages.filter(({ type }) => (subscriber.subscribedEvents ?? []).includes(type));
|
|
806
|
+
if (named.length === 0) continue;
|
|
807
|
+
const consumer = `subscriber '${name}'`;
|
|
808
|
+
const events = [];
|
|
809
|
+
for (const message of named) {
|
|
810
|
+
const schema = topic.eventSchemas[message.type];
|
|
811
|
+
events.push({
|
|
812
|
+
type: message.type,
|
|
813
|
+
payload: await accepted(schema, message, consumer)
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
await (0, _geekmidas_testkit_context.runAsServer)(id, () => new TestSubscriberAdaptor(subscriber, state.discovery).invoke({ events })).catch((error) => {
|
|
817
|
+
throw new DeliveryFailed(consumer, named, error);
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
/** The payload, if the consumer's schema takes it — what the handler is handed. */
|
|
822
|
+
async function accepted(schema, message, consumer) {
|
|
823
|
+
const standard = schema?.["~standard"];
|
|
824
|
+
if (!standard) return message.payload;
|
|
825
|
+
const result = await standard.validate(message.payload);
|
|
826
|
+
if (result.issues) throw new MessageRejected(consumer, message, result.issues);
|
|
827
|
+
return result.value;
|
|
828
|
+
}
|
|
829
|
+
/** `fetch`, delivering what the test published once each of its requests answers. */
|
|
830
|
+
function deliveringFetch(inner, settle) {
|
|
831
|
+
return async (input, init) => {
|
|
832
|
+
const context = (0, _geekmidas_testkit_context.currentTestContext)();
|
|
833
|
+
const response = await inner(input, init);
|
|
834
|
+
if (context?.side === "client") await settle(context.id);
|
|
835
|
+
return response;
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
/** An adaptor whose `invoke` delivers what it published before returning. */
|
|
839
|
+
function thenDeliver(adaptor, settle) {
|
|
840
|
+
const invoke = adaptor.invoke.bind(adaptor);
|
|
841
|
+
adaptor.invoke = (async (...args) => {
|
|
842
|
+
const result = await invoke(...args);
|
|
843
|
+
await settle();
|
|
844
|
+
return result;
|
|
845
|
+
});
|
|
846
|
+
return adaptor;
|
|
847
|
+
}
|
|
848
|
+
function bindToTests(database) {
|
|
849
|
+
if (bound.has(database)) return;
|
|
850
|
+
bound.add(database);
|
|
851
|
+
const service = database.service;
|
|
852
|
+
const register = service.register;
|
|
853
|
+
service.register = (options) => {
|
|
854
|
+
const context = (0, _geekmidas_testkit_context.currentTestContext)();
|
|
855
|
+
const state = context && contexts.get(context.id);
|
|
856
|
+
if (!state) return register(options);
|
|
857
|
+
return state.transactions.get(database.id, urlOf(database, options.envParser), database.clientConfig);
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
function urlOf(database, envParser) {
|
|
861
|
+
return envParser.create((get) => ({ url: get((0, _geekmidas_manifest.provideKey)(database.id, "url")).string() })).parse().url;
|
|
862
|
+
}
|
|
863
|
+
/** Each surface's endpoints, served at its URL for the test that asked. */
|
|
864
|
+
function surfaceHandlers(endpoints, env) {
|
|
865
|
+
const bySurface = /* @__PURE__ */ new Map();
|
|
866
|
+
for (const endpoint of endpoints) {
|
|
867
|
+
const surface = endpoint.surface?.id;
|
|
868
|
+
if (!surface) continue;
|
|
869
|
+
bySurface.set(surface, [...bySurface.get(surface) ?? [], endpoint]);
|
|
870
|
+
}
|
|
871
|
+
return [...bySurface].flatMap(([surface, served]) => {
|
|
872
|
+
const base = env[(0, _geekmidas_manifest.provideKey)(surface, "url")];
|
|
873
|
+
if (!base) return [];
|
|
874
|
+
return msw.http.all(`${trim(base)}/*`, ({ request }) => serve(request, async (state) => {
|
|
875
|
+
let app = state.surfaces.get(surface);
|
|
876
|
+
if (!app) {
|
|
877
|
+
app = Promise.resolve().then(() => {
|
|
878
|
+
const hono$1 = new hono.Hono();
|
|
879
|
+
require_HonoEndpointAdaptor.HonoEndpoint.addRoutes(served, state.discovery, hono$1);
|
|
880
|
+
return hono$1;
|
|
881
|
+
});
|
|
882
|
+
state.surfaces.set(surface, app);
|
|
883
|
+
}
|
|
884
|
+
return (await app).fetch(request, inProcessBindings());
|
|
885
|
+
}));
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* An external API's app fake, answering at the URL the test stage resolved for
|
|
890
|
+
* it — so a handler that calls Polar calls the fake, and the test sets up
|
|
891
|
+
* nothing.
|
|
892
|
+
*
|
|
893
|
+
* Not per test, unlike a surface: a fake stands in for somebody else's server,
|
|
894
|
+
* which no test's transaction reaches. An image fake has no handler here — its
|
|
895
|
+
* container answers on localhost, which is let through.
|
|
896
|
+
*/
|
|
897
|
+
function fakeHandler(id, fake, env) {
|
|
898
|
+
const base = env[(0, _geekmidas_manifest.provideKey)(id, "url")];
|
|
899
|
+
if (fake.kind !== "app" || !base) return [];
|
|
900
|
+
return [msw.http.all(`${trim(base)}/*`, ({ request }) => fake.handler.fetch(request))];
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* A construct's client for this test, by service name — through the test's own
|
|
904
|
+
* discovery, so a topic or queue is its recorder and a database its
|
|
905
|
+
* transaction, exactly as the test's endpoints get them.
|
|
906
|
+
*/
|
|
907
|
+
async function resolveService(app, state, name) {
|
|
908
|
+
const construct = app.consumables.find((candidate) => candidate.service.serviceName === name);
|
|
909
|
+
if (!construct) throw new UnknownService(name, app.consumables.map((candidate) => candidate.service.serviceName));
|
|
910
|
+
return (await state.discovery.register([construct.service]))[name];
|
|
911
|
+
}
|
|
912
|
+
/** An auth server, served at its URL, on the transaction of the test that asked. */
|
|
913
|
+
function authHandler(auth, env, envParser) {
|
|
914
|
+
const base = trim(env[(0, _geekmidas_manifest.provideKey)(auth.id, "url")] ?? "");
|
|
915
|
+
return msw.http.all(`${base}/*`, ({ request }) => serve(request, async (state) => {
|
|
916
|
+
let app = state.auth.get(auth);
|
|
917
|
+
if (!app) {
|
|
918
|
+
app = auth.server({
|
|
919
|
+
envParser,
|
|
920
|
+
context: _geekmidas_services.serviceContext
|
|
921
|
+
}).then(({ app }) => app);
|
|
922
|
+
state.auth.set(auth, app);
|
|
923
|
+
}
|
|
924
|
+
return (await app).fetch(request, inProcessBindings());
|
|
925
|
+
}));
|
|
926
|
+
}
|
|
927
|
+
/** Handle a request as the server side of the test it was made for. */
|
|
928
|
+
async function serve(request, handle) {
|
|
929
|
+
const id = (0, _geekmidas_testkit_context.testContextOf)(request);
|
|
930
|
+
const state = id ? contexts.get(id) : void 0;
|
|
931
|
+
if (!id || !state) throw new UnknownTestContext(request.url, id);
|
|
932
|
+
return (0, _geekmidas_testkit_context.runAsServer)(id, () => handle(state));
|
|
933
|
+
}
|
|
934
|
+
function trim(url) {
|
|
935
|
+
return url.replace(/\/$/, "");
|
|
936
|
+
}
|
|
937
|
+
function isLocal(url) {
|
|
938
|
+
return url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname.endsWith(".localhost");
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Sign a test's browser in the way a person does: ask the auth server for a
|
|
942
|
+
* magic link, open the email it sent, follow the link.
|
|
943
|
+
*
|
|
944
|
+
* What the generated `browser.signIn(email)` calls. The address's mail is
|
|
945
|
+
* cleared first — so the link is this request's, not one an earlier test left
|
|
946
|
+
* — and cleared again after the test, like any address a test reads. Returns
|
|
947
|
+
* the session the auth server then reports, as better-auth hands it back.
|
|
948
|
+
*/
|
|
949
|
+
async function signInWithMagicLink(browser, auth, email = freshAddress()) {
|
|
950
|
+
const context = (0, _geekmidas_testkit_context.currentTestContext)();
|
|
951
|
+
const state = context && contexts.get(context.id);
|
|
952
|
+
if (!state) throw new UnknownTestContext("signIn", context?.id);
|
|
953
|
+
if (!state.mailbox) throw new NoInbox();
|
|
954
|
+
const inbox = state.mailbox(email);
|
|
955
|
+
await inbox.clear();
|
|
956
|
+
const requested = await auth.signIn.magicLink({ email });
|
|
957
|
+
if (requested.error) throw new SignInFailed(email, `the auth server refused the magic link: ${requested.error.message ?? "no reason given"}`);
|
|
958
|
+
const mail = await inbox.last();
|
|
959
|
+
if (!mail.link) throw new SignInFailed(email, "its email held no link");
|
|
960
|
+
await browser.visit(mail.link);
|
|
961
|
+
const { data } = await auth.getSession();
|
|
962
|
+
if (!data) throw new SignInFailed(email, "the auth server reported no session after the link was opened");
|
|
963
|
+
return data;
|
|
964
|
+
}
|
|
965
|
+
/**
|
|
966
|
+
* An address nobody else signs in as — what `browser.signIn()` uses when the
|
|
967
|
+
* test does not care who it is.
|
|
968
|
+
*
|
|
969
|
+
* Unique rather than only seeded: tests in other files run at the same time,
|
|
970
|
+
* against the same Mailpit and the same unique columns, and a seeded address
|
|
971
|
+
* repeats wherever two tests share a name. The readable half is the seeded
|
|
972
|
+
* faker's, so a failure still names somebody.
|
|
973
|
+
*/
|
|
974
|
+
function freshAddress() {
|
|
975
|
+
return `${_geekmidas_testkit_faker.faker.internet.username().toLowerCase().replace(/[^a-z0-9]/g, "")}.${(0, node_crypto.randomUUID)().slice(0, 8)}@example.test`;
|
|
976
|
+
}
|
|
977
|
+
/** `browser.signIn` finished without a session. */
|
|
978
|
+
var SignInFailed = class extends Error {
|
|
979
|
+
email;
|
|
980
|
+
reason;
|
|
981
|
+
constructor(email, reason) {
|
|
982
|
+
super(`Signing ${email} in failed: ${reason}.`);
|
|
983
|
+
this.email = email;
|
|
984
|
+
this.reason = reason;
|
|
985
|
+
this.name = "SignInFailed";
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
/** A request reached an in-process server without a test to belong to. */
|
|
989
|
+
var UnknownTestContext = class extends Error {
|
|
990
|
+
url;
|
|
991
|
+
id;
|
|
992
|
+
constructor(url, id) {
|
|
993
|
+
super(id ? `${url} was requested for test ${id}, which is not running. A request that outlives its test — an un-awaited call — lands here.` : `${url} was requested outside any test. Make the request inside a featureTest, so it reaches that test's transactions.`);
|
|
994
|
+
this.url = url;
|
|
995
|
+
this.id = id;
|
|
996
|
+
this.name = "UnknownTestContext";
|
|
997
|
+
}
|
|
998
|
+
};
|
|
999
|
+
/** `mailbox` was used in an app that declares no Email construct. */
|
|
1000
|
+
var NoInbox = class extends Error {
|
|
1001
|
+
constructor() {
|
|
1002
|
+
super("mailbox() reads the inbox of the app's Email construct, and none was found in featureTest's modules — or `gkm test` did not inject its <ID>_INBOX_URL.");
|
|
1003
|
+
this.name = "NoInbox";
|
|
1004
|
+
}
|
|
1005
|
+
};
|
|
1006
|
+
/** `db.get(name)` named no database the test is handed. */
|
|
1007
|
+
var UnknownDatabase = class extends Error {
|
|
1008
|
+
database;
|
|
1009
|
+
known;
|
|
1010
|
+
constructor(database, known) {
|
|
1011
|
+
super(`db.get('${database}') names no database of this app's. ` + (known.length > 0 ? `Its databases: ${known.join(", ")}. ` : "It declares no database of its own. ") + "A tenant an auth server owns is reached through that server, and a reader through the database it reads.");
|
|
1012
|
+
this.database = database;
|
|
1013
|
+
this.known = known;
|
|
1014
|
+
this.name = "UnknownDatabase";
|
|
1015
|
+
}
|
|
1016
|
+
};
|
|
1017
|
+
/** A factory keyed by a service name no database the app declares has. */
|
|
1018
|
+
var UnknownFactory = class extends Error {
|
|
1019
|
+
factory;
|
|
1020
|
+
known;
|
|
1021
|
+
constructor(factory, known) {
|
|
1022
|
+
super(`There is a test factory for '${factory}', and no database this app declares is called that. ` + (known.length > 0 ? `Its databases: ${known.join(", ")}. ` : "It declares no database. ") + `Name the file after the database construct: test/factories/<construct>.ts.`);
|
|
1023
|
+
this.factory = factory;
|
|
1024
|
+
this.known = known;
|
|
1025
|
+
this.name = "UnknownFactory";
|
|
1026
|
+
}
|
|
1027
|
+
};
|
|
1028
|
+
/** A published message the consumer's own schema refuses. */
|
|
1029
|
+
var MessageRejected = class extends Error {
|
|
1030
|
+
consumer;
|
|
1031
|
+
published;
|
|
1032
|
+
issues;
|
|
1033
|
+
constructor(consumer, published, issues) {
|
|
1034
|
+
super(`${consumer} refuses '${published.type}': ${issues.map((issue) => issue.message).join("; ")}. What is published must be what the consumer's schema takes — fix the producer, or the schema.`);
|
|
1035
|
+
this.consumer = consumer;
|
|
1036
|
+
this.published = published;
|
|
1037
|
+
this.issues = issues;
|
|
1038
|
+
this.name = "MessageRejected";
|
|
1039
|
+
}
|
|
1040
|
+
};
|
|
1041
|
+
/** A consumer that threw while handling what the test published. */
|
|
1042
|
+
var DeliveryFailed = class extends Error {
|
|
1043
|
+
consumer;
|
|
1044
|
+
messages;
|
|
1045
|
+
cause;
|
|
1046
|
+
constructor(consumer, messages, cause) {
|
|
1047
|
+
super(`${consumer} failed handling ${messages.map(({ type }) => `'${type}'`).join(", ")}: ${cause?.message ?? String(cause)}. Deployed, the message would be retried; here the failure is the result.`, { cause });
|
|
1048
|
+
this.consumer = consumer;
|
|
1049
|
+
this.messages = messages;
|
|
1050
|
+
this.cause = cause;
|
|
1051
|
+
this.name = "DeliveryFailed";
|
|
1052
|
+
}
|
|
1053
|
+
};
|
|
1054
|
+
/** Consumers that kept publishing to each other without settling. */
|
|
1055
|
+
var DeliveryDidNotSettle = class extends Error {
|
|
1056
|
+
rounds;
|
|
1057
|
+
channels;
|
|
1058
|
+
constructor(rounds, channels) {
|
|
1059
|
+
super(`Delivery did not settle after ${rounds} rounds — consumers are still publishing to ${channels.join(", ")}. A consumer that publishes to what triggers it loops forever deployed too.`);
|
|
1060
|
+
this.rounds = rounds;
|
|
1061
|
+
this.channels = channels;
|
|
1062
|
+
this.name = "DeliveryDidNotSettle";
|
|
1063
|
+
}
|
|
1064
|
+
};
|
|
1065
|
+
/** `services.get(name)` for a name no construct of the app is served by. */
|
|
1066
|
+
var UnknownService = class extends Error {
|
|
1067
|
+
serviceName;
|
|
1068
|
+
available;
|
|
1069
|
+
constructor(serviceName, available) {
|
|
1070
|
+
super(`The app declares no construct served as '${serviceName}'. Its services are: ${available.join(", ") || "none"}.`);
|
|
1071
|
+
this.serviceName = serviceName;
|
|
1072
|
+
this.available = available;
|
|
1073
|
+
this.name = "UnknownService";
|
|
1074
|
+
}
|
|
1075
|
+
};
|
|
1076
|
+
//#endregion
|
|
1077
|
+
exports.DeliveryDidNotSettle = DeliveryDidNotSettle;
|
|
1078
|
+
exports.DeliveryFailed = DeliveryFailed;
|
|
1079
|
+
exports.IN_PROCESS_PEER_ADDRESS = IN_PROCESS_PEER_ADDRESS;
|
|
1080
|
+
exports.MessageRejected = MessageRejected;
|
|
1081
|
+
exports.NoInbox = NoInbox;
|
|
1082
|
+
exports.NoTestManifest = NoTestManifest;
|
|
1083
|
+
exports.SignInFailed = SignInFailed;
|
|
1084
|
+
exports.TEST_CONTEXT_HEADER = _geekmidas_testkit_context.TEST_CONTEXT_HEADER;
|
|
1085
|
+
exports.TEST_MANIFEST_ENV = TEST_MANIFEST_ENV;
|
|
429
1086
|
exports.TestEndpointAdaptor = TestEndpointAdaptor;
|
|
430
1087
|
exports.TestFunctionAdaptor = TestFunctionAdaptor;
|
|
431
1088
|
exports.TestQueueAdaptor = TestQueueAdaptor;
|
|
432
1089
|
exports.TestSubscriberAdaptor = TestSubscriberAdaptor;
|
|
1090
|
+
exports.UnknownDatabase = UnknownDatabase;
|
|
1091
|
+
exports.UnknownFactory = UnknownFactory;
|
|
1092
|
+
exports.UnknownService = UnknownService;
|
|
1093
|
+
exports.UnknownTestContext = UnknownTestContext;
|
|
433
1094
|
exports.createMswHandlers = createMswHandlers;
|
|
1095
|
+
exports.featureTest = featureTest;
|
|
1096
|
+
exports.inProcessBindings = inProcessBindings;
|
|
1097
|
+
exports.loadTestManifest = loadTestManifest;
|
|
1098
|
+
exports.signInWithMagicLink = signInWithMagicLink;
|
|
1099
|
+
|
|
434
1100
|
//# sourceMappingURL=testing.cjs.map
|