@geekmidas/cli 10.0.0-alpha.3 → 10.0.0-alpha.30
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 +25 -48
- package/bin/adjacent-tsconfig.mjs +97 -0
- package/bin/gkm.mjs +7 -1
- package/dist/{HostingerProvider-h3EbEp-Z.cjs → HostingerProvider-BZFGYYVA.cjs} +21 -22
- package/dist/HostingerProvider-BZFGYYVA.cjs.map +1 -0
- package/dist/{HostingerProvider-Cft7lEYr.mjs → HostingerProvider-GF6NAZAK.mjs} +20 -21
- package/dist/HostingerProvider-GF6NAZAK.mjs.map +1 -0
- package/dist/{LocalStateProvider-Db9SEjyH.cjs → LocalStateProvider-7HR6SS2t.cjs} +11 -6
- package/dist/LocalStateProvider-7HR6SS2t.cjs.map +1 -0
- package/dist/{LocalStateProvider-Bn6tS-oL.mjs → LocalStateProvider-D7QyJoLM.mjs} +10 -4
- package/dist/LocalStateProvider-D7QyJoLM.mjs.map +1 -0
- package/dist/{openapi-c9bpieI5.cjs → OpenApiTsGenerator-D1nhN98m.cjs} +135 -298
- package/dist/OpenApiTsGenerator-D1nhN98m.cjs.map +1 -0
- package/dist/{openapi-CY9xFkTH.mjs → OpenApiTsGenerator-hDINScnz.mjs} +106 -250
- package/dist/OpenApiTsGenerator-hDINScnz.mjs.map +1 -0
- package/dist/{Route53Provider-RdpUDJQk.mjs → Route53Provider-Cvj40A8I.mjs} +12 -12
- package/dist/Route53Provider-Cvj40A8I.mjs.map +1 -0
- package/dist/{Route53Provider-di2fMM5N.cjs → Route53Provider-DVILBsvV.cjs} +20 -21
- package/dist/Route53Provider-DVILBsvV.cjs.map +1 -0
- package/dist/{SSMStateProvider-BRt2py-A.mjs → SSMStateProvider-B0Hu1MKZ.mjs} +13 -4
- package/dist/SSMStateProvider-B0Hu1MKZ.mjs.map +1 -0
- package/dist/{SSMStateProvider-DyX8s9HI.cjs → SSMStateProvider-VrQ-yeqd.cjs} +17 -9
- package/dist/SSMStateProvider-VrQ-yeqd.cjs.map +1 -0
- package/dist/{backup-provisioner-BrsHwYG0.cjs → backup-provisioner-BmhIO_Yr.cjs} +21 -19
- package/dist/backup-provisioner-BmhIO_Yr.cjs.map +1 -0
- package/dist/{backup-provisioner-Bgj5Vi_L.mjs → backup-provisioner-DIVCbfee.mjs} +10 -7
- package/dist/backup-provisioner-DIVCbfee.mjs.map +1 -0
- package/dist/{bundler-BY1OmV1e.cjs → bundler-CYF5mWi9.cjs} +10 -28
- package/dist/bundler-CYF5mWi9.cjs.map +1 -0
- package/dist/{bundler-Be8Zg4Wd.mjs → bundler-D9IPNvDt.mjs} +9 -26
- package/dist/bundler-D9IPNvDt.mjs.map +1 -0
- package/dist/{config-CzWnfcXM.mjs → config-BH3Ki69F.mjs} +49 -89
- package/dist/config-BH3Ki69F.mjs.map +1 -0
- package/dist/{config-BholeD-0.cjs → config-URVD4bTj.cjs} +101 -148
- package/dist/config-URVD4bTj.cjs.map +1 -0
- package/dist/config.cjs +5 -5
- package/dist/config.d.cts +25 -17
- package/dist/config.d.cts.map +1 -1
- package/dist/config.d.mts +25 -17
- package/dist/config.d.mts.map +1 -1
- package/dist/config.mjs +3 -5
- package/dist/deploy/sniffer-envkit-patch.cjs +18 -10
- package/dist/deploy/sniffer-envkit-patch.cjs.map +1 -1
- package/dist/deploy/sniffer-envkit-patch.d.cts +0 -2
- package/dist/deploy/sniffer-envkit-patch.d.cts.map +1 -1
- package/dist/deploy/sniffer-envkit-patch.d.mts +0 -2
- package/dist/deploy/sniffer-envkit-patch.d.mts.map +1 -1
- package/dist/deploy/sniffer-envkit-patch.mjs +10 -2
- package/dist/deploy/sniffer-envkit-patch.mjs.map +1 -1
- package/dist/deploy/sniffer-hooks.cjs +13 -7
- package/dist/deploy/sniffer-hooks.cjs.map +1 -1
- package/dist/deploy/sniffer-hooks.d.cts +1 -2
- package/dist/deploy/sniffer-hooks.d.cts.map +1 -1
- package/dist/deploy/sniffer-hooks.d.mts +1 -2
- package/dist/deploy/sniffer-hooks.d.mts.map +1 -1
- package/dist/deploy/sniffer-hooks.mjs +9 -2
- package/dist/deploy/sniffer-hooks.mjs.map +1 -1
- package/dist/deploy/sniffer-loader.cjs +16 -7
- package/dist/deploy/sniffer-loader.cjs.map +1 -1
- package/dist/deploy/sniffer-loader.d.cts +1 -1
- package/dist/deploy/sniffer-loader.d.mts +1 -1
- package/dist/deploy/sniffer-loader.mjs +13 -2
- package/dist/deploy/sniffer-loader.mjs.map +1 -1
- package/dist/deploy/sniffer-routes-worker.cjs +11 -11
- package/dist/deploy/sniffer-routes-worker.cjs.map +1 -1
- package/dist/deploy/sniffer-routes-worker.d.cts +1 -1
- package/dist/deploy/sniffer-routes-worker.d.mts +1 -1
- package/dist/deploy/sniffer-routes-worker.mjs +3 -3
- package/dist/deploy/sniffer-routes-worker.mjs.map +1 -1
- package/dist/deploy/sniffer-worker.cjs +15 -6
- package/dist/deploy/sniffer-worker.cjs.map +1 -1
- package/dist/deploy/sniffer-worker.d.cts +1 -3
- package/dist/deploy/sniffer-worker.d.cts.map +1 -1
- package/dist/deploy/sniffer-worker.d.mts +1 -3
- package/dist/deploy/sniffer-worker.d.mts.map +1 -1
- package/dist/deploy/sniffer-worker.mjs +15 -4
- package/dist/deploy/sniffer-worker.mjs.map +1 -1
- package/dist/{discover-CAlFgYec.mjs → discover-CMxesskG.mjs} +66 -22
- package/dist/discover-CMxesskG.mjs.map +1 -0
- package/dist/{discover-DTmFeloL.cjs → discover-CroTIm0K.cjs} +117 -66
- package/dist/discover-CroTIm0K.cjs.map +1 -0
- package/dist/{emit-CbcRolsa.cjs → emit-BdoPFjZf.cjs} +53 -36
- package/dist/emit-BdoPFjZf.cjs.map +1 -0
- package/dist/{emit-CiZS_5Ub.mjs → emit-Bzq7JsMP.mjs} +24 -6
- package/dist/emit-Bzq7JsMP.mjs.map +1 -0
- package/dist/github-B2fis34P.cjs +216 -0
- package/dist/github-B2fis34P.cjs.map +1 -0
- package/dist/github-BrDmlm1x.mjs +216 -0
- package/dist/github-BrDmlm1x.mjs.map +1 -0
- package/dist/{index-CHB4tSzX.d.cts → index-p89EzILa.d.mts} +4 -23
- package/dist/index-p89EzILa.d.mts.map +1 -0
- package/dist/{index-BThUzEIp.d.mts → index-rU9u761a.d.cts} +4 -23
- package/dist/index-rU9u761a.d.cts.map +1 -0
- package/dist/index.cjs +6001 -4452
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +5895 -4367
- package/dist/index.mjs.map +1 -1
- package/dist/migrate-BuCx7tIv.cjs +498 -0
- package/dist/migrate-BuCx7tIv.cjs.map +1 -0
- package/dist/migrate-CBICVMAi.mjs +493 -0
- package/dist/migrate-CBICVMAi.mjs.map +1 -0
- package/dist/openapi.cjs +197 -8
- package/dist/openapi.cjs.map +1 -0
- package/dist/openapi.d.cts +47 -14
- package/dist/openapi.d.cts.map +1 -1
- package/dist/openapi.d.mts +47 -14
- package/dist/openapi.d.mts.map +1 -1
- package/dist/openapi.mjs +191 -5
- package/dist/openapi.mjs.map +1 -0
- package/dist/ready-CvP3JVEw.mjs +355 -0
- package/dist/ready-CvP3JVEw.mjs.map +1 -0
- package/dist/ready-DXtNfk7v.cjs +390 -0
- package/dist/ready-DXtNfk7v.cjs.map +1 -0
- package/dist/reconcile/public.cjs +4 -4
- package/dist/reconcile/public.d.cts +27 -26
- package/dist/reconcile/public.d.cts.map +1 -1
- package/dist/reconcile/public.d.mts +27 -26
- package/dist/reconcile/public.d.mts.map +1 -1
- package/dist/reconcile/public.mjs +3 -4
- package/dist/reconcile-BxZTV2vl.cjs +332 -0
- package/dist/reconcile-BxZTV2vl.cjs.map +1 -0
- package/dist/reconcile-yNq6Shau.mjs +279 -0
- package/dist/reconcile-yNq6Shau.mjs.map +1 -0
- package/dist/rolldown-runtime-BhDjJH2R.mjs +15 -0
- package/dist/{chunk-CUT6urMc.cjs → rolldown-runtime-C0BPl7ul.cjs} +23 -10
- package/dist/ssm-BKN1Cg4z.mjs +60 -0
- package/dist/ssm-BKN1Cg4z.mjs.map +1 -0
- package/dist/ssm-FxeSAt35.cjs +60 -0
- package/dist/ssm-FxeSAt35.cjs.map +1 -0
- package/dist/{storage-Bi24WfoW.mjs → storage-BjTLjDDp.mjs} +22 -22
- package/dist/storage-BjTLjDDp.mjs.map +1 -0
- package/dist/{storage-BJmzYxt8.cjs → storage-C2JC31V9.cjs} +65 -84
- package/dist/storage-C2JC31V9.cjs.map +1 -0
- package/dist/types-CzK6hsXP.d.mts +6286 -0
- package/dist/types-CzK6hsXP.d.mts.map +1 -0
- package/dist/types-DOOAevjG.d.cts +6287 -0
- package/dist/types-DOOAevjG.d.cts.map +1 -0
- package/dist/vitest.cjs +61 -0
- package/dist/vitest.cjs.map +1 -0
- package/dist/vitest.d.cts +28 -0
- package/dist/vitest.d.cts.map +1 -0
- package/dist/vitest.d.mts +28 -0
- package/dist/vitest.d.mts.map +1 -0
- package/dist/vitest.mjs +61 -0
- package/dist/vitest.mjs.map +1 -0
- package/dist/workspace/index.cjs +3 -4
- package/dist/workspace/index.d.cts +3 -3
- package/dist/workspace/index.d.mts +3 -3
- package/dist/workspace/index.mjs +2 -3
- package/dist/workspace-Bav3TVrP.cjs +6710 -0
- package/dist/workspace-Bav3TVrP.cjs.map +1 -0
- package/dist/{credentials-DQmbRnr1.cjs → workspace-BhiMkRuA.cjs} +2076 -1963
- package/dist/workspace-BhiMkRuA.cjs.map +1 -0
- package/dist/{credentials-BZcp3CGx.mjs → workspace-C97QUCIf.mjs} +1957 -1837
- package/dist/workspace-C97QUCIf.mjs.map +1 -0
- package/dist/workspace-T4IVGSoB.mjs +6538 -0
- package/dist/workspace-T4IVGSoB.mjs.map +1 -0
- package/package.json +45 -38
- package/dist/CachedStateProvider-BLo7zv7S.cjs +0 -3
- package/dist/CachedStateProvider-BxoAm-B2.mjs +0 -3
- package/dist/CachedStateProvider-CqPdW7CB.cjs +0 -60
- package/dist/CachedStateProvider-CqPdW7CB.cjs.map +0 -1
- package/dist/CachedStateProvider-DTniehMP.mjs +0 -54
- package/dist/CachedStateProvider-DTniehMP.mjs.map +0 -1
- package/dist/HostingerProvider-Cft7lEYr.mjs.map +0 -1
- package/dist/HostingerProvider-h3EbEp-Z.cjs.map +0 -1
- package/dist/LocalStateProvider-Bn6tS-oL.mjs.map +0 -1
- package/dist/LocalStateProvider-Db9SEjyH.cjs.map +0 -1
- package/dist/Route53Provider-RdpUDJQk.mjs.map +0 -1
- package/dist/Route53Provider-di2fMM5N.cjs.map +0 -1
- package/dist/SSMStateProvider-BRt2py-A.mjs.map +0 -1
- package/dist/SSMStateProvider-DyX8s9HI.cjs.map +0 -1
- package/dist/backup-provisioner-Bgj5Vi_L.mjs.map +0 -1
- package/dist/backup-provisioner-BrsHwYG0.cjs.map +0 -1
- package/dist/bundler-BY1OmV1e.cjs.map +0 -1
- package/dist/bundler-Be8Zg4Wd.mjs.map +0 -1
- package/dist/chunk-Duj1WY3L.mjs +0 -7
- package/dist/config-BholeD-0.cjs.map +0 -1
- package/dist/config-CzWnfcXM.mjs.map +0 -1
- package/dist/credentials-B5zRHdAl.mjs +0 -141
- package/dist/credentials-B5zRHdAl.mjs.map +0 -1
- package/dist/credentials-BZcp3CGx.mjs.map +0 -1
- package/dist/credentials-BapzjyV5.cjs +0 -195
- package/dist/credentials-BapzjyV5.cjs.map +0 -1
- package/dist/credentials-DQmbRnr1.cjs.map +0 -1
- package/dist/discover-CAlFgYec.mjs.map +0 -1
- package/dist/discover-DTmFeloL.cjs.map +0 -1
- package/dist/dokploy-api-BS2xnJLZ.mjs +0 -3
- package/dist/dokploy-api-BsVH0y_s.cjs +0 -718
- package/dist/dokploy-api-BsVH0y_s.cjs.map +0 -1
- package/dist/dokploy-api-BuxDRo3l.cjs +0 -3
- package/dist/dokploy-api-aTFPz7_4.mjs +0 -706
- package/dist/dokploy-api-aTFPz7_4.mjs.map +0 -1
- package/dist/emit-CbcRolsa.cjs.map +0 -1
- package/dist/emit-CiZS_5Ub.mjs.map +0 -1
- package/dist/encryption-C6jaU0hE.cjs +0 -54
- package/dist/encryption-C6jaU0hE.cjs.map +0 -1
- package/dist/encryption-CHzzeR8a.cjs +0 -4
- package/dist/encryption-CQUncu7v.mjs +0 -3
- package/dist/encryption-D-abyhZx.mjs +0 -42
- package/dist/encryption-D-abyhZx.mjs.map +0 -1
- package/dist/fullstack-secrets-C-GGohke.cjs +0 -111
- package/dist/fullstack-secrets-C-GGohke.cjs.map +0 -1
- package/dist/fullstack-secrets-D1o9-G5l.mjs +0 -87
- package/dist/fullstack-secrets-D1o9-G5l.mjs.map +0 -1
- package/dist/generator-CNzBw7g5.cjs +0 -289
- package/dist/generator-CNzBw7g5.cjs.map +0 -1
- package/dist/generator-VE2JrFlY.mjs +0 -253
- package/dist/generator-VE2JrFlY.mjs.map +0 -1
- package/dist/index-BThUzEIp.d.mts.map +0 -1
- package/dist/index-CHB4tSzX.d.cts.map +0 -1
- package/dist/openapi-CY9xFkTH.mjs.map +0 -1
- package/dist/openapi-c9bpieI5.cjs.map +0 -1
- package/dist/openapi-react-query-Byltvm_7.mjs +0 -167
- package/dist/openapi-react-query-Byltvm_7.mjs.map +0 -1
- package/dist/openapi-react-query-CmJ1xDbn.cjs +0 -173
- package/dist/openapi-react-query-CmJ1xDbn.cjs.map +0 -1
- package/dist/openapi-react-query.cjs +0 -4
- package/dist/openapi-react-query.d.cts +0 -11
- package/dist/openapi-react-query.d.cts.map +0 -1
- package/dist/openapi-react-query.d.mts +0 -11
- package/dist/openapi-react-query.d.mts.map +0 -1
- package/dist/openapi-react-query.mjs +0 -4
- package/dist/reconcile-CFs8C_1a.mjs +0 -37
- package/dist/reconcile-CFs8C_1a.mjs.map +0 -1
- package/dist/reconcile-D0pimNiy.cjs +0 -37
- package/dist/reconcile-D0pimNiy.cjs.map +0 -1
- package/dist/setup-Bc73t0ve.mjs +0 -443
- package/dist/setup-Bc73t0ve.mjs.map +0 -1
- package/dist/setup-C48C9hto.cjs +0 -473
- package/dist/setup-C48C9hto.cjs.map +0 -1
- package/dist/setup-DCuudnCl.mjs +0 -11
- package/dist/setup-fG_ivuxO.cjs +0 -11
- package/dist/storage-BJmzYxt8.cjs.map +0 -1
- package/dist/storage-BM9hGpgv.cjs +0 -7
- package/dist/storage-Bi24WfoW.mjs.map +0 -1
- package/dist/storage-CYlGwWTj.mjs +0 -3
- package/dist/sync-CmEuNZsB.mjs +0 -76
- package/dist/sync-CmEuNZsB.mjs.map +0 -1
- package/dist/sync-D306yOWu.cjs +0 -4
- package/dist/sync-SizaChmC.cjs +0 -93
- package/dist/sync-SizaChmC.cjs.map +0 -1
- package/dist/sync-hOqAWHR6.mjs +0 -3
- package/dist/types-C-arnQ1r.d.mts +0 -2184
- package/dist/types-C-arnQ1r.d.mts.map +0 -1
- package/dist/types-abJQuk7m.d.cts +0 -2184
- package/dist/types-abJQuk7m.d.cts.map +0 -1
- package/dist/workspace-7FqprxuJ.mjs +0 -4323
- package/dist/workspace-7FqprxuJ.mjs.map +0 -1
- package/dist/workspace-BO_KWohI.cjs +0 -9
- package/dist/workspace-DK4WWJIn.mjs +0 -8
- package/dist/workspace-XvqO9jIv.cjs +0 -4472
- package/dist/workspace-XvqO9jIv.cjs.map +0 -1
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
const require_discover = require("./discover-CroTIm0K.cjs");
|
|
2
|
+
const require_OpenApiTsGenerator = require("./OpenApiTsGenerator-D1nhN98m.cjs");
|
|
3
|
+
let node_fs = require("node:fs");
|
|
4
|
+
let node_fs_promises = require("node:fs/promises");
|
|
5
|
+
let node_path = require("node:path");
|
|
6
|
+
let _geekmidas_manifest = require("@geekmidas/manifest");
|
|
7
|
+
let _geekmidas_envkit = require("@geekmidas/envkit");
|
|
8
|
+
let _geekmidas_services = require("@geekmidas/services");
|
|
9
|
+
//#region src/generators/drivers.ts
|
|
10
|
+
/**
|
|
11
|
+
* Driver registration for generated entry points.
|
|
12
|
+
*
|
|
13
|
+
* A construct declares that it needs object storage and is handed one URL; the
|
|
14
|
+
* scheme in that URL picks the driver that builds the client. Which drivers
|
|
15
|
+
* exist is therefore the *entry point's* decision, not the construct's and not
|
|
16
|
+
* the application's — an app that never talks to S3 should never resolve the AWS
|
|
17
|
+
* SDK, and application code that registers a driver has named a provider in the
|
|
18
|
+
* one layer this design keeps provider-free.
|
|
19
|
+
*
|
|
20
|
+
* So the generated entries register: `gkm dev`'s server for local development,
|
|
21
|
+
* and each Lambda handler for its own target. Registration is an explicit call
|
|
22
|
+
* rather than an import side effect, because a side-effecting module is exactly
|
|
23
|
+
* what a bundler is entitled to drop.
|
|
24
|
+
*/
|
|
25
|
+
/** The `s3://` driver, which serves MinIO locally and S3 deployed. */
|
|
26
|
+
const S3 = {
|
|
27
|
+
imports: `import { registerStorageDriver } from '@geekmidas/storage';\nimport { s3Driver } from '@geekmidas/storage/aws';`,
|
|
28
|
+
setup: "registerStorageDriver(s3Driver);"
|
|
29
|
+
};
|
|
30
|
+
const NONE = {
|
|
31
|
+
imports: "",
|
|
32
|
+
setup: ""
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* The cache driver for a backend — exactly one, never all three.
|
|
36
|
+
*
|
|
37
|
+
* Keyed off the *backend* rather than off a dependency, because unlike storage
|
|
38
|
+
* the backend already says which driver is right: a project caching in Postgres
|
|
39
|
+
* resolves `pg` and never `ioredis`, and one caching in Upstash resolves
|
|
40
|
+
* neither. That is the whole reason the drivers live behind separate subpaths.
|
|
41
|
+
*
|
|
42
|
+
* Both HTTP schemes are registered for Upstash, because the local proxy speaks
|
|
43
|
+
* `http://` and Upstash speaks `https://`, and an entry that registered one
|
|
44
|
+
* would work in exactly one of the two places.
|
|
45
|
+
*/
|
|
46
|
+
const CACHE_DRIVERS = {
|
|
47
|
+
upstash: {
|
|
48
|
+
imports: `import { registerCacheDriver } from '@geekmidas/cache';\nimport { upstashCacheDriver, upstashInsecureCacheDriver } from '@geekmidas/cache/upstash';`,
|
|
49
|
+
setup: "registerCacheDriver(upstashCacheDriver);\nregisterCacheDriver(upstashInsecureCacheDriver);"
|
|
50
|
+
},
|
|
51
|
+
elasticache: {
|
|
52
|
+
imports: `import { registerCacheDriver } from '@geekmidas/cache';\nimport { redisCacheDriver, redissCacheDriver } from '@geekmidas/cache/redis';`,
|
|
53
|
+
setup: "registerCacheDriver(redisCacheDriver);\nregisterCacheDriver(redissCacheDriver);"
|
|
54
|
+
},
|
|
55
|
+
db: {
|
|
56
|
+
imports: `import { registerCacheDriver } from '@geekmidas/cache';\nimport { postgresCacheDriver } from '@geekmidas/cache/postgres';`,
|
|
57
|
+
setup: "registerCacheDriver(postgresCacheDriver);"
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Everything a generated entry should register: object storage, and the cache.
|
|
62
|
+
*
|
|
63
|
+
* Merged here rather than threaded separately, because a generated file has one
|
|
64
|
+
* import block and one setup block whatever fills them.
|
|
65
|
+
*/
|
|
66
|
+
function driversFor(options) {
|
|
67
|
+
const backends = options.cache === false || options.cache === void 0 ? [] : typeof options.cache === "string" ? [options.cache] : options.cache;
|
|
68
|
+
const parts = [storageDriversFor(options.appRoot), ...[...new Set(backends)].map((backend) => CACHE_DRIVERS[backend] ?? CACHE_DRIVERS[require_discover.DEFAULT_CACHE.aws])].filter((part) => part.imports || part.setup);
|
|
69
|
+
if (parts.length === 0) return NONE;
|
|
70
|
+
return {
|
|
71
|
+
imports: parts.map((part) => part.imports).join("\n"),
|
|
72
|
+
setup: parts.map((part) => part.setup).join("\n")
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Which cache backends an app's entry actually has to speak.
|
|
77
|
+
*
|
|
78
|
+
* The config answers this for a cache that named nowhere, and the *declaration*
|
|
79
|
+
* answers it for one that named a database — `orders.cache('Sessions')` is a
|
|
80
|
+
* statement about the application, so a deployment cannot move that cache and
|
|
81
|
+
* the entry cannot register a driver for somewhere else.
|
|
82
|
+
*
|
|
83
|
+
* Reading only the config is how an entry ends up registering the Upstash
|
|
84
|
+
* driver for a URL the target composed as `postgres://`, which fails at the
|
|
85
|
+
* first request with `UnregisteredCacheScheme` and a stack that points at the
|
|
86
|
+
* cache rather than at the config that disagreed.
|
|
87
|
+
*/
|
|
88
|
+
function cacheBackendsIn(manifest, configured) {
|
|
89
|
+
const caches = Object.values(manifest).filter((d) => d.kind === "cache");
|
|
90
|
+
return [...new Set(caches.map((c) => c.of ? "db" : configured))];
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The drivers an app's entry points should register.
|
|
94
|
+
*
|
|
95
|
+
* Keyed off the dependency rather than off a declared bucket, because the entry
|
|
96
|
+
* is generated before discovery has run on the watcher's rebuild path — and an
|
|
97
|
+
* app that installed `@geekmidas/storage` has already paid for the SDK it would
|
|
98
|
+
* otherwise resolve lazily.
|
|
99
|
+
*/
|
|
100
|
+
function storageDriversFor(appRoot) {
|
|
101
|
+
return dependsOnStorage(appRoot) ? S3 : NONE;
|
|
102
|
+
}
|
|
103
|
+
function dependsOnStorage(appRoot) {
|
|
104
|
+
const manifest = (0, node_path.join)(appRoot, "package.json");
|
|
105
|
+
if (!(0, node_fs.existsSync)(manifest)) return false;
|
|
106
|
+
try {
|
|
107
|
+
const pkg = JSON.parse((0, node_fs.readFileSync)(manifest, "utf8"));
|
|
108
|
+
return Boolean(pkg.dependencies?.["@geekmidas/storage"] ?? pkg.devDependencies?.["@geekmidas/storage"]);
|
|
109
|
+
} catch {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//#endregion
|
|
114
|
+
//#region src/test/harness.ts
|
|
115
|
+
/**
|
|
116
|
+
* What `gkm test` writes for a feature test to be built from.
|
|
117
|
+
*
|
|
118
|
+
* `gkm test` already discovers an app's constructs and resolves the test
|
|
119
|
+
* stage's environment; this keeps both, under `.gkm/test/`:
|
|
120
|
+
*
|
|
121
|
+
* - `manifest.json` — which construct and endpoint is exported where, and the
|
|
122
|
+
* environment the test stage resolved, keyed as the constructs derive keys.
|
|
123
|
+
* - `clients/<surface>.ts` — each surface's typed client, from the same
|
|
124
|
+
* generator `gkm build` uses for the app's own.
|
|
125
|
+
* - `index.ts` — the configured `it`, and a `Browser` holding one client per
|
|
126
|
+
* surface and one per auth server, each on the browser's `fetch`.
|
|
127
|
+
*
|
|
128
|
+
* A test imports `it` from there and declares nothing — not a construct, not an
|
|
129
|
+
* environment key, not a client.
|
|
130
|
+
*/
|
|
131
|
+
/**
|
|
132
|
+
* The variable the manifest's path travels in — what
|
|
133
|
+
* `@geekmidas/constructs/testing` reads. Its own constant rather than an import:
|
|
134
|
+
* that entry carries the whole harness (MSW, Hono, Vitest), and importing it
|
|
135
|
+
* here put all of it on the start-up path of every `gkm` command.
|
|
136
|
+
*/
|
|
137
|
+
const TEST_MANIFEST_ENV = "GKM_TEST_MANIFEST";
|
|
138
|
+
/** Where the harness lives, relative to the workspace root. */
|
|
139
|
+
const TEST_HARNESS_DIR = ".gkm/test";
|
|
140
|
+
/**
|
|
141
|
+
* Better-auth's client plugin for each server plugin id.
|
|
142
|
+
*
|
|
143
|
+
* Written down rather than derived: the ids and the client names do not follow
|
|
144
|
+
* one rule (`admin` pairs with `adminClient`, `email-otp` with
|
|
145
|
+
* `emailOTPClient`), and a guess that missed would hand a test a client
|
|
146
|
+
* silently lacking every method of that plugin. A server plugin not listed has
|
|
147
|
+
* no client counterpart — `bearer`, `open-api` — or needs one from another
|
|
148
|
+
* package, and is left out.
|
|
149
|
+
*/
|
|
150
|
+
const CLIENT_PLUGINS = {
|
|
151
|
+
admin: "adminClient",
|
|
152
|
+
anonymous: "anonymousClient",
|
|
153
|
+
"device-authorization": "deviceAuthorizationClient",
|
|
154
|
+
"email-otp": "emailOTPClient",
|
|
155
|
+
jwt: "jwtClient",
|
|
156
|
+
"last-login-method": "lastLoginMethodClient",
|
|
157
|
+
"magic-link": "magicLinkClient",
|
|
158
|
+
"multi-session": "multiSessionClient",
|
|
159
|
+
"oauth-popup": "oauthPopupClient",
|
|
160
|
+
"one-tap": "oneTapClient",
|
|
161
|
+
"one-time-token": "oneTimeTokenClient",
|
|
162
|
+
organization: "organizationClient",
|
|
163
|
+
"phone-number": "phoneNumberClient",
|
|
164
|
+
siwe: "siweClient",
|
|
165
|
+
"two-factor": "twoFactorClient",
|
|
166
|
+
username: "usernameClient"
|
|
167
|
+
};
|
|
168
|
+
/** Write the manifest, the clients and the harness; return the manifest path. */
|
|
169
|
+
async function writeTestHarness(options) {
|
|
170
|
+
const sources = {};
|
|
171
|
+
const declared = await require_discover.discover({
|
|
172
|
+
patterns: options.patterns,
|
|
173
|
+
cwd: options.root,
|
|
174
|
+
sources
|
|
175
|
+
});
|
|
176
|
+
const endpoints = (await new require_OpenApiTsGenerator.EndpointGenerator().load(options.patterns, options.root)).map(({ key, construct, path }) => ({
|
|
177
|
+
surface: construct.owner ?? construct.surface?.id,
|
|
178
|
+
construct,
|
|
179
|
+
source: {
|
|
180
|
+
file: path.absolute,
|
|
181
|
+
export: key
|
|
182
|
+
}
|
|
183
|
+
})).filter((endpoint) => endpoint.surface !== void 0);
|
|
184
|
+
const manifest = {
|
|
185
|
+
stage: options.stage,
|
|
186
|
+
constructs: Object.fromEntries(Object.entries(declared).flatMap(([id, declaration]) => {
|
|
187
|
+
const source = sources[id];
|
|
188
|
+
return source ? [[id, {
|
|
189
|
+
kind: declaration.kind,
|
|
190
|
+
source: {
|
|
191
|
+
file: source.file,
|
|
192
|
+
export: source.exportName
|
|
193
|
+
}
|
|
194
|
+
}]] : [];
|
|
195
|
+
})),
|
|
196
|
+
endpoints: endpoints.map(({ surface, source }) => ({
|
|
197
|
+
surface,
|
|
198
|
+
source
|
|
199
|
+
})),
|
|
200
|
+
env: options.env
|
|
201
|
+
};
|
|
202
|
+
const surfaces = [...new Set(endpoints.map(({ surface }) => surface))];
|
|
203
|
+
const generator = new require_OpenApiTsGenerator.OpenApiTsGenerator();
|
|
204
|
+
const clients = await Promise.all(surfaces.map(async (surface) => ({
|
|
205
|
+
file: `${(0, _geekmidas_manifest.kebabCase)(surface)}.ts`,
|
|
206
|
+
content: await generator.generate(endpoints.filter((endpoint) => endpoint.surface === surface).map(({ construct }) => construct), {
|
|
207
|
+
title: surface,
|
|
208
|
+
version: "1.0.0"
|
|
209
|
+
})
|
|
210
|
+
})));
|
|
211
|
+
const auths = await authClients(declared, sources, options.env);
|
|
212
|
+
const drivers = driversFor({
|
|
213
|
+
appRoot: options.root,
|
|
214
|
+
cache: cacheBackendsIn(declared, options.cacheBackend)
|
|
215
|
+
});
|
|
216
|
+
const database = await databaseSource(declared, sources, endpoints.map(({ construct }) => construct));
|
|
217
|
+
const files = [.../* @__PURE__ */ new Set([...Object.values(manifest.constructs).map(({ source }) => source.file), ...manifest.endpoints.map(({ source }) => source.file)])];
|
|
218
|
+
const harnessFor = (dir) => harnessModule({
|
|
219
|
+
dir,
|
|
220
|
+
files,
|
|
221
|
+
database,
|
|
222
|
+
surfaces: surfaces.map((id, index) => ({
|
|
223
|
+
id,
|
|
224
|
+
secured: clients[index].content.includes("authStrategies:")
|
|
225
|
+
})),
|
|
226
|
+
auths,
|
|
227
|
+
drivers
|
|
228
|
+
});
|
|
229
|
+
const json = `${JSON.stringify(manifest, null, 2)}\n`;
|
|
230
|
+
return (await Promise.all([...new Set(options.targets)].map(async (target) => {
|
|
231
|
+
const dir = (0, node_path.join)(target, TEST_HARNESS_DIR);
|
|
232
|
+
await (0, node_fs_promises.rm)(dir, {
|
|
233
|
+
recursive: true,
|
|
234
|
+
force: true
|
|
235
|
+
});
|
|
236
|
+
await (0, node_fs_promises.mkdir)((0, node_path.join)(dir, "clients"), { recursive: true });
|
|
237
|
+
await (0, node_fs_promises.writeFile)((0, node_path.join)(dir, "manifest.json"), json);
|
|
238
|
+
await (0, node_fs_promises.writeFile)((0, node_path.join)(dir, "index.ts"), harnessFor(dir));
|
|
239
|
+
for (const client of clients) await (0, node_fs_promises.writeFile)((0, node_path.join)(dir, "clients", client.file), client.content);
|
|
240
|
+
return (0, node_path.join)(dir, "manifest.json");
|
|
241
|
+
})))[0];
|
|
242
|
+
}
|
|
243
|
+
/** Each auth server, with the client plugins its server plugins pair with. */
|
|
244
|
+
async function authClients(declared, sources, env) {
|
|
245
|
+
const clients = [];
|
|
246
|
+
const envParser = new _geekmidas_envkit.EnvironmentParser({ ...env });
|
|
247
|
+
for (const [id, declaration] of Object.entries(declared)) {
|
|
248
|
+
const source = sources[id];
|
|
249
|
+
if (declaration.kind !== "rest-api" || !source) continue;
|
|
250
|
+
const exported = (await import(source.file))[source.exportName];
|
|
251
|
+
if (!isBetterAuth(exported) || exported.id !== id) continue;
|
|
252
|
+
const ids = await exported.pluginIds({
|
|
253
|
+
envParser,
|
|
254
|
+
context: _geekmidas_services.serviceContext
|
|
255
|
+
});
|
|
256
|
+
clients.push({
|
|
257
|
+
id,
|
|
258
|
+
basePath: exported.basePath,
|
|
259
|
+
plugins: ids.flatMap((plugin) => CLIENT_PLUGINS[plugin] ?? [])
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
return clients;
|
|
263
|
+
}
|
|
264
|
+
function isBetterAuth(value) {
|
|
265
|
+
return typeof value === "object" && value !== null && "pluginIds" in value && "basePath" in value;
|
|
266
|
+
}
|
|
267
|
+
/** `Api` → `api`, `AdminApi` → `adminApi`: a construct id as a property name. */
|
|
268
|
+
function propertyOf(id) {
|
|
269
|
+
return id.charAt(0).toLowerCase() + id.slice(1);
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* The database a test's `db` is — the one the endpoints were given with
|
|
273
|
+
* `.database()`, found by the service they hold, which is the construct's own.
|
|
274
|
+
* The same rule `featureTest` applies at runtime, applied here so `db` can be
|
|
275
|
+
* typed by the construct's schema.
|
|
276
|
+
*/
|
|
277
|
+
async function databaseSource(declared, sources, endpoints) {
|
|
278
|
+
const services = new Set(endpoints.map((endpoint) => endpoint.databaseService).filter(Boolean));
|
|
279
|
+
if (services.size === 0) return void 0;
|
|
280
|
+
for (const [id, declaration] of Object.entries(declared)) {
|
|
281
|
+
const source = sources[id];
|
|
282
|
+
if (!source || !declaration.kind.startsWith("database")) continue;
|
|
283
|
+
const exported = (await import(source.file))[source.exportName];
|
|
284
|
+
if (services.has(exported?.service)) return source;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/** An import specifier for `file`, from the harness written into `dir`. */
|
|
288
|
+
function specifierFrom(dir, file) {
|
|
289
|
+
const path = (0, node_path.relative)(dir, file).replace(/\.tsx?$/, ".js");
|
|
290
|
+
return path.startsWith(".") ? path : `./${path}`;
|
|
291
|
+
}
|
|
292
|
+
function harnessModule(options) {
|
|
293
|
+
const { surfaces, auths, drivers, database, dir, files } = options;
|
|
294
|
+
const plugins = [...new Set(auths.flatMap(({ plugins }) => plugins))].sort();
|
|
295
|
+
const imports = [
|
|
296
|
+
`import {${database ? " type DatabaseOf," : ""} featureTest, loadTestManifest } from '@geekmidas/constructs/testing';`,
|
|
297
|
+
...database ? [`import type { ${database.exportName} as __database } from '${specifierFrom(dir, database.file)}';`] : [],
|
|
298
|
+
`import { Browser as TestBrowser } from '@geekmidas/testkit/browser';`,
|
|
299
|
+
...surfaces.map(({ id }) => `import { createApi as create${id} } from './clients/${(0, _geekmidas_manifest.kebabCase)(id)}.js';`),
|
|
300
|
+
...auths.length ? [`import { createAuthClient } from 'better-auth/client';`] : [],
|
|
301
|
+
...plugins.length ? [`import { ${plugins.join(", ")} } from 'better-auth/client/plugins';`] : [],
|
|
302
|
+
...drivers.imports ? [drivers.imports] : [],
|
|
303
|
+
...files.map((file, index) => `import * as __module${index} from '${specifierFrom(dir, file)}';`)
|
|
304
|
+
];
|
|
305
|
+
const url = (id) => `manifest.env[${JSON.stringify((0, _geekmidas_manifest.provideKey)(id, "url"))}]!`;
|
|
306
|
+
const members = [...surfaces.map(({ id, secured }) => ` /** \`${id}\`, at ${(0, _geekmidas_manifest.provideKey)(id, "url")}. */
|
|
307
|
+
readonly ${propertyOf(id)} = create${id}({
|
|
308
|
+
baseURL: ${url(id)},
|
|
309
|
+
fetch: this.fetch,${secured ? `
|
|
310
|
+
// Served in-process: no authorizer runs, so nothing is signed. A scheme
|
|
311
|
+
// with no strategy sends its request without credentials.
|
|
312
|
+
authStrategies: {} as never,` : ""}
|
|
313
|
+
});`), ...auths.map((auth) => ` /** \`${auth.id}\`, at ${(0, _geekmidas_manifest.provideKey)(auth.id, "url")}. */
|
|
314
|
+
readonly ${propertyOf(auth.id)} = createAuthClient({
|
|
315
|
+
baseURL: \`\${${url(auth.id)}}${auth.basePath}\`,
|
|
316
|
+
plugins: [${auth.plugins.map((plugin) => `${plugin}()`).join(", ")}],
|
|
317
|
+
fetchOptions: { customFetchImpl: this.fetch },
|
|
318
|
+
});`)];
|
|
319
|
+
return `// Generated by \`gkm test\` from the app's constructs — do not edit.
|
|
320
|
+
${imports.join("\n")}
|
|
321
|
+
|
|
322
|
+
${drivers.setup ? `// The drivers the app's server entry registers, registered the same way.\n${drivers.setup}\n\n` : ""}const manifest = loadTestManifest(new URL('./manifest.json', import.meta.url));
|
|
323
|
+
|
|
324
|
+
/** A browser with a client for every surface the app declares. */
|
|
325
|
+
export class Browser extends TestBrowser {
|
|
326
|
+
${members.join("\n\n")}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** The app's modules, by the path the manifest records for each. */
|
|
330
|
+
const modules = {
|
|
331
|
+
${files.map((file, index) => `\t${JSON.stringify(file)}: __module${index},`).join("\n")}
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
/** \`it\`, for a test that drives the app the way it runs deployed. */
|
|
335
|
+
export const it = featureTest${database ? "<Browser, DatabaseOf<typeof __database>>" : ""}({ manifest, modules, browser: Browser });
|
|
336
|
+
`;
|
|
337
|
+
}
|
|
338
|
+
//#endregion
|
|
339
|
+
//#region src/test/ready.ts
|
|
340
|
+
/**
|
|
341
|
+
* The hand-off between `gkm test` and the Vitest global setup.
|
|
342
|
+
*
|
|
343
|
+
* `gkm test` reconciles and migrates the test stage before Vitest starts; the
|
|
344
|
+
* setup, run inside Vitest, only has to load what that left. Started by plain
|
|
345
|
+
* `vitest` instead, the setup runs `gkm test --setup` itself first — so there is
|
|
346
|
+
* one way the stage gets ready, whichever started the suite.
|
|
347
|
+
*/
|
|
348
|
+
/** Set on the Vitest process by `gkm test`: the stage is ready, here. */
|
|
349
|
+
const TEST_READY_ENV = "GKM_TEST_READY";
|
|
350
|
+
/** Where `gkm test` records what it prepared, from where it ran. */
|
|
351
|
+
const TEST_READY_FILE = ".gkm/test-ready.json";
|
|
352
|
+
//#endregion
|
|
353
|
+
Object.defineProperty(exports, "TEST_MANIFEST_ENV", {
|
|
354
|
+
enumerable: true,
|
|
355
|
+
get: function() {
|
|
356
|
+
return TEST_MANIFEST_ENV;
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
Object.defineProperty(exports, "TEST_READY_ENV", {
|
|
360
|
+
enumerable: true,
|
|
361
|
+
get: function() {
|
|
362
|
+
return TEST_READY_ENV;
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
Object.defineProperty(exports, "TEST_READY_FILE", {
|
|
366
|
+
enumerable: true,
|
|
367
|
+
get: function() {
|
|
368
|
+
return TEST_READY_FILE;
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
Object.defineProperty(exports, "cacheBackendsIn", {
|
|
372
|
+
enumerable: true,
|
|
373
|
+
get: function() {
|
|
374
|
+
return cacheBackendsIn;
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
Object.defineProperty(exports, "driversFor", {
|
|
378
|
+
enumerable: true,
|
|
379
|
+
get: function() {
|
|
380
|
+
return driversFor;
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
Object.defineProperty(exports, "writeTestHarness", {
|
|
384
|
+
enumerable: true,
|
|
385
|
+
get: function() {
|
|
386
|
+
return writeTestHarness;
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
//# sourceMappingURL=ready-DXtNfk7v.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ready-DXtNfk7v.cjs","names":["DEFAULT_CACHE","join","existsSync","readFileSync","discover","EndpointGenerator","OpenApiTsGenerator","kebabCase","join","rm","mkdir","writeFile","EnvironmentParser","serviceContext","relative","provideKey"],"sources":["../src/generators/drivers.ts","../src/test/harness.ts","../src/test/ready.ts"],"sourcesContent":["/**\n * Driver registration for generated entry points.\n *\n * A construct declares that it needs object storage and is handed one URL; the\n * scheme in that URL picks the driver that builds the client. Which drivers\n * exist is therefore the *entry point's* decision, not the construct's and not\n * the application's — an app that never talks to S3 should never resolve the AWS\n * SDK, and application code that registers a driver has named a provider in the\n * one layer this design keeps provider-free.\n *\n * So the generated entries register: `gkm dev`'s server for local development,\n * and each Lambda handler for its own target. Registration is an explicit call\n * rather than an import side effect, because a side-effecting module is exactly\n * what a bundler is entitled to drop.\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { type CacheBackend, DEFAULT_CACHE } from '../types.js';\n\n/** The `s3://` driver, which serves MinIO locally and S3 deployed. */\nconst S3 = {\n\timports: `import { registerStorageDriver } from '@geekmidas/storage';\\nimport { s3Driver } from '@geekmidas/storage/aws';`,\n\tsetup: 'registerStorageDriver(s3Driver);',\n};\n\n/** What a generated entry needs in order to register the drivers it uses. */\nexport interface RuntimeDrivers {\n\t/** Import lines, or `''` when there is nothing to register. */\n\timports: string;\n\t/** The registration calls, or `''`. */\n\tsetup: string;\n}\n\n/** @deprecated The same shape, from when only storage had drivers. */\nexport type StorageDrivers = RuntimeDrivers;\n\nconst NONE: RuntimeDrivers = { imports: '', setup: '' };\n\n/**\n * The cache driver for a backend — exactly one, never all three.\n *\n * Keyed off the *backend* rather than off a dependency, because unlike storage\n * the backend already says which driver is right: a project caching in Postgres\n * resolves `pg` and never `ioredis`, and one caching in Upstash resolves\n * neither. That is the whole reason the drivers live behind separate subpaths.\n *\n * Both HTTP schemes are registered for Upstash, because the local proxy speaks\n * `http://` and Upstash speaks `https://`, and an entry that registered one\n * would work in exactly one of the two places.\n */\nconst CACHE_DRIVERS: Record<CacheBackend, RuntimeDrivers> = {\n\tupstash: {\n\t\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { upstashCacheDriver, upstashInsecureCacheDriver } from '@geekmidas/cache/upstash';`,\n\t\tsetup:\n\t\t\t'registerCacheDriver(upstashCacheDriver);\\nregisterCacheDriver(upstashInsecureCacheDriver);',\n\t},\n\telasticache: {\n\t\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { redisCacheDriver, redissCacheDriver } from '@geekmidas/cache/redis';`,\n\t\tsetup:\n\t\t\t'registerCacheDriver(redisCacheDriver);\\nregisterCacheDriver(redissCacheDriver);',\n\t},\n\tdb: {\n\t\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { postgresCacheDriver } from '@geekmidas/cache/postgres';`,\n\t\tsetup: 'registerCacheDriver(postgresCacheDriver);',\n\t},\n};\n\n/**\n * Everything a generated entry should register: object storage, and the cache.\n *\n * Merged here rather than threaded separately, because a generated file has one\n * import block and one setup block whatever fills them.\n */\nexport function driversFor(options: {\n\tappRoot: string;\n\t/**\n\t * The cache backends actually in play — normally one, and more than one only\n\t * where an app declares two caches that live in different places.\n\t *\n\t * Absent or empty means nothing declared a cache, so no cache driver is\n\t * registered at all.\n\t */\n\tcache?: CacheBackend | readonly CacheBackend[] | false;\n}): RuntimeDrivers {\n\tconst backends =\n\t\toptions.cache === false || options.cache === undefined\n\t\t\t? []\n\t\t\t: typeof options.cache === 'string'\n\t\t\t\t? [options.cache]\n\t\t\t\t: options.cache;\n\n\tconst parts = [\n\t\tstorageDriversFor(options.appRoot),\n\t\t...[...new Set(backends)].map(\n\t\t\t(backend) => CACHE_DRIVERS[backend] ?? CACHE_DRIVERS[DEFAULT_CACHE.aws],\n\t\t),\n\t].filter((part) => part.imports || part.setup);\n\n\tif (parts.length === 0) return NONE;\n\n\treturn {\n\t\timports: parts.map((part) => part.imports).join('\\n'),\n\t\tsetup: parts.map((part) => part.setup).join('\\n'),\n\t};\n}\n\n/**\n * Which cache backends an app's entry actually has to speak.\n *\n * The config answers this for a cache that named nowhere, and the *declaration*\n * answers it for one that named a database — `orders.cache('Sessions')` is a\n * statement about the application, so a deployment cannot move that cache and\n * the entry cannot register a driver for somewhere else.\n *\n * Reading only the config is how an entry ends up registering the Upstash\n * driver for a URL the target composed as `postgres://`, which fails at the\n * first request with `UnregisteredCacheScheme` and a stack that points at the\n * cache rather than at the config that disagreed.\n */\nexport function cacheBackendsIn(\n\tmanifest: Record<string, { kind: string; of?: string }>,\n\tconfigured: CacheBackend,\n): CacheBackend[] {\n\tconst caches = Object.values(manifest).filter((d) => d.kind === 'cache');\n\n\treturn [...new Set(caches.map((c) => (c.of ? 'db' : configured)))];\n}\n\n/**\n * The drivers an app's entry points should register.\n *\n * Keyed off the dependency rather than off a declared bucket, because the entry\n * is generated before discovery has run on the watcher's rebuild path — and an\n * app that installed `@geekmidas/storage` has already paid for the SDK it would\n * otherwise resolve lazily.\n */\nexport function storageDriversFor(appRoot: string): RuntimeDrivers {\n\treturn dependsOnStorage(appRoot) ? S3 : NONE;\n}\n\nfunction dependsOnStorage(appRoot: string): boolean {\n\tconst manifest = join(appRoot, 'package.json');\n\tif (!existsSync(manifest)) return false;\n\n\ttry {\n\t\tconst pkg = JSON.parse(readFileSync(manifest, 'utf8')) as {\n\t\t\tdependencies?: Record<string, string>;\n\t\t\tdevDependencies?: Record<string, string>;\n\t\t};\n\n\t\treturn Boolean(\n\t\t\tpkg.dependencies?.['@geekmidas/storage'] ??\n\t\t\t\tpkg.devDependencies?.['@geekmidas/storage'],\n\t\t);\n\t} catch {\n\t\t// An unreadable package.json is the build's problem, not this function's.\n\t\treturn false;\n\t}\n}\n","/**\n * What `gkm test` writes for a feature test to be built from.\n *\n * `gkm test` already discovers an app's constructs and resolves the test\n * stage's environment; this keeps both, under `.gkm/test/`:\n *\n * - `manifest.json` — which construct and endpoint is exported where, and the\n * environment the test stage resolved, keyed as the constructs derive keys.\n * - `clients/<surface>.ts` — each surface's typed client, from the same\n * generator `gkm build` uses for the app's own.\n * - `index.ts` — the configured `it`, and a `Browser` holding one client per\n * surface and one per auth server, each on the browser's `fetch`.\n *\n * A test imports `it` from there and declares nothing — not a construct, not an\n * environment key, not a client.\n */\n\nimport { mkdir, rm, writeFile } from 'node:fs/promises';\nimport { join, relative } from 'node:path';\nimport type { BetterAuth } from '@geekmidas/constructs/auth';\nimport type { Endpoint } from '@geekmidas/constructs/endpoints';\nimport type { TestManifest } from '@geekmidas/constructs/testing';\nimport { EnvironmentParser } from '@geekmidas/envkit';\nimport { kebabCase, provideKey } from '@geekmidas/manifest';\nimport { serviceContext } from '@geekmidas/services';\nimport {\n\tcacheBackendsIn,\n\tdriversFor,\n\ttype RuntimeDrivers,\n} from '../generators/drivers.js';\nimport { EndpointGenerator } from '../generators/EndpointGenerator.js';\nimport { OpenApiTsGenerator } from '../generators/OpenApiTsGenerator.js';\nimport { type ConstructSource, discover } from '../reconcile/discover.js';\nimport type { CacheBackend } from '../types.js';\n\n/**\n * The variable the manifest's path travels in — what\n * `@geekmidas/constructs/testing` reads. Its own constant rather than an import:\n * that entry carries the whole harness (MSW, Hono, Vitest), and importing it\n * here put all of it on the start-up path of every `gkm` command.\n */\nexport const TEST_MANIFEST_ENV = 'GKM_TEST_MANIFEST';\n\n/** Where the harness lives, relative to the workspace root. */\nexport const TEST_HARNESS_DIR = '.gkm/test';\n\n/**\n * Better-auth's client plugin for each server plugin id.\n *\n * Written down rather than derived: the ids and the client names do not follow\n * one rule (`admin` pairs with `adminClient`, `email-otp` with\n * `emailOTPClient`), and a guess that missed would hand a test a client\n * silently lacking every method of that plugin. A server plugin not listed has\n * no client counterpart — `bearer`, `open-api` — or needs one from another\n * package, and is left out.\n */\nconst CLIENT_PLUGINS: Readonly<Record<string, string>> = {\n\tadmin: 'adminClient',\n\tanonymous: 'anonymousClient',\n\t'device-authorization': 'deviceAuthorizationClient',\n\t'email-otp': 'emailOTPClient',\n\tjwt: 'jwtClient',\n\t'last-login-method': 'lastLoginMethodClient',\n\t'magic-link': 'magicLinkClient',\n\t'multi-session': 'multiSessionClient',\n\t'oauth-popup': 'oauthPopupClient',\n\t'one-tap': 'oneTapClient',\n\t'one-time-token': 'oneTimeTokenClient',\n\torganization: 'organizationClient',\n\t'phone-number': 'phoneNumberClient',\n\tsiwe: 'siweClient',\n\t'two-factor': 'twoFactorClient',\n\tusername: 'usernameClient',\n};\n\nexport interface WriteTestHarnessOptions {\n\t/** The workspace root: where discovery runs from. */\n\troot: string;\n\t/**\n\t * The directories to write `.gkm/test/` into — each app's own. A package\n\t * maps its `#test` import only to a path inside itself, so each app gets its\n\t * own copy of the harness rather than one at the root it cannot reach.\n\t */\n\ttargets: string[];\n\t/** The constructs glob — the same patterns reconcile and the build use. */\n\tpatterns: string[];\n\t/** The stage the environment was resolved for. */\n\tstage: string;\n\t/** The environment the suite runs with. */\n\tenv: Record<string, string>;\n\t/**\n\t * Where a cache lives when it names nowhere — the target's default, which\n\t * the build's server entry registers a driver for.\n\t */\n\tcacheBackend: CacheBackend;\n}\n\n/** Write the manifest, the clients and the harness; return the manifest path. */\nexport async function writeTestHarness(\n\toptions: WriteTestHarnessOptions,\n): Promise<string> {\n\tconst sources: Record<string, ConstructSource> = {};\n\tconst declared = await discover({\n\t\tpatterns: options.patterns,\n\t\tcwd: options.root,\n\t\tsources,\n\t});\n\n\tconst loaded = await new EndpointGenerator().load(\n\t\toptions.patterns,\n\t\toptions.root,\n\t);\n\tconst endpoints = loaded\n\t\t.map(({ key, construct, path }) => ({\n\t\t\tsurface: construct.owner ?? construct.surface?.id,\n\t\t\tconstruct,\n\t\t\tsource: { file: path.absolute, export: key },\n\t\t}))\n\t\t.filter(\n\t\t\t(endpoint): endpoint is typeof endpoint & { surface: string } =>\n\t\t\t\tendpoint.surface !== undefined,\n\t\t);\n\n\tconst manifest: TestManifest = {\n\t\tstage: options.stage,\n\t\tconstructs: Object.fromEntries(\n\t\t\tObject.entries(declared).flatMap(([id, declaration]) => {\n\t\t\t\tconst source = sources[id];\n\t\t\t\treturn source\n\t\t\t\t\t? [\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tkind: declaration.kind,\n\t\t\t\t\t\t\t\t\tsource: { file: source.file, export: source.exportName },\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t]\n\t\t\t\t\t: [];\n\t\t\t}),\n\t\t),\n\t\tendpoints: endpoints.map(({ surface, source }) => ({ surface, source })),\n\t\tenv: options.env,\n\t};\n\t// One typed client per surface, from the generator the build uses.\n\tconst surfaces = [...new Set(endpoints.map(({ surface }) => surface))];\n\tconst generator = new OpenApiTsGenerator();\n\tconst clients = await Promise.all(\n\t\tsurfaces.map(async (surface) => ({\n\t\t\tfile: `${kebabCase(surface)}.ts`,\n\t\t\tcontent: await generator.generate(\n\t\t\t\tendpoints\n\t\t\t\t\t.filter((endpoint) => endpoint.surface === surface)\n\t\t\t\t\t.map(({ construct }) => construct as Endpoint<any, any, any, any>),\n\t\t\t\t{ title: surface, version: '1.0.0' },\n\t\t\t),\n\t\t})),\n\t);\n\n\tconst auths = await authClients(declared, sources, options.env);\n\t// The drivers the server entry registers — which cache and storage clients\n\t// exist is the entry's decision, and the harness stands where the entry does.\n\tconst drivers = driversFor({\n\t\tappRoot: options.root,\n\t\tcache: cacheBackendsIn(declared, options.cacheBackend),\n\t});\n\tconst database = await databaseSource(\n\t\tdeclared,\n\t\tsources,\n\t\tendpoints.map(({ construct }) => construct as Endpoint<any, any, any, any>),\n\t);\n\tconst files = [\n\t\t...new Set([\n\t\t\t...Object.values(manifest.constructs).map(({ source }) => source.file),\n\t\t\t...manifest.endpoints.map(({ source }) => source.file),\n\t\t]),\n\t];\n\tconst harnessFor = (dir: string) =>\n\t\tharnessModule({\n\t\t\tdir,\n\t\t\tfiles,\n\t\t\tdatabase,\n\t\t\tsurfaces: surfaces.map((id, index) => ({\n\t\t\t\tid,\n\t\t\t\t// The client the generator writes for a surface with authorizers\n\t\t\t\t// takes a strategy per scheme; one without takes none.\n\t\t\t\tsecured: clients[index]!.content.includes('authStrategies:'),\n\t\t\t})),\n\t\t\tauths,\n\t\t\tdrivers,\n\t\t});\n\tconst json = `${JSON.stringify(manifest, null, 2)}\\n`;\n\n\tconst written = await Promise.all(\n\t\t[...new Set(options.targets)].map(async (target) => {\n\t\t\tconst dir = join(target, TEST_HARNESS_DIR);\n\t\t\t// Rewritten whole on every run: a client for a surface that no longer\n\t\t\t// exists is not something a test should be able to import.\n\t\t\tawait rm(dir, { recursive: true, force: true });\n\t\t\tawait mkdir(join(dir, 'clients'), { recursive: true });\n\t\t\tawait writeFile(join(dir, 'manifest.json'), json);\n\t\t\tawait writeFile(join(dir, 'index.ts'), harnessFor(dir));\n\t\t\tfor (const client of clients) {\n\t\t\t\tawait writeFile(join(dir, 'clients', client.file), client.content);\n\t\t\t}\n\t\t\treturn join(dir, 'manifest.json');\n\t\t}),\n\t);\n\n\treturn written[0]!;\n}\n\ninterface AuthClient {\n\tid: string;\n\tbasePath: string;\n\tplugins: string[];\n}\n\n/** Each auth server, with the client plugins its server plugins pair with. */\nasync function authClients(\n\tdeclared: Awaited<ReturnType<typeof discover>>,\n\tsources: Record<string, ConstructSource>,\n\tenv: Record<string, string>,\n): Promise<AuthClient[]> {\n\tconst clients: AuthClient[] = [];\n\tconst envParser = new EnvironmentParser({ ...env });\n\n\tfor (const [id, declaration] of Object.entries(declared)) {\n\t\tconst source = sources[id];\n\t\tif (declaration.kind !== 'rest-api' || !source) continue;\n\n\t\tconst exported = (await import(source.file))[source.exportName] as unknown;\n\t\tif (!isBetterAuth(exported) || exported.id !== id) continue;\n\n\t\tconst ids = await exported.pluginIds({\n\t\t\tenvParser,\n\t\t\tcontext: serviceContext,\n\t\t});\n\t\tclients.push({\n\t\t\tid,\n\t\t\tbasePath: exported.basePath,\n\t\t\tplugins: ids.flatMap((plugin) => CLIENT_PLUGINS[plugin] ?? []),\n\t\t});\n\t}\n\n\treturn clients;\n}\n\nfunction isBetterAuth(value: unknown): value is BetterAuth {\n\treturn (\n\t\ttypeof value === 'object' &&\n\t\tvalue !== null &&\n\t\t'pluginIds' in value &&\n\t\t'basePath' in value\n\t);\n}\n\n/** `Api` → `api`, `AdminApi` → `adminApi`: a construct id as a property name. */\nfunction propertyOf(id: string): string {\n\treturn id.charAt(0).toLowerCase() + id.slice(1);\n}\n\n/**\n * The database a test's `db` is — the one the endpoints were given with\n * `.database()`, found by the service they hold, which is the construct's own.\n * The same rule `featureTest` applies at runtime, applied here so `db` can be\n * typed by the construct's schema.\n */\nasync function databaseSource(\n\tdeclared: Awaited<ReturnType<typeof discover>>,\n\tsources: Record<string, ConstructSource>,\n\tendpoints: Endpoint<any, any, any, any>[],\n): Promise<ConstructSource | undefined> {\n\tconst services = new Set<unknown>(\n\t\tendpoints.map((endpoint) => endpoint.databaseService).filter(Boolean),\n\t);\n\tif (services.size === 0) return undefined;\n\n\tfor (const [id, declaration] of Object.entries(declared)) {\n\t\tconst source = sources[id];\n\t\tif (!source || !declaration.kind.startsWith('database')) continue;\n\n\t\tconst exported = (await import(source.file))[source.exportName] as {\n\t\t\tservice?: unknown;\n\t\t};\n\t\tif (services.has(exported?.service)) return source;\n\t}\n\treturn undefined;\n}\n\n/** An import specifier for `file`, from the harness written into `dir`. */\nfunction specifierFrom(dir: string, file: string): string {\n\tconst path = relative(dir, file).replace(/\\.tsx?$/, '.js');\n\treturn path.startsWith('.') ? path : `./${path}`;\n}\n\nfunction harnessModule(options: {\n\tdir: string;\n\t/** Every module the manifest points at, by absolute path. */\n\tfiles: string[];\n\tdatabase: ConstructSource | undefined;\n\tsurfaces: { id: string; secured: boolean }[];\n\tauths: AuthClient[];\n\tdrivers: RuntimeDrivers;\n}): string {\n\tconst { surfaces, auths, drivers, database, dir, files } = options;\n\tconst plugins = [...new Set(auths.flatMap(({ plugins }) => plugins))].sort();\n\n\tconst imports = [\n\t\t`import {${database ? ' type DatabaseOf,' : ''} featureTest, loadTestManifest } from '@geekmidas/constructs/testing';`,\n\t\t...(database\n\t\t\t? [\n\t\t\t\t\t`import type { ${database.exportName} as __database } from '${specifierFrom(dir, database.file)}';`,\n\t\t\t\t]\n\t\t\t: []),\n\t\t`import { Browser as TestBrowser } from '@geekmidas/testkit/browser';`,\n\t\t...surfaces.map(\n\t\t\t({ id }) =>\n\t\t\t\t`import { createApi as create${id} } from './clients/${kebabCase(id)}.js';`,\n\t\t),\n\t\t...(auths.length\n\t\t\t? [`import { createAuthClient } from 'better-auth/client';`]\n\t\t\t: []),\n\t\t...(plugins.length\n\t\t\t? [`import { ${plugins.join(', ')} } from 'better-auth/client/plugins';`]\n\t\t\t: []),\n\t\t...(drivers.imports ? [drivers.imports] : []),\n\t\t// Every construct and endpoint module, imported here — inside the app,\n\t\t// where its tsconfig paths resolve — rather than by path from the kit.\n\t\t...files.map(\n\t\t\t(file, index) =>\n\t\t\t\t`import * as __module${index} from '${specifierFrom(dir, file)}';`,\n\t\t),\n\t];\n\n\tconst url = (id: string) =>\n\t\t`manifest.env[${JSON.stringify(provideKey(id, 'url'))}]!`;\n\n\tconst members = [\n\t\t...surfaces.map(\n\t\t\t({ id, secured }) => `\t/** \\`${id}\\`, at ${provideKey(id, 'url')}. */\n\treadonly ${propertyOf(id)} = create${id}({\n\t\tbaseURL: ${url(id)},\n\t\tfetch: this.fetch,${\n\t\t\tsecured\n\t\t\t\t? `\n\t\t// Served in-process: no authorizer runs, so nothing is signed. A scheme\n\t\t// with no strategy sends its request without credentials.\n\t\tauthStrategies: {} as never,`\n\t\t\t\t: ''\n\t\t}\n\t});`,\n\t\t),\n\t\t...auths.map(\n\t\t\t(auth) => `\t/** \\`${auth.id}\\`, at ${provideKey(auth.id, 'url')}. */\n\treadonly ${propertyOf(auth.id)} = createAuthClient({\n\t\tbaseURL: \\`\\${${url(auth.id)}}${auth.basePath}\\`,\n\t\tplugins: [${auth.plugins.map((plugin) => `${plugin}()`).join(', ')}],\n\t\tfetchOptions: { customFetchImpl: this.fetch },\n\t});`,\n\t\t),\n\t];\n\n\treturn `// Generated by \\`gkm test\\` from the app's constructs — do not edit.\n${imports.join('\\n')}\n\n${drivers.setup ? `// The drivers the app's server entry registers, registered the same way.\\n${drivers.setup}\\n\\n` : ''}const manifest = loadTestManifest(new URL('./manifest.json', import.meta.url));\n\n/** A browser with a client for every surface the app declares. */\nexport class Browser extends TestBrowser {\n${members.join('\\n\\n')}\n}\n\n/** The app's modules, by the path the manifest records for each. */\nconst modules = {\n${files.map((file, index) => `\\t${JSON.stringify(file)}: __module${index},`).join('\\n')}\n};\n\n/** \\`it\\`, for a test that drives the app the way it runs deployed. */\nexport const it = featureTest${database ? '<Browser, DatabaseOf<typeof __database>>' : ''}({ manifest, modules, browser: Browser });\n`;\n}\n","/**\n * The hand-off between `gkm test` and the Vitest global setup.\n *\n * `gkm test` reconciles and migrates the test stage before Vitest starts; the\n * setup, run inside Vitest, only has to load what that left. Started by plain\n * `vitest` instead, the setup runs `gkm test --setup` itself first — so there is\n * one way the stage gets ready, whichever started the suite.\n */\n\n/** Set on the Vitest process by `gkm test`: the stage is ready, here. */\nexport const TEST_READY_ENV = 'GKM_TEST_READY';\n\n/** Where `gkm test` records what it prepared, from where it ran. */\nexport const TEST_READY_FILE = '.gkm/test-ready.json';\n\nexport interface TestReady {\n\t/** The resolved environment, as a JSON object of strings. */\n\tenv: string;\n\t/** The test manifest the `#test` harness reads. */\n\tmanifest?: string;\n}\n"],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
3
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_discover = require("../discover-CroTIm0K.cjs");
|
|
3
|
+
const require_emit = require("../emit-BdoPFjZf.cjs");
|
|
4
4
|
exports.MANIFEST_PATH = require_emit.MANIFEST_PATH;
|
|
5
5
|
exports.discover = require_discover.discover;
|
|
6
6
|
exports.isDeclarable = require_discover.isDeclarable;
|
|
7
7
|
exports.manifestModule = require_emit.manifestModule;
|
|
8
8
|
exports.withRoutes = require_emit.withRoutes;
|
|
9
|
-
exports.writeManifestModule = require_emit.writeManifestModule;
|
|
9
|
+
exports.writeManifestModule = require_emit.writeManifestModule;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as EmailBackend, t as CacheBackend } from "../types-DOOAevjG.cjs";
|
|
2
2
|
import { ConstructManifest, Declaration, RouteInfo } from "@geekmidas/manifest";
|
|
3
|
-
|
|
4
3
|
//#region src/reconcile/discover.d.ts
|
|
5
|
-
|
|
6
4
|
/** The construct face discovery needs: an id, and the ability to declare. */
|
|
7
5
|
interface Declarable {
|
|
8
6
|
id: string;
|
|
@@ -15,7 +13,7 @@ interface Declarable {
|
|
|
15
13
|
* `service` is still a construct, and requiring one would exclude the kinds that
|
|
16
14
|
* own no address.
|
|
17
15
|
*/
|
|
18
|
-
declare function isDeclarable(value: unknown): value is Declarable;
|
|
16
|
+
export declare function isDeclarable(value: unknown): value is Declarable;
|
|
19
17
|
interface DiscoverOptions {
|
|
20
18
|
/** The glob(s) to search. One pattern, not one per kind. */
|
|
21
19
|
patterns: string | readonly string[];
|
|
@@ -39,6 +37,18 @@ interface DiscoverOptions {
|
|
|
39
37
|
* caller, which wants the manifest and nothing else, is unchanged.
|
|
40
38
|
*/
|
|
41
39
|
sources?: Record<string, ConstructSource>;
|
|
40
|
+
/**
|
|
41
|
+
* Filled in, when provided, with the ids each owner's runnables declared an
|
|
42
|
+
* edge to — keyed by the surface an endpoint was built from, or the worker
|
|
43
|
+
* a cron or subscriber was.
|
|
44
|
+
*
|
|
45
|
+
* A runnable is not a declaration: a surface's endpoints are found by the
|
|
46
|
+
* glob, not listed by the surface, so its node says `endpoints: []` until a
|
|
47
|
+
* build folds the routes in. Anything deciding what an app's process reaches
|
|
48
|
+
* — which keys its container is given — needs those edges before any build,
|
|
49
|
+
* and discovery is already importing the files they are in.
|
|
50
|
+
*/
|
|
51
|
+
runnables?: Record<string, string[]>;
|
|
42
52
|
}
|
|
43
53
|
/** Where a construct was declared, and under what name. */
|
|
44
54
|
interface ConstructSource {
|
|
@@ -46,6 +56,13 @@ interface ConstructSource {
|
|
|
46
56
|
file: string;
|
|
47
57
|
/** The name it was exported as — what an import statement has to say. */
|
|
48
58
|
exportName: string;
|
|
59
|
+
/**
|
|
60
|
+
* The exported construct itself.
|
|
61
|
+
*
|
|
62
|
+
* What a declaration cannot say — whether it serves itself — the object can
|
|
63
|
+
* be asked, and discovery already imported it.
|
|
64
|
+
*/
|
|
65
|
+
construct: unknown;
|
|
49
66
|
}
|
|
50
67
|
/**
|
|
51
68
|
* Build the construct manifest for a project.
|
|
@@ -55,12 +72,11 @@ interface ConstructSource {
|
|
|
55
72
|
*
|
|
56
73
|
* @throws {DuplicateConstruct} when two exports claim one id.
|
|
57
74
|
*/
|
|
58
|
-
declare function discover(options: DiscoverOptions): Promise<ConstructManifest>;
|
|
59
|
-
/** Two constructs claiming one id. */
|
|
75
|
+
export declare function discover(options: DiscoverOptions): Promise<ConstructManifest>;
|
|
60
76
|
//#endregion
|
|
61
77
|
//#region src/reconcile/emit.d.ts
|
|
62
78
|
/** Where the build writes it, relative to the app root. */
|
|
63
|
-
declare const MANIFEST_PATH = ".gkm/manifest.ts";
|
|
79
|
+
export declare const MANIFEST_PATH = ".gkm/manifest.ts";
|
|
64
80
|
/**
|
|
65
81
|
* Fold the build's generated routes into the surface that serves them.
|
|
66
82
|
*
|
|
@@ -89,37 +105,22 @@ declare const MANIFEST_PATH = ".gkm/manifest.ts";
|
|
|
89
105
|
* document's contents depend on which `--provider` ran last. A manifest must not
|
|
90
106
|
* do that.
|
|
91
107
|
*/
|
|
92
|
-
declare function withRoutes(manifest: ConstructManifest, routes: readonly RouteInfo[], options: {
|
|
108
|
+
export declare function withRoutes(manifest: ConstructManifest, routes: readonly RouteInfo[], options: {
|
|
93
109
|
perRoute: boolean;
|
|
94
110
|
}): ConstructManifest;
|
|
95
|
-
/**
|
|
96
|
-
* Fold in the compute the build generated: functions, crons, queue workers and
|
|
97
|
-
* topic subscribers.
|
|
98
|
-
*
|
|
99
|
-
* All four are named by their *export*, the same way an endpoint's handler is —
|
|
100
|
-
* `CronGenerator` uses the export key as the name — so none of them can declare
|
|
101
|
-
* themselves and all of them are the build's to add. That is why a `Function`
|
|
102
|
-
* has no `declare()` and never will.
|
|
103
|
-
*
|
|
104
|
-
* A worker and a subscriber **nest** inside the queue or topic that triggers
|
|
105
|
-
* them, because position carries the trigger. A function and a cron are
|
|
106
|
-
* top-level, because nothing triggers them but a caller or a clock.
|
|
107
|
-
*/
|
|
108
|
-
|
|
109
111
|
/**
|
|
110
112
|
* The manifest as TypeScript source.
|
|
111
113
|
*
|
|
112
114
|
* Pure, so what gets written can be asserted without touching a filesystem.
|
|
113
115
|
*/
|
|
114
|
-
declare function manifestModule(manifest: ConstructManifest, backends?: Backends): string;
|
|
116
|
+
export declare function manifestModule(manifest: ConstructManifest, backends?: Backends): string;
|
|
115
117
|
/** Write it where the deploy config expects to import it from. */
|
|
116
|
-
declare function writeManifestModule(manifest: ConstructManifest, appRoot: string, backends?: Backends): Promise<string>;
|
|
118
|
+
export declare function writeManifestModule(manifest: ConstructManifest, appRoot: string, backends?: Backends): Promise<string>;
|
|
117
119
|
/** The choices that are deployment config rather than declaration. */
|
|
118
120
|
interface Backends {
|
|
119
121
|
cache?: CacheBackend;
|
|
120
122
|
email?: EmailBackend;
|
|
121
123
|
}
|
|
122
|
-
//# sourceMappingURL=emit.d.ts.map
|
|
123
124
|
//#endregion
|
|
124
|
-
export { DiscoverOptions
|
|
125
|
+
export type { DiscoverOptions };
|
|
125
126
|
//# sourceMappingURL=public.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public.d.cts","names":[],"sources":["../../src/reconcile/discover.ts","../../src/reconcile/emit.ts"],"
|
|
1
|
+
{"version":3,"file":"public.d.cts","names":[],"sources":["../../src/reconcile/discover.ts","../../src/reconcile/emit.ts"],"mappings":";;;;UA8BU;EACT;EACA,WAAW;;;;;;;;;wBAUI,aAAa,iBAAiB,SAAS;UAYtC;;EAEhB;EACA;;;;;;;EAOA;;;;;;;;;;;;EAYA,UAAU,eAAe;;;;;;;;;;;;EAYzB,YAAY;;;UAII;;EAEhB;;EAEA;;;;;;;EAOA;;;;;;;;;;wBAWqB,SACrB,SAAS,kBACP,QAAQ;;;;qBC/EE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBA8BG,WACf,UAAU,mBACV,iBAAiB,aACjB;EAAW;IACT;;;;;;wBA2Ja,eACf,UAAU,mBACV,WAAU;;wBA8BW,oBACrB,UAAU,mBACV,iBACA,WAAU,WACR;;UAUc;EAChB,QAAQ;EACR,QAAQ"}
|