@geekmidas/constructs 10.0.0-alpha.38 → 10.0.0-alpha.39
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 +156 -155
- package/dist/{Construct-Detz65RA.mjs → Construct-BO1X-kt9.mjs} +3 -6
- 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-yG9Sw-5p.cjs → Construct-DmvXceVY.cjs} +3 -6
- 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-Brs_n_vQ.d.cts → Cron-CMEGfRs8.d.cts} +7 -5
- package/dist/Cron-CMEGfRs8.d.cts.map +1 -0
- package/dist/{Cron-C18-2Lgb.d.mts → Cron-D6X4-xjB.d.mts} +7 -5
- package/dist/Cron-D6X4-xjB.d.mts.map +1 -0
- package/dist/{CronBuilder-4WnWKR40.d.cts → CronBuilder-CEK7aOqD.d.cts} +21 -16
- package/dist/CronBuilder-CEK7aOqD.d.cts.map +1 -0
- package/dist/{CronBuilder-BbnT7tqo.cjs → CronBuilder-DB35T5dL.cjs} +13 -11
- package/dist/CronBuilder-DB35T5dL.cjs.map +1 -0
- package/dist/{CronBuilder-BRkzUOAs.mjs → CronBuilder-Dv0IlZs0.mjs} +14 -12
- package/dist/CronBuilder-Dv0IlZs0.mjs.map +1 -0
- package/dist/{CronBuilder-CGv51EoE.d.mts → CronBuilder-O_gvButp.d.mts} +21 -16
- package/dist/CronBuilder-O_gvButp.d.mts.map +1 -0
- package/dist/{Endpoint-DO332Dnm.mjs → Endpoint-CQAeh1Rh.mjs} +5 -5
- package/dist/Endpoint-CQAeh1Rh.mjs.map +1 -0
- package/dist/{Endpoint-QKuFg5gs.cjs → Endpoint-D1L9qu5C.cjs} +5 -5
- package/dist/Endpoint-D1L9qu5C.cjs.map +1 -0
- package/dist/{Endpoint-BkhPHCuX.d.cts → Endpoint-Dp5AKIXl.d.cts} +10 -13
- package/dist/Endpoint-Dp5AKIXl.d.cts.map +1 -0
- package/dist/{Endpoint-DkiD0GJs.d.mts → Endpoint-DtI7heFm.d.mts} +10 -13
- package/dist/Endpoint-DtI7heFm.d.mts.map +1 -0
- package/dist/{EndpointFactory-B9HAjDAP.mjs → EndpointFactory-BBdNeaGJ.mjs} +23 -52
- package/dist/EndpointFactory-BBdNeaGJ.mjs.map +1 -0
- package/dist/{EndpointFactory-BmO0WnXE.cjs → EndpointFactory-BZOCOaK6.cjs} +22 -51
- package/dist/EndpointFactory-BZOCOaK6.cjs.map +1 -0
- package/dist/{EndpointFactory-WJzZfkTW.d.mts → EndpointFactory-CgMDvERK.d.mts} +58 -48
- package/dist/EndpointFactory-CgMDvERK.d.mts.map +1 -0
- package/dist/{EndpointFactory-n96CT_Ys.d.cts → EndpointFactory-FeFRmlDZ.d.cts} +58 -48
- package/dist/EndpointFactory-FeFRmlDZ.d.cts.map +1 -0
- package/dist/{HonoEndpointAdaptor-CiqAZ5Mt.mjs → HonoEndpointAdaptor-Aui-x9Lg.mjs} +4 -3
- package/dist/HonoEndpointAdaptor-Aui-x9Lg.mjs.map +1 -0
- package/dist/{HonoEndpointAdaptor-DoEQdC3r.cjs → HonoEndpointAdaptor-Cq99r9H7.cjs} +5 -4
- package/dist/HonoEndpointAdaptor-Cq99r9H7.cjs.map +1 -0
- package/dist/{Queue-DpKTLl6E.mjs → Queue-C1uDgjxt.mjs} +29 -48
- package/dist/Queue-C1uDgjxt.mjs.map +1 -0
- package/dist/{Queue-B7yRXQmL.cjs → Queue-Ckmudcm8.cjs} +29 -48
- package/dist/Queue-Ckmudcm8.cjs.map +1 -0
- package/dist/{Queue-Bhzi2WQC.d.cts → Queue-CpQIGB_2.d.cts} +22 -33
- package/dist/Queue-CpQIGB_2.d.cts.map +1 -0
- package/dist/{Queue-BoUA-e83.d.mts → Queue-YmvN2vEP.d.mts} +21 -32
- 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-DQCaiOXF.d.cts → Subscriber-B91YSkZt.d.cts} +12 -13
- package/dist/Subscriber-B91YSkZt.d.cts.map +1 -0
- package/dist/{Subscriber-CIO3pwji.d.mts → Subscriber-BjHYe5hK.d.mts} +11 -12
- package/dist/Subscriber-BjHYe5hK.d.mts.map +1 -0
- package/dist/{SubscriberBuilder-CIXTozBi.d.cts → SubscriberBuilder-BSpQX1JT.d.cts} +19 -23
- package/dist/SubscriberBuilder-BSpQX1JT.d.cts.map +1 -0
- package/dist/{SubscriberBuilder-CMBzPdRX.d.mts → SubscriberBuilder-BhMphk4z.d.mts} +18 -22
- package/dist/SubscriberBuilder-BhMphk4z.d.mts.map +1 -0
- package/dist/{SubscriberBuilder-C2bCOuPT.cjs → SubscriberBuilder-Duk028pD.cjs} +11 -19
- package/dist/SubscriberBuilder-Duk028pD.cjs.map +1 -0
- package/dist/{SubscriberBuilder-B4YRTBng.mjs → SubscriberBuilder-DzQUWE08.mjs} +11 -19
- package/dist/SubscriberBuilder-DzQUWE08.mjs.map +1 -0
- package/dist/{Topic-D5rlM9wS.cjs → Topic-BBVXC3H4.cjs} +46 -51
- package/dist/Topic-BBVXC3H4.cjs.map +1 -0
- package/dist/Topic-DOw051ch.mjs +98 -0
- package/dist/Topic-DOw051ch.mjs.map +1 -0
- package/dist/adaptors/aws.cjs +5 -5
- package/dist/adaptors/aws.cjs.map +1 -1
- package/dist/adaptors/aws.d.cts +26 -26
- package/dist/adaptors/aws.d.cts.map +1 -1
- package/dist/adaptors/aws.d.mts +25 -25
- package/dist/adaptors/aws.d.mts.map +1 -1
- package/dist/adaptors/aws.mjs +5 -5
- package/dist/adaptors/aws.mjs.map +1 -1
- package/dist/adaptors/hono.cjs +1 -1
- package/dist/adaptors/hono.d.cts +4 -5
- package/dist/adaptors/hono.d.cts.map +1 -1
- package/dist/adaptors/hono.d.mts +4 -5
- package/dist/adaptors/hono.d.mts.map +1 -1
- package/dist/adaptors/hono.mjs +1 -1
- package/dist/adaptors/testing.cjs +15 -17
- package/dist/adaptors/testing.cjs.map +1 -1
- package/dist/adaptors/testing.d.cts +26 -28
- package/dist/adaptors/testing.d.cts.map +1 -1
- package/dist/adaptors/testing.d.mts +26 -28
- package/dist/adaptors/testing.d.mts.map +1 -1
- package/dist/adaptors/testing.mjs +15 -17
- package/dist/adaptors/testing.mjs.map +1 -1
- package/dist/{auth-6Cdx5amw.d.cts → auth-CMwml9QN.d.mts} +4 -4
- package/dist/{auth-6Cdx5amw.d.cts.map → auth-CMwml9QN.d.mts.map} +1 -1
- package/dist/{auth-B_c9b54v.d.mts → auth-rVRDjIw_.d.cts} +4 -4
- package/dist/{auth-B_c9b54v.d.mts.map → auth-rVRDjIw_.d.cts.map} +1 -1
- package/dist/auth.d.cts +1 -1
- package/dist/auth.d.mts +1 -1
- package/dist/{cache-CSJHbgwS.d.cts → cache-C9a_hVCc.d.cts} +2 -2
- package/dist/{cache-CSJHbgwS.d.cts.map → cache-C9a_hVCc.d.cts.map} +1 -1
- package/dist/{cache-HHg5No4L.d.mts → cache-CeTMI8LR.d.mts} +2 -2
- package/dist/{cache-HHg5No4L.d.mts.map → cache-CeTMI8LR.d.mts.map} +1 -1
- package/dist/cache.d.cts +1 -1
- package/dist/cache.d.mts +1 -1
- package/dist/{construct-interface-BLG_BEH5.d.mts → construct-interface-_3zgxzXl.d.mts} +9 -18
- package/dist/{construct-interface-BLG_BEH5.d.mts.map → construct-interface-_3zgxzXl.d.mts.map} +1 -1
- package/dist/{construct-interface-DcNaRKoC.d.cts → construct-interface-_xi4BPN8.d.cts} +9 -18
- package/dist/{construct-interface-DcNaRKoC.d.cts.map → construct-interface-_xi4BPN8.d.cts.map} +1 -1
- package/dist/construct-interface.cjs +1 -30
- package/dist/construct-interface.cjs.map +1 -1
- package/dist/construct-interface.d.cts +2 -2
- package/dist/construct-interface.d.mts +2 -2
- package/dist/construct-interface.mjs +2 -29
- package/dist/construct-interface.mjs.map +1 -1
- package/dist/credential.d.cts +1 -1
- package/dist/credential.d.mts +1 -1
- package/dist/crons/index.cjs +1 -1
- package/dist/crons/index.d.cts +2 -2
- package/dist/crons/index.d.mts +2 -2
- package/dist/crons/index.mjs +1 -1
- package/dist/database/kysely.d.cts +1 -1
- package/dist/database/kysely.d.mts +1 -1
- package/dist/email.d.cts +1 -1
- package/dist/email.d.mts +1 -1
- package/dist/endpoints/index.cjs +5 -4
- package/dist/endpoints/index.d.cts +4 -13
- package/dist/endpoints/index.d.cts.map +1 -1
- package/dist/endpoints/index.d.mts +4 -13
- package/dist/endpoints/index.d.mts.map +1 -1
- package/dist/endpoints/index.mjs +4 -3
- package/dist/{external-api-CZHev1Oh.d.mts → external-api-DS3mujT5.d.mts} +2 -2
- package/dist/{external-api-BKOb2jSC.d.cts.map → external-api-DS3mujT5.d.mts.map} +1 -1
- package/dist/{external-api-BKOb2jSC.d.cts → external-api-LBfE-q3g.d.cts} +2 -2
- package/dist/{external-api-CZHev1Oh.d.mts.map → external-api-LBfE-q3g.d.cts.map} +1 -1
- package/dist/external-api.d.cts +1 -1
- package/dist/external-api.d.mts +1 -1
- package/dist/file-server.d.cts +1 -1
- package/dist/file-server.d.mts +1 -1
- package/dist/functions/index.cjs +1 -1
- package/dist/functions/index.d.cts +1 -1
- package/dist/functions/index.d.mts +1 -1
- package/dist/functions/index.mjs +1 -1
- package/dist/{functions-DlFm8HmH.mjs → functions-DCIfCRR-.mjs} +94 -23
- package/dist/functions-DCIfCRR-.mjs.map +1 -0
- package/dist/{functions-DKN6aQWJ.cjs → functions-ZH60FE7H.cjs} +99 -19
- package/dist/functions-ZH60FE7H.cjs.map +1 -0
- package/dist/{index-Dzkdm-IE.d.cts → index-B0kxstwf.d.cts} +36 -24
- package/dist/index-B0kxstwf.d.cts.map +1 -0
- package/dist/{index-DwveWr3c.d.mts → index-CjVEjOw0.d.mts} +36 -24
- package/dist/index-CjVEjOw0.d.mts.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{kysely-Dy8nNgvk.d.cts → kysely-BJ8IQ_1P.d.mts} +4 -4
- package/dist/{kysely-Dy8nNgvk.d.cts.map → kysely-BJ8IQ_1P.d.mts.map} +1 -1
- package/dist/{kysely-QLoPapbM.d.mts → kysely-DVF8wOT4.d.cts} +4 -4
- package/dist/{kysely-QLoPapbM.d.mts.map → kysely-DVF8wOT4.d.cts.map} +1 -1
- package/dist/{lazyAccessors-BHtcqgz2.mjs → lazyAccessors-CFanua0O.mjs} +2 -39
- package/dist/lazyAccessors-CFanua0O.mjs.map +1 -0
- package/dist/{lazyAccessors-Dx9NLwJZ.cjs → lazyAccessors-hJKGv8Ay.cjs} +1 -50
- package/dist/lazyAccessors-hJKGv8Ay.cjs.map +1 -0
- package/dist/mobile-app.d.cts +1 -1
- package/dist/mobile-app.d.mts +1 -1
- package/dist/object-storage.d.cts +1 -1
- package/dist/object-storage.d.mts +1 -1
- package/dist/processAudits-BpRlPQMW.mjs.map +1 -1
- package/dist/processAudits-D_2W5x8k.cjs.map +1 -1
- package/dist/queue/index.cjs +4 -78
- package/dist/queue/index.d.cts +3 -53
- package/dist/queue/index.d.mts +3 -53
- package/dist/queue/index.mjs +3 -77
- package/dist/rest-api.cjs +2 -4
- package/dist/rest-api.cjs.map +1 -1
- package/dist/rest-api.d.cts +4 -5
- package/dist/rest-api.d.cts.map +1 -1
- package/dist/rest-api.d.mts +4 -5
- package/dist/rest-api.d.mts.map +1 -1
- package/dist/rest-api.mjs +2 -4
- package/dist/rest-api.mjs.map +1 -1
- package/dist/site.d.cts +1 -1
- package/dist/site.d.mts +1 -1
- package/dist/subscribers/index.cjs +1 -1
- package/dist/subscribers/index.d.cts +2 -2
- package/dist/subscribers/index.d.mts +2 -2
- package/dist/subscribers/index.mjs +1 -1
- package/dist/topic/index.cjs +1 -36
- package/dist/topic/index.d.cts +2 -3
- package/dist/topic/index.d.mts +2 -3
- package/dist/topic/index.mjs +2 -35
- package/dist/worker.cjs +26 -17
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.d.cts +37 -29
- package/dist/worker.d.cts.map +1 -1
- package/dist/worker.d.mts +37 -29
- package/dist/worker.d.mts.map +1 -1
- package/dist/worker.mjs +26 -17
- package/dist/worker.mjs.map +1 -1
- package/package.json +31 -31
- package/dist/Construct-DTqYvkPG.d.cts +0 -115
- package/dist/Construct-DTqYvkPG.d.cts.map +0 -1
- package/dist/Construct-Detz65RA.mjs.map +0 -1
- package/dist/Construct-pVadmURD.d.mts +0 -115
- package/dist/Construct-pVadmURD.d.mts.map +0 -1
- package/dist/Construct-yG9Sw-5p.cjs.map +0 -1
- package/dist/Cron-Brs_n_vQ.d.cts.map +0 -1
- package/dist/Cron-C18-2Lgb.d.mts.map +0 -1
- package/dist/CronBuilder-4WnWKR40.d.cts.map +0 -1
- package/dist/CronBuilder-BRkzUOAs.mjs.map +0 -1
- package/dist/CronBuilder-BbnT7tqo.cjs.map +0 -1
- package/dist/CronBuilder-CGv51EoE.d.mts.map +0 -1
- package/dist/Endpoint-BkhPHCuX.d.cts.map +0 -1
- package/dist/Endpoint-DO332Dnm.mjs.map +0 -1
- package/dist/Endpoint-DkiD0GJs.d.mts.map +0 -1
- package/dist/Endpoint-QKuFg5gs.cjs.map +0 -1
- package/dist/EndpointFactory-B9HAjDAP.mjs.map +0 -1
- package/dist/EndpointFactory-BmO0WnXE.cjs.map +0 -1
- package/dist/EndpointFactory-WJzZfkTW.d.mts.map +0 -1
- package/dist/EndpointFactory-n96CT_Ys.d.cts.map +0 -1
- package/dist/HonoEndpointAdaptor-CiqAZ5Mt.mjs.map +0 -1
- package/dist/HonoEndpointAdaptor-DoEQdC3r.cjs.map +0 -1
- package/dist/Queue-B7yRXQmL.cjs.map +0 -1
- package/dist/Queue-Bhzi2WQC.d.cts.map +0 -1
- package/dist/Queue-BoUA-e83.d.mts.map +0 -1
- package/dist/Queue-DpKTLl6E.mjs.map +0 -1
- package/dist/Subscriber-CIO3pwji.d.mts.map +0 -1
- package/dist/Subscriber-DQCaiOXF.d.cts.map +0 -1
- package/dist/SubscriberBuilder-B4YRTBng.mjs.map +0 -1
- package/dist/SubscriberBuilder-C2bCOuPT.cjs.map +0 -1
- package/dist/SubscriberBuilder-CIXTozBi.d.cts.map +0 -1
- package/dist/SubscriberBuilder-CMBzPdRX.d.mts.map +0 -1
- package/dist/Topic-Ct3Le8Wt.mjs +0 -103
- package/dist/Topic-Ct3Le8Wt.mjs.map +0 -1
- package/dist/Topic-D5rlM9wS.cjs.map +0 -1
- package/dist/Topic-D6blllEn.d.cts +0 -89
- package/dist/Topic-D6blllEn.d.cts.map +0 -1
- package/dist/Topic-DVvheJ_-.d.mts +0 -89
- package/dist/Topic-DVvheJ_-.d.mts.map +0 -1
- package/dist/functions-DKN6aQWJ.cjs.map +0 -1
- package/dist/functions-DlFm8HmH.mjs.map +0 -1
- package/dist/index-DwveWr3c.d.mts.map +0 -1
- package/dist/index-Dzkdm-IE.d.cts.map +0 -1
- package/dist/index-fnpjXiA-.d.mts +0 -26
- package/dist/index-fnpjXiA-.d.mts.map +0 -1
- package/dist/index-tE_119SX.d.cts +0 -26
- package/dist/index-tE_119SX.d.cts.map +0 -1
- package/dist/lazyAccessors-BHtcqgz2.mjs.map +0 -1
- package/dist/lazyAccessors-Dx9NLwJZ.cjs.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/topic/index.cjs.map +0 -1
- package/dist/topic/index.mjs.map +0 -1
package/dist/rest-api.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { edgeTo } from "./construct-interface.mjs";
|
|
2
|
-
import { o as getSecurityScheme, t as EndpointFactory } from "./EndpointFactory-
|
|
2
|
+
import { o as getSecurityScheme, t as EndpointFactory } from "./EndpointFactory-BBdNeaGJ.mjs";
|
|
3
3
|
import { n as envParserFor } from "./surfaceEnv-wMT5QzgZ.mjs";
|
|
4
4
|
import { canonicalId, provideKey } from "@geekmidas/manifest";
|
|
5
5
|
import { DEFAULT_LOGGER } from "@geekmidas/logger/console";
|
|
@@ -7,7 +7,7 @@ import { DEFAULT_LOGGER } from "@geekmidas/logger/console";
|
|
|
7
7
|
/**
|
|
8
8
|
* What is *not* here, and why.
|
|
9
9
|
*
|
|
10
|
-
* `dependsOn`, `database`, `auditor` and `
|
|
10
|
+
* `dependsOn`, `database`, `auditor` and `.event(topic, …)` all inject a capability
|
|
11
11
|
* into a handler. Putting one on the surface hands it to every route the
|
|
12
12
|
* surface serves — a health check gets the database because a report needed it
|
|
13
13
|
* — which is the thing least privilege forbids and the reason `.calls()` is not
|
|
@@ -94,7 +94,6 @@ var RestApi = class RestApi {
|
|
|
94
94
|
this.session = endpoints.session.bind(endpoints);
|
|
95
95
|
this.auditor = endpoints.auditor.bind(endpoints);
|
|
96
96
|
this.actor = endpoints.actor.bind(endpoints);
|
|
97
|
-
this.publisher = endpoints.publisher.bind(endpoints);
|
|
98
97
|
this.authorizer = endpoints.authorizer.bind(endpoints);
|
|
99
98
|
this.authorize = endpoints.authorize.bind(endpoints);
|
|
100
99
|
this.rls = endpoints.rls.bind(endpoints);
|
|
@@ -137,7 +136,6 @@ var RestApi = class RestApi {
|
|
|
137
136
|
session;
|
|
138
137
|
auditor;
|
|
139
138
|
actor;
|
|
140
|
-
publisher;
|
|
141
139
|
authorizer;
|
|
142
140
|
authorize;
|
|
143
141
|
rls;
|
package/dist/rest-api.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rest-api.mjs","names":[],"sources":["../src/rest-api.ts"],"sourcesContent":["/**\n * `RestApi` — the application's own HTTP surface, declared.\n *\n * Everything an app serves already existed; what did not exist was a *node* for\n * it. Without one, three things that are properties of the graph had to be\n * written down somewhere else: which origins the API accepts, which origins the\n * auth server trusts, and which URL a frontend is built against. All three were\n * being derived by walking the workspace config for ports — a second mechanism,\n * running beside the graph and answering the same question worse, since it can\n * only ever describe what this repo happens to run.\n *\n * With the surface declared, all three become one edge read backwards: a\n * consumer depends on the API, and *that* is the CORS entry, the trusted origin,\n * and the build-time URL. Nothing enumerates its own callers.\n *\n * ```ts\n * export const api = new RestApi('Api', {\n * authorizers: ['session'],\n * defaultAuthorizer: 'session',\n * });\n * ```\n */\n\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { Logger } from '@geekmidas/logger';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport {\n\ttype ConstructName,\n\tcanonicalId,\n\ttype Declaration,\n\ttype Dependency,\n\tprovideKey,\n} from '@geekmidas/manifest';\nimport type { Telescope } from '@geekmidas/telescope';\nimport {\n\ttype Authenticator,\n\ttype Declarable,\n\tedgeTo,\n} from './construct-interface';\n\nexport type { Authenticator } from './construct-interface';\n\nimport {\n\ttype BuiltInSecuritySchemeId,\n\tgetSecurityScheme,\n} from './endpoints/Authorizer';\nimport { EndpointFactory } from './endpoints/EndpointFactory';\nimport { envParserFor } from './endpoints/surfaceEnv';\n\n/** The factory a surface builds its endpoints from. */\ntype Endpoints = EndpointFactory<[], '', Logger>;\n\nexport interface RestApiConfig<\n\t// `readonly []` rather than `readonly string[]`: a surface that declares no\n\t// authorizers of its own should narrow `defaultAuthorizer` to the built-ins\n\t// and `'none'`, not widen it back to any string.\n\tTAuthorizers extends readonly string[] = readonly [],\n> {\n\t/**\n\t * The app that serves this surface, relative to the workspace root —\n\t * `'apps/api'`, or `'.'` in a single-app project.\n\t *\n\t * Required. It was inferred from the id (`apps/<kebab-id>`, or the root\n\t * when no such directory existed), which meant a surface's home was\n\t * whatever happened to be on disk. It is where the app's `package.json`\n\t * and base `tsconfig.json` are, and which build serves this surface. It\n\t * does not change discovery: constructs still load from the workspace's\n\t * `constructs` glob.\n\t */\n\tpath: string;\n\t/**\n\t * CORS tunables. The *origins* are never here — they are read off the\n\t * constructs that declared an edge to this surface, which is the whole point\n\t * of declaring one. These are the parts a graph cannot answer.\n\t */\n\tcors?: {\n\t\tmaxAge?: number;\n\t\tcredentials?: boolean;\n\t\tallowHeaders?: readonly string[];\n\t\texposeHeaders?: readonly string[];\n\t};\n\t/**\n\t * The authorizer names this surface exposes. Names only — what verifies a\n\t * request legitimately differs between local and deployed, so the mechanism\n\t * belongs to the target and never to portable code.\n\t */\n\tauthorizers?: TAuthorizers;\n\t/**\n\t * The authorizer applied to an endpoint that names none.\n\t *\n\t * Required, and `'none'` is a valid answer that has to be typed out. The\n\t * alternative is an API that ships open because a field was left off, which\n\t * is the one default worth refusing to have.\n\t *\n\t * It was called `default`, which said what it was to the type and nothing\n\t * to the reader: next to `authorizers: ['iam']`, a bare `default: 'none'`\n\t * could be defaulting the runtime or the target. The manifest has always\n\t * called it `defaultAuthorizer` and the factory `defaultAuthorizerName`;\n\t * this is the same fact under the same name.\n\t *\n\t * Typed from `authorizers`, so it can only name one this surface exposes —\n\t * plus a built-in scheme, and `'none'`. It was a bare `string`, which let\n\t * `defaultAuthorizer: 'jwtt'` compile and every endpoint on the surface\n\t * default to an authorizer that does not exist.\n\t */\n\tdefaultAuthorizer: TAuthorizers[number] | BuiltInSecuritySchemeId | 'none';\n\t/**\n\t * The logger every endpoint on this surface runs with.\n\t *\n\t * The actual logger, not a path to one. It used to be named in config as\n\t * `logger: './config/logger'`, because the build wrote an import into each\n\t * generated handler and a generator can print a specifier but not an object.\n\t * Endpoints built from the surface carry it, so there is nothing to print\n\t * and nothing to keep in step with a moved file.\n\t */\n\tlogger?: Logger;\n\t/**\n\t * The environment parser every endpoint on this surface runs with.\n\t *\n\t * Same reason as `logger`, and the same field it replaces —\n\t * `envParser: './config/env#envParser'`, a module path with an export name\n\t * appended, parsed by us, checked by nothing.\n\t */\n\tenvParser?: EnvironmentParser<{}>;\n\t/**\n\t * The Telescope instance this surface's entry mounts.\n\t *\n\t * The actual instance, for the reason `logger` is: it used to be named as\n\t * `telescope: './config/telescope#telescope'`, a module path the build\n\t * printed an import for. The entry already imports the surface.\n\t */\n\ttelescope?: Telescope;\n}\n\n/**\n * What is *not* here, and why.\n *\n * `dependsOn`, `database`, `auditor` and `publisher` all inject a capability\n * into a handler. Putting one on the surface hands it to every route the\n * surface serves — a health check gets the database because a report needed it\n * — which is the thing least privilege forbids and the reason `.calls()` is not\n * spelled `dependsOn`. They belong to the endpoint, or to a branch —\n * `api.database(db)` — for a group of endpoints that genuinely share them. A\n * branch is a new factory; the surface config stays free of grants.\n *\n * `auth` is not here either: `.auth(construct)` already declares it, and a\n * second way to say the same thing is the duplication this whole model removes.\n *\n * What is left is what the *process* is rather than what a route may reach.\n */\n\nexport class RestApi<\n\tTName extends string = string,\n\tconst TAuthorizers extends readonly string[] = readonly [],\n> implements Declarable<TName>\n{\n\treadonly id: TName;\n\n\t/**\n\t * Declared once and read by both `declare()` and anything that consumes this\n\t * surface, so what the target publishes and what a consumer reads cannot\n\t * drift.\n\t */\n\treadonly keys: {\n\t\turl: string;\n\t\ttrustedOrigins: string;\n\t\tcookieDomain: string;\n\t};\n\n\t/**\n\t * The logger every endpoint on this surface runs with.\n\t *\n\t * Always defined — the surface's own if it was given one, the console\n\t * logger otherwise — so nothing downstream has to decide what to do about a\n\t * missing one. The generated entry reads it from here.\n\t */\n\treadonly logger: Logger;\n\n\t/**\n\t * The environment parser every endpoint on this surface runs with.\n\t *\n\t * Always defined, and normally the default: `process.env` merged with the\n\t * credentials `gkm dev`/`gkm exec` injected. That is what every\n\t * application's own `config/env.ts` was — boilerplate identical in every\n\t * project, and mandatory, which is a poor combination.\n\t */\n\treadonly envParser: EnvironmentParser<{}>;\n\n\t/** The Telescope instance, when this surface was given one. */\n\treadonly telescope?: Telescope;\n\n\tconstructor(\n\t\tid: ConstructName<TName>,\n\t\tprivate readonly config: RestApiConfig<TAuthorizers>,\n\t\t/** Internal: how `.calls()` carries edges into the copy it returns. */\n\t\tprivate readonly dependencies: readonly Dependency[] = [],\n\t\t/** Internal: how `.auth()` carries the authenticator into its copy. */\n\t\tprivate readonly authenticator?: Authenticator,\n\t) {\n\t\tconst canonical = canonicalId(id as string);\n\n\t\tthis.id = canonical as TName;\n\n\t\tthis.keys = {\n\t\t\turl: provideKey(canonical, 'url'),\n\t\t\ttrustedOrigins: provideKey(canonical, 'trustedOrigins'),\n\t\t\tcookieDomain: provideKey(canonical, 'cookieDomain'),\n\t\t};\n\n\t\tthis.logger = config.logger ?? DEFAULT_LOGGER;\n\t\tthis.telescope = config.telescope;\n\t\tthis.envParser = envParserFor(\n\t\t\tconfig.envParser\n\t\t\t\t? { id: canonical, envParser: config.envParser }\n\t\t\t\t: undefined,\n\t\t);\n\n\t\tthis.#endpoints = new EndpointFactory({\n\t\t\tdefaultLogger: this.logger,\n\t\t\t...(config.authorizers\n\t\t\t\t? {\n\t\t\t\t\t\t// With the scheme a built-in name stands for, as the factory's own\n\t\t\t\t\t\t// `.authorizers()` resolves it. Bare names reached the OpenAPI\n\t\t\t\t\t\t// document with no scheme, so a guarded endpoint read as public.\n\t\t\t\t\t\tavailableAuthorizers: config.authorizers.map((name) => {\n\t\t\t\t\t\t\tconst securityScheme = getSecurityScheme(name);\n\t\t\t\t\t\t\treturn securityScheme ? { name, securityScheme } : { name };\n\t\t\t\t\t\t}),\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t\t// `'none'` reaches the factory as no default at all, which is what\n\t\t\t// public means to an endpoint that names no authorizer.\n\t\t\t...(config.defaultAuthorizer && config.defaultAuthorizer !== 'none'\n\t\t\t\t? { defaultAuthorizerName: config.defaultAuthorizer }\n\t\t\t\t: {}),\n\t\t\tsurface: {\n\t\t\t\tid: this.id,\n\t\t\t\tenvParser: this.envParser,\n\t\t\t\t...(authenticator ? { auth: authenticator } : {}),\n\t\t\t},\n\t\t});\n\n\t\tconst endpoints = this.#endpoints;\n\t\tthis.get = endpoints.get.bind(endpoints);\n\t\tthis.post = endpoints.post.bind(endpoints);\n\t\tthis.put = endpoints.put.bind(endpoints);\n\t\tthis.patch = endpoints.patch.bind(endpoints);\n\t\tthis.delete = endpoints.delete.bind(endpoints);\n\t\tthis.options = endpoints.options.bind(endpoints);\n\t\tthis.database = endpoints.database.bind(endpoints);\n\t\tthis.session = endpoints.session.bind(endpoints);\n\t\tthis.auditor = endpoints.auditor.bind(endpoints);\n\t\tthis.actor = endpoints.actor.bind(endpoints);\n\t\tthis.publisher = endpoints.publisher.bind(endpoints);\n\t\tthis.authorizer = endpoints.authorizer.bind(endpoints);\n\t\tthis.authorize = endpoints.authorize.bind(endpoints);\n\t\tthis.rls = endpoints.rls.bind(endpoints);\n\t\tthis.route = endpoints.route.bind(endpoints);\n\t}\n\n\t/**\n\t * This surface's endpoint factory, which every method below starts from.\n\t *\n\t * Private: a surface *is* where endpoints are built, so there is no second\n\t * object to reach through first. `api.database(db)` said the same\n\t * thing as `api.database(db)` with one more word to learn.\n\t */\n\treadonly #endpoints: Endpoints;\n\n\t/** `api.get('/users')` — one route on this surface. */\n\treadonly get: Endpoints['get'];\n\treadonly post: Endpoints['post'];\n\treadonly put: Endpoints['put'];\n\treadonly patch: Endpoints['patch'];\n\treadonly delete: Endpoints['delete'];\n\treadonly options: Endpoints['options'];\n\n\t/**\n\t * A branch for a *group* of endpoints that shares what a single route would\n\t * otherwise repeat — a database, an auditor, a publisher, a session:\n\t *\n\t * ```ts\n\t * export const router = api.database(database);\n\t * export const createUser = router.post('/users').handle(({ db }) => …);\n\t * ```\n\t *\n\t * Each returns a new factory and leaves the surface as it was. That is the\n\t * whole difference from putting the same thing in the surface's config: a\n\t * group opts in, where the surface would grant it to every route it serves —\n\t * a health check handed the database because a profile endpoint needed it.\n\t *\n\t * `dependsOn` is deliberately not here. It is per endpoint, and on the\n\t * surface it would read as the API depending on something, which is what\n\t * `.calls()` and `.auth()` say instead.\n\t */\n\treadonly database: Endpoints['database'];\n\treadonly session: Endpoints['session'];\n\treadonly auditor: Endpoints['auditor'];\n\treadonly actor: Endpoints['actor'];\n\treadonly publisher: Endpoints['publisher'];\n\treadonly authorizer: Endpoints['authorizer'];\n\treadonly authorize: Endpoints['authorize'];\n\treadonly rls: Endpoints['rls'];\n\treadonly route: Endpoints['route'];\n\n\t/**\n\t * What authenticates this surface.\n\t *\n\t * An edge like `.calls()` — the auth server learns this origin, and the two\n\t * share a cookie domain — but a named one, because *who authenticates me* is\n\t * a different fact from *who I happen to call*, and only one of them decides\n\t * what a request is allowed to be. A surface with two of the first is a\n\t * mistake; two of the second is Tuesday.\n\t *\n\t * It declares the id, and keeps the construct: a session callback reads from\n\t * it as `auth` — `session(async ({ auth }) => auth.getSession())` — which\n\t * calls the construct's `verify(headers)`. That is the one thing every\n\t * provider shares, whether the server is mounted in this process, deployed\n\t * beside it, or an OIDC issuer somebody else runs.\n\t */\n\tauth(construct: Authenticator): RestApi<TName, TAuthorizers> {\n\t\treturn new RestApi<TName, TAuthorizers>(\n\t\t\tthis.id as ConstructName<TName>,\n\t\t\tthis.config,\n\t\t\t[...this.dependencies, edgeTo(construct)],\n\t\t\tconstruct,\n\t\t);\n\t}\n\n\t/**\n\t * Other surfaces this one calls.\n\t *\n\t * **Not `.dependsOn()`, and the difference matters.** An endpoint's\n\t * `.dependsOn()` injects a client into that handler and grants it exactly\n\t * what it named. This grants nothing and injects nothing: it records that\n\t * this API calls another surface, which is what puts this API's origin on\n\t * that surface's trusted-origin list.\n\t *\n\t * Spelling it `dependsOn` would invite the thing least privilege forbids —\n\t * every route on the surface receiving whatever the surface named.\n\t *\n\t * Immutable, like every other builder here.\n\t */\n\tcalls(constructs: readonly Declarable[]): RestApi<TName, TAuthorizers> {\n\t\treturn new RestApi<TName, TAuthorizers>(\n\t\t\tthis.id as ConstructName<TName>,\n\t\t\tthis.config,\n\t\t\t[...this.dependencies, ...constructs.map(edgeTo)],\n\t\t\tthis.authenticator,\n\t\t);\n\t}\n\n\t/**\n\t * One surface node, with its endpoints left to the build.\n\t *\n\t * The three provided keys are one fact about this surface and two about its\n\t * callers. Declaring the caller-derived pair here rather than having the\n\t * target invent them is what makes the contract checkable: a target that\n\t * resolves no origins is caught against this list instead of being noticed\n\t * when a browser is refused in production.\n\t */\n\tdeclare(): Declaration[] {\n\t\treturn [\n\t\t\t{\n\t\t\t\tkind: 'rest-api',\n\t\t\t\tid: this.id,\n\t\t\t\tpath: this.config.path,\n\t\t\t\t...(this.config.telescope ? { telescope: true } : {}),\n\t\t\t\t...(this.config.cors ? { cors: this.config.cors } : {}),\n\t\t\t\t...(this.authenticator ? { auth: this.authenticator.id } : {}),\n\t\t\t\t// Filled by the build, which already generates one handler per\n\t\t\t\t// endpoint and knows the path it wrote it to. A surface that\n\t\t\t\t// enumerates its own routes statically — an auth server's single\n\t\t\t\t// wildcard — puts them here instead.\n\t\t\t\tendpoints: [],\n\t\t\t\t...(this.dependencies.length ? { calls: this.dependencies } : {}),\n\t\t\t\t...(this.config.authorizers?.length\n\t\t\t\t\t? { authorizers: this.config.authorizers }\n\t\t\t\t\t: {}),\n\t\t\t\tdefaultAuthorizer: this.config.defaultAuthorizer,\n\t\t\t\tprovides: [\n\t\t\t\t\tthis.keys.url,\n\t\t\t\t\tthis.keys.trustedOrigins,\n\t\t\t\t\tthis.keys.cookieDomain,\n\t\t\t\t],\n\t\t\t},\n\t\t];\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rest-api.mjs","names":[],"sources":["../src/rest-api.ts"],"sourcesContent":["/**\n * `RestApi` — the application's own HTTP surface, declared.\n *\n * Everything an app serves already existed; what did not exist was a *node* for\n * it. Without one, three things that are properties of the graph had to be\n * written down somewhere else: which origins the API accepts, which origins the\n * auth server trusts, and which URL a frontend is built against. All three were\n * being derived by walking the workspace config for ports — a second mechanism,\n * running beside the graph and answering the same question worse, since it can\n * only ever describe what this repo happens to run.\n *\n * With the surface declared, all three become one edge read backwards: a\n * consumer depends on the API, and *that* is the CORS entry, the trusted origin,\n * and the build-time URL. Nothing enumerates its own callers.\n *\n * ```ts\n * export const api = new RestApi('Api', {\n * authorizers: ['session'],\n * defaultAuthorizer: 'session',\n * });\n * ```\n */\n\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { Logger } from '@geekmidas/logger';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport {\n\ttype ConstructName,\n\tcanonicalId,\n\ttype Declaration,\n\ttype Dependency,\n\tprovideKey,\n} from '@geekmidas/manifest';\nimport type { Telescope } from '@geekmidas/telescope';\nimport {\n\ttype Authenticator,\n\ttype Declarable,\n\tedgeTo,\n} from './construct-interface';\n\nexport type { Authenticator } from './construct-interface';\n\nimport {\n\ttype BuiltInSecuritySchemeId,\n\tgetSecurityScheme,\n} from './endpoints/Authorizer';\nimport { EndpointFactory } from './endpoints/EndpointFactory';\nimport { envParserFor } from './endpoints/surfaceEnv';\n\n/** The factory a surface builds its endpoints from. */\ntype Endpoints = EndpointFactory<[], '', Logger>;\n\nexport interface RestApiConfig<\n\t// `readonly []` rather than `readonly string[]`: a surface that declares no\n\t// authorizers of its own should narrow `defaultAuthorizer` to the built-ins\n\t// and `'none'`, not widen it back to any string.\n\tTAuthorizers extends readonly string[] = readonly [],\n> {\n\t/**\n\t * The app that serves this surface, relative to the workspace root —\n\t * `'apps/api'`, or `'.'` in a single-app project.\n\t *\n\t * Required. It was inferred from the id (`apps/<kebab-id>`, or the root\n\t * when no such directory existed), which meant a surface's home was\n\t * whatever happened to be on disk. It is where the app's `package.json`\n\t * and base `tsconfig.json` are, and which build serves this surface. It\n\t * does not change discovery: constructs still load from the workspace's\n\t * `constructs` glob.\n\t */\n\tpath: string;\n\t/**\n\t * CORS tunables. The *origins* are never here — they are read off the\n\t * constructs that declared an edge to this surface, which is the whole point\n\t * of declaring one. These are the parts a graph cannot answer.\n\t */\n\tcors?: {\n\t\tmaxAge?: number;\n\t\tcredentials?: boolean;\n\t\tallowHeaders?: readonly string[];\n\t\texposeHeaders?: readonly string[];\n\t};\n\t/**\n\t * The authorizer names this surface exposes. Names only — what verifies a\n\t * request legitimately differs between local and deployed, so the mechanism\n\t * belongs to the target and never to portable code.\n\t */\n\tauthorizers?: TAuthorizers;\n\t/**\n\t * The authorizer applied to an endpoint that names none.\n\t *\n\t * Required, and `'none'` is a valid answer that has to be typed out. The\n\t * alternative is an API that ships open because a field was left off, which\n\t * is the one default worth refusing to have.\n\t *\n\t * It was called `default`, which said what it was to the type and nothing\n\t * to the reader: next to `authorizers: ['iam']`, a bare `default: 'none'`\n\t * could be defaulting the runtime or the target. The manifest has always\n\t * called it `defaultAuthorizer` and the factory `defaultAuthorizerName`;\n\t * this is the same fact under the same name.\n\t *\n\t * Typed from `authorizers`, so it can only name one this surface exposes —\n\t * plus a built-in scheme, and `'none'`. It was a bare `string`, which let\n\t * `defaultAuthorizer: 'jwtt'` compile and every endpoint on the surface\n\t * default to an authorizer that does not exist.\n\t */\n\tdefaultAuthorizer: TAuthorizers[number] | BuiltInSecuritySchemeId | 'none';\n\t/**\n\t * The logger every endpoint on this surface runs with.\n\t *\n\t * The actual logger, not a path to one. It used to be named in config as\n\t * `logger: './config/logger'`, because the build wrote an import into each\n\t * generated handler and a generator can print a specifier but not an object.\n\t * Endpoints built from the surface carry it, so there is nothing to print\n\t * and nothing to keep in step with a moved file.\n\t */\n\tlogger?: Logger;\n\t/**\n\t * The environment parser every endpoint on this surface runs with.\n\t *\n\t * Same reason as `logger`, and the same field it replaces —\n\t * `envParser: './config/env#envParser'`, a module path with an export name\n\t * appended, parsed by us, checked by nothing.\n\t */\n\tenvParser?: EnvironmentParser<{}>;\n\t/**\n\t * The Telescope instance this surface's entry mounts.\n\t *\n\t * The actual instance, for the reason `logger` is: it used to be named as\n\t * `telescope: './config/telescope#telescope'`, a module path the build\n\t * printed an import for. The entry already imports the surface.\n\t */\n\ttelescope?: Telescope;\n}\n\n/**\n * What is *not* here, and why.\n *\n * `dependsOn`, `database`, `auditor` and `.event(topic, …)` all inject a capability\n * into a handler. Putting one on the surface hands it to every route the\n * surface serves — a health check gets the database because a report needed it\n * — which is the thing least privilege forbids and the reason `.calls()` is not\n * spelled `dependsOn`. They belong to the endpoint, or to a branch —\n * `api.database(db)` — for a group of endpoints that genuinely share them. A\n * branch is a new factory; the surface config stays free of grants.\n *\n * `auth` is not here either: `.auth(construct)` already declares it, and a\n * second way to say the same thing is the duplication this whole model removes.\n *\n * What is left is what the *process* is rather than what a route may reach.\n */\n\nexport class RestApi<\n\tTName extends string = string,\n\tconst TAuthorizers extends readonly string[] = readonly [],\n> implements Declarable<TName>\n{\n\treadonly id: TName;\n\n\t/**\n\t * Declared once and read by both `declare()` and anything that consumes this\n\t * surface, so what the target publishes and what a consumer reads cannot\n\t * drift.\n\t */\n\treadonly keys: {\n\t\turl: string;\n\t\ttrustedOrigins: string;\n\t\tcookieDomain: string;\n\t};\n\n\t/**\n\t * The logger every endpoint on this surface runs with.\n\t *\n\t * Always defined — the surface's own if it was given one, the console\n\t * logger otherwise — so nothing downstream has to decide what to do about a\n\t * missing one. The generated entry reads it from here.\n\t */\n\treadonly logger: Logger;\n\n\t/**\n\t * The environment parser every endpoint on this surface runs with.\n\t *\n\t * Always defined, and normally the default: `process.env` merged with the\n\t * credentials `gkm dev`/`gkm exec` injected. That is what every\n\t * application's own `config/env.ts` was — boilerplate identical in every\n\t * project, and mandatory, which is a poor combination.\n\t */\n\treadonly envParser: EnvironmentParser<{}>;\n\n\t/** The Telescope instance, when this surface was given one. */\n\treadonly telescope?: Telescope;\n\n\tconstructor(\n\t\tid: ConstructName<TName>,\n\t\tprivate readonly config: RestApiConfig<TAuthorizers>,\n\t\t/** Internal: how `.calls()` carries edges into the copy it returns. */\n\t\tprivate readonly dependencies: readonly Dependency[] = [],\n\t\t/** Internal: how `.auth()` carries the authenticator into its copy. */\n\t\tprivate readonly authenticator?: Authenticator,\n\t) {\n\t\tconst canonical = canonicalId(id as string);\n\n\t\tthis.id = canonical as TName;\n\n\t\tthis.keys = {\n\t\t\turl: provideKey(canonical, 'url'),\n\t\t\ttrustedOrigins: provideKey(canonical, 'trustedOrigins'),\n\t\t\tcookieDomain: provideKey(canonical, 'cookieDomain'),\n\t\t};\n\n\t\tthis.logger = config.logger ?? DEFAULT_LOGGER;\n\t\tthis.telescope = config.telescope;\n\t\tthis.envParser = envParserFor(\n\t\t\tconfig.envParser\n\t\t\t\t? { id: canonical, envParser: config.envParser }\n\t\t\t\t: undefined,\n\t\t);\n\n\t\tthis.#endpoints = new EndpointFactory({\n\t\t\tdefaultLogger: this.logger,\n\t\t\t...(config.authorizers\n\t\t\t\t? {\n\t\t\t\t\t\t// With the scheme a built-in name stands for, as the factory's own\n\t\t\t\t\t\t// `.authorizers()` resolves it. Bare names reached the OpenAPI\n\t\t\t\t\t\t// document with no scheme, so a guarded endpoint read as public.\n\t\t\t\t\t\tavailableAuthorizers: config.authorizers.map((name) => {\n\t\t\t\t\t\t\tconst securityScheme = getSecurityScheme(name);\n\t\t\t\t\t\t\treturn securityScheme ? { name, securityScheme } : { name };\n\t\t\t\t\t\t}),\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t\t// `'none'` reaches the factory as no default at all, which is what\n\t\t\t// public means to an endpoint that names no authorizer.\n\t\t\t...(config.defaultAuthorizer && config.defaultAuthorizer !== 'none'\n\t\t\t\t? { defaultAuthorizerName: config.defaultAuthorizer }\n\t\t\t\t: {}),\n\t\t\tsurface: {\n\t\t\t\tid: this.id,\n\t\t\t\tenvParser: this.envParser,\n\t\t\t\t...(authenticator ? { auth: authenticator } : {}),\n\t\t\t},\n\t\t});\n\n\t\tconst endpoints = this.#endpoints;\n\t\tthis.get = endpoints.get.bind(endpoints);\n\t\tthis.post = endpoints.post.bind(endpoints);\n\t\tthis.put = endpoints.put.bind(endpoints);\n\t\tthis.patch = endpoints.patch.bind(endpoints);\n\t\tthis.delete = endpoints.delete.bind(endpoints);\n\t\tthis.options = endpoints.options.bind(endpoints);\n\t\tthis.database = endpoints.database.bind(endpoints);\n\t\tthis.session = endpoints.session.bind(endpoints);\n\t\tthis.auditor = endpoints.auditor.bind(endpoints);\n\t\tthis.actor = endpoints.actor.bind(endpoints);\n\t\tthis.authorizer = endpoints.authorizer.bind(endpoints);\n\t\tthis.authorize = endpoints.authorize.bind(endpoints);\n\t\tthis.rls = endpoints.rls.bind(endpoints);\n\t\tthis.route = endpoints.route.bind(endpoints);\n\t}\n\n\t/**\n\t * This surface's endpoint factory, which every method below starts from.\n\t *\n\t * Private: a surface *is* where endpoints are built, so there is no second\n\t * object to reach through first. `api.database(db)` said the same\n\t * thing as `api.database(db)` with one more word to learn.\n\t */\n\treadonly #endpoints: Endpoints;\n\n\t/** `api.get('/users')` — one route on this surface. */\n\treadonly get: Endpoints['get'];\n\treadonly post: Endpoints['post'];\n\treadonly put: Endpoints['put'];\n\treadonly patch: Endpoints['patch'];\n\treadonly delete: Endpoints['delete'];\n\treadonly options: Endpoints['options'];\n\n\t/**\n\t * A branch for a *group* of endpoints that shares what a single route would\n\t * otherwise repeat — a database, an auditor, a publisher, a session:\n\t *\n\t * ```ts\n\t * export const router = api.database(database);\n\t * export const createUser = router.post('/users').handle(({ db }) => …);\n\t * ```\n\t *\n\t * Each returns a new factory and leaves the surface as it was. That is the\n\t * whole difference from putting the same thing in the surface's config: a\n\t * group opts in, where the surface would grant it to every route it serves —\n\t * a health check handed the database because a profile endpoint needed it.\n\t *\n\t * `dependsOn` is deliberately not here. It is per endpoint, and on the\n\t * surface it would read as the API depending on something, which is what\n\t * `.calls()` and `.auth()` say instead.\n\t */\n\treadonly database: Endpoints['database'];\n\treadonly session: Endpoints['session'];\n\treadonly auditor: Endpoints['auditor'];\n\treadonly actor: Endpoints['actor'];\n\treadonly authorizer: Endpoints['authorizer'];\n\treadonly authorize: Endpoints['authorize'];\n\treadonly rls: Endpoints['rls'];\n\treadonly route: Endpoints['route'];\n\n\t/**\n\t * What authenticates this surface.\n\t *\n\t * An edge like `.calls()` — the auth server learns this origin, and the two\n\t * share a cookie domain — but a named one, because *who authenticates me* is\n\t * a different fact from *who I happen to call*, and only one of them decides\n\t * what a request is allowed to be. A surface with two of the first is a\n\t * mistake; two of the second is Tuesday.\n\t *\n\t * It declares the id, and keeps the construct: a session callback reads from\n\t * it as `auth` — `session(async ({ auth }) => auth.getSession())` — which\n\t * calls the construct's `verify(headers)`. That is the one thing every\n\t * provider shares, whether the server is mounted in this process, deployed\n\t * beside it, or an OIDC issuer somebody else runs.\n\t */\n\tauth(construct: Authenticator): RestApi<TName, TAuthorizers> {\n\t\treturn new RestApi<TName, TAuthorizers>(\n\t\t\tthis.id as ConstructName<TName>,\n\t\t\tthis.config,\n\t\t\t[...this.dependencies, edgeTo(construct)],\n\t\t\tconstruct,\n\t\t);\n\t}\n\n\t/**\n\t * Other surfaces this one calls.\n\t *\n\t * **Not `.dependsOn()`, and the difference matters.** An endpoint's\n\t * `.dependsOn()` injects a client into that handler and grants it exactly\n\t * what it named. This grants nothing and injects nothing: it records that\n\t * this API calls another surface, which is what puts this API's origin on\n\t * that surface's trusted-origin list.\n\t *\n\t * Spelling it `dependsOn` would invite the thing least privilege forbids —\n\t * every route on the surface receiving whatever the surface named.\n\t *\n\t * Immutable, like every other builder here.\n\t */\n\tcalls(constructs: readonly Declarable[]): RestApi<TName, TAuthorizers> {\n\t\treturn new RestApi<TName, TAuthorizers>(\n\t\t\tthis.id as ConstructName<TName>,\n\t\t\tthis.config,\n\t\t\t[...this.dependencies, ...constructs.map(edgeTo)],\n\t\t\tthis.authenticator,\n\t\t);\n\t}\n\n\t/**\n\t * One surface node, with its endpoints left to the build.\n\t *\n\t * The three provided keys are one fact about this surface and two about its\n\t * callers. Declaring the caller-derived pair here rather than having the\n\t * target invent them is what makes the contract checkable: a target that\n\t * resolves no origins is caught against this list instead of being noticed\n\t * when a browser is refused in production.\n\t */\n\tdeclare(): Declaration[] {\n\t\treturn [\n\t\t\t{\n\t\t\t\tkind: 'rest-api',\n\t\t\t\tid: this.id,\n\t\t\t\tpath: this.config.path,\n\t\t\t\t...(this.config.telescope ? { telescope: true } : {}),\n\t\t\t\t...(this.config.cors ? { cors: this.config.cors } : {}),\n\t\t\t\t...(this.authenticator ? { auth: this.authenticator.id } : {}),\n\t\t\t\t// Filled by the build, which already generates one handler per\n\t\t\t\t// endpoint and knows the path it wrote it to. A surface that\n\t\t\t\t// enumerates its own routes statically — an auth server's single\n\t\t\t\t// wildcard — puts them here instead.\n\t\t\t\tendpoints: [],\n\t\t\t\t...(this.dependencies.length ? { calls: this.dependencies } : {}),\n\t\t\t\t...(this.config.authorizers?.length\n\t\t\t\t\t? { authorizers: this.config.authorizers }\n\t\t\t\t\t: {}),\n\t\t\t\tdefaultAuthorizer: this.config.defaultAuthorizer,\n\t\t\t\tprovides: [\n\t\t\t\t\tthis.keys.url,\n\t\t\t\t\tthis.keys.trustedOrigins,\n\t\t\t\t\tthis.keys.cookieDomain,\n\t\t\t\t],\n\t\t\t},\n\t\t];\n\t}\n}\n"],"mappings":"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"}
|
package/dist/site.d.cts
CHANGED
package/dist/site.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_SubscriberBuilder = require("../SubscriberBuilder-
|
|
2
|
+
const require_SubscriberBuilder = require("../SubscriberBuilder-Duk028pD.cjs");
|
|
3
3
|
exports.Subscriber = require_SubscriberBuilder.Subscriber;
|
|
4
4
|
exports.SubscriberBuilder = require_SubscriberBuilder.SubscriberBuilder;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as Subscriber } from "../Subscriber-
|
|
2
|
-
import { t as SubscriberBuilder } from "../SubscriberBuilder-
|
|
1
|
+
import { t as Subscriber } from "../Subscriber-B91YSkZt.cjs";
|
|
2
|
+
import { t as SubscriberBuilder } from "../SubscriberBuilder-BSpQX1JT.cjs";
|
|
3
3
|
export { Subscriber, SubscriberBuilder };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as Subscriber } from "../Subscriber-
|
|
2
|
-
import { t as SubscriberBuilder } from "../SubscriberBuilder-
|
|
1
|
+
import { t as Subscriber } from "../Subscriber-BjHYe5hK.mjs";
|
|
2
|
+
import { t as SubscriberBuilder } from "../SubscriberBuilder-BhMphk4z.mjs";
|
|
3
3
|
export { Subscriber, SubscriberBuilder };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as Subscriber, t as SubscriberBuilder } from "../SubscriberBuilder-
|
|
1
|
+
import { n as Subscriber, t as SubscriberBuilder } from "../SubscriberBuilder-DzQUWE08.mjs";
|
|
2
2
|
export { Subscriber, SubscriberBuilder };
|
package/dist/topic/index.cjs
CHANGED
|
@@ -1,38 +1,3 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
3
|
-
const require_Topic = require("../Topic-D5rlM9wS.cjs");
|
|
4
|
-
let _geekmidas_logger_console = require("@geekmidas/logger/console");
|
|
5
|
-
//#region src/topic/TopicBuilder.ts
|
|
6
|
-
/**
|
|
7
|
-
* Builds a {@link Topic} — `t.topic('users').events({ 'user.created': schema, … })`.
|
|
8
|
-
* The event map is the topic's contract: it types the derived `topic.publisher`
|
|
9
|
-
* and the subscribers that bind via `s.topic(topic)`.
|
|
10
|
-
*/
|
|
11
|
-
var TopicBuilder = class {
|
|
12
|
-
_name;
|
|
13
|
-
_logger = _geekmidas_logger_console.DEFAULT_LOGGER;
|
|
14
|
-
/** The topic name — drives the infra topic and its `<NAME>_*` env vars. */
|
|
15
|
-
topic(name) {
|
|
16
|
-
return require_clone.cloneWith(this, { _name: name });
|
|
17
|
-
}
|
|
18
|
-
logger(logger) {
|
|
19
|
-
return require_clone.cloneWith(this, { _logger: logger });
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* The event contract — a map of event type → payload schema. Building the
|
|
23
|
-
* topic requires this; it's what types the publisher and subscribers.
|
|
24
|
-
*/
|
|
25
|
-
events(events) {
|
|
26
|
-
if (!this._name) throw new Error("Topic requires a name — call .topic(name) before .events().");
|
|
27
|
-
return new require_Topic.Topic(this._name, events, this._logger);
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
//#endregion
|
|
31
|
-
//#region src/topic/index.ts
|
|
32
|
-
const t = new TopicBuilder();
|
|
33
|
-
//#endregion
|
|
2
|
+
const require_Topic = require("../Topic-BBVXC3H4.cjs");
|
|
34
3
|
exports.Topic = require_Topic.Topic;
|
|
35
|
-
exports.TopicBuilder = TopicBuilder;
|
|
36
|
-
exports.t = t;
|
|
37
|
-
|
|
38
|
-
//# sourceMappingURL=index.cjs.map
|
package/dist/topic/index.d.cts
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export { Topic, TopicBuilder, type TopicEvents, type TopicMessage, t };
|
|
1
|
+
import { c as Topic, d as TopicOptions, l as TopicEvents, u as TopicMessage } from "../Construct-BQALFCh2.cjs";
|
|
2
|
+
export { Topic, type TopicEvents, type TopicMessage, type TopicOptions };
|
package/dist/topic/index.d.mts
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export { Topic, TopicBuilder, type TopicEvents, type TopicMessage, t };
|
|
1
|
+
import { c as Topic, d as TopicOptions, l as TopicEvents, u as TopicMessage } from "../Construct-QXnl3VqT.mjs";
|
|
2
|
+
export { Topic, type TopicEvents, type TopicMessage, type TopicOptions };
|
package/dist/topic/index.mjs
CHANGED
|
@@ -1,35 +1,2 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
|
|
3
|
-
import { DEFAULT_LOGGER } from "@geekmidas/logger/console";
|
|
4
|
-
//#region src/topic/TopicBuilder.ts
|
|
5
|
-
/**
|
|
6
|
-
* Builds a {@link Topic} — `t.topic('users').events({ 'user.created': schema, … })`.
|
|
7
|
-
* The event map is the topic's contract: it types the derived `topic.publisher`
|
|
8
|
-
* and the subscribers that bind via `s.topic(topic)`.
|
|
9
|
-
*/
|
|
10
|
-
var TopicBuilder = class {
|
|
11
|
-
_name;
|
|
12
|
-
_logger = DEFAULT_LOGGER;
|
|
13
|
-
/** The topic name — drives the infra topic and its `<NAME>_*` env vars. */
|
|
14
|
-
topic(name) {
|
|
15
|
-
return cloneWith(this, { _name: name });
|
|
16
|
-
}
|
|
17
|
-
logger(logger) {
|
|
18
|
-
return cloneWith(this, { _logger: logger });
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* The event contract — a map of event type → payload schema. Building the
|
|
22
|
-
* topic requires this; it's what types the publisher and subscribers.
|
|
23
|
-
*/
|
|
24
|
-
events(events) {
|
|
25
|
-
if (!this._name) throw new Error("Topic requires a name — call .topic(name) before .events().");
|
|
26
|
-
return new Topic(this._name, events, this._logger);
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
//#endregion
|
|
30
|
-
//#region src/topic/index.ts
|
|
31
|
-
const t = new TopicBuilder();
|
|
32
|
-
//#endregion
|
|
33
|
-
export { Topic, TopicBuilder, t };
|
|
34
|
-
|
|
35
|
-
//# sourceMappingURL=index.mjs.map
|
|
1
|
+
import { t as Topic } from "../Topic-DOw051ch.mjs";
|
|
2
|
+
export { Topic };
|
package/dist/worker.cjs
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_construct_interface = require("./construct-interface.cjs");
|
|
3
|
-
const require_functions = require("./functions-
|
|
3
|
+
const require_functions = require("./functions-ZH60FE7H.cjs");
|
|
4
4
|
const require_surfaceEnv = require("./surfaceEnv-CNimK76p.cjs");
|
|
5
|
-
const require_CronBuilder = require("./CronBuilder-
|
|
6
|
-
const
|
|
5
|
+
const require_CronBuilder = require("./CronBuilder-DB35T5dL.cjs");
|
|
6
|
+
const require_QueueBuilder = require("./QueueBuilder-CJNcv4dy.cjs");
|
|
7
|
+
const require_SubscriberBuilder = require("./SubscriberBuilder-Duk028pD.cjs");
|
|
7
8
|
let _geekmidas_manifest = require("@geekmidas/manifest");
|
|
8
9
|
let _geekmidas_logger_console = require("@geekmidas/logger/console");
|
|
9
10
|
//#region src/worker.ts
|
|
@@ -64,12 +65,31 @@ var Worker = class Worker {
|
|
|
64
65
|
return this.own(new require_CronBuilder.CronBuilder().logger(this.logger)).schedule(schedule);
|
|
65
66
|
}
|
|
66
67
|
/**
|
|
68
|
+
* A queue and its one consumer, run by this worker.
|
|
69
|
+
*
|
|
70
|
+
* One construct, because a queue has exactly one consumer: nothing else can
|
|
71
|
+
* attach a second, or forget to attach the first. Producers send to it by
|
|
72
|
+
* depending on it — `.dependsOn([emails])` makes `services.emails` the
|
|
73
|
+
* publisher.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```ts
|
|
77
|
+
* export const emails = worker
|
|
78
|
+
* .queue('Emails')
|
|
79
|
+
* .message(z.object({ userId: z.uuid(), template: z.string() }))
|
|
80
|
+
* .handle(async ({ messages, services }) => { … });
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
queue(name) {
|
|
84
|
+
return this.own(new require_QueueBuilder.QueueBuilder().logger(this.logger)).queue(name);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
67
87
|
* A runnable that consumes a topic.
|
|
68
88
|
*
|
|
69
89
|
* Binding is not publishing: a consumer is handed the topic's event types
|
|
70
|
-
* and no connection string it could publish with.
|
|
71
|
-
*
|
|
72
|
-
* one.
|
|
90
|
+
* and no connection string it could publish with. A handler that emits
|
|
91
|
+
* follow-up events depends on the topic it emits to —
|
|
92
|
+
* `.dependsOn([orders])` — which is the only reason it would need one.
|
|
73
93
|
*
|
|
74
94
|
* @example `worker.topic(users).subscribe(['user.created']).handle(…)`
|
|
75
95
|
*/
|
|
@@ -77,17 +97,6 @@ var Worker = class Worker {
|
|
|
77
97
|
return this.own(new require_SubscriberBuilder.SubscriberBuilder().logger(this.logger)).topic(topic);
|
|
78
98
|
}
|
|
79
99
|
/**
|
|
80
|
-
* A runnable that consumes events typed from a publisher service.
|
|
81
|
-
*
|
|
82
|
-
* The older of the two ways to bind a subscriber, kept for a project whose
|
|
83
|
-
* events come from a hand-written publisher rather than a `Topic`
|
|
84
|
-
* construct. `topic()` is the one to reach for: it types the events the same
|
|
85
|
-
* way and hands the consumer no connection string it could publish with.
|
|
86
|
-
*/
|
|
87
|
-
publisher(service) {
|
|
88
|
-
return this.own(new require_SubscriberBuilder.SubscriberBuilder().logger(this.logger)).publisher(service);
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
100
|
* Everything below builds a function — the runnable with no schedule and no
|
|
92
101
|
* topic, invoked by something else.
|
|
93
102
|
*
|
package/dist/worker.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.cjs","names":["canonicalId","DEFAULT_LOGGER","envParserFor","edgeTo","CronBuilder","SubscriberBuilder","FunctionBuilder"],"sources":["../src/worker.ts"],"sourcesContent":["/**\n * A process with no port.\n *\n * `RestApi` is the process that answers HTTP. This is the one that does not —\n * the thing that runs a schedule, drains a queue, consumes a topic. Until it\n * existed, those had nowhere to say they belonged: a cron declared a schedule\n * and nothing else, and which container ran it was decided by the directory the\n * file sat in. A glob owned it.\n *\n * The cost of that shows up in the scaffolds. `gkm init --template worker` —\n * a project that is background jobs and nothing else — declared\n * `new RestApi('Api', { defaultAuthorizer: 'none', logger })`: an HTTP surface\n * with no HTTP on it, written because it was the only construct that made an\n * app exist and the only way to get a logger into the generated runtime.\n *\n * It takes no authorizer, and that is the tell that these are two constructs\n * rather than one with a flag: a `RestApi` needs one because an HTTP surface\n * can ship open by omission, and nothing reaches a worker from outside.\n *\n * ## Crons on a server target need a database\n *\n * A cron becomes an EventBridge rule on AWS and nothing here is consulted. A\n * server has nothing firing its crons but itself, and a process that schedules\n * in memory fires every job once per replica while reporting success — so the\n * schedule is kept in Postgres, and the worker says which one with\n * `.database(db)`.\n *\n * A worker with crons and no `.database(…)` schedules nothing on a server\n * target and says so at startup. That is a limitation of workers today rather\n * than a design: the store could be a cache, or something the target\n * provisions, and Postgres is what exists.\n *\n * It is not an app. A worker names the process that runs its crons and\n * subscribers, and that process is the app's server — the same one the\n * endpoints run in, minus the HTTP surface. Giving a worker a container of its\n * own made a second thing to build, deploy and keep alive for work that was\n * always going to run somewhere already. Declare as many as the shape of the\n * work wants: they are groupings, not deployments.\n *\n * @example\n * ```ts\n * // constructs/worker.ts\n * import { Worker } from '@geekmidas/constructs/worker';\n * import { logger } from './logger.js';\n *\n * export const worker = new Worker('Worker', { logger }).calls([database]);\n *\n * // crons/cleanup.ts\n * import { worker } from '@acme/constructs/worker.js';\n *\n * export const cleanup = worker\n * .cron('rate(1 day)')\n * .handle(async ({ logger }) => { … });\n * ```\n */\n\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport {\n\ttype ConstructName,\n\tcanonicalId,\n\ttype Declaration,\n\ttype Dependency,\n} from '@geekmidas/manifest';\nimport type { ComposableStandardSchema } from '@geekmidas/schema';\nimport type { Service } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport {\n\ttype Consumable,\n\ttype Declarable,\n\tedgeTo,\n} from './construct-interface';\nimport type { ScheduleExpression } from './crons/Cron';\nimport { CronBuilder } from './crons/CronBuilder';\nimport { envParserFor } from './endpoints/surfaceEnv';\nimport { FunctionBuilder } from './functions/FunctionBuilder';\nimport { SubscriberBuilder } from './subscribers/SubscriberBuilder';\nimport type { Topic, TopicEvents } from './topic/Topic';\n\nexport interface WorkerConfig {\n\t/**\n\t * The logger everything built from this worker runs with.\n\t *\n\t * The actual logger, not a path to one — the same reason `RestApi` takes\n\t * one. Every cron and subscriber in a scaffolded project used to open with\n\t * `import { logger } from '…/constructs/logger.js'`, which is the line this\n\t * removes.\n\t */\n\tlogger?: Logger;\n\t/**\n\t * The environment parser everything built from this worker runs with.\n\t *\n\t * Defaults to `process.env` merged with the credentials `gkm dev` injected,\n\t * which is what an application's own `config/env.ts` always was.\n\t */\n\tenvParser?: EnvironmentParser<{}>;\n}\n\nexport class Worker<TName extends string = string>\n\timplements Declarable<TName>\n{\n\treadonly id: TName;\n\n\t/** Always defined — the worker's own, or the console logger. */\n\treadonly logger: Logger;\n\n\t/** Always defined, and normally the default. */\n\treadonly envParser: EnvironmentParser<{}>;\n\n\t/**\n\t * Where this worker's schedules live, when it has crons and a server runs\n\t * them. `undefined` until `.database(…)` says.\n\t */\n\treadonly scheduleStore?: Consumable<string, unknown>;\n\n\tconstructor(\n\t\tid: ConstructName<TName>,\n\t\tprivate readonly config: WorkerConfig = {},\n\t\t/** Internal: how `.calls()` carries edges into the copy it returns. */\n\t\tprivate readonly dependencies: readonly Dependency[] = [],\n\t\tscheduleStore?: Consumable<string, unknown>,\n\t) {\n\t\tconst canonical = canonicalId(id as string);\n\n\t\tthis.id = canonical as TName;\n\t\tthis.logger = config.logger ?? DEFAULT_LOGGER;\n\t\tthis.envParser = envParserFor(\n\t\t\tconfig.envParser\n\t\t\t\t? { id: canonical, envParser: config.envParser }\n\t\t\t\t: undefined,\n\t\t);\n\t\tthis.scheduleStore = scheduleStore;\n\t}\n\n\t/**\n\t * The database this worker keeps its schedules in.\n\t *\n\t * Only a server target uses it. On AWS a cron is an EventBridge rule\n\t * invoking a Lambda and nothing here is consulted — but a server has nothing\n\t * firing its crons except itself, and a process that schedules in memory\n\t * fires every job once per replica while reporting that all is well.\n\t *\n\t * So the store is declared rather than discovered. It could have been\n\t * inferred — from a database the app happens to declare, or from the events\n\t * backend when that backend is pg-boss — and both would work until an app\n\t * had two databases, or moved its events to SNS, at which point the\n\t * schedules would quietly move or stop. Naming it is one line and cannot\n\t * drift.\n\t *\n\t * @example\n\t * ```ts\n\t * export const jobs = new Worker('Jobs', { logger }).database(database);\n\t * ```\n\t */\n\tdatabase<T, TDbName extends string>(\n\t\tsource: Consumable<TDbName, T>,\n\t): Worker<TName> {\n\t\treturn new Worker<TName>(\n\t\t\tthis.id as ConstructName<TName>,\n\t\t\tthis.config,\n\t\t\t[...this.dependencies, edgeTo(source)],\n\t\t\tsource as unknown as Consumable<string, unknown>,\n\t\t);\n\t}\n\n\t/**\n\t * A scheduled runnable.\n\t *\n\t * @example `worker.cron('rate(1 day)').handle(async ({ logger }) => { … })`\n\t */\n\tcron(schedule: ScheduleExpression) {\n\t\treturn this.own(new CronBuilder().logger(this.logger)).schedule(schedule);\n\t}\n\n\t/**\n\t * A runnable that consumes a topic.\n\t *\n\t * Binding is not publishing: a consumer is handed the topic's event types\n\t * and no connection string it could publish with. Chain `.publishes(…)` when\n\t * the handler emits follow-up events, which is the only reason it would need\n\t * one.\n\t *\n\t * @example `worker.topic(users).subscribe(['user.created']).handle(…)`\n\t */\n\ttopic<TTopicName extends string, TEvents extends TopicEvents>(\n\t\ttopic: Topic<TTopicName, TEvents>,\n\t) {\n\t\treturn this.own(new SubscriberBuilder().logger(this.logger)).topic(topic);\n\t}\n\n\t/**\n\t * A runnable that consumes events typed from a publisher service.\n\t *\n\t * The older of the two ways to bind a subscriber, kept for a project whose\n\t * events come from a hand-written publisher rather than a `Topic`\n\t * construct. `topic()` is the one to reach for: it types the events the same\n\t * way and hands the consumer no connection string it could publish with.\n\t */\n\tpublisher<T extends EventPublisher<any>, TPubName extends string>(\n\t\tservice: Service<TPubName, T> | Consumable<TPubName, T>,\n\t) {\n\t\treturn this.own(new SubscriberBuilder().logger(this.logger)).publisher(\n\t\t\tservice as never,\n\t\t);\n\t}\n\n\t/**\n\t * Everything below builds a function — the runnable with no schedule and no\n\t * topic, invoked by something else.\n\t *\n\t * These sit on the worker rather than behind a `functions` namespace because\n\t * the namespace was a hop that said nothing: `worker.functions.input(…)`\n\t * names a collection to reach one member of it. What kind of runnable is\n\t * being built is decided by which of these is called first — a schedule\n\t * makes a cron, a topic makes a subscriber, and anything else makes a\n\t * function.\n\t */\n\tinput<T extends ComposableStandardSchema>(schema: T) {\n\t\treturn this.functions.input(schema);\n\t}\n\n\toutput<T extends StandardSchemaV1>(schema: T) {\n\t\treturn this.functions.output(schema);\n\t}\n\n\ttimeout(ms: number) {\n\t\treturn this.functions.timeout(ms);\n\t}\n\n\tmemorySize(mb: number) {\n\t\treturn this.functions.memorySize(mb);\n\t}\n\n\tdependsOn<const T extends readonly Consumable[]>(constructs: T) {\n\t\treturn this.functions.dependsOn(constructs);\n\t}\n\n\tservices<T extends Service[]>(services: T) {\n\t\treturn this.functions.services(services);\n\t}\n\n\t/**\n\t * A function with no input schema — the shortest runnable there is.\n\t *\n\t * @example `worker.handle(async ({ logger }) => { … })`\n\t */\n\thandle(\n\t\t...args: Parameters<\n\t\t\tReturnType<Worker<TName>['schemalessFunction']>['handle']\n\t\t>\n\t) {\n\t\treturn this.schemalessFunction().handle(...args);\n\t}\n\n\t/** The builder `handle` delegates to, named so its type can be referred to. */\n\tprivate schemalessFunction() {\n\t\treturn this.functions;\n\t}\n\n\t/** The function builder the delegating methods above are built on. */\n\tprivate get functions() {\n\t\treturn this.own(new FunctionBuilder().logger(this.logger));\n\t}\n\n\t/**\n\t * Resources and surfaces this worker calls.\n\t *\n\t * Not `.dependsOn()`: this records the edge that derives the worker's\n\t * environment and its access, the same distinction `RestApi.calls()` draws.\n\t */\n\tcalls(constructs: readonly Declarable[]): Worker<TName> {\n\t\treturn new Worker<TName>(\n\t\t\tthis.id as ConstructName<TName>,\n\t\t\tthis.config,\n\t\t\t[...this.dependencies, ...constructs.map(edgeTo)],\n\t\t\tthis.scheduleStore,\n\t\t);\n\t}\n\n\t/**\n\t * Stamps this worker's id onto a builder, so everything built from it says\n\t * which process runs it.\n\t */\n\tprivate own<T extends { _owner?: string; _scheduleStore?: unknown }>(\n\t\tbuilder: T,\n\t): T {\n\t\tbuilder._owner = this.id;\n\t\tbuilder._scheduleStore = this.scheduleStore;\n\n\t\treturn builder;\n\t}\n\n\tdeclare(): Declaration[] {\n\t\treturn [\n\t\t\t{\n\t\t\t\tkind: 'worker',\n\t\t\t\tid: this.id,\n\t\t\t\t...(this.dependencies.length\n\t\t\t\t\t? { dependencies: this.dependencies }\n\t\t\t\t\t: {}),\n\t\t\t\t// Nothing calls a worker, so it publishes no address.\n\t\t\t\tprovides: [],\n\t\t\t},\n\t\t];\n\t}\n}\n"],"mappings":";;;;;;;;;AAoGA,IAAa,SAAb,MAAa,OAEb;CAiBmB;CAEA;CAlBlB;;CAGA;;CAGA;;;;;CAMA;CAEA,YACC,IACA,SAAwC,CAAC,GAEzC,eAAuD,CAAC,GACxD,eACC;EAJgB,KAAA,SAAA;EAEA,KAAA,eAAA;EAGjB,MAAM,aAAA,GAAYA,oBAAAA,YAAAA,CAAY,EAAY;EAE1C,KAAK,KAAK;EACV,KAAK,SAAS,OAAO,UAAUC,0BAAAA;EAC/B,KAAK,YAAYC,mBAAAA,aAChB,OAAO,YACJ;GAAE,IAAI;GAAW,WAAW,OAAO;EAAU,IAC7C,KAAA,CACJ;EACA,KAAK,gBAAgB;CACtB;;;;;;;;;;;;;;;;;;;;;CAsBA,SACC,QACgB;EAChB,OAAO,IAAI,OACV,KAAK,IACL,KAAK,QACL,CAAC,GAAG,KAAK,cAAcC,4BAAAA,OAAO,MAAM,CAAC,GACrC,MACD;CACD;;;;;;CAOA,KAAK,UAA8B;EAClC,OAAO,KAAK,IAAI,IAAIC,oBAAAA,YAAY,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,QAAQ;CACzE;;;;;;;;;;;CAYA,MACC,OACC;EACD,OAAO,KAAK,IAAI,IAAIC,0BAAAA,kBAAkB,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,KAAK;CACzE;;;;;;;;;CAUA,UACC,SACC;EACD,OAAO,KAAK,IAAI,IAAIA,0BAAAA,kBAAkB,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,UAC5D,OACD;CACD;;;;;;;;;;;;CAaA,MAA0C,QAAW;EACpD,OAAO,KAAK,UAAU,MAAM,MAAM;CACnC;CAEA,OAAmC,QAAW;EAC7C,OAAO,KAAK,UAAU,OAAO,MAAM;CACpC;CAEA,QAAQ,IAAY;EACnB,OAAO,KAAK,UAAU,QAAQ,EAAE;CACjC;CAEA,WAAW,IAAY;EACtB,OAAO,KAAK,UAAU,WAAW,EAAE;CACpC;CAEA,UAAiD,YAAe;EAC/D,OAAO,KAAK,UAAU,UAAU,UAAU;CAC3C;CAEA,SAA8B,UAAa;EAC1C,OAAO,KAAK,UAAU,SAAS,QAAQ;CACxC;;;;;;CAOA,OACC,GAAG,MAGF;EACD,OAAO,KAAK,mBAAmB,CAAC,CAAC,OAAO,GAAG,IAAI;CAChD;;CAGA,qBAA6B;EAC5B,OAAO,KAAK;CACb;;CAGA,IAAY,YAAY;EACvB,OAAO,KAAK,IAAI,IAAIC,kBAAAA,gBAAgB,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC;CAC1D;;;;;;;CAQA,MAAM,YAAkD;EACvD,OAAO,IAAI,OACV,KAAK,IACL,KAAK,QACL,CAAC,GAAG,KAAK,cAAc,GAAG,WAAW,IAAIH,4BAAAA,MAAM,CAAC,GAChD,KAAK,aACN;CACD;;;;;CAMA,IACC,SACI;EACJ,QAAQ,SAAS,KAAK;EACtB,QAAQ,iBAAiB,KAAK;EAE9B,OAAO;CACR;CAEA,UAAyB;EACxB,OAAO,CACN;GACC,MAAM;GACN,IAAI,KAAK;GACT,GAAI,KAAK,aAAa,SACnB,EAAE,cAAc,KAAK,aAAa,IAClC,CAAC;GAEJ,UAAU,CAAC;EACZ,CACD;CACD;AACD"}
|
|
1
|
+
{"version":3,"file":"worker.cjs","names":["canonicalId","DEFAULT_LOGGER","envParserFor","edgeTo","CronBuilder","QueueBuilder","SubscriberBuilder","FunctionBuilder"],"sources":["../src/worker.ts"],"sourcesContent":["/**\n * A process with no port.\n *\n * `RestApi` is the process that answers HTTP. This is the one that does not —\n * the thing that runs a schedule, drains a queue, consumes a topic. Until it\n * existed, those had nowhere to say they belonged: a cron declared a schedule\n * and nothing else, and which container ran it was decided by the directory the\n * file sat in. A glob owned it.\n *\n * The cost of that shows up in the scaffolds. `gkm init --template worker` —\n * a project that is background jobs and nothing else — declared\n * `new RestApi('Api', { defaultAuthorizer: 'none', logger })`: an HTTP surface\n * with no HTTP on it, written because it was the only construct that made an\n * app exist and the only way to get a logger into the generated runtime.\n *\n * It takes no authorizer, and that is the tell that these are two constructs\n * rather than one with a flag: a `RestApi` needs one because an HTTP surface\n * can ship open by omission, and nothing reaches a worker from outside.\n *\n * ## Crons on a server target need a database\n *\n * A cron becomes an EventBridge rule on AWS and nothing here is consulted. A\n * server has nothing firing its crons but itself, and a process that schedules\n * in memory fires every job once per replica while reporting success — so the\n * schedule is kept in Postgres, and the worker says which one with\n * `.database(db)`.\n *\n * A worker with crons and no `.database(…)` schedules nothing on a server\n * target and says so at startup. That is a limitation of workers today rather\n * than a design: the store could be a cache, or something the target\n * provisions, and Postgres is what exists.\n *\n * It is not an app. A worker names the process that runs its crons and\n * subscribers, and that process is the app's server — the same one the\n * endpoints run in, minus the HTTP surface. Giving a worker a container of its\n * own made a second thing to build, deploy and keep alive for work that was\n * always going to run somewhere already. Declare as many as the shape of the\n * work wants: they are groupings, not deployments.\n *\n * @example\n * ```ts\n * // constructs/worker.ts\n * import { Worker } from '@geekmidas/constructs/worker';\n * import { logger } from './logger.js';\n *\n * export const worker = new Worker('Worker', { logger }).calls([database]);\n *\n * // crons/cleanup.ts\n * import { worker } from '@acme/constructs/worker.js';\n *\n * export const cleanup = worker\n * .cron('rate(1 day)')\n * .handle(async ({ logger }) => { … });\n * ```\n */\n\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport {\n\ttype ConstructName,\n\tcanonicalId,\n\ttype Declaration,\n\ttype Dependency,\n} from '@geekmidas/manifest';\nimport type { ComposableStandardSchema } from '@geekmidas/schema';\nimport type { Service } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport {\n\ttype Consumable,\n\ttype Declarable,\n\tedgeTo,\n} from './construct-interface';\nimport type { ScheduleExpression } from './crons/Cron';\nimport { CronBuilder } from './crons/CronBuilder';\nimport { envParserFor } from './endpoints/surfaceEnv';\nimport { FunctionBuilder } from './functions/FunctionBuilder';\nimport { QueueBuilder } from './queue/QueueBuilder';\nimport { SubscriberBuilder } from './subscribers/SubscriberBuilder';\nimport type { Topic, TopicEvents } from './topic/Topic';\n\nexport interface WorkerConfig {\n\t/**\n\t * The logger everything built from this worker runs with.\n\t *\n\t * The actual logger, not a path to one — the same reason `RestApi` takes\n\t * one. Every cron and subscriber in a scaffolded project used to open with\n\t * `import { logger } from '…/constructs/logger.js'`, which is the line this\n\t * removes.\n\t */\n\tlogger?: Logger;\n\t/**\n\t * The environment parser everything built from this worker runs with.\n\t *\n\t * Defaults to `process.env` merged with the credentials `gkm dev` injected,\n\t * which is what an application's own `config/env.ts` always was.\n\t */\n\tenvParser?: EnvironmentParser<{}>;\n}\n\nexport class Worker<TName extends string = string>\n\timplements Declarable<TName>\n{\n\treadonly id: TName;\n\n\t/** Always defined — the worker's own, or the console logger. */\n\treadonly logger: Logger;\n\n\t/** Always defined, and normally the default. */\n\treadonly envParser: EnvironmentParser<{}>;\n\n\t/**\n\t * Where this worker's schedules live, when it has crons and a server runs\n\t * them. `undefined` until `.database(…)` says.\n\t */\n\treadonly scheduleStore?: Consumable<string, unknown>;\n\n\tconstructor(\n\t\tid: ConstructName<TName>,\n\t\tprivate readonly config: WorkerConfig = {},\n\t\t/** Internal: how `.calls()` carries edges into the copy it returns. */\n\t\tprivate readonly dependencies: readonly Dependency[] = [],\n\t\tscheduleStore?: Consumable<string, unknown>,\n\t) {\n\t\tconst canonical = canonicalId(id as string);\n\n\t\tthis.id = canonical as TName;\n\t\tthis.logger = config.logger ?? DEFAULT_LOGGER;\n\t\tthis.envParser = envParserFor(\n\t\t\tconfig.envParser\n\t\t\t\t? { id: canonical, envParser: config.envParser }\n\t\t\t\t: undefined,\n\t\t);\n\t\tthis.scheduleStore = scheduleStore;\n\t}\n\n\t/**\n\t * The database this worker keeps its schedules in.\n\t *\n\t * Only a server target uses it. On AWS a cron is an EventBridge rule\n\t * invoking a Lambda and nothing here is consulted — but a server has nothing\n\t * firing its crons except itself, and a process that schedules in memory\n\t * fires every job once per replica while reporting that all is well.\n\t *\n\t * So the store is declared rather than discovered. It could have been\n\t * inferred — from a database the app happens to declare, or from the events\n\t * backend when that backend is pg-boss — and both would work until an app\n\t * had two databases, or moved its events to SNS, at which point the\n\t * schedules would quietly move or stop. Naming it is one line and cannot\n\t * drift.\n\t *\n\t * @example\n\t * ```ts\n\t * export const jobs = new Worker('Jobs', { logger }).database(database);\n\t * ```\n\t */\n\tdatabase<T, TDbName extends string>(\n\t\tsource: Consumable<TDbName, T>,\n\t): Worker<TName> {\n\t\treturn new Worker<TName>(\n\t\t\tthis.id as ConstructName<TName>,\n\t\t\tthis.config,\n\t\t\t[...this.dependencies, edgeTo(source)],\n\t\t\tsource as unknown as Consumable<string, unknown>,\n\t\t);\n\t}\n\n\t/**\n\t * A scheduled runnable.\n\t *\n\t * @example `worker.cron('rate(1 day)').handle(async ({ logger }) => { … })`\n\t */\n\tcron(schedule: ScheduleExpression) {\n\t\treturn this.own(new CronBuilder().logger(this.logger)).schedule(schedule);\n\t}\n\n\t/**\n\t * A queue and its one consumer, run by this worker.\n\t *\n\t * One construct, because a queue has exactly one consumer: nothing else can\n\t * attach a second, or forget to attach the first. Producers send to it by\n\t * depending on it — `.dependsOn([emails])` makes `services.emails` the\n\t * publisher.\n\t *\n\t * @example\n\t * ```ts\n\t * export const emails = worker\n\t * .queue('Emails')\n\t * .message(z.object({ userId: z.uuid(), template: z.string() }))\n\t * .handle(async ({ messages, services }) => { … });\n\t * ```\n\t */\n\tqueue<TQueueName extends string>(name: TQueueName) {\n\t\treturn this.own(new QueueBuilder().logger(this.logger)).queue(name);\n\t}\n\n\t/**\n\t * A runnable that consumes a topic.\n\t *\n\t * Binding is not publishing: a consumer is handed the topic's event types\n\t * and no connection string it could publish with. A handler that emits\n\t * follow-up events depends on the topic it emits to —\n\t * `.dependsOn([orders])` — which is the only reason it would need one.\n\t *\n\t * @example `worker.topic(users).subscribe(['user.created']).handle(…)`\n\t */\n\ttopic<TTopicName extends string, TEvents extends TopicEvents>(\n\t\ttopic: Topic<TTopicName, TEvents>,\n\t) {\n\t\treturn this.own(new SubscriberBuilder().logger(this.logger)).topic(topic);\n\t}\n\n\t/**\n\t * Everything below builds a function — the runnable with no schedule and no\n\t * topic, invoked by something else.\n\t *\n\t * These sit on the worker rather than behind a `functions` namespace because\n\t * the namespace was a hop that said nothing: `worker.functions.input(…)`\n\t * names a collection to reach one member of it. What kind of runnable is\n\t * being built is decided by which of these is called first — a schedule\n\t * makes a cron, a topic makes a subscriber, and anything else makes a\n\t * function.\n\t */\n\tinput<T extends ComposableStandardSchema>(schema: T) {\n\t\treturn this.functions.input(schema);\n\t}\n\n\toutput<T extends StandardSchemaV1>(schema: T) {\n\t\treturn this.functions.output(schema);\n\t}\n\n\ttimeout(ms: number) {\n\t\treturn this.functions.timeout(ms);\n\t}\n\n\tmemorySize(mb: number) {\n\t\treturn this.functions.memorySize(mb);\n\t}\n\n\tdependsOn<const T extends readonly Consumable[]>(constructs: T) {\n\t\treturn this.functions.dependsOn(constructs);\n\t}\n\n\tservices<T extends Service[]>(services: T) {\n\t\treturn this.functions.services(services);\n\t}\n\n\t/**\n\t * A function with no input schema — the shortest runnable there is.\n\t *\n\t * @example `worker.handle(async ({ logger }) => { … })`\n\t */\n\thandle(\n\t\t...args: Parameters<\n\t\t\tReturnType<Worker<TName>['schemalessFunction']>['handle']\n\t\t>\n\t) {\n\t\treturn this.schemalessFunction().handle(...args);\n\t}\n\n\t/** The builder `handle` delegates to, named so its type can be referred to. */\n\tprivate schemalessFunction() {\n\t\treturn this.functions;\n\t}\n\n\t/** The function builder the delegating methods above are built on. */\n\tprivate get functions() {\n\t\treturn this.own(new FunctionBuilder().logger(this.logger));\n\t}\n\n\t/**\n\t * Resources and surfaces this worker calls.\n\t *\n\t * Not `.dependsOn()`: this records the edge that derives the worker's\n\t * environment and its access, the same distinction `RestApi.calls()` draws.\n\t */\n\tcalls(constructs: readonly Declarable[]): Worker<TName> {\n\t\treturn new Worker<TName>(\n\t\t\tthis.id as ConstructName<TName>,\n\t\t\tthis.config,\n\t\t\t[...this.dependencies, ...constructs.map(edgeTo)],\n\t\t\tthis.scheduleStore,\n\t\t);\n\t}\n\n\t/**\n\t * Stamps this worker's id onto a builder, so everything built from it says\n\t * which process runs it.\n\t */\n\tprivate own<T extends { _owner?: string; _scheduleStore?: unknown }>(\n\t\tbuilder: T,\n\t): T {\n\t\tbuilder._owner = this.id;\n\t\tbuilder._scheduleStore = this.scheduleStore;\n\n\t\treturn builder;\n\t}\n\n\tdeclare(): Declaration[] {\n\t\treturn [\n\t\t\t{\n\t\t\t\tkind: 'worker',\n\t\t\t\tid: this.id,\n\t\t\t\t...(this.dependencies.length\n\t\t\t\t\t? { dependencies: this.dependencies }\n\t\t\t\t\t: {}),\n\t\t\t\t// Nothing calls a worker, so it publishes no address.\n\t\t\t\tprovides: [],\n\t\t\t},\n\t\t];\n\t}\n}\n"],"mappings":";;;;;;;;;;AAqGA,IAAa,SAAb,MAAa,OAEb;CAiBmB;CAEA;CAlBlB;;CAGA;;CAGA;;;;;CAMA;CAEA,YACC,IACA,SAAwC,CAAC,GAEzC,eAAuD,CAAC,GACxD,eACC;EAJgB,KAAA,SAAA;EAEA,KAAA,eAAA;EAGjB,MAAM,aAAA,GAAYA,oBAAAA,YAAAA,CAAY,EAAY;EAE1C,KAAK,KAAK;EACV,KAAK,SAAS,OAAO,UAAUC,0BAAAA;EAC/B,KAAK,YAAYC,mBAAAA,aAChB,OAAO,YACJ;GAAE,IAAI;GAAW,WAAW,OAAO;EAAU,IAC7C,KAAA,CACJ;EACA,KAAK,gBAAgB;CACtB;;;;;;;;;;;;;;;;;;;;;CAsBA,SACC,QACgB;EAChB,OAAO,IAAI,OACV,KAAK,IACL,KAAK,QACL,CAAC,GAAG,KAAK,cAAcC,4BAAAA,OAAO,MAAM,CAAC,GACrC,MACD;CACD;;;;;;CAOA,KAAK,UAA8B;EAClC,OAAO,KAAK,IAAI,IAAIC,oBAAAA,YAAY,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,QAAQ;CACzE;;;;;;;;;;;;;;;;;CAkBA,MAAiC,MAAkB;EAClD,OAAO,KAAK,IAAI,IAAIC,qBAAAA,aAAa,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,IAAI;CACnE;;;;;;;;;;;CAYA,MACC,OACC;EACD,OAAO,KAAK,IAAI,IAAIC,0BAAAA,kBAAkB,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,KAAK;CACzE;;;;;;;;;;;;CAaA,MAA0C,QAAW;EACpD,OAAO,KAAK,UAAU,MAAM,MAAM;CACnC;CAEA,OAAmC,QAAW;EAC7C,OAAO,KAAK,UAAU,OAAO,MAAM;CACpC;CAEA,QAAQ,IAAY;EACnB,OAAO,KAAK,UAAU,QAAQ,EAAE;CACjC;CAEA,WAAW,IAAY;EACtB,OAAO,KAAK,UAAU,WAAW,EAAE;CACpC;CAEA,UAAiD,YAAe;EAC/D,OAAO,KAAK,UAAU,UAAU,UAAU;CAC3C;CAEA,SAA8B,UAAa;EAC1C,OAAO,KAAK,UAAU,SAAS,QAAQ;CACxC;;;;;;CAOA,OACC,GAAG,MAGF;EACD,OAAO,KAAK,mBAAmB,CAAC,CAAC,OAAO,GAAG,IAAI;CAChD;;CAGA,qBAA6B;EAC5B,OAAO,KAAK;CACb;;CAGA,IAAY,YAAY;EACvB,OAAO,KAAK,IAAI,IAAIC,kBAAAA,gBAAgB,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC;CAC1D;;;;;;;CAQA,MAAM,YAAkD;EACvD,OAAO,IAAI,OACV,KAAK,IACL,KAAK,QACL,CAAC,GAAG,KAAK,cAAc,GAAG,WAAW,IAAIJ,4BAAAA,MAAM,CAAC,GAChD,KAAK,aACN;CACD;;;;;CAMA,IACC,SACI;EACJ,QAAQ,SAAS,KAAK;EACtB,QAAQ,iBAAiB,KAAK;EAE9B,OAAO;CACR;CAEA,UAAyB;EACxB,OAAO,CACN;GACC,MAAM;GACN,IAAI,KAAK;GACT,GAAI,KAAK,aAAa,SACnB,EAAE,cAAc,KAAK,aAAa,IAClC,CAAC;GAEJ,UAAU,CAAC;EACZ,CACD;CACD;AACD"}
|
package/dist/worker.d.cts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { t as StandardSchemaV1 } from "./index-CeEtxDmK.cjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { i as
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { t as
|
|
8
|
-
import "./
|
|
2
|
+
import { c as Topic, l as TopicEvents, u as TopicMessage } from "./Construct-BQALFCh2.cjs";
|
|
3
|
+
import { a as FunctionHandler, r as Function, t as FunctionBuilder } from "./index-B0kxstwf.cjs";
|
|
4
|
+
import { c as ServicesOf, i as Declarable, r as Consumable } from "./construct-interface-_xi4BPN8.cjs";
|
|
5
|
+
import { i as ScheduleExpression } from "./Cron-CMEGfRs8.cjs";
|
|
6
|
+
import { t as CronBuilder } from "./CronBuilder-CEK7aOqD.cjs";
|
|
7
|
+
import { t as QueueBuilder } from "./QueueBuilder-XEnnOWGe.cjs";
|
|
8
|
+
import { t as SubscriberBuilder } from "./SubscriberBuilder-BSpQX1JT.cjs";
|
|
9
|
+
import "./topic/index.cjs";
|
|
9
10
|
import { EnvironmentParser } from "@geekmidas/envkit";
|
|
10
|
-
import { EventPublisher } from "@geekmidas/events";
|
|
11
11
|
import { Logger } from "@geekmidas/logger";
|
|
12
12
|
import { ComposableStandardSchema } from "@geekmidas/schema";
|
|
13
13
|
import { Service } from "@geekmidas/services";
|
|
@@ -74,27 +74,35 @@ export declare class Worker<TName extends string = string> implements Declarable
|
|
|
74
74
|
*
|
|
75
75
|
* @example `worker.cron('rate(1 day)').handle(async ({ logger }) => { … })`
|
|
76
76
|
*/
|
|
77
|
-
cron(schedule: ScheduleExpression): CronBuilder<ComposableStandardSchema, Service[], Logger, undefined, undefined, string
|
|
77
|
+
cron(schedule: ScheduleExpression): CronBuilder<ComposableStandardSchema, Service[], Logger, undefined, undefined, string>;
|
|
78
78
|
/**
|
|
79
|
-
* A
|
|
79
|
+
* A queue and its one consumer, run by this worker.
|
|
80
80
|
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
81
|
+
* One construct, because a queue has exactly one consumer: nothing else can
|
|
82
|
+
* attach a second, or forget to attach the first. Producers send to it by
|
|
83
|
+
* depending on it — `.dependsOn([emails])` makes `services.emails` the
|
|
84
|
+
* publisher.
|
|
85
85
|
*
|
|
86
|
-
* @example
|
|
86
|
+
* @example
|
|
87
|
+
* ```ts
|
|
88
|
+
* export const emails = worker
|
|
89
|
+
* .queue('Emails')
|
|
90
|
+
* .message(z.object({ userId: z.uuid(), template: z.string() }))
|
|
91
|
+
* .handle(async ({ messages, services }) => { … });
|
|
92
|
+
* ```
|
|
87
93
|
*/
|
|
88
|
-
|
|
94
|
+
queue<TQueueName extends string>(name: TQueueName): QueueBuilder<TQueueName, undefined, [], Logger>;
|
|
89
95
|
/**
|
|
90
|
-
* A runnable that consumes
|
|
96
|
+
* A runnable that consumes a topic.
|
|
91
97
|
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
98
|
+
* Binding is not publishing: a consumer is handed the topic's event types
|
|
99
|
+
* and no connection string it could publish with. A handler that emits
|
|
100
|
+
* follow-up events depends on the topic it emits to —
|
|
101
|
+
* `.dependsOn([orders])` — which is the only reason it would need one.
|
|
102
|
+
*
|
|
103
|
+
* @example `worker.topic(users).subscribe(['user.created']).handle(…)`
|
|
96
104
|
*/
|
|
97
|
-
|
|
105
|
+
topic<TTopicName extends string, TEvents extends TopicEvents>(topic: Topic<TTopicName, TEvents>): SubscriberBuilder<[], Logger, undefined, EventPublisher<TopicMessage<TEvents_1>>, []>;
|
|
98
106
|
/**
|
|
99
107
|
* Everything below builds a function — the runnable with no schedule and no
|
|
100
108
|
* topic, invoked by something else.
|
|
@@ -106,18 +114,18 @@ export declare class Worker<TName extends string = string> implements Declarable
|
|
|
106
114
|
* makes a cron, a topic makes a subscriber, and anything else makes a
|
|
107
115
|
* function.
|
|
108
116
|
*/
|
|
109
|
-
input<T extends ComposableStandardSchema>(schema: T): FunctionBuilder<T, undefined, [], Logger, undefined, string, undefined, string,
|
|
110
|
-
output<T extends StandardSchemaV1>(schema: T): FunctionBuilder<ComposableStandardSchema, T, [], Logger, undefined, string, undefined, string,
|
|
111
|
-
timeout(ms: number): FunctionBuilder<ComposableStandardSchema, undefined, [], Logger, undefined, string, undefined, string,
|
|
112
|
-
memorySize(mb: number): FunctionBuilder<ComposableStandardSchema, undefined, [], Logger, undefined, string, undefined, string,
|
|
113
|
-
dependsOn<const T extends readonly Consumable[]>(constructs: T): FunctionBuilder<ComposableStandardSchema, undefined, [...ServicesOf<T>], Logger, undefined, string, undefined, string,
|
|
114
|
-
services<T extends Service[]>(services: T): FunctionBuilder<ComposableStandardSchema, undefined, [...T], Logger, undefined, string, undefined, string,
|
|
117
|
+
input<T extends ComposableStandardSchema>(schema: T): FunctionBuilder<T, undefined, [], Logger, undefined, string, undefined, string, AuditableAction<string, unknown>>;
|
|
118
|
+
output<T extends StandardSchemaV1>(schema: T): FunctionBuilder<ComposableStandardSchema, T, [], Logger, undefined, string, undefined, string, AuditableAction<string, unknown>>;
|
|
119
|
+
timeout(ms: number): FunctionBuilder<ComposableStandardSchema, undefined, [], Logger, undefined, string, undefined, string, AuditableAction<string, unknown>>;
|
|
120
|
+
memorySize(mb: number): FunctionBuilder<ComposableStandardSchema, undefined, [], Logger, undefined, string, undefined, string, AuditableAction<string, unknown>>;
|
|
121
|
+
dependsOn<const T extends readonly Consumable[]>(constructs: T): FunctionBuilder<ComposableStandardSchema, undefined, [...ServicesOf<T>], Logger, undefined, string, undefined, string, AuditableAction<string, unknown>>;
|
|
122
|
+
services<T extends Service[]>(services: T): FunctionBuilder<ComposableStandardSchema, undefined, [...T], Logger, undefined, string, undefined, string, AuditableAction<string, unknown>>;
|
|
115
123
|
/**
|
|
116
124
|
* A function with no input schema — the shortest runnable there is.
|
|
117
125
|
*
|
|
118
126
|
* @example `worker.handle(async ({ logger }) => { … })`
|
|
119
127
|
*/
|
|
120
|
-
handle(...args: Parameters<ReturnType<Worker<TName>['schemalessFunction']>['handle']>): Function<ComposableStandardSchema, [], Logger, undefined, undefined, string, undefined, string,
|
|
128
|
+
handle(...args: Parameters<ReturnType<Worker<TName>['schemalessFunction']>['handle']>): Function<ComposableStandardSchema, [], Logger, undefined, undefined, string, undefined, string, AuditableAction<string, unknown>, FunctionHandler<ComposableStandardSchema, [], Logger, undefined, undefined, undefined, AuditableAction<string, unknown>>>;
|
|
121
129
|
/** The builder `handle` delegates to, named so its type can be referred to. */
|
|
122
130
|
private schemalessFunction;
|
|
123
131
|
/** The function builder the delegating methods above are built on. */
|
package/dist/worker.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.cts","names":["_$__topic0.TopicMessage","_$__construct_interface0.ServicesOf","_$__functions0.FunctionHandler","_$__functions0.Function"],"sources":["../src/worker.ts"],"mappings":";;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"worker.d.cts","names":["_$__topic0.TopicMessage","_$__construct_interface0.ServicesOf","_$__functions0.FunctionHandler","_$__functions0.Function"],"sources":["../src/worker.ts"],"mappings":";;;;;;;;;;;;;;;iBAkFiB;;;;;;;;;EAShB,SAAS;;;;;;;EAOT,YAAY;;qBAGA,OAAO,0CACR,WAAW;mBAkBJ;;mBAEA;WAlBT,IAAI;;WAGJ,QAAQ;;WAGR,WAAW;;;;;WAMX,gBAAgB;EAEzB,YACC,IAAI,cAAc,QACD,SAAQ,cAER;;EAAA,wBAAuB,cACxC,gBAAgB;;;;;;;;;;;;;;;;;;;;;EAkCjB,SAAS,GAAG,wBACX,QAAQ,WAAW,SAAS,KAC1B,OAAO;;;;;;EAcV,KAAK,UAAU,qBAAkB,YAAA,0BAAA,WAAA;;;;;;;;;;;;;;;;;EAoBjC,MAAM,2BAA2B,MAAM,aAAU,aAAA,2BAAA;;;;;;;;;;;EAcjD,MAAM,2BAA2B,gBAAgB,aAChD,OAAO,MAAM,YAAY,WAAQ,sBAAA,mBAAA,eAAAA,aAAA;;;;;;;;;;;;EAgBlC,MAAM,UAAU,0BAA0B,QAAQ,IAAC,gBAAA,kBAAA,8CAAA;EAInD,OAAO,UAAU,kBAAkB,QAAQ,IAAC,gBAAA,0BAAA,OAAA,8CAAA;EAI5C,QAAQ,aAAU,gBAAA,yCAAA,8CAAA;EAIlB,WAAW,aAAU,gBAAA,yCAAA,8CAAA;EAIrB,gBAAgB,mBAAmB,cAAc,YAAY,IAAC,gBAAA,yCAAAC,WAAA,KAAA,8CAAA;EAI9D,SAAS,UAAU,WAAW,UAAU,IAAC,gBAAA,yCAAA,IAAA,8CAAA;;;;;;EASzC,UACI,MAAM,WACR,WAAW,OAAO,2CAClBE,SAAA,8BAAA,yDAAA,kCAAAD,gBAAA,8BAAA,yCAAA;;UAMM;;cAKI;;;;;;;EAUZ,MAAM,qBAAqB,eAAe,OAAO;;;;;UAazC;EASR,WAAW"}
|