@fjall/generator 0.94.1 → 0.96.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/.minified +1 -1
- package/dist/src/ast/astCodeInjection.d.ts +9 -0
- package/dist/src/ast/astCodeInjection.js +8 -0
- package/dist/src/ast/astInfrastructureParser.d.ts +1 -1
- 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 +7 -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/__tests__/fixtures.d.ts +55 -0
- package/dist/src/codemod/drift/__tests__/fixtures.js +2 -0
- 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 +24 -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 +28 -0
- package/dist/src/codemod/edits/vpcPeer.js +1 -0
- package/dist/src/codemod/edits/vpcPeerAccepter.d.ts +23 -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/__tests__/fixtures.d.ts +5 -0
- package/dist/src/codemod/llmFallback/__tests__/fixtures.js +7 -0
- 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/__tests__/errorKinds.fixture.d.ts +1 -0
- package/dist/src/codemod/telemetry/__tests__/errorKinds.fixture.js +1 -0
- 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 +105 -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/dns/domainFileGenerator.js +22 -183
- package/dist/src/index.js +1 -21
- 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/resourcePlanning.d.ts +0 -46
- package/dist/src/planning/resourcePlanning.js +1 -1
- package/dist/src/schemas/applicationSchemas.d.ts +16 -0
- package/dist/src/schemas/applicationSchemas.js +1 -1
- package/dist/src/schemas/baseSchemas.d.ts +8 -5
- package/dist/src/schemas/baseSchemas.js +2 -2
- package/dist/src/schemas/networkSchemas.d.ts +119 -5
- package/dist/src/schemas/networkSchemas.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 +314 -0
- package/dist/src/validation/validationMessages.js +1 -0
- package/dist/src/validation/validationPatterns.d.ts +20 -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 +3 -3
package/dist/.minified
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
132 files minified at 2026-04-27T00:08:23.229Z
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type CustomCodeBlock } from "./astInfrastructureParser.js";
|
|
2
|
+
import { type SurgicalModificationResult } from "./astSurgicalModification.js";
|
|
3
|
+
export declare function ensureImports(content: string, requiredImports: string[]): SurgicalModificationResult;
|
|
4
|
+
export declare function injectCustomCodeBlocks(generatedCode: string, customBlocks: CustomCodeBlock[], resourceMapping?: Map<string, string>): SurgicalModificationResult;
|
|
5
|
+
export interface FileValidationResult {
|
|
6
|
+
valid: boolean;
|
|
7
|
+
errors?: string[];
|
|
8
|
+
}
|
|
9
|
+
export declare function validateModifiedFile(content: string): FileValidationResult;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import*as f from"typescript";import{classifyStatements as x}from"./astInfrastructureParser.js";import{parseSourceFile as d,insertAtPosition as m,formatLeadingNewlines as C,getContextBeforePosition as y,formatResourceId as l,createErrorResult as p,findClassification as g,findLastClassification as w}from"./astSurgicalModification.js";import{getErrorMessage as P}from"../util/errorUtils.js";function q(t,e){try{const n=d(t),o=new Set;for(const i of n.statements)f.isImportDeclaration(i)&&o.add(i.getText(n).trim());const r=[];for(const i of e){const c=i.trim();[...o].some(I=>O(I,c))||r.push(c)}if(r.length===0)return{content:t,success:!0};const s=n.statements.filter(f.isImportDeclaration).at(-1)?.getEnd()??0,u=`
|
|
2
|
+
`+r.join(`
|
|
3
|
+
`);return{content:m(t,s,u),success:!0}}catch(n){return p(n,t)}}function h(t){return new Set(t.split(",").map(e=>e.trim()).filter(Boolean))}function N(t,e){const n=h(t),o=h(e);for(const r of o)if(!n.has(r))return!1;return!0}function O(t,e){const n=t.match(R),o=e.match(R);if(!n||!o||n[1]!==o[1])return!1;const r=t.match(M),s=e.match(M);return r&&s?N(r[1],s[1]):!!r==!!s}const R=/from\s+["']([^"']+)["']/,M=/\{([^}]+)\}/,E={"before-imports":0,"after-imports":1,"after-app-init":2,"after-tags":3,"after-resource":4,"end-of-file":5};function T(t){return[...t].sort((e,n)=>{const o=E[n.position]-E[e.position];return o!==0?o:(n.originalLine??0)-(e.originalLine??0)})}function A(t,e,n,o){if(n!==null){const s=F(t,n,e);return m(t,n,s)}const r=e.afterManagedResource?`// [ORPHANED: was after ${l(e.afterManagedResource.type,e.afterManagedResource.name)}]
|
|
4
|
+
`:`// [ORPHANED]
|
|
5
|
+
`;return o.push(`Custom code from line ${e.originalLine} could not be positioned. Added at end of file.`),t+`
|
|
6
|
+
`+r+e.sourceText}function U(t,e,n){if(!e||e.length===0)return{content:t,success:!0};try{const o=d(t),r=x(o),s=[],u=T(e);let a=t;for(const i of u){const c=D(o,r,i,n,s);a=A(a,i,c,s)}return{content:a,success:!0,warnings:s.length>0?s:void 0}}catch(o){return p(o,t)}}function S(t,e,n,o){if(!e.afterManagedResource)return null;const r=e.afterManagedResource,s=(r.name&&n?.get(r.name))??r.name,u=t.find(i=>i.type===r.type&&i.resourceName===s);if(u)return u.endPos;const a=t.filter(i=>i.type===r.type&&i.isManaged);return a.length===1?a[0].endPos:(o&&o.push(`Resource ${l(e.afterManagedResource.type,e.afterManagedResource.name)} not found. Custom code may be orphaned.`),null)}function D(t,e,n,o,r){switch(n.position){case"before-imports":return 0;case"after-imports":return w(e,"import")?.endPos??0;case"after-app-init":return g(e,"app-init")?.endPos??null;case"after-tags":return g(e,"tags")?.endPos??null;case"after-resource":return S(e,n,o,r);case"end-of-file":return t.getEnd();default:return null}}function F(t,e,n){const o=y(t,e);let r=n.sourceText;return r=C(o,r)+r,r.endsWith(`
|
|
7
|
+
`)||(r+=`
|
|
8
|
+
`),r}function $(t){try{let o=function(r){r.kind===f.SyntaxKind.Unknown&&n.push("Unknown node found in AST - possible parse error"),f.forEachChild(r,o)};const e=d(t),n=[];return o(e),n.length>0?{valid:!1,errors:n}:{valid:!0}}catch(e){return{valid:!1,errors:[P(e)]}}}export{q as ensureImports,U as injectCustomCodeBlocks,$ as validateModifiedFile};
|
|
@@ -14,7 +14,7 @@ export { type ParsedSQSResource } from "./astMessagingParser.js";
|
|
|
14
14
|
export { type ParsedCDNResource } from "./astCdnParser.js";
|
|
15
15
|
export { type ParsedNetworkResource } from "./astNetworkParser.js";
|
|
16
16
|
export { type ParsedLambdaConfig, type ParsedPatternResource, } from "./astPatternParser.js";
|
|
17
|
-
export { type
|
|
17
|
+
export { type AstStatementType } from "../schemas/resourceSchemas.js";
|
|
18
18
|
export { type CustomCodeBlock } from "../schemas/resourceSchemas.js";
|
|
19
19
|
export { type ClassifiedStatement, classifyStatements, } from "./astStatementClassifier.js";
|
|
20
20
|
export { extractCustomCodeBlocks, findManagedResourcePosition, getLastManagedStatementOfType, getManagedResourcesByType, } from "./astStatementQueries.js";
|
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
* src/codemod/. Do not add mutation helpers here.
|
|
7
7
|
*/
|
|
8
8
|
import * as ts from "typescript";
|
|
9
|
-
import type {
|
|
9
|
+
import type { AstStatementType } from "../schemas/resourceSchemas.js";
|
|
10
10
|
/** Imports that the generator always handles -- don't preserve as "additional" */
|
|
11
11
|
declare const KNOWN_FJALL_IMPORTS: Set<string>;
|
|
12
12
|
export { KNOWN_FJALL_IMPORTS };
|
|
13
13
|
/** Check if a module specifier belongs to a managed (Fjall/CDK) package */
|
|
14
14
|
export declare function isManagedModuleSpecifier(specifier: string): boolean;
|
|
15
15
|
export interface ClassifiedStatement {
|
|
16
|
-
type:
|
|
16
|
+
type: AstStatementType;
|
|
17
17
|
node: ts.Statement;
|
|
18
18
|
resourceName?: string;
|
|
19
19
|
startPos: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as a from"typescript";import{isAppGetAppCall as
|
|
1
|
+
import*as a from"typescript";import{isAppGetAppCall as f}from"./astCommonParser.js";import{S3_BUCKET_CLASSES as d}from"./astStorageParser.js";const y=new Set(["App","Architecture","DatabaseFactory","StorageFactory","ComputeFactory","getConfig","MessagingFactory","CdnFactory","Code","Runtime","FunctionUrlAuthType","NetworkFactory","PatternFactory","VpcPeerFactory","VpcPeerAccepterFactory","CrossPlanConnectionFactory"]);function l(r){return!!(r.startsWith("@fjall/")||r==="aws-cdk-lib"||r.startsWith("aws-cdk-lib/")||r==="constructs")}function C(r){const n=[];for(const e of r.statements){const t=e.getFullStart(),i=e.getEnd();if(a.isImportDeclaration(e)){n.push({type:"import",node:e,startPos:t,endPos:i,isManaged:g(e)});continue}if(a.isVariableStatement(e)){n.push(m(e,t,i));continue}if(a.isExpressionStatement(e)){n.push(x(e,t,i));continue}n.push({type:"custom",node:e,startPos:t,endPos:i,isManaged:!1})}return n}function g(r){return a.isStringLiteral(r.moduleSpecifier)?l(r.moduleSpecifier.text):!1}function m(r,n,e){for(const t of r.declarationList.declarations)if(t.initializer){if(f(t.initializer))return{type:"app-init",node:r,resourceName:a.isIdentifier(t.name)?t.name.text:void 0,startPos:n,endPos:e,isManaged:!0};if(a.isIdentifier(t.name)&&t.name.text==="appName"&&a.isStringLiteral(t.initializer))return{type:"app-init",node:r,startPos:n,endPos:e,isManaged:!0};if(a.isNewExpression(t.initializer)){const i=t.initializer;if(a.isIdentifier(i.expression)){const o=i.expression.text;if(d.has(o)){const c=i.arguments&&i.arguments.length>=2&&a.isStringLiteral(i.arguments[1])?i.arguments[1].text:void 0;return{type:"storage",node:r,resourceName:c,startPos:n,endPos:e,isManaged:!0}}}}if(a.isCallExpression(t.initializer)){const i=p(t.initializer,r,n,e);if(i.type!=="custom")return i}}return{type:"custom",node:r,startPos:n,endPos:e,isManaged:!1}}function x(r,n,e){const t=r.expression;return a.isCallExpression(t)?p(t,r,n,e):{type:"custom",node:r,startPos:n,endPos:e,isManaged:!1}}function p(r,n,e,t){const i=r.expression;if(a.isPropertyAccessExpression(i)&&a.isIdentifier(i.expression)){const o=i.name.text;if(o==="addTags")return{type:"tags",node:n,startPos:e,endPos:t,isManaged:!0};const s={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"},addVpcPeer:{type:"vpc-peer",factory:"VpcPeerFactory"},addVpcPeerAccepter:{type:"vpc-peer-accepter",factory:"VpcPeerAccepterFactory"}}[o];if(s){const u=F(r,s.factory);return{type:s.type,node:n,resourceName:u,startPos:e,endPos:t,isManaged:!0}}}return{type:"custom",node:n,startPos:e,endPos:t,isManaged:!1}}function F(r,n){if(r.arguments.length===0)return;const e=r.arguments[0];if(!a.isCallExpression(e))return;const t=e.expression;if(!a.isPropertyAccessExpression(t)||t.name.text!=="build"||!a.isIdentifier(t.expression)||t.expression.text!==n||e.arguments.length===0)return;const i=e.arguments[0];if(a.isStringLiteral(i))return i.text}export{y as KNOWN_FJALL_IMPORTS,C as classifyStatements,l as isManagedModuleSpecifier};
|
|
@@ -5,17 +5,17 @@
|
|
|
5
5
|
* add mutation helpers here.
|
|
6
6
|
*/
|
|
7
7
|
import * as ts from "typescript";
|
|
8
|
-
import type {
|
|
8
|
+
import type { AstStatementType, CustomCodeBlock } from "../schemas/resourceSchemas.js";
|
|
9
9
|
import { type ClassifiedStatement } from "./astStatementClassifier.js";
|
|
10
10
|
/** Extract custom code blocks from an infrastructure file. */
|
|
11
11
|
export declare function extractCustomCodeBlocks(sourceFile: ts.SourceFile, precomputedClassifications?: ClassifiedStatement[]): CustomCodeBlock[];
|
|
12
12
|
/** Find the position information for a specific managed resource. */
|
|
13
|
-
export declare function findManagedResourcePosition(sourceFile: ts.SourceFile, resourceType:
|
|
13
|
+
export declare function findManagedResourcePosition(sourceFile: ts.SourceFile, resourceType: AstStatementType, resourceName: string, precomputedClassifications?: ClassifiedStatement[]): {
|
|
14
14
|
startPos: number;
|
|
15
15
|
endPos: number;
|
|
16
16
|
node: ts.Statement;
|
|
17
17
|
} | null;
|
|
18
18
|
/** Get the last managed statement of a specific type. */
|
|
19
|
-
export declare function getLastManagedStatementOfType(sourceFile: ts.SourceFile, type:
|
|
19
|
+
export declare function getLastManagedStatementOfType(sourceFile: ts.SourceFile, type: AstStatementType, precomputedClassifications?: ClassifiedStatement[]): ClassifiedStatement | null;
|
|
20
20
|
/** Get all managed resources grouped by type. */
|
|
21
|
-
export declare function getManagedResourcesByType(sourceFile: ts.SourceFile, precomputedClassifications?: ClassifiedStatement[]): Record<
|
|
21
|
+
export declare function getManagedResourcesByType(sourceFile: ts.SourceFile, precomputedClassifications?: ClassifiedStatement[]): Record<AstStatementType, ClassifiedStatement[]>;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import*as
|
|
2
|
-
`),
|
|
3
|
-
`)}function y(t,n){const i=[],o=
|
|
1
|
+
import*as l from"typescript";import{classifyStatements as r}from"./astStatementClassifier.js";const d={import:"after-imports","app-init":"after-app-init",tags:"after-tags"};function g(t){if(!t)return{position:"before-imports"};const n=d[t.type];return n?{position:n}:{position:"after-resource",afterManagedResource:{type:t.type,name:t.resourceName}}}function C(t,n){const i=n??r(t),o=[],s=t.getFullText();let e=null;for(const c of i)if(c.type==="custom"&&!c.isManaged){const{position:a,afterManagedResource:p}=g(e),u=x(s,c.node),f=y(s,c.node),m=t.getLineAndCharacterOfPosition(c.node.getStart());o.push({sourceText:u,position:a,afterManagedResource:p,originalLine:m.line+1,leadingComments:f.length>0?f:void 0})}else c.isManaged&&(e=c);return o}function x(t,n){const i=n.getFullStart(),o=n.getEnd(),e=t.slice(i,o).split(`
|
|
2
|
+
`),c=e.findIndex(a=>a.trim()!=="");return e.slice(c===-1?0:c).join(`
|
|
3
|
+
`)}function y(t,n){const i=[],o=l.getLeadingCommentRanges(t,n.getFullStart());if(o)for(const s of o){const e=t.slice(s.pos,s.end);i.push(e)}return i}function P(t,n,i,o){const s=o??r(t);for(const e of s)if(e.type===n&&e.resourceName===i)return{startPos:e.startPos,endPos:e.endPos,node:e.node};return null}function S(t,n,i){const o=i??r(t);let s=null;for(const e of o)e.type===n&&e.isManaged&&(s=e);return s}function h(t,n){const i=n??r(t),o={import:[],"app-init":[],tags:[],database:[],compute:[],storage:[],network:[],messaging:[],cdn:[],pattern:[],"vpc-peer":[],"vpc-peer-accepter":[],"cross-plan-connection":[],custom:[]};for(const s of i)o[s.type].push(s);return o}export{C as extractCustomCodeBlocks,P as findManagedResourcePosition,S as getLastManagedStatementOfType,h as getManagedResourcesByType};
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as ts from "typescript";
|
|
2
|
+
import { type AstStatementType, type ClassifiedStatement } from "./astInfrastructureParser.js";
|
|
3
|
+
export declare function insertAtPosition(content: string, position: number, text: string): string;
|
|
4
|
+
export declare function findClassification(classifications: ClassifiedStatement[], type: AstStatementType): ClassifiedStatement | undefined;
|
|
5
|
+
export declare function findLastClassification(classifications: ClassifiedStatement[], type: AstStatementType): ClassifiedStatement | undefined;
|
|
2
6
|
export interface SurgicalModificationResult {
|
|
3
7
|
content: string;
|
|
4
8
|
success: boolean;
|
|
@@ -8,20 +12,20 @@ export interface SurgicalModificationResult {
|
|
|
8
12
|
}
|
|
9
13
|
export interface InsertionOptions {
|
|
10
14
|
/** The type of resource being inserted */
|
|
11
|
-
resourceType:
|
|
15
|
+
resourceType: AstStatementType;
|
|
12
16
|
/** The resource name (e.g., "MyDatabase") */
|
|
13
17
|
resourceName: string;
|
|
14
18
|
/** The code to insert (complete statement) */
|
|
15
19
|
code: string;
|
|
16
20
|
/** Insert after a specific resource (by name) */
|
|
17
21
|
afterResource?: {
|
|
18
|
-
type:
|
|
22
|
+
type: AstStatementType;
|
|
19
23
|
name: string;
|
|
20
24
|
};
|
|
21
25
|
}
|
|
22
26
|
export interface UpdateOptions {
|
|
23
27
|
/** The type of resource being updated */
|
|
24
|
-
resourceType:
|
|
28
|
+
resourceType: AstStatementType;
|
|
25
29
|
/** The resource name to update */
|
|
26
30
|
resourceName: string;
|
|
27
31
|
/** The new code for the resource (complete statement) */
|
|
@@ -29,19 +33,17 @@ export interface UpdateOptions {
|
|
|
29
33
|
}
|
|
30
34
|
export interface DeleteOptions {
|
|
31
35
|
/** The type of resource being deleted */
|
|
32
|
-
resourceType:
|
|
36
|
+
resourceType: AstStatementType;
|
|
33
37
|
/** The resource name to delete */
|
|
34
38
|
resourceName: string;
|
|
35
39
|
/** How to handle custom code that was after this resource */
|
|
36
40
|
orphanHandling: "preserve-with-warning" | "move-to-previous" | "delete";
|
|
37
41
|
}
|
|
42
|
+
export declare function parseSourceFile(content: string): ts.SourceFile;
|
|
43
|
+
export declare function formatLeadingNewlines(beforeText: string, codeToInsert: string): string;
|
|
44
|
+
export declare function getContextBeforePosition(content: string, position: number): string;
|
|
45
|
+
export declare function formatResourceId(type: string, name?: string): string;
|
|
46
|
+
export declare function createErrorResult(error: unknown, content: string): SurgicalModificationResult;
|
|
38
47
|
export declare function addResourceSurgically(content: string, options: InsertionOptions): SurgicalModificationResult;
|
|
39
48
|
export declare function updateResourceSurgically(content: string, options: UpdateOptions): SurgicalModificationResult;
|
|
40
49
|
export declare function removeResourceSurgically(content: string, options: DeleteOptions): SurgicalModificationResult;
|
|
41
|
-
export declare function ensureImports(content: string, requiredImports: string[]): SurgicalModificationResult;
|
|
42
|
-
export declare function injectCustomCodeBlocks(generatedCode: string, customBlocks: CustomCodeBlock[], resourceMapping?: Map<string, string>): SurgicalModificationResult;
|
|
43
|
-
export interface FileValidationResult {
|
|
44
|
-
valid: boolean;
|
|
45
|
-
errors?: string[];
|
|
46
|
-
}
|
|
47
|
-
export declare function validateModifiedFile(content: string): FileValidationResult;
|
|
@@ -1,19 +1,12 @@
|
|
|
1
|
-
import*as
|
|
1
|
+
import*as x from"typescript";import{parseInfrastructure as F,classifyStatements as E,findManagedResourcePosition as y}from"./astInfrastructureParser.js";import{getErrorMessage as O}from"../util/errorUtils.js";function C(e,r,t){return e.slice(0,r)+t+e.slice(r)}function L(e,r){return e.find(t=>t.type===r)}function A(e,r){return e.filter(t=>t.type===r).pop()}function w(e){return x.createSourceFile("infrastructure.ts",e,x.ScriptTarget.Latest,!0,x.ScriptKind.TS)}function T(e,r){return e.endsWith(`
|
|
2
2
|
|
|
3
|
-
`)||
|
|
3
|
+
`)||r.startsWith(`
|
|
4
4
|
`)?"":e.endsWith(`
|
|
5
5
|
`)?`
|
|
6
6
|
`:`
|
|
7
7
|
|
|
8
|
-
`}function
|
|
8
|
+
`}function I(e,r){return e.slice(Math.max(0,r-2),r)}function p(e,r){return r?`${e}:${r}`:e}function f(e,r){return{content:r,success:!1,error:O(e)}}function P(e,r,t,i){const n=y(e,t,i);return n||f(new Error(`Resource not found: ${p(t,i)}`),r)}function R(e){return"success"in e&&!e.success}function B(e,r){const{resourceType:t,code:i,afterResource:n}=r;try{const s=w(e);let o;if(n){const u=y(s,n.type,n.name);if(!u)return f(new Error(`Insertion target not found: ${p(n.type,n.name)}`),e);o=u.endPos}else o=M(s,t);const c=v(e,o,i);return{content:C(e,o,c),success:!0}}catch(s){return f(s,e)}}function M(e,r){const t=E(e),i=["import","app-init","tags","database","storage","messaging","compute","network","vpc-peer","vpc-peer-accepter","cross-plan-connection","cdn","pattern"],n=i.indexOf(r);let s=null;for(const o of t)if(o.isManaged){const c=i.indexOf(o.type);c!==-1&&c<=n&&(s=o)}return s?s.endPos:e.getEnd()}function v(e,r,t){const i=I(e,r),n=e.slice(r,Math.min(e.length,r+2)),s=T(i,t),o=n.startsWith(`
|
|
9
9
|
`)?"":`
|
|
10
|
-
`;return
|
|
11
|
-
`,a=
|
|
12
|
-
`?
|
|
13
|
-
`+n.join(`
|
|
14
|
-
`);return{content:R(e,o,i),success:!0}}catch(r){return l(r,e)}}function O(e){return new Set(e.split(",").map(t=>t.trim()).filter(Boolean))}function _(e,t){const r=O(e),s=O(t);for(const n of s)if(!r.has(n))return!1;return!0}function q(e,t){const r=e.match(F),s=t.match(F);if(!r||!s||r[1]!==s[1])return!1;const n=e.match(N),o=t.match(N);return n&&o?_(n[1],o[1]):!0}const F=/from\s+["']([^"']+)["']/,N=/\{([^}]+)\}/,v={"before-imports":0,"after-imports":1,"after-app-init":2,"after-tags":3,"after-resource":4,"end-of-file":5};function U(e){return[...e].sort((t,r)=>{const s=v[r.position]-v[t.position];return s!==0?s:(r.originalLine??0)-(t.originalLine??0)})}function j(e,t,r,s){if(r!==null){const o=K(e,r,t);return R(e,r,o)}const n=t.afterManagedResource?`// [ORPHANED: was after ${p(t.afterManagedResource.type,t.afterManagedResource.name)}]
|
|
15
|
-
`:`// [ORPHANED]
|
|
16
|
-
`;return s.push(`Custom code from line ${t.originalLine} could not be positioned. Added at end of file.`),e+`
|
|
17
|
-
`+n+t.sourceText}function Z(e,t,r){if(!t||t.length===0)return{content:e,success:!0};try{const s=m(e),n=y(s),o=[],i=U(t);let c=e;for(const a of i){const u=G(s,n,a,r,o);c=j(c,a,u,o)}return{content:c,success:!0,warnings:o.length>0?o:void 0}}catch(s){return l(s,e)}}function k(e,t,r,s){if(!t.afterManagedResource)return null;const n=t.afterManagedResource,o=(n.name&&r?.get(n.name))??n.name,i=e.find(a=>a.type===n.type&&a.resourceName===o);if(i)return i.endPos;const c=e.filter(a=>a.type===n.type&&a.isManaged);return c.length===1?c[0].endPos:(s&&s.push(`Resource ${p(t.afterManagedResource.type,t.afterManagedResource.name)} not found. Custom code may be orphaned.`),null)}function G(e,t,r,s,n){switch(r.position){case"before-imports":return 0;case"after-imports":return L(t,"import")?.endPos??0;case"after-app-init":return P(t,"app-init")?.endPos??null;case"after-tags":return P(t,"tags")?.endPos??null;case"after-resource":return k(t,r,s,n);case"end-of-file":return e.getEnd();default:return null}}function K(e,t,r){const s=E(e,t);let n=r.sourceText;return n=I(s,n)+n,n.endsWith(`
|
|
18
|
-
`)||(n+=`
|
|
19
|
-
`),n}function b(e){try{let s=function(n){n.kind===f.SyntaxKind.Unknown&&r.push("Unknown node found in AST - possible parse error"),f.forEachChild(n,s)};const t=m(e),r=[];return s(t),r.length>0?{valid:!1,errors:r}:{valid:!0}}catch(t){return{valid:!1,errors:[M(t)]}}}export{J as addResourceSurgically,Y as ensureImports,Z as injectCustomCodeBlocks,V as removeResourceSurgically,Q as updateResourceSurgically,b as validateModifiedFile};
|
|
10
|
+
`;return s+t+o}function H(e,r){const{resourceType:t,resourceName:i,newCode:n}=r;try{const s=w(e),o=P(s,e,t,i);if(R(o))return o;const c=o,a=c.node.getFullStart(),u=c.endPos,l=e.slice(a,c.node.getStart()),g=$(l);return{content:e.slice(0,a)+g+n+e.slice(u),success:!0}}catch(s){return f(s,e)}}function $(e){const r=e.match(/^[\s]*[\n\r]+[\s]*/);return r?r[0]:""}function N(e,r,t,i){let n=e,s=0;for(const o of r){const c=`// [ORPHANED: was after ${p(t,i)}]
|
|
11
|
+
`,a=o.sourceText.trim(),u=n.indexOf(a,s);u!==-1&&(n=C(n,u,c),s=u+c.length+a.length)}return n}function b(e,r){const{resourceType:t,resourceName:i,orphanHandling:n}=r,s=[];try{const o=w(e),c=P(o,e,t,i);if(R(c))return c;const a=c,l=(F(e,{extractCustomCode:!0}).customCodeBlocks??[]).filter(h=>h.position==="after-resource"&&h.afterManagedResource?.type===t&&h.afterManagedResource?.name===i);if(l.length>0)if(n==="preserve-with-warning")s.push(`Custom code after ${p(t,i)} will be orphaned. Added // [ORPHANED] marker comment.`);else return f(new Error(`Orphan handling "${n}" is not yet implemented`),e);const g=a.node.getFullStart(),d=a.endPos,S=e[d]===`
|
|
12
|
+
`?d+1:d;let m=e.slice(0,g)+e.slice(S);return l.length>0&&n==="preserve-with-warning"&&(m=N(m,l,t,i)),{content:m,success:!0,warnings:s.length>0?s:void 0}}catch(o){return f(o,e)}}export{B as addResourceSurgically,f as createErrorResult,L as findClassification,A as findLastClassification,T as formatLeadingNewlines,p as formatResourceId,I as getContextBeforePosition,C as insertAtPosition,w as parseSourceFile,b as removeResourceSurgically,H as updateResourceSurgically};
|
|
@@ -2,10 +2,10 @@ import { parseInfrastructure } from "@fjall/generator";
|
|
|
2
2
|
export { parseInfrastructure };
|
|
3
3
|
export declare function parseAndConvert(code: string): {
|
|
4
4
|
appName: string;
|
|
5
|
-
type: "
|
|
5
|
+
type: "standard" | "resilient" | "enterprise" | "tinkerer" | "lightweight" | "custom";
|
|
6
6
|
database: {
|
|
7
7
|
name: string;
|
|
8
|
-
type: "
|
|
8
|
+
type: "Aurora" | "Instance" | "GlobalAurora";
|
|
9
9
|
databaseName: string;
|
|
10
10
|
instanceType?: string | undefined;
|
|
11
11
|
allocatedStorage?: number | undefined;
|
|
@@ -109,7 +109,7 @@ export declare function parseAndConvert(code: string): {
|
|
|
109
109
|
} | undefined;
|
|
110
110
|
} | undefined;
|
|
111
111
|
retain?: boolean | undefined;
|
|
112
|
-
stackPlacement?: "
|
|
112
|
+
stackPlacement?: "storage" | "compute" | "cdn" | undefined;
|
|
113
113
|
variableName?: string | undefined;
|
|
114
114
|
extraProperties?: {
|
|
115
115
|
key: string;
|
|
@@ -213,7 +213,7 @@ export declare function parseAndConvert(code: string): {
|
|
|
213
213
|
} | undefined;
|
|
214
214
|
}[] | undefined;
|
|
215
215
|
dockerfilePath?: string | undefined;
|
|
216
|
-
deployment?: "
|
|
216
|
+
deployment?: "code" | "container" | undefined;
|
|
217
217
|
codePath?: string | undefined;
|
|
218
218
|
timeout?: number | undefined;
|
|
219
219
|
memory?: number | undefined;
|
|
@@ -272,7 +272,7 @@ export declare function parseAndConvert(code: string): {
|
|
|
272
272
|
name: string;
|
|
273
273
|
domain?: string | undefined;
|
|
274
274
|
database?: {
|
|
275
|
-
type?: "
|
|
275
|
+
type?: "Aurora" | "Instance" | undefined;
|
|
276
276
|
databaseName?: string | undefined;
|
|
277
277
|
databaseEngine?: "postgresql" | "mysql" | undefined;
|
|
278
278
|
deletionProtection?: boolean | undefined;
|
|
@@ -384,7 +384,7 @@ export declare function parseAndConvert(code: string): {
|
|
|
384
384
|
name: string;
|
|
385
385
|
domain?: string | undefined;
|
|
386
386
|
database?: {
|
|
387
|
-
type?: "
|
|
387
|
+
type?: "Aurora" | "Instance" | undefined;
|
|
388
388
|
databaseName?: string | undefined;
|
|
389
389
|
databaseEngine?: "postgresql" | "mysql" | undefined;
|
|
390
390
|
deletionProtection?: boolean | undefined;
|
|
@@ -619,7 +619,7 @@ export declare function parseAndConvert(code: string): {
|
|
|
619
619
|
sourceText: string;
|
|
620
620
|
position: "before-imports" | "after-imports" | "after-app-init" | "after-tags" | "after-resource" | "end-of-file";
|
|
621
621
|
afterManagedResource?: {
|
|
622
|
-
type: "custom" | "
|
|
622
|
+
type: "custom" | "storage" | "database" | "compute" | "pattern" | "import" | "app-init" | "tags" | "network" | "messaging" | "cdn" | "vpc-peer" | "vpc-peer-accepter" | "cross-plan-connection";
|
|
623
623
|
name?: string | undefined;
|
|
624
624
|
} | undefined;
|
|
625
625
|
originalLine?: number | undefined;
|
package/dist/src/ast/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { type IdentifierRef, type ExpressionRef, type CallRef, type ParsedValue, type ParsedObject, isPlainObject, isIdentifierRef, isExpressionRef, isCallRef, isParsedObject, asString, asNumber, asBoolean, asStringArray, asStringUnion, captureExtraProperties, } from "./astCommonParser.js";
|
|
2
|
-
export { type ParsedInfrastructure, type ParsedDatabaseResource, type ParsedS3Resource, type ParsedComputeResource, type ParsedSQSResource, type ParsedCDNResource, type ParsedNetworkResource, type ParsedDynamoDBResource, type ParsedLambdaConfig, type ParsedPatternResource, type ImportInfo, type
|
|
3
|
-
export { type SurgicalModificationResult, type InsertionOptions, type UpdateOptions, type DeleteOptions,
|
|
2
|
+
export { type ParsedInfrastructure, type ParsedDatabaseResource, type ParsedS3Resource, type ParsedComputeResource, type ParsedSQSResource, type ParsedCDNResource, type ParsedNetworkResource, type ParsedDynamoDBResource, type ParsedLambdaConfig, type ParsedPatternResource, type ImportInfo, type AstStatementType, type ClassifiedStatement, type CustomCodeBlock, parseInfrastructure, convertToResourcePlan, classifyStatements, extractCustomCodeBlocks, findManagedResourcePosition, getLastManagedStatementOfType, getManagedResourcesByType, } from "./astInfrastructureParser.js";
|
|
3
|
+
export { type SurgicalModificationResult, type InsertionOptions, type UpdateOptions, type DeleteOptions, addResourceSurgically, updateResourceSurgically, removeResourceSurgically, } from "./astSurgicalModification.js";
|
|
4
|
+
export { type FileValidationResult, ensureImports, injectCustomCodeBlocks, validateModifiedFile, } from "./astCodeInjection.js";
|
|
4
5
|
export { type DomainCallMatch, type ParsedFjallTarget, type ParsedDnsRecordFromAst, type ParsedCertificateFromAst, type ParsedDelegationFromAst, type ParsedDomainProps, type ParsedRoute53ApexDomainProps, type ParsedExternalDelegatedDomainProps, type ParsedExternalRecordsDomainProps, type ParseDomainError, findDomainConstructCall, parseDomainProps, parseDomainErrorMessage, splitTopLevelArgs, } from "./astDomainParser.js";
|
package/dist/src/ast/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isPlainObject as s,isIdentifierRef as
|
|
1
|
+
import{isPlainObject as s,isIdentifierRef as o,isExpressionRef as a,isCallRef as t,isParsedObject as i,asString as n,asNumber as c,asBoolean as l,asStringArray as p,asStringUnion as u,captureExtraProperties as d}from"./astCommonParser.js";import{parseInfrastructure as m,convertToResourcePlan as g,classifyStatements as R,extractCustomCodeBlocks as x,findManagedResourcePosition as y,getLastManagedStatementOfType as S,getManagedResourcesByType as C}from"./astInfrastructureParser.js";import{addResourceSurgically as M,updateResourceSurgically as v,removeResourceSurgically as B}from"./astSurgicalModification.js";import{ensureImports as b,injectCustomCodeBlocks as j,validateModifiedFile as D}from"./astCodeInjection.js";import{findDomainConstructCall as I,parseDomainProps as O,parseDomainErrorMessage as k,splitTopLevelArgs as A}from"./astDomainParser.js";export{M as addResourceSurgically,l as asBoolean,c as asNumber,n as asString,p as asStringArray,u as asStringUnion,d as captureExtraProperties,R as classifyStatements,g as convertToResourcePlan,b as ensureImports,x as extractCustomCodeBlocks,I as findDomainConstructCall,y as findManagedResourcePosition,S as getLastManagedStatementOfType,C as getManagedResourcesByType,j as injectCustomCodeBlocks,t as isCallRef,a as isExpressionRef,o as isIdentifierRef,i as isParsedObject,s as isPlainObject,k as parseDomainErrorMessage,O as parseDomainProps,m as parseInfrastructure,B as removeResourceSurgically,A as splitTopLevelArgs,v as updateResourceSurgically,D as validateModifiedFile};
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import type { LinesChanged } from "./types.js";
|
|
2
2
|
export declare const DEFAULT_FILE_PATH = "/__codemod__/input.ts";
|
|
3
3
|
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* @internal Runtime validates each element as `Record<string, unknown>` only.
|
|
6
|
+
* `T`'s extra shape is asserted at the type level — callers are responsible
|
|
7
|
+
* for ensuring the parsed AST nodes conform to the declared `T`.
|
|
8
|
+
*/
|
|
9
|
+
export declare function extractProgramBody<T extends Record<string, unknown>>(file: unknown): T[] | undefined;
|
|
5
10
|
/**
|
|
6
11
|
* Bidirectional line-count delta between `before` and `after`. The source's
|
|
7
12
|
* dominant line terminator is detected once; positive deltas map to
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{detectLineTerminator as i}from"./fileRewriter/index.js";const c="/__codemod__/input.ts";function o(r){return typeof r=="object"&&r!==null&&!Array.isArray(r)}function f(r){if(!o(r))return;const t=r.program;if(!o(t))return;const e=t.body;if(Array.isArray(e)&&e.every(
|
|
1
|
+
import{detectLineTerminator as i}from"./fileRewriter/index.js";const c="/__codemod__/input.ts";function o(r){return typeof r=="object"&&r!==null&&!Array.isArray(r)}function f(r){if(!o(r))return;const t=r.program;if(!o(t))return;const e=t.body;if(Array.isArray(e)&&e.every(o))return e}function d(r,t){return r.length===0?0:r.split(t).length}function a(r,t){const e=i(r),n=d(t,e)-d(r,e);return n>=0?{added:n,removed:0}:{added:0,removed:-n}}export{c as DEFAULT_FILE_PATH,a as computeLinesDelta,f as extractProgramBody,o as isRecord};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type PropertyDelta, type ResourceSnapshot } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Shared fixture builders for the drift test suite. Keeping these in
|
|
4
|
+
* one module means every test can rely on `.strict()` Zod validation
|
|
5
|
+
* of the inputs it constructs, removing schema drift risk from the
|
|
6
|
+
* suite.
|
|
7
|
+
*/
|
|
8
|
+
interface StorageFixtureOptions {
|
|
9
|
+
name?: string;
|
|
10
|
+
properties?: Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Build a minimal `.ts` source string that contains a single
|
|
14
|
+
* `StorageFactory.build` call-site so the listing + parse path in
|
|
15
|
+
* `detect.ts` and `snapshot.ts` has something to locate.
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildStorageSource(options?: StorageFixtureOptions): string;
|
|
18
|
+
/**
|
|
19
|
+
* Format a JavaScript literal value as source text. Only covers the
|
|
20
|
+
* literal kinds that exercise the snapshot extractor's literal path —
|
|
21
|
+
* strings, numbers, booleans, nulls, arrays, and plain objects. Raw
|
|
22
|
+
* identifier / member-expression values (e.g. `Runtime.NODEJS_20_X`)
|
|
23
|
+
* are encoded as `rawExpression`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function formatLiteral(value: unknown): string;
|
|
26
|
+
/**
|
|
27
|
+
* Tag a string so `formatLiteral` emits it verbatim rather than
|
|
28
|
+
* JSON-encoding. Use for construct expressions (`Runtime.NODEJS_20_X`,
|
|
29
|
+
* `cdk.Duration.days(14)`) that are not valid JSON literals but must
|
|
30
|
+
* appear in the generated source.
|
|
31
|
+
*/
|
|
32
|
+
export interface RawExpression {
|
|
33
|
+
raw: string;
|
|
34
|
+
}
|
|
35
|
+
export declare function raw(expression: string): RawExpression;
|
|
36
|
+
/**
|
|
37
|
+
* Validate and return a `ResourceSnapshot`. Run through
|
|
38
|
+
* `.strict()` so any field-name typo in a test fixture surfaces
|
|
39
|
+
* at the boundary instead of silently producing a malformed
|
|
40
|
+
* baseline the merge path would interpret incorrectly.
|
|
41
|
+
*/
|
|
42
|
+
export declare function makeSnapshot(properties: Record<string, unknown>, name?: string, type?: ResourceSnapshot["type"]): ResourceSnapshot;
|
|
43
|
+
/**
|
|
44
|
+
* Validate and return a `PropertyDelta`. Helper for constructing
|
|
45
|
+
* golden expected shapes inline without writing a literal object
|
|
46
|
+
* that might drift from the schema.
|
|
47
|
+
*/
|
|
48
|
+
export declare function makeDelta(delta: {
|
|
49
|
+
property: string;
|
|
50
|
+
base?: unknown;
|
|
51
|
+
theirs: unknown;
|
|
52
|
+
ours: unknown;
|
|
53
|
+
verdict: PropertyDelta["verdict"];
|
|
54
|
+
}): PropertyDelta;
|
|
55
|
+
export {};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{PropertyDeltaSchema as f,ResourceSnapshotSchema as c}from"../types.js";const i="Photos";function u(r={}){const n=r.name??i,t=r.properties??{versioned:!0},e=Object.entries(t).map(([p,s])=>` ${p}: ${o(s)},`);return['import { StorageFactory } from "@fjall/components-infrastructure";',"",`StorageFactory.build("${n}", {`,...e,"});",""].join(`
|
|
2
|
+
`)}function o(r){if(r===null)return"null";if(typeof r=="string")return JSON.stringify(r);if(typeof r=="number"||typeof r=="boolean")return String(r);if(a(r))return r.raw;if(Array.isArray(r))return`[${r.map(o).join(", ")}]`;if(typeof r=="object"&&r!==null)return`{ ${Object.entries(r).map(([t,e])=>`${t}: ${o(e)}`).join(", ")} }`;throw new Error(`formatLiteral: unsupported value type for ${String(r)}`)}function y(r){return{raw:r}}function a(r){return typeof r=="object"&&r!==null&&"raw"in r&&typeof r.raw=="string"}function S(r,n=i,t="storage"){return c.parse({type:t,name:n,properties:r,schemaVersion:1})}function g(r){return f.parse(r)}export{u as buildStorageSource,o as formatLiteral,g as makeDelta,S as makeSnapshot,y as raw};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Result } from "../../types/Result.js";
|
|
2
|
+
import type { CodemodError, ResourceName, StatementType } from "../types.js";
|
|
3
|
+
import type { DriftState, ResourceSnapshot } from "./types.js";
|
|
4
|
+
export type DriftOp = "add" | "modify";
|
|
5
|
+
export interface DriftPlan {
|
|
6
|
+
type: StatementType;
|
|
7
|
+
name: ResourceName;
|
|
8
|
+
properties: Record<string, unknown>;
|
|
9
|
+
op: DriftOp;
|
|
10
|
+
}
|
|
11
|
+
export declare function detectDrift(content: string, plan: DriftPlan, baseline?: ResourceSnapshot): Result<DriftState, CodemodError>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{failure as c,success as m}from"../../types/Result.js";import{listResources as l}from"../listResources.js";import{mergeProperties as y}from"./merge.js";import{snapshotProperties as h}from"./snapshot.js";function R(o,e,n){const r=l(o);if(!r.success)return c(r.error);if(r.data.resources.find(s=>s.type===e.type&&s.name===e.name)===void 0)return e.op==="add"?m({resource:{type:e.type,name:e.name},deltas:[],summary:"clean"}):c({kind:"ResourceNotFoundError",type:e.type,name:e.name,knownNames:r.data.resources.filter(s=>s.type===e.type).map(s=>s.name)});const t=h(o,e.type,e.name);if(!t.success)return c(t.error);const i={...t.data.properties},d={...e.properties},u=n===void 0?void 0:{...n.properties},f=y(u,i,d),p=b(f.deltas,u!==void 0);return m({resource:{type:e.type,name:e.name},deltas:f.deltas,summary:p})}function b(o,e){if(o.length===0)return"clean";let n=!1,r=!1,a=!1,t=!1;for(const i of o)switch(i.verdict){case"conflict":n=!0;break;case"compatible":r=!0;break;case"clean":a=!0;break;case"no-op":t=!0;break}return n?"drifted-conflict":r?"drifted-compatible":t&&!a?"no-op":"clean"}export{R as detectDrift};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { detectDrift, type DriftOp, type DriftPlan } from "./detect.js";
|
|
2
|
+
export { mergeProperties, type MergeResult } from "./merge.js";
|
|
3
|
+
export { snapshotProperties } from "./snapshot.js";
|
|
4
|
+
export { DriftPolicySchema, DriftStateSchema, PropertyDeltaSchema, ResourceSnapshotSchema, type DriftPolicy, type DriftState, type PropertyDelta, type ResourceSnapshot, } from "./types.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{detectDrift as t}from"./detect.js";import{mergeProperties as m}from"./merge.js";import{snapshotProperties as a}from"./snapshot.js";import{DriftPolicySchema as f,DriftStateSchema as h,PropertyDeltaSchema as i,ResourceSnapshotSchema as s}from"./types.js";export{f as DriftPolicySchema,h as DriftStateSchema,i as PropertyDeltaSchema,s as ResourceSnapshotSchema,t as detectDrift,m as mergeProperties,a as snapshotProperties};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { PropertyDelta } from "./types.js";
|
|
2
|
+
export interface MergeResult {
|
|
3
|
+
merged: Record<string, unknown>;
|
|
4
|
+
deltas: PropertyDelta[];
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* 3-way merge for resource properties.
|
|
8
|
+
*
|
|
9
|
+
* Per-property verdicts:
|
|
10
|
+
* - `clean` — user untouched; apply Fjall's intent (or preserve base).
|
|
11
|
+
* - `no-op` — user edited the property to exactly match Fjall's intent.
|
|
12
|
+
* - `compatible` — user edited a property Fjall is not touching; keep user's.
|
|
13
|
+
* - `conflict` — user edited AND Fjall wants a different value.
|
|
14
|
+
*
|
|
15
|
+
* When `base` is undefined, falls back to a user-preserving 2-way merge:
|
|
16
|
+
* properties present in `theirs` but not `ours` are preserved; properties
|
|
17
|
+
* present in both with different values surface as `conflict`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function mergeProperties(base: Record<string, unknown> | undefined, theirs: Record<string, unknown>, ours: Record<string, unknown>): MergeResult;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{isDeepStrictEqual as v}from"node:util";function P(e,t,n){const o=[...new Set([...e===void 0?[]:Object.keys(e),...Object.keys(t),...Object.keys(n)])].sort((r,u)=>r.localeCompare(u,"en")),i={},d=[];for(const r of o){const u=e!==void 0&&g(e,r),l=g(t,r),m=g(n,r),k=u?e[r]:void 0,p=l?t[r]:void 0,s=m?n[r]:void 0,y=e===void 0?j(l,p,m,s):b(u,k,l,p,m,s);d.push({property:r,base:k,theirs:p,ours:s,verdict:y.verdict}),y.merged.has&&(i[r]=y.merged.value)}return{merged:i,deltas:d}}function j(e,t,n,c){return!e&&n?f(c):e&&!n||v(t,c)?f(t):a()}function b(e,t,n,c,o,i){if(!e&&!n&&o)return f(i);if(e&&!n&&o)return a();const d=e===n&&v(t,c);return d&&o?f(i):d&&!o?f(t):o?n===o&&v(c,i)?{verdict:"no-op",merged:{has:!0,value:c}}:a():n?{verdict:"compatible",merged:{has:!0,value:c}}:{verdict:"compatible",merged:{has:!1}}}function f(e){return{verdict:"clean",merged:{has:!0,value:e}}}function a(){return{verdict:"conflict",merged:{has:!1}}}function g(e,t){return Object.prototype.hasOwnProperty.call(e,t)}export{P as mergeProperties};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type Result } from "../../types/Result.js";
|
|
2
|
+
import type { CodemodError, ResourceName, StatementType } from "../types.js";
|
|
3
|
+
import type { ResourceSnapshot } from "./types.js";
|
|
4
|
+
export declare function snapshotProperties(content: string, type: StatementType, name: ResourceName): Result<ResourceSnapshot, CodemodError>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{failure as a,success as y}from"../../types/Result.js";import{extractProgramBody as h,isRecord as p}from"../_internal.js";import{astToLiteral as g,isNodeShape as l,NON_LITERAL as k,readKeyName as b}from"../edits/modifyResource/literalConversion.js";import{parse as E}from"../fileRewriter/index.js";import{listResources as x}from"../listResources.js";import{STATEMENT_REGISTRY as N}from"../registry.js";function F(t,n,r){const e=x(t);if(!e.success)return a(e.error);const i=e.data.resources.find(o=>o.type===n&&o.name===r);if(i===void 0)return a({kind:"ResourceNotFoundError",type:n,name:r,knownNames:e.data.resources.filter(o=>o.type===n).map(o=>o.name)});const s=E(t,i.filePath);if(!s.success)return a(s.error);const c=h(s.data);if(c===void 0)return a({kind:"SemanticQueryError",reason:"recast File is missing program.body"});const f=O(c,i);if(f===void 0)return a({kind:"SemanticQueryError",reason:"Unable to resolve config ObjectExpression for the located resource."});const u=new Set(Object.keys(N[n].schemaFragment.shape)),m={};for(const o of f.properties){if(o.type!=="Property"&&o.type!=="ObjectProperty"||o.shorthand===!0||o.computed===!0)continue;const d=b(o.key);d===void 0||!u.has(d)||o.value!==void 0&&(m[d]=j(o.value,t))}return y({type:n,name:r,properties:m,schemaVersion:1})}function O(t,n){const r=n.start+n.length;let e;const i=s=>{if(e!==void 0||s.type!=="CallExpression")return;const c=s.arguments;if(!Array.isArray(c)||c.length<2)return;const f=c[0];if(!l(f)||f.type!=="StringLiteral"||f.start!==n.start||f.end!==r)return;const u=c[1];l(u)&&u.type==="ObjectExpression"&&(e=u)};for(const s of t)if(v(s,i),e!==void 0)break;return e}function j(t,n){const r=g(t);return r!==k?r:S(t,n)}function S(t,n){const r=typeof t.start=="number"?t.start:void 0,e=typeof t.end=="number"?t.end:void 0;return{kind:"expression",text:r!==void 0&&e!==void 0&&r<e?n.slice(r,e):""}}function v(t,n){const r=[t];for(;r.length>0;){const e=r.pop();if(p(e)){typeof e.type=="string"&&n(e);for(const i of Object.keys(e)){if(i==="loc"||i==="comments"||i==="tokens")continue;const s=e[i];if(Array.isArray(s))for(const c of s)p(c)&&r.push(c);else p(s)&&r.push(s)}}}}export{F as snapshotProperties};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { PropertyDeltaSchema } from "../types.js";
|
|
3
|
+
export { DriftPolicySchema, type DriftPolicy } from "../types.js";
|
|
4
|
+
export { PropertyDeltaSchema };
|
|
5
|
+
export declare const ResourceSnapshotSchema: z.ZodObject<{
|
|
6
|
+
type: z.ZodEnum<{
|
|
7
|
+
storage: "storage";
|
|
8
|
+
database: "database";
|
|
9
|
+
compute: "compute";
|
|
10
|
+
pattern: "pattern";
|
|
11
|
+
network: "network";
|
|
12
|
+
messaging: "messaging";
|
|
13
|
+
cdn: "cdn";
|
|
14
|
+
"vpc-peer": "vpc-peer";
|
|
15
|
+
"vpc-peer-accepter": "vpc-peer-accepter";
|
|
16
|
+
"cross-plan-connection": "cross-plan-connection";
|
|
17
|
+
}>;
|
|
18
|
+
name: z.ZodString;
|
|
19
|
+
properties: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
20
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
21
|
+
}, z.core.$strict>;
|
|
22
|
+
export declare const DriftStateSchema: z.ZodObject<{
|
|
23
|
+
resource: z.ZodObject<{
|
|
24
|
+
type: z.ZodEnum<{
|
|
25
|
+
storage: "storage";
|
|
26
|
+
database: "database";
|
|
27
|
+
compute: "compute";
|
|
28
|
+
pattern: "pattern";
|
|
29
|
+
network: "network";
|
|
30
|
+
messaging: "messaging";
|
|
31
|
+
cdn: "cdn";
|
|
32
|
+
"vpc-peer": "vpc-peer";
|
|
33
|
+
"vpc-peer-accepter": "vpc-peer-accepter";
|
|
34
|
+
"cross-plan-connection": "cross-plan-connection";
|
|
35
|
+
}>;
|
|
36
|
+
name: z.ZodString;
|
|
37
|
+
}, z.core.$strict>;
|
|
38
|
+
deltas: z.ZodArray<z.ZodObject<{
|
|
39
|
+
property: z.ZodString;
|
|
40
|
+
base: z.ZodOptional<z.ZodUnknown>;
|
|
41
|
+
theirs: z.ZodUnknown;
|
|
42
|
+
ours: z.ZodUnknown;
|
|
43
|
+
verdict: z.ZodEnum<{
|
|
44
|
+
clean: "clean";
|
|
45
|
+
"no-op": "no-op";
|
|
46
|
+
compatible: "compatible";
|
|
47
|
+
conflict: "conflict";
|
|
48
|
+
}>;
|
|
49
|
+
}, z.core.$strict>>;
|
|
50
|
+
summary: z.ZodEnum<{
|
|
51
|
+
unknown: "unknown";
|
|
52
|
+
clean: "clean";
|
|
53
|
+
"no-op": "no-op";
|
|
54
|
+
"drifted-compatible": "drifted-compatible";
|
|
55
|
+
"drifted-conflict": "drifted-conflict";
|
|
56
|
+
}>;
|
|
57
|
+
}, z.core.$strict>;
|
|
58
|
+
export type PropertyDelta = z.infer<typeof PropertyDeltaSchema>;
|
|
59
|
+
export type ResourceSnapshot = z.infer<typeof ResourceSnapshotSchema>;
|
|
60
|
+
export type DriftState = z.infer<typeof DriftStateSchema>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{z as e}from"zod";import{PropertyDeltaSchema as t,ResourceNameSchema as r,StatementTypeSchema as o}from"../types.js";import{DriftPolicySchema as s}from"../types.js";const m=e.object({type:o,name:r,properties:e.record(e.string(),e.unknown()),schemaVersion:e.literal(1)}).strict(),n=e.object({resource:e.object({type:o,name:r}).strict(),deltas:e.array(t),summary:e.enum(["clean","no-op","drifted-compatible","drifted-conflict","unknown"])}).strict();export{s as DriftPolicySchema,n as DriftStateSchema,t as PropertyDeltaSchema,m as ResourceSnapshotSchema};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type namedTypes as n } from "ast-types";
|
|
2
2
|
import { type Result } from "../../../types/Result.js";
|
|
3
|
-
import type
|
|
3
|
+
import { type ObjectPropertyInput, type QuoteStyle } from "../../fileRewriter/index.js";
|
|
4
4
|
import type { InvalidPropertyError } from "../../types.js";
|
|
5
5
|
export declare function buildPropertyInputs(properties: Record<string, unknown>, quoteStyle: QuoteStyle): Result<ObjectPropertyInput[], InvalidPropertyError>;
|
|
6
6
|
export declare function toAstValue(value: unknown, quoteStyle: QuoteStyle): n.Property["value"] | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{builders as
|
|
1
|
+
import{builders as i}from"ast-types";import{failure as p,success as c}from"../../../types/Result.js";import{buildObjectProperty as u}from"../../fileRewriter/index.js";function m(r,n){const e=[];for(const[t,o]of Object.entries(r)){const s=f(o,n);if(s===void 0)return p({kind:"InvalidPropertyError",property:t,reason:`Unsupported property value for "${t}" (accepts string/number/boolean/null/array/object of the same).`});e.push({key:t,value:s})}return c(e)}function f(r,n){if(typeof r=="string")return a(r,n);if(typeof r=="number"&&Number.isFinite(r))return i.numericLiteral(r);if(typeof r=="boolean")return i.booleanLiteral(r);if(r===null)return i.nullLiteral();if(Array.isArray(r)){const e=[];for(const t of r){const o=f(t,n);if(o===void 0)return;e.push(o)}return i.arrayExpression(e)}if(typeof r=="object"&&r!==null){const e=[];for(const[t,o]of Object.entries(r)){const s=f(o,n);if(s===void 0)return;e.push(u(t,s,n))}return i.objectExpression(e)}}function a(r,n){const e=n==="single"?"'":'"',t=n==="single"?r.replace(/\\/g,"\\\\").replace(/'/g,"\\'"):r.replace(/\\/g,"\\\\").replace(/"/g,'\\"'),o=`${e}${t}${e}`;return i.stringLiteral.from({value:r,extra:{rawValue:r,raw:o}})}export{m as buildPropertyInputs,a as makeStringLiteral,f as toAstValue};
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { type Result } from "../../types/Result.js";
|
|
2
|
-
import
|
|
2
|
+
import { type ResourceSnapshot } from "../drift/index.js";
|
|
3
|
+
import type { AddOptions, ControlFlowClassifierError, DriftConflictError, DuplicateResourceError, InvalidPropertyError, LinesChanged, ParseError, SemanticQueryError, TemplateLiteralNameError } from "../types.js";
|
|
3
4
|
export type { AddOptions, LinesChanged } from "../types.js";
|
|
4
|
-
export type AddResourceError = ParseError | TemplateLiteralNameError | DuplicateResourceError | InvalidPropertyError | SemanticQueryError;
|
|
5
|
+
export type AddResourceError = ParseError | TemplateLiteralNameError | DuplicateResourceError | InvalidPropertyError | SemanticQueryError | DriftConflictError | ControlFlowClassifierError;
|
|
5
6
|
export interface AddResourceSuccess {
|
|
6
7
|
content: string;
|
|
7
8
|
linesChanged: LinesChanged;
|
|
9
|
+
skipped?: boolean;
|
|
8
10
|
}
|
|
9
|
-
export
|
|
11
|
+
export interface AddResourceCallContext {
|
|
12
|
+
baseline?: ResourceSnapshot;
|
|
13
|
+
}
|
|
14
|
+
export declare function addResource(content: string, options: AddOptions, context?: AddResourceCallContext): Result<AddResourceSuccess, AddResourceError>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{failure as o,success as
|
|
1
|
+
import{failure as o,success as E}from"../../types/Result.js";import{DEFAULT_FILE_PATH as g,computeLinesDelta as T,extractProgramBody as F,isRecord as C}from"../_internal.js";import{appendSpecifier as D,buildFactoryStatement as b,buildImportDeclaration as x,detectQuoteStyle as w,parse as Q,printFile as v}from"../fileRewriter/index.js";import{STATEMENT_REGISTRY as A}from"../registry.js";import{locateAllShapes as _,locateByShape as B}from"../semanticIndex/index.js";import{checkControlFlowPolicy as G}from"./controlFlowPolicy.js";import{driftGate as N,runPipeline as O,schemaGate as U}from"../validationGate/index.js";import{buildResourceLocations as M,indexBody as Y,orderByEndPos as j,resolveInsertIndex as q}from"./addResource/bodyIndex.js";import{buildPropertyInputs as H}from"./addResource/propertyBuilder.js";import{findInsertionPosition as $}from"./findInsertionPosition.js";const S="@fjall/components-infrastructure";function nr(e,r,u={}){const i=r.filePath??g,a=Q(e,i);if(!a.success)return o(a.error);const t=B(e,{type:r.type,name:r.name},i);if(!t.success)return t.error.kind==="TemplateLiteralNameError"?o(t.error):o({kind:"SemanticQueryError",reason:t.error.reason,cause:t.error.cause});if(t.data!==void 0){const y=G({ast:a.data,target:t.data,resource:{type:r.type,name:r.name},op:"add"});if(y.refusal!==void 0)return o(y.refusal);const l=O([U,N],{content:e,plan:{type:r.type,name:r.name,properties:r.properties,op:"add"},baseline:u.baseline,policy:r.driftPolicy});return l.success?l.data.action==="skip"?E({content:e,linesChanged:{added:0,removed:0},skipped:!0}):o({kind:"DuplicateResourceError",type:r.type,name:r.name}):o(z(l.error))}const d=w(a.data),p=H(r.properties,d);if(!p.success)return o(p.error);const m=A[r.type].factoryIdentifier,I=b({factory:m,name:r.name,properties:p.data,quoteStyle:d}),c=F(a.data);if(c===void 0)return o({kind:"SemanticQueryError",reason:"recast File is missing program.body"});const s=Y(c,e),n=_(e,i);if(!n.success)return n.error.kind==="TemplateLiteralNameError"?o(n.error):o({kind:"SemanticQueryError",reason:n.error.reason,cause:n.error.cause});const h=M(s.anchors,n.data),k=j([...s.appInitLocations,...h]),L=$(s.importInfos,k,r.type,s.programEnd),P=q(s.anchors,L);c.splice(P,0,I),J(c,m,d);const f=v(a.data,e),R=T(e,f);return E({content:f,linesChanged:R})}function z(e){switch(e.kind){case"ParseError":case"TemplateLiteralNameError":case"DuplicateResourceError":case"InvalidPropertyError":case"SemanticQueryError":case"DriftConflictError":case"ControlFlowClassifierError":return e;default:return{kind:"SemanticQueryError",reason:`Unexpected codemod error in validation-gate pipeline: ${e.kind}`}}}function J(e,r,u){for(const a of e){if(a.type!=="ImportDeclaration")continue;const t=a.source;if(!(!C(t)||t.type!=="StringLiteral")&&t.value===S){D(a,r);return}}const i=x([r],S,u);e.unshift(i)}export{nr as addResource};
|