@opensaas/stack-core 0.1.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.
- package/.turbo/turbo-build.log +4 -0
- package/README.md +447 -0
- package/dist/access/engine.d.ts +73 -0
- package/dist/access/engine.d.ts.map +1 -0
- package/dist/access/engine.js +244 -0
- package/dist/access/engine.js.map +1 -0
- package/dist/access/field-transforms.d.ts +47 -0
- package/dist/access/field-transforms.d.ts.map +1 -0
- package/dist/access/field-transforms.js +2 -0
- package/dist/access/field-transforms.js.map +1 -0
- package/dist/access/index.d.ts +3 -0
- package/dist/access/index.d.ts.map +1 -0
- package/dist/access/index.js +2 -0
- package/dist/access/index.js.map +1 -0
- package/dist/access/types.d.ts +83 -0
- package/dist/access/types.d.ts.map +1 -0
- package/dist/access/types.js +2 -0
- package/dist/access/types.js.map +1 -0
- package/dist/config/index.d.ts +39 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +38 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/types.d.ts +413 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -0
- package/dist/context/index.d.ts +31 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +524 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/nested-operations.d.ts +10 -0
- package/dist/context/nested-operations.d.ts.map +1 -0
- package/dist/context/nested-operations.js +261 -0
- package/dist/context/nested-operations.js.map +1 -0
- package/dist/fields/index.d.ts +78 -0
- package/dist/fields/index.d.ts.map +1 -0
- package/dist/fields/index.js +381 -0
- package/dist/fields/index.js.map +1 -0
- package/dist/hooks/index.d.ts +58 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +79 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/case-utils.d.ts +49 -0
- package/dist/lib/case-utils.d.ts.map +1 -0
- package/dist/lib/case-utils.js +68 -0
- package/dist/lib/case-utils.js.map +1 -0
- package/dist/lib/case-utils.test.d.ts +2 -0
- package/dist/lib/case-utils.test.d.ts.map +1 -0
- package/dist/lib/case-utils.test.js +101 -0
- package/dist/lib/case-utils.test.js.map +1 -0
- package/dist/utils/password.d.ts +81 -0
- package/dist/utils/password.d.ts.map +1 -0
- package/dist/utils/password.js +132 -0
- package/dist/utils/password.js.map +1 -0
- package/dist/validation/schema.d.ts +17 -0
- package/dist/validation/schema.d.ts.map +1 -0
- package/dist/validation/schema.js +42 -0
- package/dist/validation/schema.js.map +1 -0
- package/dist/validation/schema.test.d.ts +2 -0
- package/dist/validation/schema.test.d.ts.map +1 -0
- package/dist/validation/schema.test.js +143 -0
- package/dist/validation/schema.test.js.map +1 -0
- package/docs/type-distribution-fix.md +136 -0
- package/package.json +48 -0
- package/src/access/engine.ts +360 -0
- package/src/access/field-transforms.ts +99 -0
- package/src/access/index.ts +20 -0
- package/src/access/types.ts +103 -0
- package/src/config/index.ts +71 -0
- package/src/config/types.ts +478 -0
- package/src/context/index.ts +814 -0
- package/src/context/nested-operations.ts +412 -0
- package/src/fields/index.ts +438 -0
- package/src/hooks/index.ts +132 -0
- package/src/index.ts +62 -0
- package/src/lib/case-utils.test.ts +127 -0
- package/src/lib/case-utils.ts +74 -0
- package/src/utils/password.ts +147 -0
- package/src/validation/schema.test.ts +171 -0
- package/src/validation/schema.ts +59 -0
- package/tests/access-relationships.test.ts +613 -0
- package/tests/access.test.ts +499 -0
- package/tests/config.test.ts +195 -0
- package/tests/context.test.ts +248 -0
- package/tests/hooks.test.ts +417 -0
- package/tests/password-type-distribution.test.ts +155 -0
- package/tests/password-types.test.ts +147 -0
- package/tests/password.test.ts +249 -0
- package/tsconfig.json +12 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +27 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { checkAccess, filterWritableFields, getRelatedListConfig } from '../access/index.js';
|
|
2
|
+
import { executeResolveInput, executeValidateInput, validateFieldRules, ValidationError, } from '../hooks/index.js';
|
|
3
|
+
import { getDbKey } from '../lib/case-utils.js';
|
|
4
|
+
/**
|
|
5
|
+
* Check if a field config is a relationship field
|
|
6
|
+
*/
|
|
7
|
+
function isRelationshipField(fieldConfig) {
|
|
8
|
+
return fieldConfig?.type === 'relationship';
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Process nested create operations
|
|
12
|
+
* Applies hooks and access control to each item being created
|
|
13
|
+
*/
|
|
14
|
+
async function processNestedCreate(items, relatedListConfig, context, config) {
|
|
15
|
+
const itemsArray = Array.isArray(items) ? items : [items];
|
|
16
|
+
const processedItems = await Promise.all(itemsArray.map(async (item) => {
|
|
17
|
+
// 1. Check create access
|
|
18
|
+
const createAccess = relatedListConfig.access?.operation?.create;
|
|
19
|
+
const accessResult = await checkAccess(createAccess, {
|
|
20
|
+
session: context.session,
|
|
21
|
+
context,
|
|
22
|
+
});
|
|
23
|
+
if (accessResult === false) {
|
|
24
|
+
throw new Error('Access denied: Cannot create related item');
|
|
25
|
+
}
|
|
26
|
+
// 2. Execute resolveInput hook
|
|
27
|
+
let resolvedData = await executeResolveInput(relatedListConfig.hooks, {
|
|
28
|
+
operation: 'create',
|
|
29
|
+
resolvedData: item,
|
|
30
|
+
context,
|
|
31
|
+
});
|
|
32
|
+
// 3. Execute validateInput hook
|
|
33
|
+
await executeValidateInput(relatedListConfig.hooks, {
|
|
34
|
+
operation: 'create',
|
|
35
|
+
resolvedData,
|
|
36
|
+
context,
|
|
37
|
+
});
|
|
38
|
+
// 4. Field validation
|
|
39
|
+
const validation = validateFieldRules(resolvedData, relatedListConfig.fields, 'create');
|
|
40
|
+
if (validation.errors.length > 0) {
|
|
41
|
+
throw new ValidationError(validation.errors, validation.fieldErrors);
|
|
42
|
+
}
|
|
43
|
+
// 5. Filter writable fields
|
|
44
|
+
const filtered = await filterWritableFields(resolvedData, relatedListConfig.fields, 'create', {
|
|
45
|
+
session: context.session,
|
|
46
|
+
context,
|
|
47
|
+
});
|
|
48
|
+
// 6. Recursively process nested operations in this item
|
|
49
|
+
return await processNestedOperations(filtered, relatedListConfig.fields, config, context, 'create');
|
|
50
|
+
}));
|
|
51
|
+
return Array.isArray(items) ? processedItems : processedItems[0];
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Process nested connect operations
|
|
55
|
+
* Verifies update access to the items being connected
|
|
56
|
+
*/
|
|
57
|
+
async function processNestedConnect(connections, relatedListName, relatedListConfig, context, prisma) {
|
|
58
|
+
const connectionsArray = Array.isArray(connections) ? connections : [connections];
|
|
59
|
+
// Check update access for each item being connected
|
|
60
|
+
for (const connection of connectionsArray) {
|
|
61
|
+
// Access Prisma model dynamically - required because model names are generated at runtime
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
63
|
+
const model = prisma[getDbKey(relatedListName)];
|
|
64
|
+
// Fetch the item to check access
|
|
65
|
+
const item = await model.findUnique({
|
|
66
|
+
where: connection,
|
|
67
|
+
});
|
|
68
|
+
if (!item) {
|
|
69
|
+
throw new Error(`Cannot connect: Item not found`);
|
|
70
|
+
}
|
|
71
|
+
// Check update access (connecting modifies the relationship)
|
|
72
|
+
const updateAccess = relatedListConfig.access?.operation?.update;
|
|
73
|
+
const accessResult = await checkAccess(updateAccess, {
|
|
74
|
+
session: context.session,
|
|
75
|
+
item,
|
|
76
|
+
context,
|
|
77
|
+
});
|
|
78
|
+
if (accessResult === false) {
|
|
79
|
+
throw new Error('Access denied: Cannot connect to this item');
|
|
80
|
+
}
|
|
81
|
+
// If access returns a filter, check if item matches
|
|
82
|
+
if (typeof accessResult === 'object') {
|
|
83
|
+
// Simple field matching
|
|
84
|
+
for (const [key, value] of Object.entries(accessResult)) {
|
|
85
|
+
if (typeof value === 'object' && value !== null && 'equals' in value) {
|
|
86
|
+
if (item[key] !== value.equals) {
|
|
87
|
+
throw new Error('Access denied: Cannot connect to this item');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else if (item[key] !== value) {
|
|
91
|
+
throw new Error('Access denied: Cannot connect to this item');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return connections;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Process nested update operations
|
|
100
|
+
* Applies hooks and access control to updates
|
|
101
|
+
*/
|
|
102
|
+
async function processNestedUpdate(updates, relatedListName, relatedListConfig, context, config, prisma) {
|
|
103
|
+
const updatesArray = Array.isArray(updates) ? updates : [updates];
|
|
104
|
+
const processedUpdates = await Promise.all(updatesArray.map(async (update) => {
|
|
105
|
+
// Access Prisma model dynamically - required because model names are generated at runtime
|
|
106
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
107
|
+
const model = prisma[getDbKey(relatedListName)];
|
|
108
|
+
// Fetch the existing item
|
|
109
|
+
const item = await model.findUnique({
|
|
110
|
+
where: update.where,
|
|
111
|
+
});
|
|
112
|
+
if (!item) {
|
|
113
|
+
throw new Error('Cannot update: Item not found');
|
|
114
|
+
}
|
|
115
|
+
// Check update access
|
|
116
|
+
const updateAccess = relatedListConfig.access?.operation?.update;
|
|
117
|
+
const accessResult = await checkAccess(updateAccess, {
|
|
118
|
+
session: context.session,
|
|
119
|
+
item,
|
|
120
|
+
context,
|
|
121
|
+
});
|
|
122
|
+
if (accessResult === false) {
|
|
123
|
+
throw new Error('Access denied: Cannot update related item');
|
|
124
|
+
}
|
|
125
|
+
// Execute resolveInput hook
|
|
126
|
+
const updateData = update.data;
|
|
127
|
+
let resolvedData = await executeResolveInput(relatedListConfig.hooks, {
|
|
128
|
+
operation: 'update',
|
|
129
|
+
resolvedData: updateData,
|
|
130
|
+
item,
|
|
131
|
+
context,
|
|
132
|
+
});
|
|
133
|
+
// Execute validateInput hook
|
|
134
|
+
await executeValidateInput(relatedListConfig.hooks, {
|
|
135
|
+
operation: 'update',
|
|
136
|
+
resolvedData,
|
|
137
|
+
item,
|
|
138
|
+
context,
|
|
139
|
+
});
|
|
140
|
+
// Field validation
|
|
141
|
+
const validation = validateFieldRules(resolvedData, relatedListConfig.fields, 'update');
|
|
142
|
+
if (validation.errors.length > 0) {
|
|
143
|
+
throw new ValidationError(validation.errors, validation.fieldErrors);
|
|
144
|
+
}
|
|
145
|
+
// Filter writable fields
|
|
146
|
+
const filtered = await filterWritableFields(resolvedData, relatedListConfig.fields, 'update', {
|
|
147
|
+
session: context.session,
|
|
148
|
+
item,
|
|
149
|
+
context,
|
|
150
|
+
});
|
|
151
|
+
// Recursively process nested operations
|
|
152
|
+
const processedData = await processNestedOperations(filtered, relatedListConfig.fields, config, context, 'update');
|
|
153
|
+
return {
|
|
154
|
+
where: update.where,
|
|
155
|
+
data: processedData,
|
|
156
|
+
};
|
|
157
|
+
}));
|
|
158
|
+
return Array.isArray(updates) ? processedUpdates : processedUpdates[0];
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Process nested connectOrCreate operations
|
|
162
|
+
*/
|
|
163
|
+
async function processNestedConnectOrCreate(operations, relatedListName, relatedListConfig, context, config, prisma) {
|
|
164
|
+
const operationsArray = Array.isArray(operations) ? operations : [operations];
|
|
165
|
+
const processedOps = await Promise.all(operationsArray.map(async (op) => {
|
|
166
|
+
// Process the create portion through create hooks
|
|
167
|
+
const opRecord = op;
|
|
168
|
+
const processedCreate = await processNestedCreate(opRecord.create, relatedListConfig, context, config);
|
|
169
|
+
// Check access for the connect portion (try to find existing item)
|
|
170
|
+
try {
|
|
171
|
+
// Access Prisma model dynamically - required because model names are generated at runtime
|
|
172
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
173
|
+
const model = prisma[getDbKey(relatedListName)];
|
|
174
|
+
const existingItem = await model.findUnique({
|
|
175
|
+
where: opRecord.where,
|
|
176
|
+
});
|
|
177
|
+
if (existingItem) {
|
|
178
|
+
// Check update access for connection
|
|
179
|
+
const updateAccess = relatedListConfig.access?.operation?.update;
|
|
180
|
+
const accessResult = await checkAccess(updateAccess, {
|
|
181
|
+
session: context.session,
|
|
182
|
+
item: existingItem,
|
|
183
|
+
context,
|
|
184
|
+
});
|
|
185
|
+
if (accessResult === false) {
|
|
186
|
+
throw new Error('Access denied: Cannot connect to existing item');
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
// Item doesn't exist, will use create (already processed)
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
where: op.where,
|
|
195
|
+
create: processedCreate,
|
|
196
|
+
};
|
|
197
|
+
}));
|
|
198
|
+
return Array.isArray(operations) ? processedOps : processedOps[0];
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Process all nested operations in a data payload
|
|
202
|
+
* Recursively handles relationship fields with nested writes
|
|
203
|
+
*/
|
|
204
|
+
export async function processNestedOperations(data, fieldConfigs, config, context, operation, depth = 0) {
|
|
205
|
+
const MAX_DEPTH = 5;
|
|
206
|
+
if (depth >= MAX_DEPTH) {
|
|
207
|
+
return data;
|
|
208
|
+
}
|
|
209
|
+
const processed = {};
|
|
210
|
+
for (const [fieldName, value] of Object.entries(data)) {
|
|
211
|
+
const fieldConfig = fieldConfigs[fieldName];
|
|
212
|
+
// If not a relationship field or no value, pass through
|
|
213
|
+
if (!isRelationshipField(fieldConfig) || value === null || value === undefined) {
|
|
214
|
+
processed[fieldName] = value;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
// Get related list config
|
|
218
|
+
const relationshipField = fieldConfig;
|
|
219
|
+
const relatedConfig = getRelatedListConfig(relationshipField.ref, config);
|
|
220
|
+
if (!relatedConfig) {
|
|
221
|
+
processed[fieldName] = value;
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
const { listName: relatedListName, listConfig: relatedListConfig } = relatedConfig;
|
|
225
|
+
// Process different nested operation types
|
|
226
|
+
const nestedOp = {};
|
|
227
|
+
const valueRecord = value;
|
|
228
|
+
if (valueRecord.create !== undefined) {
|
|
229
|
+
nestedOp.create = await processNestedCreate(valueRecord.create, relatedListConfig, context, config);
|
|
230
|
+
}
|
|
231
|
+
if (valueRecord.connect !== undefined) {
|
|
232
|
+
nestedOp.connect = await processNestedConnect(valueRecord.connect, relatedListName, relatedListConfig, context, context.prisma);
|
|
233
|
+
}
|
|
234
|
+
if (valueRecord.connectOrCreate !== undefined) {
|
|
235
|
+
nestedOp.connectOrCreate = await processNestedConnectOrCreate(valueRecord.connectOrCreate, relatedListName, relatedListConfig, context, config, context.prisma);
|
|
236
|
+
}
|
|
237
|
+
if (valueRecord.update !== undefined) {
|
|
238
|
+
nestedOp.update = await processNestedUpdate(valueRecord.update, relatedListName, relatedListConfig, context, config, context.prisma);
|
|
239
|
+
}
|
|
240
|
+
// For other operations, pass through (disconnect, delete, set, etc.)
|
|
241
|
+
// These will be subject to Prisma's own constraints
|
|
242
|
+
if (valueRecord.disconnect !== undefined) {
|
|
243
|
+
nestedOp.disconnect = valueRecord.disconnect;
|
|
244
|
+
}
|
|
245
|
+
if (valueRecord.delete !== undefined) {
|
|
246
|
+
nestedOp.delete = valueRecord.delete;
|
|
247
|
+
}
|
|
248
|
+
if (valueRecord.deleteMany !== undefined) {
|
|
249
|
+
nestedOp.deleteMany = valueRecord.deleteMany;
|
|
250
|
+
}
|
|
251
|
+
if (valueRecord.set !== undefined) {
|
|
252
|
+
nestedOp.set = valueRecord.set;
|
|
253
|
+
}
|
|
254
|
+
if (valueRecord.updateMany !== undefined) {
|
|
255
|
+
nestedOp.updateMany = valueRecord.updateMany;
|
|
256
|
+
}
|
|
257
|
+
processed[fieldName] = nestedOp;
|
|
258
|
+
}
|
|
259
|
+
return processed;
|
|
260
|
+
}
|
|
261
|
+
//# sourceMappingURL=nested-operations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nested-operations.js","sourceRoot":"","sources":["../../src/context/nested-operations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { TextField, IntegerField, CheckboxField, TimestampField, PasswordField, SelectField, RelationshipField } from '../config/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Text field
|
|
4
|
+
*/
|
|
5
|
+
export declare function text(options?: Omit<TextField, 'type'>): TextField;
|
|
6
|
+
/**
|
|
7
|
+
* Integer field
|
|
8
|
+
*/
|
|
9
|
+
export declare function integer(options?: Omit<IntegerField, 'type'>): IntegerField;
|
|
10
|
+
/**
|
|
11
|
+
* Checkbox (boolean) field
|
|
12
|
+
*/
|
|
13
|
+
export declare function checkbox(options?: Omit<CheckboxField, 'type'>): CheckboxField;
|
|
14
|
+
/**
|
|
15
|
+
* Timestamp (DateTime) field
|
|
16
|
+
*/
|
|
17
|
+
export declare function timestamp(options?: Omit<TimestampField, 'type'>): TimestampField;
|
|
18
|
+
/**
|
|
19
|
+
* Password field (automatically hashed using bcrypt)
|
|
20
|
+
*
|
|
21
|
+
* **Security Features:**
|
|
22
|
+
* - Passwords are automatically hashed during create/update operations
|
|
23
|
+
* - Uses bcrypt with cost factor 10 (good balance of security and performance)
|
|
24
|
+
* - Already-hashed passwords are not re-hashed (idempotent)
|
|
25
|
+
* - Password values in query results include a `compare()` method for authentication
|
|
26
|
+
*
|
|
27
|
+
* **Usage Example:**
|
|
28
|
+
* ```typescript
|
|
29
|
+
* // In opensaas.config.ts
|
|
30
|
+
* fields: {
|
|
31
|
+
* password: password({
|
|
32
|
+
* validation: { isRequired: true }
|
|
33
|
+
* })
|
|
34
|
+
* }
|
|
35
|
+
*
|
|
36
|
+
* // Creating a user - password is automatically hashed
|
|
37
|
+
* const user = await context.db.user.create({
|
|
38
|
+
* data: {
|
|
39
|
+
* email: 'user@example.com',
|
|
40
|
+
* password: 'plaintextPassword' // Automatically hashed before storage
|
|
41
|
+
* }
|
|
42
|
+
* })
|
|
43
|
+
*
|
|
44
|
+
* // Authenticating - use the compare() method
|
|
45
|
+
* const user = await context.db.user.findUnique({
|
|
46
|
+
* where: { email: 'user@example.com' }
|
|
47
|
+
* })
|
|
48
|
+
*
|
|
49
|
+
* if (user && await user.password.compare('plaintextPassword')) {
|
|
50
|
+
* // Password is correct - login successful
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* **Important Notes:**
|
|
55
|
+
* - Password fields are excluded from read operations by default in access control
|
|
56
|
+
* - Always use the `compare()` method to verify passwords - never compare strings directly
|
|
57
|
+
* - The password field value has type `HashedPassword` which extends string with compare()
|
|
58
|
+
* - Empty strings and undefined values are skipped (not hashed) to allow partial updates
|
|
59
|
+
*
|
|
60
|
+
* **Implementation Details:**
|
|
61
|
+
* - Uses field-level hooks (`resolveInput` and `resolveOutput`) for automatic transformations
|
|
62
|
+
* - The hashing happens via `hooks.resolveInput` during create/update operations
|
|
63
|
+
* - The wrapping happens via `hooks.resolveOutput` during read operations
|
|
64
|
+
* - This pattern allows third-party field types to define their own transformations
|
|
65
|
+
*
|
|
66
|
+
* @param options - Field configuration options
|
|
67
|
+
* @returns Password field configuration
|
|
68
|
+
*/
|
|
69
|
+
export declare function password(options?: Omit<PasswordField, 'type'>): PasswordField;
|
|
70
|
+
/**
|
|
71
|
+
* Select field (enum-like)
|
|
72
|
+
*/
|
|
73
|
+
export declare function select(options: Omit<SelectField, 'type'>): SelectField;
|
|
74
|
+
/**
|
|
75
|
+
* Relationship field
|
|
76
|
+
*/
|
|
77
|
+
export declare function relationship(options: Omit<RelationshipField, 'type'>): RelationshipField;
|
|
78
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fields/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,SAAS,EACT,YAAY,EACZ,aAAa,EACb,cAAc,EACd,aAAa,EACb,WAAW,EACX,iBAAiB,EAClB,MAAM,oBAAoB,CAAA;AAa3B;;GAEG;AACH,wBAAgB,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,GAAG,SAAS,CAqEjE;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,GAAG,YAAY,CA4C1E;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,GAAG,aAAa,CA2B7E;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,GAAG,cAAc,CAsChF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,wBAAgB,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,GAAG,aAAa,CAgG7E;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,GAAG,WAAW,CA2CtE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,GAAG,iBAAiB,CAiBxF"}
|