@coast/core-utils 0.0.25 → 0.0.29

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 (46) hide show
  1. package/dist/AssertionUtils.d.ts +0 -9
  2. package/dist/AssertionUtils.js +0 -16
  3. package/dist/AssertionUtils.js.map +1 -1
  4. package/dist/CompactSet.d.ts +5 -0
  5. package/dist/CompactSet.js +21 -0
  6. package/dist/CompactSet.js.map +1 -0
  7. package/dist/EntityRehydrator.d.ts +12 -0
  8. package/dist/EntityRehydrator.js +30 -0
  9. package/dist/EntityRehydrator.js.map +1 -0
  10. package/dist/EntityUtils.d.ts +11 -0
  11. package/dist/EntityUtils.js +24 -0
  12. package/dist/EntityUtils.js.map +1 -0
  13. package/dist/ISODateStringUtils.d.ts +5 -0
  14. package/dist/ISODateStringUtils.js +16 -0
  15. package/dist/ISODateStringUtils.js.map +1 -0
  16. package/dist/JsonPath.d.ts +8 -0
  17. package/dist/JsonPath.js +38 -0
  18. package/dist/JsonPath.js.map +1 -0
  19. package/dist/test/AssertionUtilsTest.js +0 -41
  20. package/dist/test/AssertionUtilsTest.js.map +1 -1
  21. package/dist/test/CompactSetTest.d.ts +1 -0
  22. package/dist/test/CompactSetTest.js +95 -0
  23. package/dist/test/CompactSetTest.js.map +1 -0
  24. package/dist/test/EntityRehydratorTest.d.ts +1 -0
  25. package/dist/test/EntityRehydratorTest.js +48 -0
  26. package/dist/test/EntityRehydratorTest.js.map +1 -0
  27. package/dist/test/EntityUtilsTest.d.ts +1 -0
  28. package/dist/test/EntityUtilsTest.js +87 -0
  29. package/dist/test/EntityUtilsTest.js.map +1 -0
  30. package/dist/test/JsonPathTest.d.ts +1 -0
  31. package/dist/test/JsonPathTest.js +419 -0
  32. package/dist/test/JsonPathTest.js.map +1 -0
  33. package/jest.config.js +1 -0
  34. package/package.json +5 -7
  35. package/src/AssertionUtils.ts +0 -33
  36. package/src/CompactSet.ts +31 -0
  37. package/src/EntityRehydrator.ts +35 -0
  38. package/src/EntityUtils.ts +37 -0
  39. package/src/ISODateStringUtils.ts +24 -0
  40. package/src/JsonPath.ts +58 -0
  41. package/src/test/AssertionUtilsTest.ts +0 -48
  42. package/src/test/CompactSetTest.ts +106 -0
  43. package/src/test/EntityRehydratorTest.ts +74 -0
  44. package/src/test/EntityUtilsTest.ts +97 -0
  45. package/src/test/JsonPathTest.ts +516 -0
  46. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,516 @@
1
+ import { safeExpect } from '@paradoxical-io/common-test';
2
+
3
+ import { JsonPath } from '../JsonPath';
4
+
5
+ const TEST_PATHS = {
6
+ FOO_BAR_BAZ: 'foo.bar.baz',
7
+ FOO_BAR_BAZ_FORMATTED: '$.foo.bar.baz',
8
+ FOO_BAR_FORMATTED: '$.foo.bar',
9
+ FOO_FORMATTED: '$.foo',
10
+ FOO: 'foo',
11
+ BAR_BAZ_HYPHENATED: 'bar-baz',
12
+ FOO_BAR_BAZ_HYPHENATED: 'foo.bar-baz',
13
+ BAR_BAZ_HYPHENATED_FORMATTED: "$.foo['bar-baz']",
14
+ BAR_BAZ_QUX_FORMATTED: "$.foo['bar-baz'].qux",
15
+ FOO_BAR_BAZ_QUX_FORMATTED: '$.foo.bar.baz.qux',
16
+ SIMPLE_FOO_BAR: 'foo.bar',
17
+ } as const;
18
+
19
+ describe('JsonPath', () => {
20
+ describe('constructor', () => {
21
+ it('creates a JsonPath with a given value', () => {
22
+ const path = new JsonPath(TEST_PATHS.FOO_BAR_FORMATTED);
23
+ safeExpect(path.value).toEqual(TEST_PATHS.FOO_BAR_FORMATTED);
24
+ });
25
+ });
26
+
27
+ describe('fromString', () => {
28
+ describe('simple paths', () => {
29
+ it('converts a simple dot-notation path', () => {
30
+ const path = JsonPath.fromString(TEST_PATHS.FOO_BAR_BAZ);
31
+ safeExpect(path.value).toEqual(TEST_PATHS.FOO_BAR_BAZ_FORMATTED);
32
+ });
33
+
34
+ it('converts a single segment path', () => {
35
+ const path = JsonPath.fromString(TEST_PATHS.FOO);
36
+ safeExpect(path.value).toEqual(TEST_PATHS.FOO_FORMATTED);
37
+ });
38
+
39
+ it('converts a two segment path', () => {
40
+ const path = JsonPath.fromString(TEST_PATHS.SIMPLE_FOO_BAR);
41
+ safeExpect(path.value).toEqual(TEST_PATHS.FOO_BAR_FORMATTED);
42
+ });
43
+
44
+ it('returns the path unchanged if it already starts with $', () => {
45
+ const path = JsonPath.fromString(TEST_PATHS.FOO_BAR_FORMATTED);
46
+ safeExpect(path.value).toEqual(TEST_PATHS.FOO_BAR_FORMATTED);
47
+ });
48
+
49
+ it('handles paths with underscores', () => {
50
+ const path = JsonPath.fromString('foo_bar.baz_qux');
51
+ safeExpect(path.value).toEqual('$.foo_bar.baz_qux');
52
+ });
53
+
54
+ it('handles paths with mixed case', () => {
55
+ const path = JsonPath.fromString('FooBar.BazQux');
56
+ safeExpect(path.value).toEqual('$.FooBar.BazQux');
57
+ });
58
+ });
59
+
60
+ describe('numeric segments', () => {
61
+ it('uses bracket notation for segments starting with numbers', () => {
62
+ const path = JsonPath.fromString('foo.123bar');
63
+ safeExpect(path.value).toEqual("$.foo['123bar']");
64
+ });
65
+
66
+ it('uses bracket notation for purely numeric segments', () => {
67
+ const path = JsonPath.fromString('foo.123');
68
+ safeExpect(path.value).toEqual("$.foo['123']");
69
+ });
70
+
71
+ it('uses bracket notation for array index notation', () => {
72
+ const path = JsonPath.fromString('foo.0.bar');
73
+ safeExpect(path.value).toEqual("$.foo['0'].bar");
74
+ });
75
+
76
+ it('handles multiple numeric segments', () => {
77
+ const path = JsonPath.fromString('foo.123.456.bar');
78
+ safeExpect(path.value).toEqual("$.foo['123']['456'].bar");
79
+ });
80
+ });
81
+
82
+ describe('special characters', () => {
83
+ it('uses bracket notation for segments with hyphens', () => {
84
+ const path = JsonPath.fromString(TEST_PATHS.FOO_BAR_BAZ_HYPHENATED);
85
+ safeExpect(path.value).toEqual(TEST_PATHS.BAR_BAZ_HYPHENATED_FORMATTED);
86
+ });
87
+
88
+ it('uses bracket notation for segments with spaces', () => {
89
+ const path = JsonPath.fromString('foo.bar baz');
90
+ safeExpect(path.value).toEqual("$.foo['bar baz']");
91
+ });
92
+
93
+ it('uses bracket notation for segments with dots', () => {
94
+ const path = JsonPath.fromString('foo.bar.baz.qux');
95
+ safeExpect(path.value).toEqual(TEST_PATHS.FOO_BAR_BAZ_QUX_FORMATTED);
96
+ });
97
+
98
+ it('uses bracket notation for segments with special characters', () => {
99
+ const path = JsonPath.fromString('foo.bar@baz');
100
+ safeExpect(path.value).toEqual("$.foo['bar@baz']");
101
+ });
102
+
103
+ it('uses bracket notation for segments with dollar signs', () => {
104
+ const path = JsonPath.fromString('foo.$bar');
105
+ safeExpect(path.value).toEqual("$.foo['$bar']");
106
+ });
107
+
108
+ it('uses bracket notation for segments with brackets', () => {
109
+ const path = JsonPath.fromString('foo.[bar]');
110
+ safeExpect(path.value).toEqual("$.foo['[bar]']");
111
+ });
112
+
113
+ it('uses bracket notation for segments with parentheses', () => {
114
+ const path = JsonPath.fromString('foo.(bar)');
115
+ safeExpect(path.value).toEqual("$.foo['(bar)']");
116
+ });
117
+
118
+ it('uses bracket notation for segments with slashes', () => {
119
+ const path = JsonPath.fromString('foo.bar/baz');
120
+ safeExpect(path.value).toEqual("$.foo['bar/baz']");
121
+ });
122
+
123
+ it('uses bracket notation for segments with colons', () => {
124
+ const path = JsonPath.fromString('foo.bar:baz');
125
+ safeExpect(path.value).toEqual("$.foo['bar:baz']");
126
+ });
127
+
128
+ it('uses bracket notation for segments with commas', () => {
129
+ const path = JsonPath.fromString('foo.bar,baz');
130
+ safeExpect(path.value).toEqual("$.foo['bar,baz']");
131
+ });
132
+
133
+ it('uses bracket notation for segments with semicolons', () => {
134
+ const path = JsonPath.fromString('foo.bar;baz');
135
+ safeExpect(path.value).toEqual("$.foo['bar;baz']");
136
+ });
137
+
138
+ it('uses bracket notation for segments with quotes', () => {
139
+ const path = JsonPath.fromString('foo.bar"baz');
140
+ safeExpect(path.value).toEqual("$.foo['bar\"baz']");
141
+ });
142
+
143
+ it('uses bracket notation for segments with apostrophes', () => {
144
+ const path = JsonPath.fromString("foo.bar'baz");
145
+ safeExpect(path.value).toEqual("$.foo['bar'baz']");
146
+ });
147
+
148
+ it('uses bracket notation for segments with asterisks', () => {
149
+ const path = JsonPath.fromString('foo.bar*baz');
150
+ safeExpect(path.value).toEqual("$.foo['bar*baz']");
151
+ });
152
+
153
+ it('uses bracket notation for segments with plus signs', () => {
154
+ const path = JsonPath.fromString('foo.bar+baz');
155
+ safeExpect(path.value).toEqual("$.foo['bar+baz']");
156
+ });
157
+
158
+ it('uses bracket notation for segments with equals signs', () => {
159
+ const path = JsonPath.fromString('foo.bar=baz');
160
+ safeExpect(path.value).toEqual("$.foo['bar=baz']");
161
+ });
162
+ });
163
+
164
+ describe('UUID segments', () => {
165
+ it('uses bracket notation for UUID segments', () => {
166
+ const path = JsonPath.fromString('foo.123e4567-e89b-12d3-a456-426614174000');
167
+ safeExpect(path.value).toEqual("$.foo['123e4567-e89b-12d3-a456-426614174000']");
168
+ });
169
+
170
+ it('uses bracket notation for uppercase UUID segments', () => {
171
+ const path = JsonPath.fromString('foo.123E4567-E89B-12D3-A456-426614174000');
172
+ safeExpect(path.value).toEqual("$.foo['123E4567-E89B-12D3-A456-426614174000']");
173
+ });
174
+
175
+ it('uses bracket notation for mixed case UUID segments', () => {
176
+ const path = JsonPath.fromString('foo.123e4567-E89B-12d3-A456-426614174000');
177
+ safeExpect(path.value).toEqual("$.foo['123e4567-E89B-12d3-A456-426614174000']");
178
+ });
179
+
180
+ it('handles UUIDs in the middle of a path', () => {
181
+ const path = JsonPath.fromString('foo.123e4567-e89b-12d3-a456-426614174000.bar');
182
+ safeExpect(path.value).toEqual("$.foo['123e4567-e89b-12d3-a456-426614174000'].bar");
183
+ });
184
+
185
+ it('handles multiple UUIDs in a path', () => {
186
+ const path = JsonPath.fromString(
187
+ 'foo.123e4567-e89b-12d3-a456-426614174000.bar.987fcdeb-51a2-43e1-9876-543210fedcba'
188
+ );
189
+
190
+ safeExpect(path.value).toEqual(
191
+ "$.foo['123e4567-e89b-12d3-a456-426614174000'].bar['987fcdeb-51a2-43e1-9876-543210fedcba']"
192
+ );
193
+ });
194
+ });
195
+
196
+ describe('complex paths', () => {
197
+ it('handles paths with mixed regular and special segments', () => {
198
+ const path = JsonPath.fromString('foo.bar-baz.qux');
199
+ safeExpect(path.value).toEqual(TEST_PATHS.BAR_BAZ_QUX_FORMATTED);
200
+ });
201
+
202
+ it('handles paths with multiple special character segments', () => {
203
+ const path = JsonPath.fromString('foo.bar-baz.qux-quux');
204
+ safeExpect(path.value).toEqual("$.foo['bar-baz']['qux-quux']");
205
+ });
206
+
207
+ it('handles paths with numeric and special character segments', () => {
208
+ const path = JsonPath.fromString('foo.123.bar-baz');
209
+ safeExpect(path.value).toEqual("$.foo['123']['bar-baz']");
210
+ });
211
+
212
+ it('handles paths with UUIDs and special character segments', () => {
213
+ const path = JsonPath.fromString('foo.123e4567-e89b-12d3-a456-426614174000.bar-baz');
214
+ safeExpect(path.value).toEqual("$.foo['123e4567-e89b-12d3-a456-426614174000']['bar-baz']");
215
+ });
216
+
217
+ it('handles long paths with many segments', () => {
218
+ const path = JsonPath.fromString('a.b.c.d.e.f.g.h.i.j');
219
+ safeExpect(path.value).toEqual('$.a.b.c.d.e.f.g.h.i.j');
220
+ });
221
+
222
+ it('handles paths with consecutive special segments', () => {
223
+ const path = JsonPath.fromString('foo.bar-baz.qux-quux.hello-world');
224
+ safeExpect(path.value).toEqual("$.foo['bar-baz']['qux-quux']['hello-world']");
225
+ });
226
+ });
227
+
228
+ describe('edge cases', () => {
229
+ it('handles empty string gracefully', () => {
230
+ const path = JsonPath.fromString('');
231
+ safeExpect(path.value).toEqual('$.');
232
+ });
233
+
234
+ it('handles path that is just $', () => {
235
+ const path = JsonPath.fromString('$');
236
+ safeExpect(path.value).toEqual('$');
237
+ });
238
+
239
+ it('handles path starting with $ and having segments', () => {
240
+ const path = JsonPath.fromString(TEST_PATHS.FOO_BAR_BAZ_FORMATTED);
241
+ safeExpect(path.value).toEqual(TEST_PATHS.FOO_BAR_BAZ_FORMATTED);
242
+ });
243
+
244
+ it('handles path with special characters already in bracket notation', () => {
245
+ const path = JsonPath.fromString(TEST_PATHS.BAR_BAZ_HYPHENATED_FORMATTED);
246
+ safeExpect(path.value).toEqual(TEST_PATHS.BAR_BAZ_HYPHENATED_FORMATTED);
247
+ });
248
+ });
249
+ });
250
+
251
+ describe('appendPath', () => {
252
+ describe('appending JsonPath instances', () => {
253
+ it('appends a JsonPath to another JsonPath', () => {
254
+ const path1 = new JsonPath(TEST_PATHS.FOO_BAR_FORMATTED);
255
+ const path2 = new JsonPath('$.baz.qux');
256
+ const result = path1.appendPath(path2);
257
+ safeExpect(result.value).toEqual(TEST_PATHS.FOO_BAR_BAZ_QUX_FORMATTED);
258
+ });
259
+
260
+ it('appends a simple JsonPath', () => {
261
+ const path1 = new JsonPath(TEST_PATHS.FOO_FORMATTED);
262
+ const path2 = new JsonPath('$.bar');
263
+ const result = path1.appendPath(path2);
264
+ safeExpect(result.value).toEqual(TEST_PATHS.FOO_BAR_FORMATTED);
265
+ });
266
+
267
+ it('appends a JsonPath with bracket notation', () => {
268
+ const path1 = new JsonPath(TEST_PATHS.FOO_BAR_FORMATTED);
269
+ const path2 = new JsonPath("$['baz-qux']");
270
+ const result = path1.appendPath(path2);
271
+ safeExpect(result.value).toEqual("$.foo.bar['baz-qux']");
272
+ });
273
+
274
+ it('appends to a path with bracket notation', () => {
275
+ const path1 = new JsonPath(TEST_PATHS.BAR_BAZ_HYPHENATED_FORMATTED);
276
+ const path2 = new JsonPath('$.qux');
277
+ const result = path1.appendPath(path2);
278
+ safeExpect(result.value).toEqual(TEST_PATHS.BAR_BAZ_QUX_FORMATTED);
279
+ });
280
+
281
+ it('appends a complex path to another complex path', () => {
282
+ const path1 = new JsonPath(TEST_PATHS.BAR_BAZ_HYPHENATED_FORMATTED);
283
+ const path2 = new JsonPath("$.qux['hello-world']");
284
+ const result = path1.appendPath(path2);
285
+ safeExpect(result.value).toEqual("$.foo['bar-baz'].qux['hello-world']");
286
+ });
287
+ });
288
+
289
+ describe('appending string paths', () => {
290
+ it('appends a string path to a JsonPath', () => {
291
+ const path1 = new JsonPath(TEST_PATHS.FOO_BAR_FORMATTED);
292
+ const result = path1.appendPath('baz.qux');
293
+ safeExpect(result.value).toEqual(TEST_PATHS.FOO_BAR_BAZ_QUX_FORMATTED);
294
+ });
295
+
296
+ it('appends a simple string path', () => {
297
+ const path1 = new JsonPath(TEST_PATHS.FOO_FORMATTED);
298
+ const result = path1.appendPath('bar');
299
+ safeExpect(result.value).toEqual(TEST_PATHS.FOO_BAR_FORMATTED);
300
+ });
301
+
302
+ it('converts and appends a string path with special characters', () => {
303
+ const path1 = new JsonPath(TEST_PATHS.FOO_BAR_FORMATTED);
304
+ const result = path1.appendPath('baz-qux');
305
+ safeExpect(result.value).toEqual("$.foo.bar['baz-qux']");
306
+ });
307
+
308
+ it('converts and appends a string path with numbers', () => {
309
+ const path1 = new JsonPath(TEST_PATHS.FOO_BAR_FORMATTED);
310
+ const result = path1.appendPath('123.baz');
311
+ safeExpect(result.value).toEqual("$.foo.bar['123'].baz");
312
+ });
313
+
314
+ it('converts and appends a string path with UUIDs', () => {
315
+ const path1 = new JsonPath(TEST_PATHS.FOO_FORMATTED);
316
+ const result = path1.appendPath('123e4567-e89b-12d3-a456-426614174000');
317
+ safeExpect(result.value).toEqual("$.foo['123e4567-e89b-12d3-a456-426614174000']");
318
+ });
319
+
320
+ it('appends a multi-segment string path', () => {
321
+ const path1 = new JsonPath(TEST_PATHS.FOO_FORMATTED);
322
+ const result = path1.appendPath('bar.baz.qux');
323
+ safeExpect(result.value).toEqual(TEST_PATHS.FOO_BAR_BAZ_QUX_FORMATTED);
324
+ });
325
+
326
+ it('appends a string path that already starts with $', () => {
327
+ const path1 = new JsonPath(TEST_PATHS.FOO_FORMATTED);
328
+ const result = path1.appendPath('$.bar.baz');
329
+ safeExpect(result.value).toEqual('$.foo.bar.baz');
330
+ });
331
+ });
332
+
333
+ describe('chaining multiple appends', () => {
334
+ it('chains multiple appendPath calls', () => {
335
+ const path = new JsonPath(TEST_PATHS.FOO_FORMATTED);
336
+ const result = path.appendPath('bar').appendPath('baz').appendPath('qux');
337
+ safeExpect(result.value).toEqual(TEST_PATHS.FOO_BAR_BAZ_QUX_FORMATTED);
338
+ });
339
+
340
+ it('chains appends with mixed types', () => {
341
+ const path = new JsonPath(TEST_PATHS.FOO_FORMATTED);
342
+ const path2 = new JsonPath('$.bar');
343
+ const result = path.appendPath(path2).appendPath('baz').appendPath(new JsonPath('$.qux'));
344
+ safeExpect(result.value).toEqual(TEST_PATHS.FOO_BAR_BAZ_QUX_FORMATTED);
345
+ });
346
+
347
+ it('chains appends with special characters', () => {
348
+ const path = new JsonPath(TEST_PATHS.FOO_FORMATTED);
349
+ const result = path.appendPath('bar-baz').appendPath('qux').appendPath('hello-world');
350
+ safeExpect(result.value).toEqual("$.foo['bar-baz'].qux['hello-world']");
351
+ });
352
+ });
353
+
354
+ describe('edge cases', () => {
355
+ it('appends an empty string', () => {
356
+ const path1 = new JsonPath(TEST_PATHS.FOO_BAR_FORMATTED);
357
+ const result = path1.appendPath('');
358
+ safeExpect(result.value).toEqual('$.foo.bar.');
359
+ });
360
+
361
+ it('appends to root path', () => {
362
+ const path1 = new JsonPath('$');
363
+ const result = path1.appendPath(TEST_PATHS.SIMPLE_FOO_BAR);
364
+ safeExpect(result.value).toEqual(TEST_PATHS.FOO_BAR_FORMATTED);
365
+ });
366
+
367
+ it('appends to path ending with bracket notation', () => {
368
+ const path1 = new JsonPath(TEST_PATHS.BAR_BAZ_HYPHENATED_FORMATTED);
369
+ const result = path1.appendPath('qux.quux');
370
+ safeExpect(result.value).toEqual("$.foo['bar-baz'].qux.quux");
371
+ });
372
+ });
373
+ });
374
+
375
+ describe('toString', () => {
376
+ it('returns the string representation of the path', () => {
377
+ const path = new JsonPath(TEST_PATHS.FOO_BAR_BAZ_FORMATTED);
378
+ safeExpect(path.toString()).toEqual(TEST_PATHS.FOO_BAR_BAZ_FORMATTED);
379
+ });
380
+
381
+ it('returns the string representation of a path with bracket notation', () => {
382
+ const path = new JsonPath(TEST_PATHS.BAR_BAZ_QUX_FORMATTED);
383
+ safeExpect(path.toString()).toEqual(TEST_PATHS.BAR_BAZ_QUX_FORMATTED);
384
+ });
385
+
386
+ it('returns the string representation of a complex path', () => {
387
+ const path = new JsonPath("$.foo['123'].bar['456-abc']");
388
+ safeExpect(path.toString()).toEqual("$.foo['123'].bar['456-abc']");
389
+ });
390
+
391
+ it('returns the same value as the value property', () => {
392
+ const path = new JsonPath(TEST_PATHS.FOO_BAR_FORMATTED);
393
+ safeExpect(path.toString()).toEqual(path.value);
394
+ });
395
+ });
396
+
397
+ describe('integration tests', () => {
398
+ it('handles a real-world workflow path', () => {
399
+ const path = JsonPath.fromString('workflowTemplate.components.textField.value');
400
+ safeExpect(path.value).toEqual('$.workflowTemplate.components.textField.value');
401
+ });
402
+
403
+ it('handles a path with UUID identifiers', () => {
404
+ const path = JsonPath.fromString(
405
+ 'channels.123e4567-e89b-12d3-a456-426614174000.workflowTemplates.987fcdeb-51a2-43e1-9876-543210fedcba'
406
+ );
407
+
408
+ safeExpect(path.value).toEqual(
409
+ "$.channels['123e4567-e89b-12d3-a456-426614174000'].workflowTemplates['987fcdeb-51a2-43e1-9876-543210fedcba']"
410
+ );
411
+ });
412
+
413
+ it('handles a path with array indices', () => {
414
+ const path = JsonPath.fromString('items.0.name');
415
+ safeExpect(path.value).toEqual("$.items['0'].name");
416
+ });
417
+
418
+ it('handles building a path incrementally', () => {
419
+ const base = JsonPath.fromString('workflowEntity');
420
+ const withComponent = base.appendPath('components');
421
+ const withField = withComponent.appendPath('date-field');
422
+ const withValue = withField.appendPath('value');
423
+
424
+ safeExpect(withValue.value).toEqual("$.workflowEntity.components['date-field'].value");
425
+ });
426
+
427
+ it('handles error paths with field names', () => {
428
+ const path = JsonPath.fromString('validation.errors.email-address.message');
429
+ safeExpect(path.value).toEqual("$.validation.errors['email-address'].message");
430
+ });
431
+
432
+ it('handles deeply nested object paths', () => {
433
+ const path = JsonPath.fromString('a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p');
434
+ safeExpect(path.value).toEqual('$.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p');
435
+ });
436
+
437
+ it('handles mixed notation in real-world scenario', () => {
438
+ const base = JsonPath.fromString('workflow.123e4567-e89b-12d3-a456-426614174000');
439
+ const result = base.appendPath('components.my-field.settings');
440
+
441
+ safeExpect(result.value).toEqual(
442
+ "$.workflow['123e4567-e89b-12d3-a456-426614174000'].components['my-field'].settings"
443
+ );
444
+ });
445
+ });
446
+
447
+ describe('RFC 9535 compliance', () => {
448
+ it('never produces dot before bracket notation', () => {
449
+ const testCases = [
450
+ { input: TEST_PATHS.FOO_BAR_BAZ_HYPHENATED, invalid: ".['", description: 'hyphenated segment' },
451
+ { input: 'foo.123', invalid: ".['", description: 'numeric segment' },
452
+ { input: 'foo.bar baz', invalid: ".['", description: 'segment with space' },
453
+ { input: 'foo.$bar', invalid: ".['", description: 'segment with dollar' },
454
+ ];
455
+
456
+ testCases.forEach(({ input, invalid, description }) => {
457
+ const path = JsonPath.fromString(input);
458
+ safeExpect(path.value).not.toContain(invalid);
459
+
460
+ if (path.value.includes(invalid)) {
461
+ throw new Error(`Path "${input}" (${description}) produced invalid syntax: ${path.value}`);
462
+ }
463
+ });
464
+ });
465
+
466
+ it('produces valid bracket notation without preceding dots', () => {
467
+ const path = JsonPath.fromString('foo.bar-baz.qux');
468
+ safeExpect(path.value).toEqual(TEST_PATHS.BAR_BAZ_QUX_FORMATTED);
469
+ safeExpect(path.value.includes(".['")).toEqual(false);
470
+ });
471
+
472
+ it('produces paths that work with jsonpath library', () => {
473
+ const validPaths = [
474
+ JsonPath.fromString(TEST_PATHS.FOO_BAR_BAZ).value,
475
+ JsonPath.fromString(TEST_PATHS.FOO_BAR_BAZ_HYPHENATED).value,
476
+ JsonPath.fromString('foo.123.bar').value,
477
+ JsonPath.fromString('foo.123e4567-e89b-12d3-a456-426614174000').value,
478
+ ];
479
+
480
+ validPaths.forEach((path) => {
481
+ safeExpect(path.startsWith('$')).toEqual(true);
482
+ safeExpect(path.includes('.[')).toEqual(false);
483
+ });
484
+ });
485
+
486
+ it('handles first segment with special characters correctly', () => {
487
+ const path = JsonPath.fromString('123-start.foo');
488
+ safeExpect(path.value).toEqual("$['123-start'].foo");
489
+ safeExpect(path.value).not.toEqual("$.['123-start'].foo");
490
+ });
491
+
492
+ it('uses consistent notation throughout path', () => {
493
+ const path = JsonPath.fromString('foo.bar-baz.qux.hello-world.end');
494
+ const expected = "$.foo['bar-baz'].qux['hello-world'].end";
495
+ safeExpect(path.value).toEqual(expected);
496
+
497
+ safeExpect(path.value.includes(".['")).toEqual(false);
498
+ safeExpect(path.value.includes('][')).toEqual(false);
499
+ });
500
+
501
+ it('appended paths maintain RFC compliance', () => {
502
+ const base = new JsonPath(TEST_PATHS.FOO_FORMATTED);
503
+ const appended = base.appendPath(TEST_PATHS.BAR_BAZ_HYPHENATED).appendPath('qux');
504
+
505
+ safeExpect(appended.value).toEqual(TEST_PATHS.BAR_BAZ_QUX_FORMATTED);
506
+ safeExpect(appended.value.includes(".['")).toEqual(false);
507
+ });
508
+
509
+ it('handles complex real-world paths in RFC-compliant way', () => {
510
+ const path = JsonPath.fromString('workflowTemplate.components.email-field.validation.pattern');
511
+
512
+ safeExpect(path.value).toEqual("$.workflowTemplate.components['email-field'].validation.pattern");
513
+ safeExpect(path.value.includes(".['")).toEqual(false);
514
+ });
515
+ });
516
+ });