@player-tools/fluent 0.13.0--canary.221.5662
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/dist/cjs/index.cjs +2257 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/index.legacy-esm.js +2143 -0
- package/dist/index.mjs +2143 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +36 -0
- package/src/core/base-builder/__tests__/fluent-builder-base.test.ts +2257 -0
- package/src/core/base-builder/__tests__/id-generator.test.ts +658 -0
- package/src/core/base-builder/__tests__/registry.test.ts +411 -0
- package/src/core/base-builder/__tests__/switch.test.ts +501 -0
- package/src/core/base-builder/__tests__/template.test.ts +449 -0
- package/src/core/base-builder/__tests__/value-extraction.test.ts +200 -0
- package/src/core/base-builder/conditional/index.ts +64 -0
- package/src/core/base-builder/context.ts +151 -0
- package/src/core/base-builder/fluent-builder-base.ts +261 -0
- package/src/core/base-builder/guards.ts +137 -0
- package/src/core/base-builder/id/generator.ts +286 -0
- package/src/core/base-builder/id/registry.ts +152 -0
- package/src/core/base-builder/index.ts +60 -0
- package/src/core/base-builder/resolution/path-resolver.ts +108 -0
- package/src/core/base-builder/resolution/pipeline.ts +96 -0
- package/src/core/base-builder/resolution/steps/asset-id.ts +77 -0
- package/src/core/base-builder/resolution/steps/asset-wrappers.ts +64 -0
- package/src/core/base-builder/resolution/steps/builders.ts +85 -0
- package/src/core/base-builder/resolution/steps/mixed-arrays.ts +117 -0
- package/src/core/base-builder/resolution/steps/static-values.ts +35 -0
- package/src/core/base-builder/resolution/steps/switches.ts +63 -0
- package/src/core/base-builder/resolution/steps/templates.ts +30 -0
- package/src/core/base-builder/resolution/value-resolver.ts +308 -0
- package/src/core/base-builder/storage/auxiliary-storage.ts +82 -0
- package/src/core/base-builder/storage/value-storage.ts +280 -0
- package/src/core/base-builder/types.ts +184 -0
- package/src/core/base-builder/utils.ts +10 -0
- package/src/core/flow/__tests__/index.test.ts +292 -0
- package/src/core/flow/index.ts +141 -0
- package/src/core/index.ts +8 -0
- package/src/core/mocks/generated/action.builder.ts +109 -0
- package/src/core/mocks/generated/choice.builder.ts +161 -0
- package/src/core/mocks/generated/choiceItem.builder.ts +133 -0
- package/src/core/mocks/generated/collection.builder.ts +117 -0
- package/src/core/mocks/generated/index.ts +7 -0
- package/src/core/mocks/generated/info.builder.ts +80 -0
- package/src/core/mocks/generated/input.builder.ts +75 -0
- package/src/core/mocks/generated/text.builder.ts +63 -0
- package/src/core/mocks/index.ts +1 -0
- package/src/core/mocks/types/action.ts +92 -0
- package/src/core/mocks/types/choice.ts +129 -0
- package/src/core/mocks/types/collection.ts +140 -0
- package/src/core/mocks/types/info.ts +7 -0
- package/src/core/mocks/types/input.ts +7 -0
- package/src/core/mocks/types/text.ts +5 -0
- package/src/core/schema/__tests__/index.test.ts +127 -0
- package/src/core/schema/index.ts +195 -0
- package/src/core/schema/types.ts +7 -0
- package/src/core/switch/__tests__/index.test.ts +156 -0
- package/src/core/switch/index.ts +76 -0
- package/src/core/tagged-template/README.md +448 -0
- package/src/core/tagged-template/__tests__/extract-bindings-from-schema.test.ts +207 -0
- package/src/core/tagged-template/__tests__/index.test.ts +190 -0
- package/src/core/tagged-template/__tests__/schema-std-integration.test.ts +580 -0
- package/src/core/tagged-template/binding.ts +95 -0
- package/src/core/tagged-template/expression.ts +92 -0
- package/src/core/tagged-template/extract-bindings-from-schema.ts +120 -0
- package/src/core/tagged-template/index.ts +5 -0
- package/src/core/tagged-template/std.ts +472 -0
- package/src/core/tagged-template/types.ts +123 -0
- package/src/core/template/__tests__/index.test.ts +380 -0
- package/src/core/template/index.ts +191 -0
- package/src/core/utils/index.ts +160 -0
- package/src/fp/README.md +411 -0
- package/src/fp/__tests__/index.test.ts +1178 -0
- package/src/fp/index.ts +386 -0
- package/src/gen/common.ts +2 -0
- package/src/gen/plugin.mjs +315 -0
- package/src/index.ts +5 -0
- package/src/types.ts +203 -0
- package/types/core/base-builder/conditional/index.d.ts +21 -0
- package/types/core/base-builder/context.d.ts +39 -0
- package/types/core/base-builder/fluent-builder-base.d.ts +132 -0
- package/types/core/base-builder/guards.d.ts +58 -0
- package/types/core/base-builder/id/generator.d.ts +69 -0
- package/types/core/base-builder/id/registry.d.ts +93 -0
- package/types/core/base-builder/index.d.ts +8 -0
- package/types/core/base-builder/resolution/path-resolver.d.ts +15 -0
- package/types/core/base-builder/resolution/pipeline.d.ts +25 -0
- package/types/core/base-builder/resolution/steps/asset-id.d.ts +14 -0
- package/types/core/base-builder/resolution/steps/asset-wrappers.d.ts +14 -0
- package/types/core/base-builder/resolution/steps/builders.d.ts +14 -0
- package/types/core/base-builder/resolution/steps/mixed-arrays.d.ts +14 -0
- package/types/core/base-builder/resolution/steps/static-values.d.ts +14 -0
- package/types/core/base-builder/resolution/steps/switches.d.ts +15 -0
- package/types/core/base-builder/resolution/steps/templates.d.ts +14 -0
- package/types/core/base-builder/resolution/value-resolver.d.ts +37 -0
- package/types/core/base-builder/storage/auxiliary-storage.d.ts +50 -0
- package/types/core/base-builder/storage/value-storage.d.ts +82 -0
- package/types/core/base-builder/types.d.ts +141 -0
- package/types/core/base-builder/utils.d.ts +2 -0
- package/types/core/flow/index.d.ts +23 -0
- package/types/core/index.d.ts +8 -0
- package/types/core/mocks/index.d.ts +2 -0
- package/types/core/mocks/types/action.d.ts +58 -0
- package/types/core/mocks/types/choice.d.ts +95 -0
- package/types/core/mocks/types/collection.d.ts +102 -0
- package/types/core/mocks/types/info.d.ts +7 -0
- package/types/core/mocks/types/input.d.ts +7 -0
- package/types/core/mocks/types/text.d.ts +5 -0
- package/types/core/schema/index.d.ts +34 -0
- package/types/core/schema/types.d.ts +5 -0
- package/types/core/switch/index.d.ts +21 -0
- package/types/core/tagged-template/binding.d.ts +19 -0
- package/types/core/tagged-template/expression.d.ts +11 -0
- package/types/core/tagged-template/extract-bindings-from-schema.d.ts +7 -0
- package/types/core/tagged-template/index.d.ts +6 -0
- package/types/core/tagged-template/std.d.ts +174 -0
- package/types/core/tagged-template/types.d.ts +69 -0
- package/types/core/template/index.d.ts +97 -0
- package/types/core/utils/index.d.ts +47 -0
- package/types/fp/index.d.ts +149 -0
- package/types/gen/common.d.ts +3 -0
- package/types/index.d.ts +3 -0
- package/types/types.d.ts +163 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach, vi } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
IDRegistry,
|
|
4
|
+
createIdRegistry,
|
|
5
|
+
globalIdRegistry,
|
|
6
|
+
genId,
|
|
7
|
+
} from "../id/generator";
|
|
8
|
+
import { BaseBuildContext } from "../types";
|
|
9
|
+
|
|
10
|
+
describe("IDRegistry", () => {
|
|
11
|
+
let registry: IDRegistry;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
registry = new IDRegistry();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe("ensureUnique", () => {
|
|
18
|
+
test("returns original ID when no collision", () => {
|
|
19
|
+
const id = registry.ensureUnique("test-id");
|
|
20
|
+
expect(id).toBe("test-id");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("appends counter when collision detected", () => {
|
|
24
|
+
const id1 = registry.ensureUnique("duplicate");
|
|
25
|
+
const id2 = registry.ensureUnique("duplicate");
|
|
26
|
+
const id3 = registry.ensureUnique("duplicate");
|
|
27
|
+
|
|
28
|
+
expect(id1).toBe("duplicate");
|
|
29
|
+
expect(id2).toBe("duplicate-1");
|
|
30
|
+
expect(id3).toBe("duplicate-2");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("handles complex ID patterns", () => {
|
|
34
|
+
const id1 = registry.ensureUnique("parent-slot-child");
|
|
35
|
+
const id2 = registry.ensureUnique("parent-slot-child");
|
|
36
|
+
|
|
37
|
+
expect(id1).toBe("parent-slot-child");
|
|
38
|
+
expect(id2).toBe("parent-slot-child-1");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("maintains separate counters for different base IDs", () => {
|
|
42
|
+
registry.ensureUnique("id-a");
|
|
43
|
+
registry.ensureUnique("id-a"); // id-a-1
|
|
44
|
+
registry.ensureUnique("id-b");
|
|
45
|
+
registry.ensureUnique("id-a"); // id-a-2
|
|
46
|
+
registry.ensureUnique("id-b"); // id-b-1
|
|
47
|
+
|
|
48
|
+
expect(registry.ensureUnique("id-a")).toBe("id-a-3");
|
|
49
|
+
expect(registry.ensureUnique("id-b")).toBe("id-b-2");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("handles empty string IDs", () => {
|
|
53
|
+
const id1 = registry.ensureUnique("");
|
|
54
|
+
const id2 = registry.ensureUnique("");
|
|
55
|
+
|
|
56
|
+
expect(id1).toBe("");
|
|
57
|
+
expect(id2).toBe("-1");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("returns ID as-is when registry is disabled", () => {
|
|
61
|
+
registry.setEnabled(false);
|
|
62
|
+
|
|
63
|
+
const id1 = registry.ensureUnique("test");
|
|
64
|
+
const id2 = registry.ensureUnique("test");
|
|
65
|
+
|
|
66
|
+
expect(id1).toBe("test");
|
|
67
|
+
expect(id2).toBe("test"); // No collision detection
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("allows template placeholder IDs as duplicates", () => {
|
|
71
|
+
const id1 = registry.ensureUnique("parent-_index_");
|
|
72
|
+
const id2 = registry.ensureUnique("parent-_index_");
|
|
73
|
+
const id3 = registry.ensureUnique("parent-_index1_");
|
|
74
|
+
const id4 = registry.ensureUnique("parent-_row_");
|
|
75
|
+
|
|
76
|
+
// Template placeholders should not trigger collision detection
|
|
77
|
+
expect(id1).toBe("parent-_index_");
|
|
78
|
+
expect(id2).toBe("parent-_index_");
|
|
79
|
+
expect(id3).toBe("parent-_index1_");
|
|
80
|
+
expect(id4).toBe("parent-_row_");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("enforces uniqueness for non-template IDs with similar patterns", () => {
|
|
84
|
+
const id1 = registry.ensureUnique("parent-_index_-field");
|
|
85
|
+
const id2 = registry.ensureUnique("parent-_index_-field");
|
|
86
|
+
const id3 = registry.ensureUnique("parent-something");
|
|
87
|
+
const id4 = registry.ensureUnique("parent-something");
|
|
88
|
+
|
|
89
|
+
// Non-template IDs should still enforce uniqueness
|
|
90
|
+
expect(id1).toBe("parent-_index_-field");
|
|
91
|
+
expect(id2).toBe("parent-_index_-field-1");
|
|
92
|
+
expect(id3).toBe("parent-something");
|
|
93
|
+
expect(id4).toBe("parent-something-1");
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe("has", () => {
|
|
98
|
+
test("returns false for unregistered IDs", () => {
|
|
99
|
+
expect(registry.has("unknown")).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("returns true for registered IDs", () => {
|
|
103
|
+
registry.ensureUnique("known");
|
|
104
|
+
expect(registry.has("known")).toBe(true);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("tracks modified IDs", () => {
|
|
108
|
+
registry.ensureUnique("base");
|
|
109
|
+
registry.ensureUnique("base"); // Creates "base-1"
|
|
110
|
+
|
|
111
|
+
expect(registry.has("base")).toBe(true);
|
|
112
|
+
expect(registry.has("base-1")).toBe(true);
|
|
113
|
+
expect(registry.has("base-2")).toBe(false);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe("reset", () => {
|
|
118
|
+
test("clears all registered IDs", () => {
|
|
119
|
+
registry.ensureUnique("id1");
|
|
120
|
+
registry.ensureUnique("id2");
|
|
121
|
+
registry.ensureUnique("id3");
|
|
122
|
+
|
|
123
|
+
expect(registry.size()).toBe(3);
|
|
124
|
+
|
|
125
|
+
registry.reset();
|
|
126
|
+
|
|
127
|
+
expect(registry.size()).toBe(0);
|
|
128
|
+
expect(registry.has("id1")).toBe(false);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("allows reuse of IDs after reset", () => {
|
|
132
|
+
registry.ensureUnique("reusable");
|
|
133
|
+
registry.ensureUnique("reusable"); // Would be "reusable-1"
|
|
134
|
+
|
|
135
|
+
registry.reset();
|
|
136
|
+
|
|
137
|
+
const id2 = registry.ensureUnique("reusable");
|
|
138
|
+
expect(id2).toBe("reusable"); // Not "reusable-2"
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
describe("size", () => {
|
|
143
|
+
test("returns 0 for empty registry", () => {
|
|
144
|
+
expect(registry.size()).toBe(0);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("counts unique registered IDs", () => {
|
|
148
|
+
registry.ensureUnique("a");
|
|
149
|
+
registry.ensureUnique("b");
|
|
150
|
+
registry.ensureUnique("a"); // Creates "a-1"
|
|
151
|
+
|
|
152
|
+
expect(registry.size()).toBe(3);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe("getRegisteredIds", () => {
|
|
157
|
+
test("returns empty array for empty registry", () => {
|
|
158
|
+
expect(registry.getRegisteredIds()).toEqual([]);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("returns all registered IDs", () => {
|
|
162
|
+
registry.ensureUnique("first");
|
|
163
|
+
registry.ensureUnique("second");
|
|
164
|
+
registry.ensureUnique("first"); // Creates "first-1"
|
|
165
|
+
|
|
166
|
+
const ids = registry.getRegisteredIds();
|
|
167
|
+
expect(ids).toContain("first");
|
|
168
|
+
expect(ids).toContain("second");
|
|
169
|
+
expect(ids).toContain("first-1");
|
|
170
|
+
expect(ids).toHaveLength(3);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
describe("createIdRegistry", () => {
|
|
176
|
+
test("creates independent registry instances", () => {
|
|
177
|
+
const registry1 = createIdRegistry();
|
|
178
|
+
const registry2 = createIdRegistry();
|
|
179
|
+
|
|
180
|
+
registry1.ensureUnique("shared");
|
|
181
|
+
|
|
182
|
+
// registry2 should not know about registry1's IDs
|
|
183
|
+
const id = registry2.ensureUnique("shared");
|
|
184
|
+
expect(id).toBe("shared");
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("respects enabled parameter", () => {
|
|
188
|
+
const disabled = createIdRegistry(false);
|
|
189
|
+
|
|
190
|
+
const id1 = disabled.ensureUnique("test");
|
|
191
|
+
const id2 = disabled.ensureUnique("test");
|
|
192
|
+
|
|
193
|
+
expect(id1).toBe("test");
|
|
194
|
+
expect(id2).toBe("test");
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
describe("genId with IDRegistry integration", () => {
|
|
199
|
+
beforeEach(() => {
|
|
200
|
+
// Reset the global registry before each test
|
|
201
|
+
globalIdRegistry.reset();
|
|
202
|
+
globalIdRegistry.setEnabled(true);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test("prevents ID collisions in slot branches", () => {
|
|
206
|
+
const ctx1: BaseBuildContext = {
|
|
207
|
+
parentId: "form",
|
|
208
|
+
branch: { type: "slot", name: "label" },
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const ctx2: BaseBuildContext = {
|
|
212
|
+
parentId: "form",
|
|
213
|
+
branch: { type: "slot", name: "label" },
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const id1 = genId(ctx1);
|
|
217
|
+
const id2 = genId(ctx2);
|
|
218
|
+
|
|
219
|
+
expect(id1).toBe("form-label");
|
|
220
|
+
expect(id2).toBe("form-label-1");
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test("prevents collisions across different branch types", () => {
|
|
224
|
+
// These contexts would generate the same base ID
|
|
225
|
+
const slotCtx: BaseBuildContext = {
|
|
226
|
+
parentId: "parent",
|
|
227
|
+
branch: { type: "slot", name: "0" },
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
const arrayCtx: BaseBuildContext = {
|
|
231
|
+
parentId: "parent",
|
|
232
|
+
branch: { type: "array-item", index: 0 },
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const id1 = genId(slotCtx);
|
|
236
|
+
const id2 = genId(arrayCtx);
|
|
237
|
+
|
|
238
|
+
expect(id1).toBe("parent-0");
|
|
239
|
+
expect(id2).toBe("parent-0-1");
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test("handles complex nested contexts", () => {
|
|
243
|
+
const ctx1: BaseBuildContext = {
|
|
244
|
+
parentId: "collection-values-0",
|
|
245
|
+
branch: { type: "slot", name: "label" },
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const ctx2: BaseBuildContext = {
|
|
249
|
+
parentId: "collection-values-0",
|
|
250
|
+
branch: { type: "slot", name: "label" },
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const id1 = genId(ctx1);
|
|
254
|
+
const id2 = genId(ctx2);
|
|
255
|
+
|
|
256
|
+
expect(id1).toBe("collection-values-0-label");
|
|
257
|
+
expect(id2).toBe("collection-values-0-label-1");
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test("warns about collisions in development", () => {
|
|
261
|
+
const originalEnv = process.env.NODE_ENV;
|
|
262
|
+
process.env.NODE_ENV = "development";
|
|
263
|
+
|
|
264
|
+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
265
|
+
|
|
266
|
+
const ctx: BaseBuildContext = {
|
|
267
|
+
parentId: "test",
|
|
268
|
+
branch: { type: "slot", name: "slot" },
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
genId(ctx);
|
|
272
|
+
genId(ctx); // Should trigger collision warning
|
|
273
|
+
|
|
274
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
275
|
+
expect.stringContaining("ID collision detected"),
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
warnSpy.mockRestore();
|
|
279
|
+
process.env.NODE_ENV = originalEnv;
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test("allows template placeholders as duplicates", () => {
|
|
283
|
+
const templateCtx1: BaseBuildContext = {
|
|
284
|
+
parentId: "list",
|
|
285
|
+
branch: { type: "template", depth: 0 },
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const templateCtx2: BaseBuildContext = {
|
|
289
|
+
parentId: "list",
|
|
290
|
+
branch: { type: "template", depth: 0 },
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
const id1 = genId(templateCtx1);
|
|
294
|
+
const id2 = genId(templateCtx2);
|
|
295
|
+
|
|
296
|
+
// Template placeholders should be allowed as duplicates
|
|
297
|
+
expect(id1).toBe("list-_index_");
|
|
298
|
+
expect(id2).toBe("list-_index_"); // Should be the same, not "list-_index_-1"
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
test("handles switch branches with collision detection", () => {
|
|
302
|
+
const switchCtx1: BaseBuildContext = {
|
|
303
|
+
parentId: "condition",
|
|
304
|
+
branch: { type: "switch", index: 0, kind: "static" },
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const switchCtx2: BaseBuildContext = {
|
|
308
|
+
parentId: "condition",
|
|
309
|
+
branch: { type: "switch", index: 0, kind: "static" },
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
const id1 = genId(switchCtx1);
|
|
313
|
+
const id2 = genId(switchCtx2);
|
|
314
|
+
|
|
315
|
+
expect(id1).toBe("condition-staticSwitch-0");
|
|
316
|
+
expect(id2).toBe("condition-staticSwitch-0-1");
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
describe("ID Registry with real-world scenarios", () => {
|
|
321
|
+
beforeEach(() => {
|
|
322
|
+
globalIdRegistry.reset();
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
test("collection with duplicate slot names", () => {
|
|
326
|
+
// Simulates a collection with multiple items having the same structure
|
|
327
|
+
const contexts = [
|
|
328
|
+
{
|
|
329
|
+
parentId: "collection",
|
|
330
|
+
branch: { type: "slot" as const, name: "values" },
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
parentId: "collection-values",
|
|
334
|
+
branch: { type: "array-item" as const, index: 0 },
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
parentId: "collection-values-0",
|
|
338
|
+
branch: { type: "slot" as const, name: "label" },
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
parentId: "collection-values",
|
|
342
|
+
branch: { type: "array-item" as const, index: 1 },
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
parentId: "collection-values-1",
|
|
346
|
+
branch: { type: "slot" as const, name: "label" },
|
|
347
|
+
},
|
|
348
|
+
];
|
|
349
|
+
|
|
350
|
+
const ids = contexts.map((ctx) => genId(ctx as BaseBuildContext));
|
|
351
|
+
|
|
352
|
+
// All IDs should be unique
|
|
353
|
+
const uniqueIds = new Set(ids);
|
|
354
|
+
expect(uniqueIds.size).toBe(ids.length);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
test("deeply nested components with potential collisions", () => {
|
|
358
|
+
// Form > Section > Field > Validation > Error
|
|
359
|
+
const deepNesting: BaseBuildContext[] = [
|
|
360
|
+
{ parentId: "form", branch: { type: "slot", name: "sections" } },
|
|
361
|
+
{ parentId: "form-sections", branch: { type: "array-item", index: 0 } },
|
|
362
|
+
{ parentId: "form-sections-0", branch: { type: "slot", name: "fields" } },
|
|
363
|
+
{
|
|
364
|
+
parentId: "form-sections-0-fields",
|
|
365
|
+
branch: { type: "array-item", index: 0 },
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
parentId: "form-sections-0-fields-0",
|
|
369
|
+
branch: { type: "slot", name: "validation" },
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
parentId: "form-sections-0-fields-0-validation",
|
|
373
|
+
branch: { type: "slot", name: "error" },
|
|
374
|
+
},
|
|
375
|
+
// Duplicate path (e.g., from a template)
|
|
376
|
+
{
|
|
377
|
+
parentId: "form-sections-0-fields-0-validation",
|
|
378
|
+
branch: { type: "slot", name: "error" },
|
|
379
|
+
},
|
|
380
|
+
];
|
|
381
|
+
|
|
382
|
+
const ids = deepNesting.map((ctx) => genId(ctx));
|
|
383
|
+
const lastTwo = ids.slice(-2);
|
|
384
|
+
|
|
385
|
+
expect(lastTwo[0]).toBe("form-sections-0-fields-0-validation-error");
|
|
386
|
+
expect(lastTwo[1]).toBe("form-sections-0-fields-0-validation-error-1");
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
test("mixed static and dynamic content", () => {
|
|
390
|
+
const contexts: BaseBuildContext[] = [
|
|
391
|
+
// Static content
|
|
392
|
+
{ parentId: "page", branch: { type: "slot", name: "header" } },
|
|
393
|
+
// Dynamic switch
|
|
394
|
+
{
|
|
395
|
+
parentId: "page",
|
|
396
|
+
branch: { type: "switch", index: 0, kind: "dynamic" },
|
|
397
|
+
},
|
|
398
|
+
// Template-generated content
|
|
399
|
+
{ parentId: "page", branch: { type: "template", depth: 0 } },
|
|
400
|
+
// Another header (collision with first)
|
|
401
|
+
{ parentId: "page", branch: { type: "slot", name: "header" } },
|
|
402
|
+
];
|
|
403
|
+
|
|
404
|
+
const ids = contexts.map((ctx) => genId(ctx));
|
|
405
|
+
|
|
406
|
+
expect(ids[0]).toBe("page-header");
|
|
407
|
+
expect(ids[1]).toBe("page-dynamicSwitch-0");
|
|
408
|
+
expect(ids[2]).toBe("page-_index_");
|
|
409
|
+
expect(ids[3]).toBe("page-header-1");
|
|
410
|
+
});
|
|
411
|
+
});
|