@gtkx/utils 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +373 -0
- package/dist/class.d.ts +16 -0
- package/dist/class.d.ts.map +1 -0
- package/dist/class.js +2 -0
- package/dist/class.js.map +1 -0
- package/dist/collection.d.ts +58 -0
- package/dist/collection.d.ts.map +1 -0
- package/dist/collection.js +98 -0
- package/dist/collection.js.map +1 -0
- package/dist/error.d.ts +13 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +13 -0
- package/dist/error.js.map +1 -0
- package/dist/graceful-shutdown.d.ts +70 -0
- package/dist/graceful-shutdown.d.ts.map +1 -0
- package/dist/graceful-shutdown.js +94 -0
- package/dist/graceful-shutdown.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/source.d.ts +34 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +99 -0
- package/dist/source.js.map +1 -0
- package/dist/string.d.ts +59 -0
- package/dist/string.d.ts.map +1 -0
- package/dist/string.js +73 -0
- package/dist/string.js.map +1 -0
- package/package.json +35 -0
- package/src/class.ts +17 -0
- package/src/collection.ts +103 -0
- package/src/error.ts +12 -0
- package/src/graceful-shutdown.ts +141 -0
- package/src/index.ts +10 -0
- package/src/source.ts +103 -0
- package/src/string.ts +74 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graceful-shutdown.d.ts","sourceRoot":"","sources":["../src/graceful-shutdown.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAAI,QAAQ,MAAM,CAAC,OAAO,GAAG,IAAI,KAAG,MAGjE,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG;IAClC;;;;OAIG;IACH,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,KAAK,MAAM,CAAC;CACjD,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACjC;;;OAGG;IACH,SAAS,EAAE,MAAM,IAAI,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,uBAAuB,GAAI,SAAS,uBAAuB,KAAG,sBA8D1E,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-process graceful shutdown primitive for long-running Node processes.
|
|
3
|
+
*
|
|
4
|
+
* The helper installs `SIGINT`/`SIGTERM`/`SIGHUP` handlers, routes the first
|
|
5
|
+
* delivered signal through a user-supplied close callback, and escalates either
|
|
6
|
+
* on a second `SIGINT` or after a configurable timeout. On completion it calls
|
|
7
|
+
* `process.exit` with the canonical exit code for the signal.
|
|
8
|
+
*/
|
|
9
|
+
const HANDLED_SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
10
|
+
const DEFAULT_FORCE_KILL_TIMEOUT_MS = 5000;
|
|
11
|
+
/**
|
|
12
|
+
* Maps the POSIX signal that ended a process into the exit code shells use to
|
|
13
|
+
* report it. `SIGINT` yields `130` (Ctrl-C), every other tracked signal yields
|
|
14
|
+
* `143` (`SIGTERM`). A `null` signal — i.e. a clean exit — yields `0`.
|
|
15
|
+
*
|
|
16
|
+
* @param signal - The signal name, or `null` for a clean exit.
|
|
17
|
+
* @returns The exit code shells expect for `signal`.
|
|
18
|
+
*/
|
|
19
|
+
export const exitCodeForSignal = (signal) => {
|
|
20
|
+
if (!signal)
|
|
21
|
+
return 0;
|
|
22
|
+
return signal === "SIGINT" ? 130 : 143;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Installs the graceful-shutdown primitive on the current process.
|
|
26
|
+
*
|
|
27
|
+
* On the first matching signal: invokes `onSignal`, optionally schedules
|
|
28
|
+
* `onForce` after `forceKillAfterMs`, awaits the close, then exits. A second
|
|
29
|
+
* `SIGINT` (the canonical "force-kill" gesture) invokes `onForce`
|
|
30
|
+
* immediately. The exit code defaults to {@link exitCodeForSignal} but can
|
|
31
|
+
* be overridden by `exitCode`.
|
|
32
|
+
*
|
|
33
|
+
* @param options - Shutdown behaviour.
|
|
34
|
+
* @returns A handle that detaches the installed signal handlers.
|
|
35
|
+
*/
|
|
36
|
+
export const installGracefulShutdown = (options) => {
|
|
37
|
+
const forceKillMs = options.forceKillAfterMs ?? DEFAULT_FORCE_KILL_TIMEOUT_MS;
|
|
38
|
+
let firstSignal = null;
|
|
39
|
+
let exited = false;
|
|
40
|
+
let forceTimer = null;
|
|
41
|
+
const clearTimer = () => {
|
|
42
|
+
if (forceTimer) {
|
|
43
|
+
clearTimeout(forceTimer);
|
|
44
|
+
forceTimer = null;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const finish = (signal) => {
|
|
48
|
+
if (exited)
|
|
49
|
+
return;
|
|
50
|
+
exited = true;
|
|
51
|
+
clearTimer();
|
|
52
|
+
const code = options.exitCode ? options.exitCode(signal) : exitCodeForSignal(signal);
|
|
53
|
+
process.exit(code);
|
|
54
|
+
};
|
|
55
|
+
const handle = (signal) => {
|
|
56
|
+
if (firstSignal === null) {
|
|
57
|
+
firstSignal = signal;
|
|
58
|
+
if (options.onForce && forceKillMs > 0) {
|
|
59
|
+
forceTimer = setTimeout(() => {
|
|
60
|
+
options.onForce?.();
|
|
61
|
+
finish(signal);
|
|
62
|
+
}, forceKillMs);
|
|
63
|
+
forceTimer.unref?.();
|
|
64
|
+
}
|
|
65
|
+
Promise.resolve()
|
|
66
|
+
.then(() => options.onSignal(signal))
|
|
67
|
+
.catch((error) => {
|
|
68
|
+
console.error("Graceful shutdown error:", error);
|
|
69
|
+
})
|
|
70
|
+
.finally(() => finish(signal));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (signal === "SIGINT" && firstSignal === "SIGINT") {
|
|
74
|
+
options.onForce?.();
|
|
75
|
+
finish(signal);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
const handlers = new Map();
|
|
79
|
+
for (const sig of HANDLED_SIGNALS) {
|
|
80
|
+
const listener = () => handle(sig);
|
|
81
|
+
handlers.set(sig, listener);
|
|
82
|
+
process.on(sig, listener);
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
uninstall: () => {
|
|
86
|
+
for (const [sig, listener] of handlers) {
|
|
87
|
+
process.removeListener(sig, listener);
|
|
88
|
+
}
|
|
89
|
+
handlers.clear();
|
|
90
|
+
clearTimer();
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
//# sourceMappingURL=graceful-shutdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graceful-shutdown.js","sourceRoot":"","sources":["../src/graceful-shutdown.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type { AnyClass } from "./class.js";
|
|
2
|
+
export { isShallowArrayEqual, isShallowEqual, omit, reverseNumericEnum } from "./collection.js";
|
|
3
|
+
export { errorMessage } from "./error.js";
|
|
4
|
+
export { exitCodeForSignal, type GracefulShutdownHandle, installGracefulShutdown, } from "./graceful-shutdown.js";
|
|
5
|
+
export { quote, toIdentifier } from "./source.js";
|
|
6
|
+
export { toCamelCase, toKebabCase, toPascalCase, toUpperFirst } from "./string.js";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAChG,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EACH,iBAAiB,EACjB,KAAK,sBAAsB,EAC3B,uBAAuB,GAC1B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { isShallowArrayEqual, isShallowEqual, omit, reverseNumericEnum } from "./collection.js";
|
|
2
|
+
export { errorMessage } from "./error.js";
|
|
3
|
+
export { exitCodeForSignal, installGracefulShutdown, } from "./graceful-shutdown.js";
|
|
4
|
+
export { quote, toIdentifier } from "./source.js";
|
|
5
|
+
export { toCamelCase, toKebabCase, toPascalCase, toUpperFirst } from "./string.js";
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAChG,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EACH,iBAAiB,EAEjB,uBAAuB,GAC1B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/source.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for shaping values into safe JavaScript/TypeScript source fragments:
|
|
3
|
+
* a reserved-word-safe identifier and a source-safe string literal. Both are
|
|
4
|
+
* pure and runtime-agnostic, intended for code generators that emit TypeScript.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Rewrites a candidate name into a JavaScript identifier safe to use at
|
|
8
|
+
* variable, parameter, or property position.
|
|
9
|
+
*
|
|
10
|
+
* The input is expected to already use valid identifier characters (for
|
|
11
|
+
* example the output of a case-conversion helper); the only transformation
|
|
12
|
+
* applied is appending an underscore when `name` collides with a reserved
|
|
13
|
+
* word or global identifier, so `toIdentifier("class")` is `"class_"` and
|
|
14
|
+
* `toIdentifier("iconName")` is `"iconName"`.
|
|
15
|
+
*
|
|
16
|
+
* @param name - The candidate identifier.
|
|
17
|
+
* @returns A reserved-word-safe identifier.
|
|
18
|
+
*/
|
|
19
|
+
export declare const toIdentifier: (name: string) => string;
|
|
20
|
+
/**
|
|
21
|
+
* Quotes a string for safe embedding as a literal in generated TypeScript
|
|
22
|
+
* source.
|
|
23
|
+
*
|
|
24
|
+
* Builds a double-quoted literal with `JSON.stringify`, then escapes the
|
|
25
|
+
* characters that are valid inside a JSON string yet unsafe inside JavaScript
|
|
26
|
+
* source: the angle brackets that could otherwise break out of an enclosing
|
|
27
|
+
* `</script>` and the U+2028/U+2029 line separators that prematurely terminate
|
|
28
|
+
* a string literal. The escaped form parses back to the original value.
|
|
29
|
+
*
|
|
30
|
+
* @param value - The string to embed.
|
|
31
|
+
* @returns A source-safe double-quoted string literal.
|
|
32
|
+
*/
|
|
33
|
+
export declare const quote: (value: string) => string;
|
|
34
|
+
//# sourceMappingURL=source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAqDH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,KAAG,MAAkD,CAAC;AAmB/F;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,KAAK,GAAI,OAAO,MAAM,KAAG,MAA8E,CAAC"}
|
package/dist/source.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for shaping values into safe JavaScript/TypeScript source fragments:
|
|
3
|
+
* a reserved-word-safe identifier and a source-safe string literal. Both are
|
|
4
|
+
* pure and runtime-agnostic, intended for code generators that emit TypeScript.
|
|
5
|
+
*/
|
|
6
|
+
/** Reserved words and global identifiers a generated identifier must not collide with. */
|
|
7
|
+
const RESERVED = new Set([
|
|
8
|
+
"arguments",
|
|
9
|
+
"await",
|
|
10
|
+
"break",
|
|
11
|
+
"case",
|
|
12
|
+
"catch",
|
|
13
|
+
"class",
|
|
14
|
+
"const",
|
|
15
|
+
"continue",
|
|
16
|
+
"debugger",
|
|
17
|
+
"default",
|
|
18
|
+
"delete",
|
|
19
|
+
"do",
|
|
20
|
+
"else",
|
|
21
|
+
"enum",
|
|
22
|
+
"eval",
|
|
23
|
+
"export",
|
|
24
|
+
"extends",
|
|
25
|
+
"false",
|
|
26
|
+
"finally",
|
|
27
|
+
"for",
|
|
28
|
+
"function",
|
|
29
|
+
"if",
|
|
30
|
+
"import",
|
|
31
|
+
"in",
|
|
32
|
+
"instanceof",
|
|
33
|
+
"interface",
|
|
34
|
+
"let",
|
|
35
|
+
"new",
|
|
36
|
+
"null",
|
|
37
|
+
"package",
|
|
38
|
+
"private",
|
|
39
|
+
"protected",
|
|
40
|
+
"public",
|
|
41
|
+
"return",
|
|
42
|
+
"static",
|
|
43
|
+
"super",
|
|
44
|
+
"switch",
|
|
45
|
+
"this",
|
|
46
|
+
"throw",
|
|
47
|
+
"true",
|
|
48
|
+
"try",
|
|
49
|
+
"typeof",
|
|
50
|
+
"var",
|
|
51
|
+
"void",
|
|
52
|
+
"while",
|
|
53
|
+
"with",
|
|
54
|
+
"yield",
|
|
55
|
+
]);
|
|
56
|
+
/**
|
|
57
|
+
* Rewrites a candidate name into a JavaScript identifier safe to use at
|
|
58
|
+
* variable, parameter, or property position.
|
|
59
|
+
*
|
|
60
|
+
* The input is expected to already use valid identifier characters (for
|
|
61
|
+
* example the output of a case-conversion helper); the only transformation
|
|
62
|
+
* applied is appending an underscore when `name` collides with a reserved
|
|
63
|
+
* word or global identifier, so `toIdentifier("class")` is `"class_"` and
|
|
64
|
+
* `toIdentifier("iconName")` is `"iconName"`.
|
|
65
|
+
*
|
|
66
|
+
* @param name - The candidate identifier.
|
|
67
|
+
* @returns A reserved-word-safe identifier.
|
|
68
|
+
*/
|
|
69
|
+
export const toIdentifier = (name) => (RESERVED.has(name) ? `${name}_` : name);
|
|
70
|
+
const UNSAFE_SOURCE_CHARS = /[<>\u2028\u2029]/g;
|
|
71
|
+
const escapeSourceChar = (char) => {
|
|
72
|
+
switch (char) {
|
|
73
|
+
case "<":
|
|
74
|
+
return "\\u003C";
|
|
75
|
+
case ">":
|
|
76
|
+
return "\\u003E";
|
|
77
|
+
case "\u2028":
|
|
78
|
+
return "\\u2028";
|
|
79
|
+
case "\u2029":
|
|
80
|
+
return "\\u2029";
|
|
81
|
+
default:
|
|
82
|
+
return char;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Quotes a string for safe embedding as a literal in generated TypeScript
|
|
87
|
+
* source.
|
|
88
|
+
*
|
|
89
|
+
* Builds a double-quoted literal with `JSON.stringify`, then escapes the
|
|
90
|
+
* characters that are valid inside a JSON string yet unsafe inside JavaScript
|
|
91
|
+
* source: the angle brackets that could otherwise break out of an enclosing
|
|
92
|
+
* `</script>` and the U+2028/U+2029 line separators that prematurely terminate
|
|
93
|
+
* a string literal. The escaped form parses back to the original value.
|
|
94
|
+
*
|
|
95
|
+
* @param value - The string to embed.
|
|
96
|
+
* @returns A source-safe double-quoted string literal.
|
|
97
|
+
*/
|
|
98
|
+
export const quote = (value) => JSON.stringify(value).replace(UNSAFE_SOURCE_CHARS, escapeSourceChar);
|
|
99
|
+
//# sourceMappingURL=source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source.js","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,0FAA0F;AAC1F,MAAM,QAAQ,GAAwB,IAAI,GAAG,CAAC;IAC1C,WAAW;IACX,OAAO;IACP,OAAO;IACP,MAAM;IACN,OAAO;IACP,OAAO;IACP,OAAO;IACP,UAAU;IACV,UAAU;IACV,SAAS;IACT,QAAQ;IACR,IAAI;IACJ,MAAM;IACN,MAAM;IACN,MAAM;IACN,QAAQ;IACR,SAAS;IACT,OAAO;IACP,SAAS;IACT,KAAK;IACL,UAAU;IACV,IAAI;IACJ,QAAQ;IACR,IAAI;IACJ,YAAY;IACZ,WAAW;IACX,KAAK;IACL,KAAK;IACL,MAAM;IACN,SAAS;IACT,SAAS;IACT,WAAW;IACX,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,MAAM;IACN,OAAO;IACP,MAAM;IACN,KAAK;IACL,QAAQ;IACR,KAAK;IACL,MAAM;IACN,OAAO;IACP,MAAM;IACN,OAAO;CACV,CAAC,CAAC;AAEH;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAE/F,MAAM,mBAAmB,GAAG,mBAAmB,CAAC;AAEhD,MAAM,gBAAgB,GAAG,CAAC,IAAY,EAAU,EAAE;IAC9C,QAAQ,IAAI,EAAE,CAAC;QACX,KAAK,GAAG;YACJ,OAAO,SAAS,CAAC;QACrB,KAAK,GAAG;YACJ,OAAO,SAAS,CAAC;QACrB,KAAK,QAAQ;YACT,OAAO,SAAS,CAAC;QACrB,KAAK,QAAQ;YACT,OAAO,SAAS,CAAC;QACrB;YACI,OAAO,IAAI,CAAC;IACpB,CAAC;AACL,CAAC,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,mBAAmB,EAAE,gBAAgB,CAAC,CAAC"}
|
package/dist/string.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, runtime-agnostic string-case helpers.
|
|
3
|
+
*
|
|
4
|
+
* The conversions translate between snake_case, kebab-case, camelCase, and
|
|
5
|
+
* PascalCase. They split only on underscores and hyphens and preserve the case
|
|
6
|
+
* of each segment, so they are not a substitute for a full Unicode-aware case
|
|
7
|
+
* transform.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Uppercases the first character of `value`, leaving the remaining characters
|
|
11
|
+
* untouched.
|
|
12
|
+
*
|
|
13
|
+
* The tail is preserved verbatim rather than lowercased, so
|
|
14
|
+
* `toUpperFirst("fooBar")` is `"FooBar"` and `toUpperFirst("URL")` is `"URL"`. An
|
|
15
|
+
* empty string returns an empty string.
|
|
16
|
+
*
|
|
17
|
+
* @param value - The string to transform.
|
|
18
|
+
* @returns `value` with its first character uppercased.
|
|
19
|
+
*/
|
|
20
|
+
export declare const toUpperFirst: (value: string) => string;
|
|
21
|
+
/**
|
|
22
|
+
* Converts a snake_case or kebab-case string to camelCase.
|
|
23
|
+
*
|
|
24
|
+
* The input is split on underscores and hyphens, dropping empty segments from
|
|
25
|
+
* leading, trailing, or repeated separators. The first segment is kept
|
|
26
|
+
* verbatim and every later segment is {@link toUpperFirst}-cased before joining,
|
|
27
|
+
* so `toCamelCase("icon_name")` is `"iconName"` and `toCamelCase("Box")` is
|
|
28
|
+
* `"Box"`. A string with no separators is returned unchanged.
|
|
29
|
+
*
|
|
30
|
+
* @param input - The snake_case or kebab-case identifier.
|
|
31
|
+
* @returns The camelCase form of `input`.
|
|
32
|
+
*/
|
|
33
|
+
export declare const toCamelCase: (input: string) => string;
|
|
34
|
+
/**
|
|
35
|
+
* Converts a snake_case, kebab-case, or already-PascalCase string to
|
|
36
|
+
* PascalCase.
|
|
37
|
+
*
|
|
38
|
+
* The input is split on underscores and hyphens, dropping empty segments, and
|
|
39
|
+
* every remaining segment is {@link toUpperFirst}-cased before joining, so
|
|
40
|
+
* `toPascalCase("icon_name")` is `"IconName"` and `toPascalCase("Box")` is
|
|
41
|
+
* `"Box"`. An empty string is returned unchanged.
|
|
42
|
+
*
|
|
43
|
+
* @param input - The identifier to transform.
|
|
44
|
+
* @returns The PascalCase form of `input`.
|
|
45
|
+
*/
|
|
46
|
+
export declare const toPascalCase: (input: string) => string;
|
|
47
|
+
/**
|
|
48
|
+
* Converts a camelCase or PascalCase string to kebab-case.
|
|
49
|
+
*
|
|
50
|
+
* Each uppercase character is lowercased; every uppercase character other than
|
|
51
|
+
* the first is additionally prefixed with a hyphen, so `toKebabCase("iconName")`
|
|
52
|
+
* is `"icon-name"` and `toKebabCase("Title")` is `"title"`. The leading
|
|
53
|
+
* character is never prefixed with a hyphen.
|
|
54
|
+
*
|
|
55
|
+
* @param input - The camelCase or PascalCase identifier.
|
|
56
|
+
* @returns The kebab-case form of `input`.
|
|
57
|
+
*/
|
|
58
|
+
export declare const toKebabCase: (input: string) => string;
|
|
59
|
+
//# sourceMappingURL=string.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"string.d.ts","sourceRoot":"","sources":["../src/string.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,KAAG,MAAwD,CAAC;AAEtG;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,KAAG,MAM3C,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,KAAG,MAK5C,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,KAAG,MAC0E,CAAC"}
|
package/dist/string.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, runtime-agnostic string-case helpers.
|
|
3
|
+
*
|
|
4
|
+
* The conversions translate between snake_case, kebab-case, camelCase, and
|
|
5
|
+
* PascalCase. They split only on underscores and hyphens and preserve the case
|
|
6
|
+
* of each segment, so they are not a substitute for a full Unicode-aware case
|
|
7
|
+
* transform.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Uppercases the first character of `value`, leaving the remaining characters
|
|
11
|
+
* untouched.
|
|
12
|
+
*
|
|
13
|
+
* The tail is preserved verbatim rather than lowercased, so
|
|
14
|
+
* `toUpperFirst("fooBar")` is `"FooBar"` and `toUpperFirst("URL")` is `"URL"`. An
|
|
15
|
+
* empty string returns an empty string.
|
|
16
|
+
*
|
|
17
|
+
* @param value - The string to transform.
|
|
18
|
+
* @returns `value` with its first character uppercased.
|
|
19
|
+
*/
|
|
20
|
+
export const toUpperFirst = (value) => value.charAt(0).toUpperCase() + value.slice(1);
|
|
21
|
+
/**
|
|
22
|
+
* Converts a snake_case or kebab-case string to camelCase.
|
|
23
|
+
*
|
|
24
|
+
* The input is split on underscores and hyphens, dropping empty segments from
|
|
25
|
+
* leading, trailing, or repeated separators. The first segment is kept
|
|
26
|
+
* verbatim and every later segment is {@link toUpperFirst}-cased before joining,
|
|
27
|
+
* so `toCamelCase("icon_name")` is `"iconName"` and `toCamelCase("Box")` is
|
|
28
|
+
* `"Box"`. A string with no separators is returned unchanged.
|
|
29
|
+
*
|
|
30
|
+
* @param input - The snake_case or kebab-case identifier.
|
|
31
|
+
* @returns The camelCase form of `input`.
|
|
32
|
+
*/
|
|
33
|
+
export const toCamelCase = (input) => {
|
|
34
|
+
const parts = input.split(/[_-]/g).filter((part) => part.length > 0);
|
|
35
|
+
if (parts.length === 0)
|
|
36
|
+
return input;
|
|
37
|
+
const [first, ...rest] = parts;
|
|
38
|
+
const head = first ?? "";
|
|
39
|
+
return head + rest.map(toUpperFirst).join("");
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Converts a snake_case, kebab-case, or already-PascalCase string to
|
|
43
|
+
* PascalCase.
|
|
44
|
+
*
|
|
45
|
+
* The input is split on underscores and hyphens, dropping empty segments, and
|
|
46
|
+
* every remaining segment is {@link toUpperFirst}-cased before joining, so
|
|
47
|
+
* `toPascalCase("icon_name")` is `"IconName"` and `toPascalCase("Box")` is
|
|
48
|
+
* `"Box"`. An empty string is returned unchanged.
|
|
49
|
+
*
|
|
50
|
+
* @param input - The identifier to transform.
|
|
51
|
+
* @returns The PascalCase form of `input`.
|
|
52
|
+
*/
|
|
53
|
+
export const toPascalCase = (input) => {
|
|
54
|
+
if (input.length === 0)
|
|
55
|
+
return input;
|
|
56
|
+
const parts = input.split(/[_-]/g).filter((part) => part.length > 0);
|
|
57
|
+
if (parts.length === 0)
|
|
58
|
+
return input;
|
|
59
|
+
return parts.map(toUpperFirst).join("");
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Converts a camelCase or PascalCase string to kebab-case.
|
|
63
|
+
*
|
|
64
|
+
* Each uppercase character is lowercased; every uppercase character other than
|
|
65
|
+
* the first is additionally prefixed with a hyphen, so `toKebabCase("iconName")`
|
|
66
|
+
* is `"icon-name"` and `toKebabCase("Title")` is `"title"`. The leading
|
|
67
|
+
* character is never prefixed with a hyphen.
|
|
68
|
+
*
|
|
69
|
+
* @param input - The camelCase or PascalCase identifier.
|
|
70
|
+
* @returns The kebab-case form of `input`.
|
|
71
|
+
*/
|
|
72
|
+
export const toKebabCase = (input) => input.replaceAll(/[A-Z]/g, (char, index) => (index === 0 ? char.toLowerCase() : `-${char.toLowerCase()}`));
|
|
73
|
+
//# sourceMappingURL=string.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"string.js","sourceRoot":"","sources":["../src/string.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEtG;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAa,EAAU,EAAE;IACjD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACrE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACrC,MAAM,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,GAAG,KAAK,CAAC;IAC/B,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;IACzB,OAAO,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAClD,CAAC,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,KAAa,EAAU,EAAE;IAClD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACrC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACrE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACrC,OAAO,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC5C,CAAC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAa,EAAU,EAAE,CACjD,KAAK,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,KAAa,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gtkx/utils",
|
|
3
|
+
"version": "0.21.0",
|
|
4
|
+
"description": "Cross-package utilities shared by GTKX runtime entry points",
|
|
5
|
+
"homepage": "https://gtkx.dev",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/gtkx-org/gtkx/issues"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/gtkx-org/gtkx.git",
|
|
12
|
+
"directory": "packages/utils"
|
|
13
|
+
},
|
|
14
|
+
"license": "MPL-2.0",
|
|
15
|
+
"author": "Eugenio Depalo <eugeniodepalo@gmail.com>",
|
|
16
|
+
"type": "module",
|
|
17
|
+
"exports": {
|
|
18
|
+
"./package.json": "./package.json",
|
|
19
|
+
".": {
|
|
20
|
+
"source": "./src/index.ts",
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"default": "./dist/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"sideEffects": false,
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"src"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc -b",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"typecheck": "tsc -b --emitDeclarationOnly"
|
|
34
|
+
}
|
|
35
|
+
}
|
package/src/class.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural type matching any class — abstract or concrete — whose instances
|
|
3
|
+
* have type `T`.
|
|
4
|
+
*
|
|
5
|
+
* A class value carries a construct signature plus a `prototype`, but a bare
|
|
6
|
+
* construct signature types `prototype` as `any`. The `& { prototype: T }`
|
|
7
|
+
* intersection recovers the precise instance type so callers can read
|
|
8
|
+
* `cls.prototype` as `T`, and the `abstract new` form accepts both abstract and
|
|
9
|
+
* concrete classes, making this the widest supertype of every class value. The
|
|
10
|
+
* `never[]` constructor parameters admit any class while documenting that the
|
|
11
|
+
* type is used as an identity token, not to construct instances.
|
|
12
|
+
*/
|
|
13
|
+
export type AnyClass<T extends object = object> = (abstract new (
|
|
14
|
+
...args: never[]
|
|
15
|
+
) => T) & {
|
|
16
|
+
readonly prototype: T;
|
|
17
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, runtime-agnostic collection helpers shared across GTKX packages.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Returns a shallow copy of `record` with the given `keys` removed.
|
|
7
|
+
*
|
|
8
|
+
* Keys absent from `record` are ignored, and `record` is not mutated. The
|
|
9
|
+
* result is typed as the input shape because callers treat the excluded keys
|
|
10
|
+
* as runtime-only concerns absent from the static type.
|
|
11
|
+
*
|
|
12
|
+
* @typeParam T - The record shape.
|
|
13
|
+
* @param record - The source object.
|
|
14
|
+
* @param keys - The keys to exclude from the copy.
|
|
15
|
+
* @returns A new object holding every own enumerable key of `record` except
|
|
16
|
+
* those listed in `keys`.
|
|
17
|
+
*/
|
|
18
|
+
export const omit = <T extends Record<string, unknown>>(record: T, keys: readonly string[]): T => {
|
|
19
|
+
const result: Record<string, unknown> = {};
|
|
20
|
+
for (const key of Object.keys(record)) {
|
|
21
|
+
if (!keys.includes(key)) {
|
|
22
|
+
result[key] = record[key];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return result as T;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Compares two arrays of primitives for element-wise strict equality.
|
|
30
|
+
*
|
|
31
|
+
* Returns `true` when both arguments are the same reference (including both
|
|
32
|
+
* being `null` or `undefined`), or when they have equal length and every
|
|
33
|
+
* element is strictly equal (`===`) at the same index. A `null`/`undefined`
|
|
34
|
+
* argument is equal only to another `null`/`undefined` argument.
|
|
35
|
+
*
|
|
36
|
+
* @typeParam T - The primitive element type.
|
|
37
|
+
* @param a - The first array, or `null`/`undefined`.
|
|
38
|
+
* @param b - The second array, or `null`/`undefined`.
|
|
39
|
+
* @returns Whether the two arrays are shallowly equal.
|
|
40
|
+
*/
|
|
41
|
+
export const isShallowEqual = <T extends string | number | boolean>(
|
|
42
|
+
a: readonly T[] | null | undefined,
|
|
43
|
+
b: readonly T[] | null | undefined,
|
|
44
|
+
): boolean => {
|
|
45
|
+
if (a === b) return true;
|
|
46
|
+
if (!a || !b) return false;
|
|
47
|
+
if (a.length !== b.length) return false;
|
|
48
|
+
for (let i = 0; i < a.length; i++) {
|
|
49
|
+
if (a[i] !== b[i]) return false;
|
|
50
|
+
}
|
|
51
|
+
return true;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Compares two arrays of records by shallow per-element equality.
|
|
56
|
+
*
|
|
57
|
+
* Returns `true` when both arrays have equal length and, at every index, the
|
|
58
|
+
* two records expose the same set of keys with strictly equal (`===`) values.
|
|
59
|
+
* Values are compared one level deep only; nested objects are compared by
|
|
60
|
+
* reference.
|
|
61
|
+
*
|
|
62
|
+
* @typeParam T - The record element type.
|
|
63
|
+
* @param a - The first array of records.
|
|
64
|
+
* @param b - The second array of records.
|
|
65
|
+
* @returns Whether the two arrays are element-wise shallowly equal.
|
|
66
|
+
*/
|
|
67
|
+
export const isShallowArrayEqual = <T extends Record<string, unknown>>(a: readonly T[], b: readonly T[]): boolean => {
|
|
68
|
+
if (a.length !== b.length) return false;
|
|
69
|
+
|
|
70
|
+
for (let i = 0; i < a.length; i++) {
|
|
71
|
+
const itemA = a[i];
|
|
72
|
+
const itemB = b[i];
|
|
73
|
+
if (!itemA || !itemB) return false;
|
|
74
|
+
|
|
75
|
+
const keysA = Object.keys(itemA);
|
|
76
|
+
const keysB = Object.keys(itemB);
|
|
77
|
+
if (keysA.length !== keysB.length) return false;
|
|
78
|
+
|
|
79
|
+
for (const key of keysA) {
|
|
80
|
+
if (itemA[key] !== itemB[key]) return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return true;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Builds a reverse lookup from a numeric enum's values to their member names.
|
|
89
|
+
*
|
|
90
|
+
* A TypeScript numeric enum's runtime object carries both name-to-value and
|
|
91
|
+
* value-to-name entries; this keeps only the name-to-value direction and
|
|
92
|
+
* inverts it, yielding a `Map` from each numeric value to the name that
|
|
93
|
+
* declared it.
|
|
94
|
+
*
|
|
95
|
+
* @param enumObject - A numeric enum's runtime object.
|
|
96
|
+
* @returns A map from each numeric enum value to its declared member name.
|
|
97
|
+
*/
|
|
98
|
+
export const reverseNumericEnum = (enumObject: Record<string, string | number>): Map<number, string> =>
|
|
99
|
+
new Map<number, string>(
|
|
100
|
+
Object.entries(enumObject)
|
|
101
|
+
.filter((entry): entry is [string, number] => typeof entry[1] === "number")
|
|
102
|
+
.map(([name, value]) => [value, name]),
|
|
103
|
+
);
|
package/src/error.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coerces an unknown thrown value into a human-readable string.
|
|
3
|
+
*
|
|
4
|
+
* Returns `error.message` when `error` is an `Error` instance, otherwise
|
|
5
|
+
* delegates to `String(error)`. Use at boundaries where exceptions are
|
|
6
|
+
* surfaced to logs, IPC frames, or user-facing output and the type cannot
|
|
7
|
+
* be narrowed otherwise.
|
|
8
|
+
*
|
|
9
|
+
* @param error - The caught value of unknown shape.
|
|
10
|
+
* @returns A string describing the error.
|
|
11
|
+
*/
|
|
12
|
+
export const errorMessage = (error: unknown): string => (error instanceof Error ? error.message : String(error));
|