@adminforge/core 0.3.1
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 +56 -0
- package/CHANGELOG.md +32 -0
- package/LICENSE +21 -0
- package/bin/adminforge.js +317 -0
- package/dist/auth-client.cjs +45 -0
- package/dist/auth-client.cjs.map +1 -0
- package/dist/auth-client.d.cts +17 -0
- package/dist/auth-client.d.ts +17 -0
- package/dist/auth-client.js +20 -0
- package/dist/auth-client.js.map +1 -0
- package/dist/auth.cjs +65 -0
- package/dist/auth.cjs.map +1 -0
- package/dist/auth.d.cts +21 -0
- package/dist/auth.d.ts +21 -0
- package/dist/auth.js +36 -0
- package/dist/auth.js.map +1 -0
- package/dist/client-D0cjJVsn.d.ts +20 -0
- package/dist/client-sRnmZ-Y9.d.cts +20 -0
- package/dist/index-CyzxaE7n.d.cts +124 -0
- package/dist/index-CyzxaE7n.d.ts +124 -0
- package/dist/index.cjs +453 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +65 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.js +410 -0
- package/dist/index.js.map +1 -0
- package/dist/next.cjs +839 -0
- package/dist/next.cjs.map +1 -0
- package/dist/next.d.cts +84 -0
- package/dist/next.d.ts +84 -0
- package/dist/next.js +800 -0
- package/dist/next.js.map +1 -0
- package/dist/styles.css +763 -0
- package/dist/styles.css.map +1 -0
- package/dist/styles.d.cts +2 -0
- package/dist/styles.d.ts +2 -0
- package/dist/ui.cjs +2500 -0
- package/dist/ui.cjs.map +1 -0
- package/dist/ui.d.cts +119 -0
- package/dist/ui.d.ts +119 -0
- package/dist/ui.js +2448 -0
- package/dist/ui.js.map +1 -0
- package/eslint.config.js +35 -0
- package/package.json +99 -0
- package/src/api/controller.ts +234 -0
- package/src/api/index.ts +4 -0
- package/src/api/next.ts +281 -0
- package/src/api/security/agent-auth.ts +134 -0
- package/src/auth/config.ts +20 -0
- package/src/auth/index.ts +3 -0
- package/src/auth/middleware.ts +15 -0
- package/src/auth/provider.tsx +28 -0
- package/src/core/fields/index.ts +119 -0
- package/src/core/hooks/index.ts +60 -0
- package/src/core/index.ts +43 -0
- package/src/core/registry/index.ts +22 -0
- package/src/core/schema/collection.ts +12 -0
- package/src/core/schema/config.ts +11 -0
- package/src/core/schema/normalize.ts +32 -0
- package/src/core/types/index.ts +114 -0
- package/src/db/client.ts +146 -0
- package/src/db/index.ts +3 -0
- package/src/db/schema-generator.ts +104 -0
- package/src/fields/index.ts +1 -0
- package/src/index.ts +4 -0
- package/src/next.ts +3 -0
- package/src/styles/adminforge.css +840 -0
- package/src/ui/AdminDashboard.tsx +176 -0
- package/src/ui/AdminForgeContext.tsx +64 -0
- package/src/ui/components/AdminLayout.tsx +107 -0
- package/src/ui/form-engine/FormEngine.tsx +250 -0
- package/src/ui/form-engine/ImageUpload.tsx +68 -0
- package/src/ui/form-engine/RelationInput.tsx +215 -0
- package/src/ui/form-engine/RichTextEditor.tsx +708 -0
- package/src/ui/index.ts +18 -0
- package/src/ui/screens/AdminPage.tsx +162 -0
- package/src/ui/screens/AgentTokenPage.tsx +232 -0
- package/src/ui/screens/CollectionFormPage.tsx +135 -0
- package/src/ui/screens/CollectionListPage.tsx +170 -0
- package/src/ui/screens/CollectionSchemaPage.tsx +180 -0
- package/src/ui/screens/RoleDetailPage.tsx +147 -0
- package/src/ui/screens/RolesListPage.tsx +57 -0
- package/src/ui/table-engine/TableEngine.tsx +157 -0
- package/src/ui.ts +3 -0
- package/tsconfig.json +10 -0
- package/tsup.config.ts +54 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
// src/core/schema/config.ts
|
|
2
|
+
function defineConfig(config) {
|
|
3
|
+
return {
|
|
4
|
+
collections: config.collections,
|
|
5
|
+
auth: config.auth ?? { enabled: false }
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// src/core/schema/collection.ts
|
|
10
|
+
function collection(input) {
|
|
11
|
+
return {
|
|
12
|
+
name: input.name,
|
|
13
|
+
label: input.label ?? input.name.charAt(0).toUpperCase() + input.name.slice(1),
|
|
14
|
+
icon: input.icon,
|
|
15
|
+
fields: input.fields,
|
|
16
|
+
hooks: input.hooks,
|
|
17
|
+
access: input.access
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// src/core/schema/normalize.ts
|
|
22
|
+
function normalize(config) {
|
|
23
|
+
return {
|
|
24
|
+
collections: config.collections.map((c) => ({
|
|
25
|
+
...c,
|
|
26
|
+
label: c.label ?? c.name.charAt(0).toUpperCase() + c.name.slice(1)
|
|
27
|
+
})),
|
|
28
|
+
auth: config.auth ?? { enabled: false }
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function serializeConfig(config) {
|
|
32
|
+
return {
|
|
33
|
+
...config,
|
|
34
|
+
collections: config.collections.map((c) => ({
|
|
35
|
+
...c,
|
|
36
|
+
fields: Object.fromEntries(
|
|
37
|
+
Object.entries(c.fields).map(([name, field]) => {
|
|
38
|
+
const { validation, ...rest } = field;
|
|
39
|
+
return [name, rest];
|
|
40
|
+
})
|
|
41
|
+
)
|
|
42
|
+
}))
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/core/fields/index.ts
|
|
47
|
+
import { z } from "zod";
|
|
48
|
+
function fieldMeta(options = {}) {
|
|
49
|
+
return {
|
|
50
|
+
required: Boolean(options.required),
|
|
51
|
+
unique: Boolean(options.unique),
|
|
52
|
+
default: options.default,
|
|
53
|
+
label: options.label,
|
|
54
|
+
hidden: options.hidden,
|
|
55
|
+
readOnly: options.readOnly,
|
|
56
|
+
description: options.description
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function text(options = {}) {
|
|
60
|
+
return {
|
|
61
|
+
type: "text",
|
|
62
|
+
db: { type: "String", nullable: !options.required, unique: options.unique, default: options.default },
|
|
63
|
+
access: options.access,
|
|
64
|
+
ui: { component: "text", props: { label: options.label, hidden: options.hidden, readOnly: options.readOnly } },
|
|
65
|
+
validation: options.required ? z.string().min(1) : z.string().optional(),
|
|
66
|
+
meta: fieldMeta(options)
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function boolean(options = {}) {
|
|
70
|
+
return {
|
|
71
|
+
type: "boolean",
|
|
72
|
+
db: { type: "Boolean", nullable: !options.required, default: options.default ?? false },
|
|
73
|
+
access: options.access,
|
|
74
|
+
ui: { component: "boolean", props: { label: options.label, hidden: options.hidden, readOnly: options.readOnly } },
|
|
75
|
+
validation: options.required ? z.boolean() : z.boolean().optional(),
|
|
76
|
+
meta: fieldMeta(options)
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function richText(options = {}) {
|
|
80
|
+
return {
|
|
81
|
+
type: "richText",
|
|
82
|
+
db: { type: "String", nullable: !options.required },
|
|
83
|
+
access: options.access,
|
|
84
|
+
ui: { component: "richText", props: { label: options.label, hidden: options.hidden, readOnly: options.readOnly } },
|
|
85
|
+
validation: options.required ? z.string().min(1) : z.string().optional(),
|
|
86
|
+
meta: fieldMeta(options)
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function slug(options) {
|
|
90
|
+
return {
|
|
91
|
+
type: "slug",
|
|
92
|
+
db: { type: "String", nullable: !options.required, unique: options.unique ?? true },
|
|
93
|
+
access: options.access,
|
|
94
|
+
ui: { component: "slug", props: { from: options.from, label: options.label, hidden: options.hidden, readOnly: options.readOnly } },
|
|
95
|
+
validation: options.required ? z.string().regex(/^[a-z0-9-]+$/) : z.string().regex(/^[a-z0-9-]+$/).optional(),
|
|
96
|
+
hooks: { beforeSave: (value) => {
|
|
97
|
+
if (typeof value === "string") return value.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
|
|
98
|
+
return value;
|
|
99
|
+
} },
|
|
100
|
+
meta: { ...fieldMeta(options), unique: options.unique ?? true }
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function relation(options) {
|
|
104
|
+
const isMulti = options.type === "many-to-many" || options.type === "one-to-many";
|
|
105
|
+
return {
|
|
106
|
+
type: "relation",
|
|
107
|
+
db: { type: "String", nullable: !options.required, references: { model: options.to, field: "id" }, relationType: options.type },
|
|
108
|
+
access: options.access,
|
|
109
|
+
ui: { component: "relation", props: { to: options.to, relationType: options.type, label: options.label, hidden: options.hidden, readOnly: options.readOnly } },
|
|
110
|
+
validation: isMulti ? options.required ? z.array(z.string()).min(1) : z.array(z.string()).optional() : options.required ? z.string().min(1) : z.string().optional(),
|
|
111
|
+
meta: fieldMeta(options)
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function date(options = {}) {
|
|
115
|
+
return {
|
|
116
|
+
type: "date",
|
|
117
|
+
db: { type: "DateTime", nullable: !options.required },
|
|
118
|
+
access: options.access,
|
|
119
|
+
ui: { component: "date", props: { label: options.label, hidden: options.hidden, readOnly: options.readOnly } },
|
|
120
|
+
validation: options.required ? z.string().datetime() : z.string().datetime().optional(),
|
|
121
|
+
hooks: { beforeSave: (value) => {
|
|
122
|
+
if (options.autoCreate && !value) return (/* @__PURE__ */ new Date()).toISOString();
|
|
123
|
+
return value;
|
|
124
|
+
} },
|
|
125
|
+
meta: fieldMeta(options)
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function image(options = {}) {
|
|
129
|
+
return {
|
|
130
|
+
type: "image",
|
|
131
|
+
db: { type: "String", nullable: true },
|
|
132
|
+
access: options.access,
|
|
133
|
+
ui: { component: "image", props: { label: options.label, hidden: options.hidden, readOnly: options.readOnly } },
|
|
134
|
+
validation: z.string().optional(),
|
|
135
|
+
meta: fieldMeta(options)
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
var fields = {
|
|
139
|
+
text,
|
|
140
|
+
boolean,
|
|
141
|
+
richText,
|
|
142
|
+
slug,
|
|
143
|
+
relation,
|
|
144
|
+
date,
|
|
145
|
+
image
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// src/core/registry/index.ts
|
|
149
|
+
var fieldRegistry = /* @__PURE__ */ new Map();
|
|
150
|
+
function registerField(name, definition) {
|
|
151
|
+
if (fieldRegistry.has(name)) {
|
|
152
|
+
throw new Error(`Field "${name}" is already registered`);
|
|
153
|
+
}
|
|
154
|
+
fieldRegistry.set(name, definition);
|
|
155
|
+
}
|
|
156
|
+
function getField(name) {
|
|
157
|
+
return fieldRegistry.get(name);
|
|
158
|
+
}
|
|
159
|
+
function getRegisteredFields() {
|
|
160
|
+
return new Map(fieldRegistry);
|
|
161
|
+
}
|
|
162
|
+
function clearRegistry() {
|
|
163
|
+
fieldRegistry.clear();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// src/core/hooks/index.ts
|
|
167
|
+
async function executeBeforeCreate(hooks, data) {
|
|
168
|
+
if (hooks?.beforeCreate) {
|
|
169
|
+
return await hooks.beforeCreate({ data });
|
|
170
|
+
}
|
|
171
|
+
return data;
|
|
172
|
+
}
|
|
173
|
+
async function executeAfterCreate(hooks, data, id) {
|
|
174
|
+
if (hooks?.afterCreate) {
|
|
175
|
+
await hooks.afterCreate({ data, id });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async function executeBeforeUpdate(hooks, data, id) {
|
|
179
|
+
if (hooks?.beforeUpdate) {
|
|
180
|
+
return await hooks.beforeUpdate({ data, id });
|
|
181
|
+
}
|
|
182
|
+
return data;
|
|
183
|
+
}
|
|
184
|
+
async function executeAfterUpdate(hooks, data, id) {
|
|
185
|
+
if (hooks?.afterUpdate) {
|
|
186
|
+
await hooks.afterUpdate({ data, id });
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
async function executeBeforeDelete(hooks, id) {
|
|
190
|
+
if (hooks?.beforeDelete) {
|
|
191
|
+
await hooks.beforeDelete({ id });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
async function executeAfterDelete(hooks, id) {
|
|
195
|
+
if (hooks?.afterDelete) {
|
|
196
|
+
await hooks.afterDelete({ id });
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/db/schema-generator.ts
|
|
201
|
+
function mapFieldType(dbType) {
|
|
202
|
+
const typeMap = {
|
|
203
|
+
String: "String",
|
|
204
|
+
Boolean: "Boolean",
|
|
205
|
+
DateTime: "DateTime",
|
|
206
|
+
Int: "Int",
|
|
207
|
+
Float: "Float",
|
|
208
|
+
Json: "Json"
|
|
209
|
+
};
|
|
210
|
+
return typeMap[dbType] ?? "String";
|
|
211
|
+
}
|
|
212
|
+
function generatePrismaSchema(config, options) {
|
|
213
|
+
const provider = options?.provider ?? "sqlite";
|
|
214
|
+
const lines = [];
|
|
215
|
+
lines.push("generator client {");
|
|
216
|
+
lines.push(' provider = "prisma-client-js"');
|
|
217
|
+
lines.push("}");
|
|
218
|
+
lines.push("");
|
|
219
|
+
lines.push("datasource db {");
|
|
220
|
+
lines.push(` provider = "${provider}"`);
|
|
221
|
+
lines.push(' url = env("DATABASE_URL")');
|
|
222
|
+
lines.push("}");
|
|
223
|
+
lines.push("");
|
|
224
|
+
const inverseRelations = {};
|
|
225
|
+
const modelBlocks = {};
|
|
226
|
+
for (const collection2 of config.collections) {
|
|
227
|
+
const modelName = collection2.name;
|
|
228
|
+
const block = [];
|
|
229
|
+
block.push(`model ${modelName} {`);
|
|
230
|
+
block.push(" id String @id @default(cuid())");
|
|
231
|
+
block.push(" createdAt DateTime @default(now())");
|
|
232
|
+
block.push(" updatedAt DateTime @updatedAt");
|
|
233
|
+
for (const [name, field] of Object.entries(collection2.fields)) {
|
|
234
|
+
if (field.type === "relation") {
|
|
235
|
+
const targetModel = field.db.references?.model || field.ui.props?.to || "String";
|
|
236
|
+
const relType = field.db.relationType || "many-to-one";
|
|
237
|
+
if (!inverseRelations[targetModel]) inverseRelations[targetModel] = [];
|
|
238
|
+
if (relType === "many-to-many") {
|
|
239
|
+
block.push(` ${name} ${targetModel}[]`);
|
|
240
|
+
inverseRelations[targetModel].push(` adminforge_inverse_${modelName}_${name} ${modelName}[]`);
|
|
241
|
+
} else if (relType === "one-to-many") {
|
|
242
|
+
block.push(` ${name} ${targetModel}[]`);
|
|
243
|
+
inverseRelations[targetModel].push(` adminforge_inverse_${modelName}_${name}Id String?`);
|
|
244
|
+
inverseRelations[targetModel].push(` adminforge_inverse_${modelName}_${name} ${modelName}? @relation(fields: [adminforge_inverse_${modelName}_${name}Id], references: [id])`);
|
|
245
|
+
} else {
|
|
246
|
+
block.push(` ${name}Id String${field.db.nullable ? "?" : ""}`);
|
|
247
|
+
block.push(` ${name} ${targetModel}${field.db.nullable ? "?" : ""} @relation(fields: [${name}Id], references: [id])`);
|
|
248
|
+
inverseRelations[targetModel].push(` adminforge_inverse_${modelName}_${name} ${modelName}[]`);
|
|
249
|
+
}
|
|
250
|
+
} else {
|
|
251
|
+
const prismaType = mapFieldType(field.db.type);
|
|
252
|
+
const nul = field.db.nullable ? "?" : "";
|
|
253
|
+
const annotations = [];
|
|
254
|
+
if (field.db.unique) annotations.push("@unique");
|
|
255
|
+
if (field.db.default !== void 0) {
|
|
256
|
+
const val = field.db.default;
|
|
257
|
+
if (typeof val === "string") {
|
|
258
|
+
annotations.push(`@default("${val}")`);
|
|
259
|
+
} else if (typeof val === "boolean") {
|
|
260
|
+
annotations.push(`@default(${String(val)})`);
|
|
261
|
+
} else {
|
|
262
|
+
annotations.push(`@default(${String(val)})`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const ann = annotations.length > 0 ? ` ${annotations.join(" ")}` : "";
|
|
266
|
+
block.push(` ${name} ${prismaType}${nul}${ann}`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
modelBlocks[modelName] = block;
|
|
270
|
+
}
|
|
271
|
+
for (const collection2 of config.collections) {
|
|
272
|
+
const modelName = collection2.name;
|
|
273
|
+
const block = modelBlocks[modelName];
|
|
274
|
+
if (inverseRelations[modelName]) {
|
|
275
|
+
block.push("");
|
|
276
|
+
block.push(" // Auto-generated inverse relations");
|
|
277
|
+
for (const inv of inverseRelations[modelName]) {
|
|
278
|
+
block.push(inv);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
block.push("}");
|
|
282
|
+
lines.push(block.join("\n"));
|
|
283
|
+
lines.push("");
|
|
284
|
+
}
|
|
285
|
+
return lines.join("\n");
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// src/db/client.ts
|
|
289
|
+
import { PrismaClient } from "@prisma/client";
|
|
290
|
+
function createDbClient(config, existingPrisma) {
|
|
291
|
+
const prisma = existingPrisma || new PrismaClient();
|
|
292
|
+
const collectionMap = /* @__PURE__ */ new Map();
|
|
293
|
+
const hookMap = /* @__PURE__ */ new Map();
|
|
294
|
+
for (const collection2 of config.collections) {
|
|
295
|
+
collectionMap.set(collection2.name, collection2);
|
|
296
|
+
hookMap.set(collection2.name, collection2.hooks);
|
|
297
|
+
}
|
|
298
|
+
const getPrismaModel = (collection2) => {
|
|
299
|
+
const model = prisma[collection2];
|
|
300
|
+
if (!model || typeof model !== "object") {
|
|
301
|
+
throw new Error(`Collection "${collection2}" not found in Prisma schema`);
|
|
302
|
+
}
|
|
303
|
+
return model;
|
|
304
|
+
};
|
|
305
|
+
const getIncludeMap = (collectionName) => {
|
|
306
|
+
const collDef = collectionMap.get(collectionName);
|
|
307
|
+
if (!collDef) return void 0;
|
|
308
|
+
const include = {};
|
|
309
|
+
let hasRelations = false;
|
|
310
|
+
for (const [fieldName, field] of Object.entries(collDef.fields)) {
|
|
311
|
+
if (field.type === "relation") {
|
|
312
|
+
include[fieldName] = true;
|
|
313
|
+
hasRelations = true;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return hasRelations ? include : void 0;
|
|
317
|
+
};
|
|
318
|
+
const transformRelations = (collectionName, data, isUpdate) => {
|
|
319
|
+
const collDef = collectionMap.get(collectionName);
|
|
320
|
+
if (!collDef) return data;
|
|
321
|
+
const transformed = { ...data };
|
|
322
|
+
for (const [key, value] of Object.entries(transformed)) {
|
|
323
|
+
const field = collDef.fields[key];
|
|
324
|
+
if (field?.type === "relation") {
|
|
325
|
+
const relationType = field.db.relationType ?? "many-to-one";
|
|
326
|
+
const isMulti = relationType === "many-to-many" || relationType === "one-to-many";
|
|
327
|
+
if (Array.isArray(value)) {
|
|
328
|
+
const ids = value.filter((id) => typeof id === "string" && id.length > 0);
|
|
329
|
+
if (isUpdate) {
|
|
330
|
+
transformed[key] = { set: ids.map((id) => ({ id })) };
|
|
331
|
+
} else {
|
|
332
|
+
transformed[key] = { connect: ids.map((id) => ({ id })) };
|
|
333
|
+
}
|
|
334
|
+
} else if (!isMulti && typeof value === "string" && value !== "") {
|
|
335
|
+
transformed[key] = { connect: { id: value } };
|
|
336
|
+
} else if ((value === null || value === "") && isUpdate) {
|
|
337
|
+
transformed[key] = { disconnect: true };
|
|
338
|
+
} else if (value === null || value === "") {
|
|
339
|
+
delete transformed[key];
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return transformed;
|
|
344
|
+
};
|
|
345
|
+
return {
|
|
346
|
+
async create(collection2, data) {
|
|
347
|
+
const hooks = hookMap.get(collection2);
|
|
348
|
+
const processedData = await executeBeforeCreate(hooks, data);
|
|
349
|
+
const prismaData = transformRelations(collection2, processedData, false);
|
|
350
|
+
const model = getPrismaModel(collection2);
|
|
351
|
+
const include = getIncludeMap(collection2);
|
|
352
|
+
const result = await model.create({ data: prismaData, ...include ? { include } : {} });
|
|
353
|
+
const resultId = result.id;
|
|
354
|
+
await executeAfterCreate(hooks, processedData, resultId);
|
|
355
|
+
return result;
|
|
356
|
+
},
|
|
357
|
+
async findMany(collection2, args = {}) {
|
|
358
|
+
const model = getPrismaModel(collection2);
|
|
359
|
+
const include = getIncludeMap(collection2);
|
|
360
|
+
return model.findMany({ ...args, ...include ? { include } : {} });
|
|
361
|
+
},
|
|
362
|
+
async findUnique(collection2, id) {
|
|
363
|
+
const model = getPrismaModel(collection2);
|
|
364
|
+
const include = getIncludeMap(collection2);
|
|
365
|
+
return model.findUnique({ where: { id }, ...include ? { include } : {} });
|
|
366
|
+
},
|
|
367
|
+
async update(collection2, id, data) {
|
|
368
|
+
const hooks = hookMap.get(collection2);
|
|
369
|
+
const processedData = await executeBeforeUpdate(hooks, data, id);
|
|
370
|
+
const prismaData = transformRelations(collection2, processedData, true);
|
|
371
|
+
const model = getPrismaModel(collection2);
|
|
372
|
+
const include = getIncludeMap(collection2);
|
|
373
|
+
const result = await model.update({ where: { id }, data: prismaData, ...include ? { include } : {} });
|
|
374
|
+
await executeAfterUpdate(hooks, processedData, id);
|
|
375
|
+
return result;
|
|
376
|
+
},
|
|
377
|
+
async delete(collection2, id) {
|
|
378
|
+
const hooks = hookMap.get(collection2);
|
|
379
|
+
await executeBeforeDelete(hooks, id);
|
|
380
|
+
const model = getPrismaModel(collection2);
|
|
381
|
+
const result = await model.delete({ where: { id } });
|
|
382
|
+
await executeAfterDelete(hooks, id);
|
|
383
|
+
return result;
|
|
384
|
+
},
|
|
385
|
+
async count(collection2, args = {}) {
|
|
386
|
+
const model = getPrismaModel(collection2);
|
|
387
|
+
return model.count(args);
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
export {
|
|
392
|
+
clearRegistry,
|
|
393
|
+
collection,
|
|
394
|
+
createDbClient,
|
|
395
|
+
defineConfig,
|
|
396
|
+
executeAfterCreate,
|
|
397
|
+
executeAfterDelete,
|
|
398
|
+
executeAfterUpdate,
|
|
399
|
+
executeBeforeCreate,
|
|
400
|
+
executeBeforeDelete,
|
|
401
|
+
executeBeforeUpdate,
|
|
402
|
+
fields,
|
|
403
|
+
generatePrismaSchema,
|
|
404
|
+
getField,
|
|
405
|
+
getRegisteredFields,
|
|
406
|
+
normalize,
|
|
407
|
+
registerField,
|
|
408
|
+
serializeConfig
|
|
409
|
+
};
|
|
410
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/schema/config.ts","../src/core/schema/collection.ts","../src/core/schema/normalize.ts","../src/core/fields/index.ts","../src/core/registry/index.ts","../src/core/hooks/index.ts","../src/db/schema-generator.ts","../src/db/client.ts"],"sourcesContent":["import type { AdminForgeConfig, CollectionDefinition, AuthConfig } from \"../types/index.js\";\n\nexport function defineConfig(config: {\n collections: CollectionDefinition[];\n auth?: AuthConfig;\n}): AdminForgeConfig {\n return {\n collections: config.collections,\n auth: config.auth ?? { enabled: false },\n };\n}\n","import type { CollectionDefinition, CollectionInput } from \"../types/index.js\";\n\nexport function collection(input: CollectionInput): CollectionDefinition {\n return {\n name: input.name,\n label: input.label ?? input.name.charAt(0).toUpperCase() + input.name.slice(1),\n icon: input.icon,\n fields: input.fields,\n hooks: input.hooks,\n access: input.access,\n };\n}\n","import type { AdminForgeConfig } from \"../types/index.js\";\n\nexport type NormalizedSchema = AdminForgeConfig;\n\nexport function normalize(config: AdminForgeConfig): NormalizedSchema {\n return {\n collections: config.collections.map((c) => ({\n ...c,\n label: c.label ?? c.name.charAt(0).toUpperCase() + c.name.slice(1),\n })),\n auth: config.auth ?? { enabled: false },\n };\n}\n\n/**\n * Removes non-serializable parts of the config (like Zod schemas)\n * so it can be passed from Server Components to Client Components.\n */\nexport function serializeConfig(config: AdminForgeConfig): any {\n return {\n ...config,\n collections: config.collections.map((c) => ({\n ...c,\n fields: Object.fromEntries(\n Object.entries(c.fields).map(([name, field]) => {\n const { validation, ...rest } = field;\n return [name, rest];\n })\n ),\n })),\n };\n}\n","import { z } from \"zod\";\nimport type {\n FieldDefinition,\n FieldMeta,\n FieldOptions,\n TextOptions,\n BooleanOptions,\n RichTextOptions,\n SlugOptions,\n RelationOptions,\n DateOptions,\n ImageOptions,\n} from \"../types/index.js\";\n\nfunction fieldMeta(options: FieldOptions = {}): FieldMeta {\n return {\n required: Boolean(options.required),\n unique: Boolean(options.unique),\n default: options.default,\n label: options.label,\n hidden: options.hidden,\n readOnly: options.readOnly,\n description: options.description,\n };\n}\n\nfunction text(options: TextOptions = {}): FieldDefinition {\n return {\n type: \"text\",\n db: { type: \"String\", nullable: !options.required, unique: options.unique, default: options.default },\n access: options.access,\n ui: { component: \"text\", props: { label: options.label, hidden: options.hidden, readOnly: options.readOnly } },\n validation: options.required ? z.string().min(1) : z.string().optional(),\n meta: fieldMeta(options),\n };\n}\n\nfunction boolean(options: BooleanOptions = {}): FieldDefinition {\n return {\n type: \"boolean\",\n db: { type: \"Boolean\", nullable: !options.required, default: options.default ?? false },\n access: options.access,\n ui: { component: \"boolean\", props: { label: options.label, hidden: options.hidden, readOnly: options.readOnly } },\n validation: options.required ? z.boolean() : z.boolean().optional(),\n meta: fieldMeta(options),\n };\n}\n\nfunction richText(options: RichTextOptions = {}): FieldDefinition {\n return {\n type: \"richText\",\n db: { type: \"String\", nullable: !options.required },\n access: options.access,\n ui: { component: \"richText\", props: { label: options.label, hidden: options.hidden, readOnly: options.readOnly } },\n validation: options.required ? z.string().min(1) : z.string().optional(),\n meta: fieldMeta(options),\n };\n}\n\nfunction slug(options: SlugOptions): FieldDefinition {\n return {\n type: \"slug\",\n db: { type: \"String\", nullable: !options.required, unique: options.unique ?? true },\n access: options.access,\n ui: { component: \"slug\", props: { from: options.from, label: options.label, hidden: options.hidden, readOnly: options.readOnly } },\n validation: options.required\n ? z.string().regex(/^[a-z0-9-]+$/)\n : z.string().regex(/^[a-z0-9-]+$/).optional(),\n hooks: { beforeSave: (value: unknown) => {\n if (typeof value === \"string\") return value.toLowerCase().replace(/\\s+/g, \"-\").replace(/[^a-z0-9-]/g, \"\");\n return value;\n }},\n meta: { ...fieldMeta(options), unique: options.unique ?? true },\n };\n}\n\nfunction relation(options: RelationOptions): FieldDefinition {\n const isMulti = options.type === \"many-to-many\" || options.type === \"one-to-many\";\n return {\n type: \"relation\",\n db: { type: \"String\", nullable: !options.required, references: { model: options.to, field: \"id\" }, relationType: options.type },\n access: options.access,\n ui: { component: \"relation\", props: { to: options.to, relationType: options.type, label: options.label, hidden: options.hidden, readOnly: options.readOnly } },\n validation: isMulti\n ? (options.required ? z.array(z.string()).min(1) : z.array(z.string()).optional())\n : (options.required ? z.string().min(1) : z.string().optional()),\n meta: fieldMeta(options),\n };\n}\n\nfunction date(options: DateOptions = {}): FieldDefinition {\n return {\n type: \"date\",\n db: { type: \"DateTime\", nullable: !options.required },\n access: options.access,\n ui: { component: \"date\", props: { label: options.label, hidden: options.hidden, readOnly: options.readOnly } },\n validation: options.required ? z.string().datetime() : z.string().datetime().optional(),\n hooks: { beforeSave: (value: unknown) => {\n if (options.autoCreate && !value) return new Date().toISOString();\n return value;\n }},\n meta: fieldMeta(options),\n };\n}\n\nfunction image(options: ImageOptions = {}): FieldDefinition {\n return {\n type: \"image\",\n db: { type: \"String\", nullable: true },\n access: options.access,\n ui: { component: \"image\", props: { label: options.label, hidden: options.hidden, readOnly: options.readOnly } },\n validation: z.string().optional(),\n meta: fieldMeta(options),\n };\n}\n\nexport const fields = {\n text, boolean, richText, slug, relation, date, image,\n} as const;\n","import type { FieldDefinition } from \"../types/index.js\";\n\nconst fieldRegistry = new Map<string, FieldDefinition>();\n\nexport function registerField(name: string, definition: FieldDefinition): void {\n if (fieldRegistry.has(name)) {\n throw new Error(`Field \"${name}\" is already registered`);\n }\n fieldRegistry.set(name, definition);\n}\n\nexport function getField(name: string): FieldDefinition | undefined {\n return fieldRegistry.get(name);\n}\n\nexport function getRegisteredFields(): Map<string, FieldDefinition> {\n return new Map(fieldRegistry);\n}\n\nexport function clearRegistry(): void {\n fieldRegistry.clear();\n}\n","import type { CollectionHooks } from \"../types/index.js\";\n\nexport async function executeBeforeCreate(\n hooks: CollectionHooks | undefined,\n data: Record<string, unknown>\n): Promise<Record<string, unknown>> {\n if (hooks?.beforeCreate) {\n return await hooks.beforeCreate({ data });\n }\n return data;\n}\n\nexport async function executeAfterCreate(\n hooks: CollectionHooks | undefined,\n data: Record<string, unknown>,\n id: string\n): Promise<void> {\n if (hooks?.afterCreate) {\n await hooks.afterCreate({ data, id });\n }\n}\n\nexport async function executeBeforeUpdate(\n hooks: CollectionHooks | undefined,\n data: Record<string, unknown>,\n id: string\n): Promise<Record<string, unknown>> {\n if (hooks?.beforeUpdate) {\n return await hooks.beforeUpdate({ data, id });\n }\n return data;\n}\n\nexport async function executeAfterUpdate(\n hooks: CollectionHooks | undefined,\n data: Record<string, unknown>,\n id: string\n): Promise<void> {\n if (hooks?.afterUpdate) {\n await hooks.afterUpdate({ data, id });\n }\n}\n\nexport async function executeBeforeDelete(\n hooks: CollectionHooks | undefined,\n id: string\n): Promise<void> {\n if (hooks?.beforeDelete) {\n await hooks.beforeDelete({ id });\n }\n}\n\nexport async function executeAfterDelete(\n hooks: CollectionHooks | undefined,\n id: string\n): Promise<void> {\n if (hooks?.afterDelete) {\n await hooks.afterDelete({ id });\n }\n}\n","import type { AdminForgeConfig } from \"../core\";\n\nfunction mapFieldType(dbType: string): string {\n const typeMap: Record<string, string> = {\n String: \"String\",\n Boolean: \"Boolean\",\n DateTime: \"DateTime\",\n Int: \"Int\",\n Float: \"Float\",\n Json: \"Json\",\n };\n return typeMap[dbType] ?? \"String\";\n}\n\nexport function generatePrismaSchema(\n config: AdminForgeConfig,\n options?: { provider?: string }\n): string {\n const provider = options?.provider ?? \"sqlite\";\n const lines: string[] = [];\n lines.push(\"generator client {\");\n lines.push(' provider = \"prisma-client-js\"');\n lines.push(\"}\");\n lines.push(\"\");\n lines.push(\"datasource db {\");\n lines.push(` provider = \"${provider}\"`);\n lines.push(' url = env(\"DATABASE_URL\")');\n lines.push(\"}\");\n lines.push(\"\");\n\n const inverseRelations: Record<string, string[]> = {};\n\n // First pass: generate fields for each model and collect inverse relations\n const modelBlocks: Record<string, string[]> = {};\n\n for (const collection of config.collections) {\n const modelName = collection.name;\n const block: string[] = [];\n block.push(`model ${modelName} {`);\n block.push(\" id String @id @default(cuid())\");\n block.push(\" createdAt DateTime @default(now())\");\n block.push(\" updatedAt DateTime @updatedAt\");\n\n for (const [name, field] of Object.entries(collection.fields)) {\n if (field.type === \"relation\") {\n const targetModel = (field.db.references?.model as string) || (field.ui.props?.to as string) || \"String\";\n const relType = field.db.relationType || \"many-to-one\";\n\n if (!inverseRelations[targetModel]) inverseRelations[targetModel] = [];\n\n if (relType === \"many-to-many\") {\n block.push(` ${name} ${targetModel}[]`);\n inverseRelations[targetModel].push(` adminforge_inverse_${modelName}_${name} ${modelName}[]`);\n } else if (relType === \"one-to-many\") {\n block.push(` ${name} ${targetModel}[]`);\n inverseRelations[targetModel].push(` adminforge_inverse_${modelName}_${name}Id String?`);\n inverseRelations[targetModel].push(` adminforge_inverse_${modelName}_${name} ${modelName}? @relation(fields: [adminforge_inverse_${modelName}_${name}Id], references: [id])`);\n } else {\n // many-to-one (default)\n block.push(` ${name}Id String${field.db.nullable ? \"?\" : \"\"}`);\n block.push(` ${name} ${targetModel}${field.db.nullable ? \"?\" : \"\"} @relation(fields: [${name}Id], references: [id])`);\n inverseRelations[targetModel].push(` adminforge_inverse_${modelName}_${name} ${modelName}[]`);\n }\n } else {\n const prismaType = mapFieldType(field.db.type);\n const nul = field.db.nullable ? \"?\" : \"\";\n const annotations: string[] = [];\n if (field.db.unique) annotations.push(\"@unique\");\n if (field.db.default !== undefined) {\n const val = field.db.default;\n if (typeof val === \"string\") {\n annotations.push(`@default(\"${val}\")`);\n } else if (typeof val === \"boolean\") {\n annotations.push(`@default(${String(val)})`);\n } else {\n annotations.push(`@default(${String(val)})`);\n }\n }\n const ann = annotations.length > 0 ? ` ${annotations.join(\" \")}` : \"\";\n block.push(` ${name} ${prismaType}${nul}${ann}`);\n }\n }\n\n modelBlocks[modelName] = block;\n }\n\n // Second pass: append inverse relations and close blocks\n for (const collection of config.collections) {\n const modelName = collection.name;\n const block = modelBlocks[modelName];\n if (inverseRelations[modelName]) {\n block.push(\"\");\n block.push(\" // Auto-generated inverse relations\");\n for (const inv of inverseRelations[modelName]) {\n block.push(inv);\n }\n }\n block.push(\"}\");\n lines.push(block.join(\"\\n\"));\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\");\n}\n","import { PrismaClient } from \"@prisma/client\";\nimport type { AdminForgeConfig, CollectionDefinition, CollectionHooks } from \"../core\";\nimport {\n executeBeforeCreate,\n executeAfterCreate,\n executeBeforeUpdate,\n executeAfterUpdate,\n executeBeforeDelete,\n executeAfterDelete,\n} from \"../core\";\n\nexport interface DbClient {\n create(collection: string, data: Record<string, unknown>): Promise<unknown>;\n findMany(collection: string, args?: { where?: Record<string, unknown>; orderBy?: Record<string, string>; skip?: number; take?: number }): Promise<unknown[]>;\n findUnique(collection: string, id: string): Promise<unknown | null>;\n update(collection: string, id: string, data: Record<string, unknown>): Promise<unknown>;\n delete(collection: string, id: string): Promise<unknown>;\n count(collection: string, args?: { where?: Record<string, unknown> }): Promise<number>;\n}\n\nexport function createDbClient(config: AdminForgeConfig, existingPrisma?: any): DbClient {\n const prisma = existingPrisma || new PrismaClient();\n const collectionMap = new Map<string, CollectionDefinition>();\n\n const hookMap = new Map<string, CollectionHooks | undefined>();\n\n for (const collection of config.collections) {\n collectionMap.set(collection.name, collection);\n hookMap.set(collection.name, collection.hooks);\n }\n\n const getPrismaModel = (collection: string) => {\n const model = (prisma as unknown as Record<string, unknown>)[collection];\n if (!model || typeof model !== \"object\") {\n throw new Error(`Collection \"${collection}\" not found in Prisma schema`);\n }\n return model as {\n create: (args: { data: Record<string, unknown>; include?: Record<string, boolean> }) => Promise<unknown>;\n findMany: (args?: Record<string, unknown>) => Promise<unknown[]>;\n findUnique: (args: { where: { id: string }; include?: Record<string, boolean> }) => Promise<unknown | null>;\n update: (args: { where: { id: string }; data: Record<string, unknown>; include?: Record<string, boolean> }) => Promise<unknown>;\n delete: (args: { where: { id: string } }) => Promise<unknown>;\n count: (args?: { where?: Record<string, unknown> }) => Promise<number>;\n };\n };\n\n // Build include map for collections with relation fields\n const getIncludeMap = (collectionName: string): Record<string, boolean> | undefined => {\n const collDef = collectionMap.get(collectionName);\n if (!collDef) return undefined;\n const include: Record<string, boolean> = {};\n let hasRelations = false;\n for (const [fieldName, field] of Object.entries(collDef.fields)) {\n if (field.type === \"relation\") {\n include[fieldName] = true;\n hasRelations = true;\n }\n }\n return hasRelations ? include : undefined;\n };\n\n // Transform relation fields from raw IDs/arrays into Prisma connect/set syntax\n const transformRelations = (collectionName: string, data: Record<string, unknown>, isUpdate: boolean): Record<string, unknown> => {\n const collDef = collectionMap.get(collectionName);\n if (!collDef) return data;\n const transformed = { ...data };\n for (const [key, value] of Object.entries(transformed)) {\n const field = collDef.fields[key];\n if (field?.type === \"relation\") {\n const relationType = field.db.relationType ?? \"many-to-one\";\n const isMulti = relationType === \"many-to-many\" || relationType === \"one-to-many\";\n\n if (Array.isArray(value)) {\n // Many-to-many or one-to-many: array of IDs\n const ids = value.filter((id): id is string => typeof id === \"string\" && id.length > 0);\n if (isUpdate) {\n transformed[key] = { set: ids.map((id) => ({ id })) };\n } else {\n transformed[key] = { connect: ids.map((id) => ({ id })) };\n }\n } else if (!isMulti && typeof value === \"string\" && value !== \"\") {\n // Many-to-one: single ID string\n transformed[key] = { connect: { id: value } };\n // Remove the raw field and set the FK field instead for many-to-one\n // Actually Prisma accepts both connect syntax and raw FK, but connect is cleaner\n } else if ((value === null || value === \"\") && isUpdate) {\n transformed[key] = { disconnect: true };\n } else if (value === null || value === \"\") {\n delete transformed[key];\n }\n }\n }\n return transformed;\n };\n\n return {\n async create(collection: string, data: Record<string, unknown>): Promise<unknown> {\n const hooks = hookMap.get(collection);\n const processedData = await executeBeforeCreate(hooks, data);\n const prismaData = transformRelations(collection, processedData, false);\n const model = getPrismaModel(collection);\n const include = getIncludeMap(collection);\n const result = await model.create({ data: prismaData, ...(include ? { include } : {}) });\n const resultId = (result as { id: string }).id;\n await executeAfterCreate(hooks, processedData, resultId);\n return result;\n },\n\n async findMany(collection: string, args: Record<string, unknown> = {}): Promise<unknown[]> {\n const model = getPrismaModel(collection);\n const include = getIncludeMap(collection);\n return model.findMany({ ...args, ...(include ? { include } : {}) });\n },\n\n async findUnique(collection: string, id: string): Promise<unknown | null> {\n const model = getPrismaModel(collection);\n const include = getIncludeMap(collection);\n return model.findUnique({ where: { id }, ...(include ? { include } : {}) });\n },\n\n async update(collection: string, id: string, data: Record<string, unknown>): Promise<unknown> {\n const hooks = hookMap.get(collection);\n const processedData = await executeBeforeUpdate(hooks, data, id);\n const prismaData = transformRelations(collection, processedData, true);\n const model = getPrismaModel(collection);\n const include = getIncludeMap(collection);\n const result = await model.update({ where: { id }, data: prismaData, ...(include ? { include } : {}) });\n await executeAfterUpdate(hooks, processedData, id);\n return result;\n },\n\n async delete(collection: string, id: string): Promise<unknown> {\n const hooks = hookMap.get(collection);\n await executeBeforeDelete(hooks, id);\n const model = getPrismaModel(collection);\n const result = await model.delete({ where: { id } });\n await executeAfterDelete(hooks, id);\n return result;\n },\n\n async count(collection: string, args: { where?: Record<string, unknown> } = {}): Promise<number> {\n const model = getPrismaModel(collection);\n return model.count(args);\n },\n };\n}\n"],"mappings":";AAEO,SAAS,aAAa,QAGR;AACnB,SAAO;AAAA,IACL,aAAa,OAAO;AAAA,IACpB,MAAM,OAAO,QAAQ,EAAE,SAAS,MAAM;AAAA,EACxC;AACF;;;ACRO,SAAS,WAAW,OAA8C;AACvE,SAAO;AAAA,IACL,MAAM,MAAM;AAAA,IACZ,OAAO,MAAM,SAAS,MAAM,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,MAAM,KAAK,MAAM,CAAC;AAAA,IAC7E,MAAM,MAAM;AAAA,IACZ,QAAQ,MAAM;AAAA,IACd,OAAO,MAAM;AAAA,IACb,QAAQ,MAAM;AAAA,EAChB;AACF;;;ACPO,SAAS,UAAU,QAA4C;AACpE,SAAO;AAAA,IACL,aAAa,OAAO,YAAY,IAAI,CAAC,OAAO;AAAA,MAC1C,GAAG;AAAA,MACH,OAAO,EAAE,SAAS,EAAE,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,EAAE,KAAK,MAAM,CAAC;AAAA,IACnE,EAAE;AAAA,IACF,MAAM,OAAO,QAAQ,EAAE,SAAS,MAAM;AAAA,EACxC;AACF;AAMO,SAAS,gBAAgB,QAA+B;AAC7D,SAAO;AAAA,IACL,GAAG;AAAA,IACH,aAAa,OAAO,YAAY,IAAI,CAAC,OAAO;AAAA,MAC1C,GAAG;AAAA,MACH,QAAQ,OAAO;AAAA,QACb,OAAO,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM;AAC9C,gBAAM,EAAE,YAAY,GAAG,KAAK,IAAI;AAChC,iBAAO,CAAC,MAAM,IAAI;AAAA,QACpB,CAAC;AAAA,MACH;AAAA,IACF,EAAE;AAAA,EACJ;AACF;;;AC/BA,SAAS,SAAS;AAclB,SAAS,UAAU,UAAwB,CAAC,GAAc;AACxD,SAAO;AAAA,IACL,UAAU,QAAQ,QAAQ,QAAQ;AAAA,IAClC,QAAQ,QAAQ,QAAQ,MAAM;AAAA,IAC9B,SAAS,QAAQ;AAAA,IACjB,OAAO,QAAQ;AAAA,IACf,QAAQ,QAAQ;AAAA,IAChB,UAAU,QAAQ;AAAA,IAClB,aAAa,QAAQ;AAAA,EACvB;AACF;AAEA,SAAS,KAAK,UAAuB,CAAC,GAAoB;AACxD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,IAAI,EAAE,MAAM,UAAU,UAAU,CAAC,QAAQ,UAAU,QAAQ,QAAQ,QAAQ,SAAS,QAAQ,QAAQ;AAAA,IACpG,QAAQ,QAAQ;AAAA,IAChB,IAAI,EAAE,WAAW,QAAQ,OAAO,EAAE,OAAO,QAAQ,OAAO,QAAQ,QAAQ,QAAQ,UAAU,QAAQ,SAAS,EAAE;AAAA,IAC7G,YAAY,QAAQ,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS;AAAA,IACvE,MAAM,UAAU,OAAO;AAAA,EACzB;AACF;AAEA,SAAS,QAAQ,UAA0B,CAAC,GAAoB;AAC9D,SAAO;AAAA,IACL,MAAM;AAAA,IACN,IAAI,EAAE,MAAM,WAAW,UAAU,CAAC,QAAQ,UAAU,SAAS,QAAQ,WAAW,MAAM;AAAA,IACtF,QAAQ,QAAQ;AAAA,IAChB,IAAI,EAAE,WAAW,WAAW,OAAO,EAAE,OAAO,QAAQ,OAAO,QAAQ,QAAQ,QAAQ,UAAU,QAAQ,SAAS,EAAE;AAAA,IAChH,YAAY,QAAQ,WAAW,EAAE,QAAQ,IAAI,EAAE,QAAQ,EAAE,SAAS;AAAA,IAClE,MAAM,UAAU,OAAO;AAAA,EACzB;AACF;AAEA,SAAS,SAAS,UAA2B,CAAC,GAAoB;AAChE,SAAO;AAAA,IACL,MAAM;AAAA,IACN,IAAI,EAAE,MAAM,UAAU,UAAU,CAAC,QAAQ,SAAS;AAAA,IAClD,QAAQ,QAAQ;AAAA,IAChB,IAAI,EAAE,WAAW,YAAY,OAAO,EAAE,OAAO,QAAQ,OAAO,QAAQ,QAAQ,QAAQ,UAAU,QAAQ,SAAS,EAAE;AAAA,IACjH,YAAY,QAAQ,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS;AAAA,IACvE,MAAM,UAAU,OAAO;AAAA,EACzB;AACF;AAEA,SAAS,KAAK,SAAuC;AACnD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,IAAI,EAAE,MAAM,UAAU,UAAU,CAAC,QAAQ,UAAU,QAAQ,QAAQ,UAAU,KAAK;AAAA,IAClF,QAAQ,QAAQ;AAAA,IAChB,IAAI,EAAE,WAAW,QAAQ,OAAO,EAAE,MAAM,QAAQ,MAAM,OAAO,QAAQ,OAAO,QAAQ,QAAQ,QAAQ,UAAU,QAAQ,SAAS,EAAE;AAAA,IACjI,YAAY,QAAQ,WAChB,EAAE,OAAO,EAAE,MAAM,cAAc,IAC/B,EAAE,OAAO,EAAE,MAAM,cAAc,EAAE,SAAS;AAAA,IAC9C,OAAO,EAAE,YAAY,CAAC,UAAmB;AACvC,UAAI,OAAO,UAAU,SAAU,QAAO,MAAM,YAAY,EAAE,QAAQ,QAAQ,GAAG,EAAE,QAAQ,eAAe,EAAE;AACxG,aAAO;AAAA,IACT,EAAC;AAAA,IACD,MAAM,EAAE,GAAG,UAAU,OAAO,GAAG,QAAQ,QAAQ,UAAU,KAAK;AAAA,EAChE;AACF;AAEA,SAAS,SAAS,SAA2C;AAC3D,QAAM,UAAU,QAAQ,SAAS,kBAAkB,QAAQ,SAAS;AACpE,SAAO;AAAA,IACL,MAAM;AAAA,IACN,IAAI,EAAE,MAAM,UAAU,UAAU,CAAC,QAAQ,UAAU,YAAY,EAAE,OAAO,QAAQ,IAAI,OAAO,KAAK,GAAG,cAAc,QAAQ,KAAK;AAAA,IAC9H,QAAQ,QAAQ;AAAA,IAChB,IAAI,EAAE,WAAW,YAAY,OAAO,EAAE,IAAI,QAAQ,IAAI,cAAc,QAAQ,MAAM,OAAO,QAAQ,OAAO,QAAQ,QAAQ,QAAQ,UAAU,QAAQ,SAAS,EAAE;AAAA,IAC7J,YAAY,UACP,QAAQ,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,IAC7E,QAAQ,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS;AAAA,IAChE,MAAM,UAAU,OAAO;AAAA,EACzB;AACF;AAEA,SAAS,KAAK,UAAuB,CAAC,GAAoB;AACxD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,IAAI,EAAE,MAAM,YAAY,UAAU,CAAC,QAAQ,SAAS;AAAA,IACpD,QAAQ,QAAQ;AAAA,IAChB,IAAI,EAAE,WAAW,QAAQ,OAAO,EAAE,OAAO,QAAQ,OAAO,QAAQ,QAAQ,QAAQ,UAAU,QAAQ,SAAS,EAAE;AAAA,IAC7G,YAAY,QAAQ,WAAW,EAAE,OAAO,EAAE,SAAS,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,IACtF,OAAO,EAAE,YAAY,CAAC,UAAmB;AACvC,UAAI,QAAQ,cAAc,CAAC,MAAO,SAAO,oBAAI,KAAK,GAAE,YAAY;AAChE,aAAO;AAAA,IACT,EAAC;AAAA,IACD,MAAM,UAAU,OAAO;AAAA,EACzB;AACF;AAEA,SAAS,MAAM,UAAwB,CAAC,GAAoB;AAC1D,SAAO;AAAA,IACL,MAAM;AAAA,IACN,IAAI,EAAE,MAAM,UAAU,UAAU,KAAK;AAAA,IACrC,QAAQ,QAAQ;AAAA,IAChB,IAAI,EAAE,WAAW,SAAS,OAAO,EAAE,OAAO,QAAQ,OAAO,QAAQ,QAAQ,QAAQ,UAAU,QAAQ,SAAS,EAAE;AAAA,IAC9G,YAAY,EAAE,OAAO,EAAE,SAAS;AAAA,IAChC,MAAM,UAAU,OAAO;AAAA,EACzB;AACF;AAEO,IAAM,SAAS;AAAA,EACpB;AAAA,EAAM;AAAA,EAAS;AAAA,EAAU;AAAA,EAAM;AAAA,EAAU;AAAA,EAAM;AACjD;;;ACpHA,IAAM,gBAAgB,oBAAI,IAA6B;AAEhD,SAAS,cAAc,MAAc,YAAmC;AAC7E,MAAI,cAAc,IAAI,IAAI,GAAG;AAC3B,UAAM,IAAI,MAAM,UAAU,IAAI,yBAAyB;AAAA,EACzD;AACA,gBAAc,IAAI,MAAM,UAAU;AACpC;AAEO,SAAS,SAAS,MAA2C;AAClE,SAAO,cAAc,IAAI,IAAI;AAC/B;AAEO,SAAS,sBAAoD;AAClE,SAAO,IAAI,IAAI,aAAa;AAC9B;AAEO,SAAS,gBAAsB;AACpC,gBAAc,MAAM;AACtB;;;ACnBA,eAAsB,oBACpB,OACA,MACkC;AAClC,MAAI,OAAO,cAAc;AACvB,WAAO,MAAM,MAAM,aAAa,EAAE,KAAK,CAAC;AAAA,EAC1C;AACA,SAAO;AACT;AAEA,eAAsB,mBACpB,OACA,MACA,IACe;AACf,MAAI,OAAO,aAAa;AACtB,UAAM,MAAM,YAAY,EAAE,MAAM,GAAG,CAAC;AAAA,EACtC;AACF;AAEA,eAAsB,oBACpB,OACA,MACA,IACkC;AAClC,MAAI,OAAO,cAAc;AACvB,WAAO,MAAM,MAAM,aAAa,EAAE,MAAM,GAAG,CAAC;AAAA,EAC9C;AACA,SAAO;AACT;AAEA,eAAsB,mBACpB,OACA,MACA,IACe;AACf,MAAI,OAAO,aAAa;AACtB,UAAM,MAAM,YAAY,EAAE,MAAM,GAAG,CAAC;AAAA,EACtC;AACF;AAEA,eAAsB,oBACpB,OACA,IACe;AACf,MAAI,OAAO,cAAc;AACvB,UAAM,MAAM,aAAa,EAAE,GAAG,CAAC;AAAA,EACjC;AACF;AAEA,eAAsB,mBACpB,OACA,IACe;AACf,MAAI,OAAO,aAAa;AACtB,UAAM,MAAM,YAAY,EAAE,GAAG,CAAC;AAAA,EAChC;AACF;;;ACzDA,SAAS,aAAa,QAAwB;AAC5C,QAAM,UAAkC;AAAA,IACtC,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,UAAU;AAAA,IACV,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,EACR;AACA,SAAO,QAAQ,MAAM,KAAK;AAC5B;AAEO,SAAS,qBACd,QACA,SACQ;AACR,QAAM,WAAW,SAAS,YAAY;AACtC,QAAM,QAAkB,CAAC;AACzB,QAAM,KAAK,oBAAoB;AAC/B,QAAM,KAAK,iCAAiC;AAC5C,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,iBAAiB;AAC5B,QAAM,KAAK,iBAAiB,QAAQ,GAAG;AACvC,QAAM,KAAK,kCAAkC;AAC7C,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,EAAE;AAEb,QAAM,mBAA6C,CAAC;AAGpD,QAAM,cAAwC,CAAC;AAE/C,aAAWA,eAAc,OAAO,aAAa;AAC3C,UAAM,YAAYA,YAAW;AAC7B,UAAM,QAAkB,CAAC;AACzB,UAAM,KAAK,SAAS,SAAS,IAAI;AACjC,UAAM,KAAK,2CAA2C;AACtD,UAAM,KAAK,sCAAsC;AACjD,UAAM,KAAK,iCAAiC;AAE5C,eAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQA,YAAW,MAAM,GAAG;AAC7D,UAAI,MAAM,SAAS,YAAY;AAC7B,cAAM,cAAe,MAAM,GAAG,YAAY,SAAqB,MAAM,GAAG,OAAO,MAAiB;AAChG,cAAM,UAAU,MAAM,GAAG,gBAAgB;AAEzC,YAAI,CAAC,iBAAiB,WAAW,EAAG,kBAAiB,WAAW,IAAI,CAAC;AAErE,YAAI,YAAY,gBAAgB;AAC9B,gBAAM,KAAK,KAAK,IAAI,IAAI,WAAW,IAAI;AACvC,2BAAiB,WAAW,EAAE,KAAK,wBAAwB,SAAS,IAAI,IAAI,IAAI,SAAS,IAAI;AAAA,QAC/F,WAAW,YAAY,eAAe;AACpC,gBAAM,KAAK,KAAK,IAAI,IAAI,WAAW,IAAI;AACvC,2BAAiB,WAAW,EAAE,KAAK,wBAAwB,SAAS,IAAI,IAAI,YAAY;AACxF,2BAAiB,WAAW,EAAE,KAAK,wBAAwB,SAAS,IAAI,IAAI,IAAI,SAAS,2CAA2C,SAAS,IAAI,IAAI,wBAAwB;AAAA,QAC/K,OAAO;AAEL,gBAAM,KAAK,KAAK,IAAI,YAAY,MAAM,GAAG,WAAW,MAAM,EAAE,EAAE;AAC9D,gBAAM,KAAK,KAAK,IAAI,MAAM,WAAW,GAAG,MAAM,GAAG,WAAW,MAAM,EAAE,uBAAuB,IAAI,wBAAwB;AACvH,2BAAiB,WAAW,EAAE,KAAK,wBAAwB,SAAS,IAAI,IAAI,IAAI,SAAS,IAAI;AAAA,QAC/F;AAAA,MACF,OAAO;AACL,cAAM,aAAa,aAAa,MAAM,GAAG,IAAI;AAC7C,cAAM,MAAM,MAAM,GAAG,WAAW,MAAM;AACtC,cAAM,cAAwB,CAAC;AAC/B,YAAI,MAAM,GAAG,OAAQ,aAAY,KAAK,SAAS;AAC/C,YAAI,MAAM,GAAG,YAAY,QAAW;AAClC,gBAAM,MAAM,MAAM,GAAG;AACrB,cAAI,OAAO,QAAQ,UAAU;AAC3B,wBAAY,KAAK,aAAa,GAAG,IAAI;AAAA,UACvC,WAAW,OAAO,QAAQ,WAAW;AACnC,wBAAY,KAAK,YAAY,OAAO,GAAG,CAAC,GAAG;AAAA,UAC7C,OAAO;AACL,wBAAY,KAAK,YAAY,OAAO,GAAG,CAAC,GAAG;AAAA,UAC7C;AAAA,QACF;AACA,cAAM,MAAM,YAAY,SAAS,IAAI,IAAI,YAAY,KAAK,GAAG,CAAC,KAAK;AACnE,cAAM,KAAK,KAAK,IAAI,IAAI,UAAU,GAAG,GAAG,GAAG,GAAG,EAAE;AAAA,MAClD;AAAA,IACF;AAEA,gBAAY,SAAS,IAAI;AAAA,EAC3B;AAGA,aAAWA,eAAc,OAAO,aAAa;AAC3C,UAAM,YAAYA,YAAW;AAC7B,UAAM,QAAQ,YAAY,SAAS;AACnC,QAAI,iBAAiB,SAAS,GAAG;AAC/B,YAAM,KAAK,EAAE;AACb,YAAM,KAAK,uCAAuC;AAClD,iBAAW,OAAO,iBAAiB,SAAS,GAAG;AAC7C,cAAM,KAAK,GAAG;AAAA,MAChB;AAAA,IACF;AACA,UAAM,KAAK,GAAG;AACd,UAAM,KAAK,MAAM,KAAK,IAAI,CAAC;AAC3B,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;;;ACvGA,SAAS,oBAAoB;AAoBtB,SAAS,eAAe,QAA0B,gBAAgC;AACvF,QAAM,SAAS,kBAAkB,IAAI,aAAa;AAClD,QAAM,gBAAgB,oBAAI,IAAkC;AAE5D,QAAM,UAAU,oBAAI,IAAyC;AAE7D,aAAWC,eAAc,OAAO,aAAa;AAC3C,kBAAc,IAAIA,YAAW,MAAMA,WAAU;AAC7C,YAAQ,IAAIA,YAAW,MAAMA,YAAW,KAAK;AAAA,EAC/C;AAEA,QAAM,iBAAiB,CAACA,gBAAuB;AAC7C,UAAM,QAAS,OAA8CA,WAAU;AACvE,QAAI,CAAC,SAAS,OAAO,UAAU,UAAU;AACvC,YAAM,IAAI,MAAM,eAAeA,WAAU,8BAA8B;AAAA,IACzE;AACA,WAAO;AAAA,EAQT;AAGA,QAAM,gBAAgB,CAAC,mBAAgE;AACrF,UAAM,UAAU,cAAc,IAAI,cAAc;AAChD,QAAI,CAAC,QAAS,QAAO;AACrB,UAAM,UAAmC,CAAC;AAC1C,QAAI,eAAe;AACnB,eAAW,CAAC,WAAW,KAAK,KAAK,OAAO,QAAQ,QAAQ,MAAM,GAAG;AAC/D,UAAI,MAAM,SAAS,YAAY;AAC7B,gBAAQ,SAAS,IAAI;AACrB,uBAAe;AAAA,MACjB;AAAA,IACF;AACA,WAAO,eAAe,UAAU;AAAA,EAClC;AAGA,QAAM,qBAAqB,CAAC,gBAAwB,MAA+B,aAA+C;AAChI,UAAM,UAAU,cAAc,IAAI,cAAc;AAChD,QAAI,CAAC,QAAS,QAAO;AACrB,UAAM,cAAc,EAAE,GAAG,KAAK;AAC9B,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,WAAW,GAAG;AACtD,YAAM,QAAQ,QAAQ,OAAO,GAAG;AAChC,UAAI,OAAO,SAAS,YAAY;AAC9B,cAAM,eAAe,MAAM,GAAG,gBAAgB;AAC9C,cAAM,UAAU,iBAAiB,kBAAkB,iBAAiB;AAEpE,YAAI,MAAM,QAAQ,KAAK,GAAG;AAExB,gBAAM,MAAM,MAAM,OAAO,CAAC,OAAqB,OAAO,OAAO,YAAY,GAAG,SAAS,CAAC;AACtF,cAAI,UAAU;AACZ,wBAAY,GAAG,IAAI,EAAE,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE;AAAA,UACtD,OAAO;AACL,wBAAY,GAAG,IAAI,EAAE,SAAS,IAAI,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE;AAAA,UAC1D;AAAA,QACF,WAAW,CAAC,WAAW,OAAO,UAAU,YAAY,UAAU,IAAI;AAEhE,sBAAY,GAAG,IAAI,EAAE,SAAS,EAAE,IAAI,MAAM,EAAE;AAAA,QAG9C,YAAY,UAAU,QAAQ,UAAU,OAAO,UAAU;AACvD,sBAAY,GAAG,IAAI,EAAE,YAAY,KAAK;AAAA,QACxC,WAAW,UAAU,QAAQ,UAAU,IAAI;AACzC,iBAAO,YAAY,GAAG;AAAA,QACxB;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,MAAM,OAAOA,aAAoB,MAAiD;AAChF,YAAM,QAAQ,QAAQ,IAAIA,WAAU;AACpC,YAAM,gBAAgB,MAAM,oBAAoB,OAAO,IAAI;AAC3D,YAAM,aAAa,mBAAmBA,aAAY,eAAe,KAAK;AACtE,YAAM,QAAQ,eAAeA,WAAU;AACvC,YAAM,UAAU,cAAcA,WAAU;AACxC,YAAM,SAAS,MAAM,MAAM,OAAO,EAAE,MAAM,YAAY,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,EAAG,CAAC;AACvF,YAAM,WAAY,OAA0B;AAC5C,YAAM,mBAAmB,OAAO,eAAe,QAAQ;AACvD,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,SAASA,aAAoB,OAAgC,CAAC,GAAuB;AACzF,YAAM,QAAQ,eAAeA,WAAU;AACvC,YAAM,UAAU,cAAcA,WAAU;AACxC,aAAO,MAAM,SAAS,EAAE,GAAG,MAAM,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,EAAG,CAAC;AAAA,IACpE;AAAA,IAEA,MAAM,WAAWA,aAAoB,IAAqC;AACxE,YAAM,QAAQ,eAAeA,WAAU;AACvC,YAAM,UAAU,cAAcA,WAAU;AACxC,aAAO,MAAM,WAAW,EAAE,OAAO,EAAE,GAAG,GAAG,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,EAAG,CAAC;AAAA,IAC5E;AAAA,IAEA,MAAM,OAAOA,aAAoB,IAAY,MAAiD;AAC5F,YAAM,QAAQ,QAAQ,IAAIA,WAAU;AACpC,YAAM,gBAAgB,MAAM,oBAAoB,OAAO,MAAM,EAAE;AAC/D,YAAM,aAAa,mBAAmBA,aAAY,eAAe,IAAI;AACrE,YAAM,QAAQ,eAAeA,WAAU;AACvC,YAAM,UAAU,cAAcA,WAAU;AACxC,YAAM,SAAS,MAAM,MAAM,OAAO,EAAE,OAAO,EAAE,GAAG,GAAG,MAAM,YAAY,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,EAAG,CAAC;AACtG,YAAM,mBAAmB,OAAO,eAAe,EAAE;AACjD,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,OAAOA,aAAoB,IAA8B;AAC7D,YAAM,QAAQ,QAAQ,IAAIA,WAAU;AACpC,YAAM,oBAAoB,OAAO,EAAE;AACnC,YAAM,QAAQ,eAAeA,WAAU;AACvC,YAAM,SAAS,MAAM,MAAM,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;AACnD,YAAM,mBAAmB,OAAO,EAAE;AAClC,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,MAAMA,aAAoB,OAA4C,CAAC,GAAoB;AAC/F,YAAM,QAAQ,eAAeA,WAAU;AACvC,aAAO,MAAM,MAAM,IAAI;AAAA,IACzB;AAAA,EACF;AACF;","names":["collection","collection"]}
|