@geekmidas/envkit 1.0.7 → 1.1.1
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/CHANGELOG.md +48 -0
- package/dist/sst.cjs +280 -2
- package/dist/sst.cjs.map +1 -1
- package/dist/sst.d.cts +166 -2
- package/dist/sst.d.cts.map +1 -1
- package/dist/sst.d.mts +166 -2
- package/dist/sst.d.mts.map +1 -1
- package/dist/sst.mjs +273 -3
- package/dist/sst.mjs.map +1 -1
- package/package.json +2 -2
- package/src/SstEnvValidator.ts +369 -0
- package/src/SstEnvironmentBuilder.ts +26 -0
- package/src/__tests__/SstEnvValidator.spec.ts +236 -0
- package/src/__tests__/SstEnvironmentBuilder.spec.ts +4 -0
- package/src/__tests__/sst.spec.ts +36 -0
- package/src/sst.ts +16 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
# @geekmidas/envkit
|
|
2
2
|
|
|
3
|
+
## 1.1.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 🐛 [#11](https://github.com/geekmidas/toolbox/pull/11) [`40f4dc0`](https://github.com/geekmidas/toolbox/commit/40f4dc095911b2223a255029d8f776caf7781309) Thanks [@geekmidas](https://github.com/geekmidas)! - Patch release across all packages to realign published versions with the
|
|
8
|
+
registry. The previous release only published the four packages that had
|
|
9
|
+
version bumps; the remaining packages failed with "cannot publish over the
|
|
10
|
+
previously published versions" because their versions were unchanged.
|
|
11
|
+
|
|
12
|
+
## 1.1.0
|
|
13
|
+
|
|
14
|
+
### Minor Changes
|
|
15
|
+
|
|
16
|
+
- ✨ [#8](https://github.com/geekmidas/toolbox/pull/8) [`7323f34`](https://github.com/geekmidas/toolbox/commit/7323f34176d63170dd53450889ac0b5959420c3c) Thanks [@geekmidas](https://github.com/geekmidas)! - feat(envkit): add Queue resolver and publisher connection strings (sst)
|
|
17
|
+
|
|
18
|
+
`@geekmidas/envkit/sst` gains a `Queue` resource type (`ResourceType.Queue` /
|
|
19
|
+
`SSTQueue`) whose resolver emits `<NAME>_URL`, `<NAME>_ARN`, and a
|
|
20
|
+
`<NAME>_PUBLISHER_CONNECTION_STRING` (`sqs://?queueUrl=…`). The SNS topic
|
|
21
|
+
resolver now also emits `<NAME>_PUBLISHER_CONNECTION_STRING` (`sns://?topicArn=…`).
|
|
22
|
+
|
|
23
|
+
These name-namespaced connection strings are what `@geekmidas/events`'
|
|
24
|
+
`Publisher.fromConnectionString` consumes, so a linked queue/topic resolves to a
|
|
25
|
+
ready-to-use publisher (the protocol selects the transport — SQS/SNS deployed,
|
|
26
|
+
or a local backend in dev).
|
|
27
|
+
|
|
28
|
+
- ✨ [#8](https://github.com/geekmidas/toolbox/pull/8) [`79e2929`](https://github.com/geekmidas/toolbox/commit/79e292978d3dbc8927e25814bdb051d1c380600a) Thanks [@geekmidas](https://github.com/geekmidas)! - feat(envkit): add an SST env-var validator to `@geekmidas/envkit/sst`
|
|
29
|
+
|
|
30
|
+
Adds `EnvValidator`, `resolveEnvKeys`, and `EnvValidationError` alongside
|
|
31
|
+
`SstEnvironmentBuilder`, so a deployable unit's required environment variables
|
|
32
|
+
can be validated **before** deploy — at `sst.config.ts` synth time.
|
|
33
|
+
- `resolveEnvKeys` derives the env-var keys a set of linked resources will
|
|
34
|
+
produce by replaying the **same** `sstResolvers` used at runtime (reduced to
|
|
35
|
+
the resource `type`, so no SST `Output` value is ever read). The infra-time
|
|
36
|
+
validator and the runtime resolution share a single source of truth — they
|
|
37
|
+
cannot drift, and there is no parallel suffix table to maintain.
|
|
38
|
+
- `EnvValidationError` is a structured, catchable error carrying `.missing`,
|
|
39
|
+
`.available`, `.suggestions`, and `.context`. Its message names the failing
|
|
40
|
+
unit and gives a nearest-match "did you mean DB_URL?" hint per missing
|
|
41
|
+
variable (edit-distance + token-overlap).
|
|
42
|
+
- Platform whitelists are **exported, never assumed** — SST deploys to AWS, GCP,
|
|
43
|
+
and Cloudflare. `AWS_RUNTIME_ENV_VARS`, `GCP_RUNTIME_ENV_VARS`,
|
|
44
|
+
`CLOUDFLARE_RUNTIME_ENV_VARS`, the `PLATFORM_ENV_VARS` registry, and
|
|
45
|
+
`platformEnvVars(platform)` are exported; the caller opts in via
|
|
46
|
+
`new EnvValidator(links, { platform })`. Optional vars are marked with a
|
|
47
|
+
trailing `?`.
|
|
48
|
+
- `getProvidersForEnvVars(requested)` returns the link names that provide a
|
|
49
|
+
requested var, for least-privilege linking (attach only the links a unit needs).
|
|
50
|
+
|
|
3
51
|
## 1.0.7
|
|
4
52
|
|
|
5
53
|
### Patch Changes
|
package/dist/sst.cjs
CHANGED
|
@@ -13,6 +13,7 @@ let ResourceType = /* @__PURE__ */ function(ResourceType$1) {
|
|
|
13
13
|
ResourceType$1["Vpc"] = "sst.aws.Vpc";
|
|
14
14
|
ResourceType$1["Secret"] = "sst.sst.Secret";
|
|
15
15
|
ResourceType$1["Dynamo"] = "sst.aws.Dynamo";
|
|
16
|
+
ResourceType$1["Queue"] = "sst.aws.Queue";
|
|
16
17
|
ResourceType$1["SSTSecret"] = "sst:sst:Secret";
|
|
17
18
|
ResourceType$1["SSTFunction"] = "sst:sst:Function";
|
|
18
19
|
ResourceType$1["SSTApiGatewayV2"] = "sst:aws:ApiGatewayV2";
|
|
@@ -20,6 +21,7 @@ let ResourceType = /* @__PURE__ */ function(ResourceType$1) {
|
|
|
20
21
|
ResourceType$1["SSTBucket"] = "sst:aws:Bucket";
|
|
21
22
|
ResourceType$1["SnsTopic"] = "sst:aws:SnsTopic";
|
|
22
23
|
ResourceType$1["SSTDynamo"] = "sst:aws:Dynamo";
|
|
24
|
+
ResourceType$1["SSTQueue"] = "sst:aws:Queue";
|
|
23
25
|
return ResourceType$1;
|
|
24
26
|
}({});
|
|
25
27
|
const secretResolver = (name, value) => ({ [name]: value.value });
|
|
@@ -32,7 +34,15 @@ const postgresResolver = (key, value) => ({
|
|
|
32
34
|
[`${key}Url`]: `postgresql://${encodeURIComponent(value.username)}:${encodeURIComponent(value.password)}@${value.host}:${value.port}/${value.database}`
|
|
33
35
|
});
|
|
34
36
|
const bucketResolver = (name, value) => ({ [`${name}Name`]: value.name });
|
|
35
|
-
const topicResolver = (name, value) => ({
|
|
37
|
+
const topicResolver = (name, value) => ({
|
|
38
|
+
[`${name}Arn`]: value.arn,
|
|
39
|
+
[`${name}PublisherConnectionString`]: `sns://?topicArn=${encodeURIComponent(value.arn)}`
|
|
40
|
+
});
|
|
41
|
+
const queueResolver = (name, value) => ({
|
|
42
|
+
[`${name}Url`]: value.url,
|
|
43
|
+
[`${name}Arn`]: value.arn,
|
|
44
|
+
[`${name}PublisherConnectionString`]: `sqs://?queueUrl=${encodeURIComponent(value.url)}`
|
|
45
|
+
});
|
|
36
46
|
const dynamoResolver = (name, value) => ({ [`${name}Name`]: value.name });
|
|
37
47
|
const noopResolver = () => ({});
|
|
38
48
|
/**
|
|
@@ -46,13 +56,15 @@ const sstResolvers = {
|
|
|
46
56
|
[ResourceType.Postgres]: postgresResolver,
|
|
47
57
|
[ResourceType.Bucket]: bucketResolver,
|
|
48
58
|
[ResourceType.Dynamo]: dynamoResolver,
|
|
59
|
+
[ResourceType.Queue]: queueResolver,
|
|
49
60
|
[ResourceType.SSTSecret]: secretResolver,
|
|
50
61
|
[ResourceType.SSTBucket]: bucketResolver,
|
|
51
62
|
[ResourceType.SSTFunction]: noopResolver,
|
|
52
63
|
[ResourceType.SSTPostgres]: postgresResolver,
|
|
53
64
|
[ResourceType.SSTApiGatewayV2]: noopResolver,
|
|
54
65
|
[ResourceType.SnsTopic]: topicResolver,
|
|
55
|
-
[ResourceType.SSTDynamo]: dynamoResolver
|
|
66
|
+
[ResourceType.SSTDynamo]: dynamoResolver,
|
|
67
|
+
[ResourceType.SSTQueue]: queueResolver
|
|
56
68
|
};
|
|
57
69
|
/**
|
|
58
70
|
* SST-specific environment builder with built-in resolvers for all known
|
|
@@ -109,6 +121,264 @@ var SstEnvironmentBuilder = class {
|
|
|
109
121
|
}
|
|
110
122
|
};
|
|
111
123
|
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/SstEnvValidator.ts
|
|
126
|
+
/**
|
|
127
|
+
* Variables AWS Lambda injects into the runtime. Always valid on AWS, but never
|
|
128
|
+
* assumed — opt in via `platform: 'aws'` or by spreading {@link AWS_RUNTIME_ENV_VARS}
|
|
129
|
+
* into `whitelist`.
|
|
130
|
+
*/
|
|
131
|
+
const AWS_RUNTIME_ENV_VARS = [
|
|
132
|
+
"AWS_REGION",
|
|
133
|
+
"AWS_DEFAULT_REGION",
|
|
134
|
+
"AWS_ENDPOINT_URL",
|
|
135
|
+
"AWS_ENDPOINT",
|
|
136
|
+
"AWS_ACCESS_KEY_ID",
|
|
137
|
+
"AWS_SECRET_ACCESS_KEY",
|
|
138
|
+
"AWS_SESSION_TOKEN",
|
|
139
|
+
"AWS_LAMBDA_FUNCTION_NAME",
|
|
140
|
+
"AWS_LAMBDA_FUNCTION_VERSION"
|
|
141
|
+
];
|
|
142
|
+
/**
|
|
143
|
+
* Variables GCP injects into Cloud Functions / Cloud Run.
|
|
144
|
+
*/
|
|
145
|
+
const GCP_RUNTIME_ENV_VARS = [
|
|
146
|
+
"GOOGLE_CLOUD_PROJECT",
|
|
147
|
+
"GCP_PROJECT",
|
|
148
|
+
"FUNCTION_TARGET",
|
|
149
|
+
"FUNCTION_SIGNATURE_TYPE",
|
|
150
|
+
"K_SERVICE",
|
|
151
|
+
"K_REVISION",
|
|
152
|
+
"K_CONFIGURATION",
|
|
153
|
+
"PORT"
|
|
154
|
+
];
|
|
155
|
+
/**
|
|
156
|
+
* Variables Cloudflare exposes to Workers. Workers inject almost nothing into
|
|
157
|
+
* the global env, so this is intentionally minimal.
|
|
158
|
+
*/
|
|
159
|
+
const CLOUDFLARE_RUNTIME_ENV_VARS = ["CF_PAGES", "CF_PAGES_URL"];
|
|
160
|
+
/**
|
|
161
|
+
* Registry of platform → always-valid runtime variables. Export, don't assume:
|
|
162
|
+
* a caller states the platform it deploys to and the matching set is trusted.
|
|
163
|
+
*/
|
|
164
|
+
const PLATFORM_ENV_VARS = {
|
|
165
|
+
aws: AWS_RUNTIME_ENV_VARS,
|
|
166
|
+
gcp: GCP_RUNTIME_ENV_VARS,
|
|
167
|
+
cloudflare: CLOUDFLARE_RUNTIME_ENV_VARS
|
|
168
|
+
};
|
|
169
|
+
/**
|
|
170
|
+
* The always-valid runtime variables for a platform. The "whitelist construct":
|
|
171
|
+
* resolve a platform to its set, to spread into `whitelist` or compare against.
|
|
172
|
+
*/
|
|
173
|
+
function platformEnvVars(platform) {
|
|
174
|
+
return PLATFORM_ENV_VARS[platform];
|
|
175
|
+
}
|
|
176
|
+
/** How many link-provided vars to list in an error before truncating. */
|
|
177
|
+
const MAX_LISTED_VARS = 8;
|
|
178
|
+
/** Minimum similarity for a variable to be offered as a "did you mean". */
|
|
179
|
+
const SUGGESTION_THRESHOLD = .3;
|
|
180
|
+
/**
|
|
181
|
+
* Levenshtein edit distance between two strings.
|
|
182
|
+
*/
|
|
183
|
+
function levenshtein(a, b) {
|
|
184
|
+
const m = a.length;
|
|
185
|
+
const n = b.length;
|
|
186
|
+
if (m === 0) return n;
|
|
187
|
+
if (n === 0) return m;
|
|
188
|
+
let prev = Array.from({ length: n + 1 }, (_, i) => i);
|
|
189
|
+
for (let i = 1; i <= m; i++) {
|
|
190
|
+
const curr = [i];
|
|
191
|
+
for (let j = 1; j <= n; j++) {
|
|
192
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
193
|
+
const del = (prev[j] ?? 0) + 1;
|
|
194
|
+
const ins = (curr[j - 1] ?? 0) + 1;
|
|
195
|
+
const sub = (prev[j - 1] ?? 0) + cost;
|
|
196
|
+
curr[j] = Math.min(del, ins, sub);
|
|
197
|
+
}
|
|
198
|
+
prev = curr;
|
|
199
|
+
}
|
|
200
|
+
return prev[n] ?? 0;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Similarity in `[0, 1]` combining edit distance with `_`-token overlap, so both
|
|
204
|
+
* near-typos (a missing underscore) and renames (`DATABASE_URL` ~ `DB_URL`,
|
|
205
|
+
* which share the `URL` token) are caught.
|
|
206
|
+
*/
|
|
207
|
+
function similarity(a, b) {
|
|
208
|
+
const editSim = 1 - levenshtein(a, b) / Math.max(a.length, b.length, 1);
|
|
209
|
+
const ta = new Set(a.split("_").filter(Boolean));
|
|
210
|
+
const tb = new Set(b.split("_").filter(Boolean));
|
|
211
|
+
const shared = [...ta].filter((t) => tb.has(t)).length;
|
|
212
|
+
const union = new Set([...ta, ...tb]).size || 1;
|
|
213
|
+
const tokenSim = shared / union;
|
|
214
|
+
return Math.max(editSim, tokenSim * 1.15);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Returns the nearest available variables to `missing`, best first, limited to
|
|
218
|
+
* those above {@link SUGGESTION_THRESHOLD}.
|
|
219
|
+
*/
|
|
220
|
+
function suggestionsFor(missing, available, max = 1) {
|
|
221
|
+
return available.map((candidate) => ({
|
|
222
|
+
candidate,
|
|
223
|
+
score: similarity(missing, candidate)
|
|
224
|
+
})).filter((s) => s.score >= SUGGESTION_THRESHOLD).sort((a, b) => b.score - a.score).slice(0, max).map((s) => s.candidate);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Thrown when a deployable unit requires environment variables its links do not
|
|
228
|
+
* provide. Carries the structured `missing`/`available`/`suggestions` so callers
|
|
229
|
+
* can inspect the failure programmatically, not just read the message.
|
|
230
|
+
*/
|
|
231
|
+
var EnvValidationError = class EnvValidationError extends Error {
|
|
232
|
+
isEnvValidationError = true;
|
|
233
|
+
missing;
|
|
234
|
+
available;
|
|
235
|
+
suggestions;
|
|
236
|
+
context;
|
|
237
|
+
constructor(args) {
|
|
238
|
+
super(EnvValidationError.format(args));
|
|
239
|
+
this.name = "EnvValidationError";
|
|
240
|
+
this.missing = args.missing;
|
|
241
|
+
this.available = args.available;
|
|
242
|
+
this.suggestions = args.suggestions;
|
|
243
|
+
this.context = args.context;
|
|
244
|
+
Error.captureStackTrace?.(this, EnvValidationError);
|
|
245
|
+
}
|
|
246
|
+
static format(args) {
|
|
247
|
+
const { missing, linkVars, suggestions, context } = args;
|
|
248
|
+
const who = context ? `'${context}'` : "a deployable unit";
|
|
249
|
+
const width = Math.max(...missing.map((v) => v.length));
|
|
250
|
+
const lines = missing.map((v) => {
|
|
251
|
+
const hints = suggestions[v] ?? [];
|
|
252
|
+
const hint = hints.length ? `(did you mean ${hints.join(" or ")}?)` : "";
|
|
253
|
+
return ` - ${v.padEnd(width)} ${hint}`.trimEnd();
|
|
254
|
+
});
|
|
255
|
+
let provided;
|
|
256
|
+
if (linkVars.length === 0) provided = "(no links provide environment variables)";
|
|
257
|
+
else {
|
|
258
|
+
const shown = linkVars.slice(0, MAX_LISTED_VARS).join(", ");
|
|
259
|
+
const extra = linkVars.length > MAX_LISTED_VARS ? ` ...(+${linkVars.length - MAX_LISTED_VARS})` : "";
|
|
260
|
+
provided = `Provided by links: ${shown}${extra}`;
|
|
261
|
+
}
|
|
262
|
+
return `${who} is missing required env vars:\n${lines.join("\n")}\n\n${provided}`;
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
/**
|
|
266
|
+
* Derives the set of environment-variable **keys** a group of linked resources
|
|
267
|
+
* will produce at runtime — without resolving any value.
|
|
268
|
+
*
|
|
269
|
+
* This reuses the runtime resolvers (`sstResolvers`) via {@link EnvironmentBuilder},
|
|
270
|
+
* so the keys computed here are exactly the keys a deployed function receives:
|
|
271
|
+
* the validator and the runtime resolution share a single source of truth and
|
|
272
|
+
* cannot drift. Because every resolver's output keys depend only on the record
|
|
273
|
+
* key and the resource `type` (never the value), each resource is reduced to
|
|
274
|
+
* `{ type }` before building, so no SST `Output` is ever read.
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* resolveEnvKeys({ db: { type: ResourceType.Postgres } });
|
|
278
|
+
* // ['DB_NAME', 'DB_HOST', 'DB_PASSWORD', 'DB_PORT', 'DB_USERNAME', 'DB_URL']
|
|
279
|
+
*/
|
|
280
|
+
function resolveEnvKeys(record) {
|
|
281
|
+
const typeOnly = {};
|
|
282
|
+
for (const [key, value] of Object.entries(record)) typeOnly[key] = typeof value === "string" ? "" : { type: value.type };
|
|
283
|
+
return Object.keys(new require_EnvironmentBuilder.EnvironmentBuilder(typeOnly, sstResolvers).build());
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Validates that the environment variables a deployable unit requires are
|
|
287
|
+
* actually provided by its linked resources — at infra time, before deploy.
|
|
288
|
+
*
|
|
289
|
+
* Available variables are the union of the keys derived from `links`
|
|
290
|
+
* ({@link resolveEnvKeys}), the chosen platform's runtime variables (only when
|
|
291
|
+
* `options.platform` is set — nothing is assumed), and any `options.whitelist`
|
|
292
|
+
* (e.g. the keys of a function's explicit `environment`). A requested variable
|
|
293
|
+
* suffixed with `?` is treated as optional and never causes a failure.
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* const validator = new EnvValidator(
|
|
297
|
+
* { db: { type: ResourceType.Postgres } },
|
|
298
|
+
* { platform: 'aws', whitelist: ['APP_NAME'], context: 'orders-fn' },
|
|
299
|
+
* );
|
|
300
|
+
* validator.assert(['DB_HOST', 'AWS_REGION', 'APP_NAME', 'SENTRY_DSN?']); // ok
|
|
301
|
+
* validator.assert(['DATABASE_URL']); // throws EnvValidationError (did you mean DB_URL?)
|
|
302
|
+
*/
|
|
303
|
+
var EnvValidator = class {
|
|
304
|
+
/** Every variable available — link-derived, platform, and extra whitelist. */
|
|
305
|
+
availableVars;
|
|
306
|
+
/** Just the variables the links provide (used for error context). */
|
|
307
|
+
linkVars;
|
|
308
|
+
context;
|
|
309
|
+
availableSet;
|
|
310
|
+
/** Per-link (record key → its env-var keys), for least-privilege filtering. */
|
|
311
|
+
linkVarsByName;
|
|
312
|
+
constructor(links, options = {}) {
|
|
313
|
+
const { platform, whitelist = [], context } = options;
|
|
314
|
+
this.context = context;
|
|
315
|
+
this.linkVarsByName = new Map(Object.entries(links).map(([name, value]) => [name, resolveEnvKeys({ [name]: value })]));
|
|
316
|
+
this.linkVars = [...this.linkVarsByName.values()].flat();
|
|
317
|
+
this.availableVars = [
|
|
318
|
+
...this.linkVars,
|
|
319
|
+
...platform ? PLATFORM_ENV_VARS[platform] : [],
|
|
320
|
+
...whitelist
|
|
321
|
+
];
|
|
322
|
+
this.availableSet = new Set(this.availableVars);
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Returns the names of the links that provide at least one of the requested
|
|
326
|
+
* variables — so a construct can attach only the links a unit actually needs
|
|
327
|
+
* (least privilege) rather than the whole pool. Trailing `?` is ignored.
|
|
328
|
+
*/
|
|
329
|
+
getProvidersForEnvVars(requestedEnvVars) {
|
|
330
|
+
const requested = new Set(requestedEnvVars.map((v) => v.endsWith("?") ? v.slice(0, -1) : v));
|
|
331
|
+
const providers = [];
|
|
332
|
+
for (const [name, vars] of this.linkVarsByName) if (vars.some((v) => requested.has(v))) providers.push(name);
|
|
333
|
+
return providers;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Checks every requested variable against the available set. Variables
|
|
337
|
+
* ending in `?` are optional and never reported as invalid. Invalid
|
|
338
|
+
* variables come back with their nearest available matches.
|
|
339
|
+
*/
|
|
340
|
+
validate(requestedEnvVars) {
|
|
341
|
+
const invalidVars = requestedEnvVars.filter((envVar) => {
|
|
342
|
+
if (envVar.endsWith("?")) return false;
|
|
343
|
+
return !this.availableSet.has(envVar);
|
|
344
|
+
});
|
|
345
|
+
const suggestions = {};
|
|
346
|
+
for (const envVar of invalidVars) {
|
|
347
|
+
const matches = suggestionsFor(envVar, this.availableVars);
|
|
348
|
+
if (matches.length) suggestions[envVar] = matches;
|
|
349
|
+
}
|
|
350
|
+
return {
|
|
351
|
+
valid: invalidVars.length === 0,
|
|
352
|
+
invalidVars,
|
|
353
|
+
availableVars: this.availableVars,
|
|
354
|
+
suggestions
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Asserts that every requested variable is available, throwing an
|
|
359
|
+
* {@link EnvValidationError} otherwise. Intended to run in a construct's
|
|
360
|
+
* constructor so a misconfigured `sst.config.ts` fails at synth time, before
|
|
361
|
+
* any AWS call.
|
|
362
|
+
*/
|
|
363
|
+
assert(requestedEnvVars) {
|
|
364
|
+
const { valid, invalidVars, suggestions } = this.validate(requestedEnvVars);
|
|
365
|
+
if (!valid) throw new EnvValidationError({
|
|
366
|
+
missing: invalidVars,
|
|
367
|
+
available: this.availableVars,
|
|
368
|
+
linkVars: this.linkVars,
|
|
369
|
+
suggestions,
|
|
370
|
+
context: this.context
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Whether a single variable is available. Does not treat a trailing `?` as
|
|
375
|
+
* optional — pass the bare name.
|
|
376
|
+
*/
|
|
377
|
+
has(envVar) {
|
|
378
|
+
return this.availableSet.has(envVar);
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
|
|
112
382
|
//#endregion
|
|
113
383
|
//#region src/sst.ts
|
|
114
384
|
/**
|
|
@@ -132,9 +402,17 @@ function normalizeResourceEnv(record) {
|
|
|
132
402
|
}
|
|
133
403
|
|
|
134
404
|
//#endregion
|
|
405
|
+
exports.AWS_RUNTIME_ENV_VARS = AWS_RUNTIME_ENV_VARS;
|
|
406
|
+
exports.CLOUDFLARE_RUNTIME_ENV_VARS = CLOUDFLARE_RUNTIME_ENV_VARS;
|
|
407
|
+
exports.EnvValidationError = EnvValidationError;
|
|
408
|
+
exports.EnvValidator = EnvValidator;
|
|
409
|
+
exports.GCP_RUNTIME_ENV_VARS = GCP_RUNTIME_ENV_VARS;
|
|
410
|
+
exports.PLATFORM_ENV_VARS = PLATFORM_ENV_VARS;
|
|
135
411
|
exports.ResourceType = ResourceType;
|
|
136
412
|
exports.SstEnvironmentBuilder = SstEnvironmentBuilder;
|
|
137
413
|
exports.environmentCase = require_EnvironmentBuilder.environmentCase;
|
|
138
414
|
exports.normalizeResourceEnv = normalizeResourceEnv;
|
|
415
|
+
exports.platformEnvVars = platformEnvVars;
|
|
416
|
+
exports.resolveEnvKeys = resolveEnvKeys;
|
|
139
417
|
exports.sstResolvers = sstResolvers;
|
|
140
418
|
//# sourceMappingURL=sst.cjs.map
|
package/dist/sst.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sst.cjs","names":["name: string","value: SecretValue","key: string","value: PostgresValue","value: BucketValue","value: SnsTopicValue","value: DynamoValue","sstResolvers: Resolvers","record: TRecord","additionalResolvers?: CustomResolvers<TRecord>","options?: EnvironmentBuilderOptions","mergedResolvers: Resolvers","EnvironmentBuilder","record: Record<string, SstResource | string>"],"sources":["../src/SstEnvironmentBuilder.ts","../src/sst.ts"],"sourcesContent":["import {\n\tEnvironmentBuilder,\n\ttype EnvironmentBuilderOptions,\n\ttype EnvironmentResolver,\n\ttype EnvRecord,\n\ttype InputValue,\n\ttype Resolvers,\n} from './EnvironmentBuilder';\n\n/**\n * Enumeration of supported SST (Serverless Stack Toolkit) resource types.\n * Used to identify and process different AWS and SST resources.\n */\nexport enum ResourceType {\n\t// Legacy format (dot notation)\n\tApiGatewayV2 = 'sst.aws.ApiGatewayV2',\n\tPostgres = 'sst.aws.Postgres',\n\tFunction = 'sst.aws.Function',\n\tBucket = 'sst.aws.Bucket',\n\tVpc = 'sst.aws.Vpc',\n\tSecret = 'sst.sst.Secret',\n\tDynamo = 'sst.aws.Dynamo',\n\n\t// Modern format (colon notation)\n\tSSTSecret = 'sst:sst:Secret',\n\tSSTFunction = 'sst:sst:Function',\n\tSSTApiGatewayV2 = 'sst:aws:ApiGatewayV2',\n\tSSTPostgres = 'sst:aws:Postgres',\n\tSSTBucket = 'sst:aws:Bucket',\n\tSnsTopic = 'sst:aws:SnsTopic',\n\tSSTDynamo = 'sst:aws:Dynamo',\n}\n\n/**\n * AWS API Gateway V2 resource type.\n * Represents an HTTP/WebSocket API.\n */\nexport type ApiGatewayV2 = {\n\ttype: ResourceType.ApiGatewayV2 | ResourceType.SSTApiGatewayV2;\n\turl: string;\n};\n\n/**\n * PostgreSQL database resource type.\n * Contains all connection details needed to connect to the database.\n */\nexport type Postgres = {\n\ttype: ResourceType.Postgres | ResourceType.SSTPostgres;\n\tdatabase: string;\n\thost: string;\n\tpassword: string;\n\tport: number;\n\tusername: string;\n};\n\n/**\n * AWS Lambda Function resource type.\n */\nexport type Function = {\n\ttype: ResourceType.Function | ResourceType.SSTFunction;\n\tname: string;\n};\n\n/**\n * AWS S3 Bucket resource type.\n */\nexport type Bucket = {\n\ttype: ResourceType.Bucket | ResourceType.SSTBucket;\n\tname: string;\n};\n\n/**\n * AWS VPC (Virtual Private Cloud) resource type.\n */\nexport type Vpc = {\n\ttype: ResourceType.Vpc;\n\tbastion: string;\n};\n\n/**\n * Secret resource type for storing sensitive values.\n */\nexport type Secret = {\n\ttype: ResourceType.Secret | ResourceType.SSTSecret;\n\tvalue: string;\n};\n\n/**\n * AWS SNS Topic resource type.\n */\nexport type SnsTopic = {\n\ttype: ResourceType.SnsTopic;\n\tarn: string;\n};\n\n/**\n * AWS DynamoDB Table resource type.\n */\nexport type Dynamo = {\n\ttype: ResourceType.Dynamo | ResourceType.SSTDynamo;\n\tname: string;\n};\n\n/**\n * Union type of all supported SST resource types.\n */\nexport type SstResource =\n\t| ApiGatewayV2\n\t| Postgres\n\t| Function\n\t| Bucket\n\t| Vpc\n\t| Secret\n\t| SnsTopic\n\t| Dynamo;\n\n// Value types without the `type` key (for resolver parameters)\ntype SecretValue = Omit<Secret, 'type'>;\ntype PostgresValue = Omit<Postgres, 'type'>;\ntype BucketValue = Omit<Bucket, 'type'>;\ntype SnsTopicValue = Omit<SnsTopic, 'type'>;\ntype DynamoValue = Omit<Dynamo, 'type'>;\n\n/**\n * Function type for processing a specific resource type into environment variables.\n *\n * @template K - The specific resource type (without `type` key)\n * @param name - The resource name\n * @param value - The resource value (without `type` key)\n * @returns Object mapping environment variable names to values\n */\nexport type ResourceProcessor<K> = (name: string, value: K) => EnvRecord;\n\n// SST Resource Resolvers (receive values without `type` key)\n\nconst secretResolver = (name: string, value: SecretValue) => ({\n\t[name]: value.value,\n});\n\nconst postgresResolver = (key: string, value: PostgresValue) => ({\n\t[`${key}Name`]: value.database,\n\t[`${key}Host`]: value.host,\n\t[`${key}Password`]: value.password,\n\t[`${key}Port`]: value.port,\n\t[`${key}Username`]: value.username,\n\t[`${key}Url`]: `postgresql://${encodeURIComponent(value.username)}:${encodeURIComponent(value.password)}@${value.host}:${value.port}/${value.database}`,\n});\n\nconst bucketResolver = (name: string, value: BucketValue) => ({\n\t[`${name}Name`]: value.name,\n});\n\nconst topicResolver = (name: string, value: SnsTopicValue) => ({\n\t[`${name}Arn`]: value.arn,\n});\n\nconst dynamoResolver = (name: string, value: DynamoValue) => ({\n\t[`${name}Name`]: value.name,\n});\n\nconst noopResolver = () => ({});\n\n/**\n * Pre-configured resolvers for all SST resource types.\n */\nexport const sstResolvers: Resolvers = {\n\t// Legacy format\n\t[ResourceType.ApiGatewayV2]: noopResolver,\n\t[ResourceType.Function]: noopResolver,\n\t[ResourceType.Vpc]: noopResolver,\n\t[ResourceType.Secret]: secretResolver,\n\t[ResourceType.Postgres]: postgresResolver,\n\t[ResourceType.Bucket]: bucketResolver,\n\t[ResourceType.Dynamo]: dynamoResolver,\n\n\t// Modern format\n\t[ResourceType.SSTSecret]: secretResolver,\n\t[ResourceType.SSTBucket]: bucketResolver,\n\t[ResourceType.SSTFunction]: noopResolver,\n\t[ResourceType.SSTPostgres]: postgresResolver,\n\t[ResourceType.SSTApiGatewayV2]: noopResolver,\n\t[ResourceType.SnsTopic]: topicResolver,\n\t[ResourceType.SSTDynamo]: dynamoResolver,\n};\n\n/**\n * All known SST resource type strings.\n */\ntype SstResourceTypeString = `${ResourceType}`;\n\n/**\n * Extracts the `type` string value from an input value.\n */\ntype ExtractType<T> = T extends { type: infer U extends string } ? U : never;\n\n/**\n * Removes the `type` key from an object type.\n */\ntype OmitType<T> = T extends { type: string } ? Omit<T, 'type'> : never;\n\n/**\n * Extracts all unique `type` values from a record (excluding plain strings).\n */\ntype AllTypeValues<TRecord extends Record<string, InputValue>> = {\n\t[K in keyof TRecord]: ExtractType<TRecord[K]>;\n}[keyof TRecord];\n\n/**\n * Extracts only the custom (non-SST) type values from a record.\n */\ntype CustomTypeValues<TRecord extends Record<string, InputValue>> = Exclude<\n\tAllTypeValues<TRecord>,\n\tSstResourceTypeString\n>;\n\n/**\n * For a given type value, finds the corresponding value type (without `type` key).\n */\ntype ValueForType<\n\tTRecord extends Record<string, InputValue>,\n\tTType extends string,\n> = {\n\t[K in keyof TRecord]: TRecord[K] extends { type: TType }\n\t\t? OmitType<TRecord[K]>\n\t\t: never;\n}[keyof TRecord];\n\n/**\n * Generates typed resolvers for custom (non-SST) types in the input record.\n */\ntype CustomResolvers<TRecord extends Record<string, InputValue>> =\n\tCustomTypeValues<TRecord> extends never\n\t\t? Resolvers | undefined\n\t\t: {\n\t\t\t\t[TType in CustomTypeValues<TRecord>]: EnvironmentResolver<\n\t\t\t\t\tValueForType<TRecord, TType>\n\t\t\t\t>;\n\t\t\t};\n\n/**\n * SST-specific environment builder with built-in resolvers for all known\n * SST resource types.\n *\n * Wraps the generic EnvironmentBuilder with pre-configured SST resolvers.\n *\n * @template TRecord - The input record type for type inference\n *\n * @example\n * ```typescript\n * const env = new SstEnvironmentBuilder({\n * database: { type: 'sst:aws:Postgres', host: '...', ... },\n * apiKey: { type: 'sst:sst:Secret', value: 'secret' },\n * appName: 'my-app',\n * }).build();\n *\n * // With custom resolvers (typed based on input)\n * const env = new SstEnvironmentBuilder(\n * {\n * database: postgresResource,\n * custom: { type: 'my-custom' as const, data: 'foo' },\n * },\n * {\n * // TypeScript requires 'my-custom' resolver with typed value\n * 'my-custom': (key, value) => ({ [`${key}Data`]: value.data }),\n * }\n * ).build();\n * ```\n */\nexport class SstEnvironmentBuilder<\n\tTRecord extends Record<string, SstResource | InputValue | string>,\n> {\n\tprivate readonly builder: EnvironmentBuilder<\n\t\tRecord<string, InputValue>,\n\t\tResolvers\n\t>;\n\n\t/**\n\t * Create a new SST environment builder.\n\t *\n\t * @param record - Object containing SST resources, custom resources, and/or string values\n\t * @param additionalResolvers - Optional custom resolvers (typed based on custom types in record)\n\t * @param options - Optional configuration options\n\t */\n\tconstructor(\n\t\trecord: TRecord,\n\t\tadditionalResolvers?: CustomResolvers<TRecord>,\n\t\toptions?: EnvironmentBuilderOptions,\n\t) {\n\t\t// Merge resolvers with custom ones taking precedence\n\t\tconst mergedResolvers: Resolvers = additionalResolvers\n\t\t\t? { ...sstResolvers, ...additionalResolvers }\n\t\t\t: sstResolvers;\n\n\t\tthis.builder = new EnvironmentBuilder(\n\t\t\trecord as Record<string, InputValue>,\n\t\t\tmergedResolvers,\n\t\t\toptions,\n\t\t);\n\t}\n\n\t/**\n\t * Build environment variables from the input record.\n\t *\n\t * @returns A record of environment variables\n\t */\n\tbuild(): EnvRecord {\n\t\treturn this.builder.build();\n\t}\n}\n\nexport type {\n\tEnvironmentBuilderOptions,\n\tEnvRecord,\n\tEnvValue,\n} from './EnvironmentBuilder';\n// Re-export useful types\nexport { environmentCase } from './EnvironmentBuilder';\n","// Re-export everything from SstEnvironmentBuilder\n\n// Re-export types from EnvironmentBuilder\nexport type {\n\tEnvironmentBuilderOptions,\n\tEnvRecord,\n\tEnvValue,\n} from './EnvironmentBuilder';\n\n// Re-export environmentCase from EnvironmentBuilder\nexport { environmentCase } from './EnvironmentBuilder';\nexport {\n\ttype ApiGatewayV2,\n\ttype Bucket,\n\ttype Dynamo,\n\ttype Function,\n\ttype Postgres,\n\ttype ResourceProcessor,\n\tResourceType,\n\ttype Secret,\n\ttype SnsTopic,\n\tSstEnvironmentBuilder,\n\ttype SstResource,\n\tsstResolvers,\n\ttype Vpc,\n} from './SstEnvironmentBuilder';\n\n// Import for deprecated function\nimport {\n\tSstEnvironmentBuilder,\n\ttype SstResource,\n} from './SstEnvironmentBuilder';\n\n/**\n * @deprecated Use `new SstEnvironmentBuilder(record).build()` instead.\n *\n * Normalizes SST resources and plain strings into environment variables.\n * Processes resources based on their type and converts names to environment case.\n *\n * @param record - Object containing resources and/or string values\n * @returns Normalized environment variables object\n *\n * @example\n * // Old usage (deprecated):\n * normalizeResourceEnv({ database: postgresResource })\n *\n * // New usage:\n * new SstEnvironmentBuilder({ database: postgresResource }).build()\n */\nexport function normalizeResourceEnv(\n\trecord: Record<string, SstResource | string>,\n): Record<string, string | number | boolean | Record<string, unknown>> {\n\treturn new SstEnvironmentBuilder(record).build();\n}\n\n// Keep Resource type as deprecated alias for backwards compatibility\n/**\n * @deprecated Use `SstResource` instead.\n */\nexport type Resource = SstResource;\n"],"mappings":";;;;;;;AAaA,IAAY,wDAAL;AAEN;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;;AACA;AAwGD,MAAM,iBAAiB,CAACA,MAAcC,WAAwB,GAC5D,OAAO,MAAM,MACd;AAED,MAAM,mBAAmB,CAACC,KAAaC,WAA0B;GAC9D,EAAE,IAAI,QAAQ,MAAM;GACpB,EAAE,IAAI,QAAQ,MAAM;GACpB,EAAE,IAAI,YAAY,MAAM;GACxB,EAAE,IAAI,QAAQ,MAAM;GACpB,EAAE,IAAI,YAAY,MAAM;GACxB,EAAE,IAAI,QAAQ,eAAe,mBAAmB,MAAM,SAAS,CAAC,GAAG,mBAAmB,MAAM,SAAS,CAAC,GAAG,MAAM,KAAK,GAAG,MAAM,KAAK,GAAG,MAAM,SAAS;AACtJ;AAED,MAAM,iBAAiB,CAACH,MAAcI,WAAwB,IAC3D,EAAE,KAAK,QAAQ,MAAM,KACvB;AAED,MAAM,gBAAgB,CAACJ,MAAcK,WAA0B,IAC5D,EAAE,KAAK,OAAO,MAAM,IACtB;AAED,MAAM,iBAAiB,CAACL,MAAcM,WAAwB,IAC3D,EAAE,KAAK,QAAQ,MAAM,KACvB;AAED,MAAM,eAAe,OAAO,CAAE;;;;AAK9B,MAAaC,eAA0B;EAErC,aAAa,eAAe;EAC5B,aAAa,WAAW;EACxB,aAAa,MAAM;EACnB,aAAa,SAAS;EACtB,aAAa,WAAW;EACxB,aAAa,SAAS;EACtB,aAAa,SAAS;EAGtB,aAAa,YAAY;EACzB,aAAa,YAAY;EACzB,aAAa,cAAc;EAC3B,aAAa,cAAc;EAC3B,aAAa,kBAAkB;EAC/B,aAAa,WAAW;EACxB,aAAa,YAAY;AAC1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqFD,IAAa,wBAAb,MAEE;CACD,AAAiB;;;;;;;;CAYjB,YACCC,QACAC,qBACAC,SACC;EAED,MAAMC,kBAA6B,sBAChC;GAAE,GAAG;GAAc,GAAG;EAAqB,IAC3C;AAEH,OAAK,UAAU,IAAIC,8CAClB,QACA,iBACA;CAED;;;;;;CAOD,QAAmB;AAClB,SAAO,KAAK,QAAQ,OAAO;CAC3B;AACD;;;;;;;;;;;;;;;;;;;;ACnQD,SAAgB,qBACfC,QACsE;AACtE,QAAO,IAAI,sBAAsB,QAAQ,OAAO;AAChD"}
|
|
1
|
+
{"version":3,"file":"sst.cjs","names":["name: string","value: SecretValue","key: string","value: PostgresValue","value: BucketValue","value: SnsTopicValue","value: QueueValue","value: DynamoValue","sstResolvers: Resolvers","record: TRecord","additionalResolvers?: CustomResolvers<TRecord>","options?: EnvironmentBuilderOptions","mergedResolvers: Resolvers","EnvironmentBuilder","platform: Platform","a: string","b: string","curr: number[]","missing: string","available: readonly string[]","args: {\n\t\tmissing: string[];\n\t\tavailable: string[];\n\t\tlinkVars: string[];\n\t\tsuggestions: Record<string, string[]>;\n\t\tcontext?: string;\n\t}","args: {\n\t\tmissing: string[];\n\t\tlinkVars: string[];\n\t\tsuggestions: Record<string, string[]>;\n\t\tcontext?: string;\n\t}","provided: string","record: LinkRecord","typeOnly: Record<string, InputValue>","EnvironmentBuilder","links: LinkRecord","options: EnvValidatorOptions","requestedEnvVars: readonly string[]","providers: string[]","suggestions: Record<string, string[]>","envVar: string","record: Record<string, SstResource | string>"],"sources":["../src/SstEnvironmentBuilder.ts","../src/SstEnvValidator.ts","../src/sst.ts"],"sourcesContent":["import {\n\tEnvironmentBuilder,\n\ttype EnvironmentBuilderOptions,\n\ttype EnvironmentResolver,\n\ttype EnvRecord,\n\ttype InputValue,\n\ttype Resolvers,\n} from './EnvironmentBuilder';\n\n/**\n * Enumeration of supported SST (Serverless Stack Toolkit) resource types.\n * Used to identify and process different AWS and SST resources.\n */\nexport enum ResourceType {\n\t// Legacy format (dot notation)\n\tApiGatewayV2 = 'sst.aws.ApiGatewayV2',\n\tPostgres = 'sst.aws.Postgres',\n\tFunction = 'sst.aws.Function',\n\tBucket = 'sst.aws.Bucket',\n\tVpc = 'sst.aws.Vpc',\n\tSecret = 'sst.sst.Secret',\n\tDynamo = 'sst.aws.Dynamo',\n\tQueue = 'sst.aws.Queue',\n\n\t// Modern format (colon notation)\n\tSSTSecret = 'sst:sst:Secret',\n\tSSTFunction = 'sst:sst:Function',\n\tSSTApiGatewayV2 = 'sst:aws:ApiGatewayV2',\n\tSSTPostgres = 'sst:aws:Postgres',\n\tSSTBucket = 'sst:aws:Bucket',\n\tSnsTopic = 'sst:aws:SnsTopic',\n\tSSTDynamo = 'sst:aws:Dynamo',\n\tSSTQueue = 'sst:aws:Queue',\n}\n\n/**\n * AWS API Gateway V2 resource type.\n * Represents an HTTP/WebSocket API.\n */\nexport type ApiGatewayV2 = {\n\ttype: ResourceType.ApiGatewayV2 | ResourceType.SSTApiGatewayV2;\n\turl: string;\n};\n\n/**\n * PostgreSQL database resource type.\n * Contains all connection details needed to connect to the database.\n */\nexport type Postgres = {\n\ttype: ResourceType.Postgres | ResourceType.SSTPostgres;\n\tdatabase: string;\n\thost: string;\n\tpassword: string;\n\tport: number;\n\tusername: string;\n};\n\n/**\n * AWS Lambda Function resource type.\n */\nexport type Function = {\n\ttype: ResourceType.Function | ResourceType.SSTFunction;\n\tname: string;\n};\n\n/**\n * AWS S3 Bucket resource type.\n */\nexport type Bucket = {\n\ttype: ResourceType.Bucket | ResourceType.SSTBucket;\n\tname: string;\n};\n\n/**\n * AWS VPC (Virtual Private Cloud) resource type.\n */\nexport type Vpc = {\n\ttype: ResourceType.Vpc;\n\tbastion: string;\n};\n\n/**\n * Secret resource type for storing sensitive values.\n */\nexport type Secret = {\n\ttype: ResourceType.Secret | ResourceType.SSTSecret;\n\tvalue: string;\n};\n\n/**\n * AWS SNS Topic resource type.\n */\nexport type SnsTopic = {\n\ttype: ResourceType.SnsTopic;\n\tarn: string;\n};\n\n/**\n * AWS SQS Queue resource type.\n */\nexport type Queue = {\n\ttype: ResourceType.Queue | ResourceType.SSTQueue;\n\turl: string;\n\tarn: string;\n};\n\n/**\n * AWS DynamoDB Table resource type.\n */\nexport type Dynamo = {\n\ttype: ResourceType.Dynamo | ResourceType.SSTDynamo;\n\tname: string;\n};\n\n/**\n * Union type of all supported SST resource types.\n */\nexport type SstResource =\n\t| ApiGatewayV2\n\t| Postgres\n\t| Function\n\t| Bucket\n\t| Vpc\n\t| Secret\n\t| SnsTopic\n\t| Queue\n\t| Dynamo;\n\n// Value types without the `type` key (for resolver parameters)\ntype SecretValue = Omit<Secret, 'type'>;\ntype PostgresValue = Omit<Postgres, 'type'>;\ntype BucketValue = Omit<Bucket, 'type'>;\ntype SnsTopicValue = Omit<SnsTopic, 'type'>;\ntype QueueValue = Omit<Queue, 'type'>;\ntype DynamoValue = Omit<Dynamo, 'type'>;\n\n/**\n * Function type for processing a specific resource type into environment variables.\n *\n * @template K - The specific resource type (without `type` key)\n * @param name - The resource name\n * @param value - The resource value (without `type` key)\n * @returns Object mapping environment variable names to values\n */\nexport type ResourceProcessor<K> = (name: string, value: K) => EnvRecord;\n\n// SST Resource Resolvers (receive values without `type` key)\n\nconst secretResolver = (name: string, value: SecretValue) => ({\n\t[name]: value.value,\n});\n\nconst postgresResolver = (key: string, value: PostgresValue) => ({\n\t[`${key}Name`]: value.database,\n\t[`${key}Host`]: value.host,\n\t[`${key}Password`]: value.password,\n\t[`${key}Port`]: value.port,\n\t[`${key}Username`]: value.username,\n\t[`${key}Url`]: `postgresql://${encodeURIComponent(value.username)}:${encodeURIComponent(value.password)}@${value.host}:${value.port}/${value.database}`,\n});\n\nconst bucketResolver = (name: string, value: BucketValue) => ({\n\t[`${name}Name`]: value.name,\n});\n\nconst topicResolver = (name: string, value: SnsTopicValue) => ({\n\t[`${name}Arn`]: value.arn,\n\t// A publisher connection string for `@geekmidas/events` (SNS); region is\n\t// resolved from the ARN / AWS_REGION at runtime.\n\t[`${name}PublisherConnectionString`]: `sns://?topicArn=${encodeURIComponent(value.arn)}`,\n});\n\nconst queueResolver = (name: string, value: QueueValue) => ({\n\t[`${name}Url`]: value.url,\n\t[`${name}Arn`]: value.arn,\n\t// A publisher connection string for `@geekmidas/events` (SQS); region is\n\t// resolved from the URL / AWS_REGION at runtime.\n\t[`${name}PublisherConnectionString`]: `sqs://?queueUrl=${encodeURIComponent(value.url)}`,\n});\n\nconst dynamoResolver = (name: string, value: DynamoValue) => ({\n\t[`${name}Name`]: value.name,\n});\n\nconst noopResolver = () => ({});\n\n/**\n * Pre-configured resolvers for all SST resource types.\n */\nexport const sstResolvers: Resolvers = {\n\t// Legacy format\n\t[ResourceType.ApiGatewayV2]: noopResolver,\n\t[ResourceType.Function]: noopResolver,\n\t[ResourceType.Vpc]: noopResolver,\n\t[ResourceType.Secret]: secretResolver,\n\t[ResourceType.Postgres]: postgresResolver,\n\t[ResourceType.Bucket]: bucketResolver,\n\t[ResourceType.Dynamo]: dynamoResolver,\n\t[ResourceType.Queue]: queueResolver,\n\n\t// Modern format\n\t[ResourceType.SSTSecret]: secretResolver,\n\t[ResourceType.SSTBucket]: bucketResolver,\n\t[ResourceType.SSTFunction]: noopResolver,\n\t[ResourceType.SSTPostgres]: postgresResolver,\n\t[ResourceType.SSTApiGatewayV2]: noopResolver,\n\t[ResourceType.SnsTopic]: topicResolver,\n\t[ResourceType.SSTDynamo]: dynamoResolver,\n\t[ResourceType.SSTQueue]: queueResolver,\n};\n\n/**\n * All known SST resource type strings.\n */\ntype SstResourceTypeString = `${ResourceType}`;\n\n/**\n * Extracts the `type` string value from an input value.\n */\ntype ExtractType<T> = T extends { type: infer U extends string } ? U : never;\n\n/**\n * Removes the `type` key from an object type.\n */\ntype OmitType<T> = T extends { type: string } ? Omit<T, 'type'> : never;\n\n/**\n * Extracts all unique `type` values from a record (excluding plain strings).\n */\ntype AllTypeValues<TRecord extends Record<string, InputValue>> = {\n\t[K in keyof TRecord]: ExtractType<TRecord[K]>;\n}[keyof TRecord];\n\n/**\n * Extracts only the custom (non-SST) type values from a record.\n */\ntype CustomTypeValues<TRecord extends Record<string, InputValue>> = Exclude<\n\tAllTypeValues<TRecord>,\n\tSstResourceTypeString\n>;\n\n/**\n * For a given type value, finds the corresponding value type (without `type` key).\n */\ntype ValueForType<\n\tTRecord extends Record<string, InputValue>,\n\tTType extends string,\n> = {\n\t[K in keyof TRecord]: TRecord[K] extends { type: TType }\n\t\t? OmitType<TRecord[K]>\n\t\t: never;\n}[keyof TRecord];\n\n/**\n * Generates typed resolvers for custom (non-SST) types in the input record.\n */\ntype CustomResolvers<TRecord extends Record<string, InputValue>> =\n\tCustomTypeValues<TRecord> extends never\n\t\t? Resolvers | undefined\n\t\t: {\n\t\t\t\t[TType in CustomTypeValues<TRecord>]: EnvironmentResolver<\n\t\t\t\t\tValueForType<TRecord, TType>\n\t\t\t\t>;\n\t\t\t};\n\n/**\n * SST-specific environment builder with built-in resolvers for all known\n * SST resource types.\n *\n * Wraps the generic EnvironmentBuilder with pre-configured SST resolvers.\n *\n * @template TRecord - The input record type for type inference\n *\n * @example\n * ```typescript\n * const env = new SstEnvironmentBuilder({\n * database: { type: 'sst:aws:Postgres', host: '...', ... },\n * apiKey: { type: 'sst:sst:Secret', value: 'secret' },\n * appName: 'my-app',\n * }).build();\n *\n * // With custom resolvers (typed based on input)\n * const env = new SstEnvironmentBuilder(\n * {\n * database: postgresResource,\n * custom: { type: 'my-custom' as const, data: 'foo' },\n * },\n * {\n * // TypeScript requires 'my-custom' resolver with typed value\n * 'my-custom': (key, value) => ({ [`${key}Data`]: value.data }),\n * }\n * ).build();\n * ```\n */\nexport class SstEnvironmentBuilder<\n\tTRecord extends Record<string, SstResource | InputValue | string>,\n> {\n\tprivate readonly builder: EnvironmentBuilder<\n\t\tRecord<string, InputValue>,\n\t\tResolvers\n\t>;\n\n\t/**\n\t * Create a new SST environment builder.\n\t *\n\t * @param record - Object containing SST resources, custom resources, and/or string values\n\t * @param additionalResolvers - Optional custom resolvers (typed based on custom types in record)\n\t * @param options - Optional configuration options\n\t */\n\tconstructor(\n\t\trecord: TRecord,\n\t\tadditionalResolvers?: CustomResolvers<TRecord>,\n\t\toptions?: EnvironmentBuilderOptions,\n\t) {\n\t\t// Merge resolvers with custom ones taking precedence\n\t\tconst mergedResolvers: Resolvers = additionalResolvers\n\t\t\t? { ...sstResolvers, ...additionalResolvers }\n\t\t\t: sstResolvers;\n\n\t\tthis.builder = new EnvironmentBuilder(\n\t\t\trecord as Record<string, InputValue>,\n\t\t\tmergedResolvers,\n\t\t\toptions,\n\t\t);\n\t}\n\n\t/**\n\t * Build environment variables from the input record.\n\t *\n\t * @returns A record of environment variables\n\t */\n\tbuild(): EnvRecord {\n\t\treturn this.builder.build();\n\t}\n}\n\nexport type {\n\tEnvironmentBuilderOptions,\n\tEnvRecord,\n\tEnvValue,\n} from './EnvironmentBuilder';\n// Re-export useful types\nexport { environmentCase } from './EnvironmentBuilder';\n","import { EnvironmentBuilder, type InputValue } from './EnvironmentBuilder';\nimport { type SstResource, sstResolvers } from './SstEnvironmentBuilder';\n\n/**\n * Variables AWS Lambda injects into the runtime. Always valid on AWS, but never\n * assumed — opt in via `platform: 'aws'` or by spreading {@link AWS_RUNTIME_ENV_VARS}\n * into `whitelist`.\n */\nexport const AWS_RUNTIME_ENV_VARS = [\n\t'AWS_REGION',\n\t'AWS_DEFAULT_REGION',\n\t'AWS_ENDPOINT_URL',\n\t'AWS_ENDPOINT',\n\t'AWS_ACCESS_KEY_ID',\n\t'AWS_SECRET_ACCESS_KEY',\n\t'AWS_SESSION_TOKEN',\n\t'AWS_LAMBDA_FUNCTION_NAME',\n\t'AWS_LAMBDA_FUNCTION_VERSION',\n] as const;\n\n/**\n * Variables GCP injects into Cloud Functions / Cloud Run.\n */\nexport const GCP_RUNTIME_ENV_VARS = [\n\t'GOOGLE_CLOUD_PROJECT',\n\t'GCP_PROJECT',\n\t'FUNCTION_TARGET',\n\t'FUNCTION_SIGNATURE_TYPE',\n\t'K_SERVICE',\n\t'K_REVISION',\n\t'K_CONFIGURATION',\n\t'PORT',\n] as const;\n\n/**\n * Variables Cloudflare exposes to Workers. Workers inject almost nothing into\n * the global env, so this is intentionally minimal.\n */\nexport const CLOUDFLARE_RUNTIME_ENV_VARS = [\n\t'CF_PAGES',\n\t'CF_PAGES_URL',\n] as const;\n\n/**\n * Registry of platform → always-valid runtime variables. Export, don't assume:\n * a caller states the platform it deploys to and the matching set is trusted.\n */\nexport const PLATFORM_ENV_VARS = {\n\taws: AWS_RUNTIME_ENV_VARS,\n\tgcp: GCP_RUNTIME_ENV_VARS,\n\tcloudflare: CLOUDFLARE_RUNTIME_ENV_VARS,\n} as const;\n\nexport type Platform = keyof typeof PLATFORM_ENV_VARS;\n\n/**\n * The always-valid runtime variables for a platform. The \"whitelist construct\":\n * resolve a platform to its set, to spread into `whitelist` or compare against.\n */\nexport function platformEnvVars(platform: Platform): readonly string[] {\n\treturn PLATFORM_ENV_VARS[platform];\n}\n\n/** How many link-provided vars to list in an error before truncating. */\nconst MAX_LISTED_VARS = 8;\n\n/** Minimum similarity for a variable to be offered as a \"did you mean\". */\nconst SUGGESTION_THRESHOLD = 0.3;\n\n/**\n * Input shape for the validator: the same record `SstEnvironmentBuilder`\n * accepts — keyed by resource name, valued by an SST resource (or a plain\n * string for pass-through env vars).\n */\nexport type LinkRecord = Record<string, SstResource | InputValue | string>;\n\nexport interface ValidationResult {\n\tvalid: boolean;\n\t/** Requested variables that are not available (excluding optional `?` vars). */\n\tinvalidVars: string[];\n\t/** Every variable the links + whitelist make available. */\n\tavailableVars: string[];\n\t/** Nearest available matches for each invalid variable (best first). */\n\tsuggestions: Record<string, string[]>;\n}\n\nexport interface EnvValidatorOptions {\n\t/**\n\t * Platform whose runtime-injected variables are always valid. Omit to assume\n\t * none — the validator never bakes in a platform's variables.\n\t */\n\tplatform?: Platform;\n\t/**\n\t * Extra always-valid variables in addition to the platform set — typically\n\t * the keys of a function's explicit `environment`.\n\t */\n\twhitelist?: readonly string[];\n\t/**\n\t * Label for the deployable unit (e.g. a function name), used in error\n\t * messages so a failure points at exactly one unit.\n\t */\n\tcontext?: string;\n}\n\n/**\n * Levenshtein edit distance between two strings.\n */\nfunction levenshtein(a: string, b: string): number {\n\tconst m = a.length;\n\tconst n = b.length;\n\tif (m === 0) return n;\n\tif (n === 0) return m;\n\n\tlet prev = Array.from({ length: n + 1 }, (_, i) => i);\n\n\tfor (let i = 1; i <= m; i++) {\n\t\tconst curr: number[] = [i];\n\t\tfor (let j = 1; j <= n; j++) {\n\t\t\tconst cost = a[i - 1] === b[j - 1] ? 0 : 1;\n\t\t\tconst del = (prev[j] ?? 0) + 1;\n\t\t\tconst ins = (curr[j - 1] ?? 0) + 1;\n\t\t\tconst sub = (prev[j - 1] ?? 0) + cost;\n\t\t\tcurr[j] = Math.min(del, ins, sub);\n\t\t}\n\t\tprev = curr;\n\t}\n\n\treturn prev[n] ?? 0;\n}\n\n/**\n * Similarity in `[0, 1]` combining edit distance with `_`-token overlap, so both\n * near-typos (a missing underscore) and renames (`DATABASE_URL` ~ `DB_URL`,\n * which share the `URL` token) are caught.\n */\nfunction similarity(a: string, b: string): number {\n\tconst editSim = 1 - levenshtein(a, b) / Math.max(a.length, b.length, 1);\n\n\tconst ta = new Set(a.split('_').filter(Boolean));\n\tconst tb = new Set(b.split('_').filter(Boolean));\n\tconst shared = [...ta].filter((t) => tb.has(t)).length;\n\tconst union = new Set([...ta, ...tb]).size || 1;\n\tconst tokenSim = shared / union;\n\n\t// Nudge token-overlap matches ahead of coincidental edit-distance ones, so a\n\t// rename like DATABASE_URL prefers DB_URL (shares `URL`) over DB_PASSWORD.\n\treturn Math.max(editSim, tokenSim * 1.15);\n}\n\n/**\n * Returns the nearest available variables to `missing`, best first, limited to\n * those above {@link SUGGESTION_THRESHOLD}.\n */\nfunction suggestionsFor(\n\tmissing: string,\n\tavailable: readonly string[],\n\tmax = 1,\n): string[] {\n\treturn available\n\t\t.map((candidate) => ({ candidate, score: similarity(missing, candidate) }))\n\t\t.filter((s) => s.score >= SUGGESTION_THRESHOLD)\n\t\t.sort((a, b) => b.score - a.score)\n\t\t.slice(0, max)\n\t\t.map((s) => s.candidate);\n}\n\n/**\n * Thrown when a deployable unit requires environment variables its links do not\n * provide. Carries the structured `missing`/`available`/`suggestions` so callers\n * can inspect the failure programmatically, not just read the message.\n */\nexport class EnvValidationError extends Error {\n\treadonly isEnvValidationError = true;\n\treadonly missing: string[];\n\treadonly available: string[];\n\treadonly suggestions: Record<string, string[]>;\n\treadonly context?: string;\n\n\tconstructor(args: {\n\t\tmissing: string[];\n\t\tavailable: string[];\n\t\tlinkVars: string[];\n\t\tsuggestions: Record<string, string[]>;\n\t\tcontext?: string;\n\t}) {\n\t\tsuper(EnvValidationError.format(args));\n\t\tthis.name = 'EnvValidationError';\n\t\tthis.missing = args.missing;\n\t\tthis.available = args.available;\n\t\tthis.suggestions = args.suggestions;\n\t\tthis.context = args.context;\n\t\t// V8-only; keeps the constructor out of the stack trace.\n\t\t(\n\t\t\tError as unknown as {\n\t\t\t\tcaptureStackTrace?(target: object, ctor?: unknown): void;\n\t\t\t}\n\t\t).captureStackTrace?.(this, EnvValidationError);\n\t}\n\n\tprivate static format(args: {\n\t\tmissing: string[];\n\t\tlinkVars: string[];\n\t\tsuggestions: Record<string, string[]>;\n\t\tcontext?: string;\n\t}): string {\n\t\tconst { missing, linkVars, suggestions, context } = args;\n\t\tconst who = context ? `'${context}'` : 'a deployable unit';\n\n\t\tconst width = Math.max(...missing.map((v) => v.length));\n\t\tconst lines = missing.map((v) => {\n\t\t\tconst hints = suggestions[v] ?? [];\n\t\t\tconst hint = hints.length ? `(did you mean ${hints.join(' or ')}?)` : '';\n\t\t\treturn ` - ${v.padEnd(width)} ${hint}`.trimEnd();\n\t\t});\n\n\t\tlet provided: string;\n\t\tif (linkVars.length === 0) {\n\t\t\tprovided = '(no links provide environment variables)';\n\t\t} else {\n\t\t\tconst shown = linkVars.slice(0, MAX_LISTED_VARS).join(', ');\n\t\t\tconst extra =\n\t\t\t\tlinkVars.length > MAX_LISTED_VARS\n\t\t\t\t\t? ` ...(+${linkVars.length - MAX_LISTED_VARS})`\n\t\t\t\t\t: '';\n\t\t\tprovided = `Provided by links: ${shown}${extra}`;\n\t\t}\n\n\t\treturn `${who} is missing required env vars:\\n${lines.join('\\n')}\\n\\n${provided}`;\n\t}\n}\n\n/**\n * Derives the set of environment-variable **keys** a group of linked resources\n * will produce at runtime — without resolving any value.\n *\n * This reuses the runtime resolvers (`sstResolvers`) via {@link EnvironmentBuilder},\n * so the keys computed here are exactly the keys a deployed function receives:\n * the validator and the runtime resolution share a single source of truth and\n * cannot drift. Because every resolver's output keys depend only on the record\n * key and the resource `type` (never the value), each resource is reduced to\n * `{ type }` before building, so no SST `Output` is ever read.\n *\n * @example\n * resolveEnvKeys({ db: { type: ResourceType.Postgres } });\n * // ['DB_NAME', 'DB_HOST', 'DB_PASSWORD', 'DB_PORT', 'DB_USERNAME', 'DB_URL']\n */\nexport function resolveEnvKeys(record: LinkRecord): string[] {\n\tconst typeOnly: Record<string, InputValue> = {};\n\tfor (const [key, value] of Object.entries(record)) {\n\t\ttypeOnly[key] = typeof value === 'string' ? '' : { type: value.type };\n\t}\n\treturn Object.keys(new EnvironmentBuilder(typeOnly, sstResolvers).build());\n}\n\n/**\n * Validates that the environment variables a deployable unit requires are\n * actually provided by its linked resources — at infra time, before deploy.\n *\n * Available variables are the union of the keys derived from `links`\n * ({@link resolveEnvKeys}), the chosen platform's runtime variables (only when\n * `options.platform` is set — nothing is assumed), and any `options.whitelist`\n * (e.g. the keys of a function's explicit `environment`). A requested variable\n * suffixed with `?` is treated as optional and never causes a failure.\n *\n * @example\n * const validator = new EnvValidator(\n * { db: { type: ResourceType.Postgres } },\n * { platform: 'aws', whitelist: ['APP_NAME'], context: 'orders-fn' },\n * );\n * validator.assert(['DB_HOST', 'AWS_REGION', 'APP_NAME', 'SENTRY_DSN?']); // ok\n * validator.assert(['DATABASE_URL']); // throws EnvValidationError (did you mean DB_URL?)\n */\nexport class EnvValidator {\n\t/** Every variable available — link-derived, platform, and extra whitelist. */\n\treadonly availableVars: string[];\n\t/** Just the variables the links provide (used for error context). */\n\treadonly linkVars: string[];\n\treadonly context?: string;\n\tprivate readonly availableSet: Set<string>;\n\t/** Per-link (record key → its env-var keys), for least-privilege filtering. */\n\tprivate readonly linkVarsByName: Map<string, string[]>;\n\n\tconstructor(links: LinkRecord, options: EnvValidatorOptions = {}) {\n\t\tconst { platform, whitelist = [], context } = options;\n\t\tthis.context = context;\n\t\tthis.linkVarsByName = new Map(\n\t\t\tObject.entries(links).map(([name, value]) => [\n\t\t\t\tname,\n\t\t\t\tresolveEnvKeys({ [name]: value }),\n\t\t\t]),\n\t\t);\n\t\tthis.linkVars = [...this.linkVarsByName.values()].flat();\n\t\tthis.availableVars = [\n\t\t\t...this.linkVars,\n\t\t\t...(platform ? PLATFORM_ENV_VARS[platform] : []),\n\t\t\t...whitelist,\n\t\t];\n\t\tthis.availableSet = new Set(this.availableVars);\n\t}\n\n\t/**\n\t * Returns the names of the links that provide at least one of the requested\n\t * variables — so a construct can attach only the links a unit actually needs\n\t * (least privilege) rather than the whole pool. Trailing `?` is ignored.\n\t */\n\tgetProvidersForEnvVars(requestedEnvVars: readonly string[]): string[] {\n\t\tconst requested = new Set(\n\t\t\trequestedEnvVars.map((v) => (v.endsWith('?') ? v.slice(0, -1) : v)),\n\t\t);\n\t\tconst providers: string[] = [];\n\t\tfor (const [name, vars] of this.linkVarsByName) {\n\t\t\tif (vars.some((v) => requested.has(v))) providers.push(name);\n\t\t}\n\t\treturn providers;\n\t}\n\n\t/**\n\t * Checks every requested variable against the available set. Variables\n\t * ending in `?` are optional and never reported as invalid. Invalid\n\t * variables come back with their nearest available matches.\n\t */\n\tvalidate(requestedEnvVars: readonly string[]): ValidationResult {\n\t\tconst invalidVars = requestedEnvVars.filter((envVar) => {\n\t\t\tif (envVar.endsWith('?')) return false;\n\t\t\treturn !this.availableSet.has(envVar);\n\t\t});\n\n\t\tconst suggestions: Record<string, string[]> = {};\n\t\tfor (const envVar of invalidVars) {\n\t\t\tconst matches = suggestionsFor(envVar, this.availableVars);\n\t\t\tif (matches.length) suggestions[envVar] = matches;\n\t\t}\n\n\t\treturn {\n\t\t\tvalid: invalidVars.length === 0,\n\t\t\tinvalidVars,\n\t\t\tavailableVars: this.availableVars,\n\t\t\tsuggestions,\n\t\t};\n\t}\n\n\t/**\n\t * Asserts that every requested variable is available, throwing an\n\t * {@link EnvValidationError} otherwise. Intended to run in a construct's\n\t * constructor so a misconfigured `sst.config.ts` fails at synth time, before\n\t * any AWS call.\n\t */\n\tassert(requestedEnvVars: readonly string[]): void {\n\t\tconst { valid, invalidVars, suggestions } = this.validate(requestedEnvVars);\n\n\t\tif (!valid) {\n\t\t\tthrow new EnvValidationError({\n\t\t\t\tmissing: invalidVars,\n\t\t\t\tavailable: this.availableVars,\n\t\t\t\tlinkVars: this.linkVars,\n\t\t\t\tsuggestions,\n\t\t\t\tcontext: this.context,\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Whether a single variable is available. Does not treat a trailing `?` as\n\t * optional — pass the bare name.\n\t */\n\thas(envVar: string): boolean {\n\t\treturn this.availableSet.has(envVar);\n\t}\n}\n","// Re-export everything from SstEnvironmentBuilder\n\n// Re-export types from EnvironmentBuilder\nexport type {\n\tEnvironmentBuilderOptions,\n\tEnvRecord,\n\tEnvValue,\n} from './EnvironmentBuilder';\n\n// Re-export environmentCase from EnvironmentBuilder\nexport { environmentCase } from './EnvironmentBuilder';\nexport {\n\ttype ApiGatewayV2,\n\ttype Bucket,\n\ttype Dynamo,\n\ttype Function,\n\ttype Postgres,\n\ttype Queue,\n\ttype ResourceProcessor,\n\tResourceType,\n\ttype Secret,\n\ttype SnsTopic,\n\tSstEnvironmentBuilder,\n\ttype SstResource,\n\tsstResolvers,\n\ttype Vpc,\n} from './SstEnvironmentBuilder';\n\nexport {\n\tAWS_RUNTIME_ENV_VARS,\n\tCLOUDFLARE_RUNTIME_ENV_VARS,\n\tEnvValidationError,\n\tEnvValidator,\n\ttype EnvValidatorOptions,\n\tGCP_RUNTIME_ENV_VARS,\n\ttype LinkRecord,\n\tPLATFORM_ENV_VARS,\n\ttype Platform,\n\tplatformEnvVars,\n\tresolveEnvKeys,\n\ttype ValidationResult,\n} from './SstEnvValidator';\n\n// Import for deprecated function\nimport {\n\tSstEnvironmentBuilder,\n\ttype SstResource,\n} from './SstEnvironmentBuilder';\n\n/**\n * @deprecated Use `new SstEnvironmentBuilder(record).build()` instead.\n *\n * Normalizes SST resources and plain strings into environment variables.\n * Processes resources based on their type and converts names to environment case.\n *\n * @param record - Object containing resources and/or string values\n * @returns Normalized environment variables object\n *\n * @example\n * // Old usage (deprecated):\n * normalizeResourceEnv({ database: postgresResource })\n *\n * // New usage:\n * new SstEnvironmentBuilder({ database: postgresResource }).build()\n */\nexport function normalizeResourceEnv(\n\trecord: Record<string, SstResource | string>,\n): Record<string, string | number | boolean | Record<string, unknown>> {\n\treturn new SstEnvironmentBuilder(record).build();\n}\n\n// Keep Resource type as deprecated alias for backwards compatibility\n/**\n * @deprecated Use `SstResource` instead.\n */\nexport type Resource = SstResource;\n"],"mappings":"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"}
|