@reportwright/cli 0.12.0 → 0.12.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/dist/pw.mjs CHANGED
@@ -3849,9 +3849,23 @@ function nodeRequest(url, init, trusted) {
3849
3849
  for (let i = 0; i < res.rawHeaders.length; i += 2) pairs2.push([res.rawHeaders[i], res.rawHeaders[i + 1]]);
3850
3850
  const none = [101, 204, 205, 304].includes(res.statusCode) || (init.method || "GET").toUpperCase() === "HEAD";
3851
3851
  if (none) res.resume();
3852
- resolve(new Response(none ? null : stream.Readable.toWeb(res), { status: res.statusCode, statusText: res.statusMessage, headers: (
3852
+ const enc = String(res.headers["content-encoding"] || "").trim().toLowerCase();
3853
+ let body = res, decoded = false;
3854
+ if (!none && enc && enc !== "identity") {
3855
+ const zlib = nodeMod("node:zlib");
3856
+ const dec2 = enc === "gzip" || enc === "x-gzip" ? zlib.createGunzip() : enc === "deflate" ? zlib.createInflate() : enc === "br" ? zlib.createBrotliDecompress() : null;
3857
+ if (!dec2) {
3858
+ res.resume();
3859
+ return reject(Object.assign(new Error(`the response is encoded as "${enc}", which the data fetch cannot decode`), { status: 415 }));
3860
+ }
3861
+ body = stream.pipeline(res, dec2, () => {
3862
+ });
3863
+ decoded = true;
3864
+ }
3865
+ const kept = decoded ? pairs2.filter(([k]) => !/^(content-encoding|content-length)$/i.test(k)) : pairs2;
3866
+ resolve(new Response(none ? null : stream.Readable.toWeb(body), { status: res.statusCode, statusText: res.statusMessage, headers: (
3853
3867
  /** @type {any} */
3854
- pairs2
3868
+ kept
3855
3869
  ) }));
3856
3870
  });
3857
3871
  req.on("error", reject);
@@ -3869,7 +3883,7 @@ function redirectRequest(from, to, req, status) {
3869
3883
  }
3870
3884
  function guardedFetch(opt, base) {
3871
3885
  const allow = (opt.allowHosts || []).map((h) => String(h).toLowerCase().replace(/^\[|\]$/g, ""));
3872
- const node2 = inNode();
3886
+ const node2 = !opt.viaBase && inNode();
3873
3887
  return (
3874
3888
  /** @type {any} */
3875
3889
  (async (input, init = {}) => {
@@ -4802,7 +4816,10 @@ function fetcherFor(opt) {
4802
4816
  return boundedFetch(f4, opt);
4803
4817
  }
4804
4818
  function checkHostSource(src, url, opt) {
4805
- if (opt.fetch || opt.unsafeFetch) return;
4819
+ const rule = opt.fetch ? opt.fetch.hostRule : opt.unsafeFetch ? null : opt;
4820
+ if (!rule) return;
4821
+ const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(String(url).trim());
4822
+ if (scheme && !/^https?$/i.test(scheme[1])) throw Object.assign(new Error(`Only http and https URLs can be fetched, not ${scheme[1].toLowerCase()}:`), { status: 400 });
4806
4823
  const t = String(src.url ?? "").trim();
4807
4824
  if (/^\/(?![/\\])/.test(t)) return;
4808
4825
  const m = /^https?:\/\/([^/?#\\]*)/i.exec(t);
@@ -4813,7 +4830,7 @@ function checkHostSource(src, url, opt) {
4813
4830
  host = new URL(url).hostname.replace(/^\[|\]$/g, "").toLowerCase();
4814
4831
  } catch {
4815
4832
  }
4816
- if (whole && host && (opt.allowHosts || []).map((h) => String(h).toLowerCase()).includes(host)) return;
4833
+ if (whole && host && (rule.allowHosts || []).map((h) => String(h).toLowerCase()).includes(host)) return;
4817
4834
  throw Object.assign(new Error("its host is made by a parameter or an expression: write the scheme and host out (parameters may fill the path and query), or make the whole URL a parameter and list its host in allowHosts"), { status: 403 });
4818
4835
  }
4819
4836
  async function loadSources(def, opt) {
@@ -11348,7 +11365,8 @@ function toRdlx3(root, notes, reportName) {
11348
11365
  const on = prop(b, "aggregateOn") ?? simpleList(b, "aggregateOn")[0]?.text;
11349
11366
  const gc = { ...c, group: on && on !== "All" ? on : void 0 };
11350
11367
  if (fn3) {
11351
- const arg = listProp(b, "arguments").map((x) => prop(x, "value")).find((x) => x != null);
11368
+ const arg = listProp(b, "arguments").map((x) => prop(x, "value")).find((x) => x != null) ?? prop(b, "expression");
11369
+ if (arg == null && fn3 !== "count") throw new Untranslatable2(`the aggregate ${fn3.toUpperCase()} with no expression`);
11352
11370
  const inner2 = arg != null ? vb2(arg, { ...c, inSet: false }, depth) : "1";
11353
11371
  if (prop(b, "filterExpr")) throw new Untranslatable2(`the bound column "${prop(b, "name")}", an aggregate with a filter`);
11354
11372
  const all = gc.group ?? c.region;
@@ -12126,6 +12144,7 @@ function special(cp) {
12126
12144
  if (cp >= 8192 && cp <= 8303) return cp >= 8203 && cp <= 8207 || cp >= 8234 && cp <= 8238 || cp >= 8288;
12127
12145
  return cp >= 11904 && cp <= 42191 || cp >= 43056 && cp <= 44031 || cp >= 44032 && cp <= 55215 || cp >= 63744 && cp <= 64255 || cp >= 64285 && cp <= 65023 || cp >= 65024 && cp <= 65039 || cp >= 65072 && cp <= 65103 || cp >= 65136 && cp <= 65278 || cp >= 65280 && cp <= 65519 || cp >= 65536;
12128
12146
  }
12147
+ var FORMAT_CHAR = new RegExp("^\\p{Cf}$", "u");
12129
12148
  var cjk = (cp) => cp >= 11904 && cp <= 40959 || cp >= 44032 && cp <= 55215 || cp >= 63744 && cp <= 64255 || cp >= 65280 && cp <= 65519 || cp >= 131072 && cp <= 262143;
12130
12149
  var NO_START = new Set("、。,.・:;?!ー)」』】〕〉》〗〙〟’”}⦆ぁぃぅぇぉっゃゅょゎゕゖァィゥェォッャュョヮヵヶ々〻‐゠–〜~…‥,.!?:;)]}%ゝゞヽヾ゛゜ㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ。」、・ー%]⦆");
12131
12150
  var NO_END = new Set("(「『【〔〈《〖〘〝‘“{⦅([{[「$¥£");
@@ -12521,7 +12540,7 @@ var TextMeasurer = class {
12521
12540
  }
12522
12541
  /** The font for one character: the chosen one, else the bundled fallback for its script, else the one before. */
12523
12542
  pick(cp, key, prev) {
12524
- if (cp < 32 || this.has(key, cp)) return key;
12543
+ if (cp < 32 || this.has(key, cp) || FORMAT_CHAR.test(String.fromCodePoint(cp))) return key;
12525
12544
  const fb = fallbackKey(cp, key);
12526
12545
  if (fb && !this.store.failed.has(fb)) {
12527
12546
  if (!this.store.has(fb)) {
@@ -13377,6 +13396,11 @@ function niceScale(min, max, ticks = 5) {
13377
13396
  const step3 = niceNum(range / Math.max(1, ticks - 1), true);
13378
13397
  return { lo: Math.floor(min / step3) * step3, hi: Math.ceil(max / step3) * step3, step: step3 };
13379
13398
  }
13399
+ function niceScaleSpan(min, max, ticks = 5) {
13400
+ if (!Number.isFinite(min) || !Number.isFinite(max) || !(max > min)) return niceScale(min, max, ticks);
13401
+ const step3 = niceNum((max - min) / Math.max(1, ticks - 1), true);
13402
+ return { lo: Math.floor(min / step3) * step3, hi: Math.ceil(max / step3) * step3, step: step3 };
13403
+ }
13380
13404
  function niceNum(x, round) {
13381
13405
  const e2 = Math.floor(Math.log10(x));
13382
13406
  const f4 = x / 10 ** e2;
@@ -14334,7 +14358,7 @@ var byName = (lc, pal) => (name) => {
14334
14358
  };
14335
14359
  function chartData(item, ctx, lc, pal = paletteOf(item.palette)) {
14336
14360
  let rows = item.dataSet ? related(lc.dataSets[item.dataSet] || [], ctx) : ctx.aggRows || [];
14337
- if (item.dataSet && !lc.dataSets[item.dataSet]) lc.warn(`${item.name}: the data set "${item.dataSet}" does not exist`);
14361
+ if (item.dataSet && !lc.dataSets[item.dataSet]) lc.warn(`${item.name || item.type}: the data set "${item.dataSet}" does not exist`);
14338
14362
  if (item.filters?.length) rows = applyFilters(rows, item.filters, ctx);
14339
14363
  if (item.sort?.length) rows = applySort(rows, item.sort, ctx);
14340
14364
  const who = item.name || "Chart";
@@ -14622,13 +14646,15 @@ function valueAxis(spec, lo, hi, minPos, { includeZero = true, len = 100, fs: fs
14622
14646
  lo = lo < 0 ? -1 : 0;
14623
14647
  hi = 1;
14624
14648
  }
14649
+ let padded = false;
14625
14650
  if (pad2 > 0 && hi > lo) {
14626
14651
  const d = (hi - lo) * pad2;
14627
- if (smin == null) lo -= d;
14628
- if (smax == null) hi += d;
14652
+ if (smin == null) lo = lo >= 0 && lo - d < 0 ? 0 : lo - d;
14653
+ if (smax == null) hi = hi <= 0 && hi + d > 0 ? 0 : hi + d;
14654
+ padded = true;
14629
14655
  }
14630
14656
  const want = len < fs2 * 6 ? 3 : 5;
14631
- const sc = niceScale(smin ?? lo, smax ?? hi, want);
14657
+ const sc = padded && smin == null && smax == null && (lo >= 0 || hi <= 0) ? niceScaleSpan(lo, hi, want) : niceScale(smin ?? lo, smax ?? hi, want);
14632
14658
  let a = sc.lo, b = sc.hi, ticks;
14633
14659
  if (smin != null || smax != null) {
14634
14660
  a = smin ?? sc.lo;
@@ -15056,31 +15082,27 @@ function rowPoints(type, g) {
15056
15082
  rows = rows.slice(0, cap2);
15057
15083
  }
15058
15084
  const blank2 = (v) => v == null || v === "";
15059
- const missing2 = /* @__PURE__ */ new Set();
15085
+ const drawn = /* @__PURE__ */ new Set();
15060
15086
  const sets = data.series.map((s, si) => {
15061
15087
  const pts2 = [];
15062
15088
  rows.forEach((r, ri) => {
15063
15089
  c.fields = r;
15064
15090
  const rx = safe2(item.xValue, c, lc, who), ry = safe2(s.def.value || "=0", { ...c, aggRows: [r] }, lc, who);
15065
- if (blank2(rx) || blank2(ry)) {
15066
- missing2.add(ri);
15067
- return;
15068
- }
15091
+ if (blank2(rx) || blank2(ry)) return;
15069
15092
  const x = Number(rx), y = Number(ry);
15070
- if (!Number.isFinite(x) || !Number.isFinite(y)) {
15071
- missing2.add(ri);
15072
- return;
15073
- }
15093
+ if (!Number.isFinite(x) || !Number.isFinite(y)) return;
15074
15094
  let size = 1;
15075
15095
  if (type === "bubble") {
15076
15096
  size = Number(safe2(s.def.size || "=1", { ...c, aggRows: [r] }, lc, who));
15077
15097
  if (!Number.isFinite(size) || size <= 0) return;
15078
15098
  }
15079
15099
  pts2.push({ x, y, size });
15100
+ drawn.add(ri);
15080
15101
  });
15081
15102
  return { s, pts: pts2, i: si };
15082
15103
  });
15083
- if (missing2.size) lc.warn(`${who}: ${missing2.size} of ${rows.length} rows have no x or y value and are not drawn`);
15104
+ const missing2 = rows.length - drawn.size;
15105
+ if (missing2 > 0) lc.warn(`${who}: ${missing2} of ${rows.length} rows have no x or y value and are not drawn`);
15084
15106
  return sets;
15085
15107
  }
15086
15108
  function paintXY(type, g) {
@@ -17512,7 +17534,7 @@ function hidden(item, ctx, lc) {
17512
17534
  const r = evalSafe(item.hidden, ctx, lc, item.name || item.type);
17513
17535
  h = !r.err && truthy(r.v);
17514
17536
  }
17515
- return item.name && lc.state?.toggles?.[`item:${item.name}`] ? !h : h;
17537
+ return item.name && lc.state?.toggles?.[`item:${item.name || item.type}`] ? !h : h;
17516
17538
  }
17517
17539
  var SHRINK_DEFAULTS = { minFontSize: 4, step: 0.5 };
17518
17540
  function rotationOf(item) {
@@ -17848,7 +17870,7 @@ register("table", {
17848
17870
  else {
17849
17871
  rows = item.dataSet && lc.dataSets[item.dataSet] || [];
17850
17872
  rows = related(rows, tctx);
17851
- if (item.dataSet && !lc.dataSets[item.dataSet]) lc.warn(`${item.name || "Table"}: the data set "${item.dataSet}" does not exist`);
17873
+ if (item.dataSet && !lc.dataSets[item.dataSet]) lc.warn(`${item.name || item.type}: the data set "${item.dataSet}" does not exist`);
17852
17874
  if (item.filters?.length) rows = applyFilters(rows, item.filters, tctx);
17853
17875
  const sort = lc.state?.sort?.[item.name] || item.sort;
17854
17876
  if (sort?.length) rows = applySort(rows, sort, tctx);
@@ -17864,11 +17886,11 @@ register("table", {
17864
17886
  if (overflow === "shrink" && room > 0) {
17865
17887
  const k = room / designedW;
17866
17888
  cols = cols.map((c) => ({ ...c, width: (Number(c.width) || 0) * k }));
17867
- lc.warn(`${item.name || "Table"}: its columns are ${Math.round(designedW)}pt wide, wider than the page (${Math.round(room)}pt), so they are narrowed to fit`);
17889
+ lc.warn(`${item.name || item.type}: its columns are ${Math.round(designedW)}pt wide, wider than the page (${Math.round(room)}pt), so they are narrowed to fit`);
17868
17890
  } else if (overflow === "clip" || overflow === "paginate" && lc.fixed) {
17869
- lc.warn(`${item.name || "Table"}: its columns are ${Math.round(designedW)}pt wide, wider than the page (${Math.round(room)}pt), so the columns past the page edge are cut`, true);
17891
+ lc.warn(`${item.name || item.type}: its columns are ${Math.round(designedW)}pt wide, wider than the page (${Math.round(room)}pt), so the columns past the page edge are cut`, true);
17870
17892
  } else if (overflow === "paginate" && item.overflow !== "paginate") {
17871
- lc.warn(`${item.name || "Table"}: its columns are ${Math.round(designedW)}pt wide, wider than the page (${Math.round(room)}pt), so they continue on the next pages`);
17893
+ lc.warn(`${item.name || item.type}: its columns are ${Math.round(designedW)}pt wide, wider than the page (${Math.round(room)}pt), so they continue on the next pages`);
17872
17894
  }
17873
17895
  const colX = [];
17874
17896
  let acc = item.x;
@@ -17878,6 +17900,7 @@ register("table", {
17878
17900
  }
17879
17901
  const width = acc - item.x;
17880
17902
  const region = { name: item.name, columns: cols.map((c) => c.width), rows: [] };
17903
+ if (lc.tablesSeen) lc.tablesSeen.n++;
17881
17904
  const tableScope = { rows, index: -1, run: [] };
17882
17905
  const scopes = { ...tctx.scopes || {}, [item.name]: tableScope };
17883
17906
  const groupChains = {};
@@ -17901,7 +17924,7 @@ register("table", {
17901
17924
  const covered = (ci) => cover[ci] !== null;
17902
17925
  const cellsAt = /* @__PURE__ */ new WeakMap();
17903
17926
  const layRow = (row, ctx, kind, extra) => {
17904
- if (row.hidden != null && row.hidden !== false && row.hidden !== "" && hidden({ hidden: row.hidden, name: `${item.name || "Table"} row` }, ctx, lc)) {
17927
+ if (row.hidden != null && row.hidden !== false && row.hidden !== "" && hidden({ hidden: row.hidden, name: `${item.name || item.type} row` }, ctx, lc)) {
17905
17928
  if (openSpans.length) {
17906
17929
  for (const s of openSpans) s.left--;
17907
17930
  for (const s of openSpans) if (s.left <= 0) closeSpan(s);
@@ -17935,7 +17958,7 @@ register("table", {
17935
17958
  else try {
17936
17959
  v = evalValue(cell.value ?? "", cctx);
17937
17960
  } catch (e2) {
17938
- lc.warn(`${item.name || "Table"} cell: ${e2.message}`);
17961
+ lc.warn(`${item.name || item.type} cell: ${e2.message}`);
17939
17962
  v = "#Error";
17940
17963
  }
17941
17964
  let text2 = textOf(v, st, cctx, lc);
@@ -17978,7 +18001,7 @@ register("table", {
17978
18001
  cells.forEach((c, k) => {
17979
18002
  if (c.cell.visual?.type) {
17980
18003
  items.push(...paintBox({ x: c.x, y: 0, w: c.w, h, st: c.st, tl: null, m: lc.m }));
17981
- if (first) items.push(...paintVisual(c.cell.visual, { x: c.x, y: 0, w: c.w, h }, c.ctx, lc, c.st, `${item.name} cell`, { cell: true }));
18004
+ if (first) items.push(...paintVisual(c.cell.visual, { x: c.x, y: 0, w: c.w, h }, c.ctx, lc, c.st, `${item.name || item.type} cell`, { cell: true }));
17982
18005
  return;
17983
18006
  }
17984
18007
  if (c.nested) {
@@ -18033,7 +18056,7 @@ register("table", {
18033
18056
  };
18034
18057
  const layoutInCell = (inner2, x, cw, st, cctx, row) => {
18035
18058
  const pd = padding(st);
18036
- const it = { ...inner2, name: inner2.name || `${item.name}_cell`, x: x + pd[3], y: 0, w: Math.max(1, cw - pd[1] - pd[3]), h: Number(inner2.h) || Math.max(1, (row.height || 18) - pd[0] - pd[2]) };
18059
+ const it = { ...inner2, name: inner2.name || `${item.name || item.type}_cell`, x: x + pd[3], y: 0, w: Math.max(1, cw - pd[1] - pd[3]), h: Number(inner2.h) || Math.max(1, (row.height || 18) - pd[0] - pd[2]) };
18037
18060
  const scopeRows2 = cctx.aggIndex != null ? [cctx.fields] : cctx.aggRows || [];
18038
18061
  const current = item.dataSet ? { ...cctx.current || {}, [item.dataSet]: scopeRows2 } : cctx.current;
18039
18062
  const child = {
@@ -18053,7 +18076,7 @@ register("table", {
18053
18076
  const reads = (it.dataSet && it.dataSet !== item.dataSet ? child.ctxFor(it).aggRows?.length : scopeRows2.length) || 1;
18054
18077
  if (budget.left < reads) {
18055
18078
  budget.left = 0;
18056
- lc.warn(`${item.name}: more than ${CELL_ITEM_UNITS} rows of items inside cells; the rest are left empty`);
18079
+ lc.warn(`${item.name || item.type}: more than ${CELL_ITEM_UNITS} rows of items inside cells; the rest are left empty`);
18057
18080
  return { items: [], height: 0 };
18058
18081
  }
18059
18082
  budget.left -= reads;
@@ -18097,7 +18120,7 @@ register("table", {
18097
18120
  u.items.splice(at2 < 0 ? u.items.length : at2, 0, ...add2);
18098
18121
  });
18099
18122
  if (s.tl.needed > s.units[0].height + GROW_TOLERANCE || s.st.verticalAlign && s.st.verticalAlign !== "top") {
18100
- const id = `${item.name}:span:${lc.nextId()}`;
18123
+ const id = `${item.name || item.type}:span:${lc.nextId()}`;
18101
18124
  for (const u of s.units) u.block ||= id;
18102
18125
  } else {
18103
18126
  let above = s.units[0].height;
@@ -18139,7 +18162,7 @@ register("table", {
18139
18162
  const groups = item.groups || [];
18140
18163
  let rowNo = lc.win?.rowOffset || 0;
18141
18164
  const groupTables = [];
18142
- const tableKey = `${item.name}#${lc.nextId()}`;
18165
+ const tableKey = `${item.name || item.type}#${lc.nextId()}`;
18143
18166
  const emit2 = (level, grows, ctx, chain, keyPath) => {
18144
18167
  if (level === groups.length) {
18145
18168
  for (let i = 0; i < grows.length; i++) {
@@ -18201,7 +18224,7 @@ register("table", {
18201
18224
  kids = /* @__PURE__ */ new Map();
18202
18225
  roots = [];
18203
18226
  for (const b of buckets) {
18204
- const pk = evalSafe(g.parent, { ...ctx, fields: b.rows[0], aggRows: b.rows, aggIndex: void 0 }, lc, `${item.name} group parent`).v;
18227
+ const pk = evalSafe(g.parent, { ...ctx, fields: b.rows[0], aggRows: b.rows, aggIndex: void 0 }, lc, `${item.name || item.type} group parent`).v;
18205
18228
  const pr = pk == null || pk === "" ? null : byText.get(pk instanceof Date ? String(pk.getTime()) : String(pk));
18206
18229
  if (!pr || pr === b) roots.push(b);
18207
18230
  else {
@@ -18233,7 +18256,7 @@ register("table", {
18233
18256
  if (g.indent) gctx.indent = (ctx.indent || 0) + depth * Number(g.indent);
18234
18257
  }
18235
18258
  const path2 = `${parentPath}/${bk.text}`;
18236
- const tkey = `${item.name}|${level}|${path2}`;
18259
+ const tkey = `${item.name || item.type}|${level}|${path2}`;
18237
18260
  const dflt = kids && g.expandLevels != null ? depth < Number(g.expandLevels) : !g.initiallyCollapsed;
18238
18261
  const open = !g.collapsible ? true : lc.state?.toggles?.[tkey] ?? dflt;
18239
18262
  const start = units.length;
@@ -18255,7 +18278,7 @@ register("table", {
18255
18278
  const ch = kids?.get(bk) || [];
18256
18279
  if (open) {
18257
18280
  emit2(level + 1, bk.rows, gctx, [...chain, sc], path2);
18258
- if (ch.length && depth + 1 >= MAX_TREE_DEPTH) lc.warn(`${item.name}: the group "${gname}" is more than ${MAX_TREE_DEPTH} levels deep; deeper rows are listed at the top level`);
18281
+ if (ch.length && depth + 1 >= MAX_TREE_DEPTH) lc.warn(`${item.name || item.type}: the group "${gname}" is more than ${MAX_TREE_DEPTH} levels deep; deeper rows are listed at the top level`);
18259
18282
  else ch.forEach((c, ci) => node2(c, ci, ch, depth + 1, path2));
18260
18283
  } else {
18261
18284
  const rs = recOf.get(bk) || bk.rows;
@@ -18284,7 +18307,7 @@ register("table", {
18284
18307
  if (g.newSection) units[start].sectionStart = true;
18285
18308
  if (g.pageBreak === "after" && bi < sibs.length - 1) units[units.length - 1].breakAfter = true;
18286
18309
  if (g.keepTogether) {
18287
- const id = `${item.name}:g${level}:${path2}:${lc.nextId()}`;
18310
+ const id = `${item.name || item.type}:g${level}:${path2}:${lc.nextId()}`;
18288
18311
  for (let k = start; k < units.length; k++) if (!units[k].block) {
18289
18312
  units[k].block = id;
18290
18313
  if (cont) units[k].blockOpen = true;
@@ -18305,7 +18328,7 @@ register("table", {
18305
18328
  if (kids) {
18306
18329
  const rest2 = buckets.filter((b) => !shown2.has(b));
18307
18330
  if (rest2.length) {
18308
- lc.warn(`${item.name}: ${rest2.length} rows of the group "${gname}" have no root (a cycle or too deep); they are listed at the top level`);
18331
+ lc.warn(`${item.name || item.type}: ${rest2.length} rows of the group "${gname}" have no root (a cycle or too deep); they are listed at the top level`);
18309
18332
  rest2.forEach((bk, bi) => node2(bk, bi, rest2, 0, keyPath));
18310
18333
  }
18311
18334
  }
@@ -18333,7 +18356,7 @@ register("table", {
18333
18356
  units[units.length - 1].breakAfter = true;
18334
18357
  }
18335
18358
  if (item.bookmark && units.length) {
18336
- const b = evalSafe(item.bookmark, base, lc, `${item.name || "Table"} bookmark`).v;
18359
+ const b = evalSafe(item.bookmark, base, lc, `${item.name || item.type} bookmark`).v;
18337
18360
  if (b != null && b !== "" && b !== "#Error") units[0].items.unshift({ t: "bookmark", label: String(b), x: item.x, y: 0, level: item.bookmarkLevel ?? 0 });
18338
18361
  }
18339
18362
  if (!lc.fixed) lc.regions?.push(region);
@@ -18428,7 +18451,7 @@ register("container", {
18428
18451
  const ctx = lc.ctxFor(item);
18429
18452
  if (hidden(item, ctx, lc)) return { height: 0, units: [] };
18430
18453
  const res = lc.layoutItems(item.items, lc, { dx: item.x });
18431
- return frame2(item, lc, ctx, res, item.keepTogether !== false ? `c:${item.name}:${lc.nextId()}` : null);
18454
+ return frame2(item, lc, ctx, res, item.keepTogether !== false ? `c:${item.name || item.type}:${lc.nextId()}` : null);
18432
18455
  }
18433
18456
  });
18434
18457
  function frame2(item, lc, ctx, res, blockId0) {
@@ -18485,7 +18508,7 @@ register("list", {
18485
18508
  let rows = item.dataSet && lc.dataSets[item.dataSet] || [];
18486
18509
  if (item.inheritRows && item.dataSet && ctx.current?.[item.dataSet]) rows = ctx.current[item.dataSet];
18487
18510
  else rows = related(rows, ctx);
18488
- if (item.dataSet && !lc.dataSets[item.dataSet]) lc.warn(`${item.name}: the data set "${item.dataSet}" does not exist`);
18511
+ if (item.dataSet && !lc.dataSets[item.dataSet]) lc.warn(`${item.name || item.type}: the data set "${item.dataSet}" does not exist`);
18489
18512
  if (item.filters?.length) rows = applyFilters(rows, item.filters, ctx);
18490
18513
  if (item.sort?.length) rows = applySort(rows, item.sort, ctx);
18491
18514
  const reps = item.groupBy ? groupRows(rows, item.groupBy, ctx, lc, item.name, item.groupAdjacent === true).map((b) => b.rows) : rows.map((r) => [r]);
@@ -18514,7 +18537,7 @@ register("list", {
18514
18537
  } };
18515
18538
  const k = i % ncol;
18516
18539
  const res = lc.layoutItems(item.items, child, { dx: item.x + k * (item.w + gap) });
18517
- const fr = frame2({ ...item, x: item.x + k * (item.w + gap), name: `${item.name}[${i + 1}]` }, child, rctx, res, item.keepTogether !== false ? `l:${item.name}:${i}:${lc.nextId()}` : null);
18540
+ const fr = frame2({ ...item, x: item.x + k * (item.w + gap), name: `${item.name || item.type}[${i + 1}]` }, child, rctx, res, item.keepTogether !== false ? `l:${item.name || item.type}:${i}:${lc.nextId()}` : null);
18518
18541
  if (ncol > 1) {
18519
18542
  if (k === 0) {
18520
18543
  cells = { top: y, height: 0, items: [], x0: item.x, x1: item.x + ncol * item.w + (ncol - 1) * gap, block: void 0 };
@@ -18985,7 +19008,7 @@ register("subreport", {
18985
19008
  if (hidden(item, ctx, lc)) return { height: 0, units: [] };
18986
19009
  const params = {};
18987
19010
  for (const [k, v] of Object.entries(item.params || {})) {
18988
- const r = evalSafe(v, ctx, lc, `${item.name} parameter ${k}`);
19011
+ const r = evalSafe(v, ctx, lc, `${item.name || item.type} parameter ${k}`);
18989
19012
  params[k] = r.v instanceof Date ? isoDate(r.v, ctx.timeZone) : r.v;
18990
19013
  }
18991
19014
  const key = `${item.report}|${JSON.stringify(params)}`;
@@ -19004,10 +19027,10 @@ register("subreport", {
19004
19027
  const res = sub.results.get(key);
19005
19028
  if (!res) return placeholder(`Subreport "${item.report}" did not load`);
19006
19029
  if (res.error) {
19007
- lc.warn(`${item.name}: ${res.error}`);
19030
+ lc.warn(`${item.name || item.type}: ${res.error}`);
19008
19031
  return placeholder(`Subreport "${item.report}": ${res.error}`);
19009
19032
  }
19010
- const block = item.keepTogether ? `s:${item.name}:${lc.nextId()}` : void 0;
19033
+ const block = item.keepTogether ? `s:${item.name || item.type}:${lc.nextId()}` : void 0;
19011
19034
  const units = res.units.map((u) => ({ ...u, items: shiftItems(u.items, item.x, 0), ...u.cont ? { cont: shiftItems(u.cont, item.x, 0) } : {}, x0: (u.x0 ?? 0) + item.x, x1: (u.x1 ?? item.w) + item.x, block: u.block || block, tableId: void 0 }));
19012
19035
  const tables = res.tables.map((t) => ({ ...t, x0: t.x0 + item.x, x1: t.x1 + item.x, headerItems: shiftItems(t.headerItems, item.x, 0), id: `sub${lc.nextId()}` }));
19013
19036
  for (const r of res.regions || []) lc.regions?.push(r);
@@ -22027,6 +22050,7 @@ var SCHEMA_ALIAS = "reportwright/report@1";
22027
22050
  var ITEM_KEYS = new Set("id type name x y w h style styleName items hidden layer locked label alt value dataSet canGrow canShrink action bookmark tooltip keepTogether pageBreakBefore pageBreakAfter pageBreakBetween newSection filters sort groups groupBy groupAdjacent inheritRows columns header detail footer repeatHeader widowControl noRowsText pageBreakEvery cells rows rowGroup rowGroups columnGro\
22028
22051
  up columnGroups values value valueFormat rowSort columnSort rowTotals columnTotals cellStyle headerStyle totalStyle corner partProps library part properties overflow overflowTo src fit headingLevel rotate shrinkToFit minFontSize shrinkStep tocDots tocLink tocPage format chartType series seriesGroup category title legend palette stacked valueAxis xTitle maxCategories outliers categorySort categoryF\
22029
22052
  ormat showValues labelPosition symbology quietZone report params text toggle initiallyCollapsed collapsible expandLevels repeatColumns columnGap rowHeaderWidth anim animation visual comment notes description".split(" "));
22053
+ var NO_SERIES_CHARTS = /* @__PURE__ */ new Set(["gantt"]);
22030
22054
  function looksLikeLink(it) {
22031
22055
  if (it.type !== "textbox" || it.action && it.action.type && it.action.type !== "none") return false;
22032
22056
  const st = it.style && typeof it.style === "object" ? it.style : {};
@@ -22155,6 +22179,9 @@ function validate(def) {
22155
22179
  if (p?.hidden && p.required && (p.default == null || p.default === "") && !p.nullable) {
22156
22180
  warnings.push(`Parameter "${p.name}" is hidden and required but has no default: only a URL, the API or a drill-through can set it`);
22157
22181
  }
22182
+ if (p?.required && p.nullable && !p.multi && (p.default == null || p.default === "")) {
22183
+ warnings.push(`Parameter "${p.name}" is required and nullable with no default: when no value is given it is null and the report can draw blank; give it a default, or drop "nullable" to require a value`);
22184
+ }
22158
22185
  if (p?.type === "date" && typeof p.default === "string" && p.default.trim() && !p.default.startsWith("=") && !toDate(p.default)) {
22159
22186
  warnings.push(`Parameter "${p.name}": its default "${p.default}" is not a date; write it as 2026-12-31 (year-month-day)`);
22160
22187
  }
@@ -22235,7 +22262,7 @@ function validate(def) {
22235
22262
  warnings.push(`${label}: there is no named style "${it.styleName}"${g ? ` (did you mean "${g}"?)` : ""}; it is ignored`);
22236
22263
  }
22237
22264
  if (Array.isArray(it.sort)) sortIssues(it.sort, label, warnings);
22238
- if (it.type === "chart" && Array.isArray(it.series) && !it.series.length) warnings.push(`${label}: the chart has no series, so it draws nothing; add one, e.g. series: [{ "name": "Value", "value": "=Sum(Fields.Amount)" }]`);
22265
+ if (it.type === "chart" && !NO_SERIES_CHARTS.has(it.chartType) && !(Array.isArray(it.series) && it.series.length)) warnings.push(`${label}: the chart has no series, so it plots a count of its rows; add one, e.g. series: [{ "name": "Value", "value": "=Sum(Fields.Amount)" }]`);
22239
22266
  if (it.type === "image" && typeof it.src === "string" && !it.src.startsWith("=") && LOCAL_IMAGE.test(it.src.trim())) warnings.push(`${label}: "${it.src}" is a local file path, which a browser cannot load; embed the image (images: { name: "data:image/png;base64,..." } and src: "embedded:name") or use an https:// address`);
22240
22267
  if (it.style != null && (typeof it.style !== "object" || Array.isArray(it.style))) warnings.push(`${label}: "style" must be an object of style properties, such as { "fontWeight": "bold" }; ${JSON.stringify(it.style).slice(0, 40)} is ignored`);
22241
22268
  const own = ownKeys(it.type);
@@ -22278,6 +22305,9 @@ function validate(def) {
22278
22305
  const n5 = Array.isArray(it.columns) ? it.columns.length : 0;
22279
22306
  const rowsOf = [["header", it.header], ["detail", it.detail], ["footer", it.footer], ...(it.groups || []).flatMap((g, gi) => [[`group ${gi + 1} header`, g?.header], [`group ${gi + 1} footer`, g?.footer]])];
22280
22307
  for (const [part2, rows] of rowsOf) (Array.isArray(rows) ? rows : []).forEach((row, ri) => {
22308
+ const cells = Array.isArray(row?.cells) ? row.cells : [];
22309
+ const placed = cells.length < n5 && cells.some((c) => Number(c?.colSpan) > 1) ? cells.reduce((s, c) => s + Math.max(1, Math.floor(Number(c?.colSpan)) || 1), 0) : cells.length;
22310
+ if (placed < n5) warnings.push(`${label} ${part2} row ${ri + 1}: the row has ${placed} of ${n5} cells and no colSpan covers the other ${n5 - placed}; add the cells or a colSpan`);
22281
22311
  for (const [k, j] of rowCells(row?.cells, n5).covered) warnings.push(`${label} ${part2} row ${ri + 1}: cell ${k} is covered by colSpan of cell ${j}, so it is not drawn (leave it null, or omit the spanned cells: a row with fewer cells than columns places each cell after the previous one's span)`);
22282
22312
  });
22283
22313
  }
@@ -22693,7 +22723,7 @@ function* textsOf(def, dataSets) {
22693
22723
  }
22694
22724
  }
22695
22725
  }
22696
- var NO_CJK_FONTS = "Text in Japanese/Chinese/Korean needs the optional CJK fonts: npm run fonts:cjk";
22726
+ var NO_CJK_FONTS = "Text in Japanese/Chinese/Korean needs the optional CJK fonts: npm i @reportwright/fonts-cjk, then npx reportwright-fonts-cjk (npm); in the app, npm run fonts:cjk";
22697
22727
  async function render(def0, opts = {}) {
22698
22728
  const t0 = Date.now();
22699
22729
  const reuseWarnings = [];
@@ -22764,6 +22794,7 @@ async function render(def0, opts = {}) {
22764
22794
  const base = { ...BASE_STYLE, ...def.styles?.base || {} };
22765
22795
  let idSeq = 0;
22766
22796
  const regions = [];
22797
+ const tablesSeen = { n: 0 };
22767
22798
  const imageBudget = opts.imageBudget || { seen: /* @__PURE__ */ new Set(), bytes: 0 };
22768
22799
  const makeLc = (globals, fixed2) => {
22769
22800
  const lc = {
@@ -22774,6 +22805,7 @@ async function render(def0, opts = {}) {
22774
22805
  fixed: fixed2,
22775
22806
  maxUnitHeight: contentH,
22776
22807
  state: opts.state || {},
22808
+ tablesSeen,
22777
22809
  regions: opts.exportData || opts.dataOnly ? regions : null,
22778
22810
  dataOnly: !!opts.dataOnly,
22779
22811
  // exportData: true: table data and cell tags (Excel, CSV, Word, HTML, accessible PDF)
@@ -22891,8 +22923,8 @@ async function render(def0, opts = {}) {
22891
22923
  u.breakBefore = false;
22892
22924
  u.breakAfter = false;
22893
22925
  }
22894
- const slices = paginate(body.units, body.tables, g.cH, warn);
22895
- parts2.push({ body, slices, g });
22926
+ const slices2 = paginate(body.units, body.tables, g.cH, warn);
22927
+ parts2.push({ body, slices: slices2, g });
22896
22928
  if (lc.rowBudget?.cut) break sections;
22897
22929
  if (!overflow.more) break;
22898
22930
  if (rep + 1 >= REUSE_LIMITS.repeats) {
@@ -22933,7 +22965,7 @@ async function render(def0, opts = {}) {
22933
22965
  const headings = [];
22934
22966
  const pages2 = [];
22935
22967
  list2.forEach(({ part: part2, k, last }, n5) => {
22936
- const { body, slices, g } = part2;
22968
+ const { body, slices: slices2, g } = part2;
22937
22969
  const { pg, colCount, colW, colGap, cH } = g;
22938
22970
  const ph = parts2.length === 1 ? pageH : pg.height;
22939
22971
  const ml = pg.margins.left, mt = pg.margins.top;
@@ -22971,7 +23003,7 @@ async function render(def0, opts = {}) {
22971
23003
  band(g.hdr, mt, g.own.h ? k === 0 : isFirst, g.own.h ? last : isLast);
22972
23004
  const frozen = [];
22973
23005
  for (let c = 0; c < colCount; c++) {
22974
- const p = slices[k * colCount + c];
23006
+ const p = slices2[k * colCount + c];
22975
23007
  if (!p) break;
22976
23008
  const cx = ml + c * (colW + colGap);
22977
23009
  for (const t of p.repeat) placeItems(t.headerItems, cx, bodyTop, items);
@@ -23003,7 +23035,7 @@ async function render(def0, opts = {}) {
23003
23035
  items.length = j;
23004
23036
  const out = frozen.length ? { items, frozen } : { items };
23005
23037
  if (win) {
23006
- const u = slices[k * colCount]?.units[0];
23038
+ const u = slices2[k * colCount]?.units[0];
23007
23039
  if (u?.dataRow != null) {
23008
23040
  out.firstRow = u.dataRow;
23009
23041
  out.rowHead = !!u.rowHead;
@@ -23077,7 +23109,7 @@ async function render(def0, opts = {}) {
23077
23109
  if (m.noGlyph.size) {
23078
23110
  const cjk2 = [...m.noGlyph].some((c) => /[\u1100-\u11ff\u2600-\u27bf\u2b00-\u2bff\u3000-\u9fff\uac00-\ud7af\uf900-\ufaff\uff00-\uffef]|[\u{1f000}-\u{1faff}]/u.test(c));
23079
23111
  const list2 = [...m.noGlyph].slice(0, 20).join(" ");
23080
- warn(cjk2 ? `${NO_CJK_FONTS} (the app), or npm i @reportwright/fonts-cjk then npx reportwright-fonts-cjk (the npm engine). Until then these characters show as boxes: ${list2}.` : `No font has these characters, so they show as boxes: ${list2}. Upload a font that has them and use it for this text.`);
23112
+ warn(cjk2 ? `${NO_CJK_FONTS}. Until then these characters show as boxes: ${list2}.` : `No font has these characters, so they show as boxes: ${list2}. Upload a font that has them and use it for this text.`);
23081
23113
  }
23082
23114
  if (opts.bodyOnly) return { units: result.body.units, tables: result.body.tables, height: result.body.height, regions, warnings, clipped: clipped() };
23083
23115
  if (opts.dataOnly) return {
@@ -23117,6 +23149,7 @@ async function render(def0, opts = {}) {
23117
23149
  headings: result.headings || [],
23118
23150
  pageStarts: result.pageStarts || [],
23119
23151
  regions: opts.exportData ? regions : null,
23152
+ hasTables: tablesSeen.n > 0,
23120
23153
  name: def.name || "",
23121
23154
  partial: cutRows(),
23122
23155
  timeZone: timeZone || null,
@@ -24144,7 +24177,7 @@ function cellVisuals(items, used, tables) {
24144
24177
 
24145
24178
  // src/exporters/regions.js
24146
24179
  function needsTableData(model, what) {
24147
- if (model && model.regions === null) throw Object.assign(new Error(`${what} needs the report's table data: render it with { exportData: true }`), { code: "NO_EXPORT_DATA" });
24180
+ if (model && model.regions === null && model.hasTables !== false) throw Object.assign(new Error(`${what} needs the report's table data: render it with { exportData: true }`), { code: "NO_EXPORT_DATA" });
24148
24181
  }
24149
24182
  function mergedRegions(model) {
24150
24183
  const map = /* @__PURE__ */ new Map();
@@ -24595,6 +24628,10 @@ var n4 = (v) => Math.round(v * 1e4) / 1e4;
24595
24628
  var hex4 = (id) => id.toString(16).padStart(4, "0");
24596
24629
  var KAPPA = 4 * ((Math.SQRT2 - 1) / 3);
24597
24630
  var cleanText = (t) => t.replace(/\t|\u0085|\u2028|\u2029/g, " ").replace(/[\b\v]/g, "");
24631
+ var formatAt = (run, cl) => {
24632
+ const cp = run.text.codePointAt(cl);
24633
+ return cp !== void 0 && FORMAT_CHAR.test(String.fromCodePoint(cp));
24634
+ };
24598
24635
  function drawRun(c, run, x, y, size, pf, color2, spans2, texts) {
24599
24636
  const f4 = pf.embedder.font;
24600
24637
  const per = 1e3 / size;
@@ -24647,7 +24684,7 @@ function drawRun(c, run, x, y, size, pf, color2, spans2, texts) {
24647
24684
  rise = gy;
24648
24685
  }
24649
24686
  const adj = Math.round((pen - gx) * per * 1e3) / 1e3;
24650
- if (id === 0 && c.tagged) pen = gx;
24687
+ if (id === 0 && (c.tagged || formatAt(run, cl))) pen = gx;
24651
24688
  else {
24652
24689
  arr += (adj ? `${adj}` : "") + `<${hex4(id)}>`;
24653
24690
  pen = gx + adv(id) * size / f4.unitsPerEm;
@@ -24706,6 +24743,7 @@ var Content = class {
24706
24743
  this.saved = [];
24707
24744
  this.lines = null;
24708
24745
  this.tagged = false;
24746
+ this.artLines = false;
24709
24747
  }
24710
24748
  /**
24711
24749
  * The operators so far. Any other drawing reads or appends to it, so the lines waiting are drawn first: the text
@@ -24724,7 +24762,7 @@ var Content = class {
24724
24762
  * ponytail: lines move after the text drawn just after them; both are thin and do not cover each other.
24725
24763
  */
24726
24764
  line(stroke, w, dash, x1, y1, x2, y2) {
24727
- if (this.tagged) {
24765
+ if (this.tagged && !this.artLines) {
24728
24766
  this.paint(null, stroke, w, dash);
24729
24767
  this._s += `${n2(x1)} ${n2(y1)} m ${n2(x2)} ${n2(y2)} l S
24730
24768
  `;
@@ -24744,6 +24782,8 @@ var Content = class {
24744
24782
  if (!L) return;
24745
24783
  this.lines = null;
24746
24784
  this.et();
24785
+ const end = this.artLines ? "EMC\n" : "";
24786
+ if (end) this._s += "/Artifact BMC\n";
24747
24787
  this.paint(null, L.stroke, L.w, L.dash);
24748
24788
  const g = L.segs;
24749
24789
  const dy = g.length > 2 ? Math.round((g[1][1] - g[0][1]) * 100) : 0;
@@ -24752,10 +24792,10 @@ var Content = class {
24752
24792
  `;
24753
24793
  this._s += `q
24754
24794
  ${one}${`1 0 0 1 0 ${dy / 100} cm ${one}`.repeat(g.length - 1)}Q
24755
- `;
24795
+ ${end}`;
24756
24796
  return;
24757
24797
  }
24758
- this._s += g.map(([a, b, c, d]) => `${n2(a)} ${n2(b)} m ${n2(c)} ${n2(d)} l`).join(" ") + " S\n";
24798
+ this._s += g.map(([a, b, c, d]) => `${n2(a)} ${n2(b)} m ${n2(c)} ${n2(d)} l`).join(" ") + " S\n" + end;
24759
24799
  }
24760
24800
  q() {
24761
24801
  this.et();
@@ -25541,6 +25581,7 @@ function createSubsetter(loadWasm) {
25541
25581
  for (const g of opt.glyphs) x.hb_set_add(gs, g);
25542
25582
  }
25543
25583
  x.hb_subset_input_pin_all_axes_to_default(input, face);
25584
+ x.hb_subset_input_pin_axis_location(input, face, tag3("wght"), opt.weight ?? 400);
25544
25585
  const sub = x.hb_subset_or_fail(face, input);
25545
25586
  if (!sub) throw new Error("HarfBuzz: subsetting failed");
25546
25587
  const res = x.hb_face_reference_blob(sub);
@@ -25561,6 +25602,7 @@ function createSubsetter(loadWasm) {
25561
25602
 
25562
25603
  // src/exporters/htmldata.js
25563
25604
  init_format();
25605
+ var dateKey = (d, tz) => tz ? toWall(d, tz) : Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds());
25564
25606
  var MAX_DATA_ROWS = 2e4;
25565
25607
  var DATA_STRINGS = { pages: "Pages", tables: "Tables", all: "All", filter: "Filter", toggle: "Show or hide the rows of this group", sortAsc: "Sort ascending", sortDesc: "Sort descending" };
25566
25608
  function tablesData(model, o = {}) {
@@ -25583,7 +25625,7 @@ function tablesData(model, o = {}) {
25583
25625
  rows: r.rows.map((row) => ({
25584
25626
  k: row.kind,
25585
25627
  l: row.level || 0,
25586
- c: row.cells.filter((c) => c.colIndex >= 0).map((c) => [c.colIndex, String(show(c) ?? ""), c.value instanceof Date ? c.value.getTime() : typeof c.value === "number" ? c.value : String(c.value ?? "").toLowerCase(), c.span || 1])
25628
+ c: row.cells.filter((c) => c.colIndex >= 0).map((c) => [c.colIndex, String(show(c) ?? ""), c.value instanceof Date ? dateKey(c.value, model.timeZone || void 0) : typeof c.value === "number" ? c.value : String(c.value ?? "").toLowerCase(), c.span || 1])
25587
25629
  }))
25588
25630
  }));
25589
25631
  }
@@ -25715,13 +25757,29 @@ async function cspHash(text2) {
25715
25757
  }
25716
25758
 
25717
25759
  // src/exporters/html.js
25718
- function exportHtml(model, fontStore2, opt = {}) {
25760
+ function exportHtml(model, options, legacyOpt) {
25761
+ const isStore = (x) => !!x && typeof x.load === "function" && typeof x.get === "function" && x.bytes instanceof Map;
25762
+ let fontStore2, opt;
25763
+ if (isStore(options)) {
25764
+ if (!warnedLegacy) {
25765
+ warnedLegacy = true;
25766
+ console.warn("[reportwright] DEPRECATED: exportHtml(model, fontStore, options) is replaced by exportHtml(model, { fonts: fontStore, ...options }); the old form is removed in 0.9");
25767
+ }
25768
+ fontStore2 = options;
25769
+ opt = legacyOpt || {};
25770
+ } else {
25771
+ opt = /** @type {any} */
25772
+ options || {};
25773
+ fontStore2 = opt.fonts || opt.fontStore;
25774
+ if (!isStore(fontStore2)) throw new TypeError("exportHtml(model, { fonts }): fonts must be a FontStore (defaultFontStore() or new FontStore(loader)), got " + (fontStore2 === void 0 ? "nothing" : typeof fontStore2));
25775
+ }
25719
25776
  return withDeadline(
25720
25777
  htmlOf(model, fontStore2, opt),
25721
25778
  /** @type {any} */
25722
25779
  opt.timeoutMs
25723
25780
  );
25724
25781
  }
25782
+ var warnedLegacy = false;
25725
25783
  async function htmlOf(model, fontStore2, opt) {
25726
25784
  const used = /* @__PURE__ */ new Map();
25727
25785
  const layout = /* @__PURE__ */ new Set();
@@ -26473,9 +26531,22 @@ async function xlsxOf(model, opt) {
26473
26531
  }
26474
26532
 
26475
26533
  // src/exporters/docx.js
26476
- import { zipSync as zipSync3, strToU8 as strToU83 } from "fflate";
26534
+ import { Zip, ZipDeflate, strToU8 as strToU83 } from "fflate";
26477
26535
  var TW = 20;
26478
26536
  var EMU = 12700;
26537
+ var XML_CHUNK = 4 * 1024 * 1024;
26538
+ function slices(s, max) {
26539
+ const out = [];
26540
+ let i = 0;
26541
+ while (s.length - i > max) {
26542
+ const j = s.indexOf(">", i + max);
26543
+ if (j < 0) break;
26544
+ out.push(s.slice(i, j + 1));
26545
+ i = j + 1;
26546
+ }
26547
+ out.push(s.slice(i));
26548
+ return out;
26549
+ }
26479
26550
  var esc3 = (s) => String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
26480
26551
  var hex5 = (c) => {
26481
26552
  const m = /^#?([0-9a-f]{6})$/i.exec(String(c || "").trim());
@@ -26619,9 +26690,9 @@ async function docxOf(model, opt) {
26619
26690
  const margin = 18;
26620
26691
  const media = [];
26621
26692
  let docPr = 0;
26622
- const addMedia = (bytes, ext) => {
26693
+ const addMedia = (bytes2, ext) => {
26623
26694
  const rid = `rIdm${media.length + 1}`;
26624
- media.push({ name: `image${media.length + 1}.${ext}`, bytes, rid });
26695
+ media.push({ name: `image${media.length + 1}.${ext}`, bytes: bytes2, rid });
26625
26696
  return rid;
26626
26697
  };
26627
26698
  const pictureRun = (pic, alt) => {
@@ -26685,7 +26756,7 @@ async function docxOf(model, opt) {
26685
26756
  }
26686
26757
  for (const f4 of figs) if (!f4.used) blocks.push({ kind: "fig", y: f4.box.y, x: f4.box.x, fig: f4 });
26687
26758
  blocks.sort((a, b) => a.y - b.y || a.x - b.x);
26688
- let xml3 = "";
26759
+ let xml2 = "";
26689
26760
  for (let bi = 0; bi < blocks.length; bi++) {
26690
26761
  const b = blocks[bi];
26691
26762
  if (b.kind === "fig") {
@@ -26700,7 +26771,7 @@ async function docxOf(model, opt) {
26700
26771
  right = pic.x + pic.w;
26701
26772
  }
26702
26773
  const first = row[0];
26703
- xml3 += `<w:p><w:pPr><w:spacing w:before="0" w:after="80"/>${Math.abs(first.x - margin) > 0.5 ? `<w:ind w:left="${Math.round(Math.max(-margin, first.x - margin) * TW)}"/>` : ""}</w:pPr>${runs}</w:p>`;
26774
+ xml2 += `<w:p><w:pPr><w:spacing w:before="0" w:after="80"/>${Math.abs(first.x - margin) > 0.5 ? `<w:ind w:left="${Math.round(Math.max(-margin, first.x - margin) * TW)}"/>` : ""}</w:pPr>${runs}</w:p>`;
26704
26775
  } else if (b.kind === "table") {
26705
26776
  const pics = /* @__PURE__ */ new Map();
26706
26777
  for (const [k, v] of visuals.get(b.key) || []) {
@@ -26708,19 +26779,19 @@ async function docxOf(model, opt) {
26708
26779
  const svg = figureSvg({ items: v.items, box: v.box }, opt.fontStore);
26709
26780
  pics.set(k, pictureRun({ svg, png: await rasterize(svg, v.box.w, v.box.h, picOpt), w: v.box.w, h: v.box.h }, "Chart in a table cell"));
26710
26781
  }
26711
- xml3 += table(b.cells, b.x - margin, pics);
26712
- } else if (b.kind === "rich") xml3 += richPara(b.items, b.x - margin);
26782
+ xml2 += table(b.cells, b.x - margin, pics);
26783
+ } else if (b.kind === "rich") xml2 += richPara(b.items, b.x - margin);
26713
26784
  else {
26714
26785
  const row = [b];
26715
26786
  const top = (t) => t.it.clip?.y ?? t.y - t.it.size;
26716
26787
  while (blocks[bi + 1]?.kind === "text" && Math.abs(top(blocks[bi + 1]) - top(b)) < 3 && blocks[bi + 1].it.clip && b.it.clip) row.push(blocks[++bi]);
26717
26788
  row.sort((p, q3) => p.it.clip.x - q3.it.clip.x);
26718
26789
  const apart = row.every((r, k) => k === 0 || r.it.clip.x >= row[k - 1].it.clip.x + row[k - 1].it.clip.w - 0.5);
26719
- if (row.length > 1 && apart) xml3 += layoutRow(row, margin);
26720
- else for (const r of row) xml3 += para(r.it, (r.it.clip?.x ?? r.x) - margin, r.decor);
26790
+ if (row.length > 1 && apart) xml2 += layoutRow(row, margin);
26791
+ else for (const r of row) xml2 += para(r.it, (r.it.clip?.x ?? r.x) - margin, r.decor);
26721
26792
  }
26722
26793
  }
26723
- body.push(xml3);
26794
+ body.push(xml2);
26724
26795
  }
26725
26796
  const M = margin * TW, V = 36 * TW;
26726
26797
  const sectPr = (pg) => {
@@ -26728,17 +26799,39 @@ async function docxOf(model, opt) {
26728
26799
  return `<w:sectPr><w:pgSz w:w="${W2}" w:h="${H}"${W2 > H ? ' w:orient="landscape"' : ""}/><w:pgMar w:top="${V}" w:right="${M}" w:bottom="${V}" w:left="${M}" w:header="0" w:footer="0" w:gutter="0"/></w:sectPr>`;
26729
26800
  };
26730
26801
  const sizeOf = (pg) => `${pg.width ?? model.width}x${pg.height ?? model.height}`;
26731
- let xml2 = "";
26802
+ const zipOut = [];
26803
+ const zip = new Zip((err, data) => {
26804
+ if (err) throw err;
26805
+ zipOut.push(data);
26806
+ });
26807
+ const put = (name, bytes2) => {
26808
+ const e2 = new ZipDeflate(name, { level: 6 });
26809
+ zip.add(e2);
26810
+ e2.push(bytes2, true);
26811
+ };
26812
+ const parts = [];
26813
+ const max = opt.xmlChunk || XML_CHUNK;
26814
+ let buf = "";
26815
+ const write = (str) => {
26816
+ for (const p of slices(str, max)) {
26817
+ if (buf.length + p.length > max) {
26818
+ parts.push(buf);
26819
+ buf = "";
26820
+ }
26821
+ buf += p;
26822
+ }
26823
+ };
26824
+ write(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
26825
+ <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"><w:body>`);
26732
26826
  model.pages.forEach((pg, i) => {
26733
- xml2 += body[i];
26827
+ write(body[i]);
26734
26828
  const next = model.pages[i + 1];
26735
26829
  if (!next) return;
26736
- xml2 += sizeOf(next) !== sizeOf(pg) ? `<w:p><w:pPr>${sectPr(pg)}</w:pPr></w:p>` : '<w:p><w:pPr><w:pageBreakBefore/><w:spacing w:before="0" w:after="0" w:line="20" w:lineRule="exact"/><w:rPr><w:sz w:val="2"/></w:rPr></w:pPr></w:p>';
26830
+ write(sizeOf(next) !== sizeOf(pg) ? `<w:p><w:pPr>${sectPr(pg)}</w:pPr></w:p>` : '<w:p><w:pPr><w:pageBreakBefore/><w:spacing w:before="0" w:after="0" w:line="20" w:lineRule="exact"/><w:rPr><w:sz w:val="2"/></w:rPr></w:pPr></w:p>');
26737
26831
  });
26738
26832
  const last = model.pages[model.pages.length - 1] || {};
26739
- const document2 = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
26740
- <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"><w:body>${xml2}${sectPr(
26741
- last)}</w:body></w:document>`;
26833
+ write(`${sectPr(last)}</w:body></w:document>`);
26834
+ parts.push(buf);
26742
26835
  const lang = `<w:lang w:val="${esc3(opt.lang || model.locale || "en-US")}"/>`;
26743
26836
  const headings = [28, 24, 22, 20, 18, 18].map((sz, i) => `<w:style w:type="paragraph" w:styleId="Heading${i + 1}"><w:name w:val="heading ${i + 1}"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/><w:qFormat/><w:pPr><w:keepNext/><w:outlineLvl w:val="${i}"/></w:pPr><w:rPr><w:b/><w:sz w:val="${sz}"/></w:rPr></w:style>`).join("");
26744
26837
  const styles = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
@@ -26748,24 +26841,32 @@ name w:val="Normal"/></w:style>${headings}</w:styles>`;
26748
26841
  const core = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
26749
26842
  <cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dc:title>${esc3(opt.title || model.name || "Report")}</dc:title>${opt.author ? `<dc:creator>${esc3(opt.author)}</dc:creator>` : ""}<dcterms:created xsi:type=\
26750
26843
  "dcterms:W3CDTF">${now}</dcterms:created></cp:coreProperties>`;
26751
- const files = {
26752
- "[Content_Types].xml": strToU83(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
26844
+ put("[Content_Types].xml", strToU83(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
26753
26845
  <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Default Extension="png" ContentType="image/png"/><Default Extension="jpeg" ContentType="image/jpeg"/><Default Extension="svg" ContentType="image/svg+xml"/><Override Part\
26754
- Name="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/><Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/></Types>`),
26755
- "_rels/.rels": strToU83(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
26756
- <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/></Relationships>`),
26757
- "word/_rels/document.xml.rels": strToU83(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
26758
- <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>${media.map((m) => `<Relationship Id="${m.rid}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/${m.name}"/>`).join("")}</Relationships>`),
26759
- "word/document.xml": strToU83(document2),
26760
- "word/styles.xml": strToU83(styles),
26761
- "docProps/core.xml": strToU83(core)
26762
- };
26763
- for (const m of media) files[`word/media/${m.name}`] = m.bytes;
26764
- return zipSync3(files, { level: 6 });
26846
+ Name="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/><Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/></Types>`));
26847
+ put("_rels/.rels", strToU83(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
26848
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/></Relationships>`));
26849
+ put("word/_rels/document.xml.rels", strToU83(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
26850
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>${media.map((m) => `<Relationship Id="${m.rid}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/${m.name}"/>`).join("")}</Relationships>`));
26851
+ const doc = new ZipDeflate("word/document.xml", { level: 6 });
26852
+ zip.add(doc);
26853
+ for (const p of parts) doc.push(strToU83(p), false);
26854
+ doc.push(new Uint8Array(0), true);
26855
+ put("word/styles.xml", strToU83(styles));
26856
+ put("docProps/core.xml", strToU83(core));
26857
+ for (const m of media) put(`word/media/${m.name}`, m.bytes);
26858
+ zip.end();
26859
+ const total = zipOut.reduce((n5, c) => n5 + c.length, 0), bytes = new Uint8Array(total);
26860
+ let at2 = 0;
26861
+ for (const c of zipOut) {
26862
+ bytes.set(c, at2);
26863
+ at2 += c.length;
26864
+ }
26865
+ return bytes;
26765
26866
  }
26766
26867
 
26767
26868
  // src/exporters/pptx.js
26768
- import { zipSync as zipSync4, strToU8 as strToU84 } from "fflate";
26869
+ import { zipSync as zipSync3, strToU8 as strToU84 } from "fflate";
26769
26870
  var EMU2 = 12700;
26770
26871
  var e = (v) => Math.round(v * EMU2);
26771
26872
  var esc4 = (s) => String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f￾￿]/g, "");
@@ -26948,7 +27049,7 @@ rops/core.xml"/></Relationships>`);
26948
27049
  ="svg" ContentType="image/svg+xml"/><Override PartName="/ppt/presentation.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"/><Override PartName="/ppt/slideMasters/slideMaster1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml"/><Override PartName="/ppt/slideLayouts/slideLayout1.xml" ContentType="applic\
26949
27050
  ation/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml"/><Override PartName="/ppt/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/>${slides.map((n5) => `<Override PartName="/ppt/slides/slide${n5}.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slide+xml"/>`).join("")}<Override PartName="/docProps/core.xml" Conte\
26950
27051
  ntType="application/vnd.openxmlformats-package.core-properties+xml"/></Types>`);
26951
- return zipSync4(files, { level: 6 });
27052
+ return zipSync3(files, { level: 6 });
26952
27053
  }
26953
27054
  var THEME = `${XML2}<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="ReportWright"><a:themeElements><a:clrScheme name="ReportWright"><a:dk1><a:srgbClr val="000000"/></a:dk1><a:lt1><a:srgbClr val="FFFFFF"/></a:lt1><a:dk2><a:srgbClr val="1F2328"/></a:dk2><a:lt2><a:srgbClr val="E7E6E6"/></a:lt2><a:accent1><a:srgbClr val="2563EB"/></a:accent1><a:accent2><a:srgbClr val="F5\
26954
27055
  9E0B"/></a:accent2><a:accent3><a:srgbClr val="16A34A"/></a:accent3><a:accent4><a:srgbClr val="DC2626"/></a:accent4><a:accent5><a:srgbClr val="7C3AED"/></a:accent5><a:accent6><a:srgbClr val="0891B2"/></a:accent6><a:hlink><a:srgbClr val="0563C1"/></a:hlink><a:folHlink><a:srgbClr val="954F72"/></a:folHlink></a:clrScheme><a:fontScheme name="ReportWright"><a:majorFont><a:latin typeface="Inter"/><a:ea t\
@@ -26968,6 +27069,7 @@ pw test [folder] [--update] [--layout]
26968
27069
  render every <folder>/__tests__/*.test.json case and compare it with its snapshot
26969
27070
  (the words; --layout: the layout, see the CLI README)
26970
27071
  pw diff <old.pw.json> <new.pw.json> [-p name=value]… [-d source=data.json]… [--base URL]
27072
+ SERVER ONLY (the self-hosted app, run in its folder; the npm CLI does not include these):
26971
27073
  pw audit verify [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--json] [--public-key file.pem]
26972
27074
  check the server's audit log (hash chain, signed checkpoints); run it in the app
26973
27075
  folder with the server's PW_STORAGE and PW_AUDIT_KEY / PW_SESSION_SECRET
@@ -26984,7 +27086,7 @@ pw audit rekey --old-secret S | --old-key file.pem
26984
27086
  --ua an accessible PDF (PDF/UA-1; with --pdfa, PDF/A-2a) --values Excel totals as values, not formulas
26985
27087
  --layout CSV of the printed layout (every table, group lines, totals), not just the data
26986
27088
  --static an HTML file without its Tables view (sort, groups, filter)
26987
- pw migrate-storage --to <url> [--from file|<url>]
27089
+ pw migrate-storage --to <url> [--from file|<url>] (SERVER ONLY, as above)
26988
27090
  copy everything (reports and their versions, users, grants, folders, schedules,
26989
27091
  jobs, fonts, settings, blobs, the audit log) from one storage to another, e.g. the
26990
27092
  files in this folder to sqlite:/srv/pw.db or postgres://…; stop the server first
@@ -27030,7 +27132,9 @@ var baseHost = (base, internal) => {
27030
27132
  };
27031
27133
  async function renderModel(def, { parameters = {}, data, base, internal = false, now, state, file, exportData = true, dataOnly = false } = {}) {
27032
27134
  try {
27033
- return await render(withData(def, data), { fontStore, parameters, exportData, dataOnly, baseUrl: base, allowHosts: baseHost(base, internal), now: now ? new Date(now) : void 0, ...state ? { state } : {}, ...file ? { loadReport: siblingLoader(file) } : {} });
27135
+ const m = await render(withData(def, data), { fontStore, parameters, exportData, dataOnly, baseUrl: base, allowHosts: baseHost(base, internal), now: now ? new Date(now) : void 0, ...state ? { state } : {}, ...file ? { loadReport: siblingLoader(file) } : {} });
27136
+ const seen = /* @__PURE__ */ new Set();
27137
+ return { ...m, warnings: m.warnings.filter((w) => !seen.has(w.toLowerCase()) && seen.add(w.toLowerCase())) };
27034
27138
  } catch (e2) {
27035
27139
  const relative = /fetch|URL|Invalid URL/i.test(e2.message) && !base;
27036
27140
  fail(`${e2.message}${relative ? "\n A data source needs a server: pass --base http://localhost:3000, or push the data with -d source=file.json." : ""}`);
@@ -27095,7 +27199,7 @@ async function cmdRender(args) {
27095
27199
  if (encrypt && values.pdfa) usage("a PDF/A file cannot have a password");
27096
27200
  if (values.ua && fmt2 !== "pdf") usage("--ua applies to -f pdf only");
27097
27201
  if (fmt2 === "pdf") bytes = await exportPdf(model, fontStore, { title: def.name, subset, encrypt, drillBase: values.base, pdfa: values.pdfa ? { icc: fs.readFileSync(path.join(PUB, "icc", "sRGB-v2-magic.icc")) } : void 0, tagged: values.ua ? { lang: def.locale || "en" } : void 0 });
27098
- else if (fmt2 === "html") bytes = await exportHtml(model, fontStore, { title: def.name, subset, static: !!values.static, locale: def.locale, currency: def.currency });
27202
+ else if (fmt2 === "html") bytes = await exportHtml(model, { fonts: fontStore, title: def.name, subset, static: !!values.static, locale: def.locale, currency: def.currency });
27099
27203
  else if (fmt2 === "csv") bytes = exportCsv(model, { locale: def.locale, currency: def.currency, layout: !!values.layout });
27100
27204
  else if (fmt2 === "xlsx") bytes = await exportXlsx(model, { currency: def.currency, title: def.name, formulas: !values.values, fontStore });
27101
27205
  else if (fmt2 === "docx") bytes = await exportDocx(model, { title: def.name, lang: def.locale, fontStore });