@schmock/core 1.0.0

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 (48) hide show
  1. package/dist/builder.d.ts +62 -0
  2. package/dist/builder.d.ts.map +1 -0
  3. package/dist/builder.js +432 -0
  4. package/dist/errors.d.ts +56 -0
  5. package/dist/errors.d.ts.map +1 -0
  6. package/dist/errors.js +92 -0
  7. package/dist/index.d.ts +27 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +1 -0
  10. package/dist/parser.d.ts +19 -0
  11. package/dist/parser.d.ts.map +1 -0
  12. package/dist/parser.js +40 -0
  13. package/dist/types.d.ts +15 -0
  14. package/dist/types.d.ts.map +1 -0
  15. package/dist/types.js +2 -0
  16. package/package.json +39 -0
  17. package/src/builder.d.ts.map +1 -0
  18. package/src/builder.test.ts +289 -0
  19. package/src/builder.ts +580 -0
  20. package/src/debug.test.ts +241 -0
  21. package/src/delay.test.ts +319 -0
  22. package/src/errors.d.ts.map +1 -0
  23. package/src/errors.test.ts +223 -0
  24. package/src/errors.ts +124 -0
  25. package/src/factory.test.ts +133 -0
  26. package/src/index.d.ts.map +1 -0
  27. package/src/index.ts +80 -0
  28. package/src/namespace.test.ts +273 -0
  29. package/src/parser.d.ts.map +1 -0
  30. package/src/parser.test.ts +131 -0
  31. package/src/parser.ts +61 -0
  32. package/src/plugin-system.test.ts +511 -0
  33. package/src/response-parsing.test.ts +255 -0
  34. package/src/route-matching.test.ts +351 -0
  35. package/src/smart-defaults.test.ts +361 -0
  36. package/src/steps/async-support.steps.ts +427 -0
  37. package/src/steps/basic-usage.steps.ts +316 -0
  38. package/src/steps/developer-experience.steps.ts +439 -0
  39. package/src/steps/error-handling.steps.ts +387 -0
  40. package/src/steps/fluent-api.steps.ts +252 -0
  41. package/src/steps/http-methods.steps.ts +397 -0
  42. package/src/steps/performance-reliability.steps.ts +459 -0
  43. package/src/steps/plugin-integration.steps.ts +279 -0
  44. package/src/steps/route-key-format.steps.ts +118 -0
  45. package/src/steps/state-concurrency.steps.ts +643 -0
  46. package/src/steps/stateful-workflows.steps.ts +351 -0
  47. package/src/types.d.ts.map +1 -0
  48. package/src/types.ts +17 -0
@@ -0,0 +1,241 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { schmock } from "./index";
3
+
4
+ describe("debug functionality", () => {
5
+ const originalConsole = console.log;
6
+ const originalTime = console.time;
7
+ const originalTimeEnd = console.timeEnd;
8
+ let consoleLogs: any[] = [];
9
+
10
+ beforeEach(() => {
11
+ consoleLogs = [];
12
+ console.log = vi.fn((...args) => consoleLogs.push(args));
13
+ console.time = vi.fn();
14
+ console.timeEnd = vi.fn();
15
+ });
16
+
17
+ afterEach(() => {
18
+ console.log = originalConsole;
19
+ console.time = originalTime;
20
+ console.timeEnd = originalTimeEnd;
21
+ });
22
+
23
+ describe("debug logging", () => {
24
+ it("logs debug messages when debug mode is enabled", async () => {
25
+ const mock = schmock({ debug: true });
26
+ mock("GET /test", () => ({ data: "test" }));
27
+
28
+ await mock.handle("GET", "/test");
29
+
30
+ const logMessages = consoleLogs.map((args) => args.join(" "));
31
+ expect(
32
+ logMessages.some((msg) => msg.includes("Debug mode enabled")),
33
+ ).toBe(true);
34
+ expect(logMessages.some((msg) => msg.includes("[SCHMOCK:REQUEST]"))).toBe(
35
+ true,
36
+ );
37
+ });
38
+
39
+ it("does not log when debug mode is disabled", async () => {
40
+ const mock = schmock({ debug: false });
41
+ mock("GET /test", () => ({ data: "test" }));
42
+
43
+ await mock.handle("GET", "/test");
44
+
45
+ expect(consoleLogs).toHaveLength(0);
46
+ });
47
+
48
+ it("defaults to no logging when debug is not configured", async () => {
49
+ const mock = schmock();
50
+ mock("GET /test", () => ({ data: "test" }));
51
+
52
+ await mock.handle("GET", "/test");
53
+
54
+ expect(consoleLogs).toHaveLength(0);
55
+ });
56
+
57
+ it("logs plugin registration with debug enabled", () => {
58
+ const plugin = {
59
+ name: "test-plugin",
60
+ version: "1.0.0",
61
+ enforce: "pre" as const,
62
+ beforeRequest: () => {},
63
+ generate: () => {},
64
+ };
65
+
66
+ const mock = schmock({ debug: true });
67
+ mock.pipe(plugin);
68
+
69
+ const logMessages = consoleLogs.map((args) => args.join(" "));
70
+ expect(
71
+ logMessages.some((msg) =>
72
+ msg.includes("Registered plugin: test-plugin@1.0.0"),
73
+ ),
74
+ ).toBe(true);
75
+ });
76
+
77
+ it("logs plugin execution during request handling", async () => {
78
+ const plugin = {
79
+ name: "logging-test",
80
+ version: "1.0.0",
81
+ process: (ctx: any, response: any) => ({
82
+ context: ctx,
83
+ response: response || { data: "generated" },
84
+ }),
85
+ };
86
+
87
+ const mock = schmock({ debug: true });
88
+ mock.pipe(plugin);
89
+ mock("GET /test", undefined);
90
+
91
+ await mock.handle("GET", "/test");
92
+
93
+ const logMessages = consoleLogs.map((args) => args.join(" "));
94
+ expect(
95
+ logMessages.some((msg) =>
96
+ msg.includes("Processing plugin: logging-test"),
97
+ ),
98
+ ).toBe(true);
99
+ expect(
100
+ logMessages.some((msg) =>
101
+ msg.includes("Plugin logging-test generated response"),
102
+ ),
103
+ ).toBe(true);
104
+ });
105
+
106
+ it("logs response status and timing", async () => {
107
+ const mock = schmock({ debug: true });
108
+ mock("GET /test", () => [201, { created: true }]);
109
+
110
+ await mock.handle("GET", "/test");
111
+
112
+ const logMessages = consoleLogs.map((args) => args.join(" "));
113
+ expect(
114
+ logMessages.some((msg) => msg.includes("Sending response 201")),
115
+ ).toBe(true);
116
+
117
+ expect(console.time).toHaveBeenCalledWith(
118
+ expect.stringMatching(/\[SCHMOCK\] request-/),
119
+ );
120
+ expect(console.timeEnd).toHaveBeenCalledWith(
121
+ expect.stringMatching(/\[SCHMOCK\] request-/),
122
+ );
123
+ });
124
+
125
+ it("logs route matching information", async () => {
126
+ const mock = schmock({ debug: true });
127
+ mock("GET /users/:id", ({ params }) => ({ userId: params.id }));
128
+
129
+ await mock.handle("GET", "/users/123");
130
+
131
+ const logMessages = consoleLogs.map((args) => args.join(" "));
132
+ expect(
133
+ logMessages.some(
134
+ (msg) =>
135
+ msg.includes("Matched route:") || msg.includes("GET /users/123"),
136
+ ),
137
+ ).toBe(true);
138
+ });
139
+
140
+ it("logs 404 errors with debug enabled", async () => {
141
+ const mock = schmock({ debug: true });
142
+ mock("GET /exists", () => "OK");
143
+
144
+ await mock.handle("GET", "/missing");
145
+
146
+ const logMessages = consoleLogs.map((args) => args.join(" "));
147
+ expect(
148
+ logMessages.some((msg) =>
149
+ msg.includes("No route found for GET /missing"),
150
+ ),
151
+ ).toBe(true);
152
+ });
153
+
154
+ it("logs error details when exceptions occur", async () => {
155
+ const mock = schmock({ debug: true });
156
+ mock("GET /error", () => {
157
+ throw new Error("Test error");
158
+ });
159
+
160
+ await mock.handle("GET", "/error");
161
+
162
+ const logMessages = consoleLogs.map((args) => args.join(" "));
163
+ expect(
164
+ logMessages.some((msg) =>
165
+ msg.includes("Error processing request: Test error"),
166
+ ),
167
+ ).toBe(true);
168
+ });
169
+
170
+ it("includes request details in debug logs", async () => {
171
+ const mock = schmock({ debug: true });
172
+ mock("POST /data", ({ body }) => body);
173
+
174
+ await mock.handle("POST", "/data", {
175
+ headers: { "content-type": "application/json" },
176
+ body: { test: "data" },
177
+ query: { filter: "active" },
178
+ });
179
+
180
+ const logMessages = consoleLogs.map((args) => args.join(" "));
181
+ const requestLog = logMessages.find((msg) => msg.includes("POST /data"));
182
+ expect(requestLog).toBeDefined();
183
+ });
184
+ });
185
+
186
+ describe("performance timing", () => {
187
+ it("times the overall request processing", async () => {
188
+ const mock = schmock({ debug: true });
189
+ mock("GET /test", () => "OK");
190
+
191
+ await mock.handle("GET", "/test");
192
+
193
+ expect(console.time).toHaveBeenCalledWith(
194
+ expect.stringMatching(/\[SCHMOCK\] request-/),
195
+ );
196
+ expect(console.timeEnd).toHaveBeenCalledWith(
197
+ expect.stringMatching(/\[SCHMOCK\] request-/),
198
+ );
199
+ });
200
+
201
+ it("times the request processing", async () => {
202
+ const mock = schmock({ debug: true });
203
+ mock("GET /test", () => "OK");
204
+
205
+ await mock.handle("GET", "/test");
206
+
207
+ expect(console.time).toHaveBeenCalledWith(
208
+ expect.stringMatching(/\[SCHMOCK\] request-/),
209
+ );
210
+ expect(console.timeEnd).toHaveBeenCalledWith(
211
+ expect.stringMatching(/\[SCHMOCK\] request-/),
212
+ );
213
+ });
214
+ });
215
+
216
+ describe("debug configuration inheritance", () => {
217
+ it("preserves debug setting through chained calls", () => {
218
+ // Create mock with debug enabled, then configure namespace
219
+ const mock = schmock({ debug: true, namespace: "/api" });
220
+ mock("GET /test", () => "OK");
221
+
222
+ expect(
223
+ consoleLogs.some((args) =>
224
+ args.join(" ").includes("Debug mode enabled"),
225
+ ),
226
+ ).toBe(true);
227
+ });
228
+
229
+ it("can disable debug mode with subsequent config", async () => {
230
+ const mock = schmock({ debug: false });
231
+ mock("GET /test", () => "OK");
232
+
233
+ // Clear logs from build phase
234
+ consoleLogs = [];
235
+
236
+ await mock.handle("GET", "/test");
237
+
238
+ expect(consoleLogs).toHaveLength(0);
239
+ });
240
+ });
241
+ });
@@ -0,0 +1,319 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { schmock } from "./index";
3
+
4
+ describe("response delay functionality", () => {
5
+ describe("fixed delay", () => {
6
+ it("applies fixed delay to responses", async () => {
7
+ const mock = schmock({ delay: 100 });
8
+ mock("GET /test", "response");
9
+
10
+ const start = Date.now();
11
+ const response = await mock.handle("GET", "/test");
12
+ const duration = Date.now() - start;
13
+
14
+ expect(response.body).toBe("response");
15
+ expect(duration).toBeGreaterThanOrEqual(95); // Allow some tolerance
16
+ expect(duration).toBeLessThan(150);
17
+ });
18
+
19
+ it("applies delay to all routes", async () => {
20
+ const mock = schmock({ delay: 50 });
21
+ mock("GET /route1", "response1");
22
+ mock("POST /route2", "response2");
23
+
24
+ const start1 = Date.now();
25
+ const response1 = await mock.handle("GET", "/route1");
26
+ const duration1 = Date.now() - start1;
27
+
28
+ const start2 = Date.now();
29
+ const response2 = await mock.handle("POST", "/route2");
30
+ const duration2 = Date.now() - start2;
31
+
32
+ expect(response1.body).toBe("response1");
33
+ expect(response2.body).toBe("response2");
34
+ expect(duration1).toBeGreaterThanOrEqual(45);
35
+ expect(duration2).toBeGreaterThanOrEqual(45);
36
+ });
37
+
38
+ it("works with zero delay", async () => {
39
+ const mock = schmock({ delay: 0 });
40
+ mock("GET /test", "response");
41
+
42
+ const start = Date.now();
43
+ const response = await mock.handle("GET", "/test");
44
+ const duration = Date.now() - start;
45
+
46
+ expect(response.body).toBe("response");
47
+ expect(duration).toBeLessThan(50); // Should be very fast
48
+ });
49
+ });
50
+
51
+ describe("random delay range", () => {
52
+ it("applies random delay within specified range", async () => {
53
+ const mock = schmock({ delay: [100, 200] });
54
+ mock("GET /test", "response");
55
+
56
+ const start = Date.now();
57
+ const response = await mock.handle("GET", "/test");
58
+ const duration = Date.now() - start;
59
+
60
+ expect(response.body).toBe("response");
61
+ expect(duration).toBeGreaterThanOrEqual(95); // Allow some tolerance for timer precision
62
+ expect(duration).toBeLessThan(250);
63
+ });
64
+
65
+ it("generates different delays for multiple requests", async () => {
66
+ const mock = schmock({ delay: [50, 150] });
67
+ mock("GET /test", "response");
68
+
69
+ const durations: number[] = [];
70
+
71
+ for (let i = 0; i < 5; i++) {
72
+ const start = Date.now();
73
+ await mock.handle("GET", "/test");
74
+ durations.push(Date.now() - start);
75
+ }
76
+
77
+ // Check that we got some variation in delays
78
+ const minDuration = Math.min(...durations);
79
+ const maxDuration = Math.max(...durations);
80
+
81
+ expect(minDuration).toBeGreaterThanOrEqual(45);
82
+ expect(maxDuration).toBeLessThan(200);
83
+ // There should be some variation (not all identical)
84
+ expect(maxDuration - minDuration).toBeGreaterThan(10);
85
+ });
86
+
87
+ it("handles reversed range [max, min]", async () => {
88
+ const mock = schmock({ delay: [200, 100] });
89
+ mock("GET /test", "response");
90
+
91
+ const start = Date.now();
92
+ const response = await mock.handle("GET", "/test");
93
+ const duration = Date.now() - start;
94
+
95
+ expect(response.body).toBe("response");
96
+ // Should still work, treating it as [100, 200]
97
+ expect(duration).toBeGreaterThanOrEqual(0);
98
+ expect(duration).toBeLessThan(300);
99
+ });
100
+
101
+ it("handles equal min and max values", async () => {
102
+ const mock = schmock({ delay: [100, 100] });
103
+ mock("GET /test", "response");
104
+
105
+ const start = Date.now();
106
+ const response = await mock.handle("GET", "/test");
107
+ const duration = Date.now() - start;
108
+
109
+ expect(response.body).toBe("response");
110
+ expect(duration).toBeGreaterThanOrEqual(95);
111
+ expect(duration).toBeLessThan(150);
112
+ });
113
+ });
114
+
115
+ describe("delay with different response types", () => {
116
+ it("applies delay to function generators", async () => {
117
+ const mock = schmock({ delay: 50 });
118
+ mock("GET /dynamic", () => ({ timestamp: Date.now() }));
119
+
120
+ const start = Date.now();
121
+ const response = await mock.handle("GET", "/dynamic");
122
+ const duration = Date.now() - start;
123
+
124
+ expect(response.body).toHaveProperty("timestamp");
125
+ expect(duration).toBeGreaterThanOrEqual(45);
126
+ });
127
+
128
+ it("applies delay to static responses", async () => {
129
+ const mock = schmock({ delay: 50 });
130
+ mock("GET /static", { data: "static" });
131
+
132
+ const start = Date.now();
133
+ const response = await mock.handle("GET", "/static");
134
+ const duration = Date.now() - start;
135
+
136
+ expect(response.body).toEqual({ data: "static" });
137
+ expect(duration).toBeGreaterThanOrEqual(45);
138
+ });
139
+
140
+ it("applies delay to tuple responses", async () => {
141
+ const mock = schmock({ delay: 50 });
142
+ mock("GET /tuple", () => [201, { created: true }]);
143
+
144
+ const start = Date.now();
145
+ const response = await mock.handle("GET", "/tuple");
146
+ const duration = Date.now() - start;
147
+
148
+ expect(response.status).toBe(201);
149
+ expect(response.body).toEqual({ created: true });
150
+ expect(duration).toBeGreaterThanOrEqual(45);
151
+ });
152
+
153
+ it("applies delay to error responses", async () => {
154
+ const mock = schmock({ delay: 50 });
155
+ mock("GET /error", () => {
156
+ throw new Error("Test error");
157
+ });
158
+
159
+ const start = Date.now();
160
+ const response = await mock.handle("GET", "/error");
161
+ const duration = Date.now() - start;
162
+
163
+ expect(response.status).toBe(500);
164
+ expect(duration).toBeGreaterThanOrEqual(45);
165
+ });
166
+ });
167
+
168
+ describe("delay with plugins", () => {
169
+ it("applies delay after plugin processing", async () => {
170
+ const mock = schmock({ delay: 50 });
171
+
172
+ const plugin = {
173
+ name: "slow-plugin",
174
+ process: async (ctx: any, res: any) => {
175
+ await new Promise((resolve) => setTimeout(resolve, 30));
176
+ return { context: ctx, response: `processed-${res}` };
177
+ },
178
+ };
179
+
180
+ mock("GET /test", "original").pipe(plugin);
181
+
182
+ const start = Date.now();
183
+ const response = await mock.handle("GET", "/test");
184
+ const duration = Date.now() - start;
185
+
186
+ expect(response.body).toBe("processed-original");
187
+ expect(duration).toBeGreaterThanOrEqual(75); // Plugin delay + response delay
188
+ });
189
+
190
+ it("applies delay even when plugin generates response", async () => {
191
+ const mock = schmock({ delay: 50 });
192
+
193
+ const plugin = {
194
+ name: "generator-plugin",
195
+ process: (ctx: any, _res: any) => {
196
+ return { context: ctx, response: "plugin-generated" };
197
+ },
198
+ };
199
+
200
+ mock("GET /test", null).pipe(plugin);
201
+
202
+ const start = Date.now();
203
+ const response = await mock.handle("GET", "/test");
204
+ const duration = Date.now() - start;
205
+
206
+ expect(response.body).toBe("plugin-generated");
207
+ expect(duration).toBeGreaterThanOrEqual(45);
208
+ });
209
+ });
210
+
211
+ describe("no delay configuration", () => {
212
+ it("doesn't apply delay when not configured", async () => {
213
+ const mock = schmock();
214
+ mock("GET /test", "response");
215
+
216
+ const start = Date.now();
217
+ const response = await mock.handle("GET", "/test");
218
+ const duration = Date.now() - start;
219
+
220
+ expect(response.body).toBe("response");
221
+ expect(duration).toBeLessThan(30); // Should be very fast without delay
222
+ });
223
+
224
+ it("doesn't apply delay when delay is undefined", async () => {
225
+ const mock = schmock({ delay: undefined });
226
+ mock("GET /test", "response");
227
+
228
+ const start = Date.now();
229
+ const response = await mock.handle("GET", "/test");
230
+ const duration = Date.now() - start;
231
+
232
+ expect(response.body).toBe("response");
233
+ expect(duration).toBeLessThan(30);
234
+ });
235
+ });
236
+
237
+ describe("delay edge cases", () => {
238
+ it("handles very small delays", async () => {
239
+ const mock = schmock({ delay: 1 });
240
+ mock("GET /test", "response");
241
+
242
+ const start = Date.now();
243
+ const response = await mock.handle("GET", "/test");
244
+ const duration = Date.now() - start;
245
+
246
+ expect(response.body).toBe("response");
247
+ expect(duration).toBeGreaterThanOrEqual(0);
248
+ expect(duration).toBeLessThan(50);
249
+ });
250
+
251
+ it("handles large delays", async () => {
252
+ const mock = schmock({ delay: 500 });
253
+ mock("GET /test", "response");
254
+
255
+ const start = Date.now();
256
+ const response = await mock.handle("GET", "/test");
257
+ const duration = Date.now() - start;
258
+
259
+ expect(response.body).toBe("response");
260
+ expect(duration).toBeGreaterThanOrEqual(490);
261
+ expect(duration).toBeLessThan(600);
262
+ }, 1000); // Increase test timeout
263
+
264
+ it("handles negative delays gracefully", async () => {
265
+ const mock = schmock({ delay: -50 });
266
+ mock("GET /test", "response");
267
+
268
+ const start = Date.now();
269
+ const response = await mock.handle("GET", "/test");
270
+ const duration = Date.now() - start;
271
+
272
+ expect(response.body).toBe("response");
273
+ // Should not crash, and should be fast
274
+ expect(duration).toBeLessThan(50);
275
+ });
276
+
277
+ it("handles delay range with negative values", async () => {
278
+ const mock = schmock({ delay: [-10, 50] });
279
+ mock("GET /test", "response");
280
+
281
+ const start = Date.now();
282
+ const response = await mock.handle("GET", "/test");
283
+ const duration = Date.now() - start;
284
+
285
+ expect(response.body).toBe("response");
286
+ // Should handle gracefully
287
+ expect(duration).toBeGreaterThanOrEqual(0);
288
+ expect(duration).toBeLessThan(100);
289
+ });
290
+ });
291
+
292
+ describe("concurrent requests with delay", () => {
293
+ it("applies delay to concurrent requests independently", async () => {
294
+ const mock = schmock({ delay: 100 });
295
+ mock("GET /test", "response");
296
+
297
+ const start = Date.now();
298
+
299
+ // Start multiple requests concurrently
300
+ const promises = [
301
+ mock.handle("GET", "/test"),
302
+ mock.handle("GET", "/test"),
303
+ mock.handle("GET", "/test"),
304
+ ];
305
+
306
+ const responses = await Promise.all(promises);
307
+ const duration = Date.now() - start;
308
+
309
+ // All should succeed
310
+ responses.forEach((response) => {
311
+ expect(response.body).toBe("response");
312
+ });
313
+
314
+ // Should take about 100ms, not 300ms (concurrent, not sequential)
315
+ expect(duration).toBeGreaterThanOrEqual(95);
316
+ expect(duration).toBeLessThan(200);
317
+ });
318
+ });
319
+ });
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,YAAa,SAAQ,KAAK;aAGnB,IAAI,EAAE,MAAM;aACZ,OAAO,CAAC,EAAE,OAAO;gBAFjC,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,OAAO,YAAA;CAMpC;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,YAAY;gBACtC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAQzC;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,YAAY;gBACnC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAQ7C;AAED;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,YAAY;gBAC3C,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK;CAQxC;AAED;;GAEG;AACH,qBAAa,WAAY,SAAQ,YAAY;gBAC/B,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK;CAQ7C;AAED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,YAAY;gBACxC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAQ7C;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,YAAY;gBACzC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;CAQnE;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,YAAY;gBACzC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,OAAO;CAQ1D;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,YAAY;gBACtC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;CAQ7D"}