@cosmicdrift/kumiko-framework 0.100.0 → 0.102.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-framework",
3
- "version": "0.100.0",
3
+ "version": "0.102.0",
4
4
  "description": "Framework core — engine, pipeline, API, DB, and every other bit that makes Kumiko go.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -181,7 +181,7 @@
181
181
  "zod": "^4.4.3"
182
182
  },
183
183
  "devDependencies": {
184
- "@cosmicdrift/kumiko-dispatcher-live": "0.100.0",
184
+ "@cosmicdrift/kumiko-dispatcher-live": "0.102.0",
185
185
  "bun-types": "^1.3.13",
186
186
  "pino-pretty": "^13.1.3"
187
187
  },
@@ -61,6 +61,10 @@ export type JobMeta = {
61
61
  // the run-started event so audit queries can distinguish "fresh run" vs.
62
62
  // "nth retry" without joining back to BullMQ-internals.
63
63
  attempt?: number | undefined;
64
+ // BullMQ job priority (lower = processed first). Set per-dispatch so the same
65
+ // job definition can be enqueued at different urgencies (e.g. delivery maps
66
+ // critical/normal/low onto it).
67
+ priority?: number | undefined;
64
68
  };
65
69
 
66
70
  export type JobRunner = {
@@ -177,6 +181,11 @@ export function createJobRunner(options: JobRunnerOptions): JobRunner {
177
181
  worker: new Queue(queueNameFor(queueNamePrefix, "worker"), { connection: redisOpts }),
178
182
  };
179
183
  let worker: Worker | null = null;
184
+ // Forward reference to the runner's own API, exposed on the job-handler ctx
185
+ // so a handler can dispatch a follow-up job (job→job chaining, e.g.
186
+ // delivery.render → delivery.send). Assigned just before return; reads happen
187
+ // at job-execution time (after start()), so it is always defined by then.
188
+ let selfRunner: JobRunner | undefined;
180
189
 
181
190
  // Counts active + waiting jobs with this name for this tenant across
182
191
  // BOTH lane queues. Jobs with the same name should only live in one
@@ -303,6 +312,9 @@ export function createJobRunner(options: JobRunnerOptions): JobRunner {
303
312
  // workers can reach projections/jobs without the app author duplicating
304
313
  // it into `context` (the JobContext contract guarantees `registry`).
305
314
  registry,
315
+ // Expose the runner so handlers can chain a follow-up job. Symmetric with
316
+ // how the command-dispatcher hands write-handlers their jobRunner.
317
+ ...(selfRunner !== undefined && { jobRunner: selfRunner }),
306
318
  systemUser: createSystemUser(tenantId),
307
319
  triggeredBy: triggeredById !== null ? { id: triggeredById, tenantId } : null,
308
320
  log: createJobLogger(logs),
@@ -379,7 +391,7 @@ export function createJobRunner(options: JobRunnerOptions): JobRunner {
379
391
  }
380
392
  }
381
393
 
382
- return {
394
+ const runnerApi: JobRunner = {
383
395
  async start(): Promise<void> {
384
396
  // skip: enqueuer-only runner — no BullMQ worker, no cron schedules,
385
397
  // no boot jobs. The API-process (runLocalJobs=false) lands here; it
@@ -510,6 +522,7 @@ export function createJobRunner(options: JobRunnerOptions): JobRunner {
510
522
  if (jobDef.retries !== undefined) bullOpts["attempts"] = jobDef.retries + 1;
511
523
  if (jobDef.backoff) bullOpts["backoff"] = { type: jobDef.backoff };
512
524
  if (jobDef.timeout) bullOpts["timeout"] = jobDef.timeout;
525
+ if (meta?.priority !== undefined) bullOpts["priority"] = meta.priority;
513
526
 
514
527
  // Pack meta into job data with _ prefix
515
528
  const data: Record<string, unknown> = { ...payload };
@@ -575,4 +588,7 @@ export function createJobRunner(options: JobRunnerOptions): JobRunner {
575
588
  }
576
589
  },
577
590
  };
591
+
592
+ selfRunner = runnerApi;
593
+ return runnerApi;
578
594
  }