@exellix/jobs-ui 1.0.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 +66 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +17 -0
- package/dist/cli.js.map +1 -0
- package/dist/http/server.d.ts +13 -0
- package/dist/http/server.d.ts.map +1 -0
- package/dist/http/server.js +43 -0
- package/dist/http/server.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/queries.d.ts +26 -0
- package/dist/queries.d.ts.map +1 -0
- package/dist/queries.js +33 -0
- package/dist/queries.js.map +1 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# @exellix/jobs-ui
|
|
2
|
+
|
|
3
|
+
Read-only **query + HTTP** layer over the Exellix job queue. Lists, groups, and counts **job runs** from [`@exellix/exellix-db`](../exellix-db/README.md). No execution rights — no `enqueue` / `claim` / `complete` / `fail`.
|
|
4
|
+
|
|
5
|
+
> A **JobRun** is one graph run on one input. See [`temp/jobs/`](../temp/jobs/README.md) for the design. This package replaces the old `@exellix/exellix-matrix-read`.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @exellix/jobs-ui @exellix/exellix-db
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Queries
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createJobRunStore } from '@exellix/exellix-db';
|
|
17
|
+
import { listRuns, getRun, groupByItem, countByStatus, countRuns } from '@exellix/jobs-ui';
|
|
18
|
+
|
|
19
|
+
const { store } = await createJobRunStore({ mongoUri: process.env.MONGO_URI });
|
|
20
|
+
|
|
21
|
+
await listRuns(store, { jobDefId, status: 'pending', page: 1, pageSize: 50 });
|
|
22
|
+
await getRun(store, jobRunId);
|
|
23
|
+
await groupByItem(store, itemId); // all runs for one source item
|
|
24
|
+
await countByStatus(store, jobDefId); // { blocked, pending, running, done, failed }
|
|
25
|
+
await countRuns(store, { graphId, status });
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## HTTP server
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# MONGO_URI must be set (or available in ../graph-engine/.env via the workspace)
|
|
32
|
+
npm run ui:serve # → jobs-ui listening on http://0.0.0.0:3099
|
|
33
|
+
# or, after install:
|
|
34
|
+
npx exellix-jobs-ui serve
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Set `PORT` to override the default `3099`.
|
|
38
|
+
|
|
39
|
+
| Route | Returns |
|
|
40
|
+
|-------|---------|
|
|
41
|
+
| `GET /health` | liveness |
|
|
42
|
+
| `GET /runs` | paged list (query: `jobDefId`, `status`, `itemId`, `graphId`, `page`, `pageSize`) |
|
|
43
|
+
| `GET /runs/:id` | a single job run (404 if missing) |
|
|
44
|
+
| `GET /items/:itemId/runs` | all job runs for one source item |
|
|
45
|
+
| `GET /stats/by-status` | counts by status (optional `?jobDefId=`) |
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
curl http://localhost:3099/health
|
|
49
|
+
curl "http://localhost:3099/runs?status=pending&pageSize=20"
|
|
50
|
+
curl http://localhost:3099/stats/by-status
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
There is no bundled HTML dashboard; this package is the read API a frontend builds on.
|
|
54
|
+
|
|
55
|
+
## Scripts
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npm run build # tsc → dist/
|
|
59
|
+
npm test # unit + live (live runs only when MONGO_URI is set)
|
|
60
|
+
npm run test:live # live Mongo integration tests only
|
|
61
|
+
npm run ui:serve # start the HTTP server
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## License
|
|
65
|
+
|
|
66
|
+
exellix-license
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { startJobsUiHttp } from './http/server.js';
|
|
3
|
+
const cmd = process.argv[2];
|
|
4
|
+
async function main() {
|
|
5
|
+
if (cmd === 'serve') {
|
|
6
|
+
const { url } = await startJobsUiHttp();
|
|
7
|
+
console.log(`jobs-ui listening on ${url}`);
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
console.log('Usage: exellix-jobs-ui serve');
|
|
11
|
+
process.exit(cmd ? 1 : 0);
|
|
12
|
+
}
|
|
13
|
+
main().catch((err) => {
|
|
14
|
+
console.error(err);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
});
|
|
17
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAE5B,KAAK,UAAU,IAAI;IACjB,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;QACpB,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,eAAe,EAAE,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,wBAAwB,GAAG,EAAE,CAAC,CAAC;QAC3C,OAAO;IACT,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;IAC5C,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5B,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { JobRunStore } from '@exellix/exellix-db';
|
|
2
|
+
export type StartJobsUiHttpOptions = {
|
|
3
|
+
store?: JobRunStore;
|
|
4
|
+
mongoUri?: string;
|
|
5
|
+
port?: number;
|
|
6
|
+
host?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function startJobsUiHttp(options?: StartJobsUiHttpOptions): Promise<{
|
|
9
|
+
store: JobRunStore;
|
|
10
|
+
close: () => Promise<void>;
|
|
11
|
+
url: string;
|
|
12
|
+
}>;
|
|
13
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/http/server.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGvD,MAAM,MAAM,sBAAsB,GAAG;IACnC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,wBAAsB,eAAe,CAAC,OAAO,GAAE,sBAA2B,GAAG,OAAO,CAAC;IACnF,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;CACb,CAAC,CA6CD"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import Fastify from 'fastify';
|
|
2
|
+
import { createJobRunStore } from '@exellix/exellix-db';
|
|
3
|
+
import { countByStatus, getRun, groupByItem, listRuns } from '../queries.js';
|
|
4
|
+
export async function startJobsUiHttp(options = {}) {
|
|
5
|
+
const bundle = options.store
|
|
6
|
+
? { store: options.store, close: async () => undefined }
|
|
7
|
+
: await createJobRunStore({ mongoUri: options.mongoUri });
|
|
8
|
+
const app = Fastify({ logger: false });
|
|
9
|
+
const { store } = bundle;
|
|
10
|
+
app.get('/health', async () => ({ ok: true }));
|
|
11
|
+
app.get('/runs', async (req) => {
|
|
12
|
+
const q = req.query;
|
|
13
|
+
return listRuns(store, {
|
|
14
|
+
jobDefId: q.jobDefId,
|
|
15
|
+
itemId: q.itemId,
|
|
16
|
+
graphId: q.graphId,
|
|
17
|
+
status: q.status,
|
|
18
|
+
page: q.page ? Number(q.page) : undefined,
|
|
19
|
+
pageSize: q.pageSize ? Number(q.pageSize) : undefined,
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
app.get('/runs/:id', async (req, reply) => {
|
|
23
|
+
const { id } = req.params;
|
|
24
|
+
const run = await getRun(store, id);
|
|
25
|
+
if (!run) {
|
|
26
|
+
return reply.code(404).send({ error: 'job run not found' });
|
|
27
|
+
}
|
|
28
|
+
return run;
|
|
29
|
+
});
|
|
30
|
+
app.get('/items/:itemId/runs', async (req) => {
|
|
31
|
+
const { itemId } = req.params;
|
|
32
|
+
return groupByItem(store, itemId);
|
|
33
|
+
});
|
|
34
|
+
app.get('/stats/by-status', async (req) => {
|
|
35
|
+
const q = req.query;
|
|
36
|
+
return countByStatus(store, q.jobDefId);
|
|
37
|
+
});
|
|
38
|
+
const port = options.port ?? Number(process.env.PORT ?? 3099);
|
|
39
|
+
const host = options.host ?? '0.0.0.0';
|
|
40
|
+
await app.listen({ port, host });
|
|
41
|
+
return { store, close: async () => { await app.close(); await bundle.close(); }, url: `http://${host}:${port}` };
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/http/server.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { countByStatus, countRuns, getRun, groupByItem, listRuns, type ListRunsQuery, type RunPage, type StatusCounts, } from './queries.js';
|
|
2
|
+
export { startJobsUiHttp, type StartJobsUiHttpOptions } from './http/server.js';
|
|
3
|
+
export type { JobRun, JobRunStatus, JobRunStore } from '@exellix/exellix-db';
|
|
4
|
+
export { createMemoryJobRunStore, createJobRunStore } from '@exellix/exellix-db';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,SAAS,EACT,MAAM,EACN,WAAW,EACX,QAAQ,EACR,KAAK,aAAa,EAClB,KAAK,OAAO,EACZ,KAAK,YAAY,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAEhF,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,SAAS,EACT,MAAM,EACN,WAAW,EACX,QAAQ,GAIT,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAA+B,MAAM,kBAAkB,CAAC;AAGhF,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { JobRun, JobRunStatus, JobRunStore } from '@exellix/exellix-db';
|
|
2
|
+
export type ListRunsQuery = {
|
|
3
|
+
jobDefId?: string;
|
|
4
|
+
status?: JobRunStatus | JobRunStatus[];
|
|
5
|
+
itemId?: string;
|
|
6
|
+
graphId?: string;
|
|
7
|
+
page?: number;
|
|
8
|
+
pageSize?: number;
|
|
9
|
+
};
|
|
10
|
+
export type RunPage = {
|
|
11
|
+
runs: JobRun[];
|
|
12
|
+
total: number;
|
|
13
|
+
page: number;
|
|
14
|
+
pageSize: number;
|
|
15
|
+
};
|
|
16
|
+
export type StatusCounts = Record<JobRunStatus, number>;
|
|
17
|
+
export declare function listRuns(store: JobRunStore, query?: ListRunsQuery): Promise<RunPage>;
|
|
18
|
+
export declare function getRun(store: JobRunStore, id: string): Promise<JobRun | null>;
|
|
19
|
+
export declare function groupByItem(store: JobRunStore, itemId: string): Promise<JobRun[]>;
|
|
20
|
+
export declare function countByStatus(store: JobRunStore, jobDefId?: string): Promise<StatusCounts>;
|
|
21
|
+
export declare function countRuns(store: JobRunStore, filter: {
|
|
22
|
+
jobDefId?: string;
|
|
23
|
+
graphId?: string;
|
|
24
|
+
status?: JobRunStatus | JobRunStatus[];
|
|
25
|
+
}): Promise<number>;
|
|
26
|
+
//# sourceMappingURL=queries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAE7E,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,YAAY,GAAG,YAAY,EAAE,CAAC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;AAIxD,wBAAsB,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,GAAE,aAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,CAgB9F;AAED,wBAAsB,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEnF;AAED,wBAAsB,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAEvF;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAMhG;AAED,wBAAsB,SAAS,CAC7B,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,YAAY,GAAG,YAAY,EAAE,CAAA;CAAE,GACtF,OAAO,CAAC,MAAM,CAAC,CAEjB"}
|
package/dist/queries.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const ALL_STATUSES = ['blocked', 'pending', 'running', 'done', 'failed'];
|
|
2
|
+
export async function listRuns(store, query = {}) {
|
|
3
|
+
const page = Math.max(1, query.page ?? 1);
|
|
4
|
+
const pageSize = Math.min(500, Math.max(1, query.pageSize ?? 50));
|
|
5
|
+
const skip = (page - 1) * pageSize;
|
|
6
|
+
const filter = {
|
|
7
|
+
jobDefId: query.jobDefId,
|
|
8
|
+
status: query.status,
|
|
9
|
+
itemId: query.itemId,
|
|
10
|
+
graphId: query.graphId,
|
|
11
|
+
sort: { createdAt: -1 },
|
|
12
|
+
};
|
|
13
|
+
const total = await store.count(filter);
|
|
14
|
+
const runs = await store.find({ ...filter, skip, limit: pageSize });
|
|
15
|
+
return { runs, total, page, pageSize };
|
|
16
|
+
}
|
|
17
|
+
export async function getRun(store, id) {
|
|
18
|
+
return store.get(id);
|
|
19
|
+
}
|
|
20
|
+
export async function groupByItem(store, itemId) {
|
|
21
|
+
return store.find({ itemId, sort: { createdAt: 1 } });
|
|
22
|
+
}
|
|
23
|
+
export async function countByStatus(store, jobDefId) {
|
|
24
|
+
const counts = Object.fromEntries(ALL_STATUSES.map((s) => [s, 0]));
|
|
25
|
+
for (const status of ALL_STATUSES) {
|
|
26
|
+
counts[status] = await store.count({ jobDefId, status });
|
|
27
|
+
}
|
|
28
|
+
return counts;
|
|
29
|
+
}
|
|
30
|
+
export async function countRuns(store, filter) {
|
|
31
|
+
return store.count(filter);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=queries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queries.js","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":"AAoBA,MAAM,YAAY,GAAmB,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;AAEzF,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,KAAkB,EAAE,QAAuB,EAAE;IAC1E,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC;IAClE,MAAM,IAAI,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC;IAEnC,MAAM,MAAM,GAAG;QACb,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,IAAI,EAAE,EAAE,SAAS,EAAE,CAAC,CAAU,EAAE;KACjC,CAAC;IAEF,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IACpE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AACzC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,KAAkB,EAAE,EAAU;IACzD,OAAO,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,KAAkB,EAAE,MAAc;IAClE,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAkB,EAAE,QAAiB;IACvE,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAiB,CAAC;IACnF,KAAK,MAAM,MAAM,IAAI,YAAY,EAAE,CAAC;QAClC,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,KAAkB,EAClB,MAAuF;IAEvF,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AAC7B,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@exellix/jobs-ui",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Read-only job queue queries and optional HTTP serve CLI over @exellix/exellix-db.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=20"
|
|
8
|
+
},
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public",
|
|
11
|
+
"registry": "https://registry.npmjs.org/"
|
|
12
|
+
},
|
|
13
|
+
"main": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./cli": {
|
|
21
|
+
"types": "./dist/cli.d.ts",
|
|
22
|
+
"import": "./dist/cli.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"bin": {
|
|
26
|
+
"exellix-jobs-ui": "./dist/cli.js"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"README.md"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc -p tsconfig.build.json",
|
|
34
|
+
"clean": "node -e \"import('node:fs').then(fs=>fs.promises.rm('dist',{recursive:true,force:true}))\"",
|
|
35
|
+
"prepack": "npm run build",
|
|
36
|
+
"ui:serve": "node dist/cli.js serve",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"test:live": "vitest run test/queries.live.spec.ts",
|
|
39
|
+
"test:watch": "vitest"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@exellix/exellix-db": "^1.0.0",
|
|
43
|
+
"fastify": "^5.8.5"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^22.10.2",
|
|
47
|
+
"typescript": "^5.7.2",
|
|
48
|
+
"vitest": "^3.0.0"
|
|
49
|
+
},
|
|
50
|
+
"keywords": [
|
|
51
|
+
"exellix",
|
|
52
|
+
"jobs",
|
|
53
|
+
"read-tier",
|
|
54
|
+
"dashboard"
|
|
55
|
+
],
|
|
56
|
+
"license": "exellix-license"
|
|
57
|
+
}
|