@owlmeans/client-job 0.1.18-rc.9 → 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 CHANGED
@@ -1,21 +1,8 @@
1
1
  # @owlmeans/client-job
2
2
 
3
- The browser half of a job feed: a state store holding `JobRecord`s, hooks to read it, and one
4
- socket subscription that keeps it current. It addresses the entrypoints
5
- [`@owlmeans/server-job`](../server-job) declares.
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 Jobs: FC = () => {
38
- const { seeded } = useJobFeed({ root: REPORTS })
39
- const jobs = useJobs({ state: [JobState.Waiting, JobState.Active] })
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 seeded
43
- ? <ul>{jobs.map(job => <li key={job.id}>
44
- <JobStatus job={job.record} /><JobProgress job={job.record} />
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
- ## The one gotcha
51
-
52
- `useJobFeed` opens a socket per call. Mount it once, high enough that everything showing jobs sits
53
- under it, and read the store everywhere else — a second feed makes the server push every event
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
- - [`@owlmeans/server-job`](../server-job) — the entrypoints this addresses
65
- - [`@owlmeans/web-panel`](../web-panel) `./jobs` — the components to render what it collects
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-rc.20
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
@@ -1,8 +1,8 @@
1
1
  {
2
- "schemaVersion": 2,
2
+ "schemaVersion": 3,
3
3
  "package": "@owlmeans/client-job",
4
- "version": "0.1.18-rc.9",
5
- "generatedAt": "2026-09-14T19:17:35.194Z",
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 use @owlmeans/client-job — appendJobs() to register the job store, useJob/useJobs to read it, and useJobFeed() to mount the socket subscription and seed it from the list entrypoint. Auto-invoked when showing queue job progress in a browser client or importing job hooks.
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-rc.9"` in `dependencies`
12
-
13
- The browser half of a job feed. It holds `JobRecord`s in a `@owlmeans/state` store, keeps that
14
- store current from the entrypoints `@owlmeans/server-job` declares, and hands screens the ordinary
15
- state hooks. Rendering is `@owlmeans/web-panel/jobs`.
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
- Jobs get a store of their own rather than the context's default one: a job id is the broker's, and
44
- sharing an id space with the app's records is how a completed job overwrites an unrelated row.
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
- const { seeded, connected, error } = useJobFeed({ root: REPORTS, query: { size: 50 } })
51
-
52
- const running = useJobs({ state: [JobState.Waiting, JobState.Active] })
53
- const mine = useJobs({ 'data.target': projectId })
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
- The criteria language is the resource one, so a filter written for the list entrypoint means the
58
- same thing applied locally, and a dotted key reaches into the payload. `seeded` is what tells "no
59
- jobs" apart from "not loaded yet" — `useJob` answers an `empty` model for an id the store has not
60
- seen, which is a loading state and not an error. `connected` says the socket is open and `error`
61
- carries whatever the seeding call threw, so a screen can distinguish a failed load from an empty
62
- one. `JobFeedOptions` also takes `resource` for a store other than `JOBS`, and a `query` narrowing
63
- the seeding call — compared by CONTENT, so a fresh object literal per render does not re-fetch
64
- while a changed filter does re-seed.
65
-
66
- ## What `useJobFeed` does
67
-
68
- 1. Opens the watch socket through `@owlmeans/client-auth`'s `useWs`, which puts the current token
69
- on the connection query — the watch entrypoint is guarded, and the server derives ownership
70
- from that token.
71
- 2. Calls the list entrypoint once and writes the answer with **`store.replace`**, not a run of
72
- saves: it is the server saying what exists, so a job cancelled from another tab has to LEAVE the
73
- store, and one write wakes the subscribers once instead of once per record.
74
- 3. Folds each `JobEvent` frame in with `applyJobEvent` — merged over the record the store holds,
75
- because an event carries only what changed while `save` replaces. An event for an id the store
76
- never saw still writes a row, so a job enqueued in another tab reports its progress here and the
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
- - `server-job` — the entrypoints this addresses, and the ownership rule that decides what arrives
100
- - `state` — the store contract; `client-socket` — how the connection is addressed
101
- - `web-panel` — `./jobs` (`JobProgress`, `JobStatus`, `useJobToasts`)
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 { JobRecord } from '@owlmeans/queue';
2
- import type { StateAlias } from '@owlmeans/state';
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<JobRecord>;
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
@@ -1 +1 @@
1
- {"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEjD,kEAAkE;AAClE,eAAO,MAAM,IAAI,EAAE,UAAU,CAAC,SAAS,CAAsC,CAAA;AAE7E;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,SAAS,CAAA;AAEtC,eAAO,MAAM,SAAS,cAAc,CAAA"}
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
@@ -1 +1 @@
1
- {"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAG5C,kEAAkE;AAClE,MAAM,CAAC,MAAM,IAAI,GAA0B,UAAU,CAAY,WAAW,CAAC,CAAA;AAE7E;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAA;AAEtC,MAAM,CAAC,MAAM,SAAS,GAAG,WAAW,CAAA"}
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"}
@@ -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;AAG1E;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,UAAU,UAAW,cAAc,KAAG,OAkDlD,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, JOBS, JOB_EVENT } from './consts.js';
5
- import { jobEntrypointAliases } from './helper.js';
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
- console.error('Job feed apply error:', e);
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":"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"}
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 { StateAlias } from '@owlmeans/state';
2
- import type { JobRecord } from '@owlmeans/queue';
3
- import type { Config, Context, JobEntrypointAliases } from './types.js';
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 a job id is the
8
- * broker's, and sharing an id space with the app's own records is how a completed job overwrites
9
- * an unrelated row. Idempotent, like every `append*` — an app that calls it twice keeps whatever
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<JobRecord>) => T;
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
@@ -1 +1 @@
1
- {"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAEvE;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,WACtD,CAAC,UAAS,UAAU,CAAC,SAAS,CAAC,KACvC,CAA8D,CAAA;AAEjE;;GAEG;AACH,eAAO,MAAM,oBAAoB,UACzB,MAAM,KACX,oBAMD,CAAA"}
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 { DEFAULT_JOB_ROOT, JOBS } from './consts.js';
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 a job id is the
7
- * broker's, and sharing an id space with the app's own records is how a completed job overwrites
8
- * an unrelated row. Idempotent, like every `append*` — an app that calls it twice keeps whatever
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
@@ -1 +1 @@
1
- {"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAGrD,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAGpD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CACxB,OAAU,EAAE,KAAK,GAA0B,IAAI,EAC5C,EAAE,CAAC,mBAAmB,CAAkB,OAAO,EAAE,KAAK,CAAM,CAAA;AAEjE;;GAEG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,IAAI,GAAW,gBAAgB,EACT,EAAE,CAAC,CAAC;IAC1B,IAAI,EAAE,IAAI;IACV,IAAI,EAAE,GAAG,IAAI,OAAO;IACpB,GAAG,EAAE,GAAG,IAAI,MAAM;IAClB,MAAM,EAAE,GAAG,IAAI,SAAS;IACxB,KAAK,EAAE,GAAG,IAAI,QAAQ;CACvB,CAAC,CAAA"}
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 { JobRecord } from '@owlmeans/queue';
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: <D = unknown, R = unknown>(id?: string, resource?: string) => StateModel<JobRecord<D, R>>;
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: `{ state: JobState.Active }`, `{ name: 'report:build' }`, `{ 'data.target': id }`.
15
+ * same thing here: `{ status: JobViewStatus.Running }`, `{ kind: 'report-build' }` or an
16
+ * allowlisted metadata field.
16
17
  */
17
- export declare const useJobs: <D = unknown, R = unknown>(filter?: JobFilter<D, R>, opts?: UseJobsOptions<D, R>) => StateModel<JobRecord<D, R>>[];
18
+ export declare const useJobs: (filter?: JobFilter, opts?: UseJobsOptions) => StateModel<JobView>[];
18
19
  //# sourceMappingURL=hooks.d.ts.map
@@ -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,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3D;;;;;GAKG;AACH,eAAO,MAAM,MAAM,GAAI,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,OACxC,MAAM,aAAY,MAAM,KAC5B,UAAU,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAiD,CAAA;AAE9E;;;;;GAKG;AACH,eAAO,MAAM,OAAO,GAAI,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,WACrC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,SAAS,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,KACpD,UAAU,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAI5B,CAAA"}
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: `{ state: JobState.Active }`, `{ name: 'report:build' }`, `{ 'data.target': id }`.
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,
@@ -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,EACP,EAAE,CAAC,aAAa,CAAkB,EAAE,EAAE,QAAQ,CAAC,CAAA;AAE9E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CACrB,MAAwB,EAAE,IAA2B,EACtB,EAAE,CAAC,YAAY,CAAkB;IAChE,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"}
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
@@ -1,4 +1,5 @@
1
1
  export type * from './types.js';
2
+ export { DEFAULT_JOB_ROOT, JOB_EVENT, jobEntrypointAliases } from '@owlmeans/job';
2
3
  export * from './consts.js';
3
4
  export * from './helper.js';
4
5
  export * from './hooks.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,YAAY,CAAA;AAE/B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,kBAAkB,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
@@ -1,3 +1,4 @@
1
+ export { DEFAULT_JOB_ROOT, JOB_EVENT, jobEntrypointAliases } from '@owlmeans/job';
1
2
  export * from './consts.js';
2
3
  export * from './helper.js';
3
4
  export * from './hooks.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,kBAAkB,CAAA"}
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 { JobRecord } from '@owlmeans/queue';
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
- /** The alias every entrypoint of one job group answers under. */
11
- export interface JobEntrypointAliases {
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<JobRecord>;
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<JobRecord>;
27
- /** What the seeding list call asks for — `state`, `name`, `page`, `size`. */
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<D = unknown, R = unknown> = Criteria<JobRecord<D, R>>;
32
+ export type JobFilter = Criteria<JobView>;
42
33
  //# sourceMappingURL=types.d.ts.map
@@ -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,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAChD,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,iEAAiE;AACjE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO;IACtD,IAAI,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IAC9B,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,CAAA;CACjC;AAED,MAAM,WAAW,cAAc;IAC7B,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,CAAA;IAChC,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,CAAA;CACpD;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,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,IAAI,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA"}
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"}
@@ -1,15 +1,11 @@
1
- import type { JobEvent, JobRecord } from '@owlmeans/queue';
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
- * The frame is merged over whatever the store holds rather than saved as itself: an event carries
7
- * only what changed, and `save` replaces the record. An event for an id the store never saw still
8
- * writes a row — a job enqueued in another tab reports its progress here too, and the next seeding
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<JobRecord>, event: JobEvent) => Promise<void>;
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,QAAQ,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEpD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,UACjB,aAAa,CAAC,SAAS,CAAC,SAAS,QAAQ,KAC/C,OAAO,CAAC,IAAI,CAqBd,CAAA"}
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"}
@@ -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
- * The frame is merged over whatever the store holds rather than saved as itself: an event carries
6
- * only what changed, and `save` replaces the record. An event for an id the store never saw still
7
- * writes a row — a job enqueued in another tab reports its progress here too, and the next seeding
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
- const known = await store.load(event.id);
15
- const change = event.type === JobEventType.Completed
16
- ? { state: JobState.Completed, result: event.result }
17
- : event.type === JobEventType.Failed
18
- ? { state: JobState.Failed, error: event.error }
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
@@ -1 +1 @@
1
- {"version":3,"file":"event.js","sourceRoot":"","sources":["../../src/utils/event.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAGnE;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,EAChC,KAA+B,EAAE,KAAe,EACjC,EAAE;IACjB,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAExC,MAAM,MAAM,GAAuB,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC,SAAS;QACtE,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE;QACrD,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC,MAAM;YAClC,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE;YAChD,CAAC,CAAC;gBACA,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,GAAG,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;aAC/D,CAAA;IAEL,MAAM,KAAK,CAAC,IAAI,CAAC;QACf,GAAG,CAAC,KAAK,IAAI,EAAE,IAAI,EAAE,SAAoB,EAAE,CAAC;QAC5C,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,wFAAwF;QACxF,yDAAyD;QACzD,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClD,GAAG,MAAM;KACV,CAAC,CAAA;AACJ,CAAC,CAAA"}
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-rc.9",
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": "workspace:*",
28
- "@types/react": "^19.2.17",
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-rc.24",
34
- "@owlmeans/client-auth": "^0.1.18-rc.29",
35
- "@owlmeans/client-context": "^0.1.18-rc.21",
36
- "@owlmeans/client-entrypoint": "^0.1.18-rc.21",
37
- "@owlmeans/context": "^0.1.18-rc.16",
38
- "@owlmeans/queue": "^0.1.18-rc.17",
39
- "@owlmeans/resource": "^0.1.18-rc.17",
40
- "@owlmeans/socket": "^0.1.18-rc.17",
41
- "@owlmeans/state": "^0.1.18-rc.18"
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 { JobRecord } from '@owlmeans/queue'
2
- import { stateAlias } from '@owlmeans/state'
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<JobRecord> = stateAlias<JobRecord>('job-state')
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 type { JobEvent, JobRecord } from '@owlmeans/queue'
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 { DEFAULT_JOB_ROOT, JOBS, JOB_EVENT } from './consts.js'
8
- import { jobEntrypointAliases } from './helper.js'
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<JobRecord>>>(aliases.list)
41
- .call<ListResult<JobRecord>>({ query: opts?.query })
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<JobEvent>(JOB_EVENT, async message => {
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
- console.error('Job feed apply error:', e)
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 { StateAlias } from '@owlmeans/state'
3
- import type { JobRecord } from '@owlmeans/queue'
4
- import { DEFAULT_JOB_ROOT, JOBS } from './consts.js'
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 a job id is the
11
- * broker's, and sharing an id space with the app's own records is how a completed job overwrites
12
- * an unrelated row. Idempotent, like every `append*` — an app that calls it twice keeps whatever
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<JobRecord> = JOBS
17
- ): T => appendStateResource<C, T, JobRecord>(context, alias) as 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 { JobRecord } from '@owlmeans/queue'
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 = <D = unknown, R = unknown>(
13
+ export const useJob = (
14
14
  id?: string, resource: string = JOBS
15
- ): StateModel<JobRecord<D, R>> => useStoreModel<JobRecord<D, R>>(id, resource)
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: `{ state: JobState.Active }`, `{ name: 'report:build' }`, `{ 'data.target': id }`.
21
+ * same thing here: `{ status: JobViewStatus.Running }`, `{ kind: 'report-build' }` or an
22
+ * allowlisted metadata field.
22
23
  */
23
- export const useJobs = <D = unknown, R = unknown>(
24
- filter?: JobFilter<D, R>, opts?: UseJobsOptions<D, R>
25
- ): StateModel<JobRecord<D, R>>[] => useStoreList<JobRecord<D, R>>({
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
@@ -1,4 +1,5 @@
1
1
  export type * from './types.js'
2
+ export { DEFAULT_JOB_ROOT, JOB_EVENT, jobEntrypointAliases } from '@owlmeans/job'
2
3
 
3
4
  export * from './consts.js'
4
5
  export * from './helper.js'
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 { JobRecord } from '@owlmeans/queue'
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
- /** The alias every entrypoint of one job group answers under. */
12
- export interface JobEntrypointAliases {
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<JobRecord>
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<JobRecord>
30
- /** What the seeding list call asks for — `state`, `name`, `page`, `size`. */
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<D = unknown, R = unknown> = Criteria<JobRecord<D, R>>
36
+ export type JobFilter = Criteria<JobView>
@@ -1,39 +1,19 @@
1
- import type { JobEvent, JobRecord } from '@owlmeans/queue'
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
- * The frame is merged over whatever the store holds rather than saved as itself: an event carries
9
- * only what changed, and `save` replaces the record. An event for an id the store never saw still
10
- * writes a row — a job enqueued in another tab reports its progress here too, and the next seeding
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<JobRecord>, event: JobEvent
12
+ store: StateResource<JobView>, event: JobViewEvent
18
13
  ): Promise<void> => {
19
- const known = await store.load(event.id)
20
-
21
- const change: Partial<JobRecord> = event.type === JobEventType.Completed
22
- ? { state: JobState.Completed, result: event.result }
23
- : event.type === JobEventType.Failed
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
  }