@daloyjs/core 1.3.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/app.d.ts CHANGED
@@ -1600,7 +1600,9 @@ export declare class App<Routes extends readonly RouteDefinition<any, any, any,
1600
1600
  *
1601
1601
  * @param def - The task definition (schedule), same shape as {@link App.cron}.
1602
1602
  * @param job - The job to enqueue on each tick. `payload` defaults to
1603
- * `{ scheduledFor: <ISO time of the tick> }`.
1603
+ * `{ scheduledFor: <ISO time of the tick slot> }`, floored to the same
1604
+ * slot the idempotency key uses so replicas ticking milliseconds apart
1605
+ * enqueue byte-identical payloads and dedupe instead of conflicting.
1604
1606
  * @returns This `App` instance for chaining.
1605
1607
  * @throws {@link JobConfigError} (`store_required`) when called before
1606
1608
  * {@link App.useJobs} — fail fast at registration, not at the first tick.
package/dist/app.js CHANGED
@@ -2161,7 +2161,9 @@ export class App {
2161
2161
  *
2162
2162
  * @param def - The task definition (schedule), same shape as {@link App.cron}.
2163
2163
  * @param job - The job to enqueue on each tick. `payload` defaults to
2164
- * `{ scheduledFor: <ISO time of the tick> }`.
2164
+ * `{ scheduledFor: <ISO time of the tick slot> }`, floored to the same
2165
+ * slot the idempotency key uses so replicas ticking milliseconds apart
2166
+ * enqueue byte-identical payloads and dedupe instead of conflicting.
2165
2167
  * @returns This `App` instance for chaining.
2166
2168
  * @throws {@link JobConfigError} (`store_required`) when called before
2167
2169
  * {@link App.useJobs} — fail fast at registration, not at the first tick.
@@ -2177,7 +2179,10 @@ export class App {
2177
2179
  const slot = Math.floor(scheduledFor.getTime() / granularityMs);
2178
2180
  await queue.enqueue({
2179
2181
  name: job.name,
2180
- payload: job.payload ?? { scheduledFor: scheduledFor.toISOString() },
2182
+ // Derive the default payload from the slot, not the raw tick time:
2183
+ // replicas tick a few milliseconds apart, and an identical key with
2184
+ // a different payload is a JobIdempotencyConflictError, not a dedup.
2185
+ payload: job.payload ?? { scheduledFor: new Date(slot * granularityMs).toISOString() },
2181
2186
  ...(job.queue !== undefined ? { queue: job.queue } : {}),
2182
2187
  ...(job.priority !== undefined ? { priority: job.priority } : {}),
2183
2188
  idempotencyKey: `cron:${encodeURIComponent(name)}:${slot}`,
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "bomFormat": "CycloneDX",
3
3
  "specVersion": "1.5",
4
- "serialNumber": "urn:uuid:4d270580-4d68-58c8-b0b2-a9f70a933061",
4
+ "serialNumber": "urn:uuid:3be788cc-3073-595a-871d-593791c06327",
5
5
  "version": 1,
6
6
  "metadata": {
7
- "timestamp": "2026-08-28T11:08:31.801Z",
7
+ "timestamp": "2026-08-29T14:05:07.830Z",
8
8
  "tools": [
9
9
  {
10
10
  "vendor": "DaloyJS",
11
11
  "name": "daloy-generate-sbom",
12
- "version": "1.3.0"
12
+ "version": "1.3.1"
13
13
  }
14
14
  ],
15
15
  "authors": [
@@ -19,11 +19,11 @@
19
19
  ],
20
20
  "component": {
21
21
  "type": "library",
22
- "bom-ref": "pkg:npm/@daloyjs/core@1.3.0",
22
+ "bom-ref": "pkg:npm/@daloyjs/core@1.3.1",
23
23
  "name": "@daloyjs/core",
24
- "version": "1.3.0",
24
+ "version": "1.3.1",
25
25
  "description": "DaloyJS is a runtime-portable, contract-first TypeScript web framework with built-in OpenAPI (Hey API), typed client generation, large-scale maintainability, and security-first defaults. Hono-grade portability, Elysia-grade DX, FastAPI-grade docs, Fastify-grade ops — distributed via pnpm.",
26
- "purl": "pkg:npm/@daloyjs/core@1.3.0",
26
+ "purl": "pkg:npm/@daloyjs/core@1.3.1",
27
27
  "licenses": [
28
28
  {
29
29
  "license": {
@@ -46,9 +46,9 @@
46
46
  }
47
47
  ],
48
48
  "swid": {
49
- "tagId": "swidtag--daloyjs-core-1.3.0",
49
+ "tagId": "swidtag--daloyjs-core-1.3.1",
50
50
  "name": "@daloyjs/core",
51
- "version": "1.3.0",
51
+ "version": "1.3.1",
52
52
  "tagVersion": 0,
53
53
  "patch": false
54
54
  }
@@ -57,7 +57,7 @@
57
57
  "components": [],
58
58
  "dependencies": [
59
59
  {
60
- "ref": "pkg:npm/@daloyjs/core@1.3.0",
60
+ "ref": "pkg:npm/@daloyjs/core@1.3.1",
61
61
  "dependsOn": []
62
62
  }
63
63
  ]
@@ -2,10 +2,10 @@
2
2
  "spdxVersion": "SPDX-2.3",
3
3
  "dataLicense": "CC0-1.0",
4
4
  "SPDXID": "SPDXRef-DOCUMENT",
5
- "name": "@daloyjs/core-1.3.0",
6
- "documentNamespace": "https://github.com/daloyjs/daloy/sbom/@daloyjs/core-1.3.0-4d270580-4d68-58c8-b0b2-a9f70a933061",
5
+ "name": "@daloyjs/core-1.3.1",
6
+ "documentNamespace": "https://github.com/daloyjs/daloy/sbom/@daloyjs/core-1.3.1-3be788cc-3073-595a-871d-593791c06327",
7
7
  "creationInfo": {
8
- "created": "2026-08-28T11:08:31.801Z",
8
+ "created": "2026-08-29T14:05:07.830Z",
9
9
  "creators": [
10
10
  "Tool: daloy-generate-sbom",
11
11
  "Organization: DaloyJS"
@@ -16,7 +16,7 @@
16
16
  {
17
17
  "SPDXID": "SPDXRef-Package--daloyjs-core",
18
18
  "name": "@daloyjs/core",
19
- "versionInfo": "1.3.0",
19
+ "versionInfo": "1.3.1",
20
20
  "downloadLocation": "https://github.com/daloyjs/daloy",
21
21
  "filesAnalyzed": false,
22
22
  "licenseConcluded": "MIT",
@@ -27,7 +27,7 @@
27
27
  {
28
28
  "referenceCategory": "PACKAGE-MANAGER",
29
29
  "referenceType": "purl",
30
- "referenceLocator": "pkg:npm/@daloyjs/core@1.3.0"
30
+ "referenceLocator": "pkg:npm/@daloyjs/core@1.3.1"
31
31
  }
32
32
  ]
33
33
  }
package/dist/scheduler.js CHANGED
@@ -487,16 +487,21 @@ export class Scheduler {
487
487
  }, 0);
488
488
  return;
489
489
  }
490
- const delay = this.#nextDelay(task);
491
- task.nextRunAt = this.#now() + delay;
490
+ // One clock read anchors both the delay and nextRunAt: splitting them
491
+ // let a millisecond slip between the two reads push nextRunAt (and so
492
+ // the tick's scheduledFor) off the cron boundary by a per-process
493
+ // delta, which broke cronEnqueue's cross-replica payload dedup.
494
+ const now = this.#now();
495
+ const delay = this.#nextDelay(task, now);
496
+ task.nextRunAt = now + delay;
492
497
  task.timer = this.#timers.set(() => {
493
498
  void this.#tick(task);
494
499
  }, delay);
495
500
  }
496
- #nextDelay(task) {
501
+ #nextDelay(task, now) {
497
502
  if (task.fields !== undefined) {
498
- const next = nextCronRun(task.fields, new Date(this.#now()), task.def.timeZone);
499
- return Math.max(0, next.getTime() - this.#now());
503
+ const next = nextCronRun(task.fields, new Date(now), task.def.timeZone);
504
+ return Math.max(0, next.getTime() - now);
500
505
  }
501
506
  return task.def.intervalMs;
502
507
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daloyjs/core",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "DaloyJS is a runtime-portable, contract-first TypeScript web framework with built-in OpenAPI (Hey API), typed client generation, large-scale maintainability, and security-first defaults. Hono-grade portability, Elysia-grade DX, FastAPI-grade docs, Fastify-grade ops \u2014 distributed via pnpm.",
5
5
  "type": "module",
6
6
  "publishConfig": {