@sekiban/dcb-domain 0.1.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/LICENSE +92 -0
- package/README.md +104 -0
- package/dist/bridge.d.ts +162 -0
- package/dist/bridge.js +250 -0
- package/dist/command.d.ts +53 -0
- package/dist/command.js +64 -0
- package/dist/domain.d.ts +27 -0
- package/dist/domain.js +79 -0
- package/dist/event.d.ts +49 -0
- package/dist/event.js +178 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +15692 -0
- package/dist/parse.d.ts +24 -0
- package/dist/parse.js +71 -0
- package/dist/session.d.ts +108 -0
- package/dist/session.js +381 -0
- package/dist/state.d.ts +81 -0
- package/dist/state.js +112 -0
- package/dist/testing.d.ts +32 -0
- package/dist/testing.js +416 -0
- package/dist/types.d.ts +150 -0
- package/dist/types.js +90 -0
- package/package.json +54 -0
package/dist/command.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { DomainAuthoringError, V1_REJECT_ERROR_CODES, } from "./types";
|
|
2
|
+
export function read(projector, tag) {
|
|
3
|
+
return readSet({ kind: "state", projectorId: projector.id, projector, tag });
|
|
4
|
+
}
|
|
5
|
+
export function readExists(tag) {
|
|
6
|
+
return readSet({ kind: "exists", tag });
|
|
7
|
+
}
|
|
8
|
+
export const readTag = readExists;
|
|
9
|
+
export function readSet(...declarations) {
|
|
10
|
+
const flattened = declarations.flatMap((declaration) => "claims" in declaration ? [...declaration.claims] : [declaration]);
|
|
11
|
+
const claims = flattened.map((declaration) => Object.freeze({
|
|
12
|
+
...declaration,
|
|
13
|
+
tag: Object.freeze(declaration.tag),
|
|
14
|
+
}));
|
|
15
|
+
const tags = [...new Map(claims.map((claim) => [claim.tag.id, claim.tag])).values()];
|
|
16
|
+
const has = (kind, projectorId, tag) => claims.some((claim) => claim.kind === kind &&
|
|
17
|
+
claim.tag.id === tag.id &&
|
|
18
|
+
(kind === "exists" || claim.projectorId === projectorId));
|
|
19
|
+
return Object.freeze({ claims: Object.freeze(claims), tags: Object.freeze(tags), has });
|
|
20
|
+
}
|
|
21
|
+
export function command(options) {
|
|
22
|
+
if (options.id.length === 0)
|
|
23
|
+
throw new DomainAuthoringError("COMMAND_ID_REQUIRED", "Command id is required");
|
|
24
|
+
const parseInput = (value) => {
|
|
25
|
+
try {
|
|
26
|
+
return options.input.parse(value);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
throw new DomainAuthoringError("COMMAND_INPUT_INVALID", `Command ${options.id} input was rejected`, { cause: error });
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const execute = (value, context) => {
|
|
33
|
+
const parsed = parseInput(value);
|
|
34
|
+
const declarations = options.reads(parsed);
|
|
35
|
+
if (!declarations || typeof declarations.has !== "function") {
|
|
36
|
+
throw new DomainAuthoringError("READ_SET_INVALID", `Command ${options.id} reads() must return a ReadSet`);
|
|
37
|
+
}
|
|
38
|
+
return options.handle(parsed, context);
|
|
39
|
+
};
|
|
40
|
+
return Object.freeze({
|
|
41
|
+
id: options.id,
|
|
42
|
+
input: options.input,
|
|
43
|
+
parseInput,
|
|
44
|
+
reads: options.reads,
|
|
45
|
+
handle: options.handle,
|
|
46
|
+
execute,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
export function done(value) {
|
|
50
|
+
return Object.freeze({ kind: "done", ...(value === undefined ? {} : { value }) });
|
|
51
|
+
}
|
|
52
|
+
export function none(reason) {
|
|
53
|
+
return Object.freeze({ kind: "none", ...(reason === undefined ? {} : { reason }) });
|
|
54
|
+
}
|
|
55
|
+
export function reject(rejectKind, reason, details) {
|
|
56
|
+
return Object.freeze({
|
|
57
|
+
kind: "reject",
|
|
58
|
+
rejectKind,
|
|
59
|
+
reason,
|
|
60
|
+
code: V1_REJECT_ERROR_CODES[rejectKind],
|
|
61
|
+
...(details === undefined ? {} : { details }),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
export const terminal = { done, none, reject };
|
package/dist/domain.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { EventDefinition, EventUnion } from "./event.js";
|
|
2
|
+
import type { CommandDefinition } from "./command.js";
|
|
3
|
+
import type { ProjectorDefinition } from "./state.js";
|
|
4
|
+
export type DomainEventInput = EventDefinition | EventUnion;
|
|
5
|
+
export interface DomainViewDefinition {
|
|
6
|
+
readonly id: string;
|
|
7
|
+
readonly source: string;
|
|
8
|
+
readonly projector?: string;
|
|
9
|
+
/** Domain-owned half of the per-view delivery policy. */
|
|
10
|
+
readonly deliveryClass?: "immediate-preferred" | "queued";
|
|
11
|
+
}
|
|
12
|
+
export interface AuthoringDomain<Events extends readonly EventDefinition[] = readonly EventDefinition[], Projectors extends readonly unknown[] = readonly ProjectorDefinition[], Commands extends readonly unknown[] = readonly CommandDefinition[]> {
|
|
13
|
+
readonly events: Events;
|
|
14
|
+
readonly projectors: Projectors;
|
|
15
|
+
readonly commands: Commands;
|
|
16
|
+
readonly views: readonly DomainViewDefinition[];
|
|
17
|
+
readonly eventByType: ReadonlyMap<string, EventDefinition>;
|
|
18
|
+
readonly eventByName: ReadonlyMap<string, EventDefinition>;
|
|
19
|
+
}
|
|
20
|
+
export interface DomainOptions<Events extends readonly DomainEventInput[] = readonly DomainEventInput[], Projectors extends readonly unknown[] = readonly ProjectorDefinition[], Commands extends readonly unknown[] = readonly CommandDefinition[]> {
|
|
21
|
+
readonly events?: Events;
|
|
22
|
+
readonly eventUnions?: readonly EventUnion[];
|
|
23
|
+
readonly projectors?: Projectors;
|
|
24
|
+
readonly commands?: Commands;
|
|
25
|
+
readonly views?: readonly DomainViewDefinition[];
|
|
26
|
+
}
|
|
27
|
+
export declare function domain<const Events extends readonly DomainEventInput[], const Projectors extends readonly unknown[], const Commands extends readonly unknown[]>(options: DomainOptions<Events, Projectors, Commands>): AuthoringDomain<Extract<Events[number], EventDefinition> extends never ? readonly EventDefinition[] : readonly EventDefinition[], Projectors, Commands>;
|
package/dist/domain.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { DomainRegistrationError, } from "./types";
|
|
2
|
+
function flattenEvents(values) {
|
|
3
|
+
const result = [];
|
|
4
|
+
for (const value of values) {
|
|
5
|
+
if ("kind" in value && value.kind === "event-union")
|
|
6
|
+
result.push(...value.events);
|
|
7
|
+
else
|
|
8
|
+
result.push(value);
|
|
9
|
+
}
|
|
10
|
+
return result;
|
|
11
|
+
}
|
|
12
|
+
export function domain(options) {
|
|
13
|
+
const eventInputs = [
|
|
14
|
+
...(options.events ?? []),
|
|
15
|
+
...(options.eventUnions ?? []),
|
|
16
|
+
];
|
|
17
|
+
const events = flattenEvents(eventInputs);
|
|
18
|
+
const projectors = [...(options.projectors ?? [])];
|
|
19
|
+
const commands = [...(options.commands ?? [])];
|
|
20
|
+
const views = [...(options.views ?? [])];
|
|
21
|
+
const collisions = [];
|
|
22
|
+
const identityOwners = new Map();
|
|
23
|
+
const register = (identity, owner) => {
|
|
24
|
+
const previous = identityOwners.get(identity);
|
|
25
|
+
if (previous !== undefined)
|
|
26
|
+
collisions.push(`${identity} (${previous}, ${owner})`);
|
|
27
|
+
identityOwners.set(identity, owner);
|
|
28
|
+
};
|
|
29
|
+
for (const definition of events)
|
|
30
|
+
register(`event:${definition.eventType}`, definition.eventType);
|
|
31
|
+
for (const commandValue of commands) {
|
|
32
|
+
const definition = commandValue;
|
|
33
|
+
register(`command:${definition.id}`, definition.id);
|
|
34
|
+
}
|
|
35
|
+
for (const projectorValue of projectors) {
|
|
36
|
+
const definition = projectorValue;
|
|
37
|
+
register(`projector:${definition.id}`, definition.id);
|
|
38
|
+
}
|
|
39
|
+
for (const view of views)
|
|
40
|
+
register(`view:${view.id}`, view.id);
|
|
41
|
+
if (collisions.length > 0)
|
|
42
|
+
throw new DomainRegistrationError("Domain contains duplicate definition identities", collisions);
|
|
43
|
+
const eventByType = new Map();
|
|
44
|
+
const eventByName = new Map();
|
|
45
|
+
for (const definition of events) {
|
|
46
|
+
if (eventByType.has(definition.eventType))
|
|
47
|
+
throw new DomainRegistrationError(`Duplicate event identity ${definition.eventType}`, [definition.eventType]);
|
|
48
|
+
eventByType.set(definition.eventType, definition);
|
|
49
|
+
if (!eventByName.has(definition.name))
|
|
50
|
+
eventByName.set(definition.name, definition);
|
|
51
|
+
}
|
|
52
|
+
for (const projectorValue of projectors) {
|
|
53
|
+
const projector = projectorValue;
|
|
54
|
+
for (const eventType of projector.eventTypes) {
|
|
55
|
+
const definition = eventByType.get(eventType);
|
|
56
|
+
if (definition === undefined)
|
|
57
|
+
throw new DomainRegistrationError(`Projector ${projector.id} references unregistered ${eventType}`, [eventType]);
|
|
58
|
+
if (definition.tagFamilies.length > 0 && !definition.tagFamilies.includes(projector.tag.family)) {
|
|
59
|
+
throw new DomainRegistrationError(`Projector ${projector.id} has a source/family mismatch for ${eventType}`, [eventType]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
for (const view of views) {
|
|
64
|
+
if (!projectors.some((projectorValue) => {
|
|
65
|
+
const projector = projectorValue;
|
|
66
|
+
return projector.id === view.source || projector.id === view.projector;
|
|
67
|
+
})) {
|
|
68
|
+
throw new DomainRegistrationError(`View ${view.id} has no registered source projector`, [view.id]);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return Object.freeze({
|
|
72
|
+
events: Object.freeze(events),
|
|
73
|
+
projectors: Object.freeze(projectors),
|
|
74
|
+
commands: Object.freeze(commands),
|
|
75
|
+
views: Object.freeze(views),
|
|
76
|
+
eventByType,
|
|
77
|
+
eventByName,
|
|
78
|
+
});
|
|
79
|
+
}
|
package/dist/event.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type EventOf, type JsonValue, type Tag, type TagDeriver, type TagFamily, type TagFamilyOfDeriver } from "./types.js";
|
|
3
|
+
export declare function isEventPayload(value: unknown): boolean;
|
|
4
|
+
export interface EventDefinition<Name extends string = string, Schema extends z.ZodTypeAny = z.ZodTypeAny, Family extends string = string> {
|
|
5
|
+
readonly name: Name;
|
|
6
|
+
readonly eventPayloadName: Name;
|
|
7
|
+
readonly eventType: Name | string;
|
|
8
|
+
readonly key: Name | string;
|
|
9
|
+
readonly schema: Schema;
|
|
10
|
+
readonly tags: (payload: z.infer<Schema>) => readonly Tag<Family>[];
|
|
11
|
+
readonly make: (payload: unknown) => EventOf<EventDefinition<Name, Schema, Family>>;
|
|
12
|
+
readonly parse: (payload: unknown) => z.infer<Schema>;
|
|
13
|
+
readonly create: (payload: unknown) => RuntimeEventValue;
|
|
14
|
+
readonly construct: (payload: unknown) => RuntimeEventValue;
|
|
15
|
+
readonly tagFamilies: readonly string[];
|
|
16
|
+
}
|
|
17
|
+
export interface RuntimeEventValue {
|
|
18
|
+
readonly eventName: string;
|
|
19
|
+
readonly eventPayloadName: string;
|
|
20
|
+
readonly eventType: string;
|
|
21
|
+
readonly payload: JsonValue;
|
|
22
|
+
readonly tags: readonly Tag[];
|
|
23
|
+
}
|
|
24
|
+
export interface EventOptions<Payload, Deriver extends TagDeriver<Payload>> {
|
|
25
|
+
readonly tags: Deriver;
|
|
26
|
+
/** Removed by G32: define a distinct event name for each payload revision. */
|
|
27
|
+
readonly version?: never;
|
|
28
|
+
readonly tagFamily?: TagFamily | string;
|
|
29
|
+
}
|
|
30
|
+
export declare function event<const Name extends string, Schema extends z.ZodTypeAny, Deriver extends TagDeriver<z.infer<Schema>>>(name: Name, schema: Schema, options: EventOptions<z.infer<Schema>, Deriver>): EventDefinition<Name, Schema, TagFamilyOfDeriver<Deriver>>;
|
|
31
|
+
export interface EventUnion<Events extends readonly EventDefinition[] = readonly EventDefinition[]> {
|
|
32
|
+
readonly kind: "event-union";
|
|
33
|
+
readonly events: Events;
|
|
34
|
+
readonly eventTypes: readonly string[];
|
|
35
|
+
readonly discriminator?: string;
|
|
36
|
+
readonly schema: z.ZodType<EventOf<Events[number]>>;
|
|
37
|
+
readonly parse: (value: unknown) => EventOf<Events[number]>;
|
|
38
|
+
readonly safeParse: (value: unknown) => EventUnionSafeParse<EventOf<Events[number]>>;
|
|
39
|
+
}
|
|
40
|
+
export type EventUnionSafeParse<Value> = {
|
|
41
|
+
readonly success: true;
|
|
42
|
+
readonly data: Value;
|
|
43
|
+
} | {
|
|
44
|
+
readonly success: false;
|
|
45
|
+
readonly error: z.ZodError;
|
|
46
|
+
};
|
|
47
|
+
export declare function eventUnion<const Events extends readonly EventDefinition[]>(events: Events): EventUnion<Events>;
|
|
48
|
+
export declare function eventUnion<const Discriminator extends string, const Events extends readonly EventDefinition[]>(discriminator: Discriminator, events: Events): EventUnion<Events>;
|
|
49
|
+
export type EventUnionOf<Union extends EventUnion> = EventOf<Union["events"][number]>;
|
package/dist/event.js
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { assertJsonValue, cloneAndFreeze, DomainAuthoringError, normalizeTag, } from "./types";
|
|
3
|
+
const brandedPayloads = new WeakSet();
|
|
4
|
+
const CAMEL_CASE_KEY = /^[a-z][A-Za-z0-9]*$/;
|
|
5
|
+
const FORBIDDEN_PAYLOAD_DISCRIMINATORS = new Set(["eventType", "eventName", "eventPayloadName"]);
|
|
6
|
+
function rememberPayload(value) {
|
|
7
|
+
if (typeof value === "object" && value !== null)
|
|
8
|
+
brandedPayloads.add(value);
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
export function isEventPayload(value) {
|
|
12
|
+
return typeof value === "object" && value !== null && brandedPayloads.has(value);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* C# serialized payloads use JsonNamingPolicy.CamelCase and fail on a
|
|
16
|
+
* case-mismatched member. Check the authoring schema once so callers cannot
|
|
17
|
+
* accidentally publish a PascalCase or discriminator-bearing wire shape.
|
|
18
|
+
*/
|
|
19
|
+
function assertCamelCaseSchemaKeys(schema, path = "$") {
|
|
20
|
+
const definition = schema;
|
|
21
|
+
const def = definition.def;
|
|
22
|
+
if (def?.type === "object" && typeof def.shape === "object" && def.shape !== null) {
|
|
23
|
+
for (const [key, child] of Object.entries(def.shape)) {
|
|
24
|
+
if (!CAMEL_CASE_KEY.test(key) || FORBIDDEN_PAYLOAD_DISCRIMINATORS.has(key)) {
|
|
25
|
+
throw new DomainAuthoringError("EVENT_PAYLOAD_CAMEL_CASE_REQUIRED", `Event schema member ${path}.${key} must be camelCase and must not be a type discriminator`);
|
|
26
|
+
}
|
|
27
|
+
assertCamelCaseSchemaKeys(child, `${path}.${key}`);
|
|
28
|
+
}
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (def?.type === "array" && def.element instanceof z.ZodType) {
|
|
32
|
+
assertCamelCaseSchemaKeys(def.element, `${path}[]`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (def?.innerType instanceof z.ZodType) {
|
|
36
|
+
assertCamelCaseSchemaKeys(def.innerType, path);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (Array.isArray(def?.options)) {
|
|
40
|
+
for (const option of def.options)
|
|
41
|
+
if (option instanceof z.ZodType)
|
|
42
|
+
assertCamelCaseSchemaKeys(option, path);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export function event(name, schema, options) {
|
|
46
|
+
if (name.length === 0)
|
|
47
|
+
throw new DomainAuthoringError("EVENT_NAME_INVALID", "Event name must not be empty");
|
|
48
|
+
if (name.includes(":"))
|
|
49
|
+
throw new DomainAuthoringError("EVENT_NAME_INVALID", "Event name must not contain ':'");
|
|
50
|
+
if (Object.prototype.hasOwnProperty.call(options, "version")) {
|
|
51
|
+
throw new DomainAuthoringError("EVENT_VERSION_REMOVED", "Event version is removed; use a distinct event payload name");
|
|
52
|
+
}
|
|
53
|
+
const eventType = name;
|
|
54
|
+
assertCamelCaseSchemaKeys(schema);
|
|
55
|
+
const parse = (payload) => {
|
|
56
|
+
const parsed = schema.parse(payload);
|
|
57
|
+
assertJsonValue(parsed, "event-construction");
|
|
58
|
+
return parsed;
|
|
59
|
+
};
|
|
60
|
+
const make = (payload) => {
|
|
61
|
+
const parsed = cloneAndFreeze(parse(payload));
|
|
62
|
+
return rememberPayload(parsed);
|
|
63
|
+
};
|
|
64
|
+
const create = (payload) => {
|
|
65
|
+
const parsed = parse(payload);
|
|
66
|
+
const rawTags = options.tags(parsed);
|
|
67
|
+
if (!Array.isArray(rawTags))
|
|
68
|
+
throw new DomainAuthoringError("EVENT_TAGS_INVALID", `Event ${name} tags must be an array`);
|
|
69
|
+
const tags = rawTags.map(normalizeTag);
|
|
70
|
+
return Object.freeze({
|
|
71
|
+
eventName: name,
|
|
72
|
+
eventPayloadName: name,
|
|
73
|
+
eventType,
|
|
74
|
+
payload: assertJsonValue(parsed),
|
|
75
|
+
tags,
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
const tagFamilies = options.tagFamily === undefined
|
|
79
|
+
? []
|
|
80
|
+
: [typeof options.tagFamily === "string" ? options.tagFamily : options.tagFamily.family];
|
|
81
|
+
return Object.freeze({
|
|
82
|
+
name,
|
|
83
|
+
eventPayloadName: name,
|
|
84
|
+
eventType,
|
|
85
|
+
key: eventType,
|
|
86
|
+
schema,
|
|
87
|
+
tags: (payload) => {
|
|
88
|
+
const rawTags = options.tags(payload);
|
|
89
|
+
if (!Array.isArray(rawTags))
|
|
90
|
+
throw new DomainAuthoringError("EVENT_TAGS_INVALID", `Event ${name} tags must be an array`);
|
|
91
|
+
return Object.freeze(rawTags.map(normalizeTag));
|
|
92
|
+
},
|
|
93
|
+
make,
|
|
94
|
+
parse,
|
|
95
|
+
create,
|
|
96
|
+
construct: create,
|
|
97
|
+
tagFamilies: Object.freeze(tagFamilies),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
function parseUnion(events, value) {
|
|
101
|
+
let lastError;
|
|
102
|
+
for (const definition of events) {
|
|
103
|
+
try {
|
|
104
|
+
return definition.make(value);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
lastError = error;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
throw new DomainAuthoringError("EVENT_UNION_INVALID", "Value did not match any event in the union", { cause: lastError });
|
|
111
|
+
}
|
|
112
|
+
function schemaHasDiscriminator(schema, discriminator) {
|
|
113
|
+
const candidate = schema;
|
|
114
|
+
return candidate.def?.type === "object" &&
|
|
115
|
+
typeof candidate.def.shape === "object" &&
|
|
116
|
+
candidate.def.shape !== null &&
|
|
117
|
+
discriminator in candidate.def.shape;
|
|
118
|
+
}
|
|
119
|
+
export function eventUnion(discriminatorOrEvents, maybeEvents) {
|
|
120
|
+
const discriminator = typeof discriminatorOrEvents === "string" ? discriminatorOrEvents : undefined;
|
|
121
|
+
const events = (typeof discriminatorOrEvents === "string" ? maybeEvents : discriminatorOrEvents) ?? [];
|
|
122
|
+
if (events.length === 0)
|
|
123
|
+
throw new DomainAuthoringError("EVENT_UNION_EMPTY", "An event union must contain an event");
|
|
124
|
+
let zodUnion;
|
|
125
|
+
if (discriminator === undefined || events.every((definition) => schemaHasDiscriminator(definition.schema, discriminator))) {
|
|
126
|
+
try {
|
|
127
|
+
zodUnion = (discriminator === undefined
|
|
128
|
+
? z.union(events.map((definition) => definition.schema))
|
|
129
|
+
: z.discriminatedUnion(discriminator, events.map((definition) => definition.schema)));
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// A caller may use the optional discriminator only as an authoring label
|
|
133
|
+
// while supplying schemas that are not Zod discriminated objects. The
|
|
134
|
+
// branded parser below remains the fail-closed fallback for that surface.
|
|
135
|
+
zodUnion = undefined;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const parse = (value) => {
|
|
139
|
+
if (discriminator !== undefined && (typeof value !== "object" || value === null || Array.isArray(value))) {
|
|
140
|
+
throw new DomainAuthoringError("EVENT_UNION_DISCRIMINATOR_INVALID", `Union discriminator ${discriminator} was absent`);
|
|
141
|
+
}
|
|
142
|
+
if (discriminator !== undefined && zodUnion !== undefined) {
|
|
143
|
+
try {
|
|
144
|
+
zodUnion.parse(value);
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
throw new DomainAuthoringError("EVENT_UNION_INVALID", "Value did not match the event discriminator", { cause: error });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return parseUnion(events, value);
|
|
151
|
+
};
|
|
152
|
+
const schema = zodUnion ?? z.any().refine((value) => {
|
|
153
|
+
try {
|
|
154
|
+
parse(value);
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
}, { message: "Value did not match the event union" });
|
|
161
|
+
const safeParse = (value) => {
|
|
162
|
+
try {
|
|
163
|
+
return { success: true, data: parse(value) };
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
return { success: false, error: error instanceof z.ZodError ? error : new z.ZodError([]) };
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
return Object.freeze({
|
|
170
|
+
kind: "event-union",
|
|
171
|
+
events,
|
|
172
|
+
eventTypes: Object.freeze(events.map((definition) => definition.eventType)),
|
|
173
|
+
...(discriminator === undefined ? {} : { discriminator }),
|
|
174
|
+
schema,
|
|
175
|
+
parse,
|
|
176
|
+
safeParse,
|
|
177
|
+
});
|
|
178
|
+
}
|
package/dist/index.d.ts
ADDED