@floegence/flowersec-core 0.27.0 → 2.0.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/README.md +91 -22
- package/THIRD_PARTY_NOTICES.md +141 -0
- package/dist/browser/connectSession.d.ts +13 -0
- package/dist/browser/connectSession.js +40 -0
- package/dist/browser/index.d.ts +3 -10
- package/dist/browser/index.js +2 -5
- package/dist/browser/runtimeCapability.d.ts +13 -0
- package/dist/browser/runtimeCapability.js +27 -0
- package/dist/browser/sessionRuntime.d.ts +2 -0
- package/dist/browser/sessionRuntime.js +21 -0
- package/dist/browser/webTransportClient.d.ts +8 -0
- package/dist/browser/webTransportClient.js +55 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +126 -0
- package/dist/cliSpendMarker.d.ts +1 -0
- package/dist/cliSpendMarker.js +22 -0
- package/dist/connectionController.d.ts +54 -0
- package/dist/connectionController.js +438 -0
- package/dist/connector/adapters/webSocketCandidate.d.ts +7 -0
- package/dist/connector/adapters/webSocketCandidate.js +116 -0
- package/dist/connector/adapters/webTransportCandidate.d.ts +5 -0
- package/dist/connector/adapters/webTransportCandidate.js +73 -0
- package/dist/connector/admissionCommit.d.ts +31 -0
- package/dist/connector/admissionCommit.js +239 -0
- package/dist/connector/sessionAcceptor.d.ts +16 -0
- package/dist/connector/sessionAcceptor.js +114 -0
- package/dist/connector/sessionConfig.d.ts +3 -0
- package/dist/connector/sessionConfig.js +30 -0
- package/dist/connector/sessionConnector.d.ts +45 -0
- package/dist/connector/sessionConnector.js +603 -0
- package/dist/defaults.d.ts +7 -7
- package/dist/defaults.js +7 -7
- package/dist/facade.d.ts +12 -27
- package/dist/facade.js +6 -24
- package/dist/node/connectSession.d.ts +21 -0
- package/dist/node/connectSession.js +62 -0
- package/dist/node/index.d.ts +3 -11
- package/dist/node/index.js +2 -8
- package/dist/node/runtimeCapability.d.ts +7 -0
- package/dist/node/runtimeCapability.js +10 -0
- package/dist/node/sessionRuntime.d.ts +2 -0
- package/dist/node/sessionRuntime.js +13 -0
- package/dist/node/webTransportClient.d.ts +9 -0
- package/dist/node/webTransportClient.js +56 -0
- package/dist/node/webTransportServer.d.ts +22 -0
- package/dist/node/webTransportServer.js +100 -0
- package/dist/node/wsFactory.d.ts +3 -1
- package/dist/node/wsFactory.js +4 -3
- package/dist/proxy/controllerGuard.d.ts +4 -4
- package/dist/proxy/controllerGuard.js +83 -177
- package/dist/proxy/disableUpstreamServiceWorkerRegister.d.ts +3 -1
- package/dist/proxy/disableUpstreamServiceWorkerRegister.js +19 -17
- package/dist/proxy/index.d.ts +16 -15
- package/dist/proxy/index.js +8 -14
- package/dist/proxy/integration.d.ts +23 -71
- package/dist/proxy/integration.js +37 -340
- package/dist/proxy/registerServiceWorker.d.ts +1 -10
- package/dist/proxy/registerServiceWorker.js +43 -105
- package/dist/proxy/runtime.d.ts +3 -63
- package/dist/proxy/runtime.js +349 -462
- package/dist/proxy/scope.d.ts +6 -0
- package/dist/proxy/scope.js +131 -0
- package/dist/proxy/serviceWorker.d.ts +5 -16
- package/dist/proxy/serviceWorker.js +230 -617
- package/dist/proxy/stream.d.ts +9 -0
- package/dist/proxy/stream.js +40 -0
- package/dist/proxy/types.d.ts +78 -30
- package/dist/proxy/windowBridge.d.ts +61 -0
- package/dist/proxy/windowBridge.js +344 -0
- package/dist/proxy/wsPatch.d.ts +4 -14
- package/dist/proxy/wsPatch.js +173 -268
- package/dist/public/artifact.d.ts +11 -0
- package/dist/public/artifact.js +44 -0
- package/dist/public/artifactLease.d.ts +10 -0
- package/dist/public/artifactLease.js +48 -0
- package/dist/public/connectError.d.ts +6 -0
- package/dist/public/connectError.js +9 -0
- package/dist/public/contract.d.ts +83 -0
- package/dist/public/contract.js +17 -0
- package/dist/public/streamMetadata.d.ts +10 -0
- package/dist/public/streamMetadata.js +40 -0
- package/dist/rpc/caller.d.ts +1 -1
- package/dist/rpc/client.d.ts +1 -4
- package/dist/rpc/client.js +0 -21
- package/dist/rpc/rpcProxy.d.ts +1 -1
- package/dist/rpc/server.d.ts +1 -1
- package/dist/rpc/validate.d.ts +1 -1
- package/dist/rpc/wire.d.ts +15 -0
- package/dist/runtime/errors.d.ts +6 -0
- package/dist/runtime/errors.js +10 -0
- package/dist/transport/webSocketAdapter.d.ts +24 -0
- package/dist/transport/webSocketAdapter.js +234 -0
- package/dist/transport/webTransportAdapter.d.ts +27 -0
- package/dist/transport/webTransportAdapter.js +519 -0
- package/dist/utils/errors.d.ts +2 -20
- package/dist/utils/errors.js +73 -20
- package/dist/v2/admissionError.d.ts +4 -0
- package/dist/v2/admissionError.js +8 -0
- package/dist/v2/artifact.d.ts +128 -0
- package/dist/v2/artifact.js +1126 -0
- package/dist/v2/artifactLease.d.ts +1 -0
- package/dist/v2/artifactLease.js +7 -0
- package/dist/v2/capability.d.ts +29 -0
- package/dist/v2/capability.js +190 -0
- package/dist/v2/carrier.d.ts +79 -0
- package/dist/v2/carrier.js +405 -0
- package/dist/v2/contract.d.ts +37 -0
- package/dist/v2/contract.js +1 -0
- package/dist/v2/handshake.d.ts +80 -0
- package/dist/v2/handshake.js +470 -0
- package/dist/v2/opaqueArtifact.d.ts +1 -0
- package/dist/v2/opaqueArtifact.js +2 -0
- package/dist/v2/protocol.d.ts +99 -0
- package/dist/v2/protocol.js +739 -0
- package/dist/v2/publicSession.d.ts +1 -0
- package/dist/v2/publicSession.js +179 -0
- package/dist/v2/retryDisposition.d.ts +16 -0
- package/dist/v2/retryDisposition.js +30 -0
- package/dist/v2/session.d.ts +277 -0
- package/dist/v2/session.js +1879 -0
- package/dist/v2/streamLifetimeLedger.d.ts +31 -0
- package/dist/v2/streamLifetimeLedger.js +131 -0
- package/dist/v2/streamMetadata.d.ts +1 -0
- package/dist/v2/streamMetadata.js +7 -0
- package/dist/v2/unreliableMessage.d.ts +43 -0
- package/dist/v2/unreliableMessage.js +252 -0
- package/dist/vendor/tr46.d.ts +10 -0
- package/dist/vendor/tr46.js +501 -0
- package/dist/ws-client/binaryTransport.d.ts +0 -3
- package/dist/ws-client/binaryTransport.js +15 -39
- package/dist/yamux/errors.d.ts +0 -4
- package/dist/yamux/errors.js +0 -9
- package/dist/yamux/session.d.ts +0 -5
- package/dist/yamux/session.js +4 -70
- package/dist/yamux/stream.d.ts +1 -0
- package/dist/yamux/stream.js +4 -0
- package/package.json +29 -81
- package/sbom/cyclonedx.json +2764 -0
- package/sbom/spdx.json +1947 -0
- package/YAMUX_ALIGNMENT.md +0 -127
- package/dist/_examples/flowersec/demo/v1.facade.gen.d.ts +0 -12
- package/dist/_examples/flowersec/demo/v1.facade.gen.js +0 -15
- package/dist/_examples/flowersec/demo/v1.gen.d.ts +0 -16
- package/dist/_examples/flowersec/demo/v1.gen.js +0 -86
- package/dist/_examples/flowersec/demo/v1.rpc.gen.d.ts +0 -11
- package/dist/_examples/flowersec/demo/v1.rpc.gen.js +0 -22
- package/dist/browser/connect.d.ts +0 -13
- package/dist/browser/connect.js +0 -31
- package/dist/browser/reconnectConfig.d.ts +0 -15
- package/dist/browser/reconnectConfig.js +0 -20
- package/dist/client-connect/common.d.ts +0 -26
- package/dist/client-connect/common.js +0 -167
- package/dist/client-connect/connectCore.d.ts +0 -61
- package/dist/client-connect/connectCore.js +0 -572
- package/dist/client-connect/contract.d.ts +0 -8
- package/dist/client-connect/contract.js +0 -51
- package/dist/client-connect/termination.d.ts +0 -6
- package/dist/client-connect/termination.js +0 -7
- package/dist/client-connect/transportSecurity.d.ts +0 -27
- package/dist/client-connect/transportSecurity.js +0 -199
- package/dist/client-connect/tunnelAttachCloseReason.d.ts +0 -3
- package/dist/client-connect/tunnelAttachCloseReason.js +0 -23
- package/dist/client.d.ts +0 -23
- package/dist/connect/artifact.d.ts +0 -36
- package/dist/connect/artifact.js +0 -213
- package/dist/connect/resolveArtifact.d.ts +0 -22
- package/dist/connect/resolveArtifact.js +0 -87
- package/dist/controlplane/channelInit.d.ts +0 -27
- package/dist/controlplane/channelInit.js +0 -116
- package/dist/controlplane/http.d.ts +0 -16
- package/dist/controlplane/http.js +0 -59
- package/dist/controlplane/index.d.ts +0 -6
- package/dist/controlplane/index.js +0 -5
- package/dist/controlplane/issuer.d.ts +0 -18
- package/dist/controlplane/issuer.js +0 -113
- package/dist/controlplane/request.d.ts +0 -51
- package/dist/controlplane/request.js +0 -273
- package/dist/controlplane/token.d.ts +0 -33
- package/dist/controlplane/token.js +0 -119
- package/dist/direct-client/connect.d.ts +0 -9
- package/dist/direct-client/connect.js +0 -90
- package/dist/direct-client/index.d.ts +0 -1
- package/dist/direct-client/index.js +0 -1
- package/dist/e2ee/constants.d.ts +0 -9
- package/dist/e2ee/constants.js +0 -18
- package/dist/e2ee/errors.d.ts +0 -5
- package/dist/e2ee/errors.js +0 -8
- package/dist/e2ee/framing.d.ts +0 -12
- package/dist/e2ee/framing.js +0 -57
- package/dist/e2ee/handshake.d.ts +0 -88
- package/dist/e2ee/handshake.js +0 -353
- package/dist/e2ee/index.d.ts +0 -7
- package/dist/e2ee/index.js +0 -7
- package/dist/e2ee/kdf.d.ts +0 -15
- package/dist/e2ee/kdf.js +0 -39
- package/dist/e2ee/record.d.ts +0 -11
- package/dist/e2ee/record.js +0 -80
- package/dist/e2ee/secureChannel.d.ts +0 -93
- package/dist/e2ee/secureChannel.js +0 -342
- package/dist/e2ee/transcript.d.ts +0 -23
- package/dist/e2ee/transcript.js +0 -31
- package/dist/endpoint/index.d.ts +0 -84
- package/dist/endpoint/index.js +0 -557
- package/dist/endpoint/node.d.ts +0 -9
- package/dist/endpoint/node.js +0 -51
- package/dist/gen/flowersec/controlplane/v1.gen.d.ts +0 -36
- package/dist/gen/flowersec/controlplane/v1.gen.js +0 -135
- package/dist/gen/flowersec/direct/v1.gen.d.ts +0 -21
- package/dist/gen/flowersec/direct/v1.gen.js +0 -101
- package/dist/gen/flowersec/e2ee/v1.gen.d.ts +0 -68
- package/dist/gen/flowersec/e2ee/v1.gen.js +0 -194
- package/dist/gen/flowersec/rpc/v1.gen.d.ts +0 -30
- package/dist/gen/flowersec/rpc/v1.gen.js +0 -107
- package/dist/gen/flowersec/tunnel/v1.gen.d.ts +0 -23
- package/dist/gen/flowersec/tunnel/v1.gen.js +0 -104
- package/dist/index.d.ts +0 -22
- package/dist/index.js +0 -22
- package/dist/node/connect.d.ts +0 -10
- package/dist/node/connect.js +0 -29
- package/dist/node/reconnectConfig.d.ts +0 -16
- package/dist/node/reconnectConfig.js +0 -20
- package/dist/observability/index.d.ts +0 -1
- package/dist/observability/index.js +0 -1
- package/dist/observability/observer.d.ts +0 -52
- package/dist/observability/observer.js +0 -314
- package/dist/proxy/appWindow.d.ts +0 -37
- package/dist/proxy/appWindow.js +0 -273
- package/dist/proxy/bootstrap.d.ts +0 -35
- package/dist/proxy/bootstrap.js +0 -119
- package/dist/proxy/constants.d.ts +0 -7
- package/dist/proxy/constants.js +0 -8
- package/dist/proxy/controllerWindow.d.ts +0 -12
- package/dist/proxy/controllerWindow.js +0 -384
- package/dist/proxy/cookieJar.d.ts +0 -7
- package/dist/proxy/cookieJar.js +0 -136
- package/dist/proxy/headerPolicy.d.ts +0 -14
- package/dist/proxy/headerPolicy.js +0 -134
- package/dist/proxy/portStream.d.ts +0 -5
- package/dist/proxy/portStream.js +0 -256
- package/dist/proxy/preset.d.ts +0 -29
- package/dist/proxy/preset.js +0 -111
- package/dist/proxy/runtimeScope.d.ts +0 -57
- package/dist/proxy/runtimeScope.js +0 -218
- package/dist/proxy/server.d.ts +0 -28
- package/dist/proxy/server.js +0 -833
- package/dist/proxy/windowBridgeProtocol.d.ts +0 -65
- package/dist/proxy/windowBridgeProtocol.js +0 -11
- package/dist/reconnect/artifactControlplane.d.ts +0 -16
- package/dist/reconnect/artifactControlplane.js +0 -35
- package/dist/reconnect/index.d.ts +0 -36
- package/dist/reconnect/index.js +0 -388
- package/dist/rpc-proxy/index.d.ts +0 -1
- package/dist/rpc-proxy/index.js +0 -1
- package/dist/rpc-proxy/rpcProxy.d.ts +0 -1
- package/dist/rpc-proxy/rpcProxy.js +0 -1
- package/dist/streamhello/index.d.ts +0 -1
- package/dist/streamhello/index.js +0 -1
- package/dist/streamhello/streamHello.d.ts +0 -3
- package/dist/streamhello/streamHello.js +0 -13
- package/dist/streamio/index.d.ts +0 -32
- package/dist/streamio/index.js +0 -87
- package/dist/tunnel-client/connect.d.ts +0 -9
- package/dist/tunnel-client/connect.js +0 -147
- package/dist/tunnel-client/index.d.ts +0 -1
- package/dist/tunnel-client/index.js +0 -1
- package/dist/ws/index.d.ts +0 -1
- package/dist/ws/index.js +0 -1
- /package/dist/{client.js → rpc/wire.js} +0 -0
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { Role } from "../gen/flowersec/controlplane/v1.gen.js";
|
|
2
|
-
import { base64urlEncode } from "../utils/base64url.js";
|
|
3
|
-
export const CHANNEL_INIT_WINDOW_SECONDS = 120;
|
|
4
|
-
export const DEFAULT_IDLE_TIMEOUT_SECONDS = 60;
|
|
5
|
-
export const DEFAULT_TOKEN_EXP_SECONDS = 60;
|
|
6
|
-
export class ChannelInitService {
|
|
7
|
-
issuer;
|
|
8
|
-
params;
|
|
9
|
-
nowUnixS;
|
|
10
|
-
constructor(issuer, params, nowUnixS = () => Math.floor(Date.now() / 1000)) {
|
|
11
|
-
this.issuer = issuer;
|
|
12
|
-
this.params = params;
|
|
13
|
-
this.nowUnixS = nowUnixS;
|
|
14
|
-
}
|
|
15
|
-
issue(channelIdInput) {
|
|
16
|
-
const params = normalizeParams(this.params);
|
|
17
|
-
const channelId = normalizeChannelId(channelIdInput);
|
|
18
|
-
const psk = crypto.getRandomValues(new Uint8Array(32));
|
|
19
|
-
let pskB64u;
|
|
20
|
-
try {
|
|
21
|
-
pskB64u = base64urlEncode(psk);
|
|
22
|
-
}
|
|
23
|
-
finally {
|
|
24
|
-
psk.fill(0);
|
|
25
|
-
}
|
|
26
|
-
const now = normalizeNow(this.nowUnixS());
|
|
27
|
-
const initExp = now + CHANNEL_INIT_WINDOW_SECONDS;
|
|
28
|
-
const grant = (role, token) => ({
|
|
29
|
-
tunnel_url: params.tunnelUrl,
|
|
30
|
-
channel_id: channelId,
|
|
31
|
-
channel_init_expire_at_unix_s: initExp,
|
|
32
|
-
idle_timeout_seconds: params.idleTimeoutSeconds,
|
|
33
|
-
role,
|
|
34
|
-
token,
|
|
35
|
-
e2ee_psk_b64u: pskB64u,
|
|
36
|
-
allowed_suites: [...params.allowedSuites],
|
|
37
|
-
default_suite: params.defaultSuite,
|
|
38
|
-
});
|
|
39
|
-
return {
|
|
40
|
-
client: grant(Role.Role_client, this.signRoleToken(channelId, Role.Role_client, initExp, params.idleTimeoutSeconds, params.tokenExpSeconds, now, params)),
|
|
41
|
-
server: grant(Role.Role_server, this.signRoleToken(channelId, Role.Role_server, initExp, params.idleTimeoutSeconds, params.tokenExpSeconds, now, params)),
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
reissue(grant) {
|
|
45
|
-
const params = normalizeParams(this.params);
|
|
46
|
-
if (grant.idle_timeout_seconds <= 0 || (grant.role !== Role.Role_client && grant.role !== Role.Role_server))
|
|
47
|
-
throw new Error("invalid grant");
|
|
48
|
-
const now = normalizeNow(this.nowUnixS());
|
|
49
|
-
const skewSeconds = Math.ceil(params.clockSkewMs / 1000);
|
|
50
|
-
if (now > grant.channel_init_expire_at_unix_s + skewSeconds)
|
|
51
|
-
throw new Error("channel init expired");
|
|
52
|
-
return {
|
|
53
|
-
...grant,
|
|
54
|
-
token: this.signRoleToken(normalizeChannelId(grant.channel_id), grant.role, grant.channel_init_expire_at_unix_s, grant.idle_timeout_seconds, params.tokenExpSeconds, now, params),
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
signRoleToken(channelId, role, initExp, idleTimeoutSeconds, tokenExpSeconds, now, params) {
|
|
58
|
-
const iat = Math.min(now, initExp);
|
|
59
|
-
const exp = Math.min(initExp, iat + tokenExpSeconds);
|
|
60
|
-
const tokenId = crypto.getRandomValues(new Uint8Array(24));
|
|
61
|
-
try {
|
|
62
|
-
return this.issuer.sign({
|
|
63
|
-
aud: params.tunnelAudience,
|
|
64
|
-
iss: params.issuerId,
|
|
65
|
-
channel_id: channelId,
|
|
66
|
-
role: role,
|
|
67
|
-
token_id: base64urlEncode(tokenId),
|
|
68
|
-
init_exp: initExp,
|
|
69
|
-
idle_timeout_seconds: idleTimeoutSeconds,
|
|
70
|
-
iat,
|
|
71
|
-
exp,
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
finally {
|
|
75
|
-
tokenId.fill(0);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
function normalizeParams(params) {
|
|
80
|
-
const tunnelUrl = params.tunnelUrl.trim();
|
|
81
|
-
const tunnelAudience = params.tunnelAudience.trim();
|
|
82
|
-
const issuerId = params.issuerId.trim();
|
|
83
|
-
if (tunnelUrl === "")
|
|
84
|
-
throw new Error("missing tunnel URL");
|
|
85
|
-
if (tunnelAudience === "")
|
|
86
|
-
throw new Error("missing tunnel audience");
|
|
87
|
-
if (issuerId === "")
|
|
88
|
-
throw new Error("missing issuer ID");
|
|
89
|
-
const tokenExpSeconds = normalizeNonNegative("tokenExpSeconds", params.tokenExpSeconds ?? 0) || DEFAULT_TOKEN_EXP_SECONDS;
|
|
90
|
-
const idleTimeoutSeconds = normalizeNonNegative("idleTimeoutSeconds", params.idleTimeoutSeconds ?? 0) || DEFAULT_IDLE_TIMEOUT_SECONDS;
|
|
91
|
-
const clockSkewMs = normalizeNonNegative("clockSkewMs", params.clockSkewMs ?? 0);
|
|
92
|
-
const allowedSuites = [...new Set(params.allowedSuites?.length ? params.allowedSuites : [1])];
|
|
93
|
-
for (const suite of allowedSuites)
|
|
94
|
-
if (suite !== 1 && suite !== 2)
|
|
95
|
-
throw new Error("unsupported suite");
|
|
96
|
-
const defaultSuite = params.defaultSuite ?? allowedSuites[0];
|
|
97
|
-
if (!allowedSuites.includes(defaultSuite))
|
|
98
|
-
throw new Error("default suite not allowed");
|
|
99
|
-
return { tunnelUrl, tunnelAudience, issuerId, tokenExpSeconds, idleTimeoutSeconds, clockSkewMs, allowedSuites, defaultSuite };
|
|
100
|
-
}
|
|
101
|
-
function normalizeChannelId(input) {
|
|
102
|
-
const value = input.trim();
|
|
103
|
-
if (value === "" || new TextEncoder().encode(value).length > 256)
|
|
104
|
-
throw new Error("invalid channel ID");
|
|
105
|
-
return value;
|
|
106
|
-
}
|
|
107
|
-
function normalizeNonNegative(name, value) {
|
|
108
|
-
if (!Number.isSafeInteger(value) || value < 0)
|
|
109
|
-
throw new Error(`${name} must be a non-negative integer`);
|
|
110
|
-
return value;
|
|
111
|
-
}
|
|
112
|
-
function normalizeNow(value) {
|
|
113
|
-
if (!Number.isSafeInteger(value) || value <= 0)
|
|
114
|
-
throw new Error("invalid clock");
|
|
115
|
-
return value;
|
|
116
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export type ArtifactRequest = Readonly<{
|
|
2
|
-
endpoint_id: string;
|
|
3
|
-
payload?: Record<string, unknown>;
|
|
4
|
-
correlation?: Readonly<{
|
|
5
|
-
trace_id?: string;
|
|
6
|
-
}>;
|
|
7
|
-
}>;
|
|
8
|
-
export declare class ControlplaneCodecError extends Error {
|
|
9
|
-
readonly status: number;
|
|
10
|
-
readonly code: string;
|
|
11
|
-
constructor(status: number, code: string, message: string);
|
|
12
|
-
}
|
|
13
|
-
export declare function decodeArtifactRequest(contentType: string, body: Uint8Array, maxBodyBytes?: number): ArtifactRequest;
|
|
14
|
-
export declare function encodeArtifactEnvelope(artifact: unknown): Uint8Array;
|
|
15
|
-
export declare function encodeControlplaneError(code: string, message: string): Uint8Array;
|
|
16
|
-
export declare function bearerToken(authorization: string): string | undefined;
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { assertConnectArtifact } from "../connect/artifact.js";
|
|
2
|
-
import { SDK_DEFAULTS } from "../defaults.js";
|
|
3
|
-
export class ControlplaneCodecError extends Error {
|
|
4
|
-
status;
|
|
5
|
-
code;
|
|
6
|
-
constructor(status, code, message) {
|
|
7
|
-
super(message);
|
|
8
|
-
this.status = status;
|
|
9
|
-
this.code = code;
|
|
10
|
-
this.name = "ControlplaneCodecError";
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
export function decodeArtifactRequest(contentType, body, maxBodyBytes = SDK_DEFAULTS.controlplane.maxRequestBodyBytes) {
|
|
14
|
-
if (contentType.split(";", 1)[0]?.trim().toLowerCase() !== "application/json") {
|
|
15
|
-
throw new ControlplaneCodecError(415, "unsupported_media_type", "content type must be application/json");
|
|
16
|
-
}
|
|
17
|
-
if (body.length > maxBodyBytes)
|
|
18
|
-
throw new ControlplaneCodecError(413, "body_too_large", `request body exceeds ${maxBodyBytes} bytes`);
|
|
19
|
-
let value;
|
|
20
|
-
try {
|
|
21
|
-
value = JSON.parse(new TextDecoder().decode(body));
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
throw new ControlplaneCodecError(400, "invalid_json", "malformed JSON request body");
|
|
25
|
-
}
|
|
26
|
-
if (typeof value !== "object" || value == null || Array.isArray(value))
|
|
27
|
-
throw new ControlplaneCodecError(400, "invalid_json", "request body must be a JSON object");
|
|
28
|
-
const input = value;
|
|
29
|
-
const endpointId = typeof input["endpoint_id"] === "string" ? input["endpoint_id"].trim() : "";
|
|
30
|
-
if (endpointId === "")
|
|
31
|
-
throw new ControlplaneCodecError(400, "invalid_request", "bad endpoint_id");
|
|
32
|
-
const payload = input["payload"];
|
|
33
|
-
if (payload !== undefined && (typeof payload !== "object" || payload == null || Array.isArray(payload)))
|
|
34
|
-
throw new ControlplaneCodecError(400, "invalid_request", "bad payload");
|
|
35
|
-
const correlation = input["correlation"];
|
|
36
|
-
if (correlation !== undefined && (typeof correlation !== "object" || correlation == null || Array.isArray(correlation)))
|
|
37
|
-
throw new ControlplaneCodecError(400, "invalid_request", "bad correlation");
|
|
38
|
-
const traceId = typeof correlation?.["trace_id"] === "string"
|
|
39
|
-
? String(correlation["trace_id"]).trim()
|
|
40
|
-
: "";
|
|
41
|
-
return {
|
|
42
|
-
endpoint_id: endpointId,
|
|
43
|
-
...(payload === undefined ? {} : { payload: payload }),
|
|
44
|
-
...(traceId === "" ? {} : { correlation: { trace_id: traceId } }),
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
export function encodeArtifactEnvelope(artifact) {
|
|
48
|
-
return new TextEncoder().encode(JSON.stringify({ connect_artifact: assertConnectArtifact(artifact) }));
|
|
49
|
-
}
|
|
50
|
-
export function encodeControlplaneError(code, message) {
|
|
51
|
-
return new TextEncoder().encode(JSON.stringify({ error: { code, message } }));
|
|
52
|
-
}
|
|
53
|
-
export function bearerToken(authorization) {
|
|
54
|
-
const value = authorization.trim();
|
|
55
|
-
if (!value.startsWith("Bearer "))
|
|
56
|
-
return undefined;
|
|
57
|
-
const token = value.slice("Bearer ".length).trim();
|
|
58
|
-
return token === "" ? undefined : token;
|
|
59
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export type { ArtifactRequestCorrelation, ConnectArtifactEnvelope, ControlplaneBaseConfig, ControlplaneErrorEnvelope, RequestConnectArtifactInput, RequestEntryConnectArtifactInput, } from "./request.js";
|
|
2
|
-
export { ControlplaneRequestError, DEFAULT_CONNECT_ARTIFACT_PATH, DEFAULT_ENTRY_CONNECT_ARTIFACT_PATH, requestConnectArtifact, requestEntryConnectArtifact, } from "./request.js";
|
|
3
|
-
export * from "./token.js";
|
|
4
|
-
export * from "./issuer.js";
|
|
5
|
-
export * from "./channelInit.js";
|
|
6
|
-
export * from "./http.js";
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
export { ControlplaneRequestError, DEFAULT_CONNECT_ARTIFACT_PATH, DEFAULT_ENTRY_CONNECT_ARTIFACT_PATH, requestConnectArtifact, requestEntryConnectArtifact, } from "./request.js";
|
|
2
|
-
export * from "./token.js";
|
|
3
|
-
export * from "./issuer.js";
|
|
4
|
-
export * from "./channelInit.js";
|
|
5
|
-
export * from "./http.js";
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { type TokenPayload } from "./token.js";
|
|
2
|
-
export declare class IssuerKeyset {
|
|
3
|
-
private active;
|
|
4
|
-
private readonly verificationKeys;
|
|
5
|
-
constructor(kid: string, signingSeed: Uint8Array);
|
|
6
|
-
static random(kid: string): IssuerKeyset;
|
|
7
|
-
currentKID(): string;
|
|
8
|
-
publicKeys(): ReadonlyMap<string, Uint8Array>;
|
|
9
|
-
sign(payload: Omit<TokenPayload, "kid"> & Readonly<{
|
|
10
|
-
kid?: string;
|
|
11
|
-
}>): string;
|
|
12
|
-
addVerificationKey(kid: string, publicKey: Uint8Array): void;
|
|
13
|
-
rotate(kid: string, signingSeed: Uint8Array): void;
|
|
14
|
-
retireVerificationKey(kid: string): void;
|
|
15
|
-
exportTunnelKeyset(): Uint8Array;
|
|
16
|
-
dispose(): void;
|
|
17
|
-
private requireActive;
|
|
18
|
-
}
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { ed25519 } from "@noble/curves/ed25519";
|
|
2
|
-
import { base64urlEncode } from "../utils/base64url.js";
|
|
3
|
-
import { signToken } from "./token.js";
|
|
4
|
-
export class IssuerKeyset {
|
|
5
|
-
active;
|
|
6
|
-
verificationKeys = new Map();
|
|
7
|
-
constructor(kid, signingSeed) {
|
|
8
|
-
const normalizedKID = normalizeKid(kid);
|
|
9
|
-
const retainedSeed = copySigningSeed(signingSeed);
|
|
10
|
-
this.active = { kid: normalizedKID, signingSeed: retainedSeed };
|
|
11
|
-
this.verificationKeys.set(normalizedKID, ed25519.getPublicKey(retainedSeed));
|
|
12
|
-
}
|
|
13
|
-
static random(kid) {
|
|
14
|
-
const seed = crypto.getRandomValues(new Uint8Array(32));
|
|
15
|
-
try {
|
|
16
|
-
return new IssuerKeyset(kid, seed);
|
|
17
|
-
}
|
|
18
|
-
finally {
|
|
19
|
-
seed.fill(0);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
currentKID() {
|
|
23
|
-
return this.requireActive().kid;
|
|
24
|
-
}
|
|
25
|
-
publicKeys() {
|
|
26
|
-
this.requireActive();
|
|
27
|
-
return new Map([...this.verificationKeys.entries()]
|
|
28
|
-
.sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)
|
|
29
|
-
.map(([kid, publicKey]) => [kid, publicKey.slice()]));
|
|
30
|
-
}
|
|
31
|
-
sign(payload) {
|
|
32
|
-
const active = this.requireActive();
|
|
33
|
-
return signToken(active.signingSeed, { ...payload, kid: active.kid });
|
|
34
|
-
}
|
|
35
|
-
addVerificationKey(kid, publicKey) {
|
|
36
|
-
this.requireActive();
|
|
37
|
-
const normalizedKID = normalizeKid(kid);
|
|
38
|
-
const retainedKey = copyPublicKey(publicKey);
|
|
39
|
-
const existing = this.verificationKeys.get(normalizedKID);
|
|
40
|
-
if (existing != null) {
|
|
41
|
-
if (!equalBytes(existing, retainedKey))
|
|
42
|
-
throw new Error(`key ID ${normalizedKID} is already bound to a different public key`);
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
this.verificationKeys.set(normalizedKID, retainedKey);
|
|
46
|
-
}
|
|
47
|
-
rotate(kid, signingSeed) {
|
|
48
|
-
const active = this.requireActive();
|
|
49
|
-
const nextKID = normalizeKid(kid);
|
|
50
|
-
const nextSeed = copySigningSeed(signingSeed);
|
|
51
|
-
const nextPublicKey = ed25519.getPublicKey(nextSeed);
|
|
52
|
-
const prepublishedKey = this.verificationKeys.get(nextKID);
|
|
53
|
-
if (prepublishedKey == null) {
|
|
54
|
-
nextSeed.fill(0);
|
|
55
|
-
throw new Error(`key ID ${nextKID} must be prepublished before rotation`);
|
|
56
|
-
}
|
|
57
|
-
if (!equalBytes(prepublishedKey, nextPublicKey)) {
|
|
58
|
-
nextSeed.fill(0);
|
|
59
|
-
throw new Error(`key ID ${nextKID} is bound to a different public key`);
|
|
60
|
-
}
|
|
61
|
-
this.active = { kid: nextKID, signingSeed: nextSeed };
|
|
62
|
-
active.signingSeed.fill(0);
|
|
63
|
-
}
|
|
64
|
-
retireVerificationKey(kid) {
|
|
65
|
-
const active = this.requireActive();
|
|
66
|
-
const normalizedKID = normalizeKid(kid);
|
|
67
|
-
if (normalizedKID === active.kid)
|
|
68
|
-
throw new Error("the active signing key cannot be retired");
|
|
69
|
-
if (!this.verificationKeys.delete(normalizedKID))
|
|
70
|
-
throw new Error(`key ID ${normalizedKID} is not published`);
|
|
71
|
-
}
|
|
72
|
-
exportTunnelKeyset() {
|
|
73
|
-
const keys = [...this.publicKeys()].map(([kid, publicKey]) => ({ kid, pubkey_b64u: base64urlEncode(publicKey) }));
|
|
74
|
-
return new TextEncoder().encode(JSON.stringify({ keys }, null, 2));
|
|
75
|
-
}
|
|
76
|
-
dispose() {
|
|
77
|
-
const active = this.active;
|
|
78
|
-
if (active == null)
|
|
79
|
-
return;
|
|
80
|
-
active.signingSeed.fill(0);
|
|
81
|
-
this.active = undefined;
|
|
82
|
-
this.verificationKeys.clear();
|
|
83
|
-
}
|
|
84
|
-
requireActive() {
|
|
85
|
-
if (this.active == null)
|
|
86
|
-
throw new Error("issuer keyset is disposed");
|
|
87
|
-
return this.active;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
function copySigningSeed(signingSeed) {
|
|
91
|
-
if (signingSeed.length !== 32)
|
|
92
|
-
throw new TypeError("Ed25519 signing seed must be 32 bytes");
|
|
93
|
-
return signingSeed.slice();
|
|
94
|
-
}
|
|
95
|
-
function copyPublicKey(publicKey) {
|
|
96
|
-
if (publicKey.length !== 32)
|
|
97
|
-
throw new TypeError("Ed25519 public key must be 32 bytes");
|
|
98
|
-
return publicKey.slice();
|
|
99
|
-
}
|
|
100
|
-
function equalBytes(left, right) {
|
|
101
|
-
if (left.length !== right.length)
|
|
102
|
-
return false;
|
|
103
|
-
let diff = 0;
|
|
104
|
-
for (let index = 0; index < left.length; index++)
|
|
105
|
-
diff |= left[index] ^ right[index];
|
|
106
|
-
return diff === 0;
|
|
107
|
-
}
|
|
108
|
-
function normalizeKid(kid) {
|
|
109
|
-
const value = kid.trim();
|
|
110
|
-
if (value === "")
|
|
111
|
-
throw new TypeError("key ID is required");
|
|
112
|
-
return value;
|
|
113
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { type ConnectArtifact } from "../connect/artifact.js";
|
|
2
|
-
type FetchLike = typeof fetch;
|
|
3
|
-
export type ControlplaneBaseConfig = Readonly<{
|
|
4
|
-
baseUrl?: string;
|
|
5
|
-
path?: string;
|
|
6
|
-
headers?: HeadersInit;
|
|
7
|
-
credentials?: RequestCredentials;
|
|
8
|
-
fetch?: FetchLike;
|
|
9
|
-
signal?: AbortSignal;
|
|
10
|
-
allowLoopbackHTTP?: boolean;
|
|
11
|
-
}>;
|
|
12
|
-
export type ArtifactRequestCorrelation = Readonly<{
|
|
13
|
-
traceId?: string;
|
|
14
|
-
}>;
|
|
15
|
-
export type RequestConnectArtifactInput = ControlplaneBaseConfig & Readonly<{
|
|
16
|
-
endpointId: string;
|
|
17
|
-
payload?: Record<string, unknown>;
|
|
18
|
-
correlation?: ArtifactRequestCorrelation;
|
|
19
|
-
}>;
|
|
20
|
-
export type RequestEntryConnectArtifactInput = RequestConnectArtifactInput & Readonly<{
|
|
21
|
-
entryTicket: string;
|
|
22
|
-
}>;
|
|
23
|
-
export type ConnectArtifactEnvelope = Readonly<{
|
|
24
|
-
connect_artifact: ConnectArtifact;
|
|
25
|
-
}>;
|
|
26
|
-
export type ControlplaneErrorEnvelope = Readonly<{
|
|
27
|
-
error: Readonly<{
|
|
28
|
-
code: string;
|
|
29
|
-
message: string;
|
|
30
|
-
}>;
|
|
31
|
-
}>;
|
|
32
|
-
export declare class ControlplaneRequestError extends Error {
|
|
33
|
-
readonly status: number;
|
|
34
|
-
readonly code: string;
|
|
35
|
-
readonly responseBody: unknown;
|
|
36
|
-
constructor(args: Readonly<{
|
|
37
|
-
status: number;
|
|
38
|
-
message: string;
|
|
39
|
-
code?: string;
|
|
40
|
-
responseBody?: unknown;
|
|
41
|
-
}>);
|
|
42
|
-
}
|
|
43
|
-
export declare const DEFAULT_CONNECT_ARTIFACT_PATH = "/v1/connect/artifact";
|
|
44
|
-
export declare const DEFAULT_ENTRY_CONNECT_ARTIFACT_PATH = "/v1/connect/artifact/entry";
|
|
45
|
-
export declare function buildControlplaneURL(baseUrl: string | undefined, path: string): string;
|
|
46
|
-
export declare function requestControlplaneJSON(url: string, init: RequestInit & Readonly<{
|
|
47
|
-
fetch?: FetchLike;
|
|
48
|
-
}>): Promise<unknown>;
|
|
49
|
-
export declare function requestConnectArtifact(config: RequestConnectArtifactInput): Promise<ConnectArtifact>;
|
|
50
|
-
export declare function requestEntryConnectArtifact(config: RequestEntryConnectArtifactInput): Promise<ConnectArtifact>;
|
|
51
|
-
export {};
|
|
@@ -1,273 +0,0 @@
|
|
|
1
|
-
import { assertConnectArtifact } from "../connect/artifact.js";
|
|
2
|
-
import { SDK_DEFAULTS } from "../defaults.js";
|
|
3
|
-
export class ControlplaneRequestError extends Error {
|
|
4
|
-
status;
|
|
5
|
-
code;
|
|
6
|
-
responseBody;
|
|
7
|
-
constructor(args) {
|
|
8
|
-
super(args.message);
|
|
9
|
-
this.name = "ControlplaneRequestError";
|
|
10
|
-
this.status = args.status;
|
|
11
|
-
this.code = String(args.code ?? "").trim();
|
|
12
|
-
this.responseBody = args.responseBody;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
export const DEFAULT_CONNECT_ARTIFACT_PATH = "/v1/connect/artifact";
|
|
16
|
-
export const DEFAULT_ENTRY_CONNECT_ARTIFACT_PATH = "/v1/connect/artifact/entry";
|
|
17
|
-
const DEFAULT_MAX_CONTROLPLANE_RESPONSE_BYTES = SDK_DEFAULTS.controlplane.maxResponseBodyBytes;
|
|
18
|
-
class ControlplaneResponseTooLargeError extends Error {
|
|
19
|
-
constructor(maxBytes) {
|
|
20
|
-
super(`controlplane response exceeded ${maxBytes} bytes`);
|
|
21
|
-
this.name = "ControlplaneResponseTooLargeError";
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
function resolveFetch(fetchImpl) {
|
|
25
|
-
if (fetchImpl)
|
|
26
|
-
return fetchImpl;
|
|
27
|
-
if (typeof globalThis.fetch === "function")
|
|
28
|
-
return globalThis.fetch.bind(globalThis);
|
|
29
|
-
throw new Error("global fetch is not available");
|
|
30
|
-
}
|
|
31
|
-
export function buildControlplaneURL(baseUrl, path) {
|
|
32
|
-
const base = String(baseUrl ?? "").trim();
|
|
33
|
-
if (base === "")
|
|
34
|
-
return path;
|
|
35
|
-
return `${base.replace(/\/+$/, "")}${path}`;
|
|
36
|
-
}
|
|
37
|
-
function transportPolicyDenied() {
|
|
38
|
-
return new ControlplaneRequestError({
|
|
39
|
-
status: 0,
|
|
40
|
-
code: "transport_policy_denied",
|
|
41
|
-
message: "controlplane transport security policy denied URL",
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
function validateArtifactURL(rawUrl, allowLoopbackHTTP) {
|
|
45
|
-
let target;
|
|
46
|
-
try {
|
|
47
|
-
if (/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(rawUrl)) {
|
|
48
|
-
target = new URL(rawUrl);
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
const base = globalThis.location?.href;
|
|
52
|
-
if (!base)
|
|
53
|
-
throw new Error("relative URL requires a browser location");
|
|
54
|
-
target = new URL(rawUrl, base);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
catch {
|
|
58
|
-
throw transportPolicyDenied();
|
|
59
|
-
}
|
|
60
|
-
if (target.username !== "" || target.password !== "" || target.hash !== "") {
|
|
61
|
-
throw transportPolicyDenied();
|
|
62
|
-
}
|
|
63
|
-
if (target.protocol === "https:")
|
|
64
|
-
return;
|
|
65
|
-
if (target.protocol === "http:"
|
|
66
|
-
&& allowLoopbackHTTP === true
|
|
67
|
-
&& isLiteralLoopbackHost(sourceHostname(rawUrl, target)))
|
|
68
|
-
return;
|
|
69
|
-
throw transportPolicyDenied();
|
|
70
|
-
}
|
|
71
|
-
function sourceHostname(rawUrl, target) {
|
|
72
|
-
const authority = /^(?:[A-Za-z][A-Za-z0-9+.-]*:)?\/\/([^/?#]*)/.exec(rawUrl)?.[1];
|
|
73
|
-
if (authority === undefined)
|
|
74
|
-
return target.hostname;
|
|
75
|
-
const hostPort = authority.slice(authority.lastIndexOf("@") + 1);
|
|
76
|
-
if (hostPort.startsWith("[")) {
|
|
77
|
-
const end = hostPort.indexOf("]");
|
|
78
|
-
if (end < 0 || (hostPort.length > end + 1 && !/^:\d+$/.test(hostPort.slice(end + 1)))) {
|
|
79
|
-
return hostPort;
|
|
80
|
-
}
|
|
81
|
-
return hostPort.slice(1, end);
|
|
82
|
-
}
|
|
83
|
-
const colon = hostPort.lastIndexOf(":");
|
|
84
|
-
if (colon < 0)
|
|
85
|
-
return hostPort;
|
|
86
|
-
if (hostPort.indexOf(":") !== colon || !/^\d+$/.test(hostPort.slice(colon + 1)))
|
|
87
|
-
return hostPort;
|
|
88
|
-
return hostPort.slice(0, colon);
|
|
89
|
-
}
|
|
90
|
-
function isLiteralLoopbackHost(rawHost) {
|
|
91
|
-
const host = rawHost.startsWith("[") && rawHost.endsWith("]")
|
|
92
|
-
? rawHost.slice(1, -1).toLowerCase()
|
|
93
|
-
: rawHost.toLowerCase();
|
|
94
|
-
if (host === "localhost" || host === "::1")
|
|
95
|
-
return true;
|
|
96
|
-
const parts = host.split(".");
|
|
97
|
-
if (parts.length !== 4)
|
|
98
|
-
return false;
|
|
99
|
-
const octets = parts.map((part) => {
|
|
100
|
-
if (!/^(0|[1-9]\d{0,2})$/.test(part))
|
|
101
|
-
return -1;
|
|
102
|
-
const value = Number(part);
|
|
103
|
-
return value <= 255 ? value : -1;
|
|
104
|
-
});
|
|
105
|
-
return octets.every((value) => value >= 0) && octets[0] === 127;
|
|
106
|
-
}
|
|
107
|
-
function parseMaybeJSON(bodyText) {
|
|
108
|
-
const trimmed = String(bodyText ?? "").trim();
|
|
109
|
-
if (trimmed === "")
|
|
110
|
-
return "";
|
|
111
|
-
try {
|
|
112
|
-
return JSON.parse(trimmed);
|
|
113
|
-
}
|
|
114
|
-
catch {
|
|
115
|
-
return trimmed;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
function parseContentLength(headerValue) {
|
|
119
|
-
const raw = String(headerValue ?? "").trim();
|
|
120
|
-
if (raw === "")
|
|
121
|
-
return null;
|
|
122
|
-
if (!/^[0-9]+$/.test(raw))
|
|
123
|
-
return null;
|
|
124
|
-
const parsed = Number(raw);
|
|
125
|
-
if (!Number.isSafeInteger(parsed) || parsed < 0)
|
|
126
|
-
return null;
|
|
127
|
-
return parsed;
|
|
128
|
-
}
|
|
129
|
-
async function readControlplaneText(response, maxBytes) {
|
|
130
|
-
const contentLength = parseContentLength(response.headers.get("Content-Length"));
|
|
131
|
-
if (contentLength !== null && contentLength > maxBytes) {
|
|
132
|
-
throw new ControlplaneResponseTooLargeError(maxBytes);
|
|
133
|
-
}
|
|
134
|
-
if (!response.body) {
|
|
135
|
-
return "";
|
|
136
|
-
}
|
|
137
|
-
const reader = response.body.getReader();
|
|
138
|
-
const decoder = new TextDecoder();
|
|
139
|
-
let totalBytes = 0;
|
|
140
|
-
let text = "";
|
|
141
|
-
while (true) {
|
|
142
|
-
const chunk = await reader.read();
|
|
143
|
-
if (chunk.done)
|
|
144
|
-
break;
|
|
145
|
-
totalBytes += chunk.value.byteLength;
|
|
146
|
-
if (totalBytes > maxBytes) {
|
|
147
|
-
try {
|
|
148
|
-
await reader.cancel();
|
|
149
|
-
}
|
|
150
|
-
catch {
|
|
151
|
-
// The size violation is authoritative; cancellation is secondary cleanup.
|
|
152
|
-
}
|
|
153
|
-
throw new ControlplaneResponseTooLargeError(maxBytes);
|
|
154
|
-
}
|
|
155
|
-
text += decoder.decode(chunk.value, { stream: true });
|
|
156
|
-
}
|
|
157
|
-
text += decoder.decode();
|
|
158
|
-
return text;
|
|
159
|
-
}
|
|
160
|
-
function decodeErrorMessage(status, responseBody) {
|
|
161
|
-
let message = `controlplane request failed: ${status}`;
|
|
162
|
-
let code = "";
|
|
163
|
-
if (responseBody && typeof responseBody === "object") {
|
|
164
|
-
const error = responseBody.error;
|
|
165
|
-
if (error && typeof error === "object") {
|
|
166
|
-
const nextMessage = String(error.message ?? "").trim();
|
|
167
|
-
if (nextMessage !== "") {
|
|
168
|
-
message = nextMessage;
|
|
169
|
-
}
|
|
170
|
-
code = String(error.code ?? "").trim();
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
else if (typeof responseBody === "string" && responseBody !== "") {
|
|
174
|
-
message = responseBody;
|
|
175
|
-
}
|
|
176
|
-
return { code, message };
|
|
177
|
-
}
|
|
178
|
-
export async function requestControlplaneJSON(url, init) {
|
|
179
|
-
const runFetch = resolveFetch(init.fetch);
|
|
180
|
-
const response = await runFetch(url, {
|
|
181
|
-
...init,
|
|
182
|
-
cache: "no-store",
|
|
183
|
-
redirect: "error",
|
|
184
|
-
});
|
|
185
|
-
let rawBody = "";
|
|
186
|
-
try {
|
|
187
|
-
rawBody = await readControlplaneText(response, DEFAULT_MAX_CONTROLPLANE_RESPONSE_BYTES);
|
|
188
|
-
}
|
|
189
|
-
catch (err) {
|
|
190
|
-
if (err instanceof ControlplaneResponseTooLargeError && !response.ok) {
|
|
191
|
-
throw new ControlplaneRequestError({
|
|
192
|
-
status: response.status,
|
|
193
|
-
message: err.message,
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
throw err;
|
|
197
|
-
}
|
|
198
|
-
const responseBody = parseMaybeJSON(rawBody);
|
|
199
|
-
if (!response.ok) {
|
|
200
|
-
const error = decodeErrorMessage(response.status, responseBody);
|
|
201
|
-
throw new ControlplaneRequestError({
|
|
202
|
-
status: response.status,
|
|
203
|
-
message: error.message,
|
|
204
|
-
code: error.code,
|
|
205
|
-
responseBody,
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
if (typeof responseBody === "string") {
|
|
209
|
-
if (responseBody === "") {
|
|
210
|
-
throw new Error("Invalid controlplane response: expected JSON body");
|
|
211
|
-
}
|
|
212
|
-
throw new Error("Invalid controlplane response: expected JSON body");
|
|
213
|
-
}
|
|
214
|
-
return responseBody;
|
|
215
|
-
}
|
|
216
|
-
function buildConnectArtifactPayload(config) {
|
|
217
|
-
const body = {
|
|
218
|
-
endpoint_id: String(config.endpointId ?? "").trim(),
|
|
219
|
-
};
|
|
220
|
-
if (body.endpoint_id === "")
|
|
221
|
-
throw new Error("endpointId is required");
|
|
222
|
-
if (config.payload !== undefined)
|
|
223
|
-
body.payload = { ...config.payload };
|
|
224
|
-
const traceId = String(config.correlation?.traceId ?? "").trim();
|
|
225
|
-
if (traceId !== "") {
|
|
226
|
-
body.correlation = { trace_id: traceId };
|
|
227
|
-
}
|
|
228
|
-
return body;
|
|
229
|
-
}
|
|
230
|
-
function createJSONHeaders(input) {
|
|
231
|
-
const headers = new Headers(input);
|
|
232
|
-
if (!headers.has("Content-Type")) {
|
|
233
|
-
headers.set("Content-Type", "application/json");
|
|
234
|
-
}
|
|
235
|
-
return headers;
|
|
236
|
-
}
|
|
237
|
-
export async function requestConnectArtifact(config) {
|
|
238
|
-
const url = buildControlplaneURL(config.baseUrl, config.path ?? DEFAULT_CONNECT_ARTIFACT_PATH);
|
|
239
|
-
validateArtifactURL(url, config.allowLoopbackHTTP);
|
|
240
|
-
const data = (await requestControlplaneJSON(url, {
|
|
241
|
-
...(config.fetch === undefined ? {} : { fetch: config.fetch }),
|
|
242
|
-
method: "POST",
|
|
243
|
-
credentials: config.credentials ?? "omit",
|
|
244
|
-
headers: createJSONHeaders(config.headers),
|
|
245
|
-
body: JSON.stringify(buildConnectArtifactPayload(config)),
|
|
246
|
-
...(config.signal === undefined ? {} : { signal: config.signal }),
|
|
247
|
-
}));
|
|
248
|
-
if (!data?.connect_artifact) {
|
|
249
|
-
throw new Error("Invalid controlplane response: missing `connect_artifact`");
|
|
250
|
-
}
|
|
251
|
-
return assertConnectArtifact(data.connect_artifact);
|
|
252
|
-
}
|
|
253
|
-
export async function requestEntryConnectArtifact(config) {
|
|
254
|
-
const entryTicket = String(config.entryTicket ?? "").trim();
|
|
255
|
-
if (entryTicket === "")
|
|
256
|
-
throw new Error("entryTicket is required");
|
|
257
|
-
const headers = createJSONHeaders(config.headers);
|
|
258
|
-
headers.set("Authorization", `Bearer ${entryTicket}`);
|
|
259
|
-
const url = buildControlplaneURL(config.baseUrl, config.path ?? DEFAULT_ENTRY_CONNECT_ARTIFACT_PATH);
|
|
260
|
-
validateArtifactURL(url, config.allowLoopbackHTTP);
|
|
261
|
-
const data = (await requestControlplaneJSON(url, {
|
|
262
|
-
...(config.fetch === undefined ? {} : { fetch: config.fetch }),
|
|
263
|
-
method: "POST",
|
|
264
|
-
credentials: config.credentials ?? "omit",
|
|
265
|
-
headers,
|
|
266
|
-
body: JSON.stringify(buildConnectArtifactPayload(config)),
|
|
267
|
-
...(config.signal === undefined ? {} : { signal: config.signal }),
|
|
268
|
-
}));
|
|
269
|
-
if (!data?.connect_artifact) {
|
|
270
|
-
throw new Error("Invalid controlplane response: missing `connect_artifact`");
|
|
271
|
-
}
|
|
272
|
-
return assertConnectArtifact(data.connect_artifact);
|
|
273
|
-
}
|