create-raredays-app 0.1.4 → 0.1.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-raredays-app",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Scaffold a new RareDays Next.js app.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "Jeremy Harper <jeremy@raredays.com>",
@@ -14,13 +14,16 @@
14
14
  "skillex"
15
15
  ],
16
16
  "type": "module",
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
17
20
  "dependencies": {
18
21
  "@clack/prompts": "1.4.0"
19
22
  },
20
23
  "devDependencies": {
21
24
  "typescript": "5.9.3",
22
- "vitest": "4.1.5",
23
- "@raredays/lint": "0.1.0"
25
+ "vitest": "4.1.9",
26
+ "@raredays/lint": "0.2.0"
24
27
  },
25
28
  "engines": {
26
29
  "node": ">=20"
@@ -34,6 +37,7 @@
34
37
  "style": "raredays style",
35
38
  "style:fix": "raredays style:fix",
36
39
  "fix": "raredays fix",
40
+ "publint": "publint run --strict",
37
41
  "check": "raredays check",
38
42
  "test": "vitest run",
39
43
  "test:watch": "vitest",
@@ -0,0 +1 @@
1
+ 24.15.0
@@ -0,0 +1,12 @@
1
+ # Semgrep ignore — paths excluded from SAST scans.
2
+ #
3
+ # Add specific paths or globs here when a rule produces unavoidable
4
+ # false positives (e.g. mock servers using http://localhost, generated
5
+ # code that trips security patterns).
6
+ #
7
+ # Example:
8
+ # src/mocks/*.test.ts
9
+ #
10
+ # Prefer inline `// nosemgrep: <rule-id> -- <reason>` over path-level
11
+ # exclusions when the case is local; reserve this file for whole files
12
+ # that legitimately need to be skipped.
@@ -6,6 +6,9 @@ on:
6
6
  pull_request:
7
7
  branches: [main]
8
8
 
9
+ permissions:
10
+ contents: read
11
+
9
12
  concurrency:
10
13
  group: ci-${{ github.ref }}
11
14
  cancel-in-progress: true
@@ -20,9 +23,11 @@ jobs:
20
23
  runs-on: ubuntu-latest
21
24
  timeout-minutes: 10
22
25
  steps:
23
- - uses: actions/checkout@v4
24
- - uses: pnpm/action-setup@v4
25
- - uses: actions/setup-node@v4
26
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
27
+ with:
28
+ persist-credentials: false
29
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
30
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
26
31
  with: { node-version: 24, cache: pnpm }
27
32
  - run: pnpm install --frozen-lockfile
28
33
  - run: pnpm lint
@@ -32,9 +37,11 @@ jobs:
32
37
  runs-on: ubuntu-latest
33
38
  timeout-minutes: 10
34
39
  steps:
35
- - uses: actions/checkout@v4
36
- - uses: pnpm/action-setup@v4
37
- - uses: actions/setup-node@v4
40
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
41
+ with:
42
+ persist-credentials: false
43
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
44
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
38
45
  with: { node-version: 24, cache: pnpm }
39
46
  - run: pnpm install --frozen-lockfile
40
47
  - run: pnpm format:check
@@ -44,9 +51,11 @@ jobs:
44
51
  runs-on: ubuntu-latest
45
52
  timeout-minutes: 10
46
53
  steps:
47
- - uses: actions/checkout@v4
48
- - uses: pnpm/action-setup@v4
49
- - uses: actions/setup-node@v4
54
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
55
+ with:
56
+ persist-credentials: false
57
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
58
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
50
59
  with: { node-version: 24, cache: pnpm }
51
60
  - run: pnpm install --frozen-lockfile
52
61
  - run: pnpm style
@@ -56,9 +65,11 @@ jobs:
56
65
  runs-on: ubuntu-latest
57
66
  timeout-minutes: 10
58
67
  steps:
59
- - uses: actions/checkout@v4
60
- - uses: pnpm/action-setup@v4
61
- - uses: actions/setup-node@v4
68
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
69
+ with:
70
+ persist-credentials: false
71
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
72
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
62
73
  with: { node-version: 24, cache: pnpm }
63
74
  - run: pnpm install --frozen-lockfile
64
75
  - run: pnpm theme:build
@@ -69,9 +80,11 @@ jobs:
69
80
  runs-on: ubuntu-latest
70
81
  timeout-minutes: 15
71
82
  steps:
72
- - uses: actions/checkout@v4
73
- - uses: pnpm/action-setup@v4
74
- - uses: actions/setup-node@v4
83
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
84
+ with:
85
+ persist-credentials: false
86
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
87
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
75
88
  with: { node-version: 24, cache: pnpm }
76
89
  - run: pnpm install --frozen-lockfile
77
90
  - run: pnpm theme:build
@@ -82,9 +95,11 @@ jobs:
82
95
  runs-on: ubuntu-latest
83
96
  timeout-minutes: 15
84
97
  steps:
85
- - uses: actions/checkout@v4
86
- - uses: pnpm/action-setup@v4
87
- - uses: actions/setup-node@v4
98
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
99
+ with:
100
+ persist-credentials: false
101
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
102
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
88
103
  with: { node-version: 24, cache: pnpm }
89
104
  - run: pnpm install --frozen-lockfile
90
105
  - run: pnpm build
@@ -93,12 +108,38 @@ jobs:
93
108
  name: Storybook build
94
109
  runs-on: ubuntu-latest
95
110
  timeout-minutes: 15
96
- if: hashFiles('.storybook/main.ts', '.storybook/main.js') != ''
97
111
  steps:
98
- - uses: actions/checkout@v4
99
- - uses: pnpm/action-setup@v4
100
- - uses: actions/setup-node@v4
112
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
113
+ with:
114
+ persist-credentials: false
115
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
116
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
101
117
  with: { node-version: 24, cache: pnpm }
102
118
  - run: pnpm install --frozen-lockfile
103
119
  - run: pnpm theme:build
104
120
  - run: pnpm storybook:build
121
+
122
+ # Single aggregation gate — make THIS the only required status check in
123
+ # branch protection so you don't have to enumerate every job (skipped
124
+ # jobs are tolerated by the gate).
125
+ ci-result:
126
+ name: ci-result
127
+ needs: [lint, format-check, style, typecheck, test, build, storybook]
128
+ if: ${{ !cancelled() }}
129
+ runs-on: ubuntu-latest
130
+ permissions: {}
131
+ steps:
132
+ - name: Aggregate CI results
133
+ env:
134
+ NEEDS: ${{ toJson(needs) }}
135
+ run: |
136
+ echo "$NEEDS" | jq -r 'to_entries[] | "\(.key)\t\(.value.result)"'
137
+ failed=$(echo "$NEEDS" | jq -r '
138
+ to_entries[]
139
+ | select(.value.result == "failure" or .value.result == "cancelled")
140
+ | .key')
141
+ if [ -n "$failed" ]; then
142
+ echo "::error::Failed or cancelled jobs: $failed"
143
+ exit 1
144
+ fi
145
+ echo "All required CI jobs passed (or were skipped)."
@@ -0,0 +1,41 @@
1
+ name: Coverage
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: coverage-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ # Runs Vitest with --coverage and uploads the HTML report as an artifact.
17
+ # Coverage thresholds are not enforced by default — opt in by configuring
18
+ # `test.coverage.thresholds` in vitest.config.ts when your suite is ready.
19
+
20
+ jobs:
21
+ coverage:
22
+ name: Coverage
23
+ runs-on: ubuntu-latest
24
+ timeout-minutes: 15
25
+ steps:
26
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
27
+ with:
28
+ persist-credentials: false
29
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
30
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
31
+ with: { node-version: 24, cache: pnpm }
32
+ - run: pnpm install --frozen-lockfile
33
+ - run: pnpm theme:build
34
+ - run: pnpm coverage
35
+ - name: Upload coverage report
36
+ if: always()
37
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
38
+ with:
39
+ name: coverage-report
40
+ path: coverage/
41
+ retention-days: 14
@@ -0,0 +1,42 @@
1
+ name: SAST
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: sast-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ # Static Application Security Testing via Semgrep. Scans first-party
17
+ # source against TypeScript, JavaScript, React, and OWASP top-ten
18
+ # rulesets. Findings fail the build — silence false positives with
19
+ # `.semgrepignore` (path-level) or inline `// nosemgrep: <rule-id>` with
20
+ # a justification.
21
+
22
+ jobs:
23
+ semgrep:
24
+ name: Semgrep
25
+ runs-on: ubuntu-latest
26
+ timeout-minutes: 10
27
+ if: github.actor != 'dependabot[bot]'
28
+ container:
29
+ image: semgrep/semgrep:1.168.0@sha256:59fbed6127ea7c5dde3ba6a85142733bb20ea9aaa36120c953904f1539aaf66e
30
+ steps:
31
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
32
+ with:
33
+ persist-credentials: false
34
+ - name: Run Semgrep
35
+ run: |
36
+ semgrep scan \
37
+ --config=p/typescript \
38
+ --config=p/javascript \
39
+ --config=p/react \
40
+ --config=p/owasp-top-ten \
41
+ --error \
42
+ --metrics=off
@@ -0,0 +1,34 @@
1
+ name: Security
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: security-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ # Audits production dependencies for known vulnerabilities. Fails the
17
+ # build on high or critical severity findings; moderate and low findings
18
+ # appear in the log but don't block merges.
19
+
20
+ jobs:
21
+ audit:
22
+ name: pnpm audit
23
+ runs-on: ubuntu-latest
24
+ timeout-minutes: 5
25
+ steps:
26
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
27
+ with:
28
+ persist-credentials: false
29
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
30
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
31
+ with: { node-version: 24, cache: pnpm }
32
+ - run: pnpm install --frozen-lockfile
33
+ - name: Audit production dependencies
34
+ run: pnpm audit --audit-level=high --prod
@@ -0,0 +1,43 @@
1
+ name: Zizmor
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths: [".github/**"]
7
+ pull_request:
8
+ branches: [main]
9
+ paths: [".github/**"]
10
+
11
+ permissions: {}
12
+
13
+ concurrency:
14
+ group: zizmor-${{ github.ref }}
15
+ cancel-in-progress: true
16
+
17
+ # Static security analysis of this app's GitHub Actions workflows
18
+ # (template-injection, unpinned actions, excessive token permissions,
19
+ # credential persistence). Free runner-hosted SAST — no GitHub Advanced
20
+ # Security needed. Findings surface as inline PR annotations. Starts at
21
+ # min-severity: high; lower to medium/low once your workflows are clean.
22
+
23
+ jobs:
24
+ zizmor:
25
+ name: GitHub Actions security (zizmor)
26
+ runs-on: ubuntu-latest
27
+ timeout-minutes: 10
28
+ permissions:
29
+ contents: read
30
+ actions: read
31
+ steps:
32
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
33
+ with:
34
+ persist-credentials: false
35
+ - uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6
36
+ with:
37
+ min-severity: high
38
+ min-confidence: medium
39
+ # This app's repo may not have GitHub Advanced Security; disable the
40
+ # SARIF/code-scanning upload (mutually exclusive with annotations) and
41
+ # surface findings as inline PR annotations instead.
42
+ advanced-security: false
43
+ annotations: true
@@ -12,6 +12,7 @@
12
12
  "theme:build": "node ./scripts/build-theme.mjs",
13
13
  "test": "vitest run",
14
14
  "test:watch": "vitest",
15
+ "coverage": "vitest run --coverage",
15
16
  "storybook": "storybook dev -p 6006",
16
17
  "storybook:build": "storybook build -o storybook-static",
17
18
  "lint": "raredays lint",
@@ -48,6 +49,7 @@
48
49
  "@types/react": "19.2.14",
49
50
  "@types/react-dom": "19.2.3",
50
51
  "@vitejs/plugin-react": "6.0.1",
52
+ "@vitest/coverage-v8": "4.1.5",
51
53
  "happy-dom": "15.11.7",
52
54
  "storybook": "10.3.6",
53
55
  "tailwindcss": "4.2.4",
@@ -57,5 +59,6 @@
57
59
  },
58
60
  "engines": {
59
61
  "node": ">=24"
60
- }
62
+ },
63
+ "packageManager": "pnpm@11.9.0"
61
64
  }
@@ -0,0 +1 @@
1
+ 24.15.0
@@ -0,0 +1,12 @@
1
+ # Semgrep ignore — paths excluded from SAST scans.
2
+ #
3
+ # Add specific paths or globs here when a rule produces unavoidable
4
+ # false positives (e.g. mock servers using http://localhost, generated
5
+ # code that trips security patterns).
6
+ #
7
+ # Example:
8
+ # src/mocks/*.test.ts
9
+ #
10
+ # Prefer inline `// nosemgrep: <rule-id> -- <reason>` over path-level
11
+ # exclusions when the case is local; reserve this file for whole files
12
+ # that legitimately need to be skipped.
@@ -6,6 +6,9 @@ on:
6
6
  pull_request:
7
7
  branches: [main]
8
8
 
9
+ permissions:
10
+ contents: read
11
+
9
12
  concurrency:
10
13
  group: ci-${{ github.ref }}
11
14
  cancel-in-progress: true
@@ -24,9 +27,11 @@ jobs:
24
27
  runs-on: ubuntu-latest
25
28
  timeout-minutes: 10
26
29
  steps:
27
- - uses: actions/checkout@v4
28
- - uses: pnpm/action-setup@v4
29
- - uses: actions/setup-node@v4
30
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
31
+ with:
32
+ persist-credentials: false
33
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
34
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
30
35
  with: { node-version: 24, cache: pnpm }
31
36
  - run: pnpm install --frozen-lockfile
32
37
  - run: pnpm lint
@@ -36,9 +41,11 @@ jobs:
36
41
  runs-on: ubuntu-latest
37
42
  timeout-minutes: 10
38
43
  steps:
39
- - uses: actions/checkout@v4
40
- - uses: pnpm/action-setup@v4
41
- - uses: actions/setup-node@v4
44
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
45
+ with:
46
+ persist-credentials: false
47
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
48
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
42
49
  with: { node-version: 24, cache: pnpm }
43
50
  - run: pnpm install --frozen-lockfile
44
51
  - run: pnpm format:check
@@ -48,9 +55,11 @@ jobs:
48
55
  runs-on: ubuntu-latest
49
56
  timeout-minutes: 10
50
57
  steps:
51
- - uses: actions/checkout@v4
52
- - uses: pnpm/action-setup@v4
53
- - uses: actions/setup-node@v4
58
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
59
+ with:
60
+ persist-credentials: false
61
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
62
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
54
63
  with: { node-version: 24, cache: pnpm }
55
64
  - run: pnpm install --frozen-lockfile
56
65
  - run: pnpm style
@@ -60,9 +69,11 @@ jobs:
60
69
  runs-on: ubuntu-latest
61
70
  timeout-minutes: 10
62
71
  steps:
63
- - uses: actions/checkout@v4
64
- - uses: pnpm/action-setup@v4
65
- - uses: actions/setup-node@v4
72
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
73
+ with:
74
+ persist-credentials: false
75
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
76
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
66
77
  with: { node-version: 24, cache: pnpm }
67
78
  - run: pnpm install --frozen-lockfile
68
79
  - run: pnpm theme:build
@@ -73,9 +84,11 @@ jobs:
73
84
  runs-on: ubuntu-latest
74
85
  timeout-minutes: 15
75
86
  steps:
76
- - uses: actions/checkout@v4
77
- - uses: pnpm/action-setup@v4
78
- - uses: actions/setup-node@v4
87
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
88
+ with:
89
+ persist-credentials: false
90
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
91
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
79
92
  with: { node-version: 24, cache: pnpm }
80
93
  - run: pnpm install --frozen-lockfile
81
94
  - run: pnpm theme:build
@@ -86,9 +99,11 @@ jobs:
86
99
  runs-on: ubuntu-latest
87
100
  timeout-minutes: 15
88
101
  steps:
89
- - uses: actions/checkout@v4
90
- - uses: pnpm/action-setup@v4
91
- - uses: actions/setup-node@v4
102
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
103
+ with:
104
+ persist-credentials: false
105
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
106
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
92
107
  with: { node-version: 24, cache: pnpm }
93
108
  - run: pnpm install --frozen-lockfile
94
109
  - run: pnpm build
@@ -97,13 +112,38 @@ jobs:
97
112
  name: Storybook build
98
113
  runs-on: ubuntu-latest
99
114
  timeout-minutes: 15
100
- # Only run if storybook is configured.
101
- if: hashFiles('.storybook/main.ts', '.storybook/main.js') != ''
102
115
  steps:
103
- - uses: actions/checkout@v4
104
- - uses: pnpm/action-setup@v4
105
- - uses: actions/setup-node@v4
116
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
117
+ with:
118
+ persist-credentials: false
119
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
120
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
106
121
  with: { node-version: 24, cache: pnpm }
107
122
  - run: pnpm install --frozen-lockfile
108
123
  - run: pnpm theme:build
109
124
  - run: pnpm storybook:build
125
+
126
+ # Single aggregation gate — make THIS the only required status check in
127
+ # branch protection so you don't have to enumerate every job (skipped
128
+ # jobs are tolerated by the gate).
129
+ ci-result:
130
+ name: ci-result
131
+ needs: [lint, format-check, style, typecheck, test, build, storybook]
132
+ if: ${{ !cancelled() }}
133
+ runs-on: ubuntu-latest
134
+ permissions: {}
135
+ steps:
136
+ - name: Aggregate CI results
137
+ env:
138
+ NEEDS: ${{ toJson(needs) }}
139
+ run: |
140
+ echo "$NEEDS" | jq -r 'to_entries[] | "\(.key)\t\(.value.result)"'
141
+ failed=$(echo "$NEEDS" | jq -r '
142
+ to_entries[]
143
+ | select(.value.result == "failure" or .value.result == "cancelled")
144
+ | .key')
145
+ if [ -n "$failed" ]; then
146
+ echo "::error::Failed or cancelled jobs: $failed"
147
+ exit 1
148
+ fi
149
+ echo "All required CI jobs passed (or were skipped)."
@@ -0,0 +1,41 @@
1
+ name: Coverage
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: coverage-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ # Runs Vitest with --coverage and uploads the HTML report as an artifact.
17
+ # Coverage thresholds are not enforced by default — opt in by configuring
18
+ # `test.coverage.thresholds` in vitest.config.ts when your suite is ready.
19
+
20
+ jobs:
21
+ coverage:
22
+ name: Coverage
23
+ runs-on: ubuntu-latest
24
+ timeout-minutes: 15
25
+ steps:
26
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
27
+ with:
28
+ persist-credentials: false
29
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
30
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
31
+ with: { node-version: 24, cache: pnpm }
32
+ - run: pnpm install --frozen-lockfile
33
+ - run: pnpm theme:build
34
+ - run: pnpm coverage
35
+ - name: Upload coverage report
36
+ if: always()
37
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
38
+ with:
39
+ name: coverage-report
40
+ path: coverage/
41
+ retention-days: 14
@@ -19,13 +19,18 @@ on:
19
19
  - "package.json"
20
20
  - ".github/workflows/db-check.yml"
21
21
 
22
+ permissions:
23
+ contents: read
24
+
22
25
  jobs:
23
26
  db-check:
24
27
  runs-on: ubuntu-latest
25
28
  steps:
26
- - uses: actions/checkout@v4
27
- - uses: pnpm/action-setup@v4
28
- - uses: actions/setup-node@v4
29
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
30
+ with:
31
+ persist-credentials: false
32
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
33
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
29
34
  with:
30
35
  node-version: 24
31
36
  cache: pnpm
@@ -15,6 +15,9 @@ on:
15
15
  - ".github/workflows/db-migrate.yml"
16
16
  workflow_dispatch:
17
17
 
18
+ permissions:
19
+ contents: read
20
+
18
21
  concurrency:
19
22
  # Advisory lock at the workflow layer too — defense in depth alongside
20
23
  # safeMigrate's per-database advisory lock.
@@ -26,9 +29,11 @@ jobs:
26
29
  runs-on: ubuntu-latest
27
30
  environment: production
28
31
  steps:
29
- - uses: actions/checkout@v4
30
- - uses: pnpm/action-setup@v4
31
- - uses: actions/setup-node@v4
32
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
33
+ with:
34
+ persist-credentials: false
35
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
36
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
32
37
  with:
33
38
  node-version: 24
34
39
  cache: pnpm
@@ -0,0 +1,42 @@
1
+ name: SAST
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: sast-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ # Static Application Security Testing via Semgrep. Scans first-party
17
+ # source against TypeScript, JavaScript, React, and OWASP top-ten
18
+ # rulesets. Findings fail the build — silence false positives with
19
+ # `.semgrepignore` (path-level) or inline `// nosemgrep: <rule-id>` with
20
+ # a justification.
21
+
22
+ jobs:
23
+ semgrep:
24
+ name: Semgrep
25
+ runs-on: ubuntu-latest
26
+ timeout-minutes: 10
27
+ if: github.actor != 'dependabot[bot]'
28
+ container:
29
+ image: semgrep/semgrep:1.168.0@sha256:59fbed6127ea7c5dde3ba6a85142733bb20ea9aaa36120c953904f1539aaf66e
30
+ steps:
31
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
32
+ with:
33
+ persist-credentials: false
34
+ - name: Run Semgrep
35
+ run: |
36
+ semgrep scan \
37
+ --config=p/typescript \
38
+ --config=p/javascript \
39
+ --config=p/react \
40
+ --config=p/owasp-top-ten \
41
+ --error \
42
+ --metrics=off
@@ -0,0 +1,34 @@
1
+ name: Security
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: security-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ # Audits production dependencies for known vulnerabilities. Fails the
17
+ # build on high or critical severity findings; moderate and low findings
18
+ # appear in the log but don't block merges.
19
+
20
+ jobs:
21
+ audit:
22
+ name: pnpm audit
23
+ runs-on: ubuntu-latest
24
+ timeout-minutes: 5
25
+ steps:
26
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
27
+ with:
28
+ persist-credentials: false
29
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
30
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
31
+ with: { node-version: 24, cache: pnpm }
32
+ - run: pnpm install --frozen-lockfile
33
+ - name: Audit production dependencies
34
+ run: pnpm audit --audit-level=high --prod
@@ -0,0 +1,43 @@
1
+ name: Zizmor
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths: [".github/**"]
7
+ pull_request:
8
+ branches: [main]
9
+ paths: [".github/**"]
10
+
11
+ permissions: {}
12
+
13
+ concurrency:
14
+ group: zizmor-${{ github.ref }}
15
+ cancel-in-progress: true
16
+
17
+ # Static security analysis of this app's GitHub Actions workflows
18
+ # (template-injection, unpinned actions, excessive token permissions,
19
+ # credential persistence). Free runner-hosted SAST — no GitHub Advanced
20
+ # Security needed. Findings surface as inline PR annotations. Starts at
21
+ # min-severity: high; lower to medium/low once your workflows are clean.
22
+
23
+ jobs:
24
+ zizmor:
25
+ name: GitHub Actions security (zizmor)
26
+ runs-on: ubuntu-latest
27
+ timeout-minutes: 10
28
+ permissions:
29
+ contents: read
30
+ actions: read
31
+ steps:
32
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
33
+ with:
34
+ persist-credentials: false
35
+ - uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6
36
+ with:
37
+ min-severity: high
38
+ min-confidence: medium
39
+ # This app's repo may not have GitHub Advanced Security; disable the
40
+ # SARIF/code-scanning upload (mutually exclusive with annotations) and
41
+ # surface findings as inline PR annotations instead.
42
+ advanced-security: false
43
+ annotations: true
@@ -12,6 +12,7 @@
12
12
  "theme:build": "node ./scripts/build-theme.mjs",
13
13
  "test": "vitest run",
14
14
  "test:watch": "vitest",
15
+ "coverage": "vitest run --coverage",
15
16
  "storybook": "storybook dev -p 6006",
16
17
  "storybook:build": "storybook build -o storybook-static",
17
18
  "db:generate": "drizzle-kit generate",
@@ -58,6 +59,7 @@
58
59
  "@storybook/react-vite": "10.3.6",
59
60
  "@tailwindcss/vite": "4.2.4",
60
61
  "@vitejs/plugin-react": "6.0.1",
62
+ "@vitest/coverage-v8": "4.1.5",
61
63
  "drizzle-kit": "0.31.10",
62
64
  "happy-dom": "15.11.7",
63
65
  "storybook": "10.3.6",
@@ -68,5 +70,6 @@
68
70
  },
69
71
  "engines": {
70
72
  "node": ">=24"
71
- }
73
+ },
74
+ "packageManager": "pnpm@11.9.0"
72
75
  }
@@ -0,0 +1 @@
1
+ 24.15.0
@@ -0,0 +1,12 @@
1
+ # Semgrep ignore — paths excluded from SAST scans.
2
+ #
3
+ # Add specific paths or globs here when a rule produces unavoidable
4
+ # false positives (e.g. mock servers using http://localhost, generated
5
+ # code that trips security patterns).
6
+ #
7
+ # Example:
8
+ # src/mocks/*.test.ts
9
+ #
10
+ # Prefer inline `// nosemgrep: <rule-id> -- <reason>` over path-level
11
+ # exclusions when the case is local; reserve this file for whole files
12
+ # that legitimately need to be skipped.
@@ -6,6 +6,9 @@ on:
6
6
  pull_request:
7
7
  branches: [main]
8
8
 
9
+ permissions:
10
+ contents: read
11
+
9
12
  concurrency:
10
13
  group: ci-${{ github.ref }}
11
14
  cancel-in-progress: true
@@ -20,9 +23,11 @@ jobs:
20
23
  runs-on: ubuntu-latest
21
24
  timeout-minutes: 10
22
25
  steps:
23
- - uses: actions/checkout@v4
24
- - uses: pnpm/action-setup@v4
25
- - uses: actions/setup-node@v4
26
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
27
+ with:
28
+ persist-credentials: false
29
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
30
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
26
31
  with: { node-version: 24, cache: pnpm }
27
32
  - run: pnpm install --frozen-lockfile
28
33
  - run: pnpm lint
@@ -32,9 +37,11 @@ jobs:
32
37
  runs-on: ubuntu-latest
33
38
  timeout-minutes: 10
34
39
  steps:
35
- - uses: actions/checkout@v4
36
- - uses: pnpm/action-setup@v4
37
- - uses: actions/setup-node@v4
40
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
41
+ with:
42
+ persist-credentials: false
43
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
44
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
38
45
  with: { node-version: 24, cache: pnpm }
39
46
  - run: pnpm install --frozen-lockfile
40
47
  - run: pnpm format:check
@@ -44,9 +51,11 @@ jobs:
44
51
  runs-on: ubuntu-latest
45
52
  timeout-minutes: 10
46
53
  steps:
47
- - uses: actions/checkout@v4
48
- - uses: pnpm/action-setup@v4
49
- - uses: actions/setup-node@v4
54
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
55
+ with:
56
+ persist-credentials: false
57
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
58
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
50
59
  with: { node-version: 24, cache: pnpm }
51
60
  - run: pnpm install --frozen-lockfile
52
61
  - run: pnpm style
@@ -56,9 +65,11 @@ jobs:
56
65
  runs-on: ubuntu-latest
57
66
  timeout-minutes: 10
58
67
  steps:
59
- - uses: actions/checkout@v4
60
- - uses: pnpm/action-setup@v4
61
- - uses: actions/setup-node@v4
68
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
69
+ with:
70
+ persist-credentials: false
71
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
72
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
62
73
  with: { node-version: 24, cache: pnpm }
63
74
  - run: pnpm install --frozen-lockfile
64
75
  - run: pnpm theme:build
@@ -69,9 +80,11 @@ jobs:
69
80
  runs-on: ubuntu-latest
70
81
  timeout-minutes: 15
71
82
  steps:
72
- - uses: actions/checkout@v4
73
- - uses: pnpm/action-setup@v4
74
- - uses: actions/setup-node@v4
83
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
84
+ with:
85
+ persist-credentials: false
86
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
87
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
75
88
  with: { node-version: 24, cache: pnpm }
76
89
  - run: pnpm install --frozen-lockfile
77
90
  - run: pnpm build
@@ -80,12 +93,38 @@ jobs:
80
93
  name: Storybook build
81
94
  runs-on: ubuntu-latest
82
95
  timeout-minutes: 15
83
- if: hashFiles('.storybook/main.ts', '.storybook/main.js') != ''
84
96
  steps:
85
- - uses: actions/checkout@v4
86
- - uses: pnpm/action-setup@v4
87
- - uses: actions/setup-node@v4
97
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
98
+ with:
99
+ persist-credentials: false
100
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
101
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
88
102
  with: { node-version: 24, cache: pnpm }
89
103
  - run: pnpm install --frozen-lockfile
90
104
  - run: pnpm theme:build
91
105
  - run: pnpm storybook:build
106
+
107
+ # Single aggregation gate — make THIS the only required status check in
108
+ # branch protection so you don't have to enumerate every job (skipped
109
+ # jobs are tolerated by the gate).
110
+ ci-result:
111
+ name: ci-result
112
+ needs: [lint, format-check, style, typecheck, build, storybook]
113
+ if: ${{ !cancelled() }}
114
+ runs-on: ubuntu-latest
115
+ permissions: {}
116
+ steps:
117
+ - name: Aggregate CI results
118
+ env:
119
+ NEEDS: ${{ toJson(needs) }}
120
+ run: |
121
+ echo "$NEEDS" | jq -r 'to_entries[] | "\(.key)\t\(.value.result)"'
122
+ failed=$(echo "$NEEDS" | jq -r '
123
+ to_entries[]
124
+ | select(.value.result == "failure" or .value.result == "cancelled")
125
+ | .key')
126
+ if [ -n "$failed" ]; then
127
+ echo "::error::Failed or cancelled jobs: $failed"
128
+ exit 1
129
+ fi
130
+ echo "All required CI jobs passed (or were skipped)."
@@ -0,0 +1,42 @@
1
+ name: SAST
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: sast-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ # Static Application Security Testing via Semgrep. Scans first-party
17
+ # source against TypeScript, JavaScript, React, and OWASP top-ten
18
+ # rulesets. Findings fail the build — silence false positives with
19
+ # `.semgrepignore` (path-level) or inline `// nosemgrep: <rule-id>` with
20
+ # a justification.
21
+
22
+ jobs:
23
+ semgrep:
24
+ name: Semgrep
25
+ runs-on: ubuntu-latest
26
+ timeout-minutes: 10
27
+ if: github.actor != 'dependabot[bot]'
28
+ container:
29
+ image: semgrep/semgrep:1.168.0@sha256:59fbed6127ea7c5dde3ba6a85142733bb20ea9aaa36120c953904f1539aaf66e
30
+ steps:
31
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
32
+ with:
33
+ persist-credentials: false
34
+ - name: Run Semgrep
35
+ run: |
36
+ semgrep scan \
37
+ --config=p/typescript \
38
+ --config=p/javascript \
39
+ --config=p/react \
40
+ --config=p/owasp-top-ten \
41
+ --error \
42
+ --metrics=off
@@ -0,0 +1,34 @@
1
+ name: Security
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: security-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ # Audits production dependencies for known vulnerabilities. Fails the
17
+ # build on high or critical severity findings; moderate and low findings
18
+ # appear in the log but don't block merges.
19
+
20
+ jobs:
21
+ audit:
22
+ name: pnpm audit
23
+ runs-on: ubuntu-latest
24
+ timeout-minutes: 5
25
+ steps:
26
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
27
+ with:
28
+ persist-credentials: false
29
+ - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
30
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
31
+ with: { node-version: 24, cache: pnpm }
32
+ - run: pnpm install --frozen-lockfile
33
+ - name: Audit production dependencies
34
+ run: pnpm audit --audit-level=high --prod
@@ -0,0 +1,43 @@
1
+ name: Zizmor
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths: [".github/**"]
7
+ pull_request:
8
+ branches: [main]
9
+ paths: [".github/**"]
10
+
11
+ permissions: {}
12
+
13
+ concurrency:
14
+ group: zizmor-${{ github.ref }}
15
+ cancel-in-progress: true
16
+
17
+ # Static security analysis of this app's GitHub Actions workflows
18
+ # (template-injection, unpinned actions, excessive token permissions,
19
+ # credential persistence). Free runner-hosted SAST — no GitHub Advanced
20
+ # Security needed. Findings surface as inline PR annotations. Starts at
21
+ # min-severity: high; lower to medium/low once your workflows are clean.
22
+
23
+ jobs:
24
+ zizmor:
25
+ name: GitHub Actions security (zizmor)
26
+ runs-on: ubuntu-latest
27
+ timeout-minutes: 10
28
+ permissions:
29
+ contents: read
30
+ actions: read
31
+ steps:
32
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
33
+ with:
34
+ persist-credentials: false
35
+ - uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6
36
+ with:
37
+ min-severity: high
38
+ min-confidence: medium
39
+ # This app's repo may not have GitHub Advanced Security; disable the
40
+ # SARIF/code-scanning upload (mutually exclusive with annotations) and
41
+ # surface findings as inline PR annotations instead.
42
+ advanced-security: false
43
+ annotations: true
@@ -25,6 +25,7 @@
25
25
  "@raredays/components": "^0.1.0",
26
26
  "@raredays/core": "^0.1.0",
27
27
  "@raredays/style": "^0.1.0",
28
+ "lucide-react": "1.14.0",
28
29
  "next": "16.2.4",
29
30
  "react": "19.2.5",
30
31
  "react-dom": "19.2.5"
@@ -49,5 +50,6 @@
49
50
  },
50
51
  "engines": {
51
52
  "node": ">=24"
52
- }
53
+ },
54
+ "packageManager": "pnpm@11.9.0"
53
55
  }