@prosopo/api-express-router 3.1.50 → 3.1.52

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 (52) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +14 -12
  2. package/.turbo/turbo-build$colon$tsc.log +21 -21
  3. package/.turbo/turbo-build.log +16 -13
  4. package/CHANGELOG.md +23 -0
  5. package/dist/_virtual/_rolldown/runtime.js +3 -0
  6. package/dist/apiExpressRouterFactory.js +17 -27
  7. package/dist/cjs/apiExpressRouterFactory.cjs +18 -28
  8. package/dist/cjs/endpointAdapter/apiExpressDefaultEndpointAdapter.cjs +29 -35
  9. package/dist/cjs/errorHandler.cjs +10 -10
  10. package/dist/cjs/index.cjs +14 -13
  11. package/dist/cjs/middlewares/authMiddleware.cjs +54 -56
  12. package/dist/cjs/middlewares/requestLoggerMiddleware.cjs +57 -59
  13. package/dist/endpointAdapter/apiExpressDefaultEndpointAdapter.js +26 -33
  14. package/dist/errorHandler.js +10 -10
  15. package/dist/index.js +8 -12
  16. package/dist/middlewares/authMiddleware.js +54 -57
  17. package/dist/middlewares/requestLoggerMiddleware.js +56 -58
  18. package/dist/tests/apiExpressRouter.test-d.d.ts +2 -0
  19. package/dist/tests/apiExpressRouter.test-d.d.ts.map +1 -0
  20. package/dist/tests/apiExpressRouter.test-d.js +76 -0
  21. package/dist/tests/apiExpressRouter.test-d.js.map +1 -0
  22. package/dist/tests/unit/apiExpressDefaultEndpointAdapter.unit.test.d.ts +2 -0
  23. package/dist/tests/unit/apiExpressDefaultEndpointAdapter.unit.test.d.ts.map +1 -0
  24. package/dist/tests/unit/apiExpressDefaultEndpointAdapter.unit.test.js +197 -0
  25. package/dist/tests/unit/apiExpressDefaultEndpointAdapter.unit.test.js.map +1 -0
  26. package/dist/tests/unit/apiExpressRouterFactory.unit.test.d.ts +2 -0
  27. package/dist/tests/unit/apiExpressRouterFactory.unit.test.d.ts.map +1 -0
  28. package/dist/tests/unit/apiExpressRouterFactory.unit.test.js +108 -0
  29. package/dist/tests/unit/apiExpressRouterFactory.unit.test.js.map +1 -0
  30. package/dist/tests/unit/index.unit.test.d.ts +2 -0
  31. package/dist/tests/unit/index.unit.test.d.ts.map +1 -0
  32. package/dist/tests/unit/index.unit.test.js +44 -0
  33. package/dist/tests/unit/index.unit.test.js.map +1 -0
  34. package/dist/tests/unit/middlewares/authMiddleware.unit.test.js +210 -136
  35. package/dist/tests/unit/middlewares/authMiddleware.unit.test.js.map +1 -1
  36. package/dist/tests/unit/middlewares/requestLoggerMiddleware.unit.test.d.ts +2 -0
  37. package/dist/tests/unit/middlewares/requestLoggerMiddleware.unit.test.d.ts.map +1 -0
  38. package/dist/tests/unit/middlewares/requestLoggerMiddleware.unit.test.js +242 -0
  39. package/dist/tests/unit/middlewares/requestLoggerMiddleware.unit.test.js.map +1 -0
  40. package/dist/tests/unit/testDoubles.d.ts +8 -0
  41. package/dist/tests/unit/testDoubles.d.ts.map +1 -0
  42. package/dist/tests/unit/testDoubles.js +11 -0
  43. package/dist/tests/unit/testDoubles.js.map +1 -0
  44. package/package.json +10 -10
  45. package/src/tests/apiExpressRouter.test-d.ts +190 -0
  46. package/src/tests/unit/apiExpressDefaultEndpointAdapter.unit.test.ts +395 -0
  47. package/src/tests/unit/apiExpressRouterFactory.unit.test.ts +215 -0
  48. package/src/tests/unit/index.unit.test.ts +95 -0
  49. package/src/tests/unit/middlewares/authMiddleware.unit.test.ts +318 -159
  50. package/src/tests/unit/middlewares/requestLoggerMiddleware.unit.test.ts +368 -0
  51. package/src/tests/unit/testDoubles.ts +39 -0
  52. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,197 @@
1
+ import { ApiEndpointResponseStatus, } from "@prosopo/api-route";
2
+ import { ProsopoApiError } from "@prosopo/common";
3
+ import { LogLevel } from "@prosopo/logger";
4
+ import { beforeEach, describe, expect, test, vi } from "vitest";
5
+ import { z } from "zod";
6
+ import { ApiExpressDefaultEndpointAdapter } from "../../endpointAdapter/apiExpressDefaultEndpointAdapter.js";
7
+ import { captureNext } from "./testDoubles.js";
8
+ const build = (body = {}, errorStatusCode = 500) => {
9
+ const logged = vi.fn();
10
+ const logger = {
11
+ error: logged,
12
+ info: vi.fn(),
13
+ debug: vi.fn(),
14
+ warn: vi.fn(),
15
+ };
16
+ const json = vi.fn();
17
+ const send = vi.fn();
18
+ const status = vi.fn();
19
+ const response = { json, send, status };
20
+ status.mockReturnValue(response);
21
+ return {
22
+ adapter: new ApiExpressDefaultEndpointAdapter(LogLevel.enum.info, errorStatusCode),
23
+ request: { body, logger },
24
+ response,
25
+ next: captureNext(),
26
+ logger,
27
+ json,
28
+ status,
29
+ send,
30
+ logged,
31
+ };
32
+ };
33
+ const endpoint = (schema, process = async () => ({
34
+ status: ApiEndpointResponseStatus.SUCCESS,
35
+ })) => ({
36
+ getRequestArgsSchema: () => schema,
37
+ processRequest: vi.fn(process),
38
+ });
39
+ let harness;
40
+ beforeEach(() => {
41
+ harness = build();
42
+ });
43
+ describe("a request the endpoint accepts", () => {
44
+ test("parses the body and passes the parsed value on", async () => {
45
+ harness = build({ count: "5" });
46
+ const schema = z.object({ count: z.coerce.number() });
47
+ const target = endpoint(schema);
48
+ await harness.adapter.handleRequest(target, harness.request, harness.response, harness.next.fn);
49
+ expect(target.processRequest).toHaveBeenCalledWith({ count: 5 }, harness.logger);
50
+ });
51
+ test("passes undefined args when the endpoint declares no schema", async () => {
52
+ const target = endpoint(undefined);
53
+ await harness.adapter.handleRequest(target, harness.request, harness.response, harness.next.fn);
54
+ expect(target.processRequest).toHaveBeenCalledWith(undefined, harness.logger);
55
+ });
56
+ test("hands the endpoint the request's own logger", async () => {
57
+ const target = endpoint(undefined);
58
+ await harness.adapter.handleRequest(target, harness.request, harness.response, harness.next.fn);
59
+ expect(vi.mocked(target.processRequest).mock.calls[0]?.[1]).toBe(harness.logger);
60
+ });
61
+ test("answers with the endpoint's response as JSON", async () => {
62
+ await harness.adapter.handleRequest(endpoint(undefined, async () => ({
63
+ status: ApiEndpointResponseStatus.SUCCESS,
64
+ data: { value: 1 },
65
+ })), harness.request, harness.response, harness.next.fn);
66
+ expect(harness.json).toHaveBeenCalledWith({
67
+ status: ApiEndpointResponseStatus.SUCCESS,
68
+ data: { value: 1 },
69
+ });
70
+ expect(harness.next.calls).toHaveLength(0);
71
+ });
72
+ test("stringifies bigints, which res.json() cannot serialise", async () => {
73
+ await harness.adapter.handleRequest(endpoint(undefined, async () => ({
74
+ status: ApiEndpointResponseStatus.SUCCESS,
75
+ data: { big: 10n },
76
+ })), harness.request, harness.response, harness.next.fn);
77
+ expect(harness.json).toHaveBeenCalledWith({
78
+ status: ApiEndpointResponseStatus.SUCCESS,
79
+ data: { big: "10" },
80
+ });
81
+ });
82
+ test("passes an empty data payload straight through", async () => {
83
+ await harness.adapter.handleRequest(endpoint(undefined, async () => ({
84
+ status: ApiEndpointResponseStatus.SUCCESS,
85
+ data: {},
86
+ })), harness.request, harness.response, harness.next.fn);
87
+ expect(harness.json).toHaveBeenCalledWith({
88
+ status: ApiEndpointResponseStatus.SUCCESS,
89
+ data: {},
90
+ });
91
+ });
92
+ test("an absent request body is still handed to the schema", async () => {
93
+ harness.request.body = undefined;
94
+ const schema = z.object({ a: z.string() }).optional();
95
+ const target = endpoint(schema);
96
+ await harness.adapter.handleRequest(target, harness.request, harness.response, harness.next.fn);
97
+ expect(target.processRequest).toHaveBeenCalledWith(undefined, harness.logger);
98
+ expect(harness.json).toHaveBeenCalledWith({
99
+ status: ApiEndpointResponseStatus.SUCCESS,
100
+ });
101
+ });
102
+ });
103
+ describe("a body the schema rejects", () => {
104
+ test("goes to the error handler rather than being answered here", async () => {
105
+ harness = build({ count: "not a number" });
106
+ const target = endpoint(z.object({ count: z.number() }));
107
+ await harness.adapter.handleRequest(target, harness.request, harness.response, harness.next.fn);
108
+ expect(harness.next.calls).toHaveLength(1);
109
+ expect(harness.json).not.toHaveBeenCalled();
110
+ expect(target.processRequest).not.toHaveBeenCalled();
111
+ });
112
+ test("is reported as a 400 parse error carrying the cause", async () => {
113
+ harness = build({ count: "not a number" });
114
+ await harness.adapter.handleRequest(endpoint(z.object({ count: z.number() })), harness.request, harness.response, harness.next.fn);
115
+ const [forwarded] = harness.next.calls[0] ?? [];
116
+ expect(forwarded).toBeInstanceOf(ProsopoApiError);
117
+ const error = forwarded;
118
+ expect(error.context?.code).toBe(400);
119
+ expect(error.context?.error).toBeDefined();
120
+ });
121
+ test("a schema that throws something other than a ZodError is treated the same", async () => {
122
+ const schema = {
123
+ parse: () => {
124
+ throw new Error("custom validator exploded");
125
+ },
126
+ };
127
+ await harness.adapter.handleRequest(endpoint(schema), harness.request, harness.response, harness.next.fn);
128
+ expect(harness.next.calls).toHaveLength(1);
129
+ expect(harness.next.calls[0]?.[0]).toBeInstanceOf(ProsopoApiError);
130
+ });
131
+ test("an endpoint that fails while building its schema is caught too", async () => {
132
+ const target = {
133
+ getRequestArgsSchema: () => {
134
+ throw new Error("schema construction failed");
135
+ },
136
+ processRequest: vi.fn(),
137
+ };
138
+ await harness.adapter.handleRequest(target, harness.request, harness.response, harness.next.fn);
139
+ expect(harness.next.calls[0]?.[0]).toBeInstanceOf(ProsopoApiError);
140
+ expect(target.processRequest).not.toHaveBeenCalled();
141
+ });
142
+ });
143
+ describe("an endpoint that fails", () => {
144
+ test("is answered with a 500 and a generic message", async () => {
145
+ await harness.adapter.handleRequest(endpoint(undefined, async () => {
146
+ throw new Error("database down");
147
+ }), harness.request, harness.response, harness.next.fn);
148
+ expect(harness.status).toHaveBeenCalledWith(500);
149
+ expect(harness.send).toHaveBeenCalledWith("An internal server error occurred.");
150
+ });
151
+ test("does not leak the failure's message to the client", async () => {
152
+ await harness.adapter.handleRequest(endpoint(undefined, async () => {
153
+ throw new Error("connection string: postgres://user:secret@host");
154
+ }), harness.request, harness.response, harness.next.fn);
155
+ expect(String(harness.send.mock.calls[0]?.[0])).not.toContain("secret");
156
+ });
157
+ test("logs the failure against the request's logger", async () => {
158
+ const failure = new Error("database down");
159
+ await harness.adapter.handleRequest(endpoint(undefined, async () => {
160
+ throw failure;
161
+ }), harness.request, harness.response, harness.next.fn);
162
+ expect(harness.logged).toHaveBeenCalledTimes(1);
163
+ const entry = harness.logged.mock.calls[0]?.[0];
164
+ expect(entry?.()).toEqual({ err: failure });
165
+ });
166
+ test("does not also call next(), so the error handler is not double-run", async () => {
167
+ await harness.adapter.handleRequest(endpoint(undefined, async () => {
168
+ throw new Error("database down");
169
+ }), harness.request, harness.response, harness.next.fn);
170
+ expect(harness.next.calls).toHaveLength(0);
171
+ });
172
+ test("ignores the configured errorStatusCode and always answers 500", async () => {
173
+ harness = build({}, 503);
174
+ await harness.adapter.handleRequest(endpoint(undefined, async () => {
175
+ throw new Error("unavailable");
176
+ }), harness.request, harness.response, harness.next.fn);
177
+ expect(harness.status).toHaveBeenCalledWith(500);
178
+ expect(harness.status).not.toHaveBeenCalledWith(503);
179
+ });
180
+ test("a rejection with a non-Error value is handled just the same", async () => {
181
+ await harness.adapter.handleRequest(endpoint(undefined, async () => {
182
+ throw "a bare string";
183
+ }), harness.request, harness.response, harness.next.fn);
184
+ expect(harness.status).toHaveBeenCalledWith(500);
185
+ expect(harness.logged).toHaveBeenCalledTimes(1);
186
+ });
187
+ test("a failure while serialising the response is caught too", async () => {
188
+ const hostile = {
189
+ get status() {
190
+ throw new Error("getter exploded");
191
+ },
192
+ };
193
+ await harness.adapter.handleRequest(endpoint(undefined, async () => hostile), harness.request, harness.response, harness.next.fn);
194
+ expect(harness.status).toHaveBeenCalledWith(500);
195
+ });
196
+ });
197
+ //# sourceMappingURL=apiExpressDefaultEndpointAdapter.unit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiExpressDefaultEndpointAdapter.unit.test.js","sourceRoot":"","sources":["../../../src/tests/unit/apiExpressDefaultEndpointAdapter.unit.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=apiExpressRouterFactory.unit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiExpressRouterFactory.unit.test.d.ts","sourceRoot":"","sources":["../../../src/tests/unit/apiExpressRouterFactory.unit.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,108 @@
1
+ import { ApiEndpointResponseStatus, } from "@prosopo/api-route";
2
+ import { beforeEach, describe, expect, test, vi } from "vitest";
3
+ import { ApiExpressRouterFactory } from "../../apiExpressRouterFactory.js";
4
+ import { handleErrors } from "../../errorHandler.js";
5
+ import { captureNext } from "./testDoubles.js";
6
+ const endpoint = () => ({
7
+ getRequestArgsSchema: vi.fn(() => undefined),
8
+ processRequest: vi.fn(async () => ({ status: ApiEndpointResponseStatus.SUCCESS })),
9
+ });
10
+ const provider = (routes) => ({
11
+ getRoutes: () => routes,
12
+ });
13
+ const adapter = () => ({
14
+ handleRequest: vi.fn(async () => undefined),
15
+ });
16
+ const postRoutes = (router) => {
17
+ const registered = [];
18
+ for (const layer of router.stack) {
19
+ const route = layer.route;
20
+ if (!route)
21
+ continue;
22
+ const handler = route.stack[route.stack.length - 1]?.handle;
23
+ if (handler)
24
+ registered.push([route.path, handler]);
25
+ }
26
+ return registered;
27
+ };
28
+ let factory;
29
+ beforeEach(() => {
30
+ factory = new ApiExpressRouterFactory();
31
+ });
32
+ describe("createRouter", () => {
33
+ test("registers one POST route per entry the provider declares", () => {
34
+ const router = factory.createRouter(provider({ "/a": endpoint(), "/b": endpoint() }), adapter());
35
+ expect(postRoutes(router).map(([path]) => path)).toEqual(["/a", "/b"]);
36
+ });
37
+ test("registers the routes as POST only", () => {
38
+ const router = factory.createRouter(provider({ "/a": endpoint() }), adapter());
39
+ const route = router.stack.find((layer) => layer.route)?.route;
40
+ expect(route?.methods).toEqual({ post: true });
41
+ });
42
+ test("a provider with no routes still yields a router", () => {
43
+ const router = factory.createRouter(provider({}), adapter());
44
+ expect(postRoutes(router)).toEqual([]);
45
+ expect(router.stack).toHaveLength(1);
46
+ });
47
+ test("mounts the error handler last, after every route", () => {
48
+ const router = factory.createRouter(provider({ "/a": endpoint(), "/b": endpoint() }), adapter());
49
+ const last = router.stack[router.stack.length - 1];
50
+ expect(last?.handle).toBe(handleErrors);
51
+ expect(last?.route).toBeUndefined();
52
+ });
53
+ test("asks the provider for its routes exactly once", () => {
54
+ const getRoutes = vi.fn(() => ({ "/a": endpoint() }));
55
+ factory.createRouter({ getRoutes }, adapter());
56
+ expect(getRoutes).toHaveBeenCalledTimes(1);
57
+ });
58
+ test("each call builds a fresh router", () => {
59
+ const routes = provider({ "/a": endpoint() });
60
+ expect(factory.createRouter(routes, adapter())).not.toBe(factory.createRouter(routes, adapter()));
61
+ });
62
+ test("routes named oddly are passed to express verbatim", () => {
63
+ const router = factory.createRouter(provider({ "/": endpoint(), "/nested/:id": endpoint() }), adapter());
64
+ expect(postRoutes(router).map(([path]) => path)).toEqual([
65
+ "/",
66
+ "/nested/:id",
67
+ ]);
68
+ });
69
+ });
70
+ describe("the handler each route is given", () => {
71
+ test("hands the endpoint and the express trio to the adapter", async () => {
72
+ const target = endpoint();
73
+ const endpointAdapter = adapter();
74
+ const router = factory.createRouter(provider({ "/a": target }), endpointAdapter);
75
+ const request = {};
76
+ const response = {};
77
+ const next = captureNext();
78
+ const handler = postRoutes(router)[0]?.[1];
79
+ await handler?.(request, response, next.fn);
80
+ expect(endpointAdapter.handleRequest).toHaveBeenCalledWith(target, request, response, next.fn);
81
+ });
82
+ test("routes each path to its own endpoint, not the last one registered", () => {
83
+ const first = endpoint();
84
+ const second = endpoint();
85
+ const endpointAdapter = adapter();
86
+ const router = factory.createRouter(provider({ "/first": first, "/second": second }), endpointAdapter);
87
+ for (const [, handler] of postRoutes(router)) {
88
+ void handler({}, {}, captureNext().fn);
89
+ }
90
+ const handled = vi.mocked(endpointAdapter.handleRequest).mock.calls;
91
+ expect(handled.map(([called]) => called)).toEqual([first, second]);
92
+ });
93
+ test("propagates a rejection from the adapter to the caller", async () => {
94
+ const endpointAdapter = adapter();
95
+ vi.mocked(endpointAdapter.handleRequest).mockRejectedValue(new Error("adapter blew up"));
96
+ const router = factory.createRouter(provider({ "/a": endpoint() }), endpointAdapter);
97
+ const handler = postRoutes(router)[0]?.[1];
98
+ await expect(handler?.({}, {}, captureNext().fn)).rejects.toThrow("adapter blew up");
99
+ });
100
+ test("does not call the endpoint itself — that is the adapter's job", async () => {
101
+ const target = endpoint();
102
+ const router = factory.createRouter(provider({ "/a": target }), adapter());
103
+ const handler = postRoutes(router)[0]?.[1];
104
+ await handler?.({}, {}, captureNext().fn);
105
+ expect(target.processRequest).not.toHaveBeenCalled();
106
+ });
107
+ });
108
+ //# sourceMappingURL=apiExpressRouterFactory.unit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiExpressRouterFactory.unit.test.js","sourceRoot":"","sources":["../../../src/tests/unit/apiExpressRouterFactory.unit.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.unit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.unit.test.d.ts","sourceRoot":"","sources":["../../../src/tests/unit/index.unit.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,44 @@
1
+ import { LogLevel } from "@prosopo/logger";
2
+ import { describe, expect, test, vi } from "vitest";
3
+ import { apiExpressRouterFactory, authMiddleware, createApiExpressDefaultEndpointAdapter, handleErrors, requestLoggerMiddleware, } from "../../index.js";
4
+ describe("createApiExpressDefaultEndpointAdapter", () => {
5
+ test("builds an adapter exposing handleRequest", () => {
6
+ const adapter = createApiExpressDefaultEndpointAdapter(LogLevel.enum.info);
7
+ expect(typeof adapter.handleRequest).toBe("function");
8
+ });
9
+ test("builds a fresh adapter per call", () => {
10
+ expect(createApiExpressDefaultEndpointAdapter(LogLevel.enum.info)).not.toBe(createApiExpressDefaultEndpointAdapter(LogLevel.enum.info));
11
+ });
12
+ test("defaults the error status code so callers may omit it", () => {
13
+ expect(() => createApiExpressDefaultEndpointAdapter(LogLevel.enum.debug)).not.toThrow();
14
+ });
15
+ test("accepts an explicit error status code", () => {
16
+ expect(() => createApiExpressDefaultEndpointAdapter(LogLevel.enum.debug, 503)).not.toThrow();
17
+ });
18
+ });
19
+ describe("apiExpressRouterFactory", () => {
20
+ test("is exported as a single shared instance", () => {
21
+ expect(typeof apiExpressRouterFactory.createRouter).toBe("function");
22
+ });
23
+ test("builds a router from a provider", () => {
24
+ const provider = {
25
+ getRoutes: () => ({}),
26
+ };
27
+ const router = apiExpressRouterFactory.createRouter(provider, {
28
+ handleRequest: vi.fn(async () => undefined),
29
+ });
30
+ expect(typeof router).toBe("function");
31
+ });
32
+ });
33
+ describe("the re-exported middleware", () => {
34
+ test("exposes the error handler", () => {
35
+ expect(typeof handleErrors).toBe("function");
36
+ });
37
+ test("exposes the auth middleware", () => {
38
+ expect(typeof authMiddleware).toBe("function");
39
+ });
40
+ test("exposes the request logger middleware", () => {
41
+ expect(typeof requestLoggerMiddleware).toBe("function");
42
+ });
43
+ });
44
+ //# sourceMappingURL=index.unit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.unit.test.js","sourceRoot":"","sources":["../../../src/tests/unit/index.unit.test.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACpD,OAAO,EACN,uBAAuB,EACvB,cAAc,EACd,sCAAsC,EACtC,YAAY,EACZ,uBAAuB,GACvB,MAAM,gBAAgB,CAAC;AAQxB,QAAQ,CAAC,wCAAwC,EAAE,GAAG,EAAE;IACvD,IAAI,CAAC,0CAA0C,EAAE,GAAG,EAAE;QACrD,MAAM,OAAO,GAAG,sCAAsC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3E,MAAM,CAAC,OAAO,OAAO,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,iCAAiC,EAAE,GAAG,EAAE;QAC5C,MAAM,CAAC,sCAAsC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAC1E,sCAAsC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAC1D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAClE,MAAM,CAAC,GAAG,EAAE,CACX,sCAAsC,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAC3D,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,GAAG,EAAE,CACX,sCAAsC,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAChE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACxC,IAAI,CAAC,yCAAyC,EAAE,GAAG,EAAE;QAGpD,MAAM,CAAC,OAAO,uBAAuB,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,iCAAiC,EAAE,GAAG,EAAE;QAC5C,MAAM,QAAQ,GAAsB;YACnC,SAAS,EAAE,GAAc,EAAE,CAAC,CAAC,EAAE,CAAC;SAChC,CAAC;QACF,MAAM,MAAM,GAAG,uBAAuB,CAAC,YAAY,CAAC,QAAQ,EAAE;YAC7D,aAAa,EAAE,EAAE,CAAC,EAAE,CAOlB,KAAK,IAAI,EAAE,CAAC,SAAS,CAAC;SACxB,CAAC,CAAC;QACH,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC3C,IAAI,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACtC,MAAM,CAAC,OAAO,YAAY,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACxC,MAAM,CAAC,OAAO,cAAc,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,OAAO,uBAAuB,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC"}