@moxxy/sdk 0.46.0 → 0.47.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.
package/dist/cron.d.ts CHANGED
@@ -54,5 +54,11 @@ export declare function isValidTimeZone(tz: string | undefined): boolean;
54
54
  * null instead of looping forever.
55
55
  */
56
56
  export declare function nextFireTime(expr: string, after: Date, timeZone?: string): Date | null;
57
+ /**
58
+ * The first fire from `fromMs` to `toMs` (both inclusive), or null when none
59
+ * falls inside. A `fromMs` inside a minute looks from the next whole minute:
60
+ * cron fires on whole minutes, and the one just gone is before the window.
61
+ */
62
+ export declare function firstFireBetween(expr: string, fromMs: number, toMs: number, timeZone?: string): Date | null;
57
63
  export {};
58
64
  //# sourceMappingURL=cron.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cron.d.ts","sourceRoot":"","sources":["../src/cron.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,UAAU,SAAS;IACjB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACrC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;CACzB;AAwDD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAalD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOjD;AAsCD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAQ/D;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,IAAI,EACX,QAAQ,CAAC,EAAE,MAAM,GAChB,IAAI,GAAG,IAAI,CAkEb"}
1
+ {"version":3,"file":"cron.d.ts","sourceRoot":"","sources":["../src/cron.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,UAAU,SAAS;IACjB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACrC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;CACzB;AAwDD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAalD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOjD;AAsCD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAQ/D;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,IAAI,EACX,QAAQ,CAAC,EAAE,MAAM,GAChB,IAAI,GAAG,IAAI,CAkEb;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAK3G"}
package/dist/cron.js CHANGED
@@ -233,6 +233,17 @@ export function nextFireTime(expr, after, timeZone) {
233
233
  }
234
234
  return null;
235
235
  }
236
+ /**
237
+ * The first fire from `fromMs` to `toMs` (both inclusive), or null when none
238
+ * falls inside. A `fromMs` inside a minute looks from the next whole minute:
239
+ * cron fires on whole minutes, and the one just gone is before the window.
240
+ */
241
+ export function firstFireBetween(expr, fromMs, toMs, timeZone) {
242
+ const firstMinute = Math.ceil(fromMs / 60_000) * 60_000;
243
+ // nextFireTime looks from the whole minute after `after`.
244
+ const next = nextFireTime(expr, new Date(firstMinute - 60_000), timeZone);
245
+ return next !== null && next.getTime() <= toMs ? next : null;
246
+ }
236
247
  // Memoized Intl.DateTimeFormat per IANA zone. Formatters are stateless, so
237
248
  // caching them across the (potentially long) minute-walk avoids re-allocating
238
249
  // one on every cursor step for explicit-zone crons.
package/dist/cron.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cron.js","sourceRoot":"","sources":["../src/cron.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cron.js","sourceRoot":"","sources":["../src/cron.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /** Test seams only: let a test hold a waiter at the exact moment a race needs. */
2
+ export interface FileLockHooks {
3
+ /** Called when this waiter has judged the lock stale, before anything is removed. */
4
+ readonly onStaleLockSeen?: () => Promise<void>;
5
+ }
6
+ /**
7
+ * Hold `<file>.lock` while `fn` runs, so processes sharing the file change it
8
+ * one at a time. The lock is a file created only if absent (`open(…, 'wx')`,
9
+ * atomic on every platform); a stale one is removed and claimed again, by one
10
+ * waiter at a time ({@link removeStaleLock}).
11
+ */
12
+ export declare function withFileLock<R>(file: string, fn: () => Promise<R>, hooks?: FileLockHooks): Promise<R>;
13
+ //# sourceMappingURL=file-lock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-lock.d.ts","sourceRoot":"","sources":["../src/file-lock.ts"],"names":[],"mappings":"AAOA,kFAAkF;AAClF,MAAM,WAAW,aAAa;IAC5B,qFAAqF;IACrF,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAkCD;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,KAAK,GAAE,aAAkB,GACxB,OAAO,CAAC,CAAC,CAAC,CAqBZ"}
@@ -0,0 +1,70 @@
1
+ import { mkdir, open, stat, unlink } from 'node:fs/promises';
2
+ import { dirname } from 'node:path';
3
+ /** A change takes milliseconds; a lock this old was left by a process that died. */
4
+ const STALE_LOCK_MS = 10_000;
5
+ const LOCK_RETRY_MS = 15;
6
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
7
+ const ageOf = (path) => stat(path).then((st) => Date.now() - st.mtimeMs, () => 0);
8
+ /**
9
+ * Remove a stale lock, but only as the one waiter holding `<lock>.takeover`,
10
+ * and only if the lock is still stale then. Without this, two waiters could
11
+ * both judge the dead lock stale and the slower one would remove the fresh
12
+ * lock the faster one had just taken, so both would change the file at once.
13
+ */
14
+ async function removeStaleLock(lock) {
15
+ const guard = `${lock}.takeover`;
16
+ try {
17
+ await (await open(guard, 'wx')).close();
18
+ }
19
+ catch (err) {
20
+ if (err.code !== 'EEXIST')
21
+ throw err;
22
+ // Another waiter is taking over. A guard this old was left by one that died.
23
+ if ((await ageOf(guard)) > STALE_LOCK_MS)
24
+ await unlink(guard).catch(() => undefined);
25
+ else
26
+ await sleep(LOCK_RETRY_MS);
27
+ return;
28
+ }
29
+ try {
30
+ if ((await ageOf(lock)) > STALE_LOCK_MS)
31
+ await unlink(lock).catch(() => undefined);
32
+ }
33
+ finally {
34
+ await unlink(guard).catch(() => undefined);
35
+ }
36
+ }
37
+ /**
38
+ * Hold `<file>.lock` while `fn` runs, so processes sharing the file change it
39
+ * one at a time. The lock is a file created only if absent (`open(…, 'wx')`,
40
+ * atomic on every platform); a stale one is removed and claimed again, by one
41
+ * waiter at a time ({@link removeStaleLock}).
42
+ */
43
+ export async function withFileLock(file, fn, hooks = {}) {
44
+ const lock = `${file}.lock`;
45
+ await mkdir(dirname(lock), { recursive: true });
46
+ for (;;) {
47
+ try {
48
+ const handle = await open(lock, 'wx');
49
+ await handle.writeFile(String(process.pid)).finally(() => handle.close());
50
+ break;
51
+ }
52
+ catch (err) {
53
+ if (err.code !== 'EEXIST')
54
+ throw err;
55
+ }
56
+ if ((await ageOf(lock)) > STALE_LOCK_MS) {
57
+ await hooks.onStaleLockSeen?.();
58
+ await removeStaleLock(lock);
59
+ }
60
+ else
61
+ await sleep(LOCK_RETRY_MS);
62
+ }
63
+ try {
64
+ return await fn();
65
+ }
66
+ finally {
67
+ await unlink(lock).catch(() => undefined);
68
+ }
69
+ }
70
+ //# sourceMappingURL=file-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-lock.js","sourceRoot":"","sources":["../src/file-lock.ts"],"names":[],"mappings":"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"}
@@ -57,6 +57,13 @@ export interface JsonFileStoreOptions<T extends {
57
57
  readonly stringify?: (fileObject: Record<string, unknown>) => string;
58
58
  /** Atomic-write options forwarded to {@link writeFileAtomic} (e.g. `mode`). */
59
59
  readonly writeOptions?: WriteFileAtomicOptions;
60
+ /**
61
+ * Other processes write this file too (the desktop, the CLI and every
62
+ * runner share `schedules.json`). Re-read it before every read and every
63
+ * change, so one process never misses — or writes over — another's change.
64
+ * Default `false`: the first read is cached until {@link JsonFileStore.invalidate}.
65
+ */
66
+ readonly sharedWithOtherProcesses?: boolean;
60
67
  }
61
68
  /**
62
69
  * The collection store handle. Domain stores compose this and add their own
@@ -1 +1 @@
1
- {"version":3,"file":"json-file-store.d.ts","sourceRoot":"","sources":["../src/json-file-store.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmB,KAAK,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAE7E;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,oBAAoB,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE;IAC5D,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IAC1D;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IAC5D;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9C;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC;IACrE,+EAA+E;IAC/E,QAAQ,CAAC,YAAY,CAAC,EAAE,sBAAsB,CAAC;CAChD;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE;IACrD;;;;;OAKG;IACH,IAAI,IAAI,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IACrB,2CAA2C;IAC3C,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACnC;;;;OAIG;IACH,MAAM,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,4DAA4D;IAC5D,UAAU,IAAI,IAAI,CAAC;CACpB;AAID,wBAAgB,mBAAmB,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EAC1D,IAAI,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAC5B,aAAa,CAAC,CAAC,CAAC,CA0ElB"}
1
+ {"version":3,"file":"json-file-store.d.ts","sourceRoot":"","sources":["../src/json-file-store.ts"],"names":[],"mappings":"AAGA,OAAO,EAAmB,KAAK,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAE7E;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,oBAAoB,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE;IAC5D,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IAC1D;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IAC5D;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9C;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC;IACrE,+EAA+E;IAC/E,QAAQ,CAAC,YAAY,CAAC,EAAE,sBAAsB,CAAC;IAC/C;;;;;OAKG;IACH,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAC;CAC7C;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE;IACrD;;;;;OAKG;IACH,IAAI,IAAI,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IACrB,2CAA2C;IAC3C,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACnC;;;;OAIG;IACH,MAAM,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,4DAA4D;IAC5D,UAAU,IAAI,IAAI,CAAC;CACpB;AAID,wBAAgB,mBAAmB,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EAC1D,IAAI,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAC5B,aAAa,CAAC,CAAC,CAAC,CAkFlB"}
@@ -1,16 +1,17 @@
1
1
  import { readFile } from 'node:fs/promises';
2
+ import { withFileLock } from './file-lock.js';
2
3
  import { createMutex } from './mutex.js';
3
4
  import { writeFileAtomic } from './fs-utils.js';
4
5
  const prettyJson = (value) => JSON.stringify(value, null, 2);
5
6
  export function createJsonFileStore(opts) {
6
- const { file, load, onReadError, itemsKey = 'items', fileFields = { version: 1 }, stringify = prettyJson, writeOptions, } = opts;
7
+ const { file, load, onReadError, itemsKey = 'items', fileFields = { version: 1 }, stringify = prettyJson, writeOptions, sharedWithOtherProcesses = false, } = opts;
7
8
  let cache = null;
8
9
  // In-flight load so a burst of concurrent cold reads coalesces into one
9
10
  // filesystem read + one parse pass, and the second loader can't clobber the
10
11
  // first's cache assignment with a stale/half-applied snapshot.
11
12
  let loading = null;
12
13
  const mutex = createMutex();
13
- async function loadIntoCache() {
14
+ async function readItems() {
14
15
  let raw;
15
16
  try {
16
17
  raw = await readFile(file, 'utf8');
@@ -20,17 +21,19 @@ export function createJsonFileStore(opts) {
20
21
  raw = null;
21
22
  }
22
23
  else if (onReadError) {
23
- cache = await onReadError(err);
24
- return;
24
+ return onReadError(err);
25
25
  }
26
26
  else {
27
27
  throw err;
28
28
  }
29
29
  }
30
- cache = await load(raw);
30
+ return load(raw);
31
+ }
32
+ async function loadIntoCache() {
33
+ cache = await readItems();
31
34
  }
32
35
  function ensureLoaded() {
33
- if (cache)
36
+ if (cache && !sharedWithOtherProcesses)
34
37
  return Promise.resolve();
35
38
  if (loading)
36
39
  return loading;
@@ -53,8 +56,13 @@ export function createJsonFileStore(opts) {
53
56
  return cache.find((item) => item.id === id) ?? null;
54
57
  },
55
58
  async mutate(fn) {
56
- await mutex.run(async () => {
57
- await ensureLoaded();
59
+ const change = async () => {
60
+ // A shared file is read afresh here, never joined to a read that began
61
+ // before the last write, so the change lands on the file as it is now.
62
+ if (sharedWithOtherProcesses)
63
+ cache = await readItems();
64
+ else
65
+ await ensureLoaded();
58
66
  const updated = await fn(cache.slice());
59
67
  // Persist first so a write failure (ENOSPC/EACCES/EIO) leaves the
60
68
  // in-memory cache consistent with disk — advancing the cache before the
@@ -62,7 +70,8 @@ export function createJsonFileStore(opts) {
62
70
  // mutate and silently defeat the crash-atomic guarantee.
63
71
  await persist(updated);
64
72
  cache = updated;
65
- });
73
+ };
74
+ await mutex.run(() => (sharedWithOtherProcesses ? withFileLock(file, change) : change()));
66
75
  },
67
76
  invalidate() {
68
77
  cache = null;
@@ -1 +1 @@
1
- {"version":3,"file":"json-file-store.js","sourceRoot":"","sources":["../src/json-file-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"json-file-store.js","sourceRoot":"","sources":["../src/json-file-store.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moxxy/sdk",
3
- "version": "0.46.0",
3
+ "version": "0.47.0",
4
4
  "description": "Typed public surface for the moxxy framework: event types, define* factories, lifecycle hook signatures.",
5
5
  "keywords": [
6
6
  "moxxy",
package/src/cron.test.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
  import { assertDefined } from './assert.js';
3
- import { isValidCron, isValidTimeZone, nextFireTime, parseCron } from './cron.js';
3
+ import { firstFireBetween, isValidCron, isValidTimeZone, nextFireTime, parseCron } from './cron.js';
4
4
 
5
5
  describe('parseCron', () => {
6
6
  it('parses every-minute', () => {
@@ -263,6 +263,29 @@ describe('nextFireTime', () => {
263
263
  });
264
264
  });
265
265
 
266
+ describe('firstFireBetween', () => {
267
+ const at = (hour: number, minute: number, second = 0) => new Date(2026, 9, 30, hour, minute, second).getTime();
268
+
269
+ it('finds the first fire inside the window', () => {
270
+ expect(firstFireBetween('*/5 * * * *', at(3, 47), at(4, 0))?.getTime()).toBe(at(3, 50));
271
+ });
272
+
273
+ it('counts both ends of the window', () => {
274
+ expect(firstFireBetween('*/5 * * * *', at(3, 50), at(4, 0))?.getTime()).toBe(at(3, 50));
275
+ expect(firstFireBetween('*/5 * * * *', at(3, 47), at(3, 50))?.getTime()).toBe(at(3, 50));
276
+ });
277
+
278
+ it('is null when no fire falls inside the window', () => {
279
+ expect(firstFireBetween('*/5 * * * *', at(3, 47), at(3, 49))).toBeNull();
280
+ expect(firstFireBetween('0 9 31 2 *', at(3, 47), at(3, 49))).toBeNull();
281
+ });
282
+
283
+ it('never counts the whole minute before a window that opens mid-minute', () => {
284
+ expect(firstFireBetween('* * * * *', at(3, 47, 30), at(3, 48))?.getTime()).toBe(at(3, 48));
285
+ expect(firstFireBetween('50 3 * * *', at(3, 50, 30), at(3, 59))).toBeNull();
286
+ });
287
+ });
288
+
266
289
  describe('isValidTimeZone', () => {
267
290
  it('accepts undefined / local / real IANA zones', () => {
268
291
  expect(isValidTimeZone(undefined)).toBe(true);
package/src/cron.ts CHANGED
@@ -251,6 +251,18 @@ export function nextFireTime(
251
251
  return null;
252
252
  }
253
253
 
254
+ /**
255
+ * The first fire from `fromMs` to `toMs` (both inclusive), or null when none
256
+ * falls inside. A `fromMs` inside a minute looks from the next whole minute:
257
+ * cron fires on whole minutes, and the one just gone is before the window.
258
+ */
259
+ export function firstFireBetween(expr: string, fromMs: number, toMs: number, timeZone?: string): Date | null {
260
+ const firstMinute = Math.ceil(fromMs / 60_000) * 60_000;
261
+ // nextFireTime looks from the whole minute after `after`.
262
+ const next = nextFireTime(expr, new Date(firstMinute - 60_000), timeZone);
263
+ return next !== null && next.getTime() <= toMs ? next : null;
264
+ }
265
+
254
266
  interface DateParts {
255
267
  readonly year: number;
256
268
  readonly month: number; // 1-12
@@ -0,0 +1,59 @@
1
+ import { mkdtemp, readdir, utimes, writeFile } from 'node:fs/promises';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
5
+ import { removeDir } from '@moxxy/vitest-preset/fs';
6
+ import { withFileLock } from './file-lock.js';
7
+
8
+ describe('withFileLock', () => {
9
+ let dir: string;
10
+ let file: string;
11
+
12
+ beforeEach(async () => {
13
+ dir = await mkdtemp(join(tmpdir(), 'moxxy-file-lock-'));
14
+ file = join(dir, 'store.json');
15
+ });
16
+ afterEach(async () => {
17
+ await removeDir(dir);
18
+ });
19
+
20
+ async function leaveStale(path: string): Promise<void> {
21
+ await writeFile(path, '12345');
22
+ const longAgo = new Date(Date.now() - 60_000);
23
+ await utimes(path, longAgo, longAgo);
24
+ }
25
+
26
+ it('lets only one of two waiters take over a lock left behind', async () => {
27
+ // Both waiters judge the same dead lock stale. The first takes it over and
28
+ // starts its change; only then does the second act on its stale verdict.
29
+ // It must not remove the first one's fresh lock and run at the same time.
30
+ await leaveStale(`${file}.lock`);
31
+ let inside = 0;
32
+ let mostAtOnce = 0;
33
+ let firstInside!: () => void;
34
+ const firstIsInside = new Promise<void>((resolve) => (firstInside = resolve));
35
+ const change = (onEnter?: () => void) => async () => {
36
+ inside += 1;
37
+ mostAtOnce = Math.max(mostAtOnce, inside);
38
+ onEnter?.();
39
+ await new Promise((r) => setTimeout(r, 50));
40
+ inside -= 1;
41
+ };
42
+
43
+ await Promise.all([
44
+ withFileLock(file, change(firstInside)),
45
+ withFileLock(file, change(), { onStaleLockSeen: () => firstIsInside }),
46
+ ]);
47
+
48
+ expect(mostAtOnce).toBe(1);
49
+ expect(await readdir(dir)).toEqual([]);
50
+ });
51
+
52
+ it('still takes over when a waiter died while taking over a lock', async () => {
53
+ await leaveStale(`${file}.lock`);
54
+ await leaveStale(`${file}.lock.takeover`);
55
+
56
+ expect(await withFileLock(file, async () => 'done')).toBe('done');
57
+ expect(await readdir(dir)).toEqual([]);
58
+ });
59
+ });
@@ -0,0 +1,77 @@
1
+ import { mkdir, open, stat, unlink } from 'node:fs/promises';
2
+ import { dirname } from 'node:path';
3
+
4
+ /** A change takes milliseconds; a lock this old was left by a process that died. */
5
+ const STALE_LOCK_MS = 10_000;
6
+ const LOCK_RETRY_MS = 15;
7
+
8
+ /** Test seams only: let a test hold a waiter at the exact moment a race needs. */
9
+ export interface FileLockHooks {
10
+ /** Called when this waiter has judged the lock stale, before anything is removed. */
11
+ readonly onStaleLockSeen?: () => Promise<void>;
12
+ }
13
+
14
+ const sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));
15
+
16
+ const ageOf = (path: string): Promise<number> =>
17
+ stat(path).then(
18
+ (st) => Date.now() - st.mtimeMs,
19
+ () => 0,
20
+ );
21
+
22
+ /**
23
+ * Remove a stale lock, but only as the one waiter holding `<lock>.takeover`,
24
+ * and only if the lock is still stale then. Without this, two waiters could
25
+ * both judge the dead lock stale and the slower one would remove the fresh
26
+ * lock the faster one had just taken, so both would change the file at once.
27
+ */
28
+ async function removeStaleLock(lock: string): Promise<void> {
29
+ const guard = `${lock}.takeover`;
30
+ try {
31
+ await (await open(guard, 'wx')).close();
32
+ } catch (err) {
33
+ if ((err as NodeJS.ErrnoException).code !== 'EEXIST') throw err;
34
+ // Another waiter is taking over. A guard this old was left by one that died.
35
+ if ((await ageOf(guard)) > STALE_LOCK_MS) await unlink(guard).catch(() => undefined);
36
+ else await sleep(LOCK_RETRY_MS);
37
+ return;
38
+ }
39
+ try {
40
+ if ((await ageOf(lock)) > STALE_LOCK_MS) await unlink(lock).catch(() => undefined);
41
+ } finally {
42
+ await unlink(guard).catch(() => undefined);
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Hold `<file>.lock` while `fn` runs, so processes sharing the file change it
48
+ * one at a time. The lock is a file created only if absent (`open(…, 'wx')`,
49
+ * atomic on every platform); a stale one is removed and claimed again, by one
50
+ * waiter at a time ({@link removeStaleLock}).
51
+ */
52
+ export async function withFileLock<R>(
53
+ file: string,
54
+ fn: () => Promise<R>,
55
+ hooks: FileLockHooks = {},
56
+ ): Promise<R> {
57
+ const lock = `${file}.lock`;
58
+ await mkdir(dirname(lock), { recursive: true });
59
+ for (;;) {
60
+ try {
61
+ const handle = await open(lock, 'wx');
62
+ await handle.writeFile(String(process.pid)).finally(() => handle.close());
63
+ break;
64
+ } catch (err) {
65
+ if ((err as NodeJS.ErrnoException).code !== 'EEXIST') throw err;
66
+ }
67
+ if ((await ageOf(lock)) > STALE_LOCK_MS) {
68
+ await hooks.onStaleLockSeen?.();
69
+ await removeStaleLock(lock);
70
+ } else await sleep(LOCK_RETRY_MS);
71
+ }
72
+ try {
73
+ return await fn();
74
+ } finally {
75
+ await unlink(lock).catch(() => undefined);
76
+ }
77
+ }
@@ -1,4 +1,4 @@
1
- import { mkdir, mkdtemp, readFile, readdir, writeFile } from 'node:fs/promises';
1
+ import { mkdir, mkdtemp, readFile, readdir, utimes, writeFile } from 'node:fs/promises';
2
2
  import { tmpdir } from 'node:os';
3
3
  import { join } from 'node:path';
4
4
  import { afterEach, beforeEach, describe, expect, it } from 'vitest';
@@ -246,4 +246,67 @@ describe('createJsonFileStore', () => {
246
246
  // …until invalidated.
247
247
  expect(await s.read()).toEqual([{ id: 'b', n: 2 }]);
248
248
  });
249
+
250
+ describe('sharedWithOtherProcesses', () => {
251
+ function shared() {
252
+ return createJsonFileStore<Item>({ file, load: lenientLoad, sharedWithOtherProcesses: true });
253
+ }
254
+
255
+ it('reads changes another process made after its first read', async () => {
256
+ const mine = shared();
257
+ expect(await mine.read()).toEqual([]);
258
+ await shared().mutate((items) => [...items, { id: 'a', n: 1 }]);
259
+ expect(await mine.read()).toEqual([{ id: 'a', n: 1 }]);
260
+ expect(await mine.get('a')).toEqual({ id: 'a', n: 1 });
261
+ });
262
+
263
+ it('lets one process change the file at a time, so two changes at once both land', async () => {
264
+ // Two stores on one file stand for two processes: each has its own
265
+ // in-process mutex, so only a lock on the file itself keeps them apart.
266
+ const slowAdd = (item: Item) => async (items: Item[]) => {
267
+ await new Promise((r) => setTimeout(r, 30));
268
+ return [...items, item];
269
+ };
270
+ await Promise.all([
271
+ shared().mutate(slowAdd({ id: 'a', n: 1 })),
272
+ shared().mutate(slowAdd({ id: 'b', n: 2 })),
273
+ ]);
274
+ expect((await store().read()).map((i) => i.id).sort()).toEqual(['a', 'b']);
275
+ expect(await readdir(join(dir, 'sub'))).toEqual(['store.json']);
276
+ });
277
+
278
+ it('takes over a lock left behind by a process that died while writing', async () => {
279
+ await mkdir(join(dir, 'sub'), { recursive: true });
280
+ const lock = `${file}.lock`;
281
+ await writeFile(lock, '12345');
282
+ const longAgo = new Date(Date.now() - 60_000);
283
+ await utimes(lock, longAgo, longAgo);
284
+
285
+ await shared().mutate((items) => [...items, { id: 'a', n: 1 }]);
286
+
287
+ expect(await store().read()).toEqual([{ id: 'a', n: 1 }]);
288
+ expect(await readdir(join(dir, 'sub'))).toEqual(['store.json']);
289
+ });
290
+
291
+ it('releases the lock when the change fails', async () => {
292
+ await expect(
293
+ shared().mutate(() => {
294
+ throw new Error('boom');
295
+ }),
296
+ ).rejects.toThrow('boom');
297
+ await shared().mutate((items) => [...items, { id: 'a', n: 1 }]);
298
+ expect(await store().read()).toEqual([{ id: 'a', n: 1 }]);
299
+ });
300
+
301
+ it('changes the file as it is now, not as it was at its first read', async () => {
302
+ const mine = shared();
303
+ expect(await mine.read()).toEqual([]);
304
+ await shared().mutate((items) => [...items, { id: 'a', n: 1 }]);
305
+ await mine.mutate((items) => [...items, { id: 'b', n: 2 }]);
306
+ expect(await store().read()).toEqual([
307
+ { id: 'a', n: 1 },
308
+ { id: 'b', n: 2 },
309
+ ]);
310
+ });
311
+ });
249
312
  });
@@ -1,4 +1,5 @@
1
1
  import { readFile } from 'node:fs/promises';
2
+ import { withFileLock } from './file-lock.js';
2
3
  import { createMutex, type Mutex } from './mutex.js';
3
4
  import { writeFileAtomic, type WriteFileAtomicOptions } from './fs-utils.js';
4
5
 
@@ -58,6 +59,13 @@ export interface JsonFileStoreOptions<T extends { id: string }> {
58
59
  readonly stringify?: (fileObject: Record<string, unknown>) => string;
59
60
  /** Atomic-write options forwarded to {@link writeFileAtomic} (e.g. `mode`). */
60
61
  readonly writeOptions?: WriteFileAtomicOptions;
62
+ /**
63
+ * Other processes write this file too (the desktop, the CLI and every
64
+ * runner share `schedules.json`). Re-read it before every read and every
65
+ * change, so one process never misses — or writes over — another's change.
66
+ * Default `false`: the first read is cached until {@link JsonFileStore.invalidate}.
67
+ */
68
+ readonly sharedWithOtherProcesses?: boolean;
61
69
  }
62
70
 
63
71
  /**
@@ -97,6 +105,7 @@ export function createJsonFileStore<T extends { id: string }>(
97
105
  fileFields = { version: 1 },
98
106
  stringify = prettyJson,
99
107
  writeOptions,
108
+ sharedWithOtherProcesses = false,
100
109
  } = opts;
101
110
 
102
111
  let cache: T[] | null = null;
@@ -106,7 +115,7 @@ export function createJsonFileStore<T extends { id: string }>(
106
115
  let loading: Promise<void> | null = null;
107
116
  const mutex: Mutex = createMutex();
108
117
 
109
- async function loadIntoCache(): Promise<void> {
118
+ async function readItems(): Promise<T[]> {
110
119
  let raw: string | null;
111
120
  try {
112
121
  raw = await readFile(file, 'utf8');
@@ -114,17 +123,20 @@ export function createJsonFileStore<T extends { id: string }>(
114
123
  if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
115
124
  raw = null;
116
125
  } else if (onReadError) {
117
- cache = await onReadError(err);
118
- return;
126
+ return onReadError(err);
119
127
  } else {
120
128
  throw err;
121
129
  }
122
130
  }
123
- cache = await load(raw);
131
+ return load(raw);
132
+ }
133
+
134
+ async function loadIntoCache(): Promise<void> {
135
+ cache = await readItems();
124
136
  }
125
137
 
126
138
  function ensureLoaded(): Promise<void> {
127
- if (cache) return Promise.resolve();
139
+ if (cache && !sharedWithOtherProcesses) return Promise.resolve();
128
140
  if (loading) return loading;
129
141
  loading = loadIntoCache().finally(() => {
130
142
  loading = null;
@@ -147,8 +159,11 @@ export function createJsonFileStore<T extends { id: string }>(
147
159
  return cache!.find((item) => item.id === id) ?? null;
148
160
  },
149
161
  async mutate(fn): Promise<void> {
150
- await mutex.run(async () => {
151
- await ensureLoaded();
162
+ const change = async (): Promise<void> => {
163
+ // A shared file is read afresh here, never joined to a read that began
164
+ // before the last write, so the change lands on the file as it is now.
165
+ if (sharedWithOtherProcesses) cache = await readItems();
166
+ else await ensureLoaded();
152
167
  const updated = await fn(cache!.slice());
153
168
  // Persist first so a write failure (ENOSPC/EACCES/EIO) leaves the
154
169
  // in-memory cache consistent with disk — advancing the cache before the
@@ -156,7 +171,8 @@ export function createJsonFileStore<T extends { id: string }>(
156
171
  // mutate and silently defeat the crash-atomic guarantee.
157
172
  await persist(updated);
158
173
  cache = updated;
159
- });
174
+ };
175
+ await mutex.run(() => (sharedWithOtherProcesses ? withFileLock(file, change) : change()));
160
176
  },
161
177
  invalidate(): void {
162
178
  cache = null;