@owlmeans/client-job 0.1.18-rc.8 → 0.1.18
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 +18 -43
- package/agent-meta/manifest.json +3 -3
- package/agent-meta/skills/client-job/SKILL.md +39 -69
- package/build/consts.d.ts +3 -13
- package/build/consts.d.ts.map +1 -1
- package/build/consts.js +0 -10
- package/build/consts.js.map +1 -1
- package/build/feed.d.ts.map +1 -1
- package/build/feed.js +5 -3
- package/build/feed.js.map +1 -1
- package/build/helper.d.ts +8 -12
- package/build/helper.d.ts.map +1 -1
- package/build/helper.js +5 -15
- package/build/helper.js.map +1 -1
- package/build/hooks.d.ts +5 -4
- package/build/hooks.d.ts.map +1 -1
- package/build/hooks.js +2 -1
- package/build/hooks.js.map +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/build/types.d.ts +7 -16
- package/build/types.d.ts.map +1 -1
- package/build/utils/event.d.ts +5 -9
- package/build/utils/event.d.ts.map +1 -1
- package/build/utils/event.js +9 -26
- package/build/utils/event.js.map +1 -1
- package/package.json +13 -12
- package/src/consts.ts +3 -16
- package/src/feed.ts +9 -7
- package/src/helper.ts +10 -24
- package/src/hooks.ts +8 -7
- package/src/index.ts +1 -0
- package/src/types.ts +7 -17
- package/src/utils/event.ts +10 -30
package/README.md
CHANGED
|
@@ -1,21 +1,8 @@
|
|
|
1
1
|
# @owlmeans/client-job
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Overview
|
|
8
|
-
|
|
9
|
-
- `appendJobs(context, alias?)` — register the job store on the client context
|
|
10
|
-
- `useJob(id)` / `useJobs(filter)` — live reads over that store
|
|
11
|
-
- `useJobFeed(opts?)` — one socket plus one seeding list call; mount it ONCE per screen
|
|
12
|
-
- `applyJobEvent(store, event)` — the fold, for a feed of your own
|
|
13
|
-
|
|
14
|
-
## Installation
|
|
15
|
-
|
|
16
|
-
```bash
|
|
17
|
-
bun add @owlmeans/client-job@^0.1.18-rc.0
|
|
18
|
-
```
|
|
3
|
+
Browser state and hooks for the sanitized application-job contract from `@owlmeans/job`. This
|
|
4
|
+
package has no queue dependency and never receives transport records, credentials, ownership
|
|
5
|
+
fields, stack traces or internal payloads.
|
|
19
6
|
|
|
20
7
|
## Usage
|
|
21
8
|
|
|
@@ -30,39 +17,27 @@ export const makeContext = <C extends Config, T extends Context<C>>(cfg: C): T =
|
|
|
30
17
|
```
|
|
31
18
|
|
|
32
19
|
```tsx
|
|
20
|
+
import { JobViewStatus } from '@owlmeans/job'
|
|
33
21
|
import { useJobFeed, useJobs } from '@owlmeans/client-job'
|
|
34
|
-
import { JobProgress, JobStatus, useJobToasts } from '@owlmeans/web-panel/jobs'
|
|
35
|
-
import { JobState } from '@owlmeans/queue'
|
|
36
22
|
|
|
37
|
-
export const
|
|
38
|
-
const { seeded } = useJobFeed({ root: REPORTS })
|
|
39
|
-
const
|
|
40
|
-
useJobToasts(jobs.map(job => job.record))
|
|
23
|
+
export const Operations = () => {
|
|
24
|
+
const { seeded, error } = useJobFeed({ root: REPORTS, query: { size: 50 } })
|
|
25
|
+
const running = useJobs({ status: JobViewStatus.Running })
|
|
41
26
|
|
|
42
|
-
return
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
</li>)}</ul>
|
|
46
|
-
: <Spinner/>
|
|
27
|
+
if (error != null) return <p>Operations could not be loaded.</p>
|
|
28
|
+
if (!seeded) return <Spinner />
|
|
29
|
+
return <ul>{running.map(model => <li key={model.id}>{model.record.summary}</li>)}</ul>
|
|
47
30
|
}
|
|
48
31
|
```
|
|
49
32
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
`useJobFeed`
|
|
53
|
-
|
|
54
|
-
twice and doubles what the screen reacts to.
|
|
55
|
-
|
|
56
|
-
## Depends On
|
|
57
|
-
|
|
58
|
-
- [`@owlmeans/state`](../state) — the store; [`@owlmeans/client`](../client) — `useStoreModel` / `useStoreList`
|
|
59
|
-
- [`@owlmeans/client-auth`](../client-auth) — `useWs`, which puts the token on the socket query
|
|
60
|
-
- [`@owlmeans/queue`](../queue) — `JobRecord`, `JobEvent`, `JobState`
|
|
61
|
-
|
|
62
|
-
## Related
|
|
33
|
+
- `appendJobs(context, alias?)` registers a dedicated `JobView` store.
|
|
34
|
+
- `useJob(id)` and `useJobs(filter)` read safe public views.
|
|
35
|
+
- `useJobFeed(opts?)` seeds from list and applies `JobViewEvent` socket frames.
|
|
36
|
+
- `applyJobEvent(store, event)` is the public-view fold for a custom feed.
|
|
63
37
|
|
|
64
|
-
|
|
65
|
-
|
|
38
|
+
Mount `useJobFeed` once per screen; every call owns a socket. Public filtering is limited to
|
|
39
|
+
`status`, `kind`, `page` and `size`. A server must expose the matching routes through an explicit
|
|
40
|
+
`@owlmeans/server-job` policy.
|
|
66
41
|
|
|
67
42
|
<!-- owlmeans:agent-guidance:start -->
|
|
68
43
|
## Agent guidance
|
|
@@ -72,7 +47,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
72
47
|
your project's skill store (`.agents/skills/`):
|
|
73
48
|
|
|
74
49
|
```sh
|
|
75
|
-
npx @owlmeans/agent-skills@^0.1.18
|
|
50
|
+
npx @owlmeans/agent-skills@^0.1.18
|
|
76
51
|
```
|
|
77
52
|
|
|
78
53
|
The embedded files are version-matched to this package release. Do not edit them
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"schemaVersion":
|
|
2
|
+
"schemaVersion": 3,
|
|
3
3
|
"package": "@owlmeans/client-job",
|
|
4
|
-
"version": "0.1.18
|
|
5
|
-
"generatedAt": "2026-
|
|
4
|
+
"version": "0.1.18",
|
|
5
|
+
"generatedAt": "2026-10-28T00:31:34.751Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: client-job
|
|
3
|
-
description: How to
|
|
3
|
+
description: How to consume sanitized application JobView projections in a browser with appendJobs, useJob/useJobs and one useJobFeed subscription. Auto-invoked when rendering application job status or importing @owlmeans/client-job.
|
|
4
4
|
user-invocable: false
|
|
5
5
|
---
|
|
6
6
|
<!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
|
|
@@ -8,25 +8,11 @@ user-invocable: false
|
|
|
8
8
|
# @owlmeans/client-job
|
|
9
9
|
|
|
10
10
|
**Layer:** Client
|
|
11
|
-
**Install:** `"@owlmeans/client-job": "^0.1.18
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
## Key Exports
|
|
18
|
-
|
|
19
|
-
| Export | Description |
|
|
20
|
-
|--------|-------------|
|
|
21
|
-
| `appendJobs(context, alias?)` | Register the job store on the client context |
|
|
22
|
-
| `useJob(id?, resource?)` | One job, live — an unknown or absent id yields an `empty` model, never a throw |
|
|
23
|
-
| `useJobs(filter?, opts?)` | A live query over the store, newest first unless `opts.sort` says otherwise |
|
|
24
|
-
| `useJobFeed(opts?)` | Mount the socket and seed the store. **Once per screen** |
|
|
25
|
-
| `applyJobEvent(store, event)` | The fold one `JobEvent` performs, for a feed of your own |
|
|
26
|
-
| `jobEntrypointAliases(root?)` | `{ base, list, get, cancel, watch }` — the same shape the server declares |
|
|
27
|
-
| `JobFeed` / `JobFeedOptions` / `UseJobsOptions` / `JobFilter` | What the feed reports, and the option shapes |
|
|
28
|
-
| `JOBS` | The store's alias (`job-state`), typed as `StateAlias<JobRecord>` |
|
|
29
|
-
| Constants | `DEFAULT_JOB_ROOT` (`jobs`), `JOB_EVENT` (`job-event`) |
|
|
11
|
+
**Install:** `"@owlmeans/client-job": "^0.1.18"` in `dependencies`
|
|
12
|
+
|
|
13
|
+
This package stores and streams `JobView` from `@owlmeans/job`. It has no dependency on
|
|
14
|
+
`@owlmeans/queue`; raw transport records, internal payloads, credentials, ownership fields and
|
|
15
|
+
stack traces must never reach it.
|
|
30
16
|
|
|
31
17
|
## Wiring
|
|
32
18
|
|
|
@@ -40,62 +26,46 @@ export const makeContext = <C extends Config, T extends Context<C>>(cfg: C): T =
|
|
|
40
26
|
}
|
|
41
27
|
```
|
|
42
28
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
`appendJobs` is idempotent like every `append*`.
|
|
29
|
+
`appendJobs` registers a dedicated `StateAlias<JobView>` and is idempotent. The separate store
|
|
30
|
+
prevents an opaque operation id from colliding with an application resource id.
|
|
46
31
|
|
|
47
32
|
## Reading
|
|
48
33
|
|
|
49
34
|
```tsx
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const
|
|
35
|
+
import { JobViewStatus } from '@owlmeans/job'
|
|
36
|
+
import { useJobFeed, useJobs } from '@owlmeans/client-job'
|
|
37
|
+
|
|
38
|
+
const { seeded, connected, error } = useJobFeed({
|
|
39
|
+
root: REPORTS,
|
|
40
|
+
query: { status: JobViewStatus.Running, size: 50 },
|
|
41
|
+
})
|
|
42
|
+
const running = useJobs({ status: JobViewStatus.Running })
|
|
54
43
|
const one = useJob(id)
|
|
55
44
|
```
|
|
56
45
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
next seeding call fills in the rest.
|
|
78
|
-
|
|
79
|
-
Two details the fold gets right and a hand-written one usually does not: a progress frame never
|
|
80
|
-
moves a settled job back to `Active` (the completion and the last progress ping race), and an empty
|
|
81
|
-
`JobEvent.name` never overwrites a known one (the contract sends empty when the job is already
|
|
82
|
-
gone).
|
|
83
|
-
|
|
84
|
-
## The one gotcha
|
|
85
|
-
|
|
86
|
-
**`useJobFeed` opens a socket per call.** Mount it once, high enough that everything showing jobs
|
|
87
|
-
sits under it, and read the store everywhere else. Two feeds make the server push every event twice
|
|
88
|
-
and re-seed over each other.
|
|
89
|
-
|
|
90
|
-
## Depends On
|
|
91
|
-
|
|
92
|
-
- `@owlmeans/state` — the store, `replace` and the `StateModel` commit rules
|
|
93
|
-
- `@owlmeans/client` — `useStoreModel`, `useStoreList`, `useContext`
|
|
94
|
-
- `@owlmeans/client-auth` — `useWs` (the token-carrying socket)
|
|
95
|
-
- `@owlmeans/queue` — `JobRecord`, `JobEvent`, `JobState`, `isSettled`
|
|
46
|
+
- Mount `useJobFeed` once per screen. Each call owns a socket.
|
|
47
|
+
- `seeded` distinguishes an empty list from one not loaded yet.
|
|
48
|
+
- `error` is the list failure; `connected` reports the watch connection.
|
|
49
|
+
- Public query fields are `status`, `kind`, `page` and `size` only.
|
|
50
|
+
- `useJob` yields an empty model for an unseen id rather than throwing.
|
|
51
|
+
- `applyJobEvent` replaces a row with the complete sanitized view or removes its opaque id.
|
|
52
|
+
|
|
53
|
+
The server must expose the same alias root through `@owlmeans/job` declarations and an explicit
|
|
54
|
+
`@owlmeans/server-job` policy. Do not reconstruct queue names, raw ids or technical payload fields
|
|
55
|
+
in browser code.
|
|
56
|
+
|
|
57
|
+
## Key exports
|
|
58
|
+
|
|
59
|
+
| Export | Purpose |
|
|
60
|
+
|---|---|
|
|
61
|
+
| `appendJobs` | Register the `JobView` state resource |
|
|
62
|
+
| `useJob` / `useJobs` | Read public job views |
|
|
63
|
+
| `useJobFeed` | Seed list plus sanitized event subscription |
|
|
64
|
+
| `applyJobEvent` | Fold a `JobViewEvent` into a store |
|
|
65
|
+
| `JOBS` | Default store alias |
|
|
96
66
|
|
|
97
67
|
## Related
|
|
98
68
|
|
|
99
|
-
- `
|
|
100
|
-
- `
|
|
101
|
-
- `
|
|
69
|
+
- `job` — safe contracts, schemas and route declarations
|
|
70
|
+
- `server-job` — policy-enforced technical-to-domain bridge
|
|
71
|
+
- `state` and `client-socket` — local storage and authenticated socket transport
|
package/build/consts.d.ts
CHANGED
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import type {
|
|
1
|
+
import { type StateAlias } from '@owlmeans/state';
|
|
2
|
+
import type { JobView } from '@owlmeans/job';
|
|
3
3
|
/** The store this package registers and every hook here reads. */
|
|
4
|
-
export declare const JOBS: StateAlias<
|
|
5
|
-
/**
|
|
6
|
-
* The alias root a group answers under when a caller names none — the same default
|
|
7
|
-
* `@owlmeans/server-job` declares with.
|
|
8
|
-
*
|
|
9
|
-
* That package's `<root>` / `<root>:<verb>` alias shape and its `job-event` frame name are the
|
|
10
|
-
* whole contract between the two halves. They are restated here rather than imported because the
|
|
11
|
-
* server half pulls fastify in, and a browser bundle must not.
|
|
12
|
-
*/
|
|
13
|
-
export declare const DEFAULT_JOB_ROOT = "jobs";
|
|
14
|
-
export declare const JOB_EVENT = "job-event";
|
|
4
|
+
export declare const JOBS: StateAlias<JobView>;
|
|
15
5
|
//# sourceMappingURL=consts.d.ts.map
|
package/build/consts.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAc,MAAM,iBAAiB,CAAA;AAC7D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAE5C,kEAAkE;AAClE,eAAO,MAAM,IAAI,EAAE,UAAU,CAAC,OAAO,CAAoC,CAAA"}
|
package/build/consts.js
CHANGED
|
@@ -1,14 +1,4 @@
|
|
|
1
1
|
import { stateAlias } from '@owlmeans/state';
|
|
2
2
|
/** The store this package registers and every hook here reads. */
|
|
3
3
|
export const JOBS = stateAlias('job-state');
|
|
4
|
-
/**
|
|
5
|
-
* The alias root a group answers under when a caller names none — the same default
|
|
6
|
-
* `@owlmeans/server-job` declares with.
|
|
7
|
-
*
|
|
8
|
-
* That package's `<root>` / `<root>:<verb>` alias shape and its `job-event` frame name are the
|
|
9
|
-
* whole contract between the two halves. They are restated here rather than imported because the
|
|
10
|
-
* server half pulls fastify in, and a browser bundle must not.
|
|
11
|
-
*/
|
|
12
|
-
export const DEFAULT_JOB_ROOT = 'jobs';
|
|
13
|
-
export const JOB_EVENT = 'job-event';
|
|
14
4
|
//# sourceMappingURL=consts.js.map
|
package/build/consts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAG7D,kEAAkE;AAClE,MAAM,CAAC,MAAM,IAAI,GAAwB,UAAU,CAAU,WAAW,CAAC,CAAA"}
|
package/build/feed.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feed.d.ts","sourceRoot":"","sources":["../src/feed.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAmB,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"feed.d.ts","sourceRoot":"","sources":["../src/feed.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAmB,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAK1E;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,UAAU,UAAW,cAAc,KAAG,OAkDlD,CAAA"}
|
package/build/feed.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import { useContext } from '@owlmeans/client';
|
|
3
3
|
import { useWs } from '@owlmeans/client-auth';
|
|
4
|
-
import { DEFAULT_JOB_ROOT,
|
|
5
|
-
import {
|
|
4
|
+
import { DEFAULT_JOB_ROOT, JOB_EVENT, jobEntrypointAliases } from '@owlmeans/job';
|
|
5
|
+
import { logger } from '@owlmeans/log';
|
|
6
|
+
import { JOBS } from './consts.js';
|
|
6
7
|
import { applyJobEvent } from './utils/index.js';
|
|
8
|
+
const log = logger('client-job');
|
|
7
9
|
/**
|
|
8
10
|
* Mount the job feed: one socket, seeded once from the list entrypoint.
|
|
9
11
|
*
|
|
@@ -55,7 +57,7 @@ export const useJobFeed = (opts) => {
|
|
|
55
57
|
await applyJobEvent(store, message.payload);
|
|
56
58
|
}
|
|
57
59
|
catch (e) {
|
|
58
|
-
|
|
60
|
+
log.warn('Job feed apply failed', e);
|
|
59
61
|
}
|
|
60
62
|
});
|
|
61
63
|
return () => unsubscribe();
|
package/build/feed.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feed.js","sourceRoot":"","sources":["../src/feed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"feed.js","sourceRoot":"","sources":["../src/feed.ts"],"names":[],"mappings":"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"}
|
package/build/helper.d.ts
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import type {
|
|
3
|
-
import type { Config, Context
|
|
1
|
+
import { type StateAlias } from '@owlmeans/state';
|
|
2
|
+
import type { JobView } from '@owlmeans/job';
|
|
3
|
+
import type { Config, Context } from './types.js';
|
|
4
4
|
/**
|
|
5
5
|
* Register the store the job hooks read.
|
|
6
6
|
*
|
|
7
|
-
* Jobs get a store of their own rather than the context's default one because
|
|
8
|
-
*
|
|
9
|
-
* an unrelated row. Idempotent, like every `append*` — an app that
|
|
10
|
-
* the store already collected.
|
|
7
|
+
* Jobs get a store of their own rather than the context's default one because their opaque ids
|
|
8
|
+
* are a separate domain namespace. Sharing that id space with the app's own records is how a
|
|
9
|
+
* completed operation overwrites an unrelated row. Idempotent, like every `append*` — an app that
|
|
10
|
+
* calls it twice keeps whatever the store already collected.
|
|
11
11
|
*/
|
|
12
|
-
export declare const appendJobs: <C extends Config, T extends Context<C>>(context: T, alias?: StateAlias<
|
|
13
|
-
/**
|
|
14
|
-
* The aliases one job group answers under — the same shape `@owlmeans/server-job` declares.
|
|
15
|
-
*/
|
|
16
|
-
export declare const jobEntrypointAliases: (root?: string) => JobEntrypointAliases;
|
|
12
|
+
export declare const appendJobs: <C extends Config, T extends Context<C>>(context: T, alias?: StateAlias<JobView>) => T;
|
|
17
13
|
//# sourceMappingURL=helper.d.ts.map
|
package/build/helper.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACtE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAE5C,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAEjD;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,WACtD,CAAC,UAAS,UAAU,CAAC,OAAO,CAAC,KACrC,CAA4D,CAAA"}
|
package/build/helper.js
CHANGED
|
@@ -1,22 +1,12 @@
|
|
|
1
1
|
import { appendStateResource } from '@owlmeans/state';
|
|
2
|
-
import {
|
|
2
|
+
import { JOBS } from './consts.js';
|
|
3
3
|
/**
|
|
4
4
|
* Register the store the job hooks read.
|
|
5
5
|
*
|
|
6
|
-
* Jobs get a store of their own rather than the context's default one because
|
|
7
|
-
*
|
|
8
|
-
* an unrelated row. Idempotent, like every `append*` — an app that
|
|
9
|
-
* the store already collected.
|
|
6
|
+
* Jobs get a store of their own rather than the context's default one because their opaque ids
|
|
7
|
+
* are a separate domain namespace. Sharing that id space with the app's own records is how a
|
|
8
|
+
* completed operation overwrites an unrelated row. Idempotent, like every `append*` — an app that
|
|
9
|
+
* calls it twice keeps whatever the store already collected.
|
|
10
10
|
*/
|
|
11
11
|
export const appendJobs = (context, alias = JOBS) => appendStateResource(context, alias);
|
|
12
|
-
/**
|
|
13
|
-
* The aliases one job group answers under — the same shape `@owlmeans/server-job` declares.
|
|
14
|
-
*/
|
|
15
|
-
export const jobEntrypointAliases = (root = DEFAULT_JOB_ROOT) => ({
|
|
16
|
-
base: root,
|
|
17
|
-
list: `${root}:list`,
|
|
18
|
-
get: `${root}:get`,
|
|
19
|
-
cancel: `${root}:cancel`,
|
|
20
|
-
watch: `${root}:watch`,
|
|
21
|
-
});
|
|
22
12
|
//# sourceMappingURL=helper.js.map
|
package/build/helper.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,
|
|
1
|
+
{"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAmB,MAAM,iBAAiB,CAAA;AAEtE,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAGlC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CACxB,OAAU,EAAE,KAAK,GAAwB,IAAI,EAC1C,EAAE,CAAC,mBAAmB,CAAgB,OAAO,EAAE,KAAK,CAAM,CAAA"}
|
package/build/hooks.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { StateModel } from '@owlmeans/state';
|
|
2
|
-
import type {
|
|
2
|
+
import type { JobView } from '@owlmeans/job';
|
|
3
3
|
import type { JobFilter, UseJobsOptions } from './types.js';
|
|
4
4
|
/**
|
|
5
5
|
* One job, live.
|
|
@@ -7,12 +7,13 @@ import type { JobFilter, UseJobsOptions } from './types.js';
|
|
|
7
7
|
* Never throws for an id the store has not seen — the model comes back `empty`, which is what a
|
|
8
8
|
* screen renders as "still loading" while {@link useJobFeed} seeds.
|
|
9
9
|
*/
|
|
10
|
-
export declare const useJob:
|
|
10
|
+
export declare const useJob: (id?: string, resource?: string) => StateModel<JobView>;
|
|
11
11
|
/**
|
|
12
12
|
* A live query over the job store, newest first unless told otherwise.
|
|
13
13
|
*
|
|
14
14
|
* The criteria is the resource language, so a filter written for the list entrypoint means the
|
|
15
|
-
* same thing here: `{
|
|
15
|
+
* same thing here: `{ status: JobViewStatus.Running }`, `{ kind: 'report-build' }` or an
|
|
16
|
+
* allowlisted metadata field.
|
|
16
17
|
*/
|
|
17
|
-
export declare const useJobs:
|
|
18
|
+
export declare const useJobs: (filter?: JobFilter, opts?: UseJobsOptions) => StateModel<JobView>[];
|
|
18
19
|
//# sourceMappingURL=hooks.d.ts.map
|
package/build/hooks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAE5C,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3D;;;;;GAKG;AACH,eAAO,MAAM,MAAM,QACZ,MAAM,aAAY,MAAM,KAC5B,UAAU,CAAC,OAAO,CAAyC,CAAA;AAE9D;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,YACT,SAAS,SAAS,cAAc,KACxC,UAAU,CAAC,OAAO,CAAC,EAIpB,CAAA"}
|
package/build/hooks.js
CHANGED
|
@@ -11,7 +11,8 @@ export const useJob = (id, resource = JOBS) => useStoreModel(id, resource);
|
|
|
11
11
|
* A live query over the job store, newest first unless told otherwise.
|
|
12
12
|
*
|
|
13
13
|
* The criteria is the resource language, so a filter written for the list entrypoint means the
|
|
14
|
-
* same thing here: `{
|
|
14
|
+
* same thing here: `{ status: JobViewStatus.Running }`, `{ kind: 'report-build' }` or an
|
|
15
|
+
* allowlisted metadata field.
|
|
15
16
|
*/
|
|
16
17
|
export const useJobs = (filter, opts) => useStoreList({
|
|
17
18
|
query: filter,
|
package/build/hooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAG9D,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAGlC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CACpB,EAAW,EAAE,QAAQ,GAAW,IAAI,
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAG9D,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAGlC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CACpB,EAAW,EAAE,QAAQ,GAAW,IAAI,EACf,EAAE,CAAC,aAAa,CAAU,EAAE,EAAE,QAAQ,CAAC,CAAA;AAE9D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CACrB,MAAkB,EAAE,IAAqB,EAClB,EAAE,CAAC,YAAY,CAAU;IAChD,KAAK,EAAE,MAAM;IACb,IAAI,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAC3D,QAAQ,EAAE,IAAI,EAAE,QAAQ,IAAI,IAAI;CACjC,CAAC,CAAA"}
|
package/build/index.d.ts
CHANGED
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,YAAY,CAAA;AAC/B,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AAEjF,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,kBAAkB,CAAA"}
|
package/build/index.js
CHANGED
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AAEjF,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,kBAAkB,CAAA"}
|
package/build/types.d.ts
CHANGED
|
@@ -1,31 +1,22 @@
|
|
|
1
1
|
import type { ClientConfig } from '@owlmeans/client-context';
|
|
2
2
|
import type { ClientContext } from '@owlmeans/client';
|
|
3
|
-
import type {
|
|
3
|
+
import type { JobListQuery, JobView } from '@owlmeans/job';
|
|
4
4
|
import type { Criteria, Sort } from '@owlmeans/resource';
|
|
5
5
|
import type { StateAlias } from '@owlmeans/state';
|
|
6
6
|
export interface Config extends ClientConfig {
|
|
7
7
|
}
|
|
8
8
|
export interface Context<C extends Config = Config> extends ClientContext<C> {
|
|
9
9
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
base: string;
|
|
13
|
-
list: string;
|
|
14
|
-
get: string;
|
|
15
|
-
cancel: string;
|
|
16
|
-
watch: string;
|
|
17
|
-
}
|
|
18
|
-
export interface UseJobsOptions<D = unknown, R = unknown> {
|
|
19
|
-
sort?: Sort<JobRecord<D, R>>[];
|
|
10
|
+
export interface UseJobsOptions {
|
|
11
|
+
sort?: Sort<JobView>[];
|
|
20
12
|
/** Which store to read; the package's own when omitted. */
|
|
21
|
-
resource?: StateAlias<
|
|
13
|
+
resource?: StateAlias<JobView>;
|
|
22
14
|
}
|
|
23
15
|
export interface JobFeedOptions {
|
|
24
16
|
/** The alias root the group was declared under. */
|
|
25
17
|
root?: string;
|
|
26
|
-
resource?: StateAlias<
|
|
27
|
-
|
|
28
|
-
query?: Record<string, string | number | undefined>;
|
|
18
|
+
resource?: StateAlias<JobView>;
|
|
19
|
+
query?: JobListQuery;
|
|
29
20
|
}
|
|
30
21
|
/**
|
|
31
22
|
* Whether the feed is carrying anything yet, so a screen can tell "no jobs" from "not loaded".
|
|
@@ -38,5 +29,5 @@ export interface JobFeed {
|
|
|
38
29
|
/** What the seeding call threw, if it threw. */
|
|
39
30
|
error: Error | null;
|
|
40
31
|
}
|
|
41
|
-
export type JobFilter
|
|
32
|
+
export type JobFilter = Criteria<JobView>;
|
|
42
33
|
//# sourceMappingURL=types.d.ts.map
|
package/build/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAC1D,OAAO,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEjD,MAAM,WAAW,MAAO,SAAQ,YAAY;CAAI;AAEhD,MAAM,WAAW,OAAO,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,CAAE,SAAQ,aAAa,CAAC,CAAC,CAAC;CAAI;AAEhF,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAA;IACtB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAA;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAA;IAC9B,KAAK,CAAC,EAAE,YAAY,CAAA;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,0BAA0B;IAC1B,SAAS,EAAE,OAAO,CAAA;IAClB,4EAA4E;IAC5E,MAAM,EAAE,OAAO,CAAA;IACf,gDAAgD;IAChD,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;CACpB;AAED,MAAM,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAA"}
|
package/build/utils/event.d.ts
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { JobView, JobViewEvent } from '@owlmeans/job';
|
|
2
2
|
import type { StateResource } from '@owlmeans/state';
|
|
3
3
|
/**
|
|
4
4
|
* Fold one lifecycle frame into the store.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* call fills in the rest.
|
|
10
|
-
*
|
|
11
|
-
* A progress frame does NOT move a settled job back to active: the broker's completion and its
|
|
12
|
-
* last progress ping race, and the losing order would leave a finished job spinning forever.
|
|
6
|
+
* An upsert carries the complete sanitized public view and replaces the stored row. An event for
|
|
7
|
+
* an id the store never saw still writes a row, so an operation started in another tab reports its
|
|
8
|
+
* progress here too. A remove carries only the opaque public id.
|
|
13
9
|
*/
|
|
14
|
-
export declare const applyJobEvent: (store: StateResource<
|
|
10
|
+
export declare const applyJobEvent: (store: StateResource<JobView>, event: JobViewEvent) => Promise<void>;
|
|
15
11
|
//# sourceMappingURL=event.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event.d.ts","sourceRoot":"","sources":["../../src/utils/event.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"event.d.ts","sourceRoot":"","sources":["../../src/utils/event.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEpD;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,UACjB,aAAa,CAAC,OAAO,CAAC,SAAS,YAAY,KACjD,OAAO,CAAC,IAAI,CAMd,CAAA"}
|
package/build/utils/event.js
CHANGED
|
@@ -1,33 +1,16 @@
|
|
|
1
|
-
import { isSettled, JobEventType, JobState } from '@owlmeans/queue';
|
|
2
1
|
/**
|
|
3
2
|
* Fold one lifecycle frame into the store.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* call fills in the rest.
|
|
9
|
-
*
|
|
10
|
-
* A progress frame does NOT move a settled job back to active: the broker's completion and its
|
|
11
|
-
* last progress ping race, and the losing order would leave a finished job spinning forever.
|
|
4
|
+
* An upsert carries the complete sanitized public view and replaces the stored row. An event for
|
|
5
|
+
* an id the store never saw still writes a row, so an operation started in another tab reports its
|
|
6
|
+
* progress here too. A remove carries only the opaque public id.
|
|
12
7
|
*/
|
|
13
8
|
export const applyJobEvent = async (store, event) => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
progress: event.progress,
|
|
21
|
-
...(isSettled(known?.state) ? {} : { state: JobState.Active }),
|
|
22
|
-
};
|
|
23
|
-
await store.save({
|
|
24
|
-
...(known ?? { data: undefined }),
|
|
25
|
-
id: event.id,
|
|
26
|
-
queue: event.queue,
|
|
27
|
-
// A queue that removed the job has no name left to report — never overwrite a known one
|
|
28
|
-
// with the empty string the contract sends in its place.
|
|
29
|
-
...(event.name !== '' ? { name: event.name } : {}),
|
|
30
|
-
...change,
|
|
31
|
-
});
|
|
9
|
+
if (event.type === 'remove') {
|
|
10
|
+
if (await store.load(event.id) != null)
|
|
11
|
+
await store.delete(event.id);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
await store.save(event.job);
|
|
32
15
|
};
|
|
33
16
|
//# sourceMappingURL=event.js.map
|
package/build/utils/event.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event.js","sourceRoot":"","sources":["../../src/utils/event.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"event.js","sourceRoot":"","sources":["../../src/utils/event.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,EAChC,KAA6B,EAAE,KAAmB,EACnC,EAAE;IACjB,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC5B,IAAI,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,IAAI;YAAE,MAAM,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QACpE,OAAM;IACR,CAAC;IACD,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;AAC7B,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/client-job",
|
|
3
|
-
"version": "0.1.18
|
|
3
|
+
"version": "0.1.18",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -24,21 +24,22 @@
|
|
|
24
24
|
"react": "*"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@owlmeans/dep-config": "
|
|
28
|
-
"@types/react": "^19.
|
|
27
|
+
"@owlmeans/dep-config": "^0.1.18",
|
|
28
|
+
"@types/react": "^19.3.0",
|
|
29
29
|
"nodemon": "^3.1.14",
|
|
30
30
|
"typescript": "^7.0.2"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@owlmeans/client": "^0.1.18
|
|
34
|
-
"@owlmeans/client-auth": "^0.1.18
|
|
35
|
-
"@owlmeans/client-context": "^0.1.18
|
|
36
|
-
"@owlmeans/client-entrypoint": "^0.1.18
|
|
37
|
-
"@owlmeans/context": "^0.1.18
|
|
38
|
-
"@owlmeans/
|
|
39
|
-
"@owlmeans/
|
|
40
|
-
"@owlmeans/
|
|
41
|
-
"@owlmeans/
|
|
33
|
+
"@owlmeans/client": "^0.1.18",
|
|
34
|
+
"@owlmeans/client-auth": "^0.1.18",
|
|
35
|
+
"@owlmeans/client-context": "^0.1.18",
|
|
36
|
+
"@owlmeans/client-entrypoint": "^0.1.18",
|
|
37
|
+
"@owlmeans/context": "^0.1.18",
|
|
38
|
+
"@owlmeans/job": "^0.1.18",
|
|
39
|
+
"@owlmeans/log": "^0.1.18",
|
|
40
|
+
"@owlmeans/resource": "^0.1.18",
|
|
41
|
+
"@owlmeans/socket": "^0.1.18",
|
|
42
|
+
"@owlmeans/state": "^0.1.18"
|
|
42
43
|
},
|
|
43
44
|
"publishConfig": {
|
|
44
45
|
"access": "public"
|
package/src/consts.ts
CHANGED
|
@@ -1,18 +1,5 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import {
|
|
3
|
-
import type { StateAlias } from '@owlmeans/state'
|
|
1
|
+
import { type StateAlias, stateAlias } from '@owlmeans/state'
|
|
2
|
+
import type { JobView } from '@owlmeans/job'
|
|
4
3
|
|
|
5
4
|
/** The store this package registers and every hook here reads. */
|
|
6
|
-
export const JOBS: StateAlias<
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* The alias root a group answers under when a caller names none — the same default
|
|
10
|
-
* `@owlmeans/server-job` declares with.
|
|
11
|
-
*
|
|
12
|
-
* That package's `<root>` / `<root>:<verb>` alias shape and its `job-event` frame name are the
|
|
13
|
-
* whole contract between the two halves. They are restated here rather than imported because the
|
|
14
|
-
* server half pulls fastify in, and a browser bundle must not.
|
|
15
|
-
*/
|
|
16
|
-
export const DEFAULT_JOB_ROOT = 'jobs'
|
|
17
|
-
|
|
18
|
-
export const JOB_EVENT = 'job-event'
|
|
5
|
+
export const JOBS: StateAlias<JobView> = stateAlias<JobView>('job-state')
|
package/src/feed.ts
CHANGED
|
@@ -2,13 +2,15 @@ import { useEffect, useMemo, useState } from 'react'
|
|
|
2
2
|
import { useContext } from '@owlmeans/client'
|
|
3
3
|
import { useWs } from '@owlmeans/client-auth'
|
|
4
4
|
import type { ClientEntrypoint } from '@owlmeans/client-entrypoint'
|
|
5
|
-
import
|
|
5
|
+
import { DEFAULT_JOB_ROOT, JOB_EVENT, jobEntrypointAliases, type JobView, type JobViewEvent } from '@owlmeans/job'
|
|
6
6
|
import type { ListResult } from '@owlmeans/resource'
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { logger } from '@owlmeans/log'
|
|
8
|
+
import { JOBS } from './consts.js'
|
|
9
9
|
import type { Config, Context, JobFeed, JobFeedOptions } from './types.js'
|
|
10
10
|
import { applyJobEvent } from './utils/index.js'
|
|
11
11
|
|
|
12
|
+
const log = logger('client-job')
|
|
13
|
+
|
|
12
14
|
/**
|
|
13
15
|
* Mount the job feed: one socket, seeded once from the list entrypoint.
|
|
14
16
|
*
|
|
@@ -37,8 +39,8 @@ export const useJobFeed = (opts?: JobFeedOptions): JobFeed => {
|
|
|
37
39
|
let live = true
|
|
38
40
|
void (async () => {
|
|
39
41
|
try {
|
|
40
|
-
const result = await context.entrypoint<ClientEntrypoint<ListResult<
|
|
41
|
-
.call<ListResult<
|
|
42
|
+
const result = await context.entrypoint<ClientEntrypoint<ListResult<JobView>>>(aliases.list)
|
|
43
|
+
.call<ListResult<JobView>>({ query: opts?.query })
|
|
42
44
|
if (!live) {
|
|
43
45
|
return
|
|
44
46
|
}
|
|
@@ -59,11 +61,11 @@ export const useJobFeed = (opts?: JobFeedOptions): JobFeed => {
|
|
|
59
61
|
return
|
|
60
62
|
}
|
|
61
63
|
const store = context.getStateResource(alias)
|
|
62
|
-
const unsubscribe = connection.observe<
|
|
64
|
+
const unsubscribe = connection.observe<JobViewEvent>(JOB_EVENT, async message => {
|
|
63
65
|
try {
|
|
64
66
|
await applyJobEvent(store, message.payload)
|
|
65
67
|
} catch (e) {
|
|
66
|
-
|
|
68
|
+
log.warn('Job feed apply failed', e)
|
|
67
69
|
}
|
|
68
70
|
})
|
|
69
71
|
|
package/src/helper.ts
CHANGED
|
@@ -1,30 +1,16 @@
|
|
|
1
|
-
import { appendStateResource } from '@owlmeans/state'
|
|
2
|
-
import type {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import type { Config, Context, JobEntrypointAliases } from './types.js'
|
|
1
|
+
import { appendStateResource, type StateAlias } from '@owlmeans/state'
|
|
2
|
+
import type { JobView } from '@owlmeans/job'
|
|
3
|
+
import { JOBS } from './consts.js'
|
|
4
|
+
import type { Config, Context } from './types.js'
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* Register the store the job hooks read.
|
|
9
8
|
*
|
|
10
|
-
* Jobs get a store of their own rather than the context's default one because
|
|
11
|
-
*
|
|
12
|
-
* an unrelated row. Idempotent, like every `append*` — an app that
|
|
13
|
-
* the store already collected.
|
|
9
|
+
* Jobs get a store of their own rather than the context's default one because their opaque ids
|
|
10
|
+
* are a separate domain namespace. Sharing that id space with the app's own records is how a
|
|
11
|
+
* completed operation overwrites an unrelated row. Idempotent, like every `append*` — an app that
|
|
12
|
+
* calls it twice keeps whatever the store already collected.
|
|
14
13
|
*/
|
|
15
14
|
export const appendJobs = <C extends Config, T extends Context<C>>(
|
|
16
|
-
context: T, alias: StateAlias<
|
|
17
|
-
): T => appendStateResource<C, T,
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* The aliases one job group answers under — the same shape `@owlmeans/server-job` declares.
|
|
21
|
-
*/
|
|
22
|
-
export const jobEntrypointAliases = (
|
|
23
|
-
root: string = DEFAULT_JOB_ROOT
|
|
24
|
-
): JobEntrypointAliases => ({
|
|
25
|
-
base: root,
|
|
26
|
-
list: `${root}:list`,
|
|
27
|
-
get: `${root}:get`,
|
|
28
|
-
cancel: `${root}:cancel`,
|
|
29
|
-
watch: `${root}:watch`,
|
|
30
|
-
})
|
|
15
|
+
context: T, alias: StateAlias<JobView> = JOBS
|
|
16
|
+
): T => appendStateResource<C, T, JobView>(context, alias) as T
|
package/src/hooks.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useStoreList, useStoreModel } from '@owlmeans/client'
|
|
2
2
|
import type { StateModel } from '@owlmeans/state'
|
|
3
|
-
import type {
|
|
3
|
+
import type { JobView } from '@owlmeans/job'
|
|
4
4
|
import { JOBS } from './consts.js'
|
|
5
5
|
import type { JobFilter, UseJobsOptions } from './types.js'
|
|
6
6
|
|
|
@@ -10,19 +10,20 @@ import type { JobFilter, UseJobsOptions } from './types.js'
|
|
|
10
10
|
* Never throws for an id the store has not seen — the model comes back `empty`, which is what a
|
|
11
11
|
* screen renders as "still loading" while {@link useJobFeed} seeds.
|
|
12
12
|
*/
|
|
13
|
-
export const useJob =
|
|
13
|
+
export const useJob = (
|
|
14
14
|
id?: string, resource: string = JOBS
|
|
15
|
-
): StateModel<
|
|
15
|
+
): StateModel<JobView> => useStoreModel<JobView>(id, resource)
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* A live query over the job store, newest first unless told otherwise.
|
|
19
19
|
*
|
|
20
20
|
* The criteria is the resource language, so a filter written for the list entrypoint means the
|
|
21
|
-
* same thing here: `{
|
|
21
|
+
* same thing here: `{ status: JobViewStatus.Running }`, `{ kind: 'report-build' }` or an
|
|
22
|
+
* allowlisted metadata field.
|
|
22
23
|
*/
|
|
23
|
-
export const useJobs =
|
|
24
|
-
filter?: JobFilter
|
|
25
|
-
): StateModel<
|
|
24
|
+
export const useJobs = (
|
|
25
|
+
filter?: JobFilter, opts?: UseJobsOptions
|
|
26
|
+
): StateModel<JobView>[] => useStoreList<JobView>({
|
|
26
27
|
query: filter,
|
|
27
28
|
sort: opts?.sort ?? [{ field: 'createdAt', order: 'desc' }],
|
|
28
29
|
resource: opts?.resource ?? JOBS,
|
package/src/index.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ClientConfig } from '@owlmeans/client-context'
|
|
2
2
|
import type { ClientContext } from '@owlmeans/client'
|
|
3
|
-
import type {
|
|
3
|
+
import type { JobListQuery, JobView } from '@owlmeans/job'
|
|
4
4
|
import type { Criteria, Sort } from '@owlmeans/resource'
|
|
5
5
|
import type { StateAlias } from '@owlmeans/state'
|
|
6
6
|
|
|
@@ -8,27 +8,17 @@ export interface Config extends ClientConfig { }
|
|
|
8
8
|
|
|
9
9
|
export interface Context<C extends Config = Config> extends ClientContext<C> { }
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
base: string
|
|
14
|
-
list: string
|
|
15
|
-
get: string
|
|
16
|
-
cancel: string
|
|
17
|
-
watch: string
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface UseJobsOptions<D = unknown, R = unknown> {
|
|
21
|
-
sort?: Sort<JobRecord<D, R>>[]
|
|
11
|
+
export interface UseJobsOptions {
|
|
12
|
+
sort?: Sort<JobView>[]
|
|
22
13
|
/** Which store to read; the package's own when omitted. */
|
|
23
|
-
resource?: StateAlias<
|
|
14
|
+
resource?: StateAlias<JobView>
|
|
24
15
|
}
|
|
25
16
|
|
|
26
17
|
export interface JobFeedOptions {
|
|
27
18
|
/** The alias root the group was declared under. */
|
|
28
19
|
root?: string
|
|
29
|
-
resource?: StateAlias<
|
|
30
|
-
|
|
31
|
-
query?: Record<string, string | number | undefined>
|
|
20
|
+
resource?: StateAlias<JobView>
|
|
21
|
+
query?: JobListQuery
|
|
32
22
|
}
|
|
33
23
|
|
|
34
24
|
/**
|
|
@@ -43,4 +33,4 @@ export interface JobFeed {
|
|
|
43
33
|
error: Error | null
|
|
44
34
|
}
|
|
45
35
|
|
|
46
|
-
export type JobFilter
|
|
36
|
+
export type JobFilter = Criteria<JobView>
|
package/src/utils/event.ts
CHANGED
|
@@ -1,39 +1,19 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { isSettled, JobEventType, JobState } from '@owlmeans/queue'
|
|
1
|
+
import type { JobView, JobViewEvent } from '@owlmeans/job'
|
|
3
2
|
import type { StateResource } from '@owlmeans/state'
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* Fold one lifecycle frame into the store.
|
|
7
6
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* call fills in the rest.
|
|
12
|
-
*
|
|
13
|
-
* A progress frame does NOT move a settled job back to active: the broker's completion and its
|
|
14
|
-
* last progress ping race, and the losing order would leave a finished job spinning forever.
|
|
7
|
+
* An upsert carries the complete sanitized public view and replaces the stored row. An event for
|
|
8
|
+
* an id the store never saw still writes a row, so an operation started in another tab reports its
|
|
9
|
+
* progress here too. A remove carries only the opaque public id.
|
|
15
10
|
*/
|
|
16
11
|
export const applyJobEvent = async (
|
|
17
|
-
store: StateResource<
|
|
12
|
+
store: StateResource<JobView>, event: JobViewEvent
|
|
18
13
|
): Promise<void> => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
? { state: JobState.Failed, error: event.error }
|
|
25
|
-
: {
|
|
26
|
-
progress: event.progress,
|
|
27
|
-
...(isSettled(known?.state) ? {} : { state: JobState.Active }),
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
await store.save({
|
|
31
|
-
...(known ?? { data: undefined as unknown }),
|
|
32
|
-
id: event.id,
|
|
33
|
-
queue: event.queue,
|
|
34
|
-
// A queue that removed the job has no name left to report — never overwrite a known one
|
|
35
|
-
// with the empty string the contract sends in its place.
|
|
36
|
-
...(event.name !== '' ? { name: event.name } : {}),
|
|
37
|
-
...change,
|
|
38
|
-
})
|
|
14
|
+
if (event.type === 'remove') {
|
|
15
|
+
if (await store.load(event.id) != null) await store.delete(event.id)
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
await store.save(event.job)
|
|
39
19
|
}
|