@x12i/api-hub-jobs 0.0.0-stage → 0.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/README.md +10 -2
- package/dist/index.d.ts +37 -0
- package/dist/index.js +110 -0
- package/dist/index.js.map +1 -0
- package/package.json +34 -4
package/README.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @x12i/api-hub-jobs
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Async job runner with a 24 hour result store for the x12i API hub
|
|
4
|
+
|
|
5
|
+
ESM package for Node.js 24 or newer. Import the public name:
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
import * as jobs from '@x12i/api-hub-jobs';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The whole set is also available from [`@x12i/api-hub`](https://www.npmjs.com/package/@x12i/api-hub).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type AuthContext, type JobRecord, type JobRunner, type Logger } from '@x12i/api-hub-core';
|
|
2
|
+
export declare const JOB_TTL_MS: number;
|
|
3
|
+
interface StoredJob extends JobRecord {
|
|
4
|
+
owner: string;
|
|
5
|
+
}
|
|
6
|
+
/** Result store. Memory for now; a shared store (e.g. Mongo via @x12i/dispatchix-queue-mongo) can implement it later. */
|
|
7
|
+
export interface JobStore {
|
|
8
|
+
put(job: StoredJob): Promise<void>;
|
|
9
|
+
get(id: string): Promise<StoredJob | undefined>;
|
|
10
|
+
delete(id: string): Promise<void>;
|
|
11
|
+
expired(now: number): Promise<string[]>;
|
|
12
|
+
}
|
|
13
|
+
export declare function memoryJobStore(): JobStore;
|
|
14
|
+
export interface RefundEvent {
|
|
15
|
+
jobId: string;
|
|
16
|
+
capability: string;
|
|
17
|
+
auth: AuthContext;
|
|
18
|
+
errorCode: string;
|
|
19
|
+
}
|
|
20
|
+
/** Called when a job fails after being accepted. Billing lives elsewhere; this is the interface only. */
|
|
21
|
+
export type RefundHook = (event: RefundEvent) => void | Promise<void>;
|
|
22
|
+
export interface JobRunnerOptions {
|
|
23
|
+
store?: JobStore;
|
|
24
|
+
ttlMs?: number;
|
|
25
|
+
concurrency?: number;
|
|
26
|
+
refund?: RefundHook;
|
|
27
|
+
log?: Logger;
|
|
28
|
+
now?: () => number;
|
|
29
|
+
}
|
|
30
|
+
export interface ManagedJobRunner extends JobRunner {
|
|
31
|
+
/** Resolves when every job submitted so far has finished. For tests and graceful shutdown. */
|
|
32
|
+
drain(): Promise<void>;
|
|
33
|
+
sweep(): Promise<number>;
|
|
34
|
+
stop(): void;
|
|
35
|
+
}
|
|
36
|
+
export declare function createJobRunner(options?: JobRunnerOptions): ManagedJobRunner;
|
|
37
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { invokeCapability, } from '@x12i/api-hub-core';
|
|
3
|
+
export const JOB_TTL_MS = 24 * 60 * 60 * 1000;
|
|
4
|
+
export function memoryJobStore() {
|
|
5
|
+
const map = new Map();
|
|
6
|
+
return {
|
|
7
|
+
async put(job) {
|
|
8
|
+
map.set(job.id, { ...job });
|
|
9
|
+
},
|
|
10
|
+
async get(id) {
|
|
11
|
+
const job = map.get(id);
|
|
12
|
+
return job ? { ...job } : undefined;
|
|
13
|
+
},
|
|
14
|
+
async delete(id) {
|
|
15
|
+
map.delete(id);
|
|
16
|
+
},
|
|
17
|
+
async expired(now) {
|
|
18
|
+
return [...map.values()].filter((j) => Date.parse(j.expiresAt) <= now).map((j) => j.id);
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function createJobRunner(options = {}) {
|
|
23
|
+
const store = options.store ?? memoryJobStore();
|
|
24
|
+
const ttl = options.ttlMs ?? JOB_TTL_MS;
|
|
25
|
+
const concurrency = options.concurrency ?? 4;
|
|
26
|
+
const now = options.now ?? Date.now;
|
|
27
|
+
const queue = [];
|
|
28
|
+
const running = new Set();
|
|
29
|
+
let active = 0;
|
|
30
|
+
const iso = (t) => new Date(t).toISOString();
|
|
31
|
+
const pump = () => {
|
|
32
|
+
while (active < concurrency && queue.length > 0) {
|
|
33
|
+
const task = queue.shift();
|
|
34
|
+
active++;
|
|
35
|
+
const p = task().finally(() => {
|
|
36
|
+
active--;
|
|
37
|
+
running.delete(p);
|
|
38
|
+
pump();
|
|
39
|
+
});
|
|
40
|
+
running.add(p);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const update = async (id, patch) => {
|
|
44
|
+
const job = await store.get(id);
|
|
45
|
+
if (!job)
|
|
46
|
+
return;
|
|
47
|
+
await store.put({ ...job, ...patch, updatedAt: iso(now()) });
|
|
48
|
+
};
|
|
49
|
+
const view = ({ owner: _owner, ...job }) => job;
|
|
50
|
+
const sweeper = setInterval(() => void runner.sweep(), 10 * 60 * 1000);
|
|
51
|
+
sweeper.unref();
|
|
52
|
+
const runner = {
|
|
53
|
+
async submit(capability, input, auth, makeCtx) {
|
|
54
|
+
const created = now();
|
|
55
|
+
const job = {
|
|
56
|
+
id: randomUUID(),
|
|
57
|
+
capability: capability.slug,
|
|
58
|
+
status: 'queued',
|
|
59
|
+
createdAt: iso(created),
|
|
60
|
+
updatedAt: iso(created),
|
|
61
|
+
expiresAt: iso(created + ttl),
|
|
62
|
+
owner: auth.callerId,
|
|
63
|
+
};
|
|
64
|
+
await store.put(job);
|
|
65
|
+
queue.push(async () => {
|
|
66
|
+
await update(job.id, { status: 'running' });
|
|
67
|
+
const controller = new AbortController();
|
|
68
|
+
const result = await invokeCapability(capability, input, makeCtx(controller.signal));
|
|
69
|
+
if (result.ok) {
|
|
70
|
+
await update(job.id, { status: 'succeeded', result: result.output });
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
await update(job.id, { status: 'failed', error: result.error.toBody().error });
|
|
74
|
+
try {
|
|
75
|
+
await options.refund?.({ jobId: job.id, capability: capability.slug, auth, errorCode: result.error.code });
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
options.log?.error('refund hook failed', { jobId: job.id });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
pump();
|
|
83
|
+
return view(job);
|
|
84
|
+
},
|
|
85
|
+
async get(id, auth) {
|
|
86
|
+
const job = await store.get(id);
|
|
87
|
+
if (!job || job.owner !== auth.callerId)
|
|
88
|
+
return undefined;
|
|
89
|
+
if (Date.parse(job.expiresAt) <= now()) {
|
|
90
|
+
await store.delete(id);
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
return view(job);
|
|
94
|
+
},
|
|
95
|
+
async drain() {
|
|
96
|
+
while (queue.length > 0 || running.size > 0)
|
|
97
|
+
await Promise.all([...running]);
|
|
98
|
+
},
|
|
99
|
+
async sweep() {
|
|
100
|
+
const ids = await store.expired(now());
|
|
101
|
+
await Promise.all(ids.map((id) => store.delete(id)));
|
|
102
|
+
return ids.length;
|
|
103
|
+
},
|
|
104
|
+
stop() {
|
|
105
|
+
clearInterval(sweeper);
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
return runner;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/jobs/src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from 'node:crypto';\nimport {\n invokeCapability,\n type AnyCapability,\n type AuthContext,\n type Ctx,\n type JobRecord,\n type JobRunner,\n type Logger,\n} from '@x12i/api-hub-core';\n\nexport const JOB_TTL_MS = 24 * 60 * 60 * 1000;\n\ninterface StoredJob extends JobRecord {\n owner: string;\n}\n\n/** Result store. Memory for now; a shared store (e.g. Mongo via @x12i/dispatchix-queue-mongo) can implement it later. */\nexport interface JobStore {\n put(job: StoredJob): Promise<void>;\n get(id: string): Promise<StoredJob | undefined>;\n delete(id: string): Promise<void>;\n expired(now: number): Promise<string[]>;\n}\n\nexport function memoryJobStore(): JobStore {\n const map = new Map<string, StoredJob>();\n return {\n async put(job) {\n map.set(job.id, { ...job });\n },\n async get(id) {\n const job = map.get(id);\n return job ? { ...job } : undefined;\n },\n async delete(id) {\n map.delete(id);\n },\n async expired(now) {\n return [...map.values()].filter((j) => Date.parse(j.expiresAt) <= now).map((j) => j.id);\n },\n };\n}\n\nexport interface RefundEvent {\n jobId: string;\n capability: string;\n auth: AuthContext;\n errorCode: string;\n}\n\n/** Called when a job fails after being accepted. Billing lives elsewhere; this is the interface only. */\nexport type RefundHook = (event: RefundEvent) => void | Promise<void>;\n\nexport interface JobRunnerOptions {\n store?: JobStore;\n ttlMs?: number;\n concurrency?: number;\n refund?: RefundHook;\n log?: Logger;\n now?: () => number;\n}\n\nexport interface ManagedJobRunner extends JobRunner {\n /** Resolves when every job submitted so far has finished. For tests and graceful shutdown. */\n drain(): Promise<void>;\n sweep(): Promise<number>;\n stop(): void;\n}\n\nexport function createJobRunner(options: JobRunnerOptions = {}): ManagedJobRunner {\n const store = options.store ?? memoryJobStore();\n const ttl = options.ttlMs ?? JOB_TTL_MS;\n const concurrency = options.concurrency ?? 4;\n const now = options.now ?? Date.now;\n const queue: (() => Promise<void>)[] = [];\n const running = new Set<Promise<void>>();\n let active = 0;\n\n const iso = (t: number) => new Date(t).toISOString();\n\n const pump = () => {\n while (active < concurrency && queue.length > 0) {\n const task = queue.shift()!;\n active++;\n const p = task().finally(() => {\n active--;\n running.delete(p);\n pump();\n });\n running.add(p);\n }\n };\n\n const update = async (id: string, patch: Partial<StoredJob>) => {\n const job = await store.get(id);\n if (!job) return;\n await store.put({ ...job, ...patch, updatedAt: iso(now()) });\n };\n\n const view = ({ owner: _owner, ...job }: StoredJob): JobRecord => job;\n\n const sweeper = setInterval(() => void runner.sweep(), 10 * 60 * 1000);\n sweeper.unref();\n\n const runner: ManagedJobRunner = {\n async submit(capability: AnyCapability, input: unknown, auth: AuthContext, makeCtx: (signal: AbortSignal) => Ctx) {\n const created = now();\n const job: StoredJob = {\n id: randomUUID(),\n capability: capability.slug,\n status: 'queued',\n createdAt: iso(created),\n updatedAt: iso(created),\n expiresAt: iso(created + ttl),\n owner: auth.callerId,\n };\n await store.put(job);\n\n queue.push(async () => {\n await update(job.id, { status: 'running' });\n const controller = new AbortController();\n const result = await invokeCapability(capability, input, makeCtx(controller.signal));\n if (result.ok) {\n await update(job.id, { status: 'succeeded', result: result.output });\n } else {\n await update(job.id, { status: 'failed', error: result.error.toBody().error });\n try {\n await options.refund?.({ jobId: job.id, capability: capability.slug, auth, errorCode: result.error.code });\n } catch {\n options.log?.error('refund hook failed', { jobId: job.id });\n }\n }\n });\n pump();\n return view(job);\n },\n\n async get(id: string, auth: AuthContext) {\n const job = await store.get(id);\n if (!job || job.owner !== auth.callerId) return undefined;\n if (Date.parse(job.expiresAt) <= now()) {\n await store.delete(id);\n return undefined;\n }\n return view(job);\n },\n\n async drain() {\n while (queue.length > 0 || running.size > 0) await Promise.all([...running]);\n },\n\n async sweep() {\n const ids = await store.expired(now());\n await Promise.all(ids.map((id) => store.delete(id)));\n return ids.length;\n },\n\n stop() {\n clearInterval(sweeper);\n },\n };\n return runner;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@x12i/api-hub-jobs",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Async job runner with a 24 hour result store for the x12i API hub",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=24"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/x12i/apis-hub.git",
|
|
24
|
+
"directory": "packages/jobs"
|
|
25
|
+
},
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/x12i/apis-hub/issues"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/x12i/apis-hub",
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@x12i/api-hub-core": "0.1.0"
|
|
35
|
+
}
|
|
36
|
+
}
|