@wener/common 2.0.3 → 2.0.6

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 (312) hide show
  1. package/lib/ai/qwen3vl/index.js +1 -1
  2. package/lib/ai/qwen3vl/utils.js +15 -15
  3. package/lib/ai/qwen3vl/utils.js.map +1 -1
  4. package/lib/ai/vision/DocLayoutElementTypeSchema.js +22 -22
  5. package/lib/ai/vision/ImageAnnotationSchema.js +63 -47
  6. package/lib/ai/vision/index.js +2 -2
  7. package/lib/ai/vision/resolveImageAnnotation.js +81 -95
  8. package/lib/cn/ChineseResidentIdNo.js +55 -41
  9. package/lib/cn/ChineseResidentIdNo.js.map +1 -1
  10. package/lib/cn/ChineseResidentIdNo.mod.js +6 -1
  11. package/lib/cn/ChineseResidentIdNo.test.js +22 -21
  12. package/lib/cn/DivisionCode.js +220 -235
  13. package/lib/cn/DivisionCode.mod.js +6 -1
  14. package/lib/cn/DivisionCode.test.js +92 -121
  15. package/lib/cn/Mod11.js +18 -37
  16. package/lib/cn/Mod11.js.map +1 -1
  17. package/lib/cn/Mod31.js +23 -41
  18. package/lib/cn/UnifiedSocialCreditCode.js +143 -137
  19. package/lib/cn/UnifiedSocialCreditCode.mod.js +6 -1
  20. package/lib/cn/UnifiedSocialCreditCode.test.js +21 -15
  21. package/lib/cn/formatChineseAmount.js +46 -71
  22. package/lib/cn/index.js +6 -6
  23. package/lib/cn/mod.js +5 -3
  24. package/lib/cn/parseChineseNumber.js +81 -85
  25. package/lib/cn/parseChineseNumber.test.js +183 -261
  26. package/lib/cn/pinyin/cartesianProduct.js +19 -19
  27. package/lib/cn/pinyin/cartesianProduct.test.js +78 -178
  28. package/lib/cn/pinyin/loader.js +13 -11
  29. package/lib/cn/pinyin/preload.js +2 -1
  30. package/lib/cn/pinyin/toPinyin.test.js +149 -161
  31. package/lib/cn/pinyin/toPinyinPure.js +28 -23
  32. package/lib/cn/pinyin/transform.js +11 -11
  33. package/lib/cn/types.d.js +2 -2
  34. package/lib/consola/createStandardConsolaReporter.js +14 -15
  35. package/lib/consola/formatLogObject.js +149 -133
  36. package/lib/consola/formatLogObject.js.map +1 -1
  37. package/lib/consola/formatLogObject.test.js +167 -178
  38. package/lib/consola/index.js +2 -2
  39. package/lib/data/formatSort.js +14 -12
  40. package/lib/data/formatSort.test.js +33 -33
  41. package/lib/data/index.js +3 -3
  42. package/lib/data/maybeNumber.js +23 -23
  43. package/lib/data/maybeNumber.js.map +1 -1
  44. package/lib/data/parseSort.js +75 -68
  45. package/lib/data/parseSort.test.js +196 -187
  46. package/lib/data/resolvePagination.js +38 -39
  47. package/lib/data/resolvePagination.test.js +228 -218
  48. package/lib/data/types.d.js +2 -2
  49. package/lib/data/types.d.js.map +1 -1
  50. package/lib/dayjs/dayjs.js +20 -20
  51. package/lib/dayjs/formatDuration.js +56 -56
  52. package/lib/dayjs/formatDuration.js.map +1 -1
  53. package/lib/dayjs/formatDuration.test.js +63 -77
  54. package/lib/dayjs/index.js +4 -4
  55. package/lib/dayjs/parseDuration.js +21 -26
  56. package/lib/dayjs/parseRelativeTime.js +65 -66
  57. package/lib/dayjs/parseRelativeTime.test.js +227 -243
  58. package/lib/dayjs/resolveRelativeTime.js +74 -144
  59. package/lib/dayjs/resolveRelativeTime.js.map +1 -1
  60. package/lib/dayjs/resolveRelativeTime.test.js +296 -307
  61. package/lib/decimal/index.js +1 -1
  62. package/lib/decimal/parseDecimal.js +12 -12
  63. package/lib/drain3/Drain.js +321 -0
  64. package/lib/drain3/Drain.js.map +1 -0
  65. package/lib/drain3/LogCluster.js +38 -0
  66. package/lib/drain3/LogCluster.js.map +1 -0
  67. package/lib/drain3/Node.js +39 -0
  68. package/lib/drain3/Node.js.map +1 -0
  69. package/lib/drain3/TemplateMiner.js +205 -0
  70. package/lib/drain3/TemplateMiner.js.map +1 -0
  71. package/lib/drain3/index.js +31 -0
  72. package/lib/drain3/index.js.map +1 -0
  73. package/lib/drain3/persistence/FilePersistence.js +24 -0
  74. package/lib/drain3/persistence/FilePersistence.js.map +1 -0
  75. package/lib/drain3/persistence/MemoryPersistence.js +18 -0
  76. package/lib/drain3/persistence/MemoryPersistence.js.map +1 -0
  77. package/lib/drain3/persistence/PersistenceHandler.js +5 -0
  78. package/lib/drain3/persistence/PersistenceHandler.js.map +1 -0
  79. package/lib/drain3/types.js +7 -0
  80. package/lib/drain3/types.js.map +1 -0
  81. package/lib/emittery/emitter.js +7 -7
  82. package/lib/emittery/index.js +1 -1
  83. package/lib/foundation/schema/SexType.js +15 -12
  84. package/lib/foundation/schema/index.js +1 -1
  85. package/lib/foundation/schema/parseSexType.js +15 -16
  86. package/lib/foundation/schema/types.js +8 -6
  87. package/lib/fs/FileSystemError.js +18 -18
  88. package/lib/fs/IFileSystem.d.js +2 -2
  89. package/lib/fs/IFileSystem.d.js.map +1 -1
  90. package/lib/fs/MemoryFileSystem.test.js +172 -181
  91. package/lib/fs/createBrowserFileSystem.js +222 -233
  92. package/lib/fs/createBrowserFileSystem.js.map +1 -1
  93. package/lib/fs/createMemoryFileSystem.js +473 -510
  94. package/lib/fs/createMemoryFileSystem.js.map +1 -1
  95. package/lib/fs/createSandboxFileSystem.js +102 -101
  96. package/lib/fs/createSandboxFileSystem.js.map +1 -1
  97. package/lib/fs/createWebDavFileSystem.js +162 -132
  98. package/lib/fs/createWebDavFileSystem.js.map +1 -1
  99. package/lib/fs/createWebFileSystem.js +202 -0
  100. package/lib/fs/createWebFileSystem.js.map +1 -0
  101. package/lib/fs/findMimeType.js +14 -14
  102. package/lib/fs/findMimeType.js.map +1 -1
  103. package/lib/fs/index.js +7 -7
  104. package/lib/fs/index.js.map +1 -1
  105. package/lib/fs/minio/createMinioFileSystem.js +977 -0
  106. package/lib/fs/minio/createMinioFileSystem.js.map +1 -0
  107. package/lib/fs/minio/index.js +2 -0
  108. package/lib/fs/minio/index.js.map +1 -0
  109. package/lib/fs/orpc/FileSystemContract.js +57 -57
  110. package/lib/fs/orpc/createContractClientFileSystem.js +88 -88
  111. package/lib/fs/orpc/createContractClientFileSystem.js.map +1 -1
  112. package/lib/fs/orpc/index.js +2 -2
  113. package/lib/fs/orpc/server/createFileSystemContractImpl.js +62 -60
  114. package/lib/fs/orpc/server/createFileSystemContractImpl.js.map +1 -1
  115. package/lib/fs/orpc/server/index.js +1 -1
  116. package/lib/fs/s3/createS3MiniFileSystem.js +756 -689
  117. package/lib/fs/s3/createS3MiniFileSystem.js.map +1 -1
  118. package/lib/fs/s3/index.js +1 -1
  119. package/lib/fs/s3/s3mini.test.js +524 -553
  120. package/lib/fs/scandir.js +56 -56
  121. package/lib/fs/server/createDatabaseFileSystem.js +834 -741
  122. package/lib/fs/server/createDatabaseFileSystem.js.map +1 -1
  123. package/lib/fs/server/createNodeFileSystem.js +407 -380
  124. package/lib/fs/server/createNodeFileSystem.js.map +1 -1
  125. package/lib/fs/server/dbfs.test.js +201 -214
  126. package/lib/fs/server/index.js +1 -1
  127. package/lib/fs/server/loadTestDatabase.js +40 -43
  128. package/lib/fs/tests/runFileSystemTest.js +352 -315
  129. package/lib/fs/tests/runFileSystemTest.js.map +1 -1
  130. package/lib/fs/types.js +17 -20
  131. package/lib/fs/utils/getFileUrl.js +24 -30
  132. package/lib/fs/utils.js +17 -17
  133. package/lib/fs/utils.js.map +1 -1
  134. package/lib/fs/webdav/index.js +2 -0
  135. package/lib/fs/webdav/index.js.map +1 -0
  136. package/lib/index.js +2 -2
  137. package/lib/jsonschema/JsonSchema.js +216 -155
  138. package/lib/jsonschema/JsonSchema.js.map +1 -1
  139. package/lib/jsonschema/JsonSchema.test.js +123 -124
  140. package/lib/jsonschema/forEachJsonSchema.js +41 -41
  141. package/lib/jsonschema/forEachJsonSchema.js.map +1 -1
  142. package/lib/jsonschema/index.js +2 -2
  143. package/lib/jsonschema/types.d.js +2 -2
  144. package/lib/jsonschema/types.d.js.map +1 -1
  145. package/lib/meta/defineFileType.js +32 -38
  146. package/lib/meta/defineInit.js +39 -35
  147. package/lib/meta/defineMetadata.js +37 -34
  148. package/lib/meta/defineMetadata.js.map +1 -1
  149. package/lib/meta/defineMetadata.test.js +13 -12
  150. package/lib/meta/index.js +3 -3
  151. package/lib/orpc/createOpenApiContractClient.js +26 -24
  152. package/lib/orpc/createOpenApiContractClient.js.map +1 -1
  153. package/lib/orpc/createRpcContractClient.js +37 -31
  154. package/lib/orpc/index.js +2 -2
  155. package/lib/orpc/resolveLinkPlugins.js +25 -25
  156. package/lib/password/PHC.js +187 -189
  157. package/lib/password/PHC.js.map +1 -1
  158. package/lib/password/PHC.test.js +517 -535
  159. package/lib/password/Password.js +85 -80
  160. package/lib/password/Password.test.js +330 -364
  161. package/lib/password/createArgon2PasswordAlgorithm.js +50 -51
  162. package/lib/password/createArgon2PasswordAlgorithm.js.map +1 -1
  163. package/lib/password/createBase64PasswordAlgorithm.js +11 -11
  164. package/lib/password/createBase64PasswordAlgorithm.js.map +1 -1
  165. package/lib/password/createBcryptPasswordAlgorithm.js +20 -18
  166. package/lib/password/createBcryptPasswordAlgorithm.js.map +1 -1
  167. package/lib/password/createPBKDF2PasswordAlgorithm.js +65 -52
  168. package/lib/password/createPBKDF2PasswordAlgorithm.js.map +1 -1
  169. package/lib/password/createScryptPasswordAlgorithm.js +74 -63
  170. package/lib/password/createScryptPasswordAlgorithm.js.map +1 -1
  171. package/lib/password/index.js +5 -5
  172. package/lib/password/server/index.js +1 -1
  173. package/lib/resource/Identifiable.js +2 -2
  174. package/lib/resource/ListQuery.js +42 -42
  175. package/lib/resource/ListQuery.js.map +1 -1
  176. package/lib/resource/getTitleOfResource.js +5 -5
  177. package/lib/resource/index.js +2 -2
  178. package/lib/resource/index.js.map +1 -1
  179. package/lib/resource/schema/AnyResourceSchema.js +91 -89
  180. package/lib/resource/schema/BaseResourceSchema.js +26 -26
  181. package/lib/resource/schema/ResourceActionType.js +117 -115
  182. package/lib/resource/schema/ResourceStatus.js +94 -92
  183. package/lib/resource/schema/ResourceType.js +25 -23
  184. package/lib/resource/schema/index.js +5 -5
  185. package/lib/resource/schema/types.js +86 -55
  186. package/lib/resource/schema/types.test.js +16 -13
  187. package/lib/s3/formatS3Url.js +60 -60
  188. package/lib/s3/formatS3Url.js.map +1 -1
  189. package/lib/s3/formatS3Url.test.js +238 -261
  190. package/lib/s3/index.js +2 -2
  191. package/lib/s3/parseS3Url.js +61 -60
  192. package/lib/s3/parseS3Url.js.map +1 -1
  193. package/lib/s3/parseS3Url.test.js +270 -269
  194. package/lib/schema/SchemaRegistry.js +41 -42
  195. package/lib/schema/SchemaRegistry.js.map +1 -1
  196. package/lib/schema/SchemaRegistry.mod.js +1 -1
  197. package/lib/schema/TypeSchema.d.js +2 -2
  198. package/lib/schema/TypeSchema.d.js.map +1 -1
  199. package/lib/schema/createSchemaData.js +113 -67
  200. package/lib/schema/createSchemaData.js.map +1 -1
  201. package/lib/schema/findJsonSchemaByPath.js +28 -23
  202. package/lib/schema/findJsonSchemaByPath.js.map +1 -1
  203. package/lib/schema/formatZodError.js +113 -134
  204. package/lib/schema/formatZodError.js.map +1 -1
  205. package/lib/schema/formatZodError.test.js +192 -195
  206. package/lib/schema/getSchemaCache.js +7 -7
  207. package/lib/schema/getSchemaOptions.js +17 -16
  208. package/lib/schema/index.js +6 -6
  209. package/lib/schema/toJsonSchema.js +196 -190
  210. package/lib/schema/toJsonSchema.js.map +1 -1
  211. package/lib/schema/toJsonSchema.test.js +34 -26
  212. package/lib/schema/validate.js +106 -97
  213. package/lib/schema/validate.js.map +1 -1
  214. package/lib/tools/generateSchema.js +40 -40
  215. package/lib/tools/renderJsonSchemaToMarkdownDoc.js +74 -74
  216. package/lib/utils/buildBaseUrl.js +8 -8
  217. package/lib/utils/buildRedactorFormSchema.js +55 -54
  218. package/lib/utils/buildRedactorFormSchema.js.map +1 -1
  219. package/lib/utils/getEstimateProcessTime.js +24 -19
  220. package/lib/utils/index.js +3 -3
  221. package/lib/utils/resolveFeatureOptions.js +9 -9
  222. package/package.json +37 -18
  223. package/src/ai/qwen3vl/utils.ts +1 -1
  224. package/src/ai/vision/index.ts +2 -2
  225. package/src/cn/ChineseResidentIdNo.ts +1 -1
  226. package/src/cn/Mod11.ts +1 -1
  227. package/src/cn/__snapshots__/ChineseResidentIdNo.test.ts.snap +1 -1
  228. package/src/cn/__snapshots__/UnifiedSocialCreditCode.test.ts.snap +0 -23
  229. package/src/cn/index.ts +1 -2
  230. package/src/cn/parseChineseNumber.test.ts +4 -4
  231. package/src/consola/formatLogObject.ts +6 -6
  232. package/src/consola/index.ts +1 -1
  233. package/src/data/index.ts +3 -4
  234. package/src/data/maybeNumber.ts +1 -1
  235. package/src/data/parseSort.test.ts +0 -1
  236. package/src/data/resolvePagination.ts +2 -2
  237. package/src/data/types.d.ts +2 -2
  238. package/src/dayjs/formatDuration.ts +10 -11
  239. package/src/dayjs/index.ts +1 -1
  240. package/src/dayjs/parseRelativeTime.ts +1 -1
  241. package/src/dayjs/resolveRelativeTime.ts +11 -14
  242. package/src/drain3/Drain.test.ts +378 -0
  243. package/src/drain3/Drain.ts +394 -0
  244. package/src/drain3/LogCluster.ts +46 -0
  245. package/src/drain3/Node.ts +53 -0
  246. package/src/drain3/TemplateMiner.ts +246 -0
  247. package/src/drain3/index.ts +34 -0
  248. package/src/drain3/persistence/FilePersistence.ts +24 -0
  249. package/src/drain3/persistence/MemoryPersistence.ts +23 -0
  250. package/src/drain3/persistence/PersistenceHandler.ts +19 -0
  251. package/src/drain3/types.ts +75 -0
  252. package/src/fs/IFileSystem.d.ts +1 -2
  253. package/src/fs/createBrowserFileSystem.ts +7 -5
  254. package/src/fs/createMemoryFileSystem.ts +9 -13
  255. package/src/fs/createSandboxFileSystem.ts +1 -1
  256. package/src/fs/createWebDavFileSystem.ts +30 -17
  257. package/src/fs/createWebFileSystem.ts +242 -0
  258. package/src/fs/findMimeType.ts +1 -4
  259. package/src/fs/index.ts +5 -5
  260. package/src/fs/minio/createMinioFileSystem.ts +1148 -0
  261. package/src/fs/minio/index.ts +1 -0
  262. package/src/fs/orpc/createContractClientFileSystem.ts +5 -5
  263. package/src/fs/orpc/server/createFileSystemContractImpl.ts +1 -1
  264. package/src/fs/s3/createS3MiniFileSystem.ts +120 -79
  265. package/src/fs/s3/s3fs.test.ts +441 -0
  266. package/src/fs/s3/s3mini.test.ts +2 -2
  267. package/src/fs/server/createDatabaseFileSystem.ts +78 -114
  268. package/src/fs/server/createNodeFileSystem.ts +32 -13
  269. package/src/fs/server/dbfs.test.ts +13 -8
  270. package/src/fs/server/index.ts +1 -0
  271. package/src/fs/server/loadTestDatabase.ts +8 -119
  272. package/src/fs/tests/runFileSystemTest.ts +29 -28
  273. package/src/fs/utils.ts +1 -1
  274. package/src/fs/webdav/index.ts +1 -0
  275. package/src/jsonschema/JsonSchema.ts +5 -5
  276. package/src/jsonschema/forEachJsonSchema.ts +1 -1
  277. package/src/jsonschema/index.ts +1 -1
  278. package/src/jsonschema/types.d.ts +1 -1
  279. package/src/meta/defineMetadata.ts +1 -1
  280. package/src/meta/index.ts +2 -3
  281. package/src/orm/createSqliteDialect.ts +17 -0
  282. package/src/orm/index.ts +1 -0
  283. package/src/orpc/createOpenApiContractClient.ts +3 -3
  284. package/src/orpc/index.ts +1 -1
  285. package/src/password/PHC.ts +3 -3
  286. package/src/password/createArgon2PasswordAlgorithm.ts +2 -2
  287. package/src/password/createBase64PasswordAlgorithm.ts +2 -2
  288. package/src/password/createBcryptPasswordAlgorithm.ts +4 -2
  289. package/src/password/createPBKDF2PasswordAlgorithm.ts +2 -2
  290. package/src/password/createScryptPasswordAlgorithm.ts +4 -4
  291. package/src/password/index.ts +2 -2
  292. package/src/resource/ListQuery.ts +4 -1
  293. package/src/resource/index.ts +3 -3
  294. package/src/resource/schema/index.ts +4 -4
  295. package/src/s3/formatS3Url.test.ts +1 -1
  296. package/src/s3/formatS3Url.ts +2 -2
  297. package/src/s3/index.ts +1 -1
  298. package/src/s3/parseS3Url.ts +1 -1
  299. package/src/schema/SchemaRegistry.ts +2 -2
  300. package/src/schema/TypeSchema.d.ts +6 -6
  301. package/src/schema/createSchemaData.ts +5 -5
  302. package/src/schema/findJsonSchemaByPath.ts +5 -5
  303. package/src/schema/formatZodError.test.ts +2 -1
  304. package/src/schema/formatZodError.ts +50 -62
  305. package/src/schema/index.ts +5 -5
  306. package/src/schema/toJsonSchema.ts +6 -6
  307. package/src/schema/validate.ts +2 -2
  308. package/src/utils/buildRedactorFormSchema.ts +4 -4
  309. package/src/utils/formatNumber.ts +18 -0
  310. package/src/utils/formatPercent.ts +17 -0
  311. package/src/utils/index.ts +3 -3
  312. package/src/utils/resolveFeatureOptions.ts +1 -1
@@ -0,0 +1,394 @@
1
+ import { LogCluster } from './LogCluster';
2
+ import { Node } from './Node';
3
+ import type { ClusterUpdateType, DrainOptions, SearchStrategy } from './types';
4
+
5
+ /**
6
+ * Simple LRU cache using Map iteration order.
7
+ * Map preserves insertion order; re-inserting a key moves it to the end.
8
+ */
9
+ class SimpleLRU<K, V> {
10
+ private readonly map = new Map<K, V>();
11
+ constructor(private readonly max: number) {}
12
+
13
+ get(key: K): V | undefined {
14
+ const value = this.map.get(key);
15
+ if (value !== undefined) {
16
+ // Move to end (most recently used)
17
+ this.map.delete(key);
18
+ this.map.set(key, value);
19
+ }
20
+ return value;
21
+ }
22
+
23
+ set(key: K, value: V): void {
24
+ if (this.map.has(key)) {
25
+ this.map.delete(key);
26
+ } else if (this.map.size >= this.max) {
27
+ // Evict oldest (first key)
28
+ const first = this.map.keys().next().value;
29
+ if (first !== undefined) {
30
+ this.map.delete(first);
31
+ }
32
+ }
33
+ this.map.set(key, value);
34
+ }
35
+
36
+ values(): IterableIterator<V> {
37
+ return this.map.values();
38
+ }
39
+ }
40
+
41
+ function isSliceEqual<T>(a: T[], b: T[]): boolean {
42
+ if (a.length !== b.length) return false;
43
+ for (let i = 0; i < a.length; i++) {
44
+ if (a[i] !== b[i]) return false;
45
+ }
46
+ return true;
47
+ }
48
+
49
+ function hasNumbers(s: string): boolean {
50
+ return /\d/.test(s);
51
+ }
52
+
53
+ /**
54
+ * Core Drain algorithm for log clustering.
55
+ *
56
+ * Drain is an online log parsing algorithm that groups log messages into clusters
57
+ * based on their structural similarity, extracting templates by parameterizing
58
+ * variable parts of the logs.
59
+ */
60
+ export class Drain {
61
+ public readonly logClusterDepth: number;
62
+ public readonly maxNodeDepth: number;
63
+ public readonly simTh: number;
64
+ public readonly maxChildren: number;
65
+ public readonly rootNode: Node;
66
+ public readonly maxClusters: number;
67
+ public readonly extraDelimiters: readonly string[];
68
+ public readonly paramStr: string;
69
+ public readonly parametrizeNumericTokens: boolean;
70
+
71
+ private readonly idToCluster: SimpleLRU<number, LogCluster>;
72
+ private clustersCounter: number = 0;
73
+
74
+ constructor(options: DrainOptions = {}) {
75
+ this.logClusterDepth = options.logClusterDepth ?? 4;
76
+ this.simTh = options.simTh ?? 0.4;
77
+ this.maxChildren = options.maxChildren ?? 100;
78
+ this.maxClusters = options.maxClusters ?? 1000;
79
+ this.extraDelimiters = options.extraDelimiters ?? [];
80
+ this.paramStr = options.paramStr ?? '<*>';
81
+ this.parametrizeNumericTokens = options.parametrizeNumericTokens ?? true;
82
+
83
+ if (this.logClusterDepth < 3) {
84
+ throw new Error('depth argument must be at least 3');
85
+ }
86
+
87
+ this.maxNodeDepth = this.logClusterDepth - 2;
88
+ this.rootNode = Node.newNode();
89
+ this.idToCluster = new SimpleLRU<number, LogCluster>(this.maxClusters);
90
+ }
91
+
92
+ addLogMessage(content: string): { cluster: LogCluster; updateType: ClusterUpdateType } {
93
+ const contentTokens = this.getContentAsTokens(content);
94
+ const matchCluster = this.treeSearch(this.rootNode, contentTokens, this.simTh, false);
95
+
96
+ let updateType: ClusterUpdateType = 'none';
97
+
98
+ if (matchCluster === null) {
99
+ this.clustersCounter++;
100
+ const clusterId = this.clustersCounter;
101
+ const cluster = new LogCluster(clusterId, contentTokens);
102
+ this.idToCluster.set(clusterId, cluster);
103
+ this.addSeqToPrefixTree(this.rootNode, cluster);
104
+ updateType = 'created';
105
+ return { cluster, updateType };
106
+ }
107
+
108
+ const newTemplateTokens = this.createTemplate(contentTokens, matchCluster.logTemplateTokens);
109
+
110
+ if (!isSliceEqual(newTemplateTokens, matchCluster.logTemplateTokens)) {
111
+ matchCluster.logTemplateTokens = newTemplateTokens;
112
+ updateType = 'templateChanged';
113
+ }
114
+
115
+ matchCluster.size++;
116
+
117
+ // Touch cluster to update its position in the LRU cache
118
+ this.idToCluster.get(matchCluster.clusterId);
119
+
120
+ return { cluster: matchCluster, updateType };
121
+ }
122
+
123
+ getContentAsTokens(content: string): string[] {
124
+ let processed = content.trim();
125
+ for (const delimiter of this.extraDelimiters) {
126
+ processed = processed.replaceAll(delimiter, ' ');
127
+ }
128
+ return processed.split(/\s+/).filter((token) => token.length > 0);
129
+ }
130
+
131
+ private treeSearch(rootNode: Node, tokens: string[], simTh: number, includeParams: boolean): LogCluster | null {
132
+ const tokenCount = tokens.length;
133
+ const firstNode = rootNode.keyToChildNode.get(tokenCount.toString());
134
+
135
+ if (firstNode === undefined) return null;
136
+
137
+ if (tokenCount === 0) {
138
+ const firstClusterId = firstNode.clusterIds[0];
139
+ if (firstClusterId === undefined) return null;
140
+ return this.idToCluster.get(firstClusterId) ?? null;
141
+ }
142
+
143
+ let currentNode: Node = firstNode;
144
+ let currentNodeDepth = 1;
145
+ for (const token of tokens) {
146
+ if (currentNodeDepth >= this.maxNodeDepth || currentNodeDepth === tokenCount) break;
147
+
148
+ const keyToChildNode: Map<string, Node> = currentNode.keyToChildNode;
149
+ let nextNode: Node | undefined = keyToChildNode.get(token);
150
+ if (nextNode === undefined) {
151
+ nextNode = keyToChildNode.get(this.paramStr);
152
+ }
153
+ if (nextNode === undefined) return null;
154
+
155
+ currentNode = nextNode;
156
+ currentNodeDepth += 1;
157
+ }
158
+
159
+ return this.fastMatch(currentNode.clusterIds, tokens, simTh, includeParams);
160
+ }
161
+
162
+ private fastMatch(clusterIds: number[], tokens: string[], simTh: number, includeParams: boolean): LogCluster | null {
163
+ let maxSim = -1;
164
+ let maxParamCount = -1;
165
+ let maxCluster: LogCluster | null = null;
166
+
167
+ for (const clusterId of clusterIds) {
168
+ const cluster = this.idToCluster.get(clusterId);
169
+ if (cluster === undefined) continue;
170
+
171
+ const [currentSim, paramCount] = this.getSeqDistance(cluster.logTemplateTokens, tokens, includeParams);
172
+
173
+ if (currentSim > maxSim || (currentSim === maxSim && paramCount > maxParamCount)) {
174
+ maxSim = currentSim;
175
+ maxParamCount = paramCount;
176
+ maxCluster = cluster;
177
+ }
178
+ }
179
+
180
+ return maxSim >= simTh ? maxCluster : null;
181
+ }
182
+
183
+ private getSeqDistance(seq1: string[], seq2: string[], includeParams: boolean): [number, number] {
184
+ if (seq1.length !== seq2.length) {
185
+ throw new Error(`seq1 length ${seq1.length} not equals to seq2 length ${seq2.length}`);
186
+ }
187
+
188
+ if (seq1.length === 0) return [1, 0];
189
+
190
+ let simTokens = 0;
191
+ let paramCount = 0;
192
+
193
+ for (let i = 0; i < seq1.length; i++) {
194
+ if (seq1[i] === this.paramStr) {
195
+ paramCount++;
196
+ } else if (seq1[i] === seq2[i]) {
197
+ simTokens++;
198
+ }
199
+ }
200
+
201
+ if (includeParams) simTokens += paramCount;
202
+
203
+ return [simTokens / seq1.length, paramCount];
204
+ }
205
+
206
+ private addSeqToPrefixTree(rootNode: Node, cluster: LogCluster): void {
207
+ const tokenCount = cluster.logTemplateTokens.length;
208
+ const tokenCountStr = tokenCount.toString();
209
+ let firstLayerNode = rootNode.keyToChildNode.get(tokenCountStr);
210
+ if (firstLayerNode === undefined) {
211
+ firstLayerNode = Node.newNode();
212
+ rootNode.keyToChildNode.set(tokenCountStr, firstLayerNode);
213
+ }
214
+
215
+ let currentNode = firstLayerNode;
216
+
217
+ if (tokenCount === 0) {
218
+ currentNode.clusterIds = [cluster.clusterId];
219
+ return;
220
+ }
221
+
222
+ let currentDepth = 1;
223
+ for (const token of cluster.logTemplateTokens) {
224
+ if (currentDepth >= this.maxNodeDepth || currentDepth >= tokenCount) {
225
+ const newClusterIds: number[] = [];
226
+ for (const clusterId of currentNode.clusterIds) {
227
+ if (this.idToCluster.get(clusterId) !== undefined) {
228
+ newClusterIds.push(clusterId);
229
+ }
230
+ }
231
+ newClusterIds.push(cluster.clusterId);
232
+ currentNode.clusterIds = newClusterIds;
233
+ break;
234
+ }
235
+
236
+ if (!currentNode.keyToChildNode.has(token)) {
237
+ if (this.parametrizeNumericTokens && hasNumbers(token)) {
238
+ const node = currentNode.keyToChildNode.get(this.paramStr);
239
+ if (node === undefined) {
240
+ const newNode = Node.newNode();
241
+ currentNode.keyToChildNode.set(this.paramStr, newNode);
242
+ currentNode = newNode;
243
+ } else {
244
+ currentNode = node;
245
+ }
246
+ } else {
247
+ const wildcardNode = currentNode.keyToChildNode.get(this.paramStr);
248
+ if (wildcardNode !== undefined) {
249
+ if (currentNode.keyToChildNode.size < this.maxChildren) {
250
+ const newNode = Node.newNode();
251
+ currentNode.keyToChildNode.set(token, newNode);
252
+ currentNode = newNode;
253
+ } else {
254
+ currentNode = wildcardNode;
255
+ }
256
+ } else {
257
+ if (currentNode.keyToChildNode.size + 1 < this.maxChildren) {
258
+ const newNode = Node.newNode();
259
+ currentNode.keyToChildNode.set(token, newNode);
260
+ currentNode = newNode;
261
+ } else if (currentNode.keyToChildNode.size + 1 === this.maxChildren) {
262
+ const newNode = Node.newNode();
263
+ currentNode.keyToChildNode.set(this.paramStr, newNode);
264
+ currentNode = newNode;
265
+ } else {
266
+ currentNode = currentNode.keyToChildNode.get(this.paramStr)!;
267
+ }
268
+ }
269
+ }
270
+ } else {
271
+ currentNode = currentNode.keyToChildNode.get(token)!;
272
+ }
273
+
274
+ currentDepth++;
275
+ }
276
+ }
277
+
278
+ private createTemplate(seq1: string[], seq2: string[]): string[] {
279
+ if (seq1.length !== seq2.length) {
280
+ throw new Error(`seq1 length ${seq1.length} not equals to seq2 length ${seq2.length}`);
281
+ }
282
+ const retVal = [...seq2];
283
+ for (let i = 0; i < seq1.length; i++) {
284
+ if (seq1[i] !== seq2[i]) {
285
+ retVal[i] = this.paramStr;
286
+ }
287
+ }
288
+ return retVal;
289
+ }
290
+
291
+ match(content: string, strategy: SearchStrategy = 'never'): LogCluster | null {
292
+ const requiredSimTh = 1.0;
293
+ const contentTokens = this.getContentAsTokens(content);
294
+
295
+ const fullSearch = (): LogCluster | null => {
296
+ const allIds = this.getClustersIdsForSeqLen(contentTokens.length);
297
+ return this.fastMatch(allIds, contentTokens, requiredSimTh, true);
298
+ };
299
+
300
+ if (strategy === 'always') return fullSearch();
301
+
302
+ const matchCluster = this.treeSearch(this.rootNode, contentTokens, requiredSimTh, true);
303
+ if (matchCluster !== null) return matchCluster;
304
+
305
+ if (strategy === 'never') return null;
306
+
307
+ return fullSearch();
308
+ }
309
+
310
+ private getClustersIdsForSeqLen(seqLen: number): number[] {
311
+ const appendClusterRecursive = (node: Node, idListToFill: number[]): void => {
312
+ idListToFill.push(...node.clusterIds);
313
+ for (const childNode of node.keyToChildNode.values()) {
314
+ appendClusterRecursive(childNode, idListToFill);
315
+ }
316
+ };
317
+
318
+ const currentNode = this.rootNode.keyToChildNode.get(seqLen.toString());
319
+ if (currentNode === undefined) return [];
320
+
321
+ const target: number[] = [];
322
+ appendClusterRecursive(currentNode, target);
323
+ return target;
324
+ }
325
+
326
+ getClusters(): LogCluster[] {
327
+ return [...this.idToCluster.values()];
328
+ }
329
+
330
+ toJSON() {
331
+ return {
332
+ logClusterDepth: this.logClusterDepth,
333
+ maxNodeDepth: this.maxNodeDepth,
334
+ simTh: this.simTh,
335
+ maxChildren: this.maxChildren,
336
+ rootNode: this.rootNode.toJSON(),
337
+ maxClusters: this.maxClusters,
338
+ extraDelimiters: [...this.extraDelimiters],
339
+ paramStr: this.paramStr,
340
+ parametrizeNumericTokens: this.parametrizeNumericTokens,
341
+ clusters: this.getClusters().map((c) => c.toJSON()),
342
+ clustersCounter: this.clustersCounter,
343
+ };
344
+ }
345
+
346
+ static fromJSON(data: {
347
+ logClusterDepth: number;
348
+ maxNodeDepth: number;
349
+ simTh: number;
350
+ maxChildren: number;
351
+ rootNode: {
352
+ keyToChildNode: Record<string, unknown>;
353
+ clusterIds: number[];
354
+ };
355
+ maxClusters: number;
356
+ extraDelimiters: string[];
357
+ paramStr: string;
358
+ parametrizeNumericTokens: boolean;
359
+ clusters: Array<{
360
+ clusterId: number;
361
+ logTemplateTokens: string[];
362
+ size: number;
363
+ }>;
364
+ clustersCounter: number;
365
+ }): Drain {
366
+ const drain = new Drain({
367
+ logClusterDepth: data.logClusterDepth,
368
+ simTh: data.simTh,
369
+ maxChildren: data.maxChildren,
370
+ maxClusters: data.maxClusters,
371
+ extraDelimiters: data.extraDelimiters,
372
+ paramStr: data.paramStr,
373
+ parametrizeNumericTokens: data.parametrizeNumericTokens,
374
+ });
375
+
376
+ drain.rootNode.keyToChildNode.clear();
377
+ drain.rootNode.clusterIds = [];
378
+
379
+ const rootNode = Node.fromJSON(data.rootNode);
380
+ for (const [key, node] of rootNode.keyToChildNode) {
381
+ drain.rootNode.keyToChildNode.set(key, node);
382
+ }
383
+ drain.rootNode.clusterIds = rootNode.clusterIds;
384
+
385
+ for (const clusterData of data.clusters) {
386
+ const cluster = LogCluster.fromJSON(clusterData);
387
+ drain.idToCluster.set(cluster.clusterId, cluster);
388
+ }
389
+
390
+ drain.clustersCounter = data.clustersCounter;
391
+
392
+ return drain;
393
+ }
394
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Represents a cluster of similar log messages with a common template.
3
+ */
4
+ export class LogCluster {
5
+ constructor(
6
+ public readonly clusterId: number,
7
+ public logTemplateTokens: string[],
8
+ public size: number = 1,
9
+ ) {}
10
+
11
+ /**
12
+ * Returns the template as a space-separated string.
13
+ */
14
+ getTemplate(): string {
15
+ return this.logTemplateTokens.join(' ');
16
+ }
17
+
18
+ /**
19
+ * Returns a string representation of the cluster.
20
+ */
21
+ toString(): string {
22
+ return `ID=${this.clusterId.toString().padEnd(5)} : size=${this.size.toString().padEnd(10)}: ${this.getTemplate()}`;
23
+ }
24
+
25
+ /**
26
+ * Creates a LogCluster from JSON data.
27
+ */
28
+ static fromJSON(data: { clusterId: number; logTemplateTokens: string[]; size: number }): LogCluster {
29
+ return new LogCluster(data.clusterId, data.logTemplateTokens, data.size);
30
+ }
31
+
32
+ /**
33
+ * Converts the cluster to JSON for serialization.
34
+ */
35
+ toJSON(): {
36
+ clusterId: number;
37
+ logTemplateTokens: string[];
38
+ size: number;
39
+ } {
40
+ return {
41
+ clusterId: this.clusterId,
42
+ logTemplateTokens: this.logTemplateTokens,
43
+ size: this.size,
44
+ };
45
+ }
46
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Represents a node in the prefix tree used by Drain algorithm.
3
+ */
4
+ export class Node {
5
+ /**
6
+ * Map of token keys to child nodes.
7
+ */
8
+ public readonly keyToChildNode: Map<string, Node> = new Map();
9
+
10
+ /**
11
+ * List of cluster IDs associated with this node.
12
+ */
13
+ public clusterIds: number[] = [];
14
+
15
+ /**
16
+ * Creates a new empty node.
17
+ */
18
+ static newNode(): Node {
19
+ return new Node();
20
+ }
21
+
22
+ /**
23
+ * Converts the node to JSON for serialization.
24
+ */
25
+ toJSON(): {
26
+ keyToChildNode: Record<string, unknown>;
27
+ clusterIds: number[];
28
+ } {
29
+ const keyToChildNode: Record<string, unknown> = {};
30
+ for (const [key, node] of this.keyToChildNode) {
31
+ keyToChildNode[key] = node.toJSON();
32
+ }
33
+ return {
34
+ keyToChildNode,
35
+ clusterIds: this.clusterIds,
36
+ };
37
+ }
38
+
39
+ /**
40
+ * Creates a Node from JSON data.
41
+ */
42
+ static fromJSON(data: { keyToChildNode: Record<string, unknown>; clusterIds: number[] }): Node {
43
+ const node = new Node();
44
+ node.clusterIds = data.clusterIds;
45
+ for (const [key, childData] of Object.entries(data.keyToChildNode)) {
46
+ node.keyToChildNode.set(
47
+ key,
48
+ Node.fromJSON(childData as { keyToChildNode: Record<string, unknown>; clusterIds: number[] }),
49
+ );
50
+ }
51
+ return node;
52
+ }
53
+ }