taro-bluetooth-print 2.5.0 → 2.6.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/index.cjs.js +1 -1
- package/dist/index.es.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/logo.svg +17 -0
- package/dist/manifest.webmanifest +17 -0
- package/dist/types/adapters/WebBluetoothAdapter.d.ts +87 -1
- package/dist/types/adapters/index.d.ts +1 -1
- package/dist/types/barcode/BarcodeGenerator.d.ts +61 -4
- package/dist/types/barcode/index.d.ts +1 -1
- package/dist/types/template/TemplateEngine.d.ts +140 -1
- package/dist/types/utils/uuid.d.ts +191 -0
- package/package.json +1 -1
- package/src/adapters/ReactNativeAdapter.ts +19 -30
- package/src/adapters/WebBluetoothAdapter.ts +275 -14
- package/src/adapters/index.ts +6 -1
- package/src/barcode/BarcodeGenerator.ts +312 -6
- package/src/barcode/index.ts +1 -1
- package/src/drivers/StarPrinter.ts +14 -13
- package/src/encoding/EncodingService.ts +13 -6
- package/src/encoding/korean-japanese.ts +84 -48
- package/src/services/BatchPrintManager.ts +15 -16
- package/src/services/PrintStatistics.ts +8 -8
- package/src/services/ScheduledRetryManager.ts +15 -19
- package/src/template/TemplateEngine.ts +543 -4
- package/src/utils/image.ts +92 -43
- package/src/utils/platform.ts +28 -14
- package/src/utils/uuid.ts +522 -0
- package/src/utils/validation.ts +1155 -0
|
@@ -27,12 +27,11 @@
|
|
|
27
27
|
|
|
28
28
|
// Korean Hangul Syllables (U+AC00 - U+D7AF)
|
|
29
29
|
// These are composed syllables that map to EUC-KR precomposed forms
|
|
30
|
-
export const HANGUL_SYLLABLE_OFFSET =
|
|
31
|
-
export const HANGUL_INITIAL_COUNT = 21;
|
|
30
|
+
export const HANGUL_SYLLABLE_OFFSET = 0xac00;
|
|
31
|
+
export const HANGUL_INITIAL_COUNT = 21; // 19 initials × 21 vowels
|
|
32
32
|
export const HANGUL_MEDIAL_COUNT = 21;
|
|
33
33
|
export const HANGUL_FINAL_COUNT = 28;
|
|
34
34
|
|
|
35
|
-
|
|
36
35
|
// ============================================================================
|
|
37
36
|
// Shift-JIS (Japanese) Constants
|
|
38
37
|
// ============================================================================
|
|
@@ -51,18 +50,18 @@ export const HANGUL_FINAL_COUNT = 28;
|
|
|
51
50
|
*/
|
|
52
51
|
|
|
53
52
|
// JIS X 0201 Katakana range
|
|
54
|
-
export const SHIFT_JIS_KATAKANA_START =
|
|
55
|
-
export const SHIFT_JIS_KATAKANA_END =
|
|
53
|
+
export const SHIFT_JIS_KATAKANA_START = 0xa1;
|
|
54
|
+
export const SHIFT_JIS_KATAKANA_END = 0xdf;
|
|
56
55
|
|
|
57
56
|
// JIS X 0208 Kanji regions
|
|
58
57
|
export const SHIFT_JIS_KANJI_LOW_START = 0x81;
|
|
59
|
-
export const SHIFT_JIS_KANJI_LOW_END =
|
|
60
|
-
export const SHIFT_JIS_KANJI_HIGH_START =
|
|
61
|
-
export const SHIFT_JIS_KANJI_HIGH_END =
|
|
58
|
+
export const SHIFT_JIS_KANJI_LOW_END = 0x9f;
|
|
59
|
+
export const SHIFT_JIS_KANJI_HIGH_START = 0xe0;
|
|
60
|
+
export const SHIFT_JIS_KANJI_HIGH_END = 0xef;
|
|
62
61
|
|
|
63
62
|
// JIS non-pictorial (Kanji) area
|
|
64
63
|
export const SHIFT_JIS_KANJI_SECOND_MIN = 0x40;
|
|
65
|
-
export const SHIFT_JIS_KANJI_SECOND_MAX =
|
|
64
|
+
export const SHIFT_JIS_KANJI_SECOND_MAX = 0xfc;
|
|
66
65
|
|
|
67
66
|
// ============================================================================
|
|
68
67
|
// Character Classification Helpers
|
|
@@ -72,39 +71,43 @@ export const SHIFT_JIS_KANJI_SECOND_MAX = 0xFC;
|
|
|
72
71
|
* Check if a Unicode code point is a Korean Hangul syllable
|
|
73
72
|
*/
|
|
74
73
|
export function isKoreanHangul(code: number): boolean {
|
|
75
|
-
return code >=
|
|
74
|
+
return code >= 0xac00 && code <= 0xd7af;
|
|
76
75
|
}
|
|
77
76
|
|
|
78
77
|
/**
|
|
79
78
|
* Check if a Unicode code point is a Korean Hanja (Hanja)
|
|
80
79
|
*/
|
|
81
80
|
export function isKoreanHanja(code: number): boolean {
|
|
82
|
-
return (
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
return (
|
|
82
|
+
(code >= 0x4e00 && code <= 0x9fff) ||
|
|
83
|
+
(code >= 0xf900 && code <= 0xfaff) ||
|
|
84
|
+
(code >= 0x3400 && code <= 0x4dbf)
|
|
85
|
+
);
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
/**
|
|
88
89
|
* Check if a Unicode code point is basic Korean (Hangul Jamo)
|
|
89
90
|
*/
|
|
90
91
|
export function isKoreanJamo(code: number): boolean {
|
|
91
|
-
return (
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
return (
|
|
93
|
+
(code >= 0x1100 && code <= 0x11ff) ||
|
|
94
|
+
(code >= 0x3130 && code <= 0x318f) ||
|
|
95
|
+
(code >= 0xffa0 && code <= 0xffdf)
|
|
96
|
+
);
|
|
94
97
|
}
|
|
95
98
|
|
|
96
99
|
/**
|
|
97
100
|
* Check if a code point is Japanese Hiragana
|
|
98
101
|
*/
|
|
99
102
|
export function isJapaneseHiragana(code: number): boolean {
|
|
100
|
-
return code >= 0x3040 && code <=
|
|
103
|
+
return code >= 0x3040 && code <= 0x309f;
|
|
101
104
|
}
|
|
102
105
|
|
|
103
106
|
/**
|
|
104
107
|
* Check if a code point is Japanese Katakana (full-width)
|
|
105
108
|
*/
|
|
106
109
|
export function isJapaneseKatakana(code: number): boolean {
|
|
107
|
-
return code >=
|
|
110
|
+
return code >= 0x30a0 && code <= 0x30ff;
|
|
108
111
|
}
|
|
109
112
|
|
|
110
113
|
/**
|
|
@@ -115,9 +118,11 @@ export function isJapaneseKanji(code: number): boolean {
|
|
|
115
118
|
// Common: U+4E00-U+9FFF (with many gaps)
|
|
116
119
|
// Extended A: U+3400-U+4DBF
|
|
117
120
|
// Extended B: U+20000-U+2A6DF (not supported)
|
|
118
|
-
return (
|
|
119
|
-
|
|
120
|
-
|
|
121
|
+
return (
|
|
122
|
+
(code >= 0x4e00 && code <= 0x9fff) ||
|
|
123
|
+
(code >= 0x3400 && code <= 0x4dbf) ||
|
|
124
|
+
(code >= 0xf900 && code <= 0xfaff)
|
|
125
|
+
);
|
|
121
126
|
}
|
|
122
127
|
|
|
123
128
|
// ============================================================================
|
|
@@ -142,18 +147,50 @@ export function encodeHangulSyllable(code: number): [number, number] | null {
|
|
|
142
147
|
|
|
143
148
|
// Calculate leading consonant (initial), vowel (medial), trailing consonant (final)
|
|
144
149
|
const leadingConsonant = Math.floor(syllableIndex / (HANGUL_MEDIAL_COUNT * HANGUL_FINAL_COUNT));
|
|
145
|
-
const vowelIndex = Math.floor(
|
|
150
|
+
const vowelIndex = Math.floor(
|
|
151
|
+
(syllableIndex % (HANGUL_MEDIAL_COUNT * HANGUL_FINAL_COUNT)) / HANGUL_FINAL_COUNT
|
|
152
|
+
);
|
|
146
153
|
const trailingConsonant = syllableIndex % HANGUL_FINAL_COUNT;
|
|
147
154
|
|
|
148
155
|
// Lead consonants: ㄱㄴㄷㄹㅁㅂㅅㅇㅈㅊㅋㅌㅍㅎ (14 consonants)
|
|
149
|
-
const LEAD_CONSONANTS = [
|
|
156
|
+
const LEAD_CONSONANTS = [
|
|
157
|
+
0x81, 0x82, 0x84, 0x85, 0x88, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92,
|
|
158
|
+
];
|
|
150
159
|
// Vowels: ㅏㅐㅑㅒㅓㅔㅕㅖㅗㅘㅙㅚㅛㅜㅝㅞㅟㅠㅡㅢㅣ (21 vowels)
|
|
151
|
-
const VOWELS = [
|
|
160
|
+
const VOWELS = [
|
|
161
|
+
0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0,
|
|
162
|
+
0xb1, 0xb2, 0xb3, 0xb4, 0xb5,
|
|
163
|
+
];
|
|
152
164
|
// Trail consonants (finals): ㄱㄲㄳㄴㄵㄶㄷㄹㄺㄻㄼㄽㄾㄿㅀㅁㅂㅄㅅㅆㅇㅈㅊㅋㅌㅍㅎ (27 finals, 0=none)
|
|
153
165
|
const TRAIL_CONSONANTS: number[] = [
|
|
154
166
|
0x00, // No final
|
|
155
|
-
0x81,
|
|
156
|
-
|
|
167
|
+
0x81,
|
|
168
|
+
0x82,
|
|
169
|
+
0x83,
|
|
170
|
+
0x84,
|
|
171
|
+
0x85,
|
|
172
|
+
0x86,
|
|
173
|
+
0x87,
|
|
174
|
+
0x88,
|
|
175
|
+
0x89,
|
|
176
|
+
0x8a,
|
|
177
|
+
0x8b,
|
|
178
|
+
0x8c,
|
|
179
|
+
0x8d,
|
|
180
|
+
0x8e,
|
|
181
|
+
0x8f,
|
|
182
|
+
0x90,
|
|
183
|
+
0x91,
|
|
184
|
+
0x92,
|
|
185
|
+
0x93,
|
|
186
|
+
0x94,
|
|
187
|
+
0x95,
|
|
188
|
+
0x96,
|
|
189
|
+
0x97,
|
|
190
|
+
0x98,
|
|
191
|
+
0x99,
|
|
192
|
+
0x9a,
|
|
193
|
+
0x9b,
|
|
157
194
|
];
|
|
158
195
|
|
|
159
196
|
if (leadingConsonant >= LEAD_CONSONANTS.length) return null;
|
|
@@ -179,9 +216,9 @@ export function encodeHangulSyllable(code: number): [number, number] | null {
|
|
|
179
216
|
if (trail === 0x00) {
|
|
180
217
|
// Map to EUC-KR compatible range
|
|
181
218
|
const row = Math.floor((lead - 0x81) * 21 + vowelIndex);
|
|
182
|
-
const eucLead =
|
|
183
|
-
const eucTrail =
|
|
184
|
-
return [eucLead &
|
|
219
|
+
const eucLead = 0xb0 + Math.floor(row / 0x5e);
|
|
220
|
+
const eucTrail = 0xa1 + (row % 0x5e);
|
|
221
|
+
return [eucLead & 0xff, eucTrail & 0xff];
|
|
185
222
|
}
|
|
186
223
|
|
|
187
224
|
return [leadByte, trailByte];
|
|
@@ -196,12 +233,12 @@ export function encodeHangulSyllable(code: number): [number, number] | null {
|
|
|
196
233
|
* U+3040-309F → 0x829F-0x82F1 (simplified mapping)
|
|
197
234
|
*/
|
|
198
235
|
export function unicodeToShiftJisHiragana(code: number): [number, number] | null {
|
|
199
|
-
if (code >= 0x3040 && code <=
|
|
236
|
+
if (code >= 0x3040 && code <= 0x309f) {
|
|
200
237
|
const offset = code - 0x3040;
|
|
201
238
|
// JIS X 0201 Hiragana range
|
|
202
239
|
const jisLead = 0x82;
|
|
203
|
-
const jisTrail =
|
|
204
|
-
return [jisLead, jisTrail &
|
|
240
|
+
const jisTrail = 0x9f + offset;
|
|
241
|
+
return [jisLead, jisTrail & 0xff];
|
|
205
242
|
}
|
|
206
243
|
return null;
|
|
207
244
|
}
|
|
@@ -211,11 +248,11 @@ export function unicodeToShiftJisHiragana(code: number): [number, number] | null
|
|
|
211
248
|
* U+30A0-30FF → 0x8340-0x8396 (full-width Katakana)
|
|
212
249
|
*/
|
|
213
250
|
export function unicodeToShiftJisKatakana(code: number): [number, number] | null {
|
|
214
|
-
if (code >=
|
|
215
|
-
const offset = code -
|
|
251
|
+
if (code >= 0x30a0 && code <= 0x30ff) {
|
|
252
|
+
const offset = code - 0x30a0;
|
|
216
253
|
const jisLead = 0x83;
|
|
217
254
|
const jisTrail = 0x40 + offset;
|
|
218
|
-
return [jisLead, jisTrail &
|
|
255
|
+
return [jisLead, jisTrail & 0xff];
|
|
219
256
|
}
|
|
220
257
|
return null;
|
|
221
258
|
}
|
|
@@ -225,8 +262,8 @@ export function unicodeToShiftJisKatakana(code: number): [number, number] | null
|
|
|
225
262
|
* U+FF61-U+FF9F → 0xA1-0xDF
|
|
226
263
|
*/
|
|
227
264
|
export function unicodeToHalfWidthKatakana(code: number): number | null {
|
|
228
|
-
if (code >=
|
|
229
|
-
return code -
|
|
265
|
+
if (code >= 0xff61 && code <= 0xff9f) {
|
|
266
|
+
return code - 0xff61 + 0xa1;
|
|
230
267
|
}
|
|
231
268
|
return null;
|
|
232
269
|
}
|
|
@@ -237,8 +274,7 @@ export function unicodeToHalfWidthKatakana(code: number): number | null {
|
|
|
237
274
|
*/
|
|
238
275
|
export function isInJisX0208Range(code: number): boolean {
|
|
239
276
|
// Level 1 and Level 2 Kanji
|
|
240
|
-
return (code >=
|
|
241
|
-
(code >= 0x3400 && code <= 0x4DBF);
|
|
277
|
+
return (code >= 0x4e00 && code <= 0x9fff) || (code >= 0x3400 && code <= 0x4dbf);
|
|
242
278
|
}
|
|
243
279
|
|
|
244
280
|
// ============================================================================
|
|
@@ -258,9 +294,9 @@ export function isInJisX0208Range(code: number): boolean {
|
|
|
258
294
|
/**
|
|
259
295
|
* ISO-2022-JP escape sequences
|
|
260
296
|
*/
|
|
261
|
-
export const ISO2022JP_ESC_ASCII = new Uint8Array([0x1b, 0x28, 0x42]);
|
|
262
|
-
export const ISO2022JP_ESC_JIS0201 = new Uint8Array([0x1b, 0x28, 0x4a]);
|
|
263
|
-
export const ISO2022JP_ESC_JIS0208 = new Uint8Array([0x1b, 0x24, 0x42]);
|
|
297
|
+
export const ISO2022JP_ESC_ASCII = new Uint8Array([0x1b, 0x28, 0x42]); // ESC ( B
|
|
298
|
+
export const ISO2022JP_ESC_JIS0201 = new Uint8Array([0x1b, 0x28, 0x4a]); // ESC ( J
|
|
299
|
+
export const ISO2022JP_ESC_JIS0208 = new Uint8Array([0x1b, 0x24, 0x42]); // ESC $ B
|
|
264
300
|
export const ISO2022JP_ESC_JIS0208_83 = new Uint8Array([0x1b, 0x24, 0x40]); // ESC $ @
|
|
265
301
|
export const ISO2022JP_ESC_JIS0208_90 = new Uint8Array([0x1b, 0x24, 0x44]); // ESC $ D
|
|
266
302
|
|
|
@@ -273,9 +309,7 @@ export type ISO2022JPState = 'ASCII' | 'JIS0208' | 'JIS0201';
|
|
|
273
309
|
* Check if a code point requires JIS X 0208 escape sequence
|
|
274
310
|
*/
|
|
275
311
|
export function requiresJisX0208Escape(code: number): boolean {
|
|
276
|
-
return isJapaneseHiragana(code) ||
|
|
277
|
-
isJapaneseKatakana(code) ||
|
|
278
|
-
isJapaneseKanji(code);
|
|
312
|
+
return isJapaneseHiragana(code) || isJapaneseKatakana(code) || isJapaneseKanji(code);
|
|
279
313
|
}
|
|
280
314
|
|
|
281
315
|
/**
|
|
@@ -283,7 +317,9 @@ export function requiresJisX0208Escape(code: number): boolean {
|
|
|
283
317
|
*/
|
|
284
318
|
export function isJisX0201Katakana(code: number): boolean {
|
|
285
319
|
// Half-width Katakana range
|
|
286
|
-
return (
|
|
287
|
-
|
|
288
|
-
|
|
320
|
+
return (
|
|
321
|
+
(code >= 0xff61 && code <= 0xff9f) ||
|
|
322
|
+
// Full-width Katakana (JIS X 0201 range mapped to U+30A0)
|
|
323
|
+
(code >= 0x30a0 && code <= 0x30ff)
|
|
324
|
+
);
|
|
289
325
|
}
|
|
@@ -238,7 +238,9 @@ export class BatchPrintManager {
|
|
|
238
238
|
this.jobs.sort((a, b) => b.priority - a.priority);
|
|
239
239
|
|
|
240
240
|
this.emit('job-added', job);
|
|
241
|
-
this.logger.debug(
|
|
241
|
+
this.logger.debug(
|
|
242
|
+
`Job added: ${id} (priority: ${priority}, queue size: ${this.jobs.length}, bytes: ${data.length})`
|
|
243
|
+
);
|
|
242
244
|
|
|
243
245
|
// Start/restart wait timer
|
|
244
246
|
this.startWaitTimer();
|
|
@@ -361,7 +363,8 @@ export class BatchPrintManager {
|
|
|
361
363
|
|
|
362
364
|
// Report merge stats if jobs were merged
|
|
363
365
|
if (fromCount !== toCount) {
|
|
364
|
-
const savedBytes =
|
|
366
|
+
const savedBytes =
|
|
367
|
+
batchJobs.slice(0, fromCount).reduce((sum, j) => sum + j.data.length, 0) -
|
|
365
368
|
mergedData.length;
|
|
366
369
|
this.emit('jobs-merged', { fromCount, toCount, savedBytes: Math.abs(savedBytes) });
|
|
367
370
|
this.stats.mergedJobs += fromCount - toCount;
|
|
@@ -427,15 +430,17 @@ export class BatchPrintManager {
|
|
|
427
430
|
* @param jobs - Jobs to merge
|
|
428
431
|
* @returns Merged data with metadata about the merge operation
|
|
429
432
|
*/
|
|
430
|
-
private mergeJobs(
|
|
431
|
-
|
|
432
|
-
|
|
433
|
+
private mergeJobs(jobs: BatchJob[]): {
|
|
434
|
+
mergedData: Uint8Array;
|
|
435
|
+
fromCount: number;
|
|
436
|
+
toCount: number;
|
|
437
|
+
} {
|
|
433
438
|
if (!this.config.enableMerging || jobs.length === 1) {
|
|
434
439
|
let mergedData: Uint8Array;
|
|
435
440
|
|
|
436
441
|
if (jobs.length === 1 && this.config.unifiedCutCommand) {
|
|
437
442
|
// Single job with unified cut
|
|
438
|
-
mergedData = this.concatBuffers([jobs[0]!.data, this.config.unifiedCutCommand
|
|
443
|
+
mergedData = this.concatBuffers([jobs[0]!.data, this.config.unifiedCutCommand]);
|
|
439
444
|
this.stats.unifiedCutsApplied++;
|
|
440
445
|
} else {
|
|
441
446
|
mergedData = jobs[0]?.data ?? new Uint8Array(0);
|
|
@@ -486,7 +491,7 @@ export class BatchPrintManager {
|
|
|
486
491
|
|
|
487
492
|
this.logger.debug(
|
|
488
493
|
`Merged ${fromCount} jobs into ${toCountAfterSmallMerge} after small-job merge, ` +
|
|
489
|
-
|
|
494
|
+
`final size: ${totalSize} bytes`
|
|
490
495
|
);
|
|
491
496
|
|
|
492
497
|
return { mergedData: result, fromCount, toCount: toCountAfterSmallMerge };
|
|
@@ -520,9 +525,7 @@ export class BatchPrintManager {
|
|
|
520
525
|
bufferPriority = job.priority;
|
|
521
526
|
bufferAddedAt = job.addedAt;
|
|
522
527
|
}
|
|
523
|
-
this.logger.debug(
|
|
524
|
-
`Small job ${job.id} (${job.data.length} bytes) queued for merge buffer`
|
|
525
|
-
);
|
|
528
|
+
this.logger.debug(`Small job ${job.id} (${job.data.length} bytes) queued for merge buffer`);
|
|
526
529
|
} else {
|
|
527
530
|
// Non-small job - flush buffer first if not empty
|
|
528
531
|
if (buffer.length > 0) {
|
|
@@ -550,11 +553,7 @@ export class BatchPrintManager {
|
|
|
550
553
|
* @param addedAt - Timestamp of the first job in the merge
|
|
551
554
|
* @returns Merged BatchJob
|
|
552
555
|
*/
|
|
553
|
-
private createMergedJob(
|
|
554
|
-
buffers: Uint8Array[],
|
|
555
|
-
priority: number,
|
|
556
|
-
addedAt: number
|
|
557
|
-
): BatchJob {
|
|
556
|
+
private createMergedJob(buffers: Uint8Array[], priority: number, addedAt: number): BatchJob {
|
|
558
557
|
// Calculate total size
|
|
559
558
|
let totalSize = 0;
|
|
560
559
|
for (const buf of buffers) {
|
|
@@ -674,7 +673,7 @@ export class BatchPrintManager {
|
|
|
674
673
|
if (elapsed >= this.config.flushIntervalTimeout && this.jobs.length > 0) {
|
|
675
674
|
this.logger.debug(
|
|
676
675
|
`Flush interval timeout triggered: ${this.jobs.length} jobs pending, ` +
|
|
677
|
-
|
|
676
|
+
`elapsed: ${elapsed}ms`
|
|
678
677
|
);
|
|
679
678
|
this.stats.autoFlushCount++;
|
|
680
679
|
this.emit('auto-flush', {
|
|
@@ -266,8 +266,7 @@ export class PrintStatistics {
|
|
|
266
266
|
const completedOrFailed = this.completedJobs + this.failedJobs;
|
|
267
267
|
const successRate = completedOrFailed > 0 ? this.completedJobs / completedOrFailed : 0;
|
|
268
268
|
|
|
269
|
-
const averagePrintTime =
|
|
270
|
-
this.completedJobs > 0 ? this.totalPrintTime / this.completedJobs : 0;
|
|
269
|
+
const averagePrintTime = this.completedJobs > 0 ? this.totalPrintTime / this.completedJobs : 0;
|
|
271
270
|
|
|
272
271
|
return {
|
|
273
272
|
totalJobs: this.totalJobs,
|
|
@@ -352,10 +351,7 @@ export class PrintStatistics {
|
|
|
352
351
|
* @param endDate - End date (timestamp or Date)
|
|
353
352
|
* @returns Filtered statistics
|
|
354
353
|
*/
|
|
355
|
-
getStatisticsByDateRange(
|
|
356
|
-
startDate: Date | number,
|
|
357
|
-
endDate: Date | number
|
|
358
|
-
): PrintStatisticsData {
|
|
354
|
+
getStatisticsByDateRange(startDate: Date | number, endDate: Date | number): PrintStatisticsData {
|
|
359
355
|
const start = typeof startDate === 'number' ? startDate : startDate.getTime();
|
|
360
356
|
const end = typeof endDate === 'number' ? endDate : endDate.getTime();
|
|
361
357
|
|
|
@@ -467,7 +463,9 @@ export class PrintStatistics {
|
|
|
467
463
|
return String(error);
|
|
468
464
|
}
|
|
469
465
|
|
|
470
|
-
private aggregateByDate(
|
|
466
|
+
private aggregateByDate(
|
|
467
|
+
jobs: JobRecord[]
|
|
468
|
+
): Record<string, { completed: number; failed: number }> {
|
|
471
469
|
const result: Record<string, { completed: number; failed: number }> = {};
|
|
472
470
|
for (const job of jobs) {
|
|
473
471
|
const dateKey = this.formatDateKey(job.startedAt);
|
|
@@ -483,7 +481,9 @@ export class PrintStatistics {
|
|
|
483
481
|
return result;
|
|
484
482
|
}
|
|
485
483
|
|
|
486
|
-
private aggregateByDriver(
|
|
484
|
+
private aggregateByDriver(
|
|
485
|
+
jobs: JobRecord[]
|
|
486
|
+
): Record<string, { completed: number; failed: number }> {
|
|
487
487
|
const result: Record<string, { completed: number; failed: number }> = {};
|
|
488
488
|
for (const job of jobs) {
|
|
489
489
|
if (!job.driver) continue;
|
|
@@ -141,10 +141,7 @@ export class ScheduledRetryManager {
|
|
|
141
141
|
* @param config - Optional configuration overrides
|
|
142
142
|
* @param offlineCache - Optional OfflineCache instance (uses singleton if not provided)
|
|
143
143
|
*/
|
|
144
|
-
constructor(
|
|
145
|
-
config?: Partial<ScheduledRetryManagerConfig>,
|
|
146
|
-
offlineCache?: OfflineCache
|
|
147
|
-
) {
|
|
144
|
+
constructor(config?: Partial<ScheduledRetryManagerConfig>, offlineCache?: OfflineCache) {
|
|
148
145
|
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
149
146
|
this.offlineCache = offlineCache ?? new OfflineCache();
|
|
150
147
|
|
|
@@ -197,15 +194,13 @@ export class ScheduledRetryManager {
|
|
|
197
194
|
|
|
198
195
|
// Schedule the timeout
|
|
199
196
|
entry.timeout = setTimeout(() => {
|
|
200
|
-
this.executeRetry(entry);
|
|
197
|
+
void this.executeRetry(entry);
|
|
201
198
|
}, delay);
|
|
202
199
|
|
|
203
200
|
this.scheduledRetries.set(jobId, entry);
|
|
204
|
-
this.persistRetry(entry);
|
|
201
|
+
void this.persistRetry(entry);
|
|
205
202
|
|
|
206
|
-
this.logger.info(
|
|
207
|
-
`Retry scheduled: ${jobId}, runAt: ${runAt.toISOString()}, delay: ${delay}ms`
|
|
208
|
-
);
|
|
203
|
+
this.logger.info(`Retry scheduled: ${jobId}, runAt: ${runAt.toISOString()}, delay: ${delay}ms`);
|
|
209
204
|
}
|
|
210
205
|
|
|
211
206
|
/**
|
|
@@ -289,7 +284,8 @@ export class ScheduledRetryManager {
|
|
|
289
284
|
const entry = this.scheduledRetries.get(jobId);
|
|
290
285
|
if (!entry) return undefined;
|
|
291
286
|
|
|
292
|
-
// Return a copy without the timeout
|
|
287
|
+
// Return a copy without the timeout (timeout cannot be serialized)
|
|
288
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
293
289
|
const { timeout, ...rest } = entry;
|
|
294
290
|
return rest as ScheduledRetry;
|
|
295
291
|
}
|
|
@@ -425,11 +421,11 @@ export class ScheduledRetryManager {
|
|
|
425
421
|
entry.runAt = nextRunAt;
|
|
426
422
|
|
|
427
423
|
entry.timeout = setTimeout(() => {
|
|
428
|
-
this.executeRetry(entry);
|
|
424
|
+
void this.executeRetry(entry);
|
|
429
425
|
}, nextDelay);
|
|
430
426
|
|
|
431
427
|
this.scheduledRetries.set(jobId, entry);
|
|
432
|
-
this.persistRetry(entry);
|
|
428
|
+
void this.persistRetry(entry);
|
|
433
429
|
|
|
434
430
|
this.logger.info(
|
|
435
431
|
`Scheduled next retry for ${jobId}: ${nextRunAt.toISOString()}, attempt: ${entry.attemptCount}`
|
|
@@ -499,11 +495,8 @@ export class ScheduledRetryManager {
|
|
|
499
495
|
|
|
500
496
|
for (const cached of cachedJobs) {
|
|
501
497
|
// Only restore scheduled_retry entries
|
|
502
|
-
if (
|
|
503
|
-
cached.metadata
|
|
504
|
-
(cached.metadata as Record<string, unknown>).type === 'scheduled_retry'
|
|
505
|
-
) {
|
|
506
|
-
const meta = cached.metadata as Record<string, unknown>;
|
|
498
|
+
if (cached.metadata && cached.metadata.type === 'scheduled_retry') {
|
|
499
|
+
const meta = cached.metadata;
|
|
507
500
|
const runAtStr = meta.runAt as string;
|
|
508
501
|
const runAt = new Date(runAtStr);
|
|
509
502
|
|
|
@@ -526,7 +519,7 @@ export class ScheduledRetryManager {
|
|
|
526
519
|
|
|
527
520
|
// Schedule with correct delay
|
|
528
521
|
entry.timeout = setTimeout(() => {
|
|
529
|
-
this.executeRetry(entry);
|
|
522
|
+
void this.executeRetry(entry);
|
|
530
523
|
}, runAt.getTime() - now);
|
|
531
524
|
|
|
532
525
|
this.scheduledRetries.set(entry.jobId, entry);
|
|
@@ -546,7 +539,10 @@ export class ScheduledRetryManager {
|
|
|
546
539
|
/**
|
|
547
540
|
* Emit an event
|
|
548
541
|
*/
|
|
549
|
-
private emit<K extends keyof ScheduledRetryEvents>(
|
|
542
|
+
private emit<K extends keyof ScheduledRetryEvents>(
|
|
543
|
+
event: K,
|
|
544
|
+
data: ScheduledRetryEvents[K]
|
|
545
|
+
): void {
|
|
550
546
|
const handlers = this.listeners.get(event);
|
|
551
547
|
if (handlers) {
|
|
552
548
|
handlers.forEach(handler => {
|