@fjall/generator 0.88.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.
Files changed (57) hide show
  1. package/dist/src/ast/astComputeParser.d.ts +4 -0
  2. package/dist/src/ast/astComputeParser.js +427 -0
  3. package/dist/src/ast/astInfrastructureParser.d.ts +357 -0
  4. package/dist/src/ast/astInfrastructureParser.js +1925 -0
  5. package/dist/src/ast/astSurgicalModification.d.ts +47 -0
  6. package/dist/src/ast/astSurgicalModification.js +400 -0
  7. package/dist/src/ast/index.d.ts +2 -0
  8. package/dist/src/ast/index.js +2 -0
  9. package/dist/src/aws/regions.d.ts +30 -0
  10. package/dist/src/aws/regions.js +254 -0
  11. package/dist/src/generation/common.d.ts +86 -0
  12. package/dist/src/generation/common.js +187 -0
  13. package/dist/src/generation/compute.d.ts +6 -0
  14. package/dist/src/generation/compute.js +547 -0
  15. package/dist/src/generation/database.d.ts +54 -0
  16. package/dist/src/generation/database.js +201 -0
  17. package/dist/src/generation/index.d.ts +12 -0
  18. package/dist/src/generation/index.js +18 -0
  19. package/dist/src/generation/infrastructure.d.ts +44 -0
  20. package/dist/src/generation/infrastructure.js +389 -0
  21. package/dist/src/generation/storage.d.ts +23 -0
  22. package/dist/src/generation/storage.js +174 -0
  23. package/dist/src/generation/storageConnections.d.ts +37 -0
  24. package/dist/src/generation/storageConnections.js +71 -0
  25. package/dist/src/index.d.ts +10 -0
  26. package/dist/src/index.js +19 -0
  27. package/dist/src/planning/index.d.ts +1 -0
  28. package/dist/src/planning/index.js +1 -0
  29. package/dist/src/planning/resourcePlanning.d.ts +58 -0
  30. package/dist/src/planning/resourcePlanning.js +216 -0
  31. package/dist/src/presets/index.d.ts +3 -0
  32. package/dist/src/presets/index.js +3 -0
  33. package/dist/src/presets/patternTierPresets.d.ts +93 -0
  34. package/dist/src/presets/patternTierPresets.js +131 -0
  35. package/dist/src/presets/storagePresets.d.ts +11 -0
  36. package/dist/src/presets/storagePresets.js +36 -0
  37. package/dist/src/presets/tierPresets.d.ts +59 -0
  38. package/dist/src/presets/tierPresets.js +384 -0
  39. package/dist/src/presets/tierTypes.d.ts +301 -0
  40. package/dist/src/presets/tierTypes.js +7 -0
  41. package/dist/src/schemas/constants.d.ts +74 -0
  42. package/dist/src/schemas/constants.js +208 -0
  43. package/dist/src/schemas/index.d.ts +3 -0
  44. package/dist/src/schemas/index.js +3 -0
  45. package/dist/src/schemas/instanceTypeArchitecture.d.ts +35 -0
  46. package/dist/src/schemas/instanceTypeArchitecture.js +75 -0
  47. package/dist/src/schemas/resourceSchemas.d.ts +3534 -0
  48. package/dist/src/schemas/resourceSchemas.js +2015 -0
  49. package/dist/src/types/Result.d.ts +19 -0
  50. package/dist/src/types/Result.js +31 -0
  51. package/dist/src/util/errorUtils.d.ts +2 -0
  52. package/dist/src/util/errorUtils.js +15 -0
  53. package/dist/src/validation/patterns.d.ts +300 -0
  54. package/dist/src/validation/patterns.js +360 -0
  55. package/dist/src/version.d.ts +1 -0
  56. package/dist/src/version.js +1 -0
  57. package/package.json +32 -0
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Storage Code Generation
3
+ *
4
+ * Functions for generating storage infrastructure code including
5
+ * S3 buckets, DynamoDB tables, and SQS queues.
6
+ */
7
+ import { buildProperty, getVariableName, emitExtraProperties, } from "./common.js";
8
+ /**
9
+ * Generate S3 bucket configuration properties using buildProperty for consistent formatting
10
+ */
11
+ function generateS3BucketProps(bucket) {
12
+ let code = "";
13
+ code += buildProperty(bucket.bucketName !== undefined, "bucketName", bucket.bucketName, "string");
14
+ code += buildProperty(bucket.stackPlacement !== undefined && bucket.stackPlacement !== "storage", "stackPlacement", bucket.stackPlacement, "string");
15
+ code += buildProperty(bucket.publicReadAccess === true, "publicReadAccess", true);
16
+ code += buildProperty(bucket.websiteHosting !== undefined, "websiteHosting", bucket.websiteHosting, "object");
17
+ code += buildProperty(bucket.backupVaultTier !== undefined, "backupVaultTier", bucket.backupVaultTier, "string");
18
+ code += buildProperty(bucket.versioned !== undefined, "versioned", bucket.versioned);
19
+ code += buildProperty(bucket.encryption !== undefined, "encryption", bucket.encryption, "string");
20
+ code += buildProperty(bucket.kmsKeyArn !== undefined, "kmsKeyArn", bucket.kmsKeyArn, "string");
21
+ code += buildProperty(bucket.cors !== undefined && bucket.cors.length > 0, "cors", bucket.cors, "object");
22
+ code += buildProperty(bucket.deployment !== undefined, "deployment", bucket.deployment, "object");
23
+ return code;
24
+ }
25
+ /**
26
+ * Check if an S3 bucket needs a variable assignment (referenced by compute or CDN)
27
+ */
28
+ export function s3NeedsVariable(bucket, plan) {
29
+ // Referenced by compute via connectedStorage
30
+ const computeRef = plan.compute.some((compute) => compute.connectedStorage?.includes(bucket.name));
31
+ if (computeRef)
32
+ return true;
33
+ // Referenced by CDN as an origin
34
+ if (plan.cdn) {
35
+ if (plan.cdn.defaultOriginRef === bucket.name)
36
+ return true;
37
+ if (plan.cdn.behaviours?.some((b) => b.originRef === bucket.name))
38
+ return true;
39
+ }
40
+ return false;
41
+ }
42
+ /**
43
+ * Generate S3 bucket infrastructure code
44
+ */
45
+ export function generateS3Code(plan) {
46
+ if (plan.s3.length === 0)
47
+ return "";
48
+ let code = "";
49
+ for (let i = 0; i < plan.s3.length; i++) {
50
+ const bucket = plan.s3[i];
51
+ const bucketVariable = getVariableName(bucket);
52
+ const hasReferences = s3NeedsVariable(bucket, plan);
53
+ const needsLeadingNewline = plan.database.length > 0 || i > 0;
54
+ const leadingNewline = needsLeadingNewline ? "\n" : "";
55
+ const props = generateS3BucketProps(bucket);
56
+ const prefix = hasReferences ? `const ${bucketVariable} = ` : "";
57
+ const extraPropsStr = emitExtraProperties(bucket.extraProperties);
58
+ if (props || extraPropsStr) {
59
+ code += `${leadingNewline}${prefix}app.addStorage(
60
+ StorageFactory.build("${bucket.name}", {${props}${extraPropsStr}
61
+ })
62
+ );
63
+ `;
64
+ }
65
+ else {
66
+ code += `${leadingNewline}${prefix}app.addStorage(
67
+ StorageFactory.build("${bucket.name}", {})
68
+ );
69
+ `;
70
+ }
71
+ }
72
+ return code;
73
+ }
74
+ /**
75
+ * Generate DynamoDB table infrastructure code
76
+ */
77
+ export function generateDynamoDBCode(plan) {
78
+ if (!plan.dynamodb || plan.dynamodb.length === 0)
79
+ return "";
80
+ let code = "";
81
+ for (const table of plan.dynamodb) {
82
+ const tableVariable = getVariableName(table);
83
+ code += `
84
+ const ${tableVariable} = app.addDatabase(
85
+ DatabaseFactory.build("${table.name}", {
86
+ type: "DynamoDB",
87
+ partitionKey: {
88
+ name: "${table.partitionKey.name}",
89
+ type: "${table.partitionKey.type}",
90
+ }`;
91
+ if (table.sortKey) {
92
+ code += `,
93
+ sortKey: {
94
+ name: "${table.sortKey.name}",
95
+ type: "${table.sortKey.type}",
96
+ }`;
97
+ }
98
+ if (table.globalSecondaryIndexes &&
99
+ table.globalSecondaryIndexes.length > 0) {
100
+ const gsiStr = table.globalSecondaryIndexes
101
+ .map((gsi) => {
102
+ let gsiCode = `{
103
+ indexName: "${gsi.indexName}",
104
+ partitionKey: {
105
+ name: "${gsi.partitionKey.name}",
106
+ type: "${gsi.partitionKey.type}",
107
+ },`;
108
+ if (gsi.sortKey) {
109
+ gsiCode += `
110
+ sortKey: {
111
+ name: "${gsi.sortKey.name}",
112
+ type: "${gsi.sortKey.type}",
113
+ },`;
114
+ }
115
+ gsiCode += `
116
+ }`;
117
+ return gsiCode;
118
+ })
119
+ .join(",\n ");
120
+ code += `,
121
+ globalSecondaryIndexes: [
122
+ ${gsiStr}
123
+ ]`;
124
+ }
125
+ if (table.ttlAttribute) {
126
+ code += `,
127
+ ttlAttribute: "${table.ttlAttribute}"`;
128
+ }
129
+ if (table.stream) {
130
+ code += `,
131
+ stream: true`;
132
+ }
133
+ code += emitExtraProperties(table.extraProperties);
134
+ code += `
135
+ })
136
+ );
137
+ `;
138
+ }
139
+ return code;
140
+ }
141
+ /**
142
+ * Generate SQS queue infrastructure code
143
+ */
144
+ export function generateSQSCode(plan) {
145
+ if (!plan.sqs || plan.sqs.length === 0)
146
+ return "";
147
+ let code = "";
148
+ for (const queue of plan.sqs) {
149
+ const queueVariable = getVariableName(queue);
150
+ code += `
151
+ const ${queueVariable} = app.addMessaging(
152
+ MessagingFactory.build("${queue.name}", {
153
+ type: "queue",
154
+ queueType: "${queue.queueType}"`;
155
+ if (queue.visibilityTimeout !== undefined) {
156
+ code += `,
157
+ visibilityTimeout: ${queue.visibilityTimeout}`;
158
+ }
159
+ if (queue.retentionPeriod !== undefined) {
160
+ code += `,
161
+ messageRetentionPeriod: ${queue.retentionPeriod}`;
162
+ }
163
+ if (queue.contentBasedDeduplication) {
164
+ code += `,
165
+ contentBasedDeduplication: true`;
166
+ }
167
+ code += emitExtraProperties(queue.extraProperties);
168
+ code += `
169
+ })
170
+ );
171
+ `;
172
+ }
173
+ return code;
174
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Storage Connection Code Generation
3
+ *
4
+ * Functions for generating storage connection code (env vars, connections array)
5
+ * following the same pattern as database connections.
6
+ */
7
+ import type { S3ResourcePlan, ApplicationResourcePlan, ComputeResourcePlan, DatabaseResourcePlan } from "../schemas/resourceSchemas.js";
8
+ export declare const STORAGE_ENV_VARS: Readonly<{
9
+ readonly NAME: "BUCKET_NAME";
10
+ }>;
11
+ export type StorageEnvVarEntry = {
12
+ key: string;
13
+ expression: string;
14
+ };
15
+ export interface StorageEnvVars {
16
+ env: Record<string, unknown>;
17
+ secrets: Record<string, unknown>;
18
+ }
19
+ /**
20
+ * Get S3 buckets connected to a compute resource
21
+ */
22
+ export declare function getConnectedStorage(plan: ApplicationResourcePlan, compute: ComputeResourcePlan): S3ResourcePlan[];
23
+ /**
24
+ * Generate storage environment variable entries for a list of connected buckets.
25
+ * S3 uses IAM-based access, so there are no secrets — only BUCKET_NAME env vars.
26
+ */
27
+ export declare function generateStorageEnvVarEntries(connectedStorage: S3ResourcePlan[]): StorageEnvVarEntry[];
28
+ /**
29
+ * Build storage environment variables for a compute resource.
30
+ * S3 always uses IAM — secrets is always empty.
31
+ */
32
+ export declare function buildStorageEnvVars(plan: ApplicationResourcePlan, compute: ComputeResourcePlan): StorageEnvVars;
33
+ /**
34
+ * Format combined connections array for generated infrastructure code.
35
+ * Merges both database and storage variable names into a single connections line.
36
+ */
37
+ export declare function formatAllConnectionsCode(connectedDatabases: DatabaseResourcePlan[], connectedStorage: S3ResourcePlan[]): string;
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Storage Connection Code Generation
3
+ *
4
+ * Functions for generating storage connection code (env vars, connections array)
5
+ * following the same pattern as database connections.
6
+ */
7
+ import { getVariableName } from "./common.js";
8
+ export const STORAGE_ENV_VARS = Object.freeze({
9
+ NAME: "BUCKET_NAME",
10
+ });
11
+ /**
12
+ * Get S3 buckets connected to a compute resource
13
+ */
14
+ export function getConnectedStorage(plan, compute) {
15
+ const { connectedStorage } = compute;
16
+ if (!connectedStorage?.length)
17
+ return [];
18
+ return plan.s3.filter((bucket) => connectedStorage.includes(bucket.name));
19
+ }
20
+ /**
21
+ * Generate storage environment variable entries for a list of connected buckets.
22
+ * S3 uses IAM-based access, so there are no secrets — only BUCKET_NAME env vars.
23
+ */
24
+ export function generateStorageEnvVarEntries(connectedStorage) {
25
+ const entries = [];
26
+ if (connectedStorage.length === 1) {
27
+ const bucketVar = getVariableName(connectedStorage[0]);
28
+ entries.push({
29
+ key: STORAGE_ENV_VARS.NAME,
30
+ expression: `${bucketVar}.getBucketName()`,
31
+ });
32
+ }
33
+ else {
34
+ for (const [index, bucket] of connectedStorage.entries()) {
35
+ const bucketVar = getVariableName(bucket);
36
+ const suffix = index === 0 ? "" : `_${index + 1}`;
37
+ entries.push({
38
+ key: `${STORAGE_ENV_VARS.NAME}${suffix}`,
39
+ expression: `${bucketVar}.getBucketName()`,
40
+ });
41
+ }
42
+ }
43
+ return entries;
44
+ }
45
+ /**
46
+ * Build storage environment variables for a compute resource.
47
+ * S3 always uses IAM — secrets is always empty.
48
+ */
49
+ export function buildStorageEnvVars(plan, compute) {
50
+ const env = {};
51
+ const secrets = {};
52
+ if (!compute.connectedStorage?.length) {
53
+ return { env, secrets };
54
+ }
55
+ const connectedStorage = getConnectedStorage(plan, compute);
56
+ const entries = generateStorageEnvVarEntries(connectedStorage);
57
+ for (const entry of entries) {
58
+ env[entry.key] = { __expression: entry.expression };
59
+ }
60
+ return { env, secrets };
61
+ }
62
+ /**
63
+ * Format combined connections array for generated infrastructure code.
64
+ * Merges both database and storage variable names into a single connections line.
65
+ */
66
+ export function formatAllConnectionsCode(connectedDatabases, connectedStorage) {
67
+ const dbVars = connectedDatabases.map((db) => getVariableName(db));
68
+ const storageVars = connectedStorage.map((b) => getVariableName(b));
69
+ const allVars = [...dbVars, ...storageVars].join(", ");
70
+ return `connections: [${allVars}],`;
71
+ }
@@ -0,0 +1,10 @@
1
+ export { normaliseError, getErrorMessage } from "./util/errorUtils.js";
2
+ export { type Result, isSuccess, isFailure, success, failure, tryAsync, trySync, } from "./types/Result.js";
3
+ export { VALIDATION_PATTERNS, VALIDATION_MESSAGES, type ValidationPatternKey, type ValidationMessageKey, } from "./validation/patterns.js";
4
+ export { type RegionInfo, DEFAULT_REGION, regions, AWS_REGIONS_METADATA, topRegions, commonRegions, parseRegionList, isValidRegion, isValidRegionFormat, getSuggestions, validateRegion, validateRegionList, filterDuplicateRegions, getRegionOptions, getRegionOptionsExcluding, getRegionName, createRegionFormatter, } from "./aws/regions.js";
5
+ export { GENERATOR_VERSION } from "./version.js";
6
+ export * from "./schemas/index.js";
7
+ export * from "./generation/index.js";
8
+ export * from "./presets/index.js";
9
+ export * from "./ast/index.js";
10
+ export * from "./planning/index.js";
@@ -0,0 +1,19 @@
1
+ // Utility foundation
2
+ export { normaliseError, getErrorMessage } from "./util/errorUtils.js";
3
+ export { isSuccess, isFailure, success, failure, tryAsync, trySync, } from "./types/Result.js";
4
+ // Validation
5
+ export { VALIDATION_PATTERNS, VALIDATION_MESSAGES, } from "./validation/patterns.js";
6
+ // AWS
7
+ export { DEFAULT_REGION, regions, AWS_REGIONS_METADATA, topRegions, commonRegions, parseRegionList, isValidRegion, isValidRegionFormat, getSuggestions, validateRegion, validateRegionList, filterDuplicateRegions, getRegionOptions, getRegionOptionsExcluding, getRegionName, createRegionFormatter, } from "./aws/regions.js";
8
+ // Version
9
+ export { GENERATOR_VERSION } from "./version.js";
10
+ // Schemas, plan types, constants
11
+ export * from "./schemas/index.js";
12
+ // Code generation
13
+ export * from "./generation/index.js";
14
+ // Tier, pattern, and storage presets
15
+ export * from "./presets/index.js";
16
+ // AST parsing & surgical modification
17
+ export * from "./ast/index.js";
18
+ // Resource planning
19
+ export * from "./planning/index.js";
@@ -0,0 +1 @@
1
+ export { planApplicationResources, type OpenNextResourceOptions, planOpenNextResources, type GenerationOptions, generateInfrastructureFromPlan, } from "./resourcePlanning.js";
@@ -0,0 +1 @@
1
+ export { planApplicationResources, planOpenNextResources, generateInfrastructureFromPlan, } from "./resourcePlanning.js";
@@ -0,0 +1,58 @@
1
+ import { type UserServiceConfig, type AppType } from "../presets/tierPresets.js";
2
+ import { type PatternTypeWithCustom } from "../presets/patternTierPresets.js";
3
+ import { type PatternType } from "../schemas/constants.js";
4
+ import { type Result } from "../types/Result.js";
5
+ import type { ApplicationResourcePlan } from "../schemas/resourceSchemas.js";
6
+ export declare function planApplicationResources(appName: string, appType: AppType, includeDatabase?: boolean, services?: UserServiceConfig[], snapshotOptions?: {
7
+ snapshotIdentifier?: string;
8
+ snapshotUsername?: string;
9
+ }): ApplicationResourcePlan;
10
+ export interface OpenNextResourceOptions {
11
+ /** Pattern tier: lightweight, standard, resilient, or custom */
12
+ tier?: PatternTypeWithCustom;
13
+ /** Custom domain (auto-creates certificate + DNS) */
14
+ domain?: string;
15
+ /** Database configuration (uses tier defaults if not specified) */
16
+ database?: {
17
+ type?: "Instance" | "Aurora";
18
+ instanceType?: string;
19
+ databaseName?: string;
20
+ publiclyAccessible?: boolean;
21
+ allowedIpCidr?: string;
22
+ backupRetention?: number;
23
+ deletionProtection?: boolean;
24
+ encryption?: {
25
+ useCMK: true;
26
+ } | false;
27
+ };
28
+ /** Compute (Lambda) configuration (uses tier defaults if not specified) */
29
+ compute?: {
30
+ memorySize?: number;
31
+ timeout?: number;
32
+ };
33
+ /** CDN configuration (advanced - prefer `domain` for simple setup) */
34
+ cdn?: {
35
+ domainNames?: string[];
36
+ certificateArn?: string;
37
+ };
38
+ /** Additional environment variables */
39
+ environment?: Record<string, string>;
40
+ /** Resource tags */
41
+ tags?: Record<string, string>;
42
+ }
43
+ /**
44
+ * Plan resources for OpenNext patterns (Next.js/Payload).
45
+ *
46
+ * All OpenNext patterns use the PatternFactory approach, generating a single
47
+ * `PatternFactory.build()` call. The pattern class internally handles all
48
+ * infrastructure (database, S3, DynamoDB, SQS, Lambdas, CDN).
49
+ *
50
+ * @example
51
+ * const plan = planOpenNextResources("my-cms", "payload", { tier: "standard" });
52
+ */
53
+ export declare function planOpenNextResources(appName: string, pattern: PatternType, options?: OpenNextResourceOptions): Result<ApplicationResourcePlan, Error>;
54
+ export interface GenerationOptions {
55
+ /** Resource name mapping from old names to new names (for custom code repositioning) */
56
+ resourceMapping?: Map<string, string>;
57
+ }
58
+ export declare function generateInfrastructureFromPlan(plan: ApplicationResourcePlan, options?: GenerationOptions): Result<string, Error>;
@@ -0,0 +1,216 @@
1
+ import { TIER_PRESETS, getDefaultDatabaseTypeForTier, getNetworkPreset, applyTierDefaultsToServices, } from "../presets/tierPresets.js";
2
+ import { getPatternTierPreset, isPatternTierName, } from "../presets/patternTierPresets.js";
3
+ import { COMPUTE_TYPE } from "../schemas/constants.js";
4
+ import { success, failure } from "../types/Result.js";
5
+ import { PatternConfigSchema } from "../schemas/resourceSchemas.js";
6
+ import { injectCustomCodeBlocks } from "../ast/astSurgicalModification.js";
7
+ import { toVariableName, toValidDatabaseName, generateImports, generateAppInit, generateTags, generateNetworkCode, generateDatabaseCode, generateS3Code, generateDynamoDBCode, generateSQSCode, generateComputeCode, generateCDNCode, collectCdnReferencedResources, usesPatternApproach, generatePatternCodeWithComments, } from "../generation/index.js";
8
+ const DEFAULT_NETWORK = getNetworkPreset("standard");
9
+ /** Type guard that narrows a pattern string to a supported PatternConfig type via Zod schema. */
10
+ function isPatternConfigType(value) {
11
+ return PatternConfigSchema.options.some((schema) => schema.shape?.type?.value === value);
12
+ }
13
+ export function planApplicationResources(appName, appType, includeDatabase = true, services, snapshotOptions) {
14
+ const basePlan = {
15
+ appName,
16
+ type: appType,
17
+ database: [],
18
+ s3: [],
19
+ compute: [],
20
+ importedResources: [],
21
+ };
22
+ if (appType === "custom") {
23
+ return basePlan;
24
+ }
25
+ const tier = appType;
26
+ const tierPreset = TIER_PRESETS[tier];
27
+ return {
28
+ ...basePlan,
29
+ database: buildDatabasePlan(appName, tierPreset, tier, includeDatabase, snapshotOptions),
30
+ compute: buildComputePlan(appName, tierPreset, tier, includeDatabase, services),
31
+ network: tierPreset.network,
32
+ backup: tierPreset.backup,
33
+ };
34
+ }
35
+ function buildDatabasePlan(appName, tierPreset, tier, includeDatabase, snapshotOptions) {
36
+ const databaseType = getDefaultDatabaseTypeForTier(tier);
37
+ const databasePreset = tierPreset.database[databaseType];
38
+ if (!includeDatabase || databasePreset === null)
39
+ return [];
40
+ return [
41
+ {
42
+ name: appName,
43
+ type: databaseType,
44
+ databaseName: toValidDatabaseName(appName),
45
+ variableName: `${toVariableName(appName)}Database`,
46
+ ...databasePreset,
47
+ ...(snapshotOptions?.snapshotIdentifier !== undefined && {
48
+ snapshotIdentifier: snapshotOptions.snapshotIdentifier,
49
+ }),
50
+ ...(snapshotOptions?.snapshotUsername !== undefined && {
51
+ snapshotUsername: snapshotOptions.snapshotUsername,
52
+ }),
53
+ },
54
+ ];
55
+ }
56
+ function buildScalingConfig(minCapacity, maxCapacity) {
57
+ if (minCapacity === undefined && maxCapacity === undefined)
58
+ return undefined;
59
+ return {
60
+ scaling: {
61
+ ...(minCapacity !== undefined && { minCapacity }),
62
+ ...(maxCapacity !== undefined && { maxCapacity }),
63
+ },
64
+ };
65
+ }
66
+ function buildServiceEntry(tierService, userService) {
67
+ const { minCapacity, maxCapacity, ...rest } = tierService;
68
+ const containerPort = userService?.containerPort ?? 3000;
69
+ return {
70
+ ...rest,
71
+ dockerfilePath: userService?.dockerfilePath,
72
+ needsDatabaseConnection: userService?.needsDatabaseConnection,
73
+ containers: [{ port: containerPort }],
74
+ ...buildScalingConfig(minCapacity, maxCapacity),
75
+ };
76
+ }
77
+ function buildComputePlan(appName, tierPreset, tier, includeDatabase, services) {
78
+ const ecsPreset = tierPreset.compute.ecs;
79
+ const tierServices = services && services.length > 0
80
+ ? applyTierDefaultsToServices(tier, services)
81
+ : ecsPreset.services.map((s, i) => ({
82
+ ...s,
83
+ name: i === 0 ? "api" : `service-${i + 1}`,
84
+ }));
85
+ return [
86
+ {
87
+ name: appName,
88
+ type: COMPUTE_TYPE.ECS,
89
+ needsConnection: includeDatabase,
90
+ connectedDatabase: includeDatabase ? [appName] : undefined,
91
+ variableName: `${toVariableName(appName)}Compute`,
92
+ cluster: ecsPreset.cluster,
93
+ services: tierServices.map((tierService, index) => buildServiceEntry(tierService, services?.[index])),
94
+ },
95
+ ];
96
+ }
97
+ /**
98
+ * Plan resources for OpenNext patterns (Next.js/Payload).
99
+ *
100
+ * All OpenNext patterns use the PatternFactory approach, generating a single
101
+ * `PatternFactory.build()` call. The pattern class internally handles all
102
+ * infrastructure (database, S3, DynamoDB, SQS, Lambdas, CDN).
103
+ *
104
+ * @example
105
+ * const plan = planOpenNextResources("my-cms", "payload", { tier: "standard" });
106
+ */
107
+ export function planOpenNextResources(appName, pattern, options) {
108
+ const { tier = "standard", domain, database, compute, cdn, environment, tags, } = options ?? {};
109
+ const validTier = isPatternTierName(tier);
110
+ if (!validTier && tier !== "custom") {
111
+ return failure(new Error(`Invalid pattern tier: "${tier}". Valid tiers are: lightweight, standard, resilient, custom`));
112
+ }
113
+ if (!isPatternConfigType(pattern)) {
114
+ return failure(new Error(`Unsupported pattern type: ${pattern}`));
115
+ }
116
+ const tierPreset = validTier ? getPatternTierPreset(tier) : null;
117
+ const patternConfig = buildPatternConfig(pattern, appName, {
118
+ domain,
119
+ database,
120
+ compute,
121
+ cdn,
122
+ environment,
123
+ tierPreset,
124
+ });
125
+ if (patternConfig instanceof Error) {
126
+ return failure(patternConfig);
127
+ }
128
+ return success({
129
+ appName,
130
+ type: "standard",
131
+ pattern,
132
+ network: tierPreset ? tierPreset.network : DEFAULT_NETWORK,
133
+ backup: tierPreset ? tierPreset.backup : false,
134
+ patternConfig,
135
+ database: [],
136
+ s3: [],
137
+ compute: [],
138
+ tags: tags ?? {},
139
+ importedResources: [],
140
+ });
141
+ }
142
+ function mergeDatabaseConfig(database, tierPreset) {
143
+ return {
144
+ type: database?.type ?? tierPreset.database.type,
145
+ instanceType: database?.instanceType ?? tierPreset.database.instanceType,
146
+ databaseName: database?.databaseName,
147
+ publiclyAccessible: database?.publiclyAccessible,
148
+ allowedIpCidr: database?.allowedIpCidr,
149
+ backupRetention: database?.backupRetention ?? tierPreset.database.backupRetention,
150
+ deletionProtection: database?.deletionProtection ?? tierPreset.database.deletionProtection,
151
+ encryption: database?.encryption,
152
+ };
153
+ }
154
+ function mergeComputeConfig(compute, tierPreset) {
155
+ return {
156
+ memorySize: compute?.memorySize ?? tierPreset.compute.memorySize,
157
+ timeout: compute?.timeout ?? tierPreset.compute.timeout,
158
+ };
159
+ }
160
+ function toPatternDatabaseConfig(db) {
161
+ const { encryption, ...rest } = db;
162
+ return {
163
+ ...rest,
164
+ ...(encryption && { encryption: { storageKey: encryption } }),
165
+ };
166
+ }
167
+ function buildPatternConfig(type, appName, opts) {
168
+ const { domain, database, compute, cdn, environment, tierPreset } = opts;
169
+ const mergedDatabase = tierPreset
170
+ ? mergeDatabaseConfig(database, tierPreset)
171
+ : database;
172
+ const mergedCompute = tierPreset
173
+ ? mergeComputeConfig(compute, tierPreset)
174
+ : compute;
175
+ const config = {
176
+ type,
177
+ name: appName,
178
+ domain,
179
+ database: mergedDatabase
180
+ ? toPatternDatabaseConfig(mergedDatabase)
181
+ : undefined,
182
+ compute: mergedCompute ? { server: mergedCompute } : undefined,
183
+ cdn: cdn
184
+ ? { domainNames: cdn.domainNames, certificateArn: cdn.certificateArn }
185
+ : undefined,
186
+ environment,
187
+ };
188
+ const result = PatternConfigSchema.safeParse(config);
189
+ if (!result.success) {
190
+ return new Error(`Invalid pattern config: ${result.error.message}`);
191
+ }
192
+ return result.data;
193
+ }
194
+ export function generateInfrastructureFromPlan(plan, options) {
195
+ const parts = [
196
+ generateImports(plan),
197
+ generateAppInit(plan),
198
+ generateTags(plan),
199
+ ];
200
+ if (usesPatternApproach(plan)) {
201
+ parts.push(generatePatternCodeWithComments(plan), generateNetworkCode(plan));
202
+ }
203
+ else {
204
+ const cdnReferencedResources = collectCdnReferencedResources(plan);
205
+ parts.push(generateNetworkCode(plan), generateDatabaseCode(plan), generateS3Code(plan), generateDynamoDBCode(plan), generateSQSCode(plan), generateComputeCode(plan, cdnReferencedResources), generateCDNCode(plan));
206
+ }
207
+ const code = parts.join("");
208
+ if (plan.customCodeBlocks && plan.customCodeBlocks.length > 0) {
209
+ const injectionResult = injectCustomCodeBlocks(code, plan.customCodeBlocks, options?.resourceMapping);
210
+ if (!injectionResult.success) {
211
+ return failure(new Error(`Custom code injection failed: ${injectionResult.error ?? "unknown error"}`));
212
+ }
213
+ return success(injectionResult.content);
214
+ }
215
+ return success(code);
216
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./tierPresets.js";
2
+ export { PATTERN_TIER_NAMES, type PatternTierName, PATTERN_TYPES_WITH_CUSTOM, type PatternTypeWithCustom, type PatternDatabasePreset, type PatternComputePreset, type PatternTierPreset, PATTERN_TIER_PRESETS, getPatternTierPreset, getPatternTierOptions, isPatternTierName, } from "./patternTierPresets.js";
3
+ export { type StoragePreset, STORAGE_PRESETS, getStoragePreset, getStoragePresetOptions, } from "./storagePresets.js";
@@ -0,0 +1,3 @@
1
+ export * from "./tierPresets.js";
2
+ export { PATTERN_TIER_NAMES, PATTERN_TYPES_WITH_CUSTOM, PATTERN_TIER_PRESETS, getPatternTierPreset, getPatternTierOptions, isPatternTierName, } from "./patternTierPresets.js";
3
+ export { STORAGE_PRESETS, getStoragePreset, getStoragePresetOptions, } from "./storagePresets.js";