@byollm/server 0.1.0-alpha.95 → 0.1.0-alpha.96

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 CHANGED
@@ -1,5 +1,5 @@
1
1
  > [!WARNING]
2
- > **Alpha (`0.1.0-alpha.95`) — under active development. Don't use this yet.**
2
+ > **Alpha (`0.1.0-alpha.96`) — under active development. Don't use this yet.**
3
3
  >
4
4
  > Install it deliberately: `npm install @byollm/server@alpha`.
5
5
  >
@@ -83,4 +83,4 @@ export {
83
83
  PollingDelivery,
84
84
  labelFallback
85
85
  };
86
- //# sourceMappingURL=chunk-I3ER27QG.js.map
86
+ //# sourceMappingURL=chunk-YBNLLQZT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/delivery.ts"],"sourcesContent":["import type { DeliveredResult } from \"@byollm/protocol\";\n\n/** Why a wait ended without a result. */\nexport class NoRunnerAvailableError extends Error {\n override readonly name = \"NoRunnerAvailableError\";\n constructor(\n readonly jobId: string,\n readonly reason: string,\n ) {\n super(\n `no runner is available to execute job ${jobId} (${reason}). ` +\n `Fall back to a hosted model, or prompt the user to start their runner.`,\n );\n }\n}\n\n/** The wait exceeded its timeout while a runner was still plausibly working. */\nexport class ResultTimeoutError extends Error {\n override readonly name = \"ResultTimeoutError\";\n constructor(\n readonly jobId: string,\n readonly timeoutMs: number,\n ) {\n super(`job ${jobId} did not finish within ${String(timeoutMs)}ms`);\n }\n}\n\nexport interface WaitOptions {\n /** Give up after this long. Default 5 minutes. */\n readonly timeoutMs?: number;\n /**\n * Called instead of throwing when no runner can take the job. Return a\n * substitute and the wait resolves with it; return nothing and\n * {@link NoRunnerAvailableError} is thrown.\n *\n * **`void` is in the union so that \"return nothing\" can be written the way\n * anybody would write it.** The sentence above has always promised that\n * mode, and the type refused it: `() => { showConnectModal(); }` is\n * `() => void`, which TypeScript will not assign to a signature returning\n * `string | undefined`, and `async () => { await ask(); }` failed the same\n * way. The async one is the worse miss, because prompting somebody to\n * connect and waiting for them IS asynchronous — the README advertises that\n * exact use and the first person to follow it would have been told their\n * correct code was wrong. Found by installing the published package into an\n * empty project and typing the README out.\n *\n * **A string is enough.** It is the app's own fallback answer — a hosted\n * model's text, a cached reply — not wire data, and requiring a whole\n * `DeliveredResult` for it was ceremony that invited invented shapes. The\n * README's own example got it wrong, which is how this was found.\n *\n * **Whatever comes back is labelled `fallback: true` by the wait, not by\n * the caller** — {@link MUSTS.FALLBACK_LABELED}. Work that did not come\n * from the user's own compute must not be reportable as though it did, and\n * that stays true whether an app returns a bare string or a full record it\n * assembled itself. The stamp is applied after this function returns, so\n * there is no shape an app can hand back that hides what it is.\n */\n /* eslint-disable @typescript-eslint/no-invalid-void-type -- the rule exists\n to stop `void` standing in for `undefined`, and this is the case its own\n documentation excepts: a CALLBACK the caller may implement for its effect.\n `undefined` alone does not accept `() => { … }` or `async () => { … }`,\n which is the whole bug being fixed; the runtime already treats anything\n falsy as \"no substitute\". TypeScript's own DOM listener types read the\n same way. */\n readonly onNoRunner?: (\n reason: string,\n ) =>\n | string\n | DeliveredResult\n | undefined\n | void\n | Promise<string | DeliveredResult | undefined | void>;\n /* eslint-enable @typescript-eslint/no-invalid-void-type */\n /** Abort the wait. */\n readonly signal?: AbortSignal;\n}\n\n/**\n * How an app learns a job finished.\n *\n * byollm_003 Rev 1 is explicit that this is a *channel* — webhook, Realtime\n * subscription, or poll — and never an implied in-request `await`. The\n * polling implementation below is the portable default; the Supabase adapter\n * substitutes Realtime for the same interface.\n */\nexport interface ResultDelivery {\n waitFor(jobId: string, options?: WaitOptions): Promise<DeliveredResult>;\n}\n\nexport interface PollingDeliveryDeps {\n /** Current state of the job, or null if unknown. */\n readonly read: (jobId: string) => Promise<DeliveredResult | null>;\n /**\n * Whether a runner could still take this job — **when there is anybody to\n * ask.**\n *\n * Optional since alpha.66, and its absence is the answer rather than a\n * missing dependency. On the cloud lane nothing writes runners into this\n * site's store: devices pair with the relay, so the question has no local\n * answer and `runnerAvailability` refuses to invent one.\n *\n * The refusal was correct and it landed in a loop that asked every 500ms.\n * `job.result()` threw on its first poll for every cloud-lane site — a\n * refusal aimed at outsiders that our own delivery tripped over.\n *\n * Not fixed by catching the throw here. That is a swallowed error in\n * costume, and a catch wide enough to hold it would also eat a store that\n * had genuinely gone away. The instrument is simply not handed over on a\n * lane where it cannot see, and this loop does not ask a question nobody\n * can answer.\n */\n readonly availability?: (\n jobId: string,\n ) => Promise<{ available: boolean; reason?: string; blocked: boolean }>;\n readonly sleep?: (ms: number) => Promise<void>;\n /**\n * Injectable clock. It must advance in step with {@link sleep}: a test that\n * stubs one and not the other gets a loop whose grace window never elapses.\n */\n readonly now?: () => number;\n /**\n * How long a sustained no-runner signal must persist before it is believed.\n * Defaults to {@link NO_RUNNER_GRACE_MS}.\n */\n readonly graceMs?: number;\n}\n\nconst DEFAULT_TIMEOUT_MS = 5 * 60_000;\nconst POLL_INTERVAL_MS = 500;\n/**\n * How long to let a job sit with no available runner before giving up.\n *\n * Not zero: a daemon restarting, or one whose heartbeat is momentarily late,\n * would otherwise fail every job in flight. The signal has to be sustained\n * before it is believed.\n */\nconst NO_RUNNER_GRACE_MS = 10_000;\n\nconst defaultSleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * The portable delivery channel: poll the store until the job is terminal.\n *\n * Correct everywhere and adequate for most apps. An adapter with a push\n * channel should replace it — see the Supabase adapter's Realtime delivery.\n */\nexport class PollingDelivery implements ResultDelivery {\n readonly #deps: PollingDeliveryDeps;\n\n constructor(deps: PollingDeliveryDeps) {\n this.#deps = deps;\n }\n\n async waitFor(\n jobId: string,\n options: WaitOptions = {},\n ): Promise<DeliveredResult> {\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const sleep = this.#deps.sleep ?? defaultSleep;\n const now = this.#deps.now ?? Date.now;\n const graceMs = this.#deps.graceMs ?? NO_RUNNER_GRACE_MS;\n const started = now();\n let noRunnerSince: number | null = null;\n\n for (;;) {\n options.signal?.throwIfAborted();\n\n const current = await this.#deps.read(jobId);\n if (current && isTerminalState(current.state)) return current;\n\n /**\n * The no-runner signal, when this deployment has one.\n *\n * With no instrument there is no sustained-absence signal and no\n * `NoRunnerAvailableError` — the wait ends when the job reaches a\n * terminal state or the timeout does. That is the honest behaviour on\n * the cloud lane, where an unsatisfiable slot is refused at enqueue and\n * a job with nowhere to run expires, both of which arrive through\n * `read` as states rather than as guesses made here.\n */\n const availability = await this.#deps.availability?.(jobId);\n if (\n availability === undefined ||\n availability.available ||\n availability.blocked\n ) {\n // `blocked` means the job is waiting on a dependency, which is not the\n // same event as \"nobody can run this\" ({@link MUSTS.NO_RUNNER_SIGNAL}).\n noRunnerSince = null;\n } else {\n noRunnerSince ??= now();\n if (now() - noRunnerSince >= graceMs) {\n const reason = availability.reason ?? \"no-runner-online\";\n const substitute = await options.onNoRunner?.(reason);\n if (substitute !== undefined) {\n return labelFallback(jobId, substitute);\n }\n throw new NoRunnerAvailableError(jobId, reason);\n }\n }\n\n if (now() - started >= timeoutMs) {\n throw new ResultTimeoutError(jobId, timeoutMs);\n }\n await sleep(POLL_INTERVAL_MS);\n }\n }\n}\n\nfunction isTerminalState(state: string): boolean {\n return (\n state === \"ok\" ||\n state === \"error\" ||\n state === \"canceled\" ||\n state === \"expired\"\n );\n}\n\n/**\n * Turn an app's fallback into a delivered result, marked as one.\n *\n * Exported because there are two delivery channels — polling here, Supabase\n * Realtime next door — and a label applied by one of them is a label an app\n * gets or does not get depending on which store it chose. That is exactly the\n * kind of divergence a \"delivery adapter must not change what a result means\"\n * rule exists to prevent.\n *\n * Two jobs, and the second is the one that matters. A string becomes the\n * obvious record — that is the sugar. Everything, string or record, gets\n * `fallback: true` — that is {@link MUSTS.FALLBACK_LABELED}, and it is\n * applied here rather than trusted from the caller because an app that\n * assembled its own record could otherwise return something indistinguishable\n * from a runner's answer. Spreading the caller's object first and the flag\n * second is deliberate: a supplied `fallback` cannot overwrite it.\n */\nexport function labelFallback(\n jobId: string,\n substitute: string | DeliveredResult,\n): DeliveredResult {\n if (typeof substitute === \"string\") {\n return {\n jobId,\n state: \"ok\",\n outcome: { outcome: \"ok\", text: substitute },\n fallback: true,\n };\n }\n return { ...substitute, fallback: true };\n}\n"],"mappings":";AAGO,IAAM,yBAAN,cAAqC,MAAM;AAAA,EAEhD,YACW,OACA,QACT;AACA;AAAA,MACE,yCAAyC,KAAK,KAAK,MAAM;AAAA,IAE3D;AANS;AACA;AAAA,EAMX;AAAA,EAPW;AAAA,EACA;AAAA,EAHO,OAAO;AAU3B;AAGO,IAAM,qBAAN,cAAiC,MAAM;AAAA,EAE5C,YACW,OACA,WACT;AACA,UAAM,OAAO,KAAK,0BAA0B,OAAO,SAAS,CAAC,IAAI;AAHxD;AACA;AAAA,EAGX;AAAA,EAJW;AAAA,EACA;AAAA,EAHO,OAAO;AAO3B;AAuGA,IAAM,qBAAqB,IAAI;AAC/B,IAAM,mBAAmB;AAQzB,IAAM,qBAAqB;AAE3B,IAAM,eAAe,CAAC,OACpB,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAQ3C,IAAM,kBAAN,MAAgD;AAAA,EAC5C;AAAA,EAET,YAAY,MAA2B;AACrC,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,MAAM,QACJ,OACA,UAAuB,CAAC,GACE;AAC1B,UAAM,YAAY,QAAQ,aAAa;AACvC,UAAM,QAAQ,KAAK,MAAM,SAAS;AAClC,UAAM,MAAM,KAAK,MAAM,OAAO,KAAK;AACnC,UAAM,UAAU,KAAK,MAAM,WAAW;AACtC,UAAM,UAAU,IAAI;AACpB,QAAI,gBAA+B;AAEnC,eAAS;AACP,cAAQ,QAAQ,eAAe;AAE/B,YAAM,UAAU,MAAM,KAAK,MAAM,KAAK,KAAK;AAC3C,UAAI,WAAW,gBAAgB,QAAQ,KAAK,EAAG,QAAO;AAYtD,YAAM,eAAe,MAAM,KAAK,MAAM,eAAe,KAAK;AAC1D,UACE,iBAAiB,UACjB,aAAa,aACb,aAAa,SACb;AAGA,wBAAgB;AAAA,MAClB,OAAO;AACL,0BAAkB,IAAI;AACtB,YAAI,IAAI,IAAI,iBAAiB,SAAS;AACpC,gBAAM,SAAS,aAAa,UAAU;AACtC,gBAAM,aAAa,MAAM,QAAQ,aAAa,MAAM;AACpD,cAAI,eAAe,QAAW;AAC5B,mBAAO,cAAc,OAAO,UAAU;AAAA,UACxC;AACA,gBAAM,IAAI,uBAAuB,OAAO,MAAM;AAAA,QAChD;AAAA,MACF;AAEA,UAAI,IAAI,IAAI,WAAW,WAAW;AAChC,cAAM,IAAI,mBAAmB,OAAO,SAAS;AAAA,MAC/C;AACA,YAAM,MAAM,gBAAgB;AAAA,IAC9B;AAAA,EACF;AACF;AAEA,SAAS,gBAAgB,OAAwB;AAC/C,SACE,UAAU,QACV,UAAU,WACV,UAAU,cACV,UAAU;AAEd;AAmBO,SAAS,cACd,OACA,YACiB;AACjB,MAAI,OAAO,eAAe,UAAU;AAClC,WAAO;AAAA,MACL;AAAA,MACA,OAAO;AAAA,MACP,SAAS,EAAE,SAAS,MAAM,MAAM,WAAW;AAAA,MAC3C,UAAU;AAAA,IACZ;AAAA,EACF;AACA,SAAO,EAAE,GAAG,YAAY,UAAU,KAAK;AACzC;","names":[]}
@@ -22,6 +22,17 @@ interface WaitOptions {
22
22
  * substitute and the wait resolves with it; return nothing and
23
23
  * {@link NoRunnerAvailableError} is thrown.
24
24
  *
25
+ * **`void` is in the union so that "return nothing" can be written the way
26
+ * anybody would write it.** The sentence above has always promised that
27
+ * mode, and the type refused it: `() => { showConnectModal(); }` is
28
+ * `() => void`, which TypeScript will not assign to a signature returning
29
+ * `string | undefined`, and `async () => { await ask(); }` failed the same
30
+ * way. The async one is the worse miss, because prompting somebody to
31
+ * connect and waiting for them IS asynchronous — the README advertises that
32
+ * exact use and the first person to follow it would have been told their
33
+ * correct code was wrong. Found by installing the published package into an
34
+ * empty project and typing the README out.
35
+ *
25
36
  * **A string is enough.** It is the app's own fallback answer — a hosted
26
37
  * model's text, a cached reply — not wire data, and requiring a whole
27
38
  * `DeliveredResult` for it was ceremony that invited invented shapes. The
@@ -34,7 +45,7 @@ interface WaitOptions {
34
45
  * assembled itself. The stamp is applied after this function returns, so
35
46
  * there is no shape an app can hand back that hides what it is.
36
47
  */
37
- readonly onNoRunner?: (reason: string) => string | DeliveredResult | undefined | Promise<string | DeliveredResult | undefined>;
48
+ readonly onNoRunner?: (reason: string) => string | DeliveredResult | undefined | void | Promise<string | DeliveredResult | undefined | void>;
38
49
  /** Abort the wait. */
39
50
  readonly signal?: AbortSignal;
40
51
  }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { StoredKeys, JobKind, Audience, DeliveredResult, Endpoint, Capability } from '@byollm/protocol';
2
- import { P as PollingDeliveryDeps, R as ResultDelivery, W as WaitOptions } from './delivery-CaGbp0Tc.js';
3
- export { N as NoRunnerAvailableError, a as PollingDelivery, b as ResultTimeoutError } from './delivery-CaGbp0Tc.js';
2
+ import { P as PollingDeliveryDeps, R as ResultDelivery, W as WaitOptions } from './delivery-C5mz_Ykm.js';
3
+ export { N as NoRunnerAvailableError, a as PollingDelivery, b as ResultTimeoutError } from './delivery-C5mz_Ykm.js';
4
4
  import { B as ByollmStore, J as JobRecord, E as EnqueueInput, R as RunnerRecord, S as StoredJobInput, C as ClaimArgs, a as RenewArgs, b as RenewResult, A as AdoptArgs, c as CompleteArgs, d as CompleteResult, e as ReleaseArgs, P as PairingRecord, f as ApproveArgs, T as TouchArgs } from './store-Cx2_bck1.js';
5
5
  export { g as CompleteHolder, h as JobStore, i as RunnerStore } from './store-Cx2_bck1.js';
6
6
  import { H as HandlerConfig } from './handlers-CTV3Jc6Q.js';
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  NoRunnerAvailableError,
20
20
  PollingDelivery,
21
21
  ResultTimeoutError
22
- } from "./chunk-I3ER27QG.js";
22
+ } from "./chunk-YBNLLQZT.js";
23
23
 
24
24
  // src/app.ts
25
25
  import {
@@ -1,6 +1,6 @@
1
1
  import { SupabaseClient } from '@supabase/supabase-js';
2
2
  import { B as ByollmStore } from '../store-Cx2_bck1.js';
3
- import { P as PollingDeliveryDeps, R as ResultDelivery } from '../delivery-CaGbp0Tc.js';
3
+ import { P as PollingDeliveryDeps, R as ResultDelivery } from '../delivery-C5mz_Ykm.js';
4
4
  import '@byollm/protocol';
5
5
 
6
6
  /**
@@ -2,7 +2,7 @@ import {
2
2
  NoRunnerAvailableError,
3
3
  ResultTimeoutError,
4
4
  labelFallback
5
- } from "../chunk-I3ER27QG.js";
5
+ } from "../chunk-YBNLLQZT.js";
6
6
 
7
7
  // src/supabase/realtime.ts
8
8
  var DEFAULT_TIMEOUT_MS = 5 * 6e4;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byollm/server",
3
- "version": "0.1.0-alpha.95",
3
+ "version": "0.1.0-alpha.96",
4
4
  "description": "Framework-agnostic BYOLLM protocol handlers, a reference in-memory store, a Next.js mount, and a Supabase adapter.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -30,7 +30,7 @@
30
30
  "node": ">=22.14"
31
31
  },
32
32
  "dependencies": {
33
- "@byollm/protocol": "0.1.0-alpha.95"
33
+ "@byollm/protocol": "0.1.0-alpha.96"
34
34
  },
35
35
  "peerDependencies": {
36
36
  "@supabase/supabase-js": "^2.58.0"
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/delivery.ts"],"sourcesContent":["import type { DeliveredResult } from \"@byollm/protocol\";\n\n/** Why a wait ended without a result. */\nexport class NoRunnerAvailableError extends Error {\n override readonly name = \"NoRunnerAvailableError\";\n constructor(\n readonly jobId: string,\n readonly reason: string,\n ) {\n super(\n `no runner is available to execute job ${jobId} (${reason}). ` +\n `Fall back to a hosted model, or prompt the user to start their runner.`,\n );\n }\n}\n\n/** The wait exceeded its timeout while a runner was still plausibly working. */\nexport class ResultTimeoutError extends Error {\n override readonly name = \"ResultTimeoutError\";\n constructor(\n readonly jobId: string,\n readonly timeoutMs: number,\n ) {\n super(`job ${jobId} did not finish within ${String(timeoutMs)}ms`);\n }\n}\n\nexport interface WaitOptions {\n /** Give up after this long. Default 5 minutes. */\n readonly timeoutMs?: number;\n /**\n * Called instead of throwing when no runner can take the job. Return a\n * substitute and the wait resolves with it; return nothing and\n * {@link NoRunnerAvailableError} is thrown.\n *\n * **A string is enough.** It is the app's own fallback answer — a hosted\n * model's text, a cached reply — not wire data, and requiring a whole\n * `DeliveredResult` for it was ceremony that invited invented shapes. The\n * README's own example got it wrong, which is how this was found.\n *\n * **Whatever comes back is labelled `fallback: true` by the wait, not by\n * the caller** — {@link MUSTS.FALLBACK_LABELED}. Work that did not come\n * from the user's own compute must not be reportable as though it did, and\n * that stays true whether an app returns a bare string or a full record it\n * assembled itself. The stamp is applied after this function returns, so\n * there is no shape an app can hand back that hides what it is.\n */\n readonly onNoRunner?: (\n reason: string,\n ) =>\n | string\n | DeliveredResult\n | undefined\n | Promise<string | DeliveredResult | undefined>;\n /** Abort the wait. */\n readonly signal?: AbortSignal;\n}\n\n/**\n * How an app learns a job finished.\n *\n * byollm_003 Rev 1 is explicit that this is a *channel* — webhook, Realtime\n * subscription, or poll — and never an implied in-request `await`. The\n * polling implementation below is the portable default; the Supabase adapter\n * substitutes Realtime for the same interface.\n */\nexport interface ResultDelivery {\n waitFor(jobId: string, options?: WaitOptions): Promise<DeliveredResult>;\n}\n\nexport interface PollingDeliveryDeps {\n /** Current state of the job, or null if unknown. */\n readonly read: (jobId: string) => Promise<DeliveredResult | null>;\n /**\n * Whether a runner could still take this job — **when there is anybody to\n * ask.**\n *\n * Optional since alpha.66, and its absence is the answer rather than a\n * missing dependency. On the cloud lane nothing writes runners into this\n * site's store: devices pair with the relay, so the question has no local\n * answer and `runnerAvailability` refuses to invent one.\n *\n * The refusal was correct and it landed in a loop that asked every 500ms.\n * `job.result()` threw on its first poll for every cloud-lane site — a\n * refusal aimed at outsiders that our own delivery tripped over.\n *\n * Not fixed by catching the throw here. That is a swallowed error in\n * costume, and a catch wide enough to hold it would also eat a store that\n * had genuinely gone away. The instrument is simply not handed over on a\n * lane where it cannot see, and this loop does not ask a question nobody\n * can answer.\n */\n readonly availability?: (\n jobId: string,\n ) => Promise<{ available: boolean; reason?: string; blocked: boolean }>;\n readonly sleep?: (ms: number) => Promise<void>;\n /**\n * Injectable clock. It must advance in step with {@link sleep}: a test that\n * stubs one and not the other gets a loop whose grace window never elapses.\n */\n readonly now?: () => number;\n /**\n * How long a sustained no-runner signal must persist before it is believed.\n * Defaults to {@link NO_RUNNER_GRACE_MS}.\n */\n readonly graceMs?: number;\n}\n\nconst DEFAULT_TIMEOUT_MS = 5 * 60_000;\nconst POLL_INTERVAL_MS = 500;\n/**\n * How long to let a job sit with no available runner before giving up.\n *\n * Not zero: a daemon restarting, or one whose heartbeat is momentarily late,\n * would otherwise fail every job in flight. The signal has to be sustained\n * before it is believed.\n */\nconst NO_RUNNER_GRACE_MS = 10_000;\n\nconst defaultSleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * The portable delivery channel: poll the store until the job is terminal.\n *\n * Correct everywhere and adequate for most apps. An adapter with a push\n * channel should replace it — see the Supabase adapter's Realtime delivery.\n */\nexport class PollingDelivery implements ResultDelivery {\n readonly #deps: PollingDeliveryDeps;\n\n constructor(deps: PollingDeliveryDeps) {\n this.#deps = deps;\n }\n\n async waitFor(\n jobId: string,\n options: WaitOptions = {},\n ): Promise<DeliveredResult> {\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const sleep = this.#deps.sleep ?? defaultSleep;\n const now = this.#deps.now ?? Date.now;\n const graceMs = this.#deps.graceMs ?? NO_RUNNER_GRACE_MS;\n const started = now();\n let noRunnerSince: number | null = null;\n\n for (;;) {\n options.signal?.throwIfAborted();\n\n const current = await this.#deps.read(jobId);\n if (current && isTerminalState(current.state)) return current;\n\n /**\n * The no-runner signal, when this deployment has one.\n *\n * With no instrument there is no sustained-absence signal and no\n * `NoRunnerAvailableError` — the wait ends when the job reaches a\n * terminal state or the timeout does. That is the honest behaviour on\n * the cloud lane, where an unsatisfiable slot is refused at enqueue and\n * a job with nowhere to run expires, both of which arrive through\n * `read` as states rather than as guesses made here.\n */\n const availability = await this.#deps.availability?.(jobId);\n if (\n availability === undefined ||\n availability.available ||\n availability.blocked\n ) {\n // `blocked` means the job is waiting on a dependency, which is not the\n // same event as \"nobody can run this\" ({@link MUSTS.NO_RUNNER_SIGNAL}).\n noRunnerSince = null;\n } else {\n noRunnerSince ??= now();\n if (now() - noRunnerSince >= graceMs) {\n const reason = availability.reason ?? \"no-runner-online\";\n const substitute = await options.onNoRunner?.(reason);\n if (substitute !== undefined) {\n return labelFallback(jobId, substitute);\n }\n throw new NoRunnerAvailableError(jobId, reason);\n }\n }\n\n if (now() - started >= timeoutMs) {\n throw new ResultTimeoutError(jobId, timeoutMs);\n }\n await sleep(POLL_INTERVAL_MS);\n }\n }\n}\n\nfunction isTerminalState(state: string): boolean {\n return (\n state === \"ok\" ||\n state === \"error\" ||\n state === \"canceled\" ||\n state === \"expired\"\n );\n}\n\n/**\n * Turn an app's fallback into a delivered result, marked as one.\n *\n * Exported because there are two delivery channels — polling here, Supabase\n * Realtime next door — and a label applied by one of them is a label an app\n * gets or does not get depending on which store it chose. That is exactly the\n * kind of divergence a \"delivery adapter must not change what a result means\"\n * rule exists to prevent.\n *\n * Two jobs, and the second is the one that matters. A string becomes the\n * obvious record — that is the sugar. Everything, string or record, gets\n * `fallback: true` — that is {@link MUSTS.FALLBACK_LABELED}, and it is\n * applied here rather than trusted from the caller because an app that\n * assembled its own record could otherwise return something indistinguishable\n * from a runner's answer. Spreading the caller's object first and the flag\n * second is deliberate: a supplied `fallback` cannot overwrite it.\n */\nexport function labelFallback(\n jobId: string,\n substitute: string | DeliveredResult,\n): DeliveredResult {\n if (typeof substitute === \"string\") {\n return {\n jobId,\n state: \"ok\",\n outcome: { outcome: \"ok\", text: substitute },\n fallback: true,\n };\n }\n return { ...substitute, fallback: true };\n}\n"],"mappings":";AAGO,IAAM,yBAAN,cAAqC,MAAM;AAAA,EAEhD,YACW,OACA,QACT;AACA;AAAA,MACE,yCAAyC,KAAK,KAAK,MAAM;AAAA,IAE3D;AANS;AACA;AAAA,EAMX;AAAA,EAPW;AAAA,EACA;AAAA,EAHO,OAAO;AAU3B;AAGO,IAAM,qBAAN,cAAiC,MAAM;AAAA,EAE5C,YACW,OACA,WACT;AACA,UAAM,OAAO,KAAK,0BAA0B,OAAO,SAAS,CAAC,IAAI;AAHxD;AACA;AAAA,EAGX;AAAA,EAJW;AAAA,EACA;AAAA,EAHO,OAAO;AAO3B;AAmFA,IAAM,qBAAqB,IAAI;AAC/B,IAAM,mBAAmB;AAQzB,IAAM,qBAAqB;AAE3B,IAAM,eAAe,CAAC,OACpB,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAQ3C,IAAM,kBAAN,MAAgD;AAAA,EAC5C;AAAA,EAET,YAAY,MAA2B;AACrC,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,MAAM,QACJ,OACA,UAAuB,CAAC,GACE;AAC1B,UAAM,YAAY,QAAQ,aAAa;AACvC,UAAM,QAAQ,KAAK,MAAM,SAAS;AAClC,UAAM,MAAM,KAAK,MAAM,OAAO,KAAK;AACnC,UAAM,UAAU,KAAK,MAAM,WAAW;AACtC,UAAM,UAAU,IAAI;AACpB,QAAI,gBAA+B;AAEnC,eAAS;AACP,cAAQ,QAAQ,eAAe;AAE/B,YAAM,UAAU,MAAM,KAAK,MAAM,KAAK,KAAK;AAC3C,UAAI,WAAW,gBAAgB,QAAQ,KAAK,EAAG,QAAO;AAYtD,YAAM,eAAe,MAAM,KAAK,MAAM,eAAe,KAAK;AAC1D,UACE,iBAAiB,UACjB,aAAa,aACb,aAAa,SACb;AAGA,wBAAgB;AAAA,MAClB,OAAO;AACL,0BAAkB,IAAI;AACtB,YAAI,IAAI,IAAI,iBAAiB,SAAS;AACpC,gBAAM,SAAS,aAAa,UAAU;AACtC,gBAAM,aAAa,MAAM,QAAQ,aAAa,MAAM;AACpD,cAAI,eAAe,QAAW;AAC5B,mBAAO,cAAc,OAAO,UAAU;AAAA,UACxC;AACA,gBAAM,IAAI,uBAAuB,OAAO,MAAM;AAAA,QAChD;AAAA,MACF;AAEA,UAAI,IAAI,IAAI,WAAW,WAAW;AAChC,cAAM,IAAI,mBAAmB,OAAO,SAAS;AAAA,MAC/C;AACA,YAAM,MAAM,gBAAgB;AAAA,IAC9B;AAAA,EACF;AACF;AAEA,SAAS,gBAAgB,OAAwB;AAC/C,SACE,UAAU,QACV,UAAU,WACV,UAAU,cACV,UAAU;AAEd;AAmBO,SAAS,cACd,OACA,YACiB;AACjB,MAAI,OAAO,eAAe,UAAU;AAClC,WAAO;AAAA,MACL;AAAA,MACA,OAAO;AAAA,MACP,SAAS,EAAE,SAAS,MAAM,MAAM,WAAW;AAAA,MAC3C,UAAU;AAAA,IACZ;AAAA,EACF;AACA,SAAO,EAAE,GAAG,YAAY,UAAU,KAAK;AACzC;","names":[]}