@stackwright-services/compiler 0.0.1

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.
@@ -0,0 +1,423 @@
1
+ import { CapabilityRegistry } from '@stackwright-services/capabilities';
2
+ import { DataSourceScanResult } from '@stackwright-services/runtime';
3
+ import { FlowDefinition, WorkflowDefinition, RequiredPermission, Trigger } from '@stackwright-services/types';
4
+
5
+ /**
6
+ * Validation result -- either success with parsed data, or failure with errors.
7
+ */
8
+ interface ValidationResult<T = unknown> {
9
+ success: boolean;
10
+ data?: T;
11
+ errors: ValidationError[];
12
+ warnings: ValidationWarning[];
13
+ }
14
+ interface ValidationError {
15
+ code: string;
16
+ message: string;
17
+ path?: string;
18
+ }
19
+ interface ValidationWarning {
20
+ code: string;
21
+ message: string;
22
+ path?: string;
23
+ }
24
+ /**
25
+ * Options for validation — injectable for testing.
26
+ */
27
+ interface ValidateOptions {
28
+ /**
29
+ * Injectable scanDataSources function — defaults to the real runtime scan.
30
+ * Pass a mock in tests to avoid depending on installed packages.
31
+ */
32
+ scanDataSources?: () => DataSourceScanResult;
33
+ }
34
+ /**
35
+ * Detect whether a parsed YAML object is a flow or workflow.
36
+ */
37
+ declare function detectDefinitionType(obj: unknown): 'flow' | 'workflow' | 'unknown';
38
+ /**
39
+ * Validate a YAML string as a flow or workflow definition.
40
+ *
41
+ * Pass 1: Schema validation (Zod parse)
42
+ * Pass 2: Semantic validation (capability refs, state graph, etc.)
43
+ *
44
+ * @param yamlContent - Raw YAML string
45
+ * @param registry - Capability registry for ref validation (optional)
46
+ */
47
+ declare function validate(yamlContent: string, registry?: CapabilityRegistry, options?: ValidateOptions): ValidationResult;
48
+ /**
49
+ * Validate a flow definition (two-pass).
50
+ */
51
+ declare function validateFlow(parsed: unknown, registry?: CapabilityRegistry, options?: ValidateOptions): ValidationResult<FlowDefinition>;
52
+ /**
53
+ * Validate a workflow definition (two-pass).
54
+ */
55
+ declare function validateWorkflow(parsed: unknown, registry?: CapabilityRegistry): ValidationResult<WorkflowDefinition>;
56
+
57
+ /**
58
+ * A derived permission set -- the minimal set of permissions a service needs.
59
+ * Computed by walking all capability invocations and collecting their requiredPermissions.
60
+ */
61
+ interface DerivedPermissions {
62
+ /** All required permissions, deduplicated */
63
+ permissions: RequiredPermission[];
64
+ /** Which capabilities contributed each permission (for audit trail) */
65
+ sources: PermissionSource[];
66
+ }
67
+ interface PermissionSource {
68
+ /** The permission */
69
+ permission: RequiredPermission;
70
+ /** The capability that requires it */
71
+ capabilityName: string;
72
+ /** Where in the definition it's invoked */
73
+ location: string;
74
+ }
75
+ /**
76
+ * Derive the minimal permission set from a flow definition.
77
+ *
78
+ * Walks all steps, looks up each capability in the registry,
79
+ * collects requiredPermissions from Effect capabilities.
80
+ * Transform capabilities contribute no permissions (they're pure).
81
+ *
82
+ * Permission variables (e.g. `$url`) are resolved from step parameters
83
+ * at compile time for static permission derivation.
84
+ *
85
+ * @security-critical -- This is least-privilege derivation.
86
+ * The output of this function determines the IAM policy / RBAC manifest.
87
+ */
88
+ declare function deriveFlowPermissions(flow: FlowDefinition, registry: CapabilityRegistry): DerivedPermissions;
89
+ /**
90
+ * Derive the minimal permission set from a workflow definition.
91
+ *
92
+ * Walks all states' on_enter actions, collects requiredPermissions
93
+ * from Effect capabilities.
94
+ */
95
+ declare function deriveWorkflowPermissions(workflow: WorkflowDefinition, registry: CapabilityRegistry): DerivedPermissions;
96
+ /**
97
+ * Generate an AWS IAM policy document from derived permissions.
98
+ * Used by the Lambda compile target.
99
+ */
100
+ declare function toIamPolicy(derived: DerivedPermissions): object;
101
+ /**
102
+ * Generate an IAM policy for a Step Functions execution role.
103
+ *
104
+ * Includes:
105
+ * - Lambda invoke permissions for each state's on_enter handler
106
+ * - Capability-derived permissions (from deriveWorkflowPermissions)
107
+ * - EventBridge permissions if timeouts are configured
108
+ * - CloudWatch Logs for execution logging
109
+ *
110
+ * @security-critical
111
+ */
112
+ declare function toStepFunctionsIamPolicy(workflow: WorkflowDefinition, derived: DerivedPermissions): object;
113
+ /**
114
+ * Generate a Kubernetes RBAC rule set from derived permissions.
115
+ * Used by the Helm compile target.
116
+ */
117
+ declare function toRbacRules(derived: DerivedPermissions): object[];
118
+
119
+ /**
120
+ * The ObservabilityProvider interface -- what the generated code calls.
121
+ * This is defined here for code generation; the runtime package implements it.
122
+ */
123
+ interface ObservabilityProvider {
124
+ /** Start a span for a capability invocation */
125
+ startSpan(name: string, attributes?: Record<string, string | number | boolean>): SpanHandle;
126
+ /** Record a metric */
127
+ recordMetric(name: string, value: number, tags?: Record<string, string>): void;
128
+ }
129
+ interface SpanHandle {
130
+ /** Set attributes on the span */
131
+ setAttribute(key: string, value: string | number | boolean): void;
132
+ /** Mark the span as errored */
133
+ setError(error: Error): void;
134
+ /** End the span */
135
+ end(): void;
136
+ }
137
+ /**
138
+ * Instrumentation metadata for a single step.
139
+ */
140
+ interface StepInstrumentation {
141
+ /** Step name (used as span name) */
142
+ stepName: string;
143
+ /** Capability name (span attribute) */
144
+ capabilityName: string;
145
+ /** Flow ID (span attribute) */
146
+ flowId: string;
147
+ /** Metric names */
148
+ metrics: {
149
+ latency: string;
150
+ invocations: string;
151
+ errors: string;
152
+ };
153
+ }
154
+ /**
155
+ * Generate instrumentation metadata for all steps in a flow.
156
+ */
157
+ declare function generateFlowInstrumentation(flow: FlowDefinition): StepInstrumentation[];
158
+ /**
159
+ * Sanitize a string to be a valid TypeScript identifier.
160
+ */
161
+ declare function sanitizeIdentifier(name: string): string;
162
+ /**
163
+ * Generate the TypeScript code for an instrumented step handler.
164
+ *
165
+ * This is what the compiler emits into the generated handler file.
166
+ * Each step becomes an async function wrapped with observability AND audit.
167
+ */
168
+ declare function generateInstrumentedStepCode(instrumentation: StepInstrumentation): string;
169
+ /**
170
+ * Generate the full instrumented handler code for a flow.
171
+ * Returns the complete TypeScript source for the compiled handler.
172
+ */
173
+ declare function generateInstrumentedFlowCode(flow: FlowDefinition): string;
174
+
175
+ /**
176
+ * Instrumentation metadata for a single workflow state handler.
177
+ * Mirrors StepInstrumentation but is scoped to state machine semantics:
178
+ * transitions, dwell time, timeout events.
179
+ */
180
+ interface WorkflowStateInstrumentation {
181
+ /** State name */
182
+ stateName: string;
183
+ /** Capability invoked on entry (from on_enter.use) */
184
+ capabilityName: string;
185
+ /** Workflow ID */
186
+ workflowId: string;
187
+ /** Whether this is a terminal state */
188
+ isTerminal: boolean;
189
+ /** Metric name constants */
190
+ metrics: {
191
+ /** Counter — fires each time the state is entered */
192
+ invocations: string;
193
+ /** Counter — fires when the on_enter capability throws */
194
+ errors: string;
195
+ /** Histogram — on_enter handler execution time in ms */
196
+ latency: string;
197
+ /** Counter — per-signal transition counts into this state */
198
+ transitions: string;
199
+ };
200
+ }
201
+ /**
202
+ * Generate instrumentation metadata for all states that have an on_enter
203
+ * capability in the workflow. States without on_enter have no Lambda handler
204
+ * and are therefore excluded.
205
+ */
206
+ declare function generateWorkflowInstrumentation(workflow: WorkflowDefinition): WorkflowStateInstrumentation[];
207
+ /**
208
+ * Generate a complete TypeScript Lambda handler file for an instrumented
209
+ * workflow state.
210
+ *
211
+ * The generated handler:
212
+ * - Opens a span per state execution (one span = one transition through the state)
213
+ * - Records a `workflow.state.transitions` counter tagged with the incoming signal
214
+ * - Records `workflow.state.invocations` / `workflow.state.errors` counters
215
+ * - Records `workflow.state.latency` histogram (on_enter execution time)
216
+ * - Emits an AuditProvider record on both success and error paths
217
+ *
218
+ * Providers default to noop — wire real implementations via your DI bootstrap.
219
+ */
220
+ declare function generateInstrumentedStateHandlerCode(instrumentation: WorkflowStateInstrumentation, capabilityParams: Record<string, unknown>): string;
221
+
222
+ /**
223
+ * Output files generated by the Lambda compile target.
224
+ */
225
+ interface LambdaTarget {
226
+ /** Generated files, keyed by relative path */
227
+ files: Record<string, string>;
228
+ }
229
+ /**
230
+ * Compile a flow definition to Lambda target output.
231
+ *
232
+ * Generates:
233
+ * - handler.ts: Hono app entry point wired to the compiled flow
234
+ * - Dockerfile: AWS Lambda Web Adapter base image
235
+ * - iam-policy.json: Least-privilege IAM policy from derived permissions
236
+ * - package.json: Minimal package for the Lambda function
237
+ */
238
+ declare function compileLambdaTarget(flow: FlowDefinition, permissions: DerivedPermissions, instrumentedCode: string): LambdaTarget;
239
+ /**
240
+ * Return additional IAM policy statements required by the trigger type.
241
+ * These are merged with capability-derived permissions at compile time.
242
+ */
243
+ declare function getTriggerPermissions(trigger: Trigger): object[];
244
+
245
+ /**
246
+ * Output files generated by the Helm compile target.
247
+ */
248
+ interface HelmTarget {
249
+ /** Generated files, keyed by relative path */
250
+ files: Record<string, string>;
251
+ }
252
+ /**
253
+ * Compile a flow definition to Helm chart output.
254
+ *
255
+ * Generates a complete Helm chart compatible with the Defense Unicorns ecosystem.
256
+ */
257
+ declare function compileHelmTarget(flow: FlowDefinition, permissions: DerivedPermissions): HelmTarget;
258
+ declare function sanitizeChartName(name: string): string;
259
+
260
+ /**
261
+ * Output files generated by the Step Functions compile target.
262
+ */
263
+ interface StepFunctionsTarget {
264
+ /** Generated files, keyed by relative path */
265
+ files: Record<string, string>;
266
+ }
267
+ /**
268
+ * Compile a workflow definition to AWS Step Functions target output.
269
+ *
270
+ * Generates:
271
+ * - asl-definition.json: Step Functions state machine definition in ASL
272
+ * - iam-execution-role.json: IAM policy for the execution role
273
+ * - handlers/<state>.ts: Lambda handler stubs for each state with on_enter
274
+ * - package.json: Minimal package for the handlers
275
+ */
276
+ declare function compileStepFunctionsTarget(workflow: WorkflowDefinition, permissions: DerivedPermissions): StepFunctionsTarget;
277
+ /**
278
+ * Convert an ISO 8601 duration string to seconds.
279
+ *
280
+ * Uses calendar approximations: 1 year = 365 days, 1 month = 30 days.
281
+ *
282
+ * @example
283
+ * iso8601ToSeconds('P3D') // → 259200
284
+ * iso8601ToSeconds('PT1H') // → 3600
285
+ * iso8601ToSeconds('PT30M') // → 1800
286
+ */
287
+ declare function iso8601ToSeconds(duration: string): number;
288
+
289
+ /**
290
+ * Output from the OpenAPI spec compile target.
291
+ */
292
+ interface OpenApiTarget {
293
+ /** Generated files, keyed by relative path */
294
+ files: Record<string, string>;
295
+ }
296
+ /**
297
+ * Options for OpenAPI spec generation.
298
+ */
299
+ interface OpenApiTargetOptions {
300
+ /** Optional build key to include as an apiKey security scheme */
301
+ buildKey?: string;
302
+ /** Optional server URL for the spec */
303
+ serverUrl?: string;
304
+ /** Optional version string (defaults to "0.0.1") */
305
+ version?: string;
306
+ }
307
+ /**
308
+ * Compile an HTTP-triggered flow definition to an OpenAPI 3.1 specification.
309
+ *
310
+ * This is the bridge between Services and Pro: the generated spec is consumed
311
+ * by Pro's existing OpenAPI parser to produce typed Pulse clients.
312
+ *
313
+ * For non-HTTP triggers, returns an empty target (no spec to generate).
314
+ * AsyncAPI for event-driven flows is future work.
315
+ *
316
+ * @returns OpenApiTarget with openapi.yaml content, or empty files for non-HTTP triggers
317
+ */
318
+ declare function compileOpenApiTarget(flow: FlowDefinition, options?: OpenApiTargetOptions): OpenApiTarget;
319
+
320
+ /**
321
+ * Auth requirements derived from a definition.
322
+ * The compiler emits these into target-specific auth configuration.
323
+ */
324
+ interface DerivedAuthRequirements {
325
+ /** Whether any auth is required at all */
326
+ requiresAuth: boolean;
327
+ /** All roles referenced in guard_role transitions */
328
+ requiredRoles: string[];
329
+ /** Locations where auth is referenced (for audit trail) */
330
+ sources: AuthRequirementSource[];
331
+ }
332
+ interface AuthRequirementSource {
333
+ /** The role required */
334
+ role: string;
335
+ /** Where in the definition it's referenced */
336
+ location: string;
337
+ }
338
+ /**
339
+ * Derive auth requirements from a flow definition.
340
+ *
341
+ * For now, flows require auth if the HTTP trigger has auth config.
342
+ * (Future: auth field on trigger schema)
343
+ *
344
+ * @security-critical — Determines whether auth is wired into compiled output.
345
+ */
346
+ declare function deriveFlowAuthRequirements(flow: FlowDefinition): DerivedAuthRequirements;
347
+ /**
348
+ * Derive auth requirements from a workflow definition.
349
+ *
350
+ * Walks all states' transitions, collects guard_role references.
351
+ * If ANY transition has guard_role, the workflow requires auth.
352
+ *
353
+ * @security-critical — Determines auth wiring in compiled output.
354
+ */
355
+ declare function deriveWorkflowAuthRequirements(workflow: WorkflowDefinition): DerivedAuthRequirements;
356
+ /**
357
+ * Generate Lambda authorizer configuration from auth requirements.
358
+ * Used by the Lambda/Step Functions compile targets.
359
+ */
360
+ declare function toLambdaAuthorizerConfig(auth: DerivedAuthRequirements): object | null;
361
+ /**
362
+ * Generate Helm auth annotations from auth requirements.
363
+ * Used by the Helm compile target.
364
+ */
365
+ declare function toHelmAuthAnnotations(auth: DerivedAuthRequirements): Record<string, string>;
366
+
367
+ /**
368
+ * Build-coherence configuration derived from a definition.
369
+ *
370
+ * This is NOT authentication (that's AuthProvider). It's a deployment
371
+ * coherence mechanism that ensures frontend and backend were compiled
372
+ * from the same source at the same time.
373
+ *
374
+ * The key is:
375
+ * - Generated deterministically from flow content + build timestamp
376
+ * - Embedded in the compiled handler as middleware
377
+ * - Embedded in the OpenAPI spec as an apiKey security scheme
378
+ * - Short-lived: replaced every build
379
+ * - NOT a secret: visible in frontend bundle and spec file
380
+ *
381
+ * Works naturally with blue/green and canary deployments —
382
+ * matched frontend/backend pairs share the same key.
383
+ */
384
+ interface BuildCoherenceConfig {
385
+ /** Whether build coherence is enabled */
386
+ enabled: boolean;
387
+ /** The build key (hex string) */
388
+ buildKey: string;
389
+ /** Hash of the flow content (for tracing which definition produced this key) */
390
+ contentHash: string;
391
+ /** Build timestamp (ISO 8601) */
392
+ buildTimestamp: string;
393
+ }
394
+ /**
395
+ * Derive build coherence config from a flow definition.
396
+ *
397
+ * The key is a SHA-256 hash of: flow content (serialized) + build timestamp.
398
+ * Same source + same moment = same key. Different build = different key.
399
+ *
400
+ * Follows the same pattern as deriveFlowAuthRequirements() and
401
+ * deriveFlowPermissions() — pure derivation, no side effects.
402
+ *
403
+ * @security-critical — The build key becomes a deployment coherence check.
404
+ */
405
+ declare function deriveBuildCoherence(flow: FlowDefinition | WorkflowDefinition, options?: {
406
+ timestamp?: string;
407
+ enabled?: boolean;
408
+ }): BuildCoherenceConfig;
409
+ /**
410
+ * Generate Hono middleware code that validates the build-coherence key.
411
+ *
412
+ * Injected into the compiled handler.ts by the Lambda and Helm targets.
413
+ * The middleware runs before the flow handler and rejects requests
414
+ * with a mismatched or missing build key.
415
+ */
416
+ declare function toBuildKeyMiddleware(config: BuildCoherenceConfig): string;
417
+ /**
418
+ * Generate environment variables for the build key.
419
+ * Used by Helm values.yaml and Lambda environment config.
420
+ */
421
+ declare function toBuildKeyEnv(config: BuildCoherenceConfig): Record<string, string>;
422
+
423
+ export { type AuthRequirementSource, type BuildCoherenceConfig, type DerivedAuthRequirements, type DerivedPermissions, type HelmTarget, type LambdaTarget, type ObservabilityProvider, type OpenApiTarget, type OpenApiTargetOptions, type PermissionSource, type SpanHandle, type StepFunctionsTarget, type StepInstrumentation, type ValidationError, type ValidationResult, type ValidationWarning, type WorkflowStateInstrumentation, compileHelmTarget, compileLambdaTarget, compileOpenApiTarget, compileStepFunctionsTarget, deriveBuildCoherence, deriveFlowAuthRequirements, deriveFlowPermissions, deriveWorkflowAuthRequirements, deriveWorkflowPermissions, detectDefinitionType, generateFlowInstrumentation, generateInstrumentedFlowCode, generateInstrumentedStateHandlerCode, generateInstrumentedStepCode, generateWorkflowInstrumentation, getTriggerPermissions, iso8601ToSeconds, sanitizeChartName, sanitizeIdentifier, toBuildKeyEnv, toBuildKeyMiddleware, toHelmAuthAnnotations, toIamPolicy, toLambdaAuthorizerConfig, toRbacRules, toStepFunctionsIamPolicy, validate, validateFlow, validateWorkflow };