@fjall/generator 0.88.4 → 0.89.4
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/LICENSE +21 -0
- package/dist/src/ast/astCdnParser.d.ts +15 -0
- package/dist/src/ast/astCdnParser.js +114 -0
- package/dist/src/ast/astCommonParser.d.ts +90 -0
- package/dist/src/ast/astCommonParser.js +351 -0
- package/dist/src/ast/astComputeParser.d.ts +14 -2
- package/dist/src/ast/astComputeParser.js +55 -9
- package/dist/src/ast/astDatabaseParser.d.ts +104 -0
- package/dist/src/ast/astDatabaseParser.js +275 -0
- package/dist/src/ast/astInfrastructureParser.d.ts +23 -277
- package/dist/src/ast/astInfrastructureParser.js +83 -1456
- package/dist/src/ast/astMessagingParser.d.ts +25 -0
- package/dist/src/ast/astMessagingParser.js +78 -0
- package/dist/src/ast/astNetworkParser.d.ts +70 -0
- package/dist/src/ast/astNetworkParser.js +219 -0
- package/dist/src/ast/astPatternParser.d.ts +80 -0
- package/dist/src/ast/astPatternParser.js +155 -0
- package/dist/src/ast/astStorageParser.d.ts +18 -0
- package/dist/src/ast/astStorageParser.js +164 -0
- package/dist/src/ast/index.d.ts +1 -0
- package/dist/src/ast/index.js +4 -0
- package/dist/src/dns/bindParser.d.ts +13 -0
- package/dist/src/dns/bindParser.js +224 -0
- package/dist/src/dns/bindWriter.d.ts +2 -0
- package/dist/src/dns/bindWriter.js +52 -0
- package/dist/src/dns/index.d.ts +4 -0
- package/dist/src/dns/index.js +4 -0
- package/dist/src/dns/infrastructureWriter.d.ts +2 -0
- package/dist/src/dns/infrastructureWriter.js +58 -0
- package/dist/src/dns/types.d.ts +82 -0
- package/dist/src/dns/types.js +52 -0
- package/dist/src/generation/common.d.ts +1 -16
- package/dist/src/generation/common.js +2 -28
- package/dist/src/generation/compute.js +77 -28
- package/dist/src/generation/index.d.ts +2 -1
- package/dist/src/generation/index.js +3 -1
- package/dist/src/generation/messagingConnections.d.ts +33 -0
- package/dist/src/generation/messagingConnections.js +73 -0
- package/dist/src/generation/storage.d.ts +5 -1
- package/dist/src/generation/storage.js +9 -1
- package/dist/src/generation/storageConnections.d.ts +3 -3
- package/dist/src/generation/storageConnections.js +8 -4
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +2 -0
- package/dist/src/planning/resourcePlanning.js +0 -2
- package/dist/src/presets/tierTypes.d.ts +4 -1
- package/dist/src/schemas/applicationSchemas.d.ts +854 -0
- package/dist/src/schemas/applicationSchemas.js +80 -0
- package/dist/src/schemas/baseSchemas.d.ts +206 -0
- package/dist/src/schemas/baseSchemas.js +248 -0
- package/dist/src/schemas/cdnSchemas.d.ts +61 -0
- package/dist/src/schemas/cdnSchemas.js +62 -0
- package/dist/src/schemas/computeSchemas.d.ts +723 -0
- package/dist/src/schemas/computeSchemas.js +727 -0
- package/dist/src/schemas/constants.d.ts +12 -8
- package/dist/src/schemas/constants.js +14 -4
- package/dist/src/schemas/databaseSchemas.d.ts +638 -0
- package/dist/src/schemas/databaseSchemas.js +366 -0
- package/dist/src/schemas/messagingSchemas.d.ts +20 -0
- package/dist/src/schemas/messagingSchemas.js +29 -0
- package/dist/src/schemas/networkSchemas.d.ts +246 -0
- package/dist/src/schemas/networkSchemas.js +125 -0
- package/dist/src/schemas/patternSchemas.d.ts +708 -0
- package/dist/src/schemas/patternSchemas.js +294 -0
- package/dist/src/schemas/resourceSchemas.d.ts +24 -3530
- package/dist/src/schemas/resourceSchemas.js +24 -2011
- package/dist/src/schemas/storageSchemas.d.ts +93 -0
- package/dist/src/schemas/storageSchemas.js +119 -0
- package/dist/src/util/errorUtils.d.ts +1 -2
- package/dist/src/util/errorUtils.js +1 -15
- package/dist/src/validation/patterns.d.ts +9 -0
- package/dist/src/validation/patterns.js +9 -0
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/package.json +5 -3
|
@@ -1,5 +1,43 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as ts from "typescript";
|
|
2
|
+
import { isPlainObject, isParsedObject, isIdentifierRef, isExpressionRef, isCallRef, asString, asNumber, asBoolean, asStringArray, asStringUnion, captureExtraProperties, collectFromAst, extractVariableName, isFactoryBuildCall, isFactoryMethodCall, parseObjectLiteral, } from "./astCommonParser.js";
|
|
2
3
|
import { COMPUTE_TYPES, DEPLOYMENT_TYPES, EC2_INSTANCE_TYPES, ECS_CAPACITY_PROVIDERS, } from "../schemas/constants.js";
|
|
4
|
+
// ---- Extraction helpers ----
|
|
5
|
+
function extractComputeResource(buildCall) {
|
|
6
|
+
if (buildCall.arguments.length < 2)
|
|
7
|
+
return null;
|
|
8
|
+
const nameArg = buildCall.arguments[0];
|
|
9
|
+
const configArg = buildCall.arguments[1];
|
|
10
|
+
if (!ts.isStringLiteral(nameArg) ||
|
|
11
|
+
!ts.isObjectLiteralExpression(configArg)) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
const resourceName = nameArg.text;
|
|
15
|
+
const config = parseObjectLiteral(configArg);
|
|
16
|
+
return {
|
|
17
|
+
resourceName,
|
|
18
|
+
type: asString(config.type) ?? "ecs",
|
|
19
|
+
config,
|
|
20
|
+
node: buildCall,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** Find all compute resources from addCompute calls */
|
|
24
|
+
export function findComputeResources(sourceFile) {
|
|
25
|
+
return collectFromAst(sourceFile, (node) => {
|
|
26
|
+
if (!ts.isCallExpression(node) || !isFactoryMethodCall(node, "addCompute"))
|
|
27
|
+
return null;
|
|
28
|
+
const computeArg = node.arguments[0];
|
|
29
|
+
if (!isFactoryBuildCall(computeArg, "ComputeFactory"))
|
|
30
|
+
return null;
|
|
31
|
+
const resource = extractComputeResource(computeArg);
|
|
32
|
+
if (resource) {
|
|
33
|
+
const varName = extractVariableName(node);
|
|
34
|
+
if (varName)
|
|
35
|
+
resource.variableName = varName;
|
|
36
|
+
}
|
|
37
|
+
return resource;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
// ---- Internal helpers ----
|
|
3
41
|
const MAX_DATABASE_CONNECTION_INDICES = 10;
|
|
4
42
|
const LOAD_BALANCER_TYPES = ["public", "internal"];
|
|
5
43
|
function isEnvironmentRecord(value) {
|
|
@@ -97,12 +135,20 @@ function parseServiceScaling(scalingVal) {
|
|
|
97
135
|
function parseServiceEc2Config(ec2ConfigVal) {
|
|
98
136
|
if (!isParsedObject(ec2ConfigVal))
|
|
99
137
|
return undefined;
|
|
138
|
+
const warmPoolVal = ec2ConfigVal.warmPool;
|
|
139
|
+
const warmPool = isParsedObject(warmPoolVal)
|
|
140
|
+
? {
|
|
141
|
+
minSize: asNumber(warmPoolVal.minSize),
|
|
142
|
+
reuseOnScaleIn: asBoolean(warmPoolVal.reuseOnScaleIn),
|
|
143
|
+
}
|
|
144
|
+
: undefined;
|
|
100
145
|
return {
|
|
101
146
|
instanceType: asString(ec2ConfigVal.instanceType),
|
|
102
147
|
amiHardwareType: asStringUnion(ec2ConfigVal.amiHardwareType, AMI_HARDWARE_TYPES),
|
|
103
148
|
minCapacity: asNumber(ec2ConfigVal.minCapacity),
|
|
104
149
|
maxCapacity: asNumber(ec2ConfigVal.maxCapacity),
|
|
105
150
|
memoryLimitMiB: asNumber(ec2ConfigVal.memoryLimitMiB),
|
|
151
|
+
warmPool,
|
|
106
152
|
};
|
|
107
153
|
}
|
|
108
154
|
/** Resolve connected database names from AST connection references */
|
|
@@ -259,7 +305,7 @@ function convertEcsCompute(compute, computePlan) {
|
|
|
259
305
|
if (extras.length > 0)
|
|
260
306
|
computePlan.extraProperties = extras;
|
|
261
307
|
}
|
|
262
|
-
/** Extract a string from a literal or an expression like `Prefix.VALUE`
|
|
308
|
+
/** Extract a string from a literal or an expression like `Prefix.VALUE` -> "VALUE" */
|
|
263
309
|
function extractEnumString(value, prefix) {
|
|
264
310
|
const str = asString(value);
|
|
265
311
|
if (str)
|
|
@@ -279,10 +325,10 @@ function extractCallStringArg(value) {
|
|
|
279
325
|
function convertLambdaCompute(compute, computePlan) {
|
|
280
326
|
assignIfDefined(computePlan, "deployment", asStringUnion(compute.config.deployment, DEPLOYMENT_TYPES));
|
|
281
327
|
assignIfDefined(computePlan, "timeout", asNumber(compute.config.timeout));
|
|
282
|
-
// Code gen emits `memorySize` but plan field is `memory`
|
|
328
|
+
// Code gen emits `memorySize` but plan field is `memory` -- handle both keys
|
|
283
329
|
assignIfDefined(computePlan, "memory", asNumber(compute.config.memorySize) ?? asNumber(compute.config.memory));
|
|
284
330
|
assignIfDefined(computePlan, "handler", asString(compute.config.handler));
|
|
285
|
-
// Code gen emits `runtime: Runtime.NODEJS_20_X`
|
|
331
|
+
// Code gen emits `runtime: Runtime.NODEJS_20_X` -- extract enum value from expression
|
|
286
332
|
assignIfDefined(computePlan, "runtime", extractEnumString(compute.config.runtime, "Runtime"));
|
|
287
333
|
if (isEnvironmentRecord(compute.config.environment)) {
|
|
288
334
|
computePlan.environment = compute.config.environment;
|
|
@@ -312,7 +358,7 @@ function convertLambdaCompute(compute, computePlan) {
|
|
|
312
358
|
computePlan.codePath = codePath;
|
|
313
359
|
}
|
|
314
360
|
}
|
|
315
|
-
// CDK prop is `lambdaDescription`
|
|
361
|
+
// CDK prop is `lambdaDescription` -- plan stores as `description`
|
|
316
362
|
assignIfDefined(computePlan, "description", asString(compute.config.lambdaDescription));
|
|
317
363
|
assignIfDefined(computePlan, "scheduleExpression", asString(compute.config.scheduleExpression));
|
|
318
364
|
assignIfDefined(computePlan, "architecture", extractEnumString(compute.config.architecture, "Architecture"));
|
|
@@ -347,7 +393,7 @@ function convertLambdaCompute(compute, computePlan) {
|
|
|
347
393
|
}
|
|
348
394
|
function convertEc2Compute(compute, computePlan) {
|
|
349
395
|
assignIfDefined(computePlan, "instanceType", asStringUnion(compute.config.instanceType, EC2_INSTANCE_TYPES));
|
|
350
|
-
// Code gen emits `ssh: {}` or `ssh: false`
|
|
396
|
+
// Code gen emits `ssh: {}` or `ssh: false` -- map to enableSSH boolean.
|
|
351
397
|
// Also accept `enableSSH: true/false` for manually-written code.
|
|
352
398
|
const sshVal = compute.config.ssh;
|
|
353
399
|
if (sshVal === false) {
|
|
@@ -408,8 +454,8 @@ function applyTypeSpecificConfig(computePlan, compute) {
|
|
|
408
454
|
TYPE_CONVERTERS[computePlan.type]?.(compute, computePlan);
|
|
409
455
|
}
|
|
410
456
|
/** Convert parsed compute resources to plan format */
|
|
411
|
-
export function convertComputeResources(
|
|
412
|
-
return
|
|
457
|
+
export function convertComputeResources(computeResources, databaseResources, s3Resources, plan) {
|
|
458
|
+
return computeResources.map((compute) => {
|
|
413
459
|
const computeType = asStringUnion(compute.config.type, COMPUTE_TYPES) ?? "ecs";
|
|
414
460
|
const computePlan = {
|
|
415
461
|
name: compute.resourceName,
|
|
@@ -418,7 +464,7 @@ export function convertComputeResources(parsed, plan) {
|
|
|
418
464
|
connectedDatabase: [],
|
|
419
465
|
...(compute.variableName && { variableName: compute.variableName }),
|
|
420
466
|
};
|
|
421
|
-
applyConnectionConfig(computePlan, compute,
|
|
467
|
+
applyConnectionConfig(computePlan, compute, databaseResources, s3Resources);
|
|
422
468
|
applyTypeSpecificConfig(computePlan, compute);
|
|
423
469
|
inferDatabaseConnectionsFromEnv(computePlan, compute.config.services, plan.database);
|
|
424
470
|
inferStorageConnectionsFromEnv(computePlan, compute.config.services, plan.s3);
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import * as ts from "typescript";
|
|
2
|
+
import type { ApplicationResourcePlan, ExtraProperty } from "../schemas/resourceSchemas.js";
|
|
3
|
+
interface ParsedProxyConfig {
|
|
4
|
+
maxConnections?: number;
|
|
5
|
+
maxIdleConnections?: number;
|
|
6
|
+
connectionBorrowTimeout?: number;
|
|
7
|
+
requireTLS?: boolean;
|
|
8
|
+
}
|
|
9
|
+
interface ParsedReadReplicaConfig {
|
|
10
|
+
instanceType?: string;
|
|
11
|
+
availabilityZone?: string;
|
|
12
|
+
}
|
|
13
|
+
interface ParsedCredentialsConfig {
|
|
14
|
+
username?: string;
|
|
15
|
+
secretRotation?: {
|
|
16
|
+
automaticallyAfterDays?: number;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
interface ParsedDatabaseInsightsConfig {
|
|
20
|
+
mode?: "standard" | "advanced";
|
|
21
|
+
encryptionKey?: {
|
|
22
|
+
awsManaged: true;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
interface ParsedEncryptionConfig {
|
|
26
|
+
storageKey?: {
|
|
27
|
+
awsManaged: true;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
interface ParsedAuroraWriterConfig {
|
|
31
|
+
enableDatabaseInsights?: boolean;
|
|
32
|
+
identifierSuffix?: string;
|
|
33
|
+
availabilityZone?: string;
|
|
34
|
+
}
|
|
35
|
+
interface ParsedAuroraReadersConfig {
|
|
36
|
+
count?: number;
|
|
37
|
+
defaultEnableDatabaseInsights?: boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface ParsedDatabaseResource {
|
|
40
|
+
variableName?: string;
|
|
41
|
+
resourceName: string;
|
|
42
|
+
type: string;
|
|
43
|
+
databaseName: string;
|
|
44
|
+
databaseEngine?: "postgresql" | "mysql";
|
|
45
|
+
engineExpression?: string;
|
|
46
|
+
port?: number;
|
|
47
|
+
deletionProtection?: boolean;
|
|
48
|
+
instanceType?: string;
|
|
49
|
+
multiAz?: boolean;
|
|
50
|
+
publiclyAccessible?: boolean;
|
|
51
|
+
enableSecretRotation?: boolean;
|
|
52
|
+
encryption?: ParsedEncryptionConfig;
|
|
53
|
+
databaseInsights?: ParsedDatabaseInsightsConfig | false;
|
|
54
|
+
proxy?: ParsedProxyConfig | false;
|
|
55
|
+
readReplica?: ParsedReadReplicaConfig | false;
|
|
56
|
+
credentials?: ParsedCredentialsConfig;
|
|
57
|
+
writer?: ParsedAuroraWriterConfig;
|
|
58
|
+
readers?: ParsedAuroraReadersConfig | false;
|
|
59
|
+
backupRetention?: number;
|
|
60
|
+
preferredMaintenanceWindow?: string;
|
|
61
|
+
primaryRegion?: string;
|
|
62
|
+
secondaryRegions?: string[];
|
|
63
|
+
globalClusterIdentifier?: string;
|
|
64
|
+
enableGlobalWriteForwarding?: boolean;
|
|
65
|
+
allocatedStorage?: number;
|
|
66
|
+
monitoringInterval?: number;
|
|
67
|
+
snapshotIdentifier?: string;
|
|
68
|
+
snapshotUsername?: string;
|
|
69
|
+
extraProperties?: ExtraProperty[];
|
|
70
|
+
node: ts.Node;
|
|
71
|
+
}
|
|
72
|
+
declare const DYNAMODB_KEY_TYPES: readonly ["S", "N", "B"];
|
|
73
|
+
type DynamoDBKeyType = (typeof DYNAMODB_KEY_TYPES)[number];
|
|
74
|
+
interface DynamoDBKey {
|
|
75
|
+
name: string;
|
|
76
|
+
type: DynamoDBKeyType;
|
|
77
|
+
}
|
|
78
|
+
export interface ParsedDynamoDBResource {
|
|
79
|
+
variableName?: string;
|
|
80
|
+
resourceName: string;
|
|
81
|
+
partitionKey: DynamoDBKey;
|
|
82
|
+
sortKey?: DynamoDBKey;
|
|
83
|
+
globalSecondaryIndexes?: Array<{
|
|
84
|
+
indexName: string;
|
|
85
|
+
partitionKey: DynamoDBKey;
|
|
86
|
+
sortKey?: DynamoDBKey;
|
|
87
|
+
}>;
|
|
88
|
+
ttlAttribute?: string;
|
|
89
|
+
stream?: boolean;
|
|
90
|
+
extraProperties?: ExtraProperty[];
|
|
91
|
+
node: ts.Node;
|
|
92
|
+
}
|
|
93
|
+
/** Find all database resources (including DynamoDB) from addDatabase calls */
|
|
94
|
+
export declare function findDatabaseResources(sourceFile: ts.SourceFile): ParsedDatabaseResource[];
|
|
95
|
+
/** Split DynamoDB resources (type: "DynamoDB") from regular database resources */
|
|
96
|
+
export declare function splitDynamoDBFromDatabases(allResources: ParsedDatabaseResource[]): {
|
|
97
|
+
databases: ParsedDatabaseResource[];
|
|
98
|
+
dynamodb: ParsedDynamoDBResource[];
|
|
99
|
+
};
|
|
100
|
+
/** Convert parsed database resources to plan format */
|
|
101
|
+
export declare function convertDatabaseResources(databaseResources: ParsedDatabaseResource[]): ApplicationResourcePlan["database"];
|
|
102
|
+
/** Convert parsed DynamoDB resources to plan format */
|
|
103
|
+
export declare function convertDynamoDBResources(dynamodbResources: ParsedDynamoDBResource[]): ApplicationResourcePlan["dynamodb"];
|
|
104
|
+
export {};
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import * as ts from "typescript";
|
|
2
|
+
import { constIncludes } from "../schemas/constants.js";
|
|
3
|
+
import { asBoolean, asNumber, asString, asStringArray, asStringUnion, captureExtraProperties, collectFromAst, extractVariableName, isCallRef, isFactoryBuildCall, isFactoryMethodCall, isParsedObject, omitUndefined, parseObjectLiteral, parseOptionalConfig, parseBooleanOrConfig, resolveTemplateLiteral, typed, } from "./astCommonParser.js";
|
|
4
|
+
const DYNAMODB_KEY_TYPES = ["S", "N", "B"];
|
|
5
|
+
// ---- Extraction helpers ----
|
|
6
|
+
/** Derive database engine from parsed config */
|
|
7
|
+
function extractDatabaseEngine(config) {
|
|
8
|
+
const directEngine = asString(config.databaseEngine);
|
|
9
|
+
if (directEngine === "postgresql" || directEngine === "mysql")
|
|
10
|
+
return directEngine;
|
|
11
|
+
const engine = config.engine;
|
|
12
|
+
if (isCallRef(engine)) {
|
|
13
|
+
const callText = String(engine.__call);
|
|
14
|
+
if (callText.includes("postgres") || callText.includes("auroraPostgres")) {
|
|
15
|
+
return "postgresql";
|
|
16
|
+
}
|
|
17
|
+
if (callText.includes("mysql") || callText.includes("auroraMysql")) {
|
|
18
|
+
return "mysql";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
/** Extract raw engine expression text */
|
|
24
|
+
function extractEngineExpression(config) {
|
|
25
|
+
const engine = config.engine;
|
|
26
|
+
if (isCallRef(engine)) {
|
|
27
|
+
return String(engine.__call);
|
|
28
|
+
}
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
const DATABASE_KNOWN_KEYS = new Set([
|
|
32
|
+
"type",
|
|
33
|
+
"databaseName",
|
|
34
|
+
"databaseEngine",
|
|
35
|
+
"engine",
|
|
36
|
+
"vpc",
|
|
37
|
+
"port",
|
|
38
|
+
"deletionProtection",
|
|
39
|
+
"instanceType",
|
|
40
|
+
"multiAz",
|
|
41
|
+
"publiclyAccessible",
|
|
42
|
+
"enableSecretRotation",
|
|
43
|
+
"encryption",
|
|
44
|
+
"databaseInsights",
|
|
45
|
+
"proxy",
|
|
46
|
+
"readReplica",
|
|
47
|
+
"credentials",
|
|
48
|
+
"writer",
|
|
49
|
+
"readers",
|
|
50
|
+
"backupRetention",
|
|
51
|
+
"preferredMaintenanceWindow",
|
|
52
|
+
"primaryRegion",
|
|
53
|
+
"secondaryRegions",
|
|
54
|
+
"globalClusterIdentifier",
|
|
55
|
+
"enableGlobalWriteForwarding",
|
|
56
|
+
"snapshotIdentifier",
|
|
57
|
+
"allocatedStorage",
|
|
58
|
+
"monitoringInterval",
|
|
59
|
+
"snapshotUsername",
|
|
60
|
+
]);
|
|
61
|
+
function extractDatabaseResource(sourceFile, addDatabaseCall, buildCall) {
|
|
62
|
+
if (buildCall.arguments.length < 2)
|
|
63
|
+
return null;
|
|
64
|
+
const nameArg = buildCall.arguments[0];
|
|
65
|
+
const configArg = buildCall.arguments[1];
|
|
66
|
+
if (!ts.isStringLiteral(nameArg) && !ts.isTemplateExpression(nameArg)) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const resourceName = ts.isStringLiteral(nameArg)
|
|
70
|
+
? nameArg.text
|
|
71
|
+
: resolveTemplateLiteral(sourceFile, nameArg);
|
|
72
|
+
if (!ts.isObjectLiteralExpression(configArg)) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const config = parseObjectLiteral(configArg);
|
|
76
|
+
const resource = {
|
|
77
|
+
resourceName,
|
|
78
|
+
type: asString(config.type) ?? "",
|
|
79
|
+
databaseName: asString(config.databaseName) ?? "",
|
|
80
|
+
databaseEngine: extractDatabaseEngine(config),
|
|
81
|
+
engineExpression: extractEngineExpression(config),
|
|
82
|
+
port: asNumber(config.port),
|
|
83
|
+
deletionProtection: asBoolean(config.deletionProtection),
|
|
84
|
+
instanceType: asString(config.instanceType),
|
|
85
|
+
multiAz: asBoolean(config.multiAz),
|
|
86
|
+
publiclyAccessible: asBoolean(config.publiclyAccessible),
|
|
87
|
+
enableSecretRotation: asBoolean(config.enableSecretRotation),
|
|
88
|
+
encryption: parseOptionalConfig(config.encryption, typed()),
|
|
89
|
+
databaseInsights: parseBooleanOrConfig(config.databaseInsights, typed()),
|
|
90
|
+
proxy: parseBooleanOrConfig(config.proxy, typed()),
|
|
91
|
+
readReplica: parseBooleanOrConfig(config.readReplica, typed()),
|
|
92
|
+
credentials: parseOptionalConfig(config.credentials, typed()),
|
|
93
|
+
writer: parseOptionalConfig(config.writer, typed()),
|
|
94
|
+
readers: parseBooleanOrConfig(config.readers, typed()),
|
|
95
|
+
backupRetention: asNumber(config.backupRetention),
|
|
96
|
+
preferredMaintenanceWindow: asString(config.preferredMaintenanceWindow),
|
|
97
|
+
primaryRegion: asString(config.primaryRegion),
|
|
98
|
+
secondaryRegions: asStringArray(config.secondaryRegions),
|
|
99
|
+
globalClusterIdentifier: asString(config.globalClusterIdentifier),
|
|
100
|
+
enableGlobalWriteForwarding: asBoolean(config.enableGlobalWriteForwarding),
|
|
101
|
+
allocatedStorage: asNumber(config.allocatedStorage),
|
|
102
|
+
monitoringInterval: asNumber(config.monitoringInterval),
|
|
103
|
+
snapshotIdentifier: asString(config.snapshotIdentifier),
|
|
104
|
+
snapshotUsername: asString(config.snapshotUsername),
|
|
105
|
+
node: addDatabaseCall,
|
|
106
|
+
};
|
|
107
|
+
const varName = extractVariableName(addDatabaseCall);
|
|
108
|
+
if (varName)
|
|
109
|
+
resource.variableName = varName;
|
|
110
|
+
const extras = captureExtraProperties(config, DATABASE_KNOWN_KEYS);
|
|
111
|
+
if (extras.length > 0)
|
|
112
|
+
resource.extraProperties = extras;
|
|
113
|
+
return resource;
|
|
114
|
+
}
|
|
115
|
+
// ---- DynamoDB helpers ----
|
|
116
|
+
/** Parse a DynamoDB key schema from parsed AST object */
|
|
117
|
+
function parseDynamoDBKey(value) {
|
|
118
|
+
if (!isParsedObject(value))
|
|
119
|
+
return undefined;
|
|
120
|
+
const name = asString(value.name);
|
|
121
|
+
const type = asStringUnion(value.type, DYNAMODB_KEY_TYPES);
|
|
122
|
+
if (!name || !type)
|
|
123
|
+
return undefined;
|
|
124
|
+
return { name, type };
|
|
125
|
+
}
|
|
126
|
+
/** Extract DynamoDB-specific fields from a parsed database resource */
|
|
127
|
+
function extractDynamoDBFields(resource) {
|
|
128
|
+
const node = resource.node;
|
|
129
|
+
if (!ts.isCallExpression(node))
|
|
130
|
+
return null;
|
|
131
|
+
const buildCallArg = node.arguments[0];
|
|
132
|
+
if (!ts.isCallExpression(buildCallArg) || buildCallArg.arguments.length < 2)
|
|
133
|
+
return null;
|
|
134
|
+
const configArg = buildCallArg.arguments[1];
|
|
135
|
+
if (!ts.isObjectLiteralExpression(configArg))
|
|
136
|
+
return null;
|
|
137
|
+
const config = parseObjectLiteral(configArg);
|
|
138
|
+
const partitionKey = parseDynamoDBKey(config.partitionKey);
|
|
139
|
+
if (!partitionKey)
|
|
140
|
+
return null;
|
|
141
|
+
const result = {
|
|
142
|
+
resourceName: resource.resourceName,
|
|
143
|
+
partitionKey,
|
|
144
|
+
node: resource.node,
|
|
145
|
+
};
|
|
146
|
+
if (resource.variableName)
|
|
147
|
+
result.variableName = resource.variableName;
|
|
148
|
+
const sortKey = parseDynamoDBKey(config.sortKey);
|
|
149
|
+
if (sortKey)
|
|
150
|
+
result.sortKey = sortKey;
|
|
151
|
+
if (Array.isArray(config.globalSecondaryIndexes)) {
|
|
152
|
+
const gsis = config.globalSecondaryIndexes
|
|
153
|
+
.filter(isParsedObject)
|
|
154
|
+
.map((gsi) => {
|
|
155
|
+
const pk = parseDynamoDBKey(gsi.partitionKey);
|
|
156
|
+
if (!pk)
|
|
157
|
+
return null;
|
|
158
|
+
const indexName = asString(gsi.indexName);
|
|
159
|
+
if (!indexName)
|
|
160
|
+
return null;
|
|
161
|
+
const entry = { indexName, partitionKey: pk };
|
|
162
|
+
const sk = parseDynamoDBKey(gsi.sortKey);
|
|
163
|
+
if (sk)
|
|
164
|
+
entry.sortKey = sk;
|
|
165
|
+
return entry;
|
|
166
|
+
})
|
|
167
|
+
.filter((g) => g !== null);
|
|
168
|
+
if (gsis.length > 0)
|
|
169
|
+
result.globalSecondaryIndexes = gsis;
|
|
170
|
+
}
|
|
171
|
+
const ttlAttribute = asString(config.ttlAttribute);
|
|
172
|
+
if (ttlAttribute)
|
|
173
|
+
result.ttlAttribute = ttlAttribute;
|
|
174
|
+
const stream = asBoolean(config.stream);
|
|
175
|
+
if (stream !== undefined)
|
|
176
|
+
result.stream = stream;
|
|
177
|
+
const DYNAMODB_KNOWN_KEYS = new Set([
|
|
178
|
+
"type",
|
|
179
|
+
"databaseName",
|
|
180
|
+
"partitionKey",
|
|
181
|
+
"sortKey",
|
|
182
|
+
"globalSecondaryIndexes",
|
|
183
|
+
"ttlAttribute",
|
|
184
|
+
"stream",
|
|
185
|
+
]);
|
|
186
|
+
const extras = captureExtraProperties(config, DYNAMODB_KNOWN_KEYS);
|
|
187
|
+
if (extras.length > 0)
|
|
188
|
+
result.extraProperties = extras;
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
191
|
+
// ---- Public API ----
|
|
192
|
+
/** Find all database resources (including DynamoDB) from addDatabase calls */
|
|
193
|
+
export function findDatabaseResources(sourceFile) {
|
|
194
|
+
return collectFromAst(sourceFile, (node) => {
|
|
195
|
+
if (!ts.isCallExpression(node) || !isFactoryMethodCall(node, "addDatabase"))
|
|
196
|
+
return null;
|
|
197
|
+
const databaseArg = node.arguments[0];
|
|
198
|
+
if (!isFactoryBuildCall(databaseArg, "DatabaseFactory"))
|
|
199
|
+
return null;
|
|
200
|
+
return extractDatabaseResource(sourceFile, node, databaseArg);
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
/** Split DynamoDB resources (type: "DynamoDB") from regular database resources */
|
|
204
|
+
export function splitDynamoDBFromDatabases(allResources) {
|
|
205
|
+
const databases = [];
|
|
206
|
+
const dynamodb = [];
|
|
207
|
+
for (const resource of allResources) {
|
|
208
|
+
if (resource.type === "DynamoDB") {
|
|
209
|
+
const dynamo = extractDynamoDBFields(resource);
|
|
210
|
+
if (dynamo)
|
|
211
|
+
dynamodb.push(dynamo);
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
databases.push(resource);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return { databases, dynamodb };
|
|
218
|
+
}
|
|
219
|
+
const VALID_DB_TYPES = ["Instance", "Aurora", "GlobalAurora"];
|
|
220
|
+
/** Convert parsed database resources to plan format */
|
|
221
|
+
export function convertDatabaseResources(databaseResources) {
|
|
222
|
+
return databaseResources
|
|
223
|
+
.filter((db) => db.type !== "DynamoDB")
|
|
224
|
+
.map((database) => ({
|
|
225
|
+
name: database.resourceName,
|
|
226
|
+
type: constIncludes(VALID_DB_TYPES, database.type)
|
|
227
|
+
? database.type
|
|
228
|
+
: "Instance",
|
|
229
|
+
databaseName: database.databaseName,
|
|
230
|
+
...omitUndefined({
|
|
231
|
+
port: database.port,
|
|
232
|
+
deletionProtection: database.deletionProtection,
|
|
233
|
+
instanceType: database.instanceType,
|
|
234
|
+
multiAz: database.multiAz,
|
|
235
|
+
publiclyAccessible: database.publiclyAccessible,
|
|
236
|
+
enableSecretRotation: database.enableSecretRotation,
|
|
237
|
+
encryption: database.encryption,
|
|
238
|
+
databaseInsights: database.databaseInsights,
|
|
239
|
+
proxy: database.proxy,
|
|
240
|
+
readReplica: database.readReplica,
|
|
241
|
+
credentials: database.credentials,
|
|
242
|
+
writer: database.writer,
|
|
243
|
+
readers: database.readers,
|
|
244
|
+
backupRetention: database.backupRetention,
|
|
245
|
+
preferredMaintenanceWindow: database.preferredMaintenanceWindow,
|
|
246
|
+
primaryRegion: database.primaryRegion,
|
|
247
|
+
secondaryRegions: database.secondaryRegions,
|
|
248
|
+
globalClusterIdentifier: database.globalClusterIdentifier,
|
|
249
|
+
enableGlobalWriteForwarding: database.enableGlobalWriteForwarding,
|
|
250
|
+
allocatedStorage: database.allocatedStorage,
|
|
251
|
+
monitoringInterval: database.monitoringInterval,
|
|
252
|
+
variableName: database.variableName,
|
|
253
|
+
databaseEngine: database.databaseEngine,
|
|
254
|
+
engineExpression: database.engineExpression,
|
|
255
|
+
extraProperties: database.extraProperties,
|
|
256
|
+
}),
|
|
257
|
+
}));
|
|
258
|
+
}
|
|
259
|
+
/** Convert parsed DynamoDB resources to plan format */
|
|
260
|
+
export function convertDynamoDBResources(dynamodbResources) {
|
|
261
|
+
if (dynamodbResources.length === 0)
|
|
262
|
+
return undefined;
|
|
263
|
+
return dynamodbResources.map((table) => ({
|
|
264
|
+
name: table.resourceName,
|
|
265
|
+
partitionKey: table.partitionKey,
|
|
266
|
+
...omitUndefined({
|
|
267
|
+
sortKey: table.sortKey,
|
|
268
|
+
globalSecondaryIndexes: table.globalSecondaryIndexes,
|
|
269
|
+
ttlAttribute: table.ttlAttribute,
|
|
270
|
+
stream: table.stream,
|
|
271
|
+
variableName: table.variableName,
|
|
272
|
+
extraProperties: table.extraProperties,
|
|
273
|
+
}),
|
|
274
|
+
}));
|
|
275
|
+
}
|