@heychunky/core 0.1.0
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 +2 -0
- package/dist/admin/apps.d.ts +49 -0
- package/dist/admin/apps.d.ts.map +1 -0
- package/dist/admin/apps.js +97 -0
- package/dist/admin/apps.js.map +1 -0
- package/dist/admin/index.d.ts +26 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +27 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/master.d.ts +9 -0
- package/dist/admin/master.d.ts.map +1 -0
- package/dist/admin/master.js +14 -0
- package/dist/admin/master.js.map +1 -0
- package/dist/admin/provision.d.ts +42 -0
- package/dist/admin/provision.d.ts.map +1 -0
- package/dist/admin/provision.js +157 -0
- package/dist/admin/provision.js.map +1 -0
- package/dist/admin/run.d.ts +89 -0
- package/dist/admin/run.d.ts.map +1 -0
- package/dist/admin/run.js +218 -0
- package/dist/admin/run.js.map +1 -0
- package/dist/admin/secrets/infisical.d.ts +45 -0
- package/dist/admin/secrets/infisical.d.ts.map +1 -0
- package/dist/admin/secrets/infisical.js +182 -0
- package/dist/admin/secrets/infisical.js.map +1 -0
- package/dist/admin/services/agentmail.d.ts +16 -0
- package/dist/admin/services/agentmail.d.ts.map +1 -0
- package/dist/admin/services/agentmail.js +43 -0
- package/dist/admin/services/agentmail.js.map +1 -0
- package/dist/admin/services/ai-gateway.d.ts +13 -0
- package/dist/admin/services/ai-gateway.d.ts.map +1 -0
- package/dist/admin/services/ai-gateway.js +32 -0
- package/dist/admin/services/ai-gateway.js.map +1 -0
- package/dist/admin/services/bunny.d.ts +16 -0
- package/dist/admin/services/bunny.d.ts.map +1 -0
- package/dist/admin/services/bunny.js +59 -0
- package/dist/admin/services/bunny.js.map +1 -0
- package/dist/admin/services/cloudflare.d.ts +42 -0
- package/dist/admin/services/cloudflare.d.ts.map +1 -0
- package/dist/admin/services/cloudflare.js +97 -0
- package/dist/admin/services/cloudflare.js.map +1 -0
- package/dist/admin/services/db.d.ts +16 -0
- package/dist/admin/services/db.d.ts.map +1 -0
- package/dist/admin/services/db.js +81 -0
- package/dist/admin/services/db.js.map +1 -0
- package/dist/admin/services/elevenlabs.d.ts +22 -0
- package/dist/admin/services/elevenlabs.d.ts.map +1 -0
- package/dist/admin/services/elevenlabs.js +45 -0
- package/dist/admin/services/elevenlabs.js.map +1 -0
- package/dist/admin/services/firecrawl.d.ts +13 -0
- package/dist/admin/services/firecrawl.d.ts.map +1 -0
- package/dist/admin/services/firecrawl.js +36 -0
- package/dist/admin/services/firecrawl.js.map +1 -0
- package/dist/admin/services/fireworks.d.ts +9 -0
- package/dist/admin/services/fireworks.d.ts.map +1 -0
- package/dist/admin/services/fireworks.js +31 -0
- package/dist/admin/services/fireworks.js.map +1 -0
- package/dist/admin/services/github.d.ts +26 -0
- package/dist/admin/services/github.d.ts.map +1 -0
- package/dist/admin/services/github.js +164 -0
- package/dist/admin/services/github.js.map +1 -0
- package/dist/admin/services/neon.d.ts +42 -0
- package/dist/admin/services/neon.d.ts.map +1 -0
- package/dist/admin/services/neon.js +157 -0
- package/dist/admin/services/neon.js.map +1 -0
- package/dist/admin/services/resend.d.ts +38 -0
- package/dist/admin/services/resend.d.ts.map +1 -0
- package/dist/admin/services/resend.js +81 -0
- package/dist/admin/services/resend.js.map +1 -0
- package/dist/admin/services/togetherai.d.ts +10 -0
- package/dist/admin/services/togetherai.d.ts.map +1 -0
- package/dist/admin/services/togetherai.js +36 -0
- package/dist/admin/services/togetherai.js.map +1 -0
- package/dist/admin/services/vercel.d.ts +66 -0
- package/dist/admin/services/vercel.d.ts.map +1 -0
- package/dist/admin/services/vercel.js +209 -0
- package/dist/admin/services/vercel.js.map +1 -0
- package/dist/admin/syncenv.d.ts +12 -0
- package/dist/admin/syncenv.d.ts.map +1 -0
- package/dist/admin/syncenv.js +114 -0
- package/dist/admin/syncenv.js.map +1 -0
- package/dist/admin/users.d.ts +27 -0
- package/dist/admin/users.d.ts.map +1 -0
- package/dist/admin/users.js +48 -0
- package/dist/admin/users.js.map +1 -0
- package/dist/config/environments.d.ts +50 -0
- package/dist/config/environments.d.ts.map +1 -0
- package/dist/config/environments.js +62 -0
- package/dist/config/environments.js.map +1 -0
- package/dist/config/names.d.ts +45 -0
- package/dist/config/names.d.ts.map +1 -0
- package/dist/config/names.js +75 -0
- package/dist/config/names.js.map +1 -0
- package/dist/http/errors.d.ts +45 -0
- package/dist/http/errors.d.ts.map +1 -0
- package/dist/http/errors.js +70 -0
- package/dist/http/errors.js.map +1 -0
- package/dist/http/request.d.ts +36 -0
- package/dist/http/request.d.ts.map +1 -0
- package/dist/http/request.js +73 -0
- package/dist/http/request.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/registry.d.ts +66 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +137 -0
- package/dist/registry.js.map +1 -0
- package/dist/secrets/store.d.ts +50 -0
- package/dist/secrets/store.d.ts.map +1 -0
- package/dist/secrets/store.js +71 -0
- package/dist/secrets/store.js.map +1 -0
- package/dist/services/types.d.ts +39 -0
- package/dist/services/types.d.ts.map +1 -0
- package/dist/services/types.js +28 -0
- package/dist/services/types.js.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vercel — projects, deployments, environment variables.
|
|
3
|
+
*
|
|
4
|
+
* Tokens are team-wide and cannot be narrowed to a project. That is Vercel's
|
|
5
|
+
* model, not an oversight, and it is why this key is one of the widest the
|
|
6
|
+
* factory holds.
|
|
7
|
+
*/
|
|
8
|
+
import { request } from "../../http/request.js";
|
|
9
|
+
import { matchByName } from "../../config/names.js";
|
|
10
|
+
import { probe, probeAccess } from "../../services/types.js";
|
|
11
|
+
const BASE = "https://api.vercel.com";
|
|
12
|
+
export function vercel(token) {
|
|
13
|
+
const creds = { token };
|
|
14
|
+
const o = { credentials: creds };
|
|
15
|
+
const projects = async () => (await request(`${BASE}/v9/projects?limit=100`, o)).projects;
|
|
16
|
+
/**
|
|
17
|
+
* Find a project for an app name.
|
|
18
|
+
*
|
|
19
|
+
* Deliberately tries several forms: the same account has `delters` for
|
|
20
|
+
* delters.com and `carmelcity-ai` for carmelcity.ai. A single slug rule
|
|
21
|
+
* matches one and silently misses the other — which is what broke sync-env
|
|
22
|
+
* for every app.
|
|
23
|
+
*/
|
|
24
|
+
async function findProject(app) {
|
|
25
|
+
return matchByName(app, await projects(), (p) => p.name);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The team this token acts on.
|
|
29
|
+
*
|
|
30
|
+
* Vercel scopes a token to a team, but the create endpoint defaults to the
|
|
31
|
+
* personal account when no team is named — so a project would land somewhere
|
|
32
|
+
* nobody thinks to look. Read once, then remembered.
|
|
33
|
+
*/
|
|
34
|
+
let team;
|
|
35
|
+
async function teamId() {
|
|
36
|
+
if (team === undefined) {
|
|
37
|
+
const { token: t } = await request(`${BASE}/v5/user/tokens/current`, o);
|
|
38
|
+
team = t.scopes.find((s) => s.type === "team")?.teamId ?? null;
|
|
39
|
+
}
|
|
40
|
+
return team ?? undefined;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Create a project, optionally linked to a GitHub repository.
|
|
44
|
+
*
|
|
45
|
+
* Only the identity is returned. Vercel's response carries far more than a
|
|
46
|
+
* project's name, and passing a provider payload through is how a credential
|
|
47
|
+
* reaches a terminal.
|
|
48
|
+
*/
|
|
49
|
+
/**
|
|
50
|
+
* Create a project.
|
|
51
|
+
*
|
|
52
|
+
* The framework defaults to Next.js rather than to null. A null framework is
|
|
53
|
+
* not "detect it" — Vercel builds the repository as a bare static site,
|
|
54
|
+
* publishes nothing routable, and every URL it owns returns 404: the
|
|
55
|
+
* deployment's own, the project's, and any domain pointed at it. The build
|
|
56
|
+
* succeeds and reports READY, so nothing anywhere says what is wrong.
|
|
57
|
+
*
|
|
58
|
+
* Every app this factory provisions is Next.js. Something else is passed
|
|
59
|
+
* explicitly, and passing null is still possible for a project that really
|
|
60
|
+
* has no framework — but it is now a choice rather than the default.
|
|
61
|
+
*/
|
|
62
|
+
async function createProject(name, opts = {}) {
|
|
63
|
+
const t = await teamId();
|
|
64
|
+
try {
|
|
65
|
+
const p = await request(`${BASE}/v11/projects${t ? `?teamId=${t}` : ""}`, {
|
|
66
|
+
...o, method: "POST",
|
|
67
|
+
body: {
|
|
68
|
+
name,
|
|
69
|
+
framework: opts.framework === undefined ? "nextjs" : opts.framework,
|
|
70
|
+
...(opts.repo ? { gitRepository: { type: "github", repo: opts.repo } } : {}),
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
return { id: p.id, name: p.name };
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
// Vercel answers "make sure there are no typos", which sends you to check
|
|
77
|
+
// the repository name — and the name is nearly always right. What it
|
|
78
|
+
// actually means is that Vercel's GitHub App was never installed on that
|
|
79
|
+
// organisation, so the repository is invisible to it rather than absent.
|
|
80
|
+
// A brand-new org is exactly that case, and the honest message saves
|
|
81
|
+
// someone an hour of looking at the wrong thing.
|
|
82
|
+
if (opts.repo && err instanceof Error && err.message.includes("repo_not_found")) {
|
|
83
|
+
const org = opts.repo.split("/")[0];
|
|
84
|
+
throw new Error(`Vercel cannot see ${opts.repo}. Its GitHub App is most likely not installed on ` +
|
|
85
|
+
`"${org}" — grant it there at github.com/apps/vercel, then re-run.`);
|
|
86
|
+
}
|
|
87
|
+
throw err;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const envVars = async (projectId, teamId) => (await request(`${BASE}/v9/projects/${projectId}/env${teamId ? `?teamId=${teamId}` : ""}`, o)).envs;
|
|
91
|
+
/**
|
|
92
|
+
* Set one variable for one target, replacing what is there.
|
|
93
|
+
*
|
|
94
|
+
* Idempotent by deleting first: Vercel would otherwise accept a duplicate
|
|
95
|
+
* key on the same target and leave you guessing which one is live.
|
|
96
|
+
*
|
|
97
|
+
* Written as `sensitive` by default, so it cannot be read back out of the
|
|
98
|
+
* dashboard or the API — including by us.
|
|
99
|
+
*/
|
|
100
|
+
async function setEnv(projectId, key, value, scope, opts = {}) {
|
|
101
|
+
const team = opts.teamId ? `?teamId=${opts.teamId}` : "";
|
|
102
|
+
// Deleting is scoped as narrowly as writing. Matching on the key alone
|
|
103
|
+
// would remove the same variable from every other environment.
|
|
104
|
+
const inScope = (e) => "target" in scope
|
|
105
|
+
? (e.target ?? []).includes(scope.target) && !(e.customEnvironmentIds ?? []).length
|
|
106
|
+
: (e.customEnvironmentIds ?? []).includes(scope.customEnvironmentId);
|
|
107
|
+
for (const e of (await envVars(projectId, opts.teamId)).filter((e) => e.key === key && inScope(e))) {
|
|
108
|
+
if (e.id)
|
|
109
|
+
await request(`${BASE}/v9/projects/${projectId}/env/${e.id}${team}`, { ...o, method: "DELETE" });
|
|
110
|
+
}
|
|
111
|
+
await request(`${BASE}/v10/projects/${projectId}/env${team}`, {
|
|
112
|
+
...o, method: "POST",
|
|
113
|
+
body: {
|
|
114
|
+
key, value,
|
|
115
|
+
type: opts.sensitive === false ? "encrypted" : "sensitive",
|
|
116
|
+
...("target" in scope
|
|
117
|
+
? { target: [scope.target] }
|
|
118
|
+
: { customEnvironmentIds: [scope.customEnvironmentId] }),
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
/** Domains on a project, and whether Vercel is happy with their DNS. */
|
|
123
|
+
const projectDomains = async (projectId, teamId) => (await request(`${BASE}/v9/projects/${projectId}/domains${teamId ? `?teamId=${teamId}` : ""}`, o)).domains;
|
|
124
|
+
/** Attach a domain. Idempotent: one already attached is reported, not re-added. */
|
|
125
|
+
async function addDomain(projectId, domain, teamId) {
|
|
126
|
+
const already = (await projectDomains(projectId, teamId)).find((d) => d.name === domain);
|
|
127
|
+
if (already)
|
|
128
|
+
return already;
|
|
129
|
+
return await request(`${BASE}/v10/projects/${projectId}/domains${teamId ? `?teamId=${teamId}` : ""}`, { ...o, method: "POST", body: { name: domain } });
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Build a git ref.
|
|
133
|
+
*
|
|
134
|
+
* The framework is the project's, set when it was created — a deployment
|
|
135
|
+
* cannot supply one, which is why a project made without it produces builds
|
|
136
|
+
* that succeed and serve nothing.
|
|
137
|
+
*/
|
|
138
|
+
async function deploy(name, opts) {
|
|
139
|
+
const [org, repo] = opts.repo.split("/");
|
|
140
|
+
return await request(`${BASE}/v13/deployments${opts.teamId ? `?teamId=${opts.teamId}&forceNew=1` : "?forceNew=1"}`, {
|
|
141
|
+
...o, method: "POST",
|
|
142
|
+
body: {
|
|
143
|
+
name,
|
|
144
|
+
...(opts.target ? { target: opts.target } : {}),
|
|
145
|
+
gitSource: { type: "github", org, repo, ref: opts.ref },
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/** Make an existing deployment the live one, without rebuilding it. */
|
|
150
|
+
const promote = async (projectId, deploymentId, teamId) => await request(`${BASE}/v10/projects/${projectId}/promote/${deploymentId}${teamId ? `?teamId=${teamId}` : ""}`, { ...o, method: "POST" });
|
|
151
|
+
const customEnvironments = async (projectId, teamId) => (await request(`${BASE}/v9/projects/${projectId}/custom-environments${teamId ? `?teamId=${teamId}` : ""}`, o)).environments;
|
|
152
|
+
/**
|
|
153
|
+
* The custom environment for a slug, made if it is not there.
|
|
154
|
+
*
|
|
155
|
+
* Pro allows one per project, so this is the only way `staging` can exist at
|
|
156
|
+
* all — and asking first means a second sync does not fail against the limit.
|
|
157
|
+
*/
|
|
158
|
+
async function ensureCustomEnvironment(projectId, slug, teamId) {
|
|
159
|
+
const found = (await customEnvironments(projectId, teamId)).find((e) => e.slug === slug);
|
|
160
|
+
if (found)
|
|
161
|
+
return found;
|
|
162
|
+
return await request(`${BASE}/v9/projects/${projectId}/custom-environments${teamId ? `?teamId=${teamId}` : ""}`, { ...o, method: "POST", body: { slug } });
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
projects,
|
|
166
|
+
findProject,
|
|
167
|
+
teamId,
|
|
168
|
+
createProject,
|
|
169
|
+
projectDomains,
|
|
170
|
+
addDomain,
|
|
171
|
+
deploy,
|
|
172
|
+
promote,
|
|
173
|
+
envVars,
|
|
174
|
+
customEnvironments,
|
|
175
|
+
ensureCustomEnvironment,
|
|
176
|
+
setEnv,
|
|
177
|
+
deployments: async (projectId, limit = 5) => (await request(`${BASE}/v6/deployments?limit=${limit}${projectId ? `&projectId=${projectId}` : ""}`, o)).deployments,
|
|
178
|
+
async status() {
|
|
179
|
+
return probe("vercel", async () => {
|
|
180
|
+
const p = await projects();
|
|
181
|
+
return `${p.length} project${p.length === 1 ? "" : "s"}`;
|
|
182
|
+
});
|
|
183
|
+
},
|
|
184
|
+
async access() {
|
|
185
|
+
return probeAccess("vercel", async () => {
|
|
186
|
+
const { token: t } = await request(`${BASE}/v5/user/tokens/current`, o);
|
|
187
|
+
const teams = t.scopes.filter((s) => s.type === "team").length;
|
|
188
|
+
return {
|
|
189
|
+
evidence: "scopes",
|
|
190
|
+
scopes: t.scopes.map((s) => (s.teamId ? `${s.type}:${s.teamId}` : s.type)),
|
|
191
|
+
can: teams
|
|
192
|
+
? [
|
|
193
|
+
`read and write every project on ${teams} team${teams === 1 ? "" : "s"}`,
|
|
194
|
+
"create projects and trigger deployments",
|
|
195
|
+
"read and write project environment variables",
|
|
196
|
+
"promote and roll back production deployments",
|
|
197
|
+
]
|
|
198
|
+
: ["act on the personal account only"],
|
|
199
|
+
notes: [
|
|
200
|
+
`token "${t.name}", created ${new Date(t.createdAt).toISOString().slice(0, 10)}`,
|
|
201
|
+
t.expiresAt ? `expires ${new Date(t.expiresAt).toISOString().slice(0, 10)}` : "does not expire",
|
|
202
|
+
"team scope is whole-team: Vercel tokens cannot be narrowed to one project",
|
|
203
|
+
],
|
|
204
|
+
};
|
|
205
|
+
});
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=vercel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vercel.js","sourceRoot":"","sources":["../../../src/admin/services/vercel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type RunContext } from "./run.ts";
|
|
2
|
+
import { type Environment } from "../config/environments.ts";
|
|
3
|
+
import type { Run } from "./provision.ts";
|
|
4
|
+
export interface SyncSpec {
|
|
5
|
+
/** The app, which names both the vault folder and the Vercel project. */
|
|
6
|
+
app: string;
|
|
7
|
+
environment: Environment;
|
|
8
|
+
}
|
|
9
|
+
/** Where an app's values for one environment live. */
|
|
10
|
+
export declare const folderFor: (app: string, environment: Environment) => string;
|
|
11
|
+
export declare function syncEnv(spec: SyncSpec, ctx: RunContext): Promise<Run>;
|
|
12
|
+
//# sourceMappingURL=syncenv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syncenv.d.ts","sourceRoot":"","sources":["../../src/admin/syncenv.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAW,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAW,KAAK,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAE,GAAG,EAAc,MAAM,gBAAgB,CAAC;AAEtD,MAAM,WAAW,QAAQ;IACvB,yEAAyE;IACzE,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,sDAAsD;AACtD,eAAO,MAAM,SAAS,GAAI,KAAK,MAAM,EAAE,aAAa,WAAW,WACV,CAAC;AAEtD,wBAAsB,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,CAmG3E"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pushing an app's environment from the vault to Vercel.
|
|
3
|
+
*
|
|
4
|
+
* The vault is the source. A deployment reads Vercel's own store, so the two
|
|
5
|
+
* have to be made to agree, and this is the direction that agreement travels:
|
|
6
|
+
* every key under `heychunky-apps/<environment>/<app>/` is written to the
|
|
7
|
+
* Vercel scope that environment deploys to.
|
|
8
|
+
*
|
|
9
|
+
* **Always push, never diff.** Vercel writes these as `sensitive`, so no one
|
|
10
|
+
* can read them back — not the dashboard, not the API, not us. "Has this value
|
|
11
|
+
* changed?" is therefore unanswerable, and a sync that tried to answer it would
|
|
12
|
+
* be guessing. Pushing every time is instead safe to repeat, and the only
|
|
13
|
+
* drift that *can* be reported honestly is a name: present, or stray.
|
|
14
|
+
*
|
|
15
|
+
* Without `confirmed` this plans, for the same reason provisioning does — the
|
|
16
|
+
* plan comes from the same walk as the run, so it cannot disagree with it.
|
|
17
|
+
*/
|
|
18
|
+
import { vercel } from "./services/vercel.js";
|
|
19
|
+
import { KEY_FOR } from "./run.js";
|
|
20
|
+
import { DEPLOYS } from "../config/environments.js";
|
|
21
|
+
/** Where an app's values for one environment live. */
|
|
22
|
+
export const folderFor = (app, environment) => `infisical://heychunky-apps/${environment}/${app}/`;
|
|
23
|
+
export async function syncEnv(spec, ctx) {
|
|
24
|
+
const steps = [];
|
|
25
|
+
const planned = !ctx.confirmed;
|
|
26
|
+
const done = () => ({
|
|
27
|
+
app: spec.app,
|
|
28
|
+
planned,
|
|
29
|
+
steps,
|
|
30
|
+
ok: !steps.some((s) => s.state === "failed"),
|
|
31
|
+
});
|
|
32
|
+
const fail = (step, detail) => {
|
|
33
|
+
steps.push({ step, state: "failed", detail });
|
|
34
|
+
return done();
|
|
35
|
+
};
|
|
36
|
+
const where = DEPLOYS[spec.environment];
|
|
37
|
+
if (!where) {
|
|
38
|
+
return fail("environment", `${spec.environment} never deploys — it is a machine, reached by heychunky run`);
|
|
39
|
+
}
|
|
40
|
+
const vc = vercel(await ctx.secrets.get(ctx.reference(KEY_FOR["vercel"])));
|
|
41
|
+
const project = await vc.findProject(spec.app);
|
|
42
|
+
if (!project)
|
|
43
|
+
return fail("vercel project", `no Vercel project for ${spec.app}`);
|
|
44
|
+
steps.push({ step: "vercel project", state: "exists", detail: project.name });
|
|
45
|
+
const teamId = await vc.teamId();
|
|
46
|
+
// A custom environment is created even when planning would not create
|
|
47
|
+
// anything else: without it there is no scope to report against, and it
|
|
48
|
+
// holds no values of its own.
|
|
49
|
+
let scope;
|
|
50
|
+
if ("target" in where) {
|
|
51
|
+
scope = { target: where.target };
|
|
52
|
+
steps.push({ step: "target", state: "exists", detail: where.target });
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
const existing = (await vc.customEnvironments(project.id, teamId))
|
|
56
|
+
.find((e) => e.slug === where.customEnvironment);
|
|
57
|
+
if (existing) {
|
|
58
|
+
scope = { customEnvironmentId: existing.id };
|
|
59
|
+
steps.push({ step: "custom environment", state: "exists", detail: existing.slug });
|
|
60
|
+
}
|
|
61
|
+
else if (planned) {
|
|
62
|
+
steps.push({
|
|
63
|
+
step: "custom environment", state: "would-create", detail: where.customEnvironment,
|
|
64
|
+
});
|
|
65
|
+
scope = undefined;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
const made = await vc.ensureCustomEnvironment(project.id, where.customEnvironment, teamId);
|
|
69
|
+
scope = { customEnvironmentId: made.id };
|
|
70
|
+
steps.push({ step: "custom environment", state: "created", detail: made.slug });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const values = await ctx.secrets.list(folderFor(spec.app, spec.environment));
|
|
74
|
+
const keys = Object.keys(values).sort();
|
|
75
|
+
if (!keys.length) {
|
|
76
|
+
steps.push({
|
|
77
|
+
step: "vault",
|
|
78
|
+
state: "exists",
|
|
79
|
+
detail: `nothing at heychunky-apps/${spec.environment}/${spec.app}`,
|
|
80
|
+
});
|
|
81
|
+
return done();
|
|
82
|
+
}
|
|
83
|
+
// Names already on the project, so a plan can say which are new. Values are
|
|
84
|
+
// never compared, because they cannot be read.
|
|
85
|
+
const present = new Set((await vc.envVars(project.id, teamId))
|
|
86
|
+
.filter((e) => ("target" in where
|
|
87
|
+
? (e.target ?? []).includes(where.target)
|
|
88
|
+
: e.customEnvironmentIds?.length))
|
|
89
|
+
.map((e) => e.key));
|
|
90
|
+
for (const key of keys) {
|
|
91
|
+
if (planned || !scope) {
|
|
92
|
+
steps.push({
|
|
93
|
+
step: key,
|
|
94
|
+
state: present.has(key) ? "exists" : "would-create",
|
|
95
|
+
detail: present.has(key) ? "would be replaced" : "would be set",
|
|
96
|
+
});
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
await vc.setEnv(project.id, key, values[key], scope, { teamId });
|
|
101
|
+
steps.push({ step: key, state: "created", detail: present.has(key) ? "replaced" : "set" });
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
return fail(key, err instanceof Error ? err.message : String(err));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Reported, never removed: a name Vercel has and the vault does not may be
|
|
108
|
+
// set by a platform integration, and deleting it is not this tool's call.
|
|
109
|
+
for (const key of [...present].filter((k) => !(k in values)).sort()) {
|
|
110
|
+
steps.push({ step: key, state: "exists", detail: "stray — on Vercel, not in the vault" });
|
|
111
|
+
}
|
|
112
|
+
return done();
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=syncenv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syncenv.js","sourceRoot":"","sources":["../../src/admin/syncenv.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { RunContext } from "./run.ts";
|
|
2
|
+
export type Role = "superadmin" | "developer";
|
|
3
|
+
export interface User {
|
|
4
|
+
id: string;
|
|
5
|
+
email: string;
|
|
6
|
+
name: string | null;
|
|
7
|
+
role: Role;
|
|
8
|
+
created_at: string;
|
|
9
|
+
}
|
|
10
|
+
export declare class DuplicateUserError extends Error {
|
|
11
|
+
constructor(email: string);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Create an account.
|
|
15
|
+
*
|
|
16
|
+
* The address is stored as given and matched without case, which is what the
|
|
17
|
+
* unique index on `lower(email)` enforces — one person, one account, however
|
|
18
|
+
* they typed it.
|
|
19
|
+
*/
|
|
20
|
+
export declare function inviteUser(invite: {
|
|
21
|
+
email: string;
|
|
22
|
+
name?: string | undefined;
|
|
23
|
+
role?: Role;
|
|
24
|
+
}, ctx: RunContext): Promise<User>;
|
|
25
|
+
/** Everyone with an account. Never their sessions or codes. */
|
|
26
|
+
export declare function listUsers(ctx: RunContext): Promise<User[]>;
|
|
27
|
+
//# sourceMappingURL=users.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/admin/users.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,MAAM,IAAI,GAAG,YAAY,GAAG,WAAW,CAAC;AAE9C,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,IAAI,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,KAAK,EAAE,MAAM;CAI1B;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,IAAI,CAAC,EAAE,IAAI,CAAA;CAAE,EACjE,GAAG,EAAE,UAAU,GACd,OAAO,CAAC,IAAI,CAAC,CAef;AAED,+DAA+D;AAC/D,wBAAsB,SAAS,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAMhE"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who may use the factory.
|
|
3
|
+
*
|
|
4
|
+
* Accounts are made, never self-served. Provisioning creates real
|
|
5
|
+
* repositories, databases and projects in the factory's accounts and on its
|
|
6
|
+
* bill, so an open sign-up form would let a stranger spend someone else's
|
|
7
|
+
* money. Opening it later is a decision; un-provisioning what strangers made
|
|
8
|
+
* is not.
|
|
9
|
+
*
|
|
10
|
+
* There is exactly one superadmin, and the database says so rather than this
|
|
11
|
+
* file — a partial unique index on the role. Inviting a second is refused by
|
|
12
|
+
* Postgres, which is the only place a rule like that survives a rewrite.
|
|
13
|
+
*/
|
|
14
|
+
import { master } from "./master.js";
|
|
15
|
+
export class DuplicateUserError extends Error {
|
|
16
|
+
constructor(email) {
|
|
17
|
+
super(`${email} already has an account`);
|
|
18
|
+
this.name = "DuplicateUserError";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create an account.
|
|
23
|
+
*
|
|
24
|
+
* The address is stored as given and matched without case, which is what the
|
|
25
|
+
* unique index on `lower(email)` enforces — one person, one account, however
|
|
26
|
+
* they typed it.
|
|
27
|
+
*/
|
|
28
|
+
export async function inviteUser(invite, ctx) {
|
|
29
|
+
const email = invite.email.trim();
|
|
30
|
+
if (!email.includes("@"))
|
|
31
|
+
throw new Error(`Not an email address: ${email}`);
|
|
32
|
+
const conn = await master(ctx);
|
|
33
|
+
const { rows } = await conn.write(`insert into users (email, name, role) values ($1, $2, $3)
|
|
34
|
+
on conflict do nothing
|
|
35
|
+
returning id, email, name, role, created_at`, [email, invite.name ?? null, invite.role ?? "developer"]);
|
|
36
|
+
// `on conflict do nothing` returns nothing when the address is taken. Saying
|
|
37
|
+
// so is better than a second account or a silent no-op.
|
|
38
|
+
if (!rows.length)
|
|
39
|
+
throw new DuplicateUserError(email);
|
|
40
|
+
return rows[0];
|
|
41
|
+
}
|
|
42
|
+
/** Everyone with an account. Never their sessions or codes. */
|
|
43
|
+
export async function listUsers(ctx) {
|
|
44
|
+
const conn = await master(ctx);
|
|
45
|
+
const { rows } = await conn.query("select id, email, name, role, created_at from users order by created_at");
|
|
46
|
+
return rows;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=users.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"users.js","sourceRoot":"","sources":["../../src/admin/users.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAarC,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAC3C,YAAY,KAAa;QACvB,KAAK,CAAC,GAAG,KAAK,yBAAyB,CAAC,CAAC;QACzC,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,MAAiE,EACjE,GAAe;IAEf,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IAClC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,KAAK,EAAE,CAAC,CAAC;IAE5E,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAC/B;;iDAE6C,EAC7C,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,IAAI,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,WAAW,CAAC,CACzD,CAAC;IACF,6EAA6E;IAC7E,wDAAwD;IACxD,IAAI,CAAC,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACtD,OAAO,IAAI,CAAC,CAAC,CAAoB,CAAC;AACpC,CAAC;AAED,+DAA+D;AAC/D,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAe;IAC7C,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAC/B,yEAAyE,CAC1E,CAAC;IACF,OAAO,IAAyB,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The environments every app has.
|
|
3
|
+
*
|
|
4
|
+
* One list, because three systems have to agree on it and the cost of them
|
|
5
|
+
* disagreeing is a database that exists with no credential pointing at it, or a
|
|
6
|
+
* credential pointing at a database that was never made:
|
|
7
|
+
*
|
|
8
|
+
* Neon one branch per environment
|
|
9
|
+
* Infisical one folder per environment, holding that branch's DATABASE_URL
|
|
10
|
+
* Vercel three of these are deployed, and three are only ever local
|
|
11
|
+
*
|
|
12
|
+
* `production` is Neon's default branch, which it calls `main`. The other five
|
|
13
|
+
* are branched from it, so every environment starts as a copy-on-write clone of
|
|
14
|
+
* production rather than an empty database that drifts.
|
|
15
|
+
*
|
|
16
|
+
* Which of them a deployment reaches:
|
|
17
|
+
*
|
|
18
|
+
* production Vercel production, from the `production` git branch
|
|
19
|
+
* staging a Vercel custom environment — stable enough to show a client,
|
|
20
|
+
* so no pull request may write to it
|
|
21
|
+
* dev Vercel preview, from `main`. Every preview shares it
|
|
22
|
+
*
|
|
23
|
+
* The rest never deploy. `local1` and `local2` are other developers' machines
|
|
24
|
+
* and `agents` is Chunky's, each reached by `heychunky run` and nothing else.
|
|
25
|
+
*/
|
|
26
|
+
export declare const ENVIRONMENTS: readonly ["production", "staging", "dev", "local1", "local2", "agents"];
|
|
27
|
+
export type Environment = (typeof ENVIRONMENTS)[number];
|
|
28
|
+
/** Neon's name for the default branch, which holds production. */
|
|
29
|
+
export declare const DEFAULT_BRANCH = "main";
|
|
30
|
+
/** The environments that need a branch creating. Production already has one. */
|
|
31
|
+
export declare const BRANCHED: readonly Environment[];
|
|
32
|
+
/**
|
|
33
|
+
* How a deployed environment is addressed on Vercel.
|
|
34
|
+
*
|
|
35
|
+
* Absent means it never deploys: `local1`, `local2` and `agents` are machines,
|
|
36
|
+
* reached by `heychunky run`. Nothing may push a value to them, which is why
|
|
37
|
+
* this is a lookup rather than a mapping over every environment.
|
|
38
|
+
*
|
|
39
|
+
* `staging` names a custom environment rather than a target because Vercel has
|
|
40
|
+
* only three targets and staging is shown to clients — sharing `preview` with
|
|
41
|
+
* every pull request would let one break it.
|
|
42
|
+
*/
|
|
43
|
+
export declare const DEPLOYS: Partial<Record<Environment, {
|
|
44
|
+
target: "production" | "preview";
|
|
45
|
+
} | {
|
|
46
|
+
customEnvironment: string;
|
|
47
|
+
}>>;
|
|
48
|
+
/** The environments a deployment ever reads. */
|
|
49
|
+
export declare const DEPLOYED: readonly Environment[];
|
|
50
|
+
//# sourceMappingURL=environments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environments.d.ts","sourceRoot":"","sources":["../../src/config/environments.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,eAAO,MAAM,YAAY,yEAaf,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAExD,kEAAkE;AAClE,eAAO,MAAM,cAAc,SAAS,CAAC;AAErC,gFAAgF;AAChF,eAAO,MAAM,QAAQ,EAAE,SAAS,WAAW,EAAmD,CAAC;AAE/F;;;;;;;;;;GAUG;AACH,eAAO,MAAM,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE;IAAE,MAAM,EAAE,YAAY,GAAG,SAAS,CAAA;CAAE,GAAG;IAAE,iBAAiB,EAAE,MAAM,CAAA;CAAE,CAAC,CAItH,CAAC;AAEF,gDAAgD;AAChD,eAAO,MAAM,QAAQ,EAAE,SAAS,WAAW,EAA6C,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The environments every app has.
|
|
3
|
+
*
|
|
4
|
+
* One list, because three systems have to agree on it and the cost of them
|
|
5
|
+
* disagreeing is a database that exists with no credential pointing at it, or a
|
|
6
|
+
* credential pointing at a database that was never made:
|
|
7
|
+
*
|
|
8
|
+
* Neon one branch per environment
|
|
9
|
+
* Infisical one folder per environment, holding that branch's DATABASE_URL
|
|
10
|
+
* Vercel three of these are deployed, and three are only ever local
|
|
11
|
+
*
|
|
12
|
+
* `production` is Neon's default branch, which it calls `main`. The other five
|
|
13
|
+
* are branched from it, so every environment starts as a copy-on-write clone of
|
|
14
|
+
* production rather than an empty database that drifts.
|
|
15
|
+
*
|
|
16
|
+
* Which of them a deployment reaches:
|
|
17
|
+
*
|
|
18
|
+
* production Vercel production, from the `production` git branch
|
|
19
|
+
* staging a Vercel custom environment — stable enough to show a client,
|
|
20
|
+
* so no pull request may write to it
|
|
21
|
+
* dev Vercel preview, from `main`. Every preview shares it
|
|
22
|
+
*
|
|
23
|
+
* The rest never deploy. `local1` and `local2` are other developers' machines
|
|
24
|
+
* and `agents` is Chunky's, each reached by `heychunky run` and nothing else.
|
|
25
|
+
*/
|
|
26
|
+
export const ENVIRONMENTS = [
|
|
27
|
+
/** Live. Neon's default branch. */
|
|
28
|
+
"production",
|
|
29
|
+
/** Shown to clients. Stable, and no pull request writes to it. */
|
|
30
|
+
"staging",
|
|
31
|
+
/** What every preview deployment runs against, built from `main`. */
|
|
32
|
+
"dev",
|
|
33
|
+
/** A second developer, locally. */
|
|
34
|
+
"local1",
|
|
35
|
+
/** A third. */
|
|
36
|
+
"local2",
|
|
37
|
+
/** Where Chunky works on features without touching anyone's branch. */
|
|
38
|
+
"agents",
|
|
39
|
+
];
|
|
40
|
+
/** Neon's name for the default branch, which holds production. */
|
|
41
|
+
export const DEFAULT_BRANCH = "main";
|
|
42
|
+
/** The environments that need a branch creating. Production already has one. */
|
|
43
|
+
export const BRANCHED = ENVIRONMENTS.filter((e) => e !== "production");
|
|
44
|
+
/**
|
|
45
|
+
* How a deployed environment is addressed on Vercel.
|
|
46
|
+
*
|
|
47
|
+
* Absent means it never deploys: `local1`, `local2` and `agents` are machines,
|
|
48
|
+
* reached by `heychunky run`. Nothing may push a value to them, which is why
|
|
49
|
+
* this is a lookup rather than a mapping over every environment.
|
|
50
|
+
*
|
|
51
|
+
* `staging` names a custom environment rather than a target because Vercel has
|
|
52
|
+
* only three targets and staging is shown to clients — sharing `preview` with
|
|
53
|
+
* every pull request would let one break it.
|
|
54
|
+
*/
|
|
55
|
+
export const DEPLOYS = {
|
|
56
|
+
production: { target: "production" },
|
|
57
|
+
staging: { customEnvironment: "staging" },
|
|
58
|
+
dev: { target: "preview" },
|
|
59
|
+
};
|
|
60
|
+
/** The environments a deployment ever reads. */
|
|
61
|
+
export const DEPLOYED = ENVIRONMENTS.filter((e) => e in DEPLOYS);
|
|
62
|
+
//# sourceMappingURL=environments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environments.js","sourceRoot":"","sources":["../../src/config/environments.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,mCAAmC;IACnC,YAAY;IACZ,kEAAkE;IAClE,SAAS;IACT,qEAAqE;IACrE,KAAK;IACL,mCAAmC;IACnC,QAAQ;IACR,eAAe;IACf,QAAQ;IACR,uEAAuE;IACvE,QAAQ;CACA,CAAC;AAIX,kEAAkE;AAClE,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC;AAErC,gFAAgF;AAChF,MAAM,CAAC,MAAM,QAAQ,GAA2B,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,YAAY,CAAC,CAAC;AAE/F;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,OAAO,GAAuG;IACzH,UAAU,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE;IACpC,OAAO,EAAE,EAAE,iBAAiB,EAAE,SAAS,EAAE;IACzC,GAAG,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE;CAC3B,CAAC;AAEF,gDAAgD;AAChD,MAAM,CAAC,MAAM,QAAQ,GAA2B,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How an app's name maps onto the names providers actually use.
|
|
3
|
+
*
|
|
4
|
+
* There is no single right answer, because providers were named by hand over
|
|
5
|
+
* years and disagree with each other. Today, for the same estate:
|
|
6
|
+
*
|
|
7
|
+
* delters.com → Vercel project "delters" (bare name)
|
|
8
|
+
* carmelcity.ai → Vercel project "carmelcity-ai" (dots to hyphens)
|
|
9
|
+
*
|
|
10
|
+
* So a lookup cannot slug one way and hope. It generates the forms a provider
|
|
11
|
+
* plausibly used and tries them in order of how likely they are to be right.
|
|
12
|
+
*
|
|
13
|
+
* The previous rule stripped every non-alphanumeric character, turning
|
|
14
|
+
* "delters.com" into "delterscom" — which matched nothing, ever. `sync-env`
|
|
15
|
+
* therefore never found a project for any app, and the apps report claimed
|
|
16
|
+
* there were no Vercel projects while eleven existed.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* The product name without its domain suffix.
|
|
20
|
+
* `delters.com` → `delters`, `carmel.city` → `carmel`, `heychunky` → `heychunky`.
|
|
21
|
+
*/
|
|
22
|
+
export declare function baseName(app: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Every form a provider might have used for this app, most likely first.
|
|
25
|
+
* Deduplicated, so a name with no dot yields exactly one candidate.
|
|
26
|
+
*
|
|
27
|
+
* carmelcity.ai → ["carmelcity-ai", "carmelcity", "carmelcityai"]
|
|
28
|
+
* delters.com → ["delters-com", "delters", "delterscom"]
|
|
29
|
+
* heychunky → ["heychunky"]
|
|
30
|
+
*/
|
|
31
|
+
export declare function nameCandidates(app: string): string[];
|
|
32
|
+
/**
|
|
33
|
+
* Find the entry whose name matches this app, preferring earlier candidates.
|
|
34
|
+
* Returns undefined rather than throwing — a missing project is a normal
|
|
35
|
+
* state during provisioning, not an error.
|
|
36
|
+
*/
|
|
37
|
+
export declare function matchByName<T>(app: string, items: readonly T[], nameOf: (item: T) => string): T | undefined;
|
|
38
|
+
/** The app's storage zone names on Bunny: `<base>` and `<base>-private`. */
|
|
39
|
+
export declare function zoneNames(app: string): {
|
|
40
|
+
public: string;
|
|
41
|
+
private: string;
|
|
42
|
+
};
|
|
43
|
+
/** Subdomains the factory attaches to an app's domain. */
|
|
44
|
+
export declare function subdomains(domain: string): Record<string, string>;
|
|
45
|
+
//# sourceMappingURL=names.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"names.d.ts","sourceRoot":"","sources":["../../src/config/names.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAUH;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG5C;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAKpD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAC3B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAC1B,CAAC,GAAG,SAAS,CAOf;AAED,4EAA4E;AAC5E,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAG1E;AAED,0DAA0D;AAC1D,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOjE"}
|