@gateweb/react-utils 2.0.0 → 2.1.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.
@@ -706,6 +706,35 @@ declare function invariant(condition: any, message?: string | (() => string)): a
706
706
  */
707
707
  declare const isServer: () => boolean;
708
708
 
709
+ type MaskKeySelector = string | ((key: string, value: unknown, path: Array<string | number>) => boolean);
710
+ /**
711
+ * 深度走訪物件/陣列,將指定鍵名且值為字串的欄位遮罩為固定字串(預設 "******")。
712
+ *
713
+ * - 預設僅遮罩鍵名等於 `password` 的欄位(區分大小寫)
714
+ * - 可傳入「字串鍵名」或「函式選擇器」來自訂要遮罩的欄位
715
+ * - 僅在值為字串時遮罩,其他型別維持原樣
716
+ * - 不變更輸入參考;回傳全新的資料結構
717
+ * - 函式選擇器會收到 `key`、`value` 與 `path`(到該鍵的路徑,陣列用索引)
718
+ * - 支援巢狀物件與陣列
719
+ *
720
+ * @param input - 來源資料
721
+ * @param mask - 遮罩字串,預設為 "******"
722
+ * @param selector - 欄位選擇器,字串或函式,預設 'password'
723
+ * @returns 回傳遮罩後的新資料
724
+ *
725
+ * @example
726
+ * const input = { password: 'secret', user: { password: 'abc' }, list: [{ password: 'x' }, { ok: 1 }] };
727
+ * const output = maskPasswords(input);
728
+ * // { password: '******', user: { password: '******' }, list: [{ password: '******' }, { ok: 1 }] }
729
+ *
730
+ * // 指定鍵名
731
+ * maskPasswords({ secret: 'xxx' }, '***', 'secret'); // { secret: '***' }
732
+ *
733
+ * // 使用函式選擇器(鍵名包含 'pass' 都遮)
734
+ * maskPasswords({ password: 'a', passcode: 'b' }, '***', (k) => k.includes('pass')); // { password: '***', passcode: '***' }
735
+ */
736
+ declare const maskPasswords: <T>(input: T, mask?: string, selector?: MaskKeySelector) => T;
737
+
709
738
  type DeepPartialWithBooleanOverride<T> = {
710
739
  [K in keyof T]?: T[K] extends object ? T[K] extends boolean ? T[K] : DeepPartialWithBooleanOverride<T[K]> | boolean : T[K];
711
740
  };
@@ -1232,5 +1261,5 @@ declare const generatePeriodArray: () => string[];
1232
1261
  */
1233
1262
  declare const getCurrentPeriod: () => string;
1234
1263
 
1235
- export { ByteSize, MimeTypeMap, OtherMimeType, adToRocEra, camelCase2PascalCase, camelCase2SnakeCase, camelString2PascalString, camelString2SnakeString, convertBytes, createEnumLikeObject, debounce, decodeBase64, decodeJson, deepClone, deepMerge, encodeBase64, encodeJson, extractEnumLikeObject, fakeApi, formatAmount, formatBytes, formatStarMask, generatePeriodArray, getCurrentPeriod, getMimeType, invariant, isChinese, isDateString, isDateTimeString, isEmail, isEnglish, isEqual, isNil, isNonZeroStart, isNumber, isNumberAtLeastN, isNumberN, isNumberNM, isServer, isTWMobile, isTWPhone, isTimeString, isValidPassword, maskString, mergeConfig, objectToSearchParams, omit, omitByValue, parseFileInfoFromFilename, parseFilenameFromDisposition, pascalCase2CamelCase, pascalCase2SnakeCase, pascalString2CamelString, pascalString2SnakeString, pick, pickByValue, renameKey, rocEraToAd, searchParamsToObject, snakeCase2CamelCase, snakeCase2PascalCase, snakeString2CamelString, snakeString2PascalString, throttle, validTaxId, validateDateString, validateFileType, wait };
1236
- export type { MimeTypeExtension, MimeTypeValue, PartialBy, RequiredBy };
1264
+ export { ByteSize, MimeTypeMap, OtherMimeType, adToRocEra, camelCase2PascalCase, camelCase2SnakeCase, camelString2PascalString, camelString2SnakeString, convertBytes, createEnumLikeObject, debounce, decodeBase64, decodeJson, deepClone, deepMerge, encodeBase64, encodeJson, extractEnumLikeObject, fakeApi, formatAmount, formatBytes, formatStarMask, generatePeriodArray, getCurrentPeriod, getMimeType, invariant, isChinese, isDateString, isDateTimeString, isEmail, isEnglish, isEqual, isNil, isNonZeroStart, isNumber, isNumberAtLeastN, isNumberN, isNumberNM, isServer, isTWMobile, isTWPhone, isTimeString, isValidPassword, maskPasswords, maskString, mergeConfig, objectToSearchParams, omit, omitByValue, parseFileInfoFromFilename, parseFilenameFromDisposition, pascalCase2CamelCase, pascalCase2SnakeCase, pascalString2CamelString, pascalString2SnakeString, pick, pickByValue, renameKey, rocEraToAd, searchParamsToObject, snakeCase2CamelCase, snakeCase2PascalCase, snakeString2CamelString, snakeString2PascalString, throttle, validTaxId, validateDateString, validateFileType, wait };
1265
+ export type { MaskKeySelector, MimeTypeExtension, MimeTypeValue, PartialBy, RequiredBy };
package/dist/cjs/index.js CHANGED
@@ -493,6 +493,74 @@ message) {
493
493
  * 判斷執行環境是否為 Server(node.js) 端
494
494
  */ const isServer = ()=>typeof window === 'undefined';
495
495
 
496
+ /**
497
+ * 深度走訪物件/陣列,將指定鍵名且值為字串的欄位遮罩為固定字串(預設 "******")。
498
+ *
499
+ * - 預設僅遮罩鍵名等於 `password` 的欄位(區分大小寫)
500
+ * - 可傳入「字串鍵名」或「函式選擇器」來自訂要遮罩的欄位
501
+ * - 僅在值為字串時遮罩,其他型別維持原樣
502
+ * - 不變更輸入參考;回傳全新的資料結構
503
+ * - 函式選擇器會收到 `key`、`value` 與 `path`(到該鍵的路徑,陣列用索引)
504
+ * - 支援巢狀物件與陣列
505
+ *
506
+ * @param input - 來源資料
507
+ * @param mask - 遮罩字串,預設為 "******"
508
+ * @param selector - 欄位選擇器,字串或函式,預設 'password'
509
+ * @returns 回傳遮罩後的新資料
510
+ *
511
+ * @example
512
+ * const input = { password: 'secret', user: { password: 'abc' }, list: [{ password: 'x' }, { ok: 1 }] };
513
+ * const output = maskPasswords(input);
514
+ * // { password: '******', user: { password: '******' }, list: [{ password: '******' }, { ok: 1 }] }
515
+ *
516
+ * // 指定鍵名
517
+ * maskPasswords({ secret: 'xxx' }, '***', 'secret'); // { secret: '***' }
518
+ *
519
+ * // 使用函式選擇器(鍵名包含 'pass' 都遮)
520
+ * maskPasswords({ password: 'a', passcode: 'b' }, '***', (k) => k.includes('pass')); // { password: '***', passcode: '***' }
521
+ */ const maskPasswords = (input, mask = '******', selector = 'password')=>{
522
+ const isObject = (val)=>val !== null && typeof val === 'object' && !Array.isArray(val) && !(val instanceof Date);
523
+ const shouldMask = (key, value, path)=>{
524
+ if (typeof selector === 'string') {
525
+ return key === selector && typeof value === 'string';
526
+ }
527
+ return selector(key, value, path) && typeof value === 'string';
528
+ };
529
+ const walk = (val, path)=>{
530
+ if (val === null || val === undefined) return val;
531
+ if (Array.isArray(val)) {
532
+ return val.map((item, idx)=>walk(item, [
533
+ ...path,
534
+ idx
535
+ ]));
536
+ }
537
+ if (val instanceof Date) {
538
+ return new Date(val.getTime());
539
+ }
540
+ if (isObject(val)) {
541
+ const result = Object.entries(val).reduce((acc, [key, v])=>{
542
+ const nextPath = [
543
+ ...path,
544
+ key
545
+ ];
546
+ if (shouldMask(key, v, nextPath)) {
547
+ return {
548
+ ...acc,
549
+ [key]: mask
550
+ };
551
+ }
552
+ return {
553
+ ...acc,
554
+ [key]: walk(v, nextPath)
555
+ };
556
+ }, {});
557
+ return result;
558
+ }
559
+ return val;
560
+ };
561
+ return walk(input, []);
562
+ };
563
+
496
564
  /**
497
565
  * 將物件中的某些 key 排除
498
566
  *
@@ -1446,6 +1514,7 @@ exports.isTWMobile = isTWMobile;
1446
1514
  exports.isTWPhone = isTWPhone;
1447
1515
  exports.isTimeString = isTimeString;
1448
1516
  exports.isValidPassword = isValidPassword;
1517
+ exports.maskPasswords = maskPasswords;
1449
1518
  exports.maskString = maskString;
1450
1519
  exports.mergeConfig = mergeConfig;
1451
1520
  exports.objectToSearchParams = objectToSearchParams;
@@ -706,6 +706,35 @@ declare function invariant(condition: any, message?: string | (() => string)): a
706
706
  */
707
707
  declare const isServer: () => boolean;
708
708
 
709
+ type MaskKeySelector = string | ((key: string, value: unknown, path: Array<string | number>) => boolean);
710
+ /**
711
+ * 深度走訪物件/陣列,將指定鍵名且值為字串的欄位遮罩為固定字串(預設 "******")。
712
+ *
713
+ * - 預設僅遮罩鍵名等於 `password` 的欄位(區分大小寫)
714
+ * - 可傳入「字串鍵名」或「函式選擇器」來自訂要遮罩的欄位
715
+ * - 僅在值為字串時遮罩,其他型別維持原樣
716
+ * - 不變更輸入參考;回傳全新的資料結構
717
+ * - 函式選擇器會收到 `key`、`value` 與 `path`(到該鍵的路徑,陣列用索引)
718
+ * - 支援巢狀物件與陣列
719
+ *
720
+ * @param input - 來源資料
721
+ * @param mask - 遮罩字串,預設為 "******"
722
+ * @param selector - 欄位選擇器,字串或函式,預設 'password'
723
+ * @returns 回傳遮罩後的新資料
724
+ *
725
+ * @example
726
+ * const input = { password: 'secret', user: { password: 'abc' }, list: [{ password: 'x' }, { ok: 1 }] };
727
+ * const output = maskPasswords(input);
728
+ * // { password: '******', user: { password: '******' }, list: [{ password: '******' }, { ok: 1 }] }
729
+ *
730
+ * // 指定鍵名
731
+ * maskPasswords({ secret: 'xxx' }, '***', 'secret'); // { secret: '***' }
732
+ *
733
+ * // 使用函式選擇器(鍵名包含 'pass' 都遮)
734
+ * maskPasswords({ password: 'a', passcode: 'b' }, '***', (k) => k.includes('pass')); // { password: '***', passcode: '***' }
735
+ */
736
+ declare const maskPasswords: <T>(input: T, mask?: string, selector?: MaskKeySelector) => T;
737
+
709
738
  type DeepPartialWithBooleanOverride<T> = {
710
739
  [K in keyof T]?: T[K] extends object ? T[K] extends boolean ? T[K] : DeepPartialWithBooleanOverride<T[K]> | boolean : T[K];
711
740
  };
@@ -1232,5 +1261,5 @@ declare const generatePeriodArray: () => string[];
1232
1261
  */
1233
1262
  declare const getCurrentPeriod: () => string;
1234
1263
 
1235
- export { ByteSize, MimeTypeMap, OtherMimeType, adToRocEra, camelCase2PascalCase, camelCase2SnakeCase, camelString2PascalString, camelString2SnakeString, convertBytes, createEnumLikeObject, debounce, decodeBase64, decodeJson, deepClone, deepMerge, encodeBase64, encodeJson, extractEnumLikeObject, fakeApi, formatAmount, formatBytes, formatStarMask, generatePeriodArray, getCurrentPeriod, getMimeType, invariant, isChinese, isDateString, isDateTimeString, isEmail, isEnglish, isEqual, isNil, isNonZeroStart, isNumber, isNumberAtLeastN, isNumberN, isNumberNM, isServer, isTWMobile, isTWPhone, isTimeString, isValidPassword, maskString, mergeConfig, objectToSearchParams, omit, omitByValue, parseFileInfoFromFilename, parseFilenameFromDisposition, pascalCase2CamelCase, pascalCase2SnakeCase, pascalString2CamelString, pascalString2SnakeString, pick, pickByValue, renameKey, rocEraToAd, searchParamsToObject, snakeCase2CamelCase, snakeCase2PascalCase, snakeString2CamelString, snakeString2PascalString, throttle, validTaxId, validateDateString, validateFileType, wait };
1236
- export type { MimeTypeExtension, MimeTypeValue, PartialBy, RequiredBy };
1264
+ export { ByteSize, MimeTypeMap, OtherMimeType, adToRocEra, camelCase2PascalCase, camelCase2SnakeCase, camelString2PascalString, camelString2SnakeString, convertBytes, createEnumLikeObject, debounce, decodeBase64, decodeJson, deepClone, deepMerge, encodeBase64, encodeJson, extractEnumLikeObject, fakeApi, formatAmount, formatBytes, formatStarMask, generatePeriodArray, getCurrentPeriod, getMimeType, invariant, isChinese, isDateString, isDateTimeString, isEmail, isEnglish, isEqual, isNil, isNonZeroStart, isNumber, isNumberAtLeastN, isNumberN, isNumberNM, isServer, isTWMobile, isTWPhone, isTimeString, isValidPassword, maskPasswords, maskString, mergeConfig, objectToSearchParams, omit, omitByValue, parseFileInfoFromFilename, parseFilenameFromDisposition, pascalCase2CamelCase, pascalCase2SnakeCase, pascalString2CamelString, pascalString2SnakeString, pick, pickByValue, renameKey, rocEraToAd, searchParamsToObject, snakeCase2CamelCase, snakeCase2PascalCase, snakeString2CamelString, snakeString2PascalString, throttle, validTaxId, validateDateString, validateFileType, wait };
1265
+ export type { MaskKeySelector, MimeTypeExtension, MimeTypeValue, PartialBy, RequiredBy };
package/dist/es/index.mjs CHANGED
@@ -487,6 +487,74 @@ message) {
487
487
  * 判斷執行環境是否為 Server(node.js) 端
488
488
  */ const isServer = ()=>typeof window === 'undefined';
489
489
 
490
+ /**
491
+ * 深度走訪物件/陣列,將指定鍵名且值為字串的欄位遮罩為固定字串(預設 "******")。
492
+ *
493
+ * - 預設僅遮罩鍵名等於 `password` 的欄位(區分大小寫)
494
+ * - 可傳入「字串鍵名」或「函式選擇器」來自訂要遮罩的欄位
495
+ * - 僅在值為字串時遮罩,其他型別維持原樣
496
+ * - 不變更輸入參考;回傳全新的資料結構
497
+ * - 函式選擇器會收到 `key`、`value` 與 `path`(到該鍵的路徑,陣列用索引)
498
+ * - 支援巢狀物件與陣列
499
+ *
500
+ * @param input - 來源資料
501
+ * @param mask - 遮罩字串,預設為 "******"
502
+ * @param selector - 欄位選擇器,字串或函式,預設 'password'
503
+ * @returns 回傳遮罩後的新資料
504
+ *
505
+ * @example
506
+ * const input = { password: 'secret', user: { password: 'abc' }, list: [{ password: 'x' }, { ok: 1 }] };
507
+ * const output = maskPasswords(input);
508
+ * // { password: '******', user: { password: '******' }, list: [{ password: '******' }, { ok: 1 }] }
509
+ *
510
+ * // 指定鍵名
511
+ * maskPasswords({ secret: 'xxx' }, '***', 'secret'); // { secret: '***' }
512
+ *
513
+ * // 使用函式選擇器(鍵名包含 'pass' 都遮)
514
+ * maskPasswords({ password: 'a', passcode: 'b' }, '***', (k) => k.includes('pass')); // { password: '***', passcode: '***' }
515
+ */ const maskPasswords = (input, mask = '******', selector = 'password')=>{
516
+ const isObject = (val)=>val !== null && typeof val === 'object' && !Array.isArray(val) && !(val instanceof Date);
517
+ const shouldMask = (key, value, path)=>{
518
+ if (typeof selector === 'string') {
519
+ return key === selector && typeof value === 'string';
520
+ }
521
+ return selector(key, value, path) && typeof value === 'string';
522
+ };
523
+ const walk = (val, path)=>{
524
+ if (val === null || val === undefined) return val;
525
+ if (Array.isArray(val)) {
526
+ return val.map((item, idx)=>walk(item, [
527
+ ...path,
528
+ idx
529
+ ]));
530
+ }
531
+ if (val instanceof Date) {
532
+ return new Date(val.getTime());
533
+ }
534
+ if (isObject(val)) {
535
+ const result = Object.entries(val).reduce((acc, [key, v])=>{
536
+ const nextPath = [
537
+ ...path,
538
+ key
539
+ ];
540
+ if (shouldMask(key, v, nextPath)) {
541
+ return {
542
+ ...acc,
543
+ [key]: mask
544
+ };
545
+ }
546
+ return {
547
+ ...acc,
548
+ [key]: walk(v, nextPath)
549
+ };
550
+ }, {});
551
+ return result;
552
+ }
553
+ return val;
554
+ };
555
+ return walk(input, []);
556
+ };
557
+
490
558
  /**
491
559
  * 將物件中的某些 key 排除
492
560
  *
@@ -1397,4 +1465,4 @@ const FILE_SIZE_UNITS = [
1397
1465
  return dayjs(endMonth, 'YYYYMM').subtract(1911, 'year').format('YYYYMM').substring(1);
1398
1466
  };
1399
1467
 
1400
- export { ByteSize, MimeTypeMap, OtherMimeType, adToRocEra, camelCase2PascalCase, camelCase2SnakeCase, camelString2PascalString, camelString2SnakeString, convertBytes, createEnumLikeObject, debounce, decodeBase64, decodeJson, deepClone, deepMerge, encodeBase64, encodeJson, extractEnumLikeObject, fakeApi, formatAmount, formatBytes, formatStarMask, generatePeriodArray, getCurrentPeriod, getMimeType, invariant, isChinese, isDateString, isDateTimeString, isEmail, isEnglish, isEqual, isNil, isNonZeroStart, isNumber, isNumberAtLeastN, isNumberN, isNumberNM, isServer, isTWMobile, isTWPhone, isTimeString, isValidPassword, maskString, mergeConfig, objectToSearchParams, omit, omitByValue, parseFileInfoFromFilename, parseFilenameFromDisposition, pascalCase2CamelCase, pascalCase2SnakeCase, pascalString2CamelString, pascalString2SnakeString, pick, pickByValue, renameKey, rocEraToAd, searchParamsToObject, snakeCase2CamelCase, snakeCase2PascalCase, snakeString2CamelString, snakeString2PascalString, throttle, validTaxId, validateDateString, validateFileType, wait };
1468
+ export { ByteSize, MimeTypeMap, OtherMimeType, adToRocEra, camelCase2PascalCase, camelCase2SnakeCase, camelString2PascalString, camelString2SnakeString, convertBytes, createEnumLikeObject, debounce, decodeBase64, decodeJson, deepClone, deepMerge, encodeBase64, encodeJson, extractEnumLikeObject, fakeApi, formatAmount, formatBytes, formatStarMask, generatePeriodArray, getCurrentPeriod, getMimeType, invariant, isChinese, isDateString, isDateTimeString, isEmail, isEnglish, isEqual, isNil, isNonZeroStart, isNumber, isNumberAtLeastN, isNumberN, isNumberNM, isServer, isTWMobile, isTWPhone, isTimeString, isValidPassword, maskPasswords, maskString, mergeConfig, objectToSearchParams, omit, omitByValue, parseFileInfoFromFilename, parseFilenameFromDisposition, pascalCase2CamelCase, pascalCase2SnakeCase, pascalString2CamelString, pascalString2SnakeString, pick, pickByValue, renameKey, rocEraToAd, searchParamsToObject, snakeCase2CamelCase, snakeCase2PascalCase, snakeString2CamelString, snakeString2PascalString, throttle, validTaxId, validateDateString, validateFileType, wait };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gateweb/react-utils",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "React Utils for GateWeb",
5
5
  "homepage": "https://github.com/GatewebSolutions/react-utils",
6
6
  "files": [