@paneljs/paneljs 0.1.0 → 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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,27 @@
1
+ # Changelog
2
+
3
+ ## [0.2.0] - 2026-08-19
4
+
5
+ ORM-agnostic core contract. Adapters speak PanelJS query types; Express and the UI do not.
6
+
7
+ ### Breaking
8
+
9
+ - `ModelResource` methods now take typed PanelJS queries (`FindManyQuery`, `FindFirstQuery`, `CreateQuery`, and the rest), not Prisma-shaped `Record<string, unknown>` args.
10
+ - Custom action `where` is `{ scope, ids }`, not a Prisma `where` object. Combine it yourself when calling Prisma (or use `prismaActionWhere` from `@paneljs/prisma`).
11
+ - `buildListWhere` was removed. List search/filters are fields on `findMany` / `count`.
12
+ - `databaseProvider` on `createAdmin` is ignored. Search folding belongs to the data adapter.
13
+
14
+ ### Added
15
+
16
+ - Query types: `FindManyQuery`, `FindFirstQuery`, `CountQuery`, `CreateQuery`, `UpdateManyQuery`, `DeleteManyQuery`, `FieldSelect`, `FieldFilters`, `SearchQuery`, `ActionWhere`.
17
+ - `AdminAuthStore` plus core login helpers (`loginWithPassword`, `authenticateBuiltInRequest`, `resolveAuthStore`, session cookie helpers).
18
+ - Optional `DataAdapter.createAuthStore` for built-in login.
19
+
20
+ ### Changed
21
+
22
+ - List select builders return `FieldSelect` (`fields` + `relations`), not a Prisma `select`.
23
+ - `parseListQuery` returns PanelJS filters and `{ text, fields }` search only.
24
+
25
+ ## [0.1.0]
26
+
27
+ Initial public release.
package/README.md CHANGED
@@ -1,15 +1,15 @@
1
- # paneljs
1
+ # @paneljs/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/express @paneljs/prisma
8
+ npm install @paneljs/paneljs @paneljs/express @paneljs/prisma
9
9
  ```
10
10
 
11
11
  ```ts
12
- import { createAdmin } from "paneljs";
12
+ import { createAdmin } from "@paneljs/paneljs";
13
13
  import { prismaAdapter } from "@paneljs/prisma";
14
14
  import { mount } from "@paneljs/express";
15
15
 
package/dist/adapter.d.ts CHANGED
@@ -1,14 +1,16 @@
1
+ import type { AdminAuthStore, AuthStoreOptions } from "./authStore.js";
1
2
  import type { AdminModelMeta } from "./types.js";
3
+ import type { CountQuery, CreateQuery, DeleteManyQuery, FindFirstQuery, FindManyQuery, UpdateManyQuery } from "./query.js";
2
4
  /** The subset of CRUD a host ORM must implement for one model. */
3
5
  export interface ModelResource {
4
- findMany(args: Record<string, unknown>): Promise<Record<string, unknown>[]>;
5
- findFirst(args: Record<string, unknown>): Promise<Record<string, unknown> | null>;
6
- count(args: Record<string, unknown>): Promise<number>;
7
- create(args: Record<string, unknown>): Promise<Record<string, unknown>>;
8
- updateMany(args: Record<string, unknown>): Promise<{
6
+ findMany(query: FindManyQuery): Promise<Record<string, unknown>[]>;
7
+ findFirst(query: FindFirstQuery): Promise<Record<string, unknown> | null>;
8
+ count(query: CountQuery): Promise<number>;
9
+ create(query: CreateQuery): Promise<Record<string, unknown>>;
10
+ updateMany(query: UpdateManyQuery): Promise<{
9
11
  count: number;
10
12
  }>;
11
- deleteMany(args: Record<string, unknown>): Promise<{
13
+ deleteMany(query: DeleteManyQuery): Promise<{
12
14
  count: number;
13
15
  }>;
14
16
  }
@@ -21,4 +23,6 @@ export interface DataAdapter {
21
23
  readonly client: unknown;
22
24
  introspect(): Promise<Map<string, AdminModelMeta>>;
23
25
  resource(meta: AdminModelMeta): ModelResource;
26
+ /** Optional. Built-in login uses this when `auth.store` is not set. */
27
+ createAuthStore?(options: AuthStoreOptions): AdminAuthStore;
24
28
  }
@@ -0,0 +1,31 @@
1
+ export declare const DEFAULT_AUTH_USER_MODEL = "ExpressAdminUser";
2
+ export declare const DEFAULT_AUTH_SESSION_MODEL = "ExpressAdminSession";
3
+ export type AuthStoreOptions = {
4
+ identifier: "email" | "username";
5
+ userModel?: string;
6
+ sessionModel?: string;
7
+ };
8
+ export interface BuiltInUserRecord {
9
+ id: string;
10
+ email?: string;
11
+ username?: string;
12
+ passwordHash: string;
13
+ role: string;
14
+ isActive: boolean;
15
+ tenantId?: string;
16
+ }
17
+ export interface BuiltInSessionRecord {
18
+ tokenHash: string;
19
+ expiresAt: Date;
20
+ user: BuiltInUserRecord;
21
+ }
22
+ export interface AdminAuthStore {
23
+ findUserByIdentifier(identifier: string): Promise<BuiltInUserRecord | null>;
24
+ findSessionWithUser(tokenHash: string): Promise<BuiltInSessionRecord | null>;
25
+ createSession(input: {
26
+ tokenHash: string;
27
+ userId: string;
28
+ expiresAt: Date;
29
+ }): Promise<void>;
30
+ deleteSessionByTokenHash(tokenHash: string): Promise<void>;
31
+ }
@@ -0,0 +1,3 @@
1
+ export const DEFAULT_AUTH_USER_MODEL = "ExpressAdminUser";
2
+ export const DEFAULT_AUTH_SESSION_MODEL = "ExpressAdminSession";
3
+ //# sourceMappingURL=authStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authStore.js","sourceRoot":"","sources":["../src/authStore.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,uBAAuB,GAAG,kBAAkB,CAAC;AAC1D,MAAM,CAAC,MAAM,0BAA0B,GAAG,qBAAqB,CAAC"}
@@ -0,0 +1,49 @@
1
+ import type { AdminAuthStore, BuiltInUserRecord } from "./authStore.js";
2
+ import type { AdminHttpRequest, AdminUser, BuiltInAuthConfig } from "./types.js";
3
+ import type { DataAdapter } from "./adapter.js";
4
+ export declare const SESSION_COOKIE_NAME = "paneljs_session";
5
+ export declare const DEFAULT_SESSION_TTL_SECONDS: number;
6
+ export type SessionCookie = {
7
+ name: string;
8
+ value: string;
9
+ path: string;
10
+ httpOnly: true;
11
+ sameSite: "Lax";
12
+ maxAge: number;
13
+ expires: Date;
14
+ secure: boolean;
15
+ };
16
+ export type BuiltInLoginFailure = {
17
+ ok: false;
18
+ status: 401;
19
+ body: {
20
+ error: string;
21
+ code: string;
22
+ };
23
+ };
24
+ export type BuiltInLoginSuccess = {
25
+ ok: true;
26
+ status: 200;
27
+ body: {
28
+ ok: true;
29
+ };
30
+ cookie: SessionCookie;
31
+ };
32
+ export type BuiltInLoginResult = BuiltInLoginFailure | BuiltInLoginSuccess;
33
+ export type BuiltInLogoutResult = {
34
+ status: 204;
35
+ cookie: SessionCookie;
36
+ };
37
+ export declare function hashSessionToken(token: string): string;
38
+ export declare function readSessionToken(req: AdminHttpRequest): string | null;
39
+ export declare function sessionCookie(token: string, config: BuiltInAuthConfig, basePath: string, expires: Date): SessionCookie;
40
+ export declare function clearedSessionCookie(config: BuiltInAuthConfig, basePath: string): SessionCookie;
41
+ export declare function builtInUserToAdminUser(user: BuiltInUserRecord): AdminUser | null;
42
+ export declare function resolveAuthStore(adapter: DataAdapter, auth: BuiltInAuthConfig): AdminAuthStore;
43
+ export declare function authenticateSession(store: AdminAuthStore, token: string | null): Promise<AdminUser | null>;
44
+ export declare function authenticateBuiltInRequest(req: AdminHttpRequest, store: AdminAuthStore): Promise<AdminUser | null>;
45
+ export declare function loginWithPassword(store: AdminAuthStore, config: BuiltInAuthConfig, credentials: {
46
+ identifier: string;
47
+ password: string;
48
+ }, basePath: string): Promise<BuiltInLoginResult>;
49
+ export declare function logoutBuiltIn(store: AdminAuthStore, req: AdminHttpRequest, config: BuiltInAuthConfig, basePath: string): Promise<BuiltInLogoutResult>;
@@ -0,0 +1,117 @@
1
+ import { createHash, randomBytes } from "node:crypto";
2
+ import { verifyLoginPassword } from "./passwords.js";
3
+ export const SESSION_COOKIE_NAME = "paneljs_session";
4
+ export const DEFAULT_SESSION_TTL_SECONDS = 60 * 60 * 24 * 7;
5
+ export function hashSessionToken(token) {
6
+ return createHash("sha256").update(token).digest("hex");
7
+ }
8
+ export function readSessionToken(req) {
9
+ const header = req.headers.cookie;
10
+ if (typeof header !== "string")
11
+ return null;
12
+ for (const part of header.split(";")) {
13
+ const [key, ...value] = part.trim().split("=");
14
+ if (key === SESSION_COOKIE_NAME) {
15
+ try {
16
+ return decodeURIComponent(value.join("="));
17
+ }
18
+ catch {
19
+ return null;
20
+ }
21
+ }
22
+ }
23
+ return null;
24
+ }
25
+ function cookieSecure(config) {
26
+ return config.secureCookies ?? process.env.NODE_ENV === "production";
27
+ }
28
+ export function sessionCookie(token, config, basePath, expires) {
29
+ return {
30
+ name: SESSION_COOKIE_NAME,
31
+ value: token,
32
+ path: basePath,
33
+ httpOnly: true,
34
+ sameSite: "Lax",
35
+ maxAge: Math.max(0, Math.floor((expires.getTime() - Date.now()) / 1000)),
36
+ expires,
37
+ secure: cookieSecure(config),
38
+ };
39
+ }
40
+ export function clearedSessionCookie(config, basePath) {
41
+ return {
42
+ name: SESSION_COOKIE_NAME,
43
+ value: "",
44
+ path: basePath,
45
+ httpOnly: true,
46
+ sameSite: "Lax",
47
+ maxAge: 0,
48
+ expires: new Date(0),
49
+ secure: cookieSecure(config),
50
+ };
51
+ }
52
+ export function builtInUserToAdminUser(user) {
53
+ if (!user.isActive || !["ADMIN", "SUPER_ADMIN"].includes(user.role))
54
+ return null;
55
+ return {
56
+ id: user.id,
57
+ email: user.email ?? user.username ?? user.id,
58
+ ...(user.username ? { username: user.username } : {}),
59
+ ...(user.tenantId ? { tenantId: user.tenantId } : {}),
60
+ role: user.role,
61
+ isSuperAdmin: user.role === "SUPER_ADMIN",
62
+ };
63
+ }
64
+ export function resolveAuthStore(adapter, auth) {
65
+ if (auth.store)
66
+ return auth.store;
67
+ if (adapter.createAuthStore)
68
+ return adapter.createAuthStore(auth);
69
+ throw new Error("[paneljs] Built-in auth requires auth.store or an adapter that implements createAuthStore().");
70
+ }
71
+ export async function authenticateSession(store, token) {
72
+ if (!token)
73
+ return null;
74
+ const session = await store.findSessionWithUser(hashSessionToken(token));
75
+ if (!session)
76
+ return null;
77
+ if (session.expiresAt.getTime() <= Date.now())
78
+ return null;
79
+ return builtInUserToAdminUser(session.user);
80
+ }
81
+ export async function authenticateBuiltInRequest(req, store) {
82
+ return authenticateSession(store, readSessionToken(req));
83
+ }
84
+ const invalidCredentials = {
85
+ ok: false,
86
+ status: 401,
87
+ body: { error: "Invalid credentials.", code: "INVALID_CREDENTIALS" },
88
+ };
89
+ export async function loginWithPassword(store, config, credentials, basePath) {
90
+ const identifier = credentials.identifier.trim();
91
+ const password = credentials.password;
92
+ if (!identifier || !password || identifier.length > 254 || password.length > 1_024) {
93
+ return invalidCredentials;
94
+ }
95
+ const user = await store.findUserByIdentifier(identifier);
96
+ const passwordMatches = await verifyLoginPassword(password, user?.passwordHash);
97
+ const adminUser = user && passwordMatches ? builtInUserToAdminUser(user) : null;
98
+ if (!user || !adminUser)
99
+ return invalidCredentials;
100
+ const token = randomBytes(32).toString("base64url");
101
+ const ttlSeconds = config.sessionTtlSeconds ?? DEFAULT_SESSION_TTL_SECONDS;
102
+ const expiresAt = new Date(Date.now() + ttlSeconds * 1_000);
103
+ await store.createSession({ tokenHash: hashSessionToken(token), userId: user.id, expiresAt });
104
+ return {
105
+ ok: true,
106
+ status: 200,
107
+ body: { ok: true },
108
+ cookie: sessionCookie(token, config, basePath, expiresAt),
109
+ };
110
+ }
111
+ export async function logoutBuiltIn(store, req, config, basePath) {
112
+ const token = readSessionToken(req);
113
+ if (token)
114
+ await store.deleteSessionByTokenHash(hashSessionToken(token));
115
+ return { status: 204, cookie: clearedSessionCookie(config, basePath) };
116
+ }
117
+ //# sourceMappingURL=builtInAuth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builtInAuth.js","sourceRoot":"","sources":["../src/builtInAuth.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -9,6 +9,8 @@ export interface Admin {
9
9
  export declare function createAdmin(config: AdminConfig): Admin;
10
10
  export type { AdminConfig, ModelConfig, AdminUser, AuthConfig, BuiltInAuthConfig, ExternalAuthConfig, AdminHttpRequest, AuditConfig, AdminAuditEvent, AdminFieldMeta, AdminModelMeta, AdminFieldType, RelationKind, ModelPermissions, AdminAction, AdminFieldOverride, PaginatedResponse, SchemaResponse, } from "./types.js";
11
11
  export type { DataAdapter, ModelResource } from "./adapter.js";
12
+ export type { ActionWhere, CountQuery, CreateQuery, DeleteManyQuery, EqualityFilter, FieldFilter, FieldFilters, FieldSelect, FindFirstQuery, FindManyQuery, RelationSelect, SearchQuery, UpdateManyQuery, } from "./query.js";
13
+ export { idSelect, withSelectFields } from "./query.js";
12
14
  export type { ResolvedModelConfig, FullRegisteredModel } from "./registry.js";
13
15
  export { AdminRegistry } from "./registry.js";
14
16
  export { hashAdminPassword, verifyAdminPassword, verifyLoginPassword } from "./passwords.js";
@@ -19,9 +21,14 @@ export type { AdminOperation } from "./permissions.js";
19
21
  export { DELETE_SELECTED_ACTION } from "./defaultActions.js";
20
22
  export { writeAuditEvent } from "./audit.js";
21
23
  export { buildSchemaResponse } from "./schema.js";
22
- export { parseListQuery, buildListWhere } from "./listQuery.js";
24
+ export { parseListQuery } from "./listQuery.js";
25
+ export type { ParsedListQuery } from "./listQuery.js";
23
26
  export { parseRecordId } from "./ids.js";
24
27
  export { buildRecordSelect, buildListRecordSelect, assertSelectedRelationsAreVisible } from "./recordSelection.js";
25
28
  export { applyCreateScope, assertScopeFieldsUnchanged, buildScopedRecordWhere, collectScopeFieldNames, resolveScope } from "./scope.js";
26
29
  export { getAdminUiDist } from "./uiPath.js";
27
30
  export { isBuiltInAuth } from "./authMode.js";
31
+ export type { AdminAuthStore, AuthStoreOptions, BuiltInSessionRecord, BuiltInUserRecord } from "./authStore.js";
32
+ export { DEFAULT_AUTH_SESSION_MODEL, DEFAULT_AUTH_USER_MODEL } from "./authStore.js";
33
+ export { SESSION_COOKIE_NAME, authenticateBuiltInRequest, authenticateSession, clearedSessionCookie, loginWithPassword, logoutBuiltIn, readSessionToken, resolveAuthStore, sessionCookie, } from "./builtInAuth.js";
34
+ export type { BuiltInLoginResult, BuiltInLogoutResult, SessionCookie } from "./builtInAuth.js";
package/dist/index.js CHANGED
@@ -23,6 +23,7 @@ export function createAdmin(config) {
23
23
  };
24
24
  return admin;
25
25
  }
26
+ export { idSelect, withSelectFields } from "./query.js";
26
27
  export { AdminRegistry } from "./registry.js";
27
28
  export { hashAdminPassword, verifyAdminPassword, verifyLoginPassword } from "./passwords.js";
28
29
  export { AdminApiError, AuthenticationError, PermissionDeniedError, ModelNotFoundError, RecordNotFoundError } from "./errors.js";
@@ -31,10 +32,12 @@ export { hasModelPermission, hasActionPermission, hasRegisteredActionPermission
31
32
  export { DELETE_SELECTED_ACTION } from "./defaultActions.js";
32
33
  export { writeAuditEvent } from "./audit.js";
33
34
  export { buildSchemaResponse } from "./schema.js";
34
- export { parseListQuery, buildListWhere } from "./listQuery.js";
35
+ export { parseListQuery } from "./listQuery.js";
35
36
  export { parseRecordId } from "./ids.js";
36
37
  export { buildRecordSelect, buildListRecordSelect, assertSelectedRelationsAreVisible } from "./recordSelection.js";
37
38
  export { applyCreateScope, assertScopeFieldsUnchanged, buildScopedRecordWhere, collectScopeFieldNames, resolveScope } from "./scope.js";
38
39
  export { getAdminUiDist } from "./uiPath.js";
39
40
  export { isBuiltInAuth } from "./authMode.js";
41
+ export { DEFAULT_AUTH_SESSION_MODEL, DEFAULT_AUTH_USER_MODEL } from "./authStore.js";
42
+ export { SESSION_COOKIE_NAME, authenticateBuiltInRequest, authenticateSession, clearedSessionCookie, loginWithPassword, logoutBuiltIn, readSessionToken, resolveAuthStore, sessionCookie, } from "./builtInAuth.js";
40
43
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAS9C,MAAM,UAAU,WAAW,CAAC,MAAmB;IAC5C,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;IACrC,IAAI,WAAW,GAAG,KAAK,CAAC;IAExB,MAAM,KAAK,GAAU;QAClB,QAAQ,CAAC,SAAiB,EAAE,cAA2B,EAAE;YACtD,QAAQ,CAAC,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;YAC1C,OAAO,KAAK,CAAC;QAChB,CAAC;QACD,KAAK,CAAC,UAAU;YACb,IAAI,WAAW;gBAAE,OAAO;YACxB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;YACjD,MAAM,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YAClC,WAAW,GAAG,IAAI,CAAC;QACtB,CAAC;QACD,IAAI,QAAQ;YACT,OAAO,QAAQ,CAAC;QACnB,CAAC;QACD,IAAI,MAAM;YACP,OAAO,MAAM,CAAC;QACjB,CAAC;KACH,CAAC;IAEF,OAAO,KAAK,CAAC;AAChB,CAAC;AAwBD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC7F,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACjI,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,eAAe,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AACvL,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,6BAA6B,EAAE,MAAM,kBAAkB,CAAC;AAE1G,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,iCAAiC,EAAE,MAAM,sBAAsB,CAAC;AACnH,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACxI,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAS9C,MAAM,UAAU,WAAW,CAAC,MAAmB;IAC5C,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;IACrC,IAAI,WAAW,GAAG,KAAK,CAAC;IAExB,MAAM,KAAK,GAAU;QAClB,QAAQ,CAAC,SAAiB,EAAE,cAA2B,EAAE;YACtD,QAAQ,CAAC,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;YAC1C,OAAO,KAAK,CAAC;QAChB,CAAC;QACD,KAAK,CAAC,UAAU;YACb,IAAI,WAAW;gBAAE,OAAO;YACxB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;YACjD,MAAM,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YAClC,WAAW,GAAG,IAAI,CAAC;QACtB,CAAC;QACD,IAAI,QAAQ;YACT,OAAO,QAAQ,CAAC;QACnB,CAAC;QACD,IAAI,MAAM;YACP,OAAO,MAAM,CAAC;QACjB,CAAC;KACH,CAAC;IAEF,OAAO,KAAK,CAAC;AAChB,CAAC;AAsCD,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAExD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC7F,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACjI,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,eAAe,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AACvL,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,6BAA6B,EAAE,MAAM,kBAAkB,CAAC;AAE1G,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,iCAAiC,EAAE,MAAM,sBAAsB,CAAC;AACnH,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACxI,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,OAAO,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AACrF,OAAO,EACJ,mBAAmB,EACnB,0BAA0B,EAC1B,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,GACf,MAAM,kBAAkB,CAAC"}
@@ -1,19 +1,13 @@
1
1
  import type { FullRegisteredModel } from "./registry.js";
2
+ import type { FieldFilters, SearchQuery } from "./query.js";
2
3
  import type { AdminModelMeta } from "./types.js";
3
4
  export type QueryValue = string | string[] | undefined;
4
5
  export type QueryMap = Record<string, QueryValue>;
5
- export declare function buildListWhere(scope: Record<string, unknown>, filters: Record<string, unknown>, search: Record<string, unknown> | undefined): Record<string, unknown>;
6
- export declare function parseListQuery(query: QueryMap, meta: AdminModelMeta, model: FullRegisteredModel, databaseProvider?: string): {
6
+ export type ParsedListQuery = {
7
7
  page: number;
8
8
  sort: string;
9
- dir: string;
10
- filters: Record<string, unknown>;
11
- search: {
12
- OR: {
13
- [x: string]: {
14
- mode?: string | undefined;
15
- contains: string;
16
- };
17
- }[];
18
- } | undefined;
9
+ dir: "asc" | "desc";
10
+ filters: FieldFilters;
11
+ search?: SearchQuery;
19
12
  };
13
+ export declare function parseListQuery(query: QueryMap, meta: AdminModelMeta, model: FullRegisteredModel): ParsedListQuery;
package/dist/listQuery.js CHANGED
@@ -37,15 +37,7 @@ function parseFilterValue(field, value) {
37
37
  throw new RequestValidationError(`Filter "${field.name}" is not supported.`);
38
38
  }
39
39
  }
40
- export function buildListWhere(scope, filters, search) {
41
- const conditions = [scope, filters, search].filter((condition) => condition !== undefined && Object.keys(condition).length > 0);
42
- if (conditions.length === 0)
43
- return {};
44
- if (conditions.length === 1)
45
- return conditions[0] ?? {};
46
- return { AND: conditions };
47
- }
48
- export function parseListQuery(query, meta, model, databaseProvider) {
40
+ export function parseListQuery(query, meta, model) {
49
41
  const pageValue = Number(getQueryValue(query, "page") ?? 1);
50
42
  if (!Number.isInteger(pageValue) || pageValue < 1)
51
43
  throw new RequestValidationError("Query parameter \"page\" must be a positive integer.");
@@ -79,12 +71,15 @@ export function parseListQuery(query, meta, model, databaseProvider) {
79
71
  if (field.type !== "datetime")
80
72
  throw new RequestValidationError(`Filter "${parameterName}" is only supported for date-time fields.`);
81
73
  const operator = rangeMatch[2];
82
- if (!operator)
74
+ if (operator !== "gte" && operator !== "lte")
83
75
  throw new RequestValidationError(`Filter "${parameterName}" is invalid.`);
84
- filters[fieldName] = { ...filters[fieldName], [operator]: parseFilterValue(field, rawValue) };
76
+ const existing = filters[fieldName];
77
+ const range = existing && !("equals" in existing) && !("in" in existing) ? { ...existing } : {};
78
+ range[operator] = parseFilterValue(field, rawValue);
79
+ filters[fieldName] = range;
85
80
  }
86
81
  else {
87
- filters[fieldName] = parseFilterValue(field, rawValue);
82
+ filters[fieldName] = { equals: parseFilterValue(field, rawValue) };
88
83
  }
89
84
  }
90
85
  const searchValue = getQueryValue(query, "search")?.trim();
@@ -94,13 +89,11 @@ export function parseListQuery(query, meta, model, databaseProvider) {
94
89
  const field = fieldsByName.get(fieldName);
95
90
  return field?.type === "string" && isFieldVisible(field, model.raw);
96
91
  });
97
- const search = searchValue ? {
98
- OR: searchFields.map((fieldName) => ({
99
- [fieldName]: { contains: searchValue, ...(databaseProvider === "postgresql" ? { mode: "insensitive" } : {}) },
100
- })),
101
- } : undefined;
102
92
  if (searchValue && searchFields.length === 0)
103
93
  throw new RequestValidationError(`Model "${meta.name}" has no searchable fields.`);
94
+ const search = searchValue
95
+ ? { text: searchValue, fields: searchFields }
96
+ : undefined;
104
97
  return { page: pageValue, sort, dir, filters, search };
105
98
  }
106
99
  //# sourceMappingURL=listQuery.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"listQuery.js","sourceRoot":"","sources":["../src/listQuery.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"listQuery.js","sourceRoot":"","sources":["../src/listQuery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ /** Field equality used by `scope()` and as the base of every query. */
2
+ export type EqualityFilter = Record<string, unknown>;
3
+ /** One list/query constraint on a single field. Not a Prisma `where` object. */
4
+ export type FieldFilter = {
5
+ equals: string | number | boolean | Date | null;
6
+ } | {
7
+ in: Array<string | number>;
8
+ } | {
9
+ gte?: Date | number;
10
+ lte?: Date | number;
11
+ };
12
+ export type FieldFilters = Record<string, FieldFilter>;
13
+ export type SearchQuery = {
14
+ text: string;
15
+ fields: string[];
16
+ };
17
+ export type RelationSelect = {
18
+ field: string;
19
+ displayField: string;
20
+ };
21
+ export type FieldSelect = {
22
+ fields: string[];
23
+ relations: RelationSelect[];
24
+ };
25
+ export type FindManyQuery = {
26
+ scope: EqualityFilter;
27
+ filters: FieldFilters;
28
+ search?: SearchQuery;
29
+ ids?: Array<string | number>;
30
+ sort?: {
31
+ field: string;
32
+ direction: "asc" | "desc";
33
+ };
34
+ skip?: number;
35
+ take?: number;
36
+ select: FieldSelect;
37
+ };
38
+ export type CountQuery = Pick<FindManyQuery, "scope" | "filters" | "search" | "ids">;
39
+ export type FindFirstQuery = {
40
+ scope: EqualityFilter;
41
+ id: string | number;
42
+ select: FieldSelect;
43
+ };
44
+ export type CreateQuery = {
45
+ data: Record<string, unknown>;
46
+ select: FieldSelect;
47
+ };
48
+ export type UpdateManyQuery = {
49
+ scope: EqualityFilter;
50
+ id?: string | number;
51
+ ids?: Array<string | number>;
52
+ data: Record<string, unknown>;
53
+ };
54
+ export type DeleteManyQuery = {
55
+ scope: EqualityFilter;
56
+ id?: string | number;
57
+ ids?: Array<string | number>;
58
+ };
59
+ /** Custom action `where`: scope plus the selected row ids. Not a Prisma `where`. */
60
+ export type ActionWhere = {
61
+ scope: EqualityFilter;
62
+ ids: Array<string | number>;
63
+ };
64
+ export declare function idSelect(idField: string): FieldSelect;
65
+ export declare function withSelectFields(select: FieldSelect, fieldNames: string[]): FieldSelect;
package/dist/query.js ADDED
@@ -0,0 +1,12 @@
1
+ export function idSelect(idField) {
2
+ return { fields: [idField], relations: [] };
3
+ }
4
+ export function withSelectFields(select, fieldNames) {
5
+ const fields = [...select.fields];
6
+ for (const name of fieldNames) {
7
+ if (!fields.includes(name))
8
+ fields.push(name);
9
+ }
10
+ return { fields, relations: select.relations };
11
+ }
12
+ //# sourceMappingURL=query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAqEA,MAAM,UAAU,QAAQ,CAAC,OAAe;IACrC,OAAO,EAAE,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAmB,EAAE,UAAoB;IACvE,MAAM,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAClC,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC7B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC;AAClD,CAAC"}
@@ -1,13 +1,10 @@
1
1
  import type { DataAdapter } from "./adapter.js";
2
2
  import type { FullRegisteredModel } from "./registry.js";
3
+ import type { FieldSelect } from "./query.js";
3
4
  import type { AdminModelMeta, AdminUser } from "./types.js";
4
5
  /** Build the safe select used by detail and edit endpoints. */
5
- export declare function buildRecordSelect(meta: AdminModelMeta, model: FullRegisteredModel): Record<string, true | {
6
- select: Record<string, true>;
7
- }>;
6
+ export declare function buildRecordSelect(meta: AdminModelMeta, model: FullRegisteredModel): FieldSelect;
8
7
  /** Only return columns the list is configured to display, plus the primary key. */
9
- export declare function buildListRecordSelect(meta: AdminModelMeta, model: FullRegisteredModel): Record<string, true | {
10
- select: Record<string, true>;
11
- }>;
8
+ export declare function buildListRecordSelect(meta: AdminModelMeta, model: FullRegisteredModel): FieldSelect;
12
9
  /** Verify submitted belongs-to IDs are visible under the related model's own access rules. */
13
10
  export declare function assertSelectedRelationsAreVisible(data: Record<string, unknown>, model: FullRegisteredModel, models: Map<string, FullRegisteredModel>, adapter: DataAdapter, adminUser: AdminUser): Promise<void>;
@@ -1,12 +1,14 @@
1
1
  import { hasModelPermission } from "./permissions.js";
2
- import { buildScopedRecordWhere, resolveScope } from "./scope.js";
2
+ import { idSelect } from "./query.js";
3
+ import { resolveScope } from "./scope.js";
3
4
  import { parseRecordId } from "./ids.js";
4
5
  import { isFieldVisible, isSensitiveFieldName, RequestValidationError } from "./validation.js";
5
6
  import { PermissionDeniedError } from "./errors.js";
6
- /** Build the safe select used by detail and edit endpoints. */
7
- export function buildRecordSelect(meta, model) {
8
- const select = Object.fromEntries(meta.fields.filter((field) => field.type !== "relation" && isFieldVisible(field, model.raw)).map((field) => [field.name, true]));
7
+ function relationSelects(meta, model, allowedNames) {
8
+ const relations = [];
9
9
  for (const fieldName of model.resolved.listDisplay) {
10
+ if (allowedNames && !allowedNames.has(fieldName))
11
+ continue;
10
12
  const field = meta.fields.find((candidate) => candidate.name === fieldName);
11
13
  if (!field?.relation || !isFieldVisible(field, model.raw))
12
14
  continue;
@@ -14,25 +16,28 @@ export function buildRecordSelect(meta, model) {
14
16
  continue;
15
17
  if (isSensitiveFieldName(field.relation.displayField))
16
18
  continue;
17
- select[field.name] = { select: { [field.relation.displayField]: true } };
19
+ relations.push({ field: field.name, displayField: field.relation.displayField });
18
20
  }
19
- return select;
21
+ return relations;
22
+ }
23
+ /** Build the safe select used by detail and edit endpoints. */
24
+ export function buildRecordSelect(meta, model) {
25
+ const fields = meta.fields
26
+ .filter((field) => field.type !== "relation" && isFieldVisible(field, model.raw))
27
+ .map((field) => field.name);
28
+ if (!fields.includes(meta.idField))
29
+ fields.unshift(meta.idField);
30
+ return { fields, relations: relationSelects(meta, model) };
20
31
  }
21
32
  /** Only return columns the list is configured to display, plus the primary key. */
22
33
  export function buildListRecordSelect(meta, model) {
23
34
  const allowed = new Set([meta.idField, ...model.resolved.listDisplay]);
24
- const select = Object.fromEntries(meta.fields.filter((field) => field.type !== "relation" && allowed.has(field.name) && isFieldVisible(field, model.raw)).map((field) => [field.name, true]));
25
- select[meta.idField] = true;
26
- for (const fieldName of model.resolved.listDisplay) {
27
- const field = meta.fields.find((candidate) => candidate.name === fieldName);
28
- if (!field?.relation || !isFieldVisible(field, model.raw))
29
- continue;
30
- if (field.relation.kind !== "belongsTo" && field.relation.kind !== "hasOne")
31
- continue;
32
- if (!isSensitiveFieldName(field.relation.displayField))
33
- select[field.name] = { select: { [field.relation.displayField]: true } };
34
- }
35
- return select;
35
+ const fields = meta.fields
36
+ .filter((field) => field.type !== "relation" && allowed.has(field.name) && isFieldVisible(field, model.raw))
37
+ .map((field) => field.name);
38
+ if (!fields.includes(meta.idField))
39
+ fields.unshift(meta.idField);
40
+ return { fields, relations: relationSelects(meta, model, allowed) };
36
41
  }
37
42
  /** Verify submitted belongs-to IDs are visible under the related model's own access rules. */
38
43
  export async function assertSelectedRelationsAreVisible(data, model, models, adapter, adminUser) {
@@ -55,8 +60,9 @@ export async function assertSelectedRelationsAreVisible(data, model, models, ada
55
60
  throw new PermissionDeniedError();
56
61
  const relatedScope = await resolveScope(relatedModel.raw, adminUser);
57
62
  const relatedRecord = await adapter.resource(relatedModel.meta).findFirst({
58
- where: buildScopedRecordWhere(relatedScope, relatedModel.meta.idField, parseRecordId(relatedModel.meta, String(selectedId))),
59
- select: { [relatedModel.meta.idField]: true },
63
+ scope: relatedScope,
64
+ id: parseRecordId(relatedModel.meta, String(selectedId)),
65
+ select: idSelect(relatedModel.meta.idField),
60
66
  });
61
67
  if (!relatedRecord)
62
68
  throw new RequestValidationError(`The selected ${relation.model} record is unavailable.`);
@@ -1 +1 @@
1
- {"version":3,"file":"recordSelection.js","sourceRoot":"","sources":["../src/recordSelection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"recordSelection.js","sourceRoot":"","sources":["../src/recordSelection.ts"],"names":[],"mappings":"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"}
package/dist/scope.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { AdminUser, ModelConfig } from "./types.js";
2
- /** A Prisma-compatible where fragment returned by a model scope function. */
2
+ /** Equality (and historically nested) filter returned by a model scope function. */
3
3
  export type ScopeFilter = Record<string, unknown>;
4
4
  /**
5
5
  * Resolve a model's tenant or ownership scope for an authenticated admin.
package/dist/types.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import type { DataAdapter } from "./adapter.js";
2
+ import type { ActionWhere } from "./query.js";
3
+ import type { AdminAuthStore } from "./authStore.js";
2
4
  export type AdminFieldType = "string" | "number" | "boolean" | "datetime" | "json" | "enum" | "relation" | "bytes";
3
5
  export type RelationKind = "belongsTo" | "hasMany" | "manyToMany" | "hasOne";
4
6
  export interface AdminFieldMeta {
@@ -56,7 +58,7 @@ export interface AdminAction {
56
58
  ids: Array<string | number>;
57
59
  adminUser: AdminUser;
58
60
  client: unknown;
59
- where: Record<string, unknown>;
61
+ where: ActionWhere;
60
62
  }) => Promise<{
61
63
  message: string;
62
64
  }>;
@@ -119,6 +121,8 @@ export interface BuiltInAuthConfig {
119
121
  windowMs?: number;
120
122
  maxAttempts?: number;
121
123
  };
124
+ /** Optional override. Prisma adapters supply a store via `createAuthStore`. */
125
+ store?: AdminAuthStore;
122
126
  }
123
127
  export type AuthConfig = ExternalAuthConfig | BuiltInAuthConfig;
124
128
  export interface AdminAuditEvent {
@@ -142,6 +146,7 @@ export interface AdminConfig {
142
146
  audit?: AuditConfig;
143
147
  basePath?: string;
144
148
  siteName?: string;
149
+ /** @deprecated Ignored. The Prisma adapter reads the datasource provider from `schema.prisma`. */
145
150
  databaseProvider?: "postgresql" | "mysql" | "sqlite" | "sqlserver" | "mongodb";
146
151
  }
147
152
  export interface PaginatedResponse<T> {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@paneljs/paneljs",
3
- "version": "0.1.0",
4
- "description": "Schema-driven admin panel core. Register models, serve a guarded operations UI.",
3
+ "version": "0.2.0",
4
+ "description": "Django-style, schema-driven Prisma admin panel for Express and Node.js applications.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "author": "Divinesta",
@@ -15,10 +15,15 @@
15
15
  },
16
16
  "homepage": "https://github.com/divinesta/paneljs",
17
17
  "keywords": [
18
- "admin",
19
- "admin-panel",
18
+ "prisma-admin",
19
+ "prisma-admin-panel",
20
20
  "express",
21
21
  "prisma",
22
+ "express-admin",
23
+ "nodejs-admin",
24
+ "django-admin-alternative",
25
+ "typescript-admin",
26
+ "schema-driven",
22
27
  "crud"
23
28
  ],
24
29
  "exports": {
@@ -33,6 +38,7 @@
33
38
  "dist",
34
39
  "ui/dist",
35
40
  "README.md",
41
+ "CHANGELOG.md",
36
42
  "LICENSE"
37
43
  ],
38
44
  "engines": {
@@ -41,14 +47,15 @@
41
47
  "publishConfig": {
42
48
  "access": "public"
43
49
  },
44
- "devDependencies": {
45
- "@types/node": "^22.15.3",
46
- "typescript": "5.9.2"
47
- },
48
50
  "scripts": {
49
51
  "build": "pnpm --dir ui build && tsc -p tsconfig.build.json",
50
52
  "dev": "pnpm --dir ui dev",
51
53
  "lint": "echo \"paneljs: no lint yet\"",
52
- "check-types": "tsc --noEmit -p tsconfig.json"
54
+ "check-types": "tsc --noEmit -p tsconfig.json",
55
+ "prepublishOnly": "pnpm run build"
56
+ },
57
+ "devDependencies": {
58
+ "@types/node": "^22.15.3",
59
+ "typescript": "5.9.2"
53
60
  }
54
- }
61
+ }