@the-open-engine/zeroshot 6.29.0 → 6.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/cli/index.js +2 -4
- package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/codex.js +0 -1
- package/lib/agent-cli-provider/adapters/codex.js.map +1 -1
- package/lib/agent-cli-provider/contract-actions.d.ts +1 -1
- package/lib/agent-cli-provider/contract-actions.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-actions.js +5 -5
- package/lib/agent-cli-provider/contract-actions.js.map +1 -1
- package/lib/agent-cli-provider/contract-invoke.d.ts +1 -1
- package/lib/agent-cli-provider/contract-invoke.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-invoke.js +2 -2
- package/lib/agent-cli-provider/contract-invoke.js.map +1 -1
- package/lib/agent-cli-provider/contract-support.d.ts +1 -1
- package/lib/agent-cli-provider/contract-support.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-support.js +2 -2
- package/lib/agent-cli-provider/contract-support.js.map +1 -1
- package/lib/agent-cli-provider/contract.d.ts +1 -0
- package/lib/agent-cli-provider/contract.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract.js +1 -1
- package/lib/agent-cli-provider/contract.js.map +1 -1
- package/lib/agent-cli-provider/index.d.ts +1 -1
- package/lib/agent-cli-provider/index.d.ts.map +1 -1
- package/lib/agent-cli-provider/index.js +2 -1
- package/lib/agent-cli-provider/index.js.map +1 -1
- package/lib/agent-cli-provider/provider-registry.d.ts +1 -0
- package/lib/agent-cli-provider/provider-registry.d.ts.map +1 -1
- package/lib/agent-cli-provider/provider-registry.js +4 -0
- package/lib/agent-cli-provider/provider-registry.js.map +1 -1
- package/lib/agent-cli-provider/single-agent-runtime.d.ts +3 -3
- package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -1
- package/lib/agent-cli-provider/single-agent-runtime.js +22 -48
- package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -1
- package/lib/cluster/client.cjs +30 -7
- package/lib/cluster/client.d.cts +52 -0
- package/lib/cluster/client.d.mts +52 -0
- package/lib/cluster/client.d.ts +4 -6
- package/lib/cluster/client.mjs +32 -9
- package/lib/cluster/connection-state.cjs +25 -0
- package/lib/cluster/connection-state.d.cts +13 -0
- package/lib/cluster/connection-state.d.mts +13 -0
- package/lib/cluster/connection-state.d.ts +13 -0
- package/lib/cluster/connection-state.mjs +21 -0
- package/lib/{target/hosted-run/contracts.js → cluster/connection-types.cjs} +0 -1
- package/lib/cluster/connection-types.d.cts +33 -0
- package/lib/cluster/connection-types.d.mts +33 -0
- package/lib/cluster/connection-types.d.ts +33 -0
- package/lib/cluster/connection-types.mjs +1 -0
- package/lib/cluster/connection.cjs +18 -46
- package/lib/cluster/connection.d.cts +26 -0
- package/lib/cluster/connection.d.mts +26 -0
- package/lib/cluster/connection.d.ts +7 -25
- package/lib/cluster/connection.mjs +12 -42
- package/lib/cluster/errors.cjs +9 -1
- package/lib/cluster/errors.d.cts +29 -0
- package/lib/cluster/errors.d.mts +29 -0
- package/lib/cluster/errors.d.ts +4 -0
- package/lib/cluster/errors.mjs +7 -0
- package/lib/cluster/frames.d.cts +12 -0
- package/lib/cluster/frames.d.mts +12 -0
- package/lib/cluster/generated/protocol-schema.d.cts +1 -0
- package/lib/cluster/generated/protocol-schema.d.mts +1 -0
- package/lib/cluster/generated/protocol.d.cts +781 -0
- package/lib/cluster/generated/protocol.d.mts +781 -0
- package/lib/cluster/index.cjs +2 -2
- package/lib/cluster/index.d.cts +13 -0
- package/lib/cluster/index.d.mts +13 -0
- package/lib/cluster/index.d.ts +3 -3
- package/lib/cluster/index.mjs +2 -2
- package/lib/cluster/json-source.cjs +1 -0
- package/lib/cluster/json-source.d.cts +5 -0
- package/lib/cluster/json-source.d.mts +5 -0
- package/lib/cluster/json-source.d.ts +1 -0
- package/lib/cluster/json-source.mjs +1 -0
- package/lib/cluster/payload-value.cjs +1 -0
- package/lib/cluster/payload-value.d.cts +8 -0
- package/lib/cluster/payload-value.d.mts +8 -0
- package/lib/cluster/payload-value.d.ts +1 -0
- package/lib/cluster/payload-value.mjs +1 -0
- package/lib/cluster/queue.d.cts +15 -0
- package/lib/cluster/queue.d.mts +15 -0
- package/lib/cluster/socket.cjs +8 -0
- package/lib/cluster/socket.d.cts +13 -0
- package/lib/cluster/socket.d.mts +13 -0
- package/lib/cluster/socket.d.ts +2 -0
- package/lib/cluster/socket.mjs +7 -0
- package/lib/cluster/subscriptions.d.cts +73 -0
- package/lib/cluster/subscriptions.d.mts +73 -0
- package/lib/cluster/subscriptions.d.ts +5 -1
- package/lib/cluster/validators.cjs +1 -0
- package/lib/cluster/validators.d.cts +7 -0
- package/lib/cluster/validators.d.mts +7 -0
- package/lib/cluster/validators.d.ts +1 -0
- package/lib/cluster/validators.mjs +1 -0
- package/lib/cluster-worker/engine-adapter-common.js +37 -0
- package/lib/cluster-worker/engine-adapter.js +24 -30
- package/lib/cluster-worker/engine-start.js +59 -0
- package/lib/cluster-worker/index.js +49 -67
- package/lib/cluster-worker/profiles.js +1 -42
- package/lib/cluster-worker/runtime-dependencies.js +49 -0
- package/lib/cluster-worker/runtime-support.js +2 -34
- package/lib/cluster-worker/terminal-normalizer.js +9 -10
- package/lib/hosted-session/authority.cjs +61 -0
- package/lib/hosted-session/authority.d.cts +3 -0
- package/lib/hosted-session/authority.d.mts +3 -0
- package/lib/hosted-session/authority.d.ts +3 -0
- package/lib/hosted-session/authority.mjs +57 -0
- package/lib/hosted-session/coordinator.cjs +78 -51
- package/lib/hosted-session/coordinator.d.cts +11 -0
- package/lib/hosted-session/coordinator.d.mts +11 -0
- package/lib/hosted-session/coordinator.d.ts +4 -2
- package/lib/hosted-session/coordinator.mjs +75 -51
- package/lib/hosted-session/errors.cjs +25 -0
- package/lib/hosted-session/errors.d.cts +10 -0
- package/lib/hosted-session/errors.d.mts +10 -0
- package/lib/hosted-session/errors.d.ts +10 -0
- package/lib/hosted-session/errors.mjs +19 -0
- package/lib/hosted-session/index.cjs +4 -1
- package/lib/hosted-session/index.d.cts +2 -0
- package/lib/hosted-session/index.d.mts +2 -0
- package/lib/hosted-session/index.d.ts +2 -2
- package/lib/hosted-session/index.mjs +1 -1
- package/lib/hosted-session/reconnecting-watch.cjs +131 -0
- package/lib/hosted-session/reconnecting-watch.d.cts +13 -0
- package/lib/hosted-session/reconnecting-watch.d.mts +13 -0
- package/lib/hosted-session/reconnecting-watch.d.ts +13 -0
- package/lib/hosted-session/reconnecting-watch.mjs +127 -0
- package/lib/hosted-session/types.d.cts +32 -0
- package/lib/hosted-session/types.d.mts +32 -0
- package/lib/hosted-session/types.d.ts +19 -7
- package/lib/hosted-target/access-url.cjs +44 -0
- package/lib/hosted-target/access-url.d.cts +2 -0
- package/lib/hosted-target/access-url.d.mts +2 -0
- package/lib/hosted-target/access-url.d.ts +2 -0
- package/lib/hosted-target/access-url.mjs +41 -0
- package/lib/hosted-target/adapter-request.cjs +12 -0
- package/lib/hosted-target/adapter-request.d.cts +17 -0
- package/lib/hosted-target/adapter-request.d.mts +17 -0
- package/lib/hosted-target/adapter-request.d.ts +17 -0
- package/lib/hosted-target/adapter-request.mjs +9 -0
- package/lib/hosted-target/adapter-types.cjs +2 -0
- package/lib/hosted-target/adapter-types.d.cts +27 -0
- package/lib/hosted-target/adapter-types.d.mts +27 -0
- package/lib/hosted-target/adapter-types.d.ts +27 -0
- package/lib/hosted-target/adapter-types.mjs +1 -0
- package/lib/hosted-target/bounds.cjs +10 -0
- package/lib/hosted-target/bounds.d.cts +7 -0
- package/lib/hosted-target/bounds.d.mts +7 -0
- package/lib/hosted-target/bounds.d.ts +7 -0
- package/lib/hosted-target/bounds.mjs +7 -0
- package/lib/hosted-target/capsule-error-response.cjs +76 -0
- package/lib/hosted-target/capsule-error-response.d.cts +2 -0
- package/lib/hosted-target/capsule-error-response.d.mts +2 -0
- package/lib/hosted-target/capsule-error-response.d.ts +2 -0
- package/lib/hosted-target/capsule-error-response.mjs +73 -0
- package/lib/hosted-target/errors.cjs +88 -0
- package/lib/hosted-target/errors.d.cts +36 -0
- package/lib/hosted-target/errors.d.mts +36 -0
- package/lib/hosted-target/errors.d.ts +36 -0
- package/lib/hosted-target/errors.mjs +75 -0
- package/lib/hosted-target/index.cjs +16 -0
- package/lib/hosted-target/index.d.cts +3 -0
- package/lib/hosted-target/index.d.mts +3 -0
- package/lib/hosted-target/index.d.ts +3 -0
- package/lib/hosted-target/index.mjs +2 -0
- package/lib/hosted-target/response-validation.cjs +169 -0
- package/lib/hosted-target/response-validation.d.cts +5 -0
- package/lib/hosted-target/response-validation.d.mts +5 -0
- package/lib/hosted-target/response-validation.d.ts +5 -0
- package/lib/hosted-target/response-validation.mjs +163 -0
- package/lib/hosted-target/retry-executor.cjs +51 -0
- package/lib/hosted-target/retry-executor.d.cts +8 -0
- package/lib/hosted-target/retry-executor.d.mts +8 -0
- package/lib/hosted-target/retry-executor.d.ts +8 -0
- package/lib/hosted-target/retry-executor.mjs +48 -0
- package/lib/hosted-target/retry.cjs +78 -0
- package/lib/hosted-target/retry.d.cts +9 -0
- package/lib/hosted-target/retry.d.mts +9 -0
- package/lib/hosted-target/retry.d.ts +9 -0
- package/lib/hosted-target/retry.mjs +73 -0
- package/lib/hosted-target/target-adapter.cjs +10 -0
- package/lib/hosted-target/target-adapter.d.cts +3 -0
- package/lib/hosted-target/target-adapter.d.mts +3 -0
- package/lib/hosted-target/target-adapter.d.ts +3 -0
- package/lib/hosted-target/target-adapter.mjs +7 -0
- package/lib/hosted-target/types.cjs +4 -0
- package/lib/hosted-target/types.d.cts +50 -0
- package/lib/hosted-target/types.d.mts +50 -0
- package/lib/hosted-target/types.d.ts +50 -0
- package/lib/hosted-target/types.mjs +1 -0
- package/lib/hosted-target/zero-cloud-v1-adapter.cjs +185 -0
- package/lib/hosted-target/zero-cloud-v1-adapter.d.cts +13 -0
- package/lib/hosted-target/zero-cloud-v1-adapter.d.mts +13 -0
- package/lib/hosted-target/zero-cloud-v1-adapter.d.ts +13 -0
- package/lib/hosted-target/zero-cloud-v1-adapter.mjs +181 -0
- package/lib/start-cluster.js +3 -3
- package/lib/target/bounded-response.cjs +51 -0
- package/lib/target/bounded-response.d.cts +1 -0
- package/lib/target/bounded-response.d.mts +1 -0
- package/lib/target/bounded-response.d.ts +1 -0
- package/lib/target/bounded-response.js +51 -0
- package/lib/target/bounded-response.mjs +48 -0
- package/lib/target/credential-lock.js +1 -3
- package/lib/target/credential-store.js +9 -4
- package/lib/target/device-flow.cjs +213 -0
- package/lib/target/device-flow.d.cts +54 -0
- package/lib/target/device-flow.d.mts +54 -0
- package/lib/target/device-flow.d.ts +26 -14
- package/lib/target/device-flow.js +162 -58
- package/lib/target/device-flow.mjs +203 -0
- package/lib/target/discovery-errors.cjs +10 -0
- package/lib/target/discovery-errors.d.cts +3 -0
- package/lib/target/discovery-errors.d.mts +3 -0
- package/lib/target/discovery-errors.d.ts +3 -0
- package/lib/target/discovery-errors.js +10 -0
- package/lib/target/discovery-errors.mjs +6 -0
- package/lib/target/discovery-sections.cjs +191 -0
- package/lib/target/discovery-sections.d.cts +52 -0
- package/lib/target/discovery-sections.d.mts +52 -0
- package/lib/target/discovery-sections.d.ts +52 -0
- package/lib/target/discovery-sections.js +191 -0
- package/lib/target/discovery-sections.mjs +179 -0
- package/lib/target/discovery-validation.cjs +109 -0
- package/lib/target/discovery-validation.d.cts +28 -0
- package/lib/target/discovery-validation.d.mts +28 -0
- package/lib/target/discovery-validation.d.ts +28 -0
- package/lib/target/discovery-validation.js +109 -0
- package/lib/target/discovery-validation.mjs +99 -0
- package/lib/target/discovery.cjs +112 -0
- package/lib/target/discovery.d.cts +74 -0
- package/lib/target/discovery.d.mts +74 -0
- package/lib/target/discovery.d.ts +67 -5
- package/lib/target/discovery.js +94 -79
- package/lib/target/discovery.mjs +105 -0
- package/lib/target/index.d.ts +6 -6
- package/lib/target/index.js +36 -36
- package/lib/target/oauth-http.d.ts +2 -0
- package/lib/target/oauth-http.js +25 -0
- package/lib/target/refresh-exchange.d.ts +11 -0
- package/lib/target/refresh-exchange.js +24 -0
- package/lib/target/refresh-revocation.d.ts +8 -0
- package/lib/target/refresh-revocation.js +19 -0
- package/lib/target/route-template.cjs +85 -0
- package/lib/target/route-template.d.cts +7 -0
- package/lib/target/route-template.d.mts +7 -0
- package/lib/target/route-template.d.ts +7 -0
- package/lib/target/route-template.js +85 -0
- package/lib/target/route-template.mjs +81 -0
- package/lib/target/session-errors.d.ts +4 -0
- package/lib/target/session-errors.js +12 -0
- package/lib/target/session-verification.d.ts +10 -0
- package/lib/target/session-verification.js +42 -0
- package/lib/target/target-registry.d.ts +7 -3
- package/lib/target/target-registry.js +41 -10
- package/lib/target/target-session-manager.d.ts +48 -0
- package/lib/target/target-session-manager.js +226 -0
- package/lib/target/target-session.d.ts +39 -32
- package/lib/target/target-session.js +63 -151
- package/lib/target/token-response.cjs +51 -0
- package/lib/target/token-response.d.cts +7 -0
- package/lib/target/token-response.d.mts +7 -0
- package/lib/target/token-response.d.ts +7 -0
- package/lib/target/token-response.js +51 -0
- package/lib/target/token-response.mjs +48 -0
- package/package.json +14 -7
- package/protocol/openengine-cluster/v1/worker.schema.json +171 -9
- package/scripts/build-cluster.js +23 -3
- package/scripts/opcore-introduced-check.js +0 -2
- package/src/agent-cli-provider/adapters/codex.ts +0 -1
- package/src/agent-cli-provider/contract-actions.ts +9 -5
- package/src/agent-cli-provider/contract-invoke.ts +3 -2
- package/src/agent-cli-provider/contract-support.ts +12 -6
- package/src/agent-cli-provider/contract.ts +6 -1
- package/src/agent-cli-provider/index.ts +1 -0
- package/src/agent-cli-provider/provider-registry.ts +4 -0
- package/src/agent-cli-provider/single-agent-runtime.ts +32 -45
- package/src/cluster/client.ts +42 -15
- package/src/cluster/connection-state.ts +33 -0
- package/src/cluster/connection-types.ts +28 -0
- package/src/cluster/connection.ts +37 -68
- package/src/cluster/errors.ts +5 -0
- package/src/cluster/index.ts +3 -1
- package/src/cluster/json-source.ts +1 -0
- package/src/cluster/payload-value.ts +1 -0
- package/src/cluster/socket.ts +13 -0
- package/src/cluster/subscriptions.ts +7 -2
- package/src/cluster/validators.ts +1 -0
- package/src/cluster/ws.d.ts +1 -1
- package/src/hosted-session/authority.ts +77 -0
- package/src/hosted-session/coordinator.ts +113 -56
- package/src/hosted-session/errors.ts +21 -0
- package/src/hosted-session/index.ts +14 -2
- package/src/hosted-session/reconnecting-watch.ts +148 -0
- package/src/hosted-session/types.ts +22 -8
- package/src/hosted-target/access-url.ts +46 -0
- package/src/hosted-target/adapter-request.ts +28 -0
- package/src/hosted-target/adapter-types.ts +39 -0
- package/src/hosted-target/bounds.ts +1 -0
- package/src/hosted-target/capsule-error-response.ts +107 -0
- package/src/hosted-target/errors.ts +36 -35
- package/src/hosted-target/index.ts +10 -23
- package/src/hosted-target/response-validation.ts +170 -62
- package/src/hosted-target/retry-executor.ts +62 -0
- package/src/hosted-target/retry.ts +47 -11
- package/src/hosted-target/target-adapter.ts +12 -8
- package/src/hosted-target/types.ts +19 -18
- package/src/hosted-target/zero-cloud-v1-adapter.ts +229 -316
- package/src/isolation-manager.js +1 -16
- package/src/target/bounded-response.ts +68 -0
- package/src/target/credential-lock.ts +1 -3
- package/src/target/credential-store.ts +14 -10
- package/src/target/device-flow.ts +209 -85
- package/src/target/discovery-errors.ts +6 -0
- package/src/target/discovery-sections.ts +251 -0
- package/src/target/discovery-validation.ts +144 -0
- package/src/target/discovery.ts +163 -97
- package/src/target/index.ts +15 -11
- package/src/target/oauth-http.ts +27 -0
- package/src/target/refresh-exchange.ts +36 -0
- package/src/target/refresh-revocation.ts +29 -0
- package/src/target/route-template.ts +105 -0
- package/src/target/session-errors.ts +9 -0
- package/src/target/session-verification.ts +51 -0
- package/src/target/target-registry.ts +60 -27
- package/src/target/target-session-manager.ts +285 -0
- package/src/target/target-session.ts +105 -226
- package/src/target/token-response.ts +62 -0
- package/docker/zeroshot-oecp/Cargo.toml +0 -12
- package/docker/zeroshot-oecp/Dockerfile +0 -65
- package/docker/zeroshot-oecp/src/main.rs +0 -32
- package/lib/target/bounded-json.d.ts +0 -6
- package/lib/target/bounded-json.js +0 -43
- package/lib/target/hosted-run/client.d.ts +0 -26
- package/lib/target/hosted-run/client.js +0 -158
- package/lib/target/hosted-run/commands.d.ts +0 -4
- package/lib/target/hosted-run/commands.js +0 -113
- package/lib/target/hosted-run/contracts.d.ts +0 -49
- package/lib/target/hosted-run/input.d.ts +0 -5
- package/lib/target/hosted-run/input.js +0 -200
- package/lib/target/hosted-run.d.ts +0 -4
- package/lib/target/hosted-run.js +0 -12
- package/src/target/bounded-json.ts +0 -48
- package/src/target/hosted-run/client.ts +0 -198
- package/src/target/hosted-run/commands.ts +0 -140
- package/src/target/hosted-run/contracts.ts +0 -53
- package/src/target/hosted-run/input.ts +0 -199
- package/src/target/hosted-run.ts +0 -8
package/src/cluster/client.ts
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
|
+
/// <reference path="./ws.d.ts" />
|
|
1
2
|
import { PROTOCOL_VERSION } from './generated/protocol.js';
|
|
2
3
|
import type {
|
|
3
4
|
AgentAttachParams, AgentAttachResult, ApplyParams, ApplyResult, DeleteParams, DeleteResult,
|
|
4
5
|
GetParams, GetResult, InitializeParams, InitializeResult, LogsParams, LogsResult,
|
|
5
6
|
PlanParams, PlanResult, ResubmitParams, ResubmitResult, RetryParams, RetryResult,
|
|
6
|
-
StopParams, StopResult, UpdateParams, UpdateResult, WatchParams,
|
|
7
|
+
StopParams, StopResult, UpdateParams, UpdateResult, WatchParams,
|
|
7
8
|
} from './generated/protocol.js';
|
|
8
9
|
import { Connection } from './connection.js';
|
|
9
10
|
import type { CallOptions } from './connection.js';
|
|
10
|
-
import { addSocketListener } from './socket.js';
|
|
11
|
+
import { addSocketEmitterListener, addSocketListener } from './socket.js';
|
|
11
12
|
import type { WebSocketLike } from './socket.js';
|
|
12
|
-
import { ClusterConfigError, ClusterProtocolError, ClusterTransportError } from './errors.js';
|
|
13
|
+
import { ClusterConfigError, ClusterProtocolError, ClusterTransportError, ClusterUpgradeError } from './errors.js';
|
|
13
14
|
import {
|
|
14
15
|
AgentAttachSubscriptionStream,
|
|
15
16
|
LogsSubscriptionStream,
|
|
16
17
|
WatchSubscriptionStream,
|
|
17
18
|
} from './subscriptions.js';
|
|
19
|
+
import type { WatchSubscription } from './subscriptions.js';
|
|
20
|
+
export type { WatchSubscription } from './subscriptions.js';
|
|
18
21
|
|
|
19
22
|
export interface WebSocketFactoryOptions {
|
|
20
23
|
readonly headers?: Readonly<Record<string, string>>;
|
|
@@ -26,7 +29,6 @@ export interface ConnectOptions {
|
|
|
26
29
|
readonly initialize?: InitializeParams;
|
|
27
30
|
readonly headers?: Readonly<Record<string, string>>;
|
|
28
31
|
}
|
|
29
|
-
export interface WatchSubscription { readonly result: WatchResult; readonly stream: WatchSubscriptionStream; }
|
|
30
32
|
export interface LogsSubscription { readonly result: LogsResult; readonly stream: LogsSubscriptionStream; }
|
|
31
33
|
export interface AgentAttachSubscription { readonly result: AgentAttachResult; readonly stream: AgentAttachSubscriptionStream; }
|
|
32
34
|
export interface CoherentWatchSubscription extends WatchSubscription { readonly snapshot: GetResult; }
|
|
@@ -131,13 +133,7 @@ async function defaultWebSocketFactory(
|
|
|
131
133
|
protocols?: string | readonly string[],
|
|
132
134
|
) => WebSocketLike;
|
|
133
135
|
}).WebSocket;
|
|
134
|
-
if (globalWebSocket) {
|
|
135
|
-
if (options?.headers && Object.keys(options.headers).length > 0) {
|
|
136
|
-
throw new ClusterConfigError(
|
|
137
|
-
'WebSocket upgrade headers require the ws library; the browser WebSocket API cannot carry request headers',
|
|
138
|
-
'HEADERS_UNSUPPORTED',
|
|
139
|
-
);
|
|
140
|
-
}
|
|
136
|
+
if (globalWebSocket && (!options?.headers || Object.keys(options.headers).length === 0)) {
|
|
141
137
|
return new globalWebSocket(url, protocols);
|
|
142
138
|
}
|
|
143
139
|
try {
|
|
@@ -175,6 +171,7 @@ async function waitForOpen(socket: WebSocketLike, signal?: AbortSignal): Promise
|
|
|
175
171
|
await new Promise<void>((resolve, reject) => {
|
|
176
172
|
let settled = false;
|
|
177
173
|
const removers: Array<() => void> = [];
|
|
174
|
+
let upgradeFailure: ClusterUpgradeError | ClusterTransportError | undefined;
|
|
178
175
|
const settle = (fn: () => void) => {
|
|
179
176
|
if (settled) return;
|
|
180
177
|
settled = true;
|
|
@@ -186,15 +183,45 @@ async function waitForOpen(socket: WebSocketLike, signal?: AbortSignal): Promise
|
|
|
186
183
|
'connect aborted locally; the server may still have committed this request',
|
|
187
184
|
'AbortError',
|
|
188
185
|
)));
|
|
186
|
+
const onUnexpectedResponse = (...args: unknown[]) => {
|
|
187
|
+
const response = args.find(
|
|
188
|
+
(value) => value !== null && typeof value === 'object' && 'statusCode' in value,
|
|
189
|
+
) as { statusCode?: unknown; resume?: () => void } | undefined;
|
|
190
|
+
const status = response?.statusCode;
|
|
191
|
+
upgradeFailure = typeof status === 'number'
|
|
192
|
+
? new ClusterUpgradeError(status)
|
|
193
|
+
: new ClusterTransportError('WebSocket upgrade rejected', 'UPGRADE_REJECTED');
|
|
194
|
+
response?.resume?.();
|
|
195
|
+
if (socket.terminate) {
|
|
196
|
+
socket.terminate();
|
|
197
|
+
} else {
|
|
198
|
+
settle(() => reject(upgradeFailure));
|
|
199
|
+
}
|
|
200
|
+
};
|
|
189
201
|
removers.push(
|
|
190
202
|
addSocketListener(socket, 'open', () => settle(resolve)),
|
|
191
|
-
addSocketListener(socket, 'error', (error) => settle(() => reject(
|
|
192
|
-
|
|
203
|
+
addSocketListener(socket, 'error', (error) => settle(() => reject(
|
|
204
|
+
upgradeFailure ??
|
|
205
|
+
new ClusterTransportError('WebSocket failed to open', 'OPEN_FAILED', { cause: error }),
|
|
206
|
+
))),
|
|
207
|
+
addSocketListener(socket, 'close', () => settle(() => reject(
|
|
208
|
+
upgradeFailure ??
|
|
209
|
+
new ClusterTransportError('WebSocket closed before opening', 'OPEN_FAILED'),
|
|
210
|
+
))),
|
|
211
|
+
addSocketEmitterListener(socket, 'unexpected-response', onUnexpectedResponse),
|
|
193
212
|
);
|
|
194
213
|
signal?.addEventListener('abort', onAbort, { once: true });
|
|
195
214
|
});
|
|
196
215
|
}
|
|
197
216
|
|
|
217
|
+
async function disposeFailedSocket(socket: WebSocketLike): Promise<void> {
|
|
218
|
+
if (socket.readyState === 0 && socket.terminate) {
|
|
219
|
+
socket.terminate();
|
|
220
|
+
} else {
|
|
221
|
+
await socket.close();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
198
225
|
export async function connect(url: string, options: ConnectOptions = {}): Promise<Connection> {
|
|
199
226
|
const factory = options.webSocketFactory ?? defaultWebSocketFactory;
|
|
200
227
|
let socket: WebSocketLike | undefined;
|
|
@@ -209,7 +236,7 @@ export async function connect(url: string, options: ConnectOptions = {}): Promis
|
|
|
209
236
|
return connection;
|
|
210
237
|
} catch (error) {
|
|
211
238
|
if (socket) {
|
|
212
|
-
try { await socket
|
|
239
|
+
try { await disposeFailedSocket(socket); }
|
|
213
240
|
catch { /* preserve the construction error */ }
|
|
214
241
|
}
|
|
215
242
|
throw error;
|
|
@@ -231,7 +258,7 @@ export async function connectInitialized(url: string, options: ConnectOptions =
|
|
|
231
258
|
return { connection, client, initializeResult };
|
|
232
259
|
} catch (error) {
|
|
233
260
|
if (socket) {
|
|
234
|
-
try { await socket
|
|
261
|
+
try { await disposeFailedSocket(socket); }
|
|
235
262
|
catch { /* preserve the construction error */ }
|
|
236
263
|
}
|
|
237
264
|
throw error;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
if (!Object.prototype.hasOwnProperty.call(Symbol, 'asyncDispose')) {
|
|
2
|
+
Object.defineProperty(Symbol, 'asyncDispose', {
|
|
3
|
+
configurable: false, enumerable: false,
|
|
4
|
+
value: Symbol.for('Symbol.asyncDispose'), writable: false,
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type ConnectionState = 'OPEN' | 'CLOSING' | 'CLOSED';
|
|
9
|
+
export const CONNECTION_TRANSITIONS: Readonly<Record<ConnectionState, readonly ConnectionState[]>> = Object.freeze({
|
|
10
|
+
OPEN: Object.freeze(['CLOSING'] as const),
|
|
11
|
+
CLOSING: Object.freeze(['CLOSED'] as const),
|
|
12
|
+
CLOSED: Object.freeze([] as const),
|
|
13
|
+
});
|
|
14
|
+
export const PROTOCOL_DIAGNOSTIC_CAPACITY = 128;
|
|
15
|
+
export interface ConnectionCloseSnapshot {
|
|
16
|
+
readonly code: number | null;
|
|
17
|
+
readonly reason: null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type Deferred<T> = {
|
|
21
|
+
readonly promise: Promise<T>;
|
|
22
|
+
readonly resolve: (value: T) => void;
|
|
23
|
+
readonly reject: (reason: unknown) => void;
|
|
24
|
+
};
|
|
25
|
+
export function deferred<T>(): Deferred<T> {
|
|
26
|
+
let resolve!: (value: T) => void;
|
|
27
|
+
let reject!: (reason: unknown) => void;
|
|
28
|
+
const promise = new Promise<T>((onResolve, onReject) => {
|
|
29
|
+
resolve = onResolve;
|
|
30
|
+
reject = onReject;
|
|
31
|
+
});
|
|
32
|
+
return { promise, resolve, reject };
|
|
33
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { FrameRecord } from './frames.js';
|
|
2
|
+
import type { BoundedQueue } from './queue.js';
|
|
3
|
+
import type { ClusterMethod } from './generated/protocol.js';
|
|
4
|
+
|
|
5
|
+
export interface CallOptions {
|
|
6
|
+
readonly signal?: AbortSignal;
|
|
7
|
+
readonly requestTimeoutMs?: number;
|
|
8
|
+
}
|
|
9
|
+
export type SubscriptionKind = 'watch' | 'logs' | 'agent/attach';
|
|
10
|
+
export type SubscriptionRegistration = {
|
|
11
|
+
readonly id: string;
|
|
12
|
+
readonly kind: SubscriptionKind;
|
|
13
|
+
readonly queue: BoundedQueue<FrameRecord>;
|
|
14
|
+
overflowed: boolean;
|
|
15
|
+
cancelSent: boolean;
|
|
16
|
+
abortHandler?: () => void;
|
|
17
|
+
abortSignal?: AbortSignal;
|
|
18
|
+
};
|
|
19
|
+
export type EstablishedSubscription<R> = {
|
|
20
|
+
readonly result: R;
|
|
21
|
+
readonly registration: SubscriptionRegistration;
|
|
22
|
+
};
|
|
23
|
+
export type PendingEntry = {
|
|
24
|
+
readonly id: string; readonly method: ClusterMethod; readonly expectedId: string;
|
|
25
|
+
readonly resolve: (value: unknown) => void; readonly reject: (reason: unknown) => void;
|
|
26
|
+
readonly subscriptionKind?: SubscriptionKind; settled: boolean;
|
|
27
|
+
abortHandler?: () => void; signal?: AbortSignal; timeout?: ReturnType<typeof setTimeout>;
|
|
28
|
+
};
|
|
@@ -13,68 +13,32 @@ import { BoundedQueue } from './queue.js';
|
|
|
13
13
|
import { addSocketListener } from './socket.js';
|
|
14
14
|
import type { WebSocketLike } from './socket.js';
|
|
15
15
|
import { assertDefinition, assertMethodResult } from './validators.js';
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
export
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return retained.join('');
|
|
43
|
-
}
|
|
44
|
-
export interface CallOptions { readonly signal?: AbortSignal; readonly requestTimeoutMs?: number; }
|
|
45
|
-
|
|
46
|
-
type Deferred<T> = {
|
|
47
|
-
readonly promise: Promise<T>;
|
|
48
|
-
readonly resolve: (value: T) => void;
|
|
49
|
-
readonly reject: (reason: unknown) => void;
|
|
50
|
-
};
|
|
51
|
-
function deferred<T>(): Deferred<T> {
|
|
52
|
-
let resolve!: (value: T) => void; let reject!: (reason: unknown) => void;
|
|
53
|
-
const promise = new Promise<T>((onResolve, onReject) => {
|
|
54
|
-
resolve = onResolve; reject = onReject;
|
|
55
|
-
});
|
|
56
|
-
return { promise, resolve, reject };
|
|
57
|
-
}
|
|
58
|
-
export type SubscriptionKind = 'watch' | 'logs' | 'agent/attach';
|
|
59
|
-
export type SubscriptionRegistration = {
|
|
60
|
-
readonly id: string;
|
|
61
|
-
readonly kind: SubscriptionKind;
|
|
62
|
-
readonly queue: BoundedQueue<FrameRecord>;
|
|
63
|
-
overflowed: boolean;
|
|
64
|
-
cancelSent: boolean;
|
|
65
|
-
abortHandler?: () => void;
|
|
66
|
-
abortSignal?: AbortSignal;
|
|
67
|
-
};
|
|
68
|
-
export type EstablishedSubscription<R> = {
|
|
69
|
-
readonly result: R;
|
|
70
|
-
readonly registration: SubscriptionRegistration;
|
|
71
|
-
};
|
|
72
|
-
type PendingEntry = {
|
|
73
|
-
readonly id: string; readonly method: ClusterMethod; readonly expectedId: string;
|
|
74
|
-
readonly resolve: (value: unknown) => void; readonly reject: (reason: unknown) => void;
|
|
75
|
-
readonly subscriptionKind?: SubscriptionKind; settled: boolean;
|
|
76
|
-
abortHandler?: () => void; signal?: AbortSignal; timeout?: ReturnType<typeof setTimeout>;
|
|
77
|
-
};
|
|
16
|
+
import {
|
|
17
|
+
CONNECTION_TRANSITIONS,
|
|
18
|
+
PROTOCOL_DIAGNOSTIC_CAPACITY,
|
|
19
|
+
deferred,
|
|
20
|
+
type ConnectionCloseSnapshot,
|
|
21
|
+
type ConnectionState,
|
|
22
|
+
} from './connection-state.js';
|
|
23
|
+
export {
|
|
24
|
+
CONNECTION_TRANSITIONS,
|
|
25
|
+
PROTOCOL_DIAGNOSTIC_CAPACITY,
|
|
26
|
+
type ConnectionCloseSnapshot,
|
|
27
|
+
type ConnectionState,
|
|
28
|
+
} from './connection-state.js';
|
|
29
|
+
import type {
|
|
30
|
+
CallOptions,
|
|
31
|
+
EstablishedSubscription,
|
|
32
|
+
PendingEntry,
|
|
33
|
+
SubscriptionKind,
|
|
34
|
+
SubscriptionRegistration,
|
|
35
|
+
} from './connection-types.js';
|
|
36
|
+
export type {
|
|
37
|
+
CallOptions,
|
|
38
|
+
EstablishedSubscription,
|
|
39
|
+
SubscriptionKind,
|
|
40
|
+
SubscriptionRegistration,
|
|
41
|
+
} from './connection-types.js';
|
|
78
42
|
|
|
79
43
|
export class Connection {
|
|
80
44
|
#state: ConnectionState = 'OPEN'; #sequence = 1;
|
|
@@ -85,7 +49,9 @@ export class Connection {
|
|
|
85
49
|
readonly #ownedSubscriptions = new WeakSet<SubscriptionRegistration>();
|
|
86
50
|
#closePromise?: Promise<void>;
|
|
87
51
|
#closeCode: number | undefined;
|
|
88
|
-
#
|
|
52
|
+
readonly #closedCompletion = deferred<ConnectionCloseSnapshot>();
|
|
53
|
+
readonly closed: Promise<ConnectionCloseSnapshot> = this.#closedCompletion.promise;
|
|
54
|
+
#closeSnapshot: ConnectionCloseSnapshot | undefined;
|
|
89
55
|
readonly closeDiagnostics: unknown[] = [];
|
|
90
56
|
readonly protocolDiagnostics: ClusterProtocolError[] = [];
|
|
91
57
|
|
|
@@ -103,7 +69,8 @@ export class Connection {
|
|
|
103
69
|
get pendingSize(): number { return this.#pending.size; }
|
|
104
70
|
get subscriptionCount(): number { return this.#subscriptions.size; }
|
|
105
71
|
get closeCode(): number | undefined { return this.#closeCode; }
|
|
106
|
-
get closeReason():
|
|
72
|
+
get closeReason(): undefined { return undefined; }
|
|
73
|
+
get closeSnapshot(): ConnectionCloseSnapshot | undefined { return this.#closeSnapshot; }
|
|
107
74
|
call<M extends UnaryClusterMethod>(method: M, params: ClusterMethodParams[M], options: CallOptions = {}): Promise<ClusterMethodResults[M]> {
|
|
108
75
|
if (!(UNARY_METHODS as readonly string[]).includes(method)) {
|
|
109
76
|
throw new ClusterConfigError(`${method} is a subscription method`, 'INVALID_METHOD');
|
|
@@ -289,12 +256,9 @@ export class Connection {
|
|
|
289
256
|
const first = args[0];
|
|
290
257
|
if (typeof first === 'number') {
|
|
291
258
|
this.#closeCode = first;
|
|
292
|
-
const raw = args.length > 1 ? String(args[1]) : undefined;
|
|
293
|
-
this.#closeReason = raw === undefined ? undefined : boundedCloseReason(raw);
|
|
294
259
|
} else if (first !== null && typeof first === 'object') {
|
|
295
|
-
const event = first as { code?: unknown
|
|
260
|
+
const event = first as { code?: unknown };
|
|
296
261
|
if (typeof event.code === 'number') this.#closeCode = event.code;
|
|
297
|
-
if (typeof event.reason === 'string') this.#closeReason = boundedCloseReason(event.reason);
|
|
298
262
|
}
|
|
299
263
|
}
|
|
300
264
|
#startClose(sendCancels: boolean): Promise<void> {
|
|
@@ -319,6 +283,11 @@ export class Connection {
|
|
|
319
283
|
try { remove(); } catch (error) { this.recordDiagnostic(error); }
|
|
320
284
|
}
|
|
321
285
|
this.#transition('CLOSED');
|
|
286
|
+
this.#closeSnapshot = Object.freeze({
|
|
287
|
+
code: this.#closeCode ?? null,
|
|
288
|
+
reason: null,
|
|
289
|
+
});
|
|
290
|
+
this.#closedCompletion.resolve(this.#closeSnapshot);
|
|
322
291
|
}
|
|
323
292
|
#transition(to: ConnectionState): void {
|
|
324
293
|
if (!CONNECTION_TRANSITIONS[this.#state].includes(to)) throw new ClusterInternalError(`illegal connection transition ${this.#state} -> ${to}`, 'ILLEGAL_STATE_TRANSITION');
|
package/src/cluster/errors.ts
CHANGED
|
@@ -15,6 +15,11 @@ export class ClusterTransportError extends ClusterError {}
|
|
|
15
15
|
export class ClusterTimeoutError extends ClusterError {}
|
|
16
16
|
export class ClusterRequestError extends ClusterError {}
|
|
17
17
|
|
|
18
|
+
export class ClusterUpgradeError extends ClusterTransportError {
|
|
19
|
+
constructor(readonly status: number) {
|
|
20
|
+
super(`WebSocket upgrade rejected with HTTP ${status}`, 'UPGRADE_REJECTED');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
18
23
|
export function requestAbortError(method: string): DOMException {
|
|
19
24
|
return new DOMException(
|
|
20
25
|
`${method} aborted locally; the server may still have committed this request`,
|
package/src/cluster/index.ts
CHANGED
|
@@ -10,14 +10,16 @@ export {
|
|
|
10
10
|
ClusterStateError,
|
|
11
11
|
ClusterTimeoutError,
|
|
12
12
|
ClusterTransportError,
|
|
13
|
+
ClusterUpgradeError,
|
|
13
14
|
} from './errors.js';
|
|
14
15
|
export { assertGraphProfile, assertGraphProfileSupported, assertGraphSpec } from './validators.js';
|
|
15
16
|
export * from './payload-value.js';
|
|
16
17
|
export * from './json-source.js';
|
|
17
|
-
export {
|
|
18
|
+
export { CONNECTION_TRANSITIONS, PROTOCOL_DIAGNOSTIC_CAPACITY, Connection } from './connection.js';
|
|
18
19
|
export type {
|
|
19
20
|
CallOptions,
|
|
20
21
|
ConnectionState,
|
|
22
|
+
ConnectionCloseSnapshot,
|
|
21
23
|
} from './connection.js';
|
|
22
24
|
export type { WebSocketLike } from './socket.js';
|
|
23
25
|
export {
|
|
@@ -3,6 +3,7 @@ import { MAX_FRAME_BYTES } from './generated/protocol.js';
|
|
|
3
3
|
|
|
4
4
|
export const MAX_REQUEST_BYTES = MAX_FRAME_BYTES;
|
|
5
5
|
|
|
6
|
+
/** Decode one already-bounded request body with strict UTF-8 and JSON handling. */
|
|
6
7
|
export function decodeBoundedJson(bytes: Uint8Array): unknown {
|
|
7
8
|
if (bytes.length > MAX_REQUEST_BYTES) {
|
|
8
9
|
throw new ClusterRequestError(
|
|
@@ -65,6 +65,7 @@ function evaluate(type: PayloadType, value: unknown, path: string): InternalIssu
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/** Return the first deterministic schema issue without retaining the input value. */
|
|
68
69
|
export function firstInputValidationIssue(
|
|
69
70
|
type: PayloadType,
|
|
70
71
|
value: unknown,
|
package/src/cluster/socket.ts
CHANGED
|
@@ -4,6 +4,7 @@ export interface WebSocketLike {
|
|
|
4
4
|
readonly readyState: number;
|
|
5
5
|
send(data: string, callback?: (error?: Error) => void): void | Promise<void>;
|
|
6
6
|
close(code?: number, reason?: string): void | Promise<void>;
|
|
7
|
+
terminate?(): void;
|
|
7
8
|
addEventListener?(type: string, listener: (...args: unknown[]) => void): void;
|
|
8
9
|
removeEventListener?(type: string, listener: (...args: unknown[]) => void): void;
|
|
9
10
|
on?(type: string, listener: (...args: unknown[]) => void): void;
|
|
@@ -29,3 +30,15 @@ export function addSocketListener(
|
|
|
29
30
|
'INVALID_WEBSOCKET',
|
|
30
31
|
);
|
|
31
32
|
}
|
|
33
|
+
|
|
34
|
+
export function addSocketEmitterListener(
|
|
35
|
+
socket: WebSocketLike,
|
|
36
|
+
type: string,
|
|
37
|
+
listener: (...args: unknown[]) => void,
|
|
38
|
+
): () => void {
|
|
39
|
+
if (socket.on) {
|
|
40
|
+
socket.on(type, listener);
|
|
41
|
+
return () => (socket.off ?? socket.removeListener)?.call(socket, type, listener);
|
|
42
|
+
}
|
|
43
|
+
return addSocketListener(socket, type, listener);
|
|
44
|
+
}
|
|
@@ -22,6 +22,11 @@ export interface Subscription<T> extends AsyncIterator<SubscriptionItem<T>>, Asy
|
|
|
22
22
|
readonly retainedCount: number;
|
|
23
23
|
cancel(): Promise<void>;
|
|
24
24
|
}
|
|
25
|
+
export interface WatchSubscription {
|
|
26
|
+
readonly result: WatchResult;
|
|
27
|
+
readonly stream: WatchSubscriptionStream;
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
|
|
26
31
|
type FrameParser<T> = (frame: FrameRecord) => SubscriptionItem<T>;
|
|
27
32
|
class SubscriptionStream<T> implements Subscription<T> {
|
|
@@ -198,13 +203,13 @@ export class WatchSubscriptionStream implements AsyncIterator<WatchSubscriptionI
|
|
|
198
203
|
await this.#terminate(true); throw error;
|
|
199
204
|
}
|
|
200
205
|
cancel(): Promise<void> { return this.#terminate(true); }
|
|
201
|
-
reconnect(freshConnection: Connection): Promise<
|
|
206
|
+
reconnect(freshConnection: Connection): Promise<WatchSubscription> {
|
|
202
207
|
if (this.#reconnectConsumed) {
|
|
203
208
|
throw new ClusterStateError('watch stream reconnect is one-shot', 'RECONNECT_CONSUMED');
|
|
204
209
|
}
|
|
205
210
|
this.#reconnectConsumed = true; return this.#reconnectOn(freshConnection);
|
|
206
211
|
}
|
|
207
|
-
async #reconnectOn(freshConnection: Connection): Promise<
|
|
212
|
+
async #reconnectOn(freshConnection: Connection): Promise<WatchSubscription> {
|
|
208
213
|
await this.#terminate(true);
|
|
209
214
|
try {
|
|
210
215
|
const runId = this.#runId; const fromCursor = this.#lastDelivered;
|
|
@@ -64,6 +64,7 @@ function assertRequestDefinition(definition: string, value: unknown, code: strin
|
|
|
64
64
|
throw new ClusterRequestError(`${definition} validation failed: ${details}`, code);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
/** Validate an execution graph against the frozen cluster protocol schema. */
|
|
67
68
|
export function assertGraphSpec(value: unknown): asserts value is GraphSpec {
|
|
68
69
|
assertRequestDefinition('GraphSpec', value, 'INVALID_GRAPH');
|
|
69
70
|
}
|
package/src/cluster/ws.d.ts
CHANGED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { ClusterConfigError } from '../cluster/index.js';
|
|
2
|
+
import type { HostedAccess } from './types.js';
|
|
3
|
+
|
|
4
|
+
const LOOPBACK_HOSTS: Readonly<Record<string, true>> = Object.freeze({
|
|
5
|
+
'127.0.0.1': true,
|
|
6
|
+
'::1': true,
|
|
7
|
+
'[::1]': true,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
function parseCanonicalUrl(value: string, message: string, code: string): URL {
|
|
11
|
+
try {
|
|
12
|
+
if (/[\u0000-\u0020\u007f]/.test(value)) throw new TypeError('unsafe URL code point');
|
|
13
|
+
const url = new URL(value);
|
|
14
|
+
if (url.href !== value && url.origin !== value) throw new TypeError('non-canonical URL');
|
|
15
|
+
return url;
|
|
16
|
+
} catch {
|
|
17
|
+
throw new ClusterConfigError(message, code);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function hasOriginSuffix(url: URL): boolean {
|
|
22
|
+
return Boolean(
|
|
23
|
+
url.username ||
|
|
24
|
+
url.password ||
|
|
25
|
+
url.pathname !== '/' ||
|
|
26
|
+
url.search ||
|
|
27
|
+
url.hash,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function normalizedAuthority(value: string): string {
|
|
32
|
+
const message = 'targetAuthority must be an HTTPS or literal-loopback HTTP origin';
|
|
33
|
+
const url = parseCanonicalUrl(value, message, 'INVALID_TARGET_AUTHORITY');
|
|
34
|
+
const loopbackHttp = url.protocol === 'http:' && LOOPBACK_HOSTS[url.hostname] === true;
|
|
35
|
+
if ((url.protocol !== 'https:' && !loopbackHttp) || hasOriginSuffix(url) ||
|
|
36
|
+
value !== url.origin) {
|
|
37
|
+
throw new ClusterConfigError(message, 'INVALID_TARGET_AUTHORITY');
|
|
38
|
+
}
|
|
39
|
+
return url.origin;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function accessAuthorityMatches(endpoint: URL, target: URL): boolean {
|
|
43
|
+
const expectedProtocol = target.protocol === 'http:' ? 'ws:' : 'wss:';
|
|
44
|
+
return (
|
|
45
|
+
endpoint.protocol === expectedProtocol &&
|
|
46
|
+
endpoint.host === target.host &&
|
|
47
|
+
!endpoint.username &&
|
|
48
|
+
!endpoint.password &&
|
|
49
|
+
!endpoint.search &&
|
|
50
|
+
!endpoint.hash
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function validateHostedAccess(access: HostedAccess, targetAuthority: string): void {
|
|
55
|
+
const endpoint = parseCanonicalUrl(
|
|
56
|
+
access.websocketUrl,
|
|
57
|
+
'access endpoint must be an absolute target WebSocket URL',
|
|
58
|
+
'INVALID_ACCESS_ENDPOINT',
|
|
59
|
+
);
|
|
60
|
+
if (endpoint.href !== access.websocketUrl ||
|
|
61
|
+
!accessAuthorityMatches(endpoint, new URL(targetAuthority))) {
|
|
62
|
+
throw new ClusterConfigError(
|
|
63
|
+
'access endpoint must remain on the exact target authority',
|
|
64
|
+
'INVALID_ACCESS_ENDPOINT',
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
if (
|
|
68
|
+
access.protocol !== 'openengine.cluster/v1' ||
|
|
69
|
+
access.tokenType !== 'Bearer' ||
|
|
70
|
+
access.accessToken.length === 0
|
|
71
|
+
) {
|
|
72
|
+
throw new ClusterConfigError(
|
|
73
|
+
'access grant does not match the hosted session contract',
|
|
74
|
+
'INVALID_ACCESS_GRANT',
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|