@techstream/quark-create-app 1.5.3 → 1.7.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 (53) hide show
  1. package/package.json +4 -2
  2. package/src/index.js +62 -14
  3. package/templates/base-project/.github/dependabot.yml +12 -0
  4. package/templates/base-project/.github/workflows/ci.yml +97 -0
  5. package/templates/base-project/.github/workflows/dependabot-auto-merge.yml +22 -0
  6. package/templates/base-project/.github/workflows/release.yml +38 -0
  7. package/templates/base-project/apps/web/biome.json +7 -0
  8. package/templates/base-project/apps/web/jsconfig.json +5 -5
  9. package/templates/base-project/apps/web/next.config.js +90 -1
  10. package/templates/base-project/apps/web/package.json +7 -7
  11. package/templates/base-project/apps/web/railway.json +15 -0
  12. package/templates/base-project/apps/web/src/app/api/auth/register/route.js +6 -7
  13. package/templates/base-project/apps/web/src/app/layout.js +3 -4
  14. package/templates/base-project/apps/web/src/app/manifest.js +12 -0
  15. package/templates/base-project/apps/web/src/app/robots.js +21 -0
  16. package/templates/base-project/apps/web/src/app/sitemap.js +20 -0
  17. package/templates/base-project/apps/web/src/lib/seo/indexing.js +23 -0
  18. package/templates/base-project/apps/web/src/lib/seo/site-metadata.js +33 -0
  19. package/templates/base-project/apps/web/src/proxy.js +1 -2
  20. package/templates/base-project/apps/worker/package.json +5 -5
  21. package/templates/base-project/apps/worker/railway.json +13 -0
  22. package/templates/base-project/apps/worker/src/index.js +30 -12
  23. package/templates/base-project/apps/worker/src/index.test.js +296 -15
  24. package/templates/base-project/biome.json +44 -0
  25. package/templates/base-project/docker-compose.yml +7 -4
  26. package/templates/base-project/package.json +1 -1
  27. package/templates/base-project/packages/db/package.json +1 -1
  28. package/templates/base-project/packages/db/prisma/schema.prisma +1 -17
  29. package/templates/base-project/packages/db/prisma.config.ts +8 -10
  30. package/templates/base-project/packages/db/scripts/seed.js +117 -30
  31. package/templates/base-project/packages/db/src/client.js +1 -18
  32. package/templates/base-project/packages/db/src/connection.js +44 -0
  33. package/templates/base-project/packages/db/src/connection.test.js +119 -0
  34. package/templates/base-project/packages/db/src/queries.js +52 -118
  35. package/templates/base-project/packages/db/src/queries.test.js +0 -29
  36. package/templates/base-project/packages/db/src/schemas.js +0 -12
  37. package/templates/base-project/pnpm-workspace.yaml +4 -0
  38. package/templates/base-project/turbo.json +5 -3
  39. package/templates/config/package.json +2 -0
  40. package/templates/config/src/environment.js +270 -0
  41. package/templates/config/src/index.js +13 -18
  42. package/templates/config/src/load-config.js +135 -0
  43. package/templates/config/src/validate-env.js +123 -16
  44. package/templates/jobs/package.json +2 -2
  45. package/templates/jobs/src/definitions.test.js +34 -0
  46. package/templates/jobs/src/index.js +1 -1
  47. package/templates/ui/package.json +4 -4
  48. package/templates/ui/src/button.test.js +23 -0
  49. package/templates/ui/src/index.js +1 -3
  50. package/templates/base-project/apps/web/src/app/api/posts/[id]/route.js +0 -65
  51. package/templates/base-project/apps/web/src/app/api/posts/route.js +0 -95
  52. package/templates/ui/src/card.js +0 -14
  53. package/templates/ui/src/input.js +0 -11
@@ -2,10 +2,10 @@
2
2
  "name": "@myquark/ui",
3
3
  "version": "1.0.0",
4
4
  "type": "module",
5
- "exports": {
6
- ".": "./src/index.js"
7
- },
8
5
  "devDependencies": {
9
- "react": "^18.0.0"
6
+ "@types/react": "^19.2.14",
7
+ "@types/react-dom": "^19.2.3",
8
+ "react": "19.2.4",
9
+ "react-dom": "19.2.4"
10
10
  }
11
11
  }
@@ -0,0 +1,23 @@
1
+ import assert from "node:assert";
2
+ import { test } from "node:test";
3
+ import { Button } from "./button.js";
4
+
5
+ test("Button - component exports correctly", () => {
6
+ assert(typeof Button === "function", "Button should be a function");
7
+ });
8
+
9
+ test("Button - component accepts props", () => {
10
+ // Test that component can be called with props
11
+ const result = Button({ variant: "primary", className: "custom" });
12
+ assert.ok(result, "Component should return an element");
13
+ });
14
+
15
+ test("Button - supports primary variant", () => {
16
+ const result = Button({ variant: "primary" });
17
+ assert.ok(result, "Primary variant should be supported");
18
+ });
19
+
20
+ test("Button - supports secondary variant", () => {
21
+ const result = Button({ variant: "secondary" });
22
+ assert.ok(result, "Secondary variant should be supported");
23
+ });
@@ -1,3 +1 @@
1
- export { Button } from "./button.js";
2
- export { Card } from "./card.js";
3
- export { Input } from "./input.js";
1
+ export * from "./button.js";
@@ -1,65 +0,0 @@
1
- import {
2
- UnauthorizedError,
3
- validateBody,
4
- withCsrfProtection,
5
- } from "@techstream/quark-core";
6
- import { post, postUpdateSchema } from "@techstream/quark-db";
7
- import { NextResponse } from "next/server";
8
- import { requireAuth } from "@/lib/auth-middleware";
9
- import { handleError } from "../../error-handler";
10
-
11
- export async function GET(_request, { params }) {
12
- try {
13
- const { id } = await params;
14
- const foundPost = await post.findById(id);
15
- if (!foundPost) {
16
- return NextResponse.json({ message: "Post not found" }, { status: 404 });
17
- }
18
- return NextResponse.json(foundPost);
19
- } catch (error) {
20
- return handleError(error);
21
- }
22
- }
23
-
24
- export const PATCH = withCsrfProtection(async (request, { params }) => {
25
- try {
26
- const session = await requireAuth();
27
- const { id } = await params;
28
-
29
- const foundPost = await post.findById(id);
30
- if (!foundPost) {
31
- return NextResponse.json({ message: "Post not found" }, { status: 404 });
32
- }
33
-
34
- if (foundPost.authorId !== session.user.id) {
35
- throw new UnauthorizedError("You can only edit your own posts");
36
- }
37
-
38
- const data = await validateBody(request, postUpdateSchema);
39
- const updatedPost = await post.update(id, data);
40
- return NextResponse.json(updatedPost);
41
- } catch (error) {
42
- return handleError(error);
43
- }
44
- });
45
-
46
- export const DELETE = withCsrfProtection(async (_request, { params }) => {
47
- try {
48
- const session = await requireAuth();
49
- const { id } = await params;
50
-
51
- const foundPost = await post.findById(id);
52
- if (!foundPost) {
53
- return NextResponse.json({ message: "Post not found" }, { status: 404 });
54
- }
55
-
56
- if (foundPost.authorId !== session.user.id) {
57
- throw new UnauthorizedError("You can only delete your own posts");
58
- }
59
-
60
- await post.delete(id);
61
- return NextResponse.json({ success: true });
62
- } catch (error) {
63
- return handleError(error);
64
- }
65
- });
@@ -1,95 +0,0 @@
1
- import {
2
- createQueryBuilder,
3
- validateBody,
4
- withCsrfProtection,
5
- } from "@techstream/quark-core";
6
- import { post, postCreateSchema } from "@techstream/quark-db";
7
- import { NextResponse } from "next/server";
8
- import { z } from "zod";
9
- import { requireAuth } from "@/lib/auth-middleware";
10
- import { handleError } from "../error-handler";
11
-
12
- const paginationSchema = z.object({
13
- page: z.coerce.number().int().min(1).default(1),
14
- limit: z.coerce.number().int().min(1).max(100).default(10),
15
- });
16
-
17
- const querySchema = paginationSchema.extend({
18
- search: z.string().optional(),
19
- status: z.enum(["draft", "published"]).optional(),
20
- authorId: z.string().optional(),
21
- sort: z.enum(["createdAt", "updatedAt", "title"]).optional(),
22
- order: z.enum(["asc", "desc"]).default("desc"),
23
- });
24
-
25
- export async function GET(request) {
26
- try {
27
- const { searchParams } = new URL(request.url);
28
- const { page, limit, search, status, authorId, sort, order } =
29
- querySchema.parse({
30
- page: searchParams.get("page") ?? undefined,
31
- limit: searchParams.get("limit") ?? undefined,
32
- search: searchParams.get("search") ?? undefined,
33
- status: searchParams.get("status") ?? undefined,
34
- authorId: searchParams.get("authorId") ?? undefined,
35
- sort: searchParams.get("sort") ?? undefined,
36
- order: searchParams.get("order") ?? undefined,
37
- });
38
-
39
- const skip = (page - 1) * limit;
40
-
41
- // Build query with filters, search, and sort
42
- const qb = createQueryBuilder({
43
- filterableFields: ["published", "authorId"],
44
- searchFields: ["title", "content"],
45
- sortableFields: ["createdAt", "updatedAt", "title"],
46
- });
47
-
48
- // Apply filters
49
- if (status === "published") {
50
- qb.filter("published", "eq", true);
51
- } else if (status === "draft") {
52
- qb.filter("published", "eq", false);
53
- }
54
-
55
- if (authorId) {
56
- qb.filter("authorId", "eq", authorId);
57
- }
58
-
59
- // Apply search
60
- if (search) {
61
- qb.search(search);
62
- }
63
-
64
- // Apply sort
65
- if (sort) {
66
- qb.sort(sort, order);
67
- }
68
-
69
- const posts = await post.findAll({
70
- skip,
71
- take: limit,
72
- where: qb.toWhere(),
73
- orderBy: qb.toOrderBy(),
74
- });
75
-
76
- return NextResponse.json(posts);
77
- } catch (error) {
78
- return handleError(error);
79
- }
80
- }
81
-
82
- export const POST = withCsrfProtection(async (request) => {
83
- try {
84
- const session = await requireAuth();
85
- const data = await validateBody(request, postCreateSchema);
86
-
87
- const newPost = await post.create({
88
- ...data,
89
- authorId: session.user.id,
90
- });
91
- return NextResponse.json(newPost, { status: 201 });
92
- } catch (error) {
93
- return handleError(error);
94
- }
95
- });
@@ -1,14 +0,0 @@
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
- };
@@ -1,11 +0,0 @@
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
- };