@gtkx/utils 1.0.0-rc.1 → 1.0.0-rc.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.
Files changed (319) hide show
  1. package/dist/array/index-before-or-end.d.ts +15 -0
  2. package/dist/array/index-before-or-end.d.ts.map +1 -0
  3. package/dist/array/index-before-or-end.js +21 -0
  4. package/dist/array/index-before-or-end.js.map +1 -0
  5. package/dist/array/index.d.ts +6 -0
  6. package/dist/array/index.d.ts.map +1 -0
  7. package/dist/array/index.js +6 -0
  8. package/dist/array/index.js.map +1 -0
  9. package/dist/array/remove.d.ts +14 -0
  10. package/dist/array/remove.d.ts.map +1 -0
  11. package/dist/array/remove.js +19 -0
  12. package/dist/array/remove.js.map +1 -0
  13. package/dist/array/sort-strings-by.d.ts +15 -0
  14. package/dist/array/sort-strings-by.d.ts.map +1 -0
  15. package/dist/array/sort-strings-by.js +17 -0
  16. package/dist/array/sort-strings-by.js.map +1 -0
  17. package/dist/array/sort-strings.d.ts +12 -0
  18. package/dist/array/sort-strings.d.ts.map +1 -0
  19. package/dist/array/sort-strings.js +14 -0
  20. package/dist/array/sort-strings.js.map +1 -0
  21. package/dist/array/uniq-by.d.ts +18 -0
  22. package/dist/array/uniq-by.d.ts.map +1 -0
  23. package/dist/array/uniq-by.js +29 -0
  24. package/dist/array/uniq-by.js.map +1 -0
  25. package/dist/class/any-class.d.ts +8 -0
  26. package/dist/class/any-class.d.ts.map +1 -0
  27. package/dist/class/any-class.js +2 -0
  28. package/dist/class/any-class.js.map +1 -0
  29. package/dist/class/get-parent-class.d.ts +10 -0
  30. package/dist/class/get-parent-class.d.ts.map +1 -0
  31. package/dist/class/get-parent-class.js +12 -0
  32. package/dist/class/get-parent-class.js.map +1 -0
  33. package/dist/class/index.d.ts +4 -0
  34. package/dist/class/index.d.ts.map +1 -0
  35. package/dist/class/index.js +3 -0
  36. package/dist/class/index.js.map +1 -0
  37. package/dist/class/walk-class-chain.d.ts +13 -0
  38. package/dist/class/walk-class-chain.d.ts.map +1 -0
  39. package/dist/class/walk-class-chain.js +23 -0
  40. package/dist/class/walk-class-chain.js.map +1 -0
  41. package/dist/error/error-like.d.ts +6 -0
  42. package/dist/error/error-like.d.ts.map +1 -0
  43. package/dist/error/error-like.js +15 -0
  44. package/dist/error/error-like.js.map +1 -0
  45. package/dist/error/error-message.d.ts +13 -0
  46. package/dist/error/error-message.d.ts.map +1 -0
  47. package/dist/error/error-message.js +16 -0
  48. package/dist/error/error-message.js.map +1 -0
  49. package/dist/error/format-child-process-error.d.ts +12 -0
  50. package/dist/error/format-child-process-error.d.ts.map +1 -0
  51. package/dist/error/format-child-process-error.js +21 -0
  52. package/dist/error/format-child-process-error.js.map +1 -0
  53. package/dist/error/index.d.ts +4 -0
  54. package/dist/error/index.d.ts.map +1 -0
  55. package/dist/error/index.js +4 -0
  56. package/dist/error/index.js.map +1 -0
  57. package/dist/error/normalize-error.d.ts +13 -0
  58. package/dist/error/normalize-error.d.ts.map +1 -0
  59. package/dist/error/normalize-error.js +20 -0
  60. package/dist/error/normalize-error.js.map +1 -0
  61. package/dist/function/call-method.d.ts +3 -0
  62. package/dist/function/call-method.d.ts.map +1 -0
  63. package/dist/function/call-method.js +19 -0
  64. package/dist/function/call-method.js.map +1 -0
  65. package/dist/function/index.d.ts +2 -0
  66. package/dist/function/index.d.ts.map +1 -0
  67. package/dist/function/index.js +2 -0
  68. package/dist/function/index.js.map +1 -0
  69. package/dist/index.d.ts +13 -9
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +13 -9
  72. package/dist/index.js.map +1 -1
  73. package/dist/log/create-logger.d.ts +15 -0
  74. package/dist/log/create-logger.d.ts.map +1 -0
  75. package/dist/log/create-logger.js +17 -0
  76. package/dist/log/create-logger.js.map +1 -0
  77. package/dist/log/default-logger.d.ts +36 -0
  78. package/dist/log/default-logger.d.ts.map +1 -0
  79. package/dist/log/default-logger.js +44 -0
  80. package/dist/log/default-logger.js.map +1 -0
  81. package/dist/log/index.d.ts +4 -0
  82. package/dist/log/index.d.ts.map +1 -0
  83. package/dist/log/index.js +4 -0
  84. package/dist/log/index.js.map +1 -0
  85. package/dist/log/logger.d.ts +64 -0
  86. package/dist/log/logger.d.ts.map +1 -0
  87. package/dist/log/logger.js +100 -0
  88. package/dist/log/logger.js.map +1 -0
  89. package/dist/map/get-or-insert.d.ts +23 -0
  90. package/dist/map/get-or-insert.d.ts.map +1 -0
  91. package/dist/map/get-or-insert.js +25 -0
  92. package/dist/map/get-or-insert.js.map +1 -0
  93. package/dist/map/index.d.ts +2 -0
  94. package/dist/map/index.d.ts.map +1 -0
  95. package/dist/map/index.js +2 -0
  96. package/dist/map/index.js.map +1 -0
  97. package/dist/object/index.d.ts +4 -0
  98. package/dist/object/index.d.ts.map +1 -0
  99. package/dist/object/index.js +4 -0
  100. package/dist/object/index.js.map +1 -0
  101. package/dist/object/omit.d.ts +15 -0
  102. package/dist/object/omit.d.ts.map +1 -0
  103. package/dist/object/omit.js +24 -0
  104. package/dist/object/omit.js.map +1 -0
  105. package/dist/object/pick-by.d.ts +15 -0
  106. package/dist/object/pick-by.d.ts.map +1 -0
  107. package/dist/object/pick-by.js +24 -0
  108. package/dist/object/pick-by.js.map +1 -0
  109. package/dist/object/structured-clone.d.ts +19 -0
  110. package/dist/object/structured-clone.d.ts.map +1 -0
  111. package/dist/object/structured-clone.js +28 -0
  112. package/dist/object/structured-clone.js.map +1 -0
  113. package/dist/predicate/are-object-keys-equal.d.ts +3 -0
  114. package/dist/predicate/are-object-keys-equal.d.ts.map +1 -0
  115. package/dist/predicate/are-object-keys-equal.js +9 -0
  116. package/dist/predicate/are-object-keys-equal.js.map +1 -0
  117. package/dist/predicate/index.d.ts +7 -0
  118. package/dist/predicate/index.d.ts.map +1 -0
  119. package/dist/predicate/index.js +7 -0
  120. package/dist/predicate/index.js.map +1 -0
  121. package/dist/predicate/is-deep-equal.d.ts +3 -0
  122. package/dist/predicate/is-deep-equal.d.ts.map +1 -0
  123. package/dist/predicate/is-deep-equal.js +35 -0
  124. package/dist/predicate/is-deep-equal.js.map +1 -0
  125. package/dist/predicate/is-plain-object.d.ts +17 -0
  126. package/dist/predicate/is-plain-object.d.ts.map +1 -0
  127. package/dist/predicate/is-plain-object.js +23 -0
  128. package/dist/predicate/is-plain-object.js.map +1 -0
  129. package/dist/predicate/is-record.d.ts +14 -0
  130. package/dist/predicate/is-record.d.ts.map +1 -0
  131. package/dist/predicate/is-record.js +16 -0
  132. package/dist/predicate/is-record.js.map +1 -0
  133. package/dist/predicate/is-same-array-by.d.ts +15 -0
  134. package/dist/predicate/is-same-array-by.d.ts.map +1 -0
  135. package/dist/predicate/is-same-array-by.js +20 -0
  136. package/dist/predicate/is-same-array-by.js.map +1 -0
  137. package/dist/predicate/is-same-array.d.ts +15 -0
  138. package/dist/predicate/is-same-array.d.ts.map +1 -0
  139. package/dist/predicate/is-same-array.js +18 -0
  140. package/dist/predicate/is-same-array.js.map +1 -0
  141. package/dist/predicate/is-shallow-equal.d.ts +3 -0
  142. package/dist/predicate/is-shallow-equal.d.ts.map +1 -0
  143. package/dist/predicate/is-shallow-equal.js +28 -0
  144. package/dist/predicate/is-shallow-equal.js.map +1 -0
  145. package/dist/process/exit-code-for-signal.d.ts +14 -0
  146. package/dist/process/exit-code-for-signal.d.ts.map +1 -0
  147. package/dist/process/exit-code-for-signal.js +19 -0
  148. package/dist/process/exit-code-for-signal.js.map +1 -0
  149. package/dist/process/index.d.ts +4 -0
  150. package/dist/process/index.d.ts.map +1 -0
  151. package/dist/process/index.js +4 -0
  152. package/dist/process/index.js.map +1 -0
  153. package/dist/process/install-graceful-shutdown.d.ts +16 -0
  154. package/dist/process/install-graceful-shutdown.d.ts.map +1 -0
  155. package/dist/{graceful-shutdown.js → process/install-graceful-shutdown.js} +28 -28
  156. package/dist/process/install-graceful-shutdown.js.map +1 -0
  157. package/dist/process/resolve-executable.d.ts +14 -0
  158. package/dist/process/resolve-executable.d.ts.map +1 -0
  159. package/dist/process/resolve-executable.js +47 -0
  160. package/dist/process/resolve-executable.js.map +1 -0
  161. package/dist/set/drain.d.ts +11 -0
  162. package/dist/set/drain.d.ts.map +1 -0
  163. package/dist/set/drain.js +16 -0
  164. package/dist/set/drain.js.map +1 -0
  165. package/dist/set/index.d.ts +2 -0
  166. package/dist/set/index.d.ts.map +1 -0
  167. package/dist/set/index.js +2 -0
  168. package/dist/set/index.js.map +1 -0
  169. package/dist/source/index.d.ts +4 -0
  170. package/dist/source/index.d.ts.map +1 -0
  171. package/dist/source/index.js +4 -0
  172. package/dist/source/index.js.map +1 -0
  173. package/dist/source/sanitize-identifier.d.ts +14 -0
  174. package/dist/source/sanitize-identifier.d.ts.map +1 -0
  175. package/dist/source/sanitize-identifier.js +65 -0
  176. package/dist/source/sanitize-identifier.js.map +1 -0
  177. package/dist/source/source-string-literal.d.ts +13 -0
  178. package/dist/source/source-string-literal.d.ts.map +1 -0
  179. package/dist/source/source-string-literal.js +24 -0
  180. package/dist/source/source-string-literal.js.map +1 -0
  181. package/dist/source/to-camel-identifier.d.ts +13 -0
  182. package/dist/source/to-camel-identifier.d.ts.map +1 -0
  183. package/dist/source/to-camel-identifier.js +17 -0
  184. package/dist/source/to-camel-identifier.js.map +1 -0
  185. package/dist/string/camel-case.d.ts +17 -0
  186. package/dist/string/camel-case.d.ts.map +1 -0
  187. package/dist/string/camel-case.js +21 -0
  188. package/dist/string/camel-case.js.map +1 -0
  189. package/dist/string/index.d.ts +6 -0
  190. package/dist/string/index.d.ts.map +1 -0
  191. package/dist/string/index.js +6 -0
  192. package/dist/string/index.js.map +1 -0
  193. package/dist/string/kebab-case.d.ts +14 -0
  194. package/dist/string/kebab-case.d.ts.map +1 -0
  195. package/dist/string/kebab-case.js +16 -0
  196. package/dist/string/kebab-case.js.map +1 -0
  197. package/dist/string/lower-first.d.ts +13 -0
  198. package/dist/string/lower-first.d.ts.map +1 -0
  199. package/dist/string/lower-first.js +15 -0
  200. package/dist/string/lower-first.js.map +1 -0
  201. package/dist/string/pascal-case.d.ts +16 -0
  202. package/dist/string/pascal-case.d.ts.map +1 -0
  203. package/dist/string/pascal-case.js +20 -0
  204. package/dist/string/pascal-case.js.map +1 -0
  205. package/dist/string/upper-first.d.ts +13 -0
  206. package/dist/string/upper-first.d.ts.map +1 -0
  207. package/dist/string/upper-first.js +15 -0
  208. package/dist/string/upper-first.js.map +1 -0
  209. package/dist/string/word-segments.d.ts +3 -0
  210. package/dist/string/word-segments.d.ts.map +1 -0
  211. package/dist/string/word-segments.js +12 -0
  212. package/dist/string/word-segments.js.map +1 -0
  213. package/dist/util/index.d.ts +2 -0
  214. package/dist/util/index.d.ts.map +1 -0
  215. package/dist/util/index.js +2 -0
  216. package/dist/util/index.js.map +1 -0
  217. package/dist/util/package-version.d.ts +12 -0
  218. package/dist/util/package-version.d.ts.map +1 -0
  219. package/dist/util/package-version.js +15 -0
  220. package/dist/util/package-version.js.map +1 -0
  221. package/package.json +1 -3
  222. package/src/array/index-before-or-end.ts +27 -0
  223. package/src/array/index.ts +5 -0
  224. package/src/array/remove.ts +20 -0
  225. package/src/array/sort-strings-by.ts +17 -0
  226. package/src/array/sort-strings.ts +14 -0
  227. package/src/array/uniq-by.ts +33 -0
  228. package/src/class/any-class.ts +10 -0
  229. package/src/class/get-parent-class.ts +15 -0
  230. package/src/class/index.ts +3 -0
  231. package/src/class/walk-class-chain.ts +29 -0
  232. package/src/error/error-like.ts +19 -0
  233. package/src/error/error-message.ts +17 -0
  234. package/src/error/format-child-process-error.ts +24 -0
  235. package/src/error/index.ts +3 -0
  236. package/src/error/normalize-error.ts +22 -0
  237. package/src/function/call-method.ts +22 -0
  238. package/src/function/index.ts +1 -0
  239. package/src/index.ts +13 -19
  240. package/src/log/create-logger.ts +18 -0
  241. package/src/log/default-logger.ts +49 -0
  242. package/src/log/index.ts +3 -0
  243. package/src/log/logger.ts +142 -0
  244. package/src/map/get-or-insert.ts +33 -0
  245. package/src/map/index.ts +1 -0
  246. package/src/object/index.ts +3 -0
  247. package/src/object/omit.ts +26 -0
  248. package/src/object/pick-by.ts +30 -0
  249. package/src/object/structured-clone.ts +31 -0
  250. package/src/predicate/are-object-keys-equal.ts +15 -0
  251. package/src/predicate/index.ts +6 -0
  252. package/src/predicate/is-deep-equal.ts +41 -0
  253. package/src/predicate/is-plain-object.ts +25 -0
  254. package/src/predicate/is-record.ts +16 -0
  255. package/src/predicate/is-same-array-by.ts +21 -0
  256. package/src/predicate/is-same-array.ts +19 -0
  257. package/src/predicate/is-shallow-equal.ts +32 -0
  258. package/src/process/exit-code-for-signal.ts +20 -0
  259. package/src/process/index.ts +3 -0
  260. package/src/{graceful-shutdown.ts → process/install-graceful-shutdown.ts} +48 -36
  261. package/src/process/resolve-executable.ts +57 -0
  262. package/src/set/drain.ts +17 -0
  263. package/src/set/index.ts +1 -0
  264. package/src/source/index.ts +3 -0
  265. package/src/source/sanitize-identifier.ts +66 -0
  266. package/src/source/source-string-literal.ts +27 -0
  267. package/src/source/to-camel-identifier.ts +18 -0
  268. package/src/string/camel-case.ts +22 -0
  269. package/src/string/index.ts +5 -0
  270. package/src/string/kebab-case.ts +18 -0
  271. package/src/string/lower-first.ts +15 -0
  272. package/src/string/pascal-case.ts +21 -0
  273. package/src/string/upper-first.ts +15 -0
  274. package/src/string/word-segments.ts +15 -0
  275. package/src/util/index.ts +1 -0
  276. package/src/util/package-version.ts +16 -0
  277. package/dist/class.d.ts +0 -22
  278. package/dist/class.d.ts.map +0 -1
  279. package/dist/class.js +0 -28
  280. package/dist/class.js.map +0 -1
  281. package/dist/collection.d.ts +0 -30
  282. package/dist/collection.d.ts.map +0 -1
  283. package/dist/collection.js +0 -64
  284. package/dist/collection.js.map +0 -1
  285. package/dist/error.d.ts +0 -21
  286. package/dist/error.d.ts.map +0 -1
  287. package/dist/error.js +0 -40
  288. package/dist/error.js.map +0 -1
  289. package/dist/graceful-shutdown.d.ts +0 -22
  290. package/dist/graceful-shutdown.d.ts.map +0 -1
  291. package/dist/graceful-shutdown.js.map +0 -1
  292. package/dist/log.d.ts +0 -103
  293. package/dist/log.d.ts.map +0 -1
  294. package/dist/log.js +0 -129
  295. package/dist/log.js.map +0 -1
  296. package/dist/package-version.d.ts +0 -7
  297. package/dist/package-version.d.ts.map +0 -1
  298. package/dist/package-version.js +0 -8
  299. package/dist/package-version.js.map +0 -1
  300. package/dist/reflect.d.ts +0 -11
  301. package/dist/reflect.d.ts.map +0 -1
  302. package/dist/reflect.js +0 -14
  303. package/dist/reflect.js.map +0 -1
  304. package/dist/source.d.ts +0 -21
  305. package/dist/source.d.ts.map +0 -1
  306. package/dist/source.js +0 -78
  307. package/dist/source.js.map +0 -1
  308. package/dist/string.d.ts +0 -32
  309. package/dist/string.d.ts.map +0 -1
  310. package/dist/string.js +0 -43
  311. package/dist/string.js.map +0 -1
  312. package/src/class.ts +0 -36
  313. package/src/collection.ts +0 -65
  314. package/src/error.ts +0 -42
  315. package/src/log.ts +0 -164
  316. package/src/package-version.ts +0 -9
  317. package/src/reflect.ts +0 -13
  318. package/src/source.ts +0 -84
  319. package/src/string.ts +0 -46
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Creates a new object holding every entry of `obj` except the named keys.
3
+ *
4
+ * @template T - The type of the source object.
5
+ * @template K - The keys to leave out.
6
+ * @param obj - The object to copy entries from.
7
+ * @param keys - The keys to leave out of the result.
8
+ * @returns A new object without the named keys.
9
+ *
10
+ * @example
11
+ * omit({ a: 1, b: 2 }, ["a"]); // { b: 2 }
12
+ */
13
+ function omit<T extends object, K extends keyof T>(obj: T, keys: K[]): Omit<T, K> {
14
+ const excluded: Set<PropertyKey> = new Set(keys);
15
+ const result: Record<string, unknown> = {};
16
+
17
+ for (const [key, value] of Object.entries(obj)) {
18
+ if (!excluded.has(key)) {
19
+ result[key] = value;
20
+ }
21
+ }
22
+
23
+ return result as Omit<T, K>;
24
+ }
25
+
26
+ export { omit };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Creates a new object of the entries of `obj` that satisfy the predicate.
3
+ *
4
+ * @template T - The type of the source object.
5
+ * @param obj - The object to pick entries from.
6
+ * @param shouldPick - Called with each value and key; return `true` to keep the entry.
7
+ * @returns A new object holding only the entries the predicate kept.
8
+ *
9
+ * @example
10
+ * pickBy({ a: 1, b: 2 }, (value) => value > 1); // { b: 2 }
11
+ * pickBy({ a: 1, b: 2 }, (_value, key) => key !== "a"); // { b: 2 }
12
+ */
13
+ function pickBy<T extends Record<string, unknown>>(
14
+ obj: T,
15
+ shouldPick: (value: T[keyof T], key: keyof T) => boolean,
16
+ ): Partial<T> {
17
+ const result: Partial<T> = {};
18
+
19
+ for (const key of Object.keys(obj) as (keyof T)[]) {
20
+ const value = obj[key];
21
+
22
+ if (shouldPick(value, key)) {
23
+ result[key] = value;
24
+ }
25
+ }
26
+
27
+ return result;
28
+ }
29
+
30
+ export { pickBy };
@@ -0,0 +1,31 @@
1
+ import { isPlainObject } from "../predicate/is-plain-object.js";
2
+
3
+ /**
4
+ * Recursively copies arrays and plain objects, sharing every other value by reference. The result
5
+ * mirrors exactly the structure `isDeepEqual` traverses, so comparing a value against its earlier
6
+ * clone detects in-place mutations of the original. Unlike the global of the same name, class
7
+ * instances such as `Date` and `Map` are shared rather than copied, and no value is ever rejected.
8
+ *
9
+ * @template T - The type of the value.
10
+ * @param value - The value to clone.
11
+ * @returns A clone whose arrays and plain objects are fresh and whose other values are shared.
12
+ *
13
+ * @example
14
+ * const source = { a: [{ b: 1 }] };
15
+ * const copy = structuredClone(source);
16
+ * source.a[0].b = 2;
17
+ * copy.a[0].b; // 1
18
+ */
19
+ function structuredClone<T>(value: T): T {
20
+ if (Array.isArray(value)) {
21
+ return value.map((item: unknown) => structuredClone(item)) as T;
22
+ }
23
+
24
+ if (isPlainObject(value)) {
25
+ return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, structuredClone(entry)])) as T;
26
+ }
27
+
28
+ return value;
29
+ }
30
+
31
+ export { structuredClone };
@@ -0,0 +1,15 @@
1
+ const areObjectKeysEqual = (
2
+ a: Record<string, unknown>,
3
+ b: Record<string, unknown>,
4
+ isValueEqual: (a: unknown, b: unknown) => boolean,
5
+ ): boolean => {
6
+ const keysA = Object.keys(a);
7
+
8
+ if (keysA.length !== Object.keys(b).length) {
9
+ return false;
10
+ }
11
+
12
+ return keysA.every((key) => Object.hasOwn(b, key) && isValueEqual(a[key], b[key]));
13
+ };
14
+
15
+ export { areObjectKeysEqual };
@@ -0,0 +1,6 @@
1
+ export { isDeepEqual } from "./is-deep-equal.js";
2
+ export { isPlainObject } from "./is-plain-object.js";
3
+ export { isRecord } from "./is-record.js";
4
+ export { isSameArrayBy } from "./is-same-array-by.js";
5
+ export { isSameArray } from "./is-same-array.js";
6
+ export { isShallowEqual } from "./is-shallow-equal.js";
@@ -0,0 +1,41 @@
1
+ import { areObjectKeysEqual } from "./are-object-keys-equal.js";
2
+ import { isPlainObject } from "./is-plain-object.js";
3
+
4
+ /**
5
+ * Checks whether two values are deeply equal: identical, arrays of the same length whose elements
6
+ * are deeply equal in order, or plain objects with the same own enumerable keys and deeply equal
7
+ * values. Values that are neither both arrays nor both plain objects are equal only when identical.
8
+ *
9
+ * @param a - The first value.
10
+ * @param b - The second value.
11
+ * @returns `true` when the values are deeply equal.
12
+ *
13
+ * @example
14
+ * isDeepEqual({ a: [1, { b: 2 }] }, { a: [1, { b: 2 }] }); // true
15
+ * isDeepEqual({ a: [1] }, { a: [2] }); // false
16
+ */
17
+ const isDeepArrayEqual = (a: unknown, b: unknown): boolean => {
18
+ if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) {
19
+ return false;
20
+ }
21
+
22
+ return a.every((item, index) => isDeepEqual(item, b[index]));
23
+ };
24
+
25
+ function isDeepEqual(a: unknown, b: unknown): boolean {
26
+ if (a === b) {
27
+ return true;
28
+ }
29
+
30
+ if (Array.isArray(a) || Array.isArray(b)) {
31
+ return isDeepArrayEqual(a, b);
32
+ }
33
+
34
+ if (isPlainObject(a) && isPlainObject(b)) {
35
+ return areObjectKeysEqual(a, b, isDeepEqual);
36
+ }
37
+
38
+ return false;
39
+ }
40
+
41
+ export { isDeepEqual };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Checks whether a value is a plain object, i.e. one whose prototype is `Object.prototype` or `null`.
3
+ *
4
+ * Class instances, arrays, and other exotic objects return `false`.
5
+ *
6
+ * @param value - The value to test.
7
+ * @returns `true` when `value` is a plain object.
8
+ *
9
+ * @example
10
+ * isPlainObject({ a: 1 }); // true
11
+ * isPlainObject(Object.create(null)); // true
12
+ * isPlainObject([1, 2]); // false
13
+ * isPlainObject(new Date()); // false
14
+ */
15
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
16
+ if (typeof value !== "object" || value === null) {
17
+ return false;
18
+ }
19
+
20
+ const proto: unknown = Object.getPrototypeOf(value);
21
+
22
+ return proto === Object.prototype || proto === null;
23
+ }
24
+
25
+ export { isPlainObject };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Checks whether a value is a non-null object (including arrays and class instances, excluding `null`).
3
+ *
4
+ * @param value - The value to test.
5
+ * @returns `true` when `value` is a non-null object.
6
+ *
7
+ * @example
8
+ * isRecord({ a: 1 }); // true
9
+ * isRecord(null); // false
10
+ * isRecord("x"); // false
11
+ */
12
+ function isRecord(value: unknown): value is Record<string, unknown> {
13
+ return typeof value === "object" && value !== null;
14
+ }
15
+
16
+ export { isRecord };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Checks whether two arrays have the same length and elements the comparator treats as equal, in order.
3
+ *
4
+ * @template T - The type of the array elements.
5
+ * @param a - The first array.
6
+ * @param b - The second array.
7
+ * @param isEqual - Compares elements at the same index; return `true` when they are equal.
8
+ * @returns `true` when the arrays are element-wise equal under `isEqual`.
9
+ *
10
+ * @example
11
+ * isSameArrayBy([{ id: 1 }], [{ id: 1 }], (x, y) => x.id === y.id); // true
12
+ */
13
+ function isSameArrayBy<T>(a: T[], b: T[], isEqual: (x: T, y: T) => boolean): boolean {
14
+ if (a.length !== b.length) {
15
+ return false;
16
+ }
17
+
18
+ return a.every((item, index) => isEqual(item, b[index] as T));
19
+ }
20
+
21
+ export { isSameArrayBy };
@@ -0,0 +1,19 @@
1
+ import { isSameArrayBy } from "./is-same-array-by.js";
2
+
3
+ /**
4
+ * Checks whether two arrays have the same length and strictly equal (`===`) elements in order.
5
+ *
6
+ * @template T - The type of the array elements.
7
+ * @param a - The first array.
8
+ * @param b - The second array.
9
+ * @returns `true` when the arrays are element-wise strictly equal.
10
+ *
11
+ * @example
12
+ * isSameArray([1, 2], [1, 2]); // true
13
+ * isSameArray([1, 2], [2, 1]); // false
14
+ */
15
+ function isSameArray<T>(a: T[], b: T[]): boolean {
16
+ return isSameArrayBy(a, b, (x, y) => x === y);
17
+ }
18
+
19
+ export { isSameArray };
@@ -0,0 +1,32 @@
1
+ import { areObjectKeysEqual } from "./are-object-keys-equal.js";
2
+ import { isPlainObject } from "./is-plain-object.js";
3
+
4
+ /**
5
+ * Checks whether two values are shallowly equal: identical, or plain objects with the same own
6
+ * enumerable keys and strictly equal (`===`) values. Values that are not both plain objects are
7
+ * equal only when identical.
8
+ *
9
+ * @param a - The first value.
10
+ * @param b - The second value.
11
+ * @returns `true` when the values are shallowly equal.
12
+ *
13
+ * @example
14
+ * isShallowEqual({ a: 1 }, { a: 1 }); // true
15
+ * isShallowEqual({ a: 1 }, { a: 2 }); // false
16
+ * isShallowEqual(3, 3); // true
17
+ */
18
+ const isStrictEqual = (a: unknown, b: unknown): boolean => a === b;
19
+
20
+ function isShallowEqual(a: unknown, b: unknown): boolean {
21
+ if (a === b) {
22
+ return true;
23
+ }
24
+
25
+ if (isPlainObject(a) && isPlainObject(b)) {
26
+ return areObjectKeysEqual(a, b, isStrictEqual);
27
+ }
28
+
29
+ return false;
30
+ }
31
+
32
+ export { isShallowEqual };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Maps a terminating signal to its conventional process exit code (130 for `SIGINT`, 143 otherwise),
3
+ * or 0 when no signal is given.
4
+ *
5
+ * @param signal - The signal that triggered termination, or `null`.
6
+ * @returns The conventional exit code for the signal.
7
+ *
8
+ * @example
9
+ * exitCodeForSignal("SIGINT"); // 130
10
+ * exitCodeForSignal(null); // 0
11
+ */
12
+ function exitCodeForSignal(signal: NodeJS.Signals | null): number {
13
+ if (!signal) {
14
+ return 0;
15
+ }
16
+
17
+ return signal === "SIGINT" ? 130 : 143;
18
+ }
19
+
20
+ export { exitCodeForSignal };
@@ -0,0 +1,3 @@
1
+ export { exitCodeForSignal } from "./exit-code-for-signal.js";
2
+ export { installGracefulShutdown } from "./install-graceful-shutdown.js";
3
+ export { resolveExecutable } from "./resolve-executable.js";
@@ -1,26 +1,12 @@
1
- import { error } from "./log.js";
1
+ import { error } from "../log/default-logger.js";
2
+ import { exitCodeForSignal } from "./exit-code-for-signal.js";
2
3
 
3
- const HANDLED_SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"] as const satisfies NodeJS.Signals[];
4
- const DEFAULT_FORCE_KILL_TIMEOUT_MS = 5000;
5
- const DEFAULT_COALESCE_WINDOW_MS = 500;
6
-
7
- /**
8
- * Maps a terminating signal to its conventional process exit code (130 for `SIGINT`, 143 otherwise),
9
- * or 0 when no signal is given.
10
- *
11
- * @param signal The signal that triggered termination, or `null`.
12
- */
13
- export const exitCodeForSignal = (signal: NodeJS.Signals | null): number => {
14
- if (!signal) return 0;
15
- return signal === "SIGINT" ? 130 : 143;
16
- };
17
-
18
- export type GracefulShutdownOptions = {
4
+ type GracefulShutdownOptions = {
19
5
  onSignal: (signal: NodeJS.Signals) => void | Promise<void>;
20
6
  onForce?: () => void;
21
7
  forceKillAfterMs?: number;
22
8
  coalesceWindowMs?: number;
23
- exitCode?: (signal: NodeJS.Signals, graceful: boolean) => number;
9
+ exitCode?: (signal: NodeJS.Signals, isGraceful: boolean) => number;
24
10
  };
25
11
 
26
12
  type ShutdownState = {
@@ -34,59 +20,80 @@ type ShutdownState = {
34
20
  coalesceTimer: NodeJS.Timeout | null;
35
21
  };
36
22
 
23
+ const HANDLED_SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"] as const satisfies NodeJS.Signals[];
24
+ const DEFAULT_FORCE_KILL_TIMEOUT_MS = 5000;
25
+ const DEFAULT_COALESCE_WINDOW_MS = 500;
26
+
37
27
  const clearTimers = (state: ShutdownState): void => {
38
28
  if (state.forceTimer) {
39
29
  clearTimeout(state.forceTimer);
40
30
  state.forceTimer = null;
41
31
  }
32
+
42
33
  if (state.coalesceTimer) {
43
34
  clearTimeout(state.coalesceTimer);
44
35
  state.coalesceTimer = null;
45
36
  }
46
37
  };
47
38
 
48
- const finish = (state: ShutdownState, signal: NodeJS.Signals, graceful: boolean): void => {
49
- if (state.exited) return;
39
+ const finish = (state: ShutdownState, signal: NodeJS.Signals, isGraceful: boolean): void => {
40
+ if (state.exited) {
41
+ return;
42
+ }
43
+
50
44
  state.exited = true;
51
45
  clearTimers(state);
52
46
  const { exitCode } = state.options;
53
- const code = exitCode ? exitCode(signal, graceful) : graceful ? 0 : exitCodeForSignal(signal);
54
- process.exit(code);
47
+ const defaultCode = isGraceful ? 0 : exitCodeForSignal(signal);
48
+ process.exit(exitCode ? exitCode(signal, isGraceful) : defaultCode);
49
+ };
50
+
51
+ const runShutdown = async (state: ShutdownState, signal: NodeJS.Signals): Promise<void> => {
52
+ try {
53
+ await state.options.onSignal(signal);
54
+ finish(state, signal, true);
55
+ } catch (error_) {
56
+ error("graceful shutdown failed", error_);
57
+ finish(state, signal, false);
58
+ }
55
59
  };
56
60
 
57
61
  const beginShutdown = (state: ShutdownState, signal: NodeJS.Signals): void => {
58
62
  state.firstSignal = signal;
63
+
59
64
  if (state.coalesceWindowMs > 0) {
60
65
  state.coalescing = true;
66
+
61
67
  state.coalesceTimer = setTimeout(() => {
62
68
  state.coalescing = false;
63
69
  }, state.coalesceWindowMs);
70
+
64
71
  state.coalesceTimer.unref();
65
72
  }
73
+
66
74
  if (state.options.onForce && state.forceKillMs > 0) {
67
75
  state.forceTimer = setTimeout(() => {
68
76
  state.options.onForce?.();
69
77
  finish(state, signal, false);
70
78
  }, state.forceKillMs);
79
+
71
80
  state.forceTimer.unref();
72
81
  }
73
- Promise.resolve()
74
- .then(() => state.options.onSignal(signal))
75
- .then(
76
- () => finish(state, signal, true),
77
- (reason: unknown) => {
78
- error("graceful shutdown failed", reason);
79
- finish(state, signal, false);
80
- },
81
- );
82
+
83
+ void runShutdown(state, signal);
82
84
  };
83
85
 
84
86
  const handle = (state: ShutdownState, signal: NodeJS.Signals): void => {
85
87
  if (state.firstSignal === null) {
86
88
  beginShutdown(state, signal);
89
+
90
+ return;
91
+ }
92
+
93
+ if (state.coalescing) {
87
94
  return;
88
95
  }
89
- if (state.coalescing) return;
96
+
90
97
  state.options.onForce?.();
91
98
  finish(state, signal, false);
92
99
  };
@@ -95,9 +102,9 @@ const handle = (state: ShutdownState, signal: NodeJS.Signals): void => {
95
102
  * Registers handlers for `SIGINT`, `SIGTERM`, and `SIGHUP` that run the given cleanup callback once
96
103
  * and then exit, forcing exit if a repeated signal arrives or the cleanup exceeds its timeout.
97
104
  *
98
- * @param options The shutdown callbacks and timing configuration.
105
+ * @param options - The shutdown callbacks and timing configuration.
99
106
  */
100
- export const installGracefulShutdown = (options: GracefulShutdownOptions): void => {
107
+ function installGracefulShutdown(options: GracefulShutdownOptions): void {
101
108
  const state: ShutdownState = {
102
109
  options,
103
110
  forceKillMs: options.forceKillAfterMs ?? DEFAULT_FORCE_KILL_TIMEOUT_MS,
@@ -108,7 +115,12 @@ export const installGracefulShutdown = (options: GracefulShutdownOptions): void
108
115
  forceTimer: null,
109
116
  coalesceTimer: null,
110
117
  };
118
+
111
119
  for (const sig of HANDLED_SIGNALS) {
112
- process.on(sig, () => handle(state, sig));
120
+ process.on(sig, () => {
121
+ handle(state, sig);
122
+ });
113
123
  }
114
- };
124
+ }
125
+
126
+ export { installGracefulShutdown, type GracefulShutdownOptions };
@@ -0,0 +1,57 @@
1
+ import { accessSync, constants } from "node:fs";
2
+ import { delimiter, isAbsolute, join, resolve } from "node:path";
3
+
4
+ const isExecutable = (path: string): boolean => {
5
+ try {
6
+ accessSync(path, constants.X_OK);
7
+
8
+ return true;
9
+ } catch {
10
+ return false;
11
+ }
12
+ };
13
+
14
+ const findOnPath = (command: string): string | undefined => {
15
+ const searchPaths = (process.env.PATH ?? "").split(delimiter).filter((entry) => entry.length > 0);
16
+
17
+ for (const directory of searchPaths) {
18
+ const candidate = join(directory, command);
19
+
20
+ if (isExecutable(candidate)) {
21
+ return candidate;
22
+ }
23
+ }
24
+
25
+ return undefined;
26
+ };
27
+
28
+ /**
29
+ * Resolves an executable to an absolute path, searching `PATH` only for a bare command name, so
30
+ * spawning it never re-runs a lookup against a `PATH` that may have changed.
31
+ *
32
+ * @param command - An executable name, or a path to one.
33
+ * @returns The absolute path of the executable.
34
+ * @throws When a bare command name is not found on `PATH`.
35
+ *
36
+ * @example
37
+ * resolveExecutable("weston"); // "/usr/bin/weston"
38
+ */
39
+ function resolveExecutable(command: string): string {
40
+ if (isAbsolute(command)) {
41
+ return command;
42
+ }
43
+
44
+ if (command.includes("/")) {
45
+ return resolve(command);
46
+ }
47
+
48
+ const found = findOnPath(command);
49
+
50
+ if (found === undefined) {
51
+ throw new Error(`Cannot find the "${command}" executable on PATH`);
52
+ }
53
+
54
+ return found;
55
+ }
56
+
57
+ export { resolveExecutable };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Iterates every item currently in `set`, then clears it. Use it to drain a set of pending work
3
+ * accumulated since the last drain.
4
+ *
5
+ * @template T - The set item type.
6
+ * @param set - The set to drain and clear.
7
+ * @param visit - Called with each item before the set is cleared.
8
+ */
9
+ const drain = <T>(set: Set<T>, visit: (item: T) => void): void => {
10
+ for (const item of set) {
11
+ visit(item);
12
+ }
13
+
14
+ set.clear();
15
+ };
16
+
17
+ export { drain };
@@ -0,0 +1 @@
1
+ export { drain } from "./drain.js";
@@ -0,0 +1,3 @@
1
+ export { sanitizeIdentifier } from "./sanitize-identifier.js";
2
+ export { sourceStringLiteral } from "./source-string-literal.js";
3
+ export { toCamelIdentifier } from "./to-camel-identifier.js";
@@ -0,0 +1,66 @@
1
+ const RESERVED: Set<string> = new Set([
2
+ "arguments",
3
+ "await",
4
+ "break",
5
+ "case",
6
+ "catch",
7
+ "class",
8
+ "const",
9
+ "continue",
10
+ "debugger",
11
+ "default",
12
+ "delete",
13
+ "do",
14
+ "else",
15
+ "enum",
16
+ "eval",
17
+ "export",
18
+ "extends",
19
+ "false",
20
+ "finally",
21
+ "for",
22
+ "function",
23
+ "if",
24
+ "import",
25
+ "in",
26
+ "instanceof",
27
+ "interface",
28
+ "let",
29
+ "new",
30
+ "null",
31
+ "package",
32
+ "private",
33
+ "protected",
34
+ "public",
35
+ "return",
36
+ "static",
37
+ "super",
38
+ "switch",
39
+ "this",
40
+ "throw",
41
+ "true",
42
+ "try",
43
+ "typeof",
44
+ "var",
45
+ "void",
46
+ "while",
47
+ "with",
48
+ "yield",
49
+ ]);
50
+
51
+ /**
52
+ * Returns the name unchanged, or with a trailing underscore when it collides with a reserved word,
53
+ * so it is safe to emit as a JavaScript identifier.
54
+ *
55
+ * @param name - The candidate identifier.
56
+ * @returns The name, suffixed with `_` when it is a reserved word.
57
+ *
58
+ * @example
59
+ * sanitizeIdentifier("iconName"); // "iconName"
60
+ * sanitizeIdentifier("class"); // "class_"
61
+ */
62
+ function sanitizeIdentifier(name: string): string {
63
+ return RESERVED.has(name) ? `${name}_` : name;
64
+ }
65
+
66
+ export { sanitizeIdentifier };
@@ -0,0 +1,27 @@
1
+ const LINE_SEPARATOR = String.fromCodePoint(0x20_28);
2
+ const PARAGRAPH_SEPARATOR = String.fromCodePoint(0x20_29);
3
+
4
+ const SOURCE_ESCAPES: Record<string, string> = {
5
+ "<": String.raw`\u003C`,
6
+ ">": String.raw`\u003E`,
7
+ [LINE_SEPARATOR]: String.raw`\u2028`,
8
+ [PARAGRAPH_SEPARATOR]: String.raw`\u2029`,
9
+ };
10
+
11
+ const UNSAFE_SOURCE_CHARS = new RegExp(`[${Object.keys(SOURCE_ESCAPES).join("")}]`, "g");
12
+
13
+ /**
14
+ * Encodes a string as a JavaScript string literal, additionally escaping characters that are unsafe
15
+ * to embed in generated source (angle brackets and the line and paragraph separators).
16
+ *
17
+ * @param value - The string to encode.
18
+ * @returns The quoted, source-safe string literal.
19
+ *
20
+ * @example
21
+ * sourceStringLiteral("hello"); // '"hello"'
22
+ */
23
+ function sourceStringLiteral(value: string): string {
24
+ return JSON.stringify(value).replaceAll(UNSAFE_SOURCE_CHARS, (char) => SOURCE_ESCAPES[char] ?? char);
25
+ }
26
+
27
+ export { sourceStringLiteral };
@@ -0,0 +1,18 @@
1
+ import { camelCase } from "../string/camel-case.js";
2
+ import { sanitizeIdentifier } from "./sanitize-identifier.js";
3
+
4
+ /**
5
+ * Converts a name to camel case and sanitizes it into a valid JavaScript identifier.
6
+ *
7
+ * @param name - The name to convert.
8
+ * @returns The camel-cased, reserved-word-safe identifier.
9
+ *
10
+ * @example
11
+ * toCamelIdentifier("icon_name"); // "iconName"
12
+ * toCamelIdentifier("class"); // "class_"
13
+ */
14
+ function toCamelIdentifier(name: string): string {
15
+ return sanitizeIdentifier(camelCase(name));
16
+ }
17
+
18
+ export { toCamelIdentifier };
@@ -0,0 +1,22 @@
1
+ import { upperFirst } from "./upper-first.js";
2
+ import { mapWordSegments } from "./word-segments.js";
3
+
4
+ /**
5
+ * Converts an underscore- or hyphen-delimited string to camel case.
6
+ *
7
+ * Unlike a general-purpose converter, this splits only on `_` and `-` and keeps the first segment
8
+ * verbatim, so GObject acronyms are preserved (`camelCase("GLArea")` stays `"GLArea"`).
9
+ *
10
+ * @param str - The string to convert.
11
+ * @returns The camel-cased string.
12
+ *
13
+ * @example
14
+ * camelCase("icon_name"); // "iconName"
15
+ * camelCase("start-widget"); // "startWidget"
16
+ * camelCase("Box"); // "Box"
17
+ */
18
+ function camelCase(str: string): string {
19
+ return mapWordSegments(str, (part, index) => (index === 0 ? part : upperFirst(part)));
20
+ }
21
+
22
+ export { camelCase };