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.
- package/README.md +89 -0
- package/bin/index.js +1024 -0
- package/package.json +43 -0
- package/scaffold-fragments/billing/files/app/api/billing/webhook/route.ts +5 -0
- package/scaffold-fragments/billing/module.json +31 -0
- package/scaffold-fragments/billing/runtime/config.ts +8 -0
- package/scaffold-fragments/billing/runtime/exports.ts +6 -0
- package/scaffold-fragments/billing/runtime/imports.ts +6 -0
- package/scaffold-fragments/billing/runtime/ports.ts +3 -0
- package/scaffold-fragments/cms/module.json +29 -0
- package/scaffold-fragments/cms/runtime/config.ts +4 -0
- package/scaffold-fragments/cms/runtime/exports.ts +1 -0
- package/scaffold-fragments/cms/runtime/imports.ts +1 -0
- package/scaffold-fragments/email/module.json +30 -0
- package/scaffold-fragments/email/runtime/config.ts +1 -0
- package/scaffold-fragments/email/runtime/imports.ts +1 -0
- package/scaffold-fragments/email/runtime/ports.ts +1 -0
- package/scaffold-fragments/feature-flags/files/app/lib/flags.ts +48 -0
- package/scaffold-fragments/feature-flags/module.json +66 -0
- package/scaffold-fragments/i18n/files/messages/en-US.json +15 -0
- package/scaffold-fragments/i18n/module.json +9 -0
- package/scaffold-fragments/i18n/runtime/config.ts +7 -0
- package/scaffold-fragments/i18n/runtime/exports.ts +1 -0
- package/scaffold-fragments/i18n/runtime/imports.ts +2 -0
- package/scaffold-fragments/i18n/runtime/ports.ts +1 -0
- package/scaffold-fragments/identity/module.json +30 -0
- package/scaffold-fragments/identity/vendors/better-auth/files/app/api/auth/[...all]/route.ts +15 -0
- package/scaffold-fragments/identity/vendors/better-auth/files/app/lib/auth.ts +20 -0
- package/scaffold-fragments/jobs/module.json +12 -0
- package/scaffold-fragments/jobs/runtime/config.ts +4 -0
- package/scaffold-fragments/jobs/runtime/imports.ts +1 -0
- package/scaffold-fragments/jobs/runtime/ports.ts +1 -0
- package/scaffold-fragments/notifications/module.json +12 -0
- package/scaffold-fragments/notifications/runtime/config.ts +8 -0
- package/scaffold-fragments/notifications/runtime/imports.ts +5 -0
- package/scaffold-fragments/notifications/runtime/ports.ts +1 -0
- package/scaffold-fragments/organizations/module.json +12 -0
- package/scaffold-fragments/telemetry/module.json +43 -0
- package/scaffold-fragments/telemetry/runtime/config.ts +7 -0
- package/scaffold-fragments/telemetry/runtime/imports.ts +7 -0
- package/scaffold-fragments/telemetry/runtime/ports.ts +1 -0
- package/skillex/private/contribute.md +193 -0
- package/skillex/public/consumer.md +157 -0
- package/templates/client/.storybook/main.ts +77 -0
- package/templates/client/.storybook/preview.ts +12 -0
- package/templates/client/CLAUDE.md +50 -0
- package/templates/client/_claude/settings.json +107 -0
- package/templates/client/_github/PULL_REQUEST_TEMPLATE.md +19 -0
- package/templates/client/_github/dependabot.yml +48 -0
- package/templates/client/_github/workflows/ci.yml +104 -0
- package/templates/client/_gitignore +15 -0
- package/templates/client/_npmrc +1 -0
- package/templates/client/app/(app)/layout.tsx +49 -0
- package/templates/client/app/(marketing)/layout.tsx +21 -0
- package/templates/client/app/(marketing)/page.tsx +34 -0
- package/templates/client/app/api/backend/[...path]/route.ts +37 -0
- package/templates/client/app/api/dev/login/route.ts +14 -0
- package/templates/client/app/api/dev/logout/route.ts +18 -0
- package/templates/client/app/globals.css +22 -0
- package/templates/client/app/home-auth-controls.tsx +28 -0
- package/templates/client/app/layout.tsx +38 -0
- package/templates/client/app/lib/backend-client.ts +55 -0
- package/templates/client/app/lib/runtime.test.ts +38 -0
- package/templates/client/app/lib/runtime.ts +54 -0
- package/templates/client/app/protected/page.tsx +31 -0
- package/templates/client/app/theme.override.css +9 -0
- package/templates/client/instrumentation.ts +10 -0
- package/templates/client/mocks/browser.tsx +28 -0
- package/templates/client/mocks/handlers.ts +20 -0
- package/templates/client/mocks/node.ts +7 -0
- package/templates/client/next.config.ts +8 -0
- package/templates/client/oxfmt.config.ts +8 -0
- package/templates/client/oxlint.config.ts +8 -0
- package/templates/client/package.json +61 -0
- package/templates/client/postcss.config.mjs +1 -0
- package/templates/client/public/mockServiceWorker.js +349 -0
- package/templates/client/scripts/build-theme.mjs +18 -0
- package/templates/client/site.spec.ts +58 -0
- package/templates/client/skillex.json +12 -0
- package/templates/client/skills/repo.md +56 -0
- package/templates/client/stylelint.config.mjs +4 -0
- package/templates/client/tsconfig.json +11 -0
- package/templates/client/vitest.config.ts +27 -0
- package/templates/client/vitest.setup.ts +18 -0
- package/templates/full-stack/.storybook/main.ts +77 -0
- package/templates/full-stack/.storybook/preview.ts +12 -0
- package/templates/full-stack/CLAUDE.md +50 -0
- package/templates/full-stack/_claude/settings.json +107 -0
- package/templates/full-stack/_github/PULL_REQUEST_TEMPLATE.md +19 -0
- package/templates/full-stack/_github/dependabot.yml +57 -0
- package/templates/full-stack/_github/workflows/ci.yml +109 -0
- package/templates/full-stack/_github/workflows/db-check.yml +36 -0
- package/templates/full-stack/_github/workflows/db-migrate.yml +39 -0
- package/templates/full-stack/_gitignore +15 -0
- package/templates/full-stack/_npmrc +1 -0
- package/templates/full-stack/app/(app)/layout.tsx +53 -0
- package/templates/full-stack/app/(marketing)/layout.tsx +21 -0
- package/templates/full-stack/app/(marketing)/page.tsx +38 -0
- package/templates/full-stack/app/api/dev/login/route.ts +14 -0
- package/templates/full-stack/app/api/dev/logout/route.ts +18 -0
- package/templates/full-stack/app/api/widgets/route.ts +10 -0
- package/templates/full-stack/app/demo/widgets/page.tsx +86 -0
- package/templates/full-stack/app/globals.css +22 -0
- package/templates/full-stack/app/home-auth-controls.tsx +28 -0
- package/templates/full-stack/app/layout.tsx +38 -0
- package/templates/full-stack/app/lib/runtime.test.ts +38 -0
- package/templates/full-stack/app/lib/runtime.ts +54 -0
- package/templates/full-stack/app/protected/page.tsx +31 -0
- package/templates/full-stack/app/theme.override.css +9 -0
- package/templates/full-stack/db/check.ts +46 -0
- package/templates/full-stack/db/index.ts +109 -0
- package/templates/full-stack/db/migrate.ts +66 -0
- package/templates/full-stack/db/migrations/.gitkeep +0 -0
- package/templates/full-stack/db/schema.ts +20 -0
- package/templates/full-stack/drizzle.config.ts +16 -0
- package/templates/full-stack/instrumentation.ts +10 -0
- package/templates/full-stack/mocks/browser.tsx +28 -0
- package/templates/full-stack/mocks/handlers.ts +34 -0
- package/templates/full-stack/mocks/node.ts +7 -0
- package/templates/full-stack/next.config.ts +8 -0
- package/templates/full-stack/oxfmt.config.ts +8 -0
- package/templates/full-stack/oxlint.config.ts +8 -0
- package/templates/full-stack/package.json +72 -0
- package/templates/full-stack/postcss.config.mjs +1 -0
- package/templates/full-stack/public/mockServiceWorker.js +349 -0
- package/templates/full-stack/scripts/build-theme.mjs +18 -0
- package/templates/full-stack/site.spec.ts +58 -0
- package/templates/full-stack/skillex.json +12 -0
- package/templates/full-stack/skills/repo.md +56 -0
- package/templates/full-stack/stylelint.config.mjs +4 -0
- package/templates/full-stack/tsconfig.json +11 -0
- package/templates/full-stack/vitest.config.ts +42 -0
- package/templates/full-stack/vitest.setup.ts +18 -0
- package/templates/static/.storybook/main.ts +77 -0
- package/templates/static/.storybook/preview.ts +12 -0
- package/templates/static/CLAUDE.md +50 -0
- package/templates/static/_claude/settings.json +107 -0
- package/templates/static/_github/PULL_REQUEST_TEMPLATE.md +18 -0
- package/templates/static/_github/dependabot.yml +43 -0
- package/templates/static/_github/workflows/ci.yml +91 -0
- package/templates/static/_gitignore +15 -0
- package/templates/static/_npmrc +1 -0
- package/templates/static/app/(marketing)/layout.tsx +21 -0
- package/templates/static/app/(marketing)/page.tsx +17 -0
- package/templates/static/app/globals.css +22 -0
- package/templates/static/app/layout.tsx +24 -0
- package/templates/static/app/theme.override.css +9 -0
- package/templates/static/next.config.ts +19 -0
- package/templates/static/oxfmt.config.ts +8 -0
- package/templates/static/oxlint.config.ts +8 -0
- package/templates/static/package.json +53 -0
- package/templates/static/postcss.config.mjs +1 -0
- package/templates/static/scripts/build-theme.mjs +18 -0
- package/templates/static/site.spec.ts +58 -0
- package/templates/static/skillex.json +12 -0
- package/templates/static/skills/repo.md +56 -0
- package/templates/static/stylelint.config.mjs +4 -0
- 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,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 @@
|
|
|
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
|
+
}
|