@fjall/generator 0.95.0 → 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.
Files changed (124) hide show
  1. package/dist/.minified +1 -1
  2. package/dist/src/ast/astCodeInjection.d.ts +9 -0
  3. package/dist/src/ast/astCodeInjection.js +8 -0
  4. package/dist/src/ast/astInfrastructureParser.d.ts +1 -1
  5. package/dist/src/ast/astStatementClassifier.d.ts +2 -2
  6. package/dist/src/ast/astStatementClassifier.js +1 -1
  7. package/dist/src/ast/astStatementQueries.d.ts +4 -4
  8. package/dist/src/ast/astStatementQueries.js +3 -3
  9. package/dist/src/ast/astSurgicalModification.d.ts +14 -12
  10. package/dist/src/ast/astSurgicalModification.js +6 -13
  11. package/dist/src/ast/astTestHelpers.d.ts +1 -1
  12. package/dist/src/ast/index.d.ts +3 -2
  13. package/dist/src/ast/index.js +1 -1
  14. package/dist/src/codemod/_internal.d.ts +6 -1
  15. package/dist/src/codemod/_internal.js +1 -1
  16. package/dist/src/codemod/drift/__tests__/fixtures.d.ts +55 -0
  17. package/dist/src/codemod/drift/__tests__/fixtures.js +2 -0
  18. package/dist/src/codemod/drift/detect.d.ts +11 -0
  19. package/dist/src/codemod/drift/detect.js +1 -0
  20. package/dist/src/codemod/drift/index.d.ts +4 -0
  21. package/dist/src/codemod/drift/index.js +1 -0
  22. package/dist/src/codemod/drift/merge.d.ts +19 -0
  23. package/dist/src/codemod/drift/merge.js +1 -0
  24. package/dist/src/codemod/drift/snapshot.d.ts +4 -0
  25. package/dist/src/codemod/drift/snapshot.js +1 -0
  26. package/dist/src/codemod/drift/types.d.ts +60 -0
  27. package/dist/src/codemod/drift/types.js +1 -0
  28. package/dist/src/codemod/edits/addResource/propertyBuilder.d.ts +1 -1
  29. package/dist/src/codemod/edits/addResource/propertyBuilder.js +1 -1
  30. package/dist/src/codemod/edits/addResource.d.ts +8 -3
  31. package/dist/src/codemod/edits/addResource.js +1 -1
  32. package/dist/src/codemod/edits/controlFlowPolicy.d.ts +19 -0
  33. package/dist/src/codemod/edits/controlFlowPolicy.js +1 -0
  34. package/dist/src/codemod/edits/crossPlanConnection.d.ts +24 -0
  35. package/dist/src/codemod/edits/crossPlanConnection.js +1 -0
  36. package/dist/src/codemod/edits/driftPolicy.d.ts +7 -0
  37. package/dist/src/codemod/edits/driftPolicy.js +1 -0
  38. package/dist/src/codemod/edits/findInsertionPosition.js +1 -1
  39. package/dist/src/codemod/edits/index.d.ts +3 -0
  40. package/dist/src/codemod/edits/index.js +1 -1
  41. package/dist/src/codemod/edits/modifyResource.d.ts +9 -3
  42. package/dist/src/codemod/edits/modifyResource.js +1 -1
  43. package/dist/src/codemod/edits/removeResource.d.ts +2 -2
  44. package/dist/src/codemod/edits/removeResource.js +1 -1
  45. package/dist/src/codemod/edits/vpcPeer.d.ts +28 -0
  46. package/dist/src/codemod/edits/vpcPeer.js +1 -0
  47. package/dist/src/codemod/edits/vpcPeerAccepter.d.ts +23 -0
  48. package/dist/src/codemod/edits/vpcPeerAccepter.js +1 -0
  49. package/dist/src/codemod/index.d.ts +16 -4
  50. package/dist/src/codemod/index.js +1 -1
  51. package/dist/src/codemod/llmFallback/__tests__/fixtures.d.ts +5 -0
  52. package/dist/src/codemod/llmFallback/__tests__/fixtures.js +7 -0
  53. package/dist/src/codemod/llmFallback/apply.d.ts +10 -0
  54. package/dist/src/codemod/llmFallback/apply.js +1 -0
  55. package/dist/src/codemod/llmFallback/claudeTier.d.ts +6 -0
  56. package/dist/src/codemod/llmFallback/claudeTier.js +1 -0
  57. package/dist/src/codemod/llmFallback/egressGate.d.ts +5 -0
  58. package/dist/src/codemod/llmFallback/egressGate.js +1 -0
  59. package/dist/src/codemod/llmFallback/egressGate.types.d.ts +9 -0
  60. package/dist/src/codemod/llmFallback/egressGate.types.js +0 -0
  61. package/dist/src/codemod/llmFallback/index.d.ts +6 -0
  62. package/dist/src/codemod/llmFallback/index.js +1 -0
  63. package/dist/src/codemod/llmFallback/morphTier.d.ts +2 -0
  64. package/dist/src/codemod/llmFallback/morphTier.js +3 -0
  65. package/dist/src/codemod/llmFallback/prompt.d.ts +12 -0
  66. package/dist/src/codemod/llmFallback/prompt.js +36 -0
  67. package/dist/src/codemod/llmFallback/runFallback.d.ts +13 -0
  68. package/dist/src/codemod/llmFallback/runFallback.js +1 -0
  69. package/dist/src/codemod/llmFallback/shouldTryFallback.d.ts +13 -0
  70. package/dist/src/codemod/llmFallback/shouldTryFallback.js +1 -0
  71. package/dist/src/codemod/llmFallback/signals.d.ts +4 -0
  72. package/dist/src/codemod/llmFallback/signals.js +1 -0
  73. package/dist/src/codemod/llmFallback/telemetryEvents.d.ts +141 -0
  74. package/dist/src/codemod/llmFallback/telemetryEvents.js +1 -0
  75. package/dist/src/codemod/llmFallback/tierRunner.d.ts +7 -0
  76. package/dist/src/codemod/llmFallback/tierRunner.js +1 -0
  77. package/dist/src/codemod/llmFallback/types.d.ts +104 -0
  78. package/dist/src/codemod/llmFallback/types.js +1 -0
  79. package/dist/src/codemod/registry.d.ts +4 -1
  80. package/dist/src/codemod/registry.js +1 -1
  81. package/dist/src/codemod/semanticIndex/classifyControlFlow.d.ts +2 -0
  82. package/dist/src/codemod/semanticIndex/classifyControlFlow.js +1 -0
  83. package/dist/src/codemod/semanticIndex/findReferences.js +2 -2
  84. package/dist/src/codemod/telemetry/__tests__/errorKinds.fixture.d.ts +1 -0
  85. package/dist/src/codemod/telemetry/__tests__/errorKinds.fixture.js +1 -0
  86. package/dist/src/codemod/telemetry/errorKinds.d.ts +2 -0
  87. package/dist/src/codemod/telemetry/errorKinds.js +1 -0
  88. package/dist/src/codemod/types.d.ts +105 -1
  89. package/dist/src/codemod/types.js +1 -1
  90. package/dist/src/codemod/validationGate/gates/classify.d.ts +2 -0
  91. package/dist/src/codemod/validationGate/gates/classify.js +1 -0
  92. package/dist/src/codemod/validationGate/gates/drift.d.ts +2 -0
  93. package/dist/src/codemod/validationGate/gates/drift.js +1 -0
  94. package/dist/src/codemod/validationGate/gates/locate.d.ts +7 -0
  95. package/dist/src/codemod/validationGate/gates/locate.js +1 -0
  96. package/dist/src/codemod/validationGate/gates/parse.d.ts +2 -0
  97. package/dist/src/codemod/validationGate/gates/parse.js +1 -0
  98. package/dist/src/codemod/validationGate/gates/schema.d.ts +2 -0
  99. package/dist/src/codemod/validationGate/gates/schema.js +1 -0
  100. package/dist/src/codemod/validationGate/index.d.ts +6 -0
  101. package/dist/src/codemod/validationGate/index.js +1 -0
  102. package/dist/src/codemod/validationGate/types.d.ts +35 -0
  103. package/dist/src/codemod/validationGate/types.js +1 -0
  104. package/dist/src/planning/index.d.ts +2 -1
  105. package/dist/src/planning/index.js +1 -1
  106. package/dist/src/planning/openNextPlanning.d.ts +38 -0
  107. package/dist/src/planning/openNextPlanning.js +1 -0
  108. package/dist/src/planning/resourcePlanning.d.ts +0 -46
  109. package/dist/src/planning/resourcePlanning.js +1 -1
  110. package/dist/src/schemas/applicationSchemas.d.ts +16 -0
  111. package/dist/src/schemas/applicationSchemas.js +1 -1
  112. package/dist/src/schemas/baseSchemas.d.ts +8 -5
  113. package/dist/src/schemas/baseSchemas.js +2 -2
  114. package/dist/src/schemas/networkSchemas.d.ts +119 -5
  115. package/dist/src/schemas/networkSchemas.js +1 -1
  116. package/dist/src/validation/patterns.d.ts +2 -318
  117. package/dist/src/validation/patterns.js +1 -1
  118. package/dist/src/validation/validationMessages.d.ts +314 -0
  119. package/dist/src/validation/validationMessages.js +1 -0
  120. package/dist/src/validation/validationPatterns.d.ts +20 -0
  121. package/dist/src/validation/validationPatterns.js +1 -0
  122. package/dist/src/version.d.ts +1 -1
  123. package/dist/src/version.js +1 -1
  124. package/package.json +3 -3
package/dist/.minified CHANGED
@@ -1 +1 @@
1
- 93 files minified at 2026-04-21T03:05:07.650Z
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 StatementType } from "../schemas/resourceSchemas.js";
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 { StatementType } from "../schemas/resourceSchemas.js";
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: StatementType;
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 p}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"]);function l(r){return!!(r.startsWith("@fjall/")||r==="aws-cdk-lib"||r.startsWith("aws-cdk-lib/")||r==="constructs")}function h(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(p(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 s=i.expression.text;if(d.has(s)){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=u(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)?u(t,r,n,e):{type:"custom",node:r,startPos:n,endPos:e,isManaged:!1}}function u(r,n,e,t){const i=r.expression;if(a.isPropertyAccessExpression(i)&&a.isIdentifier(i.expression)){const s=i.name.text;if(s==="addTags")return{type:"tags",node:n,startPos:e,endPos:t,isManaged:!0};const o={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"}}[s];if(o){const f=S(r,o.factory);return{type:o.type,node:n,resourceName:f,startPos:e,endPos:t,isManaged:!0}}}return{type:"custom",node:n,startPos:e,endPos:t,isManaged:!1}}function S(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,h as classifyStatements,l as isManagedModuleSpecifier};
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 { StatementType, CustomCodeBlock } from "../schemas/resourceSchemas.js";
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: StatementType, resourceName: string, precomputedClassifications?: ClassifiedStatement[]): {
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: StatementType, precomputedClassifications?: ClassifiedStatement[]): ClassifiedStatement | null;
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<StatementType, ClassifiedStatement[]>;
21
+ export declare function getManagedResourcesByType(sourceFile: ts.SourceFile, precomputedClassifications?: ClassifiedStatement[]): Record<AstStatementType, ClassifiedStatement[]>;
@@ -1,3 +1,3 @@
1
- import*as p from"typescript";import{classifyStatements as a}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??a(t),o=[],s=t.getFullText();let e=null;for(const r of i)if(r.type==="custom"&&!r.isManaged){const{position:c,afterManagedResource:u}=g(e),m=x(s,r.node),f=y(s,r.node),l=t.getLineAndCharacterOfPosition(r.node.getStart());o.push({sourceText:m,position:c,afterManagedResource:u,originalLine:l.line+1,leadingComments:f.length>0?f:void 0})}else r.isManaged&&(e=r);return o}function x(t,n){const i=n.getFullStart(),o=n.getEnd(),e=t.slice(i,o).split(`
2
- `),r=e.findIndex(c=>c.trim()!=="");return e.slice(r===-1?0:r).join(`
3
- `)}function y(t,n){const i=[],o=p.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??a(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??a(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??a(t),o={import:[],"app-init":[],tags:[],database:[],compute:[],storage:[],network:[],messaging:[],cdn:[],pattern:[],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
+ 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 { type StatementType, type CustomCodeBlock } from "./astInfrastructureParser.js";
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: StatementType;
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: StatementType;
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: StatementType;
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: StatementType;
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 f from"typescript";import{parseInfrastructure as D,classifyStatements as y,findManagedResourcePosition as C}from"./astInfrastructureParser.js";import{getErrorMessage as M}from"../util/errorUtils.js";function R(e,t,r){return e.slice(0,t)+r+e.slice(t)}function P(e,t){return e.find(r=>r.type===t)}function L(e,t){return e.filter(r=>r.type===t).pop()}function m(e){return f.createSourceFile("infrastructure.ts",e,f.ScriptTarget.Latest,!0,f.ScriptKind.TS)}function I(e,t){return e.endsWith(`
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
- `)||t.startsWith(`
3
+ `)||r.startsWith(`
4
4
  `)?"":e.endsWith(`
5
5
  `)?`
6
6
  `:`
7
7
 
8
- `}function E(e,t){return e.slice(Math.max(0,t-2),t)}function p(e,t){return t?`${e}:${t}`:e}function l(e,t){return{content:t,success:!1,error:M(e)}}function S(e,t,r,s){const n=C(e,r,s);return n||l(new Error(`Resource not found: ${p(r,s)}`),t)}function T(e){return"success"in e&&!e.success}function J(e,t){const{resourceType:r,code:s,afterResource:n}=t;try{const o=m(e);let i;if(n){const u=C(o,n.type,n.name);if(!u)return l(new Error(`Insertion target not found: ${p(n.type,n.name)}`),e);i=u.endPos}else i=$(o,r);const c=B(e,i,s);return{content:R(e,i,c),success:!0}}catch(o){return l(o,e)}}function $(e,t){const r=y(e),s=["import","app-init","tags","database","storage","messaging","compute","network","cdn","pattern"],n=s.indexOf(t);let o=null;for(const i of r)if(i.isManaged){const c=s.indexOf(i.type);c!==-1&&c<=n&&(o=i)}return o?o.endPos:e.getEnd()}function B(e,t,r){const s=E(e,t),n=e.slice(t,Math.min(e.length,t+2)),o=I(s,r),i=n.startsWith(`
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 o+r+i}function Q(e,t){const{resourceType:r,resourceName:s,newCode:n}=t;try{const o=m(e),i=S(o,e,r,s);if(T(i))return i;const c=i,a=c.node.getFullStart(),u=c.endPos,d=e.slice(a,c.node.getStart()),g=W(d);return{content:e.slice(0,a)+g+n+e.slice(u),success:!0}}catch(o){return l(o,e)}}function W(e){const t=e.match(/^[\s]*[\n\r]+[\s]*/);return t?t[0]:""}function H(e,t,r,s){let n=e,o=0;for(const i of t){const c=`// [ORPHANED: was after ${p(r,s)}]
11
- `,a=i.sourceText.trim(),u=n.indexOf(a,o);u!==-1&&(n=R(n,u,c),o=u+c.length+a.length)}return n}function V(e,t){const{resourceType:r,resourceName:s,orphanHandling:n}=t,o=[];try{const i=m(e),c=S(i,e,r,s);if(T(c))return c;const a=c,d=(D(e,{extractCustomCode:!0}).customCodeBlocks??[]).filter(x=>x.position==="after-resource"&&x.afterManagedResource?.type===r&&x.afterManagedResource?.name===s);d.length>0&&n==="preserve-with-warning"&&o.push(`Custom code after ${p(r,s)} will be orphaned. Added // [ORPHANED] marker comment.`);const g=a.node.getFullStart(),h=a.endPos,A=e[h]===`
12
- `?h+1:h;let w=e.slice(0,g)+e.slice(A);return d.length>0&&n==="preserve-with-warning"&&(w=H(w,d,r,s)),{content:w,success:!0,warnings:o.length>0?o:void 0}}catch(i){return l(i,e)}}function Y(e,t){try{const r=m(e),s=new Set;for(const a of r.statements)f.isImportDeclaration(a)&&s.add(a.getText(r).trim());const n=[];for(const a of t){const u=a.trim();[...s].some(g=>q(g,u))||n.push(u)}if(n.length===0)return{content:e,success:!0};const o=r.statements.filter(f.isImportDeclaration).at(-1)?.getEnd()??0,i=`
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};
@@ -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" | "storage" | "database" | "compute" | "pattern" | "import" | "app-init" | "tags" | "network" | "messaging" | "cdn";
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;
@@ -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 StatementType, type ClassifiedStatement, type CustomCodeBlock, parseInfrastructure, convertToResourcePlan, classifyStatements, extractCustomCodeBlocks, findManagedResourcePosition, getLastManagedStatementOfType, getManagedResourcesByType, } from "./astInfrastructureParser.js";
3
- export { type SurgicalModificationResult, type InsertionOptions, type UpdateOptions, type DeleteOptions, type FileValidationResult, addResourceSurgically, updateResourceSurgically, removeResourceSurgically, ensureImports, injectCustomCodeBlocks, validateModifiedFile, } from "./astSurgicalModification.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 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";
@@ -1 +1 @@
1
- import{isPlainObject as s,isIdentifierRef as a,isExpressionRef as o,isCallRef as t,isParsedObject as i,asString as n,asNumber as c,asBoolean as l,asStringArray as u,asStringUnion as p,captureExtraProperties as d}from"./astCommonParser.js";import{parseInfrastructure as m,convertToResourcePlan as g,classifyStatements as R,extractCustomCodeBlocks as y,findManagedResourcePosition as S,getLastManagedStatementOfType as x,getManagedResourcesByType as C}from"./astInfrastructureParser.js";import{addResourceSurgically as M,updateResourceSurgically as v,removeResourceSurgically as B,ensureImports as T,injectCustomCodeBlocks as b,validateModifiedFile as j}from"./astSurgicalModification.js";import{findDomainConstructCall as E,parseDomainProps as I,parseDomainErrorMessage as O,splitTopLevelArgs as k}from"./astDomainParser.js";export{M as addResourceSurgically,l as asBoolean,c as asNumber,n as asString,u as asStringArray,p as asStringUnion,d as captureExtraProperties,R as classifyStatements,g as convertToResourcePlan,T as ensureImports,y as extractCustomCodeBlocks,E as findDomainConstructCall,S as findManagedResourcePosition,x as getLastManagedStatementOfType,C as getManagedResourcesByType,b as injectCustomCodeBlocks,t as isCallRef,o as isExpressionRef,a as isIdentifierRef,i as isParsedObject,s as isPlainObject,O as parseDomainErrorMessage,I as parseDomainProps,m as parseInfrastructure,B as removeResourceSurgically,k as splitTopLevelArgs,v as updateResourceSurgically,j as validateModifiedFile};
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
- export declare function extractProgramBody<T>(file: unknown): T[] | undefined;
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(n=>o(n)))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};
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 { ObjectPropertyInput, QuoteStyle } from "../../fileRewriter/index.js";
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 o}from"ast-types";import{failure as p,success as u}from"../../../types/Result.js";function d(r,e){const n=[];for(const[t,i]of Object.entries(r)){const s=a(i,e);if(s===void 0)return p({kind:"InvalidPropertyError",property:t,reason:`Unsupported property value for "${t}" (Phase 1 accepts string/number/boolean/null only).`});n.push({key:t,value:s})}return u(n)}function a(r,e){if(typeof r=="string")return l(r,e);if(typeof r=="number"&&Number.isFinite(r))return o.numericLiteral(r);if(typeof r=="boolean")return o.booleanLiteral(r);if(r===null)return o.nullLiteral()}function l(r,e){const n=e==="single"?"'":'"',t=e==="single"?r.replace(/\\/g,"\\\\").replace(/'/g,"\\'"):r.replace(/\\/g,"\\\\").replace(/"/g,'\\"'),i=`${n}${t}${n}`;return o.stringLiteral.from({value:r,extra:{rawValue:r,raw:i}})}export{d as buildPropertyInputs,l as makeStringLiteral,a as toAstValue};
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 type { AddOptions, DuplicateResourceError, InvalidPropertyError, LinesChanged, ParseError, SemanticQueryError, TemplateLiteralNameError } from "../types.js";
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 declare function addResource(content: string, options: AddOptions): Result<AddResourceSuccess, AddResourceError>;
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 h}from"../../types/Result.js";import{DEFAULT_FILE_PATH as T,computeLinesDelta as R,extractProgramBody as F,isRecord as P}from"../_internal.js";import{appendSpecifier as g,buildFactoryStatement as k,buildImportDeclaration as x,detectQuoteStyle as D,parse as b,printFile as A}from"../fileRewriter/index.js";import{STATEMENT_REGISTRY as _}from"../registry.js";import{locateAllShapes as B,locateByShape as O}from"../semanticIndex/index.js";import{buildResourceLocations as Q,indexBody as C,orderByEndPos as N,resolveInsertIndex as U}from"./addResource/bodyIndex.js";import{buildPropertyInputs as v}from"./addResource/propertyBuilder.js";import{findInsertionPosition as w}from"./findInsertionPosition.js";const f="@fjall/components-infrastructure";function V(t,r){const i=r.filePath??T,n=b(t,i);if(!n.success)return o(n.error);const e=O(t,{type:r.type,name:r.name},i);if(!e.success)return e.error.kind==="TemplateLiteralNameError"?o(e.error):o({kind:"SemanticQueryError",reason:e.error.reason,cause:e.error.cause});if(e.data!==void 0)return o({kind:"DuplicateResourceError",type:r.type,name:r.name});const a=D(n.data),m=v(r.properties,a);if(!m.success)return o(m.error);const d=_[r.type].factoryIdentifier,l=k({factory:d,name:r.name,properties:m.data,quoteStyle:a}),u=F(n.data);if(u===void 0)return o({kind:"SemanticQueryError",reason:"recast File is missing program.body"});const c=C(u,t),s=B(t,i);if(!s.success)return s.error.kind==="TemplateLiteralNameError"?o(s.error):o({kind:"SemanticQueryError",reason:s.error.reason,cause:s.error.cause});const y=Q(c.anchors,s.data),E=N([...c.appInitLocations,...y]),S=w(c.importInfos,E,r.type,c.programEnd),I=U(c.anchors,S);u.splice(I,0,l),M(u,d,a);const p=A(n.data,t),L=R(t,p);return h({content:p,linesChanged:L})}function M(t,r,i){for(const e of t){if(e.type!=="ImportDeclaration")continue;const a=e.source;if(!(!P(a)||a.type!=="StringLiteral")&&a.value===f){g(e,r);return}}const n=x([r],f,i);t.unshift(n)}export{V as addResource};
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};
@@ -0,0 +1,19 @@
1
+ import type { ControlFlowClassifierError, NodeLocation, StatementType } from "../types.js";
2
+ export type ControlFlowPolicyOp = "add" | "remove" | "modify";
3
+ export interface ControlFlowPolicyInput {
4
+ ast: unknown;
5
+ target: NodeLocation;
6
+ resource: {
7
+ type: StatementType;
8
+ name: string;
9
+ };
10
+ op: ControlFlowPolicyOp;
11
+ }
12
+ export interface ControlFlowWarning {
13
+ message: string;
14
+ }
15
+ export interface ControlFlowPolicyResult {
16
+ refusal?: ControlFlowClassifierError;
17
+ warning?: ControlFlowWarning;
18
+ }
19
+ export declare function checkControlFlowPolicy(input: ControlFlowPolicyInput): ControlFlowPolicyResult;
@@ -0,0 +1 @@
1
+ import{isRecord as y}from"../_internal.js";const h=new Set(["loc","tokens","comments","original","range"]),p=new Set(["ForStatement","ForInStatement","ForOfStatement","WhileStatement","DoWhileStatement"]),m={"try-catch":"Move the resource declaration outside the try block.","switch-fall-through":"Add a break statement or convert to if/else.","nested-ternary":"Convert nested ternaries to if/else.",loop:"Extract the resource declaration before the loop.","generator-function":"Extract the resource declaration outside the generator.","async-iterator":"Extract the resource declaration outside the async iterator.","anonymous-helper":"Extract the resource to module scope.","decorator-return":"Extract the resource declaration outside the decorated function."};function g(e,r){if(!c(e))return;const n=[{node:e,ancestors:[]}];for(;n.length>0;){const s=n.pop();if(s===void 0)continue;const{node:o,ancestors:a}=s;if(o.type==="StringLiteral"&&typeof o.start=="number"&&o.start===r)return{node:o,ancestors:a};const t=[...a,o];for(const u of Object.keys(o)){if(h.has(u))continue;const f=o[u];if(Array.isArray(f))for(let d=f.length-1;d>=0;d-=1){const l=f[d];c(l)&&n.push({node:l,ancestors:t})}else c(f)&&n.push({node:f,ancestors:t})}}}function c(e){return typeof e=="object"&&e!==null&&typeof e.type=="string"}function i(e,r,n){return{kind:"ControlFlowClassifierError",reason:e,resource:r,location:S(n),remediation:m[e]}}function S(e){const r=e.loc;if(r?.start!==void 0&&typeof r.start.line=="number"&&typeof r.start.column=="number")return{line:r.start.line,column:r.start.column}}function F(e,r){const n=e.finalizer;return n!==void 0&&n===r}function E(e,r){const n=e.handler;return n!==void 0&&n===r}function b(e){const r=e.consequent;if(!Array.isArray(r)||r.length===0)return!0;const n=r[r.length-1];return c(n)?n.type!=="BreakStatement"&&n.type!=="ReturnStatement":!0}function k(e){const r=A(e.ast);if(r===void 0)return{};const n=g(r,e.target.start);if(n===void 0)return{};const{ancestors:s}=n;let o=n.node;for(let a=s.length-1;a>=0;a-=1){const t=s[a];if(t!==void 0){if(p.has(t.type))return{refusal:i("loop",e.resource,t)};if(t.type==="TryStatement"){if(E(t,o)||F(t,o))return{refusal:i("try-catch",e.resource,t)};o=t;continue}if(t.type==="CatchClause")return{refusal:i("try-catch",e.resource,t)};if(t.type==="SwitchCase"&&b(t))return{refusal:i("switch-fall-through",e.resource,t)};if(t.type==="ConditionalExpression"){const u=a-1;if((u>=0?s[u]:void 0)?.type==="ConditionalExpression")return{refusal:i("nested-ternary",e.resource,t)};if(e.op==="modify")return{warning:{message:`Modified property on resource "${e.resource.name}" declared in a ternary expression \u2014 consider converting to if/else.`}};if(e.op==="add"||e.op==="remove")return{refusal:i("nested-ternary",e.resource,t)}}if(t.type==="FunctionDeclaration"||t.type==="FunctionExpression"||t.type==="ArrowFunctionExpression"||t.type==="ObjectMethod"||t.type==="ClassMethod")return t.generator===!0?{refusal:i("generator-function",e.resource,t)}:t.async===!0&&w(t)?{refusal:i("async-iterator",e.resource,t)}:x(t)===void 0?{refusal:i("anonymous-helper",e.resource,t)}:{refusal:i("anonymous-helper",e.resource,t)};o=t}}return{}}function x(e){if(e.type==="FunctionDeclaration"){const r=e.id;if(c(r)&&typeof r.name=="string")return r.name}}function w(e){if(e.type!=="FunctionDeclaration"&&e.type!=="FunctionExpression")return!1;const r=e.body;if(!c(r)||r.type!=="BlockStatement")return!1;const n=r.body;return Array.isArray(n)?n.some(s=>c(s)&&s.type==="ForOfStatement"&&s.await===!0):!1}function A(e){if(!y(e))return;const r=e.program;if(y(r)&&typeof r.type=="string")return e}export{k as checkControlFlowPolicy};
@@ -0,0 +1,24 @@
1
+ import type { CrossPlanConnectionResourcePlan } from "../../schemas/applicationSchemas.js";
2
+ import { type Result } from "../../types/Result.js";
3
+ import type { CodemodError, LinesChanged } from "../types.js";
4
+ export type CrossPlanConnectionAddOptions = Omit<CrossPlanConnectionResourcePlan, "name"> & {
5
+ name: string;
6
+ filePath?: string;
7
+ };
8
+ export interface CrossPlanConnectionRemoveOptions {
9
+ name: string;
10
+ filePath?: string;
11
+ force?: boolean;
12
+ }
13
+ export type CrossPlanConnectionModifyOptions = Partial<Omit<CrossPlanConnectionResourcePlan, "name">> & {
14
+ name: string;
15
+ filePath?: string;
16
+ };
17
+ export interface CrossPlanConnectionOrchestratorSuccess {
18
+ content: string;
19
+ linesChanged: LinesChanged;
20
+ warnings?: string[];
21
+ }
22
+ export declare function addCrossPlanConnection(content: string, options: CrossPlanConnectionAddOptions): Result<CrossPlanConnectionOrchestratorSuccess, CodemodError>;
23
+ export declare function removeCrossPlanConnection(content: string, options: CrossPlanConnectionRemoveOptions): Result<CrossPlanConnectionOrchestratorSuccess, CodemodError>;
24
+ export declare function modifyCrossPlanConnection(content: string, options: CrossPlanConnectionModifyOptions): Result<CrossPlanConnectionOrchestratorSuccess, CodemodError>;