@paneljs/paneljs 0.1.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 (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +25 -0
  3. package/dist/adapter.d.ts +24 -0
  4. package/dist/adapter.js +2 -0
  5. package/dist/adapter.js.map +1 -0
  6. package/dist/audit.d.ts +5 -0
  7. package/dist/audit.js +11 -0
  8. package/dist/audit.js.map +1 -0
  9. package/dist/authMode.d.ts +2 -0
  10. package/dist/authMode.js +2 -0
  11. package/dist/authMode.js.map +1 -0
  12. package/dist/defaultActions.d.ts +5 -0
  13. package/dist/defaultActions.js +6 -0
  14. package/dist/defaultActions.js.map +1 -0
  15. package/dist/errors.d.ts +18 -0
  16. package/dist/errors.js +36 -0
  17. package/dist/errors.js.map +1 -0
  18. package/dist/ids.d.ts +3 -0
  19. package/dist/ids.js +14 -0
  20. package/dist/ids.js.map +1 -0
  21. package/dist/index.d.ts +27 -0
  22. package/dist/index.js +40 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/listQuery.d.ts +19 -0
  25. package/dist/listQuery.js +106 -0
  26. package/dist/listQuery.js.map +1 -0
  27. package/dist/passwords.d.ts +4 -0
  28. package/dist/passwords.js +57 -0
  29. package/dist/passwords.js.map +1 -0
  30. package/dist/permissions.d.ts +15 -0
  31. package/dist/permissions.js +36 -0
  32. package/dist/permissions.js.map +1 -0
  33. package/dist/recordSelection.d.ts +13 -0
  34. package/dist/recordSelection.js +65 -0
  35. package/dist/recordSelection.js.map +1 -0
  36. package/dist/registry.d.ts +156 -0
  37. package/dist/registry.js +388 -0
  38. package/dist/registry.js.map +1 -0
  39. package/dist/schema.d.ts +8 -0
  40. package/dist/schema.js +59 -0
  41. package/dist/schema.js.map +1 -0
  42. package/dist/scope.d.ts +31 -0
  43. package/dist/scope.js +86 -0
  44. package/dist/scope.js.map +1 -0
  45. package/dist/types.d.ts +189 -0
  46. package/dist/types.js +2 -0
  47. package/dist/types.js.map +1 -0
  48. package/dist/uiPath.d.ts +2 -0
  49. package/dist/uiPath.js +7 -0
  50. package/dist/uiPath.js.map +1 -0
  51. package/dist/validation.d.ts +29 -0
  52. package/dist/validation.js +147 -0
  53. package/dist/validation.js.map +1 -0
  54. package/package.json +54 -0
  55. package/ui/dist/assets/index-ChtweaBk.js +11 -0
  56. package/ui/dist/assets/index-eXfQCDXx.css +1 -0
  57. package/ui/dist/fonts/dm-mono-400.woff2 +0 -0
  58. package/ui/dist/fonts/dm-mono-500.woff2 +0 -0
  59. package/ui/dist/fonts/dm-sans-400.woff2 +0 -0
  60. package/ui/dist/fonts/dm-sans-500.woff2 +0 -0
  61. package/ui/dist/fonts/dm-sans-600.woff2 +0 -0
  62. package/ui/dist/fonts/dm-sans-700.woff2 +0 -0
  63. package/ui/dist/index.html +14 -0
package/dist/schema.js ADDED
@@ -0,0 +1,59 @@
1
+ import { hasModelPermission, hasRegisteredActionPermission } from "./permissions.js";
2
+ import { DELETE_SELECTED_ACTION } from "./defaultActions.js";
3
+ import { isFieldVisible, isFieldWritable } from "./validation.js";
4
+ export function buildSchemaResponse(registry, options) {
5
+ const { adminUser, siteName, basePath, authMode } = options;
6
+ return {
7
+ identity: {
8
+ id: adminUser.id,
9
+ email: adminUser.email,
10
+ role: adminUser.role,
11
+ isSuperAdmin: adminUser.isSuperAdmin,
12
+ },
13
+ authMode,
14
+ siteName,
15
+ basePath,
16
+ models: registry
17
+ .getAll()
18
+ .filter(({ resolved }) => hasModelPermission(adminUser, resolved.permissions, "list"))
19
+ .map(({ meta, resolved, raw }) => {
20
+ const visibleFields = meta.fields
21
+ .filter((field) => isFieldVisible(field, raw))
22
+ .map((field) => ({ ...field, isReadOnly: field.isReadOnly || !isFieldWritable(field, raw, adminUser) }));
23
+ const visibleFieldNames = new Set(visibleFields.map((field) => field.name));
24
+ const canDelete = hasModelPermission(adminUser, resolved.permissions, "delete");
25
+ const customActions = (raw.actions ?? [])
26
+ .filter((action) => hasRegisteredActionPermission(adminUser, resolved.permissions, action))
27
+ .map(({ name, label }) => ({ name, label }));
28
+ const actions = canDelete ? [DELETE_SELECTED_ACTION, ...customActions] : customActions;
29
+ return {
30
+ meta: {
31
+ ...meta,
32
+ fields: visibleFields,
33
+ searchableFields: meta.searchableFields.filter((fieldName) => visibleFieldNames.has(fieldName)),
34
+ filterableFields: meta.filterableFields.filter((fieldName) => visibleFieldNames.has(fieldName)),
35
+ },
36
+ config: {
37
+ listDisplay: resolved.listDisplay.filter((fieldName) => visibleFieldNames.has(fieldName)),
38
+ listFilter: resolved.listFilter.filter((fieldName) => visibleFieldNames.has(fieldName)),
39
+ searchFields: resolved.searchFields.filter((fieldName) => visibleFieldNames.has(fieldName)),
40
+ defaultSort: resolved.defaultSort,
41
+ perPage: resolved.perPage,
42
+ permissions: {
43
+ list: hasModelPermission(adminUser, resolved.permissions, "list"),
44
+ view: hasModelPermission(adminUser, resolved.permissions, "view"),
45
+ create: hasModelPermission(adminUser, resolved.permissions, "create"),
46
+ update: hasModelPermission(adminUser, resolved.permissions, "update"),
47
+ delete: canDelete,
48
+ actions: {
49
+ [DELETE_SELECTED_ACTION.name]: canDelete,
50
+ ...Object.fromEntries((raw.actions ?? []).map((action) => [action.name, hasRegisteredActionPermission(adminUser, resolved.permissions, action)])),
51
+ },
52
+ },
53
+ actions,
54
+ },
55
+ };
56
+ }),
57
+ };
58
+ }
59
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { AdminUser, ModelConfig } from "./types.js";
2
+ /** A Prisma-compatible where fragment returned by a model scope function. */
3
+ export type ScopeFilter = Record<string, unknown>;
4
+ /**
5
+ * Resolve a model's tenant or ownership scope for an authenticated admin.
6
+ *
7
+ * Scope is always returned as a separate `AND` clause. Spreading the scope
8
+ * into an ID condition would allow overlapping keys to overwrite one another.
9
+ */
10
+ export declare function resolveScope(config: ModelConfig, adminUser: AdminUser): Promise<ScopeFilter>;
11
+ /**
12
+ * Combine a scope with a record identifier without allowing either condition
13
+ * to replace the other. Routes should use this for detail, update, and delete
14
+ * lookups—not just list queries.
15
+ */
16
+ export declare function buildScopedRecordWhere(scope: ScopeFilter, idField: string, id: unknown): ScopeFilter;
17
+ /**
18
+ * Apply a simple tenant/ownership scope to newly created data. This prevents
19
+ * callers from creating a record inside another tenant by submitting a
20
+ * different foreign key. Complex Prisma predicates are safe for reads and
21
+ * mutations, but cannot be converted into create data automatically.
22
+ */
23
+ export declare function applyCreateScope(data: Record<string, unknown>, scope: ScopeFilter): Record<string, unknown>;
24
+ /** Prevent a caller from moving a record out of its authorized scope. */
25
+ export declare function assertScopeFieldsUnchanged(data: Record<string, unknown>, scope: ScopeFilter): void;
26
+ /**
27
+ * Return every scalar field named by a Prisma where tree. This deliberately
28
+ * errs on the side of locking too much: an update must never be able to alter
29
+ * a value that a nested AND/OR/NOT predicate uses to establish its scope.
30
+ */
31
+ export declare function collectScopeFieldNames(scope: ScopeFilter): Set<string>;
package/dist/scope.js ADDED
@@ -0,0 +1,86 @@
1
+ import { RequestValidationError } from "./validation.js";
2
+ /**
3
+ * Resolve a model's tenant or ownership scope for an authenticated admin.
4
+ *
5
+ * Scope is always returned as a separate `AND` clause. Spreading the scope
6
+ * into an ID condition would allow overlapping keys to overwrite one another.
7
+ */
8
+ export async function resolveScope(config, adminUser) {
9
+ const scope = (await config.scope?.(adminUser)) ?? {};
10
+ assertNoUndefinedScopeValues(scope);
11
+ return scope;
12
+ }
13
+ function assertNoUndefinedScopeValues(value, path = []) {
14
+ if (value === undefined) {
15
+ const label = path.length ? path.join(".") : "scope";
16
+ throw new RequestValidationError(`Scope field "${label}" resolved to undefined. Return a concrete value or a match-nothing fallback instead.`);
17
+ }
18
+ if (Array.isArray(value)) {
19
+ value.forEach((item, index) => assertNoUndefinedScopeValues(item, [...path, String(index)]));
20
+ return;
21
+ }
22
+ if (value === null || typeof value !== "object")
23
+ return;
24
+ for (const [key, child] of Object.entries(value)) {
25
+ assertNoUndefinedScopeValues(child, [...path, key]);
26
+ }
27
+ }
28
+ /**
29
+ * Combine a scope with a record identifier without allowing either condition
30
+ * to replace the other. Routes should use this for detail, update, and delete
31
+ * lookups—not just list queries.
32
+ */
33
+ export function buildScopedRecordWhere(scope, idField, id) {
34
+ return {
35
+ AND: [scope, { [idField]: id }],
36
+ };
37
+ }
38
+ /**
39
+ * Apply a simple tenant/ownership scope to newly created data. This prevents
40
+ * callers from creating a record inside another tenant by submitting a
41
+ * different foreign key. Complex Prisma predicates are safe for reads and
42
+ * mutations, but cannot be converted into create data automatically.
43
+ */
44
+ export function applyCreateScope(data, scope) {
45
+ const scopedData = { ...data };
46
+ for (const [fieldName, value] of Object.entries(scope)) {
47
+ if (value !== null && typeof value === "object") {
48
+ throw new RequestValidationError(`Cannot apply complex scope field "${fieldName}" when creating a record. Use a simple equality scope or a beforeCreate hook.`);
49
+ }
50
+ if (scopedData[fieldName] !== undefined && scopedData[fieldName] !== value) {
51
+ throw new RequestValidationError(`Create payload conflicts with the configured scope for field "${fieldName}".`);
52
+ }
53
+ scopedData[fieldName] = value;
54
+ }
55
+ return scopedData;
56
+ }
57
+ /** Prevent a caller from moving a record out of its authorized scope. */
58
+ export function assertScopeFieldsUnchanged(data, scope) {
59
+ for (const fieldName of collectScopeFieldNames(scope)) {
60
+ if (data[fieldName] !== undefined) {
61
+ throw new RequestValidationError(`Field "${fieldName}" is controlled by the configured scope and cannot be updated through the admin.`);
62
+ }
63
+ }
64
+ }
65
+ /**
66
+ * Return every scalar field named by a Prisma where tree. This deliberately
67
+ * errs on the side of locking too much: an update must never be able to alter
68
+ * a value that a nested AND/OR/NOT predicate uses to establish its scope.
69
+ */
70
+ export function collectScopeFieldNames(scope) {
71
+ const names = new Set();
72
+ const visit = (value) => {
73
+ if (Array.isArray(value))
74
+ return void value.forEach(visit);
75
+ if (value === null || typeof value !== "object")
76
+ return;
77
+ for (const [key, child] of Object.entries(value)) {
78
+ if (key !== "AND" && key !== "OR" && key !== "NOT")
79
+ names.add(key);
80
+ visit(child);
81
+ }
82
+ };
83
+ visit(scope);
84
+ return names;
85
+ }
86
+ //# sourceMappingURL=scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.js","sourceRoot":"","sources":["../src/scope.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,189 @@
1
+ import type { DataAdapter } from "./adapter.js";
2
+ export type AdminFieldType = "string" | "number" | "boolean" | "datetime" | "json" | "enum" | "relation" | "bytes";
3
+ export type RelationKind = "belongsTo" | "hasMany" | "manyToMany" | "hasOne";
4
+ export interface AdminFieldMeta {
5
+ name: string;
6
+ type: AdminFieldType;
7
+ /** The ORM's native type name, e.g. Prisma "DateTime". */
8
+ nativeType: string;
9
+ isId: boolean;
10
+ isRequired: boolean;
11
+ isUnique: boolean;
12
+ isReadOnly: boolean;
13
+ isList: boolean;
14
+ isFilterable: boolean;
15
+ isSearchable: boolean;
16
+ defaultValue: unknown;
17
+ enumValues?: string[];
18
+ relation?: {
19
+ model: string;
20
+ kind: RelationKind;
21
+ relationName: string;
22
+ foreignKeyFields: string[];
23
+ onDelete?: string | null;
24
+ displayField: string;
25
+ } | null;
26
+ }
27
+ export interface AdminModelMeta {
28
+ name: string;
29
+ pluralName: string;
30
+ /** Property used to select this model on the ORM client. */
31
+ clientKey: string;
32
+ fields: AdminFieldMeta[];
33
+ idField: string;
34
+ displayField: string;
35
+ searchableFields: string[];
36
+ filterableFields: string[];
37
+ timestamps: {
38
+ createdAt?: string;
39
+ updatedAt?: string;
40
+ };
41
+ }
42
+ export interface AdminUser {
43
+ id: string;
44
+ email: string;
45
+ username?: string;
46
+ role: string;
47
+ isSuperAdmin: boolean;
48
+ institutionId?: string;
49
+ tenantId?: string;
50
+ metadata?: Record<string, unknown>;
51
+ }
52
+ export interface AdminAction {
53
+ name: string;
54
+ label: string;
55
+ handler: (params: {
56
+ ids: Array<string | number>;
57
+ adminUser: AdminUser;
58
+ client: unknown;
59
+ where: Record<string, unknown>;
60
+ }) => Promise<{
61
+ message: string;
62
+ }>;
63
+ allowedRoles?: string[];
64
+ }
65
+ export interface AdminFieldOverride {
66
+ exclude?: boolean;
67
+ expose?: boolean;
68
+ readOnly?: boolean;
69
+ writeRoles?: string[];
70
+ }
71
+ export interface ModelPermissions {
72
+ list?: string[];
73
+ view?: string[];
74
+ create?: string[];
75
+ update?: string[];
76
+ delete?: string[];
77
+ actions?: Record<string, string[]>;
78
+ }
79
+ export interface ModelConfig {
80
+ listDisplay?: string[];
81
+ listFilter?: string[];
82
+ searchFields?: string[];
83
+ defaultSort?: {
84
+ field: string;
85
+ direction: "asc" | "desc";
86
+ };
87
+ perPage?: number;
88
+ fields?: Record<string, AdminFieldOverride>;
89
+ actions?: AdminAction[];
90
+ permissions?: ModelPermissions;
91
+ scope?: (adminUser: AdminUser) => Promise<Record<string, unknown>>;
92
+ beforeCreate?: (data: Record<string, unknown>) => Promise<Record<string, unknown>>;
93
+ afterCreate?: (record: Record<string, unknown>) => Promise<void>;
94
+ beforeUpdate?: (id: string, data: Record<string, unknown>) => Promise<Record<string, unknown>>;
95
+ afterUpdate?: (record: Record<string, unknown>) => Promise<void>;
96
+ beforeDelete?: (id: string) => Promise<void>;
97
+ afterDelete?: (id: string) => Promise<void>;
98
+ displayField?: string;
99
+ pluralName?: string;
100
+ }
101
+ /** Minimal request shape so core auth types do not import Express. */
102
+ export interface AdminHttpRequest {
103
+ headers: Record<string, string | string[] | undefined>;
104
+ get?(name: string): string | undefined;
105
+ ip?: string;
106
+ }
107
+ export interface ExternalAuthConfig {
108
+ mode?: "external";
109
+ getCurrentUser: (req: AdminHttpRequest) => Promise<AdminUser | null>;
110
+ }
111
+ export interface BuiltInAuthConfig {
112
+ mode: "built-in";
113
+ identifier: "email" | "username";
114
+ userModel?: string;
115
+ sessionModel?: string;
116
+ sessionTtlSeconds?: number;
117
+ secureCookies?: boolean;
118
+ loginRateLimit?: false | {
119
+ windowMs?: number;
120
+ maxAttempts?: number;
121
+ };
122
+ }
123
+ export type AuthConfig = ExternalAuthConfig | BuiltInAuthConfig;
124
+ export interface AdminAuditEvent {
125
+ type: "create" | "update" | "delete" | "action";
126
+ modelName: string;
127
+ recordIds: Array<string | number>;
128
+ actor: {
129
+ id: string;
130
+ email: string;
131
+ role: string;
132
+ };
133
+ timestamp: Date;
134
+ metadata?: Record<string, string | number | boolean | null>;
135
+ }
136
+ export interface AuditConfig {
137
+ write(event: AdminAuditEvent): Promise<void>;
138
+ }
139
+ export interface AdminConfig {
140
+ adapter: DataAdapter;
141
+ auth: AuthConfig;
142
+ audit?: AuditConfig;
143
+ basePath?: string;
144
+ siteName?: string;
145
+ databaseProvider?: "postgresql" | "mysql" | "sqlite" | "sqlserver" | "mongodb";
146
+ }
147
+ export interface PaginatedResponse<T> {
148
+ records: T[];
149
+ total: number;
150
+ page: number;
151
+ perPage: number;
152
+ totalPages: number;
153
+ }
154
+ export interface SchemaResponse {
155
+ identity: {
156
+ id: string;
157
+ email: string;
158
+ role: string;
159
+ isSuperAdmin: boolean;
160
+ };
161
+ authMode: "built-in" | "external";
162
+ models: Array<{
163
+ meta: AdminModelMeta;
164
+ config: {
165
+ listDisplay: string[];
166
+ listFilter: string[];
167
+ searchFields: string[];
168
+ defaultSort: {
169
+ field: string;
170
+ direction: "asc" | "desc";
171
+ };
172
+ perPage: number;
173
+ permissions: {
174
+ list: boolean;
175
+ view: boolean;
176
+ create: boolean;
177
+ update: boolean;
178
+ delete: boolean;
179
+ actions: Record<string, boolean>;
180
+ };
181
+ actions: Array<{
182
+ name: string;
183
+ label: string;
184
+ }>;
185
+ };
186
+ }>;
187
+ siteName: string;
188
+ basePath: string;
189
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ /** Absolute path to the pre-built admin SPA (`ui/dist`). */
2
+ export declare function getAdminUiDist(): string;
package/dist/uiPath.js ADDED
@@ -0,0 +1,7 @@
1
+ import { dirname, resolve } from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ /** Absolute path to the pre-built admin SPA (`ui/dist`). */
4
+ export function getAdminUiDist() {
5
+ return resolve(dirname(fileURLToPath(import.meta.url)), "../ui/dist");
6
+ }
7
+ //# sourceMappingURL=uiPath.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uiPath.js","sourceRoot":"","sources":["../src/uiPath.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,4DAA4D;AAC5D,MAAM,UAAU,cAAc;IAC3B,OAAO,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;AACzE,CAAC"}
@@ -0,0 +1,29 @@
1
+ import type { AdminFieldMeta, AdminModelMeta, AdminUser, ModelConfig } from "./types.js";
2
+ import { AdminApiError } from "./errors.js";
3
+ /** An error a route can safely return to an API caller. */
4
+ export declare class RequestValidationError extends AdminApiError {
5
+ constructor(message: string);
6
+ }
7
+ /**
8
+ * Conservative name matching for data that should not appear in an admin by
9
+ * accident. Developers can make a deliberate exception with `{ expose: true }`.
10
+ */
11
+ export declare function isSensitiveFieldName(name: string): boolean;
12
+ /** True when a field may be included in the admin's schema or a form. */
13
+ export declare function isFieldVisible(field: AdminFieldMeta, config: ModelConfig): boolean;
14
+ /**
15
+ * Return the scalar fields the admin may accept on create or update. Relation
16
+ * writes are intentionally excluded from the first CRUD release.
17
+ */
18
+ export declare function getWritableFields(meta: AdminModelMeta, config: ModelConfig): AdminFieldMeta[];
19
+ /** True when the current administrator can modify this otherwise writable field. */
20
+ export declare function isFieldWritable(field: AdminFieldMeta, config: ModelConfig, adminUser: AdminUser): boolean;
21
+ /** Ensure Prisma never turns a user omission into an opaque 500 response. */
22
+ export declare function assertRequiredCreateFields(meta: AdminModelMeta, config: ModelConfig, adminUser: AdminUser, data: Record<string, unknown>): void;
23
+ /** Validate trusted hook output without applying caller permissions or visibility. */
24
+ export declare function validateHookPayload(meta: AdminModelMeta, body: unknown): Record<string, unknown>;
25
+ /**
26
+ * Reject unknown, hidden, read-only, and incorrectly typed write properties.
27
+ * The returned object is safe to pass to the scalar-only Prisma CRUD layer.
28
+ */
29
+ export declare function validateWritePayload(meta: AdminModelMeta, config: ModelConfig, adminUser: AdminUser, body: unknown): Record<string, unknown>;
@@ -0,0 +1,147 @@
1
+ import { AdminApiError } from "./errors.js";
2
+ // ============================================================
3
+ // REQUEST VALIDATION
4
+ // ============================================================
5
+ /** An error a route can safely return to an API caller. */
6
+ export class RequestValidationError extends AdminApiError {
7
+ constructor(message) {
8
+ super(400, "VALIDATION_ERROR", message);
9
+ this.name = "RequestValidationError";
10
+ }
11
+ }
12
+ /**
13
+ * Conservative name matching for data that should not appear in an admin by
14
+ * accident. Developers can make a deliberate exception with `{ expose: true }`.
15
+ */
16
+ export function isSensitiveFieldName(name) {
17
+ return /password|token|secret|api[_-]?key|credential|private[_-]?key/i.test(name);
18
+ }
19
+ /** True when a field may be included in the admin's schema or a form. */
20
+ export function isFieldVisible(field, config) {
21
+ const override = config.fields?.[field.name];
22
+ if (override?.exclude)
23
+ return false;
24
+ if (isSensitiveFieldName(field.name))
25
+ return override?.expose === true;
26
+ return true;
27
+ }
28
+ /**
29
+ * Return the scalar fields the admin may accept on create or update. Relation
30
+ * writes are intentionally excluded from the first CRUD release.
31
+ */
32
+ export function getWritableFields(meta, config) {
33
+ return meta.fields.filter((field) => isFieldWritableByConfiguration(field, config));
34
+ }
35
+ function isFieldWritableByConfiguration(field, config) {
36
+ const override = config.fields?.[field.name];
37
+ return isFieldVisible(field, config) && field.type !== "relation" && !field.isList && !field.isReadOnly && !override?.readOnly;
38
+ }
39
+ /** True when the current administrator can modify this otherwise writable field. */
40
+ export function isFieldWritable(field, config, adminUser) {
41
+ if (!isFieldWritableByConfiguration(field, config))
42
+ return false;
43
+ const writeRoles = config.fields?.[field.name]?.writeRoles;
44
+ if (adminUser.isSuperAdmin)
45
+ return true;
46
+ if (writeRoles !== undefined)
47
+ return writeRoles.includes(adminUser.role);
48
+ return !/^(role|isactive|issuperadmin)$/i.test(field.name);
49
+ }
50
+ function isPlainObject(value) {
51
+ return typeof value === "object" && value !== null && !Array.isArray(value);
52
+ }
53
+ function assertFieldValue(field, value) {
54
+ if (value === null) {
55
+ if (field.isRequired)
56
+ throw new RequestValidationError(`Field "${field.name}" cannot be null.`);
57
+ return;
58
+ }
59
+ switch (field.type) {
60
+ case "string":
61
+ case "bytes":
62
+ if (typeof value !== "string")
63
+ throw new RequestValidationError(`Field "${field.name}" must be a string.`);
64
+ return;
65
+ case "number":
66
+ if (field.nativeType === "Decimal" || field.nativeType === "BigInt") {
67
+ if (typeof value !== "string" || !/^-?\d+(?:\.\d+)?$/.test(value))
68
+ throw new RequestValidationError(`Field "${field.name}" must be a decimal string.`);
69
+ return;
70
+ }
71
+ if (typeof value !== "number" || !Number.isFinite(value))
72
+ throw new RequestValidationError(`Field "${field.name}" must be a finite number.`);
73
+ return;
74
+ case "boolean":
75
+ if (typeof value !== "boolean")
76
+ throw new RequestValidationError(`Field "${field.name}" must be a boolean.`);
77
+ return;
78
+ case "datetime":
79
+ if (typeof value !== "string" || Number.isNaN(Date.parse(value)))
80
+ throw new RequestValidationError(`Field "${field.name}" must be an ISO date-time string.`);
81
+ return;
82
+ case "enum":
83
+ if (typeof value !== "string" || !field.enumValues?.includes(value))
84
+ throw new RequestValidationError(`Field "${field.name}" must be a valid ${field.nativeType} value.`);
85
+ return;
86
+ case "json":
87
+ if (!isJsonValue(value))
88
+ throw new RequestValidationError(`Field "${field.name}" must be valid JSON data.`);
89
+ return;
90
+ case "relation":
91
+ throw new RequestValidationError(`Relation field "${field.name}" is not supported for writes yet.`);
92
+ }
93
+ }
94
+ function isJsonValue(value, depth = 0) {
95
+ if (depth > 20)
96
+ return false;
97
+ if (value === null || typeof value === "string" || typeof value === "boolean")
98
+ return true;
99
+ if (typeof value === "number")
100
+ return Number.isFinite(value);
101
+ if (Array.isArray(value))
102
+ return value.length <= 1_000 && value.every((entry) => isJsonValue(entry, depth + 1));
103
+ return isPlainObject(value) && Object.keys(value).length <= 1_000 && Object.values(value).every((entry) => isJsonValue(entry, depth + 1));
104
+ }
105
+ /** Ensure Prisma never turns a user omission into an opaque 500 response. */
106
+ export function assertRequiredCreateFields(meta, config, adminUser, data) {
107
+ for (const field of meta.fields) {
108
+ if (field.type === "relation" || field.isList || field.isReadOnly || !field.isRequired || field.defaultValue !== null)
109
+ continue;
110
+ if (data[field.name] === undefined)
111
+ throw new RequestValidationError(`Field "${field.name}" is required.`);
112
+ }
113
+ }
114
+ /** Validate trusted hook output without applying caller permissions or visibility. */
115
+ export function validateHookPayload(meta, body) {
116
+ if (!isPlainObject(body))
117
+ throw new RequestValidationError("Hook output must be a JSON object.");
118
+ const fieldsByName = new Map(meta.fields.filter((field) => field.type !== "relation" && !field.isList && !field.isReadOnly).map((field) => [field.name, field]));
119
+ const data = {};
120
+ for (const [fieldName, value] of Object.entries(body)) {
121
+ const field = fieldsByName.get(fieldName);
122
+ if (!field)
123
+ throw new RequestValidationError(`Hook output field "${fieldName}" cannot be written.`);
124
+ assertFieldValue(field, value);
125
+ data[fieldName] = value;
126
+ }
127
+ return data;
128
+ }
129
+ /**
130
+ * Reject unknown, hidden, read-only, and incorrectly typed write properties.
131
+ * The returned object is safe to pass to the scalar-only Prisma CRUD layer.
132
+ */
133
+ export function validateWritePayload(meta, config, adminUser, body) {
134
+ if (!isPlainObject(body))
135
+ throw new RequestValidationError("Request body must be a JSON object.");
136
+ const writableByName = new Map(getWritableFields(meta, config).filter((field) => isFieldWritable(field, config, adminUser)).map((field) => [field.name, field]));
137
+ const data = {};
138
+ for (const [fieldName, value] of Object.entries(body)) {
139
+ const field = writableByName.get(fieldName);
140
+ if (!field)
141
+ throw new RequestValidationError(`Field "${fieldName}" cannot be written through the admin.`);
142
+ assertFieldValue(field, value);
143
+ data[fieldName] = value;
144
+ }
145
+ return data;
146
+ }
147
+ //# sourceMappingURL=validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@paneljs/paneljs",
3
+ "version": "0.1.0",
4
+ "description": "Schema-driven admin panel core. Register models, serve a guarded operations UI.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "author": "Divinesta",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/divinesta/paneljs.git",
11
+ "directory": "packages/paneljs"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/divinesta/paneljs/issues"
15
+ },
16
+ "homepage": "https://github.com/divinesta/paneljs",
17
+ "keywords": [
18
+ "admin",
19
+ "admin-panel",
20
+ "express",
21
+ "prisma",
22
+ "crud"
23
+ ],
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "import": "./dist/index.js"
28
+ }
29
+ },
30
+ "main": "./dist/index.js",
31
+ "types": "./dist/index.d.ts",
32
+ "files": [
33
+ "dist",
34
+ "ui/dist",
35
+ "README.md",
36
+ "LICENSE"
37
+ ],
38
+ "engines": {
39
+ "node": "^20.19 || ^22.12 || >=24.0"
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "^22.15.3",
46
+ "typescript": "5.9.2"
47
+ },
48
+ "scripts": {
49
+ "build": "pnpm --dir ui build && tsc -p tsconfig.build.json",
50
+ "dev": "pnpm --dir ui dev",
51
+ "lint": "echo \"paneljs: no lint yet\"",
52
+ "check-types": "tsc --noEmit -p tsconfig.json"
53
+ }
54
+ }