@nuetzliches/croniq-runner 0.2.0 → 0.4.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 (51) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +25 -2
  3. package/dist/client.d.ts +61 -0
  4. package/dist/client.d.ts.map +1 -1
  5. package/dist/client.js +87 -0
  6. package/dist/client.js.map +1 -1
  7. package/dist/context.d.ts +9 -0
  8. package/dist/context.d.ts.map +1 -1
  9. package/dist/context.js +2 -0
  10. package/dist/context.js.map +1 -1
  11. package/dist/dispatcher.d.ts +1 -1
  12. package/dist/dispatcher.d.ts.map +1 -1
  13. package/dist/dispatcher.js +39 -8
  14. package/dist/dispatcher.js.map +1 -1
  15. package/dist/duration.d.ts +6 -0
  16. package/dist/duration.d.ts.map +1 -1
  17. package/dist/duration.js +11 -0
  18. package/dist/duration.js.map +1 -1
  19. package/dist/index.d.ts +2 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +2 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/log-writer.d.ts +1 -1
  24. package/dist/log-writer.d.ts.map +1 -1
  25. package/dist/log-writer.js +17 -3
  26. package/dist/log-writer.js.map +1 -1
  27. package/dist/logger.d.ts.map +1 -1
  28. package/dist/logger.js +10 -2
  29. package/dist/logger.js.map +1 -1
  30. package/dist/options.d.ts +42 -1
  31. package/dist/options.d.ts.map +1 -1
  32. package/dist/options.js +21 -1
  33. package/dist/options.js.map +1 -1
  34. package/dist/protocol.d.ts +5 -0
  35. package/dist/protocol.d.ts.map +1 -1
  36. package/dist/runner.d.ts.map +1 -1
  37. package/dist/runner.js +145 -6
  38. package/dist/runner.js.map +1 -1
  39. package/dist/sanitize.d.ts +82 -0
  40. package/dist/sanitize.d.ts.map +1 -0
  41. package/dist/sanitize.js +157 -0
  42. package/dist/sanitize.js.map +1 -0
  43. package/dist/security.d.ts +19 -0
  44. package/dist/security.d.ts.map +1 -0
  45. package/dist/security.js +83 -0
  46. package/dist/security.js.map +1 -0
  47. package/dist/trigger.d.ts +21 -1
  48. package/dist/trigger.d.ts.map +1 -1
  49. package/dist/trigger.js +4 -0
  50. package/dist/trigger.js.map +1 -1
  51. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,72 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0 - 2026-08-21
4
+
5
+ ### Added
6
+
7
+ - **A ceiling on consecutive authentication failures
8
+ ([#473](https://github.com/nuetzliches/croniq/issues/473)).** New `maxConsecutiveAuthFailures` option (default `3`, range `[1, 100]`) budgets
9
+ consecutive `401 Unauthorized` responses to `POST /v1/work/poll`. On
10
+ exhaustion the runner stops with the new exported `AuthFailedError`, which carries the streak length and
11
+ names the remedy: restart with the current key. The counter resets on a
12
+ successful poll and on any other failure — a 5xx says nothing about whether
13
+ the credential is valid.
14
+
15
+ **Behaviour change.** A `401` was previously classified as transient, so a
16
+ runner whose key was revoked retried it every poll interval forever. The
17
+ credential is read once, at construction, and never re-read, so retrying
18
+ could not clear it: the process stayed up, looked healthy, did nothing, and
19
+ never exited non-zero — which meant no supervisor ever restarted it, and
20
+ restarting is exactly what would have picked up the new key.
21
+
22
+ Unlike the `403` of #437 the first `401` is *not* fatal. Key rotation hands
23
+ over by installing the new key and giving the old one an expiry
24
+ ([#471](https://github.com/nuetzliches/croniq/issues/471)), so dying on a
25
+ single rejection would turn a narrow race around that handover into an
26
+ outage. Conformance case `17-poll-401-auth-ceiling.yaml` pins the contract on
27
+ the wire across all five runner bindings.
28
+
29
+ - **A ceiling on consecutive poll conflicts ([#466](https://github.com/nuetzliches/croniq/issues/466)).** New `maxConsecutivePollConflicts` option (default `3`, range `[1, 100]`) budgets consecutive `409 Conflict` responses to `POST /v1/work/poll`. On exhaustion `run()` rejects with the new exported `PollInstanceConflictError`, which carries `runnerId` and `consecutiveCount` and names the remedy: stop the duplicate process or rotate the `runner_id`. The counter resets on a successful poll or on any non-409 failure (5xx, network, timeout), which say nothing about instance ownership.
30
+
31
+ **Behaviour change.** A sustained `409` previously retried forever. One conflict is still transient — a deposed instance may win its identity back, and conformance case 11 pins that it is retried — but a *streak* of them is a duplicate deployment, two processes started with the same fixed `runnerId`, and retrying that forever left the misconfiguration behind a warning that scrolled past. The runner now exits so the process can fail non-zero and reach monitoring, matching what the Rust and .NET SDKs have done since [#134](https://github.com/nuetzliches/croniq/issues/134) sub-item 1. Set the option to `100` to get close to the old behaviour. The `403` half was already symmetric across the SDKs (#437/#458); this closes the `409` half.
32
+
33
+ Conformance case `16-poll-409-conflict-ceiling.yaml` pins the contract on the wire and now runs green in all five bindings — including .NET, whose implementation had no corpus coverage until now.
34
+
35
+ ### Fixed
36
+
37
+ - **A `403` on the work endpoints is fatal ([#437](https://github.com/nuetzliches/croniq/issues/437)).** Since server issue #436 bound a runner's identity to the authenticated caller, `/v1/work/*` answers `403` when the credential does not own the `runner_id` the request names. The poll loop retried that forever on `pollRetryDelayMs`, so a fenced-out runner looked idle rather than misconfigured. A `403` is permanent — no retry can clear it — so `run()` now rejects with the new exported `RunnerOwnershipDeniedError` on the first one. It carries `runnerId` and names both fixes: give the runner its own `runner_id`, or release the existing binding with `DELETE /v1/runners/{id}`. The drain step still runs first, so in-flight handlers get their grace period before the rejection surfaces.
38
+
39
+ - **A `403` on ack, renew or log events is visible ([#437](https://github.com/nuetzliches/croniq/issues/437)).** These paths never inspected the status, so an ownership refusal was indistinguishable from a 5xx: `logger.debug` for renew, `logger.warn` for a dropped log batch. Each now logs at `logger.error` with the remedy, because each has its own consequence — an unacked execution stays claimed until its lease expires, a refused renew means the lease expires mid-handler, and a refused batch means the execution produces no log output. Renew's `404`/`409` — routine when a renew races the runner's own completion, see server issue #438 — stay at `debug`.
40
+
41
+ ### Security
42
+
43
+ - **HTTPS is required for a non-loopback `serverUrl` ([#440](https://github.com/nuetzliches/croniq/issues/440)).** `serverUrl` was only checked for parseability, so an operator who kept the documented `http://localhost:4000` shape and swapped in a real host shipped the API key as a cleartext `Authorization` header on every poll — and, because undici honours `HTTP_PROXY` by default, through any configured proxy as well. `resolveOptions` (i.e. `createRunner`) and the `CroniqTriggerClient` constructor now validate the scheme up front, so a misconfiguration fails fast instead of on the first poll. `https://` is always accepted; `http://` only when the host is loopback (`localhost`, `127.0.0.0/8`, `::1`, including `[::1]`), keeping the documented quickstart working; anything else throws a `TypeError` naming the URL and the opt-in. The new `allowInsecureHttp: true` option accepts a cleartext URL deliberately and emits one loud `logger.warn` instead. `CroniqTriggerClientOptions` gained a `logger` option so that warning can be routed; `isLoopbackHostname` is exported for callers that want the same classification.
44
+
45
+ - **`job_key` and `execution_id` no longer reach log messages, and are validated
46
+ on ingest ([#441](https://github.com/nuetzliches/croniq/issues/441)).**
47
+ `dispatcher.ts`, `runner.ts` and `log-writer.ts` interpolated both identifiers
48
+ into their messages via template literals, and `consoleLogger` then wrote the
49
+ message to the console verbatim — so a server-supplied value carrying CRLF
50
+ forged log records and one carrying ANSI escapes reached the operator's
51
+ terminal raw. Both identifiers now travel only in the `fields` map, which is
52
+ rendered with `JSON.stringify` and therefore already escaped, and
53
+ `consoleLogger` escapes control characters (C0 including ESC, DEL, and C1) in
54
+ the message before writing. The runner additionally validates both identifiers
55
+ before dispatching. A `job_key` is refused only for containing a control
56
+ character — C0, DEL or C1 — or exceeding 256 scalar values; every printable
57
+ character in any script is accepted, interior spaces included, because
58
+ `job "billing:monthly invoice" { … }` is legal DSL and `POST /v1/jobs`
59
+ constrains the key not at all. Execution ids keep a narrow
60
+ `a-z A-Z 0-9 - _ . :` charset up to 64 characters, which the server's v4
61
+ UUIDs satisfy strictly. A refused assignment with a *valid* `execution_id` is
62
+ acked as a failure naming the offending field, so it dead-letters rather than
63
+ looping; one whose `execution_id` is itself unsafe is dropped, since nothing
64
+ safely addresses the server.
65
+
66
+ ## 0.3.0 - 2026-07-18
67
+
68
+ - **`ExecutionContext.scheduledFor`** exposes the trigger's original logical fire time (`Date | null`), stable across retries and dead-letter replays. Use it for time-relative job logic (e.g. the month a report covers) instead of `new Date()`. `null` when the server predates the field — the SDK never falls back to the queue fire time.
69
+
3
70
  ## 0.2.0 - 2026-07-15
4
71
 
5
72
  - First-class producer **trigger client** ([#284](https://github.com/nuetzliches/croniq/issues/284), parity with the .NET SDK [#277](https://github.com/nuetzliches/croniq/issues/277)): `createTriggerClient(...)` / `CroniqTriggerClient.trigger(jobKey, { metadata, require, prefer, timeout, idempotencyKey })` wraps `POST /v1/trigger` and returns `{ executionId, queued, deduplicated }`. Independent of the runner and carries its own `jobs:trigger`-scoped credentials. Unset optionals are omitted from the body; `idempotency_key` drives server-side dedup ([#279](https://github.com/nuetzliches/croniq/issues/279)) with a missing `deduplicated` flag parsed as `false`. A `429` per-job queue-overflow ([#299](https://github.com/nuetzliches/croniq/issues/299)) surfaces as `QueueOverflowError` (subclass of `HttpError`, with `retryAfterMs`); other non-2xx surface as `HttpError`.
package/README.md CHANGED
@@ -147,17 +147,20 @@ The same registered handler serves both its Croniqfile schedule (safety-net / re
147
147
 
148
148
  | Option | Default | Description |
149
149
  | ------------------ | -------------- | ---------------------------------------------------- |
150
- | `serverUrl` | _(required)_ | Croniq server base URL. |
150
+ | `serverUrl` | _(required)_ | Croniq server base URL. HTTPS required off loopback. |
151
151
  | `apiKey` | — | `Authorization: ApiKey {…}`. Precedence over bearer. |
152
152
  | `bearerToken` | — | `Authorization: Bearer {…}`. |
153
153
  | `requestTimeoutMs` | `30_000` | Per-request timeout. |
154
154
  | `fetchImpl` | global `fetch` | Custom `fetch` (testing / proxies). |
155
+ | `allowInsecureHttp`| `false` | Opt in to cleartext `http://` off loopback. |
156
+ | `logger` | console (warn) | Sink for SDK diagnostics. |
155
157
 
156
158
  ## Configuration
157
159
 
158
160
  | Option | Default | Description |
159
161
  | --------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------ |
160
- | `serverUrl` | _(required)_ | Croniq server base URL. |
162
+ | `serverUrl` | _(required)_ | Croniq server base URL. HTTPS required off loopback — see below. |
163
+ | `allowInsecureHttp` | `false` | Opt in to a cleartext `http://` `serverUrl` on a non-loopback host. |
161
164
  | `runnerId` | resolved | Stable id. Falls back to `RUNNER_ID` env var → state file → generated. |
162
165
  | `runnerIdPrefix` | `"runner"` | Used when generating an id. |
163
166
  | `runnerDataDir` | platform default | Where the persistent `runner-id` file lives. |
@@ -171,10 +174,30 @@ The same registered handler serves both its Croniqfile schedule (safety-net / re
171
174
  | `drainTimeoutMs` | `30_000` | Graceful shutdown budget before hard-cancel. |
172
175
  | `pollRetryDelayMs` | `5_000` | Back-off after a failed poll. |
173
176
  | `capacityBackoffMs` | `500` | Idle wait when at `maxInflight`. |
177
+ | `maxConsecutivePollConflicts` | `3` | Consecutive poll `409`s before `PollInstanceConflictError`. |
174
178
  | `logWriter` | see below | Streaming log-writer tunables. |
175
179
 
176
180
  LogWriter sub-options: `channelCapacity` (256), `batchSizeThreshold` (32), `batchTimeThresholdMs` (200), `maxBatchPerPost` (100), `shutdownTimeoutMs` (5_000).
177
181
 
182
+ ### Transport security
183
+
184
+ The credential is attached to every request as an `Authorization` header. Over `http://` it travels in cleartext — and undici honours `HTTP_PROXY` by default, so it can traverse an intermediary in the clear too.
185
+
186
+ Both `createRunner` and `createTriggerClient` therefore refuse a cleartext `serverUrl` at construction time unless the host is loopback:
187
+
188
+ - accepted: any `https://` URL, and `http://` on `localhost`, `127.0.0.0/8` or `::1` — so the `http://localhost:4000` quickstart default keeps working;
189
+ - refused: `http://` on any other host, with a `TypeError` naming the URL and the opt-in.
190
+
191
+ If a deployment genuinely has no TLS terminator (a lab or staging box), opt in explicitly — the SDK then starts, but emits one loud warning through the configured `logger`:
192
+
193
+ ```ts
194
+ const runner = createRunner({
195
+ serverUrl: 'http://croniq.internal:4000',
196
+ apiKey: process.env.CRONIQ_API_KEY,
197
+ allowInsecureHttp: true, // the API key travels in cleartext
198
+ });
199
+ ```
200
+
178
201
  ## Wire-protocol conformance
179
202
 
180
203
  Validated against the shared, language-neutral suite at [`sdks/conformance/`](../conformance/) — the runner (consumer) cases in [`cases/`](../conformance/cases), plus the producer (trigger) cases in [`cases-trigger/`](../conformance/cases-trigger) ([#287](https://github.com/nuetzliches/croniq/issues/287)) which the binding's trigger runner picks up automatically once present. The TypeScript binding lives at [`sdks/conformance/bindings/typescript/`](../conformance/bindings/typescript).
package/dist/client.d.ts CHANGED
@@ -15,6 +15,67 @@ export declare class HttpError extends Error {
15
15
  readonly body: string;
16
16
  constructor(status: number, statusText: string, path: string, body: string);
17
17
  }
18
+ /**
19
+ * A work endpoint answered `403 Forbidden`: the authenticated credential is
20
+ * bound to a different `runner_id` than the one this runner names in its
21
+ * requests (server issue #436).
22
+ *
23
+ * Unlike a `409` — where a duplicate deployment may release the identity on
24
+ * its own — this is **permanent**: retrying cannot clear it. `CroniqRunner.run`
25
+ * rejects with this instead of polling forever, so a misconfigured runner
26
+ * exits rather than looking merely idle (issue #437). The fix is an operator
27
+ * action: give the runner its own `runner_id`, or release the existing
28
+ * binding with `DELETE /v1/runners/{id}`.
29
+ */
30
+ export declare class RunnerOwnershipDeniedError extends Error {
31
+ readonly runnerId: string;
32
+ constructor(runnerId: string);
33
+ }
34
+ /** True when `err` is a 403 from a work endpoint — the ownership refusal. */
35
+ export declare function isOwnershipDenied(err: unknown): boolean;
36
+ /**
37
+ * `POST /v1/work/poll` answered `409 Conflict` `maxConsecutivePollConflicts`
38
+ * times in a row: another process is already registered under this
39
+ * `runner_id` and keeps winning the identity (fencing, server issue #374).
40
+ *
41
+ * A single 409 is transient — a deposed instance may legitimately take its
42
+ * identity back — so the runner backs off and retries. A streak of them is
43
+ * not: it is a duplicate deployment, two processes started with the same
44
+ * fixed `runnerId`. Retrying forever there leaves the misconfiguration behind
45
+ * a warning that scrolls past, so `CroniqRunner.run` rejects with this
46
+ * instead (issue #134 sub-item 1).
47
+ *
48
+ * Distinct from {@link RunnerOwnershipDeniedError}, which is a 403 and
49
+ * permanent from the first response.
50
+ */
51
+ export declare class PollInstanceConflictError extends Error {
52
+ readonly runnerId: string;
53
+ readonly consecutiveCount: number;
54
+ constructor(runnerId: string, consecutiveCount: number);
55
+ }
56
+ /** True when `err` is a 409 from the poll endpoint — the fencing refusal. */
57
+ export declare function isInstanceConflict(err: unknown): boolean;
58
+ /**
59
+ * A work endpoint answered `401 Unauthorized` `maxConsecutiveAuthFailures`
60
+ * times in a row: the API key was rejected and keeps being rejected.
61
+ *
62
+ * The credential is read once, when the client is built, and never re-read,
63
+ * so retrying presents the same dead key forever. Before this existed a 401
64
+ * fell into the generic transient bucket and the runner retried on the poll
65
+ * interval indefinitely: the process stayed up, looked healthy, did nothing,
66
+ * and never exited non-zero — so no supervisor restarted it, and restarting
67
+ * is exactly what would have picked up the new key (issue #473).
68
+ *
69
+ * Not thrown on the first 401. Key rotation hands over by installing the new
70
+ * key and giving the old one an expiry (server issue #471), and dying on a
71
+ * single 401 would turn a narrow race around that handover into an outage.
72
+ */
73
+ export declare class AuthFailedError extends Error {
74
+ readonly consecutiveCount: number;
75
+ constructor(consecutiveCount: number);
76
+ }
77
+ /** True when `err` is a 401 from a work endpoint — the rejected credential. */
78
+ export declare function isUnauthorized(err: unknown): boolean;
18
79
  /**
19
80
  * HTTP client for the Croniq runner API. Adds `Authorization` per request
20
81
  * (ApiKey takes precedence over Bearer when both are set) and serialises
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACnB,YAAY,EACZ,SAAS,EACV,MAAM,eAAe,CAAC;AAGvB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,qEAAqE;IACrE,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED,qBAAa,SAAU,SAAQ,KAAK;aAEhB,MAAM,EAAE,MAAM;aACd,UAAU,EAAE,MAAM;aAClB,IAAI,EAAE,MAAM;aACZ,IAAI,EAAE,MAAM;gBAHZ,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM;CAK/B;AAED;;;;GAIG;AACH,qBAAa,YAAY;;gBAOX,IAAI,EAAE,mBAAmB;IAQ/B,IAAI,CAAC,GAAG,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAKrF,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxD,KAAK,CAAC,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5D,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAMxF,WAAW,CAAC,GAAG,EAAE,kBAAkB,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;CAgE1G"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACnB,YAAY,EACZ,SAAS,EACV,MAAM,eAAe,CAAC;AAGvB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,qEAAqE;IACrE,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED,qBAAa,SAAU,SAAQ,KAAK;aAEhB,MAAM,EAAE,MAAM;aACd,UAAU,EAAE,MAAM;aAClB,IAAI,EAAE,MAAM;aACZ,IAAI,EAAE,MAAM;gBAHZ,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM;CAK/B;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;aACvB,QAAQ,EAAE,MAAM;gBAAhB,QAAQ,EAAE,MAAM;CAS7C;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAEvD;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;aAEhC,QAAQ,EAAE,MAAM;aAChB,gBAAgB,EAAE,MAAM;gBADxB,QAAQ,EAAE,MAAM,EAChB,gBAAgB,EAAE,MAAM;CAS3C;AAED,6EAA6E;AAC7E,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAExD;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,eAAgB,SAAQ,KAAK;aACZ,gBAAgB,EAAE,MAAM;gBAAxB,gBAAgB,EAAE,MAAM;CAQrD;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAEpD;AAED;;;;GAIG;AACH,qBAAa,YAAY;;gBAOX,IAAI,EAAE,mBAAmB;IAQ/B,IAAI,CAAC,GAAG,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAKrF,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxD,KAAK,CAAC,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5D,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAMxF,WAAW,CAAC,GAAG,EAAE,kBAAkB,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;CAgE1G"}
package/dist/client.js CHANGED
@@ -15,6 +15,93 @@ export class HttpError extends Error {
15
15
  this.name = 'HttpError';
16
16
  }
17
17
  }
18
+ /**
19
+ * A work endpoint answered `403 Forbidden`: the authenticated credential is
20
+ * bound to a different `runner_id` than the one this runner names in its
21
+ * requests (server issue #436).
22
+ *
23
+ * Unlike a `409` — where a duplicate deployment may release the identity on
24
+ * its own — this is **permanent**: retrying cannot clear it. `CroniqRunner.run`
25
+ * rejects with this instead of polling forever, so a misconfigured runner
26
+ * exits rather than looking merely idle (issue #437). The fix is an operator
27
+ * action: give the runner its own `runner_id`, or release the existing
28
+ * binding with `DELETE /v1/runners/{id}`.
29
+ */
30
+ export class RunnerOwnershipDeniedError extends Error {
31
+ runnerId;
32
+ constructor(runnerId) {
33
+ super(`work ownership denied — the credential this runner authenticates with does not own ` +
34
+ `runner_id '${runnerId}'. The server answered 403 Forbidden on POST /v1/work/poll ` +
35
+ `and will keep doing so: give this runner its own runner_id, or release the ` +
36
+ `existing binding with DELETE /v1/runners/{id}.`);
37
+ this.runnerId = runnerId;
38
+ this.name = 'RunnerOwnershipDeniedError';
39
+ }
40
+ }
41
+ /** True when `err` is a 403 from a work endpoint — the ownership refusal. */
42
+ export function isOwnershipDenied(err) {
43
+ return err instanceof HttpError && err.status === 403;
44
+ }
45
+ /**
46
+ * `POST /v1/work/poll` answered `409 Conflict` `maxConsecutivePollConflicts`
47
+ * times in a row: another process is already registered under this
48
+ * `runner_id` and keeps winning the identity (fencing, server issue #374).
49
+ *
50
+ * A single 409 is transient — a deposed instance may legitimately take its
51
+ * identity back — so the runner backs off and retries. A streak of them is
52
+ * not: it is a duplicate deployment, two processes started with the same
53
+ * fixed `runnerId`. Retrying forever there leaves the misconfiguration behind
54
+ * a warning that scrolls past, so `CroniqRunner.run` rejects with this
55
+ * instead (issue #134 sub-item 1).
56
+ *
57
+ * Distinct from {@link RunnerOwnershipDeniedError}, which is a 403 and
58
+ * permanent from the first response.
59
+ */
60
+ export class PollInstanceConflictError extends Error {
61
+ runnerId;
62
+ consecutiveCount;
63
+ constructor(runnerId, consecutiveCount) {
64
+ super(`poll instance conflict — another runner is already registered with runner_id ` +
65
+ `'${runnerId}'. Observed ${consecutiveCount} consecutive 409 Conflict responses ` +
66
+ `on POST /v1/work/poll. Stop the duplicate process or rotate the runner_id.`);
67
+ this.runnerId = runnerId;
68
+ this.consecutiveCount = consecutiveCount;
69
+ this.name = 'PollInstanceConflictError';
70
+ }
71
+ }
72
+ /** True when `err` is a 409 from the poll endpoint — the fencing refusal. */
73
+ export function isInstanceConflict(err) {
74
+ return err instanceof HttpError && err.status === 409;
75
+ }
76
+ /**
77
+ * A work endpoint answered `401 Unauthorized` `maxConsecutiveAuthFailures`
78
+ * times in a row: the API key was rejected and keeps being rejected.
79
+ *
80
+ * The credential is read once, when the client is built, and never re-read,
81
+ * so retrying presents the same dead key forever. Before this existed a 401
82
+ * fell into the generic transient bucket and the runner retried on the poll
83
+ * interval indefinitely: the process stayed up, looked healthy, did nothing,
84
+ * and never exited non-zero — so no supervisor restarted it, and restarting
85
+ * is exactly what would have picked up the new key (issue #473).
86
+ *
87
+ * Not thrown on the first 401. Key rotation hands over by installing the new
88
+ * key and giving the old one an expiry (server issue #471), and dying on a
89
+ * single 401 would turn a narrow race around that handover into an outage.
90
+ */
91
+ export class AuthFailedError extends Error {
92
+ consecutiveCount;
93
+ constructor(consecutiveCount) {
94
+ super(`unauthorized — the API key was rejected on ${consecutiveCount} consecutive ` +
95
+ `POST /v1/work/poll attempts. It may have been revoked, or its rotation grace ` +
96
+ `window may have elapsed. Restart the runner with the current key.`);
97
+ this.consecutiveCount = consecutiveCount;
98
+ this.name = 'AuthFailedError';
99
+ }
100
+ }
101
+ /** True when `err` is a 401 from a work endpoint — the rejected credential. */
102
+ export function isUnauthorized(err) {
103
+ return err instanceof HttpError && err.status === 401;
104
+ }
18
105
  /**
19
106
  * HTTP client for the Croniq runner API. Adds `Authorization` per request
20
107
  * (ApiKey takes precedence over Bearer when both are set) and serialises
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/context.d.ts CHANGED
@@ -9,6 +9,13 @@ export interface ExecutionContext {
9
9
  readonly executionId: string;
10
10
  /** Job key, e.g. `billing:invoice`. */
11
11
  readonly jobKey: string;
12
+ /**
13
+ * The trigger's original logical fire time — stable across retries and
14
+ * dead-letter replays. Use this (not `new Date()`) for time-relative job
15
+ * logic like "the month being reported". `null` when the server predates
16
+ * the field; the SDK never falls back to the queue fire time.
17
+ */
18
+ readonly scheduledFor: Date | null;
12
19
  /** 1-based attempt counter, incremented on each retry. */
13
20
  readonly attempt: number;
14
21
  /**
@@ -49,6 +56,7 @@ export interface ExecutionContext {
49
56
  export interface ExecutionContextInput {
50
57
  executionId: string;
51
58
  jobKey: string;
59
+ scheduledFor: Date | null;
52
60
  attempt: number;
53
61
  metadata: unknown;
54
62
  timeoutMs: number;
@@ -65,6 +73,7 @@ export declare class ExecutionContextImpl implements ExecutionContext {
65
73
  #private;
66
74
  readonly executionId: string;
67
75
  readonly jobKey: string;
76
+ readonly scheduledFor: Date | null;
68
77
  readonly attempt: number;
69
78
  readonly metadata: unknown;
70
79
  readonly timeoutMs: number;
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/D,MAAM,WAAW,gBAAgB;IAC/B,4CAA4C;IAC5C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,uCAAuC;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,mEAAmE;IACnE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,sCAAsC;IACtC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,gDAAgD;IAChD,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,gFAAgF;IAChF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAE9B;;;OAGG;IACH,SAAS,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClG,oCAAoC;IACpC,UAAU,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,EAAE,aAAa,CAAC;IAC1B,gBAAgB,EAAE,wBAAwB,CAAC;IAC3C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,oBAAqB,YAAW,gBAAgB;;IAC3D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAQZ,KAAK,EAAE,qBAAqB;IAgBxC,IAAI,SAAS,IAAI,SAAS,CASzB;IAED,iFAAiF;IACjF,IAAI,kBAAkB,IAAI,kBAAkB,GAAG,IAAI,CAElD;IAED,SAAS,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAK3F,UAAU,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAMrD"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/D,MAAM,WAAW,gBAAgB;IAC/B,4CAA4C;IAC5C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,uCAAuC;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,EAAE,IAAI,GAAG,IAAI,CAAC;IACnC,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,mEAAmE;IACnE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,sCAAsC;IACtC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,gDAAgD;IAChD,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,gFAAgF;IAChF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAE9B;;;OAGG;IACH,SAAS,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClG,oCAAoC;IACpC,UAAU,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,EAAE,aAAa,CAAC;IAC1B,gBAAgB,EAAE,wBAAwB,CAAC;IAC3C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,oBAAqB,YAAW,gBAAgB;;IAC3D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,EAAE,IAAI,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAQZ,KAAK,EAAE,qBAAqB;IAiBxC,IAAI,SAAS,IAAI,SAAS,CASzB;IAED,iFAAiF;IACjF,IAAI,kBAAkB,IAAI,kBAAkB,GAAG,IAAI,CAElD;IAED,SAAS,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAK3F,UAAU,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAMrD"}
package/dist/context.js CHANGED
@@ -2,6 +2,7 @@ import { StreamingLogWriter } from './log-writer.js';
2
2
  export class ExecutionContextImpl {
3
3
  executionId;
4
4
  jobKey;
5
+ scheduledFor;
5
6
  attempt;
6
7
  metadata;
7
8
  timeoutMs;
@@ -17,6 +18,7 @@ export class ExecutionContextImpl {
17
18
  constructor(input) {
18
19
  this.executionId = input.executionId;
19
20
  this.jobKey = input.jobKey;
21
+ this.scheduledFor = input.scheduledFor;
20
22
  this.attempt = input.attempt;
21
23
  this.metadata = input.metadata;
22
24
  this.timeoutMs = input.timeoutMs;
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAkB,MAAM,iBAAiB,CAAC;AAgErE,MAAM,OAAO,oBAAoB;IACtB,WAAW,CAAS;IACpB,MAAM,CAAS;IACf,OAAO,CAAS;IAChB,QAAQ,CAAU;IAClB,SAAS,CAAS;IAClB,QAAQ,CAAS;IACjB,UAAU,CAAoB;IAC9B,MAAM,CAAc;IACpB,MAAM,CAAS;IAEf,OAAO,CAAe;IACtB,WAAW,CAAgB;IAC3B,iBAAiB,CAA2B;IAC5C,UAAU,CAAS;IAC5B,UAAU,GAA8B,IAAI,CAAC;IAE7C,YAAY,KAA4B;QACtC,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;QACrC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QAC7B,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;QAC/B,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;QACjC,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;QAC/B,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;QACnC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC;QAC5B,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,UAAU,CAAC;QACpC,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC,gBAAgB,CAAC;QAChD,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC;IACpC,CAAC;IAED,IAAI,SAAS;QACX,IAAI,CAAC,UAAU,KAAK,IAAI,kBAAkB,CACxC,IAAI,CAAC,OAAO,EACZ,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,iBAAiB,EACtB,IAAI,CAAC,UAAU,CAChB,CAAC;QACF,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,iFAAiF;IACjF,IAAI,kBAAkB;QACpB,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,SAAS,CAAC,KAAqB,EAAE,OAAe,EAAE,MAA+B;QAC/E,MAAM,KAAK,GAAc,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;QAClF,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,MAAmB;QAClC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAChC,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/D,MAAM,EAAE,GAAG,IAAI,eAAe,EAAE,CAAC;QACjC,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC;IACvE,CAAC;CACF"}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAkB,MAAM,iBAAiB,CAAC;AAwErE,MAAM,OAAO,oBAAoB;IACtB,WAAW,CAAS;IACpB,MAAM,CAAS;IACf,YAAY,CAAc;IAC1B,OAAO,CAAS;IAChB,QAAQ,CAAU;IAClB,SAAS,CAAS;IAClB,QAAQ,CAAS;IACjB,UAAU,CAAoB;IAC9B,MAAM,CAAc;IACpB,MAAM,CAAS;IAEf,OAAO,CAAe;IACtB,WAAW,CAAgB;IAC3B,iBAAiB,CAA2B;IAC5C,UAAU,CAAS;IAC5B,UAAU,GAA8B,IAAI,CAAC;IAE7C,YAAY,KAA4B;QACtC,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;QACrC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,YAAY,CAAC;QACvC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QAC7B,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;QAC/B,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;QACjC,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;QAC/B,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;QACnC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC;QAC5B,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,UAAU,CAAC;QACpC,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC,gBAAgB,CAAC;QAChD,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC;IACpC,CAAC;IAED,IAAI,SAAS;QACX,IAAI,CAAC,UAAU,KAAK,IAAI,kBAAkB,CACxC,IAAI,CAAC,OAAO,EACZ,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,iBAAiB,EACtB,IAAI,CAAC,UAAU,CAChB,CAAC;QACF,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,iFAAiF;IACjF,IAAI,kBAAkB;QACpB,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,SAAS,CAAC,KAAqB,EAAE,OAAe,EAAE,MAA+B;QAC/E,MAAM,KAAK,GAAc,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;QAClF,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,MAAmB;QAClC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAChC,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/D,MAAM,EAAE,GAAG,IAAI,eAAe,EAAE,CAAC;QACjC,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC;IACvE,CAAC;CACF"}
@@ -1,4 +1,4 @@
1
- import type { CroniqClient } from './client.js';
1
+ import { type CroniqClient } from './client.js';
2
2
  import { type HandlerRegistry } from './handler.js';
3
3
  import { type Logger } from './logger.js';
4
4
  import type { ResolvedRunnerOptions } from './options.js';
@@ -1 +1 @@
1
- {"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../src/dispatcher.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKhD,OAAO,EAAE,KAAK,eAAe,EAA4B,MAAM,cAAc,CAAC;AAC9E,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,KAAK,EAAyB,cAAc,EAAE,MAAM,eAAe,CAAC;AAE3E,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,eAAe,CAAC;IAC1B,OAAO,EAAE,qBAAqB,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,qBAAa,mBAAmB;;gBAGlB,IAAI,EAAE,cAAc;IAIhC;;;;;;;OAOG;IACG,QAAQ,CACZ,UAAU,EAAE,cAAc,EAC1B,WAAW,EAAE,eAAe,EAC5B,WAAW,EAAE,WAAW,GACvB,OAAO,CAAC,IAAI,CAAC;CA+HjB"}
1
+ {"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../src/dispatcher.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,YAAY,EAAqB,MAAM,aAAa,CAAC;AAKnE,OAAO,EAAE,KAAK,eAAe,EAA4B,MAAM,cAAc,CAAC;AAC9E,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,KAAK,EAAyB,cAAc,EAAE,MAAM,eAAe,CAAC;AAE3E,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,eAAe,CAAC;IAC1B,OAAO,EAAE,qBAAqB,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,qBAAa,mBAAmB;;gBAGlB,IAAI,EAAE,cAAc;IAIhC;;;;;;;OAOG;IACG,QAAQ,CACZ,UAAU,EAAE,cAAc,EAC1B,WAAW,EAAE,eAAe,EAC5B,WAAW,EAAE,WAAW,GACvB,OAAO,CAAC,IAAI,CAAC;CAkKjB"}
@@ -1,7 +1,8 @@
1
1
  import { anySignal } from './abort.js';
2
+ import { isOwnershipDenied } from './client.js';
2
3
  import { ExecutionContextImpl } from './context.js';
3
4
  import { sleep } from './deferred.js';
4
- import { parseTimeoutMs } from './duration.js';
5
+ import { parseScheduledFor, parseTimeoutMs } from './duration.js';
5
6
  import { LogEnrichment } from './enrichment.js';
6
7
  import { NoHandlerRegisteredError } from './handler.js';
7
8
  import { scopedLogger } from './logger.js';
@@ -30,6 +31,9 @@ export class ExecutionDispatcher {
30
31
  const jobKey = assignment.job_key;
31
32
  const attempt = assignment.attempt;
32
33
  const timeoutMs = parseTimeoutMs(assignment.timeout) ?? 5 * 60_000;
34
+ // Original logical fire time; a missing or unparseable value yields null
35
+ // rather than falling back to fire_at (see ExecutionContext.scheduledFor).
36
+ const scheduledFor = parseScheduledFor(assignment.scheduled_for);
33
37
  const handlerLogger = scopedLogger(logger, {
34
38
  execution_id: executionId,
35
39
  job_key: jobKey,
@@ -40,6 +44,7 @@ export class ExecutionDispatcher {
40
44
  const ctx = new ExecutionContextImpl({
41
45
  executionId,
42
46
  jobKey,
47
+ scheduledFor,
43
48
  attempt,
44
49
  metadata: assignment.metadata,
45
50
  timeoutMs,
@@ -78,7 +83,9 @@ export class ExecutionDispatcher {
78
83
  }
79
84
  else {
80
85
  const ex = err instanceof Error ? err : new Error(String(err));
81
- logger.warn(`handler for ${jobKey} (execution ${executionId}) threw`, {
86
+ // Identifiers travel as fields only, never interpolated into the
87
+ // message — see sanitize.ts. The same applies to every log call below.
88
+ logger.warn('job handler threw', {
82
89
  error: ex.message,
83
90
  job_key: jobKey,
84
91
  execution_id: executionId,
@@ -103,9 +110,10 @@ export class ExecutionDispatcher {
103
110
  await writer.dispose();
104
111
  }
105
112
  catch (err) {
106
- logger.warn(`log_writer drain failed for execution ${executionId}`, {
113
+ logger.warn('log_writer drain failed', {
107
114
  error: String(err),
108
115
  execution_id: executionId,
116
+ job_key: jobKey,
109
117
  });
110
118
  }
111
119
  }
@@ -125,10 +133,20 @@ export class ExecutionDispatcher {
125
133
  await client.ack(payload, ackAC.signal);
126
134
  }
127
135
  catch (err) {
128
- logger.error(`failed to ack execution ${executionId}`, {
129
- error: String(err),
130
- execution_id: executionId,
131
- });
136
+ if (isOwnershipDenied(err)) {
137
+ // Permanent (#436/#437) — the execution stays claimed until its
138
+ // lease expires, so name the fix rather than just the failure.
139
+ logger.error('ack refused with 403 Forbidden — this runner\'s credential does not own runner_id, ' +
140
+ 'so the execution stays claimed until its lease expires. Give the runner its own ' +
141
+ 'runner_id, or release the existing binding with DELETE /v1/runners/{id}', { error: String(err), runner_id: runnerId, execution_id: executionId, job_key: jobKey });
142
+ }
143
+ else {
144
+ logger.error('failed to ack execution', {
145
+ error: String(err),
146
+ execution_id: executionId,
147
+ job_key: jobKey,
148
+ });
149
+ }
132
150
  }
133
151
  }
134
152
  async #renewLoop(executionId, runnerId, signal) {
@@ -148,7 +166,20 @@ export class ExecutionDispatcher {
148
166
  catch (err) {
149
167
  if (signal.aborted)
150
168
  return;
151
- logger.debug(`lease renew failed for execution ${executionId}`, {
169
+ if (isOwnershipDenied(err)) {
170
+ // Permanent (#436/#437): every later renew fails the same way and
171
+ // the lease will expire mid-handler.
172
+ logger.error('lease renew refused with 403 Forbidden — this runner\'s credential does not own ' +
173
+ 'runner_id, so the lease will expire and the execution be reclaimed. Give the ' +
174
+ 'runner its own runner_id, or release the existing binding with ' +
175
+ 'DELETE /v1/runners/{id}', { error: String(err), runner_id: runnerId, execution_id: executionId });
176
+ continue;
177
+ }
178
+ // Since #447 renew is a real per-execution lease: 404 (no longer
179
+ // leased here) and 409 (already terminal) are the normal outcome of
180
+ // a renew racing our own completion, so they stay at debug
181
+ // alongside the transient failures.
182
+ logger.debug('lease renew failed', {
152
183
  error: String(err),
153
184
  execution_id: executionId,
154
185
  });
@@ -1 +1 @@
1
- {"version":3,"file":"dispatcher.js","sourceRoot":"","sources":["../src/dispatcher.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dispatcher.js","sourceRoot":"","sources":["../src/dispatcher.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,8 @@
1
1
  export declare function parseTimeoutMs(raw: string | undefined | null): number | undefined;
2
+ /**
3
+ * Parse the server's `scheduled_for` (RFC 3339) into a Date. Returns null when
4
+ * the field is absent (older server) or unparseable — never falls back to the
5
+ * queue fire time, which would reintroduce the wrong-logical-time bug.
6
+ */
7
+ export declare function parseScheduledFor(raw: string | undefined | null): Date | null;
2
8
  //# sourceMappingURL=duration.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"duration.d.ts","sourceRoot":"","sources":["../src/duration.ts"],"names":[],"mappings":"AAIA,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAejF"}
1
+ {"version":3,"file":"duration.d.ts","sourceRoot":"","sources":["../src/duration.ts"],"names":[],"mappings":"AAIA,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAejF;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAI7E"}
package/dist/duration.js CHANGED
@@ -20,4 +20,15 @@ export function parseTimeoutMs(raw) {
20
20
  default: return undefined;
21
21
  }
22
22
  }
23
+ /**
24
+ * Parse the server's `scheduled_for` (RFC 3339) into a Date. Returns null when
25
+ * the field is absent (older server) or unparseable — never falls back to the
26
+ * queue fire time, which would reintroduce the wrong-logical-time bug.
27
+ */
28
+ export function parseScheduledFor(raw) {
29
+ if (!raw)
30
+ return null;
31
+ const d = new Date(raw);
32
+ return Number.isNaN(d.getTime()) ? null : d;
33
+ }
23
34
  //# sourceMappingURL=duration.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"duration.js","sourceRoot":"","sources":["../src/duration.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,0EAA0E;AAC1E,2EAA2E;AAE3E,MAAM,UAAU,cAAc,CAAC,GAA8B;IAC3D,IAAI,GAAG,IAAI,IAAI;QAAE,OAAO,SAAS,CAAC;IAClC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC9C,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IACtC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC/B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC9C,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,GAAG,CAAC,CAAC,OAAO,KAAK,GAAG,KAAK,CAAC;QAC/B,KAAK,GAAG,CAAC,CAAC,OAAO,KAAK,GAAG,MAAM,CAAC;QAChC,KAAK,GAAG,CAAC,CAAC,OAAO,KAAK,GAAG,SAAS,CAAC;QACnC,KAAK,GAAG,CAAC,CAAC,OAAO,KAAK,GAAG,UAAU,CAAC;QACpC,OAAO,CAAC,CAAC,OAAO,SAAS,CAAC;IAC5B,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"duration.js","sourceRoot":"","sources":["../src/duration.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,0EAA0E;AAC1E,2EAA2E;AAE3E,MAAM,UAAU,cAAc,CAAC,GAA8B;IAC3D,IAAI,GAAG,IAAI,IAAI;QAAE,OAAO,SAAS,CAAC;IAClC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC9C,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IACtC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC/B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC9C,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,GAAG,CAAC,CAAC,OAAO,KAAK,GAAG,KAAK,CAAC;QAC/B,KAAK,GAAG,CAAC,CAAC,OAAO,KAAK,GAAG,MAAM,CAAC;QAChC,KAAK,GAAG,CAAC,CAAC,OAAO,KAAK,GAAG,SAAS,CAAC;QACnC,KAAK,GAAG,CAAC,CAAC,OAAO,KAAK,GAAG,UAAU,CAAC;QACpC,OAAO,CAAC,CAAC,OAAO,SAAS,CAAC;IAC5B,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAA8B;IAC9D,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9C,CAAC"}
package/dist/index.d.ts CHANGED
@@ -7,7 +7,8 @@ export type { Logger, LogLevel } from './logger.js';
7
7
  export { consoleLogger, noopLogger } from './logger.js';
8
8
  export type { LogWriter } from './log-writer.js';
9
9
  export type { AckRequest, AckStatus, PollRequest, PollResponse, RegisterJobRequest, RegisterJobResponse, RenewRequest, TriggerRequest, TriggerResponse, WorkAssignment, WorkEvent, WorkEventLevel, } from './protocol.js';
10
- export { HttpError } from './client.js';
10
+ export { AuthFailedError, HttpError, PollInstanceConflictError, RunnerOwnershipDeniedError } from './client.js';
11
+ export { isLoopbackHostname } from './security.js';
11
12
  export { CroniqTriggerClient, createTriggerClient, QueueOverflowError } from './trigger.js';
12
13
  export type { CroniqTriggerClientOptions, TriggerParams, TriggerResult, } from './trigger.js';
13
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACzD,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,YAAY,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AACxD,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACxD,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,YAAY,EACV,UAAU,EACV,SAAS,EACT,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACnB,YAAY,EACZ,cAAc,EACd,eAAe,EACf,cAAc,EACd,SAAS,EACT,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC5F,YAAY,EACV,0BAA0B,EAC1B,aAAa,EACb,aAAa,GACd,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACzD,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,YAAY,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AACxD,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACxD,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,YAAY,EACV,UAAU,EACV,SAAS,EACT,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACnB,YAAY,EACZ,cAAc,EACd,eAAe,EACf,cAAc,EACd,SAAS,EACT,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,yBAAyB,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAChH,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAGnD,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC5F,YAAY,EACV,0BAA0B,EAC1B,aAAa,EACb,aAAa,GACd,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -2,7 +2,8 @@
2
2
  export { CroniqRunner, createRunner } from './runner.js';
3
3
  export { NoHandlerRegisteredError } from './handler.js';
4
4
  export { consoleLogger, noopLogger } from './logger.js';
5
- export { HttpError } from './client.js';
5
+ export { AuthFailedError, HttpError, PollInstanceConflictError, RunnerOwnershipDeniedError } from './client.js';
6
+ export { isLoopbackHostname } from './security.js';
6
7
  // Producer-side trigger (on-demand) client — parity with the .NET SDK (#277).
7
8
  export { CroniqTriggerClient, createTriggerClient, QueueOverflowError } from './trigger.js';
8
9
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AASzD,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAExD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAgBxD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,8EAA8E;AAC9E,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AASzD,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAExD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAgBxD,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,yBAAyB,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAChH,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEnD,8EAA8E;AAC9E,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { CroniqClient } from './client.js';
1
+ import { type CroniqClient } from './client.js';
2
2
  import type { LogEnrichment } from './enrichment.js';
3
3
  import type { Logger } from './logger.js';
4
4
  import type { ResolvedLogWriterOptions } from './options.js';