@eventvisor/sdk 0.0.2

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.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +9 -0
  3. package/dist/attributesManager.d.ts +36 -0
  4. package/dist/bucketer.d.ts +30 -0
  5. package/dist/compareVersions.d.ts +4 -0
  6. package/dist/conditions.d.ts +20 -0
  7. package/dist/datafileReader.d.ts +29 -0
  8. package/dist/effectsManager.d.ts +33 -0
  9. package/dist/emitter.d.ts +11 -0
  10. package/dist/index.d.ts +12 -0
  11. package/dist/index.js +2 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/index.mjs +2 -0
  14. package/dist/index.mjs.gz +0 -0
  15. package/dist/index.mjs.map +1 -0
  16. package/dist/instance.d.ts +67 -0
  17. package/dist/logger.d.ts +26 -0
  18. package/dist/modulesManager.d.ts +67 -0
  19. package/dist/murmurhash.d.ts +1 -0
  20. package/dist/persister.d.ts +40 -0
  21. package/dist/sourceResolver.d.ts +31 -0
  22. package/dist/transformer.d.ts +21 -0
  23. package/dist/validator.d.ts +28 -0
  24. package/jest.config.js +6 -0
  25. package/lib/attributesManager.d.ts +36 -0
  26. package/lib/bucketer.d.ts +30 -0
  27. package/lib/compareVersions.d.ts +4 -0
  28. package/lib/conditions.d.ts +20 -0
  29. package/lib/datafileReader.d.ts +29 -0
  30. package/lib/effectsManager.d.ts +33 -0
  31. package/lib/emitter.d.ts +11 -0
  32. package/lib/index.d.ts +12 -0
  33. package/lib/instance.d.ts +67 -0
  34. package/lib/logger.d.ts +26 -0
  35. package/lib/modulesManager.d.ts +67 -0
  36. package/lib/murmurhash.d.ts +1 -0
  37. package/lib/persister.d.ts +40 -0
  38. package/lib/sourceResolver.d.ts +31 -0
  39. package/lib/transformer.d.ts +21 -0
  40. package/lib/validator.d.ts +28 -0
  41. package/package.json +45 -0
  42. package/src/attributesManager.ts +181 -0
  43. package/src/bucketer.spec.ts +156 -0
  44. package/src/bucketer.ts +152 -0
  45. package/src/compareVersions.ts +93 -0
  46. package/src/conditions.ts +224 -0
  47. package/src/datafileReader.ts +133 -0
  48. package/src/effectsManager.ts +214 -0
  49. package/src/emitter.ts +64 -0
  50. package/src/index.spec.ts +5 -0
  51. package/src/index.ts +14 -0
  52. package/src/instance.spec.ts +184 -0
  53. package/src/instance.ts +608 -0
  54. package/src/logger.ts +90 -0
  55. package/src/modulesManager.ts +276 -0
  56. package/src/murmurhash.ts +71 -0
  57. package/src/persister.ts +162 -0
  58. package/src/sourceResolver.spec.ts +253 -0
  59. package/src/sourceResolver.ts +213 -0
  60. package/src/transformer.ts +316 -0
  61. package/src/transformer_static.spec.ts +377 -0
  62. package/src/transformer_types.spec.ts +820 -0
  63. package/src/validator.spec.ts +579 -0
  64. package/src/validator.ts +366 -0
  65. package/tsconfig.cjs.json +8 -0
  66. package/tsconfig.esm.json +8 -0
  67. package/webpack.config.js +80 -0
@@ -0,0 +1,316 @@
1
+ import type { Value, Transform, Inputs } from "@eventvisor/types";
2
+
3
+ import type { Logger } from "./logger";
4
+ import type { ConditionsChecker } from "./conditions";
5
+ import type { SourceResolver } from "./sourceResolver";
6
+
7
+ export type GetTransformer = () => Transformer;
8
+
9
+ export interface TransformerOptions {
10
+ logger: Logger;
11
+ conditionsChecker: ConditionsChecker;
12
+ sourceResolver: SourceResolver;
13
+ }
14
+
15
+ export class Transformer {
16
+ private logger: Logger;
17
+ private conditionsChecker: ConditionsChecker;
18
+ private sourceResolver: SourceResolver;
19
+
20
+ constructor(options: TransformerOptions) {
21
+ this.logger = options.logger;
22
+ this.conditionsChecker = options.conditionsChecker;
23
+ this.sourceResolver = options.sourceResolver;
24
+ }
25
+
26
+ async applyAll(value: Value, transforms: Transform[], inputs: Inputs = {}): Promise<Value> {
27
+ let result = value;
28
+
29
+ for (const transform of transforms) {
30
+ /**
31
+ * Conditions
32
+ */
33
+ if (transform.conditions) {
34
+ const conditionMatched = await this.conditionsChecker.allAreMatched(
35
+ transform.conditions,
36
+ inputs,
37
+ );
38
+
39
+ if (!conditionMatched) {
40
+ continue;
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Source value
46
+ */
47
+ let sourceValue = await this.sourceResolver.resolve(transform, inputs);
48
+
49
+ // when Transform has no source, but only target
50
+ if (sourceValue === null || sourceValue === undefined) {
51
+ if (transform.target) {
52
+ sourceValue = await this.sourceResolver.resolve(
53
+ {
54
+ payload: transform.target,
55
+ },
56
+ typeof inputs.payload === "undefined"
57
+ ? {
58
+ ...inputs,
59
+ payload: value,
60
+ }
61
+ : inputs,
62
+ );
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Transform value
68
+ */
69
+ // plain target
70
+ if (transform.target) {
71
+ // @TODO: target is always single string. tidy it up
72
+ const targets = Array.isArray(transform.target) ? transform.target : [transform.target];
73
+
74
+ for (const target of targets) {
75
+ // @TODO: use if/elseif below later
76
+
77
+ // string only
78
+ if (typeof sourceValue === "string") {
79
+ if (transform.type === "trim") {
80
+ result = Transformer.setValueAtPath(result, target, sourceValue.trim());
81
+ }
82
+ }
83
+
84
+ // array only
85
+ if (Array.isArray(sourceValue)) {
86
+ if (transform.type === "concat") {
87
+ const separator = transform.separator || " ";
88
+ result = Transformer.setValueAtPath(result, target, sourceValue.join(separator));
89
+ }
90
+ }
91
+
92
+ // others
93
+ if (transform.type === "set") {
94
+ if ("value" in transform) {
95
+ result = Transformer.setValueAtPath(result, target, transform.value);
96
+ } else {
97
+ result = Transformer.setValueAtPath(result, target, sourceValue);
98
+ }
99
+ }
100
+
101
+ if (transform.type === "remove") {
102
+ result = Transformer.removeValueAt(result, target);
103
+ }
104
+
105
+ // to other types
106
+ if (transform.type === "toInteger") {
107
+ result = Transformer.setValueAtPath(result, target, parseInt(String(sourceValue)));
108
+ } else if (transform.type === "toDouble") {
109
+ result = Transformer.setValueAtPath(result, target, parseFloat(String(sourceValue)));
110
+ } else if (transform.type === "toString") {
111
+ result = Transformer.setValueAtPath(result, target, String(sourceValue) || "");
112
+ } else if (transform.type === "toBoolean") {
113
+ const lowerCasedValue = String(sourceValue).toLowerCase();
114
+
115
+ result = Transformer.setValueAtPath(
116
+ result,
117
+ target,
118
+ ["true", "1", "checked", "yes", "on", "y"].indexOf(lowerCasedValue) !== -1 ||
119
+ sourceValue === true,
120
+ );
121
+ }
122
+ }
123
+ } else {
124
+ // without target (meaning, self)
125
+
126
+ // set
127
+ if (transform.type === "set") {
128
+ if ("value" in transform) {
129
+ result = transform.value;
130
+ }
131
+ }
132
+ }
133
+
134
+ if (transform.type === "spread") {
135
+ if (transform.target) {
136
+ const currentTargetValue = Transformer.getValueAtPath(result, transform.target);
137
+ result = Transformer.setValueAtPath(result, transform.target, {
138
+ ...((currentTargetValue as object) || {}),
139
+ ...((sourceValue as object) || {}),
140
+ });
141
+ } else {
142
+ result = {
143
+ ...((result as object) || {}),
144
+ ...((sourceValue as object) || {}),
145
+ };
146
+ }
147
+ }
148
+
149
+ // mathematical
150
+ if (transform.type === "increment") {
151
+ const by = typeof transform.value === "number" ? transform.value : 1;
152
+
153
+ if (transform.target) {
154
+ result = Transformer.setValueAtPath(result, transform.target, Number(sourceValue) + by);
155
+ } else {
156
+ result = (result as number) + by;
157
+ }
158
+ } else if (transform.type === "decrement") {
159
+ const by = typeof transform.value === "number" ? transform.value : 1;
160
+
161
+ if (transform.target) {
162
+ result = Transformer.setValueAtPath(result, transform.target, Number(sourceValue) - by);
163
+ } else {
164
+ result = (result as number) - by;
165
+ }
166
+ }
167
+
168
+ // target map
169
+ if (transform.targetMap) {
170
+ const targetMaps: Record<string, string>[] = [];
171
+
172
+ // @TODO: tidy it up
173
+ if (Array.isArray(transform.targetMap)) {
174
+ for (const targetMap of transform.targetMap) {
175
+ Object.entries(targetMap).forEach(([key, value]) => {
176
+ targetMaps.push({ [key]: value });
177
+ });
178
+ }
179
+ } else {
180
+ Object.entries(transform.targetMap).forEach(([key, value]) => {
181
+ targetMaps.push({ [key]: value });
182
+ });
183
+ }
184
+
185
+ for (const targetMap of targetMaps) {
186
+ // rename
187
+ if (transform.type === "rename") {
188
+ result = Transformer.renameValueAt(result, targetMap as Record<string, string>);
189
+ }
190
+ }
191
+ }
192
+ }
193
+
194
+ return result;
195
+ }
196
+
197
+ // Helper function to rename value at path
198
+ public static renameValueAt(obj: any, target: Record<string, string>): any {
199
+ if (!obj || typeof obj !== "object") return obj;
200
+ if (!target || typeof target !== "object") return obj;
201
+
202
+ const entries = Object.entries(target);
203
+ if (entries.length === 0) return obj;
204
+
205
+ const [oldKey, newKey] = entries[0];
206
+ if (!oldKey || !newKey) return obj;
207
+
208
+ // Get the value at old path
209
+ const oldValue = Transformer.getValueAtPath(obj, oldKey);
210
+ if (oldValue === undefined) return obj;
211
+
212
+ // Create a copy to avoid mutating the original
213
+ const result = JSON.parse(JSON.stringify(obj));
214
+
215
+ // Remove old property
216
+ const oldKeys = oldKey.split(".");
217
+ let current = result;
218
+
219
+ // Navigate to parent of old property
220
+ for (let i = 0; i < oldKeys.length - 1; i++) {
221
+ const key = oldKeys[i];
222
+ if (current[key] === undefined) return result;
223
+ current = current[key];
224
+ }
225
+
226
+ // Remove old property
227
+ delete current[oldKeys[oldKeys.length - 1]];
228
+
229
+ // Set at new path
230
+ return Transformer.setValueAtPath(result, newKey, oldValue);
231
+ }
232
+
233
+ // Helper function to get value at path
234
+ public static getValueAtPath(obj: any, path: string): any {
235
+ if (!path || typeof path !== "string") return undefined;
236
+
237
+ const keys = path.split(".");
238
+ let current = obj;
239
+
240
+ for (const key of keys) {
241
+ if (current === null || current === undefined) return undefined;
242
+ if (typeof current === "object" && !Array.isArray(current)) {
243
+ current = (current as any)[key];
244
+ } else if (Array.isArray(current) && /^\d+$/.test(key)) {
245
+ current = current[parseInt(key)];
246
+ } else {
247
+ return undefined;
248
+ }
249
+ }
250
+
251
+ return current;
252
+ }
253
+
254
+ // Helper function to set value at path
255
+ public static setValueAtPath(obj: any, path: string, value: any): any {
256
+ if (!path || typeof path !== "string") return obj;
257
+ if (obj === null || obj === undefined) return obj;
258
+
259
+ // Create a copy to avoid mutating the original
260
+ const result = JSON.parse(JSON.stringify(obj));
261
+ const keys = path.split(".");
262
+ let current = result as any;
263
+
264
+ // Navigate to the parent of the target path
265
+ for (let i = 0; i < keys.length - 1; i++) {
266
+ const key = keys[i];
267
+ if (current[key] === undefined) {
268
+ // Create nested object or array as needed
269
+ if (/^\d+$/.test(keys[i + 1])) {
270
+ current[key] = [];
271
+ } else {
272
+ current[key] = {};
273
+ }
274
+ }
275
+ current = current[key];
276
+ }
277
+
278
+ // Set the final value
279
+ const finalKey = keys[keys.length - 1];
280
+ current[finalKey] = value;
281
+
282
+ return result;
283
+ }
284
+
285
+ // Helper function to remove value at path
286
+ public static removeValueAt(obj: any, path: string): any {
287
+ if (!path || typeof path !== "string") return obj;
288
+ if (obj === null || obj === undefined) return obj;
289
+
290
+ // Create a copy to avoid mutating the original
291
+ const result = JSON.parse(JSON.stringify(obj));
292
+ const keys = path.split(".");
293
+ let current = result as any;
294
+
295
+ // Navigate to the parent of the target path
296
+ for (let i = 0; i < keys.length - 1; i++) {
297
+ const key = keys[i];
298
+ if (current[key] === undefined) {
299
+ return result; // Path doesn't exist, nothing to remove
300
+ }
301
+ current = current[key];
302
+ }
303
+
304
+ // Remove the property
305
+ const finalKey = keys[keys.length - 1];
306
+ if (Array.isArray(current)) {
307
+ if (/^\d+$/.test(finalKey)) {
308
+ current.splice(parseInt(finalKey), 1);
309
+ }
310
+ } else if (typeof current === "object" && current !== null) {
311
+ delete current[finalKey];
312
+ }
313
+
314
+ return result;
315
+ }
316
+ }
@@ -0,0 +1,377 @@
1
+ import { Transformer } from "./transformer";
2
+
3
+ describe("Transformer static methods", () => {
4
+ describe("getValueAtPath", () => {
5
+ const testObj = {
6
+ user: {
7
+ profile: {
8
+ name: "John Doe",
9
+ email: "john@example.com",
10
+ settings: {
11
+ theme: "dark",
12
+ notifications: true,
13
+ },
14
+ },
15
+ preferences: ["reading", "music"],
16
+ },
17
+ config: {
18
+ version: "1.0.0",
19
+ features: ["auth", "api"],
20
+ },
21
+ };
22
+
23
+ it("should get value at simple path", () => {
24
+ const result = Transformer.getValueAtPath(testObj, "user.profile.name");
25
+ expect(result).toBe("John Doe");
26
+ });
27
+
28
+ it("should get value at nested path", () => {
29
+ const result = Transformer.getValueAtPath(testObj, "user.profile.settings.theme");
30
+ expect(result).toBe("dark");
31
+ });
32
+
33
+ it("should get value at root level", () => {
34
+ const result = Transformer.getValueAtPath(testObj, "config");
35
+ expect(result).toEqual({
36
+ version: "1.0.0",
37
+ features: ["auth", "api"],
38
+ });
39
+ });
40
+
41
+ it("should get array value", () => {
42
+ const result = Transformer.getValueAtPath(testObj, "user.preferences");
43
+ expect(result).toEqual(["reading", "music"]);
44
+ });
45
+
46
+ it("should get array element by index", () => {
47
+ const result = Transformer.getValueAtPath(testObj, "user.preferences.0");
48
+ expect(result).toBe("reading");
49
+ });
50
+
51
+ it("should get array element by numeric string", () => {
52
+ const result = Transformer.getValueAtPath(testObj, "user.preferences.1");
53
+ expect(result).toBe("music");
54
+ });
55
+
56
+ it("should return undefined for non-existent path", () => {
57
+ const result = Transformer.getValueAtPath(testObj, "user.profile.age");
58
+ expect(result).toBeUndefined();
59
+ });
60
+
61
+ it("should return undefined for invalid path", () => {
62
+ const result = Transformer.getValueAtPath(testObj, "");
63
+ expect(result).toBeUndefined();
64
+ });
65
+
66
+ it("should return undefined for null path", () => {
67
+ const result = Transformer.getValueAtPath(testObj, null as any);
68
+ expect(result).toBeUndefined();
69
+ });
70
+
71
+ it("should return undefined for non-string path", () => {
72
+ const result = Transformer.getValueAtPath(testObj, 123 as any);
73
+ expect(result).toBeUndefined();
74
+ });
75
+
76
+ it("should return undefined for non-existent array index", () => {
77
+ const result = Transformer.getValueAtPath(testObj, "user.preferences.5");
78
+ expect(result).toBeUndefined();
79
+ });
80
+
81
+ it("should return undefined for invalid array index", () => {
82
+ const result = Transformer.getValueAtPath(testObj, "user.preferences.abc");
83
+ expect(result).toBeUndefined();
84
+ });
85
+
86
+ it("should handle null object", () => {
87
+ const result = Transformer.getValueAtPath(null, "user.profile.name");
88
+ expect(result).toBeUndefined();
89
+ });
90
+
91
+ it("should handle undefined object", () => {
92
+ const result = Transformer.getValueAtPath(undefined, "user.profile.name");
93
+ expect(result).toBeUndefined();
94
+ });
95
+ });
96
+
97
+ describe("setValueAtPath", () => {
98
+ let testObj: any;
99
+
100
+ beforeEach(() => {
101
+ testObj = {
102
+ user: {
103
+ profile: {
104
+ name: "John Doe",
105
+ email: "john@example.com",
106
+ },
107
+ preferences: ["reading", "music"],
108
+ },
109
+ config: {
110
+ version: "1.0.0",
111
+ },
112
+ };
113
+ });
114
+
115
+ it("should set value at simple path", () => {
116
+ const result = Transformer.setValueAtPath(testObj, "user.profile.age", 30);
117
+ expect(result.user.profile.age).toBe(30);
118
+ });
119
+
120
+ it("should set value at nested path", () => {
121
+ const result = Transformer.setValueAtPath(testObj, "user.profile.settings.theme", "light");
122
+ expect(result.user.profile.settings.theme).toBe("light");
123
+ });
124
+
125
+ it("should set value at root level", () => {
126
+ const result = Transformer.setValueAtPath(testObj, "appName", "MyApp");
127
+ expect(result.appName).toBe("MyApp");
128
+ });
129
+
130
+ it("should set array element by index", () => {
131
+ const result = Transformer.setValueAtPath(testObj, "user.preferences.0", "gaming");
132
+ expect(result.user.preferences[0]).toBe("gaming");
133
+ });
134
+
135
+ it("should create nested objects when they don't exist", () => {
136
+ const result = Transformer.setValueAtPath(testObj, "user.profile.address.city", "New York");
137
+ expect(result.user.profile.address.city).toBe("New York");
138
+ });
139
+
140
+ it("should create arrays when numeric keys are expected", () => {
141
+ const result = Transformer.setValueAtPath(testObj, "user.scores.0", 100);
142
+ expect(Array.isArray(result.user.scores)).toBe(true);
143
+ expect(result.user.scores[0]).toBe(100);
144
+ });
145
+
146
+ it("should handle empty path", () => {
147
+ const result = Transformer.setValueAtPath(testObj, "", "value");
148
+ expect(result).toBe(testObj);
149
+ });
150
+
151
+ it("should handle null path", () => {
152
+ const result = Transformer.setValueAtPath(testObj, null as any, "value");
153
+ expect(result).toBe(testObj);
154
+ });
155
+
156
+ it("should handle non-string path", () => {
157
+ const result = Transformer.setValueAtPath(testObj, 123 as any, "value");
158
+ expect(result).toBe(testObj);
159
+ });
160
+
161
+ it("should handle null object", () => {
162
+ const result = Transformer.setValueAtPath(null, "user.profile.name", "Jane");
163
+ expect(result).toBe(null);
164
+ });
165
+
166
+ it("should handle undefined object", () => {
167
+ const result = Transformer.setValueAtPath(undefined, "user.profile.name", "Jane");
168
+ expect(result).toBe(undefined);
169
+ });
170
+
171
+ it("should not mutate original object", () => {
172
+ const original = JSON.parse(JSON.stringify(testObj));
173
+ Transformer.setValueAtPath(testObj, "user.profile.age", 30);
174
+ expect(testObj).toEqual(original);
175
+ });
176
+ });
177
+
178
+ describe("removeValueAt", () => {
179
+ let testObj: any;
180
+
181
+ beforeEach(() => {
182
+ testObj = {
183
+ user: {
184
+ profile: {
185
+ name: "John Doe",
186
+ email: "john@example.com",
187
+ settings: {
188
+ theme: "dark",
189
+ notifications: true,
190
+ },
191
+ },
192
+ preferences: ["reading", "music", "gaming"],
193
+ },
194
+ config: {
195
+ version: "1.0.0",
196
+ },
197
+ };
198
+ });
199
+
200
+ it("should remove value at simple path", () => {
201
+ const result = Transformer.removeValueAt(testObj, "user.profile.email");
202
+ expect(result.user.profile.email).toBeUndefined();
203
+ expect(result.user.profile.name).toBe("John Doe"); // Other properties should remain
204
+ });
205
+
206
+ it("should remove value at nested path", () => {
207
+ const result = Transformer.removeValueAt(testObj, "user.profile.settings.theme");
208
+ expect(result.user.profile.settings.theme).toBeUndefined();
209
+ expect(result.user.profile.settings.notifications).toBe(true); // Other properties should remain
210
+ });
211
+
212
+ it("should remove value at root level", () => {
213
+ const result = Transformer.removeValueAt(testObj, "config");
214
+ expect(result.config).toBeUndefined();
215
+ expect(result.user).toBeDefined(); // Other properties should remain
216
+ });
217
+
218
+ it("should remove array element by index", () => {
219
+ const result = Transformer.removeValueAt(testObj, "user.preferences.1");
220
+ expect(result.user.preferences).toEqual(["reading", "gaming"]);
221
+ expect(result.user.preferences.length).toBe(2);
222
+ });
223
+
224
+ it("should handle non-existent path gracefully", () => {
225
+ const result = Transformer.removeValueAt(testObj, "user.profile.age");
226
+ expect(result).toEqual(testObj); // Should return original object unchanged
227
+ });
228
+
229
+ it("should handle empty path", () => {
230
+ const result = Transformer.removeValueAt(testObj, "");
231
+ expect(result).toEqual(testObj);
232
+ });
233
+
234
+ it("should handle null path", () => {
235
+ const result = Transformer.removeValueAt(testObj, null as any);
236
+ expect(result).toEqual(testObj);
237
+ });
238
+
239
+ it("should handle non-string path", () => {
240
+ const result = Transformer.removeValueAt(testObj, 123 as any);
241
+ expect(result).toEqual(testObj);
242
+ });
243
+
244
+ it("should handle null object", () => {
245
+ const result = Transformer.removeValueAt(null, "user.profile.name");
246
+ expect(result).toBe(null);
247
+ });
248
+
249
+ it("should handle undefined object", () => {
250
+ const result = Transformer.removeValueAt(undefined, "user.profile.name");
251
+ expect(result).toBe(undefined);
252
+ });
253
+
254
+ it("should not mutate original object", () => {
255
+ const original = JSON.parse(JSON.stringify(testObj));
256
+ Transformer.removeValueAt(testObj, "user.profile.email");
257
+ expect(testObj).toEqual(original);
258
+ });
259
+ });
260
+
261
+ describe("renameValueAt", () => {
262
+ let testObj: any;
263
+
264
+ beforeEach(() => {
265
+ testObj = {
266
+ user: {
267
+ profile: {
268
+ name: "John Doe",
269
+ email: "john@example.com",
270
+ settings: {
271
+ theme: "dark",
272
+ notifications: true,
273
+ },
274
+ },
275
+ preferences: ["reading", "music"],
276
+ },
277
+ config: {
278
+ version: "1.0.0",
279
+ },
280
+ };
281
+ });
282
+
283
+ it("should rename single property", () => {
284
+ const result = Transformer.renameValueAt(
285
+ {
286
+ name: "John Doe",
287
+ },
288
+ { name: "fullName" },
289
+ );
290
+ expect(result.fullName).toBe("John Doe");
291
+ expect(result.name).toBeUndefined();
292
+ });
293
+
294
+ it("should rename simple property", () => {
295
+ const result = Transformer.renameValueAt(testObj, {
296
+ "user.profile.name": "user.profile.fullName",
297
+ });
298
+ expect(result.user.profile.fullName).toBe("John Doe");
299
+ expect(result.user.profile.name).toBeUndefined();
300
+ });
301
+
302
+ it("should rename nested property", () => {
303
+ const result = Transformer.renameValueAt(testObj, {
304
+ "user.profile.email": "user.profile.userEmail",
305
+ });
306
+ expect(result.user.profile.userEmail).toBe("john@example.com");
307
+ expect(result.user.profile.email).toBeUndefined();
308
+ });
309
+
310
+ it("should rename to nested path", () => {
311
+ const result = Transformer.renameValueAt(testObj, {
312
+ "user.profile.name": "user.profile.fullName",
313
+ });
314
+ expect(result.user.profile.fullName).toBe("John Doe");
315
+ expect(result.user.profile.name).toBeUndefined();
316
+ });
317
+
318
+ it("should rename root property", () => {
319
+ const result = Transformer.renameValueAt(testObj, { config: "configuration" });
320
+ expect(result.configuration.version).toBe("1.0.0");
321
+ expect(result.config).toBeUndefined();
322
+ });
323
+
324
+ it("should handle non-existent source path gracefully", () => {
325
+ const result = Transformer.renameValueAt(testObj, { "user.profile.age": "userAge" });
326
+ expect(result).toEqual(testObj); // Should return original object unchanged
327
+ });
328
+
329
+ it("should handle empty target object", () => {
330
+ const result = Transformer.renameValueAt(testObj, {});
331
+ expect(result).toEqual(testObj);
332
+ });
333
+
334
+ it("should handle null target object", () => {
335
+ const result = Transformer.renameValueAt(testObj, null as any);
336
+ expect(result).toEqual(testObj);
337
+ });
338
+
339
+ it("should handle null object", () => {
340
+ const result = Transformer.renameValueAt(null, { name: "fullName" });
341
+ expect(result).toBe(null);
342
+ });
343
+
344
+ it("should handle undefined object", () => {
345
+ const result = Transformer.renameValueAt(undefined, { name: "fullName" });
346
+ expect(result).toBe(undefined);
347
+ });
348
+
349
+ it("should handle non-object", () => {
350
+ const result = Transformer.renameValueAt("string", { name: "fullName" });
351
+ expect(result).toBe("string");
352
+ });
353
+
354
+ it("should not mutate original object", () => {
355
+ const original = JSON.parse(JSON.stringify(testObj));
356
+ Transformer.renameValueAt(testObj, { "user.profile.name": "user.profile.fullName" });
357
+ expect(testObj).toEqual(original);
358
+ });
359
+
360
+ it("should handle complex nested rename", () => {
361
+ const result = Transformer.renameValueAt(testObj, {
362
+ "user.profile.settings.theme": "user.profile.settings.colorScheme",
363
+ });
364
+ expect(result.user.profile.settings.colorScheme).toBe("dark");
365
+ expect(result.user.profile.settings.theme).toBeUndefined();
366
+ });
367
+
368
+ it("should handle rename with array path", () => {
369
+ const result = Transformer.renameValueAt(testObj, {
370
+ "user.preferences.0": "user.preferences.1",
371
+ });
372
+ expect(result.user.preferences[1]).toBe("reading");
373
+ expect(result.user.preferences.length).toBe(2); // Array length extends to accommodate index 1
374
+ // Note: Array manipulation during rename can be complex due to splice operations
375
+ });
376
+ });
377
+ });