velocious 1.0.666 → 1.0.668
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/README.md +40 -11
- package/build/background-jobs/adapter-client.js +22 -0
- package/build/background-jobs/adapter.js +15 -0
- package/build/background-jobs/client.js +147 -1
- package/build/background-jobs/job-semantics.js +30 -0
- package/build/background-jobs/job.js +26 -1
- package/build/background-jobs/local-adapter.js +36 -8
- package/build/background-jobs/local-store.js +410 -39
- package/build/background-jobs/main.js +69 -2
- package/build/background-jobs/platform-job.js +26 -1
- package/build/background-jobs/runtime.js +58 -0
- package/build/background-jobs/store.js +374 -6
- package/build/background-jobs/types.js +27 -2
- package/build/background-jobs/web/controller.js +1 -0
- package/build/configuration.js +30 -6
- package/build/database/drivers/mssql/column.js +2 -2
- package/build/database/drivers/mssql/table.js +19 -5
- package/build/database/pool/async-tracked-multi-connection.js +2 -1
- package/build/database/query/alter-table-base.js +8 -1
- package/build/database/record/attachments/store.js +2 -2
- package/build/environment-handlers/node/cli/commands/test/timing-manifest/merge.js +2 -48
- package/build/environment-handlers/node/cli/commands/test.js +17 -69
- package/build/frontend-model-controller.js +22 -2
- package/build/frontend-models/websocket-channel.js +18 -0
- package/build/http-server/client/websocket-session.js +41 -4
- package/build/http-server/websocket-channel.js +14 -0
- package/build/http-server/websocket-event-log-store.js +85 -12
- package/build/http-server/websocket-events-host.js +17 -7
- package/build/src/background-jobs/adapter-client.d.ts +17 -0
- package/build/src/background-jobs/adapter-client.d.ts.map +1 -1
- package/build/src/background-jobs/adapter-client.js +19 -1
- package/build/src/background-jobs/adapter.d.ts +15 -0
- package/build/src/background-jobs/adapter.d.ts.map +1 -1
- package/build/src/background-jobs/adapter.js +14 -1
- package/build/src/background-jobs/client.d.ts +17 -0
- package/build/src/background-jobs/client.d.ts.map +1 -1
- package/build/src/background-jobs/client.js +129 -1
- package/build/src/background-jobs/job-semantics.d.ts +18 -0
- package/build/src/background-jobs/job-semantics.d.ts.map +1 -1
- package/build/src/background-jobs/job-semantics.js +28 -1
- package/build/src/background-jobs/job.d.ts +15 -0
- package/build/src/background-jobs/job.d.ts.map +1 -1
- package/build/src/background-jobs/job.js +21 -2
- package/build/src/background-jobs/local-adapter.d.ts +23 -8
- package/build/src/background-jobs/local-adapter.d.ts.map +1 -1
- package/build/src/background-jobs/local-adapter.js +35 -9
- package/build/src/background-jobs/local-store.d.ts +120 -5
- package/build/src/background-jobs/local-store.d.ts.map +1 -1
- package/build/src/background-jobs/local-store.js +366 -40
- package/build/src/background-jobs/main.d.ts +24 -2
- package/build/src/background-jobs/main.d.ts.map +1 -1
- package/build/src/background-jobs/main.js +68 -3
- package/build/src/background-jobs/platform-job.d.ts +15 -0
- package/build/src/background-jobs/platform-job.d.ts.map +1 -1
- package/build/src/background-jobs/platform-job.js +19 -2
- package/build/src/background-jobs/runtime.d.ts +39 -0
- package/build/src/background-jobs/runtime.d.ts.map +1 -1
- package/build/src/background-jobs/runtime.js +49 -1
- package/build/src/background-jobs/store.d.ts +100 -1
- package/build/src/background-jobs/store.d.ts.map +1 -1
- package/build/src/background-jobs/store.js +334 -8
- package/build/src/background-jobs/types.d.ts +97 -4
- package/build/src/background-jobs/types.d.ts.map +1 -1
- package/build/src/background-jobs/types.js +28 -3
- package/build/src/background-jobs/web/controller.d.ts.map +1 -1
- package/build/src/background-jobs/web/controller.js +2 -1
- package/build/src/configuration.d.ts +18 -1
- package/build/src/configuration.d.ts.map +1 -1
- package/build/src/configuration.js +28 -6
- package/build/src/database/drivers/mssql/column.d.ts.map +1 -1
- package/build/src/database/drivers/mssql/column.js +2 -2
- package/build/src/database/drivers/mssql/table.d.ts.map +1 -1
- package/build/src/database/drivers/mssql/table.js +19 -5
- package/build/src/database/pool/async-tracked-multi-connection.d.ts.map +1 -1
- package/build/src/database/pool/async-tracked-multi-connection.js +3 -2
- package/build/src/database/query/alter-table-base.d.ts.map +1 -1
- package/build/src/database/query/alter-table-base.js +9 -2
- package/build/src/database/record/attachments/store.js +3 -3
- package/build/src/environment-handlers/node/cli/commands/test/timing-manifest/merge.d.ts.map +1 -1
- package/build/src/environment-handlers/node/cli/commands/test/timing-manifest/merge.js +3 -47
- package/build/src/environment-handlers/node/cli/commands/test.d.ts +2 -28
- package/build/src/environment-handlers/node/cli/commands/test.d.ts.map +1 -1
- package/build/src/environment-handlers/node/cli/commands/test.js +10 -61
- package/build/src/frontend-model-controller.d.ts.map +1 -1
- package/build/src/frontend-model-controller.js +18 -3
- package/build/src/frontend-models/websocket-channel.d.ts +9 -0
- package/build/src/frontend-models/websocket-channel.d.ts.map +1 -1
- package/build/src/frontend-models/websocket-channel.js +16 -1
- package/build/src/http-server/client/websocket-session.d.ts +25 -2
- package/build/src/http-server/client/websocket-session.d.ts.map +1 -1
- package/build/src/http-server/client/websocket-session.js +40 -5
- package/build/src/http-server/websocket-channel.d.ts +11 -0
- package/build/src/http-server/websocket-channel.d.ts.map +1 -1
- package/build/src/http-server/websocket-channel.js +14 -1
- package/build/src/http-server/websocket-event-log-store.d.ts +60 -41
- package/build/src/http-server/websocket-event-log-store.d.ts.map +1 -1
- package/build/src/http-server/websocket-event-log-store.js +79 -12
- package/build/src/http-server/websocket-events-host.d.ts +10 -3
- package/build/src/http-server/websocket-events-host.d.ts.map +1 -1
- package/build/src/http-server/websocket-events-host.js +17 -8
- package/build/src/testing/test-files-finder.d.ts +17 -117
- package/build/src/testing/test-files-finder.d.ts.map +1 -1
- package/build/src/testing/test-files-finder.js +29 -294
- package/build/src/testing/test-filter-parser.d.ts +32 -22
- package/build/src/testing/test-filter-parser.d.ts.map +1 -1
- package/build/src/testing/test-filter-parser.js +25 -217
- package/build/src/testing/test-profile-activity.d.ts +1 -6
- package/build/src/testing/test-profile-activity.d.ts.map +1 -1
- package/build/src/testing/test-profile-activity.js +2 -12
- package/build/src/testing/test-profile-output.d.ts +1 -40
- package/build/src/testing/test-profile-output.d.ts.map +1 -1
- package/build/src/testing/test-profile-output.js +2 -217
- package/build/src/testing/test-profiler.d.ts +15 -659
- package/build/src/testing/test-profiler.d.ts.map +1 -1
- package/build/src/testing/test-profiler.js +35 -833
- package/build/src/testing/test-runner.d.ts +10 -1
- package/build/src/testing/test-runner.d.ts.map +1 -1
- package/build/src/testing/test-runner.js +26 -4
- package/build/src/testing/test-suite-splitter.d.ts +1 -128
- package/build/src/testing/test-suite-splitter.d.ts.map +1 -1
- package/build/src/testing/test-suite-splitter.js +2 -259
- package/build/src/testing/timing-manifest.d.ts +5 -93
- package/build/src/testing/timing-manifest.d.ts.map +1 -1
- package/build/src/testing/timing-manifest.js +4 -283
- package/build/src/testing/velocious-attempt-executor.d.ts.map +1 -1
- package/build/src/testing/velocious-attempt-executor.js +8 -2
- package/build/testing/test-files-finder.js +32 -343
- package/build/testing/test-filter-parser.js +29 -248
- package/build/testing/test-profile-activity.js +1 -12
- package/build/testing/test-profile-output.js +8 -253
- package/build/testing/test-profiler.js +38 -895
- package/build/testing/test-runner.js +33 -9
- package/build/testing/test-suite-splitter.js +1 -301
- package/build/testing/timing-manifest.js +10 -344
- package/build/testing/velocious-attempt-executor.js +7 -1
- package/package.json +3 -3
- package/scripts/test-browser.js +30 -6
- package/scripts/verify-docker-dev-environment.js +13 -1
- package/src/background-jobs/adapter-client.js +22 -0
- package/src/background-jobs/adapter.js +15 -0
- package/src/background-jobs/client.js +147 -1
- package/src/background-jobs/job-semantics.js +30 -0
- package/src/background-jobs/job.js +26 -1
- package/src/background-jobs/local-adapter.js +36 -8
- package/src/background-jobs/local-store.js +410 -39
- package/src/background-jobs/main.js +69 -2
- package/src/background-jobs/platform-job.js +26 -1
- package/src/background-jobs/runtime.js +58 -0
- package/src/background-jobs/store.js +374 -6
- package/src/background-jobs/types.js +27 -2
- package/src/background-jobs/web/controller.js +1 -0
- package/src/configuration.js +30 -6
- package/src/database/drivers/mssql/column.js +2 -2
- package/src/database/drivers/mssql/table.js +19 -5
- package/src/database/pool/async-tracked-multi-connection.js +2 -1
- package/src/database/query/alter-table-base.js +8 -1
- package/src/database/record/attachments/store.js +2 -2
- package/src/environment-handlers/node/cli/commands/test/timing-manifest/merge.js +2 -48
- package/src/environment-handlers/node/cli/commands/test.js +17 -69
- package/src/frontend-model-controller.js +22 -2
- package/src/frontend-models/websocket-channel.js +18 -0
- package/src/http-server/client/websocket-session.js +41 -4
- package/src/http-server/websocket-channel.js +14 -0
- package/src/http-server/websocket-event-log-store.js +85 -12
- package/src/http-server/websocket-events-host.js +17 -7
- package/src/testing/test-files-finder.js +32 -343
- package/src/testing/test-filter-parser.js +29 -248
- package/src/testing/test-profile-activity.js +1 -12
- package/src/testing/test-profile-output.js +8 -253
- package/src/testing/test-profiler.js +38 -895
- package/src/testing/test-runner.js +33 -9
- package/src/testing/test-suite-splitter.js +1 -301
- package/src/testing/timing-manifest.js +10 -344
- package/src/testing/velocious-attempt-executor.js +7 -1
package/README.md
CHANGED
|
@@ -32,12 +32,13 @@
|
|
|
32
32
|
* Per-record ability checks via `.abilities(...)` on frontend queries + `record.can(action)` (see [docs/abilities.md](docs/abilities.md))
|
|
33
33
|
* Translated model attributes with current-locale relationship sorting (see [docs/translations.md](docs/translations.md))
|
|
34
34
|
* Cross-process broadcast bus for `broadcastToChannel` via `velocious beacon`, including background job runner processes (see [docs/beacon.md](docs/beacon.md))
|
|
35
|
+
* Websocket channel primitives for 1:N pub/sub over the shared session socket (`channel-subscribe` protocol): `matches()`-routed streams, interest-gated event-log persistence with 10-minute retention, stream-scoped `lastEventId` replay with `channel-replay-gap` gap reporting, and `{liveOnly: true}` channels that never reach the replay log (see [docs/websocket-channels.md](docs/websocket-channels.md))
|
|
35
36
|
* Rails-style application process initializer teardown with immutable process identity, reverse idempotent shutdown, and explicit HTTP/background-job ownership (see [docs/application-process-lifecycle.md](docs/application-process-lifecycle.md))
|
|
36
37
|
* Configurable HTTP server worker handlers plus backpressured, descriptor-only file responses with completion callbacks (see [docs/http-server.md](docs/http-server.md))
|
|
37
38
|
* Explicit database-free in-process HTTP applications with optional request and buffered-response byte limits (see [docs/http-server.md](docs/http-server.md#database-free-applications))
|
|
38
39
|
* Default-on buffered HTTP response compression with Brotli/gzip content negotiation, global and per-response opt-outs, and HEAD-correct representation headers (see [docs/http-server.md](docs/http-server.md#response-compression))
|
|
39
40
|
* Background jobs with Node SQL/TCP workers plus a Browser/Expo local SQLite store and in-process dispatcher, including failure events, authorized database-scoped dashboard counts, and an opt-in release-scoped main/worker generation protocol with acknowledged activation, asynchronous retirement, and retired-main recovery. Production compliance additionally requires downstream supervisor retention/activation ordering and release pins (see [docs/background-jobs.md](docs/background-jobs.md), [docs/local-background-jobs.md](docs/local-background-jobs.md), and [docs/background-jobs-dashboard.md](docs/background-jobs-dashboard.md))
|
|
40
|
-
* Durable one-off background-job scheduling with exact epoch timestamps (see [docs/scheduled-background-job-enqueue.md](docs/scheduled-background-job-enqueue.md))
|
|
41
|
+
* Durable one-off background-job scheduling with exact epoch timestamps plus stable replacement, cancellation, normalized readback, and duplicate-free wake across Node SQL/TCP and Browser/Expo local SQLite adapters (see [docs/scheduled-background-job-enqueue.md](docs/scheduled-background-job-enqueue.md))
|
|
41
42
|
* Rails-style request and database query logging with structured credential redaction (see [docs/logging.md](docs/logging.md))
|
|
42
43
|
* EJS-backed mailers with delivery, queueing, and payload rendering support (see [docs/mailers.md](docs/mailers.md))
|
|
43
44
|
* Trusted reverse proxy handling for `request.remoteAddress()` (see [docs/trusted-proxies.md](docs/trusted-proxies.md))
|
|
@@ -125,7 +126,7 @@ Maintainers cutting a package release must follow the [Velocious release runbook
|
|
|
125
126
|
|
|
126
127
|
# Docker development environment
|
|
127
128
|
|
|
128
|
-
The checked-in root `Dockerfile` and `compose.yml` define one canonical `dev` service used by humans, CI, and agent systems alike (see [docs/docker-development-environment.md](docs/docker-development-environment.md)). The image is Ubuntu 26.04 LTS (pinned by digest) with Node.js 24.x from signed NodeSource, the universal apt coding/debugging baseline,
|
|
129
|
+
The checked-in root `Dockerfile` and `compose.yml` define one canonical `dev` service used by humans, CI, and agent systems alike (see [docs/docker-development-environment.md](docs/docker-development-environment.md)). The image is Ubuntu 26.04 LTS (pinned by digest) with Node.js 24.x from signed NodeSource, the universal apt coding/debugging baseline, the newest published retained provider CLIs, and owner-pinned native Qwen Code 0.23.3; it is source-independent — no project source is copied and no project dependencies are installed at image build time.
|
|
129
130
|
|
|
130
131
|
Prerequisites: Docker with the Compose v2 plugin, and this repository checked out at `$DEV_HOME_PATH/velocious` (default `DEV_HOME_PATH`: `/home/dev`).
|
|
131
132
|
|
|
@@ -179,10 +180,13 @@ Baselines are generated against a fresh checkout (no generated dummy `configurat
|
|
|
179
180
|
# Testing
|
|
180
181
|
|
|
181
182
|
Application tests may import the testing DSL from the independent public package.
|
|
182
|
-
`@velocious/testing` `0.0.
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
183
|
+
`@velocious/testing` `0.0.14` owns framework-neutral discovery, filtering,
|
|
184
|
+
execution, reporting, deterministic weighted grouping, profiling, and timing
|
|
185
|
+
manifests. Compatible installed copies share one protocol-1/schema-3 default registry.
|
|
186
|
+
Velocious retains application/configuration startup, database and transaction
|
|
187
|
+
lifecycle, browser execution, environment handling, framework profile spans, and
|
|
188
|
+
report translation. Existing Velocious import paths remain supported as thin
|
|
189
|
+
delegating compatibility facades.
|
|
186
190
|
|
|
187
191
|
```js
|
|
188
192
|
import {describe, expect, it} from "@velocious/testing"
|
|
@@ -245,7 +249,9 @@ export default async function configureTesting() {
|
|
|
245
249
|
}
|
|
246
250
|
```
|
|
247
251
|
|
|
248
|
-
Retry flaky tests by setting a retry count on the test args
|
|
252
|
+
Retry flaky tests by setting a retry count on the test args, or set CLI defaults for
|
|
253
|
+
retries and lifecycle timeout. Import repeatable global setup before the testing
|
|
254
|
+
configuration and test declarations with `--setup`.
|
|
249
255
|
|
|
250
256
|
```js
|
|
251
257
|
describe("Tasks", () => {
|
|
@@ -253,6 +259,10 @@ it("retries a flaky check", {retry: 2}, async () => {})
|
|
|
253
259
|
})
|
|
254
260
|
```
|
|
255
261
|
|
|
262
|
+
```bash
|
|
263
|
+
npx velocious test --retry=2 --timeout=30000 --setup=spec/setup.js spec/
|
|
264
|
+
```
|
|
265
|
+
|
|
256
266
|
Velocious prints the slowest tests after every run so suite hotspots are easy to spot. Each line shows the duration, full description and `file:line`.
|
|
257
267
|
|
|
258
268
|
```bash
|
|
@@ -295,7 +305,7 @@ npx velocious test:timing-manifest:merge --output tmp/test-timings.json \
|
|
|
295
305
|
See [test profiling](docs/test-profiling.md) for lifecycle accounting, custom
|
|
296
306
|
activity spans, schema, and privacy guarantees.
|
|
297
307
|
|
|
298
|
-
Prefer waiting for a real signal or condition over sleeping a fixed duration. `waitForEvent(emitter, eventName, {timeoutMs, filter})` resolves the instant a matching event fires (a background job finishing, a model update, a websocket message) and rejects on timeout; for polling an arbitrary condition, use awaitery's `waitFor`. Both `@velocious/testing` and the backward-compatible `velocious/build/src/testing/test.js` facade are supported imports; the Velocious runner consumes public-package declarations and adds the framework-specific database, request,
|
|
308
|
+
Prefer waiting for a real signal or condition over sleeping a fixed duration. `waitForEvent(emitter, eventName, {timeoutMs, filter})` resolves the instant a matching event fires (a background job finishing, a model update, a websocket message) and rejects on timeout; for polling an arbitrary condition, use awaitery's `waitFor`. Both `@velocious/testing` and the backward-compatible `velocious/build/src/testing/test.js` facade are supported imports; the Velocious runner consumes public-package declarations and adds the framework-specific startup, database, request, browser, profile-span, and cleanup behavior.
|
|
299
309
|
|
|
300
310
|
```js
|
|
301
311
|
import {waitForEvent} from "velocious/build/src/testing/test.js"
|
|
@@ -880,7 +890,7 @@ Use `await FrontendModelBase.waitForIdle()` when a test harness or app lifecycle
|
|
|
880
890
|
Frontend-model HTTP requests always use `credentials: "include"` so shared custom commands can set session cookies without app-level transport overrides.
|
|
881
891
|
|
|
882
892
|
Unexpected frontend-model endpoint failures return their original message and full stack trace by default in every environment, including production. Responses use `errorType: "internal_error"`, a server-generated `correlationId` shared with the matching framework-error report, and the established `debugErrorClass`, `debugErrorMessage`, and `debugBacktrace` fields. Expected application failures can use `VelociousError.safe(message, {errorType, details, code})`; generated frontend-model callers preserve the server's safe error fields without adding irrelevant debug fields. See [docs/frontend-models.md](docs/frontend-models.md#error-payloads).
|
|
883
|
-
Invalid client query descriptors, such as unknown `select`, `where`, `search`, `joins`, `preload`, `group`, `sort`, `pluck`, or Ransack attributes, return the specific frontend-model query error message with `velocious.code: "frontend-model-query-error"` and are not emitted as framework errors. Shared index payloads reject unknown top-level keys per request, including wrapper nesting such as `payload.query.where`; use `payload.where` directly.
|
|
893
|
+
Invalid client query descriptors, such as unknown `select`, `where`, `search`, `joins`, `preload`, `group`, `sort`, `pluck`, or Ransack attributes, return the specific frontend-model query error message with `velocious.code: "frontend-model-query-error"` and are not emitted as framework errors. Shared index payloads reject unknown top-level keys per request, including wrapper nesting such as `payload.query.where`; use `payload.where` directly. Legacy shared index callers may send `payload.authenticationToken`, which is extracted into request context before validation; it is not a query option, `requestContext.authenticationToken` wins when both are present, and unrelated unknown keys still fail. See [frontend-model transport](docs/frontend-models.md#core-transport).
|
|
884
894
|
Invalid frontend-model write attributes and attachment names, including attributes rejected by `permittedParams()`, return the specific safe error message with `velocious.code: "frontend-model-attribute-error"` and are not emitted as framework errors.
|
|
885
895
|
To mask unexpected internal details, explicitly opt out for the application configuration:
|
|
886
896
|
|
|
@@ -2224,6 +2234,16 @@ socket.send(JSON.stringify({
|
|
|
2224
2234
|
}))
|
|
2225
2235
|
```
|
|
2226
2236
|
|
|
2237
|
+
### V2 channel registration, replay, and live-only channels
|
|
2238
|
+
|
|
2239
|
+
`configuration.registerWebsocketChannel(name, ChannelClass)` registers a channel class for the `channel-subscribe` protocol — distinct from the `websocketChannelResolver` above, which serves the legacy `{type: "subscribe"}` path. Subscribers are routed by the class's `matches(broadcastParams)`, and publishers emit with `configuration.broadcastToChannel(name, broadcastParams, body)`.
|
|
2240
|
+
|
|
2241
|
+
- **Stream-scoped replay.** Delivered `channel-message` frames carry the server `eventId`; a client can pass its last-seen `lastEventId` on (re)subscribe and the server replays only the persisted events belonging to that subscription's stream (10-minute retention, written only while the channel is interested). A checkpoint that expired or belongs to a different stream reports `channel-replay-gap` and the subscription continues with live delivery.
|
|
2242
|
+
- **Live-only channels.** `registerWebsocketChannel(name, ChannelClass, {liveOnly: true})` declares a channel that must never reach the replay event log; marking one interested throws, and the persistence decision rejects it even if stale interest state exists.
|
|
2243
|
+
- **Replayable params.** Channel classes can override the static `replayableBroadcastParams(broadcastParams)` hook to control which broadcast params are persisted for replay (for example, stripping server-only values).
|
|
2244
|
+
|
|
2245
|
+
See [docs/websocket-channels.md](docs/websocket-channels.md) for the full wire protocol, V1/V2 gap semantics, and lifecycle guarantees.
|
|
2246
|
+
|
|
2227
2247
|
## Raw websocket handlers
|
|
2228
2248
|
|
|
2229
2249
|
If you need to accept custom websocket message formats (for example, a vendor that does not use the Velocious request/subscribe protocol), provide a `websocketMessageHandlerResolver` in your configuration. It receives the upgrade request and can return a handler object with `onOpen`, `onMessage`, `onClose`, and `onError` hooks:
|
|
@@ -2723,7 +2743,16 @@ const result = await MyJob.replaceScheduled({
|
|
|
2723
2743
|
await MyJob.cancelScheduled(`event:${eventId}:reminder:24h`)
|
|
2724
2744
|
```
|
|
2725
2745
|
|
|
2726
|
-
|
|
2746
|
+
Read the durable owner/history or expedite the same queued row without creating another job:
|
|
2747
|
+
|
|
2748
|
+
```js
|
|
2749
|
+
const scheduled = await MyJob.getScheduledJob(`event:${eventId}:reminder:24h`, {
|
|
2750
|
+
includeLatestTerminal: true
|
|
2751
|
+
})
|
|
2752
|
+
const wake = await MyJob.wakeScheduled(`event:${eventId}:reminder:24h`)
|
|
2753
|
+
```
|
|
2754
|
+
|
|
2755
|
+
A queued owner is atomically cancelled during replacement/cancellation. On Node/TCP, its acknowledgement waits for the corresponding main-process dispatch drain lifecycle. The local adapter commits an adapter-owned transaction before its dispatcher wake runs; inside an ambient application transaction, the mutation and wake remain deferred to that outer commit. Readback returns normalized public jobs, including the transaction-assigned `scheduleOrder` used for causal history ordering; Node retains the per-key order high-water mark when terminal history is pruned. Wake moves only a future queued owner's eligibility to now while preserving its id, attempts, last error, and lineage; repeated calls never enqueue duplicates. A `previousStatus`, cancellation `outcome`, or wake `outcome` of `"handed_off"` means the worker may already be running; Velocious removes or replaces key ownership where appropriate but does not claim that JavaScript stopped. Store a generation/revision in application state, pass it to the job, and re-check it immediately before irreversible effects. Stable keys and full result shapes are documented in [Scheduling One-Off Background Jobs](docs/scheduled-background-job-enqueue.md#replacing-or-cancelling-a-logical-schedule).
|
|
2727
2756
|
|
|
2728
2757
|
Set `deduplicateWhileQueued: true` to coalesce an enqueue onto the earliest identical queued job with the same job name, arguments, and queue when that existing job is scheduled no later than the new request. A retry backed off into the future does not suppress a new immediate enqueue, while repeated immediate triggers and equal or later schedules still coalesce.
|
|
2729
2758
|
|
|
@@ -2817,7 +2846,7 @@ Each job must define exactly one of `every` or `cron`. Cron times are evaluated
|
|
|
2817
2846
|
|
|
2818
2847
|
## Persistence and retries
|
|
2819
2848
|
|
|
2820
|
-
Jobs are persisted in the configured database (`backgroundJobs.databaseIdentifier`) in an internal `background_jobs` table. When a worker picks a job, the main generates a unique lease id before asking the adapter to mark the job handed off, and the worker reports completion or failure back to the main process. If that persistence call has an ambiguous result, only the exact caller-generated lease is conditionally returned; failed recovery is retained for the dispatch error-retry path, so worker admission and concurrency do not remain stranded and a newer lease is never reclaimed. Custom adapters must persist a supplied `markHandedOff({handoffId})` exactly; built-in adapters continue generating one for legacy direct callers that omit it. A legacy worker disconnect returns only that socket's leases immediately. Generation mode instead preserves the exact leases through reconnect grace for the same qualified worker, then returns
|
|
2849
|
+
Jobs are persisted in the configured database (`backgroundJobs.databaseIdentifier`) in an internal `background_jobs` table. When a worker picks a job, the main generates a unique lease id before asking the adapter to mark the job handed off, and the worker reports completion or failure back to the main process. If that persistence call has an ambiguous result, only the exact caller-generated lease is conditionally returned; failed recovery is retained for the dispatch error-retry path, so worker admission and concurrency do not remain stranded and a newer lease is never reclaimed. Custom adapters must persist a supplied `markHandedOff({handoffId})` exactly; built-in adapters continue generating one for legacy direct callers that omit it. A legacy worker disconnect returns only that socket's leases immediately. Generation mode instead preserves the exact leases through reconnect grace for the same qualified worker, starting that grace only after active ownership completes its initial startup drain, then returns absent workers' leases to the global queue on expiry. Late reports are fenced by generation-qualified worker id, lease id, and handoff time so they cannot mutate a newer attempt. This recovery is at-least-once and may repeat application side effects if the disconnected attempt had already started them. A release-retiring worker revokes readiness but retains heartbeat, its unchanged old endpoint, exact-generation reconnect, accepted work, child execution, durable reports, and acknowledgements until its drain settles; retiring/retired mains reject new identities and never grant reconnecting workers readiness. Startup reconnection/adoption is an abnormal crash/legacy-recovery facility, not the normal deploy topology: during ordinary release retirement the old main remains alive and owns its old workers, and they must not reconnect to the new main. A production integration that restarts jobs-main on every deploy and depends on worker adoption is not compliant with the release-generation contract. See [release-generation draining](docs/background-jobs.md#release-generation-draining) and [worker disconnect recovery](docs/background-jobs.md#worker-disconnect-recovery).
|
|
2821
2850
|
|
|
2822
2851
|
Failed jobs are re-queued with backoff and retried up to 10 times by default (10s, 1m, 10m, 1h, then +1h per retry). You can override the retry limit per job:
|
|
2823
2852
|
|
|
@@ -42,4 +42,26 @@ export default class BackgroundJobsAdapterClient {
|
|
|
42
42
|
|
|
43
43
|
return await adapter.cancelScheduled(scheduleKey)
|
|
44
44
|
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Reads a stable schedule through the configured adapter.
|
|
48
|
+
* @param {{scheduleKey: string, includeLatestTerminal?: boolean}} args - Lookup request.
|
|
49
|
+
* @returns {Promise<import("./types.js").BackgroundJobScheduledLookupResult>} - Normalized stable schedule jobs.
|
|
50
|
+
*/
|
|
51
|
+
async getScheduledJob({scheduleKey, includeLatestTerminal}) {
|
|
52
|
+
const adapter = await this.configuration.acquireReadyBackgroundJobsAdapter()
|
|
53
|
+
|
|
54
|
+
return await adapter.getScheduledJob(scheduleKey, {includeLatestTerminal})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Wakes a stable schedule through the configured adapter.
|
|
59
|
+
* @param {{scheduleKey: string}} args - Wake request.
|
|
60
|
+
* @returns {Promise<import("./types.js").BackgroundJobWakeResult>} - Wake result.
|
|
61
|
+
*/
|
|
62
|
+
async wakeScheduled({scheduleKey}) {
|
|
63
|
+
const adapter = await this.configuration.acquireReadyBackgroundJobsAdapter()
|
|
64
|
+
|
|
65
|
+
return await adapter.wakeScheduled(scheduleKey)
|
|
66
|
+
}
|
|
45
67
|
}
|
|
@@ -93,6 +93,21 @@ export default class BackgroundJobsAdapter {
|
|
|
93
93
|
*/
|
|
94
94
|
async cancelScheduled(_scheduleKey) { throw new Error("BackgroundJobsAdapter#cancelScheduled is not implemented") }
|
|
95
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Reads current stable ownership and optional terminal history.
|
|
98
|
+
* @param {string} _scheduleKey - Stable schedule key.
|
|
99
|
+
* @param {{includeLatestTerminal?: boolean}} [_options] - Lookup options.
|
|
100
|
+
* @returns {Promise<import("./types.js").BackgroundJobScheduledLookupResult>} - Normalized public jobs.
|
|
101
|
+
*/
|
|
102
|
+
async getScheduledJob(_scheduleKey, _options = {}) { throw new Error("BackgroundJobsAdapter#getScheduledJob is not implemented") }
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Expedites a future queued stable owner without changing its identity.
|
|
106
|
+
* @param {string} _scheduleKey - Stable schedule key.
|
|
107
|
+
* @returns {Promise<import("./types.js").BackgroundJobWakeResult>} - Wake result.
|
|
108
|
+
*/
|
|
109
|
+
async wakeScheduled(_scheduleKey) { throw new Error("BackgroundJobsAdapter#wakeScheduled is not implemented") }
|
|
110
|
+
|
|
96
111
|
/**
|
|
97
112
|
* Finds the next eligible job.
|
|
98
113
|
* @param {{executionMode?: import("./types.js").BackgroundJobExecutionMode | import("./types.js").BackgroundJobExecutionMode[]}} [_args] - Dequeue filters.
|
|
@@ -1,12 +1,82 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
-
import timeout, {TimeoutError} from "awaitery/build/timeout.js"
|
|
3
|
+
import timeout, { TimeoutError } from "awaitery/build/timeout.js"
|
|
4
4
|
import configurationResolver from "../configuration-resolver.js"
|
|
5
|
+
import isPlainObject from "../utils/plain-object.js"
|
|
5
6
|
import BackgroundJobEnqueueAcknowledgementTimeoutError from "./enqueue-acknowledgement-timeout-error.js"
|
|
6
7
|
import BackgroundJobsSocketRequest from "./socket-request.js"
|
|
7
8
|
import { DEFAULT_GENERATION_HANDSHAKE_TIMEOUT_MS, validateGenerationHandshakeTimeoutMs } from "./generation-handshake-timeout-error.js"
|
|
9
|
+
import { BACKGROUND_JOB_ACTIVE_STATUSES, BACKGROUND_JOB_EXECUTION_MODES, BACKGROUND_JOB_STATUSES, BACKGROUND_JOB_TERMINAL_STATUSES } from "./job-semantics.js"
|
|
8
10
|
|
|
9
11
|
const DEFAULT_ENQUEUE_TIMEOUT_MS = 5000
|
|
12
|
+
const BACKGROUND_JOB_WAKE_OUTCOMES = ["woken", "already_due", "handed_off", "not_found"]
|
|
13
|
+
const BACKGROUND_JOB_NULLABLE_NUMBER_FIELDS = [
|
|
14
|
+
"attempts",
|
|
15
|
+
"childPid",
|
|
16
|
+
"childReceivedAtMs",
|
|
17
|
+
"childStartedAtMs",
|
|
18
|
+
"completedAtMs",
|
|
19
|
+
"createdAtMs",
|
|
20
|
+
"failedAtMs",
|
|
21
|
+
"handedOffAtMs",
|
|
22
|
+
"maxConcurrency",
|
|
23
|
+
"maxRetries",
|
|
24
|
+
"orphanedAtMs",
|
|
25
|
+
"scheduleOrder",
|
|
26
|
+
"scheduledAtMs",
|
|
27
|
+
"timeoutMs"
|
|
28
|
+
]
|
|
29
|
+
const BACKGROUND_JOB_NULLABLE_STRING_FIELDS = ["childInstanceId", "concurrencyKey", "handoffId", "lastError", "scheduleKey", "workerId"]
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Checks a required nullable number from a normalized wire row.
|
|
33
|
+
* @param {ReturnType<typeof JSON.parse>} value - Field value.
|
|
34
|
+
* @returns {boolean} - Whether the field is null or a finite number.
|
|
35
|
+
*/
|
|
36
|
+
function isNullableBackgroundJobNumber(value) {
|
|
37
|
+
return value === null || (typeof value === "number" && Number.isFinite(value))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Checks a required nullable string from a normalized wire row.
|
|
42
|
+
* @param {ReturnType<typeof JSON.parse>} value - Field value.
|
|
43
|
+
* @returns {boolean} - Whether the field is null or a string.
|
|
44
|
+
*/
|
|
45
|
+
function isNullableBackgroundJobString(value) {
|
|
46
|
+
return value === null || typeof value === "string"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Checks that a transport job uses the normalized public camel-case shape.
|
|
51
|
+
* @param {ReturnType<typeof JSON.parse>} value - Transport value.
|
|
52
|
+
* @returns {value is import("./types.js").BackgroundJobRow} - Whether normalized.
|
|
53
|
+
*/
|
|
54
|
+
function isNormalizedBackgroundJob(value) {
|
|
55
|
+
if (!isPlainObject(value)) return false
|
|
56
|
+
|
|
57
|
+
const job = value
|
|
58
|
+
|
|
59
|
+
return typeof job.id === "string"
|
|
60
|
+
&& typeof job.jobName === "string"
|
|
61
|
+
&& Array.isArray(job.args)
|
|
62
|
+
&& BACKGROUND_JOB_EXECUTION_MODES.some((executionMode) => executionMode === job.executionMode)
|
|
63
|
+
&& typeof job.queue === "string"
|
|
64
|
+
&& BACKGROUND_JOB_STATUSES.some((status) => status === job.status)
|
|
65
|
+
&& BACKGROUND_JOB_NULLABLE_NUMBER_FIELDS.every((field) => isNullableBackgroundJobNumber(job[field]))
|
|
66
|
+
&& BACKGROUND_JOB_NULLABLE_STRING_FIELDS.every((field) => isNullableBackgroundJobString(job[field]))
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Describes an unexpected protocol response without echoing its payload.
|
|
71
|
+
* @param {string} operation - Public operation name.
|
|
72
|
+
* @param {import("./types.js").BackgroundJobSocketMessage} message - Response.
|
|
73
|
+
* @returns {Error} - Protocol error.
|
|
74
|
+
*/
|
|
75
|
+
function unexpectedResponseError(operation, message) {
|
|
76
|
+
const responseType = message && typeof message.type === "string" ? message.type : "missing type"
|
|
77
|
+
|
|
78
|
+
return new Error(`Unexpected ${operation} response: ${responseType}`)
|
|
79
|
+
}
|
|
10
80
|
|
|
11
81
|
export default class BackgroundJobsClient {
|
|
12
82
|
/**
|
|
@@ -236,4 +306,80 @@ export default class BackgroundJobsClient {
|
|
|
236
306
|
}
|
|
237
307
|
})
|
|
238
308
|
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Reads current stable ownership and optional terminal history.
|
|
312
|
+
* @param {{scheduleKey: string, includeLatestTerminal?: boolean}} args - Lookup request.
|
|
313
|
+
* @returns {Promise<import("./types.js").BackgroundJobScheduledLookupResult>} - Normalized stable schedule jobs.
|
|
314
|
+
*/
|
|
315
|
+
async getScheduledJob({scheduleKey, includeLatestTerminal}) {
|
|
316
|
+
const request = await this._request()
|
|
317
|
+
|
|
318
|
+
return await request.run({
|
|
319
|
+
onConnect: (jsonSocket) => {
|
|
320
|
+
jsonSocket.send({type: "get-scheduled-job", scheduleKey, includeLatestTerminal})
|
|
321
|
+
},
|
|
322
|
+
onMessage: ({message, resolve, reject}) => {
|
|
323
|
+
if (message?.type === "scheduled-job") {
|
|
324
|
+
const {currentJob, latestTerminalJob} = message
|
|
325
|
+
const currentJobValid = currentJob === null
|
|
326
|
+
|| (isNormalizedBackgroundJob(currentJob) && BACKGROUND_JOB_ACTIVE_STATUSES.some((status) => status === currentJob.status))
|
|
327
|
+
const latestTerminalJobValid = latestTerminalJob === null
|
|
328
|
+
|| (isNormalizedBackgroundJob(latestTerminalJob) && BACKGROUND_JOB_TERMINAL_STATUSES.some((status) => status === latestTerminalJob.status))
|
|
329
|
+
|
|
330
|
+
if (!currentJobValid || !latestTerminalJobValid) {
|
|
331
|
+
reject(new Error("Invalid getScheduledJob response: expected normalized public job values"))
|
|
332
|
+
return
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
resolve({currentJob, latestTerminalJob})
|
|
336
|
+
return
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (message?.type === "get-scheduled-job-error") {
|
|
340
|
+
reject(new Error(message.error || "Failed to read scheduled job"))
|
|
341
|
+
return
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
reject(unexpectedResponseError("getScheduledJob", message))
|
|
345
|
+
}
|
|
346
|
+
})
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Expedites a future queued stable owner without changing job identity.
|
|
351
|
+
* @param {{scheduleKey: string}} args - Wake request.
|
|
352
|
+
* @returns {Promise<import("./types.js").BackgroundJobWakeResult>} - Wake result.
|
|
353
|
+
*/
|
|
354
|
+
async wakeScheduled({scheduleKey}) {
|
|
355
|
+
const request = await this._request()
|
|
356
|
+
|
|
357
|
+
return await request.run({
|
|
358
|
+
onConnect: (jsonSocket) => {
|
|
359
|
+
jsonSocket.send({type: "wake-scheduled", scheduleKey})
|
|
360
|
+
},
|
|
361
|
+
onMessage: ({message, resolve, reject}) => {
|
|
362
|
+
if (message?.type === "schedule-woken") {
|
|
363
|
+
const outcome = message.outcome
|
|
364
|
+
const knownOutcome = BACKGROUND_JOB_WAKE_OUTCOMES.includes(outcome)
|
|
365
|
+
const validJobId = outcome === "not_found" ? message.jobId === null : typeof message.jobId === "string" && message.jobId.length > 0
|
|
366
|
+
|
|
367
|
+
if (!knownOutcome || !validJobId) {
|
|
368
|
+
reject(new Error("Invalid wakeScheduled response"))
|
|
369
|
+
return
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
resolve({jobId: message.jobId, outcome})
|
|
373
|
+
return
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (message?.type === "wake-scheduled-error") {
|
|
377
|
+
reject(new Error(message.error || "Failed to wake scheduled job"))
|
|
378
|
+
return
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
reject(unexpectedResponseError("wakeScheduled", message))
|
|
382
|
+
}
|
|
383
|
+
})
|
|
384
|
+
}
|
|
239
385
|
}
|
|
@@ -8,6 +8,36 @@ export const DEFAULT_BACKGROUND_JOB_QUEUE = "default"
|
|
|
8
8
|
export const QUEUE_CONCURRENCY_KEY_PREFIX = "queue:"
|
|
9
9
|
/** @type {import("./types.js").BackgroundJobExecutionMode[]} */
|
|
10
10
|
export const BACKGROUND_JOB_EXECUTION_MODES = ["inline", "forked", "pooled", "spawned"]
|
|
11
|
+
/** @type {import("./types.js").BackgroundJobActiveStatus[]} */
|
|
12
|
+
export const BACKGROUND_JOB_ACTIVE_STATUSES = ["queued", "handed_off"]
|
|
13
|
+
/** @type {import("./types.js").BackgroundJobTerminalStatus[]} */
|
|
14
|
+
export const BACKGROUND_JOB_TERMINAL_STATUSES = ["cancelled", "completed", "failed", "orphaned"]
|
|
15
|
+
/** @type {import("./types.js").BackgroundJobStatus[]} */
|
|
16
|
+
export const BACKGROUND_JOB_STATUSES = [...BACKGROUND_JOB_ACTIVE_STATUSES, ...BACKGROUND_JOB_TERMINAL_STATUSES]
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Normalizes persisted or transported job status vocabulary.
|
|
20
|
+
* @param {string} value - Candidate status.
|
|
21
|
+
* @returns {import("./types.js").BackgroundJobStatus} - Known status.
|
|
22
|
+
*/
|
|
23
|
+
export function normalizeBackgroundJobStatus(value) {
|
|
24
|
+
const status = BACKGROUND_JOB_STATUSES.find((candidate) => candidate === value)
|
|
25
|
+
|
|
26
|
+
if (status) return status
|
|
27
|
+
|
|
28
|
+
throw new Error(`Unknown background job status: ${value}`)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Validates a stable logical schedule key at every persistence boundary.
|
|
33
|
+
* @param {string} scheduleKey - Stable schedule key.
|
|
34
|
+
* @returns {string} - Validated key.
|
|
35
|
+
*/
|
|
36
|
+
export function normalizeBackgroundJobScheduleKey(scheduleKey) {
|
|
37
|
+
if (typeof scheduleKey === "string" && scheduleKey.length > 0 && scheduleKey.length <= 255) return scheduleKey
|
|
38
|
+
|
|
39
|
+
throw VelociousError.safe("background job scheduleKey must be a non-empty string of at most 255 characters")
|
|
40
|
+
}
|
|
11
41
|
|
|
12
42
|
/**
|
|
13
43
|
* Normalizes a job queue.
|
|
@@ -8,7 +8,9 @@ import PlatformVelociousJob from "./platform-job.js"
|
|
|
8
8
|
import {
|
|
9
9
|
cancelScheduledBackgroundJobForConfiguration,
|
|
10
10
|
enqueueBackgroundJobForConfiguration,
|
|
11
|
-
|
|
11
|
+
getScheduledBackgroundJobForConfiguration,
|
|
12
|
+
replaceScheduledBackgroundJobForConfiguration,
|
|
13
|
+
wakeScheduledBackgroundJobForConfiguration
|
|
12
14
|
} from "./runtime.js"
|
|
13
15
|
|
|
14
16
|
/**
|
|
@@ -84,4 +86,27 @@ export default class VelociousJob extends PlatformVelociousJob {
|
|
|
84
86
|
|
|
85
87
|
return await cancelScheduledBackgroundJobForConfiguration({configuration, scheduleKey})
|
|
86
88
|
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Reads current ownership and optional terminal history for a stable key.
|
|
92
|
+
* @param {string} scheduleKey - Stable logical schedule key.
|
|
93
|
+
* @param {{includeLatestTerminal?: boolean}} [options] - Lookup options.
|
|
94
|
+
* @returns {Promise<import("./types.js").BackgroundJobScheduledLookupResult>} - Normalized stable schedule jobs.
|
|
95
|
+
*/
|
|
96
|
+
static async getScheduledJob(scheduleKey, options = {}) {
|
|
97
|
+
const configuration = await configurationResolver()
|
|
98
|
+
|
|
99
|
+
return await getScheduledBackgroundJobForConfiguration({configuration, scheduleKey, ...options})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Expedites a future queued owner without creating another job.
|
|
104
|
+
* @param {string} scheduleKey - Stable logical schedule key.
|
|
105
|
+
* @returns {Promise<import("./types.js").BackgroundJobWakeResult>} - Wake result.
|
|
106
|
+
*/
|
|
107
|
+
static async wakeScheduled(scheduleKey) {
|
|
108
|
+
const configuration = await configurationResolver()
|
|
109
|
+
|
|
110
|
+
return await wakeScheduledBackgroundJobForConfiguration({configuration, scheduleKey})
|
|
111
|
+
}
|
|
87
112
|
}
|
|
@@ -67,18 +67,46 @@ export default class LocalBackgroundJobsAdapter extends BackgroundJobsAdapter {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
/**
|
|
70
|
-
*
|
|
71
|
-
* @param {string}
|
|
72
|
-
* @returns {Promise<import("./types.js").BackgroundJobCancellationResult>} -
|
|
70
|
+
* Cancels or detaches the current owner of a stable schedule key.
|
|
71
|
+
* @param {string} scheduleKey - Stable schedule key.
|
|
72
|
+
* @returns {Promise<import("./types.js").BackgroundJobCancellationResult>} - Cancellation result.
|
|
73
73
|
*/
|
|
74
|
-
async cancelScheduled(
|
|
74
|
+
async cancelScheduled(scheduleKey) {
|
|
75
|
+
await this.ensureReady()
|
|
76
|
+
return await this.store.cancelScheduled(scheduleKey)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Replaces the current owner of a stable schedule key.
|
|
81
|
+
* @param {{scheduleKey: string, jobName: string, args: Array<ReturnType<typeof JSON.parse>>, options?: import("./types.js").BackgroundJobOptions}} args - Replacement request.
|
|
82
|
+
* @returns {Promise<import("./types.js").BackgroundJobReplacementResult>} - Replacement result.
|
|
83
|
+
*/
|
|
84
|
+
async replaceScheduled(args) {
|
|
85
|
+
await this.ensureReady()
|
|
86
|
+
this.registry.resolve(args.jobName)
|
|
87
|
+
return await this.store.replaceScheduled(args)
|
|
88
|
+
}
|
|
75
89
|
|
|
76
90
|
/**
|
|
77
|
-
*
|
|
78
|
-
* @param {
|
|
79
|
-
* @
|
|
91
|
+
* Reads stable ownership and optional terminal history.
|
|
92
|
+
* @param {string} scheduleKey - Stable schedule key.
|
|
93
|
+
* @param {{includeLatestTerminal?: boolean}} [options] - Lookup options.
|
|
94
|
+
* @returns {Promise<import("./types.js").BackgroundJobScheduledLookupResult>} - Normalized local jobs.
|
|
80
95
|
*/
|
|
81
|
-
async
|
|
96
|
+
async getScheduledJob(scheduleKey, options) {
|
|
97
|
+
await this.ensureReady()
|
|
98
|
+
return await this.store.getScheduledJob(scheduleKey, options)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Makes a future queued stable owner due without replacing it.
|
|
103
|
+
* @param {string} scheduleKey - Stable schedule key.
|
|
104
|
+
* @returns {Promise<import("./types.js").BackgroundJobWakeResult>} - Exact wake result.
|
|
105
|
+
*/
|
|
106
|
+
async wakeScheduled(scheduleKey) {
|
|
107
|
+
await this.ensureReady()
|
|
108
|
+
return await this.store.wakeScheduled(scheduleKey)
|
|
109
|
+
}
|
|
82
110
|
|
|
83
111
|
/**
|
|
84
112
|
* Finds the next eligible local job.
|