@bitclaw/jobs 1.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/dist/queue.js ADDED
@@ -0,0 +1,522 @@
1
+ // packages/jobs/src/queue.ts
2
+ // JobQueue — SQLite-backed job queue with typed payloads and dependency support
3
+ import { Database } from 'bun:sqlite';
4
+ import { mkdirSync } from 'node:fs';
5
+ import { dirname } from 'node:path';
6
+ import { applyPragmas, initializeSchema } from './schema';
7
+ import { nowISO } from './utils';
8
+ import { JobWorker } from './worker';
9
+ function toJob(row) {
10
+ return {
11
+ id: row.id,
12
+ type: row.type,
13
+ data: JSON.parse(row.data),
14
+ status: row.status,
15
+ priority: row.priority,
16
+ progress: row.progress,
17
+ maxRetries: row.max_retries,
18
+ retryCount: row.retry_count,
19
+ runAt: row.run_at,
20
+ createdAt: row.created_at,
21
+ updatedAt: row.updated_at,
22
+ startedAt: row.started_at,
23
+ completedAt: row.completed_at,
24
+ error: row.error,
25
+ batchId: row.batch_id,
26
+ requestLog: row.request_log,
27
+ responseLog: row.response_log
28
+ };
29
+ }
30
+ function toBatch(row) {
31
+ return {
32
+ id: row.id,
33
+ name: row.name,
34
+ totalJobs: row.total_jobs,
35
+ pendingJobs: row.pending_jobs,
36
+ failedJobs: row.failed_jobs,
37
+ failedJobIds: JSON.parse(row.failed_job_ids),
38
+ options: row.options ? JSON.parse(row.options) : null,
39
+ cancelledAt: row.cancelled_at,
40
+ createdAt: row.created_at,
41
+ finishedAt: row.finished_at
42
+ };
43
+ }
44
+ function toFailedJob(row) {
45
+ return {
46
+ id: row.id,
47
+ originalJobId: row.original_job_id,
48
+ type: row.type,
49
+ data: JSON.parse(row.data),
50
+ error: row.error,
51
+ retryCount: row.retry_count,
52
+ maxRetries: row.max_retries,
53
+ createdAt: row.created_at,
54
+ failedAt: row.failed_at,
55
+ requestLog: row.request_log,
56
+ responseLog: row.response_log
57
+ };
58
+ }
59
+ export class JobQueue {
60
+ db;
61
+ insertJobStmt;
62
+ insertDepStmt;
63
+ selectJobStmt;
64
+ selectPendingStmt;
65
+ markProcessingStmt;
66
+ markDoneStmt;
67
+ markFailedStmt;
68
+ updateProgressStmt;
69
+ selectStatsStmt;
70
+ countFailedStmt;
71
+ insertFailedJobStmt;
72
+ deleteJobStmt;
73
+ selectDependentsStmt;
74
+ countUnmetDepsStmt;
75
+ unblockJobStmt;
76
+ lastInsertRowIdStmt;
77
+ // Batch statements
78
+ insertBatchStmt;
79
+ selectBatchStmt;
80
+ decrementBatchPendingStmt;
81
+ incrementBatchFailedStmt;
82
+ finishBatchStmt;
83
+ cancelBatchStmt;
84
+ cancelBatchJobsStmt;
85
+ constructor(dbPath) {
86
+ mkdirSync(dirname(dbPath), { recursive: true });
87
+ this.db = new Database(dbPath, { create: true });
88
+ applyPragmas(this.db);
89
+ initializeSchema(this.db);
90
+ this.insertJobStmt = this.db.query(`
91
+ INSERT INTO jobs (type, data, status, priority, max_retries, run_at, batch_id)
92
+ VALUES ($type, $data, $status, $priority, $maxRetries, $runAt, $batchId)
93
+ `);
94
+ this.insertDepStmt = this.db.query(`
95
+ INSERT INTO job_dependencies (job_id, depends_on_id) VALUES ($jobId, $depsOnId)
96
+ `);
97
+ this.selectJobStmt = this.db.query('SELECT * FROM jobs WHERE id = $id');
98
+ this.selectPendingStmt = this.db.query(`
99
+ SELECT * FROM jobs
100
+ WHERE status = 'pending' AND type = $type AND run_at <= $now
101
+ ORDER BY priority DESC, created_at ASC
102
+ LIMIT 1
103
+ `);
104
+ this.markProcessingStmt = this.db.query(`
105
+ UPDATE jobs SET status = 'processing', started_at = $now, updated_at = $now
106
+ WHERE id = $id
107
+ `);
108
+ this.markDoneStmt = this.db.query(`
109
+ UPDATE jobs SET status = 'done', completed_at = $now, updated_at = $now, progress = 100
110
+ WHERE id = $id
111
+ `);
112
+ this.markFailedStmt = this.db.query(`
113
+ UPDATE jobs
114
+ SET status = 'pending',
115
+ retry_count = retry_count + 1,
116
+ error = $error,
117
+ updated_at = $now
118
+ WHERE id = $id
119
+ `);
120
+ this.updateProgressStmt = this.db.query(`
121
+ UPDATE jobs SET progress = $progress, updated_at = $now WHERE id = $id
122
+ `);
123
+ this.selectStatsStmt = this.db.query('SELECT status, COUNT(*) as count FROM jobs GROUP BY status');
124
+ this.countFailedStmt = this.db.query('SELECT COUNT(*) as count FROM failed_jobs');
125
+ this.insertFailedJobStmt = this.db.query(`
126
+ INSERT INTO failed_jobs (original_job_id, type, data, error, retry_count, max_retries, created_at, request_log, response_log)
127
+ VALUES ($originalJobId, $type, $data, $error, $retryCount, $maxRetries, $createdAt, $requestLog, $responseLog)
128
+ `);
129
+ this.deleteJobStmt = this.db.query('DELETE FROM jobs WHERE id = $id');
130
+ this.selectDependentsStmt = this.db.query(`
131
+ SELECT job_id FROM job_dependencies WHERE depends_on_id = $depsOnId
132
+ `);
133
+ this.countUnmetDepsStmt = this.db.query(`
134
+ SELECT COUNT(*) as count FROM job_dependencies jd
135
+ JOIN jobs j ON jd.depends_on_id = j.id
136
+ WHERE jd.job_id = $jobId AND j.status != 'done'
137
+ `);
138
+ this.unblockJobStmt = this.db.query(`
139
+ UPDATE jobs SET status = 'pending', updated_at = $now
140
+ WHERE id = $id AND status = 'blocked'
141
+ `);
142
+ this.lastInsertRowIdStmt = this.db.query('SELECT last_insert_rowid() as id');
143
+ // Batch statements
144
+ this.insertBatchStmt = this.db.query(`
145
+ INSERT INTO job_batches (id, name, options, created_at)
146
+ VALUES ($id, $name, $options, $createdAt)
147
+ `);
148
+ this.selectBatchStmt = this.db.query('SELECT * FROM job_batches WHERE id = $id');
149
+ this.decrementBatchPendingStmt = this.db.query(`
150
+ UPDATE job_batches SET pending_jobs = pending_jobs - 1
151
+ WHERE id = $id
152
+ `);
153
+ this.incrementBatchFailedStmt = this.db.query(`
154
+ UPDATE job_batches
155
+ SET failed_jobs = failed_jobs + 1,
156
+ failed_job_ids = json_insert(failed_job_ids, '$[#]', $jobId)
157
+ WHERE id = $id
158
+ `);
159
+ this.finishBatchStmt = this.db.query(`
160
+ UPDATE job_batches SET finished_at = $now WHERE id = $id
161
+ `);
162
+ this.cancelBatchStmt = this.db.query(`
163
+ UPDATE job_batches SET cancelled_at = $now WHERE id = $id
164
+ `);
165
+ this.cancelBatchJobsStmt = this.db.query(`
166
+ UPDATE jobs SET status = 'cancelled', updated_at = $now
167
+ WHERE batch_id = $batchId AND status IN ('pending', 'blocked')
168
+ `);
169
+ }
170
+ add(type, data, options) {
171
+ return this.insertJob(type, data, null, options);
172
+ }
173
+ getJob(id) {
174
+ const row = this.selectJobStmt.get({ $id: id });
175
+ return row ? toJob(row) : null;
176
+ }
177
+ getStats() {
178
+ const rows = this.selectStatsStmt.all();
179
+ const deadRow = this.countFailedStmt.get();
180
+ const stats = {
181
+ pending: 0,
182
+ blocked: 0,
183
+ processing: 0,
184
+ done: 0,
185
+ failed: 0,
186
+ cancelled: 0,
187
+ dead: deadRow.count
188
+ };
189
+ for (const row of rows) {
190
+ const key = row.status;
191
+ if (key in stats) {
192
+ stats[key] = row.count;
193
+ }
194
+ }
195
+ return stats;
196
+ }
197
+ getFailedJobs(options) {
198
+ const limit = options?.limit ?? 100;
199
+ const offset = options?.offset ?? 0;
200
+ let rows;
201
+ let total;
202
+ if (options?.type) {
203
+ rows = this.db
204
+ .query('SELECT * FROM failed_jobs WHERE type = $type ORDER BY failed_at DESC LIMIT $limit OFFSET $offset')
205
+ .all({
206
+ $type: options.type,
207
+ $limit: limit,
208
+ $offset: offset
209
+ });
210
+ total = this.db
211
+ .query('SELECT COUNT(*) as count FROM failed_jobs WHERE type = $type')
212
+ .get({ $type: options.type }).count;
213
+ }
214
+ else {
215
+ rows = this.db
216
+ .query('SELECT * FROM failed_jobs ORDER BY failed_at DESC LIMIT $limit OFFSET $offset')
217
+ .all({ $limit: limit, $offset: offset });
218
+ total = this.countFailedStmt.get().count;
219
+ }
220
+ return { items: rows.map(toFailedJob), total };
221
+ }
222
+ listJobs(options) {
223
+ const limit = options?.limit ?? 50;
224
+ const offset = options?.offset ?? 0;
225
+ const conditions = [];
226
+ const filterParams = {};
227
+ if (options?.status) {
228
+ conditions.push('status = $status');
229
+ filterParams.$status = options.status;
230
+ }
231
+ if (options?.type) {
232
+ conditions.push('type = $type');
233
+ filterParams.$type = options.type;
234
+ }
235
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
236
+ const rows = this.db
237
+ .query(`SELECT * FROM jobs ${where} ORDER BY created_at DESC LIMIT $limit OFFSET $offset`)
238
+ .all({ ...filterParams, $limit: limit, $offset: offset });
239
+ const total = this.db
240
+ .query(`SELECT COUNT(*) as count FROM jobs ${where}`)
241
+ .get(filterParams).count;
242
+ return { items: rows.map(toJob), total };
243
+ }
244
+ cancelJob(id) {
245
+ const result = this.db
246
+ .query("UPDATE jobs SET status = 'cancelled', updated_at = $now WHERE id = $id AND status IN ('pending', 'blocked')")
247
+ .run({ $id: id, $now: nowISO() });
248
+ return result.changes > 0;
249
+ }
250
+ forceRetryJob(id) {
251
+ const result = this.db
252
+ .query("UPDATE jobs SET status = 'pending', retry_count = 0, error = NULL, run_at = $now, started_at = NULL, updated_at = $now WHERE id = $id AND status IN ('processing', 'cancelled')")
253
+ .run({ $id: id, $now: nowISO() });
254
+ return result.changes > 0;
255
+ }
256
+ setJobHttpLog(id, requestLog, responseLog) {
257
+ this.db
258
+ .query('UPDATE jobs SET request_log = $req, response_log = $res, updated_at = $now WHERE id = $id')
259
+ .run({ $id: id, $req: requestLog, $res: responseLog, $now: nowISO() });
260
+ }
261
+ getJobTypes() {
262
+ const rows = this.db
263
+ .query('SELECT DISTINCT type FROM jobs ORDER BY type')
264
+ .all();
265
+ return rows.map(r => r.type);
266
+ }
267
+ retryFailedJob(failedJobId) {
268
+ const row = this.db
269
+ .query('SELECT * FROM failed_jobs WHERE id = $id')
270
+ .get({ $id: failedJobId });
271
+ if (!row) {
272
+ throw new Error(`Failed job ${failedJobId} not found`);
273
+ }
274
+ const now = nowISO();
275
+ this.insertJobStmt.run({
276
+ $type: row.type,
277
+ $data: row.data,
278
+ $status: 'pending',
279
+ $priority: 0,
280
+ $maxRetries: row.max_retries,
281
+ $runAt: now,
282
+ $batchId: null
283
+ });
284
+ const newJobId = this.lastInsertRowIdStmt.get();
285
+ this.db
286
+ .query('DELETE FROM failed_jobs WHERE id = $id')
287
+ .run({ $id: failedJobId });
288
+ return newJobId.id;
289
+ }
290
+ purgeFailedJobs(olderThanMs) {
291
+ const cutoff = new Date(Date.now() - olderThanMs).toISOString();
292
+ const result = this.db
293
+ .query('DELETE FROM failed_jobs WHERE failed_at < $cutoff')
294
+ .run({ $cutoff: cutoff });
295
+ return result.changes;
296
+ }
297
+ purge(options) {
298
+ const cutoff = new Date(Date.now() - options.olderThanMs).toISOString();
299
+ const result = this.db
300
+ .query('DELETE FROM jobs WHERE status = $status AND updated_at < $cutoff')
301
+ .run({ $status: options.status, $cutoff: cutoff });
302
+ return result.changes;
303
+ }
304
+ pollAndClaim(type) {
305
+ const now = nowISO();
306
+ const claimTx = this.db.transaction(() => {
307
+ const row = this.selectPendingStmt.get({
308
+ $type: type,
309
+ $now: now
310
+ });
311
+ if (!row)
312
+ return null;
313
+ this.markProcessingStmt.run({ $id: row.id, $now: now });
314
+ return row;
315
+ });
316
+ const row = claimTx.immediate();
317
+ return row ? toJob(row) : null;
318
+ }
319
+ markJobDone(id) {
320
+ this.db.transaction(() => {
321
+ const now = nowISO();
322
+ const row = this.selectJobStmt.get({ $id: id });
323
+ this.markDoneStmt.run({ $id: id, $now: now });
324
+ this.unblockDependents(id);
325
+ if (row?.batch_id) {
326
+ this.handleBatchJobComplete(row.batch_id);
327
+ }
328
+ })();
329
+ }
330
+ markJobDead(id, error) {
331
+ this.db.transaction(() => {
332
+ const row = this.selectJobStmt.get({ $id: id });
333
+ if (!row)
334
+ return;
335
+ this.insertFailedJobStmt.run({
336
+ $originalJobId: row.id,
337
+ $type: row.type,
338
+ $data: row.data,
339
+ $error: error,
340
+ $retryCount: row.retry_count,
341
+ $maxRetries: row.max_retries,
342
+ $createdAt: row.created_at,
343
+ $requestLog: row.request_log,
344
+ $responseLog: row.response_log
345
+ });
346
+ this.deleteJobStmt.run({ $id: id });
347
+ if (row.batch_id) {
348
+ this.incrementBatchFailedStmt.run({
349
+ $id: row.batch_id,
350
+ $jobId: row.id
351
+ });
352
+ this.handleBatchJobComplete(row.batch_id);
353
+ }
354
+ })();
355
+ }
356
+ markJobFailed(id, error) {
357
+ this.db.transaction(() => {
358
+ const now = nowISO();
359
+ const row = this.selectJobStmt.get({ $id: id });
360
+ if (!row)
361
+ return;
362
+ if (row.retry_count + 1 >= row.max_retries) {
363
+ this.insertFailedJobStmt.run({
364
+ $originalJobId: row.id,
365
+ $type: row.type,
366
+ $data: row.data,
367
+ $error: error,
368
+ $retryCount: row.retry_count + 1,
369
+ $maxRetries: row.max_retries,
370
+ $createdAt: row.created_at,
371
+ $requestLog: row.request_log,
372
+ $responseLog: row.response_log
373
+ });
374
+ this.deleteJobStmt.run({ $id: id });
375
+ // Job is permanently dead — decrement batch counter and track failure
376
+ if (row.batch_id) {
377
+ this.incrementBatchFailedStmt.run({
378
+ $id: row.batch_id,
379
+ $jobId: row.id
380
+ });
381
+ this.handleBatchJobComplete(row.batch_id);
382
+ }
383
+ }
384
+ else {
385
+ this.markFailedStmt.run({ $id: id, $error: error, $now: now });
386
+ }
387
+ })();
388
+ }
389
+ updateProgress(id, progress) {
390
+ this.updateProgressStmt.run({
391
+ $id: id,
392
+ $progress: progress,
393
+ $now: nowISO()
394
+ });
395
+ }
396
+ // --- Batch API ---
397
+ createBatch(name, options) {
398
+ const id = crypto.randomUUID();
399
+ this.insertBatchStmt.run({
400
+ $id: id,
401
+ $name: name,
402
+ $options: options ? JSON.stringify(options) : null,
403
+ $createdAt: nowISO()
404
+ });
405
+ return id;
406
+ }
407
+ addToBatch(batchId, type, data, options) {
408
+ const jobId = this.insertJob(type, data, batchId, options);
409
+ this.db
410
+ .query('UPDATE job_batches SET total_jobs = total_jobs + 1, pending_jobs = pending_jobs + 1 WHERE id = $id')
411
+ .run({ $id: batchId });
412
+ return jobId;
413
+ }
414
+ getBatch(batchId) {
415
+ const row = this.selectBatchStmt.get({
416
+ $id: batchId
417
+ });
418
+ return row ? toBatch(row) : null;
419
+ }
420
+ cancelBatch(batchId) {
421
+ const now = nowISO();
422
+ this.cancelBatchStmt.run({ $id: batchId, $now: now });
423
+ this.cancelBatchJobsStmt.run({ $batchId: batchId, $now: now });
424
+ }
425
+ createWorker(options) {
426
+ return new JobWorker(this, options);
427
+ }
428
+ insertJob(type, data, batchId, options) {
429
+ const now = nowISO();
430
+ const runAt = options?.runAt ? options.runAt.toISOString() : now;
431
+ const hasDeps = options?.dependsOn && options.dependsOn.length > 0;
432
+ const status = hasDeps ? 'blocked' : 'pending';
433
+ this.insertJobStmt.run({
434
+ $type: type,
435
+ $data: JSON.stringify(data),
436
+ $status: status,
437
+ $priority: options?.priority ?? 0,
438
+ $maxRetries: options?.maxRetries ?? 3,
439
+ $runAt: runAt,
440
+ $batchId: batchId
441
+ });
442
+ const jobId = this.lastInsertRowIdStmt.get();
443
+ if (hasDeps) {
444
+ for (const depId of options.dependsOn) {
445
+ const dep = this.selectJobStmt.get({ $id: depId });
446
+ if (!dep) {
447
+ throw new Error(`Dependency job ${depId} does not exist`);
448
+ }
449
+ this.insertDepStmt.run({
450
+ $jobId: jobId.id,
451
+ $depsOnId: depId
452
+ });
453
+ }
454
+ }
455
+ return jobId.id;
456
+ }
457
+ close() {
458
+ try {
459
+ if (this.db.filename !== ':memory:' && this.db.filename !== '') {
460
+ this.db.run('PRAGMA wal_checkpoint(PASSIVE)');
461
+ }
462
+ }
463
+ catch {
464
+ // ignore checkpoint errors on close
465
+ }
466
+ this.db.close();
467
+ }
468
+ unblockDependents(completedJobId) {
469
+ const now = nowISO();
470
+ const dependents = this.selectDependentsStmt.all({
471
+ $depsOnId: completedJobId
472
+ });
473
+ for (const dep of dependents) {
474
+ const unmetCount = this.countUnmetDepsStmt.get({
475
+ $jobId: dep.job_id
476
+ });
477
+ if (unmetCount.count === 0) {
478
+ this.unblockJobStmt.run({ $id: dep.job_id, $now: now });
479
+ }
480
+ }
481
+ }
482
+ handleBatchJobComplete(batchId) {
483
+ this.decrementBatchPendingStmt.run({ $id: batchId });
484
+ const batch = this.selectBatchStmt.get({
485
+ $id: batchId
486
+ });
487
+ if (!batch || batch.pending_jobs > 0)
488
+ return;
489
+ // Batch is complete
490
+ const now = nowISO();
491
+ this.finishBatchStmt.run({ $id: batchId, $now: now });
492
+ const options = batch.options
493
+ ? JSON.parse(batch.options)
494
+ : null;
495
+ if (!options)
496
+ return;
497
+ // Enqueue "then" callback job only if zero failures
498
+ if (batch.failed_jobs === 0 && options.thenType) {
499
+ this.insertJobStmt.run({
500
+ $type: options.thenType,
501
+ $data: JSON.stringify(options.thenData ?? {}),
502
+ $status: 'pending',
503
+ $priority: 0,
504
+ $maxRetries: 3,
505
+ $runAt: now,
506
+ $batchId: null
507
+ });
508
+ }
509
+ // Enqueue "finally" callback job regardless of failures
510
+ if (options.finallyType) {
511
+ this.insertJobStmt.run({
512
+ $type: options.finallyType,
513
+ $data: JSON.stringify(options.finallyData ?? {}),
514
+ $status: 'pending',
515
+ $priority: 0,
516
+ $maxRetries: 3,
517
+ $runAt: now,
518
+ $batchId: null
519
+ });
520
+ }
521
+ }
522
+ }
@@ -0,0 +1,11 @@
1
+ export declare class SlidingWindowRateLimiter {
2
+ private timestamps;
3
+ private maxCount;
4
+ private windowMs;
5
+ constructor(maxCount: number, windowMs: number);
6
+ canProceed(): boolean;
7
+ record(): void;
8
+ reset(): void;
9
+ private prune;
10
+ }
11
+ //# sourceMappingURL=rate-limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"AAGA,qBAAa,wBAAwB;IACnC,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAS;gBAEb,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAK9C,UAAU,IAAI,OAAO;IAKrB,MAAM,IAAI,IAAI;IAId,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,KAAK;CAMd"}
@@ -0,0 +1,27 @@
1
+ // packages/jobs/src/rate-limiter.ts
2
+ // In-memory sliding window rate limiter for per-type job throttling
3
+ export class SlidingWindowRateLimiter {
4
+ timestamps = [];
5
+ maxCount;
6
+ windowMs;
7
+ constructor(maxCount, windowMs) {
8
+ this.maxCount = maxCount;
9
+ this.windowMs = windowMs;
10
+ }
11
+ canProceed() {
12
+ this.prune();
13
+ return this.timestamps.length < this.maxCount;
14
+ }
15
+ record() {
16
+ this.timestamps.push(Date.now());
17
+ }
18
+ reset() {
19
+ this.timestamps = [];
20
+ }
21
+ prune() {
22
+ const cutoff = Date.now() - this.windowMs;
23
+ while (this.timestamps.length > 0 && this.timestamps[0] < cutoff) {
24
+ this.timestamps.shift();
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,26 @@
1
+ import type { JobQueue } from './queue';
2
+ import type { AddScheduleOptions, JobMap, Schedule } from './types';
3
+ export declare class Scheduler<TMap extends JobMap = Record<string, unknown>> {
4
+ private readonly queue;
5
+ private timer;
6
+ private readonly upsertStmt;
7
+ private readonly selectByNameStmt;
8
+ private readonly selectAllStmt;
9
+ private readonly selectDueStmt;
10
+ private readonly updateLastRunStmt;
11
+ private readonly pauseStmt;
12
+ private readonly resumeStmt;
13
+ private readonly removeStmt;
14
+ constructor(queue: JobQueue<TMap>);
15
+ register(name: string, type: string, cron: string, options?: AddScheduleOptions): Schedule;
16
+ cleanup(registeredNames: string[]): number;
17
+ pauseSchedule(name: string): void;
18
+ resumeSchedule(name: string): void;
19
+ getSchedules(): Schedule[];
20
+ getSchedule(name: string): Schedule | null;
21
+ removeSchedule(name: string): boolean;
22
+ tick(): number;
23
+ start(intervalMs?: number): void;
24
+ stop(): void;
25
+ }
26
+ //# sourceMappingURL=scheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,KAAK,EACV,kBAAkB,EAClB,MAAM,EACN,QAAQ,EAET,MAAM,SAAS,CAAC;AAqBjB,qBAAa,SAAS,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAClE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiB;IACvC,OAAO,CAAC,KAAK,CAA+C;IAE5D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;IAC5B,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC;IAClC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC;IAC/B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC;IAC/B,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;IAC3B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;IAC5B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;gBAEhB,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC;IAwCjC,QAAQ,CACN,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,kBAAkB,GAC3B,QAAQ;IAoBX,OAAO,CAAC,eAAe,EAAE,MAAM,EAAE,GAAG,MAAM;IAoB1C,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIjC,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIlC,YAAY,IAAI,QAAQ,EAAE;IAK1B,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IAO1C,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAKrC,IAAI,IAAI,MAAM;IA0Dd,KAAK,CAAC,UAAU,SAAS,GAAG,IAAI;IAKhC,IAAI,IAAI,IAAI;CAMb"}