mybase 1.1.51 → 1.2.2
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/ip6addr.d.ts +10 -1
- package/jest.config.js +8 -1
- package/mybase.d.ts +57 -0
- package/mybase.js +401 -684
- package/mybase.test.ts +647 -0
- package/mybase.ts +397 -0
- package/package.json +3 -2
- package/ts/funcs/Geoip2Paths.js +7 -5
- package/ts/funcs/Geoip2Paths.ts +6 -4
- package/ts/funcs/asJSON.d.ts +1 -1
- package/ts/funcs/asJSON.js +6 -4
- package/ts/funcs/asJSON.ts +8 -5
- package/ts/funcs/hash_sha512.d.ts +1 -1
- package/ts/funcs/hash_sha512.js +4 -4
- package/ts/funcs/hash_sha512.ts +3 -4
- package/ts/funcs/isLANIp.d.ts +2 -3
- package/ts/funcs/isLANIp.js +14 -15
- package/ts/funcs/isLANIp.test.ts +7 -8
- package/ts/funcs/isLANIp.ts +25 -28
- package/ts/funcs/isLoopbackIP.d.ts +2 -3
- package/ts/funcs/isLoopbackIP.js +15 -16
- package/ts/funcs/isLoopbackIP.test.ts +7 -7
- package/ts/funcs/isLoopbackIP.ts +21 -23
- package/ts/funcs/validEmail.d.ts +1 -1
- package/ts/funcs/validEmail.js +0 -3
- package/ts/funcs/validEmail.ts +1 -3
- package/ts/funcs/vaultFill.js +1 -1
- package/ts/funcs/vaultFill.ts +1 -1
- package/ts/funcs/vaultRead.js +9 -3
- package/ts/funcs/vaultRead.ts +8 -3
- package/ts/index.d.ts +1 -0
- package/ts/index.js +1 -0
- package/ts/index.ts +1 -1
- package/ts/models/DateIterator.d.ts +33 -0
- package/ts/models/DateIterator.js +76 -0
- package/ts/models/DateIterator.test.ts +149 -0
- package/ts/models/DateIterator.ts +80 -0
- package/ts/models/IPAddress.d.ts +13 -13
- package/ts/models/IPAddress.ts +4 -4
- package/ts/models/OTPGenerator.test.ts +1 -1
- package/ts/types.d.ts +35 -0
- package/ts/types.js +1 -0
- package/ts/types.ts +42 -1
- package/tsconfig.jest.json +11 -0
- package/tsconfig.json +2 -1
- package/types/third-party.d.ts +21 -0
package/ts/funcs/isLANIp.ts
CHANGED
|
@@ -2,36 +2,33 @@ import ip6addr from '@7c/node-ip6addr'
|
|
|
2
2
|
import net from 'net'
|
|
3
3
|
|
|
4
4
|
export const private_network_cidrs = [
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
ip6addr.createCIDR('10.0.0.0/8'),
|
|
6
|
+
ip6addr.createCIDR('172.16.0.0/12'),
|
|
7
|
+
ip6addr.createCIDR('192.168.0.0/16'),
|
|
8
|
+
ip6addr.createCIDR('fd00::/8'), // Reserved by IETF for future use, but not currently in active use.
|
|
9
|
+
ip6addr.createCIDR('fc00::/8'), // The range currently in use for local communications within a site or organization.
|
|
10
10
|
]
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
// speed optimized
|
|
17
|
-
let ipVersion = net.isIPv4(ip) ? 'ipv4' : 'ipv6'
|
|
18
|
-
for (let cidr of private_network_cidrs) {
|
|
19
|
-
if (!cidr) continue
|
|
20
|
-
|
|
21
|
-
let first = cidr.first()
|
|
22
|
-
if (first.kind() !== ipVersion) continue
|
|
23
|
-
if (cidr.contains(ip))
|
|
24
|
-
return true
|
|
25
|
-
}
|
|
26
|
-
} catch (err) {
|
|
27
|
-
console.log(err)
|
|
28
|
-
}
|
|
29
|
-
return false
|
|
12
|
+
function normalizeIp(ip: string): string | false {
|
|
13
|
+
if (net.isIP(ip) === 0) return false
|
|
14
|
+
if (net.isIPv6(ip)) return ip.toLowerCase().replace(/^::ffff:/, '')
|
|
15
|
+
return ip
|
|
30
16
|
}
|
|
31
17
|
|
|
32
|
-
function
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
18
|
+
export function isLANIp(ip: string): boolean | null {
|
|
19
|
+
const n = normalizeIp(ip)
|
|
20
|
+
if (n === false || net.isIP(n) === 0) return false
|
|
21
|
+
try {
|
|
22
|
+
const ipVersion = net.isIPv4(n) ? 'ipv4' : 'ipv6'
|
|
23
|
+
for (const cidr of private_network_cidrs) {
|
|
24
|
+
if (!cidr) continue
|
|
25
|
+
const first = cidr.first()
|
|
26
|
+
if (first.kind() !== ipVersion) continue
|
|
27
|
+
if (cidr.contains(n)) return true
|
|
28
|
+
}
|
|
29
|
+
return null
|
|
30
|
+
} catch (err) {
|
|
31
|
+
console.log(err)
|
|
32
|
+
}
|
|
33
|
+
return false
|
|
37
34
|
}
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export declare
|
|
3
|
-
export declare function isLoopbackIP(ip: string): boolean;
|
|
1
|
+
export declare const local_network_cidrs: import("@7c/node-ip6addr").CIDR[];
|
|
2
|
+
export declare function isLoopbackIP(ip: string): boolean | null;
|
package/ts/funcs/isLoopbackIP.js
CHANGED
|
@@ -9,34 +9,33 @@ const node_ip6addr_1 = __importDefault(require("@7c/node-ip6addr"));
|
|
|
9
9
|
const net_1 = __importDefault(require("net"));
|
|
10
10
|
exports.local_network_cidrs = [
|
|
11
11
|
node_ip6addr_1.default.createCIDR('127.0.0.0/8'),
|
|
12
|
-
node_ip6addr_1.default.createCIDR('::1/128')
|
|
12
|
+
node_ip6addr_1.default.createCIDR('::1/128'),
|
|
13
13
|
];
|
|
14
|
-
function
|
|
15
|
-
ip = normalizeIp(ip);
|
|
14
|
+
function normalizeIp(ip) {
|
|
16
15
|
if (net_1.default.isIP(ip) === 0)
|
|
17
16
|
return false;
|
|
17
|
+
if (net_1.default.isIPv6(ip))
|
|
18
|
+
return ip.toLowerCase().replace(/^::ffff:/, '');
|
|
19
|
+
return ip;
|
|
20
|
+
}
|
|
21
|
+
function isLoopbackIP(ip) {
|
|
22
|
+
const n = normalizeIp(ip);
|
|
23
|
+
if (n === false || net_1.default.isIP(n) === 0)
|
|
24
|
+
return false;
|
|
18
25
|
try {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
let first = cidr.first();
|
|
26
|
+
const ipVersion = net_1.default.isIPv4(n) ? 'ipv4' : 'ipv6';
|
|
27
|
+
for (const cidr of exports.local_network_cidrs) {
|
|
28
|
+
const first = cidr.first();
|
|
23
29
|
if (first.kind() !== ipVersion)
|
|
24
30
|
continue;
|
|
25
|
-
if (cidr.contains(
|
|
31
|
+
if (cidr.contains(n))
|
|
26
32
|
return true;
|
|
27
33
|
}
|
|
34
|
+
return null;
|
|
28
35
|
}
|
|
29
36
|
catch (err) {
|
|
30
37
|
console.log(err);
|
|
31
38
|
}
|
|
32
39
|
return false;
|
|
33
40
|
}
|
|
34
|
-
function normalizeIp(ip) {
|
|
35
|
-
// also support ipv6
|
|
36
|
-
if (net_1.default.isIP(ip) === 0)
|
|
37
|
-
return '';
|
|
38
|
-
if (net_1.default.isIPv6(ip))
|
|
39
|
-
return ip.toLowerCase().replace(/^::ffff:/, '');
|
|
40
|
-
return ip;
|
|
41
|
-
}
|
|
42
41
|
//# sourceMappingURL=isLoopbackIP.js.map
|
|
@@ -10,12 +10,12 @@ describe('isLoopbackIP', () => {
|
|
|
10
10
|
expect(isLoopbackIP('::1')).toBe(true);
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
-
test('should return
|
|
14
|
-
expect(isLoopbackIP('192.168.0.1')).
|
|
13
|
+
test('should return null for non-loopback IPv4 address', () => {
|
|
14
|
+
expect(isLoopbackIP('192.168.0.1')).toBeNull();
|
|
15
15
|
});
|
|
16
16
|
|
|
17
|
-
test('should return
|
|
18
|
-
expect(isLoopbackIP('2001:0db8:85a3:0000:0000:8a2e:0370:7334')).
|
|
17
|
+
test('should return null for non-loopback IPv6 address', () => {
|
|
18
|
+
expect(isLoopbackIP('2001:0db8:85a3:0000:0000:8a2e:0370:7334')).toBeNull();
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
test('should return false for invalid IP address', () => {
|
|
@@ -49,12 +49,12 @@ describe('isLoopbackIP', () => {
|
|
|
49
49
|
|
|
50
50
|
test('should return null for non-local IPv4', () => {
|
|
51
51
|
const ip = '8.8.8.8'
|
|
52
|
-
expect(isLoopbackIP(ip)).
|
|
52
|
+
expect(isLoopbackIP(ip)).toBeNull()
|
|
53
53
|
})
|
|
54
54
|
|
|
55
55
|
test('should return null for non-local IPv6', () => {
|
|
56
56
|
const ip = '2001:4860:4860::8888'
|
|
57
|
-
expect(isLoopbackIP(ip)).
|
|
57
|
+
expect(isLoopbackIP(ip)).toBeNull()
|
|
58
58
|
})
|
|
59
59
|
|
|
60
60
|
test('should return false for invalid IP', () => {
|
|
@@ -65,7 +65,7 @@ describe('isLoopbackIP', () => {
|
|
|
65
65
|
test('random ips should not be Loopback', () => {
|
|
66
66
|
for(let i=0;i<10;i++){
|
|
67
67
|
let ip = randomIP()
|
|
68
|
-
expect(isLoopbackIP(ip)).
|
|
68
|
+
expect(isLoopbackIP(ip)).toBeNull()
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
})
|
package/ts/funcs/isLoopbackIP.ts
CHANGED
|
@@ -2,31 +2,29 @@ import ip6addr from '@7c/node-ip6addr'
|
|
|
2
2
|
import net from 'net'
|
|
3
3
|
|
|
4
4
|
export const local_network_cidrs = [
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
ip6addr.createCIDR('127.0.0.0/8'),
|
|
6
|
+
ip6addr.createCIDR('::1/128'),
|
|
7
7
|
]
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
// speed optimized
|
|
14
|
-
let ipVersion = net.isIPv4(ip) ? 'ipv4' : 'ipv6'
|
|
15
|
-
for (let cidr of local_network_cidrs) {
|
|
16
|
-
let first = cidr.first()
|
|
17
|
-
if (first.kind() !== ipVersion) continue
|
|
18
|
-
if (cidr.contains(ip))
|
|
19
|
-
return true
|
|
20
|
-
}
|
|
21
|
-
} catch (err) {
|
|
22
|
-
console.log(err)
|
|
23
|
-
}
|
|
24
|
-
return false
|
|
9
|
+
function normalizeIp(ip: string): string | false {
|
|
10
|
+
if (net.isIP(ip) === 0) return false
|
|
11
|
+
if (net.isIPv6(ip)) return ip.toLowerCase().replace(/^::ffff:/, '')
|
|
12
|
+
return ip
|
|
25
13
|
}
|
|
26
14
|
|
|
27
|
-
function
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
15
|
+
export function isLoopbackIP(ip: string): boolean | null {
|
|
16
|
+
const n = normalizeIp(ip)
|
|
17
|
+
if (n === false || net.isIP(n) === 0) return false
|
|
18
|
+
try {
|
|
19
|
+
const ipVersion = net.isIPv4(n) ? 'ipv4' : 'ipv6'
|
|
20
|
+
for (const cidr of local_network_cidrs) {
|
|
21
|
+
const first = cidr.first()
|
|
22
|
+
if (first.kind() !== ipVersion) continue
|
|
23
|
+
if (cidr.contains(n)) return true
|
|
24
|
+
}
|
|
25
|
+
return null
|
|
26
|
+
} catch (err) {
|
|
27
|
+
console.log(err)
|
|
28
|
+
}
|
|
29
|
+
return false
|
|
32
30
|
}
|
package/ts/funcs/validEmail.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function validEmail(email:
|
|
1
|
+
export declare function validEmail(email: unknown): boolean;
|
package/ts/funcs/validEmail.js
CHANGED
|
@@ -13,8 +13,5 @@ function validEmail(email) {
|
|
|
13
13
|
// taken from https://www.w3resource.com/javascript/form/email-validation.php
|
|
14
14
|
// strange looking emails might be indeed valid
|
|
15
15
|
// check https://www.w3resource.com/javascript/form/example-javascript-form-validation-email-REC-2822.html
|
|
16
|
-
if (typeof email === 'string')
|
|
17
|
-
email = email.toLowerCase().trim();
|
|
18
|
-
return (/(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/).test(email);
|
|
19
16
|
}
|
|
20
17
|
//# sourceMappingURL=validEmail.js.map
|
package/ts/funcs/validEmail.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import validator from 'validator'
|
|
2
2
|
|
|
3
|
-
export function validEmail(email:
|
|
3
|
+
export function validEmail(email: unknown): boolean {
|
|
4
4
|
if (typeof email==='string')
|
|
5
5
|
return validator.isEmail(email) // validator needs a string
|
|
6
6
|
return false
|
|
@@ -8,6 +8,4 @@ export function validEmail(email:string) {
|
|
|
8
8
|
// taken from https://www.w3resource.com/javascript/form/email-validation.php
|
|
9
9
|
// strange looking emails might be indeed valid
|
|
10
10
|
// check https://www.w3resource.com/javascript/form/example-javascript-form-validation-email-REC-2822.html
|
|
11
|
-
if (typeof email==='string') email=email.toLowerCase().trim()
|
|
12
|
-
return (/(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/).test(email)
|
|
13
11
|
}
|
package/ts/funcs/vaultFill.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.vaultFill = vaultFill;
|
|
4
4
|
const debug_1 = require("debug");
|
|
5
5
|
const vaultRead_1 = require("./vaultRead");
|
|
6
|
-
const dbg = (0, debug_1.debug)('
|
|
6
|
+
const dbg = (0, debug_1.debug)('mybase');
|
|
7
7
|
function vaultFill(vaultInstance, inputObject, ignoreError = false, keepCache = true) {
|
|
8
8
|
// v2.1
|
|
9
9
|
// fills all strings with ^vault@/secret/... with proper vault values
|
package/ts/funcs/vaultFill.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { debug } from "debug"
|
|
2
2
|
import nodeVault from "node-vault";
|
|
3
3
|
import { vaultRead } from "./vaultRead";
|
|
4
|
-
const dbg = debug('
|
|
4
|
+
const dbg = debug('mybase')
|
|
5
5
|
|
|
6
6
|
export function vaultFill(vaultInstance: nodeVault.client, inputObject: any, ignoreError = false, keepCache = true) {
|
|
7
7
|
// v2.1
|
package/ts/funcs/vaultRead.js
CHANGED
|
@@ -5,18 +5,23 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.vaultRead = vaultRead;
|
|
7
7
|
const debug_1 = require("debug");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
9
|
const path_1 = __importDefault(require("path"));
|
|
9
10
|
const fs_1 = __importDefault(require("fs"));
|
|
10
11
|
const fs_2 = require("fs");
|
|
11
12
|
const fileCacheIsValid_1 = require("./fileCacheIsValid");
|
|
12
13
|
const global_1 = require("../global");
|
|
13
14
|
const hash_sha512_1 = require("./hash_sha512");
|
|
14
|
-
const dbg = (0, debug_1.debug)('
|
|
15
|
+
const dbg = (0, debug_1.debug)('mybase');
|
|
15
16
|
// need to implement it better
|
|
16
17
|
function vaultRead(vaultInstance, vaultKey, cache_in_minutes = 10) {
|
|
17
18
|
// v2 - supports caching
|
|
18
19
|
return new Promise((resolve, reject) => {
|
|
19
|
-
|
|
20
|
+
const cacheKeyMaterial = `${vaultInstance.endpoint}/${vaultKey}`;
|
|
21
|
+
const cacheHash = (0, hash_sha512_1.hash_sha512)(cacheKeyMaterial);
|
|
22
|
+
if (cacheHash === false)
|
|
23
|
+
return reject(new Error('vault cache key hash failed'));
|
|
24
|
+
let cache_file = path_1.default.join(global_1.vault_cache_folder, cacheHash);
|
|
20
25
|
if (cache_in_minutes > 0 && (0, fileCacheIsValid_1.fileCacheIsValid)(cache_file, cache_in_minutes))
|
|
21
26
|
return resolve(JSON.parse((0, fs_2.readFileSync)(cache_file).toString()));
|
|
22
27
|
vaultInstance.read(vaultKey).then((r) => {
|
|
@@ -33,10 +38,11 @@ function vaultRead(vaultInstance, vaultKey, cache_in_minutes = 10) {
|
|
|
33
38
|
dbg(`vault ${vaultKey} - failed`);
|
|
34
39
|
reject(r);
|
|
35
40
|
}).catch(e => {
|
|
41
|
+
console.log(e);
|
|
36
42
|
dbg(`exception inside vaultRead`, e);
|
|
37
43
|
// we will still return latest information from cache
|
|
38
44
|
try {
|
|
39
|
-
|
|
45
|
+
console.log(chalk_1.default.bgRed(`returning vault@${vaultKey} from cache due to error`));
|
|
40
46
|
if (fs_1.default.existsSync(cache_file))
|
|
41
47
|
return resolve(JSON.parse(fs_1.default.readFileSync(cache_file).toString()));
|
|
42
48
|
}
|
package/ts/funcs/vaultRead.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { debug } from "debug"
|
|
2
|
+
import chalk from "chalk"
|
|
2
3
|
import nodeVault from "node-vault";
|
|
3
4
|
import path from "path";
|
|
4
5
|
import fs from "fs";
|
|
@@ -6,14 +7,17 @@ import { readFileSync } from 'fs';
|
|
|
6
7
|
import { fileCacheIsValid } from "./fileCacheIsValid";
|
|
7
8
|
import { vault_cache_folder } from "../global";
|
|
8
9
|
import { hash_sha512 } from "./hash_sha512";
|
|
9
|
-
const dbg = debug('
|
|
10
|
+
const dbg = debug('mybase')
|
|
10
11
|
|
|
11
12
|
// need to implement it better
|
|
12
13
|
|
|
13
14
|
export function vaultRead(vaultInstance: nodeVault.client, vaultKey: string, cache_in_minutes = 10) : Promise<any> {
|
|
14
15
|
// v2 - supports caching
|
|
15
16
|
return new Promise((resolve, reject) => {
|
|
16
|
-
|
|
17
|
+
const cacheKeyMaterial = `${vaultInstance.endpoint}/${vaultKey}`
|
|
18
|
+
const cacheHash = hash_sha512(cacheKeyMaterial)
|
|
19
|
+
if (cacheHash === false) return reject(new Error('vault cache key hash failed'))
|
|
20
|
+
let cache_file = path.join(vault_cache_folder, cacheHash)
|
|
17
21
|
|
|
18
22
|
if (cache_in_minutes > 0 && fileCacheIsValid(cache_file, cache_in_minutes))
|
|
19
23
|
return resolve(JSON.parse(readFileSync(cache_file as string).toString()))
|
|
@@ -28,10 +32,11 @@ export function vaultRead(vaultInstance: nodeVault.client, vaultKey: string, cac
|
|
|
28
32
|
dbg(`vault ${vaultKey} - failed`)
|
|
29
33
|
reject(r);
|
|
30
34
|
}).catch(e => {
|
|
35
|
+
console.log(e)
|
|
31
36
|
dbg(`exception inside vaultRead`, e)
|
|
32
37
|
// we will still return latest information from cache
|
|
33
38
|
try {
|
|
34
|
-
|
|
39
|
+
console.log(chalk.bgRed(`returning vault@${vaultKey} from cache due to error`))
|
|
35
40
|
if (fs.existsSync(cache_file))
|
|
36
41
|
return resolve(JSON.parse(fs.readFileSync(cache_file as string).toString()))
|
|
37
42
|
} catch (e2) {
|
package/ts/index.d.ts
CHANGED
package/ts/index.js
CHANGED
|
@@ -49,4 +49,5 @@ __exportStar(require("./models/Timespan"), exports);
|
|
|
49
49
|
__exportStar(require("./models/IPAddress"), exports);
|
|
50
50
|
__exportStar(require("./models/OTPGenerator"), exports);
|
|
51
51
|
__exportStar(require("./models/Interfaces"), exports);
|
|
52
|
+
__exportStar(require("./models/DateIterator"), exports);
|
|
52
53
|
//# sourceMappingURL=index.js.map
|
package/ts/index.ts
CHANGED
|
@@ -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
|
+
});
|