@semiont/jobs 0.5.36 → 0.5.38
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 +186 -4
- package/dist/index.js +513 -11
- package/dist/index.js.map +1 -1
- package/dist/worker-main.js +9 -7
- package/dist/worker-main.js.map +1 -1
- package/package.json +9 -8
package/dist/index.d.ts
CHANGED
|
@@ -290,7 +290,22 @@ interface JobQueue {
|
|
|
290
290
|
destroy(): void;
|
|
291
291
|
createJob(job: AnyJob): Promise<void>;
|
|
292
292
|
getJob(jobId: JobId): Promise<AnyJob | null>;
|
|
293
|
-
|
|
293
|
+
/**
|
|
294
|
+
* Atomically claim the NEXT pending job matching one of `types` — the ONE
|
|
295
|
+
* transition that makes this a queue rather than a state store
|
|
296
|
+
* (JOB-QUEUE-DRIVER P0; reshaped claim-by-TYPE in P2, while every worker
|
|
297
|
+
* is still ours). pending → running, `startedAt` stamped, progress empty.
|
|
298
|
+
* An announcement is a WAKE-UP, not a reservation: the claimed job may
|
|
299
|
+
* differ from any announced one, no ordering among matching pending jobs
|
|
300
|
+
* is promised, and an empty `types` accepts any type. Simultaneous claims
|
|
301
|
+
* admit one winner PER pending job; a claim that finds nothing is
|
|
302
|
+
* DECLINED (`none-available`), never an error.
|
|
303
|
+
*/
|
|
304
|
+
claimNextJob(types: string[]): Promise<{
|
|
305
|
+
job: AnyJob;
|
|
306
|
+
} | {
|
|
307
|
+
declined: 'none-available';
|
|
308
|
+
}>;
|
|
294
309
|
/** Move a running job to `complete`. Returns false if the job isn't running. */
|
|
295
310
|
completeJob(jobId: JobId, result: Record<string, unknown>): Promise<boolean>;
|
|
296
311
|
/**
|
|
@@ -348,6 +363,15 @@ interface JobQueue {
|
|
|
348
363
|
cancelled: number;
|
|
349
364
|
}>;
|
|
350
365
|
}
|
|
366
|
+
/**
|
|
367
|
+
* Every bus channel a queue DRIVER emits, maintained beside the interface
|
|
368
|
+
* both drivers implement and censused against their sources
|
|
369
|
+
* (queue-emits-census.test.ts). The gateway's signal bridge forwards these
|
|
370
|
+
* from its local bus onto the plane — a queue announcement that stays on
|
|
371
|
+
* the raw bus reaches no worker under a remote driver (the job:queued
|
|
372
|
+
* starvation, 2026-09-15).
|
|
373
|
+
*/
|
|
374
|
+
declare const JOB_QUEUE_EMITS: readonly ["job:queued"];
|
|
351
375
|
|
|
352
376
|
/**
|
|
353
377
|
* Job Queue Manager
|
|
@@ -398,10 +422,30 @@ declare class FsJobQueue implements JobQueue {
|
|
|
398
422
|
* Get a job by ID (searches all status directories)
|
|
399
423
|
*/
|
|
400
424
|
getJob(jobId: JobId): Promise<AnyJob | null>;
|
|
425
|
+
/**
|
|
426
|
+
* Serializes claims. This driver's atomicity is single-process by design,
|
|
427
|
+
* but `claimJob`'s contract must hold even against interleaved awaits in
|
|
428
|
+
* that one process — a read-check-write with await points in it can admit
|
|
429
|
+
* two winners without this chain (the TOCTOU the operation exists to kill).
|
|
430
|
+
*/
|
|
431
|
+
private claimChain;
|
|
432
|
+
claimNextJob(types: string[]): Promise<{
|
|
433
|
+
job: AnyJob;
|
|
434
|
+
} | {
|
|
435
|
+
declined: 'none-available';
|
|
436
|
+
}>;
|
|
437
|
+
private doClaimNext;
|
|
401
438
|
/**
|
|
402
439
|
* Update a job (atomic: delete old, write new)
|
|
403
440
|
*/
|
|
404
|
-
|
|
441
|
+
/**
|
|
442
|
+
* Cross-status move (or in-place rewrite): delete old slot, write new.
|
|
443
|
+
* Private — the interface exposes named transitions only (JOB-QUEUE-DRIVER
|
|
444
|
+
* P0); a generic public patch is trivial on files and impossible on an
|
|
445
|
+
* in-flight message, so no driver may offer one. Re-entering `pending`
|
|
446
|
+
* announces, which is how failJob's retry reaches a listening worker.
|
|
447
|
+
*/
|
|
448
|
+
private transition;
|
|
405
449
|
/**
|
|
406
450
|
* Move a running job to `complete`. Returns false (and changes
|
|
407
451
|
* nothing) if the job is missing or not running — which also makes
|
|
@@ -478,6 +522,144 @@ declare class FsJobQueue implements JobQueue {
|
|
|
478
522
|
}>;
|
|
479
523
|
}
|
|
480
524
|
|
|
525
|
+
/**
|
|
526
|
+
* JetStreamJobQueue — the JetStream driver behind the `JobQueue` interface
|
|
527
|
+
* (JOB-QUEUE-DRIVER P1, topology ruling M: gateway-mediated).
|
|
528
|
+
*
|
|
529
|
+
* Two primitives, one authority each:
|
|
530
|
+
*
|
|
531
|
+
* - **KV bucket `jobs`** is the AUTHORITATIVE operational state: one entry
|
|
532
|
+
* per job (`{ job, lastProgressAt }`), every transition a revision-CAS —
|
|
533
|
+
* which is what makes `claimJob` atomic: simultaneous claims race on one
|
|
534
|
+
* revision and exactly one update wins.
|
|
535
|
+
* - **Stream `JOBS`** (subjects `jobs.<category>.<type>`, work-queue
|
|
536
|
+
* retention) is the DELIVERY vehicle and redelivery timer. A delivered
|
|
537
|
+
* message is the lease this process holds for a job; `working()`
|
|
538
|
+
* heartbeats extend it, and a gateway that dies stops heartbeating, so
|
|
539
|
+
* `AckWait` redelivers the job to a live instance — gateway-death
|
|
540
|
+
* recovery, protocol-native.
|
|
541
|
+
*
|
|
542
|
+
* Worker-death recovery is NOT AckWait's job under the mediated topology
|
|
543
|
+
* (the gateway holding the lease is alive; the worker vanished): it is the
|
|
544
|
+
* `lastProgressAt` sweep (`recoverStaleRunningJobs`), the same contract the
|
|
545
|
+
* fs driver's mtime janitor implemented. The redelivery handler checks KV
|
|
546
|
+
* state, so the two recovery paths can never double-apply.
|
|
547
|
+
*
|
|
548
|
+
* Battery ownership (JOB-QUEUE-DRIVER P0): the retry decision belongs to
|
|
549
|
+
* `will-retry.ts` — `max_deliver` is unlimited and a deterministic failure
|
|
550
|
+
* is `term()`ed, so the backend's retry engine never becomes a second
|
|
551
|
+
* authority. The cursor merge belongs to `checkpoint-merge.ts`, shared with
|
|
552
|
+
* every driver.
|
|
553
|
+
*
|
|
554
|
+
* The boundary is the point: no NATS type, subject string, or delivery
|
|
555
|
+
* handle escapes this file.
|
|
556
|
+
*/
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* The JOBS stream's capture filter — exported as the ONE home of the fact.
|
|
560
|
+
* A JetStream stream is a server-side subscription: anything published under
|
|
561
|
+
* these subjects is persisted regardless of which client API produced it.
|
|
562
|
+
* The signal plane's disjointness gate derives from this export instead of
|
|
563
|
+
* restating it (SIGNAL-PLANE D3 gate 3).
|
|
564
|
+
*/
|
|
565
|
+
declare const JOBS_STREAM_SUBJECTS: readonly ["jobs.>"];
|
|
566
|
+
interface JetStreamJobQueueOptions {
|
|
567
|
+
/** NATS server address(es), e.g. "192.168.64.42:4222". */
|
|
568
|
+
servers: string | string[];
|
|
569
|
+
/** Worker presumed dead after this long without progress (default 30 min). */
|
|
570
|
+
staleRunningMs?: number;
|
|
571
|
+
/** Lease redelivery window when THIS process stops heartbeating (default 30 s). */
|
|
572
|
+
ackWaitMs?: number;
|
|
573
|
+
/** Re-announce + worker-death-sweep cadence (default 30 s; tests shrink it). */
|
|
574
|
+
tickMs?: number;
|
|
575
|
+
/** Reconnect on connection loss (default true; tests turn it off). */
|
|
576
|
+
reconnect?: boolean;
|
|
577
|
+
}
|
|
578
|
+
declare class JetStreamJobQueue implements JobQueue {
|
|
579
|
+
private readonly options;
|
|
580
|
+
private readonly logger;
|
|
581
|
+
private readonly eventBus?;
|
|
582
|
+
private nc;
|
|
583
|
+
private js;
|
|
584
|
+
private jsm;
|
|
585
|
+
private kv;
|
|
586
|
+
private iter;
|
|
587
|
+
/** Delivered messages this process holds — the lease, keyed by jobId,
|
|
588
|
+
* with the job's type so a by-type claim can walk them without a read. */
|
|
589
|
+
private readonly held;
|
|
590
|
+
private heartbeat;
|
|
591
|
+
private reconciling;
|
|
592
|
+
private tick;
|
|
593
|
+
private ticking;
|
|
594
|
+
private readonly lastProgressWrite;
|
|
595
|
+
private readonly staleRunningMs;
|
|
596
|
+
private readonly ackWaitMs;
|
|
597
|
+
private readonly tickMs;
|
|
598
|
+
constructor(options: JetStreamJobQueueOptions, logger: Logger, eventBus?: EventBus | undefined);
|
|
599
|
+
initialize(): Promise<void>;
|
|
600
|
+
destroy(): void;
|
|
601
|
+
/**
|
|
602
|
+
* A delivery is a job arriving at this gateway. What it means depends on
|
|
603
|
+
* the job's authoritative (KV) state:
|
|
604
|
+
* - pending → hold the lease, announce for a worker to claim;
|
|
605
|
+
* - running → hold silently (a claim raced ahead of the delivery, or a
|
|
606
|
+
* redelivery reached a fresh instance after a gateway death);
|
|
607
|
+
* - terminal → the work already concluded elsewhere; consume the message;
|
|
608
|
+
* - unknown → not ours to run; terminate it.
|
|
609
|
+
*/
|
|
610
|
+
private onDelivery;
|
|
611
|
+
/** Same wire shape as the fs driver: only jobs with a resourceId announce. */
|
|
612
|
+
private announce;
|
|
613
|
+
private read;
|
|
614
|
+
private write;
|
|
615
|
+
/**
|
|
616
|
+
* Read-transform-CAS with bounded retries: the transform sees the current
|
|
617
|
+
* job and returns the replacement (or null to stop). Every state
|
|
618
|
+
* transition goes through here, which is what makes each one atomic.
|
|
619
|
+
*/
|
|
620
|
+
private cas;
|
|
621
|
+
createJob(job: AnyJob): Promise<void>;
|
|
622
|
+
getJob(jobIdArg: JobId): Promise<AnyJob | null>;
|
|
623
|
+
claimNextJob(types: string[]): Promise<{
|
|
624
|
+
job: AnyJob;
|
|
625
|
+
} | {
|
|
626
|
+
declined: 'none-available';
|
|
627
|
+
}>;
|
|
628
|
+
/** CAS one pending job to running; null when someone else won it. */
|
|
629
|
+
private tryClaim;
|
|
630
|
+
completeJob(jobIdArg: JobId, result: Record<string, unknown>): Promise<boolean>;
|
|
631
|
+
failJob(jobIdArg: JobId, error: string, completedUnits?: string[], failureClass?: 'transient' | 'deterministic', unitCursors?: Record<string, UnitCursor>): Promise<'retried' | 'failed' | null>;
|
|
632
|
+
checkpointUnits(jobIdArg: JobId, completedUnits: string[], unitCursors?: Record<string, UnitCursor>): Promise<void>;
|
|
633
|
+
recordProgress(jobIdArg: JobId, progress: Record<string, unknown>): Promise<void>;
|
|
634
|
+
cancelPendingJobs(category: 'annotation' | 'generation'): Promise<number>;
|
|
635
|
+
cancelJob(jobIdArg: JobId): Promise<boolean>;
|
|
636
|
+
getStats(): Promise<{
|
|
637
|
+
pending: number;
|
|
638
|
+
running: number;
|
|
639
|
+
complete: number;
|
|
640
|
+
failed: number;
|
|
641
|
+
cancelled: number;
|
|
642
|
+
}>;
|
|
643
|
+
/**
|
|
644
|
+
* Worker-death recovery: the same contract the fs driver's mtime janitor
|
|
645
|
+
* implemented — a running job with no progress inside the stale window is
|
|
646
|
+
* retried or failed, checkpoint intact. Gateway-death recovery never comes
|
|
647
|
+
* through here; that is AckWait redelivery.
|
|
648
|
+
*/
|
|
649
|
+
recoverStaleRunningJobs(): Promise<number>;
|
|
650
|
+
/** The heartbeat body — extend live leases, settle concluded ones. */
|
|
651
|
+
private reconcileHeld;
|
|
652
|
+
/**
|
|
653
|
+
* Materialize the KV key list BEFORE touching any entry: interleaving
|
|
654
|
+
* `get`/`update` with an open `keys()` iterator on the same connection
|
|
655
|
+
* makes the iterator drop entries (measured: the second pending job
|
|
656
|
+
* vanished from every sweep until this was split into two passes).
|
|
657
|
+
*/
|
|
658
|
+
private allKeys;
|
|
659
|
+
/** Conclude this process's lease on a job, if it holds one. */
|
|
660
|
+
private settleLease;
|
|
661
|
+
}
|
|
662
|
+
|
|
481
663
|
/**
|
|
482
664
|
* Citation-token resolver (INLINE-CITATIONS P1).
|
|
483
665
|
*
|
|
@@ -865,5 +1047,5 @@ declare function generateResourceFromTopic(topic: string, entityTypes: string[],
|
|
|
865
1047
|
*/
|
|
866
1048
|
declare const STALL_THRESHOLD_MS: number;
|
|
867
1049
|
|
|
868
|
-
export { AnnotationDetection, FsJobQueue, STALL_THRESHOLD_MS, generateResourceFromTopic, isCancelledJob, isCompleteJob, isFailedJob, isPendingJob, isRunningJob, processAssessmentJob, processCommentJob, processGenerationJob, processHighlightJob, processReferenceJob, processTagJob };
|
|
869
|
-
export type { AnyJob, AssessmentDetectionJob, AssessmentDetectionParams, AssessmentDetectionProgress, CancelledJob, CommentDetectionJob, CommentDetectionParams, CommentDetectionProgress, CompleteJob, DetectionJob, DetectionParams, DetectionProgress, FailedJob, GenerationJob, GenerationResult, HighlightDetectionJob, HighlightDetectionParams, HighlightDetectionProgress, JobMetadata, JobQueryFilters, JobQueue, JobStatus, JobType, OnProgress, PendingJob, ProcessorResult, RunningJob, TagDetectionJob, TagDetectionParams, TagDetectionProgress, YieldProgress };
|
|
1050
|
+
export { AnnotationDetection, FsJobQueue, JOBS_STREAM_SUBJECTS, JOB_QUEUE_EMITS, JetStreamJobQueue, STALL_THRESHOLD_MS, generateResourceFromTopic, isCancelledJob, isCompleteJob, isFailedJob, isPendingJob, isRunningJob, processAssessmentJob, processCommentJob, processGenerationJob, processHighlightJob, processReferenceJob, processTagJob };
|
|
1051
|
+
export type { AnyJob, AssessmentDetectionJob, AssessmentDetectionParams, AssessmentDetectionProgress, CancelledJob, CommentDetectionJob, CommentDetectionParams, CommentDetectionProgress, CompleteJob, DetectionJob, DetectionParams, DetectionProgress, FailedJob, GenerationJob, GenerationResult, HighlightDetectionJob, HighlightDetectionParams, HighlightDetectionProgress, JetStreamJobQueueOptions, JobMetadata, JobQueryFilters, JobQueue, JobStatus, JobType, OnProgress, PendingJob, ProcessorResult, RunningJob, TagDetectionJob, TagDetectionParams, TagDetectionProgress, YieldProgress };
|