my-crud-lib 1.0.0 → 1.0.2

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/README.md CHANGED
@@ -196,6 +196,12 @@ The package exports the main public types:
196
196
 
197
197
  ---
198
198
 
199
+ ## Optional
200
+
201
+ if you want to use the prisma adeapter use:
202
+
203
+ npm i @prisma/client prisma
204
+ npx prisma generate
199
205
 
200
206
  ## Contributing
201
207
 
@@ -0,0 +1,3 @@
1
+ import type { PrismaClient } from '@prisma/client';
2
+ import type { UserRepo } from '../core/ports/user.repo.js';
3
+ export declare function makePrismaUserRepo(prisma: PrismaClient): UserRepo;
@@ -0,0 +1,132 @@
1
+ export function makePrismaUserRepo(prisma) {
2
+ return {
3
+ async count({ role, search }) {
4
+ const where = {};
5
+ if (role)
6
+ where.role = role;
7
+ if (search?.trim()) {
8
+ const s = search.trim();
9
+ where.OR = [
10
+ { email: { contains: s, mode: 'insensitive' } },
11
+ { name: { contains: s, mode: 'insensitive' } },
12
+ ];
13
+ }
14
+ return prisma.user.count({ where });
15
+ },
16
+ async findMany({ page, pageSize, role, search, sortField, sortDir }) {
17
+ const where = {};
18
+ if (role)
19
+ where.role = role;
20
+ if (search?.trim()) {
21
+ const s = search.trim();
22
+ where.OR = [
23
+ { email: { contains: s, mode: 'insensitive' } },
24
+ { name: { contains: s, mode: 'insensitive' } },
25
+ ];
26
+ }
27
+ const orderBy = { [sortField]: sortDir };
28
+ const users = await prisma.user.findMany({
29
+ where,
30
+ orderBy,
31
+ skip: (page - 1) * pageSize,
32
+ take: pageSize,
33
+ select: {
34
+ id: true,
35
+ email: true,
36
+ name: true,
37
+ role: true,
38
+ createdAt: true,
39
+ updatedAt: true,
40
+ profile: { select: { bio: true, avatarUrl: true } },
41
+ },
42
+ });
43
+ return users;
44
+ },
45
+ async findById(id) {
46
+ return prisma.user.findUnique({
47
+ where: { id: id },
48
+ select: {
49
+ id: true,
50
+ email: true,
51
+ name: true,
52
+ role: true,
53
+ createdAt: true,
54
+ updatedAt: true,
55
+ profile: { select: { bio: true, avatarUrl: true } },
56
+ },
57
+ });
58
+ },
59
+ async findByEmail(email) {
60
+ return prisma.user.findUnique({
61
+ where: { email },
62
+ select: {
63
+ id: true,
64
+ email: true,
65
+ name: true,
66
+ role: true,
67
+ passwordHash: true,
68
+ createdAt: true,
69
+ updatedAt: true,
70
+ profile: { select: { bio: true, avatarUrl: true } },
71
+ },
72
+ });
73
+ },
74
+ async create(input) {
75
+ return prisma.user.create({
76
+ data: {
77
+ email: input.email,
78
+ passwordHash: input.passwordHash,
79
+ name: input.name ?? null,
80
+ role: input.role ?? 'USER',
81
+ profile: { create: { bio: input.bio ?? null, avatarUrl: input.avatarUrl ?? null } },
82
+ },
83
+ select: {
84
+ id: true, email: true, name: true, role: true,
85
+ createdAt: true, updatedAt: true,
86
+ profile: { select: { bio: true, avatarUrl: true } },
87
+ },
88
+ });
89
+ },
90
+ async update(id, input) {
91
+ return prisma.user.update({
92
+ where: { id: id },
93
+ data: {
94
+ name: input.name ?? undefined,
95
+ role: input.role ?? undefined,
96
+ profile: {
97
+ upsert: {
98
+ create: { bio: input.bio ?? null, avatarUrl: input.avatarUrl ?? null },
99
+ update: { bio: input.bio ?? null, avatarUrl: input.avatarUrl ?? null },
100
+ },
101
+ },
102
+ },
103
+ select: {
104
+ id: true, email: true, name: true, role: true,
105
+ profile: { select: { bio: true, avatarUrl: true } },
106
+ createdAt: true, updatedAt: true,
107
+ },
108
+ });
109
+ },
110
+ async delete(id) {
111
+ await prisma.user.delete({ where: { id: id } });
112
+ },
113
+ async updateMe(userId, data) {
114
+ return prisma.user.update({
115
+ where: { id: userId },
116
+ data: {
117
+ name: data.name ?? undefined,
118
+ profile: {
119
+ upsert: {
120
+ create: { bio: data.bio ?? null, avatarUrl: data.avatarUrl ?? null },
121
+ update: { bio: data.bio ?? null, avatarUrl: data.avatarUrl ?? null },
122
+ },
123
+ },
124
+ },
125
+ select: {
126
+ id: true, email: true, name: true, role: true,
127
+ profile: { select: { bio: true, avatarUrl: true } },
128
+ },
129
+ });
130
+ },
131
+ };
132
+ }
@@ -0,0 +1,34 @@
1
+ import type { AdminUpdateUserInput, UserListItem } from "../../modules/user/user.types.js";
2
+ export interface UserRepo {
3
+ count(where: {
4
+ role?: string;
5
+ search?: string;
6
+ }): Promise<number>;
7
+ findMany(params: {
8
+ page: number;
9
+ pageSize: number;
10
+ role?: string;
11
+ search?: string;
12
+ sortField: 'createdAt' | 'updatedAt' | 'email' | 'name';
13
+ sortDir: 'asc' | 'desc';
14
+ }): Promise<UserListItem[]>;
15
+ findById(id: number | string): Promise<UserListItem | null>;
16
+ findByEmail(email: string): Promise<UserListItem & {
17
+ passwordHash?: string;
18
+ } | null>;
19
+ create(input: {
20
+ email: string;
21
+ passwordHash: string;
22
+ name?: string | null;
23
+ role?: string;
24
+ bio?: string | null;
25
+ avatarUrl?: string | null;
26
+ }): Promise<UserListItem>;
27
+ update(id: number | string, input: AdminUpdateUserInput): Promise<UserListItem>;
28
+ delete(id: number | string): Promise<void>;
29
+ updateMe(userId: number | string, input: {
30
+ name?: string | null;
31
+ bio?: string | null;
32
+ avatarUrl?: string | null;
33
+ }): Promise<UserListItem>;
34
+ }
@@ -0,0 +1 @@
1
+ export {};
package/dist/dev.js CHANGED
@@ -2,9 +2,13 @@ import { createServer } from './index.js';
2
2
  import { createAuthRouter } from './modules/auth/auth.controller.js';
3
3
  import 'dotenv/config';
4
4
  import { createUserRouter } from './modules/user/user.controller.js';
5
+ import { makePrismaUserRepo } from './adapters/prisma.js';
6
+ import { PrismaClient } from '@prisma/client';
5
7
  const app = createServer();
8
+ const prisma = new PrismaClient();
9
+ const userRepo = makePrismaUserRepo(prisma);
6
10
  app.use('/auth', createAuthRouter());
7
- app.use('/users', createUserRouter());
11
+ app.use('/users', createUserRouter({ userRepo }));
8
12
  const PORT = Number(process.env.PORT) || 3000;
9
13
  app.listen(PORT, () => {
10
14
  console.log(`API dev up on http://localhost:${PORT}`);
@@ -1 +1,4 @@
1
- export declare function createUserRouter(): import("express-serve-static-core").Router;
1
+ import type { UserRepo } from '../../core/ports/user.repo.js';
2
+ export declare function createUserRouter(deps: {
3
+ userRepo: UserRepo;
4
+ }): import("express-serve-static-core").Router;
@@ -2,13 +2,14 @@ import { Router } from 'express';
2
2
  import { isAuth } from '../../middleware/isAuth.js';
3
3
  import { hasRole, isSelfOrAdmin } from '../../middleware/hasRole.js';
4
4
  import { listUsersQuerySchema, updateMeSchema, adminCreateUserSchema, adminUpdateUserSchema, } from './user.schemas.js';
5
- import { listUsers, getUserById, updateMe, adminCreateUser, adminUpdateUser, adminDeleteUser, } from './user.service.js';
6
- export function createUserRouter() {
5
+ import { makeUserService } from './user.service.js';
6
+ export function createUserRouter(deps) {
7
7
  const router = Router();
8
+ const service = makeUserService({ userRepo: deps.userRepo });
8
9
  router.get('/', isAuth, hasRole('ADMIN'), async (req, res) => {
9
10
  try {
10
11
  const q = listUsersQuerySchema.parse(req.query);
11
- const data = await listUsers(q);
12
+ const data = await service.listUsers(q);
12
13
  res.json(data);
13
14
  }
14
15
  catch (e) {
@@ -17,15 +18,8 @@ export function createUserRouter() {
17
18
  res.status(500).json({ error: 'InternalError' });
18
19
  }
19
20
  });
20
- router.get('/:id', isAuth, isSelfOrAdmin(), async (req, res) => {
21
- const id = Number(req.params.id);
22
- const data = await getUserById(id);
23
- if (!data)
24
- return res.status(404).json({ error: 'User not found' });
25
- res.json(data);
26
- });
27
- router.get('/me/self', isAuth, async (req, res) => {
28
- const data = await getUserById(req.user.id);
21
+ router.get('/me', isAuth, async (req, res) => {
22
+ const data = await service.getUserById(req.user.id);
29
23
  if (!data)
30
24
  return res.status(404).json({ error: 'User not found' });
31
25
  res.json(data);
@@ -33,7 +27,7 @@ export function createUserRouter() {
33
27
  router.put('/me', isAuth, async (req, res) => {
34
28
  try {
35
29
  const body = updateMeSchema.parse(req.body);
36
- const data = await updateMe(req.user.id, body);
30
+ const data = await service.updateMe(req.user.id, body);
37
31
  res.json(data);
38
32
  }
39
33
  catch (e) {
@@ -45,7 +39,7 @@ export function createUserRouter() {
45
39
  router.post('/', isAuth, hasRole('ADMIN'), async (req, res) => {
46
40
  try {
47
41
  const body = adminCreateUserSchema.parse(req.body);
48
- const data = await adminCreateUser(body);
42
+ const data = await service.adminCreateUser(body);
49
43
  res.status(201).json(data);
50
44
  }
51
45
  catch (e) {
@@ -56,14 +50,26 @@ export function createUserRouter() {
56
50
  res.status(500).json({ error: 'InternalError' });
57
51
  }
58
52
  });
53
+ router.get('/:id', isAuth, isSelfOrAdmin(), async (req, res) => {
54
+ const id = Number(req.params.id);
55
+ if (Number.isNaN(id))
56
+ return res.status(400).json({ error: 'Invalid id' });
57
+ const data = await service.getUserById(id);
58
+ if (!data)
59
+ return res.status(404).json({ error: 'User not found' });
60
+ res.json(data);
61
+ });
59
62
  router.put('/:id', isAuth, isSelfOrAdmin(), async (req, res) => {
60
63
  try {
61
64
  const id = Number(req.params.id);
65
+ if (Number.isNaN(id))
66
+ return res.status(400).json({ error: 'Invalid id' });
62
67
  const body = adminUpdateUserSchema.parse(req.body);
63
68
  if (req.user.role !== 'ADMIN' && body.role) {
64
69
  return res.status(403).json({ error: 'Forbidden: cannot change role' });
65
70
  }
66
- const data = await adminUpdateUser(id, body);
71
+ const sanitizedBody = { ...body, role: body.role === null ? undefined : body.role };
72
+ const data = await service.adminUpdateUser(id, sanitizedBody);
67
73
  res.json(data);
68
74
  }
69
75
  catch (e) {
@@ -75,8 +81,10 @@ export function createUserRouter() {
75
81
  router.delete('/:id', isAuth, hasRole('ADMIN'), async (req, res) => {
76
82
  try {
77
83
  const id = Number(req.params.id);
78
- const data = await adminDeleteUser(id);
79
- res.json(data);
84
+ if (Number.isNaN(id))
85
+ return res.status(400).json({ error: 'Invalid id' });
86
+ await service.adminDeleteUser(id);
87
+ res.status(204).end();
80
88
  }
81
89
  catch {
82
90
  res.status(500).json({ error: 'InternalError' });
@@ -1,18 +1,19 @@
1
1
  import { z } from 'zod';
2
+ export declare const SortEnum: z.ZodEnum<["createdAt:desc", "createdAt:asc", "updatedAt:asc", "updatedAt:desc", "email:asc", "email:desc", "name:asc", "name:desc"]>;
2
3
  export declare const listUsersQuerySchema: z.ZodObject<{
3
4
  page: z.ZodDefault<z.ZodNumber>;
4
5
  pageSize: z.ZodDefault<z.ZodNumber>;
5
- search: z.ZodOptional<z.ZodString>;
6
+ search: z.ZodOptional<z.ZodEffects<z.ZodString, string | undefined, string>>;
6
7
  role: z.ZodOptional<z.ZodEnum<["USER", "ADMIN"]>>;
7
- sort: z.ZodDefault<z.ZodString>;
8
+ sort: z.ZodDefault<z.ZodEnum<["createdAt:desc", "createdAt:asc", "updatedAt:asc", "updatedAt:desc", "email:asc", "email:desc", "name:asc", "name:desc"]>>;
8
9
  }, "strip", z.ZodTypeAny, {
9
- sort: string;
10
+ sort: "createdAt:desc" | "createdAt:asc" | "updatedAt:asc" | "updatedAt:desc" | "email:asc" | "email:desc" | "name:asc" | "name:desc";
10
11
  page: number;
11
12
  pageSize: number;
12
13
  search?: string | undefined;
13
14
  role?: "USER" | "ADMIN" | undefined;
14
15
  }, {
15
- sort?: string | undefined;
16
+ sort?: "createdAt:desc" | "createdAt:asc" | "updatedAt:asc" | "updatedAt:desc" | "email:asc" | "email:desc" | "name:asc" | "name:desc" | undefined;
16
17
  search?: string | undefined;
17
18
  role?: "USER" | "ADMIN" | undefined;
18
19
  page?: number | undefined;
@@ -1,13 +1,24 @@
1
1
  import { z } from 'zod';
2
+ export const SortEnum = z.enum([
3
+ 'createdAt:desc',
4
+ 'createdAt:asc',
5
+ 'updatedAt:asc',
6
+ 'updatedAt:desc',
7
+ 'email:asc',
8
+ 'email:desc',
9
+ 'name:asc',
10
+ 'name:desc',
11
+ ]);
2
12
  export const listUsersQuerySchema = z.object({
3
13
  page: z.coerce.number().int().min(1).default(1),
4
14
  pageSize: z.coerce.number().int().min(1).max(100).default(10),
5
- search: z.string().trim().min(1).optional(),
6
- role: z.enum(['USER', 'ADMIN']).optional(),
7
- sort: z
15
+ search: z
8
16
  .string()
9
- .regex(/^(createdAt|updatedAt|email|name):(asc|desc)$/i)
10
- .default('createdAt:desc'),
17
+ .trim()
18
+ .transform((v) => (v === '' ? undefined : v))
19
+ .optional(),
20
+ role: z.enum(['USER', 'ADMIN']).optional(),
21
+ sort: SortEnum.default('createdAt:desc'),
11
22
  });
12
23
  export const updateMeSchema = z.object({
13
24
  name: z.string().min(1).max(100).nullish(),
@@ -1,68 +1,14 @@
1
- import type { AdminCreateUserInput, AdminUpdateUserInput, ListUsersQuery, UpdateMeInput } from './user.schemas.js';
2
- export declare function listUsers(q: ListUsersQuery): Promise<{
3
- page: number;
4
- pageSize: number;
5
- total: number;
6
- items: {
7
- email: string;
8
- name: string | null;
9
- id: number;
10
- role: import("@prisma/client").$Enums.Role;
11
- createdAt: Date;
12
- updatedAt: Date;
13
- profile: {
14
- bio: string | null;
15
- avatarUrl: string | null;
16
- } | null;
17
- }[];
18
- totalPages: number;
19
- }>;
20
- export declare function getUserById(id: number): Promise<{
21
- email: string;
22
- name: string | null;
23
- id: number;
24
- role: import("@prisma/client").$Enums.Role;
25
- createdAt: Date;
26
- updatedAt: Date;
27
- profile: {
28
- bio: string | null;
29
- avatarUrl: string | null;
30
- } | null;
31
- } | null>;
32
- export declare function updateMe(userId: number, data: UpdateMeInput): Promise<{
33
- email: string;
34
- name: string | null;
35
- id: number;
36
- role: import("@prisma/client").$Enums.Role;
37
- profile: {
38
- bio: string | null;
39
- avatarUrl: string | null;
40
- } | null;
41
- }>;
42
- export declare function adminCreateUser(input: AdminCreateUserInput): Promise<{
43
- email: string;
44
- name: string | null;
45
- id: number;
46
- role: import("@prisma/client").$Enums.Role;
47
- createdAt: Date;
48
- updatedAt: Date;
49
- profile: {
50
- bio: string | null;
51
- avatarUrl: string | null;
52
- } | null;
53
- }>;
54
- export declare function adminUpdateUser(id: number, input: AdminUpdateUserInput): Promise<{
55
- email: string;
56
- name: string | null;
57
- id: number;
58
- role: import("@prisma/client").$Enums.Role;
59
- createdAt: Date;
60
- updatedAt: Date;
61
- profile: {
62
- bio: string | null;
63
- avatarUrl: string | null;
64
- } | null;
65
- }>;
66
- export declare function adminDeleteUser(id: number): Promise<{
67
- ok: boolean;
68
- }>;
1
+ import type { AdminCreateUserInput, AdminUpdateUserInput, ListUsersQuery, Paginated, UpdateMeInput, UserListItem } from './user.types.js';
2
+ import type { UserRepo } from '../../core/ports/user.repo.js';
3
+ export declare function makeUserService(deps: {
4
+ userRepo: UserRepo;
5
+ }): {
6
+ listUsers(q: ListUsersQuery): Promise<Paginated<UserListItem>>;
7
+ getUserById(id: number | string): Promise<UserListItem | null>;
8
+ updateMe(userId: number | string, data: UpdateMeInput): Promise<UserListItem>;
9
+ adminCreateUser(input: AdminCreateUserInput): Promise<UserListItem>;
10
+ adminUpdateUser(id: number | string, input: AdminUpdateUserInput): Promise<UserListItem>;
11
+ adminDeleteUser(id: number | string): Promise<{
12
+ ok: true;
13
+ }>;
14
+ };
@@ -1,136 +1,52 @@
1
- import { prisma } from '../../utils/prisma.js';
2
1
  import bcrypt from 'bcryptjs';
3
- export async function listUsers(q) {
4
- const { page, pageSize, role, search } = q;
5
- const [rawField, rawDir] = (q.sort ?? 'createdAt:desc').split(':');
6
- const allowedFields = ['createdAt', 'updatedAt', 'email', 'name'];
2
+ const allowedFields = ['createdAt', 'updatedAt', 'email', 'name'];
3
+ function parseSort(sort) {
4
+ const [rawField, rawDir] = (sort ?? 'createdAt:desc').split(':');
7
5
  const sortField = allowedFields.includes(rawField || '')
8
6
  ? rawField
9
7
  : 'createdAt';
10
8
  const sortDir = rawDir === 'asc' || rawDir === 'desc' ? rawDir : 'desc';
11
- const where = {};
12
- if (role)
13
- where.role = role;
14
- if (search && search.trim() !== '') {
15
- const s = search.trim();
16
- where.OR = [
17
- { email: { contains: s, mode: 'insensitive' } },
18
- { name: { contains: s, mode: 'insensitive' } },
19
- ];
20
- }
21
- const orderBy = { [sortField]: sortDir };
22
- const [total, items] = await Promise.all([
23
- prisma.user.count({ where }),
24
- prisma.user.findMany({
25
- where,
26
- orderBy,
27
- skip: (page - 1) * pageSize,
28
- take: pageSize,
29
- select: {
30
- id: true,
31
- email: true,
32
- name: true,
33
- role: true,
34
- createdAt: true,
35
- updatedAt: true,
36
- profile: { select: { bio: true, avatarUrl: true } },
37
- },
38
- }),
39
- ]);
40
- return {
41
- page,
42
- pageSize,
43
- total,
44
- items,
45
- totalPages: Math.ceil(total / pageSize),
46
- };
9
+ return { sortField, sortDir };
47
10
  }
48
- export async function getUserById(id) {
49
- return prisma.user.findUnique({
50
- where: { id },
51
- select: {
52
- id: true,
53
- email: true,
54
- name: true,
55
- role: true,
56
- createdAt: true,
57
- updatedAt: true,
58
- profile: { select: { bio: true, avatarUrl: true } },
11
+ export function makeUserService(deps) {
12
+ const { userRepo } = deps;
13
+ return {
14
+ async listUsers(q) {
15
+ const { page, pageSize, role, search } = q;
16
+ const { sortField, sortDir } = parseSort(q.sort);
17
+ const [total, items] = await Promise.all([
18
+ userRepo.count({ role, search }),
19
+ userRepo.findMany({ page, pageSize, role, search, sortField, sortDir }),
20
+ ]);
21
+ return { page, pageSize, total, totalPages: Math.ceil(total / pageSize), items };
59
22
  },
60
- });
61
- }
62
- export async function updateMe(userId, data) {
63
- return prisma.user.update({
64
- where: { id: userId },
65
- data: {
66
- name: data.name ?? undefined,
67
- profile: {
68
- upsert: {
69
- create: { bio: data.bio ?? null, avatarUrl: data.avatarUrl ?? null },
70
- update: { bio: data.bio ?? null, avatarUrl: data.avatarUrl ?? null },
71
- },
72
- },
23
+ getUserById(id) {
24
+ return userRepo.findById(id);
73
25
  },
74
- select: {
75
- id: true,
76
- email: true,
77
- name: true,
78
- role: true,
79
- profile: { select: { bio: true, avatarUrl: true } },
26
+ updateMe(userId, data) {
27
+ return userRepo.updateMe(userId, data);
80
28
  },
81
- });
82
- }
83
- export async function adminCreateUser(input) {
84
- const exists = await prisma.user.findUnique({ where: { email: input.email } });
85
- if (exists)
86
- throw new Error('EMAIL_TAKEN');
87
- const passwordHash = await bcrypt.hash(input.password, Number(process.env.BCRYPT_SALT) || 10);
88
- return prisma.user.create({
89
- data: {
90
- email: input.email,
91
- passwordHash,
92
- name: input.name ?? null,
93
- role: input.role ?? 'USER',
94
- profile: {
95
- create: {
96
- bio: input.bio ?? null,
97
- avatarUrl: input.avatarUrl ?? null,
98
- },
99
- },
29
+ async adminCreateUser(input) {
30
+ const exists = await userRepo.findByEmail(input.email);
31
+ if (exists)
32
+ throw new Error('EMAIL_TAKEN');
33
+ const rounds = Number(process.env.BCRYPT_SALT) || 10;
34
+ const passwordHash = await bcrypt.hash(input.password, rounds);
35
+ return userRepo.create({
36
+ email: input.email,
37
+ passwordHash,
38
+ name: input.name ?? null,
39
+ role: input.role ?? 'USER',
40
+ bio: input.bio ?? null,
41
+ avatarUrl: input.avatarUrl ?? null,
42
+ });
100
43
  },
101
- select: {
102
- id: true,
103
- email: true,
104
- name: true,
105
- role: true,
106
- profile: { select: { bio: true, avatarUrl: true } },
107
- createdAt: true,
108
- updatedAt: true,
44
+ adminUpdateUser(id, input) {
45
+ return userRepo.update(id, input);
109
46
  },
110
- });
111
- }
112
- export async function adminUpdateUser(id, input) {
113
- return prisma.user.update({
114
- where: { id },
115
- data: {
116
- name: input.name ?? undefined,
117
- role: input.role ?? undefined,
118
- profile: {
119
- upsert: {
120
- create: { bio: input.bio ?? null, avatarUrl: input.avatarUrl ?? null },
121
- update: { bio: input.bio ?? null, avatarUrl: input.avatarUrl ?? null },
122
- },
123
- },
124
- },
125
- select: {
126
- id: true, email: true, name: true, role: true,
127
- profile: { select: { bio: true, avatarUrl: true } },
128
- createdAt: true, updatedAt: true,
47
+ async adminDeleteUser(id) {
48
+ await userRepo.delete(id);
49
+ return { ok: true };
129
50
  },
130
- });
131
- }
132
- export async function adminDeleteUser(id) {
133
- const user = await prisma.user.findUnique({ where: { id } });
134
- await prisma.user.delete({ where: { id } });
135
- return { ok: true };
51
+ };
136
52
  }
@@ -1 +1,46 @@
1
- export {};
1
+ export type Role = 'USER' | 'ADMIN';
2
+ export type UserListItem = {
3
+ id: number | string;
4
+ email: string;
5
+ name: string | null;
6
+ role: Role;
7
+ createdAt: Date | string;
8
+ updatedAt: Date | string;
9
+ profile?: {
10
+ bio: string | null;
11
+ avatarUrl: string | null;
12
+ } | null;
13
+ };
14
+ export type ListUsersQuery = {
15
+ page: number;
16
+ pageSize: number;
17
+ role?: Role;
18
+ search?: string;
19
+ sort?: `${'createdAt' | 'updatedAt' | 'email' | 'name'}:${'asc' | 'desc'}`;
20
+ };
21
+ export type Paginated<T> = {
22
+ page: number;
23
+ pageSize: number;
24
+ total: number;
25
+ totalPages: number;
26
+ items: T[];
27
+ };
28
+ export type UpdateMeInput = {
29
+ name?: string | null;
30
+ bio?: string | null;
31
+ avatarUrl?: string | null;
32
+ };
33
+ export type AdminCreateUserInput = {
34
+ email: string;
35
+ password: string;
36
+ name?: string | null;
37
+ role?: Role;
38
+ bio?: string | null;
39
+ avatarUrl?: string | null;
40
+ };
41
+ export type AdminUpdateUserInput = {
42
+ name?: string | null;
43
+ role?: Role;
44
+ bio?: string | null;
45
+ avatarUrl?: string | null;
46
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "my-crud-lib",
3
- "version": "1.0.0",
4
- "description": "Libreria CRUD modulare (Auth/User/Profile) con Prisma + TS",
3
+ "version": "1.0.2",
4
+ "description": "Libreria CRUD modulare (Auth/User/Profile) con TS; Prisma opzionale via adapter",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
@@ -10,10 +10,16 @@
10
10
  ".": {
11
11
  "import": "./dist/index.js",
12
12
  "types": "./dist/index.d.ts"
13
+ },
14
+ "./adapter-prisma": {
15
+ "import": "./dist/adapters/prisma.js",
16
+ "types": "./dist/adapters/prisma.d.ts"
13
17
  }
14
18
  },
15
19
  "files": [
16
20
  "dist",
21
+ "README.md",
22
+ "LICENSE",
17
23
  "prisma/schema.prisma"
18
24
  ],
19
25
  "engines": {
@@ -31,7 +37,6 @@
31
37
  "prisma:generate": "prisma generate",
32
38
  "prisma:migrate": "prisma migrate dev",
33
39
  "prisma:studio": "prisma studio",
34
- "postinstall": "prisma generate",
35
40
  "prepublishOnly": "npm run build",
36
41
  "db:schema": "docker exec -i mycrud_postgres psql -U app -d mycrud -v ON_ERROR_STOP=1 -f /dev/stdin < prisma/schema.sql",
37
42
  "db:seed": "docker exec -i mycrud_postgres psql -U app -d mycrud -v ON_ERROR_STOP=1 -f /dev/stdin < prisma/seed.sql",
@@ -45,20 +50,30 @@
45
50
  "@types/jsonwebtoken": "^9.0.10",
46
51
  "@types/node": "^24.3.0",
47
52
  "prisma": "^6.14.0",
53
+ "@prisma/client": "^6.14.0",
48
54
  "ts-node": "^10.9.2",
49
55
  "typescript": "^5.9.2"
50
56
  },
51
57
  "dependencies": {
52
- "@prisma/client": "^6.14.0",
53
58
  "bcryptjs": "^2.4.3",
54
59
  "dotenv": "^16.3.1",
55
60
  "jsonwebtoken": "^9.0.2",
56
61
  "zod": "^3.23.8"
57
62
  },
58
63
  "peerDependencies": {
59
- "body-parser": "^1.20.2",
64
+ "express": "^4.18.2",
60
65
  "cors": "^2.8.5",
61
- "express": "^4.18.2"
66
+ "body-parser": "^1.20.2",
67
+ "@prisma/client": "^6.14.0",
68
+ "prisma": "^6.14.0"
69
+ },
70
+ "peerDependenciesMeta": {
71
+ "@prisma/client": {
72
+ "optional": true
73
+ },
74
+ "prisma": {
75
+ "optional": true
76
+ }
62
77
  },
63
78
  "publishConfig": {
64
79
  "access": "public"