@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
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
// Copyright 2025 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 { bench, describe } from "vitest";
|
|
11
|
+
|
|
12
|
+
import { deep } from "@/deep";
|
|
13
|
+
|
|
14
|
+
describe("deep.remove benchmarks", () => {
|
|
15
|
+
bench("shallow property remove", () => {
|
|
16
|
+
const obj = { a: 1, b: 2, c: 3, d: 4, e: 5 };
|
|
17
|
+
deep.remove(obj, "c");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
bench("nested property remove (5 levels)", () => {
|
|
21
|
+
const obj = {
|
|
22
|
+
level1: {
|
|
23
|
+
level2: {
|
|
24
|
+
level3: {
|
|
25
|
+
level4: {
|
|
26
|
+
level5: {
|
|
27
|
+
value: 42,
|
|
28
|
+
other: "keep",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
deep.remove(obj, "level1.level2.level3.level4.level5.value");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
bench("array index remove", () => {
|
|
39
|
+
const obj = {
|
|
40
|
+
items: [1, 2, 3, 4, 5],
|
|
41
|
+
};
|
|
42
|
+
deep.remove(obj, "items.2");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
bench("keyed array remove", () => {
|
|
46
|
+
const obj = {
|
|
47
|
+
items: [
|
|
48
|
+
{ key: "item1", value: 1 },
|
|
49
|
+
{ key: "item2", value: 2 },
|
|
50
|
+
{ key: "item3", value: 3 },
|
|
51
|
+
{ key: "item4", value: 4 },
|
|
52
|
+
{ key: "item5", value: 5 },
|
|
53
|
+
],
|
|
54
|
+
};
|
|
55
|
+
deep.remove(obj, "items.item3");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
bench("remove from mixed array and object", () => {
|
|
59
|
+
const obj = {
|
|
60
|
+
a: [
|
|
61
|
+
{
|
|
62
|
+
b: [
|
|
63
|
+
{
|
|
64
|
+
c: [{ value: 1 }, { value: 2 }, { value: 3 }],
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
deep.remove(obj, "a.0.b.0.c.1");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
bench("remove from large object", () => {
|
|
74
|
+
const obj: any = {};
|
|
75
|
+
for (let i = 0; i < 1000; i++)
|
|
76
|
+
obj[`key${i}`] = {
|
|
77
|
+
nested: {
|
|
78
|
+
value: i,
|
|
79
|
+
data: {
|
|
80
|
+
id: i,
|
|
81
|
+
name: `name${i}`,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
deep.remove(obj, "key500.nested.data.name");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
bench("remove from very deep path (100 levels)", () => {
|
|
90
|
+
const obj: any = {};
|
|
91
|
+
let current = obj;
|
|
92
|
+
for (let i = 0; i < 100; i++) {
|
|
93
|
+
current.next = { level: i, value: "keep" };
|
|
94
|
+
current = current.next;
|
|
95
|
+
}
|
|
96
|
+
const path = `${new Array(100).fill("next").join(".")}.level`;
|
|
97
|
+
deep.remove(obj, path);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
bench("remove with keys containing periods", () => {
|
|
101
|
+
const obj = {
|
|
102
|
+
channels: [
|
|
103
|
+
{ key: "sensor.temperature.1", reading: 25.5 },
|
|
104
|
+
{ key: "sensor.temperature.2", reading: 26.1 },
|
|
105
|
+
{ key: "sensor.pressure.1", reading: 101.3 },
|
|
106
|
+
],
|
|
107
|
+
};
|
|
108
|
+
deep.remove(obj, "channels.sensor.temperature.1");
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
bench("remove non-existent key (no-op)", () => {
|
|
112
|
+
const obj = {
|
|
113
|
+
a: {
|
|
114
|
+
b: {
|
|
115
|
+
c: 1,
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
deep.remove(obj, "a.b.d");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
bench("remove beyond array bounds (no-op)", () => {
|
|
123
|
+
const obj = { arr: [1, 2, 3] };
|
|
124
|
+
deep.remove(obj, "arr.100");
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
bench("remove with very deep non-existent path (no-op)", () => {
|
|
128
|
+
const obj = { a: { b: 1 } };
|
|
129
|
+
deep.remove(obj, "a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p");
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
bench("remove entire nested object", () => {
|
|
133
|
+
const obj = {
|
|
134
|
+
a: 1,
|
|
135
|
+
b: {
|
|
136
|
+
c: 2,
|
|
137
|
+
d: {
|
|
138
|
+
e: 3,
|
|
139
|
+
f: 4,
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
g: 5,
|
|
143
|
+
};
|
|
144
|
+
deep.remove(obj, "b");
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
bench("repeated remove operations", () => {
|
|
148
|
+
const obj = { a: 1, b: 2, c: 3, d: 4, e: 5 };
|
|
149
|
+
for (let i = 0; i < 5; i++) deep.remove(obj, String.fromCharCode(97 + i));
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
bench("remove from nested arrays", () => {
|
|
153
|
+
const obj = {
|
|
154
|
+
matrix: [
|
|
155
|
+
[1, 2, 3],
|
|
156
|
+
[4, 5, 6],
|
|
157
|
+
[7, 8, 9],
|
|
158
|
+
],
|
|
159
|
+
};
|
|
160
|
+
deep.remove(obj, "matrix.1.1");
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
bench("remove with empty path (no-op)", () => {
|
|
164
|
+
const obj = { a: 1, b: 2 };
|
|
165
|
+
deep.remove(obj, "");
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
bench("remove from array with objects", () => {
|
|
169
|
+
const obj = {
|
|
170
|
+
users: [
|
|
171
|
+
{ id: 1, name: "User1", active: true },
|
|
172
|
+
{ id: 2, name: "User2", active: true },
|
|
173
|
+
{ id: 3, name: "User3", active: false },
|
|
174
|
+
],
|
|
175
|
+
};
|
|
176
|
+
deep.remove(obj, "users.1");
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
bench("remove nested property from keyed array with periods", () => {
|
|
180
|
+
const obj = {
|
|
181
|
+
items: [
|
|
182
|
+
{ key: "item.one", value: { "subitem.one": 3, "subitem.two": 4 } },
|
|
183
|
+
{ key: "item.two", value: 2 },
|
|
184
|
+
],
|
|
185
|
+
};
|
|
186
|
+
deep.remove(obj, "items.item.one.value.subitem.one");
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
bench("batch remove operations on different paths", () => {
|
|
190
|
+
const obj = {
|
|
191
|
+
user: {
|
|
192
|
+
profile: {
|
|
193
|
+
name: "John",
|
|
194
|
+
age: 30,
|
|
195
|
+
email: "john@example.com",
|
|
196
|
+
},
|
|
197
|
+
settings: {
|
|
198
|
+
theme: "dark",
|
|
199
|
+
notifications: true,
|
|
200
|
+
privacy: "public",
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
deep.remove(obj, "user.profile.email");
|
|
205
|
+
deep.remove(obj, "user.settings.privacy");
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
bench("remove from complex nested structure", () => {
|
|
209
|
+
const obj = {
|
|
210
|
+
app: {
|
|
211
|
+
modules: [
|
|
212
|
+
{
|
|
213
|
+
key: "dashboard",
|
|
214
|
+
components: {
|
|
215
|
+
header: { props: { title: "Dashboard", logo: "logo.png" } },
|
|
216
|
+
footer: { props: { copyright: "2025", links: ["home", "about"] } },
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
],
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
deep.remove(obj, "app.modules.dashboard.components.footer.props.links");
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
bench("remove with alternating arrays and objects", () => {
|
|
226
|
+
const obj = {
|
|
227
|
+
data: [
|
|
228
|
+
{
|
|
229
|
+
items: [
|
|
230
|
+
{ key: "a", values: [1, 2, 3] },
|
|
231
|
+
{ key: "b", values: [4, 5, 6] },
|
|
232
|
+
],
|
|
233
|
+
},
|
|
234
|
+
],
|
|
235
|
+
};
|
|
236
|
+
deep.remove(obj, "data.0.items.a.values.1");
|
|
237
|
+
});
|
|
238
|
+
});
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// Copyright 2025 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, it } 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
|
+
c: number[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("remove", () => {
|
|
24
|
+
it("should delete a key", () => {
|
|
25
|
+
const a: TestRecord = {
|
|
26
|
+
a: 1,
|
|
27
|
+
b: {
|
|
28
|
+
c: 2,
|
|
29
|
+
},
|
|
30
|
+
c: [1, 2, 3],
|
|
31
|
+
};
|
|
32
|
+
const cpy = deep.copy(a);
|
|
33
|
+
deep.remove(a, "b.c");
|
|
34
|
+
expect(a).toEqual({ ...cpy, b: {} });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("should delete an array index", () => {
|
|
38
|
+
const a: TestRecord = {
|
|
39
|
+
a: 1,
|
|
40
|
+
b: {
|
|
41
|
+
c: 2,
|
|
42
|
+
},
|
|
43
|
+
c: [1, 2, 3],
|
|
44
|
+
};
|
|
45
|
+
const cpy = deep.copy(a);
|
|
46
|
+
deep.remove(a, "c.1");
|
|
47
|
+
expect(a).toEqual({ ...cpy, c: [1, 3] });
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("should not throw an error when the index is out of bounds", () => {
|
|
51
|
+
const a: TestRecord = {
|
|
52
|
+
a: 1,
|
|
53
|
+
b: {
|
|
54
|
+
c: 2,
|
|
55
|
+
},
|
|
56
|
+
c: [1, 2, 3],
|
|
57
|
+
};
|
|
58
|
+
const cpy = deep.copy(a);
|
|
59
|
+
deep.remove(a, "c.100");
|
|
60
|
+
expect(a).toEqual(cpy);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("should not throw an error when the key doesn't exist", () => {
|
|
64
|
+
const a: TestRecord = {
|
|
65
|
+
a: 1,
|
|
66
|
+
b: {
|
|
67
|
+
c: 2,
|
|
68
|
+
},
|
|
69
|
+
c: [1, 2, 3],
|
|
70
|
+
};
|
|
71
|
+
const cpy = deep.copy(a);
|
|
72
|
+
deep.remove(a, "b.d");
|
|
73
|
+
expect(a).toEqual(cpy);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("should not throw an error when recursion depth exceeds the depth of the object", () => {
|
|
77
|
+
const a: TestRecord = {
|
|
78
|
+
a: 1,
|
|
79
|
+
b: {
|
|
80
|
+
c: 2,
|
|
81
|
+
},
|
|
82
|
+
c: [1, 2, 3],
|
|
83
|
+
};
|
|
84
|
+
const cpy = deep.copy(a);
|
|
85
|
+
deep.remove(a, "b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z");
|
|
86
|
+
expect(a).toEqual(cpy);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("should handle removing from root level", () => {
|
|
90
|
+
const obj = { a: 1, b: 2, c: 3 };
|
|
91
|
+
deep.remove(obj, "b");
|
|
92
|
+
expect(obj).toEqual({ a: 1, c: 3 });
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("should handle removing entire nested object", () => {
|
|
96
|
+
const obj = { a: 1, b: { c: 2, d: 3 }, e: 4 };
|
|
97
|
+
deep.remove(obj, "b");
|
|
98
|
+
expect(obj).toEqual({ a: 1, e: 4 });
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("should handle removing from keyed records in arrays", () => {
|
|
102
|
+
const obj = {
|
|
103
|
+
items: [
|
|
104
|
+
{ key: "item1", value: 1 },
|
|
105
|
+
{ key: "item2", value: 2 },
|
|
106
|
+
],
|
|
107
|
+
};
|
|
108
|
+
deep.remove(obj, "items.item1");
|
|
109
|
+
expect(obj.items).toEqual([{ key: "item2", value: 2 }]);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("should handle empty path", () => {
|
|
113
|
+
const obj = { a: 1, b: 2 };
|
|
114
|
+
deep.remove(obj, "");
|
|
115
|
+
expect(obj).toEqual({ a: 1, b: 2 });
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("should handle removing from nested arrays", () => {
|
|
119
|
+
const obj = {
|
|
120
|
+
a: {
|
|
121
|
+
b: [1, 2, 3],
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
deep.remove(obj, "a.b.1");
|
|
125
|
+
expect(obj.a.b).toEqual([1, 3]);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("should not affect other properties when removing", () => {
|
|
129
|
+
const obj = {
|
|
130
|
+
a: 1,
|
|
131
|
+
b: {
|
|
132
|
+
c: 2,
|
|
133
|
+
d: 3,
|
|
134
|
+
e: 4,
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
deep.remove(obj, "b.d");
|
|
138
|
+
expect(obj).toEqual({
|
|
139
|
+
a: 1,
|
|
140
|
+
b: {
|
|
141
|
+
c: 2,
|
|
142
|
+
e: 4,
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("should handle removing non-existent intermediate paths", () => {
|
|
148
|
+
const obj = { a: { b: 1 } };
|
|
149
|
+
deep.remove(obj, "c.d.e");
|
|
150
|
+
expect(obj).toEqual({ a: { b: 1 } });
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("should handle arrays with objects that don't have keys", () => {
|
|
154
|
+
const obj = {
|
|
155
|
+
items: [{ name: "item1" }, { name: "item2" }],
|
|
156
|
+
};
|
|
157
|
+
deep.remove(obj, "items.0");
|
|
158
|
+
expect(obj.items).toEqual([{ name: "item2" }]);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("should handle removing with keys containing periods", () => {
|
|
162
|
+
const obj = {
|
|
163
|
+
items: [
|
|
164
|
+
{ key: "item.one", value: 1 },
|
|
165
|
+
{ key: "item.two", value: 2 },
|
|
166
|
+
],
|
|
167
|
+
};
|
|
168
|
+
deep.remove(obj, `items.item.one`);
|
|
169
|
+
expect(obj.items).toEqual([{ key: "item.two", value: 2 }]);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("should handle removing with keys that contain periods as intermediate keys", () => {
|
|
173
|
+
const obj = {
|
|
174
|
+
items: [
|
|
175
|
+
{ key: "item.one", value: { "subitem.one": 3 } },
|
|
176
|
+
{ key: "item.two", value: 2 },
|
|
177
|
+
],
|
|
178
|
+
};
|
|
179
|
+
deep.remove(obj, `items.item.one.value.subitem.one`);
|
|
180
|
+
expect(obj.items).toEqual([
|
|
181
|
+
{ key: "item.one", value: {} },
|
|
182
|
+
{ key: "item.two", value: 2 },
|
|
183
|
+
]);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("should handle nested array indexes and keys with periods", () => {
|
|
187
|
+
const obj = {
|
|
188
|
+
items: [
|
|
189
|
+
{
|
|
190
|
+
key: "item.one",
|
|
191
|
+
value: [{ key: "subitem.one", value: { "grandchild.one": 3 } }],
|
|
192
|
+
},
|
|
193
|
+
{ key: "item.two", value: 2 },
|
|
194
|
+
],
|
|
195
|
+
};
|
|
196
|
+
deep.remove(obj, `items.item.one.value.subitem.one.value.grandchild.one`);
|
|
197
|
+
expect(obj.items).toEqual([
|
|
198
|
+
{ key: "item.one", value: [{ key: "subitem.one", value: {} }] },
|
|
199
|
+
{ key: "item.two", value: 2 },
|
|
200
|
+
]);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it("should handle nested array indexes and keys with periods", () => {
|
|
204
|
+
const obj = {
|
|
205
|
+
items: [
|
|
206
|
+
{
|
|
207
|
+
key: "item.one",
|
|
208
|
+
value: [{ key: "subitem.one", value: { "grandchild.one": 3 } }],
|
|
209
|
+
},
|
|
210
|
+
{ key: "item.two", value: 2 },
|
|
211
|
+
],
|
|
212
|
+
};
|
|
213
|
+
deep.remove(obj, `items.item.one.value.subitem.one.value.grandchild.one`);
|
|
214
|
+
expect(obj.items).toEqual([
|
|
215
|
+
{ key: "item.one", value: [{ key: "subitem.one", value: {} }] },
|
|
216
|
+
{ key: "item.two", value: 2 },
|
|
217
|
+
]);
|
|
218
|
+
});
|
|
219
|
+
});
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// Copyright 2025 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 { defaultGetter, SEPARATOR } from "@/deep/path";
|
|
11
|
+
import { type record } from "@/record";
|
|
12
|
+
|
|
13
|
+
export const remove = <V>(obj: V, path: string): void => {
|
|
14
|
+
const parts = path.split(SEPARATOR);
|
|
15
|
+
let result: record.Unknown = obj as record.Unknown;
|
|
16
|
+
|
|
17
|
+
let i = 0;
|
|
18
|
+
while (i < parts.length) {
|
|
19
|
+
if (i === parts.length - 1) {
|
|
20
|
+
// Last part - perform deletion
|
|
21
|
+
const lastPart = parts[i];
|
|
22
|
+
|
|
23
|
+
if (Array.isArray(result)) {
|
|
24
|
+
const index = parseInt(lastPart);
|
|
25
|
+
if (!isNaN(index) && index < result.length) {
|
|
26
|
+
result.splice(index, 1);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const first = result[0];
|
|
31
|
+
if (typeof first === "object" && "key" in first) {
|
|
32
|
+
const objIndex = result.findIndex((o) => o.key === lastPart);
|
|
33
|
+
if (objIndex !== -1) {
|
|
34
|
+
result.splice(objIndex, 1);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
delete result[lastPart];
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Not the last part - navigate deeper
|
|
46
|
+
let found = false;
|
|
47
|
+
|
|
48
|
+
// First try to match with keyed items in arrays
|
|
49
|
+
if (Array.isArray(result) && result.length > 0) {
|
|
50
|
+
const first = result[0];
|
|
51
|
+
if (typeof first === "object" && "key" in first)
|
|
52
|
+
// Try to find an item with a matching key (considering periods in keys)
|
|
53
|
+
for (let j = parts.length - i; j >= 1; j--) {
|
|
54
|
+
const candidateKey = parts.slice(i, i + j).join(SEPARATOR);
|
|
55
|
+
const item = result.find((o) => o.key === candidateKey);
|
|
56
|
+
if (item != null) {
|
|
57
|
+
if (i + j === parts.length) {
|
|
58
|
+
// This is the item to remove
|
|
59
|
+
const objIndex = result.findIndex((o) => o.key === candidateKey);
|
|
60
|
+
if (objIndex !== -1) result.splice(objIndex, 1);
|
|
61
|
+
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
result = item as record.Unknown;
|
|
65
|
+
i += j;
|
|
66
|
+
found = true;
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!found)
|
|
73
|
+
// Try to match properties with periods in objects
|
|
74
|
+
for (let j = parts.length - i; j >= 1; j--) {
|
|
75
|
+
const candidateKey = parts.slice(i, i + j).join(SEPARATOR);
|
|
76
|
+
if (
|
|
77
|
+
!Array.isArray(result) &&
|
|
78
|
+
typeof result === "object" &&
|
|
79
|
+
result !== null &&
|
|
80
|
+
candidateKey in result
|
|
81
|
+
) {
|
|
82
|
+
if (i + j === parts.length) {
|
|
83
|
+
// This is the property to remove
|
|
84
|
+
delete result[candidateKey];
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
result = result[candidateKey] as record.Unknown;
|
|
88
|
+
i += j;
|
|
89
|
+
found = true;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (!found) {
|
|
95
|
+
// Try normal property access
|
|
96
|
+
const next = defaultGetter(result, parts[i]);
|
|
97
|
+
if (next == null) return;
|
|
98
|
+
result = next as record.Unknown;
|
|
99
|
+
i++;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|