dsh-log 0.2.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/dist/client.js ADDED
@@ -0,0 +1,460 @@
1
+ // AUTO-GENERATED by node packages/dsh-log/build.mjs — DO NOT EDIT. Source: packages/dsh-log/src/client.ts
2
+ import {
3
+ assertPluginId,
4
+ buildPhoneNames,
5
+ CLIENT_BATCH_INTERVAL_MS,
6
+ CLIENT_BATCH_MAX,
7
+ CLIENT_PACKET_BYTES,
8
+ CLIENT_QUEUE_MAX,
9
+ parseEventListManifest
10
+ } from "./config.js";
11
+ import { buildPhoneName, buildPhoneNames as buildPhoneNames2 } from "./config.js";
12
+ const CLIENT_BATCH = {
13
+ maxPerBatch: CLIENT_BATCH_MAX,
14
+ intervalMs: CLIENT_BATCH_INTERVAL_MS,
15
+ packetBytes: CLIENT_PACKET_BYTES,
16
+ queueMax: CLIENT_QUEUE_MAX
17
+ };
18
+ function buildClientPhoneNames(prefix) {
19
+ return buildPhoneNames(prefix);
20
+ }
21
+ function resolveClientLogConfig(input) {
22
+ const raw = input === void 0 || input === null ? {} : input;
23
+ const pluginId = raw.pluginId === void 0 ? "wf" : assertPluginId(raw.pluginId, "\u63D2\u4EF6\u6807\u8BC6 pluginId");
24
+ const prefix = raw.prefix === void 0 ? pluginId : assertPluginId(raw.prefix, "\u7535\u8BDD\u540D\u524D\u7F00 prefix");
25
+ const eventList = raw.eventList === void 0 ? null : raw.eventList;
26
+ if (eventList !== null && typeof eventList === "object") {
27
+ parseEventListManifest(eventList);
28
+ }
29
+ return { pluginId, prefix, eventList };
30
+ }
31
+ const LOG_DEBUG_KEY = "dsws.debug";
32
+ const LOG_BATCH_MAX = CLIENT_BATCH_MAX;
33
+ const LOG_FLUSH_MS = CLIENT_BATCH_INTERVAL_MS;
34
+ const LOG_PACKET_BYTES = CLIENT_PACKET_BYTES;
35
+ const LOG_QUEUE_MAX = CLIENT_QUEUE_MAX;
36
+ const LOG_WATCHDOG_MS = 5e3;
37
+ const LOG_REV = 1;
38
+ const LOG_LEVELS = ["error", "warn", "info", "debug"];
39
+ function createClientLog(deps, configInput) {
40
+ const input = deps || {};
41
+ const host = input.host === void 0 ? null : input.host;
42
+ const timer = input.timer === void 0 ? null : input.timer;
43
+ const storage = input.storage !== void 0 && input.storage !== null ? input.storage : input.localStorage === void 0 ? null : input.localStorage;
44
+ const broadcast = typeof input.broadcastLogSwitch === "function" ? input.broadcastLogSwitch : null;
45
+ const config = resolveClientLogConfig(configInput);
46
+ const phoneNames = buildClientPhoneNames(config.prefix);
47
+ function readLocalDebugSwitch() {
48
+ const fallback = { enabled: false, sampleRate: 1, rev: LOG_REV };
49
+ try {
50
+ if (!storage || typeof storage.getItem !== "function") return fallback;
51
+ const raw = storage.getItem(LOG_DEBUG_KEY);
52
+ if (!raw) return fallback;
53
+ const saved = JSON.parse(raw);
54
+ if (!saved || typeof saved !== "object") return fallback;
55
+ return {
56
+ enabled: saved.enabled === true,
57
+ sampleRate: typeof saved.sampleRate === "number" && isFinite(saved.sampleRate) ? saved.sampleRate : 1,
58
+ rev: typeof saved.rev === "number" && isFinite(saved.rev) ? saved.rev : LOG_REV
59
+ };
60
+ } catch (e) {
61
+ void e;
62
+ return fallback;
63
+ }
64
+ }
65
+ function persistLocalDebugSwitch(state) {
66
+ try {
67
+ if (!storage || typeof storage.setItem !== "function") return false;
68
+ storage.setItem(
69
+ LOG_DEBUG_KEY,
70
+ JSON.stringify({
71
+ enabled: !!(state && state.enabled),
72
+ sampleRate: state && typeof state.sampleRate === "number" && isFinite(state.sampleRate) ? state.sampleRate : 1,
73
+ rev: state && typeof state.rev === "number" && isFinite(state.rev) ? state.rev : LOG_REV
74
+ })
75
+ );
76
+ return true;
77
+ } catch (e) {
78
+ void e;
79
+ return false;
80
+ }
81
+ }
82
+ const logSwitch = readLocalDebugSwitch();
83
+ const logQueue = [];
84
+ const logDroppedState = { count: 0 };
85
+ const logForwardState = { lastSummaryAt: 0, lastSummaryDropped: 0, lastReason: "" };
86
+ const logFlushTimer = { id: null };
87
+ const setLogSwitchGen = { n: 0 };
88
+ function isEnabled(level) {
89
+ if (level === "error" || level === "warn") return true;
90
+ try {
91
+ return logSwitch.enabled === true;
92
+ } catch (e) {
93
+ void e;
94
+ return false;
95
+ }
96
+ }
97
+ function log(level, event, fields) {
98
+ if (!isEnabled(level)) return;
99
+ if (logQueue.length >= LOG_QUEUE_MAX) {
100
+ logDroppedState.count += 1;
101
+ logForwardState.lastReason = "queue-full";
102
+ return;
103
+ }
104
+ logQueue.push({
105
+ ts: Date.now(),
106
+ level,
107
+ event: String(event || ""),
108
+ fields: fields && typeof fields === "object" ? fields : {}
109
+ });
110
+ if (level === "error" || level === "warn") scheduleLogFlush(true);
111
+ else scheduleLogFlush(false);
112
+ }
113
+ function scheduleLogFlush(immediate) {
114
+ const later = function(fn, ms) {
115
+ try {
116
+ if (timer !== null && timer !== void 0 && typeof timer.timeout === "function") return timer.timeout(fn, ms);
117
+ } catch (e) {
118
+ void e;
119
+ }
120
+ return setTimeout(fn, ms);
121
+ };
122
+ if (immediate) {
123
+ if (logFlushTimer.id !== null) {
124
+ try {
125
+ clearTimeout(logFlushTimer.id);
126
+ } catch (e) {
127
+ void e;
128
+ }
129
+ logFlushTimer.id = null;
130
+ }
131
+ later(sendLogBatch, 0);
132
+ return;
133
+ }
134
+ if (logFlushTimer.id !== null) return;
135
+ logFlushTimer.id = later(function() {
136
+ logFlushTimer.id = null;
137
+ sendLogBatch();
138
+ }, LOG_FLUSH_MS);
139
+ }
140
+ function estimateBatchBytes(entries) {
141
+ try {
142
+ const text = JSON.stringify(entries);
143
+ const g = globalThis;
144
+ if (typeof g.TextEncoder !== "undefined") return new g.TextEncoder().encode(text).length;
145
+ return String(text).length;
146
+ } catch (e) {
147
+ void e;
148
+ return LOG_PACKET_BYTES + 1;
149
+ }
150
+ }
151
+ function maybeForwardSummary() {
152
+ const delta = logDroppedState.count - logForwardState.lastSummaryDropped;
153
+ if (delta <= 0) return;
154
+ logForwardState.lastSummaryDropped = logDroppedState.count;
155
+ const now = Date.now();
156
+ const windowMs = now - logForwardState.lastSummaryAt;
157
+ logForwardState.lastSummaryAt = now;
158
+ try {
159
+ log("warn", "log.forward.summary", {
160
+ droppedDelta: delta,
161
+ totalDropped: logDroppedState.count,
162
+ reason: logForwardState.lastReason || "send-fail",
163
+ windowMs
164
+ });
165
+ } catch (e) {
166
+ void e;
167
+ }
168
+ }
169
+ function hash8(value) {
170
+ try {
171
+ const t = String(value || "");
172
+ let h = 5381;
173
+ for (let i = 0; i < t.length; i++) h = (h << 5) + h + t.charCodeAt(i) >>> 0;
174
+ return ("0000000" + h.toString(16)).slice(-8);
175
+ } catch (e) {
176
+ void e;
177
+ return "00000000";
178
+ }
179
+ }
180
+ function logExportFail(op, reason, err) {
181
+ try {
182
+ const g = globalThis;
183
+ if (typeof g.dswsLogHash === "function" && typeof g.dswsLogTrunc === "function") {
184
+ const trunc = g.dswsLogTrunc;
185
+ const hash = g.dswsLogHash;
186
+ const raw = err && typeof err === "object" && "message" in err ? String(err.message) : String(err || reason);
187
+ log("warn", "log.export.fail", { op, reason, errorHash: hash(trunc(raw, 120, "error")) });
188
+ } else {
189
+ const raw = err && typeof err === "object" && "message" in err ? String(err.message) : String(err || reason);
190
+ log("warn", "log.export.fail", { op, reason, errorHash: hash8(raw) });
191
+ }
192
+ } catch (e) {
193
+ void e;
194
+ }
195
+ }
196
+ function watchSwitchOp(op, pending) {
197
+ let settled = false;
198
+ try {
199
+ if (pending && typeof pending.then === "function") {
200
+ ;
201
+ pending.then(
202
+ function() {
203
+ settled = true;
204
+ },
205
+ function() {
206
+ settled = true;
207
+ }
208
+ );
209
+ }
210
+ } catch (e) {
211
+ void e;
212
+ }
213
+ const fire = function() {
214
+ if (!settled) {
215
+ settled = true;
216
+ try {
217
+ log("warn", "log.switch.watchdog", { op, timeoutMs: LOG_WATCHDOG_MS, stage: "waiting-host" });
218
+ } catch (e) {
219
+ void e;
220
+ }
221
+ }
222
+ };
223
+ try {
224
+ if (timer !== null && timer !== void 0 && typeof timer.timeout === "function") {
225
+ timer.timeout(fire, LOG_WATCHDOG_MS);
226
+ return;
227
+ }
228
+ } catch (e) {
229
+ void e;
230
+ }
231
+ try {
232
+ setTimeout(fire, LOG_WATCHDOG_MS);
233
+ } catch (e2) {
234
+ void e2;
235
+ }
236
+ }
237
+ function sendLogBatch() {
238
+ if (logQueue.length === 0) return Promise.resolve({ ok: true, sent: 0 });
239
+ const entries = logQueue.splice(0, LOG_BATCH_MAX);
240
+ let trimmed = 0;
241
+ while (entries.length > 1 && estimateBatchBytes(entries) > LOG_PACKET_BYTES) {
242
+ entries.pop();
243
+ trimmed += 1;
244
+ }
245
+ while (logQueue.length > LOG_QUEUE_MAX) {
246
+ logQueue.shift();
247
+ trimmed += 1;
248
+ }
249
+ if (trimmed > 0) {
250
+ logDroppedState.count += trimmed;
251
+ logForwardState.lastReason = "packet-trim";
252
+ try {
253
+ entries[entries.length - 1].truncated = true;
254
+ } catch (e) {
255
+ void e;
256
+ }
257
+ }
258
+ const args = { entries, droppedCount: logDroppedState.count };
259
+ const onlySummary = entries.length === 1 && entries[0] && entries[0].event === "log.forward.summary";
260
+ if (!host || typeof host.call !== "function") {
261
+ logDroppedState.count += entries.length;
262
+ logForwardState.lastReason = "send-fail";
263
+ if (!onlySummary) maybeForwardSummary();
264
+ return Promise.resolve({ ok: false, sent: 0 });
265
+ }
266
+ try {
267
+ return host.call(phoneNames.logBatch, args).then(function(res) {
268
+ const ok = !!res && typeof res === "object" && res.ok === true;
269
+ if (!ok) {
270
+ logDroppedState.count += entries.length;
271
+ logForwardState.lastReason = "host-reject";
272
+ }
273
+ if (ok) maybeForwardSummary();
274
+ else if (!onlySummary) maybeForwardSummary();
275
+ return { ok, sent: entries.length };
276
+ }).catch(function() {
277
+ logDroppedState.count += entries.length;
278
+ logForwardState.lastReason = "send-fail";
279
+ if (!onlySummary) maybeForwardSummary();
280
+ return { ok: false, sent: 0 };
281
+ });
282
+ } catch (e) {
283
+ void e;
284
+ logDroppedState.count += entries.length;
285
+ logForwardState.lastReason = "send-fail";
286
+ if (!onlySummary) maybeForwardSummary();
287
+ return Promise.resolve({ ok: false, sent: 0 });
288
+ }
289
+ }
290
+ function flush() {
291
+ if (logFlushTimer.id !== null) {
292
+ try {
293
+ clearTimeout(logFlushTimer.id);
294
+ } catch (e) {
295
+ void e;
296
+ }
297
+ logFlushTimer.id = null;
298
+ }
299
+ try {
300
+ sendLogBatch();
301
+ } catch (e) {
302
+ void e;
303
+ }
304
+ return { ok: true };
305
+ }
306
+ function getDroppedCount() {
307
+ return logDroppedState.count;
308
+ }
309
+ function reconcileLogSwitch() {
310
+ if (!host || typeof host.call !== "function") {
311
+ return Promise.resolve({ ok: false, enabled: logSwitch.enabled, sampleRate: logSwitch.sampleRate });
312
+ }
313
+ try {
314
+ const pendingGet = host.call(phoneNames.logGetSwitch, {});
315
+ watchSwitchOp("reconcile", pendingGet);
316
+ return pendingGet.then(function(res) {
317
+ const body = res && typeof res === "object" ? res : null;
318
+ if (!body || body.ok !== true) return { ok: false, enabled: logSwitch.enabled, sampleRate: logSwitch.sampleRate };
319
+ logSwitch.enabled = body.enabled === true;
320
+ if (typeof body.sampleRate === "number" && isFinite(body.sampleRate)) logSwitch.sampleRate = body.sampleRate;
321
+ persistLocalDebugSwitch(logSwitch);
322
+ try {
323
+ if (broadcast) broadcast();
324
+ } catch (e) {
325
+ void e;
326
+ }
327
+ return { ok: true, enabled: logSwitch.enabled, sampleRate: logSwitch.sampleRate };
328
+ }).catch(function() {
329
+ return { ok: false, enabled: logSwitch.enabled, sampleRate: logSwitch.sampleRate };
330
+ });
331
+ } catch (e) {
332
+ void e;
333
+ return Promise.resolve({ ok: false, enabled: logSwitch.enabled, sampleRate: logSwitch.sampleRate });
334
+ }
335
+ }
336
+ const logSwitchSetFail = function(kind, hint) {
337
+ try {
338
+ log("warn", "host.call.fail", {
339
+ method: phoneNames.logSetSwitch,
340
+ kind: "set-switch-" + kind,
341
+ errorHash: hash8(String(hint === void 0 || hint === null ? kind : hint).slice(0, 120))
342
+ });
343
+ } catch (e) {
344
+ void e;
345
+ }
346
+ };
347
+ const switchThrowKind = function(e) {
348
+ const msg = String((e && typeof e === "object" && ("code" in e || "message" in e) ? e.code || e.message : e) || "");
349
+ if (/unknown endpoint/i.test(msg)) return "throw-unknown-endpoint";
350
+ if (/connection|host\.call 不可用|unavailable/i.test(msg)) return "throw-connection";
351
+ return "throw";
352
+ };
353
+ const failByThrow = function(e) {
354
+ const kind = switchThrowKind(e);
355
+ logSwitchSetFail(
356
+ kind,
357
+ (e && typeof e === "object" && "message" in e ? e.message : void 0) || e
358
+ );
359
+ return kind;
360
+ };
361
+ function setLogSwitch(enabled, sampleRate) {
362
+ const next = {
363
+ enabled: enabled === true,
364
+ sampleRate: typeof sampleRate === "number" && isFinite(sampleRate) ? sampleRate : logSwitch.sampleRate
365
+ };
366
+ if (!host || typeof host.call !== "function") {
367
+ logSwitchSetFail("host-unavailable", "host-unavailable");
368
+ return Promise.resolve({ ok: false, enabled: logSwitch.enabled, error: "host-unavailable" });
369
+ }
370
+ try {
371
+ const pendingSet = host.call(phoneNames.logSetSwitch, next);
372
+ watchSwitchOp("set", pendingSet);
373
+ const myGen = setLogSwitchGen.n += 1;
374
+ const timeoutAt = new Promise(function(resolve) {
375
+ const fire = function() {
376
+ resolve({ switchTimedOut: true });
377
+ };
378
+ try {
379
+ if (timer !== null && timer !== void 0 && typeof timer.timeout === "function") {
380
+ timer.timeout(fire, LOG_WATCHDOG_MS);
381
+ return;
382
+ }
383
+ } catch (e) {
384
+ void e;
385
+ }
386
+ try {
387
+ setTimeout(fire, LOG_WATCHDOG_MS);
388
+ } catch (e2) {
389
+ void e2;
390
+ }
391
+ });
392
+ return Promise.race([pendingSet, timeoutAt]).then(function(res) {
393
+ const body = res && typeof res === "object" ? res : null;
394
+ if (body && body["switchTimedOut"] === true) {
395
+ logSwitchSetFail("timeout", "timeout-" + LOG_WATCHDOG_MS);
396
+ return { ok: false, enabled: logSwitch.enabled, error: "switch-timeout" };
397
+ }
398
+ if (myGen !== setLogSwitchGen.n) return { ok: false, enabled: logSwitch.enabled, error: "stale" };
399
+ if (!body || body["ok"] !== true) {
400
+ logSwitchSetFail("host-rejected", "host-rejected");
401
+ return { ok: false, enabled: logSwitch.enabled, error: "host-rejected" };
402
+ }
403
+ logSwitch.enabled = body["enabled"] === true;
404
+ logSwitch.sampleRate = next.sampleRate;
405
+ persistLocalDebugSwitch(logSwitch);
406
+ try {
407
+ if (broadcast) broadcast();
408
+ } catch (e) {
409
+ void e;
410
+ }
411
+ return { ok: true, enabled: logSwitch.enabled, sampleRate: logSwitch.sampleRate };
412
+ }).catch(function(e) {
413
+ if (myGen !== setLogSwitchGen.n) return { ok: false, enabled: logSwitch.enabled, error: "stale" };
414
+ return { ok: false, enabled: logSwitch.enabled, error: failByThrow(e) };
415
+ });
416
+ } catch (e) {
417
+ return Promise.resolve({ ok: false, enabled: logSwitch.enabled, error: failByThrow(e) });
418
+ }
419
+ }
420
+ return {
421
+ config,
422
+ phoneNames,
423
+ logSwitch,
424
+ logQueue,
425
+ logDroppedState,
426
+ logForwardState,
427
+ logFlushTimer,
428
+ isEnabled,
429
+ log,
430
+ scheduleLogFlush,
431
+ estimateBatchBytes,
432
+ maybeForwardSummary,
433
+ hash8,
434
+ logExportFail,
435
+ watchSwitchOp,
436
+ sendLogBatch,
437
+ flush,
438
+ getDroppedCount,
439
+ readLocalDebugSwitch,
440
+ persistLocalDebugSwitch,
441
+ reconcileLogSwitch,
442
+ setLogSwitch
443
+ };
444
+ }
445
+ export {
446
+ CLIENT_BATCH,
447
+ LOG_BATCH_MAX,
448
+ LOG_DEBUG_KEY,
449
+ LOG_FLUSH_MS,
450
+ LOG_LEVELS,
451
+ LOG_PACKET_BYTES,
452
+ LOG_QUEUE_MAX,
453
+ LOG_REV,
454
+ LOG_WATCHDOG_MS,
455
+ buildClientPhoneNames,
456
+ buildPhoneName,
457
+ buildPhoneNames2 as buildPhoneNames,
458
+ createClientLog,
459
+ resolveClientLogConfig
460
+ };
package/dist/config.js ADDED
@@ -0,0 +1,221 @@
1
+ // AUTO-GENERATED by node packages/dsh-log/build.mjs — DO NOT EDIT. Source: packages/dsh-log/src/config.ts
2
+ const LOG_DEBOUNCE_MS = 1e3;
3
+ const DEFAULT_MAX_QUEUE = 1e3;
4
+ const CLIENT_BATCH_MAX = 50;
5
+ const CLIENT_BATCH_INTERVAL_MS = 1e3;
6
+ const CLIENT_PACKET_BYTES = 128 * 1024;
7
+ const CLIENT_QUEUE_MAX = 100;
8
+ const LEGACY_LOG_DIR_NAME = "logs";
9
+ const LEGACY_SWITCH_FILE_NAME = "log-switch.json";
10
+ const PHONE_ACTIONS = ["logBatch", "logExport", "logClear", "logGetSwitch", "logSetSwitch"];
11
+ const ID_PATTERN = /^[a-z0-9-]{1,32}$/;
12
+ function assertPluginId(value, role) {
13
+ if (typeof value !== "string" || !ID_PATTERN.test(value)) {
14
+ throw new Error(
15
+ "[dsh-log] " + role + " \u975E\u6CD5\uFF1A\u53EA\u80FD\u7528\u5C0F\u5199\u82F1\u6587\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u4E2D\u6A2A\u7EBF\uFF0C\u957F\u5EA6 1 \u5230 32\uFF08\u6536\u5230 " + JSON.stringify(value) + "\uFF09"
16
+ );
17
+ }
18
+ return value;
19
+ }
20
+ function resolveHostLogConfig(input) {
21
+ if (!input || typeof input !== "object") throw new Error("[dsh-log] \u5EFA\u65E5\u5FD7\u5E93\u7F3A\u5C11\u914D\u7F6E\uFF1A\u63D2\u4EF6\u6807\u8BC6 pluginId \u5FC5\u586B");
22
+ const pluginId = assertPluginId(input.pluginId, "\u63D2\u4EF6\u6807\u8BC6 pluginId");
23
+ const prefix = input.prefix === void 0 ? pluginId : assertPluginId(input.prefix, "\u7535\u8BDD\u540D\u524D\u7F00 prefix");
24
+ const logDirName = input.logDirName !== void 0 ? input.logDirName : pluginId === "wf" ? LEGACY_LOG_DIR_NAME : LEGACY_LOG_DIR_NAME + "-" + pluginId;
25
+ const switchFileName = input.switchFileName !== void 0 ? input.switchFileName : pluginId === "wf" ? LEGACY_SWITCH_FILE_NAME : "log-switch-" + pluginId + ".json";
26
+ const fileNamePolicy = input.fileNamePolicy === void 0 ? "daily" : input.fileNamePolicy;
27
+ if (fileNamePolicy !== "daily" && fileNamePolicy !== "four-segment") {
28
+ throw new Error("[dsh-log] \u6587\u4EF6\u540D\u7B56\u7565 fileNamePolicy \u975E\u6CD5\uFF1A\u53EA\u8BB8 daily \u6216 four-segment");
29
+ }
30
+ const maxQueue = input.maxQueue === void 0 ? DEFAULT_MAX_QUEUE : input.maxQueue;
31
+ if (typeof maxQueue !== "number" || !isFinite(maxQueue) || maxQueue < 1) {
32
+ throw new Error("[dsh-log] \u5185\u5B58\u961F\u5217\u4E0A\u9650 maxQueue \u975E\u6CD5\uFF1A\u5FC5\u987B\u662F\u4E0D\u5C0F\u4E8E 1 \u7684\u6570\u5B57");
33
+ }
34
+ const eventList = input.eventList === void 0 ? null : input.eventList;
35
+ if (eventList !== null && typeof eventList === "object") {
36
+ parseEventListManifest(eventList);
37
+ }
38
+ return {
39
+ pluginId,
40
+ prefix,
41
+ logDirName,
42
+ switchFileName,
43
+ fileNamePolicy,
44
+ maxQueue: Math.floor(maxQueue),
45
+ eventList
46
+ };
47
+ }
48
+ function buildPhoneName(prefix, action) {
49
+ return assertPluginId(prefix, "\u7535\u8BDD\u540D\u524D\u7F00 prefix") + "." + action;
50
+ }
51
+ function buildPhoneNames(prefix) {
52
+ const checked = assertPluginId(prefix, "\u7535\u8BDD\u540D\u524D\u7F00 prefix");
53
+ const names = {};
54
+ for (const action of PHONE_ACTIONS) names[action] = checked + "." + action;
55
+ return names;
56
+ }
57
+ function formatDailyFileName(date) {
58
+ const d = date instanceof Date ? date : new Date(date);
59
+ const pad = (n) => String(n).padStart(2, "0");
60
+ return d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate()) + ".log";
61
+ }
62
+ function formatFourSegmentFileName(date, pluginId, pid, startedAt) {
63
+ const d = date instanceof Date ? date : new Date(date);
64
+ const pad = (n) => String(n).padStart(2, "0");
65
+ const day = d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate());
66
+ const safeId = assertPluginId(pluginId, "\u63D2\u4EF6\u6807\u8BC6 pluginId");
67
+ const safePid = typeof pid === "number" && isFinite(pid) && pid > 0 ? Math.floor(pid) : 0;
68
+ const safeStartedAt = String(startedAt || "").replace(/[:/\\]/g, "-");
69
+ return day + "." + safeId + "." + safePid + "." + safeStartedAt + ".log";
70
+ }
71
+ function resolveLogFileName(config, date, pid, startedAt) {
72
+ if (config.fileNamePolicy === "four-segment") {
73
+ return formatFourSegmentFileName(date, config.pluginId, pid, startedAt);
74
+ }
75
+ return formatDailyFileName(date);
76
+ }
77
+ function logFileNamePattern(config) {
78
+ if (config.fileNamePolicy === "four-segment") {
79
+ return /^\d{4}-\d{2}-\d{2}\.[a-z0-9-]{1,32}\.\d+\..+\.log$/;
80
+ }
81
+ return /^\d{4}-\d{2}-\d{2}\.log$/;
82
+ }
83
+ function matchExportFileName(config, candidates, wantDate, fallbackFileName) {
84
+ if (config.fileNamePolicy !== "four-segment") {
85
+ return /^\d{4}-\d{2}-\d{2}$/.test(wantDate) ? wantDate + ".log" : fallbackFileName;
86
+ }
87
+ const pattern = logFileNamePattern(config);
88
+ const hits = (Array.isArray(candidates) ? candidates : []).filter((name) => typeof name === "string" && pattern.test(name) && name.indexOf(wantDate + ".") === 0).sort();
89
+ return hits.length > 0 ? hits[0] : fallbackFileName;
90
+ }
91
+ const EVENT_LEVELS = ["error", "warn", "info", "debug"];
92
+ const EVENT_KINDS = ["resident", "ondemand", "selfmon"];
93
+ function isNonEmptyString(value) {
94
+ return typeof value === "string" && value.length > 0;
95
+ }
96
+ function assertStringArray(value, what) {
97
+ if (!Array.isArray(value)) throw new Error("[dsh-log] \u4E8B\u4EF6\u6E05\u5355 eventList \u975E\u6CD5\uFF1A" + what + " \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6570\u7EC4");
98
+ const seen = [];
99
+ for (const item of value) {
100
+ if (!isNonEmptyString(item)) throw new Error("[dsh-log] \u4E8B\u4EF6\u6E05\u5355 eventList \u975E\u6CD5\uFF1A" + what + " \u91CC\u6709\u7A7A\u5B57\u6BB5\u540D");
101
+ if (seen.indexOf(item) >= 0) throw new Error("[dsh-log] \u4E8B\u4EF6\u6E05\u5355 eventList \u975E\u6CD5\uFF1A" + what + " \u91CC\u5B57\u6BB5\u540D\u91CD\u590D\uFF1A" + item);
102
+ seen.push(item);
103
+ }
104
+ return seen;
105
+ }
106
+ function parseEventListManifest(value) {
107
+ if (typeof value === "string") {
108
+ throw new Error("[dsh-log] \u4E8B\u4EF6\u6E05\u5355 eventList \u975E\u6CD5\uFF1A\u8DEF\u5F84\u5F62\u5F0F\u8BF7\u8C03\u7528\u65B9\u81EA\u5DF1\u8BFB\u6210\u5BF9\u8C61\u518D\u4F20\u5165\uFF0C\u65E5\u5FD7\u5305\u4E0D\u8BFB\u76D8");
109
+ }
110
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
111
+ throw new Error("[dsh-log] \u4E8B\u4EF6\u6E05\u5355 eventList \u975E\u6CD5\uFF1A\u53EA\u6536\u5BF9\u8C61\u5F62\u5F0F\uFF08\u7A7A\u6A21\u677F\u89C1\u5305\u5185\u7684 event-list.template.json\uFF09");
112
+ }
113
+ const input = value;
114
+ if (input["version"] !== 1) {
115
+ throw new Error("[dsh-log] \u4E8B\u4EF6\u6E05\u5355 eventList \u975E\u6CD5\uFF1Aversion \u73B0\u5728\u53EA\u8BA4 1\uFF08\u6536\u5230 " + JSON.stringify(input["version"]) + "\uFF09");
116
+ }
117
+ const pluginId = assertPluginId(input["pluginId"], "\u4E8B\u4EF6\u6E05\u5355 pluginId");
118
+ const countsRaw = input["counts"];
119
+ if (!countsRaw || typeof countsRaw !== "object" || Array.isArray(countsRaw)) {
120
+ throw new Error("[dsh-log] \u4E8B\u4EF6\u6E05\u5355 eventList \u975E\u6CD5\uFF1Acounts \u5FC5\u987B\u662F\u542B\u4E09\u7C7B\u8BA1\u6570\u7684\u5BF9\u8C61");
121
+ }
122
+ const countsRecord = countsRaw;
123
+ const counts = { resident: 0, ondemand: 0, selfmon: 0 };
124
+ for (const kind of EVENT_KINDS) {
125
+ const n = countsRecord[kind];
126
+ if (typeof n !== "number" || !isFinite(n) || Math.floor(n) !== n || n < 0) {
127
+ throw new Error("[dsh-log] \u4E8B\u4EF6\u6E05\u5355 eventList \u975E\u6CD5\uFF1Acounts." + kind + " \u5FC5\u987B\u662F\u975E\u8D1F\u6574\u6570");
128
+ }
129
+ counts[kind] = n;
130
+ }
131
+ const eventsRaw = input["events"];
132
+ if (!eventsRaw || typeof eventsRaw !== "object" || Array.isArray(eventsRaw)) {
133
+ throw new Error("[dsh-log] \u4E8B\u4EF6\u6E05\u5355 eventList \u975E\u6CD5\uFF1Aevents \u5FC5\u987B\u662F\u4E8B\u4EF6\u540D\u5230\u6761\u76EE\u7684\u5BF9\u8C61");
134
+ }
135
+ const events = {};
136
+ for (const name of Object.keys(eventsRaw)) {
137
+ events[name] = parseEventEntry(name, eventsRaw[name]);
138
+ }
139
+ return { version: 1, pluginId, counts, events };
140
+ }
141
+ function parseEventEntry(name, value) {
142
+ if (!isNonEmptyString(name)) throw new Error("[dsh-log] \u4E8B\u4EF6\u6E05\u5355 eventList \u975E\u6CD5\uFF1A\u4E8B\u4EF6\u540D\u4E0D\u80FD\u4E3A\u7A7A");
143
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
144
+ throw new Error("[dsh-log] \u4E8B\u4EF6\u6E05\u5355 eventList \u975E\u6CD5\uFF1A\u4E8B\u4EF6 " + name + " \u5FC5\u987B\u662F\u5BF9\u8C61");
145
+ }
146
+ const input = value;
147
+ if (EVENT_LEVELS.indexOf(input["level"]) < 0) {
148
+ throw new Error("[dsh-log] \u4E8B\u4EF6\u6E05\u5355 eventList \u975E\u6CD5\uFF1A\u4E8B\u4EF6 " + name + " \u7684 level \u53EA\u8BB8 error\u3001warn\u3001info\u3001debug");
149
+ }
150
+ if (EVENT_KINDS.indexOf(input["kind"]) < 0) {
151
+ throw new Error("[dsh-log] \u4E8B\u4EF6\u6E05\u5355 eventList \u975E\u6CD5\uFF1A\u4E8B\u4EF6 " + name + " \u7684 kind \u53EA\u8BB8 resident\u3001ondemand\u3001selfmon");
152
+ }
153
+ for (const key of Object.keys(input)) {
154
+ if (["level", "kind", "fields", "codes", "rules", "guard"].indexOf(key) < 0) {
155
+ throw new Error("[dsh-log] \u4E8B\u4EF6\u6E05\u5355 eventList \u975E\u6CD5\uFF1A\u4E8B\u4EF6 " + name + " \u6709\u4E0D\u8BA4\u8BC6\u7684\u952E " + key);
156
+ }
157
+ }
158
+ const entry = {
159
+ level: input["level"],
160
+ kind: input["kind"],
161
+ fields: assertStringArray(input["fields"], "\u4E8B\u4EF6 " + name + " \u7684 fields")
162
+ };
163
+ if (input["codes"] !== void 0) entry.codes = assertStringArray(input["codes"], "\u4E8B\u4EF6 " + name + " \u7684 codes");
164
+ if (input["rules"] !== void 0) entry.rules = assertStringArray(input["rules"], "\u4E8B\u4EF6 " + name + " \u7684 rules");
165
+ if (input["guard"] !== void 0) {
166
+ if (typeof input["guard"] !== "string") {
167
+ throw new Error("[dsh-log] \u4E8B\u4EF6\u6E05\u5355 eventList \u975E\u6CD5\uFF1A\u4E8B\u4EF6 " + name + " \u7684 guard \u5FC5\u987B\u662F\u5B57\u7B26\u4E32");
168
+ }
169
+ entry.guard = input["guard"];
170
+ }
171
+ return entry;
172
+ }
173
+ function checkEventFields(manifest, eventName, fieldNames) {
174
+ const entry = manifest.events[eventName];
175
+ if (!entry) return { ok: false, unknownEvent: true, unknownFields: [] };
176
+ const allowed = new Set(entry.fields);
177
+ const unknownFields = (Array.isArray(fieldNames) ? fieldNames : []).filter(
178
+ (field) => !allowed.has(field)
179
+ );
180
+ return { ok: unknownFields.length === 0, unknownEvent: false, unknownFields };
181
+ }
182
+ function checkEventCounts(manifest) {
183
+ const actual = { resident: 0, ondemand: 0, selfmon: 0 };
184
+ for (const name of Object.keys(manifest.events)) {
185
+ actual[manifest.events[name].kind] += 1;
186
+ }
187
+ const problems = [];
188
+ for (const kind of EVENT_KINDS) {
189
+ if (actual[kind] !== manifest.counts[kind]) {
190
+ problems.push(
191
+ "[dsh-log] \u4E8B\u4EF6\u6E05\u5355\u8BA1\u6570\u5BF9\u4E0D\u4E0A\uFF1A" + kind + " \u7C7B\u5B9E\u9645 " + actual[kind] + " \u6761\uFF0C\u6E05\u5355\u81EA\u62A5 " + manifest.counts[kind] + " \u6761"
192
+ );
193
+ }
194
+ }
195
+ return { ok: problems.length === 0, problems };
196
+ }
197
+ export {
198
+ CLIENT_BATCH_INTERVAL_MS,
199
+ CLIENT_BATCH_MAX,
200
+ CLIENT_PACKET_BYTES,
201
+ CLIENT_QUEUE_MAX,
202
+ DEFAULT_MAX_QUEUE,
203
+ EVENT_KINDS,
204
+ EVENT_LEVELS,
205
+ LEGACY_LOG_DIR_NAME,
206
+ LEGACY_SWITCH_FILE_NAME,
207
+ LOG_DEBOUNCE_MS,
208
+ PHONE_ACTIONS,
209
+ assertPluginId,
210
+ buildPhoneName,
211
+ buildPhoneNames,
212
+ checkEventCounts,
213
+ checkEventFields,
214
+ formatDailyFileName,
215
+ formatFourSegmentFileName,
216
+ logFileNamePattern,
217
+ matchExportFileName,
218
+ parseEventListManifest,
219
+ resolveHostLogConfig,
220
+ resolveLogFileName
221
+ };