vite-plugin-ferry 0.1.2 → 0.1.4
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/README.md +246 -2
- package/dist/generators/enums.d.ts +3 -1
- package/dist/generators/enums.d.ts.map +1 -1
- package/dist/generators/enums.js +45 -52
- package/dist/generators/resources.d.ts +4 -6
- package/dist/generators/resources.d.ts.map +1 -1
- package/dist/generators/resources.js +35 -197
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -8
- package/dist/utils/php-parser.d.ts +29 -12
- package/dist/utils/php-parser.d.ts.map +1 -1
- package/dist/utils/php-parser.js +524 -73
- package/dist/utils/ts-generator.d.ts +67 -0
- package/dist/utils/ts-generator.d.ts.map +1 -0
- package/dist/utils/ts-generator.js +179 -0
- package/package.json +11 -3
|
@@ -1,221 +1,53 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
1
2
|
import { existsSync } from 'node:fs';
|
|
2
3
|
import { join, parse } from 'node:path';
|
|
3
4
|
import { getPhpFiles, readFileSafe, writeFileEnsureDir } from '../utils/file.js';
|
|
4
|
-
import { extractDocblockArrayShape,
|
|
5
|
-
import { mapDocTypeToTs
|
|
6
|
-
|
|
7
|
-
* Map a PHP cast to a TypeScript type, potentially collecting enum references.
|
|
8
|
-
*/
|
|
9
|
-
function mapCastToType(cast, enumsDir, collectedEnums) {
|
|
10
|
-
const original = cast;
|
|
11
|
-
const lower = cast.toLowerCase();
|
|
12
|
-
// Try to find enum in app/Enums
|
|
13
|
-
const match = original.match(/([A-Za-z0-9_\\]+)$/);
|
|
14
|
-
const short = match ? match[1].replace(/^\\+/, '') : original;
|
|
15
|
-
const enumPath = join(enumsDir, `${short}.php`);
|
|
16
|
-
if (existsSync(enumPath)) {
|
|
17
|
-
const def = parseEnumFile(enumPath);
|
|
18
|
-
if (def) {
|
|
19
|
-
collectedEnums[def.name] = def;
|
|
20
|
-
return def.name;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return mapPhpTypeToTs(cast);
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Infer TypeScript type from a PHP resource value expression.
|
|
27
|
-
*/
|
|
28
|
-
function inferTypeFromValue(value, key, resourceClass, resourcesDir, modelsDir, enumsDir, collectedEnums) {
|
|
29
|
-
let optional = false;
|
|
30
|
-
// Resource::collection
|
|
31
|
-
const collMatch = value.match(/([A-Za-z0-9_]+)::collection\s*\(\s*(.*?)\s*\)/);
|
|
32
|
-
if (collMatch) {
|
|
33
|
-
const res = collMatch[1];
|
|
34
|
-
const inside = collMatch[2];
|
|
35
|
-
if (inside.includes('whenLoaded('))
|
|
36
|
-
optional = true;
|
|
37
|
-
return { type: `${res}[]`, optional };
|
|
38
|
-
}
|
|
39
|
-
// whenLoaded
|
|
40
|
-
const whenLoadedMatch = value.match(/whenLoaded\(\s*["']([A-Za-z0-9_]+)["']\s*\)/);
|
|
41
|
-
if (whenLoadedMatch) {
|
|
42
|
-
const name = whenLoadedMatch[1];
|
|
43
|
-
optional = true;
|
|
44
|
-
const candidate = `${name[0].toUpperCase()}${name.slice(1)}Resource`;
|
|
45
|
-
const resPath = join(resourcesDir, `${candidate}.php`);
|
|
46
|
-
if (existsSync(resPath)) {
|
|
47
|
-
return { type: candidate, optional };
|
|
48
|
-
}
|
|
49
|
-
return { type: 'Record<string, any>', optional };
|
|
50
|
-
}
|
|
51
|
-
// $this->resource->property
|
|
52
|
-
const propMatch = value.match(/\$this->resource->([A-Za-z0-9_]+)/);
|
|
53
|
-
if (propMatch) {
|
|
54
|
-
const prop = propMatch[1];
|
|
55
|
-
// Boolean checks
|
|
56
|
-
if (/\?\s*true\s*:\s*false|===\s*(true|false)|==\s*(true|false)/i.test(value)) {
|
|
57
|
-
return { type: 'boolean', optional: false };
|
|
58
|
-
}
|
|
59
|
-
if (/\$this->resource->(is|has)[A-Za-z0-9_]*\s*\(/i.test(value)) {
|
|
60
|
-
return { type: 'boolean', optional: false };
|
|
61
|
-
}
|
|
62
|
-
const lower = prop.toLowerCase();
|
|
63
|
-
if (lower.startsWith('is_') || lower.startsWith('has_') || /^(is|has)[A-Z]/.test(prop)) {
|
|
64
|
-
return { type: 'boolean', optional: false };
|
|
65
|
-
}
|
|
66
|
-
// IDs and UUIDs
|
|
67
|
-
if (prop === 'id' || prop.endsWith('_id') || lower === 'uuid') {
|
|
68
|
-
return { type: 'string', optional: false };
|
|
69
|
-
}
|
|
70
|
-
// Check model casts
|
|
71
|
-
const modelCandidate = resourceClass.replace(/Resource$/, '');
|
|
72
|
-
const modelPath = join(modelsDir, `${modelCandidate}.php`);
|
|
73
|
-
if (existsSync(modelPath)) {
|
|
74
|
-
const casts = getModelCasts(modelPath);
|
|
75
|
-
if (casts[prop]) {
|
|
76
|
-
const cast = casts[prop];
|
|
77
|
-
const trim = cast.trim();
|
|
78
|
-
const tsType = trim.startsWith('{') || trim.includes(':') || /array\s*\{/.test(trim)
|
|
79
|
-
? trim
|
|
80
|
-
: mapCastToType(cast, enumsDir, collectedEnums);
|
|
81
|
-
return { type: tsType, optional: false };
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
// Number heuristics
|
|
85
|
-
if (['last4', 'count', 'total'].includes(prop) || /\d$/.test(prop)) {
|
|
86
|
-
return { type: 'number', optional: false };
|
|
87
|
-
}
|
|
88
|
-
// String heuristics
|
|
89
|
-
if (['id', 'uuid', 'slug', 'name', 'repository', 'region', 'email'].includes(prop)) {
|
|
90
|
-
return { type: 'string', optional: false };
|
|
91
|
-
}
|
|
92
|
-
// Timestamps
|
|
93
|
-
if (prop.endsWith('_at') || ['created_at', 'updated_at', 'lastActive'].includes(prop)) {
|
|
94
|
-
return { type: 'string', optional: false };
|
|
95
|
-
}
|
|
96
|
-
return { type: 'string', optional: false };
|
|
97
|
-
}
|
|
98
|
-
return { type: 'any', optional: false };
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Parse fields from a PHP array block (from toArray() method).
|
|
102
|
-
*/
|
|
103
|
-
function parseFieldsFromArrayBlock(block, resourceClass, docShape, resourcesDir, modelsDir, enumsDir, collectedEnums) {
|
|
104
|
-
const lines = block.split(/\r?\n/);
|
|
105
|
-
const fields = {};
|
|
106
|
-
for (let i = 0; i < lines.length; i++) {
|
|
107
|
-
const line = lines[i].trim();
|
|
108
|
-
if (!line || line.startsWith('//'))
|
|
109
|
-
continue;
|
|
110
|
-
const match = line.match(/["'](?<key>[A-Za-z0-9_]+)["']\s*=>\s*(?<value>.*?)(?:,\s*$|$)/);
|
|
111
|
-
if (!match || !match.groups)
|
|
112
|
-
continue;
|
|
113
|
-
const key = match.groups.key;
|
|
114
|
-
let value = match.groups.value.trim();
|
|
115
|
-
// Boolean heuristic
|
|
116
|
-
const lowerKey = key.toLowerCase();
|
|
117
|
-
if (lowerKey.startsWith('is_') || lowerKey.startsWith('has_') || /^(is|has)[A-Z]/.test(key)) {
|
|
118
|
-
fields[key] = { type: 'boolean', optional: false };
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
// Handle nested arrays
|
|
122
|
-
if (value.startsWith('[')) {
|
|
123
|
-
let bracketDepth = (value.match(/\[/g) || []).length - (value.match(/\]/g) || []).length;
|
|
124
|
-
const innerLines = [];
|
|
125
|
-
const rest = value.replace(/^\[\s*/, '');
|
|
126
|
-
if (rest)
|
|
127
|
-
innerLines.push(rest);
|
|
128
|
-
let j = i + 1;
|
|
129
|
-
while (j < lines.length && bracketDepth > 0) {
|
|
130
|
-
const l = lines[j];
|
|
131
|
-
bracketDepth += (l.match(/\[/g) || []).length - (l.match(/\]/g) || []).length;
|
|
132
|
-
innerLines.push(l.trim());
|
|
133
|
-
j++;
|
|
134
|
-
}
|
|
135
|
-
i = j - 1;
|
|
136
|
-
const innerBlock = innerLines.join('\n');
|
|
137
|
-
const nested = parseFieldsFromArrayBlock(innerBlock, resourceClass, docShape, resourcesDir, modelsDir, enumsDir, collectedEnums);
|
|
138
|
-
// Apply docblock shape if available
|
|
139
|
-
if (docShape && docShape[key]) {
|
|
140
|
-
const docType = docShape[key].trim();
|
|
141
|
-
if (docType.startsWith('{')) {
|
|
142
|
-
const docInner = parseTsObjectStringToPairs(docType);
|
|
143
|
-
for (const dk of Object.keys(docInner)) {
|
|
144
|
-
nested[dk] = { type: docInner[dk], optional: false };
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
const props = [];
|
|
149
|
-
for (const nkey of Object.keys(nested)) {
|
|
150
|
-
const ninfo = nested[nkey];
|
|
151
|
-
const ntype = ninfo.type || 'any';
|
|
152
|
-
const nopt = ninfo.optional ? '?' : '';
|
|
153
|
-
props.push(`${nkey}${nopt}: ${ntype}`);
|
|
154
|
-
}
|
|
155
|
-
const inline = `{ ${props.join('; ')} }`;
|
|
156
|
-
fields[key] = { type: inline, optional: false };
|
|
157
|
-
continue;
|
|
158
|
-
}
|
|
159
|
-
// Use docblock type if available
|
|
160
|
-
if (docShape && docShape[key]) {
|
|
161
|
-
fields[key] = { type: docShape[key], optional: false };
|
|
162
|
-
continue;
|
|
163
|
-
}
|
|
164
|
-
// Infer type from value
|
|
165
|
-
const info = inferTypeFromValue(value, key, resourceClass, resourcesDir, modelsDir, enumsDir, collectedEnums);
|
|
166
|
-
if (docShape && docShape[key] && (!info.type || info.type === 'any')) {
|
|
167
|
-
info.type = docShape[key];
|
|
168
|
-
info.optional = info.optional ?? false;
|
|
169
|
-
}
|
|
170
|
-
fields[key] = info;
|
|
171
|
-
}
|
|
172
|
-
return fields;
|
|
173
|
-
}
|
|
5
|
+
import { extractDocblockArrayShape, parseResourceFieldsAst, } from '../utils/php-parser.js';
|
|
6
|
+
import { mapDocTypeToTs } from '../utils/type-mapper.js';
|
|
7
|
+
import { printNode, createTypeAlias, createImportType, parseTypeString, createTypeLiteral, } from '../utils/ts-generator.js';
|
|
174
8
|
/**
|
|
175
9
|
* Generate TypeScript type declarations for resources.
|
|
176
10
|
*/
|
|
177
11
|
export function generateResourceTypeScript(resources, fallbacks, referencedEnums) {
|
|
178
|
-
const
|
|
179
|
-
lines.push('// This file is auto-generated by the primcloud Vite plugin.');
|
|
180
|
-
lines.push('// Do not edit directly.');
|
|
181
|
-
lines.push('');
|
|
12
|
+
const nodes = [];
|
|
182
13
|
// Import referenced enums from @app/enums
|
|
183
14
|
if (referencedEnums.size > 0) {
|
|
184
|
-
const enumImports = Array.from(referencedEnums).sort()
|
|
185
|
-
|
|
186
|
-
lines.push('');
|
|
15
|
+
const enumImports = Array.from(referencedEnums).sort();
|
|
16
|
+
nodes.push(createImportType(enumImports, '@app/enums'));
|
|
187
17
|
}
|
|
188
18
|
// Generate resource types
|
|
189
19
|
for (const className of Object.keys(resources)) {
|
|
190
20
|
const fields = resources[className];
|
|
191
21
|
if (fallbacks.includes(className)) {
|
|
192
|
-
|
|
193
|
-
|
|
22
|
+
// Fallback type: Record<string, any>
|
|
23
|
+
const recordType = ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Record'), [
|
|
24
|
+
ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
|
|
25
|
+
ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
|
|
26
|
+
]);
|
|
27
|
+
nodes.push(createTypeAlias(className, recordType));
|
|
194
28
|
continue;
|
|
195
29
|
}
|
|
196
|
-
|
|
197
|
-
|
|
30
|
+
// Create type literal with all fields
|
|
31
|
+
const properties = Object.keys(fields).map((key) => {
|
|
198
32
|
const info = fields[key];
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
33
|
+
return {
|
|
34
|
+
name: key,
|
|
35
|
+
type: parseTypeString(info.type || 'any'),
|
|
36
|
+
optional: info.optional,
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
nodes.push(createTypeAlias(className, createTypeLiteral(properties)));
|
|
205
40
|
}
|
|
206
|
-
|
|
41
|
+
if (nodes.length === 0)
|
|
42
|
+
return '';
|
|
43
|
+
return nodes.map(printNode).join('\n\n') + '\n';
|
|
207
44
|
}
|
|
208
45
|
/**
|
|
209
46
|
* Generate runtime JavaScript for resources.
|
|
210
47
|
* Resources are type-only, so this just exports an empty object.
|
|
211
48
|
*/
|
|
212
49
|
export function generateResourceRuntime() {
|
|
213
|
-
|
|
214
|
-
lines.push('// Auto-generated by primcloud Vite plugin');
|
|
215
|
-
lines.push('// Resources are type-only exports');
|
|
216
|
-
lines.push('');
|
|
217
|
-
lines.push('export default {};');
|
|
218
|
-
return lines.join('\n');
|
|
50
|
+
return 'export default {};';
|
|
219
51
|
}
|
|
220
52
|
/**
|
|
221
53
|
* Generate resource type files (TypeScript declarations and runtime JavaScript).
|
|
@@ -236,13 +68,19 @@ export function generateResources(options) {
|
|
|
236
68
|
const content = readFileSafe(filePath) || '';
|
|
237
69
|
const className = parse(file).name;
|
|
238
70
|
const docShape = extractDocblockArrayShape(content);
|
|
239
|
-
const
|
|
240
|
-
|
|
71
|
+
const mappedDocShape = docShape ? mapDocTypeToTsForShape(docShape) : null;
|
|
72
|
+
const fields = parseResourceFieldsAst(content, {
|
|
73
|
+
resourcesDir,
|
|
74
|
+
modelsDir,
|
|
75
|
+
enumsDir,
|
|
76
|
+
docShape: mappedDocShape,
|
|
77
|
+
collectedEnums,
|
|
78
|
+
});
|
|
79
|
+
if (!fields) {
|
|
241
80
|
fallbacks.push(className);
|
|
242
81
|
resources[className] = {};
|
|
243
82
|
}
|
|
244
83
|
else {
|
|
245
|
-
const fields = parseFieldsFromArrayBlock(arrayBlock, className, docShape ? mapDocTypeToTsForShape(docShape) : null, resourcesDir, modelsDir, enumsDir, collectedEnums);
|
|
246
84
|
resources[className] = fields;
|
|
247
85
|
}
|
|
248
86
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAMnC,MAAM,MAAM,0BAA0B,GAAG;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAMnC,MAAM,MAAM,0BAA0B,GAAG;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,OAAO,GAAE,0BAA+B,GAAG,MAAM,CAyF9E"}
|
package/dist/index.js
CHANGED
|
@@ -11,18 +11,19 @@ import { setupResourceWatcher } from './watchers/resources.js';
|
|
|
11
11
|
* - @app/resources - Laravel JsonResource types
|
|
12
12
|
* - @app/schemas - (future) Zod schemas from FormRequests
|
|
13
13
|
*/
|
|
14
|
-
export default function ferry(options = {
|
|
15
|
-
cwd: process.cwd(),
|
|
16
|
-
}) {
|
|
14
|
+
export default function ferry(options = {}) {
|
|
17
15
|
const namespace = '@ferry';
|
|
18
16
|
const name = 'vite-plugin-ferry';
|
|
17
|
+
// Apply defaults
|
|
18
|
+
const cwd = options.cwd ?? process.cwd();
|
|
19
|
+
const prettyPrint = options.prettyPrint ?? true;
|
|
19
20
|
// Directory paths
|
|
20
|
-
const enumsDir = join(
|
|
21
|
-
const resourcesDir = join(
|
|
22
|
-
const modelsDir = join(
|
|
21
|
+
const enumsDir = join(cwd, 'app/Enums');
|
|
22
|
+
const resourcesDir = join(cwd, 'app/Http/Resources');
|
|
23
|
+
const modelsDir = join(cwd, 'app/Models');
|
|
23
24
|
// Output directories for each package
|
|
24
|
-
const enumsOutputDir = join(
|
|
25
|
-
const resourcesOutputDir = join(
|
|
25
|
+
const enumsOutputDir = join(cwd, 'node_modules', ...namespace.split('/'), 'enums');
|
|
26
|
+
const resourcesOutputDir = join(cwd, 'node_modules', ...namespace.split('/'), 'resources');
|
|
26
27
|
/**
|
|
27
28
|
* Generate all packages.
|
|
28
29
|
*/
|
|
@@ -32,6 +33,7 @@ export default function ferry(options = {
|
|
|
32
33
|
enumsDir,
|
|
33
34
|
outputDir: enumsOutputDir,
|
|
34
35
|
packageName: `${namespace}/enums`,
|
|
36
|
+
prettyPrint,
|
|
35
37
|
});
|
|
36
38
|
// Generate @app/resources package
|
|
37
39
|
generateResources({
|
|
@@ -40,6 +42,7 @@ export default function ferry(options = {
|
|
|
40
42
|
modelsDir,
|
|
41
43
|
outputDir: resourcesOutputDir,
|
|
42
44
|
packageName: `${namespace}/resources`,
|
|
45
|
+
prettyPrint,
|
|
43
46
|
});
|
|
44
47
|
}
|
|
45
48
|
return {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type EnumCase = {
|
|
2
2
|
key: string;
|
|
3
|
-
value: string;
|
|
3
|
+
value: string | number;
|
|
4
4
|
label?: string;
|
|
5
5
|
};
|
|
6
6
|
export type EnumDefinition = {
|
|
@@ -9,23 +9,40 @@ export type EnumDefinition = {
|
|
|
9
9
|
cases: EnumCase[];
|
|
10
10
|
};
|
|
11
11
|
/**
|
|
12
|
-
* Parse
|
|
12
|
+
* Parse PHP enum content and extract its definition.
|
|
13
|
+
* This is a pure function that takes PHP source code as input.
|
|
13
14
|
*/
|
|
14
|
-
export declare function
|
|
15
|
+
export declare function parseEnumContent(phpContent: string): EnumDefinition | null;
|
|
15
16
|
/**
|
|
16
|
-
* Parse
|
|
17
|
+
* Parse model casts from PHP model content.
|
|
18
|
+
* This is a pure function that takes PHP source code as input.
|
|
17
19
|
*/
|
|
18
|
-
export declare function
|
|
20
|
+
export declare function parseModelCasts(phpContent: string): Record<string, string>;
|
|
19
21
|
/**
|
|
20
|
-
* Extract
|
|
21
|
-
|
|
22
|
-
export declare function getModelCasts(modelPath: string): Record<string, string>;
|
|
23
|
-
/**
|
|
24
|
-
* Extract docblock array shape from PHP file content.
|
|
22
|
+
* Extract docblock array shape from PHP content.
|
|
23
|
+
* This is a pure function that takes PHP source code as input.
|
|
25
24
|
*/
|
|
26
25
|
export declare function extractDocblockArrayShape(phpContent: string): Record<string, string> | null;
|
|
26
|
+
export type ResourceFieldInfo = {
|
|
27
|
+
type: string;
|
|
28
|
+
optional: boolean;
|
|
29
|
+
};
|
|
30
|
+
export type ResourceArrayEntry = {
|
|
31
|
+
key: string;
|
|
32
|
+
fieldInfo: ResourceFieldInfo;
|
|
33
|
+
nested?: Record<string, ResourceArrayEntry>;
|
|
34
|
+
};
|
|
35
|
+
export type ParseResourceOptions = {
|
|
36
|
+
resourcesDir?: string;
|
|
37
|
+
modelsDir?: string;
|
|
38
|
+
enumsDir?: string;
|
|
39
|
+
docShape?: Record<string, string> | null;
|
|
40
|
+
collectedEnums?: Record<string, EnumDefinition>;
|
|
41
|
+
resourceClass?: string;
|
|
42
|
+
};
|
|
27
43
|
/**
|
|
28
|
-
*
|
|
44
|
+
* Parse resource fields from PHP content using AST.
|
|
45
|
+
* Returns null if parsing fails or no toArray method is found.
|
|
29
46
|
*/
|
|
30
|
-
export declare function
|
|
47
|
+
export declare function parseResourceFieldsAst(phpContent: string, options?: Omit<ParseResourceOptions, 'resourceClass'>): Record<string, ResourceFieldInfo> | null;
|
|
31
48
|
//# sourceMappingURL=php-parser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"php-parser.d.ts","sourceRoot":"","sources":["../../src/utils/php-parser.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"php-parser.d.ts","sourceRoot":"","sources":["../../src/utils/php-parser.ts"],"names":[],"mappings":"AAqBA,MAAM,MAAM,QAAQ,GAAG;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,CAAC;AA6FF;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAsF1E;AA4CD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAwC1E;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAwE3F;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,iBAAiB,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACzC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAChD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AA8RF;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,IAAI,CAAC,oBAAoB,EAAE,eAAe,CAAM,GACxD,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,IAAI,CAqC1C"}
|