@quario/docx 0.1.0 → 0.1.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/CHANGELOG.md CHANGED
@@ -1,5 +1,48 @@
1
1
  # @quario/docx
2
2
 
3
+ ## 0.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - **Every target factory now refuses an option it does not understand.** An unknown key, a key with
8
+ a value of the wrong type, or an `options` that is not an object throws a `TypeError` at the
9
+ factory call, naming the option path — `options.meta.title`, `options.page.size`. `@quario/docx`
10
+ already behaved this way. `@quario/pdf`, `@quario/html`, `@quario/xlsx` and `@quario/layout` read
11
+ the keys they knew and ignored the rest, so a typo cost you the option you meant to set with no
12
+ signal at any point. `csv()` takes no options, and it refuses an argument rather than discarding
13
+ one.
14
+
15
+ The check itself is one thing, so the engine now exports it: `hostOptions` closes a key set and
16
+ `hostMeta` validates the `{ title, author, subject }` contract the document targets share. A
17
+ fourth document property is one edit rather than three.
18
+
19
+ **What this changes for you.** One options object spread across several targets stops working if
20
+ any target does not know one of its keys — `const o = { page, fonts, meta }` passed to `pdf(o)`,
21
+ `html(o)` and `xlsx(o)` is three different key sets. An object holding only what its consumers
22
+ share is unaffected, so `{ page, fonts }` into both `pdf()` and `layout()` still works.
23
+ TypeScript does not warn about this: excess-property checking fires on an object literal and not
24
+ on a variable, so a bag held in a `const` compiles clean and throws when you call the factory.
25
+
26
+ Nullish is absence everywhere, at both levels, so `{ meta: config.meta ?? null }` and
27
+ `{ meta: { title: config.title } }` over a config that carries neither are both fine.
28
+ `html({ paths })` now takes `true` or `false` rather than anything truthy, so `paths: "no"` throws
29
+ instead of turning path stamping on. A `fonts` mapping given as an array is refused by
30
+ `@quario/html` and `@quario/layout` rather than read as families named `0`, `1`, `2`.
31
+
32
+ Every host-option failure is now a `TypeError` rather than a plain `Error`. A `catch` that tests
33
+ `instanceof Error` is unaffected. One that compares the constructor is not.
34
+
35
+ `@quario/docx` is relaxed in three places, each of them now accepting what it refused: `meta: null`
36
+ and `page: null` are absence rather than errors, a property written as `meta: { title: undefined }`
37
+ is a property you did not write rather than one of the wrong type, and an inherited enumerable key
38
+ is no longer reported as an option you wrote.
39
+
40
+ `@quario/pdf`, `@quario/xlsx` and `@quario/docx` now copy `meta` at the factory call. Mutating the
41
+ object you passed no longer changes what a configured target writes.
42
+
43
+ - Updated dependencies
44
+ - quario@0.9.0
45
+
3
46
  ## 0.1.0
4
47
 
5
48
  ### Minor Changes
package/README.md CHANGED
@@ -11,7 +11,7 @@ npm install quario @quario/docx
11
11
  ```
12
12
 
13
13
  The engine is a peer dependency, installed beside the target. ESM-only, Node 22+, and
14
- browser-ready through any standards-based ESM bundler. The renderer resolves a `Uint8Array`; you
14
+ browser-ready through any standards-based ESM bundler. The renderer resolves a `Uint8Array`. You
15
15
  write the file.
16
16
 
17
17
  ## Quick start
@@ -39,8 +39,8 @@ writeFileSync("orders.docx", bytes);
39
39
 
40
40
  ## Options
41
41
 
42
- Both are optional, and both are validated at the factory call an unknown option, or one of the
43
- wrong type, throws there rather than at the first render.
42
+ Both are optional. The factory call validates both: an unknown option, or one of the wrong type,
43
+ throws there rather than at the first render.
44
44
 
45
45
  ```js
46
46
  docx({
@@ -56,25 +56,25 @@ docx({
56
56
 
57
57
  ## What it renders
58
58
 
59
- A report's bands become paragraphs; a table detail becomes a **real Word table** carrying the
59
+ A report's bands become paragraphs. A table detail becomes a **real Word table** carrying the
60
60
  author's column shares as a fixed grid, with `span` as `gridSpan` and a header row that repeats
61
- after every page break. A split is a borderless one-row table, and an image is an inline picture at its natural size, capped at the text
62
- column, carrying its `alt` as the drawing's description.
61
+ after every page break. A split is a borderless one-row table. An image is an inline picture at
62
+ its natural size, capped at the text column, carrying its `alt` as the drawing's description.
63
63
 
64
64
  The grouping becomes the **navigation pane**: a group header's first item takes `Heading{depth+1}`
65
65
  from this package's own `styles.xml`, so no theme typography leaks in. Everything a report declares
66
- is direct formatting on the runs and paragraphs that wear it `Normal` stays empty, because a look
66
+ is direct formatting on the runs and paragraphs that wear it. `Normal` stays empty, because a look
67
67
  that depends on a style lookup is a look three readers may resolve three ways.
68
68
 
69
69
  Page bands become a section's header and footer. A bare `{{ page.number }}` or `{{ page.total }}`
70
- becomes a live `PAGE` / `NUMPAGES` field the reader's own application recomputes; anything computed
71
- from them is frozen at the value the render saw. A group's `reset: "page"` opens a section that
70
+ becomes a live `PAGE` / `NUMPAGES` field the reader's own application recomputes. Anything computed
71
+ from them freezes at the value the render saw. A group's `reset: "page"` opens a section that
72
72
  restarts the numbering, and `break: "page"` starts the instance on a fresh page.
73
73
 
74
74
  ## Determinism
75
75
 
76
- Two renders of one report are byte-identical, on **every supported runtime**: `fflate` is pure
77
- JavaScript, so its deflate output does not vary by engine. Every entry in the package is stamped
76
+ Two renders of one report are byte-identical, on **every supported runtime**. `fflate` is pure
77
+ JavaScript, so its deflate output does not vary by engine. The package stamps every entry
78
78
  1980-01-01 and nothing in the document carries a clock, so a digest over the bytes is a fair test.
79
79
 
80
80
  ## Documentation
@@ -87,10 +87,10 @@ of a declaration.
87
87
 
88
88
  ## License
89
89
 
90
- Commercial software with readable source. Free, unlimited, watermarked evaluation; per-developer
91
- licenses at [getquario.com](https://getquario.com). See the bundled LICENSE.
90
+ Commercial software with readable source. Evaluation is free, unlimited, and watermarked.
91
+ Per-developer licenses at [getquario.com](https://getquario.com). See the bundled LICENSE.
92
92
 
93
- Pass your license key once, on the instance; it is verified offline:
93
+ Pass your license key once, on the instance. quario verifies it offline:
94
94
 
95
95
  ```js
96
96
  const q = quario({ license: "quario_..." });
package/lib/index.d.ts CHANGED
@@ -18,7 +18,11 @@ export interface DocxMeta {
18
18
  subject?: string;
19
19
  }
20
20
 
21
- /** Host controls, taken and validated at the factory call. */
21
+ /**
22
+ * Host controls, taken and validated at the factory call: an option this
23
+ * target does not understand, or one of the wrong type, throws a `TypeError`
24
+ * there rather than costing the host the option in silence.
25
+ */
22
26
  export interface DocxOptions {
23
27
  page?: DocxPage;
24
28
  meta?: DocxMeta;
package/lib/index.js CHANGED
@@ -11,7 +11,7 @@
11
11
  * `furniture.js` for the parts a page band becomes, `styles.js` for the base
12
12
  * every part inherits, and `text.js` for the runs all of them are made of.
13
13
  */
14
- import { walk } from "quario";
14
+ import { hostMeta, hostOptions, walk } from "quario";
15
15
  import { bodyOf } from "./body.js";
16
16
  import { DOCUMENT_NS, furnish } from "./furniture.js";
17
17
  import { STYLES } from "./stylepart.js";
@@ -29,44 +29,6 @@ const WML = "application/vnd.openxmlformats-officedocument.wordprocessingml";
29
29
  // own default, and what the page bands will be laid against.
30
30
  const FURNITURE = 720;
31
31
 
32
- /** @type {(msg: string) => never} */
33
- let err = (msg) => {
34
- throw Error(msg);
35
- };
36
-
37
- // Split from `object` below rather than inlined: the three terms together
38
- // breach the complexity budget `npm run fallow` holds this package to.
39
- /** @type {(value: any) => boolean} */
40
- let plain = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
41
-
42
- /** @type {(value: any, at: string) => any} */
43
- let object = (value, at) => {
44
- // oxlint-disable-next-line no-unused-expressions
45
- value === undefined || plain(value) || err(at + ": expected an object");
46
- return value;
47
- };
48
-
49
- /** @type {(value: any, keys: string[], at: string) => void} */
50
- let only = (value, keys, at) => {
51
- for (let key in value) if (!keys.includes(key)) err(at + ': unknown option "' + key + '"');
52
- };
53
-
54
- // The three document properties a host may write.
55
- let PROPERTIES = ["title", "author", "subject"];
56
-
57
- // The document properties, validated: strings, and never a date. What a host
58
- // cannot write is what keeps a render deterministic -- `core.xml` is the one
59
- // part with a slot for a clock, and it has none of them.
60
- /** @type {(meta: any) => any} */
61
- let metaOf = (meta) => {
62
- object(meta, "options.meta");
63
- only(meta, PROPERTIES, "options.meta");
64
- for (let key in meta)
65
- // oxlint-disable-next-line no-unused-expressions
66
- typeof meta[key] === "string" || err("options.meta." + key + ": expected a string");
67
- return meta;
68
- };
69
-
70
32
  /**
71
33
  * One section. The child order is the schema's and Word is strict about it:
72
34
  * the part references, then `type`, the geometry, `pgNumType`, `cols`, and
@@ -191,15 +153,20 @@ let parts = (body, meta, furniture, rels) => ({
191
153
  * bytes described in SCHEMA.md ("The DOCX target"). Options are taken and
192
154
  * validated at the factory call.
193
155
  *
194
- * @param {any} [options] Host controls: `page` and `meta`.
156
+ * The options are described once, in the hand-written public declarations,
157
+ * and read back here -- a second copy in JSDoc is a copy that drifts.
158
+ * @import { DocxOptions } from './index.d.ts'
159
+ *
160
+ * @param {DocxOptions} [options] Host controls (see SCHEMA.md, "The DOCX target").
195
161
  * @returns {{name: "docx", compile: (stream: any) => (data?: any) => Promise<Uint8Array>}}
196
162
  * The target (see SCHEMA.md, "Instances and targets").
197
163
  */
198
164
  export function docx(options) {
199
- object(options, "options");
200
- only(options, ["page", "meta"], "options");
201
- let page = object(options?.page, "options.page");
202
- let meta = metaOf(options?.meta);
165
+ hostOptions(options, ["page", "meta"], "options");
166
+ // Closed here rather than in `pageBox`, for the reason the layout package
167
+ // gives at the same call: the surface elements share that function.
168
+ let page = hostOptions(options?.page, ["size", "margin"], "options.page");
169
+ let meta = hostMeta(options?.meta, "options.meta");
203
170
  // The size fails here rather than at the first render: a host wrote it, so
204
171
  // a host hears about it where it was written. The margin is validated again
205
172
  // per render, because the document may be the one declaring it.
package/lib/page.js CHANGED
@@ -25,7 +25,7 @@ let SIZES = /** @type {Record<string, [number, number]>} */ ({
25
25
 
26
26
  /** @type {(msg: string) => never} */
27
27
  let err = (msg) => {
28
- throw Error(msg);
28
+ throw TypeError(msg);
29
29
  };
30
30
 
31
31
  /** @type {(value: number) => boolean} */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quario/docx",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "The Word render target for quario — a flow target: real Word tables, a navigable outline, live page-number fields",
5
5
  "homepage": "https://getquario.com",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -41,12 +41,12 @@
41
41
  "devDependencies": {
42
42
  "@arethetypeswrong/cli": "^0.18.3",
43
43
  "@size-limit/preset-small-lib": "^13.0.3",
44
- "quario": "^0.8.0",
44
+ "quario": "^0.9.0",
45
45
  "size-limit": "^13.0.3",
46
46
  "typescript": "^7.0.2"
47
47
  },
48
48
  "peerDependencies": {
49
- "quario": "^0.8.0"
49
+ "quario": "^0.9.0"
50
50
  },
51
51
  "size-limit": [
52
52
  {