@nextsparkjs/core 0.1.0-beta.135 → 0.1.0-beta.137
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/public/pageBuilder/PageRenderer.d.ts +6 -12
- package/dist/components/public/pageBuilder/PageRenderer.d.ts.map +1 -1
- package/dist/components/public/pageBuilder/PageRenderer.js +4 -5
- package/dist/lib/blocks/loader.d.ts +3 -15
- package/dist/lib/blocks/loader.d.ts.map +1 -1
- package/dist/lib/blocks/loader.js +2 -9
- 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 +14 -3
- 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/[slug]/page.tsx +3 -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/layout.tsx +10 -2
- 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/block-registry.mjs +13 -28
- 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 +14 -3
- 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/[slug]/page.tsx +3 -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/layout.tsx +10 -2
- package/templates/app/superadmin/docs/[section]/[page]/page.tsx +3 -5
- package/templates/next.config.mjs +3 -0
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* PageRenderer Component
|
|
2
|
+
* PageRenderer Component
|
|
3
3
|
*
|
|
4
|
-
* Renders pages from the Page Builder
|
|
5
|
-
*
|
|
4
|
+
* Renders pages from the Page Builder by iterating over blocks
|
|
5
|
+
* and directly loading block components from the SSR registry.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* 3. Suspense boundaries let React stream HTML progressively
|
|
11
|
-
* 4. Client receives complete HTML (zero CLS, SEO-safe)
|
|
12
|
-
* 5. Only JS chunks for blocks actually on the page are sent to client
|
|
13
|
-
*
|
|
14
|
-
* This gives per-block code splitting WITHOUT the CLS issues of next/dynamic,
|
|
15
|
-
* because the server fully resolves each block before flushing the HTML.
|
|
7
|
+
* Uses direct imports (BLOCK_COMPONENTS_SSR) for synchronous hydration.
|
|
8
|
+
* This ensures zero CLS — HTML is fully visible and stable without
|
|
9
|
+
* waiting for async chunk resolution on the client.
|
|
16
10
|
*
|
|
17
11
|
* @module core/components/public/pageBuilder
|
|
18
12
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PageRenderer.d.ts","sourceRoot":"","sources":["../../../../src/components/public/pageBuilder/PageRenderer.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"PageRenderer.d.ts","sourceRoot":"","sources":["../../../../src/components/public/pageBuilder/PageRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAiC1D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,CAAA;QACV,KAAK,EAAE,MAAM,CAAA;QACb,IAAI,EAAE,MAAM,CAAA;QACZ,MAAM,EAAE,aAAa,EAAE,CAAA;QACvB,MAAM,EAAE,MAAM,CAAA;KACf,CAAA;CACF;AAED,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,EAAE,iBAAiB,2CA+BvD"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import { loadBlockSSR, normalizeBlockProps } from "../../../lib/blocks/loader.js";
|
|
2
|
+
import { getBlockComponentSSR, normalizeBlockProps } from "../../../lib/blocks/loader.js";
|
|
4
3
|
function BlockError({ blockSlug }) {
|
|
5
4
|
return /* @__PURE__ */ jsx("div", { className: "w-full py-12 px-4 bg-destructive/10 border border-destructive/20 rounded", children: /* @__PURE__ */ jsxs("div", { className: "max-w-7xl mx-auto text-center", children: [
|
|
6
5
|
/* @__PURE__ */ jsxs("p", { className: "text-destructive", children: [
|
|
@@ -10,8 +9,8 @@ function BlockError({ blockSlug }) {
|
|
|
10
9
|
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-2", children: "This block may not be available or there was an error rendering it." })
|
|
11
10
|
] }) });
|
|
12
11
|
}
|
|
13
|
-
|
|
14
|
-
const BlockComponent =
|
|
12
|
+
function BlockRenderer({ block }) {
|
|
13
|
+
const BlockComponent = getBlockComponentSSR(block.blockSlug);
|
|
15
14
|
if (!BlockComponent) {
|
|
16
15
|
console.warn(`Block component not found for slug: ${block.blockSlug}`);
|
|
17
16
|
return /* @__PURE__ */ jsx(BlockError, { blockSlug: block.blockSlug });
|
|
@@ -33,7 +32,7 @@ function PageRenderer({ page }) {
|
|
|
33
32
|
className: "@container w-full",
|
|
34
33
|
"data-block-id": block.id,
|
|
35
34
|
"data-block-slug": block.blockSlug,
|
|
36
|
-
children: /* @__PURE__ */ jsx(
|
|
35
|
+
children: /* @__PURE__ */ jsx(BlockRenderer, { block })
|
|
37
36
|
},
|
|
38
37
|
block.id
|
|
39
38
|
)) });
|
|
@@ -23,26 +23,14 @@ export declare function getBlockComponents(): Record<string, BlockComponent>;
|
|
|
23
23
|
*/
|
|
24
24
|
export declare function getBlockComponent(slug: string): BlockComponent | undefined;
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
26
|
+
* Get all SSR block components (direct imports, no React.lazy)
|
|
27
|
+
* Use for public page rendering where no-JS SSR is required
|
|
27
28
|
*/
|
|
28
29
|
export declare function getBlockComponentsSSR(): Record<string, BlockComponent>;
|
|
29
30
|
/**
|
|
30
|
-
*
|
|
31
|
+
* Get a specific SSR block component by slug (direct import, no React.lazy)
|
|
31
32
|
*/
|
|
32
33
|
export declare function getBlockComponentSSR(slug: string): BlockComponent | undefined;
|
|
33
|
-
/**
|
|
34
|
-
* Async block loader for RSC streaming.
|
|
35
|
-
*
|
|
36
|
-
* Dynamically imports a block component by slug. Used by PageRenderer
|
|
37
|
-
* (async Server Component) with Suspense for:
|
|
38
|
-
* - Per-block code splitting (only used blocks ship JS to client)
|
|
39
|
-
* - Streaming SSR (server flushes HTML progressively)
|
|
40
|
-
* - Zero CLS (HTML complete before client JS runs)
|
|
41
|
-
*
|
|
42
|
-
* @param slug - Block slug (e.g., 'hero', 'accordion')
|
|
43
|
-
* @returns Resolved block component, or undefined if not found
|
|
44
|
-
*/
|
|
45
|
-
export declare function loadBlockSSR(slug: string): Promise<BlockComponent | undefined>;
|
|
46
34
|
/**
|
|
47
35
|
* Check if a block exists in the registry
|
|
48
36
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/lib/blocks/loader.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAIrC,KAAK,cAAc,GAAG,aAAa,CAAC,GAAG,CAAC,CAAA;AAExC;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAEnE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAE1E;AAED
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/lib/blocks/loader.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAIrC,KAAK,cAAc,GAAG,aAAa,CAAC,GAAG,CAAC,CAAA;AAExC;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAEnE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAE1E;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAEtE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAE7E;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgD3F"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BLOCK_COMPONENTS, BLOCK_COMPONENTS_SSR
|
|
1
|
+
import { BLOCK_COMPONENTS, BLOCK_COMPONENTS_SSR } from "@nextsparkjs/registries/block-registry";
|
|
2
2
|
function getBlockComponents() {
|
|
3
3
|
return BLOCK_COMPONENTS;
|
|
4
4
|
}
|
|
@@ -11,14 +11,8 @@ function getBlockComponentsSSR() {
|
|
|
11
11
|
function getBlockComponentSSR(slug) {
|
|
12
12
|
return BLOCK_COMPONENTS_SSR[slug];
|
|
13
13
|
}
|
|
14
|
-
async function loadBlockSSR(slug) {
|
|
15
|
-
const importFn = BLOCK_IMPORTS_SSR[slug];
|
|
16
|
-
if (!importFn) return void 0;
|
|
17
|
-
const mod = await importFn();
|
|
18
|
-
return mod.default;
|
|
19
|
-
}
|
|
20
14
|
function hasBlock(slug) {
|
|
21
|
-
return slug in BLOCK_COMPONENTS
|
|
15
|
+
return slug in BLOCK_COMPONENTS;
|
|
22
16
|
}
|
|
23
17
|
function normalizeBlockProps(props) {
|
|
24
18
|
if (!props || typeof props !== "object" || Array.isArray(props)) {
|
|
@@ -64,6 +58,5 @@ export {
|
|
|
64
58
|
getBlockComponents,
|
|
65
59
|
getBlockComponentsSSR,
|
|
66
60
|
hasBlock,
|
|
67
|
-
loadBlockSSR,
|
|
68
61
|
normalizeBlockProps
|
|
69
62
|
};
|
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
* Integrates with next-intl and entity system for seamless i18n.
|
|
6
6
|
*/
|
|
7
7
|
import type { SupportedLocale } from '../entities/types';
|
|
8
|
+
/**
|
|
9
|
+
* Deep merge with key preservation
|
|
10
|
+
* Priority: Core < Theme < Entity (later wins)
|
|
11
|
+
*/
|
|
12
|
+
export declare function deepMergeMessages(target: Record<string, unknown>, source: Record<string, unknown>): Record<string, unknown>;
|
|
8
13
|
/**
|
|
9
14
|
* Register theme messages for a locale
|
|
10
15
|
* Call this from your project's initialization to provide theme-specific translations
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/lib/translations/registry.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/lib/translations/registry.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAuBxD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAyBzB;AAwCD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAE7F;AAmDD;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,eAAe,GACtB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CA4DlC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
interface StaticIntlProviderProps {
|
|
3
|
+
locale: string;
|
|
4
|
+
messages: Record<string, unknown>;
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
}
|
|
7
|
+
export declare function StaticIntlProvider({ locale, messages, children }: StaticIntlProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=static-intl-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-intl-provider.d.ts","sourceRoot":"","sources":["../../src/providers/static-intl-provider.tsx"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,UAAU,uBAAuB;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjC,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,uBAAuB,2CAMzF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { IntlProvider } from "use-intl/react";
|
|
4
|
+
function StaticIntlProvider({ locale, messages, children }) {
|
|
5
|
+
return /* @__PURE__ */ jsx(IntlProvider, { locale, messages, children });
|
|
6
|
+
}
|
|
7
|
+
export {
|
|
8
|
+
StaticIntlProvider
|
|
9
|
+
};
|
package/dist/styles/classes.json
CHANGED
|
@@ -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,7 +52,6 @@ function getEntityConfigs(): Record<string, EntityConfig> {
|
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
// Enable ISR with 1 hour revalidation
|
|
54
|
-
export const revalidate = 3600
|
|
55
55
|
|
|
56
56
|
interface PageProps {
|
|
57
57
|
params: Promise<{ slug: string[] }>
|
|
@@ -247,9 +247,9 @@ export async function generateMetadata({
|
|
|
247
247
|
}
|
|
248
248
|
|
|
249
249
|
/**
|
|
250
|
-
* Main catch-all page
|
|
250
|
+
* Main catch-all page content (async, wrapped in Suspense by parent)
|
|
251
251
|
*/
|
|
252
|
-
|
|
252
|
+
async function DynamicPublicPageContent({
|
|
253
253
|
params,
|
|
254
254
|
searchParams,
|
|
255
255
|
}: PageProps) {
|
|
@@ -376,3 +376,14 @@ export default async function DynamicPublicPage({
|
|
|
376
376
|
// No match found
|
|
377
377
|
notFound()
|
|
378
378
|
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Main catch-all page component — wraps async content in Suspense for PPR compatibility
|
|
382
|
+
*/
|
|
383
|
+
export default function DynamicPublicPage(props: PageProps) {
|
|
384
|
+
return (
|
|
385
|
+
<Suspense fallback={null}>
|
|
386
|
+
<DynamicPublicPageContent {...props} />
|
|
387
|
+
</Suspense>
|
|
388
|
+
)
|
|
389
|
+
}
|
|
@@ -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)
|
|
@@ -33,7 +33,9 @@ export default async function BlockDetailPage({ params }: BlockDetailPageProps)
|
|
|
33
33
|
* Generate static params for all blocks
|
|
34
34
|
*/
|
|
35
35
|
export async function generateStaticParams() {
|
|
36
|
-
|
|
36
|
+
const keys = Object.keys(BLOCK_REGISTRY);
|
|
37
|
+
if (keys.length === 0) return [{ slug: '_placeholder' }];
|
|
38
|
+
return keys.map((slug) => ({
|
|
37
39
|
slug,
|
|
38
40
|
}));
|
|
39
41
|
}
|
|
@@ -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
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Suspense } from "react";
|
|
1
2
|
import { TagsOverview } from "@nextsparkjs/core/components/devtools/TagsOverview";
|
|
2
3
|
import { Tag } 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 test tags organized by category.
|
|
9
10
|
* Provides search and copy-to-clipboard functionality.
|
|
10
11
|
*/
|
|
11
|
-
|
|
12
|
+
async function DevTagsPageContent() {
|
|
12
13
|
const t = await getTranslations("devtools.tags");
|
|
13
14
|
|
|
14
15
|
return (
|
|
@@ -29,3 +30,11 @@ export default async function DevTagsPage() {
|
|
|
29
30
|
</div>
|
|
30
31
|
);
|
|
31
32
|
}
|
|
33
|
+
|
|
34
|
+
export default function DevTagsPage() {
|
|
35
|
+
return (
|
|
36
|
+
<Suspense fallback={null}>
|
|
37
|
+
<DevTagsPageContent />
|
|
38
|
+
</Suspense>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Suspense } from "react";
|
|
1
2
|
import { TestCasesViewer } from "@nextsparkjs/core/components/devtools";
|
|
2
3
|
import { FileText } from "lucide-react";
|
|
3
4
|
import { getTranslations } from "next-intl/server";
|
|
@@ -17,7 +18,7 @@ interface DevTestsPageProps {
|
|
|
17
18
|
* - /dev/tests/auth/login-logout.bdd.md → File selected
|
|
18
19
|
* - /dev/tests/page-builder/admin/block-editor.bdd.md → Nested file
|
|
19
20
|
*/
|
|
20
|
-
|
|
21
|
+
async function DevTestsPageContent({ params }: DevTestsPageProps) {
|
|
21
22
|
const { path } = await params;
|
|
22
23
|
const t = await getTranslations("dev.tests");
|
|
23
24
|
|
|
@@ -45,3 +46,11 @@ export default async function DevTestsPage({ params }: DevTestsPageProps) {
|
|
|
45
46
|
</div>
|
|
46
47
|
);
|
|
47
48
|
}
|
|
49
|
+
|
|
50
|
+
export default function DevTestsPage({ params }: DevTestsPageProps) {
|
|
51
|
+
return (
|
|
52
|
+
<Suspense fallback={null}>
|
|
53
|
+
<DevTestsPageContent params={params} />
|
|
54
|
+
</Suspense>
|
|
55
|
+
);
|
|
56
|
+
}
|