@rowsncolumns/utils 9.2.35 → 9.2.37

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.
@@ -17,6 +17,12 @@ export declare const isDatePattern: (pattern: string | undefined | null) => bool
17
17
  export declare const isDateTimePattern: (pattern: string | undefined | null) => boolean;
18
18
  export declare const isTimePattern: (pattern: string | undefined | null) => boolean;
19
19
  export declare const isTextPattern: (pattern: string | undefined | null) => boolean;
20
+ /**
21
+ * Does the pattern spell a month name (`mmm`+) with no day-of-month field
22
+ * (`d`/`dd`) beside it? Quoted literals, bracket directives and escaped
23
+ * characters are skipped; `ddd`/`dddd` are weekday names, not a day.
24
+ */
25
+ export declare const isStandaloneMonthPattern: (pattern: string) => boolean;
20
26
  export declare const ssfFormat: (pattern: string, value: any, locale?: string) => string;
21
27
  export declare const ssfFormatColor: (pattern: string, value: any) => string | undefined;
22
28
  //# sourceMappingURL=formatter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../../formatter.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OASlE,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KACjC,OAWF,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OAWlE,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OAOlE,CAAC;AAMF,eAAO,MAAM,SAAS,GACpB,SAAS,MAAM,EACf,OAAO,GAAG,EACV,SAAS,MAAM,KACd,MAeF,CAAC;AAmLF,eAAO,MAAM,cAAc,GACzB,SAAS,MAAM,EACf,OAAO,GAAG,KACT,MAAM,GAAG,SAoBX,CAAC"}
1
+ {"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../../formatter.ts"],"names":[],"mappings":"AAYA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OASlE,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KACjC,OAWF,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OAWlE,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OAOlE,CAAC;AAMF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GAAI,SAAS,MAAM,KAAG,OA4B1D,CAAC;AA8EF,eAAO,MAAM,SAAS,GACpB,SAAS,MAAM,EACf,OAAO,GAAG,EACV,SAAS,MAAM,KACd,MAeF,CAAC;AAmLF,eAAO,MAAM,cAAc,GACzB,SAAS,MAAM,EACf,OAAO,GAAG,KACT,MAAM,GAAG,SAoBX,CAAC"}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ssfFormatColor = exports.ssfFormat = exports.isTextPattern = exports.isTimePattern = exports.isDateTimePattern = exports.isDatePattern = void 0;
3
+ exports.ssfFormatColor = exports.ssfFormat = exports.isStandaloneMonthPattern = exports.isTextPattern = exports.isTimePattern = exports.isDateTimePattern = exports.isDatePattern = void 0;
4
+ var tslib_1 = require("tslib");
4
5
  var numfmt_1 = require("numfmt");
5
6
  var colors_1 = require("./colors");
6
7
  /**
@@ -79,12 +80,120 @@ var isSingleSectionElapsedTimePattern = function (pattern) {
79
80
  return !pattern.includes(";") &&
80
81
  /\[(?:h+|m+|s+)\]/i.test(pattern.replace(/\[\$[^\]]+\]/g, ""));
81
82
  };
83
+ /**
84
+ * Does the pattern spell a month name (`mmm`+) with no day-of-month field
85
+ * (`d`/`dd`) beside it? Quoted literals, bracket directives and escaped
86
+ * characters are skipped; `ddd`/`dddd` are weekday names, not a day.
87
+ */
88
+ var isStandaloneMonthPattern = function (pattern) {
89
+ var _a;
90
+ var hasMonthName = false;
91
+ var hasDay = false;
92
+ var inQuotes = false;
93
+ var inBrackets = false;
94
+ for (var i = 0; i < pattern.length; i++) {
95
+ var char = pattern[i];
96
+ if (inQuotes) {
97
+ inQuotes = char !== '"';
98
+ }
99
+ else if (inBrackets) {
100
+ inBrackets = char !== "]";
101
+ }
102
+ else if (char === '"') {
103
+ inQuotes = true;
104
+ }
105
+ else if (char === "[") {
106
+ inBrackets = true;
107
+ }
108
+ else if (char === "\\" || char === "_" || char === "*") {
109
+ i++;
110
+ }
111
+ else {
112
+ var field = char.toLowerCase();
113
+ if (field !== "m" && field !== "d")
114
+ continue;
115
+ var run = 1;
116
+ while (((_a = pattern[i + run]) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === field)
117
+ run++;
118
+ if (field === "m")
119
+ hasMonthName || (hasMonthName = run >= 3);
120
+ else
121
+ hasDay || (hasDay = run <= 2);
122
+ i += run - 1;
123
+ }
124
+ }
125
+ return hasMonthName && !hasDay;
126
+ };
127
+ exports.isStandaloneMonthPattern = isStandaloneMonthPattern;
128
+ var standaloneMonthPatternCache = new Map();
129
+ /**
130
+ * Languages whose standalone month names Excel/Windows title-case ("Январь",
131
+ * "Січень") while the in-date forms stay lowercase ("1 января"). Intl keeps
132
+ * both lowercase, so the standalone table is capitalized for these; Latin-
133
+ * script inflecting locales (pl, cs, fi, …) stay lowercase, as Excel shows
134
+ * them.
135
+ */
136
+ var TITLE_CASED_STANDALONE_MONTHS = new Set(["ru", "uk", "be"]);
137
+ var intlMonthNames = function (locale, options) {
138
+ var formatter = new Intl.DateTimeFormat(locale, tslib_1.__assign(tslib_1.__assign({}, options), { timeZone: "UTC" }));
139
+ return Array.from({ length: 12 }, function (_, month) {
140
+ var _a, _b;
141
+ return (_b = (_a = formatter
142
+ .formatToParts(new Date(Date.UTC(2020, month, 1)))
143
+ .find(function (part) { return part.type === "month"; })) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : "";
144
+ });
145
+ };
146
+ var standaloneMonthLocales = new Map();
147
+ /**
148
+ * numfmt ships one month-name table per locale: the form used beside a day
149
+ * number, which in inflecting languages is the genitive (ru "сентября" as in
150
+ * "1 сентября 2026"). A month shown without a day ("Сентябрь 2026") takes the
151
+ * standalone/nominative form, so those locales get a companion numfmt locale
152
+ * carrying the standalone names. Undefined where the names don't inflect.
153
+ */
154
+ var getStandaloneMonthLocale = function (locale) {
155
+ if (standaloneMonthLocales.has(locale)) {
156
+ return standaloneMonthLocales.get(locale);
157
+ }
158
+ var overlay;
159
+ try {
160
+ var base = (0, numfmt_1.getLocale)(locale);
161
+ var standalone = intlMonthNames(locale, { month: "long" });
162
+ var withDay_1 = intlMonthNames(locale, { day: "numeric", month: "long" });
163
+ if (base && standalone.some(function (name, month) { return name !== withDay_1[month]; })) {
164
+ var language_1 = new Intl.Locale(locale).language;
165
+ var titleCase = function (name) {
166
+ return TITLE_CASED_STANDALONE_MONTHS.has(language_1)
167
+ ? name.charAt(0).toLocaleUpperCase(locale) + name.slice(1)
168
+ : name;
169
+ };
170
+ overlay = "standalone_".concat(locale.replace(/[^a-z\d]/gi, ""));
171
+ (0, numfmt_1.addLocale)(tslib_1.__assign(tslib_1.__assign({}, base), { mmmm: standalone.map(titleCase), mmm: intlMonthNames(locale, { month: "short" }).map(titleCase) }), overlay);
172
+ }
173
+ }
174
+ catch (_a) {
175
+ // Unknown locale: keep numfmt's own table.
176
+ }
177
+ standaloneMonthLocales.set(locale, overlay);
178
+ return overlay;
179
+ };
180
+ var localeForPattern = function (pattern, locale) {
181
+ var _a;
182
+ if (!locale)
183
+ return locale;
184
+ var standalone = standaloneMonthPatternCache.get(pattern);
185
+ if (standalone === undefined) {
186
+ standalone = (0, exports.isStandaloneMonthPattern)(pattern);
187
+ standaloneMonthPatternCache.set(pattern, standalone);
188
+ }
189
+ return standalone ? ((_a = getStandaloneMonthLocale(locale)) !== null && _a !== void 0 ? _a : locale) : locale;
190
+ };
82
191
  var ssfFormat = function (pattern, value, locale) {
83
192
  try {
84
193
  var isNegativeElapsedTime = typeof value === "number" &&
85
194
  value < 0 &&
86
195
  isSingleSectionElapsedTimePattern(pattern);
87
- var formatted = (0, numfmt_1.format)(pattern, isNegativeElapsedTime ? Math.abs(value) : value, { locale: locale });
196
+ var formatted = (0, numfmt_1.format)(pattern, isNegativeElapsedTime ? Math.abs(value) : value, { locale: localeForPattern(pattern, locale) });
88
197
  return isNegativeElapsedTime ? "-".concat(formatted) : formatted;
89
198
  }
90
199
  catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"sort.d.ts","sourceRoot":"","sources":["../../sort.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACxB;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,CAAC,SAAS,SAAS,EACnD,QAAQ,CAAC,EAAE,EACX,gBAAgB,OAAO;;IA0CxB,CAAC"}
1
+ {"version":3,"file":"sort.d.ts","sourceRoot":"","sources":["../../sort.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACxB;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,CAAC,SAAS,SAAS,EACnD,QAAQ,CAAC,EAAE,EACX,gBAAgB,OAAO;;IAwCxB,CAAC"}
package/dist/cjs/sort.js CHANGED
@@ -10,9 +10,7 @@ var tslib_1 = require("tslib");
10
10
  var sortSheetsByIndex = function (sheets, includeHidden) {
11
11
  var filtered = sheets
12
12
  .filter(Boolean)
13
- .filter(function (sheet) {
14
- return includeHidden ? sheet.title : !sheet.hidden && sheet.title;
15
- });
13
+ .filter(function (sheet) { return includeHidden || !sheet.hidden; });
16
14
  // Remove duplicate sheets with the same sheetId, keeping only the first occurrence
17
15
  var seen = new Set();
18
16
  var deduplicated = filtered.filter(function (sheet) {
@@ -64,9 +64,16 @@ export declare const supplant: (s: string, o: Record<string, any>) => string;
64
64
  /**
65
65
  * Normalize an external hyperlink target for the browser. A stored URL
66
66
  * without a scheme ("paradigm.xyz") would otherwise be resolved relative to
67
- * the current page. Prefixes https:// unless the URL already has a scheme
68
- * (https:, mailto:, tel:, …), is protocol-relative ("//host"), or is a
69
- * fragment/path reference.
67
+ * the current page. Prefixes https:// unless the URL already has a scheme,
68
+ * is protocol-relative ("//host"), or is a fragment/path reference.
69
+ *
70
+ * URLs with an explicit scheme are only passed through when the scheme is in
71
+ * the safe allowlist (http, https, mailto, tel, ftp — case-insensitive);
72
+ * anything else returns "" so it can never reach window.open/href sinks.
73
+ * Mirrors browser URL canonicalization before matching the scheme: embedded
74
+ * ASCII tab/newline characters are removed and leading/trailing C0 controls
75
+ * and whitespace are stripped, so obfuscations like "Java\tScript:" or a
76
+ * leading "\n" cannot smuggle a dangerous scheme past the check.
70
77
  * @param url
71
78
  * @returns
72
79
  */
@@ -1 +1 @@
1
- {"version":3,"file":"string.d.ts","sourceRoot":"","sources":["../../string.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAK3E;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,aAAa,KAAG,SAKvD,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,aAAa,GACxB,SAAS,MAAM,EACf,UAAS;IACP,QAAQ,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACjB,KACL,aAAa,GAAG,IAuDlB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,GAAI,SAAS,MAAM,uBAEvD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,sBAAsB,GAAI,SAAS,MAAM,uBAErD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,GACxB,MAAM,aAAa,GAAG,IAAI,EAC1B,0BAAwB,EACxB,uBAAqB,EACrB,sBAAoB,EACpB,mBAAiB,KAChB,MAAM,GAAG,IAiBX,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAC7B,WAAW,MAAM,GAAG,SAAS,EAC7B,OAAO,SAAS,WAUjB,CAAC;AAGF,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,GAAG,SAAS,uBASzD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAAI,MAAM,MAAM,GAAG,SAAS,uBAgB3D,CAAC;AAKF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,WAI3C,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,GACnC,OAAO,aAAa,EACpB,MAAK,aAAa,GAAG,IAAY,KAChC,SAQF,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,WAKzD,CAAC;AAKF;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,MAAM,WAY/C,CAAC"}
1
+ {"version":3,"file":"string.d.ts","sourceRoot":"","sources":["../../string.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAK3E;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,aAAa,KAAG,SAKvD,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,aAAa,GACxB,SAAS,MAAM,EACf,UAAS;IACP,QAAQ,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACjB,KACL,aAAa,GAAG,IAuDlB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,GAAI,SAAS,MAAM,uBAEvD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,sBAAsB,GAAI,SAAS,MAAM,uBAErD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,GACxB,MAAM,aAAa,GAAG,IAAI,EAC1B,0BAAwB,EACxB,uBAAqB,EACrB,sBAAoB,EACpB,mBAAiB,KAChB,MAAM,GAAG,IAiBX,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAC7B,WAAW,MAAM,GAAG,SAAS,EAC7B,OAAO,SAAS,WAUjB,CAAC;AAGF,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,GAAG,SAAS,uBASzD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAAI,MAAM,MAAM,GAAG,SAAS,uBAgB3D,CAAC;AAKF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,WAI3C,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,GACnC,OAAO,aAAa,EACpB,MAAK,aAAa,GAAG,IAAY,KAChC,SAQF,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,WAKzD,CAAC;AAUF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,MAAM,WAoB/C,CAAC"}
@@ -213,21 +213,40 @@ var supplant = function (s, o) {
213
213
  };
214
214
  exports.supplant = supplant;
215
215
  // RFC 3986 scheme: ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) ":"
216
- var URL_SCHEME = /^[a-zA-Z][a-zA-Z0-9+.-]*:/;
216
+ var URL_SCHEME = /^([a-zA-Z][a-zA-Z0-9+.-]*):/;
217
+ // Schemes an external hyperlink is allowed to carry. Anything else with an
218
+ // explicit scheme (javascript:, data:, vbscript:, …) is a script/content
219
+ // injection vector and is rejected.
220
+ var SAFE_URL_SCHEMES = new Set(["http", "https", "mailto", "tel", "ftp"]);
217
221
  /**
218
222
  * Normalize an external hyperlink target for the browser. A stored URL
219
223
  * without a scheme ("paradigm.xyz") would otherwise be resolved relative to
220
- * the current page. Prefixes https:// unless the URL already has a scheme
221
- * (https:, mailto:, tel:, …), is protocol-relative ("//host"), or is a
222
- * fragment/path reference.
224
+ * the current page. Prefixes https:// unless the URL already has a scheme,
225
+ * is protocol-relative ("//host"), or is a fragment/path reference.
226
+ *
227
+ * URLs with an explicit scheme are only passed through when the scheme is in
228
+ * the safe allowlist (http, https, mailto, tel, ftp — case-insensitive);
229
+ * anything else returns "" so it can never reach window.open/href sinks.
230
+ * Mirrors browser URL canonicalization before matching the scheme: embedded
231
+ * ASCII tab/newline characters are removed and leading/trailing C0 controls
232
+ * and whitespace are stripped, so obfuscations like "Java\tScript:" or a
233
+ * leading "\n" cannot smuggle a dangerous scheme past the check.
223
234
  * @param url
224
235
  * @returns
225
236
  */
226
237
  var normalizeExternalUrl = function (url) {
227
- var trimmed = url.trim();
228
- if (!trimmed ||
229
- URL_SCHEME.test(trimmed) ||
230
- trimmed.startsWith("//") ||
238
+ var trimmed = url
239
+ .replace(/[\t\n\r]/g, "")
240
+ .replace(/^[\u0000-\u0020]+|[\u0000-\u0020]+$/g, "")
241
+ .trim();
242
+ if (!trimmed) {
243
+ return "";
244
+ }
245
+ var schemeMatch = URL_SCHEME.exec(trimmed);
246
+ if (schemeMatch) {
247
+ return SAFE_URL_SCHEMES.has(schemeMatch[1].toLowerCase()) ? trimmed : "";
248
+ }
249
+ if (trimmed.startsWith("//") ||
231
250
  trimmed.startsWith("#") ||
232
251
  trimmed.startsWith("/")) {
233
252
  return trimmed;
@@ -17,6 +17,12 @@ export declare const isDatePattern: (pattern: string | undefined | null) => bool
17
17
  export declare const isDateTimePattern: (pattern: string | undefined | null) => boolean;
18
18
  export declare const isTimePattern: (pattern: string | undefined | null) => boolean;
19
19
  export declare const isTextPattern: (pattern: string | undefined | null) => boolean;
20
+ /**
21
+ * Does the pattern spell a month name (`mmm`+) with no day-of-month field
22
+ * (`d`/`dd`) beside it? Quoted literals, bracket directives and escaped
23
+ * characters are skipped; `ddd`/`dddd` are weekday names, not a day.
24
+ */
25
+ export declare const isStandaloneMonthPattern: (pattern: string) => boolean;
20
26
  export declare const ssfFormat: (pattern: string, value: any, locale?: string) => string;
21
27
  export declare const ssfFormatColor: (pattern: string, value: any) => string | undefined;
22
28
  //# sourceMappingURL=formatter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../../formatter.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OASlE,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KACjC,OAWF,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OAWlE,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OAOlE,CAAC;AAMF,eAAO,MAAM,SAAS,GACpB,SAAS,MAAM,EACf,OAAO,GAAG,EACV,SAAS,MAAM,KACd,MAeF,CAAC;AAmLF,eAAO,MAAM,cAAc,GACzB,SAAS,MAAM,EACf,OAAO,GAAG,KACT,MAAM,GAAG,SAoBX,CAAC"}
1
+ {"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../../formatter.ts"],"names":[],"mappings":"AAYA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OASlE,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KACjC,OAWF,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OAWlE,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,OAOlE,CAAC;AAMF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GAAI,SAAS,MAAM,KAAG,OA4B1D,CAAC;AA8EF,eAAO,MAAM,SAAS,GACpB,SAAS,MAAM,EACf,OAAO,GAAG,EACV,SAAS,MAAM,KACd,MAeF,CAAC;AAmLF,eAAO,MAAM,cAAc,GACzB,SAAS,MAAM,EACf,OAAO,GAAG,KACT,MAAM,GAAG,SAoBX,CAAC"}
@@ -1,4 +1,4 @@
1
- import { format, formatColor, getFormatDateInfo, isDateFormat, isTextFormat, } from "numfmt";
1
+ import { addLocale, format, formatColor, getFormatDateInfo, getLocale, isDateFormat, isTextFormat, } from "numfmt";
2
2
  import { IndexedColors } from "./colors";
3
3
  /**
4
4
  * Pattern-based type inference (Excel parity). xlsx files routinely carry
@@ -70,12 +70,119 @@ export const isTextPattern = (pattern) => {
70
70
  };
71
71
  const isSingleSectionElapsedTimePattern = (pattern) => !pattern.includes(";") &&
72
72
  /\[(?:h+|m+|s+)\]/i.test(pattern.replace(/\[\$[^\]]+\]/g, ""));
73
+ /**
74
+ * Does the pattern spell a month name (`mmm`+) with no day-of-month field
75
+ * (`d`/`dd`) beside it? Quoted literals, bracket directives and escaped
76
+ * characters are skipped; `ddd`/`dddd` are weekday names, not a day.
77
+ */
78
+ export const isStandaloneMonthPattern = (pattern) => {
79
+ let hasMonthName = false;
80
+ let hasDay = false;
81
+ let inQuotes = false;
82
+ let inBrackets = false;
83
+ for (let i = 0; i < pattern.length; i++) {
84
+ const char = pattern[i];
85
+ if (inQuotes) {
86
+ inQuotes = char !== '"';
87
+ }
88
+ else if (inBrackets) {
89
+ inBrackets = char !== "]";
90
+ }
91
+ else if (char === '"') {
92
+ inQuotes = true;
93
+ }
94
+ else if (char === "[") {
95
+ inBrackets = true;
96
+ }
97
+ else if (char === "\\" || char === "_" || char === "*") {
98
+ i++;
99
+ }
100
+ else {
101
+ const field = char.toLowerCase();
102
+ if (field !== "m" && field !== "d")
103
+ continue;
104
+ let run = 1;
105
+ while (pattern[i + run]?.toLowerCase() === field)
106
+ run++;
107
+ if (field === "m")
108
+ hasMonthName ||= run >= 3;
109
+ else
110
+ hasDay ||= run <= 2;
111
+ i += run - 1;
112
+ }
113
+ }
114
+ return hasMonthName && !hasDay;
115
+ };
116
+ const standaloneMonthPatternCache = new Map();
117
+ /**
118
+ * Languages whose standalone month names Excel/Windows title-case ("Январь",
119
+ * "Січень") while the in-date forms stay lowercase ("1 января"). Intl keeps
120
+ * both lowercase, so the standalone table is capitalized for these; Latin-
121
+ * script inflecting locales (pl, cs, fi, …) stay lowercase, as Excel shows
122
+ * them.
123
+ */
124
+ const TITLE_CASED_STANDALONE_MONTHS = new Set(["ru", "uk", "be"]);
125
+ const intlMonthNames = (locale, options) => {
126
+ const formatter = new Intl.DateTimeFormat(locale, {
127
+ ...options,
128
+ timeZone: "UTC",
129
+ });
130
+ return Array.from({ length: 12 }, (_, month) => formatter
131
+ .formatToParts(new Date(Date.UTC(2020, month, 1)))
132
+ .find((part) => part.type === "month")?.value ?? "");
133
+ };
134
+ const standaloneMonthLocales = new Map();
135
+ /**
136
+ * numfmt ships one month-name table per locale: the form used beside a day
137
+ * number, which in inflecting languages is the genitive (ru "сентября" as in
138
+ * "1 сентября 2026"). A month shown without a day ("Сентябрь 2026") takes the
139
+ * standalone/nominative form, so those locales get a companion numfmt locale
140
+ * carrying the standalone names. Undefined where the names don't inflect.
141
+ */
142
+ const getStandaloneMonthLocale = (locale) => {
143
+ if (standaloneMonthLocales.has(locale)) {
144
+ return standaloneMonthLocales.get(locale);
145
+ }
146
+ let overlay;
147
+ try {
148
+ const base = getLocale(locale);
149
+ const standalone = intlMonthNames(locale, { month: "long" });
150
+ const withDay = intlMonthNames(locale, { day: "numeric", month: "long" });
151
+ if (base && standalone.some((name, month) => name !== withDay[month])) {
152
+ const language = new Intl.Locale(locale).language;
153
+ const titleCase = (name) => TITLE_CASED_STANDALONE_MONTHS.has(language)
154
+ ? name.charAt(0).toLocaleUpperCase(locale) + name.slice(1)
155
+ : name;
156
+ overlay = `standalone_${locale.replace(/[^a-z\d]/gi, "")}`;
157
+ addLocale({
158
+ ...base,
159
+ mmmm: standalone.map(titleCase),
160
+ mmm: intlMonthNames(locale, { month: "short" }).map(titleCase),
161
+ }, overlay);
162
+ }
163
+ }
164
+ catch {
165
+ // Unknown locale: keep numfmt's own table.
166
+ }
167
+ standaloneMonthLocales.set(locale, overlay);
168
+ return overlay;
169
+ };
170
+ const localeForPattern = (pattern, locale) => {
171
+ if (!locale)
172
+ return locale;
173
+ let standalone = standaloneMonthPatternCache.get(pattern);
174
+ if (standalone === undefined) {
175
+ standalone = isStandaloneMonthPattern(pattern);
176
+ standaloneMonthPatternCache.set(pattern, standalone);
177
+ }
178
+ return standalone ? (getStandaloneMonthLocale(locale) ?? locale) : locale;
179
+ };
73
180
  export const ssfFormat = (pattern, value, locale) => {
74
181
  try {
75
182
  const isNegativeElapsedTime = typeof value === "number" &&
76
183
  value < 0 &&
77
184
  isSingleSectionElapsedTimePattern(pattern);
78
- const formatted = format(pattern, isNegativeElapsedTime ? Math.abs(value) : value, { locale });
185
+ const formatted = format(pattern, isNegativeElapsedTime ? Math.abs(value) : value, { locale: localeForPattern(pattern, locale) });
79
186
  return isNegativeElapsedTime ? `-${formatted}` : formatted;
80
187
  }
81
188
  catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"sort.d.ts","sourceRoot":"","sources":["../../sort.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACxB;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,CAAC,SAAS,SAAS,EACnD,QAAQ,CAAC,EAAE,EACX,gBAAgB,OAAO;;IA0CxB,CAAC"}
1
+ {"version":3,"file":"sort.d.ts","sourceRoot":"","sources":["../../sort.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACxB;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,CAAC,SAAS,SAAS,EACnD,QAAQ,CAAC,EAAE,EACX,gBAAgB,OAAO;;IAwCxB,CAAC"}
package/dist/esm/sort.js CHANGED
@@ -6,7 +6,7 @@
6
6
  export const sortSheetsByIndex = (sheets, includeHidden) => {
7
7
  const filtered = sheets
8
8
  .filter(Boolean)
9
- .filter((sheet) => includeHidden ? sheet.title : !sheet.hidden && sheet.title);
9
+ .filter((sheet) => includeHidden || !sheet.hidden);
10
10
  // Remove duplicate sheets with the same sheetId, keeping only the first occurrence
11
11
  const seen = new Set();
12
12
  const deduplicated = filtered.filter((sheet) => {
@@ -64,9 +64,16 @@ export declare const supplant: (s: string, o: Record<string, any>) => string;
64
64
  /**
65
65
  * Normalize an external hyperlink target for the browser. A stored URL
66
66
  * without a scheme ("paradigm.xyz") would otherwise be resolved relative to
67
- * the current page. Prefixes https:// unless the URL already has a scheme
68
- * (https:, mailto:, tel:, …), is protocol-relative ("//host"), or is a
69
- * fragment/path reference.
67
+ * the current page. Prefixes https:// unless the URL already has a scheme,
68
+ * is protocol-relative ("//host"), or is a fragment/path reference.
69
+ *
70
+ * URLs with an explicit scheme are only passed through when the scheme is in
71
+ * the safe allowlist (http, https, mailto, tel, ftp — case-insensitive);
72
+ * anything else returns "" so it can never reach window.open/href sinks.
73
+ * Mirrors browser URL canonicalization before matching the scheme: embedded
74
+ * ASCII tab/newline characters are removed and leading/trailing C0 controls
75
+ * and whitespace are stripped, so obfuscations like "Java\tScript:" or a
76
+ * leading "\n" cannot smuggle a dangerous scheme past the check.
70
77
  * @param url
71
78
  * @returns
72
79
  */
@@ -1 +1 @@
1
- {"version":3,"file":"string.d.ts","sourceRoot":"","sources":["../../string.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAK3E;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,aAAa,KAAG,SAKvD,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,aAAa,GACxB,SAAS,MAAM,EACf,UAAS;IACP,QAAQ,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACjB,KACL,aAAa,GAAG,IAuDlB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,GAAI,SAAS,MAAM,uBAEvD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,sBAAsB,GAAI,SAAS,MAAM,uBAErD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,GACxB,MAAM,aAAa,GAAG,IAAI,EAC1B,0BAAwB,EACxB,uBAAqB,EACrB,sBAAoB,EACpB,mBAAiB,KAChB,MAAM,GAAG,IAiBX,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAC7B,WAAW,MAAM,GAAG,SAAS,EAC7B,OAAO,SAAS,WAUjB,CAAC;AAGF,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,GAAG,SAAS,uBASzD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAAI,MAAM,MAAM,GAAG,SAAS,uBAgB3D,CAAC;AAKF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,WAI3C,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,GACnC,OAAO,aAAa,EACpB,MAAK,aAAa,GAAG,IAAY,KAChC,SAQF,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,WAKzD,CAAC;AAKF;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,MAAM,WAY/C,CAAC"}
1
+ {"version":3,"file":"string.d.ts","sourceRoot":"","sources":["../../string.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAK3E;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,aAAa,KAAG,SAKvD,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,aAAa,GACxB,SAAS,MAAM,EACf,UAAS;IACP,QAAQ,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACjB,KACL,aAAa,GAAG,IAuDlB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,GAAI,SAAS,MAAM,uBAEvD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,sBAAsB,GAAI,SAAS,MAAM,uBAErD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,GACxB,MAAM,aAAa,GAAG,IAAI,EAC1B,0BAAwB,EACxB,uBAAqB,EACrB,sBAAoB,EACpB,mBAAiB,KAChB,MAAM,GAAG,IAiBX,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAC7B,WAAW,MAAM,GAAG,SAAS,EAC7B,OAAO,SAAS,WAUjB,CAAC;AAGF,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,GAAG,SAAS,uBASzD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAAI,MAAM,MAAM,GAAG,SAAS,uBAgB3D,CAAC;AAKF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,WAI3C,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,GACnC,OAAO,aAAa,EACpB,MAAK,aAAa,GAAG,IAAY,KAChC,SAQF,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,WAKzD,CAAC;AAUF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,MAAM,WAoB/C,CAAC"}
@@ -190,21 +190,40 @@ export const supplant = (s, o) => {
190
190
  });
191
191
  };
192
192
  // RFC 3986 scheme: ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) ":"
193
- const URL_SCHEME = /^[a-zA-Z][a-zA-Z0-9+.-]*:/;
193
+ const URL_SCHEME = /^([a-zA-Z][a-zA-Z0-9+.-]*):/;
194
+ // Schemes an external hyperlink is allowed to carry. Anything else with an
195
+ // explicit scheme (javascript:, data:, vbscript:, …) is a script/content
196
+ // injection vector and is rejected.
197
+ const SAFE_URL_SCHEMES = new Set(["http", "https", "mailto", "tel", "ftp"]);
194
198
  /**
195
199
  * Normalize an external hyperlink target for the browser. A stored URL
196
200
  * without a scheme ("paradigm.xyz") would otherwise be resolved relative to
197
- * the current page. Prefixes https:// unless the URL already has a scheme
198
- * (https:, mailto:, tel:, …), is protocol-relative ("//host"), or is a
199
- * fragment/path reference.
201
+ * the current page. Prefixes https:// unless the URL already has a scheme,
202
+ * is protocol-relative ("//host"), or is a fragment/path reference.
203
+ *
204
+ * URLs with an explicit scheme are only passed through when the scheme is in
205
+ * the safe allowlist (http, https, mailto, tel, ftp — case-insensitive);
206
+ * anything else returns "" so it can never reach window.open/href sinks.
207
+ * Mirrors browser URL canonicalization before matching the scheme: embedded
208
+ * ASCII tab/newline characters are removed and leading/trailing C0 controls
209
+ * and whitespace are stripped, so obfuscations like "Java\tScript:" or a
210
+ * leading "\n" cannot smuggle a dangerous scheme past the check.
200
211
  * @param url
201
212
  * @returns
202
213
  */
203
214
  export const normalizeExternalUrl = (url) => {
204
- const trimmed = url.trim();
205
- if (!trimmed ||
206
- URL_SCHEME.test(trimmed) ||
207
- trimmed.startsWith("//") ||
215
+ const trimmed = url
216
+ .replace(/[\t\n\r]/g, "")
217
+ .replace(/^[\u0000-\u0020]+|[\u0000-\u0020]+$/g, "")
218
+ .trim();
219
+ if (!trimmed) {
220
+ return "";
221
+ }
222
+ const schemeMatch = URL_SCHEME.exec(trimmed);
223
+ if (schemeMatch) {
224
+ return SAFE_URL_SCHEMES.has(schemeMatch[1].toLowerCase()) ? trimmed : "";
225
+ }
226
+ if (trimmed.startsWith("//") ||
208
227
  trimmed.startsWith("#") ||
209
228
  trimmed.startsWith("/")) {
210
229
  return trimmed;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rowsncolumns/utils",
3
- "version": "9.2.35",
3
+ "version": "9.2.37",
4
4
  "author": "Vinay M",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "main": "./dist/cjs/index.js",
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "numfmt": "^3.2.2",
45
- "@rowsncolumns/common-types": ">=5.2.29"
45
+ "@rowsncolumns/common-types": ">=5.2.30"
46
46
  },
47
47
  "devDependencies": {},
48
48
  "peerDependencies": {}