@oirnoir/util 0.0.60 → 0.0.62
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/dist/Proxy.d.ts +16 -0
- package/dist/Proxy.d.ts.map +1 -0
- package/dist/Type.d.ts +63 -0
- package/dist/Type.d.ts.map +1 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +425 -0
- package/package.json +43 -44
package/dist/Proxy.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The response object for the request. Includes the
|
|
3
|
+
* response headers from the proxy service.
|
|
4
|
+
*/
|
|
5
|
+
export interface ProxyResponse extends Response {
|
|
6
|
+
proxyHeaders: Headers;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Proxy requests through a specific proprietary proxy.
|
|
10
|
+
*/
|
|
11
|
+
export declare class FetchProxy {
|
|
12
|
+
proxyEndpoint: string;
|
|
13
|
+
constructor(proxyEndpoint: string);
|
|
14
|
+
fetch(endpoint: string | URL, options?: RequestInit): Promise<ProxyResponse>;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=Proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Proxy.d.ts","sourceRoot":"","sources":["../src/Proxy.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,aAAc,SAAQ,QAAQ;IAC9C,YAAY,EAAE,OAAO,CAAC;CACtB;AAED;;GAEG;AACH,qBAAa,UAAU;IACtB,aAAa,EAAE,MAAM,CAAC;gBAEV,aAAa,EAAE,MAAM;IAM3B,KAAK,CACV,QAAQ,EAAE,MAAM,GAAG,GAAG,EACtB,OAAO,GAAE,WAAgB,GACvB,OAAO,CAAC,aAAa,CAAC;CA2CzB"}
|
package/dist/Type.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The class for deep checking Types
|
|
3
|
+
*/
|
|
4
|
+
export declare class Type {
|
|
5
|
+
/**
|
|
6
|
+
* The value to generate a deep Type of
|
|
7
|
+
*/ value: any;
|
|
8
|
+
/**
|
|
9
|
+
* The shallow type of this
|
|
10
|
+
*/
|
|
11
|
+
is: string;
|
|
12
|
+
/**
|
|
13
|
+
* The parent of this type
|
|
14
|
+
*/
|
|
15
|
+
private parent?;
|
|
16
|
+
/**
|
|
17
|
+
* The child keys of this Type
|
|
18
|
+
*/
|
|
19
|
+
private childKeys;
|
|
20
|
+
/**
|
|
21
|
+
* The child values of this Type
|
|
22
|
+
*/
|
|
23
|
+
private childValues;
|
|
24
|
+
/**
|
|
25
|
+
* @param value - The value to generate a deep Type of
|
|
26
|
+
* @param parent - The parent value used in recursion
|
|
27
|
+
*/ constructor(value: any, parent?: Type);
|
|
28
|
+
/**
|
|
29
|
+
* The type string for the children of this Type
|
|
30
|
+
*/
|
|
31
|
+
get childTypes(): string;
|
|
32
|
+
/**
|
|
33
|
+
* The full type string generated.
|
|
34
|
+
*/
|
|
35
|
+
toString(): string;
|
|
36
|
+
/**
|
|
37
|
+
* The subtype to create based on this.value's sub value.
|
|
38
|
+
*/ addValue(value: any): void;
|
|
39
|
+
/**
|
|
40
|
+
* The subtype to create based on this.value's entries.
|
|
41
|
+
*/ private addEntry;
|
|
42
|
+
/**
|
|
43
|
+
* Walks the linked list backwards, for checking circulars.
|
|
44
|
+
*/
|
|
45
|
+
private parents;
|
|
46
|
+
/**
|
|
47
|
+
* Get the deep type name that defines the input.
|
|
48
|
+
*/
|
|
49
|
+
check(): void;
|
|
50
|
+
/**
|
|
51
|
+
* Checks if the value of this Type is a circular reference to any parent.
|
|
52
|
+
*/
|
|
53
|
+
isCircular(): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Resolves the type name that defines the input.
|
|
56
|
+
*/
|
|
57
|
+
static resolve(value: any): string;
|
|
58
|
+
/**
|
|
59
|
+
* Joins the list of child types.
|
|
60
|
+
*/
|
|
61
|
+
static list(values: Map<string, Type>): string;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=Type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Type.d.ts","sourceRoot":"","sources":["../src/Type.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,qBAAa,IAAI;IAChB;;OAEG,CACI,KAAK,EAAE,GAAG,CAAC;IAElB;;OAEG;IACI,EAAE,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,CAAO;IAEtB;;OAEG;IACH,OAAO,CAAC,SAAS,CAAoB;IAErC;;OAEG;IACH,OAAO,CAAC,WAAW,CAAoB;IAEvC;;;OAGG,aACS,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,IAAI;IAQrC;;OAEG;IACH,IAAI,UAAU,IAAI,MAAM,CAMvB;IAED;;OAEG;IACH,QAAQ,IAAI,MAAM;IAKlB;;OAEG,CACI,QAAQ,CAAC,KAAK,EAAE,GAAG;IAK1B;;OAEG,CACH,OAAO,CAAC,QAAQ;IAMhB;;OAEG;IACH,OAAO,CAAE,OAAO;IAMhB;;OAEG;IACH,KAAK;IAYL;;OAEG;IACH,UAAU,IAAI,OAAO;IAMrB;;OAEG;IAEH,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM;IAclC;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,MAAM;CAG9C"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { type ExecException, type ExecOptions } from "node:child_process";
|
|
2
|
+
export * from "./Proxy.ts";
|
|
3
|
+
export * from "./Type.ts";
|
|
4
|
+
/**
|
|
5
|
+
* Adds commas to a number, for pretty human reading
|
|
6
|
+
*/
|
|
7
|
+
export declare function commaNumber(num: number): string;
|
|
8
|
+
/**
|
|
9
|
+
* Represents a parsed user-inputted time.
|
|
10
|
+
* Returned by parseUserInputtedTime.
|
|
11
|
+
*/
|
|
12
|
+
export declare class ParsedTime {
|
|
13
|
+
readonly intervalMs: number;
|
|
14
|
+
readonly timestamp?: number;
|
|
15
|
+
constructor(data: {
|
|
16
|
+
intervalMs: number;
|
|
17
|
+
timestamp?: number;
|
|
18
|
+
});
|
|
19
|
+
get intervalSeconds(): number;
|
|
20
|
+
get string(): string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Parse user-inputted time. Timestamps can be allowed, optionally.
|
|
24
|
+
* relativeTime is used to calculate intervals when timestamps are
|
|
25
|
+
* inputted.
|
|
26
|
+
*/
|
|
27
|
+
export declare function parseUserInputtedTime(rawTimeInterval: string, allowTimestamps?: boolean, relativeTime?: number): ParsedTime;
|
|
28
|
+
/**
|
|
29
|
+
* Converts a quantity of ms to human-readable format
|
|
30
|
+
*/
|
|
31
|
+
export declare function msToHuman(ms: number, includeMilliseconds?: boolean): string;
|
|
32
|
+
/**
|
|
33
|
+
* Converts a quantity of ms to a shorter human-readable format
|
|
34
|
+
*/
|
|
35
|
+
export declare function msToShort(ms: number, includeMilliseconds?: boolean): string;
|
|
36
|
+
/**
|
|
37
|
+
* Generate a random string of a specific length. Uses Math.random().
|
|
38
|
+
*/
|
|
39
|
+
export declare function randomString(length: number, alphabet?: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* Check if a string is entirely numeric.
|
|
42
|
+
*/
|
|
43
|
+
export declare function isNumeric(str: string | undefined | null, options: {
|
|
44
|
+
allowNegative: boolean;
|
|
45
|
+
allowDecimal: boolean;
|
|
46
|
+
}): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Returns a promise that resolves after a given number of milliseconds.
|
|
49
|
+
*/
|
|
50
|
+
export declare function sleep(ms: number): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Asynchronously execute a command-line command.
|
|
53
|
+
*/
|
|
54
|
+
export declare function execAsync(command: string, settings?: ExecOptions): Promise<{
|
|
55
|
+
error: ExecException | null;
|
|
56
|
+
stdout: string;
|
|
57
|
+
stderr: string;
|
|
58
|
+
}>;
|
|
59
|
+
/**
|
|
60
|
+
* A lightweight curl wrapper. Input arguments
|
|
61
|
+
* for curl, and a web API Response object is returned.
|
|
62
|
+
*/
|
|
63
|
+
export declare function curl(...curlArgs: string[]): Promise<Response>;
|
|
64
|
+
/**
|
|
65
|
+
* Parses a raw HTTP response into a fetch Response
|
|
66
|
+
*/
|
|
67
|
+
export declare function parseRawHttpResponse(raw: string): Response;
|
|
68
|
+
/**
|
|
69
|
+
* Await a promise, but reject if the promise takes too long.
|
|
70
|
+
*/
|
|
71
|
+
export declare function withTimeout<T>(promise: Promise<T>, timeout: number): Promise<T>;
|
|
72
|
+
/**
|
|
73
|
+
* Similar to `Array#splice()` but for two Uint8Arrays.
|
|
74
|
+
* Replaces `deleteCount` bytes of `array` starting from `start`
|
|
75
|
+
* with the contents of `toInsert`. If `toInsert` is not given, just deletes `deleteCount`.
|
|
76
|
+
*/
|
|
77
|
+
export declare function spliceUint8Array(array: Uint8Array, start: number, deleteCount: number, toInsert?: Uint8Array): Uint8Array<ArrayBuffer>;
|
|
78
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,aAAa,EAClB,KAAK,WAAW,EAGhB,MAAM,oBAAoB,CAAC;AAK5B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAE1B;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI/C;AAED;;;GAGG;AACH,qBAAa,UAAU;IACtB,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,SAAgB,SAAS,CAAC,EAAE,MAAM,CAAC;gBAEvB,IAAI,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE;IAK5D,IAAI,eAAe,IAAI,MAAM,CAE5B;IAED,IAAI,MAAM,IAAI,MAAM,CAEnB;CACD;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACpC,eAAe,EAAE,MAAM,EACvB,eAAe,UAAQ,EACvB,YAAY,GAAE,MAAmB,GAC/B,UAAU,CAmCZ;AAiBD;;GAEG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,mBAAmB,UAAQ,GAAG,MAAM,CAoCzE;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,mBAAmB,UAAQ,GAAG,MAAM,CA0CzE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC3B,MAAM,EAAE,MAAM,EACd,QAAQ,SAAmE,GACzE,MAAM,CAQR;AAED;;GAEG;AACH,wBAAgB,SAAS,CACxB,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAC9B,OAAO,EAAE;IAAE,aAAa,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAE,GACxD,OAAO,CAiBT;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAI/C;AAED;;GAEG;AACH,wBAAgB,SAAS,CACxB,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC;IACV,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CACf,CAAC,CAMD;AAED;;;GAGG;AACH,wBAAsB,IAAI,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAwBnE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CA6B1D;AAID;;GAEG;AACH,wBAAsB,WAAW,CAAC,CAAC,EAClC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,OAAO,EAAE,MAAM,GACb,OAAO,CAAC,CAAC,CAAC,CAUZ;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC/B,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,UAAU,GACnB,UAAU,CAAC,WAAW,CAAC,CAazB"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import {
|
|
3
|
+
exec,
|
|
4
|
+
spawn
|
|
5
|
+
} from "node:child_process";
|
|
6
|
+
import { setTimeout } from "node:timers";
|
|
7
|
+
import timeString from "timestring";
|
|
8
|
+
|
|
9
|
+
// src/Proxy.ts
|
|
10
|
+
var FetchProxy = class {
|
|
11
|
+
proxyEndpoint;
|
|
12
|
+
constructor(proxyEndpoint) {
|
|
13
|
+
this.proxyEndpoint = proxyEndpoint;
|
|
14
|
+
if (this.proxyEndpoint.length == 0)
|
|
15
|
+
throw new Error("Proxy endpoint must not be a blank string");
|
|
16
|
+
}
|
|
17
|
+
async fetch(endpoint, options = {}) {
|
|
18
|
+
const configuredOptions = {
|
|
19
|
+
method: options.method ?? "GET",
|
|
20
|
+
body: options.body ?? void 0,
|
|
21
|
+
mode: options.mode ?? void 0,
|
|
22
|
+
keepalive: options.keepalive ?? void 0,
|
|
23
|
+
signal: options.signal ?? void 0,
|
|
24
|
+
credentials: options.credentials ?? void 0,
|
|
25
|
+
redirect: "manual",
|
|
26
|
+
headers: {
|
|
27
|
+
endpoint: typeof endpoint == "string" ? endpoint : endpoint.toString(),
|
|
28
|
+
/* cspell: disable-next-line */
|
|
29
|
+
"oproxy-options": JSON.stringify({
|
|
30
|
+
method: options.method ?? "GET",
|
|
31
|
+
mode: options.mode ?? void 0,
|
|
32
|
+
credentials: options.credentials ?? void 0,
|
|
33
|
+
redirect: options.redirect ?? void 0,
|
|
34
|
+
referrer: options.referrer ?? void 0,
|
|
35
|
+
referrerPolicy: options.referrerPolicy ?? void 0,
|
|
36
|
+
integrity: options.integrity ?? void 0,
|
|
37
|
+
keepalive: options.keepalive ?? void 0
|
|
38
|
+
}),
|
|
39
|
+
headers: options.headers == null ? "{}" : JSON.stringify(options.headers)
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const res = await fetch(this.proxyEndpoint, configuredOptions);
|
|
43
|
+
const receivedHeaders = res.headers.has("incomingHeaders") ? JSON.parse(res.headers?.get("incomingHeaders") ?? "") : null;
|
|
44
|
+
const receivedStatus = res.headers.has("receivedStatus") ? Number(res.headers.get("receivedStatus")) : res.status;
|
|
45
|
+
Object.defineProperty(res, "status", { get: () => receivedStatus });
|
|
46
|
+
const proxyHeaders = res.headers;
|
|
47
|
+
Object.defineProperty(res, "proxyHeaders", { get: () => proxyHeaders });
|
|
48
|
+
const resWithHeaders = res;
|
|
49
|
+
if (receivedHeaders != null) {
|
|
50
|
+
Object.defineProperty(resWithHeaders, "headers", {
|
|
51
|
+
get: () => new Headers(receivedHeaders)
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return resWithHeaders;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// src/Type.ts
|
|
59
|
+
var Type = class _Type {
|
|
60
|
+
/**
|
|
61
|
+
* The value to generate a deep Type of
|
|
62
|
+
*/
|
|
63
|
+
// biome-ignore lint/suspicious/noExplicitAny: This can truly be any
|
|
64
|
+
value;
|
|
65
|
+
/**
|
|
66
|
+
* The shallow type of this
|
|
67
|
+
*/
|
|
68
|
+
is;
|
|
69
|
+
/**
|
|
70
|
+
* The parent of this type
|
|
71
|
+
*/
|
|
72
|
+
parent;
|
|
73
|
+
/**
|
|
74
|
+
* The child keys of this Type
|
|
75
|
+
*/
|
|
76
|
+
childKeys;
|
|
77
|
+
/**
|
|
78
|
+
* The child values of this Type
|
|
79
|
+
*/
|
|
80
|
+
childValues;
|
|
81
|
+
/**
|
|
82
|
+
* @param value - The value to generate a deep Type of
|
|
83
|
+
* @param parent - The parent value used in recursion
|
|
84
|
+
*/
|
|
85
|
+
// biome-ignore lint/suspicious/noExplicitAny: This can truly be any
|
|
86
|
+
constructor(value, parent) {
|
|
87
|
+
this.value = value;
|
|
88
|
+
this.is = _Type.resolve(value);
|
|
89
|
+
this.parent = parent;
|
|
90
|
+
this.childKeys = /* @__PURE__ */ new Map();
|
|
91
|
+
this.childValues = /* @__PURE__ */ new Map();
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The type string for the children of this Type
|
|
95
|
+
*/
|
|
96
|
+
get childTypes() {
|
|
97
|
+
if (!this.childValues.size) return "";
|
|
98
|
+
return `<${(this.childKeys.size ? `${_Type.list(this.childKeys)}, ` : "") + _Type.list(this.childValues)}>`;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The full type string generated.
|
|
102
|
+
*/
|
|
103
|
+
toString() {
|
|
104
|
+
this.check();
|
|
105
|
+
return this.is + this.childTypes;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The subtype to create based on this.value's sub value.
|
|
109
|
+
*/
|
|
110
|
+
// biome-ignore lint/suspicious/noExplicitAny: This can truly be any
|
|
111
|
+
addValue(value) {
|
|
112
|
+
const child = new _Type(value, this);
|
|
113
|
+
this.childValues.set(child.is, child);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The subtype to create based on this.value's entries.
|
|
117
|
+
*/
|
|
118
|
+
// biome-ignore lint/suspicious/noExplicitAny: This can truly be any
|
|
119
|
+
addEntry([key, value]) {
|
|
120
|
+
const child = new _Type(key, this);
|
|
121
|
+
this.childKeys.set(child.is, child);
|
|
122
|
+
this.addValue(value);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Walks the linked list backwards, for checking circulars.
|
|
126
|
+
*/
|
|
127
|
+
*parents() {
|
|
128
|
+
let current = this;
|
|
129
|
+
while (current = current.parent) yield current;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Get the deep type name that defines the input.
|
|
133
|
+
*/
|
|
134
|
+
check() {
|
|
135
|
+
if (Object.isFrozen(this)) return;
|
|
136
|
+
if (typeof this.value === "object" && this.isCircular())
|
|
137
|
+
this.is = `[Circular:${this.is}]`;
|
|
138
|
+
else if (this.value instanceof Map)
|
|
139
|
+
for (const entry of this.value) this.addEntry(entry);
|
|
140
|
+
else if (Array.isArray(this.value) || this.value instanceof Set)
|
|
141
|
+
for (const value of this.value) this.addValue(value);
|
|
142
|
+
else if (this.is === "Object") this.is = "any";
|
|
143
|
+
Object.freeze(this);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Checks if the value of this Type is a circular reference to any parent.
|
|
147
|
+
*/
|
|
148
|
+
isCircular() {
|
|
149
|
+
for (const parent of this.parents())
|
|
150
|
+
if (parent.value === this.value) return true;
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Resolves the type name that defines the input.
|
|
155
|
+
*/
|
|
156
|
+
// biome-ignore lint/suspicious/noExplicitAny: This can truly be any
|
|
157
|
+
static resolve(value) {
|
|
158
|
+
const type = typeof value;
|
|
159
|
+
switch (type) {
|
|
160
|
+
case "object":
|
|
161
|
+
return value === null ? "null" : value.constructor?.name || "any";
|
|
162
|
+
case "function":
|
|
163
|
+
return `${value.constructor.name}(${value.length}-arity)`;
|
|
164
|
+
case "undefined":
|
|
165
|
+
return "void";
|
|
166
|
+
default:
|
|
167
|
+
return type;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Joins the list of child types.
|
|
172
|
+
*/
|
|
173
|
+
static list(values) {
|
|
174
|
+
return values.has("any") ? "any" : [...values.values()].sort().join(" | ");
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// src/index.ts
|
|
179
|
+
function commaNumber(num) {
|
|
180
|
+
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
181
|
+
}
|
|
182
|
+
var ParsedTime = class {
|
|
183
|
+
intervalMs;
|
|
184
|
+
timestamp;
|
|
185
|
+
constructor(data) {
|
|
186
|
+
this.intervalMs = data.intervalMs;
|
|
187
|
+
this.timestamp = data.timestamp;
|
|
188
|
+
}
|
|
189
|
+
get intervalSeconds() {
|
|
190
|
+
return Math.trunc(this.intervalMs / 1e3);
|
|
191
|
+
}
|
|
192
|
+
get string() {
|
|
193
|
+
return msToHuman(this.intervalMs);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
function parseUserInputtedTime(rawTimeInterval, allowTimestamps = false, relativeTime = Date.now()) {
|
|
197
|
+
const digits = rawTimeInterval.match(/\d/g);
|
|
198
|
+
if ((rawTimeInterval.endsWith("t") || digits != null && digits.length > 0 && digits.join("") == rawTimeInterval) && allowTimestamps) {
|
|
199
|
+
let timestampString = rawTimeInterval.match(/\d/g)?.join("");
|
|
200
|
+
if (timestampString == null || !isNumeric(timestampString, { allowDecimal: true, allowNegative: true }))
|
|
201
|
+
timestampString = String(relativeTime);
|
|
202
|
+
const timestamp = timestampString.length == String(relativeTime).length || timestampString.length == String(relativeTime).length + 1 || timestampString.length == String(relativeTime).length - 1 ? Number(timestampString) : timestampString.length == String(relativeTime).length - 3 || timestampString.length == String(relativeTime).length - 2 || timestampString.length == String(relativeTime).length - 4 ? Number(timestampString) * 1e3 : Number(timestampString);
|
|
203
|
+
const timeInterval2 = timestamp - relativeTime;
|
|
204
|
+
return new ParsedTime({ intervalMs: timeInterval2, timestamp });
|
|
205
|
+
}
|
|
206
|
+
let timeInterval;
|
|
207
|
+
try {
|
|
208
|
+
timeInterval = timeString(rawTimeInterval, "ms");
|
|
209
|
+
} catch {
|
|
210
|
+
return { intervalSeconds: 0, intervalMs: 0, string: "" };
|
|
211
|
+
}
|
|
212
|
+
return new ParsedTime({ intervalMs: timeInterval });
|
|
213
|
+
}
|
|
214
|
+
function explodeMs(ms) {
|
|
215
|
+
return {
|
|
216
|
+
milliseconds: Math.trunc(ms % 1e3),
|
|
217
|
+
seconds: Math.trunc(ms / 1e3 % 60),
|
|
218
|
+
minutes: Math.trunc(ms / 6e4 % 60),
|
|
219
|
+
hours: Math.trunc(ms / 36e5 % 24),
|
|
220
|
+
days: Math.trunc(ms / 864e5 % 7),
|
|
221
|
+
weeks: Math.trunc(ms / 6048e5 % 52),
|
|
222
|
+
years: Math.trunc(ms / 314496e5)
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function msToHuman(ms, includeMilliseconds = false) {
|
|
226
|
+
const { milliseconds, seconds, minutes, hours, days, weeks, years } = explodeMs(ms);
|
|
227
|
+
const durationComponents = [];
|
|
228
|
+
if (years > 0) {
|
|
229
|
+
durationComponents.push(`${years} year${years == 1 ? "" : "s"}`);
|
|
230
|
+
}
|
|
231
|
+
if (weeks > 0) {
|
|
232
|
+
durationComponents.push(`${weeks} week${weeks == 1 ? "" : "s"}`);
|
|
233
|
+
}
|
|
234
|
+
if (days > 0) {
|
|
235
|
+
durationComponents.push(`${days} day${days == 1 ? "" : "s"}`);
|
|
236
|
+
}
|
|
237
|
+
if (hours > 0) {
|
|
238
|
+
durationComponents.push(`${hours} hour${hours == 1 ? "" : "s"}`);
|
|
239
|
+
}
|
|
240
|
+
if (minutes > 0) {
|
|
241
|
+
durationComponents.push(`${minutes} minute${minutes == 1 ? "" : "s"}`);
|
|
242
|
+
}
|
|
243
|
+
if (seconds > 0) {
|
|
244
|
+
durationComponents.push(`${seconds} second${seconds == 1 ? "" : "s"}`);
|
|
245
|
+
}
|
|
246
|
+
if (milliseconds > 0 && includeMilliseconds) {
|
|
247
|
+
durationComponents.push(
|
|
248
|
+
`${milliseconds} millisecond${milliseconds == 1 ? "" : "s"}`
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
if (durationComponents.length == 0)
|
|
252
|
+
durationComponents.push(
|
|
253
|
+
includeMilliseconds ? "no time" : "less than a second"
|
|
254
|
+
);
|
|
255
|
+
const durationWords = durationComponents.length >= 3 ? `${durationComponents.slice(0, -1).join(", ")}, and ${durationComponents.at(-1)}` : durationComponents.join(" and ");
|
|
256
|
+
return durationWords;
|
|
257
|
+
}
|
|
258
|
+
function msToShort(ms, includeMilliseconds = false) {
|
|
259
|
+
const { milliseconds, seconds, minutes, hours, days, weeks, years } = explodeMs(ms);
|
|
260
|
+
let twoDigitMin = String(minutes);
|
|
261
|
+
while (twoDigitMin.length < 2) {
|
|
262
|
+
twoDigitMin = `0${twoDigitMin}`;
|
|
263
|
+
}
|
|
264
|
+
let twoDigitSec = String(seconds);
|
|
265
|
+
while (twoDigitSec.length < 2) {
|
|
266
|
+
twoDigitSec = `0${twoDigitSec}`;
|
|
267
|
+
}
|
|
268
|
+
let threeDigitMs = String(milliseconds);
|
|
269
|
+
while (threeDigitMs.length < 3) {
|
|
270
|
+
threeDigitMs = `0${threeDigitMs}`;
|
|
271
|
+
}
|
|
272
|
+
const durationComponents = [];
|
|
273
|
+
let topFound = false;
|
|
274
|
+
if (years > 0 || topFound) {
|
|
275
|
+
topFound = true;
|
|
276
|
+
durationComponents.push(`${years}y`);
|
|
277
|
+
}
|
|
278
|
+
if (weeks > 0 || topFound) {
|
|
279
|
+
topFound = true;
|
|
280
|
+
durationComponents.push(`${weeks}w`);
|
|
281
|
+
}
|
|
282
|
+
if (days > 0 || topFound) {
|
|
283
|
+
topFound = true;
|
|
284
|
+
durationComponents.push(`${days}d `);
|
|
285
|
+
}
|
|
286
|
+
if (hours > 0 || topFound) {
|
|
287
|
+
topFound = true;
|
|
288
|
+
durationComponents.push(`${hours}:`);
|
|
289
|
+
}
|
|
290
|
+
if (minutes > 0 || topFound) {
|
|
291
|
+
durationComponents.push(`${topFound ? twoDigitMin : minutes}:${twoDigitSec}`);
|
|
292
|
+
if (includeMilliseconds) durationComponents.push(`.${threeDigitMs}`);
|
|
293
|
+
} else {
|
|
294
|
+
durationComponents.push(`${seconds}`);
|
|
295
|
+
if (includeMilliseconds) durationComponents.push(`.${threeDigitMs}`);
|
|
296
|
+
durationComponents.push("s");
|
|
297
|
+
}
|
|
298
|
+
return durationComponents.join("");
|
|
299
|
+
}
|
|
300
|
+
function randomString(length, alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") {
|
|
301
|
+
let result = "";
|
|
302
|
+
let counter = 0;
|
|
303
|
+
while (counter < length) {
|
|
304
|
+
result += alphabet.charAt(Math.floor(Math.random() * alphabet.length));
|
|
305
|
+
counter += 1;
|
|
306
|
+
}
|
|
307
|
+
return result;
|
|
308
|
+
}
|
|
309
|
+
function isNumeric(str, options) {
|
|
310
|
+
if (str == null) return false;
|
|
311
|
+
let regex;
|
|
312
|
+
if (options.allowNegative) {
|
|
313
|
+
if (options.allowDecimal) {
|
|
314
|
+
regex = /^-?\d+(\.\d+)?$/;
|
|
315
|
+
} else {
|
|
316
|
+
regex = /^-?\d+$/;
|
|
317
|
+
}
|
|
318
|
+
} else {
|
|
319
|
+
if (options.allowDecimal) {
|
|
320
|
+
regex = /^\d+(\.\d+)?$/;
|
|
321
|
+
} else {
|
|
322
|
+
regex = /^\d+$/;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return regex.test(str);
|
|
326
|
+
}
|
|
327
|
+
function sleep(ms) {
|
|
328
|
+
return new Promise((resolve) => {
|
|
329
|
+
setTimeout(resolve, ms);
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
function execAsync(command, settings) {
|
|
333
|
+
return new Promise((resolve) => {
|
|
334
|
+
exec(command, settings, (error, stdout, stderr) => {
|
|
335
|
+
resolve({ error, stdout, stderr });
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
async function curl(...curlArgs) {
|
|
340
|
+
const child = spawn("curl", ["-isSL", ...curlArgs]);
|
|
341
|
+
const stdoutChunks = [];
|
|
342
|
+
const stderrChunks = [];
|
|
343
|
+
child.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
|
|
344
|
+
child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
|
|
345
|
+
const code = await new Promise((resolve, reject) => {
|
|
346
|
+
child.on("close", (c) => resolve(c ?? -1));
|
|
347
|
+
child.on("error", reject);
|
|
348
|
+
});
|
|
349
|
+
const stdoutText = new TextDecoder().decode(Buffer.concat(stdoutChunks));
|
|
350
|
+
const stderrText = new TextDecoder().decode(Buffer.concat(stderrChunks));
|
|
351
|
+
if (code !== 0) {
|
|
352
|
+
console.error("Curl stderr:");
|
|
353
|
+
console.error(stderrText);
|
|
354
|
+
throw new Error(`Curl exited with code ${code}`);
|
|
355
|
+
}
|
|
356
|
+
return parseRawHttpResponse(stdoutText);
|
|
357
|
+
}
|
|
358
|
+
function parseRawHttpResponse(raw) {
|
|
359
|
+
const [headerPart, ...bodyParts] = raw.split(/\r?\n\r?\n/);
|
|
360
|
+
const body = bodyParts.join("\n\n");
|
|
361
|
+
const lines = headerPart.split(/\r?\n/);
|
|
362
|
+
const statusLine = lines.shift();
|
|
363
|
+
if (statusLine === void 0)
|
|
364
|
+
throw new Error("Curl HTTP Response Syntax Error: No status line!");
|
|
365
|
+
const [, statusCode, ...statusTextParts] = statusLine.split(" ");
|
|
366
|
+
const status = Number(statusCode);
|
|
367
|
+
const statusText = statusTextParts.join(" ");
|
|
368
|
+
const headers = new Headers();
|
|
369
|
+
for (const line of lines) {
|
|
370
|
+
const idx = line.indexOf(":");
|
|
371
|
+
if (idx !== -1) {
|
|
372
|
+
headers.append(line.slice(0, idx).trim(), line.slice(idx + 1).trim());
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
const isNullBodyStatus = status === 101 || status === 204 || status === 205 || status === 304;
|
|
376
|
+
return new Response(isNullBodyStatus ? null : body, {
|
|
377
|
+
status,
|
|
378
|
+
statusText,
|
|
379
|
+
headers
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
var PrivateTimeoutIndicator = class {
|
|
383
|
+
};
|
|
384
|
+
async function withTimeout(promise, timeout) {
|
|
385
|
+
const promises = [
|
|
386
|
+
promise,
|
|
387
|
+
sleep(timeout).then(() => new PrivateTimeoutIndicator())
|
|
388
|
+
];
|
|
389
|
+
const result = await Promise.race(promises);
|
|
390
|
+
if (result instanceof PrivateTimeoutIndicator) {
|
|
391
|
+
throw new Error("Timeout reached when awaiting promise resolution!");
|
|
392
|
+
}
|
|
393
|
+
return result;
|
|
394
|
+
}
|
|
395
|
+
function spliceUint8Array(array, start, deleteCount, toInsert) {
|
|
396
|
+
const s = Math.max(0, Math.min(Math.trunc(start) || 0, array.length));
|
|
397
|
+
const dc = Math.max(
|
|
398
|
+
0,
|
|
399
|
+
Math.min(Math.trunc(deleteCount) || 0, array.length - s)
|
|
400
|
+
);
|
|
401
|
+
const output = new Uint8Array(array.length - dc + (toInsert?.length ?? 0));
|
|
402
|
+
const before = array.slice(0, s);
|
|
403
|
+
const after = array.slice(s + dc, array.length);
|
|
404
|
+
output.set(before);
|
|
405
|
+
if (toInsert != void 0) output.set(toInsert, s);
|
|
406
|
+
output.set(after, s + (toInsert?.length ?? 0));
|
|
407
|
+
return output;
|
|
408
|
+
}
|
|
409
|
+
export {
|
|
410
|
+
FetchProxy,
|
|
411
|
+
ParsedTime,
|
|
412
|
+
Type,
|
|
413
|
+
commaNumber,
|
|
414
|
+
curl,
|
|
415
|
+
execAsync,
|
|
416
|
+
isNumeric,
|
|
417
|
+
msToHuman,
|
|
418
|
+
msToShort,
|
|
419
|
+
parseRawHttpResponse,
|
|
420
|
+
parseUserInputtedTime,
|
|
421
|
+
randomString,
|
|
422
|
+
sleep,
|
|
423
|
+
spliceUint8Array,
|
|
424
|
+
withTimeout
|
|
425
|
+
};
|
package/package.json
CHANGED
|
@@ -1,45 +1,44 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
2
|
+
"name": "@oirnoir/util",
|
|
3
|
+
"version": "0.0.62",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "Utility methods for Node.js and Deno",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=26"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"deno": "./src/index.ts",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"main": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"src"
|
|
22
|
+
],
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"timestring": "^7.0.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@biomejs/biome": "2.6.2",
|
|
28
|
+
"@types/node": "26.6.6",
|
|
29
|
+
"@types/timestring": "^7.0.0",
|
|
30
|
+
"esbuild": "0.25.12",
|
|
31
|
+
"typescript": "5.9.3"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "esbuild src/index.ts --bundle --outfile=dist/index.js --format=esm --platform=node --external:timestring && tsc -p tsconfig.json",
|
|
38
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
39
|
+
"lint": "biome check . --write && biome lint",
|
|
40
|
+
"test": "pnpm run build && node --test test/*.ts",
|
|
41
|
+
"publish:npm": "pnpm publish",
|
|
42
|
+
"publish:jsr": "deno publish --token $JSR_TOKEN"
|
|
43
|
+
}
|
|
44
|
+
}
|