@techstream/quark-create-app 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +38 -0
  2. package/package.json +34 -0
  3. package/src/index.js +611 -0
  4. package/templates/base-project/README.md +35 -0
  5. package/templates/base-project/apps/web/next.config.js +6 -0
  6. package/templates/base-project/apps/web/package.json +32 -0
  7. package/templates/base-project/apps/web/postcss.config.mjs +7 -0
  8. package/templates/base-project/apps/web/public/file.svg +1 -0
  9. package/templates/base-project/apps/web/public/globe.svg +1 -0
  10. package/templates/base-project/apps/web/public/next.svg +1 -0
  11. package/templates/base-project/apps/web/public/vercel.svg +1 -0
  12. package/templates/base-project/apps/web/public/window.svg +1 -0
  13. package/templates/base-project/apps/web/src/app/api/auth/[...nextauth]/route.js +4 -0
  14. package/templates/base-project/apps/web/src/app/api/auth/register/route.js +39 -0
  15. package/templates/base-project/apps/web/src/app/api/csrf/route.js +42 -0
  16. package/templates/base-project/apps/web/src/app/api/error-handler.js +21 -0
  17. package/templates/base-project/apps/web/src/app/api/health/route.js +78 -0
  18. package/templates/base-project/apps/web/src/app/api/posts/[id]/route.js +61 -0
  19. package/templates/base-project/apps/web/src/app/api/posts/route.js +34 -0
  20. package/templates/base-project/apps/web/src/app/api/users/[id]/route.js +54 -0
  21. package/templates/base-project/apps/web/src/app/api/users/route.js +36 -0
  22. package/templates/base-project/apps/web/src/app/favicon.ico +0 -0
  23. package/templates/base-project/apps/web/src/app/globals.css +26 -0
  24. package/templates/base-project/apps/web/src/app/layout.js +12 -0
  25. package/templates/base-project/apps/web/src/app/page.js +10 -0
  26. package/templates/base-project/apps/web/src/app/page.test.js +11 -0
  27. package/templates/base-project/apps/web/src/lib/auth-middleware.js +14 -0
  28. package/templates/base-project/apps/web/src/lib/auth.js +102 -0
  29. package/templates/base-project/apps/web/src/middleware.js +265 -0
  30. package/templates/base-project/apps/worker/package.json +28 -0
  31. package/templates/base-project/apps/worker/src/index.js +154 -0
  32. package/templates/base-project/apps/worker/src/index.test.js +19 -0
  33. package/templates/base-project/docker-compose.yml +40 -0
  34. package/templates/base-project/package.json +26 -0
  35. package/templates/base-project/packages/db/package.json +29 -0
  36. package/templates/base-project/packages/db/prisma/migrations/20260202061128_initial/migration.sql +176 -0
  37. package/templates/base-project/packages/db/prisma/migrations/migration_lock.toml +3 -0
  38. package/templates/base-project/packages/db/prisma/schema.prisma +147 -0
  39. package/templates/base-project/packages/db/prisma.config.ts +25 -0
  40. package/templates/base-project/packages/db/scripts/seed.js +47 -0
  41. package/templates/base-project/packages/db/src/client.js +52 -0
  42. package/templates/base-project/packages/db/src/generated/prisma/browser.ts +53 -0
  43. package/templates/base-project/packages/db/src/generated/prisma/client.ts +82 -0
  44. package/templates/base-project/packages/db/src/generated/prisma/commonInputTypes.ts +649 -0
  45. package/templates/base-project/packages/db/src/generated/prisma/enums.ts +19 -0
  46. package/templates/base-project/packages/db/src/generated/prisma/internal/class.ts +305 -0
  47. package/templates/base-project/packages/db/src/generated/prisma/internal/prismaNamespace.ts +1428 -0
  48. package/templates/base-project/packages/db/src/generated/prisma/internal/prismaNamespaceBrowser.ts +217 -0
  49. package/templates/base-project/packages/db/src/generated/prisma/models/Account.ts +2098 -0
  50. package/templates/base-project/packages/db/src/generated/prisma/models/AuditLog.ts +1805 -0
  51. package/templates/base-project/packages/db/src/generated/prisma/models/Job.ts +1737 -0
  52. package/templates/base-project/packages/db/src/generated/prisma/models/Post.ts +1762 -0
  53. package/templates/base-project/packages/db/src/generated/prisma/models/Session.ts +1738 -0
  54. package/templates/base-project/packages/db/src/generated/prisma/models/User.ts +2298 -0
  55. package/templates/base-project/packages/db/src/generated/prisma/models/VerificationToken.ts +1450 -0
  56. package/templates/base-project/packages/db/src/generated/prisma/models.ts +18 -0
  57. package/templates/base-project/packages/db/src/index.js +3 -0
  58. package/templates/base-project/packages/db/src/queries.js +267 -0
  59. package/templates/base-project/packages/db/src/queries.test.js +79 -0
  60. package/templates/base-project/packages/db/src/schemas.js +31 -0
  61. package/templates/base-project/pnpm-workspace.yaml +7 -0
  62. package/templates/base-project/turbo.json +25 -0
  63. package/templates/config/package.json +8 -0
  64. package/templates/config/src/index.js +21 -0
  65. package/templates/jobs/package.json +8 -0
  66. package/templates/jobs/src/definitions.js +9 -0
  67. package/templates/jobs/src/handlers.js +20 -0
  68. package/templates/jobs/src/index.js +2 -0
  69. package/templates/ui/package.json +11 -0
  70. package/templates/ui/src/button.js +19 -0
  71. package/templates/ui/src/card.js +14 -0
  72. package/templates/ui/src/index.js +3 -0
  73. package/templates/ui/src/input.js +11 -0
@@ -0,0 +1,18 @@
1
+ /* !!! This is code generated by Prisma. Do not edit directly. !!! */
2
+ /* eslint-disable */
3
+ // biome-ignore-all lint: generated file
4
+ // @ts-nocheck
5
+ /*
6
+ * This is a barrel export file for all models and their related types.
7
+ *
8
+ * 🟢 You can import this file directly.
9
+ */
10
+
11
+ export type * from "./commonInputTypes.ts";
12
+ export type * from "./models/Account.ts";
13
+ export type * from "./models/AuditLog.ts";
14
+ export type * from "./models/Job.ts";
15
+ export type * from "./models/Post.ts";
16
+ export type * from "./models/Session.ts";
17
+ export type * from "./models/User.ts";
18
+ export type * from "./models/VerificationToken.ts";
@@ -0,0 +1,3 @@
1
+ export * from "./client.js";
2
+ export * from "./queries.js";
3
+ export * from "./schemas.js";
@@ -0,0 +1,267 @@
1
+ import { prisma } from "./client.js";
2
+
3
+ /**
4
+ * Safe select for user queries — excludes sensitive fields (password).
5
+ * Use this on any query whose result is returned to the client.
6
+ */
7
+ const USER_SAFE_SELECT = {
8
+ id: true,
9
+ email: true,
10
+ emailVerified: true,
11
+ name: true,
12
+ image: true,
13
+ role: true,
14
+ createdAt: true,
15
+ updatedAt: true,
16
+ };
17
+
18
+ // User queries
19
+ export const user = {
20
+ findById: (id) => {
21
+ return prisma.user.findUnique({
22
+ where: { id },
23
+ select: USER_SAFE_SELECT,
24
+ });
25
+ },
26
+ findByIdWithPosts: (id) => {
27
+ return prisma.user.findUnique({
28
+ where: { id },
29
+ select: { ...USER_SAFE_SELECT, posts: true },
30
+ });
31
+ },
32
+ /**
33
+ * findByEmail returns ALL fields including password.
34
+ * Only use for internal auth — never expose the result directly to clients.
35
+ */
36
+ findByEmail: (email) => {
37
+ return prisma.user.findUnique({
38
+ where: { email },
39
+ });
40
+ },
41
+ findAll: (options = {}) => {
42
+ const { skip = 0, take = 10 } = options;
43
+ return prisma.user.findMany({
44
+ skip,
45
+ take,
46
+ select: USER_SAFE_SELECT,
47
+ orderBy: { createdAt: "desc" },
48
+ });
49
+ },
50
+ create: (data) => {
51
+ return prisma.user.create({
52
+ data,
53
+ select: USER_SAFE_SELECT,
54
+ });
55
+ },
56
+ update: (id, data) => {
57
+ return prisma.user.update({
58
+ where: { id },
59
+ data,
60
+ select: USER_SAFE_SELECT,
61
+ });
62
+ },
63
+ delete: (id) => {
64
+ return prisma.user.delete({
65
+ where: { id },
66
+ });
67
+ },
68
+ };
69
+
70
+ // Post queries
71
+ export const post = {
72
+ findById: (id) => {
73
+ return prisma.post.findUnique({
74
+ where: { id },
75
+ include: { author: true },
76
+ });
77
+ },
78
+ findAll: (options = {}) => {
79
+ const { skip = 0, take = 10 } = options;
80
+ return prisma.post.findMany({
81
+ skip,
82
+ take,
83
+ include: { author: true },
84
+ orderBy: { createdAt: "desc" },
85
+ });
86
+ },
87
+ findPublished: (options = {}) => {
88
+ const { skip = 0, take = 10 } = options;
89
+ return prisma.post.findMany({
90
+ where: { published: true },
91
+ skip,
92
+ take,
93
+ include: { author: true },
94
+ orderBy: { createdAt: "desc" },
95
+ });
96
+ },
97
+ findByAuthor: (authorId, options = {}) => {
98
+ const { skip = 0, take = 10 } = options;
99
+ return prisma.post.findMany({
100
+ where: { authorId },
101
+ skip,
102
+ take,
103
+ include: { author: true },
104
+ orderBy: { createdAt: "desc" },
105
+ });
106
+ },
107
+ create: (data) => {
108
+ return prisma.post.create({
109
+ data,
110
+ include: { author: true },
111
+ });
112
+ },
113
+ update: (id, data) => {
114
+ return prisma.post.update({
115
+ where: { id },
116
+ data,
117
+ include: { author: true },
118
+ });
119
+ },
120
+ delete: (id) => {
121
+ return prisma.post.delete({
122
+ where: { id },
123
+ });
124
+ },
125
+ };\n\n// Note: Job tracking is handled by BullMQ's built-in Redis persistence.\n// The Prisma Job model is retained in the schema for optional audit/reporting\n// but these query helpers have been removed to avoid confusion with BullMQ.\n// If you need database-backed job auditing, re-add job queries here and wire\n// the worker to write status updates to the Job table.\n\n// Account queries (NextAuth)
126
+ export const account = {
127
+ findById: (id) => {
128
+ return prisma.account.findUnique({
129
+ where: { id },
130
+ });
131
+ },
132
+ findByUserIdAndProvider: (userId, provider) => {
133
+ return prisma.account.findFirst({
134
+ where: { userId, provider },
135
+ });
136
+ },
137
+ findByUser: (userId) => {
138
+ return prisma.account.findMany({
139
+ where: { userId },
140
+ });
141
+ },
142
+ create: (data) => {
143
+ return prisma.account.create({
144
+ data,
145
+ });
146
+ },
147
+ delete: (id) => {
148
+ return prisma.account.delete({
149
+ where: { id },
150
+ });
151
+ },
152
+ };
153
+
154
+ // Session queries (NextAuth)
155
+ export const session = {
156
+ findByToken: (sessionToken) => {
157
+ return prisma.session.findUnique({
158
+ where: { sessionToken },
159
+ include: { user: true },
160
+ });
161
+ },
162
+ findByUserId: (userId) => {
163
+ return prisma.session.findMany({
164
+ where: { userId },
165
+ });
166
+ },
167
+ create: (data) => {
168
+ return prisma.session.create({
169
+ data,
170
+ include: { user: true },
171
+ });
172
+ },
173
+ update: (sessionToken, data) => {
174
+ return prisma.session.update({
175
+ where: { sessionToken },
176
+ data,
177
+ });
178
+ },
179
+ delete: (sessionToken) => {
180
+ return prisma.session.delete({
181
+ where: { sessionToken },
182
+ });
183
+ },
184
+ deleteByUserId: (userId) => {
185
+ return prisma.session.deleteMany({
186
+ where: { userId },
187
+ });
188
+ },
189
+ };
190
+
191
+ // Verification Token queries (NextAuth)
192
+ export const verificationToken = {
193
+ findByToken: (token) => {
194
+ return prisma.verificationToken.findUnique({
195
+ where: { token },
196
+ });
197
+ },
198
+ findByIdentifierAndToken: (identifier, token) => {
199
+ return prisma.verificationToken.findUnique({
200
+ where: { identifier_token: { identifier, token } },
201
+ });
202
+ },
203
+ create: (data) => {
204
+ return prisma.verificationToken.create({
205
+ data,
206
+ });
207
+ },
208
+ delete: (identifier, token) => {
209
+ return prisma.verificationToken.deleteMany({
210
+ where: { identifier, token },
211
+ });
212
+ },
213
+ deleteExpired: () => {
214
+ return prisma.verificationToken.deleteMany({
215
+ where: { expires: { lt: new Date() } },
216
+ });
217
+ },
218
+ };
219
+
220
+ // AuditLog queries
221
+ export const auditLog = {
222
+ findAll: (options = {}) => {
223
+ const { skip = 0, take = 50 } = options;
224
+ return prisma.auditLog.findMany({
225
+ skip,
226
+ take,
227
+ include: { user: { select: { id: true, email: true, name: true } } },
228
+ orderBy: { createdAt: "desc" },
229
+ });
230
+ },
231
+ findByUserId: (userId, options = {}) => {
232
+ const { skip = 0, take = 50 } = options;
233
+ return prisma.auditLog.findMany({
234
+ where: { userId },
235
+ skip,
236
+ take,
237
+ include: { user: { select: { id: true, email: true, name: true } } },
238
+ orderBy: { createdAt: "desc" },
239
+ });
240
+ },
241
+ findByEntity: (entity, options = {}) => {
242
+ const { skip = 0, take = 50 } = options;
243
+ return prisma.auditLog.findMany({
244
+ where: { entity },
245
+ skip,
246
+ take,
247
+ include: { user: { select: { id: true, email: true, name: true } } },
248
+ orderBy: { createdAt: "desc" },
249
+ });
250
+ },
251
+ findByAction: (action, options = {}) => {
252
+ const { skip = 0, take = 50 } = options;
253
+ return prisma.auditLog.findMany({
254
+ where: { action },
255
+ skip,
256
+ take,
257
+ include: { user: { select: { id: true, email: true, name: true } } },
258
+ orderBy: { createdAt: "desc" },
259
+ });
260
+ },
261
+ create: (data) => {
262
+ return prisma.auditLog.create({
263
+ data,
264
+ include: { user: { select: { id: true, email: true, name: true } } },
265
+ });
266
+ },
267
+ };
@@ -0,0 +1,79 @@
1
+ import assert from "node:assert";
2
+ import { test } from "node:test";
3
+
4
+ // Create a mock for prisma client
5
+ const _mockPrisma = {
6
+ user: {
7
+ findUnique: async (params) => params,
8
+ create: async (params) => params,
9
+ },
10
+ post: {
11
+ create: async (params) => params,
12
+ findMany: async (params) => params,
13
+ },
14
+ };
15
+
16
+ // Mock module for queries
17
+ const mockQueries = {
18
+ user: {
19
+ findById: async (id) => ({
20
+ id,
21
+ email: "test@example.com",
22
+ name: "Test",
23
+ }),
24
+ findByEmail: async (email) => ({
25
+ id: "1",
26
+ email,
27
+ name: "Test",
28
+ }),
29
+ create: async (data) => ({
30
+ id: "1",
31
+ ...data,
32
+ }),
33
+ },
34
+ post: {
35
+ create: async (data) => ({
36
+ id: "1",
37
+ ...data,
38
+ }),
39
+ findPublished: async () => [
40
+ { id: "1", title: "Published", published: true },
41
+ ],
42
+ },
43
+ };
44
+
45
+ test("User Queries - findById calls prisma with correct params", async () => {
46
+ const result = await mockQueries.user.findById("1");
47
+ assert.strictEqual(result.id, "1");
48
+ assert.strictEqual(result.email, "test@example.com");
49
+ });
50
+
51
+ test("User Queries - findByEmail calls prisma with correct params", async () => {
52
+ const result = await mockQueries.user.findByEmail("test@example.com");
53
+ assert.strictEqual(result.email, "test@example.com");
54
+ });
55
+
56
+ test("User Queries - create calls prisma with correct params", async () => {
57
+ const result = await mockQueries.user.create({
58
+ email: "new@example.com",
59
+ name: "New User",
60
+ });
61
+ assert.strictEqual(result.email, "new@example.com");
62
+ assert.strictEqual(result.name, "New User");
63
+ });
64
+
65
+ test("Post Queries - create calls prisma with correct params", async () => {
66
+ const result = await mockQueries.post.create({
67
+ title: "Test Post",
68
+ authorId: "1",
69
+ });
70
+ assert.strictEqual(result.title, "Test Post");
71
+ assert.strictEqual(result.authorId, "1");
72
+ });
73
+
74
+ test("Post Queries - findPublished returns only published posts", async () => {
75
+ const result = await mockQueries.post.findPublished();
76
+ assert.ok(Array.isArray(result));
77
+ assert.strictEqual(result.length, 1);
78
+ assert.strictEqual(result[0].published, true);
79
+ });
@@ -0,0 +1,31 @@
1
+ import { z } from "zod";
2
+
3
+ export const userCreateSchema = z.object({
4
+ email: z.string().email(),
5
+ name: z.string().optional(),
6
+ image: z.string().url().optional(),
7
+ });
8
+
9
+ export const userRegisterSchema = z.object({
10
+ email: z.string().email("Invalid email address"),
11
+ password: z.string().min(8, "Password must be at least 8 characters"),
12
+ name: z.string().min(2, "Name must be at least 2 characters").optional(),
13
+ });
14
+
15
+ export const userUpdateSchema = z.object({
16
+ email: z.string().email().optional(),
17
+ name: z.string().optional(),
18
+ image: z.string().url().optional(),
19
+ });
20
+
21
+ export const postCreateSchema = z.object({
22
+ title: z.string().min(1, "Title is required"),
23
+ content: z.string().optional(),
24
+ published: z.boolean().optional(),
25
+ });
26
+
27
+ export const postUpdateSchema = z.object({
28
+ title: z.string().min(1, "Title is required").optional(),
29
+ content: z.string().optional(),
30
+ published: z.boolean().optional(),
31
+ });
@@ -0,0 +1,7 @@
1
+ packages:
2
+ - apps/*
3
+ - packages/*
4
+
5
+ onlyBuiltDependencies:
6
+ - '@prisma/engines'
7
+ - prisma
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "https://turbo.build/schema.json",
3
+ "tasks": {
4
+ "build": {
5
+ "dependsOn": ["^build"],
6
+ "outputs": ["dist/**", ".next/**"],
7
+ "env": ["NODE_ENV"]
8
+ },
9
+ "lint": {
10
+ "outputs": []
11
+ },
12
+ "test": {
13
+ "dependsOn": ["build"],
14
+ "outputs": []
15
+ },
16
+ "db:generate": {
17
+ "cache": false,
18
+ "outputs": ["node_modules/.prisma/client"]
19
+ },
20
+ "dev": {
21
+ "cache": false,
22
+ "persistent": true
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "@myquark/config",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": "./src/index.js"
7
+ }
8
+ }
@@ -0,0 +1,21 @@
1
+ export const config = {
2
+ appName: "My Quark App",
3
+ environment: process.env.NODE_ENV || "development",
4
+ api: {
5
+ baseUrl: process.env.API_BASE_URL || "http://localhost:3000",
6
+ },
7
+ database: {
8
+ url:
9
+ process.env.DATABASE_URL ||
10
+ "postgresql://user:password@localhost:5432/myapp",
11
+ },
12
+ redis: {
13
+ url: process.env.REDIS_URL || "redis://localhost:6379",
14
+ },
15
+ email: {
16
+ from: process.env.EMAIL_FROM || "noreply@myquarkapp.com",
17
+ provider: process.env.EMAIL_PROVIDER || "mailhog",
18
+ },
19
+ };
20
+
21
+ export default config;
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "@myquark/jobs",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": "./src/index.js"
7
+ }
8
+ }
@@ -0,0 +1,9 @@
1
+ export const JOB_QUEUES = {
2
+ EMAIL: "email-queue",
3
+ NOTIFICATIONS: "notifications-queue",
4
+ };
5
+
6
+ export const JOB_NAMES = {
7
+ SEND_WELCOME_EMAIL: "send-welcome-email",
8
+ SEND_RESET_PASSWORD_EMAIL: "send-reset-password-email",
9
+ };
@@ -0,0 +1,20 @@
1
+ import { JOB_NAMES } from "./definitions.js";
2
+
3
+ export async function sendWelcomeEmail(job) {
4
+ const { email } = job.data;
5
+ console.log(`Sending welcome email to ${email}`);
6
+ // Add your email sending logic here
7
+ return { success: true };
8
+ }
9
+
10
+ export async function sendResetPasswordEmail(job) {
11
+ const { email } = job.data;
12
+ console.log(`Sending reset password email to ${email}`);
13
+ // Add your email sending logic here
14
+ return { success: true };
15
+ }
16
+
17
+ export const jobHandlers = {
18
+ [JOB_NAMES.SEND_WELCOME_EMAIL]: sendWelcomeEmail,
19
+ [JOB_NAMES.SEND_RESET_PASSWORD_EMAIL]: sendResetPasswordEmail,
20
+ };
@@ -0,0 +1,2 @@
1
+ export { JOB_NAMES, JOB_QUEUES } from "./definitions.js";
2
+ export { jobHandlers } from "./handlers.js";
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@myquark/ui",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": "./src/index.js"
7
+ },
8
+ "devDependencies": {
9
+ "react": "^18.0.0"
10
+ }
11
+ }
@@ -0,0 +1,19 @@
1
+ import React from "react";
2
+
3
+ export const Button = ({ variant = "primary", className, ...props }) => {
4
+ const baseStyles = "px-4 py-2 rounded-md font-medium transition-colors";
5
+ const variants = {
6
+ primary: "bg-blue-600 text-white hover:bg-blue-700",
7
+ secondary: "bg-gray-200 text-gray-900 hover:bg-gray-300",
8
+ };
9
+
10
+ return React.createElement(
11
+ "button",
12
+ {
13
+ type: "button",
14
+ className: `${baseStyles} ${variants[variant]} ${className || ""}`,
15
+ ...props,
16
+ },
17
+ props.children,
18
+ );
19
+ };
@@ -0,0 +1,14 @@
1
+ import React from "react";
2
+
3
+ export const Card = ({ className, children, ...props }) => {
4
+ const baseStyles = "bg-white rounded-lg shadow-md p-4";
5
+
6
+ return React.createElement(
7
+ "div",
8
+ {
9
+ className: `${baseStyles} ${className || ""}`,
10
+ ...props,
11
+ },
12
+ children,
13
+ );
14
+ };
@@ -0,0 +1,3 @@
1
+ export { Button } from "./button.js";
2
+ export { Card } from "./card.js";
3
+ export { Input } from "./input.js";
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+
3
+ export const Input = ({ className, ...props }) => {
4
+ const baseStyles =
5
+ "px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500";
6
+
7
+ return React.createElement("input", {
8
+ className: `${baseStyles} ${className || ""}`,
9
+ ...props,
10
+ });
11
+ };