@geekmidas/cloud 10.0.0-alpha.5 → 10.0.0-alpha.50
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 +14 -6
- package/dist/index.cjs +3 -3
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +2 -3
- package/dist/utils/index.cjs +106 -5
- package/dist/utils/index.cjs.map +1 -0
- package/dist/utils/index.d.cts +62 -2
- package/dist/utils/index.d.cts.map +1 -0
- package/dist/utils/index.d.mts +62 -2
- package/dist/utils/index.d.mts.map +1 -0
- package/dist/utils/index.mjs +78 -2
- package/dist/utils/index.mjs.map +1 -0
- package/package.json +7 -7
- package/src/dokploy/Application.ts +52 -5
- package/src/dokploy/__tests__/Application.provider.spec.ts +206 -0
- package/src/sst/__tests__/api.spec.ts +185 -0
- package/src/sst/__tests__/encryption.spec.ts +90 -0
- package/src/sst/__tests__/fromManifest.integration.spec.ts +427 -0
- package/src/sst/__tests__/fromManifest.spec.ts +306 -5
- package/src/sst/__tests__/function.spec.ts +121 -0
- package/src/sst/__tests__/links.spec.ts +209 -0
- package/src/sst/__tests__/surfaces.spec.ts +17 -0
- package/src/sst/aws/Api.ts +16 -44
- package/src/sst/aws/Cache.ts +4 -4
- package/src/sst/aws/Credential.ts +42 -9
- package/src/sst/aws/Cron.ts +5 -45
- package/src/sst/aws/Database.ts +15 -1
- package/src/sst/aws/DatabaseBootstrap.ts +3 -0
- package/src/sst/aws/Encryption.ts +91 -0
- package/src/sst/aws/ExternalApi.ts +58 -0
- package/src/sst/aws/Function.ts +9 -38
- package/src/sst/aws/Queue.ts +34 -1
- package/src/sst/aws/RestApiSurface.ts +36 -14
- package/src/sst/aws/bootstrap/handler.ts +10 -1
- package/src/sst/fromManifest.ts +229 -20
- package/src/sst/index.ts +1 -0
- package/dist/index-B5CZ1xVf.d.cts +0 -64
- package/dist/index-B5CZ1xVf.d.cts.map +0 -1
- package/dist/index-DhHRjduZ.d.mts +0 -64
- package/dist/index-DhHRjduZ.d.mts.map +0 -1
- package/dist/utils-B1a2UuEO.cjs +0 -129
- package/dist/utils-B1a2UuEO.cjs.map +0 -1
- package/dist/utils-DMOXJ27j.mjs +0 -83
- package/dist/utils-DMOXJ27j.mjs.map +0 -1
- package/src/sst/__type-tests__/manifest.type-test.ts +0 -77
|
@@ -28,6 +28,11 @@ export interface BootstrapTenant {
|
|
|
28
28
|
runtime: string;
|
|
29
29
|
owner: string;
|
|
30
30
|
reader?: string;
|
|
31
|
+
/**
|
|
32
|
+
* The database's own roles rather than a tenant's: its owner may create in
|
|
33
|
+
* the database, for trusted extensions.
|
|
34
|
+
*/
|
|
35
|
+
ownsDatabase?: boolean;
|
|
31
36
|
passwords: { runtime: string; owner: string; reader?: string };
|
|
32
37
|
}
|
|
33
38
|
|
|
@@ -75,7 +80,11 @@ export async function handler(event: BootstrapEvent): Promise<BootstrapResult> {
|
|
|
75
80
|
|
|
76
81
|
try {
|
|
77
82
|
for (const tenant of event.tenants) {
|
|
78
|
-
|
|
83
|
+
const spec = {
|
|
84
|
+
...tenant,
|
|
85
|
+
...(tenant.ownsDatabase ? { database: event.master.database } : {}),
|
|
86
|
+
};
|
|
87
|
+
for (const statement of roleStatements(spec)) {
|
|
79
88
|
if (statement.exists) {
|
|
80
89
|
const { rows } = await client.query(
|
|
81
90
|
statement.exists.sql,
|
package/src/sst/fromManifest.ts
CHANGED
|
@@ -26,10 +26,12 @@ import {
|
|
|
26
26
|
cookieDomain,
|
|
27
27
|
DEFAULT_POSTGRES_VERSION,
|
|
28
28
|
dependentsOf,
|
|
29
|
+
mobileOrigins,
|
|
29
30
|
PUBLIC,
|
|
30
31
|
PUBLIC_PREFIX,
|
|
31
32
|
providedKeyFor,
|
|
32
33
|
provisionOrder,
|
|
34
|
+
schemeBase,
|
|
33
35
|
} from '@geekmidas/manifest';
|
|
34
36
|
import {
|
|
35
37
|
Cache,
|
|
@@ -39,11 +41,15 @@ import {
|
|
|
39
41
|
withCacheTable,
|
|
40
42
|
} from './aws/Cache';
|
|
41
43
|
import { Credential } from './aws/Credential';
|
|
42
|
-
import {
|
|
44
|
+
import { Cron, type CronSchedule } from './aws/Cron';
|
|
45
|
+
import { Database, DatabaseNeedsVpc, type DatabaseProps } from './aws/Database';
|
|
43
46
|
import { DatabaseBootstrap } from './aws/DatabaseBootstrap';
|
|
44
47
|
import { DatabaseReader, DatabaseSchema } from './aws/DerivedDatabase';
|
|
45
48
|
import { Email, EmailNeedsSender } from './aws/Email';
|
|
49
|
+
import { Encryption, type EncryptionProps } from './aws/Encryption';
|
|
50
|
+
import { ExternalApi, type ExternalApiProps } from './aws/ExternalApi';
|
|
46
51
|
import { FileServer } from './aws/FileServer';
|
|
52
|
+
import { Function } from './aws/Function';
|
|
47
53
|
import { ObjectStorage } from './aws/ObjectStorage';
|
|
48
54
|
import { Queue } from './aws/Queue';
|
|
49
55
|
import { RestApiSurface } from './aws/RestApiSurface';
|
|
@@ -98,8 +104,8 @@ export interface ProvisionContext {
|
|
|
98
104
|
/**
|
|
99
105
|
* Where a declared cache lives, and who delivers mail.
|
|
100
106
|
*
|
|
101
|
-
* Deployment choices rather than declarations, for the same reason
|
|
102
|
-
*
|
|
107
|
+
* Deployment choices rather than declarations, for the same reason the
|
|
108
|
+
* event broker is: the same application code caches into any of them and
|
|
103
109
|
* sends through any of them. They reach the provisioner here because it is
|
|
104
110
|
* the only place that knows both the choice and the manifest.
|
|
105
111
|
*/
|
|
@@ -220,7 +226,11 @@ const PROVISIONERS: Partial<Record<DeclarationKind, Provisioner>> = {
|
|
|
220
226
|
// rather than whatever Aurora defaults to that month. Local ran 18
|
|
221
227
|
// while this ran 17.7, and neither was written down anywhere.
|
|
222
228
|
version: String(d.version ?? DEFAULT_POSTGRES_VERSION),
|
|
223
|
-
|
|
229
|
+
// `DatabaseProps`, not `sst.aws.PostgresArgs`: the database needs the
|
|
230
|
+
// `Vpc` component itself (its bootstrap function runs in the same VPC
|
|
231
|
+
// and needs the security groups too), where RDS would accept bare
|
|
232
|
+
// subnet ids. TS 7 checks the spread; 6 let the wider type through.
|
|
233
|
+
...(props as unknown as Omit<DatabaseProps, 'schema'>),
|
|
224
234
|
});
|
|
225
235
|
|
|
226
236
|
// A database needs its own roles, not just its tenants'. Registering only
|
|
@@ -232,13 +242,21 @@ const PROVISIONERS: Partial<Record<DeclarationKind, Provisioner>> = {
|
|
|
232
242
|
const bootstrap = bootstrapFor(cluster, d.id, context);
|
|
233
243
|
const runtime = roleNameFor(d, context);
|
|
234
244
|
|
|
235
|
-
bootstrap?.add({
|
|
245
|
+
const credentials = bootstrap?.add({
|
|
236
246
|
id: d.id,
|
|
237
247
|
schema: d.schema,
|
|
238
248
|
runtime,
|
|
239
249
|
owner: ownerRole(runtime),
|
|
250
|
+
ownsDatabase: true,
|
|
240
251
|
...(hasReader(d.id, context) ? { reader: readerRole(runtime) } : {}),
|
|
241
252
|
});
|
|
253
|
+
|
|
254
|
+
// Registering the role was half of it: its handlers have to connect as
|
|
255
|
+
// it too, or the DDL creates a role nothing uses and every handler
|
|
256
|
+
// linked to the database still holds the master.
|
|
257
|
+
if (credentials) {
|
|
258
|
+
cluster.connectAs({ user: runtime, password: credentials.runtime });
|
|
259
|
+
}
|
|
242
260
|
}
|
|
243
261
|
|
|
244
262
|
return cluster;
|
|
@@ -363,17 +381,12 @@ const PROVISIONERS: Partial<Record<DeclarationKind, Provisioner>> = {
|
|
|
363
381
|
},
|
|
364
382
|
|
|
365
383
|
/**
|
|
366
|
-
* The surface, without its routes.
|
|
367
|
-
*
|
|
368
|
-
* An API Gateway with no routes 404s everything, and that is the honest
|
|
369
|
-
* state: the *surface* is what this kind declares, and mounting handlers on
|
|
370
|
-
* it is the endpoint merge that has not landed — routes still reach the
|
|
371
|
-
* deploy target through the separate `RouteInfo[]` pipeline.
|
|
384
|
+
* The surface, without its routes yet.
|
|
372
385
|
*
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
*
|
|
376
|
-
*
|
|
386
|
+
* Its address is what a site inlines as `VITE_API_URL`, what an auth server
|
|
387
|
+
* puts on its trusted-origin list, and what the cookie domain derives from —
|
|
388
|
+
* so it is provisioned in order, and its endpoints are mounted by
|
|
389
|
+
* {@link mountRoutes} once everything they link to exists.
|
|
377
390
|
*/
|
|
378
391
|
'rest-api': (stack, d, props, context) =>
|
|
379
392
|
new RestApiSurface(stack, d.id, {
|
|
@@ -385,6 +398,30 @@ const PROVISIONERS: Partial<Record<DeclarationKind, Provisioner>> = {
|
|
|
385
398
|
|
|
386
399
|
// The same storage as a secret, under a different role — see the component.
|
|
387
400
|
credential: (stack, d, props) => new Credential(stack, d.id, props),
|
|
401
|
+
|
|
402
|
+
// In the stage's region, as mail is: a key in another region is a key
|
|
403
|
+
// every call crosses the world to reach.
|
|
404
|
+
encryption: (stack, d, props) =>
|
|
405
|
+
new Encryption(stack, d.id, {
|
|
406
|
+
...(props as Partial<EncryptionProps>),
|
|
407
|
+
region:
|
|
408
|
+
(props as Partial<EncryptionProps>).region ??
|
|
409
|
+
$app.providers?.aws?.region ??
|
|
410
|
+
'us-east-1',
|
|
411
|
+
}),
|
|
412
|
+
|
|
413
|
+
// Nothing to provision: the URL this stage calls, and the credentials kept
|
|
414
|
+
// as a secret.
|
|
415
|
+
'external-api': (stack, d, props) => {
|
|
416
|
+
if (d.kind !== 'external-api') {
|
|
417
|
+
throw new UnknownDeclarationKind(d.kind, []);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return new ExternalApi(stack, d.id, {
|
|
421
|
+
...(props as Partial<ExternalApiProps>),
|
|
422
|
+
url: d.url,
|
|
423
|
+
});
|
|
424
|
+
},
|
|
388
425
|
};
|
|
389
426
|
|
|
390
427
|
/**
|
|
@@ -565,9 +602,7 @@ function rootCluster(component: Provisioned): Database {
|
|
|
565
602
|
* printed: one line per route, naming what it can reach.
|
|
566
603
|
*
|
|
567
604
|
* A surface with no routes says so rather than printing nothing, because "no
|
|
568
|
-
* routes
|
|
569
|
-
* application's own API that is currently the true state, pending the endpoint
|
|
570
|
-
* merge.
|
|
605
|
+
* routes" and "this printed nothing" look identical otherwise.
|
|
571
606
|
*
|
|
572
607
|
* Pure, so what gets printed can be asserted without a deploy.
|
|
573
608
|
*/
|
|
@@ -636,6 +671,8 @@ async function callersOf(
|
|
|
636
671
|
id: string,
|
|
637
672
|
manifest: ConstructManifest,
|
|
638
673
|
provisioned: ProvisionedManifest,
|
|
674
|
+
/** The app's name — a mobile caller's scheme base when it gives none. */
|
|
675
|
+
project: string,
|
|
639
676
|
): Promise<{ trustedOrigins: string; cookieDomain?: string }> {
|
|
640
677
|
// Every address here is a Pulumi output, not a string — a CloudFront domain
|
|
641
678
|
// and an API Gateway endpoint are both known only after their resource
|
|
@@ -660,8 +697,17 @@ async function callersOf(
|
|
|
660
697
|
const own = await resolved(provisioned[id]?.provides().url);
|
|
661
698
|
const domain = cookieDomain([...(own ? [own] : []), ...origins]);
|
|
662
699
|
|
|
700
|
+
// A mobile caller is reached by its scheme — the bare one, deployed: what
|
|
701
|
+
// the store build registers.
|
|
702
|
+
const schemes = dependentsOf(manifest, id).flatMap((caller) => {
|
|
703
|
+
const app = manifest[caller];
|
|
704
|
+
return app?.kind === 'mobile-app'
|
|
705
|
+
? mobileOrigins(schemeBase(project, app.scheme))
|
|
706
|
+
: [];
|
|
707
|
+
});
|
|
708
|
+
|
|
663
709
|
return {
|
|
664
|
-
trustedOrigins: origins.join(','),
|
|
710
|
+
trustedOrigins: [...origins, ...schemes].join(','),
|
|
665
711
|
...(domain ? { cookieDomain: domain } : {}),
|
|
666
712
|
};
|
|
667
713
|
}
|
|
@@ -711,6 +757,162 @@ export function isServed(id: string, manifest: ConstructManifest): boolean {
|
|
|
711
757
|
);
|
|
712
758
|
}
|
|
713
759
|
|
|
760
|
+
/**
|
|
761
|
+
* Kinds the in-order pass skips.
|
|
762
|
+
*
|
|
763
|
+
* - `mobile-app` ships through EAS and the stores. It is in the manifest for
|
|
764
|
+
* what it makes the surfaces trust, which `callersOf` reads off the graph.
|
|
765
|
+
* - `worker` is a process, not a resource: what it runs is provisioned as
|
|
766
|
+
* what it is — a queue's consumer by {@link subscribeConsumers}.
|
|
767
|
+
* - `cron` and `function` are Lambdas linked to what they depend on, so they
|
|
768
|
+
* are provisioned once all of it exists, by {@link provisionCompute}.
|
|
769
|
+
*/
|
|
770
|
+
export const PROVISIONED_ELSEWHERE: ReadonlySet<DeclarationKind> =
|
|
771
|
+
new Set<DeclarationKind>(['mobile-app', 'worker', 'cron', 'function']);
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Every function and cron: a Lambda running the handler the build wrote,
|
|
775
|
+
* linked to exactly what it depends on, in the database's network when it
|
|
776
|
+
* reaches one. A cron is that Lambda on its schedule; a function is reached
|
|
777
|
+
* at its URL, which only callers granted IAM access can invoke.
|
|
778
|
+
*
|
|
779
|
+
* After everything else is provisioned, because each links to things
|
|
780
|
+
* `provisionOrder` does not order before it.
|
|
781
|
+
*/
|
|
782
|
+
export function provisionCompute(
|
|
783
|
+
stack: StackType,
|
|
784
|
+
manifest: ConstructManifest,
|
|
785
|
+
provisioned: ProvisionedManifest,
|
|
786
|
+
): Cron[] {
|
|
787
|
+
const crons: Cron[] = [];
|
|
788
|
+
|
|
789
|
+
for (const [id, declaration] of Object.entries(manifest)) {
|
|
790
|
+
if (declaration?.kind !== 'function' && declaration?.kind !== 'cron') {
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
const { link } = resolveEdges(declaration.dependencies, provisioned);
|
|
795
|
+
const vpc = vpcFor(link);
|
|
796
|
+
const lambda = new Function(
|
|
797
|
+
stack,
|
|
798
|
+
declaration.kind === 'cron' ? `${id}Function` : id,
|
|
799
|
+
{
|
|
800
|
+
name: stack.logicalPrefixedName(id),
|
|
801
|
+
handler: declaration.handler,
|
|
802
|
+
link,
|
|
803
|
+
...(vpc ? { vpc } : {}),
|
|
804
|
+
...(declaration.kind === 'function'
|
|
805
|
+
? { url: { authorization: 'iam' } }
|
|
806
|
+
: {}),
|
|
807
|
+
},
|
|
808
|
+
);
|
|
809
|
+
|
|
810
|
+
if (declaration.kind === 'cron') {
|
|
811
|
+
crons.push(
|
|
812
|
+
new Cron(stack, id, {
|
|
813
|
+
processor: lambda,
|
|
814
|
+
schedule: declaration.schedule as CronSchedule,
|
|
815
|
+
}),
|
|
816
|
+
);
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
assertProvides(
|
|
821
|
+
id,
|
|
822
|
+
declaration.provides,
|
|
823
|
+
Object.keys(lambda.provides()).map((role) =>
|
|
824
|
+
providedKeyFor(id, declaration.kind, role),
|
|
825
|
+
),
|
|
826
|
+
);
|
|
827
|
+
provisioned[id] = lambda;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
return crons;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/** A provisioned queue: what {@link subscribeConsumers} subscribes through. */
|
|
834
|
+
interface Consumable {
|
|
835
|
+
consume(consumer: {
|
|
836
|
+
handler: string;
|
|
837
|
+
link?: unknown[];
|
|
838
|
+
vpc?: sst.aws.Vpc;
|
|
839
|
+
}): unknown;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* The network a function has to run in to reach what it links to.
|
|
844
|
+
*
|
|
845
|
+
* A database is private to its VPC, so a handler linked to one — or to a
|
|
846
|
+
* schema tenant or reader of one — runs inside it, or its connection times out.
|
|
847
|
+
*/
|
|
848
|
+
export function vpcFor(link: readonly Provisioned[]): sst.aws.Vpc | undefined {
|
|
849
|
+
for (const component of link) {
|
|
850
|
+
const cluster = rootCluster(component);
|
|
851
|
+
if (cluster instanceof Database) return cluster.vpc;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
return undefined;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
/**
|
|
858
|
+
* Mount every surface's endpoints — the handlers the build wrote, each linked
|
|
859
|
+
* to exactly what that endpoint depends on.
|
|
860
|
+
*
|
|
861
|
+
* After every construct is provisioned, for the reason consumers are: an
|
|
862
|
+
* endpoint links to things `provisionOrder` does not order before its surface.
|
|
863
|
+
*/
|
|
864
|
+
export function mountRoutes(
|
|
865
|
+
manifest: ConstructManifest,
|
|
866
|
+
provisioned: ProvisionedManifest,
|
|
867
|
+
): void {
|
|
868
|
+
for (const [id, declaration] of Object.entries(manifest)) {
|
|
869
|
+
if (declaration?.kind !== 'rest-api') continue;
|
|
870
|
+
|
|
871
|
+
const surface = provisioned[id];
|
|
872
|
+
if (!(surface instanceof RestApiSurface)) continue;
|
|
873
|
+
|
|
874
|
+
for (const endpoint of declaration.endpoints) {
|
|
875
|
+
const { link } = resolveEdges(endpoint.dependencies, provisioned);
|
|
876
|
+
const vpc = vpcFor(link);
|
|
877
|
+
surface.mount({
|
|
878
|
+
method: endpoint.method,
|
|
879
|
+
path: endpoint.path,
|
|
880
|
+
handler: endpoint.handler,
|
|
881
|
+
link,
|
|
882
|
+
...(vpc ? { vpc } : {}),
|
|
883
|
+
...(endpoint.authorizer ? { authorizer: endpoint.authorizer } : {}),
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
/**
|
|
890
|
+
* Subscribe every queue's one consumer — the handler the build wrote for it,
|
|
891
|
+
* linked to what that consumer depends on.
|
|
892
|
+
*
|
|
893
|
+
* After every construct is provisioned, because a consumer links to things
|
|
894
|
+
* that `provisionOrder` does not order before the queue.
|
|
895
|
+
*/
|
|
896
|
+
export function subscribeConsumers(
|
|
897
|
+
manifest: ConstructManifest,
|
|
898
|
+
provisioned: ProvisionedManifest,
|
|
899
|
+
): void {
|
|
900
|
+
for (const [id, declaration] of Object.entries(manifest)) {
|
|
901
|
+
if (declaration?.kind !== 'queue') continue;
|
|
902
|
+
|
|
903
|
+
const queue = provisioned[id] as unknown as Consumable | undefined;
|
|
904
|
+
if (!queue) continue;
|
|
905
|
+
|
|
906
|
+
const { link } = resolveEdges(declaration.worker.dependencies, provisioned);
|
|
907
|
+
const vpc = vpcFor(link);
|
|
908
|
+
queue.consume({
|
|
909
|
+
handler: declaration.worker.handler,
|
|
910
|
+
link,
|
|
911
|
+
...(vpc ? { vpc } : {}),
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
714
916
|
/** The provisioner for a kind. Pure — the lookup is testable without Pulumi. */
|
|
715
917
|
export function provisionerFor(kind: DeclarationKind): Provisioner {
|
|
716
918
|
const provisioner = PROVISIONERS[kind];
|
|
@@ -799,6 +1001,7 @@ export function fromManifest(
|
|
|
799
1001
|
for (const id of [...rest, ...sites]) {
|
|
800
1002
|
const declaration = manifest[id];
|
|
801
1003
|
if (!declaration) continue;
|
|
1004
|
+
if (PROVISIONED_ELSEWHERE.has(declaration.kind)) continue;
|
|
802
1005
|
|
|
803
1006
|
const component = provisionerFor(declaration.kind)(
|
|
804
1007
|
stack,
|
|
@@ -822,6 +1025,12 @@ export function fromManifest(
|
|
|
822
1025
|
provisioned[id] = component;
|
|
823
1026
|
}
|
|
824
1027
|
|
|
1028
|
+
// The functions and crons, each queue's one consumer and each surface's
|
|
1029
|
+
// endpoints, once everything they link to exists.
|
|
1030
|
+
provisionCompute(stack, manifest, provisioned);
|
|
1031
|
+
subscribeConsumers(manifest, provisioned);
|
|
1032
|
+
mountRoutes(manifest, provisioned);
|
|
1033
|
+
|
|
825
1034
|
for (const line of describeRoutes(manifest)) console.log(line);
|
|
826
1035
|
|
|
827
1036
|
// Now everything exists, so every surface's callers can be resolved — the
|
|
@@ -829,7 +1038,7 @@ export function fromManifest(
|
|
|
829
1038
|
for (const [id, deferred] of callers) {
|
|
830
1039
|
// Resolved with the promise itself: the addresses it needs are Pulumi
|
|
831
1040
|
// outputs, so the answer arrives when they do.
|
|
832
|
-
deferred.resolve(callersOf(id, manifest, provisioned));
|
|
1041
|
+
deferred.resolve(callersOf(id, manifest, provisioned, $app.name));
|
|
833
1042
|
}
|
|
834
1043
|
|
|
835
1044
|
// Last, and only now: the roles exist as passwords and secrets from the
|
package/src/sst/index.ts
CHANGED
|
@@ -34,6 +34,7 @@ export {
|
|
|
34
34
|
} from './aws/DatabaseBootstrap';
|
|
35
35
|
export { DatabaseReader, DatabaseSchema } from './aws/DerivedDatabase';
|
|
36
36
|
export { Email, EmailNeedsUrl, type EmailProps } from './aws/Email';
|
|
37
|
+
export { Encryption, type EncryptionProps } from './aws/Encryption';
|
|
37
38
|
export { FileServer, type FileServerProps } from './aws/FileServer';
|
|
38
39
|
export { Function, type FunctionProps } from './aws/Function';
|
|
39
40
|
export {
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
//#region src/utils/index.d.ts
|
|
2
|
-
declare function getLocalIpAddress(): string | null;
|
|
3
|
-
declare function environmentCase(name: string): string;
|
|
4
|
-
declare enum ResourceType {
|
|
5
|
-
ApiGatewayV2 = "sst.aws.ApiGatewayV2",
|
|
6
|
-
Postgres = "sst.aws.Postgres",
|
|
7
|
-
Function = "sst.aws.Function",
|
|
8
|
-
ObjectStorage = "sst.aws.Bucket",
|
|
9
|
-
Vpc = "sst.aws.Vpc",
|
|
10
|
-
Secret = "sst.sst.Secret",
|
|
11
|
-
SSTSecret = "sst:sst:Secret",
|
|
12
|
-
SSTFunction = "sst:sst:Function",
|
|
13
|
-
SSTApiGatewayV2 = "sst:aws:ApiGatewayV2",
|
|
14
|
-
SSTPostgres = "sst:aws:Postgres",
|
|
15
|
-
SSTObjectStorage = "sst:aws:Bucket",
|
|
16
|
-
SnsTopic = "sst:aws:SnsTopic",
|
|
17
|
-
}
|
|
18
|
-
declare function buildResourceEnv(record: Record<string, Resource | string>): Record<string, string>;
|
|
19
|
-
/**
|
|
20
|
-
* SST links a resource under one of two spellings — `sst.aws.Bucket` when it
|
|
21
|
-
* comes from the state file, `sst:aws:Bucket` when it comes from the runtime —
|
|
22
|
-
* and `processors` has always handled both. Only the dotted half was admitted
|
|
23
|
-
* here, so the colon half could not be passed to `buildResourceEnv` without a
|
|
24
|
-
* cast, and `SnsTopic` was missing from `Resource` altogether despite being the
|
|
25
|
-
* only thing that produces `*_ARN`.
|
|
26
|
-
*/
|
|
27
|
-
type ApiGatewayV2 = {
|
|
28
|
-
type: ResourceType.ApiGatewayV2 | ResourceType.SSTApiGatewayV2;
|
|
29
|
-
url: string;
|
|
30
|
-
};
|
|
31
|
-
type Postgres = {
|
|
32
|
-
database: string;
|
|
33
|
-
host: string;
|
|
34
|
-
password: string;
|
|
35
|
-
port: number;
|
|
36
|
-
type: ResourceType.Postgres | ResourceType.SSTPostgres;
|
|
37
|
-
username: string;
|
|
38
|
-
};
|
|
39
|
-
type Function = {
|
|
40
|
-
name: string;
|
|
41
|
-
type: ResourceType.Function | ResourceType.SSTFunction;
|
|
42
|
-
};
|
|
43
|
-
type Bucket = {
|
|
44
|
-
name: string;
|
|
45
|
-
type: ResourceType.ObjectStorage | ResourceType.SSTObjectStorage;
|
|
46
|
-
};
|
|
47
|
-
type SnsTopic = {
|
|
48
|
-
arn: string;
|
|
49
|
-
type: ResourceType.SnsTopic;
|
|
50
|
-
};
|
|
51
|
-
type Vpc = {
|
|
52
|
-
bastion: string;
|
|
53
|
-
type: ResourceType.Vpc;
|
|
54
|
-
};
|
|
55
|
-
type Secret = {
|
|
56
|
-
type: ResourceType.Secret | ResourceType.SSTSecret;
|
|
57
|
-
value: string;
|
|
58
|
-
};
|
|
59
|
-
type Resource = ApiGatewayV2 | Postgres | Function | Bucket | SnsTopic | Vpc | Secret;
|
|
60
|
-
type ResourceProcessor<K extends Resource> = (name: string, value: K) => Record<string, string | number>;
|
|
61
|
-
//# sourceMappingURL=index.d.ts.map
|
|
62
|
-
//#endregion
|
|
63
|
-
export { ApiGatewayV2, Bucket, Function, Postgres, Resource, ResourceProcessor, ResourceType, Secret, SnsTopic, Vpc, buildResourceEnv, environmentCase, getLocalIpAddress };
|
|
64
|
-
//# sourceMappingURL=index-B5CZ1xVf.d.cts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-B5CZ1xVf.d.cts","names":[],"sources":["../src/utils/index.ts"],"sourcesContent":[],"mappings":";iBAGgB,iBAAA,CAAA;AAAA,iBAcA,eAAA,CAdiB,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA;AAcjB,aAOJ,YAAA;EAAA,YAAA,GAAY,sBAAA;EA+DR,QAAA,GAAA,kBAAgB;EAAA,QAAA,GAAA,kBAAA;EAAA,aACR,GAAA,gBAAA;EAAQ,GAAvB,GAAA,aAAA;EAAM,MACZ,GAAA,gBAAA;EAAM,SAAA,GAAA,gBAAA;EA0BG,WAAA,GAAA,kBAAY;EAAA,eAAA,GAAA,sBAAA;EAAA,WACjB,GAAA,kBAAa;EAAY,gBAAgB,GAAA,gBAAA;EAAe,QAAA,GAAA,kBAAA;AAI/D;AAAoB,iBAjCJ,gBAAA,CAiCI,MAAA,EAhCX,MAgCW,CAAA,MAAA,EAhCI,QAgCJ,GAAA,MAAA,CAAA,CAAA,EA/BjB,MA+BiB,CAAA,MAAA,EAAA,MAAA,CAAA;;;AAKmC;AAIvD;;;;AAEuD;AAG3C,KAnBA,YAAA,GAmBM;EAAA,IAAA,EAlBX,YAAA,CAAa,YAkBF,GAlBiB,YAAA,CAAa,eAkB9B;EAAA,GAEX,EAAA,MAAA;CAA0B;AAAgC,KAhBrD,QAAA,GAgBqD;EAGrD,QAAA,EAAA,MAAQ;EAKR,IAAA,EAAG,MAAA;EAKH,QAAA,EAAM,MAAA;EAAA,IAAA,EAAA,MAAA;EAAA,IACX,EAzBA,YAAA,CAAa,QAyBA,GAzBW,YAAA,CAAa,WAyBxB;EAAM,QAAG,EAAA,MAAa;AAAS,CAAA;AAIvC,KAzBA,QAAA,GAyBQ;EAAA,IAAA,EAAA,MAAA;EAAA,IACjB,EAxBI,YAAA,CAAa,QAwBjB,GAxB4B,YAAA,CAAa,WAwBzC;CAAY;AAEZ,KAvBS,MAAA,GAuBT;EAAQ,IACR,EAAA,MAAA;EAAM,IACN,EAvBI,YAAA,CAAa,aAuBjB,GAvBiC,YAAA,CAAa,gBAuB9C;CAAQ;AAER,KAtBS,QAAA,GAsBT;EAAM,GAAA,EAAA,MAAA;EAEG,IAAA,EAtBL,YAAA,CAAa,QAsBS;CAAA;AAAW,KAnB5B,GAAA,GAmB4B;EAAQ,OAExC,EAAA,MAAA;EAAC,IACJ,EApBE,YAAA,CAAa,GAoBf;AAAM,CAAA;KAjBC,MAAA;QACL,YAAA,CAAa,SAAS,YAAA,CAAa;;;KAI9B,QAAA,GACT,eACA,WACA,WACA,SACA,WACA,MACA;KAES,4BAA4B,kCAEhC,MACH"}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
//#region src/utils/index.d.ts
|
|
2
|
-
declare function getLocalIpAddress(): string | null;
|
|
3
|
-
declare function environmentCase(name: string): string;
|
|
4
|
-
declare enum ResourceType {
|
|
5
|
-
ApiGatewayV2 = "sst.aws.ApiGatewayV2",
|
|
6
|
-
Postgres = "sst.aws.Postgres",
|
|
7
|
-
Function = "sst.aws.Function",
|
|
8
|
-
ObjectStorage = "sst.aws.Bucket",
|
|
9
|
-
Vpc = "sst.aws.Vpc",
|
|
10
|
-
Secret = "sst.sst.Secret",
|
|
11
|
-
SSTSecret = "sst:sst:Secret",
|
|
12
|
-
SSTFunction = "sst:sst:Function",
|
|
13
|
-
SSTApiGatewayV2 = "sst:aws:ApiGatewayV2",
|
|
14
|
-
SSTPostgres = "sst:aws:Postgres",
|
|
15
|
-
SSTObjectStorage = "sst:aws:Bucket",
|
|
16
|
-
SnsTopic = "sst:aws:SnsTopic",
|
|
17
|
-
}
|
|
18
|
-
declare function buildResourceEnv(record: Record<string, Resource | string>): Record<string, string>;
|
|
19
|
-
/**
|
|
20
|
-
* SST links a resource under one of two spellings — `sst.aws.Bucket` when it
|
|
21
|
-
* comes from the state file, `sst:aws:Bucket` when it comes from the runtime —
|
|
22
|
-
* and `processors` has always handled both. Only the dotted half was admitted
|
|
23
|
-
* here, so the colon half could not be passed to `buildResourceEnv` without a
|
|
24
|
-
* cast, and `SnsTopic` was missing from `Resource` altogether despite being the
|
|
25
|
-
* only thing that produces `*_ARN`.
|
|
26
|
-
*/
|
|
27
|
-
type ApiGatewayV2 = {
|
|
28
|
-
type: ResourceType.ApiGatewayV2 | ResourceType.SSTApiGatewayV2;
|
|
29
|
-
url: string;
|
|
30
|
-
};
|
|
31
|
-
type Postgres = {
|
|
32
|
-
database: string;
|
|
33
|
-
host: string;
|
|
34
|
-
password: string;
|
|
35
|
-
port: number;
|
|
36
|
-
type: ResourceType.Postgres | ResourceType.SSTPostgres;
|
|
37
|
-
username: string;
|
|
38
|
-
};
|
|
39
|
-
type Function = {
|
|
40
|
-
name: string;
|
|
41
|
-
type: ResourceType.Function | ResourceType.SSTFunction;
|
|
42
|
-
};
|
|
43
|
-
type Bucket = {
|
|
44
|
-
name: string;
|
|
45
|
-
type: ResourceType.ObjectStorage | ResourceType.SSTObjectStorage;
|
|
46
|
-
};
|
|
47
|
-
type SnsTopic = {
|
|
48
|
-
arn: string;
|
|
49
|
-
type: ResourceType.SnsTopic;
|
|
50
|
-
};
|
|
51
|
-
type Vpc = {
|
|
52
|
-
bastion: string;
|
|
53
|
-
type: ResourceType.Vpc;
|
|
54
|
-
};
|
|
55
|
-
type Secret = {
|
|
56
|
-
type: ResourceType.Secret | ResourceType.SSTSecret;
|
|
57
|
-
value: string;
|
|
58
|
-
};
|
|
59
|
-
type Resource = ApiGatewayV2 | Postgres | Function | Bucket | SnsTopic | Vpc | Secret;
|
|
60
|
-
type ResourceProcessor<K extends Resource> = (name: string, value: K) => Record<string, string | number>;
|
|
61
|
-
//# sourceMappingURL=index.d.ts.map
|
|
62
|
-
//#endregion
|
|
63
|
-
export { ApiGatewayV2, Bucket, Function, Postgres, Resource, ResourceProcessor, ResourceType, Secret, SnsTopic, Vpc, buildResourceEnv, environmentCase, getLocalIpAddress };
|
|
64
|
-
//# sourceMappingURL=index-DhHRjduZ.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-DhHRjduZ.d.mts","names":[],"sources":["../src/utils/index.ts"],"sourcesContent":[],"mappings":";iBAGgB,iBAAA,CAAA;AAAA,iBAcA,eAAA,CAdiB,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA;AAcjB,aAOJ,YAAA;EAAA,YAAA,GAAY,sBAAA;EA+DR,QAAA,GAAA,kBAAgB;EAAA,QAAA,GAAA,kBAAA;EAAA,aACR,GAAA,gBAAA;EAAQ,GAAvB,GAAA,aAAA;EAAM,MACZ,GAAA,gBAAA;EAAM,SAAA,GAAA,gBAAA;EA0BG,WAAA,GAAA,kBAAY;EAAA,eAAA,GAAA,sBAAA;EAAA,WACjB,GAAA,kBAAa;EAAY,gBAAgB,GAAA,gBAAA;EAAe,QAAA,GAAA,kBAAA;AAI/D;AAAoB,iBAjCJ,gBAAA,CAiCI,MAAA,EAhCX,MAgCW,CAAA,MAAA,EAhCI,QAgCJ,GAAA,MAAA,CAAA,CAAA,EA/BjB,MA+BiB,CAAA,MAAA,EAAA,MAAA,CAAA;;;AAKmC;AAIvD;;;;AAEuD;AAG3C,KAnBA,YAAA,GAmBM;EAAA,IAAA,EAlBX,YAAA,CAAa,YAkBF,GAlBiB,YAAA,CAAa,eAkB9B;EAAA,GAEX,EAAA,MAAA;CAA0B;AAAgC,KAhBrD,QAAA,GAgBqD;EAGrD,QAAA,EAAA,MAAQ;EAKR,IAAA,EAAG,MAAA;EAKH,QAAA,EAAM,MAAA;EAAA,IAAA,EAAA,MAAA;EAAA,IACX,EAzBA,YAAA,CAAa,QAyBA,GAzBW,YAAA,CAAa,WAyBxB;EAAM,QAAG,EAAA,MAAa;AAAS,CAAA;AAIvC,KAzBA,QAAA,GAyBQ;EAAA,IAAA,EAAA,MAAA;EAAA,IACjB,EAxBI,YAAA,CAAa,QAwBjB,GAxB4B,YAAA,CAAa,WAwBzC;CAAY;AAEZ,KAvBS,MAAA,GAuBT;EAAQ,IACR,EAAA,MAAA;EAAM,IACN,EAvBI,YAAA,CAAa,aAuBjB,GAvBiC,YAAA,CAAa,gBAuB9C;CAAQ;AAER,KAtBS,QAAA,GAsBT;EAAM,GAAA,EAAA,MAAA;EAEG,IAAA,EAtBL,YAAA,CAAa,QAsBS;CAAA;AAAW,KAnB5B,GAAA,GAmB4B;EAAQ,OAExC,EAAA,MAAA;EAAC,IACJ,EApBE,YAAA,CAAa,GAoBf;AAAM,CAAA;KAjBC,MAAA;QACL,YAAA,CAAa,SAAS,YAAA,CAAa;;;KAI9B,QAAA,GACT,eACA,WACA,WACA,SACA,WACA,MACA;KAES,4BAA4B,kCAEhC,MACH"}
|
package/dist/utils-B1a2UuEO.cjs
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
//#region rolldown:runtime
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __copyProps = (to, from, except, desc) => {
|
|
9
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
-
key = keys[i];
|
|
11
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
-
get: ((k) => from[k]).bind(null, key),
|
|
13
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
-
value: mod,
|
|
20
|
-
enumerable: true
|
|
21
|
-
}) : target, mod));
|
|
22
|
-
|
|
23
|
-
//#endregion
|
|
24
|
-
const node_os = __toESM(require("node:os"));
|
|
25
|
-
const lodash_snakecase = __toESM(require("lodash.snakecase"));
|
|
26
|
-
|
|
27
|
-
//#region src/utils/index.ts
|
|
28
|
-
function getLocalIpAddress() {
|
|
29
|
-
const networkInterfaces = node_os.default.networkInterfaces();
|
|
30
|
-
for (const interfaceName in networkInterfaces) {
|
|
31
|
-
const addresses = networkInterfaces[interfaceName] || [];
|
|
32
|
-
for (const addressInfo of addresses) if (addressInfo.family === "IPv4" && !addressInfo.internal) return addressInfo.address;
|
|
33
|
-
}
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
function environmentCase(name) {
|
|
37
|
-
return (0, lodash_snakecase.default)(name).toUpperCase().replace(/_\d+/g, (r) => {
|
|
38
|
-
return r.replace("_", "");
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
let ResourceType = /* @__PURE__ */ function(ResourceType$1) {
|
|
42
|
-
ResourceType$1["ApiGatewayV2"] = "sst.aws.ApiGatewayV2";
|
|
43
|
-
ResourceType$1["Postgres"] = "sst.aws.Postgres";
|
|
44
|
-
ResourceType$1["Function"] = "sst.aws.Function";
|
|
45
|
-
ResourceType$1["ObjectStorage"] = "sst.aws.Bucket";
|
|
46
|
-
ResourceType$1["Vpc"] = "sst.aws.Vpc";
|
|
47
|
-
ResourceType$1["Secret"] = "sst.sst.Secret";
|
|
48
|
-
ResourceType$1["SSTSecret"] = "sst:sst:Secret";
|
|
49
|
-
ResourceType$1["SSTFunction"] = "sst:sst:Function";
|
|
50
|
-
ResourceType$1["SSTApiGatewayV2"] = "sst:aws:ApiGatewayV2";
|
|
51
|
-
ResourceType$1["SSTPostgres"] = "sst:aws:Postgres";
|
|
52
|
-
ResourceType$1["SSTObjectStorage"] = "sst:aws:Bucket";
|
|
53
|
-
ResourceType$1["SnsTopic"] = "sst:aws:SnsTopic";
|
|
54
|
-
return ResourceType$1;
|
|
55
|
-
}({});
|
|
56
|
-
const secret = (name, value) => ({ [environmentCase(name)]: value.value });
|
|
57
|
-
const postgres = (key, value) => {
|
|
58
|
-
const prefix = `${environmentCase(key)}`;
|
|
59
|
-
return {
|
|
60
|
-
[`${prefix}_NAME`]: value.database,
|
|
61
|
-
[`${prefix}_HOST`]: value.host,
|
|
62
|
-
[`${prefix}_PASSWORD`]: value.password,
|
|
63
|
-
[`${prefix}_PORT`]: value.port,
|
|
64
|
-
[`${prefix}_USERNAME`]: value.username
|
|
65
|
-
};
|
|
66
|
-
};
|
|
67
|
-
const bucket = (name, value) => {
|
|
68
|
-
const prefix = `${environmentCase(name)}`;
|
|
69
|
-
return { [`${prefix}_NAME`]: value.name };
|
|
70
|
-
};
|
|
71
|
-
const topic = (name, value) => {
|
|
72
|
-
const prefix = `${environmentCase(name)}`;
|
|
73
|
-
const key = `${prefix}_ARN`;
|
|
74
|
-
return { [key]: value.arn };
|
|
75
|
-
};
|
|
76
|
-
const noop = (_name, _value) => ({});
|
|
77
|
-
const processors = {
|
|
78
|
-
[ResourceType.ApiGatewayV2]: noop,
|
|
79
|
-
[ResourceType.Function]: noop,
|
|
80
|
-
[ResourceType.Vpc]: noop,
|
|
81
|
-
[ResourceType.Secret]: secret,
|
|
82
|
-
[ResourceType.Postgres]: postgres,
|
|
83
|
-
[ResourceType.ObjectStorage]: bucket,
|
|
84
|
-
[ResourceType.SSTSecret]: secret,
|
|
85
|
-
[ResourceType.SSTObjectStorage]: bucket,
|
|
86
|
-
[ResourceType.SSTFunction]: noop,
|
|
87
|
-
[ResourceType.SSTPostgres]: postgres,
|
|
88
|
-
[ResourceType.SSTApiGatewayV2]: noop,
|
|
89
|
-
[ResourceType.SnsTopic]: topic
|
|
90
|
-
};
|
|
91
|
-
function buildResourceEnv(record) {
|
|
92
|
-
const env = {};
|
|
93
|
-
for (const [k, value] of Object.entries(record)) {
|
|
94
|
-
if (typeof value === "string") {
|
|
95
|
-
env[environmentCase(k)] = value;
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
const processor = processors[value.type];
|
|
99
|
-
if (processor) Object.assign(env, processor(k, value));
|
|
100
|
-
}
|
|
101
|
-
return env;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
//#endregion
|
|
105
|
-
Object.defineProperty(exports, 'ResourceType', {
|
|
106
|
-
enumerable: true,
|
|
107
|
-
get: function () {
|
|
108
|
-
return ResourceType;
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
Object.defineProperty(exports, 'buildResourceEnv', {
|
|
112
|
-
enumerable: true,
|
|
113
|
-
get: function () {
|
|
114
|
-
return buildResourceEnv;
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
Object.defineProperty(exports, 'environmentCase', {
|
|
118
|
-
enumerable: true,
|
|
119
|
-
get: function () {
|
|
120
|
-
return environmentCase;
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
Object.defineProperty(exports, 'getLocalIpAddress', {
|
|
124
|
-
enumerable: true,
|
|
125
|
-
get: function () {
|
|
126
|
-
return getLocalIpAddress;
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
//# sourceMappingURL=utils-B1a2UuEO.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils-B1a2UuEO.cjs","names":["name: string","value: Secret","key: string","value: Postgres","value: Bucket","value: SnsTopic","_name: string","_value: any","processors: Record<ResourceType, ResourceProcessor<any>>","record: Record<string, Resource | string>","env: Record<string, string>"],"sources":["../src/utils/index.ts"],"sourcesContent":["import os from 'node:os';\nimport snakecase from 'lodash.snakecase';\n\nexport function getLocalIpAddress() {\n\tconst networkInterfaces = os.networkInterfaces();\n\tfor (const interfaceName in networkInterfaces) {\n\t\tconst addresses = networkInterfaces[interfaceName] || [];\n\t\tfor (const addressInfo of addresses) {\n\t\t\t// Look for an IPv4 address that is not internal (loopback)\n\t\t\tif (addressInfo.family === 'IPv4' && !addressInfo.internal) {\n\t\t\t\treturn addressInfo.address;\n\t\t\t}\n\t\t}\n\t}\n\treturn null; // No suitable IP address found\n}\n\nexport function environmentCase(name: string) {\n\treturn snakecase(name)\n\t\t.toUpperCase()\n\t\t.replace(/_\\d+/g, (r) => {\n\t\t\treturn r.replace('_', '');\n\t\t});\n}\nexport enum ResourceType {\n\tApiGatewayV2 = 'sst.aws.ApiGatewayV2',\n\tPostgres = 'sst.aws.Postgres',\n\tFunction = 'sst.aws.Function',\n\tObjectStorage = 'sst.aws.Bucket',\n\tVpc = 'sst.aws.Vpc',\n\tSecret = 'sst.sst.Secret',\n\tSSTSecret = 'sst:sst:Secret',\n\tSSTFunction = 'sst:sst:Function',\n\tSSTApiGatewayV2 = 'sst:aws:ApiGatewayV2',\n\tSSTPostgres = 'sst:aws:Postgres',\n\tSSTObjectStorage = 'sst:aws:Bucket',\n\tSnsTopic = 'sst:aws:SnsTopic',\n}\n\nconst secret = (name: string, value: Secret) => ({\n\t[environmentCase(name)]: value.value,\n});\nconst postgres = (key: string, value: Postgres) => {\n\tconst prefix = `${environmentCase(key)}`;\n\treturn {\n\t\t[`${prefix}_NAME`]: value.database,\n\t\t[`${prefix}_HOST`]: value.host,\n\t\t[`${prefix}_PASSWORD`]: value.password,\n\t\t[`${prefix}_PORT`]: value.port,\n\t\t[`${prefix}_USERNAME`]: value.username,\n\t};\n};\n\nconst bucket = (name: string, value: Bucket) => {\n\tconst prefix = `${environmentCase(name)}`;\n\treturn {\n\t\t[`${prefix}_NAME`]: value.name,\n\t};\n};\n\nconst topic = (name: string, value: SnsTopic) => {\n\tconst prefix = `${environmentCase(name)}`;\n\tconst key = `${prefix}_ARN`;\n\n\treturn {\n\t\t[key]: value.arn,\n\t};\n};\n\nconst noop = (_name: string, _value: any) => ({});\n\nconst processors: Record<ResourceType, ResourceProcessor<any>> = {\n\t[ResourceType.ApiGatewayV2]: noop,\n\t[ResourceType.Function]: noop,\n\t[ResourceType.Vpc]: noop,\n\t[ResourceType.Secret]: secret,\n\t[ResourceType.Postgres]: postgres,\n\t[ResourceType.ObjectStorage]: bucket,\n\n\t[ResourceType.SSTSecret]: secret,\n\t[ResourceType.SSTObjectStorage]: bucket,\n\t[ResourceType.SSTFunction]: noop,\n\t[ResourceType.SSTPostgres]: postgres,\n\t[ResourceType.SSTApiGatewayV2]: noop,\n\t[ResourceType.SnsTopic]: topic,\n};\n\nexport function buildResourceEnv(\n\trecord: Record<string, Resource | string>,\n): Record<string, string> {\n\tconst env: Record<string, string> = {};\n\tfor (const [k, value] of Object.entries(record)) {\n\t\tif (typeof value === 'string') {\n\t\t\tenv[environmentCase(k)] = value;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst processor = processors[value.type];\n\t\tif (processor) {\n\t\t\tObject.assign(env, processor(k, value));\n\t\t} else {\n\t\t}\n\t}\n\n\treturn env;\n}\n\n/**\n * SST links a resource under one of two spellings — `sst.aws.Bucket` when it\n * comes from the state file, `sst:aws:Bucket` when it comes from the runtime —\n * and `processors` has always handled both. Only the dotted half was admitted\n * here, so the colon half could not be passed to `buildResourceEnv` without a\n * cast, and `SnsTopic` was missing from `Resource` altogether despite being the\n * only thing that produces `*_ARN`.\n */\nexport type ApiGatewayV2 = {\n\ttype: ResourceType.ApiGatewayV2 | ResourceType.SSTApiGatewayV2;\n\turl: string;\n};\n\nexport type Postgres = {\n\tdatabase: string;\n\thost: string;\n\tpassword: string;\n\tport: number;\n\ttype: ResourceType.Postgres | ResourceType.SSTPostgres;\n\tusername: string;\n};\n\nexport type Function = {\n\tname: string;\n\ttype: ResourceType.Function | ResourceType.SSTFunction;\n};\n\nexport type Bucket = {\n\tname: string;\n\ttype: ResourceType.ObjectStorage | ResourceType.SSTObjectStorage;\n};\n\nexport type SnsTopic = {\n\tarn: string;\n\ttype: ResourceType.SnsTopic;\n};\n\nexport type Vpc = {\n\tbastion: string;\n\ttype: ResourceType.Vpc;\n};\n\nexport type Secret = {\n\ttype: ResourceType.Secret | ResourceType.SSTSecret;\n\tvalue: string;\n};\n\nexport type Resource =\n\t| ApiGatewayV2\n\t| Postgres\n\t| Function\n\t| Bucket\n\t| SnsTopic\n\t| Vpc\n\t| Secret;\n\nexport type ResourceProcessor<K extends Resource> = (\n\tname: string,\n\tvalue: K,\n) => Record<string, string | number>;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAGA,SAAgB,oBAAoB;CACnC,MAAM,oBAAoB,gBAAG,mBAAmB;AAChD,MAAK,MAAM,iBAAiB,mBAAmB;EAC9C,MAAM,YAAY,kBAAkB,kBAAkB,CAAE;AACxD,OAAK,MAAM,eAAe,UAEzB,KAAI,YAAY,WAAW,WAAW,YAAY,SACjD,QAAO,YAAY;CAGrB;AACD,QAAO;AACP;AAED,SAAgB,gBAAgBA,MAAc;AAC7C,QAAO,8BAAU,KAAK,CACpB,aAAa,CACb,QAAQ,SAAS,CAAC,MAAM;AACxB,SAAO,EAAE,QAAQ,KAAK,GAAG;CACzB,EAAC;AACH;AACD,IAAY,wDAAL;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AACA;AAED,MAAM,SAAS,CAACA,MAAcC,WAAmB,GAC/C,gBAAgB,KAAK,GAAG,MAAM,MAC/B;AACD,MAAM,WAAW,CAACC,KAAaC,UAAoB;CAClD,MAAM,UAAU,EAAE,gBAAgB,IAAI,CAAC;AACvC,QAAO;IACJ,EAAE,OAAO,SAAS,MAAM;IACxB,EAAE,OAAO,SAAS,MAAM;IACxB,EAAE,OAAO,aAAa,MAAM;IAC5B,EAAE,OAAO,SAAS,MAAM;IACxB,EAAE,OAAO,aAAa,MAAM;CAC9B;AACD;AAED,MAAM,SAAS,CAACH,MAAcI,UAAkB;CAC/C,MAAM,UAAU,EAAE,gBAAgB,KAAK,CAAC;AACxC,QAAO,IACJ,EAAE,OAAO,SAAS,MAAM,KAC1B;AACD;AAED,MAAM,QAAQ,CAACJ,MAAcK,UAAoB;CAChD,MAAM,UAAU,EAAE,gBAAgB,KAAK,CAAC;CACxC,MAAM,OAAO,EAAE,OAAO;AAEtB,QAAO,GACL,MAAM,MAAM,IACb;AACD;AAED,MAAM,OAAO,CAACC,OAAeC,YAAiB,CAAE;AAEhD,MAAMC,aAA2D;EAC/D,aAAa,eAAe;EAC5B,aAAa,WAAW;EACxB,aAAa,MAAM;EACnB,aAAa,SAAS;EACtB,aAAa,WAAW;EACxB,aAAa,gBAAgB;EAE7B,aAAa,YAAY;EACzB,aAAa,mBAAmB;EAChC,aAAa,cAAc;EAC3B,aAAa,cAAc;EAC3B,aAAa,kBAAkB;EAC/B,aAAa,WAAW;AACzB;AAED,SAAgB,iBACfC,QACyB;CACzB,MAAMC,MAA8B,CAAE;AACtC,MAAK,MAAM,CAAC,GAAG,MAAM,IAAI,OAAO,QAAQ,OAAO,EAAE;AAChD,aAAW,UAAU,UAAU;AAC9B,OAAI,gBAAgB,EAAE,IAAI;AAC1B;EACA;EAED,MAAM,YAAY,WAAW,MAAM;AACnC,MAAI,UACH,QAAO,OAAO,KAAK,UAAU,GAAG,MAAM,CAAC;CAGxC;AAED,QAAO;AACP"}
|