@fjall/generator 0.88.4 → 0.89.2

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.
Files changed (73) hide show
  1. package/LICENSE +21 -0
  2. package/dist/src/ast/astCdnParser.d.ts +15 -0
  3. package/dist/src/ast/astCdnParser.js +114 -0
  4. package/dist/src/ast/astCommonParser.d.ts +90 -0
  5. package/dist/src/ast/astCommonParser.js +351 -0
  6. package/dist/src/ast/astComputeParser.d.ts +14 -2
  7. package/dist/src/ast/astComputeParser.js +55 -9
  8. package/dist/src/ast/astDatabaseParser.d.ts +104 -0
  9. package/dist/src/ast/astDatabaseParser.js +275 -0
  10. package/dist/src/ast/astInfrastructureParser.d.ts +23 -277
  11. package/dist/src/ast/astInfrastructureParser.js +83 -1456
  12. package/dist/src/ast/astMessagingParser.d.ts +25 -0
  13. package/dist/src/ast/astMessagingParser.js +78 -0
  14. package/dist/src/ast/astNetworkParser.d.ts +70 -0
  15. package/dist/src/ast/astNetworkParser.js +219 -0
  16. package/dist/src/ast/astPatternParser.d.ts +80 -0
  17. package/dist/src/ast/astPatternParser.js +155 -0
  18. package/dist/src/ast/astStorageParser.d.ts +18 -0
  19. package/dist/src/ast/astStorageParser.js +164 -0
  20. package/dist/src/ast/index.d.ts +1 -0
  21. package/dist/src/ast/index.js +4 -0
  22. package/dist/src/dns/bindParser.d.ts +13 -0
  23. package/dist/src/dns/bindParser.js +224 -0
  24. package/dist/src/dns/bindWriter.d.ts +2 -0
  25. package/dist/src/dns/bindWriter.js +52 -0
  26. package/dist/src/dns/index.d.ts +4 -0
  27. package/dist/src/dns/index.js +4 -0
  28. package/dist/src/dns/infrastructureWriter.d.ts +2 -0
  29. package/dist/src/dns/infrastructureWriter.js +58 -0
  30. package/dist/src/dns/types.d.ts +82 -0
  31. package/dist/src/dns/types.js +52 -0
  32. package/dist/src/generation/common.d.ts +1 -16
  33. package/dist/src/generation/common.js +2 -28
  34. package/dist/src/generation/compute.js +77 -28
  35. package/dist/src/generation/index.d.ts +2 -1
  36. package/dist/src/generation/index.js +3 -1
  37. package/dist/src/generation/messagingConnections.d.ts +33 -0
  38. package/dist/src/generation/messagingConnections.js +73 -0
  39. package/dist/src/generation/storage.d.ts +5 -1
  40. package/dist/src/generation/storage.js +9 -1
  41. package/dist/src/generation/storageConnections.d.ts +3 -3
  42. package/dist/src/generation/storageConnections.js +8 -4
  43. package/dist/src/index.d.ts +1 -0
  44. package/dist/src/index.js +2 -0
  45. package/dist/src/planning/resourcePlanning.js +0 -2
  46. package/dist/src/presets/tierTypes.d.ts +4 -1
  47. package/dist/src/schemas/applicationSchemas.d.ts +854 -0
  48. package/dist/src/schemas/applicationSchemas.js +80 -0
  49. package/dist/src/schemas/baseSchemas.d.ts +206 -0
  50. package/dist/src/schemas/baseSchemas.js +248 -0
  51. package/dist/src/schemas/cdnSchemas.d.ts +61 -0
  52. package/dist/src/schemas/cdnSchemas.js +62 -0
  53. package/dist/src/schemas/computeSchemas.d.ts +723 -0
  54. package/dist/src/schemas/computeSchemas.js +727 -0
  55. package/dist/src/schemas/constants.d.ts +12 -8
  56. package/dist/src/schemas/constants.js +14 -4
  57. package/dist/src/schemas/databaseSchemas.d.ts +638 -0
  58. package/dist/src/schemas/databaseSchemas.js +366 -0
  59. package/dist/src/schemas/messagingSchemas.d.ts +20 -0
  60. package/dist/src/schemas/messagingSchemas.js +29 -0
  61. package/dist/src/schemas/networkSchemas.d.ts +246 -0
  62. package/dist/src/schemas/networkSchemas.js +125 -0
  63. package/dist/src/schemas/patternSchemas.d.ts +708 -0
  64. package/dist/src/schemas/patternSchemas.js +294 -0
  65. package/dist/src/schemas/resourceSchemas.d.ts +24 -3530
  66. package/dist/src/schemas/resourceSchemas.js +24 -2011
  67. package/dist/src/schemas/storageSchemas.d.ts +93 -0
  68. package/dist/src/schemas/storageSchemas.js +119 -0
  69. package/dist/src/util/errorUtils.d.ts +1 -2
  70. package/dist/src/util/errorUtils.js +1 -15
  71. package/dist/src/validation/patterns.d.ts +9 -0
  72. package/dist/src/validation/patterns.js +9 -0
  73. package/package.json +4 -3
@@ -1,5 +1,43 @@
1
- import { isPlainObject, isParsedObject, isIdentifierRef, isExpressionRef, isCallRef, asString, asNumber, asBoolean, asStringArray, asStringUnion, captureExtraProperties, } from "./astInfrastructureParser.js";
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` "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` handle both keys
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` extract enum value from expression
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` plan stores as `description`
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` map to enableSSH boolean.
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(parsed, plan) {
412
- return parsed.computeResources.map((compute) => {
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, parsed.databaseResources, parsed.s3Resources);
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
+ }