@kodelyth/zalouser 2026.5.42 → 2026.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/klaw.plugin.json +286 -3
  2. package/package.json +17 -4
  3. package/api.ts +0 -9
  4. package/channel-plugin-api.ts +0 -3
  5. package/contract-api.ts +0 -2
  6. package/doctor-contract-api.ts +0 -1
  7. package/index.ts +0 -34
  8. package/runtime-api.ts +0 -62
  9. package/secret-contract-api.ts +0 -4
  10. package/setup-entry.ts +0 -9
  11. package/setup-plugin-api.ts +0 -2
  12. package/src/accounts.runtime.ts +0 -1
  13. package/src/accounts.test-mocks.ts +0 -14
  14. package/src/accounts.test.ts +0 -298
  15. package/src/accounts.ts +0 -136
  16. package/src/channel-api.ts +0 -16
  17. package/src/channel.adapters.ts +0 -432
  18. package/src/channel.directory.test.ts +0 -59
  19. package/src/channel.runtime.ts +0 -12
  20. package/src/channel.sendpayload.test.ts +0 -311
  21. package/src/channel.setup.test.ts +0 -30
  22. package/src/channel.setup.ts +0 -12
  23. package/src/channel.test.ts +0 -424
  24. package/src/channel.ts +0 -221
  25. package/src/config-schema.ts +0 -33
  26. package/src/directory.ts +0 -54
  27. package/src/doctor-contract.ts +0 -156
  28. package/src/doctor.test.ts +0 -87
  29. package/src/doctor.ts +0 -37
  30. package/src/group-policy.test.ts +0 -61
  31. package/src/group-policy.ts +0 -83
  32. package/src/message-sid.test.ts +0 -66
  33. package/src/message-sid.ts +0 -80
  34. package/src/monitor.account-scope.test.ts +0 -122
  35. package/src/monitor.group-gating.test.ts +0 -967
  36. package/src/monitor.send-mocks.ts +0 -20
  37. package/src/monitor.ts +0 -1057
  38. package/src/probe.test.ts +0 -60
  39. package/src/probe.ts +0 -35
  40. package/src/qr-temp-file.ts +0 -19
  41. package/src/reaction.test.ts +0 -19
  42. package/src/reaction.ts +0 -32
  43. package/src/runtime.ts +0 -9
  44. package/src/security-audit.test.ts +0 -83
  45. package/src/security-audit.ts +0 -71
  46. package/src/send-receipt.ts +0 -31
  47. package/src/send.test.ts +0 -424
  48. package/src/send.ts +0 -280
  49. package/src/session-route.ts +0 -121
  50. package/src/setup-core.ts +0 -36
  51. package/src/setup-surface.test.ts +0 -367
  52. package/src/setup-surface.ts +0 -481
  53. package/src/setup-test-helpers.ts +0 -42
  54. package/src/shared.ts +0 -92
  55. package/src/status-issues.test.ts +0 -31
  56. package/src/status-issues.ts +0 -55
  57. package/src/test-helpers.ts +0 -26
  58. package/src/text-styles.test.ts +0 -203
  59. package/src/text-styles.ts +0 -540
  60. package/src/tool.test.ts +0 -212
  61. package/src/tool.ts +0 -200
  62. package/src/types.ts +0 -127
  63. package/src/zalo-js.credentials.test.ts +0 -465
  64. package/src/zalo-js.test-mocks.ts +0 -89
  65. package/src/zalo-js.ts +0 -1889
  66. package/src/zca-client.test.ts +0 -27
  67. package/src/zca-client.ts +0 -259
  68. package/src/zca-constants.ts +0 -55
  69. package/src/zca-js-exports.d.ts +0 -22
  70. package/test-api.ts +0 -21
  71. package/tsconfig.json +0 -16
@@ -1,540 +0,0 @@
1
- import { TextStyle, type Style } from "./zca-constants.js";
2
-
3
- const ESCAPE_SENTINEL_START = "\u0001";
4
- const ESCAPE_SENTINEL_END = "\u0002";
5
-
6
- type InlineStyle = (typeof TextStyle)[keyof typeof TextStyle];
7
-
8
- type LineStyle = {
9
- lineIndex: number;
10
- style: InlineStyle;
11
- indentSize?: number;
12
- };
13
-
14
- type Segment = {
15
- text: string;
16
- styles: InlineStyle[];
17
- };
18
-
19
- type InlineMarker = {
20
- pattern: RegExp;
21
- extractText: (match: RegExpExecArray) => string;
22
- resolveStyles?: (match: RegExpExecArray) => InlineStyle[];
23
- literal?: boolean;
24
- };
25
-
26
- type ResolvedInlineMatch = {
27
- match: RegExpExecArray;
28
- marker: InlineMarker;
29
- styles: InlineStyle[];
30
- text: string;
31
- priority: number;
32
- };
33
-
34
- type FenceMarker = {
35
- char: "`" | "~";
36
- length: number;
37
- indent: number;
38
- };
39
-
40
- type ActiveFence = FenceMarker & {
41
- quoteIndent: number;
42
- };
43
-
44
- const TAG_STYLE_MAP: Record<string, InlineStyle | null> = {
45
- red: TextStyle.Red,
46
- orange: TextStyle.Orange,
47
- yellow: TextStyle.Yellow,
48
- green: TextStyle.Green,
49
- small: null,
50
- big: TextStyle.Big,
51
- underline: TextStyle.Underline,
52
- };
53
-
54
- const INLINE_MARKERS: InlineMarker[] = [
55
- {
56
- pattern: /`([^`\n]+)`/g,
57
- extractText: (match) => match[0],
58
- literal: true,
59
- },
60
- {
61
- pattern: /\\([*_~#\\{}>+\-`])/g,
62
- extractText: (match) => match[1],
63
- literal: true,
64
- },
65
- {
66
- pattern: new RegExp(`\\{(${Object.keys(TAG_STYLE_MAP).join("|")})\\}(.+?)\\{/\\1\\}`, "g"),
67
- extractText: (match) => match[2],
68
- resolveStyles: (match) => {
69
- const style = TAG_STYLE_MAP[match[1]];
70
- return style ? [style] : [];
71
- },
72
- },
73
- {
74
- pattern: /(?<!\*)\*\*\*(?=\S)([^\n]*?\S)(?<!\*)\*\*\*(?!\*)/g,
75
- extractText: (match) => match[1],
76
- resolveStyles: () => [TextStyle.Bold, TextStyle.Italic],
77
- },
78
- {
79
- pattern: /(?<!\*)\*\*(?![\s*])([^\n]*?\S)(?<!\*)\*\*(?!\*)/g,
80
- extractText: (match) => match[1],
81
- resolveStyles: () => [TextStyle.Bold],
82
- },
83
- {
84
- pattern: /(?<![\w_])__(?![\s_])([^\n]*?\S)(?<!_)__(?![\w_])/g,
85
- extractText: (match) => match[1],
86
- resolveStyles: () => [TextStyle.Bold],
87
- },
88
- {
89
- pattern: /(?<!~)~~(?=\S)([^\n]*?\S)(?<!~)~~(?!~)/g,
90
- extractText: (match) => match[1],
91
- resolveStyles: () => [TextStyle.StrikeThrough],
92
- },
93
- {
94
- pattern: /(?<!\*)\*(?![\s*])([^\n]*?\S)(?<!\*)\*(?!\*)/g,
95
- extractText: (match) => match[1],
96
- resolveStyles: () => [TextStyle.Italic],
97
- },
98
- {
99
- pattern: /(?<![\w_])_(?![\s_])([^\n]*?\S)(?<!_)_(?![\w_])/g,
100
- extractText: (match) => match[1],
101
- resolveStyles: () => [TextStyle.Italic],
102
- },
103
- ];
104
-
105
- export function parseZalouserTextStyles(input: string): { text: string; styles: Style[] } {
106
- const allStyles: Style[] = [];
107
-
108
- const escapeMap: string[] = [];
109
- const lines = input.replace(/\r\n?/g, "\n").split("\n");
110
- const lineStyles: LineStyle[] = [];
111
- const processedLines: string[] = [];
112
- let activeFence: ActiveFence | null = null;
113
-
114
- for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
115
- const rawLine = lines[lineIndex];
116
- const { text: unquotedLine, indent: baseIndent } = stripQuotePrefix(rawLine);
117
-
118
- if (activeFence) {
119
- const codeLine =
120
- activeFence.quoteIndent > 0
121
- ? stripQuotePrefix(rawLine, activeFence.quoteIndent).text
122
- : rawLine;
123
- if (isClosingFence(codeLine, activeFence)) {
124
- activeFence = null;
125
- continue;
126
- }
127
- processedLines.push(
128
- escapeLiteralText(
129
- normalizeCodeBlockLeadingWhitespace(stripCodeFenceIndent(codeLine, activeFence.indent)),
130
- escapeMap,
131
- ),
132
- );
133
- continue;
134
- }
135
-
136
- let line = unquotedLine;
137
- const openingFence = resolveOpeningFence(rawLine);
138
- if (openingFence) {
139
- const fenceLine = openingFence.quoteIndent > 0 ? unquotedLine : rawLine;
140
- if (!hasClosingFence(lines, lineIndex + 1, openingFence)) {
141
- processedLines.push(escapeLiteralText(fenceLine, escapeMap));
142
- activeFence = openingFence;
143
- continue;
144
- }
145
- activeFence = openingFence;
146
- continue;
147
- }
148
-
149
- const outputLineIndex = processedLines.length;
150
- if (isIndentedCodeBlockLine(line)) {
151
- if (baseIndent > 0) {
152
- lineStyles.push({
153
- lineIndex: outputLineIndex,
154
- style: TextStyle.Indent,
155
- indentSize: baseIndent,
156
- });
157
- }
158
- processedLines.push(escapeLiteralText(normalizeCodeBlockLeadingWhitespace(line), escapeMap));
159
- continue;
160
- }
161
-
162
- const { text: markdownLine, size: markdownPadding } = stripOptionalMarkdownPadding(line);
163
-
164
- const headingMatch = markdownLine.match(/^(#{1,4})\s(.*)$/);
165
- if (headingMatch) {
166
- const depth = headingMatch[1].length;
167
- lineStyles.push({ lineIndex: outputLineIndex, style: TextStyle.Bold });
168
- if (depth === 1) {
169
- lineStyles.push({ lineIndex: outputLineIndex, style: TextStyle.Big });
170
- }
171
- if (baseIndent > 0) {
172
- lineStyles.push({
173
- lineIndex: outputLineIndex,
174
- style: TextStyle.Indent,
175
- indentSize: baseIndent,
176
- });
177
- }
178
- processedLines.push(headingMatch[2]);
179
- continue;
180
- }
181
-
182
- const indentMatch = markdownLine.match(/^(\s+)(.*)$/);
183
- let indentLevel = 0;
184
- let content = markdownLine;
185
- if (indentMatch) {
186
- indentLevel = clampIndent(indentMatch[1].length);
187
- content = indentMatch[2];
188
- }
189
- const totalIndent = Math.min(5, baseIndent + indentLevel);
190
-
191
- if (/^[-*+]\s\[[ xX]\]\s/.test(content)) {
192
- if (totalIndent > 0) {
193
- lineStyles.push({
194
- lineIndex: outputLineIndex,
195
- style: TextStyle.Indent,
196
- indentSize: totalIndent,
197
- });
198
- }
199
- processedLines.push(content);
200
- continue;
201
- }
202
-
203
- const orderedListMatch = content.match(/^(\d+)\.\s(.*)$/);
204
- if (orderedListMatch) {
205
- if (totalIndent > 0) {
206
- lineStyles.push({
207
- lineIndex: outputLineIndex,
208
- style: TextStyle.Indent,
209
- indentSize: totalIndent,
210
- });
211
- }
212
- lineStyles.push({ lineIndex: outputLineIndex, style: TextStyle.OrderedList });
213
- processedLines.push(orderedListMatch[2]);
214
- continue;
215
- }
216
-
217
- const unorderedListMatch = content.match(/^[-*+]\s(.*)$/);
218
- if (unorderedListMatch) {
219
- if (totalIndent > 0) {
220
- lineStyles.push({
221
- lineIndex: outputLineIndex,
222
- style: TextStyle.Indent,
223
- indentSize: totalIndent,
224
- });
225
- }
226
- lineStyles.push({ lineIndex: outputLineIndex, style: TextStyle.UnorderedList });
227
- processedLines.push(unorderedListMatch[1]);
228
- continue;
229
- }
230
-
231
- if (markdownPadding > 0) {
232
- if (baseIndent > 0) {
233
- lineStyles.push({
234
- lineIndex: outputLineIndex,
235
- style: TextStyle.Indent,
236
- indentSize: baseIndent,
237
- });
238
- }
239
- processedLines.push(line);
240
- continue;
241
- }
242
-
243
- if (totalIndent > 0) {
244
- lineStyles.push({
245
- lineIndex: outputLineIndex,
246
- style: TextStyle.Indent,
247
- indentSize: totalIndent,
248
- });
249
- processedLines.push(content);
250
- continue;
251
- }
252
-
253
- processedLines.push(line);
254
- }
255
-
256
- const segments = parseInlineSegments(processedLines.join("\n"));
257
-
258
- let plainText = "";
259
- for (const segment of segments) {
260
- const start = plainText.length;
261
- plainText += segment.text;
262
- for (const style of segment.styles) {
263
- allStyles.push({ start, len: segment.text.length, st: style } as Style);
264
- }
265
- }
266
-
267
- if (escapeMap.length > 0) {
268
- const escapeRegex = new RegExp(`${ESCAPE_SENTINEL_START}(\\d+)${ESCAPE_SENTINEL_END}`, "g");
269
- const shifts: Array<{ pos: number; delta: number }> = [];
270
- let cumulativeDelta = 0;
271
-
272
- for (const match of plainText.matchAll(escapeRegex)) {
273
- const escapeIndex = Number.parseInt(match[1], 10);
274
- cumulativeDelta += match[0].length - escapeMap[escapeIndex].length;
275
- shifts.push({ pos: (match.index ?? 0) + match[0].length, delta: cumulativeDelta });
276
- }
277
-
278
- for (const style of allStyles) {
279
- let startDelta = 0;
280
- let endDelta = 0;
281
- const end = style.start + style.len;
282
- for (const shift of shifts) {
283
- if (shift.pos <= style.start) {
284
- startDelta = shift.delta;
285
- }
286
- if (shift.pos <= end) {
287
- endDelta = shift.delta;
288
- }
289
- }
290
- style.start -= startDelta;
291
- style.len -= endDelta - startDelta;
292
- }
293
-
294
- plainText = plainText.replace(
295
- escapeRegex,
296
- (_match, index) => escapeMap[Number.parseInt(index, 10)],
297
- );
298
- }
299
-
300
- const finalLines = plainText.split("\n");
301
- let offset = 0;
302
- for (let lineIndex = 0; lineIndex < finalLines.length; lineIndex += 1) {
303
- const lineLength = finalLines[lineIndex].length;
304
- if (lineLength > 0) {
305
- for (const lineStyle of lineStyles) {
306
- if (lineStyle.lineIndex !== lineIndex) {
307
- continue;
308
- }
309
-
310
- if (lineStyle.style === TextStyle.Indent) {
311
- allStyles.push({
312
- start: offset,
313
- len: lineLength,
314
- st: TextStyle.Indent,
315
- indentSize: lineStyle.indentSize,
316
- });
317
- } else {
318
- allStyles.push({ start: offset, len: lineLength, st: lineStyle.style } as Style);
319
- }
320
- }
321
- }
322
- offset += lineLength + 1;
323
- }
324
-
325
- return { text: plainText, styles: allStyles };
326
- }
327
-
328
- function clampIndent(spaceCount: number): number {
329
- return Math.min(5, Math.max(1, Math.floor(spaceCount / 2)));
330
- }
331
-
332
- function stripOptionalMarkdownPadding(line: string): { text: string; size: number } {
333
- const match = line.match(/^( {1,3})(?=\S)/);
334
- if (!match) {
335
- return { text: line, size: 0 };
336
- }
337
- return {
338
- text: line.slice(match[1].length),
339
- size: match[1].length,
340
- };
341
- }
342
-
343
- function hasClosingFence(lines: string[], startIndex: number, fence: ActiveFence): boolean {
344
- for (let index = startIndex; index < lines.length; index += 1) {
345
- const candidate =
346
- fence.quoteIndent > 0 ? stripQuotePrefix(lines[index], fence.quoteIndent).text : lines[index];
347
- if (isClosingFence(candidate, fence)) {
348
- return true;
349
- }
350
- }
351
- return false;
352
- }
353
-
354
- function resolveOpeningFence(line: string): ActiveFence | null {
355
- const directFence = parseFenceMarker(line);
356
- if (directFence) {
357
- return { ...directFence, quoteIndent: 0 };
358
- }
359
-
360
- const quoted = stripQuotePrefix(line);
361
- if (quoted.indent === 0) {
362
- return null;
363
- }
364
-
365
- const quotedFence = parseFenceMarker(quoted.text);
366
- if (!quotedFence) {
367
- return null;
368
- }
369
-
370
- return {
371
- ...quotedFence,
372
- quoteIndent: quoted.indent,
373
- };
374
- }
375
-
376
- function stripQuotePrefix(
377
- line: string,
378
- maxDepth = Number.POSITIVE_INFINITY,
379
- ): { text: string; indent: number } {
380
- let cursor = 0;
381
- while (cursor < line.length && cursor < 3 && line[cursor] === " ") {
382
- cursor += 1;
383
- }
384
-
385
- let removedDepth = 0;
386
- let consumedCursor = cursor;
387
- while (removedDepth < maxDepth && consumedCursor < line.length && line[consumedCursor] === ">") {
388
- removedDepth += 1;
389
- consumedCursor += 1;
390
- if (line[consumedCursor] === " ") {
391
- consumedCursor += 1;
392
- }
393
- }
394
-
395
- if (removedDepth === 0) {
396
- return { text: line, indent: 0 };
397
- }
398
-
399
- return {
400
- text: line.slice(consumedCursor),
401
- indent: Math.min(5, removedDepth),
402
- };
403
- }
404
-
405
- function parseFenceMarker(line: string): FenceMarker | null {
406
- const match = line.match(/^([ ]{0,3})(`{3,}|~{3,})(.*)$/);
407
- if (!match) {
408
- return null;
409
- }
410
-
411
- const marker = match[2];
412
- const char = marker[0];
413
- if (char !== "`" && char !== "~") {
414
- return null;
415
- }
416
-
417
- return {
418
- char,
419
- length: marker.length,
420
- indent: match[1].length,
421
- };
422
- }
423
-
424
- function isClosingFence(line: string, fence: FenceMarker): boolean {
425
- const match = line.match(/^([ ]{0,3})(`{3,}|~{3,})[ \t]*$/);
426
- if (!match) {
427
- return false;
428
- }
429
- return match[2][0] === fence.char && match[2].length >= fence.length;
430
- }
431
-
432
- function escapeLiteralText(input: string, escapeMap: string[]): string {
433
- return input.replace(/[\\*_~{}`]/g, (ch) => {
434
- const index = escapeMap.length;
435
- escapeMap.push(ch);
436
- return `\x01${index}\x02`;
437
- });
438
- }
439
-
440
- function parseInlineSegments(text: string, inheritedStyles: InlineStyle[] = []): Segment[] {
441
- const segments: Segment[] = [];
442
- let cursor = 0;
443
-
444
- while (cursor < text.length) {
445
- const nextMatch = findNextInlineMatch(text, cursor);
446
- if (!nextMatch) {
447
- pushSegment(segments, text.slice(cursor), inheritedStyles);
448
- break;
449
- }
450
-
451
- if (nextMatch.match.index > cursor) {
452
- pushSegment(segments, text.slice(cursor, nextMatch.match.index), inheritedStyles);
453
- }
454
-
455
- const combinedStyles = [...inheritedStyles, ...nextMatch.styles];
456
- if (nextMatch.marker.literal) {
457
- pushSegment(segments, nextMatch.text, combinedStyles);
458
- } else {
459
- segments.push(...parseInlineSegments(nextMatch.text, combinedStyles));
460
- }
461
-
462
- cursor = nextMatch.match.index + nextMatch.match[0].length;
463
- }
464
-
465
- return segments;
466
- }
467
-
468
- function findNextInlineMatch(text: string, startIndex: number): ResolvedInlineMatch | null {
469
- let bestMatch: ResolvedInlineMatch | null = null;
470
-
471
- for (const [priority, marker] of INLINE_MARKERS.entries()) {
472
- const regex = new RegExp(marker.pattern.source, marker.pattern.flags);
473
- regex.lastIndex = startIndex;
474
- const match = regex.exec(text);
475
- if (!match) {
476
- continue;
477
- }
478
-
479
- if (
480
- bestMatch &&
481
- (match.index > bestMatch.match.index ||
482
- (match.index === bestMatch.match.index && priority > bestMatch.priority))
483
- ) {
484
- continue;
485
- }
486
-
487
- bestMatch = {
488
- match,
489
- marker,
490
- text: marker.extractText(match),
491
- styles: marker.resolveStyles?.(match) ?? [],
492
- priority,
493
- };
494
- }
495
-
496
- return bestMatch;
497
- }
498
-
499
- function pushSegment(segments: Segment[], text: string, styles: InlineStyle[]): void {
500
- if (!text) {
501
- return;
502
- }
503
-
504
- const lastSegment = segments.at(-1);
505
- if (lastSegment && sameStyles(lastSegment.styles, styles)) {
506
- lastSegment.text += text;
507
- return;
508
- }
509
-
510
- segments.push({
511
- text,
512
- styles: [...styles],
513
- });
514
- }
515
-
516
- function sameStyles(left: InlineStyle[], right: InlineStyle[]): boolean {
517
- return left.length === right.length && left.every((style, index) => style === right[index]);
518
- }
519
-
520
- function normalizeCodeBlockLeadingWhitespace(line: string): string {
521
- return line.replace(/^[ \t]+/, (leadingWhitespace) =>
522
- leadingWhitespace.replace(/\t/g, "\u00A0\u00A0\u00A0\u00A0").replace(/ /g, "\u00A0"),
523
- );
524
- }
525
-
526
- function isIndentedCodeBlockLine(line: string): boolean {
527
- return /^(?: {4,}|\t)/.test(line);
528
- }
529
-
530
- function stripCodeFenceIndent(line: string, indent: number): string {
531
- let consumed = 0;
532
- let cursor = 0;
533
-
534
- while (cursor < line.length && consumed < indent && line[cursor] === " ") {
535
- cursor += 1;
536
- consumed += 1;
537
- }
538
-
539
- return line.slice(cursor);
540
- }