@demokit-ai/remix 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.
@@ -0,0 +1,307 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/server.ts
21
+ var server_exports = {};
22
+ __export(server_exports, {
23
+ FixtureStore: () => FixtureStore,
24
+ clearDemoModeCookie: () => clearDemoModeCookie,
25
+ createDemoModeChecker: () => createDemoModeChecker,
26
+ createDemoModeCookie: () => createDemoModeCookie,
27
+ createFixtureStore: () => createFixtureStore,
28
+ defineRemixActionFixtures: () => defineRemixActionFixtures,
29
+ defineRemixFixtures: () => defineRemixFixtures,
30
+ defineRemixLoaderFixtures: () => defineRemixLoaderFixtures,
31
+ getDemoActionResult: () => getDemoActionResult,
32
+ getDemoData: () => getDemoData,
33
+ getFixtureStore: () => getFixtureStore,
34
+ isDemoMode: () => isDemoMode,
35
+ setupDemoFixtures: () => setupDemoFixtures
36
+ });
37
+ module.exports = __toCommonJS(server_exports);
38
+
39
+ // src/fixtures.ts
40
+ var import_core = require("@demokit-ai/core");
41
+ var FixtureStore = class {
42
+ loaders = {};
43
+ actions = {};
44
+ constructor(config) {
45
+ if (config?.loaders) {
46
+ this.loaders = { ...config.loaders };
47
+ }
48
+ if (config?.actions) {
49
+ this.actions = { ...config.actions };
50
+ }
51
+ }
52
+ /**
53
+ * Register a loader fixture
54
+ */
55
+ setLoader(path, handler) {
56
+ this.loaders[path] = handler;
57
+ return this;
58
+ }
59
+ /**
60
+ * Register an action fixture
61
+ */
62
+ setAction(path, handler) {
63
+ this.actions[path] = handler;
64
+ return this;
65
+ }
66
+ /**
67
+ * Get a loader fixture for a path
68
+ */
69
+ getLoader(path) {
70
+ if (path in this.loaders) {
71
+ return this.loaders[path];
72
+ }
73
+ for (const [pattern, handler] of Object.entries(this.loaders)) {
74
+ const fullPattern = pattern.startsWith("GET ") ? pattern : `GET ${pattern}`;
75
+ const result = (0, import_core.matchUrl)(fullPattern, "GET", path);
76
+ if (result) {
77
+ return handler;
78
+ }
79
+ }
80
+ return void 0;
81
+ }
82
+ /**
83
+ * Get an action fixture for a path and method
84
+ */
85
+ getAction(path, method) {
86
+ if (path in this.actions) {
87
+ return resolveActionHandler(this.actions[path], method);
88
+ }
89
+ for (const [pattern, fixture] of Object.entries(this.actions)) {
90
+ const fullPattern = /^(GET|POST|PUT|PATCH|DELETE)\s/.test(pattern) ? pattern : `${method} ${pattern}`;
91
+ const result = (0, import_core.matchUrl)(fullPattern, method, path);
92
+ if (result) {
93
+ return resolveActionHandler(fixture, method);
94
+ }
95
+ }
96
+ return void 0;
97
+ }
98
+ /**
99
+ * Find loader fixture with match info
100
+ */
101
+ findLoader(path) {
102
+ if (path in this.loaders) {
103
+ return {
104
+ handler: this.loaders[path],
105
+ match: { matched: true, params: {} }
106
+ };
107
+ }
108
+ for (const [pattern, handler] of Object.entries(this.loaders)) {
109
+ const fullPattern = pattern.startsWith("GET ") ? pattern : `GET ${pattern}`;
110
+ const result = (0, import_core.matchUrl)(fullPattern, "GET", path);
111
+ if (result) {
112
+ return { handler, match: result };
113
+ }
114
+ }
115
+ return null;
116
+ }
117
+ /**
118
+ * Find action fixture with match info
119
+ */
120
+ findAction(path, method) {
121
+ if (path in this.actions) {
122
+ const handler = resolveActionHandler(this.actions[path], method);
123
+ if (handler) {
124
+ return {
125
+ handler,
126
+ match: { matched: true, params: {} }
127
+ };
128
+ }
129
+ }
130
+ for (const [pattern, fixture] of Object.entries(this.actions)) {
131
+ const fullPattern = /^(GET|POST|PUT|PATCH|DELETE)\s/.test(pattern) ? pattern : `${method} ${pattern}`;
132
+ const result = (0, import_core.matchUrl)(fullPattern, method, path);
133
+ if (result) {
134
+ const handler = resolveActionHandler(fixture, method);
135
+ if (handler) {
136
+ return { handler, match: result };
137
+ }
138
+ }
139
+ }
140
+ return null;
141
+ }
142
+ /**
143
+ * Get all loader fixtures
144
+ */
145
+ getLoaders() {
146
+ return { ...this.loaders };
147
+ }
148
+ /**
149
+ * Get all action fixtures
150
+ */
151
+ getActions() {
152
+ return { ...this.actions };
153
+ }
154
+ /**
155
+ * Clear all fixtures
156
+ */
157
+ clear() {
158
+ this.loaders = {};
159
+ this.actions = {};
160
+ }
161
+ };
162
+ function resolveActionHandler(fixture, method) {
163
+ if (isMethodActionHandlers(fixture)) {
164
+ const upperMethod = method.toUpperCase();
165
+ return fixture[upperMethod];
166
+ }
167
+ return fixture;
168
+ }
169
+ function isMethodActionHandlers(fixture) {
170
+ if (typeof fixture !== "object" || fixture === null) {
171
+ return false;
172
+ }
173
+ const methods = ["POST", "PUT", "PATCH", "DELETE"];
174
+ return methods.some((method) => method in fixture);
175
+ }
176
+ function createFixtureStore(config) {
177
+ return new FixtureStore(config);
178
+ }
179
+ function defineRemixLoaderFixtures(fixtures) {
180
+ return fixtures;
181
+ }
182
+ function defineRemixActionFixtures(fixtures) {
183
+ return fixtures;
184
+ }
185
+ function defineRemixFixtures(config) {
186
+ return {
187
+ loaders: config.loaders ?? {},
188
+ actions: config.actions ?? {}
189
+ };
190
+ }
191
+
192
+ // src/server.ts
193
+ var DEFAULT_COOKIE_NAME = "demokit-demo-mode";
194
+ var DEFAULT_HEADER_NAME = "x-demokit-demo-mode";
195
+ var DEFAULT_ENV_VAR = "DEMOKIT_DEMO_MODE";
196
+ var DEFAULT_QUERY_PARAM = "demo";
197
+ function isDemoMode(request, options) {
198
+ const {
199
+ cookieName = DEFAULT_COOKIE_NAME,
200
+ headerName = DEFAULT_HEADER_NAME,
201
+ envVar = DEFAULT_ENV_VAR,
202
+ queryParam = DEFAULT_QUERY_PARAM
203
+ } = options ?? {};
204
+ const url = new URL(request.url);
205
+ const queryValue = url.searchParams.get(queryParam);
206
+ if (queryValue !== null) {
207
+ return queryValue === "true" || queryValue === "1" || queryValue === "";
208
+ }
209
+ const cookieHeader = request.headers.get("cookie");
210
+ if (cookieHeader) {
211
+ const cookies = parseCookies(cookieHeader);
212
+ const cookieValue = cookies[cookieName];
213
+ if (cookieValue !== void 0) {
214
+ return cookieValue === "true" || cookieValue === "1";
215
+ }
216
+ }
217
+ const headerValue = request.headers.get(headerName);
218
+ if (headerValue !== null) {
219
+ return headerValue === "true" || headerValue === "1";
220
+ }
221
+ if (typeof process !== "undefined" && process.env?.[envVar]) {
222
+ const envValue = process.env[envVar];
223
+ return envValue === "true" || envValue === "1";
224
+ }
225
+ return false;
226
+ }
227
+ function parseCookies(cookieHeader) {
228
+ const cookies = {};
229
+ for (const pair of cookieHeader.split(";")) {
230
+ const [name, ...rest] = pair.trim().split("=");
231
+ if (name) {
232
+ cookies[name] = rest.join("=");
233
+ }
234
+ }
235
+ return cookies;
236
+ }
237
+ function createDemoModeChecker(options) {
238
+ return (request) => isDemoMode(request, options);
239
+ }
240
+ var globalFixtureStore = null;
241
+ function setupDemoFixtures(config) {
242
+ globalFixtureStore = createFixtureStore(config);
243
+ }
244
+ function getFixtureStore() {
245
+ return globalFixtureStore;
246
+ }
247
+ async function getDemoData(pattern, context) {
248
+ const store = getFixtureStore();
249
+ if (!store) return void 0;
250
+ const handler = store.getLoader(pattern);
251
+ if (!handler) return void 0;
252
+ return executeHandler(handler, context);
253
+ }
254
+ async function getDemoActionResult(pattern, context, method) {
255
+ const store = getFixtureStore();
256
+ if (!store) return void 0;
257
+ const handler = store.getAction(pattern, method);
258
+ if (!handler) return void 0;
259
+ return executeHandler(handler, context);
260
+ }
261
+ async function executeHandler(handler, context) {
262
+ if (typeof handler === "function") {
263
+ return handler(context);
264
+ }
265
+ return handler;
266
+ }
267
+ function createDemoModeCookie(enabled, options) {
268
+ const {
269
+ cookieName = DEFAULT_COOKIE_NAME,
270
+ maxAge = 60 * 60 * 24 * 7,
271
+ // 7 days
272
+ path = "/",
273
+ sameSite = "Lax",
274
+ secure = process.env.NODE_ENV === "production"
275
+ } = options ?? {};
276
+ const parts = [
277
+ `${cookieName}=${enabled ? "true" : "false"}`,
278
+ `Max-Age=${maxAge}`,
279
+ `Path=${path}`,
280
+ `SameSite=${sameSite}`
281
+ ];
282
+ if (secure) {
283
+ parts.push("Secure");
284
+ }
285
+ return parts.join("; ");
286
+ }
287
+ function clearDemoModeCookie(options) {
288
+ const { cookieName = DEFAULT_COOKIE_NAME, path = "/" } = options ?? {};
289
+ return `${cookieName}=; Max-Age=0; Path=${path}`;
290
+ }
291
+ // Annotate the CommonJS export names for ESM import in node:
292
+ 0 && (module.exports = {
293
+ FixtureStore,
294
+ clearDemoModeCookie,
295
+ createDemoModeChecker,
296
+ createDemoModeCookie,
297
+ createFixtureStore,
298
+ defineRemixActionFixtures,
299
+ defineRemixFixtures,
300
+ defineRemixLoaderFixtures,
301
+ getDemoActionResult,
302
+ getDemoData,
303
+ getFixtureStore,
304
+ isDemoMode,
305
+ setupDemoFixtures
306
+ });
307
+ //# sourceMappingURL=server.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server.ts","../src/fixtures.ts"],"sourcesContent":["/**\n * @demokit-ai/remix/server\n *\n * Server-side utilities for Remix demo mode detection and fixture management.\n * Import from '@demokit-ai/remix/server' in your loader/action files.\n */\n\nimport type {\n DemoModeOptions,\n LoaderFixtureContext,\n ActionFixtureContext,\n LoaderFixtureHandler,\n ActionFixtureHandler,\n MethodActionHandlers,\n LoaderFixtureMapObject,\n ActionFixtureMapObject,\n} from './types'\nimport { FixtureStore, createFixtureStore, defineRemixFixtures } from './fixtures'\n\n/** Default demo mode cookie name */\nconst DEFAULT_COOKIE_NAME = 'demokit-demo-mode'\n\n/** Default demo mode header name */\nconst DEFAULT_HEADER_NAME = 'x-demokit-demo-mode'\n\n/** Default environment variable name */\nconst DEFAULT_ENV_VAR = 'DEMOKIT_DEMO_MODE'\n\n/** Default query parameter name */\nconst DEFAULT_QUERY_PARAM = 'demo'\n\n/**\n * Check if demo mode is enabled for a request\n *\n * Checks in order:\n * 1. Query parameter (for easy testing)\n * 2. Cookie (persistent across requests)\n * 3. Header (for API requests)\n * 4. Environment variable (global override)\n *\n * @example\n * import { isDemoMode } from '@demokit-ai/remix/server'\n *\n * export async function loader({ request }: LoaderFunctionArgs) {\n * if (isDemoMode(request)) {\n * return json({ user: { id: '1', name: 'Demo User' } })\n * }\n * return json({ user: await db.users.findFirst() })\n * }\n */\nexport function isDemoMode(request: Request, options?: DemoModeOptions): boolean {\n const {\n cookieName = DEFAULT_COOKIE_NAME,\n headerName = DEFAULT_HEADER_NAME,\n envVar = DEFAULT_ENV_VAR,\n queryParam = DEFAULT_QUERY_PARAM,\n } = options ?? {}\n\n // Check query parameter first (highest priority for easy testing)\n const url = new URL(request.url)\n const queryValue = url.searchParams.get(queryParam)\n if (queryValue !== null) {\n return queryValue === 'true' || queryValue === '1' || queryValue === ''\n }\n\n // Check cookie\n const cookieHeader = request.headers.get('cookie')\n if (cookieHeader) {\n const cookies = parseCookies(cookieHeader)\n const cookieValue = cookies[cookieName]\n if (cookieValue !== undefined) {\n return cookieValue === 'true' || cookieValue === '1'\n }\n }\n\n // Check header\n const headerValue = request.headers.get(headerName)\n if (headerValue !== null) {\n return headerValue === 'true' || headerValue === '1'\n }\n\n // Check environment variable (global fallback)\n if (typeof process !== 'undefined' && process.env?.[envVar]) {\n const envValue = process.env[envVar]\n return envValue === 'true' || envValue === '1'\n }\n\n return false\n}\n\n/**\n * Parse cookies from a cookie header string\n */\nfunction parseCookies(cookieHeader: string): Record<string, string> {\n const cookies: Record<string, string> = {}\n for (const pair of cookieHeader.split(';')) {\n const [name, ...rest] = pair.trim().split('=')\n if (name) {\n cookies[name] = rest.join('=')\n }\n }\n return cookies\n}\n\n/**\n * Create a demo mode checker function with preset options\n *\n * @example\n * import { createDemoModeChecker } from '@demokit-ai/remix/server'\n *\n * const isDemoMode = createDemoModeChecker({\n * cookieName: 'my-demo-cookie',\n * envVar: 'MY_DEMO_MODE',\n * })\n *\n * export async function loader({ request }: LoaderFunctionArgs) {\n * if (isDemoMode(request)) {\n * return json(demoData)\n * }\n * return json(await fetchRealData())\n * }\n */\nexport function createDemoModeChecker(\n options?: DemoModeOptions\n): (request: Request) => boolean {\n return (request: Request) => isDemoMode(request, options)\n}\n\n// Global fixture store instance (for simple usage)\nlet globalFixtureStore: FixtureStore | null = null\n\n/**\n * Set up the global fixture store\n *\n * Call this once at app startup to configure demo fixtures.\n *\n * @example\n * // In your entry.server.tsx or root loader\n * import { setupDemoFixtures } from '@demokit-ai/remix/server'\n *\n * setupDemoFixtures({\n * loaders: {\n * '/users': [{ id: '1', name: 'Demo User' }],\n * '/users/:id': ({ params }) => ({ id: params.id, name: 'Demo User' }),\n * },\n * actions: {\n * '/users': ({ formData }) => ({ created: true }),\n * },\n * })\n */\nexport function setupDemoFixtures(config: {\n loaders?: LoaderFixtureMapObject\n actions?: ActionFixtureMapObject\n}): void {\n globalFixtureStore = createFixtureStore(config)\n}\n\n/**\n * Get the global fixture store\n */\nexport function getFixtureStore(): FixtureStore | null {\n return globalFixtureStore\n}\n\n/**\n * Get demo data for a loader path\n *\n * Returns the fixture data if available, otherwise undefined.\n * Use with isDemoMode() for conditional demo data.\n *\n * @example\n * import { isDemoMode, getDemoData } from '@demokit-ai/remix/server'\n *\n * export async function loader({ request, params }: LoaderFunctionArgs) {\n * if (isDemoMode(request)) {\n * const demoData = await getDemoData('/users/:id', {\n * params,\n * request,\n * path: '/users/' + params.id,\n * })\n * if (demoData) return json(demoData)\n * }\n * return json(await db.users.findFirst({ where: { id: params.id } }))\n * }\n */\nexport async function getDemoData<T = unknown>(\n pattern: string,\n context: LoaderFixtureContext\n): Promise<T | undefined> {\n const store = getFixtureStore()\n if (!store) return undefined\n\n const handler = store.getLoader(pattern)\n if (!handler) return undefined\n\n return executeHandler(handler, context) as Promise<T>\n}\n\n/**\n * Get demo action result for an action path\n *\n * @example\n * import { isDemoMode, getDemoActionResult } from '@demokit-ai/remix/server'\n *\n * export async function action({ request, params }: ActionFunctionArgs) {\n * if (isDemoMode(request)) {\n * const formData = await request.clone().formData()\n * const demoResult = await getDemoActionResult('/users/:id', {\n * params,\n * request,\n * path: '/users/' + params.id,\n * formData,\n * }, request.method)\n * if (demoResult) return json(demoResult)\n * }\n * // Real action...\n * }\n */\nexport async function getDemoActionResult<T = unknown>(\n pattern: string,\n context: ActionFixtureContext,\n method: string\n): Promise<T | undefined> {\n const store = getFixtureStore()\n if (!store) return undefined\n\n const handler = store.getAction(pattern, method)\n if (!handler) return undefined\n\n return executeHandler(handler, context) as Promise<T>\n}\n\n/**\n * Execute a fixture handler\n */\nasync function executeHandler<T, C extends LoaderFixtureContext | ActionFixtureContext>(\n handler: T | ((context: C) => T | Promise<T>),\n context: C\n): Promise<T> {\n if (typeof handler === 'function') {\n return (handler as (context: C) => T | Promise<T>)(context)\n }\n return handler\n}\n\n/**\n * Create a cookie value to enable demo mode\n *\n * @example\n * import { createDemoModeCookie } from '@demokit-ai/remix/server'\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * return redirect('/', {\n * headers: {\n * 'Set-Cookie': createDemoModeCookie(true),\n * },\n * })\n * }\n */\nexport function createDemoModeCookie(\n enabled: boolean,\n options?: {\n cookieName?: string\n maxAge?: number\n path?: string\n sameSite?: 'Strict' | 'Lax' | 'None'\n secure?: boolean\n }\n): string {\n const {\n cookieName = DEFAULT_COOKIE_NAME,\n maxAge = 60 * 60 * 24 * 7, // 7 days\n path = '/',\n sameSite = 'Lax',\n secure = process.env.NODE_ENV === 'production',\n } = options ?? {}\n\n const parts = [\n `${cookieName}=${enabled ? 'true' : 'false'}`,\n `Max-Age=${maxAge}`,\n `Path=${path}`,\n `SameSite=${sameSite}`,\n ]\n\n if (secure) {\n parts.push('Secure')\n }\n\n return parts.join('; ')\n}\n\n/**\n * Create a cookie value to clear demo mode\n *\n * @example\n * import { clearDemoModeCookie } from '@demokit-ai/remix/server'\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * return redirect('/', {\n * headers: {\n * 'Set-Cookie': clearDemoModeCookie(),\n * },\n * })\n * }\n */\nexport function clearDemoModeCookie(options?: { cookieName?: string; path?: string }): string {\n const { cookieName = DEFAULT_COOKIE_NAME, path = '/' } = options ?? {}\n return `${cookieName}=; Max-Age=0; Path=${path}`\n}\n\n// Re-export fixture utilities for convenience\nexport { FixtureStore, createFixtureStore, defineRemixFixtures }\nexport {\n defineRemixLoaderFixtures,\n defineRemixActionFixtures,\n} from './fixtures'\n\n// Re-export types\nexport type {\n DemoModeOptions,\n LoaderFixtureContext,\n ActionFixtureContext,\n LoaderFixtureHandler,\n ActionFixtureHandler,\n MethodActionHandlers,\n LoaderFixtureMapObject,\n ActionFixtureMapObject,\n}\n","import { matchUrl, type MatchResult } from '@demokit-ai/core'\nimport type {\n LoaderFixtureMapObject,\n ActionFixtureMapObject,\n LoaderFixtureHandler,\n ActionFixtureHandler,\n MethodActionHandlers,\n FixtureStoreConfig,\n} from './types'\n\n/**\n * Server-side fixture store for managing demo data\n *\n * Use this to centralize fixture definitions for your Remix app.\n * Works with the demo mode detection utilities.\n */\nexport class FixtureStore {\n private loaders: LoaderFixtureMapObject = {}\n private actions: ActionFixtureMapObject = {}\n\n constructor(config?: FixtureStoreConfig) {\n if (config?.loaders) {\n this.loaders = { ...config.loaders }\n }\n if (config?.actions) {\n this.actions = { ...config.actions }\n }\n }\n\n /**\n * Register a loader fixture\n */\n setLoader(path: string, handler: LoaderFixtureHandler): this {\n this.loaders[path] = handler\n return this\n }\n\n /**\n * Register an action fixture\n */\n setAction(path: string, handler: ActionFixtureHandler | MethodActionHandlers): this {\n this.actions[path] = handler\n return this\n }\n\n /**\n * Get a loader fixture for a path\n */\n getLoader(path: string): LoaderFixtureHandler | undefined {\n // Try exact match first\n if (path in this.loaders) {\n return this.loaders[path]\n }\n\n // Try pattern matching\n for (const [pattern, handler] of Object.entries(this.loaders)) {\n const fullPattern = pattern.startsWith('GET ') ? pattern : `GET ${pattern}`\n const result = matchUrl(fullPattern, 'GET', path)\n if (result) {\n return handler\n }\n }\n\n return undefined\n }\n\n /**\n * Get an action fixture for a path and method\n */\n getAction(path: string, method: string): ActionFixtureHandler | undefined {\n // Try exact match first\n if (path in this.actions) {\n return resolveActionHandler(this.actions[path], method)\n }\n\n // Try pattern matching\n for (const [pattern, fixture] of Object.entries(this.actions)) {\n const fullPattern = /^(GET|POST|PUT|PATCH|DELETE)\\s/.test(pattern)\n ? pattern\n : `${method} ${pattern}`\n const result = matchUrl(fullPattern, method, path)\n if (result) {\n return resolveActionHandler(fixture, method)\n }\n }\n\n return undefined\n }\n\n /**\n * Find loader fixture with match info\n */\n findLoader(path: string): { handler: LoaderFixtureHandler; match: MatchResult } | null {\n // Try exact match first\n if (path in this.loaders) {\n return {\n handler: this.loaders[path],\n match: { matched: true, params: {} },\n }\n }\n\n // Try pattern matching\n for (const [pattern, handler] of Object.entries(this.loaders)) {\n const fullPattern = pattern.startsWith('GET ') ? pattern : `GET ${pattern}`\n const result = matchUrl(fullPattern, 'GET', path)\n if (result) {\n return { handler, match: result }\n }\n }\n\n return null\n }\n\n /**\n * Find action fixture with match info\n */\n findAction(\n path: string,\n method: string\n ): { handler: ActionFixtureHandler; match: MatchResult } | null {\n // Try exact match first\n if (path in this.actions) {\n const handler = resolveActionHandler(this.actions[path], method)\n if (handler) {\n return {\n handler,\n match: { matched: true, params: {} },\n }\n }\n }\n\n // Try pattern matching\n for (const [pattern, fixture] of Object.entries(this.actions)) {\n const fullPattern = /^(GET|POST|PUT|PATCH|DELETE)\\s/.test(pattern)\n ? pattern\n : `${method} ${pattern}`\n const result = matchUrl(fullPattern, method, path)\n if (result) {\n const handler = resolveActionHandler(fixture, method)\n if (handler) {\n return { handler, match: result }\n }\n }\n }\n\n return null\n }\n\n /**\n * Get all loader fixtures\n */\n getLoaders(): LoaderFixtureMapObject {\n return { ...this.loaders }\n }\n\n /**\n * Get all action fixtures\n */\n getActions(): ActionFixtureMapObject {\n return { ...this.actions }\n }\n\n /**\n * Clear all fixtures\n */\n clear(): void {\n this.loaders = {}\n this.actions = {}\n }\n}\n\n/**\n * Resolve action handler based on method\n */\nfunction resolveActionHandler(\n fixture: ActionFixtureHandler | MethodActionHandlers,\n method: string\n): ActionFixtureHandler | undefined {\n if (isMethodActionHandlers(fixture)) {\n const upperMethod = method.toUpperCase() as keyof MethodActionHandlers\n return fixture[upperMethod]\n }\n return fixture\n}\n\n/**\n * Type guard for method action handlers\n */\nfunction isMethodActionHandlers(fixture: unknown): fixture is MethodActionHandlers {\n if (typeof fixture !== 'object' || fixture === null) {\n return false\n }\n const methods = ['POST', 'PUT', 'PATCH', 'DELETE']\n return methods.some((method) => method in fixture)\n}\n\n/**\n * Create a fixture store with initial fixtures\n *\n * @example\n * import { createFixtureStore } from '@demokit-ai/remix/server'\n *\n * export const fixtures = createFixtureStore({\n * loaders: {\n * '/users': [{ id: '1', name: 'Demo User' }],\n * '/users/:id': ({ params }) => ({ id: params.id, name: 'Demo User' }),\n * },\n * actions: {\n * '/users': {\n * POST: ({ formData }) => ({ id: crypto.randomUUID(), name: formData?.get('name') }),\n * },\n * '/users/:id': {\n * PUT: ({ formData }) => ({ updated: true }),\n * DELETE: ({ params }) => ({ deleted: true, id: params.id }),\n * },\n * },\n * })\n */\nexport function createFixtureStore(config?: FixtureStoreConfig): FixtureStore {\n return new FixtureStore(config)\n}\n\n/**\n * Define loader fixtures with type inference\n *\n * @example\n * const loaders = defineRemixLoaderFixtures({\n * '/users': [{ id: '1', name: 'Demo User' }],\n * '/users/:id': ({ params }) => ({ id: params.id, name: 'Demo User' }),\n * '/projects': async () => fetchDemoProjects(),\n * })\n */\nexport function defineRemixLoaderFixtures<T extends LoaderFixtureMapObject>(fixtures: T): T {\n return fixtures\n}\n\n/**\n * Define action fixtures with type inference\n *\n * @example\n * const actions = defineRemixActionFixtures({\n * '/users': {\n * POST: ({ formData }) => ({ id: crypto.randomUUID(), name: formData?.get('name') }),\n * },\n * '/users/:id': {\n * PUT: ({ formData }) => ({ updated: true }),\n * DELETE: ({ params }) => ({ deleted: true, id: params.id }),\n * },\n * })\n */\nexport function defineRemixActionFixtures<T extends ActionFixtureMapObject>(fixtures: T): T {\n return fixtures\n}\n\n/**\n * Define complete Remix fixtures (loaders + actions)\n *\n * @example\n * const { loaders, actions } = defineRemixFixtures({\n * loaders: {\n * '/users': [{ id: '1', name: 'Demo User' }],\n * },\n * actions: {\n * '/users': ({ formData }) => ({ created: true }),\n * },\n * })\n */\nexport function defineRemixFixtures<\n L extends LoaderFixtureMapObject,\n A extends ActionFixtureMapObject,\n>(config: { loaders?: L; actions?: A }): { loaders: L; actions: A } {\n return {\n loaders: (config.loaders ?? {}) as L,\n actions: (config.actions ?? {}) as A,\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,162 @@
1
+ import { o as DemoModeOptions, j as LoaderFixtureMapObject, l as ActionFixtureMapObject, F as FixtureStore, g as LoaderFixtureContext, h as ActionFixtureContext } from './fixtures-BP3SzTkj.cjs';
2
+ export { A as ActionFixtureHandler, L as LoaderFixtureHandler, M as MethodActionHandlers, c as createFixtureStore, e as defineRemixActionFixtures, f as defineRemixFixtures, d as defineRemixLoaderFixtures } from './fixtures-BP3SzTkj.cjs';
3
+ import '@demokit-ai/core';
4
+ import '@remix-run/node';
5
+
6
+ /**
7
+ * @demokit-ai/remix/server
8
+ *
9
+ * Server-side utilities for Remix demo mode detection and fixture management.
10
+ * Import from '@demokit-ai/remix/server' in your loader/action files.
11
+ */
12
+
13
+ /**
14
+ * Check if demo mode is enabled for a request
15
+ *
16
+ * Checks in order:
17
+ * 1. Query parameter (for easy testing)
18
+ * 2. Cookie (persistent across requests)
19
+ * 3. Header (for API requests)
20
+ * 4. Environment variable (global override)
21
+ *
22
+ * @example
23
+ * import { isDemoMode } from '@demokit-ai/remix/server'
24
+ *
25
+ * export async function loader({ request }: LoaderFunctionArgs) {
26
+ * if (isDemoMode(request)) {
27
+ * return json({ user: { id: '1', name: 'Demo User' } })
28
+ * }
29
+ * return json({ user: await db.users.findFirst() })
30
+ * }
31
+ */
32
+ declare function isDemoMode(request: Request, options?: DemoModeOptions): boolean;
33
+ /**
34
+ * Create a demo mode checker function with preset options
35
+ *
36
+ * @example
37
+ * import { createDemoModeChecker } from '@demokit-ai/remix/server'
38
+ *
39
+ * const isDemoMode = createDemoModeChecker({
40
+ * cookieName: 'my-demo-cookie',
41
+ * envVar: 'MY_DEMO_MODE',
42
+ * })
43
+ *
44
+ * export async function loader({ request }: LoaderFunctionArgs) {
45
+ * if (isDemoMode(request)) {
46
+ * return json(demoData)
47
+ * }
48
+ * return json(await fetchRealData())
49
+ * }
50
+ */
51
+ declare function createDemoModeChecker(options?: DemoModeOptions): (request: Request) => boolean;
52
+ /**
53
+ * Set up the global fixture store
54
+ *
55
+ * Call this once at app startup to configure demo fixtures.
56
+ *
57
+ * @example
58
+ * // In your entry.server.tsx or root loader
59
+ * import { setupDemoFixtures } from '@demokit-ai/remix/server'
60
+ *
61
+ * setupDemoFixtures({
62
+ * loaders: {
63
+ * '/users': [{ id: '1', name: 'Demo User' }],
64
+ * '/users/:id': ({ params }) => ({ id: params.id, name: 'Demo User' }),
65
+ * },
66
+ * actions: {
67
+ * '/users': ({ formData }) => ({ created: true }),
68
+ * },
69
+ * })
70
+ */
71
+ declare function setupDemoFixtures(config: {
72
+ loaders?: LoaderFixtureMapObject;
73
+ actions?: ActionFixtureMapObject;
74
+ }): void;
75
+ /**
76
+ * Get the global fixture store
77
+ */
78
+ declare function getFixtureStore(): FixtureStore | null;
79
+ /**
80
+ * Get demo data for a loader path
81
+ *
82
+ * Returns the fixture data if available, otherwise undefined.
83
+ * Use with isDemoMode() for conditional demo data.
84
+ *
85
+ * @example
86
+ * import { isDemoMode, getDemoData } from '@demokit-ai/remix/server'
87
+ *
88
+ * export async function loader({ request, params }: LoaderFunctionArgs) {
89
+ * if (isDemoMode(request)) {
90
+ * const demoData = await getDemoData('/users/:id', {
91
+ * params,
92
+ * request,
93
+ * path: '/users/' + params.id,
94
+ * })
95
+ * if (demoData) return json(demoData)
96
+ * }
97
+ * return json(await db.users.findFirst({ where: { id: params.id } }))
98
+ * }
99
+ */
100
+ declare function getDemoData<T = unknown>(pattern: string, context: LoaderFixtureContext): Promise<T | undefined>;
101
+ /**
102
+ * Get demo action result for an action path
103
+ *
104
+ * @example
105
+ * import { isDemoMode, getDemoActionResult } from '@demokit-ai/remix/server'
106
+ *
107
+ * export async function action({ request, params }: ActionFunctionArgs) {
108
+ * if (isDemoMode(request)) {
109
+ * const formData = await request.clone().formData()
110
+ * const demoResult = await getDemoActionResult('/users/:id', {
111
+ * params,
112
+ * request,
113
+ * path: '/users/' + params.id,
114
+ * formData,
115
+ * }, request.method)
116
+ * if (demoResult) return json(demoResult)
117
+ * }
118
+ * // Real action...
119
+ * }
120
+ */
121
+ declare function getDemoActionResult<T = unknown>(pattern: string, context: ActionFixtureContext, method: string): Promise<T | undefined>;
122
+ /**
123
+ * Create a cookie value to enable demo mode
124
+ *
125
+ * @example
126
+ * import { createDemoModeCookie } from '@demokit-ai/remix/server'
127
+ *
128
+ * export async function action({ request }: ActionFunctionArgs) {
129
+ * return redirect('/', {
130
+ * headers: {
131
+ * 'Set-Cookie': createDemoModeCookie(true),
132
+ * },
133
+ * })
134
+ * }
135
+ */
136
+ declare function createDemoModeCookie(enabled: boolean, options?: {
137
+ cookieName?: string;
138
+ maxAge?: number;
139
+ path?: string;
140
+ sameSite?: 'Strict' | 'Lax' | 'None';
141
+ secure?: boolean;
142
+ }): string;
143
+ /**
144
+ * Create a cookie value to clear demo mode
145
+ *
146
+ * @example
147
+ * import { clearDemoModeCookie } from '@demokit-ai/remix/server'
148
+ *
149
+ * export async function action({ request }: ActionFunctionArgs) {
150
+ * return redirect('/', {
151
+ * headers: {
152
+ * 'Set-Cookie': clearDemoModeCookie(),
153
+ * },
154
+ * })
155
+ * }
156
+ */
157
+ declare function clearDemoModeCookie(options?: {
158
+ cookieName?: string;
159
+ path?: string;
160
+ }): string;
161
+
162
+ export { ActionFixtureContext, ActionFixtureMapObject, DemoModeOptions, FixtureStore, LoaderFixtureContext, LoaderFixtureMapObject, clearDemoModeCookie, createDemoModeChecker, createDemoModeCookie, getDemoActionResult, getDemoData, getFixtureStore, isDemoMode, setupDemoFixtures };
@@ -0,0 +1,162 @@
1
+ import { o as DemoModeOptions, j as LoaderFixtureMapObject, l as ActionFixtureMapObject, F as FixtureStore, g as LoaderFixtureContext, h as ActionFixtureContext } from './fixtures-BP3SzTkj.js';
2
+ export { A as ActionFixtureHandler, L as LoaderFixtureHandler, M as MethodActionHandlers, c as createFixtureStore, e as defineRemixActionFixtures, f as defineRemixFixtures, d as defineRemixLoaderFixtures } from './fixtures-BP3SzTkj.js';
3
+ import '@demokit-ai/core';
4
+ import '@remix-run/node';
5
+
6
+ /**
7
+ * @demokit-ai/remix/server
8
+ *
9
+ * Server-side utilities for Remix demo mode detection and fixture management.
10
+ * Import from '@demokit-ai/remix/server' in your loader/action files.
11
+ */
12
+
13
+ /**
14
+ * Check if demo mode is enabled for a request
15
+ *
16
+ * Checks in order:
17
+ * 1. Query parameter (for easy testing)
18
+ * 2. Cookie (persistent across requests)
19
+ * 3. Header (for API requests)
20
+ * 4. Environment variable (global override)
21
+ *
22
+ * @example
23
+ * import { isDemoMode } from '@demokit-ai/remix/server'
24
+ *
25
+ * export async function loader({ request }: LoaderFunctionArgs) {
26
+ * if (isDemoMode(request)) {
27
+ * return json({ user: { id: '1', name: 'Demo User' } })
28
+ * }
29
+ * return json({ user: await db.users.findFirst() })
30
+ * }
31
+ */
32
+ declare function isDemoMode(request: Request, options?: DemoModeOptions): boolean;
33
+ /**
34
+ * Create a demo mode checker function with preset options
35
+ *
36
+ * @example
37
+ * import { createDemoModeChecker } from '@demokit-ai/remix/server'
38
+ *
39
+ * const isDemoMode = createDemoModeChecker({
40
+ * cookieName: 'my-demo-cookie',
41
+ * envVar: 'MY_DEMO_MODE',
42
+ * })
43
+ *
44
+ * export async function loader({ request }: LoaderFunctionArgs) {
45
+ * if (isDemoMode(request)) {
46
+ * return json(demoData)
47
+ * }
48
+ * return json(await fetchRealData())
49
+ * }
50
+ */
51
+ declare function createDemoModeChecker(options?: DemoModeOptions): (request: Request) => boolean;
52
+ /**
53
+ * Set up the global fixture store
54
+ *
55
+ * Call this once at app startup to configure demo fixtures.
56
+ *
57
+ * @example
58
+ * // In your entry.server.tsx or root loader
59
+ * import { setupDemoFixtures } from '@demokit-ai/remix/server'
60
+ *
61
+ * setupDemoFixtures({
62
+ * loaders: {
63
+ * '/users': [{ id: '1', name: 'Demo User' }],
64
+ * '/users/:id': ({ params }) => ({ id: params.id, name: 'Demo User' }),
65
+ * },
66
+ * actions: {
67
+ * '/users': ({ formData }) => ({ created: true }),
68
+ * },
69
+ * })
70
+ */
71
+ declare function setupDemoFixtures(config: {
72
+ loaders?: LoaderFixtureMapObject;
73
+ actions?: ActionFixtureMapObject;
74
+ }): void;
75
+ /**
76
+ * Get the global fixture store
77
+ */
78
+ declare function getFixtureStore(): FixtureStore | null;
79
+ /**
80
+ * Get demo data for a loader path
81
+ *
82
+ * Returns the fixture data if available, otherwise undefined.
83
+ * Use with isDemoMode() for conditional demo data.
84
+ *
85
+ * @example
86
+ * import { isDemoMode, getDemoData } from '@demokit-ai/remix/server'
87
+ *
88
+ * export async function loader({ request, params }: LoaderFunctionArgs) {
89
+ * if (isDemoMode(request)) {
90
+ * const demoData = await getDemoData('/users/:id', {
91
+ * params,
92
+ * request,
93
+ * path: '/users/' + params.id,
94
+ * })
95
+ * if (demoData) return json(demoData)
96
+ * }
97
+ * return json(await db.users.findFirst({ where: { id: params.id } }))
98
+ * }
99
+ */
100
+ declare function getDemoData<T = unknown>(pattern: string, context: LoaderFixtureContext): Promise<T | undefined>;
101
+ /**
102
+ * Get demo action result for an action path
103
+ *
104
+ * @example
105
+ * import { isDemoMode, getDemoActionResult } from '@demokit-ai/remix/server'
106
+ *
107
+ * export async function action({ request, params }: ActionFunctionArgs) {
108
+ * if (isDemoMode(request)) {
109
+ * const formData = await request.clone().formData()
110
+ * const demoResult = await getDemoActionResult('/users/:id', {
111
+ * params,
112
+ * request,
113
+ * path: '/users/' + params.id,
114
+ * formData,
115
+ * }, request.method)
116
+ * if (demoResult) return json(demoResult)
117
+ * }
118
+ * // Real action...
119
+ * }
120
+ */
121
+ declare function getDemoActionResult<T = unknown>(pattern: string, context: ActionFixtureContext, method: string): Promise<T | undefined>;
122
+ /**
123
+ * Create a cookie value to enable demo mode
124
+ *
125
+ * @example
126
+ * import { createDemoModeCookie } from '@demokit-ai/remix/server'
127
+ *
128
+ * export async function action({ request }: ActionFunctionArgs) {
129
+ * return redirect('/', {
130
+ * headers: {
131
+ * 'Set-Cookie': createDemoModeCookie(true),
132
+ * },
133
+ * })
134
+ * }
135
+ */
136
+ declare function createDemoModeCookie(enabled: boolean, options?: {
137
+ cookieName?: string;
138
+ maxAge?: number;
139
+ path?: string;
140
+ sameSite?: 'Strict' | 'Lax' | 'None';
141
+ secure?: boolean;
142
+ }): string;
143
+ /**
144
+ * Create a cookie value to clear demo mode
145
+ *
146
+ * @example
147
+ * import { clearDemoModeCookie } from '@demokit-ai/remix/server'
148
+ *
149
+ * export async function action({ request }: ActionFunctionArgs) {
150
+ * return redirect('/', {
151
+ * headers: {
152
+ * 'Set-Cookie': clearDemoModeCookie(),
153
+ * },
154
+ * })
155
+ * }
156
+ */
157
+ declare function clearDemoModeCookie(options?: {
158
+ cookieName?: string;
159
+ path?: string;
160
+ }): string;
161
+
162
+ export { ActionFixtureContext, ActionFixtureMapObject, DemoModeOptions, FixtureStore, LoaderFixtureContext, LoaderFixtureMapObject, clearDemoModeCookie, createDemoModeChecker, createDemoModeCookie, getDemoActionResult, getDemoData, getFixtureStore, isDemoMode, setupDemoFixtures };