@shivaedev/jobs 0.36.0 → 0.37.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.37.1 - 2026-11-05
4
+
5
+ ### Changed
6
+
7
+ - Update `@shivaedev/sql` from 0.40.0 to 0.41.0.
8
+
9
+ ## 0.37.0 - 2026-11-04
10
+
11
+ ### Breaking
12
+
13
+ - Requires the catalog-compatible `@effect/sql-pg` peer to compile shared keyset fragments through the existing pg-boss connection. Consumers must install `@effect/sql-pg` alongside `@shivaedev/jobs`, `pg-boss` and `effect`.
14
+
3
15
  ## 0.36.0 - 2026-11-04
4
16
 
5
17
  ### Added
package/README.md CHANGED
@@ -595,10 +595,10 @@ Worker registration always requests `includeMetadata: true`, including when `wor
595
595
  ### Install, setup and limits
596
596
 
597
597
  ```sh
598
- pnpm add @shivaedev/jobs effect
598
+ pnpm add @shivaedev/jobs @effect/sql-pg effect
599
599
  ```
600
600
 
601
- The package requires Node.js 24 or later and PostgreSQL. `effect` and `pg-boss` are peers. pnpm installs `pg-boss` as the jobs package’s peer; apps do not list `pg-boss` themselves.
601
+ The package requires Node.js 24 or later and PostgreSQL. `effect`, `pg-boss` and `@effect/sql-pg` are required peers. The PostgreSQL compiler builds paged listing queries for the existing pg-boss connection; it opens no additional pool. pnpm installs `pg-boss` as the jobs package’s peer; apps do not list `pg-boss` themselves.
602
602
 
603
603
  `connectionString` configures pg-boss's own `pg` Pool, separately from the application's Effect SQL pool, and pg-boss's driver decides how its `sslmode` behaves. Set the TLS mode in that URL; the [framework database connection security guide](https://github.com/ShivaeDev/platform/blob/main/docs/framework/quickstart.md#database-connection-security) compares the drivers and the application's connection paths.
604
604
 
@@ -1 +1 @@
1
- {"version":3,"file":"listJobs.d.ts","sourceRoot":"","sources":["../../../src/server/adapters/listJobs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAU,MAAM,QAAQ,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAG5D,wBAAgB,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC,UAAU,CAAC,CAmClH"}
1
+ {"version":3,"file":"listJobs.d.ts","sourceRoot":"","sources":["../../../src/server/adapters/listJobs.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,OAAO,EAAU,MAAM,QAAQ,CAAC;AAE9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAK5D,wBAAgB,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC,UAAU,CAAC,CAuClH"}
@@ -1,16 +1,20 @@
1
+ import { makeCompiler } from "@effect/sql-pg/PgClient";
1
2
  import { Effect } from "effect";
3
+ import * as Statement from "effect/sql/Statement";
2
4
  import { toPgBossError } from "#server/error.ts";
3
5
  import { deadLetterQueueName } from "#server/health.ts";
4
6
  import { quotedSchema } from "#server/internal/storedRows.ts";
7
+ const compiler = makeCompiler();
5
8
  export function listJobs(client, schema) {
6
- return (listing, queue, after, limit) => Effect.tryPromise({
9
+ return (listing, queue, { limit, orderBy, seek }) => Effect.tryPromise({
7
10
  catch: (error) => toPgBossError(listing, error, queue),
8
11
  try: async () => {
9
12
  if (client.getDb === undefined) {
10
13
  throw new TypeError("This pg-boss client does not support getDb");
11
14
  }
12
15
  const quoted = quotedSchema(schema);
13
- const { rows: storedRows } = await client.getDb().executeSql(`SELECT
16
+ const query = Statement.fragment([
17
+ Statement.literal(`SELECT
14
18
  id,
15
19
  name AS queue,
16
20
  state,
@@ -25,11 +29,17 @@ export function listJobs(client, schema) {
25
29
  source_output AS "sourceOutput",
26
30
  to_json(created_on) #>> '{}' AS "cursorAt"
27
31
  FROM ${quoted}.job
28
- WHERE name = $1
29
- ${listing === "failedJobs" ? "AND state = 'failed'" : ""}
30
- AND ($2::timestamptz IS NULL OR (created_on, id) < ($2::timestamptz, $3::uuid))
31
- ORDER BY created_on DESC, id DESC
32
- LIMIT $4`, [listing === "failedJobs" ? queue : deadLetterQueueName(queue), after?.[0] ?? null, after?.[1] ?? null, limit + 1]);
32
+ WHERE name = `),
33
+ Statement.parameter(listing === "failedJobs" ? queue : deadLetterQueueName(queue)),
34
+ Statement.literal(listing === "failedJobs" ? " AND state = 'failed' AND " : " AND "),
35
+ ...seek.segments,
36
+ Statement.literal(" ORDER BY "),
37
+ ...orderBy.segments,
38
+ Statement.literal(" LIMIT "),
39
+ Statement.parameter(limit),
40
+ ]);
41
+ const [text, parameters] = compiler.compile(query, false);
42
+ const { rows: storedRows } = await client.getDb().executeSql(text, [...parameters]);
33
43
  return storedRows;
34
44
  },
35
45
  });
@@ -1 +1 @@
1
- {"version":3,"file":"listJobs.js","sourceRoot":"","sources":["../../../src/server/adapters/listJobs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAE9D,MAAM,UAAU,QAAQ,CAAC,MAAoB,EAAE,MAAc;IAC5D,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CACvC,MAAM,CAAC,UAAU,CAAC;QACjB,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC;QACtD,GAAG,EAAE,KAAK,IAAI,EAAE;YACf,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBAChC,MAAM,IAAI,SAAS,CAAC,4CAA4C,CAAC,CAAC;YACnE,CAAC;YACD,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;YACpC,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC,UAAU,CAC3D;;;;;;;;;;;;;;WAcM,MAAM;;OAEV,OAAO,KAAK,YAAY,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE;;;aAGhD,EACR,CAAC,OAAO,KAAK,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAClH,CAAC;YACF,OAAO,UAAU,CAAC;QACnB,CAAC;KACD,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"listJobs.js","sourceRoot":"","sources":["../../../src/server/adapters/listJobs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAgB,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAC;AAElD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAE9D,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;AAEhC,MAAM,UAAU,QAAQ,CAAC,MAAoB,EAAE,MAAc;IAC5D,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CACnD,MAAM,CAAC,UAAU,CAAC;QACjB,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC;QACtD,GAAG,EAAE,KAAK,IAAI,EAAE;YACf,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBAChC,MAAM,IAAI,SAAS,CAAC,4CAA4C,CAAC,CAAC;YACnE,CAAC;YACD,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;YACpC,MAAM,KAAK,GAAG,SAAS,CAAC,QAAQ,CAAC;gBAChC,SAAS,CAAC,OAAO,CAAC;;;;;;;;;;;;;;WAcZ,MAAM;kBACC,CAAC;gBACd,SAAS,CAAC,SAAS,CAAC,OAAO,KAAK,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;gBAClF,SAAS,CAAC,OAAO,CAAC,OAAO,KAAK,YAAY,CAAC,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC,OAAO,CAAC;gBACpF,GAAG,IAAI,CAAC,QAAQ;gBAChB,SAAS,CAAC,OAAO,CAAC,YAAY,CAAC;gBAC/B,GAAG,OAAO,CAAC,QAAQ;gBACnB,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC;gBAC5B,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC;aAC1B,CAAC,CAAC;YACH,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAC1D,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;YACpF,OAAO,UAAU,CAAC;QACnB,CAAC;KACD,CAAC,CAAC;AACL,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { Context, type Effect } from "effect";
2
+ import type { KeysetPageQuery } from "@shivaedev/sql/keysetPage.ts";
2
3
  import type { PgBossClient } from "#server/client.ts";
3
4
  import type { PgBossError } from "#server/error.ts";
4
5
  import type { PgBossService } from "#server/service.ts";
@@ -8,7 +9,7 @@ export type Listing = "deadLetteredJobs" | "failedJobs";
8
9
  declare const JobEngine_base: Context.ServiceClass<JobEngine, "@shivaedev/jobs/JobEngine", Pick<PgBossService, "deleteQueue" | "refreshQueueStats" | "unschedule"> & {
9
10
  readonly getJob: (queue: string, id: string) => Effect.Effect<StoredJob | undefined, PgBossError>;
10
11
  readonly retry: (queue: string, id: string) => Effect.Effect<void, PgBossError>;
11
- readonly listJobs: (listing: Listing, queue: string, after: readonly [string, string] | undefined, limit: number) => Effect.Effect<readonly unknown[], PgBossError>;
12
+ readonly listJobs: (listing: Listing, queue: string, query: KeysetPageQuery) => Effect.Effect<readonly unknown[], PgBossError>;
12
13
  readonly settleDeferral: (job: JobAttempt, at: string, reason: string, maxDeferrals: number) => Effect.Effect<{
13
14
  readonly claimed: boolean;
14
15
  readonly deferred: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/server/internal/engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAC3F,MAAM,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,YAAY,CAAC,CAAC;AACvE,MAAM,MAAM,OAAO,GAAG,kBAAkB,GAAG,YAAY,CAAC;;qBAKrC,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,SAAS,GAAG,SAAS,EAAE,WAAW,CAAC;oBACjF,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC;uBAC5D,CAClB,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EAC5C,KAAK,EAAE,MAAM,KACT,MAAM,CAAC,MAAM,CAAC,SAAS,OAAO,EAAE,EAAE,WAAW,CAAC;6BAC1B,CACxB,GAAG,EAAE,UAAU,EACf,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,KAChB,MAAM,CAAC,MAAM,CAAC;QAAE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,EAAE,WAAW,CAAC;;AAhB5F,qBAAa,SAAU,SAAQ,cAkBC;CAAG"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/server/internal/engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAC3F,MAAM,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,YAAY,CAAC,CAAC;AACvE,MAAM,MAAM,OAAO,GAAG,kBAAkB,GAAG,YAAY,CAAC;;qBAKrC,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,SAAS,GAAG,SAAS,EAAE,WAAW,CAAC;oBACjF,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC;uBAC5D,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,SAAS,OAAO,EAAE,EAAE,WAAW,CAAC;6BACrG,CACxB,GAAG,EAAE,UAAU,EACf,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,KAChB,MAAM,CAAC,MAAM,CAAC;QAAE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,EAAE,WAAW,CAAC;;AAX5F,qBAAa,SAAU,SAAQ,cAaC;CAAG"}
@@ -1 +1 @@
1
- {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../src/server/internal/engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAe,MAAM,QAAQ,CAAC;AAS9C,MAAM,OAAO,SAAU,SAAQ,OAAO,CAAC,OAAO,EAkB3C,CAAC,2BAA2B,CAAC;CAAG"}
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../src/server/internal/engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAe,MAAM,QAAQ,CAAC;AAU9C,MAAM,OAAO,SAAU,SAAQ,OAAO,CAAC,OAAO,EAa3C,CAAC,2BAA2B,CAAC;CAAG"}
@@ -1 +1 @@
1
- {"version":3,"file":"jobListings.d.ts","sourceRoot":"","sources":["../../../src/server/internal/jobListings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,MAAM,EAAoB,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,KAAK,WAAW,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAClF,OAAO,EAAE,SAAS,EAAgB,MAAM,4BAA4B,CAAC;AAsCrE,eAAO,MAAM,WAAW;;;;;;;;;oBAWtB,CAAC"}
1
+ {"version":3,"file":"jobListings.d.ts","sourceRoot":"","sources":["../../../src/server/internal/jobListings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,MAAM,EAAoB,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,KAAK,WAAW,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAClF,OAAO,EAAE,SAAS,EAAgB,MAAM,4BAA4B,CAAC;AAsCrE,eAAO,MAAM,WAAW;;;;;;;;;oBAWtB,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { Effect, Option, Redacted, Schema } from "effect";
2
2
  import { redact } from "@shivaedev/errors/redact.ts";
3
+ import { keysetPage } from "@shivaedev/sql/keysetPage.ts";
3
4
  import { PgBossPayloadError } from "#server/error.ts";
4
5
  import { JobEngine } from "#server/internal/engine.ts";
5
6
  import { decodeStoredData } from "#server/internal/storedRows.ts";
@@ -36,17 +37,22 @@ export const jobListings = Effect.gen(function* () {
36
37
  });
37
38
  const listJobs = Effect.fnUntraced(function* (engine, listing, queue, options) {
38
39
  const { cursor: after, limit } = yield* Schema.decodeUnknownEffect(PageOptions)(options);
39
- const rows = yield* engine.listJobs(listing, queue.name, after, limit);
40
- const decodedRows = yield* Schema.decodeUnknownEffect(Rows)(rows).pipe(Effect.mapError((error) => new PgBossPayloadError({ direction: "decode", original: Redacted.make(error), queue: queue.name })));
41
- const page = decodedRows.slice(0, limit);
42
- const last = page.at(-1);
43
- const next = decodedRows.length > limit && last !== undefined ? Option.some(Schema.encodeSync(Cursor)([last.cursorAt, last.id])) : Option.none();
44
- const jobs = yield* Effect.forEach(page, Effect.fnUntraced(function* ({ cursorAt: _, data, ...row }) {
40
+ const page = yield* keysetPage({
41
+ after,
42
+ cursorFor: (row) => [row.cursorAt, row.id],
43
+ limit,
44
+ orderBy: [{ column: "created_on", direction: "desc", value: (cursor) => cursor[0] }],
45
+ read: (query) => engine
46
+ .listJobs(listing, queue.name, query)
47
+ .pipe(Effect.flatMap((rows) => Schema.decodeUnknownEffect(Rows)(rows).pipe(Effect.mapError((error) => new PgBossPayloadError({ direction: "decode", original: Redacted.make(error), queue: queue.name }))))),
48
+ tieBreaker: { column: "id", direction: "desc", value: (cursor) => cursor[1] },
49
+ });
50
+ const jobs = yield* Effect.forEach(page.rows, Effect.fnUntraced(function* ({ cursorAt: _, data, ...row }) {
45
51
  const decoded = yield* decodeStoredData(queue, data);
46
52
  const output = yield* readJobFailure(queue.name, row.output);
47
53
  const sourceOutput = yield* readJobFailure(queue.name, row.sourceOutput);
48
54
  return { ...row, attempt: Option.isSome(row.startedOn) ? row.retryCount + 1 : 0, data: redact(decoded), output, sourceOutput };
49
55
  }));
50
- return { jobs, next };
56
+ return { jobs, next: Option.map(page.next, Schema.encodeSync(Cursor)) };
51
57
  });
52
58
  //# sourceMappingURL=jobListings.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"jobListings.js","sourceRoot":"","sources":["../../../src/server/internal/jobListings.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"jobListings.js","sourceRoot":"","sources":["../../../src/server/internal/jobListings.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shivaedev/jobs",
3
- "version": "0.36.0",
3
+ "version": "0.37.1",
4
4
  "description": "Effect-native jobs and lifecycle management for pg-boss",
5
5
  "keywords": [
6
6
  "effect"
@@ -45,14 +45,15 @@
45
45
  "dependencies": {
46
46
  "@shivaedev/errors": "0.9.1",
47
47
  "@shivaedev/service": "0.7.1",
48
- "@shivaedev/sql": "0.39.0"
48
+ "@shivaedev/sql": "0.41.0"
49
49
  },
50
50
  "devDependencies": {
51
- "@shivaedev/http": "0.6.1",
52
- "@shivaedev/quality": "0.61.1",
53
- "@shivaedev/test-kits": "0.4.0",
54
- "@shivaedev/test-sql": "0.21.1",
55
- "@shivaedev/test-story": "0.39.1",
51
+ "@effect/sql-pg": "4.0.7",
52
+ "@shivaedev/http": "0.7.0",
53
+ "@shivaedev/quality": "0.63.0",
54
+ "@shivaedev/test-kits": "0.6.0",
55
+ "@shivaedev/test-sql": "0.22.0",
56
+ "@shivaedev/test-story": "0.40.0",
56
57
  "@types/node": "24.19.2",
57
58
  "effect": "4.0.7",
58
59
  "pg-boss": "12.38.0",
@@ -60,6 +61,7 @@
60
61
  "vitest": "5.0.5"
61
62
  },
62
63
  "peerDependencies": {
64
+ "@effect/sql-pg": "4.0.7",
63
65
  "effect": "4.0.7",
64
66
  "pg-boss": "12.38.0"
65
67
  },
@@ -1,12 +1,16 @@
1
+ import { makeCompiler } from "@effect/sql-pg/PgClient";
1
2
  import { type Context, Effect } from "effect";
3
+ import * as Statement from "effect/sql/Statement";
2
4
  import type { PgBossClient } from "#server/client.ts";
3
5
  import { toPgBossError } from "#server/error.ts";
4
6
  import { deadLetterQueueName } from "#server/health.ts";
5
7
  import type { JobEngine } from "#server/internal/engine.ts";
6
8
  import { quotedSchema } from "#server/internal/storedRows.ts";
7
9
 
10
+ const compiler = makeCompiler();
11
+
8
12
  export function listJobs(client: PgBossClient, schema: string): Context.Service.Shape<typeof JobEngine>["listJobs"] {
9
- return (listing, queue, after, limit) =>
13
+ return (listing, queue, { limit, orderBy, seek }) =>
10
14
  Effect.tryPromise({
11
15
  catch: (error) => toPgBossError(listing, error, queue),
12
16
  try: async () => {
@@ -14,8 +18,8 @@ export function listJobs(client: PgBossClient, schema: string): Context.Service.
14
18
  throw new TypeError("This pg-boss client does not support getDb");
15
19
  }
16
20
  const quoted = quotedSchema(schema);
17
- const { rows: storedRows } = await client.getDb().executeSql(
18
- `SELECT
21
+ const query = Statement.fragment([
22
+ Statement.literal(`SELECT
19
23
  id,
20
24
  name AS queue,
21
25
  state,
@@ -30,13 +34,17 @@ export function listJobs(client: PgBossClient, schema: string): Context.Service.
30
34
  source_output AS "sourceOutput",
31
35
  to_json(created_on) #>> '{}' AS "cursorAt"
32
36
  FROM ${quoted}.job
33
- WHERE name = $1
34
- ${listing === "failedJobs" ? "AND state = 'failed'" : ""}
35
- AND ($2::timestamptz IS NULL OR (created_on, id) < ($2::timestamptz, $3::uuid))
36
- ORDER BY created_on DESC, id DESC
37
- LIMIT $4`,
38
- [listing === "failedJobs" ? queue : deadLetterQueueName(queue), after?.[0] ?? null, after?.[1] ?? null, limit + 1],
39
- );
37
+ WHERE name = `),
38
+ Statement.parameter(listing === "failedJobs" ? queue : deadLetterQueueName(queue)),
39
+ Statement.literal(listing === "failedJobs" ? " AND state = 'failed' AND " : " AND "),
40
+ ...seek.segments,
41
+ Statement.literal(" ORDER BY "),
42
+ ...orderBy.segments,
43
+ Statement.literal(" LIMIT "),
44
+ Statement.parameter(limit),
45
+ ]);
46
+ const [text, parameters] = compiler.compile(query, false);
47
+ const { rows: storedRows } = await client.getDb().executeSql(text, [...parameters]);
40
48
  return storedRows;
41
49
  },
42
50
  });
@@ -1,4 +1,5 @@
1
1
  import { Context, type Effect } from "effect";
2
+ import type { KeysetPageQuery } from "@shivaedev/sql/keysetPage.ts";
2
3
  import type { PgBossClient } from "#server/client.ts";
3
4
  import type { PgBossError } from "#server/error.ts";
4
5
  import type { PgBossService } from "#server/service.ts";
@@ -12,12 +13,7 @@ export class JobEngine extends Context.Service<
12
13
  Pick<PgBossService, "deleteQueue" | "refreshQueueStats" | "unschedule"> & {
13
14
  readonly getJob: (queue: string, id: string) => Effect.Effect<StoredJob | undefined, PgBossError>;
14
15
  readonly retry: (queue: string, id: string) => Effect.Effect<void, PgBossError>;
15
- readonly listJobs: (
16
- listing: Listing,
17
- queue: string,
18
- after: readonly [string, string] | undefined,
19
- limit: number,
20
- ) => Effect.Effect<readonly unknown[], PgBossError>;
16
+ readonly listJobs: (listing: Listing, queue: string, query: KeysetPageQuery) => Effect.Effect<readonly unknown[], PgBossError>;
21
17
  readonly settleDeferral: (
22
18
  job: JobAttempt,
23
19
  at: string,
@@ -1,5 +1,6 @@
1
1
  import { type Context, Effect, Option, Redacted, Schema } from "effect";
2
2
  import { redact } from "@shivaedev/errors/redact.ts";
3
+ import { keysetPage } from "@shivaedev/sql/keysetPage.ts";
3
4
  import type { JobPayloadSchema } from "#server/definition.ts";
4
5
  import { type PgBossError, PgBossPayloadError } from "#server/error.ts";
5
6
  import type { DeadLetteredJob, JobPage, JobPageOptions } from "#server/health.ts";
@@ -65,15 +66,25 @@ const listJobs = Effect.fnUntraced(function* <const TName extends string, const
65
66
  TPayload["DecodingServices"]
66
67
  > {
67
68
  const { cursor: after, limit } = yield* Schema.decodeUnknownEffect(PageOptions)(options);
68
- const rows = yield* engine.listJobs(listing, queue.name, after, limit);
69
- const decodedRows = yield* Schema.decodeUnknownEffect(Rows)(rows).pipe(
70
- Effect.mapError((error) => new PgBossPayloadError({ direction: "decode", original: Redacted.make(error), queue: queue.name })),
71
- );
72
- const page = decodedRows.slice(0, limit);
73
- const last = page.at(-1);
74
- const next = decodedRows.length > limit && last !== undefined ? Option.some(Schema.encodeSync(Cursor)([last.cursorAt, last.id])) : Option.none();
69
+ const page = yield* keysetPage({
70
+ after,
71
+ cursorFor: (row: typeof Row.Type) => [row.cursorAt, row.id] as const,
72
+ limit,
73
+ orderBy: [{ column: "created_on", direction: "desc", value: (cursor) => cursor[0] }],
74
+ read: (query) =>
75
+ engine
76
+ .listJobs(listing, queue.name, query)
77
+ .pipe(
78
+ Effect.flatMap((rows) =>
79
+ Schema.decodeUnknownEffect(Rows)(rows).pipe(
80
+ Effect.mapError((error) => new PgBossPayloadError({ direction: "decode", original: Redacted.make(error), queue: queue.name })),
81
+ ),
82
+ ),
83
+ ),
84
+ tieBreaker: { column: "id", direction: "desc", value: (cursor) => cursor[1] },
85
+ });
75
86
  const jobs = yield* Effect.forEach(
76
- page,
87
+ page.rows,
77
88
  Effect.fnUntraced(function* ({ cursorAt: _, data, ...row }) {
78
89
  const decoded = yield* decodeStoredData(queue, data);
79
90
  const output = yield* readJobFailure(queue.name, row.output);
@@ -81,5 +92,5 @@ const listJobs = Effect.fnUntraced(function* <const TName extends string, const
81
92
  return { ...row, attempt: Option.isSome(row.startedOn) ? row.retryCount + 1 : 0, data: redact(decoded), output, sourceOutput };
82
93
  }),
83
94
  );
84
- return { jobs, next };
95
+ return { jobs, next: Option.map(page.next, Schema.encodeSync(Cursor)) };
85
96
  });