@noy-db/as-xlsx 0.2.0-pre.21 → 0.2.0-pre.23
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/index.cjs +136 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -1
- package/dist/index.d.ts +40 -1
- package/dist/index.js +133 -1
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -35,12 +35,14 @@ __export(index_exports, {
|
|
|
35
35
|
download: () => download,
|
|
36
36
|
formula: () => formula,
|
|
37
37
|
fromBytes: () => fromBytes,
|
|
38
|
+
inferSchema: () => inferSchema,
|
|
38
39
|
readXlsx: () => readXlsx,
|
|
39
40
|
styled: () => styled,
|
|
40
41
|
toBytes: () => toBytes,
|
|
41
42
|
toBytesFromCollection: () => toBytesFromCollection,
|
|
42
43
|
write: () => write,
|
|
43
|
-
writeXlsx: () => writeXlsx
|
|
44
|
+
writeXlsx: () => writeXlsx,
|
|
45
|
+
zodSourceFor: () => zodSourceFor
|
|
44
46
|
});
|
|
45
47
|
module.exports = __toCommonJS(index_exports);
|
|
46
48
|
|
|
@@ -465,6 +467,92 @@ function stringAttr(raw) {
|
|
|
465
467
|
return "";
|
|
466
468
|
}
|
|
467
469
|
|
|
470
|
+
// src/infer.ts
|
|
471
|
+
var ISO_DATE = /^\d{4}-\d{2}-\d{2}([T ]\d{2}:\d{2})?/;
|
|
472
|
+
function inferType(vals) {
|
|
473
|
+
if (vals.length === 0) return "string";
|
|
474
|
+
if (vals.every((v) => typeof v === "boolean")) return "boolean";
|
|
475
|
+
if (vals.every((v) => typeof v === "number")) return "number";
|
|
476
|
+
if (vals.every((v) => typeof v === "string" && ISO_DATE.test(v))) return "date";
|
|
477
|
+
return "string";
|
|
478
|
+
}
|
|
479
|
+
async function inferSchema(bytes, options = {}) {
|
|
480
|
+
const skip = new Set(options.skipSheets ?? []);
|
|
481
|
+
const decoded = await readXlsx(bytes);
|
|
482
|
+
const sheets = [];
|
|
483
|
+
for (const sh of decoded.sheets) {
|
|
484
|
+
if (sh.name.startsWith("_") || skip.has(sh.name)) continue;
|
|
485
|
+
const headerRow = sh.rows[0] ?? {};
|
|
486
|
+
const colToField = /* @__PURE__ */ new Map();
|
|
487
|
+
const fields = [];
|
|
488
|
+
for (const [letter, val] of Object.entries(headerRow)) {
|
|
489
|
+
const name = typeof val === "string" ? val.trim() : typeof val === "number" || typeof val === "boolean" ? String(val) : "";
|
|
490
|
+
if (!name) continue;
|
|
491
|
+
colToField.set(letter, name);
|
|
492
|
+
fields.push(name);
|
|
493
|
+
}
|
|
494
|
+
const records = sh.rows.slice(1).map((row) => {
|
|
495
|
+
const rec = {};
|
|
496
|
+
for (const [letter, val] of Object.entries(row)) {
|
|
497
|
+
const f = colToField.get(letter);
|
|
498
|
+
if (f !== void 0) rec[f] = val;
|
|
499
|
+
}
|
|
500
|
+
return rec;
|
|
501
|
+
});
|
|
502
|
+
sheets.push({ name: sh.name, fields, records });
|
|
503
|
+
}
|
|
504
|
+
const valuesOf = (s, f) => s.records.map((r) => r[f]).filter((v) => v != null && v !== "");
|
|
505
|
+
const idFieldOf = (s) => {
|
|
506
|
+
if (s.fields.includes("id")) return "id";
|
|
507
|
+
for (const f of s.fields) {
|
|
508
|
+
const vals = valuesOf(s, f);
|
|
509
|
+
if (vals.length > 0 && new Set(vals.map((v) => String(v))).size === vals.length) return f;
|
|
510
|
+
}
|
|
511
|
+
return s.fields[0] ?? "id";
|
|
512
|
+
};
|
|
513
|
+
const meta = sheets.map((s) => ({ s, idField: idFieldOf(s) }));
|
|
514
|
+
const idValuesByName = /* @__PURE__ */ new Map();
|
|
515
|
+
for (const { s, idField } of meta) {
|
|
516
|
+
idValuesByName.set(s.name, new Set(valuesOf(s, idField).map((v) => String(v))));
|
|
517
|
+
}
|
|
518
|
+
const collections = {};
|
|
519
|
+
for (const { s, idField } of meta) {
|
|
520
|
+
const fields = {};
|
|
521
|
+
for (const f of s.fields) {
|
|
522
|
+
const vals = valuesOf(s, f);
|
|
523
|
+
const type = inferType(vals);
|
|
524
|
+
let references;
|
|
525
|
+
if (f !== idField && type === "string" && vals.length > 0) {
|
|
526
|
+
const distinct = new Set(vals.map((v) => String(v)));
|
|
527
|
+
for (const { s: other } of meta) {
|
|
528
|
+
if (other.name === s.name) continue;
|
|
529
|
+
const ids = idValuesByName.get(other.name);
|
|
530
|
+
if (ids && ids.size > 0 && [...distinct].every((v) => ids.has(v))) {
|
|
531
|
+
references = other.name;
|
|
532
|
+
break;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
fields[f] = references ? { type, references } : { type };
|
|
537
|
+
}
|
|
538
|
+
collections[s.name] = { idField, fields };
|
|
539
|
+
}
|
|
540
|
+
return { collections };
|
|
541
|
+
}
|
|
542
|
+
function zodSourceFor(schema) {
|
|
543
|
+
const zType = (t) => t === "number" ? "z.number()" : t === "boolean" ? "z.boolean()" : t === "date" ? "z.string().datetime()" : "z.string()";
|
|
544
|
+
const blocks = [`import { z } from 'zod'`];
|
|
545
|
+
for (const [name, c] of Object.entries(schema.collections)) {
|
|
546
|
+
const lines = Object.entries(c.fields).map(
|
|
547
|
+
([f, d]) => ` ${f}: ${zType(d.type)},${d.references ? ` // \u2192 ${d.references}` : ""}`
|
|
548
|
+
);
|
|
549
|
+
blocks.push(`export const ${name}Schema = z.object({
|
|
550
|
+
${lines.join("\n")}
|
|
551
|
+
})`);
|
|
552
|
+
}
|
|
553
|
+
return blocks.join("\n\n");
|
|
554
|
+
}
|
|
555
|
+
|
|
468
556
|
// src/index.ts
|
|
469
557
|
var import_hub = require("@noy-db/hub");
|
|
470
558
|
async function toBytesFromCollection(vault, collectionName) {
|
|
@@ -682,6 +770,7 @@ async function buildSmartSheets(vault, options) {
|
|
|
682
770
|
return [m.opt.name, m.records.length, Object.entries(refs).map(([f, r]) => `${f}\u2192${r.target}`).join(", ")];
|
|
683
771
|
})
|
|
684
772
|
};
|
|
773
|
+
const dialect = options.dialect ?? "excel";
|
|
685
774
|
const summarySheets = [];
|
|
686
775
|
for (const spec of options.summaries ?? []) {
|
|
687
776
|
const src = sheetMeta.get(spec.from);
|
|
@@ -689,6 +778,26 @@ async function buildSmartSheets(vault, options) {
|
|
|
689
778
|
const gCol = src?.colIndex.get(spec.groupBy);
|
|
690
779
|
if (!src || !srcMat || gCol === void 0) continue;
|
|
691
780
|
const gLetter = colLetter(gCol);
|
|
781
|
+
if (dialect === "sheets") {
|
|
782
|
+
const selects = [];
|
|
783
|
+
const labels = [];
|
|
784
|
+
for (const a of spec.aggregates) {
|
|
785
|
+
if (a.op === "count") {
|
|
786
|
+
selects.push(`COUNT(${gLetter})`);
|
|
787
|
+
labels.push(`COUNT(${gLetter}) '${a.label}'`);
|
|
788
|
+
continue;
|
|
789
|
+
}
|
|
790
|
+
const vIdx = a.field ? src.colIndex.get(a.field) : void 0;
|
|
791
|
+
if (vIdx === void 0) continue;
|
|
792
|
+
const vL = colLetter(vIdx);
|
|
793
|
+
const fn = a.op === "sum" ? "SUM" : "AVG";
|
|
794
|
+
selects.push(`${fn}(${vL})`);
|
|
795
|
+
labels.push(`${fn}(${vL}) '${a.label}'`);
|
|
796
|
+
}
|
|
797
|
+
const q = `QUERY('${src.name}'!A:ZZ, "SELECT ${gLetter}, ${selects.join(", ")} GROUP BY ${gLetter} LABEL ${labels.join(", ")}", 1)`;
|
|
798
|
+
summarySheets.push({ name: spec.name, rows: [[formula(q)]] });
|
|
799
|
+
continue;
|
|
800
|
+
}
|
|
692
801
|
const seen = /* @__PURE__ */ new Set();
|
|
693
802
|
const groups = [];
|
|
694
803
|
for (const r of srcMat.records) {
|
|
@@ -818,6 +927,13 @@ async function fromBytes(vault, bytes, options) {
|
|
|
818
927
|
} catch {
|
|
819
928
|
}
|
|
820
929
|
}
|
|
930
|
+
const i18nBases = /* @__PURE__ */ new Set();
|
|
931
|
+
if (options.smart) {
|
|
932
|
+
for (const field of colToField.values()) {
|
|
933
|
+
const m = /^(.+)__(.+)$/.exec(field);
|
|
934
|
+
if (m && m[2] !== "label") i18nBases.add(m[1]);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
821
937
|
const records = [];
|
|
822
938
|
for (let i = headerRowIdx + 1; i < allRows.length; i++) {
|
|
823
939
|
const row = allRows[i];
|
|
@@ -826,6 +942,22 @@ async function fromBytes(vault, bytes, options) {
|
|
|
826
942
|
for (const [col, value] of Object.entries(row)) {
|
|
827
943
|
const field = colToField.get(col);
|
|
828
944
|
if (field === void 0) continue;
|
|
945
|
+
if (options.smart) {
|
|
946
|
+
if (field.endsWith("__label")) continue;
|
|
947
|
+
const lm = /^(.+)__(.+)$/.exec(field);
|
|
948
|
+
if (lm && lm[2] !== "label" && i18nBases.has(lm[1])) {
|
|
949
|
+
const base = lm[1];
|
|
950
|
+
const coerced2 = coerceXlsxCell(value, types[base]);
|
|
951
|
+
if (coerced2 !== void 0 && coerced2 !== "") {
|
|
952
|
+
const map = record[base] ?? {};
|
|
953
|
+
map[lm[2]] = coerced2;
|
|
954
|
+
record[base] = map;
|
|
955
|
+
hasAny = true;
|
|
956
|
+
}
|
|
957
|
+
continue;
|
|
958
|
+
}
|
|
959
|
+
if (i18nBases.has(field)) continue;
|
|
960
|
+
}
|
|
829
961
|
const coerced = coerceXlsxCell(value, types[field]);
|
|
830
962
|
if (coerced !== void 0) {
|
|
831
963
|
const invMap = invertMaps.get(field);
|
|
@@ -935,11 +1067,13 @@ function excelSerialToMs(serial) {
|
|
|
935
1067
|
download,
|
|
936
1068
|
formula,
|
|
937
1069
|
fromBytes,
|
|
1070
|
+
inferSchema,
|
|
938
1071
|
readXlsx,
|
|
939
1072
|
styled,
|
|
940
1073
|
toBytes,
|
|
941
1074
|
toBytesFromCollection,
|
|
942
1075
|
write,
|
|
943
|
-
writeXlsx
|
|
1076
|
+
writeXlsx,
|
|
1077
|
+
zodSourceFor
|
|
944
1078
|
});
|
|
945
1079
|
//# sourceMappingURL=index.cjs.map
|