mybase 1.1.50 → 1.1.53

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/jest.config.js CHANGED
@@ -3,4 +3,5 @@ module.exports = {
3
3
  preset: 'ts-jest',
4
4
  testEnvironment: 'node',
5
5
  testTimeout: 15000,
6
+ testMatch: ['**/?(*.)+(spec|test).ts'],
6
7
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mybase",
3
- "version": "1.1.50",
3
+ "version": "1.1.53",
4
4
  "description": "",
5
5
  "main": "mybase.js",
6
6
  "scripts": {
@@ -0,0 +1 @@
1
+ export declare function deepCopy<T>(obj: T, visited?: WeakMap<object, any>): T;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deepCopy = deepCopy;
4
+ function deepCopy(obj, visited = new WeakMap()) {
5
+ // Handle null and non-objects
6
+ if (obj === null || typeof obj !== 'object') {
7
+ return obj;
8
+ }
9
+ // Handle circular references
10
+ if (visited.has(obj)) {
11
+ return visited.get(obj);
12
+ }
13
+ // Handle Date
14
+ if (obj instanceof Date) {
15
+ return new Date(obj.getTime());
16
+ }
17
+ // Handle Array
18
+ if (Array.isArray(obj)) {
19
+ const copy = [];
20
+ visited.set(obj, copy);
21
+ obj.forEach((item, i) => {
22
+ copy[i] = deepCopy(item, visited);
23
+ });
24
+ return copy;
25
+ }
26
+ // Handle Object
27
+ const copy = Object.create(Object.getPrototypeOf(obj));
28
+ visited.set(obj, copy);
29
+ for (const key of Object.keys(obj)) {
30
+ copy[key] = deepCopy(obj[key], visited);
31
+ }
32
+ return copy;
33
+ }
34
+ //# sourceMappingURL=deepCopy.js.map
@@ -0,0 +1,108 @@
1
+ import { Unixtime } from "../models/Unixtime";
2
+ import { deepCopy } from "./deepCopy"
3
+
4
+ describe('deepCopy', () => {
5
+ it('deep-copies plain objects', () => {
6
+ const src = { a: 1, b: { c: 2 } };
7
+ const copy = deepCopy(src) as typeof src;
8
+
9
+ expect(copy).toEqual(src);
10
+ expect(copy).not.toBe(src);
11
+
12
+ copy.b.c = 3;
13
+ expect(src.b.c).toBe(2); // original stays intact
14
+ });
15
+
16
+
17
+ it('Date objects are copied as Date objects', () => {
18
+ const src = { date: new Date() }
19
+ const copy = deepCopy(src) as typeof src
20
+ expect(copy.date).toBeInstanceOf(Date)
21
+ expect(copy.date).not.toBe(src.date)
22
+ })
23
+
24
+ it('deep-copies arrays', () => {
25
+ const src = [1, [2, 3]];
26
+ const copy = deepCopy(src) as typeof src;
27
+
28
+ expect(copy).toEqual(src);
29
+ //@ts-ignore
30
+ copy[1][0] = 99;
31
+ //@ts-ignore
32
+ expect(src[1][0]).toBe(2);
33
+ });
34
+
35
+ it('returns primitives unchanged', () => {
36
+ expect(deepCopy(42 as any)).toBe(42);
37
+ expect(deepCopy('hello' as any)).toBe('hello');
38
+ expect(deepCopy(null as any)).toBeNull();
39
+ });
40
+
41
+ it('should handle circular references', () => {
42
+ const obj: any = { a: 1 };
43
+ obj.self = obj;
44
+ const copy = deepCopy(obj);
45
+ expect(copy).toEqual({ a: 1, self: copy });
46
+ expect(copy.self).toBe(copy);
47
+ });
48
+
49
+ it('should preserve prototype chain', () => {
50
+ class MyClass { x = 1; }
51
+ const obj = new MyClass();
52
+ const copy = deepCopy(obj);
53
+ expect(copy).toBeInstanceOf(MyClass);
54
+ expect(copy.x).toBe(1);
55
+ });
56
+
57
+ it('changing Date at src should not change copy', () => {
58
+ const src = { a: new Date("2024-05-05"), b: { c: 2 } };
59
+ const copy = deepCopy(src) as typeof src;
60
+ expect(copy).toEqual(src);
61
+ expect(copy).not.toBe(src);
62
+ src.a.setFullYear(2025)
63
+ expect(src.a.getFullYear()).toBe(2025)
64
+ expect(copy.a.getFullYear()).toBe(2024)
65
+ })
66
+
67
+ it('changing src integer should not change copy', () => {
68
+ const src = { a: 1, b: { c: 2 } };
69
+ const copy = deepCopy(src) as typeof src;
70
+ expect(copy).toEqual(src);
71
+ expect(copy).not.toBe(src);
72
+ src.a = 2
73
+ expect(src.a).toBe(2)
74
+ expect(copy.a).toBe(1)
75
+ })
76
+
77
+ it('changing src string should not change copy', () => {
78
+ const src = { a: "hello", b: { c: 2 } };
79
+ const copy = deepCopy(src) as typeof src;
80
+ expect(copy).toEqual(src);
81
+ expect(copy).not.toBe(src);
82
+ src.a = "world"
83
+ expect(src.a).toBe("world")
84
+ expect(copy.a).toBe("hello")
85
+ })
86
+
87
+ it('changing src boolean should not change copy', () => {
88
+ const src = { a: true, b: { c: 2 } };
89
+ const copy = deepCopy(src) as typeof src;
90
+ expect(copy).toEqual(src);
91
+ expect(copy).not.toBe(src);
92
+ src.a = false
93
+ expect(src.a).toBe(false)
94
+ expect(copy.a).toBe(true)
95
+
96
+ })
97
+
98
+ it('changing src Unixtime should not change copy', () => {
99
+ const src = { a: new Unixtime("2024-05-02"), b: { c: 2 } };
100
+ const copy = deepCopy(src) as typeof src;
101
+ expect(copy).toEqual(src);
102
+ expect(copy).not.toBe(src);
103
+ src.a.addYears(-1)
104
+ expect(src.a.toDate().getFullYear()).toBe(2023)
105
+ expect(copy.a.toDate().getFullYear()).toBe(2024)
106
+ })
107
+
108
+ });
@@ -0,0 +1,34 @@
1
+ export function deepCopy<T>(obj: T, visited = new WeakMap()): T {
2
+ // Handle null and non-objects
3
+ if (obj === null || typeof obj !== 'object') {
4
+ return obj;
5
+ }
6
+
7
+ // Handle circular references
8
+ if (visited.has(obj)) {
9
+ return visited.get(obj);
10
+ }
11
+
12
+ // Handle Date
13
+ if (obj instanceof Date) {
14
+ return new Date(obj.getTime()) as any;
15
+ }
16
+
17
+ // Handle Array
18
+ if (Array.isArray(obj)) {
19
+ const copy: any[] = [];
20
+ visited.set(obj, copy);
21
+ obj.forEach((item, i) => {
22
+ copy[i] = deepCopy(item, visited);
23
+ });
24
+ return copy as T;
25
+ }
26
+
27
+ // Handle Object
28
+ const copy = Object.create(Object.getPrototypeOf(obj));
29
+ visited.set(obj, copy);
30
+ for (const key of Object.keys(obj)) {
31
+ copy[key] = deepCopy((obj as any)[key], visited);
32
+ }
33
+ return copy;
34
+ }
package/ts/index.d.ts CHANGED
@@ -26,8 +26,10 @@ export * from "./funcs/randomUTFString";
26
26
  export * from "./funcs/MaxRuntimeHours";
27
27
  export * from "./funcs/ensureFolder";
28
28
  export * from "./funcs/knexConnection";
29
+ export * from "./funcs/deepCopy";
29
30
  export * from "./models/Unixtime";
30
31
  export * from "./models/Timespan";
31
32
  export * from "./models/IPAddress";
32
33
  export * from "./models/OTPGenerator";
33
34
  export * from "./models/Interfaces";
35
+ export * from "./models/DateIterator";
package/ts/index.js CHANGED
@@ -42,10 +42,12 @@ __exportStar(require("./funcs/randomUTFString"), exports);
42
42
  __exportStar(require("./funcs/MaxRuntimeHours"), exports);
43
43
  __exportStar(require("./funcs/ensureFolder"), exports);
44
44
  __exportStar(require("./funcs/knexConnection"), exports);
45
+ __exportStar(require("./funcs/deepCopy"), exports);
45
46
  // models
46
47
  __exportStar(require("./models/Unixtime"), exports);
47
48
  __exportStar(require("./models/Timespan"), exports);
48
49
  __exportStar(require("./models/IPAddress"), exports);
49
50
  __exportStar(require("./models/OTPGenerator"), exports);
50
51
  __exportStar(require("./models/Interfaces"), exports);
52
+ __exportStar(require("./models/DateIterator"), exports);
51
53
  //# sourceMappingURL=index.js.map
package/ts/index.ts CHANGED
@@ -26,6 +26,7 @@ export * from "./funcs/randomUTFString"
26
26
  export * from "./funcs/MaxRuntimeHours"
27
27
  export * from "./funcs/ensureFolder"
28
28
  export * from "./funcs/knexConnection"
29
+ export * from "./funcs/deepCopy"
29
30
 
30
31
 
31
32
 
@@ -35,4 +36,4 @@ export * from "./models/Timespan"
35
36
  export * from "./models/IPAddress"
36
37
  export * from "./models/OTPGenerator"
37
38
  export * from "./models/Interfaces"
38
-
39
+ export * from "./models/DateIterator"
@@ -0,0 +1,33 @@
1
+ import { Unixtime } from "./Unixtime";
2
+ /**
3
+ * Iterates day-by-day backwards from `start` toward `end` (end-exclusive),
4
+ * emitting each date as a `yyyy{sep}mm{sep}dd` string.
5
+ *
6
+ * @example
7
+ * const it = new DateIterator(Unixtime.now(), Unixtime.now().addDays(-5))
8
+ * it.next() // "20260417"
9
+ * it.next() // "20260416"
10
+ * // ...
11
+ * it.next() // null
12
+ *
13
+ * @example separator
14
+ * new DateIterator(start, end, "-").toArray()
15
+ * // ["2026-04-17", "2026-04-16", ...]
16
+ *
17
+ * @example for..of
18
+ * for (const d of new DateIterator(start, end, "/")) console.log(d)
19
+ */
20
+ export declare class DateIterator implements Iterable<string> {
21
+ private readonly start;
22
+ private readonly end;
23
+ private readonly separator;
24
+ private cursor;
25
+ constructor(start: Unixtime, end: Unixtime, separator?: string);
26
+ /** Returns the next formatted date going backwards, or `null` when exhausted. */
27
+ next(): string | null;
28
+ /** Reset the iterator so it can be consumed again from `start`. */
29
+ reset(): this;
30
+ /** Collect all remaining dates into an array. */
31
+ toArray(): string[];
32
+ [Symbol.iterator](): Iterator<string>;
33
+ }
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DateIterator = void 0;
4
+ /**
5
+ * Iterates day-by-day backwards from `start` toward `end` (end-exclusive),
6
+ * emitting each date as a `yyyy{sep}mm{sep}dd` string.
7
+ *
8
+ * @example
9
+ * const it = new DateIterator(Unixtime.now(), Unixtime.now().addDays(-5))
10
+ * it.next() // "20260417"
11
+ * it.next() // "20260416"
12
+ * // ...
13
+ * it.next() // null
14
+ *
15
+ * @example separator
16
+ * new DateIterator(start, end, "-").toArray()
17
+ * // ["2026-04-17", "2026-04-16", ...]
18
+ *
19
+ * @example for..of
20
+ * for (const d of new DateIterator(start, end, "/")) console.log(d)
21
+ */
22
+ class DateIterator {
23
+ constructor(start, end, separator = "") {
24
+ if (start.lessThan(end)) {
25
+ throw new Error('DateIterator: `start` must be newer than or equal to `end`');
26
+ }
27
+ // Defensive clones: callers' instances must not be mutated, and
28
+ // Unixtime.addDays() mutates the underlying Date.
29
+ this.start = start.clone();
30
+ this.end = end.clone();
31
+ this.separator = separator;
32
+ this.cursor = this.start.clone();
33
+ }
34
+ /** Returns the next formatted date going backwards, or `null` when exhausted. */
35
+ next() {
36
+ if (this.cursor === null)
37
+ return null;
38
+ if (!this.cursor.greaterThan(this.end)) {
39
+ this.cursor = null;
40
+ return null;
41
+ }
42
+ const value = this.cursor.yyyymmdd(this.separator);
43
+ this.cursor.addDays(-1);
44
+ return value;
45
+ }
46
+ /** Reset the iterator so it can be consumed again from `start`. */
47
+ reset() {
48
+ this.cursor = this.start.clone();
49
+ return this;
50
+ }
51
+ /** Collect all remaining dates into an array. */
52
+ toArray() {
53
+ const out = [];
54
+ let d;
55
+ while ((d = this.next()) !== null)
56
+ out.push(d);
57
+ return out;
58
+ }
59
+ [Symbol.iterator]() {
60
+ let cursor = this.start.clone();
61
+ const end = this.end;
62
+ const sep = this.separator;
63
+ return {
64
+ next() {
65
+ if (cursor === null || !cursor.greaterThan(end)) {
66
+ return { value: undefined, done: true };
67
+ }
68
+ const value = cursor.yyyymmdd(sep);
69
+ cursor.addDays(-1);
70
+ return { value, done: false };
71
+ }
72
+ };
73
+ }
74
+ }
75
+ exports.DateIterator = DateIterator;
76
+ //# sourceMappingURL=DateIterator.js.map
@@ -0,0 +1,149 @@
1
+ import { DateIterator } from './DateIterator';
2
+ import { Unixtime } from './Unixtime';
3
+
4
+ describe('DateIterator', () => {
5
+ const start = () => Unixtime.fromYYYYMMDD('20240110');
6
+ const end = () => Unixtime.fromYYYYMMDD('20240105');
7
+
8
+ it('should iterate backwards day-by-day (end-exclusive) with default separator', () => {
9
+ const it = new DateIterator(start(), end());
10
+ expect(it.toArray()).toEqual([
11
+ '20240110',
12
+ '20240109',
13
+ '20240108',
14
+ '20240107',
15
+ '20240106',
16
+ ]);
17
+ });
18
+
19
+ it('should honor a custom separator', () => {
20
+ const it = new DateIterator(start(), end(), '-');
21
+ expect(it.toArray()).toEqual([
22
+ '2024-01-10',
23
+ '2024-01-09',
24
+ '2024-01-08',
25
+ '2024-01-07',
26
+ '2024-01-06',
27
+ ]);
28
+ });
29
+
30
+ it('should honor a slash separator', () => {
31
+ const it = new DateIterator(start(), end(), '/');
32
+ expect(it.toArray()).toEqual([
33
+ '2024/01/10',
34
+ '2024/01/09',
35
+ '2024/01/08',
36
+ '2024/01/07',
37
+ '2024/01/06',
38
+ ]);
39
+ });
40
+
41
+ it('next() should return null when exhausted', () => {
42
+ const s = Unixtime.fromYYYYMMDD('20240103');
43
+ const e = Unixtime.fromYYYYMMDD('20240101');
44
+ const it = new DateIterator(s, e);
45
+ expect(it.next()).toEqual('20240103');
46
+ expect(it.next()).toEqual('20240102');
47
+ expect(it.next()).toBeNull();
48
+ expect(it.next()).toBeNull();
49
+ });
50
+
51
+ it('should return empty array when start equals end (end-exclusive)', () => {
52
+ const s = Unixtime.fromYYYYMMDD('20240105');
53
+ const e = Unixtime.fromYYYYMMDD('20240105');
54
+ const it = new DateIterator(s, e);
55
+ expect(it.toArray()).toEqual([]);
56
+ expect(it.next()).toBeNull();
57
+ });
58
+
59
+ it('should throw when start is older than end', () => {
60
+ const s = Unixtime.fromYYYYMMDD('20240101');
61
+ const e = Unixtime.fromYYYYMMDD('20240110');
62
+ expect(() => new DateIterator(s, e)).toThrow(
63
+ 'DateIterator: `start` must be newer than or equal to `end`'
64
+ );
65
+ });
66
+
67
+ it('should not mutate the caller-supplied Unixtime instances', () => {
68
+ const s = Unixtime.fromYYYYMMDD('20240110');
69
+ const e = Unixtime.fromYYYYMMDD('20240105');
70
+ const sBefore = s.toLongUnixtime();
71
+ const eBefore = e.toLongUnixtime();
72
+ const it = new DateIterator(s, e);
73
+ it.toArray();
74
+ expect(s.toLongUnixtime()).toEqual(sBefore);
75
+ expect(e.toLongUnixtime()).toEqual(eBefore);
76
+ });
77
+
78
+ it('reset() should allow the iterator to be consumed again', () => {
79
+ const it = new DateIterator(start(), end());
80
+ const first = it.toArray();
81
+ expect(it.next()).toBeNull();
82
+ it.reset();
83
+ const second = it.toArray();
84
+ expect(second).toEqual(first);
85
+ });
86
+
87
+ it('reset() should return the iterator for chaining', () => {
88
+ const it = new DateIterator(start(), end());
89
+ expect(it.reset()).toBe(it);
90
+ });
91
+
92
+ it('should be iterable via for..of', () => {
93
+ const it = new DateIterator(start(), end(), '-');
94
+ const out: string[] = [];
95
+ for (const d of it) out.push(d);
96
+ expect(out).toEqual([
97
+ '2024-01-10',
98
+ '2024-01-09',
99
+ '2024-01-08',
100
+ '2024-01-07',
101
+ '2024-01-06',
102
+ ]);
103
+ });
104
+
105
+ it('for..of should be independent of next()/toArray() cursor', () => {
106
+ const it = new DateIterator(start(), end());
107
+ expect(it.next()).toEqual('20240110');
108
+ const collected: string[] = [];
109
+ for (const d of it) collected.push(d);
110
+ expect(collected).toEqual([
111
+ '20240110',
112
+ '20240109',
113
+ '20240108',
114
+ '20240107',
115
+ '20240106',
116
+ ]);
117
+ expect(it.next()).toEqual('20240109');
118
+ });
119
+
120
+ it('should support spread syntax', () => {
121
+ const it = new DateIterator(
122
+ Unixtime.fromYYYYMMDD('20240103'),
123
+ Unixtime.fromYYYYMMDD('20240101')
124
+ );
125
+ expect([...it]).toEqual(['20240103', '20240102']);
126
+ });
127
+
128
+ it('should cross month boundaries correctly', () => {
129
+ const s = Unixtime.fromYYYYMMDD('20240302');
130
+ const e = Unixtime.fromYYYYMMDD('20240227');
131
+ const it = new DateIterator(s, e, '-');
132
+ expect(it.toArray()).toEqual([
133
+ '2024-03-02',
134
+ '2024-03-01',
135
+ '2024-02-29',
136
+ '2024-02-28',
137
+ ]);
138
+ });
139
+
140
+ it('should produce N entries for a span of N+1 days (end-exclusive)', () => {
141
+ const s = Unixtime.fromYYYYMMDD('20240620');
142
+ const e = Unixtime.fromYYYYMMDD('20240610');
143
+ const it = new DateIterator(s, e);
144
+ const arr = it.toArray();
145
+ expect(arr.length).toEqual(10);
146
+ expect(arr[0]).toEqual('20240620');
147
+ expect(arr[arr.length - 1]).toEqual('20240611');
148
+ });
149
+ });
@@ -0,0 +1,80 @@
1
+ import { Unixtime } from "./Unixtime"
2
+
3
+ /**
4
+ * Iterates day-by-day backwards from `start` toward `end` (end-exclusive),
5
+ * emitting each date as a `yyyy{sep}mm{sep}dd` string.
6
+ *
7
+ * @example
8
+ * const it = new DateIterator(Unixtime.now(), Unixtime.now().addDays(-5))
9
+ * it.next() // "20260417"
10
+ * it.next() // "20260416"
11
+ * // ...
12
+ * it.next() // null
13
+ *
14
+ * @example separator
15
+ * new DateIterator(start, end, "-").toArray()
16
+ * // ["2026-04-17", "2026-04-16", ...]
17
+ *
18
+ * @example for..of
19
+ * for (const d of new DateIterator(start, end, "/")) console.log(d)
20
+ */
21
+ export class DateIterator implements Iterable<string> {
22
+ private readonly start: Unixtime
23
+ private readonly end: Unixtime
24
+ private readonly separator: string
25
+ private cursor: Unixtime | null
26
+
27
+ constructor(start: Unixtime, end: Unixtime, separator: string = "") {
28
+ if (start.lessThan(end)) {
29
+ throw new Error('DateIterator: `start` must be newer than or equal to `end`')
30
+ }
31
+ // Defensive clones: callers' instances must not be mutated, and
32
+ // Unixtime.addDays() mutates the underlying Date.
33
+ this.start = start.clone()
34
+ this.end = end.clone()
35
+ this.separator = separator
36
+ this.cursor = this.start.clone()
37
+ }
38
+
39
+ /** Returns the next formatted date going backwards, or `null` when exhausted. */
40
+ next(): string | null {
41
+ if (this.cursor === null) return null
42
+ if (!this.cursor.greaterThan(this.end)) {
43
+ this.cursor = null
44
+ return null
45
+ }
46
+ const value = this.cursor.yyyymmdd(this.separator)
47
+ this.cursor.addDays(-1)
48
+ return value
49
+ }
50
+
51
+ /** Reset the iterator so it can be consumed again from `start`. */
52
+ reset(): this {
53
+ this.cursor = this.start.clone()
54
+ return this
55
+ }
56
+
57
+ /** Collect all remaining dates into an array. */
58
+ toArray(): string[] {
59
+ const out: string[] = []
60
+ let d: string | null
61
+ while ((d = this.next()) !== null) out.push(d)
62
+ return out
63
+ }
64
+
65
+ [Symbol.iterator](): Iterator<string> {
66
+ let cursor: Unixtime | null = this.start.clone()
67
+ const end = this.end
68
+ const sep = this.separator
69
+ return {
70
+ next(): IteratorResult<string> {
71
+ if (cursor === null || !cursor.greaterThan(end)) {
72
+ return { value: undefined as unknown as string, done: true }
73
+ }
74
+ const value = cursor.yyyymmdd(sep)
75
+ cursor.addDays(-1)
76
+ return { value, done: false }
77
+ }
78
+ }
79
+ }
80
+ }
@@ -15,7 +15,7 @@ describe('OTPGenerator', () => {
15
15
  it('should generate different OTPs in milliseconds', async () => {
16
16
  const otpGenerator = new OTPGenerator(passkey, 5);
17
17
  const otp1 = otpGenerator.generateOTP()
18
- await wait(1/1000);
18
+ await wait(3/1000);
19
19
  const otp2 = otpGenerator.generateOTP();
20
20
  expect(otp1).not.toBe(otp2);
21
21
  })