@truefoundry/tfy-infra-engine 0.0.0-canary.6233945

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,287 @@
1
+ # @truefoundry/tfy-infra-engine
2
+
3
+ Config-driven HCL templating engine for infrastructure generation with integrity verification.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ yarn add @truefoundry/tfy-infra-engine
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { createEngine } from '@truefoundry/tfy-infra-engine';
15
+ import type { Template, Envelope } from '@truefoundry/tfy-infra-engine';
16
+
17
+ const engine = createEngine();
18
+
19
+ // The engine accepts pre-resolved Template objects.
20
+ // The caller is responsible for fetching/resolving templates.
21
+ const template: Template = {
22
+ metadata: { name: 'sample', version: '0.1.0' },
23
+ jsonSchema: {
24
+ type: 'object',
25
+ required: ['name'],
26
+ properties: {
27
+ name: { type: 'string' },
28
+ environment: { type: 'string', default: 'dev' },
29
+ },
30
+ },
31
+ files: new Map([['tfy_main.tf', '# HCL template content']]),
32
+ source: 'file:///path/to/template',
33
+ version: { major: 'v1', semver: '0.1.0', full: 'v1/0.1.0' },
34
+ };
35
+
36
+ const result = await engine.install({
37
+ template,
38
+ inputs: { name: 'my-cluster', environment: 'production' },
39
+ intentId: 'cluster-prod-001',
40
+ });
41
+
42
+ // result.files is a Map<string, FileEntry> (zone-tagged)
43
+ for (const [path, entry] of result.files) {
44
+ console.log(`${path} [${entry.zone}]:\n${entry.content}`);
45
+ }
46
+
47
+ // result.manifest is a JSON Manifest v1.0
48
+ console.log('Aggregate Hash:', result.manifest.aggregateHash);
49
+ ```
50
+
51
+ ## Features
52
+
53
+ - **Pure transformer**: Accepts pre-resolved `Template` objects -- no I/O for fetching
54
+ - **JSON Schema validation**: Validate inputs against JSON Schema Draft-07 using AJV with `useDefaults`, `allErrors`, and `discriminator` support
55
+ - **Handlebars templating**: 7 built-in helpers for HCL generation
56
+ - **HCL formatting**: Automatic formatting with `tofu fmt`
57
+ - **Deterministic output**: Byte-identical output for identical inputs
58
+ - **Integrity verification**: Zone-based file ownership, `@tfy-status` headers, JSON Manifest, and drift detection
59
+ - **Four-operation API**: `install`, `verify`, `upgrade`, and `hashOnly`
60
+
61
+ ## API
62
+
63
+ ### `createEngine()`
64
+
65
+ Create a new engine instance. Takes no parameters.
66
+
67
+ ```typescript
68
+ const engine = createEngine();
69
+ ```
70
+
71
+ ### `engine.install(envelope)`
72
+
73
+ Generate all files for a new cluster. Returns zone-tagged files with `@tfy-status` headers on platform files and a JSON Manifest.
74
+
75
+ ```typescript
76
+ const result = await engine.install({
77
+ template, // Pre-resolved Template object
78
+ inputs: { name: 'test' },
79
+ intentId: 'cluster-001',
80
+ options: { skipFormat: false },
81
+ });
82
+
83
+ // result.files: Map<string, FileEntry>
84
+ // result.manifest: Manifest (JSON v1.0)
85
+ ```
86
+
87
+ ### `engine.verify(currentFiles, previousManifest)`
88
+
89
+ Check integrity of existing files against a Manifest. Returns a structured drift report.
90
+
91
+ ```typescript
92
+ const result = await engine.verify(currentFiles, previousManifest);
93
+ if (!result.driftReport.valid) {
94
+ for (const entry of result.driftReport.entries) {
95
+ console.log(`${entry.path}: ${entry.type} -- ${entry.details}`);
96
+ }
97
+ }
98
+ ```
99
+
100
+ ### `engine.upgrade(envelope, currentFiles, previousManifest)`
101
+
102
+ Update platform files to new template versions. Performs pre-upgrade drift detection and source mismatch blocking.
103
+
104
+ ```typescript
105
+ const result = await engine.upgrade(envelope, currentFiles, previousManifest);
106
+ if (result.sourceBlocked) {
107
+ console.log('Upgrade blocked: template source mismatch');
108
+ } else if (!result.driftReport.valid) {
109
+ console.log('Pre-upgrade drift detected');
110
+ }
111
+ ```
112
+
113
+ ### `engine.hashOnly(envelope)`
114
+
115
+ Calculate the expected Aggregate Platform Hash without generating files. Used by the Control Plane for fleet status comparison.
116
+
117
+ ```typescript
118
+ const result = await engine.hashOnly(envelope);
119
+ console.log('Expected hash:', result.aggregateHash);
120
+ console.log('File count:', result.fileCount);
121
+ ```
122
+
123
+ ### Input Validation (AJV-based)
124
+
125
+ Validate inputs against a template's JSON Schema. Exported as public utilities.
126
+
127
+ ```typescript
128
+ import {
129
+ createInputValidator,
130
+ validateAndApplyDefaults,
131
+ validateJsonSchemaStructure,
132
+ } from '@truefoundry/tfy-infra-engine';
133
+
134
+ // Check schema is valid
135
+ validateJsonSchemaStructure(template.jsonSchema); // Throws if not object with properties
136
+
137
+ // Compile and validate (AJV resolves local $ref pointers natively)
138
+ const validator = createInputValidator(template.jsonSchema);
139
+ const inputs = { name: 'my-cluster' };
140
+ const result = validateAndApplyDefaults(validator, inputs);
141
+
142
+ if (!result.valid) {
143
+ console.error('Validation errors:', result.errors);
144
+ }
145
+ // inputs now has defaults applied in-place
146
+ ```
147
+
148
+ ### Template JSON Validation
149
+
150
+ Validate the top-level structure of a `template.json` file:
151
+
152
+ ```typescript
153
+ import { validateTemplateJson } from '@truefoundry/tfy-infra-engine';
154
+
155
+ const parsed = JSON.parse(fs.readFileSync('template.json', 'utf-8'));
156
+ const { metadata, jsonSchema } = validateTemplateJson(parsed);
157
+ // metadata: { name, version, description? }
158
+ // jsonSchema: the raw JSON Schema object
159
+ ```
160
+
161
+ ## Envelope Configuration
162
+
163
+ The envelope is the engine's input configuration:
164
+
165
+ ```typescript
166
+ {
167
+ template: templateObject, // Pre-resolved Template object
168
+ inputs: { name: 'my-cluster' }, // Input values
169
+ intentId: 'cluster-prod-001', // Cluster identity (required)
170
+ platformPrefix: 'tfy_', // Platform Zone filename prefix (default: "tfy_")
171
+ options: {
172
+ skipFormat: false, // Skip tofu fmt
173
+ },
174
+ }
175
+ ```
176
+
177
+ ## Template Object
178
+
179
+ The `Template` type represents a fully resolved template:
180
+
181
+ ```typescript
182
+ interface Template {
183
+ metadata: TemplateMetadata; // { name, version, description? }
184
+ jsonSchema: JSONSchema7; // JSON Schema Draft-07 for input validation
185
+ files: Map<string, string>; // HBS template files from src/ (rendered via Handlebars)
186
+ staticFiles: Map<string, string>; // Static files from src/ (copied verbatim)
187
+ source: string; // Source URI for tracking
188
+ version: VersionInfo; // Version metadata
189
+ }
190
+ ```
191
+
192
+ ## Integrity Engine
193
+
194
+ ### Zone Classification
195
+
196
+ Files are classified into two zones based on filename prefix:
197
+
198
+ - **Platform Zone** (`tfy_*` prefix, `manifest.json`): Engine-managed, includes `@tfy-status` headers
199
+ - **User Zone** (no prefix): Customer-owned, excluded from integrity tracking
200
+
201
+ ### `@tfy-status` Header
202
+
203
+ Platform Zone files include a structured metadata header:
204
+
205
+ ```hcl
206
+ # @tfy-status:begin
207
+ # {"managed":true,"source":"github://...","version":"v2.4.1","intent_id":"cluster-001","content_hash":"sha256:..."}
208
+ # @tfy-status:end
209
+
210
+ resource "aws_eks_cluster" "main" {
211
+ ...
212
+ }
213
+ ```
214
+
215
+ ### JSON Manifest
216
+
217
+ Each install/upgrade produces a `manifest.json` containing:
218
+
219
+ - Cluster identity (`intentId`)
220
+ - Template source and version
221
+ - Aggregate Platform Hash
222
+ - Per-file hashes, sources, versions, and zones
223
+
224
+ ### Drift Detection
225
+
226
+ The `verify()` operation detects five types of drift:
227
+
228
+ | Type | Description |
229
+ |------|-------------|
230
+ | `content_drift` | File content hash doesn't match Manifest |
231
+ | `metadata_inconsistency` | Header fields don't match Manifest entry |
232
+ | `source_mismatch` | File source differs from incoming source |
233
+ | `missing_file` | File in Manifest but not on disk |
234
+ | `unexpected_file` | Platform-prefixed file not in Manifest |
235
+
236
+ ## Handlebars Helpers
237
+
238
+ The engine provides 7 focused helpers for HCL generation:
239
+
240
+ ### HCL Formatting
241
+ - `toTerraformValue` - Convert any value to HCL syntax
242
+
243
+ ### Logic
244
+ - `eq` - Strict equality comparison
245
+ - `and` - Logical AND (all arguments truthy)
246
+ - `or` - Logical OR (any argument truthy)
247
+
248
+ ### Type Checking
249
+ - `isString` - Check if value is a string
250
+ - `isDefined` - Check if value is defined (not null/undefined)
251
+ - `isNotEmpty` - Check if value is not empty
252
+
253
+ ## Error Handling
254
+
255
+ All errors are instances of `EngineError` with typed error codes:
256
+
257
+ ```typescript
258
+ import { EngineError, EngineErrorCode } from '@truefoundry/tfy-infra-engine';
259
+
260
+ try {
261
+ await engine.install(envelope);
262
+ } catch (error) {
263
+ if (error instanceof EngineError) {
264
+ switch (error.code) {
265
+ case EngineErrorCode.ENVELOPE_VALIDATION_FAILED:
266
+ console.log('Invalid envelope:', error.message);
267
+ break;
268
+ case EngineErrorCode.INPUT_VALIDATION_FAILED:
269
+ console.log('Invalid inputs:', error.details);
270
+ break;
271
+ case EngineErrorCode.TEMPLATE_JSON_NOT_FOUND:
272
+ console.log('template.json not found in template directory');
273
+ break;
274
+ case EngineErrorCode.TEMPLATE_JSON_INVALID:
275
+ console.log('template.json has invalid structure:', error.message);
276
+ break;
277
+ case EngineErrorCode.TOFU_NOT_FOUND:
278
+ console.log('Install OpenTofu or use skipFormat option');
279
+ break;
280
+ }
281
+ }
282
+ }
283
+ ```
284
+
285
+ ## License
286
+
287
+ MIT