@origints/xlsx 0.1.1 → 0.2.0

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/parse.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { XlsxWorkbook } from './xlsx-workbook';
2
+ import { XlsxSpecBuilder } from './xlsx-spec-builder';
2
3
  /**
3
4
  * Transform AST specification.
4
5
  * Compatible with @origints/core TransformAst.
@@ -9,6 +10,14 @@ export interface TransformAst {
9
10
  readonly name: string;
10
11
  readonly args?: unknown;
11
12
  }
13
+ /**
14
+ * Typed transform AST with spec builder type parameter.
15
+ * Compatible with @origints/core TypedTransformAst.
16
+ */
17
+ export interface TypedTransformAst<SB = unknown> extends TransformAst {
18
+ readonly __specBuilder?: SB;
19
+ readonly specBuilderFactory: () => SB;
20
+ }
12
21
  /**
13
22
  * Transform implementation.
14
23
  * Compatible with @origints/core TransformImpl.
@@ -59,12 +68,12 @@ export interface XlsxParseOptions {
59
68
  * .in(loadFile('data.xlsx'))
60
69
  * .mapIn(parseXlsx())
61
70
  * .emit((out, $) => out
62
- * .add('revenue', $.sheet('Sales').cell('B2').number())
71
+ * .add('revenue', $.get('Sales').get('B2').number())
63
72
  * )
64
73
  * .compile()
65
74
  * ```
66
75
  */
67
- export declare function parseXlsx(options?: XlsxParseOptions): TransformAst;
76
+ export declare function parseXlsx(options?: XlsxParseOptions): TypedTransformAst<XlsxSpecBuilder>;
68
77
  /**
69
78
  * Sync transform implementation for parseXlsx.
70
79
  * Note: XLSX parsing requires async due to file reading.
@@ -1,3 +1,4 @@
1
+ import { TYPE_LABEL } from '@origints/core';
1
2
  import { Cell as ExcelJSCell } from 'exceljs';
2
3
  import { XlsxResult, XlsxPath, CellValue, ExtendedCellValue } from './xlsx-result';
3
4
  /**
@@ -61,6 +62,7 @@ export declare class XlsxCell {
61
62
  private readonly cell;
62
63
  private readonly _path;
63
64
  private readonly _sheetName;
65
+ get [TYPE_LABEL](): string;
64
66
  private constructor();
65
67
  /**
66
68
  * Creates an XlsxCell from an ExcelJS cell.
@@ -1,3 +1,4 @@
1
+ import { TYPE_LABEL } from '@origints/core';
1
2
  import { XlsxSheet } from './xlsx-sheet';
2
3
  import { XlsxCell } from './xlsx-cell';
3
4
  import { XlsxResult, XlsxPath } from './xlsx-result';
@@ -33,6 +34,7 @@ export declare class XlsxCursor {
33
34
  private readonly _row;
34
35
  private readonly _col;
35
36
  private readonly _direction;
37
+ get [TYPE_LABEL](): string;
36
38
  private constructor();
37
39
  /**
38
40
  * Create a cursor from an address string.
@@ -0,0 +1,17 @@
1
+ import { XlsxSheet } from './xlsx-sheet';
2
+ import { CellPredicate } from './xlsx-query';
3
+ import { SheetPredicate } from './xlsx-workbook';
4
+ import { CellPredicateSpec, SheetPredicateSpec, RowPredicateSpec } from './xlsx-predicate-spec';
5
+ /**
6
+ * Compile a CellPredicateSpec AST into a runtime CellPredicate closure.
7
+ */
8
+ export declare function compileCellPredicate(spec: CellPredicateSpec): CellPredicate;
9
+ /**
10
+ * Compile a SheetPredicateSpec AST into a runtime SheetPredicate closure.
11
+ */
12
+ export declare function compileSheetPredicate(spec: SheetPredicateSpec): SheetPredicate;
13
+ export type RowPredicate = (sheet: XlsxSheet, row: number, anchorCol: number) => boolean;
14
+ /**
15
+ * Compile a RowPredicateSpec AST into a runtime RowPredicate closure.
16
+ */
17
+ export declare function compileRowPredicate(spec: RowPredicateSpec): RowPredicate;
@@ -0,0 +1,175 @@
1
+ import { CellValue } from './xlsx-result';
2
+ export type CellPredicateSpec = {
3
+ readonly kind: 'equals';
4
+ readonly value: CellValue;
5
+ } | {
6
+ readonly kind: 'contains';
7
+ readonly text: string;
8
+ readonly caseSensitive: boolean;
9
+ } | {
10
+ readonly kind: 'matches';
11
+ readonly pattern: string;
12
+ readonly flags: string;
13
+ } | {
14
+ readonly kind: 'startsWith';
15
+ readonly prefix: string;
16
+ readonly caseSensitive: boolean;
17
+ } | {
18
+ readonly kind: 'endsWith';
19
+ readonly suffix: string;
20
+ readonly caseSensitive: boolean;
21
+ } | {
22
+ readonly kind: 'isString';
23
+ } | {
24
+ readonly kind: 'isNumber';
25
+ } | {
26
+ readonly kind: 'isBoolean';
27
+ } | {
28
+ readonly kind: 'isDate';
29
+ } | {
30
+ readonly kind: 'isEmpty';
31
+ } | {
32
+ readonly kind: 'isNotEmpty';
33
+ } | {
34
+ readonly kind: 'isFormula';
35
+ } | {
36
+ readonly kind: 'isError';
37
+ } | {
38
+ readonly kind: 'isMerged';
39
+ } | {
40
+ readonly kind: 'gt';
41
+ readonly value: number;
42
+ } | {
43
+ readonly kind: 'gte';
44
+ readonly value: number;
45
+ } | {
46
+ readonly kind: 'lt';
47
+ readonly value: number;
48
+ } | {
49
+ readonly kind: 'lte';
50
+ readonly value: number;
51
+ } | {
52
+ readonly kind: 'between';
53
+ readonly min: number;
54
+ readonly max: number;
55
+ } | {
56
+ readonly kind: 'isBold';
57
+ } | {
58
+ readonly kind: 'isItalic';
59
+ } | {
60
+ readonly kind: 'hasHyperlink';
61
+ } | {
62
+ readonly kind: 'hasComment';
63
+ } | {
64
+ readonly kind: 'and';
65
+ readonly left: CellPredicateSpec;
66
+ readonly right: CellPredicateSpec;
67
+ } | {
68
+ readonly kind: 'or';
69
+ readonly left: CellPredicateSpec;
70
+ readonly right: CellPredicateSpec;
71
+ } | {
72
+ readonly kind: 'not';
73
+ readonly operand: CellPredicateSpec;
74
+ };
75
+ export type SheetPredicateSpec = {
76
+ readonly kind: 'nameEquals';
77
+ readonly name: string;
78
+ } | {
79
+ readonly kind: 'nameContains';
80
+ readonly text: string;
81
+ } | {
82
+ readonly kind: 'nameStartsWith';
83
+ readonly prefix: string;
84
+ } | {
85
+ readonly kind: 'nameEndsWith';
86
+ readonly suffix: string;
87
+ } | {
88
+ readonly kind: 'nameMatches';
89
+ readonly pattern: string;
90
+ readonly flags: string;
91
+ } | {
92
+ readonly kind: 'and';
93
+ readonly left: SheetPredicateSpec;
94
+ readonly right: SheetPredicateSpec;
95
+ } | {
96
+ readonly kind: 'or';
97
+ readonly left: SheetPredicateSpec;
98
+ readonly right: SheetPredicateSpec;
99
+ } | {
100
+ readonly kind: 'not';
101
+ readonly operand: SheetPredicateSpec;
102
+ };
103
+ export interface CellPred {
104
+ readonly _spec: CellPredicateSpec;
105
+ and(other: CellPred): CellPred;
106
+ or(other: CellPred): CellPred;
107
+ not(): CellPred;
108
+ describe(): string;
109
+ }
110
+ export interface SheetPred {
111
+ readonly _spec: SheetPredicateSpec;
112
+ and(other: SheetPred): SheetPred;
113
+ or(other: SheetPred): SheetPred;
114
+ not(): SheetPred;
115
+ describe(): string;
116
+ }
117
+ export declare const cell: Readonly<{
118
+ equals: (value: CellValue) => CellPred;
119
+ contains: (text: string, caseSensitive?: boolean) => CellPred;
120
+ matches: (pattern: RegExp) => CellPred;
121
+ startsWith: (prefix: string, caseSensitive?: boolean) => CellPred;
122
+ endsWith: (suffix: string, caseSensitive?: boolean) => CellPred;
123
+ isString: () => CellPred;
124
+ isNumber: () => CellPred;
125
+ isBoolean: () => CellPred;
126
+ isDate: () => CellPred;
127
+ isEmpty: () => CellPred;
128
+ isNotEmpty: () => CellPred;
129
+ isFormula: () => CellPred;
130
+ isError: () => CellPred;
131
+ isMerged: () => CellPred;
132
+ gt: (value: number) => CellPred;
133
+ gte: (value: number) => CellPred;
134
+ lt: (value: number) => CellPred;
135
+ lte: (value: number) => CellPred;
136
+ between: (min: number, max: number) => CellPred;
137
+ isBold: () => CellPred;
138
+ isItalic: () => CellPred;
139
+ hasHyperlink: () => CellPred;
140
+ hasComment: () => CellPred;
141
+ }>;
142
+ export declare const sheet: Readonly<{
143
+ nameEquals: (name: string) => SheetPred;
144
+ nameContains: (text: string) => SheetPred;
145
+ nameStartsWith: (prefix: string) => SheetPred;
146
+ nameEndsWith: (suffix: string) => SheetPred;
147
+ nameMatches: (pattern: RegExp) => SheetPred;
148
+ }>;
149
+ export declare function describeCellPredicate(spec: CellPredicateSpec): string;
150
+ export type RowPredicateSpec = {
151
+ readonly kind: 'rowCol';
152
+ readonly offset: number;
153
+ readonly predicate: CellPredicateSpec;
154
+ } | {
155
+ readonly kind: 'and';
156
+ readonly left: RowPredicateSpec;
157
+ readonly right: RowPredicateSpec;
158
+ } | {
159
+ readonly kind: 'or';
160
+ readonly left: RowPredicateSpec;
161
+ readonly right: RowPredicateSpec;
162
+ } | {
163
+ readonly kind: 'not';
164
+ readonly operand: RowPredicateSpec;
165
+ };
166
+ export interface RowPred {
167
+ readonly _spec: RowPredicateSpec;
168
+ and(other: RowPred): RowPred;
169
+ or(other: RowPred): RowPred;
170
+ not(): RowPred;
171
+ describe(): string;
172
+ }
173
+ export declare function rowCol(offset: number, predicate: CellPred): RowPred;
174
+ export declare function describeRowPredicate(spec: RowPredicateSpec): string;
175
+ export declare function describeSheetPredicate(spec: SheetPredicateSpec): string;
@@ -1,3 +1,4 @@
1
+ import { TYPE_LABEL } from '@origints/core';
1
2
  import { Worksheet } from 'exceljs';
2
3
  import { XlsxCell } from './xlsx-cell';
3
4
  import { XlsxResult, XlsxPath, CellValue } from './xlsx-result';
@@ -18,6 +19,7 @@ export declare class XlsxRange {
18
19
  private readonly _path;
19
20
  private readonly _sheetName;
20
21
  private readonly _file?;
22
+ get [TYPE_LABEL](): string;
21
23
  private constructor();
22
24
  /**
23
25
  * Creates an XlsxRange from coordinates.
@@ -1,3 +1,4 @@
1
+ import { TYPE_LABEL } from '@origints/core';
1
2
  import { Worksheet } from 'exceljs';
2
3
  import { XlsxCell } from './xlsx-cell';
3
4
  import { XlsxRange } from './xlsx-range';
@@ -26,6 +27,7 @@ export declare class XlsxSheet {
26
27
  private readonly worksheet;
27
28
  private readonly _path;
28
29
  private readonly _file?;
30
+ get [TYPE_LABEL](): string;
29
31
  private constructor();
30
32
  /**
31
33
  * Creates an XlsxSheet from an ExcelJS worksheet.
@@ -0,0 +1,177 @@
1
+ import { ArraySpec, ExtractSpec, Spec } from '@origints/core';
2
+ import { XlsxStep, Direction } from './xlsx-spec';
3
+ import { CellPred, SheetPred, RowPred } from './xlsx-predicate-spec';
4
+ /**
5
+ * Spec builder at the cell level — terminal extraction and navigation methods.
6
+ */
7
+ export declare class XlsxCellSB {
8
+ private readonly steps;
9
+ /** @internal */
10
+ constructor(steps: readonly XlsxStep[]);
11
+ /** @internal Expose steps for colWhere/rowWhere headerRef */
12
+ get _steps(): readonly XlsxStep[];
13
+ /** Extract cell value as string */
14
+ string(): ExtractSpec<XlsxStep, 'string'>;
15
+ /** Extract cell value as number */
16
+ number(): ExtractSpec<XlsxStep, 'number'>;
17
+ /** Extract cell value as boolean */
18
+ boolean(): ExtractSpec<XlsxStep, 'boolean'>;
19
+ /** Extract cell value as Date */
20
+ date(): ExtractSpec<XlsxStep, 'date'>;
21
+ /** Extract raw cell value */
22
+ value(): ExtractSpec<XlsxStep, 'value'>;
23
+ /** Extract cell formula */
24
+ formula(): ExtractSpec<XlsxStep, 'formula'>;
25
+ /** Move right by count cells (default 1) */
26
+ right(count?: number): XlsxCellSB;
27
+ /** Move left by count cells (default 1) */
28
+ left(count?: number): XlsxCellSB;
29
+ /** Move up by count cells (default 1) */
30
+ up(count?: number): XlsxCellSB;
31
+ /** Move down by count cells (default 1) */
32
+ down(count?: number): XlsxCellSB;
33
+ /** Move by row and column deltas */
34
+ offset(rowDelta: number, colDelta: number): XlsxCellSB;
35
+ /** Scan in direction until predicate matches */
36
+ scanTo(direction: Direction, predicate: CellPred): XlsxCellSB;
37
+ /** Skip cells in direction while predicate matches, land on first non-matching */
38
+ skipWhile(direction: Direction, predicate: CellPred): XlsxCellSB;
39
+ /**
40
+ * Iterate over cells from this position in a direction while the predicate matches.
41
+ * Returns an ArraySpec whose items are produced by the itemMapper.
42
+ */
43
+ eachCell<T extends Spec>(direction: Direction, while_: CellPred, itemMapper: (cell: XlsxCellSB) => T): ArraySpec<T>;
44
+ /**
45
+ * Iterate over slices (rows or columns) from this cell position.
46
+ * Each slice is passed as an XlsxSliceSB to the mapper, providing
47
+ * header-relative column/row access via colWhere/rowWhere.
48
+ */
49
+ eachSlice<T extends Spec>(direction: Direction, while_: RowPred, itemMapper: (slice: XlsxSliceSB) => T): ArraySpec<T>;
50
+ }
51
+ /**
52
+ * Spec builder for items within a slice (row or column section).
53
+ * Provides header-relative column/row access.
54
+ */
55
+ export declare class XlsxSliceSB {
56
+ private readonly steps;
57
+ /** @internal */
58
+ constructor(steps: readonly XlsxStep[]);
59
+ /** Access a column by matching its header cell against a predicate (header-relative) */
60
+ colWhere(headerRef: XlsxCellSB, predicate: CellPred): XlsxCellSB;
61
+ /** Access a row by matching its header cell against a predicate (header-relative, for rotated tables) */
62
+ rowWhere(headerRef: XlsxCellSB, predicate: CellPred): XlsxCellSB;
63
+ /** Access a column by 0-based offset from the anchor */
64
+ col(offset: number): XlsxCellSB;
65
+ /** Access a row by 0-based offset from the anchor (for rotated tables) */
66
+ row(offset: number): XlsxCellSB;
67
+ }
68
+ /**
69
+ * Spec builder for items within a row (receives an XlsxRange representing one row).
70
+ * Provides column access to navigate within the row.
71
+ */
72
+ export declare class XlsxRowItemSB {
73
+ private readonly steps;
74
+ /** @internal */
75
+ constructor(steps: readonly XlsxStep[]);
76
+ /** Access a column by 1-based index within the row */
77
+ col(index: number): XlsxCellSB;
78
+ /** Navigate to a cell by address within the row */
79
+ cell(ref: string): XlsxCellSB;
80
+ }
81
+ /**
82
+ * Spec builder at the range level — cell navigation, rows, and cells collection.
83
+ */
84
+ export declare class XlsxRangeSB {
85
+ private readonly steps;
86
+ /** @internal */
87
+ constructor(steps: readonly XlsxStep[]);
88
+ /** Navigate to a cell by address within the range */
89
+ cell(ref: string): XlsxCellSB;
90
+ /** Navigate to a cell by coordinates within the range */
91
+ cellAt(row: number, col: number): XlsxCellSB;
92
+ /**
93
+ * Iterate over rows in the range.
94
+ * Each row is provided as an XlsxRowItemSB to the mapper.
95
+ * Returns an ArraySpec.
96
+ */
97
+ rows<T extends Spec>(itemMapper: (row: XlsxRowItemSB) => T): ArraySpec<T>;
98
+ /**
99
+ * Iterate over all cells in the range as a flat list.
100
+ * Each cell is provided as an XlsxCellSB to the mapper.
101
+ * Returns an ArraySpec.
102
+ */
103
+ cells<T extends Spec>(itemMapper: (cell: XlsxCellSB) => T): ArraySpec<T>;
104
+ }
105
+ /**
106
+ * Spec builder at the row level.
107
+ */
108
+ export declare class XlsxRowSB {
109
+ private readonly steps;
110
+ /** @internal */
111
+ constructor(steps: readonly XlsxStep[]);
112
+ /** Navigate to a cell by address within this row */
113
+ cell(ref: string): XlsxCellSB;
114
+ /** Navigate to a cell by column within this row */
115
+ cellAt(row: number, col: number): XlsxCellSB;
116
+ }
117
+ /**
118
+ * Spec builder at the column level.
119
+ */
120
+ export declare class XlsxColSB {
121
+ private readonly steps;
122
+ /** @internal */
123
+ constructor(steps: readonly XlsxStep[]);
124
+ /** Navigate to a cell by address within this column */
125
+ cell(ref: string): XlsxCellSB;
126
+ /** Navigate to a cell by coordinates within this column */
127
+ cellAt(row: number, col: number): XlsxCellSB;
128
+ }
129
+ /**
130
+ * Spec builder at the sheet level — cell, range, row, column, and search navigation.
131
+ */
132
+ export declare class XlsxSheetSB {
133
+ private readonly steps;
134
+ /** @internal */
135
+ constructor(steps: readonly XlsxStep[]);
136
+ /** Navigate to a cell by address (e.g., "A1", "B2") */
137
+ cell(ref: string): XlsxCellSB;
138
+ /** Navigate to a cell by 1-based row and column coordinates */
139
+ cellAt(row: number, col: number): XlsxCellSB;
140
+ /** Find the first cell matching the predicate */
141
+ find(predicate: CellPred): XlsxCellSB;
142
+ /** Find the first cell matching the predicate in the specified row */
143
+ findInRow(rowIndex: number, predicate: CellPred): XlsxCellSB;
144
+ /** Find the first cell matching the predicate in the specified column */
145
+ findInCol(colIndex: number, predicate: CellPred): XlsxCellSB;
146
+ /** Find the first cell matching the predicate in the specified range */
147
+ findInRange(rangeRef: string, predicate: CellPred): XlsxCellSB;
148
+ /** Navigate to a range by address (e.g., "A1:C10") */
149
+ range(ref: string): XlsxRangeSB;
150
+ /** Navigate to a range by two corner addresses */
151
+ range(from: string, to: string): XlsxRangeSB;
152
+ /** Navigate to a range defined by two dynamically resolved cells */
153
+ range(from: XlsxCellSB, to: XlsxCellSB): XlsxRangeSB;
154
+ /** Navigate to a row by 1-based index */
155
+ row(index: number): XlsxRowSB;
156
+ /** Navigate to a column by 1-based index */
157
+ col(index: number): XlsxColSB;
158
+ }
159
+ /**
160
+ * Spec builder for XLSX workbook — the entry point for xlsx navigation specs.
161
+ *
162
+ * Used as `$` in `emit()` after `.mapIn(parseXlsx())`.
163
+ */
164
+ export declare class XlsxSpecBuilder {
165
+ private readonly steps;
166
+ private constructor();
167
+ /** Create a root spec builder */
168
+ static root(): XlsxSpecBuilder;
169
+ /** Navigate to a sheet by name */
170
+ sheet(name: string): XlsxSheetSB;
171
+ /** Navigate to a sheet by 1-based index */
172
+ sheetAt(index: number): XlsxSheetSB;
173
+ /** Navigate to the first sheet */
174
+ firstSheet(): XlsxSheetSB;
175
+ /** Find a sheet matching the predicate */
176
+ findSheet(predicate: SheetPred): XlsxSheetSB;
177
+ }
@@ -0,0 +1,25 @@
1
+ import { TYPE_LABEL, ExtractionResult } from '@origints/core';
2
+ import { XlsxSpec } from './xlsx-spec';
3
+ import { XlsxWorkbook } from './xlsx-workbook';
4
+ import { XlsxSheet } from './xlsx-sheet';
5
+ import { XlsxCell } from './xlsx-cell';
6
+ /**
7
+ * Context wrapper for slice items.
8
+ * Carries workbook+sheet references so colWhere/rowWhere can resolve headers.
9
+ */
10
+ export declare class XlsxSliceContext {
11
+ readonly workbook: XlsxWorkbook;
12
+ readonly sheet: XlsxSheet;
13
+ readonly anchorCell: XlsxCell;
14
+ get [TYPE_LABEL](): string;
15
+ constructor(workbook: XlsxWorkbook, sheet: XlsxSheet, anchorCell: XlsxCell);
16
+ }
17
+ /**
18
+ * Execute an XlsxSpec against data.
19
+ *
20
+ * The executor is polymorphic — it detects the input type:
21
+ * - XlsxWorkbook → starts at workbook level
22
+ * - XlsxRange → starts at range level (used for row items)
23
+ * - XlsxCell → starts at cell level (used for cell items)
24
+ */
25
+ export declare function executeXlsxSpec(spec: XlsxSpec, data: unknown): ExtractionResult<unknown>;
@@ -0,0 +1,121 @@
1
+ import { ExtractSpec, Spec } from '@origints/core';
2
+ import { CellPredicateSpec, SheetPredicateSpec, RowPredicateSpec } from './xlsx-predicate-spec';
3
+ /**
4
+ * Direction for cell navigation.
5
+ */
6
+ export type Direction = 'right' | 'left' | 'up' | 'down';
7
+ /**
8
+ * A single navigation step through an XLSX workbook.
9
+ */
10
+ export type XlsxStep = {
11
+ readonly kind: 'sheet';
12
+ readonly name: string;
13
+ } | {
14
+ readonly kind: 'sheetAt';
15
+ readonly index: number;
16
+ } | {
17
+ readonly kind: 'firstSheet';
18
+ } | {
19
+ readonly kind: 'findSheet';
20
+ readonly predicate: SheetPredicateSpec;
21
+ } | {
22
+ readonly kind: 'find';
23
+ readonly predicate: CellPredicateSpec;
24
+ } | {
25
+ readonly kind: 'findInRow';
26
+ readonly rowIndex: number;
27
+ readonly predicate: CellPredicateSpec;
28
+ } | {
29
+ readonly kind: 'findInCol';
30
+ readonly colIndex: number;
31
+ readonly predicate: CellPredicateSpec;
32
+ } | {
33
+ readonly kind: 'findInRange';
34
+ readonly rangeRef: string;
35
+ readonly predicate: CellPredicateSpec;
36
+ } | {
37
+ readonly kind: 'cell';
38
+ readonly ref: string;
39
+ } | {
40
+ readonly kind: 'cellAt';
41
+ readonly row: number;
42
+ readonly col: number;
43
+ } | {
44
+ readonly kind: 'right';
45
+ readonly count: number;
46
+ } | {
47
+ readonly kind: 'left';
48
+ readonly count: number;
49
+ } | {
50
+ readonly kind: 'up';
51
+ readonly count: number;
52
+ } | {
53
+ readonly kind: 'down';
54
+ readonly count: number;
55
+ } | {
56
+ readonly kind: 'offset';
57
+ readonly rowDelta: number;
58
+ readonly colDelta: number;
59
+ } | {
60
+ readonly kind: 'scanTo';
61
+ readonly direction: Direction;
62
+ readonly predicate: CellPredicateSpec;
63
+ } | {
64
+ readonly kind: 'skipWhile';
65
+ readonly direction: Direction;
66
+ readonly predicate: CellPredicateSpec;
67
+ } | {
68
+ readonly kind: 'colWhere';
69
+ readonly headerRef: Spec;
70
+ readonly predicate: CellPredicateSpec;
71
+ } | {
72
+ readonly kind: 'rowWhere';
73
+ readonly headerRef: Spec;
74
+ readonly predicate: CellPredicateSpec;
75
+ } | {
76
+ readonly kind: 'range';
77
+ readonly ref: string;
78
+ } | {
79
+ readonly kind: 'rangeFromCells';
80
+ readonly from: Spec;
81
+ readonly to: Spec;
82
+ } | {
83
+ readonly kind: 'row';
84
+ readonly index: number;
85
+ } | {
86
+ readonly kind: 'col';
87
+ readonly index: number;
88
+ };
89
+ /**
90
+ * Specification for iterating over cells in a direction while a predicate matches.
91
+ */
92
+ export interface EachCellExtract {
93
+ readonly kind: 'eachCell';
94
+ readonly direction: Direction;
95
+ readonly while: CellPredicateSpec;
96
+ }
97
+ /**
98
+ * Specification for iterating over slices (rows or columns) in a direction
99
+ * while a row-level predicate matches. Each slice is an anchor cell
100
+ * wrapped in XlsxSliceContext for header-relative navigation.
101
+ */
102
+ export interface EachSliceExtract {
103
+ readonly kind: 'eachSlice';
104
+ readonly direction: Direction;
105
+ readonly while: RowPredicateSpec;
106
+ }
107
+ /**
108
+ * The terminal extraction type for an XLSX value.
109
+ *
110
+ * - Cell-level: 'string', 'number', 'boolean', 'date', 'value', 'formula'
111
+ * - Range-level (produce arrays): 'rows', 'cells'
112
+ * - Cell-level (produce arrays): EachCellExtract, EachSliceExtract
113
+ */
114
+ export type XlsxExtract = 'string' | 'number' | 'boolean' | 'date' | 'value' | 'formula' | 'rows' | 'cells' | EachCellExtract | EachSliceExtract;
115
+ /**
116
+ * A complete XLSX extraction spec.
117
+ *
118
+ * Describes a path through the workbook (steps) and what to extract (extract).
119
+ * JSON-serializable and suitable for AST introspection.
120
+ */
121
+ export type XlsxSpec = ExtractSpec<XlsxStep, XlsxExtract>;
@@ -1,3 +1,4 @@
1
+ import { TYPE_LABEL } from '@origints/core';
1
2
  import { Workbook } from 'exceljs';
2
3
  import { XlsxSheet } from './xlsx-sheet';
3
4
  import { XlsxResult, XlsxPath } from './xlsx-result';
@@ -25,6 +26,7 @@ export interface WorkbookProperties {
25
26
  export declare class XlsxWorkbook {
26
27
  private readonly workbook;
27
28
  private readonly _path;
29
+ get [TYPE_LABEL](): string;
28
30
  private readonly sheetsCache;
29
31
  private constructor();
30
32
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@origints/xlsx",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "XLSX parsing for Origins with full traceability to sheets, ranges, and cells.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -20,27 +20,46 @@
20
20
  "publishConfig": {
21
21
  "access": "public"
22
22
  },
23
- "scripts": {
24
- "build": "vite build",
25
- "test": "vitest run",
26
- "test:coverage": "vitest run --coverage",
27
- "lint": "eslint \"{src,tests}/**/*.{ts,tsx}\" --max-warnings 0",
28
- "typecheck": "tsc -p tsconfig.json --noEmit"
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/fponticelli/origints.git",
26
+ "directory": "packages/xlsx"
27
+ },
28
+ "homepage": "https://origints.dev",
29
+ "bugs": "https://github.com/fponticelli/origints/issues",
30
+ "keywords": [
31
+ "origints",
32
+ "data-extraction",
33
+ "lineage",
34
+ "provenance",
35
+ "xlsx",
36
+ "excel",
37
+ "spreadsheet"
38
+ ],
39
+ "engines": {
40
+ "node": ">=18"
29
41
  },
30
42
  "dependencies": {
31
43
  "exceljs": "^4.4.0"
32
44
  },
33
45
  "peerDependencies": {
34
- "@origints/core": "^0.1.0"
46
+ "@origints/core": "^0.2.0"
35
47
  },
36
48
  "devDependencies": {
37
- "@origints/core": "workspace:*",
38
49
  "@types/node": "25.0.6",
39
50
  "@vitest/coverage-v8": "^4.0.16",
40
51
  "eslint": "9.39.2",
41
52
  "typescript": "5.9.3",
42
53
  "vite": "7.3.1",
43
54
  "vite-plugin-dts": "4.5.4",
44
- "vitest": "4.0.16"
55
+ "vitest": "4.0.16",
56
+ "@origints/core": "0.2.0"
57
+ },
58
+ "scripts": {
59
+ "build": "vite build",
60
+ "test": "vitest run",
61
+ "test:coverage": "vitest run --coverage",
62
+ "lint": "eslint \"{src,tests}/**/*.{ts,tsx}\" --max-warnings 0",
63
+ "typecheck": "tsc -p tsconfig.json --noEmit"
45
64
  }
46
- }
65
+ }