archive-codec 1.3.0 → 1.4.1

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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![GitHub](https://img.shields.io/badge/GitHub-181717?logo=github&logoColor=white)](https://github.com/ExaDev/documents.js/tree/main/packages/archive-codec) [![npm](https://img.shields.io/badge/npm-CB3837?logo=npm&logoColor=white)](https://www.npmjs.com/package/archive-codec) [![npm version](https://img.shields.io/npm/v/archive-codec)](https://www.npmjs.com/package/archive-codec) [![CI](https://img.shields.io/github/actions/workflow/status/ExaDev/documents.js/ci.yml?branch=main)](https://github.com/ExaDev/documents.js/actions)
4
4
 
5
- > ZIP-in-ZIP recursive walking under depth and cumulative decompressed-size guards, and classic OLE compound-file ([MS-CFB]) reading and writing — zero document-format knowledge, the archive and container utility package for the [documents.js family](../../README.md). Worker-isomorphic: the same code runs under Node and inside a Cloudflare Workers isolate.
5
+ > ZIP-in-ZIP recursive walking under depth and cumulative decompressed-size guards, classic OLE compound-file ([MS-CFB]) reading and writing, and [MS-OLEPS] Property Set Stream reading and writing — zero document-format knowledge, the archive and container utility package for the [documents.js family](../../README.md). Worker-isomorphic: the same code runs under Node and inside a Cloudflare Workers isolate.
6
6
 
7
7
  Created for [documents.js#564](https://github.com/ExaDev/documents.js/issues/564): nothing in the ecosystem recursed into a nested archive. Most concretely, OOXML's embedded-object model — a docx/pptx carrying a genuinely separate ZIP blob at `word/embeddings/oleObject1.xlsx` — had no safe handling anywhere, and no package guarded against recursive-archive inputs at all (`byte-codec`'s 512 MiB per-stream inflate cap does not compose across recursion). A new sibling was chosen over extending `byte-codec` (whose charter is byte/image primitives, zero container-format knowledge) or doing it inline in `documents.js` (which would repeat the duplication `byte-codec`'s own extraction was meant to avoid). Its first family consumer is `ooxml.js`'s OLE embedded-object recovery — [documents.js#733](https://github.com/ExaDev/documents.js/issues/733) (pptx, `p:oleObj`) and [documents.js#734](https://github.com/ExaDev/documents.js/issues/734) (docx, `o:OLEObject`): an OLE payload part's bytes are checked through `isZipArchive` and, when they are a ZIP, decoded as a nested OOXML package behind this package's guarded walk — the bounded inflate that populates `document-schema.js`'s `ContentEmbeddedObject`/`ContentEmbeddedObjectBlock` (the same vocabulary odf.js embeds formula sub-documents through) with a genuinely recovered sub-document.
8
8
 
@@ -10,7 +10,9 @@ Created for [documents.js#564](https://github.com/ExaDev/documents.js/issues/564
10
10
 
11
11
  [documents.js#815](https://github.com/ExaDev/documents.js/issues/815), [#816](https://github.com/ExaDev/documents.js/issues/816), and [#817](https://github.com/ExaDev/documents.js/issues/817) then needed the other direction. `xls-codec`, `doc-codec`, `ppt-codec`, and `wpd-codec` each read a legacy Office binary format out of an [MS-CFB] container, and none of them can write one back, because there was no container to put their streams into: a `.xls` writer producing a `Workbook` stream, or a `.doc` writer producing `WordDocument` and `1Table`, needs a conformant compound file to hold them. That container is structural knowledge exactly as the reader's is, so `writeCompoundFile` is the mirror of `readCompoundFile` here rather than four hand-rolled emitters in four codecs.
12
12
 
13
- Scope: **ZIP containers** (read and write over [`fflate`](https://github.com/101arrowz/fflate), recursive walking of ZIP-in-ZIP entries) and **classic OLE compound files** (bounded [MS-CFB] reading and conformant [MS-CFB] writing, plus the OLE Package stream unwrapping). **tar and gzip are explicitly out of scope.**
13
+ [documents.js#815](https://github.com/ExaDev/documents.js/issues/815), [#816](https://github.com/ExaDev/documents.js/issues/816), and [#817](https://github.com/ExaDev/documents.js/issues/817) also each named the same remaining gap: `doc-codec`, `xls-codec`, and `ppt-codec` all hard-coded document metadata (title, author, dates) to an empty object, because that metadata lives in a genuinely different structure from the one each format's own reader already parses -- a [MS-OLEPS] Property Set Stream, conventionally stored as a "\x05SummaryInformation" stream beside `WordDocument`/`Workbook`/`PowerPoint Document` in the identical [MS-CFB] container all three already read through this package. `oleps/read` and `oleps/write` are the generic property-set codec (the stream header, the PropertySet packet's dictionary, and VT_I2/VT_I4/VT_LPSTR/VT_LPWSTR/VT_FILETIME typed values), and `oleps/summary-information` is the SummaryInformation-specific mapping on top of it -- the same two-layer split `cfb/read.ts` and `cfb/ole-package.ts` already establish for the OLE Package stream, container structure below, one named stream's own field layout above.
14
+
15
+ Scope: **ZIP containers** (read and write over [`fflate`](https://github.com/101arrowz/fflate), recursive walking of ZIP-in-ZIP entries), **classic OLE compound files** (bounded [MS-CFB] reading and conformant [MS-CFB] writing, plus the OLE Package stream unwrapping), and **[MS-OLEPS] Property Set Streams** (generic read/write of a single-property-set stream, plus SummaryInformation's own title/subject/author/keywords/comments/created/last-saved/last-printed fields). **tar and gzip, DocumentSummaryInformation's extended and user-defined property sets, and writing VT_LPSTR (ANSI-codepage) string properties are explicitly out of scope.**
14
16
 
15
17
  ## Getting started
16
18
 
@@ -40,15 +42,19 @@ import { walkArchive } from "archive-codec/zip/walk";
40
42
 
41
43
  The smoke suite (`test/smoke.test.mjs`) is the guard on that advertisement: it loads each module below from the built `dist/` in both module systems, so a build config that stops serving an advertised subpath fails the suite — neither publint nor `attw` catches a wildcard whose targets are missing.
42
44
 
43
- | Module | Exports |
44
- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
45
- | `zip/container` | `zipPackage` (ordered-entries ZIP write with stored-uncompressed support), `unzipPackage`, `ZipEntry` |
46
- | `zip/detect` | `detectArchiveFormat` (`'zip' \| 'cfb' \| 'unknown'`), `isZipArchive`, `ArchiveFormat` |
47
- | `zip/walk` | `walkArchive` (recursive ZIP-in-ZIP walking), `ArchiveWalkEntry`, `ArchiveWalkLimitError`, `MAX_WALK_DEPTH`, `MAX_WALK_TOTAL_BYTES`, `WalkArchiveOptions` |
48
- | `cfb/detect` | `isCompoundFile` (the `D0 CF 11 E0 …` magic-byte check) |
49
- | `cfb/read` | `readCompoundFile` (bounded [MS-CFB] stream extraction), `CompoundFileStream`, `CompoundFileFormatError`, `MAX_CFB_TOTAL_STREAM_BYTES`, `ReadCompoundFileOptions` |
50
- | `cfb/write` | `writeCompoundFile` ([MS-CFB] container generation), `CompoundFileWriteError`, `WriteCompoundFileOptions` — takes the `CompoundFileStream` array `cfb/read` returns |
51
- | `cfb/ole-package` | `readOlePackage` (OLE Package stream unwrapping), `OlePackage`, `OlePackageFormatError` |
45
+ | Module | Exports |
46
+ | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
47
+ | `zip/container` | `zipPackage` (ordered-entries ZIP write with stored-uncompressed support), `unzipPackage`, `ZipEntry` |
48
+ | `zip/detect` | `detectArchiveFormat` (`'zip' \| 'cfb' \| 'unknown'`), `isZipArchive`, `ArchiveFormat` |
49
+ | `zip/walk` | `walkArchive` (recursive ZIP-in-ZIP walking), `ArchiveWalkEntry`, `ArchiveWalkLimitError`, `MAX_WALK_DEPTH`, `MAX_WALK_TOTAL_BYTES`, `WalkArchiveOptions` |
50
+ | `cfb/detect` | `isCompoundFile` (the `D0 CF 11 E0 …` magic-byte check) |
51
+ | `cfb/read` | `readCompoundFile` (bounded [MS-CFB] stream extraction), `CompoundFileStream`, `CompoundFileFormatError`, `MAX_CFB_TOTAL_STREAM_BYTES`, `ReadCompoundFileOptions` |
52
+ | `cfb/write` | `writeCompoundFile` ([MS-CFB] container generation), `CompoundFileWriteError`, `WriteCompoundFileOptions` — takes the `CompoundFileStream` array `cfb/read` returns |
53
+ | `cfb/ole-package` | `readOlePackage` (OLE Package stream unwrapping), `OlePackage`, `OlePackageFormatError` |
54
+ | `oleps/read` | `readPropertySetStream` (generic [MS-OLEPS] property-set decoding), `PropertySetFormatError` |
55
+ | `oleps/write` | `writePropertySetStream` (generic [MS-OLEPS] property-set encoding), `PropertySetWriteError` — takes the `PropertySet` shape `oleps/read` returns |
56
+ | `oleps/summary-information` | `readSummaryInformation`, `writeSummaryInformationStream`, `SummaryInformationProperties`, `FMTID_SUMMARY_INFORMATION` |
57
+ | `oleps/layout-metadata` | `summaryInformationToLayoutMetadata`, `layoutMetadataToSummaryInformation`, `hasSummaryInformationFields` — the `SummaryInformationProperties` <-> `document-schema.js`'s `LayoutMetadata` mapping, shared by `doc-codec`/`xls-codec`/`ppt-codec` (see [Property sets](#property-sets)) |
52
58
 
53
59
  ### Recursive walking
54
60
 
@@ -113,6 +119,35 @@ Two details are deliberate rather than incidental. The directory's sibling trees
113
119
 
114
120
  Correctness is checked against independent parsers, not only against this package's own reader: the written files are accepted by [`olefile`](https://github.com/decalage2/olefile) in its strict `DEFECT_INCORRECT` mode and by 7-Zip's Compound handler, both of which return byte-identical stream content, and a real LibreOffice-authored `.doc` read through `readCompoundFile` and re-emitted through `writeCompoundFile` still opens in LibreOffice Writer.
115
121
 
122
+ ### Property sets
123
+
124
+ ```ts
125
+ import {
126
+ readCompoundFile,
127
+ readSummaryInformation,
128
+ writeSummaryInformationStream,
129
+ } from "archive-codec";
130
+
131
+ const stream = readCompoundFile(docBytes).find(
132
+ (s) => s.path === "\x05SummaryInformation",
133
+ );
134
+ if (stream !== undefined) {
135
+ const metadata = readSummaryInformation(stream.bytes);
136
+ metadata.title; // string | undefined
137
+ metadata.createdIso; // string | undefined, ISO-8601
138
+ }
139
+
140
+ // The mirror image: builds a "\x05SummaryInformation" stream's bytes from the
141
+ // same shape, to hand to writeCompoundFile alongside the format's own streams.
142
+ const summaryStream = writeSummaryInformationStream({ title: "Q3 report" });
143
+ ```
144
+
145
+ `readSummaryInformation`/`writeSummaryInformationStream` cover the seven SummaryInformation fields a caller actually needs (title, subject, author, keywords, comments, and the created/last-saved/last-printed FILETIME timestamps, as ISO-8601 strings); everything else the property set can carry (template, last author, revision number, application name, edit time, page/word/character counts, document security) is read into the stream but not projected into `SummaryInformationProperties`, and the separate `"\x05DocumentSummaryInformation"` stream (company, manager, and custom user-defined properties) is not read or written at all. `readPropertySetStream`/`writePropertySetStream` are the generic layer beneath it — a `PropertySet`'s `formatId` and its `properties` map, keyed by `PropertyIdentifier`, valued by a `{ type, value }` pair over `VT_I2`/`VT_I4`/`VT_LPSTR`/`VT_LPWSTR`/`VT_FILETIME` — for a caller working with a different, non-SummaryInformation property set built on the identical [MS-OLEPS] wire format. The writer only emits `VT_LPWSTR` (Unicode) strings, never `VT_LPSTR`: a `CodePageString`'s ANSI encoding depends on the property set's own CodePage property, and writing an arbitrary codepage's bytes would need a full codepage table this package does not carry, so `VT_LPWSTR`'s codepage-independent UTF-16LE sidesteps the question entirely. The reader still decodes `VT_LPSTR` on the way in — `CP_WINUNICODE` (1200) and windows-1252 (1252, the value the [MS-OLEPS] SummaryInformation worked example itself declares, and the same ANSI convention `cfb/ole-package.ts` already uses) — since a real Office-authored file almost always writes ANSI strings, not Unicode ones.
146
+
147
+ A property whose type this reader does not decode — an unsupported `PropertyType` (e.g. `VT_CF`, the clipboard-format type `PIDSI_THUMBNAIL` uses), or a `VT_LPSTR` under a `CodePage` other than `CP_WINUNICODE`/windows-1252 — is skipped rather than aborting the whole read: a real SummaryInformation stream routinely carries a thumbnail or a non-Western codepage, and one undecodable property must not fail a read that also carries properties this reader can decode. Genuine structural nonconformance (a bad `ByteOrder`, a truncated stream, a `Dictionary` property, non-zero `TypedPropertyValue` padding) still throws `PropertySetFormatError`.
148
+
149
+ `oleps/layout-metadata`'s `summaryInformationToLayoutMetadata`/`layoutMetadataToSummaryInformation`/`hasSummaryInformationFields` map `SummaryInformationProperties` to and from `document-schema.js`'s own `LayoutMetadata` — the shared metadata shape every codec's `ContentDocument` carries, format-agnostic rather than specific to any one legacy binary format. `doc-codec`, `xls-codec`, and `ppt-codec` each import these directly rather than maintaining their own copy, wrapping `layoutMetadataToSummaryInformation` with their own `createdIso`/`modifiedIso` date validation so a malformed date is reported through that package's own error vocabulary rather than an opaque `RangeError` out of the FILETIME conversion.
150
+
116
151
  ### ZIP container
117
152
 
118
153
  `zipPackage` takes an _ordered_ array of `[path, entry]` tuples, not a `Record`, so the caller controls the exact emission order deterministically (the property formats with a fixed-offset first entry — ODF's `mimetype` — depend on), and any entry can be written stored-uncompressed via `stored: true`. `unzipPackage` is the read side; the returned `Record` makes no ordering promise and collapses duplicate paths.
@@ -121,7 +156,7 @@ Correctness is checked against independent parsers, not only against this packag
121
156
 
122
157
  - Worker-isomorphic (see the [family-wide convention](../../README.md#conventions)): runtime `src/` must not import `node:*`, a bare Node builtin, or use the `Buffer` global — enforced by a `no-restricted-imports`/`no-restricted-globals` ESLint rule and exercised in CI by running the test suite inside an actual `workerd` isolate (`pnpm test:workers`). Test files under `src/**/*.test.ts` and `src/test-support/` are exempt and may use Node APIs for fixtures.
123
158
  - Only `src/index.ts` may be named `index.*` — a custom ESLint rule (`local/no-non-barrel-index`) rejects any other module using an `index` basename, since that would be a hidden entry point the `exports` map in `package.json` doesn't advertise.
124
- - Zero document-format knowledge: this package knows bytes and container structure — ZIP entries, compound-file sectors and directory entries, the OLE packaging wrapper — never that any entry or stream is a document. It depends only on `fflate` — not on `byte-codec`, `ooxml.js`, or `odf.js` (whose ZIP wrappers it deliberately mirrors rather than imports, keeping their branding and release cadences decoupled).
159
+ - Zero document-format knowledge: this package knows bytes and container structure — ZIP entries, compound-file sectors and directory entries, the OLE packaging wrapper, [MS-OLEPS] property identifiers and typed values — never that any entry or stream is a document, or that PID 2 means a title. It depends only on `fflate` — not on `byte-codec`, `ooxml.js`, or `odf.js` (whose ZIP wrappers it deliberately mirrors rather than imports, keeping their branding and release cadences decoupled).
125
160
 
126
161
  ## Install
127
162
 
package/dist/index.cjs CHANGED
@@ -3,22 +3,36 @@ const require_cfb_detect = require("./cfb/detect.cjs");
3
3
  const require_cfb_ole_package = require("./cfb/ole-package.cjs");
4
4
  const require_cfb_read = require("./cfb/read.cjs");
5
5
  const require_cfb_write = require("./cfb/write.cjs");
6
+ const require_oleps_layout_metadata = require("./oleps/layout-metadata.cjs");
7
+ const require_oleps_read = require("./oleps/read.cjs");
8
+ const require_oleps_write = require("./oleps/write.cjs");
9
+ const require_oleps_summary_information = require("./oleps/summary-information.cjs");
6
10
  const require_zip_container = require("./zip/container.cjs");
7
11
  const require_zip_detect = require("./zip/detect.cjs");
8
12
  const require_zip_walk = require("./zip/walk.cjs");
9
13
  exports.ArchiveWalkLimitError = require_zip_walk.ArchiveWalkLimitError;
10
14
  exports.CompoundFileFormatError = require_cfb_read.CompoundFileFormatError;
11
15
  exports.CompoundFileWriteError = require_cfb_write.CompoundFileWriteError;
16
+ exports.FMTID_SUMMARY_INFORMATION = require_oleps_summary_information.FMTID_SUMMARY_INFORMATION;
12
17
  exports.MAX_CFB_TOTAL_STREAM_BYTES = require_cfb_read.MAX_CFB_TOTAL_STREAM_BYTES;
13
18
  exports.MAX_WALK_DEPTH = require_zip_walk.MAX_WALK_DEPTH;
14
19
  exports.MAX_WALK_TOTAL_BYTES = require_zip_walk.MAX_WALK_TOTAL_BYTES;
15
20
  exports.OlePackageFormatError = require_cfb_ole_package.OlePackageFormatError;
21
+ exports.PropertySetFormatError = require_oleps_read.PropertySetFormatError;
22
+ exports.PropertySetWriteError = require_oleps_write.PropertySetWriteError;
16
23
  exports.detectArchiveFormat = require_zip_detect.detectArchiveFormat;
24
+ exports.hasSummaryInformationFields = require_oleps_layout_metadata.hasSummaryInformationFields;
17
25
  exports.isCompoundFile = require_cfb_detect.isCompoundFile;
18
26
  exports.isZipArchive = require_zip_detect.isZipArchive;
27
+ exports.layoutMetadataToSummaryInformation = require_oleps_layout_metadata.layoutMetadataToSummaryInformation;
19
28
  exports.readCompoundFile = require_cfb_read.readCompoundFile;
20
29
  exports.readOlePackage = require_cfb_ole_package.readOlePackage;
30
+ exports.readPropertySetStream = require_oleps_read.readPropertySetStream;
31
+ exports.readSummaryInformation = require_oleps_summary_information.readSummaryInformation;
32
+ exports.summaryInformationToLayoutMetadata = require_oleps_layout_metadata.summaryInformationToLayoutMetadata;
21
33
  exports.unzipPackage = require_zip_container.unzipPackage;
22
34
  exports.walkArchive = require_zip_walk.walkArchive;
23
35
  exports.writeCompoundFile = require_cfb_write.writeCompoundFile;
36
+ exports.writePropertySetStream = require_oleps_write.writePropertySetStream;
37
+ exports.writeSummaryInformationStream = require_oleps_summary_information.writeSummaryInformationStream;
24
38
  exports.zipPackage = require_zip_container.zipPackage;
package/dist/index.d.cts CHANGED
@@ -2,7 +2,12 @@ import { isCompoundFile } from "./cfb/detect.cjs";
2
2
  import { OlePackage, OlePackageFormatError, readOlePackage } from "./cfb/ole-package.cjs";
3
3
  import { CompoundFileFormatError, CompoundFileStream, MAX_CFB_TOTAL_STREAM_BYTES, ReadCompoundFileOptions, readCompoundFile } from "./cfb/read.cjs";
4
4
  import { CompoundFileWriteError, WriteCompoundFileOptions, writeCompoundFile } from "./cfb/write.cjs";
5
+ import { FMTID_SUMMARY_INFORMATION, SummaryInformationProperties, readSummaryInformation, writeSummaryInformationStream } from "./oleps/summary-information.cjs";
6
+ import { hasSummaryInformationFields, layoutMetadataToSummaryInformation, summaryInformationToLayoutMetadata } from "./oleps/layout-metadata.cjs";
7
+ import { PropertySet, PropertyValue } from "./oleps/wire.cjs";
8
+ import { PropertySetFormatError, readPropertySetStream } from "./oleps/read.cjs";
9
+ import { PropertySetWriteError, writePropertySetStream } from "./oleps/write.cjs";
5
10
  import { ZipEntry, unzipPackage, zipPackage } from "./zip/container.cjs";
6
11
  import { ArchiveFormat, detectArchiveFormat, isZipArchive } from "./zip/detect.cjs";
7
12
  import { ArchiveWalkEntry, ArchiveWalkLimit, ArchiveWalkLimitError, MAX_WALK_DEPTH, MAX_WALK_TOTAL_BYTES, WalkArchiveOptions, walkArchive } from "./zip/walk.cjs";
8
- export { ArchiveFormat, ArchiveWalkEntry, ArchiveWalkLimit, ArchiveWalkLimitError, CompoundFileFormatError, CompoundFileStream, CompoundFileWriteError, MAX_CFB_TOTAL_STREAM_BYTES, MAX_WALK_DEPTH, MAX_WALK_TOTAL_BYTES, OlePackage, OlePackageFormatError, ReadCompoundFileOptions, WalkArchiveOptions, WriteCompoundFileOptions, ZipEntry, detectArchiveFormat, isCompoundFile, isZipArchive, readCompoundFile, readOlePackage, unzipPackage, walkArchive, writeCompoundFile, zipPackage };
13
+ export { ArchiveFormat, ArchiveWalkEntry, ArchiveWalkLimit, ArchiveWalkLimitError, CompoundFileFormatError, CompoundFileStream, CompoundFileWriteError, FMTID_SUMMARY_INFORMATION, MAX_CFB_TOTAL_STREAM_BYTES, MAX_WALK_DEPTH, MAX_WALK_TOTAL_BYTES, OlePackage, OlePackageFormatError, type PropertySet, PropertySetFormatError, PropertySetWriteError, type PropertyValue, ReadCompoundFileOptions, SummaryInformationProperties, WalkArchiveOptions, WriteCompoundFileOptions, ZipEntry, detectArchiveFormat, hasSummaryInformationFields, isCompoundFile, isZipArchive, layoutMetadataToSummaryInformation, readCompoundFile, readOlePackage, readPropertySetStream, readSummaryInformation, summaryInformationToLayoutMetadata, unzipPackage, walkArchive, writeCompoundFile, writePropertySetStream, writeSummaryInformationStream, zipPackage };
package/dist/index.d.ts CHANGED
@@ -2,7 +2,12 @@ import { isCompoundFile } from "./cfb/detect.js";
2
2
  import { OlePackage, OlePackageFormatError, readOlePackage } from "./cfb/ole-package.js";
3
3
  import { CompoundFileFormatError, CompoundFileStream, MAX_CFB_TOTAL_STREAM_BYTES, ReadCompoundFileOptions, readCompoundFile } from "./cfb/read.js";
4
4
  import { CompoundFileWriteError, WriteCompoundFileOptions, writeCompoundFile } from "./cfb/write.js";
5
+ import { FMTID_SUMMARY_INFORMATION, SummaryInformationProperties, readSummaryInformation, writeSummaryInformationStream } from "./oleps/summary-information.js";
6
+ import { hasSummaryInformationFields, layoutMetadataToSummaryInformation, summaryInformationToLayoutMetadata } from "./oleps/layout-metadata.js";
7
+ import { PropertySet, PropertyValue } from "./oleps/wire.js";
8
+ import { PropertySetFormatError, readPropertySetStream } from "./oleps/read.js";
9
+ import { PropertySetWriteError, writePropertySetStream } from "./oleps/write.js";
5
10
  import { ZipEntry, unzipPackage, zipPackage } from "./zip/container.js";
6
11
  import { ArchiveFormat, detectArchiveFormat, isZipArchive } from "./zip/detect.js";
7
12
  import { ArchiveWalkEntry, ArchiveWalkLimit, ArchiveWalkLimitError, MAX_WALK_DEPTH, MAX_WALK_TOTAL_BYTES, WalkArchiveOptions, walkArchive } from "./zip/walk.js";
8
- export { ArchiveFormat, ArchiveWalkEntry, ArchiveWalkLimit, ArchiveWalkLimitError, CompoundFileFormatError, CompoundFileStream, CompoundFileWriteError, MAX_CFB_TOTAL_STREAM_BYTES, MAX_WALK_DEPTH, MAX_WALK_TOTAL_BYTES, OlePackage, OlePackageFormatError, ReadCompoundFileOptions, WalkArchiveOptions, WriteCompoundFileOptions, ZipEntry, detectArchiveFormat, isCompoundFile, isZipArchive, readCompoundFile, readOlePackage, unzipPackage, walkArchive, writeCompoundFile, zipPackage };
13
+ export { ArchiveFormat, ArchiveWalkEntry, ArchiveWalkLimit, ArchiveWalkLimitError, CompoundFileFormatError, CompoundFileStream, CompoundFileWriteError, FMTID_SUMMARY_INFORMATION, MAX_CFB_TOTAL_STREAM_BYTES, MAX_WALK_DEPTH, MAX_WALK_TOTAL_BYTES, OlePackage, OlePackageFormatError, type PropertySet, PropertySetFormatError, PropertySetWriteError, type PropertyValue, ReadCompoundFileOptions, SummaryInformationProperties, WalkArchiveOptions, WriteCompoundFileOptions, ZipEntry, detectArchiveFormat, hasSummaryInformationFields, isCompoundFile, isZipArchive, layoutMetadataToSummaryInformation, readCompoundFile, readOlePackage, readPropertySetStream, readSummaryInformation, summaryInformationToLayoutMetadata, unzipPackage, walkArchive, writeCompoundFile, writePropertySetStream, writeSummaryInformationStream, zipPackage };
package/dist/index.js CHANGED
@@ -2,7 +2,11 @@ import { isCompoundFile } from "./cfb/detect.js";
2
2
  import { OlePackageFormatError, readOlePackage } from "./cfb/ole-package.js";
3
3
  import { CompoundFileFormatError, MAX_CFB_TOTAL_STREAM_BYTES, readCompoundFile } from "./cfb/read.js";
4
4
  import { CompoundFileWriteError, writeCompoundFile } from "./cfb/write.js";
5
+ import { hasSummaryInformationFields, layoutMetadataToSummaryInformation, summaryInformationToLayoutMetadata } from "./oleps/layout-metadata.js";
6
+ import { PropertySetFormatError, readPropertySetStream } from "./oleps/read.js";
7
+ import { PropertySetWriteError, writePropertySetStream } from "./oleps/write.js";
8
+ import { FMTID_SUMMARY_INFORMATION, readSummaryInformation, writeSummaryInformationStream } from "./oleps/summary-information.js";
5
9
  import { unzipPackage, zipPackage } from "./zip/container.js";
6
10
  import { detectArchiveFormat, isZipArchive } from "./zip/detect.js";
7
11
  import { ArchiveWalkLimitError, MAX_WALK_DEPTH, MAX_WALK_TOTAL_BYTES, walkArchive } from "./zip/walk.js";
8
- export { ArchiveWalkLimitError, CompoundFileFormatError, CompoundFileWriteError, MAX_CFB_TOTAL_STREAM_BYTES, MAX_WALK_DEPTH, MAX_WALK_TOTAL_BYTES, OlePackageFormatError, detectArchiveFormat, isCompoundFile, isZipArchive, readCompoundFile, readOlePackage, unzipPackage, walkArchive, writeCompoundFile, zipPackage };
12
+ export { ArchiveWalkLimitError, CompoundFileFormatError, CompoundFileWriteError, FMTID_SUMMARY_INFORMATION, MAX_CFB_TOTAL_STREAM_BYTES, MAX_WALK_DEPTH, MAX_WALK_TOTAL_BYTES, OlePackageFormatError, PropertySetFormatError, PropertySetWriteError, detectArchiveFormat, hasSummaryInformationFields, isCompoundFile, isZipArchive, layoutMetadataToSummaryInformation, readCompoundFile, readOlePackage, readPropertySetStream, readSummaryInformation, summaryInformationToLayoutMetadata, unzipPackage, walkArchive, writeCompoundFile, writePropertySetStream, writeSummaryInformationStream, zipPackage };
@@ -0,0 +1,30 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/oleps/layout-metadata.ts
3
+ function summaryInformationToLayoutMetadata(info) {
4
+ return {
5
+ title: info.title,
6
+ subject: info.subject,
7
+ author: info.author,
8
+ keywords: info.keywords === void 0 ? void 0 : [...info.keywords],
9
+ createdIso: info.createdIso,
10
+ modifiedIso: info.lastSavedIso
11
+ };
12
+ }
13
+ function layoutMetadataToSummaryInformation(metadata) {
14
+ return {
15
+ title: metadata.title,
16
+ subject: metadata.subject,
17
+ author: metadata.author,
18
+ keywords: metadata.keywords,
19
+ createdIso: metadata.createdIso,
20
+ lastSavedIso: metadata.modifiedIso
21
+ };
22
+ }
23
+ /** Whether a LayoutMetadata carries anything SummaryInformation can actually represent -- `creator`/`producer`/`language` alone should not force a stream carrying nothing but the CodePage property into existence, since a reader would see that back as `{}` regardless (see each caller's own write-side entry point). */
24
+ function hasSummaryInformationFields(metadata) {
25
+ return metadata.title !== void 0 || metadata.subject !== void 0 || metadata.author !== void 0 || metadata.keywords !== void 0 && metadata.keywords.length > 0 || metadata.createdIso !== void 0 || metadata.modifiedIso !== void 0;
26
+ }
27
+ //#endregion
28
+ exports.hasSummaryInformationFields = hasSummaryInformationFields;
29
+ exports.layoutMetadataToSummaryInformation = layoutMetadataToSummaryInformation;
30
+ exports.summaryInformationToLayoutMetadata = summaryInformationToLayoutMetadata;
@@ -0,0 +1,9 @@
1
+ import { SummaryInformationProperties } from "./summary-information.cjs";
2
+ import { LayoutMetadata } from "document-schema.js";
3
+ //#region src/oleps/layout-metadata.d.ts
4
+ declare function summaryInformationToLayoutMetadata(info: SummaryInformationProperties): LayoutMetadata;
5
+ declare function layoutMetadataToSummaryInformation(metadata: LayoutMetadata): SummaryInformationProperties;
6
+ /** Whether a LayoutMetadata carries anything SummaryInformation can actually represent -- `creator`/`producer`/`language` alone should not force a stream carrying nothing but the CodePage property into existence, since a reader would see that back as `{}` regardless (see each caller's own write-side entry point). */
7
+ declare function hasSummaryInformationFields(metadata: LayoutMetadata): boolean;
8
+ //#endregion
9
+ export { hasSummaryInformationFields, layoutMetadataToSummaryInformation, summaryInformationToLayoutMetadata };
@@ -0,0 +1,9 @@
1
+ import { SummaryInformationProperties } from "./summary-information.js";
2
+ import { LayoutMetadata } from "document-schema.js";
3
+ //#region src/oleps/layout-metadata.d.ts
4
+ declare function summaryInformationToLayoutMetadata(info: SummaryInformationProperties): LayoutMetadata;
5
+ declare function layoutMetadataToSummaryInformation(metadata: LayoutMetadata): SummaryInformationProperties;
6
+ /** Whether a LayoutMetadata carries anything SummaryInformation can actually represent -- `creator`/`producer`/`language` alone should not force a stream carrying nothing but the CodePage property into existence, since a reader would see that back as `{}` regardless (see each caller's own write-side entry point). */
7
+ declare function hasSummaryInformationFields(metadata: LayoutMetadata): boolean;
8
+ //#endregion
9
+ export { hasSummaryInformationFields, layoutMetadataToSummaryInformation, summaryInformationToLayoutMetadata };
@@ -0,0 +1,27 @@
1
+ //#region src/oleps/layout-metadata.ts
2
+ function summaryInformationToLayoutMetadata(info) {
3
+ return {
4
+ title: info.title,
5
+ subject: info.subject,
6
+ author: info.author,
7
+ keywords: info.keywords === void 0 ? void 0 : [...info.keywords],
8
+ createdIso: info.createdIso,
9
+ modifiedIso: info.lastSavedIso
10
+ };
11
+ }
12
+ function layoutMetadataToSummaryInformation(metadata) {
13
+ return {
14
+ title: metadata.title,
15
+ subject: metadata.subject,
16
+ author: metadata.author,
17
+ keywords: metadata.keywords,
18
+ createdIso: metadata.createdIso,
19
+ lastSavedIso: metadata.modifiedIso
20
+ };
21
+ }
22
+ /** Whether a LayoutMetadata carries anything SummaryInformation can actually represent -- `creator`/`producer`/`language` alone should not force a stream carrying nothing but the CodePage property into existence, since a reader would see that back as `{}` regardless (see each caller's own write-side entry point). */
23
+ function hasSummaryInformationFields(metadata) {
24
+ return metadata.title !== void 0 || metadata.subject !== void 0 || metadata.author !== void 0 || metadata.keywords !== void 0 && metadata.keywords.length > 0 || metadata.createdIso !== void 0 || metadata.modifiedIso !== void 0;
25
+ }
26
+ //#endregion
27
+ export { hasSummaryInformationFields, layoutMetadataToSummaryInformation, summaryInformationToLayoutMetadata };
@@ -0,0 +1,132 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_oleps_wire = require("./wire.cjs");
3
+ //#region src/oleps/read.ts
4
+ var PropertySetFormatError = class extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = "PropertySetFormatError";
8
+ }
9
+ };
10
+ function requireBytes(byteLength, offset, length, what) {
11
+ if (offset < 0 || length < 0 || offset + length > byteLength) throw new PropertySetFormatError(`property set stream ends before ${what} (needs ${length} bytes at offset ${offset}, stream is ${byteLength} bytes)`);
12
+ }
13
+ const ANSI_DECODER = new TextDecoder("windows-1252");
14
+ const UTF16_DECODER = new TextDecoder("utf-16le");
15
+ function decodeAnsi(bytes, codepage) {
16
+ if (codepage !== 1252) return;
17
+ return ANSI_DECODER.decode(bytes);
18
+ }
19
+ function truncateAtNull(value) {
20
+ const index = value.indexOf("\0");
21
+ return index === -1 ? value : value.slice(0, index);
22
+ }
23
+ function readCodePageString(bytes, view, offset, codepage) {
24
+ requireBytes(bytes.length, offset, 4, "a CodePageString's Size field");
25
+ const size = view.getUint32(offset, true);
26
+ requireBytes(bytes.length, offset + 4, size, "a CodePageString's Characters field");
27
+ const raw = bytes.subarray(offset + 4, offset + 4 + size);
28
+ if (codepage === 1200) return truncateAtNull(UTF16_DECODER.decode(raw));
29
+ const decoded = decodeAnsi(raw, codepage);
30
+ return decoded === void 0 ? void 0 : truncateAtNull(decoded);
31
+ }
32
+ function readUnicodeString(bytes, view, offset) {
33
+ requireBytes(bytes.length, offset, 4, "a UnicodeString's Length field");
34
+ const byteLength = view.getUint32(offset, true) * 2;
35
+ requireBytes(bytes.length, offset + 4, byteLength, "a UnicodeString's Characters field");
36
+ const raw = bytes.subarray(offset + 4, offset + 4 + byteLength);
37
+ return truncateAtNull(UTF16_DECODER.decode(raw));
38
+ }
39
+ function readPropertySetStream(bytes) {
40
+ requireBytes(bytes.length, 0, 48, "the PropertySetStream header");
41
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
42
+ const byteOrder = view.getUint16(0, true);
43
+ if (byteOrder !== 65534) throw new PropertySetFormatError(`property set stream's ByteOrder field is 0x${byteOrder.toString(16)}, not the mandated 0xFFFE`);
44
+ const numPropertySets = view.getUint32(24, true);
45
+ if (numPropertySets !== 1) throw new PropertySetFormatError(`property set stream declares ${numPropertySets} property sets; this reader only handles the single-property-set form every "\\x05SummaryInformation" stream uses (the two-property-set DocumentSummaryInformation/UserDefinedProperties spelling is out of scope, see the package README)`);
46
+ const formatId = require_oleps_wire.readGuid(view, 28);
47
+ const offset0 = view.getUint32(44, true);
48
+ requireBytes(bytes.length, offset0, 8, "the PropertySet packet header");
49
+ const size = view.getUint32(offset0, true);
50
+ requireBytes(bytes.length, offset0, size, "the PropertySet packet's own declared Size");
51
+ const numProperties = view.getUint32(offset0 + 4, true);
52
+ const tableStart = offset0 + 8;
53
+ requireBytes(bytes.length, tableStart, numProperties * 8, "the PropertyIdentifierAndOffset dictionary");
54
+ const entries = [];
55
+ for (let i = 0; i < numProperties; i++) {
56
+ const entryOffset = tableStart + i * 8;
57
+ const pid = view.getUint32(entryOffset, true);
58
+ if (pid === 0) throw new PropertySetFormatError("property set carries a Dictionary property (PID 0), which names string-keyed properties this reader does not support -- no \"\\x05SummaryInformation\" stream should carry one");
59
+ entries.push({
60
+ pid,
61
+ relativeOffset: view.getUint32(entryOffset + 4, true)
62
+ });
63
+ }
64
+ let codepage = require_oleps_wire.WINDOWS_1252_CODEPAGE;
65
+ for (const entry of entries) {
66
+ if (entry.pid !== 1) continue;
67
+ const abs = offset0 + entry.relativeOffset;
68
+ requireBytes(bytes.length, abs, 8, "the CodePage property's TypedPropertyValue");
69
+ const type = view.getUint16(abs, true);
70
+ if (type !== 2) throw new PropertySetFormatError(`CodePage property (PID 1) has type 0x${type.toString(16)}, not VT_I2 as [MS-OLEPS] requires`);
71
+ const raw = view.getInt16(abs + 4, true);
72
+ codepage = raw < 0 ? raw + 65536 : raw;
73
+ }
74
+ const properties = /* @__PURE__ */ new Map();
75
+ for (const entry of entries) {
76
+ const abs = offset0 + entry.relativeOffset;
77
+ requireBytes(bytes.length, abs, 4, "a property's TypedPropertyValue header");
78
+ const type = view.getUint16(abs, true);
79
+ const padding = view.getUint16(abs + 2, true);
80
+ if (padding !== 0) throw new PropertySetFormatError(`property ${entry.pid}'s TypedPropertyValue padding is 0x${padding.toString(16)}, not zero as [MS-OLEPS] requires`);
81
+ const valueOffset = abs + 4;
82
+ switch (type) {
83
+ case 2:
84
+ requireBytes(bytes.length, valueOffset, 4, `property ${entry.pid}'s VT_I2 value`);
85
+ properties.set(entry.pid, {
86
+ type: "VT_I2",
87
+ value: view.getInt16(valueOffset, true)
88
+ });
89
+ break;
90
+ case 3:
91
+ requireBytes(bytes.length, valueOffset, 4, `property ${entry.pid}'s VT_I4 value`);
92
+ properties.set(entry.pid, {
93
+ type: "VT_I4",
94
+ value: view.getInt32(valueOffset, true)
95
+ });
96
+ break;
97
+ case 30: {
98
+ const value = readCodePageString(bytes, view, valueOffset, codepage);
99
+ if (value !== void 0) properties.set(entry.pid, {
100
+ type: "VT_LPSTR",
101
+ value
102
+ });
103
+ break;
104
+ }
105
+ case 31: {
106
+ const value = readUnicodeString(bytes, view, valueOffset);
107
+ properties.set(entry.pid, {
108
+ type: "VT_LPWSTR",
109
+ value
110
+ });
111
+ break;
112
+ }
113
+ case 64: {
114
+ requireBytes(bytes.length, valueOffset, 8, `property ${entry.pid}'s VT_FILETIME value`);
115
+ const low = view.getUint32(valueOffset, true);
116
+ const high = view.getUint32(valueOffset + 4, true);
117
+ properties.set(entry.pid, {
118
+ type: "VT_FILETIME",
119
+ value: require_oleps_wire.filetimeToDate(low, high)
120
+ });
121
+ break;
122
+ }
123
+ }
124
+ }
125
+ return {
126
+ formatId,
127
+ properties
128
+ };
129
+ }
130
+ //#endregion
131
+ exports.PropertySetFormatError = PropertySetFormatError;
132
+ exports.readPropertySetStream = readPropertySetStream;
@@ -0,0 +1,8 @@
1
+ import { PropertySet } from "./wire.cjs";
2
+ //#region src/oleps/read.d.ts
3
+ declare class PropertySetFormatError extends Error {
4
+ constructor(message: string);
5
+ }
6
+ declare function readPropertySetStream(bytes: Uint8Array<ArrayBuffer>): PropertySet;
7
+ //#endregion
8
+ export { PropertySetFormatError, readPropertySetStream };
@@ -0,0 +1,8 @@
1
+ import { PropertySet } from "./wire.js";
2
+ //#region src/oleps/read.d.ts
3
+ declare class PropertySetFormatError extends Error {
4
+ constructor(message: string);
5
+ }
6
+ declare function readPropertySetStream(bytes: Uint8Array<ArrayBuffer>): PropertySet;
7
+ //#endregion
8
+ export { PropertySetFormatError, readPropertySetStream };
@@ -0,0 +1,130 @@
1
+ import { WINDOWS_1252_CODEPAGE, filetimeToDate, readGuid } from "./wire.js";
2
+ //#region src/oleps/read.ts
3
+ var PropertySetFormatError = class extends Error {
4
+ constructor(message) {
5
+ super(message);
6
+ this.name = "PropertySetFormatError";
7
+ }
8
+ };
9
+ function requireBytes(byteLength, offset, length, what) {
10
+ if (offset < 0 || length < 0 || offset + length > byteLength) throw new PropertySetFormatError(`property set stream ends before ${what} (needs ${length} bytes at offset ${offset}, stream is ${byteLength} bytes)`);
11
+ }
12
+ const ANSI_DECODER = new TextDecoder("windows-1252");
13
+ const UTF16_DECODER = new TextDecoder("utf-16le");
14
+ function decodeAnsi(bytes, codepage) {
15
+ if (codepage !== 1252) return;
16
+ return ANSI_DECODER.decode(bytes);
17
+ }
18
+ function truncateAtNull(value) {
19
+ const index = value.indexOf("\0");
20
+ return index === -1 ? value : value.slice(0, index);
21
+ }
22
+ function readCodePageString(bytes, view, offset, codepage) {
23
+ requireBytes(bytes.length, offset, 4, "a CodePageString's Size field");
24
+ const size = view.getUint32(offset, true);
25
+ requireBytes(bytes.length, offset + 4, size, "a CodePageString's Characters field");
26
+ const raw = bytes.subarray(offset + 4, offset + 4 + size);
27
+ if (codepage === 1200) return truncateAtNull(UTF16_DECODER.decode(raw));
28
+ const decoded = decodeAnsi(raw, codepage);
29
+ return decoded === void 0 ? void 0 : truncateAtNull(decoded);
30
+ }
31
+ function readUnicodeString(bytes, view, offset) {
32
+ requireBytes(bytes.length, offset, 4, "a UnicodeString's Length field");
33
+ const byteLength = view.getUint32(offset, true) * 2;
34
+ requireBytes(bytes.length, offset + 4, byteLength, "a UnicodeString's Characters field");
35
+ const raw = bytes.subarray(offset + 4, offset + 4 + byteLength);
36
+ return truncateAtNull(UTF16_DECODER.decode(raw));
37
+ }
38
+ function readPropertySetStream(bytes) {
39
+ requireBytes(bytes.length, 0, 48, "the PropertySetStream header");
40
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
41
+ const byteOrder = view.getUint16(0, true);
42
+ if (byteOrder !== 65534) throw new PropertySetFormatError(`property set stream's ByteOrder field is 0x${byteOrder.toString(16)}, not the mandated 0xFFFE`);
43
+ const numPropertySets = view.getUint32(24, true);
44
+ if (numPropertySets !== 1) throw new PropertySetFormatError(`property set stream declares ${numPropertySets} property sets; this reader only handles the single-property-set form every "\\x05SummaryInformation" stream uses (the two-property-set DocumentSummaryInformation/UserDefinedProperties spelling is out of scope, see the package README)`);
45
+ const formatId = readGuid(view, 28);
46
+ const offset0 = view.getUint32(44, true);
47
+ requireBytes(bytes.length, offset0, 8, "the PropertySet packet header");
48
+ const size = view.getUint32(offset0, true);
49
+ requireBytes(bytes.length, offset0, size, "the PropertySet packet's own declared Size");
50
+ const numProperties = view.getUint32(offset0 + 4, true);
51
+ const tableStart = offset0 + 8;
52
+ requireBytes(bytes.length, tableStart, numProperties * 8, "the PropertyIdentifierAndOffset dictionary");
53
+ const entries = [];
54
+ for (let i = 0; i < numProperties; i++) {
55
+ const entryOffset = tableStart + i * 8;
56
+ const pid = view.getUint32(entryOffset, true);
57
+ if (pid === 0) throw new PropertySetFormatError("property set carries a Dictionary property (PID 0), which names string-keyed properties this reader does not support -- no \"\\x05SummaryInformation\" stream should carry one");
58
+ entries.push({
59
+ pid,
60
+ relativeOffset: view.getUint32(entryOffset + 4, true)
61
+ });
62
+ }
63
+ let codepage = WINDOWS_1252_CODEPAGE;
64
+ for (const entry of entries) {
65
+ if (entry.pid !== 1) continue;
66
+ const abs = offset0 + entry.relativeOffset;
67
+ requireBytes(bytes.length, abs, 8, "the CodePage property's TypedPropertyValue");
68
+ const type = view.getUint16(abs, true);
69
+ if (type !== 2) throw new PropertySetFormatError(`CodePage property (PID 1) has type 0x${type.toString(16)}, not VT_I2 as [MS-OLEPS] requires`);
70
+ const raw = view.getInt16(abs + 4, true);
71
+ codepage = raw < 0 ? raw + 65536 : raw;
72
+ }
73
+ const properties = /* @__PURE__ */ new Map();
74
+ for (const entry of entries) {
75
+ const abs = offset0 + entry.relativeOffset;
76
+ requireBytes(bytes.length, abs, 4, "a property's TypedPropertyValue header");
77
+ const type = view.getUint16(abs, true);
78
+ const padding = view.getUint16(abs + 2, true);
79
+ if (padding !== 0) throw new PropertySetFormatError(`property ${entry.pid}'s TypedPropertyValue padding is 0x${padding.toString(16)}, not zero as [MS-OLEPS] requires`);
80
+ const valueOffset = abs + 4;
81
+ switch (type) {
82
+ case 2:
83
+ requireBytes(bytes.length, valueOffset, 4, `property ${entry.pid}'s VT_I2 value`);
84
+ properties.set(entry.pid, {
85
+ type: "VT_I2",
86
+ value: view.getInt16(valueOffset, true)
87
+ });
88
+ break;
89
+ case 3:
90
+ requireBytes(bytes.length, valueOffset, 4, `property ${entry.pid}'s VT_I4 value`);
91
+ properties.set(entry.pid, {
92
+ type: "VT_I4",
93
+ value: view.getInt32(valueOffset, true)
94
+ });
95
+ break;
96
+ case 30: {
97
+ const value = readCodePageString(bytes, view, valueOffset, codepage);
98
+ if (value !== void 0) properties.set(entry.pid, {
99
+ type: "VT_LPSTR",
100
+ value
101
+ });
102
+ break;
103
+ }
104
+ case 31: {
105
+ const value = readUnicodeString(bytes, view, valueOffset);
106
+ properties.set(entry.pid, {
107
+ type: "VT_LPWSTR",
108
+ value
109
+ });
110
+ break;
111
+ }
112
+ case 64: {
113
+ requireBytes(bytes.length, valueOffset, 8, `property ${entry.pid}'s VT_FILETIME value`);
114
+ const low = view.getUint32(valueOffset, true);
115
+ const high = view.getUint32(valueOffset + 4, true);
116
+ properties.set(entry.pid, {
117
+ type: "VT_FILETIME",
118
+ value: filetimeToDate(low, high)
119
+ });
120
+ break;
121
+ }
122
+ }
123
+ }
124
+ return {
125
+ formatId,
126
+ properties
127
+ };
128
+ }
129
+ //#endregion
130
+ export { PropertySetFormatError, readPropertySetStream };
@@ -0,0 +1,94 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_oleps_wire = require("./wire.cjs");
3
+ const require_oleps_read = require("./read.cjs");
4
+ const require_oleps_write = require("./write.cjs");
5
+ //#region src/oleps/summary-information.ts
6
+ const FMTID_SUMMARY_INFORMATION = "{F29F85E0-4FF9-1068-AB91-08002B27B3D9}";
7
+ const PID_TITLE = 2;
8
+ const PID_SUBJECT = 3;
9
+ const PID_AUTHOR = 4;
10
+ const PID_KEYWORDS = 5;
11
+ const PID_COMMENTS = 6;
12
+ const PID_LASTPRINTED = 11;
13
+ const PID_CREATE_DTM = 12;
14
+ const PID_LASTSAVE_DTM = 13;
15
+ const KEYWORDS_DELIMITER = ", ";
16
+ function splitKeywords(value) {
17
+ const parts = value.split(",").map((part) => part.trim()).filter((part) => part.length > 0);
18
+ return parts.length > 0 ? parts : void 0;
19
+ }
20
+ function stringValue(value, pid) {
21
+ if (value === void 0) return void 0;
22
+ if (value.type !== "VT_LPSTR" && value.type !== "VT_LPWSTR") throw new require_oleps_read.PropertySetFormatError(`SummaryInformation property ${pid} has type ${value.type}, not a string type as [MS-OLEPS]'s SummaryInformation Property Set defines`);
23
+ return value.value.length > 0 ? value.value : void 0;
24
+ }
25
+ const FILETIME_UNSET_ISO = "1601-01-01T00:00:00.000Z";
26
+ function dateIsoValue(value, pid) {
27
+ if (value === void 0) return void 0;
28
+ if (value.type !== "VT_FILETIME") throw new require_oleps_read.PropertySetFormatError(`SummaryInformation property ${pid} has type ${value.type}, not VT_FILETIME as [MS-OLEPS]'s SummaryInformation Property Set defines`);
29
+ const iso = value.value.toISOString();
30
+ return iso === FILETIME_UNSET_ISO ? void 0 : iso;
31
+ }
32
+ function readSummaryInformation(bytes) {
33
+ const propertySet = require_oleps_read.readPropertySetStream(bytes);
34
+ if (propertySet.formatId !== "{F29F85E0-4FF9-1068-AB91-08002B27B3D9}") throw new require_oleps_read.PropertySetFormatError(`property set stream declares FMTID ${propertySet.formatId}, not FMTID_SummaryInformation (${FMTID_SUMMARY_INFORMATION}); this is not a "\\x05SummaryInformation" stream`);
35
+ const { properties } = propertySet;
36
+ const keywords = stringValue(properties.get(PID_KEYWORDS), PID_KEYWORDS);
37
+ return {
38
+ title: stringValue(properties.get(PID_TITLE), PID_TITLE),
39
+ subject: stringValue(properties.get(PID_SUBJECT), PID_SUBJECT),
40
+ author: stringValue(properties.get(PID_AUTHOR), PID_AUTHOR),
41
+ keywords: keywords === void 0 ? void 0 : splitKeywords(keywords),
42
+ comments: stringValue(properties.get(PID_COMMENTS), PID_COMMENTS),
43
+ createdIso: dateIsoValue(properties.get(PID_CREATE_DTM), PID_CREATE_DTM),
44
+ lastSavedIso: dateIsoValue(properties.get(PID_LASTSAVE_DTM), PID_LASTSAVE_DTM),
45
+ lastPrintedIso: dateIsoValue(properties.get(PID_LASTPRINTED), PID_LASTPRINTED)
46
+ };
47
+ }
48
+ function writeSummaryInformationStream(properties) {
49
+ const entries = /* @__PURE__ */ new Map();
50
+ entries.set(1, {
51
+ type: "VT_I2",
52
+ value: require_oleps_wire.CP_WINUNICODE
53
+ });
54
+ if (properties.title !== void 0) entries.set(PID_TITLE, {
55
+ type: "VT_LPWSTR",
56
+ value: properties.title
57
+ });
58
+ if (properties.subject !== void 0) entries.set(PID_SUBJECT, {
59
+ type: "VT_LPWSTR",
60
+ value: properties.subject
61
+ });
62
+ if (properties.author !== void 0) entries.set(PID_AUTHOR, {
63
+ type: "VT_LPWSTR",
64
+ value: properties.author
65
+ });
66
+ if (properties.keywords !== void 0 && properties.keywords.length > 0) entries.set(PID_KEYWORDS, {
67
+ type: "VT_LPWSTR",
68
+ value: properties.keywords.join(KEYWORDS_DELIMITER)
69
+ });
70
+ if (properties.comments !== void 0) entries.set(PID_COMMENTS, {
71
+ type: "VT_LPWSTR",
72
+ value: properties.comments
73
+ });
74
+ if (properties.createdIso !== void 0) entries.set(PID_CREATE_DTM, {
75
+ type: "VT_FILETIME",
76
+ value: new Date(properties.createdIso)
77
+ });
78
+ if (properties.lastSavedIso !== void 0) entries.set(PID_LASTSAVE_DTM, {
79
+ type: "VT_FILETIME",
80
+ value: new Date(properties.lastSavedIso)
81
+ });
82
+ if (properties.lastPrintedIso !== void 0) entries.set(PID_LASTPRINTED, {
83
+ type: "VT_FILETIME",
84
+ value: new Date(properties.lastPrintedIso)
85
+ });
86
+ return require_oleps_write.writePropertySetStream({
87
+ formatId: FMTID_SUMMARY_INFORMATION,
88
+ properties: entries
89
+ });
90
+ }
91
+ //#endregion
92
+ exports.FMTID_SUMMARY_INFORMATION = FMTID_SUMMARY_INFORMATION;
93
+ exports.readSummaryInformation = readSummaryInformation;
94
+ exports.writeSummaryInformationStream = writeSummaryInformationStream;
@@ -0,0 +1,16 @@
1
+ //#region src/oleps/summary-information.d.ts
2
+ declare const FMTID_SUMMARY_INFORMATION = "{F29F85E0-4FF9-1068-AB91-08002B27B3D9}";
3
+ interface SummaryInformationProperties {
4
+ readonly title?: string;
5
+ readonly subject?: string;
6
+ readonly author?: string;
7
+ readonly keywords?: readonly string[];
8
+ readonly comments?: string;
9
+ readonly createdIso?: string;
10
+ readonly lastSavedIso?: string;
11
+ readonly lastPrintedIso?: string;
12
+ }
13
+ declare function readSummaryInformation(bytes: Uint8Array<ArrayBuffer>): SummaryInformationProperties;
14
+ declare function writeSummaryInformationStream(properties: SummaryInformationProperties): Uint8Array<ArrayBuffer>;
15
+ //#endregion
16
+ export { FMTID_SUMMARY_INFORMATION, SummaryInformationProperties, readSummaryInformation, writeSummaryInformationStream };
@@ -0,0 +1,16 @@
1
+ //#region src/oleps/summary-information.d.ts
2
+ declare const FMTID_SUMMARY_INFORMATION = "{F29F85E0-4FF9-1068-AB91-08002B27B3D9}";
3
+ interface SummaryInformationProperties {
4
+ readonly title?: string;
5
+ readonly subject?: string;
6
+ readonly author?: string;
7
+ readonly keywords?: readonly string[];
8
+ readonly comments?: string;
9
+ readonly createdIso?: string;
10
+ readonly lastSavedIso?: string;
11
+ readonly lastPrintedIso?: string;
12
+ }
13
+ declare function readSummaryInformation(bytes: Uint8Array<ArrayBuffer>): SummaryInformationProperties;
14
+ declare function writeSummaryInformationStream(properties: SummaryInformationProperties): Uint8Array<ArrayBuffer>;
15
+ //#endregion
16
+ export { FMTID_SUMMARY_INFORMATION, SummaryInformationProperties, readSummaryInformation, writeSummaryInformationStream };
@@ -0,0 +1,91 @@
1
+ import { CP_WINUNICODE } from "./wire.js";
2
+ import { PropertySetFormatError, readPropertySetStream } from "./read.js";
3
+ import { writePropertySetStream } from "./write.js";
4
+ //#region src/oleps/summary-information.ts
5
+ const FMTID_SUMMARY_INFORMATION = "{F29F85E0-4FF9-1068-AB91-08002B27B3D9}";
6
+ const PID_TITLE = 2;
7
+ const PID_SUBJECT = 3;
8
+ const PID_AUTHOR = 4;
9
+ const PID_KEYWORDS = 5;
10
+ const PID_COMMENTS = 6;
11
+ const PID_LASTPRINTED = 11;
12
+ const PID_CREATE_DTM = 12;
13
+ const PID_LASTSAVE_DTM = 13;
14
+ const KEYWORDS_DELIMITER = ", ";
15
+ function splitKeywords(value) {
16
+ const parts = value.split(",").map((part) => part.trim()).filter((part) => part.length > 0);
17
+ return parts.length > 0 ? parts : void 0;
18
+ }
19
+ function stringValue(value, pid) {
20
+ if (value === void 0) return void 0;
21
+ if (value.type !== "VT_LPSTR" && value.type !== "VT_LPWSTR") throw new PropertySetFormatError(`SummaryInformation property ${pid} has type ${value.type}, not a string type as [MS-OLEPS]'s SummaryInformation Property Set defines`);
22
+ return value.value.length > 0 ? value.value : void 0;
23
+ }
24
+ const FILETIME_UNSET_ISO = "1601-01-01T00:00:00.000Z";
25
+ function dateIsoValue(value, pid) {
26
+ if (value === void 0) return void 0;
27
+ if (value.type !== "VT_FILETIME") throw new PropertySetFormatError(`SummaryInformation property ${pid} has type ${value.type}, not VT_FILETIME as [MS-OLEPS]'s SummaryInformation Property Set defines`);
28
+ const iso = value.value.toISOString();
29
+ return iso === FILETIME_UNSET_ISO ? void 0 : iso;
30
+ }
31
+ function readSummaryInformation(bytes) {
32
+ const propertySet = readPropertySetStream(bytes);
33
+ if (propertySet.formatId !== "{F29F85E0-4FF9-1068-AB91-08002B27B3D9}") throw new PropertySetFormatError(`property set stream declares FMTID ${propertySet.formatId}, not FMTID_SummaryInformation (${FMTID_SUMMARY_INFORMATION}); this is not a "\\x05SummaryInformation" stream`);
34
+ const { properties } = propertySet;
35
+ const keywords = stringValue(properties.get(PID_KEYWORDS), PID_KEYWORDS);
36
+ return {
37
+ title: stringValue(properties.get(PID_TITLE), PID_TITLE),
38
+ subject: stringValue(properties.get(PID_SUBJECT), PID_SUBJECT),
39
+ author: stringValue(properties.get(PID_AUTHOR), PID_AUTHOR),
40
+ keywords: keywords === void 0 ? void 0 : splitKeywords(keywords),
41
+ comments: stringValue(properties.get(PID_COMMENTS), PID_COMMENTS),
42
+ createdIso: dateIsoValue(properties.get(PID_CREATE_DTM), PID_CREATE_DTM),
43
+ lastSavedIso: dateIsoValue(properties.get(PID_LASTSAVE_DTM), PID_LASTSAVE_DTM),
44
+ lastPrintedIso: dateIsoValue(properties.get(PID_LASTPRINTED), PID_LASTPRINTED)
45
+ };
46
+ }
47
+ function writeSummaryInformationStream(properties) {
48
+ const entries = /* @__PURE__ */ new Map();
49
+ entries.set(1, {
50
+ type: "VT_I2",
51
+ value: CP_WINUNICODE
52
+ });
53
+ if (properties.title !== void 0) entries.set(PID_TITLE, {
54
+ type: "VT_LPWSTR",
55
+ value: properties.title
56
+ });
57
+ if (properties.subject !== void 0) entries.set(PID_SUBJECT, {
58
+ type: "VT_LPWSTR",
59
+ value: properties.subject
60
+ });
61
+ if (properties.author !== void 0) entries.set(PID_AUTHOR, {
62
+ type: "VT_LPWSTR",
63
+ value: properties.author
64
+ });
65
+ if (properties.keywords !== void 0 && properties.keywords.length > 0) entries.set(PID_KEYWORDS, {
66
+ type: "VT_LPWSTR",
67
+ value: properties.keywords.join(KEYWORDS_DELIMITER)
68
+ });
69
+ if (properties.comments !== void 0) entries.set(PID_COMMENTS, {
70
+ type: "VT_LPWSTR",
71
+ value: properties.comments
72
+ });
73
+ if (properties.createdIso !== void 0) entries.set(PID_CREATE_DTM, {
74
+ type: "VT_FILETIME",
75
+ value: new Date(properties.createdIso)
76
+ });
77
+ if (properties.lastSavedIso !== void 0) entries.set(PID_LASTSAVE_DTM, {
78
+ type: "VT_FILETIME",
79
+ value: new Date(properties.lastSavedIso)
80
+ });
81
+ if (properties.lastPrintedIso !== void 0) entries.set(PID_LASTPRINTED, {
82
+ type: "VT_FILETIME",
83
+ value: new Date(properties.lastPrintedIso)
84
+ });
85
+ return writePropertySetStream({
86
+ formatId: FMTID_SUMMARY_INFORMATION,
87
+ properties: entries
88
+ });
89
+ }
90
+ //#endregion
91
+ export { FMTID_SUMMARY_INFORMATION, readSummaryInformation, writeSummaryInformationStream };
@@ -0,0 +1,70 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/oleps/wire.ts
3
+ const BYTE_ORDER_MARK = 65534;
4
+ const HEADER_SIZE = 48;
5
+ const PROPERTY_SET_HEADER_SIZE = 8;
6
+ const IDENTIFIER_AND_OFFSET_SIZE = 8;
7
+ const TYPED_VALUE_HEADER_SIZE = 4;
8
+ const VT_I2 = 2;
9
+ const VT_I4 = 3;
10
+ const VT_LPSTR = 30;
11
+ const VT_LPWSTR = 31;
12
+ const VT_FILETIME = 64;
13
+ const PID_DICTIONARY = 0;
14
+ const PID_CODEPAGE = 1;
15
+ const CP_WINUNICODE = 1200;
16
+ const WINDOWS_1252_CODEPAGE = 1252;
17
+ const GUID_NULL = "{00000000-0000-0000-0000-000000000000}";
18
+ function hex(n, width) {
19
+ return n.toString(16).padStart(width, "0");
20
+ }
21
+ function readGuid(view, offset) {
22
+ const data1 = hex(view.getUint32(offset, true), 8);
23
+ const data2 = hex(view.getUint16(offset + 4, true), 4);
24
+ const data3 = hex(view.getUint16(offset + 6, true), 4);
25
+ let data4a = "";
26
+ for (let i = 0; i < 2; i++) data4a += hex(view.getUint8(offset + 8 + i), 2);
27
+ let data4b = "";
28
+ for (let i = 2; i < 8; i++) data4b += hex(view.getUint8(offset + 8 + i), 2);
29
+ return `{${data1}-${data2}-${data3}-${data4a}-${data4b}}`.toUpperCase();
30
+ }
31
+ function writeGuid(view, offset, guid) {
32
+ const digits = guid.replace(/[{}-]/g, "");
33
+ view.setUint32(offset, Number.parseInt(digits.slice(0, 8), 16), true);
34
+ view.setUint16(offset + 4, Number.parseInt(digits.slice(8, 12), 16), true);
35
+ view.setUint16(offset + 6, Number.parseInt(digits.slice(12, 16), 16), true);
36
+ for (let i = 0; i < 8; i++) view.setUint8(offset + 8 + i, Number.parseInt(digits.slice(16 + i * 2, 18 + i * 2), 16));
37
+ }
38
+ const FILETIME_EPOCH_OFFSET_100NS = 116444736000000000n;
39
+ const HUNDRED_NS_PER_MS = 10000n;
40
+ function filetimeToDate(low, high) {
41
+ const ms = ((BigInt(high) << 32n | BigInt(low)) - FILETIME_EPOCH_OFFSET_100NS) / HUNDRED_NS_PER_MS;
42
+ return new Date(Number(ms));
43
+ }
44
+ function dateToFiletime(date) {
45
+ const ticks = BigInt(date.getTime()) * HUNDRED_NS_PER_MS + FILETIME_EPOCH_OFFSET_100NS;
46
+ return {
47
+ low: Number(ticks & 4294967295n),
48
+ high: Number(ticks >> 32n & 4294967295n)
49
+ };
50
+ }
51
+ //#endregion
52
+ exports.BYTE_ORDER_MARK = BYTE_ORDER_MARK;
53
+ exports.CP_WINUNICODE = CP_WINUNICODE;
54
+ exports.GUID_NULL = GUID_NULL;
55
+ exports.HEADER_SIZE = HEADER_SIZE;
56
+ exports.IDENTIFIER_AND_OFFSET_SIZE = IDENTIFIER_AND_OFFSET_SIZE;
57
+ exports.PID_CODEPAGE = PID_CODEPAGE;
58
+ exports.PID_DICTIONARY = PID_DICTIONARY;
59
+ exports.PROPERTY_SET_HEADER_SIZE = PROPERTY_SET_HEADER_SIZE;
60
+ exports.TYPED_VALUE_HEADER_SIZE = TYPED_VALUE_HEADER_SIZE;
61
+ exports.VT_FILETIME = VT_FILETIME;
62
+ exports.VT_I2 = VT_I2;
63
+ exports.VT_I4 = VT_I4;
64
+ exports.VT_LPSTR = VT_LPSTR;
65
+ exports.VT_LPWSTR = VT_LPWSTR;
66
+ exports.WINDOWS_1252_CODEPAGE = WINDOWS_1252_CODEPAGE;
67
+ exports.dateToFiletime = dateToFiletime;
68
+ exports.filetimeToDate = filetimeToDate;
69
+ exports.readGuid = readGuid;
70
+ exports.writeGuid = writeGuid;
@@ -0,0 +1,45 @@
1
+ //#region src/oleps/wire.d.ts
2
+ declare const BYTE_ORDER_MARK = 65534;
3
+ declare const HEADER_SIZE = 48;
4
+ declare const PROPERTY_SET_HEADER_SIZE = 8;
5
+ declare const IDENTIFIER_AND_OFFSET_SIZE = 8;
6
+ declare const TYPED_VALUE_HEADER_SIZE = 4;
7
+ declare const VT_I2 = 2;
8
+ declare const VT_I4 = 3;
9
+ declare const VT_LPSTR = 30;
10
+ declare const VT_LPWSTR = 31;
11
+ declare const VT_FILETIME = 64;
12
+ declare const PID_DICTIONARY = 0;
13
+ declare const PID_CODEPAGE = 1;
14
+ declare const CP_WINUNICODE = 1200;
15
+ declare const WINDOWS_1252_CODEPAGE = 1252;
16
+ declare const GUID_NULL = "{00000000-0000-0000-0000-000000000000}";
17
+ declare function readGuid(view: DataView, offset: number): string;
18
+ declare function writeGuid(view: DataView, offset: number, guid: string): void;
19
+ declare function filetimeToDate(low: number, high: number): Date;
20
+ declare function dateToFiletime(date: Date): {
21
+ readonly low: number;
22
+ readonly high: number;
23
+ };
24
+ type PropertyValue = {
25
+ readonly type: "VT_I2";
26
+ readonly value: number;
27
+ } | {
28
+ readonly type: "VT_I4";
29
+ readonly value: number;
30
+ } | {
31
+ readonly type: "VT_LPSTR";
32
+ readonly value: string;
33
+ } | {
34
+ readonly type: "VT_LPWSTR";
35
+ readonly value: string;
36
+ } | {
37
+ readonly type: "VT_FILETIME";
38
+ readonly value: Date;
39
+ };
40
+ interface PropertySet {
41
+ readonly formatId: string;
42
+ readonly properties: ReadonlyMap<number, PropertyValue>;
43
+ }
44
+ //#endregion
45
+ export { BYTE_ORDER_MARK, CP_WINUNICODE, GUID_NULL, HEADER_SIZE, IDENTIFIER_AND_OFFSET_SIZE, PID_CODEPAGE, PID_DICTIONARY, PROPERTY_SET_HEADER_SIZE, PropertySet, PropertyValue, TYPED_VALUE_HEADER_SIZE, VT_FILETIME, VT_I2, VT_I4, VT_LPSTR, VT_LPWSTR, WINDOWS_1252_CODEPAGE, dateToFiletime, filetimeToDate, readGuid, writeGuid };
@@ -0,0 +1,45 @@
1
+ //#region src/oleps/wire.d.ts
2
+ declare const BYTE_ORDER_MARK = 65534;
3
+ declare const HEADER_SIZE = 48;
4
+ declare const PROPERTY_SET_HEADER_SIZE = 8;
5
+ declare const IDENTIFIER_AND_OFFSET_SIZE = 8;
6
+ declare const TYPED_VALUE_HEADER_SIZE = 4;
7
+ declare const VT_I2 = 2;
8
+ declare const VT_I4 = 3;
9
+ declare const VT_LPSTR = 30;
10
+ declare const VT_LPWSTR = 31;
11
+ declare const VT_FILETIME = 64;
12
+ declare const PID_DICTIONARY = 0;
13
+ declare const PID_CODEPAGE = 1;
14
+ declare const CP_WINUNICODE = 1200;
15
+ declare const WINDOWS_1252_CODEPAGE = 1252;
16
+ declare const GUID_NULL = "{00000000-0000-0000-0000-000000000000}";
17
+ declare function readGuid(view: DataView, offset: number): string;
18
+ declare function writeGuid(view: DataView, offset: number, guid: string): void;
19
+ declare function filetimeToDate(low: number, high: number): Date;
20
+ declare function dateToFiletime(date: Date): {
21
+ readonly low: number;
22
+ readonly high: number;
23
+ };
24
+ type PropertyValue = {
25
+ readonly type: "VT_I2";
26
+ readonly value: number;
27
+ } | {
28
+ readonly type: "VT_I4";
29
+ readonly value: number;
30
+ } | {
31
+ readonly type: "VT_LPSTR";
32
+ readonly value: string;
33
+ } | {
34
+ readonly type: "VT_LPWSTR";
35
+ readonly value: string;
36
+ } | {
37
+ readonly type: "VT_FILETIME";
38
+ readonly value: Date;
39
+ };
40
+ interface PropertySet {
41
+ readonly formatId: string;
42
+ readonly properties: ReadonlyMap<number, PropertyValue>;
43
+ }
44
+ //#endregion
45
+ export { BYTE_ORDER_MARK, CP_WINUNICODE, GUID_NULL, HEADER_SIZE, IDENTIFIER_AND_OFFSET_SIZE, PID_CODEPAGE, PID_DICTIONARY, PROPERTY_SET_HEADER_SIZE, PropertySet, PropertyValue, TYPED_VALUE_HEADER_SIZE, VT_FILETIME, VT_I2, VT_I4, VT_LPSTR, VT_LPWSTR, WINDOWS_1252_CODEPAGE, dateToFiletime, filetimeToDate, readGuid, writeGuid };
@@ -0,0 +1,51 @@
1
+ //#region src/oleps/wire.ts
2
+ const BYTE_ORDER_MARK = 65534;
3
+ const HEADER_SIZE = 48;
4
+ const PROPERTY_SET_HEADER_SIZE = 8;
5
+ const IDENTIFIER_AND_OFFSET_SIZE = 8;
6
+ const TYPED_VALUE_HEADER_SIZE = 4;
7
+ const VT_I2 = 2;
8
+ const VT_I4 = 3;
9
+ const VT_LPSTR = 30;
10
+ const VT_LPWSTR = 31;
11
+ const VT_FILETIME = 64;
12
+ const PID_DICTIONARY = 0;
13
+ const PID_CODEPAGE = 1;
14
+ const CP_WINUNICODE = 1200;
15
+ const WINDOWS_1252_CODEPAGE = 1252;
16
+ const GUID_NULL = "{00000000-0000-0000-0000-000000000000}";
17
+ function hex(n, width) {
18
+ return n.toString(16).padStart(width, "0");
19
+ }
20
+ function readGuid(view, offset) {
21
+ const data1 = hex(view.getUint32(offset, true), 8);
22
+ const data2 = hex(view.getUint16(offset + 4, true), 4);
23
+ const data3 = hex(view.getUint16(offset + 6, true), 4);
24
+ let data4a = "";
25
+ for (let i = 0; i < 2; i++) data4a += hex(view.getUint8(offset + 8 + i), 2);
26
+ let data4b = "";
27
+ for (let i = 2; i < 8; i++) data4b += hex(view.getUint8(offset + 8 + i), 2);
28
+ return `{${data1}-${data2}-${data3}-${data4a}-${data4b}}`.toUpperCase();
29
+ }
30
+ function writeGuid(view, offset, guid) {
31
+ const digits = guid.replace(/[{}-]/g, "");
32
+ view.setUint32(offset, Number.parseInt(digits.slice(0, 8), 16), true);
33
+ view.setUint16(offset + 4, Number.parseInt(digits.slice(8, 12), 16), true);
34
+ view.setUint16(offset + 6, Number.parseInt(digits.slice(12, 16), 16), true);
35
+ for (let i = 0; i < 8; i++) view.setUint8(offset + 8 + i, Number.parseInt(digits.slice(16 + i * 2, 18 + i * 2), 16));
36
+ }
37
+ const FILETIME_EPOCH_OFFSET_100NS = 116444736000000000n;
38
+ const HUNDRED_NS_PER_MS = 10000n;
39
+ function filetimeToDate(low, high) {
40
+ const ms = ((BigInt(high) << 32n | BigInt(low)) - FILETIME_EPOCH_OFFSET_100NS) / HUNDRED_NS_PER_MS;
41
+ return new Date(Number(ms));
42
+ }
43
+ function dateToFiletime(date) {
44
+ const ticks = BigInt(date.getTime()) * HUNDRED_NS_PER_MS + FILETIME_EPOCH_OFFSET_100NS;
45
+ return {
46
+ low: Number(ticks & 4294967295n),
47
+ high: Number(ticks >> 32n & 4294967295n)
48
+ };
49
+ }
50
+ //#endregion
51
+ export { BYTE_ORDER_MARK, CP_WINUNICODE, GUID_NULL, HEADER_SIZE, IDENTIFIER_AND_OFFSET_SIZE, PID_CODEPAGE, PID_DICTIONARY, PROPERTY_SET_HEADER_SIZE, TYPED_VALUE_HEADER_SIZE, VT_FILETIME, VT_I2, VT_I4, VT_LPSTR, VT_LPWSTR, WINDOWS_1252_CODEPAGE, dateToFiletime, filetimeToDate, readGuid, writeGuid };
@@ -0,0 +1,103 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_oleps_wire = require("./wire.cjs");
3
+ //#region src/oleps/write.ts
4
+ var PropertySetWriteError = class extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = "PropertySetWriteError";
8
+ }
9
+ };
10
+ function encodeUnicodeStringValue(value) {
11
+ const characterBytes = new Uint8Array((value.length + 1) * 2);
12
+ const charView = new DataView(characterBytes.buffer);
13
+ for (let i = 0; i < value.length; i++) charView.setUint16(i * 2, value.charCodeAt(i), true);
14
+ charView.setUint16(value.length * 2, 0, true);
15
+ return characterBytes;
16
+ }
17
+ function padTo4(length) {
18
+ return Math.ceil(length / 4) * 4;
19
+ }
20
+ function encodeTypedPropertyValue(value) {
21
+ switch (value.type) {
22
+ case "VT_I2": {
23
+ const bytes = /* @__PURE__ */ new Uint8Array(8);
24
+ const view = new DataView(bytes.buffer);
25
+ view.setUint16(0, 2, true);
26
+ view.setUint16(2, 0, true);
27
+ view.setInt16(4, value.value, true);
28
+ view.setUint16(6, 0, true);
29
+ return bytes;
30
+ }
31
+ case "VT_I4": {
32
+ const bytes = /* @__PURE__ */ new Uint8Array(8);
33
+ const view = new DataView(bytes.buffer);
34
+ view.setUint16(0, 3, true);
35
+ view.setUint16(2, 0, true);
36
+ view.setInt32(4, value.value, true);
37
+ return bytes;
38
+ }
39
+ case "VT_FILETIME": {
40
+ const { low, high } = require_oleps_wire.dateToFiletime(value.value);
41
+ const bytes = /* @__PURE__ */ new Uint8Array(12);
42
+ const view = new DataView(bytes.buffer);
43
+ view.setUint16(0, 64, true);
44
+ view.setUint16(2, 0, true);
45
+ view.setUint32(4, low, true);
46
+ view.setUint32(8, high, true);
47
+ return bytes;
48
+ }
49
+ case "VT_LPWSTR": {
50
+ const characters = encodeUnicodeStringValue(value.value);
51
+ const paddedLength = padTo4(characters.length);
52
+ const bytes = new Uint8Array(8 + paddedLength);
53
+ const view = new DataView(bytes.buffer);
54
+ view.setUint16(0, 31, true);
55
+ view.setUint16(2, 0, true);
56
+ view.setUint32(4, characters.length / 2, true);
57
+ bytes.set(characters, 8);
58
+ return bytes;
59
+ }
60
+ case "VT_LPSTR": throw new PropertySetWriteError("writePropertySetStream cannot write a VT_LPSTR property: this writer emits Unicode (VT_LPWSTR) strings only, since encoding to an arbitrary ANSI codepage is out of scope -- see the package README's OLEPS scope note");
61
+ }
62
+ }
63
+ function writePropertySetStream(propertySet) {
64
+ const entries = [...propertySet.properties.entries()].sort(([a], [b]) => a - b);
65
+ const dictionaryBytes = new Uint8Array(entries.length * 8);
66
+ const dictionaryView = new DataView(dictionaryBytes.buffer);
67
+ const valueChunks = [];
68
+ let valueOffset = 8 + dictionaryBytes.length;
69
+ let index = 0;
70
+ for (const [pid, value] of entries) {
71
+ const encoded = encodeTypedPropertyValue(value);
72
+ dictionaryView.setUint32(index * 8, pid, true);
73
+ dictionaryView.setUint32(index * 8 + 4, valueOffset, true);
74
+ valueChunks.push(encoded);
75
+ valueOffset += encoded.length;
76
+ index += 1;
77
+ }
78
+ const propertySetSize = valueOffset;
79
+ const propertySetBytes = new Uint8Array(propertySetSize);
80
+ const propertySetView = new DataView(propertySetBytes.buffer);
81
+ propertySetView.setUint32(0, propertySetSize, true);
82
+ propertySetView.setUint32(4, entries.length, true);
83
+ propertySetBytes.set(dictionaryBytes, 8);
84
+ let cursor = 8 + dictionaryBytes.length;
85
+ for (const chunk of valueChunks) {
86
+ propertySetBytes.set(chunk, cursor);
87
+ cursor += chunk.length;
88
+ }
89
+ const streamBytes = new Uint8Array(48 + propertySetBytes.length);
90
+ const view = new DataView(streamBytes.buffer);
91
+ view.setUint16(0, require_oleps_wire.BYTE_ORDER_MARK, true);
92
+ view.setUint16(2, 0, true);
93
+ view.setUint32(4, 0, true);
94
+ require_oleps_wire.writeGuid(view, 8, require_oleps_wire.GUID_NULL);
95
+ view.setUint32(24, 1, true);
96
+ require_oleps_wire.writeGuid(view, 28, propertySet.formatId);
97
+ view.setUint32(44, 48, true);
98
+ streamBytes.set(propertySetBytes, 48);
99
+ return streamBytes;
100
+ }
101
+ //#endregion
102
+ exports.PropertySetWriteError = PropertySetWriteError;
103
+ exports.writePropertySetStream = writePropertySetStream;
@@ -0,0 +1,8 @@
1
+ import { PropertySet } from "./wire.cjs";
2
+ //#region src/oleps/write.d.ts
3
+ declare class PropertySetWriteError extends Error {
4
+ constructor(message: string);
5
+ }
6
+ declare function writePropertySetStream(propertySet: PropertySet): Uint8Array<ArrayBuffer>;
7
+ //#endregion
8
+ export { PropertySetWriteError, writePropertySetStream };
@@ -0,0 +1,8 @@
1
+ import { PropertySet } from "./wire.js";
2
+ //#region src/oleps/write.d.ts
3
+ declare class PropertySetWriteError extends Error {
4
+ constructor(message: string);
5
+ }
6
+ declare function writePropertySetStream(propertySet: PropertySet): Uint8Array<ArrayBuffer>;
7
+ //#endregion
8
+ export { PropertySetWriteError, writePropertySetStream };
@@ -0,0 +1,101 @@
1
+ import { BYTE_ORDER_MARK, GUID_NULL, dateToFiletime, writeGuid } from "./wire.js";
2
+ //#region src/oleps/write.ts
3
+ var PropertySetWriteError = class extends Error {
4
+ constructor(message) {
5
+ super(message);
6
+ this.name = "PropertySetWriteError";
7
+ }
8
+ };
9
+ function encodeUnicodeStringValue(value) {
10
+ const characterBytes = new Uint8Array((value.length + 1) * 2);
11
+ const charView = new DataView(characterBytes.buffer);
12
+ for (let i = 0; i < value.length; i++) charView.setUint16(i * 2, value.charCodeAt(i), true);
13
+ charView.setUint16(value.length * 2, 0, true);
14
+ return characterBytes;
15
+ }
16
+ function padTo4(length) {
17
+ return Math.ceil(length / 4) * 4;
18
+ }
19
+ function encodeTypedPropertyValue(value) {
20
+ switch (value.type) {
21
+ case "VT_I2": {
22
+ const bytes = /* @__PURE__ */ new Uint8Array(8);
23
+ const view = new DataView(bytes.buffer);
24
+ view.setUint16(0, 2, true);
25
+ view.setUint16(2, 0, true);
26
+ view.setInt16(4, value.value, true);
27
+ view.setUint16(6, 0, true);
28
+ return bytes;
29
+ }
30
+ case "VT_I4": {
31
+ const bytes = /* @__PURE__ */ new Uint8Array(8);
32
+ const view = new DataView(bytes.buffer);
33
+ view.setUint16(0, 3, true);
34
+ view.setUint16(2, 0, true);
35
+ view.setInt32(4, value.value, true);
36
+ return bytes;
37
+ }
38
+ case "VT_FILETIME": {
39
+ const { low, high } = dateToFiletime(value.value);
40
+ const bytes = /* @__PURE__ */ new Uint8Array(12);
41
+ const view = new DataView(bytes.buffer);
42
+ view.setUint16(0, 64, true);
43
+ view.setUint16(2, 0, true);
44
+ view.setUint32(4, low, true);
45
+ view.setUint32(8, high, true);
46
+ return bytes;
47
+ }
48
+ case "VT_LPWSTR": {
49
+ const characters = encodeUnicodeStringValue(value.value);
50
+ const paddedLength = padTo4(characters.length);
51
+ const bytes = new Uint8Array(8 + paddedLength);
52
+ const view = new DataView(bytes.buffer);
53
+ view.setUint16(0, 31, true);
54
+ view.setUint16(2, 0, true);
55
+ view.setUint32(4, characters.length / 2, true);
56
+ bytes.set(characters, 8);
57
+ return bytes;
58
+ }
59
+ case "VT_LPSTR": throw new PropertySetWriteError("writePropertySetStream cannot write a VT_LPSTR property: this writer emits Unicode (VT_LPWSTR) strings only, since encoding to an arbitrary ANSI codepage is out of scope -- see the package README's OLEPS scope note");
60
+ }
61
+ }
62
+ function writePropertySetStream(propertySet) {
63
+ const entries = [...propertySet.properties.entries()].sort(([a], [b]) => a - b);
64
+ const dictionaryBytes = new Uint8Array(entries.length * 8);
65
+ const dictionaryView = new DataView(dictionaryBytes.buffer);
66
+ const valueChunks = [];
67
+ let valueOffset = 8 + dictionaryBytes.length;
68
+ let index = 0;
69
+ for (const [pid, value] of entries) {
70
+ const encoded = encodeTypedPropertyValue(value);
71
+ dictionaryView.setUint32(index * 8, pid, true);
72
+ dictionaryView.setUint32(index * 8 + 4, valueOffset, true);
73
+ valueChunks.push(encoded);
74
+ valueOffset += encoded.length;
75
+ index += 1;
76
+ }
77
+ const propertySetSize = valueOffset;
78
+ const propertySetBytes = new Uint8Array(propertySetSize);
79
+ const propertySetView = new DataView(propertySetBytes.buffer);
80
+ propertySetView.setUint32(0, propertySetSize, true);
81
+ propertySetView.setUint32(4, entries.length, true);
82
+ propertySetBytes.set(dictionaryBytes, 8);
83
+ let cursor = 8 + dictionaryBytes.length;
84
+ for (const chunk of valueChunks) {
85
+ propertySetBytes.set(chunk, cursor);
86
+ cursor += chunk.length;
87
+ }
88
+ const streamBytes = new Uint8Array(48 + propertySetBytes.length);
89
+ const view = new DataView(streamBytes.buffer);
90
+ view.setUint16(0, BYTE_ORDER_MARK, true);
91
+ view.setUint16(2, 0, true);
92
+ view.setUint32(4, 0, true);
93
+ writeGuid(view, 8, GUID_NULL);
94
+ view.setUint32(24, 1, true);
95
+ writeGuid(view, 28, propertySet.formatId);
96
+ view.setUint32(44, 48, true);
97
+ streamBytes.set(propertySetBytes, 48);
98
+ return streamBytes;
99
+ }
100
+ //#endregion
101
+ export { PropertySetWriteError, writePropertySetStream };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "archive-codec",
3
- "version": "1.3.0",
4
- "description": "ZIP-in-ZIP recursive walking with depth and cumulative decompressed-size guards, plus bounded classic OLE compound-file ([MS-CFB]) reading - zero document-format knowledge, the archive and container utility package for the documents.js family.",
3
+ "version": "1.4.1",
4
+ "description": "ZIP-in-ZIP recursive walking with depth and cumulative decompressed-size guards, bounded classic OLE compound-file ([MS-CFB]) reading and writing, and [MS-OLEPS] Property Set Stream reading and writing - zero document-format knowledge, the archive and container utility package for the documents.js family.",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",
@@ -67,6 +67,7 @@
67
67
  },
68
68
  "packageManager": "pnpm@11.6.0",
69
69
  "dependencies": {
70
+ "document-schema.js": "^5.6.0",
70
71
  "fflate": "^0.8.2"
71
72
  },
72
73
  "devDependencies": {