@pelatform/starter.db 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.
package/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # Pelatform Starter
2
+
3
+ A part of SaaS starter kit for Pelatform applications.
4
+
5
+ [![Version](https://img.shields.io/npm/v/@pelatform/starter.svg)](https://www.npmjs.com/package/@pelatform/starter)
@@ -0,0 +1,219 @@
1
+ declare const prefixes: readonly ["acc_", "api_", "ct_", "cus_", "dev_", "elog_", "org_", "invi_", "mem_", "pass_", "prod_", "proj_", "rt_", "sess_", "subs_", "two_", "user_", "ver_", "wh_", "ws_"];
2
+ declare const createId: ({ prefix }: {
3
+ prefix: (typeof prefixes)[number];
4
+ }) => string;
5
+ declare const createIdCustom: ({ prefix }: {
6
+ prefix: string;
7
+ }) => string;
8
+
9
+ /**
10
+ * Creates a new email log entry in the database.
11
+ *
12
+ * This function logs email events (e.g., sent emails) with associated metadata
13
+ * for tracking and auditing purposes.
14
+ *
15
+ * @param db - The database client instance (e.g., PrismaClient).
16
+ * @param email - The recipient's email address.
17
+ * @param metadata - Additional metadata related to the email (e.g., subject, template ID).
18
+ * @returns A promise that resolves when the log is created.
19
+ */
20
+ declare function createEmailLog(db: any, email: string, metadata: any): Promise<void>;
21
+
22
+ /**
23
+ * Retrieves user details from the database.
24
+ *
25
+ * Fetches the user based on the session ID, including their role and active workspace
26
+ * based on the current session's active organization.
27
+ *
28
+ * @param db - The database client instance.
29
+ * @param session - The current user session object.
30
+ * @param customSelect - Optional Prisma select object to customize returned fields.
31
+ * @returns The user object with role and active workspace, or an error object.
32
+ */
33
+ declare function getUser(db: any, session: any, customSelect?: any): Promise<any>;
34
+ /**
35
+ * Updates a user's information in the database.
36
+ *
37
+ * Updates the user record identified by the session ID with the provided data.
38
+ * Returns the updated user object with role and active workspace information.
39
+ *
40
+ * @param db - The database client instance.
41
+ * @param session - The current user session object.
42
+ * @param data - The data to update (key-value pairs).
43
+ * @param customSelect - Optional Prisma select object to customize returned fields.
44
+ * @returns The updated user object with role and active workspace, or an error object.
45
+ */
46
+ declare function updateUser(db: any, session: any, data: Record<string, any>, customSelect?: any): Promise<any>;
47
+ /**
48
+ * Updates a specific user's data by their ID.
49
+ *
50
+ * Direct database update for a user without session context.
51
+ *
52
+ * @param db - The database client instance.
53
+ * @param userId - The ID of the user to update.
54
+ * @param data - The data to update.
55
+ * @returns
56
+ */
57
+ declare function updateUserData(db: any, userId: string, data: Record<string, any>): Promise<void>;
58
+ /**
59
+ * Fetches initial user data for app initialization.
60
+ *
61
+ * Retrieves the user and their membership details for the active organization
62
+ * stored in the session.
63
+ *
64
+ * @param db - The database client instance.
65
+ * @param session - The current user session object.
66
+ * @returns Object containing user data and authentication status.
67
+ */
68
+ declare function getInitialUserData(db: any, session: any): Promise<{
69
+ user: any;
70
+ isAuthenticated: boolean;
71
+ }>;
72
+
73
+ /**
74
+ * Retrieves a workspace by its slug.
75
+ *
76
+ * Fetches workspace details if the user is a member of the workspace.
77
+ * Includes the current user's role in the returned data.
78
+ *
79
+ * @param db - The database client instance.
80
+ * @param session - The current user session.
81
+ * @param slug - The unique slug of the workspace.
82
+ * @param customSelect - Optional Prisma select object.
83
+ * @returns The workspace object with currentUserRole, or an error object.
84
+ */
85
+ declare function getWorkspacebySlug(db: any, session: any, slug: string, customSelect?: any): Promise<any>;
86
+ /**
87
+ * Retrieves a list of workspaces the user is a member of.
88
+ *
89
+ * @param db - The database client instance.
90
+ * @param session - The current user session.
91
+ * @param customSelect - Optional Prisma select object.
92
+ * @returns Array of workspace objects with currentUserRole, or an error object.
93
+ */
94
+ declare function getWorkspaceList(db: any, session: any, customSelect?: any): Promise<any>;
95
+ /**
96
+ * Fetches the initial workspace data for the active user.
97
+ *
98
+ * If a workspace slug is provided, the function attempts to fetch
99
+ * that workspace (checking membership). Otherwise, it falls back
100
+ * to the user's currently active session workspace.
101
+ *
102
+ * @param db - The database client instance.
103
+ * @param session - The user session object containing active workspace ID.
104
+ * @param customSelect - Optional Prisma select object.
105
+ * @returns The workspace data or null if not found.
106
+ */
107
+ declare function getInitialWorkspaceData(db: any, session: any, customSelect?: any): Promise<any>;
108
+ /**
109
+ * Determines which workspace should be activated for a given user.
110
+ *
111
+ * The function checks, in order:
112
+ * 1. The user's **last visited workspace** (stored in cookies), verifying they still have access to it.
113
+ * 2. If not found or access was lost, the first workspace where the user is an **owner**.
114
+ * 3. If still none, the first workspace where the user is a **member**.
115
+ *
116
+ * Returns the workspace's `slug` and `id`, or `undefined` if the user has no accessible workspaces.
117
+ *
118
+ * @param db - The database client instance.
119
+ * @param userId - The ID of the user to look up workspaces for.
120
+ * @param slug - Optional workspace slug to check if user still has access.
121
+ * @returns An object containing `{ slug, id }` for the selected workspace, or `undefined` if none found.
122
+ */
123
+ declare function getLastActiveWorkspace(db: any, userId: string, slug?: string): Promise<{
124
+ slug: string;
125
+ id: string;
126
+ } | undefined>;
127
+ /**
128
+ * Validates whether the given workspace slug exists and the active user has access to it.
129
+ *
130
+ * @param db - The database client instance.
131
+ * @param session - The current user session.
132
+ * @param slug - The workspace slug to validate.
133
+ * @returns {Promise<boolean>} True if the workspace exists and the user is a member.
134
+ */
135
+ declare function validateWorkspaceAccess(db: any, session: any, slug: string): Promise<boolean>;
136
+
137
+ /** Reusable Prisma select object for user queries */
138
+ declare const userSelect: (workspaceId: string) => {
139
+ readonly id: true;
140
+ readonly name: true;
141
+ readonly email: true;
142
+ readonly image: true;
143
+ readonly emailVerified: true;
144
+ readonly createdAt: true;
145
+ readonly updatedAt: true;
146
+ readonly members: {
147
+ readonly where: {
148
+ readonly organizationId: string;
149
+ };
150
+ readonly select: {
151
+ readonly role: true;
152
+ readonly organization: {
153
+ readonly select: {
154
+ readonly id: true;
155
+ readonly name: true;
156
+ readonly slug: true;
157
+ };
158
+ };
159
+ };
160
+ readonly take: 1;
161
+ };
162
+ };
163
+
164
+ /** Reusable Prisma select object for workspace queries */
165
+ declare const workspaceSelect: {
166
+ readonly id: true;
167
+ readonly name: true;
168
+ readonly slug: true;
169
+ readonly logo: true;
170
+ readonly metadata: true;
171
+ readonly createdAt: true;
172
+ readonly members: {
173
+ readonly select: {
174
+ readonly id: true;
175
+ readonly organizationId: true;
176
+ readonly userId: true;
177
+ readonly role: true;
178
+ readonly createdAt: true;
179
+ readonly user: {
180
+ readonly select: {
181
+ readonly id: true;
182
+ readonly name: true;
183
+ readonly email: true;
184
+ readonly image: true;
185
+ };
186
+ };
187
+ };
188
+ };
189
+ readonly invitations: {
190
+ readonly select: {
191
+ readonly id: true;
192
+ readonly organizationId: true;
193
+ readonly inviterId: true;
194
+ readonly email: true;
195
+ readonly role: true;
196
+ readonly status: true;
197
+ readonly expiresAt: true;
198
+ };
199
+ };
200
+ readonly subscription: {
201
+ readonly select: {
202
+ readonly id: true;
203
+ readonly userId: true;
204
+ readonly workspaceId: true;
205
+ readonly subscriptionId: true;
206
+ readonly externalId: true;
207
+ readonly provider: true;
208
+ readonly status: true;
209
+ readonly plan: true;
210
+ readonly currentPeriodStart: true;
211
+ readonly currentPeriodEnd: true;
212
+ readonly cancelAtPeriodEnd: true;
213
+ readonly canceledAt: true;
214
+ readonly isLifetime: true;
215
+ };
216
+ };
217
+ };
218
+
219
+ export { createEmailLog, createId, createIdCustom, getInitialUserData, getInitialWorkspaceData, getLastActiveWorkspace, getUser, getWorkspaceList, getWorkspacebySlug, updateUser, updateUserData, userSelect, validateWorkspaceAccess, workspaceSelect };
package/dist/index.js ADDED
@@ -0,0 +1,388 @@
1
+ // src/lib/create-id.ts
2
+ import crypto from "crypto";
3
+ import baseX from "base-x";
4
+ var base32 = baseX("0123456789ABCDEFGHJKMNPQRSTVWXYZ");
5
+ function createULIDBuffer() {
6
+ const buf = new Uint8Array(16);
7
+ const timestamp = BigInt(Date.now());
8
+ buf[0] = Number(timestamp >> BigInt(40) & BigInt(255));
9
+ buf[1] = Number(timestamp >> BigInt(32) & BigInt(255));
10
+ buf[2] = Number(timestamp >> BigInt(24) & BigInt(255));
11
+ buf[3] = Number(timestamp >> BigInt(16) & BigInt(255));
12
+ buf[4] = Number(timestamp >> BigInt(8) & BigInt(255));
13
+ buf[5] = Number(timestamp & BigInt(255));
14
+ crypto.getRandomValues(buf.subarray(6));
15
+ return buf;
16
+ }
17
+ var createId = ({ prefix }) => {
18
+ const buf = createULIDBuffer();
19
+ const id = base32.encode(buf);
20
+ return `${prefix}${id}`;
21
+ };
22
+ var createIdCustom = ({ prefix }) => {
23
+ const buf = createULIDBuffer();
24
+ const id = base32.encode(buf);
25
+ return `${prefix}${id}`;
26
+ };
27
+
28
+ // src/queries/email-log.ts
29
+ async function createEmailLog(db, email, metadata) {
30
+ await db.emailLog.create({
31
+ data: {
32
+ email,
33
+ metadata
34
+ }
35
+ });
36
+ }
37
+
38
+ // src/utils/user-select.ts
39
+ var userSelect = (workspaceId) => ({
40
+ id: true,
41
+ name: true,
42
+ email: true,
43
+ image: true,
44
+ emailVerified: true,
45
+ createdAt: true,
46
+ updatedAt: true,
47
+ members: {
48
+ where: {
49
+ organizationId: workspaceId
50
+ },
51
+ select: {
52
+ role: true,
53
+ organization: {
54
+ select: {
55
+ id: true,
56
+ name: true,
57
+ slug: true
58
+ }
59
+ }
60
+ },
61
+ take: 1
62
+ }
63
+ });
64
+
65
+ // src/queries/user.ts
66
+ async function getUser(db, session, customSelect) {
67
+ if (!session || !session.user) {
68
+ return { error: true, message: "User not authenticated" };
69
+ }
70
+ try {
71
+ const result = await db.user.findUnique({
72
+ where: { id: session?.user.id },
73
+ select: customSelect || userSelect(session?.session?.activeOrganizationId || "")
74
+ });
75
+ if (!result) {
76
+ return { error: true, message: "User not found" };
77
+ }
78
+ const user = {
79
+ ...result,
80
+ workspaceRole: result?.members[0]?.role || null,
81
+ activeWorkspace: result?.members[0]?.organization || null
82
+ };
83
+ return user;
84
+ } catch (error) {
85
+ console.error("Error fetching user data:", error);
86
+ return { error: true, message: "Error fetching user data." };
87
+ }
88
+ }
89
+ async function updateUser(db, session, data, customSelect) {
90
+ if (!session || !session.user) {
91
+ return { error: true, message: "User not authenticated" };
92
+ }
93
+ try {
94
+ const result = await db.user.update({
95
+ where: { id: session?.user.id },
96
+ data,
97
+ select: customSelect || userSelect(session?.session?.activeOrganizationId || "")
98
+ });
99
+ if (!result) {
100
+ return { error: true, message: "User not found" };
101
+ }
102
+ const user = {
103
+ ...result,
104
+ workspaceRole: result?.members[0]?.role || null,
105
+ activeWorkspace: result?.members[0]?.organization || null
106
+ };
107
+ return user;
108
+ } catch (error) {
109
+ console.error("Error updating user data:", error);
110
+ return { error: true, message: "Error updating user data." };
111
+ }
112
+ }
113
+ async function updateUserData(db, userId, data) {
114
+ if (!userId) {
115
+ return;
116
+ }
117
+ await db.user.update({
118
+ where: { id: userId },
119
+ data
120
+ });
121
+ }
122
+ async function getInitialUserData(db, session) {
123
+ if (!session || !session.user) {
124
+ return { user: null, isAuthenticated: false };
125
+ }
126
+ try {
127
+ const result = await db.user.findUnique({
128
+ where: { id: session.user.id }
129
+ });
130
+ if (!result) {
131
+ return { user: null, isAuthenticated: false };
132
+ }
133
+ const activeOrganizationId = session.session?.activeOrganizationId;
134
+ if (activeOrganizationId && typeof activeOrganizationId !== "string") {
135
+ console.warn("Invalid activeOrganizationId type:", typeof activeOrganizationId);
136
+ return { user: null, isAuthenticated: true };
137
+ }
138
+ const member = activeOrganizationId ? await db.member.findFirst({
139
+ where: {
140
+ organizationId: activeOrganizationId,
141
+ userId: result.id
142
+ },
143
+ include: {
144
+ organization: {
145
+ select: {
146
+ id: true,
147
+ name: true,
148
+ slug: true
149
+ }
150
+ }
151
+ }
152
+ }) : null;
153
+ const user = {
154
+ ...result,
155
+ workspaceRole: member?.role || null,
156
+ activeWorkspace: member?.organization || null
157
+ };
158
+ return { user, isAuthenticated: true };
159
+ } catch (error) {
160
+ console.error("Error fetching initial user data:", error);
161
+ return { user: null, isAuthenticated: false };
162
+ }
163
+ }
164
+
165
+ // src/utils/workspace-select.ts
166
+ var workspaceSelect = {
167
+ id: true,
168
+ name: true,
169
+ slug: true,
170
+ logo: true,
171
+ metadata: true,
172
+ createdAt: true,
173
+ members: {
174
+ select: {
175
+ id: true,
176
+ organizationId: true,
177
+ userId: true,
178
+ role: true,
179
+ createdAt: true,
180
+ user: {
181
+ select: {
182
+ id: true,
183
+ name: true,
184
+ email: true,
185
+ image: true
186
+ }
187
+ }
188
+ }
189
+ },
190
+ invitations: {
191
+ select: {
192
+ id: true,
193
+ organizationId: true,
194
+ inviterId: true,
195
+ email: true,
196
+ role: true,
197
+ status: true,
198
+ expiresAt: true
199
+ }
200
+ },
201
+ subscription: {
202
+ select: {
203
+ id: true,
204
+ userId: true,
205
+ workspaceId: true,
206
+ subscriptionId: true,
207
+ externalId: true,
208
+ provider: true,
209
+ status: true,
210
+ plan: true,
211
+ currentPeriodStart: true,
212
+ currentPeriodEnd: true,
213
+ cancelAtPeriodEnd: true,
214
+ canceledAt: true,
215
+ isLifetime: true
216
+ }
217
+ }
218
+ };
219
+
220
+ // src/queries/workspace.ts
221
+ async function getWorkspacebySlug(db, session, slug, customSelect) {
222
+ if (!session || !session.user) {
223
+ return { error: true, message: "User not authenticated" };
224
+ }
225
+ try {
226
+ const result = await db.organization.findUnique({
227
+ where: { slug },
228
+ select: customSelect || workspaceSelect
229
+ });
230
+ if (!result) {
231
+ return { error: true, message: "Workspace not found" };
232
+ }
233
+ const isUserMember = result.members.some((member) => member.userId === session.user.id);
234
+ if (!isUserMember) {
235
+ return { error: true, message: "User is not a member of the workspace" };
236
+ }
237
+ const currentUserMember = result.members.find(
238
+ (member) => member.userId === session.user.id
239
+ );
240
+ const currentUserRole = currentUserMember?.role || null;
241
+ const workspace = {
242
+ ...result,
243
+ currentUserRole
244
+ };
245
+ return workspace;
246
+ } catch (error) {
247
+ console.error("Error fetching workspace data:", error);
248
+ return { error: true, message: "Error fetching workspace data." };
249
+ }
250
+ }
251
+ async function getWorkspaceList(db, session, customSelect) {
252
+ if (!session || !session.user) {
253
+ return { error: true, message: "User not authenticated" };
254
+ }
255
+ try {
256
+ const result = await db.organization.findMany({
257
+ where: {
258
+ members: {
259
+ some: {
260
+ userId: session.user.id
261
+ }
262
+ }
263
+ },
264
+ select: customSelect || workspaceSelect,
265
+ orderBy: {
266
+ createdAt: "desc"
267
+ }
268
+ });
269
+ const workspaces = result.map((ws) => {
270
+ const currentUserMember = ws.members.find((member) => member.userId === session.user.id);
271
+ return {
272
+ ...ws,
273
+ currentUserRole: currentUserMember?.role || null
274
+ };
275
+ });
276
+ return workspaces;
277
+ } catch (error) {
278
+ console.error("Error fetching workspace list:", error);
279
+ return { error: true, message: "Error fetching workspace list." };
280
+ }
281
+ }
282
+ async function getInitialWorkspaceData(db, session, customSelect) {
283
+ if (!session?.user || !session.session?.activeOrganizationId) {
284
+ return null;
285
+ }
286
+ try {
287
+ const workspace = await db.organization.findUnique({
288
+ where: { id: session.session.activeOrganizationId },
289
+ select: customSelect || workspaceSelect
290
+ });
291
+ if (!workspace) {
292
+ return null;
293
+ }
294
+ const currentUserMember = workspace.members.find(
295
+ (member) => member.userId === session.user.id
296
+ );
297
+ return {
298
+ ...workspace,
299
+ currentUserRole: currentUserMember?.role || null
300
+ };
301
+ } catch (error) {
302
+ console.error("Error fetching initial workspace data:", error);
303
+ return null;
304
+ }
305
+ }
306
+ async function getLastActiveWorkspace(db, userId, slug) {
307
+ if (slug) {
308
+ const workspace = await db.organization.findFirst({
309
+ where: {
310
+ slug,
311
+ members: {
312
+ some: {
313
+ userId
314
+ }
315
+ }
316
+ },
317
+ select: { slug: true, id: true }
318
+ });
319
+ if (workspace) {
320
+ return {
321
+ slug: workspace.slug,
322
+ id: workspace.id
323
+ };
324
+ }
325
+ }
326
+ const ownerWorkspace = await db.organization.findFirst({
327
+ where: {
328
+ members: {
329
+ some: {
330
+ userId,
331
+ role: "owner"
332
+ }
333
+ }
334
+ },
335
+ select: { slug: true, id: true }
336
+ });
337
+ if (ownerWorkspace) {
338
+ return {
339
+ slug: ownerWorkspace.slug,
340
+ id: ownerWorkspace.id
341
+ };
342
+ }
343
+ const memberWorkspace = await db.organization.findFirst({
344
+ where: {
345
+ members: {
346
+ some: {
347
+ userId
348
+ }
349
+ }
350
+ },
351
+ select: { slug: true, id: true }
352
+ });
353
+ if (memberWorkspace) {
354
+ return {
355
+ slug: memberWorkspace.slug,
356
+ id: memberWorkspace.id
357
+ };
358
+ }
359
+ }
360
+ async function validateWorkspaceAccess(db, session, slug) {
361
+ if (!session?.user) {
362
+ return false;
363
+ }
364
+ const workspace = await db.organization.findFirst({
365
+ where: {
366
+ slug,
367
+ members: { some: { userId: session.user.id } }
368
+ },
369
+ select: { id: true }
370
+ });
371
+ return Boolean(workspace);
372
+ }
373
+ export {
374
+ createEmailLog,
375
+ createId,
376
+ createIdCustom,
377
+ getInitialUserData,
378
+ getInitialWorkspaceData,
379
+ getLastActiveWorkspace,
380
+ getUser,
381
+ getWorkspaceList,
382
+ getWorkspacebySlug,
383
+ updateUser,
384
+ updateUserData,
385
+ userSelect,
386
+ validateWorkspaceAccess,
387
+ workspaceSelect
388
+ };
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@pelatform/starter.db",
3
+ "version": "0.1.0",
4
+ "description": "A part of SaaS starter kit for Pelatform applications.",
5
+ "author": "Pelatform",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "sideEffects": false,
11
+ "exports": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "scripts": {
16
+ "clean": "rimraf dist",
17
+ "clean:all": "rimraf .turbo dist node_modules",
18
+ "dev": "tsup --watch",
19
+ "build": "tsup",
20
+ "types:check": "tsc --noEmit"
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "keywords": [
26
+ "pelatform",
27
+ "starter kit",
28
+ "components",
29
+ "utilities",
30
+ "database"
31
+ ],
32
+ "dependencies": {
33
+ "base-x": "^5.0.1"
34
+ },
35
+ "devDependencies": {
36
+ "@pelatform/tsconfig": "^0.1.4",
37
+ "tsup": "^8.5.1"
38
+ },
39
+ "publishConfig": {
40
+ "registry": "https://registry.npmjs.org/",
41
+ "access": "public"
42
+ },
43
+ "lint-staged": {
44
+ "*.{js,jsx,ts,tsx,cjs,mjs,cts,mts}": "biome check --write --no-errors-on-unmatched",
45
+ "*.{md,yml,yaml}": "prettier --write",
46
+ "*.{json,jsonc,html}": "biome format --write --no-errors-on-unmatched"
47
+ }
48
+ }