@keeex/utils 7.4.0 → 7.6.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/lib/array.d.ts +2 -0
- package/lib/array.js +3 -0
- package/lib/async/eventqueue.js +1 -1
- package/lib/dataview.d.ts +22 -0
- package/lib/dataview.js +29 -0
- package/lib/dict.d.ts +32 -0
- package/lib/dict.js +63 -0
- package/lib/promise.js +0 -1
- package/lib/string.d.ts +8 -0
- package/lib/string.js +32 -0
- package/package.json +1 -1
- package/web/array.d.ts +2 -0
- package/web/array.js +4 -1
- package/web/async/eventqueue.js +1 -1
- package/web/dataview.d.ts +22 -0
- package/web/dataview.js +27 -0
- package/web/dict.d.ts +32 -0
- package/web/dict.js +66 -0
- package/web/promise.js +0 -1
- package/web/string.d.ts +8 -0
- package/web/string.js +29 -1
package/lib/array.d.ts
CHANGED
|
@@ -26,3 +26,5 @@ export declare const arrayEqual: <T1 = unknown, T2 = unknown>(op1: ArrayLike<T1>
|
|
|
26
26
|
* @public
|
|
27
27
|
*/
|
|
28
28
|
export declare const asArray: <T>(data: Array<T> | (T extends Array<unknown> ? never : T)) => Array<T>;
|
|
29
|
+
/** Return a new array with its string sorted */
|
|
30
|
+
export declare const toSortedStringArray: (array: Array<string>) => Array<string>;
|
package/lib/array.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
14
14
|
*
|
|
15
15
|
*/
|
|
16
|
+
import { alphaSort } from "./string.js";
|
|
16
17
|
const useEqOp = (op1, op2) => op1 === op2;
|
|
17
18
|
/**
|
|
18
19
|
* Compare two array-like structure for equal content
|
|
@@ -34,3 +35,5 @@ export const arrayEqual = (op1, op2, eqFunc = useEqOp) => {
|
|
|
34
35
|
* @public
|
|
35
36
|
*/
|
|
36
37
|
export const asArray = (data) => Array.isArray(data) ? data : [data];
|
|
38
|
+
/** Return a new array with its string sorted */
|
|
39
|
+
export const toSortedStringArray = (array) => array.toSorted(alphaSort);
|
package/lib/async/eventqueue.js
CHANGED
|
@@ -101,7 +101,7 @@ export class EventQueue {
|
|
|
101
101
|
// eslint-disable-next-line no-await-in-loop
|
|
102
102
|
const nextEvent = await this.#getNextEvent();
|
|
103
103
|
// eslint-disable-next-line no-await-in-loop
|
|
104
|
-
await Promise.all(this.#handlers.map((handler) => handler(nextEvent)));
|
|
104
|
+
await Promise.all(this.#handlers.map(async (handler) => handler(nextEvent)));
|
|
105
105
|
if (this.#events.length > 0) {
|
|
106
106
|
this.#log.debug("More events in queue");
|
|
107
107
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* @preserve
|
|
4
|
+
*
|
|
5
|
+
* MIT License
|
|
6
|
+
*
|
|
7
|
+
* Copyright (c) 2023 KeeeX SAS
|
|
8
|
+
*
|
|
9
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
10
|
+
*
|
|
11
|
+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
12
|
+
*
|
|
13
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
14
|
+
*
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Return a `DataView` on the boundaries of the provided `data`.
|
|
18
|
+
*
|
|
19
|
+
* @param byteOffset - Offset after the start of `data` in its buffer
|
|
20
|
+
* @param byteLength - Length of the available data within `data`
|
|
21
|
+
*/
|
|
22
|
+
export declare const getDataView: (data: Uint8Array, byteOffset?: number, byteLength?: number) => DataView;
|
package/lib/dataview.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* @preserve
|
|
4
|
+
*
|
|
5
|
+
* MIT License
|
|
6
|
+
*
|
|
7
|
+
* Copyright (c) 2023 KeeeX SAS
|
|
8
|
+
*
|
|
9
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
10
|
+
*
|
|
11
|
+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
12
|
+
*
|
|
13
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
14
|
+
*
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Return a `DataView` on the boundaries of the provided `data`.
|
|
18
|
+
*
|
|
19
|
+
* @param byteOffset - Offset after the start of `data` in its buffer
|
|
20
|
+
* @param byteLength - Length of the available data within `data`
|
|
21
|
+
*/
|
|
22
|
+
export const getDataView = (data, byteOffset = 0, byteLength = data.byteLength - byteOffset) => {
|
|
23
|
+
if (byteOffset < 0 ||
|
|
24
|
+
byteOffset >= data.byteLength ||
|
|
25
|
+
byteOffset + byteLength > data.byteLength) {
|
|
26
|
+
throw new Error("Out of bound");
|
|
27
|
+
}
|
|
28
|
+
return new DataView(data.buffer, data.byteOffset + byteOffset, byteLength);
|
|
29
|
+
};
|
package/lib/dict.d.ts
CHANGED
|
@@ -54,3 +54,35 @@ export type PrimitiveTypeObject = Record<string, unknown> | Array<unknown> | str
|
|
|
54
54
|
* @public
|
|
55
55
|
*/
|
|
56
56
|
export declare const deepCopyPrimitive: (source: unknown) => unknown;
|
|
57
|
+
/**
|
|
58
|
+
* Perform the merge operation between two values.
|
|
59
|
+
*
|
|
60
|
+
* If the new value have no changes from the initial value, this should always return `initial`.
|
|
61
|
+
* If there is any change, this should always return a new reference.
|
|
62
|
+
*
|
|
63
|
+
* TODO: The function returns unknown because I can't work around the requirements for TypeScript to
|
|
64
|
+
* be happy using `DataType | null`.
|
|
65
|
+
*/
|
|
66
|
+
type RefMergeDefFn<DataType> = (initial: DataType | null, newValue: DataType | null) => unknown;
|
|
67
|
+
type RefMergeDef<DataType> = DataType extends string ? "string" : DataType extends number ? "number" : DataType extends boolean ? "boolean" : DataType extends Array<string> ? "string[]" : RefMergeDefFn<DataType>;
|
|
68
|
+
type RefMergeProfile<ObjectType> = Required<{
|
|
69
|
+
[key in keyof ObjectType]: RefMergeDef<NonNullable<ObjectType[key]>>;
|
|
70
|
+
}>;
|
|
71
|
+
/**
|
|
72
|
+
* Examine each props on both `initial` and `newValue`, and if there is any change, return a new
|
|
73
|
+
* reference with all values.
|
|
74
|
+
*
|
|
75
|
+
* @param initial - The source object. If no changes are detected, returns it.
|
|
76
|
+
* @param newValue - Values to update in `initial`. `undefined` values are skipped.
|
|
77
|
+
* @param profile - Describe the expected properties and how to merge them.
|
|
78
|
+
*
|
|
79
|
+
* @returns
|
|
80
|
+
* If there is some change from `initial`, will return a new reference with all new values.
|
|
81
|
+
* This property extends to values that are object; all objects in this hierarchy that have an
|
|
82
|
+
* update belows it is replaced with a new reference.
|
|
83
|
+
*
|
|
84
|
+
* This is made to help with libraries like React that depends on state reference actually getting
|
|
85
|
+
* changed on update.
|
|
86
|
+
*/
|
|
87
|
+
export declare const refMergeObjects: <ObjectType>(initial: ObjectType, newValue: Partial<ObjectType>, profile: RefMergeProfile<ObjectType>) => ObjectType;
|
|
88
|
+
export {};
|
package/lib/dict.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
14
14
|
*
|
|
15
15
|
*/
|
|
16
|
+
import { arrayEqual } from "./array.js";
|
|
16
17
|
/**
|
|
17
18
|
* Copy all properties from secondary into primary.
|
|
18
19
|
*
|
|
@@ -72,3 +73,65 @@ export const deepCopyPrimitive = (source) => {
|
|
|
72
73
|
return res;
|
|
73
74
|
}
|
|
74
75
|
};
|
|
76
|
+
const refMergeStringArray = (initial, newValue) => {
|
|
77
|
+
if (newValue === null)
|
|
78
|
+
return null;
|
|
79
|
+
if (initial !== null && arrayEqual(initial, newValue))
|
|
80
|
+
return initial;
|
|
81
|
+
return [...newValue];
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Merge a new value and an old value according to the provided merger.
|
|
85
|
+
*
|
|
86
|
+
* If `newValue` is undefined, `initial` is always returned.
|
|
87
|
+
* If `newValue` is defined AND have difference from `initial`, a new object should be returned.
|
|
88
|
+
* If there is no change, `initial` is returned.
|
|
89
|
+
*/
|
|
90
|
+
const getMergedRef = (initial, newValue, merger) => {
|
|
91
|
+
if (newValue === undefined)
|
|
92
|
+
return initial;
|
|
93
|
+
switch (merger) {
|
|
94
|
+
case "string":
|
|
95
|
+
case "number":
|
|
96
|
+
case "boolean":
|
|
97
|
+
return newValue;
|
|
98
|
+
case "string[]":
|
|
99
|
+
return refMergeStringArray(initial, newValue);
|
|
100
|
+
default: {
|
|
101
|
+
return merger(initial, newValue);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Examine each props on both `initial` and `newValue`, and if there is any change, return a new
|
|
107
|
+
* reference with all values.
|
|
108
|
+
*
|
|
109
|
+
* @param initial - The source object. If no changes are detected, returns it.
|
|
110
|
+
* @param newValue - Values to update in `initial`. `undefined` values are skipped.
|
|
111
|
+
* @param profile - Describe the expected properties and how to merge them.
|
|
112
|
+
*
|
|
113
|
+
* @returns
|
|
114
|
+
* If there is some change from `initial`, will return a new reference with all new values.
|
|
115
|
+
* This property extends to values that are object; all objects in this hierarchy that have an
|
|
116
|
+
* update belows it is replaced with a new reference.
|
|
117
|
+
*
|
|
118
|
+
* This is made to help with libraries like React that depends on state reference actually getting
|
|
119
|
+
* changed on update.
|
|
120
|
+
*/
|
|
121
|
+
export const refMergeObjects = (initial, newValue, profile) => {
|
|
122
|
+
let anyChange = false;
|
|
123
|
+
const newValues = Object.entries(profile).map(([key, merger]) => {
|
|
124
|
+
const initialValue = initial[key];
|
|
125
|
+
const merged = getMergedRef(initialValue, newValue[key], merger);
|
|
126
|
+
anyChange ||= merged !== initialValue;
|
|
127
|
+
return { key, merged };
|
|
128
|
+
});
|
|
129
|
+
// Can't statically detect changes in map() callback
|
|
130
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
131
|
+
if (!anyChange)
|
|
132
|
+
return initial;
|
|
133
|
+
return newValues.reduce((acc, { key, merged }) => {
|
|
134
|
+
acc[key] = merged;
|
|
135
|
+
return acc;
|
|
136
|
+
}, {});
|
|
137
|
+
};
|
package/lib/promise.js
CHANGED
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
14
14
|
*
|
|
15
15
|
*/
|
|
16
|
-
/* eslint-env node */
|
|
17
16
|
import * as log from "@keeex/log";
|
|
18
17
|
import { asError } from "./error.js";
|
|
19
18
|
import { timeConvert } from "./units.js";
|
package/lib/string.d.ts
CHANGED
|
@@ -63,3 +63,11 @@ export declare const encodeUTF8: (str: string) => string;
|
|
|
63
63
|
export declare const decodeUTF8: (str: string, skipErrors?: boolean) => string;
|
|
64
64
|
/** Capitalize (or uncapitalize) the first character of a string */
|
|
65
65
|
export declare const capitalize: (str: string, upperFirstLetter: boolean) => string;
|
|
66
|
+
/** Ensure the string ends with one newline */
|
|
67
|
+
export declare const enforceNewlineEnd: (str: string) => string;
|
|
68
|
+
/** Format the text as a single line or multiline JS comment */
|
|
69
|
+
export declare const jsComment: (text: string, jsDoc?: boolean) => string;
|
|
70
|
+
/** Make sure the string starts with a capital, and all other letters are lowercase */
|
|
71
|
+
export declare const uncapitalize: (str: string) => string;
|
|
72
|
+
/** Helper to use with `Array.sort()` and similar */
|
|
73
|
+
export declare const alphaSort: (a: string, b: string) => number;
|
package/lib/string.js
CHANGED
|
@@ -106,3 +106,35 @@ export const capitalize = (str, upperFirstLetter) => {
|
|
|
106
106
|
const rest = str.slice(1);
|
|
107
107
|
return `${transformedFirstLetter}${rest}`;
|
|
108
108
|
};
|
|
109
|
+
/** Return the last index of a character that pass the needle test, or -1 if it never happens */
|
|
110
|
+
const findLastIndexOf = (hay, needle) => {
|
|
111
|
+
for (let index = hay.length - 1; --index; index >= 0) {
|
|
112
|
+
if (needle(hay.at(index)))
|
|
113
|
+
return index;
|
|
114
|
+
}
|
|
115
|
+
return -1;
|
|
116
|
+
};
|
|
117
|
+
/** Ensure the string ends with one newline */
|
|
118
|
+
export const enforceNewlineEnd = (str) => {
|
|
119
|
+
if (str.endsWith("\n")) {
|
|
120
|
+
const firstNonNewline = findLastIndexOf(str, (c) => c !== "\n");
|
|
121
|
+
if (firstNonNewline === -1)
|
|
122
|
+
return "\n";
|
|
123
|
+
return `${str.slice(0, firstNonNewline + 1)}\n`;
|
|
124
|
+
}
|
|
125
|
+
return `${str}\n`;
|
|
126
|
+
};
|
|
127
|
+
/** Format the text as a single line or multiline JS comment */
|
|
128
|
+
export const jsComment = (text, jsDoc = false) => {
|
|
129
|
+
const lines = text.split("\n");
|
|
130
|
+
if (lines.length === 1 && !jsDoc)
|
|
131
|
+
return `// ${lines[0]}`;
|
|
132
|
+
const prefix = jsDoc ? "/**" : "/*";
|
|
133
|
+
if (lines.length === 1)
|
|
134
|
+
return `${prefix} ${lines[0].replaceAll("*/", "* /")} */`;
|
|
135
|
+
return [prefix, ...lines.map((c) => ` * ${c.trimEnd().replaceAll("*/", "* /")}`), " */"].join("\n");
|
|
136
|
+
};
|
|
137
|
+
/** Make sure the string starts with a capital, and all other letters are lowercase */
|
|
138
|
+
export const uncapitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
|
139
|
+
/** Helper to use with `Array.sort()` and similar */
|
|
140
|
+
export const alphaSort = (a, b) => a.localeCompare(b);
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@keeex/utils","version":"7.
|
|
1
|
+
{"name":"@keeex/utils","version":"7.6.0","description":"Various utility functions for pure JavaScript","scripts":{},"author":"KeeeX SAS","contributors":[{"name":"Gabriel Paul \"Cley Faye\" Risterucci","email":"gabriel@keeex.net"}],"homepage":"https://keeex.me/oss","keywords":["utility"],"type":"module","license":"MIT","dependencies":{"@keeex/bubble_babble":"^3.0.1","@keeex/log":"^1.7.2","base64-arraybuffer":"^1.0.2","cron-parser":"^5.4.0","ms":"^2.1.3","text-encoding-shim":"^1.0.5"},"exports":{"./array.js":{"node":"./lib/array.js","browser":"./web/array.js","react-native":"./web/array.js","default":"./lib/array.js"},"./arraybuffer.js":{"node":"./lib/arraybuffer.js","browser":"./web/arraybuffer.js","react-native":"./web/arraybuffer.js","default":"./lib/arraybuffer.js"},"./async/asynctrigger.js":{"node":"./lib/async/asynctrigger.js","browser":"./web/async/asynctrigger.js","react-native":"./web/async/asynctrigger.js","default":"./lib/async/asynctrigger.js"},"./async/deferredpromise.js":{"node":"./lib/async/deferredpromise.js","browser":"./web/async/deferredpromise.js","react-native":"./web/async/deferredpromise.js","default":"./lib/async/deferredpromise.js"},"./async/eventqueue.js":{"node":"./lib/async/eventqueue.js","browser":"./web/async/eventqueue.js","react-native":"./web/async/eventqueue.js","default":"./lib/async/eventqueue.js"},"./async/keycache.js":{"node":"./lib/async/keycache.js","browser":"./web/async/keycache.js","react-native":"./web/async/keycache.js","default":"./lib/async/keycache.js"},"./async/queues.js":{"node":"./lib/async/queues.js","browser":"./web/async/queues.js","react-native":"./web/async/queues.js","default":"./lib/async/queues.js"},"./async/timecache.js":{"node":"./lib/async/timecache.js","browser":"./web/async/timecache.js","react-native":"./web/async/timecache.js","default":"./lib/async/timecache.js"},"./base58.js":{"node":"./lib/base58.js","browser":"./web/base58.js","react-native":"./web/base58.js","default":"./lib/base58.js"},"./base64.js":{"node":"./lib/base64.js","browser":"./web/base64.js","react-native":"./web/base64.js","default":"./lib/base64.js"},"./benchmark.js":{"node":"./lib/benchmark.js","browser":"./web/benchmark.js","react-native":"./web/benchmark.js","default":"./lib/benchmark.js"},"./bytebuffer.js":{"node":"./lib/bytebuffer.js","browser":"./web/bytebuffer.js","react-native":"./web/bytebuffer.js","default":"./lib/bytebuffer.js"},"./consts.js":{"node":"./lib/consts.js","browser":"./web/consts.js","react-native":"./web/consts.js","default":"./lib/consts.js"},"./cron.js":{"node":"./lib/cron.js","browser":"./web/cron.js","react-native":"./web/cron.js","default":"./lib/cron.js"},"./dataview.js":{"node":"./lib/dataview.js","browser":"./web/dataview.js","react-native":"./web/dataview.js","default":"./lib/dataview.js"},"./dict.js":{"node":"./lib/dict.js","browser":"./web/dict.js","react-native":"./web/dict.js","default":"./lib/dict.js"},"./error.js":{"node":"./lib/error.js","browser":"./web/error.js","react-native":"./web/error.js","default":"./lib/error.js"},"./global.js":{"node":"./lib/global.js","browser":"./web/global.js","react-native":"./web/global.js","default":"./lib/global.js"},"./hex.js":{"node":"./lib/hex.js","browser":"./web/hex.js","react-native":"./web/hex.js","default":"./lib/hex.js"},"./idx.js":{"node":"./lib/idx.js","browser":"./web/idx.js","react-native":"./web/idx.js","default":"./lib/idx.js"},"./json.js":{"node":"./lib/json.js","browser":"./web/json.js","react-native":"./web/json.js","default":"./lib/json.js"},"./linebuffer.js":{"node":"./lib/linebuffer.js","browser":"./web/linebuffer.js","react-native":"./web/linebuffer.js","default":"./lib/linebuffer.js"},"./marshalling/marshaller.js":{"node":"./lib/marshalling/marshaller.js","browser":"./web/marshalling/marshaller.js","react-native":"./web/marshalling/marshaller.js","default":"./lib/marshalling/marshaller.js"},"./marshalling/unmarshaller.js":{"node":"./lib/marshalling/unmarshaller.js","browser":"./web/marshalling/unmarshaller.js","react-native":"./web/marshalling/unmarshaller.js","default":"./lib/marshalling/unmarshaller.js"},"./number.js":{"node":"./lib/number.js","browser":"./web/number.js","react-native":"./web/number.js","default":"./lib/number.js"},"./path.js":{"node":"./lib/path.js","browser":"./web/path.js","react-native":"./web/path.js","default":"./lib/path.js"},"./promise.js":{"node":"./lib/promise.js","browser":"./web/promise.js","react-native":"./web/promise.js","default":"./lib/promise.js"},"./starttime.js":{"node":"./lib/starttime.js","browser":"./web/starttime.js","react-native":"./web/starttime.js","default":"./lib/starttime.js"},"./string.js":{"node":"./lib/string.js","browser":"./web/string.js","react-native":"./web/string.js","default":"./lib/string.js"},"./triggers.js":{"node":"./lib/triggers.js","browser":"./web/triggers.js","react-native":"./web/triggers.js","default":"./lib/triggers.js"},"./types/array.js":{"node":"./lib/types/array.js","browser":"./web/types/array.js","react-native":"./web/types/array.js","default":"./lib/types/array.js"},"./types/enum.js":{"node":"./lib/types/enum.js","browser":"./web/types/enum.js","react-native":"./web/types/enum.js","default":"./lib/types/enum.js"},"./types/predicateerror.js":{"node":"./lib/types/predicateerror.js","browser":"./web/types/predicateerror.js","react-native":"./web/types/predicateerror.js","default":"./lib/types/predicateerror.js"},"./types/primitive.js":{"node":"./lib/types/primitive.js","browser":"./web/types/primitive.js","react-native":"./web/types/primitive.js","default":"./lib/types/primitive.js"},"./types/record.js":{"node":"./lib/types/record.js","browser":"./web/types/record.js","react-native":"./web/types/record.js","default":"./lib/types/record.js"},"./types/types.js":{"node":"./lib/types/types.js","browser":"./web/types/types.js","react-native":"./web/types/types.js","default":"./lib/types/types.js"},"./types/utils.js":{"node":"./lib/types/utils.js","browser":"./web/types/utils.js","react-native":"./web/types/utils.js","default":"./lib/types/utils.js"},"./uint8array.js":{"node":"./lib/uint8array.js","browser":"./web/uint8array.js","react-native":"./web/uint8array.js","default":"./lib/uint8array.js"},"./units.js":{"node":"./lib/units.js","browser":"./web/units.js","react-native":"./web/units.js","default":"./lib/units.js"}},"files":["/lib","/web"]}
|
package/web/array.d.ts
CHANGED
|
@@ -26,3 +26,5 @@ export declare const arrayEqual: <T1 = unknown, T2 = unknown>(op1: ArrayLike<T1>
|
|
|
26
26
|
* @public
|
|
27
27
|
*/
|
|
28
28
|
export declare const asArray: <T>(data: Array<T> | (T extends Array<unknown> ? never : T)) => Array<T>;
|
|
29
|
+
/** Return a new array with its string sorted */
|
|
30
|
+
export declare const toSortedStringArray: (array: Array<string>) => Array<string>;
|
package/web/array.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
14
14
|
*
|
|
15
15
|
*/
|
|
16
|
+
import { alphaSort } from "./string.js";
|
|
16
17
|
const useEqOp = (op1, op2) => op1 === op2;
|
|
17
18
|
/**
|
|
18
19
|
* Compare two array-like structure for equal content
|
|
@@ -31,4 +32,6 @@ export const arrayEqual = (op1, op2, eqFunc = useEqOp) => {
|
|
|
31
32
|
*
|
|
32
33
|
* @public
|
|
33
34
|
*/
|
|
34
|
-
export const asArray = data => Array.isArray(data) ? data : [data];
|
|
35
|
+
export const asArray = data => Array.isArray(data) ? data : [data];
|
|
36
|
+
/** Return a new array with its string sorted */
|
|
37
|
+
export const toSortedStringArray = array => array.toSorted(alphaSort);
|
package/web/async/eventqueue.js
CHANGED
|
@@ -100,7 +100,7 @@ export class EventQueue {
|
|
|
100
100
|
// eslint-disable-next-line no-await-in-loop
|
|
101
101
|
const nextEvent = await this.#getNextEvent();
|
|
102
102
|
// eslint-disable-next-line no-await-in-loop
|
|
103
|
-
await Promise.all(this.#handlers.map(handler => handler(nextEvent)));
|
|
103
|
+
await Promise.all(this.#handlers.map(async handler => handler(nextEvent)));
|
|
104
104
|
if (this.#events.length > 0) {
|
|
105
105
|
this.#log.debug("More events in queue");
|
|
106
106
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* @preserve
|
|
4
|
+
*
|
|
5
|
+
* MIT License
|
|
6
|
+
*
|
|
7
|
+
* Copyright (c) 2023 KeeeX SAS
|
|
8
|
+
*
|
|
9
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
10
|
+
*
|
|
11
|
+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
12
|
+
*
|
|
13
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
14
|
+
*
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Return a `DataView` on the boundaries of the provided `data`.
|
|
18
|
+
*
|
|
19
|
+
* @param byteOffset - Offset after the start of `data` in its buffer
|
|
20
|
+
* @param byteLength - Length of the available data within `data`
|
|
21
|
+
*/
|
|
22
|
+
export declare const getDataView: (data: Uint8Array, byteOffset?: number, byteLength?: number) => DataView;
|
package/web/dataview.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* @preserve
|
|
4
|
+
*
|
|
5
|
+
* MIT License
|
|
6
|
+
*
|
|
7
|
+
* Copyright (c) 2023 KeeeX SAS
|
|
8
|
+
*
|
|
9
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
10
|
+
*
|
|
11
|
+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
12
|
+
*
|
|
13
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
14
|
+
*
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Return a `DataView` on the boundaries of the provided `data`.
|
|
18
|
+
*
|
|
19
|
+
* @param byteOffset - Offset after the start of `data` in its buffer
|
|
20
|
+
* @param byteLength - Length of the available data within `data`
|
|
21
|
+
*/
|
|
22
|
+
export const getDataView = (data, byteOffset = 0, byteLength = data.byteLength - byteOffset) => {
|
|
23
|
+
if (byteOffset < 0 || byteOffset >= data.byteLength || byteOffset + byteLength > data.byteLength) {
|
|
24
|
+
throw new Error("Out of bound");
|
|
25
|
+
}
|
|
26
|
+
return new DataView(data.buffer, data.byteOffset + byteOffset, byteLength);
|
|
27
|
+
};
|
package/web/dict.d.ts
CHANGED
|
@@ -54,3 +54,35 @@ export type PrimitiveTypeObject = Record<string, unknown> | Array<unknown> | str
|
|
|
54
54
|
* @public
|
|
55
55
|
*/
|
|
56
56
|
export declare const deepCopyPrimitive: (source: unknown) => unknown;
|
|
57
|
+
/**
|
|
58
|
+
* Perform the merge operation between two values.
|
|
59
|
+
*
|
|
60
|
+
* If the new value have no changes from the initial value, this should always return `initial`.
|
|
61
|
+
* If there is any change, this should always return a new reference.
|
|
62
|
+
*
|
|
63
|
+
* TODO: The function returns unknown because I can't work around the requirements for TypeScript to
|
|
64
|
+
* be happy using `DataType | null`.
|
|
65
|
+
*/
|
|
66
|
+
type RefMergeDefFn<DataType> = (initial: DataType | null, newValue: DataType | null) => unknown;
|
|
67
|
+
type RefMergeDef<DataType> = DataType extends string ? "string" : DataType extends number ? "number" : DataType extends boolean ? "boolean" : DataType extends Array<string> ? "string[]" : RefMergeDefFn<DataType>;
|
|
68
|
+
type RefMergeProfile<ObjectType> = Required<{
|
|
69
|
+
[key in keyof ObjectType]: RefMergeDef<NonNullable<ObjectType[key]>>;
|
|
70
|
+
}>;
|
|
71
|
+
/**
|
|
72
|
+
* Examine each props on both `initial` and `newValue`, and if there is any change, return a new
|
|
73
|
+
* reference with all values.
|
|
74
|
+
*
|
|
75
|
+
* @param initial - The source object. If no changes are detected, returns it.
|
|
76
|
+
* @param newValue - Values to update in `initial`. `undefined` values are skipped.
|
|
77
|
+
* @param profile - Describe the expected properties and how to merge them.
|
|
78
|
+
*
|
|
79
|
+
* @returns
|
|
80
|
+
* If there is some change from `initial`, will return a new reference with all new values.
|
|
81
|
+
* This property extends to values that are object; all objects in this hierarchy that have an
|
|
82
|
+
* update belows it is replaced with a new reference.
|
|
83
|
+
*
|
|
84
|
+
* This is made to help with libraries like React that depends on state reference actually getting
|
|
85
|
+
* changed on update.
|
|
86
|
+
*/
|
|
87
|
+
export declare const refMergeObjects: <ObjectType>(initial: ObjectType, newValue: Partial<ObjectType>, profile: RefMergeProfile<ObjectType>) => ObjectType;
|
|
88
|
+
export {};
|
package/web/dict.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
14
14
|
*
|
|
15
15
|
*/
|
|
16
|
+
import { arrayEqual } from "./array.js";
|
|
16
17
|
/**
|
|
17
18
|
* Copy all properties from secondary into primary.
|
|
18
19
|
*
|
|
@@ -64,4 +65,69 @@ export const deepCopyPrimitive = source => {
|
|
|
64
65
|
}
|
|
65
66
|
return res;
|
|
66
67
|
}
|
|
68
|
+
};
|
|
69
|
+
const refMergeStringArray = (initial, newValue) => {
|
|
70
|
+
if (newValue === null) return null;
|
|
71
|
+
if (initial !== null && arrayEqual(initial, newValue)) return initial;
|
|
72
|
+
return [...newValue];
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Merge a new value and an old value according to the provided merger.
|
|
76
|
+
*
|
|
77
|
+
* If `newValue` is undefined, `initial` is always returned.
|
|
78
|
+
* If `newValue` is defined AND have difference from `initial`, a new object should be returned.
|
|
79
|
+
* If there is no change, `initial` is returned.
|
|
80
|
+
*/
|
|
81
|
+
const getMergedRef = (initial, newValue, merger) => {
|
|
82
|
+
if (newValue === undefined) return initial;
|
|
83
|
+
switch (merger) {
|
|
84
|
+
case "string":
|
|
85
|
+
case "number":
|
|
86
|
+
case "boolean":
|
|
87
|
+
return newValue;
|
|
88
|
+
case "string[]":
|
|
89
|
+
return refMergeStringArray(initial, newValue);
|
|
90
|
+
default:
|
|
91
|
+
{
|
|
92
|
+
return merger(initial, newValue);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Examine each props on both `initial` and `newValue`, and if there is any change, return a new
|
|
98
|
+
* reference with all values.
|
|
99
|
+
*
|
|
100
|
+
* @param initial - The source object. If no changes are detected, returns it.
|
|
101
|
+
* @param newValue - Values to update in `initial`. `undefined` values are skipped.
|
|
102
|
+
* @param profile - Describe the expected properties and how to merge them.
|
|
103
|
+
*
|
|
104
|
+
* @returns
|
|
105
|
+
* If there is some change from `initial`, will return a new reference with all new values.
|
|
106
|
+
* This property extends to values that are object; all objects in this hierarchy that have an
|
|
107
|
+
* update belows it is replaced with a new reference.
|
|
108
|
+
*
|
|
109
|
+
* This is made to help with libraries like React that depends on state reference actually getting
|
|
110
|
+
* changed on update.
|
|
111
|
+
*/
|
|
112
|
+
export const refMergeObjects = (initial, newValue, profile) => {
|
|
113
|
+
let anyChange = false;
|
|
114
|
+
const newValues = Object.entries(profile).map(([key, merger]) => {
|
|
115
|
+
const initialValue = initial[key];
|
|
116
|
+
const merged = getMergedRef(initialValue, newValue[key], merger);
|
|
117
|
+
anyChange ||= merged !== initialValue;
|
|
118
|
+
return {
|
|
119
|
+
key,
|
|
120
|
+
merged
|
|
121
|
+
};
|
|
122
|
+
});
|
|
123
|
+
// Can't statically detect changes in map() callback
|
|
124
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
125
|
+
if (!anyChange) return initial;
|
|
126
|
+
return newValues.reduce((acc, {
|
|
127
|
+
key,
|
|
128
|
+
merged
|
|
129
|
+
}) => {
|
|
130
|
+
acc[key] = merged;
|
|
131
|
+
return acc;
|
|
132
|
+
}, {});
|
|
67
133
|
};
|
package/web/promise.js
CHANGED
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
14
14
|
*
|
|
15
15
|
*/
|
|
16
|
-
/* eslint-env node */
|
|
17
16
|
import * as log from "@keeex/log";
|
|
18
17
|
import { asError } from "./error.js";
|
|
19
18
|
import { timeConvert } from "./units.js";
|
package/web/string.d.ts
CHANGED
|
@@ -63,3 +63,11 @@ export declare const encodeUTF8: (str: string) => string;
|
|
|
63
63
|
export declare const decodeUTF8: (str: string, skipErrors?: boolean) => string;
|
|
64
64
|
/** Capitalize (or uncapitalize) the first character of a string */
|
|
65
65
|
export declare const capitalize: (str: string, upperFirstLetter: boolean) => string;
|
|
66
|
+
/** Ensure the string ends with one newline */
|
|
67
|
+
export declare const enforceNewlineEnd: (str: string) => string;
|
|
68
|
+
/** Format the text as a single line or multiline JS comment */
|
|
69
|
+
export declare const jsComment: (text: string, jsDoc?: boolean) => string;
|
|
70
|
+
/** Make sure the string starts with a capital, and all other letters are lowercase */
|
|
71
|
+
export declare const uncapitalize: (str: string) => string;
|
|
72
|
+
/** Helper to use with `Array.sort()` and similar */
|
|
73
|
+
export declare const alphaSort: (a: string, b: string) => number;
|
package/web/string.js
CHANGED
|
@@ -98,4 +98,32 @@ export const capitalize = (str, upperFirstLetter) => {
|
|
|
98
98
|
const transformedFirstLetter = upperFirstLetter ? firstLetter.toUpperCase() : firstLetter.toLowerCase();
|
|
99
99
|
const rest = str.slice(1);
|
|
100
100
|
return `${transformedFirstLetter}${rest}`;
|
|
101
|
-
};
|
|
101
|
+
};
|
|
102
|
+
/** Return the last index of a character that pass the needle test, or -1 if it never happens */
|
|
103
|
+
const findLastIndexOf = (hay, needle) => {
|
|
104
|
+
for (let index = hay.length - 1; --index; index >= 0) {
|
|
105
|
+
if (needle(hay.at(index))) return index;
|
|
106
|
+
}
|
|
107
|
+
return -1;
|
|
108
|
+
};
|
|
109
|
+
/** Ensure the string ends with one newline */
|
|
110
|
+
export const enforceNewlineEnd = str => {
|
|
111
|
+
if (str.endsWith("\n")) {
|
|
112
|
+
const firstNonNewline = findLastIndexOf(str, c => c !== "\n");
|
|
113
|
+
if (firstNonNewline === -1) return "\n";
|
|
114
|
+
return `${str.slice(0, firstNonNewline + 1)}\n`;
|
|
115
|
+
}
|
|
116
|
+
return `${str}\n`;
|
|
117
|
+
};
|
|
118
|
+
/** Format the text as a single line or multiline JS comment */
|
|
119
|
+
export const jsComment = (text, jsDoc = false) => {
|
|
120
|
+
const lines = text.split("\n");
|
|
121
|
+
if (lines.length === 1 && !jsDoc) return `// ${lines[0]}`;
|
|
122
|
+
const prefix = jsDoc ? "/**" : "/*";
|
|
123
|
+
if (lines.length === 1) return `${prefix} ${lines[0].replaceAll("*/", "* /")} */`;
|
|
124
|
+
return [prefix, ...lines.map(c => ` * ${c.trimEnd().replaceAll("*/", "* /")}`), " */"].join("\n");
|
|
125
|
+
};
|
|
126
|
+
/** Make sure the string starts with a capital, and all other letters are lowercase */
|
|
127
|
+
export const uncapitalize = str => str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
|
128
|
+
/** Helper to use with `Array.sort()` and similar */
|
|
129
|
+
export const alphaSort = (a, b) => a.localeCompare(b);
|