jww-parser 2025.12.2 → 2025.12.3

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.mjs CHANGED
@@ -1,4 +1,128 @@
1
1
  // src/index.ts
2
+ var JwwErrorCode = /* @__PURE__ */ ((JwwErrorCode2) => {
3
+ JwwErrorCode2["NOT_INITIALIZED"] = "NOT_INITIALIZED";
4
+ JwwErrorCode2["WASM_LOAD_FAILED"] = "WASM_LOAD_FAILED";
5
+ JwwErrorCode2["WASM_TIMEOUT"] = "WASM_TIMEOUT";
6
+ JwwErrorCode2["INVALID_SIGNATURE"] = "INVALID_SIGNATURE";
7
+ JwwErrorCode2["UNSUPPORTED_VERSION"] = "UNSUPPORTED_VERSION";
8
+ JwwErrorCode2["PARSE_ERROR"] = "PARSE_ERROR";
9
+ JwwErrorCode2["CONVERSION_ERROR"] = "CONVERSION_ERROR";
10
+ JwwErrorCode2["VALIDATION_ERROR"] = "VALIDATION_ERROR";
11
+ JwwErrorCode2["MEMORY_ERROR"] = "MEMORY_ERROR";
12
+ JwwErrorCode2["INVALID_ARGUMENT"] = "INVALID_ARGUMENT";
13
+ return JwwErrorCode2;
14
+ })(JwwErrorCode || {});
15
+ var JwwParserError = class _JwwParserError extends Error {
16
+ constructor(code, message, options) {
17
+ super(message);
18
+ this.name = "JwwParserError";
19
+ this.code = code;
20
+ this.cause = options?.cause;
21
+ this.context = options?.context;
22
+ this.timestamp = /* @__PURE__ */ new Date();
23
+ if (Error.captureStackTrace) {
24
+ Error.captureStackTrace(this, _JwwParserError);
25
+ }
26
+ }
27
+ /**
28
+ * Returns a detailed string representation of the error
29
+ */
30
+ toDetailedString() {
31
+ let details = `[${this.code}] ${this.message}`;
32
+ if (this.context) {
33
+ details += `
34
+ Context: ${JSON.stringify(this.context, null, 2)}`;
35
+ }
36
+ if (this.cause) {
37
+ details += `
38
+ Caused by: ${this.cause.message}`;
39
+ }
40
+ return details;
41
+ }
42
+ /**
43
+ * Converts the error to a plain object for logging/serialization
44
+ */
45
+ toJSON() {
46
+ return {
47
+ name: this.name,
48
+ code: this.code,
49
+ message: this.message,
50
+ context: this.context,
51
+ timestamp: this.timestamp.toISOString(),
52
+ cause: this.cause?.message,
53
+ stack: this.stack
54
+ };
55
+ }
56
+ };
57
+ var NotInitializedError = class extends JwwParserError {
58
+ constructor() {
59
+ super(
60
+ "NOT_INITIALIZED" /* NOT_INITIALIZED */,
61
+ "Parser not initialized. Call init() first or use createParser() factory function."
62
+ );
63
+ this.name = "NotInitializedError";
64
+ }
65
+ };
66
+ var WasmLoadError = class extends JwwParserError {
67
+ constructor(message, cause) {
68
+ super("WASM_LOAD_FAILED" /* WASM_LOAD_FAILED */, message, { cause });
69
+ this.name = "WasmLoadError";
70
+ }
71
+ };
72
+ var ValidationError = class extends JwwParserError {
73
+ constructor(message, issues) {
74
+ super("VALIDATION_ERROR" /* VALIDATION_ERROR */, message, {
75
+ context: { issues }
76
+ });
77
+ this.name = "ValidationError";
78
+ this.issues = issues;
79
+ }
80
+ };
81
+ var ParseError = class extends JwwParserError {
82
+ constructor(message, options) {
83
+ super("PARSE_ERROR" /* PARSE_ERROR */, message, {
84
+ cause: options?.cause,
85
+ context: {
86
+ offset: options?.offset,
87
+ section: options?.section
88
+ }
89
+ });
90
+ this.name = "ParseError";
91
+ this.offset = options?.offset;
92
+ this.section = options?.section;
93
+ }
94
+ };
95
+ function formatBytes(bytes) {
96
+ if (bytes < 1024) return `${bytes} B`;
97
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`;
98
+ if (bytes < 1024 * 1024 * 1024)
99
+ return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
100
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
101
+ }
102
+ function getSizeCategory(bytes) {
103
+ if (bytes < 100 * 1024) return "small";
104
+ if (bytes < 1024 * 1024) return "medium";
105
+ if (bytes < 10 * 1024 * 1024) return "large";
106
+ return "very_large";
107
+ }
108
+ function createParseError(result) {
109
+ const message = result.error || "Parse failed";
110
+ if (message.includes("invalid JWW signature")) {
111
+ return new ParseError("Invalid JWW file format: missing or corrupt signature", {
112
+ section: "header",
113
+ offset: 0
114
+ });
115
+ }
116
+ if (message.includes("unsupported") && message.includes("version")) {
117
+ return new ParseError(message, {
118
+ section: "header"
119
+ });
120
+ }
121
+ return new ParseError(message, {
122
+ offset: result.offset,
123
+ section: result.section
124
+ });
125
+ }
2
126
  var JwwParser = class {
3
127
  /**
4
128
  * Create a new JWW parser instance
@@ -7,6 +131,18 @@ var JwwParser = class {
7
131
  constructor(wasmPath) {
8
132
  this.initialized = false;
9
133
  this.initPromise = null;
134
+ this.goInstance = null;
135
+ this.wasmInstance = null;
136
+ // Debug and logging
137
+ this.debugOptions = { enabled: false };
138
+ this.debugLogs = [];
139
+ // Statistics
140
+ this.stats = {
141
+ parseCount: 0,
142
+ totalBytesProcessed: 0,
143
+ parseTimes: [],
144
+ errorCount: 0
145
+ };
10
146
  this.wasmPath = wasmPath || this.getDefaultWasmPath();
11
147
  }
12
148
  getDefaultWasmPath() {
@@ -15,6 +151,75 @@ var JwwParser = class {
15
151
  }
16
152
  return "jww-parser.wasm";
17
153
  }
154
+ // ===========================================================================
155
+ // Debug Methods
156
+ // ===========================================================================
157
+ /**
158
+ * Enable or configure debug mode
159
+ * @param options - Debug configuration options
160
+ */
161
+ setDebug(options) {
162
+ if (typeof options === "boolean") {
163
+ this.debugOptions = { enabled: options };
164
+ } else {
165
+ this.debugOptions = { ...this.debugOptions, ...options };
166
+ }
167
+ if (this.initialized && typeof globalThis.jwwSetDebug === "function") {
168
+ globalThis.jwwSetDebug(this.debugOptions.enabled ?? false);
169
+ }
170
+ this.log("info", "Debug mode " + (this.debugOptions.enabled ? "enabled" : "disabled"));
171
+ }
172
+ /**
173
+ * Get debug logs
174
+ * @param level - Optional minimum log level to filter
175
+ * @returns Array of debug log entries
176
+ */
177
+ getDebugLogs(level) {
178
+ if (!level) return [...this.debugLogs];
179
+ const levels = ["debug", "info", "warn", "error"];
180
+ const minIndex = levels.indexOf(level);
181
+ return this.debugLogs.filter(
182
+ (entry) => levels.indexOf(entry.level) >= minIndex
183
+ );
184
+ }
185
+ /**
186
+ * Clear debug logs
187
+ */
188
+ clearDebugLogs() {
189
+ this.debugLogs = [];
190
+ }
191
+ log(level, message, data) {
192
+ if (!this.debugOptions.enabled) return;
193
+ const levels = ["debug", "info", "warn", "error"];
194
+ const minLevel = this.debugOptions.logLevel || "info";
195
+ if (levels.indexOf(level) < levels.indexOf(minLevel)) return;
196
+ const entry = {
197
+ timestamp: /* @__PURE__ */ new Date(),
198
+ level,
199
+ message,
200
+ data: this.debugOptions.includeMemoryUsage ? { ...data, memory: this.getMemoryStats() } : data
201
+ };
202
+ this.debugLogs.push(entry);
203
+ if (this.debugLogs.length > (this.debugOptions.maxLogEntries || 1e3)) {
204
+ this.debugLogs.shift();
205
+ }
206
+ if (this.debugOptions.onDebug) {
207
+ this.debugOptions.onDebug(entry);
208
+ }
209
+ if (this.debugOptions.logToConsole) {
210
+ const consoleFn = level === "error" ? console.error : level === "warn" ? console.warn : level === "debug" ? console.debug : console.log;
211
+ consoleFn(`[JwwParser:${level}] ${message}`, data || "");
212
+ }
213
+ }
214
+ // ===========================================================================
215
+ // Initialization Methods
216
+ // ===========================================================================
217
+ /**
218
+ * Check if the parser is initialized
219
+ */
220
+ isInitialized() {
221
+ return this.initialized;
222
+ }
18
223
  /**
19
224
  * Initialize the WASM module
20
225
  * Must be called before using parse methods
@@ -22,43 +227,72 @@ var JwwParser = class {
22
227
  async init() {
23
228
  if (this.initialized) return;
24
229
  if (this.initPromise) return this.initPromise;
230
+ this.log("info", "Initializing WASM module");
231
+ const startTime = Date.now();
25
232
  this.initPromise = this.loadWasm();
26
- await this.initPromise;
27
- this.initialized = true;
233
+ try {
234
+ await this.initPromise;
235
+ this.initialized = true;
236
+ this.log("info", "WASM module initialized", {
237
+ elapsedMs: Date.now() - startTime
238
+ });
239
+ } catch (error) {
240
+ this.initPromise = null;
241
+ this.log("error", "WASM initialization failed", {
242
+ error: error instanceof Error ? error.message : String(error)
243
+ });
244
+ throw error;
245
+ }
28
246
  }
29
247
  async loadWasm() {
30
248
  if (typeof Go === "undefined") {
31
249
  await this.loadWasmExec();
32
250
  }
33
- const go = new Go();
34
- let wasmInstance;
35
- if (typeof process !== "undefined" && process.versions?.node) {
36
- const fs = await import("fs");
37
- const path = await import("path");
38
- const wasmBuffer = fs.readFileSync(this.wasmPath);
39
- const wasmModule = await WebAssembly.compile(wasmBuffer);
40
- wasmInstance = await WebAssembly.instantiate(wasmModule, go.importObject);
41
- } else {
42
- const result = await WebAssembly.instantiateStreaming(
43
- fetch(this.wasmPath),
44
- go.importObject
45
- ).catch(async () => {
46
- const response = await fetch(this.wasmPath);
47
- const bytes = await response.arrayBuffer();
48
- return WebAssembly.instantiate(bytes, go.importObject);
49
- });
50
- wasmInstance = result.instance;
251
+ this.goInstance = new Go();
252
+ try {
253
+ if (typeof process !== "undefined" && process.versions?.node) {
254
+ const fs = await import("fs");
255
+ const wasmBuffer = fs.readFileSync(this.wasmPath);
256
+ const wasmModule = await WebAssembly.compile(wasmBuffer);
257
+ this.wasmInstance = await WebAssembly.instantiate(
258
+ wasmModule,
259
+ this.goInstance.importObject
260
+ );
261
+ } else {
262
+ const result = await WebAssembly.instantiateStreaming(
263
+ fetch(this.wasmPath),
264
+ this.goInstance.importObject
265
+ ).catch(async () => {
266
+ const response = await fetch(this.wasmPath);
267
+ if (!response.ok) {
268
+ throw new WasmLoadError(
269
+ `Failed to fetch WASM file: ${response.status} ${response.statusText}`
270
+ );
271
+ }
272
+ const bytes = await response.arrayBuffer();
273
+ return WebAssembly.instantiate(bytes, this.goInstance.importObject);
274
+ });
275
+ this.wasmInstance = result.instance;
276
+ }
277
+ } catch (error) {
278
+ throw new WasmLoadError(
279
+ `Failed to load WASM module: ${error instanceof Error ? error.message : String(error)}`,
280
+ error instanceof Error ? error : void 0
281
+ );
51
282
  }
52
- go.run(wasmInstance);
283
+ this.goInstance.run(this.wasmInstance);
53
284
  await this.waitForWasmFunctions();
285
+ if (this.debugOptions.enabled && typeof globalThis.jwwSetDebug === "function") {
286
+ globalThis.jwwSetDebug(true);
287
+ }
54
288
  }
55
289
  async loadWasmExec() {
56
290
  if (typeof process !== "undefined" && process.versions?.node) {
57
291
  const wasmExecPath = new URL("../wasm/wasm_exec.js", import.meta.url).pathname;
58
292
  await import(wasmExecPath);
59
293
  } else {
60
- throw new Error(
61
- "Go runtime not loaded. Please include wasm_exec.js in your HTML."
294
+ throw new WasmLoadError(
295
+ "Go runtime not loaded. Please include wasm_exec.js in your HTML before using the parser."
62
296
  );
63
297
  }
64
298
  }
@@ -70,61 +304,420 @@ var JwwParser = class {
70
304
  }
71
305
  await new Promise((resolve) => setTimeout(resolve, interval));
72
306
  }
73
- throw new Error("WASM functions not available after timeout");
307
+ throw new JwwParserError(
308
+ "WASM_TIMEOUT" /* WASM_TIMEOUT */,
309
+ "WASM functions not available after timeout. The WASM module may have failed to initialize."
310
+ );
74
311
  }
75
312
  ensureInitialized() {
76
313
  if (!this.initialized) {
77
- throw new Error("Parser not initialized. Call init() first.");
314
+ throw new NotInitializedError();
315
+ }
316
+ }
317
+ // ===========================================================================
318
+ // Validation Methods
319
+ // ===========================================================================
320
+ /**
321
+ * Validate a JWW file without fully parsing it
322
+ * Useful for checking file validity before processing
323
+ *
324
+ * @param data - JWW file content as Uint8Array
325
+ * @returns Validation result with any issues found
326
+ */
327
+ validate(data) {
328
+ const startTime = Date.now();
329
+ const issues = [];
330
+ if (data.length < 16) {
331
+ issues.push({
332
+ severity: "error",
333
+ code: "FILE_TOO_SMALL",
334
+ message: "File is too small to be a valid JWW file",
335
+ details: { size: data.length, minimumRequired: 16 }
336
+ });
337
+ }
338
+ const signature = new TextDecoder().decode(data.slice(0, 8));
339
+ if (signature !== "JwwData.") {
340
+ issues.push({
341
+ severity: "error",
342
+ code: "INVALID_SIGNATURE",
343
+ message: `Invalid file signature: expected "JwwData.", got "${signature.replace(/[^\x20-\x7E]/g, "?")}"`,
344
+ offset: 0
345
+ });
346
+ }
347
+ let version;
348
+ if (data.length >= 12) {
349
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
350
+ version = view.getInt32(8, true);
351
+ if (version < 200 || version > 1e3) {
352
+ issues.push({
353
+ severity: "warning",
354
+ code: "UNUSUAL_VERSION",
355
+ message: `Unusual version number: ${version}. Expected between 200-1000.`,
356
+ details: { version }
357
+ });
358
+ }
359
+ }
360
+ const estimatedEntityCount = Math.floor(data.length / 75);
361
+ const sizeCategory = getSizeCategory(data.length);
362
+ if (sizeCategory === "very_large") {
363
+ issues.push({
364
+ severity: "warning",
365
+ code: "LARGE_FILE",
366
+ message: `File is very large (${formatBytes(data.length)}). Consider using streaming mode for better performance.`,
367
+ details: { size: data.length, sizeFormatted: formatBytes(data.length) }
368
+ });
369
+ }
370
+ const hasErrors = issues.some((i) => i.severity === "error");
371
+ return {
372
+ valid: !hasErrors,
373
+ version,
374
+ sizeCategory,
375
+ estimatedEntityCount,
376
+ issues,
377
+ validationTimeMs: Date.now() - startTime
378
+ };
379
+ }
380
+ /**
381
+ * Validate and throw if invalid
382
+ * Convenience method that throws a ValidationError if the file is invalid
383
+ *
384
+ * @param data - JWW file content as Uint8Array
385
+ * @throws {ValidationError} If the file is invalid
386
+ */
387
+ validateOrThrow(data) {
388
+ const result = this.validate(data);
389
+ if (!result.valid) {
390
+ throw new ValidationError(
391
+ "File validation failed: " + result.issues.filter((i) => i.severity === "error").map((i) => i.message).join("; "),
392
+ result.issues
393
+ );
78
394
  }
79
395
  }
396
+ // ===========================================================================
397
+ // Parsing Methods
398
+ // ===========================================================================
80
399
  /**
81
400
  * Parse a JWW file and return the document structure
401
+ *
82
402
  * @param data - JWW file content as Uint8Array
403
+ * @param options - Optional parsing options
83
404
  * @returns Parsed JWW document
405
+ * @throws {NotInitializedError} If parser is not initialized
406
+ * @throws {ParseError} If parsing fails
84
407
  */
85
- parse(data) {
408
+ parse(data, options) {
86
409
  this.ensureInitialized();
87
- const result = globalThis.jwwParse(data);
88
- if (!result.ok) {
89
- throw new Error(result.error || "Parse failed");
410
+ if (options?.signal?.aborted) {
411
+ throw new JwwParserError(
412
+ "PARSE_ERROR" /* PARSE_ERROR */,
413
+ "Parse operation was aborted"
414
+ );
415
+ }
416
+ const startTime = Date.now();
417
+ this.log("info", "Starting parse operation", {
418
+ dataSize: data.length,
419
+ options: options ? { ...options, onProgress: void 0 } : void 0
420
+ });
421
+ if (options?.onProgress) {
422
+ options.onProgress({
423
+ stage: "loading",
424
+ progress: 0,
425
+ overallProgress: 0,
426
+ message: "Loading file data",
427
+ elapsedMs: 0
428
+ });
429
+ }
430
+ try {
431
+ const result = globalThis.jwwParse(data);
432
+ if (!result.ok) {
433
+ this.stats.errorCount++;
434
+ throw createParseError(result);
435
+ }
436
+ if (options?.onProgress) {
437
+ options.onProgress({
438
+ stage: "complete",
439
+ progress: 100,
440
+ overallProgress: 100,
441
+ message: "Parsing complete",
442
+ elapsedMs: Date.now() - startTime
443
+ });
444
+ }
445
+ const doc = JSON.parse(result.data);
446
+ let filteredDoc = doc;
447
+ if (options) {
448
+ filteredDoc = this.applyParseOptions(doc, options);
449
+ }
450
+ const parseTime = Date.now() - startTime;
451
+ this.stats.parseCount++;
452
+ this.stats.totalBytesProcessed += data.length;
453
+ this.stats.parseTimes.push(parseTime);
454
+ if (this.stats.parseTimes.length > 100) {
455
+ this.stats.parseTimes.shift();
456
+ }
457
+ this.log("info", "Parse complete", {
458
+ parseTimeMs: parseTime,
459
+ entityCount: filteredDoc.Entities.length,
460
+ blockCount: filteredDoc.Blocks.length
461
+ });
462
+ return filteredDoc;
463
+ } catch (error) {
464
+ this.stats.errorCount++;
465
+ this.log("error", "Parse failed", {
466
+ error: error instanceof Error ? error.message : String(error)
467
+ });
468
+ if (error instanceof JwwParserError) {
469
+ throw error;
470
+ }
471
+ throw new ParseError(
472
+ error instanceof Error ? error.message : String(error),
473
+ { cause: error instanceof Error ? error : void 0 }
474
+ );
475
+ }
476
+ }
477
+ applyParseOptions(doc, options) {
478
+ let entities = doc.Entities;
479
+ let blocks = doc.Blocks;
480
+ if (options.skipEntityTypes && options.skipEntityTypes.length > 0) {
481
+ const skipTypes = new Set(options.skipEntityTypes);
482
+ entities = entities.filter((e) => !skipTypes.has(e.Type));
483
+ }
484
+ if (options.layerGroupFilter && options.layerGroupFilter.length > 0) {
485
+ const groups = new Set(options.layerGroupFilter);
486
+ entities = entities.filter((e) => groups.has(e.LayerGroup));
487
+ }
488
+ if (options.layerFilter) {
489
+ const filter = options.layerFilter;
490
+ entities = entities.filter((e) => {
491
+ const layers = filter[e.LayerGroup];
492
+ return !layers || layers.includes(e.Layer);
493
+ });
90
494
  }
91
- return JSON.parse(result.data);
495
+ if (options.maxEntities && entities.length > options.maxEntities) {
496
+ entities = entities.slice(0, options.maxEntities);
497
+ }
498
+ if (options.includeBlocks === false) {
499
+ blocks = [];
500
+ }
501
+ return {
502
+ ...doc,
503
+ Entities: entities,
504
+ Blocks: blocks
505
+ };
92
506
  }
93
507
  /**
94
508
  * Parse a JWW file and convert to DXF document structure
509
+ *
95
510
  * @param data - JWW file content as Uint8Array
511
+ * @param options - Optional conversion options
96
512
  * @returns DXF document object
97
513
  */
98
- toDxf(data) {
514
+ toDxf(data, options) {
99
515
  this.ensureInitialized();
100
- const result = globalThis.jwwToDxf(data);
101
- if (!result.ok) {
102
- throw new Error(result.error || "Conversion failed");
516
+ const startTime = Date.now();
517
+ this.log("info", "Starting DXF conversion", { dataSize: data.length });
518
+ if (options?.onProgress) {
519
+ options.onProgress({
520
+ stage: "converting",
521
+ progress: 0,
522
+ overallProgress: 0,
523
+ message: "Converting to DXF",
524
+ elapsedMs: 0
525
+ });
526
+ }
527
+ try {
528
+ const result = globalThis.jwwToDxf(data);
529
+ if (!result.ok) {
530
+ this.stats.errorCount++;
531
+ throw createParseError(result);
532
+ }
533
+ if (options?.onProgress) {
534
+ options.onProgress({
535
+ stage: "complete",
536
+ progress: 100,
537
+ overallProgress: 100,
538
+ message: "Conversion complete",
539
+ elapsedMs: Date.now() - startTime
540
+ });
541
+ }
542
+ const doc = JSON.parse(result.data);
543
+ const parseTime = Date.now() - startTime;
544
+ this.stats.parseCount++;
545
+ this.stats.totalBytesProcessed += data.length;
546
+ this.stats.parseTimes.push(parseTime);
547
+ this.log("info", "DXF conversion complete", {
548
+ parseTimeMs: parseTime,
549
+ entityCount: doc.Entities.length
550
+ });
551
+ return doc;
552
+ } catch (error) {
553
+ this.stats.errorCount++;
554
+ this.log("error", "DXF conversion failed", {
555
+ error: error instanceof Error ? error.message : String(error)
556
+ });
557
+ if (error instanceof JwwParserError) {
558
+ throw error;
559
+ }
560
+ throw new ParseError(
561
+ error instanceof Error ? error.message : String(error),
562
+ { cause: error instanceof Error ? error : void 0 }
563
+ );
103
564
  }
104
- return JSON.parse(result.data);
105
565
  }
106
566
  /**
107
567
  * Parse a JWW file and convert to DXF file content string
568
+ *
108
569
  * @param data - JWW file content as Uint8Array
109
- * @returns DXF file content as string
570
+ * @param options - Optional conversion options
571
+ * @returns DXF file content as string (ready to save as .dxf file)
110
572
  */
111
- toDxfString(data) {
573
+ toDxfString(data, options) {
112
574
  this.ensureInitialized();
113
- const result = globalThis.jwwToDxfString(data);
114
- if (!result.ok) {
115
- throw new Error(result.error || "Conversion failed");
575
+ const startTime = Date.now();
576
+ this.log("info", "Starting DXF string generation", { dataSize: data.length });
577
+ try {
578
+ const result = globalThis.jwwToDxfString(data);
579
+ if (!result.ok) {
580
+ this.stats.errorCount++;
581
+ throw createParseError(result);
582
+ }
583
+ const parseTime = Date.now() - startTime;
584
+ this.stats.parseCount++;
585
+ this.stats.totalBytesProcessed += data.length;
586
+ this.stats.parseTimes.push(parseTime);
587
+ this.log("info", "DXF string generation complete", {
588
+ parseTimeMs: parseTime,
589
+ outputLength: result.data.length
590
+ });
591
+ return result.data;
592
+ } catch (error) {
593
+ this.stats.errorCount++;
594
+ if (error instanceof JwwParserError) {
595
+ throw error;
596
+ }
597
+ throw new ParseError(
598
+ error instanceof Error ? error.message : String(error),
599
+ { cause: error instanceof Error ? error : void 0 }
600
+ );
601
+ }
602
+ }
603
+ // ===========================================================================
604
+ // Memory Management
605
+ // ===========================================================================
606
+ /**
607
+ * Get current memory usage statistics
608
+ */
609
+ getMemoryStats() {
610
+ let wasmMemoryBytes = 0;
611
+ if (this.wasmInstance?.exports.memory) {
612
+ const memory = this.wasmInstance.exports.memory;
613
+ wasmMemoryBytes = memory.buffer.byteLength;
614
+ }
615
+ let jsHeapBytes;
616
+ if (typeof process !== "undefined" && typeof process.memoryUsage === "function") {
617
+ jsHeapBytes = process.memoryUsage().heapUsed;
618
+ }
619
+ const totalBytes = wasmMemoryBytes + (jsHeapBytes || 0);
620
+ return {
621
+ wasmMemoryBytes,
622
+ jsHeapBytes,
623
+ totalBytes,
624
+ totalFormatted: formatBytes(totalBytes)
625
+ };
626
+ }
627
+ /**
628
+ * Get parser statistics
629
+ */
630
+ getStats() {
631
+ const parseTimes = this.stats.parseTimes;
632
+ return {
633
+ parseCount: this.stats.parseCount,
634
+ totalBytesProcessed: this.stats.totalBytesProcessed,
635
+ averageParseTimeMs: parseTimes.length > 0 ? parseTimes.reduce((a, b) => a + b, 0) / parseTimes.length : 0,
636
+ fastestParseTimeMs: parseTimes.length > 0 ? Math.min(...parseTimes) : 0,
637
+ slowestParseTimeMs: parseTimes.length > 0 ? Math.max(...parseTimes) : 0,
638
+ errorCount: this.stats.errorCount,
639
+ memoryStats: this.getMemoryStats()
640
+ };
641
+ }
642
+ /**
643
+ * Reset parser statistics
644
+ */
645
+ resetStats() {
646
+ this.stats = {
647
+ parseCount: 0,
648
+ totalBytesProcessed: 0,
649
+ parseTimes: [],
650
+ errorCount: 0
651
+ };
652
+ this.log("debug", "Statistics reset");
653
+ }
654
+ /**
655
+ * Clean up resources and release memory
656
+ * Call this when you're done using the parser to free WASM memory
657
+ */
658
+ dispose() {
659
+ this.log("info", "Disposing parser resources");
660
+ this.goInstance = null;
661
+ this.wasmInstance = null;
662
+ this.initialized = false;
663
+ this.initPromise = null;
664
+ this.stats = {
665
+ parseCount: 0,
666
+ totalBytesProcessed: 0,
667
+ parseTimes: [],
668
+ errorCount: 0
669
+ };
670
+ this.debugLogs = [];
671
+ }
672
+ /**
673
+ * Get the WASM module version
674
+ */
675
+ getVersion() {
676
+ if (typeof globalThis.jwwGetVersion === "function") {
677
+ return globalThis.jwwGetVersion();
116
678
  }
117
- return result.data;
679
+ return "1.0.0";
118
680
  }
119
681
  };
120
- async function createParser(wasmPath) {
682
+ async function createParser(wasmPath, options) {
121
683
  const parser = new JwwParser(wasmPath);
684
+ if (options?.debug) {
685
+ parser.setDebug(options.debug);
686
+ }
122
687
  await parser.init();
123
688
  return parser;
124
689
  }
125
- var index_default = { JwwParser, createParser };
690
+ function quickValidate(data) {
691
+ const parser = new JwwParser();
692
+ return parser.validate(data);
693
+ }
694
+ function isJwwFile(data) {
695
+ if (data.length < 8) return false;
696
+ const signature = new TextDecoder().decode(data.slice(0, 8));
697
+ return signature === "JwwData.";
698
+ }
699
+ var index_default = {
700
+ JwwParser,
701
+ createParser,
702
+ quickValidate,
703
+ isJwwFile,
704
+ JwwParserError,
705
+ NotInitializedError,
706
+ WasmLoadError,
707
+ ValidationError,
708
+ ParseError,
709
+ JwwErrorCode
710
+ };
126
711
  export {
712
+ JwwErrorCode,
127
713
  JwwParser,
714
+ JwwParserError,
715
+ NotInitializedError,
716
+ ParseError,
717
+ ValidationError,
718
+ WasmLoadError,
128
719
  createParser,
129
- index_default as default
720
+ index_default as default,
721
+ isJwwFile,
722
+ quickValidate
130
723
  };