llm-simple-router 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +128 -91
- package/dist/admin/logs.js +7 -1
- package/dist/admin/metrics.js +7 -3
- package/dist/admin/recommended.d.ts +7 -0
- package/dist/admin/recommended.js +25 -0
- package/dist/admin/routes.js +4 -0
- package/dist/admin/usage.d.ts +7 -0
- package/dist/admin/usage.js +66 -0
- package/dist/config/recommended.d.ts +24 -0
- package/dist/config/recommended.js +30 -0
- package/dist/db/index.d.ts +3 -1
- package/dist/db/index.js +2 -1
- package/dist/db/logs.d.ts +6 -0
- package/dist/db/logs.js +12 -0
- package/dist/db/metrics.d.ts +3 -3
- package/dist/db/metrics.js +50 -42
- package/dist/db/migrations/019_create_usage_windows.sql +11 -0
- package/dist/db/retry-rules.d.ts +0 -5
- package/dist/db/retry-rules.js +0 -36
- package/dist/db/usage-windows.d.ts +19 -0
- package/dist/db/usage-windows.js +37 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -3
- package/dist/proxy/usage-window-tracker.d.ts +11 -0
- package/dist/proxy/usage-window-tracker.js +75 -0
- package/dist/utils/datetime.d.ts +4 -0
- package/dist/utils/datetime.js +10 -0
- package/frontend-dist/assets/CardContent-fmM_iiuR.js +1 -0
- package/frontend-dist/assets/CardHeader-BzzFzZ1B.js +1 -0
- package/frontend-dist/assets/CardTitle-09d7O-11.js +1 -0
- package/frontend-dist/assets/Checkbox-DH8iqXQd.js +1 -0
- package/frontend-dist/assets/CollapsibleTrigger-DCRRORrU.js +1 -0
- package/frontend-dist/assets/Collection-DY9-Yue9.js +3 -0
- package/frontend-dist/assets/Dashboard-BEzoZuSm.js +3 -0
- package/frontend-dist/assets/DialogTitle-BeMGJzYO.js +1 -0
- package/frontend-dist/assets/Input-BhvZ-Up7.js +1 -0
- package/frontend-dist/assets/Label-DjtouWZ7.js +1 -0
- package/frontend-dist/assets/LogDetailDialog-BjRsy_FR.js +3 -0
- package/frontend-dist/assets/Login-hOCPB-34.js +1 -0
- package/frontend-dist/assets/Logs-C5c3BJsg.js +1 -0
- package/frontend-dist/assets/ModelMappings-CDjxwyyz.js +1 -0
- package/frontend-dist/assets/Monitor-CPAvIREG.js +1 -0
- package/frontend-dist/assets/PopperContent-CHNw_qb6.js +1 -0
- package/frontend-dist/assets/Providers-C9ZAqHxO.js +1 -0
- package/frontend-dist/assets/ProxyEnhancement-Ct5WbiB7.js +5 -0
- package/frontend-dist/assets/RetryRules-CbgyrP6w.js +1 -0
- package/frontend-dist/assets/RouterKeys-zmqgFEKp.js +1 -0
- package/frontend-dist/assets/SelectValue-CP4Sh7LP.js +1 -0
- package/frontend-dist/assets/Setup-BXDEPt4o.js +1 -0
- package/frontend-dist/assets/Switch-DF6awXqs.js +1 -0
- package/frontend-dist/assets/TableHeader-BKE_yVML.js +1 -0
- package/frontend-dist/assets/TabsTrigger-D8R7lxaI.js +1 -0
- package/frontend-dist/assets/TooltipTrigger-BjQXeFem.js +1 -0
- package/frontend-dist/assets/VisuallyHidden-B_NnkONE.js +1 -0
- package/frontend-dist/assets/VisuallyHiddenInput-cjeTgyDe.js +1 -0
- package/frontend-dist/assets/alert-dialog-BoGRIC1Q.js +1 -0
- package/frontend-dist/assets/badge-DIO8W_W9.js +1 -0
- package/frontend-dist/assets/button-qxGNBunr.js +12 -0
- package/frontend-dist/assets/{createLucideIcon-DGZkBjcJ.js → createLucideIcon-jHUFhqKn.js} +1 -1
- package/frontend-dist/assets/dialog-D8pIXeSs.js +1 -0
- package/frontend-dist/assets/index-C_disqMY.js +1 -0
- package/frontend-dist/assets/index-DDp6SHfg.css +1 -0
- package/frontend-dist/assets/lib-DjpgwSRA.js +1 -0
- package/frontend-dist/assets/{ohash.D__AXeF1-B64hB831.js → ohash.D__AXeF1-nmJ7gFbh.js} +1 -1
- package/frontend-dist/assets/{useClipboard-CWc1cTDo.js → useClipboard-CmLp2YGk.js} +1 -1
- package/frontend-dist/assets/useForwardExpose-awoGXQkg.js +1 -0
- package/frontend-dist/assets/useNonce-_2e-GL-A.js +1 -0
- package/frontend-dist/assets/x-B0G-wIAB.js +1 -0
- package/frontend-dist/index.html +7 -7
- package/package.json +1 -1
- package/frontend-dist/assets/CardContent-CTnwqTdL.js +0 -1
- package/frontend-dist/assets/CardHeader-CfUeY7tk.js +0 -1
- package/frontend-dist/assets/CardTitle-CWiDwWqd.js +0 -1
- package/frontend-dist/assets/Checkbox-BxNz70R_.js +0 -1
- package/frontend-dist/assets/CollapsibleTrigger-Uz1aGdtH.js +0 -1
- package/frontend-dist/assets/Collection-1EHC87X5.js +0 -3
- package/frontend-dist/assets/Dashboard-C3FL30UN.js +0 -3
- package/frontend-dist/assets/DialogTitle-CAOFxr83.js +0 -1
- package/frontend-dist/assets/Input-DRIid2C6.js +0 -1
- package/frontend-dist/assets/Label-UyNN2jyE.js +0 -1
- package/frontend-dist/assets/LogDetailDialog-8BT4vIlV.js +0 -3
- package/frontend-dist/assets/Login-CnzH6TdS.js +0 -1
- package/frontend-dist/assets/Logs-CbK8NB_X.js +0 -1
- package/frontend-dist/assets/ModelMappings-DeRFgsYG.js +0 -1
- package/frontend-dist/assets/Monitor-Dd80bdUn.js +0 -1
- package/frontend-dist/assets/PopperContent-B3fZao7v.js +0 -1
- package/frontend-dist/assets/Providers-B_DbV-_y.js +0 -1
- package/frontend-dist/assets/ProxyEnhancement-up1fnPzq.js +0 -5
- package/frontend-dist/assets/RetryRules-Dkuhjh0u.js +0 -1
- package/frontend-dist/assets/RouterKeys-CvMMAa4t.js +0 -1
- package/frontend-dist/assets/RovingFocusItem-X0bfqWWS.js +0 -1
- package/frontend-dist/assets/SelectValue-zO8t-tx1.js +0 -1
- package/frontend-dist/assets/Setup-ByT2ThOQ.js +0 -1
- package/frontend-dist/assets/Switch-BEMjVugO.js +0 -1
- package/frontend-dist/assets/TableHeader-DpHWSnxK.js +0 -1
- package/frontend-dist/assets/TabsTrigger-Db6RqsZc.js +0 -1
- package/frontend-dist/assets/VisuallyHidden-hs8pj8OP.js +0 -1
- package/frontend-dist/assets/VisuallyHiddenInput-1m0nNADN.js +0 -1
- package/frontend-dist/assets/alert-dialog-PP91kaO8.js +0 -1
- package/frontend-dist/assets/button-Dcc0gF5i.js +0 -1
- package/frontend-dist/assets/client-DIIo9zPK.js +0 -12
- package/frontend-dist/assets/dialog-CxSyR-fN.js +0 -1
- package/frontend-dist/assets/index-BL-LAtac.css +0 -1
- package/frontend-dist/assets/index-CvT41fGL.js +0 -1
- package/frontend-dist/assets/lib-Bl0OuBjh.js +0 -1
- package/frontend-dist/assets/useForwardExpose-AkE0lq8y.js +0 -1
- package/frontend-dist/assets/useNonce-DGyPxdjq.js +0 -1
- package/frontend-dist/assets/x-BuUpx9Fr.js +0 -1
package/dist/db/metrics.js
CHANGED
|
@@ -7,6 +7,7 @@ export function insertMetrics(db, m) {
|
|
|
7
7
|
}
|
|
8
8
|
const PERIOD_OFFSET = {
|
|
9
9
|
"1h": "-1 hours",
|
|
10
|
+
"5h": "-5 hours",
|
|
10
11
|
"6h": "-6 hours",
|
|
11
12
|
"24h": "-1 day",
|
|
12
13
|
"7d": "-7 days",
|
|
@@ -14,6 +15,7 @@ const PERIOD_OFFSET = {
|
|
|
14
15
|
};
|
|
15
16
|
const BUCKET_SECONDS = {
|
|
16
17
|
"1h": 60,
|
|
18
|
+
"5h": 300,
|
|
17
19
|
"6h": 300,
|
|
18
20
|
"24h": 900,
|
|
19
21
|
"7d": 3600,
|
|
@@ -21,10 +23,39 @@ const BUCKET_SECONDS = {
|
|
|
21
23
|
};
|
|
22
24
|
// unix epoch 秒转毫秒的乘数
|
|
23
25
|
const MS_PER_SEC = 1000;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
// 时间跨度(秒)→ 桶大小(秒)的阶梯映射,与 BUCKET_SECONDS 保持对齐
|
|
27
|
+
const BUCKET_THRESHOLDS = [
|
|
28
|
+
{ maxSec: 3600, bucketSec: 60 }, // ≤1h: 1min
|
|
29
|
+
{ maxSec: 21600, bucketSec: 300 }, // ≤6h: 5min
|
|
30
|
+
{ maxSec: 86400, bucketSec: 900 }, // ≤1d: 15min
|
|
31
|
+
{ maxSec: 604800, bucketSec: 3600 }, // ≤7d: 1h
|
|
32
|
+
];
|
|
33
|
+
const FALLBACK_BUCKET_SEC = 14400; // >7d: 4h
|
|
34
|
+
function calculateBucketSeconds(startTime, endTime) {
|
|
35
|
+
const ms = new Date(endTime).getTime() - new Date(startTime).getTime();
|
|
36
|
+
const sec = ms / MS_PER_SEC;
|
|
37
|
+
const match = BUCKET_THRESHOLDS.find((t) => sec <= t.maxSec);
|
|
38
|
+
return match ? match.bucketSec : FALLBACK_BUCKET_SEC;
|
|
39
|
+
}
|
|
40
|
+
function buildTimeCondition(period, startTime, endTime) {
|
|
41
|
+
if (startTime && endTime) {
|
|
42
|
+
// request_metrics.created_at 用 datetime('now') 格式 (YYYY-MM-DD HH:MM:SS),
|
|
43
|
+
// 前端传入 ISO 8601,需要转换格式以匹配字符串比较
|
|
44
|
+
return {
|
|
45
|
+
timeWhere: "rm.created_at >= datetime(?) AND rm.created_at < datetime(?)",
|
|
46
|
+
timeParams: [startTime, endTime],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
timeWhere: "rm.created_at >= datetime('now', ?)",
|
|
51
|
+
timeParams: [PERIOD_OFFSET[period]],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export function getMetricsSummary(db, period, providerId, backendModel, routerKeyId, startTime, endTime) {
|
|
55
|
+
const { timeWhere, timeParams } = buildTimeCondition(period, startTime, endTime);
|
|
56
|
+
const conditions = ["rm.is_complete = 1", timeWhere];
|
|
57
|
+
const params = [...timeParams];
|
|
58
|
+
const joins = ["LEFT JOIN providers p ON p.id = rm.provider_id"];
|
|
28
59
|
if (providerId) {
|
|
29
60
|
conditions.push("rm.provider_id = ?");
|
|
30
61
|
params.push(providerId);
|
|
@@ -33,48 +64,23 @@ export function getMetricsSummary(db, period, providerId, backendModel, routerKe
|
|
|
33
64
|
conditions.push("rm.backend_model = ?");
|
|
34
65
|
params.push(backendModel);
|
|
35
66
|
}
|
|
36
|
-
const joins = "LEFT JOIN providers p ON p.id = rm.provider_id";
|
|
37
67
|
if (routerKeyId) {
|
|
68
|
+
joins.push("LEFT JOIN request_logs rl ON rl.id = rm.request_log_id");
|
|
38
69
|
conditions.push("rl.router_key_id = ?");
|
|
39
70
|
params.push(routerKeyId);
|
|
40
|
-
return db.prepare(`
|
|
41
|
-
SELECT
|
|
42
|
-
rm.provider_id, COALESCE(p.name, rm.provider_id) AS provider_name, rm.backend_model,
|
|
43
|
-
COUNT(*) AS request_count, AVG(rm.ttft_ms) AS avg_ttft_ms, NULL AS p50_ttft_ms, NULL AS p95_ttft_ms,
|
|
44
|
-
AVG(rm.tokens_per_second) AS avg_tps,
|
|
45
|
-
COALESCE(SUM(rm.input_tokens), 0) AS total_input_tokens, COALESCE(SUM(rm.output_tokens), 0) AS total_output_tokens,
|
|
46
|
-
COALESCE(SUM(rm.cache_read_tokens), 0) AS total_cache_hit_tokens,
|
|
47
|
-
CASE WHEN SUM(rm.input_tokens) > 0 THEN SUM(rm.cache_read_tokens) * 1.0 / SUM(rm.input_tokens) ELSE NULL END AS cache_hit_rate
|
|
48
|
-
FROM request_metrics rm
|
|
49
|
-
LEFT JOIN providers p ON p.id = rm.provider_id
|
|
50
|
-
LEFT JOIN request_logs rl ON rl.id = rm.request_log_id
|
|
51
|
-
WHERE ${conditions.join(" AND ")}
|
|
52
|
-
GROUP BY rm.provider_id, rm.backend_model ORDER BY request_count DESC
|
|
53
|
-
`).all(...params);
|
|
54
71
|
}
|
|
55
|
-
const where = conditions.join(" AND ");
|
|
56
72
|
return db.prepare(`
|
|
57
73
|
SELECT
|
|
58
|
-
rm.provider_id,
|
|
59
|
-
|
|
60
|
-
rm.backend_model,
|
|
61
|
-
COUNT(*) AS request_count,
|
|
62
|
-
AVG(rm.ttft_ms) AS avg_ttft_ms,
|
|
63
|
-
NULL AS p50_ttft_ms,
|
|
64
|
-
NULL AS p95_ttft_ms,
|
|
74
|
+
rm.provider_id, COALESCE(p.name, rm.provider_id) AS provider_name, rm.backend_model,
|
|
75
|
+
COUNT(*) AS request_count, AVG(rm.ttft_ms) AS avg_ttft_ms, NULL AS p50_ttft_ms, NULL AS p95_ttft_ms,
|
|
65
76
|
AVG(rm.tokens_per_second) AS avg_tps,
|
|
66
|
-
COALESCE(SUM(rm.input_tokens), 0) AS total_input_tokens,
|
|
67
|
-
COALESCE(SUM(rm.output_tokens), 0) AS total_output_tokens,
|
|
77
|
+
COALESCE(SUM(rm.input_tokens), 0) AS total_input_tokens, COALESCE(SUM(rm.output_tokens), 0) AS total_output_tokens,
|
|
68
78
|
COALESCE(SUM(rm.cache_read_tokens), 0) AS total_cache_hit_tokens,
|
|
69
|
-
CASE WHEN SUM(rm.input_tokens) > 0
|
|
70
|
-
THEN SUM(rm.cache_read_tokens) * 1.0 / SUM(rm.input_tokens)
|
|
71
|
-
ELSE NULL
|
|
72
|
-
END AS cache_hit_rate
|
|
79
|
+
CASE WHEN SUM(rm.input_tokens) > 0 THEN SUM(rm.cache_read_tokens) * 1.0 / SUM(rm.input_tokens) ELSE NULL END AS cache_hit_rate
|
|
73
80
|
FROM request_metrics rm
|
|
74
|
-
${joins}
|
|
75
|
-
WHERE ${
|
|
76
|
-
GROUP BY rm.provider_id, rm.backend_model
|
|
77
|
-
ORDER BY request_count DESC
|
|
81
|
+
${joins.join(" ")}
|
|
82
|
+
WHERE ${conditions.join(" AND ")}
|
|
83
|
+
GROUP BY rm.provider_id, rm.backend_model ORDER BY request_count DESC
|
|
78
84
|
`).all(...params);
|
|
79
85
|
}
|
|
80
86
|
const METRIC_EXPR = {
|
|
@@ -87,11 +93,13 @@ const METRIC_EXPR = {
|
|
|
87
93
|
output_tokens: "SUM(rm.output_tokens)",
|
|
88
94
|
cache_hit_tokens: "SUM(rm.cache_read_tokens)",
|
|
89
95
|
};
|
|
90
|
-
export function getMetricsTimeseries(db, period, metric, providerId, backendModel, routerKeyId) {
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const
|
|
96
|
+
export function getMetricsTimeseries(db, period, metric, providerId, backendModel, routerKeyId, startTime, endTime) {
|
|
97
|
+
const bucketSec = (startTime && endTime)
|
|
98
|
+
? calculateBucketSeconds(startTime, endTime)
|
|
99
|
+
: BUCKET_SECONDS[period];
|
|
100
|
+
const { timeWhere, timeParams } = buildTimeCondition(period, startTime, endTime);
|
|
101
|
+
const conditions = ["rm.is_complete = 1", timeWhere];
|
|
102
|
+
const params = [...timeParams];
|
|
95
103
|
if (providerId) {
|
|
96
104
|
conditions.push("rm.provider_id = ?");
|
|
97
105
|
params.push(providerId);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS usage_windows (
|
|
2
|
+
id TEXT PRIMARY KEY,
|
|
3
|
+
router_key_id TEXT,
|
|
4
|
+
start_time TEXT NOT NULL,
|
|
5
|
+
end_time TEXT NOT NULL,
|
|
6
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
CREATE INDEX IF NOT EXISTS idx_usage_windows_start ON usage_windows(start_time);
|
|
10
|
+
CREATE INDEX IF NOT EXISTS idx_usage_windows_router_key ON usage_windows(router_key_id);
|
|
11
|
+
CREATE INDEX IF NOT EXISTS idx_usage_windows_end_time ON usage_windows(end_time);
|
package/dist/db/retry-rules.d.ts
CHANGED
|
@@ -25,8 +25,3 @@ export declare function createRetryRule(db: Database.Database, rule: {
|
|
|
25
25
|
}): string;
|
|
26
26
|
export declare function updateRetryRule(db: Database.Database, id: string, fields: Partial<Pick<RetryRule, "name" | "status_code" | "body_pattern" | "is_active" | "retry_strategy" | "retry_delay_ms" | "max_retries" | "max_delay_ms">>): void;
|
|
27
27
|
export declare function deleteRetryRule(db: Database.Database, id: string): void;
|
|
28
|
-
/**
|
|
29
|
-
* 启动时按名称查重插入默认重试规则。
|
|
30
|
-
* 已存在的规则不会被重复插入或覆盖。
|
|
31
|
-
*/
|
|
32
|
-
export declare function seedDefaultRules(db: Database.Database): void;
|
package/dist/db/retry-rules.js
CHANGED
|
@@ -27,39 +27,3 @@ export function updateRetryRule(db, id, fields) {
|
|
|
27
27
|
export function deleteRetryRule(db, id) {
|
|
28
28
|
deleteById(db, "retry_rules", id);
|
|
29
29
|
}
|
|
30
|
-
// ---------- Default seed rules ----------
|
|
31
|
-
const DEFAULT_RETRY_FIELDS = {
|
|
32
|
-
is_active: 1,
|
|
33
|
-
retry_strategy: "exponential",
|
|
34
|
-
retry_delay_ms: DEFAULT_RETRY_DELAY_MS,
|
|
35
|
-
max_retries: DEFAULT_MAX_RETRIES,
|
|
36
|
-
max_delay_ms: DEFAULT_MAX_DELAY_MS,
|
|
37
|
-
};
|
|
38
|
-
const DEFAULT_RULES = [
|
|
39
|
-
{ name: "429 Too Many Requests", status_code: 429, body_pattern: ".*", ...DEFAULT_RETRY_FIELDS },
|
|
40
|
-
{ name: "503 Service Unavailable", status_code: 503, body_pattern: ".*", ...DEFAULT_RETRY_FIELDS },
|
|
41
|
-
{ name: 'ZAI 网络错误 (code 1234)', status_code: 400, body_pattern: '"type"\\s*:\\s*"error".*"code"\\s*:\\s*"1234"', ...DEFAULT_RETRY_FIELDS },
|
|
42
|
-
{ name: 'ZAI 临时不可用', status_code: 400, body_pattern: '"type"\\s*:\\s*"error".*请稍后重试', ...DEFAULT_RETRY_FIELDS },
|
|
43
|
-
{ name: 'ZAI 操作失败 (code 500)', status_code: 400, body_pattern: '"type"\\s*:\\s*"error".*"code"\\s*:\\s*"500"', ...DEFAULT_RETRY_FIELDS },
|
|
44
|
-
{ name: 'ZAI 速率限制 (HTTP 200, code 1302)', status_code: 200, body_pattern: '"error".*"code"\\s*:\\s*"1302"', ...DEFAULT_RETRY_FIELDS },
|
|
45
|
-
{ name: 'ZAI SSE 错误 (HTTP 200, code 500)', status_code: 200, body_pattern: '"error".*"code"\\s*:\\s*"500"', ...DEFAULT_RETRY_FIELDS },
|
|
46
|
-
{ name: 'ZAI SSE 错误 (HTTP 200, code 1234)', status_code: 200, body_pattern: '"error".*"code"\\s*:\\s*"1234"', ...DEFAULT_RETRY_FIELDS },
|
|
47
|
-
];
|
|
48
|
-
/**
|
|
49
|
-
* 启动时按名称查重插入默认重试规则。
|
|
50
|
-
* 已存在的规则不会被重复插入或覆盖。
|
|
51
|
-
*/
|
|
52
|
-
export function seedDefaultRules(db) {
|
|
53
|
-
const names = DEFAULT_RULES.map(r => r.name);
|
|
54
|
-
const placeholders = names.map(() => '?').join(',');
|
|
55
|
-
const existing = db.prepare(`SELECT name FROM retry_rules WHERE name IN (${placeholders})`).all(...names);
|
|
56
|
-
const existingSet = new Set(existing.map(r => r.name));
|
|
57
|
-
const stmt = db.prepare(`INSERT INTO retry_rules (id, name, status_code, body_pattern, is_active, created_at, retry_strategy, retry_delay_ms, max_retries, max_delay_ms)
|
|
58
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
59
|
-
const now = new Date().toISOString();
|
|
60
|
-
for (const rule of DEFAULT_RULES) {
|
|
61
|
-
if (!existingSet.has(rule.name)) {
|
|
62
|
-
stmt.run(randomUUID(), rule.name, rule.status_code, rule.body_pattern, rule.is_active, now, rule.retry_strategy, rule.retry_delay_ms, rule.max_retries, rule.max_delay_ms);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
export interface UsageWindow {
|
|
3
|
+
id: string;
|
|
4
|
+
router_key_id: string | null;
|
|
5
|
+
start_time: string;
|
|
6
|
+
end_time: string;
|
|
7
|
+
created_at: string;
|
|
8
|
+
}
|
|
9
|
+
export interface WindowUsage {
|
|
10
|
+
request_count: number;
|
|
11
|
+
total_input_tokens: number;
|
|
12
|
+
total_output_tokens: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function insertWindow(db: Database.Database, w: Omit<UsageWindow, "created_at">): string;
|
|
15
|
+
export declare function getLatestWindow(db: Database.Database, routerKeyId?: string): UsageWindow | null;
|
|
16
|
+
/** 返回与 [start, end) 区间有重叠的窗口 */
|
|
17
|
+
export declare function getWindowsInRange(db: Database.Database, start: string, end: string, routerKeyId?: string): UsageWindow[];
|
|
18
|
+
/** 聚合指定时间窗口内的请求计数和 token 用量 */
|
|
19
|
+
export declare function getWindowUsage(db: Database.Database, startTime: string, endTime: string, routerKeyId?: string): WindowUsage;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { randomUUID } from "crypto";
|
|
2
|
+
export function insertWindow(db, w) {
|
|
3
|
+
const id = w.id || randomUUID();
|
|
4
|
+
db.prepare("INSERT INTO usage_windows (id, router_key_id, start_time, end_time) VALUES (?, ?, ?, ?)").run(id, w.router_key_id ?? null, w.start_time, w.end_time);
|
|
5
|
+
return id;
|
|
6
|
+
}
|
|
7
|
+
export function getLatestWindow(db, routerKeyId) {
|
|
8
|
+
const sql = routerKeyId
|
|
9
|
+
? "SELECT * FROM usage_windows WHERE router_key_id = ? ORDER BY start_time DESC LIMIT 1"
|
|
10
|
+
: "SELECT * FROM usage_windows ORDER BY start_time DESC LIMIT 1";
|
|
11
|
+
const params = routerKeyId ? [routerKeyId] : [];
|
|
12
|
+
return db.prepare(sql).get(...params) ?? null;
|
|
13
|
+
}
|
|
14
|
+
/** 返回与 [start, end) 区间有重叠的窗口 */
|
|
15
|
+
export function getWindowsInRange(db, start, end, routerKeyId) {
|
|
16
|
+
if (routerKeyId) {
|
|
17
|
+
return db.prepare("SELECT * FROM usage_windows WHERE start_time < ? AND end_time > ? AND router_key_id = ? ORDER BY start_time ASC").all(end, start, routerKeyId);
|
|
18
|
+
}
|
|
19
|
+
return db.prepare("SELECT * FROM usage_windows WHERE start_time < ? AND end_time > ? ORDER BY start_time ASC").all(end, start);
|
|
20
|
+
}
|
|
21
|
+
/** 聚合指定时间窗口内的请求计数和 token 用量 */
|
|
22
|
+
export function getWindowUsage(db, startTime, endTime, routerKeyId) {
|
|
23
|
+
const baseSql = `
|
|
24
|
+
SELECT
|
|
25
|
+
COUNT(*) AS request_count,
|
|
26
|
+
COALESCE(SUM(rm.input_tokens), 0) AS total_input_tokens,
|
|
27
|
+
COALESCE(SUM(rm.output_tokens), 0) AS total_output_tokens
|
|
28
|
+
FROM request_metrics rm
|
|
29
|
+
JOIN request_logs rl ON rl.id = rm.request_log_id
|
|
30
|
+
WHERE rm.is_complete = 1
|
|
31
|
+
AND rm.created_at >= datetime(?)
|
|
32
|
+
AND rm.created_at < datetime(?)`;
|
|
33
|
+
if (routerKeyId) {
|
|
34
|
+
return db.prepare(`${baseSql} AND rl.router_key_id = ?`).get(startTime, endTime, routerKeyId);
|
|
35
|
+
}
|
|
36
|
+
return db.prepare(baseSql).get(startTime, endTime);
|
|
37
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { FastifyInstance } from "fastify";
|
|
3
3
|
import { Config } from "./config.js";
|
|
4
|
+
import { UsageWindowTracker } from "./proxy/usage-window-tracker.js";
|
|
4
5
|
import Database from "better-sqlite3";
|
|
5
6
|
export interface AppOptions {
|
|
6
7
|
config?: Config;
|
|
@@ -9,6 +10,7 @@ export interface AppOptions {
|
|
|
9
10
|
export declare function buildApp(options?: AppOptions): Promise<{
|
|
10
11
|
app: FastifyInstance;
|
|
11
12
|
db: Database.Database;
|
|
13
|
+
usageWindowTracker: UsageWindowTracker;
|
|
12
14
|
close: () => Promise<void>;
|
|
13
15
|
}>;
|
|
14
16
|
export declare function main(): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -21,7 +21,8 @@ function getProxyApiType(url) {
|
|
|
21
21
|
const __filename = fileURLToPath(import.meta.url);
|
|
22
22
|
const __dirname = path.dirname(__filename);
|
|
23
23
|
import { getConfig } from "./config.js";
|
|
24
|
-
import { initDatabase,
|
|
24
|
+
import { initDatabase, getAllProviders } from "./db/index.js";
|
|
25
|
+
import { loadRecommendedConfig } from "./config/recommended.js";
|
|
25
26
|
import { authMiddleware } from "./middleware/auth.js";
|
|
26
27
|
import { openaiProxy } from "./proxy/openai.js";
|
|
27
28
|
import { anthropicProxy } from "./proxy/anthropic.js";
|
|
@@ -30,6 +31,7 @@ import { RetryRuleMatcher } from "./proxy/retry-rules.js";
|
|
|
30
31
|
import { ProviderSemaphoreManager } from "./proxy/semaphore.js";
|
|
31
32
|
import { RequestTracker } from "./monitor/request-tracker.js";
|
|
32
33
|
import { modelState } from "./proxy/model-state.js";
|
|
34
|
+
import { UsageWindowTracker } from "./proxy/usage-window-tracker.js";
|
|
33
35
|
import fastifyStatic from "@fastify/static";
|
|
34
36
|
export async function buildApp(options) {
|
|
35
37
|
const config = options?.config ?? getBaseConfig();
|
|
@@ -100,8 +102,7 @@ export async function buildApp(options) {
|
|
|
100
102
|
}
|
|
101
103
|
return reply.code(status).send({ error: { message: fastifyError.message } });
|
|
102
104
|
});
|
|
103
|
-
|
|
104
|
-
seedDefaultRules(db);
|
|
105
|
+
loadRecommendedConfig();
|
|
105
106
|
// 注入 DB 到 modelState 单例,启用会话级持久化
|
|
106
107
|
modelState.init(db);
|
|
107
108
|
const matcher = new RetryRuleMatcher();
|
|
@@ -109,6 +110,9 @@ export async function buildApp(options) {
|
|
|
109
110
|
const semaphoreManager = new ProviderSemaphoreManager();
|
|
110
111
|
const tracker = new RequestTracker({ semaphoreManager, logger: app.log });
|
|
111
112
|
tracker.startPushInterval();
|
|
113
|
+
// 5h 用量窗口追踪器,启动时自动补齐缺失窗口
|
|
114
|
+
const usageWindowTracker = new UsageWindowTracker(db);
|
|
115
|
+
usageWindowTracker.reconcileOnStartup();
|
|
112
116
|
// 从 DB 读取已有 provider 的并发配置,初始化信号量管理器和 tracker
|
|
113
117
|
const allProviders = getAllProviders(db);
|
|
114
118
|
for (const p of allProviders) {
|
|
@@ -172,6 +176,7 @@ export async function buildApp(options) {
|
|
|
172
176
|
return {
|
|
173
177
|
app,
|
|
174
178
|
db,
|
|
179
|
+
usageWindowTracker,
|
|
175
180
|
close: async () => {
|
|
176
181
|
tracker.stopPushInterval();
|
|
177
182
|
await app.close();
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
export declare class UsageWindowTracker {
|
|
3
|
+
private db;
|
|
4
|
+
constructor(db: Database.Database);
|
|
5
|
+
/** 请求成功后调用,按需创建新窗口 */
|
|
6
|
+
recordRequest(routerKeyId?: string): void;
|
|
7
|
+
/** 启动时补齐因宕机/重启而缺失的窗口 */
|
|
8
|
+
reconcileOnStartup(): void;
|
|
9
|
+
/** 从 baseTime 开始,每 5h 一个窗口,直到覆盖 lastLogTime */
|
|
10
|
+
private backfillWindows;
|
|
11
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { randomUUID } from "crypto";
|
|
2
|
+
import { getLatestWindow, insertWindow } from "../db/usage-windows.js";
|
|
3
|
+
import { toSqliteDatetime, parseSqliteDatetime as parseDate } from "../utils/datetime.js";
|
|
4
|
+
// eslint-disable-next-line no-magic-numbers
|
|
5
|
+
const WINDOW_DURATION_MS = 5 * 3600_000;
|
|
6
|
+
export class UsageWindowTracker {
|
|
7
|
+
db;
|
|
8
|
+
constructor(db) {
|
|
9
|
+
this.db = db;
|
|
10
|
+
}
|
|
11
|
+
/** 请求成功后调用,按需创建新窗口 */
|
|
12
|
+
recordRequest(routerKeyId) {
|
|
13
|
+
const now = new Date();
|
|
14
|
+
const latest = getLatestWindow(this.db, routerKeyId);
|
|
15
|
+
if (!latest || now > parseDate(latest.end_time)) {
|
|
16
|
+
const startTime = truncateToMinute(now);
|
|
17
|
+
insertWindow(this.db, {
|
|
18
|
+
id: randomUUID(),
|
|
19
|
+
router_key_id: routerKeyId ?? null,
|
|
20
|
+
start_time: toSqliteDatetime(startTime),
|
|
21
|
+
end_time: toSqliteDatetime(new Date(startTime.getTime() + WINDOW_DURATION_MS)),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** 启动时补齐因宕机/重启而缺失的窗口 */
|
|
26
|
+
reconcileOnStartup() {
|
|
27
|
+
const latest = getLatestWindow(this.db);
|
|
28
|
+
// 查找 request_logs 中最新一条请求的时间
|
|
29
|
+
const lastLog = this.db.prepare("SELECT created_at FROM request_logs ORDER BY created_at DESC LIMIT 1").get();
|
|
30
|
+
if (!lastLog)
|
|
31
|
+
return;
|
|
32
|
+
if (!latest) {
|
|
33
|
+
// 从未创建过窗口,但有请求记录,从最早请求创建初始窗口
|
|
34
|
+
const firstLog = this.db.prepare("SELECT created_at FROM request_logs ORDER BY created_at ASC LIMIT 1").get();
|
|
35
|
+
if (!firstLog)
|
|
36
|
+
return;
|
|
37
|
+
const start = parseDate(firstLog.created_at);
|
|
38
|
+
const truncated = truncateToMinute(start);
|
|
39
|
+
insertWindow(this.db, {
|
|
40
|
+
id: randomUUID(),
|
|
41
|
+
router_key_id: null,
|
|
42
|
+
start_time: toSqliteDatetime(truncated),
|
|
43
|
+
end_time: toSqliteDatetime(new Date(truncated.getTime() + WINDOW_DURATION_MS)),
|
|
44
|
+
});
|
|
45
|
+
// 继续补齐后续窗口
|
|
46
|
+
this.backfillWindows(truncated);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
// 有窗口,检查 end_time 之后是否有请求
|
|
50
|
+
this.backfillWindows(parseDate(latest.end_time));
|
|
51
|
+
}
|
|
52
|
+
/** 从 baseTime 开始,每 5h 一个窗口,直到覆盖 lastLogTime */
|
|
53
|
+
backfillWindows(baseTime) {
|
|
54
|
+
const lastLog = this.db.prepare("SELECT created_at FROM request_logs ORDER BY created_at DESC LIMIT 1").get();
|
|
55
|
+
if (!lastLog)
|
|
56
|
+
return;
|
|
57
|
+
const lastLogTime = parseDate(lastLog.created_at);
|
|
58
|
+
let windowStart = baseTime;
|
|
59
|
+
while (windowStart < lastLogTime) {
|
|
60
|
+
const windowEnd = new Date(windowStart.getTime() + WINDOW_DURATION_MS);
|
|
61
|
+
insertWindow(this.db, {
|
|
62
|
+
id: randomUUID(),
|
|
63
|
+
router_key_id: null,
|
|
64
|
+
start_time: toSqliteDatetime(windowStart),
|
|
65
|
+
end_time: toSqliteDatetime(windowEnd),
|
|
66
|
+
});
|
|
67
|
+
windowStart = windowEnd;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function truncateToMinute(date) {
|
|
72
|
+
const d = new Date(date);
|
|
73
|
+
d.setSeconds(0, 0);
|
|
74
|
+
return d;
|
|
75
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Date → SQLite datetime 文本 (YYYY-MM-DD HH:MM:SS),UTC 时区,与 DEFAULT (datetime('now')) 对齐 */
|
|
2
|
+
export function toSqliteDatetime(date) {
|
|
3
|
+
return date.toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "");
|
|
4
|
+
}
|
|
5
|
+
/** 兼容 ISO 和 SQLite datetime 格式的日期解析,均视为 UTC */
|
|
6
|
+
export function parseSqliteDatetime(s) {
|
|
7
|
+
if (s.includes("T"))
|
|
8
|
+
return new Date(s);
|
|
9
|
+
return new Date(s + "Z");
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Ft as e,K as t,Lt as n,U as r,at as i,ct as a,r as o}from"./button-qxGNBunr.js";var s=[`data-size`],c=t({__name:`Card`,props:{class:{type:[Boolean,null,String,Object,Array]},size:{default:`default`}},setup(t){let c=t;return(l,u)=>(i(),r(`div`,{"data-slot":`card`,"data-size":t.size,class:n(e(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=t({__name:`CardContent`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(t){let s=t;return(t,c)=>(i(),r(`div`,{"data-slot":`card-content`,class:n(e(o)(`px-4 group-data-[size=sm]/card:px-3`,s.class))},[a(t.$slots,`default`)],2))}});export{c as n,l as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Ft as e,K as t,Lt as n,U as r,at as i,ct as a,r as o}from"./button-qxGNBunr.js";var s=t({__name:`CardHeader`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(t){let s=t;return(t,c)=>(i(),r(`div`,{"data-slot":`card-header`,class:n(e(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(t.$slots,`default`)],2))}});export{s as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Ft as e,K as t,Lt as n,U as r,at as i,ct as a,r as o}from"./button-qxGNBunr.js";var s=t({__name:`CardTitle`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(t){let s=t;return(t,c)=>(i(),r(`div`,{"data-slot":`card-title`,class:n(e(o)(`text-base leading-snug font-medium group-data-[size=sm]/card:text-sm cn-font-heading`,s.class))},[a(t.$slots,`default`)],2))}});export{s as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{$ as e,F as t,Ft as n,G as r,H as i,I as a,J as o,K as s,Rt as c,V as l,at as u,b as d,ct as f,i as p,p as m,r as h,ut as g,yt as _,z as v}from"./button-qxGNBunr.js";import{n as y,t as b}from"./ohash.D__AXeF1-nmJ7gFbh.js";import{S as x,c as S,f as C,y as w}from"./Collection-DY9-Yue9.js";import{n as T}from"./VisuallyHidden-B_NnkONE.js";import{t as E}from"./useForwardExpose-awoGXQkg.js";import{t as D}from"./VisuallyHiddenInput-cjeTgyDe.js";import{s as O}from"./TableHeader-BKE_yVML.js";function k(e,t){return w(e)?!1:Array.isArray(e)?e.some(e=>b(e,t)):b(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=s({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 s=r,c=o,{forwardRef:d,currentElement:h}=E(),y=A(null),x=m(s,`modelValue`,c,{defaultValue:s.defaultValue??s.falseValue,passive:s.modelValue===void 0}),S=v(()=>y?.disabled.value||s.disabled),C=v(()=>b(x.value,s.trueValue)),j=v(()=>w(y?.modelValue.value)?x.value===`indeterminate`?`indeterminate`:C.value:k(y.modelValue.value,s.value));function P(){if(w(y?.modelValue.value))x.value===`indeterminate`?x.value=s.trueValue:x.value=C.value?s.falseValue:s.trueValue;else{let e=[...y.modelValue.value||[]];if(k(e,s.value)){let t=e.findIndex(e=>b(e,s.value));e.splice(t,1)}else e.push(s.value);y.modelValue.value=e}}let I=T(h),L=v(()=>s.id&&h.value?document.querySelector(`[for="${s.id}"]`)?.innerText:void 0);return F({disabled:S,state:j}),(r,o)=>(u(),l(g(n(y)?.rovingFocus.value?n(O):n(p)),e(r.$attrs,{id:r.id,ref:n(d),role:`checkbox`,"as-child":r.asChild,as:r.as,type:r.as===`button`?`button`:void 0,"aria-checked":n(M)(j.value)?`mixed`:j.value,"aria-required":r.required,"aria-label":r.$attrs[`aria-label`]||L.value,"data-state":n(N)(j.value),"data-disabled":S.value?``:void 0,disabled:S.value,focusable:n(y)?.rovingFocus.value?!S.value:void 0,onKeydown:t(a(()=>{},[`prevent`]),[`enter`]),onClick:P}),{default:_(()=>[f(r.$slots,`default`,{modelValue:n(x),state:j.value}),n(I)&&r.name&&!n(y)?(u(),l(n(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`])):i(`v-if`,!0)]),_:3},16,[`id`,`as-child`,`as`,`type`,`aria-checked`,`aria-required`,`aria-label`,`data-state`,`data-disabled`,`disabled`,`focusable`,`onKeydown`]))}}),L=s({__name:`CheckboxIndicator`,props:{forceMount:{type:Boolean,required:!1},asChild:{type:Boolean,required:!1},as:{type:null,required:!1,default:`span`}},setup(t){let{forwardRef:i}=E(),a=P();return(t,o)=>(u(),l(n(S),{present:t.forceMount||n(M)(n(a).state.value)||n(a).state.value===!0},{default:_(()=>[r(n(p),e({ref:n(i),"data-state":n(N)(n(a).state.value),"data-disabled":n(a).disabled.value?``:void 0,style:{pointerEvents:`none`},"as-child":t.asChild,as:t.as},t.$attrs),{default:_(()=>[f(t.$slots,`default`)]),_:3},16,[`data-state`,`data-disabled`,`as-child`,`as`])]),_:3},8,[`present`]))}}),R=s({__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:i}){let a=t,s=i,p=C(d(a,`class`),s);return(t,i)=>(u(),l(n(I),e({"data-slot":`checkbox`},n(p),{class:n(h)(`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`,a.class)}),{default:_(e=>[r(n(L),{"data-slot":`checkbox-indicator`,class:`[&>svg]:size-3.5 grid place-content-center text-current transition-none`},{default:_(()=>[f(t.$slots,`default`,c(o(e)),()=>[r(n(y))])]),_:2},1024)]),_:3},16,[`class`]))}});export{R as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{$ as e,Dt as t,Ft as n,G as r,H as i,J as a,K as o,Mt as s,Rt as c,V as l,at as u,ct as d,et as f,gt as p,i as m,nt as h,p as g,u as _,yt as v,z as y}from"./button-qxGNBunr.js";import{S as b,c as x,f as S,u as C}from"./Collection-DY9-Yue9.js";import{t as w}from"./useForwardExpose-awoGXQkg.js";var[T,E]=b(`CollapsibleRoot`),D=o({__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:r}){let i=e,a=g(i,`open`,r,{defaultValue:i.defaultOpen,passive:i.open===void 0}),{disabled:o,unmountOnHide:c}=s(i);return E({contentId:``,disabled:o,open:a,unmountOnHide:c,onOpenToggle:()=>{o.value||(a.value=!a.value)}}),t({open:a}),w(),(e,t)=>(u(),l(n(m),{as:e.as,"as-child":i.asChild,"data-state":n(a)?`open`:`closed`,"data-disabled":n(o)?``:void 0},{default:v(()=>[d(e.$slots,`default`,{open:n(a)})]),_:3},8,[`as`,`as-child`,`data-state`,`data-disabled`]))}}),O=o({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:o}){let s=a,c=o,g=T();g.contentId||=C(void 0,`reka-collapsible-content`);let b=t(),{forwardRef:S,currentElement:E}=w(),D=t(0),O=t(0),k=y(()=>g.open.value),A=t(k.value),j=t();p(()=>[k.value,b.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=y(()=>A.value&&g.open.value);return h(()=>{requestAnimationFrame(()=>{A.value=!1})}),_(E,`beforematch`,e=>{requestAnimationFrame(()=>{g.onOpenToggle(),c(`contentFound`)})}),(t,a)=>(u(),l(n(x),{ref_key:`presentRef`,ref:b,present:t.forceMount||n(g).open.value,"force-mount":!0},{default:v(({present:a})=>[r(n(m),e(t.$attrs,{id:n(g).contentId,ref:n(S),"as-child":s.asChild,as:t.as,hidden:a?void 0:n(g).unmountOnHide.value?``:`until-found`,"data-state":M.value?void 0:n(g).open.value?`open`:`closed`,"data-disabled":n(g).disabled?.value?``:void 0,style:{"--reka-collapsible-content-height":`${O.value}px`,"--reka-collapsible-content-width":`${D.value}px`}}),{default:v(()=>[!n(g).unmountOnHide.value||a?d(t.$slots,`default`,{key:0}):i(`v-if`,!0)]),_:2},1040,[`id`,`as-child`,`as`,`hidden`,`data-state`,`data-disabled`,`style`])]),_:3},8,[`present`]))}}),k=o({__name:`CollapsibleTrigger`,props:{asChild:{type:Boolean,required:!1},as:{type:null,required:!1,default:`button`}},setup(e){let t=e;w();let r=T();return(e,i)=>(u(),l(n(m),{type:e.as===`button`?`button`:void 0,as:e.as,"as-child":t.asChild,"aria-controls":n(r).contentId,"aria-expanded":n(r).open.value,"data-state":n(r).open.value?`open`:`closed`,"data-disabled":n(r).disabled?.value?``:void 0,disabled:n(r).disabled?.value,onClick:n(r).onOpenToggle},{default:v(()=>[d(e.$slots,`default`)]),_:3},8,[`type`,`as`,`as-child`,`aria-controls`,`aria-expanded`,`data-state`,`data-disabled`,`disabled`,`onClick`]))}}),A=o({__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:r}){let i=S(t,r);return(t,r)=>(u(),l(n(D),e({"data-slot":`collapsible`},n(i)),{default:v(e=>[d(t.$slots,`default`,c(a(e)))]),_:3},16))}}),j=o({__name:`CollapsibleContent`,props:{forceMount:{type:Boolean},asChild:{type:Boolean},as:{}},setup(t){let r=t;return(t,i)=>(u(),l(n(O),e({"data-slot":`collapsible-content`},r),{default:v(()=>[d(t.$slots,`default`)]),_:3},16))}}),M=o({__name:`CollapsibleTrigger`,props:{asChild:{type:Boolean},as:{}},setup(t){let r=t;return(t,i)=>(u(),l(n(k),e({"data-slot":`collapsible-trigger`},r),{default:v(()=>[d(t.$slots,`default`)]),_:3},16))}});export{j as n,A as r,M as t};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{Dt as e,Et as t,Ft as n,H as r,It as i,K as a,M as o,Mt as s,Nt as c,R as l,S as u,V as d,Vt as f,X as p,Y as m,_ as h,_t as g,a as _,at as v,c as ee,ct as y,d as te,et as b,g as ne,gt as x,i as S,jt as re,l as C,o as ie,ot as w,pt as ae,q as T,rt as E,s as oe,u as se,v as D,wt as ce,y as le,yt as ue,z as O,zt as de}from"./button-qxGNBunr.js";import{t as k}from"./useForwardExpose-awoGXQkg.js";function A(e,t){let n=typeof e==`string`&&!t?`${e}Context`:t,r=Symbol(n);return[t=>{let n=p(r,t);if(n||n===null)return n;throw Error(`Injection \`${r.toString()}\` not found. Component must be used within ${Array.isArray(e)?`one of the following components: ${e.join(`, `)}`:`\`${e}\``}`)},e=>(w(r,e),e)]}function j(){let e=document.activeElement;if(e==null)return null;for(;e!=null&&e.shadowRoot!=null&&e.shadowRoot.activeElement!=null;)e=e.shadowRoot.activeElement;return e}function M(e,t,n){let r=n.originalEvent.target,i=new CustomEvent(e,{bubbles:!1,cancelable:!0,detail:n});t&&r.addEventListener(e,t,{once:!0}),r.dispatchEvent(i)}function N(e){return e==null}var[P,fe]=A(`ConfigProvider`);function F(e){if(typeof e!=`object`||!e)return!1;let t=Object.getPrototypeOf(e);return t!==null&&t!==Object.prototype&&Object.getPrototypeOf(t)!==null||Symbol.iterator in e?!1:Symbol.toStringTag in e?Object.prototype.toString.call(e)===`[object Module]`:!0}function I(e,t,n=`.`,r){if(!F(t))return I(e,{},n,r);let i={...t};for(let t of Object.keys(e)){if(t===`__proto__`||t===`constructor`)continue;let a=e[t];a!=null&&(r&&r(i,t,a,n)||(Array.isArray(a)&&Array.isArray(i[t])?i[t]=[...a,...i[t]]:F(a)&&F(i[t])?i[t]=I(a,i[t],(n?`${n}.`:``)+t.toString(),r):i[t]=a))}return i}function pe(e){return(...t)=>t.reduce((t,n)=>I(t,n,``,e),{})}var L=pe(),me=h(()=>{let t=e(new Map),n=e(),r=O(()=>{for(let e of t.value.values())if(e)return!0;return!1}),i=P({scrollBody:e(!0)}),a=null,o=()=>{document.body.style.paddingRight=``,document.body.style.marginRight=``,document.body.style.pointerEvents=``,document.documentElement.style.removeProperty(`--scrollbar-width`),document.body.style.overflow=n.value??``,le&&a?.(),n.value=void 0};return x(r,(e,t)=>{if(!D)return;if(!e){t&&o();return}n.value===void 0&&(n.value=document.body.style.overflow);let s=window.innerWidth-document.documentElement.clientWidth,c={padding:s,margin:0},l=i.scrollBody?.value?typeof i.scrollBody.value==`object`?L({padding:i.scrollBody.value.padding===!0?s:i.scrollBody.value.padding,margin:i.scrollBody.value.margin===!0?s:i.scrollBody.value.margin},c):c:{padding:0,margin:0};s>0&&(document.body.style.paddingRight=typeof l.padding==`number`?`${l.padding}px`:String(l.padding),document.body.style.marginRight=typeof l.margin==`number`?`${l.margin}px`:String(l.margin),document.documentElement.style.setProperty(`--scrollbar-width`,`${s}px`),document.body.style.overflow=`hidden`),le&&(a=se(document,`touchmove`,e=>_e(e),{passive:!1})),b(()=>{r.value&&(document.body.style.pointerEvents=`none`,document.body.style.overflow=`hidden`)})},{immediate:!0,flush:`sync`}),t});function he(e){let t=Math.random().toString(36).substring(2,7),n=me();n.value.set(t,e??!1);let r=O({get:()=>n.value.get(t)??!1,set:e=>n.value.set(t,e)});return u(()=>{n.value.delete(t)}),r}function ge(e){let t=window.getComputedStyle(e);if(t.overflowX===`scroll`||t.overflowY===`scroll`||t.overflowX===`auto`&&e.clientWidth<e.scrollWidth||t.overflowY===`auto`&&e.clientHeight<e.scrollHeight)return!0;{let t=e.parentNode;return!(t instanceof Element)||t.tagName===`BODY`?!1:ge(t)}}function _e(e){let t=e||window.event,n=t.target;return n instanceof Element&&ge(n)?!1:t.touches.length>1?!0:(t.preventDefault&&t.cancelable&&t.preventDefault(),!1)}function ve(t){let n=P({dir:e(`ltr`)});return O(()=>t?.value||n.dir?.value||`ltr`)}function R(e){let t=T(),n=t?.type.emits,r={};return n?.length||console.warn(`No emitted event found. Please check component: ${t?.type.__name}`),n?.forEach(t=>{r[f(i(t))]=(...n)=>e(t,...n)}),r}function z(e){let t=T(),n=Object.keys(t?.type.props??{}).reduce((e,n)=>{let r=(t?.type.props[n]).default;return r!==void 0&&(e[n]=r),e},{}),r=re(e);return O(()=>{let e={},a=t?.vnode.props??{};return Object.keys(a).forEach(t=>{e[i(t)]=a[t]}),Object.keys({...n,...e}).reduce((e,t)=>(r.value[t]!==void 0&&(e[t]=r.value[t]),e),{})})}function ye(e,t){let n=z(e),r=t?R(t):{};return O(()=>({...n.value,...r}))}var be=function(e){return typeof document>`u`?null:(Array.isArray(e)?e[0]:e).ownerDocument.body},B=new WeakMap,V=new WeakMap,H={},U=0,W=function(e){return e&&(e.host||W(e.parentNode))},xe=function(e,t){return t.map(function(t){if(e.contains(t))return t;var n=W(t);return n&&e.contains(n)?n:(console.error(`aria-hidden`,t,`in not contained inside`,e,`. Doing nothing`),null)}).filter(function(e){return!!e})},Se=function(e,t,n,r){var i=xe(t,Array.isArray(e)?e:[e]);H[n]||(H[n]=new WeakMap);var a=H[n],o=[],s=new Set,c=new Set(i),l=function(e){!e||s.has(e)||(s.add(e),l(e.parentNode))};i.forEach(l);var u=function(e){!e||c.has(e)||Array.prototype.forEach.call(e.children,function(e){if(s.has(e))u(e);else try{var t=e.getAttribute(r),i=t!==null&&t!==`false`,c=(B.get(e)||0)+1,l=(a.get(e)||0)+1;B.set(e,c),a.set(e,l),o.push(e),c===1&&i&&V.set(e,!0),l===1&&e.setAttribute(n,`true`),i||e.setAttribute(r,`true`)}catch(t){console.error(`aria-hidden: cannot operate on `,e,t)}})};return u(t),s.clear(),U++,function(){o.forEach(function(e){var t=B.get(e)-1,i=a.get(e)-1;B.set(e,t),a.set(e,i),t||(V.has(e)||e.removeAttribute(r),V.delete(e)),i||e.removeAttribute(n)}),U--,U||(B=new WeakMap,B=new WeakMap,V=new WeakMap,H={})}},Ce=function(e,t,n){n===void 0&&(n=`data-aria-hidden`);var r=Array.from(Array.isArray(e)?e:[e]),i=t||be(e);return i?(r.push.apply(r,Array.from(i.querySelectorAll(`[aria-live], script`))),Se(r,i,n,`aria-hidden`)):function(){return null}};function we(e){let t;x(()=>C(e),e=>{let n=!1;try{n=!!e?.closest(`[popover]:not(:popover-open)`)}catch{}e&&!n?t=Ce(e):t&&t()}),E(()=>{t&&t()})}var Te=0;function Ee(e,t=`reka`){if(e)return e;let n;return n=`useId`in o?ae?.():P({useId:void 0}).useId?.()??`${++Te}`,t?`${t}-${n}`:n}function G(t,n){let r=e(t);function i(e){return n[r.value][e]??r.value}return{state:r,dispatch:e=>{r.value=i(e)}}}function De(t,n){let r=e({}),i=e(`none`),a=e(t),o=t.value?`mounted`:`unmounted`,s,c=n.value?.ownerDocument.defaultView??oe,{state:l,dispatch:u}=G(o,{mounted:{UNMOUNT:`unmounted`,ANIMATION_OUT:`unmountSuspended`},unmountSuspended:{MOUNT:`mounted`,ANIMATION_END:`unmounted`},unmounted:{MOUNT:`mounted`}}),d=e=>{if(D){let t=new CustomEvent(e,{bubbles:!1,cancelable:!1});n.value?.dispatchEvent(t)}};x(t,async(e,t)=>{let a=t!==e;if(await b(),a){let a=i.value,o=K(n.value);e?(u(`MOUNT`),d(`enter`),o===`none`&&d(`after-enter`)):o===`none`||o===`undefined`||r.value?.display===`none`?(u(`UNMOUNT`),d(`leave`),d(`after-leave`)):t&&a!==o?(u(`ANIMATION_OUT`),d(`leave`)):(u(`UNMOUNT`),d(`after-leave`))}},{immediate:!0});let f=e=>{let t=K(n.value),r=t.includes(CSS.escape(e.animationName)),i=l.value===`mounted`?`enter`:`leave`;if(e.target===n.value&&r&&(d(`after-${i}`),u(`ANIMATION_END`),!a.value)){let e=n.value.style.animationFillMode;n.value.style.animationFillMode=`forwards`,s=c?.setTimeout(()=>{n.value?.style.animationFillMode===`forwards`&&(n.value.style.animationFillMode=e)})}e.target===n.value&&t===`none`&&u(`ANIMATION_END`)},p=e=>{e.target===n.value&&(i.value=K(n.value))},m=x(n,(e,t)=>{e?(r.value=getComputedStyle(e),e.addEventListener(`animationstart`,p),e.addEventListener(`animationcancel`,f),e.addEventListener(`animationend`,f)):(u(`ANIMATION_END`),s!==void 0&&c?.clearTimeout(s),t?.removeEventListener(`animationstart`,p),t?.removeEventListener(`animationcancel`,f),t?.removeEventListener(`animationend`,f))},{immediate:!0}),h=x(l,()=>{let e=K(n.value);i.value=l.value===`mounted`?e:`none`});return E(()=>{m(),h()}),{isPresent:O(()=>[`mounted`,`unmountSuspended`].includes(l.value))}}function K(e){return e&&getComputedStyle(e).animationName||`none`}var Oe=a({name:`Presence`,props:{present:{type:Boolean,required:!0},forceMount:{type:Boolean}},slots:{},setup(t,{slots:n,expose:r}){let{present:i,forceMount:a}=s(t),o=e(),{isPresent:c}=De(i,o);r({present:c});let l=n.default({present:c.value});l=ie(l||[]);let u=T();if(l&&l?.length>1){let e=u?.parent?.type.name?`<${u.parent.type.name} />`:`component`;throw Error([`Detected an invalid children for \`${e}\` for \`Presence\` component.`,``,"Note: Presence works similarly to `v-if` directly, but it waits for animation/transition to finished before unmounting. So it expect only one direct child of valid VNode type.",`You can apply a few solutions:`,["Provide a single child element so that `presence` directive attach correctly.",`Ensure the first child is an actual element instead of a raw text node or comment node.`].map(e=>` - ${e}`).join(`
|
|
2
|
+
`)].join(`
|
|
3
|
+
`))}return()=>a.value||i.value||c.value?m(n.default({present:c.value})[0],{ref:e=>{let t=C(e);return t?.hasAttribute===void 0||(t?.hasAttribute(`data-reka-popper-content-wrapper`)?o.value=t.firstElementChild:o.value=t),t}}):null}});function q(){let t=e();return{primitiveElement:t,currentElement:O(()=>[`#text`,`#comment`].includes(t.value?.$el.nodeName)?t.value?.$el.nextElementSibling:C(t))}}var ke=`dismissableLayer.pointerDownOutside`,Ae=`dismissableLayer.focusOutside`;function J(e,t){if(!(t instanceof Element))return!1;let n=t.closest(`[data-dismissable-layer]`),r=e.dataset.dismissableLayer===``?e:e.querySelector(`[data-dismissable-layer]`),i=Array.from(e.ownerDocument.querySelectorAll(`[data-dismissable-layer]`));return!!(n&&(r===n||i.indexOf(r)<i.indexOf(n)))}function je(t,n,r=!0){let i=n?.value?.ownerDocument??globalThis?.document,a=e(!1),o=e(()=>{});return g(e=>{if(!D||!c(r))return;let s=async e=>{let r=e.target;if(!(!n?.value||!r)){if(J(n.value,r)){a.value=!1;return}if(e.target&&!a.value){let n={originalEvent:e};function r(){M(ke,t,n)}e.pointerType===`touch`?(i.removeEventListener(`click`,o.value),o.value=r,i.addEventListener(`click`,o.value,{once:!0})):r()}else i.removeEventListener(`click`,o.value);a.value=!1}},l=window.setTimeout(()=>{i.addEventListener(`pointerdown`,s)},0);e(()=>{window.clearTimeout(l),i.removeEventListener(`pointerdown`,s),i.removeEventListener(`click`,o.value)})}),{onPointerDownCapture:()=>{c(r)&&(a.value=!0)}}}function Me(t,n,r=!0){let i=n?.value?.ownerDocument??globalThis?.document,a=e(!1);return g(e=>{if(!D||!c(r))return;let o=async e=>{if(!n?.value)return;await b(),await b();let r=e.target;!n.value||!r||J(n.value,r)||e.target&&!a.value&&M(Ae,t,{originalEvent:e})};i.addEventListener(`focusin`,o),e(()=>i.removeEventListener(`focusin`,o))}),{onFocusCapture:()=>{c(r)&&(a.value=!0)},onBlurCapture:()=>{c(r)&&(a.value=!1)}}}var Y=t({layersRoot:new Set,layersWithOutsidePointerEventsDisabled:new Set,originalBodyPointerEvents:void 0,branches:new Set}),Ne=a({__name:`DismissableLayer`,props:{disableOutsidePointerEvents:{type:Boolean,required:!1,default:!1},asChild:{type:Boolean,required:!1},as:{type:null,required:!1}},emits:[`escapeKeyDown`,`pointerDownOutside`,`focusOutside`,`interactOutside`,`dismiss`],setup(e,{emit:t}){let r=e,i=t,{forwardRef:a,currentElement:o}=k(),s=O(()=>o.value?.ownerDocument??globalThis.document),c=O(()=>Y.layersRoot),l=O(()=>o.value?Array.from(c.value).indexOf(o.value):-1),u=O(()=>Y.layersWithOutsidePointerEventsDisabled.size>0),f=O(()=>{let e=Array.from(c.value),[t]=[...Y.layersWithOutsidePointerEventsDisabled].slice(-1),n=e.indexOf(t);return l.value>=n}),p=je(async e=>{let t=[...Y.branches].some(t=>t?.contains(e.target));!f.value||t||(i(`pointerDownOutside`,e),i(`interactOutside`,e),await b(),e.defaultPrevented||i(`dismiss`))},o),m=Me(e=>{[...Y.branches].some(t=>t?.contains(e.target))||(i(`focusOutside`,e),i(`interactOutside`,e),e.defaultPrevented||i(`dismiss`))},o);return ee(`Escape`,e=>{l.value===c.value.size-1&&(i(`escapeKeyDown`,e),e.defaultPrevented||i(`dismiss`))}),g(e=>{o.value&&(r.disableOutsidePointerEvents&&(Y.layersWithOutsidePointerEventsDisabled.size===0&&(Y.originalBodyPointerEvents=s.value.body.style.pointerEvents,s.value.body.style.pointerEvents=`none`),Y.layersWithOutsidePointerEventsDisabled.add(o.value)),c.value.add(o.value),e(()=>{r.disableOutsidePointerEvents&&Y.layersWithOutsidePointerEventsDisabled.size===1&&!N(Y.originalBodyPointerEvents)&&(s.value.body.style.pointerEvents=Y.originalBodyPointerEvents)}))}),g(e=>{e(()=>{o.value&&(c.value.delete(o.value),Y.layersWithOutsidePointerEventsDisabled.delete(o.value))})}),(e,t)=>(v(),d(n(S),{ref:n(a),"as-child":e.asChild,as:e.as,"data-dismissable-layer":``,style:de({pointerEvents:u.value?f.value?`auto`:`none`:void 0}),onFocusCapture:n(m).onFocusCapture,onBlurCapture:n(m).onBlurCapture,onPointerdownCapture:n(p).onPointerDownCapture},{default:ue(()=>[y(e.$slots,`default`)]),_:3},8,[`as-child`,`as`,`style`,`onFocusCapture`,`onBlurCapture`,`onPointerdownCapture`]))}}),Pe=ne(()=>e([]));function Fe(){let e=Pe();return{add(t){let n=e.value[0];t!==n&&n?.pause(),e.value=X(e.value,t),e.value.unshift(t)},remove(t){e.value=X(e.value,t),e.value[0]?.resume()}}}function X(e,t){let n=[...e],r=n.indexOf(t);return r!==-1&&n.splice(r,1),n}var Z=`focusScope.autoFocusOnMount`,Q=`focusScope.autoFocusOnUnmount`,Ie={bubbles:!1,cancelable:!0};function Le(e,{select:t=!1}={}){let n=j();for(let r of e)if($(r,{select:t}),j()!==n)return!0}function Re(e){let t=ze(e);return[Be(t,e),Be(t.reverse(),e)]}function ze(e){let t=[],n=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,{acceptNode:e=>{let t=e.tagName===`INPUT`&&e.type===`hidden`;return e.disabled||e.hidden||t?NodeFilter.FILTER_SKIP:e.tabIndex>=0?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}});for(;n.nextNode();)t.push(n.currentNode);return t}function Be(e,t){for(let n of e)if(!Ve(n,{upTo:t}))return n}function Ve(e,{upTo:t}){if(getComputedStyle(e).visibility===`hidden`)return!0;for(;e;){if(t!==void 0&&e===t)return!1;if(getComputedStyle(e).display===`none`)return!0;e=e.parentElement}return!1}function He(e){return e instanceof HTMLInputElement&&`select`in e}function $(e,{select:t=!1}={}){if(e&&e.focus){let n=j();e.focus({preventScroll:!0}),e!==n&&He(e)&&t&&e.select()}}var Ue=a({__name:`FocusScope`,props:{loop:{type:Boolean,required:!1,default:!1},trapped:{type:Boolean,required:!1,default:!1},asChild:{type:Boolean,required:!1},as:{type:null,required:!1}},emits:[`mountAutoFocus`,`unmountAutoFocus`],setup(r,{emit:i}){let a=r,o=i,{currentRef:s,currentElement:c}=k(),l=e(null),u=Fe(),f=t({paused:!1,pause(){this.paused=!0},resume(){this.paused=!1}});g(e=>{if(!D)return;let t=c.value;if(!a.trapped)return;function n(e){if(f.paused||!t)return;let n=e.target;t.contains(n)?l.value=n:$(l.value,{select:!0})}function r(e){if(f.paused||!t)return;let n=e.relatedTarget;n!==null&&(t.contains(n)||$(l.value,{select:!0}))}function i(e){let n=l.value;n!==null&&e.some(e=>e.removedNodes.length>0)&&(t.contains(n)||$(t))}document.addEventListener(`focusin`,n),document.addEventListener(`focusout`,r);let o=new MutationObserver(i);t&&o.observe(t,{childList:!0,subtree:!0}),e(()=>{document.removeEventListener(`focusin`,n),document.removeEventListener(`focusout`,r),o.disconnect()})}),g(async e=>{let t=c.value;if(await b(),!t)return;u.add(f);let n=j();if(!t.contains(n)){let e=new CustomEvent(Z,Ie);t.addEventListener(Z,e=>o(`mountAutoFocus`,e)),t.dispatchEvent(e),e.defaultPrevented||(Le(ze(t),{select:!0}),j()===n&&$(t))}e(()=>{t.removeEventListener(Z,e=>o(`mountAutoFocus`,e));let e=new CustomEvent(Q,Ie),r=e=>{o(`unmountAutoFocus`,e)};t.addEventListener(Q,r),t.dispatchEvent(e),setTimeout(()=>{e.defaultPrevented||$(n??document.body,{select:!0}),t.removeEventListener(Q,r),u.remove(f)},0)})});function p(e){if(!a.loop&&!a.trapped||f.paused)return;let t=e.key===`Tab`&&!e.altKey&&!e.ctrlKey&&!e.metaKey,n=j();if(t&&n){let t=e.currentTarget,[r,i]=Re(t);r&&i?!e.shiftKey&&n===i?(e.preventDefault(),a.loop&&$(r,{select:!0})):e.shiftKey&&n===r&&(e.preventDefault(),a.loop&&$(i,{select:!0})):n===t&&e.preventDefault()}}return(e,t)=>(v(),d(n(S),{ref_key:`currentRef`,ref:s,tabindex:`-1`,"as-child":e.asChild,as:e.as,onKeydown:p},{default:ue(()=>[y(e.$slots,`default`)]),_:3},8,[`as-child`,`as`]))}}),We=[`Enter`,` `],Ge=[`ArrowDown`,`PageUp`,`Home`],Ke=[`ArrowUp`,`PageDown`,`End`];[...Ge,...Ke],[...We],[...We];function qe(e){return e?`open`:`closed`}function Je(e){let t=j();for(let n of e)if(n===t||(n.focus(),j()!==t))return}var Ye=a({__name:`Teleport`,props:{to:{type:null,required:!1,default:`body`},disabled:{type:Boolean,required:!1},defer:{type:Boolean,required:!1},forceMount:{type:Boolean,required:!1}},setup(e){let t=te();return(e,i)=>n(t)||e.forceMount?(v(),d(l,{key:0,to:e.to,disabled:e.disabled,defer:e.defer},[y(e.$slots,`default`)],8,[`to`,`disabled`,`defer`])):r(`v-if`,!0)}}),Xe=`data-reka-collection-item`;function Ze(t={}){let{key:n=``,isProvider:r=!1}=t,i=`${n}CollectionProvider`,o;if(r){let t=e(new Map);o={collectionRef:e(),itemMap:t},w(i,o)}else o=p(i);let s=(e=!1)=>{let t=o.collectionRef.value;if(!t)return[];let n=Array.from(t.querySelectorAll(`[${Xe}]`)),r=Array.from(o.itemMap.value.values()).sort((e,t)=>n.indexOf(e.ref)-n.indexOf(t.ref));return e?r:r.filter(e=>e.ref.dataset.disabled!==``)},c=a({name:`CollectionSlot`,inheritAttrs:!1,setup(e,{slots:t,attrs:n}){let{primitiveElement:r,currentElement:i}=q();return x(i,()=>{o.collectionRef.value=i.value}),()=>m(_,{ref:r,...n},t)}}),l=a({name:`CollectionItem`,inheritAttrs:!1,props:{value:{validator:()=>!0}},setup(e,{slots:t,attrs:n}){let{primitiveElement:r,currentElement:i}=q();return g(t=>{if(i.value){let n=ce(i.value);o.itemMap.value.set(n,{ref:i.value,value:e.value}),t(()=>o.itemMap.value.delete(n))}}),()=>m(_,{...n,[Xe]:``,ref:r},t)}});return{getItems:s,reactiveItems:O(()=>Array.from(o.itemMap.value.values())),itemMapSize:O(()=>o.itemMap.value.size),CollectionSlot:c,CollectionItem:l}}export{A as S,L as _,Ue as a,M as b,Oe as c,we as d,ye as f,he as g,ve as h,qe as i,G as l,R as m,Ye as n,Ne as o,z as p,Je as r,q as s,Ze as t,Ee as u,P as v,j as x,N as y};
|