@fjall/generator 7.0.1 → 7.2.0
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/.build-source-hash +15 -14
- package/dist/.minified +1 -1
- package/dist/src/ast/parsers/common/astStatementClassifier.d.ts +11 -1
- package/dist/src/ast/parsers/common/astStatementClassifier.js +1 -1
- package/dist/src/codemod/edits/addResource/appBinding.d.ts +12 -8
- package/dist/src/codemod/edits/addResource/appBinding.js +1 -1
- package/dist/src/codemod/edits/addResource.js +1 -1
- package/dist/src/codemod/edits/modifyResource/literalConversion.d.ts +8 -0
- package/dist/src/codemod/edits/modifyResource/literalConversion.js +1 -1
- package/dist/src/codemod/edits/modifyResource.js +1 -1
- package/dist/src/codemod/edits/unmanagedShapeErrors.js +1 -1
- package/dist/src/codemod/emitGuard.d.ts +73 -0
- package/dist/src/codemod/emitGuard.js +1 -0
- package/dist/src/codemod/index.d.ts +1 -0
- package/dist/src/codemod/index.js +1 -1
- package/dist/src/codemod/registry.d.ts +53 -7
- package/dist/src/codemod/registry.js +1 -1
- package/dist/src/codemod/semanticIndex/locateByShape.d.ts +8 -6
- package/dist/src/codemod/semanticIndex/locateByShape.js +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/package.json +2 -2
package/dist/.build-source-hash
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"package.json",
|
|
10
10
|
"tsconfig.json"
|
|
11
11
|
],
|
|
12
|
-
"hash": "
|
|
12
|
+
"hash": "0baa26e2a704915b32867832ebb1fd741502646d6d1466be1ea84a7a685d901a",
|
|
13
13
|
"files": {
|
|
14
14
|
"src/ast/astTestHelpers.ts": "026cac849eb88bdb",
|
|
15
15
|
"src/ast/domain/astDomainParser.ts": "bac0b16eb2e62552",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"src/ast/mutation/astExpressionEvaluator.ts": "6082789268b91212",
|
|
19
19
|
"src/ast/mutation/astSurgicalModification.ts": "9c512e11e8239c4d",
|
|
20
20
|
"src/ast/parsers/common/astCommonParser.ts": "d4c19453e9a75cac",
|
|
21
|
-
"src/ast/parsers/common/astStatementClassifier.ts": "
|
|
21
|
+
"src/ast/parsers/common/astStatementClassifier.ts": "d5cfd1f8d5e35132",
|
|
22
22
|
"src/ast/parsers/common/astStatementQueries.ts": "e40e31e0fcdc15e3",
|
|
23
23
|
"src/ast/parsers/orchestrator/astInfrastructureParser.ts": "07fd7a94796ebf5f",
|
|
24
24
|
"src/ast/parsers/orchestrator/astPlanConverter.ts": "eb9b1f0ab90f6999",
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
"src/codemod/drift/merge.ts": "29093929befa9abd",
|
|
42
42
|
"src/codemod/drift/snapshot.ts": "f8ef6b4097aa3d10",
|
|
43
43
|
"src/codemod/drift/types.ts": "7f1d7899e420e4d7",
|
|
44
|
-
"src/codemod/edits/addResource.ts": "
|
|
45
|
-
"src/codemod/edits/addResource/appBinding.ts": "
|
|
44
|
+
"src/codemod/edits/addResource.ts": "bad55550ecff2f72",
|
|
45
|
+
"src/codemod/edits/addResource/appBinding.ts": "3553fdb30fe06335",
|
|
46
46
|
"src/codemod/edits/addResource/bodyIndex.ts": "3e179eac10d7bcf4",
|
|
47
47
|
"src/codemod/edits/addResource/propertyBuilder.ts": "18022f8899ba4b38",
|
|
48
48
|
"src/codemod/edits/appendAccountToStage.ts": "2b92f93734497303",
|
|
@@ -52,21 +52,22 @@
|
|
|
52
52
|
"src/codemod/edits/ensureImports.ts": "3ca0b6d446c4333c",
|
|
53
53
|
"src/codemod/edits/findInsertionPosition.ts": "20cd34fdcf781017",
|
|
54
54
|
"src/codemod/edits/index.ts": "bea5ed7c3983342b",
|
|
55
|
-
"src/codemod/edits/modifyResource.ts": "
|
|
56
|
-
"src/codemod/edits/modifyResource/literalConversion.ts": "
|
|
55
|
+
"src/codemod/edits/modifyResource.ts": "578412079d63ab60",
|
|
56
|
+
"src/codemod/edits/modifyResource/literalConversion.ts": "31295cccd59131b7",
|
|
57
57
|
"src/codemod/edits/removeResource.ts": "2c7c8fe67bc3a866",
|
|
58
58
|
"src/codemod/edits/removeResource/commentHeuristic.ts": "90b6ce7ba5aa2a48",
|
|
59
59
|
"src/codemod/edits/removeResource/importPruning.ts": "f22783108b40f6cb",
|
|
60
|
-
"src/codemod/edits/unmanagedShapeErrors.ts": "
|
|
60
|
+
"src/codemod/edits/unmanagedShapeErrors.ts": "47a2644913c8e619",
|
|
61
61
|
"src/codemod/edits/vpcPeer.ts": "0bcf90c2e6d16ca4",
|
|
62
62
|
"src/codemod/edits/vpcPeerAccepter.ts": "c9eace933e9e2345",
|
|
63
|
+
"src/codemod/emitGuard.ts": "cbe270e04e1fea1a",
|
|
63
64
|
"src/codemod/fileRewriter/builders.ts": "46a0d43240ff0818",
|
|
64
65
|
"src/codemod/fileRewriter/index.ts": "fd715b41176a7999",
|
|
65
66
|
"src/codemod/fileRewriter/locateByRange.ts": "889ee6256201c1fe",
|
|
66
67
|
"src/codemod/fileRewriter/parse.ts": "b12026c31562a13c",
|
|
67
68
|
"src/codemod/fileRewriter/print.ts": "f5ff7afb5b7786bf",
|
|
68
69
|
"src/codemod/historyPaths.ts": "ef8084013146db57",
|
|
69
|
-
"src/codemod/index.ts": "
|
|
70
|
+
"src/codemod/index.ts": "6d786ef76a61fdc6",
|
|
70
71
|
"src/codemod/listResources.ts": "97f206a50edbea20",
|
|
71
72
|
"src/codemod/llmFallback/apply.ts": "2e656a6e55b3b4e3",
|
|
72
73
|
"src/codemod/llmFallback/claudeTier.ts": "0a37fe782c85f75b",
|
|
@@ -81,12 +82,12 @@
|
|
|
81
82
|
"src/codemod/llmFallback/telemetryEvents.ts": "33a85939739693ab",
|
|
82
83
|
"src/codemod/llmFallback/tierRunner.ts": "a26094accd78cf15",
|
|
83
84
|
"src/codemod/llmFallback/types.ts": "14af8d2bd9afae12",
|
|
84
|
-
"src/codemod/registry.ts": "
|
|
85
|
+
"src/codemod/registry.ts": "ab1869907bcb957b",
|
|
85
86
|
"src/codemod/semanticIndex/classifyControlFlow.ts": "ed96215d91a3cae3",
|
|
86
87
|
"src/codemod/semanticIndex/findReferences.ts": "96aea37418d1e91a",
|
|
87
88
|
"src/codemod/semanticIndex/index.ts": "cfcd212d34ab20fb",
|
|
88
89
|
"src/codemod/semanticIndex/listImports.ts": "1e5fba6e34a17dab",
|
|
89
|
-
"src/codemod/semanticIndex/locateByShape.ts": "
|
|
90
|
+
"src/codemod/semanticIndex/locateByShape.ts": "1e00f068d69858ca",
|
|
90
91
|
"src/codemod/semanticIndex/projectCache.ts": "dfd9b265c9d233c9",
|
|
91
92
|
"src/codemod/semanticIndex/resolveConstructByLiteralProperty.ts": "2d013a330af15c5c",
|
|
92
93
|
"src/codemod/telemetry/errorKinds.ts": "66c736abfa8b3a67",
|
|
@@ -156,11 +157,11 @@
|
|
|
156
157
|
"src/validation/patterns.ts": "f9629eafdad7ac3e",
|
|
157
158
|
"src/validation/validationMessages.ts": "c727ade4690274f4",
|
|
158
159
|
"src/validation/validationPatterns.ts": "865f3d688975b7c8",
|
|
159
|
-
"src/version.ts": "
|
|
160
|
+
"src/version.ts": "0e8c8d5ddae4f56f",
|
|
160
161
|
"../scripts/minify-dist.mjs": "6b2e4b0df8aec601",
|
|
161
|
-
"package.json": "
|
|
162
|
+
"package.json": "f4b1c9b80d0dbdc7",
|
|
162
163
|
"tsconfig.json": "1929cf3f99fe9b1e"
|
|
163
164
|
},
|
|
164
|
-
"outputHash": "
|
|
165
|
-
"outputFileCount":
|
|
165
|
+
"outputHash": "f4cd7140916d1965092b898aae1c66a849628b3d65177ab511f1207b09329c8f",
|
|
166
|
+
"outputFileCount": 295
|
|
166
167
|
}
|
package/dist/.minified
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
147 files minified at 2026-08-06T02:33:58.009Z
|
|
@@ -7,7 +7,17 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import * as ts from "typescript";
|
|
9
9
|
import type { AstStatementType } from "../../../schemas/resourceSchemas.js";
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Imports that the generator always handles -- don't preserve as "additional".
|
|
12
|
+
*
|
|
13
|
+
* Membership is a promise that regeneration will re-emit the import from the
|
|
14
|
+
* plan. `VpcPeerFactory` / `VpcPeerAccepterFactory` are deliberately absent:
|
|
15
|
+
* there is no plan slot and no emitter for either, so listing them dropped the
|
|
16
|
+
* import from every regenerated file that used one, leaving code that no longer
|
|
17
|
+
* compiles. They are preserved as additional managed imports instead, matching
|
|
18
|
+
* the `BuildkiteFactory` / `OrganisationFactory` treatment for the other
|
|
19
|
+
* codemod-only types.
|
|
20
|
+
*/
|
|
11
21
|
declare const KNOWN_FJALL_IMPORTS: Set<string>;
|
|
12
22
|
export { KNOWN_FJALL_IMPORTS };
|
|
13
23
|
/** Check if a module specifier belongs to a managed (Fjall/CDK) package */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var d=Object.defineProperty;var
|
|
1
|
+
var d=Object.defineProperty;var s=(e,i)=>d(e,"name",{value:i,configurable:!0});import*as a from"typescript";import{isAppGetAppCall as y}from"./astCommonParser.js";import{S3_BUCKET_CLASSES as l}from"../resources/astStorageParser.js";const g=new Set(["App","Architecture","DatabaseFactory","StorageFactory","ComputeFactory","getConfig","MessagingFactory","CdnFactory","Code","Runtime","FunctionUrlAuthType","NetworkFactory","PatternFactory","CrossPlanConnectionFactory"]);function m(e){return!!(e.startsWith("@fjall/")||e==="aws-cdk-lib"||e.startsWith("aws-cdk-lib/")||e==="constructs")}s(m,"isManagedModuleSpecifier");function N(e){const i=[];for(const t of e.statements){const r=t.getFullStart(),n=t.getEnd();if(a.isImportDeclaration(t)){i.push({type:"import",node:t,startPos:r,endPos:n,isManaged:x(t)});continue}if(a.isVariableStatement(t)){i.push(S(t,r,n));continue}if(a.isExpressionStatement(t)){i.push(F(t,r,n));continue}i.push({type:"custom",node:t,startPos:r,endPos:n,isManaged:!1})}return i}s(N,"classifyStatements");function x(e){return a.isStringLiteral(e.moduleSpecifier)?m(e.moduleSpecifier.text):!1}s(x,"isManagedImport");function S(e,i,t){for(const r of e.declarationList.declarations)if(r.initializer){if(y(r.initializer))return{type:"app-init",node:e,resourceName:a.isIdentifier(r.name)?r.name.text:void 0,startPos:i,endPos:t,isManaged:!0};if(a.isIdentifier(r.name)&&r.name.text==="appName"&&a.isStringLiteral(r.initializer))return{type:"app-init",node:e,startPos:i,endPos:t,isManaged:!0};if(a.isNewExpression(r.initializer)){const n=r.initializer;if(a.isIdentifier(n.expression)){const o=n.expression.text;if(l.has(o)){const u=n.arguments&&n.arguments.length>=2&&a.isStringLiteral(n.arguments[1])?n.arguments[1].text:void 0;return{type:"storage",node:e,resourceName:u,startPos:i,endPos:t,isManaged:!0}}}}if(a.isCallExpression(r.initializer)){const n=f(r.initializer,e,i,t);if(n.type!=="custom")return n}}return{type:"custom",node:e,startPos:i,endPos:t,isManaged:!1}}s(S,"classifyVariableStatement");function F(e,i,t){const r=e.expression;return a.isCallExpression(r)?f(r,e,i,t):{type:"custom",node:e,startPos:i,endPos:t,isManaged:!1}}s(F,"classifyExpressionStatement");function f(e,i,t,r){const n=e.expression;if(a.isPropertyAccessExpression(n)&&a.isIdentifier(n.expression)){const o=n.name.text;if(o==="addTags")return{type:"tags",node:i,startPos:t,endPos:r,isManaged:!0};const c={addDatabase:{type:"database",factory:"DatabaseFactory"},addCompute:{type:"compute",factory:"ComputeFactory"},addNetwork:{type:"network",factory:"NetworkFactory"},addMessaging:{type:"messaging",factory:"MessagingFactory"},addCdn:{type:"cdn",factory:"CdnFactory"},addPattern:{type:"pattern",factory:"PatternFactory"},addStorage:{type:"storage",factory:"StorageFactory"}}[o];if(c){const p=M(e,c.factory);return{type:c.type,node:i,resourceName:p,startPos:t,endPos:r,isManaged:!0}}}return{type:"custom",node:i,startPos:t,endPos:r,isManaged:!1}}s(f,"classifyCallExpression");function M(e,i){if(e.arguments.length===0)return;const t=e.arguments[0];if(!a.isCallExpression(t))return;const r=t.expression;if(!a.isPropertyAccessExpression(r)||r.name.text!=="build"||!a.isIdentifier(r.expression)||r.expression.text!==i||t.arguments.length===0)return;const n=t.arguments[0];if(a.isStringLiteral(n))return n.text}s(M,"extractResourceNameFromFactoryCall");export{g as KNOWN_FJALL_IMPORTS,N as classifyStatements,m as isManagedModuleSpecifier};
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import type { StatementNodeLike } from "./bodyIndex.js";
|
|
2
2
|
/**
|
|
3
|
-
* Match a `const <name> = App.getApp(...)`
|
|
4
|
-
* identifier name.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
3
|
+
* Match a `const <name> = App.getApp(...)` / `App.getInstance(...)` statement
|
|
4
|
+
* and return the bound identifier name. Both accessors return the same
|
|
5
|
+
* singleton: the application scaffold emits `App.getApp(appName, { … })` and
|
|
6
|
+
* the organisation scaffolds emit `App.getInstance()`. Single matcher shared
|
|
7
|
+
* by `classifyStatement` (which classifies the binding as an `app-init`
|
|
8
|
+
* insertion anchor) and {@link resolveAppBindingName} — the two recognitions
|
|
9
|
+
* must never drift, or wrapped emission could insert before the binding it
|
|
10
|
+
* references.
|
|
8
11
|
*/
|
|
9
12
|
export declare function matchAppGetAppBinding(stmt: StatementNodeLike): string | undefined;
|
|
10
13
|
/**
|
|
11
14
|
* Resolve the app binding identifier for wrapped emission (registry
|
|
12
|
-
* `emitWrapper`): the first `const <name> = App.getApp(...)`
|
|
13
|
-
* program body. Returns `undefined` when no
|
|
14
|
-
* surfaces `MissingAppInitError` pre-write
|
|
15
|
+
* `emitWrapper`): the first `const <name> = App.getApp(...)` (or
|
|
16
|
+
* `App.getInstance(...)`) in the program body. Returns `undefined` when no
|
|
17
|
+
* binding exists — the caller surfaces `MissingAppInitError` pre-write
|
|
18
|
+
* rather than falling back to a bare, synth-inert emission.
|
|
15
19
|
*/
|
|
16
20
|
export declare function resolveAppBindingName(body: readonly StatementNodeLike[]): string | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var u=Object.defineProperty;var f=(n,t)=>u(n,"name",{value:t,configurable:!0});import{isRecord as i}from"../../_internal.js";const l=new Set(["getApp","getInstance"]);function y(n){if(n.type!=="VariableDeclaration")return;const t=n.declarations;if(Array.isArray(t))for(const e of t){if(!i(e)||e.type!=="VariableDeclarator")continue;const o=e.init;if(!i(o)||o.type!=="CallExpression")continue;const r=o.callee;if(!i(r)||r.type!=="MemberExpression")continue;const c=r.object,a=r.property;if(!i(c)||c.type!=="Identifier"||c.name!=="App"||!i(a)||a.type!=="Identifier")continue;const s=a.name;if(typeof s!="string"||!l.has(s))continue;const p=e.id;if(!i(p)||p.type!=="Identifier")continue;const d=p.name;if(typeof d=="string")return d}}f(y,"matchAppGetAppBinding");function g(n){for(const t of n){const e=y(t);if(e!==void 0)return e}}f(g,"resolveAppBindingName");export{y as matchAppGetAppBinding,g as resolveAppBindingName};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var A=Object.defineProperty;var u=(r,e)=>A(r,"name",{value:e,configurable:!0});import{failure as a,success as g}from"../../types/Result.js";import{DEFAULT_FILE_PATH as D,computeLinesDelta as F,extractProgramBody as M,isRecord as C}from"../_internal.js";import{evaluateEmitGuard as v}from"../emitGuard.js";import{appendSpecifier as x,buildAppMethodWrappedStatement as w,buildFactoryStatement as G,buildImportDeclaration as N,detectQuoteStyle as Q,parse as B,printFile as U}from"../fileRewriter/index.js";import{STATEMENT_REGISTRY as _}from"../registry.js";import{locateAllShapes as O,locateByShape as W}from"../semanticIndex/index.js";import{checkControlFlowPolicy as Y}from"./controlFlowPolicy.js";import{driftGate as j,runPipeline as q,schemaGate as H}from"../validationGate/index.js";import{resolveAppBindingName as $}from"./addResource/appBinding.js";import{buildUnmanagedDuplicateError as z}from"./unmanagedShapeErrors.js";import{buildResourceLocations as J,indexBody as K,orderByEndPos as V,resolveInsertIndex as X}from"./addResource/bodyIndex.js";import{buildPropertyInputs as Z}from"./addResource/propertyBuilder.js";import{findInsertionPosition as ee}from"./findInsertionPosition.js";const k="@fjall/components-infrastructure";function he(r,e,m={}){const s=e.filePath??D,n=B(r,s);if(!n.success)return a(n.error);const t=W(r,{type:e.type,name:e.name},s);if(!t.success)return t.error.kind==="TemplateLiteralNameError"?a(t.error):a({kind:"SemanticQueryError",reason:t.error.reason,cause:t.error.cause});if(t.data!==void 0){if(t.data.managed===!1)return a(z(e.type,t.data.symbolName));const d=Y({ast:n.data,target:t.data,resource:{type:e.type,name:e.name},op:"add"});if(d.refusal!==void 0)return a(d.refusal);const E=q([H,j],{content:r,plan:{type:e.type,name:e.name,properties:e.properties,op:"add"},baseline:m.baseline,policy:e.driftPolicy});return E.success?E.data.action==="skip"?g({content:r,linesChanged:{added:0,removed:0},skipped:!0}):a({kind:"DuplicateResourceError",type:e.type,name:e.name}):a(re(E.error))}const l=Q(n.data),f=Z(e.properties,l);if(!f.success)return a(f.error);const c=M(n.data);if(c===void 0)return a({kind:"SemanticQueryError",reason:"recast File is missing program.body"});const o=_[e.type],h=v(o.emitGuard,e.properties);if(h!==void 0)return a(h);const I={factory:o.factoryIdentifier,name:e.name,properties:f.data,quoteStyle:l};let y;if(o.emitWrapper!==void 0){const d=$(c);if(d===void 0)return a({kind:"MissingAppInitError",type:e.type,appMethod:o.emitWrapper.appMethod});y=w({...I,appIdentifier:d,appMethod:o.emitWrapper.appMethod})}else y=G(I);const p=K(c,r),i=O(r,s);if(!i.success)return i.error.kind==="TemplateLiteralNameError"?a(i.error):a({kind:"SemanticQueryError",reason:i.error.reason,cause:i.error.cause});const P=J(p.anchors,i.data),L=V([...p.appInitLocations,...P]),R=ee(p.importInfos,L,e.type,p.programEnd),b=X(p.anchors,R);c.splice(b,0,y),te(c,o.factoryIdentifier,l);const S=U(n.data,r),T=F(r,S);return g({content:S,linesChanged:T})}u(he,"addResource");function re(r){switch(r.kind){case"ParseError":case"TemplateLiteralNameError":case"DuplicateResourceError":case"InvalidPropertyError":case"SemanticQueryError":case"MissingAppInitError":case"DriftConflictError":case"ControlFlowClassifierError":return r;default:return{kind:"SemanticQueryError",reason:`Unexpected codemod error in validation-gate pipeline: ${r.kind}`}}}u(re,"narrowGateError");function te(r,e,m){for(const n of r){if(n.type!=="ImportDeclaration")continue;const t=n.source;if(!(!C(t)||t.type!=="StringLiteral")&&t.value===k){x(n,e);return}}const s=N([e],k,m);r.unshift(s)}u(te,"ensureFactoryImport");export{he as addResource};
|
|
@@ -32,6 +32,14 @@ export type LiteralValue = unknown;
|
|
|
32
32
|
* (`vpc: app.getVpc()`).
|
|
33
33
|
*/
|
|
34
34
|
export declare function buildMergedLiteral(configObject: ObjectExpressionNodeLike, requestedProperties: Record<string, unknown>): Record<string, unknown>;
|
|
35
|
+
/**
|
|
36
|
+
* Every key the object declares, whether or not its value reduced to a
|
|
37
|
+
* literal. `buildMergedLiteral` drops non-literal values so `.strict()`
|
|
38
|
+
* fragment validation does not reject real call sites; the emit guard needs
|
|
39
|
+
* the fuller picture — a key holding `Code.fromAsset("./src")` is PRESENT,
|
|
40
|
+
* and proves the statement was authored in construct vocabulary.
|
|
41
|
+
*/
|
|
42
|
+
export declare function collectPropertyKeys(configObject: ObjectExpressionNodeLike): string[];
|
|
35
43
|
export declare function astToLiteral(node: NodeShapeLike): LiteralValue | typeof NON_LITERAL;
|
|
36
44
|
export declare function readKeyName(key: NodeShapeLike | undefined): string | undefined;
|
|
37
45
|
export declare function isNodeShape(value: unknown): value is NodeShapeLike;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var y=Object.defineProperty;var u=(e,r)=>y(e,"name",{value:r,configurable:!0});const o=Symbol("non-literal");function L(e,r){const n={};for(const t of e.properties){if(t.type!=="Property"&&t.type!=="ObjectProperty"||t.shorthand===!0||t.computed===!0)continue;const i=c(t.key);if(i===void 0||t.value===void 0)continue;const s=p(t.value);s!==o&&(n[i]=s)}return{...n,...r}}u(L,"buildMergedLiteral");function b(e){const r=[];for(const n of e.properties){if(n.type!=="Property"&&n.type!=="ObjectProperty"||n.shorthand===!0||n.computed===!0)continue;const t=c(n.key);t!==void 0&&r.push(t)}return r}u(b,"collectPropertyKeys");function p(e){const r=e.value;switch(e.type){case"StringLiteral":return typeof r=="string"?r:o;case"NumericLiteral":return typeof r=="number"?r:o;case"BooleanLiteral":return typeof r=="boolean"?r:o;case"NullLiteral":return null;case"ArrayExpression":return l(e);case"ObjectExpression":return d(e);default:return o}}u(p,"astToLiteral");function l(e){const r=e.elements;if(!Array.isArray(r))return o;const n=[];for(const t of r){if(!a(t))return o;const i=p(t);if(i===o)return o;n.push(i)}return n}u(l,"arrayToLiteral");function d(e){const r=e.properties;if(!Array.isArray(r))return o;const n={};for(const t of r){if(!a(t)||t.type!=="Property"&&t.type!=="ObjectProperty")return o;const i=c(t.key),s=t.value;if(i===void 0||s===void 0)return o;const f=p(s);if(f===o)return o;n[i]=f}return n}u(d,"objectToLiteral");function c(e){if(e===void 0)return;const r=e;if(e.type==="Identifier"&&typeof r.name=="string")return r.name;if(e.type==="StringLiteral"&&typeof r.value=="string")return r.value}u(c,"readKeyName");function a(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)&&typeof e.type=="string"}u(a,"isNodeShape");export{o as NON_LITERAL,p as astToLiteral,L as buildMergedLiteral,b as collectPropertyKeys,a as isNodeShape,c as readKeyName};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var T=Object.defineProperty;var c=(r,e)=>T(r,"name",{value:e,configurable:!0});import{failure as s,success as m}from"../../types/Result.js";import{DEFAULT_FILE_PATH as A,computeLinesDelta as F,extractProgramBody as L,isRecord as y}from"../_internal.js";import{buildObjectProperty as j,detectQuoteStyle as G,parse as O,printFile as Q}from"../fileRewriter/index.js";import{STATEMENT_REGISTRY as P}from"../registry.js";import{locateAllShapes as U,locateByShape as I}from"../semanticIndex/index.js";import{buildUnmanagedEditRefusal as D}from"./unmanagedShapeErrors.js";import{checkControlFlowPolicy as K}from"./controlFlowPolicy.js";import{driftGate as V,runPipeline as B,schemaGate as _}from"../validationGate/index.js";import{toAstValue as M}from"./addResource/propertyBuilder.js";import{buildMergedLiteral as v,collectPropertyKeys as $,isNodeShape as S,readKeyName as H}from"./modifyResource/literalConversion.js";import{evaluateEmitGuard as w,UNRESOLVED as Y}from"../emitGuard.js";function pe(r,e,t={}){const n=e.filePath??A,a=O(r,n);if(!a.success)return s(a.error);const i=I(r,{type:e.type,name:e.name},n);if(!i.success)return i.error.kind==="TemplateLiteralNameError"?s(i.error):s({kind:"SemanticQueryError",reason:i.error.reason,cause:i.error.cause});if(i.data===void 0)return s({kind:"ResourceNotFoundError",type:e.type,name:e.name,knownNames:Z(r,e.type,n)});if(i.data.managed===!1)return s(D(e.type,"modify"));const o=K({ast:a.data,target:i.data,resource:{type:e.type,name:e.name},op:"modify"});if(o.refusal!==void 0)return s(o.refusal);const u=B([_,V],{content:r,plan:{type:e.type,name:e.name,properties:e.properties,op:"modify"},baseline:t.baseline,policy:e.driftPolicy});if(!u.success)return s(q(u.error));if(u.data.action==="skip")return m({content:r,linesChanged:{added:0,removed:0},skipped:!0});const d=L(a.data);if(d===void 0)return s({kind:"SemanticQueryError",reason:"recast File is missing program.body"});const f=z(d,i.data);if(f===void 0)return s({kind:"SemanticQueryError",reason:"Unable to resolve config ObjectExpression for the located resource."});const N=G(a.data),g=v(f,e.properties),E=P[e.type].emitGuard,R=w(E,C(f,v(f,{}))),p=w(E,C(f,g));if(p!==void 0&&p.property!==R?.property)return s(p);const h=P[e.type].schemaFragment.safeParse(g);if(!h.success){const l=h.error.issues[0];return s({kind:"InvalidPropertyError",property:l!==void 0&&l.path.length>0?String(l.path[0]):Object.keys(e.properties)[0]??"<unknown>",reason:l?.message??"Schema validation failed"})}const k=J(f,e.properties,N);if(!k.success)return s(k.error);const b=Q(a.data,r),x=o.warning!==void 0?[o.warning.message]:void 0;return m({content:b,linesChanged:F(r,b),warnings:x})}c(pe,"modifyResource");function C(r,e){const t={...e};for(const n of $(r))n in t||(t[n]=Y);return t}c(C,"buildGuardView");function q(r){switch(r.kind){case"ParseError":case"ResourceNotFoundError":case"InvalidPropertyError":case"TemplateLiteralNameError":case"SemanticQueryError":case"DriftConflictError":case"ControlFlowClassifierError":return r;default:return{kind:"SemanticQueryError",reason:`Unexpected codemod error in validation-gate pipeline: ${r.kind}`}}}c(q,"narrowGateError");function z(r,e){const t=e.start+e.length;let n;const a=c(i=>{if(n!==void 0||i.type!=="CallExpression")return;const o=i.arguments;if(!Array.isArray(o)||o.length<2)return;const u=o[0];if(!S(u)||u.type!=="StringLiteral"||u.start!==e.start||u.end!==t)return;const d=o[1];S(d)&&d.type==="ObjectExpression"&&(n=d)},"visitor");for(const i of r)if(ee(i,a),n!==void 0)break;return n}c(z,"findConfigObject");function J(r,e,t){const n=X(r);for(const[a,i]of Object.entries(e)){const o=M(i,t);if(o===void 0)return s({kind:"InvalidPropertyError",property:a,reason:`Unsupported property value for "${a}" (accepts string/number/boolean/null/array/object of the same).`});const u=W(r,a);if(u!==void 0){u.value=o;continue}const d=j(a,o,t);n&&(d.extra={...d.extra??{},trailingComma:!0}),r.properties.push(d)}return m(void 0)}c(J,"applyPropertyEdits");function W(r,e){return r.properties.find(t=>(t.type==="Property"||t.type==="ObjectProperty")&&t.shorthand!==!0&&t.computed!==!0&&H(t.key)===e)}c(W,"findPropertyByKey");function X(r){return r.properties[r.properties.length-1]?.extra?.trailingComma===!0}c(X,"detectTrailingCommaStyle");function Z(r,e,t){const n=U(r,t);return n.success?n.data.filter(a=>a.type===e).map(a=>a.symbolName):[]}c(Z,"collectKnownNames");function ee(r,e){const t=[r];for(;t.length>0;){const n=t.pop();if(y(n)){typeof n.type=="string"&&e(n);for(const a of Object.keys(n)){if(a==="loc"||a==="comments"||a==="tokens")continue;const i=n[a];if(Array.isArray(i))for(const o of i)y(o)&&t.push(o);else y(i)&&t.push(i)}}}}c(ee,"visit");export{pe as modifyResource};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var o=Object.defineProperty;var r=(t,e)=>o(t,"name",{value:e,configurable:!0});import{STATEMENT_REGISTRY as m}from"../registry.js";function i(t){const{emitWrapper:e,factoryIdentifier:n}=m[t];if(e===void 0)return`${n}.build("<Name>", { \u2026 })`;const a=e.propsOverloadConstructId??"<Name>";return`app.${e.appMethod}(${n}.build("${a}", { \u2026 }))`}r(i,"managedFormExample");function c(t,e){return{kind:"DuplicateResourceError",type:t,name:e,remediation:`The existing statement is the unmanaged method form \u2014 migrate it to ${i(t)} before managing it with fjall.`}}r(c,"buildUnmanagedDuplicateError");function f(t,e){return{kind:"SemanticQueryError",reason:`cannot ${e} "${t}": the statement is not in the managed factory form \u2014 migrate it to ${i(t)} first`}}r(f,"buildUnmanagedEditRefusal");export{c as buildUnmanagedDuplicateError,f as buildUnmanagedEditRefusal};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { InvalidPropertyError } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* A value the codemod could not have written: the property is present in
|
|
4
|
+
* the target statement but holds a non-literal expression (`Code.fromAsset(
|
|
5
|
+
* "./src")`, `Runtime.NODEJS_20_X`, `assetsBucket`).
|
|
6
|
+
*
|
|
7
|
+
* Its presence is PROOF the statement was authored by a human or by the
|
|
8
|
+
* generator scaffold, both of which speak construct vocabulary. Rules that
|
|
9
|
+
* refuse a key because the construct needs a live object must therefore
|
|
10
|
+
* treat `UNRESOLVED` as satisfying the construct — otherwise `fjall modify`
|
|
11
|
+
* would refuse to touch perfectly good scaffold output. The ADD path never
|
|
12
|
+
* produces it: every value there comes from a flag.
|
|
13
|
+
*/
|
|
14
|
+
export declare const UNRESOLVED: unique symbol;
|
|
15
|
+
/**
|
|
16
|
+
* The property record the emitted statement WOULD carry. `add` passes the
|
|
17
|
+
* requested properties; `modify` passes the merge of the existing statement
|
|
18
|
+
* and the requested edit, with non-literal values as `UNRESOLVED`, because
|
|
19
|
+
* that merge is exactly what lands in the file. Evaluating the post-write
|
|
20
|
+
* shape (rather than the delta) is what lets `fjall modify compute
|
|
21
|
+
* --deployment container` repair a statement the guard would otherwise
|
|
22
|
+
* refuse to touch, and what stops a whole-statement rule such as "messaging
|
|
23
|
+
* has no `type`" from firing on an unrelated one-property edit.
|
|
24
|
+
*/
|
|
25
|
+
export type EmitGuardProperties = Readonly<Record<string, unknown>>;
|
|
26
|
+
/**
|
|
27
|
+
* One refusal. `property` names the plan key the operator typed (or the one
|
|
28
|
+
* whose absence is fatal) so the rendered `InvalidPropertyError` points at a
|
|
29
|
+
* real flag; `reason` must say WHY and what to do instead.
|
|
30
|
+
*/
|
|
31
|
+
export interface EmitGuardRule {
|
|
32
|
+
readonly property: string;
|
|
33
|
+
readonly refuseWhen: (properties: EmitGuardProperties) => boolean;
|
|
34
|
+
readonly reason: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Standard tail for a refusal with no in-vocabulary alternative.
|
|
38
|
+
*
|
|
39
|
+
* It deliberately does NOT name a construct-prop spelling that would work
|
|
40
|
+
* as an unvalidated passthrough (`fjall add` performs no property-key
|
|
41
|
+
* validation today, so `--memorySize` or `--originType` reach the emitted
|
|
42
|
+
* object untouched). Advertising those would document a surface no schema
|
|
43
|
+
* checks and no test pins, and it would go stale the moment the schema gate
|
|
44
|
+
* stops being a no-op. Hand-authoring is always correct.
|
|
45
|
+
*/
|
|
46
|
+
export declare const AUTHOR_BY_HAND: string;
|
|
47
|
+
/** True when the key holds a value the codemod itself could have written. */
|
|
48
|
+
export declare function isWrittenValue(value: unknown): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* A rule for a plan key NO construct reads, in any shape — proven by zero
|
|
51
|
+
* `props.<key>` reads across @fjall/components-infrastructure. Presence
|
|
52
|
+
* alone is the refusal; the value cannot rescue it.
|
|
53
|
+
*
|
|
54
|
+
* Phrased as a statement of consequence, because the operator's mental model
|
|
55
|
+
* is "I set it, so it took effect" and the whole point is that it did not.
|
|
56
|
+
*/
|
|
57
|
+
export declare function silentlyDropped(property: string, detail: string): EmitGuardRule;
|
|
58
|
+
/**
|
|
59
|
+
* A rule for a key the construct DOES read but only as a live object — a
|
|
60
|
+
* CDK enum member, an asset, another construct. A flag value is a string,
|
|
61
|
+
* so any value the codemod can write is wrong; only `UNRESOLVED` passes.
|
|
62
|
+
*/
|
|
63
|
+
export declare function requiresLiveValue(property: string, detail: string): EmitGuardRule;
|
|
64
|
+
/** Refuse whenever the key is missing (the missing-discriminator shape). */
|
|
65
|
+
export declare function whenAbsent(property: string): (properties: EmitGuardProperties) => boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Evaluate a rule table in declaration order and return the first refusal
|
|
68
|
+
* as the codebase's existing actionable error shape. Order is significant:
|
|
69
|
+
* a type lists its most explanatory rule first, so an invocation tripping
|
|
70
|
+
* several (`--deployment code --codePath ./src --runtime nodejs20.x`) is
|
|
71
|
+
* told about the deployment mode rather than about a single dead flag.
|
|
72
|
+
*/
|
|
73
|
+
export declare function evaluateEmitGuard(rules: ReadonlyArray<EmitGuardRule> | undefined, properties: EmitGuardProperties): InvalidPropertyError | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var u=Object.defineProperty;var r=(e,n)=>u(e,"name",{value:n,configurable:!0});const i=Symbol("unresolved-expression"),o="Write this resource in infrastructure.ts by hand \u2014 the construct props it needs cannot be expressed as `fjall add` flags.";function d(e){return e!==void 0&&e!==i}r(d,"isWrittenValue");function f(e,n){return{property:e,refuseWhen:r(t=>t[e]!==void 0,"refuseWhen"),reason:`${n} \`fjall add\` would write it into the factory call and the resource would deploy without it. ${o}`}}r(f,"silentlyDropped");function a(e,n){return{property:e,refuseWhen:r(t=>d(t[e]),"refuseWhen"),reason:`${n} ${o}`}}r(a,"requiresLiveValue");function l(e){return n=>n[e]===void 0}r(l,"whenAbsent");function c(e,n){if(e!==void 0){for(const t of e)if(t.refuseWhen(n))return{kind:"InvalidPropertyError",property:t.property,reason:t.reason}}}r(c,"evaluateEmitGuard");export{o as AUTHOR_BY_HAND,i as UNRESOLVED,c as evaluateEmitGuard,d as isWrittenValue,a as requiresLiveValue,f as silentlyDropped,l as whenAbsent};
|
|
@@ -15,6 +15,7 @@ export { detectDrift, mergeProperties, snapshotProperties, type DriftOp, type Dr
|
|
|
15
15
|
export { computeLinesDelta } from "./_internal.js";
|
|
16
16
|
export { ResourceNameSchema, StatementTypeSchema } from "./types.js";
|
|
17
17
|
export { REGISTERED_STATEMENT_TYPES, STATEMENT_REGISTRY, type StatementTypeEntry, } from "./registry.js";
|
|
18
|
+
export { evaluateEmitGuard, UNRESOLVED, type EmitGuardProperties, type EmitGuardRule, } from "./emitGuard.js";
|
|
18
19
|
export { CODEMOD_ERROR_KINDS } from "./telemetry/errorKinds.js";
|
|
19
20
|
export type { CodemodErrorKind } from "./telemetry/errorKinds.js";
|
|
20
21
|
export type { GateId } from "./validationGate/index.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{addResource as o}from"./edits/addResource.js";import{removeResource as c}from"./edits/removeResource.js";import{modifyResource as m}from"./edits/modifyResource.js";import{appendAccountToStage as
|
|
1
|
+
import{addResource as o}from"./edits/addResource.js";import{removeResource as c}from"./edits/removeResource.js";import{modifyResource as m}from"./edits/modifyResource.js";import{appendAccountToStage as s}from"./edits/appendAccountToStage.js";import{addVpcPeer as d,modifyVpcPeer as E,removeVpcPeer as i}from"./edits/vpcPeer.js";import{addVpcPeerAccepter as f,modifyVpcPeerAccepter as u,removeVpcPeerAccepter as P}from"./edits/vpcPeerAccepter.js";import{addCrossPlanConnection as R,modifyCrossPlanConnection as T,removeCrossPlanConnection as S}from"./edits/crossPlanConnection.js";import{resolveDriftPolicy as A}from"./edits/driftPolicy.js";import{listResources as V}from"./listResources.js";import{resolveConstructByLiteralProperty as y}from"./semanticIndex/index.js";import{parse as D}from"./fileRewriter/parse.js";import{CrossPlanConnectionResourcePlanSchema as N,VpcPeerAccepterResourcePlanSchema as h,VpcPeerResourcePlanSchema as F}from"../schemas/index.js";import{detectDrift as I,mergeProperties as M,snapshotProperties as O}from"./drift/index.js";import{computeLinesDelta as k}from"./_internal.js";import{ResourceNameSchema as U,StatementTypeSchema as K}from"./types.js";import{REGISTERED_STATEMENT_TYPES as j,STATEMENT_REGISTRY as q}from"./registry.js";import{evaluateEmitGuard as z,UNRESOLVED as H}from"./emitGuard.js";import{CODEMOD_ERROR_KINDS as Q}from"./telemetry/errorKinds.js";import{buildEgressBlockedEvent as X,buildFiredEvent as Z,buildGateFailedEvent as $,buildGatePassedEvent as ee,buildRejectedEvent as re,buildSucceededEvent as oe,buildTimeoutEvent as te,estimateCostUsd as ce,FALLBACK_EVENTS as pe,GATE_EVENTS as me,PARSE_GATE as ae,RUNTIME_GATE as se,runFallback as ne,shouldTryFallback as de}from"./llmFallback/index.js";export{Q as CODEMOD_ERROR_KINDS,N as CrossPlanConnectionResourcePlanSchema,pe as FALLBACK_EVENTS,me as GATE_EVENTS,ae as PARSE_GATE,j as REGISTERED_STATEMENT_TYPES,se as RUNTIME_GATE,U as ResourceNameSchema,q as STATEMENT_REGISTRY,K as StatementTypeSchema,H as UNRESOLVED,h as VpcPeerAccepterResourcePlanSchema,F as VpcPeerResourcePlanSchema,R as addCrossPlanConnection,o as addResource,d as addVpcPeer,f as addVpcPeerAccepter,s as appendAccountToStage,X as buildEgressBlockedEvent,Z as buildFiredEvent,$ as buildGateFailedEvent,ee as buildGatePassedEvent,re as buildRejectedEvent,oe as buildSucceededEvent,te as buildTimeoutEvent,k as computeLinesDelta,I as detectDrift,ce as estimateCostUsd,z as evaluateEmitGuard,V as listResources,M as mergeProperties,T as modifyCrossPlanConnection,m as modifyResource,E as modifyVpcPeer,u as modifyVpcPeerAccepter,D as parse,S as removeCrossPlanConnection,c as removeResource,i as removeVpcPeer,P as removeVpcPeerAccepter,y as resolveConstructByLiteralProperty,A as resolveDriftPolicy,ne as runFallback,de as shouldTryFallback,O as snapshotProperties};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { type Result } from "../types/Result.js";
|
|
3
|
+
import { type EmitGuardRule } from "./emitGuard.js";
|
|
3
4
|
import type { NodeLocation, SemanticQueryError, StatementType, TemplateLiteralNameError } from "./types.js";
|
|
4
5
|
export type LocatorError = TemplateLiteralNameError | SemanticQueryError;
|
|
5
6
|
export interface StatementLocator {
|
|
@@ -29,12 +30,12 @@ export interface StatementTypeEntry {
|
|
|
29
30
|
/**
|
|
30
31
|
* When present, the ADD path emits `app.<appMethod>(XFactory.build(...))`
|
|
31
32
|
* instead of the bare factory call. MANDATORY for any type whose factory
|
|
32
|
-
* returns a thunk that is only materialised by an App method
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* parity test and the
|
|
37
|
-
* method-existence test.
|
|
33
|
+
* returns a thunk that is only materialised by an App method — `cdk.json`
|
|
34
|
+
* executes `infrastructure.ts` directly, so an unmaterialised thunk
|
|
35
|
+
* synthesises nothing at all and a bare emission is silently inert.
|
|
36
|
+
* Absent = bare emission, correct only for a factory that constructs
|
|
37
|
+
* eagerly. Contract pinned by the registry parity test and the
|
|
38
|
+
* components-infrastructure `App.prototype` method-existence test.
|
|
38
39
|
*
|
|
39
40
|
* `propsOverloadConstructId` is the construct id the App method pins when
|
|
40
41
|
* called in the legacy props-direct form (`app.addBuildkite({ … })`) — it
|
|
@@ -42,11 +43,56 @@ export interface StatementTypeEntry {
|
|
|
42
43
|
* value with no compiler across the seam; proven end-to-end by the
|
|
43
44
|
* dogfood synth-parity AC). The legacy-shape classifier uses it to name
|
|
44
45
|
* unmanaged method-form hits (design § D6).
|
|
46
|
+
*
|
|
47
|
+
* It is OPTIONAL because `addBuildkite` is the only App add-method with a
|
|
48
|
+
* props-direct overload; every other one takes a factory thunk and nothing
|
|
49
|
+
* else. Absent means "no props-direct form exists", which withholds the
|
|
50
|
+
* method name from the unmanaged classifier entirely. Inventing a value
|
|
51
|
+
* would make the symmetric idiom (`const b = StorageFactory.build(…);
|
|
52
|
+
* app.addStorage(b);`) and any foreign receiver's `.addStorage(...)`
|
|
53
|
+
* report as phantom unmanaged resources, and would offer migration
|
|
54
|
+
* guidance towards a form the App class does not accept.
|
|
45
55
|
*/
|
|
46
56
|
emitWrapper?: {
|
|
47
57
|
readonly appMethod: string;
|
|
48
|
-
readonly propsOverloadConstructId
|
|
58
|
+
readonly propsOverloadConstructId?: string;
|
|
49
59
|
};
|
|
60
|
+
/**
|
|
61
|
+
* Property shapes the ADD and MODIFY paths must REFUSE to write, checked
|
|
62
|
+
* before any file is touched. Declared here, beside `emitWrapper`, because
|
|
63
|
+
* `emitWrapper` is precisely what creates the hazard: wrapping makes the
|
|
64
|
+
* emitted props load-bearing at synth, and the codemod writes resource-PLAN
|
|
65
|
+
* vocabulary verbatim into a file the constructs read as CONSTRUCT
|
|
66
|
+
* vocabulary. The generator scaffold has a translation layer for that seam
|
|
67
|
+
* (`generation/compute/lambda.ts` turns `codePath` into
|
|
68
|
+
* `Code.fromAsset(...)`); the codemod has none, so where the two
|
|
69
|
+
* vocabularies disagree the statement either takes the whole app's synth
|
|
70
|
+
* down or deploys infrastructure that is not what was asked for.
|
|
71
|
+
*
|
|
72
|
+
* Rules cover exactly two classes, and nothing else:
|
|
73
|
+
*
|
|
74
|
+
* · UNTRANSLATABLE — the construct requires a value no CLI flag can
|
|
75
|
+
* carry (a CDK `Code`/`Architecture`, an `IBucket`, a live sibling
|
|
76
|
+
* construct). No invocation can ever synthesise, so writing one is
|
|
77
|
+
* always wrong.
|
|
78
|
+
* · SILENTLY DROPPED — the construct never reads the plan key, so the
|
|
79
|
+
* resource deploys without the behaviour that was requested. Proven
|
|
80
|
+
* per key: zero `props.<key>` reads anywhere in
|
|
81
|
+
* @fjall/components-infrastructure.
|
|
82
|
+
*
|
|
83
|
+
* Deliberately NOT covered: INCOMPLETE input, where the vocabulary can
|
|
84
|
+
* express a working call and the operator has not yet. That fails loudly
|
|
85
|
+
* at synth naming the missing prop and is accepted behaviour — `fjall add
|
|
86
|
+
* buildkite` with no flags has always thrown `Buildkite: invalid props`.
|
|
87
|
+
* Nor is this general schema validation: unknown keys still pass straight
|
|
88
|
+
* through (a separate, recorded gap).
|
|
89
|
+
*
|
|
90
|
+
* Absent = every expressible property shape for this type reaches a
|
|
91
|
+
* synthesising statement. That is a claim, not an oversight: `network`,
|
|
92
|
+
* `vpc-peer` and `buildkite` each have full plan/construct parity, and the
|
|
93
|
+
* components-infrastructure materialisation contract pins it.
|
|
94
|
+
*/
|
|
95
|
+
emitGuard?: ReadonlyArray<EmitGuardRule>;
|
|
50
96
|
}
|
|
51
97
|
declare const FACTORY_IDENTIFIERS: {
|
|
52
98
|
readonly database: "DatabaseFactory";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var v=Object.defineProperty;var a=(e,t)=>v(e,"name",{value:t,configurable:!0});import{z as u}from"zod";import{BuildkitePropsObjectSchema as E,CDNResourcePlanSchema as R,ComputeResourcePlanSchema as k,CrossPlanConnectionResourcePlanSchema as T,DatabaseResourcePlanSchema as w,NetworkResourcePlanSchema as A,NextJSPatternConfigSchema as F,OrganisationResourcePlanSchema as N,PayloadPatternConfigSchema as M,S3ResourcePlanSchema as D,SQSResourcePlanSchema as x,StaticSitePatternConfigSchema as G,VpcPeerAccepterResourcePlanSchema as W,VpcPeerResourcePlanSchema as _}from"../schemas/index.js";import{failure as O}from"../types/Result.js";import{isRecord as I}from"./_internal.js";import{AUTHOR_BY_HAND as c,requiresLiveValue as f,silentlyDropped as n,UNRESOLVED as h,whenAbsent as p}from"./emitGuard.js";const r={database:"DatabaseFactory",storage:"StorageFactory",compute:"ComputeFactory",messaging:"MessagingFactory",cdn:"CdnFactory",network:"NetworkFactory",pattern:"PatternFactory","vpc-peer":"VpcPeerFactory","vpc-peer-accepter":"VpcPeerAccepterFactory","cross-plan-connection":"CrossPlanConnectionFactory",organisation:"OrganisationFactory",buildkite:"BuildkiteFactory"};function s(e){const{name:t,...i}=e.shape;return u.object(i).partial().strict()}a(s,"fragmentOf");const B=s(w),j=s(D),q=(()=>{const{name:e,...t}=k.shape;return u.object(t).partial().strict()})(),L=s(x),V=s(R),Y=s(A),K=(()=>{const{name:e,type:t,cdn:i,...b}=M.shape,{name:se,type:ie,cdn:ce,...P}=F.shape,{name:pe,type:de,domain:ue,cdn:S,...C}=G.shape;return u.object({...b,...P,...C,cdn:u.union([i,S])}).partial().strict()})(),U=s(_),z=s(W),H=s(T),Q=s(N),$=s(E);function g(e,t){return O({kind:"SemanticQueryError",reason:`StatementTypeEntry.${e} for ${t} is not wired; existing types dispatch through the shared locator/generator.`})}a(g,"notWiredError");function J(e){return{factoryIdentifier:e,findByShape:a(()=>g("locator.findByShape",e),"findByShape"),validateContext:a(()=>g("locator.validateContext",e),"validateContext")}}a(J,"createLocator");function X(e){return{build:a(()=>{const t=g("generator.build",e);throw new Error(t.success?"unreachable":t.error.reason)},"build")}}a(X,"createGenerator");const m=n("variableName","`variableName` is a plan-only round-trip key naming the `const <var> =` binding the scaffold emits; no construct reads it."),d=n("extraProperties","`extraProperties` is a plan-only round-trip key: the scaffold flattens each `{ key, sourceText }` entry into a sibling property, and no construct reads the array."),Z=[n("engineExpression","`engineExpression` carries a raw CDK engine expression for the scaffold to inline; the construct reads `databaseEngine` instead, so a cluster pinned to `DatabaseClusterEngine.auroraMysql(...)` still deploys as aurora-postgresql."),m,d],ee=[m,d],l="the connection keys are scaffold inputs, not construct props: the scaffold expands them into environment variables and a `connections: [...]` array, and the compute constructs read none of them.",te=[{property:"deployment",refuseWhen:a(e=>(e.type==="lambda"||e.deployment!==void 0)&&e.deployment!=="container"&&e.code!==h,"refuseWhen"),reason:"a code-deployed Lambda requires `code: Code`, a CDK object built by `Code.fromAsset(...)` that no flag value can carry, so the statement takes the whole app's synth down with `Cannot read properties of undefined (reading 'bind')` \u2014 naming no resource. Use `--deployment container`, which is fully expressible, or write this compute in infrastructure.ts by hand."},n("codePath","`codePath` is the scaffold's input to `Code.fromAsset(...)`; no compute construct reads it, and a container Lambda takes its code from the image."),f("runtime","the construct expects a CDK `Runtime`, not a version string, and a container Lambda takes its runtime from the image (`Runtime.FROM_IMAGE`) regardless."),f("architecture","the construct expects a CDK `Architecture`, not a string: `--architecture ARM_64` emits an EMPTY `Architectures` list, whereas omitting the flag emits the correct default."),n("memory","the Lambda construct reads `memorySize`, so `--memory 1024` deploys a function at the 128 MB default."),n("ssmSecrets","the Lambda construct reads `secrets`, so `--ssmSecrets` deploys a function with no SSM wiring and no secrets environment at all."),n("scheduleExpression","`scheduleExpression` is not a compute prop: the scaffold turns it into a SEPARATE `app.addSchedule(...)` statement, which this command cannot emit."),n("needsConnection",l),n("connectedDatabase",l),n("connectedStorage",l),n("connectedMessaging",l),m,d],ae=[{property:"type",refuseWhen:p("type"),reason:"`MessagingFactory.build` selects queue/topic/eventBus from a `type` discriminator that the messaging vocabulary does not carry, so a queue built from messaging properties throws `Unsupported messaging type: undefined` the moment `app.addMessaging` materialises it. "+c},n("retentionPeriod","the queue construct reads `messageRetentionPeriod`, so `--retentionPeriod` deploys a queue at the 4-day default."),m,d],ne=[{property:"originType",refuseWhen:p("originType"),reason:"`Cdn` selects its origin implementation from an `originType` discriminator that the CDN vocabulary does not carry, so a distribution built from CDN properties throws `Unsupported CDN origin type: undefined` the moment `app.addCdn` materialises it. "+c},{property:"originType",refuseWhen:a(e=>e.originType==="s3"&&e.bucket!==h,"refuseWhen"),reason:"an s3 origin needs `bucket` to be an `IBucket` or a Fjall `Storage` \u2014 a live construct, not a name, and not a string; omitting it throws `Cannot read properties of undefined (reading 'bucketRegionalDomainName')` at synth. "+c},{property:"originType",refuseWhen:a(e=>e.originType==="alb"&&e.loadBalancer!==h,"refuseWhen"),reason:'an alb origin needs `loadBalancer` to be an `IApplicationLoadBalancer` or an ECS compute \u2014 a live construct, not a name, and not a string; a string reaches CloudFormation as `Supplied properties not correct for "OriginProperty"`. '+c},{property:"behaviours",refuseWhen:a(e=>Array.isArray(e.behaviours)&&e.behaviours.some(t=>I(t)&&t.originRef!==void 0),"refuseWhen"),reason:"the plan's `behaviours[].originRef` names another planned resource, while the construct's behaviour takes a live origin object; the plan shape throws `Cannot read properties of undefined (reading 'computeType')` at synth. "+c},n("customDomain","the distribution reads `domainNames`, so `--customDomain` deploys a distribution with no alias."),n("defaultOriginRef","`defaultOriginRef` names another planned resource for the scaffold to resolve into a live origin; no CDN construct reads it."),d],re=[{property:"name",refuseWhen:p("name"),reason:"every pattern construct requires a `name` PROPERTY in addition to its construct id, and `fjall add --name` binds the construct id, so no invocation can deliver it \u2014 `toPascalCase(props.name)` throws at synth and takes the whole app down. "+c},{property:"type",refuseWhen:p("type"),reason:"`PatternFactory.build` reads `PATTERN_REGISTRY[props.type]` before it even returns the thunk, and the pattern vocabulary carries no `type` discriminator. "+c}],oe=[{property:"peerAppName",refuseWhen:p("peerAppName"),reason:"`peerAppName` is required: it is the SSM prefix every remote lookup is built from, and omitting it does NOT fail \u2014 synth succeeds and the app deploys a peering connection pointed at `/fjall/default/undefined/vpc-id`. Pass `--peerAppName <remote app>`."},{property:"peerAccountId",refuseWhen:p("peerAccountId"),reason:"`peerAccountId` is required: omitting it does NOT fail, it deploys a VPC peering connection with no `PeerOwnerId`. Pass `--peerAccountId <12-digit account>`."}];function o(e,t,i={}){return{factoryIdentifier:e,locator:J(e),generator:X(e),schemaFragment:t,...i}}a(o,"createEntry");const y={database:o(r.database,B,{emitWrapper:{appMethod:"addDatabase"},emitGuard:Z}),storage:o(r.storage,j,{emitWrapper:{appMethod:"addStorage"},emitGuard:ee}),compute:o(r.compute,q,{emitWrapper:{appMethod:"addCompute"},emitGuard:te}),messaging:o(r.messaging,L,{emitWrapper:{appMethod:"addMessaging"},emitGuard:ae}),cdn:o(r.cdn,V,{emitWrapper:{appMethod:"addCdn"},emitGuard:ne}),network:o(r.network,Y,{emitWrapper:{appMethod:"addNetwork"}}),pattern:o(r.pattern,K,{emitWrapper:{appMethod:"addPattern"},emitGuard:re}),"vpc-peer":o(r["vpc-peer"],U,{emitWrapper:{appMethod:"addVpcPeer"},emitGuard:oe}),"vpc-peer-accepter":o(r["vpc-peer-accepter"],z,{emitWrapper:{appMethod:"addVpcPeerAccepter"}}),"cross-plan-connection":o(r["cross-plan-connection"],H),organisation:o(r.organisation,Q),buildkite:o(r.buildkite,$,{emitWrapper:{appMethod:"addBuildkite",propsOverloadConstructId:"Buildkite"}})},be=Object.keys(y);function Pe(e){const t=Object.keys(y);for(const i of t)if(y[i].factoryIdentifier===e)return i}a(Pe,"findTypeByIdentifier");export{be as REGISTERED_STATEMENT_TYPES,y as STATEMENT_REGISTRY,Pe as findTypeByIdentifier};
|
|
@@ -38,8 +38,9 @@ export type LocatedShape = NodeLocation & {
|
|
|
38
38
|
/**
|
|
39
39
|
* Walks every call expression in the source and returns a
|
|
40
40
|
* {@link LocatedShape} for each `XFactory.build("Name", ...)` shape,
|
|
41
|
-
* plus an unmanaged hit for each legacy method-form call
|
|
42
|
-
*
|
|
41
|
+
* plus an unmanaged hit for each legacy method-form call — the App methods
|
|
42
|
+
* carrying a props-direct overload, per {@link UNMANAGED_METHOD_TYPES}.
|
|
43
|
+
* The traversal descends into
|
|
43
44
|
* `IfStatement`, `Block`, `ForStatement`, and function bodies via
|
|
44
45
|
* `forEachDescendant`.
|
|
45
46
|
*
|
|
@@ -49,10 +50,11 @@ export type LocatedShape = NodeLocation & {
|
|
|
49
50
|
*/
|
|
50
51
|
export declare function locateAllShapes(content: string, filePath?: string): Result<LocatedShape[], LocateByShapeError>;
|
|
51
52
|
/**
|
|
52
|
-
* A legacy method-form call (`app.addBuildkite({ … })`) recognised via
|
|
53
|
-
*
|
|
54
|
-
* anchor the method-name token — there is no
|
|
55
|
-
* anchor on, so write paths must never consume this
|
|
53
|
+
* A legacy method-form call (`app.addBuildkite({ … })`) recognised via the
|
|
54
|
+
* registry's `emitWrapper.propsOverloadConstructId` declarations.
|
|
55
|
+
* `methodStart`/`methodLength` anchor the method-name token — there is no
|
|
56
|
+
* name string literal to anchor on, so write paths must never consume this
|
|
57
|
+
* span.
|
|
56
58
|
*/
|
|
57
59
|
export interface UnmanagedMethodMatch {
|
|
58
60
|
type: StatementType;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var h=Object.defineProperty;var c=(n,e)=>h(n,"name",{value:e,configurable:!0});import{Node as l,SyntaxKind as s}from"ts-morph";import{failure as u,success as d}from"../../types/Result.js";import{DEFAULT_FILE_PATH as
|
|
1
|
+
var h=Object.defineProperty;var c=(n,e)=>h(n,"name",{value:e,configurable:!0});import{Node as l,SyntaxKind as s}from"ts-morph";import{failure as u,success as d}from"../../types/Result.js";import{DEFAULT_FILE_PATH as p}from"../_internal.js";import{findTypeByIdentifier as E,STATEMENT_REGISTRY as S}from"../registry.js";import{getProject as T}from"./projectCache.js";function w(n,e,t=p){const a=A(n,t);if(!a.success)return a;const r=a.data.find(i=>i.type===e.type&&i.symbolName===e.name);return d(r)}c(w,"locateByShape");function A(n,e=p){const t=[],a=x(n,e,r=>{t.push({filePath:e,start:r.nameStart,length:r.nameLength,symbolName:r.name,type:r.type})},r=>{t.push({filePath:e,start:r.methodStart,length:r.methodLength,symbolName:r.name,type:r.type,managed:!1})});return a.success?d(t):a}c(A,"locateAllShapes");function x(n,e,t,a){let r;try{r=T().createSourceFile(e,n,{overwrite:!0})}catch(i){return u({kind:"SemanticQueryError",reason:"Failed to create source file",cause:i})}try{r.forEachDescendant(i=>{if(!l.isCallExpression(i))return;const o=N(i);if(o==="not-factory-call"){if(a!==void 0){const f=b(i);f!==void 0&&a(f,i)}return}if(o.kind!=="binding-pattern-declared"){if(o.kind==="template-literal-name")throw new y(i,o.type);t({type:o.type,name:o.name,nameStart:o.nameStart,nameLength:o.nameLength},i)}})}catch(i){return i instanceof y?u(i.toError(e)):u({kind:"SemanticQueryError",reason:"forEachDescendant traversal failed",cause:i})}return d(void 0)}c(x,"forEachFactoryMatch");function N(n){const e=n.getExpression();if(!l.isPropertyAccessExpression(e))return"not-factory-call";const t=m(e);if(t===void 0||g(n))return"not-factory-call";if(k(n))return{kind:"binding-pattern-declared"};const r=n.getArguments()[0];return r===void 0?"not-factory-call":l.isTemplateExpression(r)||l.isNoSubstitutionTemplateLiteral(r)?{kind:"template-literal-name",type:t}:l.isStringLiteral(r)?{kind:"match",type:t,name:r.getLiteralValue(),nameStart:r.getStart(),nameLength:r.getWidth()}:"not-factory-call"}c(N,"classifyFactoryCall");const L=new Map(Object.entries(S).flatMap(([n,e])=>{const t=e.emitWrapper?.propsOverloadConstructId;return e.emitWrapper===void 0||t===void 0?[]:[[e.emitWrapper.appMethod,{type:n,name:t}]]}));function b(n){const e=n.getExpression();if(!l.isPropertyAccessExpression(e))return;const t=L.get(e.getName());if(t===void 0||g(n))return;const a=n.getArguments()[0];if(a!==void 0&&l.isCallExpression(a)){const i=a.getExpression();if(l.isPropertyAccessExpression(i)&&m(i)!==void 0)return}const r=e.getNameNode();return{type:t.type,name:t.name,methodStart:r.getStart(),methodLength:r.getWidth()}}c(b,"classifyUnmanagedMethodCall");function m(n){const e=n.getExpression();if(l.isIdentifier(e)&&n.getName()==="build")return E(e.getText())}c(m,"factoryTypeFromPropertyAccess");function g(n){let e=n.getParent();for(;e!==void 0;){const t=e.getKind();if(t===s.TypeReference||t===s.TypeQuery||t===s.TypeLiteral||t===s.TypeAliasDeclaration||t===s.InterfaceDeclaration||t===s.TypeParameter||t===s.PropertySignature||t===s.MethodSignature||t===s.JsxOpeningElement||t===s.JsxClosingElement||t===s.JsxSelfClosingElement||t===s.JsxAttribute)return!0;e=e.getParent()}return!1}c(g,"isInsideTypeSpace");function k(n){const e=n.getFirstAncestorByKind(s.VariableDeclaration);if(e===void 0)return!1;const a=e.getNameNode().getKind();return a===s.ObjectBindingPattern||a===s.ArrayBindingPattern}c(k,"isInsideBindingPattern");class y extends Error{static{c(this,"TemplateLiteralMarker")}call;type;constructor(e,t){super("Template-literal factory name detected"),this.call=e,this.type=t,this.name="TemplateLiteralMarker"}toError(e){const a=this.call.getArguments()[0]?.getStart()??this.call.getStart(),r=this.call.getSourceFile(),{line:i,column:o}=r.getLineAndColumnAtPos(a);return{kind:"TemplateLiteralNameError",file:e,line:i,column:o,suggestion:"Replace the template literal with a plain string literal so the resource name can be resolved statically."}}}export{x as forEachFactoryMatch,A as locateAllShapes,w as locateByShape};
|
package/dist/src/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const GENERATOR_VERSION = "7.0
|
|
1
|
+
export declare const GENERATOR_VERSION = "7.2.0";
|
package/dist/src/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const E="7.0
|
|
1
|
+
const E="7.2.0";export{E as GENERATOR_VERSION};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fjall/generator",
|
|
3
|
-
"version": "7.0
|
|
3
|
+
"version": "7.2.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/fjall-tech/fjall.git",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
},
|
|
54
54
|
"license": "SEE LICENSE IN LICENSE",
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@fjall/util": "^7.0
|
|
56
|
+
"@fjall/util": "^7.2.0",
|
|
57
57
|
"ast-types": "^0.16.1",
|
|
58
58
|
"recast": "^0.23.12",
|
|
59
59
|
"ts-morph": "^28.0.0",
|