@origints/xlsx 0.2.0 → 0.3.2

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.
@@ -1,6 +1,78 @@
1
1
  import { TYPE_LABEL } from '@origints/core';
2
- import { Cell as ExcelJSCell } from 'exceljs';
3
2
  import { XlsxResult, XlsxPath, CellValue, ExtendedCellValue } from './xlsx-result';
3
+ /**
4
+ * Internal cell data — parser-agnostic representation.
5
+ * Created by the parser (SheetJS) and consumed by XlsxCell.
6
+ */
7
+ export interface CellData {
8
+ readonly address: string;
9
+ readonly row: number;
10
+ readonly col: number;
11
+ /** Resolved value (formula result or plain value). */
12
+ readonly resolvedValue: CellValue;
13
+ /** Formula string, if this cell contains a formula. */
14
+ readonly formula?: string;
15
+ /** Error string, if this cell contains an error. */
16
+ readonly error?: string;
17
+ /** Hyperlink URL. */
18
+ readonly hyperlink?: string;
19
+ /** Comment/note text. */
20
+ readonly comment?: string;
21
+ /** Number format string. */
22
+ readonly numFmt?: string;
23
+ /** Whether this cell is part of a merged range. */
24
+ readonly isMerged: boolean;
25
+ /** Address of the master cell if this is a non-master merged cell. */
26
+ readonly masterAddress?: string;
27
+ /** Rich text segments (when available from the parser). */
28
+ readonly richText?: ReadonlyArray<RichTextSegmentData>;
29
+ /** Cell style data (best-effort, may not be available with all parsers). */
30
+ readonly style?: CellStyleData;
31
+ }
32
+ /** Rich text segment from the parser. */
33
+ export interface RichTextSegmentData {
34
+ readonly text: string;
35
+ readonly bold?: boolean;
36
+ readonly italic?: boolean;
37
+ readonly underline?: boolean;
38
+ readonly strike?: boolean;
39
+ readonly color?: string;
40
+ readonly size?: number;
41
+ readonly font?: string;
42
+ }
43
+ /** Cell style from the parser. */
44
+ export interface CellStyleData {
45
+ readonly font?: {
46
+ readonly name?: string;
47
+ readonly size?: number;
48
+ readonly bold?: boolean;
49
+ readonly italic?: boolean;
50
+ readonly underline?: boolean;
51
+ readonly strike?: boolean;
52
+ readonly color?: {
53
+ readonly argb?: string;
54
+ readonly theme?: number;
55
+ };
56
+ };
57
+ readonly fill?: {
58
+ readonly type?: string;
59
+ readonly pattern?: string;
60
+ readonly fgColor?: {
61
+ readonly argb?: string;
62
+ };
63
+ };
64
+ readonly border?: {
65
+ readonly top?: boolean;
66
+ readonly bottom?: boolean;
67
+ readonly left?: boolean;
68
+ readonly right?: boolean;
69
+ };
70
+ readonly alignment?: {
71
+ readonly horizontal?: string;
72
+ readonly vertical?: string;
73
+ readonly wrapText?: boolean;
74
+ };
75
+ }
4
76
  /**
5
77
  * Cell style information.
6
78
  */
@@ -52,23 +124,28 @@ export interface RichTextContent {
52
124
  readonly hyperlink?: string;
53
125
  }
54
126
  /**
55
- * A wrapper around ExcelJS Cell with full metadata preservation.
127
+ * A wrapper around cell data with full metadata preservation.
56
128
  *
57
129
  * XlsxCell enables typed extraction of cell values while maintaining
58
130
  * provenance information. Each cell knows its exact location in the
59
131
  * workbook, allowing full traceability.
60
132
  */
61
133
  export declare class XlsxCell {
62
- private readonly cell;
134
+ private readonly _data;
63
135
  private readonly _path;
64
136
  private readonly _sheetName;
65
137
  get [TYPE_LABEL](): string;
66
138
  private constructor();
67
139
  /**
68
- * Creates an XlsxCell from an ExcelJS cell.
140
+ * Creates an XlsxCell from internal cell data.
69
141
  * @internal
70
142
  */
71
- static fromExcelJS(cell: ExcelJSCell, sheetName: string, file?: string): XlsxCell;
143
+ static create(data: CellData, sheetName: string, file?: string): XlsxCell;
144
+ /**
145
+ * Creates an empty cell for the given coordinates.
146
+ * @internal
147
+ */
148
+ static empty(row: number, col: number, sheetName: string, file?: string): XlsxCell;
72
149
  /**
73
150
  * Returns the cell address (e.g., "A1").
74
151
  */
@@ -123,18 +200,10 @@ export declare class XlsxCell {
123
200
  formula(): XlsxResult<string>;
124
201
  /**
125
202
  * Get the cell content as rich text with formatting preserved.
126
- * Returns segments with their individual formatting properties.
127
203
  */
128
204
  richText(): XlsxResult<RichTextContent>;
129
205
  /**
130
206
  * Get the cell content as Markdown.
131
- *
132
- * Converts cell content including:
133
- * - Bold text -> **text**
134
- * - Italic text -> *text*
135
- * - Strikethrough -> ~~text~~
136
- * - Hyperlinks -> [text](url)
137
- * - Rich text segments with mixed formatting
138
207
  */
139
208
  markdown(): XlsxResult<string>;
140
209
  /**
@@ -195,17 +264,13 @@ export declare class XlsxCell {
195
264
  */
196
265
  getOffsetCell(rowDelta: number, colDelta: number, getCell: (row: number, col: number) => XlsxCell | undefined): XlsxResult<XlsxCell>;
197
266
  /**
198
- * Get the raw value, handling ExcelJS value types.
267
+ * Get the raw value, handling formula/error types.
199
268
  */
200
269
  private getRawValue;
201
270
  /**
202
271
  * Get the resolved value (formula result or plain value).
203
272
  */
204
273
  private getResolvedValue;
205
- /**
206
- * Normalize a formula result value.
207
- */
208
- private normalizeResult;
209
274
  /**
210
275
  * Convert Excel date number to JavaScript Date.
211
276
  * Excel dates are days since 1900-01-01 (with a leap year bug).
@@ -1,11 +1,15 @@
1
+ import { SpecScope } from '@origints/core';
1
2
  import { XlsxSheet } from './xlsx-sheet';
2
3
  import { CellPredicate } from './xlsx-query';
3
4
  import { SheetPredicate } from './xlsx-workbook';
4
5
  import { CellPredicateSpec, SheetPredicateSpec, RowPredicateSpec } from './xlsx-predicate-spec';
5
6
  /**
6
7
  * Compile a CellPredicateSpec AST into a runtime CellPredicate closure.
8
+ *
9
+ * @param spec - The predicate spec to compile
10
+ * @param scope - Optional scope for variable reference predicates (equalsRef, containsRef)
7
11
  */
8
- export declare function compileCellPredicate(spec: CellPredicateSpec): CellPredicate;
12
+ export declare function compileCellPredicate(spec: CellPredicateSpec, scope?: SpecScope): CellPredicate;
9
13
  /**
10
14
  * Compile a SheetPredicateSpec AST into a runtime SheetPredicate closure.
11
15
  */
@@ -13,5 +17,8 @@ export declare function compileSheetPredicate(spec: SheetPredicateSpec): SheetPr
13
17
  export type RowPredicate = (sheet: XlsxSheet, row: number, anchorCol: number) => boolean;
14
18
  /**
15
19
  * Compile a RowPredicateSpec AST into a runtime RowPredicate closure.
20
+ *
21
+ * @param spec - The row predicate spec to compile
22
+ * @param scope - Optional scope for variable reference predicates
16
23
  */
17
- export declare function compileRowPredicate(spec: RowPredicateSpec): RowPredicate;
24
+ export declare function compileRowPredicate(spec: RowPredicateSpec, scope?: SpecScope): RowPredicate;
@@ -1,3 +1,4 @@
1
+ import { PredicateWrapper } from '@origints/core';
1
2
  import { CellValue } from './xlsx-result';
2
3
  export type CellPredicateSpec = {
3
4
  readonly kind: 'equals';
@@ -60,6 +61,20 @@ export type CellPredicateSpec = {
60
61
  readonly kind: 'hasHyperlink';
61
62
  } | {
62
63
  readonly kind: 'hasComment';
64
+ } | {
65
+ readonly kind: 'equalsRef';
66
+ readonly ref: string;
67
+ readonly path?: readonly (string | number)[];
68
+ } | {
69
+ readonly kind: 'containsRef';
70
+ readonly ref: string;
71
+ readonly path?: readonly (string | number)[];
72
+ readonly caseSensitive: boolean;
73
+ } | {
74
+ readonly kind: 'startsWithRef';
75
+ readonly ref: string;
76
+ readonly path?: readonly (string | number)[];
77
+ readonly caseSensitive: boolean;
63
78
  } | {
64
79
  readonly kind: 'and';
65
80
  readonly left: CellPredicateSpec;
@@ -100,20 +115,8 @@ export type SheetPredicateSpec = {
100
115
  readonly kind: 'not';
101
116
  readonly operand: SheetPredicateSpec;
102
117
  };
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
- }
118
+ export type CellPred = PredicateWrapper<CellPredicateSpec>;
119
+ export type SheetPred = PredicateWrapper<SheetPredicateSpec>;
117
120
  export declare const cell: Readonly<{
118
121
  equals: (value: CellValue) => CellPred;
119
122
  contains: (text: string, caseSensitive?: boolean) => CellPred;
@@ -138,6 +141,45 @@ export declare const cell: Readonly<{
138
141
  isItalic: () => CellPred;
139
142
  hasHyperlink: () => CellPred;
140
143
  hasComment: () => CellPred;
144
+ /**
145
+ * Match when cell value equals a bound variable from ForEachSpec scope.
146
+ *
147
+ * @example
148
+ * ```ts
149
+ * forEach(
150
+ * $.get('companies').strings(),
151
+ * 'company',
152
+ * xlsxSB.firstSheet().find(cell.equalsRef('company')).string()
153
+ * )
154
+ * ```
155
+ */
156
+ equalsRef: (ref: string, path?: readonly (string | number)[]) => CellPred;
157
+ /**
158
+ * Match when cell string value contains a bound variable from ForEachSpec scope.
159
+ *
160
+ * @example
161
+ * ```ts
162
+ * forEach(
163
+ * $.get('keywords').strings(),
164
+ * 'keyword',
165
+ * xlsxSB.firstSheet().find(cell.containsRef('keyword')).string()
166
+ * )
167
+ * ```
168
+ */
169
+ containsRef: (ref: string, path?: readonly (string | number)[], caseSensitive?: boolean) => CellPred;
170
+ /**
171
+ * Match when cell string value starts with a bound variable from ForEachSpec scope.
172
+ *
173
+ * @example
174
+ * ```ts
175
+ * forEach(
176
+ * $.get('companies').strings(),
177
+ * 'company',
178
+ * xlsxSB.firstSheet().find(cell.startsWithRef('company')).string()
179
+ * )
180
+ * ```
181
+ */
182
+ startsWithRef: (ref: string, path?: readonly (string | number)[], caseSensitive?: boolean) => CellPred;
141
183
  }>;
142
184
  export declare const sheet: Readonly<{
143
185
  nameEquals: (name: string) => SheetPred;
@@ -163,13 +205,7 @@ export type RowPredicateSpec = {
163
205
  readonly kind: 'not';
164
206
  readonly operand: RowPredicateSpec;
165
207
  };
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
- }
208
+ export type RowPred = PredicateWrapper<RowPredicateSpec>;
173
209
  export declare function rowCol(offset: number, predicate: CellPred): RowPred;
174
210
  export declare function describeRowPredicate(spec: RowPredicateSpec): string;
175
211
  export declare function describeSheetPredicate(spec: SheetPredicateSpec): string;
@@ -1,8 +1,12 @@
1
1
  import { TYPE_LABEL } from '@origints/core';
2
- import { Worksheet } from 'exceljs';
3
2
  import { XlsxCell } from './xlsx-cell';
4
3
  import { XlsxResult, XlsxPath, CellValue } from './xlsx-result';
5
4
  import { CellPredicate } from './xlsx-query';
5
+ /**
6
+ * Cell accessor function — resolves a cell by 1-based row/col.
7
+ * Provided by XlsxSheet so XlsxRange is decoupled from sheet internals.
8
+ */
9
+ export type CellAccessor = (row: number, col: number) => XlsxResult<XlsxCell>;
6
10
  /**
7
11
  * A range of cells in an XLSX sheet.
8
12
  *
@@ -11,7 +15,7 @@ import { CellPredicate } from './xlsx-query';
11
15
  * and conversion to various formats.
12
16
  */
13
17
  export declare class XlsxRange {
14
- private readonly worksheet;
18
+ private readonly _getCell;
15
19
  private readonly _startRow;
16
20
  private readonly _startCol;
17
21
  private readonly _endRow;
@@ -25,12 +29,12 @@ export declare class XlsxRange {
25
29
  * Creates an XlsxRange from coordinates.
26
30
  * @internal
27
31
  */
28
- static fromCoords(worksheet: Worksheet, startRow: number, startCol: number, endRow: number, endCol: number, sheetName: string, file?: string): XlsxRange;
32
+ static fromCoords(getCell: CellAccessor, startRow: number, startCol: number, endRow: number, endCol: number, sheetName: string, file?: string): XlsxRange;
29
33
  /**
30
34
  * Creates an XlsxRange from an address string.
31
35
  * @internal
32
36
  */
33
- static fromAddress(worksheet: Worksheet, address: string, sheetName: string, file?: string): XlsxResult<XlsxRange>;
37
+ static fromAddress(getCell: CellAccessor, address: string, sheetName: string, file?: string): XlsxResult<XlsxRange>;
34
38
  /**
35
39
  * Returns the range address (e.g., "A1:C10").
36
40
  */
@@ -1,10 +1,9 @@
1
1
  import { TYPE_LABEL } from '@origints/core';
2
- import { Worksheet } from 'exceljs';
3
- import { XlsxCell } from './xlsx-cell';
2
+ import { CellData, XlsxCell } from './xlsx-cell';
4
3
  import { XlsxRange } from './xlsx-range';
5
- import { XlsxCursor } from './xlsx-cursor';
6
- import { XlsxResult, XlsxPath, CellValue } from './xlsx-result';
4
+ import { XlsxResult, XlsxPath } from './xlsx-result';
7
5
  import { CellPredicate } from './xlsx-query';
6
+ import { XlsxCursor } from './xlsx-cursor';
8
7
  /**
9
8
  * Dimensions of a sheet's used range.
10
9
  */
@@ -17,23 +16,30 @@ export interface SheetDimensions {
17
16
  readonly colCount: number;
18
17
  }
19
18
  /**
20
- * A wrapper around ExcelJS Worksheet with navigation and query capabilities.
19
+ * A wrapper around sheet data with navigation and query capabilities.
21
20
  *
22
21
  * XlsxSheet provides a rich API for navigating cells, ranges, and rows
23
22
  * within a worksheet. It supports predicate-based queries and cursor-based
24
23
  * navigation for complex data extraction scenarios.
25
24
  */
26
25
  export declare class XlsxSheet {
27
- private readonly worksheet;
26
+ get [TYPE_LABEL](): string;
27
+ /** Cell data from the parser — keyed by row*16384+col. */
28
+ private readonly _cells;
29
+ /** Cache for XlsxCell wrappers — avoids repeated creation. */
30
+ private readonly _cellCache;
31
+ private readonly _dims;
32
+ private readonly _name;
33
+ private readonly _index;
34
+ private readonly _mergeRanges;
28
35
  private readonly _path;
29
36
  private readonly _file?;
30
- get [TYPE_LABEL](): string;
31
37
  private constructor();
32
38
  /**
33
- * Creates an XlsxSheet from an ExcelJS worksheet.
39
+ * Creates an XlsxSheet from internal data.
34
40
  * @internal
35
41
  */
36
- static fromExcelJS(worksheet: Worksheet, file?: string): XlsxSheet;
42
+ static create(cells: Map<number, CellData>, dims: SheetDimensions, name: string, index: number, mergeRanges: string[], file?: string): XlsxSheet;
37
43
  /**
38
44
  * Returns the sheet name.
39
45
  */
@@ -57,7 +63,7 @@ export declare class XlsxSheet {
57
63
  /**
58
64
  * Get the value of a cell directly.
59
65
  */
60
- getValue(address: string): XlsxResult<CellValue>;
66
+ getValue(address: string): XlsxResult<import('./xlsx-result').CellValue>;
61
67
  /**
62
68
  * Get a range by its address (e.g., "A1:C10").
63
69
  */
@@ -109,7 +115,7 @@ export declare class XlsxSheet {
109
115
  /**
110
116
  * Find the first cell with the specified value.
111
117
  */
112
- findValue(value: CellValue): XlsxResult<XlsxCell>;
118
+ findValue(value: import('./xlsx-result').CellValue): XlsxResult<XlsxCell>;
113
119
  /**
114
120
  * Find cells within a specific range matching the predicate.
115
121
  */
@@ -138,11 +144,6 @@ export declare class XlsxSheet {
138
144
  * Check if a cell is part of a merged range.
139
145
  */
140
146
  isMerged(address: string): boolean;
141
- /**
142
- * Get the underlying ExcelJS worksheet.
143
- * @internal
144
- */
145
- getWorksheet(): Worksheet;
146
147
  /**
147
148
  * Get a cell by coordinates for internal use.
148
149
  * @internal
@@ -1,11 +1,11 @@
1
- import { ArraySpec, ExtractSpec, Spec } from '@origints/core';
1
+ import { StepBuilder, ArraySpec, ObjectSpec, ExtractSpec, Spec, SpecLike } from '@origints/core';
2
2
  import { XlsxStep, Direction } from './xlsx-spec';
3
3
  import { CellPred, SheetPred, RowPred } from './xlsx-predicate-spec';
4
4
  /**
5
5
  * Spec builder at the cell level — terminal extraction and navigation methods.
6
6
  */
7
- export declare class XlsxCellSB {
8
- private readonly steps;
7
+ export declare class XlsxCellSB extends StepBuilder<XlsxStep, 'xlsx'> {
8
+ protected get format(): "xlsx";
9
9
  /** @internal */
10
10
  constructor(steps: readonly XlsxStep[]);
11
11
  /** @internal Expose steps for colWhere/rowWhere headerRef */
@@ -22,6 +22,16 @@ export declare class XlsxCellSB {
22
22
  value(): ExtractSpec<XlsxStep, 'value'>;
23
23
  /** Extract cell formula */
24
24
  formula(): ExtractSpec<XlsxStep, 'formula'>;
25
+ /** Extract cell value as string, returning default on failure */
26
+ optionalString(defaultValue?: unknown): ExtractSpec<XlsxStep, 'string'>;
27
+ /** Extract cell value as number, returning default on failure */
28
+ optionalNumber(defaultValue?: unknown): ExtractSpec<XlsxStep, 'number'>;
29
+ /** Extract cell value as boolean, returning default on failure */
30
+ optionalBoolean(defaultValue?: unknown): ExtractSpec<XlsxStep, 'boolean'>;
31
+ /** Extract cell value as Date, returning default on failure */
32
+ optionalDate(defaultValue?: unknown): ExtractSpec<XlsxStep, 'date'>;
33
+ /** Extract raw cell value, returning default on failure */
34
+ optionalValue(defaultValue?: unknown): ExtractSpec<XlsxStep, 'value'>;
25
35
  /** Move right by count cells (default 1) */
26
36
  right(count?: number): XlsxCellSB;
27
37
  /** Move left by count cells (default 1) */
@@ -39,21 +49,38 @@ export declare class XlsxCellSB {
39
49
  /**
40
50
  * Iterate over cells from this position in a direction while the predicate matches.
41
51
  * Returns an ArraySpec whose items are produced by the itemMapper.
52
+ * The mapper can return a Spec, a FluentSpec, or a plain record (auto-wrapped to ObjectSpec).
42
53
  */
43
54
  eachCell<T extends Spec>(direction: Direction, while_: CellPred, itemMapper: (cell: XlsxCellSB) => T): ArraySpec<T>;
55
+ eachCell(direction: Direction, while_: CellPred, itemMapper: (cell: XlsxCellSB) => Record<string, SpecLike>): ArraySpec<ObjectSpec>;
44
56
  /**
45
57
  * Iterate over slices (rows or columns) from this cell position.
46
58
  * Each slice is passed as an XlsxSliceSB to the mapper, providing
47
59
  * header-relative column/row access via colWhere/rowWhere.
60
+ * The mapper can return a Spec, a FluentSpec, or a plain record (auto-wrapped to ObjectSpec).
48
61
  */
49
62
  eachSlice<T extends Spec>(direction: Direction, while_: RowPred, itemMapper: (slice: XlsxSliceSB) => T): ArraySpec<T>;
63
+ eachSlice(direction: Direction, while_: RowPred, itemMapper: (slice: XlsxSliceSB) => Record<string, SpecLike>): ArraySpec<ObjectSpec>;
50
64
  }
65
+ /**
66
+ * XLSX extract type for column definitions.
67
+ */
68
+ export type XlsxExtractType = 'string' | 'number' | 'boolean' | 'date' | 'value' | 'formula';
69
+ /**
70
+ * Column definition for XLSX `columns()` shorthand.
71
+ *
72
+ * - `CellPred` — `colWhere(header, pred).string()`
73
+ * - `[CellPred, XlsxExtractType]` — `colWhere(header, pred).{type}()`
74
+ * - `[number, XlsxExtractType?]` — `col(offset).{type ?? 'string'}()`
75
+ * - `SpecLike` — passthrough (any Spec or FluentSpec)
76
+ */
77
+ export type XlsxColumnDef = CellPred | [CellPred, XlsxExtractType] | [number, XlsxExtractType?] | SpecLike;
51
78
  /**
52
79
  * Spec builder for items within a slice (row or column section).
53
80
  * Provides header-relative column/row access.
54
81
  */
55
- export declare class XlsxSliceSB {
56
- private readonly steps;
82
+ export declare class XlsxSliceSB extends StepBuilder<XlsxStep, 'xlsx'> {
83
+ protected get format(): "xlsx";
57
84
  /** @internal */
58
85
  constructor(steps: readonly XlsxStep[]);
59
86
  /** Access a column by matching its header cell against a predicate (header-relative) */
@@ -64,13 +91,29 @@ export declare class XlsxSliceSB {
64
91
  col(offset: number): XlsxCellSB;
65
92
  /** Access a row by 0-based offset from the anchor (for rotated tables) */
66
93
  row(offset: number): XlsxCellSB;
94
+ /**
95
+ * Extract multiple columns at once, producing an ObjectSpec.
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * row.columns(header, {
100
+ * name: cell.equals('Investment'), // colWhere(header, pred).string()
101
+ * amount: [cell.contains('Amount'), 'number'], // colWhere(header, pred).number()
102
+ * note: [2, 'string'], // col(2).string()
103
+ * custom: fluent(row.col(0).string()).map(v => v.trim(), 'trim'), // passthrough
104
+ * })
105
+ * ```
106
+ */
107
+ columns(headerRef: XlsxCellSB, mapping: Record<string, XlsxColumnDef>): ObjectSpec;
108
+ /** @internal Resolve a single column definition to a Spec */
109
+ private _resolveColumnDef;
67
110
  }
68
111
  /**
69
112
  * Spec builder for items within a row (receives an XlsxRange representing one row).
70
113
  * Provides column access to navigate within the row.
71
114
  */
72
- export declare class XlsxRowItemSB {
73
- private readonly steps;
115
+ export declare class XlsxRowItemSB extends StepBuilder<XlsxStep, 'xlsx'> {
116
+ protected get format(): "xlsx";
74
117
  /** @internal */
75
118
  constructor(steps: readonly XlsxStep[]);
76
119
  /** Access a column by 1-based index within the row */
@@ -81,8 +124,8 @@ export declare class XlsxRowItemSB {
81
124
  /**
82
125
  * Spec builder at the range level — cell navigation, rows, and cells collection.
83
126
  */
84
- export declare class XlsxRangeSB {
85
- private readonly steps;
127
+ export declare class XlsxRangeSB extends StepBuilder<XlsxStep, 'xlsx'> {
128
+ protected get format(): "xlsx";
86
129
  /** @internal */
87
130
  constructor(steps: readonly XlsxStep[]);
88
131
  /** Navigate to a cell by address within the range */
@@ -105,8 +148,8 @@ export declare class XlsxRangeSB {
105
148
  /**
106
149
  * Spec builder at the row level.
107
150
  */
108
- export declare class XlsxRowSB {
109
- private readonly steps;
151
+ export declare class XlsxRowSB extends StepBuilder<XlsxStep, 'xlsx'> {
152
+ protected get format(): "xlsx";
110
153
  /** @internal */
111
154
  constructor(steps: readonly XlsxStep[]);
112
155
  /** Navigate to a cell by address within this row */
@@ -117,8 +160,8 @@ export declare class XlsxRowSB {
117
160
  /**
118
161
  * Spec builder at the column level.
119
162
  */
120
- export declare class XlsxColSB {
121
- private readonly steps;
163
+ export declare class XlsxColSB extends StepBuilder<XlsxStep, 'xlsx'> {
164
+ protected get format(): "xlsx";
122
165
  /** @internal */
123
166
  constructor(steps: readonly XlsxStep[]);
124
167
  /** Navigate to a cell by address within this column */
@@ -129,8 +172,8 @@ export declare class XlsxColSB {
129
172
  /**
130
173
  * Spec builder at the sheet level — cell, range, row, column, and search navigation.
131
174
  */
132
- export declare class XlsxSheetSB {
133
- private readonly steps;
175
+ export declare class XlsxSheetSB extends StepBuilder<XlsxStep, 'xlsx'> {
176
+ protected get format(): "xlsx";
134
177
  /** @internal */
135
178
  constructor(steps: readonly XlsxStep[]);
136
179
  /** Navigate to a cell by address (e.g., "A1", "B2") */
@@ -161,8 +204,8 @@ export declare class XlsxSheetSB {
161
204
  *
162
205
  * Used as `$` in `emit()` after `.mapIn(parseXlsx())`.
163
206
  */
164
- export declare class XlsxSpecBuilder {
165
- private readonly steps;
207
+ export declare class XlsxSpecBuilder extends StepBuilder<XlsxStep, 'xlsx'> {
208
+ protected get format(): "xlsx";
166
209
  private constructor();
167
210
  /** Create a root spec builder */
168
211
  static root(): XlsxSpecBuilder;
@@ -1,4 +1,4 @@
1
- import { TYPE_LABEL, ExtractionResult } from '@origints/core';
1
+ import { TYPE_LABEL, ExtractionResult, SpecScope } from '@origints/core';
2
2
  import { XlsxSpec } from './xlsx-spec';
3
3
  import { XlsxWorkbook } from './xlsx-workbook';
4
4
  import { XlsxSheet } from './xlsx-sheet';
@@ -22,4 +22,4 @@ export declare class XlsxSliceContext {
22
22
  * - XlsxRange → starts at range level (used for row items)
23
23
  * - XlsxCell → starts at cell level (used for cell items)
24
24
  */
25
- export declare function executeXlsxSpec(spec: XlsxSpec, data: unknown): ExtractionResult<unknown>;
25
+ export declare function executeXlsxSpec(spec: XlsxSpec, data: unknown, scope?: SpecScope): ExtractionResult<unknown>;
@@ -1,5 +1,4 @@
1
1
  import { TYPE_LABEL } from '@origints/core';
2
- import { Workbook } from 'exceljs';
3
2
  import { XlsxSheet } from './xlsx-sheet';
4
3
  import { XlsxResult, XlsxPath } from './xlsx-result';
5
4
  /**
@@ -18,22 +17,23 @@ export interface WorkbookProperties {
18
17
  readonly modified?: Date;
19
18
  }
20
19
  /**
21
- * A wrapper around ExcelJS Workbook with navigation and query capabilities.
20
+ * A wrapper around workbook data with navigation and query capabilities.
22
21
  *
23
22
  * XlsxWorkbook provides access to sheets, workbook properties, and
24
23
  * supports predicate-based queries for finding sheets.
25
24
  */
26
25
  export declare class XlsxWorkbook {
27
- private readonly workbook;
28
26
  private readonly _path;
29
27
  get [TYPE_LABEL](): string;
30
- private readonly sheetsCache;
28
+ private readonly _sheets;
29
+ private readonly _sheetsByName;
30
+ private readonly _props;
31
31
  private constructor();
32
32
  /**
33
- * Creates an XlsxWorkbook from an ExcelJS workbook.
33
+ * Creates an XlsxWorkbook from internal data.
34
34
  * @internal
35
35
  */
36
- static fromExcelJS(workbook: Workbook, file?: string): XlsxWorkbook;
36
+ static create(sheets: XlsxSheet[], props: WorkbookProperties, file?: string): XlsxWorkbook;
37
37
  /**
38
38
  * Returns the path for lineage tracking.
39
39
  */
@@ -122,9 +122,4 @@ export declare class XlsxWorkbook {
122
122
  * Map over all sheets.
123
123
  */
124
124
  map<T>(fn: (sheet: XlsxSheet, index: number) => T): T[];
125
- /**
126
- * Get the underlying ExcelJS workbook.
127
- * @internal
128
- */
129
- getWorkbook(): Workbook;
130
125
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@origints/xlsx",
3
- "version": "0.2.0",
3
+ "version": "0.3.2",
4
4
  "description": "XLSX parsing for Origins with full traceability to sheets, ranges, and cells.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -20,6 +20,13 @@
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"
29
+ },
23
30
  "repository": {
24
31
  "type": "git",
25
32
  "url": "https://github.com/fponticelli/origints.git",
@@ -40,26 +47,20 @@
40
47
  "node": ">=18"
41
48
  },
42
49
  "dependencies": {
43
- "exceljs": "^4.4.0"
50
+ "xlsx": "^0.18.5"
44
51
  },
45
52
  "peerDependencies": {
46
- "@origints/core": "^0.2.0"
53
+ "@origints/core": "^0.3.0"
47
54
  },
48
55
  "devDependencies": {
56
+ "@origints/core": "workspace:*",
57
+ "exceljs": "^4.4.0",
49
58
  "@types/node": "25.0.6",
50
59
  "@vitest/coverage-v8": "^4.0.16",
51
60
  "eslint": "9.39.2",
52
61
  "typescript": "5.9.3",
53
62
  "vite": "7.3.1",
54
63
  "vite-plugin-dts": "4.5.4",
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"
64
+ "vitest": "4.0.16"
64
65
  }
65
- }
66
+ }