@demokit-ai/next 0.0.1
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/client.cjs +152 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +114 -0
- package/dist/client.d.ts +114 -0
- package/dist/client.js +128 -0
- package/dist/client.js.map +1 -0
- package/dist/index.cjs +31 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +91 -0
- package/dist/index.d.ts +91 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware.cjs +111 -0
- package/dist/middleware.cjs.map +1 -0
- package/dist/middleware.d.cts +47 -0
- package/dist/middleware.d.ts +47 -0
- package/dist/middleware.js +106 -0
- package/dist/middleware.js.map +1 -0
- package/dist/server.cjs +183 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +89 -0
- package/dist/server.d.ts +89 -0
- package/dist/server.js +171 -0
- package/dist/server.js.map +1 -0
- package/dist/types-Cvq5JUP1.d.cts +129 -0
- package/dist/types-Cvq5JUP1.d.ts +129 -0
- package/package.json +94 -0
package/dist/client.cjs
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react$1 = require('react');
|
|
4
|
+
var react = require('@demokit-ai/react');
|
|
5
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
var navigation = require('next/navigation');
|
|
7
|
+
|
|
8
|
+
// src/client/provider.tsx
|
|
9
|
+
function getDemoModeFromCookie(cookieName) {
|
|
10
|
+
if (typeof document === "undefined") return false;
|
|
11
|
+
const cookies = document.cookie.split(";");
|
|
12
|
+
const demoCookie = cookies.find((c) => c.trim().startsWith(`${cookieName}=`));
|
|
13
|
+
return demoCookie !== void 0;
|
|
14
|
+
}
|
|
15
|
+
function getScenarioFromCookie(cookieName) {
|
|
16
|
+
if (typeof document === "undefined") return null;
|
|
17
|
+
const cookies = document.cookie.split(";");
|
|
18
|
+
const demoCookie = cookies.find((c) => c.trim().startsWith(`${cookieName}=`));
|
|
19
|
+
if (!demoCookie) return null;
|
|
20
|
+
const value = demoCookie.split("=")[1]?.trim();
|
|
21
|
+
if (!value || value === "true") return null;
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
function DemoKitNextProvider({
|
|
25
|
+
children,
|
|
26
|
+
fixtures,
|
|
27
|
+
scenarios = {},
|
|
28
|
+
storageKey = "demokit-mode",
|
|
29
|
+
cookieName = "demokit-mode",
|
|
30
|
+
initialEnabled,
|
|
31
|
+
baseUrl
|
|
32
|
+
}) {
|
|
33
|
+
const [isHydrated, setIsHydrated] = react$1.useState(false);
|
|
34
|
+
const [scenario, setScenario] = react$1.useState(null);
|
|
35
|
+
react$1.useEffect(() => {
|
|
36
|
+
getDemoModeFromCookie(cookieName);
|
|
37
|
+
const cookieScenario = getScenarioFromCookie(cookieName);
|
|
38
|
+
setScenario(cookieScenario);
|
|
39
|
+
setIsHydrated(true);
|
|
40
|
+
}, [cookieName]);
|
|
41
|
+
const activeFixtures = react$1.useMemo(() => {
|
|
42
|
+
if (scenario && scenarios[scenario]) {
|
|
43
|
+
return { ...fixtures, ...scenarios[scenario] };
|
|
44
|
+
}
|
|
45
|
+
return fixtures;
|
|
46
|
+
}, [fixtures, scenarios, scenario]);
|
|
47
|
+
const enabled = react$1.useMemo(() => {
|
|
48
|
+
if (initialEnabled !== void 0) {
|
|
49
|
+
return initialEnabled;
|
|
50
|
+
}
|
|
51
|
+
if (!isHydrated) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
return getDemoModeFromCookie(cookieName);
|
|
55
|
+
}, [initialEnabled, isHydrated, cookieName]);
|
|
56
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
57
|
+
react.DemoKitProvider,
|
|
58
|
+
{
|
|
59
|
+
fixtures: activeFixtures,
|
|
60
|
+
storageKey,
|
|
61
|
+
initialEnabled: enabled,
|
|
62
|
+
baseUrl,
|
|
63
|
+
children
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
function useNextDemoMode(options = {}) {
|
|
68
|
+
const { urlParam = "demo" } = options;
|
|
69
|
+
const demoMode = react.useDemoMode();
|
|
70
|
+
const router = navigation.useRouter();
|
|
71
|
+
const searchParams = navigation.useSearchParams();
|
|
72
|
+
const enableWithScenario = react$1.useCallback(
|
|
73
|
+
(scenario) => {
|
|
74
|
+
const params = new URLSearchParams(searchParams.toString());
|
|
75
|
+
params.set(urlParam, scenario);
|
|
76
|
+
router.push(`?${params.toString()}`);
|
|
77
|
+
},
|
|
78
|
+
[router, searchParams, urlParam]
|
|
79
|
+
);
|
|
80
|
+
const enableDemo = react$1.useCallback(() => {
|
|
81
|
+
const params = new URLSearchParams(searchParams.toString());
|
|
82
|
+
params.set(urlParam, "true");
|
|
83
|
+
router.push(`?${params.toString()}`);
|
|
84
|
+
}, [router, searchParams, urlParam]);
|
|
85
|
+
const disableDemo = react$1.useCallback(() => {
|
|
86
|
+
const params = new URLSearchParams(searchParams.toString());
|
|
87
|
+
params.set(urlParam, "false");
|
|
88
|
+
router.push(`?${params.toString()}`);
|
|
89
|
+
}, [router, searchParams, urlParam]);
|
|
90
|
+
const currentScenario = searchParams.get(urlParam);
|
|
91
|
+
const isScenario = currentScenario !== null && currentScenario !== "true" && currentScenario !== "false" && currentScenario !== "1" && currentScenario !== "0";
|
|
92
|
+
return {
|
|
93
|
+
...demoMode,
|
|
94
|
+
enableWithScenario,
|
|
95
|
+
enableDemo,
|
|
96
|
+
disableDemo,
|
|
97
|
+
currentScenario: isScenario ? currentScenario : null
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function useIsNextDemoMode() {
|
|
101
|
+
return useNextDemoMode().isDemoMode;
|
|
102
|
+
}
|
|
103
|
+
function NextDemoModeBanner({
|
|
104
|
+
showScenario = true,
|
|
105
|
+
onExit,
|
|
106
|
+
description,
|
|
107
|
+
...props
|
|
108
|
+
}) {
|
|
109
|
+
const { currentScenario, disableDemo, isDemoMode, isHydrated } = useNextDemoMode();
|
|
110
|
+
if (!isHydrated || !isDemoMode) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
let bannerDescription = description ?? "Changes are simulated and not saved";
|
|
114
|
+
if (showScenario && currentScenario) {
|
|
115
|
+
bannerDescription = `Scenario: ${currentScenario} \u2022 ${bannerDescription}`;
|
|
116
|
+
}
|
|
117
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
118
|
+
react.DemoModeBanner,
|
|
119
|
+
{
|
|
120
|
+
...props,
|
|
121
|
+
description: bannerDescription,
|
|
122
|
+
onExit: onExit ?? disableDemo
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
Object.defineProperty(exports, "DemoModeBanner", {
|
|
128
|
+
enumerable: true,
|
|
129
|
+
get: function () { return react.DemoModeBanner; }
|
|
130
|
+
});
|
|
131
|
+
Object.defineProperty(exports, "useDemoMode", {
|
|
132
|
+
enumerable: true,
|
|
133
|
+
get: function () { return react.useDemoMode; }
|
|
134
|
+
});
|
|
135
|
+
Object.defineProperty(exports, "useDemoSession", {
|
|
136
|
+
enumerable: true,
|
|
137
|
+
get: function () { return react.useDemoSession; }
|
|
138
|
+
});
|
|
139
|
+
Object.defineProperty(exports, "useIsDemoMode", {
|
|
140
|
+
enumerable: true,
|
|
141
|
+
get: function () { return react.useIsDemoMode; }
|
|
142
|
+
});
|
|
143
|
+
Object.defineProperty(exports, "useIsHydrated", {
|
|
144
|
+
enumerable: true,
|
|
145
|
+
get: function () { return react.useIsHydrated; }
|
|
146
|
+
});
|
|
147
|
+
exports.DemoKitNextProvider = DemoKitNextProvider;
|
|
148
|
+
exports.NextDemoModeBanner = NextDemoModeBanner;
|
|
149
|
+
exports.useIsNextDemoMode = useIsNextDemoMode;
|
|
150
|
+
exports.useNextDemoMode = useNextDemoMode;
|
|
151
|
+
//# sourceMappingURL=client.cjs.map
|
|
152
|
+
//# sourceMappingURL=client.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client/provider.tsx","../src/client/hooks.ts","../src/client/banner.tsx"],"names":["useState","useEffect","useMemo","jsx","DemoKitProvider","useDemoMode","useRouter","useSearchParams","useCallback","DemoModeBanner"],"mappings":"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","file":"client.cjs","sourcesContent":["'use client'\n\nimport { useEffect, useState, useMemo } from 'react'\nimport { DemoKitProvider, type DemoKitProviderProps } from '@demokit-ai/react'\nimport type { DemoKitNextProviderProps } from '../types'\n\n/**\n * Get initial demo mode state from cookie\n */\nfunction getDemoModeFromCookie(cookieName: string): boolean {\n if (typeof document === 'undefined') return false\n\n const cookies = document.cookie.split(';')\n const demoCookie = cookies.find((c) => c.trim().startsWith(`${cookieName}=`))\n\n return demoCookie !== undefined\n}\n\n/**\n * Get scenario from cookie\n */\nfunction getScenarioFromCookie(cookieName: string): string | null {\n if (typeof document === 'undefined') return null\n\n const cookies = document.cookie.split(';')\n const demoCookie = cookies.find((c) => c.trim().startsWith(`${cookieName}=`))\n\n if (!demoCookie) return null\n\n const value = demoCookie.split('=')[1]?.trim()\n if (!value || value === 'true') return null\n\n return value\n}\n\n/**\n * Next.js-aware DemoKit provider\n *\n * This provider:\n * - Reads initial state from cookies (set by middleware)\n * - Supports scenario switching\n * - Works with both App Router and Pages Router\n *\n * @example\n * // app/providers.tsx\n * 'use client'\n *\n * import { DemoKitNextProvider } from '@demokit-ai/next/client'\n * import { fixtures, scenarios } from '@/lib/demo-fixtures'\n *\n * export function Providers({ children }: { children: React.ReactNode }) {\n * return (\n * <DemoKitNextProvider\n * fixtures={fixtures}\n * scenarios={scenarios}\n * >\n * {children}\n * </DemoKitNextProvider>\n * )\n * }\n */\nexport function DemoKitNextProvider({\n children,\n fixtures,\n scenarios = {},\n storageKey = 'demokit-mode',\n cookieName = 'demokit-mode',\n initialEnabled,\n baseUrl,\n}: DemoKitNextProviderProps) {\n const [isHydrated, setIsHydrated] = useState(false)\n const [scenario, setScenario] = useState<string | null>(null)\n\n // Read initial state from cookie on mount\n useEffect(() => {\n const cookieEnabled = getDemoModeFromCookie(cookieName)\n const cookieScenario = getScenarioFromCookie(cookieName)\n setScenario(cookieScenario)\n setIsHydrated(true)\n }, [cookieName])\n\n // Merge fixtures with scenario fixtures\n const activeFixtures = useMemo(() => {\n if (scenario && scenarios[scenario]) {\n return { ...fixtures, ...scenarios[scenario] }\n }\n return fixtures\n }, [fixtures, scenarios, scenario])\n\n // Determine initial enabled state\n const enabled = useMemo(() => {\n if (initialEnabled !== undefined) {\n return initialEnabled\n }\n if (!isHydrated) {\n return false\n }\n return getDemoModeFromCookie(cookieName)\n }, [initialEnabled, isHydrated, cookieName])\n\n return (\n <DemoKitProvider\n fixtures={activeFixtures}\n storageKey={storageKey}\n initialEnabled={enabled}\n baseUrl={baseUrl}\n >\n {children}\n </DemoKitProvider>\n )\n}\n","'use client'\n\nimport { useCallback } from 'react'\nimport { useDemoMode } from '@demokit-ai/react'\nimport { useRouter, useSearchParams } from 'next/navigation'\n\n/**\n * Hook for Next.js-specific demo mode controls\n *\n * Extends useDemoMode with URL-based scenario switching\n *\n * @example\n * function DemoControls() {\n * const { isDemoMode, enableWithScenario, disableDemo, currentScenario } = useNextDemoMode()\n *\n * return (\n * <div>\n * <button onClick={() => enableWithScenario('empty-state')}>\n * Empty State Demo\n * </button>\n * <button onClick={() => enableWithScenario('error-state')}>\n * Error State Demo\n * </button>\n * <button onClick={disableDemo}>\n * Exit Demo\n * </button>\n * {currentScenario && <span>Scenario: {currentScenario}</span>}\n * </div>\n * )\n * }\n */\nexport function useNextDemoMode(options: { urlParam?: string } = {}) {\n const { urlParam = 'demo' } = options\n const demoMode = useDemoMode()\n const router = useRouter()\n const searchParams = useSearchParams()\n\n /**\n * Enable demo mode with a specific scenario via URL\n */\n const enableWithScenario = useCallback(\n (scenario: string) => {\n const params = new URLSearchParams(searchParams.toString())\n params.set(urlParam, scenario)\n router.push(`?${params.toString()}`)\n },\n [router, searchParams, urlParam]\n )\n\n /**\n * Enable demo mode without a scenario\n */\n const enableDemo = useCallback(() => {\n const params = new URLSearchParams(searchParams.toString())\n params.set(urlParam, 'true')\n router.push(`?${params.toString()}`)\n }, [router, searchParams, urlParam])\n\n /**\n * Disable demo mode via URL\n */\n const disableDemo = useCallback(() => {\n const params = new URLSearchParams(searchParams.toString())\n params.set(urlParam, 'false')\n router.push(`?${params.toString()}`)\n }, [router, searchParams, urlParam])\n\n /**\n * Get current scenario from URL\n */\n const currentScenario = searchParams.get(urlParam)\n const isScenario =\n currentScenario !== null &&\n currentScenario !== 'true' &&\n currentScenario !== 'false' &&\n currentScenario !== '1' &&\n currentScenario !== '0'\n\n return {\n ...demoMode,\n enableWithScenario,\n enableDemo,\n disableDemo,\n currentScenario: isScenario ? currentScenario : null,\n }\n}\n\n/**\n * Hook to check if we're in demo mode on the client\n * Shorthand for useNextDemoMode().isDemoMode\n */\nexport function useIsNextDemoMode(): boolean {\n return useNextDemoMode().isDemoMode\n}\n\n// Re-export base hooks from @demokit-ai/react\nexport { useDemoMode, useIsDemoMode, useIsHydrated, useDemoSession } from '@demokit-ai/react'\n","'use client'\n\nimport { DemoModeBanner, type DemoModeBannerProps } from '@demokit-ai/react'\nimport { useNextDemoMode } from './hooks'\n\nexport interface NextDemoModeBannerProps extends Omit<DemoModeBannerProps, 'onExit'> {\n /**\n * Whether to include the current scenario in the banner\n * @default true\n */\n showScenario?: boolean\n\n /**\n * Custom exit handler\n * If not provided, will use URL-based navigation\n */\n onExit?: () => void\n}\n\n/**\n * Demo mode banner for Next.js\n *\n * Extends DemoModeBanner with scenario display and URL-based exit\n *\n * @example\n * // In your layout\n * <NextDemoModeBanner />\n *\n * // With custom labels\n * <NextDemoModeBanner\n * demoLabel=\"Preview Mode\"\n * exitLabel=\"Exit Preview\"\n * showScenario={true}\n * />\n */\nexport function NextDemoModeBanner({\n showScenario = true,\n onExit,\n description,\n ...props\n}: NextDemoModeBannerProps) {\n const { currentScenario, disableDemo, isDemoMode, isHydrated } = useNextDemoMode()\n\n // Don't render if not hydrated or not in demo mode\n if (!isHydrated || !isDemoMode) {\n return null\n }\n\n // Build description with scenario\n let bannerDescription = description ?? 'Changes are simulated and not saved'\n if (showScenario && currentScenario) {\n bannerDescription = `Scenario: ${currentScenario} • ${bannerDescription}`\n }\n\n return (\n <DemoModeBanner\n {...props}\n description={bannerDescription}\n onExit={onExit ?? disableDemo}\n />\n )\n}\n\n// Re-export base banner\nexport { DemoModeBanner } from '@demokit-ai/react'\n"]}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { b as DemoKitNextProviderProps } from './types-Cvq5JUP1.cjs';
|
|
3
|
+
import * as _demokit_ai_core from '@demokit-ai/core';
|
|
4
|
+
import { DemoModeBannerProps } from '@demokit-ai/react';
|
|
5
|
+
export { DemoModeBanner, useDemoMode, useDemoSession, useIsDemoMode, useIsHydrated } from '@demokit-ai/react';
|
|
6
|
+
import 'react';
|
|
7
|
+
import 'next/server';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Next.js-aware DemoKit provider
|
|
11
|
+
*
|
|
12
|
+
* This provider:
|
|
13
|
+
* - Reads initial state from cookies (set by middleware)
|
|
14
|
+
* - Supports scenario switching
|
|
15
|
+
* - Works with both App Router and Pages Router
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // app/providers.tsx
|
|
19
|
+
* 'use client'
|
|
20
|
+
*
|
|
21
|
+
* import { DemoKitNextProvider } from '@demokit-ai/next/client'
|
|
22
|
+
* import { fixtures, scenarios } from '@/lib/demo-fixtures'
|
|
23
|
+
*
|
|
24
|
+
* export function Providers({ children }: { children: React.ReactNode }) {
|
|
25
|
+
* return (
|
|
26
|
+
* <DemoKitNextProvider
|
|
27
|
+
* fixtures={fixtures}
|
|
28
|
+
* scenarios={scenarios}
|
|
29
|
+
* >
|
|
30
|
+
* {children}
|
|
31
|
+
* </DemoKitNextProvider>
|
|
32
|
+
* )
|
|
33
|
+
* }
|
|
34
|
+
*/
|
|
35
|
+
declare function DemoKitNextProvider({ children, fixtures, scenarios, storageKey, cookieName, initialEnabled, baseUrl, }: DemoKitNextProviderProps): react_jsx_runtime.JSX.Element;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Hook for Next.js-specific demo mode controls
|
|
39
|
+
*
|
|
40
|
+
* Extends useDemoMode with URL-based scenario switching
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* function DemoControls() {
|
|
44
|
+
* const { isDemoMode, enableWithScenario, disableDemo, currentScenario } = useNextDemoMode()
|
|
45
|
+
*
|
|
46
|
+
* return (
|
|
47
|
+
* <div>
|
|
48
|
+
* <button onClick={() => enableWithScenario('empty-state')}>
|
|
49
|
+
* Empty State Demo
|
|
50
|
+
* </button>
|
|
51
|
+
* <button onClick={() => enableWithScenario('error-state')}>
|
|
52
|
+
* Error State Demo
|
|
53
|
+
* </button>
|
|
54
|
+
* <button onClick={disableDemo}>
|
|
55
|
+
* Exit Demo
|
|
56
|
+
* </button>
|
|
57
|
+
* {currentScenario && <span>Scenario: {currentScenario}</span>}
|
|
58
|
+
* </div>
|
|
59
|
+
* )
|
|
60
|
+
* }
|
|
61
|
+
*/
|
|
62
|
+
declare function useNextDemoMode(options?: {
|
|
63
|
+
urlParam?: string;
|
|
64
|
+
}): {
|
|
65
|
+
enableWithScenario: (scenario: string) => void;
|
|
66
|
+
enableDemo: () => void;
|
|
67
|
+
disableDemo: () => void;
|
|
68
|
+
currentScenario: string | null;
|
|
69
|
+
isDemoMode: boolean;
|
|
70
|
+
isHydrated: boolean;
|
|
71
|
+
enable(): void;
|
|
72
|
+
disable(): void;
|
|
73
|
+
toggle(): void;
|
|
74
|
+
setDemoMode(enabled: boolean): void;
|
|
75
|
+
resetSession(): void;
|
|
76
|
+
getSession(): _demokit_ai_core.SessionState | null;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Hook to check if we're in demo mode on the client
|
|
80
|
+
* Shorthand for useNextDemoMode().isDemoMode
|
|
81
|
+
*/
|
|
82
|
+
declare function useIsNextDemoMode(): boolean;
|
|
83
|
+
|
|
84
|
+
interface NextDemoModeBannerProps extends Omit<DemoModeBannerProps, 'onExit'> {
|
|
85
|
+
/**
|
|
86
|
+
* Whether to include the current scenario in the banner
|
|
87
|
+
* @default true
|
|
88
|
+
*/
|
|
89
|
+
showScenario?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Custom exit handler
|
|
92
|
+
* If not provided, will use URL-based navigation
|
|
93
|
+
*/
|
|
94
|
+
onExit?: () => void;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Demo mode banner for Next.js
|
|
98
|
+
*
|
|
99
|
+
* Extends DemoModeBanner with scenario display and URL-based exit
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* // In your layout
|
|
103
|
+
* <NextDemoModeBanner />
|
|
104
|
+
*
|
|
105
|
+
* // With custom labels
|
|
106
|
+
* <NextDemoModeBanner
|
|
107
|
+
* demoLabel="Preview Mode"
|
|
108
|
+
* exitLabel="Exit Preview"
|
|
109
|
+
* showScenario={true}
|
|
110
|
+
* />
|
|
111
|
+
*/
|
|
112
|
+
declare function NextDemoModeBanner({ showScenario, onExit, description, ...props }: NextDemoModeBannerProps): react_jsx_runtime.JSX.Element | null;
|
|
113
|
+
|
|
114
|
+
export { DemoKitNextProvider, NextDemoModeBanner, type NextDemoModeBannerProps, useIsNextDemoMode, useNextDemoMode };
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { b as DemoKitNextProviderProps } from './types-Cvq5JUP1.js';
|
|
3
|
+
import * as _demokit_ai_core from '@demokit-ai/core';
|
|
4
|
+
import { DemoModeBannerProps } from '@demokit-ai/react';
|
|
5
|
+
export { DemoModeBanner, useDemoMode, useDemoSession, useIsDemoMode, useIsHydrated } from '@demokit-ai/react';
|
|
6
|
+
import 'react';
|
|
7
|
+
import 'next/server';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Next.js-aware DemoKit provider
|
|
11
|
+
*
|
|
12
|
+
* This provider:
|
|
13
|
+
* - Reads initial state from cookies (set by middleware)
|
|
14
|
+
* - Supports scenario switching
|
|
15
|
+
* - Works with both App Router and Pages Router
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // app/providers.tsx
|
|
19
|
+
* 'use client'
|
|
20
|
+
*
|
|
21
|
+
* import { DemoKitNextProvider } from '@demokit-ai/next/client'
|
|
22
|
+
* import { fixtures, scenarios } from '@/lib/demo-fixtures'
|
|
23
|
+
*
|
|
24
|
+
* export function Providers({ children }: { children: React.ReactNode }) {
|
|
25
|
+
* return (
|
|
26
|
+
* <DemoKitNextProvider
|
|
27
|
+
* fixtures={fixtures}
|
|
28
|
+
* scenarios={scenarios}
|
|
29
|
+
* >
|
|
30
|
+
* {children}
|
|
31
|
+
* </DemoKitNextProvider>
|
|
32
|
+
* )
|
|
33
|
+
* }
|
|
34
|
+
*/
|
|
35
|
+
declare function DemoKitNextProvider({ children, fixtures, scenarios, storageKey, cookieName, initialEnabled, baseUrl, }: DemoKitNextProviderProps): react_jsx_runtime.JSX.Element;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Hook for Next.js-specific demo mode controls
|
|
39
|
+
*
|
|
40
|
+
* Extends useDemoMode with URL-based scenario switching
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* function DemoControls() {
|
|
44
|
+
* const { isDemoMode, enableWithScenario, disableDemo, currentScenario } = useNextDemoMode()
|
|
45
|
+
*
|
|
46
|
+
* return (
|
|
47
|
+
* <div>
|
|
48
|
+
* <button onClick={() => enableWithScenario('empty-state')}>
|
|
49
|
+
* Empty State Demo
|
|
50
|
+
* </button>
|
|
51
|
+
* <button onClick={() => enableWithScenario('error-state')}>
|
|
52
|
+
* Error State Demo
|
|
53
|
+
* </button>
|
|
54
|
+
* <button onClick={disableDemo}>
|
|
55
|
+
* Exit Demo
|
|
56
|
+
* </button>
|
|
57
|
+
* {currentScenario && <span>Scenario: {currentScenario}</span>}
|
|
58
|
+
* </div>
|
|
59
|
+
* )
|
|
60
|
+
* }
|
|
61
|
+
*/
|
|
62
|
+
declare function useNextDemoMode(options?: {
|
|
63
|
+
urlParam?: string;
|
|
64
|
+
}): {
|
|
65
|
+
enableWithScenario: (scenario: string) => void;
|
|
66
|
+
enableDemo: () => void;
|
|
67
|
+
disableDemo: () => void;
|
|
68
|
+
currentScenario: string | null;
|
|
69
|
+
isDemoMode: boolean;
|
|
70
|
+
isHydrated: boolean;
|
|
71
|
+
enable(): void;
|
|
72
|
+
disable(): void;
|
|
73
|
+
toggle(): void;
|
|
74
|
+
setDemoMode(enabled: boolean): void;
|
|
75
|
+
resetSession(): void;
|
|
76
|
+
getSession(): _demokit_ai_core.SessionState | null;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Hook to check if we're in demo mode on the client
|
|
80
|
+
* Shorthand for useNextDemoMode().isDemoMode
|
|
81
|
+
*/
|
|
82
|
+
declare function useIsNextDemoMode(): boolean;
|
|
83
|
+
|
|
84
|
+
interface NextDemoModeBannerProps extends Omit<DemoModeBannerProps, 'onExit'> {
|
|
85
|
+
/**
|
|
86
|
+
* Whether to include the current scenario in the banner
|
|
87
|
+
* @default true
|
|
88
|
+
*/
|
|
89
|
+
showScenario?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Custom exit handler
|
|
92
|
+
* If not provided, will use URL-based navigation
|
|
93
|
+
*/
|
|
94
|
+
onExit?: () => void;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Demo mode banner for Next.js
|
|
98
|
+
*
|
|
99
|
+
* Extends DemoModeBanner with scenario display and URL-based exit
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* // In your layout
|
|
103
|
+
* <NextDemoModeBanner />
|
|
104
|
+
*
|
|
105
|
+
* // With custom labels
|
|
106
|
+
* <NextDemoModeBanner
|
|
107
|
+
* demoLabel="Preview Mode"
|
|
108
|
+
* exitLabel="Exit Preview"
|
|
109
|
+
* showScenario={true}
|
|
110
|
+
* />
|
|
111
|
+
*/
|
|
112
|
+
declare function NextDemoModeBanner({ showScenario, onExit, description, ...props }: NextDemoModeBannerProps): react_jsx_runtime.JSX.Element | null;
|
|
113
|
+
|
|
114
|
+
export { DemoKitNextProvider, NextDemoModeBanner, type NextDemoModeBannerProps, useIsNextDemoMode, useNextDemoMode };
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { useState, useEffect, useMemo, useCallback } from 'react';
|
|
2
|
+
import { DemoKitProvider, useDemoMode, DemoModeBanner } from '@demokit-ai/react';
|
|
3
|
+
export { DemoModeBanner, useDemoMode, useDemoSession, useIsDemoMode, useIsHydrated } from '@demokit-ai/react';
|
|
4
|
+
import { jsx } from 'react/jsx-runtime';
|
|
5
|
+
import { useRouter, useSearchParams } from 'next/navigation';
|
|
6
|
+
|
|
7
|
+
// src/client/provider.tsx
|
|
8
|
+
function getDemoModeFromCookie(cookieName) {
|
|
9
|
+
if (typeof document === "undefined") return false;
|
|
10
|
+
const cookies = document.cookie.split(";");
|
|
11
|
+
const demoCookie = cookies.find((c) => c.trim().startsWith(`${cookieName}=`));
|
|
12
|
+
return demoCookie !== void 0;
|
|
13
|
+
}
|
|
14
|
+
function getScenarioFromCookie(cookieName) {
|
|
15
|
+
if (typeof document === "undefined") return null;
|
|
16
|
+
const cookies = document.cookie.split(";");
|
|
17
|
+
const demoCookie = cookies.find((c) => c.trim().startsWith(`${cookieName}=`));
|
|
18
|
+
if (!demoCookie) return null;
|
|
19
|
+
const value = demoCookie.split("=")[1]?.trim();
|
|
20
|
+
if (!value || value === "true") return null;
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
function DemoKitNextProvider({
|
|
24
|
+
children,
|
|
25
|
+
fixtures,
|
|
26
|
+
scenarios = {},
|
|
27
|
+
storageKey = "demokit-mode",
|
|
28
|
+
cookieName = "demokit-mode",
|
|
29
|
+
initialEnabled,
|
|
30
|
+
baseUrl
|
|
31
|
+
}) {
|
|
32
|
+
const [isHydrated, setIsHydrated] = useState(false);
|
|
33
|
+
const [scenario, setScenario] = useState(null);
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
getDemoModeFromCookie(cookieName);
|
|
36
|
+
const cookieScenario = getScenarioFromCookie(cookieName);
|
|
37
|
+
setScenario(cookieScenario);
|
|
38
|
+
setIsHydrated(true);
|
|
39
|
+
}, [cookieName]);
|
|
40
|
+
const activeFixtures = useMemo(() => {
|
|
41
|
+
if (scenario && scenarios[scenario]) {
|
|
42
|
+
return { ...fixtures, ...scenarios[scenario] };
|
|
43
|
+
}
|
|
44
|
+
return fixtures;
|
|
45
|
+
}, [fixtures, scenarios, scenario]);
|
|
46
|
+
const enabled = useMemo(() => {
|
|
47
|
+
if (initialEnabled !== void 0) {
|
|
48
|
+
return initialEnabled;
|
|
49
|
+
}
|
|
50
|
+
if (!isHydrated) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
return getDemoModeFromCookie(cookieName);
|
|
54
|
+
}, [initialEnabled, isHydrated, cookieName]);
|
|
55
|
+
return /* @__PURE__ */ jsx(
|
|
56
|
+
DemoKitProvider,
|
|
57
|
+
{
|
|
58
|
+
fixtures: activeFixtures,
|
|
59
|
+
storageKey,
|
|
60
|
+
initialEnabled: enabled,
|
|
61
|
+
baseUrl,
|
|
62
|
+
children
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
function useNextDemoMode(options = {}) {
|
|
67
|
+
const { urlParam = "demo" } = options;
|
|
68
|
+
const demoMode = useDemoMode();
|
|
69
|
+
const router = useRouter();
|
|
70
|
+
const searchParams = useSearchParams();
|
|
71
|
+
const enableWithScenario = useCallback(
|
|
72
|
+
(scenario) => {
|
|
73
|
+
const params = new URLSearchParams(searchParams.toString());
|
|
74
|
+
params.set(urlParam, scenario);
|
|
75
|
+
router.push(`?${params.toString()}`);
|
|
76
|
+
},
|
|
77
|
+
[router, searchParams, urlParam]
|
|
78
|
+
);
|
|
79
|
+
const enableDemo = useCallback(() => {
|
|
80
|
+
const params = new URLSearchParams(searchParams.toString());
|
|
81
|
+
params.set(urlParam, "true");
|
|
82
|
+
router.push(`?${params.toString()}`);
|
|
83
|
+
}, [router, searchParams, urlParam]);
|
|
84
|
+
const disableDemo = useCallback(() => {
|
|
85
|
+
const params = new URLSearchParams(searchParams.toString());
|
|
86
|
+
params.set(urlParam, "false");
|
|
87
|
+
router.push(`?${params.toString()}`);
|
|
88
|
+
}, [router, searchParams, urlParam]);
|
|
89
|
+
const currentScenario = searchParams.get(urlParam);
|
|
90
|
+
const isScenario = currentScenario !== null && currentScenario !== "true" && currentScenario !== "false" && currentScenario !== "1" && currentScenario !== "0";
|
|
91
|
+
return {
|
|
92
|
+
...demoMode,
|
|
93
|
+
enableWithScenario,
|
|
94
|
+
enableDemo,
|
|
95
|
+
disableDemo,
|
|
96
|
+
currentScenario: isScenario ? currentScenario : null
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function useIsNextDemoMode() {
|
|
100
|
+
return useNextDemoMode().isDemoMode;
|
|
101
|
+
}
|
|
102
|
+
function NextDemoModeBanner({
|
|
103
|
+
showScenario = true,
|
|
104
|
+
onExit,
|
|
105
|
+
description,
|
|
106
|
+
...props
|
|
107
|
+
}) {
|
|
108
|
+
const { currentScenario, disableDemo, isDemoMode, isHydrated } = useNextDemoMode();
|
|
109
|
+
if (!isHydrated || !isDemoMode) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
let bannerDescription = description ?? "Changes are simulated and not saved";
|
|
113
|
+
if (showScenario && currentScenario) {
|
|
114
|
+
bannerDescription = `Scenario: ${currentScenario} \u2022 ${bannerDescription}`;
|
|
115
|
+
}
|
|
116
|
+
return /* @__PURE__ */ jsx(
|
|
117
|
+
DemoModeBanner,
|
|
118
|
+
{
|
|
119
|
+
...props,
|
|
120
|
+
description: bannerDescription,
|
|
121
|
+
onExit: onExit ?? disableDemo
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export { DemoKitNextProvider, NextDemoModeBanner, useIsNextDemoMode, useNextDemoMode };
|
|
127
|
+
//# sourceMappingURL=client.js.map
|
|
128
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client/provider.tsx","../src/client/hooks.ts","../src/client/banner.tsx"],"names":["jsx"],"mappings":"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","file":"client.js","sourcesContent":["'use client'\n\nimport { useEffect, useState, useMemo } from 'react'\nimport { DemoKitProvider, type DemoKitProviderProps } from '@demokit-ai/react'\nimport type { DemoKitNextProviderProps } from '../types'\n\n/**\n * Get initial demo mode state from cookie\n */\nfunction getDemoModeFromCookie(cookieName: string): boolean {\n if (typeof document === 'undefined') return false\n\n const cookies = document.cookie.split(';')\n const demoCookie = cookies.find((c) => c.trim().startsWith(`${cookieName}=`))\n\n return demoCookie !== undefined\n}\n\n/**\n * Get scenario from cookie\n */\nfunction getScenarioFromCookie(cookieName: string): string | null {\n if (typeof document === 'undefined') return null\n\n const cookies = document.cookie.split(';')\n const demoCookie = cookies.find((c) => c.trim().startsWith(`${cookieName}=`))\n\n if (!demoCookie) return null\n\n const value = demoCookie.split('=')[1]?.trim()\n if (!value || value === 'true') return null\n\n return value\n}\n\n/**\n * Next.js-aware DemoKit provider\n *\n * This provider:\n * - Reads initial state from cookies (set by middleware)\n * - Supports scenario switching\n * - Works with both App Router and Pages Router\n *\n * @example\n * // app/providers.tsx\n * 'use client'\n *\n * import { DemoKitNextProvider } from '@demokit-ai/next/client'\n * import { fixtures, scenarios } from '@/lib/demo-fixtures'\n *\n * export function Providers({ children }: { children: React.ReactNode }) {\n * return (\n * <DemoKitNextProvider\n * fixtures={fixtures}\n * scenarios={scenarios}\n * >\n * {children}\n * </DemoKitNextProvider>\n * )\n * }\n */\nexport function DemoKitNextProvider({\n children,\n fixtures,\n scenarios = {},\n storageKey = 'demokit-mode',\n cookieName = 'demokit-mode',\n initialEnabled,\n baseUrl,\n}: DemoKitNextProviderProps) {\n const [isHydrated, setIsHydrated] = useState(false)\n const [scenario, setScenario] = useState<string | null>(null)\n\n // Read initial state from cookie on mount\n useEffect(() => {\n const cookieEnabled = getDemoModeFromCookie(cookieName)\n const cookieScenario = getScenarioFromCookie(cookieName)\n setScenario(cookieScenario)\n setIsHydrated(true)\n }, [cookieName])\n\n // Merge fixtures with scenario fixtures\n const activeFixtures = useMemo(() => {\n if (scenario && scenarios[scenario]) {\n return { ...fixtures, ...scenarios[scenario] }\n }\n return fixtures\n }, [fixtures, scenarios, scenario])\n\n // Determine initial enabled state\n const enabled = useMemo(() => {\n if (initialEnabled !== undefined) {\n return initialEnabled\n }\n if (!isHydrated) {\n return false\n }\n return getDemoModeFromCookie(cookieName)\n }, [initialEnabled, isHydrated, cookieName])\n\n return (\n <DemoKitProvider\n fixtures={activeFixtures}\n storageKey={storageKey}\n initialEnabled={enabled}\n baseUrl={baseUrl}\n >\n {children}\n </DemoKitProvider>\n )\n}\n","'use client'\n\nimport { useCallback } from 'react'\nimport { useDemoMode } from '@demokit-ai/react'\nimport { useRouter, useSearchParams } from 'next/navigation'\n\n/**\n * Hook for Next.js-specific demo mode controls\n *\n * Extends useDemoMode with URL-based scenario switching\n *\n * @example\n * function DemoControls() {\n * const { isDemoMode, enableWithScenario, disableDemo, currentScenario } = useNextDemoMode()\n *\n * return (\n * <div>\n * <button onClick={() => enableWithScenario('empty-state')}>\n * Empty State Demo\n * </button>\n * <button onClick={() => enableWithScenario('error-state')}>\n * Error State Demo\n * </button>\n * <button onClick={disableDemo}>\n * Exit Demo\n * </button>\n * {currentScenario && <span>Scenario: {currentScenario}</span>}\n * </div>\n * )\n * }\n */\nexport function useNextDemoMode(options: { urlParam?: string } = {}) {\n const { urlParam = 'demo' } = options\n const demoMode = useDemoMode()\n const router = useRouter()\n const searchParams = useSearchParams()\n\n /**\n * Enable demo mode with a specific scenario via URL\n */\n const enableWithScenario = useCallback(\n (scenario: string) => {\n const params = new URLSearchParams(searchParams.toString())\n params.set(urlParam, scenario)\n router.push(`?${params.toString()}`)\n },\n [router, searchParams, urlParam]\n )\n\n /**\n * Enable demo mode without a scenario\n */\n const enableDemo = useCallback(() => {\n const params = new URLSearchParams(searchParams.toString())\n params.set(urlParam, 'true')\n router.push(`?${params.toString()}`)\n }, [router, searchParams, urlParam])\n\n /**\n * Disable demo mode via URL\n */\n const disableDemo = useCallback(() => {\n const params = new URLSearchParams(searchParams.toString())\n params.set(urlParam, 'false')\n router.push(`?${params.toString()}`)\n }, [router, searchParams, urlParam])\n\n /**\n * Get current scenario from URL\n */\n const currentScenario = searchParams.get(urlParam)\n const isScenario =\n currentScenario !== null &&\n currentScenario !== 'true' &&\n currentScenario !== 'false' &&\n currentScenario !== '1' &&\n currentScenario !== '0'\n\n return {\n ...demoMode,\n enableWithScenario,\n enableDemo,\n disableDemo,\n currentScenario: isScenario ? currentScenario : null,\n }\n}\n\n/**\n * Hook to check if we're in demo mode on the client\n * Shorthand for useNextDemoMode().isDemoMode\n */\nexport function useIsNextDemoMode(): boolean {\n return useNextDemoMode().isDemoMode\n}\n\n// Re-export base hooks from @demokit-ai/react\nexport { useDemoMode, useIsDemoMode, useIsHydrated, useDemoSession } from '@demokit-ai/react'\n","'use client'\n\nimport { DemoModeBanner, type DemoModeBannerProps } from '@demokit-ai/react'\nimport { useNextDemoMode } from './hooks'\n\nexport interface NextDemoModeBannerProps extends Omit<DemoModeBannerProps, 'onExit'> {\n /**\n * Whether to include the current scenario in the banner\n * @default true\n */\n showScenario?: boolean\n\n /**\n * Custom exit handler\n * If not provided, will use URL-based navigation\n */\n onExit?: () => void\n}\n\n/**\n * Demo mode banner for Next.js\n *\n * Extends DemoModeBanner with scenario display and URL-based exit\n *\n * @example\n * // In your layout\n * <NextDemoModeBanner />\n *\n * // With custom labels\n * <NextDemoModeBanner\n * demoLabel=\"Preview Mode\"\n * exitLabel=\"Exit Preview\"\n * showScenario={true}\n * />\n */\nexport function NextDemoModeBanner({\n showScenario = true,\n onExit,\n description,\n ...props\n}: NextDemoModeBannerProps) {\n const { currentScenario, disableDemo, isDemoMode, isHydrated } = useNextDemoMode()\n\n // Don't render if not hydrated or not in demo mode\n if (!isHydrated || !isDemoMode) {\n return null\n }\n\n // Build description with scenario\n let bannerDescription = description ?? 'Changes are simulated and not saved'\n if (showScenario && currentScenario) {\n bannerDescription = `Scenario: ${currentScenario} • ${bannerDescription}`\n }\n\n return (\n <DemoModeBanner\n {...props}\n description={bannerDescription}\n onExit={onExit ?? disableDemo}\n />\n )\n}\n\n// Re-export base banner\nexport { DemoModeBanner } from '@demokit-ai/react'\n"]}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/config.ts
|
|
4
|
+
function defineFixtures(fixtures) {
|
|
5
|
+
return fixtures;
|
|
6
|
+
}
|
|
7
|
+
function defineScenarios(scenarios) {
|
|
8
|
+
return scenarios;
|
|
9
|
+
}
|
|
10
|
+
function createScenario(scenario) {
|
|
11
|
+
return scenario;
|
|
12
|
+
}
|
|
13
|
+
function mergeFixtures(...fixtureMaps) {
|
|
14
|
+
return Object.assign({}, ...fixtureMaps);
|
|
15
|
+
}
|
|
16
|
+
function createDemoConfig(config) {
|
|
17
|
+
return {
|
|
18
|
+
storageKey: "demokit-mode",
|
|
19
|
+
cookieName: "demokit-mode",
|
|
20
|
+
urlParam: "demo",
|
|
21
|
+
...config
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
exports.createDemoConfig = createDemoConfig;
|
|
26
|
+
exports.createScenario = createScenario;
|
|
27
|
+
exports.defineFixtures = defineFixtures;
|
|
28
|
+
exports.defineScenarios = defineScenarios;
|
|
29
|
+
exports.mergeFixtures = mergeFixtures;
|
|
30
|
+
//# sourceMappingURL=index.cjs.map
|
|
31
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts"],"names":[],"mappings":";;;AAqBO,SAAS,eAAqC,QAAA,EAAgB;AACnE,EAAA,OAAO,QAAA;AACT;AAuBO,SAAS,gBAAsD,SAAA,EAAiB;AACrF,EAAA,OAAO,SAAA;AACT;AAcO,SAAS,eAAe,QAAA,EAAsC;AACnE,EAAA,OAAO,QAAA;AACT;AAYO,SAAS,iBAAiB,WAAA,EAAuC;AACtE,EAAA,OAAO,MAAA,CAAO,MAAA,CAAO,EAAC,EAAG,GAAG,WAAW,CAAA;AACzC;AAkBO,SAAS,iBAAiB,MAAA,EAA8C;AAC7E,EAAA,OAAO;AAAA,IACL,UAAA,EAAY,cAAA;AAAA,IACZ,UAAA,EAAY,cAAA;AAAA,IACZ,QAAA,EAAU,MAAA;AAAA,IACV,GAAG;AAAA,GACL;AACF","file":"index.cjs","sourcesContent":["import type { FixtureMap, FixtureHandler } from '@demokit-ai/core'\nimport type { DemoKitNextConfig, DemoScenario } from './types'\n\n/**\n * Helper to define fixtures with type safety\n *\n * @example\n * const fixtures = defineFixtures({\n * 'GET /api/users': () => [\n * { id: '1', name: 'Demo User' },\n * ],\n * 'GET /api/users/:id': ({ params }) => ({\n * id: params.id,\n * name: `User ${params.id}`,\n * }),\n * 'POST /api/users': async ({ body }) => ({\n * id: crypto.randomUUID(),\n * ...body,\n * }),\n * })\n */\nexport function defineFixtures<T extends FixtureMap>(fixtures: T): T {\n return fixtures\n}\n\n/**\n * Helper to define scenarios with type safety\n *\n * @example\n * const scenarios = defineScenarios({\n * 'empty-state': {\n * 'GET /api/users': () => [],\n * 'GET /api/projects': () => [],\n * },\n * 'error-state': {\n * 'GET /api/users': () => {\n * throw new Error('API Error')\n * },\n * },\n * 'new-user': {\n * 'GET /api/users': () => [\n * { id: '1', name: 'Welcome, New User!', isNew: true },\n * ],\n * },\n * })\n */\nexport function defineScenarios<T extends Record<string, FixtureMap>>(scenarios: T): T {\n return scenarios\n}\n\n/**\n * Helper to create a scenario object\n *\n * @example\n * const emptyStateScenario = createScenario({\n * name: 'empty-state',\n * description: 'Shows the app with no data',\n * fixtures: {\n * 'GET /api/users': () => [],\n * },\n * })\n */\nexport function createScenario(scenario: DemoScenario): DemoScenario {\n return scenario\n}\n\n/**\n * Merge multiple fixture maps\n *\n * @example\n * const allFixtures = mergeFixtures(\n * baseFixtures,\n * usersFixtures,\n * projectsFixtures\n * )\n */\nexport function mergeFixtures(...fixtureMaps: FixtureMap[]): FixtureMap {\n return Object.assign({}, ...fixtureMaps)\n}\n\n/**\n * Create a complete DemoKit Next.js configuration\n *\n * @example\n * // lib/demo.ts\n * import { createDemoConfig, defineFixtures, defineScenarios } from '@demokit-ai/next'\n *\n * export const demoConfig = createDemoConfig({\n * fixtures: defineFixtures({\n * 'GET /api/users': () => [{ id: '1', name: 'Demo User' }],\n * }),\n * scenarios: defineScenarios({\n * 'empty': { 'GET /api/users': () => [] },\n * }),\n * })\n */\nexport function createDemoConfig(config: DemoKitNextConfig): DemoKitNextConfig {\n return {\n storageKey: 'demokit-mode',\n cookieName: 'demokit-mode',\n urlParam: 'demo',\n ...config,\n }\n}\n"]}
|