@fjall/generator 14.0.0 → 14.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/dist/.build-source-hash +42 -37
  2. package/dist/.minified +1 -1
  3. package/dist/src/ast/astTestHelpers.d.ts +2 -0
  4. package/dist/src/ast/domain/astDomainParser.d.ts +12 -0
  5. package/dist/src/ast/domain/astDomainParser.js +1 -1
  6. package/dist/src/ast/index.d.ts +1 -1
  7. package/dist/src/ast/index.js +1 -1
  8. package/dist/src/ast/parsers/resources/astCdnParser.js +1 -1
  9. package/dist/src/ast/parsers/resources/astComputeParser.js +1 -1
  10. package/dist/src/codemod/edits/addResource/propertyBuilder.d.ts +8 -0
  11. package/dist/src/codemod/edits/addResource/propertyBuilder.js +1 -1
  12. package/dist/src/codemod/edits/addResource/siblingEmission.d.ts +61 -0
  13. package/dist/src/codemod/edits/addResource/siblingEmission.js +1 -0
  14. package/dist/src/codemod/edits/addResource.js +1 -1
  15. package/dist/src/codemod/edits/connectResourceToCompute.js +1 -1
  16. package/dist/src/codemod/edits/ensureImports.d.ts +18 -0
  17. package/dist/src/codemod/edits/ensureImports.js +1 -1
  18. package/dist/src/codemod/edits/modifyResource.js +1 -1
  19. package/dist/src/codemod/edits/resourceStatementLocator.d.ts +3 -0
  20. package/dist/src/codemod/edits/resourceStatementLocator.js +1 -1
  21. package/dist/src/codemod/edits/siblingBindingResolution.d.ts +54 -1
  22. package/dist/src/codemod/edits/siblingBindingResolution.js +1 -1
  23. package/dist/src/codemod/emitGuard.d.ts +2 -30
  24. package/dist/src/codemod/emitGuard.js +1 -1
  25. package/dist/src/codemod/emitProjection.d.ts +31 -2
  26. package/dist/src/codemod/emitProjection.js +1 -1
  27. package/dist/src/codemod/fileRewriter/builders.d.ts +18 -0
  28. package/dist/src/codemod/fileRewriter/builders.js +1 -1
  29. package/dist/src/codemod/fileRewriter/index.d.ts +1 -1
  30. package/dist/src/codemod/fileRewriter/index.js +1 -1
  31. package/dist/src/codemod/index.d.ts +1 -0
  32. package/dist/src/codemod/index.js +1 -1
  33. package/dist/src/codemod/registry.d.ts +26 -27
  34. package/dist/src/codemod/registry.js +1 -1
  35. package/dist/src/codemod/validationGate/gates/projection.js +1 -1
  36. package/dist/src/generation/common.js +8 -8
  37. package/dist/src/generation/connectionWiring.d.ts +83 -0
  38. package/dist/src/generation/connectionWiring.js +1 -0
  39. package/dist/src/generation/database.d.ts +3 -34
  40. package/dist/src/generation/database.js +20 -20
  41. package/dist/src/generation/index.d.ts +1 -0
  42. package/dist/src/generation/index.js +1 -1
  43. package/dist/src/generation/infrastructure.d.ts +11 -0
  44. package/dist/src/generation/infrastructure.js +38 -28
  45. package/dist/src/generation/messagingConnections.d.ts +1 -4
  46. package/dist/src/generation/messagingConnections.js +1 -1
  47. package/dist/src/generation/storageConnections.d.ts +1 -12
  48. package/dist/src/generation/storageConnections.js +1 -1
  49. package/dist/src/planning/resourceAddition.d.ts +3 -0
  50. package/dist/src/planning/resourceAddition.js +1 -1
  51. package/dist/src/projection/compute.d.ts +31 -0
  52. package/dist/src/projection/compute.js +1 -0
  53. package/dist/src/projection/index.d.ts +3 -2
  54. package/dist/src/projection/index.js +1 -1
  55. package/dist/src/projection/project.d.ts +16 -1
  56. package/dist/src/projection/project.js +1 -1
  57. package/dist/src/projection/roundTripKeys.d.ts +12 -0
  58. package/dist/src/projection/roundTripKeys.js +1 -0
  59. package/dist/src/projection/tables.d.ts +13 -9
  60. package/dist/src/projection/tables.js +1 -1
  61. package/dist/src/projection/types.d.ts +204 -4
  62. package/dist/src/projection/unresolved.d.ts +17 -0
  63. package/dist/src/projection/unresolved.js +1 -0
  64. package/dist/src/schemas/applicationSchemas.d.ts +2 -0
  65. package/dist/src/schemas/cdnSchemas.d.ts +4 -0
  66. package/dist/src/schemas/cdnSchemas.js +1 -1
  67. package/dist/src/schemas/computeSchemas.js +1 -1
  68. package/dist/src/validation/validationMessages.d.ts +1 -0
  69. package/dist/src/validation/validationMessages.js +1 -1
  70. package/dist/src/version.d.ts +1 -1
  71. package/dist/src/version.js +1 -1
  72. package/package.json +2 -2
@@ -1,7 +1,21 @@
1
1
  import type * as recast from "recast";
2
2
  import { type Result } from "../../types/Result.js";
3
+ import { type QuoteStyle } from "../fileRewriter/index.js";
3
4
  import type { SemanticQueryError } from "../types.js";
4
5
  type ParsedFile = ReturnType<typeof recast.parse>;
6
+ /** The components barrel every emitted factory/value import comes from. */
7
+ export declare const FACTORY_MODULE_SOURCE = "@fjall/components-infrastructure";
8
+ /**
9
+ * Ensure `specifiers` are named imports from the components barrel
10
+ * (`FACTORY_MODULE_SOURCE`) in a program body. Appends to the existing
11
+ * barrel import line when one exists (deduped by imported name via
12
+ * `appendSpecifier`); otherwise unshifts a new declaration at body[0] — the
13
+ * add path's historical placement, preserved byte-for-byte from its private
14
+ * `ensureFactoryImport`. This is the ONLY import-management path the emitted
15
+ * statements use; structured-expression `imports` are barrel specifiers by
16
+ * contract, never other modules.
17
+ */
18
+ export declare function ensureBarrelSpecifiers(body: StatementNode[], specifiers: readonly string[], quoteStyle: QuoteStyle): void;
5
19
  export interface EnsureImportsAddition {
6
20
  module: string;
7
21
  specifiers: string[];
@@ -23,4 +37,8 @@ export type EnsureImportsError = SemanticQueryError;
23
37
  * Never throws — every failure path returns `failure(EnsureImportsError)`.
24
38
  */
25
39
  export declare function ensureImports(file: ParsedFile, additions: EnsureImportsAddition[]): Result<EnsureImportsSuccess, EnsureImportsError>;
40
+ interface StatementNode {
41
+ type: string;
42
+ [key: string]: unknown;
43
+ }
26
44
  export {};
@@ -1 +1 @@
1
- var l=Object.defineProperty;var s=(t,r)=>l(t,"name",{value:r,configurable:!0});import{failure as d,success as c}from"../../types/Result.js";import{extractProgramBody as y,isRecord as I}from"../_internal.js";import{appendSpecifier as g,buildImportDeclaration as S,detectQuoteStyle as x}from"../fileRewriter/index.js";function k(t,r){const e=y(t);if(e===void 0)return d({kind:"SemanticQueryError",reason:"recast File is missing program.body"});if(r.length===0)return c({mutated:!1});const n=x(t);let o=!1;for(const i of r){const f=A(e,i.module);if(f!==void 0){for(const a of i.specifiers){const m=u(f);g(f,a),u(f)>m&&(o=!0)}continue}if(i.specifiers.length===0)continue;const p=S(i.specifiers,i.module,n);D(e,p),o=!0}return c({mutated:o})}s(k,"ensureImports");function A(t,r){for(const e of t){if(e.type!=="ImportDeclaration")continue;const n=e.source;if(I(n)&&!(n.type!=="StringLiteral"&&n.type!=="Literal")&&n.value===r)return e}}s(A,"findImportDeclaration");function D(t,r){let e=-1;for(let o=0;o<t.length;o+=1){const i=t[o];i!==void 0&&i.type==="ImportDeclaration"&&(e=o)}const n=e===-1?0:e+1;t.splice(n,0,r)}s(D,"insertAfterLastImport");function u(t){if(!Array.isArray(t.specifiers))return 0;let r=0;for(const e of t.specifiers)e!==null&&typeof e=="object"&&"type"in e&&e.type==="ImportSpecifier"&&(r+=1);return r}s(u,"countImportedNames");export{k as ensureImports};
1
+ var g=Object.defineProperty;var f=(e,r)=>g(e,"name",{value:r,configurable:!0});import{failure as S,success as c}from"../../types/Result.js";import{extractProgramBody as x,isRecord as h}from"../_internal.js";import{appendSpecifier as u,buildImportDeclaration as p,detectQuoteStyle as A}from"../fileRewriter/index.js";const a="@fjall/components-infrastructure";function j(e,r,t){if(r.length===0)return;const n=m(e,a);if(n!==void 0){for(const i of r)u(n,i);return}const o=p([...r],a,t);e.unshift(o)}f(j,"ensureBarrelSpecifiers");function w(e,r){const t=x(e);if(t===void 0)return S({kind:"SemanticQueryError",reason:"recast File is missing program.body"});if(r.length===0)return c({mutated:!1});const n=A(e);let o=!1;for(const i of r){const s=m(t,i.module);if(s!==void 0){for(const y of i.specifiers){const I=l(s);u(s,y),l(s)>I&&(o=!0)}continue}if(i.specifiers.length===0)continue;const d=p(i.specifiers,i.module,n);D(t,d),o=!0}return c({mutated:o})}f(w,"ensureImports");function m(e,r){for(const t of e){if(t.type!=="ImportDeclaration")continue;const n=t.source;if(h(n)&&!(n.type!=="StringLiteral"&&n.type!=="Literal")&&n.value===r)return t}}f(m,"findImportDeclaration");function D(e,r){let t=-1;for(let o=0;o<e.length;o+=1){const i=e[o];i!==void 0&&i.type==="ImportDeclaration"&&(t=o)}const n=t===-1?0:t+1;e.splice(n,0,r)}f(D,"insertAfterLastImport");function l(e){if(!Array.isArray(e.specifiers))return 0;let r=0;for(const t of e.specifiers)t!==null&&typeof t=="object"&&"type"in t&&t.type==="ImportSpecifier"&&(r+=1);return r}f(l,"countImportedNames");export{a as FACTORY_MODULE_SOURCE,j as ensureBarrelSpecifiers,w as ensureImports};
@@ -1 +1 @@
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};
1
+ var U=Object.defineProperty;var p=(r,e)=>U(r,"name",{value:e,configurable:!0});import{failure as s,success as x}from"../../types/Result.js";import{DEFAULT_FILE_PATH as V,computeLinesDelta as j,extractProgramBody as D}from"../_internal.js";import{buildObjectProperty as B,detectQuoteStyle as M,parse as _,printFile as W}from"../fileRewriter/index.js";import{STATEMENT_REGISTRY as H}from"../registry.js";import{locateAllShapes as Y,locateByShape as z}from"../semanticIndex/index.js";import{buildUnmanagedEditRefusal as J}from"./unmanagedShapeErrors.js";import{checkControlFlowPolicy as O}from"./controlFlowPolicy.js";import{driftGate as X,runPipeline as Z,schemaGate as q}from"../validationGate/index.js";import{buildEmittedValue as ee}from"./addResource/propertyBuilder.js";import{buildMergedLiteral as R,collectPropertyKeys as re,isNodeShape as I,readKeyName as te}from"./modifyResource/literalConversion.js";import{findPropertyByKey as T,visit as ne}from"./objectGraph.js";import{evaluateEmitGuard as F,UNRESOLVED as ie}from"../emitGuard.js";import{collectImportSpecifiers as ae,projectEmittedProperties as oe,projectionRefusals as G}from"../emitProjection.js";import{constructKeyAliasesFor as se}from"../../projection/index.js";import{ensureBarrelSpecifiers as de}from"./ensureImports.js";import{applyBinding as ue}from"./resourceStatementLocator.js";import{createSiblingBindingResolution as pe}from"./siblingBindingResolution.js";function $e(r,e,i={}){const t=e.filePath??V,a=_(r,t);if(!a.success)return s(a.error);const n=z(r,{type:e.type,name:e.name},t);if(!n.success)return n.error.kind==="TemplateLiteralNameError"?s(n.error):s({kind:"SemanticQueryError",reason:n.error.reason,cause:n.error.cause});if(n.data===void 0)return s({kind:"ResourceNotFoundError",type:e.type,name:e.name,knownNames:Ee(r,e.type,t)});if(n.data.managed===!1)return s(J(e.type,"modify"));const o=O({ast:a.data,target:n.data,resource:{type:e.type,name:e.name},op:"modify"});if(o.refusal!==void 0)return s(o.refusal);const d=Z([X],{content:r,plan:{type:e.type,name:e.name,properties:e.properties,op:"modify"},baseline:i.baseline,policy:e.driftPolicy});if(!d.success)return s(L(d.error));if(d.data.action==="skip")return x({content:r,linesChanged:{added:0,removed:0},skipped:!0});const u=D(a.data);if(u===void 0)return s({kind:"SemanticQueryError",reason:"recast File is missing program.body"});const c=fe(u,n.data);if(c===void 0)return s({kind:"SemanticQueryError",reason:"Unable to resolve config ObjectExpression for the located resource."});const l=M(a.data),m=R(c,e.properties),k=H[e.type].emitGuard,h=pe(e.type,()=>u,{mustPrecedeStatementIndex:ce(u,n.data)??0}),E={editingExisting:!0,resourceName:e.name,resolveBinding:h.resolveBinding,resolveBindings:h.resolveBindings},w=K(c,R(c,{})),S=K(c,m),A=F(k,w),P=F(k,S);if(P!==void 0&&P.property!==A?.property)return s(P);const $=new Set(G(e.type,w,E).map(g=>g.property)),b=G(e.type,S,E).find(g=>!$.has(g.property));if(b!==void 0)return s(b);const v=q.run({content:r,plan:{type:e.type,name:e.name,properties:e.properties,op:"modify"}});if(!v.success)return s(L(v.error));h.takePendingWraps();const y=oe(e.type,e.properties,E);if("refusal"in y)return s(y.refusal);const f=y.siblings?.[0];if(f!==void 0)return s({kind:"InvalidPropertyError",property:f.planKey,reason:`\`${f.planKey}\` expands into its own \`app.${f.appMethod}("${f.constructId}", \u2026)\` statement, and \`fjall modify\` edits exactly one statement. Nothing has been written \u2014 this resource already exists, so \`fjall add\` would refuse it as a duplicate: author the \`app.${f.appMethod}("${f.constructId}", \u2026)\` statement in infrastructure.ts by hand, or remove the resource and re-add it with \`${f.planKey}\`.`});const C=le(c,e.type,y.props,l);if(!C.success)return s(C.error);for(const g of h.takePendingWraps())ue(u,g);de(u,ae(y.props),l);const N=W(a.data,r),Q=o.warning!==void 0?[o.warning.message]:void 0;return x({content:N,linesChanged:j(r,N),warnings:Q})}p($e,"modifyResource");function ce(r,e){const i=e.start+e.length;for(let t=0;t<r.length;t++){const a=r[t];if(a===void 0)continue;const{start:n,end:o}=a;if(!(typeof n!="number"||typeof o!="number")&&n<=e.start&&i<=o)return t}}p(ce,"findStatementIndexContaining");function K(r,e){const i={...e};for(const t of re(r))t in i||(i[t]=ie);return i}p(K,"buildGuardView");function L(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}`}}}p(L,"narrowGateError");function fe(r,e){const i=e.start+e.length;let t;const a=p(n=>{if(t!==void 0||n.type!=="CallExpression")return;const o=n.arguments;if(!Array.isArray(o)||o.length<2)return;const d=o[0];if(!I(d)||d.type!=="StringLiteral"||d.start!==e.start||d.end!==i)return;const u=o[1];I(u)&&u.type==="ObjectExpression"&&(t=u)},"visitor");for(const n of r)if(ne(n,a),t!==void 0)break;return t}p(fe,"findConfigObject");function le(r,e,i,t){const a=he(r),n=se(e);for(const o of i){const d=ee(o,t);if(!d.success)return s(d.error);const u=n[o.key]??[],c=T(r,o.key);if(c!==void 0){c.value=d.data,ye(r,u);continue}const l=me(r,u);if(l!==void 0){ge(r,l,o.key,d.data,t);continue}const m=B(o.key,d.data,t);a&&(m.extra={...m.extra??{},trailingComma:!0}),r.properties.push(m)}return x(void 0)}p(le,"applyPropertyEdits");function me(r,e){for(const i of e){const t=T(r,i);if(t!==void 0)return t}}p(me,"findAliasProperty");function ye(r,e){if(e.length!==0)for(let i=r.properties.length-1;i>=0;i--){const t=r.properties[i];if(t===void 0||t.type!=="Property"&&t.type!=="ObjectProperty"||t.shorthand===!0||t.computed===!0)continue;const a=te(t.key);a!==void 0&&e.includes(a)&&r.properties.splice(i,1)}}p(ye,"removePropertiesByKey");function ge(r,e,i,t,a){const n=r.properties.indexOf(e);if(n<0)return;const o=B(i,t,a);e.extra?.trailingComma===!0&&(o.extra={...o.extra??{},trailingComma:!0}),r.properties.splice(n,1,o)}p(ge,"replacePropertyInPlace");function he(r){return r.properties[r.properties.length-1]?.extra?.trailingComma===!0}p(he,"detectTrailingCommaStyle");function Ee(r,e,i){const t=Y(r,i);return t.success?t.data.filter(a=>a.type===e).map(a=>a.symbolName):[]}p(Ee,"collectKnownNames");export{$e as modifyResource};
@@ -9,6 +9,9 @@ export interface LocatorTarget {
9
9
  }
10
10
  export interface BindingPlan {
11
11
  variableName: string;
12
+ /** The resource name the plan binds — collision refusals name it so the
13
+ * operator sees which two resources derive the same variable. */
14
+ resourceName: string;
12
15
  /** Index of the matched statement in the module body — callers that emit a
13
16
  * reference to the binding need it to judge declaration order. */
14
17
  statementIndex: number;
@@ -1 +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};
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,i){const e=[];for(let c=0;c<r.length;c++){const l=r[c];if(l===void 0)continue;let y=!1;k(l,x=>{x.type==="CallExpression"&&D(x,n.factory,i)&&(y=!0)}),y&&e.push({statementIndex:c})}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 a=r[t.statementIndex];if(a===void 0)return o({kind:"statement-not-found"});const p=a.type==="ExportNamedDeclaration"||a.type==="ExportDefaultDeclaration",f=h(a);if(f===void 0)return o({kind:"unbindable-statement",statementType:a.type});if(f.type==="VariableDeclaration"){const c=f.declarations;if(!Array.isArray(c)||c.length!==1)return o({kind:"multiple-declarators"});const l=c[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=c[0].init;return!s(x)||!g(x,n,i)?o({kind:"init-not-app-method-wrap"}):E({variableName:y,resourceName:i,statementIndex:t.statementIndex})}if(p)return o({kind:"unbindable-statement",statementType:a.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,i))return o({kind:"bare-factory-call"});if(!g(u,n,i))return o({kind:"foreign-wrapper"});const m=A(i);return v.has(m)?o({kind:"reserved-word",variableName:m}):S(r,m)?o({kind:"name-conflict",variableName:m}):E({variableName:m,resourceName:i,statementIndex:t.statementIndex,wrap:{statementIndex:t.statementIndex,expression:u}})}d(j,"planBinding");function L(r,n){if(n.wrap===void 0)return;const{statementIndex:i,expression:e}=n.wrap,t=r[i],a=b.variableDeclaration("const",[b.variableDeclarator(b.identifier(n.variableName),e)]),p=t?.comments;p!==void 0&&(a.comments=p),r[i]=a}d(L,"applyBinding");function D(r,n,i){const e=r.callee;if(!s(e)||e.type!=="MemberExpression"||e.computed===!0)return!1;const t=e.object,a=e.property;if(!s(t)||t.type!=="Identifier"||t.name!==n||!s(a)||a.type!=="Identifier"||a.name!=="build")return!1;const p=r.arguments;if(!Array.isArray(p))return!1;const f=p[0];return!s(f)||f.type!=="StringLiteral"&&f.type!=="Literal"?!1:f.value===i}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 i=n.callee;if(!s(i)||i.type!=="MemberExpression"||i.computed===!0)return;const e=i.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,i){const e=I(r);if(M(e)!==n.appMethod)return!1;const t=e.arguments;if(!Array.isArray(t))return!1;const a=t[0];return s(a)?D(I(a),n.factory,i):!1}d(g,"isAppMethodWrap");function S(r,n){for(const i of r){const e=h(i);if(e!==void 0){if(e.type==="ImportDeclaration"){const t=e.specifiers;if(!Array.isArray(t))continue;for(const a of t){const p=a.local;if(s(p)&&p.name===n)return!0}continue}if(e.type==="VariableDeclaration"){const t=e.declarations;if(!Array.isArray(t))continue;for(const a of t){if(!s(a))continue;const p=a.id;if(!s(p))continue;let f=!1;if(k(p,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(S,"moduleScopeDeclares");export{M as appMethodNameOf,L as applyBinding,D as isFactoryBuildOf,S as moduleScopeDeclares,j as planBinding,h as unwrapExportDeclaration};
@@ -1,10 +1,48 @@
1
1
  import type { StatementType } from "../types.js";
2
2
  import type { NodeShapeLike } from "./modifyResource/literalConversion.js";
3
3
  import { type BindingPlan } from "./resourceStatementLocator.js";
4
+ /**
5
+ * Which sibling types may satisfy a live binding for one edited type.
6
+ * `default` applies to every plan key without its own entry; `perKey` narrows
7
+ * a specific plan key to the types it may bind — compute's connection keys
8
+ * each name exactly one resource type, so `connectedDatabase: "Uploads"`
9
+ * must never resolve against a storage statement even though a same-named
10
+ * one exists.
11
+ */
12
+ export interface BindingCandidates {
13
+ default?: ReadonlyArray<StatementType>;
14
+ perKey?: Readonly<Partial<Record<string, ReadonlyArray<StatementType>>>>;
15
+ }
16
+ /**
17
+ * One module-scope `const` a pending wrap plan will declare when applied.
18
+ * `planBinding` checks each wrap name against the PRE-mutation body only, so
19
+ * the caller must judge the pending declarations against EACH OTHER and
20
+ * against any primary binding it derives later — two resources whose names
21
+ * collapse to the same variable (`"Uploads"` and `"uploads"` both derive
22
+ * `uploads`) would otherwise emit two `const` declarations and write a file
23
+ * that does not parse.
24
+ */
25
+ export interface PendingDeclaration {
26
+ variableName: string;
27
+ resourceName: string;
28
+ statementIndex: number;
29
+ /** The plan key whose resolution recorded the wrap, when the caller
30
+ * supplied one — collision refusals attribute themselves to it. */
31
+ planKey?: string;
32
+ }
4
33
  export interface SiblingBindingResolution {
5
- resolveBinding: (planResourceName: string) => string | undefined;
34
+ resolveBinding: (planResourceName: string, planKey?: string) => string | undefined;
35
+ /**
36
+ * All-or-nothing array form: every name resolves or the whole call answers
37
+ * `undefined` — and any wrap plans recorded by the partial resolution are
38
+ * rolled back, so a refused array never leaves a half-bound file.
39
+ */
40
+ resolveBindings: (planResourceNames: ReadonlyArray<string>, planKey?: string) => ReadonlyArray<string> | undefined;
6
41
  /** Wrap plans recorded by successful resolutions since the last drain. */
7
42
  takePendingWraps: () => BindingPlan[];
43
+ /** The `const` names those pending wraps will declare — consulted before
44
+ * any mutation so no two planned declarations share a name. */
45
+ pendingDeclarations: () => ReadonlyArray<PendingDeclaration>;
8
46
  }
9
47
  export interface SiblingBindingConstraints {
10
48
  /**
@@ -20,3 +58,18 @@ export interface SiblingBindingConstraints {
20
58
  mustPrecedeStatementIndex?: number;
21
59
  }
22
60
  export declare function createSiblingBindingResolution(type: StatementType, getBody: () => NodeShapeLike[] | undefined, constraints?: SiblingBindingConstraints): SiblingBindingResolution;
61
+ /**
62
+ * The first pair of pending declarations that would declare the same
63
+ * module-scope `const` for two DIFFERENT statements. Each wrap passed
64
+ * `planBinding`'s body-conflict check individually, but none of the planned
65
+ * names is in the body yet, so two resources collapsing to one variable
66
+ * (`"Uploads"` and `"uploads"`) sail through separately and collide only in
67
+ * the written file — `Identifier 'uploads' has already been declared`, from
68
+ * an edit that reported success. The caller refuses pre-mutation instead.
69
+ * Same-statement duplicates are NOT a collision: re-resolving one resource
70
+ * plans the identical wrap twice, and applying it twice is idempotent.
71
+ */
72
+ export declare function findPendingWrapCollision(declarations: ReadonlyArray<PendingDeclaration>): {
73
+ first: PendingDeclaration;
74
+ second: PendingDeclaration;
75
+ } | undefined;
@@ -1 +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
+ var I=Object.defineProperty;var d=(a,o)=>I(a,"name",{value:o,configurable:!0});import{FACTORY_IDENTIFIERS as b,STATEMENT_REGISTRY as h}from"../registry.js";import{planBinding as v}from"./resourceStatementLocator.js";const x={cdn:{default:["storage","compute"]},compute:{perKey:{connectedDatabase:["database"],connectedStorage:["storage"],connectedMessaging:["messaging"]}}};function M(a,o,i={}){const r=x[a];let s=[];const p=d((n,e)=>{const u=o();if(u===void 0)return;const f=(e!==void 0?r?.perKey?.[e]:void 0)??r?.default??[],c=[];for(const l of f){const N=h[l].emitWrapper?.appMethod;if(N===void 0)continue;const m=v(u,{factory:b[l],appMethod:N},n);if(m.success){c.push(m.data);continue}if(m.error.kind!=="statement-not-found")return}if(c.length!==1)return;const t=c[0];if(t===void 0)return;const g=i.mustPrecedeStatementIndex;if(!(g!==void 0&&t.statementIndex>=g))return t.wrap!==void 0&&s.push({plan:t,...e!==void 0&&{planKey:e}}),t.variableName},"resolveBinding");return{resolveBinding:p,resolveBindings:d((n,e)=>{const u=s.length,f=[];for(const c of n){const t=p(c,e);if(t===void 0){s.length=u;return}f.push(t)}return f},"resolveBindings"),takePendingWraps:d(()=>{const n=s;return s=[],n.map(e=>e.plan)},"takePendingWraps"),pendingDeclarations:d(()=>s.map(({plan:n,planKey:e})=>({variableName:n.variableName,resourceName:n.resourceName,statementIndex:n.statementIndex,...e!==void 0&&{planKey:e}})),"pendingDeclarations")}}d(M,"createSiblingBindingResolution");function P(a){const o=new Map;for(const i of a){const r=o.get(i.variableName);if(r===void 0){o.set(i.variableName,i);continue}if(r.statementIndex!==i.statementIndex)return{first:r,second:i}}}d(P,"findPendingWrapCollision");export{M as createSiblingBindingResolution,P as findPendingWrapCollision};
@@ -1,17 +1,6 @@
1
+ import { UNRESOLVED } from "../projection/unresolved.js";
1
2
  import type { InvalidPropertyError } from "./types.js";
2
- /**
3
- * A value the codemod could not have written: the property is present in
4
- * the target statement but holds a non-literal expression (`Code.fromAsset(
5
- * "./src")`, `Runtime.NODEJS_20_X`, `assetsBucket`).
6
- *
7
- * Its presence is PROOF the statement was authored by a human or by the
8
- * generator scaffold, both of which speak construct vocabulary. Rules that
9
- * refuse a key because the construct needs a live object must therefore
10
- * treat `UNRESOLVED` as satisfying the construct — otherwise `fjall modify`
11
- * would refuse to touch perfectly good scaffold output. The ADD path never
12
- * produces it: every value there comes from a flag.
13
- */
14
- export declare const UNRESOLVED: unique symbol;
3
+ export { UNRESOLVED };
15
4
  /**
16
5
  * The property record the emitted statement WOULD carry. `add` passes the
17
6
  * requested properties; `modify` passes the merge of the existing statement
@@ -43,23 +32,6 @@ export interface EmitGuardRule {
43
32
  * always correct.
44
33
  */
45
34
  export declare const AUTHOR_BY_HAND: string;
46
- /** True when the key holds a value the codemod itself could have written. */
47
- export declare function isWrittenValue(value: unknown): boolean;
48
- /**
49
- * A rule for a plan key NO construct reads, in any shape — proven by zero
50
- * `props.<key>` reads across @fjall/components-infrastructure. Presence
51
- * alone is the refusal; the value cannot rescue it.
52
- *
53
- * Phrased as a statement of consequence, because the operator's mental model
54
- * is "I set it, so it took effect" and the whole point is that it did not.
55
- */
56
- export declare function silentlyDropped(property: string, detail: string): EmitGuardRule;
57
- /**
58
- * A rule for a key the construct DOES read but only as a live object — a
59
- * CDK enum member, an asset, another construct. A flag value is a string,
60
- * so any value the codemod can write is wrong; only `UNRESOLVED` passes.
61
- */
62
- export declare function requiresLiveValue(property: string, detail: string): EmitGuardRule;
63
35
  /** Refuse whenever the key is missing (the missing-discriminator shape). */
64
36
  export declare function whenAbsent(property: string): (properties: EmitGuardProperties) => boolean;
65
37
  /**
@@ -1 +1 @@
1
- var u=Object.defineProperty;var r=(e,n)=>u(e,"name",{value:n,configurable:!0});const i=Symbol("unresolved-expression"),o="Write this resource in infrastructure.ts by hand \u2014 the construct props it needs cannot be expressed as `fjall add` flags.";function d(e){return e!==void 0&&e!==i}r(d,"isWrittenValue");function f(e,n){return{property:e,refuseWhen:r(t=>t[e]!==void 0,"refuseWhen"),reason:`${n} \`fjall add\` would write it into the factory call and the resource would deploy without it. ${o}`}}r(f,"silentlyDropped");function a(e,n){return{property:e,refuseWhen:r(t=>d(t[e]),"refuseWhen"),reason:`${n} ${o}`}}r(a,"requiresLiveValue");function l(e){return n=>n[e]===void 0}r(l,"whenAbsent");function c(e,n){if(e!==void 0){for(const t of e)if(t.refuseWhen(n))return{kind:"InvalidPropertyError",property:t.property,reason:t.reason}}}r(c,"evaluateEmitGuard");export{o as AUTHOR_BY_HAND,i as UNRESOLVED,c as evaluateEmitGuard,d as isWrittenValue,a as requiresLiveValue,f as silentlyDropped,l as whenAbsent};
1
+ var o=Object.defineProperty;var t=(e,r)=>o(e,"name",{value:r,configurable:!0});import{UNRESOLVED as i}from"../projection/unresolved.js";const s="Write this resource in infrastructure.ts by hand \u2014 the construct props it needs cannot be expressed as `fjall add` flags.";function f(e){return r=>r[e]===void 0}t(f,"whenAbsent");function a(e,r){if(e!==void 0){for(const n of e)if(n.refuseWhen(r))return{kind:"InvalidPropertyError",property:n.property,reason:n.reason}}}t(a,"evaluateEmitGuard");export{s as AUTHOR_BY_HAND,i as UNRESOLVED,a as evaluateEmitGuard,f as whenAbsent};
@@ -1,10 +1,13 @@
1
- import { type ProjectionContext } from "../projection/index.js";
1
+ import { type ProjectionContext, type StructuredValue } from "../projection/index.js";
2
2
  import { type EmitGuardProperties } from "./emitGuard.js";
3
3
  import type { InvalidPropertyError, StatementType } from "./types.js";
4
4
  /**
5
5
  * One emitted construct prop. `literal` is a serialisable value; `expression`
6
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.
7
+ * emitters splice it as an identifier rather than serialising it;
8
+ * `structured` carries a validated expression tree plus the components-barrel
9
+ * specifiers it needs (`collectImportSpecifiers` gathers those for the
10
+ * emitters to apply once).
8
11
  */
9
12
  export type EmittedProp = {
10
13
  key: string;
@@ -14,7 +17,32 @@ export type EmittedProp = {
14
17
  key: string;
15
18
  kind: "expression";
16
19
  source: string;
20
+ } | {
21
+ key: string;
22
+ kind: "structured";
23
+ expression: StructuredValue;
24
+ imports?: readonly string[];
17
25
  };
26
+ /**
27
+ * One sibling statement in emission vocabulary — the projection's
28
+ * {@link SiblingStatement} with its props mapped to {@link EmittedProp}s. The
29
+ * add path emits each as `app.<appMethod>("<constructId>", { …props })`
30
+ * directly after the primary statement; modify refuses the whole edit when
31
+ * any arrive, because it edits exactly one statement.
32
+ */
33
+ export interface EmittedSibling {
34
+ planKey: string;
35
+ constructId: string;
36
+ appMethod: string;
37
+ props: EmittedProp[];
38
+ }
39
+ /**
40
+ * Every components-barrel specifier the emitted props need, deduplicated in
41
+ * first-appearance order. The emitters apply these once per write — a
42
+ * specifier requested by several props (or by a prop and a sibling) lands as
43
+ * one named import.
44
+ */
45
+ export declare function collectImportSpecifiers(props: ReadonlyArray<EmittedProp>): string[];
18
46
  /**
19
47
  * EVERY plan key in `properties` this type cannot turn into a construct
20
48
  * prop, one refusal per key, as the codebase's actionable error shape.
@@ -45,6 +73,7 @@ export declare function projectionRefusals(type: StatementType, properties: Emit
45
73
  */
46
74
  export declare function projectEmittedProperties(type: StatementType, properties: Readonly<Record<string, unknown>>, context?: ProjectionContext): {
47
75
  props: EmittedProp[];
76
+ siblings?: EmittedSibling[];
48
77
  } | {
49
78
  refusal: InvalidPropertyError;
50
79
  };
@@ -1 +1 @@
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
+ var m=Object.defineProperty;var r=(e,t)=>m(e,"name",{value:t,configurable:!0});import{crossKeyUnprojectables as h,isProjectableType as u,projectPlanToConstructProps as c}from"../projection/index.js";import{AUTHOR_BY_HAND as k}from"./emitGuard.js";function x(e){const t=new Set;for(const n of e)if(!(n.kind!=="structured"||n.imports===void 0))for(const i of n.imports)t.add(i);return[...t]}r(x,"collectImportSpecifiers");const y={"plan-only":`\`fjall add\` would write it into the factory call and the resource would deploy without it. ${k}`,"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.","cross-key":"Nothing has been written: the refusal follows from the statement's properties taken together, not from this key alone. Change the named property (or the counterpart it conflicts with) and run the same command again."};function d(e,t,n="plan-only"){return{kind:"InvalidPropertyError",property:e,reason:`${t} ${y[n]}`}}r(d,"unprojectableRefusal");function j(e,t,n={}){return u(e)?[...n.editingExisting===!0?h(e,t):[],...c(e,t,n).unprojectable].map(o=>d(o.key,o.reason,o.kind)):[]}r(j,"projectionRefusals");function E(e,t,n={}){if(!u(e))return{props:Object.entries(t).map(([p,f])=>({key:p,kind:"literal",value:f}))};const{props:i,unprojectable:o,siblings:a}=c(e,t,n),[s]=o;return s!==void 0?{refusal:d(s.key,s.reason,s.kind)}:{props:i.map(l),...a!==void 0&&{siblings:a.map(g)}}}r(E,"projectEmittedProperties");function g(e){return{planKey:e.planKey,constructId:e.constructId,appMethod:e.appMethod,props:e.props.map(l)}}r(g,"toEmittedSibling");function l(e){switch(e.value.kind){case"expression":return{key:e.key,kind:"expression",source:e.value.source};case"structured":return{key:e.key,kind:"structured",expression:e.value.expression,...e.value.imports!==void 0&&{imports:e.value.imports}};case"literal":return{key:e.key,kind:"literal",value:e.value.value}}}r(l,"toEmittedProp");export{x as collectImportSpecifiers,E as projectEmittedProperties,j as projectionRefusals};
@@ -41,6 +41,24 @@ export declare function buildFactoryStatement(params: BuildFactoryStatementParam
41
41
  * only materialised by an App method.
42
42
  */
43
43
  export declare function buildAppMethodWrappedStatement(params: BuildAppMethodWrappedStatementParams): n.ExpressionStatement;
44
+ export interface BuildAppMethodStatementParams {
45
+ /** App binding identifier, e.g. `"app"` — resolved, never assumed. */
46
+ appIdentifier: string;
47
+ /** App method called directly, e.g. `"addConnection"`. */
48
+ appMethod: string;
49
+ /** Construct id, used as the first argument literal. */
50
+ name: string;
51
+ /** Configuration object properties. */
52
+ properties: ObjectPropertyInput[];
53
+ /** Quote style for NEW string literals the builder synthesises. */
54
+ quoteStyle: QuoteStyle;
55
+ }
56
+ /**
57
+ * Build an `ExpressionStatement` wrapping `app.<method>("Name", { … })` — a
58
+ * direct app-method call with NO factory layer, the shape sibling statements
59
+ * take (a connection is materialised by the App method itself).
60
+ */
61
+ export declare function buildAppMethodStatement(params: BuildAppMethodStatementParams): n.ExpressionStatement;
44
62
  /**
45
63
  * Build an object `Property` with a bare-identifier key. If `key` is
46
64
  * not a valid JS identifier (e.g. contains a hyphen), the key is
@@ -1 +1 @@
1
- var f=Object.defineProperty;var n=(e,t)=>f(e,"name",{value:t,configurable:!0});import{builders as i}from"ast-types";function y(e){return i.expressionStatement(s(e))}n(y,"buildFactoryStatement");function g(e){const t=i.callExpression(l(e.appIdentifier,e.appMethod),[s(e)]);return i.expressionStatement(t)}n(g,"buildAppMethodWrappedStatement");function s(e){const t=i.memberExpression(i.identifier(e.factory),i.identifier("build"),!1),r=p(e.name,e.quoteStyle),o=i.objectExpression(e.properties.map(c=>a(c.key,c.value,e.quoteStyle)));return i.callExpression(t,[r,o])}n(s,"buildFactoryCall");function a(e,t,r){const o=b(e)?i.identifier(e):p(e,r);return i.property("init",o,t)}n(a,"buildObjectProperty");function E(e,t,r="double"){const o=e.map(u=>i.importSpecifier(i.identifier(u))),c=p(t,r);return i.importDeclaration(o,c)}n(E,"buildImportDeclaration");function l(e,t){return i.memberExpression(i.identifier(e),i.identifier(t),!1)}n(l,"buildMemberExpression");function I(e,t){Array.isArray(e.specifiers)||(e.specifiers=[]);for(const r of e.specifiers)if(m(r)&&r.imported.name===t)return;e.specifiers.push(i.importSpecifier(i.identifier(t)))}n(I,"appendSpecifier");function p(e,t){const r=t==="single"?"'":'"',o=`${r}${d(e,t)}${r}`;return i.stringLiteral.from({value:e,extra:{rawValue:e,raw:o}})}n(p,"makeStringLiteral");function d(e,t){const r=e.replace(/\\/g,"\\\\");return t==="single"?r.replace(/'/g,"\\'"):r.replace(/"/g,'\\"')}n(d,"escapeForQuote");function b(e){return/^[$_\p{ID_Start}][$_\u200C\u200D\p{ID_Continue}]*$/u.test(e)}n(b,"isValidIdentifier");function m(e){return e!==null&&typeof e=="object"&&"type"in e&&e.type==="ImportSpecifier"}n(m,"isNamedImportSpecifier");export{I as appendSpecifier,g as buildAppMethodWrappedStatement,y as buildFactoryStatement,E as buildImportDeclaration,l as buildMemberExpression,a as buildObjectProperty};
1
+ var a=Object.defineProperty;var n=(e,i)=>a(e,"name",{value:i,configurable:!0});import{builders as t}from"ast-types";function y(e){return t.expressionStatement(s(e))}n(y,"buildFactoryStatement");function E(e){const i=t.callExpression(f(e.appIdentifier,e.appMethod),[s(e)]);return t.expressionStatement(i)}n(E,"buildAppMethodWrappedStatement");function g(e){const i=t.callExpression(f(e.appIdentifier,e.appMethod),[p(e.name,e.quoteStyle),t.objectExpression(e.properties.map(r=>u(r.key,r.value,e.quoteStyle)))]);return t.expressionStatement(i)}n(g,"buildAppMethodStatement");function s(e){const i=t.memberExpression(t.identifier(e.factory),t.identifier("build"),!1),r=p(e.name,e.quoteStyle),o=t.objectExpression(e.properties.map(c=>u(c.key,c.value,e.quoteStyle)));return t.callExpression(i,[r,o])}n(s,"buildFactoryCall");function u(e,i,r){const o=b(e)?t.identifier(e):p(e,r);return t.property("init",o,i)}n(u,"buildObjectProperty");function I(e,i,r="double"){const o=e.map(l=>t.importSpecifier(t.identifier(l))),c=p(i,r);return t.importDeclaration(o,c)}n(I,"buildImportDeclaration");function f(e,i){return t.memberExpression(t.identifier(e),t.identifier(i),!1)}n(f,"buildMemberExpression");function h(e,i){Array.isArray(e.specifiers)||(e.specifiers=[]);for(const r of e.specifiers)if(x(r)&&r.imported.name===i)return;e.specifiers.push(t.importSpecifier(t.identifier(i)))}n(h,"appendSpecifier");function p(e,i){const r=i==="single"?"'":'"',o=`${r}${d(e,i)}${r}`;return t.stringLiteral.from({value:e,extra:{rawValue:e,raw:o}})}n(p,"makeStringLiteral");function d(e,i){const r=e.replace(/\\/g,"\\\\");return i==="single"?r.replace(/'/g,"\\'"):r.replace(/"/g,'\\"')}n(d,"escapeForQuote");function b(e){return/^[$_\p{ID_Start}][$_\u200C\u200D\p{ID_Continue}]*$/u.test(e)}n(b,"isValidIdentifier");function x(e){return e!==null&&typeof e=="object"&&"type"in e&&e.type==="ImportSpecifier"}n(x,"isNamedImportSpecifier");export{h as appendSpecifier,g as buildAppMethodStatement,E as buildAppMethodWrappedStatement,y as buildFactoryStatement,I as buildImportDeclaration,f as buildMemberExpression,u as buildObjectProperty};
@@ -1,4 +1,4 @@
1
1
  export { parse, type ParsedFile, type ParseError } from "./parse.js";
2
2
  export { detectLineTerminator, detectQuoteStyle, printFile } from "./print.js";
3
- export { appendSpecifier, buildAppMethodWrappedStatement, buildFactoryStatement, buildImportDeclaration, buildMemberExpression, buildObjectProperty, type BuildAppMethodWrappedStatementParams, type BuildFactoryStatementParams, type ObjectPropertyInput, type QuoteStyle, } from "./builders.js";
3
+ export { appendSpecifier, buildAppMethodStatement, buildAppMethodWrappedStatement, buildFactoryStatement, buildImportDeclaration, buildMemberExpression, buildObjectProperty, type BuildAppMethodStatementParams, type BuildAppMethodWrappedStatementParams, type BuildFactoryStatementParams, type ObjectPropertyInput, type QuoteStyle, } from "./builders.js";
4
4
  export { locateByLineColumn, locateByRange, type LineColumnHint, type NodeLocation, } from "./locateByRange.js";
@@ -1 +1 @@
1
- import{parse as r}from"./parse.js";import{detectLineTerminator as p,detectQuoteStyle as i,printFile as a}from"./print.js";import{appendSpecifier as n,buildAppMethodWrappedStatement as d,buildFactoryStatement as m,buildImportDeclaration as c,buildMemberExpression as b,buildObjectProperty as u}from"./builders.js";import{locateByLineColumn as x,locateByRange as y}from"./locateByRange.js";export{n as appendSpecifier,d as buildAppMethodWrappedStatement,m as buildFactoryStatement,c as buildImportDeclaration,b as buildMemberExpression,u as buildObjectProperty,p as detectLineTerminator,i as detectQuoteStyle,x as locateByLineColumn,y as locateByRange,r as parse,a as printFile};
1
+ import{parse as r}from"./parse.js";import{detectLineTerminator as p,detectQuoteStyle as i,printFile as a}from"./print.js";import{appendSpecifier as l,buildAppMethodStatement as n,buildAppMethodWrappedStatement as m,buildFactoryStatement as b,buildImportDeclaration as c,buildMemberExpression as u,buildObjectProperty as f}from"./builders.js";import{locateByLineColumn as y,locateByRange as S}from"./locateByRange.js";export{l as appendSpecifier,n as buildAppMethodStatement,m as buildAppMethodWrappedStatement,b as buildFactoryStatement,c as buildImportDeclaration,u as buildMemberExpression,f as buildObjectProperty,p as detectLineTerminator,i as detectQuoteStyle,y as locateByLineColumn,S as locateByRange,r as parse,a as printFile};
@@ -18,6 +18,7 @@ export { computeLinesDelta } from "./_internal.js";
18
18
  export { ResourceNameSchema, StatementTypeSchema } from "./types.js";
19
19
  export { REGISTERED_STATEMENT_TYPES, STATEMENT_REGISTRY, type StatementTypeEntry, } from "./registry.js";
20
20
  export { evaluateEmitGuard, UNRESOLVED, type EmitGuardProperties, type EmitGuardRule, } from "./emitGuard.js";
21
+ export { projectionRefusals } from "./emitProjection.js";
21
22
  export { CODEMOD_ERROR_KINDS } from "./telemetry/errorKinds.js";
22
23
  export type { CodemodErrorKind } from "./telemetry/errorKinds.js";
23
24
  export type { GateId } from "./validationGate/index.js";
@@ -1 +1 @@
1
- import{addResource as o}from"./edits/addResource.js";import{removeResource as c}from"./edits/removeResource.js";import{modifyResource as m}from"./edits/modifyResource.js";import{appendAccountToStage as s}from"./edits/appendAccountToStage.js";import{setContainerSecrets as E}from"./edits/setContainerSecrets.js";import{connectResourceToCompute as l}from"./edits/connectResourceToCompute.js";import{addVpcPeer as u,modifyVpcPeer as f,removeVpcPeer as T}from"./edits/vpcPeer.js";import{addVpcPeerAccepter as R,modifyVpcPeerAccepter as x,removeVpcPeerAccepter as S}from"./edits/vpcPeerAccepter.js";import{addCrossPlanConnection as v,modifyCrossPlanConnection as C,removeCrossPlanConnection as N}from"./edits/crossPlanConnection.js";import{resolveDriftPolicy as b}from"./edits/driftPolicy.js";import{listResources as y}from"./listResources.js";import{resolveConstructByLiteralProperty as G}from"./semanticIndex/index.js";import{parse as I}from"./fileRewriter/parse.js";import{CrossPlanConnectionResourcePlanSchema as F,VpcPeerAccepterResourcePlanSchema as L,VpcPeerResourcePlanSchema as U}from"../schemas/index.js";import{detectDrift as k,mergeProperties as B,snapshotProperties as M}from"./drift/index.js";import{computeLinesDelta as Y}from"./_internal.js";import{ResourceNameSchema as q,StatementTypeSchema as w}from"./types.js";import{REGISTERED_STATEMENT_TYPES as H,STATEMENT_REGISTRY as J}from"./registry.js";import{evaluateEmitGuard as W,UNRESOLVED as X}from"./emitGuard.js";import{CODEMOD_ERROR_KINDS as $}from"./telemetry/errorKinds.js";import{buildEgressBlockedEvent as re,buildFiredEvent as oe,buildGateFailedEvent as te,buildGatePassedEvent as ce,buildRejectedEvent as pe,buildSucceededEvent as me,buildTimeoutEvent as ae,estimateCostUsd as se,FALLBACK_EVENTS as ne,GATE_EVENTS as Ee,gateFallbackOutput as de,PARSE_GATE as le,RUNTIME_GATE as ie,runFallback as ue,shouldTryFallback as fe,UNATTRIBUTED_ORGANISATION as Te}from"./llmFallback/index.js";export{$ as CODEMOD_ERROR_KINDS,F as CrossPlanConnectionResourcePlanSchema,ne as FALLBACK_EVENTS,Ee as GATE_EVENTS,le as PARSE_GATE,H as REGISTERED_STATEMENT_TYPES,ie as RUNTIME_GATE,q as ResourceNameSchema,J as STATEMENT_REGISTRY,w as StatementTypeSchema,Te as UNATTRIBUTED_ORGANISATION,X as UNRESOLVED,L as VpcPeerAccepterResourcePlanSchema,U as VpcPeerResourcePlanSchema,v as addCrossPlanConnection,o as addResource,u as addVpcPeer,R as addVpcPeerAccepter,s as appendAccountToStage,re as buildEgressBlockedEvent,oe as buildFiredEvent,te as buildGateFailedEvent,ce as buildGatePassedEvent,pe as buildRejectedEvent,me as buildSucceededEvent,ae as buildTimeoutEvent,Y as computeLinesDelta,l as connectResourceToCompute,k as detectDrift,se as estimateCostUsd,W as evaluateEmitGuard,de as gateFallbackOutput,y as listResources,B as mergeProperties,C as modifyCrossPlanConnection,m as modifyResource,f as modifyVpcPeer,x as modifyVpcPeerAccepter,I as parse,N as removeCrossPlanConnection,c as removeResource,T as removeVpcPeer,S as removeVpcPeerAccepter,G as resolveConstructByLiteralProperty,b as resolveDriftPolicy,ue as runFallback,E as setContainerSecrets,fe as shouldTryFallback,M as snapshotProperties};
1
+ import{addResource as o}from"./edits/addResource.js";import{removeResource as c}from"./edits/removeResource.js";import{modifyResource as m}from"./edits/modifyResource.js";import{appendAccountToStage as s}from"./edits/appendAccountToStage.js";import{setContainerSecrets as E}from"./edits/setContainerSecrets.js";import{connectResourceToCompute as l}from"./edits/connectResourceToCompute.js";import{addVpcPeer as f,modifyVpcPeer as u,removeVpcPeer as T}from"./edits/vpcPeer.js";import{addVpcPeerAccepter as x,modifyVpcPeerAccepter as P,removeVpcPeerAccepter as S}from"./edits/vpcPeerAccepter.js";import{addCrossPlanConnection as v,modifyCrossPlanConnection as C,removeCrossPlanConnection as N}from"./edits/crossPlanConnection.js";import{resolveDriftPolicy as b}from"./edits/driftPolicy.js";import{listResources as y}from"./listResources.js";import{resolveConstructByLiteralProperty as G}from"./semanticIndex/index.js";import{parse as I}from"./fileRewriter/parse.js";import{CrossPlanConnectionResourcePlanSchema as F,VpcPeerAccepterResourcePlanSchema as L,VpcPeerResourcePlanSchema as U}from"../schemas/index.js";import{detectDrift as k,mergeProperties as B,snapshotProperties as M}from"./drift/index.js";import{computeLinesDelta as K}from"./_internal.js";import{ResourceNameSchema as q,StatementTypeSchema as w}from"./types.js";import{REGISTERED_STATEMENT_TYPES as H,STATEMENT_REGISTRY as J}from"./registry.js";import{evaluateEmitGuard as W,UNRESOLVED as X}from"./emitGuard.js";import{projectionRefusals as $}from"./emitProjection.js";import{CODEMOD_ERROR_KINDS as re}from"./telemetry/errorKinds.js";import{buildEgressBlockedEvent as te,buildFiredEvent as ce,buildGateFailedEvent as pe,buildGatePassedEvent as me,buildRejectedEvent as ae,buildSucceededEvent as se,buildTimeoutEvent as ne,estimateCostUsd as Ee,FALLBACK_EVENTS as de,GATE_EVENTS as le,gateFallbackOutput as ie,PARSE_GATE as fe,RUNTIME_GATE as ue,runFallback as Te,shouldTryFallback as Re,UNATTRIBUTED_ORGANISATION as xe}from"./llmFallback/index.js";export{re as CODEMOD_ERROR_KINDS,F as CrossPlanConnectionResourcePlanSchema,de as FALLBACK_EVENTS,le as GATE_EVENTS,fe as PARSE_GATE,H as REGISTERED_STATEMENT_TYPES,ue as RUNTIME_GATE,q as ResourceNameSchema,J as STATEMENT_REGISTRY,w as StatementTypeSchema,xe as UNATTRIBUTED_ORGANISATION,X as UNRESOLVED,L as VpcPeerAccepterResourcePlanSchema,U as VpcPeerResourcePlanSchema,v as addCrossPlanConnection,o as addResource,f as addVpcPeer,x as addVpcPeerAccepter,s as appendAccountToStage,te as buildEgressBlockedEvent,ce as buildFiredEvent,pe as buildGateFailedEvent,me as buildGatePassedEvent,ae as buildRejectedEvent,se as buildSucceededEvent,ne as buildTimeoutEvent,K as computeLinesDelta,l as connectResourceToCompute,k as detectDrift,Ee as estimateCostUsd,W as evaluateEmitGuard,ie as gateFallbackOutput,y as listResources,B as mergeProperties,C as modifyCrossPlanConnection,m as modifyResource,u as modifyVpcPeer,P as modifyVpcPeerAccepter,I as parse,$ as projectionRefusals,N as removeCrossPlanConnection,c as removeResource,T as removeVpcPeer,S as removeVpcPeerAccepter,G as resolveConstructByLiteralProperty,b as resolveDriftPolicy,Te as runFallback,E as setContainerSecrets,Re as shouldTryFallback,M as snapshotProperties};
@@ -30,18 +30,19 @@ export interface StatementTypeEntry {
30
30
  /**
31
31
  * The ADD path's required-aware input contract: the plan schema minus
32
32
  * `name`, WITHOUT `.partial()`, so a first-time add must carry every
33
- * required plan key (`database` demands `type` + `databaseName`) as well
34
- * as satisfying the per-key shapes. `schemaFragment` stays the partial
35
- * delta contract for MODIFY.
33
+ * required plan key (`database` demands `type` + `databaseName`; `compute`
34
+ * demands `type` + `needsConnection`) as well as satisfying the per-key
35
+ * shapes. `schemaFragment` stays the partial delta contract for MODIFY.
36
36
  *
37
37
  * Declared ONLY for guard-free types, and that is a rule, not a
38
- * convenience: a guarded type's required plan keys are exactly the
39
- * scaffold-consumed keys its own guard REFUSES to write
40
- * (`compute.needsConnection` is required by the plan schema and
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.
38
+ * convenience: a guarded type's required plan keys would be exactly the
39
+ * scaffold-consumed keys its own guard refuses to write — the catch-22
40
+ * `compute` sat in until its projection landed (its guard `silentlyDropped`
41
+ * the required `needsConnection`, so demanding it at the gate was
42
+ * unsatisfiable; the projection's expansion now CONSUMES it). When an
43
+ * increment lands a type's projection and deletes its guard table, declare
44
+ * `addContract` in the same edit — the schema-gate XOR test pins the
45
+ * pairing. `vpc-peer`, the one guarded type left, correctly declares none.
45
46
  *
46
47
  * Absent = the schema gate applies `schemaFragment` to adds too
47
48
  * (unknown-key and per-key-shape checks only, no required demand).
@@ -87,21 +88,19 @@ export interface StatementTypeEntry {
87
88
  * ONLY for types the plan→construct projection cannot describe. Where it
88
89
  * can (`src/projection/tables.ts`), the disagreement is TRANSLATED at
89
90
  * emission and no rule belongs here — `emitProjection.ts` reports what is
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.
91
+ * left from the projection's own `unprojectable` list; a live sibling
92
+ * binding is expressible through a `live` rule (`cdn`'s origin), a second
93
+ * statement through a sibling projection (`compute`'s schedule), a merged
94
+ * prop through a fragment expansion (`compute`'s connection keys), and a
95
+ * key combination through a `crossKey` rule. What survives is `vpc-peer`
96
+ * alone: required-key conditions whose absence deploys silently wrong
97
+ * infrastructure.
94
98
  *
95
- * Rules cover exactly two classes, and nothing else:
96
- *
97
- * · UNTRANSLATABLE — the construct requires a value no CLI flag can
98
- * carry (a CDK `Code`/`Architecture`, an `IBucket`, a live sibling
99
- * construct). No invocation can ever synthesise, so writing one is
100
- * always wrong.
101
- * · SILENTLY DROPPED — the construct never reads the plan key, so the
102
- * resource deploys without the behaviour that was requested. Proven
103
- * per key: zero `props.<key>` reads anywhere in
104
- * @fjall/components-infrastructure.
99
+ * The one class rules still cover: a required key whose ABSENCE deploys
100
+ * silently wrong infrastructure — synth succeeds, the deploy is green, and
101
+ * the emitted resource points at garbage (`vpc-peer`'s two required
102
+ * members). Refusing at write time is the only net, because `cdk.json`
103
+ * runs `infrastructure.ts` through tsx, which does not typecheck.
105
104
  *
106
105
  * Deliberately NOT covered: INCOMPLETE input, where the vocabulary can
107
106
  * express a working call and the operator has not yet — that is
@@ -114,9 +113,9 @@ export interface StatementTypeEntry {
114
113
  * Absent = every expressible property shape for this type reaches a
115
114
  * synthesising statement. That is a claim, not an oversight, and it is now
116
115
  * true two ways: `network` and `buildkite` have outright plan/construct
117
- * parity, while `database`, `storage` and `messaging` reach it through the
118
- * projection. The components-infrastructure materialisation contract pins
119
- * both.
116
+ * parity, while `database`, `storage`, `messaging` and `compute` reach it
117
+ * through the projection. The components-infrastructure materialisation
118
+ * contract pins both.
120
119
  */
121
120
  emitGuard?: ReadonlyArray<EmitGuardRule>;
122
121
  /**
@@ -1 +1 @@
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};
1
+ var R=Object.defineProperty;var r=(e,t)=>R(e,"name",{value:t,configurable:!0});import{DEPLOYABLE_PATTERN_TYPES as I,PATTERN_TYPE_VALUES as N,isPatternType as M,patternConstructId as m}from"@fjall/util";import{z as s}from"zod";import{BuildkitePropsObjectSchema as u,CDNResourcePlanSchema as l,ComputeResourcePlanSchema as g,CrossPlanConnectionResourcePlanSchema as h,DatabaseResourcePlanSchema as y,NetworkResourcePlanSchema as P,NextJSPatternConfigSchema as v,OrganisationResourcePlanSchema as S,PayloadPatternConfigSchema as w,S3ResourcePlanSchema as C,SQSResourcePlanSchema as f,StaticSitePatternConfigSchema as O,VpcPeerAccepterResourcePlanSchema as F,VpcPeerResourcePlanSchema as W}from"../schemas/index.js";import{failure as x}from"../types/Result.js";import{whenAbsent as b}from"./emitGuard.js";const n={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 c(e){const{name:t,...a}=e.shape;return s.object(a).partial().strict()}r(c,"fragmentOf");function p(e){const{name:t,...a}=e.shape;return s.object(a).strict()}r(p,"completeFragmentOf");const j=c(y),B=c(C),V=(()=>{const{name:e,...t}=g.shape;return s.object(t).partial().strict()})(),D=(()=>{const{name:e,...t}=g.shape;return s.object(t).strict()})(),Y=c(f),G=c(l),L=c(P),E=(()=>{const{name:e,type:t,cdn:a,...A}=w.shape,{name:te,type:re,cdn:ae,...T}=v.shape,{name:ne,type:oe,domain:ce,cdn:_,...k}=O.shape;return{...A,...T,...k,cdn:s.union([a,_]),type:s.enum(I)}})(),q=s.object(E).partial().strict(),Q=s.object(E).partial().required({type:!0}).strict(),U={emitConstructId:r((e,t)=>M(t.type)?m(e,t.type):e,"emitConstructId"),locateCandidates:r(e=>[e,...N.map(t=>m(e,t))],"locateCandidates")},$=c(W),z=c(F),J=c(h),H=c(S),K=c(u);function d(e,t){return x({kind:"SemanticQueryError",reason:`StatementTypeEntry.${e} for ${t} is not wired; existing types dispatch through the shared locator/generator.`})}r(d,"notWiredError");function X(e){return{factoryIdentifier:e,findByShape:r(()=>d("locator.findByShape",e),"findByShape"),validateContext:r(()=>d("locator.validateContext",e),"validateContext")}}r(X,"createLocator");function Z(e){return{build:r(()=>{const t=d("generator.build",e);throw new Error(t.success?"unreachable":t.error.reason)},"build")}}r(Z,"createGenerator");const ee=[{property:"peerAppName",refuseWhen:b("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:b("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,a={}){return{factoryIdentifier:e,locator:X(e),generator:Z(e),schemaFragment:t,...a}}r(o,"createEntry");const i={database:o(n.database,j,{emitWrapper:{appMethod:"addDatabase"},addContract:p(y)}),storage:o(n.storage,B,{emitWrapper:{appMethod:"addStorage"},addContract:p(C)}),compute:o(n.compute,V,{emitWrapper:{appMethod:"addCompute"},addContract:D}),messaging:o(n.messaging,Y,{emitWrapper:{appMethod:"addMessaging"},addContract:p(f)}),cdn:o(n.cdn,G,{emitWrapper:{appMethod:"addCdn"},addContract:p(l)}),network:o(n.network,L,{emitWrapper:{appMethod:"addNetwork"},addContract:p(P)}),pattern:o(n.pattern,q,{emitWrapper:{appMethod:"addPattern"},addContract:Q,statementIdentity:U}),"vpc-peer":o(n["vpc-peer"],$,{emitWrapper:{appMethod:"addVpcPeer"},emitGuard:ee}),"vpc-peer-accepter":o(n["vpc-peer-accepter"],z,{emitWrapper:{appMethod:"addVpcPeerAccepter"},addContract:p(F)}),"cross-plan-connection":o(n["cross-plan-connection"],J,{addContract:p(h)}),organisation:o(n.organisation,H,{addContract:p(S)}),buildkite:o(n.buildkite,K,{emitWrapper:{appMethod:"addBuildkite",propsOverloadConstructId:"Buildkite"},addContract:p(u)})},le=Object.keys(i);function ge(e,t,a){return i[e].statementIdentity?.emitConstructId(t,a)??t}r(ge,"constructIdFor");function he(e,t){return i[e].statementIdentity?.locateCandidates(t)??[t]}r(he,"locateNamesFor");function ye(e){const t=Object.keys(i);for(const a of t)if(i[a].factoryIdentifier===e)return a}r(ye,"findTypeByIdentifier");export{n as FACTORY_IDENTIFIERS,le as REGISTERED_STATEMENT_TYPES,i as STATEMENT_REGISTRY,ge as constructIdFor,ye as findTypeByIdentifier,he as locateNamesFor};
@@ -1 +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};
1
+ var N=Object.defineProperty;var d=(r,t)=>N(r,"name",{value:t,configurable:!0});import{failure as c,success as p}from"../../../types/Result.js";import{isProjectableType as v,TYPE_PROJECTIONS as k}from"../../../projection/index.js";import{extractProgramBody as O}from"../../_internal.js";import{findConfigObject as w}from"../../drift/snapshot.js";import{bodyCarriesSibling as E,siblingCallPropsObject as K}from"../../edits/addResource/siblingEmission.js";import{astToLiteral as S,isNodeShape as T,NON_LITERAL as x,readKeyName as j}from"../../edits/modifyResource/literalConversion.js";import{parse as M}from"../../fileRewriter/parse.js";import{constructIdFor as C,STATEMENT_REGISTRY as L}from"../../registry.js";import{locateByShape as R}from"../../semanticIndex/locateByShape.js";function _(r){if(!v(r))return[];const t=k[r].keys??{};return Object.entries(t).flatMap(([e,i])=>i.kind==="live"?[{planKey:e,constructKey:i.as??e,reason:i.reason,wants:"live binding"}]:i.kind==="structured"?[{planKey:e,constructKey:i.as??e,reason:i.reason,wants:"structured CDK expression"}]:[])}d(_,"shapeCheckedKeys");function u(r){return p({action:"reject",diagnostics:r})}d(u,"rejectOutcome");function A(r,t){if(r.plan.op!=="modify"||t?.expansion===void 0)return;const e=t.expansion;if(!e.keys.some(f=>r.plan.properties[f]!==void 0))return;const i=e.derive(r.plan.properties,{resourceName:r.plan.name,editingExisting:!0}),[o]=i.unprojectable;if(o!==void 0)return c({kind:"InvalidPropertyError",property:o.key,reason:o.reason})}d(A,"expansionModifyParity");const W={id:"projection",run(r){const{type:t,name:e,op:i}=r.plan,o=v(t)?k[t]:void 0,f=A(r,o);if(f!==void 0)return f;const I=o?.siblings!==void 0?o.siblings.keys.filter(n=>r.plan.properties[n]!==void 0):[];let y=[];if(I.length>0&&o?.siblings!==void 0){const n=o.siblings.derive(r.plan.properties,{resourceName:e}),[s]=n.unprojectable;if(s!==void 0)return c({kind:"InvalidPropertyError",property:s.key,reason:s.reason});const[a]=n.siblings;if(i!=="add"&&a!==void 0)return c({kind:"InvalidPropertyError",property:a.planKey,reason:`\`${a.planKey}\` expands into its own \`app.${a.appMethod}("${a.constructId}", \u2026)\` statement, and a modify edits exactly one statement \u2014 the deterministic lane refuses this edit and the fallback lane holds the same line.`});y=n.siblings}const m=_(t),h=i==="add"&&L[t].statementIdentity!==void 0;if(m.length===0&&!h&&y.length===0)return p({action:"proceed"});const l=B(r);if(!l.success)return l;if(l.data===void 0)return u(`could not resolve the emitted config object for ${t} "${e}"; the output cannot be verified against the projection contract`);const{symbolName:b,configObject:g,body:$}=l.data;for(const n of m){const s=P(g,n.constructKey);if(s!==void 0&&(s.type==="StringLiteral"||s.type==="TemplateLiteral"))return c({kind:"InvalidPropertyError",property:n.constructKey,reason:`the ${t} construct reads a ${n.wants} here and the output wrote a ${s.type==="StringLiteral"?"string":"template"} literal; ${n.reason}`})}for(const n of y){if(!E($,n))return u(`the \`${n.planKey}\` key expands into a second statement \`app.${n.appMethod}("${n.constructId}", \u2026)\` and the output does not carry it \u2014 the primary resource would deploy with the expansion silently missing`);if(n.props.length===0)continue;const s=K($,n);if(s===void 0)return u(`the \`${n.planKey}\` expansion statement \`app.${n.appMethod}("${n.constructId}", \u2026)\` carries no readable props object, so it cannot be verified against the props the projection derives (${n.props.map(a=>`\`${a.key}\``).join(", ")})`);for(const a of n.props)if(P(s,a.key)===void 0)return u(`the \`${n.planKey}\` expansion statement \`app.${n.appMethod}("${n.constructId}", \u2026)\` omits the derived prop \`${a.key}\` \u2014 the deterministic lane always emits it, and without it the expansion deploys wrong or incomplete infrastructure`)}if(h){const n=G(g),s=C(t,e,n);if(b!==s)return u(`output writes construct id "${b}" where the ${t} identity derivation for "${e}" requires "${s}" \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 p({action:"proceed"})}};function B(r){const t=R(r.content,{type:r.plan.type,name:r.plan.name});if(!t.success)return c(t.error);if(t.data===void 0)return p(void 0);const e=M(r.content,t.data.filePath);if(!e.success)return c(e.error);const i=O(e.data);if(i===void 0)return p(void 0);const o=w(i,{start:t.data.start,length:t.data.length});return o===void 0?p(void 0):p({symbolName:t.data.symbolName,configObject:o,body:i})}d(B,"locateEmittedStatement");function P(r,t){for(const e of r.properties)if(!(e.type!=="Property"&&e.type!=="ObjectProperty")&&!(e.shorthand===!0||e.computed===!0)&&j(e.key)===t&&e.value!==void 0&&T(e.value))return e.value}d(P,"readPropertyValueNode");function G(r){const t={};for(const e of r.properties){if(e.type!=="Property"&&e.type!=="ObjectProperty"||e.shorthand===!0||e.computed===!0)continue;const i=j(e.key);if(i===void 0||e.value===void 0)continue;const o=S(e.value);o!==x&&(t[i]=o)}return t}d(G,"literalPropertiesOf");export{W as projectionGate};