@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,381 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { hashPassword, isHashedPassword, HashedPassword } from '../utils/password.js';
|
|
3
|
+
/**
|
|
4
|
+
* Format field name for display in error messages
|
|
5
|
+
*/
|
|
6
|
+
function formatFieldName(fieldName) {
|
|
7
|
+
return fieldName
|
|
8
|
+
.replace(/([A-Z])/g, ' $1')
|
|
9
|
+
.replace(/^./, (str) => str.toUpperCase())
|
|
10
|
+
.trim();
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Text field
|
|
14
|
+
*/
|
|
15
|
+
export function text(options) {
|
|
16
|
+
return {
|
|
17
|
+
type: 'text',
|
|
18
|
+
...options,
|
|
19
|
+
getZodSchema: (fieldName, operation) => {
|
|
20
|
+
const validation = options?.validation;
|
|
21
|
+
const isRequired = validation?.isRequired;
|
|
22
|
+
const length = validation?.length;
|
|
23
|
+
const minLength = length?.min && length.min > 0 ? length.min : 1;
|
|
24
|
+
const baseSchema = z.string({
|
|
25
|
+
message: `${formatFieldName(fieldName)} must be text`,
|
|
26
|
+
});
|
|
27
|
+
const withMin = isRequired || length?.min !== undefined
|
|
28
|
+
? baseSchema.min(minLength, {
|
|
29
|
+
message: minLength > 1
|
|
30
|
+
? `${formatFieldName(fieldName)} must be at least ${minLength} characters`
|
|
31
|
+
: `${formatFieldName(fieldName)} is required`,
|
|
32
|
+
})
|
|
33
|
+
: baseSchema;
|
|
34
|
+
const withMax = length?.max !== undefined
|
|
35
|
+
? withMin.max(length.max, {
|
|
36
|
+
message: `${formatFieldName(fieldName)} must be at most ${length.max} characters`,
|
|
37
|
+
})
|
|
38
|
+
: withMin;
|
|
39
|
+
if (isRequired && operation === 'update') {
|
|
40
|
+
return z.union([withMax, z.undefined()]);
|
|
41
|
+
}
|
|
42
|
+
return !isRequired ? withMax.optional() : withMax;
|
|
43
|
+
},
|
|
44
|
+
getPrismaType: () => {
|
|
45
|
+
const validation = options?.validation;
|
|
46
|
+
const isRequired = validation?.isRequired;
|
|
47
|
+
let modifiers = '';
|
|
48
|
+
// Optional modifier
|
|
49
|
+
if (!isRequired) {
|
|
50
|
+
modifiers += '?';
|
|
51
|
+
}
|
|
52
|
+
// Unique/index modifiers
|
|
53
|
+
if (options?.isIndexed === 'unique') {
|
|
54
|
+
modifiers += ' @unique';
|
|
55
|
+
}
|
|
56
|
+
else if (options?.isIndexed === true) {
|
|
57
|
+
modifiers += ' @index';
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
type: 'String',
|
|
61
|
+
modifiers: modifiers || undefined,
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
getTypeScriptType: () => {
|
|
65
|
+
const validation = options?.validation;
|
|
66
|
+
const isRequired = validation?.isRequired;
|
|
67
|
+
return {
|
|
68
|
+
type: 'string',
|
|
69
|
+
optional: !isRequired,
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Integer field
|
|
76
|
+
*/
|
|
77
|
+
export function integer(options) {
|
|
78
|
+
return {
|
|
79
|
+
type: 'integer',
|
|
80
|
+
...options,
|
|
81
|
+
getZodSchema: (fieldName, operation) => {
|
|
82
|
+
const baseSchema = z.number({
|
|
83
|
+
message: `${formatFieldName(fieldName)} must be a number`,
|
|
84
|
+
});
|
|
85
|
+
const withMin = options?.validation?.min !== undefined
|
|
86
|
+
? baseSchema.min(options.validation.min, {
|
|
87
|
+
message: `${formatFieldName(fieldName)} must be at least ${options.validation.min}`,
|
|
88
|
+
})
|
|
89
|
+
: baseSchema;
|
|
90
|
+
const withMax = options?.validation?.max !== undefined
|
|
91
|
+
? withMin.max(options.validation.max, {
|
|
92
|
+
message: `${formatFieldName(fieldName)} must be at most ${options.validation.max}`,
|
|
93
|
+
})
|
|
94
|
+
: withMin;
|
|
95
|
+
return !options?.validation?.isRequired || operation === 'update'
|
|
96
|
+
? withMax.optional()
|
|
97
|
+
: withMax;
|
|
98
|
+
},
|
|
99
|
+
getPrismaType: () => {
|
|
100
|
+
const isRequired = options?.validation?.isRequired;
|
|
101
|
+
return {
|
|
102
|
+
type: 'Int',
|
|
103
|
+
modifiers: isRequired ? undefined : '?',
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
getTypeScriptType: () => {
|
|
107
|
+
const isRequired = options?.validation?.isRequired;
|
|
108
|
+
return {
|
|
109
|
+
type: 'number',
|
|
110
|
+
optional: !isRequired,
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Checkbox (boolean) field
|
|
117
|
+
*/
|
|
118
|
+
export function checkbox(options) {
|
|
119
|
+
return {
|
|
120
|
+
type: 'checkbox',
|
|
121
|
+
...options,
|
|
122
|
+
getZodSchema: () => {
|
|
123
|
+
return z.boolean().optional();
|
|
124
|
+
},
|
|
125
|
+
getPrismaType: () => {
|
|
126
|
+
const hasDefault = options?.defaultValue !== undefined;
|
|
127
|
+
let modifiers = '';
|
|
128
|
+
if (hasDefault) {
|
|
129
|
+
modifiers = ` @default(${options.defaultValue})`;
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
type: 'Boolean',
|
|
133
|
+
modifiers: modifiers || undefined,
|
|
134
|
+
};
|
|
135
|
+
},
|
|
136
|
+
getTypeScriptType: () => {
|
|
137
|
+
return {
|
|
138
|
+
type: 'boolean',
|
|
139
|
+
optional: options?.defaultValue === undefined,
|
|
140
|
+
};
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Timestamp (DateTime) field
|
|
146
|
+
*/
|
|
147
|
+
export function timestamp(options) {
|
|
148
|
+
return {
|
|
149
|
+
type: 'timestamp',
|
|
150
|
+
...options,
|
|
151
|
+
getZodSchema: () => {
|
|
152
|
+
return z.union([z.date(), z.iso.datetime()]).optional();
|
|
153
|
+
},
|
|
154
|
+
getPrismaType: () => {
|
|
155
|
+
let modifiers = '?';
|
|
156
|
+
// Check for default value
|
|
157
|
+
if (options?.defaultValue &&
|
|
158
|
+
typeof options.defaultValue === 'object' &&
|
|
159
|
+
'kind' in options.defaultValue &&
|
|
160
|
+
options.defaultValue.kind === 'now') {
|
|
161
|
+
modifiers = ' @default(now())';
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
type: 'DateTime',
|
|
165
|
+
modifiers,
|
|
166
|
+
};
|
|
167
|
+
},
|
|
168
|
+
getTypeScriptType: () => {
|
|
169
|
+
const hasDefault = options?.defaultValue &&
|
|
170
|
+
typeof options.defaultValue === 'object' &&
|
|
171
|
+
'kind' in options.defaultValue &&
|
|
172
|
+
options.defaultValue.kind === 'now';
|
|
173
|
+
return {
|
|
174
|
+
type: 'Date',
|
|
175
|
+
optional: !hasDefault,
|
|
176
|
+
};
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Password field (automatically hashed using bcrypt)
|
|
182
|
+
*
|
|
183
|
+
* **Security Features:**
|
|
184
|
+
* - Passwords are automatically hashed during create/update operations
|
|
185
|
+
* - Uses bcrypt with cost factor 10 (good balance of security and performance)
|
|
186
|
+
* - Already-hashed passwords are not re-hashed (idempotent)
|
|
187
|
+
* - Password values in query results include a `compare()` method for authentication
|
|
188
|
+
*
|
|
189
|
+
* **Usage Example:**
|
|
190
|
+
* ```typescript
|
|
191
|
+
* // In opensaas.config.ts
|
|
192
|
+
* fields: {
|
|
193
|
+
* password: password({
|
|
194
|
+
* validation: { isRequired: true }
|
|
195
|
+
* })
|
|
196
|
+
* }
|
|
197
|
+
*
|
|
198
|
+
* // Creating a user - password is automatically hashed
|
|
199
|
+
* const user = await context.db.user.create({
|
|
200
|
+
* data: {
|
|
201
|
+
* email: 'user@example.com',
|
|
202
|
+
* password: 'plaintextPassword' // Automatically hashed before storage
|
|
203
|
+
* }
|
|
204
|
+
* })
|
|
205
|
+
*
|
|
206
|
+
* // Authenticating - use the compare() method
|
|
207
|
+
* const user = await context.db.user.findUnique({
|
|
208
|
+
* where: { email: 'user@example.com' }
|
|
209
|
+
* })
|
|
210
|
+
*
|
|
211
|
+
* if (user && await user.password.compare('plaintextPassword')) {
|
|
212
|
+
* // Password is correct - login successful
|
|
213
|
+
* }
|
|
214
|
+
* ```
|
|
215
|
+
*
|
|
216
|
+
* **Important Notes:**
|
|
217
|
+
* - Password fields are excluded from read operations by default in access control
|
|
218
|
+
* - Always use the `compare()` method to verify passwords - never compare strings directly
|
|
219
|
+
* - The password field value has type `HashedPassword` which extends string with compare()
|
|
220
|
+
* - Empty strings and undefined values are skipped (not hashed) to allow partial updates
|
|
221
|
+
*
|
|
222
|
+
* **Implementation Details:**
|
|
223
|
+
* - Uses field-level hooks (`resolveInput` and `resolveOutput`) for automatic transformations
|
|
224
|
+
* - The hashing happens via `hooks.resolveInput` during create/update operations
|
|
225
|
+
* - The wrapping happens via `hooks.resolveOutput` during read operations
|
|
226
|
+
* - This pattern allows third-party field types to define their own transformations
|
|
227
|
+
*
|
|
228
|
+
* @param options - Field configuration options
|
|
229
|
+
* @returns Password field configuration
|
|
230
|
+
*/
|
|
231
|
+
export function password(options) {
|
|
232
|
+
return {
|
|
233
|
+
type: 'password',
|
|
234
|
+
...options,
|
|
235
|
+
typePatch: {
|
|
236
|
+
resultType: "import('@opensaas/stack-core').HashedPassword",
|
|
237
|
+
patchScope: 'scalars-only',
|
|
238
|
+
},
|
|
239
|
+
ui: {
|
|
240
|
+
...options?.ui,
|
|
241
|
+
valueForClientSerialization: ({ value }) => ({ isSet: !!value }),
|
|
242
|
+
},
|
|
243
|
+
hooks: {
|
|
244
|
+
// Hash password before writing to database
|
|
245
|
+
resolveInput: async ({ inputValue }) => {
|
|
246
|
+
// Skip if undefined or null (allows partial updates)
|
|
247
|
+
if (inputValue === undefined || inputValue === null) {
|
|
248
|
+
return inputValue;
|
|
249
|
+
}
|
|
250
|
+
// Skip if not a string
|
|
251
|
+
if (typeof inputValue !== 'string') {
|
|
252
|
+
return inputValue;
|
|
253
|
+
}
|
|
254
|
+
// Skip empty strings (let validation handle this)
|
|
255
|
+
if (inputValue.length === 0) {
|
|
256
|
+
return inputValue;
|
|
257
|
+
}
|
|
258
|
+
// Skip if already hashed (idempotent)
|
|
259
|
+
if (isHashedPassword(inputValue)) {
|
|
260
|
+
return inputValue;
|
|
261
|
+
}
|
|
262
|
+
// Hash the password
|
|
263
|
+
return await hashPassword(inputValue);
|
|
264
|
+
},
|
|
265
|
+
// Wrap password with HashedPassword class after reading from database
|
|
266
|
+
resolveOutput: ({ value }) => {
|
|
267
|
+
// Only wrap string values (hashed passwords)
|
|
268
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
269
|
+
return new HashedPassword(value);
|
|
270
|
+
}
|
|
271
|
+
return undefined;
|
|
272
|
+
},
|
|
273
|
+
// Merge with user-provided hooks if any
|
|
274
|
+
...options?.hooks,
|
|
275
|
+
},
|
|
276
|
+
getZodSchema: (fieldName, operation) => {
|
|
277
|
+
const validation = options?.validation;
|
|
278
|
+
const isRequired = validation?.isRequired;
|
|
279
|
+
if (isRequired && operation === 'create') {
|
|
280
|
+
// Required in create mode: reject undefined and empty strings
|
|
281
|
+
return z
|
|
282
|
+
.string({
|
|
283
|
+
message: `${formatFieldName(fieldName)} must be text`,
|
|
284
|
+
})
|
|
285
|
+
.min(1, {
|
|
286
|
+
message: `${formatFieldName(fieldName)} is required`,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
else if (isRequired && operation === 'update') {
|
|
290
|
+
// Required in update mode: if provided, reject empty strings
|
|
291
|
+
return z.union([
|
|
292
|
+
z.string().min(1, {
|
|
293
|
+
message: `${formatFieldName(fieldName)} is required`,
|
|
294
|
+
}),
|
|
295
|
+
z.undefined(),
|
|
296
|
+
]);
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
// Not required: can be undefined or any string
|
|
300
|
+
return z
|
|
301
|
+
.string({
|
|
302
|
+
message: `${formatFieldName(fieldName)} must be text`,
|
|
303
|
+
})
|
|
304
|
+
.optional();
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
getPrismaType: () => {
|
|
308
|
+
const isRequired = options?.validation?.isRequired;
|
|
309
|
+
return {
|
|
310
|
+
type: 'String',
|
|
311
|
+
modifiers: isRequired ? undefined : '?',
|
|
312
|
+
};
|
|
313
|
+
},
|
|
314
|
+
getTypeScriptType: () => {
|
|
315
|
+
const isRequired = options?.validation?.isRequired;
|
|
316
|
+
return {
|
|
317
|
+
type: 'string',
|
|
318
|
+
optional: !isRequired,
|
|
319
|
+
};
|
|
320
|
+
},
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Select field (enum-like)
|
|
325
|
+
*/
|
|
326
|
+
export function select(options) {
|
|
327
|
+
if (!options.options || options.options.length === 0) {
|
|
328
|
+
throw new Error('Select field must have at least one option');
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
type: 'select',
|
|
332
|
+
...options,
|
|
333
|
+
getZodSchema: (fieldName, operation) => {
|
|
334
|
+
const values = options.options.map((opt) => opt.value);
|
|
335
|
+
let schema = z.enum(values, {
|
|
336
|
+
message: `${formatFieldName(fieldName)} must be one of: ${values.join(', ')}`,
|
|
337
|
+
});
|
|
338
|
+
if (!options.validation?.isRequired || operation === 'update') {
|
|
339
|
+
schema = schema.optional();
|
|
340
|
+
}
|
|
341
|
+
return schema;
|
|
342
|
+
},
|
|
343
|
+
getPrismaType: () => {
|
|
344
|
+
let modifiers = '?';
|
|
345
|
+
// Add default value if provided
|
|
346
|
+
if (options.defaultValue !== undefined) {
|
|
347
|
+
modifiers = ` @default("${options.defaultValue}")`;
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
type: 'String',
|
|
351
|
+
modifiers,
|
|
352
|
+
};
|
|
353
|
+
},
|
|
354
|
+
getTypeScriptType: () => {
|
|
355
|
+
// Generate union type from options
|
|
356
|
+
const unionType = options.options.map((opt) => `'${opt.value}'`).join(' | ');
|
|
357
|
+
return {
|
|
358
|
+
type: unionType,
|
|
359
|
+
optional: !options.validation?.isRequired || options.defaultValue !== undefined,
|
|
360
|
+
};
|
|
361
|
+
},
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Relationship field
|
|
366
|
+
*/
|
|
367
|
+
export function relationship(options) {
|
|
368
|
+
if (!options.ref) {
|
|
369
|
+
throw new Error('Relationship field must have a ref');
|
|
370
|
+
}
|
|
371
|
+
// Validate ref format: 'ListName.fieldName'
|
|
372
|
+
const refParts = options.ref.split('.');
|
|
373
|
+
if (refParts.length !== 2) {
|
|
374
|
+
throw new Error(`Invalid relationship ref format: "${options.ref}". Expected format: "ListName.fieldName"`);
|
|
375
|
+
}
|
|
376
|
+
return {
|
|
377
|
+
type: 'relationship',
|
|
378
|
+
...options,
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/fields/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { Hooks } from '../config/types.js';
|
|
2
|
+
import type { AccessContext } from '../access/types.js';
|
|
3
|
+
import type { FieldConfig } from '../config/types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Validation error collection
|
|
6
|
+
*/
|
|
7
|
+
export declare class ValidationError extends Error {
|
|
8
|
+
errors: string[];
|
|
9
|
+
fieldErrors: Record<string, string>;
|
|
10
|
+
constructor(errors: string[], fieldErrors?: Record<string, string>);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Execute resolveInput hook
|
|
14
|
+
* Allows modification of input data before validation
|
|
15
|
+
*/
|
|
16
|
+
export declare function executeResolveInput<T = Record<string, unknown>>(hooks: Hooks<T> | undefined, args: {
|
|
17
|
+
operation: 'create' | 'update';
|
|
18
|
+
resolvedData: Partial<T>;
|
|
19
|
+
item?: T;
|
|
20
|
+
context: AccessContext;
|
|
21
|
+
}): Promise<Partial<T>>;
|
|
22
|
+
/**
|
|
23
|
+
* Execute validateInput hook
|
|
24
|
+
* Allows custom validation logic
|
|
25
|
+
*/
|
|
26
|
+
export declare function executeValidateInput<T = Record<string, unknown>>(hooks: Hooks<T> | undefined, args: {
|
|
27
|
+
operation: 'create' | 'update';
|
|
28
|
+
resolvedData: Partial<T>;
|
|
29
|
+
item?: T;
|
|
30
|
+
context: AccessContext;
|
|
31
|
+
}): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Execute beforeOperation hook
|
|
34
|
+
* Runs before database operation (cannot modify data)
|
|
35
|
+
*/
|
|
36
|
+
export declare function executeBeforeOperation<T = Record<string, unknown>>(hooks: Hooks<T> | undefined, args: {
|
|
37
|
+
operation: 'create' | 'update' | 'delete';
|
|
38
|
+
item?: T;
|
|
39
|
+
context: AccessContext;
|
|
40
|
+
}): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Execute afterOperation hook
|
|
43
|
+
* Runs after database operation
|
|
44
|
+
*/
|
|
45
|
+
export declare function executeAfterOperation<T = Record<string, unknown>>(hooks: Hooks<T> | undefined, args: {
|
|
46
|
+
operation: 'create' | 'update' | 'delete';
|
|
47
|
+
item: T;
|
|
48
|
+
context: AccessContext;
|
|
49
|
+
}): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Validate field-level validation rules using Zod
|
|
52
|
+
* Checks isRequired, length constraints, etc.
|
|
53
|
+
*/
|
|
54
|
+
export declare function validateFieldRules(data: Record<string, unknown>, fieldConfigs: Record<string, FieldConfig>, operation?: 'create' | 'update'): {
|
|
55
|
+
errors: string[];
|
|
56
|
+
fieldErrors: Record<string, string>;
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAGrD;;GAEG;AACH,qBAAa,eAAgB,SAAQ,KAAK;IACjC,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;gBAE9B,MAAM,EAAE,MAAM,EAAE,EAAE,WAAW,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM;CAMvE;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,SAAS,EAC3B,IAAI,EAAE;IACJ,SAAS,EAAE,QAAQ,GAAG,QAAQ,CAAA;IAC9B,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IACxB,IAAI,CAAC,EAAE,CAAC,CAAA;IACR,OAAO,EAAE,aAAa,CAAA;CACvB,GACA,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAOrB;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,SAAS,EAC3B,IAAI,EAAE;IACJ,SAAS,EAAE,QAAQ,GAAG,QAAQ,CAAA;IAC9B,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IACxB,IAAI,CAAC,EAAE,CAAC,CAAA;IACR,OAAO,EAAE,aAAa,CAAA;CACvB,GACA,OAAO,CAAC,IAAI,CAAC,CAmBf;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,SAAS,EAC3B,IAAI,EAAE;IACJ,SAAS,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;IACzC,IAAI,CAAC,EAAE,CAAC,CAAA;IACR,OAAO,EAAE,aAAa,CAAA;CACvB,GACA,OAAO,CAAC,IAAI,CAAC,CAMf;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,SAAS,EAC3B,IAAI,EAAE;IACJ,SAAS,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;IACzC,IAAI,EAAE,CAAC,CAAA;IACP,OAAO,EAAE,aAAa,CAAA;CACvB,GACA,OAAO,CAAC,IAAI,CAAC,CAMf;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACzC,SAAS,GAAE,QAAQ,GAAG,QAAmB,GACxC;IAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAW3D"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { validateWithZod } from '../validation/schema.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validation error collection
|
|
4
|
+
*/
|
|
5
|
+
export class ValidationError extends Error {
|
|
6
|
+
errors;
|
|
7
|
+
fieldErrors;
|
|
8
|
+
constructor(errors, fieldErrors = {}) {
|
|
9
|
+
super(`Validation failed: ${errors.join(', ')}`);
|
|
10
|
+
this.name = 'ValidationError';
|
|
11
|
+
this.errors = errors;
|
|
12
|
+
this.fieldErrors = fieldErrors;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Execute resolveInput hook
|
|
17
|
+
* Allows modification of input data before validation
|
|
18
|
+
*/
|
|
19
|
+
export async function executeResolveInput(hooks, args) {
|
|
20
|
+
if (!hooks?.resolveInput) {
|
|
21
|
+
return args.resolvedData;
|
|
22
|
+
}
|
|
23
|
+
const result = await hooks.resolveInput(args);
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Execute validateInput hook
|
|
28
|
+
* Allows custom validation logic
|
|
29
|
+
*/
|
|
30
|
+
export async function executeValidateInput(hooks, args) {
|
|
31
|
+
if (!hooks?.validateInput) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const errors = [];
|
|
35
|
+
const addValidationError = (msg) => {
|
|
36
|
+
errors.push(msg);
|
|
37
|
+
};
|
|
38
|
+
await hooks.validateInput({
|
|
39
|
+
...args,
|
|
40
|
+
addValidationError,
|
|
41
|
+
});
|
|
42
|
+
if (errors.length > 0) {
|
|
43
|
+
throw new ValidationError(errors);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Execute beforeOperation hook
|
|
48
|
+
* Runs before database operation (cannot modify data)
|
|
49
|
+
*/
|
|
50
|
+
export async function executeBeforeOperation(hooks, args) {
|
|
51
|
+
if (!hooks?.beforeOperation) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
await hooks.beforeOperation(args);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Execute afterOperation hook
|
|
58
|
+
* Runs after database operation
|
|
59
|
+
*/
|
|
60
|
+
export async function executeAfterOperation(hooks, args) {
|
|
61
|
+
if (!hooks?.afterOperation) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
await hooks.afterOperation(args);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Validate field-level validation rules using Zod
|
|
68
|
+
* Checks isRequired, length constraints, etc.
|
|
69
|
+
*/
|
|
70
|
+
export function validateFieldRules(data, fieldConfigs, operation = 'create') {
|
|
71
|
+
const result = validateWithZod(data, fieldConfigs, operation);
|
|
72
|
+
if (result.success) {
|
|
73
|
+
return { errors: [], fieldErrors: {} };
|
|
74
|
+
}
|
|
75
|
+
// Convert field errors to array of error messages
|
|
76
|
+
const errors = Object.entries(result.errors).map(([_field, message]) => message);
|
|
77
|
+
return { errors, fieldErrors: result.errors };
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAEzD;;GAEG;AACH,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACjC,MAAM,CAAU;IAChB,WAAW,CAAwB;IAE1C,YAAY,MAAgB,EAAE,cAAsC,EAAE;QACpE,KAAK,CAAC,sBAAsB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QAChD,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAA;QAC7B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAA;IAChC,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,KAA2B,EAC3B,IAKC;IAED,IAAI,CAAC,KAAK,EAAE,YAAY,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC,YAAY,CAAA;IAC1B,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,CAAA;IAC7C,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,KAA2B,EAC3B,IAKC;IAED,IAAI,CAAC,KAAK,EAAE,aAAa,EAAE,CAAC;QAC1B,OAAM;IACR,CAAC;IAED,MAAM,MAAM,GAAa,EAAE,CAAA;IAE3B,MAAM,kBAAkB,GAAG,CAAC,GAAW,EAAE,EAAE;QACzC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAClB,CAAC,CAAA;IAED,MAAM,KAAK,CAAC,aAAa,CAAC;QACxB,GAAG,IAAI;QACP,kBAAkB;KACnB,CAAC,CAAA;IAEF,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,eAAe,CAAC,MAAM,CAAC,CAAA;IACnC,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,KAA2B,EAC3B,IAIC;IAED,IAAI,CAAC,KAAK,EAAE,eAAe,EAAE,CAAC;QAC5B,OAAM;IACR,CAAC;IAED,MAAM,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,CAAA;AACnC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,KAA2B,EAC3B,IAIC;IAED,IAAI,CAAC,KAAK,EAAE,cAAc,EAAE,CAAC;QAC3B,OAAM;IACR,CAAC;IAED,MAAM,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,CAAA;AAClC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,IAA6B,EAC7B,YAAyC,EACzC,YAAiC,QAAQ;IAEzC,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,EAAE,YAAY,EAAE,SAAS,CAAC,CAAA;IAE7D,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,CAAA;IACxC,CAAC;IAED,kDAAkD;IAClD,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAA;IAEhF,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,CAAA;AAC/C,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { config, list } from './config/index.js';
|
|
2
|
+
export type { OpenSaasConfig, ListConfig, FieldConfig, BaseFieldConfig, TextField, IntegerField, CheckboxField, TimestampField, PasswordField, SelectField, RelationshipField, OperationAccess, Hooks, FieldHooks, DatabaseConfig, SessionConfig, UIConfig, ThemeConfig, ThemePreset, ThemeColors, } from './config/index.js';
|
|
3
|
+
export type { AccessControl, FieldAccess, Session, AccessContext, PrismaFilter, AccessControlledDB, } from './access/index.js';
|
|
4
|
+
export { getContext } from './context/index.js';
|
|
5
|
+
export type { PrismaClientLike } from './access/types.js';
|
|
6
|
+
export type { ServerActionProps } from './context/index.js';
|
|
7
|
+
export { getDbKey, getUrlKey, getListKeyFromUrl, pascalToCamel, pascalToKebab, kebabToPascal, kebabToCamel, } from './lib/case-utils.js';
|
|
8
|
+
export { ValidationError } from './hooks/index.js';
|
|
9
|
+
export { validateWithZod, generateZodSchema } from './validation/schema.js';
|
|
10
|
+
export { hashPassword, comparePassword, isHashedPassword, HashedPassword, } from './utils/password.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAA;AAChD,YAAY,EACV,cAAc,EACd,UAAU,EACV,WAAW,EACX,eAAe,EACf,SAAS,EACT,YAAY,EACZ,aAAa,EACb,cAAc,EACd,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,KAAK,EACL,UAAU,EACV,cAAc,EACd,aAAa,EACb,QAAQ,EACR,WAAW,EACX,WAAW,EACX,WAAW,GACZ,MAAM,mBAAmB,CAAA;AAG1B,YAAY,EACV,aAAa,EACb,WAAW,EACX,OAAO,EACP,aAAa,EACb,YAAY,EACZ,kBAAkB,GACnB,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAC/C,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACzD,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAG3D,OAAO,EACL,QAAQ,EACR,SAAS,EACT,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,aAAa,EACb,YAAY,GACb,MAAM,qBAAqB,CAAA;AAG5B,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAG3E,OAAO,EACL,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,cAAc,GACf,MAAM,qBAAqB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Config system
|
|
2
|
+
export { config, list } from './config/index.js';
|
|
3
|
+
// Context
|
|
4
|
+
export { getContext } from './context/index.js';
|
|
5
|
+
// Utilities
|
|
6
|
+
export { getDbKey, getUrlKey, getListKeyFromUrl, pascalToCamel, pascalToKebab, kebabToPascal, kebabToCamel, } from './lib/case-utils.js';
|
|
7
|
+
// Hooks and validation
|
|
8
|
+
export { ValidationError } from './hooks/index.js';
|
|
9
|
+
export { validateWithZod, generateZodSchema } from './validation/schema.js';
|
|
10
|
+
// Password utilities
|
|
11
|
+
export { hashPassword, comparePassword, isHashedPassword, HashedPassword, } from './utils/password.js';
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gBAAgB;AAChB,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAA;AAkChD,UAAU;AACV,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAI/C,YAAY;AACZ,OAAO,EACL,QAAQ,EACR,SAAS,EACT,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,aAAa,EACb,YAAY,GACb,MAAM,qBAAqB,CAAA;AAE5B,uBAAuB;AACvB,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAE3E,qBAAqB;AACrB,OAAO,EACL,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,cAAc,GACf,MAAM,qBAAqB,CAAA"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Case conversion utilities for consistent naming across the stack
|
|
3
|
+
*
|
|
4
|
+
* - Config list names: PascalCase (e.g., "AuthUser", "BlogPost")
|
|
5
|
+
* - Prisma models: PascalCase (e.g., "AuthUser", "BlogPost")
|
|
6
|
+
* - Prisma client properties: camelCase (e.g., "authUser", "blogPost")
|
|
7
|
+
* - Context db properties: camelCase (e.g., "authUser", "blogPost")
|
|
8
|
+
* - URLs: kebab-case (e.g., "auth-user", "blog-post")
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Convert PascalCase to camelCase
|
|
12
|
+
* AuthUser -> authUser
|
|
13
|
+
* BlogPost -> blogPost
|
|
14
|
+
*/
|
|
15
|
+
export declare function pascalToCamel(str: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Convert PascalCase to kebab-case
|
|
18
|
+
* AuthUser -> auth-user
|
|
19
|
+
* BlogPost -> blog-post
|
|
20
|
+
*/
|
|
21
|
+
export declare function pascalToKebab(str: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Convert kebab-case to PascalCase
|
|
24
|
+
* auth-user -> AuthUser
|
|
25
|
+
* blog-post -> BlogPost
|
|
26
|
+
*/
|
|
27
|
+
export declare function kebabToPascal(str: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* Convert kebab-case to camelCase
|
|
30
|
+
* auth-user -> authUser
|
|
31
|
+
* blog-post -> blogPost
|
|
32
|
+
*/
|
|
33
|
+
export declare function kebabToCamel(str: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* Get the database key for a list (camelCase)
|
|
36
|
+
* Used for accessing context.db and prisma client
|
|
37
|
+
*/
|
|
38
|
+
export declare function getDbKey(listKey: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* Get the URL segment for a list (kebab-case)
|
|
41
|
+
* Used for constructing admin URLs
|
|
42
|
+
*/
|
|
43
|
+
export declare function getUrlKey(listKey: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* Get the list key from a URL segment (PascalCase)
|
|
46
|
+
* Used for parsing admin URLs
|
|
47
|
+
*/
|
|
48
|
+
export declare function getListKeyFromUrl(urlSegment: string): string;
|
|
49
|
+
//# sourceMappingURL=case-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"case-utils.d.ts","sourceRoot":"","sources":["../../src/lib/case-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIjD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKjD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE5D"}
|