llm-simple-router 0.1.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 (124) hide show
  1. package/.env.example +13 -0
  2. package/LICENSE +21 -0
  3. package/README.md +121 -0
  4. package/dist/admin/constants.d.ts +10 -0
  5. package/dist/admin/constants.js +11 -0
  6. package/dist/admin/groups.d.ts +7 -0
  7. package/dist/admin/groups.js +118 -0
  8. package/dist/admin/logs.d.ts +7 -0
  9. package/dist/admin/logs.js +43 -0
  10. package/dist/admin/mappings.d.ts +7 -0
  11. package/dist/admin/mappings.js +120 -0
  12. package/dist/admin/metrics.d.ts +7 -0
  13. package/dist/admin/metrics.js +41 -0
  14. package/dist/admin/providers.d.ts +8 -0
  15. package/dist/admin/providers.js +101 -0
  16. package/dist/admin/retry-rules.d.ts +9 -0
  17. package/dist/admin/retry-rules.js +98 -0
  18. package/dist/admin/router-keys.d.ts +8 -0
  19. package/dist/admin/router-keys.js +85 -0
  20. package/dist/admin/routes.d.ts +12 -0
  21. package/dist/admin/routes.js +22 -0
  22. package/dist/admin/services.d.ts +7 -0
  23. package/dist/admin/services.js +63 -0
  24. package/dist/admin/stats.d.ts +7 -0
  25. package/dist/admin/stats.js +15 -0
  26. package/dist/config.d.ts +15 -0
  27. package/dist/config.js +28 -0
  28. package/dist/db/helpers.d.ts +12 -0
  29. package/dist/db/helpers.js +28 -0
  30. package/dist/db/index.d.ts +16 -0
  31. package/dist/db/index.js +45 -0
  32. package/dist/db/logs.d.ts +90 -0
  33. package/dist/db/logs.js +47 -0
  34. package/dist/db/mappings.d.ts +36 -0
  35. package/dist/db/mappings.js +55 -0
  36. package/dist/db/metrics.d.ts +24 -0
  37. package/dist/db/metrics.js +119 -0
  38. package/dist/db/migrations/001_init.sql +37 -0
  39. package/dist/db/migrations/002_add_request_response_body.sql +2 -0
  40. package/dist/db/migrations/003_add_full_request_chain_log.sql +4 -0
  41. package/dist/db/migrations/004_rename_to_providers.sql +9 -0
  42. package/dist/db/migrations/005_add_api_key_preview.sql +1 -0
  43. package/dist/db/migrations/006_create_request_metrics.sql +20 -0
  44. package/dist/db/migrations/007_add_retry_fields.sql +2 -0
  45. package/dist/db/migrations/008_create_router_keys.sql +17 -0
  46. package/dist/db/migrations/009_add_request_logs_indexes.sql +2 -0
  47. package/dist/db/migrations/010_add_key_encrypted.sql +1 -0
  48. package/dist/db/migrations/011_create_mapping_groups.sql +33 -0
  49. package/dist/db/migrations/012_add_provider_models.sql +2 -0
  50. package/dist/db/migrations/013_add_retry_strategy.sql +4 -0
  51. package/dist/db/providers.d.ts +27 -0
  52. package/dist/db/providers.js +29 -0
  53. package/dist/db/retry-rules.d.ts +32 -0
  54. package/dist/db/retry-rules.js +49 -0
  55. package/dist/db/router-keys.d.ts +29 -0
  56. package/dist/db/router-keys.js +36 -0
  57. package/dist/db/stats.d.ts +9 -0
  58. package/dist/db/stats.js +34 -0
  59. package/dist/index.d.ts +13 -0
  60. package/dist/index.js +131 -0
  61. package/dist/metrics/metrics-extractor.d.ts +32 -0
  62. package/dist/metrics/metrics-extractor.js +178 -0
  63. package/dist/metrics/sse-metrics-transform.d.ts +16 -0
  64. package/dist/metrics/sse-metrics-transform.js +35 -0
  65. package/dist/metrics/sse-parser.d.ts +20 -0
  66. package/dist/metrics/sse-parser.js +81 -0
  67. package/dist/middleware/admin-auth.d.ts +8 -0
  68. package/dist/middleware/admin-auth.js +57 -0
  69. package/dist/middleware/auth.d.ts +14 -0
  70. package/dist/middleware/auth.js +41 -0
  71. package/dist/proxy/anthropic.d.ts +12 -0
  72. package/dist/proxy/anthropic.js +34 -0
  73. package/dist/proxy/mapping-resolver.d.ts +3 -0
  74. package/dist/proxy/mapping-resolver.js +27 -0
  75. package/dist/proxy/openai.d.ts +12 -0
  76. package/dist/proxy/openai.js +72 -0
  77. package/dist/proxy/proxy-core.d.ts +75 -0
  78. package/dist/proxy/proxy-core.js +408 -0
  79. package/dist/proxy/retry-rules.d.ts +9 -0
  80. package/dist/proxy/retry-rules.js +27 -0
  81. package/dist/proxy/retry.d.ts +43 -0
  82. package/dist/proxy/retry.js +120 -0
  83. package/dist/proxy/strategy/failover.d.ts +4 -0
  84. package/dist/proxy/strategy/failover.js +5 -0
  85. package/dist/proxy/strategy/random.d.ts +4 -0
  86. package/dist/proxy/strategy/random.js +5 -0
  87. package/dist/proxy/strategy/round-robin.d.ts +4 -0
  88. package/dist/proxy/strategy/round-robin.js +5 -0
  89. package/dist/proxy/strategy/scheduled.d.ts +4 -0
  90. package/dist/proxy/strategy/scheduled.js +62 -0
  91. package/dist/proxy/strategy/types.d.ts +13 -0
  92. package/dist/proxy/strategy/types.js +3 -0
  93. package/dist/utils/crypto.d.ts +2 -0
  94. package/dist/utils/crypto.js +32 -0
  95. package/frontend-dist/assets/CardContent-BE9fukPi.js +1 -0
  96. package/frontend-dist/assets/CardHeader-D5lVaeAA.js +1 -0
  97. package/frontend-dist/assets/CardTitle-H-zwhi3Z.js +1 -0
  98. package/frontend-dist/assets/Checkbox--1gw0dYW.js +1 -0
  99. package/frontend-dist/assets/CollapsibleTrigger-D_ptA35Y.js +1 -0
  100. package/frontend-dist/assets/Dashboard-D4AwkULO.js +3 -0
  101. package/frontend-dist/assets/Label-GiPfoz7u.js +1 -0
  102. package/frontend-dist/assets/Login-BUet1sbM.js +1 -0
  103. package/frontend-dist/assets/Logs-yztb_F9t.js +3 -0
  104. package/frontend-dist/assets/ModelMappings-MbZhdPNv.js +1 -0
  105. package/frontend-dist/assets/Providers-BjsqH6A2.js +1 -0
  106. package/frontend-dist/assets/RetryRules-C2vvJvLr.js +1 -0
  107. package/frontend-dist/assets/RouterKeys-DavrgpAQ.js +1 -0
  108. package/frontend-dist/assets/RovingFocusItem-DnIa_lwH.js +1 -0
  109. package/frontend-dist/assets/SelectValue-BB0Ckbjh.js +1 -0
  110. package/frontend-dist/assets/TableHeader-D2GkiqRx.js +1 -0
  111. package/frontend-dist/assets/alert-dialog-CWjBke-O.js +1 -0
  112. package/frontend-dist/assets/badge-_ZHrMEpC.js +3 -0
  113. package/frontend-dist/assets/button-C4_mChkc.js +1 -0
  114. package/frontend-dist/assets/client-BWw0R36V.js +12 -0
  115. package/frontend-dist/assets/dialog-CUHMcTqp.js +1 -0
  116. package/frontend-dist/assets/index-DEl48bm9.css +1 -0
  117. package/frontend-dist/assets/index-UZK1BnPG.js +1 -0
  118. package/frontend-dist/assets/lib-Qs8xoTas.js +1 -0
  119. package/frontend-dist/assets/useForwardExpose-B-xauF1X.js +1 -0
  120. package/frontend-dist/assets/x-JBJB26JV.js +1 -0
  121. package/frontend-dist/favicon.svg +1 -0
  122. package/frontend-dist/icons.svg +24 -0
  123. package/frontend-dist/index.html +18 -0
  124. package/package.json +72 -0
@@ -0,0 +1,43 @@
1
+ import type { ProxyResult, StreamProxyResult } from "./proxy-core.js";
2
+ import type { FastifyReply } from "fastify";
3
+ import type { RetryRuleMatcher } from "./retry-rules.js";
4
+ export interface RetryConfig {
5
+ maxRetries: number;
6
+ baseDelayMs: number;
7
+ ruleMatcher?: RetryRuleMatcher;
8
+ }
9
+ export interface Attempt {
10
+ attemptIndex: number;
11
+ statusCode: number | null;
12
+ error: string | null;
13
+ latencyMs: number;
14
+ responseBody: string | null;
15
+ }
16
+ export interface RetryResult<T> {
17
+ result: T;
18
+ attempts: Attempt[];
19
+ }
20
+ export type ProxyFn<T = ProxyResult | StreamProxyResult> = () => Promise<T>;
21
+ export interface RetryStrategy {
22
+ getDelay(attempt: number): number;
23
+ }
24
+ export declare class FixedIntervalStrategy implements RetryStrategy {
25
+ private delayMs;
26
+ constructor(delayMs: number);
27
+ getDelay(_attempt: number): number;
28
+ }
29
+ export declare class ExponentialBackoffStrategy implements RetryStrategy {
30
+ private baseMs;
31
+ private capMs;
32
+ constructor(baseMs: number, capMs: number);
33
+ getDelay(attempt: number): number;
34
+ }
35
+ export declare function createStrategy(rule: {
36
+ retry_strategy: string;
37
+ retry_delay_ms: number;
38
+ max_delay_ms: number;
39
+ }): RetryStrategy;
40
+ export declare function buildRetryConfig(maxRetries: number, baseDelayMs: number, ruleMatcher?: RetryRuleMatcher): RetryConfig;
41
+ export declare function isRetryableResult(statusCode: number, body?: string, config?: RetryConfig): boolean;
42
+ export declare function isRetryableThrow(err: unknown): boolean;
43
+ export declare function retryableCall<T extends ProxyResult | StreamProxyResult>(fn: ProxyFn<T>, config: RetryConfig, reply?: FastifyReply): Promise<RetryResult<T>>;
@@ -0,0 +1,120 @@
1
+ export class FixedIntervalStrategy {
2
+ delayMs;
3
+ constructor(delayMs) {
4
+ this.delayMs = delayMs;
5
+ }
6
+ getDelay(_attempt) { return this.delayMs; }
7
+ }
8
+ export class ExponentialBackoffStrategy {
9
+ baseMs;
10
+ capMs;
11
+ constructor(baseMs, capMs) {
12
+ this.baseMs = baseMs;
13
+ this.capMs = capMs;
14
+ }
15
+ getDelay(attempt) {
16
+ return Math.min(this.baseMs * 2 ** attempt, this.capMs);
17
+ }
18
+ }
19
+ export function createStrategy(rule) {
20
+ if (rule.retry_strategy === "fixed")
21
+ return new FixedIntervalStrategy(rule.retry_delay_ms);
22
+ return new ExponentialBackoffStrategy(rule.retry_delay_ms, rule.max_delay_ms);
23
+ }
24
+ // ---------- Constants ----------
25
+ const RETRYABLE_THROW_CODES = new Set(["ETIMEDOUT", "ECONNRESET", "ECONNREFUSED"]);
26
+ const HTTP_BAD_REQUEST = 400;
27
+ const HTTP_TOO_MANY_REQUESTS = 429;
28
+ const MS_PER_SECOND = 1000;
29
+ // ---------- Shared helpers ----------
30
+ export function buildRetryConfig(maxRetries, baseDelayMs, ruleMatcher) {
31
+ return {
32
+ maxRetries,
33
+ baseDelayMs,
34
+ ruleMatcher,
35
+ };
36
+ }
37
+ // ---------- Predicates ----------
38
+ export function isRetryableResult(statusCode, body, config) {
39
+ if (body && config?.ruleMatcher?.test(statusCode, body))
40
+ return true;
41
+ return false;
42
+ }
43
+ export function isRetryableThrow(err) {
44
+ if (err && typeof err === "object" && "code" in err) {
45
+ return RETRYABLE_THROW_CODES.has(err.code ?? "");
46
+ }
47
+ return false;
48
+ }
49
+ // ---------- Internal helpers ----------
50
+ function sleep(ms) {
51
+ return new Promise((resolve) => setTimeout(resolve, ms));
52
+ }
53
+ function parseRetryAfter(headers) {
54
+ if (!headers)
55
+ return null;
56
+ const val = headers["retry-after"] ?? headers["Retry-After"];
57
+ if (!val)
58
+ return null;
59
+ const seconds = parseInt(val, 10);
60
+ return isNaN(seconds) ? null : seconds * MS_PER_SECOND;
61
+ }
62
+ function extractHeaders(result) {
63
+ return "headers" in result ? result.headers : result.upstreamResponseHeaders ?? {};
64
+ }
65
+ function extractBody(result) {
66
+ return "body" in result ? result.body : result.responseBody ?? null;
67
+ }
68
+ // ---------- Core ----------
69
+ export async function retryableCall(fn, config, reply) {
70
+ const attempts = [];
71
+ for (let attempt = 0;; attempt++) {
72
+ const start = Date.now();
73
+ try {
74
+ const result = await fn();
75
+ const elapsed = Date.now() - start;
76
+ const body = extractBody(result);
77
+ attempts.push({
78
+ attemptIndex: attempt,
79
+ statusCode: result.statusCode,
80
+ error: null,
81
+ latencyMs: elapsed,
82
+ responseBody: body,
83
+ });
84
+ if (result.statusCode < HTTP_BAD_REQUEST)
85
+ return { result, attempts };
86
+ // 通过 matcher 获取匹配规则(含策略参数)
87
+ const matchedRule = body ? config.ruleMatcher?.match(result.statusCode, body) ?? null : null;
88
+ if (!matchedRule)
89
+ return { result, attempts };
90
+ const maxAttempts = matchedRule.max_retries;
91
+ if (attempt >= maxAttempts)
92
+ return { result, attempts };
93
+ if (reply && (reply.raw.writableFinished || reply.raw.headersSent))
94
+ return { result, attempts };
95
+ const strategy = createStrategy(matchedRule);
96
+ const headers = extractHeaders(result);
97
+ const retryAfterMs = result.statusCode === HTTP_TOO_MANY_REQUESTS ? parseRetryAfter(headers) : null;
98
+ const delay = Math.max(strategy.getDelay(attempt), retryAfterMs ?? 0);
99
+ await sleep(delay);
100
+ }
101
+ catch (err) {
102
+ const elapsed = Date.now() - start;
103
+ const errMsg = err instanceof Error ? err.message : String(err);
104
+ attempts.push({
105
+ attemptIndex: attempt,
106
+ statusCode: null,
107
+ error: errMsg,
108
+ latencyMs: elapsed,
109
+ responseBody: null,
110
+ });
111
+ if (!isRetryableThrow(err))
112
+ throw err;
113
+ if (attempt >= config.maxRetries)
114
+ throw err;
115
+ if (reply && (reply.raw.writableFinished || reply.raw.headersSent))
116
+ throw err;
117
+ await sleep(config.baseDelayMs);
118
+ }
119
+ }
120
+ }
@@ -0,0 +1,4 @@
1
+ import type { MappingStrategy, ResolveContext, Target } from "./types.js";
2
+ export declare class FailoverStrategy implements MappingStrategy {
3
+ select(_rule: unknown, _context: ResolveContext): Target | undefined;
4
+ }
@@ -0,0 +1,5 @@
1
+ export class FailoverStrategy {
2
+ select(_rule, _context) {
3
+ throw new Error("Not implemented");
4
+ }
5
+ }
@@ -0,0 +1,4 @@
1
+ import type { MappingStrategy, ResolveContext, Target } from "./types.js";
2
+ export declare class RandomStrategy implements MappingStrategy {
3
+ select(_rule: unknown, _context: ResolveContext): Target | undefined;
4
+ }
@@ -0,0 +1,5 @@
1
+ export class RandomStrategy {
2
+ select(_rule, _context) {
3
+ throw new Error("Not implemented");
4
+ }
5
+ }
@@ -0,0 +1,4 @@
1
+ import type { MappingStrategy, ResolveContext, Target } from "./types.js";
2
+ export declare class RoundRobinStrategy implements MappingStrategy {
3
+ select(_rule: unknown, _context: ResolveContext): Target | undefined;
4
+ }
@@ -0,0 +1,5 @@
1
+ export class RoundRobinStrategy {
2
+ select(_rule, _context) {
3
+ throw new Error("Not implemented");
4
+ }
5
+ }
@@ -0,0 +1,4 @@
1
+ import type { MappingStrategy, ResolveContext, Target } from "./types.js";
2
+ export declare class ScheduledStrategy implements MappingStrategy {
3
+ select(rule: unknown, context: ResolveContext): Target | undefined;
4
+ }
@@ -0,0 +1,62 @@
1
+ function isTarget(value) {
2
+ return (typeof value === "object" &&
3
+ value !== null &&
4
+ "backend_model" in value &&
5
+ typeof value.backend_model === "string" &&
6
+ "provider_id" in value &&
7
+ typeof value.provider_id === "string");
8
+ }
9
+ function isTimeWindow(value) {
10
+ return (typeof value === "object" &&
11
+ value !== null &&
12
+ "start" in value &&
13
+ typeof value.start === "string" &&
14
+ "end" in value &&
15
+ typeof value.end === "string" &&
16
+ "target" in value &&
17
+ isTarget(value.target));
18
+ }
19
+ function isScheduledRule(value) {
20
+ if (typeof value !== "object" || value === null)
21
+ return false;
22
+ const r = value;
23
+ if (r.default !== undefined && !isTarget(r.default))
24
+ return false;
25
+ if (r.windows !== undefined) {
26
+ if (!Array.isArray(r.windows))
27
+ return false;
28
+ if (!r.windows.every((w) => isTimeWindow(w)))
29
+ return false;
30
+ }
31
+ return true;
32
+ }
33
+ function timeMatches(now, start, end) {
34
+ if (start > end) {
35
+ return now >= start || now <= end;
36
+ }
37
+ return now >= start && now <= end;
38
+ }
39
+ export class ScheduledStrategy {
40
+ select(rule, context) {
41
+ if (!isScheduledRule(rule)) {
42
+ return undefined;
43
+ }
44
+ const scheduledRule = rule;
45
+ const now = context.now.toLocaleTimeString("en-GB", {
46
+ hour: "2-digit",
47
+ minute: "2-digit",
48
+ hour12: false,
49
+ });
50
+ if (scheduledRule.windows) {
51
+ for (const window of scheduledRule.windows) {
52
+ if (!isTimeWindow(window)) {
53
+ continue;
54
+ }
55
+ if (timeMatches(now, window.start, window.end)) {
56
+ return window.target;
57
+ }
58
+ }
59
+ }
60
+ return scheduledRule.default;
61
+ }
62
+ }
@@ -0,0 +1,13 @@
1
+ export declare const STRATEGY_NAMES: {
2
+ readonly SCHEDULED: "scheduled";
3
+ };
4
+ export interface Target {
5
+ backend_model: string;
6
+ provider_id: string;
7
+ }
8
+ export interface ResolveContext {
9
+ now: Date;
10
+ }
11
+ export interface MappingStrategy {
12
+ select(rule: unknown, context: ResolveContext): Target | undefined;
13
+ }
@@ -0,0 +1,3 @@
1
+ export const STRATEGY_NAMES = {
2
+ SCHEDULED: "scheduled",
3
+ };
@@ -0,0 +1,2 @@
1
+ export declare function encrypt(text: string, key: string): string;
2
+ export declare function decrypt(encrypted: string, key: string): string;
@@ -0,0 +1,32 @@
1
+ import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
2
+ const ALGORITHM = "aes-256-gcm";
3
+ const IV_LENGTH = 12;
4
+ const ENCRYPTED_PARTS_COUNT = 3;
5
+ export function encrypt(text, key) {
6
+ const keyBuf = Buffer.from(key, "hex");
7
+ const iv = randomBytes(IV_LENGTH);
8
+ const cipher = createCipheriv(ALGORITHM, keyBuf, iv);
9
+ const encrypted = Buffer.concat([
10
+ cipher.update(text, "utf8"),
11
+ cipher.final(),
12
+ ]);
13
+ const authTag = cipher.getAuthTag();
14
+ return `${iv.toString("hex")}:${authTag.toString("hex")}:${encrypted.toString("hex")}`;
15
+ }
16
+ export function decrypt(encrypted, key) {
17
+ const keyBuf = Buffer.from(key, "hex");
18
+ const parts = encrypted.split(":");
19
+ if (parts.length !== ENCRYPTED_PARTS_COUNT) {
20
+ throw new Error("Invalid encrypted format, expected iv:authTag:ciphertext");
21
+ }
22
+ const iv = Buffer.from(parts[0], "hex");
23
+ const authTag = Buffer.from(parts[1], "hex");
24
+ const ciphertext = Buffer.from(parts[2], "hex");
25
+ const decipher = createDecipheriv(ALGORITHM, keyBuf, iv);
26
+ decipher.setAuthTag(authTag);
27
+ const decrypted = Buffer.concat([
28
+ decipher.update(ciphertext),
29
+ decipher.final(),
30
+ ]);
31
+ return decrypted.toString("utf8");
32
+ }
@@ -0,0 +1 @@
1
+ import{C as e,I as t,ht as n,pt as r,y as i,z as a}from"./client-BWw0R36V.js";import{r as o}from"./button-C4_mChkc.js";var s=[`data-size`],c=e({__name:`Card`,props:{class:{type:[Boolean,null,String,Object,Array]},size:{default:`default`}},setup(e){let c=e;return(l,u)=>(t(),i(`div`,{"data-slot":`card`,"data-size":e.size,class:n(r(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))},[a(l.$slots,`default`)],10,s))}}),l=e({__name:`CardContent`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(e){let s=e;return(e,c)=>(t(),i(`div`,{"data-slot":`card-content`,class:n(r(o)(`px-4 group-data-[size=sm]/card:px-3`,s.class))},[a(e.$slots,`default`)],2))}});export{c as n,l as t};
@@ -0,0 +1 @@
1
+ import{C as e,I as t,ht as n,pt as r,y as i,z as a}from"./client-BWw0R36V.js";import{r as o}from"./button-C4_mChkc.js";var s=e({__name:`CardHeader`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(e){let s=e;return(e,c)=>(t(),i(`div`,{"data-slot":`card-header`,class:n(r(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))},[a(e.$slots,`default`)],2))}});export{s as t};
@@ -0,0 +1 @@
1
+ import{C as e,I as t,ht as n,pt as r,y as i,z as a}from"./client-BWw0R36V.js";import{r as o}from"./button-C4_mChkc.js";var s=e({__name:`CardTitle`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(e){let s=e;return(e,c)=>(t(),i(`div`,{"data-slot":`card-title`,class:n(r(o)(`text-base leading-snug font-medium group-data-[size=sm]/card:text-sm cn-font-heading`,s.class))},[a(e.$slots,`default`)],2))}});export{s as t};
@@ -0,0 +1 @@
1
+ import{A as e,C as t,G as n,I as r,J as i,R as a,S as o,T as s,V as c,_ as l,f as u,gt as d,h as f,l as p,pt as m,u as h,v as g,y as _,z as v}from"./client-BWw0R36V.js";import{C as y,T as b,b as x,h as S,l as C,n as w,p as T,u as E,w as D}from"./badge-_ZHrMEpC.js";import{i as O,r as k}from"./button-C4_mChkc.js";import{c as A,m as j,t as M}from"./useForwardExpose-B-xauF1X.js";import{t as N}from"./RovingFocusItem-DnIa_lwH.js";function P(e,t){return x(e)?!1:Array.isArray(e)?e.some(e=>D(e,t)):D(e,t)}var F=t({inheritAttrs:!1,__name:`VisuallyHiddenInputBubble`,props:{name:{type:String,required:!0},value:{type:null,required:!0},checked:{type:Boolean,required:!1,default:void 0},required:{type:Boolean,required:!1},disabled:{type:Boolean,required:!1},feature:{type:String,required:!1,default:`fully-hidden`}},setup(t){let i=t,{primitiveElement:a,currentElement:o}=C();return n(f(()=>i.checked??i.value),(e,t)=>{if(!o.value)return;let n=o.value,r=window.HTMLInputElement.prototype,i=Object.getOwnPropertyDescriptor(r,`value`).set;if(i&&e!==t){let t=new Event(`input`,{bubbles:!0}),r=new Event(`change`,{bubbles:!0});i.call(n,e),n.dispatchEvent(t),n.dispatchEvent(r)}}),(t,n)=>(r(),l(w,e({ref_key:`primitiveElement`,ref:a},{...i,...t.$attrs},{as:`input`}),null,16))}}),I=t({inheritAttrs:!1,__name:`VisuallyHiddenInput`,props:{name:{type:String,required:!0},value:{type:null,required:!0},checked:{type:Boolean,required:!1,default:void 0},required:{type:Boolean,required:!1},disabled:{type:Boolean,required:!1},feature:{type:String,required:!1,default:`fully-hidden`}},setup(t){let n=t,i=f(()=>typeof n.value==`object`&&Array.isArray(n.value)&&n.value.length===0&&n.required),o=f(()=>typeof n.value==`string`||typeof n.value==`number`||typeof n.value==`boolean`||n.value===null||n.value===void 0?[{name:n.name,value:n.value}]:typeof n.value==`object`&&Array.isArray(n.value)?n.value.flatMap((e,t)=>typeof e==`object`?Object.entries(e).map(([e,r])=>({name:`${n.name}[${t}][${e}]`,value:r})):{name:`${n.name}[${t}]`,value:e}):n.value!==null&&typeof n.value==`object`&&!Array.isArray(n.value)?Object.entries(n.value).map(([e,t])=>({name:`${n.name}[${e}]`,value:t})):[]);return(t,s)=>(r(),_(u,null,[g(` We render single input if it's required `),i.value?(r(),l(F,e({key:t.name},{...n,...t.$attrs},{name:t.name,value:t.value}),null,16,[`name`,`value`])):(r(!0),_(u,{key:1},a(o.value,i=>(r(),l(F,e({key:i.name},{ref_for:!0},{...n,...t.$attrs},{name:i.name,value:i.value}),null,16,[`name`,`value`]))),128))],2112))}}),[L,R]=y(`CheckboxGroupRoot`);function z(e){return e===`indeterminate`}function B(e){return z(e)?`indeterminate`:e?`checked`:`unchecked`}var[V,H]=y(`CheckboxRoot`),U=t({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(t,{emit:n}){let a=t,o=n,{forwardRef:s,currentElement:u}=M(),d=L(null),_=A(a,`modelValue`,o,{defaultValue:a.defaultValue??a.falseValue,passive:a.modelValue===void 0}),y=f(()=>d?.disabled.value||a.disabled),b=f(()=>D(_.value,a.trueValue)),C=f(()=>x(d?.modelValue.value)?_.value===`indeterminate`?`indeterminate`:b.value:P(d.modelValue.value,a.value));function w(){if(x(d?.modelValue.value))_.value===`indeterminate`?_.value=a.trueValue:_.value=b.value?a.falseValue:a.trueValue;else{let e=[...d.modelValue.value||[]];if(P(e,a.value)){let t=e.findIndex(e=>D(e,a.value));e.splice(t,1)}else e.push(a.value);d.modelValue.value=e}}let T=S(u),E=f(()=>a.id&&u.value?document.querySelector(`[for="${a.id}"]`)?.innerText:void 0);return H({disabled:y,state:C}),(t,n)=>(r(),l(c(m(d)?.rovingFocus.value?m(N):m(O)),e(t.$attrs,{id:t.id,ref:m(s),role:`checkbox`,"as-child":t.asChild,as:t.as,type:t.as===`button`?`button`:void 0,"aria-checked":m(z)(C.value)?`mixed`:C.value,"aria-required":t.required,"aria-label":t.$attrs[`aria-label`]||E.value,"data-state":m(B)(C.value),"data-disabled":y.value?``:void 0,disabled:y.value,focusable:m(d)?.rovingFocus.value?!y.value:void 0,onKeydown:p(h(()=>{},[`prevent`]),[`enter`]),onClick:w}),{default:i(()=>[v(t.$slots,`default`,{modelValue:m(_),state:C.value}),m(T)&&t.name&&!m(d)?(r(),l(m(I),{key:0,type:`checkbox`,checked:!!C.value,name:t.name,value:t.value,disabled:y.value,required:t.required},null,8,[`checked`,`name`,`value`,`disabled`,`required`])):g(`v-if`,!0)]),_:3},16,[`id`,`as-child`,`as`,`type`,`aria-checked`,`aria-required`,`aria-label`,`data-state`,`data-disabled`,`disabled`,`focusable`,`onKeydown`]))}}),W=t({__name:`CheckboxIndicator`,props:{forceMount:{type:Boolean,required:!1},asChild:{type:Boolean,required:!1},as:{type:null,required:!1,default:`span`}},setup(t){let{forwardRef:n}=M(),a=V();return(t,s)=>(r(),l(m(E),{present:t.forceMount||m(z)(m(a).state.value)||m(a).state.value===!0},{default:i(()=>[o(m(O),e({ref:m(n),"data-state":m(B)(m(a).state.value),"data-disabled":m(a).disabled.value?``:void 0,style:{pointerEvents:`none`},"as-child":t.asChild,as:t.as},t.$attrs),{default:i(()=>[v(t.$slots,`default`)]),_:3},16,[`data-state`,`data-disabled`,`as-child`,`as`])]),_:3},8,[`present`]))}}),G=t({__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(t,{emit:n}){let a=t,c=n,u=T(j(a,`class`),c);return(t,n)=>(r(),l(m(U),e({"data-slot":`checkbox`},m(u),{class:m(k)(`border-input dark:bg-input/30 data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-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`,a.class)}),{default:i(e=>[o(m(W),{"data-slot":`checkbox-indicator`,class:`[&>svg]:size-3.5 grid place-content-center text-current transition-none`},{default:i(()=>[v(t.$slots,`default`,d(s(e)),()=>[o(m(b))])]),_:2},1024)]),_:3},16,[`class`]))}});export{G as t};
@@ -0,0 +1 @@
1
+ import{A as e,C as t,G as n,I as r,J as i,N as a,S as o,T as s,_ as c,at as l,gt as u,h as d,j as f,pt as p,ut as m,v as h,z as g}from"./client-BWw0R36V.js";import{C as _,d as v,p as y,u as b}from"./badge-_ZHrMEpC.js";import{i as x}from"./button-C4_mChkc.js";import{a as S,c as C,t as w}from"./useForwardExpose-B-xauF1X.js";var[T,E]=_(`CollapsibleRoot`),D=t({__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:t,emit:n}){let a=e,o=C(a,`open`,n,{defaultValue:a.defaultOpen,passive:a.open===void 0}),{disabled:s,unmountOnHide:l}=m(a);return E({contentId:``,disabled:s,open:o,unmountOnHide:l,onOpenToggle:()=>{s.value||(o.value=!o.value)}}),t({open:o}),w(),(e,t)=>(r(),c(p(x),{as:e.as,"as-child":a.asChild,"data-state":p(o)?`open`:`closed`,"data-disabled":p(s)?``:void 0},{default:i(()=>[g(e.$slots,`default`,{open:p(o)})]),_:3},8,[`as`,`as-child`,`data-state`,`data-disabled`]))}}),O=t({inheritAttrs:!1,__name:`CollapsibleContent`,props:{forceMount:{type:Boolean,required:!1},asChild:{type:Boolean,required:!1},as:{type:null,required:!1}},emits:[`contentFound`],setup(t,{emit:s}){let u=t,m=s,_=T();_.contentId||=v(void 0,`reka-collapsible-content`);let y=l(),{forwardRef:C,currentElement:E}=w(),D=l(0),O=l(0),k=d(()=>_.open.value),A=l(k.value),j=l();n(()=>[k.value,y.value?.present],async()=>{await f();let e=E.value;if(!e)return;j.value=j.value||{transitionDuration:e.style.transitionDuration,animationName:e.style.animationName},e.style.transitionDuration=`0s`,e.style.animationName=`none`;let t=e.getBoundingClientRect();O.value=t.height,D.value=t.width,A.value||(e.style.transitionDuration=j.value.transitionDuration,e.style.animationName=j.value.animationName)},{immediate:!0});let M=d(()=>A.value&&_.open.value);return a(()=>{requestAnimationFrame(()=>{A.value=!1})}),S(E,`beforematch`,e=>{requestAnimationFrame(()=>{_.onOpenToggle(),m(`contentFound`)})}),(t,n)=>(r(),c(p(b),{ref_key:`presentRef`,ref:y,present:t.forceMount||p(_).open.value,"force-mount":!0},{default:i(({present:n})=>[o(p(x),e(t.$attrs,{id:p(_).contentId,ref:p(C),"as-child":u.asChild,as:t.as,hidden:n?void 0:p(_).unmountOnHide.value?``:`until-found`,"data-state":M.value?void 0:p(_).open.value?`open`:`closed`,"data-disabled":p(_).disabled?.value?``:void 0,style:{"--reka-collapsible-content-height":`${O.value}px`,"--reka-collapsible-content-width":`${D.value}px`}}),{default:i(()=>[!p(_).unmountOnHide.value||n?g(t.$slots,`default`,{key:0}):h(`v-if`,!0)]),_:2},1040,[`id`,`as-child`,`as`,`hidden`,`data-state`,`data-disabled`,`style`])]),_:3},8,[`present`]))}}),k=t({__name:`CollapsibleTrigger`,props:{asChild:{type:Boolean,required:!1},as:{type:null,required:!1,default:`button`}},setup(e){let t=e;w();let n=T();return(e,a)=>(r(),c(p(x),{type:e.as===`button`?`button`:void 0,as:e.as,"as-child":t.asChild,"aria-controls":p(n).contentId,"aria-expanded":p(n).open.value,"data-state":p(n).open.value?`open`:`closed`,"data-disabled":p(n).disabled?.value?``:void 0,disabled:p(n).disabled?.value,onClick:p(n).onOpenToggle},{default:i(()=>[g(e.$slots,`default`)]),_:3},8,[`type`,`as`,`as-child`,`aria-controls`,`aria-expanded`,`data-state`,`data-disabled`,`disabled`,`onClick`]))}}),A=t({__name:`Collapsible`,props:{defaultOpen:{type:Boolean},open:{type:Boolean},disabled:{type:Boolean},unmountOnHide:{type:Boolean},asChild:{type:Boolean},as:{}},emits:[`update:open`],setup(t,{emit:n}){let a=y(t,n);return(t,n)=>(r(),c(p(D),e({"data-slot":`collapsible`},p(a)),{default:i(e=>[g(t.$slots,`default`,u(s(e)))]),_:3},16))}}),j=t({__name:`CollapsibleContent`,props:{forceMount:{type:Boolean},asChild:{type:Boolean},as:{}},setup(t){let n=t;return(t,a)=>(r(),c(p(O),e({"data-slot":`collapsible-content`},n),{default:i(()=>[g(t.$slots,`default`)]),_:3},16))}}),M=t({__name:`CollapsibleTrigger`,props:{asChild:{type:Boolean},as:{}},setup(t){let n=t;return(t,a)=>(r(),c(p(k),e({"data-slot":`collapsible-trigger`},n),{default:i(()=>[g(t.$slots,`default`)]),_:3},16))}});export{j as n,A as r,M as t};