Try the Sample Commands
Explore recorded sample responses in this browser demo. Choose a command or type 'help' to see whatβs available.
Deployment configuration repeats the same information in different forms. DuckDeploy starts from JSON Schema, generates an editable form, and compiles the resulting values into deployment manifests.
The approach: The UI derives form state from JSON Schema. Web Workers compile that state into the selected deployment format.
AST-Driven Dynamic UI Synthesis: Parses complex JSON Schema constructs (oneOf, anyOf, $ref, patternProperties) into an intermediate UI node tree (SchemaNode), mapping structural primitives to specialized, accessible input components.
Polymorphic Discriminated Union Store: Utilizes a Zustand-backed normalized state store that isolates inactive polymorphic union branches, preventing stale input parameters from leaking into compiled output manifests.
Off-Main-Thread Web Worker Compilation Pipeline: An isolated Web Worker execution sandbox (manifest.worker.ts) processes AST normalization, Zod constraint validation, and multi-format YAML generation without degrading React 19 rendering frame rates.
flowchart TD
subgraph SchemaIngest [Schema Ingestion & Form Synthesis]
A[JSON Schema / OpenAPI Spec] --> B[Schema AST Normalizer]
B --> C[Dynamic Form Synthesizer]
C --> D[Polymorphic Form State Store]
end
subgraph WorkerThread [Off-Main-Thread Worker Compiler]
D -->|Worker RPC PostMessage| E[AST Manifest Compiler]
E --> F[Cross-Field Rule & Cyclic Check]
F --> G1[Kubernetes Pod/Deployment YAML]
F --> G2[Helm Values Spec]
F --> G3[Cloud Run Knative Spec]
end
subgraph UIReview [Telemetry & Output View]
G1 & G2 & G3 --> H[Monaco Editor Preview & Telemetry Inspector]
H --> I[Dry-Run Deployment Simulation]
end
lib/schema-normalizer.ts)
// Recursive Schema AST Normalizer & Node Synthesis
interface SchemaASTNode {
id: string;
type: "string" | "number" | "boolean" | "object" | "array" | "oneOf" | "anyOf";
title: string;
description?: string;
required?: boolean;
properties?: Record<string, SchemaASTNode>;
items?: SchemaASTNode;
discriminator?: string;
branches?: Record<string, SchemaASTNode>;
}
function normalizeSchemaAST(
rawSchema: Record<string, unknown>,
definitions: Record<string, unknown> = {}
): SchemaASTNode {
if (rawSchema.$ref && typeof rawSchema.$ref === "string") {
const refKey = rawSchema.$ref.replace("#/definitions/", "");
const resolved = definitions[refKey] as Record<string, unknown>;
return normalizeSchemaAST(resolved || {}, definitions);
}
const type = (rawSchema.type as SchemaASTNode["type"]) || "object";
const node: SchemaASTNode = {
id: (rawSchema.$id as string) || ("node-" + Math.random().toString(36).slice(2, 9)),
type,
title: (rawSchema.title as string) || "Configuration Block",
description: rawSchema.description as string | undefined,
};
if (type === "object" && rawSchema.properties) {
node.properties = {};
const rawProps = rawSchema.properties as Record<string, Record<string, unknown>>;
for (const [key, propDef] of Object.entries(rawProps)) {
node.properties[key] = normalizeSchemaAST(propDef, definitions);
}
}
return node;
}
workers/k8s.manifest.worker.ts)
// Web Worker message listener for zero-jank manifest generation
import { dump as yamlDump } from "js-yaml";
addEventListener("message", ({ data }: MessageEvent<{ formState: Record<string, unknown>; target: string }>) => {
const { formState, target } = data;
const startTime = performance.now();
const manifest = {
apiVersion: "apps/v1",
kind: "Deployment",
metadata: {
name: formState.serviceName || "duckdeploy-service",
namespace: formState.namespace || "default",
labels: { "app.kubernetes.io/managed-by": "duckdeploy" },
},
spec: {
replicas: Number(formState.replicas) || 1,
selector: { matchLabels: { app: formState.serviceName } },
template: {
metadata: { labels: { app: formState.serviceName } },
spec: {
containers: [
{
name: "app",
image: formState.imageUri,
resources: {
limits: { cpu: formState.cpuLimit, memory: formState.memoryLimit },
},
},
],
},
},
},
};
postMessage({
yaml: yamlDump(manifest),
durationMs: round(performance.now() - startTime, 2),
});
});
$ref pointers are resolved with depth-bounded pointer caching (depth limit = 16), safely halting cyclic loops while preserving schema expressiveness.