@niicojs/excel 0.3.2 → 0.3.4

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 CHANGED
@@ -434,8 +434,8 @@ const formatCellValue = (value, style, locale)=>{
434
434
  return null;
435
435
  };
436
436
 
437
- // Excel epoch: December 30, 1899 (accounting for the 1900 leap year bug)
438
- const EXCEL_EPOCH = new Date(Date.UTC(1899, 11, 30));
437
+ // Excel epoch: December 31, 1899 (accounting for the 1900 leap year bug)
438
+ const EXCEL_EPOCH = new Date(Date.UTC(1899, 11, 31));
439
439
  const MS_PER_DAY = 24 * 60 * 60 * 1000;
440
440
  // Excel error types
441
441
  const ERROR_TYPES = new Set([
@@ -712,8 +712,8 @@ const ERROR_TYPES = new Set([
712
712
  */ _excelDateToJs(serial) {
713
713
  // Excel incorrectly considers 1900 a leap year
714
714
  // Dates after Feb 28, 1900 need adjustment
715
- const adjusted = serial > 60 ? serial - 1 : serial;
716
- const ms = Math.round((adjusted - 1) * MS_PER_DAY);
715
+ const adjusted = serial >= 60 ? serial - 1 : serial;
716
+ const ms = Math.round(adjusted * MS_PER_DAY);
717
717
  return new Date(EXCEL_EPOCH.getTime() + ms);
718
718
  }
719
719
  /**
@@ -721,9 +721,9 @@ const ERROR_TYPES = new Set([
721
721
  * Used when writing dates as numbers for Excel compatibility
722
722
  */ _jsDateToExcel(date) {
723
723
  const ms = date.getTime() - EXCEL_EPOCH.getTime();
724
- let serial = ms / MS_PER_DAY + 1;
724
+ let serial = ms / MS_PER_DAY;
725
725
  // Account for Excel's 1900 leap year bug
726
- if (serial > 60) {
726
+ if (serial >= 60) {
727
727
  serial += 1;
728
728
  }
729
729
  return serial;
@@ -940,12 +940,18 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
940
940
  if (attrs && Object.keys(attrs).length > 0) {
941
941
  const attrObj = {};
942
942
  for (const [key, value] of Object.entries(attrs)){
943
- attrObj[`@_${key}`] = value;
943
+ attrObj[`@_${key}`] = shouldEscapeXmlAttr(tagName, key) ? escapeXmlAttr(value) : value;
944
944
  }
945
945
  node[':@'] = attrObj;
946
946
  }
947
947
  return node;
948
948
  };
949
+ const escapeXmlAttr = (value)=>{
950
+ return value.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/&apos;/g, "'");
951
+ };
952
+ const shouldEscapeXmlAttr = (tagName, attrName)=>{
953
+ return tagName === 's' && attrName === 'v';
954
+ };
949
955
  /**
950
956
  * Creates a text node
951
957
  */ const createText = (text)=>{
@@ -2200,6 +2206,11 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
2200
2206
  return Math.max(this._totalCount, this.entries.length);
2201
2207
  }
2202
2208
  /**
2209
+ * Get all unique shared strings in insertion order.
2210
+ */ getAllStrings() {
2211
+ return this.entries.map((entry)=>entry.text);
2212
+ }
2213
+ /**
2203
2214
  * Generate XML for the shared strings table
2204
2215
  */ toXml() {
2205
2216
  const siElements = [];
@@ -3321,7 +3332,6 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
3321
3332
  baseItem: '0',
3322
3333
  subtotal: f.aggregation || 'sum'
3323
3334
  };
3324
- // Add numFmtId if it was resolved during addValueField
3325
3335
  if (f.numFmtId !== undefined) {
3326
3336
  attrs.numFmtId = String(f.numFmtId);
3327
3337
  }
@@ -3331,8 +3341,6 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
3331
3341
  count: String(dataFieldNodes.length)
3332
3342
  }, dataFieldNodes));
3333
3343
  }
3334
- // Check if any value field has a number format
3335
- const hasNumberFormats = this._valueFields.some((f)=>f.numFmtId !== undefined);
3336
3344
  // Pivot table style
3337
3345
  children.push(createElement('pivotTableStyleInfo', {
3338
3346
  name: 'PivotStyleMedium9',
@@ -3347,7 +3355,7 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
3347
3355
  'xmlns:r': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships',
3348
3356
  name: this._name,
3349
3357
  cacheId: String(this._cache.cacheId),
3350
- applyNumberFormats: hasNumberFormats ? '1' : '0',
3358
+ applyNumberFormats: '1',
3351
3359
  applyBorderFormats: '0',
3352
3360
  applyFontFormats: '0',
3353
3361
  applyPatternFormats: '0',
@@ -3607,15 +3615,24 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
3607
3615
  this._fields = [];
3608
3616
  this._records = [];
3609
3617
  this._recordCount = 0;
3618
+ this._saveData = true;
3610
3619
  this._refreshOnLoad = true; // Default to true
3611
3620
  // Optimized lookup: Map<fieldIndex, Map<stringValue, sharedItemsIndex>>
3612
3621
  this._sharedItemsIndexMap = new Map();
3622
+ this._blankItemIndexMap = new Map();
3623
+ this._styles = null;
3613
3624
  this._cacheId = cacheId;
3614
3625
  this._fileIndex = fileIndex;
3615
3626
  this._sourceSheet = sourceSheet;
3616
3627
  this._sourceRange = sourceRange;
3617
3628
  }
3618
3629
  /**
3630
+ * Set styles reference for number format resolution.
3631
+ * @internal
3632
+ */ setStyles(styles) {
3633
+ this._styles = styles;
3634
+ }
3635
+ /**
3619
3636
  * Get the cache ID
3620
3637
  */ get cacheId() {
3621
3638
  return this._cacheId;
@@ -3631,11 +3648,21 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
3631
3648
  this._refreshOnLoad = value;
3632
3649
  }
3633
3650
  /**
3651
+ * Set saveData option
3652
+ */ set saveData(value) {
3653
+ this._saveData = value;
3654
+ }
3655
+ /**
3634
3656
  * Get refreshOnLoad option
3635
3657
  */ get refreshOnLoad() {
3636
3658
  return this._refreshOnLoad;
3637
3659
  }
3638
3660
  /**
3661
+ * Get saveData option
3662
+ */ get saveData() {
3663
+ return this._saveData;
3664
+ }
3665
+ /**
3639
3666
  * Get the source sheet name
3640
3667
  */ get sourceSheet() {
3641
3668
  return this._sourceSheet;
@@ -3672,46 +3699,81 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
3672
3699
  index,
3673
3700
  isNumeric: true,
3674
3701
  isDate: false,
3702
+ hasBoolean: false,
3703
+ hasBlank: false,
3704
+ numFmtId: undefined,
3675
3705
  sharedItems: [],
3676
3706
  minValue: undefined,
3677
- maxValue: undefined
3707
+ maxValue: undefined,
3708
+ minDate: undefined,
3709
+ maxDate: undefined
3678
3710
  }));
3679
- // Use Sets for O(1) unique value collection during analysis
3680
- const sharedItemsSets = this._fields.map(()=>new Set());
3711
+ // Use Maps for unique value collection during analysis
3712
+ const sharedItemsMaps = this._fields.map(()=>new Map());
3681
3713
  // Analyze data to determine field types and collect unique values
3682
3714
  for (const row of data){
3683
3715
  for(let colIdx = 0; colIdx < row.length && colIdx < this._fields.length; colIdx++){
3684
3716
  const value = row[colIdx];
3685
3717
  const field = this._fields[colIdx];
3686
3718
  if (value === null || value === undefined) {
3719
+ field.hasBlank = true;
3687
3720
  continue;
3688
3721
  }
3689
3722
  if (typeof value === 'string') {
3690
3723
  field.isNumeric = false;
3691
- // O(1) Set.add instead of O(n) Array.includes + push
3692
- sharedItemsSets[colIdx].add(value);
3693
- } else if (typeof value === 'number') {
3694
- if (field.minValue === undefined || value < field.minValue) {
3695
- field.minValue = value;
3724
+ const map = sharedItemsMaps[colIdx];
3725
+ if (!map.has(value)) {
3726
+ map.set(value, value);
3696
3727
  }
3697
- if (field.maxValue === undefined || value > field.maxValue) {
3698
- field.maxValue = value;
3728
+ } else if (typeof value === 'number') {
3729
+ if (field.isDate) {
3730
+ const d = this._excelSerialToDate(value);
3731
+ if (!field.minDate || d < field.minDate) {
3732
+ field.minDate = d;
3733
+ }
3734
+ if (!field.maxDate || d > field.maxDate) {
3735
+ field.maxDate = d;
3736
+ }
3737
+ } else {
3738
+ if (field.minValue === undefined || value < field.minValue) {
3739
+ field.minValue = value;
3740
+ }
3741
+ if (field.maxValue === undefined || value > field.maxValue) {
3742
+ field.maxValue = value;
3743
+ }
3699
3744
  }
3700
3745
  } else if (value instanceof Date) {
3701
3746
  field.isDate = true;
3702
3747
  field.isNumeric = false;
3748
+ if (!field.minDate || value < field.minDate) {
3749
+ field.minDate = value;
3750
+ }
3751
+ if (!field.maxDate || value > field.maxDate) {
3752
+ field.maxDate = value;
3753
+ }
3703
3754
  } else if (typeof value === 'boolean') {
3704
3755
  field.isNumeric = false;
3756
+ field.hasBoolean = true;
3757
+ }
3758
+ }
3759
+ }
3760
+ // Resolve number formats if styles are available
3761
+ if (this._styles) {
3762
+ const dateFmtId = this._styles.getOrCreateNumFmtId('mm-dd-yy');
3763
+ for (const field of this._fields){
3764
+ if (field.isDate) {
3765
+ field.numFmtId = dateFmtId;
3705
3766
  }
3706
3767
  }
3707
3768
  }
3708
3769
  // Convert Sets to arrays and build reverse index Maps for O(1) lookup during XML generation
3709
3770
  this._sharedItemsIndexMap.clear();
3771
+ this._blankItemIndexMap.clear();
3710
3772
  for(let colIdx = 0; colIdx < this._fields.length; colIdx++){
3711
3773
  const field = this._fields[colIdx];
3712
- const set = sharedItemsSets[colIdx];
3713
- // Convert Set to array (maintains insertion order in ES6+)
3714
- field.sharedItems = Array.from(set);
3774
+ const map = sharedItemsMaps[colIdx];
3775
+ // Convert Map values to array (maintains insertion order in ES6+)
3776
+ field.sharedItems = Array.from(map.values());
3715
3777
  // Build reverse lookup Map: value -> index
3716
3778
  if (field.sharedItems.length > 0) {
3717
3779
  const indexMap = new Map();
@@ -3719,6 +3781,10 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
3719
3781
  indexMap.set(field.sharedItems[i], i);
3720
3782
  }
3721
3783
  this._sharedItemsIndexMap.set(colIdx, indexMap);
3784
+ if (field.hasBlank) {
3785
+ const blankIndex = field.sharedItems.length;
3786
+ this._blankItemIndexMap.set(colIdx, blankIndex);
3787
+ }
3722
3788
  }
3723
3789
  }
3724
3790
  // Store records
@@ -3742,33 +3808,78 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
3742
3808
  const sharedItemsAttrs = {};
3743
3809
  const sharedItemChildren = [];
3744
3810
  if (field.sharedItems.length > 0) {
3745
- // String field with shared items - Excel just uses count attribute
3746
- sharedItemsAttrs.count = String(field.sharedItems.length);
3811
+ // String field with shared items
3812
+ const total = field.hasBlank ? field.sharedItems.length + 1 : field.sharedItems.length;
3813
+ sharedItemsAttrs.count = String(total);
3814
+ sharedItemsAttrs.containsString = '1';
3815
+ if (field.hasBlank) {
3816
+ sharedItemsAttrs.containsBlank = '1';
3817
+ }
3747
3818
  for (const item of field.sharedItems){
3748
3819
  sharedItemChildren.push(createElement('s', {
3749
3820
  v: item
3750
3821
  }, []));
3751
3822
  }
3823
+ if (field.hasBlank) {
3824
+ sharedItemChildren.push(createElement('m', {}, []));
3825
+ }
3826
+ } else if (field.isDate) {
3827
+ sharedItemsAttrs.containsSemiMixedTypes = '0';
3828
+ sharedItemsAttrs.containsString = '0';
3829
+ sharedItemsAttrs.containsDate = '1';
3830
+ sharedItemsAttrs.containsNonDate = '0';
3831
+ if (field.hasBlank) {
3832
+ sharedItemsAttrs.containsBlank = '1';
3833
+ }
3834
+ if (field.minDate) {
3835
+ sharedItemsAttrs.minDate = this._formatDate(field.minDate);
3836
+ }
3837
+ if (field.maxDate) {
3838
+ const maxDate = new Date(field.maxDate.getTime() + 24 * 60 * 60 * 1000);
3839
+ sharedItemsAttrs.maxDate = this._formatDate(maxDate);
3840
+ }
3752
3841
  } else if (field.isNumeric) {
3753
3842
  // Numeric field - use "0"/"1" for boolean attributes as Excel expects
3754
3843
  sharedItemsAttrs.containsSemiMixedTypes = '0';
3755
3844
  sharedItemsAttrs.containsString = '0';
3756
3845
  sharedItemsAttrs.containsNumber = '1';
3846
+ if (field.hasBlank) {
3847
+ sharedItemsAttrs.containsBlank = '1';
3848
+ }
3757
3849
  // Check if all values are integers
3758
3850
  if (field.minValue !== undefined && field.maxValue !== undefined) {
3759
3851
  const isInteger = Number.isInteger(field.minValue) && Number.isInteger(field.maxValue);
3760
3852
  if (isInteger) {
3761
3853
  sharedItemsAttrs.containsInteger = '1';
3762
3854
  }
3763
- sharedItemsAttrs.minValue = String(field.minValue);
3764
- sharedItemsAttrs.maxValue = String(field.maxValue);
3855
+ sharedItemsAttrs.minValue = this._formatNumber(field.minValue);
3856
+ sharedItemsAttrs.maxValue = this._formatNumber(field.maxValue);
3765
3857
  }
3858
+ } else if (field.hasBoolean) {
3859
+ // Boolean-only field (no strings, no numbers)
3860
+ if (field.hasBlank) {
3861
+ sharedItemsAttrs.containsBlank = '1';
3862
+ }
3863
+ sharedItemsAttrs.count = field.hasBlank ? '3' : '2';
3864
+ sharedItemChildren.push(createElement('b', {
3865
+ v: '0'
3866
+ }, []));
3867
+ sharedItemChildren.push(createElement('b', {
3868
+ v: '1'
3869
+ }, []));
3870
+ if (field.hasBlank) {
3871
+ sharedItemChildren.push(createElement('m', {}, []));
3872
+ }
3873
+ } else if (field.hasBlank) {
3874
+ // Field that only contains blanks
3875
+ sharedItemsAttrs.containsBlank = '1';
3766
3876
  }
3767
3877
  const sharedItemsNode = createElement('sharedItems', sharedItemsAttrs, sharedItemChildren);
3768
- return createElement('cacheField', {
3878
+ const cacheFieldAttrs = {
3769
3879
  name: field.name,
3770
- numFmtId: '0'
3771
- }, [
3880
+ numFmtId: String(field.numFmtId ?? 0)
3881
+ };
3882
+ return createElement('cacheField', cacheFieldAttrs, [
3772
3883
  sharedItemsNode
3773
3884
  ]);
3774
3885
  });
@@ -3784,22 +3895,25 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
3784
3895
  }, [
3785
3896
  worksheetSourceNode
3786
3897
  ]);
3787
- // Build attributes - refreshOnLoad should come early per OOXML schema
3898
+ // Build attributes - align with Excel expectations
3788
3899
  const definitionAttrs = {
3789
3900
  xmlns: 'http://schemas.openxmlformats.org/spreadsheetml/2006/main',
3790
3901
  'xmlns:r': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships',
3791
3902
  'r:id': recordsRelId
3792
3903
  };
3793
- // Add refreshOnLoad early in attributes (default is true)
3794
3904
  if (this._refreshOnLoad) {
3795
3905
  definitionAttrs.refreshOnLoad = '1';
3796
3906
  }
3797
- // Continue with remaining attributes
3798
3907
  definitionAttrs.refreshedBy = 'User';
3799
3908
  definitionAttrs.refreshedVersion = '8';
3800
3909
  definitionAttrs.minRefreshableVersion = '3';
3801
3910
  definitionAttrs.createdVersion = '8';
3802
- definitionAttrs.recordCount = String(this._recordCount);
3911
+ if (!this._saveData) {
3912
+ definitionAttrs.saveData = '0';
3913
+ definitionAttrs.recordCount = '0';
3914
+ } else {
3915
+ definitionAttrs.recordCount = String(this._recordCount);
3916
+ }
3803
3917
  const definitionNode = createElement('pivotCacheDefinition', definitionAttrs, [
3804
3918
  cacheSourceNode,
3805
3919
  cacheFieldsNode
@@ -3818,7 +3932,14 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
3818
3932
  const value = colIdx < row.length ? row[colIdx] : null;
3819
3933
  if (value === null || value === undefined) {
3820
3934
  // Missing value
3821
- fieldNodes.push(createElement('m', {}, []));
3935
+ const blankIndex = this._blankItemIndexMap.get(colIdx);
3936
+ if (blankIndex !== undefined) {
3937
+ fieldNodes.push(createElement('x', {
3938
+ v: String(blankIndex)
3939
+ }, []));
3940
+ } else {
3941
+ fieldNodes.push(createElement('m', {}, []));
3942
+ }
3822
3943
  } else if (typeof value === 'string') {
3823
3944
  // String value - use index into sharedItems via O(1) Map lookup
3824
3945
  const indexMap = this._sharedItemsIndexMap.get(colIdx);
@@ -3834,16 +3955,23 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
3834
3955
  }, []));
3835
3956
  }
3836
3957
  } else if (typeof value === 'number') {
3837
- fieldNodes.push(createElement('n', {
3838
- v: String(value)
3839
- }, []));
3958
+ if (this._fields[colIdx]?.isDate) {
3959
+ const d = this._excelSerialToDate(value);
3960
+ fieldNodes.push(createElement('d', {
3961
+ v: this._formatDate(d)
3962
+ }, []));
3963
+ } else {
3964
+ fieldNodes.push(createElement('n', {
3965
+ v: String(value)
3966
+ }, []));
3967
+ }
3840
3968
  } else if (typeof value === 'boolean') {
3841
3969
  fieldNodes.push(createElement('b', {
3842
3970
  v: value ? '1' : '0'
3843
3971
  }, []));
3844
3972
  } else if (value instanceof Date) {
3845
3973
  fieldNodes.push(createElement('d', {
3846
- v: value.toISOString()
3974
+ v: this._formatDate(value)
3847
3975
  }, []));
3848
3976
  } else {
3849
3977
  // Unknown type, treat as missing
@@ -3861,6 +3989,26 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
3861
3989
  recordsNode
3862
3990
  ])}`;
3863
3991
  }
3992
+ _formatDate(value) {
3993
+ return value.toISOString().replace(/\.\d{3}Z$/, '');
3994
+ }
3995
+ _formatNumber(value) {
3996
+ if (Number.isInteger(value)) {
3997
+ return String(value);
3998
+ }
3999
+ if (Math.abs(value) >= 1000000) {
4000
+ return value.toFixed(16).replace(/0+$/, '').replace(/\.$/, '');
4001
+ }
4002
+ return String(value);
4003
+ }
4004
+ _excelSerialToDate(serial) {
4005
+ // Excel epoch: December 31, 1899
4006
+ const EXCEL_EPOCH = Date.UTC(1899, 11, 31);
4007
+ const MS_PER_DAY = 24 * 60 * 60 * 1000;
4008
+ const adjusted = serial >= 60 ? serial - 1 : serial;
4009
+ const ms = Math.round(adjusted * MS_PER_DAY);
4010
+ return new Date(EXCEL_EPOCH + ms);
4011
+ }
3864
4012
  }
3865
4013
 
3866
4014
  /**
@@ -3868,6 +4016,19 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
3868
4016
  * @param data - ZIP file as Uint8Array
3869
4017
  * @returns Promise resolving to a map of file paths to contents
3870
4018
  */ const readZip = (data)=>{
4019
+ const isBun = typeof globalThis.Bun !== 'undefined';
4020
+ if (isBun) {
4021
+ try {
4022
+ const result = fflate.unzipSync(data);
4023
+ const files = new Map();
4024
+ for (const [path, content] of Object.entries(result)){
4025
+ files.set(path, content);
4026
+ }
4027
+ return Promise.resolve(files);
4028
+ } catch (error) {
4029
+ return Promise.reject(error);
4030
+ }
4031
+ }
3871
4032
  return new Promise((resolve, reject)=>{
3872
4033
  fflate.unzip(data, (err, result)=>{
3873
4034
  if (err) {
@@ -3887,11 +4048,19 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
3887
4048
  * @param files - Map of file paths to contents
3888
4049
  * @returns Promise resolving to ZIP file as Uint8Array
3889
4050
  */ const writeZip = (files)=>{
3890
- return new Promise((resolve, reject)=>{
3891
- const zipData = {};
3892
- for (const [path, content] of files){
3893
- zipData[path] = content;
4051
+ const zipData = {};
4052
+ for (const [path, content] of files){
4053
+ zipData[path] = content;
4054
+ }
4055
+ const isBun = typeof globalThis.Bun !== 'undefined';
4056
+ if (isBun) {
4057
+ try {
4058
+ return Promise.resolve(fflate.zipSync(zipData));
4059
+ } catch (error) {
4060
+ return Promise.reject(error);
3894
4061
  }
4062
+ }
4063
+ return new Promise((resolve, reject)=>{
3895
4064
  fflate.zip(zipData, (err, result)=>{
3896
4065
  if (err) {
3897
4066
  reject(err);
@@ -3926,7 +4095,7 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
3926
4095
  // Pivot table support
3927
4096
  this._pivotTables = [];
3928
4097
  this._pivotCaches = [];
3929
- this._nextCacheId = 0;
4098
+ this._nextCacheId = 5;
3930
4099
  this._nextCacheFileIndex = 1;
3931
4100
  // Table support
3932
4101
  this._nextTableId = 1;
@@ -4395,11 +4564,16 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
4395
4564
  const cacheId = this._nextCacheId++;
4396
4565
  const cacheFileIndex = this._nextCacheFileIndex++;
4397
4566
  const cache = new PivotCache(cacheId, sourceSheet, sourceRange, cacheFileIndex);
4567
+ cache.setStyles(this._styles);
4398
4568
  cache.buildFromData(headers, data);
4399
4569
  // refreshOnLoad defaults to true; only disable if explicitly set to false
4400
4570
  if (config.refreshOnLoad === false) {
4401
4571
  cache.refreshOnLoad = false;
4402
4572
  }
4573
+ // saveData defaults to true; only disable if explicitly set to false
4574
+ if (config.saveData === false) {
4575
+ cache.saveData = false;
4576
+ }
4403
4577
  this._pivotCaches.push(cache);
4404
4578
  // Create pivot table
4405
4579
  const pivotTableIndex = this._pivotTables.length + 1;
package/dist/index.d.cts CHANGED
@@ -133,6 +133,8 @@ interface PivotTableConfig {
133
133
  target: string;
134
134
  /** Refresh the pivot table data when the file is opened (default: true) */
135
135
  refreshOnLoad?: boolean;
136
+ /** Save pivot cache data in the file (default: true) */
137
+ saveData?: boolean;
136
138
  }
137
139
  /**
138
140
  * Internal representation of a pivot cache field
@@ -146,12 +148,22 @@ interface PivotCacheField {
146
148
  isNumeric: boolean;
147
149
  /** Whether this field contains dates */
148
150
  isDate: boolean;
151
+ /** Whether this field contains boolean values */
152
+ hasBoolean: boolean;
153
+ /** Whether this field contains blank (null/undefined) values */
154
+ hasBlank: boolean;
155
+ /** Number format ID for this field (cache field numFmtId) */
156
+ numFmtId?: number;
149
157
  /** Unique string values (for shared items) */
150
158
  sharedItems: string[];
151
159
  /** Min numeric value */
152
160
  minValue?: number;
153
161
  /** Max numeric value */
154
162
  maxValue?: number;
163
+ /** Min date value (for date fields) */
164
+ minDate?: Date;
165
+ /** Max date value (for date fields) */
166
+ maxDate?: Date;
155
167
  }
156
168
  /**
157
169
  * Pivot field axis assignment
@@ -780,6 +792,10 @@ declare class SharedStrings {
780
792
  * Get total usage count of shared strings
781
793
  */
782
794
  get totalCount(): number;
795
+ /**
796
+ * Get all unique shared strings in insertion order.
797
+ */
798
+ getAllStrings(): string[];
783
799
  /**
784
800
  * Generate XML for the shared strings table
785
801
  */
@@ -870,9 +886,17 @@ declare class PivotCache {
870
886
  private _fields;
871
887
  private _records;
872
888
  private _recordCount;
889
+ private _saveData;
873
890
  private _refreshOnLoad;
874
891
  private _sharedItemsIndexMap;
892
+ private _blankItemIndexMap;
893
+ private _styles;
875
894
  constructor(cacheId: number, sourceSheet: string, sourceRange: string, fileIndex: number);
895
+ /**
896
+ * Set styles reference for number format resolution.
897
+ * @internal
898
+ */
899
+ setStyles(styles: Styles): void;
876
900
  /**
877
901
  * Get the cache ID
878
902
  */
@@ -885,10 +909,18 @@ declare class PivotCache {
885
909
  * Set refreshOnLoad option
886
910
  */
887
911
  set refreshOnLoad(value: boolean);
912
+ /**
913
+ * Set saveData option
914
+ */
915
+ set saveData(value: boolean);
888
916
  /**
889
917
  * Get refreshOnLoad option
890
918
  */
891
919
  get refreshOnLoad(): boolean;
920
+ /**
921
+ * Get saveData option
922
+ */
923
+ get saveData(): boolean;
892
924
  /**
893
925
  * Get the source sheet name
894
926
  */
@@ -931,6 +963,9 @@ declare class PivotCache {
931
963
  * Generate the pivotCacheRecords XML
932
964
  */
933
965
  toRecordsXml(): string;
966
+ private _formatDate;
967
+ private _formatNumber;
968
+ private _excelSerialToDate;
934
969
  }
935
970
 
936
971
  /**