@zamatica/workload-bundle
v0.1.0
Published
Portable workload-bundle schema (Zod) + JSON5 load/write helpers. The wire shape a producer (e.g. a desktop app) writes for a daemon to consume via CI/CD or direct copy. Framework-agnostic.
Readme
@zamatica/workload-bundle
The portable wire shape a workload producer (typically a desktop app) writes for a daemon to consume. Framework-agnostic Zod schema + JSON5 load/write helpers.
Why this exists
The template's post-auth-removal model is "desktop authors workload configs → daemon consumes them" via one of three paths: spawn the daemon locally (internal mode), point at a remote permissive-env daemon (testing mode), or ship a bundle through CI/CD to a production daemon (export mode). All three need a single agreed-upon on-disk shape so that the producer and consumer can iterate independently without breaking the wire contract. That shape is WorkloadBundle.
The template owns the shape, load, and validate steps. What the daemon DOES with the configs — run all, pick one by id, hot-swap on change — is descendant business logic.
Schema
interface WorkloadBundle {
schemaVersion: 1;
acronym: string; // lowercase, matches the target app
createdAt: string; // ISO-8601 UTC
configs: WorkloadConfig[]; // at least one; descendant interprets
secretsManifest: { key: string; required: boolean }[];
artifacts: Record<string, string>; // logical-name → bundle-relative path
}
interface WorkloadConfig {
id: string;
format: 'json5' | 'yaml'; // hint for downstream parsing of `body`
body: unknown; // descendant narrows with its own Zod schema
}body is intentionally unknown — each descendant has its own per-workload schema and narrows after loadWorkloadBundle() returns.
Use
import { loadWorkloadBundle, writeWorkloadBundle } from '@zamatica/workload-bundle';
// Producer side (e.g. apps/desktop deploy/export.ts):
await writeWorkloadBundle('/tmp/my-app-bundle.json5', {
schemaVersion: 1,
acronym: 'mtz',
createdAt: new Date().toISOString(),
configs: [{ id: 'flow-1', format: 'json5', body: { steps: ['a', 'b'] } }],
secretsManifest: [{ key: 'SLACK_TOKEN', required: true }],
artifacts: {},
});
// Consumer side (e.g. apps/headless startup):
const bundle = await loadWorkloadBundle('/etc/mtz/bundle.json5');
for (const cfg of bundle.configs) {
// descendant narrows cfg.body with its own schema
}On-disk format
The bundle file itself is JSON5 (comments + trailing commas allowed) so it survives hand inspection cleanly. The contained configs declare their OWN format via WorkloadConfig.format for downstream parsing — descendants typically use JSON5 for machine-only configs and YAML for human-editable ones.
writeWorkloadBundle does an atomic rename-into-place via a sibling .tmp file so the daemon never observes a half-written bundle under a concurrent reload.
Validation behavior
Both load and write validate against WorkloadBundleSchema with safeParse. Failures throw with a message that leads with the file path so log grepping points at the broken file rather than this lib.
Status
v0.x — schemaVersion: 1 is the only accepted version. When a v2 lands, the loader will accept both with a migration shim; descendants only ever need to read the latest shape.
