@nextsparkjs/core 0.1.0-beta.195 → 0.1.0-beta.197

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 (283) hide show
  1. package/README.md +2 -0
  2. package/dist/components/auth/forms/SignupForm.d.ts.map +1 -1
  3. package/dist/components/auth/forms/SignupForm.js +55 -45
  4. package/dist/components/billing/InvoicesPagination.js +2 -2
  5. package/dist/components/dashboard/block-editor/config-panel.d.ts.map +1 -1
  6. package/dist/components/dashboard/block-editor/config-panel.js +3 -2
  7. package/dist/components/dashboard/block-editor/entity-fields-sidebar.d.ts.map +1 -1
  8. package/dist/components/dashboard/block-editor/entity-fields-sidebar.js +3 -2
  9. package/dist/components/dashboard/navigation/DynamicNavigation.js +2 -2
  10. package/dist/components/settings/layouts/SettingsSidebar.d.ts.map +1 -1
  11. package/dist/components/settings/layouts/SettingsSidebar.js +0 -1
  12. package/dist/components/teams/CreateTeamButton.js +2 -2
  13. package/dist/components/teams/CreateTeamDialog.js +1 -1
  14. package/dist/components/teams/InviteMemberButton.js +3 -3
  15. package/dist/components/teams/InviteMemberDialog.js +1 -1
  16. package/dist/components/teams/TeamPendingInvitations.js +2 -2
  17. package/dist/components/users/tables/UsersTable.d.ts.map +1 -1
  18. package/dist/components/users/tables/UsersTable.js +3 -4
  19. package/dist/hooks/useAuthReadiness.d.ts +1 -0
  20. package/dist/hooks/useAuthReadiness.d.ts.map +1 -1
  21. package/dist/hooks/useAuthReadiness.js +3 -1
  22. package/dist/lib/actions/entity.actions.js +2 -2
  23. package/dist/lib/actions/team.actions.js +2 -2
  24. package/dist/lib/actions/user.actions.js +4 -4
  25. package/dist/lib/api/api-error.d.ts +2 -0
  26. package/dist/lib/api/api-error.d.ts.map +1 -1
  27. package/dist/lib/api/api-error.js +3 -1
  28. package/dist/lib/api/auth/dual-auth.d.ts +2 -1
  29. package/dist/lib/api/auth/dual-auth.d.ts.map +1 -1
  30. package/dist/lib/api/auth/dual-auth.js +25 -6
  31. package/dist/lib/api/auth/scope-policy.d.ts +6 -3
  32. package/dist/lib/api/auth/scope-policy.d.ts.map +1 -1
  33. package/dist/lib/api/auth.d.ts.map +1 -1
  34. package/dist/lib/api/auth.js +7 -0
  35. package/dist/lib/api/cors-response.d.ts +3 -2
  36. package/dist/lib/api/cors-response.d.ts.map +1 -1
  37. package/dist/lib/api/cors-response.js +8 -6
  38. package/dist/lib/api/entity/generic-handler.d.ts.map +1 -1
  39. package/dist/lib/api/entity/generic-handler.js +66 -15
  40. package/dist/lib/api/entity-handler.js +2 -2
  41. package/dist/lib/api/helpers.d.ts.map +1 -1
  42. package/dist/lib/api/helpers.js +9 -24
  43. package/dist/lib/api/permission-middleware.d.ts.map +1 -1
  44. package/dist/lib/api/permission-middleware.js +0 -2
  45. package/dist/lib/api/post-categories.d.ts +29 -0
  46. package/dist/lib/api/post-categories.d.ts.map +1 -0
  47. package/dist/lib/api/post-categories.js +33 -0
  48. package/dist/lib/api/rate-limit.d.ts +8 -0
  49. package/dist/lib/api/rate-limit.d.ts.map +1 -1
  50. package/dist/lib/api/rate-limit.js +34 -3
  51. package/dist/lib/api/request-origin.d.ts.map +1 -1
  52. package/dist/lib/api/request-origin.js +1 -1
  53. package/dist/lib/auth/auth-methods.d.ts +5 -2
  54. package/dist/lib/auth/auth-methods.d.ts.map +1 -1
  55. package/dist/lib/auth/auth-methods.js +4 -2
  56. package/dist/lib/auth/authorization-session.d.ts +14 -0
  57. package/dist/lib/auth/authorization-session.d.ts.map +1 -0
  58. package/dist/lib/auth/authorization-session.js +10 -0
  59. package/dist/lib/auth/request-session.d.ts +5 -0
  60. package/dist/lib/auth/request-session.d.ts.map +1 -1
  61. package/dist/lib/auth/request-session.js +3 -0
  62. package/dist/lib/auth/runtime-readiness.d.ts +7 -4
  63. package/dist/lib/auth/runtime-readiness.d.ts.map +1 -1
  64. package/dist/lib/auth/runtime-readiness.js +9 -6
  65. package/dist/lib/auth/sample-accounts.d.ts +18 -0
  66. package/dist/lib/auth/sample-accounts.d.ts.map +1 -0
  67. package/dist/lib/auth/sample-accounts.js +25 -0
  68. package/dist/lib/auth/suspension.d.ts +22 -0
  69. package/dist/lib/auth/suspension.d.ts.map +1 -0
  70. package/dist/lib/auth/suspension.js +43 -0
  71. package/dist/lib/auth.d.ts.map +1 -1
  72. package/dist/lib/auth.js +20 -17
  73. package/dist/lib/cache/public-entity-cache.d.ts +21 -0
  74. package/dist/lib/cache/public-entity-cache.d.ts.map +1 -0
  75. package/dist/lib/cache/public-entity-cache.js +25 -0
  76. package/dist/lib/config/app.config.d.ts.map +1 -1
  77. package/dist/lib/config/app.config.js +6 -7
  78. package/dist/lib/config/types.d.ts +8 -6
  79. package/dist/lib/config/types.d.ts.map +1 -1
  80. package/dist/lib/db-pool.d.ts +31 -0
  81. package/dist/lib/db-pool.d.ts.map +1 -0
  82. package/dist/lib/db-pool.js +75 -0
  83. package/dist/lib/db.d.ts +1 -1
  84. package/dist/lib/db.d.ts.map +1 -1
  85. package/dist/lib/db.js +11 -14
  86. package/dist/lib/entities/api-generator.js +5 -5
  87. package/dist/lib/entities/entity-hooks.d.ts.map +1 -1
  88. package/dist/lib/entities/entity-hooks.js +4 -0
  89. package/dist/lib/mcp/errors.d.ts +2 -0
  90. package/dist/lib/mcp/errors.d.ts.map +1 -1
  91. package/dist/lib/mcp/errors.js +3 -1
  92. package/dist/lib/mcp/index.d.ts +1 -1
  93. package/dist/lib/mcp/index.d.ts.map +1 -1
  94. package/dist/lib/mcp/index.js +2 -1
  95. package/dist/lib/oauth/token-refresh.d.ts.map +1 -1
  96. package/dist/lib/oauth/token-refresh.js +3 -6
  97. package/dist/lib/permissions/check.d.ts +9 -0
  98. package/dist/lib/permissions/check.d.ts.map +1 -1
  99. package/dist/lib/permissions/check.js +11 -1
  100. package/dist/lib/services/generic-entity.service.d.ts.map +1 -1
  101. package/dist/lib/services/generic-entity.service.js +41 -7
  102. package/dist/lib/services/transactional-meta.service.d.ts.map +1 -1
  103. package/dist/lib/services/transactional-meta.service.js +7 -8
  104. package/dist/lib/teams/actions.d.ts.map +1 -1
  105. package/dist/lib/teams/actions.js +0 -1
  106. package/dist/lib/validation.d.ts +5 -0
  107. package/dist/lib/validation.d.ts.map +1 -1
  108. package/dist/lib/validation.js +2 -0
  109. package/dist/messages/de/billing.json +12 -1
  110. package/dist/messages/de/common.json +2 -0
  111. package/dist/messages/de/docs.json +1 -0
  112. package/dist/messages/de/features.json +16 -4
  113. package/dist/messages/de/home.json +5 -0
  114. package/dist/messages/de/index.d.ts +68 -25
  115. package/dist/messages/de/index.d.ts.map +1 -1
  116. package/dist/messages/de/index.js +2 -0
  117. package/dist/messages/de/index.ts +2 -0
  118. package/dist/messages/de/settings.json +1 -0
  119. package/dist/messages/de/teams.json +3 -18
  120. package/dist/messages/en/billing.json +1 -1
  121. package/dist/messages/en/common.json +2 -0
  122. package/dist/messages/en/docs.json +1 -0
  123. package/dist/messages/en/features.json +16 -4
  124. package/dist/messages/en/home.json +5 -0
  125. package/dist/messages/en/index.d.ts +58 -25
  126. package/dist/messages/en/index.d.ts.map +1 -1
  127. package/dist/messages/en/index.js +2 -0
  128. package/dist/messages/en/index.ts +2 -0
  129. package/dist/messages/en/settings.json +1 -0
  130. package/dist/messages/en/teams.json +3 -18
  131. package/dist/messages/es/billing.json +1 -1
  132. package/dist/messages/es/common.json +2 -0
  133. package/dist/messages/es/docs.json +1 -0
  134. package/dist/messages/es/features.json +16 -4
  135. package/dist/messages/es/home.json +5 -0
  136. package/dist/messages/es/index.d.ts +41 -8
  137. package/dist/messages/es/index.d.ts.map +1 -1
  138. package/dist/messages/es/index.js +2 -0
  139. package/dist/messages/es/index.ts +2 -0
  140. package/dist/messages/es/settings.json +1 -0
  141. package/dist/messages/es/teams.json +3 -0
  142. package/dist/messages/fr/billing.json +12 -1
  143. package/dist/messages/fr/common.json +2 -0
  144. package/dist/messages/fr/docs.json +1 -0
  145. package/dist/messages/fr/features.json +16 -4
  146. package/dist/messages/fr/home.json +5 -0
  147. package/dist/messages/fr/index.d.ts +68 -25
  148. package/dist/messages/fr/index.d.ts.map +1 -1
  149. package/dist/messages/fr/index.js +2 -0
  150. package/dist/messages/fr/index.ts +2 -0
  151. package/dist/messages/fr/settings.json +1 -0
  152. package/dist/messages/fr/teams.json +3 -18
  153. package/dist/messages/it/billing.json +12 -1
  154. package/dist/messages/it/common.json +2 -0
  155. package/dist/messages/it/docs.json +1 -0
  156. package/dist/messages/it/features.json +16 -4
  157. package/dist/messages/it/home.json +5 -0
  158. package/dist/messages/it/index.d.ts +68 -25
  159. package/dist/messages/it/index.d.ts.map +1 -1
  160. package/dist/messages/it/index.js +2 -0
  161. package/dist/messages/it/index.ts +2 -0
  162. package/dist/messages/it/settings.json +1 -0
  163. package/dist/messages/it/teams.json +3 -18
  164. package/dist/messages/pt/billing.json +12 -1
  165. package/dist/messages/pt/common.json +2 -0
  166. package/dist/messages/pt/docs.json +1 -0
  167. package/dist/messages/pt/features.json +16 -4
  168. package/dist/messages/pt/home.json +5 -0
  169. package/dist/messages/pt/index.d.ts +68 -25
  170. package/dist/messages/pt/index.d.ts.map +1 -1
  171. package/dist/messages/pt/index.js +2 -0
  172. package/dist/messages/pt/index.ts +2 -0
  173. package/dist/messages/pt/settings.json +1 -0
  174. package/dist/messages/pt/teams.json +3 -18
  175. package/dist/migrations/029_sample_accounts_outside_development.sql +55 -0
  176. package/dist/migrations/030_taxonomies_team_writes.sql +29 -0
  177. package/dist/migrations/031_users_role_suspended.sql +41 -0
  178. package/dist/migrations/090_sample_data.sql +10 -0
  179. package/dist/routes/(auth)/signup/page.cc.d.ts +3 -1
  180. package/dist/routes/(auth)/signup/page.cc.d.ts.map +1 -1
  181. package/dist/routes/(auth)/signup/page.cc.js +2 -2
  182. package/dist/routes/(auth)/signup/page.d.ts +3 -1
  183. package/dist/routes/(auth)/signup/page.d.ts.map +1 -1
  184. package/dist/routes/(auth)/signup/page.js +4 -3
  185. package/dist/routes/_internal/area-access.js +2 -2
  186. package/dist/routes/_internal/entity-permission-layout.d.ts.map +1 -1
  187. package/dist/routes/_internal/entity-permission-layout.js +0 -7
  188. package/dist/routes/api/devtools/config/entities/route.js +2 -2
  189. package/dist/routes/api/devtools/config/theme/route.js +2 -2
  190. package/dist/routes/api/devtools/tests/[...path]/route.js +2 -2
  191. package/dist/routes/api/devtools/tests/route.js +2 -2
  192. package/dist/routes/api/mcp/route.d.ts.map +1 -1
  193. package/dist/routes/api/mcp/route.js +7 -1
  194. package/dist/routes/api/superadmin/subscriptions/route.js +2 -2
  195. package/dist/routes/api/superadmin/teams/[teamId]/route.js +2 -2
  196. package/dist/routes/api/superadmin/teams/route.js +2 -2
  197. package/dist/routes/api/superadmin/users/[userId]/route.d.ts +5 -3
  198. package/dist/routes/api/superadmin/users/[userId]/route.d.ts.map +1 -1
  199. package/dist/routes/api/superadmin/users/[userId]/route.js +46 -23
  200. package/dist/routes/api/superadmin/users/route.js +2 -2
  201. package/dist/routes/api/user/delete-account/route.d.ts.map +1 -1
  202. package/dist/routes/api/user/delete-account/route.js +2 -3
  203. package/dist/routes/api/user/plan-flags/route.d.ts.map +1 -1
  204. package/dist/routes/api/user/plan-flags/route.js +5 -5
  205. package/dist/routes/api/user/profile/route.js +3 -3
  206. package/dist/routes/api/v1/auth/signup-with-invite/route.d.ts.map +1 -1
  207. package/dist/routes/api/v1/auth/signup-with-invite/route.js +80 -44
  208. package/dist/routes/api/v1/media/upload/route.d.ts +2 -1
  209. package/dist/routes/api/v1/media/upload/route.d.ts.map +1 -1
  210. package/dist/routes/api/v1/media/upload/route.js +10 -2
  211. package/dist/routes/api/v1/post-categories/[id]/route.d.ts.map +1 -1
  212. package/dist/routes/api/v1/post-categories/[id]/route.js +39 -34
  213. package/dist/routes/api/v1/post-categories/route.d.ts.map +1 -1
  214. package/dist/routes/api/v1/post-categories/route.js +18 -14
  215. package/dist/routes/api/v1/team-invitations/route.d.ts.map +1 -1
  216. package/dist/routes/api/v1/team-invitations/route.js +12 -5
  217. package/dist/routes/api/v1/teams/[teamId]/members/route.d.ts.map +1 -1
  218. package/dist/routes/api/v1/teams/[teamId]/members/route.js +12 -10
  219. package/dist/routes/api/v1/teams/switch/route.d.ts.map +1 -1
  220. package/dist/routes/api/v1/teams/switch/route.js +4 -1
  221. package/dist/routes/dashboard/settings/password/page.d.ts.map +1 -1
  222. package/dist/routes/dashboard/settings/password/page.js +10 -12
  223. package/dist/routes/devtools/config/page.js +1 -1
  224. package/dist/routes/devtools/page.js +1 -1
  225. package/dist/routes/devtools/scheduled-actions/page.js +1 -1
  226. package/dist/routes/devtools/tests/[[...path]]/page.js +1 -1
  227. package/dist/routes/superadmin/users/page.js +2 -2
  228. package/dist/templates/ci-workflows/github/cypress-regression.yml +1 -1
  229. package/dist/templates/ci-workflows/github/cypress-smoke.yml +2 -1
  230. package/dist/templates/next.config.mjs +11 -31
  231. package/dist/templates/projects/blog/package.json +1 -1
  232. package/dist/templates/projects/crm/package.json +1 -1
  233. package/dist/templates/projects/productivity/package.json +1 -1
  234. package/dist/templates/projects/starter/README.md +2 -0
  235. package/dist/templates/projects/starter/config/app.config.ts +8 -0
  236. package/dist/templates/projects/starter/docs/public/01-overview/01-introduction.md +2 -0
  237. package/dist/templates/projects/starter/templates/dashboard/analytics/layout.tsx +14 -0
  238. package/dist/templates/proxy.ts +18 -8
  239. package/dist/templates/public/uploads/README.md +5 -94
  240. package/migrations/029_sample_accounts_outside_development.sql +55 -0
  241. package/migrations/030_taxonomies_team_writes.sql +29 -0
  242. package/migrations/031_users_role_suspended.sql +41 -0
  243. package/migrations/090_sample_data.sql +10 -0
  244. package/package.json +3 -3
  245. package/scripts/build/registry/__tests__/build-output.test.mjs +1 -1
  246. package/scripts/build/registry/__tests__/project-mode-root-first.test.mjs +6 -6
  247. package/scripts/build/registry/__tests__/write-places.test.mjs +1 -1
  248. package/scripts/build/registry/contracts/__tests__/contracts-wire.test.mjs +1 -1
  249. package/scripts/build/registry/contracts/__tests__/contracts.test.mjs +1 -1
  250. package/scripts/build/registry/host/README.md +2 -2
  251. package/scripts/build/registry/host/__tests__/facade-emitter.test.mjs +14 -13
  252. package/scripts/build/registry/host/__tests__/host-mode.test.mjs +1 -1
  253. package/scripts/build/registry/host/__tests__/host-project-composition.test.mjs +1 -1
  254. package/scripts/build/registry/host/__tests__/host-project.test.mjs +2 -2
  255. package/scripts/build/registry/host/__tests__/host-proxy-areas.test.mjs +99 -1
  256. package/scripts/build/registry/host/__tests__/host-robustness.test.mjs +1 -1
  257. package/scripts/build/registry/host/facade-emitter.mjs +1 -1
  258. package/scripts/build/registry/host/next-route-exports.json +2 -2
  259. package/scripts/build/registry/host/proxy-areas.mjs +92 -2
  260. package/scripts/db/run-migrations.mjs +30 -10
  261. package/scripts/db/sample-data.mjs +44 -0
  262. package/scripts/db/verify-theme-migrations.mjs +7 -1
  263. package/templates/ci-workflows/github/cypress-regression.yml +1 -1
  264. package/templates/ci-workflows/github/cypress-smoke.yml +2 -1
  265. package/templates/next.config.mjs +11 -31
  266. package/templates/projects/blog/package.json +1 -1
  267. package/templates/projects/crm/package.json +1 -1
  268. package/templates/projects/productivity/package.json +1 -1
  269. package/templates/projects/starter/README.md +2 -0
  270. package/templates/projects/starter/config/app.config.ts +8 -0
  271. package/templates/projects/starter/docs/public/01-overview/01-introduction.md +2 -0
  272. package/templates/projects/starter/templates/dashboard/analytics/layout.tsx +14 -0
  273. package/templates/proxy.ts +18 -8
  274. package/templates/public/uploads/README.md +5 -94
  275. package/tests/jest/__mocks__/@nextsparkjs/registries/api-presets-registry.ts +2 -0
  276. package/tests/jest/__mocks__/@nextsparkjs/registries/mcp-registry.ts +2 -0
  277. package/tests/jest/__mocks__/@nextsparkjs/registries/permissions-registry.ts +5 -4
  278. package/dist/templates/projects/starter/public/brand/.gitkeep +0 -0
  279. package/dist/templates/public/uploads/.gitignore +0 -6
  280. package/dist/templates/public/uploads/temp/.gitkeep +0 -1
  281. package/templates/projects/starter/public/brand/.gitkeep +0 -0
  282. package/templates/public/uploads/.gitignore +0 -6
  283. package/templates/public/uploads/temp/.gitkeep +0 -1
@@ -20,8 +20,8 @@
20
20
  * themselves and use only x-pathname from here, as a routing hint; the
21
21
  * identity headers are still stripped and re-set for project code.
22
22
  */
23
- import { betterFetch } from '@better-fetch/fetch'
24
23
  import { NextRequest, NextResponse } from 'next/server'
24
+ import { auth } from '@nextsparkjs/core/lib/auth'
25
25
  import {
26
26
  hasProjectMiddleware,
27
27
  executeProjectMiddleware,
@@ -181,14 +181,25 @@ function redirectAccessDenied(request: NextRequest): NextResponse {
181
181
  }
182
182
 
183
183
  /**
184
- * The session the request's cookies carry, asked of this app's own auth route,
185
- * which is served under the base path like every other route.
184
+ * The session the request's cookies carry, read in this process the way core's
185
+ * server code reads it. Asking the app's own /api/auth/get-session over HTTP
186
+ * built the URL from the request's origin, which behind a proxy that
187
+ * terminates TLS (X-Forwarded-Proto: https) is https on a port that speaks
188
+ * plain HTTP: the fetch failed and every signed-in user was sent to /login.
189
+ * Nothing here leaves the process, so no forwarded scheme or host can point
190
+ * the check anywhere. `disableRefresh`: this response cannot carry the renewed
191
+ * session cookie, so the rolling renewal is left to the auth route
192
+ * (lib/auth/session-refresh). `disableCookieCache`: the session comes from the
193
+ * database, not the signed session_data cookie, so a sign-out, a revoked
194
+ * session or a role change counts at once (lib/auth/authorization-session).
195
+ * A suspended account's session is no session (lib/auth/suspension).
186
196
  */
187
- function getSession(request: NextRequest) {
188
- return betterFetch<Session>('/api/auth/get-session', {
189
- baseURL: `${request.nextUrl.origin}${request.nextUrl.basePath}`,
190
- headers: { cookie: request.headers.get('cookie') || '' },
197
+ async function getSession(request: NextRequest): Promise<Session | null> {
198
+ const session = await auth.api.getSession({
199
+ headers: new Headers({ cookie: request.headers.get('cookie') || '' }),
200
+ query: { disableRefresh: true, disableCookieCache: true },
191
201
  })
202
+ return session && (session.user as { role?: unknown }).role !== 'suspended' ? (session as Session) : null
192
203
  }
193
204
 
194
205
  /**
@@ -490,7 +501,6 @@ export async function proxy(request: NextRequest) {
490
501
  const verifiedSession = async (): Promise<Session | null> => {
491
502
  if (!sessionPromise) {
492
503
  sessionPromise = getSession(request)
493
- .then(({ data }) => data)
494
504
  .catch(error => {
495
505
  sessionError = error
496
506
  return null
@@ -1,97 +1,8 @@
1
- # Media Upload System
1
+ # Media uploads
2
2
 
3
- ## Overview
4
- This application uses **Vercel Blob Storage** for all media uploads. Files are stored in the cloud, not on the local filesystem.
3
+ `POST /api/v1/media/upload` stores files in **Vercel Blob** when `BLOB_READ_WRITE_TOKEN` is set. The media library lists the Blob URLs.
5
4
 
6
- > **Note:** This directory (`public/uploads/`) is legacy from a previous filesystem-based implementation and is no longer used.
5
+ - **Development without a token:** files are written to `public/uploads/temp/` (git-ignored) so the library works offline.
6
+ - **Production without a token:** the upload answers `503` with code `STORAGE_NOT_CONFIGURED`. Nothing is written to disk: a file under `public/` is not served until the next restart and does not survive a redeploy.
7
7
 
8
- ## Upload Endpoint
9
- **POST** `/api/v1/media/upload`
10
-
11
- ### Authentication
12
- Requires valid user session (Better Auth)
13
-
14
- ### Request
15
- - **Content-Type**: `multipart/form-data`
16
- - **Field name**: `files` (accepts multiple files)
17
-
18
- ### Supported File Types
19
- - Images: JPEG, PNG, GIF, WebP
20
- - Videos: MP4, MPEG, QuickTime, WebM
21
-
22
- ### File Size Limit
23
- - Maximum: 10MB per file
24
-
25
- ### Response
26
- ```json
27
- {
28
- "message": "Files uploaded successfully",
29
- "urls": [
30
- "https://xxxxx.public.blob.vercel-storage.com/uploads/temp/1234567890_abc123.jpg",
31
- "https://xxxxx.public.blob.vercel-storage.com/uploads/temp/1234567890_def456.png"
32
- ],
33
- "count": 2
34
- }
35
- ```
36
-
37
- ## File Naming Convention
38
- Files are automatically renamed using the pattern:
39
- ```
40
- {timestamp}_{randomString}.{extension}
41
- ```
42
-
43
- Example: `1704067200000_k9x2m4p.jpg`
44
-
45
- ## Storage Location
46
- - **Storage Provider**: Vercel Blob (cloud object storage)
47
- - **Blob Path**: `uploads/temp/{filename}` (organizational prefix, not a filesystem path)
48
- - **Public URL**: `https://xxxxx.public.blob.vercel-storage.com/uploads/temp/{filename}`
49
-
50
- ### How It Works
51
-
52
- **Local Development:**
53
- - Files are uploaded directly to Vercel Blob using `BLOB_READ_WRITE_TOKEN` from `.env`
54
- - No files are saved to your local filesystem
55
- - URLs point to Vercel's CDN
56
-
57
- **Production/Staging:**
58
- - Same behavior - files go directly to Vercel Blob
59
- - Uses environment-specific Blob token
60
- - No filesystem writes (Vercel serverless is read-only)
61
-
62
- ## Usage in AI Content Generation
63
- The generate-content endpoint (`/api/content-buddy/generate-content`) automatically processes images for AI analysis.
64
-
65
- **Supported URL formats:**
66
- - Vercel Blob URLs: `https://xxxxx.public.blob.vercel-storage.com/uploads/temp/image.jpg`
67
- - External URLs: `https://example.com/image.jpg` (also supported)
68
-
69
- ## Configuration
70
- Requires `BLOB_READ_WRITE_TOKEN` environment variable:
71
-
72
- ```env
73
- BLOB_READ_WRITE_TOKEN="vercel_blob_rw_..."
74
- ```
75
-
76
- ## Cleanup
77
- Files are managed through Vercel Blob's retention policies.
78
-
79
- **Manual cleanup** (if needed):
80
- ```typescript
81
- import { del } from '@vercel/blob'
82
-
83
- // Delete specific file
84
- await del(fileUrl)
85
-
86
- // Or list and delete old files
87
- import { list } from '@vercel/blob'
88
- const { blobs } = await list({ prefix: 'uploads/temp/' })
89
- // Filter and delete as needed
90
- ```
91
-
92
- ## Security Notes
93
- - All uploads require authentication
94
- - File type validation prevents malicious uploads
95
- - File size limits prevent DoS attacks
96
- - Random filenames prevent enumeration attacks
97
- - Files stored in secure Vercel Blob infrastructure
8
+ Self-hosting needs a storage provider: set `BLOB_READ_WRITE_TOKEN` (a Vercel Blob token, starts with `vercel_blob_`). See the media library docs.
@@ -0,0 +1,55 @@
1
+ -- Migration: 029_sample_accounts_outside_development.sql
2
+ -- Description: sample accounts keep no usable credentials outside development
3
+ --
4
+ -- Sample data (090_sample_data.sql and the projects' *sample_data* files) is
5
+ -- only applied by an explicit development opt-in (`nextspark db:seed`,
6
+ -- `--sample-data` or NEXTSPARK_SEED_SAMPLE_DATA=1). Databases migrated before
7
+ -- that may still hold the sample accounts. This migration finds them by the
8
+ -- sample password, never by name, so a renamed account is found and an account
9
+ -- whose password was changed is left alone; for each one it:
10
+ -- - removes the sample password (its "credential" account row),
11
+ -- - removes its sessions,
12
+ -- - deactivates its API keys (status 'inactive'; deleting them would delete
13
+ -- their api_audit_log rows),
14
+ -- - sets its global role to 'member' (027's trigger then removes it from the
15
+ -- System Admin Team, as for any user who loses that role).
16
+ -- It also removes the sample sessions and deactivates the sample API key by
17
+ -- their fixed values, whoever owns them now. Nothing else is changed.
18
+ --
19
+ -- It runs once, everywhere, except in a development run that opted into sample
20
+ -- data (the runner sets nextspark.seed_sample_data to 'on' then), where those
21
+ -- accounts are wanted. To keep using one of them deliberately, give it a new
22
+ -- password before upgrading; to restore one afterwards, set its role back and
23
+ -- reset its password.
24
+
25
+ DO $$
26
+ DECLARE
27
+ sample_passwords CONSTANT TEXT[] := ARRAY[
28
+ '22de14d5472248ed0bece911df908b2a:d29576424798ba6845d348a3767c0b0f38a00f2aca461b3b1d34b99a93cab06c86774c6edb183e6d6ec47457649b032a49a7b60a48f6f4f7fbbc4ea40258f19f',
29
+ '3db9e98e2b4d3caca97fdf2783791cbc:34b293de615caf277a237773208858e960ea8aa10f1f5c5c309b632f192cac34d52ceafbd338385616f4929e4b1b6c055b67429c6722ffdb80b01d9bf4764866'
30
+ ];
31
+ sample_session_tokens CONSTANT TEXT[] := ARRAY['tmt_superadmin_session_token_001', 'tmt_developer_session_token_001'];
32
+ sample_key_hashes CONSTANT TEXT[] := ARRAY[
33
+ '205f13ae4d2e18df417e61123bb84030c124fcb5013699046379ae1b5ec04324',
34
+ 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
35
+ ];
36
+ sample_users TEXT[];
37
+ BEGIN
38
+ IF current_setting('nextspark.seed_sample_data', true) = 'on' THEN
39
+ RAISE NOTICE 'Sample accounts: kept (development run with sample data).';
40
+ RETURN;
41
+ END IF;
42
+
43
+ SELECT COALESCE(array_agg(DISTINCT "userId"), '{}') INTO sample_users
44
+ FROM "account"
45
+ WHERE "providerId" = 'credential' AND "password" = ANY (sample_passwords);
46
+
47
+ DELETE FROM "account" WHERE "providerId" = 'credential' AND "password" = ANY (sample_passwords);
48
+ DELETE FROM "session" WHERE "userId" = ANY (sample_users) OR token = ANY (sample_session_tokens);
49
+ UPDATE "api_key" SET status = 'inactive', "updatedAt" = now()
50
+ WHERE status IS DISTINCT FROM 'inactive' AND ("userId" = ANY (sample_users) OR "keyHash" = ANY (sample_key_hashes));
51
+ UPDATE "users" SET role = 'member', "updatedAt" = now()
52
+ WHERE id = ANY (sample_users) AND role <> 'member';
53
+
54
+ RAISE NOTICE 'Sample accounts: % disabled (no password, sessions or active API keys; role member).', cardinality(sample_users);
55
+ END $$;
@@ -0,0 +1,29 @@
1
+ -- Migration: 030_taxonomies_team_writes.sql
2
+ -- Description: Taxonomy rows are written by members of their team; rows without a team only by a superadmin.
3
+ --
4
+ -- 021 let any authenticated user write a taxonomy row with "teamId" IS NULL. Post categories are now created in
5
+ -- the caller's team (lib/api/post-categories.ts), and the rows without a team that earlier releases created stay
6
+ -- readable by everyone but are changed only by a superadmin. This policy says the same on the RLS-enforced
7
+ -- connection: reads are as before, writes need the row's team (or is_superadmin()).
8
+ -- is_superadmin() also accepts a developer enrolled in the System Admin Team; the API (lib/api/post-categories.ts) is
9
+ -- stricter and lets only users.role = 'superadmin' change a row without a team.
10
+ -- Idempotent; the anon read policy from 006 is unchanged.
11
+
12
+ DROP POLICY IF EXISTS "taxonomies auth can do all" ON public.taxonomies;
13
+ DROP POLICY IF EXISTS "taxonomies auth can select" ON public.taxonomies;
14
+ DROP POLICY IF EXISTS "taxonomies team can write" ON public.taxonomies;
15
+
16
+ CREATE POLICY "taxonomies auth can select"
17
+ ON public.taxonomies
18
+ FOR SELECT TO authenticated
19
+ USING (
20
+ "teamId" IS NULL
21
+ OR "teamId" = ANY(public.get_user_team_ids())
22
+ OR public.is_superadmin()
23
+ );
24
+
25
+ CREATE POLICY "taxonomies team can write"
26
+ ON public.taxonomies
27
+ FOR ALL TO authenticated
28
+ USING ("teamId" = ANY(public.get_user_team_ids()) OR public.is_superadmin())
29
+ WITH CHECK ("teamId" = ANY(public.get_user_team_ids()) OR public.is_superadmin());
@@ -0,0 +1,41 @@
1
+ -- Migration: 031_users_role_suspended.sql
2
+ -- Description: users.role accepts 'suspended' (lib/auth/suspension.ts).
3
+ --
4
+ -- The superadmin "suspend" action sets users.role = 'suspended', which check_users_role (002: member, superadmin,
5
+ -- developer) refused. This adds 'suspended' to the roles the constraint already accepts and keeps every other one,
6
+ -- including roles a project added to it. Idempotent: a constraint that already accepts 'suspended' is left alone. A
7
+ -- constraint that is not a plain list of roles is left alone too, with a notice (add 'suspended' to it yourself).
8
+
9
+ DO $$
10
+ DECLARE
11
+ def TEXT;
12
+ roles TEXT[];
13
+ BEGIN
14
+ SELECT pg_get_constraintdef(c.oid) INTO def
15
+ FROM pg_constraint c
16
+ WHERE c.conrelid = 'public.users'::regclass AND c.conname = 'check_users_role';
17
+
18
+ IF def IS NULL THEN
19
+ ALTER TABLE public."users" ADD CONSTRAINT check_users_role
20
+ CHECK ("role" IN ('member', 'superadmin', 'developer', 'suspended'));
21
+ RETURN;
22
+ END IF;
23
+
24
+ IF def ~ '\msuspended\M' THEN
25
+ RETURN;
26
+ END IF;
27
+
28
+ -- "role" IN (...) is stored as: CHECK ((role = ANY (ARRAY['member'::text, ...])))
29
+ IF def !~ '^CHECK \(\(+"?role"?\)?(::text)? = ANY \(\(?ARRAY\[' THEN
30
+ RAISE NOTICE 'check_users_role is not a plain list of roles (%); add ''suspended'' to it to use the suspend action', def;
31
+ RETURN;
32
+ END IF;
33
+
34
+ SELECT array_agg(m[1]) INTO roles FROM regexp_matches(def, '''([^'']+)''', 'g') AS m;
35
+
36
+ ALTER TABLE public."users" DROP CONSTRAINT check_users_role;
37
+ EXECUTE format(
38
+ 'ALTER TABLE public."users" ADD CONSTRAINT check_users_role CHECK ("role" IN (%s))',
39
+ (SELECT string_agg(quote_literal(r), ', ') FROM unnest(array_append(roles, 'suspended')) AS r)
40
+ );
41
+ END $$;
@@ -7,6 +7,16 @@
7
7
  -- Theme-specific demo data (TMT.dev users, companies, invoices) is in:
8
8
  -- migrations/090_demo_users_teams.sql
9
9
 
10
+ -- Only a development run that asked for sample data applies this file (see
11
+ -- scripts/db/sample-data.mjs); anything else that runs it, such as a tool that
12
+ -- applies every core .sql file in order, fails here instead of creating these accounts.
13
+ DO $$
14
+ BEGIN
15
+ IF current_setting('nextspark.seed_sample_data', true) IS DISTINCT FROM 'on' THEN
16
+ RAISE EXCEPTION 'Sample data is applied only by `pnpm db:seed` (or NEXTSPARK_SEED_SAMPLE_DATA=1) outside production. If you ran that, the session setting it relies on was lost: a transaction-mode pooler (PgBouncer, Supavisor on port 6543) drops it, so point MIGRATE_DATABASE_URL at a direct connection.';
17
+ END IF;
18
+ END $$;
19
+
10
20
  -- ============================================
11
21
  -- STEP 1: CREATE CORE TEST USERS
12
22
  -- ============================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextsparkjs/core",
3
- "version": "0.1.0-beta.195",
3
+ "version": "0.1.0-beta.197",
4
4
  "description": "NextSpark - The complete SaaS framework for Next.js",
5
5
  "license": "MIT",
6
6
  "author": "NextSpark <hello@nextspark.dev>",
@@ -390,7 +390,7 @@
390
390
  "scripts"
391
391
  ],
392
392
  "peerDependencies": {
393
- "next": "~16.3.6",
393
+ "next": "~16.3.8",
394
394
  "react": "^19.2.0",
395
395
  "react-dom": "^19.2.0"
396
396
  },
@@ -496,7 +496,7 @@
496
496
  "use-intl": "^4.9.1",
497
497
  "uuid": "^13.0.0",
498
498
  "zod": "^4.3.6",
499
- "@nextsparkjs/ui": "0.1.0-beta.195"
499
+ "@nextsparkjs/ui": "0.1.0-beta.197"
500
500
  },
501
501
  "engines": {
502
502
  "node": ">=22.14.0"
@@ -29,7 +29,7 @@ const RAW_CONTROL = /[\u0000-\u0009\u000b-\u001f\u007f-\u009f\u061c\u200e\u200f\
29
29
  async function createProject(parent = tmpdir(), name = 'nextspark-build-output-test-') {
30
30
  const root = await mkdtemp(join(parent, name))
31
31
  await writeFile(join(root, 'nextspark.config.ts'), 'export default {}\n')
32
- await writeFile(join(root, 'package.json'), '{"dependencies":{"next":"16.3.6"}}')
32
+ await writeFile(join(root, 'package.json'), '{"dependencies":{"next":"16.3.8"}}')
33
33
  await mkdir(join(root, 'src', 'app'), { recursive: true })
34
34
  return root
35
35
  }
@@ -17,7 +17,7 @@ async function project(files = {}) {
17
17
 
18
18
  test('the nearest nextspark.config.ts owns every project source path', async () => {
19
19
  const root = await project({
20
- 'package.json': JSON.stringify({ dependencies: { next: '16.3.6' } }),
20
+ 'package.json': JSON.stringify({ dependencies: { next: '16.3.8' } }),
21
21
  'nextspark.config.ts': 'export default {}\n',
22
22
  })
23
23
  const nested = join(root, 'entities', 'tasks')
@@ -40,7 +40,7 @@ test('the nearest nextspark.config.ts owns every project source path', async ()
40
40
 
41
41
  test('a next.config file cannot select a project without nextspark.config.ts', async () => {
42
42
  const root = await project({
43
- 'package.json': JSON.stringify({ dependencies: { next: '16.3.6' } }),
43
+ 'package.json': JSON.stringify({ dependencies: { next: '16.3.8' } }),
44
44
  'next.config.mjs': 'export default {}\n',
45
45
  })
46
46
  try {
@@ -66,7 +66,7 @@ test('an enabled packaged plugin resolves through the project dependency tree',
66
66
  const root = await project({
67
67
  'package.json': JSON.stringify({
68
68
  dependencies: {
69
- next: '16.3.6',
69
+ next: '16.3.8',
70
70
  '@example/plugin-search': '1.0.0',
71
71
  },
72
72
  }),
@@ -98,7 +98,7 @@ test('an enabled packaged plugin that is not installed fails with an actionable
98
98
  const root = await project({
99
99
  'package.json': JSON.stringify({
100
100
  dependencies: {
101
- next: '16.3.6',
101
+ next: '16.3.8',
102
102
  '@example/plugin-search': '1.0.0',
103
103
  },
104
104
  }),
@@ -119,7 +119,7 @@ test('a packaged plugin cannot use a path segment as its logical name', async ()
119
119
  const root = await project({
120
120
  'package.json': JSON.stringify({
121
121
  dependencies: {
122
- next: '16.3.6',
122
+ next: '16.3.8',
123
123
  '@example/plugin-search': '1.0.0',
124
124
  },
125
125
  }),
@@ -146,7 +146,7 @@ test('a local plugin shadows an installed packaged plugin with the same NextSpar
146
146
  const root = await project({
147
147
  'package.json': JSON.stringify({
148
148
  dependencies: {
149
- next: '16.3.6',
149
+ next: '16.3.8',
150
150
  '@example/plugin-search': '1.0.0',
151
151
  },
152
152
  }),
@@ -269,7 +269,7 @@ async function snapshot(root) {
269
269
  async function buildableProject() {
270
270
  const project = await directory()
271
271
  await writeIn(project.root, 'nextspark.config.ts', 'export default {}\n')
272
- await writeIn(project.root, 'package.json', '{"dependencies":{"next":"16.3.6"}}')
272
+ await writeIn(project.root, 'package.json', '{"dependencies":{"next":"16.3.8"}}')
273
273
  await writeIn(project.root, 'templates/pricing/page.tsx', 'export default function Pricing() { return null }\n')
274
274
  return project
275
275
  }
@@ -22,7 +22,7 @@ async function planFor(files) {
22
22
  const root = temp()
23
23
  try {
24
24
  write(root, 'nextspark.config.ts', 'export default { plugins: [] }\n')
25
- write(root, 'package.json', JSON.stringify({ name: 'wire', dependencies: { next: '16.3.6' } }))
25
+ write(root, 'package.json', JSON.stringify({ name: 'wire', dependencies: { next: '16.3.8' } }))
26
26
  write(root, 'config/theme.config.ts', "export const wireThemeConfig = { name: 'wire' }\n")
27
27
  for (const [path, content] of Object.entries(files)) write(root, path, content)
28
28
  const entities = await discoverAllEntities(getConfig(root), { includeCore: false })
@@ -73,7 +73,7 @@ const NOTES_CONFIG = `export const noteEntityConfig = {
73
73
  function project(extra = {}) {
74
74
  const root = temp()
75
75
  write(root, 'nextspark.config.ts', 'export default { plugins: [] }\n')
76
- write(root, 'package.json', JSON.stringify({ name: 'contracts-project', dependencies: { next: '16.3.6' } }))
76
+ write(root, 'package.json', JSON.stringify({ name: 'contracts-project', dependencies: { next: '16.3.8' } }))
77
77
  write(root, 'config/theme.config.ts', "export const contractsThemeConfig = { name: 'contracts' }\n")
78
78
  write(root, 'entities/tasks/tasks.config.ts', TASKS_CONFIG)
79
79
  write(root, 'entities/tasks/tasks.fields.ts', FIELDS)
@@ -28,7 +28,7 @@ const { target, content } = await emitFacade({
28
28
  Every problem is a `FacadeEmitError` carrying all diagnostics (`code`, `file`, `line`, `exportName`,
29
29
  `message`); nothing is dropped silently.
30
30
 
31
- ## Export table (Next.js 16.3.6)
31
+ ## Export table (Next.js 16.3.8)
32
32
 
33
33
  Source of truth: `next-route-exports.json`. The tests fail when the installed Next.js version differs,
34
34
  when Next's `AppSegmentConfigSchemaKeys` or `HTTP_METHODS` change, and when this rendering is stale.
@@ -71,7 +71,7 @@ export { listSlugs as generateStaticParams }
71
71
  Metadata image kinds accept Next's one-digit variants (`icon1`, `apple-icon9`, `opengraph-image2`,
72
72
  `twitter-image3`), mapped to the base kind.
73
73
 
74
- The emitter resolves `next/package.json` once (project first, then core) and accepts only `~16.3.6`: the table's
74
+ The emitter resolves `next/package.json` once (project first, then core) and accepts only `~16.3.8`: the table's
75
75
  `next` is the verified baseline, and any stable 16.3.x patch at or above it is accepted, while other minors/majors,
76
76
  lower patches and prereleases are refused (`NS_HOST_UNSUPPORTED_NEXT_VERSION`). It loads Next's segment config
77
77
  schema from that same package root.
@@ -124,11 +124,12 @@ test("page and layout exports include Next.js's TS-plugin ALLOWED_EXPORTS (excep
124
124
  }
125
125
  })
126
126
 
127
- test('metadata route loader still filters dynamicParams/generateSitemaps out of its re-exports', async () => {
128
- // The table forbids dynamicParams and generateStaticParams on metadata routes because the
129
- // loader either drops them or declares its own; detect a Next.js change to that code.
127
+ test('metadata route loader still filters generateSitemaps out of its re-exports', async () => {
128
+ // The table forbids dynamicParams and generateStaticParams on metadata routes: the loader
129
+ // declares its own generateStaticParams, and up to 16.3.7 it dropped dynamicParams (16.3.8
130
+ // re-exports it; the table keeps refusing it). Detect a Next.js change to that code.
130
131
  const loader = await readFile(requireFromCore.resolve('next/dist/build/webpack/loaders/next-metadata-route-loader'), 'utf8')
131
- assert.match(loader, /name !== 'default' && name !== 'generateSitemaps' && name !== 'dynamicParams'/)
132
+ assert.match(loader, /exportNames\.filter\(\(name\)=>name !== 'default' && name !== 'generateSitemaps'\);/)
132
133
  assert.match(loader, /export async function generateStaticParams/)
133
134
  })
134
135
 
@@ -595,14 +596,14 @@ test('an aliased magic export is forwarded under its source local name', async (
595
596
  assert.match(content, /^export \{ listSlugs as generateStaticParams \}$/m)
596
597
  })
597
598
 
598
- // --- fix round 3: ~baseline version range (baseline 16.3.6 since 0.1.0-beta.194) ---
599
+ // --- fix round 3: ~baseline version range (baseline 16.3.8 since 0.1.0-beta.197) ---
599
600
 
600
601
  test('isSupportedNextVersion accepts ~baseline: same minor, stable patch >= baseline', () => {
601
- assert.equal(ROUTE_EXPORT_TABLE.next, '16.3.6')
602
- for (const version of ['16.3.6', '16.3.7', '16.3.9', '16.3.10', '16.3.123', '16.3.6+build.1', '16.3.7+sha.abc-1']) assert.equal(isSupportedNextVersion(version), true, version)
602
+ assert.equal(ROUTE_EXPORT_TABLE.next, '16.3.8')
603
+ for (const version of ['16.3.8', '16.3.9', '16.3.10', '16.3.123', '16.3.8+build.1', '16.3.9+sha.abc-1']) assert.equal(isSupportedNextVersion(version), true, version)
603
604
  for (const version of [
604
- '16.3.5', '16.3.4', '16.3.0', '16.4.0', '16.2.9', '17.0.0', '15.3.6', '16.3.7-canary.0', '16.3.6-rc.1', '16.3.7-canary.0+build.1',
605
- '16.3.5+build.1', '16.3.6+', '16.3.6+bad..meta', '16.3', 'latest', '', undefined,
605
+ '16.3.7', '16.3.6', '16.3.5', '16.3.4', '16.3.0', '16.4.0', '16.2.9', '17.0.0', '15.3.6', '16.3.7-canary.0', '16.3.8-rc.1', '16.3.7-canary.0+build.1',
606
+ '16.3.5+build.1', '16.3.8+', '16.3.8+bad..meta', '16.3', 'latest', '', undefined,
606
607
  ]) {
607
608
  assert.equal(isSupportedNextVersion(version), false, String(version))
608
609
  }
@@ -625,7 +626,7 @@ test('a later 16.3 patch is accepted and its own schema internal is used', async
625
626
  }
626
627
  })
627
628
 
628
- for (const version of ['16.3.5', '16.4.0', '17.0.0', '16.3.6-canary.0']) {
629
+ for (const version of ['16.3.5', '16.4.0', '17.0.0', '16.3.8-canary.0']) {
629
630
  test(`${DIAGNOSTICS.UNSUPPORTED_NEXT_VERSION}: next@${version} is refused`, async () => {
630
631
  const dir = await fakeNextProject(version, "exports.AppSegmentConfigSchemaKeys = []\nexports.parseAppSegmentConfig = () => ({})\n")
631
632
  try {
@@ -634,7 +635,7 @@ for (const version of ['16.3.5', '16.4.0', '17.0.0', '16.3.6-canary.0']) {
634
635
  error => {
635
636
  assert.deepEqual(error.diagnostics.map(d => d.code), [DIAGNOSTICS.UNSUPPORTED_NEXT_VERSION])
636
637
  assert.match(error.message, new RegExp(`next@${version.replace(/\./g, '\\.')}`))
637
- assert.match(error.message, /next@~16\.3\.6/)
638
+ assert.match(error.message, /next@~16\.3\.8/)
638
639
  return true
639
640
  }
640
641
  )
@@ -646,9 +647,9 @@ for (const version of ['16.3.5', '16.4.0', '17.0.0', '16.3.6-canary.0']) {
646
647
 
647
648
  // --- fix round 4: build metadata ----------------------------------------------
648
649
 
649
- test('a Next.js version with build metadata (16.3.6+build.1) is accepted end to end', async () => {
650
+ test('a Next.js version with build metadata (16.3.8+build.1) is accepted end to end', async () => {
650
651
  const marker = "exports.AppSegmentConfigSchemaKeys = []\nexports.parseAppSegmentConfig = () => ({})\n"
651
- const dir = await fakeNextProject('16.3.6+build.1', marker)
652
+ const dir = await fakeNextProject('16.3.8+build.1', marker)
652
653
  try {
653
654
  assert.equal(loadNextSegmentConfig(dir).error, undefined)
654
655
  const { content } = await emitFacade({ kind: 'page', target: 'a/page.tsx', specifier: '@/a', file: FILE, source: PAGE_WITH_CONFIG, projectRoot: dir })
@@ -17,7 +17,7 @@ const PREPARE_CLI = join(CORE_ROOT, 'scripts/build/registry/host/prepare-cli.mjs
17
17
  function project({ legacyApp = false } = {}) {
18
18
  const root = mkdtempSync(join(tmpdir(), 'nextspark-host-mode-'))
19
19
  write(root, 'nextspark.config.ts', 'export default { plugins: [] }\n')
20
- write(root, 'package.json', JSON.stringify({ name: 'host-mode', dependencies: { next: '16.3.6' } }))
20
+ write(root, 'package.json', JSON.stringify({ name: 'host-mode', dependencies: { next: '16.3.8' } }))
21
21
  write(root, 'templates/pricing/page.tsx', PAGE('Pricing'))
22
22
  if (legacyApp) write(root, 'src/app/layout.tsx', 'export default function Layout({ children }) { return children }\n')
23
23
  return { root, cleanup: () => rmSync(root, { recursive: true, force: true }) }
@@ -36,7 +36,7 @@ const LAYOUT = 'export default function Layout({ children }: { children: React.R
36
36
  function project(extra = {}) {
37
37
  const root = mkdtempSync(join(tmpdir(), 'nextspark-host-composition-'))
38
38
  write(root, 'nextspark.config.ts', "export default { plugins: [], billing: { webhookExtensions: { stripe: './lib/billing/stripe-webhook-extensions' } } }\n")
39
- write(root, 'package.json', JSON.stringify({ name: 'composition', dependencies: { next: '16.3.6' } }))
39
+ write(root, 'package.json', JSON.stringify({ name: 'composition', dependencies: { next: '16.3.8' } }))
40
40
  write(root, 'config/theme.config.ts', "export const compositionThemeConfig = { name: 'composition' }\n")
41
41
  write(root, 'styles/globals.css', '@import "tailwindcss";\n')
42
42
  write(root, 'lib/billing/stripe-webhook-extensions.ts', 'export const stripeWebhookExtensions = {}\n')
@@ -16,7 +16,7 @@ const PREPARE_CLI = join(CORE_ROOT, 'scripts/build/registry/host/prepare-cli.mjs
16
16
  function project() {
17
17
  const root = mkdtempSync(join(tmpdir(), 'nextspark-host-project-'))
18
18
  write(root, 'nextspark.config.ts', 'export default { plugins: [] }\n')
19
- write(root, 'package.json', JSON.stringify({ name: 'host-project', dependencies: { next: '16.3.6' } }))
19
+ write(root, 'package.json', JSON.stringify({ name: 'host-project', dependencies: { next: '16.3.8' } }))
20
20
  write(root, 'templates/pricing/page.tsx', PAGE('Pricing'))
21
21
  write(root, 'api/ping/route.ts', ROUTE)
22
22
  return { root, cleanup: () => rmSync(root, { recursive: true, force: true }) }
@@ -41,7 +41,7 @@ test('a real project: the registry build is staged and published with the host,
41
41
 
42
42
  const record = readGeneration(root)
43
43
  assert.equal(Object.keys(record.files).length, first.files.length)
44
- assert.ok(record.versions.core && record.versions.next === '16.3.6')
44
+ assert.ok(record.versions.core && record.versions.next === '16.3.8')
45
45
  assert.ok(record.inputs.files['nextspark.config.ts'] && record.inputs.files['templates/pricing/page.tsx'])
46
46
  assert.deepEqual(await checkHost(config), { ok: true, problems: [] })
47
47