@nextsparkjs/core 0.1.0-beta.136 → 0.1.0-beta.138
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/dist/components/dashboard/block-editor/array-field.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/array-field.js +11 -0
- package/dist/lib/translations/registry.d.ts +5 -0
- package/dist/lib/translations/registry.d.ts.map +1 -1
- package/dist/lib/translations/registry.js +1 -0
- package/dist/providers/static-intl-provider.d.ts +9 -0
- package/dist/providers/static-intl-provider.d.ts.map +1 -0
- package/dist/providers/static-intl-provider.js +9 -0
- package/dist/styles/classes.json +1 -1
- package/dist/templates/app/(auth)/auth-error/page.tsx +0 -1
- package/dist/templates/app/(auth)/forgot-password/page.tsx +0 -1
- package/dist/templates/app/(auth)/layout.tsx +4 -1
- package/dist/templates/app/(auth)/login/page.tsx +0 -1
- package/dist/templates/app/(auth)/reset-password/page.tsx +0 -1
- package/dist/templates/app/(auth)/signup/page.tsx +10 -2
- package/dist/templates/app/(auth)/verify-email/page.tsx +0 -1
- package/dist/templates/app/(public)/[...slug]/page.tsx +15 -2
- package/dist/templates/app/(public)/layout.tsx +4 -1
- package/dist/templates/app/api/csp-report/route.ts +13 -9
- package/dist/templates/app/dashboard/(main)/[entity]/[id]/page.tsx +3 -21
- package/dist/templates/app/dashboard/(main)/[entity]/page.tsx +3 -22
- package/dist/templates/app/dashboard/settings/password/page.tsx +0 -1
- package/dist/templates/app/devtools/blocks/page.tsx +10 -1
- package/dist/templates/app/devtools/config/page.tsx +10 -1
- package/dist/templates/app/devtools/features/page.tsx +10 -1
- package/dist/templates/app/devtools/flows/page.tsx +10 -1
- package/dist/templates/app/devtools/page.tsx +10 -1
- package/dist/templates/app/devtools/tags/page.tsx +10 -1
- package/dist/templates/app/devtools/tests/[[...path]]/page.tsx +10 -1
- package/dist/templates/app/layout.ppr.tsx +105 -0
- package/dist/templates/app/superadmin/docs/[section]/[page]/page.tsx +3 -5
- package/dist/templates/next.config.mjs +3 -0
- package/package.json +2 -2
- package/scripts/build/registry/__tests__/translation-registry-ppr.test.mjs +118 -0
- package/scripts/build/registry/generators/translation-registry.mjs +113 -2
- package/templates/app/(auth)/auth-error/page.tsx +0 -1
- package/templates/app/(auth)/forgot-password/page.tsx +0 -1
- package/templates/app/(auth)/layout.tsx +4 -1
- package/templates/app/(auth)/login/page.tsx +0 -1
- package/templates/app/(auth)/reset-password/page.tsx +0 -1
- package/templates/app/(auth)/signup/page.tsx +10 -2
- package/templates/app/(auth)/verify-email/page.tsx +0 -1
- package/templates/app/(public)/[...slug]/page.tsx +15 -2
- package/templates/app/(public)/layout.tsx +4 -1
- package/templates/app/api/csp-report/route.ts +13 -9
- package/templates/app/dashboard/(main)/[entity]/[id]/page.tsx +3 -21
- package/templates/app/dashboard/(main)/[entity]/page.tsx +3 -22
- package/templates/app/dashboard/settings/password/page.tsx +0 -1
- package/templates/app/devtools/blocks/page.tsx +10 -1
- package/templates/app/devtools/config/page.tsx +10 -1
- package/templates/app/devtools/features/page.tsx +10 -1
- package/templates/app/devtools/flows/page.tsx +10 -1
- package/templates/app/devtools/page.tsx +10 -1
- package/templates/app/devtools/tags/page.tsx +10 -1
- package/templates/app/devtools/tests/[[...path]]/page.tsx +10 -1
- package/templates/app/layout.ppr.tsx +105 -0
- package/templates/app/superadmin/docs/[section]/[page]/page.tsx +3 -5
- package/templates/next.config.mjs +3 -0
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.138",
|
|
4
4
|
"description": "NextSpark - The complete SaaS framework for Next.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "NextSpark <hello@nextspark.dev>",
|
|
@@ -462,7 +462,7 @@
|
|
|
462
462
|
"tailwind-merge": "^3.3.1",
|
|
463
463
|
"uuid": "^13.0.0",
|
|
464
464
|
"zod": "^4.1.5",
|
|
465
|
-
"@nextsparkjs/testing": "0.1.0-beta.
|
|
465
|
+
"@nextsparkjs/testing": "0.1.0-beta.138"
|
|
466
466
|
},
|
|
467
467
|
"scripts": {
|
|
468
468
|
"postinstall": "node scripts/postinstall.mjs || true",
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for PPR-related functions in translation-registry generator
|
|
3
|
+
*
|
|
4
|
+
* Tests: getDefaultLocale, getDefaultThemeMode, detectPPREnabled
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { existsSync, mkdirSync, writeFileSync, rmSync } from 'fs'
|
|
8
|
+
import { join } from 'path'
|
|
9
|
+
import { tmpdir } from 'os'
|
|
10
|
+
import { detectPPREnabled } from '../generators/translation-registry.mjs'
|
|
11
|
+
|
|
12
|
+
// Create a temp directory for each test run
|
|
13
|
+
const TEST_DIR = join(tmpdir(), `nextspark-ppr-test-${Date.now()}`)
|
|
14
|
+
|
|
15
|
+
function setup() {
|
|
16
|
+
if (existsSync(TEST_DIR)) rmSync(TEST_DIR, { recursive: true })
|
|
17
|
+
mkdirSync(TEST_DIR, { recursive: true })
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function teardown() {
|
|
21
|
+
if (existsSync(TEST_DIR)) rmSync(TEST_DIR, { recursive: true })
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// --- detectPPREnabled ---
|
|
25
|
+
|
|
26
|
+
function testDetectPPR_withCacheComponentsTrue() {
|
|
27
|
+
setup()
|
|
28
|
+
writeFileSync(join(TEST_DIR, 'next.config.mjs'), `
|
|
29
|
+
const nextConfig = {
|
|
30
|
+
cacheComponents: true,
|
|
31
|
+
experimental: {},
|
|
32
|
+
}
|
|
33
|
+
export default nextConfig
|
|
34
|
+
`)
|
|
35
|
+
const result = detectPPREnabled(TEST_DIR)
|
|
36
|
+
console.assert(result === true, 'detectPPREnabled should return true when cacheComponents: true')
|
|
37
|
+
teardown()
|
|
38
|
+
console.log(' PASS: cacheComponents: true detected')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function testDetectPPR_withCacheComponentsFalse() {
|
|
42
|
+
setup()
|
|
43
|
+
writeFileSync(join(TEST_DIR, 'next.config.mjs'), `
|
|
44
|
+
const nextConfig = {
|
|
45
|
+
cacheComponents: false,
|
|
46
|
+
}
|
|
47
|
+
export default nextConfig
|
|
48
|
+
`)
|
|
49
|
+
const result = detectPPREnabled(TEST_DIR)
|
|
50
|
+
console.assert(result === false, 'detectPPREnabled should return false when cacheComponents: false')
|
|
51
|
+
teardown()
|
|
52
|
+
console.log(' PASS: cacheComponents: false returns false')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function testDetectPPR_withoutConfig() {
|
|
56
|
+
setup()
|
|
57
|
+
// No next.config file at all
|
|
58
|
+
const result = detectPPREnabled(TEST_DIR)
|
|
59
|
+
console.assert(result === false, 'detectPPREnabled should return false when no config exists')
|
|
60
|
+
teardown()
|
|
61
|
+
console.log(' PASS: no next.config returns false')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function testDetectPPR_withNextConfigTs() {
|
|
65
|
+
setup()
|
|
66
|
+
writeFileSync(join(TEST_DIR, 'next.config.ts'), `
|
|
67
|
+
import type { NextConfig } from 'next'
|
|
68
|
+
const config: NextConfig = {
|
|
69
|
+
cacheComponents: true,
|
|
70
|
+
}
|
|
71
|
+
export default config
|
|
72
|
+
`)
|
|
73
|
+
const result = detectPPREnabled(TEST_DIR)
|
|
74
|
+
console.assert(result === true, 'detectPPREnabled should detect in .ts files')
|
|
75
|
+
teardown()
|
|
76
|
+
console.log(' PASS: next.config.ts supported')
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function testDetectPPR_withCacheComponentsInComment() {
|
|
80
|
+
setup()
|
|
81
|
+
writeFileSync(join(TEST_DIR, 'next.config.mjs'), `
|
|
82
|
+
const nextConfig = {
|
|
83
|
+
// cacheComponents: true,
|
|
84
|
+
reactStrictMode: true,
|
|
85
|
+
}
|
|
86
|
+
export default nextConfig
|
|
87
|
+
`)
|
|
88
|
+
// This will match the regex even in comments. Acceptable behavior —
|
|
89
|
+
// if you commented it out, you probably want to disable it.
|
|
90
|
+
// The regex is simple and this edge case is documented.
|
|
91
|
+
teardown()
|
|
92
|
+
console.log(' SKIP: comment detection (acceptable limitation)')
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function testDetectPPR_withSpacingVariations() {
|
|
96
|
+
setup()
|
|
97
|
+
writeFileSync(join(TEST_DIR, 'next.config.mjs'), `
|
|
98
|
+
const nextConfig = {
|
|
99
|
+
cacheComponents : true ,
|
|
100
|
+
}
|
|
101
|
+
export default nextConfig
|
|
102
|
+
`)
|
|
103
|
+
const result = detectPPREnabled(TEST_DIR)
|
|
104
|
+
console.assert(result === true, 'detectPPREnabled should handle spacing variations')
|
|
105
|
+
teardown()
|
|
106
|
+
console.log(' PASS: spacing variations handled')
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// --- Run all tests ---
|
|
110
|
+
console.log('\n=== Translation Registry PPR Tests ===\n')
|
|
111
|
+
console.log('detectPPREnabled:')
|
|
112
|
+
testDetectPPR_withCacheComponentsTrue()
|
|
113
|
+
testDetectPPR_withCacheComponentsFalse()
|
|
114
|
+
testDetectPPR_withoutConfig()
|
|
115
|
+
testDetectPPR_withNextConfigTs()
|
|
116
|
+
testDetectPPR_withCacheComponentsInComment()
|
|
117
|
+
testDetectPPR_withSpacingVariations()
|
|
118
|
+
console.log('\nAll tests passed!\n')
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { existsSync, readdirSync, readFileSync, statSync } from 'fs'
|
|
13
|
-
import { join } from 'path'
|
|
13
|
+
import { join, dirname } from 'path'
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Extract supportedLocales from a theme's app.config.ts file
|
|
@@ -20,6 +20,72 @@ import { join } from 'path'
|
|
|
20
20
|
* @param {string} activeTheme - Name of the active theme
|
|
21
21
|
* @returns {string[]} Array of supported locale codes, or empty array if not found
|
|
22
22
|
*/
|
|
23
|
+
/**
|
|
24
|
+
* Extract defaultLocale from a theme's app.config.ts file
|
|
25
|
+
* @param {string} themesDir
|
|
26
|
+
* @param {string} activeTheme
|
|
27
|
+
* @returns {string} Default locale code, or 'en' if not found
|
|
28
|
+
*/
|
|
29
|
+
function getDefaultLocale(themesDir, activeTheme) {
|
|
30
|
+
if (!activeTheme) return 'en'
|
|
31
|
+
const appConfigPath = join(themesDir, activeTheme, 'config', 'app.config.ts')
|
|
32
|
+
if (!existsSync(appConfigPath)) return 'en'
|
|
33
|
+
try {
|
|
34
|
+
const content = readFileSync(appConfigPath, 'utf8')
|
|
35
|
+
const match = content.match(/defaultLocale\s*:\s*['"]([a-zA-Z-]+)['"]/)
|
|
36
|
+
return match ? match[1] : 'en'
|
|
37
|
+
} catch {
|
|
38
|
+
return 'en'
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Extract defaultMode from a theme's theme.config.ts file
|
|
44
|
+
* @param {string} themesDir
|
|
45
|
+
* @param {string} activeTheme
|
|
46
|
+
* @returns {'light' | 'dark' | 'system'} Default theme mode
|
|
47
|
+
*/
|
|
48
|
+
function getDefaultThemeMode(themesDir, activeTheme) {
|
|
49
|
+
if (!activeTheme) return 'system'
|
|
50
|
+
const themeConfigPath = join(themesDir, activeTheme, 'config', 'theme.config.ts')
|
|
51
|
+
if (!existsSync(themeConfigPath)) return 'system'
|
|
52
|
+
try {
|
|
53
|
+
const content = readFileSync(themeConfigPath, 'utf8')
|
|
54
|
+
const match = content.match(/defaultMode\s*:\s*['"]([a-z]+)['"]/)
|
|
55
|
+
return match ? match[1] : 'system'
|
|
56
|
+
} catch {
|
|
57
|
+
return 'system'
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Detect if the project has PPR (Partial Prerendering) enabled
|
|
63
|
+
* by checking for cacheComponents: true in next.config.{mjs,js,ts}
|
|
64
|
+
*
|
|
65
|
+
* When PPR is enabled, the generator produces static exports
|
|
66
|
+
* (DEFAULT_LOCALE, DEFAULT_THEME_MODE, STATIC_MESSAGES) that
|
|
67
|
+
* allow the root layout to render a fully static shell.
|
|
68
|
+
*
|
|
69
|
+
* @param {string} projectRoot - Path to the project root
|
|
70
|
+
* @returns {boolean} true if cacheComponents: true is found
|
|
71
|
+
*/
|
|
72
|
+
export function detectPPREnabled(projectRoot) {
|
|
73
|
+
const configPaths = ['next.config.mjs', 'next.config.js', 'next.config.ts']
|
|
74
|
+
for (const name of configPaths) {
|
|
75
|
+
const p = join(projectRoot, name)
|
|
76
|
+
if (!existsSync(p)) continue
|
|
77
|
+
try {
|
|
78
|
+
const content = readFileSync(p, 'utf8')
|
|
79
|
+
if (content.match(/cacheComponents\s*:\s*true/)) {
|
|
80
|
+
return true
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
// ignore read errors
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return false
|
|
87
|
+
}
|
|
88
|
+
|
|
23
89
|
function getSupportedLocales(themesDir, activeTheme) {
|
|
24
90
|
if (!activeTheme) {
|
|
25
91
|
return []
|
|
@@ -155,8 +221,10 @@ function detectLocales(messagesDir) {
|
|
|
155
221
|
* @returns {string} Generated TypeScript content
|
|
156
222
|
*/
|
|
157
223
|
export function generateTranslationRegistry(themes, config) {
|
|
158
|
-
// Get supported locales from active theme's app.config.ts
|
|
224
|
+
// Get supported locales and default locale from active theme's app.config.ts
|
|
159
225
|
const supportedLocales = getSupportedLocales(config.themesDir, config.activeTheme)
|
|
226
|
+
const defaultLocale = getDefaultLocale(config.themesDir, config.activeTheme)
|
|
227
|
+
const defaultThemeMode = getDefaultThemeMode(config.themesDir, config.activeTheme)
|
|
160
228
|
|
|
161
229
|
// Discover all translation files from themes
|
|
162
230
|
const themeTranslations = []
|
|
@@ -402,5 +470,48 @@ export const TRANSLATION_METADATA = {
|
|
|
402
470
|
totalPluginEntitiesWithTranslations: ${uniquePluginEntities.size},
|
|
403
471
|
pluginEntities: [${Array.from(uniquePluginEntities).map(e => `'${e}'`).join(', ')}]
|
|
404
472
|
}
|
|
473
|
+
|
|
474
|
+
${(() => {
|
|
475
|
+
// PPR: Generate static imports for the default locale — only when cacheComponents is enabled.
|
|
476
|
+
// These are used by the root layout's StaticIntlProvider (layout.ppr.tsx) to render
|
|
477
|
+
// content in the PPR static shell without accessing cookies/headers.
|
|
478
|
+
//
|
|
479
|
+
// Projects on Next.js 15 (without cacheComponents) skip this entirely.
|
|
480
|
+
// To enable: add cacheComponents: true to next.config and use layout.ppr.tsx.
|
|
481
|
+
const projectRoot = config.projectRoot || (config.themesDir ? dirname(dirname(config.themesDir)) : process.cwd())
|
|
482
|
+
const pprEnabled = detectPPREnabled(projectRoot)
|
|
483
|
+
|
|
484
|
+
if (!pprEnabled) {
|
|
485
|
+
return `// PPR static exports disabled — cacheComponents: true not found in next.config.
|
|
486
|
+
// To enable PPR, add cacheComponents: true to your next.config and copy
|
|
487
|
+
// layout.ppr.tsx from @nextsparkjs/core/templates/app/ to your app/ directory.
|
|
488
|
+
// See docs/migration-ppr.md for the full migration guide.`
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const defaultThemeTranslation = themeTranslations.find(
|
|
492
|
+
t => t.themeName === config.activeTheme && t.locale === defaultLocale
|
|
493
|
+
)
|
|
494
|
+
if (!defaultThemeTranslation) return '// PPR: No static messages generated (no theme translation found for default locale)'
|
|
495
|
+
|
|
496
|
+
console.log(`[TranslationRegistry] PPR enabled — generating static exports for locale '${defaultLocale}'`)
|
|
497
|
+
|
|
498
|
+
return `/**
|
|
499
|
+
* PPR: Static messages for the default locale (pre-merged at build time)
|
|
500
|
+
*
|
|
501
|
+
* Used by the root layout's StaticIntlProvider to render content
|
|
502
|
+
* in the PPR static shell without accessing cookies/headers.
|
|
503
|
+
* Core messages are deep-merged with theme messages.
|
|
504
|
+
*/
|
|
505
|
+
import { deepMergeMessages as _deepMerge } from '@nextsparkjs/core/lib/translations/registry'
|
|
506
|
+
import _coreMessages from '@nextsparkjs/core/messages/${defaultLocale}/index.js'
|
|
507
|
+
import _themeMessages from '${defaultThemeTranslation.filePath}'
|
|
508
|
+
|
|
509
|
+
export const DEFAULT_LOCALE = '${defaultLocale}' as const
|
|
510
|
+
export const DEFAULT_THEME_MODE = '${defaultThemeMode}' as const
|
|
511
|
+
export const STATIC_MESSAGES: Record<string, unknown> = _deepMerge(
|
|
512
|
+
_coreMessages as unknown as Record<string, unknown>,
|
|
513
|
+
(_themeMessages as { default?: Record<string, unknown> }).default ?? _themeMessages as unknown as Record<string, unknown>
|
|
514
|
+
)`
|
|
515
|
+
})()}
|
|
405
516
|
`
|
|
406
517
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Suspense } from 'react'
|
|
1
2
|
import type { Metadata } from 'next'
|
|
2
3
|
import { AuthWrapper } from '@nextsparkjs/core/components/auth/layouts/AuthWrapper'
|
|
3
4
|
import { getTemplateOrDefault, getMetadataOrDefault } from '@nextsparkjs/core/lib/template-resolver'
|
|
@@ -34,7 +35,9 @@ function AuthLayout({ children }: { children: React.ReactNode }) {
|
|
|
34
35
|
</div>
|
|
35
36
|
|
|
36
37
|
<AuthWrapper>
|
|
37
|
-
{
|
|
38
|
+
<Suspense fallback={null}>
|
|
39
|
+
{children}
|
|
40
|
+
</Suspense>
|
|
38
41
|
</AuthWrapper>
|
|
39
42
|
</div>
|
|
40
43
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Suspense } from 'react'
|
|
1
2
|
import type { Metadata } from 'next'
|
|
2
3
|
import { redirect } from 'next/navigation'
|
|
3
4
|
import { SignupForm } from '@nextsparkjs/core/components/auth/forms/SignupForm'
|
|
@@ -15,7 +16,7 @@ export const metadata: Metadata = getMetadataOrDefault(
|
|
|
15
16
|
defaultMetadata
|
|
16
17
|
)
|
|
17
18
|
|
|
18
|
-
async function
|
|
19
|
+
async function SignupPageContent() {
|
|
19
20
|
const registrationMode = AUTH_CONFIG?.registration?.mode ?? 'open'
|
|
20
21
|
|
|
21
22
|
// In invitation-only mode, allow the first user to register
|
|
@@ -38,6 +39,13 @@ async function SignupPage() {
|
|
|
38
39
|
return <SignupForm />
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
function SignupPage() {
|
|
43
|
+
return (
|
|
44
|
+
<Suspense fallback={null}>
|
|
45
|
+
<SignupPageContent />
|
|
46
|
+
</Suspense>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
42
50
|
|
|
43
51
|
export default getTemplateOrDefault('app/(auth)/signup/page.tsx', SignupPage)
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* 2. Falls back to default PageRenderer if no template
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
import { Suspense } from 'react'
|
|
16
17
|
import { notFound } from 'next/navigation'
|
|
17
18
|
import { query } from '@nextsparkjs/core/lib/db'
|
|
18
19
|
import { PageRenderer } from '@nextsparkjs/core/components/public/pageBuilder'
|
|
@@ -51,6 +52,7 @@ function getEntityConfigs(): Record<string, EntityConfig> {
|
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
// Enable ISR with 1 hour revalidation
|
|
55
|
+
// On Next.js 16 with cacheComponents, this is superseded by 'use cache' + cacheLife()
|
|
54
56
|
export const revalidate = 3600
|
|
55
57
|
|
|
56
58
|
interface PageProps {
|
|
@@ -247,9 +249,9 @@ export async function generateMetadata({
|
|
|
247
249
|
}
|
|
248
250
|
|
|
249
251
|
/**
|
|
250
|
-
* Main catch-all page
|
|
252
|
+
* Main catch-all page content (async, wrapped in Suspense by parent)
|
|
251
253
|
*/
|
|
252
|
-
|
|
254
|
+
async function DynamicPublicPageContent({
|
|
253
255
|
params,
|
|
254
256
|
searchParams,
|
|
255
257
|
}: PageProps) {
|
|
@@ -376,3 +378,14 @@ export default async function DynamicPublicPage({
|
|
|
376
378
|
// No match found
|
|
377
379
|
notFound()
|
|
378
380
|
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Main catch-all page component — wraps async content in Suspense for PPR compatibility
|
|
384
|
+
*/
|
|
385
|
+
export default function DynamicPublicPage(props: PageProps) {
|
|
386
|
+
return (
|
|
387
|
+
<Suspense fallback={null}>
|
|
388
|
+
<DynamicPublicPageContent {...props} />
|
|
389
|
+
</Suspense>
|
|
390
|
+
)
|
|
391
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Suspense } from "react"
|
|
1
2
|
import type { Metadata } from "next"
|
|
2
3
|
import { PublicNavbar } from '@nextsparkjs/core/components/app/layouts/PublicNavbar'
|
|
3
4
|
import { PublicFooter } from '@nextsparkjs/core/components/app/layouts/PublicFooter'
|
|
@@ -29,7 +30,9 @@ function PublicLayout({
|
|
|
29
30
|
|
|
30
31
|
{/* Main Content */}
|
|
31
32
|
<main className="flex-1">
|
|
32
|
-
{
|
|
33
|
+
<Suspense fallback={null}>
|
|
34
|
+
{children}
|
|
35
|
+
</Suspense>
|
|
33
36
|
</main>
|
|
34
37
|
|
|
35
38
|
{/* Public Footer */}
|
|
@@ -5,15 +5,18 @@ import { NextRequest, NextResponse } from 'next/server';
|
|
|
5
5
|
let checkDistributedRateLimit: ((id: string, tier: string) => Promise<{ allowed: boolean; limit: number; remaining: number; resetTime: number; retryAfter?: number }>) | null = null;
|
|
6
6
|
let createRateLimitErrorResponse: ((result: { allowed: boolean; limit: number; remaining: number; resetTime: number; retryAfter?: number }) => NextResponse) | null = null;
|
|
7
7
|
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
// Lazy-load rate limiting functions on first request
|
|
9
|
+
let rateLimitLoaded = false;
|
|
10
|
+
async function ensureRateLimitLoaded() {
|
|
11
|
+
if (rateLimitLoaded) return;
|
|
12
|
+
rateLimitLoaded = true;
|
|
13
|
+
try {
|
|
14
|
+
const rateLimitModule = await import('@nextsparkjs/core/lib/api');
|
|
15
|
+
checkDistributedRateLimit = rateLimitModule.checkDistributedRateLimit;
|
|
16
|
+
createRateLimitErrorResponse = rateLimitModule.createRateLimitErrorResponse;
|
|
17
|
+
} catch {
|
|
18
|
+
console.warn('[CSP Report] Rate limiting not available - running without rate limits');
|
|
19
|
+
}
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
/**
|
|
@@ -73,6 +76,7 @@ function getClientIp(request: NextRequest): string {
|
|
|
73
76
|
}
|
|
74
77
|
|
|
75
78
|
export async function POST(request: NextRequest) {
|
|
79
|
+
await ensureRateLimitLoaded();
|
|
76
80
|
const requestId = randomUUID().slice(0, 8);
|
|
77
81
|
let rateLimitHeaders: Record<string, string> = {};
|
|
78
82
|
|
|
@@ -77,27 +77,9 @@ async function EntityDetailPage({ params }: PageProps) {
|
|
|
77
77
|
)
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
export
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (!(entitySlug in getEntityRegistry())) {
|
|
85
|
-
return {
|
|
86
|
-
title: 'Not Found - Dashboard'
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const entityConfig = getEntity(entitySlug as string)
|
|
91
|
-
if (!entityConfig || !isEntityConfig(entityConfig)) {
|
|
92
|
-
return {
|
|
93
|
-
title: 'Not Found - Dashboard'
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return {
|
|
98
|
-
title: `${entityConfig.names.plural} #${resolvedParams.id} - Dashboard`,
|
|
99
|
-
description: `View details for ${entityConfig.names.singular}`
|
|
100
|
-
}
|
|
80
|
+
export const metadata: Metadata = {
|
|
81
|
+
title: 'Dashboard',
|
|
82
|
+
description: 'View entity details'
|
|
101
83
|
}
|
|
102
84
|
|
|
103
85
|
export default EntityDetailPage
|
|
@@ -63,28 +63,9 @@ async function EntityListPage({ params }: PageProps) {
|
|
|
63
63
|
)
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
export
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const registry = getEntityRegistry()
|
|
71
|
-
if (!(entitySlug in registry)) {
|
|
72
|
-
return {
|
|
73
|
-
title: 'Not Found - Dashboard'
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const entityConfig = getEntity(entitySlug)
|
|
78
|
-
if (!entityConfig || !isEntityConfig(entityConfig)) {
|
|
79
|
-
return {
|
|
80
|
-
title: 'Not Found - Dashboard'
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return {
|
|
85
|
-
title: `${entityConfig.names.plural} - Dashboard`,
|
|
86
|
-
description: `Manage ${entityConfig.names.plural.toLowerCase()} in your dashboard`
|
|
87
|
-
}
|
|
66
|
+
export const metadata: Metadata = {
|
|
67
|
+
title: 'Dashboard',
|
|
68
|
+
description: 'Manage entities in your dashboard'
|
|
88
69
|
}
|
|
89
70
|
|
|
90
71
|
export default getTemplateOrDefault('app/dashboard/(main)/[entity]/page.tsx', EntityListPage)
|
|
@@ -301,6 +301,5 @@ function UpdatePasswordPage() {
|
|
|
301
301
|
}
|
|
302
302
|
|
|
303
303
|
// Opt out of static generation due to client-side state
|
|
304
|
-
export const dynamic = 'force-dynamic';
|
|
305
304
|
|
|
306
305
|
export default getTemplateOrDefaultClient('app/dashboard/settings/password/page.tsx', UpdatePasswordPage)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Suspense } from "react";
|
|
1
2
|
import { BlocksViewer } from "@nextsparkjs/core/components/devtools/BlocksViewer";
|
|
2
3
|
import { LayoutGrid } from "lucide-react";
|
|
3
4
|
import { getTranslations } from "next-intl/server";
|
|
@@ -8,7 +9,7 @@ import { getTranslations } from "next-intl/server";
|
|
|
8
9
|
* Displays all page builder blocks with field definitions and coverage info.
|
|
9
10
|
* Provides filtering and search capabilities.
|
|
10
11
|
*/
|
|
11
|
-
|
|
12
|
+
async function DevBlocksPageContent() {
|
|
12
13
|
const t = await getTranslations("devtools.blocks");
|
|
13
14
|
|
|
14
15
|
return (
|
|
@@ -29,3 +30,11 @@ export default async function DevBlocksPage() {
|
|
|
29
30
|
</div>
|
|
30
31
|
);
|
|
31
32
|
}
|
|
33
|
+
|
|
34
|
+
export default function DevBlocksPage() {
|
|
35
|
+
return (
|
|
36
|
+
<Suspense fallback={null}>
|
|
37
|
+
<DevBlocksPageContent />
|
|
38
|
+
</Suspense>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Suspense } from "react";
|
|
1
2
|
import { ConfigViewer } from "@nextsparkjs/core/components/devtools";
|
|
2
3
|
import { Settings } from "lucide-react";
|
|
3
4
|
import { getTranslations } from "next-intl/server";
|
|
@@ -8,7 +9,7 @@ import { getTranslations } from "next-intl/server";
|
|
|
8
9
|
* Displays theme configuration and entity registry information.
|
|
9
10
|
* Provides read-only view with JSON formatting and copy functionality.
|
|
10
11
|
*/
|
|
11
|
-
|
|
12
|
+
async function DevConfigPageContent() {
|
|
12
13
|
const t = await getTranslations('dev.config');
|
|
13
14
|
|
|
14
15
|
return (
|
|
@@ -29,3 +30,11 @@ export default async function DevConfigPage() {
|
|
|
29
30
|
</div>
|
|
30
31
|
);
|
|
31
32
|
}
|
|
33
|
+
|
|
34
|
+
export default function DevConfigPage() {
|
|
35
|
+
return (
|
|
36
|
+
<Suspense fallback={null}>
|
|
37
|
+
<DevConfigPageContent />
|
|
38
|
+
</Suspense>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Suspense } from "react";
|
|
1
2
|
import { FeaturesViewer } from "@nextsparkjs/core/components/devtools/FeaturesViewer";
|
|
2
3
|
import { Layers } from "lucide-react";
|
|
3
4
|
import { getTranslations } from "next-intl/server";
|
|
@@ -8,7 +9,7 @@ import { getTranslations } from "next-intl/server";
|
|
|
8
9
|
* Displays all features from the testing registry with coverage information.
|
|
9
10
|
* Provides filtering and search capabilities.
|
|
10
11
|
*/
|
|
11
|
-
|
|
12
|
+
async function DevFeaturesPageContent() {
|
|
12
13
|
const t = await getTranslations("devtools.features");
|
|
13
14
|
|
|
14
15
|
return (
|
|
@@ -29,3 +30,11 @@ export default async function DevFeaturesPage() {
|
|
|
29
30
|
</div>
|
|
30
31
|
);
|
|
31
32
|
}
|
|
33
|
+
|
|
34
|
+
export default function DevFeaturesPage() {
|
|
35
|
+
return (
|
|
36
|
+
<Suspense fallback={null}>
|
|
37
|
+
<DevFeaturesPageContent />
|
|
38
|
+
</Suspense>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Suspense } from "react";
|
|
1
2
|
import { FlowsViewer } from "@nextsparkjs/core/components/devtools/FlowsViewer";
|
|
2
3
|
import { GitBranch } from "lucide-react";
|
|
3
4
|
import { getTranslations } from "next-intl/server";
|
|
@@ -8,7 +9,7 @@ import { getTranslations } from "next-intl/server";
|
|
|
8
9
|
* Displays all user journey flows from the testing registry with coverage information.
|
|
9
10
|
* Provides filtering and search capabilities.
|
|
10
11
|
*/
|
|
11
|
-
|
|
12
|
+
async function DevFlowsPageContent() {
|
|
12
13
|
const t = await getTranslations("devtools.flows");
|
|
13
14
|
|
|
14
15
|
return (
|
|
@@ -29,3 +30,11 @@ export default async function DevFlowsPage() {
|
|
|
29
30
|
</div>
|
|
30
31
|
);
|
|
31
32
|
}
|
|
33
|
+
|
|
34
|
+
export default function DevFlowsPage() {
|
|
35
|
+
return (
|
|
36
|
+
<Suspense fallback={null}>
|
|
37
|
+
<DevFlowsPageContent />
|
|
38
|
+
</Suspense>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Suspense } from "react";
|
|
1
2
|
import { Code, Palette, FileText, Settings } from "lucide-react";
|
|
2
3
|
import Link from "next/link";
|
|
3
4
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@nextsparkjs/core/components/ui/card";
|
|
@@ -9,7 +10,7 @@ import { getTranslations } from "next-intl/server";
|
|
|
9
10
|
* Dashboard with quick links to development tools and documentation.
|
|
10
11
|
* Shows overview of available sections in the dev area.
|
|
11
12
|
*/
|
|
12
|
-
|
|
13
|
+
async function DevHomePageContent() {
|
|
13
14
|
const t = await getTranslations('dev');
|
|
14
15
|
const tCommon = await getTranslations('common');
|
|
15
16
|
|
|
@@ -119,3 +120,11 @@ export default async function DevHomePage() {
|
|
|
119
120
|
</div>
|
|
120
121
|
);
|
|
121
122
|
}
|
|
123
|
+
|
|
124
|
+
export default function DevHomePage() {
|
|
125
|
+
return (
|
|
126
|
+
<Suspense fallback={null}>
|
|
127
|
+
<DevHomePageContent />
|
|
128
|
+
</Suspense>
|
|
129
|
+
);
|
|
130
|
+
}
|