create-raredays-app 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.
Files changed (158) hide show
  1. package/README.md +89 -0
  2. package/bin/index.js +1024 -0
  3. package/package.json +43 -0
  4. package/scaffold-fragments/billing/files/app/api/billing/webhook/route.ts +5 -0
  5. package/scaffold-fragments/billing/module.json +31 -0
  6. package/scaffold-fragments/billing/runtime/config.ts +8 -0
  7. package/scaffold-fragments/billing/runtime/exports.ts +6 -0
  8. package/scaffold-fragments/billing/runtime/imports.ts +6 -0
  9. package/scaffold-fragments/billing/runtime/ports.ts +3 -0
  10. package/scaffold-fragments/cms/module.json +29 -0
  11. package/scaffold-fragments/cms/runtime/config.ts +4 -0
  12. package/scaffold-fragments/cms/runtime/exports.ts +1 -0
  13. package/scaffold-fragments/cms/runtime/imports.ts +1 -0
  14. package/scaffold-fragments/email/module.json +30 -0
  15. package/scaffold-fragments/email/runtime/config.ts +1 -0
  16. package/scaffold-fragments/email/runtime/imports.ts +1 -0
  17. package/scaffold-fragments/email/runtime/ports.ts +1 -0
  18. package/scaffold-fragments/feature-flags/files/app/lib/flags.ts +48 -0
  19. package/scaffold-fragments/feature-flags/module.json +66 -0
  20. package/scaffold-fragments/i18n/files/messages/en-US.json +15 -0
  21. package/scaffold-fragments/i18n/module.json +9 -0
  22. package/scaffold-fragments/i18n/runtime/config.ts +7 -0
  23. package/scaffold-fragments/i18n/runtime/exports.ts +1 -0
  24. package/scaffold-fragments/i18n/runtime/imports.ts +2 -0
  25. package/scaffold-fragments/i18n/runtime/ports.ts +1 -0
  26. package/scaffold-fragments/identity/module.json +30 -0
  27. package/scaffold-fragments/identity/vendors/better-auth/files/app/api/auth/[...all]/route.ts +15 -0
  28. package/scaffold-fragments/identity/vendors/better-auth/files/app/lib/auth.ts +20 -0
  29. package/scaffold-fragments/jobs/module.json +12 -0
  30. package/scaffold-fragments/jobs/runtime/config.ts +4 -0
  31. package/scaffold-fragments/jobs/runtime/imports.ts +1 -0
  32. package/scaffold-fragments/jobs/runtime/ports.ts +1 -0
  33. package/scaffold-fragments/notifications/module.json +12 -0
  34. package/scaffold-fragments/notifications/runtime/config.ts +8 -0
  35. package/scaffold-fragments/notifications/runtime/imports.ts +5 -0
  36. package/scaffold-fragments/notifications/runtime/ports.ts +1 -0
  37. package/scaffold-fragments/organizations/module.json +12 -0
  38. package/scaffold-fragments/telemetry/module.json +43 -0
  39. package/scaffold-fragments/telemetry/runtime/config.ts +7 -0
  40. package/scaffold-fragments/telemetry/runtime/imports.ts +7 -0
  41. package/scaffold-fragments/telemetry/runtime/ports.ts +1 -0
  42. package/skillex/private/contribute.md +193 -0
  43. package/skillex/public/consumer.md +157 -0
  44. package/templates/client/.storybook/main.ts +77 -0
  45. package/templates/client/.storybook/preview.ts +12 -0
  46. package/templates/client/CLAUDE.md +50 -0
  47. package/templates/client/_claude/settings.json +107 -0
  48. package/templates/client/_github/PULL_REQUEST_TEMPLATE.md +19 -0
  49. package/templates/client/_github/dependabot.yml +48 -0
  50. package/templates/client/_github/workflows/ci.yml +104 -0
  51. package/templates/client/_gitignore +15 -0
  52. package/templates/client/_npmrc +1 -0
  53. package/templates/client/app/(app)/layout.tsx +49 -0
  54. package/templates/client/app/(marketing)/layout.tsx +21 -0
  55. package/templates/client/app/(marketing)/page.tsx +34 -0
  56. package/templates/client/app/api/backend/[...path]/route.ts +37 -0
  57. package/templates/client/app/api/dev/login/route.ts +14 -0
  58. package/templates/client/app/api/dev/logout/route.ts +18 -0
  59. package/templates/client/app/globals.css +22 -0
  60. package/templates/client/app/home-auth-controls.tsx +28 -0
  61. package/templates/client/app/layout.tsx +38 -0
  62. package/templates/client/app/lib/backend-client.ts +55 -0
  63. package/templates/client/app/lib/runtime.test.ts +38 -0
  64. package/templates/client/app/lib/runtime.ts +54 -0
  65. package/templates/client/app/protected/page.tsx +31 -0
  66. package/templates/client/app/theme.override.css +9 -0
  67. package/templates/client/instrumentation.ts +10 -0
  68. package/templates/client/mocks/browser.tsx +28 -0
  69. package/templates/client/mocks/handlers.ts +20 -0
  70. package/templates/client/mocks/node.ts +7 -0
  71. package/templates/client/next.config.ts +8 -0
  72. package/templates/client/oxfmt.config.ts +8 -0
  73. package/templates/client/oxlint.config.ts +8 -0
  74. package/templates/client/package.json +61 -0
  75. package/templates/client/postcss.config.mjs +1 -0
  76. package/templates/client/public/mockServiceWorker.js +349 -0
  77. package/templates/client/scripts/build-theme.mjs +18 -0
  78. package/templates/client/site.spec.ts +58 -0
  79. package/templates/client/skillex.json +12 -0
  80. package/templates/client/skills/repo.md +56 -0
  81. package/templates/client/stylelint.config.mjs +4 -0
  82. package/templates/client/tsconfig.json +11 -0
  83. package/templates/client/vitest.config.ts +27 -0
  84. package/templates/client/vitest.setup.ts +18 -0
  85. package/templates/full-stack/.storybook/main.ts +77 -0
  86. package/templates/full-stack/.storybook/preview.ts +12 -0
  87. package/templates/full-stack/CLAUDE.md +50 -0
  88. package/templates/full-stack/_claude/settings.json +107 -0
  89. package/templates/full-stack/_github/PULL_REQUEST_TEMPLATE.md +19 -0
  90. package/templates/full-stack/_github/dependabot.yml +57 -0
  91. package/templates/full-stack/_github/workflows/ci.yml +109 -0
  92. package/templates/full-stack/_github/workflows/db-check.yml +36 -0
  93. package/templates/full-stack/_github/workflows/db-migrate.yml +39 -0
  94. package/templates/full-stack/_gitignore +15 -0
  95. package/templates/full-stack/_npmrc +1 -0
  96. package/templates/full-stack/app/(app)/layout.tsx +53 -0
  97. package/templates/full-stack/app/(marketing)/layout.tsx +21 -0
  98. package/templates/full-stack/app/(marketing)/page.tsx +38 -0
  99. package/templates/full-stack/app/api/dev/login/route.ts +14 -0
  100. package/templates/full-stack/app/api/dev/logout/route.ts +18 -0
  101. package/templates/full-stack/app/api/widgets/route.ts +10 -0
  102. package/templates/full-stack/app/demo/widgets/page.tsx +86 -0
  103. package/templates/full-stack/app/globals.css +22 -0
  104. package/templates/full-stack/app/home-auth-controls.tsx +28 -0
  105. package/templates/full-stack/app/layout.tsx +38 -0
  106. package/templates/full-stack/app/lib/runtime.test.ts +38 -0
  107. package/templates/full-stack/app/lib/runtime.ts +54 -0
  108. package/templates/full-stack/app/protected/page.tsx +31 -0
  109. package/templates/full-stack/app/theme.override.css +9 -0
  110. package/templates/full-stack/db/check.ts +46 -0
  111. package/templates/full-stack/db/index.ts +109 -0
  112. package/templates/full-stack/db/migrate.ts +66 -0
  113. package/templates/full-stack/db/migrations/.gitkeep +0 -0
  114. package/templates/full-stack/db/schema.ts +20 -0
  115. package/templates/full-stack/drizzle.config.ts +16 -0
  116. package/templates/full-stack/instrumentation.ts +10 -0
  117. package/templates/full-stack/mocks/browser.tsx +28 -0
  118. package/templates/full-stack/mocks/handlers.ts +34 -0
  119. package/templates/full-stack/mocks/node.ts +7 -0
  120. package/templates/full-stack/next.config.ts +8 -0
  121. package/templates/full-stack/oxfmt.config.ts +8 -0
  122. package/templates/full-stack/oxlint.config.ts +8 -0
  123. package/templates/full-stack/package.json +72 -0
  124. package/templates/full-stack/postcss.config.mjs +1 -0
  125. package/templates/full-stack/public/mockServiceWorker.js +349 -0
  126. package/templates/full-stack/scripts/build-theme.mjs +18 -0
  127. package/templates/full-stack/site.spec.ts +58 -0
  128. package/templates/full-stack/skillex.json +12 -0
  129. package/templates/full-stack/skills/repo.md +56 -0
  130. package/templates/full-stack/stylelint.config.mjs +4 -0
  131. package/templates/full-stack/tsconfig.json +11 -0
  132. package/templates/full-stack/vitest.config.ts +42 -0
  133. package/templates/full-stack/vitest.setup.ts +18 -0
  134. package/templates/static/.storybook/main.ts +77 -0
  135. package/templates/static/.storybook/preview.ts +12 -0
  136. package/templates/static/CLAUDE.md +50 -0
  137. package/templates/static/_claude/settings.json +107 -0
  138. package/templates/static/_github/PULL_REQUEST_TEMPLATE.md +18 -0
  139. package/templates/static/_github/dependabot.yml +43 -0
  140. package/templates/static/_github/workflows/ci.yml +91 -0
  141. package/templates/static/_gitignore +15 -0
  142. package/templates/static/_npmrc +1 -0
  143. package/templates/static/app/(marketing)/layout.tsx +21 -0
  144. package/templates/static/app/(marketing)/page.tsx +17 -0
  145. package/templates/static/app/globals.css +22 -0
  146. package/templates/static/app/layout.tsx +24 -0
  147. package/templates/static/app/theme.override.css +9 -0
  148. package/templates/static/next.config.ts +19 -0
  149. package/templates/static/oxfmt.config.ts +8 -0
  150. package/templates/static/oxlint.config.ts +8 -0
  151. package/templates/static/package.json +53 -0
  152. package/templates/static/postcss.config.mjs +1 -0
  153. package/templates/static/scripts/build-theme.mjs +18 -0
  154. package/templates/static/site.spec.ts +58 -0
  155. package/templates/static/skillex.json +12 -0
  156. package/templates/static/skills/repo.md +56 -0
  157. package/templates/static/stylelint.config.mjs +4 -0
  158. package/templates/static/tsconfig.json +11 -0
@@ -0,0 +1,77 @@
1
+ import { dirname, resolve } from "node:path";
2
+ import { createRequire } from "node:module";
3
+ import type { StorybookConfig } from "@storybook/react-vite";
4
+ import react from "@vitejs/plugin-react";
5
+ import tailwindcss from "@tailwindcss/vite";
6
+
7
+ // Storybook config for the scaffolded app.
8
+ //
9
+ // Loads stories from two sources, namespaced separately:
10
+ //
11
+ // App/<...> → ../app/**/*.stories.@(ts|tsx)
12
+ // ../src/**/*.stories.@(ts|tsx)
13
+ //
14
+ // @raredays/components/<...> → resolved-package-root/dist/**/*.stories.js
15
+ //
16
+ // Framework stories are pre-compiled by the framework's build (vite
17
+ // `preserveModules: true`) — `@/` aliases are already resolved, so
18
+ // the consumer's Vite doesn't need any path-alias config to load
19
+ // them. To add more framework packages with stories, add a matching
20
+ // stories[] entry pointing at their dist/.
21
+ //
22
+ // We resolve `@raredays/components` via Node's module resolver rather
23
+ // than a literal `../node_modules/...` path because pnpm hoists
24
+ // workspace dependencies to the workspace root — under a workspace
25
+ // install the package may not exist in the app's own node_modules at
26
+ // all, and the literal path silently matches zero stories.
27
+ //
28
+ // `require.resolve("@raredays/components")` returns the package's
29
+ // main entry (`dist/index.js`); we walk up to the package root so the
30
+ // glob can target `dist/**/*.stories.js`. We avoid resolving
31
+ // `@raredays/components/package.json` directly because the package's
32
+ // `exports` map does not expose `./package.json`.
33
+ //
34
+ // Stories render in THIS Storybook's iframe — they inherit the
35
+ // consumer's theme.override.css, RaredaysProvider, fonts, and any
36
+ // other context loaded in .storybook/preview.ts. That's the whole
37
+ // point: see how framework components look with YOUR theme applied,
38
+ // not a generic one.
39
+ const require = createRequire(import.meta.url);
40
+ const componentsRoot = dirname(dirname(require.resolve("@raredays/components")));
41
+
42
+ const config: StorybookConfig = {
43
+ framework: "@storybook/react-vite",
44
+ stories: [
45
+ {
46
+ directory: "../app",
47
+ files: "**/*.stories.@(ts|tsx)",
48
+ titlePrefix: "App",
49
+ },
50
+ {
51
+ directory: "../src",
52
+ files: "**/*.stories.@(ts|tsx)",
53
+ titlePrefix: "App",
54
+ },
55
+ {
56
+ directory: `${componentsRoot}/dist`,
57
+ files: "**/*.stories.js",
58
+ titlePrefix: "@raredays/components",
59
+ },
60
+ ],
61
+ addons: [],
62
+ typescript: {
63
+ check: false,
64
+ reactDocgen: "react-docgen-typescript",
65
+ },
66
+ viteFinal: async (viteConfig) => {
67
+ viteConfig.plugins = [...(viteConfig.plugins ?? []), react(), tailwindcss()];
68
+ viteConfig.resolve ??= {};
69
+ viteConfig.resolve.alias = {
70
+ ...(viteConfig.resolve.alias as Record<string, string>),
71
+ "@": resolve(import.meta.dirname, "../app"),
72
+ };
73
+ return viteConfig;
74
+ },
75
+ };
76
+
77
+ export default config;
@@ -0,0 +1,12 @@
1
+ import type { Preview } from "@storybook/react-vite";
2
+ import "../app/globals.css";
3
+
4
+ const preview: Preview = {
5
+ parameters: {
6
+ controls: {
7
+ matchers: { color: /(background|color)$/i, date: /Date$/i },
8
+ },
9
+ },
10
+ };
11
+
12
+ export default preview;
@@ -0,0 +1,50 @@
1
+ # Project conventions
2
+
3
+ Always read `AGENTS.md` first — it lists every skill available via `@raredays/*`
4
+ packages. Use them before authoring components, modules, or providers from
5
+ scratch.
6
+
7
+ ## Quick orientation
8
+
9
+ This app was scaffolded with `create-raredays-app` and consumes the RareDays
10
+ framework via `@raredays/*` workspace deps. Conventions:
11
+
12
+ - **Components**: prefer composing from `@raredays/components` (shadcn-flavored,
13
+ Tailwind v4, browser-tested with vitest+playwright). When you do author new
14
+ components, follow the patterns documented in
15
+ `pnpm exec skillex query --package @raredays/components`.
16
+ - **Server logic**: use `@raredays/core`'s provider/port pattern. Identity,
17
+ billing, notifications, CMS, etc. are wired in `app/lib/runtime.ts` —
18
+ edit there to swap providers, don't sprinkle SDK calls through the codebase.
19
+ - **Tests**: vitest. Browser-mode tests use the `.browser.test.tsx` suffix and
20
+ run separately from node-mode tests. Conformance harnesses live in each
21
+ module's `src/testing/` directory and exercise every provider against the
22
+ same invariants.
23
+ - **Lint/format**: `pnpm check` runs lint + format-check + style + typecheck.
24
+ Pre-commit hook (if you add one) should call this.
25
+
26
+ ## Don't
27
+
28
+ - Re-implement primitives that already exist in `@raredays/*`. Always query
29
+ skillex first.
30
+ - Edit code in `node_modules/@raredays/*` directly. If a provider needs
31
+ changing, file a framework issue or PR upstream.
32
+ - Commit code with failing lint/format/style/test. `pnpm check` should be
33
+ green before every commit.
34
+
35
+ ## Skillex
36
+
37
+ Skills from installed `@raredays/*` packages are auto-discovered. To refresh
38
+ the index after adding a new dependency:
39
+
40
+ ```sh
41
+ pnpm exec skillex refresh
42
+ ```
43
+
44
+ Query patterns:
45
+
46
+ ```sh
47
+ pnpm exec skillex query --search "<concepts>"
48
+ pnpm exec skillex query --package "@raredays/components"
49
+ pnpm exec skillex query --topic identity
50
+ ```
@@ -0,0 +1,107 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/claude-code-settings.json",
3
+ "_comment": "Permissive defaults — agents can iterate without prompting on routine dev actions. Anything that mutates shared/remote state still requires explicit approval.",
4
+ "permissions": {
5
+ "allow": [
6
+ "Bash(pnpm install*)",
7
+ "Bash(pnpm run *)",
8
+ "Bash(pnpm exec *)",
9
+ "Bash(pnpm dev*)",
10
+ "Bash(pnpm build*)",
11
+ "Bash(pnpm test*)",
12
+ "Bash(pnpm lint*)",
13
+ "Bash(pnpm format*)",
14
+ "Bash(pnpm style*)",
15
+ "Bash(pnpm check*)",
16
+ "Bash(pnpm typecheck*)",
17
+ "Bash(pnpm db:*)",
18
+ "Bash(pnpm theme:build*)",
19
+ "Bash(pnpm dlx *)",
20
+ "Bash(pnpm --filter *)",
21
+ "Bash(pnpm -w *)",
22
+ "Bash(node *)",
23
+ "Bash(npx *)",
24
+ "Bash(git status*)",
25
+ "Bash(git diff*)",
26
+ "Bash(git log*)",
27
+ "Bash(git show*)",
28
+ "Bash(git branch*)",
29
+ "Bash(git ls-files*)",
30
+ "Bash(git ls-tree*)",
31
+ "Bash(git stash*)",
32
+ "Bash(git add*)",
33
+ "Bash(git checkout *)",
34
+ "Bash(git switch *)",
35
+ "Bash(git restore *)",
36
+ "Bash(git rev-parse*)",
37
+ "Bash(git remote -v)",
38
+ "Bash(git fetch*)",
39
+ "Bash(git pull --ff-only*)",
40
+ "Bash(mkdir *)",
41
+ "Bash(touch *)",
42
+ "Bash(ls *)",
43
+ "Bash(find *)",
44
+ "Bash(grep *)",
45
+ "Bash(rg *)",
46
+ "Bash(cat *)",
47
+ "Bash(head *)",
48
+ "Bash(tail *)",
49
+ "Bash(wc *)",
50
+ "Bash(sort *)",
51
+ "Bash(uniq *)",
52
+ "Bash(jq *)",
53
+ "Bash(curl http://localhost*)",
54
+ "Bash(curl http://127.0.0.1*)",
55
+ "Bash(pnpm exec skillex*)",
56
+ "Bash(pnpm exec playwright *)",
57
+ "Bash(pnpm exec vitest*)",
58
+ "Bash(pnpm exec drizzle-kit*)",
59
+ "Bash(pnpm exec next *)",
60
+ "Bash(pnpm exec tsc*)",
61
+ "Bash(pnpm exec oxlint*)",
62
+ "Bash(pnpm exec oxfmt*)",
63
+ "Bash(pnpm exec stylelint*)",
64
+ "Read",
65
+ "Edit",
66
+ "Write",
67
+ "Glob",
68
+ "Grep",
69
+ "WebFetch"
70
+ ],
71
+ "ask": [
72
+ "Bash(git commit*)",
73
+ "Bash(git push*)",
74
+ "Bash(git merge*)",
75
+ "Bash(git rebase*)",
76
+ "Bash(git reset*)",
77
+ "Bash(gh issue*)",
78
+ "Bash(gh pr*)",
79
+ "Bash(gh release*)",
80
+ "Bash(gh workflow*)",
81
+ "Bash(pnpm publish*)",
82
+ "Bash(pnpm nx release*)",
83
+ "Bash(npm publish*)"
84
+ ],
85
+ "deny": [
86
+ "Bash(git push --force*)",
87
+ "Bash(git push -f *)",
88
+ "Bash(git push * --force*)",
89
+ "Bash(rm -rf /*)",
90
+ "Bash(rm -rf ~)",
91
+ "Bash(rm -rf $HOME*)",
92
+ "Bash(cat .env*)",
93
+ "Bash(cat *.env)",
94
+ "Bash(cat *.env.local)",
95
+ "Bash(cat *.env.production)",
96
+ "Bash(printenv)",
97
+ "Bash(env)",
98
+ "Read(./.env)",
99
+ "Read(./.env.local)",
100
+ "Read(./.env.production)",
101
+ "Read(./.env.*)",
102
+ "Read(./**/.env)",
103
+ "Read(./**/.env.local)",
104
+ "Read(./**/.env.production)"
105
+ ]
106
+ }
107
+ }
@@ -0,0 +1,19 @@
1
+ <!-- Brief description of what this PR does and why. -->
2
+
3
+ ## Summary
4
+
5
+ -
6
+ -
7
+
8
+ ## Verification
9
+
10
+ - [ ] `pnpm lint` passes
11
+ - [ ] `pnpm format:check` passes
12
+ - [ ] `pnpm style` passes
13
+ - [ ] `pnpm typecheck` passes
14
+ - [ ] `pnpm test` passes
15
+ - [ ] `pnpm build` succeeds
16
+
17
+ ## Notes
18
+
19
+ <!-- Anything reviewers should know — migrations, env changes, follow-ups. -->
@@ -0,0 +1,57 @@
1
+ version: 2
2
+
3
+ # Dependabot for consumer apps scaffolded from create-raredays-app.
4
+ # Weekly cadence with grouped PRs to minimise noise — RareDays
5
+ # packages move together, and dev tooling churns enough that grouped
6
+ # updates are easier to review than per-package.
7
+
8
+ updates:
9
+ - package-ecosystem: npm
10
+ directory: /
11
+ schedule:
12
+ interval: weekly
13
+ open-pull-requests-limit: 5
14
+ groups:
15
+ raredays:
16
+ patterns:
17
+ - "@raredays/*"
18
+ react:
19
+ patterns:
20
+ - react
21
+ - react-dom
22
+ - "@types/react"
23
+ - "@types/react-dom"
24
+ next:
25
+ patterns:
26
+ - next
27
+ tailwind:
28
+ patterns:
29
+ - tailwindcss
30
+ - "@tailwindcss/*"
31
+ storybook:
32
+ patterns:
33
+ - storybook
34
+ - "@storybook/*"
35
+ vitest:
36
+ patterns:
37
+ - vitest
38
+ - "@vitejs/*"
39
+ - happy-dom
40
+ drizzle:
41
+ patterns:
42
+ - drizzle-kit
43
+ - drizzle-orm
44
+ dev-tooling:
45
+ dependency-type: development
46
+ patterns:
47
+ - "*"
48
+
49
+ - package-ecosystem: github-actions
50
+ directory: /
51
+ schedule:
52
+ interval: weekly
53
+ open-pull-requests-limit: 3
54
+ groups:
55
+ gha:
56
+ patterns:
57
+ - "*"
@@ -0,0 +1,109 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ concurrency:
10
+ group: ci-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ # Mirrors the RareDays framework's CI discipline for consumer apps.
14
+ # Each verification target runs as its own job so they execute in
15
+ # parallel — wall-clock equals the slowest target, not the sum.
16
+ #
17
+ # Shared setup (checkout + pnpm + node + install) is duplicated across
18
+ # jobs by design: it's cheap (~30s with pnpm cache) and avoids needing
19
+ # an upload-artifact dance between jobs.
20
+
21
+ jobs:
22
+ lint:
23
+ name: Lint
24
+ runs-on: ubuntu-latest
25
+ timeout-minutes: 10
26
+ steps:
27
+ - uses: actions/checkout@v4
28
+ - uses: pnpm/action-setup@v4
29
+ - uses: actions/setup-node@v4
30
+ with: { node-version: 24, cache: pnpm }
31
+ - run: pnpm install --frozen-lockfile
32
+ - run: pnpm lint
33
+
34
+ format-check:
35
+ name: Format check
36
+ runs-on: ubuntu-latest
37
+ timeout-minutes: 10
38
+ steps:
39
+ - uses: actions/checkout@v4
40
+ - uses: pnpm/action-setup@v4
41
+ - uses: actions/setup-node@v4
42
+ with: { node-version: 24, cache: pnpm }
43
+ - run: pnpm install --frozen-lockfile
44
+ - run: pnpm format:check
45
+
46
+ style:
47
+ name: Style
48
+ runs-on: ubuntu-latest
49
+ timeout-minutes: 10
50
+ steps:
51
+ - uses: actions/checkout@v4
52
+ - uses: pnpm/action-setup@v4
53
+ - uses: actions/setup-node@v4
54
+ with: { node-version: 24, cache: pnpm }
55
+ - run: pnpm install --frozen-lockfile
56
+ - run: pnpm style
57
+
58
+ typecheck:
59
+ name: Typecheck
60
+ runs-on: ubuntu-latest
61
+ timeout-minutes: 10
62
+ steps:
63
+ - uses: actions/checkout@v4
64
+ - uses: pnpm/action-setup@v4
65
+ - uses: actions/setup-node@v4
66
+ with: { node-version: 24, cache: pnpm }
67
+ - run: pnpm install --frozen-lockfile
68
+ - run: pnpm theme:build
69
+ - run: pnpm typecheck
70
+
71
+ test:
72
+ name: Test
73
+ runs-on: ubuntu-latest
74
+ timeout-minutes: 15
75
+ steps:
76
+ - uses: actions/checkout@v4
77
+ - uses: pnpm/action-setup@v4
78
+ - uses: actions/setup-node@v4
79
+ with: { node-version: 24, cache: pnpm }
80
+ - run: pnpm install --frozen-lockfile
81
+ - run: pnpm theme:build
82
+ - run: pnpm test
83
+
84
+ build:
85
+ name: Build
86
+ runs-on: ubuntu-latest
87
+ timeout-minutes: 15
88
+ steps:
89
+ - uses: actions/checkout@v4
90
+ - uses: pnpm/action-setup@v4
91
+ - uses: actions/setup-node@v4
92
+ with: { node-version: 24, cache: pnpm }
93
+ - run: pnpm install --frozen-lockfile
94
+ - run: pnpm build
95
+
96
+ storybook:
97
+ name: Storybook build
98
+ runs-on: ubuntu-latest
99
+ timeout-minutes: 15
100
+ # Only run if storybook is configured.
101
+ if: hashFiles('.storybook/main.ts', '.storybook/main.js') != ''
102
+ steps:
103
+ - uses: actions/checkout@v4
104
+ - uses: pnpm/action-setup@v4
105
+ - uses: actions/setup-node@v4
106
+ with: { node-version: 24, cache: pnpm }
107
+ - run: pnpm install --frozen-lockfile
108
+ - run: pnpm theme:build
109
+ - run: pnpm storybook:build
@@ -0,0 +1,36 @@
1
+ # Validates Drizzle migrations on every pull request.
2
+ #
3
+ # Runs `pnpm db:check`, which redirects Drizzle's bookkeeping table to a
4
+ # scratch schema, applies all pending migrations, then drops the schema.
5
+ # If the migrations don't parse or the bookkeeping plumbing fails, the
6
+ # job blocks the PR.
7
+ #
8
+ # Uses in-process PGlite when DATABASE_URL is not configured as a repo
9
+ # secret — that's usually fine for structural validation. To run against
10
+ # a real Postgres on PR (catches data-dependent issues), add a
11
+ # `DATABASE_URL` secret pointing at a clone of production.
12
+ name: db-check
13
+
14
+ on:
15
+ pull_request:
16
+ paths:
17
+ - "db/**"
18
+ - "drizzle.config.ts"
19
+ - "package.json"
20
+ - ".github/workflows/db-check.yml"
21
+
22
+ jobs:
23
+ db-check:
24
+ runs-on: ubuntu-latest
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+ - uses: pnpm/action-setup@v4
28
+ - uses: actions/setup-node@v4
29
+ with:
30
+ node-version: 24
31
+ cache: pnpm
32
+ - run: pnpm install --frozen-lockfile
33
+ - name: Validate migrations
34
+ env:
35
+ DATABASE_URL: ${{ secrets.DATABASE_URL_STAGING }}
36
+ run: pnpm db:check
@@ -0,0 +1,39 @@
1
+ # Applies Drizzle migrations to the production database after a merge
2
+ # to `main`. Gated on a GitHub Environment named `production` — configure
3
+ # required reviewers there to enforce manual approval.
4
+ #
5
+ # Also runnable manually via workflow_dispatch (e.g. to re-run a failed
6
+ # migrate after a transient outage).
7
+ name: db-migrate
8
+
9
+ on:
10
+ push:
11
+ branches: [main]
12
+ paths:
13
+ - "db/migrations/**"
14
+ - "db/migrate.ts"
15
+ - ".github/workflows/db-migrate.yml"
16
+ workflow_dispatch:
17
+
18
+ concurrency:
19
+ # Advisory lock at the workflow layer too — defense in depth alongside
20
+ # safeMigrate's per-database advisory lock.
21
+ group: db-migrate-production
22
+ cancel-in-progress: false
23
+
24
+ jobs:
25
+ migrate:
26
+ runs-on: ubuntu-latest
27
+ environment: production
28
+ steps:
29
+ - uses: actions/checkout@v4
30
+ - uses: pnpm/action-setup@v4
31
+ - uses: actions/setup-node@v4
32
+ with:
33
+ node-version: 24
34
+ cache: pnpm
35
+ - run: pnpm install --frozen-lockfile
36
+ - name: Apply migrations
37
+ env:
38
+ DATABASE_URL: ${{ secrets.DATABASE_URL }}
39
+ run: pnpm db:migrate
@@ -0,0 +1,15 @@
1
+ node_modules/
2
+ .next/
3
+ out/
4
+ dist/
5
+ *.tsbuildinfo
6
+ next-env.d.ts
7
+
8
+ .env
9
+ .env.local
10
+ .env.*.local
11
+
12
+ .skillex/
13
+
14
+ .DS_Store
15
+ *.log
@@ -0,0 +1 @@
1
+ @raredays:registry=http://localhost:4873
@@ -0,0 +1,53 @@
1
+ import {
2
+ AppLayout,
3
+ AppTopBar,
4
+ Sidebar,
5
+ SidebarContent,
6
+ SidebarGroup,
7
+ SidebarGroupLabel,
8
+ SidebarGroupContent,
9
+ SidebarMenu,
10
+ SidebarMenuItem,
11
+ SidebarMenuButton,
12
+ SidebarHeader,
13
+ } from "@raredays/components";
14
+ import type { NavItem } from "@raredays/site-config";
15
+ import type { ReactNode } from "react";
16
+ import siteSpec from "../../site.spec";
17
+
18
+ export default function AppGroupLayout({ children }: { children: ReactNode }) {
19
+ const primaryNav = (siteSpec.navigation?.primary ?? []) as readonly NavItem[];
20
+
21
+ return (
22
+ <AppLayout
23
+ sidebar={
24
+ <Sidebar>
25
+ <SidebarHeader>
26
+ <span className="px-2 text-lg font-bold">RareDays</span>
27
+ </SidebarHeader>
28
+ <SidebarContent>
29
+ <SidebarGroup>
30
+ <SidebarGroupLabel>App</SidebarGroupLabel>
31
+ <SidebarGroupContent>
32
+ <SidebarMenu>
33
+ {primaryNav.map((item) => (
34
+ <SidebarMenuItem key={item.href}>
35
+ <SidebarMenuButton asChild>
36
+ <a href={item.href}>
37
+ <span>{item.label}</span>
38
+ </a>
39
+ </SidebarMenuButton>
40
+ </SidebarMenuItem>
41
+ ))}
42
+ </SidebarMenu>
43
+ </SidebarGroupContent>
44
+ </SidebarGroup>
45
+ </SidebarContent>
46
+ </Sidebar>
47
+ }
48
+ topbar={<AppTopBar />}
49
+ >
50
+ {children}
51
+ </AppLayout>
52
+ );
53
+ }
@@ -0,0 +1,21 @@
1
+ import { MarketingLayout, MarketingHeader, MarketingFooter } from "@raredays/components";
2
+ import type { ReactNode } from "react";
3
+
4
+ export default function MarketingGroupLayout({ children }: { children: ReactNode }) {
5
+ return (
6
+ <MarketingLayout
7
+ header={
8
+ <MarketingHeader
9
+ logo={<span className="text-lg font-bold">RareDays</span>}
10
+ navigation={[
11
+ { label: "Features", href: "/#features" },
12
+ { label: "Pricing", href: "/#pricing" },
13
+ ]}
14
+ />
15
+ }
16
+ footer={<MarketingFooter copyright={`© ${new Date().getFullYear()} RareDays`} />}
17
+ >
18
+ {children}
19
+ </MarketingLayout>
20
+ );
21
+ }
@@ -0,0 +1,38 @@
1
+ import { HomeAuthControls } from "../home-auth-controls";
2
+
3
+ export default function HomePage(): React.ReactElement {
4
+ return (
5
+ <main className="mx-auto max-w-2xl p-8 font-sans">
6
+ <h1 className="text-3xl font-bold">RareDays</h1>
7
+ <p className="mt-4 text-neutral-600">
8
+ Scaffolded with <code>create-raredays-app</code>. Identity is wired through{" "}
9
+ <code>app/lib/runtime.ts</code> using the in-memory provider — swap it for{" "}
10
+ <code>@raredays/identity-better-auth</code> in production.
11
+ </p>
12
+ <HomeAuthControls />
13
+ <h2 className="mt-10 text-lg font-semibold">Next steps</h2>
14
+ <ul className="mt-3 list-disc space-y-1 pl-5 text-sm text-neutral-700">
15
+ <li>
16
+ Edit <code>site.spec.ts</code> to brand your site; <code>pnpm theme:build</code>{" "}
17
+ regenerates the override CSS.
18
+ </li>
19
+ <li>
20
+ Edit <code>app/lib/runtime.ts</code> to wire billing, jobs, notifications, email, or
21
+ telemetry. The composition root holds every port; modules read from it.
22
+ </li>
23
+ <li>
24
+ Run <code>pnpm test</code> to execute the sample test that exercises the protected page
25
+ using the in-memory provider.
26
+ </li>
27
+ <li>
28
+ Visit{" "}
29
+ <a className="underline" href="/demo/widgets">
30
+ /demo/widgets
31
+ </a>{" "}
32
+ for a <code>defineAsyncLoad</code> example — run <code>pnpm dev:mocks</code> to see it
33
+ backed by canned MSW data.
34
+ </li>
35
+ </ul>
36
+ </main>
37
+ );
38
+ }
@@ -0,0 +1,14 @@
1
+ import { NextResponse } from "next/server";
2
+ import { SEEDED } from "@/lib/runtime";
3
+
4
+ // Dev-only: drop a cookie matching the seeded session. In production this is
5
+ // replaced by Better Auth's sign-in flow at /api/auth/*.
6
+ export async function POST(): Promise<NextResponse> {
7
+ const res = NextResponse.json({ ok: true, userId: SEEDED.user.id });
8
+ res.cookies.set("rd-dev-session", SEEDED.sessionId, {
9
+ httpOnly: true,
10
+ sameSite: "lax",
11
+ path: "/",
12
+ });
13
+ return res;
14
+ }
@@ -0,0 +1,18 @@
1
+ import { NextResponse, type NextRequest } from "next/server";
2
+
3
+ export async function POST(request: NextRequest): Promise<NextResponse> {
4
+ // Progressive enhancement: an HTML form post (Accept: text/html) gets a
5
+ // 303 redirect back to /sign-in; a fetch() call (Accept: application/json)
6
+ // gets the original JSON response.
7
+ const wantsHtml = request.headers.get("accept")?.includes("text/html") ?? false;
8
+
9
+ if (wantsHtml) {
10
+ const res = NextResponse.redirect(new URL("/sign-in", request.url), 303);
11
+ res.cookies.delete("rd-dev-session");
12
+ return res;
13
+ }
14
+
15
+ const res = NextResponse.json({ ok: true });
16
+ res.cookies.delete("rd-dev-session");
17
+ return res;
18
+ }