@fjall/generator 0.95.0 → 0.99.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.minified +1 -1
- package/dist/src/ast/astClickHouseParser.d.ts +25 -0
- package/dist/src/ast/astClickHouseParser.js +1 -0
- package/dist/src/ast/astCodeInjection.d.ts +9 -0
- package/dist/src/ast/astCodeInjection.js +8 -0
- package/dist/src/ast/astComputeParser.js +1 -1
- package/dist/src/ast/astComputeParserHelpers.js +1 -1
- package/dist/src/ast/astDatabaseParser.d.ts +5 -2
- package/dist/src/ast/astDatabaseParser.js +1 -1
- package/dist/src/ast/astInfrastructureParser.d.ts +7 -1
- package/dist/src/ast/astInfrastructureParser.js +1 -1
- package/dist/src/ast/astPlanConverter.js +2 -2
- package/dist/src/ast/astScheduleParser.d.ts +18 -0
- package/dist/src/ast/astScheduleParser.js +1 -0
- package/dist/src/ast/astStatementClassifier.d.ts +2 -2
- package/dist/src/ast/astStatementClassifier.js +1 -1
- package/dist/src/ast/astStatementQueries.d.ts +4 -4
- package/dist/src/ast/astStatementQueries.js +3 -3
- package/dist/src/ast/astSurgicalModification.d.ts +14 -12
- package/dist/src/ast/astSurgicalModification.js +6 -13
- package/dist/src/ast/astTestHelpers.d.ts +41 -7
- package/dist/src/ast/index.d.ts +3 -2
- package/dist/src/ast/index.js +1 -1
- package/dist/src/codemod/_internal.d.ts +6 -1
- package/dist/src/codemod/_internal.js +1 -1
- package/dist/src/codemod/drift/detect.d.ts +11 -0
- package/dist/src/codemod/drift/detect.js +1 -0
- package/dist/src/codemod/drift/index.d.ts +4 -0
- package/dist/src/codemod/drift/index.js +1 -0
- package/dist/src/codemod/drift/merge.d.ts +19 -0
- package/dist/src/codemod/drift/merge.js +1 -0
- package/dist/src/codemod/drift/snapshot.d.ts +4 -0
- package/dist/src/codemod/drift/snapshot.js +1 -0
- package/dist/src/codemod/drift/types.d.ts +60 -0
- package/dist/src/codemod/drift/types.js +1 -0
- package/dist/src/codemod/edits/addResource/propertyBuilder.d.ts +1 -1
- package/dist/src/codemod/edits/addResource/propertyBuilder.js +1 -1
- package/dist/src/codemod/edits/addResource.d.ts +8 -3
- package/dist/src/codemod/edits/addResource.js +1 -1
- package/dist/src/codemod/edits/controlFlowPolicy.d.ts +19 -0
- package/dist/src/codemod/edits/controlFlowPolicy.js +1 -0
- package/dist/src/codemod/edits/crossPlanConnection.d.ts +20 -0
- package/dist/src/codemod/edits/crossPlanConnection.js +1 -0
- package/dist/src/codemod/edits/driftPolicy.d.ts +7 -0
- package/dist/src/codemod/edits/driftPolicy.js +1 -0
- package/dist/src/codemod/edits/findInsertionPosition.js +1 -1
- package/dist/src/codemod/edits/index.d.ts +3 -0
- package/dist/src/codemod/edits/index.js +1 -1
- package/dist/src/codemod/edits/modifyResource.d.ts +9 -3
- package/dist/src/codemod/edits/modifyResource.js +1 -1
- package/dist/src/codemod/edits/removeResource.d.ts +2 -2
- package/dist/src/codemod/edits/removeResource.js +1 -1
- package/dist/src/codemod/edits/vpcPeer.d.ts +24 -0
- package/dist/src/codemod/edits/vpcPeer.js +1 -0
- package/dist/src/codemod/edits/vpcPeerAccepter.d.ts +20 -0
- package/dist/src/codemod/edits/vpcPeerAccepter.js +1 -0
- package/dist/src/codemod/index.d.ts +16 -4
- package/dist/src/codemod/index.js +1 -1
- package/dist/src/codemod/llmFallback/apply.d.ts +10 -0
- package/dist/src/codemod/llmFallback/apply.js +1 -0
- package/dist/src/codemod/llmFallback/claudeTier.d.ts +6 -0
- package/dist/src/codemod/llmFallback/claudeTier.js +1 -0
- package/dist/src/codemod/llmFallback/egressGate.d.ts +5 -0
- package/dist/src/codemod/llmFallback/egressGate.js +1 -0
- package/dist/src/codemod/llmFallback/egressGate.types.d.ts +9 -0
- package/dist/src/codemod/llmFallback/egressGate.types.js +0 -0
- package/dist/src/codemod/llmFallback/index.d.ts +6 -0
- package/dist/src/codemod/llmFallback/index.js +1 -0
- package/dist/src/codemod/llmFallback/morphTier.d.ts +2 -0
- package/dist/src/codemod/llmFallback/morphTier.js +3 -0
- package/dist/src/codemod/llmFallback/prompt.d.ts +12 -0
- package/dist/src/codemod/llmFallback/prompt.js +36 -0
- package/dist/src/codemod/llmFallback/runFallback.d.ts +13 -0
- package/dist/src/codemod/llmFallback/runFallback.js +1 -0
- package/dist/src/codemod/llmFallback/shouldTryFallback.d.ts +13 -0
- package/dist/src/codemod/llmFallback/shouldTryFallback.js +1 -0
- package/dist/src/codemod/llmFallback/signals.d.ts +4 -0
- package/dist/src/codemod/llmFallback/signals.js +1 -0
- package/dist/src/codemod/llmFallback/telemetryEvents.d.ts +141 -0
- package/dist/src/codemod/llmFallback/telemetryEvents.js +1 -0
- package/dist/src/codemod/llmFallback/tierRunner.d.ts +7 -0
- package/dist/src/codemod/llmFallback/tierRunner.js +1 -0
- package/dist/src/codemod/llmFallback/types.d.ts +104 -0
- package/dist/src/codemod/llmFallback/types.js +1 -0
- package/dist/src/codemod/registry.d.ts +4 -1
- package/dist/src/codemod/registry.js +1 -1
- package/dist/src/codemod/semanticIndex/classifyControlFlow.d.ts +2 -0
- package/dist/src/codemod/semanticIndex/classifyControlFlow.js +1 -0
- package/dist/src/codemod/semanticIndex/findReferences.js +2 -2
- package/dist/src/codemod/telemetry/errorKinds.d.ts +2 -0
- package/dist/src/codemod/telemetry/errorKinds.js +1 -0
- package/dist/src/codemod/types.d.ts +110 -1
- package/dist/src/codemod/types.js +1 -1
- package/dist/src/codemod/validationGate/gates/classify.d.ts +2 -0
- package/dist/src/codemod/validationGate/gates/classify.js +1 -0
- package/dist/src/codemod/validationGate/gates/drift.d.ts +2 -0
- package/dist/src/codemod/validationGate/gates/drift.js +1 -0
- package/dist/src/codemod/validationGate/gates/locate.d.ts +7 -0
- package/dist/src/codemod/validationGate/gates/locate.js +1 -0
- package/dist/src/codemod/validationGate/gates/parse.d.ts +2 -0
- package/dist/src/codemod/validationGate/gates/parse.js +1 -0
- package/dist/src/codemod/validationGate/gates/schema.d.ts +2 -0
- package/dist/src/codemod/validationGate/gates/schema.js +1 -0
- package/dist/src/codemod/validationGate/index.d.ts +6 -0
- package/dist/src/codemod/validationGate/index.js +1 -0
- package/dist/src/codemod/validationGate/types.d.ts +35 -0
- package/dist/src/codemod/validationGate/types.js +1 -0
- package/dist/src/detection/index.d.ts +148 -0
- package/dist/src/detection/index.js +1 -0
- package/dist/src/generation/common.d.ts +22 -0
- package/dist/src/generation/common.js +5 -4
- package/dist/src/generation/compute/ec2.d.ts +2 -0
- package/dist/src/generation/compute/ec2.js +4 -0
- package/dist/src/generation/compute/ecs.d.ts +2 -0
- package/dist/src/generation/compute/ecs.js +42 -0
- package/dist/src/generation/compute/lambda.d.ts +3 -0
- package/dist/src/generation/compute/lambda.js +26 -0
- package/dist/src/generation/compute/shared.d.ts +22 -0
- package/dist/src/generation/compute/shared.js +4 -0
- package/dist/src/generation/compute.d.ts +4 -5
- package/dist/src/generation/compute.js +6 -86
- package/dist/src/generation/database.d.ts +11 -0
- package/dist/src/generation/database.js +23 -12
- package/dist/src/generation/index.d.ts +1 -1
- package/dist/src/generation/index.js +1 -1
- package/dist/src/generation/infrastructure.js +5 -5
- package/dist/src/generation/storage.js +30 -30
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/planning/index.d.ts +2 -1
- package/dist/src/planning/index.js +1 -1
- package/dist/src/planning/openNextPlanning.d.ts +38 -0
- package/dist/src/planning/openNextPlanning.js +1 -0
- package/dist/src/planning/resourceAddition.d.ts +5 -1
- package/dist/src/planning/resourceAddition.js +1 -1
- package/dist/src/planning/resourcePlanning.d.ts +0 -46
- package/dist/src/planning/resourcePlanning.js +1 -1
- package/dist/src/presets/clickhouseTierPreset.d.ts +35 -0
- package/dist/src/presets/clickhouseTierPreset.js +0 -0
- package/dist/src/presets/tierPresets.d.ts +5 -12
- package/dist/src/presets/tierPresets.js +1 -1
- package/dist/src/presets/tierTypes.d.ts +9 -19
- package/dist/src/schemas/applicationSchemas.d.ts +67 -8
- package/dist/src/schemas/applicationSchemas.js +1 -1
- package/dist/src/schemas/baseSchemas.d.ts +24 -5
- package/dist/src/schemas/baseSchemas.js +2 -2
- package/dist/src/schemas/computeSchemas.d.ts +108 -14
- package/dist/src/schemas/computeSchemas.js +1 -1
- package/dist/src/schemas/constants.d.ts +14 -0
- package/dist/src/schemas/constants.js +1 -1
- package/dist/src/schemas/databaseSchemas.d.ts +80 -0
- package/dist/src/schemas/databaseSchemas.js +1 -1
- package/dist/src/schemas/networkSchemas.d.ts +126 -11
- package/dist/src/schemas/networkSchemas.js +1 -1
- package/dist/src/schemas/patternSchemas.js +1 -1
- package/dist/src/schemas/sharedTypes.d.ts +1 -1
- package/dist/src/schemas/sharedTypes.js +1 -1
- package/dist/src/validation/patterns.d.ts +2 -318
- package/dist/src/validation/patterns.js +1 -1
- package/dist/src/validation/validationMessages.d.ts +315 -0
- package/dist/src/validation/validationMessages.js +1 -0
- package/dist/src/validation/validationPatterns.d.ts +34 -0
- package/dist/src/validation/validationPatterns.js +1 -0
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/package.json +19 -13
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Gate } from "../types.js";
|
|
2
|
+
export type LocateMode = "present" | "absent";
|
|
3
|
+
export interface LocateGateOptions {
|
|
4
|
+
mode: LocateMode;
|
|
5
|
+
}
|
|
6
|
+
export declare function createLocateGate(options: LocateGateOptions): Gate;
|
|
7
|
+
export declare const locateGate: Gate;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{success as o,failure as r}from"../../../types/Result.js";import{locateByShape as p}from"../../semanticIndex/locateByShape.js";function u(t){return{id:"locate",run(e){const n=p(e.content,{type:e.plan.type,name:e.plan.name});if(!n.success)return r(n.error);const a=n.data!==void 0;return t.mode==="present"&&!a?r({kind:"ResourceNotFoundError",type:e.plan.type,name:e.plan.name,knownNames:[]}):t.mode==="absent"&&a?r({kind:"DuplicateResourceError",type:e.plan.type,name:e.plan.name}):o({action:"proceed"})}}}const c=u({mode:"present"});export{u as createLocateGate,c as locateGate};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{success as o,failure as t}from"../../../types/Result.js";import{parse as i}from"../../fileRewriter/parse.js";const d={id:"parse",run(s){const r=i(s.content);if(!r.success){const e=r.error,n=e.line!==void 0&&e.column!==void 0?`${String(e.line)}:${String(e.column)}`:void 0;return t({...e,kind:"ParseError",message:n!==void 0?`${e.message} at ${n}`:e.message})}return o({action:"proceed"})}};export{d as parseGate};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{success as c}from"../../../types/Result.js";const t={id:"schema",run(e){return c({action:"proceed"})}};export{t as schemaGate};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { runPipeline, runPipelineAsync, type AsyncGate, type Gate, type GateAction, type GateContext, type GateId, type GateOutcome, type GateResult, } from "./types.js";
|
|
2
|
+
export { driftGate } from "./gates/drift.js";
|
|
3
|
+
export { schemaGate } from "./gates/schema.js";
|
|
4
|
+
export { parseGate } from "./gates/parse.js";
|
|
5
|
+
export { locateGate } from "./gates/locate.js";
|
|
6
|
+
export { classifyGate } from "./gates/classify.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{runPipeline as o,runPipelineAsync as t}from"./types.js";import{driftGate as p}from"./gates/drift.js";import{schemaGate as m}from"./gates/schema.js";import{parseGate as x}from"./gates/parse.js";import{locateGate as s}from"./gates/locate.js";import{classifyGate as c}from"./gates/classify.js";export{c as classifyGate,p as driftGate,s as locateGate,x as parseGate,o as runPipeline,t as runPipelineAsync,m as schemaGate};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type Result } from "../../types/Result.js";
|
|
2
|
+
import type { DriftOp, DriftPolicy, DriftState, ResourceSnapshot } from "../drift/index.js";
|
|
3
|
+
import type { CodemodError, ResourceName, StatementType } from "../types.js";
|
|
4
|
+
export type GateId = "parse" | "tsc" | "classify" | "locate" | "schema" | "drift" | "runtime";
|
|
5
|
+
export type GateAction = "proceed" | "skip" | "reject";
|
|
6
|
+
export interface GateContext {
|
|
7
|
+
content: string;
|
|
8
|
+
plan: {
|
|
9
|
+
type: StatementType;
|
|
10
|
+
name: ResourceName;
|
|
11
|
+
properties: Record<string, unknown>;
|
|
12
|
+
op: DriftOp;
|
|
13
|
+
};
|
|
14
|
+
baseline?: ResourceSnapshot;
|
|
15
|
+
policy?: DriftPolicy;
|
|
16
|
+
signal?: AbortSignal;
|
|
17
|
+
inputContent?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface GateOutcome {
|
|
20
|
+
action: GateAction;
|
|
21
|
+
driftState?: DriftState;
|
|
22
|
+
failedGate?: GateId;
|
|
23
|
+
diagnostics?: string;
|
|
24
|
+
}
|
|
25
|
+
export type GateResult = Result<GateOutcome, CodemodError>;
|
|
26
|
+
export interface Gate {
|
|
27
|
+
readonly id: GateId;
|
|
28
|
+
run(ctx: GateContext): GateResult;
|
|
29
|
+
}
|
|
30
|
+
export interface AsyncGate {
|
|
31
|
+
readonly id: GateId;
|
|
32
|
+
run(ctx: GateContext): Promise<GateResult>;
|
|
33
|
+
}
|
|
34
|
+
export declare function runPipeline(gates: readonly Gate[], ctx: GateContext): GateResult;
|
|
35
|
+
export declare function runPipelineAsync(gates: ReadonlyArray<Gate | AsyncGate>, ctx: GateContext): Promise<GateResult>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{success as o}from"../../types/Result.js";function i(n,r){for(const t of n){const e=t.run(r);if(!e.success||e.data.action!=="proceed")return e}return o({action:"proceed"})}async function a(n,r){for(const t of n){if(r.signal?.aborted===!0)return o({action:"reject",failedGate:t.id,diagnostics:"aborted"});const e=await t.run(r);if(!e.success||e.data.action!=="proceed")return e}return o({action:"proceed"})}export{i as runPipeline,a as runPipelineAsync};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure framework / monorepo / port detection logic.
|
|
3
|
+
*
|
|
4
|
+
* No filesystem I/O — all functions take parsed inputs (package.json
|
|
5
|
+
* objects, marker-file presence sets, dockerfile name lists) and
|
|
6
|
+
* return structured inferences. The CLI's I/O wrapper reads files
|
|
7
|
+
* and feeds them in; the MCP tool returns the result over the wire.
|
|
8
|
+
*
|
|
9
|
+
* The Zod schemas exported here are the contract for both the CLI's
|
|
10
|
+
* `apps detect --output json` frame and the MCP `detect_app_framework`
|
|
11
|
+
* tool's `outputSchema`.
|
|
12
|
+
*/
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
export declare const FRAMEWORK_VALUES: readonly ["nextjs", "payload", "nextjs+payload", "express", "remix", "astro", "unknown"];
|
|
15
|
+
export declare const FrameworkSchema: z.ZodEnum<{
|
|
16
|
+
payload: "payload";
|
|
17
|
+
nextjs: "nextjs";
|
|
18
|
+
unknown: "unknown";
|
|
19
|
+
"nextjs+payload": "nextjs+payload";
|
|
20
|
+
express: "express";
|
|
21
|
+
remix: "remix";
|
|
22
|
+
astro: "astro";
|
|
23
|
+
}>;
|
|
24
|
+
export type Framework = z.infer<typeof FrameworkSchema>;
|
|
25
|
+
export declare const MONOREPO_TOOL_VALUES: readonly ["npm-workspaces", "pnpm", "lerna", "turbo", "nx"];
|
|
26
|
+
export declare const MonorepoToolSchema: z.ZodEnum<{
|
|
27
|
+
"npm-workspaces": "npm-workspaces";
|
|
28
|
+
pnpm: "pnpm";
|
|
29
|
+
lerna: "lerna";
|
|
30
|
+
turbo: "turbo";
|
|
31
|
+
nx: "nx";
|
|
32
|
+
}>;
|
|
33
|
+
export type MonorepoTool = z.infer<typeof MonorepoToolSchema>;
|
|
34
|
+
export declare const AppDetectionSchema: z.ZodObject<{
|
|
35
|
+
relativePath: z.ZodString;
|
|
36
|
+
framework: z.ZodEnum<{
|
|
37
|
+
payload: "payload";
|
|
38
|
+
nextjs: "nextjs";
|
|
39
|
+
unknown: "unknown";
|
|
40
|
+
"nextjs+payload": "nextjs+payload";
|
|
41
|
+
express: "express";
|
|
42
|
+
remix: "remix";
|
|
43
|
+
astro: "astro";
|
|
44
|
+
}>;
|
|
45
|
+
packageName: z.ZodNullable<z.ZodString>;
|
|
46
|
+
hasDockerfile: z.ZodBoolean;
|
|
47
|
+
docker: z.ZodNullable<z.ZodObject<{
|
|
48
|
+
path: z.ZodString;
|
|
49
|
+
}, z.core.$strict>>;
|
|
50
|
+
inferredPort: z.ZodNumber;
|
|
51
|
+
suggestedAppName: z.ZodString;
|
|
52
|
+
suggestedConfigPath: z.ZodString;
|
|
53
|
+
}, z.core.$strict>;
|
|
54
|
+
export type AppDetection = z.infer<typeof AppDetectionSchema>;
|
|
55
|
+
export declare const RepositoryDetectionSchema: z.ZodObject<{
|
|
56
|
+
isMonorepo: z.ZodBoolean;
|
|
57
|
+
monorepoTool: z.ZodNullable<z.ZodEnum<{
|
|
58
|
+
"npm-workspaces": "npm-workspaces";
|
|
59
|
+
pnpm: "pnpm";
|
|
60
|
+
lerna: "lerna";
|
|
61
|
+
turbo: "turbo";
|
|
62
|
+
nx: "nx";
|
|
63
|
+
}>>;
|
|
64
|
+
rootPackageName: z.ZodNullable<z.ZodString>;
|
|
65
|
+
apps: z.ZodArray<z.ZodObject<{
|
|
66
|
+
relativePath: z.ZodString;
|
|
67
|
+
framework: z.ZodEnum<{
|
|
68
|
+
payload: "payload";
|
|
69
|
+
nextjs: "nextjs";
|
|
70
|
+
unknown: "unknown";
|
|
71
|
+
"nextjs+payload": "nextjs+payload";
|
|
72
|
+
express: "express";
|
|
73
|
+
remix: "remix";
|
|
74
|
+
astro: "astro";
|
|
75
|
+
}>;
|
|
76
|
+
packageName: z.ZodNullable<z.ZodString>;
|
|
77
|
+
hasDockerfile: z.ZodBoolean;
|
|
78
|
+
docker: z.ZodNullable<z.ZodObject<{
|
|
79
|
+
path: z.ZodString;
|
|
80
|
+
}, z.core.$strict>>;
|
|
81
|
+
inferredPort: z.ZodNumber;
|
|
82
|
+
suggestedAppName: z.ZodString;
|
|
83
|
+
suggestedConfigPath: z.ZodString;
|
|
84
|
+
}, z.core.$strict>>;
|
|
85
|
+
warnings: z.ZodArray<z.ZodString>;
|
|
86
|
+
}, z.core.$strict>;
|
|
87
|
+
export type RepositoryDetection = z.infer<typeof RepositoryDetectionSchema>;
|
|
88
|
+
export interface ParsedPackageJson {
|
|
89
|
+
name?: string;
|
|
90
|
+
workspaces?: string[] | {
|
|
91
|
+
packages?: string[];
|
|
92
|
+
};
|
|
93
|
+
dependencies?: Record<string, string>;
|
|
94
|
+
devDependencies?: Record<string, string>;
|
|
95
|
+
scripts?: Record<string, string>;
|
|
96
|
+
}
|
|
97
|
+
export interface MarkerFiles {
|
|
98
|
+
pnpmWorkspaceYaml: boolean;
|
|
99
|
+
lernaJson: boolean;
|
|
100
|
+
turboJson: boolean;
|
|
101
|
+
nxJson: boolean;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Determine the monorepo tool from root package.json + marker files.
|
|
105
|
+
* Returns null for a flat (single-app) repo.
|
|
106
|
+
*
|
|
107
|
+
* Precedence when multiple markers exist (e.g. turbo on top of npm
|
|
108
|
+
* workspaces): the more specific tool wins (turbo > nx > pnpm > lerna
|
|
109
|
+
* > npm-workspaces) since turbo/nx are orchestrators that *use* the
|
|
110
|
+
* underlying workspace tool but agents typically want the orchestrator
|
|
111
|
+
* name surfaced.
|
|
112
|
+
*/
|
|
113
|
+
export declare function inferMonorepoTool(rootPackageJson: ParsedPackageJson | null, markers: MarkerFiles): MonorepoTool | null;
|
|
114
|
+
/**
|
|
115
|
+
* Extract workspace globs from a package.json — handles both
|
|
116
|
+
* the array form (`["apps/*"]`) and the object form
|
|
117
|
+
* (`{ packages: ["apps/*"] }`, used by Yarn classic).
|
|
118
|
+
*/
|
|
119
|
+
export declare function extractWorkspaceGlobs(pkg: ParsedPackageJson | null): string[];
|
|
120
|
+
export declare function inferFrameworkFromDeps(pkg: ParsedPackageJson | null): Framework;
|
|
121
|
+
export declare function inferPortForFramework(framework: Framework): number;
|
|
122
|
+
/**
|
|
123
|
+
* Pick the preferred Dockerfile when several exist. Production-suffixless
|
|
124
|
+
* `Dockerfile` wins; `Dockerfile.dev` is only chosen when nothing else is
|
|
125
|
+
* present (and surfaces a warning to the caller). Returns null if the
|
|
126
|
+
* input list is empty.
|
|
127
|
+
*/
|
|
128
|
+
export declare function pickPreferredDockerfile(dockerfileNames: string[]): {
|
|
129
|
+
name: string;
|
|
130
|
+
isDev: boolean;
|
|
131
|
+
} | null;
|
|
132
|
+
/**
|
|
133
|
+
* Derive a kebab-case app name from a package name (`@scope/name` →
|
|
134
|
+
* `name`) or fall back to the basename of the relative path.
|
|
135
|
+
*/
|
|
136
|
+
export declare function suggestAppName(packageName: string | null, relativePath: string): string;
|
|
137
|
+
/**
|
|
138
|
+
* Build the suggested fjall config path. Flat repos land at
|
|
139
|
+
* `fjall/<name>`; monorepo workspaces land at
|
|
140
|
+
* `<relativePath>/fjall/<name>` so the IaC sits inside the app's own
|
|
141
|
+
* workspace, scoped to that app's lifecycle.
|
|
142
|
+
*
|
|
143
|
+
* Delegates to `buildAppConfigPath` from `@fjall/util` — that helper is
|
|
144
|
+
* the single source of truth for the overlay boundary convention
|
|
145
|
+
* (`<container>/fjall/<name>`). The workspace's `relativePath` plays the
|
|
146
|
+
* `container` role.
|
|
147
|
+
*/
|
|
148
|
+
export declare function suggestConfigPath(appName: string, relativePath: string): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{buildAppConfigPath as i}from"@fjall/util";import{z as o}from"zod";const p=["nextjs","payload","nextjs+payload","express","remix","astro","unknown"],c=o.enum(p),l=["npm-workspaces","pnpm","lerna","turbo","nx"],f=o.enum(l),u=o.object({relativePath:o.string().describe("Path relative to repo root. '.' for flat repo, e.g. 'apps/website' for monorepo workspace."),framework:c,packageName:o.string().nullable(),hasDockerfile:o.boolean(),docker:o.object({path:o.string().describe("Path to chosen Dockerfile, relative to relativePath.")}).strict().nullable().describe("Docker build descriptor. Null if no Dockerfile present."),inferredPort:o.number().int().min(1).max(65535),suggestedAppName:o.string().describe("kebab-case app name suggestion derived from packageName or relativePath."),suggestedConfigPath:o.string().describe("Suggested fjall config path relative to repo root, e.g. 'fjall/website' or 'apps/website/fjall/website'.")}).strict(),g=o.object({isMonorepo:o.boolean(),monorepoTool:f.nullable(),rootPackageName:o.string().nullable(),apps:o.array(u).describe("Detected apps. Empty if nothing scaffoldable was found. Single-element for flat repos, multi-element for monorepos."),warnings:o.array(o.string()).describe("Non-fatal observations (e.g. 'apps/legacy has no package.json \u2014 skipped').")}).strict();function k(e,r){return r.turboJson?"turbo":r.nxJson?"nx":r.pnpmWorkspaceYaml?"pnpm":r.lernaJson?"lerna":e?.workspaces?"npm-workspaces":null}function w(e){return e?.workspaces?Array.isArray(e.workspaces)?e.workspaces:e.workspaces.packages??[]:[]}const d=[{framework:"nextjs+payload",required:["next","payload"]},{framework:"nextjs",required:["next"]},{framework:"payload",required:["payload"]},{framework:"remix",required:["@remix-run/node"]},{framework:"astro",required:["astro"]},{framework:"express",required:["express"]}];function D(e){if(!e)return"unknown";const r={...e.dependencies??{},...e.devDependencies??{}};for(const t of d)if(t.required.every(n=>n in r))return t.framework;return"unknown"}const m={nextjs:3e3,"nextjs+payload":3e3,payload:3e3,remix:3e3,astro:4321,express:3e3,unknown:3e3};function h(e){return m[e]}function y(e){if(e.length===0)return null;const r=e.find(s=>s==="Dockerfile");if(r)return{name:r,isDev:!1};const t=e.find(s=>s.startsWith("Dockerfile")&&!/\.(dev|local|test)$/i.test(s));if(t)return{name:t,isDev:!1};const n=e.find(s=>/\.dev$/i.test(s));if(n)return{name:n,isDev:!0};const a=e[0];return a===void 0?null:{name:a,isDev:!1}}function j(e,r){if(e){const n=e.replace(/^@[^/]+\//,"");if(n.length>0)return n}return r==="."||r===""?"app":r.split("/").pop()??"app"}function v(e,r){return r==="."||r===""?i(e):i(e,r)}export{u as AppDetectionSchema,p as FRAMEWORK_VALUES,c as FrameworkSchema,l as MONOREPO_TOOL_VALUES,f as MonorepoToolSchema,g as RepositoryDetectionSchema,w as extractWorkspaceGlobs,D as inferFrameworkFromDeps,k as inferMonorepoTool,h as inferPortForFramework,y as pickPreferredDockerfile,j as suggestAppName,v as suggestConfigPath};
|
|
@@ -6,6 +6,28 @@
|
|
|
6
6
|
import type { IdentifierValue, ExpressionValue, CallValue, SpecialValue } from "../schemas/resourceSchemas.js";
|
|
7
7
|
export type { IdentifierValue, ExpressionValue, CallValue, SpecialValue };
|
|
8
8
|
export { toPascalCase, toKebab, toValidDatabaseName } from "@fjall/util";
|
|
9
|
+
/**
|
|
10
|
+
* Emit `value` as a valid TS string literal, including surrounding quotes.
|
|
11
|
+
* Routes through `JSON.stringify` so quotes, backslashes, and control characters
|
|
12
|
+
* are escaped — defence-in-depth against template injection from user-supplied
|
|
13
|
+
* fields (image tags, paths, descriptions) that schema validation does not
|
|
14
|
+
* already restrict to a quote-free regex.
|
|
15
|
+
*/
|
|
16
|
+
export declare function escapeStringLiteral(value: string): string;
|
|
17
|
+
export declare function formatStringArray(values: readonly string[]): string;
|
|
18
|
+
/**
|
|
19
|
+
* Cross-stack import name for the shared SNS alarm topic.
|
|
20
|
+
*
|
|
21
|
+
* Production-only stacks (compute, database) wire alerts through this import.
|
|
22
|
+
* Both `generation/database.ts` and `generation/compute/ecs.ts` emit a spread
|
|
23
|
+
* conditional on `getConfig().environment === "production"` — drift between
|
|
24
|
+
* the two emitters silently breaks one half of production alarm wiring with
|
|
25
|
+
* no typecheck/test catching it. Hoisted here so one edit moves both call
|
|
26
|
+
* sites; matches the `code-quality.md § "Coupled Numeric Defaults"` discipline
|
|
27
|
+
* extended to coupled API-contract literals.
|
|
28
|
+
*/
|
|
29
|
+
export declare const PRODUCTION_ALERTS_TOPIC_IMPORT = "import:SharedAlarmTopicArn";
|
|
30
|
+
export declare function emitProductionAlertsTopicSpread(indent?: string): string;
|
|
9
31
|
/**
|
|
10
32
|
* Type guard to check if a value is a special code generation value
|
|
11
33
|
*/
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import{toPascalCase as
|
|
1
|
+
import{toPascalCase as N,toKebab as S,toValidDatabaseName as A}from"@fjall/util";function s(r){return JSON.stringify(r)}function l(r){return r.map(s).join(", ")}const m="import:SharedAlarmTopicArn";function d(r=" "){return`
|
|
2
|
+
${r}...(getConfig().environment === "production" ? { alertsTopic: ${s(m)} } : {}),`}function b(r){if(typeof r!="object"||r===null)return!1;const e=r;return typeof e.__identifier=="string"||typeof e.__expression=="string"||typeof e.__call=="string"}function c(r){return r.replace(/[^a-zA-Z0-9]/g," ").split(" ").map((e,t)=>t===0?e.charAt(0).toLowerCase()+e.slice(1):e.charAt(0).toUpperCase()+e.slice(1)).join("")}function g(r){return r.variableName??c(r.name)}function y(r,e){for(const t of r.database)if(t.name===e&&t.variableName)return t.variableName;for(const t of r.s3)if(t.name===e&&t.variableName)return t.variableName;for(const t of r.compute)if(t.name===e&&t.variableName)return t.variableName;for(const t of r.dynamodb??[])if(t.name===e&&t.variableName)return t.variableName;for(const t of r.sqs??[])if(t.name===e&&t.variableName)return t.variableName;return c(e)}function u(r){const e=Object.entries(r);return e.length===0?!0:e.length>1?!1:e.every(([,t])=>typeof t=="string"||typeof t=="number"||typeof t=="boolean"||t===null)}function o(r,e=" "){if(r==null)return"undefined";if(typeof r=="string")return JSON.stringify(r);if(typeof r=="number"||typeof r=="boolean")return String(r);if(b(r)){if("__identifier"in r)return r.__identifier;if("__expression"in r)return r.__expression;if("__call"in r)return r.__call}if(Array.isArray(r))return`[${r.map(t=>o(t,e)).join(", ")}]`;if(typeof r=="object"){const t=Object.entries(r).filter(([,i])=>i!==void 0);if(u(r)){const i=t.map(([a,p])=>`${a}: ${o(p,e)}`).join(", ");return i?`{ ${i} }`:"{}"}const f=e+" ",n=t.map(([i,a])=>`${f}${i}: ${o(a,f)}`).join(`,
|
|
2
3
|
`);return n?`{
|
|
3
4
|
${n}
|
|
4
|
-
${
|
|
5
|
-
${
|
|
6
|
-
${
|
|
5
|
+
${e}}`:"{}"}return JSON.stringify(r)}function _(r,e,t,f="raw"){if(!r)return"";let n;switch(f){case"string":n=s(String(t));break;case"object":n=o(t);break;case"boolean-or-object":n=t===!1?"false":o(t);break;default:n=String(t)}return`
|
|
6
|
+
${e}: ${n},`}function x(r,e=" "){return r?.length?r.map(t=>`
|
|
7
|
+
${e}${t.key}: ${t.sourceText},`).join(""):""}export{m as PRODUCTION_ALERTS_TOPIC_IMPORT,_ as buildProperty,x as emitExtraProperties,d as emitProductionAlertsTopicSpread,s as escapeStringLiteral,l as formatStringArray,o as formatValue,g as getVariableName,b as isSpecialValue,y as resolveResourceVariable,S as toKebab,N as toPascalCase,A as toValidDatabaseName,c as toVariableName};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{emitExtraProperties as r,escapeStringLiteral as i}from"../common.js";function a(e){let n="";return e.instanceType&&(n+=`
|
|
2
|
+
instanceType: ${i(e.instanceType)},`),e.enableSSH!==void 0&&(n+=e.enableSSH?`
|
|
3
|
+
ssh: {},`:`
|
|
4
|
+
ssh: false,`),n+=r(e.extraProperties),n}export{a as generateEc2Code};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import{formatValue as S,emitExtraProperties as v,escapeStringLiteral as o,formatStringArray as T,emitProductionAlertsTopicSpread as O}from"../common.js";import{DATABASE_ENV_VARS as g,getConnectedDatabases as k,buildDatabaseEnvVars as R}from"../database.js";import{STORAGE_ENV_VARS as D,getConnectedStorage as N,buildStorageEnvVars as B,formatAllConnectionsCode as j}from"../storageConnections.js";import{MESSAGING_ENV_VARS as b,getConnectedMessaging as L,buildMessagingEnvVars as _}from"../messagingConnections.js";import{BASE_ENVIRONMENT_VARS as H,computeHasConnections as w,computeHasDatabaseConnections as C,computeHasMessagingConnections as P,computeHasStorageConnections as M,formatCompactOrExpandedObject as $,formatEnvBlock as V}from"./shared.js";function I(e){const n=[];if(e.instanceType!==void 0&&n.push(`instanceType: ${o(e.instanceType)}`),e.amiHardwareType!==void 0&&n.push(`amiHardwareType: ${o(e.amiHardwareType)}`),e.minCapacity!==void 0&&n.push(`minCapacity: ${e.minCapacity}`),e.maxCapacity!==void 0&&n.push(`maxCapacity: ${e.maxCapacity}`),e.memoryLimitMiB!==void 0&&n.push(`memoryLimitMiB: ${e.memoryLimitMiB}`),e.warmPool!==void 0&&n.push(`warmPool: ${S(e.warmPool," ")}`),e.extraProperties?.length)for(const t of e.extraProperties)n.push(`${t.key}: ${t.sourceText}`);return`
|
|
2
|
+
ec2Config: ${$(n," "," ")},`}function G(e){if(!e.cluster)return"";const n=[];if(e.cluster.directAccess&&n.push("directAccess: true"),e.cluster.domain&&n.push(`domain: ${o(e.cluster.domain)}`),e.cluster.loadBalancer!==void 0&&n.push(e.cluster.loadBalancer===!1?"loadBalancer: false":`loadBalancer: ${o(e.cluster.loadBalancer)}`),e.cluster.extraProperties?.length)for(const t of e.cluster.extraProperties)n.push(`${t.key}: ${t.sourceText}`);return`
|
|
3
|
+
cluster: ${$(n," "," ")},`}function J(e,n,t,m,l,h){let i="";e.name&&(i+=`
|
|
4
|
+
name: ${o(e.name)},`),e.image&&(i+=`
|
|
5
|
+
image: ${o(e.image)},`),e.port!==void 0&&(i+=`
|
|
6
|
+
port: ${e.port},`),e.essential!==void 0&&(i+=`
|
|
7
|
+
essential: ${e.essential},`),e.command?.length&&(i+=`
|
|
8
|
+
command: ${JSON.stringify(e.command)},`),e.entryPoint?.length&&(i+=`
|
|
9
|
+
entryPoint: ${JSON.stringify(e.entryPoint)},`);const c=[g.HOST,g.PORT,g.NAME,g.SSL,g.USERNAME,g.PASSWORD,D.NAME,b.URL,b.ARN],r=a=>{if(!a)return a;const d={};for(const[u,p]of Object.entries(a))c.some(y=>u===y||u.startsWith(`${y}_`))||(d[u]=p);return Object.keys(d).length>0?d:void 0},f=n?{...h,...t.env,...m.env,...l.env,...e.environment}:{...h,...r(e.environment)},s=n?{...t.secrets,...e.secretsImport}:r(e.secretsImport);return f&&Object.keys(f).length>0&&(i+=`
|
|
10
|
+
environment: {
|
|
11
|
+
${V(f," ")}
|
|
12
|
+
},`),s&&Object.keys(s).length>0&&(i+=`
|
|
13
|
+
secretsImport: {
|
|
14
|
+
${V(s," ")}
|
|
15
|
+
},`),e.ssmSecrets&&e.ssmSecrets.length>0&&(i+=`
|
|
16
|
+
secrets: [${T(e.ssmSecrets)}],`),e.healthCheck&&(i+=`
|
|
17
|
+
healthCheck: ${S(e.healthCheck," ")},`),i+=v(e.extraProperties," "),i}function x(e,n){const t=[];return e.path&&t.push(`path: ${o(e.path)}`),e.host&&t.push(`host: ${o(e.host)}`),e.priority!==void 0&&t.push(`priority: ${e.priority}`),e.healthCheckPath&&t.push(`healthCheckPath: ${o(e.healthCheckPath)}`),$(t,n,`${n} `)}function K(e){const n=Array.isArray(e)?e:[e];return n.length===1?`
|
|
18
|
+
routing: ${x(n[0]," ")},`:`
|
|
19
|
+
routing: [
|
|
20
|
+
${n.map(m=>x(m," ")).join(`,
|
|
21
|
+
`)},
|
|
22
|
+
],`}function U(e){if(e===!1)return`
|
|
23
|
+
scaling: false,`;if(typeof e!="object"||!(e.minCapacity!==void 0||e.maxCapacity!==void 0||e.desiredCount!==void 0||e.scalingType!==void 0))return"";const t=[];return e.minCapacity!==void 0&&t.push(`minCapacity: ${e.minCapacity}`),e.maxCapacity!==void 0&&t.push(`maxCapacity: ${e.maxCapacity}`),e.desiredCount!==void 0&&t.push(`desiredCount: ${e.desiredCount}`),e.scalingType&&t.push(`scalingType: ${o(e.scalingType)}`),`
|
|
24
|
+
scaling: ${$(t," "," ")},`}function W(e,n,t){return{needsDb:e.needsDatabaseConnection!==void 0?e.needsDatabaseConnection:n&&C(t),needsStorage:e.needsStorageConnection!==void 0?e.needsStorageConnection:n&&M(t),needsMessaging:e.needsMessagingConnection!==void 0?e.needsMessagingConnection:n&&P(t)}}function q(e){const{service:n,isFirstService:t,hasConnections:m,dbEnvVars:l,storageEnvVars:h,messagingEnvVars:i,baseEnvVars:c,plan:r,compute:f}=e;let s=`
|
|
25
|
+
name: ${o(n.name)},`;s+=`
|
|
26
|
+
capacityProvider: ${o(n.capacityProvider)},`,n.ec2Config&&(s+=I(n.ec2Config)),n.docker&&(s+=`
|
|
27
|
+
docker: ${S(n.docker," ")},`),n.image&&(s+=`
|
|
28
|
+
image: ${o(n.image)},`),n.ssmSecretsPath&&(s+=`
|
|
29
|
+
ssmSecretsPath: ${o(n.ssmSecretsPath)},`);const a=W(n,t,f);if(n.containers?.length){s+=`
|
|
30
|
+
containers: [{`;for(let d=0;d<n.containers.length;d++){const u=n.containers[d];if(!u)continue;const p=d===0&&(a.needsDb||a.needsStorage||a.needsMessaging)&&m,E=a.needsDb?l:{env:{},secrets:{}},y=a.needsStorage?h:{env:{},secrets:{}},A=a.needsMessaging?i:{env:{},secrets:{}};d>0&&(s+=`
|
|
31
|
+
}, {`),s+=J(u,p,E,y,A,c)}s+=`
|
|
32
|
+
}],`}if(n.routing&&(s+=K(n.routing)),n.cpu!==void 0&&(s+=`
|
|
33
|
+
cpu: ${n.cpu},`),n.memoryLimitMiB!==void 0&&(s+=`
|
|
34
|
+
memoryLimitMiB: ${n.memoryLimitMiB},`),n.desiredCount!==void 0&&(s+=`
|
|
35
|
+
desiredCount: ${n.desiredCount},`),n.scaling!==void 0&&(s+=U(n.scaling)),n.alarms===!1?s+=`
|
|
36
|
+
alarms: false,`:typeof n.alarms=="object"&&(s+=`
|
|
37
|
+
alarms: ${S(n.alarms," ")},`),s+=v(n.extraProperties," "),a.needsDb||a.needsStorage||a.needsMessaging){const d=a.needsDb?k(r,f):[],u=a.needsStorage?N(r,f):[],p=a.needsMessaging?L(r,f):[];(d.length>0||u.length>0||p.length>0)&&(s+=`
|
|
38
|
+
${j(d,u,p)}`)}return s}function Z(e,n){const t=w(e),m={...H},l={env:{},secrets:{}};if(C(e)){const r=R(n,e);Object.assign(l.env,r.env),Object.assign(l.secrets,r.secrets)}const h={env:{},secrets:{}};if(M(e)){const r=B(n,e);Object.assign(h.env,r.env)}const i={env:{},secrets:{}};if(P(e)){const r=_(n,e);Object.assign(i.env,r.env)}let c=`
|
|
39
|
+
ecrRepository: app.getDefaultContainerRegistry(),`;if(c+=G(e),e.services?.length){c+=`
|
|
40
|
+
services: [{`;for(let r=0;r<e.services.length;r++){const f=e.services[r];f&&(r>0&&(c+=`
|
|
41
|
+
}, {`),c+=q({service:f,isFirstService:r===0,hasConnections:t,dbEnvVars:l,storageEnvVars:h,messagingEnvVars:i,baseEnvVars:m,plan:n,compute:e}))}c+=`
|
|
42
|
+
}],`}return c+=v(e.extraProperties),c+=O(),c}export{Z as generateEcsCode};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ApplicationResourcePlan, ComputeResourcePlan } from "../../schemas/resourceSchemas.js";
|
|
2
|
+
export declare function generateLambdaCode(compute: ComputeResourcePlan): string;
|
|
3
|
+
export declare function generateLambdaConnectionsCode(compute: ComputeResourcePlan, plan: ApplicationResourcePlan): string;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import{formatValue as m,emitExtraProperties as S,escapeStringLiteral as a,formatStringArray as $}from"../common.js";import{getConnectedDatabases as v,generateDatabaseEnvVarEntries as b,toDatabaseEnvVarValue as D}from"../database.js";import{getConnectedStorage as O,generateStorageEnvVarEntries as T,formatAllConnectionsCode as A}from"../storageConnections.js";import{getConnectedMessaging as N,generateMessagingEnvVarEntries as _}from"../messagingConnections.js";import{DEPLOYMENT_TYPE as l,DEFAULT_COMPUTE_ARCHITECTURE as k}from"../../schemas/constants.js";import{BASE_ENVIRONMENT_VARS as C,computeHasConnections as x,computeHasDatabaseConnections as P,computeHasMessagingConnections as V,computeHasStorageConnections as R,formatEnvBlock as M}from"./shared.js";function z(e){let n="";if(e.deployment===l.CODE&&e.codePath?(n+=`
|
|
2
|
+
deployment: "${l.CODE}",
|
|
3
|
+
code: Code.fromAsset(${a(e.codePath)}),`,e.handler&&(n+=`
|
|
4
|
+
handler: ${a(e.handler)},`),e.runtime&&(n+=`
|
|
5
|
+
runtime: Runtime.${e.runtime},`)):n+=`
|
|
6
|
+
deployment: "${l.CONTAINER}",
|
|
7
|
+
ecrRepository: app.getDefaultContainerRegistry(),`,e.timeout!==void 0&&(n+=`
|
|
8
|
+
timeout: ${e.timeout},`),e.memory!==void 0&&(n+=`
|
|
9
|
+
memorySize: ${e.memory},`),e.functionUrl&&(n+=`
|
|
10
|
+
functionUrl: { authType: FunctionUrlAuthType.${e.functionUrl.authType} },`),e.description&&(n+=`
|
|
11
|
+
lambdaDescription: ${a(e.description)},`),e.architecture||e.deployment===l.CONTAINER){const i=e.architecture??k;n+=`
|
|
12
|
+
architecture: Architecture.${i},`}if(e.ephemeralStorageSize!==void 0&&(n+=`
|
|
13
|
+
ephemeralStorageSize: ${e.ephemeralStorageSize},`),e.functionName&&(n+=`
|
|
14
|
+
functionName: ${a(e.functionName)},`),e.ssmSecretsPath&&(n+=`
|
|
15
|
+
ssmSecretsPath: ${a(e.ssmSecretsPath)},`),e.ssmSecrets&&e.ssmSecrets.length>0&&(n+=`
|
|
16
|
+
secrets: [${$(e.ssmSecrets)}],`),!x(e)){const i={...C,...e.environment};n+=`
|
|
17
|
+
environment: {
|
|
18
|
+
${M(i," ")}
|
|
19
|
+
},`}return n+=S(e.extraProperties),n}function B(e,n){const i=P(e),h=R(e),y=V(e);if(!i&&!h&&!y)return"";const f=i?v(n,e):[],c=h?O(n,e):[],g=y?N(n,e):[];if(f.length===0&&c.length===0&&g.length===0)return"";const o={...C},d={};if(f.length>0){const r=b(f);for(const t of r){const E=D(t);t.isSecret?d[t.key]={__expression:t.expression}:o[t.key]=E}}if(c.length>0){const r=T(c);for(const t of r)o[t.key]={__expression:t.expression}}if(g.length>0){const r=_(g);for(const t of r)o[t.key]={__expression:t.expression}}if(e.environment)for(const[r,t]of Object.entries(e.environment))o[r]=t;let s="";if(Object.keys(d).length>0){s+=`
|
|
20
|
+
containerSecretsImport: {`;for(const[r,t]of Object.entries(d))s+=`
|
|
21
|
+
${r}: ${m(t)},`;s+=`
|
|
22
|
+
},`}if(Object.keys(o).length>0){s+=`
|
|
23
|
+
environment: {`;for(const[r,t]of Object.entries(o))s+=`
|
|
24
|
+
${r}: ${m(t)},`;s+=`
|
|
25
|
+
},`}return s+=`
|
|
26
|
+
${A(f,c,g)}`,s}export{z as generateLambdaCode,B as generateLambdaConnectionsCode};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ComputeResourcePlan } from "../../schemas/resourceSchemas.js";
|
|
2
|
+
/** Check if a compute resource has database connections */
|
|
3
|
+
export declare function computeHasDatabaseConnections(compute: ComputeResourcePlan): boolean;
|
|
4
|
+
/** Check if a compute resource has storage connections */
|
|
5
|
+
export declare function computeHasStorageConnections(compute: ComputeResourcePlan): boolean;
|
|
6
|
+
/** Check if a compute resource has messaging connections */
|
|
7
|
+
export declare function computeHasMessagingConnections(compute: ComputeResourcePlan): boolean;
|
|
8
|
+
/** Check if a compute resource has any connections (database, storage, or messaging) */
|
|
9
|
+
export declare function computeHasConnections(compute: ComputeResourcePlan): boolean;
|
|
10
|
+
export declare const BASE_ENVIRONMENT_VARS: {
|
|
11
|
+
readonly ENVIRONMENT: {
|
|
12
|
+
readonly __expression: "getConfig().environment";
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
export declare function formatEnvBlock(vars: Record<string, unknown>, indent: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Emit `{ part0 }` for zero or one parts, otherwise an indented multi-line
|
|
18
|
+
* object literal with each part on its own line. Shared between the ECS
|
|
19
|
+
* cluster, routing, and scaling generators which all need the same compact
|
|
20
|
+
* vs expanded shape.
|
|
21
|
+
*/
|
|
22
|
+
export declare function formatCompactOrExpandedObject(parts: string[], outerIndent: string, innerIndent: string): string;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{formatValue as c}from"../common.js";function a(n){return!!(n.needsConnection&&n.connectedDatabase?.length)}function i(n){return!!n.connectedStorage?.length}function s(n){return!!n.connectedMessaging?.length}function f(n){return a(n)||i(n)||s(n)}const g={ENVIRONMENT:{__expression:"getConfig().environment"}};function p(n,e){return Object.entries(n).map(([o,t])=>`${e}${o}: ${c(t)},`).join(`
|
|
2
|
+
`)}function m(n,e,o){return n.length<=1?`{ ${n[0]??""} }`:`{${n.map(r=>`
|
|
3
|
+
${o}${r},`).join("")}
|
|
4
|
+
${e}}`}export{g as BASE_ENVIRONMENT_VARS,f as computeHasConnections,a as computeHasDatabaseConnections,s as computeHasMessagingConnections,i as computeHasStorageConnections,m as formatCompactOrExpandedObject,p as formatEnvBlock};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type { ApplicationResourcePlan
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export
|
|
5
|
-
export declare function generateEcsCode(compute: ComputeResourcePlan, plan: ApplicationResourcePlan): string;
|
|
1
|
+
import type { ApplicationResourcePlan } from "../schemas/resourceSchemas.js";
|
|
2
|
+
export { generateLambdaCode, generateLambdaConnectionsCode, } from "./compute/lambda.js";
|
|
3
|
+
export { generateEc2Code } from "./compute/ec2.js";
|
|
4
|
+
export { generateEcsCode } from "./compute/ecs.js";
|
|
6
5
|
export declare function generateComputeCode(plan: ApplicationResourcePlan, cdnReferencedResources: Set<string>): string;
|
|
@@ -1,88 +1,8 @@
|
|
|
1
|
-
import{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
handler: "${e.handler}",`),e.runtime&&(t+=`
|
|
6
|
-
runtime: Runtime.${e.runtime},`)):t+=`
|
|
7
|
-
deployment: "${C.CONTAINER}",
|
|
8
|
-
ecrRepository: app.getDefaultContainerRegistry(),`,e.timeout!==void 0&&(t+=`
|
|
9
|
-
timeout: ${e.timeout},`),e.memory!==void 0&&(t+=`
|
|
10
|
-
memorySize: ${e.memory},`),e.functionUrl&&(t+=`
|
|
11
|
-
functionUrl: { authType: FunctionUrlAuthType.${e.functionUrl.authType} },`),e.description&&(t+=`
|
|
12
|
-
lambdaDescription: "${e.description}",`),e.scheduleExpression&&(t+=`
|
|
13
|
-
scheduleExpression: "${e.scheduleExpression}",`),e.architecture||e.deployment===C.CONTAINER){const a=e.architecture??w;t+=`
|
|
14
|
-
architecture: Architecture.${a},`}if(e.ephemeralStorageSize!==void 0&&(t+=`
|
|
15
|
-
ephemeralStorageSize: ${e.ephemeralStorageSize},`),e.functionName&&(t+=`
|
|
16
|
-
functionName: "${e.functionName}",`),e.ssmSecretsPath&&(t+=`
|
|
17
|
-
ssmSecretsPath: "${e.ssmSecretsPath}",`),e.ssmSecrets&&e.ssmSecrets.length>0){const a=e.ssmSecrets.map(o=>`"${o}"`).join(", ");t+=`
|
|
18
|
-
secrets: [${a}],`}if(!D(e)){const a={...A,...e.environment};t+=`
|
|
19
|
-
environment: {
|
|
20
|
-
${P(a," ")}
|
|
21
|
-
},`}return t+=y(e.extraProperties),t}function G(e,n){if(e.type!==E.LAMBDA)return"";const t=v(e),a=p(e),o=b(e);if(!t&&!a&&!o)return"";const g=t?x(n,e):[],d=a?M(n,e):[],f=o?O(n,e):[];if(g.length===0&&d.length===0&&f.length===0)return"";const c={...A},l={};if(g.length>0){const i=j(g);for(const r of i){const h=r.expression==='"true"'?"true":{__expression:r.expression};r.isSecret?l[r.key]={__expression:r.expression}:c[r.key]=h}}if(d.length>0){const i=I(d);for(const r of i)c[r.key]={__expression:r.expression}}if(f.length>0){const i=H(f);for(const r of i)c[r.key]={__expression:r.expression}}if(e.environment)for(const[i,r]of Object.entries(e.environment))c[i]=r;let s="";if(Object.keys(l).length>0){s+=`
|
|
22
|
-
containerSecretsImport: {`;for(const[i,r]of Object.entries(l))s+=`
|
|
23
|
-
${i}: ${$(r)},`;s+=`
|
|
24
|
-
},`}if(Object.keys(c).length>0){s+=`
|
|
25
|
-
environment: {`;for(const[i,r]of Object.entries(c))s+=`
|
|
26
|
-
${i}: ${$(r)},`;s+=`
|
|
27
|
-
},`}return s+=`
|
|
28
|
-
${T(g,d,f)}`,s}function Y(e){let n="";return e.instanceType&&(n+=`
|
|
29
|
-
instanceType: "${e.instanceType}",`),e.enableSSH!==void 0&&(n+=e.enableSSH?`
|
|
30
|
-
ssh: {},`:`
|
|
31
|
-
ssh: false,`),n+=y(e.extraProperties),n}function J(e){if(!e.cluster)return"";const n=[];return e.cluster.directAccess&&n.push("directAccess: true"),e.cluster.domain&&n.push(`domain: "${e.cluster.domain}"`),e.cluster.loadBalancer!==void 0&&n.push(e.cluster.loadBalancer===!1?"loadBalancer: false":`loadBalancer: "${e.cluster.loadBalancer}"`),n.length<=1?`
|
|
32
|
-
cluster: { ${n[0]||""} },`:`
|
|
33
|
-
cluster: {${n.map(a=>`
|
|
34
|
-
${a},`).join("")}
|
|
35
|
-
},`}function K(e,n,t,a,o,g){let d="";e.name&&(d+=`
|
|
36
|
-
name: "${e.name}",`),e.image&&(d+=`
|
|
37
|
-
image: "${e.image}",`),e.port!==void 0&&(d+=`
|
|
38
|
-
port: ${e.port},`),e.essential!==void 0&&(d+=`
|
|
39
|
-
essential: ${e.essential},`),e.command?.length&&(d+=`
|
|
40
|
-
command: ${JSON.stringify(e.command)},`),e.entryPoint?.length&&(d+=`
|
|
41
|
-
entryPoint: ${JSON.stringify(e.entryPoint)},`);const f=[m.HOST,m.PORT,m.NAME,m.SSL,m.USERNAME,m.PASSWORD,L.NAME,N.URL,N.ARN],c=i=>{if(!i)return i;const r={};for(const[h,u]of Object.entries(i))f.some(S=>h===S||h.startsWith(`${S}_`))||(r[h]=u);return Object.keys(r).length>0?r:void 0},l=n?{...g,...t.env,...a.env,...o.env,...e.environment}:{...g,...c(e.environment)},s=n?{...t.secrets,...e.secretsImport}:c(e.secretsImport);if(l&&Object.keys(l).length>0&&(d+=`
|
|
42
|
-
environment: {
|
|
43
|
-
${P(l," ")}
|
|
44
|
-
},`),s&&Object.keys(s).length>0&&(d+=`
|
|
45
|
-
secretsImport: {
|
|
46
|
-
${P(s," ")}
|
|
47
|
-
},`),e.ssmSecrets&&e.ssmSecrets.length>0){const i=e.ssmSecrets.map(r=>`"${r}"`).join(", ");d+=`
|
|
48
|
-
secrets: [${i}],`}return e.healthCheck&&(d+=`
|
|
49
|
-
healthCheck: ${$(e.healthCheck," ")},`),d+=y(e.extraProperties," "),d}function k(e,n){const t=[];return e.path&&t.push(`path: "${e.path}"`),e.host&&t.push(`host: "${e.host}"`),e.priority!==void 0&&t.push(`priority: ${e.priority}`),e.healthCheckPath&&t.push(`healthCheckPath: "${e.healthCheckPath}"`),t.length<=1?`{ ${t[0]||""} }`:`{${t.map(o=>`
|
|
50
|
-
${n} ${o},`).join("")}
|
|
51
|
-
${n}}`}function W(e){const n=Array.isArray(e)?e:[e];return n.length===1?`
|
|
52
|
-
routing: ${k(n[0]," ")},`:`
|
|
53
|
-
routing: [
|
|
54
|
-
${n.map(a=>k(a," ")).join(`,
|
|
55
|
-
`)},
|
|
56
|
-
],`}function q(e){if(e===!1)return`
|
|
57
|
-
scaling: false,`;if(typeof e!="object"||!(e.minCapacity!==void 0||e.maxCapacity!==void 0||e.desiredCount!==void 0||e.scalingType!==void 0))return"";const t=[];return e.minCapacity!==void 0&&t.push(`minCapacity: ${e.minCapacity}`),e.maxCapacity!==void 0&&t.push(`maxCapacity: ${e.maxCapacity}`),e.desiredCount!==void 0&&t.push(`desiredCount: ${e.desiredCount}`),e.scalingType&&t.push(`scalingType: "${e.scalingType}"`),t.length<=1?`
|
|
58
|
-
scaling: { ${t[0]||""} },`:`
|
|
59
|
-
scaling: {${t.map(o=>`
|
|
60
|
-
${o},`).join("")}
|
|
61
|
-
},`}function Q(e,n,t){return{needsDb:e.needsDatabaseConnection!==void 0?e.needsDatabaseConnection:n&&v(t),needsStorage:e.needsStorageConnection!==void 0?e.needsStorageConnection:n&&p(t),needsMessaging:e.needsMessagingConnection!==void 0?e.needsMessagingConnection:n&&b(t)}}function X(e){const{service:n,isFirstService:t,hasConnections:a,dbEnvVars:o,storageEnvVars:g,messagingEnvVars:d,baseEnvVars:f,plan:c,compute:l}=e;let s=`
|
|
62
|
-
name: "${n.name}",`;s+=`
|
|
63
|
-
capacityProvider: "${n.capacityProvider}",`,n.ec2Config&&(s+=`
|
|
64
|
-
ec2Config: ${$(n.ec2Config," ")},`),n.dockerfilePath&&(s+=`
|
|
65
|
-
dockerfilePath: "${n.dockerfilePath}",`),n.dockerTarget&&(s+=`
|
|
66
|
-
dockerTarget: "${n.dockerTarget}",`),n.image&&(s+=`
|
|
67
|
-
image: "${n.image}",`),n.ssmSecretsPath&&(s+=`
|
|
68
|
-
ssmSecretsPath: "${n.ssmSecretsPath}",`);const i=Q(n,t,l);if(n.containers?.length){s+=`
|
|
69
|
-
containers: [{`;for(let r=0;r<n.containers.length;r++){const h=n.containers[r];if(!h)continue;const u=r===0&&(i.needsDb||i.needsStorage||i.needsMessaging)&&a,V=i.needsDb?o:{env:{},secrets:{}},S=i.needsStorage?g:{env:{},secrets:{}},R=i.needsMessaging?d:{env:{},secrets:{}};r>0&&(s+=`
|
|
70
|
-
}, {`),s+=K(h,u,V,S,R,f)}s+=`
|
|
71
|
-
}],`}if(n.routing&&(s+=W(n.routing)),n.cpu!==void 0&&(s+=`
|
|
72
|
-
cpu: ${n.cpu},`),n.memoryLimitMiB!==void 0&&(s+=`
|
|
73
|
-
memoryLimitMiB: ${n.memoryLimitMiB},`),n.desiredCount!==void 0&&(s+=`
|
|
74
|
-
desiredCount: ${n.desiredCount},`),n.scaling!==void 0&&(s+=q(n.scaling)),n.alarms===!1?s+=`
|
|
75
|
-
alarms: false,`:typeof n.alarms=="object"&&(s+=`
|
|
76
|
-
alarms: ${$(n.alarms," ")},`),s+=y(n.extraProperties," "),i.needsDb||i.needsStorage||i.needsMessaging){const r=i.needsDb?x(c,l):[],h=i.needsStorage?M(c,l):[],u=i.needsMessaging?O(c,l):[];(r.length>0||h.length>0||u.length>0)&&(s+=`
|
|
77
|
-
${T(r,h,u)}`)}return s}function Z(e,n){const t=D(e),a={...A},o={env:{},secrets:{}};if(v(e)){const c=B(n,e);Object.assign(o.env,c.env),Object.assign(o.secrets,c.secrets)}const g={env:{},secrets:{}};if(p(e)){const c=U(n,e);Object.assign(g.env,c.env)}const d={env:{},secrets:{}};if(b(e)){const c=z(n,e);Object.assign(d.env,c.env)}let f=`
|
|
78
|
-
ecrRepository: app.getDefaultContainerRegistry(),`;if(f+=J(e),e.services?.length){f+=`
|
|
79
|
-
services: [{`;for(let c=0;c<e.services.length;c++){const l=e.services[c];l&&(c>0&&(f+=`
|
|
80
|
-
}, {`),f+=X({service:l,isFirstService:c===0,hasConnections:!!t,dbEnvVars:o,storageEnvVars:g,messagingEnvVars:d,baseEnvVars:a,plan:n,compute:e}))}f+=`
|
|
81
|
-
}],`}return f+=y(e.extraProperties),f+=`
|
|
82
|
-
...(getConfig().environment === "production" ? { alertsTopic: "import:SharedAlarmTopicArn" } : {}),`,f}function ie(e,n){if(e.compute.length===0)return"";let t="";for(let a=0;a<e.compute.length;a++){const o=e.compute[a],g=n.has(o.name),d=_(o),f=e.database.length>0||e.s3.length>0||a>0,c=g?`const ${d} = `:"";t+=`${f?`
|
|
83
|
-
`:""}${c}app.addCompute(
|
|
84
|
-
ComputeFactory.build("${o.name}", {
|
|
85
|
-
type: "${o.type}",`,o.type===E.LAMBDA?t+=F(o,e):o.type===E.EC2?t+=Y(o):o.type===E.ECS&&(t+=Z(o,e)),t+=G(o,e),t+=`
|
|
1
|
+
import{getVariableName as p,escapeStringLiteral as r}from"./common.js";import{generateLambdaCode as u,generateLambdaConnectionsCode as g}from"./compute/lambda.js";import{generateEc2Code as l}from"./compute/ec2.js";import{generateEcsCode as f}from"./compute/ecs.js";import{generateLambdaCode as L,generateLambdaConnectionsCode as N}from"./compute/lambda.js";import{generateEc2Code as k}from"./compute/ec2.js";import{generateEcsCode as v}from"./compute/ecs.js";function E(o,d){if(o.compute.length===0)return"";let t="";for(let n=0;n<o.compute.length;n++){const e=o.compute[n],c=d.has(e.name)||e.scheduleExpression!==void 0,a=p(e),s=o.database.length>0||o.s3.length>0||n>0,i=c?`const ${a} = `:"";switch(t+=`${s?`
|
|
2
|
+
`:""}${i}app.addCompute(
|
|
3
|
+
ComputeFactory.build(${r(e.name)}, {
|
|
4
|
+
type: ${r(e.type)},`,e.type){case"lambda":t+=u(e),t+=g(e,o);break;case"ec2":t+=l(e);break;case"ecs":t+=f(e,o);break;default:{const m=e.type;throw new Error(`Unhandled compute type: ${String(m)}`)}}t+=`
|
|
86
5
|
})
|
|
87
6
|
);
|
|
88
|
-
|
|
7
|
+
`,e.scheduleExpression!==void 0&&(t+=`app.addSchedule(${r(`${e.name}Schedule`)}, { schedule: ${r(e.scheduleExpression)}, target: ${a} });
|
|
8
|
+
`)}return t}export{E as generateComputeCode,k as generateEc2Code,v as generateEcsCode,L as generateLambdaCode,N as generateLambdaConnectionsCode};
|
|
@@ -40,6 +40,16 @@ export declare function generateDatabaseEnvVarEntries(connectedDatabases: Databa
|
|
|
40
40
|
* Format database connections array for generated infrastructure code
|
|
41
41
|
*/
|
|
42
42
|
export declare function formatConnectionsCode(connectedDatabases: DatabaseResourcePlan[]): string;
|
|
43
|
+
/**
|
|
44
|
+
* Translate a {@link DatabaseEnvVarEntry} expression into the value form the
|
|
45
|
+
* formatter consumes. `DATABASE_SSL` alone is the literal string `"true"`;
|
|
46
|
+
* everything else round-trips as a generator expression. Shared by the ECS
|
|
47
|
+
* (`buildDatabaseEnvVars`) and Lambda (`generateLambdaCode`) emitters so the
|
|
48
|
+
* two call sites cannot drift on which scalars get unquoted.
|
|
49
|
+
*/
|
|
50
|
+
export declare function toDatabaseEnvVarValue(entry: DatabaseEnvVarEntry): string | {
|
|
51
|
+
__expression: string;
|
|
52
|
+
};
|
|
43
53
|
/**
|
|
44
54
|
* Build database environment variables for a compute resource
|
|
45
55
|
*/
|
|
@@ -52,3 +62,4 @@ export declare function databaseNeedsVariable(database: DatabaseResourcePlan, pl
|
|
|
52
62
|
* Generate database infrastructure code
|
|
53
63
|
*/
|
|
54
64
|
export declare function generateDatabaseCode(plan: ApplicationResourcePlan): string;
|
|
65
|
+
export declare function generateClickHouseCode(plan: ApplicationResourcePlan): string;
|