@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 +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/schedule.d.ts +50 -0
- package/dist/schedule.d.ts.map +1 -0
- package/dist/schedule.js +76 -0
- package/dist/schedule.js.map +1 -0
- package/package.json +4 -4
- package/src/index.ts +8 -0
- package/src/schedule.ts +119 -0
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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"}
|
package/dist/schedule.js
ADDED
|
@@ -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
|
|
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.
|
|
52
|
-
"@kronos-ts/modelling": "0.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.
|
|
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,
|
package/src/schedule.ts
ADDED
|
@@ -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
|
+
}
|