@ankhorage/data-sources 0.6.0 → 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 (49) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +5 -5
  3. package/dist/discovery/index.d.ts +3 -3
  4. package/dist/discovery/index.d.ts.map +1 -1
  5. package/dist/discovery/index.js +19 -28
  6. package/dist/discovery/index.js.map +1 -1
  7. package/dist/generated-api/index.d.ts +10 -0
  8. package/dist/generated-api/index.d.ts.map +1 -0
  9. package/dist/generated-api/index.js +272 -0
  10. package/dist/generated-api/index.js.map +1 -0
  11. package/dist/graphql/index.d.ts +3 -3
  12. package/dist/graphql/index.d.ts.map +1 -1
  13. package/dist/graphql/index.js +24 -60
  14. package/dist/graphql/index.js.map +1 -1
  15. package/dist/index.d.ts +9 -3
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +17 -4
  18. package/dist/index.js.map +1 -1
  19. package/dist/openapi/index.d.ts +2 -2
  20. package/dist/openapi/index.d.ts.map +1 -1
  21. package/dist/openapi/index.js +19 -29
  22. package/dist/openapi/index.js.map +1 -1
  23. package/dist/rest/index.d.ts +3 -3
  24. package/dist/rest/index.d.ts.map +1 -1
  25. package/dist/rest/index.js +51 -51
  26. package/dist/rest/index.js.map +1 -1
  27. package/dist/test-runner/index.d.ts.map +1 -1
  28. package/dist/test-runner/index.js +60 -104
  29. package/dist/test-runner/index.js.map +1 -1
  30. package/package.json +2 -2
  31. package/src/discovery/index.test.ts +25 -101
  32. package/src/discovery/index.ts +22 -35
  33. package/src/{managed-api → generated-api}/index.test.ts +59 -67
  34. package/src/generated-api/index.ts +410 -0
  35. package/src/graphql/index.test.ts +28 -134
  36. package/src/graphql/index.ts +29 -89
  37. package/src/index.test.ts +25 -18
  38. package/src/index.ts +28 -7
  39. package/src/openapi/index.test.ts +36 -246
  40. package/src/openapi/index.ts +22 -60
  41. package/src/rest/index.test.ts +22 -173
  42. package/src/rest/index.ts +62 -59
  43. package/src/test-runner/index.test.ts +82 -150
  44. package/src/test-runner/index.ts +73 -143
  45. package/dist/managed-api/index.d.ts +0 -30
  46. package/dist/managed-api/index.d.ts.map +0 -1
  47. package/dist/managed-api/index.js +0 -257
  48. package/dist/managed-api/index.js.map +0 -1
  49. package/src/managed-api/index.ts +0 -364
@@ -90,23 +90,17 @@ interface EndpointOperationSelection {
90
90
 
91
91
  export async function testEndpoint(input: EndpointTestInput): Promise<EndpointTestResult> {
92
92
  const built = await buildEndpointTestRequest(input);
93
-
94
- if (!built.ok) return built;
95
- if (input.dryRun === true) return built;
93
+ if (!built.ok || input.dryRun === true) return built;
96
94
 
97
95
  if (input.fetch === undefined) {
98
- return {
99
- ok: false,
100
- request: built.request,
101
- diagnostics: [
102
- ...built.diagnostics,
103
- createDiagnostic(
104
- input,
105
- 'invalid-config',
106
- 'Endpoint test runner requires an injected fetch function.',
107
- ),
108
- ],
109
- };
96
+ return failure(built.request, [
97
+ ...built.diagnostics,
98
+ createDiagnostic(
99
+ input,
100
+ 'invalid-config',
101
+ 'Endpoint test runner requires an injected fetch function.',
102
+ ),
103
+ ]);
110
104
  }
111
105
 
112
106
  try {
@@ -117,7 +111,6 @@ export async function testEndpoint(input: EndpointTestInput): Promise<EndpointTe
117
111
  });
118
112
  const responseDiagnostic = await createResponseDiagnostic(response);
119
113
  const diagnostics = [...built.diagnostics];
120
-
121
114
  if (!responseDiagnostic.ok) {
122
115
  diagnostics.push(
123
116
  createDiagnostic(
@@ -127,7 +120,6 @@ export async function testEndpoint(input: EndpointTestInput): Promise<EndpointTe
127
120
  ),
128
121
  );
129
122
  }
130
-
131
123
  return responseDiagnostic.ok
132
124
  ? {
133
125
  ok: true,
@@ -136,21 +128,12 @@ export async function testEndpoint(input: EndpointTestInput): Promise<EndpointTe
136
128
  data: responseDiagnostic.parsedBody,
137
129
  diagnostics,
138
130
  }
139
- : {
140
- ok: false,
141
- request: built.request,
142
- response: responseDiagnostic,
143
- diagnostics,
144
- };
131
+ : failure(built.request, diagnostics, responseDiagnostic);
145
132
  } catch (error) {
146
- return {
147
- ok: false,
148
- request: built.request,
149
- diagnostics: [
150
- ...built.diagnostics,
151
- createDiagnostic(input, 'network-error', createNetworkErrorMessage(error)),
152
- ],
153
- };
133
+ return failure(built.request, [
134
+ ...built.diagnostics,
135
+ createDiagnostic(input, 'network-error', createNetworkErrorMessage(error)),
136
+ ]);
154
137
  }
155
138
  }
156
139
 
@@ -159,12 +142,19 @@ export async function buildEndpointTestRequest(
159
142
  ): Promise<EndpointTestResult> {
160
143
  const selection = selectEndpointOperation(input);
161
144
  if (selection === undefined) {
162
- return {
163
- ok: false,
164
- diagnostics: [
165
- createDiagnostic(input, 'missing-operation', 'Endpoint or operation could not be found.'),
166
- ],
167
- };
145
+ return failure(undefined, [
146
+ createDiagnostic(input, 'missing-operation', 'Endpoint or operation could not be found.'),
147
+ ]);
148
+ }
149
+
150
+ if (selection.operation.protocol === 'database') {
151
+ return failure(undefined, [
152
+ createDiagnostic(
153
+ input,
154
+ 'invalid-config',
155
+ 'Database-backed generated API operations require an adapter executor and cannot be tested through the HTTP endpoint runner.',
156
+ ),
157
+ ]);
168
158
  }
169
159
 
170
160
  const diagnostics: DataSourceDiagnostic[] = [];
@@ -175,27 +165,15 @@ export async function buildEndpointTestRequest(
175
165
  : buildHttpRequest(input, selection, credential, diagnostics);
176
166
 
177
167
  if (request === undefined || diagnostics.some((diagnostic) => diagnostic.severity === 'error')) {
178
- return {
179
- ok: false,
180
- request,
181
- diagnostics,
182
- };
168
+ return failure(request, diagnostics);
183
169
  }
184
-
185
- return {
186
- ok: true,
187
- request,
188
- diagnostics,
189
- };
170
+ return { ok: true, request, diagnostics };
190
171
  }
191
172
 
192
173
  function selectEndpointOperation(input: EndpointTestInput): EndpointOperationSelection | undefined {
193
174
  const endpoint = input.dataSource.endpoints[input.endpointId];
194
175
  const operation = endpoint?.operations[input.operationId];
195
-
196
- if (endpoint === undefined || operation === undefined) return undefined;
197
-
198
- return { endpoint, operation };
176
+ return endpoint === undefined || operation === undefined ? undefined : { endpoint, operation };
199
177
  }
200
178
 
201
179
  async function resolveEndpointCredential(
@@ -203,34 +181,25 @@ async function resolveEndpointCredential(
203
181
  selection: EndpointOperationSelection,
204
182
  diagnostics: DataSourceDiagnostic[],
205
183
  ): Promise<EndpointTestCredential | undefined> {
206
- const credentialRef =
184
+ const ref =
207
185
  selection.operation.credential ?? selection.endpoint.credential ?? input.dataSource.credential;
208
-
209
- if (credentialRef === undefined) return undefined;
210
-
186
+ if (ref === undefined) return undefined;
211
187
  if (input.credentialResolver === undefined) {
212
188
  diagnostics.push(
213
- createDiagnostic(
214
- input,
215
- 'missing-credential',
216
- `Credential '${credentialRef.id}' requires a resolver.`,
217
- ),
189
+ createDiagnostic(input, 'missing-credential', `Credential '${ref.id}' requires a resolver.`),
218
190
  );
219
191
  return undefined;
220
192
  }
221
-
222
- const credential = await input.credentialResolver(credentialRef);
223
-
193
+ const credential = await input.credentialResolver(ref);
224
194
  if (credential === undefined) {
225
195
  diagnostics.push(
226
196
  createDiagnostic(
227
197
  input,
228
198
  'missing-credential',
229
- `Credential '${credentialRef.id}' could not be resolved.`,
199
+ `Credential '${ref.id}' could not be resolved.`,
230
200
  ),
231
201
  );
232
202
  }
233
-
234
203
  return credential;
235
204
  }
236
205
 
@@ -242,14 +211,12 @@ function buildHttpRequest(
242
211
  ): EndpointTestRequestDiagnostic | undefined {
243
212
  const baseUrl = getDataSourceBaseUrl(input.dataSource, selection.endpoint);
244
213
  const path = selection.operation.path ?? selection.endpoint.path;
245
-
246
- if (baseUrl === undefined || baseUrl.trim().length === 0) {
214
+ if (!baseUrl) {
247
215
  diagnostics.push(
248
216
  createDiagnostic(input, 'invalid-config', 'HTTP endpoint requires a base URL.'),
249
217
  );
250
218
  return undefined;
251
219
  }
252
-
253
220
  if (path === undefined) {
254
221
  diagnostics.push(createDiagnostic(input, 'invalid-config', 'HTTP operation requires a path.'));
255
222
  return undefined;
@@ -257,23 +224,17 @@ function buildHttpRequest(
257
224
 
258
225
  const values = input.values ?? {};
259
226
  const parameters = selection.operation.request?.parameters ?? [];
260
- const pathResult = interpolatePath(path, parameters, values, input, diagnostics);
227
+ const resolvedPath = interpolatePath(path, parameters, values, input, diagnostics);
261
228
  const query = collectQuery(parameters, values, credential?.query);
262
- const url = appendQuery(joinUrl(baseUrl, pathResult), query);
263
229
  const body = createHttpBody(selection.operation, values);
264
- const headers: Record<string, string> = {
265
- ...(credential?.headers ?? {}),
266
- };
267
-
268
- if (body !== undefined) {
269
- headers['content-type'] = headers['content-type'] ?? 'application/json';
270
- }
230
+ const headers: Record<string, string> = { ...(credential?.headers ?? {}) };
231
+ if (body !== undefined) headers['content-type'] = headers['content-type'] ?? 'application/json';
271
232
 
272
233
  return {
273
234
  dataSourceId: input.dataSource.id,
274
235
  endpointId: input.endpointId,
275
236
  operationId: input.operationId,
276
- url,
237
+ url: appendQuery(joinUrl(baseUrl, resolvedPath), query),
277
238
  method: selection.operation.method ?? 'GET',
278
239
  headers,
279
240
  body,
@@ -288,8 +249,7 @@ function buildGraphQlRequest(
288
249
  diagnostics: DataSourceDiagnostic[],
289
250
  ): EndpointTestRequestDiagnostic | undefined {
290
251
  const endpointUrl = getGraphQlEndpointUrl(input.dataSource, selection.endpoint);
291
-
292
- if (endpointUrl === undefined || endpointUrl.trim().length === 0) {
252
+ if (!endpointUrl) {
293
253
  diagnostics.push(
294
254
  createDiagnostic(input, 'invalid-config', 'GraphQL endpoint requires an endpoint URL.'),
295
255
  );
@@ -299,7 +259,6 @@ function buildGraphQlRequest(
299
259
  const query =
300
260
  getStringMetadataValue(selection.operation.metadata, 'document') ??
301
261
  getStringValue(input.values, 'query');
302
-
303
262
  if (query === undefined) {
304
263
  diagnostics.push(
305
264
  createDiagnostic(input, 'invalid-config', 'GraphQL operation requires a query document.'),
@@ -308,41 +267,35 @@ function buildGraphQlRequest(
308
267
  }
309
268
 
310
269
  const variables = getRecordValue(input.values, 'variables');
311
- const body = JSON.stringify({ query, variables: variables ?? {} });
312
- const headers: Record<string, string> = {
313
- ...(credential?.headers ?? {}),
314
- 'content-type': 'application/json',
315
- };
316
-
317
270
  return {
318
271
  dataSourceId: input.dataSource.id,
319
272
  endpointId: input.endpointId,
320
273
  operationId: input.operationId,
321
274
  url: appendQuery(endpointUrl, credential?.query ?? {}),
322
275
  method: 'POST',
323
- headers,
324
- body,
276
+ headers: { ...(credential?.headers ?? {}), 'content-type': 'application/json' },
277
+ body: JSON.stringify({ query, variables: variables ?? {} }),
325
278
  dryRun: input.dryRun === true,
326
279
  };
327
280
  }
328
281
 
329
282
  function getDataSourceBaseUrl(
330
- dataSource: DataSourceConfig,
283
+ source: DataSourceConfig,
331
284
  endpoint: DataEndpointConfig,
332
285
  ): string | undefined {
333
286
  if (endpoint.baseUrl !== undefined) return endpoint.baseUrl;
334
- if (dataSource.kind === 'rest' || dataSource.kind === 'openapi') return dataSource.baseUrl;
335
- if (dataSource.kind === 'graphql') return dataSource.endpointUrl;
336
- return undefined;
287
+ if (source.kind !== 'api' || source.origin !== 'external') return undefined;
288
+ return source.protocol === 'rest' ? source.baseUrl : source.endpointUrl;
337
289
  }
338
290
 
339
291
  function getGraphQlEndpointUrl(
340
- dataSource: DataSourceConfig,
292
+ source: DataSourceConfig,
341
293
  endpoint: DataEndpointConfig,
342
294
  ): string | undefined {
343
295
  if (endpoint.baseUrl !== undefined) return endpoint.baseUrl;
344
- if (dataSource.kind === 'graphql') return dataSource.endpointUrl;
345
- return undefined;
296
+ return source.kind === 'api' && source.origin === 'external' && source.protocol === 'graphql'
297
+ ? source.endpointUrl
298
+ : undefined;
346
299
  }
347
300
 
348
301
  function interpolatePath(
@@ -352,12 +305,9 @@ function interpolatePath(
352
305
  input: EndpointTestInput,
353
306
  diagnostics: DataSourceDiagnostic[],
354
307
  ): string {
355
- let nextPath = path;
356
- const pathParameters = parameters.filter((parameter) => parameter.location === 'path');
357
-
358
- for (const parameter of pathParameters) {
308
+ let next = path;
309
+ for (const parameter of parameters.filter((item) => item.location === 'path')) {
359
310
  const value = values[parameter.name] ?? parameter.default;
360
-
361
311
  if (value === undefined) {
362
312
  diagnostics.push(
363
313
  createDiagnostic(
@@ -368,13 +318,12 @@ function interpolatePath(
368
318
  );
369
319
  continue;
370
320
  }
371
-
372
- const encodedValue = encodeURIComponent(serializeEndpointUrlValue(value));
373
- nextPath = nextPath.replaceAll(`{${parameter.name}}`, encodedValue);
374
- nextPath = nextPath.replaceAll(`:${parameter.name}`, encodedValue);
321
+ const encoded = encodeURIComponent(serializeEndpointUrlValue(value));
322
+ next = next
323
+ .replaceAll(`{${parameter.name}}`, encoded)
324
+ .replaceAll(`:${parameter.name}`, encoded);
375
325
  }
376
-
377
- return nextPath;
326
+ return next;
378
327
  }
379
328
 
380
329
  function collectQuery(
@@ -383,14 +332,11 @@ function collectQuery(
383
332
  credentialQuery: EndpointTestInputValues | undefined,
384
333
  ): EndpointTestInputValues {
385
334
  const query: Record<string, DataContractValue> = { ...(credentialQuery ?? {}) };
386
-
387
335
  for (const parameter of parameters) {
388
336
  if (parameter.location !== 'query') continue;
389
-
390
337
  const value = values[parameter.name] ?? parameter.default;
391
338
  if (value !== undefined) query[parameter.name] = value;
392
339
  }
393
-
394
340
  return query;
395
341
  }
396
342
 
@@ -399,31 +345,24 @@ function createHttpBody(
399
345
  values: EndpointTestInputValues,
400
346
  ): string | undefined {
401
347
  if (operation.method === 'GET' || operation.method === 'HEAD') return undefined;
402
-
403
- const { body } = values;
404
- if (body === undefined) return undefined;
405
-
406
- return JSON.stringify(body);
348
+ return values.body === undefined ? undefined : JSON.stringify(values.body);
407
349
  }
408
350
 
409
351
  async function createResponseDiagnostic(
410
352
  response: EndpointTestFetchResponse,
411
353
  ): Promise<EndpointTestResponseDiagnostic> {
412
354
  const bodyText = await response.text();
413
- const parsedBody = parseResponseBody(bodyText);
414
-
415
355
  return {
416
356
  status: response.status,
417
357
  ok: response.status >= 200 && response.status < 300,
418
358
  headers: response.headers,
419
359
  bodyText,
420
- parsedBody,
360
+ parsedBody: parseResponseBody(bodyText),
421
361
  };
422
362
  }
423
363
 
424
364
  function parseResponseBody(bodyText: string): DataContractValue | undefined {
425
365
  if (bodyText.trim().length === 0) return undefined;
426
-
427
366
  try {
428
367
  const parsed: unknown = JSON.parse(bodyText);
429
368
  return isDataContractValue(parsed) ? parsed : { raw: bodyText };
@@ -435,31 +374,26 @@ function parseResponseBody(bodyText: string): DataContractValue | undefined {
435
374
  function appendQuery(url: string, query: EndpointTestInputValues): string {
436
375
  const entries = Object.entries(query);
437
376
  if (entries.length === 0) return url;
438
-
439
377
  const search = entries
440
378
  .map(
441
379
  ([key, value]) =>
442
380
  `${encodeURIComponent(key)}=${encodeURIComponent(serializeEndpointUrlValue(value))}`,
443
381
  )
444
382
  .join('&');
445
- const separator = url.includes('?') ? '&' : '?';
446
-
447
- return `${url}${separator}${search}`;
383
+ return `${url}${url.includes('?') ? '&' : '?'}${search}`;
448
384
  }
449
385
 
450
386
  function serializeEndpointUrlValue(value: DataContractValue): string {
451
387
  if (typeof value === 'string') return value;
452
388
  if (typeof value === 'number' || typeof value === 'boolean') return value.toString();
453
389
  if (value === null) return '';
454
-
455
390
  return JSON.stringify(value);
456
391
  }
457
392
 
458
393
  function joinUrl(baseUrl: string, path: string): string {
459
- const normalizedBase = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
394
+ const normalizedBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
460
395
  const normalizedPath = path.startsWith('/') ? path : `/${path}`;
461
-
462
- return `${normalizedBase}${normalizedPath}`;
396
+ return `${normalizedBaseUrl}${normalizedPath}`;
463
397
  }
464
398
 
465
399
  function getStringValue(
@@ -483,7 +417,6 @@ function getStringMetadataValue(
483
417
  key: string,
484
418
  ): string | undefined {
485
419
  if (!isDataContractRecord(metadata)) return undefined;
486
-
487
420
  const value = metadata[key];
488
421
  return typeof value === 'string' ? value : undefined;
489
422
  }
@@ -503,25 +436,22 @@ function createDiagnostic(
503
436
  };
504
437
  }
505
438
 
439
+ function failure(
440
+ request: EndpointTestRequestDiagnostic | undefined,
441
+ diagnostics: readonly DataSourceDiagnostic[],
442
+ response?: EndpointTestResponseDiagnostic,
443
+ ): EndpointTestResult {
444
+ return { ok: false, request, response, diagnostics };
445
+ }
446
+
506
447
  function createNetworkErrorMessage(error: unknown): string {
507
- if (error instanceof Error) return error.message;
508
- return 'Endpoint test request failed.';
448
+ return error instanceof Error ? error.message : 'Endpoint test request failed.';
509
449
  }
510
450
 
511
451
  function isDataContractValue(value: unknown): value is DataContractValue {
512
- if (
513
- value === null ||
514
- typeof value === 'string' ||
515
- typeof value === 'number' ||
516
- typeof value === 'boolean'
517
- ) {
518
- return true;
519
- }
520
-
452
+ if (value === null || ['string', 'number', 'boolean'].includes(typeof value)) return true;
521
453
  if (Array.isArray(value)) return value.every(isDataContractValue);
522
- if (typeof value !== 'object') return false;
523
-
524
- return Object.values(value).every(isDataContractValue);
454
+ return typeof value === 'object' && Object.values(value).every(isDataContractValue);
525
455
  }
526
456
 
527
457
  function isDataContractRecord(
@@ -1,30 +0,0 @@
1
- import type { AdapterRef, DataEndpointConfig, DataOperationConfig, DataSchema, DataSourceDiagnostic, DataSourceDiagnosticResult, ManagedApiDataSourceConfig, OperationId } from '@ankhorage/contracts/data';
2
- import type { DbCollectionDefinition } from '@ankhorage/contracts/db';
3
- export declare const MANAGED_API_CRUD_OPERATIONS: readonly ["list", "read", "create", "update", "delete"];
4
- export type ManagedApiCrudOperation = (typeof MANAGED_API_CRUD_OPERATIONS)[number];
5
- export interface ManagedApiOperationPolicyRef {
6
- readonly id: string;
7
- readonly operation?: string;
8
- }
9
- export interface ManagedApiGenerationResourceDefinition {
10
- readonly name: string;
11
- readonly collection: DbCollectionDefinition;
12
- readonly operations?: readonly ManagedApiCrudOperation[];
13
- readonly policies?: readonly ManagedApiOperationPolicyRef[];
14
- }
15
- export interface ManagedApiGenerationDefinition {
16
- readonly id: string;
17
- readonly adapter: AdapterRef;
18
- readonly name?: string;
19
- readonly description?: string;
20
- readonly resources: readonly ManagedApiGenerationResourceDefinition[];
21
- }
22
- export declare function createManagedApiDataSource(definition: ManagedApiGenerationDefinition): DataSourceDiagnosticResult<ManagedApiDataSourceConfig>;
23
- export declare function validateManagedApiDefinition(definition: ManagedApiGenerationDefinition): readonly DataSourceDiagnostic[];
24
- export declare function normalizeManagedApiDataSource(definition: ManagedApiGenerationDefinition): ManagedApiDataSourceConfig;
25
- export declare function createManagedApiEndpoint(definition: ManagedApiGenerationDefinition, resource: ManagedApiGenerationResourceDefinition, operations?: readonly ManagedApiCrudOperation[]): DataEndpointConfig;
26
- export declare function createManagedApiOperation(definition: ManagedApiGenerationDefinition, resource: ManagedApiGenerationResourceDefinition, operation: ManagedApiCrudOperation): DataOperationConfig;
27
- export declare function getManagedApiOperations(resource: ManagedApiGenerationResourceDefinition): readonly ManagedApiCrudOperation[];
28
- export declare function createManagedApiOperationId(resourceName: string, operation: ManagedApiCrudOperation): OperationId;
29
- export declare function createManagedApiResourceSchema(collection: DbCollectionDefinition): DataSchema;
30
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/managed-api/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EAEV,kBAAkB,EAClB,mBAAmB,EAGnB,UAAU,EACV,oBAAoB,EACpB,0BAA0B,EAC1B,0BAA0B,EAE1B,WAAW,EACZ,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EACV,sBAAsB,EAGvB,MAAM,yBAAyB,CAAC;AAEjC,eAAO,MAAM,2BAA2B,yDAA0D,CAAC;AAEnG,MAAM,MAAM,uBAAuB,GAAG,CAAC,OAAO,2BAA2B,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnF,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,sCAAsC;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,sBAAsB,CAAC;IAC5C,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,uBAAuB,EAAE,CAAC;IACzD,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,4BAA4B,EAAE,CAAC;CAC7D;AAED,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,SAAS,sCAAsC,EAAE,CAAC;CACvE;AAED,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,8BAA8B,GACzC,0BAA0B,CAAC,0BAA0B,CAAC,CAWxD;AAED,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,8BAA8B,GACzC,SAAS,oBAAoB,EAAE,CAsDjC;AAED,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,8BAA8B,GACzC,0BAA0B,CA6B5B;AAED,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,8BAA8B,EAC1C,QAAQ,EAAE,sCAAsC,EAChD,UAAU,GAAE,SAAS,uBAAuB,EAAsC,GACjF,kBAAkB,CAmBpB;AAED,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,8BAA8B,EAC1C,QAAQ,EAAE,sCAAsC,EAChD,SAAS,EAAE,uBAAuB,GACjC,mBAAmB,CAsBrB;AAED,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,sCAAsC,GAC/C,SAAS,uBAAuB,EAAE,CAEpC;AAED,wBAAgB,2BAA2B,CACzC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,uBAAuB,GACjC,WAAW,CAEb;AAED,wBAAgB,8BAA8B,CAAC,UAAU,EAAE,sBAAsB,GAAG,UAAU,CAe7F"}