@synnaxlabs/x 0.44.4 → 0.45.1
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/.turbo/turbo-build.log +38 -38
- package/dist/{base-BORMP3mH.js → base-DRybODwJ.js} +7 -6
- package/dist/base-KIBsp6TI.cjs +1 -0
- package/dist/binary.cjs +1 -1
- package/dist/binary.js +1 -1
- package/dist/{bounds-BXbqBINt.cjs → bounds-C2TKFgVk.cjs} +1 -1
- package/dist/{bounds-BQqppNFf.js → bounds-DeUXrllt.js} +11 -11
- package/dist/bounds.cjs +1 -1
- package/dist/bounds.js +1 -1
- package/dist/{box-DRH5SOaY.js → box-BYuq-Gjx.js} +4 -4
- package/dist/{box-qgxWXNhm.cjs → box-Blu-4d1n.cjs} +1 -1
- package/dist/box.cjs +1 -1
- package/dist/box.js +1 -1
- package/dist/caseconv.cjs +1 -1
- package/dist/caseconv.js +1 -1
- package/dist/compare.cjs +1 -1
- package/dist/compare.js +1 -1
- package/dist/deep.cjs +1 -1
- package/dist/deep.js +211 -99
- package/dist/{dimensions-qY12pyfC.cjs → dimensions-Cg5Owbwn.cjs} +1 -1
- package/dist/{dimensions-RaRkesPq.js → dimensions-DC0uLPwn.js} +1 -1
- package/dist/dimensions.cjs +1 -1
- package/dist/dimensions.js +1 -1
- package/dist/{direction-DKdfJwj7.js → direction-C_b4tfRN.js} +1 -1
- package/dist/{direction-XCdrc4is.cjs → direction-DqQB9M37.cjs} +1 -1
- package/dist/direction.cjs +1 -1
- package/dist/direction.js +1 -1
- package/dist/external-CtHGFcox.cjs +1 -0
- package/dist/{external-BM_NS5yM.js → external-tyaEMW4S.js} +1 -1
- package/dist/get-CXkBfLu1.js +82 -0
- package/dist/get-OP63N4c3.cjs +1 -0
- package/dist/{index-B5l_quQn.js → index-BHXRDFYj.js} +1 -1
- package/dist/index-Bxlv0uf_.js +57 -0
- package/dist/{index-CBMHFqs4.js → index-C452Pas0.js} +25 -22
- package/dist/{index-BQe8OIgm.cjs → index-CwGPVvbl.cjs} +1 -1
- package/dist/index-uDxeM-cl.cjs +1 -0
- package/dist/index-xaxa1hoa.cjs +1 -0
- package/dist/index.cjs +3 -3
- package/dist/index.js +442 -380
- package/dist/location-BPoXwOni.cjs +1 -0
- package/dist/{location-CGLioInQ.js → location-CVxysrHI.js} +31 -30
- package/dist/location.cjs +1 -1
- package/dist/location.js +1 -1
- package/dist/{scale-76Azh2EE.cjs → scale-BHs716im.cjs} +1 -1
- package/dist/{scale-BhIvACdB.js → scale-DjxC6ep2.js} +4 -4
- package/dist/scale.cjs +1 -1
- package/dist/scale.js +1 -1
- package/dist/series-C6ZwNf8i.cjs +6 -0
- package/dist/{series-kgnLXSDr.js → series-W5Aafjeu.js} +268 -251
- package/dist/{spatial-QY891r0E.js → spatial-DnsaOypA.js} +1 -1
- package/dist/{spatial-BsGadoUr.cjs → spatial-DrxzaD5U.cjs} +1 -1
- package/dist/spatial.cjs +1 -1
- package/dist/spatial.js +8 -8
- package/dist/src/caseconv/caseconv.d.ts +9 -0
- package/dist/src/caseconv/caseconv.d.ts.map +1 -1
- package/dist/src/color/color.d.ts +7 -0
- package/dist/src/color/color.d.ts.map +1 -1
- package/dist/src/deep/copy.spec.d.ts +2 -0
- package/dist/src/deep/copy.spec.d.ts.map +1 -0
- package/dist/src/deep/external.d.ts +3 -1
- package/dist/src/deep/external.d.ts.map +1 -1
- package/dist/src/deep/get.bench.d.ts +2 -0
- package/dist/src/deep/get.bench.d.ts.map +1 -0
- package/dist/src/deep/get.d.ts +16 -0
- package/dist/src/deep/get.d.ts.map +1 -0
- package/dist/src/deep/get.spec.d.ts +2 -0
- package/dist/src/deep/get.spec.d.ts.map +1 -0
- package/dist/src/deep/partial.spec.d.ts +2 -0
- package/dist/src/deep/partial.spec.d.ts.map +1 -0
- package/dist/src/deep/path.d.ts +4 -92
- package/dist/src/deep/path.d.ts.map +1 -1
- package/dist/src/deep/remove.bench.d.ts +2 -0
- package/dist/src/deep/remove.bench.d.ts.map +1 -0
- package/dist/src/deep/remove.d.ts +2 -0
- package/dist/src/deep/remove.d.ts.map +1 -0
- package/dist/src/deep/remove.spec.d.ts +2 -0
- package/dist/src/deep/remove.spec.d.ts.map +1 -0
- package/dist/src/deep/set.bench.d.ts +2 -0
- package/dist/src/deep/set.bench.d.ts.map +1 -0
- package/dist/src/deep/set.d.ts +2 -0
- package/dist/src/deep/set.d.ts.map +1 -0
- package/dist/src/deep/set.spec.d.ts +2 -0
- package/dist/src/deep/set.spec.d.ts.map +1 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/primitive/primitive.d.ts +4 -0
- package/dist/src/primitive/primitive.d.ts.map +1 -1
- package/dist/src/spatial/location/location.d.ts +2 -2
- package/dist/src/spatial/location/location.d.ts.map +1 -1
- package/dist/src/status/status.d.ts +2 -1
- package/dist/src/status/status.d.ts.map +1 -1
- package/dist/src/telem/series.d.ts +1 -0
- package/dist/src/telem/series.d.ts.map +1 -1
- package/dist/src/telem/telem.d.ts +15 -6
- package/dist/src/telem/telem.d.ts.map +1 -1
- package/dist/src/undefined.d.ts +2 -0
- package/dist/src/undefined.d.ts.map +1 -0
- package/dist/telem.cjs +1 -1
- package/dist/telem.js +1 -1
- package/dist/{xy-BKIJiLu_.cjs → xy-DWwtHmgn.cjs} +1 -1
- package/dist/{xy-CBuhMaIo.js → xy-DYPw8-8C.js} +1 -1
- package/dist/xy.cjs +1 -1
- package/dist/xy.js +1 -1
- package/dist/zod.cjs +1 -1
- package/dist/zod.js +1 -1
- package/package.json +4 -4
- package/src/caseconv/caseconv.spec.ts +30 -0
- package/src/caseconv/caseconv.ts +46 -0
- package/src/color/color.spec.ts +40 -0
- package/src/color/color.ts +72 -0
- package/src/deep/copy.spec.ts +148 -0
- package/src/deep/external.ts +3 -1
- package/src/deep/get.bench.ts +170 -0
- package/src/deep/get.spec.ts +196 -0
- package/src/deep/get.ts +79 -0
- package/src/deep/partial.spec.ts +194 -0
- package/src/deep/path.spec.ts +92 -183
- package/src/deep/path.ts +27 -198
- package/src/deep/remove.bench.ts +238 -0
- package/src/deep/remove.spec.ts +219 -0
- package/src/deep/remove.ts +102 -0
- package/src/deep/set.bench.ts +208 -0
- package/src/deep/set.spec.ts +369 -0
- package/src/deep/set.ts +91 -0
- package/src/index.ts +1 -0
- package/src/primitive/primitive.spec.ts +9 -0
- package/src/primitive/primitive.ts +9 -0
- package/src/spatial/location/location.ts +2 -0
- package/src/status/status.spec.ts +52 -0
- package/src/status/status.ts +19 -6
- package/src/telem/series.ts +2 -0
- package/src/telem/telem.spec.ts +302 -0
- package/src/telem/telem.ts +60 -18
- package/src/undefined.ts +14 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/base-BLNViP3D.cjs +0 -1
- package/dist/external-E3ErJeeM.cjs +0 -1
- package/dist/index-D2xcvEO5.js +0 -46
- package/dist/index-DOJlZHqJ.cjs +0 -1
- package/dist/index-DdhM_E4k.cjs +0 -1
- package/dist/location-DJ_K4SlP.cjs +0 -1
- package/dist/path-Blh4wJuA.js +0 -110
- package/dist/path-CPSfCjde.cjs +0 -1
- package/dist/series-tAhThbnz.cjs +0 -6
- package/dist/src/deep/delete.d.ts +0 -3
- package/dist/src/deep/delete.d.ts.map +0 -1
- package/dist/src/deep/delete.spec.d.ts +0 -2
- package/dist/src/deep/delete.spec.d.ts.map +0 -1
- package/src/deep/delete.spec.ts +0 -73
- package/src/deep/delete.ts +0 -27
package/src/deep/path.spec.ts
CHANGED
|
@@ -10,164 +10,8 @@
|
|
|
10
10
|
import { describe, expect, it } from "vitest";
|
|
11
11
|
|
|
12
12
|
import { deep } from "@/deep";
|
|
13
|
-
import { type record } from "@/record";
|
|
14
|
-
|
|
15
|
-
interface TestRecord {
|
|
16
|
-
a: number;
|
|
17
|
-
b: {
|
|
18
|
-
c?: number;
|
|
19
|
-
d?: number;
|
|
20
|
-
};
|
|
21
|
-
c: number[];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
describe("path", () => {
|
|
25
|
-
describe("get", () => {
|
|
26
|
-
it("should get a key", () => {
|
|
27
|
-
const a: TestRecord = {
|
|
28
|
-
a: 1,
|
|
29
|
-
b: {
|
|
30
|
-
c: 2,
|
|
31
|
-
},
|
|
32
|
-
c: [1],
|
|
33
|
-
};
|
|
34
|
-
expect(deep.get(a, "b.c")).toEqual(2);
|
|
35
|
-
});
|
|
36
|
-
it("should get an array index", () => {
|
|
37
|
-
const a: TestRecord = {
|
|
38
|
-
a: 1,
|
|
39
|
-
b: {
|
|
40
|
-
c: 2,
|
|
41
|
-
},
|
|
42
|
-
c: [1, 2, 3],
|
|
43
|
-
};
|
|
44
|
-
expect(deep.get(a, "c.1")).toEqual(2);
|
|
45
|
-
});
|
|
46
|
-
it("should return the object itself if the key is empty", () => {
|
|
47
|
-
const a: TestRecord = {
|
|
48
|
-
a: 1,
|
|
49
|
-
b: {
|
|
50
|
-
c: 2,
|
|
51
|
-
},
|
|
52
|
-
c: [1, 2, 3],
|
|
53
|
-
};
|
|
54
|
-
expect(deep.get(a, "")).toStrictEqual(a);
|
|
55
|
-
});
|
|
56
|
-
describe("custom getter function", () => {
|
|
57
|
-
const v = {
|
|
58
|
-
a: {
|
|
59
|
-
value: () => ({
|
|
60
|
-
c: 0,
|
|
61
|
-
}),
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
it("should use the custom getter function", () => {
|
|
65
|
-
expect(
|
|
66
|
-
deep.get(v, "a.value().c", {
|
|
67
|
-
optional: false,
|
|
68
|
-
getter: (obj, key) => {
|
|
69
|
-
if (key === "value()")
|
|
70
|
-
return (obj as { value: () => { c: number } }).value();
|
|
71
|
-
return obj[key];
|
|
72
|
-
},
|
|
73
|
-
}),
|
|
74
|
-
).toEqual(0);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it("should get an array of keyed records", () => {
|
|
78
|
-
interface TestKeyedRecord {
|
|
79
|
-
values: record.KeyedNamed[];
|
|
80
|
-
}
|
|
81
|
-
const a: TestKeyedRecord = {
|
|
82
|
-
values: [
|
|
83
|
-
{ key: "a", name: "a" },
|
|
84
|
-
{ key: "b", name: "b" },
|
|
85
|
-
],
|
|
86
|
-
};
|
|
87
|
-
expect(deep.get(a, "values.a.name")).toEqual("a");
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
describe("set", () => {
|
|
93
|
-
it("should set a key", () => {
|
|
94
|
-
const a: TestRecord = {
|
|
95
|
-
a: 1,
|
|
96
|
-
b: {
|
|
97
|
-
c: 2,
|
|
98
|
-
},
|
|
99
|
-
c: [1],
|
|
100
|
-
};
|
|
101
|
-
const b: TestRecord = {
|
|
102
|
-
a: 1,
|
|
103
|
-
b: {
|
|
104
|
-
c: 3,
|
|
105
|
-
},
|
|
106
|
-
c: [1],
|
|
107
|
-
};
|
|
108
|
-
deep.set(a, "b.c", 3);
|
|
109
|
-
expect(a).toEqual(b);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it("should set an array index", () => {
|
|
113
|
-
const a: TestRecord = {
|
|
114
|
-
a: 1,
|
|
115
|
-
b: {
|
|
116
|
-
c: 2,
|
|
117
|
-
},
|
|
118
|
-
c: [1, 2, 3],
|
|
119
|
-
};
|
|
120
|
-
const b: TestRecord = {
|
|
121
|
-
a: 1,
|
|
122
|
-
b: {
|
|
123
|
-
c: 2,
|
|
124
|
-
},
|
|
125
|
-
c: [1, 4, 3],
|
|
126
|
-
};
|
|
127
|
-
deep.set(a, "c.1", 4);
|
|
128
|
-
expect(a).toEqual(b);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it("should interpret a leading number also containing letters as a key", () => {
|
|
132
|
-
const data = {
|
|
133
|
-
a: [
|
|
134
|
-
{
|
|
135
|
-
key: "1b",
|
|
136
|
-
value: 1,
|
|
137
|
-
},
|
|
138
|
-
],
|
|
139
|
-
};
|
|
140
|
-
deep.set(data, "a.1b.value", 2);
|
|
141
|
-
expect(deep.get(data, "a.1b.value")).toEqual(2);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it("should set a value on a nested object in the array by key", () => {
|
|
145
|
-
const data = {
|
|
146
|
-
config: {
|
|
147
|
-
channels: [{ key: "tMnAnJeQmn6", type: "ai_voltage" }],
|
|
148
|
-
},
|
|
149
|
-
};
|
|
150
|
-
deep.set(data, "config.channels.tMnAnJeQmn6.type", "ai_force_bridge_table");
|
|
151
|
-
expect(data.config.channels[0].type).toEqual("ai_force_bridge_table");
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it("should set an entire item in the array by its key", () => {
|
|
155
|
-
const data = {
|
|
156
|
-
config: {
|
|
157
|
-
channels: [{ key: "tMnAnJeQmn6", type: "ai_voltage" }],
|
|
158
|
-
},
|
|
159
|
-
};
|
|
160
|
-
deep.set(data, "config.channels.tMnAnJeQmn6", {
|
|
161
|
-
key: "tMnAnJeQmn6",
|
|
162
|
-
type: "ai_force_bridge_table",
|
|
163
|
-
});
|
|
164
|
-
expect(data.config.channels[0]).toEqual({
|
|
165
|
-
key: "tMnAnJeQmn6",
|
|
166
|
-
type: "ai_force_bridge_table",
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
13
|
|
|
14
|
+
describe("path utilities", () => {
|
|
171
15
|
describe("transformPath", () => {
|
|
172
16
|
it("should transform a path", () => {
|
|
173
17
|
expect(deep.transformPath("a.b.c", (part) => part.toUpperCase())).toEqual(
|
|
@@ -184,7 +28,7 @@ describe("path", () => {
|
|
|
184
28
|
});
|
|
185
29
|
});
|
|
186
30
|
|
|
187
|
-
describe("
|
|
31
|
+
describe("pathsMatch", () => {
|
|
188
32
|
it("should return true if two paths are equal", () => {
|
|
189
33
|
expect(deep.pathsMatch("a.b.c", "a.b.c")).toEqual(true);
|
|
190
34
|
});
|
|
@@ -197,6 +41,12 @@ describe("path", () => {
|
|
|
197
41
|
it("should return true for an empty pattern", () => {
|
|
198
42
|
expect(deep.pathsMatch("a.b.c", "")).toEqual(true);
|
|
199
43
|
});
|
|
44
|
+
it("should return false if pattern is longer than path", () => {
|
|
45
|
+
expect(deep.pathsMatch("a.b", "a.b.c")).toEqual(false);
|
|
46
|
+
});
|
|
47
|
+
it("should return false if paths don't match", () => {
|
|
48
|
+
expect(deep.pathsMatch("a.b.c", "a.b.d")).toEqual(false);
|
|
49
|
+
});
|
|
200
50
|
});
|
|
201
51
|
|
|
202
52
|
describe("resolvePath", () => {
|
|
@@ -225,33 +75,92 @@ describe("path", () => {
|
|
|
225
75
|
});
|
|
226
76
|
});
|
|
227
77
|
|
|
228
|
-
describe("
|
|
229
|
-
|
|
230
|
-
a
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
c
|
|
235
|
-
|
|
236
|
-
it("should delete a key", () => {
|
|
237
|
-
const cpy = deep.copy(a);
|
|
238
|
-
deep.remove(a, "b.c");
|
|
239
|
-
expect(a).toEqual({ ...cpy, b: {} });
|
|
78
|
+
describe("element", () => {
|
|
79
|
+
it("should get element at index", () => {
|
|
80
|
+
expect(deep.element("a.b.c", 1)).toEqual("b");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("should handle negative index", () => {
|
|
84
|
+
expect(deep.element("a.b.c", -1)).toEqual("c");
|
|
85
|
+
expect(deep.element("a.b.c", -2)).toEqual("b");
|
|
240
86
|
});
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
deep.
|
|
244
|
-
expect(a).toEqual({ ...cpy, c: [1, 3] });
|
|
87
|
+
|
|
88
|
+
it("should get first element", () => {
|
|
89
|
+
expect(deep.element("a.b.c", 0)).toEqual("a");
|
|
245
90
|
});
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe("getIndex", () => {
|
|
94
|
+
it("should return index for numeric string", () => {
|
|
95
|
+
expect(deep.getIndex("42")).toEqual(42);
|
|
250
96
|
});
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
deep.
|
|
254
|
-
|
|
97
|
+
|
|
98
|
+
it("should return null for non-numeric string", () => {
|
|
99
|
+
expect(deep.getIndex("abc")).toBeNull();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("should return null for mixed string", () => {
|
|
103
|
+
expect(deep.getIndex("1a")).toBeNull();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("should handle zero", () => {
|
|
107
|
+
expect(deep.getIndex("0")).toEqual(0);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe("defaultGetter", () => {
|
|
112
|
+
it("should get property from object", () => {
|
|
113
|
+
const obj = { a: 1, b: 2 };
|
|
114
|
+
expect(deep.defaultGetter(obj, "a")).toEqual(1);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("should get index from array", () => {
|
|
118
|
+
const arr = [1, 2, 3];
|
|
119
|
+
expect(deep.defaultGetter(arr as any, "1")).toEqual(2);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("should find keyed item in array", () => {
|
|
123
|
+
const arr = [
|
|
124
|
+
{ key: "item1", value: 1 },
|
|
125
|
+
{ key: "item2", value: 2 },
|
|
126
|
+
];
|
|
127
|
+
expect(deep.defaultGetter(arr as any, "item2")).toEqual({ key: "item2", value: 2 });
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("should return undefined for non-existent key", () => {
|
|
131
|
+
const obj = { a: 1 };
|
|
132
|
+
expect(deep.defaultGetter(obj, "b")).toBeUndefined();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("should handle empty array", () => {
|
|
136
|
+
const arr: any[] = [];
|
|
137
|
+
expect(deep.defaultGetter(arr as any, "0")).toBeUndefined();
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe("findBestKey", () => {
|
|
142
|
+
it("should find single part key", () => {
|
|
143
|
+
const obj = { a: { b: 1 } };
|
|
144
|
+
const result = deep.findBestKey(obj, ["a", "b"]);
|
|
145
|
+
expect(result).toEqual(["a", 1]);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("should find multi-part key with period", () => {
|
|
149
|
+
const obj = { "a.b": { c: 1 } };
|
|
150
|
+
const result = deep.findBestKey(obj, ["a.b", "c"]);
|
|
151
|
+
expect(result).toEqual(["a.b", 1]);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("should return null if no key found", () => {
|
|
155
|
+
const obj = { a: 1 };
|
|
156
|
+
const result = deep.findBestKey(obj, ["b", "c"]);
|
|
157
|
+
expect(result).toBeNull();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("should prefer shorter key", () => {
|
|
161
|
+
const obj = { a: { b: 1 }, "a.b": 2 };
|
|
162
|
+
const result = deep.findBestKey(obj, ["a", "b"]);
|
|
163
|
+
expect(result).toEqual(["a", 1]);
|
|
255
164
|
});
|
|
256
165
|
});
|
|
257
|
-
});
|
|
166
|
+
});
|
package/src/deep/path.ts
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
import { type Join } from "@/join";
|
|
11
11
|
import { type record } from "@/record";
|
|
12
12
|
|
|
13
|
+
export const SEPARATOR = ".";
|
|
14
|
+
|
|
13
15
|
type Prev = [
|
|
14
16
|
never,
|
|
15
17
|
0,
|
|
@@ -36,10 +38,6 @@ type Prev = [
|
|
|
36
38
|
...0[],
|
|
37
39
|
];
|
|
38
40
|
|
|
39
|
-
/**
|
|
40
|
-
* A type that represents a deep key in an object.
|
|
41
|
-
* @example type Key<T, 3> = "a" | "a.b" | "a.b.c"
|
|
42
|
-
*/
|
|
43
41
|
export type Key<T, D extends number = 5> = [D] extends [never]
|
|
44
42
|
? never
|
|
45
43
|
: T extends object
|
|
@@ -50,51 +48,6 @@ export type Key<T, D extends number = 5> = [D] extends [never]
|
|
|
50
48
|
}[keyof T]
|
|
51
49
|
: "";
|
|
52
50
|
|
|
53
|
-
/** Options for the get function. */
|
|
54
|
-
export interface GetOptions<O extends boolean | undefined = boolean | undefined> {
|
|
55
|
-
optional: O;
|
|
56
|
-
getter?: (obj: record.Unknown, key: string) => unknown;
|
|
57
|
-
separator?: string;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* A function that gets the value at the given path on the object. If the path does not exist
|
|
62
|
-
* and the optional flag is set to true, null will be returned. If the path does not exist and
|
|
63
|
-
* the optional flag is set to false, an error will be thrown.
|
|
64
|
-
* @param obj the object to get the value from.
|
|
65
|
-
* @param path the path to get the value at.
|
|
66
|
-
* @param options the options for getting the value.
|
|
67
|
-
* @param options.optional whether the path is optional.
|
|
68
|
-
* @param options.getter a custom getter function to use on each part of the path.
|
|
69
|
-
* @returns the value at the given path on the object.
|
|
70
|
-
*/
|
|
71
|
-
export interface Get {
|
|
72
|
-
<V = record.Unknown, T = record.Unknown>(
|
|
73
|
-
obj: T,
|
|
74
|
-
path: string,
|
|
75
|
-
options?: GetOptions<false>,
|
|
76
|
-
): V;
|
|
77
|
-
<V = record.Unknown, T = record.Unknown>(
|
|
78
|
-
obj: T,
|
|
79
|
-
path: string,
|
|
80
|
-
options?: GetOptions<boolean | undefined>,
|
|
81
|
-
): V | null;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** A strongly typed version of the @link Get function. */
|
|
85
|
-
export interface TypedGet<V = record.Unknown, T = record.Unknown> {
|
|
86
|
-
(obj: T, path: string, options?: GetOptions<false>): V;
|
|
87
|
-
(obj: T, path: string, options?: GetOptions<boolean | undefined>): V | null;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Executes the given replacer function on each part of the path.
|
|
92
|
-
* @param path the path to transform
|
|
93
|
-
* @param replacer the function to execute on each part of the path. If multiple
|
|
94
|
-
* parts are returned, they will be joined with a period. If null/undefined is returned,
|
|
95
|
-
* the part will be removed from the path.
|
|
96
|
-
* @returns the transformed path.
|
|
97
|
-
*/
|
|
98
51
|
export const transformPath = (
|
|
99
52
|
path: string,
|
|
100
53
|
replacer: (
|
|
@@ -102,7 +55,7 @@ export const transformPath = (
|
|
|
102
55
|
index: number,
|
|
103
56
|
parts: string[],
|
|
104
57
|
) => string | string[] | undefined,
|
|
105
|
-
separator =
|
|
58
|
+
separator: string = SEPARATOR,
|
|
106
59
|
): string => {
|
|
107
60
|
const parts = path.split(separator);
|
|
108
61
|
const result = parts
|
|
@@ -116,7 +69,7 @@ export const transformPath = (
|
|
|
116
69
|
return result.join(separator);
|
|
117
70
|
};
|
|
118
71
|
|
|
119
|
-
const defaultGetter = (obj: record.Unknown, key: string): unknown => {
|
|
72
|
+
export const defaultGetter = (obj: record.Unknown, key: string): unknown => {
|
|
120
73
|
if (!Array.isArray(obj)) return obj[key];
|
|
121
74
|
const res = obj[key];
|
|
122
75
|
if (res != null || obj.length == 0) return res;
|
|
@@ -127,167 +80,25 @@ const defaultGetter = (obj: record.Unknown, key: string): unknown => {
|
|
|
127
80
|
};
|
|
128
81
|
|
|
129
82
|
export const resolvePath = <T = record.Unknown>(path: string, obj: T): string => {
|
|
130
|
-
const parts = path.split(
|
|
83
|
+
const parts = path.split(SEPARATOR);
|
|
131
84
|
parts.forEach((part, i) => {
|
|
132
85
|
obj = defaultGetter(obj as record.Unknown, part) as T;
|
|
133
86
|
if (obj != null && typeof obj === "object" && "key" in obj)
|
|
134
87
|
parts[i] = obj.key as string;
|
|
135
88
|
});
|
|
136
|
-
return parts.join(
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Gets the value at the given path on the object. If the path does not exist
|
|
141
|
-
* and the optional flag is set to true, null will be returned. If the path does
|
|
142
|
-
* not exist and the optional flag is set to false, an error will be thrown.
|
|
143
|
-
* @param obj the object to get the value from.
|
|
144
|
-
* @param path the path to get the value at.
|
|
145
|
-
* @param opts the options for getting the value.
|
|
146
|
-
* @param opts.optional whether the path is optional.
|
|
147
|
-
* @param opts.getter a custom getter function to use on each part of the path.
|
|
148
|
-
* @returns the value at the given path on the object.
|
|
149
|
-
*/
|
|
150
|
-
export const get = (<V = record.Unknown, T = record.Unknown>(
|
|
151
|
-
obj: T,
|
|
152
|
-
path: string,
|
|
153
|
-
opts: GetOptions = { optional: false, separator: "." },
|
|
154
|
-
): V | null => {
|
|
155
|
-
opts.separator ??= ".";
|
|
156
|
-
const { optional, getter = defaultGetter } = opts;
|
|
157
|
-
const parts = path.split(opts.separator);
|
|
158
|
-
if (parts.length === 1 && parts[0] === "") return obj as record.Unknown as V;
|
|
159
|
-
let result: record.Unknown = obj as record.Unknown;
|
|
160
|
-
for (const part of parts) {
|
|
161
|
-
const v = getter(result, part);
|
|
162
|
-
if (v == null) {
|
|
163
|
-
if (optional) return null;
|
|
164
|
-
throw new Error(`Path ${path} does not exist. ${part} is null`);
|
|
165
|
-
}
|
|
166
|
-
result = v as record.Unknown;
|
|
167
|
-
}
|
|
168
|
-
return result as V;
|
|
169
|
-
}) as Get;
|
|
170
|
-
|
|
171
|
-
const getIndex = (part: string): number | null => {
|
|
172
|
-
// in order to be considered an index, all characters must be numbers
|
|
173
|
-
for (const char of part) if (isNaN(parseInt(char))) return null;
|
|
174
|
-
return parseInt(part);
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Sets the value at the given path on the object. If the parents of the deep path
|
|
179
|
-
* do not exist, new objects will be created.
|
|
180
|
-
* @param obj the object to set the value on.
|
|
181
|
-
* @param path the path to set the value at.
|
|
182
|
-
* @param value the value to set.
|
|
183
|
-
*/
|
|
184
|
-
export const set = <V>(obj: V, path: string, value: unknown): void => {
|
|
185
|
-
const parts = path.split(".");
|
|
186
|
-
let result: record.Unknown = obj as record.Unknown;
|
|
187
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
188
|
-
const part = parts[i];
|
|
189
|
-
const v = defaultGetter(result, part);
|
|
190
|
-
if (v == null) throw new Error(`Path ${path} does not exist. ${part} is null`);
|
|
191
|
-
result = v as record.Unknown;
|
|
192
|
-
}
|
|
193
|
-
try {
|
|
194
|
-
if (!Array.isArray(result)) {
|
|
195
|
-
result[parts[parts.length - 1]] = value;
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
if (result.length === 0) return;
|
|
199
|
-
const index = getIndex(parts[parts.length - 1]);
|
|
200
|
-
// If we can't parse an index, try to interpret it as an object key.
|
|
201
|
-
if (index == null) {
|
|
202
|
-
const first = result[0];
|
|
203
|
-
if (typeof first === "object" && "key" in first) {
|
|
204
|
-
const objIndex = result.findIndex((o) => o.key === parts[parts.length - 1]);
|
|
205
|
-
if (objIndex !== -1) {
|
|
206
|
-
result[objIndex] = value;
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
result[index] = value;
|
|
213
|
-
} catch (e) {
|
|
214
|
-
console.error("failed to set value", value, "at path", path, "on object", obj);
|
|
215
|
-
throw e;
|
|
216
|
-
}
|
|
89
|
+
return parts.join(SEPARATOR);
|
|
217
90
|
};
|
|
218
91
|
|
|
219
|
-
/**
|
|
220
|
-
* Removes the value at the given path, modifying the object in place.
|
|
221
|
-
* @param obj the object to remove the value from.
|
|
222
|
-
* @param path the path to remove the value from.
|
|
223
|
-
* @returns the object with the value removed.
|
|
224
|
-
*/
|
|
225
|
-
export const remove = <V>(obj: V, path: string): void => {
|
|
226
|
-
const parts = path.split(".");
|
|
227
|
-
let result: record.Unknown = obj as record.Unknown;
|
|
228
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
229
|
-
const part = parts[i];
|
|
230
|
-
if (result[part] == null) return;
|
|
231
|
-
result = result[part] as record.Unknown;
|
|
232
|
-
}
|
|
233
|
-
// if its an array, we need to splice it
|
|
234
|
-
if (Array.isArray(result)) {
|
|
235
|
-
const index = parseInt(parts[parts.length - 1]);
|
|
236
|
-
if (isNaN(index)) return;
|
|
237
|
-
result.splice(index, 1);
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
delete result[parts[parts.length - 1]];
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Returns the element at the given index in the path.
|
|
245
|
-
* @param path the path to get the element from
|
|
246
|
-
* @param index the index of the element to get
|
|
247
|
-
* @returns the element at the given index in the path
|
|
248
|
-
*/
|
|
249
92
|
export const element = (path: string, index: number): string => {
|
|
250
|
-
const parts = path.split(
|
|
93
|
+
const parts = path.split(SEPARATOR);
|
|
251
94
|
if (index < 0) return parts[parts.length + index];
|
|
252
95
|
return parts[index];
|
|
253
96
|
};
|
|
254
97
|
|
|
255
|
-
/**
|
|
256
|
-
* Checks if the path exists in the object.
|
|
257
|
-
* @param obj the object to check
|
|
258
|
-
* @param path the path to check
|
|
259
|
-
* @returns whether the path exists in the object
|
|
260
|
-
*/
|
|
261
|
-
export const has = <V = record.Unknown, T = record.Unknown>(
|
|
262
|
-
obj: T,
|
|
263
|
-
path: string,
|
|
264
|
-
): boolean => {
|
|
265
|
-
try {
|
|
266
|
-
get<V, T>(obj, path);
|
|
267
|
-
return true;
|
|
268
|
-
} catch {
|
|
269
|
-
return false;
|
|
270
|
-
}
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Checks if the path matches the given pattern.
|
|
275
|
-
*
|
|
276
|
-
* @param path The path to check.
|
|
277
|
-
* @param pattern The pattern to match against. Only "*" is supported as a wildcard.
|
|
278
|
-
* @returns Whether the path matches the pattern.
|
|
279
|
-
*
|
|
280
|
-
* * @example
|
|
281
|
-
* pathsMatch("a.b.c", "a.b.c") // true
|
|
282
|
-
* pathsMatch("a.b.c", "a.b") // true
|
|
283
|
-
* pathsMatch("a.b", "a.b.c") // false
|
|
284
|
-
* pathsMatch("a.b.c", "a.*") // true
|
|
285
|
-
* pathsMatch("a.b.c", "a.*.c") // true
|
|
286
|
-
*/
|
|
287
98
|
export const pathsMatch = (path: string, pattern: string): boolean => {
|
|
288
99
|
if (pattern.length === 0) return true;
|
|
289
|
-
const parts = path.split(
|
|
290
|
-
const patterns = pattern.split(
|
|
100
|
+
const parts = path.split(SEPARATOR);
|
|
101
|
+
const patterns = pattern.split(SEPARATOR);
|
|
291
102
|
if (patterns.length > parts.length) return false;
|
|
292
103
|
for (let i = 0; i < patterns.length; i++) {
|
|
293
104
|
const part = parts[i];
|
|
@@ -297,3 +108,21 @@ export const pathsMatch = (path: string, pattern: string): boolean => {
|
|
|
297
108
|
}
|
|
298
109
|
return true;
|
|
299
110
|
};
|
|
111
|
+
|
|
112
|
+
export const getIndex = (part: string): number | null => {
|
|
113
|
+
const num = parseInt(part);
|
|
114
|
+
if (isNaN(num) || num < 0 || num.toString() !== part) return null;
|
|
115
|
+
return num;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export const findBestKey = (
|
|
119
|
+
obj: record.Unknown,
|
|
120
|
+
remainingParts: string[],
|
|
121
|
+
): [string, number] | null => {
|
|
122
|
+
for (let i = 1; i <= remainingParts.length; i++) {
|
|
123
|
+
const candidateKey = remainingParts.slice(0, i).join(SEPARATOR);
|
|
124
|
+
const v = defaultGetter(obj, candidateKey);
|
|
125
|
+
if (v != null) return [candidateKey, i];
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
};
|