@lostgradient/weft 0.23.1 → 0.24.0
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 +72 -4
- package/dist/cli/codegen-emit-dedup.d.ts +81 -0
- package/dist/cli/codegen-emit-dedup.js +45 -0
- package/dist/cli/codegen-emit-registry.d.ts +90 -0
- package/dist/cli/codegen-emit-registry.js +50 -0
- package/dist/cli/codegen-emit.d.ts +26 -39
- package/dist/cli/codegen-emit.js +4 -27
- package/dist/cli/codegen-validate.d.ts +49 -0
- package/dist/cli/codegen-validate.js +186 -0
- package/dist/cli/codegen.js +6 -87
- package/dist/cli/generated/operation-catalog.snapshot.json +287 -3
- package/dist/cli/generated/operation-client.generated.d.ts +324 -10
- package/dist/cli/generated/operation-client.generated.js +12 -0
- package/dist/cli/output.js +1 -1
- package/dist/cli/storage-factory.js +2 -0
- package/dist/cli/workflow-commands.js +2 -0
- package/dist/cli-main.js +1 -1
- package/dist/core/application-payload-digest.d.ts +42 -0
- package/dist/core/application-payload-digest.js +76 -0
- package/dist/core/application-primitive-abort.d.ts +50 -0
- package/dist/core/application-primitive-abort.js +40 -0
- package/dist/core/application-primitive-attempt-registry.d.ts +74 -0
- package/dist/core/application-primitive-attempt-registry.js +73 -0
- package/dist/core/application-primitive-codec.d.ts +36 -0
- package/dist/core/application-primitive-codec.js +56 -0
- package/dist/core/application-primitive-commit.d.ts +67 -0
- package/dist/core/application-primitive-commit.js +66 -0
- package/dist/core/application-primitive-guards.d.ts +62 -0
- package/dist/core/application-primitive-guards.js +89 -0
- package/dist/core/application-primitive-payload.d.ts +29 -0
- package/dist/core/application-primitive-payload.js +65 -0
- package/dist/core/application-primitive-timing.d.ts +18 -0
- package/dist/core/application-primitive-timing.js +22 -0
- package/dist/core/atomic-state.js +2 -0
- package/dist/core/catalog/codec.d.ts +40 -0
- package/dist/core/catalog/codec.js +45 -0
- package/dist/core/catalog/errors.d.ts +104 -0
- package/dist/core/catalog/errors.js +41 -0
- package/dist/core/catalog/index.d.ts +25 -0
- package/dist/core/catalog/index.js +15 -0
- package/dist/core/catalog/reference-counts.d.ts +105 -0
- package/dist/core/catalog/reference-counts.js +28 -0
- package/dist/core/catalog/removal.d.ts +52 -0
- package/dist/core/catalog/removal.js +14 -0
- package/dist/core/catalog/storage-io.d.ts +78 -0
- package/dist/core/catalog/storage-io.js +106 -0
- package/dist/core/catalog/types.d.ts +108 -0
- package/dist/core/catalog/types.js +0 -0
- package/dist/core/catalog/workflow-catalog.d.ts +165 -0
- package/dist/core/catalog/workflow-catalog.js +198 -0
- package/dist/core/compare-codepoint.d.ts +13 -0
- package/dist/core/compare-codepoint.js +7 -0
- package/dist/core/compression.js +2 -0
- package/dist/core/context/attributes.d.ts +1 -1
- package/dist/core/context/durable-operations.d.ts +2 -2
- package/dist/core/context/index.js +5 -3
- package/dist/core/context/state-namespace.d.ts +1 -1
- package/dist/core/contract/build.d.ts +64 -0
- package/dist/core/contract/build.js +99 -0
- package/dist/core/contract/compatibility.d.ts +187 -0
- package/dist/core/contract/compatibility.js +37 -0
- package/dist/core/contract/failure.d.ts +64 -0
- package/dist/core/contract/failure.js +4 -0
- package/dist/core/contract/hash.d.ts +92 -0
- package/dist/core/contract/hash.js +34 -0
- package/dist/core/contract/index.d.ts +21 -0
- package/dist/core/contract/index.js +22 -0
- package/dist/core/contract/limits.d.ts +74 -0
- package/dist/core/contract/limits.js +1 -0
- package/dist/core/contract/manifest-parse-schema.d.ts +37 -0
- package/dist/core/contract/manifest-parse-schema.js +106 -0
- package/dist/core/contract/manifest-parse.d.ts +76 -0
- package/dist/core/contract/manifest-parse.js +189 -0
- package/dist/core/contract/manifest.d.ts +56 -0
- package/dist/core/contract/manifest.js +31 -0
- package/dist/core/contract/normalize.d.ts +97 -0
- package/dist/core/contract/normalize.js +127 -0
- package/dist/core/contract/revision.d.ts +29 -0
- package/dist/core/contract/revision.js +5 -0
- package/dist/core/contract/types.d.ts +274 -0
- package/dist/core/contract/types.js +1 -0
- package/dist/core/engine/catalog-activation.d.ts +40 -0
- package/dist/core/engine/catalog-activation.js +17 -0
- package/dist/core/engine/catalog-events.d.ts +36 -0
- package/dist/core/engine/catalog-events.js +15 -0
- package/dist/core/engine/catalog-readiness.d.ts +49 -0
- package/dist/core/engine/catalog-readiness.js +59 -0
- package/dist/core/engine/catalog-removal.d.ts +132 -0
- package/dist/core/engine/catalog-removal.js +70 -0
- package/dist/core/engine/construction.d.ts +1 -1
- package/dist/core/engine/construction.js +4 -2
- package/dist/core/engine/disposal.js +9 -0
- package/dist/core/engine/engine-workflows-namespace.d.ts +130 -0
- package/dist/core/engine/engine-workflows-namespace.js +49 -0
- package/dist/core/engine/finalizer-metadata.d.ts +22 -0
- package/dist/core/engine/finalizer-metadata.js +12 -0
- package/dist/core/engine/handle-result.js +2 -0
- package/dist/core/engine/index.d.ts +83 -0
- package/dist/core/engine/index.js +61 -0
- package/dist/core/engine/internals.d.ts +44 -1
- package/dist/core/engine/internals.js +6 -0
- package/dist/core/engine/lifecycle/start.js +3 -0
- package/dist/core/engine/registration.d.ts +76 -0
- package/dist/core/engine/registration.js +12 -6
- package/dist/core/engine/source-registration.d.ts +42 -0
- package/dist/core/engine/source-registration.js +36 -0
- package/dist/core/engine/source-resolution.d.ts +85 -0
- package/dist/core/engine/source-resolution.js +119 -0
- package/dist/core/engine/validation.js +2 -2
- package/dist/core/events/catalog-events.d.ts +117 -0
- package/dist/core/events/catalog-events.js +62 -0
- package/dist/core/events/event-map.d.ts +6 -0
- package/dist/core/events/index.d.ts +1 -0
- package/dist/core/events/index.js +1 -0
- package/dist/core/mailbox-admission.d.ts +26 -0
- package/dist/core/mailbox-admission.js +130 -0
- package/dist/core/mailbox-claims.d.ts +251 -0
- package/dist/core/mailbox-claims.js +0 -0
- package/dist/core/mailbox-codec.d.ts +32 -0
- package/dist/core/mailbox-codec.js +221 -0
- package/dist/core/mailbox-contract.d.ts +290 -0
- package/dist/core/mailbox-contract.js +0 -0
- package/dist/core/mailbox-delivery.d.ts +37 -0
- package/dist/core/mailbox-delivery.js +193 -0
- package/dist/core/mailbox-guards.d.ts +78 -0
- package/dist/core/mailbox-guards.js +58 -0
- package/dist/core/mailbox-index-codec.d.ts +41 -0
- package/dist/core/mailbox-index-codec.js +71 -0
- package/dist/core/mailbox-internals.d.ts +156 -0
- package/dist/core/mailbox-internals.js +154 -0
- package/dist/core/mailbox-maintenance.d.ts +29 -0
- package/dist/core/mailbox-maintenance.js +196 -0
- package/dist/core/mailbox-settlement.d.ts +69 -0
- package/dist/core/mailbox-settlement.js +206 -0
- package/dist/core/mailbox-storage.d.ts +117 -0
- package/dist/core/mailbox-storage.js +115 -0
- package/dist/core/mailbox-transition-helpers.d.ts +70 -0
- package/dist/core/mailbox-transition-helpers.js +43 -0
- package/dist/core/mailbox-transitions-recovery.d.ts +28 -0
- package/dist/core/mailbox-transitions-recovery.js +64 -0
- package/dist/core/mailbox-transitions.d.ts +138 -0
- package/dist/core/mailbox-transitions.js +180 -0
- package/dist/core/mailbox-types.d.ts +406 -0
- package/dist/core/mailbox-types.js +16 -0
- package/dist/core/mailbox-validation.d.ts +65 -0
- package/dist/core/mailbox-validation.js +73 -0
- package/dist/core/mailbox-waits.d.ts +50 -0
- package/dist/core/mailbox-waits.js +97 -0
- package/dist/core/mailbox.d.ts +163 -0
- package/dist/core/mailbox.js +211 -0
- package/dist/core/outbox-claims.d.ts +307 -0
- package/dist/core/outbox-claims.js +0 -0
- package/dist/core/outbox-codec.d.ts +24 -0
- package/dist/core/outbox-codec.js +257 -0
- package/dist/core/outbox-contract.d.ts +346 -0
- package/dist/core/outbox-contract.js +0 -0
- package/dist/core/outbox-delivery.d.ts +34 -0
- package/dist/core/outbox-delivery.js +149 -0
- package/dist/core/outbox-drain.d.ts +29 -0
- package/dist/core/outbox-drain.js +158 -0
- package/dist/core/outbox-enqueue.d.ts +24 -0
- package/dist/core/outbox-enqueue.js +115 -0
- package/dist/core/outbox-guards.d.ts +76 -0
- package/dist/core/outbox-guards.js +60 -0
- package/dist/core/outbox-index-codec.d.ts +33 -0
- package/dist/core/outbox-index-codec.js +71 -0
- package/dist/core/outbox-internals.d.ts +99 -0
- package/dist/core/outbox-internals.js +159 -0
- package/dist/core/outbox-listing.d.ts +12 -0
- package/dist/core/outbox-listing.js +38 -0
- package/dist/core/outbox-maintenance.d.ts +28 -0
- package/dist/core/outbox-maintenance.js +191 -0
- package/dist/core/outbox-operations.d.ts +18 -0
- package/dist/core/outbox-operations.js +46 -0
- package/dist/core/outbox-runner.d.ts +30 -0
- package/dist/core/outbox-runner.js +217 -0
- package/dist/core/outbox-settlement.d.ts +57 -0
- package/dist/core/outbox-settlement.js +183 -0
- package/dist/core/outbox-storage.d.ts +107 -0
- package/dist/core/outbox-storage.js +127 -0
- package/dist/core/outbox-transition-helpers.d.ts +83 -0
- package/dist/core/outbox-transition-helpers.js +63 -0
- package/dist/core/outbox-transitions-recovery.d.ts +30 -0
- package/dist/core/outbox-transitions-recovery.js +42 -0
- package/dist/core/outbox-transitions.d.ts +145 -0
- package/dist/core/outbox-transitions.js +236 -0
- package/dist/core/outbox-types.d.ts +400 -0
- package/dist/core/outbox-types.js +20 -0
- package/dist/core/outbox-validation.d.ts +101 -0
- package/dist/core/outbox-validation.js +127 -0
- package/dist/core/outbox-waits.d.ts +39 -0
- package/dist/core/outbox-waits.js +88 -0
- package/dist/core/outbox.d.ts +156 -0
- package/dist/core/outbox.js +234 -0
- package/dist/core/registry-limits.d.ts +49 -0
- package/dist/core/registry-limits.js +10 -0
- package/dist/core/registry-schema-conversion.d.ts +20 -0
- package/dist/core/registry-schema-conversion.js +22 -0
- package/dist/core/registry-snapshot.d.ts +91 -42
- package/dist/core/registry-snapshot.js +39 -77
- package/dist/core/registry-workflow-contract-draft.d.ts +32 -0
- package/dist/core/registry-workflow-contract-draft.js +28 -0
- package/dist/core/registry-workflow-manifest.d.ts +122 -0
- package/dist/core/registry-workflow-manifest.js +92 -0
- package/dist/core/source/errors.d.ts +55 -0
- package/dist/core/source/errors.js +13 -0
- package/dist/core/source/index.d.ts +19 -0
- package/dist/core/source/index.js +4 -0
- package/dist/core/source/resolvers.d.ts +45 -0
- package/dist/core/source/resolvers.js +9 -0
- package/dist/core/source/types.d.ts +211 -0
- package/dist/core/source/types.js +0 -0
- package/dist/core/source/validate.d.ts +91 -0
- package/dist/core/source/validate.js +62 -0
- package/dist/core/source/workflow-source.d.ts +43 -0
- package/dist/core/source/workflow-source.js +16 -0
- package/dist/core/types/workflow-registries.d.ts +7 -0
- package/dist/core/types/workflow-registry.d.ts +12 -0
- package/dist/core/versioning.js +2 -0
- package/dist/core/weft-error.d.ts +1 -1
- package/dist/core/weft-error.js +9 -1
- package/dist/http.js +2 -2
- package/dist/index.d.ts +23 -2
- package/dist/index.js +50 -0
- package/dist/indexeddb.js +1 -1
- package/dist/json-schema.js +3 -3
- package/dist/mcp/resources.js +2 -0
- package/dist/runtime/portable.d.ts +9 -1
- package/dist/server/fault-to-json-rpc.js +3 -1
- package/dist/server/handler/auth-context-principal.js +4 -0
- package/dist/server/json-rpc-http.js +2 -0
- package/dist/server/json-rpc-websocket.js +6 -4
- package/dist/server/operation-catalog/index.d.ts +1 -1
- package/dist/server/operation-catalog/pipeline-helpers.js +2 -0
- package/dist/server/operation-catalog/types.d.ts +17 -0
- package/dist/server/operation-fault.d.ts +3 -0
- package/dist/server/operation-fault.js +3 -1
- package/dist/server/operations/activate-workflow-revision.d.ts +30 -0
- package/dist/server/operations/activate-workflow-revision.js +69 -0
- package/dist/server/operations/fleet-events-subscription.js +1 -1
- package/dist/server/operations/fork-workflow.js +1 -1
- package/dist/server/operations/get-active-workflow-revision.d.ts +32 -0
- package/dist/server/operations/get-active-workflow-revision.js +46 -0
- package/dist/server/operations/get-catalog-diagnostics.d.ts +85 -0
- package/dist/server/operations/get-catalog-diagnostics.js +57 -0
- package/dist/server/operations/get-registry.js +17 -4
- package/dist/server/operations/get-workflow-revision.d.ts +25 -0
- package/dist/server/operations/get-workflow-revision.js +50 -0
- package/dist/server/operations/install-workflow-revision.d.ts +27 -0
- package/dist/server/operations/install-workflow-revision.js +46 -0
- package/dist/server/operations/list-workflow-revisions.d.ts +20 -0
- package/dist/server/operations/list-workflow-revisions.js +40 -0
- package/dist/server/operations/start-or-signal-workflow.js +1 -1
- package/dist/server/operations/start-workflow.js +1 -1
- package/dist/server/operations/static-registrations.js +36 -0
- package/dist/server/operations/storage.js +2 -2
- package/dist/server/operations/submit-review-decision.js +1 -1
- package/dist/server/operations/workflow-catalog-operation-helpers.d.ts +59 -0
- package/dist/server/operations/workflow-catalog-operation-helpers.js +135 -0
- package/dist/server/runtime/websocket-upgrade.js +2 -0
- package/dist/storage/bun-sql.js +49 -6
- package/dist/storage/catalog-keys.d.ts +45 -0
- package/dist/storage/catalog-keys.js +6 -0
- package/dist/storage/compressed-storage.js +1 -1
- package/dist/storage/derived-operations.js +1 -1
- package/dist/storage/index.d.ts +26 -1
- package/dist/storage/interface.d.ts +26 -1
- package/dist/storage/interface.js +1 -1
- package/dist/storage/key-encoding.d.ts +15 -0
- package/dist/storage/key-encoding.js +3 -0
- package/dist/storage/key-prefixes.d.ts +1 -1
- package/dist/storage/key-prefixes.js +15 -0
- package/dist/storage/lmdb.d.ts +26 -1
- package/dist/storage/lmdb.js +1 -1
- package/dist/storage/mailbox-keys.d.ts +80 -0
- package/dist/storage/mailbox-keys.js +14 -0
- package/dist/storage/memory.js +1 -1
- package/dist/storage/neon.js +2 -2
- package/dist/storage/node-sqlite-loader.d.ts +1 -1
- package/dist/storage/node-sqlite.js +49 -6
- package/dist/storage/outbox-keys.d.ts +77 -0
- package/dist/storage/outbox-keys.js +14 -0
- package/dist/storage/postgres.js +2 -2
- package/dist/storage/resolve.js +1 -1
- package/dist/storage/scoped-storage.js +1 -1
- package/dist/storage/storage-configuration.d.ts +6 -0
- package/dist/storage/testing.js +1 -1
- package/dist/storage/turso.js +2 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/web-extension.js +1 -1
- package/dist/worker/manifest/registry-contract-builder.d.ts +21 -3
- package/dist/worker/manifest/registry-contract-builder.js +40 -31
- package/package.json +1 -1
package/dist/core/compression.js
CHANGED
|
@@ -3,5 +3,5 @@ import type { ContextInternals } from './internals.ts';
|
|
|
3
3
|
export declare function validateAttribute(internals: ContextInternals, key: string, value: SearchAttributeValue): void;
|
|
4
4
|
export declare function setAttribute(internals: ContextInternals, key: string, value: SearchAttributeValue): void;
|
|
5
5
|
export declare function setAttributes(internals: ContextInternals, attributes: Record<string, SearchAttributeValue>): void;
|
|
6
|
-
export declare function getAttribute
|
|
6
|
+
export declare function getAttribute(internals: ContextInternals, key: string): SearchAttributeValue | undefined;
|
|
7
7
|
export declare function getAttributes(internals: ContextInternals): Readonly<Record<string, SearchAttributeValue>>;
|
|
@@ -43,8 +43,8 @@ export declare function readOrInitConditionDeadline(internals: ContextInternals,
|
|
|
43
43
|
*/
|
|
44
44
|
export declare function waitUntil(context: Context, internals: ContextInternals, predicate: () => boolean, timeout?: Duration): Generator<ContextOperationRequest, boolean | void, unknown>;
|
|
45
45
|
export declare function getVersion(_context: Context, internals: ContextInternals, changeId: string, minSupported: number, maxSupported: number): Generator<ContextOperationRequest, number, unknown>;
|
|
46
|
-
export declare function waitForUpdate
|
|
47
|
-
payload:
|
|
46
|
+
export declare function waitForUpdate(context: Context, internals: ContextInternals, name: string): Generator<ContextOperationRequest, {
|
|
47
|
+
payload: unknown;
|
|
48
48
|
respond: (result: unknown) => void;
|
|
49
49
|
}, unknown>;
|
|
50
50
|
export declare function review(context: Context, internals: ContextInternals, options: HumanReviewOptions): Generator<ContextOperationRequest, HumanReviewResult, unknown>;
|
|
@@ -114,9 +114,11 @@ export class Context {
|
|
|
114
114
|
if (prepared.cached)
|
|
115
115
|
return;
|
|
116
116
|
const execute = () => durableOperations.completePreparedSleepOperation(this, prepared);
|
|
117
|
-
if (!internals.workflowInterceptor)
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
if (!internals.workflowInterceptor) {
|
|
118
|
+
yield* execute();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
yield* internals.workflowInterceptor.sleep({
|
|
120
122
|
workflowId: this.workflowId,
|
|
121
123
|
duration: prepared.milliseconds,
|
|
122
124
|
headers: new Map
|
|
@@ -6,7 +6,7 @@ import type { ContextOperationRequest } from './operation-request.ts';
|
|
|
6
6
|
interface WorkflowAtomicStateOperationCache {
|
|
7
7
|
nextStep(): number;
|
|
8
8
|
has(step: number): boolean;
|
|
9
|
-
get
|
|
9
|
+
get(step: number): unknown;
|
|
10
10
|
set(step: number, value: unknown): void;
|
|
11
11
|
}
|
|
12
12
|
export declare function createStateNamespace(context: Context, internals: ContextInternals): WorkflowStateNamespace;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert an authoring-time workflow definition into a {@link WorkflowContract}.
|
|
3
|
+
*
|
|
4
|
+
* Every `DefinitionSchema` (workflow input/output, every signal/update/query,
|
|
5
|
+
* every activity, the finalizer) is converted through the existing
|
|
6
|
+
* `definitionSchemaToJsonSchema()` — this module does not reimplement schema
|
|
7
|
+
* conversion, and it does not reimplement the "unsupported construct emits
|
|
8
|
+
* `unknown`" behavior, which lives entirely in `weft codegen`'s emitter.
|
|
9
|
+
*
|
|
10
|
+
* @module core/contract/build
|
|
11
|
+
*/
|
|
12
|
+
import { WeftError } from '../weft-error.ts';
|
|
13
|
+
import type { WorkflowContract, WorkflowContractSource } from './types.ts';
|
|
14
|
+
/** Discriminates which part of a workflow contract source failed conversion. */
|
|
15
|
+
type ContractEntityKind = 'workflow' | 'signal' | 'update' | 'query' | 'activity' | 'finalizer';
|
|
16
|
+
/**
|
|
17
|
+
* Thrown when {@link buildWorkflowContract} cannot convert a registered
|
|
18
|
+
* `DefinitionSchema` to JSON Schema — no built-in vendor adapter and no
|
|
19
|
+
* structural `~standard.jsonSchema` converter. Mirrors
|
|
20
|
+
* `RegistrySchemaConversionError`'s `entityKind`/`entityName`/`direction`
|
|
21
|
+
* fields; not root-exported, matching that error's precedent (build-tooling
|
|
22
|
+
* errors surfaced to the caller of the builder, not part of the public error
|
|
23
|
+
* vocabulary).
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* import { WorkflowContractConversionError } from '@lostgradient/weft';
|
|
28
|
+
*
|
|
29
|
+
* try {
|
|
30
|
+
* throw new WorkflowContractConversionError('activity', 'charge', 'inputSchema', new Error('boom'));
|
|
31
|
+
* } catch (error) {
|
|
32
|
+
* console.log(error instanceof WorkflowContractConversionError); // true
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare class WorkflowContractConversionError extends WeftError<'WorkflowContractConversionError'> {
|
|
37
|
+
readonly entityKind: ContractEntityKind;
|
|
38
|
+
readonly entityName: string;
|
|
39
|
+
readonly direction: 'inputSchema' | 'outputSchema';
|
|
40
|
+
constructor(entityKind: ContractEntityKind, entityName: string, direction: 'inputSchema' | 'outputSchema', cause: unknown);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Build a normalized {@link WorkflowContract} from an authoring-time workflow
|
|
44
|
+
* definition.
|
|
45
|
+
*
|
|
46
|
+
* Accepts anything structurally matching {@link WorkflowContractSource} —
|
|
47
|
+
* both `WorkflowDefinition` and `BuiltWorkflowDefinition` (the return type
|
|
48
|
+
* of `workflow({...}).execute(fn)`) satisfy it directly, with no cast.
|
|
49
|
+
*
|
|
50
|
+
* Throws {@link WorkflowContractConversionError} if any declared schema
|
|
51
|
+
* (workflow input/output, a signal/update/query input/output, an activity
|
|
52
|
+
* input/output, or the finalizer's input/output) cannot be converted to JSON
|
|
53
|
+
* Schema.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* import { buildWorkflowContract } from '@lostgradient/weft';
|
|
58
|
+
*
|
|
59
|
+
* const contract = buildWorkflowContract({ name: 'checkout', version: '2.1.0' });
|
|
60
|
+
* console.log(contract.name, contract.workflowVersion); // checkout 2.1.0
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export declare function buildWorkflowContract(source: WorkflowContractSource): WorkflowContract;
|
|
64
|
+
export {};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { definitionSchemaToJsonSchema } from "../types/definition-schema-to-json.js";
|
|
2
|
+
import { DEFAULT_WORKFLOW_VERSION } from "../versioning.js";
|
|
3
|
+
import { WeftError } from "../weft-error.js";
|
|
4
|
+
import { normalizeWorkflowContract } from "./normalize.js";
|
|
5
|
+
|
|
6
|
+
export class WorkflowContractConversionError extends WeftError {
|
|
7
|
+
entityKind;
|
|
8
|
+
entityName;
|
|
9
|
+
direction;
|
|
10
|
+
constructor(entityKind, entityName, direction, cause) {
|
|
11
|
+
const causeMessage = cause instanceof Error ? cause.message : String(cause);
|
|
12
|
+
super("WorkflowContractConversionError", `Failed to convert ${direction} for ${entityKind} "${entityName}": ${causeMessage}`, { cause });
|
|
13
|
+
this.entityKind = entityKind;
|
|
14
|
+
this.entityName = entityName;
|
|
15
|
+
this.direction = direction;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function convertSchema(entityKind, entityName, direction, schema) {
|
|
19
|
+
try {
|
|
20
|
+
return definitionSchemaToJsonSchema(schema, direction === "inputSchema" ? "input" : "output");
|
|
21
|
+
} catch (cause) {
|
|
22
|
+
throw new WorkflowContractConversionError(entityKind, entityName, direction, cause);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function buildMessageContract(entityKind, entityName, source) {
|
|
26
|
+
const entry = {};
|
|
27
|
+
if (source.inputSchema !== void 0)
|
|
28
|
+
entry.inputSchema = convertSchema(entityKind, entityName, "inputSchema", source.inputSchema);
|
|
29
|
+
if (source.outputSchema !== void 0)
|
|
30
|
+
entry.outputSchema = convertSchema(entityKind, entityName, "outputSchema", source.outputSchema);
|
|
31
|
+
return entry;
|
|
32
|
+
}
|
|
33
|
+
function buildActivityContract(workflowName, activityName, source) {
|
|
34
|
+
return buildMessageContract("activity", `${workflowName}.${activityName}`, source);
|
|
35
|
+
}
|
|
36
|
+
function buildMessageRecord(workflowName, entityKind, sources) {
|
|
37
|
+
if (sources === void 0)
|
|
38
|
+
return;
|
|
39
|
+
const localKeys = Object.keys(sources);
|
|
40
|
+
if (localKeys.length === 0)
|
|
41
|
+
return;
|
|
42
|
+
const built = Object.create(null);
|
|
43
|
+
for (const localKey of localKeys) {
|
|
44
|
+
const source = sources[localKey], wireName = source.name;
|
|
45
|
+
if (Object.hasOwn(built, wireName))
|
|
46
|
+
throw Error(`Duplicate ${entityKind} runtime name "${wireName}" in workflow "${workflowName}"`);
|
|
47
|
+
built[wireName] = buildMessageContract(entityKind, `${workflowName}.${entityKind}.${wireName}`, source);
|
|
48
|
+
}
|
|
49
|
+
return built;
|
|
50
|
+
}
|
|
51
|
+
function buildActivityRecord(workflowName, sources) {
|
|
52
|
+
if (sources === void 0)
|
|
53
|
+
return;
|
|
54
|
+
const names = Object.keys(sources);
|
|
55
|
+
if (names.length === 0)
|
|
56
|
+
return;
|
|
57
|
+
const built = Object.create(null);
|
|
58
|
+
for (const name of names)
|
|
59
|
+
built[name] = buildActivityContract(workflowName, name, sources[name]);
|
|
60
|
+
return built;
|
|
61
|
+
}
|
|
62
|
+
function applyDescriptionAndTags(draft, source) {
|
|
63
|
+
if (source.description !== void 0)
|
|
64
|
+
draft.description = source.description;
|
|
65
|
+
if (source.tags !== void 0 && source.tags.length > 0)
|
|
66
|
+
draft.tags = [...source.tags];
|
|
67
|
+
}
|
|
68
|
+
function applySchemas(draft, source) {
|
|
69
|
+
if (source.inputSchema !== void 0)
|
|
70
|
+
draft.inputSchema = convertSchema("workflow", source.name, "inputSchema", source.inputSchema);
|
|
71
|
+
if (source.outputSchema !== void 0)
|
|
72
|
+
draft.outputSchema = convertSchema("workflow", source.name, "outputSchema", source.outputSchema);
|
|
73
|
+
}
|
|
74
|
+
function applyMessageRecords(draft, source) {
|
|
75
|
+
const signals = buildMessageRecord(source.name, "signal", source.signals);
|
|
76
|
+
if (signals !== void 0)
|
|
77
|
+
draft.signals = signals;
|
|
78
|
+
const updates = buildMessageRecord(source.name, "update", source.updates);
|
|
79
|
+
if (updates !== void 0)
|
|
80
|
+
draft.updates = updates;
|
|
81
|
+
const queries = buildMessageRecord(source.name, "query", source.queries);
|
|
82
|
+
if (queries !== void 0)
|
|
83
|
+
draft.queries = queries;
|
|
84
|
+
}
|
|
85
|
+
export function buildWorkflowContract(source) {
|
|
86
|
+
const draft = {
|
|
87
|
+
name: source.name,
|
|
88
|
+
workflowVersion: source.version ?? DEFAULT_WORKFLOW_VERSION
|
|
89
|
+
};
|
|
90
|
+
applyDescriptionAndTags(draft, source);
|
|
91
|
+
applySchemas(draft, source);
|
|
92
|
+
applyMessageRecords(draft, source);
|
|
93
|
+
const activities = buildActivityRecord(source.name, source.activities);
|
|
94
|
+
if (activities !== void 0)
|
|
95
|
+
draft.activities = activities;
|
|
96
|
+
if (source.finalizer !== void 0)
|
|
97
|
+
draft.finalizer = buildMessageContract("finalizer", `${source.name}.finalizer`, source.finalizer);
|
|
98
|
+
return normalizeWorkflowContract(draft);
|
|
99
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured compatibility verdicts between two workflow revision manifests.
|
|
3
|
+
*
|
|
4
|
+
* `checkWorkflowCompatibility()` answers "may `candidate` automatically
|
|
5
|
+
* activate in place of `current`" as a bounded, machine-readable
|
|
6
|
+
* {@link WorkflowCompatibilityVerdict} rather than a thrown error or a bare
|
|
7
|
+
* boolean—a catalog or refresh orchestrator can report every reason a
|
|
8
|
+
* candidate revision was rejected, but never override one during automatic
|
|
9
|
+
* activation (the reasons are policy-fixed except for one deliberate knob,
|
|
10
|
+
* see {@link WorkflowCompatibilityPolicy}).
|
|
11
|
+
*
|
|
12
|
+
* The five reasons this module can report mirror, and in one case literally
|
|
13
|
+
* call, checks that already exist elsewhere in the codebase rather than
|
|
14
|
+
* reinventing them:
|
|
15
|
+
*
|
|
16
|
+
* - `workflow-version-incompatible` calls {@link checkVersionCompatibility}
|
|
17
|
+
* from `core/versioning.ts`—the exact primitive
|
|
18
|
+
* `derivePreparedExecutionState()` (`core/engine/lifecycle/persist.ts`)
|
|
19
|
+
* already uses to reject a stored checkpoint against a re-registered
|
|
20
|
+
* workflow. This module never modifies that primitive; it reuses it as-is
|
|
21
|
+
* so the two answers can never disagree.
|
|
22
|
+
* - `contract-hash-mismatch` and `manifest-version-unsupported` reuse the
|
|
23
|
+
* same names `parseWorkflowRevisionManifest()`
|
|
24
|
+
* (`WorkflowRevisionManifestRejectionReason`, `./failure.ts`) already uses
|
|
25
|
+
* for structurally similar situations—hostile-input rejection there,
|
|
26
|
+
* activation compatibility here. The two unions are intentionally
|
|
27
|
+
* independent; the shared literal is vocabulary reuse, not a coupling.
|
|
28
|
+
* `manifest-version-unsupported` specifically is unreachable through this
|
|
29
|
+
* module's own type-safe surface today—`WorkflowRevisionManifest.manifestVersion`
|
|
30
|
+
* is typed as the single supported literal, and every in-repo producer
|
|
31
|
+
* ({@link buildWorkflowRevisionManifest}, {@link parseWorkflowRevisionManifest})
|
|
32
|
+
* guarantees it—so it costs nothing to check here and exists for the
|
|
33
|
+
* consumer this module is exported for but does not yet have: a future
|
|
34
|
+
* catalog or refresh system comparing a manifest it read from storage or
|
|
35
|
+
* the wire, which may not have round-tripped through
|
|
36
|
+
* `parseWorkflowRevisionManifest()` immediately before the comparison.
|
|
37
|
+
* Widening the parameter type to accept that case is deferred to whichever
|
|
38
|
+
* catalog work (WFT-9) first needs a type-safe way to trigger it.
|
|
39
|
+
*
|
|
40
|
+
* `checkWorkflowCompatibility()` is pure and synchronous: both manifests
|
|
41
|
+
* already carry their computed `contractHash`/`revision`, so no hashing
|
|
42
|
+
* happens here.
|
|
43
|
+
*
|
|
44
|
+
* @module core/contract/compatibility
|
|
45
|
+
*/
|
|
46
|
+
import type { WorkflowRevisionManifest } from './types.ts';
|
|
47
|
+
/**
|
|
48
|
+
* One bounded, machine-readable reason `candidate` is not compatible with
|
|
49
|
+
* `current`. Closed union, safe to use as a metric label—exactly the five
|
|
50
|
+
* reasons named by the Activation Compatibility issue, in the fixed order
|
|
51
|
+
* {@link checkWorkflowCompatibility} evaluates and reports them.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* import type { WorkflowCompatibilityReason } from '@lostgradient/weft';
|
|
56
|
+
*
|
|
57
|
+
* const counts = new Map<WorkflowCompatibilityReason, number>();
|
|
58
|
+
* counts.set('contract-hash-mismatch', 1);
|
|
59
|
+
* console.log(counts.get('contract-hash-mismatch'));
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export type WorkflowCompatibilityReason = 'name-mismatch' | 'manifest-version-unsupported' | 'contract-hash-mismatch' | 'workflow-version-incompatible' | 'artifact-revision-mismatch';
|
|
63
|
+
/**
|
|
64
|
+
* The structured result of {@link checkWorkflowCompatibility}: either fully
|
|
65
|
+
* compatible, or incompatible with the complete, ordered list of every
|
|
66
|
+
* applicable {@link WorkflowCompatibilityReason}—never just the first one
|
|
67
|
+
* found. A refresh orchestrator may report every reason here; it may never
|
|
68
|
+
* treat an incompatible verdict as compatible during automatic activation.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts
|
|
72
|
+
* import type { WorkflowCompatibilityVerdict } from '@lostgradient/weft';
|
|
73
|
+
*
|
|
74
|
+
* const verdict: WorkflowCompatibilityVerdict = {
|
|
75
|
+
* compatible: false,
|
|
76
|
+
* reasons: ['contract-hash-mismatch'],
|
|
77
|
+
* };
|
|
78
|
+
* console.log(verdict.compatible ? 'ok' : verdict.reasons.join(', '));
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export type WorkflowCompatibilityVerdict = Readonly<{
|
|
82
|
+
compatible: true;
|
|
83
|
+
}> | Readonly<{
|
|
84
|
+
compatible: false;
|
|
85
|
+
reasons: readonly WorkflowCompatibilityReason[];
|
|
86
|
+
}>;
|
|
87
|
+
/**
|
|
88
|
+
* Declared compatibility policy accepted by {@link checkWorkflowCompatibility}.
|
|
89
|
+
*
|
|
90
|
+
* `requireExactRevision` is the *only* tunable axis. `name-mismatch`,
|
|
91
|
+
* `manifest-version-unsupported`, `contract-hash-mismatch`, and
|
|
92
|
+
* `workflow-version-incompatible` can never be loosened by policy—a
|
|
93
|
+
* refresh system may report those reasons but may not override them during
|
|
94
|
+
* automatic activation, which is the literal mechanism the Activation
|
|
95
|
+
* Compatibility issue asks for.
|
|
96
|
+
*
|
|
97
|
+
* - `true` (the strict default): a candidate whose `revision` differs from
|
|
98
|
+
* `current`'s—even when `contractHash` is identical—reports
|
|
99
|
+
* `artifact-revision-mismatch` and is not compatible.
|
|
100
|
+
* - `false`: a `revision`-only difference is tolerated; `artifact-revision-mismatch`
|
|
101
|
+
* is never reported. What that difference *means* depends on how `revision`
|
|
102
|
+
* was produced. Under the default content-derived revision
|
|
103
|
+
* ({@link deriveWorkflowRevision}), a `revision`-only difference is always a
|
|
104
|
+
* documentation-only edit (`contract.description` or `contract.tags`, since
|
|
105
|
+
* `contractHash` deliberately excludes both). Under a caller-supplied
|
|
106
|
+
* revision (`buildWorkflowRevisionManifest(contract, { revision })`), a
|
|
107
|
+
* `revision`-only difference can be any artifact-identity change the caller
|
|
108
|
+
* chose to encode there (a build id, a deployment tag)—`false` tolerates
|
|
109
|
+
* that too, since this module has no way to distinguish an opaque supplied
|
|
110
|
+
* revision from a derived one by inspecting the manifest alone. Do not set
|
|
111
|
+
* `false` when your manifests use explicit revisions unless you intend to
|
|
112
|
+
* accept any `revision` change as compatible. Under the default
|
|
113
|
+
* content-derived revision, a `contractHash` difference always implies a
|
|
114
|
+
* `revision` difference too (`revision`'s full-contract digest is a strict
|
|
115
|
+
* superset of what `contractHash` covers); under a caller-supplied
|
|
116
|
+
* revision, two manifests can share the same `revision` string despite
|
|
117
|
+
* different `contractHash` values, since the caller controls that string
|
|
118
|
+
* independently of contract content. Either way,
|
|
119
|
+
* `contract-hash-mismatch` is unaffected by this setting and still blocks
|
|
120
|
+
* activation on its own.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```ts
|
|
124
|
+
* import type { WorkflowCompatibilityPolicy } from '@lostgradient/weft';
|
|
125
|
+
*
|
|
126
|
+
* const lenient: WorkflowCompatibilityPolicy = { requireExactRevision: false };
|
|
127
|
+
* console.log(lenient.requireExactRevision);
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
export interface WorkflowCompatibilityPolicy {
|
|
131
|
+
/**
|
|
132
|
+
* Whether an exact `revision` match is required for compatibility, on top
|
|
133
|
+
* of the four never-tunable reasons. Defaults to `true` when omitted or
|
|
134
|
+
* when the policy object itself omits the field.
|
|
135
|
+
*/
|
|
136
|
+
requireExactRevision?: boolean;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* The strict default policy `checkWorkflowCompatibility` uses when no
|
|
140
|
+
* policy argument is supplied. Exported so a catalog can name the default
|
|
141
|
+
* explicitly—in a log line, a configuration default, a test fixture—rather
|
|
142
|
+
* than relying on an implicit fallback. Frozen: `checkWorkflowCompatibility`
|
|
143
|
+
* consults this exact object as the default argument for every call that
|
|
144
|
+
* omits a policy, so a caller that mutated it would silently change the
|
|
145
|
+
* default for every other caller in the process.
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```ts
|
|
149
|
+
* import { DEFAULT_WORKFLOW_COMPATIBILITY_POLICY } from '@lostgradient/weft';
|
|
150
|
+
*
|
|
151
|
+
* console.log(DEFAULT_WORKFLOW_COMPATIBILITY_POLICY.requireExactRevision); // true
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
export declare const DEFAULT_WORKFLOW_COMPATIBILITY_POLICY: Readonly<Required<WorkflowCompatibilityPolicy>>;
|
|
155
|
+
/**
|
|
156
|
+
* Compare two workflow revision manifests and report whether `candidate` is
|
|
157
|
+
* compatible with `current`—the pure comparison behind automatic
|
|
158
|
+
* activation. Symmetric: `checkWorkflowCompatibility(a, b, policy)` and
|
|
159
|
+
* `checkWorkflowCompatibility(b, a, policy)` always agree, since every
|
|
160
|
+
* underlying check is an equality or bounded-value comparison with no
|
|
161
|
+
* directionality.
|
|
162
|
+
*
|
|
163
|
+
* Every applicable reason is collected and returned in the fixed order
|
|
164
|
+
* `name-mismatch`, `manifest-version-unsupported`, `contract-hash-mismatch`,
|
|
165
|
+
* `workflow-version-incompatible`, `artifact-revision-mismatch`—the
|
|
166
|
+
* function never short-circuits on the first reason found.
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```ts
|
|
170
|
+
* import {
|
|
171
|
+
* buildWorkflowContract,
|
|
172
|
+
* buildWorkflowRevisionManifest,
|
|
173
|
+
* checkWorkflowCompatibility,
|
|
174
|
+
* } from '@lostgradient/weft';
|
|
175
|
+
*
|
|
176
|
+
* const current = await buildWorkflowRevisionManifest(
|
|
177
|
+
* buildWorkflowContract({ name: 'checkout', version: '1.0.0' }),
|
|
178
|
+
* );
|
|
179
|
+
* const candidate = await buildWorkflowRevisionManifest(
|
|
180
|
+
* buildWorkflowContract({ name: 'checkout', version: '2.0.0' }),
|
|
181
|
+
* );
|
|
182
|
+
*
|
|
183
|
+
* const verdict = checkWorkflowCompatibility(current, candidate);
|
|
184
|
+
* console.log(verdict.compatible); // false
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
export declare function checkWorkflowCompatibility(current: WorkflowRevisionManifest, candidate: WorkflowRevisionManifest, policy?: WorkflowCompatibilityPolicy): WorkflowCompatibilityVerdict;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { checkVersionCompatibility } from "../versioning.js";
|
|
2
|
+
import { WORKFLOW_REVISION_MANIFEST_VERSION } from "./types.js";
|
|
3
|
+
export const DEFAULT_WORKFLOW_COMPATIBILITY_POLICY = Object.freeze({
|
|
4
|
+
requireExactRevision: !0
|
|
5
|
+
});
|
|
6
|
+
function isSupportedWorkflowRevisionManifestVersion(version) {
|
|
7
|
+
return version === WORKFLOW_REVISION_MANIFEST_VERSION;
|
|
8
|
+
}
|
|
9
|
+
function isNameMismatch(current, candidate) {
|
|
10
|
+
return current.name !== candidate.name;
|
|
11
|
+
}
|
|
12
|
+
function isManifestVersionUnsupported(current, candidate) {
|
|
13
|
+
return !isSupportedWorkflowRevisionManifestVersion(current.manifestVersion) || !isSupportedWorkflowRevisionManifestVersion(candidate.manifestVersion);
|
|
14
|
+
}
|
|
15
|
+
function isContractHashMismatch(current, candidate) {
|
|
16
|
+
return current.contractHash !== candidate.contractHash;
|
|
17
|
+
}
|
|
18
|
+
function isWorkflowVersionIncompatible(current, candidate) {
|
|
19
|
+
return checkVersionCompatibility(current.workflowVersion, candidate.workflowVersion) === "incompatible";
|
|
20
|
+
}
|
|
21
|
+
function isArtifactRevisionMismatch(current, candidate) {
|
|
22
|
+
return current.revision !== candidate.revision;
|
|
23
|
+
}
|
|
24
|
+
export function checkWorkflowCompatibility(current, candidate, policy = DEFAULT_WORKFLOW_COMPATIBILITY_POLICY) {
|
|
25
|
+
const requireExactRevision = policy.requireExactRevision ?? !0, reasons = [];
|
|
26
|
+
if (isNameMismatch(current, candidate))
|
|
27
|
+
reasons.push("name-mismatch");
|
|
28
|
+
if (isManifestVersionUnsupported(current, candidate))
|
|
29
|
+
reasons.push("manifest-version-unsupported");
|
|
30
|
+
if (isContractHashMismatch(current, candidate))
|
|
31
|
+
reasons.push("contract-hash-mismatch");
|
|
32
|
+
if (isWorkflowVersionIncompatible(current, candidate))
|
|
33
|
+
reasons.push("workflow-version-incompatible");
|
|
34
|
+
if (requireExactRevision && isArtifactRevisionMismatch(current, candidate))
|
|
35
|
+
reasons.push("artifact-revision-mismatch");
|
|
36
|
+
return reasons.length === 0 ? { compatible: !0 } : { compatible: !1, reasons };
|
|
37
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded failure vocabulary for workflow revision manifest validation.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors `src/worker/manifest/failure.ts`: rejections are returned rather
|
|
5
|
+
* than thrown, `reason` is a closed union safe to use as a metric label, and
|
|
6
|
+
* `message`/`path` are diagnostics only.
|
|
7
|
+
*
|
|
8
|
+
* @module core/contract/failure
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Why an untrusted {@link WorkflowRevisionManifest} was rejected.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import type { WorkflowRevisionManifestRejectionReason } from '@lostgradient/weft';
|
|
16
|
+
*
|
|
17
|
+
* const counts = new Map<WorkflowRevisionManifestRejectionReason, number>();
|
|
18
|
+
* counts.set('contract-hash-mismatch', 1);
|
|
19
|
+
* console.log(counts.get('contract-hash-mismatch'));
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export type WorkflowRevisionManifestRejectionReason = 'manifest-version-unsupported' | 'contract-hash-mismatch' | 'not-an-object' | 'invalid-field' | 'identifier-too-long' | 'too-many-entries' | 'manifest-too-large';
|
|
23
|
+
/**
|
|
24
|
+
* A rejected workflow revision manifest, with enough detail to fix the
|
|
25
|
+
* caller's input and not so much that it leaks contract content into logs.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* import { parseWorkflowRevisionManifest, type WorkflowRevisionManifestValidationFailure } from '@lostgradient/weft';
|
|
30
|
+
*
|
|
31
|
+
* const result = await parseWorkflowRevisionManifest({ manifestVersion: 99 });
|
|
32
|
+
* if (!result.ok) {
|
|
33
|
+
* const rejection: WorkflowRevisionManifestValidationFailure = result;
|
|
34
|
+
* console.log(rejection.reason); // 'manifest-version-unsupported'
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export type WorkflowRevisionManifestValidationFailure = Readonly<{
|
|
39
|
+
ok: false;
|
|
40
|
+
/** Bounded rejection reason, safe as a metric label. */
|
|
41
|
+
reason: WorkflowRevisionManifestRejectionReason;
|
|
42
|
+
/** Human-readable diagnostic. Never use as a metric label. */
|
|
43
|
+
message: string;
|
|
44
|
+
/** Dotted path to the offending field, when one applies. */
|
|
45
|
+
path?: string;
|
|
46
|
+
}>;
|
|
47
|
+
/**
|
|
48
|
+
* Build a validation failure whose message reads as one sentence about the
|
|
49
|
+
* offending path — `manifest.contract.workflowVersion must be a non-empty
|
|
50
|
+
* string`.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* import { workflowRevisionManifestFailure } from '@lostgradient/weft';
|
|
55
|
+
*
|
|
56
|
+
* const failure = workflowRevisionManifestFailure(
|
|
57
|
+
* 'invalid-field',
|
|
58
|
+
* 'must be a non-empty string',
|
|
59
|
+
* 'manifest.name',
|
|
60
|
+
* );
|
|
61
|
+
* console.log(failure.message);
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare function workflowRevisionManifestFailure(reason: WorkflowRevisionManifestRejectionReason, message: string, path?: string): WorkflowRevisionManifestValidationFailure;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content-addressed digests of a workflow contract's public payload.
|
|
3
|
+
*
|
|
4
|
+
* `contractHash()` answers "which public payload contract does this
|
|
5
|
+
* revision implement" — it excludes `name`, `workflowVersion`,
|
|
6
|
+
* `description`, and `tags`, which identify the workflow and describe it in
|
|
7
|
+
* prose rather than describe what callers may send and get back. That
|
|
8
|
+
* exclusion is what lets a documentation edit change `deriveWorkflowRevision()`'s
|
|
9
|
+
* broader identity (see `revision.ts`) without changing `contractHash()`.
|
|
10
|
+
*
|
|
11
|
+
* `digestCanonicalWorkflowContract()` is a generic "digest this canonical
|
|
12
|
+
* JSON text" primitive shared by both identities: which identity a caller
|
|
13
|
+
* gets back depends entirely on which canonical serializer produced the
|
|
14
|
+
* text handed to it — `canonicalWorkflowContractJson()`'s full-identity
|
|
15
|
+
* output yields the same digest `deriveWorkflowRevision()` returns; this
|
|
16
|
+
* module's internal payload-only serializer is what `contractHash()` feeds
|
|
17
|
+
* it. Treat it as a shared digesting primitive, not as itself a payload-only
|
|
18
|
+
* function.
|
|
19
|
+
*
|
|
20
|
+
* SHA-256 rather than the repository's FNV-1a helpers, matching the worker
|
|
21
|
+
* manifest digest's choice: `contractHash` is compared for compatibility
|
|
22
|
+
* decisions, so a collision would let two different contracts pass for one
|
|
23
|
+
* another, ruling out a cache-key-quality hash.
|
|
24
|
+
*
|
|
25
|
+
* @module core/contract/hash
|
|
26
|
+
*/
|
|
27
|
+
import type { WorkflowActivityContract, WorkflowContract } from './types.ts';
|
|
28
|
+
/**
|
|
29
|
+
* Algorithm tag prefixed to every digest {@link contractHash} and
|
|
30
|
+
* {@link activityContractHash} produce.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* import { WORKFLOW_CONTRACT_DIGEST_ALGORITHM } from '@lostgradient/weft';
|
|
35
|
+
*
|
|
36
|
+
* const hash = 'sha256:2b1f0c9d';
|
|
37
|
+
* console.log(hash.startsWith(`${WORKFLOW_CONTRACT_DIGEST_ALGORITHM}:`)); // true
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare const WORKFLOW_CONTRACT_DIGEST_ALGORITHM = "sha256";
|
|
41
|
+
/**
|
|
42
|
+
* Digest canonical contract JSON text.
|
|
43
|
+
*
|
|
44
|
+
* Separate from {@link contractHash} and `deriveWorkflowRevision` so a
|
|
45
|
+
* caller that already holds a canonical serialization does not serialize
|
|
46
|
+
* the contract a second time. Which identity the result represents depends
|
|
47
|
+
* entirely on which canonical serializer produced `canonicalJson` — see this
|
|
48
|
+
* module's JSDoc.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* import { canonicalWorkflowContractJson, digestCanonicalWorkflowContract } from '@lostgradient/weft';
|
|
53
|
+
*
|
|
54
|
+
* const canonical = canonicalWorkflowContractJson({ name: 'checkout', workflowVersion: '2.1.0' });
|
|
55
|
+
* console.log((await digestCanonicalWorkflowContract(canonical)).startsWith('sha256:'));
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export declare function digestCanonicalWorkflowContract(canonicalJson: string): Promise<string>;
|
|
59
|
+
/**
|
|
60
|
+
* Compute a workflow contract's payload-only content identity.
|
|
61
|
+
*
|
|
62
|
+
* Determined entirely by the payload fields (schemas, signals, updates,
|
|
63
|
+
* queries, activities, finalizer) plus the `WORKFLOW_CONTRACT_VERSION`
|
|
64
|
+
* domain separator — two contracts that differ only in `name`,
|
|
65
|
+
* `workflowVersion`, `description`, or `tags` hash identically, and two
|
|
66
|
+
* contracts that differ only in key order also hash identically.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* import { contractHash } from '@lostgradient/weft';
|
|
71
|
+
*
|
|
72
|
+
* const left = await contractHash({ name: 'checkout', workflowVersion: '1.0.0' });
|
|
73
|
+
* const right = await contractHash({ name: 'checkout', workflowVersion: '2.0.0' });
|
|
74
|
+
* console.log(left === right); // true — name/workflowVersion are excluded
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export declare function contractHash(contract: WorkflowContract): Promise<string>;
|
|
78
|
+
/**
|
|
79
|
+
* Compute one activity's payload-only content identity, independent of the
|
|
80
|
+
* workflow contract it is declared under.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```ts
|
|
84
|
+
* import { activityContractHash } from '@lostgradient/weft';
|
|
85
|
+
*
|
|
86
|
+
* const hash = await activityContractHash({
|
|
87
|
+
* inputSchema: { type: 'object', properties: { amount: { type: 'number' } } },
|
|
88
|
+
* });
|
|
89
|
+
* console.log(hash.startsWith('sha256:')); // true
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export declare function activityContractHash(activity: WorkflowActivityContract): Promise<string>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { sha256Hex } from "../../worker/manifest/content-digest.js";
|
|
2
|
+
import {
|
|
3
|
+
appendContractRecordField,
|
|
4
|
+
appendSchemaField,
|
|
5
|
+
canonicalMessageContractJson
|
|
6
|
+
} from "./normalize.js";
|
|
7
|
+
import { WORKFLOW_CONTRACT_VERSION } from "./types.js";
|
|
8
|
+
export const WORKFLOW_CONTRACT_DIGEST_ALGORITHM = "sha256";
|
|
9
|
+
function canonicalWorkflowContractPayloadJson(contract) {
|
|
10
|
+
const fields = [`"contractVersion":${JSON.stringify(WORKFLOW_CONTRACT_VERSION)}`];
|
|
11
|
+
appendSchemaField(fields, "inputSchema", contract.inputSchema);
|
|
12
|
+
appendSchemaField(fields, "outputSchema", contract.outputSchema);
|
|
13
|
+
appendContractRecordField(fields, "signals", contract.signals);
|
|
14
|
+
appendContractRecordField(fields, "updates", contract.updates);
|
|
15
|
+
appendContractRecordField(fields, "queries", contract.queries);
|
|
16
|
+
appendContractRecordField(fields, "activities", contract.activities);
|
|
17
|
+
if (contract.finalizer !== void 0)
|
|
18
|
+
fields.push(`"finalizer":${canonicalMessageContractJson(contract.finalizer)}`);
|
|
19
|
+
return `{${fields.join(",")}}`;
|
|
20
|
+
}
|
|
21
|
+
export async function digestCanonicalWorkflowContract(canonicalJson) {
|
|
22
|
+
return sha256Hex(canonicalJson);
|
|
23
|
+
}
|
|
24
|
+
export async function contractHash(contract) {
|
|
25
|
+
return digestCanonicalWorkflowContract(canonicalWorkflowContractPayloadJson(contract));
|
|
26
|
+
}
|
|
27
|
+
export async function activityContractHash(activity) {
|
|
28
|
+
const versionField = `"contractVersion":${JSON.stringify(WORKFLOW_CONTRACT_VERSION)}`, schemaFields = canonicalActivityContractSchemaFields(activity);
|
|
29
|
+
return digestCanonicalWorkflowContract(`{${[versionField, ...schemaFields].join(",")}}`);
|
|
30
|
+
}
|
|
31
|
+
function canonicalActivityContractSchemaFields(entry) {
|
|
32
|
+
const stripped = canonicalMessageContractJson(entry).slice(1, -1);
|
|
33
|
+
return stripped.length === 0 ? [] : [stripped];
|
|
34
|
+
}
|