@cs2dak/contract 0.2.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/analysis.ts +222 -0
- package/src/cohort.ts +59 -0
- package/src/contract.test.ts +559 -0
- package/src/demo-package.ts +38 -0
- package/src/index.ts +11 -599
- package/src/leaderboard.ts +102 -0
- package/src/player.ts +96 -0
- package/src/qa.ts +21 -0
- package/src/scoring.ts +79 -0
- package/src/series.ts +77 -0
- package/src/team.ts +72 -0
- package/src/upstream.ts +90 -0
- package/src/workspace.ts +167 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { PrismResult } from "@rivalhub/rival-rating";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { teamKeySchema } from "cs2-demo-format";
|
|
4
|
+
import { seasonCohortBundleSchema } from "./cohort.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 赛季排行榜展示模型。由 @cs2dak/presentation 从 SeasonCohortBundle 派生,
|
|
8
|
+
* 吸收 RivalHub 排行榜的产品信息架构(分栏、标签、默认排序),但:
|
|
9
|
+
* - 不含数据库/权限/路由语义(规则 8);身份用中立的 playerKey/teamKeys。
|
|
10
|
+
* - 缺失指标保持 null,不伪造 0(规则 6)。
|
|
11
|
+
* - 不计算评分公式;rrV1/accountRR/prism 均来自 cohort 已接线的 rival-rating 结果(规则 5)。
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** 排行榜可展示的指标 key。值统一存原始量纲,缩放与单位由 format 决定。 */
|
|
15
|
+
export const leaderboardMetricKeySchema = z.enum([
|
|
16
|
+
"maps",
|
|
17
|
+
// 评分门面
|
|
18
|
+
"rivalhubRR", // accountRR(绝对刻度 RivalHub RR)
|
|
19
|
+
"hltvRating", // rrV1(HLTV Rating 2.0 量纲,已被逆向验证)
|
|
20
|
+
// core
|
|
21
|
+
"adr",
|
|
22
|
+
"kd",
|
|
23
|
+
"kpr",
|
|
24
|
+
"hsPercent",
|
|
25
|
+
// impact(产量家族:每 100 回合 X 次)
|
|
26
|
+
"firstKillPer100",
|
|
27
|
+
"multiKillPer100",
|
|
28
|
+
"clutchPer100",
|
|
29
|
+
"openingDuelWinRate",
|
|
30
|
+
// advanced
|
|
31
|
+
"kast",
|
|
32
|
+
"utilityDamagePerRound",
|
|
33
|
+
"awpKillsPerRound",
|
|
34
|
+
"awpKillRate",
|
|
35
|
+
"flashAssistPerRound",
|
|
36
|
+
"tradeKillRate"
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 渲染格式。所有百分比类指标在 builder 中已统一为 0–100 刻度,故只有一个 percent 格式:
|
|
41
|
+
* - integer: 整数
|
|
42
|
+
* - rating: 2 位小数(RR / HLTV)
|
|
43
|
+
* - adr: 1 位小数
|
|
44
|
+
* - ratio: 2 位小数(K/D、KPR、每 100 回合产量 FK/MK/C、每回合计数)
|
|
45
|
+
* - percent: 值已是 0–100,1 位小数 + %(HS% / KAST% / Entry% / AWP%)
|
|
46
|
+
*/
|
|
47
|
+
export const leaderboardFormatSchema = z.enum([
|
|
48
|
+
"integer",
|
|
49
|
+
"rating",
|
|
50
|
+
"adr",
|
|
51
|
+
"ratio",
|
|
52
|
+
"percent"
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
export const leaderboardColumnSchema = z.object({
|
|
56
|
+
key: leaderboardMetricKeySchema,
|
|
57
|
+
label: z.string(),
|
|
58
|
+
format: leaderboardFormatSchema,
|
|
59
|
+
/** 列说明 / tooltip;无则 null。 */
|
|
60
|
+
description: z.string().nullable()
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
export const leaderboardViewKeySchema = z.enum(["core", "impact", "advanced"]);
|
|
64
|
+
|
|
65
|
+
export const leaderboardViewSchema = z.object({
|
|
66
|
+
key: leaderboardViewKeySchema,
|
|
67
|
+
label: z.string(),
|
|
68
|
+
/** 默认排序列(始终降序)。 */
|
|
69
|
+
defaultSort: leaderboardMetricKeySchema,
|
|
70
|
+
columns: z.array(leaderboardColumnSchema)
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
export const seasonLeaderboardRowSchema = z.object({
|
|
74
|
+
playerKey: z.string(),
|
|
75
|
+
name: z.string(),
|
|
76
|
+
steamIds: z.array(z.string()),
|
|
77
|
+
externalUserId: z.string().nullable(),
|
|
78
|
+
teamKeys: z.array(teamKeySchema),
|
|
79
|
+
mapCount: z.number().int().positive(),
|
|
80
|
+
confidence: z.number().min(0).max(1),
|
|
81
|
+
/** 每个指标 key 都有值;不可得为 null(不伪造 0)。 */
|
|
82
|
+
metrics: z.record(leaderboardMetricKeySchema, z.number().nullable()),
|
|
83
|
+
/** PRISM 风格画像,仅表达风格,不进入排序;缺失为 null。 */
|
|
84
|
+
prism: z.custom<PrismResult>().nullable()
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
export const seasonLeaderboardModelSchema = z.object({
|
|
88
|
+
version: z.literal("cs2-demo-analysis-kit/leaderboard-0.1"),
|
|
89
|
+
weightsVersion: z.string(),
|
|
90
|
+
matchCount: z.number().int().nonnegative(),
|
|
91
|
+
provenance: seasonCohortBundleSchema.shape.provenance,
|
|
92
|
+
views: z.array(leaderboardViewSchema),
|
|
93
|
+
rows: z.array(seasonLeaderboardRowSchema)
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
export type LeaderboardMetricKey = z.infer<typeof leaderboardMetricKeySchema>;
|
|
97
|
+
export type LeaderboardFormat = z.infer<typeof leaderboardFormatSchema>;
|
|
98
|
+
export type LeaderboardColumn = z.infer<typeof leaderboardColumnSchema>;
|
|
99
|
+
export type LeaderboardViewKey = z.infer<typeof leaderboardViewKeySchema>;
|
|
100
|
+
export type LeaderboardView = z.infer<typeof leaderboardViewSchema>;
|
|
101
|
+
export type SeasonLeaderboardRow = z.infer<typeof seasonLeaderboardRowSchema>;
|
|
102
|
+
export type SeasonLeaderboardModel = z.infer<typeof seasonLeaderboardModelSchema>;
|
package/src/player.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { teamKeySchema } from "cs2-demo-format";
|
|
3
|
+
import { accountContextAvailabilitySchema } from "./analysis.js";
|
|
4
|
+
import { leaderboardMetricKeySchema } from "./leaderboard.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 选手赛季档案。由 @cs2dak/presentation 从 SeasonCohortBundle 中单个选手派生。
|
|
8
|
+
* 产品中立:身份用 playerKey/steamIds/externalUserId,不含数据库/路由/权限语义(规则 8)。
|
|
9
|
+
* 不算评分公式;rating/style 均来自 cohort 已接线的 rival-rating 结果(规则 5)。
|
|
10
|
+
* 缺失保持 null(规则 6);style 在 PRISM 缺失时整体为 null。
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export const rrBreakdownEntrySchema = z.object({
|
|
14
|
+
key: z.enum(["combat", "trade", "clutch", "objective", "utility"]),
|
|
15
|
+
label: z.string(),
|
|
16
|
+
value: z.number()
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
/** PRISM 单根轴的展示项(八维风格雷达)。 */
|
|
20
|
+
export const playerStyleAxisSchema = z.object({
|
|
21
|
+
key: z.string(), // PrismAxisKey
|
|
22
|
+
label: z.string(),
|
|
23
|
+
percentile: z.number().min(0).max(100)
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export const playerStyleSchema = z.object({
|
|
27
|
+
weightsVersion: z.string(),
|
|
28
|
+
/** 整体着色依据:RR 百分位(0–100)。 */
|
|
29
|
+
rrPercentile: z.number().min(0).max(100),
|
|
30
|
+
/** 八根轴,按 PRISM_AXIS_ORDER 排序。 */
|
|
31
|
+
axes: z.array(playerStyleAxisSchema)
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
export const playerSeasonTrendPointSchema = z.object({
|
|
35
|
+
matchId: z.string(),
|
|
36
|
+
rivalhubRR: z.number().nonnegative(),
|
|
37
|
+
hltvRating: z.number().nonnegative()
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export const playerWeaponProfileEntrySchema = z.object({
|
|
41
|
+
weapon: z.string(),
|
|
42
|
+
label: z.string(),
|
|
43
|
+
kills: z.number().int().nonnegative(),
|
|
44
|
+
killSharePercent: z.number().min(0).max(100),
|
|
45
|
+
headshotPercent: z.number().min(0).max(100).nullable(),
|
|
46
|
+
tradeKillPercent: z.number().min(0).max(100).nullable(),
|
|
47
|
+
noScopePercent: z.number().min(0).max(100).nullable(),
|
|
48
|
+
throughSmokePercent: z.number().min(0).max(100).nullable(),
|
|
49
|
+
wallbangPercent: z.number().min(0).max(100).nullable(),
|
|
50
|
+
averagePenetratedObjects: z.number().nonnegative().nullable()
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export const playerSeasonProfileSchema = z.object({
|
|
54
|
+
version: z.literal("cs2-demo-analysis-kit/player-profile-0.1"),
|
|
55
|
+
weightsVersion: z.string(),
|
|
56
|
+
playerKey: z.string(),
|
|
57
|
+
name: z.string(),
|
|
58
|
+
steamIds: z.array(z.string()),
|
|
59
|
+
externalUserId: z.string().nullable(),
|
|
60
|
+
teamKeys: z.array(teamKeySchema),
|
|
61
|
+
mapCount: z.number().int().positive(),
|
|
62
|
+
confidence: z.number().min(0).max(1),
|
|
63
|
+
accountContextStatus: z.object({
|
|
64
|
+
buyDelta: accountContextAvailabilitySchema,
|
|
65
|
+
manState: accountContextAvailabilitySchema
|
|
66
|
+
}),
|
|
67
|
+
rating: z.object({
|
|
68
|
+
rivalhubRR: z.number().nonnegative(),
|
|
69
|
+
rivalhubRRRaw: z.number(),
|
|
70
|
+
hltvRating: z.number().nonnegative(),
|
|
71
|
+
hltvPercentile: z.number().min(0).max(100),
|
|
72
|
+
breakdown: z.array(rrBreakdownEntrySchema)
|
|
73
|
+
}),
|
|
74
|
+
/** 与排行榜同口径的展示指标(共享 SEASON_STAT_VIEWS 渲染列)。 */
|
|
75
|
+
metrics: z.record(leaderboardMetricKeySchema, z.number().nullable()),
|
|
76
|
+
weapons: z.array(playerWeaponProfileEntrySchema),
|
|
77
|
+
highlights: z.object({
|
|
78
|
+
wallbangKills: z.number().int().nonnegative().nullable(),
|
|
79
|
+
noScopeKills: z.number().int().nonnegative().nullable(),
|
|
80
|
+
throughSmokeKills: z.number().int().nonnegative().nullable(),
|
|
81
|
+
collateralKills: z.number().int().nonnegative().nullable()
|
|
82
|
+
}),
|
|
83
|
+
style: playerStyleSchema.nullable(),
|
|
84
|
+
/** 每场 RR 趋势,按 matchId 升序。 */
|
|
85
|
+
perMatch: z.array(playerSeasonTrendPointSchema),
|
|
86
|
+
/** 相对赛季 cohort 的强项 / 弱项(按技能类指标的 cohort 百分位派生)。 */
|
|
87
|
+
strengths: z.array(z.string()),
|
|
88
|
+
weaknesses: z.array(z.string())
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
export type RRBreakdownEntry = z.infer<typeof rrBreakdownEntrySchema>;
|
|
92
|
+
export type PlayerStyleAxis = z.infer<typeof playerStyleAxisSchema>;
|
|
93
|
+
export type PlayerStyle = z.infer<typeof playerStyleSchema>;
|
|
94
|
+
export type PlayerSeasonTrendPoint = z.infer<typeof playerSeasonTrendPointSchema>;
|
|
95
|
+
export type PlayerWeaponProfileEntry = z.infer<typeof playerWeaponProfileEntrySchema>;
|
|
96
|
+
export type PlayerSeasonProfile = z.infer<typeof playerSeasonProfileSchema>;
|
package/src/qa.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const qaIssueSchema = z.object({
|
|
4
|
+
severity: z.enum(["info", "warning", "error"]),
|
|
5
|
+
code: z.string().min(1),
|
|
6
|
+
message: z.string().min(1),
|
|
7
|
+
path: z.string().optional()
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export const qaReportSchema = z.object({
|
|
11
|
+
ok: z.boolean(),
|
|
12
|
+
summary: z.object({
|
|
13
|
+
issueCount: z.number().int().nonnegative(),
|
|
14
|
+
errorCount: z.number().int().nonnegative(),
|
|
15
|
+
warningCount: z.number().int().nonnegative()
|
|
16
|
+
}),
|
|
17
|
+
issues: z.array(qaIssueSchema)
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export type QaIssue = z.infer<typeof qaIssueSchema>;
|
|
21
|
+
export type QaReport = z.infer<typeof qaReportSchema>;
|
package/src/scoring.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { RRIndicators } from "@rivalhub/rival-rating";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
const clutchSplitSchema = z.object({
|
|
5
|
+
count: z.number().int().nonnegative(),
|
|
6
|
+
won: z.number().int().nonnegative()
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export const rrIndicatorsSchema: z.ZodType<RRIndicators> = z.object({
|
|
10
|
+
steamId64: z.string(),
|
|
11
|
+
totalRounds: z.number().int().nonnegative(),
|
|
12
|
+
kills: z.number().int().nonnegative(),
|
|
13
|
+
deaths: z.number().int().nonnegative(),
|
|
14
|
+
assists: z.number().int().nonnegative(),
|
|
15
|
+
kpr: z.number().nonnegative(),
|
|
16
|
+
dpr: z.number().nonnegative(),
|
|
17
|
+
apr: z.number().nonnegative(),
|
|
18
|
+
adr: z.number().nonnegative(),
|
|
19
|
+
hsPercent: z.number().min(0).max(100),
|
|
20
|
+
kast: z.number().min(0).max(100),
|
|
21
|
+
survivalRate: z.number().min(0).max(1),
|
|
22
|
+
twoKillRounds: z.number().int().nonnegative(),
|
|
23
|
+
threeKillRounds: z.number().int().nonnegative(),
|
|
24
|
+
fourKillRounds: z.number().int().nonnegative(),
|
|
25
|
+
fiveKillRounds: z.number().int().nonnegative(),
|
|
26
|
+
multiKillRate: z.number().min(0).max(1),
|
|
27
|
+
firstKillCount: z.number().int().nonnegative(),
|
|
28
|
+
firstDeathCount: z.number().int().nonnegative(),
|
|
29
|
+
firstKillRate: z.number().nonnegative(),
|
|
30
|
+
firstDeathRate: z.number().nonnegative(),
|
|
31
|
+
openingDuelRate: z.number().nonnegative(),
|
|
32
|
+
openingDuelWinRate: z.number().min(0).max(1),
|
|
33
|
+
tradeKillCount: z.number().int().nonnegative(),
|
|
34
|
+
tradeDeathCount: z.number().int().nonnegative(),
|
|
35
|
+
tradeKillRate: z.number().nonnegative(),
|
|
36
|
+
tradeDeathRate: z.number().nonnegative(),
|
|
37
|
+
clutchAttempts: z.number().int().nonnegative(),
|
|
38
|
+
clutchWins: z.number().int().nonnegative(),
|
|
39
|
+
clutchWinRate: z.number().min(0).max(1),
|
|
40
|
+
clutchFrequency: z.number().nonnegative(),
|
|
41
|
+
clutchScore: z.number().nonnegative(),
|
|
42
|
+
clutchScoreRate: z.number().nonnegative(),
|
|
43
|
+
vsOne: clutchSplitSchema,
|
|
44
|
+
vsTwo: clutchSplitSchema,
|
|
45
|
+
vsThree: clutchSplitSchema,
|
|
46
|
+
vsFour: clutchSplitSchema,
|
|
47
|
+
vsFive: clutchSplitSchema,
|
|
48
|
+
awpKills: z.number().int().nonnegative(),
|
|
49
|
+
awpKillsPerRound: z.number().nonnegative(),
|
|
50
|
+
awpKillRate: z.number().min(0).max(1),
|
|
51
|
+
sniperKills: z.number().int().nonnegative(),
|
|
52
|
+
sniperKillRate: z.number().min(0).max(1),
|
|
53
|
+
awpMultiKillRate: z.number().min(0).max(1).nullable(),
|
|
54
|
+
awpDuelWinRate: z.number().min(0).max(1).nullable(),
|
|
55
|
+
utilityDamage: z.number().int().nonnegative(),
|
|
56
|
+
utilityDamagePerRound: z.number().nonnegative(),
|
|
57
|
+
flashAssistCount: z.number().int().nonnegative(),
|
|
58
|
+
flashAssistPerRound: z.number().nonnegative(),
|
|
59
|
+
blindDurationTotal: z.number().nonnegative(),
|
|
60
|
+
blindDurationPerRound: z.number().nonnegative(),
|
|
61
|
+
enemyFlashDurationSeconds: z.number().nonnegative().nullable(),
|
|
62
|
+
enemyFlashDurationPerRound: z.number().nonnegative().nullable(),
|
|
63
|
+
teamFlashDurationSeconds: z.number().nonnegative().nullable(),
|
|
64
|
+
teamFlashDurationPerRound: z.number().nonnegative().nullable(),
|
|
65
|
+
grenadeCount: z.number().int().nonnegative(),
|
|
66
|
+
grenadeCountPerRound: z.number().nonnegative(),
|
|
67
|
+
ecoRoundCount: z.number().int().nonnegative(),
|
|
68
|
+
forceRoundCount: z.number().int().nonnegative(),
|
|
69
|
+
fullBuyRoundCount: z.number().int().nonnegative(),
|
|
70
|
+
pistolRoundCount: z.number().int().nonnegative(),
|
|
71
|
+
avgEquipmentValue: z.number().nonnegative(),
|
|
72
|
+
combatDeathCount: z.number().int().nonnegative().nullable(),
|
|
73
|
+
bombDeathCount: z.number().int().nonnegative().nullable(),
|
|
74
|
+
wallbangKillCount: z.number().int().nonnegative().nullable(),
|
|
75
|
+
roundSwingTotal: z.number().nullable(),
|
|
76
|
+
roundSwingPerKill: z.number().nullable()
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
export type { AccountSignalsV2, RRIndicators, RRResult, RRResultV2, PrismResult, ValueAccountsWeights } from "@rivalhub/rival-rating";
|
package/src/series.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { matchWorkspaceModelSchema } from "./workspace.js";
|
|
3
|
+
|
|
4
|
+
export const mvpCandidateSchema = z.object({
|
|
5
|
+
playerKey: z.string(),
|
|
6
|
+
name: z.string(),
|
|
7
|
+
teamName: z.string(),
|
|
8
|
+
recommendationScore: z.number().nonnegative(),
|
|
9
|
+
rivalhubRR: z.number().nonnegative(),
|
|
10
|
+
hltvRating: z.number().nonnegative(),
|
|
11
|
+
confidence: z.number().min(0).max(1),
|
|
12
|
+
explanation: z.array(z.string())
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const mvpRecommendationSchema = z.object({
|
|
16
|
+
version: z.literal("cs2-demo-analysis-kit/mvp-recommendation-0.1"),
|
|
17
|
+
recommended: mvpCandidateSchema,
|
|
18
|
+
candidates: z.array(mvpCandidateSchema).min(1)
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const seriesMatchInputSchema = z.object({
|
|
22
|
+
matchId: z.string(),
|
|
23
|
+
model: matchWorkspaceModelSchema
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export const seriesPlayerMapTrendSchema = z.object({
|
|
27
|
+
matchId: z.string(),
|
|
28
|
+
mapName: z.string(),
|
|
29
|
+
rivalhubRR: z.number().nonnegative(),
|
|
30
|
+
hltvRating: z.number().nonnegative(),
|
|
31
|
+
adr: z.number().nonnegative(),
|
|
32
|
+
kast: z.number().min(0).max(100)
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export const seriesPlayerRowSchema = z.object({
|
|
36
|
+
playerKey: z.string(),
|
|
37
|
+
name: z.string(),
|
|
38
|
+
teamName: z.string(),
|
|
39
|
+
mapCount: z.number().int().positive(),
|
|
40
|
+
totalRounds: z.number().int().positive(),
|
|
41
|
+
kills: z.number().int().nonnegative(),
|
|
42
|
+
deaths: z.number().int().nonnegative(),
|
|
43
|
+
assists: z.number().int().nonnegative(),
|
|
44
|
+
adr: z.number().nonnegative(),
|
|
45
|
+
kast: z.number().min(0).max(100),
|
|
46
|
+
rivalhubRR: z.number().nonnegative(),
|
|
47
|
+
hltvRating: z.number().nonnegative(),
|
|
48
|
+
confidence: z.number().min(0).max(1),
|
|
49
|
+
perMap: z.array(seriesPlayerMapTrendSchema)
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
export const seriesSummarySchema = z.object({
|
|
53
|
+
version: z.literal("cs2-demo-analysis-kit/series-summary-0.1"),
|
|
54
|
+
mapCount: z.number().int().positive(),
|
|
55
|
+
maps: z.array(z.object({
|
|
56
|
+
matchId: z.string(),
|
|
57
|
+
mapName: z.string(),
|
|
58
|
+
scoreline: z.string(),
|
|
59
|
+
teamAName: z.string(),
|
|
60
|
+
teamBName: z.string(),
|
|
61
|
+
winnerName: z.string().nullable()
|
|
62
|
+
})),
|
|
63
|
+
teams: z.array(z.object({
|
|
64
|
+
teamKey: z.string(),
|
|
65
|
+
name: z.string(),
|
|
66
|
+
mapsWon: z.number().int().nonnegative()
|
|
67
|
+
})),
|
|
68
|
+
scoreboard: z.array(seriesPlayerRowSchema),
|
|
69
|
+
mvpCandidates: z.array(mvpCandidateSchema)
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
export type MvpCandidate = z.infer<typeof mvpCandidateSchema>;
|
|
73
|
+
export type MvpRecommendation = z.infer<typeof mvpRecommendationSchema>;
|
|
74
|
+
export type SeriesMatchInput = z.infer<typeof seriesMatchInputSchema>;
|
|
75
|
+
export type SeriesPlayerMapTrend = z.infer<typeof seriesPlayerMapTrendSchema>;
|
|
76
|
+
export type SeriesPlayerRow = z.infer<typeof seriesPlayerRowSchema>;
|
|
77
|
+
export type SeriesSummary = z.infer<typeof seriesSummarySchema>;
|
package/src/team.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { leaderboardMetricKeySchema } from "./leaderboard.js";
|
|
3
|
+
import { playerStyleAxisSchema } from "./player.js";
|
|
4
|
+
|
|
5
|
+
export const teamRosterInputSchema = z.object({
|
|
6
|
+
teamKey: z.string(),
|
|
7
|
+
name: z.string(),
|
|
8
|
+
playerKeys: z.array(z.string()).min(1)
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export const teamMemberSummarySchema = z.object({
|
|
12
|
+
playerKey: z.string(),
|
|
13
|
+
name: z.string(),
|
|
14
|
+
mapCount: z.number().int().positive(),
|
|
15
|
+
confidence: z.number().min(0).max(1),
|
|
16
|
+
metrics: z.record(leaderboardMetricKeySchema, z.number().nullable())
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export const teamMetricLeaderSchema = z.object({
|
|
20
|
+
metric: z.enum(["rivalhubRR", "adr", "kast", "firstKillPer100"]),
|
|
21
|
+
label: z.string(),
|
|
22
|
+
playerKey: z.string(),
|
|
23
|
+
name: z.string(),
|
|
24
|
+
value: z.number()
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export const teamRoleSpecialistSchema = playerStyleAxisSchema.extend({
|
|
28
|
+
playerKey: z.string(),
|
|
29
|
+
name: z.string()
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export const teamCohortSummarySchema = z.object({
|
|
33
|
+
version: z.literal("cs2-demo-analysis-kit/team-summary-0.1"),
|
|
34
|
+
weightsVersion: z.string(),
|
|
35
|
+
teamKey: z.string(),
|
|
36
|
+
name: z.string(),
|
|
37
|
+
members: z.array(teamMemberSummarySchema).min(1),
|
|
38
|
+
coreMembers: z.array(teamMemberSummarySchema).min(1),
|
|
39
|
+
averages: z.object({
|
|
40
|
+
rivalhubRR: z.number().nonnegative(),
|
|
41
|
+
hltvRating: z.number().nonnegative(),
|
|
42
|
+
adr: z.number().nonnegative(),
|
|
43
|
+
kd: z.number().nullable(),
|
|
44
|
+
kast: z.number().min(0).max(100),
|
|
45
|
+
confidence: z.number().min(0).max(1)
|
|
46
|
+
}),
|
|
47
|
+
performance: z.object({
|
|
48
|
+
firstKills: z.number().int().nonnegative(),
|
|
49
|
+
firstDeaths: z.number().int().nonnegative(),
|
|
50
|
+
openingDuelWinRate: z.number().min(0).max(1).nullable(),
|
|
51
|
+
clutchAttempts: z.number().int().nonnegative(),
|
|
52
|
+
clutchWins: z.number().int().nonnegative(),
|
|
53
|
+
clutchWinRate: z.number().min(0).max(1).nullable()
|
|
54
|
+
}),
|
|
55
|
+
/** 队伍 PRISM:成员各轴百分位的简单平均,不重算 PRISM。 */
|
|
56
|
+
style: z.object({
|
|
57
|
+
axes: z.array(playerStyleAxisSchema)
|
|
58
|
+
}).nullable(),
|
|
59
|
+
leaders: z.array(teamMetricLeaderSchema),
|
|
60
|
+
roleComplementarity: z.object({
|
|
61
|
+
/** 成员主轴覆盖率:不同主轴数 / min(成员数, 8),0–100。 */
|
|
62
|
+
coverageScore: z.number().min(0).max(100),
|
|
63
|
+
specialists: z.array(teamRoleSpecialistSchema),
|
|
64
|
+
weakAxes: z.array(playerStyleAxisSchema)
|
|
65
|
+
})
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
export type TeamRosterInput = z.infer<typeof teamRosterInputSchema>;
|
|
69
|
+
export type TeamMemberSummary = z.infer<typeof teamMemberSummarySchema>;
|
|
70
|
+
export type TeamMetricLeader = z.infer<typeof teamMetricLeaderSchema>;
|
|
71
|
+
export type TeamRoleSpecialist = z.infer<typeof teamRoleSpecialistSchema>;
|
|
72
|
+
export type TeamCohortSummary = z.infer<typeof teamCohortSummarySchema>;
|
package/src/upstream.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import {
|
|
3
|
+
sideSchema,
|
|
4
|
+
teamKeySchema,
|
|
5
|
+
economyTypeSchema,
|
|
6
|
+
teamEconomySchema,
|
|
7
|
+
vec3Schema,
|
|
8
|
+
playerRowSchema,
|
|
9
|
+
roundRowSchema,
|
|
10
|
+
killRowSchema,
|
|
11
|
+
damageRowSchema,
|
|
12
|
+
playerEconomyRowSchema,
|
|
13
|
+
playerStatsRowSchema,
|
|
14
|
+
blindRowSchema,
|
|
15
|
+
bombRowSchema,
|
|
16
|
+
grenadeRowSchema,
|
|
17
|
+
clutchRowSchema,
|
|
18
|
+
shotRowSchema,
|
|
19
|
+
positionRowSchema,
|
|
20
|
+
replaySchema,
|
|
21
|
+
replayRoundSchema,
|
|
22
|
+
replayPlayerTrackSchema,
|
|
23
|
+
} from "cs2-demo-format";
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
SCHEMAS_BY_KEY,
|
|
27
|
+
blindRowSchema,
|
|
28
|
+
blindsSchema,
|
|
29
|
+
bombEventTypeSchema,
|
|
30
|
+
bombRowSchema,
|
|
31
|
+
bombsSchema,
|
|
32
|
+
clutchRowSchema,
|
|
33
|
+
clutchesSchema,
|
|
34
|
+
damageRowSchema,
|
|
35
|
+
damagesSchema,
|
|
36
|
+
economyTypeSchema,
|
|
37
|
+
endReasonSchema,
|
|
38
|
+
grenadeRowSchema,
|
|
39
|
+
grenadeTypeSchema,
|
|
40
|
+
grenadesSchema,
|
|
41
|
+
hitgroupSchema,
|
|
42
|
+
killRowSchema,
|
|
43
|
+
killsSchema,
|
|
44
|
+
manifestSchema,
|
|
45
|
+
matchSchema,
|
|
46
|
+
playerEconomiesSchema,
|
|
47
|
+
playerEconomyRowSchema,
|
|
48
|
+
playerRowSchema,
|
|
49
|
+
playersSchema,
|
|
50
|
+
playerStatsRowSchema,
|
|
51
|
+
playerStatsSchema,
|
|
52
|
+
positionRowSchema,
|
|
53
|
+
positionsSchema,
|
|
54
|
+
replaySchema,
|
|
55
|
+
replayPlayerTrackSchema,
|
|
56
|
+
replayRoundSchema,
|
|
57
|
+
roundRowSchema,
|
|
58
|
+
roundsSchema,
|
|
59
|
+
shotRowSchema,
|
|
60
|
+
shotsSchema,
|
|
61
|
+
sideSchema,
|
|
62
|
+
steamId64Schema,
|
|
63
|
+
teamEconomySchema,
|
|
64
|
+
teamEconomyTypeSchema,
|
|
65
|
+
teamKeySchema,
|
|
66
|
+
teamSummarySchema,
|
|
67
|
+
vec3Schema,
|
|
68
|
+
} from "cs2-demo-format";
|
|
69
|
+
|
|
70
|
+
export type Side = z.infer<typeof sideSchema>;
|
|
71
|
+
export type TeamKey = z.infer<typeof teamKeySchema>;
|
|
72
|
+
export type EconomyType = z.infer<typeof economyTypeSchema>;
|
|
73
|
+
export type TeamEconomyType = z.infer<typeof teamEconomySchema>;
|
|
74
|
+
export type Vec3 = z.infer<typeof vec3Schema>;
|
|
75
|
+
|
|
76
|
+
export type PackagePlayer = z.infer<typeof playerRowSchema>;
|
|
77
|
+
export type PackageRound = z.infer<typeof roundRowSchema>;
|
|
78
|
+
export type PackageKill = z.infer<typeof killRowSchema>;
|
|
79
|
+
export type PackageDamage = z.infer<typeof damageRowSchema>;
|
|
80
|
+
export type PackagePlayerEconomy = z.infer<typeof playerEconomyRowSchema>;
|
|
81
|
+
export type PackagePlayerStats = z.infer<typeof playerStatsRowSchema>;
|
|
82
|
+
export type PackageBlind = z.infer<typeof blindRowSchema>;
|
|
83
|
+
export type PackageBomb = z.infer<typeof bombRowSchema>;
|
|
84
|
+
export type PackageGrenade = z.infer<typeof grenadeRowSchema>;
|
|
85
|
+
export type PackageClutch = z.infer<typeof clutchRowSchema>;
|
|
86
|
+
export type PackageShot = z.infer<typeof shotRowSchema>;
|
|
87
|
+
export type PackagePosition = z.infer<typeof positionRowSchema>;
|
|
88
|
+
export type Replay = z.infer<typeof replaySchema>;
|
|
89
|
+
export type ReplayRound = z.infer<typeof replayRoundSchema>;
|
|
90
|
+
export type ReplayPlayerTrack = z.infer<typeof replayPlayerTrackSchema>;
|