llm-simple-router 0.3.7 → 0.4.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.
Files changed (104) hide show
  1. package/README.md +81 -49
  2. package/dist/admin/constants.d.ts +1 -8
  3. package/dist/admin/constants.js +2 -8
  4. package/dist/admin/logs.js +18 -3
  5. package/dist/admin/router-keys.js +1 -2
  6. package/dist/cli.js +0 -0
  7. package/dist/constants.d.ts +8 -0
  8. package/dist/constants.js +9 -0
  9. package/dist/db/index.d.ts +4 -4
  10. package/dist/db/index.js +2 -2
  11. package/dist/db/logs.d.ts +18 -33
  12. package/dist/db/logs.js +40 -17
  13. package/dist/db/metrics.d.ts +33 -0
  14. package/dist/db/metrics.js +7 -0
  15. package/dist/db/migrations/018_add_failover_field.sql +2 -0
  16. package/dist/db/retry-rules.d.ts +2 -2
  17. package/dist/db/retry-rules.js +26 -13
  18. package/dist/index.js +3 -5
  19. package/dist/monitor/request-tracker.d.ts +6 -0
  20. package/dist/monitor/request-tracker.js +23 -54
  21. package/dist/monitor/stream-extractor.d.ts +11 -0
  22. package/dist/monitor/stream-extractor.js +51 -0
  23. package/dist/proxy/anthropic.js +19 -32
  24. package/dist/proxy/log-helpers.d.ts +11 -4
  25. package/dist/proxy/log-helpers.js +5 -3
  26. package/dist/proxy/openai.js +18 -34
  27. package/dist/proxy/orchestrator.d.ts +52 -0
  28. package/dist/proxy/orchestrator.js +100 -0
  29. package/dist/proxy/proxy-core.d.ts +14 -26
  30. package/dist/proxy/proxy-core.js +40 -337
  31. package/dist/proxy/proxy-handler.d.ts +18 -0
  32. package/dist/proxy/proxy-handler.js +223 -0
  33. package/dist/proxy/proxy-logging.d.ts +28 -0
  34. package/dist/proxy/proxy-logging.js +122 -0
  35. package/dist/proxy/resilience.d.ts +63 -0
  36. package/dist/proxy/resilience.js +188 -0
  37. package/dist/proxy/scope.d.ts +18 -0
  38. package/dist/proxy/scope.js +37 -0
  39. package/dist/proxy/semaphore.d.ts +9 -2
  40. package/dist/proxy/semaphore.js +34 -7
  41. package/dist/proxy/stream-proxy.d.ts +7 -0
  42. package/dist/proxy/stream-proxy.js +263 -0
  43. package/dist/proxy/{upstream-call.d.ts → transport.d.ts} +25 -18
  44. package/dist/proxy/transport.js +128 -0
  45. package/dist/proxy/types.d.ts +58 -0
  46. package/dist/proxy/types.js +30 -0
  47. package/frontend-dist/assets/{CardContent-CucI6u41.js → CardContent-CTnwqTdL.js} +1 -1
  48. package/frontend-dist/assets/{CardHeader-d-DYsWxe.js → CardHeader-CfUeY7tk.js} +1 -1
  49. package/frontend-dist/assets/{CardTitle-CIDEQkWB.js → CardTitle-CWiDwWqd.js} +1 -1
  50. package/frontend-dist/assets/{Checkbox-CybCw3zS.js → Checkbox-BxNz70R_.js} +1 -1
  51. package/frontend-dist/assets/{CollapsibleTrigger-BFNhb19_.js → CollapsibleTrigger-Uz1aGdtH.js} +1 -1
  52. package/frontend-dist/assets/{Collection-DUBb4r6h.js → Collection-1EHC87X5.js} +1 -1
  53. package/frontend-dist/assets/{Dashboard-DLB6iqH1.js → Dashboard-C3FL30UN.js} +2 -2
  54. package/frontend-dist/assets/{DialogTitle-Dq-5o7nJ.js → DialogTitle-CAOFxr83.js} +1 -1
  55. package/frontend-dist/assets/{Input-HN3Il0-c.js → Input-DRIid2C6.js} +1 -1
  56. package/frontend-dist/assets/{Label-CXAeFn-r.js → Label-UyNN2jyE.js} +1 -1
  57. package/frontend-dist/assets/LogDetailDialog-8BT4vIlV.js +3 -0
  58. package/frontend-dist/assets/{Login-Br3qsdxf.js → Login-CnzH6TdS.js} +1 -1
  59. package/frontend-dist/assets/Logs-CbK8NB_X.js +1 -0
  60. package/frontend-dist/assets/{ModelMappings-DXC0sNH5.js → ModelMappings-DeRFgsYG.js} +1 -1
  61. package/frontend-dist/assets/Monitor-Dd80bdUn.js +1 -0
  62. package/frontend-dist/assets/{PopperContent-CnZejY31.js → PopperContent-B3fZao7v.js} +1 -1
  63. package/frontend-dist/assets/{Providers-8CHhW4uH.js → Providers-B_DbV-_y.js} +1 -1
  64. package/frontend-dist/assets/ProxyEnhancement-up1fnPzq.js +5 -0
  65. package/frontend-dist/assets/RetryRules-Dkuhjh0u.js +1 -0
  66. package/frontend-dist/assets/RouterKeys-CvMMAa4t.js +1 -0
  67. package/frontend-dist/assets/{RovingFocusItem-B7ZIkplZ.js → RovingFocusItem-X0bfqWWS.js} +1 -1
  68. package/frontend-dist/assets/{SelectValue-B32pgmTJ.js → SelectValue-zO8t-tx1.js} +1 -1
  69. package/frontend-dist/assets/{Setup-Df9IQo2x.js → Setup-ByT2ThOQ.js} +1 -1
  70. package/frontend-dist/assets/{Switch-CLeo7H6d.js → Switch-BEMjVugO.js} +1 -1
  71. package/frontend-dist/assets/{TableHeader-BpscAtT3.js → TableHeader-DpHWSnxK.js} +1 -1
  72. package/frontend-dist/assets/{TabsTrigger-DErAbTuM.js → TabsTrigger-Db6RqsZc.js} +1 -1
  73. package/frontend-dist/assets/{VisuallyHidden-CJBR3YB3.js → VisuallyHidden-hs8pj8OP.js} +1 -1
  74. package/frontend-dist/assets/{VisuallyHiddenInput-Cy0VuE1l.js → VisuallyHiddenInput-1m0nNADN.js} +1 -1
  75. package/frontend-dist/assets/{alert-dialog-BAR1JRmT.js → alert-dialog-PP91kaO8.js} +1 -1
  76. package/frontend-dist/assets/{button-D54q76GQ.js → button-Dcc0gF5i.js} +1 -1
  77. package/frontend-dist/assets/{client-Mb8fy_bC.js → client-DIIo9zPK.js} +2 -2
  78. package/frontend-dist/assets/{createLucideIcon-CCmQ9QKM.js → createLucideIcon-DGZkBjcJ.js} +1 -1
  79. package/frontend-dist/assets/{dialog-DSH5k5Kj.js → dialog-CxSyR-fN.js} +1 -1
  80. package/frontend-dist/assets/format-CPdJtjZ5.js +1 -0
  81. package/frontend-dist/assets/index-BL-LAtac.css +1 -0
  82. package/frontend-dist/assets/{index-BQBtSfem.js → index-CvT41fGL.js} +1 -1
  83. package/frontend-dist/assets/{lib-BgOqOzXI.js → lib-Bl0OuBjh.js} +1 -1
  84. package/frontend-dist/assets/{ohash.D__AXeF1-p4vp6Svt.js → ohash.D__AXeF1-B64hB831.js} +1 -1
  85. package/frontend-dist/assets/{useClipboard-DO-38TXr.js → useClipboard-CWc1cTDo.js} +1 -1
  86. package/frontend-dist/assets/{useForwardExpose-CzQFheaD.js → useForwardExpose-AkE0lq8y.js} +1 -1
  87. package/frontend-dist/assets/useNonce-DGyPxdjq.js +1 -0
  88. package/frontend-dist/assets/x-BuUpx9Fr.js +1 -0
  89. package/frontend-dist/index.html +7 -7
  90. package/package.json +1 -1
  91. package/dist/admin/services.d.ts +0 -7
  92. package/dist/admin/services.js +0 -63
  93. package/dist/proxy/retry.d.ts +0 -43
  94. package/dist/proxy/retry.js +0 -121
  95. package/dist/proxy/upstream-call.js +0 -208
  96. package/frontend-dist/assets/LogResponseViewer-CyBzv02a.js +0 -3
  97. package/frontend-dist/assets/Logs-Cu_IftdS.js +0 -1
  98. package/frontend-dist/assets/Monitor-CKlid1sC.js +0 -1
  99. package/frontend-dist/assets/ProxyEnhancement-CkYeXwgH.js +0 -5
  100. package/frontend-dist/assets/RetryRules-Csb7u9W4.js +0 -1
  101. package/frontend-dist/assets/RouterKeys-C6YIufmj.js +0 -1
  102. package/frontend-dist/assets/index-H-lnTkMr.css +0 -1
  103. package/frontend-dist/assets/useNonce-CU-NirfM.js +0 -1
  104. package/frontend-dist/assets/x-DEJ1xpi5.js +0 -1
@@ -0,0 +1,263 @@
1
+ import { PassThrough } from "stream";
2
+ import { UPSTREAM_SUCCESS, filterHeaders } from "./types.js";
3
+ import { _transportInternals, buildRequestOptions, } from "./transport.js";
4
+ const UPSTREAM_BAD_GATEWAY = 502;
5
+ const BUFFER_SIZE_LIMIT = 4096;
6
+ class StreamProxy {
7
+ statusCode;
8
+ sentUpstreamHeaders;
9
+ reply;
10
+ metricsTransform;
11
+ checkEarlyError;
12
+ timeoutMs;
13
+ state = "BUFFERING";
14
+ resolved = false;
15
+ resolveFn = null;
16
+ pendingResult = null;
17
+ bufferChunks = [];
18
+ captureChunks = [];
19
+ idleTimer = null;
20
+ headersSent = false;
21
+ closeHandlerRegistered = false;
22
+ sseHeaders;
23
+ passThrough = new PassThrough();
24
+ pipeEntry;
25
+ constructor(statusCode, rawUpstreamHeaders, sentUpstreamHeaders, reply, metricsTransform, checkEarlyError, timeoutMs) {
26
+ this.statusCode = statusCode;
27
+ this.sentUpstreamHeaders = sentUpstreamHeaders;
28
+ this.reply = reply;
29
+ this.metricsTransform = metricsTransform;
30
+ this.checkEarlyError = checkEarlyError;
31
+ this.timeoutMs = timeoutMs;
32
+ this.sseHeaders = filterHeaders(rawUpstreamHeaders);
33
+ this.sseHeaders["Content-Type"] = "text/event-stream";
34
+ this.sseHeaders["Cache-Control"] = "no-cache";
35
+ this.sseHeaders["Connection"] = "keep-alive";
36
+ this.pipeEntry = metricsTransform ?? this.passThrough;
37
+ }
38
+ bindResolve(resolve) {
39
+ this.resolveFn = resolve;
40
+ if (this.pendingResult)
41
+ resolve(this.pendingResult);
42
+ }
43
+ transition(newState) {
44
+ const VALID = {
45
+ BUFFERING: ["STREAMING", "EARLY_ERROR"],
46
+ STREAMING: ["COMPLETED", "ABORTED"],
47
+ COMPLETED: [],
48
+ EARLY_ERROR: [],
49
+ ABORTED: [],
50
+ };
51
+ if (!VALID[this.state].includes(newState)) {
52
+ throw new Error(`Invalid state transition: ${this.state} → ${newState}`);
53
+ }
54
+ this.state = newState;
55
+ }
56
+ terminal(kind, extra = {}, deferred = false) {
57
+ if (this.resolved)
58
+ return;
59
+ this.resolved = true;
60
+ const base = {
61
+ statusCode: this.statusCode,
62
+ upstreamResponseHeaders: this.sseHeaders,
63
+ sentHeaders: this.sentUpstreamHeaders,
64
+ };
65
+ let result;
66
+ switch (kind) {
67
+ case "stream_success":
68
+ result = { kind: "stream_success", ...base, metrics: extra.metrics };
69
+ break;
70
+ case "stream_error":
71
+ result = { kind: "stream_error", ...base, body: extra.body, headers: this.sseHeaders };
72
+ break;
73
+ case "stream_abort":
74
+ result = { kind: "stream_abort", ...base, metrics: extra.metrics };
75
+ break;
76
+ }
77
+ // deferred 模式:先 resolve 让 handler 链路(日志写入等)在 microtask 中执行,
78
+ // cleanup 由调用方在 setImmediate(macrotask)中处理。
79
+ if (deferred) {
80
+ if (this.resolveFn) {
81
+ this.resolveFn(result);
82
+ }
83
+ else {
84
+ this.pendingResult = result;
85
+ }
86
+ }
87
+ else {
88
+ this.cleanup();
89
+ if (this.resolveFn) {
90
+ this.resolveFn(result);
91
+ }
92
+ else {
93
+ this.pendingResult = result;
94
+ }
95
+ }
96
+ }
97
+ cleanup() {
98
+ if (this.idleTimer)
99
+ clearTimeout(this.idleTimer);
100
+ this.idleTimer = null;
101
+ if (!this.passThrough.destroyed)
102
+ this.passThrough.destroy();
103
+ if (this.metricsTransform && !this.metricsTransform.destroyed)
104
+ this.metricsTransform.destroy();
105
+ }
106
+ collectMetrics(isComplete) {
107
+ if (!this.metricsTransform)
108
+ return undefined;
109
+ const result = this.metricsTransform.getExtractor().getMetrics();
110
+ return isComplete ? result : { ...result, is_complete: 0 };
111
+ }
112
+ resetIdleTimer() {
113
+ if (this.idleTimer)
114
+ clearTimeout(this.idleTimer);
115
+ this.idleTimer = setTimeout(() => {
116
+ if (this.resolved)
117
+ return;
118
+ this.terminal("stream_abort", { metrics: this.collectMetrics(false) });
119
+ }, this.timeoutMs);
120
+ }
121
+ startStreaming() {
122
+ if (this.headersSent)
123
+ return;
124
+ this.transition("STREAMING");
125
+ this.headersSent = true;
126
+ this.reply.raw.writeHead(this.statusCode, this.sseHeaders);
127
+ if (this.metricsTransform) {
128
+ this.metricsTransform.pipe(this.passThrough, { end: true });
129
+ }
130
+ // 手动转发而非 pipe,避免 Node.js 在 dest 上自动注册 close/finish handler
131
+ this.passThrough.on("data", (chunk) => {
132
+ this.reply.raw.write(chunk);
133
+ });
134
+ // 不在 passThrough end 事件中调用 reply.raw.end(),
135
+ // 因为 onEnd() 统一管理响应结束时机,确保日志在 reply end 之前写入
136
+ for (const c of this.bufferChunks)
137
+ this.pipeEntry.write(c);
138
+ this.bufferChunks.length = 0;
139
+ }
140
+ registerCloseHandler() {
141
+ if (this.closeHandlerRegistered)
142
+ return;
143
+ this.closeHandlerRegistered = true;
144
+ this.reply.raw.on("close", () => {
145
+ if (this.resolved)
146
+ return;
147
+ if (this.state === "BUFFERING" || this.state === "STREAMING") {
148
+ this.transition("ABORTED");
149
+ }
150
+ this.terminal("stream_abort", { metrics: this.collectMetrics(false) });
151
+ });
152
+ }
153
+ onData(chunk) {
154
+ if (this.resolved)
155
+ return;
156
+ this.resetIdleTimer();
157
+ this.captureChunks.push(chunk);
158
+ if (this.state === "BUFFERING") {
159
+ this.bufferChunks.push(chunk);
160
+ const buf = Buffer.concat(this.bufferChunks);
161
+ const text = buf.toString("utf-8");
162
+ if (text.includes("\n\n")) {
163
+ if (this.checkEarlyError?.(text)) {
164
+ this.transition("EARLY_ERROR");
165
+ this.terminal("stream_error", { body: text });
166
+ return;
167
+ }
168
+ this.startStreaming();
169
+ }
170
+ else if (buf.length >= BUFFER_SIZE_LIMIT) {
171
+ this.startStreaming();
172
+ }
173
+ return;
174
+ }
175
+ this.pipeEntry.write(chunk);
176
+ }
177
+ onEnd() {
178
+ if (this.resolved)
179
+ return;
180
+ if (this.idleTimer)
181
+ clearTimeout(this.idleTimer);
182
+ if (this.state === "BUFFERING" && this.checkEarlyError) {
183
+ const text = Buffer.concat(this.captureChunks).toString("utf-8");
184
+ if (this.checkEarlyError(text)) {
185
+ this.transition("EARLY_ERROR");
186
+ this.terminal("stream_error", { body: text });
187
+ return;
188
+ }
189
+ this.startStreaming();
190
+ }
191
+ if (this.state === "STREAMING") {
192
+ this.transition("COMPLETED");
193
+ }
194
+ // 通过 terminal 的 deferred 模式统一 resolve:
195
+ // 先 resolve Promise,让 handler 链路(日志写入等)在 microtask 中执行。
196
+ // reply.raw.end() 延迟到 setImmediate(macrotask),确保 microtask 先完成。
197
+ // light-my-request 监听 reply.raw 的 end 事件判定响应完成,
198
+ // 这保证了 inject() 返回时日志已经写入 DB。
199
+ const metrics = this.collectMetrics(true);
200
+ this.terminal("stream_success", { metrics }, true);
201
+ // 延迟结束管道和响应,属于 reply 层面操作,不属于 StreamProxy 状态管理
202
+ setImmediate(() => {
203
+ this.pipeEntry.end();
204
+ if (this.headersSent)
205
+ this.reply.raw.end();
206
+ this.cleanup();
207
+ });
208
+ }
209
+ onUpstreamError(err) {
210
+ if (this.resolved)
211
+ return;
212
+ this.resolved = true;
213
+ this.cleanup();
214
+ const result = { kind: "throw", error: err };
215
+ if (this.resolveFn) {
216
+ this.resolveFn(result);
217
+ }
218
+ else {
219
+ this.pendingResult = result;
220
+ }
221
+ }
222
+ }
223
+ // ---------- callStream ----------
224
+ export function callStream(backend, apiKey, body, clientHeaders, reply, timeoutMs, upstreamPath, buildHeaders, metricsTransform, checkEarlyError, compatResolve) {
225
+ return new Promise((resolve) => {
226
+ const effectiveResolve = compatResolve ?? resolve;
227
+ const url = new URL(`${backend.base_url}${upstreamPath}`);
228
+ const payload = JSON.stringify(body);
229
+ const upstreamHeaders = buildHeaders(clientHeaders, apiKey, Buffer.byteLength(payload));
230
+ const options = buildRequestOptions(url, upstreamHeaders);
231
+ const upstreamReq = _transportInternals.createUpstreamRequest(url, options);
232
+ upstreamReq.on("response", (upstreamRes) => {
233
+ const statusCode = upstreamRes.statusCode || UPSTREAM_BAD_GATEWAY;
234
+ if (statusCode !== UPSTREAM_SUCCESS) {
235
+ const chunks = [];
236
+ upstreamRes.on("data", (chunk) => chunks.push(chunk));
237
+ upstreamRes.on("end", () => {
238
+ effectiveResolve({
239
+ kind: "stream_error",
240
+ statusCode,
241
+ body: Buffer.concat(chunks).toString("utf-8"),
242
+ headers: filterHeaders(upstreamRes.headers),
243
+ sentHeaders: upstreamHeaders,
244
+ });
245
+ });
246
+ return;
247
+ }
248
+ const proxy = new StreamProxy(statusCode, upstreamRes.headers, upstreamHeaders, reply, metricsTransform, checkEarlyError, timeoutMs);
249
+ proxy.bindResolve(effectiveResolve);
250
+ proxy.registerCloseHandler();
251
+ // 无 early error checker 时直接开始流式传输
252
+ if (!checkEarlyError)
253
+ proxy.startStreaming();
254
+ proxy.resetIdleTimer();
255
+ upstreamRes.on("data", (chunk) => proxy.onData(chunk));
256
+ upstreamRes.on("end", () => proxy.onEnd());
257
+ upstreamRes.on("error", (err) => proxy.onUpstreamError(err));
258
+ });
259
+ upstreamReq.on("error", (error) => effectiveResolve({ kind: "throw", error }));
260
+ upstreamReq.write(payload);
261
+ upstreamReq.end();
262
+ });
263
+ }
@@ -1,7 +1,8 @@
1
1
  import type { FastifyReply } from "fastify";
2
- import type { RawHeaders } from "./proxy-core.js";
2
+ import type { RawHeaders, TransportResult } from "./types.js";
3
3
  import type { MetricsResult } from "../metrics/metrics-extractor.js";
4
- import { SSEMetricsTransform } from "../metrics/sse-metrics-transform.js";
4
+ import type { SSEMetricsTransform } from "../metrics/sse-metrics-transform.js";
5
+ export { callStream } from "./stream-proxy.js";
5
6
  export interface UpstreamRequestOptions {
6
7
  hostname: string;
7
8
  port: number;
@@ -9,6 +10,23 @@ export interface UpstreamRequestOptions {
9
10
  method: string;
10
11
  headers: Record<string, string>;
11
12
  }
13
+ export declare const _transportInternals: {
14
+ createUpstreamRequest(url: URL, options: UpstreamRequestOptions): import("http").ClientRequest;
15
+ };
16
+ export declare function createUpstreamRequest(url: URL, options: UpstreamRequestOptions): import("http").ClientRequest;
17
+ export declare function buildRequestOptions(url: URL, headers: Record<string, string>, method?: string): UpstreamRequestOptions;
18
+ export type BuildHeadersFn = (cliHdrs: RawHeaders, key: string, bytes?: number) => Record<string, string>;
19
+ export declare function callNonStream(backend: {
20
+ base_url: string;
21
+ }, apiKey: string, body: Record<string, unknown>, clientHeaders: RawHeaders, upstreamPath: string, buildHeaders: BuildHeadersFn): Promise<TransportResult>;
22
+ export interface GetTransportResult {
23
+ statusCode: number;
24
+ body: string;
25
+ headers: Record<string, string>;
26
+ }
27
+ export declare function callGet(backend: {
28
+ base_url: string;
29
+ }, apiKey: string, clientHeaders: RawHeaders, upstreamPath: string, buildHeaders: (cliHdrs: RawHeaders, key: string) => Record<string, string>): Promise<GetTransportResult>;
12
30
  export interface ProxyResult {
13
31
  statusCode: number;
14
32
  body: string;
@@ -22,22 +40,11 @@ export interface StreamProxyResult {
22
40
  upstreamResponseHeaders?: Record<string, string>;
23
41
  sentHeaders?: Record<string, string>;
24
42
  metricsResult?: MetricsResult;
43
+ abnormalClose?: boolean;
25
44
  }
26
- export interface GetProxyResult {
27
- statusCode: number;
28
- body: string;
29
- headers: Record<string, string>;
30
- }
31
- /** 根据 URL scheme 选择 http 或 https 模块 */
32
- export declare function createUpstreamRequest(url: URL, options: UpstreamRequestOptions): import("http").ClientRequest;
33
- /** 从 URL + headers 构造 Node.js http.request 所需的 options */
34
- export declare function buildRequestOptions(url: URL, headers: Record<string, string>, method?: string): UpstreamRequestOptions;
35
- export declare function proxyNonStream(backend: {
36
- base_url: string;
37
- }, apiKey: string, body: Record<string, unknown>, clientHeaders: RawHeaders, upstreamPath: string, buildHeaders: (cliHdrs: RawHeaders, key: string, bytes?: number) => Record<string, string>): Promise<ProxyResult>;
38
- export declare function proxyStream(backend: {
45
+ export declare function proxyNonStreamCompat(backend: {
39
46
  base_url: string;
40
- }, apiKey: string, body: Record<string, unknown>, clientHeaders: RawHeaders, reply: FastifyReply, timeoutMs: number, upstreamPath: string, buildHeaders: (cliHdrs: RawHeaders, key: string, bytes?: number) => Record<string, string>, metricsTransform?: SSEMetricsTransform): Promise<StreamProxyResult>;
41
- export declare function proxyGetRequest(backend: {
47
+ }, apiKey: string, body: Record<string, unknown>, clientHeaders: RawHeaders, upstreamPath: string, buildHeaders: BuildHeadersFn): Promise<ProxyResult>;
48
+ export declare function proxyStreamCompat(backend: {
42
49
  base_url: string;
43
- }, apiKey: string, clientHeaders: RawHeaders, upstreamPath: string, buildHeaders: (cliHdrs: RawHeaders, key: string) => Record<string, string>): Promise<GetProxyResult>;
50
+ }, apiKey: string, body: Record<string, unknown>, clientHeaders: RawHeaders, reply: FastifyReply, timeoutMs: number, upstreamPath: string, buildHeaders: BuildHeadersFn, metricsTransform?: SSEMetricsTransform, checkEarlyError?: (bufferedData: string) => boolean): Promise<StreamProxyResult>;
@@ -0,0 +1,128 @@
1
+ import { request as httpRequestFn } from "http";
2
+ import { request as httpsRequestFn } from "https";
3
+ import { UPSTREAM_SUCCESS, filterHeaders } from "./types.js";
4
+ import { callStream } from "./stream-proxy.js";
5
+ // Re-export callStream from stream-proxy.ts for external consumers
6
+ export { callStream } from "./stream-proxy.js";
7
+ // ---------- Constants ----------
8
+ const UPSTREAM_BAD_GATEWAY = 502;
9
+ const UPSTREAM_SUCCESS_RANGE = 100;
10
+ const HTTPS_DEFAULT_PORT = 443;
11
+ const HTTP_DEFAULT_PORT = 80;
12
+ export const _transportInternals = {
13
+ createUpstreamRequest(url, options) {
14
+ return url.protocol === "https:"
15
+ ? httpsRequestFn(options)
16
+ : httpRequestFn(options);
17
+ },
18
+ };
19
+ export function createUpstreamRequest(url, options) {
20
+ return _transportInternals.createUpstreamRequest(url, options);
21
+ }
22
+ export function buildRequestOptions(url, headers, method = "POST") {
23
+ return {
24
+ hostname: url.hostname,
25
+ port: Number(url.port) ||
26
+ (url.protocol === "https:" ? HTTPS_DEFAULT_PORT : HTTP_DEFAULT_PORT),
27
+ path: url.pathname,
28
+ method,
29
+ headers,
30
+ };
31
+ }
32
+ // ---------- callNonStream ----------
33
+ export function callNonStream(backend, apiKey, body, clientHeaders, upstreamPath, buildHeaders) {
34
+ return new Promise((resolve) => {
35
+ const url = new URL(`${backend.base_url}${upstreamPath}`);
36
+ const payload = JSON.stringify(body);
37
+ const upstreamHeaders = buildHeaders(clientHeaders, apiKey, Buffer.byteLength(payload));
38
+ const options = buildRequestOptions(url, upstreamHeaders);
39
+ const req = _transportInternals.createUpstreamRequest(url, options);
40
+ req.on("response", (res) => {
41
+ const chunks = [];
42
+ res.on("data", (chunk) => chunks.push(chunk));
43
+ res.on("end", () => {
44
+ const statusCode = res.statusCode || UPSTREAM_BAD_GATEWAY;
45
+ const responseBody = Buffer.concat(chunks).toString("utf-8");
46
+ const headers = filterHeaders(res.headers);
47
+ if (statusCode >= UPSTREAM_SUCCESS && statusCode < UPSTREAM_SUCCESS + UPSTREAM_SUCCESS_RANGE) {
48
+ resolve({
49
+ kind: "success",
50
+ statusCode,
51
+ body: responseBody,
52
+ headers,
53
+ sentHeaders: upstreamHeaders,
54
+ sentBody: payload,
55
+ });
56
+ }
57
+ else {
58
+ resolve({
59
+ kind: "error",
60
+ statusCode,
61
+ body: responseBody,
62
+ headers,
63
+ sentHeaders: upstreamHeaders,
64
+ sentBody: payload,
65
+ });
66
+ }
67
+ });
68
+ });
69
+ req.on("error", (error) => resolve({ kind: "throw", error }));
70
+ req.write(payload);
71
+ req.end();
72
+ });
73
+ }
74
+ export function callGet(backend, apiKey, clientHeaders, upstreamPath, buildHeaders) {
75
+ return new Promise((resolve, reject) => {
76
+ const url = new URL(`${backend.base_url}${upstreamPath}`);
77
+ const headers = buildHeaders(clientHeaders, apiKey);
78
+ const options = buildRequestOptions(url, headers, "GET");
79
+ const req = _transportInternals.createUpstreamRequest(url, options);
80
+ req.on("response", (res) => {
81
+ const chunks = [];
82
+ res.on("data", (chunk) => chunks.push(chunk));
83
+ res.on("end", () => {
84
+ resolve({
85
+ statusCode: res.statusCode || UPSTREAM_BAD_GATEWAY,
86
+ body: Buffer.concat(chunks).toString("utf-8"),
87
+ headers: filterHeaders(res.headers),
88
+ });
89
+ });
90
+ });
91
+ req.on("error", (err) => reject(err));
92
+ req.end();
93
+ });
94
+ }
95
+ export function proxyNonStreamCompat(backend, apiKey, body, clientHeaders, upstreamPath, buildHeaders) {
96
+ return callNonStream(backend, apiKey, body, clientHeaders, upstreamPath, buildHeaders)
97
+ .then((r) => {
98
+ if (r.kind === "throw")
99
+ throw r.error;
100
+ return {
101
+ statusCode: r.statusCode,
102
+ body: "body" in r ? r.body : "",
103
+ headers: "headers" in r ? r.headers : {},
104
+ sentHeaders: r.sentHeaders,
105
+ sentBody: "sentBody" in r ? r.sentBody : "",
106
+ };
107
+ });
108
+ }
109
+ export function proxyStreamCompat(backend, apiKey, body, clientHeaders, reply, timeoutMs, upstreamPath, buildHeaders, metricsTransform, checkEarlyError) {
110
+ return new Promise((resolve, reject) => {
111
+ function onResult(r) {
112
+ if (r.kind === "throw") {
113
+ reject(r.error);
114
+ return;
115
+ }
116
+ const metrics = (r.kind === "stream_success" || r.kind === "stream_abort") ? r.metrics : undefined;
117
+ resolve({
118
+ statusCode: r.statusCode,
119
+ responseBody: r.kind === "stream_success" ? undefined : ("body" in r ? r.body : undefined),
120
+ upstreamResponseHeaders: ("upstreamResponseHeaders" in r ? r.upstreamResponseHeaders : undefined) ?? ("headers" in r ? r.headers : {}) ?? {},
121
+ sentHeaders: r.sentHeaders,
122
+ metricsResult: metrics ?? undefined,
123
+ abnormalClose: r.kind === "stream_abort",
124
+ });
125
+ }
126
+ callStream(backend, apiKey, body, clientHeaders, reply, timeoutMs, upstreamPath, buildHeaders, metricsTransform, checkEarlyError, onResult);
127
+ });
128
+ }
@@ -0,0 +1,58 @@
1
+ import type { MetricsResult } from "../metrics/metrics-extractor.js";
2
+ export declare const UPSTREAM_SUCCESS = 200;
3
+ export type RawHeaders = Record<string, string | string[] | undefined>;
4
+ export declare function filterHeaders(raw: RawHeaders): Record<string, string>;
5
+ /**
6
+ * 上游调用的传输层结果。
7
+ * discriminated union,按 kind 区分 6 种情况:
8
+ * - success / stream_success:正常完成
9
+ * - stream_error / stream_abort:流式传输中途异常
10
+ * - error:非流式错误响应
11
+ * - throw:未捕获异常
12
+ */
13
+ export type TransportResult = {
14
+ kind: "success";
15
+ statusCode: number;
16
+ body: string;
17
+ headers: Record<string, string>;
18
+ sentHeaders: Record<string, string>;
19
+ sentBody: string;
20
+ } | {
21
+ kind: "stream_success";
22
+ statusCode: number;
23
+ metrics?: MetricsResult;
24
+ upstreamResponseHeaders?: Record<string, string>;
25
+ sentHeaders: Record<string, string>;
26
+ } | {
27
+ kind: "stream_error";
28
+ statusCode: number;
29
+ body: string;
30
+ headers: Record<string, string>;
31
+ sentHeaders: Record<string, string>;
32
+ } | {
33
+ kind: "stream_abort";
34
+ statusCode: number;
35
+ metrics?: MetricsResult;
36
+ upstreamResponseHeaders?: Record<string, string>;
37
+ sentHeaders: Record<string, string>;
38
+ } | {
39
+ kind: "error";
40
+ statusCode: number;
41
+ body: string;
42
+ headers: Record<string, string>;
43
+ sentHeaders: Record<string, string>;
44
+ sentBody: string;
45
+ } | {
46
+ kind: "throw";
47
+ error: Error;
48
+ };
49
+ /**
50
+ * 跨 provider failover 时由 ResilienceLayer 抛出,
51
+ * orchestrator 捕获后释放当前信号量并获取新 provider 的信号量。
52
+ */
53
+ export declare class ProviderSwitchNeeded extends Error {
54
+ readonly targetProviderId: string;
55
+ constructor(targetProviderId: string);
56
+ }
57
+ /** 流式传输阶段状态 */
58
+ export type StreamState = "BUFFERING" | "STREAMING" | "COMPLETED" | "EARLY_ERROR" | "ABORTED";
@@ -0,0 +1,30 @@
1
+ // ---------- Shared constants & types ----------
2
+ export const UPSTREAM_SUCCESS = 200;
3
+ /** 过滤掉不应转发给下游的 hop-by-hop headers */
4
+ const SKIP_DOWNSTREAM = new Set([
5
+ "content-length",
6
+ "transfer-encoding",
7
+ "connection",
8
+ "keep-alive",
9
+ ]);
10
+ export function filterHeaders(raw) {
11
+ const out = {};
12
+ for (const [key, value] of Object.entries(raw)) {
13
+ if (value == null || SKIP_DOWNSTREAM.has(key.toLowerCase()))
14
+ continue;
15
+ out[key] = Array.isArray(value) ? value.join(", ") : value;
16
+ }
17
+ return out;
18
+ }
19
+ /**
20
+ * 跨 provider failover 时由 ResilienceLayer 抛出,
21
+ * orchestrator 捕获后释放当前信号量并获取新 provider 的信号量。
22
+ */
23
+ export class ProviderSwitchNeeded extends Error {
24
+ targetProviderId;
25
+ constructor(targetProviderId) {
26
+ super(`Provider switch needed: ${targetProviderId}`);
27
+ this.targetProviderId = targetProviderId;
28
+ this.name = "ProviderSwitchNeeded";
29
+ }
30
+ }
@@ -1 +1 @@
1
- import{F as e,R as t,S as n,gt as r,mt as i,y as a}from"./client-Mb8fy_bC.js";import{r as o}from"./button-D54q76GQ.js";var s=[`data-size`],c=n({__name:`Card`,props:{class:{type:[Boolean,null,String,Object,Array]},size:{default:`default`}},setup(n){let c=n;return(l,u)=>(e(),a(`div`,{"data-slot":`card`,"data-size":n.size,class:r(i(o)(`ring-foreground/10 bg-card text-card-foreground gap-4 overflow-hidden rounded-lg py-4 text-sm ring-1 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-lg *:[img:last-child]:rounded-b-lg group/card flex flex-col`,c.class))},[t(l.$slots,`default`)],10,s))}}),l=n({__name:`CardContent`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(n){let s=n;return(n,c)=>(e(),a(`div`,{"data-slot":`card-content`,class:r(i(o)(`px-4 group-data-[size=sm]/card:px-3`,s.class))},[t(n.$slots,`default`)],2))}});export{c as n,l as t};
1
+ import{F as e,R as t,S as n,gt as r,mt as i,y as a}from"./client-DIIo9zPK.js";import{r as o}from"./button-Dcc0gF5i.js";var s=[`data-size`],c=n({__name:`Card`,props:{class:{type:[Boolean,null,String,Object,Array]},size:{default:`default`}},setup(n){let c=n;return(l,u)=>(e(),a(`div`,{"data-slot":`card`,"data-size":n.size,class:r(i(o)(`ring-foreground/10 bg-card text-card-foreground gap-4 overflow-hidden rounded-lg py-4 text-sm ring-1 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-lg *:[img:last-child]:rounded-b-lg group/card flex flex-col`,c.class))},[t(l.$slots,`default`)],10,s))}}),l=n({__name:`CardContent`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(n){let s=n;return(n,c)=>(e(),a(`div`,{"data-slot":`card-content`,class:r(i(o)(`px-4 group-data-[size=sm]/card:px-3`,s.class))},[t(n.$slots,`default`)],2))}});export{c as n,l as t};
@@ -1 +1 @@
1
- import{F as e,R as t,S as n,gt as r,mt as i,y as a}from"./client-Mb8fy_bC.js";import{r as o}from"./button-D54q76GQ.js";var s=n({__name:`CardHeader`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(n){let s=n;return(n,c)=>(e(),a(`div`,{"data-slot":`card-header`,class:r(i(o)(`gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]`,s.class))},[t(n.$slots,`default`)],2))}});export{s as t};
1
+ import{F as e,R as t,S as n,gt as r,mt as i,y as a}from"./client-DIIo9zPK.js";import{r as o}from"./button-Dcc0gF5i.js";var s=n({__name:`CardHeader`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(n){let s=n;return(n,c)=>(e(),a(`div`,{"data-slot":`card-header`,class:r(i(o)(`gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]`,s.class))},[t(n.$slots,`default`)],2))}});export{s as t};
@@ -1 +1 @@
1
- import{F as e,R as t,S as n,gt as r,mt as i,y as a}from"./client-Mb8fy_bC.js";import{r as o}from"./button-D54q76GQ.js";var s=n({__name:`CardTitle`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(n){let s=n;return(n,c)=>(e(),a(`div`,{"data-slot":`card-title`,class:r(i(o)(`text-base leading-snug font-medium group-data-[size=sm]/card:text-sm cn-font-heading`,s.class))},[t(n.$slots,`default`)],2))}});export{s as t};
1
+ import{F as e,R as t,S as n,gt as r,mt as i,y as a}from"./client-DIIo9zPK.js";import{r as o}from"./button-Dcc0gF5i.js";var s=n({__name:`CardTitle`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(n){let s=n;return(n,c)=>(e(),a(`div`,{"data-slot":`card-title`,class:r(i(o)(`text-base leading-snug font-medium group-data-[size=sm]/card:text-sm cn-font-heading`,s.class))},[t(n.$slots,`default`)],2))}});export{s as t};
@@ -1 +1 @@
1
- import{B as e,F as t,R as n,S as r,Y as i,_ as a,_t as o,h as s,k as c,l,mt as u,u as d,v as f,w as p,x as m}from"./client-Mb8fy_bC.js";import{n as h,t as g}from"./ohash.D__AXeF1-p4vp6Svt.js";import{b as _,i as v,p as y,r as b}from"./button-D54q76GQ.js";import{S as x,c as S,f as C,y as w}from"./Collection-DUBb4r6h.js";import{n as T}from"./VisuallyHidden-CJBR3YB3.js";import{t as E}from"./useForwardExpose-CzQFheaD.js";import{t as D}from"./VisuallyHiddenInput-Cy0VuE1l.js";import{t as O}from"./RovingFocusItem-B7ZIkplZ.js";function k(e,t){return w(e)?!1:Array.isArray(e)?e.some(e=>g(e,t)):g(e,t)}var[A,j]=x(`CheckboxGroupRoot`);function M(e){return e===`indeterminate`}function N(e){return M(e)?`indeterminate`:e?`checked`:`unchecked`}var[P,F]=x(`CheckboxRoot`),I=r({inheritAttrs:!1,__name:`CheckboxRoot`,props:{defaultValue:{type:null,required:!1},modelValue:{type:null,required:!1,default:void 0},disabled:{type:Boolean,required:!1},value:{type:null,required:!1,default:`on`},id:{type:String,required:!1},trueValue:{type:null,required:!1,default:()=>!0},falseValue:{type:null,required:!1,default:()=>!1},asChild:{type:Boolean,required:!1},as:{type:null,required:!1,default:`button`},name:{type:String,required:!1},required:{type:Boolean,required:!1}},emits:[`update:modelValue`],setup(r,{emit:o}){let p=r,m=o,{forwardRef:h,currentElement:_}=E(),b=A(null),x=y(p,`modelValue`,m,{defaultValue:p.defaultValue??p.falseValue,passive:p.modelValue===void 0}),S=s(()=>b?.disabled.value||p.disabled),C=s(()=>g(x.value,p.trueValue)),j=s(()=>w(b?.modelValue.value)?x.value===`indeterminate`?`indeterminate`:C.value:k(b.modelValue.value,p.value));function P(){if(w(b?.modelValue.value))x.value===`indeterminate`?x.value=p.trueValue:x.value=C.value?p.falseValue:p.trueValue;else{let e=[...b.modelValue.value||[]];if(k(e,p.value)){let t=e.findIndex(e=>g(e,p.value));e.splice(t,1)}else e.push(p.value);b.modelValue.value=e}}let I=T(_),L=s(()=>p.id&&_.value?document.querySelector(`[for="${p.id}"]`)?.innerText:void 0);return F({disabled:S,state:j}),(r,o)=>(t(),a(e(u(b)?.rovingFocus.value?u(O):u(v)),c(r.$attrs,{id:r.id,ref:u(h),role:`checkbox`,"as-child":r.asChild,as:r.as,type:r.as===`button`?`button`:void 0,"aria-checked":u(M)(j.value)?`mixed`:j.value,"aria-required":r.required,"aria-label":r.$attrs[`aria-label`]||L.value,"data-state":u(N)(j.value),"data-disabled":S.value?``:void 0,disabled:S.value,focusable:u(b)?.rovingFocus.value?!S.value:void 0,onKeydown:l(d(()=>{},[`prevent`]),[`enter`]),onClick:P}),{default:i(()=>[n(r.$slots,`default`,{modelValue:u(x),state:j.value}),u(I)&&r.name&&!u(b)?(t(),a(u(D),{key:0,type:`checkbox`,checked:!!j.value,name:r.name,value:r.value,disabled:S.value,required:r.required},null,8,[`checked`,`name`,`value`,`disabled`,`required`])):f(`v-if`,!0)]),_:3},16,[`id`,`as-child`,`as`,`type`,`aria-checked`,`aria-required`,`aria-label`,`data-state`,`data-disabled`,`disabled`,`focusable`,`onKeydown`]))}}),L=r({__name:`CheckboxIndicator`,props:{forceMount:{type:Boolean,required:!1},asChild:{type:Boolean,required:!1},as:{type:null,required:!1,default:`span`}},setup(e){let{forwardRef:r}=E(),o=P();return(e,s)=>(t(),a(u(S),{present:e.forceMount||u(M)(u(o).state.value)||u(o).state.value===!0},{default:i(()=>[m(u(v),c({ref:u(r),"data-state":u(N)(u(o).state.value),"data-disabled":u(o).disabled.value?``:void 0,style:{pointerEvents:`none`},"as-child":e.asChild,as:e.as},e.$attrs),{default:i(()=>[n(e.$slots,`default`)]),_:3},16,[`data-state`,`data-disabled`,`as-child`,`as`])]),_:3},8,[`present`]))}}),R=r({__name:`Checkbox`,props:{defaultValue:{},modelValue:{},disabled:{type:Boolean},value:{},id:{},trueValue:{},falseValue:{},asChild:{type:Boolean},as:{},name:{},required:{type:Boolean},class:{type:[Boolean,null,String,Object,Array]}},emits:[`update:modelValue`],setup(e,{emit:r}){let s=e,l=r,d=C(_(s,`class`),l);return(e,r)=>(t(),a(u(I),c({"data-slot":`checkbox`},u(d),{class:u(b)(`border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 flex size-4 items-center justify-center rounded-md border transition-colors group-has-disabled/field:opacity-50 focus-visible:ring-3 aria-invalid:ring-3 peer relative shrink-0 outline-none after:absolute after:-inset-x-3 after:-inset-y-2 disabled:cursor-not-allowed disabled:opacity-50`,s.class)}),{default:i(t=>[m(u(L),{"data-slot":`checkbox-indicator`,class:`[&>svg]:size-3.5 grid place-content-center text-current transition-none`},{default:i(()=>[n(e.$slots,`default`,o(p(t)),()=>[m(u(h))])]),_:2},1024)]),_:3},16,[`class`]))}});export{R as t};
1
+ import{B as e,F as t,R as n,S as r,Y as i,_ as a,_t as o,h as s,k as c,l,mt as u,u as d,v as f,w as p,x as m}from"./client-DIIo9zPK.js";import{n as h,t as g}from"./ohash.D__AXeF1-B64hB831.js";import{b as _,i as v,p as y,r as b}from"./button-Dcc0gF5i.js";import{S as x,c as S,f as C,y as w}from"./Collection-1EHC87X5.js";import{n as T}from"./VisuallyHidden-hs8pj8OP.js";import{t as E}from"./useForwardExpose-AkE0lq8y.js";import{t as D}from"./VisuallyHiddenInput-1m0nNADN.js";import{t as O}from"./RovingFocusItem-X0bfqWWS.js";function k(e,t){return w(e)?!1:Array.isArray(e)?e.some(e=>g(e,t)):g(e,t)}var[A,j]=x(`CheckboxGroupRoot`);function M(e){return e===`indeterminate`}function N(e){return M(e)?`indeterminate`:e?`checked`:`unchecked`}var[P,F]=x(`CheckboxRoot`),I=r({inheritAttrs:!1,__name:`CheckboxRoot`,props:{defaultValue:{type:null,required:!1},modelValue:{type:null,required:!1,default:void 0},disabled:{type:Boolean,required:!1},value:{type:null,required:!1,default:`on`},id:{type:String,required:!1},trueValue:{type:null,required:!1,default:()=>!0},falseValue:{type:null,required:!1,default:()=>!1},asChild:{type:Boolean,required:!1},as:{type:null,required:!1,default:`button`},name:{type:String,required:!1},required:{type:Boolean,required:!1}},emits:[`update:modelValue`],setup(r,{emit:o}){let p=r,m=o,{forwardRef:h,currentElement:_}=E(),b=A(null),x=y(p,`modelValue`,m,{defaultValue:p.defaultValue??p.falseValue,passive:p.modelValue===void 0}),S=s(()=>b?.disabled.value||p.disabled),C=s(()=>g(x.value,p.trueValue)),j=s(()=>w(b?.modelValue.value)?x.value===`indeterminate`?`indeterminate`:C.value:k(b.modelValue.value,p.value));function P(){if(w(b?.modelValue.value))x.value===`indeterminate`?x.value=p.trueValue:x.value=C.value?p.falseValue:p.trueValue;else{let e=[...b.modelValue.value||[]];if(k(e,p.value)){let t=e.findIndex(e=>g(e,p.value));e.splice(t,1)}else e.push(p.value);b.modelValue.value=e}}let I=T(_),L=s(()=>p.id&&_.value?document.querySelector(`[for="${p.id}"]`)?.innerText:void 0);return F({disabled:S,state:j}),(r,o)=>(t(),a(e(u(b)?.rovingFocus.value?u(O):u(v)),c(r.$attrs,{id:r.id,ref:u(h),role:`checkbox`,"as-child":r.asChild,as:r.as,type:r.as===`button`?`button`:void 0,"aria-checked":u(M)(j.value)?`mixed`:j.value,"aria-required":r.required,"aria-label":r.$attrs[`aria-label`]||L.value,"data-state":u(N)(j.value),"data-disabled":S.value?``:void 0,disabled:S.value,focusable:u(b)?.rovingFocus.value?!S.value:void 0,onKeydown:l(d(()=>{},[`prevent`]),[`enter`]),onClick:P}),{default:i(()=>[n(r.$slots,`default`,{modelValue:u(x),state:j.value}),u(I)&&r.name&&!u(b)?(t(),a(u(D),{key:0,type:`checkbox`,checked:!!j.value,name:r.name,value:r.value,disabled:S.value,required:r.required},null,8,[`checked`,`name`,`value`,`disabled`,`required`])):f(`v-if`,!0)]),_:3},16,[`id`,`as-child`,`as`,`type`,`aria-checked`,`aria-required`,`aria-label`,`data-state`,`data-disabled`,`disabled`,`focusable`,`onKeydown`]))}}),L=r({__name:`CheckboxIndicator`,props:{forceMount:{type:Boolean,required:!1},asChild:{type:Boolean,required:!1},as:{type:null,required:!1,default:`span`}},setup(e){let{forwardRef:r}=E(),o=P();return(e,s)=>(t(),a(u(S),{present:e.forceMount||u(M)(u(o).state.value)||u(o).state.value===!0},{default:i(()=>[m(u(v),c({ref:u(r),"data-state":u(N)(u(o).state.value),"data-disabled":u(o).disabled.value?``:void 0,style:{pointerEvents:`none`},"as-child":e.asChild,as:e.as},e.$attrs),{default:i(()=>[n(e.$slots,`default`)]),_:3},16,[`data-state`,`data-disabled`,`as-child`,`as`])]),_:3},8,[`present`]))}}),R=r({__name:`Checkbox`,props:{defaultValue:{},modelValue:{},disabled:{type:Boolean},value:{},id:{},trueValue:{},falseValue:{},asChild:{type:Boolean},as:{},name:{},required:{type:Boolean},class:{type:[Boolean,null,String,Object,Array]}},emits:[`update:modelValue`],setup(e,{emit:r}){let s=e,l=r,d=C(_(s,`class`),l);return(e,r)=>(t(),a(u(I),c({"data-slot":`checkbox`},u(d),{class:u(b)(`border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 flex size-4 items-center justify-center rounded-md border transition-colors group-has-disabled/field:opacity-50 focus-visible:ring-3 aria-invalid:ring-3 peer relative shrink-0 outline-none after:absolute after:-inset-x-3 after:-inset-y-2 disabled:cursor-not-allowed disabled:opacity-50`,s.class)}),{default:i(t=>[m(u(L),{"data-slot":`checkbox-indicator`,class:`[&>svg]:size-3.5 grid place-content-center text-current transition-none`},{default:i(()=>[n(e.$slots,`default`,o(p(t)),()=>[m(u(h))])]),_:2},1024)]),_:3},16,[`class`]))}});export{R as t};
@@ -1 +1 @@
1
- import{A as e,F as t,K as n,M as r,R as i,S as a,Y as o,_ as s,_t as c,dt as l,h as u,k as d,mt as f,ot as p,v as m,w as h,x as g}from"./client-Mb8fy_bC.js";import{i as _,p as v,u as y}from"./button-D54q76GQ.js";import{S as b,c as x,f as S,u as C}from"./Collection-DUBb4r6h.js";import{t as w}from"./useForwardExpose-CzQFheaD.js";var[T,E]=b(`CollapsibleRoot`),D=a({__name:`CollapsibleRoot`,props:{defaultOpen:{type:Boolean,required:!1,default:!1},open:{type:Boolean,required:!1,default:void 0},disabled:{type:Boolean,required:!1},unmountOnHide:{type:Boolean,required:!1,default:!0},asChild:{type:Boolean,required:!1},as:{type:null,required:!1}},emits:[`update:open`],setup(e,{expose:n,emit:r}){let a=e,c=v(a,`open`,r,{defaultValue:a.defaultOpen,passive:a.open===void 0}),{disabled:u,unmountOnHide:d}=l(a);return E({contentId:``,disabled:u,open:c,unmountOnHide:d,onOpenToggle:()=>{u.value||(c.value=!c.value)}}),n({open:c}),w(),(e,n)=>(t(),s(f(_),{as:e.as,"as-child":a.asChild,"data-state":f(c)?`open`:`closed`,"data-disabled":f(u)?``:void 0},{default:o(()=>[i(e.$slots,`default`,{open:f(c)})]),_:3},8,[`as`,`as-child`,`data-state`,`data-disabled`]))}}),O=a({inheritAttrs:!1,__name:`CollapsibleContent`,props:{forceMount:{type:Boolean,required:!1},asChild:{type:Boolean,required:!1},as:{type:null,required:!1}},emits:[`contentFound`],setup(a,{emit:c}){let l=a,h=c,v=T();v.contentId||=C(void 0,`reka-collapsible-content`);let b=p(),{forwardRef:S,currentElement:E}=w(),D=p(0),O=p(0),k=u(()=>v.open.value),A=p(k.value),j=p();n(()=>[k.value,b.value?.present],async()=>{await e();let t=E.value;if(!t)return;j.value=j.value||{transitionDuration:t.style.transitionDuration,animationName:t.style.animationName},t.style.transitionDuration=`0s`,t.style.animationName=`none`;let n=t.getBoundingClientRect();O.value=n.height,D.value=n.width,A.value||(t.style.transitionDuration=j.value.transitionDuration,t.style.animationName=j.value.animationName)},{immediate:!0});let M=u(()=>A.value&&v.open.value);return r(()=>{requestAnimationFrame(()=>{A.value=!1})}),y(E,`beforematch`,e=>{requestAnimationFrame(()=>{v.onOpenToggle(),h(`contentFound`)})}),(e,n)=>(t(),s(f(x),{ref_key:`presentRef`,ref:b,present:e.forceMount||f(v).open.value,"force-mount":!0},{default:o(({present:t})=>[g(f(_),d(e.$attrs,{id:f(v).contentId,ref:f(S),"as-child":l.asChild,as:e.as,hidden:t?void 0:f(v).unmountOnHide.value?``:`until-found`,"data-state":M.value?void 0:f(v).open.value?`open`:`closed`,"data-disabled":f(v).disabled?.value?``:void 0,style:{"--reka-collapsible-content-height":`${O.value}px`,"--reka-collapsible-content-width":`${D.value}px`}}),{default:o(()=>[!f(v).unmountOnHide.value||t?i(e.$slots,`default`,{key:0}):m(`v-if`,!0)]),_:2},1040,[`id`,`as-child`,`as`,`hidden`,`data-state`,`data-disabled`,`style`])]),_:3},8,[`present`]))}}),k=a({__name:`CollapsibleTrigger`,props:{asChild:{type:Boolean,required:!1},as:{type:null,required:!1,default:`button`}},setup(e){let n=e;w();let r=T();return(e,a)=>(t(),s(f(_),{type:e.as===`button`?`button`:void 0,as:e.as,"as-child":n.asChild,"aria-controls":f(r).contentId,"aria-expanded":f(r).open.value,"data-state":f(r).open.value?`open`:`closed`,"data-disabled":f(r).disabled?.value?``:void 0,disabled:f(r).disabled?.value,onClick:f(r).onOpenToggle},{default:o(()=>[i(e.$slots,`default`)]),_:3},8,[`type`,`as`,`as-child`,`aria-controls`,`aria-expanded`,`data-state`,`data-disabled`,`disabled`,`onClick`]))}}),A=a({__name:`Collapsible`,props:{defaultOpen:{type:Boolean},open:{type:Boolean},disabled:{type:Boolean},unmountOnHide:{type:Boolean},asChild:{type:Boolean},as:{}},emits:[`update:open`],setup(e,{emit:n}){let r=S(e,n);return(e,n)=>(t(),s(f(D),d({"data-slot":`collapsible`},f(r)),{default:o(t=>[i(e.$slots,`default`,c(h(t)))]),_:3},16))}}),j=a({__name:`CollapsibleContent`,props:{forceMount:{type:Boolean},asChild:{type:Boolean},as:{}},setup(e){let n=e;return(e,r)=>(t(),s(f(O),d({"data-slot":`collapsible-content`},n),{default:o(()=>[i(e.$slots,`default`)]),_:3},16))}}),M=a({__name:`CollapsibleTrigger`,props:{asChild:{type:Boolean},as:{}},setup(e){let n=e;return(e,r)=>(t(),s(f(k),d({"data-slot":`collapsible-trigger`},n),{default:o(()=>[i(e.$slots,`default`)]),_:3},16))}});export{j as n,A as r,M as t};
1
+ import{A as e,F as t,K as n,M as r,R as i,S as a,Y as o,_ as s,_t as c,dt as l,h as u,k as d,mt as f,ot as p,v as m,w as h,x as g}from"./client-DIIo9zPK.js";import{i as _,p as v,u as y}from"./button-Dcc0gF5i.js";import{S as b,c as x,f as S,u as C}from"./Collection-1EHC87X5.js";import{t as w}from"./useForwardExpose-AkE0lq8y.js";var[T,E]=b(`CollapsibleRoot`),D=a({__name:`CollapsibleRoot`,props:{defaultOpen:{type:Boolean,required:!1,default:!1},open:{type:Boolean,required:!1,default:void 0},disabled:{type:Boolean,required:!1},unmountOnHide:{type:Boolean,required:!1,default:!0},asChild:{type:Boolean,required:!1},as:{type:null,required:!1}},emits:[`update:open`],setup(e,{expose:n,emit:r}){let a=e,c=v(a,`open`,r,{defaultValue:a.defaultOpen,passive:a.open===void 0}),{disabled:u,unmountOnHide:d}=l(a);return E({contentId:``,disabled:u,open:c,unmountOnHide:d,onOpenToggle:()=>{u.value||(c.value=!c.value)}}),n({open:c}),w(),(e,n)=>(t(),s(f(_),{as:e.as,"as-child":a.asChild,"data-state":f(c)?`open`:`closed`,"data-disabled":f(u)?``:void 0},{default:o(()=>[i(e.$slots,`default`,{open:f(c)})]),_:3},8,[`as`,`as-child`,`data-state`,`data-disabled`]))}}),O=a({inheritAttrs:!1,__name:`CollapsibleContent`,props:{forceMount:{type:Boolean,required:!1},asChild:{type:Boolean,required:!1},as:{type:null,required:!1}},emits:[`contentFound`],setup(a,{emit:c}){let l=a,h=c,v=T();v.contentId||=C(void 0,`reka-collapsible-content`);let b=p(),{forwardRef:S,currentElement:E}=w(),D=p(0),O=p(0),k=u(()=>v.open.value),A=p(k.value),j=p();n(()=>[k.value,b.value?.present],async()=>{await e();let t=E.value;if(!t)return;j.value=j.value||{transitionDuration:t.style.transitionDuration,animationName:t.style.animationName},t.style.transitionDuration=`0s`,t.style.animationName=`none`;let n=t.getBoundingClientRect();O.value=n.height,D.value=n.width,A.value||(t.style.transitionDuration=j.value.transitionDuration,t.style.animationName=j.value.animationName)},{immediate:!0});let M=u(()=>A.value&&v.open.value);return r(()=>{requestAnimationFrame(()=>{A.value=!1})}),y(E,`beforematch`,e=>{requestAnimationFrame(()=>{v.onOpenToggle(),h(`contentFound`)})}),(e,n)=>(t(),s(f(x),{ref_key:`presentRef`,ref:b,present:e.forceMount||f(v).open.value,"force-mount":!0},{default:o(({present:t})=>[g(f(_),d(e.$attrs,{id:f(v).contentId,ref:f(S),"as-child":l.asChild,as:e.as,hidden:t?void 0:f(v).unmountOnHide.value?``:`until-found`,"data-state":M.value?void 0:f(v).open.value?`open`:`closed`,"data-disabled":f(v).disabled?.value?``:void 0,style:{"--reka-collapsible-content-height":`${O.value}px`,"--reka-collapsible-content-width":`${D.value}px`}}),{default:o(()=>[!f(v).unmountOnHide.value||t?i(e.$slots,`default`,{key:0}):m(`v-if`,!0)]),_:2},1040,[`id`,`as-child`,`as`,`hidden`,`data-state`,`data-disabled`,`style`])]),_:3},8,[`present`]))}}),k=a({__name:`CollapsibleTrigger`,props:{asChild:{type:Boolean,required:!1},as:{type:null,required:!1,default:`button`}},setup(e){let n=e;w();let r=T();return(e,a)=>(t(),s(f(_),{type:e.as===`button`?`button`:void 0,as:e.as,"as-child":n.asChild,"aria-controls":f(r).contentId,"aria-expanded":f(r).open.value,"data-state":f(r).open.value?`open`:`closed`,"data-disabled":f(r).disabled?.value?``:void 0,disabled:f(r).disabled?.value,onClick:f(r).onOpenToggle},{default:o(()=>[i(e.$slots,`default`)]),_:3},8,[`type`,`as`,`as-child`,`aria-controls`,`aria-expanded`,`data-state`,`data-disabled`,`disabled`,`onClick`]))}}),A=a({__name:`Collapsible`,props:{defaultOpen:{type:Boolean},open:{type:Boolean},disabled:{type:Boolean},unmountOnHide:{type:Boolean},asChild:{type:Boolean},as:{}},emits:[`update:open`],setup(e,{emit:n}){let r=S(e,n);return(e,n)=>(t(),s(f(D),d({"data-slot":`collapsible`},f(r)),{default:o(t=>[i(e.$slots,`default`,c(h(t)))]),_:3},16))}}),j=a({__name:`CollapsibleContent`,props:{forceMount:{type:Boolean},asChild:{type:Boolean},as:{}},setup(e){let n=e;return(e,r)=>(t(),s(f(O),d({"data-slot":`collapsible-content`},n),{default:o(()=>[i(e.$slots,`default`)]),_:3},16))}}),M=a({__name:`CollapsibleTrigger`,props:{asChild:{type:Boolean},as:{}},setup(e){let n=e;return(e,r)=>(t(),s(f(k),d({"data-slot":`collapsible-trigger`},n),{default:o(()=>[i(e.$slots,`default`)]),_:3},16))}});export{j as n,A as r,M as t};