@bendyline/squisq 2.10.0 → 2.11.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.
Files changed (37) hide show
  1. package/dist/{Doc-DTyeaFuW.d.ts → Doc-CRPQCkHn.d.ts} +34 -2
  2. package/dist/{ImageEditDoc-dFEclQ9l.d.ts → ImageEditDoc-MbvJM1HE.d.ts} +1 -1
  3. package/dist/{chunk-IO5XO6XH.js → chunk-64I44JAP.js} +1 -1
  4. package/dist/{chunk-CUYHFOFL.js → chunk-6ATZOUCQ.js} +223 -14
  5. package/dist/{chunk-DRFJU4MK.js → chunk-B26DU2RH.js} +353 -3
  6. package/dist/{chunk-T3B4WJBR.js → chunk-B3AK7SLZ.js} +87 -5
  7. package/dist/{chunk-7YEZFHLS.js → chunk-O7HLQ22N.js} +11 -0
  8. package/dist/{chunk-XSW4QTMB.js → chunk-QQ4I5HWV.js} +228 -6
  9. package/dist/{chunk-NBKAXPSX.js → chunk-TMOHAFYE.js} +1 -0
  10. package/dist/{chunk-MIKKUKOD.js → chunk-UBFT2CDQ.js} +30 -15
  11. package/dist/{chunk-FCG3BIZN.js → chunk-UNVHAK3A.js} +5 -1
  12. package/dist/{chunk-NFHMHYUV.js → chunk-UXKFSYTQ.js} +1 -1
  13. package/dist/doc/index.d.ts +123 -6
  14. package/dist/doc/index.js +13 -5
  15. package/dist/fence/index.d.ts +1 -1
  16. package/dist/generate/index.d.ts +1 -1
  17. package/dist/imageEdit/index.d.ts +3 -3
  18. package/dist/index.d.ts +8 -7
  19. package/dist/index.js +22 -10
  20. package/dist/jsonForm/index.d.ts +1 -1
  21. package/dist/markdown/index.js +3 -3
  22. package/dist/{materializePageSection-BRNIINlU.d.ts → materializePageSection-BeUPHeCl.d.ts} +1 -1
  23. package/dist/narration/index.d.ts +1 -1
  24. package/dist/narration/index.js +4 -4
  25. package/dist/proof/index.d.ts +22 -3
  26. package/dist/proof/index.js +1 -1
  27. package/dist/recommend/index.js +1 -1
  28. package/dist/schemas/index.d.ts +4 -4
  29. package/dist/storage/index.js +1 -1
  30. package/dist/table/index.d.ts +123 -0
  31. package/dist/table/index.js +298 -0
  32. package/dist/{themeLibrary-xaxN5UYu.d.ts → themeLibrary-BG3jr6mT.d.ts} +1 -1
  33. package/dist/transform/index.d.ts +2 -2
  34. package/dist/versions/index.d.ts +15 -1
  35. package/dist/versions/index.js +5 -1
  36. package/dist/viewState-lTZS-hP_.d.ts +91 -0
  37. package/package.json +6 -1
@@ -0,0 +1,123 @@
1
+ import { T as TableViewState, V as ViewIssue } from '../viewState-lTZS-hP_.js';
2
+ export { E as EMPTY_TABLE_VIEW_STATE, F as FilterClause, a as FilterOp, P as ParsedTableViewState, S as SortTerm, i as inferNumericColumn, b as isEmptyViewState, c as isNumericCellText, m as makeTableCollator, p as parseTableViewState, q as quoteViewToken, s as serializeTableViewState } from '../viewState-lTZS-hP_.js';
3
+
4
+ /**
5
+ * Reference implementation of table view state over a plain string matrix —
6
+ * the semantics contract the grid's worker kernel is parity-tested against,
7
+ * and what the sidecar data readers apply before windowing so previews and
8
+ * exports show the author's curated view.
9
+ *
10
+ * Semantics:
11
+ * - filter first (AND conjunction), then a STABLE multi-term sort;
12
+ * - a column is numeric per `inferNumericColumn` (all non-blank cells
13
+ * numeric, no leading-zero strings) — numeric columns compare by value,
14
+ * everything else via the shared collator (`numeric: true`, so "v2" <
15
+ * "v10" in text columns too);
16
+ * - blank cells sort LAST regardless of direction (Excel behavior) and
17
+ * match only `= ''` / `!=` filters;
18
+ * - text-matching ops (`= != ~ !~ ^~ $~`) are case-INSENSITIVE by default
19
+ * (Excel AutoFilter semantics); `clause.caseSensitive` (`*` in the
20
+ * grammar) makes them exact-case. Numeric equality is unaffected.
21
+ */
22
+
23
+ interface AppliedTableView {
24
+ /** Filtered + sorted rows (the same row arrays, re-ordered — not copies). */
25
+ rows: string[][];
26
+ /** Source-row index (0-based, header excluded) per output row. */
27
+ rowIds: number[];
28
+ /** Row count before filtering (equals `rows.length` with no filter). */
29
+ unfilteredRowCount: number;
30
+ }
31
+ /**
32
+ * Apply `view` to `rows`. Row identity is positional: `rowIds[i]` is the
33
+ * index of output row `i` in the input `rows`.
34
+ */
35
+ declare function applyTableViewState(headers: readonly string[], rows: readonly (readonly string[])[], view: TableViewState): AppliedTableView;
36
+
37
+ /**
38
+ * TableQueryProvider — the async, worker-ready contract a data grid renders
39
+ * against and a query backend implements. The default implementation is the
40
+ * in-house columnar worker store in `@bendyline/squisq-grid-react`; heavier
41
+ * backends (SQL engines, remote services) implement the same surface later.
42
+ *
43
+ * Design constraints baked in:
44
+ * - Everything is async: the reference backend lives in a Web Worker, and
45
+ * retrofitting async under a sync grid API is painful — so the contract
46
+ * is async from day one and renderers use windowed prefetch.
47
+ * - View state (sort/filter) is provider-side: the renderer never holds
48
+ * more than a window of rows, so ordering/filtering must happen where
49
+ * the full dataset lives.
50
+ * - Row identity is the immutable SOURCE row index (`rowId`), assigned at
51
+ * ingest — stable under any permutation and unaffected by edits changing
52
+ * sort keys.
53
+ */
54
+
55
+ type TableColumnKind = 'number' | 'string' | 'date' | 'boolean';
56
+ interface TableColumnSchema {
57
+ name: string;
58
+ kind: TableColumnKind;
59
+ }
60
+ interface TableSchema {
61
+ columns: TableColumnSchema[];
62
+ /** Total data rows in the source (before any filter). */
63
+ rowCount: number;
64
+ }
65
+ /** A cell value as providers exchange it. `null` = blank. */
66
+ type TableCellValue = string | number | boolean | null;
67
+ interface TableRowsPage {
68
+ /** View-coordinate index of the first returned row. */
69
+ start: number;
70
+ /** Source row id per returned row (edit/selection identity). */
71
+ rowIds: number[];
72
+ /** Raw values; formatting is the renderer's job. */
73
+ cells: TableCellValue[][];
74
+ }
75
+ interface TableViewResult {
76
+ /** Row count AFTER the active filter (the virtualizer's row count). */
77
+ viewRowCount: number;
78
+ issues: ViewIssue[];
79
+ }
80
+ interface TableCellEdit {
81
+ rowId: number;
82
+ col: number;
83
+ value: TableCellValue;
84
+ }
85
+ interface TableEditResult {
86
+ /**
87
+ * True when an edited cell touched an active sort/filter column — the
88
+ * current permutation may be outdated. Providers NEVER auto re-sort on
89
+ * edit (a row teleporting out from under the caret is hostile); the
90
+ * renderer offers an explicit refresh.
91
+ */
92
+ staleView: boolean;
93
+ }
94
+ /** Result of a {@link TableQueryProvider.distinct} sweep over one column. */
95
+ interface TableDistinctResult {
96
+ /**
97
+ * Distinct non-blank values as display text, sorted (numerically for
98
+ * numeric columns, collated otherwise), capped at the requested limit.
99
+ */
100
+ values: string[];
101
+ /** Total distinct non-blank values in the column (may exceed `values`). */
102
+ totalDistinct: number;
103
+ /** Whether any cell in the column is blank. */
104
+ hasBlank: boolean;
105
+ }
106
+ interface TableQueryProvider {
107
+ describe(): Promise<TableSchema>;
108
+ /** Apply a view; subsequent `rows()` windows are in view coordinates. */
109
+ setView(view: TableViewState): Promise<TableViewResult>;
110
+ rows(start: number, count: number): Promise<TableRowsPage>;
111
+ /** Optional: providers without write support omit it (read-only grid). */
112
+ applyEdits?(edits: TableCellEdit[]): Promise<TableEditResult>;
113
+ /**
114
+ * Optional: distinct values of one column over the FULL source (not the
115
+ * filtered view), for value-picker UIs. Cheap for the columnar store —
116
+ * string columns are dictionary-encoded — so renderers feature-detect
117
+ * this rather than aggregating rows themselves.
118
+ */
119
+ distinct?(col: number, limit: number): Promise<TableDistinctResult>;
120
+ dispose(): void;
121
+ }
122
+
123
+ export { type AppliedTableView, type TableCellEdit, type TableCellValue, type TableColumnKind, type TableColumnSchema, type TableDistinctResult, type TableEditResult, type TableQueryProvider, type TableRowsPage, type TableSchema, type TableViewResult, TableViewState, ViewIssue, applyTableViewState };
@@ -0,0 +1,298 @@
1
+ // src/table/viewState.ts
2
+ var TEXT_FILTER_OPS = ["=", "!=", "~", "!~", "^~", "$~"];
3
+ var EMPTY_TABLE_VIEW_STATE = Object.freeze({
4
+ sort: [],
5
+ filter: []
6
+ });
7
+ function isEmptyViewState(view) {
8
+ return view.sort.length === 0 && view.filter.length === 0;
9
+ }
10
+ function isNumericCellText(value) {
11
+ const trimmed = value.trim();
12
+ if (trimmed === "") return false;
13
+ if (/^0\d/.test(trimmed)) return false;
14
+ return Number.isFinite(Number(trimmed));
15
+ }
16
+ function inferNumericColumn(rows, col) {
17
+ let sawValue = false;
18
+ for (const row of rows) {
19
+ const cell = row[col] ?? "";
20
+ if (cell.trim() === "") continue;
21
+ if (!isNumericCellText(cell)) return false;
22
+ sawValue = true;
23
+ }
24
+ return sawValue;
25
+ }
26
+ function makeTableCollator() {
27
+ return new Intl.Collator(void 0, { numeric: true });
28
+ }
29
+ var NEEDS_INNER_QUOTING_RE = /[,:;=!<>~"^$*]|^\s|\s$/;
30
+ function quoteViewToken(text) {
31
+ if (text === "" || NEEDS_INNER_QUOTING_RE.test(text)) {
32
+ return `"${text.replace(/"/g, '""')}"`;
33
+ }
34
+ return text;
35
+ }
36
+ function readQuoted(scanner) {
37
+ let out = "";
38
+ let i = scanner.pos + 1;
39
+ const { text } = scanner;
40
+ while (i < text.length) {
41
+ const ch = text[i];
42
+ if (ch === '"') {
43
+ if (text[i + 1] === '"') {
44
+ out += '"';
45
+ i += 2;
46
+ continue;
47
+ }
48
+ scanner.pos = i + 1;
49
+ return out;
50
+ }
51
+ out += ch;
52
+ i++;
53
+ }
54
+ return null;
55
+ }
56
+ function readBare(scanner, stops) {
57
+ const { text } = scanner;
58
+ let i = scanner.pos;
59
+ while (i < text.length && !stops.includes(text[i])) i++;
60
+ const out = text.slice(scanner.pos, i);
61
+ scanner.pos = i;
62
+ return out.trim();
63
+ }
64
+ function skipSpaces(scanner) {
65
+ while (scanner.text[scanner.pos] === " ") scanner.pos++;
66
+ }
67
+ var FILTER_OP_TOKENS = [
68
+ { token: ">=", op: ">=", caseSensitive: false },
69
+ { token: "<=", op: "<=", caseSensitive: false },
70
+ { token: "!~*", op: "!~", caseSensitive: true },
71
+ { token: "!~", op: "!~", caseSensitive: false },
72
+ { token: "!=*", op: "!=", caseSensitive: true },
73
+ { token: "!=", op: "!=", caseSensitive: false },
74
+ { token: "^~*", op: "^~", caseSensitive: true },
75
+ { token: "^~", op: "^~", caseSensitive: false },
76
+ { token: "$~*", op: "$~", caseSensitive: true },
77
+ { token: "$~", op: "$~", caseSensitive: false },
78
+ { token: "~*", op: "~", caseSensitive: true },
79
+ { token: "~", op: "~", caseSensitive: false },
80
+ { token: "=*", op: "=", caseSensitive: true },
81
+ { token: "=", op: "=", caseSensitive: false },
82
+ { token: ">", op: ">", caseSensitive: false },
83
+ { token: "<", op: "<", caseSensitive: false }
84
+ ];
85
+ function parseSortParam(raw) {
86
+ const scanner = { text: raw, pos: 0 };
87
+ const terms = [];
88
+ for (; ; ) {
89
+ skipSpaces(scanner);
90
+ const column = scanner.text[scanner.pos] === '"' ? readQuoted(scanner) : readBare(scanner, ":,");
91
+ if (column === null || column === "") return null;
92
+ let dir = "asc";
93
+ skipSpaces(scanner);
94
+ if (scanner.text[scanner.pos] === ":") {
95
+ scanner.pos++;
96
+ skipSpaces(scanner);
97
+ const dirToken = readBare(scanner, ",").toLowerCase();
98
+ if (dirToken !== "asc" && dirToken !== "desc") return null;
99
+ dir = dirToken;
100
+ }
101
+ terms.push({ column, dir });
102
+ skipSpaces(scanner);
103
+ if (scanner.pos >= scanner.text.length) return terms;
104
+ if (scanner.text[scanner.pos] !== ",") return null;
105
+ scanner.pos++;
106
+ }
107
+ }
108
+ function parseFilterParam(raw) {
109
+ const scanner = { text: raw, pos: 0 };
110
+ const clauses = [];
111
+ for (; ; ) {
112
+ skipSpaces(scanner);
113
+ const column = scanner.text[scanner.pos] === '"' ? readQuoted(scanner) : readBare(scanner, "=!<>~^$;");
114
+ if (column === null || column === "") return null;
115
+ skipSpaces(scanner);
116
+ const rest = scanner.text.slice(scanner.pos);
117
+ const match = FILTER_OP_TOKENS.find((candidate) => rest.startsWith(candidate.token));
118
+ if (!match) return null;
119
+ scanner.pos += match.token.length;
120
+ skipSpaces(scanner);
121
+ const value = scanner.text[scanner.pos] === '"' ? readQuoted(scanner) : readBare(scanner, ";");
122
+ if (value === null) return null;
123
+ const clause = { column, op: match.op, value };
124
+ if (match.caseSensitive) clause.caseSensitive = true;
125
+ clauses.push(clause);
126
+ skipSpaces(scanner);
127
+ if (scanner.pos >= scanner.text.length) return clauses;
128
+ if (scanner.text[scanner.pos] !== ";") return null;
129
+ scanner.pos++;
130
+ }
131
+ }
132
+ function parseTableViewState(sortRaw, filterRaw, headers) {
133
+ const issues = [];
134
+ const known = new Set(headers);
135
+ let sort = [];
136
+ if (sortRaw !== void 0 && sortRaw.trim() !== "") {
137
+ const parsed = parseSortParam(sortRaw);
138
+ if (parsed === null) {
139
+ issues.push({ code: "data-view-invalid", message: `sort "${sortRaw}" could not be parsed` });
140
+ } else {
141
+ sort = parsed.filter((term) => {
142
+ if (known.has(term.column)) return true;
143
+ issues.push({
144
+ code: "data-view-unknown-column",
145
+ message: `sort column "${term.column}" is not a header`
146
+ });
147
+ return false;
148
+ });
149
+ }
150
+ }
151
+ let filter = [];
152
+ if (filterRaw !== void 0 && filterRaw.trim() !== "") {
153
+ const parsed = parseFilterParam(filterRaw);
154
+ if (parsed === null) {
155
+ issues.push({
156
+ code: "data-view-invalid",
157
+ message: `filter "${filterRaw}" could not be parsed`
158
+ });
159
+ } else {
160
+ filter = parsed.filter((clause) => {
161
+ if (known.has(clause.column)) return true;
162
+ issues.push({
163
+ code: "data-view-unknown-column",
164
+ message: `filter column "${clause.column}" is not a header`
165
+ });
166
+ return false;
167
+ });
168
+ }
169
+ }
170
+ return { view: { sort, filter }, issues };
171
+ }
172
+ function serializeTableViewState(view) {
173
+ const out = {};
174
+ if (view.sort.length > 0) {
175
+ out.sort = view.sort.map((term) => `${quoteViewToken(term.column)}${term.dir === "desc" ? ":desc" : ""}`).join(",");
176
+ }
177
+ if (view.filter.length > 0) {
178
+ out.filter = view.filter.map((clause) => {
179
+ const star = clause.caseSensitive === true && TEXT_FILTER_OPS.includes(clause.op) ? "*" : "";
180
+ return `${quoteViewToken(clause.column)}${clause.op}${star}${quoteViewToken(clause.value)}`;
181
+ }).join(";");
182
+ }
183
+ return out;
184
+ }
185
+
186
+ // src/table/applyTableViewState.ts
187
+ function matchesClause(cell, clause, numericColumn, collator) {
188
+ const cellTrim = cell.trim();
189
+ const valueTrim = clause.value.trim();
190
+ const blank = cellTrim === "";
191
+ const fold = (text) => clause.caseSensitive ? text : text.toLowerCase();
192
+ switch (clause.op) {
193
+ case "=":
194
+ if (valueTrim === "") return blank;
195
+ if (blank) return false;
196
+ if (numericColumn && isNumericCellText(valueTrim)) {
197
+ return Number(cellTrim) === Number(valueTrim);
198
+ }
199
+ return fold(cellTrim) === fold(valueTrim);
200
+ case "!=":
201
+ return !matchesClause(cell, { ...clause, op: "=" }, numericColumn, collator);
202
+ case "~":
203
+ return !blank && fold(cellTrim).includes(fold(valueTrim));
204
+ case "!~":
205
+ return !matchesClause(cell, { ...clause, op: "~" }, numericColumn, collator);
206
+ case "^~":
207
+ return !blank && fold(cellTrim).startsWith(fold(valueTrim));
208
+ case "$~":
209
+ return !blank && fold(cellTrim).endsWith(fold(valueTrim));
210
+ case ">":
211
+ case "<":
212
+ case ">=":
213
+ case "<=": {
214
+ if (blank) return false;
215
+ let comparison;
216
+ if (numericColumn && isNumericCellText(valueTrim)) {
217
+ comparison = Number(cellTrim) - Number(valueTrim);
218
+ } else {
219
+ comparison = collator.compare(cellTrim, valueTrim);
220
+ }
221
+ if (clause.op === ">") return comparison > 0;
222
+ if (clause.op === "<") return comparison < 0;
223
+ if (clause.op === ">=") return comparison >= 0;
224
+ return comparison <= 0;
225
+ }
226
+ }
227
+ }
228
+ function applyTableViewState(headers, rows, view) {
229
+ const asMutable = rows;
230
+ if (isEmptyViewState(view)) {
231
+ return {
232
+ rows: [...asMutable],
233
+ rowIds: rows.map((_, index) => index),
234
+ unfilteredRowCount: rows.length
235
+ };
236
+ }
237
+ const collator = makeTableCollator();
238
+ const columnIndex = new Map(headers.map((header, index) => [header, index]));
239
+ const numericByCol = /* @__PURE__ */ new Map();
240
+ const numericFor = (col) => {
241
+ let cached = numericByCol.get(col);
242
+ if (cached === void 0) {
243
+ cached = inferNumericColumn(rows, col);
244
+ numericByCol.set(col, cached);
245
+ }
246
+ return cached;
247
+ };
248
+ const clauses = view.filter.map((clause) => ({ clause, col: columnIndex.get(clause.column) })).filter((entry) => entry.col !== void 0);
249
+ let rowIds = [];
250
+ for (let index = 0; index < rows.length; index++) {
251
+ const row = rows[index];
252
+ let pass = true;
253
+ for (const { clause, col } of clauses) {
254
+ if (!matchesClause(row[col] ?? "", clause, numericFor(col), collator)) {
255
+ pass = false;
256
+ break;
257
+ }
258
+ }
259
+ if (pass) rowIds.push(index);
260
+ }
261
+ const terms = view.sort.map((term) => ({ term, col: columnIndex.get(term.column) })).filter(
262
+ (entry) => entry.col !== void 0
263
+ );
264
+ if (terms.length > 0) {
265
+ for (const { col } of terms) numericFor(col);
266
+ rowIds = [...rowIds].sort((a, b) => {
267
+ for (const { term, col } of terms) {
268
+ const left = (rows[a][col] ?? "").trim();
269
+ const right = (rows[b][col] ?? "").trim();
270
+ const leftBlank = left === "";
271
+ const rightBlank = right === "";
272
+ if (leftBlank || rightBlank) {
273
+ if (leftBlank && rightBlank) continue;
274
+ return leftBlank ? 1 : -1;
275
+ }
276
+ const comparison = numericByCol.get(col) ? Number(left) - Number(right) : collator.compare(left, right);
277
+ if (comparison !== 0) return term.dir === "desc" ? -comparison : comparison;
278
+ }
279
+ return a - b;
280
+ });
281
+ }
282
+ return {
283
+ rows: rowIds.map((id) => asMutable[id]),
284
+ rowIds,
285
+ unfilteredRowCount: rows.length
286
+ };
287
+ }
288
+ export {
289
+ EMPTY_TABLE_VIEW_STATE,
290
+ applyTableViewState,
291
+ inferNumericColumn,
292
+ isEmptyViewState,
293
+ isNumericCellText,
294
+ makeTableCollator,
295
+ parseTableViewState,
296
+ quoteViewToken,
297
+ serializeTableViewState
298
+ };
@@ -1,4 +1,4 @@
1
- import { bh as Theme, bl as ThemeRegistry } from './Doc-DTyeaFuW.js';
1
+ import { bh as Theme, bl as ThemeRegistry } from './Doc-CRPQCkHn.js';
2
2
 
3
3
  /**
4
4
  * Built-in Theme Library
@@ -1,5 +1,5 @@
1
- import { r as ColorScheme, O as Doc, k as Block } from '../Doc-DTyeaFuW.js';
2
- import { i as PageTransformHints } from '../materializePageSection-BRNIINlU.js';
1
+ import { r as ColorScheme, O as Doc, k as Block } from '../Doc-CRPQCkHn.js';
2
+ import { i as PageTransformHints } from '../materializePageSection-BeUPHeCl.js';
3
3
  import { d as ExtractionType, E as ExtractedElement, b as ExtractionOptions } from '../contentExtractor-BNfVJV2U.js';
4
4
  import '../types-Be6sBUBx.js';
5
5
 
@@ -128,5 +128,19 @@ declare function parseVersionPath(path: string): {
128
128
  timestamp: Date;
129
129
  collision: number;
130
130
  } | null;
131
+ /** Subfolder holding pre-save backups of DATA sidecar files. */
132
+ declare const DATA_BACKUP_PREFIX = ".versions/data/";
133
+ /**
134
+ * Build a pre-save backup path for a data sidecar file:
135
+ * `.versions/data/<path with '/' → '__'>.<stamp>[-n].<ext>`.
136
+ *
137
+ * The grid's save flow writes the ORIGINAL bytes here before overwriting a
138
+ * sidecar in place — the only cross-save undo the grid offers. Unlike
139
+ * `buildVersionPath` (markdown-only, single-basename), the flattened full
140
+ * path keeps same-named files in different folders distinct, and the
141
+ * extension follows the sidecar. The flattened name goes through the same
142
+ * safety rules as document snapshots (via a per-call strategy).
143
+ */
144
+ declare function buildDataBackupPath(sidecarPath: string, date: Date, collision?: number): string;
131
145
 
132
- export { CoalesceOptions, DocumentVersionManager, type DocumentVersionManagerOptions, PrunePolicy, RevertOptions, RevertResult, SaveVersionOptions, SaveVersionResult, VERSIONS_PREFIX, VERSION_TIMESTAMP_PATTERN, Version, buildVersionPath, coalesceVersions, formatVersionTimestamp, getDocBasename, listVersions, parseVersionPath, parseVersionTimestamp, pruneVersions, readVersion, revertToVersion, saveVersion };
146
+ export { CoalesceOptions, DATA_BACKUP_PREFIX, DocumentVersionManager, type DocumentVersionManagerOptions, PrunePolicy, RevertOptions, RevertResult, SaveVersionOptions, SaveVersionResult, VERSIONS_PREFIX, VERSION_TIMESTAMP_PATTERN, Version, buildDataBackupPath, buildVersionPath, coalesceVersions, formatVersionTimestamp, getDocBasename, listVersions, parseVersionPath, parseVersionTimestamp, pruneVersions, readVersion, revertToVersion, saveVersion };
@@ -1,6 +1,8 @@
1
1
  import {
2
+ DATA_BACKUP_PREFIX,
2
3
  DocumentVersionManager,
3
4
  VERSIONS_PREFIX,
5
+ buildDataBackupPath,
4
6
  buildVersionPath,
5
7
  coalesceVersions,
6
8
  getDocBasename,
@@ -10,16 +12,18 @@ import {
10
12
  readVersion,
11
13
  revertToVersion,
12
14
  saveVersion
13
- } from "../chunk-7YEZFHLS.js";
15
+ } from "../chunk-O7HLQ22N.js";
14
16
  import {
15
17
  VERSION_TIMESTAMP_PATTERN,
16
18
  formatVersionTimestamp,
17
19
  parseVersionTimestamp
18
20
  } from "../chunk-OPO5K4DZ.js";
19
21
  export {
22
+ DATA_BACKUP_PREFIX,
20
23
  DocumentVersionManager,
21
24
  VERSIONS_PREFIX,
22
25
  VERSION_TIMESTAMP_PATTERN,
26
+ buildDataBackupPath,
23
27
  buildVersionPath,
24
28
  coalesceVersions,
25
29
  formatVersionTimestamp,
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Table view state — the non-destructive sort/filter vocabulary that rides a
3
+ * `{[dataTable src=… sort=… filter=…]}` annotation. The data bytes are never
4
+ * touched; view state is part of the DOCUMENT (an author's sort shapes the
5
+ * bounded previews and exports), exactly like `sheet`/`anchor` are part of
6
+ * the reference.
7
+ *
8
+ * Grammar (inside the param value; the whole value then rides the ordinary
9
+ * `{[…]}` attr quoting):
10
+ *
11
+ * sort := term (',' term)*
12
+ * term := colName (':' ('asc'|'desc'))? // default asc
13
+ * filter := clause (';' clause)* // conjunction (AND) only
14
+ * clause := colName op value
15
+ * op := textOp '*'? | cmpOp
16
+ * textOp := '=' | '!=' | '~' | '!~' | '^~' | '$~'
17
+ * cmpOp := '>' | '<' | '>=' | '<='
18
+ *
19
+ * Text-matching ops (`= != ~ !~ ^~ $~`) are CASE-INSENSITIVE by default
20
+ * (Excel AutoFilter semantics); a `*` suffix makes the match case-sensitive
21
+ * (`Region=*West`). `~` is contains, `^~` starts-with, `$~` ends-with (the
22
+ * `~` marks the text family; `^`/`$` are the regex-anchor mnemonics).
23
+ * Comparison ops compare numerically on numeric columns and via the shared
24
+ * collator otherwise, and take no `*`. Column names address headers by exact
25
+ * text. Names/values containing structural characters are quoted CSV-style —
26
+ * double quotes with `""` doubling, NO backslashes (the outer `{[…]}` layer
27
+ * already owns backslash escaping inside quoted attr values; stacking a
28
+ * second backslash grammar inside would be unreadable and fragile):
29
+ *
30
+ * sort=Revenue:desc,Region
31
+ * sort="Ratio A:B":desc
32
+ * filter=Region=West;Revenue>=1000
33
+ * filter=Note~"a;b"
34
+ *
35
+ * Parsing NEVER throws: an unknown column drops that term/clause with a
36
+ * `data-view-unknown-column` issue; malformed syntax drops the whole param
37
+ * with `data-view-invalid`. Renderers fall back to natural order.
38
+ */
39
+ interface SortTerm {
40
+ column: string;
41
+ dir: 'asc' | 'desc';
42
+ }
43
+ type FilterOp = '=' | '!=' | '>' | '<' | '>=' | '<=' | '~' | '!~' | '^~' | '$~';
44
+ interface FilterClause {
45
+ column: string;
46
+ op: FilterOp;
47
+ value: string;
48
+ /**
49
+ * Case-sensitive matching for text ops (serialized as a `*` op suffix).
50
+ * Absent/false = the default case-insensitive match. Meaningless on the
51
+ * comparison ops and never serialized for them.
52
+ */
53
+ caseSensitive?: boolean;
54
+ }
55
+ interface TableViewState {
56
+ sort: SortTerm[];
57
+ filter: FilterClause[];
58
+ }
59
+ interface ViewIssue {
60
+ code: 'data-view-unknown-column' | 'data-view-invalid';
61
+ message: string;
62
+ }
63
+ interface ParsedTableViewState {
64
+ view: TableViewState;
65
+ issues: ViewIssue[];
66
+ }
67
+ declare const EMPTY_TABLE_VIEW_STATE: TableViewState;
68
+ /** True when the view state changes nothing. */
69
+ declare function isEmptyViewState(view: TableViewState): boolean;
70
+ /**
71
+ * A column is numeric iff every non-blank trimmed cell parses to a finite
72
+ * number AND is not a leading-zero string (`"007"` stays text — zip codes,
73
+ * ids). Blank cells don't vote.
74
+ */
75
+ declare function isNumericCellText(value: string): boolean;
76
+ declare function inferNumericColumn(rows: readonly (readonly string[])[], col: number): boolean;
77
+ /** The one collator both sides of the parity contract use. */
78
+ declare function makeTableCollator(): Intl.Collator;
79
+ declare function quoteViewToken(text: string): string;
80
+ /**
81
+ * Parse the raw `sort`/`filter` param values against real headers.
82
+ * Never throws; degradation is per the module contract.
83
+ */
84
+ declare function parseTableViewState(sortRaw: string | undefined, filterRaw: string | undefined, headers: readonly string[]): ParsedTableViewState;
85
+ /** Serialize to `{ sort?, filter? }` raw param values (undefined = omit). */
86
+ declare function serializeTableViewState(view: TableViewState): {
87
+ sort?: string;
88
+ filter?: string;
89
+ };
90
+
91
+ export { EMPTY_TABLE_VIEW_STATE as E, type FilterClause as F, type ParsedTableViewState as P, type SortTerm as S, type TableViewState as T, type ViewIssue as V, type FilterOp as a, isEmptyViewState as b, isNumericCellText as c, inferNumericColumn as i, makeTableCollator as m, parseTableViewState as p, quoteViewToken as q, serializeTableViewState as s };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bendyline/squisq",
3
- "version": "2.10.0",
3
+ "version": "2.11.1",
4
4
  "description": "Headless utilities for doc/block rendering, spatial math, Markdown, and storage",
5
5
  "license": "MIT",
6
6
  "author": "Bendyline",
@@ -131,6 +131,11 @@
131
131
  "types": "./dist/proof/index.d.ts",
132
132
  "import": "./dist/proof/index.js",
133
133
  "default": "./dist/proof/index.js"
134
+ },
135
+ "./table": {
136
+ "types": "./dist/table/index.d.ts",
137
+ "import": "./dist/table/index.js",
138
+ "default": "./dist/table/index.js"
134
139
  }
135
140
  },
136
141
  "scripts": {