@ats-cx/cx-core 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 (48) hide show
  1. package/README.md +23 -0
  2. package/dist/apm/client.d.ts +59 -0
  3. package/dist/apm/client.js +247 -0
  4. package/dist/apm/config-state.d.ts +19 -0
  5. package/dist/apm/config-state.js +18 -0
  6. package/dist/apm/config.d.ts +93 -0
  7. package/dist/apm/config.js +72 -0
  8. package/dist/apm/errors.d.ts +39 -0
  9. package/dist/apm/errors.js +39 -0
  10. package/dist/apm/portal.d.ts +20 -0
  11. package/dist/apm/portal.js +89 -0
  12. package/dist/apm/values.d.ts +13 -0
  13. package/dist/apm/values.js +58 -0
  14. package/dist/config.d.ts +22 -0
  15. package/dist/config.js +168 -0
  16. package/dist/db/client.d.ts +23 -0
  17. package/dist/db/client.js +21 -0
  18. package/dist/diff/project-diff.d.ts +125 -0
  19. package/dist/diff/project-diff.js +531 -0
  20. package/dist/index.d.ts +38 -0
  21. package/dist/index.js +23 -0
  22. package/dist/normalize/log.d.ts +12 -0
  23. package/dist/normalize/log.js +143 -0
  24. package/dist/normalize/project.d.ts +18 -0
  25. package/dist/normalize/project.js +254 -0
  26. package/dist/providers/log-provider.d.ts +27 -0
  27. package/dist/providers/log-provider.js +63 -0
  28. package/dist/providers/project-provider.d.ts +10 -0
  29. package/dist/providers/project-provider.js +22 -0
  30. package/dist/providers/remote-project-provider.d.ts +28 -0
  31. package/dist/providers/remote-project-provider.js +167 -0
  32. package/dist/providers/source-provider.d.ts +11 -0
  33. package/dist/providers/source-provider.js +33 -0
  34. package/dist/rules/engine.d.ts +13 -0
  35. package/dist/rules/engine.js +213 -0
  36. package/dist/run/result-schema.d.ts +98 -0
  37. package/dist/run/result-schema.js +50 -0
  38. package/dist/run/store.d.ts +23 -0
  39. package/dist/run/store.js +49 -0
  40. package/dist/semantics/resolver.d.ts +37 -0
  41. package/dist/semantics/resolver.js +220 -0
  42. package/dist/semantics/table-loader.d.ts +11 -0
  43. package/dist/semantics/table-loader.js +44 -0
  44. package/dist/semantics/table-schema.d.ts +30 -0
  45. package/dist/semantics/table-schema.js +186 -0
  46. package/dist/types.d.ts +164 -0
  47. package/dist/types.js +2 -0
  48. package/package.json +33 -0
package/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # @ats-cx/cx-core
2
+
3
+ cx-cli 的事实层,随 `@ats-cx/cx-cli` 同号发布;不是独立公共 API,不承诺 semver 稳定。由确定性纯函数与 provider 组成,不感知 LLM 或传输层。
4
+
5
+ ## 主要导出
6
+
7
+ | 模块 | 职责 |
8
+ |------|------|
9
+ | `providers/` | `LogProvider`、`ProjectProvider`、`SourceProvider`(仅 git 版本快照)、`RemoteProjectProvider`(远端快照拉取) |
10
+ | `db/` | `DbClient` 薄接口 + `createMysqlClient`(mysql2,仅 `project pull` 使用) |
11
+ | `normalize/` | 日志 envelope 统一、session 聚合、project.json 规范化 |
12
+ | `semantics/` | 事件 → 统一语义标签 |
13
+ | `rules/` | 通用可疑信号规则引擎 |
14
+ | `diff/` | project 版本对比 |
15
+ | `run/` | run 目录、trace、artifact、结论 schema 校验 |
16
+
17
+ ## 测试
18
+
19
+ 在仓库根目录执行:
20
+
21
+ ```bash
22
+ pnpm vitest run packages/core
23
+ ```
@@ -0,0 +1,59 @@
1
+ import type { ApmProviderConfig } from "./config.js";
2
+ import type { ApmErrorKind } from "./errors.js";
3
+ import type { ApmOrder, LogType } from "./values.js";
4
+ export interface FlushRecord {
5
+ attempted: boolean;
6
+ ok?: boolean;
7
+ durationMs?: number;
8
+ reason?: ApmErrorKind;
9
+ }
10
+ export interface ApmQueryParams {
11
+ type: LogType;
12
+ from?: string;
13
+ to?: string;
14
+ order?: ApmOrder;
15
+ project?: string;
16
+ user?: string;
17
+ collection?: string;
18
+ session?: string;
19
+ subType?: string;
20
+ event?: string;
21
+ app?: string;
22
+ bizline?: string;
23
+ where?: Record<string, string>;
24
+ item?: string;
25
+ search?: string;
26
+ limit?: number;
27
+ flush?: boolean;
28
+ }
29
+ export interface ApmQueryResult {
30
+ rows: unknown[];
31
+ total: number;
32
+ pagesFetched: number;
33
+ request: {
34
+ url: string;
35
+ body: Record<string, unknown>;
36
+ };
37
+ warnings: string[];
38
+ flush: FlushRecord;
39
+ }
40
+ export interface ApmClientDeps {
41
+ fetchImpl?: typeof fetch;
42
+ onProgress?: (message: string) => void;
43
+ }
44
+ export interface FlushResult {
45
+ ok: boolean;
46
+ durationMs: number;
47
+ message?: string;
48
+ reason?: ApmErrorKind;
49
+ status?: number;
50
+ }
51
+ export interface ApmClient {
52
+ query(params: ApmQueryParams): Promise<ApmQueryResult>;
53
+ count(params: ApmQueryParams): Promise<{
54
+ total: number;
55
+ flush: FlushRecord;
56
+ }>;
57
+ flush(): Promise<FlushResult>;
58
+ }
59
+ export declare function createApmClient(config: ApmProviderConfig, deps?: ApmClientDeps): ApmClient;
@@ -0,0 +1,247 @@
1
+ import { ApmRequestError } from "./errors.js";
2
+ import { assertColumnName, assertSafeValue, FIXED_REQUEST_KEYS, LOG_TYPES, normalizeLogTime, ORDERS, } from "./values.js";
3
+ export function createApmClient(config, deps = {}) {
4
+ const fetchImpl = deps.fetchImpl ?? globalThis.fetch;
5
+ const onProgress = deps.onProgress ?? (message => console.error(message));
6
+ const queryUrl = `${config.baseUrl}/api/log/query`;
7
+ const flushUrl = `${config.baseUrl}/api/log/flush`;
8
+ async function flush() {
9
+ const startedAt = Date.now();
10
+ try {
11
+ const response = await fetchImpl(flushUrl, {
12
+ method: "GET",
13
+ signal: AbortSignal.timeout(config.timeoutMs.flush),
14
+ });
15
+ if (!response.ok) {
16
+ return { ok: false, durationMs: Date.now() - startedAt, reason: "http", status: response.status };
17
+ }
18
+ const parsed = parseJson(await response.text());
19
+ if (!isRecord(parsed)) {
20
+ return { ok: false, durationMs: Date.now() - startedAt, reason: "invalid_response" };
21
+ }
22
+ if (parsed.success === false) {
23
+ return { ok: false, durationMs: Date.now() - startedAt, reason: "rejected" };
24
+ }
25
+ if (parsed.success !== true) {
26
+ return { ok: false, durationMs: Date.now() - startedAt, reason: "invalid_response" };
27
+ }
28
+ return {
29
+ ok: true,
30
+ durationMs: Date.now() - startedAt,
31
+ ...(typeof parsed.message === "string" ? { message: parsed.message } : {}),
32
+ };
33
+ }
34
+ catch (error) {
35
+ return {
36
+ ok: false,
37
+ durationMs: Date.now() - startedAt,
38
+ reason: isAbortError(error) ? "timeout" : "http",
39
+ };
40
+ }
41
+ }
42
+ async function runFlush(enabled) {
43
+ if (!enabled) {
44
+ return { attempted: false };
45
+ }
46
+ const result = await flush();
47
+ const record = {
48
+ attempted: true,
49
+ ok: result.ok,
50
+ durationMs: result.durationMs,
51
+ ...(result.reason !== undefined ? { reason: result.reason } : {}),
52
+ };
53
+ if (!result.ok) {
54
+ onProgress(`同步失败(${result.reason ?? "unknown"}),最近事件可能未落库`);
55
+ }
56
+ return record;
57
+ }
58
+ async function requestPage(body) {
59
+ try {
60
+ const response = await fetchImpl(queryUrl, {
61
+ method: "POST",
62
+ headers: {
63
+ origin: config.baseUrl,
64
+ "content-type": "application/json",
65
+ },
66
+ body: JSON.stringify(body),
67
+ signal: AbortSignal.timeout(config.timeoutMs.query),
68
+ });
69
+ if (!response.ok) {
70
+ throw new ApmRequestError("http", {
71
+ status: response.status,
72
+ message: `APM 请求失败(HTTP ${response.status})`,
73
+ });
74
+ }
75
+ const parsed = parseJson(await response.text());
76
+ if (!isRecord(parsed)) {
77
+ throw new ApmRequestError("invalid_response");
78
+ }
79
+ if (parsed.success === false) {
80
+ throw new ApmRequestError("rejected", { baseUrl: config.baseUrl });
81
+ }
82
+ const data = parsed.data;
83
+ if (!isRecord(data) || !Array.isArray(data.result)) {
84
+ throw new ApmRequestError("invalid_response", { message: "APM 响应缺少 data.result 数组" });
85
+ }
86
+ const pagination = data.pagination;
87
+ const total = isRecord(pagination) ? Number(pagination.total) : Number.NaN;
88
+ if (!Number.isFinite(total) || total < 0) {
89
+ throw new ApmRequestError("invalid_response", { message: "APM 响应缺少有效的 data.pagination.total" });
90
+ }
91
+ return { rows: data.result, total };
92
+ }
93
+ catch (error) {
94
+ if (error instanceof ApmRequestError) {
95
+ throw error;
96
+ }
97
+ if (isAbortError(error)) {
98
+ throw new ApmRequestError("timeout");
99
+ }
100
+ throw new ApmRequestError("http", {
101
+ message: `APM 请求失败:${error instanceof Error ? error.message : String(error)}`,
102
+ });
103
+ }
104
+ }
105
+ async function query(params) {
106
+ validateParams(params);
107
+ const pageSize = params.limit === undefined ? config.fetch.pageSize : Math.min(params.limit, config.fetch.pageSize);
108
+ const order = params.limit === undefined ? "ASC" : (params.order ?? "DESC");
109
+ const firstBody = buildBody(config, params, 1, pageSize, order);
110
+ const flushRecord = await runFlush(params.flush !== false);
111
+ const rows = [];
112
+ const warnings = [];
113
+ const warnedTotals = new Set();
114
+ let firstTotal;
115
+ let total = 0;
116
+ let pagesFetched = 0;
117
+ for (let page = 1; page <= config.fetch.maxPages; page += 1) {
118
+ const body = page === 1 ? firstBody : buildBody(config, params, page, pageSize, order);
119
+ const result = await requestPage(body);
120
+ pagesFetched += 1;
121
+ rows.push(...result.rows);
122
+ total = result.total;
123
+ if (firstTotal === undefined) {
124
+ firstTotal = total;
125
+ }
126
+ else if (total !== firstTotal && !warnedTotals.has(total)) {
127
+ const warning = `警告: total 由 ${firstTotal} 变为 ${total}`;
128
+ warnedTotals.add(total);
129
+ warnings.push(warning);
130
+ onProgress(warning);
131
+ }
132
+ const target = params.limit === undefined ? total : Math.min(params.limit, total);
133
+ const expectedPages = Math.max(1, Math.ceil(target / pageSize));
134
+ if (expectedPages > 1) {
135
+ onProgress(`第 ${page}/${expectedPages} 页`);
136
+ }
137
+ const complete = rows.length >= target || result.rows.length === 0;
138
+ if (complete) {
139
+ return {
140
+ rows: params.limit === undefined ? rows : rows.slice(0, params.limit),
141
+ total,
142
+ pagesFetched,
143
+ request: { url: queryUrl, body: firstBody },
144
+ warnings,
145
+ flush: flushRecord,
146
+ };
147
+ }
148
+ if (page === config.fetch.maxPages) {
149
+ throw new ApmRequestError("incomplete", {
150
+ fetched: rows.length,
151
+ total,
152
+ partialRows: rows,
153
+ pagesFetched,
154
+ request: { url: queryUrl, body: firstBody },
155
+ warnings,
156
+ flush: flushRecord,
157
+ });
158
+ }
159
+ }
160
+ throw new ApmRequestError("incomplete", {
161
+ fetched: rows.length,
162
+ total,
163
+ partialRows: rows,
164
+ pagesFetched,
165
+ request: { url: queryUrl, body: firstBody },
166
+ warnings,
167
+ flush: flushRecord,
168
+ });
169
+ }
170
+ async function count(params) {
171
+ validateParams(params);
172
+ const body = buildBody(config, params, 1, 1, params.order ?? "DESC");
173
+ const flushRecord = await runFlush(params.flush !== false);
174
+ const result = await requestPage(body);
175
+ return { total: result.total, flush: flushRecord };
176
+ }
177
+ return { query, count, flush };
178
+ }
179
+ function buildBody(config, params, page, pageSize, order) {
180
+ const body = {
181
+ type: params.type,
182
+ page,
183
+ pageSize,
184
+ order,
185
+ };
186
+ addValue(body, "bizline_id", params.bizline ?? config.defaults.bizline_id);
187
+ addValue(body, "app_id", params.app ?? config.defaults.app_id);
188
+ addValue(body, "log_time_start", params.from ? normalizeLogTime(params.from, "start") : undefined);
189
+ addValue(body, "log_time_end", params.to ? normalizeLogTime(params.to, "end") : undefined);
190
+ addValue(body, "project_id", params.project);
191
+ addValue(body, "user_id", params.user);
192
+ addValue(body, "collection_id", params.collection);
193
+ addValue(body, "session_id", params.session);
194
+ addValue(body, "sub_type", params.subType);
195
+ addValue(body, "data_name", params.event);
196
+ addValue(body, "item_id", params.item);
197
+ addValue(body, "search", params.search);
198
+ for (const [column, value] of Object.entries(params.where ?? {})) {
199
+ if (FIXED_REQUEST_KEYS.has(column)) {
200
+ throw new Error(`${column} 是固定请求键,不能通过 where 覆盖`);
201
+ }
202
+ assertColumnName(column);
203
+ addValue(body, column, value);
204
+ }
205
+ return body;
206
+ }
207
+ function addValue(body, key, value) {
208
+ if (value === undefined || value === "") {
209
+ return;
210
+ }
211
+ assertSafeValue(key, value);
212
+ body[key] = value;
213
+ }
214
+ function validateParams(params) {
215
+ if (!LOG_TYPES.includes(params.type)) {
216
+ throw new Error(`不支持的日志类型: ${params.type}`);
217
+ }
218
+ if (params.order !== undefined && !ORDERS.includes(params.order)) {
219
+ throw new Error(`不支持的排序: ${params.order}`);
220
+ }
221
+ if (params.limit !== undefined && (!Number.isInteger(params.limit) || params.limit <= 0)) {
222
+ throw new Error("limit 必须是正整数");
223
+ }
224
+ const from = params.from ? normalizeLogTime(params.from, "start") : undefined;
225
+ const to = params.to ? normalizeLogTime(params.to, "end") : undefined;
226
+ if (from && to && to < from) {
227
+ throw new Error("结束时间不能早于开始时间");
228
+ }
229
+ }
230
+ function parseJson(text) {
231
+ try {
232
+ return text ? JSON.parse(text) : null;
233
+ }
234
+ catch {
235
+ return null;
236
+ }
237
+ }
238
+ function isRecord(value) {
239
+ return typeof value === "object" && value !== null && !Array.isArray(value);
240
+ }
241
+ function isAbortError(error) {
242
+ return (typeof error === "object" &&
243
+ error !== null &&
244
+ "name" in error &&
245
+ (error.name === "AbortError" || error.name === "TimeoutError"));
246
+ }
247
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1,19 @@
1
+ import { ApmRequestError } from "./errors.js";
2
+ export declare const HINT: (path: string) => string;
3
+ export declare const ERROR_EMPTY = "apm \u672A\u914D\u7F6E\uFF1AbaseUrl / defaults.host \u4E3A\u7A7A\uFF08apm \u7EC4\u4E0E run new \u62C9\u65E5\u5FD7\u90FD\u9700\u8981\u7AD9\u70B9\u5730\u5740\uFF09";
4
+ export declare const ERROR_MISSING: (configDir: string) => string;
5
+ export declare const SENTENCE: (path: string) => string;
6
+ export type ApmProviderState = {
7
+ kind: "unconfigured";
8
+ fillPath: string;
9
+ } | {
10
+ kind: "missing";
11
+ fillPath: string;
12
+ configDir: string;
13
+ } | {
14
+ kind: "invalid";
15
+ fillPath: string;
16
+ error: string;
17
+ };
18
+ export declare function isApmConfigured(state: ApmProviderState | null): state is null;
19
+ export declare function createApmConfigError(state: ApmProviderState): ApmRequestError;
@@ -0,0 +1,18 @@
1
+ import { ApmRequestError } from "./errors.js";
2
+ export const HINT = (path) => `请在 ${path} 填 baseUrl 与 defaults.host(值向团队获取)。`;
3
+ export const ERROR_EMPTY = "apm 未配置:baseUrl / defaults.host 为空(apm 组与 run new 拉日志都需要站点地址)";
4
+ export const ERROR_MISSING = (configDir) => `apm 未配置:${configDir} 缺少 apm-provider.json(apm 组与 run new 拉日志都需要它)`;
5
+ export const SENTENCE = (path) => `apm 未配置:${HINT(path)}`;
6
+ export function isApmConfigured(state) {
7
+ return state === null;
8
+ }
9
+ export function createApmConfigError(state) {
10
+ if (state.kind === "invalid") {
11
+ return new ApmRequestError("invalid_config", { message: state.error });
12
+ }
13
+ return new ApmRequestError("unconfigured", {
14
+ message: state.kind === "missing" ? ERROR_MISSING(state.configDir) : ERROR_EMPTY,
15
+ hint: HINT(state.fillPath),
16
+ });
17
+ }
18
+ //# sourceMappingURL=config-state.js.map
@@ -0,0 +1,93 @@
1
+ import { z } from "zod";
2
+ declare const apmProviderConfigSchema: z.ZodObject<{
3
+ baseUrl: z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>;
4
+ defaults: z.ZodObject<{
5
+ bizline_id: z.ZodString;
6
+ app_id: z.ZodString;
7
+ host: z.ZodEffects<z.ZodString, string, string>;
8
+ }, "strict", z.ZodTypeAny, {
9
+ bizline_id: string;
10
+ app_id: string;
11
+ host: string;
12
+ }, {
13
+ bizline_id: string;
14
+ app_id: string;
15
+ host: string;
16
+ }>;
17
+ runPreset: z.ZodObject<{
18
+ type: z.ZodEnum<["error", "performance", "api", "paint", "user_behav", "network", "server_error", "footprint", "server_api"]>;
19
+ log_time_start: z.ZodDefault<z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>>;
20
+ }, "strict", z.ZodTypeAny, {
21
+ type: "error" | "performance" | "api" | "paint" | "user_behav" | "network" | "server_error" | "footprint" | "server_api";
22
+ log_time_start: string;
23
+ }, {
24
+ type: "error" | "performance" | "api" | "paint" | "user_behav" | "network" | "server_error" | "footprint" | "server_api";
25
+ log_time_start?: string | undefined;
26
+ }>;
27
+ fetch: z.ZodObject<{
28
+ pageSize: z.ZodNumber;
29
+ maxPages: z.ZodNumber;
30
+ }, "strict", z.ZodTypeAny, {
31
+ pageSize: number;
32
+ maxPages: number;
33
+ }, {
34
+ pageSize: number;
35
+ maxPages: number;
36
+ }>;
37
+ timeoutMs: z.ZodObject<{
38
+ query: z.ZodNumber;
39
+ flush: z.ZodNumber;
40
+ portal: z.ZodNumber;
41
+ }, "strict", z.ZodTypeAny, {
42
+ query: number;
43
+ flush: number;
44
+ portal: number;
45
+ }, {
46
+ query: number;
47
+ flush: number;
48
+ portal: number;
49
+ }>;
50
+ }, "strict", z.ZodTypeAny, {
51
+ baseUrl: string;
52
+ defaults: {
53
+ bizline_id: string;
54
+ app_id: string;
55
+ host: string;
56
+ };
57
+ runPreset: {
58
+ type: "error" | "performance" | "api" | "paint" | "user_behav" | "network" | "server_error" | "footprint" | "server_api";
59
+ log_time_start: string;
60
+ };
61
+ fetch: {
62
+ pageSize: number;
63
+ maxPages: number;
64
+ };
65
+ timeoutMs: {
66
+ query: number;
67
+ flush: number;
68
+ portal: number;
69
+ };
70
+ }, {
71
+ baseUrl: string;
72
+ defaults: {
73
+ bizline_id: string;
74
+ app_id: string;
75
+ host: string;
76
+ };
77
+ runPreset: {
78
+ type: "error" | "performance" | "api" | "paint" | "user_behav" | "network" | "server_error" | "footprint" | "server_api";
79
+ log_time_start?: string | undefined;
80
+ };
81
+ fetch: {
82
+ pageSize: number;
83
+ maxPages: number;
84
+ };
85
+ timeoutMs: {
86
+ query: number;
87
+ flush: number;
88
+ portal: number;
89
+ };
90
+ }>;
91
+ export type ApmProviderConfig = z.infer<typeof apmProviderConfigSchema>;
92
+ export declare function parseApmProviderConfig(raw: unknown): ApmProviderConfig;
93
+ export {};
@@ -0,0 +1,72 @@
1
+ import { z } from "zod";
2
+ import { isSiteHost, LOG_TYPES, normalizeLogTime } from "./values.js";
3
+ const logTimeStartSchema = z
4
+ .string()
5
+ .transform((value, context) => {
6
+ if (value === "") {
7
+ return value;
8
+ }
9
+ try {
10
+ return normalizeLogTime(value, "start");
11
+ }
12
+ catch (error) {
13
+ context.addIssue({
14
+ code: z.ZodIssueCode.custom,
15
+ message: error instanceof Error ? error.message : String(error),
16
+ });
17
+ return z.NEVER;
18
+ }
19
+ })
20
+ .optional()
21
+ .default("");
22
+ function isHttpUrl(value) {
23
+ if (!/^https?:\/\//i.test(value)) {
24
+ return false;
25
+ }
26
+ try {
27
+ return ["http:", "https:"].includes(new URL(value).protocol);
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ }
33
+ const apmProviderConfigSchema = z
34
+ .object({
35
+ baseUrl: z
36
+ .string()
37
+ .refine(value => value === "" || isHttpUrl(value), "必须为空占位或合法的 http(s):// URL")
38
+ .transform(value => value.replace(/\/+$/, "")),
39
+ defaults: z
40
+ .object({
41
+ bizline_id: z.string().min(1, "不能为空"),
42
+ app_id: z.string().min(1, "不能为空"),
43
+ host: z
44
+ .string()
45
+ .refine(value => value === "" || isSiteHost(value), "必须为空占位或至少三段的完整站点 host,如 www.example.com"),
46
+ })
47
+ .strict(),
48
+ runPreset: z
49
+ .object({
50
+ type: z.enum(LOG_TYPES),
51
+ log_time_start: logTimeStartSchema,
52
+ })
53
+ .strict(),
54
+ fetch: z
55
+ .object({
56
+ pageSize: z.number().int().positive(),
57
+ maxPages: z.number().int().positive(),
58
+ })
59
+ .strict(),
60
+ timeoutMs: z
61
+ .object({
62
+ query: z.number().int().positive(),
63
+ flush: z.number().int().positive(),
64
+ portal: z.number().int().positive(),
65
+ })
66
+ .strict(),
67
+ })
68
+ .strict();
69
+ export function parseApmProviderConfig(raw) {
70
+ return apmProviderConfigSchema.parse(raw);
71
+ }
72
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1,39 @@
1
+ export type ApmErrorKind = "http" | "rejected" | "invalid_response" | "timeout" | "incomplete" | "unconfigured" | "invalid_config";
2
+ export interface ApmRequestErrorOptions {
3
+ message?: string;
4
+ status?: number;
5
+ fetched?: number;
6
+ total?: number;
7
+ partialRows?: unknown[];
8
+ pagesFetched?: number;
9
+ request?: {
10
+ url: string;
11
+ body: Record<string, unknown>;
12
+ };
13
+ warnings?: string[];
14
+ hint?: string;
15
+ baseUrl?: string;
16
+ flush?: {
17
+ attempted: boolean;
18
+ ok?: boolean;
19
+ durationMs?: number;
20
+ message?: string;
21
+ reason?: ApmErrorKind;
22
+ };
23
+ }
24
+ export declare class ApmRequestError extends Error {
25
+ readonly kind: ApmErrorKind;
26
+ readonly status?: number;
27
+ readonly fetched?: number;
28
+ readonly total?: number;
29
+ readonly partialRows?: unknown[];
30
+ readonly pagesFetched?: number;
31
+ readonly request?: {
32
+ url: string;
33
+ body: Record<string, unknown>;
34
+ };
35
+ readonly warnings?: string[];
36
+ readonly hint?: string;
37
+ readonly flush?: ApmRequestErrorOptions["flush"];
38
+ constructor(kind: ApmErrorKind, options?: ApmRequestErrorOptions);
39
+ }
@@ -0,0 +1,39 @@
1
+ function defaultMessage(kind, baseUrl) {
2
+ const messages = {
3
+ http: "APM 请求失败",
4
+ rejected: `接口拒绝(未给原因):常见为 --where 列不存在 / 值格式错;持续失败请确认 ${baseUrl ?? "配置 baseUrl"} 可用`,
5
+ invalid_response: "APM 接口返回了无法识别的响应",
6
+ timeout: "APM 请求超时",
7
+ incomplete: "APM 查询达到最大页数,结果不完整",
8
+ unconfigured: "apm 未配置",
9
+ invalid_config: "apm 配置无效",
10
+ };
11
+ return messages[kind];
12
+ }
13
+ export class ApmRequestError extends Error {
14
+ kind;
15
+ status;
16
+ fetched;
17
+ total;
18
+ partialRows;
19
+ pagesFetched;
20
+ request;
21
+ warnings;
22
+ hint;
23
+ flush;
24
+ constructor(kind, options = {}) {
25
+ super(options.message ?? defaultMessage(kind, options.baseUrl));
26
+ this.name = "ApmRequestError";
27
+ this.kind = kind;
28
+ this.status = options.status;
29
+ this.fetched = options.fetched;
30
+ this.total = options.total;
31
+ this.partialRows = options.partialRows;
32
+ this.pagesFetched = options.pagesFetched;
33
+ this.request = options.request;
34
+ this.warnings = options.warnings;
35
+ this.hint = options.hint;
36
+ this.flush = options.flush;
37
+ }
38
+ }
39
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1,20 @@
1
+ import type { ApmProviderConfig } from "./config.js";
2
+ export interface ApmPortalDeps {
3
+ fetchImpl?: typeof fetch;
4
+ }
5
+ export interface ResolveUserIdParams {
6
+ value: string;
7
+ host: string;
8
+ }
9
+ export interface FetchLiveProjectParams {
10
+ projectId: string;
11
+ host: string;
12
+ }
13
+ export interface LiveProjectResult {
14
+ text: string;
15
+ parsed: Record<string, unknown>;
16
+ updatedDate: unknown;
17
+ hasProject: boolean;
18
+ }
19
+ export declare function resolveUserId(config: ApmProviderConfig, params: ResolveUserIdParams, deps?: ApmPortalDeps): Promise<string>;
20
+ export declare function fetchLiveProject(config: ApmProviderConfig, params: FetchLiveProjectParams, deps?: ApmPortalDeps): Promise<LiveProjectResult>;