@synnaxlabs/x 0.7.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/.eslintrc.cjs +18 -0
- package/.turbo/turbo-build.log +16 -0
- package/.vscode/settings.json +3 -0
- package/LICENSE +4 -0
- package/README.md +44 -0
- package/dist/binary/encoder.d.ts +59 -0
- package/dist/binary/encoder.spec.d.ts +1 -0
- package/dist/binary/index.d.ts +1 -0
- package/dist/case.d.ts +5 -0
- package/dist/change/change.d.ts +32 -0
- package/dist/change/index.d.ts +1 -0
- package/dist/clamp.d.ts +1 -0
- package/dist/compare/compare.d.ts +39 -0
- package/dist/compare/index.d.ts +1 -0
- package/dist/debounce.d.ts +2 -0
- package/dist/deep/copy.d.ts +1 -0
- package/dist/deep/delete.d.ts +2 -0
- package/dist/deep/delete.spec.d.ts +1 -0
- package/dist/deep/equal.d.ts +8 -0
- package/dist/deep/equal.spec.d.ts +1 -0
- package/dist/deep/external.d.ts +7 -0
- package/dist/deep/index.d.ts +1 -0
- package/dist/deep/key.d.ts +30 -0
- package/dist/deep/memo.d.ts +1 -0
- package/dist/deep/merge.d.ts +2 -0
- package/dist/deep/merge.spec.d.ts +1 -0
- package/dist/deep/partial.d.ts +3 -0
- package/dist/destructor.d.ts +1 -0
- package/dist/identity.d.ts +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/join.d.ts +1 -0
- package/dist/kv/index.d.ts +1 -0
- package/dist/kv/types.d.ts +32 -0
- package/dist/mock/MockGLBufferController.d.ts +20 -0
- package/dist/mock/index.d.ts +1 -0
- package/dist/observe/index.d.ts +1 -0
- package/dist/observe/observe.d.ts +11 -0
- package/dist/optional.d.ts +1 -0
- package/dist/primitive.d.ts +8 -0
- package/dist/record.d.ts +14 -0
- package/dist/renderable.d.ts +4 -0
- package/dist/runtime/detect.d.ts +9 -0
- package/dist/runtime/external.d.ts +2 -0
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/os.d.ts +9 -0
- package/dist/search.d.ts +15 -0
- package/dist/spatial/base.d.ts +102 -0
- package/dist/spatial/bounds.d.ts +31 -0
- package/dist/spatial/bounds.spec.d.ts +1 -0
- package/dist/spatial/box.d.ts +265 -0
- package/dist/spatial/box.spec.d.ts +1 -0
- package/dist/spatial/dimensions.d.ts +59 -0
- package/dist/spatial/dimensions.spec.d.ts +1 -0
- package/dist/spatial/direction.d.ts +10 -0
- package/dist/spatial/direction.spec.d.ts +1 -0
- package/dist/spatial/external.d.ts +8 -0
- package/dist/spatial/index.d.ts +1 -0
- package/dist/spatial/location.d.ts +52 -0
- package/dist/spatial/location.spec.d.ts +1 -0
- package/dist/spatial/position.d.ts +2 -0
- package/dist/spatial/scale.d.ts +241 -0
- package/dist/spatial/scale.spec.d.ts +1 -0
- package/dist/spatial/spatial.d.ts +1 -0
- package/dist/spatial/xy.d.ts +116 -0
- package/dist/spatial/xy.spec.d.ts +1 -0
- package/dist/telem/encode.d.ts +1 -0
- package/dist/telem/generate.d.ts +2 -0
- package/dist/telem/gl.d.ts +11 -0
- package/dist/telem/index.d.ts +3 -0
- package/dist/telem/series.d.ts +104 -0
- package/dist/telem/series.spec.d.ts +1 -0
- package/dist/telem/telem.d.ts +633 -0
- package/dist/telem/telem.spec.d.ts +1 -0
- package/dist/toArray.d.ts +1 -0
- package/dist/transform.d.ts +1 -0
- package/dist/unique.d.ts +1 -0
- package/dist/url/index.d.ts +1 -0
- package/dist/url/url.d.ts +46 -0
- package/dist/url/url.spec.d.ts +1 -0
- package/dist/worker/worker.d.ts +32 -0
- package/dist/worker/worker.spec.d.ts +1 -0
- package/dist/x.cjs.js +9046 -0
- package/dist/x.cjs.js.map +1 -0
- package/dist/x.es.js +9047 -0
- package/dist/x.es.js.map +1 -0
- package/package.json +42 -0
- package/src/binary/encoder.spec.ts +31 -0
- package/src/binary/encoder.ts +118 -0
- package/src/binary/index.ts +10 -0
- package/src/case.ts +31 -0
- package/src/change/change.ts +31 -0
- package/src/change/index.ts +10 -0
- package/src/clamp.ts +14 -0
- package/src/compare/compare.ts +116 -0
- package/src/compare/index.ts +10 -0
- package/src/debounce.ts +45 -0
- package/src/deep/copy.ts +13 -0
- package/src/deep/delete.spec.ts +36 -0
- package/src/deep/delete.ts +27 -0
- package/src/deep/equal.spec.ts +82 -0
- package/src/deep/equal.ts +65 -0
- package/src/deep/external.ts +15 -0
- package/src/deep/index.ts +10 -0
- package/src/deep/key.ts +46 -0
- package/src/deep/merge.spec.ts +63 -0
- package/src/deep/merge.ts +41 -0
- package/src/deep/partial.ts +14 -0
- package/src/destructor.ts +10 -0
- package/src/identity.ts +14 -0
- package/src/index.ts +34 -0
- package/src/join.ts +14 -0
- package/src/kv/index.ts +10 -0
- package/src/kv/types.ts +52 -0
- package/src/mock/MockGLBufferController.ts +70 -0
- package/src/mock/index.ts +10 -0
- package/src/observe/index.ts +10 -0
- package/src/observe/observe.ts +33 -0
- package/src/optional.ts +10 -0
- package/src/primitive.ts +46 -0
- package/src/record.ts +45 -0
- package/src/renderable.ts +20 -0
- package/src/runtime/detect.ts +34 -0
- package/src/runtime/external.ts +11 -0
- package/src/runtime/index.ts +10 -0
- package/src/runtime/os.ts +38 -0
- package/src/search.ts +40 -0
- package/src/spatial/base.ts +80 -0
- package/src/spatial/bounds.spec.ts +99 -0
- package/src/spatial/bounds.ts +80 -0
- package/src/spatial/box.spec.ts +137 -0
- package/src/spatial/box.ts +326 -0
- package/src/spatial/dimensions.spec.ts +47 -0
- package/src/spatial/dimensions.ts +64 -0
- package/src/spatial/direction.spec.ts +25 -0
- package/src/spatial/direction.ts +47 -0
- package/src/spatial/external.ts +17 -0
- package/src/spatial/index.ts +10 -0
- package/src/spatial/location.spec.ts +24 -0
- package/src/spatial/location.ts +124 -0
- package/src/spatial/position.ts +26 -0
- package/src/spatial/scale.spec.ts +74 -0
- package/src/spatial/scale.ts +351 -0
- package/src/spatial/spatial.ts +17 -0
- package/src/spatial/xy.spec.ts +68 -0
- package/src/spatial/xy.ts +164 -0
- package/src/telem/encode.ts +22 -0
- package/src/telem/generate.ts +19 -0
- package/src/telem/gl.ts +22 -0
- package/src/telem/index.ts +12 -0
- package/src/telem/series.spec.ts +289 -0
- package/src/telem/series.ts +449 -0
- package/src/telem/telem.spec.ts +302 -0
- package/src/telem/telem.ts +1237 -0
- package/src/toArray.ts +11 -0
- package/src/transform.ts +10 -0
- package/src/unique.ts +10 -0
- package/src/url/index.ts +10 -0
- package/src/url/url.spec.ts +47 -0
- package/src/url/url.ts +113 -0
- package/src/worker/worker.spec.ts +41 -0
- package/src/worker/worker.ts +86 -0
- package/tsconfig.json +7 -0
- package/tsconfig.vite.json +4 -0
- package/vite.config.ts +23 -0
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@synnaxlabs/x",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "0.7.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Common Utilities for Synnax Labs",
|
|
7
|
+
"repository": "https://github.com/synnaxlabs/synnax/tree/main/x/go",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"synnax",
|
|
10
|
+
"utilities",
|
|
11
|
+
"data acquisition",
|
|
12
|
+
"big data",
|
|
13
|
+
"telemetry",
|
|
14
|
+
"control syustems"
|
|
15
|
+
],
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"async-mutex": "^0.4.0",
|
|
18
|
+
"js-convert-case": "^4.2.0",
|
|
19
|
+
"msgpackr": "^1.9.9",
|
|
20
|
+
"nanoid": "^5.0.2",
|
|
21
|
+
"zod": "3.22.4"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^20.8.7",
|
|
25
|
+
"@vitest/coverage-v8": "^0.34.6",
|
|
26
|
+
"vite": "^4.5.0",
|
|
27
|
+
"vitest": "^0.34.6",
|
|
28
|
+
"@synnaxlabs/tsconfig": "0.0.1",
|
|
29
|
+
"@synnaxlabs/vite-plugin": "0.0.1",
|
|
30
|
+
"eslint-config-synnaxlabs": "0.0.0"
|
|
31
|
+
},
|
|
32
|
+
"main": "dist/x.cjs.js",
|
|
33
|
+
"module": "dist/x.es.js",
|
|
34
|
+
"types": "dist/index.d.ts",
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "vite build",
|
|
37
|
+
"test": "vitest",
|
|
38
|
+
"cov": "vitest --coverage",
|
|
39
|
+
"lint": "eslint \"src/**/*.ts*\"",
|
|
40
|
+
"fix": "eslint \"src/**/*.ts*\" --fix"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Copyright 2023 Synnax Labs, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
4
|
+
// licenses/BSL.txt.
|
|
5
|
+
//
|
|
6
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
7
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
|
+
// included in the file licenses/APL.txt.
|
|
9
|
+
|
|
10
|
+
import { describe, expect, test } from "vitest";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
|
|
13
|
+
import { binary } from "@/binary";
|
|
14
|
+
|
|
15
|
+
const SampleSchema = z.object({
|
|
16
|
+
channelKey: z.string(),
|
|
17
|
+
timeStamp: z.number(),
|
|
18
|
+
value: z.unknown(),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe.each(binary.ENCODERS)("encoder", (e) => {
|
|
22
|
+
test(`[encoder] - encoder ${e.contentType} should encode correctly`, () => {
|
|
23
|
+
const sample = {
|
|
24
|
+
channelKey: "test",
|
|
25
|
+
timeStamp: 123,
|
|
26
|
+
value: new Array([1, 2, 3]),
|
|
27
|
+
};
|
|
28
|
+
const encoded = e.encode(sample);
|
|
29
|
+
expect(e.decode(encoded, SampleSchema)).toEqual(sample);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// Copyright 2023 Synnax Labs, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
4
|
+
// licenses/BSL.txt.
|
|
5
|
+
//
|
|
6
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
7
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
|
+
// included in the file licenses/APL.txt.
|
|
9
|
+
|
|
10
|
+
import { addExtension, pack, unpack } from "msgpackr";
|
|
11
|
+
import { type ZodSchema, type z } from "zod";
|
|
12
|
+
|
|
13
|
+
import { Case } from "@/case";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* CustomTypeEncoder is an interface for a class that needs to transform its
|
|
17
|
+
* value before encoding.
|
|
18
|
+
*/
|
|
19
|
+
interface CustomTypeEncoder {
|
|
20
|
+
/** The Class the custom encoder is set for */
|
|
21
|
+
Class: Function;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The function that transforms the value before encoding;
|
|
25
|
+
*
|
|
26
|
+
* @param instance - The instance of the class to transform.
|
|
27
|
+
* @returns The transformed value.
|
|
28
|
+
*/
|
|
29
|
+
write: <P>(instance: P) => unknown;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* EncoderDecoder is an entity that encodes and decodes messages to and from a
|
|
34
|
+
* binary format.
|
|
35
|
+
*/
|
|
36
|
+
export interface EncoderDecoder {
|
|
37
|
+
/** The HTTP content type of the encoder */
|
|
38
|
+
contentType: string;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Encodes the given payload into a binary representation.
|
|
42
|
+
*
|
|
43
|
+
* @param payload - The payload to encode.
|
|
44
|
+
* @returns An ArrayBuffer containing the encoded payload.
|
|
45
|
+
*/
|
|
46
|
+
encode: (payload: unknown) => ArrayBuffer;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Decodes the given binary representation into a type checked payload.
|
|
50
|
+
*
|
|
51
|
+
* @param data - The data to decode.
|
|
52
|
+
* @param schema - The schema to decode the data with.
|
|
53
|
+
*/
|
|
54
|
+
decode: <P>(data: Uint8Array | ArrayBuffer, schema?: ZodSchema<P>) => P;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface StaticEncoderDecoder {
|
|
58
|
+
registerCustomType: (encoder: CustomTypeEncoder) => void;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** MsgpackEncoderDecoder is a msgpack implementation of EncoderDecoder. */
|
|
62
|
+
export class MsgpackEncoderDecoder implements EncoderDecoder {
|
|
63
|
+
contentType = "application/msgpack";
|
|
64
|
+
|
|
65
|
+
encode(payload: unknown): ArrayBuffer {
|
|
66
|
+
return pack(Case.toSnake(payload));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
decode<P extends z.ZodTypeAny>(
|
|
70
|
+
data: Uint8Array | ArrayBuffer,
|
|
71
|
+
schema?: P,
|
|
72
|
+
): z.output<P> {
|
|
73
|
+
const unpacked = Case.toCamel(unpack(new Uint8Array(data)));
|
|
74
|
+
return schema != null ? schema.parse(unpacked) : (unpacked as P);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static registerCustomType(encoder: CustomTypeEncoder): void {
|
|
78
|
+
addExtension({ type: 0, ...encoder });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** JSONEncoderDecoder is a JSON implementation of EncoderDecoder. */
|
|
83
|
+
export class JSONEncoderDecoder implements EncoderDecoder {
|
|
84
|
+
contentType = "application/json";
|
|
85
|
+
|
|
86
|
+
encode(payload: unknown): ArrayBuffer {
|
|
87
|
+
const json = JSON.stringify(Case.toSnake(payload), (_, v) => {
|
|
88
|
+
if (ArrayBuffer.isView(v)) return Array.from(v as Uint8Array);
|
|
89
|
+
return v;
|
|
90
|
+
});
|
|
91
|
+
return new TextEncoder().encode(json);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
decode<P extends z.ZodTypeAny>(
|
|
95
|
+
data: Uint8Array | ArrayBuffer,
|
|
96
|
+
schema?: P,
|
|
97
|
+
): z.output<P> {
|
|
98
|
+
const unpacked = Case.toCamel(JSON.parse(new TextDecoder().decode(data)));
|
|
99
|
+
return schema != null ? schema.parse(unpacked) : (unpacked as P);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
static registerCustomType(): void {}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export const ENCODERS: EncoderDecoder[] = [
|
|
106
|
+
new MsgpackEncoderDecoder(),
|
|
107
|
+
new JSONEncoderDecoder(),
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
export const ENCODER_CLASSES: StaticEncoderDecoder[] = [
|
|
111
|
+
MsgpackEncoderDecoder,
|
|
112
|
+
JSONEncoderDecoder,
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
export const registerCustomTypeEncoder = (encoder: CustomTypeEncoder): void =>
|
|
116
|
+
ENCODER_CLASSES.forEach((encoderClass) => {
|
|
117
|
+
encoderClass.registerCustomType(encoder);
|
|
118
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Copyright 2023 Synnax Labs, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
4
|
+
// licenses/BSL.txt.
|
|
5
|
+
//
|
|
6
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
7
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
|
+
// included in the file licenses/APL.txt.
|
|
9
|
+
|
|
10
|
+
export * as binary from "@/binary/encoder";
|
package/src/case.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Copyright 2023 Synnax Labs, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
4
|
+
// licenses/BSL.txt.
|
|
5
|
+
//
|
|
6
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
7
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
|
+
// included in the file licenses/APL.txt.
|
|
9
|
+
|
|
10
|
+
import { camelKeys as _camelKeys, snakeKeys as _snakeKeys } from "js-convert-case";
|
|
11
|
+
|
|
12
|
+
import { type UnknownRecord } from "@/record";
|
|
13
|
+
|
|
14
|
+
const options = {
|
|
15
|
+
recursive: true,
|
|
16
|
+
recursiveInArray: true,
|
|
17
|
+
keepTypesOnRecursion: [Number, String, Uint8Array],
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const snakeKeys = <T>(entity: T): Record<string, T[keyof T]> =>
|
|
21
|
+
_snakeKeys(entity, options) as Record<string, T[keyof T]>;
|
|
22
|
+
|
|
23
|
+
const camelKeys = <T extends UnknownRecord<T>>(entity: T): Record<string, T[keyof T]> =>
|
|
24
|
+
_camelKeys(entity, options) as Record<string, T[keyof T]>;
|
|
25
|
+
|
|
26
|
+
export namespace Case {
|
|
27
|
+
export const toSnake = snakeKeys;
|
|
28
|
+
export const toCamel = camelKeys;
|
|
29
|
+
export const capitalize = (str: string): string =>
|
|
30
|
+
str[0].toUpperCase() + str.slice(1);
|
|
31
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Copyright 2023 Synnax Labs, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
4
|
+
// licenses/BSL.txt.
|
|
5
|
+
//
|
|
6
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
7
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
|
+
// included in the file licenses/APL.txt.
|
|
9
|
+
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
|
|
12
|
+
export type Variant = "set" | "delete";
|
|
13
|
+
|
|
14
|
+
export const Z = <V extends z.ZodTypeAny>(value: V) =>
|
|
15
|
+
z.object({
|
|
16
|
+
variant: z.enum(["set", "delete"]),
|
|
17
|
+
key: z.string(),
|
|
18
|
+
value,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export type Change<K, V> =
|
|
22
|
+
| {
|
|
23
|
+
variant: "set";
|
|
24
|
+
key: K;
|
|
25
|
+
value: V;
|
|
26
|
+
}
|
|
27
|
+
| {
|
|
28
|
+
variant: "delete";
|
|
29
|
+
key: K;
|
|
30
|
+
value?: V;
|
|
31
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Copyright 2023 Synnax Labs, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
4
|
+
// licenses/BSL.txt.
|
|
5
|
+
//
|
|
6
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
7
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
|
+
// included in the file licenses/APL.txt.
|
|
9
|
+
|
|
10
|
+
export * as change from "@/change/change";
|
package/src/clamp.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Copyright 2023 Synnax Labs, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
4
|
+
// licenses/BSL.txt.
|
|
5
|
+
//
|
|
6
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
7
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
|
+
// included in the file licenses/APL.txt.
|
|
9
|
+
|
|
10
|
+
export const clamp = (value: number, min?: number, max?: number): number => {
|
|
11
|
+
if (min != null) value = Math.max(value, min);
|
|
12
|
+
if (max != null) value = Math.min(value, max);
|
|
13
|
+
return value;
|
|
14
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Copyright 2023 Synnax Labs, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
4
|
+
// licenses/BSL.txt.
|
|
5
|
+
//
|
|
6
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
7
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
|
+
// included in the file licenses/APL.txt.
|
|
9
|
+
|
|
10
|
+
import { type Primitive, isStringer, type PrimitiveRecord } from "@/primitive";
|
|
11
|
+
import { type spatial } from "@/spatial";
|
|
12
|
+
|
|
13
|
+
export type CompareF<T> = (a: T, b: T) => number;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Creates the appropriate compare function for sorting the given
|
|
17
|
+
* primitive type.
|
|
18
|
+
*
|
|
19
|
+
* @param v The primitive value to create a compare function for.
|
|
20
|
+
* This is used to determine the type of comparison to perform.
|
|
21
|
+
* @param reverse Whether to reverse the sort order.
|
|
22
|
+
*/
|
|
23
|
+
export const newF = <T extends Primitive>(
|
|
24
|
+
v: T,
|
|
25
|
+
reverse: boolean = false,
|
|
26
|
+
): CompareF<T> => {
|
|
27
|
+
const t = isStringer(v) ? "stringer" : typeof v;
|
|
28
|
+
let f: CompareF<T>;
|
|
29
|
+
switch (t) {
|
|
30
|
+
case "string":
|
|
31
|
+
f = (a: T, b: T) => (a as string).localeCompare(b as string);
|
|
32
|
+
break;
|
|
33
|
+
case "stringer":
|
|
34
|
+
f = (a: T, b: T) =>
|
|
35
|
+
(a as string).toString().localeCompare((b as string).toString());
|
|
36
|
+
break;
|
|
37
|
+
case "number":
|
|
38
|
+
f = (a: T, b: T) => (a as number) - (b as number);
|
|
39
|
+
break;
|
|
40
|
+
case "bigint":
|
|
41
|
+
f = (a: T, b: T) => ((a as bigint) - (b as bigint) > BigInt(0) ? 1 : -1);
|
|
42
|
+
break;
|
|
43
|
+
case "boolean":
|
|
44
|
+
f = (a: T, b: T) => Number(a) - Number(b);
|
|
45
|
+
break;
|
|
46
|
+
default:
|
|
47
|
+
console.warn("sortFunc: unknown type");
|
|
48
|
+
return () => -1;
|
|
49
|
+
}
|
|
50
|
+
return reverse ? reverseF(f) : f;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Creates a compare function that compares the field of the given object.
|
|
55
|
+
*
|
|
56
|
+
* @param key The key of the field to compare.
|
|
57
|
+
* @param value The object to compare the field of. This is used to determine the type of
|
|
58
|
+
* comparison to perform.
|
|
59
|
+
* @param reverse Whether to reverse the sort order.
|
|
60
|
+
*/
|
|
61
|
+
export const newFieldF = <T extends PrimitiveRecord>(
|
|
62
|
+
key: keyof T,
|
|
63
|
+
value: T,
|
|
64
|
+
reverse?: boolean,
|
|
65
|
+
): CompareF<T> => {
|
|
66
|
+
const f = newF(value[key], reverse);
|
|
67
|
+
return (a: T, b: T) => f(a[key], b[key]);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Compares the two primitive arrays.
|
|
72
|
+
* @param a The first array to compare.
|
|
73
|
+
* @param b The second array to compare.
|
|
74
|
+
* @returns The array with the greater length if the array lengths are not equal. If the
|
|
75
|
+
* arrays are the same length, returns 0 if all elements are equal, otherwise returns -1.
|
|
76
|
+
*/
|
|
77
|
+
export const primitiveArrays = <T extends Primitive>(
|
|
78
|
+
a: readonly T[] | T[],
|
|
79
|
+
b: readonly T[] | T[],
|
|
80
|
+
): number => {
|
|
81
|
+
if (a.length !== b.length) return a.length - b.length;
|
|
82
|
+
return a.every((v, i) => v === b[i]) ? 0 : -1;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const unorderedPrimitiveArrays = <T extends Primitive>(
|
|
86
|
+
a: readonly T[] | T[],
|
|
87
|
+
b: readonly T[] | T[],
|
|
88
|
+
): number => {
|
|
89
|
+
if (a.length !== b.length) return a.length - b.length;
|
|
90
|
+
if (a.length === 0) return 0;
|
|
91
|
+
const compareF = newF(a[0]);
|
|
92
|
+
const aSorted = [...a].sort(compareF);
|
|
93
|
+
const bSorted = [...b].sort(compareF);
|
|
94
|
+
return aSorted.every((v, i) => v === bSorted[i]) ? 0 : -1;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export const order = (a: spatial.Order, b: spatial.Order): number => {
|
|
98
|
+
if (a === b) return 0;
|
|
99
|
+
if (a === "first" && b === "last") return 1;
|
|
100
|
+
return -1;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/** @returns the reverse of the given compare function. */
|
|
104
|
+
export const reverseF =
|
|
105
|
+
<T>(f: CompareF<T>): CompareF<T> =>
|
|
106
|
+
(a: T, b: T) =>
|
|
107
|
+
f(b, a);
|
|
108
|
+
|
|
109
|
+
/** The equal return value of a compare function. */
|
|
110
|
+
export const EQUAL = 0;
|
|
111
|
+
|
|
112
|
+
/** The less than return value of a compare function. */
|
|
113
|
+
export const LESS_THAN = -1;
|
|
114
|
+
|
|
115
|
+
/** The greater than return value of a compare function. */
|
|
116
|
+
export const GREATER_THAN = 1;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Copyright 2023 Synnax Labs, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
4
|
+
// licenses/BSL.txt.
|
|
5
|
+
//
|
|
6
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
7
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
|
+
// included in the file licenses/APL.txt.
|
|
9
|
+
|
|
10
|
+
export * as compare from "@/compare/compare";
|
package/src/debounce.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Copyright 2023 Synnax Labs, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
4
|
+
// licenses/BSL.txt.
|
|
5
|
+
//
|
|
6
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
7
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
|
+
// included in the file licenses/APL.txt.
|
|
9
|
+
|
|
10
|
+
export const debounce = <F extends (...args: any[]) => void>(
|
|
11
|
+
func: F,
|
|
12
|
+
waitFor: number
|
|
13
|
+
): F => {
|
|
14
|
+
let timeout: ReturnType<typeof setTimeout> | null = null;
|
|
15
|
+
if (waitFor === 0) return func;
|
|
16
|
+
|
|
17
|
+
const debounced = (...args: Parameters<F>): void => {
|
|
18
|
+
if (timeout !== null) {
|
|
19
|
+
clearTimeout(timeout);
|
|
20
|
+
timeout = null;
|
|
21
|
+
}
|
|
22
|
+
timeout = setTimeout(() => func(...args), waitFor);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return debounced as F;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const throttle = <F extends (...args: any[]) => void>(
|
|
29
|
+
func: F,
|
|
30
|
+
waitFor: number
|
|
31
|
+
): F => {
|
|
32
|
+
let timeout: ReturnType<typeof setTimeout> | null = null;
|
|
33
|
+
if (waitFor === 0) return func;
|
|
34
|
+
|
|
35
|
+
const throttled = (...args: Parameters<F>): void => {
|
|
36
|
+
if (timeout === null) {
|
|
37
|
+
timeout = setTimeout(() => {
|
|
38
|
+
func(...args);
|
|
39
|
+
timeout = null;
|
|
40
|
+
}, waitFor);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return throttled as F;
|
|
45
|
+
}
|
package/src/deep/copy.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Copyright 2023 Synnax Labs, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
4
|
+
// licenses/BSL.txt.
|
|
5
|
+
//
|
|
6
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
7
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
|
+
// included in the file licenses/APL.txt.
|
|
9
|
+
|
|
10
|
+
import { type UnknownRecord } from "@/record";
|
|
11
|
+
|
|
12
|
+
export const copy = <T extends UnknownRecord<T>>(obj: T): T =>
|
|
13
|
+
JSON.parse(JSON.stringify(obj));
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Copyright 2023 Synnax Labs, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
4
|
+
// licenses/BSL.txt.
|
|
5
|
+
//
|
|
6
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
7
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
|
+
// included in the file licenses/APL.txt.
|
|
9
|
+
|
|
10
|
+
import { describe, it, expect } from "vitest";
|
|
11
|
+
|
|
12
|
+
import { deep } from "@/deep";
|
|
13
|
+
|
|
14
|
+
interface TestRecord {
|
|
15
|
+
a: number;
|
|
16
|
+
b: {
|
|
17
|
+
c?: number;
|
|
18
|
+
d?: number;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe("deepDelete", () => {
|
|
23
|
+
it("should delete a key", () => {
|
|
24
|
+
const a: TestRecord = {
|
|
25
|
+
a: 1,
|
|
26
|
+
b: {
|
|
27
|
+
c: 2,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
const b: TestRecord = {
|
|
31
|
+
a: 1,
|
|
32
|
+
b: {},
|
|
33
|
+
};
|
|
34
|
+
expect(deep.deleteD<TestRecord, 2>(a, "b.c")).toEqual(b);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Copyright 2023 Synnax Labs, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
4
|
+
// licenses/BSL.txt.
|
|
5
|
+
//
|
|
6
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
7
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
|
+
// included in the file licenses/APL.txt.
|
|
9
|
+
|
|
10
|
+
import { type Key } from "@/deep/key";
|
|
11
|
+
import { type UnknownRecord } from "@/record";
|
|
12
|
+
|
|
13
|
+
export const deleteD = <T extends UnknownRecord<T>, D extends number = 5>(
|
|
14
|
+
target: T,
|
|
15
|
+
...keys: Array<Key<T, D>>
|
|
16
|
+
): T => {
|
|
17
|
+
keys.forEach((key) => {
|
|
18
|
+
let curr: any = target;
|
|
19
|
+
const arr = key.split(".");
|
|
20
|
+
arr.forEach((k, i) => {
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
22
|
+
if (i === arr.length - 1) delete curr[k];
|
|
23
|
+
else curr = curr[k];
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
return target;
|
|
27
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Copyright 2023 Synnax Labs, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
4
|
+
// licenses/BSL.txt.
|
|
5
|
+
//
|
|
6
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
7
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
|
+
// included in the file licenses/APL.txt.
|
|
9
|
+
|
|
10
|
+
import { describe, it, expect } from "vitest";
|
|
11
|
+
|
|
12
|
+
import { deep } from "@/deep";
|
|
13
|
+
|
|
14
|
+
interface TestRecord {
|
|
15
|
+
a: number;
|
|
16
|
+
b: {
|
|
17
|
+
c?: number;
|
|
18
|
+
d?: number;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe("deepEqual", () => {
|
|
23
|
+
describe("deepPartialEqual", () => {
|
|
24
|
+
it("should return true for overlapping objects", () => {
|
|
25
|
+
const base: TestRecord = { a: 1, b: { c: 2, d: 3 } };
|
|
26
|
+
const partial: TestRecord = { a: 1, b: { c: 2 } };
|
|
27
|
+
expect(deep.partialEqual(base, partial)).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
it("should return false for non-overlapping objects", () => {
|
|
30
|
+
const base = { a: 1, b: { c: 2, d: 3 } };
|
|
31
|
+
const partial = { a: 1, b: { c: 2, d: 4 } };
|
|
32
|
+
expect(deep.partialEqual(base, partial)).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe("deepEqual", () => {
|
|
36
|
+
it("should return true for equal objects", () => {
|
|
37
|
+
const a = { a: 1, b: { c: 2, d: 3 } };
|
|
38
|
+
const b = { a: 1, b: { c: 2, d: 3 } };
|
|
39
|
+
expect(deep.equal(a, b)).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
it("should return false for non-equal objects", () => {
|
|
42
|
+
const a = { a: 1, b: { c: 2, d: 3 } };
|
|
43
|
+
const b = { a: 1, b: { c: 2, d: 4 } };
|
|
44
|
+
expect(deep.equal(a, b)).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
it("should return false for partial objects", () => {
|
|
47
|
+
const a = { a: 1, b: { c: 2, d: 3 } };
|
|
48
|
+
const b = { a: 1, b: { c: 2 } };
|
|
49
|
+
expect(deep.equal(a, b)).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
it("should return true for primitive arrays that are equal", () => {
|
|
52
|
+
const a = [1, 2, 3];
|
|
53
|
+
const b = [1, 2, 3];
|
|
54
|
+
expect(deep.equal(a, b)).toBe(true);
|
|
55
|
+
})
|
|
56
|
+
it("should true for object arrays that are equal", () => {
|
|
57
|
+
const a = [{ a: 1 }, { b: 2 }];
|
|
58
|
+
const b = [{ a: 1 }, { b: 2 }];
|
|
59
|
+
expect(deep.equal(a, b)).toBe(true);
|
|
60
|
+
});
|
|
61
|
+
it("should return true for nested object arrays that are equal", () => {
|
|
62
|
+
const a = [{ a: 1 }, { b: 2, c: [{ d: 3 }] }];
|
|
63
|
+
const b = [{ a: 1 }, { b: 2, c: [{ d: 3 }] }];
|
|
64
|
+
expect(deep.equal(a, b)).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
it("shold return false for nested object arrays that are not equal", () => {
|
|
67
|
+
const a = [{ a: 1 }, { b: 2, c: [{ d: 3 }] }];
|
|
68
|
+
const b = [{ a: 1 }, { b: 2, c: [{ d: 4 }] }];
|
|
69
|
+
expect(deep.equal(a, b)).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
it("should return false for primitive arrays that are not equal", () => {
|
|
72
|
+
const a = [1, 2, 3];
|
|
73
|
+
const b = [1, 2, 4];
|
|
74
|
+
expect(deep.equal(a, b)).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
it("should return false for object arrays that are not equal", () => {
|
|
77
|
+
const a = [{ a: 1 }, { b: 2 }];
|
|
78
|
+
const b = [{ a: 1 }, { b: 2, c: 4}];
|
|
79
|
+
expect(deep.equal(a, b)).toBe(false);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|