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,193 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "Contributing to create-raredays-app"
|
|
3
|
+
description: Deep guide to the app scaffolder — CLI flow, CRDA placeholder tokens, composeRuntime/composeHandlers internals, template file trees, customiseSiteSpec regex, fragment loading, env generation, --dev flag.
|
|
4
|
+
topics: [scaffolding, contributing]
|
|
5
|
+
tags: [scaffolding, cli, contributing]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Contributing to create-raredays-app
|
|
9
|
+
|
|
10
|
+
Location: `workspaces/app/cli/create-raredays-app/`
|
|
11
|
+
|
|
12
|
+
## CLI Entry (`bin/index.js`)
|
|
13
|
+
|
|
14
|
+
Plain Node.js script (no build step). Uses `parseArgs` for flags:
|
|
15
|
+
|
|
16
|
+
| Flag | Default | Purpose |
|
|
17
|
+
| -------------------- | -------------- | ------------------------------------------- |
|
|
18
|
+
| `--template <name>` | `"full-stack"` | Template to scaffold |
|
|
19
|
+
| `--modules <list>` | — | Comma-separated module keys |
|
|
20
|
+
| `--vendors <list>` | — | Comma-separated `module:vendor` pairs |
|
|
21
|
+
| `--remote-url <url>` | — | Git remote origin URL |
|
|
22
|
+
| `--no-git` | — | Skip git init |
|
|
23
|
+
| `--no-install` | — | Skip pnpm install |
|
|
24
|
+
| `--dev` | — | Rewrite `@raredays/*` deps to `workspace:*` |
|
|
25
|
+
|
|
26
|
+
### Interactive Mode (no positional arg)
|
|
27
|
+
|
|
28
|
+
Prompts in order via `@clack/prompts`:
|
|
29
|
+
|
|
30
|
+
1. Project name (validates non-empty, no existing dir)
|
|
31
|
+
2. Template selection: `static` | `client` | `full-stack`
|
|
32
|
+
3. Display name (defaults to project name)
|
|
33
|
+
4. Canonical domain (validates `/^[a-z0-9.-]+\.[a-z]{2,}$/i`)
|
|
34
|
+
5. Brand primary color (hex, defaults `#10b981`, validates `/^#[0-9a-fA-F]{3,8}$/`)
|
|
35
|
+
6. Module selection (multiselect, filtered by template's `templates` field)
|
|
36
|
+
7. Vendor selection per module (format: `billing:stripe`)
|
|
37
|
+
8. Git remote URL (optional)
|
|
38
|
+
9. Init git repo (default: true)
|
|
39
|
+
10. Run `pnpm install` (default: true)
|
|
40
|
+
|
|
41
|
+
### Scaffold Flow
|
|
42
|
+
|
|
43
|
+
1. Error if target dir exists
|
|
44
|
+
2. Copy `templates/{template}/` recursively
|
|
45
|
+
3. Rename stub files: `_gitignore` → `.gitignore`, `_npmrc` → `.npmrc`
|
|
46
|
+
4. Derive slug: trim, lowercase, replace `[^a-z0-9]+` with `-`, strip leading/trailing `-`
|
|
47
|
+
5. Substitute `__SLUG__` in `site.spec.ts`
|
|
48
|
+
6. Apply `customiseSiteSpec()` regex replacements
|
|
49
|
+
7. Load fragments, merge deps, copy fragment `files/` dirs, compose runtime
|
|
50
|
+
8. Compose mock handlers if template ≠ `static`
|
|
51
|
+
9. Merge deps into `package.json`, apply `--dev` rewrite if flagged
|
|
52
|
+
10. Write `.env.example` if any env vars collected
|
|
53
|
+
11. `git init --quiet --initial-branch=main` (+ remote if specified)
|
|
54
|
+
12. `pnpm install` with `stdio: "inherit"`
|
|
55
|
+
|
|
56
|
+
## CRDA Placeholder Tokens
|
|
57
|
+
|
|
58
|
+
### In `app/lib/runtime.ts`:
|
|
59
|
+
|
|
60
|
+
| Token (exact string) | Replaced With | Empty Behavior |
|
|
61
|
+
| -------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------- |
|
|
62
|
+
| `// CRDA:imports` | Fragment `runtime/imports.ts` concatenated with newlines | Kept as-is |
|
|
63
|
+
| `// CRDA:config` | Fragment `runtime/config.ts` concatenated with double newlines | Comment: `// CRDA:config (no additional modules wired)` |
|
|
64
|
+
| ` // CRDA:ports` | Fragment `runtime/ports.ts` concatenated, trimmed (note 2-space indent) | Kept as-is |
|
|
65
|
+
| `// CRDA:exports` | Fragment `runtime/exports.ts` concatenated with double newlines | Kept as-is |
|
|
66
|
+
|
|
67
|
+
### In `mocks/handlers.ts`:
|
|
68
|
+
|
|
69
|
+
| Token (exact string) | Replaced With |
|
|
70
|
+
| ------------------------ | ------------------------------------------------------- |
|
|
71
|
+
| `// CRDA:mocks-imports` | Import statements: `import { <named> } from <package>;` |
|
|
72
|
+
| ` // CRDA:mocks-spread` | Spread entries: ` <named>,` (2-space indent) |
|
|
73
|
+
|
|
74
|
+
## composeRuntime(source, fragments)
|
|
75
|
+
|
|
76
|
+
Iterates fragments in order, collects all slot code per token, replaces all four tokens in a single pass.
|
|
77
|
+
|
|
78
|
+
## composeHandlers(source, vendorMocks)
|
|
79
|
+
|
|
80
|
+
Builds import list + spread list from vendor mock metadata, replaces both tokens.
|
|
81
|
+
|
|
82
|
+
## customiseSiteSpec(filePath, opts)
|
|
83
|
+
|
|
84
|
+
Three regex replacements on `site.spec.ts`:
|
|
85
|
+
|
|
86
|
+
1. Display name: `/name:\s*"[^"]*"/` → `name: ${JSON.stringify(opts.displayName)}`
|
|
87
|
+
2. Canonical domain: `/canonicalDomain:\s*"[^"]*/` → `canonicalDomain: ${JSON.stringify(opts.canonicalDomain)}`
|
|
88
|
+
3. Brand primary: `/primary:\s*"#[0-9a-fA-F]+"/` → `primary: ${JSON.stringify(opts.brandPrimary)}`
|
|
89
|
+
|
|
90
|
+
`__SLUG__` token is a separate find-replace earlier in the flow.
|
|
91
|
+
|
|
92
|
+
## Template File Trees
|
|
93
|
+
|
|
94
|
+
### `full-stack` (39 files)
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
app/api/dev/{login,logout}/route.ts, app/api/widgets/route.ts
|
|
98
|
+
app/demo/widgets/page.tsx, app/protected/page.tsx
|
|
99
|
+
app/globals.css, app/home-auth-controls.tsx, app/layout.tsx, app/page.tsx
|
|
100
|
+
app/lib/runtime.ts (CRDA placeholders), app/lib/runtime.test.ts
|
|
101
|
+
app/theme.override.css
|
|
102
|
+
db/index.ts, db/schema.ts, drizzle.config.ts
|
|
103
|
+
instrumentation.ts
|
|
104
|
+
mocks/browser.tsx, mocks/handlers.ts (CRDA placeholders), mocks/node.ts
|
|
105
|
+
next.config.ts, oxfmt.config.ts, oxlint.config.ts, package.json
|
|
106
|
+
postcss.config.mjs, scripts/build-theme.mjs, site.spec.ts
|
|
107
|
+
stylelint.config.mjs, tsconfig.json, vitest.config.ts, vitest.setup.ts
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### `client` (28 files)
|
|
111
|
+
|
|
112
|
+
Same as full-stack minus `db/`, `drizzle.config.ts`, `app/api/widgets/`. Adds `app/api/backend/[...path]/route.ts` for upstream proxying and `app/lib/backend-client.ts`.
|
|
113
|
+
|
|
114
|
+
### `static` (17 files)
|
|
115
|
+
|
|
116
|
+
Minimal: no `app/lib/`, no `app/api/`, no `mocks/`, no `instrumentation.ts`, no tests, no DB. Just layout, page, styling, and build config.
|
|
117
|
+
|
|
118
|
+
## Fragment Structure
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
scaffold-fragments/{key}/
|
|
122
|
+
├── module.json # Manifest
|
|
123
|
+
├── runtime/
|
|
124
|
+
│ ├── imports.ts # Injected at // CRDA:imports
|
|
125
|
+
│ ├── config.ts # Injected at // CRDA:config
|
|
126
|
+
│ ├── ports.ts # Injected at // CRDA:ports
|
|
127
|
+
│ └── exports.ts # Injected at // CRDA:exports
|
|
128
|
+
└── files/ # Overlay into target app
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### module.json Schema
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"key": "billing",
|
|
136
|
+
"label": "Billing (subscriptions + webhooks)",
|
|
137
|
+
"description": "...",
|
|
138
|
+
"deps": { "@raredays/billing": "0.0.1" },
|
|
139
|
+
"vendors": [
|
|
140
|
+
{
|
|
141
|
+
"key": "stripe",
|
|
142
|
+
"label": "Stripe",
|
|
143
|
+
"deps": { "@raredays/billing-stripe": "0.0.1" },
|
|
144
|
+
"env": ["STRIPE_SECRET_KEY", "STRIPE_WEBHOOK_SECRET"],
|
|
145
|
+
"mocks": { "package": "@raredays/billing-stripe/mocks", "named": "stripeMocks" }
|
|
146
|
+
}
|
|
147
|
+
],
|
|
148
|
+
"templates": ["full-stack"]
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Available Fragments
|
|
153
|
+
|
|
154
|
+
| Fragment | Templates | Vendors | Env Vars |
|
|
155
|
+
| --------------- | ------------------ | ------------- | -------------------------------------------- |
|
|
156
|
+
| `billing` | full-stack | Stripe | `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET` |
|
|
157
|
+
| `email` | full-stack | Resend | `RESEND_API_KEY` |
|
|
158
|
+
| `jobs` | full-stack | None | — |
|
|
159
|
+
| `notifications` | full-stack | None | — |
|
|
160
|
+
| `telemetry` | full-stack, client | Sentry, Axiom | `SENTRY_DSN`, `AXIOM_TOKEN`, `AXIOM_DATASET` |
|
|
161
|
+
|
|
162
|
+
## .env.example Generation
|
|
163
|
+
|
|
164
|
+
Only written if `envVars.length > 0`. Format:
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
# Environment variables required by the modules you selected.
|
|
168
|
+
# Copy to .env.local and fill in real values before running.
|
|
169
|
+
|
|
170
|
+
# Database (uncomment when wiring Drizzle queries)
|
|
171
|
+
# DATABASE_URL=
|
|
172
|
+
|
|
173
|
+
STRIPE_SECRET_KEY=
|
|
174
|
+
STRIPE_WEBHOOK_SECRET=
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## --dev Flag
|
|
178
|
+
|
|
179
|
+
Rewrites every `@raredays/*` in both `dependencies` and `devDependencies` to `workspace:*`. Used for in-repo dogfooding (playground scaffolds).
|
|
180
|
+
|
|
181
|
+
## Testing
|
|
182
|
+
|
|
183
|
+
No unit tests. Validated by:
|
|
184
|
+
|
|
185
|
+
- E2E scenarios in `/e2e/` (scaffold → build → serve → test)
|
|
186
|
+
- Manual: `pnpm playground:reset && pnpm playground`
|
|
187
|
+
|
|
188
|
+
## Build and Test
|
|
189
|
+
|
|
190
|
+
```sh
|
|
191
|
+
# No build step needed — plain Node.js script
|
|
192
|
+
pnpm --filter create-raredays-app test # if tests exist
|
|
193
|
+
```
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "Consuming create-raredays-app"
|
|
3
|
+
description: How to scaffold new apps — interactive prompts sequence, CLI flags, three templates (full-stack/client/static) with output files, five available modules with vendor options, --dev flag for monorepo dogfooding.
|
|
4
|
+
topics: [scaffolding, consuming]
|
|
5
|
+
tags: [scaffolding, cli, consuming]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Consuming create-raredays-app
|
|
9
|
+
|
|
10
|
+
## Interactive Mode
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
pnpm create raredays-app
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Prompts in order:
|
|
17
|
+
|
|
18
|
+
1. **Project name** — directory name and package slug (kebab-case, 1–64 chars)
|
|
19
|
+
2. **Template** — `full-stack`, `client`, or `static`
|
|
20
|
+
3. **Display name** — human-readable (defaults to project name)
|
|
21
|
+
4. **Canonical domain** — optional, hostname only (e.g., `example.com`)
|
|
22
|
+
5. **Brand primary color** — hex value (defaults to `#10b981`)
|
|
23
|
+
6. **Modules** — multiselect (not shown for `static`)
|
|
24
|
+
7. **Vendors** — multiselect for selected modules
|
|
25
|
+
8. **Git remote URL** — optional
|
|
26
|
+
9. **Initialize git?** — default yes
|
|
27
|
+
10. **Run pnpm install?** — default yes
|
|
28
|
+
|
|
29
|
+
## Non-Interactive Mode
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
pnpm create raredays-app my-app \
|
|
33
|
+
--template full-stack \
|
|
34
|
+
--modules billing,email,telemetry \
|
|
35
|
+
--vendors billing:stripe,email:resend,telemetry:sentry,telemetry:axiom
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## CLI Flags
|
|
39
|
+
|
|
40
|
+
| Flag | Description |
|
|
41
|
+
| -------------- | --------------------------------------------------------------------- |
|
|
42
|
+
| `--template` | `full-stack` (default), `client`, or `static` |
|
|
43
|
+
| `--modules` | Comma-separated module keys |
|
|
44
|
+
| `--vendors` | Comma-separated `MODULE:VENDOR` selectors |
|
|
45
|
+
| `--remote-url` | Git remote origin URL |
|
|
46
|
+
| `--no-git` | Skip git initialization |
|
|
47
|
+
| `--no-install` | Skip `pnpm install` |
|
|
48
|
+
| `--dev` | Rewrite all `@raredays/*` deps to `workspace:*` (monorepo dogfooding) |
|
|
49
|
+
|
|
50
|
+
## Templates
|
|
51
|
+
|
|
52
|
+
### full-stack
|
|
53
|
+
|
|
54
|
+
Complete Next.js + Drizzle ORM + PostgreSQL + framework modules.
|
|
55
|
+
|
|
56
|
+
Key output files:
|
|
57
|
+
|
|
58
|
+
- `app/lib/runtime.ts` — composition root with module ports wired
|
|
59
|
+
- `db/schema.ts` + `db/index.ts` — Drizzle schema and client
|
|
60
|
+
- `drizzle.config.ts` — Drizzle Kit configuration
|
|
61
|
+
- `mocks/handlers.ts` — MSW handlers composed from vendor mocks
|
|
62
|
+
- `site.spec.ts` — SiteSpec with your branding choices
|
|
63
|
+
- `app/api/widgets/route.ts` — demo API endpoint
|
|
64
|
+
- `instrumentation.ts` — telemetry instrumentation hook
|
|
65
|
+
|
|
66
|
+
All five modules available. Scripts include `db:generate`, `db:migrate`, `db:studio`.
|
|
67
|
+
|
|
68
|
+
### client
|
|
69
|
+
|
|
70
|
+
Next.js BFF + SPA that proxies to an external backend.
|
|
71
|
+
|
|
72
|
+
Key output files:
|
|
73
|
+
|
|
74
|
+
- `app/lib/runtime.ts` — composition root (identity + mocks)
|
|
75
|
+
- `app/api/backend/[...path]/route.ts` — backend proxy
|
|
76
|
+
- `app/api/dev/login|logout/route.ts` — dev auth routes
|
|
77
|
+
- `mocks/handlers.ts` — MSW handlers
|
|
78
|
+
|
|
79
|
+
Only telemetry module available. No database.
|
|
80
|
+
|
|
81
|
+
### static
|
|
82
|
+
|
|
83
|
+
CDN/edge-deployable static site. No modules, no database, no API routes.
|
|
84
|
+
|
|
85
|
+
Key output files:
|
|
86
|
+
|
|
87
|
+
- `site.spec.ts` — SiteSpec configuration
|
|
88
|
+
- `next.config.ts` — static export config
|
|
89
|
+
|
|
90
|
+
## Available Modules
|
|
91
|
+
|
|
92
|
+
| Module | Label | Template | Vendor Options |
|
|
93
|
+
| --------------- | --------------------------- | ------------------ | -------------- |
|
|
94
|
+
| `billing` | Subscriptions + webhooks | full-stack | Stripe |
|
|
95
|
+
| `cms` | Headless CMS + rich text | full-stack, client | Hygraph |
|
|
96
|
+
| `email` | Transactional email | full-stack | Resend |
|
|
97
|
+
| `identity` | Auth, sessions, orgs, roles | full-stack, client | Better Auth |
|
|
98
|
+
| `jobs` | Background jobs | full-stack | — (in-memory) |
|
|
99
|
+
| `notifications` | Multi-channel notifications | full-stack | — (in-memory) |
|
|
100
|
+
| `telemetry` | Logging, errors, metrics | full-stack, client | Sentry, Axiom |
|
|
101
|
+
|
|
102
|
+
Identity is always wired with an in-memory dev provider (the template hard-codes
|
|
103
|
+
`createInMemoryProvider` so `pnpm dev` works out of the box). Selecting
|
|
104
|
+
`--modules identity --vendors identity:better-auth` adds the production adapter
|
|
105
|
+
package, env vars, mock handlers, and a Next.js auth route stub — you then
|
|
106
|
+
manually replace the in-memory provider in `app/lib/runtime.ts` with
|
|
107
|
+
`createBetterAuthProvider({ auth })`.
|
|
108
|
+
|
|
109
|
+
### Vendor Environment Variables
|
|
110
|
+
|
|
111
|
+
When vendors are selected, `.env.example` is generated:
|
|
112
|
+
|
|
113
|
+
```sh
|
|
114
|
+
# billing (stripe)
|
|
115
|
+
STRIPE_SECRET_KEY=
|
|
116
|
+
STRIPE_WEBHOOK_SECRET=
|
|
117
|
+
|
|
118
|
+
# cms (hygraph)
|
|
119
|
+
HYGRAPH_ENDPOINT=
|
|
120
|
+
HYGRAPH_TOKEN=
|
|
121
|
+
|
|
122
|
+
# email (resend)
|
|
123
|
+
RESEND_API_KEY=
|
|
124
|
+
|
|
125
|
+
# identity (better-auth)
|
|
126
|
+
BETTER_AUTH_SECRET=
|
|
127
|
+
BETTER_AUTH_URL=
|
|
128
|
+
|
|
129
|
+
# telemetry (sentry)
|
|
130
|
+
SENTRY_DSN=
|
|
131
|
+
|
|
132
|
+
# telemetry (axiom)
|
|
133
|
+
AXIOM_TOKEN=
|
|
134
|
+
AXIOM_DATASET=
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## What You Get
|
|
138
|
+
|
|
139
|
+
A fully configured project with:
|
|
140
|
+
|
|
141
|
+
- `site.spec.ts` — SiteSpec with your name, domain, and brand color substituted
|
|
142
|
+
- `app/lib/runtime.ts` — composition root with selected modules wired via slot injection
|
|
143
|
+
- `app/theme.css` — generated `@theme` overrides from branding config
|
|
144
|
+
- `next.config.ts` — wraps `withRaredays()`
|
|
145
|
+
- `postcss.config.mjs` — re-exports from `@raredays/style/postcss`
|
|
146
|
+
- `mocks/` — MSW setup with vendor-specific handlers (client/full-stack)
|
|
147
|
+
- `.env.example` — required env vars for selected vendors
|
|
148
|
+
|
|
149
|
+
## After Scaffolding
|
|
150
|
+
|
|
151
|
+
```sh
|
|
152
|
+
cd my-app
|
|
153
|
+
pnpm install # if --no-install was used
|
|
154
|
+
cp .env.example .env.local # if modules with env vars
|
|
155
|
+
pnpm dev # standard dev server
|
|
156
|
+
pnpm dev:mocks # dev with MSW intercepting API calls
|
|
157
|
+
```
|
|
@@ -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. -->
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
|
|
3
|
+
updates:
|
|
4
|
+
- package-ecosystem: npm
|
|
5
|
+
directory: /
|
|
6
|
+
schedule:
|
|
7
|
+
interval: weekly
|
|
8
|
+
open-pull-requests-limit: 5
|
|
9
|
+
groups:
|
|
10
|
+
raredays:
|
|
11
|
+
patterns:
|
|
12
|
+
- "@raredays/*"
|
|
13
|
+
react:
|
|
14
|
+
patterns:
|
|
15
|
+
- react
|
|
16
|
+
- react-dom
|
|
17
|
+
- "@types/react"
|
|
18
|
+
- "@types/react-dom"
|
|
19
|
+
next:
|
|
20
|
+
patterns:
|
|
21
|
+
- next
|
|
22
|
+
tailwind:
|
|
23
|
+
patterns:
|
|
24
|
+
- tailwindcss
|
|
25
|
+
- "@tailwindcss/*"
|
|
26
|
+
storybook:
|
|
27
|
+
patterns:
|
|
28
|
+
- storybook
|
|
29
|
+
- "@storybook/*"
|
|
30
|
+
vitest:
|
|
31
|
+
patterns:
|
|
32
|
+
- vitest
|
|
33
|
+
- "@vitejs/*"
|
|
34
|
+
- happy-dom
|
|
35
|
+
dev-tooling:
|
|
36
|
+
dependency-type: development
|
|
37
|
+
patterns:
|
|
38
|
+
- "*"
|
|
39
|
+
|
|
40
|
+
- package-ecosystem: github-actions
|
|
41
|
+
directory: /
|
|
42
|
+
schedule:
|
|
43
|
+
interval: weekly
|
|
44
|
+
open-pull-requests-limit: 3
|
|
45
|
+
groups:
|
|
46
|
+
gha:
|
|
47
|
+
patterns:
|
|
48
|
+
- "*"
|