@valkyriestudios/utils 12.25.0 → 12.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -493,8 +493,8 @@ Format a date according to a spec/locale and zone
493
493
  | `W` | Week Number as pure digit | 1 2 .. 52 53 |
494
494
  | `DD` | Day of month as 2 char | 01 02 .. 30 31 |
495
495
  | `D` | Day of month as 1 char | 1 2 .. 30 31 |
496
- | `dddd` | Day of week as 3 char | Sun Mon ... Fri Sat |
497
- | `ddd` | Day of week in full | Sunday Monday ... Saturday |
496
+ | `dddd` | Day of week in full | Sunday Monday ... Saturday |
497
+ | `ddd` | Day of week as 3 char | Sun Mon ... Fri Sat |
498
498
  | `HH` | Hours as 2-char | 00 01 .. 22 23 |
499
499
  | `H` | Hours as pure digit | 0 1 .. 22 23 |
500
500
  | `hh` | Hours in 12 hour time as 2 char | 01 02 ... 11 12 |
@@ -1150,13 +1150,37 @@ isNotEmptyObject('Hi'); // FALSE
1150
1150
  ```
1151
1151
 
1152
1152
  ### object/pick(obj:Object={}, keys:Array[string]=[])
1153
- Copies the keys passed in the 'keys' array from the passed object to a new object and returns that object.**
1153
+ Copies the keys passed in the 'keys' array from the passed object to a new object and returns that object.
1154
1154
  <small>If a key wasn't found it will be set as undefined</small>
1155
1155
  ```typescript
1156
1156
  import pick from '@valkyriestudios/utils/object/pick';
1157
1157
  pick({a: 1, b: 2, c: 3}, ['a','b']); // {a: 1, b: 2}
1158
1158
  ```
1159
1159
 
1160
+ ### object/omit(obj:Object={}, keys:Array[string]=[])
1161
+ Returns an object with the keys provided in the keys array stripped from the provided object.
1162
+ ```typescript
1163
+ import { omit } from "@valkyriestudios/utils/object"; /* Or @valkyriestudios/utils/object/omit; */
1164
+ const redacted = omit({
1165
+ firstName: "Peter",
1166
+ lastName: "Vermeulen",
1167
+ age: 34,
1168
+ details: {
1169
+ phone: "...",
1170
+ email: "...",
1171
+ isActive: true,
1172
+ password: "...",
1173
+ },
1174
+ }, ["age", "details.phone", "details.email", "details.password"]);
1175
+ /**
1176
+ Redacted here will be:
1177
+ {firstName: "Peter", lastName: "Vermeulen", "details": {"isActive": true}}
1178
+
1179
+ Its type will be
1180
+ {firstName: string; lastName: string; details: {isActive: boolean}}
1181
+ */
1182
+ ```
1183
+
1160
1184
  ### object/merge(target:Object={},obj:Object|Object[]={}, opts?:{union?:boolean})
1161
1185
  Merges two objects together, with the preference over the second object.
1162
1186
  ```typescript
package/date/format.js CHANGED
@@ -171,7 +171,7 @@ function getSpecChain(spec) {
171
171
  }
172
172
  }
173
173
  const chain_len = chain.length;
174
- const result = chain_len ? { base, chain, chain_len, repl, repl_len } : null;
174
+ const result = chain_len ? { base, chain, chain_len, repl } : null;
175
175
  spec_cache[spec] = result;
176
176
  return result;
177
177
  }
@@ -190,15 +190,17 @@ function format(val, spec, locale = DEFAULT_LOCALE, zone = DEFAULT_TZ, sow = DEF
190
190
  return n_val.toISOString();
191
191
  const d = toZone(n_val, zone);
192
192
  let base = n_spec.base;
193
- const { chain_len, chain, repl_len, repl } = n_spec;
194
- for (let i = 0; i < chain_len; i++) {
195
- let pos = base.indexOf(chain[i][0]);
196
- const formatted_val = chain[i][1](d, locale, sow);
193
+ const repl = [...n_spec.repl];
194
+ let repl_len = n_spec.repl.length;
195
+ for (let i = 0; i < n_spec.chain_len; i++) {
196
+ const el = n_spec.chain[i];
197
+ let pos = base.indexOf(el[0]);
198
+ const token_val = el[1](d, locale, sow);
197
199
  while (pos !== -1) {
198
- base = base.slice(0, pos) +
199
- formatted_val +
200
- base.slice(pos + chain[i][2]);
201
- pos = base.indexOf(chain[i][0], pos + chain[i][2]);
200
+ const key = '$' + repl_len++ + '$';
201
+ repl.push([key, token_val]);
202
+ base = base.slice(0, pos) + key + base.slice(pos + el[2]);
203
+ pos = base.indexOf(el[0], pos + el[2]);
202
204
  }
203
205
  }
204
206
  for (let i = 0; i < repl_len; i++) {
@@ -4,45 +4,45 @@ exports.toObject = toObject;
4
4
  exports.default = toObject;
5
5
  const isFormat_1 = require("../date/isFormat");
6
6
  const RGX_CLOSE = /\]/g;
7
+ const RGX_DIGIT = /^\d+$/;
7
8
  function assignValue(acc, rawkey, value, single) {
8
9
  let cursor = acc;
9
10
  const keys = rawkey.replace(RGX_CLOSE, '').split(/\[|\./);
10
11
  const keys_len = keys.length;
11
12
  for (let i = 0; i < keys_len; i++) {
12
13
  const key = keys[i];
13
- if (i === keys_len - 1) {
14
+ if (i < (keys_len - 1)) {
15
+ const n_key = Array.isArray(cursor) ? Number(key) : key;
16
+ if (!cursor[n_key]) {
17
+ cursor[n_key] = RGX_DIGIT.test(keys[i + 1]) ? [] : {};
18
+ }
19
+ cursor = cursor[n_key];
20
+ }
21
+ else if (!(key in cursor) || single.has(key)) {
22
+ cursor[key] = value;
23
+ }
24
+ else {
14
25
  const cursor_val = cursor[key];
15
- if (cursor_val !== undefined && (!single || !single.has(key))) {
16
- if (Array.isArray(cursor_val)) {
17
- cursor[key].push(value);
18
- }
19
- else {
20
- cursor[key] = [cursor_val, value];
21
- }
26
+ if (Array.isArray(cursor_val)) {
27
+ cursor_val.push(value);
22
28
  }
23
29
  else {
24
- cursor[key] = value;
30
+ cursor[key] = [cursor_val, value];
25
31
  }
26
32
  }
27
- else {
28
- const n_key = Array.isArray(cursor) ? Number(key) : key;
29
- if (!cursor[n_key])
30
- cursor[n_key] = isNaN(Number(keys[i + 1])) ? {} : [];
31
- cursor = cursor[n_key];
32
- }
33
33
  }
34
34
  }
35
35
  function toObject(form, config) {
36
36
  if (!(form instanceof FormData))
37
37
  throw new Error('formdata/toObject: Value is not an instance of FormData');
38
38
  const set = config?.raw === true ? true : new Set(Array.isArray(config?.raw) ? config?.raw : []);
39
- const single = Array.isArray(config?.single) && config?.single.length ? new Set(config.single) : null;
39
+ const single = new Set(Array.isArray(config?.single) ? config.single : []);
40
40
  const nBool = config?.normalize_bool !== false;
41
41
  const nDate = config?.normalize_date !== false;
42
42
  const nNumber = config?.normalize_number !== false;
43
43
  const acc = {};
44
44
  form.forEach((value, key) => {
45
- if (set !== true && typeof value === 'string' && !set.has(key)) {
45
+ if (set !== true && typeof value === 'string' && value !== '' && !set.has(key)) {
46
46
  if (nBool) {
47
47
  const lower = value.toLowerCase();
48
48
  if (lower === 'true') {
@@ -54,17 +54,17 @@ function toObject(form, config) {
54
54
  return;
55
55
  }
56
56
  }
57
- const trimmed = value.trim();
58
- if (trimmed.length) {
59
- if (nNumber && !isNaN(Number(value))) {
60
- assignValue(acc, key, Number(value), single);
61
- return;
62
- }
63
- if (nDate && (0, isFormat_1.isDateFormat)(value, 'ISO')) {
64
- assignValue(acc, key, new Date(value), single);
57
+ if (nNumber) {
58
+ const nVal = Number(value);
59
+ if (!isNaN(nVal)) {
60
+ assignValue(acc, key, nVal, single);
65
61
  return;
66
62
  }
67
63
  }
64
+ if (nDate && (0, isFormat_1.isDateFormat)(value, 'ISO')) {
65
+ assignValue(acc, key, new Date(value), single);
66
+ return;
67
+ }
68
68
  }
69
69
  assignValue(acc, key, value, single);
70
70
  });
package/index.d.ts CHANGED
@@ -596,3 +596,16 @@ declare module "hash/index" {
596
596
  import { guid } from "hash/guid";
597
597
  export { fnv1A, guid };
598
598
  }
599
+ declare module "object/omit" {
600
+ type ObjectType = {
601
+ [key: string]: any;
602
+ };
603
+ type DottedKeys<T> = (T extends ObjectType ? {
604
+ [K in keyof T & string]: T[K] extends ObjectType ? K | `${K}.${DottedKeys<T[K]>}` : K;
605
+ }[keyof T & string] : string) & string;
606
+ type OmitFromObject<T, K extends string> = K extends `${infer Key}.${infer Rest}` ? Key extends keyof T ? T[Key] extends ObjectType ? {
607
+ [P in keyof T]: P extends Key ? OmitFromObject<T[Key], Rest> : T[P];
608
+ } : T : T : Omit<T, K>;
609
+ function omit<T extends Record<string, any>, K extends readonly DottedKeys<T>[]>(obj: T, keys: K): OmitFromObject<T, K[number]>;
610
+ export { omit, omit as default };
611
+ }
@@ -0,0 +1,17 @@
1
+ type ObjectType = {
2
+ [key: string]: any;
3
+ };
4
+ type DottedKeys<T> = (T extends ObjectType ? {
5
+ [K in keyof T & string]: T[K] extends ObjectType ? K | `${K}.${DottedKeys<T[K]>}` : K;
6
+ }[keyof T & string] : string) & string;
7
+ type OmitFromObject<T, K extends string> = K extends `${infer Key}.${infer Rest}` ? Key extends keyof T ? T[Key] extends ObjectType ? {
8
+ [P in keyof T]: P extends Key ? OmitFromObject<T[Key], Rest> : T[P];
9
+ } : T : T : Omit<T, K>;
10
+ /**
11
+ * Returns a new object with the keys omitted from the passed object, handling nested keys recursively
12
+ *
13
+ * @param {Record<string, any>} obj - Object to omit from
14
+ * @param {string[]} keys - Array of keys to omit from object
15
+ */
16
+ declare function omit<T extends Record<string, any>, K extends readonly DottedKeys<T>[]>(obj: T, keys: K): OmitFromObject<T, K[number]>;
17
+ export { omit, omit as default };
package/object/omit.js ADDED
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.omit = omit;
4
+ exports.default = omit;
5
+ function innerOmit(obj, keys) {
6
+ const result = { ...obj };
7
+ const groups = {};
8
+ for (let i = 0; i < keys.length; i++) {
9
+ const key = keys[i];
10
+ if (typeof key !== 'string')
11
+ continue;
12
+ const [root, path] = key.trim().split('.', 2);
13
+ if (path) {
14
+ if (!groups[root])
15
+ groups[root] = [];
16
+ groups[root].push(path);
17
+ }
18
+ else {
19
+ delete result[root];
20
+ }
21
+ }
22
+ for (const root in groups) {
23
+ if (typeof result[root] !== 'object' || result[root] === null)
24
+ continue;
25
+ result[root] = innerOmit(result[root], groups[root]);
26
+ }
27
+ return result;
28
+ }
29
+ function omit(obj, keys) {
30
+ if (Object.prototype.toString.call(obj) !== '[object Object]' ||
31
+ !Array.isArray(keys) ||
32
+ !keys.length)
33
+ throw new TypeError('Please pass an object to omit from and a keys array');
34
+ return innerOmit(obj, keys);
35
+ }
package/object/pick.d.ts CHANGED
@@ -13,8 +13,8 @@ type UnionToIntersection<U> = (U extends any ? (k: U) => void : object) extends
13
13
  /**
14
14
  * Returns a new object with the keys picked from the passed object
15
15
  *
16
- * @param obj - Object to pick from
17
- * @param keys - Array of keys to pick from object
16
+ * @param {Record<string, any>} obj - Object to pick from
17
+ * @param {string[]} keys - Array of keys to pick from object
18
18
  */
19
19
  declare function pick<T extends Record<string, any>, K extends readonly DottedKeys<T>[]>(obj: T, keys: K): UnionToIntersection<PickFromObject<T, K[number]>>;
20
20
  export { pick, pick as default };
package/package.json CHANGED
@@ -1 +1 @@
1
- { "name": "@valkyriestudios/utils", "version": "12.25.0", "description": "A collection of single-function utilities for common tasks", "author": { "name": "Peter Vermeulen", "url": "https://www.linkedin.com/in/petervermeulen1/" }, "keywords": [ "utility", "library", "javascript", "js", "node", "bun" ], "license": "MIT", "repository": { "type": "git", "url": "git+https://github.com/ValkyrieStudios/utils.git" }, "bugs": { "url": "https://github.com/ValkyrieStudios/utils/issues" }, "homepage": "https://github.com/ValkyrieStudios/utils#readme", "types": "index.d.ts" }
1
+ { "name": "@valkyriestudios/utils", "version": "12.26.0", "description": "A collection of single-function utilities for common tasks", "author": { "name": "Peter Vermeulen", "url": "https://www.linkedin.com/in/petervermeulen1/" }, "keywords": [ "utility", "library", "javascript", "js", "node", "bun" ], "license": "MIT", "repository": { "type": "git", "url": "git+https://github.com/ValkyrieStudios/utils.git" }, "bugs": { "url": "https://github.com/ValkyrieStudios/utils/issues" }, "homepage": "https://github.com/ValkyrieStudios/utils#readme", "types": "index.d.ts" }