jww-parser 2025.12.1 → 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.d.mts +794 -15
- package/dist/index.d.ts +794 -15
- package/dist/index.js +646 -45
- package/dist/index.mjs +637 -44
- package/package.json +1 -1
- package/wasm/jww-parser.wasm +0 -0
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
|
-
|
|
64
|
-
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
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
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
* @
|
|
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
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
760
|
+
JwwParserError,
|
|
761
|
+
NotInitializedError,
|
|
762
|
+
ParseError,
|
|
763
|
+
ValidationError,
|
|
764
|
+
WasmLoadError,
|
|
765
|
+
createParser,
|
|
766
|
+
isJwwFile,
|
|
767
|
+
quickValidate
|
|
167
768
|
});
|