@kronos-ts/eventsourcing 0.1.5 → 0.2.1

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/dist/index.d.ts CHANGED
@@ -12,5 +12,6 @@ export { type SnapshotPolicy, type EvolutionResult, afterEvents, whenSourcingTim
12
12
  export { type Snapshot, type SnapshotStore, createInMemorySnapshotStore, } from "./snapshot-store.js";
13
13
  export { createInterceptingEventStore } from "./intercepting-event-store.js";
14
14
  export { load, STATE_MANAGER_KEY } from "./load.js";
15
+ export { schedule, scheduleAfter, cancelSchedule, EVENT_SCHEDULER_KEY, type ScheduleFunction, type ScheduleAfterFunction, } from "./schedule.js";
15
16
  export { append, BUFFERED_EVENTS_KEY, SOURCING_INFOS_KEY, STATE_CACHE_KEY, STATE_MODULES_KEY, } from "./append.js";
16
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,iBAAiB,EACtB,MAAM,EACN,QAAQ,EACR,mBAAmB,EACnB,QAAQ,EACR,QAAQ,EACR,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EACL,KAAK,iBAAiB,EACtB,iBAAiB,GAClB,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EACL,KAAK,eAAe,EACpB,eAAe,GAChB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EACL,KAAK,UAAU,EACf,KAAK,cAAc,GACpB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EACL,KAAK,qBAAqB,EAC1B,2BAA2B,GAC5B,MAAM,8BAA8B,CAAA;AAErC,OAAO,EACL,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,4BAA4B,CAAA;AAEnC,OAAO,EAAE,KAAK,kBAAkB,EAAE,KAAK,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAE3F,OAAO,EACL,KAAK,WAAW,EAChB,0BAA0B,EAC1B,wBAAwB,EACxB,gBAAgB,GACjB,MAAM,mBAAmB,CAAA;AAE1B,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAA;AAC5E,YAAY,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAA;AAElF,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,WAAW,EACX,uBAAuB,EACvB,gBAAgB,GACjB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,2BAA2B,GAC5B,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAA;AAG5E,OAAO,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AACnD,OAAO,EACL,MAAM,EACN,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,GAClB,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,iBAAiB,EACtB,MAAM,EACN,QAAQ,EACR,mBAAmB,EACnB,QAAQ,EACR,QAAQ,EACR,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EACL,KAAK,iBAAiB,EACtB,iBAAiB,GAClB,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EACL,KAAK,eAAe,EACpB,eAAe,GAChB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EACL,KAAK,UAAU,EACf,KAAK,cAAc,GACpB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EACL,KAAK,qBAAqB,EAC1B,2BAA2B,GAC5B,MAAM,8BAA8B,CAAA;AAErC,OAAO,EACL,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,4BAA4B,CAAA;AAEnC,OAAO,EAAE,KAAK,kBAAkB,EAAE,KAAK,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAE3F,OAAO,EACL,KAAK,WAAW,EAChB,0BAA0B,EAC1B,wBAAwB,EACxB,gBAAgB,GACjB,MAAM,mBAAmB,CAAA;AAE1B,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAA;AAC5E,YAAY,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAA;AAElF,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,WAAW,EACX,uBAAuB,EACvB,gBAAgB,GACjB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,2BAA2B,GAC5B,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAA;AAG5E,OAAO,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AACnD,OAAO,EACL,QAAQ,EACR,aAAa,EACb,cAAc,EACd,mBAAmB,EACnB,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,GAC3B,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,MAAM,EACN,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,GAClB,MAAM,aAAa,CAAA"}
package/dist/index.js CHANGED
@@ -12,5 +12,6 @@ export { createInMemorySnapshotStore, } from "./snapshot-store.js";
12
12
  export { createInterceptingEventStore } from "./intercepting-event-store.js";
13
13
  // Module-level handler helpers (Plan 04-01 / HDL-02 / D-42)
14
14
  export { load, STATE_MANAGER_KEY } from "./load.js";
15
+ export { schedule, scheduleAfter, cancelSchedule, EVENT_SCHEDULER_KEY, } from "./schedule.js";
15
16
  export { append, BUFFERED_EVENTS_KEY, SOURCING_INFOS_KEY, STATE_CACHE_KEY, STATE_MODULES_KEY, } from "./append.js";
16
17
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,MAAM,EACN,QAAQ,EACR,mBAAmB,EACnB,QAAQ,EACR,QAAQ,EACR,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EAEL,iBAAiB,GAClB,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EAEL,eAAe,GAChB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EAGN,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAEL,2BAA2B,GAC5B,MAAM,8BAA8B,CAAA;AAErC,OAAO,EACL,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,4BAA4B,CAAA;AAEnC,OAAO,EAAmD,MAAM,2BAA2B,CAAA;AAE3F,OAAO,EAEL,0BAA0B,EAC1B,wBAAwB,EACxB,gBAAgB,GACjB,MAAM,mBAAmB,CAAA;AAE1B,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAA;AAG5E,OAAO,EAGL,WAAW,EACX,uBAAuB,EACvB,gBAAgB,GACjB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAGL,2BAA2B,GAC5B,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAA;AAE5E,4DAA4D;AAC5D,OAAO,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AACnD,OAAO,EACL,MAAM,EACN,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,GAClB,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,MAAM,EACN,QAAQ,EACR,mBAAmB,EACnB,QAAQ,EACR,QAAQ,EACR,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EAEL,iBAAiB,GAClB,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EAEL,eAAe,GAChB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EAGN,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAEL,2BAA2B,GAC5B,MAAM,8BAA8B,CAAA;AAErC,OAAO,EACL,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,4BAA4B,CAAA;AAEnC,OAAO,EAAmD,MAAM,2BAA2B,CAAA;AAE3F,OAAO,EAEL,0BAA0B,EAC1B,wBAAwB,EACxB,gBAAgB,GACjB,MAAM,mBAAmB,CAAA;AAE1B,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAA;AAG5E,OAAO,EAGL,WAAW,EACX,uBAAuB,EACvB,gBAAgB,GACjB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAGL,2BAA2B,GAC5B,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAA;AAE5E,4DAA4D;AAC5D,OAAO,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AACnD,OAAO,EACL,QAAQ,EACR,aAAa,EACb,cAAc,EACd,mBAAmB,GAGpB,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,MAAM,EACN,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,GAClB,MAAM,aAAa,CAAA"}
@@ -0,0 +1,50 @@
1
+ import { type Metadata, type ResourceKey } from "@kronos-ts/common";
2
+ import type { z } from "zod";
3
+ import type { EventDescriptor, EventScheduler, ScheduleToken, CancelResult } from "@kronos-ts/messaging";
4
+ /**
5
+ * Resource key for the event scheduler component.
6
+ * Written by handling modules + processors at handler-invocation entry,
7
+ * exactly like {@link STATE_MANAGER_KEY} — so the {@link schedule} helper
8
+ * resolves the framework-configured scheduler from the active UnitOfWork.
9
+ */
10
+ export declare const EVENT_SCHEDULER_KEY: ResourceKey<EventScheduler>;
11
+ /** Schedule an event for future append from inside a handler. */
12
+ export interface ScheduleFunction {
13
+ <P extends z.ZodType>(event: EventDescriptor<P>, payload: z.infer<P>, at: Date): Promise<ScheduleToken>;
14
+ <P extends z.ZodType>(event: EventDescriptor<P>, payload: z.infer<P>, at: Date, metadata: Metadata): Promise<ScheduleToken>;
15
+ }
16
+ /**
17
+ * Schedule {@link event} to be appended at {@link at}.
18
+ *
19
+ * The same one-call ergonomics as {@link append}/{@link send}: pass the event
20
+ * descriptor + payload and the helper builds the {@link EventMessage} (tags,
21
+ * metadata, identifier) and hands it to the configured {@link EventScheduler}.
22
+ * No need to fetch the scheduler or construct a message by hand.
23
+ *
24
+ * Must be called from inside a UnitOfWork (throws otherwise). The schedule
25
+ * participates in the active transaction — if the handler's UoW rolls back,
26
+ * nothing is scheduled; if it commits, the event is durably scheduled and will
27
+ * fire at or after `at` unless cancelled. Metadata defaults to the UoW
28
+ * metadata, so correlation/causation lineage carries onto the fired event.
29
+ *
30
+ * Returns a {@link ScheduleToken} — persist it (e.g. in state via an event) to
31
+ * {@link cancelSchedule} later. That is the deadline/process-manager pattern.
32
+ */
33
+ export declare const schedule: ScheduleFunction;
34
+ /** Schedule an event a fixed delay from now, from inside a handler. */
35
+ export interface ScheduleAfterFunction {
36
+ <P extends z.ZodType>(event: EventDescriptor<P>, payload: z.infer<P>, delayMs: number): Promise<ScheduleToken>;
37
+ <P extends z.ZodType>(event: EventDescriptor<P>, payload: z.infer<P>, delayMs: number, metadata: Metadata): Promise<ScheduleToken>;
38
+ }
39
+ /**
40
+ * Convenience wrapper over {@link schedule}: fire `delayMs` milliseconds from
41
+ * now instead of at an absolute {@link Date}.
42
+ */
43
+ export declare const scheduleAfter: ScheduleAfterFunction;
44
+ /**
45
+ * Attempt to cancel a previously {@link schedule}d event from inside a handler.
46
+ * UoW-aware: joins the active transaction, so cancelling then throwing does not
47
+ * leave the schedule cancelled. See {@link CancelResult} for outcomes.
48
+ */
49
+ export declare const cancelSchedule: (token: ScheduleToken) => Promise<CancelResult>;
50
+ //# sourceMappingURL=schedule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule.d.ts","sourceRoot":"","sources":["../src/schedule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,QAAQ,EAAe,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAEpG,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAC5B,OAAO,KAAK,EACV,eAAe,EAEf,cAAc,EACd,aAAa,EACb,YAAY,EACb,MAAM,sBAAsB,CAAA;AAE7B;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,EAAE,WAAW,CAAC,cAAc,CAAiC,CAAA;AAE7F,iEAAiE;AACjE,MAAM,WAAW,gBAAgB;IAC/B,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,CAAA;IACvG,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAClB,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,EACzB,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EACnB,EAAE,EAAE,IAAI,EACR,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,aAAa,CAAC,CAAA;CAC1B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,QAAQ,EAAE,gBA6BD,CAAA;AAEtB,uEAAuE;AACvE,MAAM,WAAW,qBAAqB;IACpC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAAA;IAC9G,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAClB,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,EACzB,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EACnB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,aAAa,CAAC,CAAA;CAC1B;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,qBAaD,CAAA;AAE3B;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAU,OAAO,aAAa,KAAG,OAAO,CAAC,YAAY,CAK/E,CAAA"}
@@ -0,0 +1,76 @@
1
+ import { generateIdentifier, resourceKey } from "@kronos-ts/common";
2
+ import { requireInvocationPhase } from "@kronos-ts/messaging/processing-state";
3
+ /**
4
+ * Resource key for the event scheduler component.
5
+ * Written by handling modules + processors at handler-invocation entry,
6
+ * exactly like {@link STATE_MANAGER_KEY} — so the {@link schedule} helper
7
+ * resolves the framework-configured scheduler from the active UnitOfWork.
8
+ */
9
+ export const EVENT_SCHEDULER_KEY = resourceKey("eventScheduler");
10
+ /**
11
+ * Schedule {@link event} to be appended at {@link at}.
12
+ *
13
+ * The same one-call ergonomics as {@link append}/{@link send}: pass the event
14
+ * descriptor + payload and the helper builds the {@link EventMessage} (tags,
15
+ * metadata, identifier) and hands it to the configured {@link EventScheduler}.
16
+ * No need to fetch the scheduler or construct a message by hand.
17
+ *
18
+ * Must be called from inside a UnitOfWork (throws otherwise). The schedule
19
+ * participates in the active transaction — if the handler's UoW rolls back,
20
+ * nothing is scheduled; if it commits, the event is durably scheduled and will
21
+ * fire at or after `at` unless cancelled. Metadata defaults to the UoW
22
+ * metadata, so correlation/causation lineage carries onto the fired event.
23
+ *
24
+ * Returns a {@link ScheduleToken} — persist it (e.g. in state via an event) to
25
+ * {@link cancelSchedule} later. That is the deadline/process-manager pattern.
26
+ */
27
+ export const schedule = (async (event, payload, at, metadata) => {
28
+ const state = requireInvocationPhase(); // D-43 mutator guard
29
+ const scheduler = state.resources.get(EVENT_SCHEDULER_KEY.symbol);
30
+ if (!scheduler)
31
+ throw new Error("No event scheduler configured");
32
+ // Reject malformed fire times. A past-but-valid `at` is allowed — it fires
33
+ // ASAP, which is the intended deadline semantic — but an Invalid Date is a
34
+ // caller bug that otherwise behaves inconsistently across schedulers (the
35
+ // in-memory one fires immediately; the postgres one throws on toISOString at
36
+ // insert time). Fail fast and uniformly here instead.
37
+ if (!(at instanceof Date) || Number.isNaN(at.getTime())) {
38
+ throw new Error(`schedule: \`at\` must be a valid Date, received ${String(at)}`);
39
+ }
40
+ const eventMessage = {
41
+ identifier: generateIdentifier(),
42
+ name: event.name,
43
+ version: event.version,
44
+ payload,
45
+ metadata: metadata ?? state.metadata,
46
+ timestamp: Date.now(),
47
+ tags: event.tags ? event.tags(payload) : [],
48
+ };
49
+ return scheduler.schedule(eventMessage, at);
50
+ });
51
+ /**
52
+ * Convenience wrapper over {@link schedule}: fire `delayMs` milliseconds from
53
+ * now instead of at an absolute {@link Date}.
54
+ */
55
+ export const scheduleAfter = (async (event, payload, delayMs, metadata) => {
56
+ // A non-finite delay (NaN/Infinity) would produce an Invalid Date; reject it
57
+ // here so the error names the actual offending argument. A negative delay is
58
+ // allowed — it resolves to a past time and fires ASAP.
59
+ if (!Number.isFinite(delayMs)) {
60
+ throw new Error(`scheduleAfter: \`delayMs\` must be a finite number, received ${String(delayMs)}`);
61
+ }
62
+ return schedule(event, payload, new Date(Date.now() + delayMs), metadata);
63
+ });
64
+ /**
65
+ * Attempt to cancel a previously {@link schedule}d event from inside a handler.
66
+ * UoW-aware: joins the active transaction, so cancelling then throwing does not
67
+ * leave the schedule cancelled. See {@link CancelResult} for outcomes.
68
+ */
69
+ export const cancelSchedule = async (token) => {
70
+ const state = requireInvocationPhase(); // D-43 mutator guard
71
+ const scheduler = state.resources.get(EVENT_SCHEDULER_KEY.symbol);
72
+ if (!scheduler)
73
+ throw new Error("No event scheduler configured");
74
+ return scheduler.cancel(token);
75
+ };
76
+ //# sourceMappingURL=schedule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule.js","sourceRoot":"","sources":["../src/schedule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAiB,WAAW,EAAoB,MAAM,mBAAmB,CAAA;AACpG,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAA;AAU9E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAgC,WAAW,CAAC,gBAAgB,CAAC,CAAA;AAa7F;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAqB,CAAC,KAAK,EAC9C,KAAyB,EACzB,OAAmB,EACnB,EAAQ,EACR,QAAmB,EACK,EAAE;IAC1B,MAAM,KAAK,GAAG,sBAAsB,EAAE,CAAA,CAAC,qBAAqB;IAC5D,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,mBAAmB,CAAC,MAAM,CAA+B,CAAA;IAC/F,IAAI,CAAC,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAA;IAEhE,2EAA2E;IAC3E,2EAA2E;IAC3E,0EAA0E;IAC1E,6EAA6E;IAC7E,sDAAsD;IACtD,IAAI,CAAC,CAAC,EAAE,YAAY,IAAI,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,mDAAmD,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;IAClF,CAAC;IAED,MAAM,YAAY,GAAiB;QACjC,UAAU,EAAE,kBAAkB,EAAE;QAChC,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,OAAO;QACP,QAAQ,EAAE,QAAQ,IAAI,KAAK,CAAC,QAAQ;QACpC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;QACrB,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE;KAC5C,CAAA;IACD,OAAO,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,EAAE,CAAC,CAAA;AAC7C,CAAC,CAAqB,CAAA;AAatB;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAA0B,CAAC,KAAK,EACxD,KAA2B,EAC3B,OAAgB,EAChB,OAAe,EACf,QAAmB,EACnB,EAAE;IACF,6EAA6E;IAC7E,6EAA6E;IAC7E,uDAAuD;IACvD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,gEAAgE,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IACpG,CAAC;IACD,OAAO,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,EAAE,QAAoB,CAAC,CAAA;AACvF,CAAC,CAA0B,CAAA;AAE3B;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EAAE,KAAoB,EAAyB,EAAE;IAClF,MAAM,KAAK,GAAG,sBAAsB,EAAE,CAAA,CAAC,qBAAqB;IAC5D,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,mBAAmB,CAAC,MAAM,CAA+B,CAAA;IAC/F,IAAI,CAAC,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAA;IAChE,OAAO,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAChC,CAAC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kronos-ts/eventsourcing",
3
- "version": "0.1.5",
3
+ "version": "0.2.1",
4
4
  "description": "Event sourcing for Kronos — dynamic-consistency-boundary event store with load/append.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -48,11 +48,11 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "@kronos-ts/common": "0.1.1",
51
- "@kronos-ts/messaging": "0.4.0",
52
- "@kronos-ts/modelling": "0.2.2",
51
+ "@kronos-ts/messaging": "0.5.1",
52
+ "@kronos-ts/modelling": "0.2.4",
53
53
  "zod": "^4.3.6"
54
54
  },
55
55
  "devDependencies": {
56
- "@kronos-ts/app": "0.3.2"
56
+ "@kronos-ts/app": "0.3.4"
57
57
  }
58
58
  }
package/src/index.ts CHANGED
@@ -64,6 +64,14 @@ export { createInterceptingEventStore } from "./intercepting-event-store.js"
64
64
 
65
65
  // Module-level handler helpers (Plan 04-01 / HDL-02 / D-42)
66
66
  export { load, STATE_MANAGER_KEY } from "./load.js"
67
+ export {
68
+ schedule,
69
+ scheduleAfter,
70
+ cancelSchedule,
71
+ EVENT_SCHEDULER_KEY,
72
+ type ScheduleFunction,
73
+ type ScheduleAfterFunction,
74
+ } from "./schedule.js"
67
75
  export {
68
76
  append,
69
77
  BUFFERED_EVENTS_KEY,
@@ -0,0 +1,119 @@
1
+ import { generateIdentifier, type Metadata, resourceKey, type ResourceKey } from "@kronos-ts/common"
2
+ import { requireInvocationPhase } from "@kronos-ts/messaging/processing-state"
3
+ import type { z } from "zod"
4
+ import type {
5
+ EventDescriptor,
6
+ EventMessage,
7
+ EventScheduler,
8
+ ScheduleToken,
9
+ CancelResult,
10
+ } from "@kronos-ts/messaging"
11
+
12
+ /**
13
+ * Resource key for the event scheduler component.
14
+ * Written by handling modules + processors at handler-invocation entry,
15
+ * exactly like {@link STATE_MANAGER_KEY} — so the {@link schedule} helper
16
+ * resolves the framework-configured scheduler from the active UnitOfWork.
17
+ */
18
+ export const EVENT_SCHEDULER_KEY: ResourceKey<EventScheduler> = resourceKey("eventScheduler")
19
+
20
+ /** Schedule an event for future append from inside a handler. */
21
+ export interface ScheduleFunction {
22
+ <P extends z.ZodType>(event: EventDescriptor<P>, payload: z.infer<P>, at: Date): Promise<ScheduleToken>
23
+ <P extends z.ZodType>(
24
+ event: EventDescriptor<P>,
25
+ payload: z.infer<P>,
26
+ at: Date,
27
+ metadata: Metadata,
28
+ ): Promise<ScheduleToken>
29
+ }
30
+
31
+ /**
32
+ * Schedule {@link event} to be appended at {@link at}.
33
+ *
34
+ * The same one-call ergonomics as {@link append}/{@link send}: pass the event
35
+ * descriptor + payload and the helper builds the {@link EventMessage} (tags,
36
+ * metadata, identifier) and hands it to the configured {@link EventScheduler}.
37
+ * No need to fetch the scheduler or construct a message by hand.
38
+ *
39
+ * Must be called from inside a UnitOfWork (throws otherwise). The schedule
40
+ * participates in the active transaction — if the handler's UoW rolls back,
41
+ * nothing is scheduled; if it commits, the event is durably scheduled and will
42
+ * fire at or after `at` unless cancelled. Metadata defaults to the UoW
43
+ * metadata, so correlation/causation lineage carries onto the fired event.
44
+ *
45
+ * Returns a {@link ScheduleToken} — persist it (e.g. in state via an event) to
46
+ * {@link cancelSchedule} later. That is the deadline/process-manager pattern.
47
+ */
48
+ export const schedule: ScheduleFunction = (async <P extends z.ZodType>(
49
+ event: EventDescriptor<P>,
50
+ payload: z.infer<P>,
51
+ at: Date,
52
+ metadata?: Metadata,
53
+ ): Promise<ScheduleToken> => {
54
+ const state = requireInvocationPhase() // D-43 mutator guard
55
+ const scheduler = state.resources.get(EVENT_SCHEDULER_KEY.symbol) as EventScheduler | undefined
56
+ if (!scheduler) throw new Error("No event scheduler configured")
57
+
58
+ // Reject malformed fire times. A past-but-valid `at` is allowed — it fires
59
+ // ASAP, which is the intended deadline semantic — but an Invalid Date is a
60
+ // caller bug that otherwise behaves inconsistently across schedulers (the
61
+ // in-memory one fires immediately; the postgres one throws on toISOString at
62
+ // insert time). Fail fast and uniformly here instead.
63
+ if (!(at instanceof Date) || Number.isNaN(at.getTime())) {
64
+ throw new Error(`schedule: \`at\` must be a valid Date, received ${String(at)}`)
65
+ }
66
+
67
+ const eventMessage: EventMessage = {
68
+ identifier: generateIdentifier(),
69
+ name: event.name,
70
+ version: event.version,
71
+ payload,
72
+ metadata: metadata ?? state.metadata,
73
+ timestamp: Date.now(),
74
+ tags: event.tags ? event.tags(payload) : [],
75
+ }
76
+ return scheduler.schedule(eventMessage, at)
77
+ }) as ScheduleFunction
78
+
79
+ /** Schedule an event a fixed delay from now, from inside a handler. */
80
+ export interface ScheduleAfterFunction {
81
+ <P extends z.ZodType>(event: EventDescriptor<P>, payload: z.infer<P>, delayMs: number): Promise<ScheduleToken>
82
+ <P extends z.ZodType>(
83
+ event: EventDescriptor<P>,
84
+ payload: z.infer<P>,
85
+ delayMs: number,
86
+ metadata: Metadata,
87
+ ): Promise<ScheduleToken>
88
+ }
89
+
90
+ /**
91
+ * Convenience wrapper over {@link schedule}: fire `delayMs` milliseconds from
92
+ * now instead of at an absolute {@link Date}.
93
+ */
94
+ export const scheduleAfter: ScheduleAfterFunction = (async (
95
+ event: EventDescriptor<any>,
96
+ payload: unknown,
97
+ delayMs: number,
98
+ metadata?: Metadata,
99
+ ) => {
100
+ // A non-finite delay (NaN/Infinity) would produce an Invalid Date; reject it
101
+ // here so the error names the actual offending argument. A negative delay is
102
+ // allowed — it resolves to a past time and fires ASAP.
103
+ if (!Number.isFinite(delayMs)) {
104
+ throw new Error(`scheduleAfter: \`delayMs\` must be a finite number, received ${String(delayMs)}`)
105
+ }
106
+ return schedule(event, payload, new Date(Date.now() + delayMs), metadata as Metadata)
107
+ }) as ScheduleAfterFunction
108
+
109
+ /**
110
+ * Attempt to cancel a previously {@link schedule}d event from inside a handler.
111
+ * UoW-aware: joins the active transaction, so cancelling then throwing does not
112
+ * leave the schedule cancelled. See {@link CancelResult} for outcomes.
113
+ */
114
+ export const cancelSchedule = async (token: ScheduleToken): Promise<CancelResult> => {
115
+ const state = requireInvocationPhase() // D-43 mutator guard
116
+ const scheduler = state.resources.get(EVENT_SCHEDULER_KEY.symbol) as EventScheduler | undefined
117
+ if (!scheduler) throw new Error("No event scheduler configured")
118
+ return scheduler.cancel(token)
119
+ }