@usequark/quark-create-app 1.23.11 → 1.23.13

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 (89) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +26 -26
  3. package/package.json +67 -55
  4. package/src/index.js +17 -17
  5. package/src/utils.js +1 -1
  6. package/templates/base-project/.cursor/rules/quark.mdc +17 -17
  7. package/templates/base-project/.github/copilot-instructions.md +5 -5
  8. package/templates/base-project/.github/skills/project-context/SKILL.md +6 -6
  9. package/templates/base-project/CLAUDE.md +12 -12
  10. package/templates/base-project/README.md +3 -3
  11. package/templates/base-project/apps/web/next.config.js +5 -5
  12. package/templates/base-project/apps/web/package.json +5 -5
  13. package/templates/base-project/apps/web/src/app/_components/HomeThemeToggle.js +1 -1
  14. package/templates/base-project/apps/web/src/app/api/auth/apple/route.js +2 -2
  15. package/templates/base-project/apps/web/src/app/api/auth/google/route.js +2 -2
  16. package/templates/base-project/apps/web/src/app/api/auth/refresh/route.js +1 -1
  17. package/templates/base-project/apps/web/src/app/api/auth/register/route.js +3 -3
  18. package/templates/base-project/apps/web/src/app/api/auth/token/route.js +2 -2
  19. package/templates/base-project/apps/web/src/app/api/csrf/route.js +1 -1
  20. package/templates/base-project/apps/web/src/app/api/device/register/route.js +2 -2
  21. package/templates/base-project/apps/web/src/app/api/error-handler.js +1 -1
  22. package/templates/base-project/apps/web/src/app/api/files/[id]/route.js +2 -2
  23. package/templates/base-project/apps/web/src/app/api/files/route.js +2 -2
  24. package/templates/base-project/apps/web/src/app/api/health/route.js +2 -2
  25. package/templates/base-project/apps/web/src/app/api/users/[id]/route.js +2 -2
  26. package/templates/base-project/apps/web/src/app/api/users/me/route.js +2 -2
  27. package/templates/base-project/apps/web/src/app/api/users/route.js +2 -2
  28. package/templates/base-project/apps/web/src/app/auth/error/page.js +1 -1
  29. package/templates/base-project/apps/web/src/app/auth/forgot-password/page.js +1 -1
  30. package/templates/base-project/apps/web/src/app/auth/register/RegisterPageClient.js +1 -1
  31. package/templates/base-project/apps/web/src/app/auth/signin/SignInPageClient.js +1 -1
  32. package/templates/base-project/apps/web/src/app/layout.js +1 -1
  33. package/templates/base-project/apps/web/src/app/manifest.js +1 -1
  34. package/templates/base-project/apps/web/src/app/page.js +19 -19
  35. package/templates/base-project/apps/web/src/app/robots.js +1 -1
  36. package/templates/base-project/apps/web/src/app/sitemap.js +1 -1
  37. package/templates/base-project/apps/web/src/lib/analytics/umami-replay.js +1 -1
  38. package/templates/base-project/apps/web/src/lib/auth-middleware.js +1 -1
  39. package/templates/base-project/apps/web/src/lib/auth-signup.js +1 -1
  40. package/templates/base-project/apps/web/src/lib/auth.js +2 -2
  41. package/templates/base-project/apps/web/src/lib/jwt.js +1 -1
  42. package/templates/base-project/apps/web/src/lib/proxy-auth.js +1 -1
  43. package/templates/base-project/apps/web/src/lib/seo/site-metadata.js +1 -1
  44. package/templates/base-project/apps/web/src/preflight.js +3 -3
  45. package/templates/base-project/apps/web/src/proxy.js +1 -1
  46. package/templates/base-project/docs/START_HERE.md +2 -2
  47. package/templates/base-project/packages/db/package.json +2 -2
  48. package/templates/base-project/packages/db/src/client.js +3 -3
  49. package/templates/base-project/packages/db/src/umami.js +1 -1
  50. package/templates/base-project/scripts/check-loading.mjs +1 -1
  51. package/templates/base-project/scripts/doctor.js +1 -1
  52. package/templates/base-project/skills/add-endpoint/SKILL.md +1 -1
  53. package/templates/base-project/skills/ecommerce/SKILL.md +2 -2
  54. package/templates/base-project/skills/ecommerce-cart/SKILL.md +3 -3
  55. package/templates/base-project/skills/ecommerce-catalog/SKILL.md +3 -3
  56. package/templates/base-project/skills/ecommerce-checkout/SKILL.md +2 -2
  57. package/templates/base-project/skills/i18n/SKILL.md +6 -6
  58. package/templates/base-project/skills/payment/SKILL.md +8 -8
  59. package/templates/config/package.json +1 -1
  60. package/templates/config/src/app-url.js +1 -1
  61. package/templates/config/src/environment.js +1 -1
  62. package/templates/config/src/load-config.js +2 -2
  63. package/templates/config/src/redis.js +1 -1
  64. package/templates/config/src/validate-env.js +2 -2
  65. package/templates/jobs/README.md +2 -2
  66. package/templates/mobile/lib/errors.ts +1 -1
  67. package/templates/ui/src/theme-constants.js +1 -1
  68. package/templates/worker/README.md +2 -2
  69. package/templates/worker/package.json +4 -4
  70. package/templates/worker/src/handlers/email-job-validation.js +1 -1
  71. package/templates/worker/src/handlers/email.js +5 -5
  72. package/templates/worker/src/handlers/files.js +4 -4
  73. package/templates/worker/src/handlers/index.js +1 -1
  74. package/templates/worker/src/handlers/push-notification.js +3 -3
  75. package/templates/worker/src/index.js +6 -6
  76. package/templates/worker/src/lib/railway.js +2 -2
  77. package/templates/worker/src/lib/tokens.js +1 -1
  78. package/src/dependabot-config.test.js +0 -128
  79. package/src/deploy/adapters/railway.test.js +0 -318
  80. package/src/deploy/deploy.integration.test.js +0 -183
  81. package/src/deploy/deploy.test.js +0 -105
  82. package/src/deploy/discovery.test.js +0 -141
  83. package/src/mobile-template.test.js +0 -232
  84. package/src/prepare-hooks.test.js +0 -535
  85. package/src/scaffold-guards.test.js +0 -140
  86. package/src/scaffold-output.test.js +0 -228
  87. package/src/template-scripts.test.js +0 -160
  88. package/src/utils.test.js +0 -95
  89. package/src/worker-dockerfile.test.js +0 -85
@@ -1,141 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import fs from "node:fs/promises";
3
- import os from "node:os";
4
- import path from "node:path";
5
- import { after, test } from "node:test";
6
- import {
7
- discoverQuarkDeployProject,
8
- QUARK_DEPLOY_DIAGNOSTIC_CODES,
9
- resolveQuarkDeployProject,
10
- } from "./discovery.js";
11
-
12
- const temporaryDirectories = [];
13
-
14
- after(async () => {
15
- await Promise.all(
16
- temporaryDirectories.map((directory) =>
17
- fs.rm(directory, { recursive: true, force: true }),
18
- ),
19
- );
20
- });
21
-
22
- async function writeJson(filePath, value) {
23
- await fs.mkdir(path.dirname(filePath), { recursive: true });
24
- await fs.writeFile(filePath, JSON.stringify(value, null, "\t"));
25
- }
26
-
27
- async function createFixture({ hasWeb = true, hasWorker = false } = {}) {
28
- const projectDir = await fs.mkdtemp(path.join(os.tmpdir(), "quark-deploy-"));
29
- temporaryDirectories.push(projectDir);
30
-
31
- await writeJson(path.join(projectDir, "package.json"), {
32
- name: "fixture-quark-project",
33
- private: true,
34
- });
35
-
36
- if (hasWeb) {
37
- await writeJson(path.join(projectDir, "apps", "web", "package.json"), {
38
- name: "@fixture/quark-web",
39
- private: true,
40
- type: "module",
41
- });
42
- }
43
-
44
- if (hasWorker) {
45
- await writeJson(path.join(projectDir, "apps", "worker", "package.json"), {
46
- name: "@fixture/quark-worker",
47
- private: true,
48
- type: "module",
49
- });
50
- }
51
-
52
- return projectDir;
53
- }
54
-
55
- test("resolveQuarkDeployProject discovers web and worker services in a Quark monorepo", async () => {
56
- const projectDir = await createFixture({ hasWeb: true, hasWorker: true });
57
- const discovery = await resolveQuarkDeployProject(projectDir);
58
-
59
- assert.equal(discovery.kind, "quark");
60
- assert.deepEqual(
61
- discovery.services.map((service) => service.kind),
62
- ["web", "worker"],
63
- );
64
-
65
- const [webService, workerService] = discovery.services;
66
-
67
- assert.equal(webService.required, true);
68
- assert.equal(webService.packageName, "@fixture/quark-web");
69
- assert.equal(webService.relativeRootDir, "apps/web");
70
- assert.equal(
71
- webService.runtime.relativeEntrypoint,
72
- "apps/web/.next/standalone/apps/web/server.js",
73
- );
74
- assert.equal(webService.runtime.healthcheckPath, "/api/health");
75
- assert.equal(
76
- webService.runtime.entrypointPath,
77
- path.join(
78
- projectDir,
79
- "apps",
80
- "web",
81
- ".next",
82
- "standalone",
83
- "apps",
84
- "web",
85
- "server.js",
86
- ),
87
- );
88
-
89
- assert.equal(workerService.required, false);
90
- assert.equal(workerService.packageName, "@fixture/quark-worker");
91
- assert.equal(workerService.relativeRootDir, "apps/worker");
92
- assert.equal(
93
- workerService.runtime.relativeEntrypoint,
94
- "apps/worker/src/index.js",
95
- );
96
- });
97
-
98
- test("resolveQuarkDeployProject accepts a Quark monorepo without a worker service", async () => {
99
- const projectDir = await createFixture({ hasWeb: true, hasWorker: false });
100
- const discovery = await resolveQuarkDeployProject(projectDir);
101
-
102
- assert.equal(discovery.diagnostics.length, 0);
103
- assert.deepEqual(
104
- discovery.services.map((service) => service.kind),
105
- ["web"],
106
- );
107
- assert.equal(discovery.services[0].required, true);
108
- });
109
-
110
- test("discoverQuarkDeployProject reports diagnostics and resolveQuarkDeployProject rejects when web is missing", async () => {
111
- const projectDir = await createFixture({ hasWeb: false, hasWorker: true });
112
- const discovery = await discoverQuarkDeployProject(projectDir);
113
-
114
- assert.deepEqual(
115
- discovery.services.map((service) => service.kind),
116
- ["worker"],
117
- );
118
- assert.deepEqual(discovery.diagnostics, [
119
- {
120
- code: QUARK_DEPLOY_DIAGNOSTIC_CODES.MISSING_REQUIRED_SERVICE,
121
- serviceKind: "web",
122
- required: true,
123
- expectedPath: "apps/web/package.json",
124
- message: "Missing required Quark web service at apps/web/package.json.",
125
- },
126
- ]);
127
-
128
- await assert.rejects(
129
- () => resolveQuarkDeployProject(projectDir),
130
- (error) => {
131
- assert.equal(error.code, "QUARK_DEPLOY_DISCOVERY_FAILED");
132
- assert.equal(error.diagnostics.length, 1);
133
- assert.match(error.message, /apps\/web\/package\.json/);
134
- assert.deepEqual(
135
- error.discovery.services.map((service) => service.kind),
136
- ["worker"],
137
- );
138
- return true;
139
- },
140
- );
141
- });
@@ -1,232 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { readFileSync } from "node:fs";
3
- import { join } from "node:path";
4
- import { test } from "node:test";
5
-
6
- const TEMPLATES_DIR = join(import.meta.dirname, "../templates");
7
-
8
- // ---------------------------------------------------------------------------
9
- // Mobile template structure
10
- // ---------------------------------------------------------------------------
11
-
12
- test("mobile template has required files", () => {
13
- const requiredFiles = [
14
- "app.json",
15
- "package.json",
16
- "tsconfig.json",
17
- "eas.json",
18
- "lib/config.ts",
19
- "lib/auth.ts",
20
- "lib/api.ts",
21
- "lib/api-client.ts",
22
- "lib/notifications.ts",
23
- "lib/storage.ts",
24
- "hooks/use-auth.ts",
25
- "app/_layout.tsx",
26
- "app/(auth)/sign-in.tsx",
27
- "app/(auth)/sign-up.tsx",
28
- "app/(app)/index.tsx",
29
- "app/(app)/profile.tsx",
30
- ];
31
-
32
- for (const file of requiredFiles) {
33
- const path = join(TEMPLATES_DIR, "mobile", file);
34
- assert.ok(
35
- readFileSync(path, "utf-8").length > 0,
36
- `mobile template missing or empty: ${file}`,
37
- );
38
- }
39
- });
40
-
41
- // ---------------------------------------------------------------------------
42
- // Package.json transforms
43
- // ---------------------------------------------------------------------------
44
-
45
- test("mobile package.json uses @myquark scope", () => {
46
- const pkg = JSON.parse(
47
- readFileSync(join(TEMPLATES_DIR, "mobile/package.json"), "utf-8"),
48
- );
49
- assert.strictEqual(pkg.name, "@myquark/mobile");
50
- });
51
-
52
- test("mobile package.json includes expo-constants", () => {
53
- const pkg = JSON.parse(
54
- readFileSync(join(TEMPLATES_DIR, "mobile/package.json"), "utf-8"),
55
- );
56
- assert.ok(
57
- pkg.dependencies?.["expo-constants"],
58
- "mobile package.json missing expo-constants dependency",
59
- );
60
- });
61
-
62
- test("mobile package.json has no monorepo workspace refs in devDependencies", () => {
63
- const pkg = JSON.parse(
64
- readFileSync(join(TEMPLATES_DIR, "mobile/package.json"), "utf-8"),
65
- );
66
- if (pkg.devDependencies) {
67
- for (const dep of Object.keys(pkg.devDependencies)) {
68
- assert.ok(
69
- !dep.startsWith("@techstream/quark-"),
70
- `mobile devDependencies has monorepo ref: ${dep}`,
71
- );
72
- }
73
- }
74
- });
75
-
76
- // ---------------------------------------------------------------------------
77
- // App.json transforms
78
- // ---------------------------------------------------------------------------
79
-
80
- test("mobile app.json has runtimeVersion for OTA updates", () => {
81
- const appJson = JSON.parse(
82
- readFileSync(join(TEMPLATES_DIR, "mobile/app.json"), "utf-8"),
83
- );
84
- assert.deepStrictEqual(appJson.expo.runtimeVersion, {
85
- policy: "appVersion",
86
- });
87
- });
88
-
89
- test("mobile app.json has __QUARK_PROJECT_NAME__ placeholder in slug", () => {
90
- const content = readFileSync(join(TEMPLATES_DIR, "mobile/app.json"), "utf-8");
91
- assert.ok(
92
- content.includes("__QUARK_PROJECT_NAME__"),
93
- "app.json should contain __QUARK_PROJECT_NAME__ placeholder",
94
- );
95
- });
96
-
97
- test("mobile app.json has __QUARK_EAS_PROJECT_ID__ placeholder", () => {
98
- const content = readFileSync(join(TEMPLATES_DIR, "mobile/app.json"), "utf-8");
99
- assert.ok(
100
- content.includes("__QUARK_EAS_PROJECT_ID__"),
101
- "app.json should contain __QUARK_EAS_PROJECT_ID__ placeholder",
102
- );
103
- });
104
-
105
- // ---------------------------------------------------------------------------
106
- // Source code uses lazy config (not module-scope throw)
107
- // ---------------------------------------------------------------------------
108
-
109
- test("mobile config.ts uses lazy getConfig() pattern", () => {
110
- const content = readFileSync(
111
- join(TEMPLATES_DIR, "mobile/lib/config.ts"),
112
- "utf-8",
113
- );
114
- // Should export a getConfig function (lazy — not a module-scope constant)
115
- assert.ok(
116
- content.includes("export function getConfig"),
117
- "config.ts should export getConfig()",
118
- );
119
- // Should NOT have a top-level API_URL constant (the old broken pattern)
120
- assert.ok(
121
- !content.includes("const API_URL"),
122
- "config.ts should not use top-level const API_URL — use lazy getConfig()",
123
- );
124
- });
125
-
126
- // ---------------------------------------------------------------------------
127
- // Notifications use dynamic imports and shouldShowBanner
128
- // ---------------------------------------------------------------------------
129
-
130
- test("mobile notifications.ts uses dynamic imports", () => {
131
- const content = readFileSync(
132
- join(TEMPLATES_DIR, "mobile/lib/notifications.ts"),
133
- "utf-8",
134
- );
135
- assert.ok(
136
- content.includes('await import("expo-notifications")'),
137
- "notifications.ts should use dynamic import for expo-notifications",
138
- );
139
- assert.ok(
140
- content.includes('await import("expo-device")'),
141
- "notifications.ts should use dynamic import for expo-device",
142
- );
143
- });
144
-
145
- test("mobile notifications.ts uses shouldShowBanner/shouldShowList", () => {
146
- const content = readFileSync(
147
- join(TEMPLATES_DIR, "mobile/lib/notifications.ts"),
148
- "utf-8",
149
- );
150
- assert.ok(
151
- content.includes("shouldShowBanner"),
152
- "notifications.ts should use shouldShowBanner",
153
- );
154
- assert.ok(
155
- content.includes("shouldShowList"),
156
- "notifications.ts should use shouldShowList",
157
- );
158
- assert.ok(
159
- !content.includes("shouldShowAlert"),
160
- "notifications.ts should not use deprecated shouldShowAlert",
161
- );
162
- });
163
-
164
- // ---------------------------------------------------------------------------
165
- // Auth screens use TS-strict-safe error handling
166
- // ---------------------------------------------------------------------------
167
-
168
- test("mobile sign-in.tsx uses safe error handling", () => {
169
- const content = readFileSync(
170
- join(TEMPLATES_DIR, "mobile/app/(auth)/sign-in.tsx"),
171
- "utf-8",
172
- );
173
- assert.ok(
174
- content.includes("error instanceof Error"),
175
- "sign-in.tsx should use instanceof check for error handling",
176
- );
177
- });
178
-
179
- test("mobile sign-up.tsx uses safe error handling", () => {
180
- const content = readFileSync(
181
- join(TEMPLATES_DIR, "mobile/app/(auth)/sign-up.tsx"),
182
- "utf-8",
183
- );
184
- assert.ok(
185
- content.includes("error instanceof Error"),
186
- "sign-up.tsx should use instanceof check for error handling",
187
- );
188
- });
189
-
190
- // ---------------------------------------------------------------------------
191
- // use-auth uses lazy notification imports
192
- // ---------------------------------------------------------------------------
193
-
194
- test("mobile use-auth.ts uses lazy notification imports", () => {
195
- const content = readFileSync(
196
- join(TEMPLATES_DIR, "mobile/hooks/use-auth.ts"),
197
- "utf-8",
198
- );
199
- assert.ok(
200
- content.includes('import("../lib/notifications")'),
201
- "use-auth.ts should use dynamic import for notifications",
202
- );
203
- });
204
-
205
- // ---------------------------------------------------------------------------
206
- // No placeholder leaks — all __QUARK_ placeholders are in expected files
207
- // ---------------------------------------------------------------------------
208
-
209
- test("mobile template has no stray __QUARK_ placeholders in source files", () => {
210
- const sourceFiles = [
211
- "lib/config.ts",
212
- "lib/auth.ts",
213
- "lib/api.ts",
214
- "lib/api-client.ts",
215
- "lib/notifications.ts",
216
- "lib/storage.ts",
217
- "hooks/use-auth.ts",
218
- "app/_layout.tsx",
219
- "app/(auth)/sign-in.tsx",
220
- "app/(auth)/sign-up.tsx",
221
- "app/(app)/index.tsx",
222
- "app/(app)/profile.tsx",
223
- ];
224
-
225
- for (const file of sourceFiles) {
226
- const content = readFileSync(join(TEMPLATES_DIR, "mobile", file), "utf-8");
227
- assert.ok(
228
- !content.includes("__QUARK_"),
229
- `${file} contains unreplaced __QUARK_ placeholder`,
230
- );
231
- }
232
- });