@fjall/generator 13.1.0 → 14.1.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 (35) hide show
  1. package/dist/.build-source-hash +26 -23
  2. package/dist/.minified +1 -1
  3. package/dist/src/codemod/drift/detect.js +1 -1
  4. package/dist/src/codemod/drift/snapshot.d.ts +12 -0
  5. package/dist/src/codemod/drift/snapshot.js +1 -1
  6. package/dist/src/codemod/edits/addResource/propertyBuilder.d.ts +32 -2
  7. package/dist/src/codemod/edits/addResource/propertyBuilder.js +1 -1
  8. package/dist/src/codemod/edits/addResource.js +1 -1
  9. package/dist/src/codemod/edits/connectResourceToCompute.js +1 -1
  10. package/dist/src/codemod/edits/modifyResource.js +1 -1
  11. package/dist/src/codemod/edits/resourceStatementLocator.d.ts +82 -0
  12. package/dist/src/codemod/edits/resourceStatementLocator.js +1 -0
  13. package/dist/src/codemod/edits/siblingBindingResolution.d.ts +22 -0
  14. package/dist/src/codemod/edits/siblingBindingResolution.js +1 -0
  15. package/dist/src/codemod/emitProjection.d.ts +28 -7
  16. package/dist/src/codemod/emitProjection.js +1 -1
  17. package/dist/src/codemod/llmFallback/gateFallbackOutput.js +1 -1
  18. package/dist/src/codemod/registry.d.ts +57 -9
  19. package/dist/src/codemod/registry.js +1 -1
  20. package/dist/src/codemod/semanticIndex/locateByShape.d.ts +11 -4
  21. package/dist/src/codemod/semanticIndex/locateByShape.js +1 -1
  22. package/dist/src/codemod/validationGate/gates/projection.d.ts +2 -0
  23. package/dist/src/codemod/validationGate/gates/projection.js +1 -0
  24. package/dist/src/codemod/validationGate/index.d.ts +1 -0
  25. package/dist/src/codemod/validationGate/index.js +1 -1
  26. package/dist/src/codemod/validationGate/types.d.ts +1 -1
  27. package/dist/src/generation/generatePatternCode.js +15 -15
  28. package/dist/src/projection/index.d.ts +1 -1
  29. package/dist/src/projection/project.js +1 -1
  30. package/dist/src/projection/tables.d.ts +9 -8
  31. package/dist/src/projection/tables.js +1 -1
  32. package/dist/src/projection/types.d.ts +41 -2
  33. package/dist/src/version.d.ts +1 -1
  34. package/dist/src/version.js +1 -1
  35. package/package.json +2 -2
@@ -9,7 +9,7 @@
9
9
  "package.json",
10
10
  "tsconfig.json"
11
11
  ],
12
- "hash": "d4f636d033f9ea3464c9b0c22103cc3258ba91538121f10c976f081f74923af3",
12
+ "hash": "297a6c78a2367a9f5699e6a8403f8ecebdec5cae2296e6a76c477ea3f41823eb",
13
13
  "files": {
14
14
  "src/ast/astTestHelpers.ts": "026cac849eb88bdb",
15
15
  "src/ast/domain/astDomainParser.ts": "940ce5742507477d",
@@ -36,38 +36,40 @@
36
36
  "src/ast/parsers/resources/astStorageParser.ts": "309bc3257f46cfdf",
37
37
  "src/aws/regions.ts": "a028bcb92b0e41d9",
38
38
  "src/codemod/_internal.ts": "e865b7172fcc457b",
39
- "src/codemod/drift/detect.ts": "af3fb518a9d7165d",
39
+ "src/codemod/drift/detect.ts": "05f17c36c25a76ad",
40
40
  "src/codemod/drift/index.ts": "e87e1ced35eefa44",
41
41
  "src/codemod/drift/merge.ts": "29093929befa9abd",
42
- "src/codemod/drift/snapshot.ts": "d3c70df4f8537237",
42
+ "src/codemod/drift/snapshot.ts": "e389b8dbc41f6dc3",
43
43
  "src/codemod/drift/types.ts": "7f1d7899e420e4d7",
44
- "src/codemod/edits/addResource.ts": "dacb2e18657b74cf",
44
+ "src/codemod/edits/addResource.ts": "7d0fb772913f64c7",
45
45
  "src/codemod/edits/addResource/appBinding.ts": "3553fdb30fe06335",
46
46
  "src/codemod/edits/addResource/bodyIndex.ts": "3e179eac10d7bcf4",
47
- "src/codemod/edits/addResource/propertyBuilder.ts": "9102dff7e9fab376",
47
+ "src/codemod/edits/addResource/propertyBuilder.ts": "3152f13c4a0f600d",
48
48
  "src/codemod/edits/appendAccountToStage.ts": "1796d909fa3ef79d",
49
49
  "src/codemod/edits/computeServiceLocator.ts": "7f86483d9a35e053",
50
- "src/codemod/edits/connectResourceToCompute.ts": "7e90a6fbfb07c630",
50
+ "src/codemod/edits/connectResourceToCompute.ts": "ab704b6e75a7f173",
51
51
  "src/codemod/edits/controlFlowPolicy.ts": "e32bbe2894f76c65",
52
52
  "src/codemod/edits/crossPlanConnection.ts": "54a4ecb7b3368160",
53
53
  "src/codemod/edits/driftPolicy.ts": "5d9b7bb3505f987d",
54
54
  "src/codemod/edits/ensureImports.ts": "3ca0b6d446c4333c",
55
55
  "src/codemod/edits/findInsertionPosition.ts": "20cd34fdcf781017",
56
56
  "src/codemod/edits/index.ts": "bea5ed7c3983342b",
57
- "src/codemod/edits/modifyResource.ts": "2c47fdf60ad340e4",
57
+ "src/codemod/edits/modifyResource.ts": "a9882dc62dacb11e",
58
58
  "src/codemod/edits/modifyResource/literalConversion.ts": "31295cccd59131b7",
59
59
  "src/codemod/edits/moduleConstResolver.ts": "85a8597427849a80",
60
60
  "src/codemod/edits/objectGraph.ts": "5efc4181547237f8",
61
61
  "src/codemod/edits/removeResource.ts": "6af70ee482fabc06",
62
62
  "src/codemod/edits/removeResource/commentHeuristic.ts": "90b6ce7ba5aa2a48",
63
63
  "src/codemod/edits/removeResource/importPruning.ts": "f22783108b40f6cb",
64
+ "src/codemod/edits/resourceStatementLocator.ts": "e3094282860c764f",
64
65
  "src/codemod/edits/secretsProvability.ts": "d723283918e31efd",
65
66
  "src/codemod/edits/setContainerSecrets.ts": "24478c4205e79cbd",
67
+ "src/codemod/edits/siblingBindingResolution.ts": "7ed8938cfff4b38d",
66
68
  "src/codemod/edits/unmanagedShapeErrors.ts": "47a2644913c8e619",
67
69
  "src/codemod/edits/vpcPeer.ts": "0bcf90c2e6d16ca4",
68
70
  "src/codemod/edits/vpcPeerAccepter.ts": "c9eace933e9e2345",
69
- "src/codemod/emitGuard.ts": "d11e4297781ca81c",
70
- "src/codemod/emitProjection.ts": "9a47ef44995be65f",
71
+ "src/codemod/emitGuard.ts": "c04655a3cbfea38b",
72
+ "src/codemod/emitProjection.ts": "26909ef16e498ac2",
71
73
  "src/codemod/fileRewriter/builders.ts": "46a0d43240ff0818",
72
74
  "src/codemod/fileRewriter/index.ts": "fd715b41176a7999",
73
75
  "src/codemod/fileRewriter/locateByRange.ts": "889ee6256201c1fe",
@@ -80,7 +82,7 @@
80
82
  "src/codemod/llmFallback/claudeTier.ts": "ace3e3baa51383c9",
81
83
  "src/codemod/llmFallback/egressGate.ts": "1b9076a5c51508fc",
82
84
  "src/codemod/llmFallback/egressGate.types.ts": "e30947f12ee546ff",
83
- "src/codemod/llmFallback/gateFallbackOutput.ts": "d61dc46527c566b8",
85
+ "src/codemod/llmFallback/gateFallbackOutput.ts": "c630f3f0215ef077",
84
86
  "src/codemod/llmFallback/index.ts": "d32779fa95df6b94",
85
87
  "src/codemod/llmFallback/morphTier.ts": "adcc1cf280f9a65d",
86
88
  "src/codemod/llmFallback/prompt.ts": "60e8f4ab434bf9ec",
@@ -90,12 +92,12 @@
90
92
  "src/codemod/llmFallback/telemetryEvents.ts": "827282cb185eea6c",
91
93
  "src/codemod/llmFallback/tierRunner.ts": "a26094accd78cf15",
92
94
  "src/codemod/llmFallback/types.ts": "0b528ccbab7d48b8",
93
- "src/codemod/registry.ts": "fca9df965dd2d7ed",
95
+ "src/codemod/registry.ts": "4b55358c3db17cea",
94
96
  "src/codemod/semanticIndex/classifyControlFlow.ts": "ed96215d91a3cae3",
95
97
  "src/codemod/semanticIndex/findReferences.ts": "96aea37418d1e91a",
96
98
  "src/codemod/semanticIndex/index.ts": "cfcd212d34ab20fb",
97
99
  "src/codemod/semanticIndex/listImports.ts": "1e5fba6e34a17dab",
98
- "src/codemod/semanticIndex/locateByShape.ts": "1e00f068d69858ca",
100
+ "src/codemod/semanticIndex/locateByShape.ts": "017f2b271862e894",
99
101
  "src/codemod/semanticIndex/projectCache.ts": "dfd9b265c9d233c9",
100
102
  "src/codemod/semanticIndex/resolveConstructByLiteralProperty.ts": "2d013a330af15c5c",
101
103
  "src/codemod/telemetry/errorKinds.ts": "66c736abfa8b3a67",
@@ -104,9 +106,10 @@
104
106
  "src/codemod/validationGate/gates/drift.ts": "bd603a216fbb91a9",
105
107
  "src/codemod/validationGate/gates/locate.ts": "2c186c32edbb97ce",
106
108
  "src/codemod/validationGate/gates/parse.ts": "5433c5c8ffa35324",
109
+ "src/codemod/validationGate/gates/projection.ts": "a29d627ce75bc6c0",
107
110
  "src/codemod/validationGate/gates/schema.ts": "7ed5acaec0620a46",
108
- "src/codemod/validationGate/index.ts": "47b475a621f5038a",
109
- "src/codemod/validationGate/types.ts": "af466ee5f3433042",
111
+ "src/codemod/validationGate/index.ts": "bbe1f460330a11cf",
112
+ "src/codemod/validationGate/types.ts": "4a1cfa054e3383d5",
110
113
  "src/detection/index.ts": "b4ad9a935564cb3e",
111
114
  "src/dns/bindParser.ts": "376d274bf9e4bf9f",
112
115
  "src/dns/bindWriter.ts": "c296911ce5aef77c",
@@ -122,7 +125,7 @@
122
125
  "src/generation/compute/lambda.ts": "4f5e31080941b83a",
123
126
  "src/generation/compute/shared.ts": "3724d4bfe92cfcb7",
124
127
  "src/generation/database.ts": "dd4a1540986ad350",
125
- "src/generation/generatePatternCode.ts": "de16f42105a45358",
128
+ "src/generation/generatePatternCode.ts": "ab752292dd72b676",
126
129
  "src/generation/index.ts": "fd58a833e13bcf07",
127
130
  "src/generation/infrastructure.ts": "6381d502f4733926",
128
131
  "src/generation/messagingConnections.ts": "fef976a8261c6751",
@@ -143,10 +146,10 @@
143
146
  "src/presets/storagePresets.ts": "5df2d4d7d21a66d8",
144
147
  "src/presets/tierPresets.ts": "c0148771bffbef9b",
145
148
  "src/presets/tierTypes.ts": "6526373876daacf0",
146
- "src/projection/index.ts": "f46e0e482bb33565",
147
- "src/projection/project.ts": "2104026f10a27264",
148
- "src/projection/tables.ts": "00904da382c06cf5",
149
- "src/projection/types.ts": "da0845cb34bfd12b",
149
+ "src/projection/index.ts": "85d17974561fb3d9",
150
+ "src/projection/project.ts": "5613c875194dede9",
151
+ "src/projection/tables.ts": "21786d551ca8cfd0",
152
+ "src/projection/types.ts": "588ef43b70ab3127",
150
153
  "src/schemas/alarmSchemas.ts": "a6f6ac07b2a102de",
151
154
  "src/schemas/applicationSchemas.ts": "d85771a4607cf2da",
152
155
  "src/schemas/baseSchemas.ts": "93d9bd1c0adfcaa0",
@@ -171,11 +174,11 @@
171
174
  "src/validation/patterns.ts": "f9629eafdad7ac3e",
172
175
  "src/validation/validationMessages.ts": "c727ade4690274f4",
173
176
  "src/validation/validationPatterns.ts": "865f3d688975b7c8",
174
- "src/version.ts": "b094df24341f77d0",
177
+ "src/version.ts": "5cb1aaed65be54d1",
175
178
  "../scripts/minify-dist.mjs": "6b2e4b0df8aec601",
176
- "package.json": "d204679fed57f16a",
179
+ "package.json": "7e84c64bccf6c4e4",
177
180
  "tsconfig.json": "1929cf3f99fe9b1e"
178
181
  },
179
- "outputHash": "a5a910bcc151266985d3340b0df7c59fd5196c06e0fc72119b4df244a2f26b98",
180
- "outputFileCount": 323
182
+ "outputHash": "f239c914eb6571c0854f15d2296c3483d5876eaffe7f062932377379a2328a3e",
183
+ "outputFileCount": 329
181
184
  }
package/dist/.minified CHANGED
@@ -1 +1 @@
1
- 161 files minified at 2026-08-16T09:59:50.110Z
1
+ 164 files minified at 2026-08-17T10:22:05.564Z
@@ -1 +1 @@
1
- var y=Object.defineProperty;var c=(r,e)=>y(r,"name",{value:e,configurable:!0});import{failure as u,success as d}from"../../types/Result.js";import{listResources as h}from"../listResources.js";import{mergeProperties as b}from"./merge.js";import{snapshotProperties as k}from"./snapshot.js";function P(r,e,n){const t=h(r);if(!t.success)return u(t.error);if(t.data.resources.find(o=>o.type===e.type&&o.name===e.name)===void 0)return e.op==="add"?d({resource:{type:e.type,name:e.name},deltas:[],summary:"clean"}):u({kind:"ResourceNotFoundError",type:e.type,name:e.name,knownNames:t.data.resources.filter(o=>o.type===e.type).map(o=>o.name)});const s=k(r,e.type,e.name);if(!s.success)return u(s.error);const i={...s.data.properties},p={...e.properties},f=n===void 0?void 0:{...n.properties},m=b(f,i,p),l=g(m.deltas,f!==void 0);return d({resource:{type:e.type,name:e.name},deltas:m.deltas,summary:l})}c(P,"detectDrift");function g(r,e){if(r.length===0)return"clean";let n=!1,t=!1,a=!1,s=!1;for(const i of r)switch(i.verdict){case"conflict":n=!0;break;case"compatible":t=!0;break;case"clean":a=!0;break;case"no-op":s=!0;break}return n?"drifted-conflict":t?"drifted-compatible":s&&!a?"no-op":"clean"}c(g,"summariseDeltas");export{P as detectDrift};
1
+ var h=Object.defineProperty;var c=(t,e)=>h(t,"name",{value:e,configurable:!0});import{failure as u,success as d}from"../../types/Result.js";import{listResources as b}from"../listResources.js";import{locateNamesFor as k}from"../registry.js";import{mergeProperties as N}from"./merge.js";import{snapshotProperties as g}from"./snapshot.js";function x(t,e,o){const r=b(t);if(!r.success)return u(r.error);const a=k(e.type,e.name);if(r.data.resources.find(s=>s.type===e.type&&a.includes(s.name))===void 0)return e.op==="add"?d({resource:{type:e.type,name:e.name},deltas:[],summary:"clean"}):u({kind:"ResourceNotFoundError",type:e.type,name:e.name,knownNames:r.data.resources.filter(s=>s.type===e.type).map(s=>s.name)});const n=g(t,e.type,e.name);if(!n.success)return u(n.error);const p={...n.data.properties},l={...e.properties},f=o===void 0?void 0:{...o.properties},m=N(f,p,l),y=C(m.deltas,f!==void 0);return d({resource:{type:e.type,name:e.name},deltas:m.deltas,summary:y})}c(x,"detectDrift");function C(t,e){if(t.length===0)return"clean";let o=!1,r=!1,a=!1,i=!1;for(const n of t)switch(n.verdict){case"conflict":o=!0;break;case"compatible":r=!0;break;case"clean":a=!0;break;case"no-op":i=!0;break}return o?"drifted-conflict":r?"drifted-compatible":i&&!a?"no-op":"clean"}c(C,"summariseDeltas");export{x as detectDrift};
@@ -1,4 +1,16 @@
1
1
  import { type Result } from "../../types/Result.js";
2
+ import { type NodeShapeLike, type ObjectExpressionNodeLike } from "../edits/modifyResource/literalConversion.js";
2
3
  import type { CodemodError, ResourceName, StatementType } from "../types.js";
3
4
  import type { ResourceSnapshot } from "./types.js";
4
5
  export declare function snapshotProperties(content: string, type: StatementType, name: ResourceName): Result<ResourceSnapshot, CodemodError>;
6
+ /**
7
+ * Resolve the config `ObjectExpression` for a located statement by matching
8
+ * the recast/babel span of the factory call's name `StringLiteral` against
9
+ * the semantic-index anchor (`start`/`length` on the name token — ts-morph
10
+ * and babel offsets agree, the same contract `gates/classify.ts` relies on).
11
+ * Shared with the projection gate, which inspects the same object.
12
+ */
13
+ export declare function findConfigObject(body: NodeShapeLike[], target: {
14
+ start: number;
15
+ length: number;
16
+ }): ObjectExpressionNodeLike | undefined;
@@ -1 +1 @@
1
- var y=Object.defineProperty;var u=(e,r)=>y(e,"name",{value:r,configurable:!0});import{failure as d,success as h}from"../../types/Result.js";import{extractProgramBody as b}from"../_internal.js";import{astToLiteral as g,isNodeShape as l,NON_LITERAL as E,readKeyName as k}from"../edits/modifyResource/literalConversion.js";import{visit as x}from"../edits/objectGraph.js";import{parse as N}from"../fileRewriter/index.js";import{listResources as O}from"../listResources.js";import{STATEMENT_REGISTRY as S}from"../registry.js";function I(e,r,o){const n=O(e);if(!n.success)return d(n.error);const f=n.data.resources.find(t=>t.type===r&&t.name===o);if(f===void 0)return d({kind:"ResourceNotFoundError",type:r,name:o,knownNames:n.data.resources.filter(t=>t.type===r).map(t=>t.name)});const s=N(e,f.filePath);if(!s.success)return d(s.error);const i=b(s.data);if(i===void 0)return d({kind:"SemanticQueryError",reason:"recast File is missing program.body"});const c=j(i,f);if(c===void 0)return d({kind:"SemanticQueryError",reason:"Unable to resolve config ObjectExpression for the located resource."});const a=new Set(Object.keys(S[r].schemaFragment.shape)),m={};for(const t of c.properties){if(t.type!=="Property"&&t.type!=="ObjectProperty"||t.shorthand===!0||t.computed===!0)continue;const p=k(t.key);p===void 0||!a.has(p)||t.value!==void 0&&(m[p]=P(t.value,e))}return h({type:r,name:o,properties:m,schemaVersion:1})}u(I,"snapshotProperties");function j(e,r){const o=r.start+r.length;let n;const f=u(s=>{if(n!==void 0||s.type!=="CallExpression")return;const i=s.arguments;if(!Array.isArray(i)||i.length<2)return;const c=i[0];if(!l(c)||c.type!=="StringLiteral"||c.start!==r.start||c.end!==o)return;const a=i[1];l(a)&&a.type==="ObjectExpression"&&(n=a)},"visitor");for(const s of e)if(x(s,f),n!==void 0)break;return n}u(j,"findConfigObject");function P(e,r){const o=g(e);return o!==E?o:T(e,r)}u(P,"extractPropertyValue");function T(e,r){const o=typeof e.start=="number"?e.start:void 0,n=typeof e.end=="number"?e.end:void 0;return{kind:"expression",text:o!==void 0&&n!==void 0&&o<n?r.slice(o,n):""}}u(T,"toExpressionValue");export{I as snapshotProperties};
1
+ var h=Object.defineProperty;var u=(r,e)=>h(r,"name",{value:e,configurable:!0});import{failure as f,success as b}from"../../types/Result.js";import{extractProgramBody as g}from"../_internal.js";import{astToLiteral as E,isNodeShape as l,NON_LITERAL as x,readKeyName as k}from"../edits/modifyResource/literalConversion.js";import{visit as N}from"../edits/objectGraph.js";import{parse as O}from"../fileRewriter/index.js";import{listResources as S}from"../listResources.js";import{locateNamesFor as j,STATEMENT_REGISTRY as P}from"../registry.js";function Q(r,e,o){const n=S(r);if(!n.success)return f(n.error);const d=j(e,o),s=n.data.resources.find(t=>t.type===e&&d.includes(t.name));if(s===void 0)return f({kind:"ResourceNotFoundError",type:e,name:o,knownNames:n.data.resources.filter(t=>t.type===e).map(t=>t.name)});const i=O(r,s.filePath);if(!i.success)return f(i.error);const a=g(i.data);if(a===void 0)return f({kind:"SemanticQueryError",reason:"recast File is missing program.body"});const c=T(a,s);if(c===void 0)return f({kind:"SemanticQueryError",reason:"Unable to resolve config ObjectExpression for the located resource."});const y=new Set(Object.keys(P[e].schemaFragment.shape)),m={};for(const t of c.properties){if(t.type!=="Property"&&t.type!=="ObjectProperty"||t.shorthand===!0||t.computed===!0)continue;const p=k(t.key);p===void 0||!y.has(p)||t.value!==void 0&&(m[p]=v(t.value,r))}return b({type:e,name:o,properties:m,schemaVersion:1})}u(Q,"snapshotProperties");function T(r,e){const o=e.start+e.length;let n;const d=u(s=>{if(n!==void 0||s.type!=="CallExpression")return;const i=s.arguments;if(!Array.isArray(i)||i.length<2)return;const a=i[0];if(!l(a)||a.type!=="StringLiteral"||a.start!==e.start||a.end!==o)return;const c=i[1];l(c)&&c.type==="ObjectExpression"&&(n=c)},"visitor");for(const s of r)if(N(s,d),n!==void 0)break;return n}u(T,"findConfigObject");function v(r,e){const o=E(r);return o!==x?o:R(r,e)}u(v,"extractPropertyValue");function R(r,e){const o=typeof r.start=="number"?r.start:void 0,n=typeof r.end=="number"?r.end:void 0;return{kind:"expression",text:o!==void 0&&n!==void 0&&o<n?e.slice(o,n):""}}u(R,"toExpressionValue");export{T as findConfigObject,Q as snapshotProperties};
@@ -1,7 +1,7 @@
1
1
  import { type namedTypes as n } from "ast-types";
2
2
  import { type Result } from "../../../types/Result.js";
3
3
  import { type ObjectPropertyInput, type QuoteStyle } from "../../fileRewriter/index.js";
4
- import type { LiteralProp } from "../../emitProjection.js";
4
+ import type { EmittedProp } from "../../emitProjection.js";
5
5
  import type { InvalidPropertyError } from "../../types.js";
6
6
  /**
7
7
  * Build the emitted object literal from CONSTRUCT-vocabulary props.
@@ -9,6 +9,36 @@ import type { InvalidPropertyError } from "../../types.js";
9
9
  * The caller has already run the plan fragment through the projection, so
10
10
  * every key here is one a construct reads, in emission order.
11
11
  */
12
- export declare function buildPropertyInputs(properties: ReadonlyArray<LiteralProp>, quoteStyle: QuoteStyle): Result<ObjectPropertyInput[], InvalidPropertyError>;
12
+ export declare function buildPropertyInputs(properties: ReadonlyArray<EmittedProp>, quoteStyle: QuoteStyle): Result<ObjectPropertyInput[], InvalidPropertyError>;
13
+ /**
14
+ * Names that match {@link IDENTIFIER_SOURCE_PATTERN} lexically but can never
15
+ * be an identifier in the emitted file: ES reserved words, the strict-mode
16
+ * future-reserved words, the contextual keywords (`await`/`yield`/`let`/
17
+ * `static` — reserved only in some contexts, so failing closed is the only
18
+ * safe reading), and the strict-mode restricted binding names
19
+ * (`eval`/`arguments` — modules are always strict). This is reachable from a
20
+ * plain resource name: `ResourceNameSchema` accepts `"Import"`, and
21
+ * `toVariableName("Import")` derives `import` — splicing or `const`-declaring
22
+ * it writes a file that does not parse, with a green exit code.
23
+ *
24
+ * Shared with the locator's wrap-path derivation
25
+ * (`resourceStatementLocator.ts § planBinding`) — the two sites judge the
26
+ * same physical name and must refuse the same set.
27
+ */
28
+ export declare const RESERVED_IDENTIFIER_NAMES: ReadonlySet<string>;
29
+ /**
30
+ * Resolve one emitted construct prop to its AST value node.
31
+ *
32
+ * An `expression` prop carries the NAME of a live binding the projection
33
+ * resolved, so it is spliced as a bare identifier. General expression source
34
+ * (`app.getVpc()`, a member read) is a deliberate limitation, not an
35
+ * oversight: no rule produces one yet, and splicing source text this module
36
+ * cannot parse is how a mangled file gets written with a green exit code.
37
+ * Refused by name so a future rule that returns one surfaces as a property
38
+ * complaint here rather than a corrupt infrastructure.ts. Reserved words are
39
+ * refused for the same reason: they pass the lexical pattern but the file
40
+ * they land in does not parse.
41
+ */
42
+ export declare function buildEmittedValue(prop: EmittedProp, quoteStyle: QuoteStyle): Result<n.Property["value"], InvalidPropertyError>;
13
43
  export declare function toAstValue(value: unknown, quoteStyle: QuoteStyle): n.Property["value"] | undefined;
14
44
  export declare function makeStringLiteral(value: string, quote: QuoteStyle): n.StringLiteral;
@@ -1 +1 @@
1
- var c=Object.defineProperty;var f=(r,e)=>c(r,"name",{value:e,configurable:!0});import{builders as i}from"ast-types";import{failure as u,success as a}from"../../../types/Result.js";import{buildObjectProperty as d}from"../../fileRewriter/index.js";function x(r,e){const n=[];for(const{key:t,value:o}of r){const s=p(o,e);if(s===void 0)return u({kind:"InvalidPropertyError",property:t,reason:`Unsupported property value for "${t}" (accepts string/number/boolean/null/array/object of the same).`});n.push({key:t,value:s})}return a(n)}f(x,"buildPropertyInputs");function p(r,e){if(typeof r=="string")return l(r,e);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 n=[];for(const t of r){const o=p(t,e);if(o===void 0)return;n.push(o)}return i.arrayExpression(n)}if(typeof r=="object"&&r!==null){const n=[];for(const[t,o]of Object.entries(r)){const s=p(o,e);if(s===void 0)return;n.push(d(t,s,e))}return i.objectExpression(n)}}f(p,"toAstValue");function l(r,e){const n=e==="single"?"'":'"',t=e==="single"?r.replace(/\\/g,"\\\\").replace(/'/g,"\\'"):r.replace(/\\/g,"\\\\").replace(/"/g,'\\"'),o=`${n}${t}${n}`;return i.stringLiteral.from({value:r,extra:{rawValue:r,raw:o}})}f(l,"makeStringLiteral");export{x as buildPropertyInputs,l as makeStringLiteral,p as toAstValue};
1
+ var d=Object.defineProperty;var s=(e,t)=>d(e,"name",{value:t,configurable:!0});import{builders as o}from"ast-types";import{failure as a,success as c}from"../../../types/Result.js";import{buildObjectProperty as l}from"../../fileRewriter/index.js";function w(e,t){const r=[];for(const i of e){const n=m(i,t);if(!n.success)return a(n.error);r.push({key:i.key,value:n.data})}return c(r)}s(w,"buildPropertyInputs");const p=/^[A-Za-z_$][A-Za-z0-9_$]*$/,y=new Set(["await","break","case","catch","class","const","continue","debugger","default","delete","do","else","enum","export","extends","false","finally","for","function","if","import","in","instanceof","new","null","return","super","switch","this","throw","true","try","typeof","var","void","while","with","yield","implements","interface","package","private","protected","public","let","static","eval","arguments"]);function m(e,t){if(e.kind==="expression")return p.test(e.source)?y.has(e.source)?a({kind:"InvalidPropertyError",property:e.key,reason:`\`${e.key}\` resolved to \`${e.source}\`, which is a JavaScript reserved word \u2014 no binding can carry that name, so splicing it would write a file that does not parse.`}):c(o.identifier(e.source)):a({kind:"InvalidPropertyError",property:e.key,reason:`\`${e.key}\` resolved to the expression \`${e.source}\`, which is not a bare identifier; this emitter only splices the name of a live binding.`});const r=u(e.value,t);return r===void 0?a({kind:"InvalidPropertyError",property:e.key,reason:`Unsupported property value for "${e.key}" (accepts string/number/boolean/null/array/object of the same).`}):c(r)}s(m,"buildEmittedValue");function u(e,t){if(typeof e=="string")return b(e,t);if(typeof e=="number"&&Number.isFinite(e))return o.numericLiteral(e);if(typeof e=="boolean")return o.booleanLiteral(e);if(e===null)return o.nullLiteral();if(Array.isArray(e)){const r=[];for(const i of e){const n=u(i,t);if(n===void 0)return;r.push(n)}return o.arrayExpression(r)}if(typeof e=="object"&&e!==null){const r=[];for(const[i,n]of Object.entries(e)){const f=u(n,t);if(f===void 0)return;r.push(l(i,f,t))}return o.objectExpression(r)}}s(u,"toAstValue");function b(e,t){const r=t==="single"?"'":'"',i=t==="single"?e.replace(/\\/g,"\\\\").replace(/'/g,"\\'"):e.replace(/\\/g,"\\\\").replace(/"/g,'\\"'),n=`${r}${i}${r}`;return o.stringLiteral.from({value:e,extra:{rawValue:e,raw:n}})}s(b,"makeStringLiteral");export{y as RESERVED_IDENTIFIER_NAMES,m as buildEmittedValue,w as buildPropertyInputs,b as makeStringLiteral,u as toAstValue};
@@ -1 +1 @@
1
- var M=Object.defineProperty;var u=(r,e)=>M(r,"name",{value:e,configurable:!0});import{failure as t,success as k}from"../../types/Result.js";import{DEFAULT_FILE_PATH as C,computeLinesDelta as v,extractProgramBody as x,isRecord as w}from"../_internal.js";import{evaluateEmitGuard as G}from"../emitGuard.js";import{projectEmittedProperties as N}from"../emitProjection.js";import{appendSpecifier as Q,buildAppMethodWrappedStatement as B,buildFactoryStatement as O,buildImportDeclaration as U,detectQuoteStyle as _,parse as W,printFile as j}from"../fileRewriter/index.js";import{STATEMENT_REGISTRY as Y}from"../registry.js";import{locateAllShapes as q,locateByShape as H}from"../semanticIndex/index.js";import{checkControlFlowPolicy as $}from"./controlFlowPolicy.js";import{driftGate as z,runPipeline as J,schemaGate as K}from"../validationGate/index.js";import{resolveAppBindingName as V}from"./addResource/appBinding.js";import{buildUnmanagedDuplicateError as X}from"./unmanagedShapeErrors.js";import{buildResourceLocations as Z,indexBody as ee,orderByEndPos as re,resolveInsertIndex as te}from"./addResource/bodyIndex.js";import{buildPropertyInputs as ae}from"./addResource/propertyBuilder.js";import{findInsertionPosition as ne}from"./findInsertionPosition.js";const L="@fjall/components-infrastructure";function Pe(r,e,m={}){const s=e.filePath??C,n=W(r,s);if(!n.success)return t(n.error);const a=H(r,{type:e.type,name:e.name},s);if(!a.success)return a.error.kind==="TemplateLiteralNameError"?t(a.error):t({kind:"SemanticQueryError",reason:a.error.reason,cause:a.error.cause});if(a.data!==void 0){if(a.data.managed===!1)return t(X(e.type,a.data.symbolName));const d=$({ast:n.data,target:a.data,resource:{type:e.type,name:e.name},op:"add"});if(d.refusal!==void 0)return t(d.refusal);const h=J([z],{content:r,plan:{type:e.type,name:e.name,properties:e.properties,op:"add"},baseline:m.baseline,policy:e.driftPolicy});return h.success?h.data.action==="skip"?k({content:r,linesChanged:{added:0,removed:0},skipped:!0}):t({kind:"DuplicateResourceError",type:e.type,name:e.name}):t(R(h.error))}const f=_(n.data),l=N(e.type,e.properties);if("refusal"in l)return t(l.refusal);const y=ae(l.props,f);if(!y.success)return t(y.error);const c=x(n.data);if(c===void 0)return t({kind:"SemanticQueryError",reason:"recast File is missing program.body"});const o=Y[e.type],I=G(o.emitGuard,e.properties);if(I!==void 0)return t(I);const S=K.run({content:r,plan:{type:e.type,name:e.name,properties:e.properties,op:"add"}});if(!S.success)return t(R(S.error));const g={factory:o.factoryIdentifier,name:e.name,properties:y.data,quoteStyle:f};let E;if(o.emitWrapper!==void 0){const d=V(c);if(d===void 0)return t({kind:"MissingAppInitError",type:e.type,appMethod:o.emitWrapper.appMethod});E=B({...g,appIdentifier:d,appMethod:o.emitWrapper.appMethod})}else E=O(g);const p=ee(c,r),i=q(r,s);if(!i.success)return i.error.kind==="TemplateLiteralNameError"?t(i.error):t({kind:"SemanticQueryError",reason:i.error.reason,cause:i.error.cause});const b=Z(p.anchors,i.data),T=re([...p.appInitLocations,...b]),A=ne(p.importInfos,T,e.type,p.programEnd),D=te(p.anchors,A);c.splice(D,0,E),oe(c,o.factoryIdentifier,f);const P=j(n.data,r),F=v(r,P);return k({content:P,linesChanged:F})}u(Pe,"addResource");function R(r){switch(r.kind){case"ParseError":case"TemplateLiteralNameError":case"DuplicateResourceError":case"InvalidPropertyError":case"SemanticQueryError":case"MissingAppInitError":case"DriftConflictError":case"ControlFlowClassifierError":return r;default:return{kind:"SemanticQueryError",reason:`Unexpected codemod error in validation-gate pipeline: ${r.kind}`}}}u(R,"narrowGateError");function oe(r,e,m){for(const n of r){if(n.type!=="ImportDeclaration")continue;const a=n.source;if(!(!w(a)||a.type!=="StringLiteral")&&a.value===L){Q(n,e);return}}const s=U([e],L,m);r.unshift(s)}u(oe,"ensureFactoryImport");export{Pe as addResource};
1
+ var M=Object.defineProperty;var u=(r,e)=>M(r,"name",{value:e,configurable:!0});import{failure as t,success as R}from"../../types/Result.js";import{DEFAULT_FILE_PATH as C,computeLinesDelta as w,extractProgramBody as b,isRecord as x}from"../_internal.js";import{evaluateEmitGuard as N}from"../emitGuard.js";import{projectEmittedProperties as G}from"../emitProjection.js";import{appendSpecifier as Q,buildAppMethodWrappedStatement as O,buildFactoryStatement as U,buildImportDeclaration as W,detectQuoteStyle as _,parse as j,printFile as Y}from"../fileRewriter/index.js";import{STATEMENT_REGISTRY as q,constructIdFor as H}from"../registry.js";import{locateAllShapes as $,locateByShape as z}from"../semanticIndex/index.js";import{checkControlFlowPolicy as J}from"./controlFlowPolicy.js";import{driftGate as K,runPipeline as V,schemaGate as X}from"../validationGate/index.js";import{resolveAppBindingName as Z}from"./addResource/appBinding.js";import{buildUnmanagedDuplicateError as ee}from"./unmanagedShapeErrors.js";import{buildResourceLocations as re,indexBody as te,orderByEndPos as ae,resolveInsertIndex as ne}from"./addResource/bodyIndex.js";import{buildPropertyInputs as ie}from"./addResource/propertyBuilder.js";import{findInsertionPosition as oe}from"./findInsertionPosition.js";import{applyBinding as se}from"./resourceStatementLocator.js";import{createSiblingBindingResolution as ce}from"./siblingBindingResolution.js";const L="@fjall/components-infrastructure";function Fe(r,e,m={}){const p=e.filePath??C,n=j(r,p);if(!n.success)return t(n.error);const a=z(r,{type:e.type,name:e.name},p);if(!a.success)return a.error.kind==="TemplateLiteralNameError"?t(a.error):t({kind:"SemanticQueryError",reason:a.error.reason,cause:a.error.cause});if(a.data!==void 0){if(a.data.managed===!1)return t(ee(e.type,a.data.symbolName));const i=J({ast:n.data,target:a.data,resource:{type:e.type,name:e.name},op:"add"});if(i.refusal!==void 0)return t(i.refusal);const g=V([K],{content:r,plan:{type:e.type,name:e.name,properties:e.properties,op:"add"},baseline:m.baseline,policy:e.driftPolicy});return g.success?g.data.action==="skip"?R({content:r,linesChanged:{added:0,removed:0},skipped:!0}):t({kind:"DuplicateResourceError",type:e.type,name:e.name}):t(B(g.error))}const l=_(n.data),h=ce(e.type,()=>b(n.data)),f=G(e.type,e.properties,{resourceName:e.name,resolveBinding:h.resolveBinding});if("refusal"in f)return t(f.refusal);const y=ie(f.props,l);if(!y.success)return t(y.error);const o=b(n.data);if(o===void 0)return t({kind:"SemanticQueryError",reason:"recast File is missing program.body"});const s=q[e.type],I=N(s.emitGuard,e.properties);if(I!==void 0)return t(I);const S=X.run({content:r,plan:{type:e.type,name:e.name,properties:e.properties,op:"add"}});if(!S.success)return t(B(S.error));const P={factory:s.factoryIdentifier,name:H(e.type,e.name,e.properties),properties:y.data,quoteStyle:l};let E;if(s.emitWrapper!==void 0){const i=Z(o);if(i===void 0)return t({kind:"MissingAppInitError",type:e.type,appMethod:s.emitWrapper.appMethod});E=O({...P,appIdentifier:i,appMethod:s.emitWrapper.appMethod})}else E=U(P);const d=te(o,r),c=$(r,p);if(!c.success)return c.error.kind==="TemplateLiteralNameError"?t(c.error):t({kind:"SemanticQueryError",reason:c.error.reason,cause:c.error.cause});const F=re(d.anchors,c.data),T=ae([...d.appInitLocations,...F]),v=oe(d.importInfos,T,e.type,d.programEnd),A=ne(d.anchors,v);for(const i of h.takePendingWraps())se(o,i);o.splice(A,0,E),pe(o,s.factoryIdentifier,l);const k=Y(n.data,r),D=w(r,k);return R({content:k,linesChanged:D})}u(Fe,"addResource");function B(r){switch(r.kind){case"ParseError":case"TemplateLiteralNameError":case"DuplicateResourceError":case"InvalidPropertyError":case"SemanticQueryError":case"MissingAppInitError":case"DriftConflictError":case"ControlFlowClassifierError":return r;default:return{kind:"SemanticQueryError",reason:`Unexpected codemod error in validation-gate pipeline: ${r.kind}`}}}u(B,"narrowGateError");function pe(r,e,m){for(const n of r){if(n.type!=="ImportDeclaration")continue;const a=n.source;if(!(!x(a)||a.type!=="StringLiteral")&&a.value===L){Q(n,e);return}}const p=W([e],L,m);r.unshift(p)}u(pe,"ensureFactoryImport");export{Fe as addResource};
@@ -1 +1 @@
1
- var H=Object.defineProperty;var u=(t,e)=>H(t,"name",{value:e,configurable:!0});import{builders as E}from"ast-types";import{CREDENTIAL_KEYS as U,DATABASE_ACCESSORS as C,DATABASE_ENV_VARS as b,DATABASE_SSL_VALUE as z}from"../../generation/database.js";import{MESSAGING_ENV_VARS as Y}from"../../generation/messagingConnections.js";import{STORAGE_ACCESSORS as Q,STORAGE_ENV_VARS as O}from"../../generation/storageConnections.js";import{connectionEnvSuffix as J,connectionKeyOrdinal as T,toVariableName as X}from"../../generation/common.js";import{failure as c,success as S}from"../../types/Result.js";import{DEFAULT_FILE_PATH as Z,computeLinesDelta as q,extractProgramBody as ee}from"../_internal.js";import{buildObjectProperty as D,detectQuoteStyle as ne,parse as te,printFile as re}from"../fileRewriter/index.js";import{FACTORY_IDENTIFIERS as P,STATEMENT_REGISTRY as oe}from"../registry.js";import{makeStringLiteral as _}from"./addResource/propertyBuilder.js";import{findLastPlainPropertyIndex as j,isArrayExpression as ie,locateContainer as ae,locateServiceByName as se}from"./computeServiceLocator.js";import{isNodeShape as f,readKeyName as M}from"./modifyResource/literalConversion.js";import{unwrapTsWrappers as I}from"./moduleConstResolver.js";import{objectAbsenceIsProvable as $,pushPropertyPreservingComma as ce,visit as L}from"./objectGraph.js";const v=Object.freeze({database:P.database,storage:P.storage});function x(t){const e=oe[t].emitWrapper?.appMethod;if(e===void 0)throw new Error(`No app method registered for statement type "${t}"`);return e}u(x,"requireAppMethod");const A=Object.freeze({database:x("database"),storage:x("storage")}),F=new Map([[A.database,0],[A.storage,1],[x("messaging"),2]]);function Me(t,e){const n=e.filePath??Z,{resourceType:o,resourceName:r}=e,i=[...new Set(e.serviceNames)];if(i.length===0)return c({kind:"UnsupportedConnectionShapeError",reason:"No target services were supplied."});const a=te(t,n);if(!a.success)return c(a.error);const s=ee(a.data);if(s===void 0)return c({kind:"UnsupportedConnectionShapeError",reason:"recast File is missing program.body"});const d=de(s,o,r);if(!d.success)return c(d.error);const{variableName:p}=d.data,l=[],y=[];for(const h of i){const k=fe(s,h,p,o);if(!k.success)return c(k.error);if(k.data==="already-connected"){l.push(h);continue}y.push(k.data)}const m=l.map(h=>`Service "${h}" already lists "${p}" in its \`connections\` array \u2014 left untouched.`);for(const h of y)h.container===void 0&&m.push(`Service "${h.serviceName}" has no \`containers\` array \u2014 wired \`connections\` only, without env/secret entries (the generator lane emits the same shape for container-less services).`);if(y.length===0)return S({content:t,linesChanged:{added:0,removed:0},variableName:p,wiredServices:[],skipped:!0,warnings:m});const g=ne(a.data),G=Se(s);pe(s,d.data);for(const h of y)he(h,p,o,g,G);const w=re(a.data,t);return S({content:w,linesChanged:q(t,w),variableName:p,wiredServices:y.map(h=>h.serviceName),skipped:!1,warnings:m})}u(Me,"connectResourceToCompute");function de(t,e,n){const o=v[e],r=[];for(let p=0;p<t.length;p++){const l=t[p];if(l===void 0)continue;let y=!1;L(l,m=>{m.type==="CallExpression"&&R(m,o,n)&&(y=!0)}),y&&r.push({statementIndex:p})}if(r.length===0)return c({kind:"ResourceStatementNotFoundError",resourceType:e,resourceName:n});if(r.length>1)return c({kind:"AmbiguousResourceStatementError",resourceType:e,resourceName:n,count:r.length});const i=r[0];if(i===void 0)return c({kind:"ResourceStatementNotFoundError",resourceType:e,resourceName:n});const a=t[i.statementIndex];if(a===void 0)return c({kind:"ResourceStatementNotFoundError",resourceType:e,resourceName:n});if(a.type==="VariableDeclaration"){const p=a.declarations;if(!Array.isArray(p)||p.length!==1)return c({kind:"UnsupportedConnectionShapeError",reason:`The statement declaring "${n}" carries multiple declarators \u2014 the binding cannot be identified.`});const l=p[0].id;if(!f(l)||l.type!=="Identifier")return c({kind:"UnsupportedConnectionShapeError",reason:`The statement declaring "${n}" binds a pattern, not an identifier.`});const y=l.name;if(typeof y!="string")return c({kind:"UnsupportedConnectionShapeError",reason:`The binding for "${n}" has no readable name.`});const m=p[0].init;return!f(m)||!B(m,e,n)?c({kind:"UnsupportedConnectionShapeError",reason:`The binding for "${n}" is not a direct \`app.${A[e]}(${v[e]}.build(...))\` call \u2014 the bound value is not the construct \`connections\` needs.`}):S({variableName:y})}if(a.type!=="ExpressionStatement")return c({kind:"UnsupportedConnectionShapeError",reason:`The statement containing "${n}" is a ${a.type}, which cannot be bound to a const.`});const s=a.expression;if(!f(s)||s.type!=="CallExpression")return c({kind:"UnsupportedConnectionShapeError",reason:`The statement containing "${n}" is not an app-method call \u2014 there is no construct value to bind.`});if(R(s,v[e],n))return c({kind:"UnsupportedConnectionShapeError",reason:`"${n}" is a bare factory call not wrapped in an app method \u2014 there is no construct value to bind.`});if(!B(s,e,n))return c({kind:"UnsupportedConnectionShapeError",reason:`The statement containing "${n}" is not a direct \`app.${A[e]}(...)\` call \u2014 binding it would wire a value \`connections\` cannot use.`});const d=X(n);return ue(t,d)?c({kind:"VariableNameConflictError",variableName:d}):S({variableName:d,wrap:{statementIndex:i.statementIndex,expression:s}})}u(de,"planBinding");function pe(t,e){if(e.wrap===void 0)return;const{statementIndex:n,expression:o}=e.wrap,r=t[n],i=E.variableDeclaration("const",[E.variableDeclarator(E.identifier(e.variableName),o)]),a=r?.comments;a!==void 0&&(i.comments=a),t[n]=i}u(pe,"applyBinding");function R(t,e,n){const o=t.callee;if(!f(o)||o.type!=="MemberExpression"||o.computed===!0)return!1;const r=o.object,i=o.property;if(!f(r)||r.type!=="Identifier"||r.name!==e||!f(i)||i.type!=="Identifier"||i.name!=="build")return!1;const a=t.arguments;if(!Array.isArray(a))return!1;const s=a[0];return!f(s)||s.type!=="StringLiteral"&&s.type!=="Literal"?!1:s.value===n}u(R,"isFactoryBuildOf");function N(t){if(t.type!=="ExportNamedDeclaration"&&t.type!=="ExportDefaultDeclaration")return t;const e=t.declaration;return f(e)?e:void 0}u(N,"unwrapExportDeclaration");function V(t){const e=I(t);if(e.type!=="CallExpression")return;const n=e.callee;if(!f(n)||n.type!=="MemberExpression"||n.computed===!0)return;const o=n.property;if(!f(o)||o.type!=="Identifier")return;const r=o.name;return typeof r=="string"?r:void 0}u(V,"appMethodNameOf");function B(t,e,n){const o=I(t);if(V(o)!==A[e])return!1;const r=o.arguments;if(!Array.isArray(r))return!1;const i=r[0];return f(i)?R(I(i),v[e],n):!1}u(B,"isAppMethodWrap");function ue(t,e){for(const n of t){const o=N(n);if(o!==void 0){if(o.type==="ImportDeclaration"){const r=o.specifiers;if(!Array.isArray(r))continue;for(const i of r){const a=i.local;if(f(a)&&a.name===e)return!0}continue}if(o.type==="VariableDeclaration"){const r=o.declarations;if(!Array.isArray(r))continue;for(const i of r){if(!f(i))continue;const a=i.id;if(!f(a))continue;let s=!1;if(L(a,d=>{d.type==="Identifier"&&d.name===e&&(s=!0)}),s)return!0}continue}if(o.type==="FunctionDeclaration"||o.type==="ClassDeclaration"){const r=o.id;if(f(r)&&r.name===e)return!0}}}return!1}u(ue,"moduleScopeDeclares");function fe(t,e,n,o){const r=se(t,e);if(!r.success){const m=r.error;return m.kind==="AmbiguousService"?c({kind:"UnsupportedConnectionShapeError",serviceName:e,reason:`${String(m.count)} services share the name "${e}" across ComputeFactory.build calls \u2014 the edit cannot be proven to target the right one.`}):c({kind:"ConnectServiceNotFoundError",serviceName:e,knownServices:m.knownServices})}const i=r.data,a=ae(i,0);let s;if(a.success)s=a.data;else switch(a.error.kind){case"ContainerNotObjectLiteral":return c({kind:"UnsupportedConnectionShapeError",serviceName:e,reason:"The first container is not an object literal."});case"ContainersNotStaticArray":return c({kind:"UnsupportedConnectionShapeError",serviceName:e,reason:"`containers` is not a static array literal, so the container to wire cannot be located."});case"NoContainersArray":if(!$(i,"containers"))return c({kind:"UnsupportedConnectionShapeError",serviceName:e,reason:"Service carries a spread, shorthand or computed key that could supply `containers` invisibly, so an absent property cannot be proven."});break;case"ContainerIndexOutOfRange":break}const d=le(i,e,n);if(!d.success)return c(d.error);if(d.data.alreadyConnected)return S("already-connected");let p,l,y="";if(s!==void 0){const m=W(s,"environment",e);if(!m.success)return c(m.error);const g=W(s,"secretsImport",e);if(!g.success)return c(g.error);p=m.data,l=g.data,y=me(o,[...m.data.keys,...g.data.keys])}return S({serviceName:e,service:i,container:s,environment:p,secretsImport:l,suffix:y,connectionsArray:d.data.array})}u(fe,"planService");function le(t,e,n){const o=j(t,"connections");if(o===-1)return $(t,"connections")?S({array:void 0,alreadyConnected:!1}):c({kind:"UnsupportedConnectionShapeError",serviceName:e,reason:"Service carries a spread, shorthand or computed key that could supply `connections` invisibly, so an absent property cannot be proven."});const r=t.properties[o]?.value;if(!ie(r))return c({kind:"UnsupportedConnectionShapeError",serviceName:e,reason:"`connections` is not an array literal."});for(const i of r.elements)if(f(i)){if(i.type==="Identifier"){if(i.name===n)return S({array:r,alreadyConnected:!0});continue}return c({kind:"UnsupportedConnectionShapeError",serviceName:e,reason:`\`connections\` contains a ${i.type}, so "${n}" cannot be proven absent from it.`})}return S({array:r,alreadyConnected:!1})}u(le,"inspectConnections");function W(t,e,n){const o=j(t,e);if(o===-1)return $(t,e)?S({obj:void 0,keys:[]}):c({kind:"UnsupportedConnectionShapeError",serviceName:n,reason:`The first container carries a spread, shorthand or computed key that could supply \`${e}\` invisibly, so an absent property cannot be proven.`});const r=t.properties[o]?.value;if(!f(r)||r.type!=="ObjectExpression")return c({kind:"UnsupportedConnectionShapeError",serviceName:n,reason:`\`${e}\` is not an object literal.`});const i=r,a=[];for(const s of i.properties){if(s.type==="SpreadElement"||s.type==="SpreadProperty")return c({kind:"UnsupportedConnectionShapeError",serviceName:n,reason:`\`${e}\` contains a spread, so existing connection keys cannot be enumerated to derive the next suffix.`});if(s.type!=="Property"&&s.type!=="ObjectProperty")continue;if(s.computed===!0)return c({kind:"UnsupportedConnectionShapeError",serviceName:n,reason:`\`${e}\` contains a computed key.`});const d=M(s.key);d!==void 0&&a.push(d)}return S({obj:i,keys:a})}u(W,"collectWiringTarget");function me(t,e){const n=Object.values(t==="database"?b:O),o=new Set;for(const r of e)for(const i of n){const a=T(r,i);a!==void 0&&o.add(a)}return J(o.size===0?0:Math.max(...o))}u(me,"nextConnectionSuffix");function ye(t,e,n,o){const r=u((s,d,p=[])=>E.callExpression(E.memberExpression(s,E.identifier(d),!1),p),"call"),i=u(()=>E.identifier(e),"variable");if(t==="storage")return[{key:`${O.NAME}${n}`,value:r(i(),Q.BUCKET_NAME),target:"environment"}];const a=u(s=>r(r(i(),C.CREDENTIALS),C.IMPORT,[_(s,o)]),"credential");return[{key:`${b.HOST}${n}`,value:r(i(),C.HOST),target:"environment"},{key:`${b.PORT}${n}`,value:r(i(),C.PORT),target:"environment"},{key:`${b.NAME}${n}`,value:r(i(),C.NAME),target:"environment"},{key:`${b.SSL}${n}`,value:_(z,o),target:"environment"},{key:`${b.USERNAME}${n}`,value:a(U.USERNAME),target:"secretsImport"},{key:`${b.PASSWORD}${n}`,value:a(U.PASSWORD),target:"secretsImport"}]}u(ye,"buildWiringEntries");function he(t,e,n,o,r){if(t.connectionsArray!==void 0){const d=Ee(t.connectionsArray,n,r);t.connectionsArray.elements.splice(d,0,E.identifier(e))}else K(t.service,"connections",E.arrayExpression([E.identifier(e)]),o);const i=t.container;if(i===void 0)return;const a=ye(n,e,t.suffix,o),s=[{key:"environment",state:t.environment},{key:"secretsImport",state:t.secretsImport}];for(const{key:d,state:p}of s){const l=a.filter(y=>y.target===d);l.length!==0&&Ae(i,d,p,l,o)}}u(he,"applyServiceWiring");function Ee(t,e,n){const o=F.get(A[e])??0;for(let r=0;r<t.elements.length;r++){const i=t.elements[r];if(!f(i)||i.type!=="Identifier")continue;const a=i.name;if(typeof a!="string")continue;const s=n.get(a);if(s!==void 0&&s>o)return r}return t.elements.length}u(Ee,"connectionInsertIndex");function Se(t){const e=new Map;for(const n of t){const o=N(n);if(o===void 0||o.type!=="VariableDeclaration")continue;const r=o.declarations;if(Array.isArray(r))for(const i of r){if(!f(i))continue;const a=i.id,s=i.init;if(!f(a)||a.type!=="Identifier"||!f(s))continue;const d=a.name;if(typeof d!="string")continue;const p=V(s);if(p===void 0)continue;const l=F.get(p);l!==void 0&&e.set(d,l)}}return e}u(Se,"collectConnectionRanks");const be=Object.freeze([...Object.values(b),...Object.values(O),...Object.values(Y)]);function ge(t){let e=-1;for(let n=0;n<t.properties.length;n++){const o=t.properties[n];if(o===void 0||o.type!=="Property"&&o.type!=="ObjectProperty"||o.computed===!0)continue;const r=M(o.key);if(r===void 0)continue;be.some(a=>T(r,a)!==void 0)&&(e=n)}return e===-1?t.properties.length:e+1}u(ge,"connectionEntryInsertIndex");function Ae(t,e,n,o,r){const i=o.map(d=>D(d.key,d.value,r)),a=n?.obj;if(a===void 0){K(t,e,E.objectExpression(i),r);return}const s=ge(a);a.properties.splice(s,0,...i)}u(Ae,"insertObjectEntries");function K(t,e,n,o){const r=D(e,n,o);ce(t,r)}u(K,"appendProperty");export{Me as connectResourceToCompute};
1
+ var B=Object.defineProperty;var p=(n,e)=>B(n,"name",{value:e,configurable:!0});import{builders as y}from"ast-types";import{CREDENTIAL_KEYS as T,DATABASE_ACCESSORS as v,DATABASE_ENV_VARS as E,DATABASE_SSL_VALUE as V}from"../../generation/database.js";import{MESSAGING_ENV_VARS as F}from"../../generation/messagingConnections.js";import{STORAGE_ACCESSORS as K,STORAGE_ENV_VARS as A}from"../../generation/storageConnections.js";import{connectionEnvSuffix as W,connectionKeyOrdinal as w}from"../../generation/common.js";import{failure as d,success as S}from"../../types/Result.js";import{DEFAULT_FILE_PATH as G,computeLinesDelta as H,extractProgramBody as z}from"../_internal.js";import{buildObjectProperty as U,detectQuoteStyle as Y,parse as J,printFile as Q}from"../fileRewriter/index.js";import{FACTORY_IDENTIFIERS as I,STATEMENT_REGISTRY as X}from"../registry.js";import{makeStringLiteral as P}from"./addResource/propertyBuilder.js";import{findLastPlainPropertyIndex as _,isArrayExpression as Z,locateContainer as q,locateServiceByName as ee}from"./computeServiceLocator.js";import{isNodeShape as g,readKeyName as x}from"./modifyResource/literalConversion.js";import{objectAbsenceIsProvable as $,pushPropertyPreservingComma as ne}from"./objectGraph.js";import{applyBinding as te,appMethodNameOf as re,planBinding as oe,unwrapExportDeclaration as ie}from"./resourceStatementLocator.js";const j=Object.freeze({database:I.database,storage:I.storage});function O(n){const e=X[n].emitWrapper?.appMethod;if(e===void 0)throw new Error(`No app method registered for statement type "${n}"`);return e}p(O,"requireAppMethod");const k=Object.freeze({database:O("database"),storage:O("storage")}),M=new Map([[k.database,0],[k.storage,1],[O("messaging"),2]]);function Pe(n,e){const t=e.filePath??G,{resourceType:o,resourceName:r}=e,i=[...new Set(e.serviceNames)];if(i.length===0)return d({kind:"UnsupportedConnectionShapeError",reason:"No target services were supplied."});const a=J(n,t);if(!a.success)return d(a.error);const s=z(a.data);if(s===void 0)return d({kind:"UnsupportedConnectionShapeError",reason:"recast File is missing program.body"});const c=oe(s,{factory:j[o],appMethod:k[o]},r);if(!c.success)return d(ae(c.error,o,r));const{variableName:u}=c.data,l=[],h=[];for(const m of i){const C=se(s,m,u,o);if(!C.success)return d(C.error);if(C.data==="already-connected"){l.push(m);continue}h.push(C.data)}const f=l.map(m=>`Service "${m}" already lists "${u}" in its \`connections\` array \u2014 left untouched.`);for(const m of h)m.container===void 0&&f.push(`Service "${m.serviceName}" has no \`containers\` array \u2014 wired \`connections\` only, without env/secret entries (the generator lane emits the same shape for container-less services).`);if(h.length===0)return S({content:n,linesChanged:{added:0,removed:0},variableName:u,wiredServices:[],skipped:!0,warnings:f});const b=Y(a.data),L=le(s);te(s,c.data);for(const m of h)ue(m,u,o,b,L);const R=Q(a.data,n);return S({content:R,linesChanged:H(n,R),variableName:u,wiredServices:h.map(m=>m.serviceName),skipped:!1,warnings:f})}p(Pe,"connectResourceToCompute");function ae(n,e,t){switch(n.kind){case"statement-not-found":return{kind:"ResourceStatementNotFoundError",resourceType:e,resourceName:t};case"ambiguous":return{kind:"AmbiguousResourceStatementError",resourceType:e,resourceName:t,count:n.count};case"multiple-declarators":return{kind:"UnsupportedConnectionShapeError",reason:`The statement declaring "${t}" carries multiple declarators \u2014 the binding cannot be identified.`};case"pattern-binding":return{kind:"UnsupportedConnectionShapeError",reason:`The statement declaring "${t}" binds a pattern, not an identifier.`};case"unreadable-name":return{kind:"UnsupportedConnectionShapeError",reason:`The binding for "${t}" has no readable name.`};case"init-not-app-method-wrap":return{kind:"UnsupportedConnectionShapeError",reason:`The binding for "${t}" is not a direct \`app.${k[e]}(${j[e]}.build(...))\` call \u2014 the bound value is not the construct \`connections\` needs.`};case"unbindable-statement":return{kind:"UnsupportedConnectionShapeError",reason:`The statement containing "${t}" is a ${n.statementType}, which cannot be bound to a const.`};case"not-app-method-call":return{kind:"UnsupportedConnectionShapeError",reason:`The statement containing "${t}" is not an app-method call \u2014 there is no construct value to bind.`};case"bare-factory-call":return{kind:"UnsupportedConnectionShapeError",reason:`"${t}" is a bare factory call not wrapped in an app method \u2014 there is no construct value to bind.`};case"foreign-wrapper":return{kind:"UnsupportedConnectionShapeError",reason:`The statement containing "${t}" is not a direct \`app.${k[e]}(...)\` call \u2014 binding it would wire a value \`connections\` cannot use.`};case"name-conflict":return{kind:"VariableNameConflictError",variableName:n.variableName};case"reserved-word":return{kind:"UnsupportedConnectionShapeError",reason:`Binding "${t}" would derive the variable name "${n.variableName}", a JavaScript reserved word no \`const\` can carry \u2014 bind the statement to a const of your own naming, or rename the resource.`}}}p(ae,"mapLocatorFailure");function se(n,e,t,o){const r=ee(n,e);if(!r.success){const f=r.error;return f.kind==="AmbiguousService"?d({kind:"UnsupportedConnectionShapeError",serviceName:e,reason:`${String(f.count)} services share the name "${e}" across ComputeFactory.build calls \u2014 the edit cannot be proven to target the right one.`}):d({kind:"ConnectServiceNotFoundError",serviceName:e,knownServices:f.knownServices})}const i=r.data,a=q(i,0);let s;if(a.success)s=a.data;else switch(a.error.kind){case"ContainerNotObjectLiteral":return d({kind:"UnsupportedConnectionShapeError",serviceName:e,reason:"The first container is not an object literal."});case"ContainersNotStaticArray":return d({kind:"UnsupportedConnectionShapeError",serviceName:e,reason:"`containers` is not a static array literal, so the container to wire cannot be located."});case"NoContainersArray":if(!$(i,"containers"))return d({kind:"UnsupportedConnectionShapeError",serviceName:e,reason:"Service carries a spread, shorthand or computed key that could supply `containers` invisibly, so an absent property cannot be proven."});break;case"ContainerIndexOutOfRange":break}const c=ce(i,e,t);if(!c.success)return d(c.error);if(c.data.alreadyConnected)return S("already-connected");let u,l,h="";if(s!==void 0){const f=N(s,"environment",e);if(!f.success)return d(f.error);const b=N(s,"secretsImport",e);if(!b.success)return d(b.error);u=f.data,l=b.data,h=de(o,[...f.data.keys,...b.data.keys])}return S({serviceName:e,service:i,container:s,environment:u,secretsImport:l,suffix:h,connectionsArray:c.data.array})}p(se,"planService");function ce(n,e,t){const o=_(n,"connections");if(o===-1)return $(n,"connections")?S({array:void 0,alreadyConnected:!1}):d({kind:"UnsupportedConnectionShapeError",serviceName:e,reason:"Service carries a spread, shorthand or computed key that could supply `connections` invisibly, so an absent property cannot be proven."});const r=n.properties[o]?.value;if(!Z(r))return d({kind:"UnsupportedConnectionShapeError",serviceName:e,reason:"`connections` is not an array literal."});for(const i of r.elements)if(g(i)){if(i.type==="Identifier"){if(i.name===t)return S({array:r,alreadyConnected:!0});continue}return d({kind:"UnsupportedConnectionShapeError",serviceName:e,reason:`\`connections\` contains a ${i.type}, so "${t}" cannot be proven absent from it.`})}return S({array:r,alreadyConnected:!1})}p(ce,"inspectConnections");function N(n,e,t){const o=_(n,e);if(o===-1)return $(n,e)?S({obj:void 0,keys:[]}):d({kind:"UnsupportedConnectionShapeError",serviceName:t,reason:`The first container carries a spread, shorthand or computed key that could supply \`${e}\` invisibly, so an absent property cannot be proven.`});const r=n.properties[o]?.value;if(!g(r)||r.type!=="ObjectExpression")return d({kind:"UnsupportedConnectionShapeError",serviceName:t,reason:`\`${e}\` is not an object literal.`});const i=r,a=[];for(const s of i.properties){if(s.type==="SpreadElement"||s.type==="SpreadProperty")return d({kind:"UnsupportedConnectionShapeError",serviceName:t,reason:`\`${e}\` contains a spread, so existing connection keys cannot be enumerated to derive the next suffix.`});if(s.type!=="Property"&&s.type!=="ObjectProperty")continue;if(s.computed===!0)return d({kind:"UnsupportedConnectionShapeError",serviceName:t,reason:`\`${e}\` contains a computed key.`});const c=x(s.key);c!==void 0&&a.push(c)}return S({obj:i,keys:a})}p(N,"collectWiringTarget");function de(n,e){const t=Object.values(n==="database"?E:A),o=new Set;for(const r of e)for(const i of t){const a=w(r,i);a!==void 0&&o.add(a)}return W(o.size===0?0:Math.max(...o))}p(de,"nextConnectionSuffix");function pe(n,e,t,o){const r=p((s,c,u=[])=>y.callExpression(y.memberExpression(s,y.identifier(c),!1),u),"call"),i=p(()=>y.identifier(e),"variable");if(n==="storage")return[{key:`${A.NAME}${t}`,value:r(i(),K.BUCKET_NAME),target:"environment"}];const a=p(s=>r(r(i(),v.CREDENTIALS),v.IMPORT,[P(s,o)]),"credential");return[{key:`${E.HOST}${t}`,value:r(i(),v.HOST),target:"environment"},{key:`${E.PORT}${t}`,value:r(i(),v.PORT),target:"environment"},{key:`${E.NAME}${t}`,value:r(i(),v.NAME),target:"environment"},{key:`${E.SSL}${t}`,value:P(V,o),target:"environment"},{key:`${E.USERNAME}${t}`,value:a(T.USERNAME),target:"secretsImport"},{key:`${E.PASSWORD}${t}`,value:a(T.PASSWORD),target:"secretsImport"}]}p(pe,"buildWiringEntries");function ue(n,e,t,o,r){if(n.connectionsArray!==void 0){const c=fe(n.connectionsArray,t,r);n.connectionsArray.elements.splice(c,0,y.identifier(e))}else D(n.service,"connections",y.arrayExpression([y.identifier(e)]),o);const i=n.container;if(i===void 0)return;const a=pe(t,e,n.suffix,o),s=[{key:"environment",state:n.environment},{key:"secretsImport",state:n.secretsImport}];for(const{key:c,state:u}of s){const l=a.filter(h=>h.target===c);l.length!==0&&Se(i,c,u,l,o)}}p(ue,"applyServiceWiring");function fe(n,e,t){const o=M.get(k[e])??0;for(let r=0;r<n.elements.length;r++){const i=n.elements[r];if(!g(i)||i.type!=="Identifier")continue;const a=i.name;if(typeof a!="string")continue;const s=t.get(a);if(s!==void 0&&s>o)return r}return n.elements.length}p(fe,"connectionInsertIndex");function le(n){const e=new Map;for(const t of n){const o=ie(t);if(o===void 0||o.type!=="VariableDeclaration")continue;const r=o.declarations;if(Array.isArray(r))for(const i of r){if(!g(i))continue;const a=i.id,s=i.init;if(!g(a)||a.type!=="Identifier"||!g(s))continue;const c=a.name;if(typeof c!="string")continue;const u=re(s);if(u===void 0)continue;const l=M.get(u);l!==void 0&&e.set(c,l)}}return e}p(le,"collectConnectionRanks");const me=Object.freeze([...Object.values(E),...Object.values(A),...Object.values(F)]);function he(n){let e=-1;for(let t=0;t<n.properties.length;t++){const o=n.properties[t];if(o===void 0||o.type!=="Property"&&o.type!=="ObjectProperty"||o.computed===!0)continue;const r=x(o.key);if(r===void 0)continue;me.some(a=>w(r,a)!==void 0)&&(e=t)}return e===-1?n.properties.length:e+1}p(he,"connectionEntryInsertIndex");function Se(n,e,t,o,r){const i=o.map(c=>U(c.key,c.value,r)),a=t?.obj;if(a===void 0){D(n,e,y.objectExpression(i),r);return}const s=he(a);a.properties.splice(s,0,...i)}p(Se,"insertObjectEntries");function D(n,e,t,o){const r=U(e,t,o);ne(n,r)}p(D,"appendProperty");export{Pe as connectResourceToCompute};
@@ -1 +1 @@
1
- var T=Object.defineProperty;var d=(r,e)=>T(r,"name",{value:e,configurable:!0});import{failure as i,success as m}from"../../types/Result.js";import{DEFAULT_FILE_PATH as L,computeLinesDelta as F,extractProgramBody as j}from"../_internal.js";import{buildObjectProperty as A,detectQuoteStyle as G,parse as Q,printFile as U}from"../fileRewriter/index.js";import{STATEMENT_REGISTRY as D}from"../registry.js";import{locateAllShapes as I,locateByShape as O}from"../semanticIndex/index.js";import{buildUnmanagedEditRefusal as V}from"./unmanagedShapeErrors.js";import{checkControlFlowPolicy as B}from"./controlFlowPolicy.js";import{driftGate as K,runPipeline as _,schemaGate as M}from"../validationGate/index.js";import{toAstValue as $}from"./addResource/propertyBuilder.js";import{buildMergedLiteral as k,collectPropertyKeys as H,isNodeShape as P}from"./modifyResource/literalConversion.js";import{findPropertyByKey as Y,visit as q}from"./objectGraph.js";import{evaluateEmitGuard as z,UNRESOLVED as J}from"../emitGuard.js";import{projectEmittedProperties as W,projectionRefusal as X}from"../emitProjection.js";function Ee(r,e,s={}){const n=e.filePath??L,t=Q(r,n);if(!t.success)return i(t.error);const a=O(r,{type:e.type,name:e.name},n);if(!a.success)return a.error.kind==="TemplateLiteralNameError"?i(a.error):i({kind:"SemanticQueryError",reason:a.error.reason,cause:a.error.cause});if(a.data===void 0)return i({kind:"ResourceNotFoundError",type:e.type,name:e.name,knownNames:te(r,e.type,n)});if(a.data.managed===!1)return i(V(e.type,"modify"));const o=B({ast:t.data,target:a.data,resource:{type:e.type,name:e.name},op:"modify"});if(o.refusal!==void 0)return i(o.refusal);const u=_([K],{content:r,plan:{type:e.type,name:e.name,properties:e.properties,op:"modify"},baseline:s.baseline,policy:e.driftPolicy});if(!u.success)return i(v(u.error));if(u.data.action==="skip")return m({content:r,linesChanged:{added:0,removed:0},skipped:!0});const c=j(t.data);if(c===void 0)return i({kind:"SemanticQueryError",reason:"recast File is missing program.body"});const f=Z(c,a.data);if(f===void 0)return i({kind:"SemanticQueryError",reason:"Unable to resolve config ObjectExpression for the located resource."});const w=G(t.data),x=k(f,e.properties),C=D[e.type].emitGuard,y=d(b=>z(C,b)??X(e.type,b),"judge"),R=y(S(f,k(f,{}))),l=y(S(f,x));if(l!==void 0&&l.property!==R?.property)return i(l);const g=M.run({content:r,plan:{type:e.type,name:e.name,properties:e.properties,op:"modify"}});if(!g.success)return i(v(g.error));const p=W(e.type,e.properties,{editingExisting:!0});if("refusal"in p)return i(p.refusal);const E=ee(f,p.props,w);if(!E.success)return i(E.error);const h=U(t.data,r),N=o.warning!==void 0?[o.warning.message]:void 0;return m({content:h,linesChanged:F(r,h),warnings:N})}d(Ee,"modifyResource");function S(r,e){const s={...e};for(const n of H(r))n in s||(s[n]=J);return s}d(S,"buildGuardView");function v(r){switch(r.kind){case"ParseError":case"ResourceNotFoundError":case"InvalidPropertyError":case"TemplateLiteralNameError":case"SemanticQueryError":case"DriftConflictError":case"ControlFlowClassifierError":return r;default:return{kind:"SemanticQueryError",reason:`Unexpected codemod error in validation-gate pipeline: ${r.kind}`}}}d(v,"narrowGateError");function Z(r,e){const s=e.start+e.length;let n;const t=d(a=>{if(n!==void 0||a.type!=="CallExpression")return;const o=a.arguments;if(!Array.isArray(o)||o.length<2)return;const u=o[0];if(!P(u)||u.type!=="StringLiteral"||u.start!==e.start||u.end!==s)return;const c=o[1];P(c)&&c.type==="ObjectExpression"&&(n=c)},"visitor");for(const a of r)if(q(a,t),n!==void 0)break;return n}d(Z,"findConfigObject");function ee(r,e,s){const n=re(r);for(const{key:t,value:a}of e){const o=$(a,s);if(o===void 0)return i({kind:"InvalidPropertyError",property:t,reason:`Unsupported property value for "${t}" (accepts string/number/boolean/null/array/object of the same).`});const u=Y(r,t);if(u!==void 0){u.value=o;continue}const c=A(t,o,s);n&&(c.extra={...c.extra??{},trailingComma:!0}),r.properties.push(c)}return m(void 0)}d(ee,"applyPropertyEdits");function re(r){return r.properties[r.properties.length-1]?.extra?.trailingComma===!0}d(re,"detectTrailingCommaStyle");function te(r,e,s){const n=I(r,s);return n.success?n.data.filter(t=>t.type===e).map(t=>t.symbolName):[]}d(te,"collectKnownNames");export{Ee as modifyResource};
1
+ var Q=Object.defineProperty;var d=(r,e)=>Q(r,"name",{value:e,configurable:!0});import{failure as o,success as E}from"../../types/Result.js";import{DEFAULT_FILE_PATH as U,computeLinesDelta as I,extractProgramBody as V}from"../_internal.js";import{buildObjectProperty as D,detectQuoteStyle as O,parse as K,printFile as _}from"../fileRewriter/index.js";import{STATEMENT_REGISTRY as M}from"../registry.js";import{locateAllShapes as W,locateByShape as H}from"../semanticIndex/index.js";import{buildUnmanagedEditRefusal as Y}from"./unmanagedShapeErrors.js";import{checkControlFlowPolicy as $}from"./controlFlowPolicy.js";import{driftGate as q,runPipeline as z,schemaGate as J}from"../validationGate/index.js";import{buildEmittedValue as X}from"./addResource/propertyBuilder.js";import{buildMergedLiteral as C,collectPropertyKeys as Z,isNodeShape as v}from"./modifyResource/literalConversion.js";import{findPropertyByKey as ee,visit as re}from"./objectGraph.js";import{evaluateEmitGuard as N,UNRESOLVED as te}from"../emitGuard.js";import{projectEmittedProperties as ne,projectionRefusals as R}from"../emitProjection.js";import{applyBinding as ie}from"./resourceStatementLocator.js";import{createSiblingBindingResolution as ae}from"./siblingBindingResolution.js";function Ne(r,e,s={}){const n=e.filePath??U,i=K(r,n);if(!i.success)return o(i.error);const t=H(r,{type:e.type,name:e.name},n);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)return o({kind:"ResourceNotFoundError",type:e.type,name:e.name,knownNames:ce(r,e.type,n)});if(t.data.managed===!1)return o(Y(e.type,"modify"));const a=$({ast:i.data,target:t.data,resource:{type:e.type,name:e.name},op:"modify"});if(a.refusal!==void 0)return o(a.refusal);const u=z([q],{content:r,plan:{type:e.type,name:e.name,properties:e.properties,op:"modify"},baseline:s.baseline,policy:e.driftPolicy});if(!u.success)return o(G(u.error));if(u.data.action==="skip")return E({content:r,linesChanged:{added:0,removed:0},skipped:!0});const c=V(i.data);if(c===void 0)return o({kind:"SemanticQueryError",reason:"recast File is missing program.body"});const f=se(c,t.data);if(f===void 0)return o({kind:"SemanticQueryError",reason:"Unable to resolve config ObjectExpression for the located resource."});const L=O(i.data),B=C(f,e.properties),h=M[e.type].emitGuard,l=ae(e.type,()=>c,{mustPrecedeStatementIndex:oe(c,t.data)??0}),m={editingExisting:!0,resourceName:e.name,resolveBinding:l.resolveBinding},P=T(f,C(f,{})),b=T(f,B),F=N(h,P),y=N(h,b);if(y!==void 0&&y.property!==F?.property)return o(y);const j=new Set(R(e.type,P,m).map(p=>p.property)),k=R(e.type,b,m).find(p=>!j.has(p.property));if(k!==void 0)return o(k);const S=J.run({content:r,plan:{type:e.type,name:e.name,properties:e.properties,op:"modify"}});if(!S.success)return o(G(S.error));l.takePendingWraps();const g=ne(e.type,e.properties,m);if("refusal"in g)return o(g.refusal);const w=ue(f,g.props,L);if(!w.success)return o(w.error);for(const p of l.takePendingWraps())ie(c,p);const x=_(i.data,r),A=a.warning!==void 0?[a.warning.message]:void 0;return E({content:x,linesChanged:I(r,x),warnings:A})}d(Ne,"modifyResource");function oe(r,e){const s=e.start+e.length;for(let n=0;n<r.length;n++){const i=r[n];if(i===void 0)continue;const{start:t,end:a}=i;if(!(typeof t!="number"||typeof a!="number")&&t<=e.start&&s<=a)return n}}d(oe,"findStatementIndexContaining");function T(r,e){const s={...e};for(const n of Z(r))n in s||(s[n]=te);return s}d(T,"buildGuardView");function G(r){switch(r.kind){case"ParseError":case"ResourceNotFoundError":case"InvalidPropertyError":case"TemplateLiteralNameError":case"SemanticQueryError":case"DriftConflictError":case"ControlFlowClassifierError":return r;default:return{kind:"SemanticQueryError",reason:`Unexpected codemod error in validation-gate pipeline: ${r.kind}`}}}d(G,"narrowGateError");function se(r,e){const s=e.start+e.length;let n;const i=d(t=>{if(n!==void 0||t.type!=="CallExpression")return;const a=t.arguments;if(!Array.isArray(a)||a.length<2)return;const u=a[0];if(!v(u)||u.type!=="StringLiteral"||u.start!==e.start||u.end!==s)return;const c=a[1];v(c)&&c.type==="ObjectExpression"&&(n=c)},"visitor");for(const t of r)if(re(t,i),n!==void 0)break;return n}d(se,"findConfigObject");function ue(r,e,s){const n=de(r);for(const i of e){const t=X(i,s);if(!t.success)return o(t.error);const a=ee(r,i.key);if(a!==void 0){a.value=t.data;continue}const u=D(i.key,t.data,s);n&&(u.extra={...u.extra??{},trailingComma:!0}),r.properties.push(u)}return E(void 0)}d(ue,"applyPropertyEdits");function de(r){return r.properties[r.properties.length-1]?.extra?.trailingComma===!0}d(de,"detectTrailingCommaStyle");function ce(r,e,s){const n=W(r,s);return n.success?n.data.filter(i=>i.type===e).map(i=>i.symbolName):[]}d(ce,"collectKnownNames");export{Ne as modifyResource};
@@ -0,0 +1,82 @@
1
+ import { type Result } from "../../types/Result.js";
2
+ import { type NodeShapeLike } from "./modifyResource/literalConversion.js";
3
+ /** The construct-producing statement shape an edit binds against. */
4
+ export interface LocatorTarget {
5
+ /** Factory identifier — the `<factory>` of `<factory>.build("<name>", …)`. */
6
+ factory: string;
7
+ /** Method that materialises the thunk — the `<m>` of `app.<m>(…)`. */
8
+ appMethod: string;
9
+ }
10
+ export interface BindingPlan {
11
+ variableName: string;
12
+ /** Index of the matched statement in the module body — callers that emit a
13
+ * reference to the binding need it to judge declaration order. */
14
+ statementIndex: number;
15
+ /** Set when the statement must be wrapped in a `const` declaration. */
16
+ wrap?: {
17
+ statementIndex: number;
18
+ expression: NodeShapeLike;
19
+ };
20
+ }
21
+ export type LocatorFailure = {
22
+ kind: "statement-not-found";
23
+ } | {
24
+ kind: "ambiguous";
25
+ count: number;
26
+ } | {
27
+ kind: "multiple-declarators";
28
+ } | {
29
+ kind: "pattern-binding";
30
+ } | {
31
+ kind: "unreadable-name";
32
+ }
33
+ /** A `const` already binds the statement, but its initialiser is not the
34
+ * app-method wrap — the bound value is not the construct. */
35
+ | {
36
+ kind: "init-not-app-method-wrap";
37
+ }
38
+ /** The matched statement can never carry a `const` binding: an `if` body, a
39
+ * declaration-less export, or an export wrapper around anything that is not
40
+ * already a `const` declaration (`export default app.addStorage(...)` —
41
+ * wrapping inside the export would change the module's exported surface).
42
+ * An `export const x = app.addX(...)` is NOT this: its declaration already
43
+ * binds, and `planBinding` unwraps the export to reuse it. */
44
+ | {
45
+ kind: "unbindable-statement";
46
+ statementType: string;
47
+ } | {
48
+ kind: "not-app-method-call";
49
+ } | {
50
+ kind: "bare-factory-call";
51
+ } | {
52
+ kind: "foreign-wrapper";
53
+ } | {
54
+ kind: "name-conflict";
55
+ variableName: string;
56
+ }
57
+ /** Same family as `name-conflict` — the statement shape is bindable, but
58
+ * the derived variable name is unusable: `toVariableName("Import")` yields
59
+ * `import`, and `const import = …` does not parse. */
60
+ | {
61
+ kind: "reserved-word";
62
+ variableName: string;
63
+ };
64
+ /**
65
+ * Pure analysis: find the one top-level statement containing
66
+ * `<factory>.build("<resourceName>", …)` and decide how the construct value
67
+ * is (or would be) bound. Reuses an existing `const` binding; otherwise
68
+ * plans a wrap under `toVariableName(resourceName)`, refusing on a
69
+ * module-scope name conflict rather than renaming.
70
+ */
71
+ export declare function planBinding(body: NodeShapeLike[], target: LocatorTarget, resourceName: string): Result<BindingPlan, LocatorFailure>;
72
+ export declare function applyBinding(body: NodeShapeLike[], plan: BindingPlan): void;
73
+ export declare function isFactoryBuildOf(node: NodeShapeLike, factory: string, resourceName: string): boolean;
74
+ /**
75
+ * The inner declaration of an export statement, the statement itself
76
+ * otherwise; `undefined` for declaration-less exports (`export default expr`,
77
+ * `export { x }`).
78
+ */
79
+ export declare function unwrapExportDeclaration(statement: NodeShapeLike): NodeShapeLike | undefined;
80
+ /** Method name of a non-computed `<recv>.method(...)` call, else undefined. */
81
+ export declare function appMethodNameOf(node: NodeShapeLike): string | undefined;
82
+ export declare function moduleScopeDeclares(body: NodeShapeLike[], name: string): boolean;
@@ -0,0 +1 @@
1
+ var w=Object.defineProperty;var d=(r,n)=>w(r,"name",{value:n,configurable:!0});import{builders as b}from"ast-types";import{toVariableName as A}from"../../generation/common.js";import{failure as o,success as E}from"../../types/Result.js";import{RESERVED_IDENTIFIER_NAMES as v}from"./addResource/propertyBuilder.js";import{isNodeShape as s}from"./modifyResource/literalConversion.js";import{unwrapTsWrappers as I}from"./moduleConstResolver.js";import{visit as k}from"./objectGraph.js";function j(r,n,a){const e=[];for(let p=0;p<r.length;p++){const l=r[p];if(l===void 0)continue;let y=!1;k(l,x=>{x.type==="CallExpression"&&D(x,n.factory,a)&&(y=!0)}),y&&e.push({statementIndex:p})}if(e.length===0)return o({kind:"statement-not-found"});if(e.length>1)return o({kind:"ambiguous",count:e.length});const t=e[0];if(t===void 0)return o({kind:"statement-not-found"});const i=r[t.statementIndex];if(i===void 0)return o({kind:"statement-not-found"});const c=i.type==="ExportNamedDeclaration"||i.type==="ExportDefaultDeclaration",f=h(i);if(f===void 0)return o({kind:"unbindable-statement",statementType:i.type});if(f.type==="VariableDeclaration"){const p=f.declarations;if(!Array.isArray(p)||p.length!==1)return o({kind:"multiple-declarators"});const l=p[0].id;if(!s(l)||l.type!=="Identifier")return o({kind:"pattern-binding"});const y=l.name;if(typeof y!="string")return o({kind:"unreadable-name"});const x=p[0].init;return!s(x)||!g(x,n,a)?o({kind:"init-not-app-method-wrap"}):E({variableName:y,statementIndex:t.statementIndex})}if(c)return o({kind:"unbindable-statement",statementType:i.type});if(f.type!=="ExpressionStatement")return o({kind:"unbindable-statement",statementType:f.type});const u=f.expression;if(!s(u)||u.type!=="CallExpression")return o({kind:"not-app-method-call"});if(D(u,n.factory,a))return o({kind:"bare-factory-call"});if(!g(u,n,a))return o({kind:"foreign-wrapper"});const m=A(a);return v.has(m)?o({kind:"reserved-word",variableName:m}):N(r,m)?o({kind:"name-conflict",variableName:m}):E({variableName:m,statementIndex:t.statementIndex,wrap:{statementIndex:t.statementIndex,expression:u}})}d(j,"planBinding");function L(r,n){if(n.wrap===void 0)return;const{statementIndex:a,expression:e}=n.wrap,t=r[a],i=b.variableDeclaration("const",[b.variableDeclarator(b.identifier(n.variableName),e)]),c=t?.comments;c!==void 0&&(i.comments=c),r[a]=i}d(L,"applyBinding");function D(r,n,a){const e=r.callee;if(!s(e)||e.type!=="MemberExpression"||e.computed===!0)return!1;const t=e.object,i=e.property;if(!s(t)||t.type!=="Identifier"||t.name!==n||!s(i)||i.type!=="Identifier"||i.name!=="build")return!1;const c=r.arguments;if(!Array.isArray(c))return!1;const f=c[0];return!s(f)||f.type!=="StringLiteral"&&f.type!=="Literal"?!1:f.value===a}d(D,"isFactoryBuildOf");function h(r){if(r.type!=="ExportNamedDeclaration"&&r.type!=="ExportDefaultDeclaration")return r;const n=r.declaration;return s(n)?n:void 0}d(h,"unwrapExportDeclaration");function M(r){const n=I(r);if(n.type!=="CallExpression")return;const a=n.callee;if(!s(a)||a.type!=="MemberExpression"||a.computed===!0)return;const e=a.property;if(!s(e)||e.type!=="Identifier")return;const t=e.name;return typeof t=="string"?t:void 0}d(M,"appMethodNameOf");function g(r,n,a){const e=I(r);if(M(e)!==n.appMethod)return!1;const t=e.arguments;if(!Array.isArray(t))return!1;const i=t[0];return s(i)?D(I(i),n.factory,a):!1}d(g,"isAppMethodWrap");function N(r,n){for(const a of r){const e=h(a);if(e!==void 0){if(e.type==="ImportDeclaration"){const t=e.specifiers;if(!Array.isArray(t))continue;for(const i of t){const c=i.local;if(s(c)&&c.name===n)return!0}continue}if(e.type==="VariableDeclaration"){const t=e.declarations;if(!Array.isArray(t))continue;for(const i of t){if(!s(i))continue;const c=i.id;if(!s(c))continue;let f=!1;if(k(c,u=>{u.type==="Identifier"&&u.name===n&&(f=!0)}),f)return!0}continue}if(e.type==="FunctionDeclaration"||e.type==="ClassDeclaration"){const t=e.id;if(s(t)&&t.name===n)return!0}}}return!1}d(N,"moduleScopeDeclares");export{M as appMethodNameOf,L as applyBinding,D as isFactoryBuildOf,N as moduleScopeDeclares,j as planBinding,h as unwrapExportDeclaration};
@@ -0,0 +1,22 @@
1
+ import type { StatementType } from "../types.js";
2
+ import type { NodeShapeLike } from "./modifyResource/literalConversion.js";
3
+ import { type BindingPlan } from "./resourceStatementLocator.js";
4
+ export interface SiblingBindingResolution {
5
+ resolveBinding: (planResourceName: string) => string | undefined;
6
+ /** Wrap plans recorded by successful resolutions since the last drain. */
7
+ takePendingWraps: () => BindingPlan[];
8
+ }
9
+ export interface SiblingBindingConstraints {
10
+ /**
11
+ * Body index the resolved binding must sit strictly BEFORE. The modify
12
+ * path supplies its own statement's index: it emits a reference into an
13
+ * EXISTING statement, so a binding declared after it — existing `const` or
14
+ * freshly planned wrap alike — would be read before its declaration (TDZ
15
+ * at runtime, TS2448 at typecheck). A violating candidate refuses to
16
+ * `undefined` rather than emit the broken reference. The add path needs no
17
+ * constraint: its insertion-order table lands the new statement after
18
+ * every bindable sibling type.
19
+ */
20
+ mustPrecedeStatementIndex?: number;
21
+ }
22
+ export declare function createSiblingBindingResolution(type: StatementType, getBody: () => NodeShapeLike[] | undefined, constraints?: SiblingBindingConstraints): SiblingBindingResolution;
@@ -0,0 +1 @@
1
+ var l=Object.defineProperty;var e=(t,i)=>l(t,"name",{value:i,configurable:!0});import{FACTORY_IDENTIFIERS as g,STATEMENT_REGISTRY as I}from"../registry.js";import{planBinding as T}from"./resourceStatementLocator.js";const E={cdn:["storage","compute"]};function A(t,i,p={}){const m=E[t]??[];let d=[];return{resolveBinding:e(r=>{const u=i();if(u===void 0)return;const o=[];for(const c of m){const f=I[c].emitWrapper?.appMethod;if(f===void 0)continue;const s=T(u,{factory:g[c],appMethod:f},r);if(s.success){o.push(s.data);continue}if(s.error.kind!=="statement-not-found")return}if(o.length!==1)return;const n=o[0];if(n===void 0)return;const a=p.mustPrecedeStatementIndex;if(!(a!==void 0&&n.statementIndex>=a))return n.wrap!==void 0&&d.push(n),n.variableName},"resolveBinding"),takePendingWraps:e(()=>{const r=d;return d=[],r},"takePendingWraps")}}e(A,"createSiblingBindingResolution");export{A as createSiblingBindingResolution};
@@ -1,29 +1,50 @@
1
1
  import { type ProjectionContext } from "../projection/index.js";
2
2
  import { type EmitGuardProperties } from "./emitGuard.js";
3
3
  import type { InvalidPropertyError, StatementType } from "./types.js";
4
- /** One emitted construct prop, resolved to a serialisable value. */
5
- export interface LiteralProp {
4
+ /**
5
+ * One emitted construct prop. `literal` is a serialisable value; `expression`
6
+ * carries the source text of a live binding a projection rule resolved — the
7
+ * emitters splice it as an identifier rather than serialising it.
8
+ */
9
+ export type EmittedProp = {
6
10
  key: string;
11
+ kind: "literal";
7
12
  value: unknown;
8
- }
13
+ } | {
14
+ key: string;
15
+ kind: "expression";
16
+ source: string;
17
+ };
9
18
  /**
10
- * The first plan key in `properties` that this type cannot turn into a
11
- * construct prop, as the codebase's actionable error shape.
19
+ * EVERY plan key in `properties` this type cannot turn into a construct
20
+ * prop, one refusal per key, as the codebase's actionable error shape.
21
+ *
22
+ * The modify judge is why this is a set and not a first-refusal probe: it
23
+ * compares a pre-edit view against a post-merge view and refuses only what
24
+ * the edit introduced. A statement can carry several pre-existing
25
+ * unprojectable keys, and holding only the first per view blamed the repair
26
+ * of one on an untouched other (fixing a dead `defaultOriginRef` was refused
27
+ * for the `behaviours` array the edit never named).
12
28
  *
13
29
  * Values are passed through verbatim, `UNRESOLVED` included: a plan-only key
14
30
  * is inert whatever it holds, which is exactly the presence test the
15
31
  * `silentlyDropped` rules applied.
16
32
  */
17
- export declare function projectionRefusal(type: StatementType, properties: EmitGuardProperties): InvalidPropertyError | undefined;
33
+ export declare function projectionRefusals(type: StatementType, properties: EmitGuardProperties, context?: ProjectionContext): InvalidPropertyError[];
18
34
  /**
19
35
  * The construct props to write for `properties`, or the refusal that stops
20
36
  * the write.
21
37
  *
22
38
  * A non-projectable type is emitted as it always was — its plan keys go into
23
39
  * the file verbatim and its `emitGuard` is what decides whether that is safe.
40
+ *
41
+ * An `expression` prop reaches the emitters only when a live rule's `bind`
42
+ * resolved a binding for it: an unresolvable bind lands in `unprojectable`
43
+ * and refuses the whole statement above, so there is no fall-through path
44
+ * that writes a plan value where a live construct belongs.
24
45
  */
25
46
  export declare function projectEmittedProperties(type: StatementType, properties: Readonly<Record<string, unknown>>, context?: ProjectionContext): {
26
- props: LiteralProp[];
47
+ props: EmittedProp[];
27
48
  } | {
28
49
  refusal: InvalidPropertyError;
29
50
  };
@@ -1 +1 @@
1
- var y=Object.defineProperty;var n=(e,r)=>y(e,"name",{value:r,configurable:!0});import{isProjectableType as u,projectPlanToConstructProps as a}from"../projection/index.js";import{AUTHOR_BY_HAND as p}from"./emitGuard.js";function l(e,r){return{kind:"InvalidPropertyError",property:e,reason:`${r} \`fjall add\` would write it into the factory call and the resource would deploy without it. ${p}`}}n(l,"unprojectableRefusal");function v(e,r){if(!u(e))return;const[t]=a(e,r).unprojectable;if(t!==void 0)return l(t.key,t.reason)}n(v,"projectionRefusal");function h(e,r,t={}){if(!u(e))return{props:Object.entries(r).map(([o,d])=>({key:o,value:d}))};const{props:c,unprojectable:f}=a(e,r,t),[i]=f;if(i!==void 0)return{refusal:l(i.key,i.reason)};const s=[];for(const o of c){if(o.value.kind==="expression")return{refusal:{kind:"InvalidPropertyError",property:o.key,reason:`\`${o.key}\` needs a live construct expression, which this command cannot resolve against a file it did not write. ${p}`}};s.push({key:o.key,value:o.value.value})}return{props:s}}n(h,"projectEmittedProperties");export{h as projectEmittedProperties,v as projectionRefusal};
1
+ var p=Object.defineProperty;var a=(e,n)=>p(e,"name",{value:n,configurable:!0});import{isProjectableType as s,projectPlanToConstructProps as u}from"../projection/index.js";import{AUTHOR_BY_HAND as f}from"./emitGuard.js";const m={"plan-only":`\`fjall add\` would write it into the factory call and the resource would deploy without it. ${f}`,"unresolved-binding":"Nothing has been written: the engine refuses rather than guessing a construct. Add or bind a sibling resource with that name (or fix the name) and run the same command again \u2014 it succeeds once a unique binding exists.","missing-context":"Nothing has been written: the command context supplied no resource name to derive it from."};function l(e,n,r="plan-only"){return{kind:"InvalidPropertyError",property:e,reason:`${n} ${m[r]}`}}a(l,"unprojectableRefusal");function g(e,n,r={}){return s(e)?u(e,n,r).unprojectable.map(o=>l(o.key,o.reason,o.kind)):[]}a(g,"projectionRefusals");function j(e,n,r={}){if(!s(e))return{props:Object.entries(n).map(([t,d])=>({key:t,kind:"literal",value:d}))};const{props:o,unprojectable:c}=u(e,n,r),[i]=c;return i!==void 0?{refusal:l(i.key,i.reason,i.kind)}:{props:o.map(t=>t.value.kind==="expression"?{key:t.key,kind:"expression",source:t.value.source}:{key:t.key,kind:"literal",value:t.value.value})}}a(j,"projectEmittedProperties");export{j as projectEmittedProperties,g as projectionRefusals};
@@ -1 +1 @@
1
- var m=Object.defineProperty;var c=(e,t)=>m(e,"name",{value:t,configurable:!0});import{isDeepStrictEqual as $}from"node:util";import{maskSensitiveOutput as d}from"@fjall/util";import{STATEMENT_REGISTRY as h}from"../registry.js";import{classifyGate as l,createLocateGate as p,driftGate as G,parseGate as f,schemaGate as b}from"../validationGate/index.js";function D(e){const{intent:t,tier:i}=e;if(t.op==="add"&&h[t.type].addContract===void 0)return{state:"rejected",tier:i,failedGate:"schema",diagnostics:`LLM fallback cannot add a ${t.type}: its complete add contract is not yet declared. Nothing has been written.`};const g={type:t.type,name:t.name,properties:t.properties,op:t.op==="add"?"add":"modify"},y={content:e.outputContent,plan:g,inputContent:e.inputContent,...e.baseline!==void 0&&{baseline:e.baseline},...e.abortSignal!==void 0&&{signal:e.abortSignal}};for(const n of E(t.op)){if(e.abortSignal?.aborted===!0)return{state:"cancelled"};const u=Date.now();let a;try{a=n.run(y)}catch(o){return e.onGateResult?.(n.id,"fail",Date.now()-u),{state:"rejected",tier:i,failedGate:n.id,diagnostics:d(o instanceof Error?o.message:String(o))}}const s=Date.now()-u;if(!a.success)return e.onGateResult?.(n.id,"fail",s),{state:"rejected",tier:i,failedGate:n.id,diagnostics:d(S(a.error))};if(a.data.action==="reject")return e.onGateResult?.(n.id,"fail",s),{state:"rejected",tier:i,failedGate:a.data.failedGate??n.id,diagnostics:d(a.data.diagnostics??"gate rejected the output")};if(a.data.driftState!==void 0){const o=a.data.driftState.deltas.filter(r=>r.verdict==="clean"&&Object.hasOwn(t.properties,r.property)&&!$(r.theirs,r.ours)).map(r=>`\`${r.property}\``);if(o.length>0)return e.onGateResult?.(n.id,"fail",s),{state:"rejected",tier:i,failedGate:n.id,diagnostics:d(`output left requested ${o.length===1?"property":"properties"} at the baseline value: ${o.join(", ")}`)}}e.onGateResult?.(n.id,"pass",s)}}c(D,"gateFallbackOutput");function E(e){return e==="remove"?[f,l,p({mode:"absent"})]:[f,l,p({mode:"present"}),b,G]}c(E,"gatesForOp");function S(e){switch(e.kind){case"ParseError":return`output does not parse: ${e.message}`;case"ControlFlowClassifierError":return`output moved ${e.resource.type} "${e.resource.name}" out of top-level scope (${e.reason})`;case"ResourceNotFoundError":return`${e.type} "${e.name}" is missing from the output`;case"DuplicateResourceError":return`${e.type} "${e.name}" is still present in the output`;case"TemplateLiteralNameError":return`output names the resource with a template literal (line ${String(e.line)})`;case"InvalidPropertyError":return`property \`${e.property}\`: ${e.reason}`;case"DriftConflictError":{const t=e.deltas.filter(i=>i.verdict==="conflict").map(i=>`\`${i.property}\``).join(", ");return`output conflicts with the requested properties on ${e.resource.type} "${e.resource.name}"`+(t.length>0?` (${t})`:"")}case"SemanticQueryError":return`output could not be analysed: ${e.reason}`;default:return e.kind}}c(S,"describeGateError");export{D as gateFallbackOutput};
1
+ var m=Object.defineProperty;var c=(e,t)=>m(e,"name",{value:t,configurable:!0});import{isDeepStrictEqual as $}from"node:util";import{maskSensitiveOutput as d}from"@fjall/util";import{STATEMENT_REGISTRY as h}from"../registry.js";import{classifyGate as l,createLocateGate as p,driftGate as G,parseGate as f,projectionGate as b,schemaGate as E}from"../validationGate/index.js";function k(e){const{intent:t,tier:i}=e;if(t.op==="add"&&h[t.type].addContract===void 0)return{state:"rejected",tier:i,failedGate:"schema",diagnostics:`LLM fallback cannot add a ${t.type}: its complete add contract is not yet declared. Nothing has been written.`};const g={type:t.type,name:t.name,properties:t.properties,op:t.op==="add"?"add":"modify"},y={content:e.outputContent,plan:g,inputContent:e.inputContent,...e.baseline!==void 0&&{baseline:e.baseline},...e.abortSignal!==void 0&&{signal:e.abortSignal}};for(const n of S(t.op)){if(e.abortSignal?.aborted===!0)return{state:"cancelled"};const u=Date.now();let a;try{a=n.run(y)}catch(o){return e.onGateResult?.(n.id,"fail",Date.now()-u),{state:"rejected",tier:i,failedGate:n.id,diagnostics:d(o instanceof Error?o.message:String(o))}}const s=Date.now()-u;if(!a.success)return e.onGateResult?.(n.id,"fail",s),{state:"rejected",tier:i,failedGate:n.id,diagnostics:d(j(a.error))};if(a.data.action==="reject")return e.onGateResult?.(n.id,"fail",s),{state:"rejected",tier:i,failedGate:a.data.failedGate??n.id,diagnostics:d(a.data.diagnostics??"gate rejected the output")};if(a.data.driftState!==void 0){const o=a.data.driftState.deltas.filter(r=>r.verdict==="clean"&&Object.hasOwn(t.properties,r.property)&&!$(r.theirs,r.ours)).map(r=>`\`${r.property}\``);if(o.length>0)return e.onGateResult?.(n.id,"fail",s),{state:"rejected",tier:i,failedGate:n.id,diagnostics:d(`output left requested ${o.length===1?"property":"properties"} at the baseline value: ${o.join(", ")}`)}}e.onGateResult?.(n.id,"pass",s)}}c(k,"gateFallbackOutput");function S(e){return e==="remove"?[f,l,p({mode:"absent"})]:[f,l,p({mode:"present"}),b,E,G]}c(S,"gatesForOp");function j(e){switch(e.kind){case"ParseError":return`output does not parse: ${e.message}`;case"ControlFlowClassifierError":return`output moved ${e.resource.type} "${e.resource.name}" out of top-level scope (${e.reason})`;case"ResourceNotFoundError":return`${e.type} "${e.name}" is missing from the output`;case"DuplicateResourceError":return`${e.type} "${e.name}" is still present in the output`;case"TemplateLiteralNameError":return`output names the resource with a template literal (line ${String(e.line)})`;case"InvalidPropertyError":return`property \`${e.property}\`: ${e.reason}`;case"DriftConflictError":{const t=e.deltas.filter(i=>i.verdict==="conflict").map(i=>`\`${i.property}\``).join(", ");return`output conflicts with the requested properties on ${e.resource.type} "${e.resource.name}"`+(t.length>0?` (${t})`:"")}case"SemanticQueryError":return`output could not be analysed: ${e.reason}`;default:return e.kind}}c(j,"describeGateError");export{k as gateFallbackOutput};
@@ -38,11 +38,10 @@ export interface StatementTypeEntry {
38
38
  * convenience: a guarded type's required plan keys are exactly the
39
39
  * scaffold-consumed keys its own guard REFUSES to write
40
40
  * (`compute.needsConnection` is required by the plan schema and
41
- * `silentlyDropped` by the guard; `cdn.defaultOriginRef` likewise), so
42
- * demanding them at the gate would be a catch-22 no invocation could
43
- * satisfy. When an increment lands a type's projection and deletes its
44
- * guard table, declare `addContract` in the same edit — the schema-gate
45
- * XOR test pins the pairing.
41
+ * `silentlyDropped` by the guard), so demanding them at the gate would be
42
+ * a catch-22 no invocation could satisfy. When an increment lands a
43
+ * type's projection and deletes its guard table, declare `addContract` in
44
+ * the same edit — the schema-gate XOR test pins the pairing.
46
45
  *
47
46
  * Absent = the schema gate applies `schemaFragment` to adds too
48
47
  * (unknown-key and per-key-shape checks only, no required demand).
@@ -88,10 +87,10 @@ export interface StatementTypeEntry {
88
87
  * ONLY for types the plan→construct projection cannot describe. Where it
89
88
  * can (`src/projection/tables.ts`), the disagreement is TRANSLATED at
90
89
  * emission and no rule belongs here — `emitProjection.ts` reports what is
91
- * left from the projection's own `unprojectable` list. What survives is
92
- * props needing a live sibling construct (`cdn`'s origin, `compute`'s
93
- * connection expansion) and cross-key conditions no per-key table can
94
- * state.
90
+ * left from the projection's own `unprojectable` list, and a single live
91
+ * sibling binding is now expressible through a `live` rule (`cdn`'s
92
+ * origin). What survives is `compute`'s connection expansion (more than
93
+ * one statement) and cross-key conditions no per-key table can state.
95
94
  *
96
95
  * Rules cover exactly two classes, and nothing else:
97
96
  *
@@ -120,6 +119,42 @@ export interface StatementTypeEntry {
120
119
  * both.
121
120
  */
122
121
  emitGuard?: ReadonlyArray<EmitGuardRule>;
122
+ /**
123
+ * Per-type derivation of the factory call's FIRST ARGUMENT — the construct
124
+ * id — where it must not be the resource name verbatim.
125
+ *
126
+ * Declared only for `pattern`, and it exists because the verbatim identity
127
+ * was synth-fatal there: a pattern construct hands `props.name` on to its
128
+ * children as construct ids — `StaticSite` materialises its internal Cdn
129
+ * via `this.app.addCdn(CdnFactory.build(this.props.name, cdnProps))`
130
+ * (components/infrastructure `staticSite.ts:274`, `createCdn`) — and a
131
+ * staticsite's placement is the SAME cdn stack the pattern construct
132
+ * occupies, so `build("Site", { name: "Site", … })` registered the id
133
+ * "Site" twice in one scope and died at synth with `There is already a
134
+ * Construct with name 'Site'`. The scaffold never had the problem because
135
+ * it splits the two identities: construct id `patternConstructId(name,
136
+ * type)` (`@fjall/util`, the ONE shared derivation — the scaffold's
137
+ * `generatePatternCodeWithComments` and both halves below consume it, so
138
+ * the emit/locate contract cannot drift site-by-site), `name` prop the
139
+ * kebab workload name. `emitConstructId` adopts that convention for the
140
+ * codemod's emission.
141
+ *
142
+ * `locateCandidates` is the read half of the same contract: `--name Site`
143
+ * must keep addressing a hand-written `build("Site", …)` (raw name FIRST —
144
+ * the legacy bare shape is pinned) and gain the scaffold-idiom
145
+ * `build("SiteStaticSite", …)` forms. Candidates derive from the FULL
146
+ * pattern registry, not the deployable subset, so a scaffold-emitted
147
+ * nextjs statement stays addressable for modify/remove. A lookup whose
148
+ * candidates match statements under more than one id is ambiguous and
149
+ * refuses — never silently picks one.
150
+ *
151
+ * Absent = both identities are the resource name, which is every other
152
+ * type's contract.
153
+ */
154
+ statementIdentity?: {
155
+ emitConstructId: (name: string, properties: Readonly<Record<string, unknown>>) => string;
156
+ locateCandidates: (name: string) => string[];
157
+ };
123
158
  }
124
159
  export declare const FACTORY_IDENTIFIERS: {
125
160
  readonly database: "DatabaseFactory";
@@ -146,4 +181,17 @@ export declare const STATEMENT_REGISTRY: Record<StatementType, StatementTypeEntr
146
181
  * the key set derive it from here rather than re-listing the literals.
147
182
  */
148
183
  export declare const REGISTERED_STATEMENT_TYPES: ReadonlyArray<StatementType>;
184
+ /**
185
+ * The construct id the ADD path writes as the factory's first argument:
186
+ * the type's `statementIdentity` derivation where one is declared, the
187
+ * resource name verbatim everywhere else.
188
+ */
189
+ export declare function constructIdFor(type: StatementType, name: string, properties: Readonly<Record<string, unknown>>): string;
190
+ /**
191
+ * Every factory first argument a `--name` lookup for `name` may address,
192
+ * raw name first. The single source every locate site matches against —
193
+ * a site comparing `=== name` instead silently loses the scaffold-idiom
194
+ * statements `statementIdentity` types emit.
195
+ */
196
+ export declare function locateNamesFor(type: StatementType, name: string): string[];
149
197
  export declare function findTypeByIdentifier(identifier: string): StatementType | undefined;
@@ -1 +1 @@
1
- var N=Object.defineProperty;var a=(e,t)=>N(e,"name",{value:t,configurable:!0});import{z as p}from"zod";import{BuildkitePropsObjectSchema as y,CDNResourcePlanSchema as M,ComputeResourcePlanSchema as _,CrossPlanConnectionResourcePlanSchema as f,DatabaseResourcePlanSchema as b,NetworkResourcePlanSchema as C,NextJSPatternConfigSchema as D,OrganisationResourcePlanSchema as S,PayloadPatternConfigSchema as O,S3ResourcePlanSchema as P,SQSResourcePlanSchema as v,StaticSitePatternConfigSchema as x,VpcPeerAccepterResourcePlanSchema as R,VpcPeerResourcePlanSchema as W}from"../schemas/index.js";import{failure as G}from"../types/Result.js";import{isRecord as I}from"./_internal.js";import{AUTHOR_BY_HAND as d,requiresLiveValue as k,silentlyDropped as n,UNRESOLVED as l,whenAbsent as u}from"./emitGuard.js";const r={database:"DatabaseFactory",storage:"StorageFactory",compute:"ComputeFactory",messaging:"MessagingFactory",cdn:"CdnFactory",network:"NetworkFactory",pattern:"PatternFactory","vpc-peer":"VpcPeerFactory","vpc-peer-accepter":"VpcPeerAccepterFactory","cross-plan-connection":"CrossPlanConnectionFactory",organisation:"OrganisationFactory",buildkite:"BuildkiteFactory"};function i(e){const{name:t,...s}=e.shape;return p.object(s).partial().strict()}a(i,"fragmentOf");function c(e){const{name:t,...s}=e.shape;return p.object(s).strict()}a(c,"completeFragmentOf");const j=i(b),B=i(P),L=(()=>{const{name:e,...t}=_.shape;return p.object(t).partial().strict()})(),V=i(v),Y=i(M),q=i(C),K=(()=>{const{name:e,type:t,cdn:s,...A}=O.shape,{name:re,type:oe,cdn:se,...E}=D.shape,{name:ie,type:ce,domain:de,cdn:F,...w}=x.shape;return p.object({...A,...E,...w,cdn:p.union([s,F])}).partial().strict()})(),U=i(W),z=i(R),H=i(f),Q=i(S),$=i(y);function h(e,t){return G({kind:"SemanticQueryError",reason:`StatementTypeEntry.${e} for ${t} is not wired; existing types dispatch through the shared locator/generator.`})}a(h,"notWiredError");function J(e){return{factoryIdentifier:e,findByShape:a(()=>h("locator.findByShape",e),"findByShape"),validateContext:a(()=>h("locator.validateContext",e),"validateContext")}}a(J,"createLocator");function X(e){return{build:a(()=>{const t=h("generator.build",e);throw new Error(t.success?"unreachable":t.error.reason)},"build")}}a(X,"createGenerator");const Z=n("variableName","`variableName` is a plan-only round-trip key naming the `const <var> =` binding the scaffold emits; no construct reads it."),T=n("extraProperties","`extraProperties` is a plan-only round-trip key: the scaffold flattens each `{ key, sourceText }` entry into a sibling property, and no construct reads the array."),m="the connection keys are scaffold inputs, not construct props: the scaffold expands them into environment variables and a `connections: [...]` array, and the compute constructs read none of them.",ee=[{property:"deployment",refuseWhen:a(e=>(e.type==="lambda"||e.deployment!==void 0)&&e.deployment!=="container"&&e.code!==l,"refuseWhen"),reason:"a code-deployed Lambda requires `code: Code`, a CDK object built by `Code.fromAsset(...)` that no flag value can carry, so the statement takes the whole app's synth down with `Cannot read properties of undefined (reading 'bind')` \u2014 naming no resource. Use `--deployment container`, which is fully expressible, or write this compute in infrastructure.ts by hand."},n("codePath","`codePath` is the scaffold's input to `Code.fromAsset(...)`; no compute construct reads it, and a container Lambda takes its code from the image."),k("runtime","the construct expects a CDK `Runtime`, not a version string, and a container Lambda takes its runtime from the image (`Runtime.FROM_IMAGE`) regardless."),k("architecture","the construct expects a CDK `Architecture`, not a string: `--architecture ARM_64` emits an EMPTY `Architectures` list, whereas omitting the flag emits the correct default."),n("memory","the Lambda construct reads `memorySize`, so `--memory 1024` deploys a function at the 128 MB default."),n("ssmSecrets","the Lambda construct reads `secrets`, so `--ssmSecrets` deploys a function with no SSM wiring and no secrets environment at all."),n("scheduleExpression","`scheduleExpression` is not a compute prop: the scaffold turns it into a SEPARATE `app.addSchedule(...)` statement, which this command cannot emit."),n("needsConnection",m),n("connectedDatabase",m),n("connectedStorage",m),n("connectedMessaging",m),Z,T],te=[{property:"originType",refuseWhen:u("originType"),reason:"`Cdn` selects its origin implementation from an `originType` discriminator that the CDN vocabulary does not carry, so a distribution built from CDN properties throws `Unsupported CDN origin type: undefined` the moment `app.addCdn` materialises it. "+d},{property:"originType",refuseWhen:a(e=>e.originType==="s3"&&e.bucket!==l,"refuseWhen"),reason:"an s3 origin needs `bucket` to be an `IBucket` or a Fjall `Storage` \u2014 a live construct, not a name, and not a string; omitting it throws `Cannot read properties of undefined (reading 'bucketRegionalDomainName')` at synth. "+d},{property:"originType",refuseWhen:a(e=>e.originType==="alb"&&e.loadBalancer!==l,"refuseWhen"),reason:'an alb origin needs `loadBalancer` to be an `IApplicationLoadBalancer` or an ECS compute \u2014 a live construct, not a name, and not a string; a string reaches CloudFormation as `Supplied properties not correct for "OriginProperty"`. '+d},{property:"behaviours",refuseWhen:a(e=>Array.isArray(e.behaviours)&&e.behaviours.some(t=>I(t)&&t.originRef!==void 0),"refuseWhen"),reason:"the plan's `behaviours[].originRef` names another planned resource, while the construct's behaviour takes a live origin object; the plan shape throws `Cannot read properties of undefined (reading 'computeType')` at synth. "+d},n("customDomain","the distribution reads `domainNames`, so `--customDomain` deploys a distribution with no alias."),n("defaultOriginRef","`defaultOriginRef` names another planned resource for the scaffold to resolve into a live origin; no CDN construct reads it."),T],ae=[{property:"name",refuseWhen:u("name"),reason:"every pattern construct requires a `name` PROPERTY in addition to its construct id, and `fjall add --name` binds the construct id, so no invocation can deliver it \u2014 `toPascalCase(props.name)` throws at synth and takes the whole app down. "+d},{property:"type",refuseWhen:u("type"),reason:"`PatternFactory.build` reads `PATTERN_REGISTRY[props.type]` before it even returns the thunk, and the pattern vocabulary carries no `type` discriminator. "+d}],ne=[{property:"peerAppName",refuseWhen:u("peerAppName"),reason:"`peerAppName` is required: it is the SSM prefix every remote lookup is built from, and omitting it does NOT fail \u2014 synth succeeds and the app deploys a peering connection pointed at `/fjall/default/undefined/vpc-id`. Pass `--peerAppName <remote app>`."},{property:"peerAccountId",refuseWhen:u("peerAccountId"),reason:"`peerAccountId` is required: omitting it does NOT fail, it deploys a VPC peering connection with no `PeerOwnerId`. Pass `--peerAccountId <12-digit account>`."}];function o(e,t,s={}){return{factoryIdentifier:e,locator:J(e),generator:X(e),schemaFragment:t,...s}}a(o,"createEntry");const g={database:o(r.database,j,{emitWrapper:{appMethod:"addDatabase"},addContract:c(b)}),storage:o(r.storage,B,{emitWrapper:{appMethod:"addStorage"},addContract:c(P)}),compute:o(r.compute,L,{emitWrapper:{appMethod:"addCompute"},emitGuard:ee}),messaging:o(r.messaging,V,{emitWrapper:{appMethod:"addMessaging"},addContract:c(v)}),cdn:o(r.cdn,Y,{emitWrapper:{appMethod:"addCdn"},emitGuard:te}),network:o(r.network,q,{emitWrapper:{appMethod:"addNetwork"},addContract:c(C)}),pattern:o(r.pattern,K,{emitWrapper:{appMethod:"addPattern"},emitGuard:ae}),"vpc-peer":o(r["vpc-peer"],U,{emitWrapper:{appMethod:"addVpcPeer"},emitGuard:ne}),"vpc-peer-accepter":o(r["vpc-peer-accepter"],z,{emitWrapper:{appMethod:"addVpcPeerAccepter"},addContract:c(R)}),"cross-plan-connection":o(r["cross-plan-connection"],H,{addContract:c(f)}),organisation:o(r.organisation,Q,{addContract:c(S)}),buildkite:o(r.buildkite,$,{emitWrapper:{appMethod:"addBuildkite",propsOverloadConstructId:"Buildkite"},addContract:c(y)})},ye=Object.keys(g);function fe(e){const t=Object.keys(g);for(const s of t)if(g[s].factoryIdentifier===e)return s}a(fe,"findTypeByIdentifier");export{r as FACTORY_IDENTIFIERS,ye as REGISTERED_STATEMENT_TYPES,g as STATEMENT_REGISTRY,fe as findTypeByIdentifier};
1
+ var _=Object.defineProperty;var a=(e,t)=>_(e,"name",{value:t,configurable:!0});import{DEPLOYABLE_PATTERN_TYPES as w,PATTERN_TYPE_VALUES as x,isPatternType as M,patternConstructId as l}from"@fjall/util";import{z as d}from"zod";import{BuildkitePropsObjectSchema as h,CDNResourcePlanSchema as y,ComputeResourcePlanSchema as I,CrossPlanConnectionResourcePlanSchema as g,DatabaseResourcePlanSchema as f,NetworkResourcePlanSchema as S,NextJSPatternConfigSchema as O,OrganisationResourcePlanSchema as C,PayloadPatternConfigSchema as D,S3ResourcePlanSchema as P,SQSResourcePlanSchema as b,StaticSitePatternConfigSchema as W,VpcPeerAccepterResourcePlanSchema as E,VpcPeerResourcePlanSchema as L}from"../schemas/index.js";import{failure as j}from"../types/Result.js";import{requiresLiveValue as A,silentlyDropped as c,UNRESOLVED as G,whenAbsent as F}from"./emitGuard.js";const r={database:"DatabaseFactory",storage:"StorageFactory",compute:"ComputeFactory",messaging:"MessagingFactory",cdn:"CdnFactory",network:"NetworkFactory",pattern:"PatternFactory","vpc-peer":"VpcPeerFactory","vpc-peer-accepter":"VpcPeerAccepterFactory","cross-plan-connection":"CrossPlanConnectionFactory",organisation:"OrganisationFactory",buildkite:"BuildkiteFactory"};function s(e){const{name:t,...n}=e.shape;return d.object(n).partial().strict()}a(s,"fragmentOf");function i(e){const{name:t,...n}=e.shape;return d.object(n).strict()}a(i,"completeFragmentOf");const V=s(f),B=s(P),Y=(()=>{const{name:e,...t}=I.shape;return d.object(t).partial().strict()})(),q=s(b),K=s(y),U=s(S),T=(()=>{const{name:e,type:t,cdn:n,...k}=D.shape,{name:se,type:ie,cdn:de,...v}=O.shape,{name:pe,type:me,domain:ue,cdn:N,...R}=W.shape;return{...k,...v,...R,cdn:d.union([n,N]),type:d.enum(w)}})(),z=d.object(T).partial().strict(),Q=d.object(T).partial().required({type:!0}).strict(),$={emitConstructId:a((e,t)=>M(t.type)?l(e,t.type):e,"emitConstructId"),locateCandidates:a(e=>[e,...x.map(t=>l(e,t))],"locateCandidates")},J=s(L),H=s(E),X=s(g),Z=s(C),ee=s(h);function u(e,t){return j({kind:"SemanticQueryError",reason:`StatementTypeEntry.${e} for ${t} is not wired; existing types dispatch through the shared locator/generator.`})}a(u,"notWiredError");function te(e){return{factoryIdentifier:e,findByShape:a(()=>u("locator.findByShape",e),"findByShape"),validateContext:a(()=>u("locator.validateContext",e),"validateContext")}}a(te,"createLocator");function ae(e){return{build:a(()=>{const t=u("generator.build",e);throw new Error(t.success?"unreachable":t.error.reason)},"build")}}a(ae,"createGenerator");const ne=c("variableName","`variableName` is a plan-only round-trip key naming the `const <var> =` binding the scaffold emits; no construct reads it."),re=c("extraProperties","`extraProperties` is a plan-only round-trip key: the scaffold flattens each `{ key, sourceText }` entry into a sibling property, and no construct reads the array."),m="the connection keys are scaffold inputs, not construct props: the scaffold expands them into environment variables and a `connections: [...]` array, and the compute constructs read none of them.",oe=[{property:"deployment",refuseWhen:a(e=>(e.type==="lambda"||e.deployment!==void 0)&&e.deployment!=="container"&&e.code!==G,"refuseWhen"),reason:"a code-deployed Lambda requires `code: Code`, a CDK object built by `Code.fromAsset(...)` that no flag value can carry, so the statement takes the whole app's synth down with `Cannot read properties of undefined (reading 'bind')` \u2014 naming no resource. Use `--deployment container`, which is fully expressible, or write this compute in infrastructure.ts by hand."},c("codePath","`codePath` is the scaffold's input to `Code.fromAsset(...)`; no compute construct reads it, and a container Lambda takes its code from the image."),A("runtime","the construct expects a CDK `Runtime`, not a version string, and a container Lambda takes its runtime from the image (`Runtime.FROM_IMAGE`) regardless."),A("architecture","the construct expects a CDK `Architecture`, not a string: `--architecture ARM_64` emits an EMPTY `Architectures` list, whereas omitting the flag emits the correct default."),c("memory","the Lambda construct reads `memorySize`, so `--memory 1024` deploys a function at the 128 MB default."),c("ssmSecrets","the Lambda construct reads `secrets`, so `--ssmSecrets` deploys a function with no SSM wiring and no secrets environment at all."),c("scheduleExpression","`scheduleExpression` is not a compute prop: the scaffold turns it into a SEPARATE `app.addSchedule(...)` statement, which this command cannot emit."),c("needsConnection",m),c("connectedDatabase",m),c("connectedStorage",m),c("connectedMessaging",m),ne,re],ce=[{property:"peerAppName",refuseWhen:F("peerAppName"),reason:"`peerAppName` is required: it is the SSM prefix every remote lookup is built from, and omitting it does NOT fail \u2014 synth succeeds and the app deploys a peering connection pointed at `/fjall/default/undefined/vpc-id`. Pass `--peerAppName <remote app>`."},{property:"peerAccountId",refuseWhen:F("peerAccountId"),reason:"`peerAccountId` is required: omitting it does NOT fail, it deploys a VPC peering connection with no `PeerOwnerId`. Pass `--peerAccountId <12-digit account>`."}];function o(e,t,n={}){return{factoryIdentifier:e,locator:te(e),generator:ae(e),schemaFragment:t,...n}}a(o,"createEntry");const p={database:o(r.database,V,{emitWrapper:{appMethod:"addDatabase"},addContract:i(f)}),storage:o(r.storage,B,{emitWrapper:{appMethod:"addStorage"},addContract:i(P)}),compute:o(r.compute,Y,{emitWrapper:{appMethod:"addCompute"},emitGuard:oe}),messaging:o(r.messaging,q,{emitWrapper:{appMethod:"addMessaging"},addContract:i(b)}),cdn:o(r.cdn,K,{emitWrapper:{appMethod:"addCdn"},addContract:i(y)}),network:o(r.network,U,{emitWrapper:{appMethod:"addNetwork"},addContract:i(S)}),pattern:o(r.pattern,z,{emitWrapper:{appMethod:"addPattern"},addContract:Q,statementIdentity:$}),"vpc-peer":o(r["vpc-peer"],J,{emitWrapper:{appMethod:"addVpcPeer"},emitGuard:ce}),"vpc-peer-accepter":o(r["vpc-peer-accepter"],H,{emitWrapper:{appMethod:"addVpcPeerAccepter"},addContract:i(E)}),"cross-plan-connection":o(r["cross-plan-connection"],X,{addContract:i(g)}),organisation:o(r.organisation,Z,{addContract:i(C)}),buildkite:o(r.buildkite,ee,{emitWrapper:{appMethod:"addBuildkite",propsOverloadConstructId:"Buildkite"},addContract:i(h)})},Ce=Object.keys(p);function Pe(e,t,n){return p[e].statementIdentity?.emitConstructId(t,n)??t}a(Pe,"constructIdFor");function be(e,t){return p[e].statementIdentity?.locateCandidates(t)??[t]}a(be,"locateNamesFor");function Ee(e){const t=Object.keys(p);for(const n of t)if(p[n].factoryIdentifier===e)return n}a(Ee,"findTypeByIdentifier");export{r as FACTORY_IDENTIFIERS,Ce as REGISTERED_STATEMENT_TYPES,p as STATEMENT_REGISTRY,Pe as constructIdFor,Ee as findTypeByIdentifier,be as locateNamesFor};
@@ -5,10 +5,17 @@ export type { NodeLocation, StatementType } from "../types.js";
5
5
  export type LocateByShapeError = TemplateLiteralNameError | SemanticQueryError;
6
6
  /**
7
7
  * Locates a single `XFactory.build("Name", ...)` call matching the
8
- * supplied `{ type, name }` query. Returns `success(undefined)` when
9
- * the file contains no matching call; returns `failure(...)` only for
10
- * template-literal factory names (Joint 1 rejection) or a genuine
11
- * semantic-query error from ts-morph.
8
+ * supplied `{ type, name }` query. The name is matched against the
9
+ * registry's identity candidates (`locateNamesFor`) the raw name plus,
10
+ * for `statementIdentity` types, every scaffold-idiom suffixed form so
11
+ * `--name Site` addresses a hand-written `build("Site", …)` AND a
12
+ * scaffold-shaped `build("SiteStaticSite", …)`. Returns
13
+ * `success(undefined)` when the file contains no matching call; returns
14
+ * `failure(...)` for template-literal factory names (Joint 1 rejection),
15
+ * a genuine semantic-query error from ts-morph, or an AMBIGUOUS lookup —
16
+ * candidates matching statements under more than one construct id refuse
17
+ * rather than silently picking one. Repeated statements under ONE id keep
18
+ * the long-standing first-match resolution.
12
19
  */
13
20
  export declare function locateByShape(content: string, query: {
14
21
  type: StatementType;
@@ -1 +1 @@
1
- var h=Object.defineProperty;var c=(n,e)=>h(n,"name",{value:e,configurable:!0});import{Node as l,SyntaxKind as s}from"ts-morph";import{failure as u,success as d}from"../../types/Result.js";import{DEFAULT_FILE_PATH as p}from"../_internal.js";import{findTypeByIdentifier as E,STATEMENT_REGISTRY as S}from"../registry.js";import{getProject as T}from"./projectCache.js";function w(n,e,t=p){const a=A(n,t);if(!a.success)return a;const r=a.data.find(i=>i.type===e.type&&i.symbolName===e.name);return d(r)}c(w,"locateByShape");function A(n,e=p){const t=[],a=x(n,e,r=>{t.push({filePath:e,start:r.nameStart,length:r.nameLength,symbolName:r.name,type:r.type})},r=>{t.push({filePath:e,start:r.methodStart,length:r.methodLength,symbolName:r.name,type:r.type,managed:!1})});return a.success?d(t):a}c(A,"locateAllShapes");function x(n,e,t,a){let r;try{r=T().createSourceFile(e,n,{overwrite:!0})}catch(i){return u({kind:"SemanticQueryError",reason:"Failed to create source file",cause:i})}try{r.forEachDescendant(i=>{if(!l.isCallExpression(i))return;const o=N(i);if(o==="not-factory-call"){if(a!==void 0){const f=b(i);f!==void 0&&a(f,i)}return}if(o.kind!=="binding-pattern-declared"){if(o.kind==="template-literal-name")throw new y(i,o.type);t({type:o.type,name:o.name,nameStart:o.nameStart,nameLength:o.nameLength},i)}})}catch(i){return i instanceof y?u(i.toError(e)):u({kind:"SemanticQueryError",reason:"forEachDescendant traversal failed",cause:i})}return d(void 0)}c(x,"forEachFactoryMatch");function N(n){const e=n.getExpression();if(!l.isPropertyAccessExpression(e))return"not-factory-call";const t=m(e);if(t===void 0||g(n))return"not-factory-call";if(k(n))return{kind:"binding-pattern-declared"};const r=n.getArguments()[0];return r===void 0?"not-factory-call":l.isTemplateExpression(r)||l.isNoSubstitutionTemplateLiteral(r)?{kind:"template-literal-name",type:t}:l.isStringLiteral(r)?{kind:"match",type:t,name:r.getLiteralValue(),nameStart:r.getStart(),nameLength:r.getWidth()}:"not-factory-call"}c(N,"classifyFactoryCall");const L=new Map(Object.entries(S).flatMap(([n,e])=>{const t=e.emitWrapper?.propsOverloadConstructId;return e.emitWrapper===void 0||t===void 0?[]:[[e.emitWrapper.appMethod,{type:n,name:t}]]}));function b(n){const e=n.getExpression();if(!l.isPropertyAccessExpression(e))return;const t=L.get(e.getName());if(t===void 0||g(n))return;const a=n.getArguments()[0];if(a!==void 0&&l.isCallExpression(a)){const i=a.getExpression();if(l.isPropertyAccessExpression(i)&&m(i)!==void 0)return}const r=e.getNameNode();return{type:t.type,name:t.name,methodStart:r.getStart(),methodLength:r.getWidth()}}c(b,"classifyUnmanagedMethodCall");function m(n){const e=n.getExpression();if(l.isIdentifier(e)&&n.getName()==="build")return E(e.getText())}c(m,"factoryTypeFromPropertyAccess");function g(n){let e=n.getParent();for(;e!==void 0;){const t=e.getKind();if(t===s.TypeReference||t===s.TypeQuery||t===s.TypeLiteral||t===s.TypeAliasDeclaration||t===s.InterfaceDeclaration||t===s.TypeParameter||t===s.PropertySignature||t===s.MethodSignature||t===s.JsxOpeningElement||t===s.JsxClosingElement||t===s.JsxSelfClosingElement||t===s.JsxAttribute)return!0;e=e.getParent()}return!1}c(g,"isInsideTypeSpace");function k(n){const e=n.getFirstAncestorByKind(s.VariableDeclaration);if(e===void 0)return!1;const a=e.getNameNode().getKind();return a===s.ObjectBindingPattern||a===s.ArrayBindingPattern}c(k,"isInsideBindingPattern");class y extends Error{static{c(this,"TemplateLiteralMarker")}call;type;constructor(e,t){super("Template-literal factory name detected"),this.call=e,this.type=t,this.name="TemplateLiteralMarker"}toError(e){const a=this.call.getArguments()[0]?.getStart()??this.call.getStart(),r=this.call.getSourceFile(),{line:i,column:o}=r.getLineAndColumnAtPos(a);return{kind:"TemplateLiteralNameError",file:e,line:i,column:o,suggestion:"Replace the template literal with a plain string literal so the resource name can be resolved statically."}}}export{x as forEachFactoryMatch,A as locateAllShapes,w as locateByShape};
1
+ var S=Object.defineProperty;var c=(r,e)=>S(r,"name",{value:e,configurable:!0});import{Node as d,SyntaxKind as s}from"ts-morph";import{failure as u,success as f}from"../../types/Result.js";import{DEFAULT_FILE_PATH as m}from"../_internal.js";import{findTypeByIdentifier as T,locateNamesFor as x,STATEMENT_REGISTRY as A}from"../registry.js";import{getProject as N}from"./projectCache.js";function $(r,e,t=m){const a=w(r,t);if(!a.success)return a;const n=x(e.type,e.name),i=a.data.filter(l=>l.type===e.type&&n.includes(l.symbolName)),o=[...new Set(i.map(l=>l.symbolName))];if(o.length>1){const h=o.includes(e.name)?`The raw id "${e.name}" is shadowed by its own suffixed twin, so no \`--name\` query can single it out \u2014 rename one of the two statements by hand in infrastructure.ts to disambiguate.`:"Rename or remove one of them, or target the exact construct id.";return u({kind:"SemanticQueryError",reason:`"${e.name}" matches more than one ${e.type} statement in this file (${o.map(E=>`"${E}"`).join(", ")}); the engine will not pick one. ${h}`})}return f(i[0])}c($,"locateByShape");function w(r,e=m){const t=[],a=b(r,e,n=>{t.push({filePath:e,start:n.nameStart,length:n.nameLength,symbolName:n.name,type:n.type})},n=>{t.push({filePath:e,start:n.methodStart,length:n.methodLength,symbolName:n.name,type:n.type,managed:!1})});return a.success?f(t):a}c(w,"locateAllShapes");function b(r,e,t,a){let n;try{n=N().createSourceFile(e,r,{overwrite:!0})}catch(i){return u({kind:"SemanticQueryError",reason:"Failed to create source file",cause:i})}try{n.forEachDescendant(i=>{if(!d.isCallExpression(i))return;const o=L(i);if(o==="not-factory-call"){if(a!==void 0){const l=F(i);l!==void 0&&a(l,i)}return}if(o.kind!=="binding-pattern-declared"){if(o.kind==="template-literal-name")throw new y(i,o.type);t({type:o.type,name:o.name,nameStart:o.nameStart,nameLength:o.nameLength},i)}})}catch(i){return i instanceof y?u(i.toError(e)):u({kind:"SemanticQueryError",reason:"forEachDescendant traversal failed",cause:i})}return f(void 0)}c(b,"forEachFactoryMatch");function L(r){const e=r.getExpression();if(!d.isPropertyAccessExpression(e))return"not-factory-call";const t=p(e);if(t===void 0||g(r))return"not-factory-call";if(I(r))return{kind:"binding-pattern-declared"};const n=r.getArguments()[0];return n===void 0?"not-factory-call":d.isTemplateExpression(n)||d.isNoSubstitutionTemplateLiteral(n)?{kind:"template-literal-name",type:t}:d.isStringLiteral(n)?{kind:"match",type:t,name:n.getLiteralValue(),nameStart:n.getStart(),nameLength:n.getWidth()}:"not-factory-call"}c(L,"classifyFactoryCall");const k=new Map(Object.entries(A).flatMap(([r,e])=>{const t=e.emitWrapper?.propsOverloadConstructId;return e.emitWrapper===void 0||t===void 0?[]:[[e.emitWrapper.appMethod,{type:r,name:t}]]}));function F(r){const e=r.getExpression();if(!d.isPropertyAccessExpression(e))return;const t=k.get(e.getName());if(t===void 0||g(r))return;const a=r.getArguments()[0];if(a!==void 0&&d.isCallExpression(a)){const i=a.getExpression();if(d.isPropertyAccessExpression(i)&&p(i)!==void 0)return}const n=e.getNameNode();return{type:t.type,name:t.name,methodStart:n.getStart(),methodLength:n.getWidth()}}c(F,"classifyUnmanagedMethodCall");function p(r){const e=r.getExpression();if(d.isIdentifier(e)&&r.getName()==="build")return T(e.getText())}c(p,"factoryTypeFromPropertyAccess");function g(r){let e=r.getParent();for(;e!==void 0;){const t=e.getKind();if(t===s.TypeReference||t===s.TypeQuery||t===s.TypeLiteral||t===s.TypeAliasDeclaration||t===s.InterfaceDeclaration||t===s.TypeParameter||t===s.PropertySignature||t===s.MethodSignature||t===s.JsxOpeningElement||t===s.JsxClosingElement||t===s.JsxSelfClosingElement||t===s.JsxAttribute)return!0;e=e.getParent()}return!1}c(g,"isInsideTypeSpace");function I(r){const e=r.getFirstAncestorByKind(s.VariableDeclaration);if(e===void 0)return!1;const a=e.getNameNode().getKind();return a===s.ObjectBindingPattern||a===s.ArrayBindingPattern}c(I,"isInsideBindingPattern");class y extends Error{static{c(this,"TemplateLiteralMarker")}call;type;constructor(e,t){super("Template-literal factory name detected"),this.call=e,this.type=t,this.name="TemplateLiteralMarker"}toError(e){const a=this.call.getArguments()[0]?.getStart()??this.call.getStart(),n=this.call.getSourceFile(),{line:i,column:o}=n.getLineAndColumnAtPos(a);return{kind:"TemplateLiteralNameError",file:e,line:i,column:o,suggestion:"Replace the template literal with a plain string literal so the resource name can be resolved statically."}}}export{b as forEachFactoryMatch,w as locateAllShapes,$ as locateByShape};
@@ -0,0 +1,2 @@
1
+ import type { Gate } from "../types.js";
2
+ export declare const projectionGate: Gate;
@@ -0,0 +1 @@
1
+ var h=Object.defineProperty;var c=(n,t)=>h(n,"name",{value:t,configurable:!0});import{failure as u,success as a}from"../../../types/Result.js";import{isProjectableType as v,TYPE_PROJECTIONS as b}from"../../../projection/index.js";import{extractProgramBody as g}from"../../_internal.js";import{findConfigObject as j}from"../../drift/snapshot.js";import{astToLiteral as N,isNodeShape as P,NON_LITERAL as O,readKeyName as y}from"../../edits/modifyResource/literalConversion.js";import{parse as T}from"../../fileRewriter/parse.js";import{constructIdFor as $,STATEMENT_REGISTRY as k}from"../../registry.js";import{locateByShape as E}from"../../semanticIndex/locateByShape.js";function S(n){if(!v(n))return[];const t=b[n].keys??{};return Object.entries(t).flatMap(([e,r])=>r.kind==="live"?[{planKey:e,constructKey:r.as??e,reason:r.reason}]:[])}c(S,"liveConstructKeys");function m(n){return a({action:"reject",diagnostics:n})}c(m,"rejectOutcome");const q={id:"projection",run(n){const{type:t,name:e,op:r}=n.plan,o=S(t),f=r==="add"&&k[t].statementIdentity!==void 0;if(o.length===0&&!f)return a({action:"proceed"});const s=I(n);if(!s.success)return s;if(s.data===void 0)return m(`could not resolve the emitted config object for ${t} "${e}"; the output cannot be verified against the projection contract`);const{symbolName:p,configObject:l}=s.data;for(const d of o){const i=K(l,d.constructKey);if(i!==void 0&&(i.type==="StringLiteral"||i.type==="TemplateLiteral"))return u({kind:"InvalidPropertyError",property:d.constructKey,reason:`the ${t} construct reads a live binding here and the output wrote a ${i.type==="StringLiteral"?"string":"template"} literal; ${d.reason}`})}if(f){const d=L(l),i=$(t,e,d);if(p!==i)return m(`output writes construct id "${p}" where the ${t} identity derivation for "${e}" requires "${i}" \u2014 an id the deterministic lane never emits (the type declares an identity precisely because non-derived ids collide or go unaddressable at synth)`)}return a({action:"proceed"})}};function I(n){const t=E(n.content,{type:n.plan.type,name:n.plan.name});if(!t.success)return u(t.error);if(t.data===void 0)return a(void 0);const e=T(n.content,t.data.filePath);if(!e.success)return u(e.error);const r=g(e.data);if(r===void 0)return a(void 0);const o=j(r,{start:t.data.start,length:t.data.length});return o===void 0?a(void 0):a({symbolName:t.data.symbolName,configObject:o})}c(I,"locateEmittedStatement");function K(n,t){for(const e of n.properties)if(!(e.type!=="Property"&&e.type!=="ObjectProperty")&&!(e.shorthand===!0||e.computed===!0)&&y(e.key)===t&&e.value!==void 0&&P(e.value))return e.value}c(K,"readPropertyValueNode");function L(n){const t={};for(const e of n.properties){if(e.type!=="Property"&&e.type!=="ObjectProperty"||e.shorthand===!0||e.computed===!0)continue;const r=y(e.key);if(r===void 0||e.value===void 0)continue;const o=N(e.value);o!==O&&(t[r]=o)}return t}c(L,"literalPropertiesOf");export{q as projectionGate};
@@ -4,3 +4,4 @@ export { schemaGate } from "./gates/schema.js";
4
4
  export { parseGate } from "./gates/parse.js";
5
5
  export { createLocateGate, locateGate, type LocateGateOptions, type LocateMode, } from "./gates/locate.js";
6
6
  export { classifyGate } from "./gates/classify.js";
7
+ export { projectionGate } from "./gates/projection.js";
@@ -1 +1 @@
1
- import{runPipeline as t,runPipelineAsync as o}from"./types.js";import{driftGate as p}from"./gates/drift.js";import{schemaGate as m}from"./gates/schema.js";import{parseGate as i}from"./gates/parse.js";import{createLocateGate as G,locateGate as n}from"./gates/locate.js";import{classifyGate as l}from"./gates/classify.js";export{l as classifyGate,G as createLocateGate,p as driftGate,n as locateGate,i as parseGate,t as runPipeline,o as runPipelineAsync,m as schemaGate};
1
+ import{runPipeline as t,runPipelineAsync as o}from"./types.js";import{driftGate as p}from"./gates/drift.js";import{schemaGate as m}from"./gates/schema.js";import{parseGate as i}from"./gates/parse.js";import{createLocateGate as G,locateGate as n}from"./gates/locate.js";import{classifyGate as l}from"./gates/classify.js";import{projectionGate as y}from"./gates/projection.js";export{l as classifyGate,G as createLocateGate,p as driftGate,n as locateGate,i as parseGate,y as projectionGate,t as runPipeline,o as runPipelineAsync,m as schemaGate};
@@ -1,7 +1,7 @@
1
1
  import { type Result } from "../../types/Result.js";
2
2
  import type { DriftOp, DriftPolicy, DriftState, ResourceSnapshot } from "../drift/index.js";
3
3
  import type { CodemodError, ResourceName, StatementType } from "../types.js";
4
- export type GateId = "parse" | "tsc" | "classify" | "locate" | "schema" | "drift" | "runtime";
4
+ export type GateId = "parse" | "tsc" | "classify" | "locate" | "projection" | "schema" | "drift" | "runtime";
5
5
  export type GateAction = "proceed" | "skip" | "reject";
6
6
  export interface GateContext {
7
7
  content: string;
@@ -1,34 +1,34 @@
1
- var f=Object.defineProperty;var r=(n,e)=>f(n,"name",{value:e,configurable:!0});import{toPascalCase as $,formatValue as l}from"./common.js";import{PATTERN_REGISTRY as b}from"@fjall/util";const m=Object.freeze({database:{type:"Instance",backupRetention:7,deletionProtection:!0},compute:{server:{memorySize:1536,timeout:30},imageOptimisation:{memorySize:1536,timeout:30},revalidation:{memorySize:768,timeout:300}}});function c(n,e,t){return e===void 0?"":`
1
+ var f=Object.defineProperty;var r=(n,e)=>f(n,"name",{value:e,configurable:!0});import{formatValue as l}from"./common.js";import{patternConstructId as $}from"@fjall/util";const m=Object.freeze({database:{type:"Instance",backupRetention:7,deletionProtection:!0},compute:{server:{memorySize:1536,timeout:30},imageOptimisation:{memorySize:1536,timeout:30},revalidation:{memorySize:768,timeout:300}}});function c(n,e,t){return e===void 0?"":`
2
2
  ${t}${n}: ${e},`}r(c,"emitScalar");function s(n,e,t){return e===void 0?"":`
3
- ${t}${n}: ${JSON.stringify(e)},`}r(s,"emitStringField");function u(n,e,t){return e===void 0?"":e===!1?`
3
+ ${t}${n}: ${JSON.stringify(e)},`}r(s,"emitStringField");function d(n,e,t){return e===void 0?"":e===!1?`
4
4
  ${t}${n}: false,`:`
5
- ${t}${n}: ${l(e,t)},`}r(u,"emitObjectOrFalse");function a(n,e,t){return e===void 0||Object.keys(e).length===0?"":`
6
- ${t}${n}: ${l(e,t)},`}r(a,"emitObject");function g(n){const e=n??{},t=" ",d=e.type??m.database.type,o=e.backupRetention??m.database.backupRetention,p=e.deletionProtection??m.database.deletionProtection;let i=`
7
- ${t}type: "${d}",`;return i+=s("databaseName",e.databaseName,t),i+=s("databaseEngine",e.databaseEngine,t),d==="Instance"&&e.instanceType!==void 0&&(i+=s("instanceType",e.instanceType,t)),i+=c("allocatedStorage",e.allocatedStorage,t),i+=c("port",e.port,t),i+=`
8
- ${t}backupRetention: ${o},`,i+=`
9
- ${t}deletionProtection: ${p},`,i+=c("publiclyAccessible",e.publiclyAccessible,t),i+=s("allowedIpCidr",e.allowedIpCidr,t),i+=c("multiAz",e.multiAz,t),i+=c("allowVpcAccess",e.allowVpcAccess,t),i+=c("monitoringInterval",e.monitoringInterval,t),i+=s("preferredMaintenanceWindow",e.preferredMaintenanceWindow,t),i+=s("snapshotIdentifier",e.snapshotIdentifier,t),i+=s("snapshotUsername",e.snapshotUsername,t),i+=u("readReplica",e.readReplica,t),i+=a("writer",e.writer,t),i+=u("readers",e.readers,t),i+=u("databaseInsights",e.databaseInsights,t),i+=u("proxy",e.proxy,t),i+=a("credentials",e.credentials,t),i+=a("encryption",e.encryption,t),`
5
+ ${t}${n}: ${l(e,t)},`}r(d,"emitObjectOrFalse");function a(n,e,t){return e===void 0||Object.keys(e).length===0?"":`
6
+ ${t}${n}: ${l(e,t)},`}r(a,"emitObject");function b(n){const e=n??{},t=" ",o=e.type??m.database.type,u=e.backupRetention??m.database.backupRetention,p=e.deletionProtection??m.database.deletionProtection;let i=`
7
+ ${t}type: "${o}",`;return i+=s("databaseName",e.databaseName,t),i+=s("databaseEngine",e.databaseEngine,t),o==="Instance"&&e.instanceType!==void 0&&(i+=s("instanceType",e.instanceType,t)),i+=c("allocatedStorage",e.allocatedStorage,t),i+=c("port",e.port,t),i+=`
8
+ ${t}backupRetention: ${u},`,i+=`
9
+ ${t}deletionProtection: ${p},`,i+=c("publiclyAccessible",e.publiclyAccessible,t),i+=s("allowedIpCidr",e.allowedIpCidr,t),i+=c("multiAz",e.multiAz,t),i+=c("allowVpcAccess",e.allowVpcAccess,t),i+=c("monitoringInterval",e.monitoringInterval,t),i+=s("preferredMaintenanceWindow",e.preferredMaintenanceWindow,t),i+=s("snapshotIdentifier",e.snapshotIdentifier,t),i+=s("snapshotUsername",e.snapshotUsername,t),i+=d("readReplica",e.readReplica,t),i+=a("writer",e.writer,t),i+=d("readers",e.readers,t),i+=d("databaseInsights",e.databaseInsights,t),i+=d("proxy",e.proxy,t),i+=a("credentials",e.credentials,t),i+=a("encryption",e.encryption,t),`
10
10
  database: {${i}
11
- },`}r(g,"emitDatabaseBlock");function S(n){const e=n??{},t=e.server?.memorySize??m.compute.server.memorySize,d=e.server?.timeout??m.compute.server.timeout,o=e.imageOptimisation?.memorySize??m.compute.imageOptimisation.memorySize,p=e.imageOptimisation?.timeout??m.compute.imageOptimisation.timeout,i=e.revalidation?.memorySize??m.compute.revalidation.memorySize,y=e.revalidation?.timeout??m.compute.revalidation.timeout;return`
11
+ },`}r(b,"emitDatabaseBlock");function g(n){const e=n??{},t=e.server?.memorySize??m.compute.server.memorySize,o=e.server?.timeout??m.compute.server.timeout,u=e.imageOptimisation?.memorySize??m.compute.imageOptimisation.memorySize,p=e.imageOptimisation?.timeout??m.compute.imageOptimisation.timeout,i=e.revalidation?.memorySize??m.compute.revalidation.memorySize,y=e.revalidation?.timeout??m.compute.revalidation.timeout;return`
12
12
  compute: {
13
13
  server: {
14
14
  memorySize: ${t},
15
- timeout: ${d},
15
+ timeout: ${o},
16
16
  },
17
17
  imageOptimisation: {
18
- memorySize: ${o},
18
+ memorySize: ${u},
19
19
  timeout: ${p},
20
20
  },
21
21
  revalidation: {
22
22
  memorySize: ${i},
23
23
  timeout: ${y},
24
24
  },
25
- },`}r(S,"emitComputeBlock");function v(n){return a("storage",n," ")}r(v,"emitStorageBlock");function z(n){return a("messaging",n," ")}r(z,"emitMessagingBlock");function I(n){return a("cdn",n," ")}r(I,"emitCdnBlock");function O(n){return a("environment",n," ")}r(O,"emitEnvironmentBlock");function T(n){const e=" ";let t="";return t+=s("source",n.source,e),t+=a("build",n.build,e),t+=s("routing",n.routing,e),t+=a("security",n.security,e),n.domain!==void 0&&n.domain!==""&&(t+=`
26
- ${e}domain: ${JSON.stringify(n.domain)},`),t+=a("forms",n.forms,e),t+=a("cdn",n.cdn,e),t}r(T,"emitStaticSiteFields");function C(n){if(!n.patternConfig)return"";const e=n.patternConfig;let o=`app.addPattern(
27
- PatternFactory.build("${`${$(e.name)}${b[e.type].constructIdSuffix}`}", {
25
+ },`}r(g,"emitComputeBlock");function S(n){return a("storage",n," ")}r(S,"emitStorageBlock");function v(n){return a("messaging",n," ")}r(v,"emitMessagingBlock");function z(n){return a("cdn",n," ")}r(z,"emitCdnBlock");function O(n){return a("environment",n," ")}r(O,"emitEnvironmentBlock");function I(n){const e=" ";let t="";return t+=s("source",n.source,e),t+=a("build",n.build,e),t+=s("routing",n.routing,e),t+=a("security",n.security,e),n.domain!==void 0&&n.domain!==""&&(t+=`
26
+ ${e}domain: ${JSON.stringify(n.domain)},`),t+=a("forms",n.forms,e),t+=a("cdn",n.cdn,e),t}r(I,"emitStaticSiteFields");function T(n){if(!n.patternConfig)return"";const e=n.patternConfig;let o=`app.addPattern(
27
+ PatternFactory.build("${$(e.name,e.type)}", {
28
28
  type: "${e.type}",
29
29
  name: "${e.name}",`;return e.type==="payload"||e.type==="nextjs"?(e.domain!==void 0&&e.domain!==""&&(o+=`
30
- domain: "${e.domain}",`),o+=g(e.database),o+=S(e.compute),o+=v(e.storage),o+=z(e.messaging),o+=I(e.cdn),o+=O(e.environment)):o+=T(e),o+=`
30
+ domain: "${e.domain}",`),o+=b(e.database),o+=g(e.compute),o+=S(e.storage),o+=v(e.messaging),o+=z(e.cdn),o+=O(e.environment)):o+=I(e),o+=`
31
31
  })
32
32
  );
33
33
 
34
- `,o}r(C,"generatePatternCodeWithComments");export{m as OPENNEXT_DEFAULTS,C as generatePatternCodeWithComments};
34
+ `,o}r(T,"generatePatternCodeWithComments");export{m as OPENNEXT_DEFAULTS,T as generatePatternCodeWithComments};
@@ -1,3 +1,3 @@
1
1
  export { projectPlanToConstructProps } from "./project.js";
2
2
  export { isProjectableType, TYPE_PROJECTIONS, type ProjectableType, } from "./tables.js";
3
- export type { PlanKeyRule, Projection, ProjectionContext, ProjectedProp, ProjectedValue, TypeProjection, UnprojectableProp, } from "./types.js";
3
+ export type { PlanKeyRule, Projection, ProjectionContext, ProjectedProp, ProjectedValue, TypeProjection, UnprojectableKind, UnprojectableProp, } from "./types.js";
@@ -1 +1 @@
1
- var k=Object.defineProperty;var f=(o,s)=>k(o,"name",{value:s,configurable:!0});import{TYPE_PROJECTIONS as y}from"./tables.js";function O(o,s,r={}){const u=y[o],l=r.editingExisting===!0?[]:u.constants??[],t=new Map(l.map(n=>[n.key,n])),i=[];for(const[n,c]of Object.entries(s)){if(c===void 0)continue;const e=u.keys?.[n];if(e===void 0||e.kind==="prop"){const d=e?.as??n;t.set(d,{key:d,value:{kind:"literal",value:c}});continue}if(e.kind==="planOnly"){i.push({key:n,reason:e.reason});continue}const a=e.bind(c,r);if(a===void 0){i.push({key:n,reason:e.reason});continue}const p=e.as??n;t.set(p,{key:p,value:{kind:"expression",source:a}})}return{props:[...t.values()],unprojectable:i}}f(O,"projectPlanToConstructProps");export{O as projectPlanToConstructProps};
1
+ var k=Object.defineProperty;var f=(r,u)=>k(r,"name",{value:u,configurable:!0});import{TYPE_PROJECTIONS as y}from"./tables.js";function b(r,u,a={}){const o=y[r],i=[];let p=[];if(a.editingExisting!==!0)if(typeof o.constants=="function"){const n=o.constants(a);p=n.props,i.push(...n.unprojectable.map(s=>({...s,kind:s.kind??"missing-context"})))}else p=o.constants??[];const c=new Map(p.map(n=>[n.key,n]));for(const[n,s]of Object.entries(u)){if(s===void 0)continue;const e=o.keys?.[n];if(e===void 0||e.kind==="prop"){const t=e?.as??n;c.set(t,{key:t,value:{kind:"literal",value:s}});continue}if(e.kind==="transform"){const t=e.as??n;c.set(t,{key:t,value:{kind:"literal",value:e.apply(s)}});continue}if(e.kind==="planOnly"){i.push({key:n,reason:e.reason,kind:"plan-only"});continue}const d=e.bind(s,a);if(d===void 0){i.push({key:n,reason:e.reason,kind:"unresolved-binding"});continue}const l=e.as??n;c.set(l,{key:l,value:{kind:"expression",source:d}})}return{props:[...c.values()],unprojectable:i}}f(b,"projectPlanToConstructProps");export{b as projectPlanToConstructProps};
@@ -12,14 +12,13 @@
12
12
  * `extraProperties` (round-trip preservation the scaffold flattens into
13
13
  * sibling properties). Neither is a construct prop anywhere.
14
14
  *
15
- * `compute`, `cdn`, `pattern` and `vpc-peer` are deliberately ABSENT. Their
16
- * disagreements are not renames: a CDN origin needs the live `IBucket` or
17
- * load balancer its sibling resource became, and a compute's four connection
18
- * keys expand into environment variables plus a whole second statement
19
- * (`app.addSchedule(...)`). Both need a projection that can carry bindings
20
- * and emit more than one statement, which is the next increment; until then
21
- * they keep their `emitGuard` tables and a type is projected or guarded,
22
- * never both.
15
+ * `compute` and `vpc-peer` are deliberately ABSENT. A compute's four
16
+ * connection keys expand into environment variables plus a whole second
17
+ * statement (`app.addSchedule(...)`) more than one statement is beyond
18
+ * what a projection can emit, so it keeps its `emitGuard` table and a type
19
+ * is projected or guarded, never both. `vpc-peer`'s rules are required-key
20
+ * conditions whose absence deploys silently wrong infrastructure guard
21
+ * territory, not vocabulary.
23
22
  */
24
23
  import type { TypeProjection } from "./types.js";
25
24
  export declare const TYPE_PROJECTIONS: {
@@ -27,6 +26,8 @@ export declare const TYPE_PROJECTIONS: {
27
26
  readonly storage: TypeProjection;
28
27
  readonly database: TypeProjection;
29
28
  readonly network: TypeProjection;
29
+ readonly pattern: TypeProjection;
30
+ readonly cdn: TypeProjection;
30
31
  };
31
32
  export type ProjectableType = keyof typeof TYPE_PROJECTIONS;
32
33
  export declare function isProjectableType(value: string): value is ProjectableType;
@@ -1 +1 @@
1
- var s=Object.defineProperty;var a=(n,t)=>s(n,"name",{value:t,configurable:!0});const r={kind:"planOnly",reason:"`variableName` is a plan-only round-trip key naming the `const <var> =` binding the scaffold emits; no construct reads it."},o={kind:"planOnly",reason:"`extraProperties` is a plan-only round-trip key: the scaffold flattens each `{ key, sourceText }` entry into a sibling property, and no construct reads the array."},e={variableName:r,extraProperties:o},i={constants:[{key:"type",value:{kind:"literal",value:"queue"}}],keys:{retentionPeriod:{kind:"prop",as:"messageRetentionPeriod"},...e}},l={keys:{...e}},p={keys:{engineExpression:{kind:"planOnly",reason:"`engineExpression` carries a raw CDK engine expression for the scaffold to inline; the construct reads `databaseEngine` instead, so a cluster pinned to `DatabaseClusterEngine.auroraMysql(...)` still deploys as aurora-postgresql."},...e}},c={keys:{...e}},y={messaging:i,storage:l,database:p,network:c};function u(n){return Object.prototype.hasOwnProperty.call(y,n)}a(u,"isProjectableType");export{y as TYPE_PROJECTIONS,u as isProjectableType};
1
+ var s=Object.defineProperty;var n=(e,r)=>s(e,"name",{value:r,configurable:!0});import{toKebab as i}from"@fjall/util";const o={kind:"planOnly",reason:"`variableName` is a plan-only round-trip key naming the `const <var> =` binding the scaffold emits; no construct reads it."},t={kind:"planOnly",reason:"`extraProperties` is a plan-only round-trip key: the scaffold flattens each `{ key, sourceText }` entry into a sibling property, and no construct reads the array."},a={variableName:o,extraProperties:t},l={constants:[{key:"type",value:{kind:"literal",value:"queue"}}],keys:{retentionPeriod:{kind:"prop",as:"messageRetentionPeriod"},...a}},d={keys:{...a}},p={keys:{engineExpression:{kind:"planOnly",reason:"`engineExpression` carries a raw CDK engine expression for the scaffold to inline; the construct reads `databaseEngine` instead, so a cluster pinned to `DatabaseClusterEngine.auroraMysql(...)` still deploys as aurora-postgresql."},...a}},c={keys:{...a}},u={constants:n(e=>e.resourceName!==void 0?{props:[{key:"name",value:{kind:"literal",value:i(e.resourceName)}}],unprojectable:[]}:{props:[],unprojectable:[{key:"name",reason:"every pattern construct reads a `name` prop (`toPascalCase(props.name)` names its child constructs), and this caller supplied no resource name to derive it from."}]},"constants")},y={constants:[{key:"originType",value:{kind:"literal",value:"auto"}}],keys:{defaultOriginRef:{kind:"live",as:"origin",reason:"`defaultOriginRef` names a planned sibling resource and the construct reads a live `origin` binding in its place; no unique storage or compute binding for that name could be resolved in this file, and writing the name as a string would deploy a distribution treating it as an HTTP origin domain.",bind:n((e,r)=>typeof e=="string"?r.resolveBinding?.(e):void 0,"bind")},customDomain:{kind:"transform",as:"domainNames",apply:n(e=>[e],"apply")},behaviours:{kind:"planOnly",reason:"each `behaviours[].originRef` names a live origin PER ELEMENT, and a single-value binding cannot reach inside an array literal; the plan shape throws `Cannot read properties of undefined (reading 'computeType')` at synth."},extraProperties:t}},g={messaging:l,storage:d,database:p,network:c,pattern:u,cdn:y};function b(e){return Object.prototype.hasOwnProperty.call(g,e)}n(b,"isProjectableType");export{g as TYPE_PROJECTIONS,b as isProjectableType};
@@ -35,15 +35,34 @@ export interface ProjectedProp {
35
35
  key: string;
36
36
  value: ProjectedValue;
37
37
  }
38
+ /**
39
+ * Why a plan key failed to project — three distinct classes with three
40
+ * distinct consequences, which is why the refusal composer needs to know:
41
+ *
42
+ * - `plan-only` — the key is NEVER a construct prop; writing it would put an
43
+ * inert key in the file and the resource would deploy without the asked-for
44
+ * behaviour.
45
+ * - `unresolved-binding` — the key IS projectable in principle (a live rule
46
+ * exists) but no unique live binding resolved in this file, so the
47
+ * statement is refused and nothing is written at all.
48
+ * - `missing-context` — a constants factory needed something the command
49
+ * context did not carry (the resource name); again nothing is written.
50
+ */
51
+ export type UnprojectableKind = "plan-only" | "unresolved-binding" | "missing-context";
38
52
  /**
39
53
  * A plan key this context cannot turn into a construct prop, and why. The
40
54
  * scaffold ignores these — it consumes such keys through its own paths. The
41
55
  * codemod refuses the whole statement, because writing the key would either
42
56
  * take synth down or deploy something other than what was asked for.
57
+ *
58
+ * `kind` is stamped at each construction site in `project.ts`; absent means
59
+ * `plan-only` (the original class, kept as the default so table-authored
60
+ * entries need not restate it).
43
61
  */
44
62
  export interface UnprojectableProp {
45
63
  key: string;
46
64
  reason: string;
65
+ kind?: UnprojectableKind;
47
66
  }
48
67
  export interface Projection {
49
68
  props: ProjectedProp[];
@@ -59,6 +78,14 @@ export interface Projection {
59
78
  */
60
79
  export interface ProjectionContext {
61
80
  resolveBinding?: (planResourceName: string) => string | undefined;
81
+ /**
82
+ * The name of the resource the statement is for, when the caller has one
83
+ * (`fjall add` always does — it is the factory's first argument). A
84
+ * constants factory may derive a prop from it; a factory that needs it and
85
+ * finds it absent must report the prop unprojectable rather than emit a
86
+ * statement without it.
87
+ */
88
+ resourceName?: string;
62
89
  /**
63
90
  * The fragment is an EDIT to an existing statement rather than a whole
64
91
  * resource. Constants describe the statement, so an edit must not carry
@@ -72,6 +99,10 @@ export interface ProjectionContext {
72
99
  *
73
100
  * - `prop` — carried through as a literal, under `as` when the two
74
101
  * vocabularies name it differently.
102
+ * - `transform` — carried through as a literal, but reshaped by `apply` on
103
+ * the way: the vocabularies disagree about the value's shape, not just the
104
+ * key's name (a plan `customDomain: string` is the construct's
105
+ * `domainNames: string[]`).
75
106
  * - `planOnly` — never a construct prop. The scaffold consumes it (a
76
107
  * `const <var> =` binding, flattened extra properties); anywhere else it is
77
108
  * inert, so it is reported rather than silently dropped.
@@ -82,6 +113,10 @@ export interface ProjectionContext {
82
113
  export type PlanKeyRule = {
83
114
  kind: "prop";
84
115
  as?: string;
116
+ } | {
117
+ kind: "transform";
118
+ as?: string;
119
+ apply: (value: unknown) => unknown;
85
120
  } | {
86
121
  kind: "planOnly";
87
122
  reason: string;
@@ -96,13 +131,17 @@ export type PlanKeyRule = {
96
131
  *
97
132
  * `constants` are props every statement of the type carries no matter what
98
133
  * the plan says — the discriminators the constructs switch on and the plan
99
- * vocabulary never carried.
134
+ * vocabulary never carried. The static form is a fixed list; the factory
135
+ * form derives them from the context (a pattern's `name` prop is the
136
+ * resource name). A factory that cannot derive a constant from the context
137
+ * it was given reports it in `unprojectable` — refusing the statement is the
138
+ * fail-closed alternative to emitting one that throws at synth.
100
139
  *
101
140
  * `keys` is consulted per plan key; a key with no entry passes through under
102
141
  * its own name, which is the common case and is why the tables stay short
103
142
  * enough to read as the specification they are.
104
143
  */
105
144
  export interface TypeProjection {
106
- constants?: ProjectedProp[];
145
+ constants?: ProjectedProp[] | ((context: ProjectionContext) => Projection);
107
146
  keys?: Record<string, PlanKeyRule>;
108
147
  }
@@ -1 +1 @@
1
- export declare const GENERATOR_VERSION = "13.1.0";
1
+ export declare const GENERATOR_VERSION = "14.1.0";
@@ -1 +1 @@
1
- const E="13.1.0";export{E as GENERATOR_VERSION};
1
+ const E="14.1.0";export{E as GENERATOR_VERSION};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fjall/generator",
3
- "version": "13.1.0",
3
+ "version": "14.1.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/fjall-tech/fjall.git",
@@ -57,7 +57,7 @@
57
57
  },
58
58
  "license": "SEE LICENSE IN LICENSE",
59
59
  "dependencies": {
60
- "@fjall/util": "^13.1.0",
60
+ "@fjall/util": "^14.1.0",
61
61
  "ast-types": "^0.16.1",
62
62
  "recast": "^0.23.12",
63
63
  "ts-morph": "^28.0.0",