@feathersjs/schema 5.0.0-pre.28 → 5.0.0-pre.30

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.
@@ -0,0 +1,157 @@
1
+ import { FromSchema } from 'json-schema-to-ts'
2
+
3
+ export const authenticationSettingsSchema = {
4
+ type: 'object',
5
+ required: ['secret', 'entity', 'authStrategies'],
6
+ properties: {
7
+ secret: {
8
+ type: 'string',
9
+ description: 'The JWT signing secret'
10
+ },
11
+ entity: {
12
+ oneOf: [
13
+ {
14
+ type: 'null'
15
+ },
16
+ {
17
+ type: 'string'
18
+ }
19
+ ],
20
+ description: 'The name of the authentication entity (e.g. user)'
21
+ },
22
+ entityId: {
23
+ type: 'string',
24
+ description: 'The name of the authentication entity id property'
25
+ },
26
+ service: {
27
+ type: 'string',
28
+ description: 'The path of the entity service'
29
+ },
30
+ authStrategies: {
31
+ type: 'array',
32
+ items: { type: 'string' },
33
+ description: 'A list of authentication strategy names that are allowed to create JWT access tokens'
34
+ },
35
+ parseStrategies: {
36
+ type: 'array',
37
+ items: { type: 'string' },
38
+ description:
39
+ 'A list of authentication strategy names that should parse HTTP headers for authentication information (defaults to `authStrategies`)'
40
+ },
41
+ jwtOptions: {
42
+ type: 'object'
43
+ },
44
+ jwt: {
45
+ type: 'object',
46
+ properties: {
47
+ header: {
48
+ type: 'string',
49
+ default: 'Authorization',
50
+ description: 'The HTTP header containing the JWT'
51
+ },
52
+ schemes: {
53
+ type: 'array',
54
+ items: { type: 'string' },
55
+ description: 'An array of schemes to support'
56
+ }
57
+ }
58
+ },
59
+ local: {
60
+ type: 'object',
61
+ required: ['usernameField', 'passwordField'],
62
+ properties: {
63
+ usernameField: {
64
+ type: 'string',
65
+ description: 'Name of the username field (e.g. `email`)'
66
+ },
67
+ passwordField: {
68
+ type: 'string',
69
+ description: 'Name of the password field (e.g. `password`)'
70
+ },
71
+ hashSize: {
72
+ type: 'number',
73
+ description: 'The BCrypt salt length'
74
+ },
75
+ errorMessage: {
76
+ type: 'string',
77
+ default: 'Invalid login',
78
+ description: 'The error message to return on errors'
79
+ },
80
+ entityUsernameField: {
81
+ type: 'string',
82
+ description:
83
+ 'Name of the username field on the entity if authentication request data and entity field names are different'
84
+ },
85
+ entityPasswordField: {
86
+ type: 'string',
87
+ description:
88
+ 'Name of the password field on the entity if authentication request data and entity field names are different'
89
+ }
90
+ }
91
+ },
92
+ oauth: {
93
+ type: 'object',
94
+ properties: {
95
+ redirect: {
96
+ type: 'string'
97
+ },
98
+ origins: {
99
+ type: 'array',
100
+ items: { type: 'string' }
101
+ },
102
+ defaults: {
103
+ type: 'object',
104
+ properties: {
105
+ key: { type: 'string' },
106
+ secret: { type: 'string' }
107
+ }
108
+ }
109
+ }
110
+ }
111
+ }
112
+ } as const
113
+
114
+ export type AuthenticationConfiguration = FromSchema<typeof authenticationSettingsSchema>
115
+
116
+ export const sqlSettingsSchema = {
117
+ type: 'object',
118
+ properties: {
119
+ client: { type: 'string' },
120
+ connection: { type: 'string' }
121
+ }
122
+ } as const
123
+
124
+ /**
125
+ * Schema for properties that are available in a standard Feathers application.
126
+ */
127
+ export const defaultAppSettings = {
128
+ authentication: authenticationSettingsSchema,
129
+ origins: {
130
+ type: 'array',
131
+ items: {
132
+ type: 'string'
133
+ }
134
+ },
135
+ paginate: {
136
+ type: 'object',
137
+ additionalProperties: false,
138
+ required: ['default', 'max'],
139
+ properties: {
140
+ default: { type: 'number' },
141
+ max: { type: 'number' }
142
+ }
143
+ },
144
+ mongodb: { type: 'string' },
145
+ mysql: sqlSettingsSchema,
146
+ postgresql: sqlSettingsSchema,
147
+ sqlite: sqlSettingsSchema,
148
+ mssql: sqlSettingsSchema
149
+ } as const
150
+
151
+ export const defaultAppConfiguration = {
152
+ type: 'object',
153
+ additionalProperties: false,
154
+ properties: defaultAppSettings
155
+ } as const
156
+
157
+ export type DefaultAppConfiguration = FromSchema<typeof defaultAppConfiguration>
@@ -170,6 +170,8 @@ export const resolveDispatch =
170
170
  })
171
171
  }
172
172
 
173
+ export const resolveExternal = resolveDispatch
174
+
173
175
  export const resolveAll = <H extends HookContext>(map: ResolveAllSettings<H>) => {
174
176
  const middleware = []
175
177
 
@@ -1,14 +1,16 @@
1
1
  import { HookContext, NextFunction } from '@feathersjs/feathers'
2
- import { BadRequest } from '../../../errors/lib'
3
- import { Schema } from '../schema'
2
+ import { BadRequest } from '@feathersjs/errors'
3
+ import { Schema, Validator } from '../schema'
4
+ import { DataValidatorMap } from '../json-schema'
4
5
 
5
- export const validateQuery =
6
- <H extends HookContext>(schema: Schema<any>) =>
7
- async (context: H, next?: NextFunction) => {
6
+ export const validateQuery = <H extends HookContext>(schema: Schema<any> | Validator) => {
7
+ const validator: Validator = typeof schema === 'function' ? schema : schema.validate.bind(schema)
8
+
9
+ return async (context: H, next?: NextFunction) => {
8
10
  const data = context?.params?.query || {}
9
11
 
10
12
  try {
11
- const query = await schema.validate(data)
13
+ const query = await validator(data)
12
14
 
13
15
  context.params = {
14
16
  ...context.params,
@@ -22,23 +24,30 @@ export const validateQuery =
22
24
  throw error.ajv ? new BadRequest(error.message, error.errors) : error
23
25
  }
24
26
  }
27
+ }
25
28
 
26
- export const validateData =
27
- <H extends HookContext>(schema: Schema<any>) =>
28
- async (context: H, next?: NextFunction) => {
29
+ export const validateData = <H extends HookContext>(schema: Schema<any> | DataValidatorMap) => {
30
+ return async (context: H, next?: NextFunction) => {
29
31
  const data = context.data
32
+ const validator =
33
+ typeof (schema as Schema<any>).validate === 'function'
34
+ ? (schema as Schema<any>).validate.bind(schema)
35
+ : (schema as any)[context.method]
30
36
 
31
- try {
32
- if (Array.isArray(data)) {
33
- context.data = await Promise.all(data.map((current) => schema.validate(current)))
34
- } else {
35
- context.data = await schema.validate(data)
37
+ if (validator) {
38
+ try {
39
+ if (Array.isArray(data)) {
40
+ context.data = await Promise.all(data.map((current) => validator(current)))
41
+ } else {
42
+ context.data = await validator(data)
43
+ }
44
+ } catch (error: any) {
45
+ throw error.ajv ? new BadRequest(error.message, error.errors) : error
36
46
  }
37
- } catch (error: any) {
38
- throw error.ajv ? new BadRequest(error.message, error.errors) : error
39
47
  }
40
48
 
41
49
  if (typeof next === 'function') {
42
50
  return next()
43
51
  }
44
52
  }
53
+ }
package/src/index.ts CHANGED
@@ -1,9 +1,17 @@
1
+ import addFormats, { FormatName, FormatOptions, FormatsPluginOptions } from 'ajv-formats'
1
2
  import { ResolverStatus } from './resolver'
2
3
 
4
+ export type { FromSchema } from 'json-schema-to-ts'
5
+ export { addFormats, FormatName, FormatOptions, FormatsPluginOptions }
6
+
3
7
  export * from './schema'
4
8
  export * from './resolver'
5
9
  export * from './hooks'
6
- export * from './query'
10
+ export * from './json-schema'
11
+ export * from './default-schemas'
12
+
13
+ export * as hooks from './hooks'
14
+ export * as jsonSchema from './json-schema'
7
15
 
8
16
  export type Infer<S extends { _type: any }> = S['_type']
9
17
 
@@ -0,0 +1,182 @@
1
+ import { _ } from '@feathersjs/commons'
2
+ import { JSONSchema } from 'json-schema-to-ts'
3
+ import { TObject } from '@sinclair/typebox'
4
+ import { JSONSchemaDefinition, Ajv, Validator } from './schema'
5
+
6
+ export type DataSchemaMap = {
7
+ create: JSONSchemaDefinition | TObject
8
+ update?: JSONSchemaDefinition | TObject
9
+ patch?: JSONSchemaDefinition | TObject
10
+ }
11
+
12
+ export type DataValidatorMap = {
13
+ create: Validator
14
+ update: Validator
15
+ patch: Validator
16
+ }
17
+
18
+ /**
19
+ * Returns a compiled validation function for a schema and AJV validator instance.
20
+ *
21
+ * @param schema The JSON schema definition
22
+ * @param validator The AJV validation instance
23
+ * @returns A compiled validation function
24
+ */
25
+ export const getValidator = <T = any, R = T>(
26
+ schema: JSONSchemaDefinition | TObject,
27
+ validator: Ajv
28
+ ): Validator<T, R> =>
29
+ validator.compile({
30
+ $async: true,
31
+ ...(schema as any)
32
+ }) as any as Validator<T, R>
33
+
34
+ /**
35
+ * Returns compiled validation functions to validate data for the `create`, `update` and `patch`
36
+ * service methods. If not passed explicitly, the `update` validator will be the same as the `create`
37
+ * and `patch` will be the `create` validator with no required fields.
38
+ *
39
+ * @param def Either general JSON schema definition or a mapping of `create`, `update` and `patch`
40
+ * to their respecitve JSON schema
41
+ * @param validator The Ajv instance to use as the validator
42
+ * @returns A map of validator functions
43
+ */
44
+ export const getDataValidator = (
45
+ def: JSONSchemaDefinition | TObject | DataSchemaMap,
46
+ validator: Ajv
47
+ ): DataValidatorMap => {
48
+ const schema = ((def as any).create ? def : { create: def }) as DataSchemaMap
49
+
50
+ return {
51
+ create: getValidator(schema.create, validator),
52
+ update: getValidator(
53
+ schema.update || {
54
+ ...(schema.create as any),
55
+ $id: `${schema.create.$id}Update`
56
+ },
57
+ validator
58
+ ),
59
+ patch: getValidator(
60
+ schema.patch || {
61
+ ...(schema.create as any),
62
+ $id: `${schema.create.$id}Patch`,
63
+ required: []
64
+ },
65
+ validator
66
+ )
67
+ }
68
+ }
69
+
70
+ export type PropertyQuery<D extends JSONSchema> = {
71
+ anyOf: [
72
+ D,
73
+ {
74
+ type: 'object'
75
+ additionalProperties: false
76
+ properties: {
77
+ $gt: D
78
+ $gte: D
79
+ $lt: D
80
+ $lte: D
81
+ $ne: D
82
+ $in: {
83
+ type: 'array'
84
+ items: D
85
+ }
86
+ $nin: {
87
+ type: 'array'
88
+ items: D
89
+ }
90
+ }
91
+ }
92
+ ]
93
+ }
94
+
95
+ /**
96
+ * Create a Feathers query syntax compatible JSON schema definition for a property definition.
97
+ *
98
+ * @param def The property definition (e.g. `{ type: 'string' }`)
99
+ * @returns A JSON schema definition for the Feathers query syntax for this property.
100
+ */
101
+ export const queryProperty = <T extends JSONSchema>(def: T) => {
102
+ const definition = _.omit(def, 'default')
103
+ return {
104
+ anyOf: [
105
+ definition,
106
+ {
107
+ type: 'object',
108
+ additionalProperties: false,
109
+ properties: {
110
+ $gt: definition,
111
+ $gte: definition,
112
+ $lt: definition,
113
+ $lte: definition,
114
+ $ne: definition,
115
+ $in: {
116
+ type: 'array',
117
+ items: definition
118
+ },
119
+ $nin: {
120
+ type: 'array',
121
+ items: definition
122
+ }
123
+ }
124
+ }
125
+ ]
126
+ } as const
127
+ }
128
+
129
+ /**
130
+ * Creates Feathers a query syntax compatible JSON schema for multiple properties.
131
+ *
132
+ * @param definition A map of property definitions
133
+ * @returns The JSON schema definition for the Feathers query syntax for multiple properties
134
+ */
135
+ export const queryProperties = <T extends { [key: string]: JSONSchema }>(definition: T) =>
136
+ Object.keys(definition).reduce((res, key) => {
137
+ const result = res as any
138
+
139
+ result[key] = queryProperty(definition[key])
140
+
141
+ return result
142
+ }, {} as { [K in keyof T]: PropertyQuery<T[K]> })
143
+
144
+ /**
145
+ * Creates a JSON schema for the complete Feathers query syntax including `$limit`, $skip`
146
+ * and `$sort` and `$select` for the allowed properties.
147
+ *
148
+ * @param definition The property definitions to create the query syntax schema for
149
+ * @returns A JSON schema for the complete query syntax
150
+ */
151
+ export const querySyntax = <T extends { [key: string]: any }>(definition: T) =>
152
+ ({
153
+ $limit: {
154
+ type: 'number',
155
+ minimum: 0
156
+ },
157
+ $skip: {
158
+ type: 'number',
159
+ minimum: 0
160
+ },
161
+ $sort: {
162
+ type: 'object',
163
+ properties: Object.keys(definition).reduce((res, key) => {
164
+ const result = res as any
165
+
166
+ result[key] = {
167
+ type: 'number',
168
+ enum: [1, -1]
169
+ }
170
+
171
+ return result
172
+ }, {} as { [K in keyof T]: { readonly type: 'number'; readonly enum: [1, -1] } })
173
+ },
174
+ $select: {
175
+ type: 'array',
176
+ items: {
177
+ type: 'string',
178
+ enum: Object.keys(definition) as any as (keyof T)[]
179
+ }
180
+ },
181
+ ...queryProperties(definition)
182
+ } as const)
package/src/resolver.ts CHANGED
@@ -20,8 +20,18 @@ export type ResolverConverter<T, C> = (
20
20
 
21
21
  export interface ResolverConfig<T, C> {
22
22
  schema?: Schema<T>
23
+ /**
24
+ * @deprecated Use the `validateData` and `validateQuery` hooks explicitly instead
25
+ */
23
26
  validate?: 'before' | 'after' | false
27
+ /**
28
+ * The properties to resolve
29
+ */
24
30
  properties: PropertyResolverMap<T, C>
31
+ /**
32
+ * A converter function that is run before property resolvers
33
+ * to transform the initial data into a different format.
34
+ */
25
35
  converter?: ResolverConverter<T, C>
26
36
  }
27
37
 
@@ -37,6 +47,15 @@ export class Resolver<T, C> {
37
47
 
38
48
  constructor(public options: ResolverConfig<T, C>) {}
39
49
 
50
+ /**
51
+ * Resolve a single property
52
+ *
53
+ * @param name The name of the property
54
+ * @param data The current data
55
+ * @param context The current resolver context
56
+ * @param status The current resolver status
57
+ * @returns The resolver property
58
+ */
40
59
  async resolveProperty<D, K extends keyof T>(
41
60
  name: K,
42
61
  data: D,
@@ -122,6 +141,12 @@ export class Resolver<T, C> {
122
141
  }
123
142
  }
124
143
 
144
+ /**
145
+ * Create a new resolver with `<DataType, ContextType>`.
146
+ *
147
+ * @param options The configuration for the returned resolver
148
+ * @returns A new resolver instance
149
+ */
125
150
  export function resolve<T, C>(options: ResolverConfig<T, C>) {
126
151
  return new Resolver<T, C>(options)
127
152
  }
package/src/schema.ts CHANGED
@@ -3,14 +3,21 @@ import { FromSchema, JSONSchema } from 'json-schema-to-ts'
3
3
  import { BadRequest } from '@feathersjs/errors'
4
4
 
5
5
  export const DEFAULT_AJV = new Ajv({
6
- coerceTypes: true
6
+ coerceTypes: true,
7
+ addUsedSchema: false
7
8
  })
8
9
 
9
10
  export { Ajv }
10
11
 
12
+ /**
13
+ * A validation function that takes data and returns the (possibly coerced)
14
+ * data or throws a validation error.
15
+ */
16
+ export type Validator<T = any, R = T> = (data: T) => Promise<R>
17
+
11
18
  export type JSONSchemaDefinition = JSONSchema & {
12
19
  $id: string
13
- $async?: boolean
20
+ $async?: true
14
21
  properties?: { [key: string]: JSONSchema }
15
22
  required?: readonly string[]
16
23
  }
package/lib/query.d.ts DELETED
@@ -1,75 +0,0 @@
1
- import { JSONSchema } from 'json-schema-to-ts';
2
- export declare type PropertyQuery<D extends JSONSchema> = {
3
- anyOf: [
4
- D,
5
- {
6
- type: 'object';
7
- additionalProperties: false;
8
- properties: {
9
- $gt: D;
10
- $gte: D;
11
- $lt: D;
12
- $lte: D;
13
- $ne: D;
14
- $in: {
15
- type: 'array';
16
- items: D;
17
- };
18
- $nin: {
19
- type: 'array';
20
- items: D;
21
- };
22
- };
23
- }
24
- ];
25
- };
26
- export declare const queryProperty: <T extends import("json-schema-to-ts").JSONSchema7>(def: T) => {
27
- readonly anyOf: readonly [any, {
28
- readonly type: "object";
29
- readonly additionalProperties: false;
30
- readonly properties: {
31
- readonly $gt: any;
32
- readonly $gte: any;
33
- readonly $lt: any;
34
- readonly $lte: any;
35
- readonly $ne: any;
36
- readonly $in: {
37
- readonly type: "array";
38
- readonly items: any;
39
- };
40
- readonly $nin: {
41
- readonly type: "array";
42
- readonly items: any;
43
- };
44
- };
45
- }];
46
- };
47
- export declare const queryProperties: <T extends {
48
- [key: string]: import("json-schema-to-ts").JSONSchema7;
49
- }>(definition: T) => { [K in keyof T]: PropertyQuery<T[K]>; };
50
- export declare const querySyntax: <T extends {
51
- [key: string]: any;
52
- }>(definition: T) => {
53
- readonly $limit: {
54
- readonly type: "number";
55
- readonly minimum: 0;
56
- };
57
- readonly $skip: {
58
- readonly type: "number";
59
- readonly minimum: 0;
60
- };
61
- readonly $sort: {
62
- readonly type: "object";
63
- readonly properties: { [K in keyof T]: {
64
- readonly type: 'number';
65
- readonly enum: [1, -1];
66
- }; };
67
- };
68
- readonly $select: {
69
- readonly type: "array";
70
- readonly items: {
71
- readonly type: "string";
72
- readonly enum: (keyof T)[];
73
- };
74
- };
75
- } & { [K_1 in keyof T]: PropertyQuery<T[K_1]>; };
package/lib/query.js DELETED
@@ -1,69 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.querySyntax = exports.queryProperties = exports.queryProperty = void 0;
4
- const commons_1 = require("@feathersjs/commons");
5
- const queryProperty = (def) => {
6
- const definition = commons_1._.omit(def, 'default');
7
- return {
8
- anyOf: [
9
- definition,
10
- {
11
- type: 'object',
12
- additionalProperties: false,
13
- properties: {
14
- $gt: definition,
15
- $gte: definition,
16
- $lt: definition,
17
- $lte: definition,
18
- $ne: definition,
19
- $in: {
20
- type: 'array',
21
- items: definition
22
- },
23
- $nin: {
24
- type: 'array',
25
- items: definition
26
- }
27
- }
28
- }
29
- ]
30
- };
31
- };
32
- exports.queryProperty = queryProperty;
33
- const queryProperties = (definition) => Object.keys(definition).reduce((res, key) => {
34
- const result = res;
35
- result[key] = (0, exports.queryProperty)(definition[key]);
36
- return result;
37
- }, {});
38
- exports.queryProperties = queryProperties;
39
- const querySyntax = (definition) => ({
40
- $limit: {
41
- type: 'number',
42
- minimum: 0
43
- },
44
- $skip: {
45
- type: 'number',
46
- minimum: 0
47
- },
48
- $sort: {
49
- type: 'object',
50
- properties: Object.keys(definition).reduce((res, key) => {
51
- const result = res;
52
- result[key] = {
53
- type: 'number',
54
- enum: [1, -1]
55
- };
56
- return result;
57
- }, {})
58
- },
59
- $select: {
60
- type: 'array',
61
- items: {
62
- type: 'string',
63
- enum: Object.keys(definition)
64
- }
65
- },
66
- ...(0, exports.queryProperties)(definition)
67
- });
68
- exports.querySyntax = querySyntax;
69
- //# sourceMappingURL=query.js.map
package/lib/query.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":";;;AAAA,iDAAuC;AA4BhC,MAAM,aAAa,GAAG,CAAuB,GAAM,EAAE,EAAE;IAC5D,MAAM,UAAU,GAAG,WAAC,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAA;IACzC,OAAO;QACL,KAAK,EAAE;YACL,UAAU;YACV;gBACE,IAAI,EAAE,QAAQ;gBACd,oBAAoB,EAAE,KAAK;gBAC3B,UAAU,EAAE;oBACV,GAAG,EAAE,UAAU;oBACf,IAAI,EAAE,UAAU;oBAChB,GAAG,EAAE,UAAU;oBACf,IAAI,EAAE,UAAU;oBAChB,GAAG,EAAE,UAAU;oBACf,GAAG,EAAE;wBACH,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE,UAAU;qBAClB;oBACD,IAAI,EAAE;wBACJ,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE,UAAU;qBAClB;iBACF;aACF;SACF;KACO,CAAA;AACZ,CAAC,CAAA;AA1BY,QAAA,aAAa,iBA0BzB;AAEM,MAAM,eAAe,GAAG,CAA0C,UAAa,EAAE,EAAE,CACxF,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IAC1C,MAAM,MAAM,GAAG,GAAU,CAAA;IAEzB,MAAM,CAAC,GAAG,CAAC,GAAG,IAAA,qBAAa,EAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAA;IAE5C,OAAO,MAAM,CAAA;AACf,CAAC,EAAE,EAA6C,CAAC,CAAA;AAPtC,QAAA,eAAe,mBAOuB;AAE5C,MAAM,WAAW,GAAG,CAAmC,UAAa,EAAE,EAAE,CAC7E,CAAC;IACC,MAAM,EAAE;QACN,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,CAAC;KACX;IACD,KAAK,EAAE;QACL,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,CAAC;KACX;IACD,KAAK,EAAE;QACL,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YACtD,MAAM,MAAM,GAAG,GAAU,CAAA;YAEzB,MAAM,CAAC,GAAG,CAAC,GAAG;gBACZ,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;aACd,CAAA;YAED,OAAO,MAAM,CAAA;QACf,CAAC,EAAE,EAA6E,CAAC;KAClF;IACD,OAAO,EAAE;QACP,IAAI,EAAE,OAAO;QACb,KAAK,EAAE;YACL,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAuB;SACpD;KACF;IACD,GAAG,IAAA,uBAAe,EAAC,UAAU,CAAC;CACrB,CAAA,CAAA;AA/BA,QAAA,WAAW,eA+BX"}