effect-mq 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 +21 -0
- package/README.md +235 -0
- package/dist/Job.d.ts +222 -0
- package/dist/Job.d.ts.map +1 -0
- package/dist/Job.js +218 -0
- package/dist/Job.js.map +1 -0
- package/dist/JobStore.d.ts +401 -0
- package/dist/JobStore.d.ts.map +1 -0
- package/dist/JobStore.js +89 -0
- package/dist/JobStore.js.map +1 -0
- package/dist/MemoryJobStore.d.ts +34 -0
- package/dist/MemoryJobStore.d.ts.map +1 -0
- package/dist/MemoryJobStore.js +381 -0
- package/dist/MemoryJobStore.js.map +1 -0
- package/dist/Worker.d.ts +127 -0
- package/dist/Worker.d.ts.map +1 -0
- package/dist/Worker.js +274 -0
- package/dist/Worker.js.map +1 -0
- package/dist/drizzle/DrizzleJobStore.d.ts +59 -0
- package/dist/drizzle/DrizzleJobStore.d.ts.map +1 -0
- package/dist/drizzle/DrizzleJobStore.js +426 -0
- package/dist/drizzle/DrizzleJobStore.js.map +1 -0
- package/dist/drizzle/index.d.ts +19 -0
- package/dist/drizzle/index.d.ts.map +1 -0
- package/dist/drizzle/index.js +19 -0
- package/dist/drizzle/index.js.map +1 -0
- package/dist/drizzle/schema.d.ts +464 -0
- package/dist/drizzle/schema.d.ts.map +1 -0
- package/dist/drizzle/schema.js +68 -0
- package/dist/drizzle/schema.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/testing/conformance.d.ts +27 -0
- package/dist/testing/conformance.d.ts.map +1 -0
- package/dist/testing/conformance.js +451 -0
- package/dist/testing/conformance.js.map +1 -0
- package/dist/testing/index.d.ts +8 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +8 -0
- package/dist/testing/index.js.map +1 -0
- package/package.json +71 -0
- package/src/Job.ts +606 -0
- package/src/JobStore.ts +446 -0
- package/src/MemoryJobStore.ts +467 -0
- package/src/Worker.ts +514 -0
- package/src/drizzle/DrizzleJobStore.ts +599 -0
- package/src/drizzle/index.ts +20 -0
- package/src/drizzle/schema.ts +116 -0
- package/src/index.ts +33 -0
- package/src/testing/conformance.ts +654 -0
- package/src/testing/index.ts +7 -0
package/dist/Worker.js
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The worker runtime of effect-mq.
|
|
3
|
+
*
|
|
4
|
+
* `Worker` is a service that runs registered job handlers against a
|
|
5
|
+
* `JobStore`. Handlers are registered through `Job.toLayer(handler)`; the
|
|
6
|
+
* worker starts a set of taker fibers per queue (bounded by the queue's
|
|
7
|
+
* concurrency), each running the loop:
|
|
8
|
+
*
|
|
9
|
+
* claim -> decode payload -> run handler -> ack (complete | retry | fail)
|
|
10
|
+
*
|
|
11
|
+
* plus two maintenance fibers: lock renewal for in-flight jobs, and stalled
|
|
12
|
+
* job recovery. On scope close, in-flight handlers are interrupted and their
|
|
13
|
+
* jobs released back to `waiting` without consuming an attempt.
|
|
14
|
+
*
|
|
15
|
+
* @since 0.1.0
|
|
16
|
+
*/
|
|
17
|
+
import { Cause, Clock, Context, Deferred, Duration, Effect, Exit, FiberSet, Layer, Schedule, Schema, Scope as Scope_, Tracer } from "effect";
|
|
18
|
+
import { isJobStoreError, JobStore, QueueName } from "./JobStore.js";
|
|
19
|
+
/**
|
|
20
|
+
* @since 0.1.0
|
|
21
|
+
*/
|
|
22
|
+
export class Worker extends Context.Service()("effect-mq/Worker") {
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Delay before attempt `attempt` (1-based) re-runs, per the job's policy.
|
|
26
|
+
*
|
|
27
|
+
* @internal
|
|
28
|
+
*/
|
|
29
|
+
export const backoffDelayMs = (backoff, attempt) => {
|
|
30
|
+
if (backoff === undefined)
|
|
31
|
+
return 0;
|
|
32
|
+
switch (backoff._tag) {
|
|
33
|
+
case "fixed":
|
|
34
|
+
return backoff.delayMs;
|
|
35
|
+
case "exponential":
|
|
36
|
+
return Math.round(backoff.delayMs * (backoff.factor ?? 2) ** (attempt - 1));
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
// Bounded so the "retry, then die/drop" paths are actually reachable and a
|
|
40
|
+
// persistently-broken driver cannot hang shutdown forever (worst case ~10s).
|
|
41
|
+
const storeRetryPolicy = Schedule.min([
|
|
42
|
+
Schedule.exponential(200, 1.5),
|
|
43
|
+
Schedule.spaced("30 seconds")
|
|
44
|
+
]).pipe(Schedule.upTo({ times: 8 }));
|
|
45
|
+
/**
|
|
46
|
+
* Build the worker service. Requires a `Scope` (fibers live in it) and the
|
|
47
|
+
* `JobStore`.
|
|
48
|
+
*
|
|
49
|
+
* @since 0.1.0
|
|
50
|
+
*/
|
|
51
|
+
export const make = (options) => Effect.gen(function* () {
|
|
52
|
+
// Taker fibers are forked from whichever registration arrives first; pin
|
|
53
|
+
// them to the worker's own context so they never inherit one job's
|
|
54
|
+
// locally provided services.
|
|
55
|
+
const workerContext = yield* Effect.context();
|
|
56
|
+
// SAFETY: when `options.store` is omitted the public signature fixes
|
|
57
|
+
// `StoreId` to its default `JobStore`, so the default key is the right
|
|
58
|
+
// `Context.Key<StoreId>`; when it is present the cast is an identity.
|
|
59
|
+
const storeKey = (options?.store ?? JobStore);
|
|
60
|
+
const store = yield* storeKey;
|
|
61
|
+
const fibers = yield* FiberSet.make();
|
|
62
|
+
const lockDurationMs = Duration.toMillis(options?.lockDuration ?? 30_000);
|
|
63
|
+
const lockRenewMs = options?.lockRenewInterval !== undefined
|
|
64
|
+
? Duration.toMillis(options.lockRenewInterval)
|
|
65
|
+
: Math.max(1, Math.floor(lockDurationMs / 2));
|
|
66
|
+
const stalledMs = Duration.toMillis(options?.stalledInterval ?? 30_000);
|
|
67
|
+
const maxStalledCount = options?.maxStalledCount ?? 1;
|
|
68
|
+
const pollMs = Duration.toMillis(options?.pollInterval ?? 5_000);
|
|
69
|
+
const workerId = options?.id ?? `worker-${Math.random().toString(36).slice(2, 10)}`;
|
|
70
|
+
const handlers = new Map();
|
|
71
|
+
const queueNames = new Map();
|
|
72
|
+
const startedQueues = new Set();
|
|
73
|
+
const inflight = new Map();
|
|
74
|
+
let tokenCounter = 0;
|
|
75
|
+
const nextToken = () => `${workerId}:${++tokenCounter}`;
|
|
76
|
+
// Local wake-up for registration changes. Versioned like the store's
|
|
77
|
+
// wakeToken so a pulse firing between "observe" and "await" is not lost.
|
|
78
|
+
let pulseVersion = 0;
|
|
79
|
+
let pulse = Deferred.makeUnsafe();
|
|
80
|
+
const firePulse = Effect.suspend(() => {
|
|
81
|
+
pulseVersion += 1;
|
|
82
|
+
const current = pulse;
|
|
83
|
+
pulse = Deferred.makeUnsafe();
|
|
84
|
+
return Deferred.succeed(current, void 0);
|
|
85
|
+
});
|
|
86
|
+
const awaitPulse = (observed) => Effect.suspend(() => pulseVersion > observed ? Effect.void : Deferred.await(pulse));
|
|
87
|
+
const retryStore = (effect) => effect.pipe(Effect.retry({
|
|
88
|
+
// Classified by tag (not instanceof) so errors from a duplicated
|
|
89
|
+
// module instance are still recognized.
|
|
90
|
+
while: (error) => isJobStoreError(error),
|
|
91
|
+
schedule: storeRetryPolicy
|
|
92
|
+
}), Effect.orDie);
|
|
93
|
+
// Ack-path safety: lost locks and vanished jobs mean another worker owns
|
|
94
|
+
// the job now — log and move on. Driver errors retry, then are dropped.
|
|
95
|
+
const ackSafely = (effect, what) => effect.pipe(Effect.retry({
|
|
96
|
+
while: (error) => isJobStoreError(error),
|
|
97
|
+
schedule: storeRetryPolicy
|
|
98
|
+
}), Effect.catch((error) => Effect.logWarning(`effect-mq: ${what} dropped (${error._tag})`, error)));
|
|
99
|
+
const routeFailure = (record, exit) => {
|
|
100
|
+
const attempt = record.attemptsMade + 1;
|
|
101
|
+
if (attempt >= record.attemptsMax) {
|
|
102
|
+
return { _tag: "Fail", exit };
|
|
103
|
+
}
|
|
104
|
+
return { _tag: "Retry", delayMs: backoffDelayMs(record.backoff, attempt), exit };
|
|
105
|
+
};
|
|
106
|
+
// Runs inside the taker's uninterruptible region; `restore` re-enables
|
|
107
|
+
// interruption only around the handler itself.
|
|
108
|
+
const processJob = (record, token, restore) => Effect.suspend(() => {
|
|
109
|
+
const entry = handlers.get(record.name);
|
|
110
|
+
if (entry === undefined) {
|
|
111
|
+
// Unregistered between claim and processing — hand the job back.
|
|
112
|
+
return ackSafely(store.release(record.id, token), "release");
|
|
113
|
+
}
|
|
114
|
+
const context = {
|
|
115
|
+
jobId: record.id,
|
|
116
|
+
name: record.name,
|
|
117
|
+
queue: record.queue,
|
|
118
|
+
attempt: record.attemptsMade + 1,
|
|
119
|
+
attemptsMax: record.attemptsMax
|
|
120
|
+
};
|
|
121
|
+
inflight.set(record.id, { id: record.id, token });
|
|
122
|
+
return Effect.gen(function* () {
|
|
123
|
+
const exit = yield* Effect.exit(restore(entry.run(record.payload, context)));
|
|
124
|
+
inflight.delete(record.id);
|
|
125
|
+
// Distinguish worker shutdown from a handler that interrupted
|
|
126
|
+
// itself: entering an interruptible region while an external
|
|
127
|
+
// interrupt is pending fails immediately.
|
|
128
|
+
const shutdown = yield* Effect.exit(restore(Effect.void));
|
|
129
|
+
if (Exit.isFailure(shutdown)) {
|
|
130
|
+
// Worker shutdown: give the job back without consuming an attempt.
|
|
131
|
+
yield* ackSafely(store.release(record.id, token), "release");
|
|
132
|
+
return yield* Effect.interrupt;
|
|
133
|
+
}
|
|
134
|
+
// A handler that interrupted itself is a failed attempt, not a
|
|
135
|
+
// shutdown — otherwise the job would hot-loop forever.
|
|
136
|
+
const effective = Exit.isFailure(exit) && Cause.hasInterruptsOnly(exit.cause)
|
|
137
|
+
? Exit.die(new Error(`effect-mq: handler for job "${record.name}" interrupted itself`))
|
|
138
|
+
: exit;
|
|
139
|
+
// Never let an encode defect escape: the job would be stuck active.
|
|
140
|
+
let encoded = yield* Effect.exit(entry.encodeExit(effective));
|
|
141
|
+
let encodable = true;
|
|
142
|
+
if (Exit.isFailure(encoded)) {
|
|
143
|
+
encodable = false;
|
|
144
|
+
yield* Effect.logError(`effect-mq: failed to encode handler exit for job "${record.name}"`, encoded.cause);
|
|
145
|
+
encoded = yield* Effect.exit(entry.encodeExit(Exit.die(new Error(`effect-mq: failed to encode handler exit for job "${record.name}"`))));
|
|
146
|
+
}
|
|
147
|
+
const exitValue = Exit.isSuccess(encoded) ? encoded.value : undefined;
|
|
148
|
+
const outcome = Exit.isSuccess(effective)
|
|
149
|
+
? encodable
|
|
150
|
+
// A success whose value can't be encoded must NOT re-run (its
|
|
151
|
+
// side effects already happened) — fail it with the defect.
|
|
152
|
+
? { _tag: "Complete", exit: exitValue }
|
|
153
|
+
: { _tag: "Fail", exit: exitValue }
|
|
154
|
+
: routeFailure(record, exitValue);
|
|
155
|
+
yield* ackSafely(store.ack(record.id, token, outcome), "ack");
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
// One loop iteration. The claim and everything after it run
|
|
159
|
+
// uninterruptibly so shutdown can never orphan a just-claimed job; only
|
|
160
|
+
// the idle waits and the handler run are interruptible.
|
|
161
|
+
const takerIteration = (queue) => Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
|
|
162
|
+
const observedPulse = pulseVersion;
|
|
163
|
+
const names = queueNames.get(queue);
|
|
164
|
+
if (names === undefined || names.size === 0) {
|
|
165
|
+
return yield* restore(Effect.race(awaitPulse(observedPulse), Effect.sleep(pollMs)));
|
|
166
|
+
}
|
|
167
|
+
const token = nextToken();
|
|
168
|
+
const result = yield* retryStore(store.claim({
|
|
169
|
+
queue,
|
|
170
|
+
names: Array.from(names),
|
|
171
|
+
token,
|
|
172
|
+
lockDurationMs
|
|
173
|
+
}));
|
|
174
|
+
if (result._tag === "Empty") {
|
|
175
|
+
const now = yield* Clock.currentTimeMillis;
|
|
176
|
+
const timeout = result.nextRunAt !== undefined
|
|
177
|
+
? Math.max(0, Math.min(result.nextRunAt - now, pollMs))
|
|
178
|
+
: pollMs;
|
|
179
|
+
return yield* restore(Effect.raceAll([
|
|
180
|
+
retryStore(store.awaitWake([queue], result.wakeToken)),
|
|
181
|
+
Effect.sleep(timeout),
|
|
182
|
+
awaitPulse(observedPulse)
|
|
183
|
+
]));
|
|
184
|
+
}
|
|
185
|
+
yield* processJob(result.job, token, restore);
|
|
186
|
+
}));
|
|
187
|
+
const takerLoop = (queue) => takerIteration(queue).pipe(Effect.catchCause((cause) => Effect.logError(`effect-mq: worker iteration failed (queue "${queue}")`, cause)), Effect.forever);
|
|
188
|
+
const queueConcurrency = (queue, registered) => Math.max(1, options?.queues?.[queue]?.concurrency ??
|
|
189
|
+
registered ??
|
|
190
|
+
options?.concurrency ??
|
|
191
|
+
1);
|
|
192
|
+
const ensureQueueLoop = (queue, registered) => Effect.suspend(() => {
|
|
193
|
+
if (startedQueues.has(queue))
|
|
194
|
+
return Effect.void;
|
|
195
|
+
startedQueues.add(queue);
|
|
196
|
+
const takers = queueConcurrency(queue, registered);
|
|
197
|
+
const loop = takerLoop(queue).pipe(Effect.updateContext(() => workerContext));
|
|
198
|
+
return Effect.forEach(Array.from({ length: takers }, (_, i) => i), () => FiberSet.run(fibers, loop), { discard: true });
|
|
199
|
+
});
|
|
200
|
+
const renewalLoop = Effect.gen(function* () {
|
|
201
|
+
yield* Effect.sleep(lockRenewMs);
|
|
202
|
+
const entries = Array.from(inflight.values());
|
|
203
|
+
if (entries.length === 0)
|
|
204
|
+
return;
|
|
205
|
+
const lost = yield* retryStore(store.extendLocks(entries, lockDurationMs));
|
|
206
|
+
if (lost.length > 0) {
|
|
207
|
+
yield* Effect.logWarning("effect-mq: failed to renew locks; jobs may run twice", lost);
|
|
208
|
+
}
|
|
209
|
+
}).pipe(Effect.catchCause((cause) => Effect.logError("effect-mq: lock renewal failed", cause)), Effect.forever);
|
|
210
|
+
const stalledLoop = Effect.gen(function* () {
|
|
211
|
+
yield* Effect.sleep(stalledMs);
|
|
212
|
+
const recovered = yield* retryStore(store.recoverStalled({ maxStalledCount }));
|
|
213
|
+
if (recovered.length > 0) {
|
|
214
|
+
yield* Effect.logWarning("effect-mq: recovered stalled jobs", recovered);
|
|
215
|
+
}
|
|
216
|
+
}).pipe(Effect.catchCause((cause) => Effect.logError("effect-mq: stalled sweep failed", cause)), Effect.forever);
|
|
217
|
+
yield* FiberSet.run(fibers, renewalLoop);
|
|
218
|
+
yield* FiberSet.run(fibers, stalledLoop);
|
|
219
|
+
// SAFETY: the public `register` signature declares Scope, the handler's R
|
|
220
|
+
// and the codec services as requirements; the implementation erases them
|
|
221
|
+
// (the trailing assertion below) because the handler runs with the
|
|
222
|
+
// context captured at registration time via `provideCaptured`.
|
|
223
|
+
return Worker.of({
|
|
224
|
+
register: (job, handler, registerOptions) => Effect.gen(function* () {
|
|
225
|
+
const name = job._tag;
|
|
226
|
+
if (handlers.has(name)) {
|
|
227
|
+
return yield* Effect.die(new Error(`effect-mq: duplicate handler registered for job "${name}"`));
|
|
228
|
+
}
|
|
229
|
+
if (job.store.key !== storeKey.key) {
|
|
230
|
+
return yield* Effect.die(new Error(`effect-mq: job "${name}" is bound to store "${job.store.key}" but this worker claims from "${storeKey.key}". ` +
|
|
231
|
+
`Provide a Worker.layer({ store }) for the job's store (use Layer.provide locally to run several workers in one process).`));
|
|
232
|
+
}
|
|
233
|
+
// Everything the handler and codecs require was provided to the
|
|
234
|
+
// registration layer; capture it, minus runtime-ambient keys that
|
|
235
|
+
// must always come from the executing fiber.
|
|
236
|
+
const services = (yield* Effect.context()).pipe(Context.omit(Scope_.Scope, Tracer.ParentSpan));
|
|
237
|
+
const decodePayload = Schema.decodeUnknownEffect(job.payloadJsonSchema);
|
|
238
|
+
const encodeExit = Schema.encodeEffect(job.exitSchema);
|
|
239
|
+
// SAFETY: per `register`'s public signature the captured context
|
|
240
|
+
// contains the handler's requirements; merging it OVER the runtime
|
|
241
|
+
// context (captured wins on conflicts, so locally provided services
|
|
242
|
+
// are not shadowed by the worker's) restores those requirements.
|
|
243
|
+
const provideCaptured = (effect) => effect.pipe(Effect.updateContext((input) => Context.merge(input, services)));
|
|
244
|
+
const entry = {
|
|
245
|
+
run: (payload, context) => provideCaptured(decodePayload(payload).pipe(Effect.orDie, Effect.flatMap((decoded) => handler(decoded, context)))),
|
|
246
|
+
encodeExit: (exit) => provideCaptured(encodeExit(exit))
|
|
247
|
+
};
|
|
248
|
+
handlers.set(name, entry);
|
|
249
|
+
const queue = registerOptions?.queue !== undefined
|
|
250
|
+
? QueueName(registerOptions.queue)
|
|
251
|
+
: job.queue;
|
|
252
|
+
let names = queueNames.get(queue);
|
|
253
|
+
if (names === undefined) {
|
|
254
|
+
names = new Set();
|
|
255
|
+
queueNames.set(queue, names);
|
|
256
|
+
}
|
|
257
|
+
names.add(name);
|
|
258
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => {
|
|
259
|
+
handlers.delete(name);
|
|
260
|
+
queueNames.get(queue)?.delete(name);
|
|
261
|
+
}));
|
|
262
|
+
yield* ensureQueueLoop(queue, registerOptions?.concurrency);
|
|
263
|
+
yield* firePulse;
|
|
264
|
+
})
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
/**
|
|
268
|
+
* Run a worker as a layer. Provide handler layers (`Job.toLayer(...)`) on top
|
|
269
|
+
* of this, and a `JobStore` below it.
|
|
270
|
+
*
|
|
271
|
+
* @since 0.1.0
|
|
272
|
+
*/
|
|
273
|
+
export const layer = (options) => Layer.effect(Worker, make(options));
|
|
274
|
+
//# sourceMappingURL=Worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Worker.js","sourceRoot":"","sources":["../src/Worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAc,KAAK,IAAI,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACxJ,OAAO,EAIL,eAAe,EAGf,QAAQ,EAGR,SAAS,EAEV,MAAM,eAAe,CAAA;AAuFtB;;GAEG;AACH,MAAM,OAAO,MAAO,SAAQ,OAAO,CAAC,OAAO,EAsBvC,CAAC,kBAAkB,CAAC;CAAG;AAY3B;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,OAAkC,EAClC,OAAe,EACP,EAAE;IACV,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,CAAC,CAAA;IACnC,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;QACrB,KAAK,OAAO;YACV,OAAO,OAAO,CAAC,OAAO,CAAA;QACxB,KAAK,aAAa;YAChB,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAA;IAC/E,CAAC;AACH,CAAC,CAAA;AAED,2EAA2E;AAC3E,6EAA6E;AAC7E,MAAM,gBAAgB,GAAG,QAAQ,CAAC,GAAG,CAAC;IACpC,QAAQ,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC;IAC9B,QAAQ,CAAC,MAAM,CAAC,YAAY,CAAC;CAC9B,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;AAIpC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,CAClB,OAA4C,EACoB,EAAE,CAClE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,yEAAyE;IACzE,mEAAmE;IACnE,6BAA6B;IAC7B,MAAM,aAAa,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,EAAS,CAAA;IACpD,qEAAqE;IACrE,uEAAuE;IACvE,sEAAsE;IACtE,MAAM,QAAQ,GAAG,CAAC,OAAO,EAAE,KAAK,IAAI,QAAQ,CAAuC,CAAA;IACnF,MAAM,KAAK,GAAiB,KAAK,CAAC,CAAC,QAAQ,CAAA;IAC3C,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAA;IAErC,MAAM,cAAc,GAAG,QAAQ,CAAC,QAAQ,CAAC,OAAO,EAAE,YAAY,IAAI,MAAM,CAAC,CAAA;IACzE,MAAM,WAAW,GAAG,OAAO,EAAE,iBAAiB,KAAK,SAAS;QAC1D,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,iBAAiB,CAAC;QAC9C,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC,CAAA;IAC/C,MAAM,SAAS,GAAG,QAAQ,CAAC,QAAQ,CAAC,OAAO,EAAE,eAAe,IAAI,MAAM,CAAC,CAAA;IACvE,MAAM,eAAe,GAAG,OAAO,EAAE,eAAe,IAAI,CAAC,CAAA;IACrD,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,OAAO,EAAE,YAAY,IAAI,KAAK,CAAC,CAAA;IAChE,MAAM,QAAQ,GAAG,OAAO,EAAE,EAAE,IAAI,UAAU,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAA;IAEnF,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAwB,CAAA;IAChD,MAAM,UAAU,GAAG,IAAI,GAAG,EAA0B,CAAA;IACpD,MAAM,aAAa,GAAG,IAAI,GAAG,EAAa,CAAA;IAC1C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAyD,CAAA;IAEjF,IAAI,YAAY,GAAG,CAAC,CAAA;IACpB,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,GAAG,QAAQ,IAAI,EAAE,YAAY,EAAE,CAAA;IAEvD,qEAAqE;IACrE,yEAAyE;IACzE,IAAI,YAAY,GAAG,CAAC,CAAA;IACpB,IAAI,KAAK,GAAG,QAAQ,CAAC,UAAU,EAAQ,CAAA;IACvC,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE;QACpC,YAAY,IAAI,CAAC,CAAA;QACjB,MAAM,OAAO,GAAG,KAAK,CAAA;QACrB,KAAK,GAAG,QAAQ,CAAC,UAAU,EAAQ,CAAA;QACnC,OAAO,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAA;IAC1C,CAAC,CAAC,CAAA;IACF,MAAM,UAAU,GAAG,CAAC,QAAgB,EAAE,EAAE,CACtC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAClB,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAC9D,CAAA;IAEH,MAAM,UAAU,GAAG,CAAU,MAA8B,EAAE,EAAE,CAC7D,MAAM,CAAC,IAAI,CACT,MAAM,CAAC,KAAK,CAAC;QACX,iEAAiE;QACjE,wCAAwC;QACxC,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC;QACxC,QAAQ,EAAE,gBAAgB;KAC3B,CAAC,EACF,MAAM,CAAC,KAAK,CACb,CAAA;IAEH,yEAAyE;IACzE,wEAAwE;IACxE,MAAM,SAAS,GAAG,CAChB,MAGC,EACD,IAAY,EACZ,EAAE,CACF,MAAM,CAAC,IAAI,CACT,MAAM,CAAC,KAAK,CAAC;QACX,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC;QACxC,QAAQ,EAAE,gBAAgB;KAC3B,CAAC,EACF,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CACrB,MAAM,CAAC,UAAU,CAAC,cAAc,IAAI,aAAa,KAAK,CAAC,IAAI,GAAG,EAAE,KAAK,CAAC,CACvE,CACF,CAAA;IAEH,MAAM,YAAY,GAAG,CAAC,MAAiB,EAAE,IAAuB,EAAc,EAAE;QAC9E,MAAM,OAAO,GAAG,MAAM,CAAC,YAAY,GAAG,CAAC,CAAA;QACvC,IAAI,OAAO,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;YAClC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;QAC/B,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,CAAA;IAClF,CAAC,CAAA;IAED,uEAAuE;IACvE,+CAA+C;IAC/C,MAAM,UAAU,GAAG,CAAC,MAAiB,EAAE,KAAa,EAAE,OAAgB,EAAE,EAAE,CACxE,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE;QAClB,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QACvC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,iEAAiE;YACjE,OAAO,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,SAAS,CAAC,CAAA;QAC9D,CAAC;QACD,MAAM,OAAO,GAAe;YAC1B,KAAK,EAAE,MAAM,CAAC,EAAE;YAChB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,OAAO,EAAE,MAAM,CAAC,YAAY,GAAG,CAAC;YAChC,WAAW,EAAE,MAAM,CAAC,WAAW;SAChC,CAAA;QACD,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;QACjD,OAAO,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACzB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;YAC5E,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;YAE1B,8DAA8D;YAC9D,6DAA6D;YAC7D,0CAA0C;YAC1C,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;YACzD,IAAI,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC7B,mEAAmE;gBACnE,KAAK,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,SAAS,CAAC,CAAA;gBAC5D,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,SAAS,CAAA;YAChC,CAAC;YAED,+DAA+D;YAC/D,uDAAuD;YACvD,MAAM,SAAS,GACb,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC;gBACzD,CAAC,CAAC,IAAI,CAAC,GAAG,CACR,IAAI,KAAK,CAAC,+BAA+B,MAAM,CAAC,IAAI,sBAAsB,CAAC,CAC5E;gBACD,CAAC,CAAC,IAAI,CAAA;YAEV,oEAAoE;YACpE,IAAI,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAA;YAC7D,IAAI,SAAS,GAAG,IAAI,CAAA;YACpB,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC5B,SAAS,GAAG,KAAK,CAAA;gBACjB,KAAK,CAAC,CAAC,MAAM,CAAC,QAAQ,CACpB,qDAAqD,MAAM,CAAC,IAAI,GAAG,EACnE,OAAO,CAAC,KAAK,CACd,CAAA;gBACD,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CACpD,IAAI,KAAK,CAAC,qDAAqD,MAAM,CAAC,IAAI,GAAG,CAAC,CAC/E,CAAC,CAAC,CAAA;YACL,CAAC;YACD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;YAErE,MAAM,OAAO,GAAe,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;gBACnD,CAAC,CAAC,SAAS;oBACT,8DAA8D;oBAC9D,4DAA4D;oBAC5D,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE;oBACvC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE;gBACrC,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;YACnC,KAAK,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,CAAA;QAC/D,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEJ,4DAA4D;IAC5D,wEAAwE;IACxE,wDAAwD;IACxD,MAAM,cAAc,GAAG,CAAC,KAAgB,EAAE,EAAE,CAC1C,MAAM,CAAC,mBAAmB,CAAC,CAAC,OAAO,EAAE,EAAE,CACrC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAClB,MAAM,aAAa,GAAG,YAAY,CAAA;QAClC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACnC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YAC5C,OAAO,KAAK,CAAC,CAAC,OAAO,CACnB,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAC7D,CAAA;QACH,CAAC;QACD,MAAM,KAAK,GAAG,SAAS,EAAE,CAAA;QACzB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC;YAC3C,KAAK;YACL,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;YACxB,KAAK;YACL,cAAc;SACf,CAAC,CAAC,CAAA;QACH,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC5B,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAA;YAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,KAAK,SAAS;gBAC5C,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,GAAG,GAAG,EAAE,MAAM,CAAC,CAAC;gBACvD,CAAC,CAAC,MAAM,CAAA;YACV,OAAO,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC;gBACnC,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;gBACtD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;gBACrB,UAAU,CAAC,aAAa,CAAC;aAC1B,CAAC,CAAC,CAAA;QACL,CAAC;QACD,KAAK,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,CAAA;IAC/C,CAAC,CAAC,CACH,CAAA;IAEH,MAAM,SAAS,GAAG,CAAC,KAAgB,EAAE,EAAE,CACrC,cAAc,CAAC,KAAK,CAAC,CAAC,IAAI,CACxB,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,EAAE,EAAE,CAC1B,MAAM,CAAC,QAAQ,CAAC,8CAA8C,KAAK,IAAI,EAAE,KAAK,CAAC,CAChF,EACD,MAAM,CAAC,OAAO,CACf,CAAA;IAEH,MAAM,gBAAgB,GAAG,CAAC,KAAgB,EAAE,UAA8B,EAAE,EAAE,CAC5E,IAAI,CAAC,GAAG,CACN,CAAC,EACD,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,WAAW;QACnC,UAAU;QACV,OAAO,EAAE,WAAW;QACpB,CAAC,CACJ,CAAA;IAEH,MAAM,eAAe,GAAG,CAAC,KAAgB,EAAE,UAA8B,EAAE,EAAE,CAC3E,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE;QAClB,IAAI,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,OAAO,MAAM,CAAC,IAAI,CAAA;QAChD,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACxB,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAA;QAClD,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,IAAI,CAChC,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,CAC1C,CAAA;QACD,OAAO,MAAM,CAAC,OAAO,CACnB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAC3C,GAAG,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,EAChC,EAAE,OAAO,EAAE,IAAI,EAAE,CAClB,CAAA;IACH,CAAC,CAAC,CAAA;IAEJ,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACtC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;QAChC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;QAC7C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAM;QAChC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAA;QAC1E,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CACtB,sDAAsD,EACtD,IAAI,CACL,CAAA;QACH,CAAC;IACH,CAAC,CAAC,CAAC,IAAI,CACL,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,gCAAgC,EAAE,KAAK,CAAC,CAAC,EACtF,MAAM,CAAC,OAAO,CACf,CAAA;IAED,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACtC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;QAC9B,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,eAAe,EAAE,CAAC,CAAC,CAAA;QAC9E,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzB,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,mCAAmC,EAAE,SAAS,CAAC,CAAA;QAC1E,CAAC;IACH,CAAC,CAAC,CAAC,IAAI,CACL,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,iCAAiC,EAAE,KAAK,CAAC,CAAC,EACvF,MAAM,CAAC,OAAO,CACf,CAAA;IAED,KAAK,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;IACxC,KAAK,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;IAExC,0EAA0E;IAC1E,yEAAyE;IACzE,mEAAmE;IACnE,+DAA+D;IAC/D,OAAO,MAAM,CAAC,EAAE,CAAC;QACf,QAAQ,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,eAAe,EAAE,EAAE,CAC1C,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClB,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAA;YACrB,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBACvB,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CACtB,IAAI,KAAK,CAAC,oDAAoD,IAAI,GAAG,CAAC,CACvE,CAAA;YACH,CAAC;YACD,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,GAAG,EAAE,CAAC;gBACnC,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CACtB,IAAI,KAAK,CACP,mBAAmB,IAAI,wBAAwB,GAAG,CAAC,KAAK,CAAC,GAAG,kCAAkC,QAAQ,CAAC,GAAG,KAAK;oBAC7G,0HAA0H,CAC7H,CACF,CAAA;YACH,CAAC;YACD,gEAAgE;YAChE,kEAAkE;YAClE,6CAA6C;YAC7C,MAAM,QAAQ,GAAG,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,EAAS,CAAC,CAAC,IAAI,CACpD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,CAC9C,CAAA;YACD,MAAM,aAAa,GAAG,MAAM,CAAC,mBAAmB,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;YACvE,MAAM,UAAU,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;YACtD,iEAAiE;YACjE,mEAAmE;YACnE,oEAAoE;YACpE,iEAAiE;YACjE,MAAM,eAAe,GAAG,CAAO,MAAoC,EAAuB,EAAE,CAC1F,MAAM,CAAC,IAAI,CACT,MAAM,CAAC,aAAa,CAAC,CAAC,KAAK,EAAE,EAAE,CAC7B,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,CAA6B,CAC3D,CACqB,CAAA;YAC1B,MAAM,KAAK,GAAiB;gBAC1B,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,CACxB,eAAe,CACb,aAAa,CAAC,OAAO,CAAC,CAAC,IAAI,CACzB,MAAM,CAAC,KAAK,EACZ,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CACvD,CACF;gBACH,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;aACxD,CAAA;YACD,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;YACzB,MAAM,KAAK,GAAG,eAAe,EAAE,KAAK,KAAK,SAAS;gBAChD,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,KAAK,CAAC;gBAClC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAA;YACb,IAAI,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;YACjC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,KAAK,GAAG,IAAI,GAAG,EAAE,CAAA;gBACjB,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;YAC9B,CAAC;YACD,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YACf,KAAK,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,CAC9B,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;gBACf,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;gBACrB,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAA;YACrC,CAAC,CAAC,CACH,CAAA;YACD,KAAK,CAAC,CAAC,eAAe,CAAC,KAAK,EAAE,eAAe,EAAE,WAAW,CAAC,CAAA;YAC3D,KAAK,CAAC,CAAC,SAAS,CAAA;QAClB,CAAC,CAAsC;KAC1C,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEJ;;;;;GAKG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CACnB,OAA4C,EACP,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Postgres `JobStore` running through drizzle's Effect driver
|
|
3
|
+
* (`drizzle-orm/effect-postgres`, which is built on `@effect/sql-pg` —
|
|
4
|
+
* Node and Bun compatible).
|
|
5
|
+
*
|
|
6
|
+
* - Claims use `FOR UPDATE SKIP LOCKED`; acks are lock-token guarded.
|
|
7
|
+
* - ALL time comes from the Effect `Clock` as bind parameters (never SQL
|
|
8
|
+
* `now()`), so the conformance suite runs against real Postgres under
|
|
9
|
+
* `TestClock`.
|
|
10
|
+
* - Wake-ups use LISTEN/NOTIFY through the shared `PgClient` (with the
|
|
11
|
+
* worker's `pollInterval` as the fallback), so cross-process workers wake
|
|
12
|
+
* promptly.
|
|
13
|
+
*
|
|
14
|
+
* TODO: a standalone non-drizzle Postgres driver on plain `@effect/sql-pg`
|
|
15
|
+
* (same table layout), and an adapter for promise-based drizzle databases.
|
|
16
|
+
*
|
|
17
|
+
* @since 0.1.0
|
|
18
|
+
*/
|
|
19
|
+
import * as JobStore from "../JobStore.ts";
|
|
20
|
+
import type { PgClient } from "@effect/sql-pg";
|
|
21
|
+
import { type Context, Effect, Layer, type Scope } from "effect";
|
|
22
|
+
import type { MqJobAttemptsTable, MqJobsTable } from "./schema.ts";
|
|
23
|
+
/**
|
|
24
|
+
* @since 0.1.0
|
|
25
|
+
*/
|
|
26
|
+
export interface DrizzleJobStoreOptions<StoreId = JobStore.JobStore> {
|
|
27
|
+
/** The jobs table instance (from `mqJobs`). */
|
|
28
|
+
readonly jobs: MqJobsTable;
|
|
29
|
+
/** The run-ledger table instance (from `mqJobAttempts`). */
|
|
30
|
+
readonly attempts: MqJobAttemptsTable;
|
|
31
|
+
/** Bind to a `JobStore.named(...)` key; default: the default `JobStore`. */
|
|
32
|
+
readonly store?: Context.Key<StoreId, JobStore.Service> | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Probe the tables at startup and fail fast when the schema is missing
|
|
35
|
+
* (default true). Migrations are owned by your drizzle-kit pipeline.
|
|
36
|
+
*/
|
|
37
|
+
readonly validate?: boolean | undefined;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Build the store implementation. Requires `PgClient` and a `Scope` (for the
|
|
41
|
+
* LISTEN subscription).
|
|
42
|
+
*
|
|
43
|
+
* @since 0.1.0
|
|
44
|
+
*/
|
|
45
|
+
export declare const make: (options: DrizzleJobStoreOptions<any>) => Effect.Effect<JobStore.Service, JobStore.JobStoreError, PgClient.PgClient | Scope.Scope>;
|
|
46
|
+
/**
|
|
47
|
+
* A Postgres-backed `JobStore` layer over your drizzle tables. Requires
|
|
48
|
+
* `PgClient` (from `@effect/sql-pg`).
|
|
49
|
+
*
|
|
50
|
+
* ```ts
|
|
51
|
+
* const StoreLive = DrizzleJobStore.layer({ jobs, attempts, store: Durable }).pipe(
|
|
52
|
+
* Layer.provide(PgClient.layer({ url: Redacted.make(DATABASE_URL) }))
|
|
53
|
+
* )
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @since 0.1.0
|
|
57
|
+
*/
|
|
58
|
+
export declare const layer: <StoreId = JobStore.JobStore>(options: DrizzleJobStoreOptions<StoreId>) => Layer.Layer<StoreId, JobStore.JobStoreError, PgClient.PgClient>;
|
|
59
|
+
//# sourceMappingURL=DrizzleJobStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DrizzleJobStore.d.ts","sourceRoot":"","sources":["../../src/drizzle/DrizzleJobStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAA;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAI9C,OAAO,EAAS,KAAK,OAAO,EAAY,MAAM,EAAE,KAAK,EAAU,KAAK,KAAK,EAAU,MAAM,QAAQ,CAAA;AACjG,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAIlE;;GAEG;AACH,MAAM,WAAW,sBAAsB,CAAC,OAAO,GAAG,QAAQ,CAAC,QAAQ;IACjE,+CAA+C;IAC/C,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;IAC1B,4DAA4D;IAC5D,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAA;IACrC,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,SAAS,CAAA;IACnE;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CACxC;AA8DD;;;;;GAKG;AACH,eAAO,MAAM,IAAI,YACN,sBAAsB,CAAC,GAAG,CAAC,KACnC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,QAAQ,GAAG,KAAK,CAAC,KAAK,CA8ctF,CAAA;AAEJ;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,KAAK,GAAI,OAAO,GAAG,QAAQ,CAAC,QAAQ,WACtC,sBAAsB,CAAC,OAAO,CAAC,KACvC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,QAAQ,CAO9D,CAAA"}
|