@valentinkolb/sync 2.1.2 → 3.0.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.
@@ -0,0 +1,164 @@
1
+ import { type MisfirePolicy } from "../internal/cron";
2
+ type JobSubmitter = {
3
+ id: string;
4
+ submit(cfg: {
5
+ input: unknown;
6
+ key?: string;
7
+ keyTtlMs?: number;
8
+ at?: number;
9
+ delayMs?: number;
10
+ meta?: Record<string, unknown>;
11
+ }): Promise<string>;
12
+ validateInput?(input: unknown): void;
13
+ };
14
+ export type SchedulerMetric = {
15
+ type: "leader_acquired";
16
+ ts: number;
17
+ } | {
18
+ type: "leader_lost";
19
+ ts: number;
20
+ reason: "extend_failed" | "stop";
21
+ } | {
22
+ type: "tick_error";
23
+ ts: number;
24
+ message: string;
25
+ } | {
26
+ type: "schedule_registered";
27
+ ts: number;
28
+ scheduleId: string;
29
+ created: boolean;
30
+ } | {
31
+ type: "schedule_updated";
32
+ ts: number;
33
+ scheduleId: string;
34
+ } | {
35
+ type: "schedule_unregistered";
36
+ ts: number;
37
+ scheduleId: string;
38
+ } | {
39
+ type: "dispatch_submitted";
40
+ ts: number;
41
+ scheduleId: string;
42
+ slotTs: number;
43
+ jobId: string;
44
+ } | {
45
+ type: "dispatch_skipped";
46
+ ts: number;
47
+ scheduleId: string;
48
+ reason: "missing_handler" | "cas_stale";
49
+ } | {
50
+ type: "dispatch_failed";
51
+ ts: number;
52
+ scheduleId: string;
53
+ message: string;
54
+ } | {
55
+ type: "dispatch_dlq";
56
+ ts: number;
57
+ scheduleId: string;
58
+ slotTs: number;
59
+ message: string;
60
+ } | {
61
+ type: "dispatch_advanced_after_failures";
62
+ ts: number;
63
+ scheduleId: string;
64
+ slotTs: number;
65
+ failures: number;
66
+ } | {
67
+ type: "trigger_submitted";
68
+ ts: number;
69
+ scheduleId: string;
70
+ jobId: string;
71
+ } | {
72
+ type: "trigger_rejected";
73
+ ts: number;
74
+ scheduleId: string;
75
+ reason: "missing_schedule" | "missing_handler" | "invalid_schedule";
76
+ } | {
77
+ type: "trigger_failed";
78
+ ts: number;
79
+ scheduleId: string;
80
+ message: string;
81
+ };
82
+ export type SchedulerConfig = {
83
+ id: string;
84
+ prefix?: string;
85
+ leader?: {
86
+ leaseMs?: number;
87
+ heartbeatMs?: number;
88
+ };
89
+ dispatch?: {
90
+ tickMs?: number;
91
+ batchSize?: number;
92
+ maxSubmitsPerTick?: number;
93
+ submitRetries?: number;
94
+ submitBackoffBaseMs?: number;
95
+ submitBackoffMaxMs?: number;
96
+ scheduledJobKeyTtlMs?: number;
97
+ dlqMaxEntries?: number;
98
+ maxConsecutiveDispatchFailures?: number;
99
+ };
100
+ strictHandlers?: boolean;
101
+ onMetric?: (metric: SchedulerMetric) => void;
102
+ };
103
+ export type SchedulerRegisterConfig = {
104
+ id: string;
105
+ cron: string;
106
+ tz?: string;
107
+ job: JobSubmitter;
108
+ input: unknown;
109
+ misfire?: MisfirePolicy;
110
+ maxCatchUpRuns?: number;
111
+ meta?: Record<string, unknown>;
112
+ };
113
+ export type SchedulerUnregisterConfig = {
114
+ id: string;
115
+ };
116
+ export type SchedulerTriggerNowConfig = {
117
+ id: string;
118
+ key?: string;
119
+ };
120
+ export type SchedulerGetConfig = {
121
+ id: string;
122
+ };
123
+ export type SchedulerInfo = {
124
+ id: string;
125
+ cron: string;
126
+ tz: string;
127
+ misfire: MisfirePolicy;
128
+ maxCatchUpRuns: number;
129
+ jobId: string;
130
+ nextRunAt: number;
131
+ createdAt: number;
132
+ updatedAt: number;
133
+ };
134
+ export type SchedulerMetricsSnapshot = {
135
+ isLeader: boolean;
136
+ leaderEpoch: number;
137
+ leaderChanges: number;
138
+ dispatchSubmitted: number;
139
+ dispatchFailed: number;
140
+ dispatchRetried: number;
141
+ dispatchSkipped: number;
142
+ dispatchDlq: number;
143
+ triggerSubmitted: number;
144
+ triggerFailed: number;
145
+ triggerRejected: number;
146
+ tickErrors: number;
147
+ lastTickAt: number | null;
148
+ };
149
+ export type Scheduler = {
150
+ id: string;
151
+ start(): void;
152
+ stop(): Promise<void>;
153
+ register(cfg: SchedulerRegisterConfig): Promise<{
154
+ created: boolean;
155
+ updated: boolean;
156
+ }>;
157
+ unregister(cfg: SchedulerUnregisterConfig): Promise<void>;
158
+ triggerNow(cfg: SchedulerTriggerNowConfig): Promise<string>;
159
+ get(cfg: SchedulerGetConfig): Promise<SchedulerInfo | null>;
160
+ list(): Promise<SchedulerInfo[]>;
161
+ metrics(): SchedulerMetricsSnapshot;
162
+ };
163
+ export declare const scheduler: (config: SchedulerConfig) => Scheduler;
164
+ export {};
@@ -0,0 +1,17 @@
1
+ export interface Store {
2
+ get(key: string): unknown | undefined;
3
+ set(key: string, value: unknown, ttlMs?: number): void;
4
+ del(key: string): void;
5
+ keys(prefix?: string): string[];
6
+ }
7
+ export declare class MemoryStore implements Store {
8
+ private data;
9
+ private timers;
10
+ get(key: string): unknown | undefined;
11
+ set(key: string, value: unknown, ttlMs?: number): void;
12
+ del(key: string): void;
13
+ keys(prefix?: string): string[];
14
+ /** Clear all keys and timers (useful for tests). */
15
+ clear(): void;
16
+ }
17
+ export declare const createMemoryStore: () => MemoryStore;
@@ -0,0 +1,65 @@
1
+ import type { z } from "zod";
2
+ import { type Store } from "./store";
3
+ export type TopicConfig<TSchema extends z.ZodTypeAny> = {
4
+ id: string;
5
+ schema: TSchema;
6
+ tenantId?: string;
7
+ prefix?: string;
8
+ limits?: {
9
+ payloadBytes?: number;
10
+ };
11
+ retentionMs?: number;
12
+ store?: Store;
13
+ };
14
+ export type TopicPubConfig<T> = {
15
+ tenantId?: string;
16
+ data: T;
17
+ orderingKey?: string;
18
+ idempotencyKey?: string;
19
+ idempotencyTtlMs?: number;
20
+ meta?: Record<string, unknown>;
21
+ };
22
+ export type TopicRecvConfig = {
23
+ tenantId?: string;
24
+ timeoutMs?: number;
25
+ wait?: boolean;
26
+ signal?: AbortSignal;
27
+ };
28
+ export type TopicDelivery<T> = {
29
+ data: T;
30
+ eventId: string;
31
+ deliveryId: string;
32
+ cursor: string;
33
+ orderingKey?: string;
34
+ publishedAt: number;
35
+ meta?: Record<string, unknown>;
36
+ commit(): Promise<boolean>;
37
+ };
38
+ export type TopicLiveConfig = {
39
+ tenantId?: string;
40
+ after?: string;
41
+ signal?: AbortSignal;
42
+ timeoutMs?: number;
43
+ };
44
+ export type TopicLiveEvent<T> = {
45
+ data: T;
46
+ eventId: string;
47
+ cursor: string;
48
+ orderingKey?: string;
49
+ publishedAt: number;
50
+ meta?: Record<string, unknown>;
51
+ };
52
+ export type TopicReader<T> = {
53
+ group: string;
54
+ recv(cfg?: TopicRecvConfig): Promise<TopicDelivery<T> | null>;
55
+ stream(cfg?: TopicRecvConfig): AsyncIterable<TopicDelivery<T>>;
56
+ };
57
+ export type Topic<T> = {
58
+ pub(cfg: TopicPubConfig<T>): Promise<{
59
+ eventId: string;
60
+ cursor: string;
61
+ }>;
62
+ reader(group?: string): TopicReader<T>;
63
+ live(cfg?: TopicLiveConfig): AsyncIterable<TopicLiveEvent<T>>;
64
+ };
65
+ export declare const topic: <TSchema extends z.ZodTypeAny>(config: TopicConfig<TSchema>) => Topic<z.infer<TSchema>>;
@@ -0,0 +1,130 @@
1
+ import type { z } from "zod";
2
+ export declare class RegistryCapacityError extends Error {
3
+ constructor(message?: string);
4
+ }
5
+ export declare class RegistryPayloadTooLargeError extends Error {
6
+ constructor(message: string);
7
+ }
8
+ export type RegistryConfig<TSchema extends z.ZodTypeAny> = {
9
+ id: string;
10
+ schema: TSchema;
11
+ tenantId?: string;
12
+ prefix?: string;
13
+ limits?: {
14
+ maxEntries?: number;
15
+ maxPayloadBytes?: number;
16
+ eventRetentionMs?: number;
17
+ eventMaxLen?: number;
18
+ tombstoneRetentionMs?: number;
19
+ reconcileBatchSize?: number;
20
+ };
21
+ };
22
+ export type RegistryUpsertConfig<T> = {
23
+ key: string;
24
+ value: T;
25
+ ttlMs?: number;
26
+ tenantId?: string;
27
+ };
28
+ export type RegistryTouchConfig = {
29
+ key: string;
30
+ tenantId?: string;
31
+ };
32
+ export type RegistryRemoveConfig = {
33
+ key: string;
34
+ reason?: string;
35
+ tenantId?: string;
36
+ };
37
+ export type RegistryGetConfig = {
38
+ key: string;
39
+ tenantId?: string;
40
+ includeExpired?: boolean;
41
+ };
42
+ export type RegistryListConfig = {
43
+ prefix?: string;
44
+ status?: "active" | "expired";
45
+ tenantId?: string;
46
+ limit?: number;
47
+ afterKey?: string;
48
+ };
49
+ export type RegistryCasConfig<T> = {
50
+ key: string;
51
+ version: string;
52
+ value: T;
53
+ tenantId?: string;
54
+ };
55
+ export type RegistryEntry<T> = {
56
+ key: string;
57
+ value: T;
58
+ version: string;
59
+ status: "active" | "expired";
60
+ createdAt: number;
61
+ updatedAt: number;
62
+ ttlMs: number | null;
63
+ expiresAt: number | null;
64
+ };
65
+ export type RegistrySnapshot<T> = {
66
+ entries: RegistryEntry<T>[];
67
+ cursor: string;
68
+ nextKey?: string;
69
+ };
70
+ export type RegistryRecvConfig = {
71
+ wait?: boolean;
72
+ timeoutMs?: number;
73
+ signal?: AbortSignal;
74
+ };
75
+ export type RegistryEvent<T> = {
76
+ type: "upsert";
77
+ cursor: string;
78
+ entry: RegistryEntry<T>;
79
+ } | {
80
+ type: "touch";
81
+ cursor: string;
82
+ key: string;
83
+ version: string;
84
+ updatedAt: number;
85
+ expiresAt: number;
86
+ } | {
87
+ type: "delete";
88
+ cursor: string;
89
+ key: string;
90
+ version: string;
91
+ removedAt: number;
92
+ reason?: string;
93
+ } | {
94
+ type: "expire";
95
+ cursor: string;
96
+ key: string;
97
+ version: string;
98
+ removedAt: number;
99
+ } | {
100
+ type: "overflow";
101
+ cursor: string;
102
+ after: string;
103
+ firstAvailable: string;
104
+ };
105
+ export type RegistryReader<T> = {
106
+ recv(cfg?: RegistryRecvConfig): Promise<RegistryEvent<T> | null>;
107
+ stream(cfg?: RegistryRecvConfig): AsyncIterable<RegistryEvent<T>>;
108
+ };
109
+ export type Registry<T> = {
110
+ upsert(cfg: RegistryUpsertConfig<T>): Promise<RegistryEntry<T>>;
111
+ touch(cfg: RegistryTouchConfig): Promise<{
112
+ ok: boolean;
113
+ version?: string;
114
+ expiresAt?: number;
115
+ }>;
116
+ remove(cfg: RegistryRemoveConfig): Promise<boolean>;
117
+ get(cfg: RegistryGetConfig): Promise<RegistryEntry<T> | null>;
118
+ list(cfg?: RegistryListConfig): Promise<RegistrySnapshot<T>>;
119
+ cas(cfg: RegistryCasConfig<T>): Promise<{
120
+ ok: boolean;
121
+ entry?: RegistryEntry<T>;
122
+ }>;
123
+ reader(cfg?: {
124
+ key?: string;
125
+ prefix?: string;
126
+ after?: string;
127
+ tenantId?: string;
128
+ }): RegistryReader<T>;
129
+ };
130
+ export declare const registry: <TSchema extends z.ZodTypeAny>(config: RegistryConfig<TSchema>) => Registry<z.infer<TSchema>>;