@arkstack/jobs 0.18.3 → 0.18.4

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.
@@ -1,17 +1,24 @@
1
1
  import { Queue } from "@arkstack/queue";
2
2
  import { randomUUID } from "node:crypto";
3
3
  //#region src/JobRegistry.ts
4
+ const REGISTRY = Symbol.for("arkstack.jobs.registry");
5
+ const registry = () => {
6
+ const store = globalThis;
7
+ return store[REGISTRY] ??= /* @__PURE__ */ new Map();
8
+ };
4
9
  /**
5
10
  * A registry mapping job names to their classes so a worker can reconstruct job
6
11
  * instances from a stored payload.
7
12
  *
8
13
  * Job classes register themselves when instantiated, which covers same-process
9
- * dispatch + work. For dedicated worker processes, ensure the job modules are
10
- * imported (or call {@link JobRegistry.register} explicitly) so the names are
11
- * known before jobs are processed.
14
+ * dispatch + work. A dedicated worker process constructs none of them, so
15
+ * `queue:work` calls {@link loadJobs} to import the application's job modules
16
+ * before it starts working.
12
17
  */
13
18
  var JobRegistry = class {
14
- static classes = /* @__PURE__ */ new Map();
19
+ static get classes() {
20
+ return registry();
21
+ }
15
22
  /**
16
23
  * Register a job class under an explicit name (defaults to the class name).
17
24
  */
@@ -1,7 +1,7 @@
1
- import { mkdir, writeFile } from "node:fs/promises";
1
+ import { dirname, resolve } from "node:path";
2
2
  import { Arkstack } from "@arkstack/contract";
3
+ import { mkdir, writeFile } from "node:fs/promises";
3
4
  import { Command } from "@h3ravel/musket";
4
- import { dirname, resolve } from "node:path";
5
5
  //#region src/commands/MakeJobCommand.ts
6
6
  /**
7
7
  * Generate a new dispatchable job class.
package/dist/index.d.ts CHANGED
@@ -106,12 +106,12 @@ interface DispatchOptions {
106
106
  * instances from a stored payload.
107
107
  *
108
108
  * Job classes register themselves when instantiated, which covers same-process
109
- * dispatch + work. For dedicated worker processes, ensure the job modules are
110
- * imported (or call {@link JobRegistry.register} explicitly) so the names are
111
- * known before jobs are processed.
109
+ * dispatch + work. A dedicated worker process constructs none of them, so
110
+ * `queue:work` calls {@link loadJobs} to import the application's job modules
111
+ * before it starts working.
112
112
  */
113
113
  declare class JobRegistry {
114
- private static classes;
114
+ private static get classes();
115
115
  /**
116
116
  * Register a job class under an explicit name (defaults to the class name).
117
117
  */
@@ -153,6 +153,23 @@ declare class JobRegistry {
153
153
  */
154
154
  declare const dispatch: <T extends Job>(job: T, options?: DispatchOptions) => PendingDispatch<T>;
155
155
  //#endregion
156
+ //#region src/loader.d.ts
157
+ /**
158
+ * Import the application's job classes so a worker can reconstruct them.
159
+ *
160
+ * A dedicated `queue:work` process never constructs the app's jobs itself, and
161
+ * a job class only reaches the {@link JobRegistry} when its module is loaded —
162
+ * without this a worker cannot resolve a single payload it pops, and every job
163
+ * is released back onto the queue until it exhausts its attempts.
164
+ *
165
+ * Every concrete `Job` subclass a module exports is registered under its class
166
+ * name, which is the name payloads are serialized with.
167
+ *
168
+ * @param subPath Directory (under `src/`, or the build output) to load from.
169
+ * @returns The names registered, in discovery order.
170
+ */
171
+ declare const loadJobs: (subPath?: string) => Promise<string[]>;
172
+ //#endregion
156
173
  //#region src/bridge.d.ts
157
174
  /**
158
175
  * Wire `@arkstack/jobs` into `@arkstack/queue` by registering how jobs are
@@ -163,4 +180,4 @@ declare const dispatch: <T extends Job>(job: T, options?: DispatchOptions) => Pe
163
180
  */
164
181
  declare const registerJobsWithQueue: () => void;
165
182
  //#endregion
166
- export { DispatchOptions, Job, JobConstructor, JobRegistry, PendingDispatch, dispatch, registerJobsWithQueue };
183
+ export { DispatchOptions, Job, JobConstructor, JobRegistry, PendingDispatch, dispatch, loadJobs, registerJobsWithQueue };
package/dist/index.js CHANGED
@@ -1,4 +1,8 @@
1
- import { i as JobRegistry, n as Job, r as PendingDispatch, t as registerJobsWithQueue } from "./bridge-BtIHRRM7.js";
1
+ import { i as JobRegistry, n as Job, r as PendingDispatch, t as registerJobsWithQueue } from "./bridge-2bzbP44n.js";
2
+ import { importFile, nodeEnv, outputDir } from "@arkstack/common";
3
+ import path, { join } from "node:path";
4
+ import { Arkstack } from "@arkstack/contract";
5
+ import { readdirSync } from "node:fs";
2
6
  //#region src/dispatch.ts
3
7
  /**
4
8
  * Dispatch a job instance onto a queue.
@@ -20,7 +24,60 @@ const dispatch = (job, options = {}) => {
20
24
  return pending;
21
25
  };
22
26
  //#endregion
27
+ //#region src/loader.ts
28
+ /**
29
+ * Whether a module export is a runnable job class.
30
+ *
31
+ * The test is `handle` on the prototype rather than `instanceof Job`: a job
32
+ * module loaded through jiti carries its own copy of the base class, so an
33
+ * identity check would reject every job it finds. An abstract base declares no
34
+ * `handle` of its own and is skipped.
35
+ */
36
+ const isJobClass = (value) => typeof value === "function" && typeof value.prototype?.handle === "function";
37
+ /** The job modules in the first candidate directory that has any. */
38
+ const jobFiles = (directories) => {
39
+ for (const directory of directories) try {
40
+ const files = readdirSync(directory, { withFileTypes: true }).filter((file) => file.isFile() && [
41
+ ".ts",
42
+ ".js",
43
+ ".mjs"
44
+ ].includes(path.extname(file.name)));
45
+ if (files.length) return [directory, files];
46
+ } catch {}
47
+ return ["", []];
48
+ };
49
+ /**
50
+ * Import the application's job classes so a worker can reconstruct them.
51
+ *
52
+ * A dedicated `queue:work` process never constructs the app's jobs itself, and
53
+ * a job class only reaches the {@link JobRegistry} when its module is loaded —
54
+ * without this a worker cannot resolve a single payload it pops, and every job
55
+ * is released back onto the queue until it exhausts its attempts.
56
+ *
57
+ * Every concrete `Job` subclass a module exports is registered under its class
58
+ * name, which is the name payloads are serialized with.
59
+ *
60
+ * @param subPath Directory (under `src/`, or the build output) to load from.
61
+ * @returns The names registered, in discovery order.
62
+ */
63
+ const loadJobs = async (subPath = join("app", "jobs")) => {
64
+ const root = Arkstack.rootDir();
65
+ const [directory, files] = jobFiles(nodeEnv() === "prod" ? [join(outputDir(), subPath), join(root, "src", subPath)] : [join(root, "src", subPath), join(outputDir(), subPath)]);
66
+ const registered = [];
67
+ for (const file of files) try {
68
+ const module = await importFile(join(directory, file.name));
69
+ for (const value of Object.values(module)) {
70
+ if (!isJobClass(value)) continue;
71
+ JobRegistry.register(value);
72
+ registered.push(value.name);
73
+ }
74
+ } catch {
75
+ continue;
76
+ }
77
+ return registered;
78
+ };
79
+ //#endregion
23
80
  //#region src/index.ts
24
81
  registerJobsWithQueue();
25
82
  //#endregion
26
- export { Job, JobRegistry, PendingDispatch, dispatch, registerJobsWithQueue };
83
+ export { Job, JobRegistry, PendingDispatch, dispatch, loadJobs, registerJobsWithQueue };
package/dist/setup.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as registerJobsWithQueue } from "./bridge-BtIHRRM7.js";
1
+ import { t as registerJobsWithQueue } from "./bridge-2bzbP44n.js";
2
2
  //#region src/setup.ts
3
3
  /**
4
4
  * Boot the jobs/queue integration. Import this from your application bootstrap:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkstack/jobs",
3
- "version": "0.18.3",
3
+ "version": "0.18.4",
4
4
  "type": "module",
5
5
  "description": "Jobs module for Arkstack, providing dispatchable job classes and a dispatcher on top of @arkstack/queue.",
6
6
  "homepage": "https://arkstack.toneflix.net/guide/jobs",
@@ -32,12 +32,12 @@
32
32
  "./package.json": "./package.json"
33
33
  },
34
34
  "dependencies": {
35
- "@arkstack/queue": "^0.18.3",
36
- "@arkstack/common": "^0.18.3"
35
+ "@arkstack/common": "^0.18.4",
36
+ "@arkstack/queue": "^0.18.4"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "@h3ravel/musket": "^2.2.13",
40
- "@arkstack/contract": "^0.18.3"
40
+ "@arkstack/contract": "^0.18.4"
41
41
  },
42
42
  "scripts": {
43
43
  "build": "tsdown",