@softure-ai/jobs 0.1.3 → 0.1.4
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 +17 -0
- package/README.md +65 -7
- package/dist/contract.d.ts +1 -1
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/messages/en.d.ts +2 -0
- package/dist/messages/en.d.ts.map +1 -1
- package/dist/messages/en.js +2 -0
- package/dist/messages/en.js.map +1 -1
- package/dist/messages/index.d.ts +2 -0
- package/dist/messages/index.d.ts.map +1 -1
- package/dist/options.d.ts +3 -0
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +4 -2
- package/dist/options.js.map +1 -1
- package/dist/schema.d.ts +253 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +19 -1
- package/dist/schema.js.map +1 -1
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/sync-cursors.d.ts +23 -0
- package/dist/server/sync-cursors.d.ts.map +1 -0
- package/dist/server/sync-cursors.js +53 -0
- package/dist/server/sync-cursors.js.map +1 -0
- package/dist/server/sync-items.d.ts +45 -0
- package/dist/server/sync-items.d.ts.map +1 -0
- package/dist/server/sync-items.js +195 -0
- package/dist/server/sync-items.js.map +1 -0
- package/dist/sync.d.ts +65 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +10 -0
- package/dist/sync.js.map +1 -0
- package/dist/testing/index.d.ts +9 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +8 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/worker/run-job-worker.d.ts.map +1 -1
- package/dist/worker/run-job-worker.js +9 -0
- package/dist/worker/run-job-worker.js.map +1 -1
- package/migrations/0004_create_sync_state.sql +41 -0
- package/module.json +2 -2
- package/package.json +1 -1
- package/src/contract.ts +3 -1
- package/src/index.ts +14 -3
- package/src/messages/en.ts +2 -0
- package/src/options.ts +4 -2
- package/src/schema.ts +27 -1
- package/src/server/index.ts +9 -0
- package/src/server/sync-cursors.ts +67 -0
- package/src/server/sync-items.ts +236 -0
- package/src/sync.ts +59 -0
- package/src/testing/index.ts +18 -2
- package/src/worker/run-job-worker.ts +7 -0
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,UAAU,gDAAmB,CAAC;AAE3C,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgBf,CAAC;AAEH,MAAM,MAAM,MAAM,GAAG,OAAO,IAAI,CAAC,YAAY,CAAC;AAE9C,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAQpB,CAAC"}
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,UAAU,gDAAmB,CAAC;AAE3C,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgBf,CAAC;AAEH,MAAM,MAAM,MAAM,GAAG,OAAO,IAAI,CAAC,YAAY,CAAC;AAE9C,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAQpB,CAAC;AAEH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAKtB,CAAC;AAEH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAerB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,OAAO,SAAS,CAAC,YAAY,CAAC"}
|
package/dist/schema.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Drizzle view of the module's tables (migrations/). The migrations are the source of truth; this
|
|
2
2
|
// file only types the queries.
|
|
3
|
-
import { bigint, integer, jsonb, pgSchema, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
|
3
|
+
import { bigint, integer, jsonb, pgSchema, primaryKey, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
|
4
4
|
export const jobsSchema = pgSchema("jobs");
|
|
5
5
|
export const jobs = jobsSchema.table("jobs", {
|
|
6
6
|
id: bigint("id", { mode: "bigint" }).primaryKey().generatedAlwaysAsIdentity(),
|
|
@@ -28,4 +28,22 @@ export const schedules = jobsSchema.table("schedules", {
|
|
|
28
28
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull(),
|
|
29
29
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull(),
|
|
30
30
|
});
|
|
31
|
+
export const syncCursors = jobsSchema.table("sync_cursors", {
|
|
32
|
+
source: text("source").primaryKey(),
|
|
33
|
+
cursor: text("cursor").notNull(),
|
|
34
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull(),
|
|
35
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull(),
|
|
36
|
+
});
|
|
37
|
+
export const syncItems = jobsSchema.table("sync_items", {
|
|
38
|
+
source: text("source").notNull(),
|
|
39
|
+
itemId: text("item_id").notNull(),
|
|
40
|
+
status: text("status", { enum: ["pending", "done", "skipped", "failed"] }).notNull(),
|
|
41
|
+
errorCode: text("error_code"),
|
|
42
|
+
attempts: integer("attempts").notNull(),
|
|
43
|
+
resultRef: text("result_ref"),
|
|
44
|
+
firstSeenAt: timestamp("first_seen_at", { withTimezone: true }).notNull(),
|
|
45
|
+
lastSeenAt: timestamp("last_seen_at", { withTimezone: true }).notNull(),
|
|
46
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull(),
|
|
47
|
+
finishedAt: timestamp("finished_at", { withTimezone: true }),
|
|
48
|
+
}, (table) => [primaryKey({ columns: [table.source, table.itemId] })]);
|
|
31
49
|
//# sourceMappingURL=schema.js.map
|
package/dist/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,+BAA+B;AAC/B,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -7,5 +7,7 @@ export { getQueueStats, pruneJobs, type QueueStats } from "./maintenance.js";
|
|
|
7
7
|
export { getJobsOptions } from "./options.js";
|
|
8
8
|
export { runClaimedJob, type RunClaimedJobOptions } from "./process.js";
|
|
9
9
|
export { enqueueDueSchedules, getScheduleSlot, SCHEDULE_DEDUP_KEY, type EnqueueDueSchedulesInput, type ScheduleState, } from "./schedules.js";
|
|
10
|
+
export { advanceSyncCursor, getSyncCursor, SYNC_SOURCE, type AdvanceSyncCursorInput } from "./sync-cursors.js";
|
|
11
|
+
export { listSyncItems, processSyncItem, pruneSyncItems, type ListSyncItemsInput, type ProcessSyncItemInput, type SyncItemContext, } from "./sync-items.js";
|
|
10
12
|
export { runDueJobs, type RunDueJobsOptions, type RunDueJobsReport } from "./run-due.js";
|
|
11
13
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AACpG,OAAO,EACL,SAAS,EACT,WAAW,EACX,cAAc,EACd,UAAU,EACV,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,UAAU,GAChB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAClF,OAAO,EAAE,iBAAiB,EAAE,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACxE,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,kBAAkB,EAClB,KAAK,wBAAwB,EAC7B,KAAK,aAAa,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AACpG,OAAO,EACL,SAAS,EACT,WAAW,EACX,cAAc,EACd,UAAU,EACV,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,UAAU,GAChB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAClF,OAAO,EAAE,iBAAiB,EAAE,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACxE,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,kBAAkB,EAClB,KAAK,wBAAwB,EAC7B,KAAK,aAAa,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,WAAW,EAAE,KAAK,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC/G,OAAO,EACL,aAAa,EACb,eAAe,EACf,cAAc,EACd,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,eAAe,GACrB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/server/index.js
CHANGED
|
@@ -8,5 +8,7 @@ export { getQueueStats, pruneJobs } from "./maintenance.js";
|
|
|
8
8
|
export { getJobsOptions } from "./options.js";
|
|
9
9
|
export { runClaimedJob } from "./process.js";
|
|
10
10
|
export { enqueueDueSchedules, getScheduleSlot, SCHEDULE_DEDUP_KEY, } from "./schedules.js";
|
|
11
|
+
export { advanceSyncCursor, getSyncCursor, SYNC_SOURCE } from "./sync-cursors.js";
|
|
12
|
+
export { listSyncItems, processSyncItem, pruneSyncItems, } from "./sync-items.js";
|
|
11
13
|
export { runDueJobs } from "./run-due.js";
|
|
12
14
|
//# sourceMappingURL=index.js.map
|
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,kHAAkH;AAClH,8BAA8B;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAA6C,MAAM,YAAY,CAAC;AACpG,OAAO,EACL,SAAS,EACT,WAAW,EACX,cAAc,EACd,UAAU,GAIX,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,UAAU,EAA0C,MAAM,cAAc,CAAC;AAClF,OAAO,EAAE,iBAAiB,EAAuC,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,aAAa,EAAE,SAAS,EAAmB,MAAM,kBAAkB,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,aAAa,EAA6B,MAAM,cAAc,CAAC;AACxE,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,kBAAkB,GAGnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,UAAU,EAAiD,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,kHAAkH;AAClH,8BAA8B;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAA6C,MAAM,YAAY,CAAC;AACpG,OAAO,EACL,SAAS,EACT,WAAW,EACX,cAAc,EACd,UAAU,GAIX,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,UAAU,EAA0C,MAAM,cAAc,CAAC;AAClF,OAAO,EAAE,iBAAiB,EAAuC,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,aAAa,EAAE,SAAS,EAAmB,MAAM,kBAAkB,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,aAAa,EAA6B,MAAM,cAAc,CAAC;AACxE,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,kBAAkB,GAGnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,WAAW,EAA+B,MAAM,mBAAmB,CAAC;AAC/G,OAAO,EACL,aAAa,EACb,eAAe,EACf,cAAc,GAIf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,UAAU,EAAiD,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Result } from "@softure-ai/core";
|
|
2
|
+
import type { JobsContext } from "./context.js";
|
|
3
|
+
/** A source name: lowercase letters, digits, `_ . : -`, starting with a letter, up to 127 characters. */
|
|
4
|
+
export declare const SYNC_SOURCE: RegExp;
|
|
5
|
+
export interface AdvanceSyncCursorInput {
|
|
6
|
+
readonly source: string;
|
|
7
|
+
/** The cursor the batch was read from, as `getSyncCursor` returned it; null for the first batch. */
|
|
8
|
+
readonly from: string | null;
|
|
9
|
+
/** Where the next batch starts. */
|
|
10
|
+
readonly to: string;
|
|
11
|
+
}
|
|
12
|
+
/** The source's cursor, or null before its first `advanceSyncCursor`. Throws on an invalid source name. */
|
|
13
|
+
export declare function getSyncCursor(ctx: JobsContext, source: string): Promise<string | null>;
|
|
14
|
+
/**
|
|
15
|
+
* Moves the source's cursor from `from` to `to`, after the batch read from `from` was handled. Refused with
|
|
16
|
+
* `jobs.sync_cursor_moved` when the stored cursor is no longer `from` (another worker moved it first): re-read from
|
|
17
|
+
* `getSyncCursor`. `ctx.db` may be a transaction, so the move commits with the batch's last writes. Throws on an
|
|
18
|
+
* invalid source name or cursor; database failures propagate.
|
|
19
|
+
*/
|
|
20
|
+
export declare function advanceSyncCursor(ctx: JobsContext, input: AdvanceSyncCursorInput): Promise<Result<undefined, "jobs.sync_cursor_moved">>;
|
|
21
|
+
/** Throws on a name the tables refuse: a typo in code, not a user error. */
|
|
22
|
+
export declare function checkSyncSource(source: string): void;
|
|
23
|
+
//# sourceMappingURL=sync-cursors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-cursors.d.ts","sourceRoot":"","sources":["../../src/server/sync-cursors.ts"],"names":[],"mappings":"AAEA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAGxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,yGAAyG;AACzG,eAAO,MAAM,WAAW,QAA+B,CAAC;AAGxD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,oGAAoG;IACpG,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,mCAAmC;IACnC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB;AAED,2GAA2G;AAC3G,wBAAsB,aAAa,CAAC,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAI5F;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,wBAAwB,CAAC,CAAC,CAqB7I;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAIpD"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// The cursor of a polled source: where its next read starts. It moves only by compare-and-set, so a worker that read
|
|
2
|
+
// an older cursor cannot move it back over a newer one; a crash before the move re-reads the batch instead of skipping it.
|
|
3
|
+
import { err, ok } from "@softure-ai/core";
|
|
4
|
+
import { and, eq } from "drizzle-orm";
|
|
5
|
+
import { syncCursors } from "../schema.js";
|
|
6
|
+
/** A source name: lowercase letters, digits, `_ . : -`, starting with a letter, up to 127 characters. */
|
|
7
|
+
export const SYNC_SOURCE = /^[a-z][a-z0-9_.:-]{0,126}$/;
|
|
8
|
+
const MAX_CURSOR_LENGTH = 2000;
|
|
9
|
+
/** The source's cursor, or null before its first `advanceSyncCursor`. Throws on an invalid source name. */
|
|
10
|
+
export async function getSyncCursor(ctx, source) {
|
|
11
|
+
checkSyncSource(source);
|
|
12
|
+
const [row] = await ctx.db.select({ cursor: syncCursors.cursor }).from(syncCursors).where(eq(syncCursors.source, source));
|
|
13
|
+
return row?.cursor ?? null;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Moves the source's cursor from `from` to `to`, after the batch read from `from` was handled. Refused with
|
|
17
|
+
* `jobs.sync_cursor_moved` when the stored cursor is no longer `from` (another worker moved it first): re-read from
|
|
18
|
+
* `getSyncCursor`. `ctx.db` may be a transaction, so the move commits with the batch's last writes. Throws on an
|
|
19
|
+
* invalid source name or cursor; database failures propagate.
|
|
20
|
+
*/
|
|
21
|
+
export async function advanceSyncCursor(ctx, input) {
|
|
22
|
+
checkSyncSource(input.source);
|
|
23
|
+
checkCursor(input.to);
|
|
24
|
+
if (input.from !== null)
|
|
25
|
+
checkCursor(input.from);
|
|
26
|
+
const now = ctx.clock.now();
|
|
27
|
+
if (input.from === null) {
|
|
28
|
+
const inserted = await ctx.db
|
|
29
|
+
.insert(syncCursors)
|
|
30
|
+
.values({ source: input.source, cursor: input.to, createdAt: now, updatedAt: now })
|
|
31
|
+
.onConflictDoNothing({ target: syncCursors.source })
|
|
32
|
+
.returning({ source: syncCursors.source });
|
|
33
|
+
return inserted.length === 1 ? ok() : err("jobs.sync_cursor_moved");
|
|
34
|
+
}
|
|
35
|
+
const updated = await ctx.db
|
|
36
|
+
.update(syncCursors)
|
|
37
|
+
.set({ cursor: input.to, updatedAt: now })
|
|
38
|
+
.where(and(eq(syncCursors.source, input.source), eq(syncCursors.cursor, input.from)))
|
|
39
|
+
.returning({ source: syncCursors.source });
|
|
40
|
+
return updated.length === 1 ? ok() : err("jobs.sync_cursor_moved");
|
|
41
|
+
}
|
|
42
|
+
/** Throws on a name the tables refuse: a typo in code, not a user error. */
|
|
43
|
+
export function checkSyncSource(source) {
|
|
44
|
+
if (!SYNC_SOURCE.test(source)) {
|
|
45
|
+
throw new RangeError(`@softure-ai/jobs: "${source}" is not a sync source name: lowercase letters, digits, _ . : and -, starting with a letter, up to 127 characters`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function checkCursor(cursor) {
|
|
49
|
+
if (cursor.length === 0 || cursor.length > MAX_CURSOR_LENGTH) {
|
|
50
|
+
throw new RangeError(`@softure-ai/jobs: a sync cursor must have 1 to ${String(MAX_CURSOR_LENGTH)} characters`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=sync-cursors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-cursors.js","sourceRoot":"","sources":["../../src/server/sync-cursors.ts"],"names":[],"mappings":"AAAA,qHAAqH;AACrH,2HAA2H;AAC3H,OAAO,EAAE,GAAG,EAAE,EAAE,EAAe,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG3C,yGAAyG;AACzG,MAAM,CAAC,MAAM,WAAW,GAAG,4BAA4B,CAAC;AACxD,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAU/B,2GAA2G;AAC3G,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAgB,EAAE,MAAc;IAClE,eAAe,CAAC,MAAM,CAAC,CAAC;IACxB,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC1H,OAAO,GAAG,EAAE,MAAM,IAAI,IAAI,CAAC;AAC7B,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,GAAgB,EAAE,KAA6B;IACrF,eAAe,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9B,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACtB,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI;QAAE,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACjD,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;IAE5B,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QACxB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,EAAE;aAC1B,MAAM,CAAC,WAAW,CAAC;aACnB,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;aAClF,mBAAmB,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC;aACnD,SAAS,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7C,OAAO,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,EAAE;SACzB,MAAM,CAAC,WAAW,CAAC;SACnB,GAAG,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;SACzC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;SACpF,SAAS,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7C,OAAO,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;AACrE,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,eAAe,CAAC,MAAc;IAC5C,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,UAAU,CAAC,sBAAsB,MAAM,mHAAmH,CAAC,CAAC;IACxK,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,MAAc;IACjC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,iBAAiB,EAAE,CAAC;QAC7D,MAAM,IAAI,UAAU,CAAC,kDAAkD,MAAM,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC;IACjH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type SyncItemRow } from "../schema.js";
|
|
2
|
+
import type { SyncItem, SyncItemOutcome, SyncItemRunResult, SyncItemStatus } from "../sync.js";
|
|
3
|
+
import type { JobsContext } from "./context.js";
|
|
4
|
+
export interface ProcessSyncItemInput {
|
|
5
|
+
readonly source: string;
|
|
6
|
+
/** The item's id at the source. */
|
|
7
|
+
readonly itemId: string;
|
|
8
|
+
/** Runs of the callback before the item is `failed` for good; the first run counts. Default 5. */
|
|
9
|
+
readonly maxAttempts?: number;
|
|
10
|
+
}
|
|
11
|
+
/** What the callback receives: the module context with the item's transaction as `db`, and this attempt's number. */
|
|
12
|
+
export interface SyncItemContext extends JobsContext {
|
|
13
|
+
/** Starts at 1. */
|
|
14
|
+
readonly attempt: number;
|
|
15
|
+
}
|
|
16
|
+
export interface ListSyncItemsInput {
|
|
17
|
+
readonly source?: string;
|
|
18
|
+
readonly status?: SyncItemStatus;
|
|
19
|
+
/** Only items whose status, attempts or error changed at or after this time. */
|
|
20
|
+
readonly since?: Date;
|
|
21
|
+
/** Default 100, at most 1000. */
|
|
22
|
+
readonly limit?: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Handles one item of a polled source at most once. Runs `run` unless the ledger already has the item `done`,
|
|
26
|
+
* `skipped` or `failed` (then `skipped`, the callback untouched). `run` gets the item's transaction as `context.db`:
|
|
27
|
+
* write the item's effect through it and it commits together with the ledger's `done`. A returned failure rolls those
|
|
28
|
+
* writes back, counts the attempt and leaves the item `pending` for the next run, or `failed` once `maxAttempts` runs
|
|
29
|
+
* failed (or at once with `failSyncItem`). A throw is recorded the same way with `jobs.sync_item_threw` and then
|
|
30
|
+
* rethrown. A call outside the database (an API) is not rolled back: a crash between it and the commit repeats it on
|
|
31
|
+
* the next run, so keep it idempotent where the source allows. Throws on invalid input; database failures propagate.
|
|
32
|
+
*/
|
|
33
|
+
export declare function processSyncItem(ctx: JobsContext, input: ProcessSyncItemInput, run: (context: SyncItemContext) => Promise<SyncItemRunResult>): Promise<SyncItemOutcome>;
|
|
34
|
+
/**
|
|
35
|
+
* The ledger, most recently changed first: for an overview page or an MCP tool. Throws on an invalid filter.
|
|
36
|
+
*/
|
|
37
|
+
export declare function listSyncItems(ctx: JobsContext, input?: ListSyncItemsInput): Promise<SyncItem[]>;
|
|
38
|
+
/**
|
|
39
|
+
* Deletes ledger items that no `processSyncItem` call met for `retention.syncItemsDays`, in batches, whatever their
|
|
40
|
+
* status: the source no longer returns them. Returns the number deleted. The worker calls it once an hour with
|
|
41
|
+
* `pruneJobs`; an app without a worker calls it from the same cron.
|
|
42
|
+
*/
|
|
43
|
+
export declare function pruneSyncItems(ctx: JobsContext): Promise<number>;
|
|
44
|
+
export declare function toSyncItem(row: SyncItemRow): SyncItem;
|
|
45
|
+
//# sourceMappingURL=sync-items.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-items.d.ts","sourceRoot":"","sources":["../../src/server/sync-items.ts"],"names":[],"mappings":"AAKA,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAmB,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAChH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAehD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,mCAAmC;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kGAAkG;IAClG,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,qHAAqH;AACrH,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD,mBAAmB;IACnB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,CAAC;IACjC,gFAAgF;IAChF,QAAQ,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC;IACtB,iCAAiC;IACjC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAWD;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,oBAAoB,EAC3B,GAAG,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,CAAC,iBAAiB,CAAC,GAC5D,OAAO,CAAC,eAAe,CAAC,CAsD1B;AAwBD;;GAEG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,GAAE,kBAAuB,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAoBzG;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAatE;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,WAAW,GAAG,QAAQ,CAarD"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { and, desc, eq, gte, lt, sql } from "drizzle-orm";
|
|
2
|
+
import { syncItems } from "../schema.js";
|
|
3
|
+
import { getJobsOptions } from "./options.js";
|
|
4
|
+
import { checkSyncSource } from "./sync-cursors.js";
|
|
5
|
+
const SYNC_ITEM_THREW = "jobs.sync_item_threw";
|
|
6
|
+
const DEFAULT_MAX_ATTEMPTS = 5;
|
|
7
|
+
const MAX_ITEM_ID_LENGTH = 500;
|
|
8
|
+
const MAX_RESULT_REF_LENGTH = 500;
|
|
9
|
+
const MAX_ERROR_LENGTH = 200;
|
|
10
|
+
const DEFAULT_LIST_LIMIT = 100;
|
|
11
|
+
const MAX_LIST_LIMIT = 1000;
|
|
12
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
13
|
+
/** Rows deleted per statement, so pruning a large ledger never holds one long lock. */
|
|
14
|
+
const PRUNE_BATCH = 1000;
|
|
15
|
+
/** Thrown inside the savepoint to roll back the callback's writes when it returned a failure. */
|
|
16
|
+
class RunFailed extends Error {
|
|
17
|
+
result;
|
|
18
|
+
constructor(result) {
|
|
19
|
+
super("sync item callback failed");
|
|
20
|
+
this.result = result;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Handles one item of a polled source at most once. Runs `run` unless the ledger already has the item `done`,
|
|
25
|
+
* `skipped` or `failed` (then `skipped`, the callback untouched). `run` gets the item's transaction as `context.db`:
|
|
26
|
+
* write the item's effect through it and it commits together with the ledger's `done`. A returned failure rolls those
|
|
27
|
+
* writes back, counts the attempt and leaves the item `pending` for the next run, or `failed` once `maxAttempts` runs
|
|
28
|
+
* failed (or at once with `failSyncItem`). A throw is recorded the same way with `jobs.sync_item_threw` and then
|
|
29
|
+
* rethrown. A call outside the database (an API) is not rolled back: a crash between it and the commit repeats it on
|
|
30
|
+
* the next run, so keep it idempotent where the source allows. Throws on invalid input; database failures propagate.
|
|
31
|
+
*/
|
|
32
|
+
export async function processSyncItem(ctx, input, run) {
|
|
33
|
+
checkSyncSource(input.source);
|
|
34
|
+
const maxAttempts = input.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
|
|
35
|
+
checkItemInput(input.itemId, maxAttempts);
|
|
36
|
+
const now = ctx.clock.now();
|
|
37
|
+
const settled = await ctx.db.transaction(async (tx) => {
|
|
38
|
+
const [seen] = await tx
|
|
39
|
+
.insert(syncItems)
|
|
40
|
+
.values({ source: input.source, itemId: input.itemId, status: "pending", attempts: 0, firstSeenAt: now, lastSeenAt: now, updatedAt: now })
|
|
41
|
+
.onConflictDoUpdate({
|
|
42
|
+
target: [syncItems.source, syncItems.itemId],
|
|
43
|
+
set: { lastSeenAt: sql `greatest(${syncItems.lastSeenAt}, excluded.last_seen_at)` },
|
|
44
|
+
})
|
|
45
|
+
.returning();
|
|
46
|
+
const row = requireRow(seen);
|
|
47
|
+
const where = and(eq(syncItems.source, input.source), eq(syncItems.itemId, input.itemId));
|
|
48
|
+
const finish = async (values) => {
|
|
49
|
+
const [updated] = await tx.update(syncItems).set({ ...values, updatedAt: now }).where(where).returning();
|
|
50
|
+
return toSyncItem(requireRow(updated));
|
|
51
|
+
};
|
|
52
|
+
if (row.status !== "pending")
|
|
53
|
+
return { outcome: { kind: "skipped", item: toSyncItem(row) } };
|
|
54
|
+
if (row.attempts >= maxAttempts) {
|
|
55
|
+
// maxAttempts was lowered below what the item already used: it has had its runs.
|
|
56
|
+
const error = row.errorCode ?? SYNC_ITEM_THREW;
|
|
57
|
+
return { outcome: { kind: "failed", error, item: await finish({ status: "failed", errorCode: error, finishedAt: now }) } };
|
|
58
|
+
}
|
|
59
|
+
const attempt = row.attempts + 1;
|
|
60
|
+
let result;
|
|
61
|
+
let thrown = null;
|
|
62
|
+
try {
|
|
63
|
+
result = await tx.transaction(async (savepoint) => {
|
|
64
|
+
const returned = await run({ ...ctx, db: savepoint, attempt });
|
|
65
|
+
if (!isOk(returned))
|
|
66
|
+
throw new RunFailed(returned);
|
|
67
|
+
checkResultRef(returned);
|
|
68
|
+
return returned;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (error instanceof RunFailed) {
|
|
73
|
+
result = error.result;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
thrown = { error };
|
|
77
|
+
result = { ok: false, error: SYNC_ITEM_THREW };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const outcome = await record(result, { attempt, maxAttempts, now, finish });
|
|
81
|
+
return thrown === null ? { outcome } : { thrown: thrown.error };
|
|
82
|
+
});
|
|
83
|
+
if (settled.outcome === undefined)
|
|
84
|
+
throw settled.thrown;
|
|
85
|
+
return settled.outcome;
|
|
86
|
+
}
|
|
87
|
+
async function record(result, context) {
|
|
88
|
+
const { attempt, now, finish } = context;
|
|
89
|
+
if (isOk(result)) {
|
|
90
|
+
const completion = result.value ?? { status: "done" };
|
|
91
|
+
const item = completion.status === "done"
|
|
92
|
+
? await finish({ status: "done", attempts: attempt, errorCode: null, resultRef: completion.resultRef ?? null, finishedAt: now })
|
|
93
|
+
: await finish({ status: "skipped", attempts: attempt, errorCode: completion.reason === undefined ? null : toErrorCode(completion.reason), finishedAt: now });
|
|
94
|
+
return { kind: "handled", item };
|
|
95
|
+
}
|
|
96
|
+
// A callback that returned neither ok() nor an error (a JavaScript caller) counts as a throw.
|
|
97
|
+
const failure = result;
|
|
98
|
+
const error = toErrorCode(typeof failure?.error === "string" ? failure.error : SYNC_ITEM_THREW);
|
|
99
|
+
if (failure?.retry === false || attempt >= context.maxAttempts) {
|
|
100
|
+
return { kind: "failed", error, item: await finish({ status: "failed", attempts: attempt, errorCode: error, finishedAt: now }) };
|
|
101
|
+
}
|
|
102
|
+
return { kind: "retrying", error, item: await finish({ attempts: attempt, errorCode: error }) };
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The ledger, most recently changed first: for an overview page or an MCP tool. Throws on an invalid filter.
|
|
106
|
+
*/
|
|
107
|
+
export async function listSyncItems(ctx, input = {}) {
|
|
108
|
+
const limit = input.limit ?? DEFAULT_LIST_LIMIT;
|
|
109
|
+
if (!(Number.isInteger(limit) && limit >= 1 && limit <= MAX_LIST_LIMIT)) {
|
|
110
|
+
throw new RangeError(`@softure-ai/jobs: listSyncItems limit must be an integer from 1 to ${String(MAX_LIST_LIMIT)}`);
|
|
111
|
+
}
|
|
112
|
+
if (input.source !== undefined)
|
|
113
|
+
checkSyncSource(input.source);
|
|
114
|
+
if (input.since !== undefined && Number.isNaN(input.since.getTime())) {
|
|
115
|
+
throw new RangeError("@softure-ai/jobs: listSyncItems since is an invalid date");
|
|
116
|
+
}
|
|
117
|
+
const filters = [];
|
|
118
|
+
if (input.source !== undefined)
|
|
119
|
+
filters.push(eq(syncItems.source, input.source));
|
|
120
|
+
if (input.status !== undefined)
|
|
121
|
+
filters.push(eq(syncItems.status, input.status));
|
|
122
|
+
if (input.since !== undefined)
|
|
123
|
+
filters.push(gte(syncItems.updatedAt, input.since));
|
|
124
|
+
const rows = await ctx.db
|
|
125
|
+
.select()
|
|
126
|
+
.from(syncItems)
|
|
127
|
+
.where(and(...filters))
|
|
128
|
+
.orderBy(desc(syncItems.updatedAt), syncItems.source, syncItems.itemId)
|
|
129
|
+
.limit(limit);
|
|
130
|
+
return rows.map(toSyncItem);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Deletes ledger items that no `processSyncItem` call met for `retention.syncItemsDays`, in batches, whatever their
|
|
134
|
+
* status: the source no longer returns them. Returns the number deleted. The worker calls it once an hour with
|
|
135
|
+
* `pruneJobs`; an app without a worker calls it from the same cron.
|
|
136
|
+
*/
|
|
137
|
+
export async function pruneSyncItems(ctx) {
|
|
138
|
+
const { retention } = getJobsOptions(ctx.config);
|
|
139
|
+
const before = new Date(ctx.clock.now().getTime() - retention.syncItemsDays * DAY_MS);
|
|
140
|
+
let deleted = 0;
|
|
141
|
+
for (;;) {
|
|
142
|
+
const batch = ctx.db.select({ source: syncItems.source, itemId: syncItems.itemId }).from(syncItems).where(lt(syncItems.lastSeenAt, before)).limit(PRUNE_BATCH);
|
|
143
|
+
const rows = await ctx.db
|
|
144
|
+
.delete(syncItems)
|
|
145
|
+
.where(sql `(${syncItems.source}, ${syncItems.itemId}) in (${batch})`)
|
|
146
|
+
.returning({ source: syncItems.source });
|
|
147
|
+
deleted += rows.length;
|
|
148
|
+
if (rows.length < PRUNE_BATCH)
|
|
149
|
+
return deleted;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
export function toSyncItem(row) {
|
|
153
|
+
return {
|
|
154
|
+
source: row.source,
|
|
155
|
+
itemId: row.itemId,
|
|
156
|
+
status: row.status,
|
|
157
|
+
errorCode: row.errorCode,
|
|
158
|
+
attempts: row.attempts,
|
|
159
|
+
resultRef: row.resultRef,
|
|
160
|
+
firstSeenAt: row.firstSeenAt,
|
|
161
|
+
lastSeenAt: row.lastSeenAt,
|
|
162
|
+
updatedAt: row.updatedAt,
|
|
163
|
+
finishedAt: row.finishedAt,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function checkItemInput(itemId, maxAttempts) {
|
|
167
|
+
if (itemId.length === 0 || itemId.length > MAX_ITEM_ID_LENGTH) {
|
|
168
|
+
throw new RangeError(`@softure-ai/jobs: a sync item id must have 1 to ${String(MAX_ITEM_ID_LENGTH)} characters`);
|
|
169
|
+
}
|
|
170
|
+
if (!(Number.isInteger(maxAttempts) && maxAttempts >= 1 && maxAttempts <= 1000)) {
|
|
171
|
+
throw new RangeError("@softure-ai/jobs: processSyncItem maxAttempts must be an integer from 1 to 1000");
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/** Inside the savepoint, so a bad reference counts as a thrown attempt and rolls the callback's writes back. */
|
|
175
|
+
function checkResultRef(result) {
|
|
176
|
+
const completion = result.ok ? result.value : undefined;
|
|
177
|
+
if (completion?.status !== "done" || completion.resultRef === undefined)
|
|
178
|
+
return;
|
|
179
|
+
if (completion.resultRef.length === 0 || completion.resultRef.length > MAX_RESULT_REF_LENGTH) {
|
|
180
|
+
throw new RangeError(`@softure-ai/jobs: a sync item resultRef must have 1 to ${String(MAX_RESULT_REF_LENGTH)} characters`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function isOk(result) {
|
|
184
|
+
return typeof result === "object" && result !== null && result.ok === true;
|
|
185
|
+
}
|
|
186
|
+
function toErrorCode(error) {
|
|
187
|
+
const trimmed = error.slice(0, MAX_ERROR_LENGTH);
|
|
188
|
+
return (trimmed.includes(".") ? trimmed : SYNC_ITEM_THREW);
|
|
189
|
+
}
|
|
190
|
+
function requireRow(row) {
|
|
191
|
+
if (row === undefined)
|
|
192
|
+
throw new Error("@softure-ai/jobs: a sync item write returned no row");
|
|
193
|
+
return row;
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=sync-items.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-items.js","sourceRoot":"","sources":["../../src/server/sync-items.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAY,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,SAAS,EAAoB,MAAM,cAAc,CAAC;AAG3D,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,MAAM,eAAe,GAAG,sBAAsB,CAAC;AAC/C,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAC/B,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAC/B,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAClC,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAC7B,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAC/B,MAAM,cAAc,GAAG,IAAI,CAAC;AAC5B,MAAM,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AACnC,uFAAuF;AACvF,MAAM,WAAW,GAAG,IAAI,CAAC;AAyBzB,iGAAiG;AACjG,MAAM,SAAU,SAAQ,KAAK;IACN;IAArB,YAAqB,MAAyB;QAC5C,KAAK,CAAC,2BAA2B,CAAC,CAAC;QADhB,WAAM,GAAN,MAAM,CAAmB;IAE9C,CAAC;CACF;AAID;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,GAAgB,EAChB,KAA2B,EAC3B,GAA6D;IAE7D,eAAe,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9B,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,IAAI,oBAAoB,CAAC;IAC9D,cAAc,CAAC,KAAK,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC1C,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;IAE5B,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,EAAoB,EAAE;QACtE,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,EAAE;aACpB,MAAM,CAAC,SAAS,CAAC;aACjB,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;aACzI,kBAAkB,CAAC;YAClB,MAAM,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC;YAC5C,GAAG,EAAE,EAAE,UAAU,EAAE,GAAG,CAAA,YAAY,SAAS,CAAC,UAAU,0BAA0B,EAAE;SACnF,CAAC;aACD,SAAS,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,KAAK,GAAG,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QAC1F,MAAM,MAAM,GAAG,KAAK,EAAE,MAA4B,EAAqB,EAAE;YACvE,MAAM,CAAC,OAAO,CAAC,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,CAAC;YACzG,OAAO,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;QACzC,CAAC,CAAC;QAEF,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QAC7F,IAAI,GAAG,CAAC,QAAQ,IAAI,WAAW,EAAE,CAAC;YAChC,iFAAiF;YACjF,MAAM,KAAK,GAAG,GAAG,CAAC,SAAS,IAAI,eAAe,CAAC;YAC/C,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7H,CAAC;QAED,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC;QACjC,IAAI,MAAyB,CAAC;QAC9B,IAAI,MAAM,GAAuC,IAAI,CAAC;QACtD,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE;gBAChD,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,EAAE,GAAG,GAAG,EAAE,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;gBAC/D,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;oBAAE,MAAM,IAAI,SAAS,CAAC,QAAQ,CAAC,CAAC;gBACnD,cAAc,CAAC,QAAQ,CAAC,CAAC;gBACzB,OAAO,QAAQ,CAAC;YAClB,CAAC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,SAAS,EAAE,CAAC;gBAC/B,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;YACxB,CAAC;iBAAM,CAAC;gBACN,MAAM,GAAG,EAAE,KAAK,EAAE,CAAC;gBACnB,MAAM,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC;YACjD,CAAC;QACH,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;QAC5E,OAAO,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS;QAAE,MAAM,OAAO,CAAC,MAAM,CAAC;IACxD,OAAO,OAAO,CAAC,OAAO,CAAC;AACzB,CAAC;AAED,KAAK,UAAU,MAAM,CACnB,MAAyB,EACzB,OAAyH;IAEzH,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IACzC,IAAI,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACjB,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QACtD,MAAM,IAAI,GACR,UAAU,CAAC,MAAM,KAAK,MAAM;YAC1B,CAAC,CAAC,MAAM,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,CAAC,SAAS,IAAI,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC;YAChI,CAAC,CAAC,MAAM,MAAM,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;QAClK,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IACnC,CAAC;IACD,8FAA8F;IAC9F,MAAM,OAAO,GAAG,MAAqD,CAAC;IACtE,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,OAAO,EAAE,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;IAChG,IAAI,OAAO,EAAE,KAAK,KAAK,KAAK,IAAI,OAAO,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;QAC/D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;IACnI,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;AAClG,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAgB,EAAE,QAA4B,EAAE;IAClF,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,kBAAkB,CAAC;IAChD,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,cAAc,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,UAAU,CAAC,sEAAsE,MAAM,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;IACvH,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,eAAe,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9D,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QACrE,MAAM,IAAI,UAAU,CAAC,0DAA0D,CAAC,CAAC;IACnF,CAAC;IACD,MAAM,OAAO,GAAU,EAAE,CAAC;IAC1B,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IACjF,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IACjF,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IACnF,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,EAAE;SACtB,MAAM,EAAE;SACR,IAAI,CAAC,SAAS,CAAC;SACf,KAAK,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC;SACtB,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC;SACtE,KAAK,CAAC,KAAK,CAAC,CAAC;IAChB,OAAO,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AAC9B,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAgB;IACnD,MAAM,EAAE,SAAS,EAAE,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,GAAG,SAAS,CAAC,aAAa,GAAG,MAAM,CAAC,CAAC;IACtF,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,SAAS,CAAC;QACR,MAAM,KAAK,GAAG,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC/J,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,EAAE;aACtB,MAAM,CAAC,SAAS,CAAC;aACjB,KAAK,CAAC,GAAG,CAAA,IAAI,SAAS,CAAC,MAAM,KAAK,SAAS,CAAC,MAAM,SAAS,KAAK,GAAG,CAAC;aACpE,SAAS,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;QAC3C,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC;QACvB,IAAI,IAAI,CAAC,MAAM,GAAG,WAAW;YAAE,OAAO,OAAO,CAAC;IAChD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAgB;IACzC,OAAO;QACL,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,UAAU,EAAE,GAAG,CAAC,UAAU;KAC3B,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,MAAc,EAAE,WAAmB;IACzD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,kBAAkB,EAAE,CAAC;QAC9D,MAAM,IAAI,UAAU,CAAC,mDAAmD,MAAM,CAAC,kBAAkB,CAAC,aAAa,CAAC,CAAC;IACnH,CAAC;IACD,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,WAAW,IAAI,CAAC,IAAI,WAAW,IAAI,IAAI,CAAC,EAAE,CAAC;QAChF,MAAM,IAAI,UAAU,CAAC,iFAAiF,CAAC,CAAC;IAC1G,CAAC;AACH,CAAC;AAED,gHAAgH;AAChH,SAAS,cAAc,CAAC,MAAyB;IAC/C,MAAM,UAAU,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IACxD,IAAI,UAAU,EAAE,MAAM,KAAK,MAAM,IAAI,UAAU,CAAC,SAAS,KAAK,SAAS;QAAE,OAAO;IAChF,IAAI,UAAU,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,UAAU,CAAC,SAAS,CAAC,MAAM,GAAG,qBAAqB,EAAE,CAAC;QAC7F,MAAM,IAAI,UAAU,CAAC,0DAA0D,MAAM,CAAC,qBAAqB,CAAC,aAAa,CAAC,CAAC;IAC7H,CAAC;AACH,CAAC;AAED,SAAS,IAAI,CAAC,MAAe;IAC3B,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAK,MAA2B,CAAC,EAAE,KAAK,IAAI,CAAC;AACnG,CAAC;AAED,SAAS,WAAW,CAAC,KAAa;IAChC,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;IACjD,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAc,CAAC;AAC1E,CAAC;AAED,SAAS,UAAU,CAAI,GAAkB;IACvC,IAAI,GAAG,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IAC9F,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/sync.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { Err, ErrorCode, Ok } from "@softure-ai/core";
|
|
2
|
+
export declare const SYNC_ITEM_STATUSES: readonly ["pending", "done", "skipped", "failed"];
|
|
3
|
+
/** `pending`: seen, not handled yet (a failed attempt with attempts left); `done`, `skipped`, `failed` are final. */
|
|
4
|
+
export type SyncItemStatus = (typeof SYNC_ITEM_STATUSES)[number];
|
|
5
|
+
/** One item of the processed-item ledger. */
|
|
6
|
+
export interface SyncItem {
|
|
7
|
+
readonly source: string;
|
|
8
|
+
readonly itemId: string;
|
|
9
|
+
readonly status: SyncItemStatus;
|
|
10
|
+
/** The code of the last failed attempt, or why the item was skipped; never a message. */
|
|
11
|
+
readonly errorCode: string | null;
|
|
12
|
+
/** Runs of the callback so far. */
|
|
13
|
+
readonly attempts: number;
|
|
14
|
+
/** What handling the item produced in the app (a task id), when the callback named it. */
|
|
15
|
+
readonly resultRef: string | null;
|
|
16
|
+
readonly firstSeenAt: Date;
|
|
17
|
+
/** The last time `processSyncItem` met the item; pruning goes by it. */
|
|
18
|
+
readonly lastSeenAt: Date;
|
|
19
|
+
/** The last change of status, attempts or error code. */
|
|
20
|
+
readonly updatedAt: Date;
|
|
21
|
+
readonly finishedAt: Date | null;
|
|
22
|
+
}
|
|
23
|
+
/** How a callback ends an item well: handled (`ok()`, or `ok({ status: "done", resultRef })`) or deliberately skipped. */
|
|
24
|
+
export type SyncItemCompletion = {
|
|
25
|
+
readonly status: "done";
|
|
26
|
+
readonly resultRef?: string;
|
|
27
|
+
} | {
|
|
28
|
+
readonly status: "skipped";
|
|
29
|
+
readonly reason?: ErrorCode;
|
|
30
|
+
};
|
|
31
|
+
/** A callback's failure: tried again on the next run unless `retry` is false or the attempts are used up. */
|
|
32
|
+
export type SyncItemFailure = Err<ErrorCode> & {
|
|
33
|
+
readonly retry?: boolean;
|
|
34
|
+
};
|
|
35
|
+
/** What a `processSyncItem` callback returns. */
|
|
36
|
+
export type SyncItemRunResult = Ok<SyncItemCompletion | undefined> | SyncItemFailure;
|
|
37
|
+
/** What `processSyncItem` did with an item. */
|
|
38
|
+
export type SyncItemOutcome =
|
|
39
|
+
/** The callback ran and succeeded; `item.status` is `done` or `skipped`. */
|
|
40
|
+
{
|
|
41
|
+
readonly kind: "handled";
|
|
42
|
+
readonly item: SyncItem;
|
|
43
|
+
}
|
|
44
|
+
/** The callback failed; the item stays `pending` and the next run tries it again. */
|
|
45
|
+
| {
|
|
46
|
+
readonly kind: "retrying";
|
|
47
|
+
readonly error: string;
|
|
48
|
+
readonly item: SyncItem;
|
|
49
|
+
}
|
|
50
|
+
/** The callback failed for good (attempts used up, or `failSyncItem`); the item is `failed`. */
|
|
51
|
+
| {
|
|
52
|
+
readonly kind: "failed";
|
|
53
|
+
readonly error: string;
|
|
54
|
+
readonly item: SyncItem;
|
|
55
|
+
}
|
|
56
|
+
/** The callback did not run: the item was already `done`, `skipped` or `failed` (see `item.status`). */
|
|
57
|
+
| {
|
|
58
|
+
readonly kind: "skipped";
|
|
59
|
+
readonly item: SyncItem;
|
|
60
|
+
};
|
|
61
|
+
/** Ends an item as `skipped` (not relevant to the app), with an optional code saying why; never tried again. */
|
|
62
|
+
export declare function skipSyncItem(reason?: ErrorCode): Ok<SyncItemCompletion>;
|
|
63
|
+
/** A failure that is not retried: the item is `failed` at once with this code. */
|
|
64
|
+
export declare function failSyncItem<E extends ErrorCode>(error: E): SyncItemFailure;
|
|
65
|
+
//# sourceMappingURL=sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAE3D,eAAO,MAAM,kBAAkB,mDAAoD,CAAC;AAEpF,qHAAqH;AACrH,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjE,6CAA6C;AAC7C,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,yFAAyF;IACzF,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,mCAAmC;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,0FAA0F;IAC1F,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC;IAC1B,yDAAyD;IACzD,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;CAClC;AAED,0HAA0H;AAC1H,MAAM,MAAM,kBAAkB,GAC1B;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACxD;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAA;CAAE,CAAC;AAEhE,6GAA6G;AAC7G,MAAM,MAAM,eAAe,GAAG,GAAG,CAAC,SAAS,CAAC,GAAG;IAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAE5E,iDAAiD;AACjD,MAAM,MAAM,iBAAiB,GAAG,EAAE,CAAC,kBAAkB,GAAG,SAAS,CAAC,GAAG,eAAe,CAAC;AAErF,+CAA+C;AAC/C,MAAM,MAAM,eAAe;AACzB,4EAA4E;AAC1E;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE;AACvD,qFAAqF;GACnF;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE;AAChF,gGAAgG;GAC9F;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE;AAC9E,wGAAwG;GACtG;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAE1D,gHAAgH;AAChH,wBAAgB,YAAY,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG,EAAE,CAAC,kBAAkB,CAAC,CAEvE;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,CAAC,SAAS,SAAS,EAAE,KAAK,EAAE,CAAC,GAAG,eAAe,CAE3E"}
|
package/dist/sync.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const SYNC_ITEM_STATUSES = ["pending", "done", "skipped", "failed"];
|
|
2
|
+
/** Ends an item as `skipped` (not relevant to the app), with an optional code saying why; never tried again. */
|
|
3
|
+
export function skipSyncItem(reason) {
|
|
4
|
+
return { ok: true, value: reason === undefined ? { status: "skipped" } : { status: "skipped", reason } };
|
|
5
|
+
}
|
|
6
|
+
/** A failure that is not retried: the item is `failed` at once with this code. */
|
|
7
|
+
export function failSyncItem(error) {
|
|
8
|
+
return { ok: false, error, retry: false };
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=sync.js.map
|
package/dist/sync.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,CAAU,CAAC;AA8CpF,gHAAgH;AAChH,MAAM,UAAU,YAAY,CAAC,MAAkB;IAC7C,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC;AAC3G,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,YAAY,CAAsB,KAAQ;IACxD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAC5C,CAAC"}
|
package/dist/testing/index.d.ts
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import type { Queryable } from "@softure-ai/db";
|
|
2
2
|
import type { Job } from "../contract.js";
|
|
3
|
+
import type { SyncItem } from "../sync.js";
|
|
3
4
|
export { runDueJobs, type RunDueJobsOptions, type RunDueJobsReport } from "../server/run-due.js";
|
|
4
5
|
/** Every job in the table, oldest first: for assertions. */
|
|
5
6
|
export declare function listJobs(db: Queryable): Promise<Job[]>;
|
|
7
|
+
export interface SyncState {
|
|
8
|
+
/** Every source's cursor, by source name. */
|
|
9
|
+
readonly cursors: Readonly<Record<string, string>>;
|
|
10
|
+
/** Every ledger item, by source and then item id. */
|
|
11
|
+
readonly items: SyncItem[];
|
|
12
|
+
}
|
|
13
|
+
/** The whole sync state, cursors and ledger: for assertions after a test ran its polling job (`runDueJobs`). */
|
|
14
|
+
export declare function listSyncState(db: Queryable): Promise<SyncState>;
|
|
6
15
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAI1C,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEjG,4DAA4D;AAC5D,wBAAsB,QAAQ,CAAC,EAAE,EAAE,SAAS,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAG5D"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAI1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEjG,4DAA4D;AAC5D,wBAAsB,QAAQ,CAAC,EAAE,EAAE,SAAS,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAG5D;AAED,MAAM,WAAW,SAAS;IACxB,6CAA6C;IAC7C,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD,qDAAqD;IACrD,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC;CAC5B;AAED,gHAAgH;AAChH,wBAAsB,aAAa,CAAC,EAAE,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAIrE"}
|
package/dist/testing/index.js
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { asc } from "drizzle-orm";
|
|
2
|
-
import { jobs } from "../schema.js";
|
|
2
|
+
import { jobs, syncCursors, syncItems } from "../schema.js";
|
|
3
3
|
import { toJob } from "../server/job-row.js";
|
|
4
|
+
import { toSyncItem } from "../server/sync-items.js";
|
|
4
5
|
export { runDueJobs } from "../server/run-due.js";
|
|
5
6
|
/** Every job in the table, oldest first: for assertions. */
|
|
6
7
|
export async function listJobs(db) {
|
|
7
8
|
const rows = await db.select().from(jobs).orderBy(asc(jobs.id));
|
|
8
9
|
return rows.map(toJob);
|
|
9
10
|
}
|
|
11
|
+
/** The whole sync state, cursors and ledger: for assertions after a test ran its polling job (`runDueJobs`). */
|
|
12
|
+
export async function listSyncState(db) {
|
|
13
|
+
const cursors = await db.select().from(syncCursors).orderBy(asc(syncCursors.source));
|
|
14
|
+
const items = await db.select().from(syncItems).orderBy(asc(syncItems.source), asc(syncItems.itemId));
|
|
15
|
+
return { cursors: Object.fromEntries(cursors.map((row) => [row.source, row.cursor])), items: items.map(toSyncItem) };
|
|
16
|
+
}
|
|
10
17
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAElC,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAElC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAGrD,OAAO,EAAE,UAAU,EAAiD,MAAM,sBAAsB,CAAC;AAEjG,4DAA4D;AAC5D,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,EAAa;IAC1C,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAChE,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACzB,CAAC;AASD,gHAAgH;AAChH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,EAAa;IAC/C,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;IACrF,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IACtG,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;AACvH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-job-worker.d.ts","sourceRoot":"","sources":["../../src/worker/run-job-worker.ts"],"names":[],"mappings":"AAIA,OAAO,EAAe,KAAK,KAAK,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC/E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAIlD,OAAO,EAAqB,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"run-job-worker.d.ts","sourceRoot":"","sources":["../../src/worker/run-job-worker.ts"],"names":[],"mappings":"AAIA,OAAO,EAAe,KAAK,KAAK,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC/E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAIlD,OAAO,EAAqB,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAYzE,MAAM,WAAW,mBAAmB;IAClC,oHAAoH;IACpH,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B,gGAAgG;IAChG,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,wEAAwE;IACxE,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;IACvB,yDAAyD;IACzD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,6CAA6C;IAC7C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;IAC/B,kGAAkG;IAClG,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAQD;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuG9E"}
|
|
@@ -7,6 +7,7 @@ import { claimJobs, extendJobLease, releaseJob } from "../server/claim.js";
|
|
|
7
7
|
import { checkHandlers } from "../server/handler-check.js";
|
|
8
8
|
import { consoleJobsLogger } from "../server/logger.js";
|
|
9
9
|
import { pruneJobs } from "../server/maintenance.js";
|
|
10
|
+
import { pruneSyncItems } from "../server/sync-items.js";
|
|
10
11
|
import { getJobsOptions, getQueue } from "../server/options.js";
|
|
11
12
|
import { runClaimedJob } from "../server/process.js";
|
|
12
13
|
import { enqueueScheduledRun, getSchedule, getScheduleNames, getScheduleSlot } from "../server/schedules.js";
|
|
@@ -108,6 +109,14 @@ export async function runJobWorker(options) {
|
|
|
108
109
|
catch (error) {
|
|
109
110
|
logger.error("could not prune finished jobs", { error });
|
|
110
111
|
}
|
|
112
|
+
try {
|
|
113
|
+
const deleted = await pruneSyncItems(ctx);
|
|
114
|
+
if (deleted > 0)
|
|
115
|
+
logger.info("pruned sync ledger items", { deleted });
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
logger.error("could not prune sync ledger items", { error });
|
|
119
|
+
}
|
|
111
120
|
}
|
|
112
121
|
if (!isClaimFailed)
|
|
113
122
|
healthFile?.touch();
|