@paneljs/paneljs 0.2.0 → 0.2.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/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.0] - 2026-08-22
4
+
5
+ Admin operations now live in core. Transports call `AdminService` instead of reimplementing CRUD, actions, and delete rules.
6
+
7
+ ### Added
8
+
9
+ - `AdminService` with `list`, `get`, `create`, `update`, `delete`, `deletePreview`, `deleteSelected`, and `runAction`.
10
+ - `admin.service`, available after `admin.initialize()`.
11
+ - `validateSelectedIds` for bulk-action ID rules: non-empty, unique, and at most 100.
12
+ - `loadDeletePreviewRelations` and `assertNoRestrictedRelations`, moved out of `@paneljs/express`.
13
+
14
+ ### Changed
15
+
16
+ - Permission, scope, hook, audit, and referential-delete orchestration run in `AdminService`, so Express no longer owns that behavior.
17
+
18
+ ## [0.2.2] - 2026-08-21
19
+
20
+ ### Breaking
21
+
22
+ - The package name is now `paneljs`, replacing `@paneljs/paneljs`. Update imports to `from "paneljs"` when upgrading.
23
+
24
+ ## [0.2.1] - 2026-08-20
25
+
26
+ ### Changed
27
+
28
+ - Delete confirmation lists related rows for Cascade, SetNull, and Restrict, not only Cascade.
29
+ - Restrict / Protect relations disable Confirm delete and explain that related records still reference the row.
30
+
3
31
  ## [0.2.0] - 2026-08-19
4
32
 
5
33
  ORM-agnostic core contract. Adapters speak PanelJS query types; Express and the UI do not.
package/README.md CHANGED
@@ -1,15 +1,15 @@
1
- # @paneljs/paneljs
1
+ # paneljs
2
2
 
3
3
  Schema-driven admin core. Register models and serve a guarded operations UI.
4
4
 
5
5
  Install with the adapters for your stack:
6
6
 
7
7
  ```bash
8
- npm install @paneljs/paneljs @paneljs/express @paneljs/prisma
8
+ npm install paneljs @paneljs/express @paneljs/prisma
9
9
  ```
10
10
 
11
11
  ```ts
12
- import { createAdmin } from "@paneljs/paneljs";
12
+ import { createAdmin } from "paneljs";
13
13
  import { prismaAdapter } from "@paneljs/prisma";
14
14
  import { mount } from "@paneljs/express";
15
15
 
@@ -0,0 +1,49 @@
1
+ import type { DataAdapter } from "./adapter.js";
2
+ import { type DeletePreviewRelation } from "./deleteRelations.js";
3
+ import type { FieldFilters, FindManyQuery, SearchQuery } from "./query.js";
4
+ import type { FullRegisteredModel } from "./registry.js";
5
+ import type { AdminUser, AuditConfig, PaginatedResponse } from "./types.js";
6
+ export type AdminListInput = {
7
+ page?: number;
8
+ perPage?: number;
9
+ filters?: FieldFilters;
10
+ search?: SearchQuery;
11
+ sort?: FindManyQuery["sort"];
12
+ };
13
+ export type DeletePreview = {
14
+ records: Record<string, unknown>[];
15
+ relations: DeletePreviewRelation[];
16
+ };
17
+ export type DeleteSelectedResult = {
18
+ requestedIds: Array<string | number>;
19
+ deletedIds: Array<string | number>;
20
+ partial: boolean;
21
+ };
22
+ export interface AdminServiceOptions {
23
+ models: Map<string, FullRegisteredModel>;
24
+ adapter: DataAdapter;
25
+ audit?: AuditConfig;
26
+ }
27
+ /** Framework-neutral CRUD, action, scope, hook, and audit orchestration. */
28
+ export declare class AdminService {
29
+ private readonly models;
30
+ private readonly adapter;
31
+ private readonly audit;
32
+ constructor(options: AdminServiceOptions);
33
+ list(model: FullRegisteredModel, adminUser: AdminUser, input?: AdminListInput): Promise<PaginatedResponse<Record<string, unknown>>>;
34
+ get(model: FullRegisteredModel, adminUser: AdminUser, id: string | number): Promise<Record<string, unknown>>;
35
+ create(model: FullRegisteredModel, adminUser: AdminUser, body: unknown): Promise<Record<string, unknown>>;
36
+ update(model: FullRegisteredModel, adminUser: AdminUser, id: string | number, body: unknown): Promise<Record<string, unknown>>;
37
+ delete(model: FullRegisteredModel, adminUser: AdminUser, id: string | number): Promise<void>;
38
+ deletePreview(model: FullRegisteredModel, adminUser: AdminUser, requestedIds: Array<string | number>): Promise<DeletePreview>;
39
+ deleteSelected(model: FullRegisteredModel, adminUser: AdminUser, requestedIds: Array<string | number>): Promise<DeleteSelectedResult>;
40
+ runAction(model: FullRegisteredModel, adminUser: AdminUser, actionName: string, requestedIds: Array<string | number>): Promise<{
41
+ message: string;
42
+ }>;
43
+ private authorize;
44
+ private validateCreateData;
45
+ private validateUpdateData;
46
+ private loadSelected;
47
+ private writeAuditSafely;
48
+ }
49
+ export declare function validateSelectedIds(ids: Array<string | number>): void;
@@ -0,0 +1,307 @@
1
+ import { writeAuditEvent } from "./audit.js";
2
+ import { assertNoRestrictedRelations, loadDeletePreviewRelations, } from "./deleteRelations.js";
3
+ import { DELETE_SELECTED_ACTION } from "./defaultActions.js";
4
+ import { PermissionDeniedError, RecordNotFoundError } from "./errors.js";
5
+ import { hasModelPermission, hasRegisteredActionPermission, } from "./permissions.js";
6
+ import { idSelect } from "./query.js";
7
+ import { assertSelectedRelationsAreVisible, buildListRecordSelect, buildRecordSelect, } from "./recordSelection.js";
8
+ import { applyCreateScope, assertScopeFieldsUnchanged, collectScopeFieldNames, resolveScope, } from "./scope.js";
9
+ import { assertRequiredCreateFields, RequestValidationError, validateHookPayload, validateWritePayload, } from "./validation.js";
10
+ const MAX_ACTION_RECORDS = 100;
11
+ /** Framework-neutral CRUD, action, scope, hook, and audit orchestration. */
12
+ export class AdminService {
13
+ models;
14
+ adapter;
15
+ audit;
16
+ constructor(options) {
17
+ this.models = options.models;
18
+ this.adapter = options.adapter;
19
+ this.audit = options.audit;
20
+ }
21
+ async list(model, adminUser, input = {}) {
22
+ this.authorize(model, adminUser, "list");
23
+ const page = input.page ?? 1;
24
+ const perPage = input.perPage ?? model.resolved.perPage;
25
+ assertPagination(page, perPage);
26
+ const scope = await resolveScope(model.raw, adminUser);
27
+ const delegate = this.adapter.resource(model.meta);
28
+ const filters = input.filters ?? {};
29
+ const sort = input.sort ?? model.resolved.defaultSort;
30
+ const [records, total] = await Promise.all([
31
+ delegate.findMany({
32
+ scope,
33
+ filters,
34
+ search: input.search,
35
+ sort,
36
+ skip: (page - 1) * perPage,
37
+ take: perPage,
38
+ select: buildListRecordSelect(model.meta, model),
39
+ }),
40
+ delegate.count({ scope, filters, search: input.search }),
41
+ ]);
42
+ return {
43
+ records,
44
+ total,
45
+ page,
46
+ perPage,
47
+ totalPages: Math.ceil(total / perPage),
48
+ };
49
+ }
50
+ async get(model, adminUser, id) {
51
+ this.authorize(model, adminUser, "view");
52
+ const scope = await resolveScope(model.raw, adminUser);
53
+ const record = await this.adapter.resource(model.meta).findFirst({
54
+ scope,
55
+ id,
56
+ select: buildRecordSelect(model.meta, model),
57
+ });
58
+ if (!record)
59
+ throw new RecordNotFoundError();
60
+ return record;
61
+ }
62
+ async create(model, adminUser, body) {
63
+ this.authorize(model, adminUser, "create");
64
+ const scope = await resolveScope(model.raw, adminUser);
65
+ let data = applyCreateScope(validateWritePayload(model.meta, model.raw, adminUser, body), scope);
66
+ await this.validateCreateData(model, adminUser, data);
67
+ if (model.raw.beforeCreate) {
68
+ const hookData = await model.raw.beforeCreate(data);
69
+ const scopeFieldNames = new Set(Object.keys(scope));
70
+ data = validateHookPayload(model.meta, Object.fromEntries(Object.entries(hookData).filter(([fieldName]) => !scopeFieldNames.has(fieldName))));
71
+ data = applyCreateScope(data, scope);
72
+ await this.validateCreateData(model, adminUser, data);
73
+ }
74
+ const record = await this.adapter.resource(model.meta).create({
75
+ data,
76
+ select: buildRecordSelect(model.meta, model),
77
+ });
78
+ if (model.raw.afterCreate) {
79
+ await runPostCommit("afterCreate", () => model.raw.afterCreate(record));
80
+ }
81
+ const recordId = record[model.meta.idField];
82
+ await this.writeAuditSafely(adminUser, {
83
+ type: "create",
84
+ modelName: model.meta.name,
85
+ recordIds: typeof recordId === "string" || typeof recordId === "number"
86
+ ? [recordId]
87
+ : [],
88
+ });
89
+ return record;
90
+ }
91
+ async update(model, adminUser, id, body) {
92
+ this.authorize(model, adminUser, "update");
93
+ const scope = await resolveScope(model.raw, adminUser);
94
+ const delegate = this.adapter.resource(model.meta);
95
+ const existing = await delegate.findFirst({
96
+ scope,
97
+ id,
98
+ select: idSelect(model.meta.idField),
99
+ });
100
+ if (!existing)
101
+ throw new RecordNotFoundError();
102
+ let data = validateWritePayload(model.meta, model.raw, adminUser, body);
103
+ await this.validateUpdateData(model, adminUser, scope, data);
104
+ if (model.raw.beforeUpdate) {
105
+ data = validateHookPayload(model.meta, await model.raw.beforeUpdate(String(id), data));
106
+ await this.validateUpdateData(model, adminUser, scope, data);
107
+ }
108
+ const result = await delegate.updateMany({ scope, id, data });
109
+ if (result.count === 0)
110
+ throw new RecordNotFoundError();
111
+ const record = await delegate.findFirst({
112
+ scope,
113
+ id,
114
+ select: buildRecordSelect(model.meta, model),
115
+ });
116
+ if (!record) {
117
+ throw new Error(`[paneljs] Updated record "${model.meta.name}/${id}" could not be reloaded.`);
118
+ }
119
+ if (model.raw.afterUpdate) {
120
+ await runPostCommit("afterUpdate", () => model.raw.afterUpdate(record));
121
+ }
122
+ await this.writeAuditSafely(adminUser, {
123
+ type: "update",
124
+ modelName: model.meta.name,
125
+ recordIds: [id],
126
+ });
127
+ return record;
128
+ }
129
+ async delete(model, adminUser, id) {
130
+ this.authorize(model, adminUser, "delete");
131
+ const scope = await resolveScope(model.raw, adminUser);
132
+ const delegate = this.adapter.resource(model.meta);
133
+ const record = await delegate.findFirst({
134
+ scope,
135
+ id,
136
+ select: buildRecordSelect(model.meta, model),
137
+ });
138
+ if (!record)
139
+ throw new RecordNotFoundError();
140
+ if (model.raw.beforeDelete)
141
+ await model.raw.beforeDelete(String(id));
142
+ await assertNoRestrictedRelations(model, this.models, this.adapter, adminUser, [id]);
143
+ const result = await delegate.deleteMany({ scope, id });
144
+ if (result.count === 0)
145
+ throw new RecordNotFoundError();
146
+ if (model.raw.afterDelete) {
147
+ await runPostCommit("afterDelete", () => model.raw.afterDelete(String(id)));
148
+ }
149
+ await this.writeAuditSafely(adminUser, {
150
+ type: "delete",
151
+ modelName: model.meta.name,
152
+ recordIds: [id],
153
+ });
154
+ }
155
+ async deletePreview(model, adminUser, requestedIds) {
156
+ this.authorize(model, adminUser, "delete");
157
+ validateSelectedIds(requestedIds);
158
+ const { records, ids } = await this.loadSelected(model, adminUser, requestedIds, buildListRecordSelect(model.meta, model));
159
+ const relations = await loadDeletePreviewRelations(model, this.models, this.adapter, adminUser, ids);
160
+ return { records, relations };
161
+ }
162
+ async deleteSelected(model, adminUser, requestedIds) {
163
+ this.authorize(model, adminUser, "list");
164
+ this.authorize(model, adminUser, "delete");
165
+ validateSelectedIds(requestedIds);
166
+ const { ids } = await this.loadSelected(model, adminUser, requestedIds, idSelect(model.meta.idField));
167
+ await assertNoRestrictedRelations(model, this.models, this.adapter, adminUser, ids);
168
+ const scope = await resolveScope(model.raw, adminUser);
169
+ const delegate = this.adapter.resource(model.meta);
170
+ const deletedIds = [];
171
+ for (const id of ids) {
172
+ if (model.raw.beforeDelete)
173
+ await model.raw.beforeDelete(String(id));
174
+ const result = await delegate.deleteMany({ scope, id });
175
+ if (result.count !== 1)
176
+ continue;
177
+ deletedIds.push(id);
178
+ if (model.raw.afterDelete) {
179
+ await runPostCommit("afterDelete", () => model.raw.afterDelete(String(id)));
180
+ }
181
+ }
182
+ if (deletedIds.length > 0) {
183
+ await this.writeAuditSafely(adminUser, {
184
+ type: "delete",
185
+ modelName: model.meta.name,
186
+ recordIds: deletedIds,
187
+ });
188
+ }
189
+ return {
190
+ requestedIds,
191
+ deletedIds,
192
+ partial: deletedIds.length !== ids.length,
193
+ };
194
+ }
195
+ async runAction(model, adminUser, actionName, requestedIds) {
196
+ if (actionName === DELETE_SELECTED_ACTION.name) {
197
+ const result = await this.deleteSelected(model, adminUser, requestedIds);
198
+ return {
199
+ message: result.partial
200
+ ? `Deleted ${result.deletedIds.length} ${pluralRecord(result.deletedIds.length)}; some records changed before deletion.`
201
+ : `Deleted ${result.deletedIds.length} ${pluralRecord(result.deletedIds.length)}.`,
202
+ };
203
+ }
204
+ this.authorize(model, adminUser, "list");
205
+ const action = model.raw.actions?.find((candidate) => candidate.name === actionName);
206
+ if (!action ||
207
+ !hasRegisteredActionPermission(adminUser, model.resolved.permissions, action)) {
208
+ throw new PermissionDeniedError();
209
+ }
210
+ validateSelectedIds(requestedIds);
211
+ const { ids } = await this.loadSelected(model, adminUser, requestedIds, idSelect(model.meta.idField));
212
+ const scope = await resolveScope(model.raw, adminUser);
213
+ const result = await action.handler({
214
+ ids,
215
+ adminUser,
216
+ client: this.adapter.client,
217
+ where: { scope, ids },
218
+ });
219
+ await this.writeAuditSafely(adminUser, {
220
+ type: "action",
221
+ modelName: model.meta.name,
222
+ recordIds: ids,
223
+ metadata: { action: action.name },
224
+ });
225
+ return result;
226
+ }
227
+ authorize(model, adminUser, operation) {
228
+ if (!hasModelPermission(adminUser, model.resolved.permissions, operation)) {
229
+ throw new PermissionDeniedError();
230
+ }
231
+ }
232
+ async validateCreateData(model, adminUser, data) {
233
+ assertRequiredCreateFields(model.meta, model.raw, adminUser, data);
234
+ await assertSelectedRelationsAreVisible(data, model, this.models, this.adapter, adminUser);
235
+ }
236
+ async validateUpdateData(model, adminUser, scope, data) {
237
+ assertScopeFieldsUnchanged(data, scope);
238
+ assertScopeRelationsUnchanged(data, model, scope);
239
+ await assertSelectedRelationsAreVisible(data, model, this.models, this.adapter, adminUser);
240
+ }
241
+ async loadSelected(model, adminUser, requestedIds, select) {
242
+ const scope = await resolveScope(model.raw, adminUser);
243
+ const records = await this.adapter.resource(model.meta).findMany({
244
+ scope,
245
+ filters: {},
246
+ ids: requestedIds,
247
+ select,
248
+ });
249
+ const ids = records
250
+ .map((record) => record[model.meta.idField])
251
+ .filter((id) => typeof id === "string" || typeof id === "number");
252
+ if (ids.length !== requestedIds.length) {
253
+ throw new RequestValidationError("One or more selected records are unavailable.");
254
+ }
255
+ return { records, ids };
256
+ }
257
+ async writeAuditSafely(actor, event) {
258
+ await runPostCommit("audit.write", () => writeAuditEvent(this.audit, actor, event));
259
+ }
260
+ }
261
+ export function validateSelectedIds(ids) {
262
+ if (ids.length === 0) {
263
+ throw new RequestValidationError("Action requests require at least one record ID.");
264
+ }
265
+ if (ids.length > MAX_ACTION_RECORDS) {
266
+ throw new RequestValidationError(`Actions can target at most ${MAX_ACTION_RECORDS} records at once.`);
267
+ }
268
+ if (new Set(ids).size !== ids.length) {
269
+ throw new RequestValidationError("Action record IDs must be unique.");
270
+ }
271
+ }
272
+ function assertPagination(page, perPage) {
273
+ if (!Number.isSafeInteger(page) || page < 1) {
274
+ throw new RequestValidationError("Page must be a positive integer.");
275
+ }
276
+ if (!Number.isSafeInteger(perPage) || perPage < 1 || perPage > 200) {
277
+ throw new RequestValidationError("Records per page must be an integer from 1 to 200.");
278
+ }
279
+ }
280
+ function pluralRecord(count) {
281
+ return count === 1 ? "record" : "records";
282
+ }
283
+ async function runPostCommit(name, task) {
284
+ try {
285
+ await task();
286
+ }
287
+ catch {
288
+ console.error(`[paneljs] ${name} failed after the database write committed.`);
289
+ }
290
+ }
291
+ function assertScopeRelationsUnchanged(data, model, scope) {
292
+ const referencedNames = collectScopeFieldNames(scope);
293
+ for (const field of model.meta.fields) {
294
+ const relation = field.relation;
295
+ if (field.type !== "relation" ||
296
+ relation?.kind !== "belongsTo" ||
297
+ !referencedNames.has(field.name)) {
298
+ continue;
299
+ }
300
+ for (const foreignKeyField of relation.foreignKeyFields) {
301
+ if (data[foreignKeyField] !== undefined) {
302
+ throw new RequestValidationError(`Field "${foreignKeyField}" is controlled by the configured scope and cannot be updated through the admin.`);
303
+ }
304
+ }
305
+ }
306
+ }
307
+ //# sourceMappingURL=adminService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adminService.js","sourceRoot":"","sources":["../src/adminService.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EACL,2BAA2B,EAC3B,0BAA0B,GAE3B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EACL,kBAAkB,EAClB,6BAA6B,GAE9B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAOtC,OAAO,EACL,iCAAiC,EACjC,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,gBAAgB,EAChB,0BAA0B,EAC1B,sBAAsB,EACtB,YAAY,GACb,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,0BAA0B,EAC1B,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,kBAAkB,GAAG,GAAG,CAAC;AA2B/B,4EAA4E;AAC5E,MAAM,OAAO,YAAY;IACN,MAAM,CAAmC;IACzC,OAAO,CAAc;IACrB,KAAK,CAA0B;IAEhD,YAAY,OAA4B;QACtC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,IAAI,CACR,KAA0B,EAC1B,SAAoB,EACpB,QAAwB,EAAE;QAE1B,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QACzC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;QAC7B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC;QACxD,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAChC,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACnD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC;QACtD,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACzC,QAAQ,CAAC,QAAQ,CAAC;gBAChB,KAAK;gBACL,OAAO;gBACP,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,IAAI;gBACJ,IAAI,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,OAAO;gBAC1B,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,qBAAqB,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC;aACjD,CAAC;YACF,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;SACzD,CAAC,CAAC;QAEH,OAAO;YACL,OAAO;YACP,KAAK;YACL,IAAI;YACJ,OAAO;YACP,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC;SACvC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,GAAG,CACP,KAA0B,EAC1B,SAAoB,EACpB,EAAmB;QAEnB,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QACzC,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QACvD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC;YAC/D,KAAK;YACL,EAAE;YACF,MAAM,EAAE,iBAAiB,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC;SAC7C,CAAC,CAAC;QACH,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,mBAAmB,EAAE,CAAC;QAC7C,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,MAAM,CACV,KAA0B,EAC1B,SAAoB,EACpB,IAAa;QAEb,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QACvD,IAAI,IAAI,GAAG,gBAAgB,CACzB,oBAAoB,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC,EAC5D,KAAK,CACN,CAAC;QACF,MAAM,IAAI,CAAC,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;QAEtD,IAAI,KAAK,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;YAC3B,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YACpD,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YACpD,IAAI,GAAG,mBAAmB,CACxB,KAAK,CAAC,IAAI,EACV,MAAM,CAAC,WAAW,CAChB,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CAC7B,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC,CACjD,CACF,CACF,CAAC;YACF,IAAI,GAAG,gBAAgB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACrC,MAAM,IAAI,CAAC,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;QACxD,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;YAC5D,IAAI;YACJ,MAAM,EAAE,iBAAiB,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC;SAC7C,CAAC,CAAC;QACH,IAAI,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;YAC1B,MAAM,aAAa,CAAC,aAAa,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,WAAY,CAAC,MAAM,CAAC,CAAC,CAAC;QAC3E,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC5C,MAAM,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE;YACrC,IAAI,EAAE,QAAQ;YACd,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI;YAC1B,SAAS,EACP,OAAO,QAAQ,KAAK,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ;gBAC1D,CAAC,CAAC,CAAC,QAAQ,CAAC;gBACZ,CAAC,CAAC,EAAE;SACT,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,MAAM,CACV,KAA0B,EAC1B,SAAoB,EACpB,EAAmB,EACnB,IAAa;QAEb,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACnD,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC;YACxC,KAAK;YACL,EAAE;YACF,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC;SACrC,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,mBAAmB,EAAE,CAAC;QAE/C,IAAI,IAAI,GAAG,oBAAoB,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;QACxE,MAAM,IAAI,CAAC,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QAC7D,IAAI,KAAK,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;YAC3B,IAAI,GAAG,mBAAmB,CACxB,KAAK,CAAC,IAAI,EACV,MAAM,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAC/C,CAAC;YACF,MAAM,IAAI,CAAC,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QAC/D,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9D,IAAI,MAAM,CAAC,KAAK,KAAK,CAAC;YAAE,MAAM,IAAI,mBAAmB,EAAE,CAAC;QACxD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC;YACtC,KAAK;YACL,EAAE;YACF,MAAM,EAAE,iBAAiB,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC;SAC7C,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CACb,6BAA6B,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,0BAA0B,CAC7E,CAAC;QACJ,CAAC;QACD,IAAI,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;YAC1B,MAAM,aAAa,CAAC,aAAa,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,WAAY,CAAC,MAAM,CAAC,CAAC,CAAC;QAC3E,CAAC;QACD,MAAM,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE;YACrC,IAAI,EAAE,QAAQ;YACd,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI;YAC1B,SAAS,EAAE,CAAC,EAAE,CAAC;SAChB,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,MAAM,CACV,KAA0B,EAC1B,SAAoB,EACpB,EAAmB;QAEnB,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC;YACtC,KAAK;YACL,EAAE;YACF,MAAM,EAAE,iBAAiB,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC;SAC7C,CAAC,CAAC;QACH,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,mBAAmB,EAAE,CAAC;QAC7C,IAAI,KAAK,CAAC,GAAG,CAAC,YAAY;YAAE,MAAM,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QACrE,MAAM,2BAA2B,CAC/B,KAAK,EACL,IAAI,CAAC,MAAM,EACX,IAAI,CAAC,OAAO,EACZ,SAAS,EACT,CAAC,EAAE,CAAC,CACL,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;QACxD,IAAI,MAAM,CAAC,KAAK,KAAK,CAAC;YAAE,MAAM,IAAI,mBAAmB,EAAE,CAAC;QACxD,IAAI,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;YAC1B,MAAM,aAAa,CAAC,aAAa,EAAE,GAAG,EAAE,CACtC,KAAK,CAAC,GAAG,CAAC,WAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CACnC,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE;YACrC,IAAI,EAAE,QAAQ;YACd,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI;YAC1B,SAAS,EAAE,CAAC,EAAE,CAAC;SAChB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,aAAa,CACjB,KAA0B,EAC1B,SAAoB,EACpB,YAAoC;QAEpC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QAC3C,mBAAmB,CAAC,YAAY,CAAC,CAAC;QAClC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,IAAI,CAAC,YAAY,CAC9C,KAAK,EACL,SAAS,EACT,YAAY,EACZ,qBAAqB,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CACzC,CAAC;QACF,MAAM,SAAS,GAAG,MAAM,0BAA0B,CAChD,KAAK,EACL,IAAI,CAAC,MAAM,EACX,IAAI,CAAC,OAAO,EACZ,SAAS,EACT,GAAG,CACJ,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,cAAc,CAClB,KAA0B,EAC1B,SAAoB,EACpB,YAAoC;QAEpC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QACzC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QAC3C,mBAAmB,CAAC,YAAY,CAAC,CAAC;QAClC,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,IAAI,CAAC,YAAY,CACrC,KAAK,EACL,SAAS,EACT,YAAY,EACZ,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAC7B,CAAC;QACF,MAAM,2BAA2B,CAC/B,KAAK,EACL,IAAI,CAAC,MAAM,EACX,IAAI,CAAC,OAAO,EACZ,SAAS,EACT,GAAG,CACJ,CAAC;QACF,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACnD,MAAM,UAAU,GAA2B,EAAE,CAAC;QAC9C,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;YACrB,IAAI,KAAK,CAAC,GAAG,CAAC,YAAY;gBAAE,MAAM,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;YACrE,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;YACxD,IAAI,MAAM,CAAC,KAAK,KAAK,CAAC;gBAAE,SAAS;YACjC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACpB,IAAI,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBAC1B,MAAM,aAAa,CAAC,aAAa,EAAE,GAAG,EAAE,CACtC,KAAK,CAAC,GAAG,CAAC,WAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CACnC,CAAC;YACJ,CAAC;QACH,CAAC;QACD,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE;gBACrC,IAAI,EAAE,QAAQ;gBACd,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI;gBAC1B,SAAS,EAAE,UAAU;aACtB,CAAC,CAAC;QACL,CAAC;QACD,OAAO;YACL,YAAY;YACZ,UAAU;YACV,OAAO,EAAE,UAAU,CAAC,MAAM,KAAK,GAAG,CAAC,MAAM;SAC1C,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,SAAS,CACb,KAA0B,EAC1B,SAAoB,EACpB,UAAkB,EAClB,YAAoC;QAEpC,IAAI,UAAU,KAAK,sBAAsB,CAAC,IAAI,EAAE,CAAC;YAC/C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;YACzE,OAAO;gBACL,OAAO,EAAE,MAAM,CAAC,OAAO;oBACrB,CAAC,CAAC,WAAW,MAAM,CAAC,UAAU,CAAC,MAAM,IAAI,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,yCAAyC;oBACxH,CAAC,CAAC,WAAW,MAAM,CAAC,UAAU,CAAC,MAAM,IAAI,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG;aACrF,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CACpC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,UAAU,CAC7C,CAAC;QACF,IACE,CAAC,MAAM;YACP,CAAC,6BAA6B,CAC5B,SAAS,EACT,KAAK,CAAC,QAAQ,CAAC,WAAW,EAC1B,MAAM,CACP,EACD,CAAC;YACD,MAAM,IAAI,qBAAqB,EAAE,CAAC;QACpC,CAAC;QACD,mBAAmB,CAAC,YAAY,CAAC,CAAC;QAClC,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,IAAI,CAAC,YAAY,CACrC,KAAK,EACL,SAAS,EACT,YAAY,EACZ,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAC7B,CAAC;QACF,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QACvD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;YAClC,GAAG;YACH,SAAS;YACT,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;YAC3B,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;SACtB,CAAC,CAAC;QACH,MAAM,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE;YACrC,IAAI,EAAE,QAAQ;YACd,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI;YAC1B,SAAS,EAAE,GAAG;YACd,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE;SAClC,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,SAAS,CACf,KAA0B,EAC1B,SAAoB,EACpB,SAAyB;QAEzB,IAAI,CAAC,kBAAkB,CAAC,SAAS,EAAE,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE,SAAS,CAAC,EAAE,CAAC;YAC1E,MAAM,IAAI,qBAAqB,EAAE,CAAC;QACpC,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,kBAAkB,CAC9B,KAA0B,EAC1B,SAAoB,EACpB,IAA6B;QAE7B,0BAA0B,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;QACnE,MAAM,iCAAiC,CACrC,IAAI,EACJ,KAAK,EACL,IAAI,CAAC,MAAM,EACX,IAAI,CAAC,OAAO,EACZ,SAAS,CACV,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,kBAAkB,CAC9B,KAA0B,EAC1B,SAAoB,EACpB,KAA8B,EAC9B,IAA6B;QAE7B,0BAA0B,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACxC,6BAA6B,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAClD,MAAM,iCAAiC,CACrC,IAAI,EACJ,KAAK,EACL,IAAI,CAAC,MAAM,EACX,IAAI,CAAC,OAAO,EACZ,SAAS,CACV,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,YAAY,CACxB,KAA0B,EAC1B,SAAoB,EACpB,YAAoC,EACpC,MAAmB;QAKnB,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QACvD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC;YAC/D,KAAK;YACL,OAAO,EAAE,EAAE;YACX,GAAG,EAAE,YAAY;YACjB,MAAM;SACP,CAAC,CAAC;QACH,MAAM,GAAG,GAAG,OAAO;aAChB,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;aAC3C,MAAM,CACL,CAAC,EAAE,EAAyB,EAAE,CAC5B,OAAO,EAAE,KAAK,QAAQ,IAAI,OAAO,EAAE,KAAK,QAAQ,CACnD,CAAC;QACJ,IAAI,GAAG,CAAC,MAAM,KAAK,YAAY,CAAC,MAAM,EAAE,CAAC;YACvC,MAAM,IAAI,sBAAsB,CAC9B,+CAA+C,CAChD,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;IAC1B,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAC5B,KAAgB,EAChB,KAA4C;QAE5C,MAAM,aAAa,CAAC,aAAa,EAAE,GAAG,EAAE,CACtC,eAAe,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAC1C,CAAC;IACJ,CAAC;CACF;AAED,MAAM,UAAU,mBAAmB,CAAC,GAA2B;IAC7D,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,sBAAsB,CAC9B,iDAAiD,CAClD,CAAC;IACJ,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,GAAG,kBAAkB,EAAE,CAAC;QACpC,MAAM,IAAI,sBAAsB,CAC9B,8BAA8B,kBAAkB,mBAAmB,CACpE,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,MAAM,EAAE,CAAC;QACrC,MAAM,IAAI,sBAAsB,CAAC,mCAAmC,CAAC,CAAC;IACxE,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAY,EAAE,OAAe;IACrD,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,sBAAsB,CAAC,kCAAkC,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC,IAAI,OAAO,GAAG,GAAG,EAAE,CAAC;QACnE,MAAM,IAAI,sBAAsB,CAC9B,oDAAoD,CACrD,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;AAC5C,CAAC;AAED,KAAK,UAAU,aAAa,CAC1B,IAAY,EACZ,IAAyB;IAEzB,IAAI,CAAC;QACH,MAAM,IAAI,EAAE,CAAC;IACf,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,KAAK,CACX,aAAa,IAAI,6CAA6C,CAC/D,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,6BAA6B,CACpC,IAA6B,EAC7B,KAA0B,EAC1B,KAA8B;IAE9B,MAAM,eAAe,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;IACtD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACtC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;QAChC,IACE,KAAK,CAAC,IAAI,KAAK,UAAU;YACzB,QAAQ,EAAE,IAAI,KAAK,WAAW;YAC9B,CAAC,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAChC,CAAC;YACD,SAAS;QACX,CAAC;QACD,KAAK,MAAM,eAAe,IAAI,QAAQ,CAAC,gBAAgB,EAAE,CAAC;YACxD,IAAI,IAAI,CAAC,eAAe,CAAC,KAAK,SAAS,EAAE,CAAC;gBACxC,MAAM,IAAI,sBAAsB,CAC9B,UAAU,eAAe,kFAAkF,CAC5G,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function argument(argv: string[], name: string): string | undefined;
2
+ export declare function hasFlag(argv: string[], name: string): boolean;
@@ -0,0 +1,16 @@
1
+ export function argument(argv, name) {
2
+ const equals = argv.find((item) => item.startsWith(`${name}=`));
3
+ if (equals)
4
+ return equals.slice(name.length + 1) || undefined;
5
+ const index = argv.indexOf(name);
6
+ if (index < 0)
7
+ return undefined;
8
+ const value = argv[index + 1];
9
+ if (!value || value.startsWith("-"))
10
+ return undefined;
11
+ return value;
12
+ }
13
+ export function hasFlag(argv, name) {
14
+ return argv.includes(name);
15
+ }
16
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,QAAQ,CACtB,IAAc,EACd,IAAY;IAEZ,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;IAChE,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,SAAS,CAAC;IAC9D,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAChC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IAC9B,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IACtD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAc,EAAE,IAAY;IAClD,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { BuiltInAuthConfig } from "../types.js";
2
+ export interface PaneljsCliConfig {
3
+ prisma: object;
4
+ auth: BuiltInAuthConfig;
5
+ }
6
+ export declare function authSchema(identifier: string): string;
7
+ export declare const createSuperuser: (argv: string[]) => Promise<void>;
@@ -0,0 +1,95 @@
1
+ import { resolve } from "node:path";
2
+ import { pathToFileURL } from "node:url";
3
+ import { hashAdminPassword } from "../passwords.js";
4
+ import { argument } from "./args.js";
5
+ import { confirm, hiddenQuestion, question } from "./prompt.js";
6
+ const emailSchema = `enum ExpressAdminRole {
7
+ ADMIN
8
+ SUPER_ADMIN
9
+ }
10
+
11
+ model ExpressAdminUser {
12
+ id String @id @default(cuid())
13
+ email String @unique
14
+ passwordHash String
15
+ role ExpressAdminRole @default(ADMIN)
16
+ isActive Boolean @default(true)
17
+ createdAt DateTime @default(now())
18
+ updatedAt DateTime @updatedAt
19
+ sessions ExpressAdminSession[]
20
+ }
21
+
22
+ model ExpressAdminSession {
23
+ id String @id @default(cuid())
24
+ tokenHash String @unique
25
+ userId String
26
+ user ExpressAdminUser @relation(fields: [userId], references: [id], onDelete: Cascade)
27
+ expiresAt DateTime
28
+ createdAt DateTime @default(now())
29
+
30
+ @@index([userId])
31
+ @@index([expiresAt])
32
+ }`;
33
+ const usernameSchema = emailSchema.replace("email String @unique", "username String @unique");
34
+ export function authSchema(identifier) {
35
+ if (identifier !== "email" && identifier !== "username") {
36
+ throw new Error("auth:schema requires --identifier email or --identifier username");
37
+ }
38
+ return identifier === "email" ? emailSchema : usernameSchema;
39
+ }
40
+ const modelKey = (modelName) => modelName.charAt(0).toLowerCase() + modelName.slice(1);
41
+ const loadConfig = async (configPath) => {
42
+ const module = await import(pathToFileURL(resolve(configPath)).href);
43
+ const config = module.default;
44
+ if (!config?.prisma || config.auth?.mode !== "built-in")
45
+ throw new Error('The config must default-export { prisma, auth: { mode: "built-in", ... } }. TypeORM apps should insert the first operator with hashAdminPassword instead.');
46
+ return config;
47
+ };
48
+ export const createSuperuser = async (argv) => {
49
+ const configPath = argument(argv, "--config");
50
+ if (!configPath)
51
+ throw new Error("createsuperuser requires --config ./paneljs.config.mjs");
52
+ const config = await loadConfig(configPath);
53
+ try {
54
+ const identifierName = config.auth.identifier;
55
+ const identifier = argument(argv, `--${identifierName}`) ??
56
+ (await question(`${identifierName === "email" ? "Email" : "Username"}: `));
57
+ const suppliedPassword = argument(argv, "--password") ?? process.env.EXPRESS_ADMIN_PASSWORD;
58
+ const password = suppliedPassword ?? (await hiddenQuestion("Password: "));
59
+ if (!suppliedPassword) {
60
+ const confirmation = await hiddenQuestion("Confirm password: ");
61
+ if (password !== confirmation)
62
+ throw new Error("Passwords do not match.");
63
+ }
64
+ if (!identifier || identifier.length > 254)
65
+ throw new Error(`A valid ${identifierName} is required.`);
66
+ if (!password)
67
+ throw new Error("Password cannot be empty.");
68
+ if (password.length < 12) {
69
+ const warning = "This password is shorter than the recommended 12 characters. Continue anyway?";
70
+ if (suppliedPassword)
71
+ console.warn(`Warning: ${warning}`);
72
+ else if (!(await confirm(warning, false)))
73
+ throw new Error("Superuser creation cancelled.");
74
+ }
75
+ const delegate = config.prisma[modelKey(config.auth.userModel ?? "ExpressAdminUser")];
76
+ if (!delegate)
77
+ throw new Error(`Prisma client has no delegate for ${config.auth.userModel ?? "ExpressAdminUser"}. Generate your Prisma client after adding the auth schema.`);
78
+ await delegate.create({
79
+ data: {
80
+ [identifierName]: identifier,
81
+ passwordHash: await hashAdminPassword(password),
82
+ role: "SUPER_ADMIN",
83
+ isActive: true,
84
+ },
85
+ });
86
+ console.log(`Superuser created. Sign in at /admin/login with your ${identifierName}.`);
87
+ }
88
+ finally {
89
+ const disconnect = config.prisma
90
+ .$disconnect;
91
+ if (disconnect)
92
+ await disconnect.call(config.prisma);
93
+ }
94
+ };
95
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/cli/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import { type InstallPlan, type PackageManifest } from "./project.js";
2
+ import type { SelectItem } from "./prompt.js";
3
+ import { type FrameworkId, type OrmId, type PackageManager } from "./stack.js";
4
+ export interface InitFlags {
5
+ framework?: string;
6
+ orm?: string;
7
+ pm?: string;
8
+ yes?: boolean;
9
+ dryRun?: boolean;
10
+ help?: boolean;
11
+ }
12
+ export interface InitIo {
13
+ stdout: {
14
+ write(chunk: string): void;
15
+ };
16
+ isTTY: boolean;
17
+ env: NodeJS.ProcessEnv;
18
+ select(label: string, items: SelectItem[]): Promise<string>;
19
+ confirm(label: string): Promise<boolean>;
20
+ install(pm: PackageManager, cwd: string, dependencies: string[], devDependencies: string[]): Promise<void>;
21
+ }
22
+ export declare function parseInitArgs(argv: string[]): InitFlags;
23
+ export declare const INIT_USAGE = "Usage:\n paneljs init [--framework express] [--orm prisma|typeorm] [--pm npm|pnpm|yarn|bun] [--yes] [--dry-run]\n\nAdds PanelJS packages to the app in this directory. Does not rewrite source files.\nFastify, Nest.js, and Drizzle are listed in the prompt but are not selectable yet.";
24
+ export interface ResolvedInit {
25
+ framework: FrameworkId;
26
+ orm: OrmId;
27
+ pm: PackageManager;
28
+ plan: InstallPlan;
29
+ packages: string[];
30
+ }
31
+ export declare function resolveInitPlan(cwd: string, flags: InitFlags, manifest: PackageManifest, env: NodeJS.ProcessEnv, framework: FrameworkId, orm: OrmId): ResolvedInit;
32
+ export declare function runInit(input: {
33
+ cwd: string;
34
+ argv: string[];
35
+ io: InitIo;
36
+ }): Promise<void>;
37
+ export declare function createDefaultInstall(): InitIo["install"];