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