@geekmidas/cli 10.0.0-alpha.63 → 10.0.0-alpha.65
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 +1 -1
- package/dist/{HostingerProvider-mJ7YruxH.cjs → HostingerProvider-Dlmbfass.cjs} +3 -3
- package/dist/{HostingerProvider-mJ7YruxH.cjs.map → HostingerProvider-Dlmbfass.cjs.map} +1 -1
- package/dist/{HostingerProvider-DBBwpDjn.mjs → HostingerProvider-x9gQTAGZ.mjs} +2 -2
- package/dist/{HostingerProvider-DBBwpDjn.mjs.map → HostingerProvider-x9gQTAGZ.mjs.map} +1 -1
- package/dist/{LegacyStateStore-CP7SZwTP.cjs → LegacyStateStore-BGoo__sO.cjs} +2 -2
- package/dist/{LegacyStateStore-CP7SZwTP.cjs.map → LegacyStateStore-BGoo__sO.cjs.map} +1 -1
- package/dist/{LegacyStateStore-Q3GmKOdw.mjs → LegacyStateStore-BUu1nQ5u.mjs} +2 -2
- package/dist/{LegacyStateStore-Q3GmKOdw.mjs.map → LegacyStateStore-BUu1nQ5u.mjs.map} +1 -1
- package/dist/{LocalStateStore-sS8mvLeq.cjs → LocalStateStore-93urXyQe.cjs} +2 -2
- package/dist/{LocalStateStore-sS8mvLeq.cjs.map → LocalStateStore-93urXyQe.cjs.map} +1 -1
- package/dist/{LocalStateStore-BGIXcrvX.mjs → LocalStateStore-BRhMQRuy.mjs} +2 -2
- package/dist/{LocalStateStore-BGIXcrvX.mjs.map → LocalStateStore-BRhMQRuy.mjs.map} +1 -1
- package/dist/{OpenApiTsGenerator-CjHWFRDd.cjs → OpenApiTsGenerator-BWuDRHe9.cjs} +2 -2
- package/dist/{OpenApiTsGenerator-CjHWFRDd.cjs.map → OpenApiTsGenerator-BWuDRHe9.cjs.map} +1 -1
- package/dist/{OpenApiTsGenerator-CnAwhGxi.mjs → OpenApiTsGenerator-C6v-IeHt.mjs} +2 -2
- package/dist/{OpenApiTsGenerator-CnAwhGxi.mjs.map → OpenApiTsGenerator-C6v-IeHt.mjs.map} +1 -1
- package/dist/{S3StateStore-6jqITH2F.cjs → S3StateStore-F4dxw6kS.cjs} +2 -2
- package/dist/{S3StateStore-6jqITH2F.cjs.map → S3StateStore-F4dxw6kS.cjs.map} +1 -1
- package/dist/{S3StateStore-D7l_lWJ3.mjs → S3StateStore-pw1GhWwt.mjs} +2 -2
- package/dist/{S3StateStore-D7l_lWJ3.mjs.map → S3StateStore-pw1GhWwt.mjs.map} +1 -1
- package/dist/{SSMStateStore-B__F7IJa.cjs → SSMStateStore-Bt9acuAF.cjs} +2 -2
- package/dist/{SSMStateStore-B__F7IJa.cjs.map → SSMStateStore-Bt9acuAF.cjs.map} +1 -1
- package/dist/{SSMStateStore-CAQDVpoc.mjs → SSMStateStore-Dmj7zzFO.mjs} +2 -2
- package/dist/{SSMStateStore-CAQDVpoc.mjs.map → SSMStateStore-Dmj7zzFO.mjs.map} +1 -1
- package/dist/{bundler-DLfOn0ls.cjs → bundler-D6MlIfBT.cjs} +3 -3
- package/dist/{bundler-DLfOn0ls.cjs.map → bundler-D6MlIfBT.cjs.map} +1 -1
- package/dist/{bundler-DyWI41Iy.mjs → bundler-D_14JLZF.mjs} +3 -3
- package/dist/{bundler-DyWI41Iy.mjs.map → bundler-D_14JLZF.mjs.map} +1 -1
- package/dist/{cli-D-ZM-X2s.cjs → cli-BfH1deAT.cjs} +8 -7
- package/dist/{cli-D-ZM-X2s.cjs.map → cli-BfH1deAT.cjs.map} +1 -1
- package/dist/{cli-PkVF28LM.mjs → cli-P56vvrDQ.mjs} +5 -4
- package/dist/{cli-PkVF28LM.mjs.map → cli-P56vvrDQ.mjs.map} +1 -1
- package/dist/compose-391oEKj1.cjs +1982 -0
- package/dist/compose-391oEKj1.cjs.map +1 -0
- package/dist/compose-DprE3ASm.mjs +1862 -0
- package/dist/compose-DprE3ASm.mjs.map +1 -0
- package/dist/{config-C2BUosLm.d.mts → config-BonvTW8p.d.mts} +3 -3
- package/dist/{config-C2BUosLm.d.mts.map → config-BonvTW8p.d.mts.map} +1 -1
- package/dist/{config-CemHVcIr.d.cts → config-V_X_BMpm.d.cts} +3 -3
- package/dist/{config-CemHVcIr.d.cts.map → config-V_X_BMpm.d.cts.map} +1 -1
- package/dist/config.cjs +416 -17
- package/dist/config.cjs.map +1 -0
- package/dist/config.d.cts +3 -3
- package/dist/config.d.mts +3 -3
- package/dist/config.mjs +401 -2
- package/dist/config.mjs.map +1 -0
- package/dist/credentials-Bzbg23n8.cjs +298 -0
- package/dist/credentials-Bzbg23n8.cjs.map +1 -0
- package/dist/credentials-Dq1MnWQf.mjs +233 -0
- package/dist/credentials-Dq1MnWQf.mjs.map +1 -0
- package/dist/{define-CHBIEc5j.mjs → define-DR8xGYfL.mjs} +6 -3
- package/dist/define-DR8xGYfL.mjs.map +1 -0
- package/dist/{define-Cv_QvDrg.cjs → define-DzRz8FgH.cjs} +6 -3
- package/dist/define-DzRz8FgH.cjs.map +1 -0
- package/dist/deploy/public.cjs +39 -33
- package/dist/deploy/public.cjs.map +1 -1
- package/dist/deploy/public.d.cts +44 -5
- package/dist/deploy/public.d.cts.map +1 -1
- package/dist/deploy/public.d.mts +44 -5
- package/dist/deploy/public.d.mts.map +1 -1
- package/dist/deploy/public.mjs +15 -12
- package/dist/deploy/public.mjs.map +1 -1
- package/dist/{deploy-CGW-QY9D.mjs → deploy-9MBG_QLd.mjs} +11 -9
- package/dist/deploy-9MBG_QLd.mjs.map +1 -0
- package/dist/{deploy-BiMrP9i7.cjs → deploy-B80idPUZ.cjs} +17 -15
- package/dist/deploy-B80idPUZ.cjs.map +1 -0
- package/dist/{deploy-CG8bfFij.mjs → deploy-CZ5nlpsu.mjs} +16 -119
- package/dist/deploy-CZ5nlpsu.mjs.map +1 -0
- package/dist/{deploy-ABBI0lf1.cjs → deploy-Cy8igpQF.cjs} +48 -151
- package/dist/deploy-Cy8igpQF.cjs.map +1 -0
- package/dist/derive-B4vDpzgP.mjs +150 -0
- package/dist/derive-B4vDpzgP.mjs.map +1 -0
- package/dist/derive-DAbo49sm.cjs +161 -0
- package/dist/derive-DAbo49sm.cjs.map +1 -0
- package/dist/{discover-C_6COHEv.cjs → discover-DHdECueV.cjs} +6 -157
- package/dist/discover-DHdECueV.cjs.map +1 -0
- package/dist/{discover-BaT6bpnA.mjs → discover-LMrqsWSU.mjs} +8 -117
- package/dist/discover-LMrqsWSU.mjs.map +1 -0
- package/dist/{dokploy-CwdmlyQL.mjs → dokploy-BdtBePjp.mjs} +3 -3
- package/dist/{dokploy-CwdmlyQL.mjs.map → dokploy-BdtBePjp.mjs.map} +1 -1
- package/dist/{dokploy-QJxMhxdf.cjs → dokploy-iFY8MY-L.cjs} +3 -3
- package/dist/{dokploy-QJxMhxdf.cjs.map → dokploy-iFY8MY-L.cjs.map} +1 -1
- package/dist/{encryption-DXw3jj0c.cjs → encryption-B1wJwgae.cjs} +6 -6
- package/dist/{encryption-DXw3jj0c.cjs.map → encryption-B1wJwgae.cjs.map} +1 -1
- package/dist/{encryption-CbQG_ayW.mjs → encryption-DF_hgIpl.mjs} +5 -5
- package/dist/{encryption-CbQG_ayW.mjs.map → encryption-DF_hgIpl.mjs.map} +1 -1
- package/dist/{github-ciwbIwVa.mjs → github-B0YYE98x.mjs} +4 -4
- package/dist/{github-ciwbIwVa.mjs.map → github-B0YYE98x.mjs.map} +1 -1
- package/dist/{github-BH_mETav.cjs → github-CYcmTdFs.cjs} +4 -4
- package/dist/{github-BH_mETav.cjs.map → github-CYcmTdFs.cjs.map} +1 -1
- package/dist/{index-DrKzpHl-.d.mts → index-DPUAGJaK.d.cts} +16 -3
- package/dist/index-DPUAGJaK.d.cts.map +1 -0
- package/dist/{index-BtXDZQuF.d.cts → index-DwKD_8lt.d.mts} +16 -3
- package/dist/index-DwKD_8lt.d.mts.map +1 -0
- package/dist/index.cjs +280 -1849
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +221 -1774
- package/dist/index.mjs.map +1 -1
- package/dist/local-BdH4XN0E.cjs +409 -0
- package/dist/local-BdH4XN0E.cjs.map +1 -0
- package/dist/local-CWR4Y_kW.mjs +350 -0
- package/dist/local-CWR4Y_kW.mjs.map +1 -0
- package/dist/{migrate-ImBKTBcW.mjs → migrate-B2qL5wj4.mjs} +8 -7
- package/dist/{migrate-ImBKTBcW.mjs.map → migrate-B2qL5wj4.mjs.map} +1 -1
- package/dist/{migrate-6oa9y_f2.cjs → migrate-q4pPAXtO.cjs} +21 -20
- package/dist/{migrate-6oa9y_f2.cjs.map → migrate-q4pPAXtO.cjs.map} +1 -1
- package/dist/openapi.cjs +2 -2
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +2 -2
- package/dist/provider-C9_-45d5.cjs +56 -0
- package/dist/provider-C9_-45d5.cjs.map +1 -0
- package/dist/provider-q4lsYzYG.mjs +45 -0
- package/dist/provider-q4lsYzYG.mjs.map +1 -0
- package/dist/{ready-Bpdnn2ON.mjs → ready-bQfu9GES.mjs} +3 -3
- package/dist/{ready-Bpdnn2ON.mjs.map → ready-bQfu9GES.mjs.map} +1 -1
- package/dist/{ready-BiG0rt4h.cjs → ready-brEhh68f.cjs} +3 -3
- package/dist/{ready-BiG0rt4h.cjs.map → ready-brEhh68f.cjs.map} +1 -1
- package/dist/reconcile/public.cjs +1 -1
- package/dist/reconcile/public.d.cts +1 -1
- package/dist/reconcile/public.d.mts +1 -1
- package/dist/reconcile/public.mjs +1 -1
- package/dist/resolve-DLkIXTct.mjs +262 -0
- package/dist/resolve-DLkIXTct.mjs.map +1 -0
- package/dist/resolve-N_i4XNd-.cjs +315 -0
- package/dist/resolve-N_i4XNd-.cjs.map +1 -0
- package/dist/{runtime-Dn_n-29S.d.cts → runtime-BnD7Nn96.d.cts} +2 -2
- package/dist/{runtime-CXPlkZzs.d.mts.map → runtime-BnD7Nn96.d.cts.map} +1 -1
- package/dist/{runtime-CXPlkZzs.d.mts → runtime-j-tmFL65.d.mts} +2 -2
- package/dist/{runtime-Dn_n-29S.d.cts.map → runtime-j-tmFL65.d.mts.map} +1 -1
- package/dist/sandbox/discover-worker.cjs +1 -1
- package/dist/sandbox/discover-worker.mjs +1 -1
- package/dist/sandbox-CL--ZDui.cjs +164 -0
- package/dist/sandbox-CL--ZDui.cjs.map +1 -0
- package/dist/sandbox-Dm247NGx.mjs +117 -0
- package/dist/sandbox-Dm247NGx.mjs.map +1 -0
- package/dist/sniffer-BSwaL-dX.cjs +2236 -0
- package/dist/sniffer-BSwaL-dX.cjs.map +1 -0
- package/dist/sniffer-BZ-OJO4Y.mjs +2068 -0
- package/dist/sniffer-BZ-OJO4Y.mjs.map +1 -0
- package/dist/sst-B2HbrDR3.mjs +430 -0
- package/dist/sst-B2HbrDR3.mjs.map +1 -0
- package/dist/sst-DcfsVH6x.cjs +453 -0
- package/dist/sst-DcfsVH6x.cjs.map +1 -0
- package/dist/{sniffer-aO_-s5vq.mjs → state-B8YwmUZU.mjs} +1204 -3236
- package/dist/state-B8YwmUZU.mjs.map +1 -0
- package/dist/{sniffer-D_SYc_pO.cjs → state-DF26e_lo.cjs} +1251 -3494
- package/dist/state-DF26e_lo.cjs.map +1 -0
- package/dist/{store-KvNQwYaB.cjs → store-CJF1Tqkq.cjs} +4 -4
- package/dist/{store-KvNQwYaB.cjs.map → store-CJF1Tqkq.cjs.map} +1 -1
- package/dist/{store-BS8A7NcA.mjs → store-CLiHnqOY.mjs} +2 -2
- package/dist/{store-BS8A7NcA.mjs.map → store-CLiHnqOY.mjs.map} +1 -1
- package/dist/target/public.cjs +6 -6
- package/dist/target/public.d.cts +3 -3
- package/dist/target/public.d.mts +3 -3
- package/dist/target/public.mjs +3 -3
- package/dist/terminal-Bs5E1oxm.cjs +88 -0
- package/dist/terminal-Bs5E1oxm.cjs.map +1 -0
- package/dist/terminal-CfbiYUbZ.mjs +84 -0
- package/dist/terminal-CfbiYUbZ.mjs.map +1 -0
- package/dist/{types-DIA7Li-J.d.cts → types-BCzMWBb6.d.mts} +84 -17
- package/dist/types-BCzMWBb6.d.mts.map +1 -0
- package/dist/{types-CfLKGfC_.d.mts → types-ByMWkXKP.d.cts} +85 -16
- package/dist/types-ByMWkXKP.d.cts.map +1 -0
- package/dist/utils-D2Nk1Nv2.cjs +205 -0
- package/dist/utils-D2Nk1Nv2.cjs.map +1 -0
- package/dist/utils-D_fxUqvU.mjs +156 -0
- package/dist/utils-D_fxUqvU.mjs.map +1 -0
- package/dist/vitest.cjs +1 -1
- package/dist/vitest.mjs +1 -1
- package/dist/workspace/index.cjs +3 -1
- package/dist/workspace/index.d.cts +3 -3
- package/dist/workspace/index.d.mts +3 -3
- package/dist/workspace/index.mjs +2 -2
- package/dist/{workspace-Kl4NG9IH.cjs → workspace-BewAHZIo.cjs} +41 -6
- package/dist/workspace-BewAHZIo.cjs.map +1 -0
- package/dist/{workspace-Dh7rr7l4.mjs → workspace-C06_pW4g.mjs} +30 -7
- package/dist/workspace-C06_pW4g.mjs.map +1 -0
- package/package.json +13 -13
- package/dist/config-BEJYKd4s.cjs +0 -648
- package/dist/config-BEJYKd4s.cjs.map +0 -1
- package/dist/config-LKKCVCnC.mjs +0 -547
- package/dist/config-LKKCVCnC.mjs.map +0 -1
- package/dist/define-CHBIEc5j.mjs.map +0 -1
- package/dist/define-Cv_QvDrg.cjs.map +0 -1
- package/dist/deploy-ABBI0lf1.cjs.map +0 -1
- package/dist/deploy-BiMrP9i7.cjs.map +0 -1
- package/dist/deploy-CG8bfFij.mjs.map +0 -1
- package/dist/deploy-CGW-QY9D.mjs.map +0 -1
- package/dist/discover-BaT6bpnA.mjs.map +0 -1
- package/dist/discover-C_6COHEv.cjs.map +0 -1
- package/dist/home-DOzBaWaL.cjs +0 -33
- package/dist/home-DOzBaWaL.cjs.map +0 -1
- package/dist/home-GI7pJl1M.mjs +0 -22
- package/dist/home-GI7pJl1M.mjs.map +0 -1
- package/dist/index-BtXDZQuF.d.cts.map +0 -1
- package/dist/index-DrKzpHl-.d.mts.map +0 -1
- package/dist/resolve-B1T0hUaR.mjs +0 -170
- package/dist/resolve-B1T0hUaR.mjs.map +0 -1
- package/dist/resolve-xytBTtG4.cjs +0 -217
- package/dist/resolve-xytBTtG4.cjs.map +0 -1
- package/dist/runtime-CMwtq0AL.cjs +0 -163
- package/dist/runtime-CMwtq0AL.cjs.map +0 -1
- package/dist/runtime-DbakJS8u.mjs +0 -134
- package/dist/runtime-DbakJS8u.mjs.map +0 -1
- package/dist/sniffer-D_SYc_pO.cjs.map +0 -1
- package/dist/sniffer-aO_-s5vq.mjs.map +0 -1
- package/dist/terminal-D0f1u0LG.cjs +0 -576
- package/dist/terminal-D0f1u0LG.cjs.map +0 -1
- package/dist/terminal-gMxAo9Dj.mjs +0 -517
- package/dist/terminal-gMxAo9Dj.mjs.map +0 -1
- package/dist/types-CfLKGfC_.d.mts.map +0 -1
- package/dist/types-DIA7Li-J.d.cts.map +0 -1
- package/dist/workspace-Dh7rr7l4.mjs.map +0 -1
- package/dist/workspace-Kl4NG9IH.cjs.map +0 -1
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { kebabCase } from "@geekmidas/manifest";
|
|
2
|
+
//#region src/workspace/derive.ts
|
|
3
|
+
/**
|
|
4
|
+
* The apps a manifest implies.
|
|
5
|
+
*
|
|
6
|
+
* `gkm.config.ts` used to carry an `apps` block naming every app: its type, its
|
|
7
|
+
* path, its port, its framework, which other apps it depended on. Every one of
|
|
8
|
+
* those facts was already declared. A `StaticSite('Web', { path: 'apps/web' })`
|
|
9
|
+
* and an `apps.web = { type: 'web', path: 'apps/web', framework: 'vite',
|
|
10
|
+
* dependencies: ['api'] }` are the same statement written twice, and only one of
|
|
11
|
+
* them was checked against anything — so the copy in config was free to drift,
|
|
12
|
+
* and a surface config had no entry for simply never deployed.
|
|
13
|
+
*
|
|
14
|
+
* So the list comes from the graph. A `site` is an app, and so is every
|
|
15
|
+
* `rest-api` — one surface, one container, at the `path` it declared.
|
|
16
|
+
*
|
|
17
|
+
* What config still supplies is what no graph can answer: the backends a cache
|
|
18
|
+
* and a mailer resolve to, the deploy endpoint, the stage's domain.
|
|
19
|
+
*/
|
|
20
|
+
/** Which frontend framework builds each site variant. */
|
|
21
|
+
const FRAMEWORKS = {
|
|
22
|
+
static: "vite",
|
|
23
|
+
next: "nextjs",
|
|
24
|
+
tanstack: "tanstack-start"
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* The app key a construct id becomes.
|
|
28
|
+
*
|
|
29
|
+
* The same kebab form every physical name is built from, so `Api` is `api` in
|
|
30
|
+
* the config, `production-kitchen-sink-api` on Dokploy, and `API_URL` in an
|
|
31
|
+
* environment — one id, three renderings, no second list.
|
|
32
|
+
*/
|
|
33
|
+
function appKey(id) {
|
|
34
|
+
return kebabCase(id);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The app serving a surface: its own, always.
|
|
38
|
+
*
|
|
39
|
+
* Every surface gets a container. There is no arrangement in which one runs
|
|
40
|
+
* inside another — two surfaces in a process share a filesystem, an
|
|
41
|
+
* environment, and every credential either was granted, so an auth server
|
|
42
|
+
* beside an API is one bug in the API away from being read by it.
|
|
43
|
+
*/
|
|
44
|
+
function hostOf(manifest, id) {
|
|
45
|
+
const declaration = manifest[id];
|
|
46
|
+
if (!declaration || declaration.kind !== "rest-api") return void 0;
|
|
47
|
+
return id;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Local ports, assigned so that adding a site does not renumber the others.
|
|
51
|
+
*
|
|
52
|
+
* Backends first, then the site holding the base domain, then the rest
|
|
53
|
+
* alphabetically. The ordering is arbitrary but it is *fixed*, which is the
|
|
54
|
+
* property that matters: a port that moves when an unrelated app is added is a
|
|
55
|
+
* port nobody can put in a bookmark. A construct that names its own port keeps
|
|
56
|
+
* it, and is skipped here.
|
|
57
|
+
*/
|
|
58
|
+
function assignPorts(apps) {
|
|
59
|
+
const rank = (name, app) => [
|
|
60
|
+
app.type === "backend" ? 0 : app.type === "web" ? 1 : 2,
|
|
61
|
+
app.root ? 0 : 1,
|
|
62
|
+
name
|
|
63
|
+
];
|
|
64
|
+
const ordered = Object.entries(apps).sort(([aName, a], [bName, b]) => {
|
|
65
|
+
const [at, ar, an] = rank(aName, a);
|
|
66
|
+
const [bt, br, bn] = rank(bName, b);
|
|
67
|
+
return at - bt || ar - br || an.localeCompare(bn);
|
|
68
|
+
});
|
|
69
|
+
const taken = new Set(ordered.map(([, app]) => app.port).filter((p) => !!p));
|
|
70
|
+
let next = 3e3;
|
|
71
|
+
for (const [, app] of ordered) {
|
|
72
|
+
if (app.port) continue;
|
|
73
|
+
while (taken.has(next)) next += 1;
|
|
74
|
+
app.port = next;
|
|
75
|
+
taken.add(next);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Whichever site holds the base domain.
|
|
80
|
+
*
|
|
81
|
+
* The convention first — a site named `web` — because it is one people already
|
|
82
|
+
* rely on. Then an explicit `root: true`. A project with one site needs neither.
|
|
83
|
+
*/
|
|
84
|
+
function markRoot(apps) {
|
|
85
|
+
const sites = Object.entries(apps).filter(([, a]) => a.type === "web");
|
|
86
|
+
if (sites.length === 0) return;
|
|
87
|
+
if (sites.some(([, a]) => a.root)) return;
|
|
88
|
+
const chosen = sites.find(([name]) => name === "web") ?? (sites.length === 1 ? sites[0] : void 0);
|
|
89
|
+
if (chosen) chosen[1].root = true;
|
|
90
|
+
}
|
|
91
|
+
function derivedApps(manifest, workspace) {
|
|
92
|
+
const apps = {};
|
|
93
|
+
const defaultTarget = workspace.deploy?.default ?? "dokploy";
|
|
94
|
+
for (const [id, declaration] of Object.entries(manifest)) {
|
|
95
|
+
if (declaration.kind !== "site" && declaration.kind !== "mobile-app" && declaration.kind !== "rest-api") continue;
|
|
96
|
+
const spec = declaration.kind === "site" || declaration.kind === "mobile-app" ? declaration.app : {
|
|
97
|
+
path: declaration.path,
|
|
98
|
+
...declaration.telescope ? { telescope: true } : {}
|
|
99
|
+
};
|
|
100
|
+
const name = appKey(id);
|
|
101
|
+
const configured = workspace.apps[name];
|
|
102
|
+
apps[name] = {
|
|
103
|
+
...configured,
|
|
104
|
+
type: declaration.kind === "site" ? "web" : declaration.kind === "mobile-app" ? "mobile" : "backend",
|
|
105
|
+
path: spec.path,
|
|
106
|
+
port: spec.port ?? configured?.port ?? 0,
|
|
107
|
+
...(declaration.kind === "rest-api" || declaration.kind === "site") && declaration.subdomain ? { subdomain: declaration.subdomain } : {},
|
|
108
|
+
dependencies: [],
|
|
109
|
+
resolvedDeployTarget: configured?.resolvedDeployTarget ?? defaultTarget,
|
|
110
|
+
...declaration.kind === "site" ? {
|
|
111
|
+
framework: configured?.framework ?? FRAMEWORKS[declaration.variant],
|
|
112
|
+
...declaration.root ? { root: true } : {},
|
|
113
|
+
...spec.config ? { config: spec.config } : {}
|
|
114
|
+
} : {},
|
|
115
|
+
...declaration.kind === "mobile-app" ? {
|
|
116
|
+
framework: configured?.framework ?? declaration.variant,
|
|
117
|
+
...spec.config ? { config: spec.config } : {}
|
|
118
|
+
} : {},
|
|
119
|
+
...spec.telescope !== void 0 ? { telescope: spec.telescope } : {},
|
|
120
|
+
...spec.openapi !== void 0 ? { openapi: spec.openapi } : {},
|
|
121
|
+
...spec.runtime !== void 0 ? { runtime: spec.runtime } : {},
|
|
122
|
+
...spec.env !== void 0 ? { env: spec.env } : {},
|
|
123
|
+
...spec.entry !== void 0 ? { entry: spec.entry } : {},
|
|
124
|
+
...configured?.domain ? { domain: configured.domain } : {}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
for (const [id, declaration] of Object.entries(manifest)) {
|
|
128
|
+
if (declaration.kind !== "site" && declaration.kind !== "mobile-app" && declaration.kind !== "rest-api") continue;
|
|
129
|
+
const app = apps[appKey(id)];
|
|
130
|
+
if (!app) continue;
|
|
131
|
+
const edges = declaration.kind === "rest-api" ? declaration.calls ?? [] : declaration.dependencies ?? [];
|
|
132
|
+
const names = /* @__PURE__ */ new Set();
|
|
133
|
+
for (const edge of edges) {
|
|
134
|
+
if (edge.kind !== "rest-api") continue;
|
|
135
|
+
const host = hostOf(manifest, edge.target);
|
|
136
|
+
if (!host) continue;
|
|
137
|
+
const key = appKey(host);
|
|
138
|
+
if (key !== appKey(id) && apps[key]) names.add(key);
|
|
139
|
+
}
|
|
140
|
+
app.dependencies = [...names].sort();
|
|
141
|
+
}
|
|
142
|
+
markRoot(apps);
|
|
143
|
+
assignPorts(apps);
|
|
144
|
+
for (const [name, app] of Object.entries(workspace.apps)) if (!apps[name]) apps[name] = app;
|
|
145
|
+
return apps;
|
|
146
|
+
}
|
|
147
|
+
//#endregion
|
|
148
|
+
export { derivedApps as n, appKey as t };
|
|
149
|
+
|
|
150
|
+
//# sourceMappingURL=derive-B4vDpzgP.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive-B4vDpzgP.mjs","names":[],"sources":["../src/workspace/derive.ts"],"sourcesContent":["/**\n * The apps a manifest implies.\n *\n * `gkm.config.ts` used to carry an `apps` block naming every app: its type, its\n * path, its port, its framework, which other apps it depended on. Every one of\n * those facts was already declared. A `StaticSite('Web', { path: 'apps/web' })`\n * and an `apps.web = { type: 'web', path: 'apps/web', framework: 'vite',\n * dependencies: ['api'] }` are the same statement written twice, and only one of\n * them was checked against anything — so the copy in config was free to drift,\n * and a surface config had no entry for simply never deployed.\n *\n * So the list comes from the graph. A `site` is an app, and so is every\n * `rest-api` — one surface, one container, at the `path` it declared.\n *\n * What config still supplies is what no graph can answer: the backends a cache\n * and a mailer resolve to, the deploy endpoint, the stage's domain.\n */\n\nimport {\n\ttype AppSpec,\n\ttype ConstructManifest,\n\tkebabCase,\n} from '@geekmidas/manifest';\nimport type {\n\tAppDomainConfig,\n\tDeployTargetName,\n\tFramework,\n\tNormalizedAppConfig,\n\tNormalizedWorkspace,\n} from './types.js';\n\n/** Which frontend framework builds each site variant. */\nconst FRAMEWORKS: Record<string, Framework> = {\n\tstatic: 'vite',\n\tnext: 'nextjs',\n\ttanstack: 'tanstack-start',\n};\n\n/**\n * The app key a construct id becomes.\n *\n * The same kebab form every physical name is built from, so `Api` is `api` in\n * the config, `production-kitchen-sink-api` on Dokploy, and `API_URL` in an\n * environment — one id, three renderings, no second list.\n */\nexport function appKey(id: string): string {\n\treturn kebabCase(id);\n}\n\n/**\n * The app serving a surface: its own, always.\n *\n * Every surface gets a container. There is no arrangement in which one runs\n * inside another — two surfaces in a process share a filesystem, an\n * environment, and every credential either was granted, so an auth server\n * beside an API is one bug in the API away from being read by it.\n */\nexport function hostOf(\n\tmanifest: ConstructManifest,\n\tid: string,\n): string | undefined {\n\tconst declaration = manifest[id];\n\tif (!declaration || declaration.kind !== 'rest-api') return undefined;\n\n\treturn id;\n}\n\n/**\n * Local ports, assigned so that adding a site does not renumber the others.\n *\n * Backends first, then the site holding the base domain, then the rest\n * alphabetically. The ordering is arbitrary but it is *fixed*, which is the\n * property that matters: a port that moves when an unrelated app is added is a\n * port nobody can put in a bookmark. A construct that names its own port keeps\n * it, and is skipped here.\n */\nfunction assignPorts(apps: Record<string, NormalizedAppConfig>): void {\n\tconst rank = (\n\t\tname: string,\n\t\tapp: NormalizedAppConfig,\n\t): [number, number, string] => [\n\t\tapp.type === 'backend' ? 0 : app.type === 'web' ? 1 : 2,\n\t\tapp.root ? 0 : 1,\n\t\tname,\n\t];\n\n\tconst ordered = Object.entries(apps).sort(([aName, a], [bName, b]) => {\n\t\tconst [at, ar, an] = rank(aName, a);\n\t\tconst [bt, br, bn] = rank(bName, b);\n\t\treturn at - bt || ar - br || an.localeCompare(bn);\n\t});\n\n\tconst taken = new Set(\n\t\tordered.map(([, app]) => app.port).filter((p): p is number => !!p),\n\t);\n\n\tlet next = 3000;\n\tfor (const [, app] of ordered) {\n\t\tif (app.port) continue;\n\t\twhile (taken.has(next)) next += 1;\n\t\tapp.port = next;\n\t\ttaken.add(next);\n\t}\n}\n\n/**\n * Whichever site holds the base domain.\n *\n * The convention first — a site named `web` — because it is one people already\n * rely on. Then an explicit `root: true`. A project with one site needs neither.\n */\nfunction markRoot(apps: Record<string, NormalizedAppConfig>): void {\n\tconst sites = Object.entries(apps).filter(([, a]) => a.type === 'web');\n\tif (sites.length === 0) return;\n\tif (sites.some(([, a]) => a.root)) return;\n\n\tconst chosen =\n\t\tsites.find(([name]) => name === 'web') ??\n\t\t(sites.length === 1 ? sites[0] : undefined);\n\tif (chosen) chosen[1].root = true;\n}\n\nexport function derivedApps(\n\tmanifest: ConstructManifest,\n\tworkspace: NormalizedWorkspace,\n): Record<string, NormalizedAppConfig> {\n\tconst apps: Record<string, NormalizedAppConfig> = {};\n\tconst defaultTarget: DeployTargetName =\n\t\tworkspace.deploy?.default ?? 'dokploy';\n\n\tfor (const [id, declaration] of Object.entries(manifest)) {\n\t\t// Three kinds are apps: a site, a mobile app and an HTTP surface. A worker\n\t\t// is not one — it names the process that runs a cron or a subscriber, and\n\t\t// that process is the app's server, which already exists. Everything else\n\t\t// is a resource one of them uses.\n\t\tif (\n\t\t\tdeclaration.kind !== 'site' &&\n\t\t\tdeclaration.kind !== 'mobile-app' &&\n\t\t\tdeclaration.kind !== 'rest-api'\n\t\t)\n\t\t\tcontinue;\n\n\t\t// No opt-in to be had. Each says where it lives.\n\t\tconst spec: AppSpec =\n\t\t\tdeclaration.kind === 'site' || declaration.kind === 'mobile-app'\n\t\t\t\t? declaration.app\n\t\t\t\t: {\n\t\t\t\t\t\tpath: declaration.path,\n\t\t\t\t\t\t...(declaration.telescope ? { telescope: true } : {}),\n\t\t\t\t\t};\n\n\t\tconst name = appKey(id);\n\t\t// A config entry of the same name still wins, so a workspace can override\n\t\t// one field without restating the app. Nothing in this repo needs to.\n\t\tconst configured = workspace.apps[name];\n\n\t\tapps[name] = {\n\t\t\t...configured,\n\t\t\ttype:\n\t\t\t\tdeclaration.kind === 'site'\n\t\t\t\t\t? 'web'\n\t\t\t\t\t: declaration.kind === 'mobile-app'\n\t\t\t\t\t\t? 'mobile'\n\t\t\t\t\t\t: 'backend',\n\t\t\tpath: spec.path,\n\t\t\tport: spec.port ?? configured?.port ?? 0,\n\t\t\t// The label it answers on, under a stage's domain and locally alike.\n\t\t\t...((declaration.kind === 'rest-api' || declaration.kind === 'site') &&\n\t\t\tdeclaration.subdomain\n\t\t\t\t? { subdomain: declaration.subdomain }\n\t\t\t\t: {}),\n\t\t\tdependencies: [],\n\t\t\tresolvedDeployTarget: configured?.resolvedDeployTarget ?? defaultTarget,\n\t\t\t...(declaration.kind === 'site'\n\t\t\t\t? {\n\t\t\t\t\t\tframework: configured?.framework ?? FRAMEWORKS[declaration.variant],\n\t\t\t\t\t\t...(declaration.root ? { root: true } : {}),\n\t\t\t\t\t\t...(spec.config ? { config: spec.config } : {}),\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t\t...(declaration.kind === 'mobile-app'\n\t\t\t\t? {\n\t\t\t\t\t\tframework: configured?.framework ?? declaration.variant,\n\t\t\t\t\t\t...(spec.config ? { config: spec.config } : {}),\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t\t// One glob fans out to the six the build reads, because each generator\n\t\t\t// already inspects every export and keeps what it recognises. A\n\t\t\t// per-kind field still wins where one was given.\n\t\t\t...(spec.telescope !== undefined ? { telescope: spec.telescope } : {}),\n\t\t\t...(spec.openapi !== undefined ? { openapi: spec.openapi } : {}),\n\t\t\t...(spec.runtime !== undefined ? { runtime: spec.runtime } : {}),\n\t\t\t...(spec.env !== undefined ? { env: spec.env } : {}),\n\t\t\t...(spec.entry !== undefined ? { entry: spec.entry } : {}),\n\t\t\t...(configured?.domain\n\t\t\t\t? { domain: configured.domain as AppDomainConfig }\n\t\t\t\t: {}),\n\t\t} as NormalizedAppConfig;\n\t}\n\n\t// Build order, from the edges rather than from a hand-kept list. A site that\n\t// depends on a surface depends on the app that serves it — which for a\n\t// mounted auth server is its host, not a container that does not exist.\n\tfor (const [id, declaration] of Object.entries(manifest)) {\n\t\tif (\n\t\t\tdeclaration.kind !== 'site' &&\n\t\t\tdeclaration.kind !== 'mobile-app' &&\n\t\t\tdeclaration.kind !== 'rest-api'\n\t\t)\n\t\t\tcontinue;\n\n\t\tconst app = apps[appKey(id)];\n\t\tif (!app) continue;\n\n\t\t// A site names its edges `dependencies`; a surface names them `calls`.\n\t\tconst edges =\n\t\t\tdeclaration.kind === 'rest-api'\n\t\t\t\t? (declaration.calls ?? [])\n\t\t\t\t: (declaration.dependencies ?? []);\n\n\t\tconst names = new Set<string>();\n\t\tfor (const edge of edges) {\n\t\t\tif (edge.kind !== 'rest-api') continue;\n\t\t\tconst host = hostOf(manifest, edge.target);\n\t\t\tif (!host) continue;\n\t\t\tconst key = appKey(host);\n\t\t\tif (key !== appKey(id) && apps[key]) names.add(key);\n\t\t}\n\n\t\tapp.dependencies = [...names].sort();\n\t}\n\n\tmarkRoot(apps);\n\tassignPorts(apps);\n\n\t// Apps config declared that the graph knows nothing about — a `type: 'mobile'`\n\t// entry, or a hand-run process. Kept rather than dropped: config is still the\n\t// escape hatch for what nothing declares.\n\tfor (const [name, app] of Object.entries(workspace.apps)) {\n\t\tif (!apps[name]) apps[name] = app;\n\t}\n\n\treturn apps;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAgCA,MAAM,aAAwC;CAC7C,QAAQ;CACR,MAAM;CACN,UAAU;AACX;;;;;;;;AASA,SAAgB,OAAO,IAAoB;CAC1C,OAAO,UAAU,EAAE;AACpB;;;;;;;;;AAUA,SAAgB,OACf,UACA,IACqB;CACrB,MAAM,cAAc,SAAS;CAC7B,IAAI,CAAC,eAAe,YAAY,SAAS,YAAY,OAAO,KAAA;CAE5D,OAAO;AACR;;;;;;;;;;AAWA,SAAS,YAAY,MAAiD;CACrE,MAAM,QACL,MACA,QAC8B;EAC9B,IAAI,SAAS,YAAY,IAAI,IAAI,SAAS,QAAQ,IAAI;EACtD,IAAI,OAAO,IAAI;EACf;CACD;CAEA,MAAM,UAAU,OAAO,QAAQ,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,OAAO,OAAO;EACrE,MAAM,CAAC,IAAI,IAAI,MAAM,KAAK,OAAO,CAAC;EAClC,MAAM,CAAC,IAAI,IAAI,MAAM,KAAK,OAAO,CAAC;EAClC,OAAO,KAAK,MAAM,KAAK,MAAM,GAAG,cAAc,EAAE;CACjD,CAAC;CAED,MAAM,QAAQ,IAAI,IACjB,QAAQ,KAAK,GAAG,SAAS,IAAI,IAAI,CAAC,CAAC,QAAQ,MAAmB,CAAC,CAAC,CAAC,CAClE;CAEA,IAAI,OAAO;CACX,KAAK,MAAM,GAAG,QAAQ,SAAS;EAC9B,IAAI,IAAI,MAAM;EACd,OAAO,MAAM,IAAI,IAAI,GAAG,QAAQ;EAChC,IAAI,OAAO;EACX,MAAM,IAAI,IAAI;CACf;AACD;;;;;;;AAQA,SAAS,SAAS,MAAiD;CAClE,MAAM,QAAQ,OAAO,QAAQ,IAAI,CAAC,CAAC,QAAQ,GAAG,OAAO,EAAE,SAAS,KAAK;CACrE,IAAI,MAAM,WAAW,GAAG;CACxB,IAAI,MAAM,MAAM,GAAG,OAAO,EAAE,IAAI,GAAG;CAEnC,MAAM,SACL,MAAM,MAAM,CAAC,UAAU,SAAS,KAAK,MACpC,MAAM,WAAW,IAAI,MAAM,KAAK,KAAA;CAClC,IAAI,QAAQ,OAAO,EAAE,CAAC,OAAO;AAC9B;AAEA,SAAgB,YACf,UACA,WACsC;CACtC,MAAM,OAA4C,CAAC;CACnD,MAAM,gBACL,UAAU,QAAQ,WAAW;CAE9B,KAAK,MAAM,CAAC,IAAI,gBAAgB,OAAO,QAAQ,QAAQ,GAAG;EAKzD,IACC,YAAY,SAAS,UACrB,YAAY,SAAS,gBACrB,YAAY,SAAS,YAErB;EAGD,MAAM,OACL,YAAY,SAAS,UAAU,YAAY,SAAS,eACjD,YAAY,MACZ;GACA,MAAM,YAAY;GAClB,GAAI,YAAY,YAAY,EAAE,WAAW,KAAK,IAAI,CAAC;EACpD;EAEH,MAAM,OAAO,OAAO,EAAE;EAGtB,MAAM,aAAa,UAAU,KAAK;EAElC,KAAK,QAAQ;GACZ,GAAG;GACH,MACC,YAAY,SAAS,SAClB,QACA,YAAY,SAAS,eACpB,WACA;GACL,MAAM,KAAK;GACX,MAAM,KAAK,QAAQ,YAAY,QAAQ;GAEvC,IAAK,YAAY,SAAS,cAAc,YAAY,SAAS,WAC7D,YAAY,YACT,EAAE,WAAW,YAAY,UAAU,IACnC,CAAC;GACJ,cAAc,CAAC;GACf,sBAAsB,YAAY,wBAAwB;GAC1D,GAAI,YAAY,SAAS,SACtB;IACA,WAAW,YAAY,aAAa,WAAW,YAAY;IAC3D,GAAI,YAAY,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,GAAI,KAAK,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;GAC9C,IACC,CAAC;GACJ,GAAI,YAAY,SAAS,eACtB;IACA,WAAW,YAAY,aAAa,YAAY;IAChD,GAAI,KAAK,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;GAC9C,IACC,CAAC;GAIJ,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC;GACpE,GAAI,KAAK,YAAY,KAAA,IAAY,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;GAC9D,GAAI,KAAK,YAAY,KAAA,IAAY,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;GAC9D,GAAI,KAAK,QAAQ,KAAA,IAAY,EAAE,KAAK,KAAK,IAAI,IAAI,CAAC;GAClD,GAAI,KAAK,UAAU,KAAA,IAAY,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;GACxD,GAAI,YAAY,SACb,EAAE,QAAQ,WAAW,OAA0B,IAC/C,CAAC;EACL;CACD;CAKA,KAAK,MAAM,CAAC,IAAI,gBAAgB,OAAO,QAAQ,QAAQ,GAAG;EACzD,IACC,YAAY,SAAS,UACrB,YAAY,SAAS,gBACrB,YAAY,SAAS,YAErB;EAED,MAAM,MAAM,KAAK,OAAO,EAAE;EAC1B,IAAI,CAAC,KAAK;EAGV,MAAM,QACL,YAAY,SAAS,aACjB,YAAY,SAAS,CAAC,IACtB,YAAY,gBAAgB,CAAC;EAElC,MAAM,wBAAQ,IAAI,IAAY;EAC9B,KAAK,MAAM,QAAQ,OAAO;GACzB,IAAI,KAAK,SAAS,YAAY;GAC9B,MAAM,OAAO,OAAO,UAAU,KAAK,MAAM;GACzC,IAAI,CAAC,MAAM;GACX,MAAM,MAAM,OAAO,IAAI;GACvB,IAAI,QAAQ,OAAO,EAAE,KAAK,KAAK,MAAM,MAAM,IAAI,GAAG;EACnD;EAEA,IAAI,eAAe,CAAC,GAAG,KAAK,CAAC,CAAC,KAAK;CACpC;CAEA,SAAS,IAAI;CACb,YAAY,IAAI;CAKhB,KAAK,MAAM,CAAC,MAAM,QAAQ,OAAO,QAAQ,UAAU,IAAI,GACtD,IAAI,CAAC,KAAK,OAAO,KAAK,QAAQ;CAG/B,OAAO;AACR"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
let _geekmidas_manifest = require("@geekmidas/manifest");
|
|
2
|
+
//#region src/workspace/derive.ts
|
|
3
|
+
/**
|
|
4
|
+
* The apps a manifest implies.
|
|
5
|
+
*
|
|
6
|
+
* `gkm.config.ts` used to carry an `apps` block naming every app: its type, its
|
|
7
|
+
* path, its port, its framework, which other apps it depended on. Every one of
|
|
8
|
+
* those facts was already declared. A `StaticSite('Web', { path: 'apps/web' })`
|
|
9
|
+
* and an `apps.web = { type: 'web', path: 'apps/web', framework: 'vite',
|
|
10
|
+
* dependencies: ['api'] }` are the same statement written twice, and only one of
|
|
11
|
+
* them was checked against anything — so the copy in config was free to drift,
|
|
12
|
+
* and a surface config had no entry for simply never deployed.
|
|
13
|
+
*
|
|
14
|
+
* So the list comes from the graph. A `site` is an app, and so is every
|
|
15
|
+
* `rest-api` — one surface, one container, at the `path` it declared.
|
|
16
|
+
*
|
|
17
|
+
* What config still supplies is what no graph can answer: the backends a cache
|
|
18
|
+
* and a mailer resolve to, the deploy endpoint, the stage's domain.
|
|
19
|
+
*/
|
|
20
|
+
/** Which frontend framework builds each site variant. */
|
|
21
|
+
const FRAMEWORKS = {
|
|
22
|
+
static: "vite",
|
|
23
|
+
next: "nextjs",
|
|
24
|
+
tanstack: "tanstack-start"
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* The app key a construct id becomes.
|
|
28
|
+
*
|
|
29
|
+
* The same kebab form every physical name is built from, so `Api` is `api` in
|
|
30
|
+
* the config, `production-kitchen-sink-api` on Dokploy, and `API_URL` in an
|
|
31
|
+
* environment — one id, three renderings, no second list.
|
|
32
|
+
*/
|
|
33
|
+
function appKey(id) {
|
|
34
|
+
return (0, _geekmidas_manifest.kebabCase)(id);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The app serving a surface: its own, always.
|
|
38
|
+
*
|
|
39
|
+
* Every surface gets a container. There is no arrangement in which one runs
|
|
40
|
+
* inside another — two surfaces in a process share a filesystem, an
|
|
41
|
+
* environment, and every credential either was granted, so an auth server
|
|
42
|
+
* beside an API is one bug in the API away from being read by it.
|
|
43
|
+
*/
|
|
44
|
+
function hostOf(manifest, id) {
|
|
45
|
+
const declaration = manifest[id];
|
|
46
|
+
if (!declaration || declaration.kind !== "rest-api") return void 0;
|
|
47
|
+
return id;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Local ports, assigned so that adding a site does not renumber the others.
|
|
51
|
+
*
|
|
52
|
+
* Backends first, then the site holding the base domain, then the rest
|
|
53
|
+
* alphabetically. The ordering is arbitrary but it is *fixed*, which is the
|
|
54
|
+
* property that matters: a port that moves when an unrelated app is added is a
|
|
55
|
+
* port nobody can put in a bookmark. A construct that names its own port keeps
|
|
56
|
+
* it, and is skipped here.
|
|
57
|
+
*/
|
|
58
|
+
function assignPorts(apps) {
|
|
59
|
+
const rank = (name, app) => [
|
|
60
|
+
app.type === "backend" ? 0 : app.type === "web" ? 1 : 2,
|
|
61
|
+
app.root ? 0 : 1,
|
|
62
|
+
name
|
|
63
|
+
];
|
|
64
|
+
const ordered = Object.entries(apps).sort(([aName, a], [bName, b]) => {
|
|
65
|
+
const [at, ar, an] = rank(aName, a);
|
|
66
|
+
const [bt, br, bn] = rank(bName, b);
|
|
67
|
+
return at - bt || ar - br || an.localeCompare(bn);
|
|
68
|
+
});
|
|
69
|
+
const taken = new Set(ordered.map(([, app]) => app.port).filter((p) => !!p));
|
|
70
|
+
let next = 3e3;
|
|
71
|
+
for (const [, app] of ordered) {
|
|
72
|
+
if (app.port) continue;
|
|
73
|
+
while (taken.has(next)) next += 1;
|
|
74
|
+
app.port = next;
|
|
75
|
+
taken.add(next);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Whichever site holds the base domain.
|
|
80
|
+
*
|
|
81
|
+
* The convention first — a site named `web` — because it is one people already
|
|
82
|
+
* rely on. Then an explicit `root: true`. A project with one site needs neither.
|
|
83
|
+
*/
|
|
84
|
+
function markRoot(apps) {
|
|
85
|
+
const sites = Object.entries(apps).filter(([, a]) => a.type === "web");
|
|
86
|
+
if (sites.length === 0) return;
|
|
87
|
+
if (sites.some(([, a]) => a.root)) return;
|
|
88
|
+
const chosen = sites.find(([name]) => name === "web") ?? (sites.length === 1 ? sites[0] : void 0);
|
|
89
|
+
if (chosen) chosen[1].root = true;
|
|
90
|
+
}
|
|
91
|
+
function derivedApps(manifest, workspace) {
|
|
92
|
+
const apps = {};
|
|
93
|
+
const defaultTarget = workspace.deploy?.default ?? "dokploy";
|
|
94
|
+
for (const [id, declaration] of Object.entries(manifest)) {
|
|
95
|
+
if (declaration.kind !== "site" && declaration.kind !== "mobile-app" && declaration.kind !== "rest-api") continue;
|
|
96
|
+
const spec = declaration.kind === "site" || declaration.kind === "mobile-app" ? declaration.app : {
|
|
97
|
+
path: declaration.path,
|
|
98
|
+
...declaration.telescope ? { telescope: true } : {}
|
|
99
|
+
};
|
|
100
|
+
const name = appKey(id);
|
|
101
|
+
const configured = workspace.apps[name];
|
|
102
|
+
apps[name] = {
|
|
103
|
+
...configured,
|
|
104
|
+
type: declaration.kind === "site" ? "web" : declaration.kind === "mobile-app" ? "mobile" : "backend",
|
|
105
|
+
path: spec.path,
|
|
106
|
+
port: spec.port ?? configured?.port ?? 0,
|
|
107
|
+
...(declaration.kind === "rest-api" || declaration.kind === "site") && declaration.subdomain ? { subdomain: declaration.subdomain } : {},
|
|
108
|
+
dependencies: [],
|
|
109
|
+
resolvedDeployTarget: configured?.resolvedDeployTarget ?? defaultTarget,
|
|
110
|
+
...declaration.kind === "site" ? {
|
|
111
|
+
framework: configured?.framework ?? FRAMEWORKS[declaration.variant],
|
|
112
|
+
...declaration.root ? { root: true } : {},
|
|
113
|
+
...spec.config ? { config: spec.config } : {}
|
|
114
|
+
} : {},
|
|
115
|
+
...declaration.kind === "mobile-app" ? {
|
|
116
|
+
framework: configured?.framework ?? declaration.variant,
|
|
117
|
+
...spec.config ? { config: spec.config } : {}
|
|
118
|
+
} : {},
|
|
119
|
+
...spec.telescope !== void 0 ? { telescope: spec.telescope } : {},
|
|
120
|
+
...spec.openapi !== void 0 ? { openapi: spec.openapi } : {},
|
|
121
|
+
...spec.runtime !== void 0 ? { runtime: spec.runtime } : {},
|
|
122
|
+
...spec.env !== void 0 ? { env: spec.env } : {},
|
|
123
|
+
...spec.entry !== void 0 ? { entry: spec.entry } : {},
|
|
124
|
+
...configured?.domain ? { domain: configured.domain } : {}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
for (const [id, declaration] of Object.entries(manifest)) {
|
|
128
|
+
if (declaration.kind !== "site" && declaration.kind !== "mobile-app" && declaration.kind !== "rest-api") continue;
|
|
129
|
+
const app = apps[appKey(id)];
|
|
130
|
+
if (!app) continue;
|
|
131
|
+
const edges = declaration.kind === "rest-api" ? declaration.calls ?? [] : declaration.dependencies ?? [];
|
|
132
|
+
const names = /* @__PURE__ */ new Set();
|
|
133
|
+
for (const edge of edges) {
|
|
134
|
+
if (edge.kind !== "rest-api") continue;
|
|
135
|
+
const host = hostOf(manifest, edge.target);
|
|
136
|
+
if (!host) continue;
|
|
137
|
+
const key = appKey(host);
|
|
138
|
+
if (key !== appKey(id) && apps[key]) names.add(key);
|
|
139
|
+
}
|
|
140
|
+
app.dependencies = [...names].sort();
|
|
141
|
+
}
|
|
142
|
+
markRoot(apps);
|
|
143
|
+
assignPorts(apps);
|
|
144
|
+
for (const [name, app] of Object.entries(workspace.apps)) if (!apps[name]) apps[name] = app;
|
|
145
|
+
return apps;
|
|
146
|
+
}
|
|
147
|
+
//#endregion
|
|
148
|
+
Object.defineProperty(exports, "appKey", {
|
|
149
|
+
enumerable: true,
|
|
150
|
+
get: function() {
|
|
151
|
+
return appKey;
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
Object.defineProperty(exports, "derivedApps", {
|
|
155
|
+
enumerable: true,
|
|
156
|
+
get: function() {
|
|
157
|
+
return derivedApps;
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
//# sourceMappingURL=derive-DAbo49sm.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive-DAbo49sm.cjs","names":["kebabCase"],"sources":["../src/workspace/derive.ts"],"sourcesContent":["/**\n * The apps a manifest implies.\n *\n * `gkm.config.ts` used to carry an `apps` block naming every app: its type, its\n * path, its port, its framework, which other apps it depended on. Every one of\n * those facts was already declared. A `StaticSite('Web', { path: 'apps/web' })`\n * and an `apps.web = { type: 'web', path: 'apps/web', framework: 'vite',\n * dependencies: ['api'] }` are the same statement written twice, and only one of\n * them was checked against anything — so the copy in config was free to drift,\n * and a surface config had no entry for simply never deployed.\n *\n * So the list comes from the graph. A `site` is an app, and so is every\n * `rest-api` — one surface, one container, at the `path` it declared.\n *\n * What config still supplies is what no graph can answer: the backends a cache\n * and a mailer resolve to, the deploy endpoint, the stage's domain.\n */\n\nimport {\n\ttype AppSpec,\n\ttype ConstructManifest,\n\tkebabCase,\n} from '@geekmidas/manifest';\nimport type {\n\tAppDomainConfig,\n\tDeployTargetName,\n\tFramework,\n\tNormalizedAppConfig,\n\tNormalizedWorkspace,\n} from './types.js';\n\n/** Which frontend framework builds each site variant. */\nconst FRAMEWORKS: Record<string, Framework> = {\n\tstatic: 'vite',\n\tnext: 'nextjs',\n\ttanstack: 'tanstack-start',\n};\n\n/**\n * The app key a construct id becomes.\n *\n * The same kebab form every physical name is built from, so `Api` is `api` in\n * the config, `production-kitchen-sink-api` on Dokploy, and `API_URL` in an\n * environment — one id, three renderings, no second list.\n */\nexport function appKey(id: string): string {\n\treturn kebabCase(id);\n}\n\n/**\n * The app serving a surface: its own, always.\n *\n * Every surface gets a container. There is no arrangement in which one runs\n * inside another — two surfaces in a process share a filesystem, an\n * environment, and every credential either was granted, so an auth server\n * beside an API is one bug in the API away from being read by it.\n */\nexport function hostOf(\n\tmanifest: ConstructManifest,\n\tid: string,\n): string | undefined {\n\tconst declaration = manifest[id];\n\tif (!declaration || declaration.kind !== 'rest-api') return undefined;\n\n\treturn id;\n}\n\n/**\n * Local ports, assigned so that adding a site does not renumber the others.\n *\n * Backends first, then the site holding the base domain, then the rest\n * alphabetically. The ordering is arbitrary but it is *fixed*, which is the\n * property that matters: a port that moves when an unrelated app is added is a\n * port nobody can put in a bookmark. A construct that names its own port keeps\n * it, and is skipped here.\n */\nfunction assignPorts(apps: Record<string, NormalizedAppConfig>): void {\n\tconst rank = (\n\t\tname: string,\n\t\tapp: NormalizedAppConfig,\n\t): [number, number, string] => [\n\t\tapp.type === 'backend' ? 0 : app.type === 'web' ? 1 : 2,\n\t\tapp.root ? 0 : 1,\n\t\tname,\n\t];\n\n\tconst ordered = Object.entries(apps).sort(([aName, a], [bName, b]) => {\n\t\tconst [at, ar, an] = rank(aName, a);\n\t\tconst [bt, br, bn] = rank(bName, b);\n\t\treturn at - bt || ar - br || an.localeCompare(bn);\n\t});\n\n\tconst taken = new Set(\n\t\tordered.map(([, app]) => app.port).filter((p): p is number => !!p),\n\t);\n\n\tlet next = 3000;\n\tfor (const [, app] of ordered) {\n\t\tif (app.port) continue;\n\t\twhile (taken.has(next)) next += 1;\n\t\tapp.port = next;\n\t\ttaken.add(next);\n\t}\n}\n\n/**\n * Whichever site holds the base domain.\n *\n * The convention first — a site named `web` — because it is one people already\n * rely on. Then an explicit `root: true`. A project with one site needs neither.\n */\nfunction markRoot(apps: Record<string, NormalizedAppConfig>): void {\n\tconst sites = Object.entries(apps).filter(([, a]) => a.type === 'web');\n\tif (sites.length === 0) return;\n\tif (sites.some(([, a]) => a.root)) return;\n\n\tconst chosen =\n\t\tsites.find(([name]) => name === 'web') ??\n\t\t(sites.length === 1 ? sites[0] : undefined);\n\tif (chosen) chosen[1].root = true;\n}\n\nexport function derivedApps(\n\tmanifest: ConstructManifest,\n\tworkspace: NormalizedWorkspace,\n): Record<string, NormalizedAppConfig> {\n\tconst apps: Record<string, NormalizedAppConfig> = {};\n\tconst defaultTarget: DeployTargetName =\n\t\tworkspace.deploy?.default ?? 'dokploy';\n\n\tfor (const [id, declaration] of Object.entries(manifest)) {\n\t\t// Three kinds are apps: a site, a mobile app and an HTTP surface. A worker\n\t\t// is not one — it names the process that runs a cron or a subscriber, and\n\t\t// that process is the app's server, which already exists. Everything else\n\t\t// is a resource one of them uses.\n\t\tif (\n\t\t\tdeclaration.kind !== 'site' &&\n\t\t\tdeclaration.kind !== 'mobile-app' &&\n\t\t\tdeclaration.kind !== 'rest-api'\n\t\t)\n\t\t\tcontinue;\n\n\t\t// No opt-in to be had. Each says where it lives.\n\t\tconst spec: AppSpec =\n\t\t\tdeclaration.kind === 'site' || declaration.kind === 'mobile-app'\n\t\t\t\t? declaration.app\n\t\t\t\t: {\n\t\t\t\t\t\tpath: declaration.path,\n\t\t\t\t\t\t...(declaration.telescope ? { telescope: true } : {}),\n\t\t\t\t\t};\n\n\t\tconst name = appKey(id);\n\t\t// A config entry of the same name still wins, so a workspace can override\n\t\t// one field without restating the app. Nothing in this repo needs to.\n\t\tconst configured = workspace.apps[name];\n\n\t\tapps[name] = {\n\t\t\t...configured,\n\t\t\ttype:\n\t\t\t\tdeclaration.kind === 'site'\n\t\t\t\t\t? 'web'\n\t\t\t\t\t: declaration.kind === 'mobile-app'\n\t\t\t\t\t\t? 'mobile'\n\t\t\t\t\t\t: 'backend',\n\t\t\tpath: spec.path,\n\t\t\tport: spec.port ?? configured?.port ?? 0,\n\t\t\t// The label it answers on, under a stage's domain and locally alike.\n\t\t\t...((declaration.kind === 'rest-api' || declaration.kind === 'site') &&\n\t\t\tdeclaration.subdomain\n\t\t\t\t? { subdomain: declaration.subdomain }\n\t\t\t\t: {}),\n\t\t\tdependencies: [],\n\t\t\tresolvedDeployTarget: configured?.resolvedDeployTarget ?? defaultTarget,\n\t\t\t...(declaration.kind === 'site'\n\t\t\t\t? {\n\t\t\t\t\t\tframework: configured?.framework ?? FRAMEWORKS[declaration.variant],\n\t\t\t\t\t\t...(declaration.root ? { root: true } : {}),\n\t\t\t\t\t\t...(spec.config ? { config: spec.config } : {}),\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t\t...(declaration.kind === 'mobile-app'\n\t\t\t\t? {\n\t\t\t\t\t\tframework: configured?.framework ?? declaration.variant,\n\t\t\t\t\t\t...(spec.config ? { config: spec.config } : {}),\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t\t// One glob fans out to the six the build reads, because each generator\n\t\t\t// already inspects every export and keeps what it recognises. A\n\t\t\t// per-kind field still wins where one was given.\n\t\t\t...(spec.telescope !== undefined ? { telescope: spec.telescope } : {}),\n\t\t\t...(spec.openapi !== undefined ? { openapi: spec.openapi } : {}),\n\t\t\t...(spec.runtime !== undefined ? { runtime: spec.runtime } : {}),\n\t\t\t...(spec.env !== undefined ? { env: spec.env } : {}),\n\t\t\t...(spec.entry !== undefined ? { entry: spec.entry } : {}),\n\t\t\t...(configured?.domain\n\t\t\t\t? { domain: configured.domain as AppDomainConfig }\n\t\t\t\t: {}),\n\t\t} as NormalizedAppConfig;\n\t}\n\n\t// Build order, from the edges rather than from a hand-kept list. A site that\n\t// depends on a surface depends on the app that serves it — which for a\n\t// mounted auth server is its host, not a container that does not exist.\n\tfor (const [id, declaration] of Object.entries(manifest)) {\n\t\tif (\n\t\t\tdeclaration.kind !== 'site' &&\n\t\t\tdeclaration.kind !== 'mobile-app' &&\n\t\t\tdeclaration.kind !== 'rest-api'\n\t\t)\n\t\t\tcontinue;\n\n\t\tconst app = apps[appKey(id)];\n\t\tif (!app) continue;\n\n\t\t// A site names its edges `dependencies`; a surface names them `calls`.\n\t\tconst edges =\n\t\t\tdeclaration.kind === 'rest-api'\n\t\t\t\t? (declaration.calls ?? [])\n\t\t\t\t: (declaration.dependencies ?? []);\n\n\t\tconst names = new Set<string>();\n\t\tfor (const edge of edges) {\n\t\t\tif (edge.kind !== 'rest-api') continue;\n\t\t\tconst host = hostOf(manifest, edge.target);\n\t\t\tif (!host) continue;\n\t\t\tconst key = appKey(host);\n\t\t\tif (key !== appKey(id) && apps[key]) names.add(key);\n\t\t}\n\n\t\tapp.dependencies = [...names].sort();\n\t}\n\n\tmarkRoot(apps);\n\tassignPorts(apps);\n\n\t// Apps config declared that the graph knows nothing about — a `type: 'mobile'`\n\t// entry, or a hand-run process. Kept rather than dropped: config is still the\n\t// escape hatch for what nothing declares.\n\tfor (const [name, app] of Object.entries(workspace.apps)) {\n\t\tif (!apps[name]) apps[name] = app;\n\t}\n\n\treturn apps;\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
|
|
2
2
|
const require_schemas = require("./schemas-Ckm_aBnF.cjs");
|
|
3
|
+
const require_sandbox = require("./sandbox-CL--ZDui.cjs");
|
|
3
4
|
const require_workerRuntime = require("./workerRuntime-BXfhbe13.cjs");
|
|
4
5
|
let node_path = require("node:path");
|
|
5
6
|
let node_fs = require("node:fs");
|
|
@@ -102,117 +103,6 @@ var ConstructGenerator = class {
|
|
|
102
103
|
}
|
|
103
104
|
};
|
|
104
105
|
//#endregion
|
|
105
|
-
//#region src/sandbox/sandbox.ts
|
|
106
|
-
/**
|
|
107
|
-
* Where a deploy runs the project's own code.
|
|
108
|
-
*
|
|
109
|
-
* A deploy holds credentials — Dokploy's token, a registry login, an AWS key —
|
|
110
|
-
* and loading `gkm.config.ts`, sniffing an app's environment and building it
|
|
111
|
-
* all execute code from the repository being deployed. On a developer's own
|
|
112
|
-
* laptop that is the developer's code. On a CI job for a fork, or a shared
|
|
113
|
-
* build runner, it is not, and that code should not be able to read the
|
|
114
|
-
* credentials used to deploy it, hang the deploy, or reach outside the
|
|
115
|
-
* checkout.
|
|
116
|
-
*
|
|
117
|
-
* So every step that runs project code goes through a `Sandbox`: one call,
|
|
118
|
-
* with an argument array rather than a shell line, the complete environment
|
|
119
|
-
* the step needs rather than the host's, a timeout it cannot outlive, secrets
|
|
120
|
-
* as files rather than variables, and a working directory inside the project.
|
|
121
|
-
* The steps that need credentials — provisioning, pushing, releasing — are
|
|
122
|
-
* the deploy's own code and never run in one.
|
|
123
|
-
*
|
|
124
|
-
* `LocalSandbox` is the default: a child process on this machine with an
|
|
125
|
-
* allowlisted environment. A host that builds repositories it does not trust
|
|
126
|
-
* passes its own — a container per build, a VM — through `deploy()`'s
|
|
127
|
-
* `sandbox`.
|
|
128
|
-
*/
|
|
129
|
-
/** The variable naming the directory a command's secrets are mounted in. */
|
|
130
|
-
const SECRETS_DIR_ENV = "GKM_SECRETS_DIR";
|
|
131
|
-
/** A command was asked to run outside the project. */
|
|
132
|
-
var SandboxCwdEscape = class extends Error {
|
|
133
|
-
cwd;
|
|
134
|
-
root;
|
|
135
|
-
constructor(cwd, root) {
|
|
136
|
-
super(`Refusing to run in '${cwd}': it is outside the project at '${root}'. A sandboxed step only runs inside the project it was given — check the app paths in gkm.config.ts for \`..\` or an absolute path.`);
|
|
137
|
-
this.cwd = cwd;
|
|
138
|
-
this.root = root;
|
|
139
|
-
this.name = "SandboxCwdEscape";
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
/** A path with its symlinks resolved where it exists — `/tmp` is `/private/tmp`. */
|
|
143
|
-
function real(path) {
|
|
144
|
-
return (0, node_fs.existsSync)(path) ? (0, node_fs.realpathSync)(path) : path;
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* `cwd` as an absolute directory inside `root`, or {@link SandboxCwdEscape}.
|
|
148
|
-
*
|
|
149
|
-
* Symlinks are resolved on both sides first, so a link inside the project
|
|
150
|
-
* that points out of it is an escape, and a project reached through a link
|
|
151
|
-
* (`/tmp` on macOS) is still itself. For a sandbox of a host's own to call
|
|
152
|
-
* before it runs anything.
|
|
153
|
-
*/
|
|
154
|
-
function confineCwd(root, cwd) {
|
|
155
|
-
const absolute = (0, node_path.resolve)(root, cwd);
|
|
156
|
-
const from = (0, node_path.relative)(real((0, node_path.resolve)(root)), real(absolute));
|
|
157
|
-
if (from === ".." || from.startsWith(`..${node_path.sep}`) || (0, node_path.isAbsolute)(from)) throw new SandboxCwdEscape(cwd, root);
|
|
158
|
-
return absolute;
|
|
159
|
-
}
|
|
160
|
-
/** A secret's file name: one path segment, so it cannot be written elsewhere. */
|
|
161
|
-
var SecretNameInvalid = class extends Error {
|
|
162
|
-
secretName;
|
|
163
|
-
constructor(secretName) {
|
|
164
|
-
super(`'${secretName}' cannot be a secret's name: it becomes a file name in the secrets directory, so it must be one segment of letters, digits, '.', '-' or '_' and not start with '.'.`);
|
|
165
|
-
this.secretName = secretName;
|
|
166
|
-
this.name = "SecretNameInvalid";
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
const SECRET_NAME = /^[A-Za-z0-9_-][A-Za-z0-9._-]*$/;
|
|
170
|
-
/** Every secret name, checked before anything is written. */
|
|
171
|
-
function assertSecretNames(secrets) {
|
|
172
|
-
for (const name of Object.keys(secrets ?? {})) if (!SECRET_NAME.test(name)) throw new SecretNameInvalid(name);
|
|
173
|
-
}
|
|
174
|
-
const active = new node_async_hooks.AsyncLocalStorage();
|
|
175
|
-
/**
|
|
176
|
-
* Run `fn` with `sandbox` as the one its steps use.
|
|
177
|
-
*
|
|
178
|
-
* Per run rather than global, as `withOutput` routes a run's lines: two
|
|
179
|
-
* deploys in one host process each build in their own sandbox. A step deep in
|
|
180
|
-
* the engine — discovering constructs, sniffing an app — asks
|
|
181
|
-
* {@link activeSandbox} rather than every function between `deploy()` and it
|
|
182
|
-
* taking one more parameter.
|
|
183
|
-
*/
|
|
184
|
-
function withSandbox(sandbox, fn) {
|
|
185
|
-
return active.run({
|
|
186
|
-
sandbox,
|
|
187
|
-
answers: /* @__PURE__ */ new Map()
|
|
188
|
-
}, fn);
|
|
189
|
-
}
|
|
190
|
-
/** The sandbox of the run this is part of, if it was started with one. */
|
|
191
|
-
function activeSandbox() {
|
|
192
|
-
return active.getStore()?.sandbox;
|
|
193
|
-
}
|
|
194
|
-
/**
|
|
195
|
-
* `ask()`'s answer, asked once per run.
|
|
196
|
-
*
|
|
197
|
-
* A deploy loads its config and discovers its constructs from several places
|
|
198
|
-
* — the first load, the engine, the Dockerfile generator — and in a sandbox
|
|
199
|
-
* each was a child process of its own, for the same answer: the checkout does
|
|
200
|
-
* not change while it deploys. Within a run in `sandbox`, the first answer
|
|
201
|
-
* for `key` is the one every later ask gets. Outside one, or for another
|
|
202
|
-
* sandbox, `ask()` runs each time.
|
|
203
|
-
*/
|
|
204
|
-
function oncePerRun(sandbox, key, ask) {
|
|
205
|
-
const run = active.getStore();
|
|
206
|
-
if (!run || run.sandbox !== sandbox) return ask();
|
|
207
|
-
let answer = run.answers.get(key);
|
|
208
|
-
if (!answer) {
|
|
209
|
-
answer = ask();
|
|
210
|
-
run.answers.set(key, answer);
|
|
211
|
-
answer.catch(() => run.answers.delete(key));
|
|
212
|
-
}
|
|
213
|
-
return answer;
|
|
214
|
-
}
|
|
215
|
-
//#endregion
|
|
216
106
|
//#region src/owningTsconfigJsx.ts
|
|
217
107
|
const REGISTER = "owning-tsconfig-jsx-register.mjs";
|
|
218
108
|
/** The CLI's `bin/` was not found above the module looking for it. */
|
|
@@ -319,12 +209,13 @@ var SandboxWorkerFailed = class extends Error {
|
|
|
319
209
|
* though it had run again.
|
|
320
210
|
*/
|
|
321
211
|
async function runWorker(sandbox, step, options) {
|
|
322
|
-
const
|
|
212
|
+
const key = JSON.stringify([
|
|
323
213
|
options.name,
|
|
324
214
|
options.cwd,
|
|
325
215
|
options.args,
|
|
326
216
|
options.env ?? {}
|
|
327
|
-
])
|
|
217
|
+
]);
|
|
218
|
+
const answer = await require_sandbox.oncePerRun(sandbox, key, () => startWorker(sandbox, step, options));
|
|
328
219
|
return structuredClone(answer);
|
|
329
220
|
}
|
|
330
221
|
async function startWorker(sandbox, step, options) {
|
|
@@ -486,7 +377,7 @@ var ConstructsNotSerializable = class extends Error {
|
|
|
486
377
|
* @throws {DuplicateConstruct} when two exports claim one id.
|
|
487
378
|
*/
|
|
488
379
|
async function discover(options) {
|
|
489
|
-
const sandbox = options.sandbox ?? activeSandbox();
|
|
380
|
+
const sandbox = options.sandbox ?? require_sandbox.activeSandbox();
|
|
490
381
|
if (sandbox) {
|
|
491
382
|
if (!options.sources && !options.bustCache) {
|
|
492
383
|
const sandboxed = await discoverInSandbox(sandbox, options);
|
|
@@ -629,48 +520,12 @@ Object.defineProperty(exports, "ConstructsNotSerializable", {
|
|
|
629
520
|
return ConstructsNotSerializable;
|
|
630
521
|
}
|
|
631
522
|
});
|
|
632
|
-
Object.defineProperty(exports, "SECRETS_DIR_ENV", {
|
|
633
|
-
enumerable: true,
|
|
634
|
-
get: function() {
|
|
635
|
-
return SECRETS_DIR_ENV;
|
|
636
|
-
}
|
|
637
|
-
});
|
|
638
|
-
Object.defineProperty(exports, "SandboxCwdEscape", {
|
|
639
|
-
enumerable: true,
|
|
640
|
-
get: function() {
|
|
641
|
-
return SandboxCwdEscape;
|
|
642
|
-
}
|
|
643
|
-
});
|
|
644
523
|
Object.defineProperty(exports, "SandboxWorkerFailed", {
|
|
645
524
|
enumerable: true,
|
|
646
525
|
get: function() {
|
|
647
526
|
return SandboxWorkerFailed;
|
|
648
527
|
}
|
|
649
528
|
});
|
|
650
|
-
Object.defineProperty(exports, "SecretNameInvalid", {
|
|
651
|
-
enumerable: true,
|
|
652
|
-
get: function() {
|
|
653
|
-
return SecretNameInvalid;
|
|
654
|
-
}
|
|
655
|
-
});
|
|
656
|
-
Object.defineProperty(exports, "activeSandbox", {
|
|
657
|
-
enumerable: true,
|
|
658
|
-
get: function() {
|
|
659
|
-
return activeSandbox;
|
|
660
|
-
}
|
|
661
|
-
});
|
|
662
|
-
Object.defineProperty(exports, "assertSecretNames", {
|
|
663
|
-
enumerable: true,
|
|
664
|
-
get: function() {
|
|
665
|
-
return assertSecretNames;
|
|
666
|
-
}
|
|
667
|
-
});
|
|
668
|
-
Object.defineProperty(exports, "confineCwd", {
|
|
669
|
-
enumerable: true,
|
|
670
|
-
get: function() {
|
|
671
|
-
return confineCwd;
|
|
672
|
-
}
|
|
673
|
-
});
|
|
674
529
|
Object.defineProperty(exports, "discover", {
|
|
675
530
|
enumerable: true,
|
|
676
531
|
get: function() {
|
|
@@ -719,11 +574,5 @@ Object.defineProperty(exports, "withOwningTsconfigJsx", {
|
|
|
719
574
|
return withOwningTsconfigJsx;
|
|
720
575
|
}
|
|
721
576
|
});
|
|
722
|
-
Object.defineProperty(exports, "withSandbox", {
|
|
723
|
-
enumerable: true,
|
|
724
|
-
get: function() {
|
|
725
|
-
return withSandbox;
|
|
726
|
-
}
|
|
727
|
-
});
|
|
728
577
|
|
|
729
|
-
//# sourceMappingURL=discover-
|
|
578
|
+
//# sourceMappingURL=discover-DHdECueV.cjs.map
|