@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.
@@ -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"}
@@ -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
- "name": "@oirnoir/util",
3
- "version": "0.0.60",
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
- "scripts": {
24
- "build": "esbuild src/index.ts --bundle --outfile=dist/index.js --format=esm --platform=node --external:timestring && tsc -p tsconfig.json",
25
- "typecheck": "tsc --noEmit -p tsconfig.json",
26
- "lint": "biome check . --write && biome lint",
27
- "test": "node --test test/*.ts",
28
- "publish:npm": "pnpm publish",
29
- "publish:jsr": "deno publish --token $JSR_TOKEN"
30
- },
31
- "dependencies": {
32
- "timestring": "^7.0.0"
33
- },
34
- "devDependencies": {
35
- "@biomejs/biome": "2.6.2",
36
- "@types/node": "26.6.6",
37
- "@types/timestring": "^7.0.0",
38
- "esbuild": "0.25.12",
39
- "typescript": "5.9.3"
40
- },
41
- "publishConfig": {
42
- "access": "public"
43
- },
44
- "packageManager": "pnpm@12.16.1"
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
+ }