@the-open-engine/zeroshot 6.28.0 → 6.29.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/cli/index.js +20 -224
- package/docker/zeroshot-cluster/Dockerfile +2 -3
- 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/cluster/connection-types.cjs +2 -0
- 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.js +1 -11
- package/lib/cluster-worker/engine-input.js +14 -0
- 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/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 -10
- package/lib/target/device-flow.js +162 -53
- 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 +68 -5
- package/lib/target/discovery.js +93 -101
- 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 -31
- package/lib/target/target-session.js +63 -150
- 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 +16 -6
- package/scripts/audit-production-dependencies.js +150 -0
- package/scripts/build-cluster.js +23 -3
- package/scripts/opcore-agent-gate.js +159 -0
- package/scripts/opcore-agent-tool-overlays.js +154 -0
- package/scripts/opcore-introduced-check.js +288 -0
- 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/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 +206 -71
- 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 -115
- 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 -222
- package/src/target/token-response.ts +62 -0
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { IDEMPOTENCY_KEY_PATTERN, MAX_RESPONSE_BYTES } from './bounds.js';
|
|
2
2
|
import {
|
|
3
|
+
TargetAdapterError,
|
|
3
4
|
TargetAuthError,
|
|
4
|
-
TargetConflictError,
|
|
5
|
-
TargetNotFoundError,
|
|
6
5
|
TargetProtocolError,
|
|
7
|
-
TargetRateLimitError,
|
|
8
6
|
TargetTransportError,
|
|
9
|
-
} from './errors.
|
|
10
|
-
import {
|
|
11
|
-
import { DefaultRetryPolicy, parseRetryAfter } from './retry.ts';
|
|
7
|
+
} from './errors.js';
|
|
8
|
+
import { DefaultRetryPolicy } from './retry.js';
|
|
12
9
|
import {
|
|
13
10
|
assertCapsule,
|
|
14
11
|
assertCapsuleAccess,
|
|
15
12
|
assertCapsuleLimits,
|
|
16
13
|
assertCapsuleListPage,
|
|
17
|
-
} from './response-validation.
|
|
18
|
-
import type { TargetAdapter } from './
|
|
14
|
+
} from './response-validation.js';
|
|
15
|
+
import type { TargetAdapter, CreateTargetAdapterOptions } from './adapter-types.js';
|
|
19
16
|
import type {
|
|
20
17
|
AllocateRequest,
|
|
21
18
|
Capsule,
|
|
@@ -24,363 +21,279 @@ import type {
|
|
|
24
21
|
CapsuleListPage,
|
|
25
22
|
Clock,
|
|
26
23
|
HttpTransport,
|
|
24
|
+
ListRequest,
|
|
27
25
|
RetryPolicy,
|
|
28
26
|
TargetAccessTokenProvider,
|
|
29
|
-
|
|
30
|
-
} from '
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
27
|
+
} from './types.js';
|
|
28
|
+
import type { TargetDiscoveryDescriptor } from '../target/discovery.js';
|
|
29
|
+
import { readBoundedResponseJson } from '../target/bounded-response.js';
|
|
30
|
+
import { throwCapsuleServerError } from './capsule-error-response.js';
|
|
31
|
+
import { validateAccessUrl } from './access-url.js';
|
|
32
|
+
import { withTargetRetry } from './retry-executor.js';
|
|
33
|
+
import {
|
|
34
|
+
requestUrl,
|
|
35
|
+
type AdapterRequest,
|
|
36
|
+
type ExecuteArguments,
|
|
37
|
+
} from './adapter-request.js';
|
|
40
38
|
|
|
41
39
|
const DEFAULT_TRANSPORT: HttpTransport = {
|
|
42
|
-
fetch(url
|
|
40
|
+
fetch(url, init) {
|
|
43
41
|
return globalThis.fetch(url, init);
|
|
44
42
|
},
|
|
45
43
|
};
|
|
46
|
-
|
|
47
44
|
const DEFAULT_CLOCK: Clock = { now: () => Date.now() };
|
|
48
45
|
|
|
49
46
|
function throwIfAborted(signal?: AbortSignal): void {
|
|
50
|
-
if (signal?.aborted)
|
|
51
|
-
throw signal.reason
|
|
52
|
-
? new DOMException('The operation was aborted', 'AbortError')
|
|
53
|
-
: signal.reason;
|
|
54
|
-
}
|
|
47
|
+
if (signal?.aborted)
|
|
48
|
+
throw signal.reason ?? new globalThis.DOMException('The operation was aborted', 'AbortError');
|
|
55
49
|
}
|
|
56
50
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
await new Promise<void>((resolve, reject) => {
|
|
62
|
-
const onAbort = (): void => {
|
|
63
|
-
clearTimeout(timer);
|
|
64
|
-
reject(
|
|
65
|
-
signal!.reason === undefined
|
|
66
|
-
? new DOMException('The operation was aborted', 'AbortError')
|
|
67
|
-
: signal!.reason,
|
|
68
|
-
);
|
|
69
|
-
};
|
|
70
|
-
const timer = setTimeout(() => {
|
|
71
|
-
signal?.removeEventListener('abort', onAbort);
|
|
72
|
-
resolve();
|
|
73
|
-
}, delayMs);
|
|
74
|
-
signal?.addEventListener('abort', onAbort, { once: true });
|
|
75
|
-
});
|
|
51
|
+
function validOpaque(value: string, field: string): void {
|
|
52
|
+
if (value.length === 0 || value.length > 1024)
|
|
53
|
+
throw new TargetProtocolError(`${field} is invalid`);
|
|
76
54
|
}
|
|
77
55
|
|
|
78
|
-
function
|
|
79
|
-
|
|
80
|
-
const u = new URL(url);
|
|
81
|
-
return u.origin;
|
|
82
|
-
} catch {
|
|
83
|
-
throw new TargetProtocolError(`Invalid URL: ${url}`);
|
|
84
|
-
}
|
|
56
|
+
function jsonRequest(body: unknown): string {
|
|
57
|
+
return JSON.stringify(body);
|
|
85
58
|
}
|
|
86
59
|
|
|
60
|
+
|
|
87
61
|
export class ZeroCloudV1TargetAdapter implements TargetAdapter {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
62
|
+
readonly #descriptor: TargetDiscoveryDescriptor;
|
|
63
|
+
readonly #organizationId: string;
|
|
64
|
+
readonly #tokenProvider: TargetAccessTokenProvider;
|
|
65
|
+
readonly #transport: HttpTransport;
|
|
66
|
+
readonly #clock: Clock;
|
|
67
|
+
readonly #retryPolicy: RetryPolicy;
|
|
68
|
+
|
|
69
|
+
constructor(options: CreateTargetAdapterOptions) {
|
|
70
|
+
this.#descriptor = options.descriptor;
|
|
71
|
+
this.#organizationId = options.organization.id;
|
|
72
|
+
validOpaque(this.#organizationId, 'organization id');
|
|
73
|
+
this.#tokenProvider = options.tokenProvider;
|
|
74
|
+
this.#transport = options.transport ?? DEFAULT_TRANSPORT;
|
|
75
|
+
this.#clock = options.clock ?? DEFAULT_CLOCK;
|
|
76
|
+
this.#retryPolicy = options.retryPolicy ?? new DefaultRetryPolicy();
|
|
77
|
+
}
|
|
95
78
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
this.clock = opts.clock ?? DEFAULT_CLOCK;
|
|
102
|
-
this.retryPolicy = opts.retryPolicy ?? new DefaultRetryPolicy();
|
|
103
|
-
this.expectedOrigin = originOf(opts.discovery.capsuleV1);
|
|
79
|
+
get credentialInstall(): TargetAdapter['credentialInstall'] {
|
|
80
|
+
const descriptor = this.#descriptor.credentialInstall;
|
|
81
|
+
return descriptor === null
|
|
82
|
+
? Object.freeze({ supported: false as const })
|
|
83
|
+
: Object.freeze({ supported: true as const, descriptor });
|
|
104
84
|
}
|
|
105
85
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
86
|
+
allocate(request: AllocateRequest, signal?: AbortSignal): Promise<Capsule> {
|
|
87
|
+
try {
|
|
88
|
+
if (!IDEMPOTENCY_KEY_PATTERN.test(request.idempotencyKey))
|
|
89
|
+
throw new TargetProtocolError('Idempotency key is invalid');
|
|
90
|
+
if (request.label !== undefined && (request.label.length < 1 || request.label.length > 100)) {
|
|
91
|
+
throw new TargetProtocolError('Capsule label is outside the supported bounds');
|
|
92
|
+
}
|
|
93
|
+
if (request.size !== undefined && !this.#descriptor.sizes.catalog.includes(request.size)) {
|
|
94
|
+
throw new TargetProtocolError('Capsule size is not advertised by the target');
|
|
95
|
+
}
|
|
96
|
+
const body = {
|
|
97
|
+
...(request.label === undefined ? {} : { label: request.label }),
|
|
98
|
+
...(request.size === undefined ? {} : { size: request.size }),
|
|
99
|
+
};
|
|
100
|
+
return this.#execute(
|
|
101
|
+
'allocate',
|
|
102
|
+
'POST',
|
|
103
|
+
this.#descriptor.capsule.routes.allocate,
|
|
104
|
+
{ org_id: this.#organizationId },
|
|
105
|
+
201,
|
|
106
|
+
assertCapsule,
|
|
107
|
+
signal,
|
|
108
|
+
{
|
|
109
|
+
body: jsonRequest(body),
|
|
110
|
+
headers: { 'Idempotency-Key': request.idempotencyKey },
|
|
111
|
+
}
|
|
110
112
|
);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
return Promise.reject(error);
|
|
111
115
|
}
|
|
112
|
-
|
|
113
|
-
const body = JSON.stringify({ profile: req.profile, organization: this.organization });
|
|
114
|
-
|
|
115
|
-
return this._withRetry(async () => {
|
|
116
|
-
const resp = await this._request('POST', '/capsules', {
|
|
117
|
-
signal,
|
|
118
|
-
body,
|
|
119
|
-
headers: { 'Idempotency-Key': req.idempotencyKey },
|
|
120
|
-
});
|
|
121
|
-
const json = await this._readJson(resp);
|
|
122
|
-
return assertCapsule(json);
|
|
123
|
-
}, signal);
|
|
124
116
|
}
|
|
125
117
|
|
|
126
|
-
async list(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
118
|
+
async list(request: ListRequest = {}, signal?: AbortSignal): Promise<CapsuleListPage> {
|
|
119
|
+
if (request.cursor !== undefined) validOpaque(request.cursor, 'cursor');
|
|
120
|
+
if (
|
|
121
|
+
request.limit !== undefined &&
|
|
122
|
+
(!Number.isSafeInteger(request.limit) ||
|
|
123
|
+
request.limit < 1 ||
|
|
124
|
+
request.limit > this.#descriptor.pagination.maxPageSize)
|
|
125
|
+
) {
|
|
126
|
+
throw new TargetProtocolError('Pagination limit is outside the advertised bounds');
|
|
127
|
+
}
|
|
128
|
+
const page = await this.#execute(
|
|
129
|
+
'list',
|
|
130
|
+
'GET',
|
|
131
|
+
this.#descriptor.capsule.routes.list,
|
|
132
|
+
{
|
|
133
|
+
org_id: this.#organizationId,
|
|
134
|
+
cursor: request.cursor,
|
|
135
|
+
limit: request.limit,
|
|
136
|
+
},
|
|
137
|
+
200,
|
|
138
|
+
assertCapsuleListPage,
|
|
139
|
+
signal
|
|
140
|
+
);
|
|
141
|
+
if (request.cursor !== undefined && page.nextCursor === request.cursor)
|
|
142
|
+
throw new TargetProtocolError('Target returned a pagination loop');
|
|
143
|
+
return page;
|
|
140
144
|
}
|
|
141
145
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
146
|
+
inspect(capsuleId: string, signal?: AbortSignal): Promise<Capsule> {
|
|
147
|
+
validOpaque(capsuleId, 'capsule id');
|
|
148
|
+
return this.#execute(
|
|
149
|
+
'inspect',
|
|
150
|
+
'GET',
|
|
151
|
+
this.#descriptor.capsule.routes.inspect,
|
|
152
|
+
{ org_id: this.#organizationId, capsule_id: capsuleId },
|
|
153
|
+
200,
|
|
154
|
+
assertCapsule,
|
|
155
|
+
signal
|
|
156
|
+
);
|
|
150
157
|
}
|
|
151
158
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}, signal);
|
|
159
|
+
terminate(capsuleId: string, signal?: AbortSignal): Promise<Capsule> {
|
|
160
|
+
validOpaque(capsuleId, 'capsule id');
|
|
161
|
+
return this.#execute(
|
|
162
|
+
'terminate',
|
|
163
|
+
'DELETE',
|
|
164
|
+
this.#descriptor.capsule.routes.terminate,
|
|
165
|
+
{ org_id: this.#organizationId, capsule_id: capsuleId },
|
|
166
|
+
202,
|
|
167
|
+
assertCapsule,
|
|
168
|
+
signal
|
|
169
|
+
);
|
|
164
170
|
}
|
|
165
171
|
|
|
166
|
-
|
|
167
|
-
return this
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
172
|
+
limits(signal?: AbortSignal): Promise<CapsuleLimits> {
|
|
173
|
+
return this.#execute(
|
|
174
|
+
'limits',
|
|
175
|
+
'GET',
|
|
176
|
+
this.#descriptor.capsule.routes.limits,
|
|
177
|
+
{ org_id: this.#organizationId },
|
|
178
|
+
200,
|
|
179
|
+
assertCapsuleLimits,
|
|
180
|
+
signal
|
|
181
|
+
);
|
|
173
182
|
}
|
|
174
183
|
|
|
175
184
|
async access(capsuleId: string, signal?: AbortSignal): Promise<CapsuleAccess> {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
+
validOpaque(capsuleId, 'capsule id');
|
|
186
|
+
const result = await this.#execute(
|
|
187
|
+
'access',
|
|
188
|
+
'POST',
|
|
189
|
+
this.#descriptor.capsule.routes.access,
|
|
190
|
+
{ capsule_id: capsuleId },
|
|
191
|
+
200,
|
|
192
|
+
assertCapsuleAccess,
|
|
193
|
+
signal,
|
|
194
|
+
{
|
|
195
|
+
body: jsonRequest({ protocol: 'openengine.cluster/v1' }),
|
|
196
|
+
}
|
|
197
|
+
);
|
|
198
|
+
validateAccessUrl(result.websocketUrl, capsuleId, this.#descriptor);
|
|
199
|
+
return result;
|
|
185
200
|
}
|
|
186
201
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
opts: { signal?: AbortSignal | undefined; body?: string | undefined; headers?: Record<string, string> | undefined },
|
|
191
|
-
): Promise<Response> {
|
|
192
|
-
const url = `${this.discovery.capsuleV1}${path}`;
|
|
193
|
-
|
|
194
|
-
const responseOrigin = originOf(url);
|
|
195
|
-
if (responseOrigin !== this.expectedOrigin) {
|
|
196
|
-
throw new TargetProtocolError(
|
|
197
|
-
`Origin mismatch: expected ${this.expectedOrigin}, got ${responseOrigin}`,
|
|
198
|
-
);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
let token: string;
|
|
202
|
-
try {
|
|
203
|
-
token = await this.tokenProvider.getAccessToken(opts.signal);
|
|
204
|
-
} catch (err) {
|
|
205
|
-
throw new TargetTransportError('Failed to acquire access token', err);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const headers: Record<string, string> = {
|
|
209
|
-
'Authorization': `Bearer ${token}`,
|
|
210
|
-
'Content-Type': 'application/json',
|
|
211
|
-
'Accept': 'application/json',
|
|
212
|
-
...opts.headers,
|
|
213
|
-
};
|
|
202
|
+
#execute<T>(...args: ExecuteArguments<T>): Promise<T> {
|
|
203
|
+
return Promise.resolve().then(() => this.#executeExpanded(args));
|
|
204
|
+
}
|
|
214
205
|
|
|
215
|
-
|
|
216
|
-
const
|
|
206
|
+
#executeExpanded<T>(args: ExecuteArguments<T>): Promise<T> {
|
|
207
|
+
const [
|
|
208
|
+
operation,
|
|
217
209
|
method,
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
210
|
+
template,
|
|
211
|
+
values,
|
|
212
|
+
expectedStatus,
|
|
213
|
+
validate,
|
|
214
|
+
signal,
|
|
215
|
+
request = {},
|
|
216
|
+
] = args;
|
|
217
|
+
let path: string;
|
|
224
218
|
try {
|
|
225
|
-
|
|
226
|
-
} catch
|
|
227
|
-
|
|
228
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
229
|
-
if (msg.includes('redirect')) {
|
|
230
|
-
throw new TargetProtocolError(`Redirect rejected for ${method} ${path}`, err);
|
|
231
|
-
}
|
|
232
|
-
throw new TargetTransportError(`Network error during ${method} ${path}`, err);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
if (response.status < 200 || response.status >= 300) {
|
|
236
|
-
await this._mapStatusError(response, method, path, opts.headers);
|
|
219
|
+
path = template.expand(values);
|
|
220
|
+
} catch {
|
|
221
|
+
throw new TargetProtocolError('Capsule route expansion is unsafe');
|
|
237
222
|
}
|
|
238
|
-
|
|
239
|
-
|
|
223
|
+
return withTargetRetry(
|
|
224
|
+
operation,
|
|
225
|
+
async () => {
|
|
226
|
+
const response = await this.#request(method, path, signal, request);
|
|
227
|
+
if (response.status >= 300 && response.status < 400) {
|
|
228
|
+
await response.body?.cancel().catch(() => undefined);
|
|
229
|
+
throw new TargetProtocolError('Capsule redirects are forbidden');
|
|
230
|
+
}
|
|
231
|
+
if (response.status !== expectedStatus) {
|
|
232
|
+
if (response.status >= 200 && response.status < 300) {
|
|
233
|
+
await response.body?.cancel().catch(() => undefined);
|
|
234
|
+
throw new TargetProtocolError('Target returned an unexpected success status');
|
|
235
|
+
}
|
|
236
|
+
await throwCapsuleServerError(
|
|
237
|
+
response,
|
|
238
|
+
(errorResponse) => this.#readJson(errorResponse),
|
|
239
|
+
this.#clock,
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
return validate(await this.#readJson(response));
|
|
243
|
+
},
|
|
244
|
+
signal,
|
|
245
|
+
{ clock: this.#clock, policy: this.#retryPolicy },
|
|
246
|
+
);
|
|
240
247
|
}
|
|
241
248
|
|
|
242
|
-
|
|
243
|
-
response: Response,
|
|
249
|
+
async #request(
|
|
244
250
|
method: string,
|
|
245
251
|
path: string,
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
if (status === 401 || status === 403) throw new TargetAuthError(context);
|
|
253
|
-
if (status === 404) throw new TargetNotFoundError(context);
|
|
254
|
-
if (status === 409) {
|
|
255
|
-
const idempotencyKey = headers?.['Idempotency-Key'] ?? 'unknown';
|
|
256
|
-
throw new TargetConflictError(idempotencyKey, context);
|
|
257
|
-
}
|
|
258
|
-
if (status === 429) {
|
|
259
|
-
const retryAfterMs = parseRetryAfter(response.headers.get('Retry-After'), this.clock);
|
|
260
|
-
throw new TargetRateLimitError(context, retryAfterMs ?? undefined);
|
|
261
|
-
}
|
|
262
|
-
if (status >= 500) throw new TargetTransportError(context);
|
|
263
|
-
throw new TargetProtocolError(`Unexpected status ${context}`);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
private async _readJson(response: Response): Promise<unknown> {
|
|
267
|
-
const contentLength = response.headers.get('Content-Length');
|
|
268
|
-
if (contentLength !== null) {
|
|
269
|
-
const len = parseInt(contentLength, 10);
|
|
270
|
-
if (Number.isFinite(len) && len > MAX_RESPONSE_BYTES) {
|
|
271
|
-
throw new TargetProtocolError(
|
|
272
|
-
`Response too large: ${len} bytes exceeds limit of ${MAX_RESPONSE_BYTES}`,
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
let text: string;
|
|
278
|
-
try {
|
|
279
|
-
const reader = response.body?.getReader();
|
|
280
|
-
if (!reader) {
|
|
281
|
-
text = await response.text();
|
|
282
|
-
} else {
|
|
283
|
-
const chunks: Uint8Array[] = [];
|
|
284
|
-
let totalBytes = 0;
|
|
285
|
-
while (true) {
|
|
286
|
-
const { done, value } = await reader.read();
|
|
287
|
-
if (done) break;
|
|
288
|
-
totalBytes += value.byteLength;
|
|
289
|
-
if (totalBytes > MAX_RESPONSE_BYTES) {
|
|
290
|
-
reader.cancel();
|
|
291
|
-
throw new TargetProtocolError(
|
|
292
|
-
`Response body exceeds limit of ${MAX_RESPONSE_BYTES} bytes`,
|
|
293
|
-
);
|
|
294
|
-
}
|
|
295
|
-
chunks.push(value);
|
|
296
|
-
}
|
|
297
|
-
const combined = new Uint8Array(totalBytes);
|
|
298
|
-
let offset = 0;
|
|
299
|
-
for (const chunk of chunks) {
|
|
300
|
-
combined.set(chunk, offset);
|
|
301
|
-
offset += chunk.byteLength;
|
|
302
|
-
}
|
|
303
|
-
text = new TextDecoder().decode(combined);
|
|
304
|
-
}
|
|
305
|
-
} catch (err) {
|
|
306
|
-
if (err instanceof TargetProtocolError) throw err;
|
|
307
|
-
throw new TargetProtocolError('Failed to read response body', err);
|
|
308
|
-
}
|
|
309
|
-
|
|
252
|
+
signal: AbortSignal | undefined,
|
|
253
|
+
request: AdapterRequest,
|
|
254
|
+
): Promise<Response> {
|
|
255
|
+
throwIfAborted(signal);
|
|
256
|
+
const url = requestUrl(path, this.#descriptor);
|
|
257
|
+
let token: string;
|
|
310
258
|
try {
|
|
311
|
-
|
|
312
|
-
} catch
|
|
313
|
-
throw new
|
|
259
|
+
token = await this.#tokenProvider.getAccessToken(signal);
|
|
260
|
+
} catch {
|
|
261
|
+
throw new TargetAuthError('Target access authorization failed');
|
|
314
262
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
263
|
+
const init: RequestInit & { redirect: 'manual' } = {
|
|
264
|
+
method,
|
|
265
|
+
headers: {
|
|
266
|
+
Accept: 'application/json',
|
|
267
|
+
Authorization: `Bearer ${token}`,
|
|
268
|
+
...(request.body === undefined ? {} : { 'Content-Type': 'application/json' }),
|
|
269
|
+
...request.headers,
|
|
270
|
+
},
|
|
271
|
+
redirect: 'manual',
|
|
272
|
+
};
|
|
273
|
+
if (request.body !== undefined) init.body = request.body;
|
|
274
|
+
if (signal !== undefined) init.signal = signal;
|
|
323
275
|
try {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
const remaining = MAX_ERROR_BODY_BYTES - totalBytes;
|
|
329
|
-
const retained = value.byteLength > remaining ? value.slice(0, remaining) : value;
|
|
330
|
-
chunks.push(retained);
|
|
331
|
-
totalBytes += retained.byteLength;
|
|
332
|
-
if (retained.byteLength !== value.byteLength || totalBytes === MAX_ERROR_BODY_BYTES) {
|
|
333
|
-
void reader.cancel().catch(() => undefined);
|
|
334
|
-
break;
|
|
335
|
-
}
|
|
276
|
+
const response = await this.#transport.fetch(url.href, init);
|
|
277
|
+
if (response.url && new globalThis.URL(response.url).href !== url.href) {
|
|
278
|
+
await response.body?.cancel().catch(() => undefined);
|
|
279
|
+
throw new TargetProtocolError('Capsule response changed target route');
|
|
336
280
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
const combined = new Uint8Array(totalBytes);
|
|
343
|
-
let offset = 0;
|
|
344
|
-
for (const chunk of chunks) {
|
|
345
|
-
combined.set(chunk, offset);
|
|
346
|
-
offset += chunk.byteLength;
|
|
281
|
+
return response;
|
|
282
|
+
} catch (error) {
|
|
283
|
+
if (error instanceof TargetAdapterError) throw error;
|
|
284
|
+
throwIfAborted(signal);
|
|
285
|
+
throw new TargetTransportError('Capsule transport failed');
|
|
347
286
|
}
|
|
348
|
-
return new TextDecoder().decode(combined);
|
|
349
287
|
}
|
|
350
288
|
|
|
351
|
-
private async _withRetry<T>(
|
|
352
|
-
fn: () => Promise<T>,
|
|
353
|
-
signal?: AbortSignal,
|
|
354
|
-
): Promise<T> {
|
|
355
|
-
const startTime = this.clock.now();
|
|
356
|
-
let attempt = 0;
|
|
357
|
-
|
|
358
|
-
while (true) {
|
|
359
|
-
throwIfAborted(signal);
|
|
360
|
-
try {
|
|
361
|
-
return await fn();
|
|
362
|
-
} catch (err) {
|
|
363
|
-
throwIfAborted(signal);
|
|
364
|
-
if (!(err instanceof TargetAdapterError)) throw err;
|
|
365
|
-
if (err instanceof TargetAuthError) throw err;
|
|
366
|
-
|
|
367
|
-
attempt++;
|
|
368
|
-
const elapsed = this.clock.now() - startTime;
|
|
369
|
-
const decision = this.retryPolicy.shouldRetry(attempt, elapsed, err);
|
|
370
|
-
const remaining = MAX_RETRY_ELAPSED_MS - elapsed;
|
|
371
|
-
|
|
372
|
-
if (
|
|
373
|
-
!decision.retry ||
|
|
374
|
-
!Number.isFinite(decision.delayMs) ||
|
|
375
|
-
decision.delayMs < 0 ||
|
|
376
|
-
decision.delayMs >= remaining
|
|
377
|
-
) {
|
|
378
|
-
throw err;
|
|
379
|
-
}
|
|
380
289
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
290
|
+
#readJson(response: Response): Promise<unknown> {
|
|
291
|
+
return readBoundedResponseJson(response, MAX_RESPONSE_BYTES, (kind) =>
|
|
292
|
+
new TargetProtocolError(
|
|
293
|
+
kind === 'size'
|
|
294
|
+
? 'Capsule response exceeds the size limit'
|
|
295
|
+
: 'Capsule response is not valid UTF-8 JSON',
|
|
296
|
+
),
|
|
297
|
+
);
|
|
385
298
|
}
|
|
386
299
|
}
|