@floegence/flowersec-core 0.17.2 → 0.19.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 +34 -9
- package/dist/browser/controlplane.d.ts +5 -18
- package/dist/browser/controlplane.js +17 -67
- package/dist/browser/index.d.ts +4 -2
- package/dist/browser/index.js +2 -1
- package/dist/browser/reconnectConfig.d.ts +15 -2
- package/dist/browser/reconnectConfig.js +43 -12
- package/dist/client-connect/connectCore.d.ts +5 -0
- package/dist/client-connect/connectCore.js +1 -1
- package/dist/client-connect/tunnelAttachCloseReason.d.ts +1 -1
- package/dist/client-connect/tunnelAttachCloseReason.js +4 -1
- package/dist/connect/artifact.d.ts +37 -0
- package/dist/connect/artifact.js +217 -0
- package/dist/connect/internalNormalize.d.ts +22 -0
- package/dist/connect/internalNormalize.js +173 -0
- package/dist/controlplane/index.d.ts +2 -0
- package/dist/controlplane/index.js +1 -0
- package/dist/controlplane/request.d.ts +50 -0
- package/dist/controlplane/request.js +136 -0
- package/dist/e2ee/secureChannel.js +2 -5
- package/dist/facade.d.ts +4 -0
- package/dist/facade.js +15 -47
- package/dist/node/connect.d.ts +3 -0
- package/dist/node/index.d.ts +4 -0
- package/dist/node/index.js +2 -0
- package/dist/node/reconnectConfig.d.ts +42 -0
- package/dist/node/reconnectConfig.js +78 -0
- package/dist/observability/observer.d.ts +28 -2
- package/dist/observability/observer.js +296 -13
- package/dist/proxy/bootstrap.d.ts +22 -4
- package/dist/proxy/bootstrap.js +51 -6
- package/dist/proxy/index.d.ts +3 -1
- package/dist/proxy/index.js +2 -1
- package/dist/proxy/integration.d.ts +3 -3
- package/dist/proxy/integration.js +21 -10
- package/dist/proxy/preset.d.ts +31 -0
- package/dist/proxy/preset.js +133 -0
- package/dist/proxy/profiles.d.ts +2 -0
- package/dist/proxy/profiles.js +40 -17
- package/dist/proxy/runtimeScope.d.ts +49 -0
- package/dist/proxy/runtimeScope.js +223 -0
- package/dist/reconnect/artifactControlplane.d.ts +13 -0
- package/dist/reconnect/artifactControlplane.js +34 -0
- package/dist/reconnect/index.d.ts +1 -1
- package/dist/reconnect/index.js +13 -6
- package/dist/utils/errors.d.ts +1 -1
- package/dist/utils/errors.js +3 -1
- package/package.json +7 -3
|
@@ -2,12 +2,33 @@ import type { ClientPath } from "../client.js";
|
|
|
2
2
|
export type ConnectResult = "ok" | "fail";
|
|
3
3
|
export type ConnectReason = "websocket_error" | "websocket_closed" | "timeout" | "canceled";
|
|
4
4
|
export type AttachResult = "ok" | "fail";
|
|
5
|
-
export type AttachReason = "send_failed" | "too_many_connections" | "expected_attach" | "invalid_attach" | "invalid_token" | "channel_mismatch" | "role_mismatch" | "init_exp_mismatch" | "idle_timeout_mismatch" | "token_replay" | "replace_rate_limited" | "attach_failed" | "timeout" | "canceled";
|
|
5
|
+
export type AttachReason = "send_failed" | "too_many_connections" | "expected_attach" | "invalid_attach" | "invalid_token" | "channel_mismatch" | "role_mismatch" | "init_exp_mismatch" | "idle_timeout_mismatch" | "token_replay" | "tenant_mismatch" | "policy_denied" | "policy_error" | "replace_rate_limited" | "attach_failed" | "timeout" | "canceled";
|
|
6
6
|
export type HandshakeResult = "ok" | "fail";
|
|
7
7
|
export type HandshakeReason = "auth_tag_mismatch" | "handshake_failed" | "invalid_suite" | "invalid_version" | "timestamp_after_init_exp" | "timestamp_out_of_skew" | "timeout" | "canceled";
|
|
8
8
|
export type WsCloseKind = "local" | "peer_or_error";
|
|
9
9
|
export type WsErrorReason = "error" | "recv_buffer_exceeded" | "unexpected_text_frame" | "unexpected_message_type";
|
|
10
10
|
export type RpcCallResult = "ok" | "rpc_error" | "handler_not_found" | "transport_error" | "canceled";
|
|
11
|
+
export type DiagnosticEvent = Readonly<{
|
|
12
|
+
v: 1;
|
|
13
|
+
namespace: "connect";
|
|
14
|
+
path: ClientPath | "auto";
|
|
15
|
+
stage: "validate" | "normalize" | "scope" | "connect" | "attach" | "handshake" | "close" | "reconnect";
|
|
16
|
+
code_domain: "error" | "event";
|
|
17
|
+
code: string;
|
|
18
|
+
result: "ok" | "fail" | "retry" | "skip";
|
|
19
|
+
elapsed_ms: number;
|
|
20
|
+
attempt_seq: number;
|
|
21
|
+
trace_id?: string;
|
|
22
|
+
session_id?: string;
|
|
23
|
+
}>;
|
|
24
|
+
type ObserverContext = Readonly<{
|
|
25
|
+
path?: ClientPath | "auto";
|
|
26
|
+
traceId?: string;
|
|
27
|
+
sessionId?: string;
|
|
28
|
+
attemptSeq?: number;
|
|
29
|
+
attemptStartMs?: number;
|
|
30
|
+
maxQueuedItems?: number;
|
|
31
|
+
}>;
|
|
11
32
|
export type ClientObserver = {
|
|
12
33
|
onConnect(path: ClientPath, result: ConnectResult, reason: ConnectReason | undefined, elapsedSeconds: number): void;
|
|
13
34
|
onAttach(result: AttachResult, reason: AttachReason | undefined): void;
|
|
@@ -16,8 +37,13 @@ export type ClientObserver = {
|
|
|
16
37
|
onWsError(reason: WsErrorReason): void;
|
|
17
38
|
onRpcCall(result: RpcCallResult, elapsedSeconds: number): void;
|
|
18
39
|
onRpcNotify(): void;
|
|
40
|
+
onDiagnosticEvent(event: DiagnosticEvent): void;
|
|
19
41
|
};
|
|
20
42
|
export type ClientObserverLike = Partial<ClientObserver>;
|
|
43
|
+
type DiagnosticEventInput = Omit<DiagnosticEvent, "v" | "namespace" | "elapsed_ms" | "attempt_seq" | "trace_id" | "session_id" | "path"> & Partial<Pick<DiagnosticEvent, "path">>;
|
|
21
44
|
export declare const NoopObserver: ClientObserver;
|
|
22
|
-
export declare function
|
|
45
|
+
export declare function withObserverContext(observer: ClientObserverLike | undefined, context: ObserverContext): ClientObserverLike | undefined;
|
|
46
|
+
export declare function emitObserverDiagnostic(observer: ClientObserverLike | undefined, event: DiagnosticEventInput): void;
|
|
47
|
+
export declare function normalizeObserver(observer?: ClientObserverLike, context?: ObserverContext): ClientObserver;
|
|
23
48
|
export declare function nowSeconds(): number;
|
|
49
|
+
export {};
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
const OBSERVER_CONTEXT = Symbol.for("floegence.flowersec.observer_context");
|
|
2
|
+
const NORMALIZED_OBSERVER = Symbol.for("floegence.flowersec.normalized_observer");
|
|
3
|
+
const DEFAULT_MAX_QUEUED_ITEMS = 64;
|
|
1
4
|
export const NoopObserver = {
|
|
2
5
|
onConnect: () => { },
|
|
3
6
|
onAttach: () => { },
|
|
@@ -5,24 +8,304 @@ export const NoopObserver = {
|
|
|
5
8
|
onWsClose: () => { },
|
|
6
9
|
onWsError: () => { },
|
|
7
10
|
onRpcCall: () => { },
|
|
8
|
-
onRpcNotify: () => { }
|
|
11
|
+
onRpcNotify: () => { },
|
|
12
|
+
onDiagnosticEvent: () => { },
|
|
9
13
|
};
|
|
10
|
-
|
|
14
|
+
function getObserverContext(observer) {
|
|
15
|
+
const context = observer?.[OBSERVER_CONTEXT];
|
|
16
|
+
if (context == null || typeof context !== "object")
|
|
17
|
+
return {};
|
|
18
|
+
return context;
|
|
19
|
+
}
|
|
20
|
+
function safeInvoke(fn) {
|
|
21
|
+
if (fn == null)
|
|
22
|
+
return;
|
|
23
|
+
try {
|
|
24
|
+
fn();
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// Best effort only; observability must not affect connect semantics.
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function hasAnyHandlers(observer) {
|
|
11
31
|
if (observer == null)
|
|
12
|
-
return
|
|
32
|
+
return false;
|
|
33
|
+
return (observer.onConnect != null ||
|
|
34
|
+
observer.onAttach != null ||
|
|
35
|
+
observer.onHandshake != null ||
|
|
36
|
+
observer.onWsClose != null ||
|
|
37
|
+
observer.onWsError != null ||
|
|
38
|
+
observer.onRpcCall != null ||
|
|
39
|
+
observer.onRpcNotify != null ||
|
|
40
|
+
observer.onDiagnosticEvent != null);
|
|
41
|
+
}
|
|
42
|
+
function buildDiagnosticEvent(context, event) {
|
|
43
|
+
return Object.freeze({
|
|
44
|
+
v: 1,
|
|
45
|
+
namespace: "connect",
|
|
46
|
+
path: event.path ?? context.path ?? "auto",
|
|
47
|
+
stage: event.stage,
|
|
48
|
+
code_domain: event.code_domain,
|
|
49
|
+
code: event.code,
|
|
50
|
+
result: event.result,
|
|
51
|
+
elapsed_ms: Math.max(0, Math.floor(nowMilliseconds() - (context.attemptStartMs ?? nowMilliseconds()))),
|
|
52
|
+
attempt_seq: Math.max(1, Math.floor(context.attemptSeq ?? 1)),
|
|
53
|
+
...(context.traceId === undefined ? {} : { trace_id: context.traceId }),
|
|
54
|
+
...(context.sessionId === undefined
|
|
55
|
+
? {}
|
|
56
|
+
: { session_id: context.sessionId }),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function mapConnectDiagnostic(path, result, reason) {
|
|
60
|
+
if (result === "ok") {
|
|
61
|
+
return {
|
|
62
|
+
path,
|
|
63
|
+
stage: "connect",
|
|
64
|
+
code_domain: "event",
|
|
65
|
+
code: "connect_ok",
|
|
66
|
+
result: "ok",
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
path,
|
|
71
|
+
stage: "connect",
|
|
72
|
+
code_domain: "error",
|
|
73
|
+
code: reason === "timeout" || reason === "canceled" ? reason : "dial_failed",
|
|
74
|
+
result: "fail",
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function mapAttachDiagnostic(result, reason, path) {
|
|
78
|
+
if (result === "ok") {
|
|
79
|
+
return {
|
|
80
|
+
path,
|
|
81
|
+
stage: "attach",
|
|
82
|
+
code_domain: "event",
|
|
83
|
+
code: "attach_ok",
|
|
84
|
+
result: "ok",
|
|
85
|
+
};
|
|
86
|
+
}
|
|
13
87
|
return {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
onRpcCall: observer.onRpcCall ?? NoopObserver.onRpcCall,
|
|
20
|
-
onRpcNotify: observer.onRpcNotify ?? NoopObserver.onRpcNotify
|
|
88
|
+
path,
|
|
89
|
+
stage: "attach",
|
|
90
|
+
code_domain: "error",
|
|
91
|
+
code: reason === "send_failed" ? "attach_failed" : (reason ?? "attach_failed"),
|
|
92
|
+
result: "fail",
|
|
21
93
|
};
|
|
22
94
|
}
|
|
95
|
+
function mapHandshakeDiagnostic(path, result, reason) {
|
|
96
|
+
if (result === "ok") {
|
|
97
|
+
return {
|
|
98
|
+
path,
|
|
99
|
+
stage: "handshake",
|
|
100
|
+
code_domain: "event",
|
|
101
|
+
code: "handshake_ok",
|
|
102
|
+
result: "ok",
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
path,
|
|
107
|
+
stage: "handshake",
|
|
108
|
+
code_domain: "error",
|
|
109
|
+
code: reason ?? "handshake_failed",
|
|
110
|
+
result: "fail",
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function mapWsCloseDiagnostic(kind, path) {
|
|
114
|
+
return {
|
|
115
|
+
path,
|
|
116
|
+
stage: "close",
|
|
117
|
+
code_domain: "event",
|
|
118
|
+
code: kind === "local" ? "ws_close_local" : "ws_close_peer_or_error",
|
|
119
|
+
result: "skip",
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function mapWsErrorDiagnostic(path) {
|
|
123
|
+
return {
|
|
124
|
+
path,
|
|
125
|
+
stage: "close",
|
|
126
|
+
code_domain: "event",
|
|
127
|
+
code: "ws_error",
|
|
128
|
+
result: "skip",
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
class ObserverDispatcher {
|
|
132
|
+
[NORMALIZED_OBSERVER] = true;
|
|
133
|
+
[OBSERVER_CONTEXT];
|
|
134
|
+
queue = [];
|
|
135
|
+
observer;
|
|
136
|
+
maxQueuedItems;
|
|
137
|
+
draining = false;
|
|
138
|
+
overflowQueued = false;
|
|
139
|
+
constructor(observer, context) {
|
|
140
|
+
this.observer = observer;
|
|
141
|
+
this[OBSERVER_CONTEXT] = context;
|
|
142
|
+
this.maxQueuedItems = Math.max(4, Math.floor(context.maxQueuedItems ?? DEFAULT_MAX_QUEUED_ITEMS));
|
|
143
|
+
}
|
|
144
|
+
onConnect(path, result, reason, elapsedSeconds) {
|
|
145
|
+
const diagnostic = mapConnectDiagnostic(path, result, reason);
|
|
146
|
+
this.enqueueCombined(() => this.observer.onConnect?.(path, result, reason, elapsedSeconds), diagnostic, result === "fail");
|
|
147
|
+
}
|
|
148
|
+
onAttach(result, reason) {
|
|
149
|
+
const diagnostic = mapAttachDiagnostic(result, reason, this[OBSERVER_CONTEXT].path ?? "auto");
|
|
150
|
+
this.enqueueCombined(() => this.observer.onAttach?.(result, reason), diagnostic, result === "fail");
|
|
151
|
+
}
|
|
152
|
+
onHandshake(path, result, reason, elapsedSeconds) {
|
|
153
|
+
const diagnostic = mapHandshakeDiagnostic(path, result, reason);
|
|
154
|
+
this.enqueueCombined(() => this.observer.onHandshake?.(path, result, reason, elapsedSeconds), diagnostic, result === "fail");
|
|
155
|
+
}
|
|
156
|
+
onWsClose(kind, code) {
|
|
157
|
+
const diagnostic = mapWsCloseDiagnostic(kind, this[OBSERVER_CONTEXT].path ?? "auto");
|
|
158
|
+
this.enqueueCombined(() => this.observer.onWsClose?.(kind, code), diagnostic, false);
|
|
159
|
+
}
|
|
160
|
+
onWsError(reason) {
|
|
161
|
+
const diagnostic = mapWsErrorDiagnostic(this[OBSERVER_CONTEXT].path ?? "auto");
|
|
162
|
+
this.enqueueCombined(() => this.observer.onWsError?.(reason), diagnostic, false);
|
|
163
|
+
}
|
|
164
|
+
onRpcCall(result, elapsedSeconds) {
|
|
165
|
+
this.enqueueCombined(() => this.observer.onRpcCall?.(result, elapsedSeconds), undefined, false);
|
|
166
|
+
}
|
|
167
|
+
onRpcNotify() {
|
|
168
|
+
this.enqueueCombined(() => this.observer.onRpcNotify?.(), undefined, false);
|
|
169
|
+
}
|
|
170
|
+
onDiagnosticEvent(event) {
|
|
171
|
+
this.enqueueCombined(() => this.observer.onDiagnosticEvent?.(event), undefined, event.result === "fail");
|
|
172
|
+
}
|
|
173
|
+
emitDiagnosticEvent(event) {
|
|
174
|
+
const diagnostic = buildDiagnosticEvent(this[OBSERVER_CONTEXT], event);
|
|
175
|
+
this.enqueue({
|
|
176
|
+
kind: event.code === "diagnostics_overflow"
|
|
177
|
+
? "overflow"
|
|
178
|
+
: event.result === "fail"
|
|
179
|
+
? "terminal"
|
|
180
|
+
: "normal",
|
|
181
|
+
deliver: () => this.observer.onDiagnosticEvent?.(diagnostic),
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
enqueueCombined(callback, diagnostic, terminal) {
|
|
185
|
+
if (callback == null && diagnostic == null)
|
|
186
|
+
return;
|
|
187
|
+
const event = diagnostic == null
|
|
188
|
+
? undefined
|
|
189
|
+
: buildDiagnosticEvent(this[OBSERVER_CONTEXT], diagnostic);
|
|
190
|
+
this.enqueue({
|
|
191
|
+
kind: terminal ? "terminal" : "normal",
|
|
192
|
+
deliver: () => {
|
|
193
|
+
safeInvoke(callback);
|
|
194
|
+
if (event != null) {
|
|
195
|
+
safeInvoke(() => this.observer.onDiagnosticEvent?.(event));
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
enqueue(item) {
|
|
201
|
+
if (this.queue.length >= this.maxQueuedItems) {
|
|
202
|
+
if (!this.makeRoom(item.kind)) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (item.kind === "overflow") {
|
|
207
|
+
if (this.overflowQueued)
|
|
208
|
+
return;
|
|
209
|
+
this.overflowQueued = true;
|
|
210
|
+
}
|
|
211
|
+
this.queue.push(item);
|
|
212
|
+
this.scheduleDrain();
|
|
213
|
+
}
|
|
214
|
+
makeRoom(kind) {
|
|
215
|
+
const removableIndex = this.queue.findIndex((entry) => entry.kind === "normal");
|
|
216
|
+
if (removableIndex >= 0) {
|
|
217
|
+
this.queue.splice(removableIndex, 1);
|
|
218
|
+
if (kind === "normal") {
|
|
219
|
+
this.queueOverflowEvent();
|
|
220
|
+
}
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
if (kind === "normal") {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
if (this.queue.length > 0) {
|
|
227
|
+
const shifted = this.queue.shift();
|
|
228
|
+
if (shifted?.kind === "overflow")
|
|
229
|
+
this.overflowQueued = false;
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
queueOverflowEvent() {
|
|
235
|
+
if (this.overflowQueued || this.observer.onDiagnosticEvent == null)
|
|
236
|
+
return;
|
|
237
|
+
this.enqueue({
|
|
238
|
+
kind: "overflow",
|
|
239
|
+
deliver: () => {
|
|
240
|
+
this.overflowQueued = false;
|
|
241
|
+
safeInvoke(() => this.observer.onDiagnosticEvent?.(buildDiagnosticEvent(this[OBSERVER_CONTEXT], {
|
|
242
|
+
stage: "reconnect",
|
|
243
|
+
code_domain: "event",
|
|
244
|
+
code: "diagnostics_overflow",
|
|
245
|
+
result: "skip",
|
|
246
|
+
})));
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
scheduleDrain() {
|
|
251
|
+
if (this.draining)
|
|
252
|
+
return;
|
|
253
|
+
this.draining = true;
|
|
254
|
+
queueMicrotask(() => this.drainOne());
|
|
255
|
+
}
|
|
256
|
+
drainOne() {
|
|
257
|
+
this.draining = false;
|
|
258
|
+
const item = this.queue.shift();
|
|
259
|
+
if (item == null)
|
|
260
|
+
return;
|
|
261
|
+
if (item.kind === "overflow") {
|
|
262
|
+
this.overflowQueued = false;
|
|
263
|
+
}
|
|
264
|
+
safeInvoke(item.deliver);
|
|
265
|
+
if (this.queue.length > 0) {
|
|
266
|
+
this.scheduleDrain();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
export function withObserverContext(observer, context) {
|
|
271
|
+
if (observer == null && Object.keys(context).length === 0)
|
|
272
|
+
return observer;
|
|
273
|
+
const previous = getObserverContext(observer);
|
|
274
|
+
return Object.assign({}, observer ?? {}, {
|
|
275
|
+
[OBSERVER_CONTEXT]: {
|
|
276
|
+
...previous,
|
|
277
|
+
...context,
|
|
278
|
+
},
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
export function emitObserverDiagnostic(observer, event) {
|
|
282
|
+
const normalized = normalizeObserver(observer);
|
|
283
|
+
if (normalized.emitDiagnosticEvent) {
|
|
284
|
+
normalized.emitDiagnosticEvent(event);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
safeInvoke(() => normalized.onDiagnosticEvent(buildDiagnosticEvent(getObserverContext(observer), event)));
|
|
288
|
+
}
|
|
289
|
+
export function normalizeObserver(observer, context = {}) {
|
|
290
|
+
if (observer?.[NORMALIZED_OBSERVER] === true) {
|
|
291
|
+
return observer;
|
|
292
|
+
}
|
|
293
|
+
if (!hasAnyHandlers(observer))
|
|
294
|
+
return NoopObserver;
|
|
295
|
+
return new ObserverDispatcher(observer ?? {}, {
|
|
296
|
+
attemptSeq: 1,
|
|
297
|
+
attemptStartMs: nowMilliseconds(),
|
|
298
|
+
...getObserverContext(observer),
|
|
299
|
+
...context,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
23
302
|
export function nowSeconds() {
|
|
24
|
-
|
|
25
|
-
|
|
303
|
+
return nowMilliseconds() / 1000;
|
|
304
|
+
}
|
|
305
|
+
function nowMilliseconds() {
|
|
306
|
+
if (typeof performance !== "undefined" &&
|
|
307
|
+
typeof performance.now === "function") {
|
|
308
|
+
return performance.now();
|
|
26
309
|
}
|
|
27
|
-
return Date.now()
|
|
310
|
+
return Date.now();
|
|
28
311
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { Client } from "../client.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ConnectArtifact } from "../connect/artifact.js";
|
|
3
|
+
import type { ConnectBrowserOptions, TunnelConnectBrowserOptions } from "../browser/connect.js";
|
|
3
4
|
import type { ChannelInitGrant } from "../gen/flowersec/controlplane/v1.gen.js";
|
|
4
|
-
import { type ProxyIntegrationPlugin, type
|
|
5
|
+
import { type ProxyIntegrationPlugin, type RegisterProxyIntegrationOptions, type ProxyIntegrationServiceWorkerOptions } from "./integration.js";
|
|
5
6
|
import { type RegisterProxyControllerWindowOptions } from "./controllerWindow.js";
|
|
6
|
-
import type {
|
|
7
|
+
import type { ProxyPresetInput } from "./preset.js";
|
|
7
8
|
import { type ProxyRuntime } from "./runtime.js";
|
|
8
9
|
export type ConnectTunnelProxyBrowserOptions = Readonly<{
|
|
9
10
|
connect?: TunnelConnectBrowserOptions;
|
|
10
|
-
|
|
11
|
+
preset?: ProxyPresetInput;
|
|
11
12
|
runtimeGlobalKey?: string;
|
|
12
13
|
runtime?: RegisterProxyIntegrationOptions["runtime"];
|
|
13
14
|
serviceWorker: ProxyIntegrationServiceWorkerOptions;
|
|
@@ -18,6 +19,14 @@ export type ConnectTunnelProxyBrowserHandle = Readonly<{
|
|
|
18
19
|
runtime: ProxyRuntime;
|
|
19
20
|
dispose: () => Promise<void>;
|
|
20
21
|
}>;
|
|
22
|
+
export type ConnectArtifactProxyBrowserOptions = Readonly<{
|
|
23
|
+
connect?: ConnectBrowserOptions;
|
|
24
|
+
preset?: ProxyPresetInput;
|
|
25
|
+
runtimeGlobalKey?: string;
|
|
26
|
+
runtime?: RegisterProxyIntegrationOptions["runtime"];
|
|
27
|
+
serviceWorker?: ProxyIntegrationServiceWorkerOptions;
|
|
28
|
+
plugins?: readonly ProxyIntegrationPlugin[];
|
|
29
|
+
}>;
|
|
21
30
|
export type ConnectTunnelProxyControllerBrowserOptions = Readonly<{
|
|
22
31
|
connect?: TunnelConnectBrowserOptions;
|
|
23
32
|
runtime?: RegisterProxyIntegrationOptions["runtime"];
|
|
@@ -30,5 +39,14 @@ export type ConnectTunnelProxyControllerBrowserHandle = Readonly<{
|
|
|
30
39
|
runtime: ProxyRuntime;
|
|
31
40
|
dispose: () => void;
|
|
32
41
|
}>;
|
|
42
|
+
export type ConnectArtifactProxyControllerBrowserOptions = Readonly<{
|
|
43
|
+
connect?: ConnectBrowserOptions;
|
|
44
|
+
runtime?: RegisterProxyIntegrationOptions["runtime"];
|
|
45
|
+
allowedOrigins?: RegisterProxyControllerWindowOptions["allowedOrigins"];
|
|
46
|
+
targetWindow?: RegisterProxyControllerWindowOptions["targetWindow"];
|
|
47
|
+
expectedSource?: RegisterProxyControllerWindowOptions["expectedSource"];
|
|
48
|
+
}>;
|
|
33
49
|
export declare function connectTunnelProxyBrowser(grant: ChannelInitGrant, opts: ConnectTunnelProxyBrowserOptions): Promise<ConnectTunnelProxyBrowserHandle>;
|
|
50
|
+
export declare function connectArtifactProxyBrowser(artifact: ConnectArtifact, opts?: ConnectArtifactProxyBrowserOptions): Promise<ConnectTunnelProxyBrowserHandle>;
|
|
34
51
|
export declare function connectTunnelProxyControllerBrowser(grant: ChannelInitGrant, opts: ConnectTunnelProxyControllerBrowserOptions): Promise<ConnectTunnelProxyControllerBrowserHandle>;
|
|
52
|
+
export declare function connectArtifactProxyControllerBrowser(artifact: ConnectArtifact, opts?: ConnectArtifactProxyControllerBrowserOptions): Promise<ConnectTunnelProxyControllerBrowserHandle>;
|
package/dist/proxy/bootstrap.js
CHANGED
|
@@ -1,13 +1,34 @@
|
|
|
1
|
-
import { connectTunnelBrowser } from "../browser/connect.js";
|
|
1
|
+
import { connectBrowser, connectTunnelBrowser } from "../browser/connect.js";
|
|
2
2
|
import { registerProxyIntegration, } from "./integration.js";
|
|
3
3
|
import { registerProxyControllerWindow } from "./controllerWindow.js";
|
|
4
|
+
import { extractProxyRuntimeScopeV1, resolvePresetInputFromScope, resolveRuntimeLimitsFromScope, resolveRuntimePresetLimits, } from "./runtimeScope.js";
|
|
4
5
|
import { createProxyRuntime } from "./runtime.js";
|
|
5
|
-
|
|
6
|
-
const
|
|
6
|
+
function scopeRuntimeToIntegrationOptions(scope, opts) {
|
|
7
|
+
const runtimeLimits = resolveRuntimeLimitsFromScope(scope, opts.runtime);
|
|
8
|
+
const presetFromScope = resolvePresetInputFromScope(scope, opts.preset);
|
|
9
|
+
const presetFromLimits = presetFromScope ?? resolveRuntimePresetLimits(scope);
|
|
10
|
+
const serviceWorker = opts.serviceWorker ?? (scope.mode === "service_worker" ? {
|
|
11
|
+
scriptUrl: scope.serviceWorker.scriptUrl,
|
|
12
|
+
scope: scope.serviceWorker.scope,
|
|
13
|
+
} : undefined);
|
|
14
|
+
if (!serviceWorker) {
|
|
15
|
+
throw new Error("service worker config is required for proxy runtime mode");
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
...(presetFromLimits === undefined ? {} : { preset: presetFromLimits }),
|
|
19
|
+
...(opts.runtimeGlobalKey === undefined ? {} : { runtimeGlobalKey: opts.runtimeGlobalKey }),
|
|
20
|
+
...(runtimeLimits === undefined ? {} : { runtime: runtimeLimits }),
|
|
21
|
+
serviceWorker,
|
|
22
|
+
...(opts.plugins === undefined ? {} : { plugins: opts.plugins }),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
async function connectProxyBrowserClient(client, opts) {
|
|
26
|
+
const compat = opts;
|
|
7
27
|
const integrationInput = {
|
|
8
28
|
client,
|
|
9
29
|
serviceWorker: opts.serviceWorker,
|
|
10
|
-
...(opts.
|
|
30
|
+
...(opts.preset === undefined ? {} : { preset: opts.preset }),
|
|
31
|
+
...(compat.profile === undefined ? {} : { profile: compat.profile }),
|
|
11
32
|
...(opts.runtimeGlobalKey === undefined ? {} : { runtimeGlobalKey: opts.runtimeGlobalKey }),
|
|
12
33
|
...(opts.runtime === undefined ? {} : { runtime: opts.runtime }),
|
|
13
34
|
...(opts.plugins === undefined ? {} : { plugins: opts.plugins }),
|
|
@@ -46,8 +67,7 @@ export async function connectTunnelProxyBrowser(grant, opts) {
|
|
|
46
67
|
},
|
|
47
68
|
};
|
|
48
69
|
}
|
|
49
|
-
|
|
50
|
-
const client = await connectTunnelBrowser(grant, opts.connect ?? {});
|
|
70
|
+
function connectProxyControllerClient(client, opts) {
|
|
51
71
|
let runtime = null;
|
|
52
72
|
let controller = null;
|
|
53
73
|
try {
|
|
@@ -80,3 +100,28 @@ export async function connectTunnelProxyControllerBrowser(grant, opts) {
|
|
|
80
100
|
},
|
|
81
101
|
};
|
|
82
102
|
}
|
|
103
|
+
export async function connectTunnelProxyBrowser(grant, opts) {
|
|
104
|
+
const client = await connectTunnelBrowser(grant, opts.connect ?? {});
|
|
105
|
+
return await connectProxyBrowserClient(client, opts);
|
|
106
|
+
}
|
|
107
|
+
export async function connectArtifactProxyBrowser(artifact, opts = {}) {
|
|
108
|
+
const scope = extractProxyRuntimeScopeV1(artifact, "service_worker");
|
|
109
|
+
const client = await connectBrowser(artifact, opts.connect ?? {});
|
|
110
|
+
const nextOpts = scopeRuntimeToIntegrationOptions(scope, opts);
|
|
111
|
+
return await connectProxyBrowserClient(client, nextOpts);
|
|
112
|
+
}
|
|
113
|
+
export async function connectTunnelProxyControllerBrowser(grant, opts) {
|
|
114
|
+
const client = await connectTunnelBrowser(grant, opts.connect ?? {});
|
|
115
|
+
return connectProxyControllerClient(client, opts);
|
|
116
|
+
}
|
|
117
|
+
export async function connectArtifactProxyControllerBrowser(artifact, opts = {}) {
|
|
118
|
+
const scope = extractProxyRuntimeScopeV1(artifact, "controller_bridge");
|
|
119
|
+
const client = await connectBrowser(artifact, opts.connect ?? {});
|
|
120
|
+
const runtime = resolveRuntimeLimitsFromScope(scope, opts.runtime);
|
|
121
|
+
return connectProxyControllerClient(client, {
|
|
122
|
+
...(runtime === undefined ? {} : { runtime }),
|
|
123
|
+
allowedOrigins: opts.allowedOrigins ?? scope.controllerBridge.allowedOrigins,
|
|
124
|
+
...(opts.targetWindow === undefined ? {} : { targetWindow: opts.targetWindow }),
|
|
125
|
+
...(opts.expectedSource === undefined ? {} : { expectedSource: opts.expectedSource }),
|
|
126
|
+
});
|
|
127
|
+
}
|
package/dist/proxy/index.d.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
export * from "./constants.js";
|
|
2
2
|
export * from "./types.js";
|
|
3
3
|
export * from "./cookieJar.js";
|
|
4
|
-
export
|
|
4
|
+
export type { ProxyPresetInput, ProxyPresetLimits, ProxyPresetManifest, ResolvedProxyPreset } from "./preset.js";
|
|
5
|
+
export { DEFAULT_PROXY_PRESET_MANIFEST, assertProxyPresetManifest, resolveProxyPreset } from "./preset.js";
|
|
5
6
|
export * from "./runtime.js";
|
|
6
7
|
export * from "./serviceWorker.js";
|
|
7
8
|
export { registerServiceWorkerAndEnsureControl } from "./registerServiceWorker.js";
|
|
8
9
|
export * from "./integration.js";
|
|
9
10
|
export * from "./controllerGuard.js";
|
|
10
11
|
export * from "./bootstrap.js";
|
|
12
|
+
export * from "./runtimeScope.js";
|
|
11
13
|
export * from "./controllerWindow.js";
|
|
12
14
|
export * from "./appWindow.js";
|
|
13
15
|
export * from "./wsPatch.js";
|
package/dist/proxy/index.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
export * from "./constants.js";
|
|
2
2
|
export * from "./types.js";
|
|
3
3
|
export * from "./cookieJar.js";
|
|
4
|
-
export
|
|
4
|
+
export { DEFAULT_PROXY_PRESET_MANIFEST, assertProxyPresetManifest, resolveProxyPreset } from "./preset.js";
|
|
5
5
|
export * from "./runtime.js";
|
|
6
6
|
export * from "./serviceWorker.js";
|
|
7
7
|
export { registerServiceWorkerAndEnsureControl } from "./registerServiceWorker.js";
|
|
8
8
|
export * from "./integration.js";
|
|
9
9
|
export * from "./controllerGuard.js";
|
|
10
10
|
export * from "./bootstrap.js";
|
|
11
|
+
export * from "./runtimeScope.js";
|
|
11
12
|
export * from "./controllerWindow.js";
|
|
12
13
|
export * from "./appWindow.js";
|
|
13
14
|
export * from "./wsPatch.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Client } from "../client.js";
|
|
2
|
-
import { type
|
|
2
|
+
import { type ProxyPresetInput, type ResolvedProxyPreset } from "./preset.js";
|
|
3
3
|
import { type ProxyRuntime } from "./runtime.js";
|
|
4
4
|
import { type ProxyServiceWorkerScriptOptions } from "./serviceWorker.js";
|
|
5
5
|
export type ProxyIntegrationMonitorOptions = Readonly<{
|
|
@@ -26,7 +26,7 @@ export type ProxyIntegrationServiceWorkerOptions = Readonly<{
|
|
|
26
26
|
}>;
|
|
27
27
|
export type RegisterProxyIntegrationOptions = Readonly<{
|
|
28
28
|
client: Client;
|
|
29
|
-
|
|
29
|
+
preset?: ProxyPresetInput;
|
|
30
30
|
runtimeGlobalKey?: string;
|
|
31
31
|
runtime?: Readonly<{
|
|
32
32
|
maxJsonFrameBytes?: number;
|
|
@@ -41,7 +41,7 @@ export type RegisterProxyIntegrationOptions = Readonly<{
|
|
|
41
41
|
export type ProxyIntegrationContext = Readonly<{
|
|
42
42
|
runtime: ProxyRuntime;
|
|
43
43
|
options: RegisterProxyIntegrationOptions;
|
|
44
|
-
|
|
44
|
+
preset: ResolvedProxyPreset;
|
|
45
45
|
}>;
|
|
46
46
|
export type ControllerMismatchContext = Readonly<{
|
|
47
47
|
expectedScriptPathSuffix: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { profileToPresetManifest } from "./profiles.js";
|
|
2
|
+
import { resolveProxyPreset } from "./preset.js";
|
|
2
3
|
import { registerServiceWorkerAndEnsureControl } from "./registerServiceWorker.js";
|
|
3
4
|
import { createProxyRuntime } from "./runtime.js";
|
|
4
5
|
import { createProxyServiceWorkerScript } from "./serviceWorker.js";
|
|
@@ -163,13 +164,23 @@ function shouldIgnoreMismatch(plugins, ctx) {
|
|
|
163
164
|
}
|
|
164
165
|
return false;
|
|
165
166
|
}
|
|
166
|
-
function
|
|
167
|
+
function resolveIntegrationPreset(opts) {
|
|
168
|
+
if (opts.preset !== undefined && opts.profile !== undefined) {
|
|
169
|
+
throw new Error("preset and deprecated profile cannot be used together");
|
|
170
|
+
}
|
|
171
|
+
if (opts.preset !== undefined)
|
|
172
|
+
return resolveProxyPreset(opts.preset);
|
|
173
|
+
if (opts.profile !== undefined)
|
|
174
|
+
return resolveProxyPreset(profileToPresetManifest(opts.profile));
|
|
175
|
+
return resolveProxyPreset();
|
|
176
|
+
}
|
|
177
|
+
function buildRuntimeOptions(preset, runtime) {
|
|
167
178
|
return {
|
|
168
|
-
maxJsonFrameBytes: runtime?.maxJsonFrameBytes ??
|
|
169
|
-
maxChunkBytes: runtime?.maxChunkBytes ??
|
|
170
|
-
maxBodyBytes: runtime?.maxBodyBytes ??
|
|
171
|
-
maxWsFrameBytes: runtime?.maxWsFrameBytes ??
|
|
172
|
-
timeoutMs: runtime?.timeoutMs ??
|
|
179
|
+
maxJsonFrameBytes: runtime?.maxJsonFrameBytes ?? preset.limits.max_json_frame_bytes,
|
|
180
|
+
maxChunkBytes: runtime?.maxChunkBytes ?? preset.limits.max_chunk_bytes,
|
|
181
|
+
maxBodyBytes: runtime?.maxBodyBytes ?? preset.limits.max_body_bytes,
|
|
182
|
+
maxWsFrameBytes: runtime?.maxWsFrameBytes ?? preset.limits.max_ws_frame_bytes,
|
|
183
|
+
timeoutMs: runtime?.timeoutMs ?? preset.limits.timeout_ms ?? 0,
|
|
173
184
|
};
|
|
174
185
|
}
|
|
175
186
|
function bindRuntimeGlobal(runtime, key) {
|
|
@@ -221,8 +232,8 @@ export function createProxyIntegrationServiceWorkerScript(opts = {}) {
|
|
|
221
232
|
export async function registerProxyIntegration(input) {
|
|
222
233
|
const plugins = input.plugins ?? [];
|
|
223
234
|
const opts = applyPluginMutations(input, plugins);
|
|
224
|
-
const
|
|
225
|
-
const runtimeOpts = buildRuntimeOptions(
|
|
235
|
+
const preset = resolveIntegrationPreset(opts);
|
|
236
|
+
const runtimeOpts = buildRuntimeOptions(preset, opts.runtime);
|
|
226
237
|
const runtime = createProxyRuntime({ ...runtimeOpts, client: opts.client });
|
|
227
238
|
const releaseRuntimeGlobal = bindRuntimeGlobal(runtime, opts.runtimeGlobalKey);
|
|
228
239
|
const swCfg = opts.serviceWorker;
|
|
@@ -285,7 +296,7 @@ export async function registerProxyIntegration(input) {
|
|
|
285
296
|
const ctx = {
|
|
286
297
|
runtime,
|
|
287
298
|
options: opts,
|
|
288
|
-
|
|
299
|
+
preset,
|
|
289
300
|
};
|
|
290
301
|
for (const plugin of plugins) {
|
|
291
302
|
await plugin.onRegistered?.(ctx);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type ProxyPresetLimits = Readonly<{
|
|
2
|
+
max_json_frame_bytes?: number;
|
|
3
|
+
max_chunk_bytes?: number;
|
|
4
|
+
max_body_bytes?: number;
|
|
5
|
+
max_ws_frame_bytes?: number;
|
|
6
|
+
timeout_ms?: number;
|
|
7
|
+
}>;
|
|
8
|
+
export type ProxyPresetManifest = Readonly<{
|
|
9
|
+
v: 1;
|
|
10
|
+
preset_id: string;
|
|
11
|
+
deprecated?: boolean;
|
|
12
|
+
limits: ProxyPresetLimits;
|
|
13
|
+
}>;
|
|
14
|
+
export type ResolvedProxyPreset = Readonly<{
|
|
15
|
+
v: 1;
|
|
16
|
+
preset_id: string;
|
|
17
|
+
deprecated: boolean;
|
|
18
|
+
limits: Readonly<{
|
|
19
|
+
max_json_frame_bytes: number;
|
|
20
|
+
max_chunk_bytes: number;
|
|
21
|
+
max_body_bytes: number;
|
|
22
|
+
max_ws_frame_bytes: number;
|
|
23
|
+
timeout_ms?: number;
|
|
24
|
+
}>;
|
|
25
|
+
}>;
|
|
26
|
+
export declare const DEFAULT_PROXY_PRESET_MANIFEST: ProxyPresetManifest;
|
|
27
|
+
export declare const CODESERVER_PROXY_PRESET_MANIFEST: ProxyPresetManifest;
|
|
28
|
+
export type ProxyPresetInput = ProxyPresetManifest | Partial<ProxyPresetLimits>;
|
|
29
|
+
export declare function assertProxyPresetManifest(value: unknown): ProxyPresetManifest;
|
|
30
|
+
export declare function resolveNamedProxyPreset(name: string): ProxyPresetManifest;
|
|
31
|
+
export declare function resolveProxyPreset(input?: ProxyPresetInput): ResolvedProxyPreset;
|