@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.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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
|
|
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 {
|
|
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.
|
|
110
|
-
*
|
|
111
|
-
*
|
|
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-
|
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arkstack/jobs",
|
|
3
|
-
"version": "0.18.
|
|
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/
|
|
36
|
-
"@arkstack/
|
|
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.
|
|
40
|
+
"@arkstack/contract": "^0.18.4"
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
43
|
"build": "tsdown",
|