@stacksjs/utils 0.70.88 → 0.70.91
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bytes.d.ts +42 -0
- package/dist/bytes.js +55 -0
- package/dist/clean.d.ts +1 -0
- package/dist/clean.js +13 -0
- package/dist/colors.d.ts +146 -0
- package/dist/colors.js +100 -0
- package/dist/config.d.ts +23 -0
- package/dist/config.js +23 -0
- package/dist/debounce.d.ts +29 -0
- package/dist/debounce.js +77 -0
- package/dist/detect.d.ts +35 -0
- package/dist/detect.js +65 -0
- package/dist/equal.d.ts +13 -0
- package/dist/equal.js +27 -0
- package/dist/errors.d.ts +65 -0
- package/dist/errors.js +44 -0
- package/dist/export-size.d.ts +2 -0
- package/dist/export-size.js +1 -0
- package/dist/find.d.ts +4 -0
- package/dist/find.js +68 -0
- package/dist/git.d.ts +1 -0
- package/dist/git.js +11 -0
- package/dist/glob.d.ts +1 -0
- package/dist/glob.js +1 -0
- package/dist/hash.d.ts +3 -0
- package/dist/hash.js +36 -0
- package/dist/helpers.d.ts +53 -0
- package/dist/helpers.js +101 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +23 -0
- package/dist/macroable.d.ts +28 -0
- package/dist/macroable.js +121 -0
- package/dist/markdown.d.ts +46 -0
- package/dist/markdown.js +92 -0
- package/dist/merge.d.ts +20 -0
- package/dist/merge.js +51 -0
- package/dist/observer.d.ts +64 -0
- package/dist/observer.js +78 -0
- package/dist/pipeline.d.ts +13 -0
- package/dist/pipeline.js +37 -0
- package/dist/safe-json.d.ts +49 -0
- package/dist/safe-json.js +33 -0
- package/dist/size.d.ts +37 -0
- package/dist/size.js +66 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +6 -0
- package/dist/versions.d.ts +8 -0
- package/dist/versions.js +19 -0
- package/package.json +11 -11
package/dist/markdown.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
export function markdownTable(table, options = {}) {
|
|
2
|
+
const {
|
|
3
|
+
align = [],
|
|
4
|
+
padding = !0,
|
|
5
|
+
delimiter = "|",
|
|
6
|
+
delimiterStart = !0,
|
|
7
|
+
delimiterEnd = !0,
|
|
8
|
+
alignDelimiter = "-"
|
|
9
|
+
} = options;
|
|
10
|
+
if (!table || table.length === 0)
|
|
11
|
+
return "";
|
|
12
|
+
const normalized = table.map((row) => row.map((cell) => {
|
|
13
|
+
if (cell === null || cell === void 0)
|
|
14
|
+
return "";
|
|
15
|
+
return String(cell);
|
|
16
|
+
})), columnCount = Math.max(...normalized.map((row) => row.length)), columnWidths = Array.from({ length: columnCount }).fill(0);
|
|
17
|
+
for (const row of normalized)
|
|
18
|
+
for (let i = 0;i < row.length; i++) {
|
|
19
|
+
const cellLength = row[i]?.length ?? 0;
|
|
20
|
+
if (cellLength > (columnWidths[i] ?? 0))
|
|
21
|
+
columnWidths[i] = cellLength;
|
|
22
|
+
}
|
|
23
|
+
const rows = [];
|
|
24
|
+
if (normalized.length > 0) {
|
|
25
|
+
const header = normalized[0] ?? [];
|
|
26
|
+
rows.push(buildRow(header, columnWidths, padding, delimiter, delimiterStart, delimiterEnd));
|
|
27
|
+
const separatorCells = columnWidths.map((width, index) => {
|
|
28
|
+
const alignment = normalizeAlignment(align[index]);
|
|
29
|
+
return buildSeparator(width, alignment, alignDelimiter);
|
|
30
|
+
});
|
|
31
|
+
rows.push(buildRow(separatorCells, columnWidths, padding, delimiter, delimiterStart, delimiterEnd));
|
|
32
|
+
for (let i = 1;i < normalized.length; i++) {
|
|
33
|
+
const dataRow = normalized[i] ?? [];
|
|
34
|
+
rows.push(buildRow(dataRow, columnWidths, padding, delimiter, delimiterStart, delimiterEnd));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return rows.join(`
|
|
38
|
+
`);
|
|
39
|
+
}
|
|
40
|
+
function buildRow(cells, columnWidths, padding, delimiter, delimiterStart, delimiterEnd) {
|
|
41
|
+
const paddedCells = cells.map((cell, index) => {
|
|
42
|
+
const width = columnWidths[index] ?? 0;
|
|
43
|
+
return cell.padEnd(width, " ");
|
|
44
|
+
}), space = padding ? " " : "";
|
|
45
|
+
let row = paddedCells.map((cell) => `${space}${cell}${space}`).join(delimiter);
|
|
46
|
+
if (delimiterStart)
|
|
47
|
+
row = delimiter + row;
|
|
48
|
+
if (delimiterEnd)
|
|
49
|
+
row = row + delimiter;
|
|
50
|
+
return row;
|
|
51
|
+
}
|
|
52
|
+
function buildSeparator(width, alignment, char) {
|
|
53
|
+
const minWidth = alignment === "center" ? 3 : alignment ? 2 : 1;
|
|
54
|
+
if (width < minWidth)
|
|
55
|
+
width = minWidth;
|
|
56
|
+
let separator = char.repeat(width);
|
|
57
|
+
if (alignment === "left")
|
|
58
|
+
separator = `:${separator.slice(1)}`;
|
|
59
|
+
else if (alignment === "right")
|
|
60
|
+
separator = separator.slice(0, -1) + ":";
|
|
61
|
+
else if (alignment === "center")
|
|
62
|
+
separator = `:${separator.slice(2)}:`;
|
|
63
|
+
return separator;
|
|
64
|
+
}
|
|
65
|
+
function normalizeAlignment(align) {
|
|
66
|
+
if (!align)
|
|
67
|
+
return null;
|
|
68
|
+
switch (align) {
|
|
69
|
+
case "l":
|
|
70
|
+
case "left":
|
|
71
|
+
return "left";
|
|
72
|
+
case "c":
|
|
73
|
+
case "center":
|
|
74
|
+
return "center";
|
|
75
|
+
case "r":
|
|
76
|
+
case "right":
|
|
77
|
+
return "right";
|
|
78
|
+
default:
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export function markdownTableFromObjects(objects, options = {}) {
|
|
83
|
+
if (!objects || objects.length === 0)
|
|
84
|
+
return "";
|
|
85
|
+
const keys = Array.from(new Set(objects.flatMap((obj) => Object.keys(obj)))), table = [
|
|
86
|
+
keys,
|
|
87
|
+
...objects.map((obj) => keys.map((key) => obj[key]))
|
|
88
|
+
];
|
|
89
|
+
return markdownTable(table, options);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export { markdownTable as default };
|
package/dist/merge.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare function merge<T extends Record<string, any>>(...objects: DeepPartial<T>[]): T;
|
|
2
|
+
/**
|
|
3
|
+
* Create a merger function with defaults (defu-like API)
|
|
4
|
+
*/
|
|
5
|
+
export declare function createMerger<T extends Record<string, any>>(defaults: T): void;
|
|
6
|
+
/**
|
|
7
|
+
* Merge with defaults (defu API)
|
|
8
|
+
*/
|
|
9
|
+
export declare function mergeDefaults<T extends Record<string, any>>(obj: DeepPartial<T>, ...defaults: DeepPartial<T>[]): T;
|
|
10
|
+
// Aliases for defu compatibility
|
|
11
|
+
export declare const defu: unknown;
|
|
12
|
+
export declare const defuArrayFn: unknown;
|
|
13
|
+
/**
|
|
14
|
+
* Deep merge objects (defu replacement)
|
|
15
|
+
*
|
|
16
|
+
* Merges multiple objects together, with later objects taking precedence.
|
|
17
|
+
* Arrays are concatenated, objects are recursively merged.
|
|
18
|
+
*/
|
|
19
|
+
declare type DeepPartial<T> = T extends object ? { [P in keyof T]?: DeepPartial<T[P]> } : T;
|
|
20
|
+
export { merge as default };
|
package/dist/merge.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export function merge(...objects) {
|
|
2
|
+
const result = {};
|
|
3
|
+
for (const obj of objects) {
|
|
4
|
+
if (!obj || typeof obj !== "object")
|
|
5
|
+
continue;
|
|
6
|
+
for (const key in obj) {
|
|
7
|
+
const value = obj[key], existing = result[key];
|
|
8
|
+
if (value === void 0)
|
|
9
|
+
continue;
|
|
10
|
+
if (Array.isArray(value))
|
|
11
|
+
result[key] = Array.isArray(existing) ? [...existing, ...value] : [...value];
|
|
12
|
+
else if (value && typeof value === "object" && !isSpecialObject(value))
|
|
13
|
+
result[key] = merge(existing || {}, value);
|
|
14
|
+
else
|
|
15
|
+
result[key] = value;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
export function createMerger(defaults) {
|
|
21
|
+
return (...objects) => {
|
|
22
|
+
return merge(defaults, ...objects);
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function mergeDefaults(obj, ...defaults) {
|
|
26
|
+
return merge({}, ...defaults, obj);
|
|
27
|
+
}
|
|
28
|
+
function isSpecialObject(obj) {
|
|
29
|
+
if (obj instanceof Date)
|
|
30
|
+
return !0;
|
|
31
|
+
if (obj instanceof RegExp)
|
|
32
|
+
return !0;
|
|
33
|
+
if (obj instanceof Error)
|
|
34
|
+
return !0;
|
|
35
|
+
if (obj instanceof Promise)
|
|
36
|
+
return !0;
|
|
37
|
+
if (obj instanceof Map)
|
|
38
|
+
return !0;
|
|
39
|
+
if (obj instanceof Set)
|
|
40
|
+
return !0;
|
|
41
|
+
if (obj instanceof WeakMap)
|
|
42
|
+
return !0;
|
|
43
|
+
if (obj instanceof WeakSet)
|
|
44
|
+
return !0;
|
|
45
|
+
if (ArrayBuffer.isView(obj))
|
|
46
|
+
return !0;
|
|
47
|
+
return !1;
|
|
48
|
+
}
|
|
49
|
+
export const defu = mergeDefaults, defuArrayFn = merge;
|
|
50
|
+
|
|
51
|
+
export { merge as default };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if ResizeObserver is supported natively
|
|
3
|
+
*/
|
|
4
|
+
export declare function isResizeObserverSupported(): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Get ResizeObserver (native or polyfill)
|
|
7
|
+
*
|
|
8
|
+
* Modern browsers all support ResizeObserver, but this provides a fallback
|
|
9
|
+
*/
|
|
10
|
+
export declare function getResizeObserver(): any;
|
|
11
|
+
/**
|
|
12
|
+
* Create a resize observer with automatic cleanup
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* const cleanup = createResizeObserver((entries) => {
|
|
17
|
+
* for (const entry of entries) {
|
|
18
|
+
* console.log('Element resized:', entry.target)
|
|
19
|
+
* }
|
|
20
|
+
* }, element)
|
|
21
|
+
*
|
|
22
|
+
* // Later, clean up
|
|
23
|
+
* cleanup()
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function createResizeObserver(callback: ResizeObserverCallback, target: Element | Element[]): () => void;
|
|
27
|
+
/**
|
|
28
|
+
* Observe element size changes
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* const stop = observeElementSize(element, (entry) => {
|
|
33
|
+
* console.log('Size:', entry.contentRect.width, entry.contentRect.height)
|
|
34
|
+
* })
|
|
35
|
+
*
|
|
36
|
+
* // Stop observing
|
|
37
|
+
* stop()
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function observeElementSize(element: Element, callback: (entry: ResizeObserverEntry) => void): () => void;
|
|
41
|
+
// Export the native ResizeObserver if available, or the polyfill
|
|
42
|
+
export declare const ResizeObserver: any;
|
|
43
|
+
/**
|
|
44
|
+
* ResizeObserver polyfill (resize-observer-polyfill replacement)
|
|
45
|
+
*
|
|
46
|
+
* Note: Modern browsers all support ResizeObserver natively now.
|
|
47
|
+
* This provides a simple fallback for older browsers.
|
|
48
|
+
*/
|
|
49
|
+
export declare interface ResizeObserverSize {
|
|
50
|
+
readonly inlineSize: number
|
|
51
|
+
readonly blockSize: number
|
|
52
|
+
}
|
|
53
|
+
export declare interface ResizeObserverEntry {
|
|
54
|
+
readonly target: Element
|
|
55
|
+
readonly contentRect: DOMRectReadOnly
|
|
56
|
+
readonly borderBoxSize: ReadonlyArray<ResizeObserverSize>
|
|
57
|
+
readonly contentBoxSize: ReadonlyArray<ResizeObserverSize>
|
|
58
|
+
readonly devicePixelContentBoxSize: ReadonlyArray<ResizeObserverSize>
|
|
59
|
+
}
|
|
60
|
+
export type ResizeObserverCallback = (
|
|
61
|
+
entries: ResizeObserverEntry[],
|
|
62
|
+
observer: ResizeObserver,
|
|
63
|
+
) => void;
|
|
64
|
+
export default ResizeObserver;
|
package/dist/observer.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export function isResizeObserverSupported() {
|
|
2
|
+
return typeof window < "u" && "ResizeObserver" in window;
|
|
3
|
+
}
|
|
4
|
+
export function getResizeObserver() {
|
|
5
|
+
if (isResizeObserverSupported())
|
|
6
|
+
return window.ResizeObserver;
|
|
7
|
+
return class ResizeObserverPolyfill {
|
|
8
|
+
callback;
|
|
9
|
+
observedElements = new Set;
|
|
10
|
+
rafId = null;
|
|
11
|
+
constructor(callback) {
|
|
12
|
+
this.callback = callback;
|
|
13
|
+
}
|
|
14
|
+
observe(target) {
|
|
15
|
+
if (!this.observedElements.has(target)) {
|
|
16
|
+
this.observedElements.add(target);
|
|
17
|
+
this.scheduleCheck();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
unobserve(target) {
|
|
21
|
+
this.observedElements.delete(target);
|
|
22
|
+
if (this.observedElements.size === 0 && this.rafId !== null) {
|
|
23
|
+
(typeof cancelAnimationFrame < "u" ? cancelAnimationFrame : clearTimeout)(this.rafId);
|
|
24
|
+
this.rafId = null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
disconnect() {
|
|
28
|
+
this.observedElements.clear();
|
|
29
|
+
if (this.rafId !== null) {
|
|
30
|
+
(typeof cancelAnimationFrame < "u" ? cancelAnimationFrame : clearTimeout)(this.rafId);
|
|
31
|
+
this.rafId = null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
scheduleCheck() {
|
|
35
|
+
if (this.rafId !== null)
|
|
36
|
+
return;
|
|
37
|
+
const raf = typeof requestAnimationFrame < "u" ? requestAnimationFrame : setTimeout;
|
|
38
|
+
this.rafId = raf(() => {
|
|
39
|
+
this.check();
|
|
40
|
+
this.rafId = null;
|
|
41
|
+
if (this.observedElements.size > 0)
|
|
42
|
+
this.scheduleCheck();
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
check() {
|
|
46
|
+
const entries = [];
|
|
47
|
+
for (const element of this.observedElements) {
|
|
48
|
+
const rect = element.getBoundingClientRect(), entry = {
|
|
49
|
+
target: element,
|
|
50
|
+
contentRect: rect,
|
|
51
|
+
borderBoxSize: [{ inlineSize: rect.width, blockSize: rect.height }],
|
|
52
|
+
contentBoxSize: [{ inlineSize: rect.width, blockSize: rect.height }],
|
|
53
|
+
devicePixelContentBoxSize: [{ inlineSize: rect.width, blockSize: rect.height }]
|
|
54
|
+
};
|
|
55
|
+
entries.push(entry);
|
|
56
|
+
}
|
|
57
|
+
if (entries.length > 0)
|
|
58
|
+
this.callback(entries, this);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export function createResizeObserver(callback, target) {
|
|
63
|
+
const observer = new (getResizeObserver())(callback), targets = Array.isArray(target) ? target : [target];
|
|
64
|
+
for (const element of targets)
|
|
65
|
+
observer.observe(element);
|
|
66
|
+
return () => {
|
|
67
|
+
observer.disconnect();
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export function observeElementSize(element, callback) {
|
|
71
|
+
return createResizeObserver((entries) => {
|
|
72
|
+
const entry = entries.find((e) => e.target === element);
|
|
73
|
+
if (entry)
|
|
74
|
+
callback(entry);
|
|
75
|
+
}, element);
|
|
76
|
+
}
|
|
77
|
+
export const ResizeObserver = getResizeObserver();
|
|
78
|
+
export default ResizeObserver;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
declare interface PipeClass<T> {
|
|
2
|
+
handle: PipeFunction<T>
|
|
3
|
+
}
|
|
4
|
+
declare type PipeFunction<T> = (data: T, next: (data: T) => T) => T;
|
|
5
|
+
declare type Pipe<T> = PipeFunction<T> | PipeClass<T>;
|
|
6
|
+
export declare class Pipeline<T> {
|
|
7
|
+
static send<T>(passable: T): Pipeline<T>;
|
|
8
|
+
through(pipes: Pipe<T>[]): this;
|
|
9
|
+
pipe(...pipes: Pipe<T>[]): this;
|
|
10
|
+
via(method: string): this;
|
|
11
|
+
then(destination: (data: T) => T): T;
|
|
12
|
+
thenReturn(): T;
|
|
13
|
+
}
|
package/dist/pipeline.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export class Pipeline {
|
|
2
|
+
passable;
|
|
3
|
+
pipes = [];
|
|
4
|
+
method = "handle";
|
|
5
|
+
static send(passable) {
|
|
6
|
+
const pipeline = new Pipeline;
|
|
7
|
+
pipeline.passable = passable;
|
|
8
|
+
return pipeline;
|
|
9
|
+
}
|
|
10
|
+
through(pipes) {
|
|
11
|
+
this.pipes = pipes;
|
|
12
|
+
return this;
|
|
13
|
+
}
|
|
14
|
+
pipe(...pipes) {
|
|
15
|
+
this.pipes.push(...pipes);
|
|
16
|
+
return this;
|
|
17
|
+
}
|
|
18
|
+
via(method) {
|
|
19
|
+
this.method = method;
|
|
20
|
+
return this;
|
|
21
|
+
}
|
|
22
|
+
then(destination) {
|
|
23
|
+
return this.pipes.reduceRight((next, pipe) => {
|
|
24
|
+
return (passable) => {
|
|
25
|
+
if (typeof pipe === "function")
|
|
26
|
+
return pipe(passable, next);
|
|
27
|
+
const handler = pipe[this.method];
|
|
28
|
+
if (typeof handler === "function")
|
|
29
|
+
return handler.call(pipe, passable, next);
|
|
30
|
+
return next(passable);
|
|
31
|
+
};
|
|
32
|
+
}, destination)(this.passable);
|
|
33
|
+
}
|
|
34
|
+
thenReturn() {
|
|
35
|
+
return this.then((passable) => passable);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse `text` as JSON, returning `defaultValue` (default: `undefined`)
|
|
3
|
+
* when the input isn't valid JSON.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* const config = safeJsonParse<Config>(rawText, { mode: 'default' })
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
10
|
+
export declare function safeJsonParse<T = unknown>(text: unknown, defaultValue?: T): T | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Result-flavored variant: surfaces *why* a parse failed when callers
|
|
13
|
+
* want to log the difference between "empty input" and "malformed input".
|
|
14
|
+
*/
|
|
15
|
+
// eslint-disable-next-line pickier/no-unused-vars
|
|
16
|
+
export declare function safeJsonParseResult<T = unknown>(text: unknown): { ok: true, value: T } | { ok: false, reason: 'not-string' | 'empty' | 'malformed', error?: unknown };
|
|
17
|
+
/**
|
|
18
|
+
* Stringify with cycle and bigint safety.
|
|
19
|
+
*
|
|
20
|
+
* `JSON.stringify` throws on circular refs (rare but real, e.g. logger
|
|
21
|
+
* captures of an Express-style req object) and on bigint values (common
|
|
22
|
+
* with database IDs that overflow Number). This wrapper substitutes
|
|
23
|
+
* `[Circular]` for cycles and converts bigints to strings — same shape
|
|
24
|
+
* downstream consumers expect from a "best-effort serialize this for
|
|
25
|
+
* logs" call.
|
|
26
|
+
*/
|
|
27
|
+
export declare function safeJsonStringify(value: unknown, space?: number): string;
|
|
28
|
+
/**
|
|
29
|
+
* Safe JSON wrappers.
|
|
30
|
+
*
|
|
31
|
+
* Bare `JSON.parse(s)` throws `SyntaxError` on malformed input —
|
|
32
|
+
* which 90% of the time is exactly what you want, except in:
|
|
33
|
+
*
|
|
34
|
+
* - Webhook handlers: a malformed payload should produce a 400, not crash
|
|
35
|
+
* - Cache reads: a corrupt cache entry should miss, not throw
|
|
36
|
+
* - Log readers: a bad line should be skipped, not stop the parser
|
|
37
|
+
* - User-supplied config: a typo should produce a clear "X is invalid" error
|
|
38
|
+
*
|
|
39
|
+
* `safeJsonParse` returns `undefined` for malformed input (or a typed
|
|
40
|
+
* default), so call sites don't need a try/catch ladder around every
|
|
41
|
+
* untrusted input. `safeJsonParseResult` returns a discriminated union
|
|
42
|
+
* for cases where the failure reason matters.
|
|
43
|
+
*/
|
|
44
|
+
export type JsonValue = | string
|
|
45
|
+
| number
|
|
46
|
+
| boolean
|
|
47
|
+
| null
|
|
48
|
+
| JsonValue[]
|
|
49
|
+
| { [key: string]: JsonValue }
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export function safeJsonParse(text, defaultValue) {
|
|
2
|
+
if (typeof text !== "string" || text.length === 0)
|
|
3
|
+
return defaultValue;
|
|
4
|
+
try {
|
|
5
|
+
return JSON.parse(text);
|
|
6
|
+
} catch {
|
|
7
|
+
return defaultValue;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function safeJsonParseResult(text) {
|
|
11
|
+
if (typeof text !== "string")
|
|
12
|
+
return { ok: !1, reason: "not-string" };
|
|
13
|
+
if (text.length === 0)
|
|
14
|
+
return { ok: !1, reason: "empty" };
|
|
15
|
+
try {
|
|
16
|
+
return { ok: !0, value: JSON.parse(text) };
|
|
17
|
+
} catch (err) {
|
|
18
|
+
return { ok: !1, reason: "malformed", error: err };
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function safeJsonStringify(value, space) {
|
|
22
|
+
const seen = new WeakSet;
|
|
23
|
+
return JSON.stringify(value, (_key, v) => {
|
|
24
|
+
if (typeof v === "bigint")
|
|
25
|
+
return v.toString();
|
|
26
|
+
if (v && typeof v === "object") {
|
|
27
|
+
if (seen.has(v))
|
|
28
|
+
return "[Circular]";
|
|
29
|
+
seen.add(v);
|
|
30
|
+
}
|
|
31
|
+
return v;
|
|
32
|
+
}, space);
|
|
33
|
+
}
|
package/dist/size.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the size of a file
|
|
3
|
+
*/
|
|
4
|
+
export declare function getFileSize(filePath: string): Promise<number>;
|
|
5
|
+
/**
|
|
6
|
+
* Get sizes of all exports in a package
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* const sizes = await getExportsSize('./dist')
|
|
11
|
+
* console.log(sizes.totalReadable) // "42.5 KB"
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export declare function getExportsSize(distPath: string): Promise<PackageExportsSizeResult>;
|
|
15
|
+
/**
|
|
16
|
+
* Get gzipped size estimate
|
|
17
|
+
*/
|
|
18
|
+
export declare function estimateGzipSize(size: number): number;
|
|
19
|
+
/**
|
|
20
|
+
* Get brotli size estimate
|
|
21
|
+
*/
|
|
22
|
+
export declare function estimateBrotliSize(size: number): number;
|
|
23
|
+
/**
|
|
24
|
+
* Format export sizes as a table
|
|
25
|
+
*/
|
|
26
|
+
export declare function formatExportSizes(result: PackageExportsSizeResult): string;
|
|
27
|
+
export declare interface ExportSize {
|
|
28
|
+
name: string
|
|
29
|
+
size: number
|
|
30
|
+
readable: string
|
|
31
|
+
}
|
|
32
|
+
export declare interface PackageExportsSizeResult {
|
|
33
|
+
totalSize: number
|
|
34
|
+
totalReadable: string
|
|
35
|
+
exports: ExportSize[]
|
|
36
|
+
}
|
|
37
|
+
export { getExportsSize as default };
|
package/dist/size.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { stat } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { formatBytes } from "./bytes";
|
|
4
|
+
export async function getFileSize(filePath) {
|
|
5
|
+
try {
|
|
6
|
+
return (await stat(filePath)).size;
|
|
7
|
+
} catch {
|
|
8
|
+
return 0;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export async function getExportsSize(distPath) {
|
|
12
|
+
const exports = [];
|
|
13
|
+
let totalSize = 0;
|
|
14
|
+
try {
|
|
15
|
+
const files = await getAllJsFiles(distPath);
|
|
16
|
+
for (const file of files) {
|
|
17
|
+
const size = await getFileSize(file.path);
|
|
18
|
+
totalSize += size;
|
|
19
|
+
exports.push({
|
|
20
|
+
name: file.name,
|
|
21
|
+
size,
|
|
22
|
+
readable: formatBytes(size)
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
} catch (error) {}
|
|
26
|
+
return {
|
|
27
|
+
totalSize,
|
|
28
|
+
totalReadable: formatBytes(totalSize),
|
|
29
|
+
exports: exports.sort((a, b) => b.size - a.size)
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
async function getAllJsFiles(dir) {
|
|
33
|
+
const files = [];
|
|
34
|
+
try {
|
|
35
|
+
const entries = await Bun.$.throws(!1)`find ${dir} -type f \\( -name "*.js" -o -name "*.mjs" -o -name "*.cjs" \\)`.text();
|
|
36
|
+
for (const entry of entries.split(`
|
|
37
|
+
`).filter(Boolean)) {
|
|
38
|
+
const name = entry.replace(dir, "").replace(/^\//, "");
|
|
39
|
+
files.push({ name, path: entry });
|
|
40
|
+
}
|
|
41
|
+
} catch {}
|
|
42
|
+
return files;
|
|
43
|
+
}
|
|
44
|
+
export function estimateGzipSize(size) {
|
|
45
|
+
return Math.floor(size * 0.3);
|
|
46
|
+
}
|
|
47
|
+
export function estimateBrotliSize(size) {
|
|
48
|
+
return Math.floor(size * 0.25);
|
|
49
|
+
}
|
|
50
|
+
export function formatExportSizes(result) {
|
|
51
|
+
if (result.exports.length === 0)
|
|
52
|
+
return "No exports found";
|
|
53
|
+
const lines = ["Export Sizes:", ""], maxNameLength = Math.max(...result.exports.map((e) => e.name.length));
|
|
54
|
+
for (const exp of result.exports) {
|
|
55
|
+
const name = exp.name.padEnd(maxNameLength, " ");
|
|
56
|
+
lines.push(` ${name} ${exp.readable}`);
|
|
57
|
+
}
|
|
58
|
+
lines.push("");
|
|
59
|
+
lines.push(`Total: ${result.totalReadable}`);
|
|
60
|
+
lines.push(`Estimated gzipped: ${formatBytes(estimateGzipSize(result.totalSize))}`);
|
|
61
|
+
lines.push(`Estimated brotli: ${formatBytes(estimateBrotliSize(result.totalSize))}`);
|
|
62
|
+
return lines.join(`
|
|
63
|
+
`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export { getExportsSize as default };
|
package/dist/utils.d.ts
ADDED
package/dist/utils.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
var __dirname = "";
|
|
2
|
+
import { resolve } from "@stacksjs/path";
|
|
3
|
+
import { fs } from "@stacksjs/storage";
|
|
4
|
+
import { kebabCase } from "@stacksjs/strings";
|
|
5
|
+
const ignore = ["readme-md"];
|
|
6
|
+
export const components = fs.readdirSync(resolve(__dirname, "./resources/components")).map((item) => kebabCase(item.replace(/\.(stx|vue)/g, ""))).filter((item) => !ignore.includes(item)), functions = fs.readdirSync(resolve(__dirname, "./resources/functions")).map((item) => kebabCase(item.replace(/.ts/g, ""))).filter((item) => !ignore.includes(item));
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { version } from '../package.json';
|
|
2
|
+
export declare function isVersionGreaterThan(version: string, compareVersion: string): boolean;
|
|
3
|
+
export declare function isVersionLessThan(version: string, compareVersion: string): boolean;
|
|
4
|
+
export declare function isVersionEqual(version: string, compareVersion: string): boolean;
|
|
5
|
+
export declare function isVersionGreaterThanOrEqual(version: string, compareVersion: string): boolean;
|
|
6
|
+
export declare function isVersionLessThanOrEqual(version: string, compareVersion: string): boolean;
|
|
7
|
+
export declare const semver: typeof Bun.semver;
|
|
8
|
+
export { version };
|
package/dist/versions.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { version } from "../package.json";
|
|
2
|
+
export const semver = Bun.semver;
|
|
3
|
+
export function isVersionGreaterThan(version, compareVersion) {
|
|
4
|
+
return Bun.semver.order(version, compareVersion) === 1;
|
|
5
|
+
}
|
|
6
|
+
export function isVersionLessThan(version, compareVersion) {
|
|
7
|
+
return Bun.semver.order(version, compareVersion) === -1;
|
|
8
|
+
}
|
|
9
|
+
export function isVersionEqual(version, compareVersion) {
|
|
10
|
+
return Bun.semver.order(version, compareVersion) === 0;
|
|
11
|
+
}
|
|
12
|
+
export function isVersionGreaterThanOrEqual(version, compareVersion) {
|
|
13
|
+
return Bun.semver.order(version, compareVersion) >= 0;
|
|
14
|
+
}
|
|
15
|
+
export function isVersionLessThanOrEqual(version, compareVersion) {
|
|
16
|
+
return Bun.semver.order(version, compareVersion) <= 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { version };
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/utils",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.70.
|
|
5
|
+
"version": "0.70.91",
|
|
6
6
|
"description": "The Stacks helper functions.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -58,20 +58,20 @@
|
|
|
58
58
|
"size": "bun scripts/export-size.ts"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@stacksjs/browser": "0.70.
|
|
61
|
+
"@stacksjs/browser": "0.70.91"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
|
-
"@stacksjs/actions": "0.70.
|
|
65
|
-
"@stacksjs/arrays": "0.70.
|
|
66
|
-
"@stacksjs/cli": "0.70.
|
|
67
|
-
"@stacksjs/config": "0.70.
|
|
64
|
+
"@stacksjs/actions": "0.70.91",
|
|
65
|
+
"@stacksjs/arrays": "0.70.91",
|
|
66
|
+
"@stacksjs/cli": "0.70.91",
|
|
67
|
+
"@stacksjs/config": "0.70.91",
|
|
68
68
|
"better-dx": "^0.2.16",
|
|
69
69
|
"filesize": "^11.0.22",
|
|
70
70
|
"markdown-table": "^3.0.4",
|
|
71
|
-
"@stacksjs/lint": "0.70.
|
|
72
|
-
"@stacksjs/path": "0.70.
|
|
73
|
-
"@stacksjs/storage": "0.70.
|
|
74
|
-
"@stacksjs/strings": "0.70.
|
|
75
|
-
"@stacksjs/types": "0.70.
|
|
71
|
+
"@stacksjs/lint": "0.70.91",
|
|
72
|
+
"@stacksjs/path": "0.70.91",
|
|
73
|
+
"@stacksjs/storage": "0.70.91",
|
|
74
|
+
"@stacksjs/strings": "0.70.91",
|
|
75
|
+
"@stacksjs/types": "0.70.91"
|
|
76
76
|
}
|
|
77
77
|
}
|