@react-spa-scaffold/mcp 2.3.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/constants.d.ts +1 -0
  2. package/dist/constants.d.ts.map +1 -1
  3. package/dist/constants.js +1 -0
  4. package/dist/constants.js.map +1 -1
  5. package/dist/features/definitions/api.d.ts.map +1 -1
  6. package/dist/features/definitions/api.js +2 -1
  7. package/dist/features/definitions/api.js.map +1 -1
  8. package/dist/features/definitions/electron.d.ts +3 -0
  9. package/dist/features/definitions/electron.d.ts.map +1 -0
  10. package/dist/features/definitions/electron.js +23 -0
  11. package/dist/features/definitions/electron.js.map +1 -0
  12. package/dist/features/definitions/index.d.ts +1 -0
  13. package/dist/features/definitions/index.d.ts.map +1 -1
  14. package/dist/features/definitions/index.js +1 -0
  15. package/dist/features/definitions/index.js.map +1 -1
  16. package/dist/features/registry.d.ts.map +1 -1
  17. package/dist/features/registry.js +2 -1
  18. package/dist/features/registry.js.map +1 -1
  19. package/dist/features/types.d.ts +1 -0
  20. package/dist/features/types.d.ts.map +1 -1
  21. package/dist/features/types.test.js +3 -2
  22. package/dist/features/types.test.js.map +1 -1
  23. package/dist/tools/get-features.test.js +7 -0
  24. package/dist/tools/get-features.test.js.map +1 -1
  25. package/dist/tools/get-scaffold.d.ts +1 -0
  26. package/dist/tools/get-scaffold.d.ts.map +1 -1
  27. package/dist/tools/get-scaffold.js +4 -1
  28. package/dist/tools/get-scaffold.js.map +1 -1
  29. package/dist/tools/get-scaffold.test.js +50 -0
  30. package/dist/tools/get-scaffold.test.js.map +1 -1
  31. package/dist/utils/scaffold/claude-md/index.d.ts.map +1 -1
  32. package/dist/utils/scaffold/claude-md/index.js +2 -1
  33. package/dist/utils/scaffold/claude-md/index.js.map +1 -1
  34. package/dist/utils/scaffold/claude-md/sections.d.ts +1 -0
  35. package/dist/utils/scaffold/claude-md/sections.d.ts.map +1 -1
  36. package/dist/utils/scaffold/claude-md/sections.js +42 -0
  37. package/dist/utils/scaffold/claude-md/sections.js.map +1 -1
  38. package/dist/utils/scaffold/compute.d.ts.map +1 -1
  39. package/dist/utils/scaffold/compute.js +3 -1
  40. package/dist/utils/scaffold/compute.js.map +1 -1
  41. package/dist/utils/scaffold/generators.d.ts +5 -0
  42. package/dist/utils/scaffold/generators.d.ts.map +1 -1
  43. package/dist/utils/scaffold/generators.js +43 -0
  44. package/dist/utils/scaffold/generators.js.map +1 -1
  45. package/package.json +1 -1
  46. package/templates/.github/workflows/ci.yml +45 -1
  47. package/templates/.github/workflows/deploy.yml +18 -31
  48. package/templates/CLAUDE.md +3 -0
  49. package/templates/docs/DEPLOYMENT.md +32 -4
  50. package/templates/forge.config.js +53 -0
  51. package/templates/package.json +5 -1
  52. package/templates/src/components/shared/ProfileSync/ProfileSync.tsx +2 -2
  53. package/templates/src/contexts/queryContext.tsx +9 -8
  54. package/templates/src/main.ts +227 -0
  55. package/templates/src/main.tsx +15 -3
  56. package/templates/src/preload.ts +26 -0
  57. package/templates/src/types/global.d.ts +28 -0
  58. package/templates/vite.main.config.mjs +20 -0
  59. package/templates/vite.preload.config.mjs +17 -0
  60. package/templates/vite.renderer.config.mjs +52 -0
@@ -6,4 +6,9 @@ export declare function generateViteEnvDts(featureIds: FeatureId[]): string;
6
6
  export declare function generateEnvTs(featureIds: FeatureId[]): string;
7
7
  /** Generates routes.ts content based on selected features. */
8
8
  export declare function generateRoutesTs(featureIds: FeatureId[]): string;
9
+ /**
10
+ * Generates global.d.ts content based on selected features.
11
+ * Currently only includes ElectronAPI types when electron feature is selected.
12
+ */
13
+ export declare function generateGlobalDts(featureIds: FeatureId[]): string | undefined;
9
14
  //# sourceMappingURL=generators.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../../src/utils/scaffold/generators.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CAqDlE;AAED,0DAA0D;AAC1D,wBAAgB,aAAa,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CA2G7D;AAED,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CAqBhE"}
1
+ {"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../../src/utils/scaffold/generators.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CAqDlE;AAED,0DAA0D;AAC1D,wBAAgB,aAAa,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CA2G7D;AAED,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CAqBhE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,GAAG,SAAS,CAyC7E"}
@@ -170,4 +170,47 @@ ${routes.join('\n')}
170
170
  export type AppRoute = (typeof ROUTES)[keyof typeof ROUTES];
171
171
  `;
172
172
  }
173
+ /**
174
+ * Generates global.d.ts content based on selected features.
175
+ * Currently only includes ElectronAPI types when electron feature is selected.
176
+ */
177
+ export function generateGlobalDts(featureIds) {
178
+ const sections = [];
179
+ // Electron API types
180
+ if (featureIds.includes(FEATURE.ELECTRON)) {
181
+ sections.push(`/**
182
+ * Global type declarations for Electron integration.
183
+ * These types are automatically available in the renderer process
184
+ * when running inside Electron.
185
+ */
186
+
187
+ interface ElectronWindowAPI {
188
+ setAlwaysOnTop: (enable: boolean) => Promise<boolean>;
189
+ setContentProtection: (enable: boolean) => Promise<boolean>;
190
+ getState: () => Promise<{
191
+ alwaysOnTop: boolean;
192
+ contentProtection: boolean;
193
+ }>;
194
+ }
195
+
196
+ interface ElectronAPI {
197
+ window: ElectronWindowAPI;
198
+ platform: NodeJS.Platform;
199
+ isElectron: true;
200
+ }
201
+
202
+ declare global {
203
+ interface Window {
204
+ electronAPI?: ElectronAPI;
205
+ }
206
+ }
207
+
208
+ export {};`);
209
+ }
210
+ // Return undefined if no content needed
211
+ if (sections.length === 0) {
212
+ return undefined;
213
+ }
214
+ return sections.join('\n\n') + '\n';
215
+ }
173
216
  //# sourceMappingURL=generators.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"generators.js","sourceRoot":"","sources":["../../../src/utils/scaffold/generators.ts"],"names":[],"mappings":"AAAA,qCAAqC;AAErC;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAG7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,iEAAiE;AACjE,MAAM,UAAU,kBAAkB,CAAC,UAAuB;IACxD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,wCAAwC;IACxC,IAAI,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACtC,QAAQ,CAAC,IAAI,CAAC;;;EAGhB,CAAC,CAAC;IACF,CAAC;IAED,mCAAmC;IACnC,MAAM,OAAO,GAAa,CAAC,mCAAmC,EAAE,kCAAkC,CAAC,CAAC;IAEpG,IAAI,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,OAAO,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;IACnD,CAAC;IAED,IAAI,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;QAC/C,OAAO,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;IAC1D,CAAC;IAED,IAAI,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7C,OAAO,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;IACrD,CAAC;IAED,IAAI,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACtC,OAAO,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;IACjE,CAAC;IAED,IAAI,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1C,OAAO,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;QAC/D,OAAO,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;IAC7D,CAAC;IAED,0DAA0D;IAC1D,OAAO,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;IACxE,OAAO,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IACzC,OAAO,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;IAE1C,QAAQ,CAAC,IAAI,CAAC;;;EAGd,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;;;;;;EAMlB,CAAC,CAAC;IAEF,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;AACtC,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,aAAa,CAAC,UAAuB;IACnD,yCAAyC;IACzC,MAAM,eAAe,GAAG,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAE/G,MAAM,YAAY,GAAa,CAAC,qCAAqC,EAAE,mCAAmC,CAAC,CAAC;IAC5G,MAAM,SAAS,GAAa;QAC1B,mDAAmD;QACnD,iDAAiD;KAClD,CAAC;IAEF,IAAI,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,YAAY,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;QACvD,SAAS,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC;IACpE,CAAC;IAED,IAAI,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;QAC/C,YAAY,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;QAC1D,YAAY,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;QACxD,SAAS,CAAC,IAAI,CAAC,uDAAuD,CAAC,CAAC;QACxE,SAAS,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;IAClF,CAAC;IAED,IAAI,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACtC,YAAY,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;QACtE,SAAS,CAAC,IAAI,CAAC,6EAA6E,CAAC,CAAC;IAChG,CAAC;IAED,IAAI,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1C,YAAY,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC;QACrE,YAAY,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;QAClE,SAAS,CAAC,IAAI,CAAC,6EAA6E,CAAC,CAAC;QAC9F,SAAS,CAAC,IAAI,CAAC,qEAAqE,CAAC,CAAC;IACxF,CAAC;IAED,IAAI,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7C,YAAY,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QACnD,SAAS,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAC;IACxE,CAAC;IAED,2CAA2C;IAC3C,YAAY,CAAC,IAAI,CAAC,wDAAwD,CAAC,CAAC;IAC5E,YAAY,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IACzC,YAAY,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAC1C,SAAS,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;IAClD,SAAS,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;IAChD,SAAS,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;IAElD,wCAAwC;IACxC,MAAM,gBAAgB,GAAG,eAAe;QACtC,CAAC,CAAC;;;;;;;;CAQL;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;;;;;;;;;EASP,gBAAgB;;EAEhB,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;;;;;;;;;;;EAWvB,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;CAwBrB,CAAC;AACF,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,gBAAgB,CAAC,UAAuB;IACtD,MAAM,MAAM,GAAa,CAAC,cAAc,CAAC,CAAC;IAE1C,sDAAsD;IACtD,IAAI,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1C,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IACxC,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAEjC,OAAO;;;;;;EAMP,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;;;;CAIlB,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"generators.js","sourceRoot":"","sources":["../../../src/utils/scaffold/generators.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-spa-scaffold/mcp",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "MCP server for scaffolding projects based on react-spa-scaffold template",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -20,6 +20,7 @@ jobs:
20
20
  - uses: ./.github/actions/setup-node-deps
21
21
  - run: npm run lint
22
22
  - run: npm run format:check
23
+ - run: npm run sync:check
23
24
 
24
25
  typecheck:
25
26
  name: Type Check
@@ -41,7 +42,7 @@ jobs:
41
42
 
42
43
  build:
43
44
  name: Build
44
- needs: [lint, typecheck]
45
+ needs: [lint, typecheck, security]
45
46
  runs-on: ubuntu-latest
46
47
  timeout-minutes: 10
47
48
  steps:
@@ -134,3 +135,46 @@ jobs:
134
135
  name: ${{ matrix.report-name }}
135
136
  path: playwright-report/
136
137
  retention-days: ${{ matrix.type == 'performance' && 14 || 7 }}
138
+
139
+ deploy:
140
+ name: Deploy
141
+ needs: [build, test, test-e2e]
142
+ runs-on: ubuntu-latest
143
+ timeout-minutes: 10
144
+ permissions:
145
+ pull-requests: write
146
+ deployments: write
147
+ steps:
148
+ - uses: actions/download-artifact@v6
149
+ with:
150
+ name: dist
151
+ path: dist/
152
+
153
+ - name: Deploy Preview
154
+ if: github.event_name == 'pull_request'
155
+ uses: nwtgck/actions-netlify@v3
156
+ with:
157
+ publish-dir: './dist'
158
+ production-deploy: false
159
+ github-token: ${{ secrets.GITHUB_TOKEN }}
160
+ deploy-message: 'Preview deploy from PR #${{ github.event.number }}'
161
+ enable-pull-request-comment: true
162
+ enable-commit-comment: false
163
+ overwrites-pull-request-comment: true
164
+ alias: pr-${{ github.event.number }}
165
+ env:
166
+ NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
167
+ NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
168
+
169
+ - name: Deploy Production
170
+ if: github.event_name == 'push'
171
+ uses: nwtgck/actions-netlify@v3
172
+ with:
173
+ publish-dir: './dist'
174
+ production-deploy: true
175
+ github-token: ${{ secrets.GITHUB_TOKEN }}
176
+ deploy-message: 'Production deploy from ${{ github.sha }}'
177
+ enable-commit-comment: true
178
+ env:
179
+ NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
180
+ NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
@@ -1,24 +1,24 @@
1
- name: Deploy
1
+ name: Deploy (Manual)
2
2
 
3
3
  on:
4
- push:
5
- branches: [main, master]
6
- pull_request:
7
- branches: [main, master]
8
4
  workflow_dispatch:
5
+ inputs:
6
+ environment:
7
+ description: 'Deploy environment'
8
+ required: true
9
+ default: 'preview'
10
+ type: choice
11
+ options:
12
+ - preview
13
+ - production
9
14
 
10
15
  permissions:
11
16
  contents: read
12
- pull-requests: write
13
17
  deployments: write
14
18
 
15
- concurrency:
16
- group: deploy-${{ github.ref }}
17
- cancel-in-progress: ${{ github.event_name == 'pull_request' }}
18
-
19
19
  jobs:
20
20
  deploy:
21
- name: Deploy
21
+ name: Deploy (${{ inputs.environment }})
22
22
  runs-on: ubuntu-latest
23
23
  timeout-minutes: 15
24
24
  steps:
@@ -28,32 +28,19 @@ jobs:
28
28
 
29
29
  - name: Build
30
30
  run: npm run build
31
-
32
- - name: Deploy Preview
33
- if: github.event_name == 'pull_request'
34
- uses: nwtgck/actions-netlify@v3
35
- with:
36
- publish-dir: './dist'
37
- production-deploy: false
38
- github-token: ${{ secrets.GITHUB_TOKEN }}
39
- deploy-message: 'Preview deploy from PR #${{ github.event.number }}'
40
- enable-pull-request-comment: true
41
- enable-commit-comment: false
42
- overwrites-pull-request-comment: true
43
- alias: pr-${{ github.event.number }}
44
31
  env:
45
- NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
46
- NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
32
+ SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
33
+ SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
34
+ SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
47
35
 
48
- - name: Deploy Production
49
- if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
36
+ - name: Deploy
50
37
  uses: nwtgck/actions-netlify@v3
51
38
  with:
52
39
  publish-dir: './dist'
53
- production-deploy: true
40
+ production-deploy: ${{ inputs.environment == 'production' }}
54
41
  github-token: ${{ secrets.GITHUB_TOKEN }}
55
- deploy-message: 'Production deploy from ${{ github.sha }}'
56
- enable-commit-comment: true
42
+ deploy-message: 'Manual ${{ inputs.environment }} deploy from ${{ github.sha }}'
43
+ enable-commit-comment: ${{ inputs.environment == 'production' }}
57
44
  env:
58
45
  NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
59
46
  NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
@@ -22,6 +22,8 @@ npm run i18n:extract # Extract translations to .po
22
22
  npm run db:types # Generate Supabase TypeScript types
23
23
  npm run db:push # Push database migrations
24
24
  npm run db:studio # Open Supabase Studio
25
+ npm run sync:check # Check monorepo dependency versions
26
+ npm run sync:fix # Auto-fix version mismatches
25
27
  ```
26
28
 
27
29
  ## Project Structure
@@ -396,3 +398,4 @@ it('handles error', async () => {
396
398
  6. **Clerk auth required** when auth feature is enabled - set `VITE_CLERK_PUBLISHABLE_KEY` in `.env`
397
399
  7. **Supabase requires Clerk** - SupabaseProvider must be inside ClerkProvider
398
400
  8. **RLS policies required** - All Supabase tables should have Row Level Security enabled
401
+ 9. **Monorepo deps** - Run `npm run sync:check` to detect version mismatches across packages
@@ -19,11 +19,36 @@ Push to main → GitHub Actions → Build → Netlify Production
19
19
  - Automatic preview deploys for pull requests
20
20
  - Production deploys on push to main/master
21
21
  - PR comments with preview URLs
22
- - Manual deploy via workflow_dispatch
22
+ - Manual deploy via workflow_dispatch (with environment selector)
23
23
  - Security headers pre-configured
24
+ - Sentry source map upload (when configured)
25
+ - **CI-gated**: Deploy only runs after all checks pass
24
26
 
25
27
  **Note:** Enable branch protection rules to require CI to pass before merging to main.
26
28
 
29
+ ### How It Works
30
+
31
+ ```
32
+ Push/PR → CI Workflow → lint, typecheck, security, build, test, e2e → deploy
33
+
34
+ (all must pass)
35
+ ```
36
+
37
+ **Two workflows:**
38
+
39
+ 1. **CI workflow** (`ci.yml`): Runs all checks, then deploys if all pass
40
+ 2. **Manual deploy** (`deploy.yml`): Emergency escape hatch, skips CI checks
41
+
42
+ **Two deployment modes:**
43
+
44
+ 1. **GitHub Actions (primary)**: Builds in CI, uploads pre-built files to Netlify
45
+ 2. **Netlify CLI (fallback)**: Use `netlify build` and `netlify deploy` locally
46
+
47
+ The `netlify.toml` file configures both:
48
+
49
+ - `[build]` section: Used by Netlify CLI and `netlify dev`
50
+ - `[[headers]]` and `[[redirects]]`: Applied at CDN level regardless of build method
51
+
27
52
  ---
28
53
 
29
54
  ## Netlify Setup
@@ -151,11 +176,14 @@ Pushing to main/master triggers production deployment:
151
176
 
152
177
  ### Manual Deploys
153
178
 
154
- Use workflow_dispatch for manual production deploys:
179
+ Use the manual deploy workflow for emergency or ad-hoc deploys (skips CI checks):
155
180
 
156
- 1. Go to Actions → Deploy → Run workflow
181
+ 1. Go to Actions → Deploy (Manual) → Run workflow
157
182
  2. Select branch
158
- 3. Click "Run workflow"
183
+ 3. Choose environment: `preview` or `production`
184
+ 4. Click "Run workflow"
185
+
186
+ > **Warning:** Manual deploys skip CI checks. Use only for emergencies or hotfixes.
159
187
 
160
188
  ---
161
189
 
@@ -0,0 +1,53 @@
1
+ // @ts-check
2
+
3
+ /** @type {import('@electron-forge/shared-types').ForgeConfig} */
4
+ const config = {
5
+ packagerConfig: {
6
+ name: 'ReactSPAScaffold',
7
+ appBundleId: 'com.example.react-spa-scaffold',
8
+ // No code signing for internal/dev use
9
+ osxSign: undefined,
10
+ osxNotarize: undefined,
11
+ },
12
+ rebuildConfig: {},
13
+ makers: [
14
+ {
15
+ name: '@electron-forge/maker-zip',
16
+ platforms: ['darwin'],
17
+ },
18
+ {
19
+ name: '@electron-forge/maker-dmg',
20
+ config: {
21
+ format: 'ULFO',
22
+ name: 'ReactSPAScaffold',
23
+ },
24
+ },
25
+ ],
26
+ plugins: [
27
+ {
28
+ name: '@electron-forge/plugin-vite',
29
+ config: {
30
+ build: [
31
+ {
32
+ entry: 'src/main.ts',
33
+ config: 'vite.main.config.mjs',
34
+ target: 'main',
35
+ },
36
+ {
37
+ entry: 'src/preload.ts',
38
+ config: 'vite.preload.config.mjs',
39
+ target: 'preload',
40
+ },
41
+ ],
42
+ renderer: [
43
+ {
44
+ name: 'main_window',
45
+ config: 'vite.renderer.config.mjs',
46
+ },
47
+ ],
48
+ },
49
+ },
50
+ ],
51
+ };
52
+
53
+ export default config;
@@ -57,7 +57,9 @@
57
57
  "mcp:inspect": "npm run inspect -w @react-spa-scaffold/mcp",
58
58
  "changeset": "changeset",
59
59
  "version": "changeset version",
60
- "release": "changeset publish"
60
+ "release": "changeset publish",
61
+ "sync:check": "syncpack lint",
62
+ "sync:fix": "syncpack fix-mismatches"
61
63
  },
62
64
  "dependencies": {
63
65
  "@clerk/react-router": "^2.3.7",
@@ -101,6 +103,7 @@
101
103
  "@react-spa-scaffold/tsconfig": "*",
102
104
  "@sentry/vite-plugin": "^4.6.1",
103
105
  "@tailwindcss/vite": "^4.1.17",
106
+ "@tanstack/react-query-devtools": "^5.91.2",
104
107
  "@testing-library/jest-dom": "^6.6.3",
105
108
  "@testing-library/react": "^16.3.0",
106
109
  "@testing-library/user-event": "^14.6.1",
@@ -127,6 +130,7 @@
127
130
  "prettier-plugin-tailwindcss": "^0.7.2",
128
131
  "shadcn": "^3.6.2",
129
132
  "supabase": "^2.70.5",
133
+ "syncpack": "^13.0.4",
130
134
  "tailwindcss": "^4.1.17",
131
135
  "typescript": "~5.9.0",
132
136
  "typescript-eslint": "^8.33.0",
@@ -50,7 +50,7 @@ interface ProfileSyncProps {
50
50
  export function ProfileSync({ onSyncComplete, onSyncError }: ProfileSyncProps) {
51
51
  const { user, isLoaded: isUserLoaded } = useUser();
52
52
  const { data: profiles, isLoading: isProfileLoading } = useCurrentProfile();
53
- const upsertProfile = useUpsertProfile();
53
+ const { mutate: upsertProfile } = useUpsertProfile();
54
54
 
55
55
  // Track if we've already synced this session to prevent infinite loops
56
56
  const hasSynced = useRef(false);
@@ -77,7 +77,7 @@ export function ProfileSync({ onSyncComplete, onSyncError }: ProfileSyncProps) {
77
77
  // Perform the sync
78
78
  isSyncing.current = true;
79
79
 
80
- upsertProfile.mutate(
80
+ upsertProfile(
81
81
  {
82
82
  id: user.id,
83
83
  email: user.primaryEmailAddress?.emailAddress ?? '',
@@ -1,16 +1,14 @@
1
1
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
2
+ import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
2
3
  import { type ReactNode, useState } from 'react';
3
4
 
4
- /**
5
- * Create QueryClient with optimized defaults.
6
- * Using a function ensures each provider instance gets its own client
7
- */
5
+ /** Create QueryClient with optimized defaults. */
8
6
  function createQueryClient() {
9
7
  return new QueryClient({
10
8
  defaultOptions: {
11
9
  queries: {
12
10
  staleTime: 1000 * 60 * 5, // 5 minutes
13
- gcTime: 1000 * 60 * 30, // 30 minutes (formerly cacheTime)
11
+ gcTime: 1000 * 60 * 30, // 30 minutes
14
12
  retry: 1,
15
13
  refetchOnWindowFocus: false,
16
14
  },
@@ -19,9 +17,12 @@ function createQueryClient() {
19
17
  }
20
18
 
21
19
  export function QueryProvider({ children }: { children: ReactNode }) {
22
- // Use useState to ensure queryClient is created once per component instance
23
- // This is the recommended pattern from TanStack Query docs
24
20
  const [queryClient] = useState(createQueryClient);
25
21
 
26
- return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>;
22
+ return (
23
+ <QueryClientProvider client={queryClient}>
24
+ {children}
25
+ <ReactQueryDevtools initialIsOpen={false} />
26
+ </QueryClientProvider>
27
+ );
27
28
  }
@@ -0,0 +1,227 @@
1
+ import path from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+
4
+ import { app, BrowserWindow, ipcMain, Menu, type MenuItemConstructorOptions } from 'electron';
5
+
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+
8
+ // Track window state
9
+ interface WindowState {
10
+ alwaysOnTop: boolean;
11
+ contentProtection: boolean;
12
+ }
13
+
14
+ let mainWindow: BrowserWindow | null = null;
15
+ const windowState: WindowState = {
16
+ alwaysOnTop: false,
17
+ contentProtection: false,
18
+ };
19
+
20
+ function createMenu(): void {
21
+ const isMac = process.platform === 'darwin';
22
+
23
+ const template: MenuItemConstructorOptions[] = [
24
+ // App menu (macOS only)
25
+ ...(isMac
26
+ ? [
27
+ {
28
+ label: app.name,
29
+ submenu: [
30
+ { role: 'about' as const },
31
+ { type: 'separator' as const },
32
+ { role: 'services' as const },
33
+ { type: 'separator' as const },
34
+ { role: 'hide' as const },
35
+ { role: 'hideOthers' as const },
36
+ { role: 'unhide' as const },
37
+ { type: 'separator' as const },
38
+ { role: 'quit' as const },
39
+ ],
40
+ },
41
+ ]
42
+ : []),
43
+ // File menu
44
+ {
45
+ label: 'File',
46
+ submenu: [isMac ? { role: 'close' as const } : { role: 'quit' as const }],
47
+ },
48
+ // Edit menu
49
+ {
50
+ label: 'Edit',
51
+ submenu: [
52
+ { role: 'undo' as const },
53
+ { role: 'redo' as const },
54
+ { type: 'separator' as const },
55
+ { role: 'cut' as const },
56
+ { role: 'copy' as const },
57
+ { role: 'paste' as const },
58
+ ...(isMac
59
+ ? [{ role: 'pasteAndMatchStyle' as const }, { role: 'delete' as const }, { role: 'selectAll' as const }]
60
+ : [{ role: 'delete' as const }, { type: 'separator' as const }, { role: 'selectAll' as const }]),
61
+ ],
62
+ },
63
+ // View menu
64
+ {
65
+ label: 'View',
66
+ submenu: [
67
+ { role: 'reload' as const },
68
+ { role: 'forceReload' as const },
69
+ { role: 'toggleDevTools' as const },
70
+ { type: 'separator' as const },
71
+ { role: 'resetZoom' as const },
72
+ { role: 'zoomIn' as const },
73
+ { role: 'zoomOut' as const },
74
+ { type: 'separator' as const },
75
+ { role: 'togglefullscreen' as const },
76
+ { type: 'separator' as const },
77
+ {
78
+ label: 'Always on Top',
79
+ type: 'checkbox' as const,
80
+ checked: windowState.alwaysOnTop,
81
+ click: () => {
82
+ windowState.alwaysOnTop = !windowState.alwaysOnTop;
83
+ mainWindow?.setAlwaysOnTop(windowState.alwaysOnTop, 'floating');
84
+ createMenu(); // Refresh menu to update checkbox state
85
+ },
86
+ },
87
+ {
88
+ label: 'Hide from Screen Sharing',
89
+ type: 'checkbox' as const,
90
+ checked: windowState.contentProtection,
91
+ click: () => {
92
+ windowState.contentProtection = !windowState.contentProtection;
93
+ mainWindow?.setContentProtection(windowState.contentProtection);
94
+ createMenu(); // Refresh menu to update checkbox state
95
+ },
96
+ },
97
+ ],
98
+ },
99
+ // Window menu
100
+ {
101
+ label: 'Window',
102
+ submenu: [
103
+ { role: 'minimize' as const },
104
+ { role: 'zoom' as const },
105
+ ...(isMac
106
+ ? [
107
+ { type: 'separator' as const },
108
+ { role: 'front' as const },
109
+ { type: 'separator' as const },
110
+ { role: 'window' as const },
111
+ ]
112
+ : [{ role: 'close' as const }]),
113
+ ],
114
+ },
115
+ ];
116
+
117
+ const menu = Menu.buildFromTemplate(template);
118
+ Menu.setApplicationMenu(menu);
119
+ }
120
+
121
+ function createWindow(): void {
122
+ // Determine preload path based on environment
123
+ const preloadPath = path.join(__dirname, 'preload.js');
124
+
125
+ mainWindow = new BrowserWindow({
126
+ width: 1280,
127
+ height: 800,
128
+ minWidth: 800,
129
+ minHeight: 600,
130
+ webPreferences: {
131
+ preload: preloadPath,
132
+ contextIsolation: true,
133
+ nodeIntegration: false,
134
+ sandbox: true,
135
+ },
136
+ // macOS specific styling
137
+ titleBarStyle: process.platform === 'darwin' ? 'hiddenInset' : 'default',
138
+ trafficLightPosition: { x: 16, y: 16 },
139
+ });
140
+
141
+ // Load the app with retry logic for dev server
142
+ // Increased retries and delay to handle Vite restart during dep-scan
143
+ const loadApp = async (retries = 10, delay = 2000): Promise<void> => {
144
+ if (!mainWindow) return;
145
+
146
+ if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
147
+ try {
148
+ await mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL);
149
+ } catch (error) {
150
+ if (retries > 0) {
151
+ console.log(`Failed to load dev server, retrying in ${delay}ms... (${retries} retries left)`);
152
+ await new Promise((resolve) => setTimeout(resolve, delay));
153
+ return loadApp(retries - 1, delay);
154
+ }
155
+ console.error('Failed to load dev server after all retries:', error);
156
+ }
157
+ } else {
158
+ mainWindow.loadFile(path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`));
159
+ }
160
+ };
161
+
162
+ loadApp();
163
+
164
+ // Open DevTools in development
165
+ if (process.env.NODE_ENV === 'development' || MAIN_WINDOW_VITE_DEV_SERVER_URL) {
166
+ mainWindow.webContents.openDevTools();
167
+ }
168
+
169
+ mainWindow.on('closed', () => {
170
+ mainWindow = null;
171
+ });
172
+ }
173
+
174
+ // Setup IPC handlers
175
+ function setupIpcHandlers(): void {
176
+ // Toggle always on top
177
+ ipcMain.handle('window:setAlwaysOnTop', (_event, enable: boolean) => {
178
+ if (mainWindow) {
179
+ windowState.alwaysOnTop = enable;
180
+ mainWindow.setAlwaysOnTop(enable, 'floating');
181
+ createMenu(); // Update menu checkbox state
182
+ return true;
183
+ }
184
+ return false;
185
+ });
186
+
187
+ // Toggle content protection (hide from screen sharing)
188
+ ipcMain.handle('window:setContentProtection', (_event, enable: boolean) => {
189
+ if (mainWindow) {
190
+ windowState.contentProtection = enable;
191
+ mainWindow.setContentProtection(enable);
192
+ createMenu(); // Update menu checkbox state
193
+ return true;
194
+ }
195
+ return false;
196
+ });
197
+
198
+ // Get current window state
199
+ ipcMain.handle('window:getState', () => {
200
+ return { ...windowState };
201
+ });
202
+ }
203
+
204
+ // App lifecycle
205
+ app.whenReady().then(() => {
206
+ setupIpcHandlers();
207
+ createMenu();
208
+ createWindow();
209
+
210
+ app.on('activate', () => {
211
+ // On macOS, re-create window when dock icon is clicked
212
+ if (BrowserWindow.getAllWindows().length === 0) {
213
+ createWindow();
214
+ }
215
+ });
216
+ });
217
+
218
+ app.on('window-all-closed', () => {
219
+ // On macOS, keep app running until explicitly quit
220
+ if (process.platform !== 'darwin') {
221
+ app.quit();
222
+ }
223
+ });
224
+
225
+ // Declare globals injected by Electron Forge Vite plugin
226
+ declare const MAIN_WINDOW_VITE_DEV_SERVER_URL: string | undefined;
227
+ declare const MAIN_WINDOW_VITE_NAME: string;