@stll/docx-core 0.15.0 → 0.15.2
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/docx_kernel_bg.wasm +0 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.js +73 -6
- package/dist/projection.d.ts +6 -6
- package/dist/projection.js +2 -2
- package/package.json +3 -2
package/dist/docx_kernel_bg.wasm
CHANGED
|
Binary file
|
package/dist/index.d.ts
CHANGED
|
@@ -122,10 +122,19 @@ type SerializeDocumentOptions = {
|
|
|
122
122
|
declare const serializeDocumentToDocx: (document: Document, options?: SerializeDocumentOptions) => Promise<ArrayBuffer>;
|
|
123
123
|
//#endregion
|
|
124
124
|
//#region src/validate/docx.d.ts
|
|
125
|
+
declare const DOCX_PACKAGE_ISSUE_CODES: {
|
|
126
|
+
readonly ArchiveBoundsExceeded: "archive_bounds_exceeded";
|
|
127
|
+
readonly InvalidArchive: "invalid_archive";
|
|
128
|
+
readonly InvalidDocumentRoot: "invalid_document_root";
|
|
129
|
+
readonly MissingNumberingPart: "missing_numbering_part";
|
|
130
|
+
readonly MissingPackagePart: "missing_package_part";
|
|
131
|
+
};
|
|
132
|
+
type DocxPackageIssueCode = (typeof DOCX_PACKAGE_ISSUE_CODES)[keyof typeof DOCX_PACKAGE_ISSUE_CODES];
|
|
125
133
|
type ValidateDocxPackageResult = {
|
|
126
134
|
valid: true;
|
|
127
135
|
} | {
|
|
128
136
|
valid: false;
|
|
137
|
+
code: DocxPackageIssueCode;
|
|
129
138
|
error: string;
|
|
130
139
|
};
|
|
131
140
|
type ValidateDocumentModelIssue = {
|
|
@@ -141,4 +150,4 @@ declare const validateDocxPackage: (buffer: ArrayBuffer | Uint8Array) => Promise
|
|
|
141
150
|
declare const validateDocumentModel: (document: Document) => ValidateDocumentModelResult;
|
|
142
151
|
declare const assertValidDocumentModel: (document: Document) => void;
|
|
143
152
|
//#endregion
|
|
144
|
-
export { type Autofix, type BlockContent, type BreakContent, type CompiledLegalDocument, DOCX_CONFORMANCE_CLASSES, type Document, type DocumentBody, type DocxConformanceClass, type DocxPackage, type LegalDraft, type LegalDraftBlock, type LegalDraftDiagnostic, type LegalSourceCompileOptions, type LegalSourceCompileResult, type LegalSourceDocxCompileResult, type LegalSourceParseResult, type Paragraph, type ParagraphContent, type PositionalTab, type Run, type RunContent, type SectionProperties, type Style, type Table, type TableCell, type TableRow, type TextContent, type ValidateDocumentModelIssue, type ValidateDocumentModelResult, type ValidateDocxPackageResult, assertValidDocumentModel, compileLegalSourceToDocument, compileLegalSourceToDocx, parseLegalSource, serializeDocumentToDocx, validateDocumentModel, validateDocxPackage, validateLegalDraft };
|
|
153
|
+
export { type Autofix, type BlockContent, type BreakContent, type CompiledLegalDocument, DOCX_CONFORMANCE_CLASSES, DOCX_PACKAGE_ISSUE_CODES, type Document, type DocumentBody, type DocxConformanceClass, type DocxPackage, type DocxPackageIssueCode, type LegalDraft, type LegalDraftBlock, type LegalDraftDiagnostic, type LegalSourceCompileOptions, type LegalSourceCompileResult, type LegalSourceDocxCompileResult, type LegalSourceParseResult, type Paragraph, type ParagraphContent, type PositionalTab, type Run, type RunContent, type SectionProperties, type Style, type Table, type TableCell, type TableRow, type TextContent, type ValidateDocumentModelIssue, type ValidateDocumentModelResult, type ValidateDocxPackageResult, assertValidDocumentModel, compileLegalSourceToDocument, compileLegalSourceToDocx, parseLegalSource, serializeDocumentToDocx, validateDocumentModel, validateDocxPackage, validateLegalDraft };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { r as isOoxmlSymbolCharacter, t as DOCX_CONFORMANCE_CLASSES } from "./document-BO7h-LxY.js";
|
|
2
2
|
import JSZip from "jszip";
|
|
3
3
|
import { panic } from "better-result";
|
|
4
|
+
import { XMLParser, XMLValidator } from "fast-xml-parser";
|
|
4
5
|
//#region src/serialize/xml.ts
|
|
5
6
|
const ILLEGAL_XML_CHARS_RE = /[^\u0009\u000A\u000D\u0020-\uD7FF\uE000-\uFFFD\u{10000}-\u{10FFFF}]/gu;
|
|
6
7
|
const stripIllegalXmlChars = (value) => value.replace(ILLEGAL_XML_CHARS_RE, "");
|
|
@@ -322,6 +323,13 @@ const serializeCoreProperties = (document) => {
|
|
|
322
323
|
};
|
|
323
324
|
//#endregion
|
|
324
325
|
//#region src/validate/docx.ts
|
|
326
|
+
const DOCX_PACKAGE_ISSUE_CODES = {
|
|
327
|
+
ArchiveBoundsExceeded: "archive_bounds_exceeded",
|
|
328
|
+
InvalidArchive: "invalid_archive",
|
|
329
|
+
InvalidDocumentRoot: "invalid_document_root",
|
|
330
|
+
MissingNumberingPart: "missing_numbering_part",
|
|
331
|
+
MissingPackagePart: "missing_package_part"
|
|
332
|
+
};
|
|
325
333
|
/**
|
|
326
334
|
* Local bounds on the ZIP archive `validateDocxPackage` inflates parts from.
|
|
327
335
|
* `docx-core` has no dependency on `@stll/folio-core`, so these mirror the
|
|
@@ -334,6 +342,17 @@ const serializeCoreProperties = (document) => {
|
|
|
334
342
|
const VALIDATE_DOCX_MAX_ENTRIES = 4096;
|
|
335
343
|
const VALIDATE_DOCX_MAX_ENTRY_BYTES = 128 * 1024 * 1024;
|
|
336
344
|
const VALIDATE_DOCX_MAX_TOTAL_BYTES = 256 * 1024 * 1024;
|
|
345
|
+
const VALIDATE_DOCX_MAX_DOCUMENT_XML_BYTES = 32 * 1024 * 1024;
|
|
346
|
+
const WORDPROCESSINGML_NAMESPACES = /* @__PURE__ */ new Set(["http://schemas.openxmlformats.org/wordprocessingml/2006/main", "http://purl.oclc.org/ooxml/wordprocessingml/main"]);
|
|
347
|
+
const packageXmlParser = new XMLParser({
|
|
348
|
+
preserveOrder: true,
|
|
349
|
+
ignoreAttributes: false,
|
|
350
|
+
attributeNamePrefix: "",
|
|
351
|
+
parseTagValue: false,
|
|
352
|
+
parseAttributeValue: false,
|
|
353
|
+
processEntities: false,
|
|
354
|
+
ignoreDeclaration: true
|
|
355
|
+
});
|
|
337
356
|
const getDeclaredUncompressedSize = (file) => {
|
|
338
357
|
const metadata = file._data;
|
|
339
358
|
return typeof metadata?.uncompressedSize === "number" ? metadata.uncompressedSize : null;
|
|
@@ -358,36 +377,84 @@ const checkDocxArchiveBounds = (zip) => {
|
|
|
358
377
|
}
|
|
359
378
|
return null;
|
|
360
379
|
};
|
|
380
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
381
|
+
const elementName = (node) => Object.keys(node).find((key) => key !== ":@" && !key.startsWith("?")) ?? null;
|
|
382
|
+
const localName = (name) => name.slice(name.indexOf(":") + 1);
|
|
383
|
+
const prefix = (name) => {
|
|
384
|
+
const separator = name.indexOf(":");
|
|
385
|
+
return separator === -1 ? "" : name.slice(0, separator);
|
|
386
|
+
};
|
|
387
|
+
const namespaceFor = (name, attributes, inherited = {}) => {
|
|
388
|
+
const key = prefix(name) === "" ? "xmlns" : `xmlns:${prefix(name)}`;
|
|
389
|
+
const value = attributes[key] ?? inherited[key];
|
|
390
|
+
return typeof value === "string" ? value : null;
|
|
391
|
+
};
|
|
392
|
+
const validateDocumentXml = (xml) => {
|
|
393
|
+
if (XMLValidator.validate(xml) !== true) return "Generated DOCX has malformed word/document.xml.";
|
|
394
|
+
const parsed = packageXmlParser.parse(xml);
|
|
395
|
+
if (!Array.isArray(parsed)) return "Generated DOCX has no word/document.xml root document.";
|
|
396
|
+
const root = parsed.find(isRecord);
|
|
397
|
+
if (!root) return "Generated DOCX has no word/document.xml root document.";
|
|
398
|
+
const rootName = elementName(root);
|
|
399
|
+
const rootAttributes = isRecord(root[":@"]) ? root[":@"] : {};
|
|
400
|
+
if (rootName === null || localName(rootName) !== "document" || !WORDPROCESSINGML_NAMESPACES.has(namespaceFor(rootName, rootAttributes) ?? "")) return "Generated DOCX has no WordprocessingML document root.";
|
|
401
|
+
const children = root[rootName];
|
|
402
|
+
if (!Array.isArray(children)) return "Generated DOCX document root has no WordprocessingML body.";
|
|
403
|
+
return children.some((child) => {
|
|
404
|
+
if (!isRecord(child)) return false;
|
|
405
|
+
const childName = elementName(child);
|
|
406
|
+
const childAttributes = isRecord(child[":@"]) ? child[":@"] : {};
|
|
407
|
+
return childName !== null && localName(childName) === "body" && WORDPROCESSINGML_NAMESPACES.has(namespaceFor(childName, childAttributes, rootAttributes) ?? "");
|
|
408
|
+
}) ? null : "Generated DOCX document root has no WordprocessingML body.";
|
|
409
|
+
};
|
|
361
410
|
const validateDocxPackage = async (buffer) => {
|
|
362
411
|
try {
|
|
363
412
|
const zip = await JSZip.loadAsync(buffer);
|
|
364
413
|
const boundsError = checkDocxArchiveBounds(zip);
|
|
365
414
|
if (boundsError) return {
|
|
366
415
|
valid: false,
|
|
416
|
+
code: DOCX_PACKAGE_ISSUE_CODES.ArchiveBoundsExceeded,
|
|
367
417
|
error: boundsError
|
|
368
418
|
};
|
|
369
419
|
for (const requiredPath of [
|
|
370
420
|
"[Content_Types].xml",
|
|
371
421
|
"_rels/.rels",
|
|
372
|
-
"word/document.xml"
|
|
373
|
-
"word/styles.xml",
|
|
374
|
-
"word/_rels/document.xml.rels"
|
|
422
|
+
"word/document.xml"
|
|
375
423
|
]) if (!zip.file(requiredPath)) return {
|
|
376
424
|
valid: false,
|
|
425
|
+
code: DOCX_PACKAGE_ISSUE_CODES.MissingPackagePart,
|
|
377
426
|
error: `Generated DOCX is missing required package part: ${requiredPath}`
|
|
378
427
|
};
|
|
379
|
-
|
|
428
|
+
const documentPart = zip.file("word/document.xml");
|
|
429
|
+
const declaredDocumentBytes = documentPart ? getDeclaredUncompressedSize(documentPart) : null;
|
|
430
|
+
if (declaredDocumentBytes !== null && declaredDocumentBytes > VALIDATE_DOCX_MAX_DOCUMENT_XML_BYTES) return {
|
|
431
|
+
valid: false,
|
|
432
|
+
code: DOCX_PACKAGE_ISSUE_CODES.ArchiveBoundsExceeded,
|
|
433
|
+
error: `Generated DOCX word/document.xml declares ${declaredDocumentBytes} uncompressed bytes, over the ${VALIDATE_DOCX_MAX_DOCUMENT_XML_BYTES}-byte limit.`
|
|
434
|
+
};
|
|
435
|
+
const documentBytes = await documentPart?.async("uint8array");
|
|
436
|
+
if (documentBytes !== void 0 && documentBytes.byteLength > VALIDATE_DOCX_MAX_DOCUMENT_XML_BYTES) return {
|
|
437
|
+
valid: false,
|
|
438
|
+
code: DOCX_PACKAGE_ISSUE_CODES.ArchiveBoundsExceeded,
|
|
439
|
+
error: `Generated DOCX word/document.xml exceeds the ${VALIDATE_DOCX_MAX_DOCUMENT_XML_BYTES}-byte limit.`
|
|
440
|
+
};
|
|
441
|
+
const documentXml = documentBytes ? new TextDecoder().decode(documentBytes) : void 0;
|
|
442
|
+
const documentError = documentXml ? validateDocumentXml(documentXml) : "Generated DOCX has no word/document.xml root document.";
|
|
443
|
+
if (documentError) return {
|
|
380
444
|
valid: false,
|
|
381
|
-
|
|
445
|
+
code: DOCX_PACKAGE_ISSUE_CODES.InvalidDocumentRoot,
|
|
446
|
+
error: documentError
|
|
382
447
|
};
|
|
383
448
|
if ((await zip.file("word/_rels/document.xml.rels")?.async("string"))?.includes("/relationships/numbering") && !zip.file("word/numbering.xml")) return {
|
|
384
449
|
valid: false,
|
|
450
|
+
code: DOCX_PACKAGE_ISSUE_CODES.MissingNumberingPart,
|
|
385
451
|
error: "Generated DOCX references numbering.xml but does not include it."
|
|
386
452
|
};
|
|
387
453
|
return { valid: true };
|
|
388
454
|
} catch (error) {
|
|
389
455
|
return {
|
|
390
456
|
valid: false,
|
|
457
|
+
code: DOCX_PACKAGE_ISSUE_CODES.InvalidArchive,
|
|
391
458
|
error: error instanceof Error ? error.message : "Invalid DOCX package."
|
|
392
459
|
};
|
|
393
460
|
}
|
|
@@ -1983,4 +2050,4 @@ const compileLegalSourceToDocx = async (source, options = {}) => {
|
|
|
1983
2050
|
};
|
|
1984
2051
|
};
|
|
1985
2052
|
//#endregion
|
|
1986
|
-
export { DOCX_CONFORMANCE_CLASSES, assertValidDocumentModel, compileLegalSourceToDocument, compileLegalSourceToDocx, parseLegalSource, serializeDocumentToDocx, validateDocumentModel, validateDocxPackage, validateLegalDraft };
|
|
2053
|
+
export { DOCX_CONFORMANCE_CLASSES, DOCX_PACKAGE_ISSUE_CODES, assertValidDocumentModel, compileLegalSourceToDocument, compileLegalSourceToDocx, parseLegalSource, serializeDocumentToDocx, validateDocumentModel, validateDocxPackage, validateLegalDraft };
|
package/dist/projection.d.ts
CHANGED
|
@@ -40,16 +40,16 @@ type DocxReviewFactsWire = readonly [schemaVersion: 2, revisions: DocxReviewFact
|
|
|
40
40
|
type DocxPackageProjectionWire = readonly [schemaVersion: 2, document: DocxProjectionWire, reviewFacts: DocxReviewFactsWire];
|
|
41
41
|
//#endregion
|
|
42
42
|
//#region src/projection.d.ts
|
|
43
|
-
declare const DocxProjectionInitializationError_base: import("better-result").TaggedErrorClass<"DocxProjectionInitializationError"
|
|
43
|
+
declare const DocxProjectionInitializationError_base: import("better-result").TaggedErrorClass<"DocxProjectionInitializationError">;
|
|
44
|
+
declare class DocxProjectionInitializationError extends DocxProjectionInitializationError_base<{
|
|
44
45
|
message: string;
|
|
45
46
|
cause: unknown;
|
|
46
|
-
}
|
|
47
|
-
declare
|
|
48
|
-
declare
|
|
47
|
+
}> {}
|
|
48
|
+
declare const DocxProjectionError_base: import("better-result").TaggedErrorClass<"DocxProjectionError">;
|
|
49
|
+
declare class DocxProjectionError extends DocxProjectionError_base<{
|
|
49
50
|
message: string;
|
|
50
51
|
cause: unknown;
|
|
51
|
-
}
|
|
52
|
-
declare class DocxProjectionError extends DocxProjectionError_base {}
|
|
52
|
+
}> {}
|
|
53
53
|
type DocxProjectionWasmSource = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
|
54
54
|
type InitializeDocxProjectionOptions = {
|
|
55
55
|
/**
|
package/dist/projection.js
CHANGED
|
@@ -222,8 +222,8 @@ async function __wbg_init(module_or_path) {
|
|
|
222
222
|
}
|
|
223
223
|
//#endregion
|
|
224
224
|
//#region src/projection.ts
|
|
225
|
-
var DocxProjectionInitializationError = class extends TaggedError("DocxProjectionInitializationError")
|
|
226
|
-
var DocxProjectionError = class extends TaggedError("DocxProjectionError")
|
|
225
|
+
var DocxProjectionInitializationError = class extends TaggedError("DocxProjectionInitializationError") {};
|
|
226
|
+
var DocxProjectionError = class extends TaggedError("DocxProjectionError") {};
|
|
227
227
|
let initialization;
|
|
228
228
|
const DOCUMENT_PROJECTION_FAILURE_MESSAGE = "Could not project the DOCX document";
|
|
229
229
|
const PACKAGE_PROJECTION_FAILURE_MESSAGE = "Could not project the DOCX package with review facts";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stll/docx-core",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.2",
|
|
4
4
|
"description": "Typed OOXML/DOCX model, validation, serialization, legal-source compilation, and browser-native package projection.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"document-model",
|
|
@@ -60,7 +60,8 @@
|
|
|
60
60
|
"prepack": "bun run build"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"better-result": "
|
|
63
|
+
"better-result": "3.0.1",
|
|
64
|
+
"fast-xml-parser": "^5.10.1",
|
|
64
65
|
"jszip": "3.10.1"
|
|
65
66
|
},
|
|
66
67
|
"devDependencies": {
|