/** * Deterministic normalization and canonical serialization for workflow * contracts. * * Two contracts that describe the same workflow must produce byte-identical * canonical output regardless of how their object keys were ordered in the * source, so `contractHash()` and `deriveWorkflowRevision()` identify the * *content* rather than an accident of construction. Serialization walks the * declared contract shape explicitly instead of generically stringifying the * input, which means an unknown extra property can never leak into the * canonical bytes and change a hash. * * @module core/contract/normalize */ import type { WorkflowContract, WorkflowMessageContract } from './types.ts'; /** * Normalize a workflow contract into its canonical in-memory form. * * Field order follows the declared shape, every open-ended record is * rebuilt with its keys sorted (and omitted entirely when empty), and every * schema fragment is deep-cloned onto a null-prototype object. The result is * a fresh value; the input is not mutated. * * @example * ```ts * import { normalizeWorkflowContract } from '@lostgradient/weft'; * * const normalized = normalizeWorkflowContract({ * name: 'checkout', * workflowVersion: '2.1.0', * signals: { * zeta: {}, * alpha: {}, * }, * }); * console.log(Object.keys(normalized.signals ?? {})); // ['alpha', 'zeta'] * ``` */ export declare function normalizeWorkflowContract(contract: WorkflowContract): WorkflowContract; /** * Serialize one signal/update/query/activity contract's schema pair. * Shared between {@link canonicalWorkflowContractJson} (the full-identity * form) and `hash.ts`'s payload-only serialization, since both walk the same * `{ inputSchema?, outputSchema? }` shape — the only difference between the * two identities is which *top-level* fields are included, not how a single * message/activity entry serializes. * * @internal Exported for reuse within `core/contract/*`; not part of the * package's public API. */ export declare function canonicalMessageContractJson(entry: WorkflowMessageContract): string; /** * @internal Exported for reuse within `core/contract/*`; not part of the * package's public API. */ export declare function canonicalContractRecordJson(record: Readonly>): string; /** * @internal Exported for reuse within `core/contract/*` (`hash.ts`'s * payload-only serialization shares this field-appending logic); not part of * the package's public API. */ export declare function appendContractRecordField(fields: string[], label: 'signals' | 'updates' | 'queries' | 'activities', record: Readonly> | undefined): void; /** * @internal Exported for reuse within `core/contract/*`; not part of the * package's public API. */ export declare function appendSchemaField(fields: string[], label: 'inputSchema' | 'outputSchema', schema: Record | undefined): void; /** * Serialize a workflow contract to its canonical, full-identity JSON string * — the digest input {@link deriveWorkflowRevision} uses, and the value * returned as `WorkflowRevisionManifestParseSuccess.canonicalJson`. * * This is a *different* identity from {@link contractHash}'s payload-only * serialization: it includes `name`, `workflowVersion`, `description`, and * `tags`, so a documentation edit changes this output (and therefore * `revision`) without changing `contractHash`. The output is byte-identical * for equivalent contracts regardless of source key order, and every field * this project cares about changes it. * * @example * ```ts * import { canonicalWorkflowContractJson } from '@lostgradient/weft'; * * const left = canonicalWorkflowContractJson({ * name: 'checkout', * workflowVersion: '2.1.0', * signals: { alpha: {}, zeta: {} }, * }); * const right = canonicalWorkflowContractJson({ * name: 'checkout', * workflowVersion: '2.1.0', * signals: { zeta: {}, alpha: {} }, * }); * console.log(left === right); // true * ``` */ export declare function canonicalWorkflowContractJson(contract: WorkflowContract): string;