@relkit/providers-local 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +40 -0
  3. package/dist/buckets/index.d.ts +5 -0
  4. package/dist/buckets/index.d.ts.map +1 -0
  5. package/dist/buckets/index.js +5 -0
  6. package/dist/buckets/index.js.map +1 -0
  7. package/dist/buckets/keys.d.ts +7 -0
  8. package/dist/buckets/keys.d.ts.map +1 -0
  9. package/dist/buckets/keys.js +60 -0
  10. package/dist/buckets/keys.js.map +1 -0
  11. package/dist/buckets/operations.d.ts +11 -0
  12. package/dist/buckets/operations.d.ts.map +1 -0
  13. package/dist/buckets/operations.js +89 -0
  14. package/dist/buckets/operations.js.map +1 -0
  15. package/dist/buckets/pagination.d.ts +5 -0
  16. package/dist/buckets/pagination.d.ts.map +1 -0
  17. package/dist/buckets/pagination.js +34 -0
  18. package/dist/buckets/pagination.js.map +1 -0
  19. package/dist/buckets/policy.d.ts +8 -0
  20. package/dist/buckets/policy.d.ts.map +1 -0
  21. package/dist/buckets/policy.js +74 -0
  22. package/dist/buckets/policy.js.map +1 -0
  23. package/dist/buckets/provider.d.ts +4 -0
  24. package/dist/buckets/provider.d.ts.map +1 -0
  25. package/dist/buckets/provider.js +119 -0
  26. package/dist/buckets/provider.js.map +1 -0
  27. package/dist/buckets/storage.d.ts +12 -0
  28. package/dist/buckets/storage.d.ts.map +1 -0
  29. package/dist/buckets/storage.js +169 -0
  30. package/dist/buckets/storage.js.map +1 -0
  31. package/dist/buckets/types.d.ts +91 -0
  32. package/dist/buckets/types.d.ts.map +1 -0
  33. package/dist/buckets/types.js +34 -0
  34. package/dist/buckets/types.js.map +1 -0
  35. package/dist/cache/index.d.ts +8 -0
  36. package/dist/cache/index.d.ts.map +1 -0
  37. package/dist/cache/index.js +8 -0
  38. package/dist/cache/index.js.map +1 -0
  39. package/dist/cache/inspector.d.ts +4 -0
  40. package/dist/cache/inspector.d.ts.map +1 -0
  41. package/dist/cache/inspector.js +62 -0
  42. package/dist/cache/inspector.js.map +1 -0
  43. package/dist/cache/keys.d.ts +6 -0
  44. package/dist/cache/keys.d.ts.map +1 -0
  45. package/dist/cache/keys.js +34 -0
  46. package/dist/cache/keys.js.map +1 -0
  47. package/dist/cache/persistence.d.ts +5 -0
  48. package/dist/cache/persistence.d.ts.map +1 -0
  49. package/dist/cache/persistence.js +108 -0
  50. package/dist/cache/persistence.js.map +1 -0
  51. package/dist/cache/policy.d.ts +9 -0
  52. package/dist/cache/policy.d.ts.map +1 -0
  53. package/dist/cache/policy.js +64 -0
  54. package/dist/cache/policy.js.map +1 -0
  55. package/dist/cache/provider.d.ts +3 -0
  56. package/dist/cache/provider.d.ts.map +1 -0
  57. package/dist/cache/provider.js +164 -0
  58. package/dist/cache/provider.js.map +1 -0
  59. package/dist/cache/store.d.ts +44 -0
  60. package/dist/cache/store.d.ts.map +1 -0
  61. package/dist/cache/store.js +132 -0
  62. package/dist/cache/store.js.map +1 -0
  63. package/dist/cache/types.d.ts +100 -0
  64. package/dist/cache/types.d.ts.map +1 -0
  65. package/dist/cache/types.js +39 -0
  66. package/dist/cache/types.js.map +1 -0
  67. package/dist/cache/write.d.ts +5 -0
  68. package/dist/cache/write.d.ts.map +1 -0
  69. package/dist/cache/write.js +13 -0
  70. package/dist/cache/write.js.map +1 -0
  71. package/dist/events/admin-actions.d.ts +7 -0
  72. package/dist/events/admin-actions.d.ts.map +1 -0
  73. package/dist/events/admin-actions.js +28 -0
  74. package/dist/events/admin-actions.js.map +1 -0
  75. package/dist/events/admin-contracts.d.ts +132 -0
  76. package/dist/events/admin-contracts.d.ts.map +1 -0
  77. package/dist/events/admin-contracts.js +4 -0
  78. package/dist/events/admin-contracts.js.map +1 -0
  79. package/dist/events/admin-errors.d.ts +5 -0
  80. package/dist/events/admin-errors.d.ts.map +1 -0
  81. package/dist/events/admin-errors.js +9 -0
  82. package/dist/events/admin-errors.js.map +1 -0
  83. package/dist/events/admin-state.d.ts +8 -0
  84. package/dist/events/admin-state.d.ts.map +1 -0
  85. package/dist/events/admin-state.js +10 -0
  86. package/dist/events/admin-state.js.map +1 -0
  87. package/dist/events/admin-utils.d.ts +27 -0
  88. package/dist/events/admin-utils.d.ts.map +1 -0
  89. package/dist/events/admin-utils.js +153 -0
  90. package/dist/events/admin-utils.js.map +1 -0
  91. package/dist/events/admin.d.ts +22 -0
  92. package/dist/events/admin.d.ts.map +1 -0
  93. package/dist/events/admin.js +92 -0
  94. package/dist/events/admin.js.map +1 -0
  95. package/dist/events/delivery-types.d.ts +74 -0
  96. package/dist/events/delivery-types.d.ts.map +1 -0
  97. package/dist/events/delivery-types.js +9 -0
  98. package/dist/events/delivery-types.js.map +1 -0
  99. package/dist/events/delivery-utils.d.ts +17 -0
  100. package/dist/events/delivery-utils.d.ts.map +1 -0
  101. package/dist/events/delivery-utils.js +133 -0
  102. package/dist/events/delivery-utils.js.map +1 -0
  103. package/dist/events/delivery.d.ts +6 -0
  104. package/dist/events/delivery.d.ts.map +1 -0
  105. package/dist/events/delivery.js +159 -0
  106. package/dist/events/delivery.js.map +1 -0
  107. package/dist/events/ephemeral.d.ts +36 -0
  108. package/dist/events/ephemeral.d.ts.map +1 -0
  109. package/dist/events/ephemeral.js +61 -0
  110. package/dist/events/ephemeral.js.map +1 -0
  111. package/dist/events/log.d.ts +37 -0
  112. package/dist/events/log.d.ts.map +1 -0
  113. package/dist/events/log.js +121 -0
  114. package/dist/events/log.js.map +1 -0
  115. package/dist/events/provider.d.ts +9 -0
  116. package/dist/events/provider.d.ts.map +1 -0
  117. package/dist/events/provider.js +37 -0
  118. package/dist/events/provider.js.map +1 -0
  119. package/dist/events/router-inspection.d.ts +18 -0
  120. package/dist/events/router-inspection.d.ts.map +1 -0
  121. package/dist/events/router-inspection.js +110 -0
  122. package/dist/events/router-inspection.js.map +1 -0
  123. package/dist/events/router-records.d.ts +34 -0
  124. package/dist/events/router-records.d.ts.map +1 -0
  125. package/dist/events/router-records.js +98 -0
  126. package/dist/events/router-records.js.map +1 -0
  127. package/dist/events/router-types.d.ts +83 -0
  128. package/dist/events/router-types.d.ts.map +1 -0
  129. package/dist/events/router-types.js +2 -0
  130. package/dist/events/router-types.js.map +1 -0
  131. package/dist/events/router.d.ts +7 -0
  132. package/dist/events/router.d.ts.map +1 -0
  133. package/dist/events/router.js +148 -0
  134. package/dist/events/router.js.map +1 -0
  135. package/dist/factory.d.ts +26 -0
  136. package/dist/factory.d.ts.map +1 -0
  137. package/dist/factory.js +66 -0
  138. package/dist/factory.js.map +1 -0
  139. package/dist/index.d.ts +18 -0
  140. package/dist/index.d.ts.map +1 -0
  141. package/dist/index.js +15 -0
  142. package/dist/index.js.map +1 -0
  143. package/dist/jobs/admin-contracts.d.ts +66 -0
  144. package/dist/jobs/admin-contracts.d.ts.map +1 -0
  145. package/dist/jobs/admin-contracts.js +4 -0
  146. package/dist/jobs/admin-contracts.js.map +1 -0
  147. package/dist/jobs/admin-errors.d.ts +5 -0
  148. package/dist/jobs/admin-errors.d.ts.map +1 -0
  149. package/dist/jobs/admin-errors.js +9 -0
  150. package/dist/jobs/admin-errors.js.map +1 -0
  151. package/dist/jobs/admin-utils.d.ts +29 -0
  152. package/dist/jobs/admin-utils.d.ts.map +1 -0
  153. package/dist/jobs/admin-utils.js +138 -0
  154. package/dist/jobs/admin-utils.js.map +1 -0
  155. package/dist/jobs/admin.d.ts +25 -0
  156. package/dist/jobs/admin.d.ts.map +1 -0
  157. package/dist/jobs/admin.js +89 -0
  158. package/dist/jobs/admin.js.map +1 -0
  159. package/dist/jobs/cron.d.ts +10 -0
  160. package/dist/jobs/cron.d.ts.map +1 -0
  161. package/dist/jobs/cron.js +18 -0
  162. package/dist/jobs/cron.js.map +1 -0
  163. package/dist/jobs/idempotency.d.ts +18 -0
  164. package/dist/jobs/idempotency.d.ts.map +1 -0
  165. package/dist/jobs/idempotency.js +82 -0
  166. package/dist/jobs/idempotency.js.map +1 -0
  167. package/dist/jobs/lease-utils.d.ts +9 -0
  168. package/dist/jobs/lease-utils.d.ts.map +1 -0
  169. package/dist/jobs/lease-utils.js +31 -0
  170. package/dist/jobs/lease-utils.js.map +1 -0
  171. package/dist/jobs/queue-admin.d.ts +12 -0
  172. package/dist/jobs/queue-admin.d.ts.map +1 -0
  173. package/dist/jobs/queue-admin.js +42 -0
  174. package/dist/jobs/queue-admin.js.map +1 -0
  175. package/dist/jobs/queue-entry.d.ts +8 -0
  176. package/dist/jobs/queue-entry.d.ts.map +1 -0
  177. package/dist/jobs/queue-entry.js +130 -0
  178. package/dist/jobs/queue-entry.js.map +1 -0
  179. package/dist/jobs/queue-lease.d.ts +4 -0
  180. package/dist/jobs/queue-lease.d.ts.map +1 -0
  181. package/dist/jobs/queue-lease.js +12 -0
  182. package/dist/jobs/queue-lease.js.map +1 -0
  183. package/dist/jobs/queue-operations.d.ts +16 -0
  184. package/dist/jobs/queue-operations.d.ts.map +1 -0
  185. package/dist/jobs/queue-operations.js +107 -0
  186. package/dist/jobs/queue-operations.js.map +1 -0
  187. package/dist/jobs/queue-recovery.d.ts +5 -0
  188. package/dist/jobs/queue-recovery.d.ts.map +1 -0
  189. package/dist/jobs/queue-recovery.js +22 -0
  190. package/dist/jobs/queue-recovery.js.map +1 -0
  191. package/dist/jobs/queue-utils.d.ts +101 -0
  192. package/dist/jobs/queue-utils.d.ts.map +1 -0
  193. package/dist/jobs/queue-utils.js +28 -0
  194. package/dist/jobs/queue-utils.js.map +1 -0
  195. package/dist/jobs/queue.d.ts +7 -0
  196. package/dist/jobs/queue.d.ts.map +1 -0
  197. package/dist/jobs/queue.js +70 -0
  198. package/dist/jobs/queue.js.map +1 -0
  199. package/dist/jobs/retry.d.ts +25 -0
  200. package/dist/jobs/retry.d.ts.map +1 -0
  201. package/dist/jobs/retry.js +84 -0
  202. package/dist/jobs/retry.js.map +1 -0
  203. package/dist/jobs/scheduler.d.ts +52 -0
  204. package/dist/jobs/scheduler.d.ts.map +1 -0
  205. package/dist/jobs/scheduler.js +122 -0
  206. package/dist/jobs/scheduler.js.map +1 -0
  207. package/dist/jobs/store-files.d.ts +20 -0
  208. package/dist/jobs/store-files.d.ts.map +1 -0
  209. package/dist/jobs/store-files.js +177 -0
  210. package/dist/jobs/store-files.js.map +1 -0
  211. package/dist/jobs/store.d.ts +60 -0
  212. package/dist/jobs/store.d.ts.map +1 -0
  213. package/dist/jobs/store.js +97 -0
  214. package/dist/jobs/store.js.map +1 -0
  215. package/dist/runtime-capabilities.d.ts +17 -0
  216. package/dist/runtime-capabilities.d.ts.map +1 -0
  217. package/dist/runtime-capabilities.js +32 -0
  218. package/dist/runtime-capabilities.js.map +1 -0
  219. package/dist/state.d.ts +15 -0
  220. package/dist/state.d.ts.map +1 -0
  221. package/dist/state.js +69 -0
  222. package/dist/state.js.map +1 -0
  223. package/package.json +47 -0
@@ -0,0 +1,122 @@
1
+ import { canonicalJson, deepFreeze, normalizeId, } from "@relkit/contracts";
2
+ import { nextCronFire } from "./cron.js";
3
+ export class ScheduleValidationError extends TypeError {
4
+ code = "RELKIT_SCHEDULE_INVALID";
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = "ScheduleValidationError";
8
+ }
9
+ }
10
+ /** Validates a static schedule and hides the cron parser behind native dates. */
11
+ export function compileSchedule(schedule, options = {}) {
12
+ try {
13
+ if (!isRecord(schedule))
14
+ throw new Error("Schedule must be an object");
15
+ const id = normalizeId(schedule.id);
16
+ const cron = requiredText(schedule.cron, "cron").replace(/\s+/g, " ");
17
+ if (cron.split(" ").length !== 5)
18
+ throw new Error("cron must have five fields");
19
+ const timezone = requiredText(schedule.timezone, "timezone");
20
+ if (!Object.prototype.hasOwnProperty.call(schedule, "input"))
21
+ throw new Error("schedule.input is required");
22
+ const input = JSON.parse(canonicalJson(schedule.input));
23
+ const overlap = schedule.overlap;
24
+ if (overlap !== "skip" && overlap !== "allow")
25
+ throw new Error("overlap must be skip or allow");
26
+ const currentDate = validDate(options.currentDate ?? new Date(0), "current date");
27
+ const first = parseNext(cron, timezone, currentDate);
28
+ return deepFreeze({
29
+ id,
30
+ cron,
31
+ timezone,
32
+ input: deepFreeze(input),
33
+ overlap,
34
+ nextFireAt: first,
35
+ nextFire: (date) => parseNext(cron, timezone, validDate(date, "current date")),
36
+ });
37
+ }
38
+ catch (cause) {
39
+ if (cause instanceof ScheduleValidationError)
40
+ throw cause;
41
+ throw new ScheduleValidationError(cause instanceof Error ? cause.message : String(cause));
42
+ }
43
+ }
44
+ /** Runs due schedules against an injected clock without owning or calling handlers. */
45
+ export function createScheduler(options = {}) {
46
+ const readClock = options.clock?.now ?? options.now ?? (() => new Date());
47
+ const states = new Map();
48
+ const register = (schedule, enqueue) => {
49
+ if (typeof enqueue !== "function")
50
+ throw new TypeError("Schedule enqueue target is required");
51
+ const compiled = compileSchedule(schedule, { currentDate: readDate(readClock()) });
52
+ if (states.has(compiled.id))
53
+ throw new ScheduleValidationError(`Duplicate schedule "${compiled.id}"`);
54
+ states.set(compiled.id, {
55
+ compiled,
56
+ enqueue,
57
+ nextFireAt: compiled.nextFireAt.getTime(),
58
+ active: 0,
59
+ });
60
+ return compiled;
61
+ };
62
+ for (const registration of options.schedules ?? [])
63
+ register(registration.schedule, registration.enqueue);
64
+ const nextFire = (scheduleId) => {
65
+ const state = states.get(normalizeId(scheduleId));
66
+ return state === undefined ? undefined : new Date(state.nextFireAt);
67
+ };
68
+ const runDue = async (currentDate) => {
69
+ const now = readDate(currentDate ?? readClock()).getTime();
70
+ const runs = [];
71
+ for (const state of [...states.values()].sort((a, b) => a.compiled.id.localeCompare(b.compiled.id))) {
72
+ while (state.nextFireAt <= now) {
73
+ const fireAt = new Date(state.nextFireAt);
74
+ state.nextFireAt = state.compiled.nextFire(fireAt).getTime();
75
+ if (state.compiled.overlap === "skip" && state.active > 0) {
76
+ runs.push(Promise.resolve({ scheduleId: state.compiled.id, fireAt, status: "skipped" }));
77
+ continue;
78
+ }
79
+ state.active += 1;
80
+ runs.push(Promise.resolve()
81
+ .then(() => state.enqueue(state.compiled.input, { scheduleId: state.compiled.id, fireAt }))
82
+ .then((result) => ({
83
+ scheduleId: state.compiled.id,
84
+ fireAt,
85
+ status: "enqueued",
86
+ result,
87
+ }))
88
+ .finally(() => {
89
+ state.active -= 1;
90
+ }));
91
+ }
92
+ }
93
+ return Promise.all(runs);
94
+ };
95
+ return Object.freeze({ register, nextFire, runDue, tick: runDue });
96
+ }
97
+ function parseNext(cron, timezone, currentDate) {
98
+ try {
99
+ return nextCronFire(cron, { timezone, currentDate });
100
+ }
101
+ catch (cause) {
102
+ throw new ScheduleValidationError(`Invalid cron/timezone: ${cause instanceof Error ? cause.message : String(cause)}`);
103
+ }
104
+ }
105
+ function requiredText(value, name) {
106
+ if (typeof value !== "string" || value.trim() === "")
107
+ throw new Error(`${name} is required`);
108
+ return value.trim();
109
+ }
110
+ function validDate(value, name) {
111
+ const date = new Date(value.getTime());
112
+ if (!Number.isFinite(date.getTime()))
113
+ throw new ScheduleValidationError(`${name} is invalid`);
114
+ return date;
115
+ }
116
+ function readDate(value) {
117
+ return validDate(typeof value === "number" ? new Date(value) : value, "clock date");
118
+ }
119
+ function isRecord(value) {
120
+ return value !== null && typeof value === "object" && !Array.isArray(value);
121
+ }
122
+ //# sourceMappingURL=scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../src/jobs/scheduler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { type JsonValue } from "@relkit/contracts";
2
+ import type { JobRecord, JobStoreCheckpoint, JobStoreIndex } from "./store.js";
3
+ export declare const STORE_VERSION: 1;
4
+ export interface JobStorePaths {
5
+ readonly records: string;
6
+ readonly index: string;
7
+ readonly checkpoint: string;
8
+ }
9
+ export declare function ensureJobRoot(requestedRoot: string): string;
10
+ export declare function createJobStorePaths(root: string): JobStorePaths;
11
+ export declare function makeMetadata(records: readonly JobRecord[]): {
12
+ readonly index: JobStoreIndex;
13
+ readonly checkpoint: JobStoreCheckpoint;
14
+ };
15
+ export declare function recoverJobRecords(path: string, root: string, validateData?: (data: JsonValue) => void): Promise<readonly JobRecord[]>;
16
+ export declare function parseJobRecord(value: unknown): JobRecord;
17
+ export declare function readJobMetadata<T>(path: string, root: string, guard: (value: unknown) => value is T): Promise<T | undefined>;
18
+ export declare function writeJobMetadata(indexPath: string, checkpointPath: string, index: JobStoreIndex, checkpoint: JobStoreCheckpoint, afterIndex?: () => void | Promise<void>, afterCheckpoint?: () => void | Promise<void>): Promise<void>;
19
+ export declare function appendDurably(path: string, value: string): Promise<void>;
20
+ //# sourceMappingURL=store-files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-files.d.ts","sourceRoot":"","sources":["../../src/jobs/store-files.ts"],"names":[],"mappings":"AAGA,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9E,OAAO,KAAK,EAAiB,SAAS,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC9F,eAAO,MAAM,aAAa,EAAG,CAAU,CAAC;AAExC,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AACD,wBAAgB,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAK3D;AACD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAM/D;AACD,wBAAgB,YAAY,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,GAAG;IAC3D,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,kBAAkB,CAAC;CACzC,CAoBA;AACD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,GACvC,OAAO,CAAC,SAAS,SAAS,EAAE,CAAC,CA+B/B;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,CAuBxD;AAED,wBAAsB,eAAe,CAAC,CAAC,EACrC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,IAAI,CAAC,GACpC,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAiBxB;AAED,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,aAAa,EACpB,UAAU,EAAE,kBAAkB,EAC9B,UAAU,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EACvC,eAAe,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAC3C,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ9E"}
@@ -0,0 +1,177 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { open, readFile, rename, rm } from "node:fs/promises";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { canonicalJson, deepFreeze } from "@relkit/contracts";
5
+ import { ensureOwnedDirectory, quarantineStateFile } from "../state.js";
6
+ export const STORE_VERSION = 1;
7
+ export function ensureJobRoot(requestedRoot) {
8
+ if (requestedRoot.trim() === "")
9
+ throw new Error("Job state root is empty");
10
+ const root = ensureOwnedDirectory(requestedRoot);
11
+ if (root === resolve("/"))
12
+ throw new Error("Job state root is too broad");
13
+ return root;
14
+ }
15
+ export function createJobStorePaths(root) {
16
+ return Object.freeze({
17
+ records: join(root, "records.ndjson"),
18
+ index: join(root, "index.json"),
19
+ checkpoint: join(root, "checkpoint.json"),
20
+ });
21
+ }
22
+ export function makeMetadata(records) {
23
+ const entries = {};
24
+ let offset = 0;
25
+ let sequence = 0;
26
+ for (const record of records) {
27
+ entries[record.instanceId] = { sequence: record.sequence, offset };
28
+ offset += Buffer.byteLength(`${canonicalJson(record)}\n`);
29
+ sequence = Math.max(sequence, record.sequence);
30
+ }
31
+ const commit = sequence;
32
+ return {
33
+ index: Object.freeze({ version: STORE_VERSION, commit, entries: Object.freeze(entries) }),
34
+ checkpoint: Object.freeze({
35
+ version: STORE_VERSION,
36
+ commit,
37
+ sequence,
38
+ offset,
39
+ recordCount: records.length,
40
+ }),
41
+ };
42
+ }
43
+ export async function recoverJobRecords(path, root, validateData) {
44
+ let contents;
45
+ try {
46
+ contents = await readFile(path, "utf8");
47
+ }
48
+ catch (cause) {
49
+ if (cause.code === "ENOENT") {
50
+ await writeDurably(path, "");
51
+ return [];
52
+ }
53
+ throw new Error("Job records cannot be read");
54
+ }
55
+ const records = [];
56
+ const sequences = new Set();
57
+ let malformed = false;
58
+ for (const line of contents.split("\n")) {
59
+ if (line === "")
60
+ continue;
61
+ try {
62
+ const record = parseJobRecord(JSON.parse(line));
63
+ validateData?.(record.data);
64
+ if (sequences.has(record.sequence))
65
+ throw new Error("duplicate sequence");
66
+ sequences.add(record.sequence);
67
+ records.push(record);
68
+ }
69
+ catch {
70
+ malformed = true;
71
+ }
72
+ }
73
+ if (!malformed)
74
+ return records;
75
+ await quarantineStateFile(path, root);
76
+ await syncDirectory(root);
77
+ await writeDurably(path, records.map((record) => `${canonicalJson(record)}\n`).join(""));
78
+ return records;
79
+ }
80
+ export function parseJobRecord(value) {
81
+ if (!isRecord(value) || value.version !== STORE_VERSION) {
82
+ throw new Error("Job record is malformed");
83
+ }
84
+ if (!isPositiveCount(value.sequence) ||
85
+ typeof value.instanceId !== "string" ||
86
+ value.instanceId.trim() === "" ||
87
+ typeof value.kind !== "string" ||
88
+ value.kind.trim() === "" ||
89
+ !isCount(value.timestamp)) {
90
+ throw new Error("Job record is malformed");
91
+ }
92
+ const data = deepFreeze(JSON.parse(canonicalJson(value.data)));
93
+ return Object.freeze({
94
+ version: STORE_VERSION,
95
+ sequence: value.sequence,
96
+ instanceId: value.instanceId,
97
+ kind: value.kind,
98
+ timestamp: value.timestamp,
99
+ data,
100
+ });
101
+ }
102
+ export async function readJobMetadata(path, root, guard) {
103
+ let contents;
104
+ try {
105
+ contents = await readFile(path, "utf8");
106
+ }
107
+ catch (cause) {
108
+ if (cause.code === "ENOENT")
109
+ return undefined;
110
+ throw new Error("Job metadata cannot be read");
111
+ }
112
+ try {
113
+ const value = JSON.parse(contents);
114
+ if (!guard(value))
115
+ throw new Error();
116
+ return value;
117
+ }
118
+ catch {
119
+ await quarantineStateFile(path, root);
120
+ await syncDirectory(root);
121
+ return undefined;
122
+ }
123
+ }
124
+ export async function writeJobMetadata(indexPath, checkpointPath, index, checkpoint, afterIndex, afterCheckpoint) {
125
+ await writeDurably(indexPath, canonicalJson(index));
126
+ await afterIndex?.();
127
+ await writeDurably(checkpointPath, canonicalJson(checkpoint));
128
+ await afterCheckpoint?.();
129
+ }
130
+ export async function appendDurably(path, value) {
131
+ const handle = await open(path, "a", 0o600);
132
+ try {
133
+ await handle.writeFile(value, "utf8");
134
+ await handle.sync();
135
+ }
136
+ finally {
137
+ await handle.close();
138
+ }
139
+ }
140
+ async function writeDurably(path, value) {
141
+ const directory = ensureOwnedDirectory(dirname(path));
142
+ const temporary = join(directory, `.relkit-tmp-${randomUUID()}`);
143
+ let handle;
144
+ try {
145
+ handle = await open(temporary, "wx", 0o600);
146
+ await handle.writeFile(value, "utf8");
147
+ await handle.sync();
148
+ await handle.close();
149
+ handle = undefined;
150
+ await rename(temporary, path);
151
+ await syncDirectory(directory);
152
+ }
153
+ catch (cause) {
154
+ await handle?.close().catch(() => undefined);
155
+ await rm(temporary, { force: true }).catch(() => undefined);
156
+ throw new Error(`Job state file could not be committed: ${cause instanceof Error ? cause.message : "unknown error"}`);
157
+ }
158
+ }
159
+ async function syncDirectory(directory) {
160
+ const handle = await open(directory, "r");
161
+ try {
162
+ await handle.sync();
163
+ }
164
+ finally {
165
+ await handle.close();
166
+ }
167
+ }
168
+ function isRecord(value) {
169
+ return value !== null && typeof value === "object" && !Array.isArray(value);
170
+ }
171
+ function isCount(value) {
172
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
173
+ }
174
+ function isPositiveCount(value) {
175
+ return isCount(value) && value > 0;
176
+ }
177
+ //# sourceMappingURL=store-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-files.js","sourceRoot":"","sources":["../../src/jobs/store-files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ import { type JsonValue } from "@relkit/contracts";
2
+ import { createJobStorePaths, STORE_VERSION } from "./store-files.js";
3
+ export { STORE_VERSION } from "./store-files.js";
4
+ export type JobStoreBoundary = "record-fsynced" | "index-committed" | "checkpoint-committed";
5
+ export interface JobStoreOptions {
6
+ readonly now?: () => number;
7
+ /** Test-only failure seam; an error rejects append before acknowledgement. */
8
+ readonly onBoundary?: (boundary: JobStoreBoundary) => void | Promise<void>;
9
+ /** Optional semantic guard used by durable stores layered on this record format. */
10
+ readonly validateData?: (data: JsonValue) => void;
11
+ }
12
+ export interface JobRecordInput {
13
+ readonly instanceId: string;
14
+ readonly kind: string;
15
+ readonly data: JsonValue;
16
+ readonly timestamp?: number;
17
+ }
18
+ export interface JobRecord {
19
+ readonly version: typeof STORE_VERSION;
20
+ readonly sequence: number;
21
+ readonly instanceId: string;
22
+ readonly kind: string;
23
+ readonly timestamp: number;
24
+ readonly data: JsonValue;
25
+ }
26
+ export interface JobIndexEntry {
27
+ readonly sequence: number;
28
+ readonly offset: number;
29
+ }
30
+ export interface JobStoreIndex {
31
+ readonly version: typeof STORE_VERSION;
32
+ readonly commit: number;
33
+ readonly entries: Readonly<Record<string, JobIndexEntry>>;
34
+ }
35
+ export interface JobStoreCheckpoint {
36
+ readonly version: typeof STORE_VERSION;
37
+ readonly commit: number;
38
+ readonly sequence: number;
39
+ readonly offset: number;
40
+ readonly recordCount: number;
41
+ }
42
+ export interface JobStoreSnapshot {
43
+ readonly records: readonly JobRecord[];
44
+ readonly index: JobStoreIndex;
45
+ readonly checkpoint: JobStoreCheckpoint;
46
+ }
47
+ export interface JobStore {
48
+ readonly root: string;
49
+ readonly paths: Readonly<ReturnType<typeof createJobStorePaths>>;
50
+ readonly append: (input: JobRecordInput) => Promise<JobRecord>;
51
+ readonly snapshot: () => JobStoreSnapshot;
52
+ readonly close: () => Promise<void>;
53
+ }
54
+ export declare class JobStoreStateError extends Error {
55
+ readonly code: "RELKIT_JOB_STORE_STATE_INVALID";
56
+ constructor(message: string);
57
+ }
58
+ /** Opens and repairs the append log before exposing durable job operations. */
59
+ export declare function createJobStore(requestedRoot: string, options?: JobStoreOptions): Promise<JobStore>;
60
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/jobs/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,97 @@
1
+ import { canonicalJson } from "@relkit/contracts";
2
+ import { createJobStorePaths, ensureJobRoot, makeMetadata, parseJobRecord, readJobMetadata, recoverJobRecords, writeJobMetadata, appendDurably, STORE_VERSION, } from "./store-files.js";
3
+ export { STORE_VERSION } from "./store-files.js";
4
+ export class JobStoreStateError extends Error {
5
+ code = "RELKIT_JOB_STORE_STATE_INVALID";
6
+ constructor(message) {
7
+ super(message);
8
+ this.name = "JobStoreStateError";
9
+ }
10
+ }
11
+ /** Opens and repairs the append log before exposing durable job operations. */
12
+ export async function createJobStore(requestedRoot, options = {}) {
13
+ const root = ensureJobRoot(requestedRoot);
14
+ const paths = createJobStorePaths(root);
15
+ const records = await recoverJobRecords(paths.records, root, options.validateData);
16
+ const current = makeMetadata(records);
17
+ const storedIndex = await readJobMetadata(paths.index, root, isIndex);
18
+ const storedCheckpoint = await readJobMetadata(paths.checkpoint, root, isCheckpoint);
19
+ if (storedIndex === undefined ||
20
+ storedCheckpoint === undefined ||
21
+ canonicalJson(storedIndex) !== canonicalJson(current.index) ||
22
+ canonicalJson(storedCheckpoint) !== canonicalJson(current.checkpoint)) {
23
+ await writeJobMetadata(paths.index, paths.checkpoint, current.index, current.checkpoint);
24
+ }
25
+ let snapshot = makeSnapshot(records, current);
26
+ // ponytail: one store-wide write lock; shard by state root if throughput matters.
27
+ let tail = Promise.resolve();
28
+ let closing = false;
29
+ let closed = false;
30
+ const append = (input) => {
31
+ if (closing || closed)
32
+ return Promise.reject(new JobStoreStateError("Job store is closed"));
33
+ const result = tail.then(async () => {
34
+ if (closed)
35
+ throw new JobStoreStateError("Job store is closed");
36
+ const record = makeRecord(input, snapshot.checkpoint.sequence + 1, options.now);
37
+ await appendDurably(paths.records, `${canonicalJson(record)}\n`);
38
+ await options.onBoundary?.("record-fsynced");
39
+ const nextRecords = [...snapshot.records, record];
40
+ const next = makeMetadata(nextRecords);
41
+ snapshot = makeSnapshot(nextRecords, next);
42
+ await writeJobMetadata(paths.index, paths.checkpoint, next.index, next.checkpoint, async () => options.onBoundary?.("index-committed"), async () => options.onBoundary?.("checkpoint-committed"));
43
+ return record;
44
+ });
45
+ tail = result.then(() => undefined, () => undefined);
46
+ return result;
47
+ };
48
+ const close = async () => {
49
+ closing = true;
50
+ await tail;
51
+ closed = true;
52
+ };
53
+ return Object.freeze({ root, paths, append, snapshot: () => snapshot, close });
54
+ }
55
+ function makeRecord(input, sequence, now) {
56
+ const timestamp = input.timestamp ?? now?.() ?? Date.now();
57
+ if (!Number.isSafeInteger(timestamp) || timestamp < 0) {
58
+ throw new JobStoreStateError("Job record timestamp is invalid");
59
+ }
60
+ return parseJobRecord({
61
+ version: STORE_VERSION,
62
+ sequence,
63
+ instanceId: input.instanceId,
64
+ kind: input.kind,
65
+ timestamp,
66
+ data: JSON.parse(canonicalJson(input.data)),
67
+ });
68
+ }
69
+ function makeSnapshot(records, current) {
70
+ return Object.freeze({
71
+ records: Object.freeze([...records]),
72
+ index: current.index,
73
+ checkpoint: current.checkpoint,
74
+ });
75
+ }
76
+ function isIndex(value) {
77
+ if (!isRecord(value) || value.version !== STORE_VERSION || !isCount(value.commit))
78
+ return false;
79
+ if (!isRecord(value.entries))
80
+ return false;
81
+ return Object.values(value.entries).every((entry) => isRecord(entry) && isCount(entry.sequence) && entry.sequence > 0 && isCount(entry.offset));
82
+ }
83
+ function isCheckpoint(value) {
84
+ return (isRecord(value) &&
85
+ value.version === STORE_VERSION &&
86
+ isCount(value.commit) &&
87
+ isCount(value.sequence) &&
88
+ isCount(value.offset) &&
89
+ isCount(value.recordCount));
90
+ }
91
+ function isCount(value) {
92
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
93
+ }
94
+ function isRecord(value) {
95
+ return value !== null && typeof value === "object" && !Array.isArray(value);
96
+ }
97
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/jobs/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { JsonValue } from "@relkit/contracts";
2
+ import { type JobQueue } from "./jobs/queue.js";
3
+ import type { JobIdempotencyDefinition } from "./jobs/queue-utils.js";
4
+ export interface LocalJobProvider {
5
+ readonly createQueue: (context: {
6
+ readonly jobId: string;
7
+ readonly idempotency?: JobIdempotencyDefinition;
8
+ }) => Promise<JobQueue>;
9
+ readonly close: () => Promise<void>;
10
+ }
11
+ export declare function createLocalJobProvider(root: string, profile: string): LocalJobProvider;
12
+ export declare function createLocalObservabilityProvider(): Readonly<{
13
+ collect: (record: JsonValue) => void;
14
+ emit: (record: JsonValue) => void;
15
+ read: () => readonly JsonValue[];
16
+ }>;
17
+ //# sourceMappingURL=runtime-capabilities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-capabilities.d.ts","sourceRoot":"","sources":["../src/runtime-capabilities.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAGtE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,EAAE;QAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,wBAAwB,CAAC;KACjD,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAkBtF;AAED,wBAAgB,gCAAgC;sBAG1B,SAAS,KAAG,IAAI;mBAGnB,SAAS,KAAG,IAAI;gBAGrB,SAAS,SAAS,EAAE;GAEjC"}
@@ -0,0 +1,32 @@
1
+ import { join } from "node:path";
2
+ import { createJobQueue } from "./jobs/queue.js";
3
+ import { createJobStore } from "./jobs/store.js";
4
+ export function createLocalJobProvider(root, profile) {
5
+ const stores = new Map();
6
+ return Object.freeze({
7
+ createQueue: async (context) => {
8
+ const existing = stores.get(context.jobId);
9
+ const store = existing ?? (await createJobStore(join(root, "jobs", profile, context.jobId)));
10
+ stores.set(context.jobId, store);
11
+ return createJobQueue(store, {
12
+ ...(context.idempotency === undefined ? {} : { idempotency: context.idempotency }),
13
+ });
14
+ },
15
+ close: async () => {
16
+ await Promise.all([...stores.values()].map((store) => store.close()));
17
+ },
18
+ });
19
+ }
20
+ export function createLocalObservabilityProvider() {
21
+ const records = [];
22
+ return Object.freeze({
23
+ collect: (record) => {
24
+ records.push(record);
25
+ },
26
+ emit: (record) => {
27
+ records.push(record);
28
+ },
29
+ read: () => Object.freeze([...records]),
30
+ });
31
+ }
32
+ //# sourceMappingURL=runtime-capabilities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-capabilities.js","sourceRoot":"","sources":["../src/runtime-capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,cAAc,EAAiB,MAAM,iBAAiB,CAAC;AAEhE,OAAO,EAAE,cAAc,EAAiB,MAAM,iBAAiB,CAAC;AAUhE,MAAM,UAAU,sBAAsB,CAAC,IAAY,EAAE,OAAe;IAClE,MAAM,MAAM,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC3C,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,WAAW,EAAE,KAAK,EAAE,OAGnB,EAAE,EAAE;YACH,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAC3C,MAAM,KAAK,GAAG,QAAQ,IAAI,CAAC,MAAM,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC7F,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YACjC,OAAO,cAAc,CAAC,KAAK,EAAE;gBAC3B,GAAG,CAAC,OAAO,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC;aACnF,CAAC,CAAC;QACL,CAAC;QACD,KAAK,EAAE,KAAK,IAAI,EAAE;YAChB,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QACxE,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,gCAAgC;IAC9C,MAAM,OAAO,GAAgB,EAAE,CAAC;IAChC,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,OAAO,EAAE,CAAC,MAAiB,EAAQ,EAAE;YACnC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC;QACD,IAAI,EAAE,CAAC,MAAiB,EAAQ,EAAE;YAChC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC;QACD,IAAI,EAAE,GAAyB,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC;KAC9D,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,15 @@
1
+ export interface LocalProviderStateRoot {
2
+ readonly root: string;
3
+ readonly buckets: string;
4
+ readonly cache: string;
5
+ }
6
+ export declare class LocalProviderStateError extends Error {
7
+ readonly code: "RELKIT_LOCAL_PROVIDER_STATE_INVALID";
8
+ constructor(message?: string);
9
+ }
10
+ /** Owns the capability directories used by one local provider generation. */
11
+ export declare function createLocalProviderStateRoot(requestedRoot?: string): LocalProviderStateRoot;
12
+ /** Moves malformed provider state aside so a later startup can continue safely. */
13
+ export declare function quarantineStateFile(file: string, ownerRoot: string): string;
14
+ export declare function ensureOwnedDirectory(path: string): string;
15
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,QAAQ,CAAC,IAAI,EAAG,qCAAqC,CAAU;gBAEnD,OAAO,SAAyC;CAI7D;AAED,6EAA6E;AAC7E,wBAAgB,4BAA4B,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,sBAAsB,CAY3F;AAED,mFAAmF;AACnF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAgB3E;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIzD"}