@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.
- package/README.md +2 -0
- package/dist/components/auth/forms/SignupForm.d.ts.map +1 -1
- package/dist/components/auth/forms/SignupForm.js +55 -45
- package/dist/components/billing/InvoicesPagination.js +2 -2
- package/dist/components/dashboard/block-editor/config-panel.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/config-panel.js +3 -2
- package/dist/components/dashboard/block-editor/entity-fields-sidebar.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/entity-fields-sidebar.js +3 -2
- package/dist/components/dashboard/navigation/DynamicNavigation.js +2 -2
- package/dist/components/settings/layouts/SettingsSidebar.d.ts.map +1 -1
- package/dist/components/settings/layouts/SettingsSidebar.js +0 -1
- package/dist/components/teams/CreateTeamButton.js +2 -2
- package/dist/components/teams/CreateTeamDialog.js +1 -1
- package/dist/components/teams/InviteMemberButton.js +3 -3
- package/dist/components/teams/InviteMemberDialog.js +1 -1
- package/dist/components/teams/TeamPendingInvitations.js +2 -2
- package/dist/components/users/tables/UsersTable.d.ts.map +1 -1
- package/dist/components/users/tables/UsersTable.js +3 -4
- package/dist/hooks/useAuthReadiness.d.ts +1 -0
- package/dist/hooks/useAuthReadiness.d.ts.map +1 -1
- package/dist/hooks/useAuthReadiness.js +3 -1
- package/dist/lib/actions/entity.actions.js +2 -2
- package/dist/lib/actions/team.actions.js +2 -2
- package/dist/lib/actions/user.actions.js +4 -4
- package/dist/lib/api/api-error.d.ts +2 -0
- package/dist/lib/api/api-error.d.ts.map +1 -1
- package/dist/lib/api/api-error.js +3 -1
- package/dist/lib/api/auth/dual-auth.d.ts +2 -1
- package/dist/lib/api/auth/dual-auth.d.ts.map +1 -1
- package/dist/lib/api/auth/dual-auth.js +25 -6
- package/dist/lib/api/auth/scope-policy.d.ts +6 -3
- package/dist/lib/api/auth/scope-policy.d.ts.map +1 -1
- package/dist/lib/api/auth.d.ts.map +1 -1
- package/dist/lib/api/auth.js +7 -0
- package/dist/lib/api/cors-response.d.ts +3 -2
- package/dist/lib/api/cors-response.d.ts.map +1 -1
- package/dist/lib/api/cors-response.js +8 -6
- package/dist/lib/api/entity/generic-handler.d.ts.map +1 -1
- package/dist/lib/api/entity/generic-handler.js +66 -15
- package/dist/lib/api/entity-handler.js +2 -2
- package/dist/lib/api/helpers.d.ts.map +1 -1
- package/dist/lib/api/helpers.js +9 -24
- package/dist/lib/api/permission-middleware.d.ts.map +1 -1
- package/dist/lib/api/permission-middleware.js +0 -2
- package/dist/lib/api/post-categories.d.ts +29 -0
- package/dist/lib/api/post-categories.d.ts.map +1 -0
- package/dist/lib/api/post-categories.js +33 -0
- package/dist/lib/api/rate-limit.d.ts +8 -0
- package/dist/lib/api/rate-limit.d.ts.map +1 -1
- package/dist/lib/api/rate-limit.js +34 -3
- package/dist/lib/api/request-origin.d.ts.map +1 -1
- package/dist/lib/api/request-origin.js +1 -1
- package/dist/lib/auth/auth-methods.d.ts +5 -2
- package/dist/lib/auth/auth-methods.d.ts.map +1 -1
- package/dist/lib/auth/auth-methods.js +4 -2
- package/dist/lib/auth/authorization-session.d.ts +14 -0
- package/dist/lib/auth/authorization-session.d.ts.map +1 -0
- package/dist/lib/auth/authorization-session.js +10 -0
- package/dist/lib/auth/request-session.d.ts +5 -0
- package/dist/lib/auth/request-session.d.ts.map +1 -1
- package/dist/lib/auth/request-session.js +3 -0
- package/dist/lib/auth/runtime-readiness.d.ts +7 -4
- package/dist/lib/auth/runtime-readiness.d.ts.map +1 -1
- package/dist/lib/auth/runtime-readiness.js +9 -6
- package/dist/lib/auth/sample-accounts.d.ts +18 -0
- package/dist/lib/auth/sample-accounts.d.ts.map +1 -0
- package/dist/lib/auth/sample-accounts.js +25 -0
- package/dist/lib/auth/suspension.d.ts +22 -0
- package/dist/lib/auth/suspension.d.ts.map +1 -0
- package/dist/lib/auth/suspension.js +43 -0
- package/dist/lib/auth.d.ts.map +1 -1
- package/dist/lib/auth.js +20 -17
- package/dist/lib/cache/public-entity-cache.d.ts +21 -0
- package/dist/lib/cache/public-entity-cache.d.ts.map +1 -0
- package/dist/lib/cache/public-entity-cache.js +25 -0
- package/dist/lib/config/app.config.d.ts.map +1 -1
- package/dist/lib/config/app.config.js +6 -7
- package/dist/lib/config/types.d.ts +8 -6
- package/dist/lib/config/types.d.ts.map +1 -1
- package/dist/lib/db-pool.d.ts +31 -0
- package/dist/lib/db-pool.d.ts.map +1 -0
- package/dist/lib/db-pool.js +75 -0
- package/dist/lib/db.d.ts +1 -1
- package/dist/lib/db.d.ts.map +1 -1
- package/dist/lib/db.js +11 -14
- package/dist/lib/entities/api-generator.js +5 -5
- package/dist/lib/entities/entity-hooks.d.ts.map +1 -1
- package/dist/lib/entities/entity-hooks.js +4 -0
- package/dist/lib/mcp/errors.d.ts +2 -0
- package/dist/lib/mcp/errors.d.ts.map +1 -1
- package/dist/lib/mcp/errors.js +3 -1
- package/dist/lib/mcp/index.d.ts +1 -1
- package/dist/lib/mcp/index.d.ts.map +1 -1
- package/dist/lib/mcp/index.js +2 -1
- package/dist/lib/oauth/token-refresh.d.ts.map +1 -1
- package/dist/lib/oauth/token-refresh.js +3 -6
- package/dist/lib/permissions/check.d.ts +9 -0
- package/dist/lib/permissions/check.d.ts.map +1 -1
- package/dist/lib/permissions/check.js +11 -1
- package/dist/lib/services/generic-entity.service.d.ts.map +1 -1
- package/dist/lib/services/generic-entity.service.js +41 -7
- package/dist/lib/services/transactional-meta.service.d.ts.map +1 -1
- package/dist/lib/services/transactional-meta.service.js +7 -8
- package/dist/lib/teams/actions.d.ts.map +1 -1
- package/dist/lib/teams/actions.js +0 -1
- package/dist/lib/validation.d.ts +5 -0
- package/dist/lib/validation.d.ts.map +1 -1
- package/dist/lib/validation.js +2 -0
- package/dist/messages/de/billing.json +12 -1
- package/dist/messages/de/common.json +2 -0
- package/dist/messages/de/docs.json +1 -0
- package/dist/messages/de/features.json +16 -4
- package/dist/messages/de/home.json +5 -0
- package/dist/messages/de/index.d.ts +68 -25
- package/dist/messages/de/index.d.ts.map +1 -1
- package/dist/messages/de/index.js +2 -0
- package/dist/messages/de/index.ts +2 -0
- package/dist/messages/de/settings.json +1 -0
- package/dist/messages/de/teams.json +3 -18
- package/dist/messages/en/billing.json +1 -1
- package/dist/messages/en/common.json +2 -0
- package/dist/messages/en/docs.json +1 -0
- package/dist/messages/en/features.json +16 -4
- package/dist/messages/en/home.json +5 -0
- package/dist/messages/en/index.d.ts +58 -25
- package/dist/messages/en/index.d.ts.map +1 -1
- package/dist/messages/en/index.js +2 -0
- package/dist/messages/en/index.ts +2 -0
- package/dist/messages/en/settings.json +1 -0
- package/dist/messages/en/teams.json +3 -18
- package/dist/messages/es/billing.json +1 -1
- package/dist/messages/es/common.json +2 -0
- package/dist/messages/es/docs.json +1 -0
- package/dist/messages/es/features.json +16 -4
- package/dist/messages/es/home.json +5 -0
- package/dist/messages/es/index.d.ts +41 -8
- package/dist/messages/es/index.d.ts.map +1 -1
- package/dist/messages/es/index.js +2 -0
- package/dist/messages/es/index.ts +2 -0
- package/dist/messages/es/settings.json +1 -0
- package/dist/messages/es/teams.json +3 -0
- package/dist/messages/fr/billing.json +12 -1
- package/dist/messages/fr/common.json +2 -0
- package/dist/messages/fr/docs.json +1 -0
- package/dist/messages/fr/features.json +16 -4
- package/dist/messages/fr/home.json +5 -0
- package/dist/messages/fr/index.d.ts +68 -25
- package/dist/messages/fr/index.d.ts.map +1 -1
- package/dist/messages/fr/index.js +2 -0
- package/dist/messages/fr/index.ts +2 -0
- package/dist/messages/fr/settings.json +1 -0
- package/dist/messages/fr/teams.json +3 -18
- package/dist/messages/it/billing.json +12 -1
- package/dist/messages/it/common.json +2 -0
- package/dist/messages/it/docs.json +1 -0
- package/dist/messages/it/features.json +16 -4
- package/dist/messages/it/home.json +5 -0
- package/dist/messages/it/index.d.ts +68 -25
- package/dist/messages/it/index.d.ts.map +1 -1
- package/dist/messages/it/index.js +2 -0
- package/dist/messages/it/index.ts +2 -0
- package/dist/messages/it/settings.json +1 -0
- package/dist/messages/it/teams.json +3 -18
- package/dist/messages/pt/billing.json +12 -1
- package/dist/messages/pt/common.json +2 -0
- package/dist/messages/pt/docs.json +1 -0
- package/dist/messages/pt/features.json +16 -4
- package/dist/messages/pt/home.json +5 -0
- package/dist/messages/pt/index.d.ts +68 -25
- package/dist/messages/pt/index.d.ts.map +1 -1
- package/dist/messages/pt/index.js +2 -0
- package/dist/messages/pt/index.ts +2 -0
- package/dist/messages/pt/settings.json +1 -0
- package/dist/messages/pt/teams.json +3 -18
- package/dist/migrations/029_sample_accounts_outside_development.sql +55 -0
- package/dist/migrations/030_taxonomies_team_writes.sql +29 -0
- package/dist/migrations/031_users_role_suspended.sql +41 -0
- package/dist/migrations/090_sample_data.sql +10 -0
- package/dist/routes/(auth)/signup/page.cc.d.ts +3 -1
- package/dist/routes/(auth)/signup/page.cc.d.ts.map +1 -1
- package/dist/routes/(auth)/signup/page.cc.js +2 -2
- package/dist/routes/(auth)/signup/page.d.ts +3 -1
- package/dist/routes/(auth)/signup/page.d.ts.map +1 -1
- package/dist/routes/(auth)/signup/page.js +4 -3
- package/dist/routes/_internal/area-access.js +2 -2
- package/dist/routes/_internal/entity-permission-layout.d.ts.map +1 -1
- package/dist/routes/_internal/entity-permission-layout.js +0 -7
- package/dist/routes/api/devtools/config/entities/route.js +2 -2
- package/dist/routes/api/devtools/config/theme/route.js +2 -2
- package/dist/routes/api/devtools/tests/[...path]/route.js +2 -2
- package/dist/routes/api/devtools/tests/route.js +2 -2
- package/dist/routes/api/mcp/route.d.ts.map +1 -1
- package/dist/routes/api/mcp/route.js +7 -1
- package/dist/routes/api/superadmin/subscriptions/route.js +2 -2
- package/dist/routes/api/superadmin/teams/[teamId]/route.js +2 -2
- package/dist/routes/api/superadmin/teams/route.js +2 -2
- package/dist/routes/api/superadmin/users/[userId]/route.d.ts +5 -3
- package/dist/routes/api/superadmin/users/[userId]/route.d.ts.map +1 -1
- package/dist/routes/api/superadmin/users/[userId]/route.js +46 -23
- package/dist/routes/api/superadmin/users/route.js +2 -2
- package/dist/routes/api/user/delete-account/route.d.ts.map +1 -1
- package/dist/routes/api/user/delete-account/route.js +2 -3
- package/dist/routes/api/user/plan-flags/route.d.ts.map +1 -1
- package/dist/routes/api/user/plan-flags/route.js +5 -5
- package/dist/routes/api/user/profile/route.js +3 -3
- package/dist/routes/api/v1/auth/signup-with-invite/route.d.ts.map +1 -1
- package/dist/routes/api/v1/auth/signup-with-invite/route.js +80 -44
- package/dist/routes/api/v1/media/upload/route.d.ts +2 -1
- package/dist/routes/api/v1/media/upload/route.d.ts.map +1 -1
- package/dist/routes/api/v1/media/upload/route.js +10 -2
- package/dist/routes/api/v1/post-categories/[id]/route.d.ts.map +1 -1
- package/dist/routes/api/v1/post-categories/[id]/route.js +39 -34
- package/dist/routes/api/v1/post-categories/route.d.ts.map +1 -1
- package/dist/routes/api/v1/post-categories/route.js +18 -14
- package/dist/routes/api/v1/team-invitations/route.d.ts.map +1 -1
- package/dist/routes/api/v1/team-invitations/route.js +12 -5
- package/dist/routes/api/v1/teams/[teamId]/members/route.d.ts.map +1 -1
- package/dist/routes/api/v1/teams/[teamId]/members/route.js +12 -10
- package/dist/routes/api/v1/teams/switch/route.d.ts.map +1 -1
- package/dist/routes/api/v1/teams/switch/route.js +4 -1
- package/dist/routes/dashboard/settings/password/page.d.ts.map +1 -1
- package/dist/routes/dashboard/settings/password/page.js +10 -12
- package/dist/routes/devtools/config/page.js +1 -1
- package/dist/routes/devtools/page.js +1 -1
- package/dist/routes/devtools/scheduled-actions/page.js +1 -1
- package/dist/routes/devtools/tests/[[...path]]/page.js +1 -1
- package/dist/routes/superadmin/users/page.js +2 -2
- package/dist/templates/ci-workflows/github/cypress-regression.yml +1 -1
- package/dist/templates/ci-workflows/github/cypress-smoke.yml +2 -1
- package/dist/templates/next.config.mjs +11 -31
- package/dist/templates/projects/blog/package.json +1 -1
- package/dist/templates/projects/crm/package.json +1 -1
- package/dist/templates/projects/productivity/package.json +1 -1
- package/dist/templates/projects/starter/README.md +2 -0
- package/dist/templates/projects/starter/config/app.config.ts +8 -0
- package/dist/templates/projects/starter/docs/public/01-overview/01-introduction.md +2 -0
- package/dist/templates/projects/starter/templates/dashboard/analytics/layout.tsx +14 -0
- package/dist/templates/proxy.ts +18 -8
- package/dist/templates/public/uploads/README.md +5 -94
- package/migrations/029_sample_accounts_outside_development.sql +55 -0
- package/migrations/030_taxonomies_team_writes.sql +29 -0
- package/migrations/031_users_role_suspended.sql +41 -0
- package/migrations/090_sample_data.sql +10 -0
- package/package.json +3 -3
- package/scripts/build/registry/__tests__/build-output.test.mjs +1 -1
- package/scripts/build/registry/__tests__/project-mode-root-first.test.mjs +6 -6
- package/scripts/build/registry/__tests__/write-places.test.mjs +1 -1
- package/scripts/build/registry/contracts/__tests__/contracts-wire.test.mjs +1 -1
- package/scripts/build/registry/contracts/__tests__/contracts.test.mjs +1 -1
- package/scripts/build/registry/host/README.md +2 -2
- package/scripts/build/registry/host/__tests__/facade-emitter.test.mjs +14 -13
- package/scripts/build/registry/host/__tests__/host-mode.test.mjs +1 -1
- package/scripts/build/registry/host/__tests__/host-project-composition.test.mjs +1 -1
- package/scripts/build/registry/host/__tests__/host-project.test.mjs +2 -2
- package/scripts/build/registry/host/__tests__/host-proxy-areas.test.mjs +99 -1
- package/scripts/build/registry/host/__tests__/host-robustness.test.mjs +1 -1
- package/scripts/build/registry/host/facade-emitter.mjs +1 -1
- package/scripts/build/registry/host/next-route-exports.json +2 -2
- package/scripts/build/registry/host/proxy-areas.mjs +92 -2
- package/scripts/db/run-migrations.mjs +30 -10
- package/scripts/db/sample-data.mjs +44 -0
- package/scripts/db/verify-theme-migrations.mjs +7 -1
- package/templates/ci-workflows/github/cypress-regression.yml +1 -1
- package/templates/ci-workflows/github/cypress-smoke.yml +2 -1
- package/templates/next.config.mjs +11 -31
- package/templates/projects/blog/package.json +1 -1
- package/templates/projects/crm/package.json +1 -1
- package/templates/projects/productivity/package.json +1 -1
- package/templates/projects/starter/README.md +2 -0
- package/templates/projects/starter/config/app.config.ts +8 -0
- package/templates/projects/starter/docs/public/01-overview/01-introduction.md +2 -0
- package/templates/projects/starter/templates/dashboard/analytics/layout.tsx +14 -0
- package/templates/proxy.ts +18 -8
- package/templates/public/uploads/README.md +5 -94
- package/tests/jest/__mocks__/@nextsparkjs/registries/api-presets-registry.ts +2 -0
- package/tests/jest/__mocks__/@nextsparkjs/registries/mcp-registry.ts +2 -0
- package/tests/jest/__mocks__/@nextsparkjs/registries/permissions-registry.ts +5 -4
- package/dist/templates/projects/starter/public/brand/.gitkeep +0 -0
- package/dist/templates/public/uploads/.gitignore +0 -6
- package/dist/templates/public/uploads/temp/.gitkeep +0 -1
- package/templates/projects/starter/public/brand/.gitkeep +0 -0
- package/templates/public/uploads/.gitignore +0 -6
- package/templates/public/uploads/temp/.gitkeep +0 -1
package/dist/templates/proxy.ts
CHANGED
|
@@ -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,
|
|
185
|
-
*
|
|
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
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
|
1
|
+
# Media uploads
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
128
|
-
// The table forbids dynamicParams and generateStaticParams on metadata routes
|
|
129
|
-
//
|
|
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'
|
|
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.
|
|
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.
|
|
602
|
-
for (const version of ['16.3.
|
|
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.
|
|
605
|
-
'16.3.5+build.1', '16.3.
|
|
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.
|
|
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\.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|