@hypersonic-js/admin 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/dist/constants.d.ts +6 -0
  3. package/dist/constants.d.ts.map +1 -0
  4. package/dist/constants.js +6 -0
  5. package/dist/constants.js.map +1 -0
  6. package/dist/crud/pagination.d.ts +11 -0
  7. package/dist/crud/pagination.d.ts.map +1 -0
  8. package/dist/crud/pagination.js +31 -0
  9. package/dist/crud/pagination.js.map +1 -0
  10. package/dist/crud/query.d.ts +64 -0
  11. package/dist/crud/query.d.ts.map +1 -0
  12. package/dist/crud/query.js +137 -0
  13. package/dist/crud/query.js.map +1 -0
  14. package/dist/crud/router.d.ts +49 -0
  15. package/dist/crud/router.d.ts.map +1 -0
  16. package/dist/crud/router.js +335 -0
  17. package/dist/crud/router.js.map +1 -0
  18. package/dist/dmmf/fields.d.ts +37 -0
  19. package/dist/dmmf/fields.d.ts.map +1 -0
  20. package/dist/dmmf/fields.js +88 -0
  21. package/dist/dmmf/fields.js.map +1 -0
  22. package/dist/dmmf/parser.d.ts +15 -0
  23. package/dist/dmmf/parser.d.ts.map +1 -0
  24. package/dist/dmmf/parser.js +54 -0
  25. package/dist/dmmf/parser.js.map +1 -0
  26. package/dist/index.d.ts +5 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +4 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/middleware/auth.d.ts +8 -0
  31. package/dist/middleware/auth.d.ts.map +1 -0
  32. package/dist/middleware/auth.js +21 -0
  33. package/dist/middleware/auth.js.map +1 -0
  34. package/dist/mount.d.ts +4 -0
  35. package/dist/mount.d.ts.map +1 -0
  36. package/dist/mount.js +21 -0
  37. package/dist/mount.js.map +1 -0
  38. package/dist/scaffold/index.d.ts +14 -0
  39. package/dist/scaffold/index.d.ts.map +1 -0
  40. package/dist/scaffold/index.js +35 -0
  41. package/dist/scaffold/index.js.map +1 -0
  42. package/dist/scaffold/templates.d.ts +19 -0
  43. package/dist/scaffold/templates.d.ts.map +1 -0
  44. package/dist/scaffold/templates.js +371 -0
  45. package/dist/scaffold/templates.js.map +1 -0
  46. package/dist/templates/Dashboard.tsx +40 -0
  47. package/dist/templates/ModelForm.tsx +288 -0
  48. package/dist/templates/ModelIndex.tsx +142 -0
  49. package/dist/templates/UserCreate.tsx +189 -0
  50. package/dist/types.d.ts +159 -0
  51. package/dist/types.d.ts.map +1 -0
  52. package/dist/types.js +3 -0
  53. package/dist/types.js.map +1 -0
  54. package/package.json +60 -0
  55. package/templates/Dashboard.tsx +40 -0
  56. package/templates/ModelForm.tsx +288 -0
  57. package/templates/ModelIndex.tsx +142 -0
  58. package/templates/UserCreate.tsx +189 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Joaquim Dalton-Pereira
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,6 @@
1
+ export declare const DEFAULT_HIDDEN_MODELS: readonly ["Session", "Account", "Verification"];
2
+ export declare const DEFAULT_PREFIX = "/admin";
3
+ export declare const DEFAULT_PER_PAGE = 20;
4
+ export declare const MAX_PER_PAGE = 100;
5
+ export declare const MAX_RELATED_OPTIONS = 100;
6
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB,iDAAkD,CAAA;AACpF,eAAO,MAAM,cAAc,WAAW,CAAA;AACtC,eAAO,MAAM,gBAAgB,KAAK,CAAA;AAClC,eAAO,MAAM,YAAY,MAAM,CAAA;AAC/B,eAAO,MAAM,mBAAmB,MAAM,CAAA"}
@@ -0,0 +1,6 @@
1
+ export const DEFAULT_HIDDEN_MODELS = ['Session', 'Account', 'Verification'];
2
+ export const DEFAULT_PREFIX = '/admin';
3
+ export const DEFAULT_PER_PAGE = 20;
4
+ export const MAX_PER_PAGE = 100;
5
+ export const MAX_RELATED_OPTIONS = 100;
6
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,cAAc,CAAU,CAAA;AACpF,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAA;AACtC,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAA;AAClC,MAAM,CAAC,MAAM,YAAY,GAAG,GAAG,CAAA;AAC/B,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAA"}
@@ -0,0 +1,11 @@
1
+ import type { AdminPaginationMeta, PaginationParams } from '../types.js';
2
+ /**
3
+ * Parses pagination query parameters from an Express request query object.
4
+ * Applies sensible defaults and clamps values to safe ranges.
5
+ */
6
+ export declare function parsePaginationParams(query: Record<string, unknown>): PaginationParams;
7
+ /**
8
+ * Builds pagination metadata from raw page/perPage/total values.
9
+ */
10
+ export declare function buildPaginationMeta(page: number, perPage: number, total: number): AdminPaginationMeta;
11
+ //# sourceMappingURL=pagination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../../src/crud/pagination.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAGxE;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,gBAAgB,CAkBtF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GACZ,mBAAmB,CAOrB"}
@@ -0,0 +1,31 @@
1
+ import { DEFAULT_PER_PAGE, MAX_PER_PAGE } from '../constants.js';
2
+ /**
3
+ * Parses pagination query parameters from an Express request query object.
4
+ * Applies sensible defaults and clamps values to safe ranges.
5
+ */
6
+ export function parsePaginationParams(query) {
7
+ const rawPage = Number(query['page']);
8
+ const rawPerPage = Number(query['perPage']);
9
+ const page = Number.isFinite(rawPage) && rawPage >= 1 ? Math.floor(rawPage) : 1;
10
+ const perPage = Number.isFinite(rawPerPage) && rawPerPage >= 1
11
+ ? Math.min(Math.floor(rawPerPage), MAX_PER_PAGE)
12
+ : DEFAULT_PER_PAGE;
13
+ return {
14
+ page,
15
+ perPage,
16
+ skip: (page - 1) * perPage,
17
+ take: perPage,
18
+ };
19
+ }
20
+ /**
21
+ * Builds pagination metadata from raw page/perPage/total values.
22
+ */
23
+ export function buildPaginationMeta(page, perPage, total) {
24
+ return {
25
+ page,
26
+ perPage,
27
+ total,
28
+ totalPages: total === 0 ? 0 : Math.ceil(total / perPage),
29
+ };
30
+ }
31
+ //# sourceMappingURL=pagination.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagination.js","sourceRoot":"","sources":["../../src/crud/pagination.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAEhE;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAA8B;IAClE,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAA;IACrC,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAA;IAE3C,MAAM,IAAI,GACR,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAEpE,MAAM,OAAO,GACX,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,UAAU,IAAI,CAAC;QAC5C,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,YAAY,CAAC;QAChD,CAAC,CAAC,gBAAgB,CAAA;IAEtB,OAAO;QACL,IAAI;QACJ,OAAO;QACP,IAAI,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,OAAO;QAC1B,IAAI,EAAE,OAAO;KACd,CAAA;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CACjC,IAAY,EACZ,OAAe,EACf,KAAa;IAEb,OAAO;QACL,IAAI;QACJ,OAAO;QACP,KAAK;QACL,UAAU,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC;KACzD,CAAA;AACH,CAAC"}
@@ -0,0 +1,64 @@
1
+ import type { PrismaClientLike, AdminModelMeta } from '../types.js';
2
+ interface PrismaDelegate {
3
+ findMany(args: object): Promise<unknown[]>;
4
+ findUnique(args: object): Promise<unknown>;
5
+ create(args: object): Promise<unknown>;
6
+ update(args: object): Promise<unknown>;
7
+ delete(args: object): Promise<unknown>;
8
+ count(args: object): Promise<number>;
9
+ }
10
+ /**
11
+ * Looks up the Prisma model delegate by camelCase model name.
12
+ * Throws a descriptive error if the delegate is not found.
13
+ */
14
+ export declare function getDelegate(prisma: PrismaClientLike, modelName: string): PrismaDelegate;
15
+ /**
16
+ * Coerces raw form string values to the correct JS types based on field metadata.
17
+ * Skips fields not present in model.formFields to prevent mass-assignment.
18
+ */
19
+ export declare function coerceData(data: Record<string, unknown>, model: AdminModelMeta): Record<string, unknown>;
20
+ export interface RelatedOption {
21
+ id: unknown;
22
+ label: string;
23
+ }
24
+ /**
25
+ * Fetches a bounded page of related model records and maps them to { id, label }
26
+ * pairs for use in a <select> dropdown on the admin form.
27
+ * The label uses the model's displayField; falls back to the idField value.
28
+ *
29
+ * @param skip Number of records to skip — used for load-more pagination.
30
+ */
31
+ export declare function fetchRelatedOptions(prisma: PrismaClientLike, model: AdminModelMeta, skip?: number): Promise<RelatedOption[]>;
32
+ export interface FindManyResult {
33
+ records: unknown[];
34
+ total: number;
35
+ }
36
+ /** Fetches a paginated list of records and the total count in parallel. */
37
+ export declare function findMany(prisma: PrismaClientLike, model: AdminModelMeta, pagination: {
38
+ skip: number;
39
+ take: number;
40
+ }): Promise<FindManyResult>;
41
+ /** Returns only the record count for a model — used on the dashboard. */
42
+ export declare function countRecords(prisma: PrismaClientLike, model: AdminModelMeta): Promise<number>;
43
+ /**
44
+ * Fetches a single record by its primary key.
45
+ * Returns null for non-numeric id strings on number-type models instead of
46
+ * passing NaN to Prisma.
47
+ */
48
+ export declare function findUnique(prisma: PrismaClientLike, model: AdminModelMeta, id: string): Promise<unknown>;
49
+ /** Creates a new record, coercing form values to correct types. */
50
+ export declare function createRecord(prisma: PrismaClientLike, model: AdminModelMeta, data: Record<string, unknown>): Promise<unknown>;
51
+ /**
52
+ * Updates an existing record by primary key, coercing form values.
53
+ * Returns null without calling Prisma for non-numeric id strings on number-type
54
+ * models.
55
+ */
56
+ export declare function updateRecord(prisma: PrismaClientLike, model: AdminModelMeta, id: string, data: Record<string, unknown>): Promise<unknown>;
57
+ /**
58
+ * Deletes a record by primary key.
59
+ * Returns early without calling Prisma for non-numeric id strings on number-type
60
+ * models.
61
+ */
62
+ export declare function deleteRecord(prisma: PrismaClientLike, model: AdminModelMeta, id: string): Promise<void>;
63
+ export {};
64
+ //# sourceMappingURL=query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/crud/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAMnE,UAAU,cAAc;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;IAC1C,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC1C,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACtC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACtC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACtC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CACrC;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,GAAG,cAAc,CAYvF;AAWD;;;GAGG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,KAAK,EAAE,cAAc,GACpB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA6BzB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,OAAO,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,gBAAgB,EACxB,KAAK,EAAE,cAAc,EACrB,IAAI,SAAI,GACP,OAAO,CAAC,aAAa,EAAE,CAAC,CAO1B;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,EAAE,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;CACd;AAED,2EAA2E;AAC3E,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,gBAAgB,EACxB,KAAK,EAAE,cAAc,EACrB,UAAU,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzC,OAAO,CAAC,cAAc,CAAC,CAOzB;AAED,yEAAyE;AACzE,wBAAsB,YAAY,CAChC,MAAM,EAAE,gBAAgB,EACxB,KAAK,EAAE,cAAc,GACpB,OAAO,CAAC,MAAM,CAAC,CAGjB;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,gBAAgB,EACxB,KAAK,EAAE,cAAc,EACrB,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,OAAO,CAAC,CAUlB;AAED,mEAAmE;AACnE,wBAAsB,YAAY,CAChC,MAAM,EAAE,gBAAgB,EACxB,KAAK,EAAE,cAAc,EACrB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,OAAO,CAAC,CAGlB;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,gBAAgB,EACxB,KAAK,EAAE,cAAc,EACrB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,OAAO,CAAC,CAUlB;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,gBAAgB,EACxB,KAAK,EAAE,cAAc,EACrB,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,IAAI,CAAC,CAWf"}
@@ -0,0 +1,137 @@
1
+ import { MAX_RELATED_OPTIONS } from '../constants.js';
2
+ /**
3
+ * Looks up the Prisma model delegate by camelCase model name.
4
+ * Throws a descriptive error if the delegate is not found.
5
+ */
6
+ export function getDelegate(prisma, modelName) {
7
+ const key = modelName.charAt(0).toLowerCase() + modelName.slice(1);
8
+ const delegate = prisma[key];
9
+ if (delegate === null || delegate === undefined || typeof delegate !== 'object') {
10
+ throw new Error(`Admin: Prisma delegate not found for model "${modelName}". ` +
11
+ `Ensure the model exists in your schema and the client has been generated.`);
12
+ }
13
+ return delegate;
14
+ }
15
+ /**
16
+ * Parses a URL-segment id string to a number.
17
+ * Returns null for non-numeric strings, preventing NaN from reaching Prisma.
18
+ */
19
+ function parseNumericId(id) {
20
+ const parsed = parseInt(id, 10);
21
+ return isNaN(parsed) ? null : parsed;
22
+ }
23
+ /**
24
+ * Coerces raw form string values to the correct JS types based on field metadata.
25
+ * Skips fields not present in model.formFields to prevent mass-assignment.
26
+ */
27
+ export function coerceData(data, model) {
28
+ const result = {};
29
+ for (const [key, value] of Object.entries(data)) {
30
+ const field = model.formFields.find((f) => f.name === key);
31
+ if (field === undefined)
32
+ continue;
33
+ if (value === '' || value === null || value === undefined) {
34
+ result[key] = field.isRequired ? undefined : null;
35
+ continue;
36
+ }
37
+ switch (field.prismaType) {
38
+ case 'Int':
39
+ case 'Float':
40
+ result[key] = Number(value);
41
+ break;
42
+ case 'Boolean':
43
+ result[key] = value === 'true' || value === true;
44
+ break;
45
+ case 'DateTime':
46
+ result[key] = new Date(String(value));
47
+ break;
48
+ default:
49
+ result[key] = value;
50
+ }
51
+ }
52
+ return result;
53
+ }
54
+ /**
55
+ * Fetches a bounded page of related model records and maps them to { id, label }
56
+ * pairs for use in a <select> dropdown on the admin form.
57
+ * The label uses the model's displayField; falls back to the idField value.
58
+ *
59
+ * @param skip Number of records to skip — used for load-more pagination.
60
+ */
61
+ export async function fetchRelatedOptions(prisma, model, skip = 0) {
62
+ const delegate = getDelegate(prisma, model.name);
63
+ const records = (await delegate.findMany({ take: MAX_RELATED_OPTIONS, skip }));
64
+ return records.map((r) => ({
65
+ id: r[model.idField],
66
+ label: String(r[model.displayField] ?? r[model.idField] ?? ''),
67
+ }));
68
+ }
69
+ /** Fetches a paginated list of records and the total count in parallel. */
70
+ export async function findMany(prisma, model, pagination) {
71
+ const delegate = getDelegate(prisma, model.name);
72
+ const [records, total] = await Promise.all([
73
+ delegate.findMany({ skip: pagination.skip, take: pagination.take }),
74
+ delegate.count({}),
75
+ ]);
76
+ return { records, total };
77
+ }
78
+ /** Returns only the record count for a model — used on the dashboard. */
79
+ export async function countRecords(prisma, model) {
80
+ const delegate = getDelegate(prisma, model.name);
81
+ return delegate.count({});
82
+ }
83
+ /**
84
+ * Fetches a single record by its primary key.
85
+ * Returns null for non-numeric id strings on number-type models instead of
86
+ * passing NaN to Prisma.
87
+ */
88
+ export async function findUnique(prisma, model, id) {
89
+ if (model.idType === 'number') {
90
+ const idValue = parseNumericId(id);
91
+ if (idValue === null)
92
+ return null;
93
+ const delegate = getDelegate(prisma, model.name);
94
+ return delegate.findUnique({ where: { [model.idField]: idValue } });
95
+ }
96
+ const delegate = getDelegate(prisma, model.name);
97
+ return delegate.findUnique({ where: { [model.idField]: id } });
98
+ }
99
+ /** Creates a new record, coercing form values to correct types. */
100
+ export async function createRecord(prisma, model, data) {
101
+ const delegate = getDelegate(prisma, model.name);
102
+ return delegate.create({ data: coerceData(data, model) });
103
+ }
104
+ /**
105
+ * Updates an existing record by primary key, coercing form values.
106
+ * Returns null without calling Prisma for non-numeric id strings on number-type
107
+ * models.
108
+ */
109
+ export async function updateRecord(prisma, model, id, data) {
110
+ if (model.idType === 'number') {
111
+ const idValue = parseNumericId(id);
112
+ if (idValue === null)
113
+ return null;
114
+ const delegate = getDelegate(prisma, model.name);
115
+ return delegate.update({ where: { [model.idField]: idValue }, data: coerceData(data, model) });
116
+ }
117
+ const delegate = getDelegate(prisma, model.name);
118
+ return delegate.update({ where: { [model.idField]: id }, data: coerceData(data, model) });
119
+ }
120
+ /**
121
+ * Deletes a record by primary key.
122
+ * Returns early without calling Prisma for non-numeric id strings on number-type
123
+ * models.
124
+ */
125
+ export async function deleteRecord(prisma, model, id) {
126
+ if (model.idType === 'number') {
127
+ const idValue = parseNumericId(id);
128
+ if (idValue === null)
129
+ return;
130
+ const delegate = getDelegate(prisma, model.name);
131
+ await delegate.delete({ where: { [model.idField]: idValue } });
132
+ return;
133
+ }
134
+ const delegate = getDelegate(prisma, model.name);
135
+ await delegate.delete({ where: { [model.idField]: id } });
136
+ }
137
+ //# sourceMappingURL=query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/crud/query.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import { Router } from 'express';
2
+ import type { PrismaClientLike, AdminModelMeta, LoggerLike, AdminAuthLike } from '../types.js';
3
+ /** Optional configuration for createAdminRouter. */
4
+ export interface AdminRouterOptions {
5
+ /**
6
+ * Full unfiltered model metadata — used when fetching FK dropdown options so
7
+ * hidden models (e.g. User when showAuthModels is false) can still be queried.
8
+ * Defaults to `models`.
9
+ */
10
+ allMeta?: AdminModelMeta[];
11
+ /** Optional structured logger. Pass `app.logger` from createApp(). */
12
+ logger?: LoggerLike;
13
+ /**
14
+ * Better Auth instance. When any of the optional admin methods (`createUser`,
15
+ * `adminUpdateUser`, `removeUser`) are present, the corresponding verb for the
16
+ * user model is routed through the Better Auth admin API instead of Prisma.
17
+ */
18
+ auth?: AdminAuthLike;
19
+ /**
20
+ * Name of the Better Auth user model in your Prisma schema.
21
+ * Defaults to `'User'`.
22
+ */
23
+ betterAuthUserModel?: string;
24
+ }
25
+ /**
26
+ * Builds an Express Router containing all admin CRUD routes.
27
+ * The router should be mounted at the admin prefix via app.use(prefix, authMiddleware, router).
28
+ *
29
+ * @param prisma Prisma client instance.
30
+ * @param models Filtered model list (nav-visible models only).
31
+ * @param prefix Route prefix the router is mounted at.
32
+ * @param options Optional configuration — see AdminRouterOptions.
33
+ *
34
+ * Routes (all relative to mount prefix):
35
+ * GET / -> Admin/Dashboard
36
+ * GET /related-options/:relatedModel -> JSON { options, hasMore } for FK load-more
37
+ * GET /:model -> Admin/ModelIndex (paginated list)
38
+ * GET /:model/new -> Admin/UserCreate (when auth.api.createUser present)
39
+ * Admin/ModelForm (create form, otherwise)
40
+ * GET /:model/:id -> Admin/ModelForm (edit form)
41
+ * POST /:model -> Better Auth createUser (when auth.api.createUser present)
42
+ * create record via Prisma, otherwise
43
+ * PATCH /:model/:id -> Better Auth adminUpdateUser (when auth.api.adminUpdateUser present)
44
+ * update record via Prisma, otherwise
45
+ * DELETE /:model/:id -> Better Auth removeUser (when auth.api.removeUser present)
46
+ * delete record via Prisma, otherwise
47
+ */
48
+ export declare function createAdminRouter(prisma: PrismaClientLike, models: AdminModelMeta[], prefix: string, options?: AdminRouterOptions): Router;
49
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/crud/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAEhC,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AA+B9F,oDAAoD;AACpD,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,OAAO,CAAC,EAAE,cAAc,EAAE,CAAA;IAC1B,sEAAsE;IACtE,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB;;;;OAIG;IACH,IAAI,CAAC,EAAE,aAAa,CAAA;IACpB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC7B;AAuCD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,cAAc,EAAE,EACxB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,kBAAuB,GAC/B,MAAM,CAmSR"}