sveltekit-admin 0.2.1 → 0.5.3

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.
Files changed (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +80 -2
  3. package/dist/index.d.ts +2 -7
  4. package/dist/index.js +2 -13
  5. package/dist/server/auth.d.ts +7 -0
  6. package/dist/server/auth.js +19 -0
  7. package/dist/server/data.d.ts +38 -0
  8. package/dist/server/data.js +116 -0
  9. package/dist/server/handler.d.ts +123 -4
  10. package/dist/server/handler.js +600 -819
  11. package/dist/server/introspection/parser.d.ts +19 -12
  12. package/dist/server/introspection/parser.js +71 -61
  13. package/dist/server/introspection/relations.d.ts +49 -0
  14. package/dist/server/introspection/relations.js +128 -0
  15. package/dist/server/query/filterDetection.d.ts +71 -0
  16. package/dist/server/query/filterDetection.js +153 -0
  17. package/dist/server/query/listQuery.d.ts +92 -0
  18. package/dist/server/query/listQuery.js +442 -0
  19. package/dist/server/query/urls.d.ts +33 -0
  20. package/dist/server/query/urls.js +58 -0
  21. package/dist/server/router.d.ts +6 -0
  22. package/dist/server/router.js +27 -0
  23. package/dist/server/views/Dashboard.svelte +29 -0
  24. package/dist/server/views/Dashboard.svelte.d.ts +15 -0
  25. package/dist/server/views/FieldInput.svelte +63 -0
  26. package/dist/server/views/FieldInput.svelte.d.ts +9 -0
  27. package/dist/server/views/Form.svelte +127 -0
  28. package/dist/server/views/Form.svelte.d.ts +12 -0
  29. package/dist/server/views/Layout.svelte +78 -0
  30. package/dist/server/views/Layout.svelte.d.ts +13 -0
  31. package/dist/server/views/List.svelte +240 -0
  32. package/dist/server/views/List.svelte.d.ts +27 -0
  33. package/dist/server/views/ListFilters.svelte +257 -0
  34. package/dist/server/views/ListFilters.svelte.d.ts +18 -0
  35. package/dist/server/views/ModelCard.svelte +11 -0
  36. package/dist/server/views/ModelCard.svelte.d.ts +8 -0
  37. package/dist/server/views/NotFound.svelte +7 -0
  38. package/dist/server/views/NotFound.svelte.d.ts +7 -0
  39. package/dist/server/views/RelatedBlock.svelte +74 -0
  40. package/dist/server/views/RelatedBlock.svelte.d.ts +11 -0
  41. package/dist/server/views/RelationCheckboxes.svelte +53 -0
  42. package/dist/server/views/RelationCheckboxes.svelte.d.ts +9 -0
  43. package/dist/server/views/RelationSelect.svelte +53 -0
  44. package/dist/server/views/RelationSelect.svelte.d.ts +12 -0
  45. package/dist/server/views/StatCard.svelte +18 -0
  46. package/dist/server/views/StatCard.svelte.d.ts +8 -0
  47. package/dist/server/views/html.d.ts +5 -0
  48. package/dist/server/views/html.js +41 -0
  49. package/dist/server/views/theme.d.ts +1 -0
  50. package/dist/server/views/theme.js +537 -0
  51. package/dist/server/views/types.d.ts +57 -0
  52. package/dist/server/views/types.js +1 -0
  53. package/package.json +24 -26
  54. package/dist/admin.d.ts +0 -227
  55. package/dist/admin.js +0 -369
  56. package/dist/components/AdminForm.svelte +0 -423
  57. package/dist/components/AdminForm.svelte.d.ts +0 -30
  58. package/dist/components/AdminLayout.svelte +0 -328
  59. package/dist/components/AdminLayout.svelte.d.ts +0 -20
  60. package/dist/components/DataTable.svelte +0 -573
  61. package/dist/components/DataTable.svelte.d.ts +0 -25
  62. package/dist/components/index.d.ts +0 -3
  63. package/dist/components/index.js +0 -3
  64. package/dist/server/auth/guard.d.ts +0 -36
  65. package/dist/server/auth/guard.js +0 -38
  66. package/dist/server/auth/index.d.ts +0 -1
  67. package/dist/server/auth/index.js +0 -1
  68. package/dist/server/crud/index.d.ts +0 -1
  69. package/dist/server/crud/index.js +0 -1
  70. package/dist/server/crud/operations.d.ts +0 -87
  71. package/dist/server/crud/operations.js +0 -276
  72. package/dist/server/introspection/index.d.ts +0 -1
  73. package/dist/server/introspection/index.js +0 -1
package/dist/admin.d.ts DELETED
@@ -1,227 +0,0 @@
1
- /**
2
- * SvelteKit Admin - Core Admin Factory
3
- * Creates all the necessary handlers for the admin panel
4
- */
5
- import { type PrismaSchema, type PrismaModel } from './server/introspection/parser.js';
6
- export interface AdminConfig {
7
- /** Prisma client instance */
8
- prisma: any;
9
- /** Path to Prisma schema file */
10
- schemaPath?: string;
11
- /** Parsed schema (alternative to schemaPath) */
12
- schema?: PrismaSchema;
13
- /** Base path for admin (default: /admin) */
14
- basePath?: string;
15
- /** Models to exclude */
16
- exclude?: string[];
17
- /** Per-model configuration */
18
- models?: Record<string, {
19
- hidden?: string[];
20
- readonly?: string[];
21
- listFields?: string[];
22
- label?: string;
23
- }>;
24
- /** Branding options */
25
- branding?: {
26
- title?: string;
27
- logo?: string;
28
- primaryColor?: string;
29
- };
30
- /** Auth check function */
31
- checkAdmin?: (user: unknown) => boolean | Promise<boolean>;
32
- /** Admin role name (if using default check) */
33
- adminRole?: string;
34
- }
35
- export interface AdminContext {
36
- config: AdminConfig;
37
- schema: PrismaSchema;
38
- models: PrismaModel[];
39
- getModel: (name: string) => PrismaModel | undefined;
40
- }
41
- /**
42
- * Create admin context with all necessary data
43
- */
44
- export declare function createAdmin(config: AdminConfig): AdminContext;
45
- /**
46
- * Layout data loader - provides models list and config to layout
47
- */
48
- export declare function createLayoutLoad(ctx: AdminContext): ({ locals }: {
49
- locals: any;
50
- }) => Promise<{
51
- models: {
52
- name: string;
53
- label: string;
54
- }[];
55
- user: {
56
- name: any;
57
- email: any;
58
- } | undefined;
59
- config: {
60
- basePath: string;
61
- branding: {
62
- title: string;
63
- logo: string | undefined;
64
- primaryColor: string;
65
- };
66
- };
67
- }>;
68
- /**
69
- * Dashboard data loader - provides model counts
70
- */
71
- export declare function createDashboardLoad(ctx: AdminContext): () => Promise<{
72
- models: {
73
- name: string;
74
- label: string;
75
- count: number;
76
- }[];
77
- stats: {
78
- totalRecords: number;
79
- modelsCount: number;
80
- };
81
- }>;
82
- /**
83
- * Model list data loader
84
- */
85
- export declare function createModelListLoad(ctx: AdminContext): ({ params, url }: {
86
- params: {
87
- model: string;
88
- };
89
- url: URL;
90
- }) => Promise<{
91
- model: {
92
- name: string;
93
- label: string;
94
- fields: {
95
- name: string;
96
- type: string;
97
- label: string;
98
- }[];
99
- primaryKey: string;
100
- };
101
- items: unknown[];
102
- total: number;
103
- page: number;
104
- perPage: number;
105
- orderBy: string;
106
- orderDir: "asc" | "desc";
107
- search: string;
108
- config: {
109
- basePath: string;
110
- hidden: string[];
111
- listFields: string[] | undefined;
112
- };
113
- }>;
114
- /**
115
- * Model create page loader
116
- */
117
- export declare function createModelNewLoad(ctx: AdminContext): ({ params }: {
118
- params: {
119
- model: string;
120
- };
121
- }) => Promise<{
122
- model: {
123
- name: string;
124
- label: string;
125
- fields: {
126
- name: string;
127
- type: string;
128
- required: boolean;
129
- label: string;
130
- }[];
131
- };
132
- config: {
133
- basePath: string;
134
- hidden: string[];
135
- readonly: string[];
136
- };
137
- relationOptions: Record<string, {
138
- id: string | number;
139
- label: string;
140
- }[]>;
141
- }>;
142
- /**
143
- * Model create action
144
- */
145
- export declare function createModelNewAction(ctx: AdminContext): ({ params, request }: {
146
- params: {
147
- model: string;
148
- };
149
- request: Request;
150
- }) => Promise<{
151
- success: boolean;
152
- error?: undefined;
153
- fieldErrors?: undefined;
154
- } | {
155
- success: boolean;
156
- error: any;
157
- fieldErrors: {};
158
- }>;
159
- /**
160
- * Model edit page loader
161
- */
162
- export declare function createModelEditLoad(ctx: AdminContext): ({ params }: {
163
- params: {
164
- model: string;
165
- id: string;
166
- };
167
- }) => Promise<{
168
- model: {
169
- name: string;
170
- label: string;
171
- primaryKey: string;
172
- fields: {
173
- name: string;
174
- type: string;
175
- required: boolean;
176
- label: string;
177
- }[];
178
- };
179
- item: {};
180
- config: {
181
- basePath: string;
182
- hidden: string[];
183
- readonly: string[];
184
- };
185
- relationOptions: Record<string, {
186
- id: string | number;
187
- label: string;
188
- }[]>;
189
- }>;
190
- /**
191
- * Model update action
192
- */
193
- export declare function createModelEditAction(ctx: AdminContext): ({ params, request }: {
194
- params: {
195
- model: string;
196
- id: string;
197
- };
198
- request: Request;
199
- }) => Promise<{
200
- success: boolean;
201
- error?: undefined;
202
- } | {
203
- success: boolean;
204
- error: any;
205
- }>;
206
- /**
207
- * Model delete action
208
- */
209
- export declare function createModelDeleteAction(ctx: AdminContext): ({ params, request }: {
210
- params: {
211
- model: string;
212
- };
213
- request: Request;
214
- }) => Promise<{
215
- success: boolean;
216
- error?: undefined;
217
- } | {
218
- success: boolean;
219
- error: any;
220
- }>;
221
- /**
222
- * Auth guard hook
223
- */
224
- export declare function createAdminGuard(ctx: AdminContext): ({ event, resolve }: {
225
- event: any;
226
- resolve: Function;
227
- }) => Promise<any>;
package/dist/admin.js DELETED
@@ -1,369 +0,0 @@
1
- /**
2
- * SvelteKit Admin - Core Admin Factory
3
- * Creates all the necessary handlers for the admin panel
4
- */
5
- import { parsePrismaSchema, getDisplayFields, getEditableFields, getInputType, fieldToLabel } from './server/introspection/parser.js';
6
- import { createListOperation, createGetOperation, createCreateOperation, createUpdateOperation, createDeleteOperation } from './server/crud/operations.js';
7
- import { defaultAdminCheck } from './server/auth/guard.js';
8
- /**
9
- * Create admin context with all necessary data
10
- */
11
- export function createAdmin(config) {
12
- // Parse schema if not provided
13
- let schema = config.schema;
14
- if (!schema && config.schemaPath) {
15
- schema = parsePrismaSchema(config.schemaPath);
16
- }
17
- if (!schema) {
18
- throw new Error('Either schema or schemaPath must be provided');
19
- }
20
- // Filter excluded models
21
- const exclude = config.exclude || [];
22
- const models = schema.models.filter(m => !exclude.includes(m.name));
23
- return {
24
- config,
25
- schema,
26
- models,
27
- getModel: (name) => models.find(m => m.name.toLowerCase() === name.toLowerCase())
28
- };
29
- }
30
- /**
31
- * Layout data loader - provides models list and config to layout
32
- */
33
- export function createLayoutLoad(ctx) {
34
- return async ({ locals }) => {
35
- const user = locals.user;
36
- return {
37
- models: ctx.models.map(m => ({
38
- name: m.name,
39
- label: ctx.config.models?.[m.name]?.label || m.name
40
- })),
41
- user: user ? { name: user.name, email: user.email } : undefined,
42
- config: {
43
- basePath: ctx.config.basePath || '/admin',
44
- branding: {
45
- title: ctx.config.branding?.title || 'Admin',
46
- logo: ctx.config.branding?.logo,
47
- primaryColor: ctx.config.branding?.primaryColor || '#6366f1'
48
- }
49
- }
50
- };
51
- };
52
- }
53
- /**
54
- * Dashboard data loader - provides model counts
55
- */
56
- export function createDashboardLoad(ctx) {
57
- return async () => {
58
- const modelCounts = await Promise.all(ctx.models.map(async (model) => {
59
- const modelKey = model.name.charAt(0).toLowerCase() + model.name.slice(1);
60
- const prismaModel = ctx.config.prisma[modelKey];
61
- let count = 0;
62
- if (prismaModel?.count) {
63
- try {
64
- count = await prismaModel.count();
65
- }
66
- catch (e) {
67
- // Model might not exist in DB yet
68
- }
69
- }
70
- return {
71
- name: model.name,
72
- label: ctx.config.models?.[model.name]?.label || model.name,
73
- count
74
- };
75
- }));
76
- const totalRecords = modelCounts.reduce((sum, m) => sum + m.count, 0);
77
- return {
78
- models: modelCounts,
79
- stats: {
80
- totalRecords,
81
- modelsCount: ctx.models.length
82
- }
83
- };
84
- };
85
- }
86
- /**
87
- * Model list data loader
88
- */
89
- export function createModelListLoad(ctx) {
90
- return async ({ params, url }) => {
91
- const model = ctx.getModel(params.model);
92
- if (!model) {
93
- throw new Error(`Model ${params.model} not found`);
94
- }
95
- const modelConfig = ctx.config.models?.[model.name] || {};
96
- const hidden = modelConfig.hidden || [];
97
- // Parse query params
98
- const page = parseInt(url.searchParams.get('page') || '1');
99
- const perPage = parseInt(url.searchParams.get('perPage') || '20');
100
- const search = url.searchParams.get('search') || '';
101
- const orderBy = url.searchParams.get('orderBy') || model.primaryKey || 'id';
102
- const orderDir = (url.searchParams.get('orderDir') || 'desc');
103
- // Get display fields
104
- let displayFields = getDisplayFields(model).filter(f => !hidden.includes(f.name));
105
- if (modelConfig.listFields?.length) {
106
- displayFields = displayFields.filter(f => modelConfig.listFields.includes(f.name));
107
- }
108
- // Execute list query
109
- const listOp = createListOperation(model);
110
- const result = await listOp.execute(ctx.config.prisma, {
111
- page,
112
- perPage,
113
- search,
114
- orderBy,
115
- orderDir
116
- });
117
- return {
118
- model: {
119
- name: model.name,
120
- label: modelConfig.label || model.name,
121
- fields: displayFields.map(f => ({
122
- name: f.name,
123
- type: f.type,
124
- label: fieldToLabel(f.name)
125
- })),
126
- primaryKey: model.primaryKey || 'id'
127
- },
128
- items: result.items,
129
- total: result.total,
130
- page: result.page,
131
- perPage: result.perPage,
132
- orderBy,
133
- orderDir,
134
- search,
135
- config: {
136
- basePath: ctx.config.basePath || '/admin',
137
- hidden,
138
- listFields: modelConfig.listFields
139
- }
140
- };
141
- };
142
- }
143
- /**
144
- * Model create page loader
145
- */
146
- export function createModelNewLoad(ctx) {
147
- return async ({ params }) => {
148
- const model = ctx.getModel(params.model);
149
- if (!model) {
150
- throw new Error(`Model ${params.model} not found`);
151
- }
152
- const modelConfig = ctx.config.models?.[model.name] || {};
153
- const hidden = modelConfig.hidden || [];
154
- const readonly = modelConfig.readonly || [];
155
- const editableFields = getEditableFields(model).filter(f => !hidden.includes(f.name));
156
- // Load relation options
157
- const relationOptions = {};
158
- for (const field of editableFields) {
159
- if (field.relation) {
160
- const relatedModel = ctx.getModel(field.relation.model);
161
- if (relatedModel) {
162
- const relKey = field.relation.model.charAt(0).toLowerCase() + field.relation.model.slice(1);
163
- const relPrisma = ctx.config.prisma[relKey];
164
- if (relPrisma?.findMany) {
165
- try {
166
- const items = await relPrisma.findMany({ take: 100 });
167
- relationOptions[field.name] = items.map((item) => ({
168
- id: item.id,
169
- label: item.name || item.title || item.email || String(item.id)
170
- }));
171
- }
172
- catch (e) {
173
- // Ignore errors
174
- }
175
- }
176
- }
177
- }
178
- }
179
- return {
180
- model: {
181
- name: model.name,
182
- label: modelConfig.label || model.name,
183
- fields: editableFields.map(f => ({
184
- name: f.name,
185
- type: f.type,
186
- required: f.isRequired && !f.hasDefault,
187
- label: fieldToLabel(f.name)
188
- }))
189
- },
190
- config: {
191
- basePath: ctx.config.basePath || '/admin',
192
- hidden,
193
- readonly
194
- },
195
- relationOptions
196
- };
197
- };
198
- }
199
- /**
200
- * Model create action
201
- */
202
- export function createModelNewAction(ctx) {
203
- return async ({ params, request }) => {
204
- const model = ctx.getModel(params.model);
205
- if (!model) {
206
- throw new Error(`Model ${params.model} not found`);
207
- }
208
- const formData = await request.formData();
209
- const data = {};
210
- formData.forEach((value, key) => {
211
- data[key] = value;
212
- });
213
- const createOp = createCreateOperation(model);
214
- try {
215
- await createOp.execute(ctx.config.prisma, data);
216
- return { success: true };
217
- }
218
- catch (error) {
219
- return {
220
- success: false,
221
- error: error.message,
222
- fieldErrors: {}
223
- };
224
- }
225
- };
226
- }
227
- /**
228
- * Model edit page loader
229
- */
230
- export function createModelEditLoad(ctx) {
231
- return async ({ params }) => {
232
- const model = ctx.getModel(params.model);
233
- if (!model) {
234
- throw new Error(`Model ${params.model} not found`);
235
- }
236
- const modelConfig = ctx.config.models?.[model.name] || {};
237
- const hidden = modelConfig.hidden || [];
238
- const readonly = modelConfig.readonly || [];
239
- // Get the record
240
- const getOp = createGetOperation(model);
241
- const item = await getOp.execute(ctx.config.prisma, params.id);
242
- if (!item) {
243
- throw new Error(`Record not found`);
244
- }
245
- const allFields = model.fields.filter(f => !hidden.includes(f.name));
246
- // Load relation options for editable relation fields
247
- const relationOptions = {};
248
- for (const field of allFields) {
249
- if (field.relation && !field.isList) {
250
- const relatedModel = ctx.getModel(field.relation.model);
251
- if (relatedModel) {
252
- const relKey = field.relation.model.charAt(0).toLowerCase() + field.relation.model.slice(1);
253
- const relPrisma = ctx.config.prisma[relKey];
254
- if (relPrisma?.findMany) {
255
- try {
256
- const items = await relPrisma.findMany({ take: 100 });
257
- relationOptions[field.name] = items.map((item) => ({
258
- id: item.id,
259
- label: item.name || item.title || item.email || String(item.id)
260
- }));
261
- }
262
- catch (e) {
263
- // Ignore errors
264
- }
265
- }
266
- }
267
- }
268
- }
269
- return {
270
- model: {
271
- name: model.name,
272
- label: modelConfig.label || model.name,
273
- primaryKey: model.primaryKey || 'id',
274
- fields: allFields.map(f => ({
275
- name: f.name,
276
- type: f.type,
277
- required: f.isRequired,
278
- label: fieldToLabel(f.name)
279
- }))
280
- },
281
- item,
282
- config: {
283
- basePath: ctx.config.basePath || '/admin',
284
- hidden,
285
- readonly
286
- },
287
- relationOptions
288
- };
289
- };
290
- }
291
- /**
292
- * Model update action
293
- */
294
- export function createModelEditAction(ctx) {
295
- return async ({ params, request }) => {
296
- const model = ctx.getModel(params.model);
297
- if (!model) {
298
- throw new Error(`Model ${params.model} not found`);
299
- }
300
- const formData = await request.formData();
301
- const data = {};
302
- formData.forEach((value, key) => {
303
- data[key] = value;
304
- });
305
- const updateOp = createUpdateOperation(model);
306
- try {
307
- await updateOp.execute(ctx.config.prisma, params.id, data);
308
- return { success: true };
309
- }
310
- catch (error) {
311
- return {
312
- success: false,
313
- error: error.message
314
- };
315
- }
316
- };
317
- }
318
- /**
319
- * Model delete action
320
- */
321
- export function createModelDeleteAction(ctx) {
322
- return async ({ params, request }) => {
323
- const model = ctx.getModel(params.model);
324
- if (!model) {
325
- throw new Error(`Model ${params.model} not found`);
326
- }
327
- const formData = await request.formData();
328
- const id = formData.get('id');
329
- if (!id) {
330
- throw new Error('Missing ID');
331
- }
332
- const deleteOp = createDeleteOperation(model);
333
- try {
334
- await deleteOp.execute(ctx.config.prisma, String(id));
335
- return { success: true };
336
- }
337
- catch (error) {
338
- return {
339
- success: false,
340
- error: error.message
341
- };
342
- }
343
- };
344
- }
345
- /**
346
- * Auth guard hook
347
- */
348
- export function createAdminGuard(ctx) {
349
- const basePath = ctx.config.basePath || '/admin';
350
- return async ({ event, resolve }) => {
351
- if (!event.url.pathname.startsWith(basePath)) {
352
- return resolve(event);
353
- }
354
- const user = event.locals.user;
355
- if (!user) {
356
- return new Response(null, {
357
- status: 302,
358
- headers: { Location: '/login' }
359
- });
360
- }
361
- const checkFn = ctx.config.checkAdmin ||
362
- ((u) => defaultAdminCheck(u, ctx.config.adminRole || 'admin'));
363
- const isAdmin = await checkFn(user);
364
- if (!isAdmin) {
365
- return new Response('Forbidden', { status: 403 });
366
- }
367
- return resolve(event);
368
- };
369
- }