@lotics/cli 0.56.0 → 0.60.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/src/cli.js CHANGED
@@ -29579,6 +29579,8 @@ var require_lib3 = __commonJS({
29579
29579
  });
29580
29580
 
29581
29581
  // src/cli.ts
29582
+ import dns from "node:dns";
29583
+ import net2 from "node:net";
29582
29584
  import fs7 from "node:fs";
29583
29585
  import path6 from "node:path";
29584
29586
  import readline from "node:readline";
@@ -29586,6 +29588,19 @@ import readline from "node:readline";
29586
29588
  // src/client.ts
29587
29589
  import fs from "node:fs";
29588
29590
  import path from "node:path";
29591
+ function gatewayErrorMessage(status) {
29592
+ if (status === 524) {
29593
+ return "The request took too long to finish (gateway timeout). It may still be running \u2014 check back in a moment, or try again.";
29594
+ }
29595
+ if (status >= 500) {
29596
+ return "The service is temporarily unavailable. Please try again shortly.";
29597
+ }
29598
+ return "The service returned an unexpected response. Please try again.";
29599
+ }
29600
+ function transportErrorMessage(status, parsed) {
29601
+ const jsonMessage = parsed && typeof parsed.message === "string" ? parsed.message : null;
29602
+ return parsed === null || status >= 500 || jsonMessage === null ? gatewayErrorMessage(status) : jsonMessage;
29603
+ }
29589
29604
  function findAvailableFilename(dir, filename, reserved) {
29590
29605
  const isTaken = (name) => {
29591
29606
  const full = path.join(dir, name);
@@ -29731,6 +29746,38 @@ var LoticsClient = class {
29731
29746
  async createApp(body) {
29732
29747
  return this.request("POST", "/v1/apps", body);
29733
29748
  }
29749
+ /**
29750
+ * Resolve the display name + fields (incl. select options) of the given tables
29751
+ * — the schema `lotics app codegen` turns into the runtime `.lotics/app_fields.ts`
29752
+ * alias maps. One `get_table` call per id (the tool surface has no batch
29753
+ * variant); a missing/inaccessible table is dropped rather than throwing, so a
29754
+ * stale id in the scope set never fails codegen.
29755
+ */
29756
+ async getWorkspaceSchema(tableIds) {
29757
+ const tables = await Promise.all(
29758
+ tableIds.map(async (table_id) => {
29759
+ const res = await this.execute("get_table", { table_id });
29760
+ if (res.error || res.result === null || typeof res.result !== "object") return null;
29761
+ const table = res.result;
29762
+ if (typeof table.id !== "string" || typeof table.name !== "string" || !Array.isArray(table.fields)) {
29763
+ return null;
29764
+ }
29765
+ const fields = table.fields.flatMap((field) => {
29766
+ if (field === null || typeof field !== "object") return [];
29767
+ const f = field;
29768
+ if (typeof f.key !== "string" || typeof f.name !== "string") return [];
29769
+ const options = Array.isArray(f.options) ? f.options.flatMap((opt) => {
29770
+ if (opt === null || typeof opt !== "object") return [];
29771
+ const o = opt;
29772
+ return typeof o.key === "string" && typeof o.name === "string" ? [{ id: o.key, label: o.name }] : [];
29773
+ }) : void 0;
29774
+ return [{ id: f.key, name: f.name, ...options && options.length > 0 ? { options } : {} }];
29775
+ });
29776
+ return { id: table.id, name: table.name, fields };
29777
+ })
29778
+ );
29779
+ return tables.filter((t) => t !== null);
29780
+ }
29734
29781
  /**
29735
29782
  * Rename an app's public subdomain — its `<slug>.lotics.app` address.
29736
29783
  * Mirrors PUT /v1/apps/{app_id}/subdomain. The old subdomain stops
@@ -29785,10 +29832,70 @@ var LoticsClient = class {
29785
29832
  * Mirrors POST /v1/apps/{app_id}/workflows/{alias}/execute.
29786
29833
  */
29787
29834
  async appWorkflow(app_id, alias, inputs) {
29835
+ const url = `${this.baseUrl}/v1/apps/${encodeURIComponent(app_id)}/workflows/${encodeURIComponent(alias)}/execute`;
29836
+ const headers = this.buildHeaders();
29837
+ headers["Content-Type"] = "application/json";
29838
+ let response;
29839
+ try {
29840
+ response = await fetch(url, { method: "POST", headers, body: JSON.stringify({ inputs }) });
29841
+ } catch (err2) {
29842
+ return { status: "error", message: err2 instanceof Error ? err2.message : "The workflow request failed." };
29843
+ }
29844
+ const text = await response.text();
29845
+ let parsed = null;
29846
+ if (text) {
29847
+ try {
29848
+ parsed = JSON.parse(text);
29849
+ } catch {
29850
+ }
29851
+ }
29852
+ if (response.ok) return parsed ?? {};
29853
+ return { status: "error", message: transportErrorMessage(response.status, parsed) };
29854
+ }
29855
+ /**
29856
+ * Bind (create or replace) an app workflow by alias via the `set_app_workflow`
29857
+ * tool — the SINGLE author of `apps.workflows` + the workflow row. `source` is
29858
+ * the verbatim JS-subset body (no `on({...})` trigger). `inputs`/`outputs` are
29859
+ * the typed schemas declared in `package.json#lotics.workflows.<alias>`. The
29860
+ * server re-verifies the body and echoes the bound `outputs` (declared, else
29861
+ * DERIVED from `return({ data })`), so the CLI can show the author what shape
29862
+ * `result.data` will carry. Wraps the tool rather than a bespoke endpoint so
29863
+ * the file flow stays a convenience over the existing single-author contract.
29864
+ */
29865
+ async setAppWorkflow(app_id, alias, body) {
29866
+ return this.execute("set_app_workflow", {
29867
+ app_id,
29868
+ alias,
29869
+ source: body.source,
29870
+ ...body.inputs ? { inputs: body.inputs } : {},
29871
+ ...body.outputs ? { outputs: body.outputs } : {},
29872
+ ...body.name ? { name: body.name } : {},
29873
+ ...body.description ? { description: body.description } : {}
29874
+ });
29875
+ }
29876
+ /**
29877
+ * Fetch one app workflow's faithful source + bound input/output schemas via
29878
+ * `get_app_workflow`. `source` is the JS-subset body re-rendered from the
29879
+ * persisted step tree (incl. the `return({ data })` clause, opaque field/option
29880
+ * keys) — the exact text `lotics app workflow set` would push back. Feeds
29881
+ * `lotics app pull`, which writes it to `src/workflows/<alias>.ts`.
29882
+ */
29883
+ async getAppWorkflow(app_id, alias) {
29884
+ return this.execute("get_app_workflow", { app_id, alias });
29885
+ }
29886
+ /**
29887
+ * Fetch the server-generated workspace `.d.ts` + the wrapper envelope that
29888
+ * make a `src/workflows/<alias>.ts` body locally typecheckable (GAP-59).
29889
+ * The server is the single source of the type model — the CLI never
29890
+ * re-implements it. `envelope_prefix`/`envelope_suffix` are the exact
29891
+ * `async function __workflow(): …` wrapper the server compiles inside, so the
29892
+ * local typecheck mirrors the set-time verdict. Mirrors
29893
+ * POST /v1/apps/{app_id}/workflows/{alias}/dts.
29894
+ */
29895
+ async getAppWorkflowDts(app_id, alias) {
29788
29896
  return this.request(
29789
29897
  "POST",
29790
- `/v1/apps/${encodeURIComponent(app_id)}/workflows/${encodeURIComponent(alias)}/execute`,
29791
- { inputs }
29898
+ `/v1/apps/${encodeURIComponent(app_id)}/workflows/${encodeURIComponent(alias)}/dts`
29792
29899
  );
29793
29900
  }
29794
29901
  /**
@@ -30339,6 +30446,45 @@ function buildStarterTemplate(args) {
30339
30446
  // fall to `unknown`. The explicit glob makes the dot-dir the non-wildcard
30340
30447
  // base, which IS read. (GAP-38.)
30341
30448
  include: ["src", ".lotics/**/*"],
30449
+ // src/workflows/<alias>.ts bodies run on the server (workflow globals:
30450
+ // trigger / runtime / tool calls). They ARE locally typecheckable —
30451
+ // each is wrapped in the server's `__workflow` envelope and references
30452
+ // its per-alias `.lotics/workflows/<alias>.globals.d.ts` — but under a
30453
+ // DEDICATED config that mirrors the server's compile options (es2022
30454
+ // lib, NO DOM). The main `npm run typecheck` excludes them so it stays
30455
+ // green and DOM-lib-free for bodies; check a body with
30456
+ // `tsc -p tsconfig.workflows.json`. The server is the single verifier
30457
+ // on `lotics app workflow set`.
30458
+ exclude: ["node_modules", "src/workflows"]
30459
+ },
30460
+ null,
30461
+ 2
30462
+ ) + "\n"
30463
+ },
30464
+ {
30465
+ // Dedicated config for the editable workflow bodies (GAP-59). Mirrors the
30466
+ // SERVER's set-time typecheck options (strict, lib es2022 with NO DOM,
30467
+ // target ES2022, skipLibCheck) so a local `tsc -p tsconfig.workflows.json`
30468
+ // gives the same verdict the server would on `lotics app workflow set`.
30469
+ // Each body file is a module (the CLI writes a bookkeeping `export {};`),
30470
+ // so the per-file `__workflow` wrapper doesn't collide across bodies.
30471
+ path: "tsconfig.workflows.json",
30472
+ content: JSON.stringify(
30473
+ {
30474
+ compilerOptions: {
30475
+ target: "ES2022",
30476
+ lib: ["ES2022"],
30477
+ module: "ESNext",
30478
+ moduleResolution: "Bundler",
30479
+ strict: true,
30480
+ skipLibCheck: true,
30481
+ noEmit: true,
30482
+ types: []
30483
+ },
30484
+ // The bodies + their per-alias ambient globals. `.lotics/workflows`
30485
+ // uses the explicit `**/*` glob for the same dot-dir walk reason the
30486
+ // main config does (TS's include walk skips bare dot-dirs).
30487
+ include: ["src/workflows", ".lotics/workflows/**/*"],
30342
30488
  exclude: ["node_modules"]
30343
30489
  },
30344
30490
  null,
@@ -30678,6 +30824,60 @@ const routes = [
30678
30824
  export default function App() {
30679
30825
  return <AppRouter routes={routes} />;
30680
30826
  }
30827
+ `
30828
+ },
30829
+ {
30830
+ // The home for editable workflow bodies. `lotics app pull` writes one
30831
+ // `src/workflows/<alias>.ts` per bound workflow (faithful server source);
30832
+ // edit it, then `lotics app workflow set <alias>` pushes it back through
30833
+ // set_app_workflow (the server verifies). A new app has no bound workflows
30834
+ // yet, so this is just the docked directory + the loop reference. It's a
30835
+ // .md (not a .ts) so tsc's `include: ["src"]` never tries to compile it —
30836
+ // a workflow body is a JS-subset expression over server globals
30837
+ // (trigger / runtime / tool calls) that does NOT typecheck standalone.
30838
+ path: "src/workflows/README.md",
30839
+ content: `# Workflow bodies
30840
+
30841
+ Editable JS-subset bodies of this app's workflows live here, one file per alias:
30842
+ \`src/workflows/<alias>.ts\`.
30843
+
30844
+ ## Loop
30845
+
30846
+ \`\`\`bash
30847
+ lotics app workflow pull # write/refresh every src/workflows/<alias>.ts from the server
30848
+ # edit src/workflows/<alias>.ts
30849
+ lotics app workflow set <alias> # push it back through set_app_workflow (the server verifies)
30850
+ \`\`\`
30851
+
30852
+ \`lotics app pull <app_id>\` also writes these files (alongside the rest of the project).
30853
+
30854
+ ## What a body is
30855
+
30856
+ A workflow body is a **JS-subset expression** that runs server-side using workflow
30857
+ globals (\`trigger\`, \`runtime\`, tool calls) and ends with \`return({ data })\`. Each
30858
+ pulled file wraps the body in the server's \`__workflow\` envelope and references its
30859
+ per-alias ambient globals at \`.lotics/workflows/<alias>.globals.d.ts\`, so it **is**
30860
+ locally typecheckable \u2014 under the dedicated config that mirrors the server's compile
30861
+ options:
30862
+
30863
+ \`\`\`bash
30864
+ tsc -p tsconfig.workflows.json # same verdict the server gives on \`set\`
30865
+ \`\`\`
30866
+
30867
+ The main \`npm run typecheck\` excludes \`src/workflows\` (it would apply the app's
30868
+ DOM lib, which the server doesn't), so the bodies have their own config. The
30869
+ verification that matters still runs on the **server** when you \`set\` \u2014 the same
30870
+ guarantee as authoring via \`set_app_workflow\` directly. Edit only the body BETWEEN
30871
+ the wrapper lines; the wrapper, the \`/// <reference>\`, and the \`export {};\` marker
30872
+ are CLI bookkeeping (stripped on \`set\`). Do not rename a file (the filename is the
30873
+ alias the binding is keyed by).
30874
+
30875
+ ## Authority
30876
+
30877
+ \`lotics app deploy\` never authors workflows \u2014 it carries code, queries, and
30878
+ capabilities only. \`apps.workflows\` has exactly one author: \`set_app_workflow\`
30879
+ (which \`lotics app workflow set\` calls). The typed \`inputs\`/\`outputs\` schema for
30880
+ each alias lives in \`package.json#lotics.workflows.<alias>\`.
30681
30881
  `
30682
30882
  },
30683
30883
  {
@@ -30867,6 +31067,18 @@ import http from "node:http";
30867
31067
  import net from "node:net";
30868
31068
  import { spawn } from "node:child_process";
30869
31069
 
31070
+ // src/child_env.ts
31071
+ function ipv4ChildEnv(env) {
31072
+ return {
31073
+ ...env,
31074
+ NODE_OPTIONS: [
31075
+ env.NODE_OPTIONS,
31076
+ "--dns-result-order=ipv4first",
31077
+ "--network-family-autoselection-attempt-timeout=2000"
31078
+ ].filter(Boolean).join(" ")
31079
+ };
31080
+ }
31081
+
30870
31082
  // src/dev/rpc_handler.ts
30871
31083
  var SUPPORTED_OPS = /* @__PURE__ */ new Set([
30872
31084
  "query",
@@ -31327,7 +31539,7 @@ async function startDevServer(args) {
31327
31539
  {
31328
31540
  cwd: args.projectDir,
31329
31541
  stdio: [process.stdin.isTTY ? "inherit" : "ignore", "inherit", "inherit"],
31330
- env: { ...process.env, FORCE_COLOR: "1" }
31542
+ env: ipv4ChildEnv({ ...process.env, FORCE_COLOR: "1" })
31331
31543
  }
31332
31544
  );
31333
31545
  let stopped = false;
@@ -31751,6 +31963,162 @@ ${lines.join("\n")}
31751
31963
  `;
31752
31964
  }
31753
31965
 
31966
+ // ../shared/src/app_query_ast.ts
31967
+ function collectQueryTableIds(node) {
31968
+ const result = /* @__PURE__ */ new Set();
31969
+ walk(node, (n) => {
31970
+ if (n.kind === "from_table") result.add(n.table_id);
31971
+ });
31972
+ return result;
31973
+ }
31974
+ function walk(node, visit) {
31975
+ visit(node);
31976
+ switch (node.kind) {
31977
+ case "from_table":
31978
+ return;
31979
+ case "project":
31980
+ case "filter":
31981
+ case "group":
31982
+ case "window":
31983
+ case "sort":
31984
+ case "limit":
31985
+ case "unpivot":
31986
+ walk(node.from, visit);
31987
+ return;
31988
+ case "join":
31989
+ walk(node.left, visit);
31990
+ walk(node.right, visit);
31991
+ return;
31992
+ case "union":
31993
+ for (const child of node.sources) walk(child, visit);
31994
+ return;
31995
+ default: {
31996
+ const _exhaustive = node;
31997
+ void _exhaustive;
31998
+ return;
31999
+ }
32000
+ }
32001
+ }
32002
+
32003
+ // src/generate_app_fields.ts
32004
+ var HEADER4 = `// Auto-generated by 'lotics app codegen' (and app pull/dev/deploy).
32005
+ // DO NOT EDIT \u2014 regenerated from the workspace schema.
32006
+ //
32007
+ // Runtime field + option ids addressed by stable display-name aliases:
32008
+ // record.data[F.<TABLE>.<field>] \u2192 "fld_\u2026"
32009
+ // value === OPT.<TABLE>.<field>.<option> \u2192 "opt_\u2026"
32010
+ // A rename on the platform re-runs codegen and moves these in lockstep.
32011
+ `;
32012
+ function slugifyAlias(name, upper) {
32013
+ const stripped = name.normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/đ/g, "d").replace(/Đ/g, "D");
32014
+ const cased = upper ? stripped.toUpperCase() : stripped.toLowerCase();
32015
+ const slug = cased.replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "");
32016
+ if (slug === "") return "_";
32017
+ return /^[0-9]/.test(slug) ? `_${slug}` : slug;
32018
+ }
32019
+ function dedupeAliases(names, upper) {
32020
+ const used = /* @__PURE__ */ new Map();
32021
+ return names.map((name) => {
32022
+ const base = slugifyAlias(name, upper);
32023
+ const seen = used.get(base);
32024
+ if (seen === void 0) {
32025
+ used.set(base, 1);
32026
+ return base;
32027
+ }
32028
+ let n = seen + 1;
32029
+ while (used.has(`${base}_${n}`)) n++;
32030
+ used.set(base, n);
32031
+ used.set(`${base}_${n}`, 1);
32032
+ return `${base}_${n}`;
32033
+ });
32034
+ }
32035
+ function propKey(alias) {
32036
+ return isValidIdentifier(alias) ? alias : JSON.stringify(alias);
32037
+ }
32038
+ function aliasTables(tables) {
32039
+ const tableAliases = dedupeAliases(
32040
+ tables.map((t) => t.name),
32041
+ true
32042
+ );
32043
+ return tables.map((table, i2) => {
32044
+ const fieldAliases = dedupeAliases(
32045
+ table.fields.map((f) => f.name),
32046
+ false
32047
+ );
32048
+ return {
32049
+ alias: tableAliases[i2],
32050
+ table,
32051
+ fields: table.fields.map((field, j) => ({ alias: fieldAliases[j], field }))
32052
+ };
32053
+ });
32054
+ }
32055
+ function emitFieldMap(aliased) {
32056
+ const tableBlocks = aliased.map(({ alias, fields }) => {
32057
+ const fieldLines = fields.map(
32058
+ ({ alias: fieldAlias, field }) => ` ${propKey(fieldAlias)}: ${JSON.stringify(field.id)},`
32059
+ );
32060
+ return ` ${propKey(alias)}: {
32061
+ ${fieldLines.join("\n")}
32062
+ },`;
32063
+ });
32064
+ return `export const F = {
32065
+ ${tableBlocks.join("\n")}
32066
+ } as const;`;
32067
+ }
32068
+ function emitOptionMap(aliased) {
32069
+ const tableBlocks = [];
32070
+ for (const { alias, fields } of aliased) {
32071
+ const fieldBlocks = [];
32072
+ for (const { alias: fieldAlias, field } of fields) {
32073
+ const options = field.options ?? [];
32074
+ if (options.length === 0) continue;
32075
+ const optionAliases = dedupeAliases(
32076
+ options.map((o) => o.label),
32077
+ false
32078
+ );
32079
+ const optionLines = options.map(
32080
+ (option, i2) => ` ${propKey(optionAliases[i2])}: ${JSON.stringify(option.id)},`
32081
+ );
32082
+ fieldBlocks.push(` ${propKey(fieldAlias)}: {
32083
+ ${optionLines.join("\n")}
32084
+ },`);
32085
+ }
32086
+ if (fieldBlocks.length === 0) continue;
32087
+ tableBlocks.push(` ${propKey(alias)}: {
32088
+ ${fieldBlocks.join("\n")}
32089
+ },`);
32090
+ }
32091
+ if (tableBlocks.length === 0) return `export const OPT = {} as const;`;
32092
+ return `export const OPT = {
32093
+ ${tableBlocks.join("\n")}
32094
+ } as const;`;
32095
+ }
32096
+ function generateAppFields(tables) {
32097
+ if (tables.length === 0) {
32098
+ return `${HEADER4}
32099
+ export const F = {} as const;
32100
+
32101
+ export const OPT = {} as const;
32102
+
32103
+ /** Field-id alias map (empty \u2014 no tables in scope). */
32104
+ export type AppFields = typeof F;
32105
+ /** Select-option alias map (empty \u2014 no tables in scope). */
32106
+ export type AppOptions = typeof OPT;
32107
+ `;
32108
+ }
32109
+ const aliased = aliasTables(tables);
32110
+ return `${HEADER4}
32111
+ ${emitFieldMap(aliased)}
32112
+
32113
+ ${emitOptionMap(aliased)}
32114
+
32115
+ /** Field-id alias map: \`F[<TABLE>][<field>]\` is the \`fld_\u2026\` id (literal-typed). */
32116
+ export type AppFields = typeof F;
32117
+ /** Select-option alias map: \`OPT[<TABLE>][<field>][<option>]\` is the \`opt_\u2026\` id. */
32118
+ export type AppOptions = typeof OPT;
32119
+ `;
32120
+ }
32121
+
31754
32122
  // src/app_commands.ts
31755
32123
  async function fetchLatestNpmVersion(packageName) {
31756
32124
  try {
@@ -31768,6 +32136,98 @@ async function fetchLatestNpmVersion(packageName) {
31768
32136
  return null;
31769
32137
  }
31770
32138
  }
32139
+ var WORKFLOWS_DIR = path4.join("src", "workflows");
32140
+ var WORKFLOW_GLOBALS_DIR = path4.join(".lotics", "workflows");
32141
+ var FALLBACK_ENVELOPE_PREFIX = "async function __workflow(): Promise<__WorkflowReturn | void> {\n";
32142
+ var FALLBACK_ENVELOPE_SUFFIX = "\n}";
32143
+ function workflowFileHeader(alias) {
32144
+ const refPath = path4.join("..", "..", WORKFLOW_GLOBALS_DIR, `${alias}.globals.d.ts`).split(path4.sep).join("/");
32145
+ return `/// <reference path="${refPath}" />
32146
+ // Auto-pulled workflow body for "${alias}". Edit the BODY between the wrapper
32147
+ // lines below, then push with:
32148
+ // lotics app workflow set ${alias}
32149
+ // The push goes through set_app_workflow, where the SERVER verifies the body.
32150
+ // The __workflow wrapper + the reference above are CLI bookkeeping (stripped on
32151
+ // set) \u2014 they only make the body typecheck locally against the workspace types.
32152
+ // Do NOT rename this file \u2014 the filename is the alias the binding is keyed by.
32153
+ export {};
32154
+ `;
32155
+ }
32156
+ function workflowFilePath(projectDir, alias) {
32157
+ return path4.join(projectDir, WORKFLOWS_DIR, `${alias}.ts`);
32158
+ }
32159
+ function workflowGlobalsPath(projectDir, alias) {
32160
+ return path4.join(projectDir, WORKFLOW_GLOBALS_DIR, `${alias}.globals.d.ts`);
32161
+ }
32162
+ function writeWorkflowGlobals(projectDir, alias, dts) {
32163
+ const dir = path4.join(projectDir, WORKFLOW_GLOBALS_DIR);
32164
+ fs3.mkdirSync(dir, { recursive: true });
32165
+ const file = workflowGlobalsPath(projectDir, alias);
32166
+ fs3.writeFileSync(file, `${dts.replace(/\s+$/, "")}
32167
+ `);
32168
+ return file;
32169
+ }
32170
+ function writeWorkflowFile(projectDir, alias, source, envelope = { prefix: FALLBACK_ENVELOPE_PREFIX, suffix: FALLBACK_ENVELOPE_SUFFIX }) {
32171
+ const dir = path4.join(projectDir, WORKFLOWS_DIR);
32172
+ fs3.mkdirSync(dir, { recursive: true });
32173
+ const file = workflowFilePath(projectDir, alias);
32174
+ const body = source.replace(/\s+$/, "");
32175
+ fs3.writeFileSync(file, `${workflowFileHeader(alias)}
32176
+ ${envelope.prefix}${body}${envelope.suffix}
32177
+ `);
32178
+ return file;
32179
+ }
32180
+ var WORKFLOW_WRAPPER_OPENER = /^\s*async\s+function\s+__workflow\s*\(/;
32181
+ function stripWorkflowHeader(content) {
32182
+ const lines = content.split("\n");
32183
+ let headerEnd = 0;
32184
+ while (headerEnd < lines.length && /^\s*\/\//.test(lines[headerEnd])) headerEnd++;
32185
+ while (headerEnd < lines.length && /^\s*export\s*\{\s*\}\s*;?\s*$/.test(lines[headerEnd])) headerEnd++;
32186
+ while (headerEnd < lines.length && lines[headerEnd].trim() === "") headerEnd++;
32187
+ let start;
32188
+ if (headerEnd < lines.length && WORKFLOW_WRAPPER_OPENER.test(lines[headerEnd])) {
32189
+ start = headerEnd;
32190
+ } else if (lines.length > 0 && WORKFLOW_WRAPPER_OPENER.test(lines[0])) {
32191
+ start = 0;
32192
+ } else {
32193
+ return content.replace(/\s+$/, "");
32194
+ }
32195
+ let end = lines.length - 1;
32196
+ while (end > start && lines[end].trim() === "") end--;
32197
+ if (lines[end]?.trim() === "}") {
32198
+ return lines.slice(start + 1, end).join("\n").replace(/\s+$/, "");
32199
+ }
32200
+ return lines.slice(start + 1).join("\n").replace(/\s+$/, "");
32201
+ }
32202
+ async function writeWorkflowFiles(client, projectDir, app_id, aliases) {
32203
+ const written = [];
32204
+ for (const alias of aliases) {
32205
+ const res = await client.getAppWorkflow(app_id, alias);
32206
+ const source = res.error || res.result === null || typeof res.result !== "object" ? null : res.result.source;
32207
+ if (typeof source !== "string" || source.trim() === "") {
32208
+ console.error(
32209
+ `\u26A0 Skipped src/workflows/${alias}.ts \u2014 the server returned no readable source (${res.error ?? "legacy workflow with no rendered body"}).`
32210
+ );
32211
+ continue;
32212
+ }
32213
+ const envelope = await fetchWorkflowGlobals(client, projectDir, app_id, alias);
32214
+ writeWorkflowFile(projectDir, alias, source, envelope);
32215
+ written.push(alias);
32216
+ }
32217
+ return written;
32218
+ }
32219
+ async function fetchWorkflowGlobals(client, projectDir, app_id, alias) {
32220
+ try {
32221
+ const { dts, envelope_prefix, envelope_suffix } = await client.getAppWorkflowDts(app_id, alias);
32222
+ writeWorkflowGlobals(projectDir, alias, dts);
32223
+ return { prefix: envelope_prefix, suffix: envelope_suffix };
32224
+ } catch (err2) {
32225
+ console.error(
32226
+ `\u26A0 Could not fetch workflow types for "${alias}" (${err2 instanceof Error ? err2.message : String(err2)}). Wrote the body with the fallback wrapper; its local typecheck may be degraded.`
32227
+ );
32228
+ return { prefix: FALLBACK_ENVELOPE_PREFIX, suffix: FALLBACK_ENVELOPE_SUFFIX };
32229
+ }
32230
+ }
31771
32231
  function runTar(args, cwd) {
31772
32232
  return new Promise((resolve, reject2) => {
31773
32233
  const proc = spawn2("tar", args, { cwd, stdio: ["ignore", "ignore", "pipe"] });
@@ -31784,7 +32244,7 @@ function runTar(args, cwd) {
31784
32244
  }
31785
32245
  function runNpm(args, cwd) {
31786
32246
  return new Promise((resolve, reject2) => {
31787
- const proc = spawn2("npm", args, { cwd, stdio: "inherit" });
32247
+ const proc = spawn2("npm", args, { cwd, stdio: "inherit", env: ipv4ChildEnv(process.env) });
31788
32248
  proc.on("error", reject2);
31789
32249
  proc.on("exit", (code) => {
31790
32250
  if (code === 0) resolve();
@@ -31823,18 +32283,71 @@ function writeAppMeta(projectDir, meta) {
31823
32283
  function writeAppDts(projectDir, manifest) {
31824
32284
  const dotLotics = path4.join(projectDir, ".lotics");
31825
32285
  fs3.mkdirSync(dotLotics, { recursive: true });
31826
- fs3.writeFileSync(
31827
- path4.join(dotLotics, "app_workflows.d.ts"),
31828
- generateAppWorkflowsDts(manifest.workflows)
31829
- );
31830
- fs3.writeFileSync(
31831
- path4.join(dotLotics, "app_queries.d.ts"),
31832
- generateAppQueriesDts(manifest.queries)
31833
- );
31834
- fs3.writeFileSync(
31835
- path4.join(dotLotics, "app_agents.d.ts"),
31836
- generateAppAgentsDts(manifest.agents)
31837
- );
32286
+ const written = [
32287
+ [path4.join(dotLotics, "app_workflows.d.ts"), generateAppWorkflowsDts(manifest.workflows)],
32288
+ [path4.join(dotLotics, "app_queries.d.ts"), generateAppQueriesDts(manifest.queries)],
32289
+ [path4.join(dotLotics, "app_agents.d.ts"), generateAppAgentsDts(manifest.agents)]
32290
+ ];
32291
+ for (const [file, content] of written) fs3.writeFileSync(file, content);
32292
+ return written.map(([file]) => file);
32293
+ }
32294
+ function readCodegenTablesAllowlist(projectDir) {
32295
+ const pkg2 = JSON.parse(fs3.readFileSync(path4.join(projectDir, "package.json"), "utf-8"));
32296
+ const tables = pkg2.lotics?.codegen?.tables;
32297
+ return Array.isArray(tables) ? tables.filter((t) => typeof t === "string") : [];
32298
+ }
32299
+ function resolveCodegenTableIds(projectDir, queries) {
32300
+ const ids = /* @__PURE__ */ new Set();
32301
+ for (const decl of Object.values(queries)) {
32302
+ for (const id of collectQueryTableIds(decl.ast)) ids.add(id);
32303
+ }
32304
+ for (const id of readCodegenTablesAllowlist(projectDir)) ids.add(id);
32305
+ return [...ids];
32306
+ }
32307
+ function writeAppFields(projectDir, tables) {
32308
+ const dotLotics = path4.join(projectDir, ".lotics");
32309
+ fs3.mkdirSync(dotLotics, { recursive: true });
32310
+ const file = path4.join(dotLotics, "app_fields.ts");
32311
+ fs3.writeFileSync(file, generateAppFields(tables));
32312
+ return file;
32313
+ }
32314
+ async function appCodegen(args) {
32315
+ const projectDir = path4.resolve(args.projectDir ?? process.cwd());
32316
+ const meta = readAppMeta(projectDir);
32317
+ const dtsPaths = writeAppDts(projectDir, {
32318
+ workflows: meta.workflows,
32319
+ queries: meta.queries,
32320
+ agents: meta.agents
32321
+ });
32322
+ for (const p of dtsPaths) console.error(`Regenerated ${p}`);
32323
+ if (!args.client) {
32324
+ console.error(
32325
+ "Skipped .lotics/app_fields.ts \u2014 no workspace credentials resolved. Run authenticated (or set LOTICS_API_KEY) to regenerate field/option ids."
32326
+ );
32327
+ return;
32328
+ }
32329
+ const tableIds = resolveCodegenTableIds(projectDir, meta.queries ?? {});
32330
+ try {
32331
+ const tables = await args.client.getWorkspaceSchema(tableIds);
32332
+ const fieldsPath = writeAppFields(projectDir, tables);
32333
+ console.error(`Regenerated ${fieldsPath} (${tables.length} table${tables.length === 1 ? "" : "s"})`);
32334
+ } catch (err2) {
32335
+ console.error(
32336
+ `\u26A0 Could not fetch the workspace schema (${err2 instanceof Error ? err2.message : String(err2)}). Kept the existing .lotics/app_fields.ts.`
32337
+ );
32338
+ }
32339
+ await refreshWorkflowGlobals(args.client, projectDir, meta.app_id, Object.keys(meta.workflows ?? {}));
32340
+ }
32341
+ async function refreshWorkflowGlobals(client, projectDir, app_id, aliases) {
32342
+ for (const alias of aliases) {
32343
+ const file = workflowFilePath(projectDir, alias);
32344
+ if (!fs3.existsSync(file)) continue;
32345
+ const body = stripWorkflowHeader(fs3.readFileSync(file, "utf-8"));
32346
+ if (body.trim() === "") continue;
32347
+ const envelope = await fetchWorkflowGlobals(client, projectDir, app_id, alias);
32348
+ writeWorkflowFile(projectDir, alias, body, envelope);
32349
+ console.error(`Refreshed ${path4.relative(projectDir, file)} + its workflow types`);
32350
+ }
31838
32351
  }
31839
32352
  function stampPulledManifest(projectDir, args) {
31840
32353
  writeAppMeta(projectDir, {
@@ -31942,6 +32455,15 @@ async function appPull(client, args) {
31942
32455
  queries: app.queries ?? {},
31943
32456
  agents: app.agents ?? {}
31944
32457
  });
32458
+ const aliases = Object.keys(app.workflows ?? {});
32459
+ if (aliases.length > 0) {
32460
+ const written = await writeWorkflowFiles(client, targetPath, app.id, aliases);
32461
+ if (written.length > 0) {
32462
+ console.error(
32463
+ `Wrote ${written.length} workflow ${written.length === 1 ? "body" : "bodies"} to ${WORKFLOWS_DIR}/ (${written.join(", ")})`
32464
+ );
32465
+ }
32466
+ }
31945
32467
  console.error(`Installing npm dependencies...`);
31946
32468
  await runNpm(["install"], targetPath);
31947
32469
  console.error(`
@@ -31965,14 +32487,14 @@ function readAppSourceText(projectDir) {
31965
32487
  const srcDir = path4.join(projectDir, "src");
31966
32488
  if (!fs3.existsSync(srcDir)) return "";
31967
32489
  const parts = [];
31968
- const walk = (dir) => {
32490
+ const walk2 = (dir) => {
31969
32491
  for (const entry of fs3.readdirSync(dir, { withFileTypes: true })) {
31970
32492
  const full = path4.join(dir, entry.name);
31971
- if (entry.isDirectory()) walk(full);
32493
+ if (entry.isDirectory()) walk2(full);
31972
32494
  else if (/\.(ts|tsx|js|jsx)$/.test(entry.name)) parts.push(fs3.readFileSync(full, "utf8"));
31973
32495
  }
31974
32496
  };
31975
- walk(srcDir);
32497
+ walk2(srcDir);
31976
32498
  return parts.join("\n");
31977
32499
  }
31978
32500
  async function appDeploy(client, args) {
@@ -32119,6 +32641,209 @@ async function appDev(client, args) {
32119
32641
  console.error("\nStopping\u2026");
32120
32642
  await handle.stop();
32121
32643
  }
32644
+ function parseCreatedRecords(value) {
32645
+ if (!Array.isArray(value)) return [];
32646
+ const groups = [];
32647
+ for (const entry of value) {
32648
+ if (!entry || typeof entry !== "object") continue;
32649
+ const tableId = entry.table_id;
32650
+ const recordIds = entry.record_ids;
32651
+ if (typeof tableId !== "string" || !Array.isArray(recordIds)) continue;
32652
+ const ids = recordIds.filter((id) => typeof id === "string");
32653
+ if (ids.length > 0) groups.push({ table_id: tableId, record_ids: ids });
32654
+ }
32655
+ return groups;
32656
+ }
32657
+ function parseIrreversibleToolNames(value) {
32658
+ if (!Array.isArray(value)) return [];
32659
+ const names = [];
32660
+ for (const entry of value) {
32661
+ if (!entry || typeof entry !== "object") continue;
32662
+ const name = entry.tool_name;
32663
+ if (typeof name === "string" && !names.includes(name)) names.push(name);
32664
+ }
32665
+ return names;
32666
+ }
32667
+ function printSideEffects(summary) {
32668
+ const created = parseCreatedRecords(summary.created_records);
32669
+ const irreversibleTools = parseIrreversibleToolNames(summary.irreversible_tool_calls);
32670
+ const subWorkflows = summary.sub_workflows_possible === true;
32671
+ console.error("\nCreated records:");
32672
+ if (created.length === 0) {
32673
+ console.error(" (none with ids to clean up)");
32674
+ } else {
32675
+ for (const group of created) {
32676
+ console.error(` ${group.table_id}: ${group.record_ids.length} record(s)`);
32677
+ const payload = JSON.stringify({ table_id: group.table_id, record_ids: group.record_ids });
32678
+ console.error(` lotics run delete_records '${payload}'`);
32679
+ }
32680
+ }
32681
+ const irreversiblePart = irreversibleTools.length > 0 ? `Could NOT auto-undo (clean up manually): ${irreversibleTools.join(", ")}.` : "Could NOT auto-undo: none.";
32682
+ const subPart = subWorkflows ? " Sub-workflows may have run (after_* table workflows) \u2014 their effects are NOT in this list." : "";
32683
+ console.error(`
32684
+ ${irreversiblePart}${subPart}`);
32685
+ }
32686
+ async function cleanupCreatedRecords(client, created) {
32687
+ if (created.length === 0) {
32688
+ console.error("\nNo created records to clean up.");
32689
+ return true;
32690
+ }
32691
+ console.error("\nCleaning up created records (delete_records \u2014 records only):");
32692
+ let allDeleted = true;
32693
+ for (const group of created) {
32694
+ const res = await client.execute("delete_records", {
32695
+ table_id: group.table_id,
32696
+ record_ids: group.record_ids
32697
+ });
32698
+ if (res.error) {
32699
+ console.error(` \u2717 ${group.table_id}: ${res.error}`);
32700
+ allDeleted = false;
32701
+ } else {
32702
+ console.error(` \u2713 ${group.table_id}: deleted ${group.record_ids.length} record(s)`);
32703
+ }
32704
+ }
32705
+ return allDeleted;
32706
+ }
32707
+ async function appExecuteWorkflow(client, args) {
32708
+ const meta = readAppMeta(process.cwd());
32709
+ const result = await client.appWorkflow(meta.app_id, args.alias, args.inputs);
32710
+ console.log(JSON.stringify(result, null, 2));
32711
+ const status = typeof result.status === "string" ? result.status : "unknown";
32712
+ const message = typeof result.message === "string" ? result.message : "";
32713
+ console.error(`Workflow "${args.alias}" \u2192 ${status}${message ? `: ${message}` : ""}`);
32714
+ let cleanupFailed = false;
32715
+ if ((args.printCreated || args.cleanup) && result.side_effects) {
32716
+ printSideEffects(result.side_effects);
32717
+ if (args.cleanup) {
32718
+ const allDeleted = await cleanupCreatedRecords(
32719
+ client,
32720
+ parseCreatedRecords(result.side_effects.created_records)
32721
+ );
32722
+ cleanupFailed = !allDeleted;
32723
+ }
32724
+ } else if (args.printCreated || args.cleanup) {
32725
+ console.error("\n(no side-effect summary returned by the server)");
32726
+ }
32727
+ if (status === "error" || cleanupFailed) process.exit(1);
32728
+ }
32729
+ async function appWorkflowSet(client, args) {
32730
+ const projectDir = process.cwd();
32731
+ const meta = readAppMeta(projectDir);
32732
+ const declaration = meta.workflows?.[args.alias];
32733
+ if (!declaration) {
32734
+ console.error(
32735
+ `No workflow "${args.alias}" in package.json#lotics.workflows. Bind it first (set_app_workflow), then 'lotics app pull' to write its body and manifest entry.`
32736
+ );
32737
+ process.exit(1);
32738
+ }
32739
+ const file = workflowFilePath(projectDir, args.alias);
32740
+ if (!fs3.existsSync(file)) {
32741
+ console.error(
32742
+ `No workflow body at ${path4.relative(projectDir, file)}. Run 'lotics app pull ${meta.app_id}' to write src/workflows/${args.alias}.ts, then edit it.`
32743
+ );
32744
+ process.exit(1);
32745
+ }
32746
+ const source = stripWorkflowHeader(fs3.readFileSync(file, "utf-8"));
32747
+ if (source.trim() === "") {
32748
+ console.error(`Workflow body ${path4.relative(projectDir, file)} is empty after stripping the header.`);
32749
+ process.exit(1);
32750
+ }
32751
+ const res = await client.setAppWorkflow(meta.app_id, args.alias, {
32752
+ source,
32753
+ inputs: declaration.inputs,
32754
+ outputs: declaration.outputs
32755
+ });
32756
+ if (res.error) {
32757
+ console.error(`Failed to set workflow "${args.alias}": ${res.error}`);
32758
+ process.exit(1);
32759
+ }
32760
+ const result = res.result ?? {};
32761
+ const workflowId = typeof result.workflow_id === "string" ? result.workflow_id : "(unknown)";
32762
+ console.error(`Set workflow "${args.alias}" \u2192 ${workflowId}`);
32763
+ if (result.outputs && typeof result.outputs === "object") {
32764
+ console.error(` result.data schema: ${JSON.stringify(result.outputs)}`);
32765
+ }
32766
+ }
32767
+ async function appWorkflowPull(client) {
32768
+ const projectDir = process.cwd();
32769
+ const meta = readAppMeta(projectDir);
32770
+ const app = await client.getApp(meta.app_id);
32771
+ const aliases = Object.keys(app.workflows ?? {});
32772
+ if (aliases.length === 0) {
32773
+ console.error(`App ${meta.app_id} has no bound workflows.`);
32774
+ return;
32775
+ }
32776
+ const written = await writeWorkflowFiles(client, projectDir, meta.app_id, aliases);
32777
+ console.error(
32778
+ `Wrote ${written.length} workflow ${written.length === 1 ? "body" : "bodies"} to ${WORKFLOWS_DIR}/` + (written.length > 0 ? ` (${written.join(", ")})` : "")
32779
+ );
32780
+ }
32781
+ function findUiSrcDir(start) {
32782
+ let dir = path4.resolve(start);
32783
+ for (; ; ) {
32784
+ const candidate = path4.join(dir, "packages", "ui", "src");
32785
+ if (fs3.existsSync(candidate) && fs3.statSync(candidate).isDirectory()) return candidate;
32786
+ const parent = path4.dirname(dir);
32787
+ if (parent === dir) return null;
32788
+ dir = parent;
32789
+ }
32790
+ }
32791
+ var UI_ALIAS_FIND_SOURCE = String.raw`/^@lotics\/ui\/(.+)$/`;
32792
+ function appUiLink(args) {
32793
+ const projectDir = path4.resolve(args.projectDir ?? process.cwd());
32794
+ const viteConfigPath = path4.join(projectDir, "vite.config.ts");
32795
+ if (!fs3.existsSync(viteConfigPath)) {
32796
+ throw new Error(
32797
+ `No vite.config.ts at ${projectDir}. Run inside a 'lotics app' project directory.`
32798
+ );
32799
+ }
32800
+ const uiSrc = findUiSrcDir(projectDir);
32801
+ if (!uiSrc) {
32802
+ throw new Error(
32803
+ "Cannot find packages/ui/src by walking up from this directory \u2014 `lotics ui link` requires a monorepo checkout. External apps consume @lotics/ui from npm; bump the package version and widen the app's dependency range instead."
32804
+ );
32805
+ }
32806
+ const hasComponent = fs3.existsSync(path4.join(uiSrc, `${args.component}.tsx`)) || fs3.existsSync(path4.join(uiSrc, `${args.component}.ts`)) || fs3.existsSync(path4.join(uiSrc, args.component));
32807
+ if (!hasComponent) {
32808
+ throw new Error(
32809
+ `No '@lotics/ui/${args.component}' under ${uiSrc} (expected ${args.component}.tsx/.ts). Check the component name.`
32810
+ );
32811
+ }
32812
+ const source = fs3.readFileSync(viteConfigPath, "utf-8");
32813
+ const aliasEntry = `{ find: ${UI_ALIAS_FIND_SOURCE}, replacement: ${JSON.stringify(`${uiSrc}/$1`)} },`;
32814
+ const alreadyLinked = source.includes(UI_ALIAS_FIND_SOURCE);
32815
+ if (args.remove) {
32816
+ if (!alreadyLinked) {
32817
+ console.error("No @lotics/ui dev-link alias present \u2014 nothing to remove.");
32818
+ return;
32819
+ }
32820
+ const stripped = source.replace(new RegExp(`^\\s*\\{ find: ${escapeRegExp(UI_ALIAS_FIND_SOURCE)}.*$\\n?`, "m"), "");
32821
+ fs3.writeFileSync(viteConfigPath, stripped);
32822
+ console.error(`Removed the @lotics/ui dev-link alias from ${viteConfigPath}.`);
32823
+ console.error("Restart `lotics app dev` and rm -rf node_modules/.vite to clear cached modules.");
32824
+ return;
32825
+ }
32826
+ if (alreadyLinked) {
32827
+ console.error(`@lotics/ui is already dev-linked in ${viteConfigPath}.`);
32828
+ return;
32829
+ }
32830
+ const aliasMatch = /resolve\s*:\s*\{[\s\S]*?alias\s*:\s*\[/.exec(source);
32831
+ if (!aliasMatch) {
32832
+ throw new Error(
32833
+ `Could not find a resolve.alias array literal in ${viteConfigPath}. Refresh vite.config.ts from the starter (packages/sdk/src/starter_template.ts) and retry.`
32834
+ );
32835
+ }
32836
+ const insertAt = aliasMatch.index + aliasMatch[0].length;
32837
+ const updated = `${source.slice(0, insertAt)}
32838
+ ${aliasEntry}${source.slice(insertAt)}`;
32839
+ fs3.writeFileSync(viteConfigPath, updated);
32840
+ console.error(`Dev-linked @lotics/ui \u2192 ${uiSrc} in ${viteConfigPath}.`);
32841
+ console.error("Restart `lotics app dev` and rm -rf node_modules/.vite to clear cached modules.");
32842
+ console.error("Finalize: PR the packages/ui change \u2192 publish \u2192 `lotics ui link <component> --remove` + bump the app's dep.");
32843
+ }
32844
+ function escapeRegExp(s) {
32845
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
32846
+ }
32122
32847
 
32123
32848
  // src/args.ts
32124
32849
  function parseArgs(argv) {
@@ -32135,6 +32860,8 @@ function parseArgs(argv) {
32135
32860
  message: void 0,
32136
32861
  local: false,
32137
32862
  all: false,
32863
+ printCreated: false,
32864
+ cleanup: false,
32138
32865
  version: false,
32139
32866
  help: false
32140
32867
  };
@@ -32185,6 +32912,13 @@ function parseArgs(argv) {
32185
32912
  case "--all":
32186
32913
  flags.all = true;
32187
32914
  break;
32915
+ case "--print-created":
32916
+ case "--report-effects":
32917
+ flags.printCreated = true;
32918
+ break;
32919
+ case "--cleanup":
32920
+ flags.cleanup = true;
32921
+ break;
32188
32922
  case "--version":
32189
32923
  case "-v":
32190
32924
  flags.version = true;
@@ -32210,6 +32944,34 @@ function parseArgs(argv) {
32210
32944
  return { command, subcommand, toolArgs, restArgs, flags };
32211
32945
  }
32212
32946
 
32947
+ // src/inputs.ts
32948
+ async function ingestJsonArgs(opts) {
32949
+ let raw = opts.rawArg;
32950
+ if (raw && raw.startsWith("@")) {
32951
+ const argsPath = raw.slice(1);
32952
+ try {
32953
+ raw = opts.readFile(argsPath);
32954
+ } catch (err2) {
32955
+ return {
32956
+ kind: "error",
32957
+ message: `Cannot read args file "${argsPath}": ${err2 instanceof Error ? err2.message : String(err2)}`
32958
+ };
32959
+ }
32960
+ } else if (!raw && !opts.stdinIsTTY) {
32961
+ raw = await opts.readStdin();
32962
+ }
32963
+ if (!raw) return { kind: "ok", args: {} };
32964
+ try {
32965
+ const parsed = JSON.parse(raw);
32966
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
32967
+ return { kind: "error", message: `JSON args must be an object, got: ${raw}` };
32968
+ }
32969
+ return { kind: "ok", args: parsed };
32970
+ } catch {
32971
+ return { kind: "error", message: `Invalid JSON: ${raw}` };
32972
+ }
32973
+ }
32974
+
32213
32975
  // src/xlsx.ts
32214
32976
  import fs5 from "node:fs";
32215
32977
 
@@ -38666,8 +39428,8 @@ function parseWorkbook(workbookXml) {
38666
39428
  }
38667
39429
  return { sheets, activeSheetIndex, date1904, namedRanges, printTitlesBySheet, fullCalcOnLoad };
38668
39430
  }
38669
- function parseWorkbookRels(relsXml) {
38670
- const doc = xmlParser3.parse(relsXml);
39431
+ function parseWorkbookRels(relsXml2) {
39432
+ const doc = xmlParser3.parse(relsXml2);
38671
39433
  const rels = doc?.["Relationships"];
38672
39434
  if (!rels) return /* @__PURE__ */ new Map();
38673
39435
  const relArr = rels["Relationship"];
@@ -39703,6 +40465,9 @@ function refToRowCol(ref) {
39703
40465
  function rowColToRef(row, col) {
39704
40466
  return colNumToLetters(col) + String(row);
39705
40467
  }
40468
+ function escapeXml(s) {
40469
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
40470
+ }
39706
40471
  var PACK_COL_BITS = 14;
39707
40472
  var PACK_COL_MULT = 1 << PACK_COL_BITS;
39708
40473
  function packRowCol(row, col) {
@@ -39874,8 +40639,8 @@ function parseHeaderFooterElement(worksheet) {
39874
40639
  if (hf["@_differentFirst"] === "1") out.differentFirst = true;
39875
40640
  return Object.keys(out).length > 0 ? out : void 0;
39876
40641
  }
39877
- function parseSheetRels(relsXml) {
39878
- const doc = relsParser.parse(relsXml);
40642
+ function parseSheetRels(relsXml2) {
40643
+ const doc = relsParser.parse(relsXml2);
39879
40644
  const relationships = doc?.["Relationships"];
39880
40645
  if (!relationships) return /* @__PURE__ */ new Map();
39881
40646
  const relArr = relationships["Relationship"];
@@ -40179,8 +40944,8 @@ function parseImages(rels, zipEntries) {
40179
40944
  }
40180
40945
  return images;
40181
40946
  }
40182
- function parseDrawingRels(relsXml) {
40183
- const doc = relsParser.parse(relsXml);
40947
+ function parseDrawingRels(relsXml2) {
40948
+ const doc = relsParser.parse(relsXml2);
40184
40949
  const relationships = doc?.["Relationships"];
40185
40950
  if (!relationships) return /* @__PURE__ */ new Map();
40186
40951
  const relArr = relationships["Relationship"];
@@ -41858,1505 +42623,1722 @@ function parsePrintTitlesRef(raw) {
41858
42623
  return result.repeatRows || result.repeatCols ? result : void 0;
41859
42624
  }
41860
42625
 
41861
- // ../xlsx/src/xlsx_writer.ts
41862
- function readWorkbookSheetOrder(originalZip) {
41863
- const wbBytes = originalZip["xl/workbook.xml"];
41864
- if (!wbBytes) return [];
41865
- const xml = decode(wbBytes);
41866
- const out = [];
41867
- const re = /<sheet\b[^>]*?r:id="([^"]+)"/g;
41868
- let m;
41869
- while ((m = re.exec(xml)) !== null) {
41870
- out.push(m[1]);
41871
- }
41872
- return out;
41873
- }
41874
- function readWorkbookRels(originalZip) {
41875
- const out = /* @__PURE__ */ new Map();
41876
- const bytes = originalZip["xl/_rels/workbook.xml.rels"];
41877
- if (!bytes) return out;
41878
- const xml = decode(bytes);
41879
- const re = /<Relationship\b([^/]*?)\/>/g;
41880
- let m;
41881
- while ((m = re.exec(xml)) !== null) {
41882
- const attrs = m[1];
41883
- const id = /\bId="([^"]+)"/.exec(attrs)?.[1];
41884
- const type = /\bType="([^"]+)"/.exec(attrs)?.[1] ?? "";
41885
- const target = /\bTarget="([^"]+)"/.exec(attrs)?.[1] ?? "";
41886
- if (id) out.set(id, { type, target });
41887
- }
41888
- return out;
41889
- }
41890
- function readPivotCacheIdMap(originalZip) {
41891
- const out = /* @__PURE__ */ new Map();
41892
- const bytes = originalZip["xl/workbook.xml"];
41893
- if (!bytes) return out;
41894
- const xml = decode(bytes);
41895
- const re = /<pivotCache\b([^/]*?)\/>/g;
41896
- let m;
41897
- while ((m = re.exec(xml)) !== null) {
41898
- const attrs = m[1];
41899
- const cacheIdMatch = /\bcacheId="(\d+)"/.exec(attrs);
41900
- const ridMatch = /\br:id="([^"]+)"/.exec(attrs);
41901
- if (cacheIdMatch && ridMatch) {
41902
- out.set(parseInt(cacheIdMatch[1], 10), ridMatch[1]);
42626
+ // ../xlsx/src/pivot_recompute.ts
42627
+ var TOTAL_LABEL = "Grand Total";
42628
+ function recomputePivot(table, cache, source) {
42629
+ const records = filterByPageAxis(table, cache, source.records);
42630
+ const rowTuples = distinctTuples(records, table.rowFieldIndices);
42631
+ const colTuples = distinctTuples(records, table.colFieldIndices);
42632
+ const groupKey = (rec) => JSON.stringify([
42633
+ tupleOf(rec, table.rowFieldIndices),
42634
+ tupleOf(rec, table.colFieldIndices)
42635
+ ]);
42636
+ const groups = /* @__PURE__ */ new Map();
42637
+ for (const rec of records) {
42638
+ const key = groupKey(rec);
42639
+ let bucket = groups.get(key);
42640
+ if (!bucket) {
42641
+ bucket = [];
42642
+ groups.set(key, bucket);
41903
42643
  }
42644
+ bucket.push(rec);
41904
42645
  }
41905
- return out;
42646
+ return buildGrid(table, source, rowTuples, colTuples, groups, records);
41906
42647
  }
41907
- function readSheetPivotPaths(originalZip, sheetTarget) {
41908
- const sheetPath = `xl/${sheetTarget}`;
41909
- const relsPath = sheetPath.replace(/([^/]+)$/, "_rels/$1.rels");
41910
- const bytes = originalZip[relsPath];
41911
- if (!bytes) return [];
41912
- const xml = decode(bytes);
41913
- const re = /<Relationship\b([^/]*?)\/>/g;
41914
- const out = [];
41915
- let m;
41916
- while ((m = re.exec(xml)) !== null) {
41917
- const attrs = m[1];
41918
- const type = /\bType="([^"]+)"/.exec(attrs)?.[1] ?? "";
41919
- if (!type.includes("/pivotTable")) continue;
41920
- const target = /\bTarget="([^"]+)"/.exec(attrs)?.[1];
41921
- if (target) out.push(target);
42648
+ function filterByPageAxis(table, cache, records) {
42649
+ const filters = [];
42650
+ for (const fi of table.pageFieldIndices) {
42651
+ const cfg = table.fields[fi];
42652
+ if (cfg?.selectedPageItem == null) continue;
42653
+ const cacheField = cache.fields[fi];
42654
+ if (!cacheField) continue;
42655
+ const allowed = cacheField.items[cfg.selectedPageItem];
42656
+ if (allowed) filters.push({ fieldIndex: fi, allowed });
41922
42657
  }
41923
- return out;
42658
+ if (filters.length === 0) return records;
42659
+ return records.filter(
42660
+ (rec) => filters.every(
42661
+ ({ fieldIndex, allowed }) => cellMatchesItem(rec[fieldIndex], allowed)
42662
+ )
42663
+ );
41924
42664
  }
41925
- function extractPivotRoundTripInfo(originalZip) {
41926
- const empty = {
41927
- cachesByWorkbook: /* @__PURE__ */ new Map(),
41928
- pivotTablesBySheetIndex: /* @__PURE__ */ new Map(),
41929
- pivotXmlPaths: []
41930
- };
41931
- if (!originalZip) return empty;
41932
- const sheetRIds = readWorkbookSheetOrder(originalZip);
41933
- const wbRels = readWorkbookRels(originalZip);
41934
- const cacheRIds = readPivotCacheIdMap(originalZip);
41935
- const cachesByWorkbook = /* @__PURE__ */ new Map();
41936
- for (const [cacheId, rid] of cacheRIds) {
41937
- const rel = wbRels.get(rid);
41938
- if (!rel) continue;
41939
- cachesByWorkbook.set(cacheId, rel.target);
41940
- }
41941
- const pivotTablesBySheetIndex = /* @__PURE__ */ new Map();
41942
- for (let i2 = 0; i2 < sheetRIds.length; i2++) {
41943
- const sheetTarget = wbRels.get(sheetRIds[i2])?.target;
41944
- if (!sheetTarget) continue;
41945
- const paths = readSheetPivotPaths(originalZip, sheetTarget);
41946
- if (paths.length > 0) pivotTablesBySheetIndex.set(i2, paths);
41947
- }
41948
- const pivotXmlPaths = [];
41949
- for (const path7 of Object.keys(originalZip)) {
41950
- if (path7.startsWith("xl/pivotTables/") && path7.endsWith(".xml")) {
41951
- pivotXmlPaths.push(path7);
41952
- }
41953
- if (path7.startsWith("xl/pivotCache/") && path7.endsWith(".xml")) {
41954
- pivotXmlPaths.push(path7);
41955
- }
42665
+ function cellMatchesItem(cell, item) {
42666
+ switch (item.kind) {
42667
+ case "string":
42668
+ return typeof cell === "string" && cell === item.value;
42669
+ case "number":
42670
+ return typeof cell === "number" && cell === item.value;
42671
+ case "boolean":
42672
+ return typeof cell === "boolean" && cell === item.value;
42673
+ case "date":
42674
+ return typeof cell === "string" && cell === item.value;
42675
+ case "missing":
42676
+ return cell === void 0 || cell === null || cell === "";
42677
+ case "error":
42678
+ return typeof cell === "string" && cell === item.value;
41956
42679
  }
41957
- return { cachesByWorkbook, pivotTablesBySheetIndex, pivotXmlPaths };
41958
42680
  }
41959
- function decode(bytes) {
41960
- return new TextDecoder().decode(bytes);
42681
+ function tupleOf(rec, indices) {
42682
+ return indices.map((i2) => rec[i2] ?? null);
41961
42683
  }
41962
- function pivotContentTypeFor(path7) {
41963
- if (path7.startsWith("xl/pivotTables/") && path7.endsWith(".xml")) {
41964
- return `<Override PartName="/${path7}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml"/>`;
41965
- }
41966
- if (path7.includes("/pivotCacheDefinition") && path7.endsWith(".xml")) {
41967
- return `<Override PartName="/${path7}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml"/>`;
42684
+ function distinctTuples(records, indices) {
42685
+ const seen = /* @__PURE__ */ new Set();
42686
+ const out = [];
42687
+ for (const rec of records) {
42688
+ const t = tupleOf(rec, indices);
42689
+ const key = JSON.stringify(t);
42690
+ if (seen.has(key)) continue;
42691
+ seen.add(key);
42692
+ out.push(t);
41968
42693
  }
41969
- if (path7.includes("/pivotCacheRecords") && path7.endsWith(".xml")) {
41970
- return `<Override PartName="/${path7}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml"/>`;
42694
+ out.sort((a, b) => {
42695
+ for (let i2 = 0; i2 < Math.max(a.length, b.length); i2++) {
42696
+ const av = a[i2];
42697
+ const bv = b[i2];
42698
+ if (av === bv) continue;
42699
+ const as = av === null || av === void 0 ? "" : String(av);
42700
+ const bs = bv === null || bv === void 0 ? "" : String(bv);
42701
+ if (as < bs) return -1;
42702
+ if (as > bs) return 1;
42703
+ }
42704
+ return 0;
42705
+ });
42706
+ return out;
42707
+ }
42708
+ function aggregate(values2, fn) {
42709
+ const numeric = values2.filter((v) => typeof v === "number");
42710
+ switch (fn) {
42711
+ case "count":
42712
+ return values2.filter((v) => v !== null && v !== void 0 && v !== "").length;
42713
+ case "countNums":
42714
+ return numeric.length;
42715
+ case "sum":
42716
+ return numeric.reduce((a, b) => a + b, 0);
42717
+ case "average":
42718
+ if (numeric.length === 0) return null;
42719
+ return numeric.reduce((a, b) => a + b, 0) / numeric.length;
42720
+ case "min":
42721
+ return numeric.length === 0 ? null : Math.min(...numeric);
42722
+ case "max":
42723
+ return numeric.length === 0 ? null : Math.max(...numeric);
42724
+ case "product":
42725
+ return numeric.length === 0 ? null : numeric.reduce((a, b) => a * b, 1);
41971
42726
  }
41972
- return void 0;
41973
42727
  }
41974
- function exportWorkbook(workbook, originalZip) {
41975
- const entries = {};
41976
- const stylesPassthrough = !!originalZip && !workbook.styles.dirty && !!originalZip["xl/styles.xml"];
41977
- if (originalZip) {
41978
- const regeneratedPaths = /* @__PURE__ */ new Set();
41979
- regeneratedPaths.add("xl/sharedStrings.xml");
41980
- if (!stylesPassthrough) regeneratedPaths.add("xl/styles.xml");
41981
- regeneratedPaths.add("xl/workbook.xml");
41982
- regeneratedPaths.add("xl/_rels/workbook.xml.rels");
41983
- regeneratedPaths.add("[Content_Types].xml");
41984
- regeneratedPaths.add("_rels/.rels");
41985
- regeneratedPaths.add("docProps/core.xml");
41986
- regeneratedPaths.add("docProps/app.xml");
41987
- for (let i2 = 0; i2 < workbook.sheets.length + 10; i2++) {
41988
- regeneratedPaths.add(`xl/worksheets/sheet${i2 + 1}.xml`);
41989
- regeneratedPaths.add(`xl/worksheets/_rels/sheet${i2 + 1}.xml.rels`);
41990
- }
41991
- for (const path7 of Object.keys(originalZip)) {
41992
- if (path7.startsWith("xl/drawings/") || path7.startsWith("xl/charts/") || path7.startsWith("xl/tables/") || path7.startsWith("xl/media/")) {
41993
- regeneratedPaths.add(path7);
42728
+ function buildGrid(table, source, rowTuples, colTuples, groups, allRecords) {
42729
+ const numRowFields = table.rowFieldIndices.length;
42730
+ const numColFields = table.colFieldIndices.length;
42731
+ const numDataFields = Math.max(table.dataFields.length, 1);
42732
+ const showRowGrand = table.display.colGrandTotals;
42733
+ const showColGrand = table.display.rowGrandTotals;
42734
+ const rowLabelCols = Math.max(numRowFields, 1);
42735
+ const colHeaderRows = numColFields + (numDataFields > 0 ? 1 : 0);
42736
+ const headerRows = Math.max(colHeaderRows, 1);
42737
+ const dataCols = colTuples.length * numDataFields;
42738
+ const totalCols = rowLabelCols + dataCols + (showColGrand ? numDataFields : 0);
42739
+ const totalRows = headerRows + rowTuples.length + (showRowGrand ? 1 : 0);
42740
+ const cells = [];
42741
+ for (let r = 0; r < totalRows; r++) {
42742
+ cells.push(new Array(totalCols).fill({ kind: "blank" }));
42743
+ }
42744
+ for (let level = 0; level < numColFields; level++) {
42745
+ let col = rowLabelCols;
42746
+ for (const tuple of colTuples) {
42747
+ const text = formatCellLabel(tuple[level]);
42748
+ for (let i2 = 0; i2 < numDataFields; i2++) {
42749
+ cells[level][col + i2] = { kind: "colHeader", depth: level, text };
41994
42750
  }
42751
+ col += numDataFields;
41995
42752
  }
41996
- for (const [path7, data] of Object.entries(originalZip)) {
41997
- if (!regeneratedPaths.has(path7)) entries[path7] = data;
41998
- }
41999
- }
42000
- const sharedStrings = buildSharedStrings(workbook);
42001
- entries["xl/sharedStrings.xml"] = strToU8(sharedStrings.xml);
42002
- const stylesResult = stylesPassthrough ? { xml: "", xfMap: /* @__PURE__ */ new Map(), numFmtMap: /* @__PURE__ */ new Map(), dxfMap: /* @__PURE__ */ new Map() } : buildStylesXml(workbook);
42003
- if (!stylesPassthrough) entries["xl/styles.xml"] = strToU8(stylesResult.xml);
42004
- const pivotInfo = extractPivotRoundTripInfo(originalZip);
42005
- entries["xl/workbook.xml"] = strToU8(buildWorkbookXml(workbook, pivotInfo));
42006
- entries["xl/_rels/workbook.xml.rels"] = strToU8(
42007
- buildWorkbookRels(workbook, pivotInfo)
42008
- );
42009
- const extraContentTypes = [];
42010
- for (const path7 of pivotInfo.pivotXmlPaths) {
42011
- const ct = pivotContentTypeFor(path7);
42012
- if (ct) extraContentTypes.push(ct);
42013
42753
  }
42014
- let globalChartIndex = 1;
42015
- let globalImageIndex = 1;
42016
- let globalTableIndex = 1;
42017
- for (let i2 = 0; i2 < workbook.sheets.length; i2++) {
42018
- const sheet = workbook.sheets[i2];
42019
- const sheetRels = [];
42020
- let nextRId = 1;
42021
- const hasCharts = sheet.charts.length > 0;
42022
- const hasImages = sheet.images.length > 0;
42023
- const hasDrawings = sheet.drawings.length > 0;
42024
- const hasTables = sheet.tables.length > 0;
42025
- const hasHyperlinks = sheet.hyperlinks.size > 0;
42026
- const needsDrawing = hasCharts || hasImages || hasDrawings;
42027
- const hyperlinkRIds = /* @__PURE__ */ new Map();
42028
- if (hasHyperlinks) {
42029
- for (const [ref, url] of sheet.hyperlinks) {
42030
- const rId = `rId${nextRId++}`;
42031
- hyperlinkRIds.set(ref, rId);
42032
- sheetRels.push(`<Relationship Id="${rId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="${escapeXml(url)}" TargetMode="External"/>`);
42754
+ if (numDataFields > 0) {
42755
+ const labelRow = colHeaderRows - 1;
42756
+ let col = rowLabelCols;
42757
+ for (let _t = 0; _t < colTuples.length; _t++) {
42758
+ for (let d = 0; d < table.dataFields.length; d++) {
42759
+ cells[labelRow][col + d] = {
42760
+ kind: "valueLabel",
42761
+ text: table.dataFields[d].name
42762
+ };
42033
42763
  }
42764
+ col += numDataFields;
42034
42765
  }
42035
- let drawingRId = "";
42036
- if (needsDrawing) {
42037
- drawingRId = `rId${nextRId++}`;
42038
- sheetRels.push(`<Relationship Id="${drawingRId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing" Target="../drawings/drawing${i2 + 1}.xml"/>`);
42039
- const drawingRels = [];
42040
- let drawingRelId = 1;
42041
- const drawingAnchors = [];
42042
- for (const chart of sheet.charts) {
42043
- const chartRId = `rId${drawingRelId++}`;
42044
- const chartPath = `xl/charts/chart${globalChartIndex}.xml`;
42045
- entries[chartPath] = strToU8(buildChartXml(chart));
42046
- extraContentTypes.push(`<Override PartName="/${chartPath}" ContentType="application/vnd.openxmlformats-officedocument.drawingml.chart+xml"/>`);
42047
- drawingRels.push(`<Relationship Id="${chartRId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart" Target="../charts/chart${globalChartIndex}.xml"/>`);
42048
- drawingAnchors.push(buildChartAnchorXml(chart, chartRId));
42049
- globalChartIndex++;
42050
- }
42051
- for (const image of sheet.images) {
42052
- const imgRId = `rId${drawingRelId++}`;
42053
- const ext = getImageExtension(image.dataUrl);
42054
- const imgPath = `xl/media/image${globalImageIndex}.${ext}`;
42055
- const imgBytes = dataUrlToBytes(image.dataUrl);
42056
- if (imgBytes) {
42057
- entries[imgPath] = imgBytes;
42058
- drawingRels.push(`<Relationship Id="${imgRId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="../media/image${globalImageIndex}.${ext}"/>`);
42059
- drawingAnchors.push(buildImageAnchorXml(image, imgRId));
42060
- globalImageIndex++;
42061
- }
42062
- }
42063
- for (const drawing of sheet.drawings) {
42064
- drawingAnchors.push(buildShapeAnchorXml(drawing));
42065
- }
42066
- entries[`xl/drawings/drawing${i2 + 1}.xml`] = strToU8(
42067
- `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
42068
- <xdr:wsDr xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:c="http://schemas.openxmlformats.org/drawingml/2006/chart">
42069
- ` + drawingAnchors.join("\n") + `
42070
- </xdr:wsDr>`
42071
- );
42072
- extraContentTypes.push(`<Override PartName="/xl/drawings/drawing${i2 + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.drawing+xml"/>`);
42073
- if (drawingRels.length > 0) {
42074
- entries[`xl/drawings/_rels/drawing${i2 + 1}.xml.rels`] = strToU8(
42075
- `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
42076
- <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
42077
- ${drawingRels.join("\n")}
42078
- </Relationships>`
42079
- );
42766
+ if (showColGrand) {
42767
+ for (let d = 0; d < table.dataFields.length; d++) {
42768
+ cells[labelRow][rowLabelCols + dataCols + d] = {
42769
+ kind: "valueLabel",
42770
+ text: table.dataFields[d].name
42771
+ };
42080
42772
  }
42081
42773
  }
42082
- const tableRIds = [];
42083
- if (hasTables) {
42084
- for (const table of sheet.tables) {
42085
- const tableRId = `rId${nextRId++}`;
42086
- tableRIds.push(tableRId);
42087
- const tablePath = `xl/tables/table${globalTableIndex}.xml`;
42088
- entries[tablePath] = strToU8(buildTableXml(table, globalTableIndex));
42089
- extraContentTypes.push(`<Override PartName="/${tablePath}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml"/>`);
42090
- sheetRels.push(`<Relationship Id="${tableRId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/table" Target="../tables/table${globalTableIndex}.xml"/>`);
42091
- globalTableIndex++;
42092
- }
42774
+ }
42775
+ for (let r = 0; r < rowTuples.length; r++) {
42776
+ const rowTuple = rowTuples[r];
42777
+ const gridRow = headerRows + r;
42778
+ for (let level = 0; level < numRowFields; level++) {
42779
+ cells[gridRow][level] = {
42780
+ kind: "rowHeader",
42781
+ depth: level,
42782
+ text: formatCellLabel(rowTuple[level])
42783
+ };
42093
42784
  }
42094
- const pivotTargets = pivotInfo.pivotTablesBySheetIndex.get(i2);
42095
- if (pivotTargets && pivotTargets.length > 0) {
42096
- for (const target of pivotTargets) {
42097
- const rId = `rId${nextRId++}`;
42098
- sheetRels.push(
42099
- `<Relationship Id="${rId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable" Target="${escapeXml(target)}"/>`
42100
- );
42785
+ for (let c = 0; c < colTuples.length; c++) {
42786
+ const colTuple = colTuples[c];
42787
+ const groupRecords = groups.get(JSON.stringify([rowTuple, colTuple])) ?? [];
42788
+ for (let d = 0; d < table.dataFields.length; d++) {
42789
+ const df = table.dataFields[d];
42790
+ const values2 = groupRecords.map((rec) => rec[df.fieldIndex]);
42791
+ cells[gridRow][rowLabelCols + c * numDataFields + d] = {
42792
+ kind: "value",
42793
+ value: aggregate(values2, df.subtotal),
42794
+ numFmt: df.numFmt
42795
+ };
42101
42796
  }
42102
42797
  }
42103
- if (sheetRels.length > 0) {
42104
- entries[`xl/worksheets/_rels/sheet${i2 + 1}.xml.rels`] = strToU8(
42105
- `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
42106
- <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
42107
- ${sheetRels.join("\n")}
42108
- </Relationships>`
42798
+ if (showColGrand) {
42799
+ const rowOnly = allRecords.filter(
42800
+ (rec) => sameTuple(tupleOf(rec, table.rowFieldIndices), rowTuple)
42109
42801
  );
42802
+ for (let d = 0; d < table.dataFields.length; d++) {
42803
+ const df = table.dataFields[d];
42804
+ cells[gridRow][rowLabelCols + dataCols + d] = {
42805
+ kind: "rowTotal",
42806
+ value: aggregate(
42807
+ rowOnly.map((rec) => rec[df.fieldIndex]),
42808
+ df.subtotal
42809
+ )
42810
+ };
42811
+ }
42110
42812
  }
42111
- entries[`xl/worksheets/sheet${i2 + 1}.xml`] = strToU8(
42112
- buildSheetXml(sheet, workbook.styles, sharedStrings.index, i2 === workbook.activeSheetIndex, stylesResult.xfMap, stylesResult.numFmtMap, stylesResult.dxfMap, drawingRId, tableRIds, hyperlinkRIds)
42113
- );
42114
42813
  }
42115
- entries["docProps/core.xml"] = strToU8(buildCoreProps());
42116
- entries["docProps/app.xml"] = strToU8(buildAppProps());
42117
- entries["[Content_Types].xml"] = strToU8(buildContentTypes(workbook.sheets.length, extraContentTypes));
42118
- entries["_rels/.rels"] = strToU8(buildRootRels());
42119
- return zipSync(entries, { level: 6 });
42120
- }
42121
- function buildSharedStrings(workbook) {
42122
- const strings = [];
42123
- const index = /* @__PURE__ */ new Map();
42124
- const richTextMap = /* @__PURE__ */ new Map();
42125
- let totalCount = 0;
42126
- for (const sheet of workbook.sheets) {
42127
- for (const cell of sheet.cells.values()) {
42128
- if (cell.error) continue;
42129
- if (cell.formula && typeof cell.value === "string") continue;
42130
- if (typeof cell.value === "string") {
42131
- totalCount++;
42132
- if (!index.has(cell.value)) {
42133
- index.set(cell.value, strings.length);
42134
- strings.push(cell.value);
42135
- if (cell.richText && cell.richText.length > 0) {
42136
- richTextMap.set(cell.value, cell.richText);
42137
- }
42138
- }
42814
+ if (showRowGrand) {
42815
+ const gridRow = headerRows + rowTuples.length;
42816
+ cells[gridRow][0] = { kind: "totalLabel", text: TOTAL_LABEL };
42817
+ for (let c = 0; c < colTuples.length; c++) {
42818
+ const colTuple = colTuples[c];
42819
+ const colOnly = allRecords.filter(
42820
+ (rec) => sameTuple(tupleOf(rec, table.colFieldIndices), colTuple)
42821
+ );
42822
+ for (let d = 0; d < table.dataFields.length; d++) {
42823
+ const df = table.dataFields[d];
42824
+ cells[gridRow][rowLabelCols + c * numDataFields + d] = {
42825
+ kind: "colTotal",
42826
+ value: aggregate(
42827
+ colOnly.map((rec) => rec[df.fieldIndex]),
42828
+ df.subtotal
42829
+ )
42830
+ };
42139
42831
  }
42140
42832
  }
42141
- }
42142
- const siEntries = strings.map((s) => {
42143
- const richText = richTextMap.get(s);
42144
- if (richText) {
42145
- return `<si>${richText.map((part) => buildRichTextRun(part)).join("")}</si>`;
42833
+ if (showColGrand) {
42834
+ for (let d = 0; d < table.dataFields.length; d++) {
42835
+ const df = table.dataFields[d];
42836
+ cells[gridRow][rowLabelCols + dataCols + d] = {
42837
+ kind: "grandTotal",
42838
+ value: aggregate(
42839
+ allRecords.map((rec) => rec[df.fieldIndex]),
42840
+ df.subtotal
42841
+ )
42842
+ };
42843
+ }
42146
42844
  }
42147
- const needsPreserve = s.length === 0 || s !== s.trim();
42148
- const spaceAttr = needsPreserve ? ' xml:space="preserve"' : "";
42149
- return `<si><t${spaceAttr}>${escapeXml(s)}</t></si>`;
42150
- });
42151
- const xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
42152
- <sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="${totalCount}" uniqueCount="${strings.length}">
42153
- ${siEntries.join("\n")}
42154
- </sst>`;
42155
- return { xml, index };
42845
+ }
42846
+ void source;
42847
+ return {
42848
+ cells,
42849
+ headerRows,
42850
+ rowLabelCols,
42851
+ rows: totalRows,
42852
+ cols: totalCols
42853
+ };
42156
42854
  }
42157
- function buildRichTextRun(part) {
42158
- const needsPreserve = part.text.length === 0 || part.text !== part.text.trim();
42159
- const spaceAttr = needsPreserve ? ' xml:space="preserve"' : "";
42160
- if (!part.font) {
42161
- return `<r><t${spaceAttr}>${escapeXml(part.text)}</t></r>`;
42855
+ function sameTuple(a, b) {
42856
+ if (a.length !== b.length) return false;
42857
+ for (let i2 = 0; i2 < a.length; i2++) {
42858
+ if (a[i2] !== b[i2]) {
42859
+ const an = a[i2] === void 0 || a[i2] === null || a[i2] === "";
42860
+ const bn = b[i2] === void 0 || b[i2] === null || b[i2] === "";
42861
+ if (!(an && bn)) return false;
42862
+ }
42162
42863
  }
42163
- return `<r>${buildRichTextRunProps(part.font)}<t${spaceAttr}>${escapeXml(part.text)}</t></r>`;
42864
+ return true;
42164
42865
  }
42165
- function buildRichTextRunProps(font) {
42166
- let parts = "";
42167
- if (font.bold) parts += "<b/>";
42168
- if (font.italic) parts += "<i/>";
42169
- if (font.strike) parts += "<strike/>";
42170
- if (font.underline) parts += `<u val="${font.underline}"/>`;
42171
- if (font.vertAlign) parts += `<vertAlign val="${font.vertAlign}"/>`;
42172
- if (font.size) parts += `<sz val="${font.size}"/>`;
42173
- if (font.color) parts += `<color rgb="${hexToArgb(font.color)}"/>`;
42174
- if (font.name) parts += `<rFont val="${escapeXml(font.name)}"/>`;
42175
- return `<rPr>${parts}</rPr>`;
42866
+ function formatCellLabel(v) {
42867
+ if (v === void 0 || v === null || v === "") return "(blank)";
42868
+ if (typeof v === "boolean") return v ? "TRUE" : "FALSE";
42869
+ return String(v);
42176
42870
  }
42177
- function buildStylesXml(workbook) {
42178
- const styles = [];
42179
- for (let i2 = 0; i2 < workbook.styles.size; i2++) {
42180
- styles.push(workbook.styles.get(i2));
42181
- }
42182
- const numFmtMap = /* @__PURE__ */ new Map();
42183
- let nextNumFmtId = 164;
42184
- for (const sheet of workbook.sheets) {
42185
- for (const cell of sheet.cells.values()) {
42186
- if (cell.numFmtCode && cell.numFmtCode !== "General" && cell.numFmtCode !== "") {
42187
- if (!numFmtMap.has(cell.numFmtCode)) {
42188
- numFmtMap.set(cell.numFmtCode, nextNumFmtId++);
42189
- }
42190
- }
42191
- }
42871
+
42872
+ // ../xlsx/src/pivot_model.ts
42873
+ var PivotTableModel = class {
42874
+ constructor(config, cache, authored = false) {
42875
+ this.config = config;
42876
+ this.cache = cache;
42877
+ this.authored = authored;
42192
42878
  }
42193
- const cellNumFmtIds = /* @__PURE__ */ new Map();
42194
- for (let si = 0; si < workbook.sheets.length; si++) {
42195
- for (const [ref, cell] of workbook.sheets[si].cells) {
42196
- if (cell.numFmtCode && cell.numFmtCode !== "General" && cell.numFmtCode !== "") {
42197
- const id = numFmtMap.get(cell.numFmtCode);
42198
- if (id !== void 0) cellNumFmtIds.set(`${si}:${ref}`, id);
42199
- }
42879
+ /**
42880
+ * Recompute the pivot result from the workbook's current source data.
42881
+ * Callers are responsible for triggering recomputation; the model does
42882
+ * not subscribe to workbook changes itself.
42883
+ */
42884
+ recompute(workbook) {
42885
+ const source = readSourceData(workbook, this.cache);
42886
+ if (!source) {
42887
+ this.result = void 0;
42888
+ return;
42200
42889
  }
42890
+ this.result = recomputePivot(this.config, this.cache, source);
42201
42891
  }
42202
- const fonts = /* @__PURE__ */ new Map();
42203
- const fontList = [];
42204
- fonts.set("default", 0);
42205
- fontList.push({});
42206
- for (const s of styles) {
42207
- const key = fontKey(s);
42208
- if (!fonts.has(key)) {
42209
- fonts.set(key, fontList.length);
42210
- fontList.push(s);
42211
- }
42212
- }
42213
- const fillEntries = [];
42214
- const fillMap = /* @__PURE__ */ new Map();
42215
- fillEntries.push('<fill><patternFill patternType="none"/></fill>');
42216
- fillEntries.push('<fill><patternFill patternType="gray125"/></fill>');
42217
- fillMap.set("", 0);
42218
- for (const s of styles) {
42219
- const fk = fillKey(s);
42220
- if (fk === "" || fillMap.has(fk)) continue;
42221
- fillMap.set(fk, fillEntries.length);
42222
- fillEntries.push(buildFillXml(s));
42223
- }
42224
- const borderEntries = [];
42225
- const borderMap = /* @__PURE__ */ new Map();
42226
- borderEntries.push("<border><left/><right/><top/><bottom/><diagonal/></border>");
42227
- borderMap.set("", 0);
42228
- for (const s of styles) {
42229
- const bk = borderKey(s);
42230
- if (bk === "" || borderMap.has(bk)) continue;
42231
- borderMap.set(bk, borderEntries.length);
42232
- borderEntries.push(buildBorderXml(s));
42892
+ };
42893
+ function readSourceData(workbook, cache) {
42894
+ if (cache.source.type !== "worksheet") return void 0;
42895
+ const source = cache.source;
42896
+ const sheet = workbook.sheets.find((s) => s.name === source.sheetName);
42897
+ if (!sheet) return void 0;
42898
+ const range = parseRange(source.ref);
42899
+ if (!range) return void 0;
42900
+ const header = [];
42901
+ for (let col = range.startCol; col <= range.endCol; col++) {
42902
+ const cell = sheet.getCell(rowColToRef(range.startRow, col));
42903
+ header.push(formatHeader(cell?.value));
42233
42904
  }
42234
- const fontsXml = fontList.map((f) => buildFontXml(f)).join("\n");
42235
- const fillsXml = fillEntries.join("\n");
42236
- const bordersXml = borderEntries.join("\n");
42237
- let numFmtsXml = "";
42238
- if (numFmtMap.size > 0) {
42239
- const entries = Array.from(numFmtMap.entries()).map(([code, id]) => `<numFmt numFmtId="${id}" formatCode="${escapeXml(code)}"/>`).join("\n");
42240
- numFmtsXml = `<numFmts count="${numFmtMap.size}">
42241
- ${entries}
42242
- </numFmts>
42243
- `;
42905
+ const records = [];
42906
+ for (let row = range.startRow + 1; row <= range.endRow; row++) {
42907
+ const rec = [];
42908
+ for (let col = range.startCol; col <= range.endCol; col++) {
42909
+ const cell = sheet.getCell(rowColToRef(row, col));
42910
+ rec.push(coerceValue(cell?.value));
42911
+ }
42912
+ records.push(rec);
42244
42913
  }
42245
- const xfEntries = [];
42246
- const xfMap = /* @__PURE__ */ new Map();
42247
- for (let styleIdx = 0; styleIdx < styles.length; styleIdx++) {
42248
- const s = styles[styleIdx];
42249
- const fontId = fonts.get(fontKey(s)) ?? 0;
42250
- const fillId = fillMap.get(fillKey(s)) ?? 0;
42251
- const borderId = borderMap.get(borderKey(s)) ?? 0;
42252
- const xfKey = `${styleIdx}:0`;
42253
- xfMap.set(xfKey, xfEntries.length);
42254
- xfEntries.push(buildXfXml(s, fontId, fillId, borderId, 0));
42914
+ return { header, records };
42915
+ }
42916
+ function formatHeader(v) {
42917
+ if (v === void 0 || v === null) return "";
42918
+ return String(v);
42919
+ }
42920
+ function coerceValue(v) {
42921
+ if (v === void 0 || v === null) return void 0;
42922
+ if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
42923
+ return v;
42255
42924
  }
42256
- for (const [cellKey2, numFmtId] of cellNumFmtIds) {
42257
- const [siStr, ref] = cellKey2.split(":");
42258
- const cell = workbook.sheets[parseInt(siStr)].cells.get(ref);
42259
- if (!cell) continue;
42260
- const xfKey = `${cell.styleIndex}:${numFmtId}`;
42261
- if (xfMap.has(xfKey)) continue;
42262
- const s = styles[cell.styleIndex] ?? {};
42263
- const fontId = fonts.get(fontKey(s)) ?? 0;
42264
- const fillId = fillMap.get(fillKey(s)) ?? 0;
42265
- const borderId = borderMap.get(borderKey(s)) ?? 0;
42266
- xfMap.set(xfKey, xfEntries.length);
42267
- xfEntries.push(buildXfXml(s, fontId, fillId, borderId, numFmtId));
42925
+ return String(v);
42926
+ }
42927
+ function parseRange(ref) {
42928
+ const range = ref.includes("!") ? ref.split("!")[1] : ref;
42929
+ const cleaned = range.replace(/\$/g, "");
42930
+ const m = cleaned.match(/^([A-Z]+\d+)(?::([A-Z]+\d+))?$/);
42931
+ if (!m) return void 0;
42932
+ const start = refToRowCol(m[1]);
42933
+ if (!start) return void 0;
42934
+ const endRef = m[2] ?? m[1];
42935
+ const end = refToRowCol(endRef);
42936
+ if (!end) return void 0;
42937
+ return {
42938
+ startRow: start.row,
42939
+ startCol: start.col,
42940
+ endRow: end.row,
42941
+ endCol: end.col
42942
+ };
42943
+ }
42944
+
42945
+ // ../xlsx/src/pivot_writer.ts
42946
+ var MAIN_NS = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
42947
+ var REL_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
42948
+ function sourceValueToItem(v) {
42949
+ if (v === void 0 || v === null || v === "") return { kind: "missing" };
42950
+ if (typeof v === "number") return { kind: "number", value: v };
42951
+ if (typeof v === "boolean") return { kind: "boolean", value: v };
42952
+ return { kind: "string", value: v };
42953
+ }
42954
+ function cacheItemKey(item) {
42955
+ switch (item.kind) {
42956
+ case "missing":
42957
+ return "m:";
42958
+ case "number":
42959
+ return `n:${item.value}`;
42960
+ case "boolean":
42961
+ return `b:${item.value}`;
42962
+ case "string":
42963
+ return `s:${item.value}`;
42964
+ case "date":
42965
+ return `d:${item.value}`;
42966
+ case "error":
42967
+ return `e:${item.value}`;
42268
42968
  }
42269
- const dxfEntries = [];
42270
- const dxfMap = /* @__PURE__ */ new Map();
42271
- for (const sheet of workbook.sheets) {
42272
- for (const cf of sheet.conditionalFormats) {
42273
- for (const rule of cf.rules) {
42274
- if (rule.ruleType === "style" && rule.style) {
42275
- const key = JSON.stringify(rule.style);
42276
- if (!dxfMap.has(key)) {
42277
- dxfMap.set(key, dxfEntries.length);
42278
- dxfEntries.push(buildDxfXml(rule.style));
42279
- }
42969
+ }
42970
+ function enumerateFlags(table, fieldCount) {
42971
+ const axis = /* @__PURE__ */ new Set([...table.rowFieldIndices, ...table.colFieldIndices, ...table.pageFieldIndices]);
42972
+ return Array.from({ length: fieldCount }, (_, i2) => axis.has(i2));
42973
+ }
42974
+ function buildPivotCacheRecordsXml(records, cache, enumerate) {
42975
+ const indexMaps = cache.fields.map((f, i2) => {
42976
+ if (!enumerate[i2]) return void 0;
42977
+ const m = /* @__PURE__ */ new Map();
42978
+ f.items.forEach((item, idx) => m.set(cacheItemKey(item), idx));
42979
+ return m;
42980
+ });
42981
+ const rows = records.map((rec) => {
42982
+ const cells = cache.fields.map((_f, i2) => {
42983
+ const item = sourceValueToItem(rec[i2]);
42984
+ const map2 = indexMaps[i2];
42985
+ if (map2) {
42986
+ const idx = map2.get(cacheItemKey(item));
42987
+ if (idx === void 0) {
42988
+ throw new Error(
42989
+ `buildPivotCacheRecordsXml: value ${JSON.stringify(rec[i2])} for field "${cache.fields[i2].name}" is missing from its cached shared items`
42990
+ );
42280
42991
  }
42992
+ return `<x v="${idx}"/>`;
42281
42993
  }
42282
- }
42283
- }
42284
- let dxfsXml = '<dxfs count="0"/>';
42285
- if (dxfEntries.length > 0) {
42286
- dxfsXml = `<dxfs count="${dxfEntries.length}">
42287
- ${dxfEntries.join("\n")}
42288
- </dxfs>`;
42994
+ return inlineRecordCell(item);
42995
+ });
42996
+ return `<r>${cells.join("")}</r>`;
42997
+ });
42998
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
42999
+ <pivotCacheRecords xmlns="${MAIN_NS}" xmlns:r="${REL_NS}" count="${records.length}">` + rows.join("") + `</pivotCacheRecords>`;
43000
+ }
43001
+ function inlineRecordCell(item) {
43002
+ switch (item.kind) {
43003
+ case "missing":
43004
+ return "<m/>";
43005
+ case "number":
43006
+ return `<n v="${item.value}"/>`;
43007
+ case "boolean":
43008
+ return `<b v="${item.value ? 1 : 0}"/>`;
43009
+ case "date":
43010
+ return `<d v="${escapeXml(item.value)}"/>`;
43011
+ case "error":
43012
+ return `<e v="${escapeXml(item.value)}"/>`;
43013
+ case "string":
43014
+ return `<s v="${escapeXml(item.value)}"/>`;
42289
43015
  }
42290
- const xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
42291
- <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
42292
- ${numFmtsXml}<fonts count="${fontList.length}">
42293
- ${fontsXml}
42294
- </fonts>
42295
- <fills count="${fillEntries.length}">
42296
- ${fillsXml}
42297
- </fills>
42298
- <borders count="${borderEntries.length}">
42299
- ${bordersXml}
42300
- </borders>
42301
- <cellStyleXfs count="1">
42302
- <xf numFmtId="0" fontId="0" fillId="0" borderId="0"/>
42303
- </cellStyleXfs>
42304
- <cellXfs count="${xfEntries.length}">
42305
- ${xfEntries.join("\n")}
42306
- </cellXfs>
42307
- <cellStyles count="1">
42308
- <cellStyle name="Normal" xfId="0" builtinId="0"/>
42309
- </cellStyles>
42310
- ${dxfsXml}
42311
- </styleSheet>`;
42312
- return { xml, xfMap, numFmtMap, dxfMap };
42313
43016
  }
42314
- function buildXfXml(s, fontId, fillId, borderId, numFmtId) {
42315
- let attrs = `numFmtId="${numFmtId}" fontId="${fontId}" fillId="${fillId}" borderId="${borderId}" xfId="0"`;
42316
- if (numFmtId > 0) attrs += ' applyNumberFormat="1"';
42317
- if (fontId > 0) attrs += ' applyFont="1"';
42318
- if (fillId > 0) attrs += ' applyFill="1"';
42319
- if (borderId > 0) attrs += ' applyBorder="1"';
42320
- if (s.horizontalAlign || s.verticalAlign || s.wrapText || s.indent || s.textRotation || s.shrinkToFit) {
42321
- const hAlign = s.horizontalAlign ? ` horizontal="${s.horizontalAlign}"` : "";
42322
- const vAlign = s.verticalAlign ? ` vertical="${s.verticalAlign}"` : "";
42323
- const wrap = s.wrapText ? ' wrapText="1"' : "";
42324
- const indent = s.indent ? ` indent="${s.indent}"` : "";
42325
- const rotation = s.textRotation !== void 0 ? ` textRotation="${s.textRotation === "vertical" ? 255 : s.textRotation}"` : "";
42326
- const shrink = s.shrinkToFit ? ' shrinkToFit="1"' : "";
42327
- return `<xf ${attrs} applyAlignment="1"><alignment${hAlign}${vAlign}${wrap}${indent}${rotation}${shrink}/></xf>`;
43017
+ function buildPivotCacheDefinitionXml(cache, enumerate, recordCount, recordsRelId) {
43018
+ if (cache.source.type !== "worksheet") {
43019
+ throw new Error("buildPivotCacheDefinitionXml: only worksheet sources are supported");
43020
+ }
43021
+ const src = cache.source;
43022
+ const fields = cache.fields.map((f, i2) => cacheFieldXml(f, enumerate[i2])).join("");
43023
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
43024
+ <pivotCacheDefinition xmlns="${MAIN_NS}" xmlns:r="${REL_NS}" r:id="${recordsRelId}" refreshOnLoad="1" refreshedBy="Lotics" createdVersion="6" refreshedVersion="6" minRefreshableVersion="3" recordCount="${recordCount}"><cacheSource type="worksheet"><worksheetSource ref="${escapeXml(src.ref)}" sheet="${escapeXml(src.sheetName)}"/></cacheSource><cacheFields count="${cache.fields.length}">${fields}</cacheFields></pivotCacheDefinition>`;
43025
+ }
43026
+ function cacheFieldXml(field, enumerate) {
43027
+ const name = `name="${escapeXml(field.name)}" numFmtId="0"`;
43028
+ if (!enumerate) {
43029
+ const flags = `containsBlank="${field.items.length === 0 ? 0 : 1}"` + (field.containsNumber ? ` containsString="0" containsNumber="1"` : "");
43030
+ return `<cacheField ${name}><sharedItems ${flags}/></cacheField>`;
43031
+ }
43032
+ const items = field.items.map(sharedItemXml).join("");
43033
+ const hasString = field.items.some((i2) => i2.kind === "string");
43034
+ const hasNumber = field.items.some((i2) => i2.kind === "number");
43035
+ const hasBlank = field.items.some((i2) => i2.kind === "missing");
43036
+ const attrs = `count="${field.items.length}"` + (hasBlank ? ` containsBlank="1"` : "") + (hasNumber && !hasString ? ` containsString="0" containsNumber="1"` : "");
43037
+ return `<cacheField ${name}><sharedItems ${attrs}>${items}</sharedItems></cacheField>`;
43038
+ }
43039
+ function sharedItemXml(item) {
43040
+ switch (item.kind) {
43041
+ case "missing":
43042
+ return `<m/>`;
43043
+ case "number":
43044
+ return `<n v="${item.value}"/>`;
43045
+ case "boolean":
43046
+ return `<b v="${item.value ? 1 : 0}"/>`;
43047
+ case "date":
43048
+ return `<d v="${escapeXml(item.value)}"/>`;
43049
+ case "error":
43050
+ return `<e v="${escapeXml(item.value)}"/>`;
43051
+ case "string":
43052
+ return `<s v="${escapeXml(item.value)}"/>`;
42328
43053
  }
42329
- return `<xf ${attrs}/>`;
42330
43054
  }
42331
- function fontKey(s) {
42332
- return `${s.fontName ?? ""}|${s.fontSize ?? 0}|${s.fontBold ? 1 : 0}|${s.fontItalic ? 1 : 0}|${s.fontColor ?? ""}|${s.fontUnderline ?? ""}|${s.fontStrike ? 1 : 0}`;
43055
+ var SUBTOTAL_FN_TO_ENUM = {
43056
+ sum: "sum",
43057
+ count: "count",
43058
+ countNums: "countNums",
43059
+ average: "average",
43060
+ min: "min",
43061
+ max: "max",
43062
+ product: "product"
43063
+ };
43064
+ function buildPivotTableXml(table, cache) {
43065
+ const rowField = table.rowFieldIndices[0];
43066
+ const colField = table.colFieldIndices[0];
43067
+ if (rowField === void 0 || colField === void 0) {
43068
+ throw new Error("buildPivotTableXml: a row field and a column field are required");
43069
+ }
43070
+ const pageSet = new Set(table.pageFieldIndices);
43071
+ const pivotFields = cache.fields.map((f, i2) => {
43072
+ const onAxis = i2 === rowField ? "axisRow" : i2 === colField ? "axisCol" : pageSet.has(i2) ? "axisPage" : void 0;
43073
+ if (onAxis) return axisPivotFieldXml(onAxis, f.items.length);
43074
+ if (table.dataFields.some((d) => d.fieldIndex === i2)) {
43075
+ return `<pivotField dataField="1" showAll="0"/>`;
43076
+ }
43077
+ return `<pivotField showAll="0"/>`;
43078
+ }).join("");
43079
+ const rowCount = cache.fields[rowField].items.length;
43080
+ const colCount = cache.fields[colField].items.length;
43081
+ const rowItems = axisItemsXml("rowItems", rowCount, table.display.colGrandTotals);
43082
+ const colItems = axisItemsXml("colItems", colCount, table.display.rowGrandTotals);
43083
+ const pageFieldsXml = table.pageFieldIndices.length ? `<pageFields count="${table.pageFieldIndices.length}">` + table.pageFieldIndices.map((fld) => {
43084
+ const sel = table.fields[fld]?.selectedPageItem;
43085
+ const item = sel === null || sel === void 0 ? "" : ` item="${sel}"`;
43086
+ return `<pageField fld="${fld}"${item} hier="-1"/>`;
43087
+ }).join("") + `</pageFields>` : "";
43088
+ const dataFieldsXml = `<dataFields count="${table.dataFields.length}">` + table.dataFields.map((d) => {
43089
+ const fn = SUBTOTAL_FN_TO_ENUM[d.subtotal] ?? "sum";
43090
+ const sub = fn === "sum" ? "" : ` subtotal="${fn}"`;
43091
+ const numFmt = d.numFmt ? ` numFmtId="${escapeXml(d.numFmt)}"` : "";
43092
+ return `<dataField name="${escapeXml(d.name)}" fld="${d.fieldIndex}"${sub} baseField="0" baseItem="0"${numFmt}/>`;
43093
+ }).join("") + `</dataFields>`;
43094
+ const style = `<pivotTableStyleInfo name="${escapeXml(table.styleName ?? "PivotStyleLight16")}" showRowHeaders="1" showColHeaders="1" showRowStripes="${table.display.showRowStripes ? 1 : 0}" showColStripes="${table.display.showColStripes ? 1 : 0}" showLastColumn="1"/>`;
43095
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
43096
+ <pivotTableDefinition xmlns="${MAIN_NS}" xmlns:r="${REL_NS}" name="${escapeXml(table.name)}" cacheId="${table.cacheId}" applyNumberFormats="0" applyBorderFormats="0" applyFontFormats="0" applyPatternFormats="0" applyAlignmentFormats="0" applyWidthHeightFormats="1" dataCaption="Values" updatedVersion="6" minRefreshableVersion="3" useAutoFormatting="1" itemPrintTitles="1" createdVersion="6" indent="0" outline="1" outlineData="1" multipleFieldFilters="0" rowGrandTotals="${table.display.rowGrandTotals ? 1 : 0}" colGrandTotals="${table.display.colGrandTotals ? 1 : 0}"><location ref="${escapeXml(table.ref)}" firstHeaderRow="${table.firstHeaderRow}" firstDataRow="${table.firstDataRow}" firstDataCol="${table.firstDataCol}"/><pivotFields count="${cache.fields.length}">${pivotFields}</pivotFields><rowFields count="1"><field x="${rowField}"/></rowFields>` + rowItems + `<colFields count="1"><field x="${colField}"/></colFields>` + colItems + pageFieldsXml + dataFieldsXml + style + `</pivotTableDefinition>`;
42333
43097
  }
42334
- function buildFontXml(s) {
42335
- let parts = "";
42336
- if (s.fontBold) parts += "<b/>";
42337
- if (s.fontItalic) parts += "<i/>";
42338
- if (s.fontStrike) parts += "<strike/>";
42339
- if (s.fontUnderline) parts += `<u val="${s.fontUnderline}"/>`;
42340
- parts += `<sz val="${s.fontSize ?? 11}"/>`;
42341
- if (s.fontColor) {
42342
- parts += `<color rgb="${hexToArgb(s.fontColor)}"/>`;
42343
- } else {
42344
- parts += '<color theme="1"/>';
42345
- }
42346
- parts += `<name val="${escapeXml(s.fontName ?? "Calibri")}"/>`;
42347
- return `<font>${parts}</font>`;
43098
+ function axisPivotFieldXml(axis, itemCount) {
43099
+ const items = [];
43100
+ for (let i2 = 0; i2 < itemCount; i2++) items.push(`<item x="${i2}"/>`);
43101
+ items.push(`<item t="default"/>`);
43102
+ return `<pivotField axis="${axis}" showAll="0"><items count="${items.length}">${items.join("")}</items></pivotField>`;
42348
43103
  }
42349
- function borderKey(s) {
42350
- const parts = [];
42351
- if (s.borderTop) parts.push(`t:${s.borderTop.style}:${s.borderTop.width}:${s.borderTop.color ?? ""}`);
42352
- if (s.borderRight) parts.push(`r:${s.borderRight.style}:${s.borderRight.width}:${s.borderRight.color ?? ""}`);
42353
- if (s.borderBottom) parts.push(`b:${s.borderBottom.style}:${s.borderBottom.width}:${s.borderBottom.color ?? ""}`);
42354
- if (s.borderLeft) parts.push(`l:${s.borderLeft.style}:${s.borderLeft.width}:${s.borderLeft.color ?? ""}`);
42355
- if (s.borderDiagonal) parts.push(`d:${s.borderDiagonal.style}:${s.borderDiagonal.width}:${s.borderDiagonal.color ?? ""}`);
42356
- if (s.diagonalUp) parts.push("du");
42357
- if (s.diagonalDown) parts.push("dd");
42358
- return parts.join("|");
43104
+ function axisItemsXml(element, itemCount, grandTotal) {
43105
+ const items = [];
43106
+ for (let i2 = 0; i2 < itemCount; i2++) items.push(`<i><x v="${i2}"/></i>`);
43107
+ if (grandTotal) items.push(`<i t="grand"><x/></i>`);
43108
+ return `<${element} count="${items.length}">${items.join("")}</${element}>`;
42359
43109
  }
42360
- function toOoxmlBorderStyle(border2) {
42361
- const ooxmlStyles = [
42362
- "thin",
42363
- "medium",
42364
- "thick",
42365
- "dotted",
42366
- "dashed",
42367
- "double",
42368
- "hair",
42369
- "mediumDashed",
42370
- "dashDot",
42371
- "mediumDashDot",
42372
- "dashDotDot",
42373
- "mediumDashDotDot",
42374
- "slantDashDot"
42375
- ];
42376
- if (ooxmlStyles.includes(border2.style)) return border2.style;
42377
- if (border2.style === "solid") {
42378
- if (border2.width <= 1) return "thin";
42379
- if (border2.width <= 2) return "medium";
42380
- return "thick";
42381
- }
42382
- if (border2.style === "dashed") return "dashed";
42383
- if (border2.style === "dotted") return "dotted";
42384
- if (border2.style === "double") return "double";
42385
- return "thin";
42386
- }
42387
- function buildBorderXml(s) {
42388
- let attrs = "";
42389
- if (s.diagonalUp) attrs += ' diagonalUp="1"';
42390
- if (s.diagonalDown) attrs += ' diagonalDown="1"';
42391
- const sides = [
42392
- { tag: "left", border: s.borderLeft },
42393
- { tag: "right", border: s.borderRight },
42394
- { tag: "top", border: s.borderTop },
42395
- { tag: "bottom", border: s.borderBottom },
42396
- { tag: "diagonal", border: s.borderDiagonal }
42397
- ];
42398
- const inner = sides.map(({ tag, border: border2 }) => {
42399
- if (!border2) return `<${tag}/>`;
42400
- const ooxmlStyle = toOoxmlBorderStyle(border2);
42401
- let colorXml = "";
42402
- if (border2.color) {
42403
- colorXml = `<color rgb="${hexToArgb(border2.color)}"/>`;
42404
- }
42405
- return `<${tag} style="${ooxmlStyle}">${colorXml}</${tag}>`;
42406
- }).join("");
42407
- return `<border${attrs}>${inner}</border>`;
43110
+
43111
+ // ../xlsx/src/xlsx_writer.ts
43112
+ function readWorkbookSheetOrder(originalZip) {
43113
+ const wbBytes = originalZip["xl/workbook.xml"];
43114
+ if (!wbBytes) return [];
43115
+ const xml = decode(wbBytes);
43116
+ const out = [];
43117
+ const re = /<sheet\b[^>]*?r:id="([^"]+)"/g;
43118
+ let m;
43119
+ while ((m = re.exec(xml)) !== null) {
43120
+ out.push(m[1]);
43121
+ }
43122
+ return out;
42408
43123
  }
42409
- function fillKey(s) {
42410
- if (s.gradientData) return `gradient:${JSON.stringify(s.gradientData)}`;
42411
- if (s.patternType && s.backgroundPattern) return `pattern:${s.patternType}|${s.backgroundPattern}`;
42412
- if (s.backgroundColor) return `solid:${s.backgroundColor}`;
42413
- return "";
43124
+ function readWorkbookRels(originalZip) {
43125
+ const out = /* @__PURE__ */ new Map();
43126
+ const bytes = originalZip["xl/_rels/workbook.xml.rels"];
43127
+ if (!bytes) return out;
43128
+ const xml = decode(bytes);
43129
+ const re = /<Relationship\b([^/]*?)\/>/g;
43130
+ let m;
43131
+ while ((m = re.exec(xml)) !== null) {
43132
+ const attrs = m[1];
43133
+ const id = /\bId="([^"]+)"/.exec(attrs)?.[1];
43134
+ const type = /\bType="([^"]+)"/.exec(attrs)?.[1] ?? "";
43135
+ const target = /\bTarget="([^"]+)"/.exec(attrs)?.[1] ?? "";
43136
+ if (id) out.set(id, { type, target });
43137
+ }
43138
+ return out;
42414
43139
  }
42415
- function buildFillXml(s) {
42416
- if (s.gradientData) {
42417
- const g = s.gradientData;
42418
- const stops = g.stops.map((stop) => {
42419
- const hex = hexToArgb(stop.color);
42420
- return `<stop position="${stop.position}"><color rgb="${hex}"/></stop>`;
42421
- }).join("");
42422
- if (g.type === "radial") {
42423
- return `<fill><gradientFill type="path" left="0.5" right="0.5" top="0.5" bottom="0.5">${stops}</gradientFill></fill>`;
43140
+ function readPivotCacheIdMap(originalZip) {
43141
+ const out = /* @__PURE__ */ new Map();
43142
+ const bytes = originalZip["xl/workbook.xml"];
43143
+ if (!bytes) return out;
43144
+ const xml = decode(bytes);
43145
+ const re = /<pivotCache\b([^/]*?)\/>/g;
43146
+ let m;
43147
+ while ((m = re.exec(xml)) !== null) {
43148
+ const attrs = m[1];
43149
+ const cacheIdMatch = /\bcacheId="(\d+)"/.exec(attrs);
43150
+ const ridMatch = /\br:id="([^"]+)"/.exec(attrs);
43151
+ if (cacheIdMatch && ridMatch) {
43152
+ out.set(parseInt(cacheIdMatch[1], 10), ridMatch[1]);
42424
43153
  }
42425
- return `<fill><gradientFill degree="${g.degree}">${stops}</gradientFill></fill>`;
42426
43154
  }
42427
- if (s.patternType && s.backgroundPattern) {
42428
- const colors = extractFillColors(s.backgroundPattern);
42429
- let colorAttrs = "";
42430
- if (colors.fg) colorAttrs += `<fgColor rgb="${hexToArgb(colors.fg)}"/>`;
42431
- if (colors.bg) colorAttrs += `<bgColor rgb="${hexToArgb(colors.bg)}"/>`;
42432
- return `<fill><patternFill patternType="${escapeXml(s.patternType)}">${colorAttrs}</patternFill></fill>`;
43155
+ return out;
43156
+ }
43157
+ function readSheetPivotPaths(originalZip, sheetTarget) {
43158
+ const sheetPath = `xl/${sheetTarget}`;
43159
+ const relsPath = sheetPath.replace(/([^/]+)$/, "_rels/$1.rels");
43160
+ const bytes = originalZip[relsPath];
43161
+ if (!bytes) return [];
43162
+ const xml = decode(bytes);
43163
+ const re = /<Relationship\b([^/]*?)\/>/g;
43164
+ const out = [];
43165
+ let m;
43166
+ while ((m = re.exec(xml)) !== null) {
43167
+ const attrs = m[1];
43168
+ const type = /\bType="([^"]+)"/.exec(attrs)?.[1] ?? "";
43169
+ if (!type.includes("/pivotTable")) continue;
43170
+ const target = /\bTarget="([^"]+)"/.exec(attrs)?.[1];
43171
+ if (target) out.push(target);
42433
43172
  }
42434
- return `<fill><patternFill patternType="solid"><fgColor rgb="${hexToArgb(s.backgroundColor)}"/></patternFill></fill>`;
43173
+ return out;
42435
43174
  }
42436
- function extractFillColors(css) {
42437
- const colorMatches = css.match(/rgba?\([^)]+\)/g);
42438
- if (colorMatches) {
42439
- return { fg: colorMatches[0], bg: colorMatches[1] };
43175
+ function extractPivotRoundTripInfo(originalZip) {
43176
+ const empty = {
43177
+ cachesByWorkbook: /* @__PURE__ */ new Map(),
43178
+ pivotTablesBySheetIndex: /* @__PURE__ */ new Map(),
43179
+ pivotXmlPaths: []
43180
+ };
43181
+ if (!originalZip) return empty;
43182
+ const sheetRIds = readWorkbookSheetOrder(originalZip);
43183
+ const wbRels = readWorkbookRels(originalZip);
43184
+ const cacheRIds = readPivotCacheIdMap(originalZip);
43185
+ const cachesByWorkbook = /* @__PURE__ */ new Map();
43186
+ for (const [cacheId, rid] of cacheRIds) {
43187
+ const rel = wbRels.get(rid);
43188
+ if (!rel) continue;
43189
+ cachesByWorkbook.set(cacheId, rel.target);
42440
43190
  }
42441
- const hexMatches = css.match(/#[0-9a-fA-F]{6}/g);
42442
- if (hexMatches) {
42443
- return { fg: hexMatches[0], bg: hexMatches[1] };
43191
+ const pivotTablesBySheetIndex = /* @__PURE__ */ new Map();
43192
+ for (let i2 = 0; i2 < sheetRIds.length; i2++) {
43193
+ const sheetTarget = wbRels.get(sheetRIds[i2])?.target;
43194
+ if (!sheetTarget) continue;
43195
+ const paths = readSheetPivotPaths(originalZip, sheetTarget);
43196
+ if (paths.length > 0) pivotTablesBySheetIndex.set(i2, paths);
42444
43197
  }
42445
- return { fg: void 0, bg: void 0 };
42446
- }
42447
- function buildSheetXml(sheet, styles, ssIndex, isActive, xfMap, numFmtMap, dxfMap, drawingRId, tableRIds, hyperlinkRIds) {
42448
- const rows = [];
42449
- const rowMap = /* @__PURE__ */ new Map();
42450
- for (const [ref, cell] of sheet.cells) {
42451
- const rc = refToRowCol(ref);
42452
- if (!rc) continue;
42453
- let arr = rowMap.get(rc.row);
42454
- if (!arr) {
42455
- arr = [];
42456
- rowMap.set(rc.row, arr);
43198
+ const pivotXmlPaths = [];
43199
+ for (const path7 of Object.keys(originalZip)) {
43200
+ if (path7.startsWith("xl/pivotTables/") && path7.endsWith(".xml")) {
43201
+ pivotXmlPaths.push(path7);
43202
+ }
43203
+ if (path7.startsWith("xl/pivotCache/") && path7.endsWith(".xml")) {
43204
+ pivotXmlPaths.push(path7);
42457
43205
  }
42458
- arr.push({ col: rc.col, cell });
42459
- }
42460
- const sortedRows = Array.from(rowMap.keys()).sort((a, b) => a - b);
42461
- for (const rowNum of sortedRows) {
42462
- const cells = rowMap.get(rowNum);
42463
- cells.sort((a, b) => a.col - b.col);
42464
- const h = sheet.rowHeights.get(rowNum);
42465
- const rowAttrs = h ? ` ht="${h}" customHeight="1"` : "";
42466
- const hidden = sheet.hiddenRows.has(rowNum) ? ' hidden="1"' : "";
42467
- const cellsXml = cells.map(({ col, cell }) => {
42468
- const ref = rowColToRef(rowNum, col);
42469
- const type = getCellType(cell, ssIndex);
42470
- const value = getCellValue(cell, ssIndex);
42471
- let attrs = `r="${ref}"`;
42472
- let xfIndex = 0;
42473
- if (xfMap.size === 0 && cell.originalXfIndex !== void 0) {
42474
- xfIndex = cell.originalXfIndex;
42475
- } else {
42476
- const numFmtId = cell.numFmtCode && cell.numFmtCode !== "General" && cell.numFmtCode !== "" ? numFmtMap.get(cell.numFmtCode) ?? 0 : 0;
42477
- const xfKey = `${cell.styleIndex}:${numFmtId}`;
42478
- xfIndex = xfMap.get(xfKey) ?? 0;
42479
- }
42480
- if (xfIndex > 0) attrs += ` s="${xfIndex}"`;
42481
- if (type) attrs += ` t="${type}"`;
42482
- let inner = "";
42483
- if (cell.formula) {
42484
- if (cell.isArrayFormula && cell.arrayRange) {
42485
- inner += `<f t="array" ref="${cell.arrayRange}">${escapeXml(cell.formula)}</f>`;
42486
- } else {
42487
- inner += `<f>${escapeXml(cell.formula)}</f>`;
42488
- }
42489
- }
42490
- if (value !== void 0) inner += `<v>${escapeXml(String(value))}</v>`;
42491
- return `<c ${attrs}>${inner}</c>`;
42492
- }).join("");
42493
- rows.push(`<row r="${rowNum}"${rowAttrs}${hidden}>${cellsXml}</row>`);
42494
43206
  }
42495
- const cols = [];
42496
- const allCols = /* @__PURE__ */ new Set([...sheet.colWidths.keys(), ...sheet.hiddenCols]);
42497
- for (const c of Array.from(allCols).sort((a, b) => a - b)) {
42498
- const w = sheet.colWidths.get(c) ?? sheet.defaultColWidth;
42499
- const hidden = sheet.hiddenCols.has(c) ? ' hidden="1"' : "";
42500
- cols.push(`<col min="${c}" max="${c}" width="${w}" customWidth="1"${hidden}/>`);
43207
+ return { cachesByWorkbook, pivotTablesBySheetIndex, pivotXmlPaths };
43208
+ }
43209
+ function decode(bytes) {
43210
+ return new TextDecoder().decode(bytes);
43211
+ }
43212
+ function pivotContentTypeFor(path7) {
43213
+ if (path7.startsWith("xl/pivotTables/") && path7.endsWith(".xml")) {
43214
+ return `<Override PartName="/${path7}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml"/>`;
42501
43215
  }
42502
- let mergeXml = "";
42503
- if (sheet.mergedCells.length > 0) {
42504
- const merges = sheet.mergedCells.map((r) => `<mergeCell ref="${r}"/>`).join("");
42505
- mergeXml = `<mergeCells count="${sheet.mergedCells.length}">${merges}</mergeCells>`;
43216
+ if (path7.includes("/pivotCacheDefinition") && path7.endsWith(".xml")) {
43217
+ return `<Override PartName="/${path7}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml"/>`;
42506
43218
  }
42507
- let paneXml = "";
42508
- let selectionXml = `<selection activeCell="A1" sqref="A1"/>`;
42509
- if (sheet.freeze) {
42510
- const activePane = sheet.freeze.col > 0 && sheet.freeze.row > 0 ? "bottomRight" : sheet.freeze.row > 0 ? "bottomLeft" : "topRight";
42511
- const topLeft = rowColToRef(sheet.freeze.row + 1, sheet.freeze.col + 1);
42512
- paneXml = `<pane xSplit="${sheet.freeze.col}" ySplit="${sheet.freeze.row}" topLeftCell="${topLeft}" activePane="${activePane}" state="frozen"/>`;
42513
- selectionXml = `<selection pane="${activePane}" activeCell="${topLeft}" sqref="${topLeft}"/>`;
43219
+ if (path7.includes("/pivotCacheRecords") && path7.endsWith(".xml")) {
43220
+ return `<Override PartName="/${path7}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml"/>`;
42514
43221
  }
42515
- const tabSelected = isActive ? ' tabSelected="1"' : "";
42516
- const sheetView = `<sheetView${tabSelected} workbookViewId="0"${!sheet.view.showGridLines ? ' showGridLines="0"' : ""}>${paneXml}${selectionXml}</sheetView>`;
42517
- let autoFilterXml = "";
42518
- if (sheet.autoFilter) {
42519
- let filterCols = "";
42520
- for (const col of sheet.autoFilter.columns) {
42521
- if (col.filterValues && col.filterValues.length > 0) {
42522
- const filters = col.filterValues.map((v) => `<filter val="${escapeXml(v)}"/>`).join("");
42523
- filterCols += `<filterColumn colId="${col.colIndex}"><filters>${filters}</filters></filterColumn>`;
42524
- }
43222
+ return void 0;
43223
+ }
43224
+ var REL_BASE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
43225
+ function relsXml(rels) {
43226
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
43227
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
43228
+ ` + rels.map((r) => `<Relationship Id="${r.id}" Type="${r.type}" Target="${escapeXml(r.target)}"/>`).join("\n") + `
43229
+ </Relationships>`;
43230
+ }
43231
+ function emitAuthoredPivots(workbook, entries, startFileIndex) {
43232
+ const info = {
43233
+ cachesByWorkbook: /* @__PURE__ */ new Map(),
43234
+ pivotTablesBySheetIndex: /* @__PURE__ */ new Map(),
43235
+ pivotXmlPaths: []
43236
+ };
43237
+ let n = startFileIndex;
43238
+ for (let i2 = 0; i2 < workbook.sheets.length; i2++) {
43239
+ for (const model of workbook.sheets[i2].pivotTables) {
43240
+ if (!model.authored) continue;
43241
+ const idx = n++;
43242
+ const enumerate = enumerateFlags(model.config, model.cache.fields.length);
43243
+ const records = readSourceData(workbook, model.cache)?.records ?? [];
43244
+ const cacheDefPath = `xl/pivotCache/pivotCacheDefinition${idx}.xml`;
43245
+ const recordsPath = `xl/pivotCache/pivotCacheRecords${idx}.xml`;
43246
+ const tablePath = `xl/pivotTables/pivotTable${idx}.xml`;
43247
+ entries[cacheDefPath] = strToU8(buildPivotCacheDefinitionXml(model.cache, enumerate, records.length, "rId1"));
43248
+ entries[`xl/pivotCache/_rels/pivotCacheDefinition${idx}.xml.rels`] = strToU8(
43249
+ relsXml([{ id: "rId1", type: `${REL_BASE}/pivotCacheRecords`, target: `pivotCacheRecords${idx}.xml` }])
43250
+ );
43251
+ entries[recordsPath] = strToU8(buildPivotCacheRecordsXml(records, model.cache, enumerate));
43252
+ entries[tablePath] = strToU8(buildPivotTableXml(model.config, model.cache));
43253
+ entries[`xl/pivotTables/_rels/pivotTable${idx}.xml.rels`] = strToU8(
43254
+ relsXml([{ id: "rId1", type: `${REL_BASE}/pivotCacheDefinition`, target: `../pivotCache/pivotCacheDefinition${idx}.xml` }])
43255
+ );
43256
+ info.cachesByWorkbook.set(model.cache.id, `pivotCache/pivotCacheDefinition${idx}.xml`);
43257
+ const list = info.pivotTablesBySheetIndex.get(i2) ?? [];
43258
+ list.push(`../pivotTables/pivotTable${idx}.xml`);
43259
+ info.pivotTablesBySheetIndex.set(i2, list);
43260
+ info.pivotXmlPaths.push(tablePath, cacheDefPath, recordsPath);
42525
43261
  }
42526
- autoFilterXml = `<autoFilter ref="${sheet.autoFilter.ref}">${filterCols}</autoFilter>`;
42527
43262
  }
42528
- let dvXml = "";
42529
- if (sheet.dataValidations.length > 0) {
42530
- const dvEntries = sheet.dataValidations.map((dv) => {
42531
- let attrs = `sqref="${dv.ref}" type="${dv.type}"`;
42532
- if (dv.operator) attrs += ` operator="${dv.operator}"`;
42533
- if (!dv.showDropdown) attrs += ' showDropDown="1"';
42534
- if (dv.errorStyle) attrs += ` errorStyle="${dv.errorStyle}"`;
42535
- if (dv.errorTitle) attrs += ` errorTitle="${escapeXml(dv.errorTitle)}"`;
42536
- if (dv.errorMessage) attrs += ` error="${escapeXml(dv.errorMessage)}"`;
42537
- if (dv.promptTitle) attrs += ` promptTitle="${escapeXml(dv.promptTitle)}"`;
42538
- if (dv.promptMessage) attrs += ` prompt="${escapeXml(dv.promptMessage)}"`;
42539
- let inner = "";
42540
- if (dv.formula1) inner += `<formula1>${escapeXml(dv.formula1)}</formula1>`;
42541
- if (dv.formula2) inner += `<formula2>${escapeXml(dv.formula2)}</formula2>`;
42542
- return `<dataValidation ${attrs}>${inner}</dataValidation>`;
42543
- }).join("");
42544
- dvXml = `<dataValidations count="${sheet.dataValidations.length}">${dvEntries}</dataValidations>`;
42545
- }
42546
- let cfXml = "";
42547
- if (sheet.conditionalFormats.length > 0) {
42548
- cfXml = sheet.conditionalFormats.map((cf) => buildConditionalFormattingXml(cf, dxfMap)).join("");
42549
- }
42550
- let hyperlinksXml = "";
42551
- if (hyperlinkRIds && hyperlinkRIds.size > 0) {
42552
- const hlEntries = Array.from(hyperlinkRIds.entries()).map(([ref, rId]) => `<hyperlink ref="${ref}" r:id="${rId}"/>`).join("");
42553
- hyperlinksXml = `<hyperlinks>${hlEntries}</hyperlinks>`;
43263
+ return info;
43264
+ }
43265
+ function mergePivotInfo(a, b) {
43266
+ const cachesByWorkbook = new Map(a.cachesByWorkbook);
43267
+ for (const [k, v] of b.cachesByWorkbook) cachesByWorkbook.set(k, v);
43268
+ const pivotTablesBySheetIndex = /* @__PURE__ */ new Map();
43269
+ for (const [k, v] of a.pivotTablesBySheetIndex) pivotTablesBySheetIndex.set(k, [...v]);
43270
+ for (const [k, v] of b.pivotTablesBySheetIndex) {
43271
+ pivotTablesBySheetIndex.set(k, [...pivotTablesBySheetIndex.get(k) ?? [], ...v]);
42554
43272
  }
42555
- const drawingXml = drawingRId ? `<drawing r:id="${drawingRId}"/>` : "";
42556
- let tablePartsXml = "";
42557
- if (tableRIds && tableRIds.length > 0) {
42558
- const parts = tableRIds.map((rId) => `<tablePart r:id="${rId}"/>`).join("");
42559
- tablePartsXml = `<tableParts count="${tableRIds.length}">${parts}</tableParts>`;
43273
+ return { cachesByWorkbook, pivotTablesBySheetIndex, pivotXmlPaths: [...a.pivotXmlPaths, ...b.pivotXmlPaths] };
43274
+ }
43275
+ function exportWorkbook(workbook, originalZip) {
43276
+ const entries = {};
43277
+ const stylesPassthrough = !!originalZip && !workbook.styles.dirty && !!originalZip["xl/styles.xml"];
43278
+ if (originalZip) {
43279
+ const regeneratedPaths = /* @__PURE__ */ new Set();
43280
+ regeneratedPaths.add("xl/sharedStrings.xml");
43281
+ if (!stylesPassthrough) regeneratedPaths.add("xl/styles.xml");
43282
+ regeneratedPaths.add("xl/workbook.xml");
43283
+ regeneratedPaths.add("xl/_rels/workbook.xml.rels");
43284
+ regeneratedPaths.add("[Content_Types].xml");
43285
+ regeneratedPaths.add("_rels/.rels");
43286
+ regeneratedPaths.add("docProps/core.xml");
43287
+ regeneratedPaths.add("docProps/app.xml");
43288
+ for (let i2 = 0; i2 < workbook.sheets.length + 10; i2++) {
43289
+ regeneratedPaths.add(`xl/worksheets/sheet${i2 + 1}.xml`);
43290
+ regeneratedPaths.add(`xl/worksheets/_rels/sheet${i2 + 1}.xml.rels`);
43291
+ }
43292
+ for (const path7 of Object.keys(originalZip)) {
43293
+ if (path7.startsWith("xl/drawings/") || path7.startsWith("xl/charts/") || path7.startsWith("xl/tables/") || path7.startsWith("xl/media/")) {
43294
+ regeneratedPaths.add(path7);
43295
+ }
43296
+ }
43297
+ for (const [path7, data] of Object.entries(originalZip)) {
43298
+ if (!regeneratedPaths.has(path7)) entries[path7] = data;
43299
+ }
42560
43300
  }
42561
- const ps = sheet.pageSetup;
42562
- const hf = sheet.headerFooter;
42563
- const fitToPage = ps && (ps.fitToWidth !== void 0 || ps.fitToHeight !== void 0);
42564
- const sheetPrXml = fitToPage ? `<sheetPr><pageSetUpPr fitToPage="1"/></sheetPr>` : "";
42565
- const marginsXml = (() => {
42566
- const m = ps?.margins;
42567
- const left = m?.left ?? 0.7;
42568
- const right = m?.right ?? 0.7;
42569
- const top = m?.top ?? 0.75;
42570
- const bottom = m?.bottom ?? 0.75;
42571
- const header = m?.header ?? 0.3;
42572
- const footer = m?.footer ?? 0.3;
42573
- return `<pageMargins left="${left}" right="${right}" top="${top}" bottom="${bottom}" header="${header}" footer="${footer}"/>`;
42574
- })();
42575
- let pageSetupXml = "";
42576
- if (ps) {
42577
- const attrs = [];
42578
- if (ps.paperSize !== void 0) attrs.push(`paperSize="${ps.paperSize}"`);
42579
- if (ps.scale !== void 0) attrs.push(`scale="${ps.scale}"`);
42580
- if (ps.fitToWidth !== void 0) attrs.push(`fitToWidth="${ps.fitToWidth}"`);
42581
- if (ps.fitToHeight !== void 0) attrs.push(`fitToHeight="${ps.fitToHeight}"`);
42582
- if (ps.orientation) attrs.push(`orientation="${ps.orientation}"`);
42583
- if (attrs.length > 0) pageSetupXml = `<pageSetup ${attrs.join(" ")}/>`;
43301
+ const sharedStrings = buildSharedStrings(workbook);
43302
+ entries["xl/sharedStrings.xml"] = strToU8(sharedStrings.xml);
43303
+ const stylesResult = stylesPassthrough ? { xml: "", xfMap: /* @__PURE__ */ new Map(), numFmtMap: /* @__PURE__ */ new Map(), dxfMap: /* @__PURE__ */ new Map() } : buildStylesXml(workbook);
43304
+ if (!stylesPassthrough) entries["xl/styles.xml"] = strToU8(stylesResult.xml);
43305
+ const roundTripInfo = extractPivotRoundTripInfo(originalZip);
43306
+ const authoredStartIndex = roundTripInfo.pivotXmlPaths.filter((p) => p.startsWith("xl/pivotTables/")).length + 1;
43307
+ const pivotInfo = mergePivotInfo(roundTripInfo, emitAuthoredPivots(workbook, entries, authoredStartIndex));
43308
+ entries["xl/workbook.xml"] = strToU8(buildWorkbookXml(workbook, pivotInfo));
43309
+ entries["xl/_rels/workbook.xml.rels"] = strToU8(
43310
+ buildWorkbookRels(workbook, pivotInfo)
43311
+ );
43312
+ const extraContentTypes = [];
43313
+ for (const path7 of pivotInfo.pivotXmlPaths) {
43314
+ const ct = pivotContentTypeFor(path7);
43315
+ if (ct) extraContentTypes.push(ct);
42584
43316
  }
42585
- let headerFooterXml = "";
42586
- if (hf) {
42587
- const rootAttrs = [];
42588
- if (hf.differentOddEven) rootAttrs.push(`differentOddEven="1"`);
42589
- if (hf.differentFirst) rootAttrs.push(`differentFirst="1"`);
42590
- const inner = [];
42591
- if (hf.oddHeader) inner.push(`<oddHeader>${escapeXml(hf.oddHeader)}</oddHeader>`);
42592
- if (hf.oddFooter) inner.push(`<oddFooter>${escapeXml(hf.oddFooter)}</oddFooter>`);
42593
- if (hf.evenHeader) inner.push(`<evenHeader>${escapeXml(hf.evenHeader)}</evenHeader>`);
42594
- if (hf.evenFooter) inner.push(`<evenFooter>${escapeXml(hf.evenFooter)}</evenFooter>`);
42595
- if (hf.firstHeader) inner.push(`<firstHeader>${escapeXml(hf.firstHeader)}</firstHeader>`);
42596
- if (hf.firstFooter) inner.push(`<firstFooter>${escapeXml(hf.firstFooter)}</firstFooter>`);
42597
- if (inner.length > 0) {
42598
- const attrStr = rootAttrs.length > 0 ? ` ${rootAttrs.join(" ")}` : "";
42599
- headerFooterXml = `<headerFooter${attrStr}>${inner.join("")}</headerFooter>`;
43317
+ let globalChartIndex = 1;
43318
+ let globalImageIndex = 1;
43319
+ let globalTableIndex = 1;
43320
+ for (let i2 = 0; i2 < workbook.sheets.length; i2++) {
43321
+ const sheet = workbook.sheets[i2];
43322
+ const sheetRels = [];
43323
+ let nextRId = 1;
43324
+ const hasCharts = sheet.charts.length > 0;
43325
+ const hasImages = sheet.images.length > 0;
43326
+ const hasDrawings = sheet.drawings.length > 0;
43327
+ const hasTables = sheet.tables.length > 0;
43328
+ const hasHyperlinks = sheet.hyperlinks.size > 0;
43329
+ const needsDrawing = hasCharts || hasImages || hasDrawings;
43330
+ const hyperlinkRIds = /* @__PURE__ */ new Map();
43331
+ if (hasHyperlinks) {
43332
+ for (const [ref, url] of sheet.hyperlinks) {
43333
+ const rId = `rId${nextRId++}`;
43334
+ hyperlinkRIds.set(ref, rId);
43335
+ sheetRels.push(`<Relationship Id="${rId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="${escapeXml(url)}" TargetMode="External"/>`);
43336
+ }
42600
43337
  }
42601
- }
42602
- let dimensionRef = "A1";
42603
- if (sortedRows.length > 0) {
42604
- let minCol = Infinity, maxCol = 0;
42605
- const minRow = sortedRows[0];
42606
- const maxRow = sortedRows[sortedRows.length - 1];
42607
- for (const rowNum of sortedRows) {
42608
- const cells = rowMap.get(rowNum);
42609
- for (const { col } of cells) {
42610
- if (col < minCol) minCol = col;
42611
- if (col > maxCol) maxCol = col;
43338
+ let drawingRId = "";
43339
+ if (needsDrawing) {
43340
+ drawingRId = `rId${nextRId++}`;
43341
+ sheetRels.push(`<Relationship Id="${drawingRId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing" Target="../drawings/drawing${i2 + 1}.xml"/>`);
43342
+ const drawingRels = [];
43343
+ let drawingRelId = 1;
43344
+ const drawingAnchors = [];
43345
+ for (const chart of sheet.charts) {
43346
+ const chartRId = `rId${drawingRelId++}`;
43347
+ const chartPath = `xl/charts/chart${globalChartIndex}.xml`;
43348
+ entries[chartPath] = strToU8(buildChartXml(chart));
43349
+ extraContentTypes.push(`<Override PartName="/${chartPath}" ContentType="application/vnd.openxmlformats-officedocument.drawingml.chart+xml"/>`);
43350
+ drawingRels.push(`<Relationship Id="${chartRId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart" Target="../charts/chart${globalChartIndex}.xml"/>`);
43351
+ drawingAnchors.push(buildChartAnchorXml(chart, chartRId));
43352
+ globalChartIndex++;
43353
+ }
43354
+ for (const image of sheet.images) {
43355
+ const imgRId = `rId${drawingRelId++}`;
43356
+ const ext = getImageExtension(image.dataUrl);
43357
+ const imgPath = `xl/media/image${globalImageIndex}.${ext}`;
43358
+ const imgBytes = dataUrlToBytes(image.dataUrl);
43359
+ if (imgBytes) {
43360
+ entries[imgPath] = imgBytes;
43361
+ drawingRels.push(`<Relationship Id="${imgRId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="../media/image${globalImageIndex}.${ext}"/>`);
43362
+ drawingAnchors.push(buildImageAnchorXml(image, imgRId));
43363
+ globalImageIndex++;
43364
+ }
43365
+ }
43366
+ for (const drawing of sheet.drawings) {
43367
+ drawingAnchors.push(buildShapeAnchorXml(drawing));
43368
+ }
43369
+ entries[`xl/drawings/drawing${i2 + 1}.xml`] = strToU8(
43370
+ `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
43371
+ <xdr:wsDr xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:c="http://schemas.openxmlformats.org/drawingml/2006/chart">
43372
+ ` + drawingAnchors.join("\n") + `
43373
+ </xdr:wsDr>`
43374
+ );
43375
+ extraContentTypes.push(`<Override PartName="/xl/drawings/drawing${i2 + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.drawing+xml"/>`);
43376
+ if (drawingRels.length > 0) {
43377
+ entries[`xl/drawings/_rels/drawing${i2 + 1}.xml.rels`] = strToU8(
43378
+ `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
43379
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
43380
+ ${drawingRels.join("\n")}
43381
+ </Relationships>`
43382
+ );
42612
43383
  }
42613
43384
  }
42614
- dimensionRef = `${rowColToRef(minRow, minCol)}:${rowColToRef(maxRow, maxCol)}`;
43385
+ const tableRIds = [];
43386
+ if (hasTables) {
43387
+ for (const table of sheet.tables) {
43388
+ const tableRId = `rId${nextRId++}`;
43389
+ tableRIds.push(tableRId);
43390
+ const tablePath = `xl/tables/table${globalTableIndex}.xml`;
43391
+ entries[tablePath] = strToU8(buildTableXml(table, globalTableIndex));
43392
+ extraContentTypes.push(`<Override PartName="/${tablePath}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml"/>`);
43393
+ sheetRels.push(`<Relationship Id="${tableRId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/table" Target="../tables/table${globalTableIndex}.xml"/>`);
43394
+ globalTableIndex++;
43395
+ }
43396
+ }
43397
+ const pivotTargets = pivotInfo.pivotTablesBySheetIndex.get(i2);
43398
+ if (pivotTargets && pivotTargets.length > 0) {
43399
+ for (const target of pivotTargets) {
43400
+ const rId = `rId${nextRId++}`;
43401
+ sheetRels.push(
43402
+ `<Relationship Id="${rId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable" Target="${escapeXml(target)}"/>`
43403
+ );
43404
+ }
43405
+ }
43406
+ if (sheetRels.length > 0) {
43407
+ entries[`xl/worksheets/_rels/sheet${i2 + 1}.xml.rels`] = strToU8(
43408
+ `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
43409
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
43410
+ ${sheetRels.join("\n")}
43411
+ </Relationships>`
43412
+ );
43413
+ }
43414
+ entries[`xl/worksheets/sheet${i2 + 1}.xml`] = strToU8(
43415
+ buildSheetXml(sheet, workbook.styles, sharedStrings.index, i2 === workbook.activeSheetIndex, stylesResult.xfMap, stylesResult.numFmtMap, stylesResult.dxfMap, drawingRId, tableRIds, hyperlinkRIds)
43416
+ );
42615
43417
  }
42616
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
42617
- <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
42618
- xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
42619
- ${sheetPrXml}<dimension ref="${dimensionRef}"/>
42620
- <sheetViews>${sheetView}</sheetViews>
42621
- <sheetFormatPr defaultRowHeight="${sheet.defaultRowHeight}" defaultColWidth="${sheet.defaultColWidth}"/>
42622
- ${cols.length > 0 ? `<cols>${cols.join("")}</cols>` : ""}
42623
- <sheetData>
42624
- ${rows.join("\n")}
42625
- </sheetData>
42626
- ${autoFilterXml}${mergeXml}${cfXml}${dvXml}${hyperlinksXml}${marginsXml}${pageSetupXml}${headerFooterXml}${drawingXml}${tablePartsXml}
42627
- </worksheet>`;
42628
- }
42629
- function getCellType(cell, ssIndex) {
42630
- if (cell.error) return "e";
42631
- if (cell.formula && typeof cell.value === "string") return "str";
42632
- if (typeof cell.value === "string" && ssIndex.has(cell.value)) return "s";
42633
- if (typeof cell.value === "boolean") return "b";
42634
- return void 0;
43418
+ entries["docProps/core.xml"] = strToU8(buildCoreProps());
43419
+ entries["docProps/app.xml"] = strToU8(buildAppProps());
43420
+ entries["[Content_Types].xml"] = strToU8(buildContentTypes(workbook.sheets.length, extraContentTypes));
43421
+ entries["_rels/.rels"] = strToU8(buildRootRels());
43422
+ return zipSync(entries, { level: 6 });
42635
43423
  }
42636
- function getCellValue(cell, ssIndex) {
42637
- if (cell.value === null) return void 0;
42638
- if (cell.error) return cell.error;
42639
- if (cell.formula && typeof cell.value === "string") return cell.value;
42640
- if (typeof cell.value === "string") {
42641
- const idx = ssIndex.get(cell.value);
42642
- return idx !== void 0 ? idx : cell.value;
43424
+ function buildSharedStrings(workbook) {
43425
+ const strings = [];
43426
+ const index = /* @__PURE__ */ new Map();
43427
+ const richTextMap = /* @__PURE__ */ new Map();
43428
+ let totalCount = 0;
43429
+ for (const sheet of workbook.sheets) {
43430
+ for (const cell of sheet.cells.values()) {
43431
+ if (cell.error) continue;
43432
+ if (cell.formula && typeof cell.value === "string") continue;
43433
+ if (typeof cell.value === "string") {
43434
+ totalCount++;
43435
+ if (!index.has(cell.value)) {
43436
+ index.set(cell.value, strings.length);
43437
+ strings.push(cell.value);
43438
+ if (cell.richText && cell.richText.length > 0) {
43439
+ richTextMap.set(cell.value, cell.richText);
43440
+ }
43441
+ }
43442
+ }
43443
+ }
42643
43444
  }
42644
- if (typeof cell.value === "boolean") return cell.value ? 1 : 0;
42645
- return cell.value;
42646
- }
42647
- function quoteSheetName(name) {
42648
- return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) ? name : `'${name.replace(/'/g, "''")}'`;
42649
- }
42650
- function buildWorkbookXml(workbook, pivotInfo) {
42651
- const sheets = workbook.sheets.map(
42652
- (s, i2) => `<sheet name="${escapeXml(s.name)}" sheetId="${i2 + 1}" r:id="rId${i2 + 1}"/>`
42653
- ).join("\n");
42654
- const nameEntries = [];
42655
- for (const [name, value] of workbook.namedRanges) {
42656
- nameEntries.push(`<definedName name="${escapeXml(name)}">${escapeXml(value)}</definedName>`);
42657
- }
42658
- workbook.sheets.forEach((s, i2) => {
42659
- const pt = s.printTitles;
42660
- if (!pt || !pt.repeatRows && !pt.repeatCols) return;
42661
- const parts = [];
42662
- const sheetName = quoteSheetName(s.name);
42663
- if (pt.repeatCols) {
42664
- const [start, end] = pt.repeatCols;
42665
- parts.push(`${sheetName}!$${colNumToLetters(start)}:$${colNumToLetters(end)}`);
42666
- }
42667
- if (pt.repeatRows) {
42668
- const [start, end] = pt.repeatRows;
42669
- parts.push(`${sheetName}!$${start}:$${end}`);
43445
+ const siEntries = strings.map((s) => {
43446
+ const richText = richTextMap.get(s);
43447
+ if (richText) {
43448
+ return `<si>${richText.map((part) => buildRichTextRun(part)).join("")}</si>`;
42670
43449
  }
42671
- nameEntries.push(
42672
- `<definedName name="_xlnm.Print_Titles" localSheetId="${i2}">${escapeXml(parts.join(","))}</definedName>`
42673
- );
43450
+ const needsPreserve = s.length === 0 || s !== s.trim();
43451
+ const spaceAttr = needsPreserve ? ' xml:space="preserve"' : "";
43452
+ return `<si><t${spaceAttr}>${escapeXml(s)}</t></si>`;
42674
43453
  });
42675
- const definedNames = nameEntries.length > 0 ? `
42676
- <definedNames>
42677
- ${nameEntries.join("\n")}
42678
- </definedNames>` : "";
42679
- let pivotCachesBlock = "";
42680
- if (pivotInfo && pivotInfo.cachesByWorkbook.size > 0) {
42681
- const baseRId = workbook.sheets.length + 3;
42682
- const cacheIds = [...pivotInfo.cachesByWorkbook.keys()].sort((a, b) => a - b);
42683
- const items = cacheIds.map((cacheId, i2) => `<pivotCache cacheId="${cacheId}" r:id="rId${baseRId + i2}"/>`).join("\n");
42684
- pivotCachesBlock = `
42685
- <pivotCaches>
42686
- ${items}
42687
- </pivotCaches>`;
42688
- }
42689
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
42690
- <workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
42691
- xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
42692
- <bookViews>
42693
- <workbookView activeTab="${workbook.activeSheetIndex}"/>
42694
- </bookViews>
42695
- <sheets>
42696
- ${sheets}
42697
- </sheets>${definedNames}${pivotCachesBlock}
42698
- <calcPr fullCalcOnLoad="1"/>
42699
- </workbook>`;
43454
+ const xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
43455
+ <sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="${totalCount}" uniqueCount="${strings.length}">
43456
+ ${siEntries.join("\n")}
43457
+ </sst>`;
43458
+ return { xml, index };
42700
43459
  }
42701
- function buildWorkbookRels(workbook, pivotInfo) {
42702
- const rels = workbook.sheets.map(
42703
- (_, i2) => `<Relationship Id="rId${i2 + 1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet${i2 + 1}.xml"/>`
42704
- );
42705
- rels.push(`<Relationship Id="rId${workbook.sheets.length + 1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>`);
42706
- rels.push(`<Relationship Id="rId${workbook.sheets.length + 2}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>`);
42707
- if (pivotInfo && pivotInfo.cachesByWorkbook.size > 0) {
42708
- const baseRId = workbook.sheets.length + 3;
42709
- const cacheIds = [...pivotInfo.cachesByWorkbook.keys()].sort((a, b) => a - b);
42710
- cacheIds.forEach((cacheId, i2) => {
42711
- const target = pivotInfo.cachesByWorkbook.get(cacheId);
42712
- if (!target) return;
42713
- rels.push(
42714
- `<Relationship Id="rId${baseRId + i2}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition" Target="${escapeXml(target)}"/>`
42715
- );
42716
- });
43460
+ function buildRichTextRun(part) {
43461
+ const needsPreserve = part.text.length === 0 || part.text !== part.text.trim();
43462
+ const spaceAttr = needsPreserve ? ' xml:space="preserve"' : "";
43463
+ if (!part.font) {
43464
+ return `<r><t${spaceAttr}>${escapeXml(part.text)}</t></r>`;
42717
43465
  }
42718
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
42719
- <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
42720
- ${rels.join("\n")}
42721
- </Relationships>`;
42722
- }
42723
- function buildContentTypes(sheetCount, extraTypes = []) {
42724
- const sheetTypes = Array.from(
42725
- { length: sheetCount },
42726
- (_, i2) => `<Override PartName="/xl/worksheets/sheet${i2 + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>`
42727
- ).join("\n");
42728
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
42729
- <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
42730
- <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
42731
- <Default Extension="xml" ContentType="application/xml"/>
42732
- <Default Extension="png" ContentType="image/png"/>
42733
- <Default Extension="jpeg" ContentType="image/jpeg"/>
42734
- <Default Extension="jpg" ContentType="image/jpeg"/>
42735
- <Default Extension="gif" ContentType="image/gif"/>
42736
- <Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
42737
- ${sheetTypes}
42738
- <Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>
42739
- <Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>
42740
- <Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
42741
- <Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
42742
- ${extraTypes.join("\n")}
42743
- </Types>`;
42744
- }
42745
- function buildRootRels() {
42746
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
42747
- <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
42748
- <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
42749
- <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
42750
- <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
42751
- </Relationships>`;
42752
- }
42753
- function buildCoreProps() {
42754
- const now = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
42755
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
42756
- <cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties"
42757
- xmlns:dc="http://purl.org/dc/elements/1.1/"
42758
- xmlns:dcterms="http://purl.org/dc/terms/"
42759
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
42760
- <dcterms:created xsi:type="dcterms:W3CDTF">${now}</dcterms:created>
42761
- <dcterms:modified xsi:type="dcterms:W3CDTF">${now}</dcterms:modified>
42762
- </cp:coreProperties>`;
42763
- }
42764
- function buildAppProps() {
42765
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
42766
- <Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties">
42767
- <Application>Microsoft Excel</Application>
42768
- </Properties>`;
43466
+ return `<r>${buildRichTextRunProps(part.font)}<t${spaceAttr}>${escapeXml(part.text)}</t></r>`;
42769
43467
  }
42770
- var CHART_TYPE_MAP2 = {
42771
- bar: "c:barChart",
42772
- col: "c:barChart",
42773
- line: "c:lineChart",
42774
- pie: "c:pieChart",
42775
- doughnut: "c:doughnutChart",
42776
- area: "c:areaChart",
42777
- scatter: "c:scatterChart",
42778
- bubble: "c:bubbleChart",
42779
- radar: "c:radarChart",
42780
- stock: "c:stockChart",
42781
- surface: "c:surfaceChart"
42782
- };
42783
- function buildSeriesXml(s, idx, chartType, categories) {
42784
- let nameXml = "";
42785
- if (s.name) nameXml = `<c:tx><c:strRef><c:strCache><c:ptCount val="1"/><c:pt idx="0"><c:v>${escapeXml(s.name)}</c:v></c:pt></c:strCache></c:strRef></c:tx>`;
42786
- let colorXml = "";
42787
- if (s.color) colorXml = `<c:spPr><a:solidFill><a:srgbClr val="${hexToOoxmlRgb(s.color)}"/></a:solidFill></c:spPr>`;
42788
- const valTag = chartType === "scatter" || chartType === "bubble" ? "c:yVal" : "c:val";
42789
- const valPts = s.values.map((v, i2) => `<c:pt idx="${i2}"><c:v>${v}</c:v></c:pt>`).join("");
42790
- const valXml = `<${valTag}><c:numRef><c:numCache><c:ptCount val="${s.values.length}"/>${valPts}</c:numCache></c:numRef></${valTag}>`;
42791
- let catXml = "";
42792
- if (categories && categories.length > 0) {
42793
- const catTag = chartType === "scatter" || chartType === "bubble" ? "c:xVal" : "c:cat";
42794
- const catPts = categories.map((c, i2) => `<c:pt idx="${i2}"><c:v>${escapeXml(c)}</c:v></c:pt>`).join("");
42795
- catXml = `<${catTag}><c:strRef><c:strCache><c:ptCount val="${categories.length}"/>${catPts}</c:strCache></c:strRef></${catTag}>`;
42796
- }
42797
- let bubbleXml = "";
42798
- if (s.bubbleSizes) {
42799
- const bPts = s.bubbleSizes.map((v, i2) => `<c:pt idx="${i2}"><c:v>${v}</c:v></c:pt>`).join("");
42800
- bubbleXml = `<c:bubbleSize><c:numRef><c:numCache><c:ptCount val="${s.bubbleSizes.length}"/>${bPts}</c:numCache></c:numRef></c:bubbleSize>`;
42801
- }
42802
- return `<c:ser><c:idx val="${idx}"/><c:order val="${idx}"/>${nameXml}${colorXml}${catXml}${valXml}${bubbleXml}</c:ser>`;
43468
+ function buildRichTextRunProps(font) {
43469
+ let parts = "";
43470
+ if (font.bold) parts += "<b/>";
43471
+ if (font.italic) parts += "<i/>";
43472
+ if (font.strike) parts += "<strike/>";
43473
+ if (font.underline) parts += `<u val="${font.underline}"/>`;
43474
+ if (font.vertAlign) parts += `<vertAlign val="${font.vertAlign}"/>`;
43475
+ if (font.size) parts += `<sz val="${font.size}"/>`;
43476
+ if (font.color) parts += `<color rgb="${hexToArgb(font.color)}"/>`;
43477
+ if (font.name) parts += `<rFont val="${escapeXml(font.name)}"/>`;
43478
+ return `<rPr>${parts}</rPr>`;
42803
43479
  }
42804
- function buildChartTypeElement(chartType, seriesXml, needsAxIds) {
42805
- const chartTag = CHART_TYPE_MAP2[chartType] ?? "c:barChart";
42806
- const isBar = chartType === "bar";
42807
- let barDir = "";
42808
- if (chartTag === "c:barChart") {
42809
- barDir = isBar ? '<c:barDir val="bar"/>' : '<c:barDir val="col"/>';
43480
+ function buildStylesXml(workbook) {
43481
+ const styles = [];
43482
+ for (let i2 = 0; i2 < workbook.styles.size; i2++) {
43483
+ styles.push(workbook.styles.get(i2));
42810
43484
  }
42811
- let grouping = "";
42812
- if (chartTag === "c:barChart") {
42813
- grouping = '<c:grouping val="clustered"/>';
42814
- } else if (chartTag === "c:lineChart" || chartTag === "c:areaChart") {
42815
- grouping = '<c:grouping val="clustered"/>';
43485
+ const numFmtMap = /* @__PURE__ */ new Map();
43486
+ let nextNumFmtId = 164;
43487
+ for (const sheet of workbook.sheets) {
43488
+ for (const cell of sheet.cells.values()) {
43489
+ if (cell.numFmtCode && cell.numFmtCode !== "General" && cell.numFmtCode !== "") {
43490
+ if (!numFmtMap.has(cell.numFmtCode)) {
43491
+ numFmtMap.set(cell.numFmtCode, nextNumFmtId++);
43492
+ }
43493
+ }
43494
+ }
42816
43495
  }
42817
- const axIds = needsAxIds ? '<c:axId val="1"/><c:axId val="2"/>' : "";
42818
- return `<${chartTag}>${barDir}${grouping}${seriesXml}${axIds}</${chartTag}>`;
42819
- }
42820
- function buildChartXml(chart) {
42821
- const needsAxIds = chart.chartType !== "pie" && chart.chartType !== "doughnut";
42822
- let plotArea;
42823
- if (chart.chartType === "combo") {
42824
- const groups = /* @__PURE__ */ new Map();
42825
- chart.series.forEach((s, idx) => {
42826
- const sType = s.seriesChartType ?? "col";
42827
- let group = groups.get(sType);
42828
- if (!group) {
42829
- group = [];
42830
- groups.set(sType, group);
43496
+ const cellNumFmtIds = /* @__PURE__ */ new Map();
43497
+ for (let si = 0; si < workbook.sheets.length; si++) {
43498
+ for (const [ref, cell] of workbook.sheets[si].cells) {
43499
+ if (cell.numFmtCode && cell.numFmtCode !== "General" && cell.numFmtCode !== "") {
43500
+ const id = numFmtMap.get(cell.numFmtCode);
43501
+ if (id !== void 0) cellNumFmtIds.set(`${si}:${ref}`, id);
42831
43502
  }
42832
- group.push({ series: s, idx });
42833
- });
42834
- let chartElements = "";
42835
- for (const [groupType, groupSeries] of groups) {
42836
- const groupSeriesXml = groupSeries.map(
42837
- ({ series, idx }) => buildSeriesXml(series, idx, groupType, chart.categories)
42838
- ).join("");
42839
- chartElements += buildChartTypeElement(groupType, groupSeriesXml, true);
42840
43503
  }
42841
- plotArea = `<c:plotArea><c:layout/>${chartElements}`;
42842
- } else {
42843
- const seriesXml = chart.series.map(
42844
- (s, idx) => buildSeriesXml(s, idx, chart.chartType, chart.categories)
42845
- ).join("");
42846
- plotArea = `<c:plotArea><c:layout/>${buildChartTypeElement(chart.chartType, seriesXml, needsAxIds)}`;
42847
43504
  }
42848
- if (chart.chartType !== "pie" && chart.chartType !== "doughnut") {
42849
- const axes = chart.axes ?? [{ type: "category" }, { type: "value" }];
42850
- for (let i2 = 0; i2 < axes.length; i2++) {
42851
- const ax = axes[i2];
42852
- const axId = i2 + 1;
42853
- const crossId = i2 === 0 ? 2 : 1;
42854
- let axTag;
42855
- switch (ax.type) {
42856
- case "value":
42857
- axTag = "c:valAx";
42858
- break;
42859
- case "date":
42860
- axTag = "c:dateAx";
42861
- break;
42862
- case "series":
42863
- axTag = "c:serAx";
42864
- break;
42865
- default:
42866
- axTag = "c:catAx";
42867
- break;
43505
+ const fonts = /* @__PURE__ */ new Map();
43506
+ const fontList = [];
43507
+ fonts.set("default", 0);
43508
+ fontList.push({});
43509
+ for (const s of styles) {
43510
+ const key = fontKey(s);
43511
+ if (!fonts.has(key)) {
43512
+ fonts.set(key, fontList.length);
43513
+ fontList.push(s);
43514
+ }
43515
+ }
43516
+ const fillEntries = [];
43517
+ const fillMap = /* @__PURE__ */ new Map();
43518
+ fillEntries.push('<fill><patternFill patternType="none"/></fill>');
43519
+ fillEntries.push('<fill><patternFill patternType="gray125"/></fill>');
43520
+ fillMap.set("", 0);
43521
+ for (const s of styles) {
43522
+ const fk = fillKey(s);
43523
+ if (fk === "" || fillMap.has(fk)) continue;
43524
+ fillMap.set(fk, fillEntries.length);
43525
+ fillEntries.push(buildFillXml(s));
43526
+ }
43527
+ const borderEntries = [];
43528
+ const borderMap = /* @__PURE__ */ new Map();
43529
+ borderEntries.push("<border><left/><right/><top/><bottom/><diagonal/></border>");
43530
+ borderMap.set("", 0);
43531
+ for (const s of styles) {
43532
+ const bk = borderKey(s);
43533
+ if (bk === "" || borderMap.has(bk)) continue;
43534
+ borderMap.set(bk, borderEntries.length);
43535
+ borderEntries.push(buildBorderXml(s));
43536
+ }
43537
+ const fontsXml = fontList.map((f) => buildFontXml(f)).join("\n");
43538
+ const fillsXml = fillEntries.join("\n");
43539
+ const bordersXml = borderEntries.join("\n");
43540
+ let numFmtsXml = "";
43541
+ if (numFmtMap.size > 0) {
43542
+ const entries = Array.from(numFmtMap.entries()).map(([code, id]) => `<numFmt numFmtId="${id}" formatCode="${escapeXml(code)}"/>`).join("\n");
43543
+ numFmtsXml = `<numFmts count="${numFmtMap.size}">
43544
+ ${entries}
43545
+ </numFmts>
43546
+ `;
43547
+ }
43548
+ const xfEntries = [];
43549
+ const xfMap = /* @__PURE__ */ new Map();
43550
+ for (let styleIdx = 0; styleIdx < styles.length; styleIdx++) {
43551
+ const s = styles[styleIdx];
43552
+ const fontId = fonts.get(fontKey(s)) ?? 0;
43553
+ const fillId = fillMap.get(fillKey(s)) ?? 0;
43554
+ const borderId = borderMap.get(borderKey(s)) ?? 0;
43555
+ const xfKey = `${styleIdx}:0`;
43556
+ xfMap.set(xfKey, xfEntries.length);
43557
+ xfEntries.push(buildXfXml(s, fontId, fillId, borderId, 0));
43558
+ }
43559
+ for (const [cellKey2, numFmtId] of cellNumFmtIds) {
43560
+ const [siStr, ref] = cellKey2.split(":");
43561
+ const cell = workbook.sheets[parseInt(siStr)].cells.get(ref);
43562
+ if (!cell) continue;
43563
+ const xfKey = `${cell.styleIndex}:${numFmtId}`;
43564
+ if (xfMap.has(xfKey)) continue;
43565
+ const s = styles[cell.styleIndex] ?? {};
43566
+ const fontId = fonts.get(fontKey(s)) ?? 0;
43567
+ const fillId = fillMap.get(fillKey(s)) ?? 0;
43568
+ const borderId = borderMap.get(borderKey(s)) ?? 0;
43569
+ xfMap.set(xfKey, xfEntries.length);
43570
+ xfEntries.push(buildXfXml(s, fontId, fillId, borderId, numFmtId));
43571
+ }
43572
+ const dxfEntries = [];
43573
+ const dxfMap = /* @__PURE__ */ new Map();
43574
+ for (const sheet of workbook.sheets) {
43575
+ for (const cf of sheet.conditionalFormats) {
43576
+ for (const rule of cf.rules) {
43577
+ if (rule.ruleType === "style" && rule.style) {
43578
+ const key = JSON.stringify(rule.style);
43579
+ if (!dxfMap.has(key)) {
43580
+ dxfMap.set(key, dxfEntries.length);
43581
+ dxfEntries.push(buildDxfXml(rule.style));
43582
+ }
43583
+ }
42868
43584
  }
42869
- let titleXml2 = "";
42870
- if (ax.title) titleXml2 = `<c:title><c:tx><c:rich><a:bodyPr/><a:lstStyle/><a:p><a:r><a:t>${escapeXml(ax.title)}</a:t></a:r></a:p></c:rich></c:tx></c:title>`;
42871
- let scalingXml = '<c:scaling><c:orientation val="minMax"/>';
42872
- if (ax.min !== void 0) scalingXml += `<c:min val="${ax.min}"/>`;
42873
- if (ax.max !== void 0) scalingXml += `<c:max val="${ax.max}"/>`;
42874
- scalingXml += "</c:scaling>";
42875
- const numFmt = ax.numFmt ? `<c:numFmt formatCode="${escapeXml(ax.numFmt)}" sourceLinked="0"/>` : "";
42876
- plotArea += `<${axTag}><c:axId val="${axId}"/>${scalingXml}${titleXml2}${numFmt}<c:crossAx val="${crossId}"/></${axTag}>`;
42877
43585
  }
42878
43586
  }
42879
- plotArea += "</c:plotArea>";
42880
- let titleXml = "";
42881
- if (chart.title) {
42882
- titleXml = `<c:title><c:tx><c:rich><a:bodyPr/><a:lstStyle/><a:p><a:r><a:t>${escapeXml(chart.title)}</a:t></a:r></a:p></c:rich></c:tx></c:title>`;
43587
+ let dxfsXml = '<dxfs count="0"/>';
43588
+ if (dxfEntries.length > 0) {
43589
+ dxfsXml = `<dxfs count="${dxfEntries.length}">
43590
+ ${dxfEntries.join("\n")}
43591
+ </dxfs>`;
42883
43592
  }
42884
- let legendXml = "";
42885
- if (chart.legendPosition && chart.legendPosition !== "none") {
42886
- const posMap = { top: "t", bottom: "b", left: "l", right: "r" };
42887
- legendXml = `<c:legend><c:legendPos val="${posMap[chart.legendPosition] ?? "b"}"/></c:legend>`;
43593
+ const xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
43594
+ <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
43595
+ ${numFmtsXml}<fonts count="${fontList.length}">
43596
+ ${fontsXml}
43597
+ </fonts>
43598
+ <fills count="${fillEntries.length}">
43599
+ ${fillsXml}
43600
+ </fills>
43601
+ <borders count="${borderEntries.length}">
43602
+ ${bordersXml}
43603
+ </borders>
43604
+ <cellStyleXfs count="1">
43605
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="0"/>
43606
+ </cellStyleXfs>
43607
+ <cellXfs count="${xfEntries.length}">
43608
+ ${xfEntries.join("\n")}
43609
+ </cellXfs>
43610
+ <cellStyles count="1">
43611
+ <cellStyle name="Normal" xfId="0" builtinId="0"/>
43612
+ </cellStyles>
43613
+ ${dxfsXml}
43614
+ </styleSheet>`;
43615
+ return { xml, xfMap, numFmtMap, dxfMap };
43616
+ }
43617
+ function buildXfXml(s, fontId, fillId, borderId, numFmtId) {
43618
+ let attrs = `numFmtId="${numFmtId}" fontId="${fontId}" fillId="${fillId}" borderId="${borderId}" xfId="0"`;
43619
+ if (numFmtId > 0) attrs += ' applyNumberFormat="1"';
43620
+ if (fontId > 0) attrs += ' applyFont="1"';
43621
+ if (fillId > 0) attrs += ' applyFill="1"';
43622
+ if (borderId > 0) attrs += ' applyBorder="1"';
43623
+ if (s.horizontalAlign || s.verticalAlign || s.wrapText || s.indent || s.textRotation || s.shrinkToFit) {
43624
+ const hAlign = s.horizontalAlign ? ` horizontal="${s.horizontalAlign}"` : "";
43625
+ const vAlign = s.verticalAlign ? ` vertical="${s.verticalAlign}"` : "";
43626
+ const wrap = s.wrapText ? ' wrapText="1"' : "";
43627
+ const indent = s.indent ? ` indent="${s.indent}"` : "";
43628
+ const rotation = s.textRotation !== void 0 ? ` textRotation="${s.textRotation === "vertical" ? 255 : s.textRotation}"` : "";
43629
+ const shrink = s.shrinkToFit ? ' shrinkToFit="1"' : "";
43630
+ return `<xf ${attrs} applyAlignment="1"><alignment${hAlign}${vAlign}${wrap}${indent}${rotation}${shrink}/></xf>`;
42888
43631
  }
42889
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
42890
- <c:chartSpace xmlns:c="http://schemas.openxmlformats.org/drawingml/2006/chart" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
42891
- <c:chart>${titleXml}${plotArea}${legendXml}</c:chart>
42892
- </c:chartSpace>`;
43632
+ return `<xf ${attrs}/>`;
42893
43633
  }
42894
- function buildAnchorPosition(pos) {
42895
- return `<xdr:col>${pos.col}</xdr:col><xdr:colOff>${pos.colOffset ?? 0}</xdr:colOff><xdr:row>${pos.row}</xdr:row><xdr:rowOff>${pos.rowOffset ?? 0}</xdr:rowOff>`;
43634
+ function fontKey(s) {
43635
+ return `${s.fontName ?? ""}|${s.fontSize ?? 0}|${s.fontBold ? 1 : 0}|${s.fontItalic ? 1 : 0}|${s.fontColor ?? ""}|${s.fontUnderline ?? ""}|${s.fontStrike ? 1 : 0}`;
42896
43636
  }
42897
- function buildChartAnchorXml(chart, rId) {
42898
- return `<xdr:twoCellAnchor>
42899
- <xdr:from>${buildAnchorPosition(chart.anchor.from)}</xdr:from>
42900
- <xdr:to>${buildAnchorPosition(chart.anchor.to)}</xdr:to>
42901
- <xdr:graphicFrame macro="">
42902
- <xdr:nvGraphicFramePr><xdr:cNvPr id="0" name="Chart"/><xdr:cNvGraphicFramePr/></xdr:nvGraphicFramePr>
42903
- <xdr:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/></xdr:xfrm>
42904
- <a:graphic><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/chart"><c:chart xmlns:c="http://schemas.openxmlformats.org/drawingml/2006/chart" r:id="${rId}"/></a:graphicData></a:graphic>
42905
- </xdr:graphicFrame>
42906
- <xdr:clientData/>
42907
- </xdr:twoCellAnchor>`;
43637
+ function buildFontXml(s) {
43638
+ let parts = "";
43639
+ if (s.fontBold) parts += "<b/>";
43640
+ if (s.fontItalic) parts += "<i/>";
43641
+ if (s.fontStrike) parts += "<strike/>";
43642
+ if (s.fontUnderline) parts += `<u val="${s.fontUnderline}"/>`;
43643
+ parts += `<sz val="${s.fontSize ?? 11}"/>`;
43644
+ if (s.fontColor) {
43645
+ parts += `<color rgb="${hexToArgb(s.fontColor)}"/>`;
43646
+ } else {
43647
+ parts += '<color theme="1"/>';
43648
+ }
43649
+ parts += `<name val="${escapeXml(s.fontName ?? "Calibri")}"/>`;
43650
+ return `<font>${parts}</font>`;
42908
43651
  }
42909
- function buildImageAnchorXml(image, rId) {
42910
- return `<xdr:twoCellAnchor editAs="oneCell">
42911
- <xdr:from>${buildAnchorPosition(image.tl)}</xdr:from>
42912
- <xdr:to>${buildAnchorPosition(image.br)}</xdr:to>
42913
- <xdr:pic>
42914
- <xdr:nvPicPr><xdr:cNvPr id="0" name="Image"/><xdr:cNvPicPr/></xdr:nvPicPr>
42915
- <xdr:blipFill><a:blip xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:embed="${rId}"/><a:stretch><a:fillRect/></a:stretch></xdr:blipFill>
42916
- <xdr:spPr><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></xdr:spPr>
42917
- </xdr:pic>
42918
- <xdr:clientData/>
42919
- </xdr:twoCellAnchor>`;
43652
+ function borderKey(s) {
43653
+ const parts = [];
43654
+ if (s.borderTop) parts.push(`t:${s.borderTop.style}:${s.borderTop.width}:${s.borderTop.color ?? ""}`);
43655
+ if (s.borderRight) parts.push(`r:${s.borderRight.style}:${s.borderRight.width}:${s.borderRight.color ?? ""}`);
43656
+ if (s.borderBottom) parts.push(`b:${s.borderBottom.style}:${s.borderBottom.width}:${s.borderBottom.color ?? ""}`);
43657
+ if (s.borderLeft) parts.push(`l:${s.borderLeft.style}:${s.borderLeft.width}:${s.borderLeft.color ?? ""}`);
43658
+ if (s.borderDiagonal) parts.push(`d:${s.borderDiagonal.style}:${s.borderDiagonal.width}:${s.borderDiagonal.color ?? ""}`);
43659
+ if (s.diagonalUp) parts.push("du");
43660
+ if (s.diagonalDown) parts.push("dd");
43661
+ return parts.join("|");
42920
43662
  }
42921
- function buildShapeAnchorXml(drawing) {
42922
- let fillXml = "";
42923
- if (drawing.fillColor) fillXml = `<a:solidFill><a:srgbClr val="${hexToOoxmlRgb(drawing.fillColor)}"/></a:solidFill>`;
42924
- let outlineXml = "";
42925
- if (drawing.outlineColor) {
42926
- const w = Math.round((drawing.outlineWidth ?? 1) * 12700);
42927
- outlineXml = `<a:ln w="${w}"><a:solidFill><a:srgbClr val="${hexToOoxmlRgb(drawing.outlineColor)}"/></a:solidFill></a:ln>`;
43663
+ function toOoxmlBorderStyle(border2) {
43664
+ const ooxmlStyles = [
43665
+ "thin",
43666
+ "medium",
43667
+ "thick",
43668
+ "dotted",
43669
+ "dashed",
43670
+ "double",
43671
+ "hair",
43672
+ "mediumDashed",
43673
+ "dashDot",
43674
+ "mediumDashDot",
43675
+ "dashDotDot",
43676
+ "mediumDashDotDot",
43677
+ "slantDashDot"
43678
+ ];
43679
+ if (ooxmlStyles.includes(border2.style)) return border2.style;
43680
+ if (border2.style === "solid") {
43681
+ if (border2.width <= 1) return "thin";
43682
+ if (border2.width <= 2) return "medium";
43683
+ return "thick";
42928
43684
  }
42929
- const geom = drawing.geometry ?? "rect";
42930
- let textXml = "";
42931
- if (drawing.text) textXml = `<xdr:txBody><a:bodyPr/><a:lstStyle/><a:p><a:r><a:t>${escapeXml(drawing.text)}</a:t></a:r></a:p></xdr:txBody>`;
42932
- return `<xdr:twoCellAnchor>
42933
- <xdr:from>${buildAnchorPosition(drawing.anchor.from)}</xdr:from>
42934
- <xdr:to>${buildAnchorPosition(drawing.anchor.to)}</xdr:to>
42935
- <xdr:sp><xdr:nvSpPr><xdr:cNvPr id="0" name="Shape"/><xdr:cNvSpPr/></xdr:nvSpPr>
42936
- <xdr:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/></a:xfrm><a:prstGeom prst="${geom}"><a:avLst/></a:prstGeom>${fillXml}${outlineXml}</xdr:spPr>
42937
- ${textXml}</xdr:sp>
42938
- <xdr:clientData/>
42939
- </xdr:twoCellAnchor>`;
43685
+ if (border2.style === "dashed") return "dashed";
43686
+ if (border2.style === "dotted") return "dotted";
43687
+ if (border2.style === "double") return "double";
43688
+ return "thin";
42940
43689
  }
42941
- function buildTableXml(table, tableId) {
42942
- const colsXml = table.columns.map((col) => {
42943
- let inner = "";
42944
- if (col.totalsFunction) inner += `<totalsRowFunction>${escapeXml(col.totalsFunction)}</totalsRowFunction>`;
42945
- if (col.totalsFormula) inner += `<totalsRowFormula>${escapeXml(col.totalsFormula)}</totalsRowFormula>`;
42946
- return `<tableColumn id="${col.id}" name="${escapeXml(col.name)}">${inner}</tableColumn>`;
43690
+ function buildBorderXml(s) {
43691
+ let attrs = "";
43692
+ if (s.diagonalUp) attrs += ' diagonalUp="1"';
43693
+ if (s.diagonalDown) attrs += ' diagonalDown="1"';
43694
+ const sides = [
43695
+ { tag: "left", border: s.borderLeft },
43696
+ { tag: "right", border: s.borderRight },
43697
+ { tag: "top", border: s.borderTop },
43698
+ { tag: "bottom", border: s.borderBottom },
43699
+ { tag: "diagonal", border: s.borderDiagonal }
43700
+ ];
43701
+ const inner = sides.map(({ tag, border: border2 }) => {
43702
+ if (!border2) return `<${tag}/>`;
43703
+ const ooxmlStyle = toOoxmlBorderStyle(border2);
43704
+ let colorXml = "";
43705
+ if (border2.color) {
43706
+ colorXml = `<color rgb="${hexToArgb(border2.color)}"/>`;
43707
+ }
43708
+ return `<${tag} style="${ooxmlStyle}">${colorXml}</${tag}>`;
42947
43709
  }).join("");
42948
- const autoFilterXml = table.autoFilter ? `<autoFilter ref="${escapeXml(table.ref)}"/>` : "";
42949
- const styleName = table.styleName ?? "TableStyleMedium2";
42950
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
42951
- <table xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" id="${tableId}" name="${escapeXml(table.name)}" displayName="${escapeXml(table.displayName)}" ref="${escapeXml(table.ref)}" totalsRowCount="${table.totalsRow ? 1 : 0}">
42952
- ${autoFilterXml}
42953
- <tableColumns count="${table.columns.length}">${colsXml}</tableColumns>
42954
- <tableStyleInfo name="${escapeXml(styleName)}" showFirstColumn="0" showLastColumn="0" showRowStripes="1" showColumnStripes="0"/>
42955
- </table>`;
43710
+ return `<border${attrs}>${inner}</border>`;
42956
43711
  }
42957
- function buildConditionalFormattingXml(cf, dxfMap) {
42958
- const rules = cf.rules.map((rule) => {
42959
- switch (rule.ruleType) {
42960
- case "colorScale": {
42961
- const count = rule.colors.length;
42962
- const cfvos = count === 2 ? '<cfvo type="min"/><cfvo type="max"/>' : '<cfvo type="min"/><cfvo type="percentile" val="50"/><cfvo type="max"/>';
42963
- const colors = rule.colors.map((c) => `<color rgb="${hexToArgb(c)}"/>`).join("");
42964
- return `<cfRule type="colorScale" priority="${rule.priority}"><colorScale>${cfvos}${colors}</colorScale></cfRule>`;
42965
- }
42966
- case "dataBar": {
42967
- const showVal = rule.showValue ? "1" : "0";
42968
- return `<cfRule type="dataBar" priority="${rule.priority}"><dataBar minLength="${rule.minLength}" maxLength="${rule.maxLength}" showValue="${showVal}"><cfvo type="min"/><cfvo type="max"/><color rgb="${hexToArgb(rule.color)}"/></dataBar></cfRule>`;
42969
- }
42970
- case "iconSet": {
42971
- const thresholds = rule.thresholds.map((t) => {
42972
- if (t.type === "min" || t.type === "autoMin") return '<cfvo type="min"/>';
42973
- if (t.type === "max" || t.type === "autoMax") return '<cfvo type="max"/>';
42974
- return `<cfvo type="${t.type}" val="${t.value ?? 0}"/>`;
42975
- }).join("");
42976
- const showVal = rule.showValue ? "" : ' showValue="0"';
42977
- const reverse = rule.reverse ? ' reverse="1"' : "";
42978
- return `<cfRule type="iconSet" priority="${rule.priority}"><iconSet iconSet="${rule.iconSet}"${showVal}${reverse}>${thresholds}</iconSet></cfRule>`;
43712
+ function fillKey(s) {
43713
+ if (s.gradientData) return `gradient:${JSON.stringify(s.gradientData)}`;
43714
+ if (s.patternType && s.backgroundPattern) return `pattern:${s.patternType}|${s.backgroundPattern}`;
43715
+ if (s.backgroundColor) return `solid:${s.backgroundColor}`;
43716
+ return "";
43717
+ }
43718
+ function buildFillXml(s) {
43719
+ if (s.gradientData) {
43720
+ const g = s.gradientData;
43721
+ const stops = g.stops.map((stop) => {
43722
+ const hex = hexToArgb(stop.color);
43723
+ return `<stop position="${stop.position}"><color rgb="${hex}"/></stop>`;
43724
+ }).join("");
43725
+ if (g.type === "radial") {
43726
+ return `<fill><gradientFill type="path" left="0.5" right="0.5" top="0.5" bottom="0.5">${stops}</gradientFill></fill>`;
43727
+ }
43728
+ return `<fill><gradientFill degree="${g.degree}">${stops}</gradientFill></fill>`;
43729
+ }
43730
+ if (s.patternType && s.backgroundPattern) {
43731
+ const colors = extractFillColors(s.backgroundPattern);
43732
+ let colorAttrs = "";
43733
+ if (colors.fg) colorAttrs += `<fgColor rgb="${hexToArgb(colors.fg)}"/>`;
43734
+ if (colors.bg) colorAttrs += `<bgColor rgb="${hexToArgb(colors.bg)}"/>`;
43735
+ return `<fill><patternFill patternType="${escapeXml(s.patternType)}">${colorAttrs}</patternFill></fill>`;
43736
+ }
43737
+ return `<fill><patternFill patternType="solid"><fgColor rgb="${hexToArgb(s.backgroundColor)}"/></patternFill></fill>`;
43738
+ }
43739
+ function extractFillColors(css) {
43740
+ const colorMatches = css.match(/rgba?\([^)]+\)/g);
43741
+ if (colorMatches) {
43742
+ return { fg: colorMatches[0], bg: colorMatches[1] };
43743
+ }
43744
+ const hexMatches = css.match(/#[0-9a-fA-F]{6}/g);
43745
+ if (hexMatches) {
43746
+ return { fg: hexMatches[0], bg: hexMatches[1] };
43747
+ }
43748
+ return { fg: void 0, bg: void 0 };
43749
+ }
43750
+ function buildSheetXml(sheet, styles, ssIndex, isActive, xfMap, numFmtMap, dxfMap, drawingRId, tableRIds, hyperlinkRIds) {
43751
+ const rows = [];
43752
+ const rowMap = /* @__PURE__ */ new Map();
43753
+ for (const [ref, cell] of sheet.cells) {
43754
+ const rc = refToRowCol(ref);
43755
+ if (!rc) continue;
43756
+ let arr = rowMap.get(rc.row);
43757
+ if (!arr) {
43758
+ arr = [];
43759
+ rowMap.set(rc.row, arr);
43760
+ }
43761
+ arr.push({ col: rc.col, cell });
43762
+ }
43763
+ const sortedRows = Array.from(rowMap.keys()).sort((a, b) => a - b);
43764
+ for (const rowNum of sortedRows) {
43765
+ const cells = rowMap.get(rowNum);
43766
+ cells.sort((a, b) => a.col - b.col);
43767
+ const h = sheet.rowHeights.get(rowNum);
43768
+ const rowAttrs = h ? ` ht="${h}" customHeight="1"` : "";
43769
+ const hidden = sheet.hiddenRows.has(rowNum) ? ' hidden="1"' : "";
43770
+ const cellsXml = cells.map(({ col, cell }) => {
43771
+ const ref = rowColToRef(rowNum, col);
43772
+ const type = getCellType(cell, ssIndex);
43773
+ const value = getCellValue(cell, ssIndex);
43774
+ let attrs = `r="${ref}"`;
43775
+ let xfIndex = 0;
43776
+ if (xfMap.size === 0 && cell.originalXfIndex !== void 0) {
43777
+ xfIndex = cell.originalXfIndex;
43778
+ } else {
43779
+ const numFmtId = cell.numFmtCode && cell.numFmtCode !== "General" && cell.numFmtCode !== "" ? numFmtMap.get(cell.numFmtCode) ?? 0 : 0;
43780
+ const xfKey = `${cell.styleIndex}:${numFmtId}`;
43781
+ xfIndex = xfMap.get(xfKey) ?? 0;
42979
43782
  }
42980
- case "style": {
42981
- let attrs = `type="${rule.type}" priority="${rule.priority}"`;
42982
- if (rule.style) {
42983
- const dxfId = dxfMap.get(JSON.stringify(rule.style));
42984
- if (dxfId !== void 0) attrs += ` dxfId="${dxfId}"`;
42985
- }
42986
- if (rule.operator) attrs += ` operator="${rule.operator}"`;
42987
- if (rule.text) attrs += ` text="${escapeXml(rule.text)}"`;
42988
- if (rule.rank !== void 0) attrs += ` rank="${rule.rank}"`;
42989
- if (rule.percent) attrs += ' percent="1"';
42990
- if (rule.bottom) attrs += ' bottom="1"';
42991
- if (rule.aboveAverage === false) attrs += ' aboveAverage="0"';
42992
- if (rule.timePeriod) attrs += ` timePeriod="${rule.timePeriod}"`;
42993
- let inner = "";
42994
- if (rule.formulae) {
42995
- inner = rule.formulae.map((f) => `<formula>${escapeXml(String(f))}</formula>`).join("");
43783
+ if (xfIndex > 0) attrs += ` s="${xfIndex}"`;
43784
+ if (type) attrs += ` t="${type}"`;
43785
+ let inner = "";
43786
+ if (cell.formula) {
43787
+ if (cell.isArrayFormula && cell.arrayRange) {
43788
+ inner += `<f t="array" ref="${cell.arrayRange}">${escapeXml(cell.formula)}</f>`;
43789
+ } else {
43790
+ inner += `<f>${escapeXml(cell.formula)}</f>`;
42996
43791
  }
42997
- return `<cfRule ${attrs}>${inner}</cfRule>`;
42998
43792
  }
43793
+ if (value !== void 0) inner += `<v>${escapeXml(String(value))}</v>`;
43794
+ return `<c ${attrs}>${inner}</c>`;
43795
+ }).join("");
43796
+ rows.push(`<row r="${rowNum}"${rowAttrs}${hidden}>${cellsXml}</row>`);
43797
+ }
43798
+ const cols = [];
43799
+ const allCols = /* @__PURE__ */ new Set([...sheet.colWidths.keys(), ...sheet.hiddenCols]);
43800
+ for (const c of Array.from(allCols).sort((a, b) => a - b)) {
43801
+ const w = sheet.colWidths.get(c) ?? sheet.defaultColWidth;
43802
+ const hidden = sheet.hiddenCols.has(c) ? ' hidden="1"' : "";
43803
+ cols.push(`<col min="${c}" max="${c}" width="${w}" customWidth="1"${hidden}/>`);
43804
+ }
43805
+ let mergeXml = "";
43806
+ if (sheet.mergedCells.length > 0) {
43807
+ const merges = sheet.mergedCells.map((r) => `<mergeCell ref="${r}"/>`).join("");
43808
+ mergeXml = `<mergeCells count="${sheet.mergedCells.length}">${merges}</mergeCells>`;
43809
+ }
43810
+ let paneXml = "";
43811
+ let selectionXml = `<selection activeCell="A1" sqref="A1"/>`;
43812
+ if (sheet.freeze) {
43813
+ const activePane = sheet.freeze.col > 0 && sheet.freeze.row > 0 ? "bottomRight" : sheet.freeze.row > 0 ? "bottomLeft" : "topRight";
43814
+ const topLeft = rowColToRef(sheet.freeze.row + 1, sheet.freeze.col + 1);
43815
+ paneXml = `<pane xSplit="${sheet.freeze.col}" ySplit="${sheet.freeze.row}" topLeftCell="${topLeft}" activePane="${activePane}" state="frozen"/>`;
43816
+ selectionXml = `<selection pane="${activePane}" activeCell="${topLeft}" sqref="${topLeft}"/>`;
43817
+ }
43818
+ const tabSelected = isActive ? ' tabSelected="1"' : "";
43819
+ const sheetView = `<sheetView${tabSelected} workbookViewId="0"${!sheet.view.showGridLines ? ' showGridLines="0"' : ""}>${paneXml}${selectionXml}</sheetView>`;
43820
+ let autoFilterXml = "";
43821
+ if (sheet.autoFilter) {
43822
+ let filterCols = "";
43823
+ for (const col of sheet.autoFilter.columns) {
43824
+ if (col.filterValues && col.filterValues.length > 0) {
43825
+ const filters = col.filterValues.map((v) => `<filter val="${escapeXml(v)}"/>`).join("");
43826
+ filterCols += `<filterColumn colId="${col.colIndex}"><filters>${filters}</filters></filterColumn>`;
43827
+ }
43828
+ }
43829
+ autoFilterXml = `<autoFilter ref="${sheet.autoFilter.ref}">${filterCols}</autoFilter>`;
43830
+ }
43831
+ let dvXml = "";
43832
+ if (sheet.dataValidations.length > 0) {
43833
+ const dvEntries = sheet.dataValidations.map((dv) => {
43834
+ let attrs = `sqref="${dv.ref}" type="${dv.type}"`;
43835
+ if (dv.operator) attrs += ` operator="${dv.operator}"`;
43836
+ if (!dv.showDropdown) attrs += ' showDropDown="1"';
43837
+ if (dv.errorStyle) attrs += ` errorStyle="${dv.errorStyle}"`;
43838
+ if (dv.errorTitle) attrs += ` errorTitle="${escapeXml(dv.errorTitle)}"`;
43839
+ if (dv.errorMessage) attrs += ` error="${escapeXml(dv.errorMessage)}"`;
43840
+ if (dv.promptTitle) attrs += ` promptTitle="${escapeXml(dv.promptTitle)}"`;
43841
+ if (dv.promptMessage) attrs += ` prompt="${escapeXml(dv.promptMessage)}"`;
43842
+ let inner = "";
43843
+ if (dv.formula1) inner += `<formula1>${escapeXml(dv.formula1)}</formula1>`;
43844
+ if (dv.formula2) inner += `<formula2>${escapeXml(dv.formula2)}</formula2>`;
43845
+ return `<dataValidation ${attrs}>${inner}</dataValidation>`;
43846
+ }).join("");
43847
+ dvXml = `<dataValidations count="${sheet.dataValidations.length}">${dvEntries}</dataValidations>`;
43848
+ }
43849
+ let cfXml = "";
43850
+ if (sheet.conditionalFormats.length > 0) {
43851
+ cfXml = sheet.conditionalFormats.map((cf) => buildConditionalFormattingXml(cf, dxfMap)).join("");
43852
+ }
43853
+ let hyperlinksXml = "";
43854
+ if (hyperlinkRIds && hyperlinkRIds.size > 0) {
43855
+ const hlEntries = Array.from(hyperlinkRIds.entries()).map(([ref, rId]) => `<hyperlink ref="${ref}" r:id="${rId}"/>`).join("");
43856
+ hyperlinksXml = `<hyperlinks>${hlEntries}</hyperlinks>`;
43857
+ }
43858
+ const drawingXml = drawingRId ? `<drawing r:id="${drawingRId}"/>` : "";
43859
+ let tablePartsXml = "";
43860
+ if (tableRIds && tableRIds.length > 0) {
43861
+ const parts = tableRIds.map((rId) => `<tablePart r:id="${rId}"/>`).join("");
43862
+ tablePartsXml = `<tableParts count="${tableRIds.length}">${parts}</tableParts>`;
43863
+ }
43864
+ const ps = sheet.pageSetup;
43865
+ const hf = sheet.headerFooter;
43866
+ const fitToPage = ps && (ps.fitToWidth !== void 0 || ps.fitToHeight !== void 0);
43867
+ const sheetPrXml = fitToPage ? `<sheetPr><pageSetUpPr fitToPage="1"/></sheetPr>` : "";
43868
+ const marginsXml = (() => {
43869
+ const m = ps?.margins;
43870
+ const left = m?.left ?? 0.7;
43871
+ const right = m?.right ?? 0.7;
43872
+ const top = m?.top ?? 0.75;
43873
+ const bottom = m?.bottom ?? 0.75;
43874
+ const header = m?.header ?? 0.3;
43875
+ const footer = m?.footer ?? 0.3;
43876
+ return `<pageMargins left="${left}" right="${right}" top="${top}" bottom="${bottom}" header="${header}" footer="${footer}"/>`;
43877
+ })();
43878
+ let pageSetupXml = "";
43879
+ if (ps) {
43880
+ const attrs = [];
43881
+ if (ps.paperSize !== void 0) attrs.push(`paperSize="${ps.paperSize}"`);
43882
+ if (ps.scale !== void 0) attrs.push(`scale="${ps.scale}"`);
43883
+ if (ps.fitToWidth !== void 0) attrs.push(`fitToWidth="${ps.fitToWidth}"`);
43884
+ if (ps.fitToHeight !== void 0) attrs.push(`fitToHeight="${ps.fitToHeight}"`);
43885
+ if (ps.orientation) attrs.push(`orientation="${ps.orientation}"`);
43886
+ if (attrs.length > 0) pageSetupXml = `<pageSetup ${attrs.join(" ")}/>`;
43887
+ }
43888
+ let headerFooterXml = "";
43889
+ if (hf) {
43890
+ const rootAttrs = [];
43891
+ if (hf.differentOddEven) rootAttrs.push(`differentOddEven="1"`);
43892
+ if (hf.differentFirst) rootAttrs.push(`differentFirst="1"`);
43893
+ const inner = [];
43894
+ if (hf.oddHeader) inner.push(`<oddHeader>${escapeXml(hf.oddHeader)}</oddHeader>`);
43895
+ if (hf.oddFooter) inner.push(`<oddFooter>${escapeXml(hf.oddFooter)}</oddFooter>`);
43896
+ if (hf.evenHeader) inner.push(`<evenHeader>${escapeXml(hf.evenHeader)}</evenHeader>`);
43897
+ if (hf.evenFooter) inner.push(`<evenFooter>${escapeXml(hf.evenFooter)}</evenFooter>`);
43898
+ if (hf.firstHeader) inner.push(`<firstHeader>${escapeXml(hf.firstHeader)}</firstHeader>`);
43899
+ if (hf.firstFooter) inner.push(`<firstFooter>${escapeXml(hf.firstFooter)}</firstFooter>`);
43900
+ if (inner.length > 0) {
43901
+ const attrStr = rootAttrs.length > 0 ? ` ${rootAttrs.join(" ")}` : "";
43902
+ headerFooterXml = `<headerFooter${attrStr}>${inner.join("")}</headerFooter>`;
42999
43903
  }
43000
- }).join("");
43001
- return `<conditionalFormatting sqref="${cf.ref}">${rules}</conditionalFormatting>`;
43002
- }
43003
- function buildDxfXml(style) {
43004
- let inner = "";
43005
- if (style.fontBold || style.fontItalic || style.fontColor) {
43006
- let fontParts = "";
43007
- if (style.fontBold) fontParts += "<b/>";
43008
- if (style.fontItalic) fontParts += "<i/>";
43009
- if (style.fontColor) fontParts += `<color rgb="${hexToArgb(style.fontColor)}"/>`;
43010
- inner += `<font>${fontParts}</font>`;
43011
43904
  }
43012
- if (style.backgroundColor) {
43013
- inner += `<fill><patternFill><bgColor rgb="${hexToArgb(style.backgroundColor)}"/></patternFill></fill>`;
43905
+ let dimensionRef = "A1";
43906
+ if (sortedRows.length > 0) {
43907
+ let minCol = Infinity, maxCol = 0;
43908
+ const minRow = sortedRows[0];
43909
+ const maxRow = sortedRows[sortedRows.length - 1];
43910
+ for (const rowNum of sortedRows) {
43911
+ const cells = rowMap.get(rowNum);
43912
+ for (const { col } of cells) {
43913
+ if (col < minCol) minCol = col;
43914
+ if (col > maxCol) maxCol = col;
43915
+ }
43916
+ }
43917
+ dimensionRef = `${rowColToRef(minRow, minCol)}:${rowColToRef(maxRow, maxCol)}`;
43014
43918
  }
43015
- return `<dxf>${inner}</dxf>`;
43016
- }
43017
- function getImageExtension(dataUrl) {
43018
- if (dataUrl.startsWith("data:image/png")) return "png";
43019
- if (dataUrl.startsWith("data:image/jpeg") || dataUrl.startsWith("data:image/jpg")) return "jpeg";
43020
- if (dataUrl.startsWith("data:image/gif")) return "gif";
43021
- return "png";
43022
- }
43023
- function dataUrlToBytes(dataUrl) {
43024
- const match = dataUrl.match(/^data:[^;]+;base64,(.+)$/);
43025
- if (!match) return null;
43026
- const binary = atob(match[1]);
43027
- const bytes = new Uint8Array(binary.length);
43028
- for (let i2 = 0; i2 < binary.length; i2++) bytes[i2] = binary.charCodeAt(i2);
43029
- return bytes;
43919
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
43920
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
43921
+ xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
43922
+ ${sheetPrXml}<dimension ref="${dimensionRef}"/>
43923
+ <sheetViews>${sheetView}</sheetViews>
43924
+ <sheetFormatPr defaultRowHeight="${sheet.defaultRowHeight}" defaultColWidth="${sheet.defaultColWidth}"/>
43925
+ ${cols.length > 0 ? `<cols>${cols.join("")}</cols>` : ""}
43926
+ <sheetData>
43927
+ ${rows.join("\n")}
43928
+ </sheetData>
43929
+ ${autoFilterXml}${mergeXml}${cfXml}${dvXml}${hyperlinksXml}${marginsXml}${pageSetupXml}${headerFooterXml}${drawingXml}${tablePartsXml}
43930
+ </worksheet>`;
43030
43931
  }
43031
- function escapeXml(s) {
43032
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
43932
+ function getCellType(cell, ssIndex) {
43933
+ if (cell.error) return "e";
43934
+ if (cell.formula && typeof cell.value === "string") return "str";
43935
+ if (typeof cell.value === "string" && ssIndex.has(cell.value)) return "s";
43936
+ if (typeof cell.value === "boolean") return "b";
43937
+ return void 0;
43033
43938
  }
43034
-
43035
- // ../xlsx/src/style_helpers.ts
43036
- function border(style = "thin", color = "#000000") {
43037
- return { width: 1, style, color };
43939
+ function getCellValue(cell, ssIndex) {
43940
+ if (cell.value === null) return void 0;
43941
+ if (cell.error) return cell.error;
43942
+ if (cell.formula && typeof cell.value === "string") return cell.value;
43943
+ if (typeof cell.value === "string") {
43944
+ const idx = ssIndex.get(cell.value);
43945
+ return idx !== void 0 ? idx : cell.value;
43946
+ }
43947
+ if (typeof cell.value === "boolean") return cell.value ? 1 : 0;
43948
+ return cell.value;
43038
43949
  }
43039
- function allBorders(style = "thin", color = "#000000") {
43040
- const b = border(style, color);
43041
- return { borderTop: b, borderRight: b, borderBottom: b, borderLeft: b };
43950
+ function quoteSheetName(name) {
43951
+ return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) ? name : `'${name.replace(/'/g, "''")}'`;
43042
43952
  }
43043
-
43044
- // ../xlsx/src/pivot_recompute.ts
43045
- var TOTAL_LABEL = "Grand Total";
43046
- function recomputePivot(table, cache, source) {
43047
- const records = filterByPageAxis(table, cache, source.records);
43048
- const rowTuples = distinctTuples(records, table.rowFieldIndices);
43049
- const colTuples = distinctTuples(records, table.colFieldIndices);
43050
- const groupKey = (rec) => JSON.stringify([
43051
- tupleOf(rec, table.rowFieldIndices),
43052
- tupleOf(rec, table.colFieldIndices)
43053
- ]);
43054
- const groups = /* @__PURE__ */ new Map();
43055
- for (const rec of records) {
43056
- const key = groupKey(rec);
43057
- let bucket = groups.get(key);
43058
- if (!bucket) {
43059
- bucket = [];
43060
- groups.set(key, bucket);
43953
+ function buildWorkbookXml(workbook, pivotInfo) {
43954
+ const sheets = workbook.sheets.map(
43955
+ (s, i2) => `<sheet name="${escapeXml(s.name)}" sheetId="${i2 + 1}" r:id="rId${i2 + 1}"/>`
43956
+ ).join("\n");
43957
+ const nameEntries = [];
43958
+ for (const [name, value] of workbook.namedRanges) {
43959
+ nameEntries.push(`<definedName name="${escapeXml(name)}">${escapeXml(value)}</definedName>`);
43960
+ }
43961
+ workbook.sheets.forEach((s, i2) => {
43962
+ const pt = s.printTitles;
43963
+ if (!pt || !pt.repeatRows && !pt.repeatCols) return;
43964
+ const parts = [];
43965
+ const sheetName = quoteSheetName(s.name);
43966
+ if (pt.repeatCols) {
43967
+ const [start, end] = pt.repeatCols;
43968
+ parts.push(`${sheetName}!$${colNumToLetters(start)}:$${colNumToLetters(end)}`);
43061
43969
  }
43062
- bucket.push(rec);
43970
+ if (pt.repeatRows) {
43971
+ const [start, end] = pt.repeatRows;
43972
+ parts.push(`${sheetName}!$${start}:$${end}`);
43973
+ }
43974
+ nameEntries.push(
43975
+ `<definedName name="_xlnm.Print_Titles" localSheetId="${i2}">${escapeXml(parts.join(","))}</definedName>`
43976
+ );
43977
+ });
43978
+ const definedNames = nameEntries.length > 0 ? `
43979
+ <definedNames>
43980
+ ${nameEntries.join("\n")}
43981
+ </definedNames>` : "";
43982
+ let pivotCachesBlock = "";
43983
+ if (pivotInfo && pivotInfo.cachesByWorkbook.size > 0) {
43984
+ const baseRId = workbook.sheets.length + 3;
43985
+ const cacheIds = [...pivotInfo.cachesByWorkbook.keys()].sort((a, b) => a - b);
43986
+ const items = cacheIds.map((cacheId, i2) => `<pivotCache cacheId="${cacheId}" r:id="rId${baseRId + i2}"/>`).join("\n");
43987
+ pivotCachesBlock = `
43988
+ <pivotCaches>
43989
+ ${items}
43990
+ </pivotCaches>`;
43063
43991
  }
43064
- return buildGrid(table, source, rowTuples, colTuples, groups, records);
43992
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
43993
+ <workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
43994
+ xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
43995
+ <bookViews>
43996
+ <workbookView activeTab="${workbook.activeSheetIndex}"/>
43997
+ </bookViews>
43998
+ <sheets>
43999
+ ${sheets}
44000
+ </sheets>${definedNames}${pivotCachesBlock}
44001
+ <calcPr fullCalcOnLoad="1"/>
44002
+ </workbook>`;
43065
44003
  }
43066
- function filterByPageAxis(table, cache, records) {
43067
- const filters = [];
43068
- for (const fi of table.pageFieldIndices) {
43069
- const cfg = table.fields[fi];
43070
- if (cfg?.selectedPageItem == null) continue;
43071
- const cacheField = cache.fields[fi];
43072
- if (!cacheField) continue;
43073
- const allowed = cacheField.items[cfg.selectedPageItem];
43074
- if (allowed) filters.push({ fieldIndex: fi, allowed });
43075
- }
43076
- if (filters.length === 0) return records;
43077
- return records.filter(
43078
- (rec) => filters.every(
43079
- ({ fieldIndex, allowed }) => cellMatchesItem(rec[fieldIndex], allowed)
43080
- )
44004
+ function buildWorkbookRels(workbook, pivotInfo) {
44005
+ const rels = workbook.sheets.map(
44006
+ (_, i2) => `<Relationship Id="rId${i2 + 1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet${i2 + 1}.xml"/>`
43081
44007
  );
43082
- }
43083
- function cellMatchesItem(cell, item) {
43084
- switch (item.kind) {
43085
- case "string":
43086
- return typeof cell === "string" && cell === item.value;
43087
- case "number":
43088
- return typeof cell === "number" && cell === item.value;
43089
- case "boolean":
43090
- return typeof cell === "boolean" && cell === item.value;
43091
- case "date":
43092
- return typeof cell === "string" && cell === item.value;
43093
- case "missing":
43094
- return cell === void 0 || cell === null || cell === "";
43095
- case "error":
43096
- return typeof cell === "string" && cell === item.value;
44008
+ rels.push(`<Relationship Id="rId${workbook.sheets.length + 1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>`);
44009
+ rels.push(`<Relationship Id="rId${workbook.sheets.length + 2}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>`);
44010
+ if (pivotInfo && pivotInfo.cachesByWorkbook.size > 0) {
44011
+ const baseRId = workbook.sheets.length + 3;
44012
+ const cacheIds = [...pivotInfo.cachesByWorkbook.keys()].sort((a, b) => a - b);
44013
+ cacheIds.forEach((cacheId, i2) => {
44014
+ const target = pivotInfo.cachesByWorkbook.get(cacheId);
44015
+ if (!target) return;
44016
+ rels.push(
44017
+ `<Relationship Id="rId${baseRId + i2}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition" Target="${escapeXml(target)}"/>`
44018
+ );
44019
+ });
43097
44020
  }
44021
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
44022
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
44023
+ ${rels.join("\n")}
44024
+ </Relationships>`;
43098
44025
  }
43099
- function tupleOf(rec, indices) {
43100
- return indices.map((i2) => rec[i2] ?? null);
44026
+ function buildContentTypes(sheetCount, extraTypes = []) {
44027
+ const sheetTypes = Array.from(
44028
+ { length: sheetCount },
44029
+ (_, i2) => `<Override PartName="/xl/worksheets/sheet${i2 + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>`
44030
+ ).join("\n");
44031
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
44032
+ <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
44033
+ <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
44034
+ <Default Extension="xml" ContentType="application/xml"/>
44035
+ <Default Extension="png" ContentType="image/png"/>
44036
+ <Default Extension="jpeg" ContentType="image/jpeg"/>
44037
+ <Default Extension="jpg" ContentType="image/jpeg"/>
44038
+ <Default Extension="gif" ContentType="image/gif"/>
44039
+ <Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
44040
+ ${sheetTypes}
44041
+ <Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>
44042
+ <Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>
44043
+ <Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
44044
+ <Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
44045
+ ${extraTypes.join("\n")}
44046
+ </Types>`;
43101
44047
  }
43102
- function distinctTuples(records, indices) {
43103
- const seen = /* @__PURE__ */ new Set();
43104
- const out = [];
43105
- for (const rec of records) {
43106
- const t = tupleOf(rec, indices);
43107
- const key = JSON.stringify(t);
43108
- if (seen.has(key)) continue;
43109
- seen.add(key);
43110
- out.push(t);
43111
- }
43112
- out.sort((a, b) => {
43113
- for (let i2 = 0; i2 < Math.max(a.length, b.length); i2++) {
43114
- const av = a[i2];
43115
- const bv = b[i2];
43116
- if (av === bv) continue;
43117
- const as = av === null || av === void 0 ? "" : String(av);
43118
- const bs = bv === null || bv === void 0 ? "" : String(bv);
43119
- if (as < bs) return -1;
43120
- if (as > bs) return 1;
43121
- }
43122
- return 0;
43123
- });
43124
- return out;
44048
+ function buildRootRels() {
44049
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
44050
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
44051
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
44052
+ <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
44053
+ <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
44054
+ </Relationships>`;
44055
+ }
44056
+ function buildCoreProps() {
44057
+ const now = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
44058
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
44059
+ <cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties"
44060
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
44061
+ xmlns:dcterms="http://purl.org/dc/terms/"
44062
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
44063
+ <dcterms:created xsi:type="dcterms:W3CDTF">${now}</dcterms:created>
44064
+ <dcterms:modified xsi:type="dcterms:W3CDTF">${now}</dcterms:modified>
44065
+ </cp:coreProperties>`;
43125
44066
  }
43126
- function aggregate(values2, fn) {
43127
- const numeric = values2.filter((v) => typeof v === "number");
43128
- switch (fn) {
43129
- case "count":
43130
- return values2.filter((v) => v !== null && v !== void 0 && v !== "").length;
43131
- case "countNums":
43132
- return numeric.length;
43133
- case "sum":
43134
- return numeric.reduce((a, b) => a + b, 0);
43135
- case "average":
43136
- if (numeric.length === 0) return null;
43137
- return numeric.reduce((a, b) => a + b, 0) / numeric.length;
43138
- case "min":
43139
- return numeric.length === 0 ? null : Math.min(...numeric);
43140
- case "max":
43141
- return numeric.length === 0 ? null : Math.max(...numeric);
43142
- case "product":
43143
- return numeric.length === 0 ? null : numeric.reduce((a, b) => a * b, 1);
43144
- }
44067
+ function buildAppProps() {
44068
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
44069
+ <Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties">
44070
+ <Application>Microsoft Excel</Application>
44071
+ </Properties>`;
43145
44072
  }
43146
- function buildGrid(table, source, rowTuples, colTuples, groups, allRecords) {
43147
- const numRowFields = table.rowFieldIndices.length;
43148
- const numColFields = table.colFieldIndices.length;
43149
- const numDataFields = Math.max(table.dataFields.length, 1);
43150
- const showRowGrand = table.display.colGrandTotals;
43151
- const showColGrand = table.display.rowGrandTotals;
43152
- const rowLabelCols = Math.max(numRowFields, 1);
43153
- const colHeaderRows = numColFields + (numDataFields > 0 ? 1 : 0);
43154
- const headerRows = Math.max(colHeaderRows, 1);
43155
- const dataCols = colTuples.length * numDataFields;
43156
- const totalCols = rowLabelCols + dataCols + (showColGrand ? numDataFields : 0);
43157
- const totalRows = headerRows + rowTuples.length + (showRowGrand ? 1 : 0);
43158
- const cells = [];
43159
- for (let r = 0; r < totalRows; r++) {
43160
- cells.push(new Array(totalCols).fill({ kind: "blank" }));
44073
+ var CHART_TYPE_MAP2 = {
44074
+ bar: "c:barChart",
44075
+ col: "c:barChart",
44076
+ line: "c:lineChart",
44077
+ pie: "c:pieChart",
44078
+ doughnut: "c:doughnutChart",
44079
+ area: "c:areaChart",
44080
+ scatter: "c:scatterChart",
44081
+ bubble: "c:bubbleChart",
44082
+ radar: "c:radarChart",
44083
+ stock: "c:stockChart",
44084
+ surface: "c:surfaceChart"
44085
+ };
44086
+ function buildSeriesXml(s, idx, chartType, categories) {
44087
+ let nameXml = "";
44088
+ if (s.name) nameXml = `<c:tx><c:strRef><c:strCache><c:ptCount val="1"/><c:pt idx="0"><c:v>${escapeXml(s.name)}</c:v></c:pt></c:strCache></c:strRef></c:tx>`;
44089
+ let colorXml = "";
44090
+ if (s.color) colorXml = `<c:spPr><a:solidFill><a:srgbClr val="${hexToOoxmlRgb(s.color)}"/></a:solidFill></c:spPr>`;
44091
+ const valTag = chartType === "scatter" || chartType === "bubble" ? "c:yVal" : "c:val";
44092
+ const valPts = s.values.map((v, i2) => `<c:pt idx="${i2}"><c:v>${v}</c:v></c:pt>`).join("");
44093
+ const valXml = `<${valTag}><c:numRef><c:numCache><c:ptCount val="${s.values.length}"/>${valPts}</c:numCache></c:numRef></${valTag}>`;
44094
+ let catXml = "";
44095
+ if (categories && categories.length > 0) {
44096
+ const catTag = chartType === "scatter" || chartType === "bubble" ? "c:xVal" : "c:cat";
44097
+ const catPts = categories.map((c, i2) => `<c:pt idx="${i2}"><c:v>${escapeXml(c)}</c:v></c:pt>`).join("");
44098
+ catXml = `<${catTag}><c:strRef><c:strCache><c:ptCount val="${categories.length}"/>${catPts}</c:strCache></c:strRef></${catTag}>`;
43161
44099
  }
43162
- for (let level = 0; level < numColFields; level++) {
43163
- let col = rowLabelCols;
43164
- for (const tuple of colTuples) {
43165
- const text = formatCellLabel(tuple[level]);
43166
- for (let i2 = 0; i2 < numDataFields; i2++) {
43167
- cells[level][col + i2] = { kind: "colHeader", depth: level, text };
43168
- }
43169
- col += numDataFields;
43170
- }
44100
+ let bubbleXml = "";
44101
+ if (s.bubbleSizes) {
44102
+ const bPts = s.bubbleSizes.map((v, i2) => `<c:pt idx="${i2}"><c:v>${v}</c:v></c:pt>`).join("");
44103
+ bubbleXml = `<c:bubbleSize><c:numRef><c:numCache><c:ptCount val="${s.bubbleSizes.length}"/>${bPts}</c:numCache></c:numRef></c:bubbleSize>`;
43171
44104
  }
43172
- if (numDataFields > 0) {
43173
- const labelRow = colHeaderRows - 1;
43174
- let col = rowLabelCols;
43175
- for (let _t = 0; _t < colTuples.length; _t++) {
43176
- for (let d = 0; d < table.dataFields.length; d++) {
43177
- cells[labelRow][col + d] = {
43178
- kind: "valueLabel",
43179
- text: table.dataFields[d].name
43180
- };
43181
- }
43182
- col += numDataFields;
43183
- }
43184
- if (showColGrand) {
43185
- for (let d = 0; d < table.dataFields.length; d++) {
43186
- cells[labelRow][rowLabelCols + dataCols + d] = {
43187
- kind: "valueLabel",
43188
- text: table.dataFields[d].name
43189
- };
43190
- }
43191
- }
44105
+ return `<c:ser><c:idx val="${idx}"/><c:order val="${idx}"/>${nameXml}${colorXml}${catXml}${valXml}${bubbleXml}</c:ser>`;
44106
+ }
44107
+ function buildChartTypeElement(chartType, seriesXml, needsAxIds) {
44108
+ const chartTag = CHART_TYPE_MAP2[chartType] ?? "c:barChart";
44109
+ const isBar = chartType === "bar";
44110
+ let barDir = "";
44111
+ if (chartTag === "c:barChart") {
44112
+ barDir = isBar ? '<c:barDir val="bar"/>' : '<c:barDir val="col"/>';
43192
44113
  }
43193
- for (let r = 0; r < rowTuples.length; r++) {
43194
- const rowTuple = rowTuples[r];
43195
- const gridRow = headerRows + r;
43196
- for (let level = 0; level < numRowFields; level++) {
43197
- cells[gridRow][level] = {
43198
- kind: "rowHeader",
43199
- depth: level,
43200
- text: formatCellLabel(rowTuple[level])
43201
- };
43202
- }
43203
- for (let c = 0; c < colTuples.length; c++) {
43204
- const colTuple = colTuples[c];
43205
- const groupRecords = groups.get(JSON.stringify([rowTuple, colTuple])) ?? [];
43206
- for (let d = 0; d < table.dataFields.length; d++) {
43207
- const df = table.dataFields[d];
43208
- const values2 = groupRecords.map((rec) => rec[df.fieldIndex]);
43209
- cells[gridRow][rowLabelCols + c * numDataFields + d] = {
43210
- kind: "value",
43211
- value: aggregate(values2, df.subtotal),
43212
- numFmt: df.numFmt
43213
- };
43214
- }
43215
- }
43216
- if (showColGrand) {
43217
- const rowOnly = allRecords.filter(
43218
- (rec) => sameTuple(tupleOf(rec, table.rowFieldIndices), rowTuple)
43219
- );
43220
- for (let d = 0; d < table.dataFields.length; d++) {
43221
- const df = table.dataFields[d];
43222
- cells[gridRow][rowLabelCols + dataCols + d] = {
43223
- kind: "rowTotal",
43224
- value: aggregate(
43225
- rowOnly.map((rec) => rec[df.fieldIndex]),
43226
- df.subtotal
43227
- )
43228
- };
43229
- }
43230
- }
44114
+ let grouping = "";
44115
+ if (chartTag === "c:barChart") {
44116
+ grouping = '<c:grouping val="clustered"/>';
44117
+ } else if (chartTag === "c:lineChart" || chartTag === "c:areaChart") {
44118
+ grouping = '<c:grouping val="clustered"/>';
43231
44119
  }
43232
- if (showRowGrand) {
43233
- const gridRow = headerRows + rowTuples.length;
43234
- cells[gridRow][0] = { kind: "totalLabel", text: TOTAL_LABEL };
43235
- for (let c = 0; c < colTuples.length; c++) {
43236
- const colTuple = colTuples[c];
43237
- const colOnly = allRecords.filter(
43238
- (rec) => sameTuple(tupleOf(rec, table.colFieldIndices), colTuple)
43239
- );
43240
- for (let d = 0; d < table.dataFields.length; d++) {
43241
- const df = table.dataFields[d];
43242
- cells[gridRow][rowLabelCols + c * numDataFields + d] = {
43243
- kind: "colTotal",
43244
- value: aggregate(
43245
- colOnly.map((rec) => rec[df.fieldIndex]),
43246
- df.subtotal
43247
- )
43248
- };
43249
- }
43250
- }
43251
- if (showColGrand) {
43252
- for (let d = 0; d < table.dataFields.length; d++) {
43253
- const df = table.dataFields[d];
43254
- cells[gridRow][rowLabelCols + dataCols + d] = {
43255
- kind: "grandTotal",
43256
- value: aggregate(
43257
- allRecords.map((rec) => rec[df.fieldIndex]),
43258
- df.subtotal
43259
- )
43260
- };
44120
+ const axIds = needsAxIds ? '<c:axId val="1"/><c:axId val="2"/>' : "";
44121
+ return `<${chartTag}>${barDir}${grouping}${seriesXml}${axIds}</${chartTag}>`;
44122
+ }
44123
+ function buildChartXml(chart) {
44124
+ const needsAxIds = chart.chartType !== "pie" && chart.chartType !== "doughnut";
44125
+ let plotArea;
44126
+ if (chart.chartType === "combo") {
44127
+ const groups = /* @__PURE__ */ new Map();
44128
+ chart.series.forEach((s, idx) => {
44129
+ const sType = s.seriesChartType ?? "col";
44130
+ let group = groups.get(sType);
44131
+ if (!group) {
44132
+ group = [];
44133
+ groups.set(sType, group);
43261
44134
  }
44135
+ group.push({ series: s, idx });
44136
+ });
44137
+ let chartElements = "";
44138
+ for (const [groupType, groupSeries] of groups) {
44139
+ const groupSeriesXml = groupSeries.map(
44140
+ ({ series, idx }) => buildSeriesXml(series, idx, groupType, chart.categories)
44141
+ ).join("");
44142
+ chartElements += buildChartTypeElement(groupType, groupSeriesXml, true);
43262
44143
  }
44144
+ plotArea = `<c:plotArea><c:layout/>${chartElements}`;
44145
+ } else {
44146
+ const seriesXml = chart.series.map(
44147
+ (s, idx) => buildSeriesXml(s, idx, chart.chartType, chart.categories)
44148
+ ).join("");
44149
+ plotArea = `<c:plotArea><c:layout/>${buildChartTypeElement(chart.chartType, seriesXml, needsAxIds)}`;
43263
44150
  }
43264
- void source;
43265
- return {
43266
- cells,
43267
- headerRows,
43268
- rowLabelCols,
43269
- rows: totalRows,
43270
- cols: totalCols
43271
- };
43272
- }
43273
- function sameTuple(a, b) {
43274
- if (a.length !== b.length) return false;
43275
- for (let i2 = 0; i2 < a.length; i2++) {
43276
- if (a[i2] !== b[i2]) {
43277
- const an = a[i2] === void 0 || a[i2] === null || a[i2] === "";
43278
- const bn = b[i2] === void 0 || b[i2] === null || b[i2] === "";
43279
- if (!(an && bn)) return false;
44151
+ if (chart.chartType !== "pie" && chart.chartType !== "doughnut") {
44152
+ const axes = chart.axes ?? [{ type: "category" }, { type: "value" }];
44153
+ for (let i2 = 0; i2 < axes.length; i2++) {
44154
+ const ax = axes[i2];
44155
+ const axId = i2 + 1;
44156
+ const crossId = i2 === 0 ? 2 : 1;
44157
+ let axTag;
44158
+ switch (ax.type) {
44159
+ case "value":
44160
+ axTag = "c:valAx";
44161
+ break;
44162
+ case "date":
44163
+ axTag = "c:dateAx";
44164
+ break;
44165
+ case "series":
44166
+ axTag = "c:serAx";
44167
+ break;
44168
+ default:
44169
+ axTag = "c:catAx";
44170
+ break;
44171
+ }
44172
+ let titleXml2 = "";
44173
+ if (ax.title) titleXml2 = `<c:title><c:tx><c:rich><a:bodyPr/><a:lstStyle/><a:p><a:r><a:t>${escapeXml(ax.title)}</a:t></a:r></a:p></c:rich></c:tx></c:title>`;
44174
+ let scalingXml = '<c:scaling><c:orientation val="minMax"/>';
44175
+ if (ax.min !== void 0) scalingXml += `<c:min val="${ax.min}"/>`;
44176
+ if (ax.max !== void 0) scalingXml += `<c:max val="${ax.max}"/>`;
44177
+ scalingXml += "</c:scaling>";
44178
+ const numFmt = ax.numFmt ? `<c:numFmt formatCode="${escapeXml(ax.numFmt)}" sourceLinked="0"/>` : "";
44179
+ plotArea += `<${axTag}><c:axId val="${axId}"/>${scalingXml}${titleXml2}${numFmt}<c:crossAx val="${crossId}"/></${axTag}>`;
43280
44180
  }
43281
44181
  }
43282
- return true;
44182
+ plotArea += "</c:plotArea>";
44183
+ let titleXml = "";
44184
+ if (chart.title) {
44185
+ titleXml = `<c:title><c:tx><c:rich><a:bodyPr/><a:lstStyle/><a:p><a:r><a:t>${escapeXml(chart.title)}</a:t></a:r></a:p></c:rich></c:tx></c:title>`;
44186
+ }
44187
+ let legendXml = "";
44188
+ if (chart.legendPosition && chart.legendPosition !== "none") {
44189
+ const posMap = { top: "t", bottom: "b", left: "l", right: "r" };
44190
+ legendXml = `<c:legend><c:legendPos val="${posMap[chart.legendPosition] ?? "b"}"/></c:legend>`;
44191
+ }
44192
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
44193
+ <c:chartSpace xmlns:c="http://schemas.openxmlformats.org/drawingml/2006/chart" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
44194
+ <c:chart>${titleXml}${plotArea}${legendXml}</c:chart>
44195
+ </c:chartSpace>`;
43283
44196
  }
43284
- function formatCellLabel(v) {
43285
- if (v === void 0 || v === null || v === "") return "(blank)";
43286
- if (typeof v === "boolean") return v ? "TRUE" : "FALSE";
43287
- return String(v);
44197
+ function buildAnchorPosition(pos) {
44198
+ return `<xdr:col>${pos.col}</xdr:col><xdr:colOff>${pos.colOffset ?? 0}</xdr:colOff><xdr:row>${pos.row}</xdr:row><xdr:rowOff>${pos.rowOffset ?? 0}</xdr:rowOff>`;
43288
44199
  }
43289
-
43290
- // ../xlsx/src/pivot_model.ts
43291
- var PivotTableModel = class {
43292
- constructor(config, cache) {
43293
- this.config = config;
43294
- this.cache = cache;
44200
+ function buildChartAnchorXml(chart, rId) {
44201
+ return `<xdr:twoCellAnchor>
44202
+ <xdr:from>${buildAnchorPosition(chart.anchor.from)}</xdr:from>
44203
+ <xdr:to>${buildAnchorPosition(chart.anchor.to)}</xdr:to>
44204
+ <xdr:graphicFrame macro="">
44205
+ <xdr:nvGraphicFramePr><xdr:cNvPr id="0" name="Chart"/><xdr:cNvGraphicFramePr/></xdr:nvGraphicFramePr>
44206
+ <xdr:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/></xdr:xfrm>
44207
+ <a:graphic><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/chart"><c:chart xmlns:c="http://schemas.openxmlformats.org/drawingml/2006/chart" r:id="${rId}"/></a:graphicData></a:graphic>
44208
+ </xdr:graphicFrame>
44209
+ <xdr:clientData/>
44210
+ </xdr:twoCellAnchor>`;
44211
+ }
44212
+ function buildImageAnchorXml(image, rId) {
44213
+ return `<xdr:twoCellAnchor editAs="oneCell">
44214
+ <xdr:from>${buildAnchorPosition(image.tl)}</xdr:from>
44215
+ <xdr:to>${buildAnchorPosition(image.br)}</xdr:to>
44216
+ <xdr:pic>
44217
+ <xdr:nvPicPr><xdr:cNvPr id="0" name="Image"/><xdr:cNvPicPr/></xdr:nvPicPr>
44218
+ <xdr:blipFill><a:blip xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:embed="${rId}"/><a:stretch><a:fillRect/></a:stretch></xdr:blipFill>
44219
+ <xdr:spPr><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></xdr:spPr>
44220
+ </xdr:pic>
44221
+ <xdr:clientData/>
44222
+ </xdr:twoCellAnchor>`;
44223
+ }
44224
+ function buildShapeAnchorXml(drawing) {
44225
+ let fillXml = "";
44226
+ if (drawing.fillColor) fillXml = `<a:solidFill><a:srgbClr val="${hexToOoxmlRgb(drawing.fillColor)}"/></a:solidFill>`;
44227
+ let outlineXml = "";
44228
+ if (drawing.outlineColor) {
44229
+ const w = Math.round((drawing.outlineWidth ?? 1) * 12700);
44230
+ outlineXml = `<a:ln w="${w}"><a:solidFill><a:srgbClr val="${hexToOoxmlRgb(drawing.outlineColor)}"/></a:solidFill></a:ln>`;
43295
44231
  }
43296
- /**
43297
- * Recompute the pivot result from the workbook's current source data.
43298
- * Callers are responsible for triggering recomputation; the model does
43299
- * not subscribe to workbook changes itself.
43300
- */
43301
- recompute(workbook) {
43302
- const source = readSourceData(workbook, this.cache);
43303
- if (!source) {
43304
- this.result = void 0;
43305
- return;
44232
+ const geom = drawing.geometry ?? "rect";
44233
+ let textXml = "";
44234
+ if (drawing.text) textXml = `<xdr:txBody><a:bodyPr/><a:lstStyle/><a:p><a:r><a:t>${escapeXml(drawing.text)}</a:t></a:r></a:p></xdr:txBody>`;
44235
+ return `<xdr:twoCellAnchor>
44236
+ <xdr:from>${buildAnchorPosition(drawing.anchor.from)}</xdr:from>
44237
+ <xdr:to>${buildAnchorPosition(drawing.anchor.to)}</xdr:to>
44238
+ <xdr:sp><xdr:nvSpPr><xdr:cNvPr id="0" name="Shape"/><xdr:cNvSpPr/></xdr:nvSpPr>
44239
+ <xdr:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/></a:xfrm><a:prstGeom prst="${geom}"><a:avLst/></a:prstGeom>${fillXml}${outlineXml}</xdr:spPr>
44240
+ ${textXml}</xdr:sp>
44241
+ <xdr:clientData/>
44242
+ </xdr:twoCellAnchor>`;
44243
+ }
44244
+ function buildTableXml(table, tableId) {
44245
+ const colsXml = table.columns.map((col) => {
44246
+ let inner = "";
44247
+ if (col.totalsFunction) inner += `<totalsRowFunction>${escapeXml(col.totalsFunction)}</totalsRowFunction>`;
44248
+ if (col.totalsFormula) inner += `<totalsRowFormula>${escapeXml(col.totalsFormula)}</totalsRowFormula>`;
44249
+ return `<tableColumn id="${col.id}" name="${escapeXml(col.name)}">${inner}</tableColumn>`;
44250
+ }).join("");
44251
+ const autoFilterXml = table.autoFilter ? `<autoFilter ref="${escapeXml(table.ref)}"/>` : "";
44252
+ const styleName = table.styleName ?? "TableStyleMedium2";
44253
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
44254
+ <table xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" id="${tableId}" name="${escapeXml(table.name)}" displayName="${escapeXml(table.displayName)}" ref="${escapeXml(table.ref)}" totalsRowCount="${table.totalsRow ? 1 : 0}">
44255
+ ${autoFilterXml}
44256
+ <tableColumns count="${table.columns.length}">${colsXml}</tableColumns>
44257
+ <tableStyleInfo name="${escapeXml(styleName)}" showFirstColumn="0" showLastColumn="0" showRowStripes="1" showColumnStripes="0"/>
44258
+ </table>`;
44259
+ }
44260
+ function buildConditionalFormattingXml(cf, dxfMap) {
44261
+ const rules = cf.rules.map((rule) => {
44262
+ switch (rule.ruleType) {
44263
+ case "colorScale": {
44264
+ const count = rule.colors.length;
44265
+ const cfvos = count === 2 ? '<cfvo type="min"/><cfvo type="max"/>' : '<cfvo type="min"/><cfvo type="percentile" val="50"/><cfvo type="max"/>';
44266
+ const colors = rule.colors.map((c) => `<color rgb="${hexToArgb(c)}"/>`).join("");
44267
+ return `<cfRule type="colorScale" priority="${rule.priority}"><colorScale>${cfvos}${colors}</colorScale></cfRule>`;
44268
+ }
44269
+ case "dataBar": {
44270
+ const showVal = rule.showValue ? "1" : "0";
44271
+ return `<cfRule type="dataBar" priority="${rule.priority}"><dataBar minLength="${rule.minLength}" maxLength="${rule.maxLength}" showValue="${showVal}"><cfvo type="min"/><cfvo type="max"/><color rgb="${hexToArgb(rule.color)}"/></dataBar></cfRule>`;
44272
+ }
44273
+ case "iconSet": {
44274
+ const thresholds = rule.thresholds.map((t) => {
44275
+ if (t.type === "min" || t.type === "autoMin") return '<cfvo type="min"/>';
44276
+ if (t.type === "max" || t.type === "autoMax") return '<cfvo type="max"/>';
44277
+ return `<cfvo type="${t.type}" val="${t.value ?? 0}"/>`;
44278
+ }).join("");
44279
+ const showVal = rule.showValue ? "" : ' showValue="0"';
44280
+ const reverse = rule.reverse ? ' reverse="1"' : "";
44281
+ return `<cfRule type="iconSet" priority="${rule.priority}"><iconSet iconSet="${rule.iconSet}"${showVal}${reverse}>${thresholds}</iconSet></cfRule>`;
44282
+ }
44283
+ case "style": {
44284
+ let attrs = `type="${rule.type}" priority="${rule.priority}"`;
44285
+ if (rule.style) {
44286
+ const dxfId = dxfMap.get(JSON.stringify(rule.style));
44287
+ if (dxfId !== void 0) attrs += ` dxfId="${dxfId}"`;
44288
+ }
44289
+ if (rule.operator) attrs += ` operator="${rule.operator}"`;
44290
+ if (rule.text) attrs += ` text="${escapeXml(rule.text)}"`;
44291
+ if (rule.rank !== void 0) attrs += ` rank="${rule.rank}"`;
44292
+ if (rule.percent) attrs += ' percent="1"';
44293
+ if (rule.bottom) attrs += ' bottom="1"';
44294
+ if (rule.aboveAverage === false) attrs += ' aboveAverage="0"';
44295
+ if (rule.timePeriod) attrs += ` timePeriod="${rule.timePeriod}"`;
44296
+ let inner = "";
44297
+ if (rule.formulae) {
44298
+ inner = rule.formulae.map((f) => `<formula>${escapeXml(String(f))}</formula>`).join("");
44299
+ }
44300
+ return `<cfRule ${attrs}>${inner}</cfRule>`;
44301
+ }
43306
44302
  }
43307
- this.result = recomputePivot(this.config, this.cache, source);
43308
- }
43309
- };
43310
- function readSourceData(workbook, cache) {
43311
- if (cache.source.type !== "worksheet") return void 0;
43312
- const source = cache.source;
43313
- const sheet = workbook.sheets.find((s) => s.name === source.sheetName);
43314
- if (!sheet) return void 0;
43315
- const range = parseRange(source.ref);
43316
- if (!range) return void 0;
43317
- const header = [];
43318
- for (let col = range.startCol; col <= range.endCol; col++) {
43319
- const cell = sheet.getCell(rowColToRef(range.startRow, col));
43320
- header.push(formatHeader(cell?.value));
44303
+ }).join("");
44304
+ return `<conditionalFormatting sqref="${cf.ref}">${rules}</conditionalFormatting>`;
44305
+ }
44306
+ function buildDxfXml(style) {
44307
+ let inner = "";
44308
+ if (style.fontBold || style.fontItalic || style.fontColor) {
44309
+ let fontParts = "";
44310
+ if (style.fontBold) fontParts += "<b/>";
44311
+ if (style.fontItalic) fontParts += "<i/>";
44312
+ if (style.fontColor) fontParts += `<color rgb="${hexToArgb(style.fontColor)}"/>`;
44313
+ inner += `<font>${fontParts}</font>`;
43321
44314
  }
43322
- const records = [];
43323
- for (let row = range.startRow + 1; row <= range.endRow; row++) {
43324
- const rec = [];
43325
- for (let col = range.startCol; col <= range.endCol; col++) {
43326
- const cell = sheet.getCell(rowColToRef(row, col));
43327
- rec.push(coerceValue(cell?.value));
43328
- }
43329
- records.push(rec);
44315
+ if (style.backgroundColor) {
44316
+ inner += `<fill><patternFill><bgColor rgb="${hexToArgb(style.backgroundColor)}"/></patternFill></fill>`;
43330
44317
  }
43331
- return { header, records };
44318
+ return `<dxf>${inner}</dxf>`;
43332
44319
  }
43333
- function formatHeader(v) {
43334
- if (v === void 0 || v === null) return "";
43335
- return String(v);
44320
+ function getImageExtension(dataUrl) {
44321
+ if (dataUrl.startsWith("data:image/png")) return "png";
44322
+ if (dataUrl.startsWith("data:image/jpeg") || dataUrl.startsWith("data:image/jpg")) return "jpeg";
44323
+ if (dataUrl.startsWith("data:image/gif")) return "gif";
44324
+ return "png";
43336
44325
  }
43337
- function coerceValue(v) {
43338
- if (v === void 0 || v === null) return void 0;
43339
- if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
43340
- return v;
43341
- }
43342
- return String(v);
44326
+ function dataUrlToBytes(dataUrl) {
44327
+ const match = dataUrl.match(/^data:[^;]+;base64,(.+)$/);
44328
+ if (!match) return null;
44329
+ const binary = atob(match[1]);
44330
+ const bytes = new Uint8Array(binary.length);
44331
+ for (let i2 = 0; i2 < binary.length; i2++) bytes[i2] = binary.charCodeAt(i2);
44332
+ return bytes;
43343
44333
  }
43344
- function parseRange(ref) {
43345
- const range = ref.includes("!") ? ref.split("!")[1] : ref;
43346
- const cleaned = range.replace(/\$/g, "");
43347
- const m = cleaned.match(/^([A-Z]+\d+)(?::([A-Z]+\d+))?$/);
43348
- if (!m) return void 0;
43349
- const start = refToRowCol(m[1]);
43350
- if (!start) return void 0;
43351
- const endRef = m[2] ?? m[1];
43352
- const end = refToRowCol(endRef);
43353
- if (!end) return void 0;
43354
- return {
43355
- startRow: start.row,
43356
- startCol: start.col,
43357
- endRow: end.row,
43358
- endCol: end.col
43359
- };
44334
+
44335
+ // ../xlsx/src/style_helpers.ts
44336
+ function border(style = "thin", color = "#000000") {
44337
+ return { width: 1, style, color };
44338
+ }
44339
+ function allBorders(style = "thin", color = "#000000") {
44340
+ const b = border(style, color);
44341
+ return { borderTop: b, borderRight: b, borderBottom: b, borderLeft: b };
43360
44342
  }
43361
44343
 
43362
44344
  // ../xlsx/src/workbook_model.ts
@@ -47923,6 +48905,8 @@ async function runDocxCommand(subcommand, toolArgs, restArgs) {
47923
48905
  }
47924
48906
 
47925
48907
  // src/cli.ts
48908
+ dns.setDefaultResultOrder("ipv4first");
48909
+ net2.setDefaultAutoSelectFamilyAttemptTimeout(2e3);
47926
48910
  function printHelp() {
47927
48911
  console.log(`Lotics CLI v${VERSION} \u2014 AI agent interface for Lotics
47928
48912
 
@@ -47979,9 +48963,21 @@ COMMANDS
47979
48963
  lotics app deploy [-m <message>] Build + upload current dir as a new version
47980
48964
  (code + queries only \u2014 workflow bindings are
47981
48965
  managed by set_app_workflow / remove_app_workflow)
48966
+ lotics app codegen [path] Regenerate .lotics/* (types + field/option ids)
48967
+ from the manifest + workspace schema \u2014 no deploy
48968
+ lotics app workflow run <alias> '<json>' Execute a bound app workflow end-to-end
48969
+ (inputs: inline JSON, @file, or stdin;
48970
+ --print-created reports created records +
48971
+ a paste-ready cleanup plan; --cleanup also
48972
+ deletes those records \u2014 NOT a rollback)
48973
+ lotics app workflow set <alias> Push the edited src/workflows/<alias>.ts body
48974
+ through set_app_workflow (server verifies)
48975
+ lotics app workflow pull Rewrite src/workflows/*.ts from the server
47982
48976
  lotics app subdomain <new-subdomain> Rename the app's public <slug>.lotics.app address
47983
48977
  lotics app rename "<new name>" Rename the app's display name (launcher title)
47984
48978
  lotics app dev [path] Run the app locally with HMR (RPC forwarded to prod)
48979
+ lotics ui link <component> [--remove] Dev-link @lotics/ui to the monorepo's
48980
+ packages/ui/src for live HMR (monorepo only)
47985
48981
  lotics xlsx <subcommand> ... Read/write/edit .xlsx files on your local filesystem
47986
48982
  (uses the bundled Lotics xlsx engine; prefer over
47987
48983
  npm xlsx/exceljs for round-trip fidelity)
@@ -48341,6 +49337,34 @@ async function main() {
48341
49337
  await runDocxCommand(subcommand, toolArgs, restArgs);
48342
49338
  return;
48343
49339
  }
49340
+ if (command === "ui") {
49341
+ if (subcommand === "link") {
49342
+ const component = toolArgs;
49343
+ if (!component) {
49344
+ console.error("Usage: lotics ui link <component> [--remove]");
49345
+ console.error("Dev-links @lotics/ui to the monorepo's packages/ui/src for live HMR.");
49346
+ process.exit(1);
49347
+ }
49348
+ appUiLink({ component, remove: restArgs.includes("--remove") });
49349
+ return;
49350
+ }
49351
+ console.error(`Unknown ui subcommand: ${subcommand ?? "(none)"}`);
49352
+ console.error("Usage: lotics ui link <component> [--remove]");
49353
+ process.exit(1);
49354
+ }
49355
+ if (command === "app" && subcommand === "codegen") {
49356
+ const projectDir = toolArgs;
49357
+ const ctx2 = resolveContext(flags);
49358
+ if (!ctx2) {
49359
+ await appCodegen({ projectDir });
49360
+ return;
49361
+ }
49362
+ const viewAsMemberId = flags.viewAs ?? process.env.LOTICS_VIEW_AS;
49363
+ const client2 = new LoticsClient({ apiKey: ctx2.apiKey, workspaceId: ctx2.workspaceId, viewAsMemberId });
49364
+ await resolveWorkspace(client2, ctx2);
49365
+ await appCodegen({ projectDir, client: client2 });
49366
+ return;
49367
+ }
48344
49368
  if (command === "org") {
48345
49369
  const global2 = loadGlobalConfig() ?? {};
48346
49370
  const profiles = global2.profiles ?? {};
@@ -48408,6 +49432,10 @@ async function main() {
48408
49432
  console.error(" lotics app create <name> [path] Scaffold a new app locally");
48409
49433
  console.error(" lotics app pull <app_id> [path] Pull an existing app for local editing");
48410
49434
  console.error(" lotics app deploy [-m <message>] Build + upload the current directory");
49435
+ console.error(" lotics app codegen [path] Regenerate .lotics/* (types + field ids) \u2014 no deploy");
49436
+ console.error(" lotics app workflow run <alias> '<json>' Execute a bound app workflow end-to-end");
49437
+ console.error(" lotics app workflow set <alias> Push the edited src/workflows/<alias>.ts body");
49438
+ console.error(" lotics app workflow pull Rewrite src/workflows/*.ts from the server");
48411
49439
  console.error(" lotics app subdomain <new-subdomain> Rename the app's public <slug>.lotics.app address");
48412
49440
  console.error(` lotics app rename "<new name>" Rename the app's display name (launcher title)`);
48413
49441
  console.error(" lotics app dev [path] Run the app locally with HMR + RPC forwarding");
@@ -48549,6 +49577,59 @@ Available workspaces:`);
48549
49577
  await appRename(client, { name: newName });
48550
49578
  return;
48551
49579
  }
49580
+ if (subcommand === "workflow") {
49581
+ const action = toolArgs;
49582
+ const workflowUsage = () => {
49583
+ console.error("Usage:");
49584
+ console.error(" lotics app workflow run <alias> '<json>' Execute a bound app workflow");
49585
+ console.error(" lotics app workflow set <alias> Push src/workflows/<alias>.ts");
49586
+ console.error(" lotics app workflow pull Rewrite src/workflows/*.ts from the server");
49587
+ process.exit(1);
49588
+ };
49589
+ if (action === "run") {
49590
+ const alias = restArgs[0];
49591
+ if (!alias) {
49592
+ console.error("Usage: lotics app workflow run <alias> '<json>'");
49593
+ console.error(" lotics app workflow run <alias> @inputs.json (read inputs from a file)");
49594
+ console.error(" cat inputs.json | lotics app workflow run <alias> (read inputs from stdin)");
49595
+ console.error("Flags: --print-created (report created records + cleanup plan + caveat)");
49596
+ console.error(" --cleanup (also delete the created records \u2014 records only, NOT a rollback)");
49597
+ process.exit(1);
49598
+ }
49599
+ const ingested = await ingestJsonArgs({
49600
+ rawArg: restArgs[1],
49601
+ stdinIsTTY: process.stdin.isTTY ?? false,
49602
+ readFile: (p) => fs7.readFileSync(p, "utf-8"),
49603
+ readStdin
49604
+ });
49605
+ if (ingested.kind === "error") {
49606
+ console.error(ingested.message);
49607
+ process.exit(1);
49608
+ }
49609
+ await appExecuteWorkflow(client, {
49610
+ alias,
49611
+ inputs: ingested.args,
49612
+ printCreated: flags.printCreated,
49613
+ cleanup: flags.cleanup
49614
+ });
49615
+ return;
49616
+ }
49617
+ if (action === "set") {
49618
+ const alias = restArgs[0];
49619
+ if (!alias) {
49620
+ console.error("Usage: lotics app workflow set <alias>");
49621
+ console.error("Pushes the edited src/workflows/<alias>.ts body via set_app_workflow.");
49622
+ process.exit(1);
49623
+ }
49624
+ await appWorkflowSet(client, { alias });
49625
+ return;
49626
+ }
49627
+ if (action === "pull") {
49628
+ await appWorkflowPull(client);
49629
+ return;
49630
+ }
49631
+ workflowUsage();
49632
+ }
48552
49633
  if (subcommand === "dev") {
48553
49634
  const projectDir = toolArgs;
48554
49635
  let port;
@@ -48647,29 +49728,17 @@ ${JSON.stringify(info.input_schema, null, 2)}`);
48647
49728
  }
48648
49729
  if (command === "run") {
48649
49730
  const toolName = subcommand;
48650
- let rawArgs = toolArgs;
48651
- if (rawArgs && rawArgs.startsWith("@")) {
48652
- const argsPath = rawArgs.slice(1);
48653
- try {
48654
- rawArgs = fs7.readFileSync(argsPath, "utf-8");
48655
- } catch (err2) {
48656
- console.error(
48657
- `Cannot read args file "${argsPath}": ${err2 instanceof Error ? err2.message : String(err2)}`
48658
- );
48659
- process.exit(1);
48660
- }
48661
- } else if (!rawArgs && !process.stdin.isTTY) {
48662
- rawArgs = await readStdin();
48663
- }
48664
- let args = {};
48665
- if (rawArgs) {
48666
- try {
48667
- args = JSON.parse(rawArgs);
48668
- } catch {
48669
- console.error(`Invalid JSON: ${rawArgs}`);
48670
- process.exit(1);
48671
- }
49731
+ const ingested = await ingestJsonArgs({
49732
+ rawArg: toolArgs,
49733
+ stdinIsTTY: process.stdin.isTTY ?? false,
49734
+ readFile: (p) => fs7.readFileSync(p, "utf-8"),
49735
+ readStdin
49736
+ });
49737
+ if (ingested.kind === "error") {
49738
+ console.error(ingested.message);
49739
+ process.exit(1);
48672
49740
  }
49741
+ const args = ingested.args;
48673
49742
  const timeoutMs = flags.timeout ?? 6e4;
48674
49743
  const result = await client.execute(toolName, args, { format: "text", timeoutMs });
48675
49744
  if (result.error) {