@demokit-ai/core 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/index.cjs +535 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +565 -0
- package/dist/index.d.ts +565 -0
- package/dist/index.js +520 -0
- package/dist/index.js.map +1 -0
- package/package.json +50 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
// src/matcher.ts
|
|
2
|
+
var patternCache = /* @__PURE__ */ new Map();
|
|
3
|
+
function parseUrlPattern(pattern) {
|
|
4
|
+
const cached = patternCache.get(pattern);
|
|
5
|
+
if (cached) {
|
|
6
|
+
return cached;
|
|
7
|
+
}
|
|
8
|
+
const spaceIndex = pattern.indexOf(" ");
|
|
9
|
+
if (spaceIndex === -1) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
`Invalid pattern "${pattern}": must be in format "METHOD /path". Example: "GET /api/users/:id"`
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
const method = pattern.slice(0, spaceIndex).toUpperCase();
|
|
15
|
+
const path = pattern.slice(spaceIndex + 1);
|
|
16
|
+
if (!path.startsWith("/")) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
`Invalid pattern "${pattern}": path must start with "/". Example: "GET /api/users"`
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
const paramNames = [];
|
|
22
|
+
let regexStr = path.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, (_, name) => {
|
|
23
|
+
paramNames.push(name);
|
|
24
|
+
return "([^/]+)";
|
|
25
|
+
}).replace(/\*/g, ".*");
|
|
26
|
+
const parsed = {
|
|
27
|
+
method,
|
|
28
|
+
pathPattern: new RegExp(`^${regexStr}$`),
|
|
29
|
+
paramNames
|
|
30
|
+
};
|
|
31
|
+
patternCache.set(pattern, parsed);
|
|
32
|
+
return parsed;
|
|
33
|
+
}
|
|
34
|
+
function matchUrl(pattern, method, pathname) {
|
|
35
|
+
const { method: patternMethod, pathPattern, paramNames } = parseUrlPattern(pattern);
|
|
36
|
+
if (patternMethod !== method.toUpperCase()) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const match = pathname.match(pathPattern);
|
|
40
|
+
if (!match) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const params = {};
|
|
44
|
+
paramNames.forEach((name, index) => {
|
|
45
|
+
const value = match[index + 1];
|
|
46
|
+
if (value !== void 0) {
|
|
47
|
+
params[name] = decodeURIComponent(value);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return { matched: true, params };
|
|
51
|
+
}
|
|
52
|
+
function findMatchingPattern(fixtures, method, pathname) {
|
|
53
|
+
for (const pattern of Object.keys(fixtures)) {
|
|
54
|
+
const result = matchUrl(pattern, method, pathname);
|
|
55
|
+
if (result) {
|
|
56
|
+
return [pattern, result];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
function clearPatternCache() {
|
|
62
|
+
patternCache.clear();
|
|
63
|
+
}
|
|
64
|
+
function deepEqual(a, b) {
|
|
65
|
+
if (a === b) return true;
|
|
66
|
+
if (a === null || b === null) return a === b;
|
|
67
|
+
if (typeof a !== typeof b) return false;
|
|
68
|
+
if (typeof a === "object" && typeof b === "object") {
|
|
69
|
+
const aObj = a;
|
|
70
|
+
const bObj = b;
|
|
71
|
+
const aKeys = Object.keys(aObj);
|
|
72
|
+
const bKeys = Object.keys(bObj);
|
|
73
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
74
|
+
return aKeys.every((key) => deepEqual(aObj[key], bObj[key]));
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
function isParamPlaceholder(value) {
|
|
79
|
+
return typeof value === "string" && value.startsWith(":") && value.length > 1;
|
|
80
|
+
}
|
|
81
|
+
function getParamName(placeholder) {
|
|
82
|
+
return placeholder.slice(1);
|
|
83
|
+
}
|
|
84
|
+
function matchObjectWithParams(value, pattern) {
|
|
85
|
+
const params = {};
|
|
86
|
+
const patternKeys = Object.keys(pattern);
|
|
87
|
+
const valueKeys = Object.keys(value);
|
|
88
|
+
if (patternKeys.length > valueKeys.length) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
for (const key of patternKeys) {
|
|
92
|
+
if (!(key in value)) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const patternVal = pattern[key];
|
|
96
|
+
const actualVal = value[key];
|
|
97
|
+
if (isParamPlaceholder(patternVal)) {
|
|
98
|
+
params[getParamName(patternVal)] = actualVal;
|
|
99
|
+
} else if (typeof patternVal === "object" && patternVal !== null && typeof actualVal === "object" && actualVal !== null) {
|
|
100
|
+
const nestedResult = matchObjectWithParams(
|
|
101
|
+
actualVal,
|
|
102
|
+
patternVal
|
|
103
|
+
);
|
|
104
|
+
if (!nestedResult) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
Object.assign(params, nestedResult.params);
|
|
108
|
+
} else if (!deepEqual(patternVal, actualVal)) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return { matched: true, params };
|
|
113
|
+
}
|
|
114
|
+
function matchQueryKey(queryKey, pattern) {
|
|
115
|
+
if (queryKey.length !== pattern.length) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const params = {};
|
|
119
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
120
|
+
const patternElement = pattern[i];
|
|
121
|
+
const keyElement = queryKey[i];
|
|
122
|
+
if (patternElement === "*") {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (isParamPlaceholder(patternElement)) {
|
|
126
|
+
params[getParamName(patternElement)] = keyElement;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (typeof patternElement === "object" && patternElement !== null && typeof keyElement === "object" && keyElement !== null) {
|
|
130
|
+
const objectResult = matchObjectWithParams(
|
|
131
|
+
keyElement,
|
|
132
|
+
patternElement
|
|
133
|
+
);
|
|
134
|
+
if (!objectResult) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
Object.assign(params, objectResult.params);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (patternElement !== keyElement) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return { matched: true, params };
|
|
145
|
+
}
|
|
146
|
+
function findMatchingQueryKeyPattern(patterns, queryKey) {
|
|
147
|
+
for (const [pattern, value] of patterns) {
|
|
148
|
+
const result = matchQueryKey(queryKey, pattern);
|
|
149
|
+
if (result) {
|
|
150
|
+
return [pattern, value, result];
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/storage.ts
|
|
157
|
+
var DEFAULT_STORAGE_KEY = "demokit-mode";
|
|
158
|
+
function isLocalStorageAvailable() {
|
|
159
|
+
try {
|
|
160
|
+
const testKey = "__demokit_test__";
|
|
161
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
window.localStorage.setItem(testKey, testKey);
|
|
165
|
+
window.localStorage.removeItem(testKey);
|
|
166
|
+
return true;
|
|
167
|
+
} catch {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function loadDemoState(key = DEFAULT_STORAGE_KEY) {
|
|
172
|
+
if (!isLocalStorageAvailable()) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
const value = window.localStorage.getItem(key);
|
|
177
|
+
return value === "true";
|
|
178
|
+
} catch {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function saveDemoState(key = DEFAULT_STORAGE_KEY, enabled) {
|
|
183
|
+
if (!isLocalStorageAvailable()) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
if (enabled) {
|
|
188
|
+
window.localStorage.setItem(key, "true");
|
|
189
|
+
} else {
|
|
190
|
+
window.localStorage.removeItem(key);
|
|
191
|
+
}
|
|
192
|
+
} catch {
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function clearDemoState(key = DEFAULT_STORAGE_KEY) {
|
|
196
|
+
if (!isLocalStorageAvailable()) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
try {
|
|
200
|
+
window.localStorage.removeItem(key);
|
|
201
|
+
} catch {
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// src/session.ts
|
|
206
|
+
function createSessionState() {
|
|
207
|
+
const store = /* @__PURE__ */ new Map();
|
|
208
|
+
return {
|
|
209
|
+
get(key) {
|
|
210
|
+
return store.get(key);
|
|
211
|
+
},
|
|
212
|
+
set(key, value) {
|
|
213
|
+
store.set(key, value);
|
|
214
|
+
},
|
|
215
|
+
delete(key) {
|
|
216
|
+
store.delete(key);
|
|
217
|
+
},
|
|
218
|
+
clear() {
|
|
219
|
+
store.clear();
|
|
220
|
+
},
|
|
221
|
+
keys() {
|
|
222
|
+
return Array.from(store.keys());
|
|
223
|
+
},
|
|
224
|
+
has(key) {
|
|
225
|
+
return store.has(key);
|
|
226
|
+
},
|
|
227
|
+
size() {
|
|
228
|
+
return store.size;
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// src/interceptor.ts
|
|
234
|
+
async function parseRequestBody(body, headers) {
|
|
235
|
+
if (!body) {
|
|
236
|
+
return void 0;
|
|
237
|
+
}
|
|
238
|
+
const contentType = headers.get("content-type") || "";
|
|
239
|
+
try {
|
|
240
|
+
if (typeof body === "string") {
|
|
241
|
+
if (contentType.includes("application/json")) {
|
|
242
|
+
return JSON.parse(body);
|
|
243
|
+
}
|
|
244
|
+
return body;
|
|
245
|
+
}
|
|
246
|
+
if (body instanceof FormData) {
|
|
247
|
+
const obj = {};
|
|
248
|
+
body.forEach((value, key) => {
|
|
249
|
+
obj[key] = value;
|
|
250
|
+
});
|
|
251
|
+
return obj;
|
|
252
|
+
}
|
|
253
|
+
if (body instanceof URLSearchParams) {
|
|
254
|
+
const obj = {};
|
|
255
|
+
body.forEach((value, key) => {
|
|
256
|
+
obj[key] = value;
|
|
257
|
+
});
|
|
258
|
+
return obj;
|
|
259
|
+
}
|
|
260
|
+
if (body instanceof Blob) {
|
|
261
|
+
const text = await body.text();
|
|
262
|
+
if (contentType.includes("application/json")) {
|
|
263
|
+
return JSON.parse(text);
|
|
264
|
+
}
|
|
265
|
+
return text;
|
|
266
|
+
}
|
|
267
|
+
if (body instanceof ArrayBuffer) {
|
|
268
|
+
const text = new TextDecoder().decode(body);
|
|
269
|
+
if (contentType.includes("application/json")) {
|
|
270
|
+
return JSON.parse(text);
|
|
271
|
+
}
|
|
272
|
+
return text;
|
|
273
|
+
}
|
|
274
|
+
} catch {
|
|
275
|
+
}
|
|
276
|
+
return body;
|
|
277
|
+
}
|
|
278
|
+
function createMockResponse(data, status = 200) {
|
|
279
|
+
const body = JSON.stringify(data);
|
|
280
|
+
return new Response(body, {
|
|
281
|
+
status,
|
|
282
|
+
statusText: status === 200 ? "OK" : "Error",
|
|
283
|
+
headers: {
|
|
284
|
+
"Content-Type": "application/json",
|
|
285
|
+
"X-DemoKit-Mock": "true"
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
function extractPathname(input, baseUrl) {
|
|
290
|
+
try {
|
|
291
|
+
if (typeof input === "string") {
|
|
292
|
+
if (input.startsWith("/")) {
|
|
293
|
+
return input.split("?")[0] || "/";
|
|
294
|
+
}
|
|
295
|
+
return new URL(input, baseUrl).pathname;
|
|
296
|
+
}
|
|
297
|
+
if (input instanceof URL) {
|
|
298
|
+
return input.pathname;
|
|
299
|
+
}
|
|
300
|
+
if (input instanceof Request) {
|
|
301
|
+
return new URL(input.url, baseUrl).pathname;
|
|
302
|
+
}
|
|
303
|
+
} catch {
|
|
304
|
+
}
|
|
305
|
+
return "/";
|
|
306
|
+
}
|
|
307
|
+
function extractUrl(input, baseUrl) {
|
|
308
|
+
try {
|
|
309
|
+
if (typeof input === "string") {
|
|
310
|
+
if (input.startsWith("/")) {
|
|
311
|
+
return new URL(input, baseUrl).toString();
|
|
312
|
+
}
|
|
313
|
+
return input;
|
|
314
|
+
}
|
|
315
|
+
if (input instanceof URL) {
|
|
316
|
+
return input.toString();
|
|
317
|
+
}
|
|
318
|
+
if (input instanceof Request) {
|
|
319
|
+
return input.url;
|
|
320
|
+
}
|
|
321
|
+
} catch {
|
|
322
|
+
}
|
|
323
|
+
return baseUrl;
|
|
324
|
+
}
|
|
325
|
+
function createDemoInterceptor(config) {
|
|
326
|
+
const {
|
|
327
|
+
fixtures: initialFixtures,
|
|
328
|
+
storageKey = DEFAULT_STORAGE_KEY,
|
|
329
|
+
onEnable,
|
|
330
|
+
onDisable,
|
|
331
|
+
initialEnabled,
|
|
332
|
+
baseUrl = "http://localhost"
|
|
333
|
+
} = config;
|
|
334
|
+
let enabled = initialEnabled ?? loadDemoState(storageKey);
|
|
335
|
+
let currentFixtures = { ...initialFixtures };
|
|
336
|
+
let sessionState = createSessionState();
|
|
337
|
+
let originalFetch = null;
|
|
338
|
+
let isPatched = false;
|
|
339
|
+
function patchFetch() {
|
|
340
|
+
if (isPatched || typeof globalThis.fetch !== "function") {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
originalFetch = globalThis.fetch;
|
|
344
|
+
globalThis.fetch = async function interceptedFetch(input, init) {
|
|
345
|
+
if (!enabled) {
|
|
346
|
+
return originalFetch(input, init);
|
|
347
|
+
}
|
|
348
|
+
const method = init?.method?.toUpperCase() || "GET";
|
|
349
|
+
const pathname = extractPathname(input, baseUrl);
|
|
350
|
+
const match = findMatchingPattern(currentFixtures, method, pathname);
|
|
351
|
+
if (!match) {
|
|
352
|
+
return originalFetch(input, init);
|
|
353
|
+
}
|
|
354
|
+
const [pattern, matchResult] = match;
|
|
355
|
+
const handler = currentFixtures[pattern];
|
|
356
|
+
const url = extractUrl(input, baseUrl);
|
|
357
|
+
const headers = new Headers(init?.headers);
|
|
358
|
+
const body = await parseRequestBody(init?.body, headers);
|
|
359
|
+
let searchParams;
|
|
360
|
+
try {
|
|
361
|
+
searchParams = new URL(url, baseUrl).searchParams;
|
|
362
|
+
} catch {
|
|
363
|
+
searchParams = new URLSearchParams();
|
|
364
|
+
}
|
|
365
|
+
const context = {
|
|
366
|
+
url,
|
|
367
|
+
method,
|
|
368
|
+
params: matchResult.params,
|
|
369
|
+
searchParams,
|
|
370
|
+
body,
|
|
371
|
+
headers,
|
|
372
|
+
session: sessionState
|
|
373
|
+
};
|
|
374
|
+
let result;
|
|
375
|
+
try {
|
|
376
|
+
if (typeof handler === "function") {
|
|
377
|
+
result = await handler(context);
|
|
378
|
+
} else {
|
|
379
|
+
result = handler;
|
|
380
|
+
}
|
|
381
|
+
} catch (error) {
|
|
382
|
+
console.error("[DemoKit] Fixture handler error:", error);
|
|
383
|
+
return createMockResponse(
|
|
384
|
+
{ error: "Fixture handler error", message: String(error) },
|
|
385
|
+
500
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
return createMockResponse(result);
|
|
389
|
+
};
|
|
390
|
+
isPatched = true;
|
|
391
|
+
}
|
|
392
|
+
function restoreFetch() {
|
|
393
|
+
if (!isPatched || !originalFetch) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
globalThis.fetch = originalFetch;
|
|
397
|
+
originalFetch = null;
|
|
398
|
+
isPatched = false;
|
|
399
|
+
}
|
|
400
|
+
patchFetch();
|
|
401
|
+
return {
|
|
402
|
+
enable() {
|
|
403
|
+
if (enabled) return;
|
|
404
|
+
enabled = true;
|
|
405
|
+
saveDemoState(storageKey, true);
|
|
406
|
+
onEnable?.();
|
|
407
|
+
},
|
|
408
|
+
disable() {
|
|
409
|
+
if (!enabled) return;
|
|
410
|
+
enabled = false;
|
|
411
|
+
saveDemoState(storageKey, false);
|
|
412
|
+
onDisable?.();
|
|
413
|
+
},
|
|
414
|
+
isEnabled() {
|
|
415
|
+
return enabled;
|
|
416
|
+
},
|
|
417
|
+
toggle() {
|
|
418
|
+
if (enabled) {
|
|
419
|
+
this.disable();
|
|
420
|
+
} else {
|
|
421
|
+
this.enable();
|
|
422
|
+
}
|
|
423
|
+
return enabled;
|
|
424
|
+
},
|
|
425
|
+
setFixtures(fixtures) {
|
|
426
|
+
currentFixtures = { ...fixtures };
|
|
427
|
+
},
|
|
428
|
+
addFixture(pattern, handler) {
|
|
429
|
+
currentFixtures[pattern] = handler;
|
|
430
|
+
},
|
|
431
|
+
removeFixture(pattern) {
|
|
432
|
+
delete currentFixtures[pattern];
|
|
433
|
+
},
|
|
434
|
+
resetSession() {
|
|
435
|
+
sessionState.clear();
|
|
436
|
+
},
|
|
437
|
+
getSession() {
|
|
438
|
+
return sessionState;
|
|
439
|
+
},
|
|
440
|
+
destroy() {
|
|
441
|
+
restoreFetch();
|
|
442
|
+
enabled = false;
|
|
443
|
+
sessionState.clear();
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// src/state.ts
|
|
449
|
+
function createDemoState(initialEnabled = false, fixtures = {}) {
|
|
450
|
+
return {
|
|
451
|
+
enabled: initialEnabled,
|
|
452
|
+
scenario: null,
|
|
453
|
+
fixtures,
|
|
454
|
+
enabledAt: initialEnabled ? Date.now() : null
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
function createDemoStateStore(options = {}) {
|
|
458
|
+
const {
|
|
459
|
+
initialEnabled = false,
|
|
460
|
+
fixtures = {},
|
|
461
|
+
scenarios = {},
|
|
462
|
+
onChange
|
|
463
|
+
} = options;
|
|
464
|
+
let state = createDemoState(initialEnabled, fixtures);
|
|
465
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
466
|
+
function notify() {
|
|
467
|
+
listeners.forEach((listener) => listener(state));
|
|
468
|
+
onChange?.(state);
|
|
469
|
+
}
|
|
470
|
+
function setState(updates) {
|
|
471
|
+
state = { ...state, ...updates };
|
|
472
|
+
notify();
|
|
473
|
+
}
|
|
474
|
+
return {
|
|
475
|
+
getState() {
|
|
476
|
+
return state;
|
|
477
|
+
},
|
|
478
|
+
enable() {
|
|
479
|
+
if (!state.enabled) {
|
|
480
|
+
setState({ enabled: true, enabledAt: Date.now() });
|
|
481
|
+
}
|
|
482
|
+
},
|
|
483
|
+
disable() {
|
|
484
|
+
if (state.enabled) {
|
|
485
|
+
setState({ enabled: false, enabledAt: null });
|
|
486
|
+
}
|
|
487
|
+
},
|
|
488
|
+
toggle() {
|
|
489
|
+
const newEnabled = !state.enabled;
|
|
490
|
+
setState({
|
|
491
|
+
enabled: newEnabled,
|
|
492
|
+
enabledAt: newEnabled ? Date.now() : null
|
|
493
|
+
});
|
|
494
|
+
return newEnabled;
|
|
495
|
+
},
|
|
496
|
+
setScenario(name) {
|
|
497
|
+
if (name === null) {
|
|
498
|
+
setState({ scenario: null, fixtures });
|
|
499
|
+
} else if (scenarios[name]) {
|
|
500
|
+
setState({ scenario: name, fixtures: { ...fixtures, ...scenarios[name] } });
|
|
501
|
+
} else {
|
|
502
|
+
console.warn(`[DemoKit] Unknown scenario: ${name}`);
|
|
503
|
+
}
|
|
504
|
+
},
|
|
505
|
+
updateFixtures(newFixtures) {
|
|
506
|
+
setState({ fixtures: { ...state.fixtures, ...newFixtures } });
|
|
507
|
+
},
|
|
508
|
+
setFixtures(newFixtures) {
|
|
509
|
+
setState({ fixtures: newFixtures });
|
|
510
|
+
},
|
|
511
|
+
subscribe(listener) {
|
|
512
|
+
listeners.add(listener);
|
|
513
|
+
return () => listeners.delete(listener);
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
export { DEFAULT_STORAGE_KEY, clearDemoState, clearPatternCache, createDemoInterceptor, createDemoState, createDemoStateStore, createSessionState, findMatchingPattern, findMatchingQueryKeyPattern, loadDemoState, matchQueryKey, matchUrl, parseUrlPattern, saveDemoState };
|
|
519
|
+
//# sourceMappingURL=index.js.map
|
|
520
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/matcher.ts","../src/storage.ts","../src/session.ts","../src/interceptor.ts","../src/state.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { MatchResult, ParsedPattern } from './types'\n\n/**\n * Cache for parsed patterns to avoid re-parsing\n */\nconst patternCache = new Map<string, ParsedPattern>()\n\n/**\n * Parse a URL pattern into its components\n *\n * @param pattern - Pattern in format \"METHOD /path/:param\"\n * @returns Parsed pattern with method, regex, and param names\n *\n * @example\n * parseUrlPattern('GET /api/users/:id')\n * // { method: 'GET', pathPattern: /^\\/api\\/users\\/([^/]+)$/, paramNames: ['id'] }\n *\n * parseUrlPattern('GET /api/projects/*')\n * // { method: 'GET', pathPattern: /^\\/api\\/projects\\/.*$/, paramNames: [] }\n */\nexport function parseUrlPattern(pattern: string): ParsedPattern {\n const cached = patternCache.get(pattern)\n if (cached) {\n return cached\n }\n\n const spaceIndex = pattern.indexOf(' ')\n if (spaceIndex === -1) {\n throw new Error(\n `Invalid pattern \"${pattern}\": must be in format \"METHOD /path\". Example: \"GET /api/users/:id\"`\n )\n }\n\n const method = pattern.slice(0, spaceIndex).toUpperCase()\n const path = pattern.slice(spaceIndex + 1)\n\n if (!path.startsWith('/')) {\n throw new Error(\n `Invalid pattern \"${pattern}\": path must start with \"/\". Example: \"GET /api/users\"`\n )\n }\n\n const paramNames: string[] = []\n\n // Escape regex special characters except : and *\n // Then convert :param to capture groups and * to wildcards\n let regexStr = path\n // Escape regex special chars (except : and *)\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&')\n // Convert :paramName to named capture group pattern\n .replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, (_, name: string) => {\n paramNames.push(name)\n return '([^/]+)'\n })\n // Convert * to wildcard (match anything)\n .replace(/\\*/g, '.*')\n\n const parsed: ParsedPattern = {\n method,\n pathPattern: new RegExp(`^${regexStr}$`),\n paramNames,\n }\n\n patternCache.set(pattern, parsed)\n return parsed\n}\n\n/**\n * Match a request against a fixture pattern\n *\n * @param pattern - Fixture pattern (e.g., \"GET /api/users/:id\")\n * @param method - HTTP method of the request\n * @param pathname - URL pathname of the request\n * @returns Match result with extracted params, or null if no match\n *\n * @example\n * matchUrl('GET /api/users/:id', 'GET', '/api/users/123')\n * // { matched: true, params: { id: '123' } }\n *\n * matchUrl('GET /api/users/:id', 'POST', '/api/users/123')\n * // null (method doesn't match)\n *\n * matchUrl('GET /api/users/:id', 'GET', '/api/projects/123')\n * // null (path doesn't match)\n */\nexport function matchUrl(\n pattern: string,\n method: string,\n pathname: string\n): MatchResult | null {\n const { method: patternMethod, pathPattern, paramNames } = parseUrlPattern(pattern)\n\n // Check method first (fast path)\n if (patternMethod !== method.toUpperCase()) {\n return null\n }\n\n // Match path against pattern\n const match = pathname.match(pathPattern)\n if (!match) {\n return null\n }\n\n // Extract params from capture groups\n const params: Record<string, string> = {}\n paramNames.forEach((name, index) => {\n const value = match[index + 1]\n if (value !== undefined) {\n params[name] = decodeURIComponent(value)\n }\n })\n\n return { matched: true, params }\n}\n\n/**\n * Find the first matching pattern from a fixture map\n *\n * @param fixtures - Map of patterns to fixtures\n * @param method - HTTP method of the request\n * @param pathname - URL pathname of the request\n * @returns Tuple of [pattern, match result] or null if no match\n */\nexport function findMatchingPattern(\n fixtures: Record<string, unknown>,\n method: string,\n pathname: string\n): [string, MatchResult] | null {\n for (const pattern of Object.keys(fixtures)) {\n const result = matchUrl(pattern, method, pathname)\n if (result) {\n return [pattern, result]\n }\n }\n return null\n}\n\n/**\n * Clear the pattern cache (useful for testing)\n */\nexport function clearPatternCache(): void {\n patternCache.clear()\n}\n\n// ============================================================================\n// Query Key Matching (for TanStack Query, SWR, etc.)\n// ============================================================================\n\n/**\n * A query key element can be a string, number, or object\n */\nexport type QueryKeyElement = string | number | boolean | null | undefined | Record<string, unknown>\n\n/**\n * A query key is an array of elements (like TanStack Query uses)\n */\nexport type QueryKey = readonly QueryKeyElement[]\n\n/**\n * Result of query key matching\n */\nexport interface QueryKeyMatchResult {\n /**\n * Whether the pattern matched the query key\n */\n matched: boolean\n\n /**\n * Extracted parameters from :param placeholders\n */\n params: Record<string, unknown>\n}\n\n/**\n * Check if two values are deeply equal\n */\nfunction deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true\n if (a === null || b === null) return a === b\n if (typeof a !== typeof b) return false\n\n if (typeof a === 'object' && typeof b === 'object') {\n const aObj = a as Record<string, unknown>\n const bObj = b as Record<string, unknown>\n const aKeys = Object.keys(aObj)\n const bKeys = Object.keys(bObj)\n\n if (aKeys.length !== bKeys.length) return false\n\n return aKeys.every((key) => deepEqual(aObj[key], bObj[key]))\n }\n\n return false\n}\n\n/**\n * Check if a string is a parameter placeholder (e.g., ':id', ':userId')\n */\nfunction isParamPlaceholder(value: unknown): value is string {\n return typeof value === 'string' && value.startsWith(':') && value.length > 1\n}\n\n/**\n * Extract the parameter name from a placeholder (e.g., ':id' -> 'id')\n */\nfunction getParamName(placeholder: string): string {\n return placeholder.slice(1)\n}\n\n/**\n * Match an object value against an object pattern with parameter extraction\n *\n * @param value - The actual object value from the query key\n * @param pattern - The pattern object (may contain :param placeholders)\n * @returns Match result with extracted params, or null if no match\n */\nfunction matchObjectWithParams(\n value: Record<string, unknown>,\n pattern: Record<string, unknown>\n): { matched: boolean; params: Record<string, unknown> } | null {\n const params: Record<string, unknown> = {}\n const patternKeys = Object.keys(pattern)\n const valueKeys = Object.keys(value)\n\n // Pattern must have same or fewer keys (pattern keys must all be present in value)\n if (patternKeys.length > valueKeys.length) {\n return null\n }\n\n for (const key of patternKeys) {\n if (!(key in value)) {\n return null\n }\n\n const patternVal = pattern[key]\n const actualVal = value[key]\n\n if (isParamPlaceholder(patternVal)) {\n // Extract parameter value\n params[getParamName(patternVal)] = actualVal\n } else if (typeof patternVal === 'object' && patternVal !== null && typeof actualVal === 'object' && actualVal !== null) {\n // Recursively match nested objects\n const nestedResult = matchObjectWithParams(\n actualVal as Record<string, unknown>,\n patternVal as Record<string, unknown>\n )\n if (!nestedResult) {\n return null\n }\n Object.assign(params, nestedResult.params)\n } else if (!deepEqual(patternVal, actualVal)) {\n return null\n }\n }\n\n return { matched: true, params }\n}\n\n/**\n * Match a query key against a pattern\n *\n * Supports:\n * - Exact string/number matching: ['users'] matches ['users']\n * - Parameter extraction with :param syntax: ['users', ':id'] matches ['users', '123']\n * - Object matching with params: ['users', { id: ':id' }] matches ['users', { id: '123' }]\n * - Wildcard matching with '*': ['users', '*'] matches ['users', 'anything']\n *\n * @param queryKey - The actual query key to match\n * @param pattern - The pattern to match against\n * @returns Match result with extracted params, or null if no match\n *\n * @example\n * // Exact match\n * matchQueryKey(['users'], ['users'])\n * // { matched: true, params: {} }\n *\n * // Parameter extraction from string\n * matchQueryKey(['users', '123'], ['users', ':id'])\n * // { matched: true, params: { id: '123' } }\n *\n * // Parameter extraction from object\n * matchQueryKey(['users', { id: '123', status: 'active' }], ['users', { id: ':userId' }])\n * // { matched: true, params: { userId: '123' } }\n *\n * // Wildcard matching\n * matchQueryKey(['users', 'anything'], ['users', '*'])\n * // { matched: true, params: {} }\n *\n * // No match - different length\n * matchQueryKey(['users'], ['users', 'list'])\n * // null\n */\nexport function matchQueryKey(\n queryKey: QueryKey,\n pattern: QueryKey\n): QueryKeyMatchResult | null {\n // Arrays must be same length\n if (queryKey.length !== pattern.length) {\n return null\n }\n\n const params: Record<string, unknown> = {}\n\n for (let i = 0; i < pattern.length; i++) {\n const patternElement = pattern[i]\n const keyElement = queryKey[i]\n\n // Wildcard matches anything\n if (patternElement === '*') {\n continue\n }\n\n // Parameter placeholder extracts value\n if (isParamPlaceholder(patternElement)) {\n params[getParamName(patternElement)] = keyElement\n continue\n }\n\n // Object matching with potential nested params\n if (\n typeof patternElement === 'object' &&\n patternElement !== null &&\n typeof keyElement === 'object' &&\n keyElement !== null\n ) {\n const objectResult = matchObjectWithParams(\n keyElement as Record<string, unknown>,\n patternElement as Record<string, unknown>\n )\n if (!objectResult) {\n return null\n }\n Object.assign(params, objectResult.params)\n continue\n }\n\n // Exact match for primitives\n if (patternElement !== keyElement) {\n return null\n }\n }\n\n return { matched: true, params }\n}\n\n/**\n * Find the first matching pattern from a map of query key patterns\n *\n * @param patterns - Map of serialized patterns to values\n * @param queryKey - The query key to match\n * @returns Tuple of [pattern, value, match result] or null if no match\n *\n * @example\n * const patterns = {\n * 'users': { data: [] },\n * 'users/:id': (params) => ({ id: params.id }),\n * }\n * findMatchingQueryKeyPattern(patterns, ['users', '123'])\n * // [['users', ':id'], { params: { id: '123' } }]\n */\nexport function findMatchingQueryKeyPattern<T>(\n patterns: Map<QueryKey, T>,\n queryKey: QueryKey\n): [QueryKey, T, QueryKeyMatchResult] | null {\n for (const [pattern, value] of patterns) {\n const result = matchQueryKey(queryKey, pattern)\n if (result) {\n return [pattern, value, result]\n }\n }\n return null\n}\n","/**\n * Default localStorage key for demo mode state\n */\nexport const DEFAULT_STORAGE_KEY = 'demokit-mode'\n\n/**\n * Check if localStorage is available (handles SSR and restricted contexts)\n */\nfunction isLocalStorageAvailable(): boolean {\n try {\n const testKey = '__demokit_test__'\n if (typeof window === 'undefined' || !window.localStorage) {\n return false\n }\n window.localStorage.setItem(testKey, testKey)\n window.localStorage.removeItem(testKey)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Load demo mode state from localStorage\n *\n * @param key - localStorage key to use\n * @returns Current demo mode state, or false if not set or unavailable\n */\nexport function loadDemoState(key: string = DEFAULT_STORAGE_KEY): boolean {\n if (!isLocalStorageAvailable()) {\n return false\n }\n\n try {\n const value = window.localStorage.getItem(key)\n return value === 'true'\n } catch {\n return false\n }\n}\n\n/**\n * Save demo mode state to localStorage\n *\n * @param key - localStorage key to use\n * @param enabled - Whether demo mode is enabled\n */\nexport function saveDemoState(\n key: string = DEFAULT_STORAGE_KEY,\n enabled: boolean\n): void {\n if (!isLocalStorageAvailable()) {\n return\n }\n\n try {\n if (enabled) {\n window.localStorage.setItem(key, 'true')\n } else {\n window.localStorage.removeItem(key)\n }\n } catch {\n // Silently fail if storage is full or restricted\n }\n}\n\n/**\n * Clear demo mode state from localStorage\n *\n * @param key - localStorage key to use\n */\nexport function clearDemoState(key: string = DEFAULT_STORAGE_KEY): void {\n if (!isLocalStorageAvailable()) {\n return\n }\n\n try {\n window.localStorage.removeItem(key)\n } catch {\n // Silently fail\n }\n}\n","/**\n * Session state management for DemoKit\n *\n * Provides in-memory storage for mutable state during a demo session.\n * State persists across requests but resets on page refresh.\n */\n\n/**\n * Session state interface for storing and retrieving demo session data\n *\n * @example\n * ```typescript\n * // In a fixture handler\n * 'POST /api/users': ({ body, session }) => {\n * const users = session.get<User[]>('users') || []\n * const newUser = { id: crypto.randomUUID(), ...body }\n * session.set('users', [...users, newUser])\n * return newUser\n * }\n *\n * 'GET /api/users': ({ session }) => {\n * return session.get<User[]>('users') || []\n * }\n * ```\n */\nexport interface SessionState {\n /**\n * Get a value from the session state\n * @param key - The key to retrieve\n * @returns The value if it exists, undefined otherwise\n */\n get<T>(key: string): T | undefined\n\n /**\n * Set a value in the session state\n * @param key - The key to set\n * @param value - The value to store\n */\n set<T>(key: string, value: T): void\n\n /**\n * Delete a value from the session state\n * @param key - The key to delete\n */\n delete(key: string): void\n\n /**\n * Clear all session state\n */\n clear(): void\n\n /**\n * Get all keys in the session state\n * @returns Array of all keys\n */\n keys(): string[]\n\n /**\n * Check if a key exists in the session state\n * @param key - The key to check\n * @returns True if the key exists\n */\n has(key: string): boolean\n\n /**\n * Get the number of items in the session state\n * @returns The number of items\n */\n size(): number\n}\n\n/**\n * Create a new session state instance\n *\n * Session state is purely in-memory and resets when the page is refreshed.\n * This is intentional - demo sessions should start fresh each time.\n *\n * @returns A new SessionState instance\n *\n * @example\n * ```typescript\n * const session = createSessionState()\n *\n * session.set('cart', [{ id: '1', quantity: 2 }])\n * const cart = session.get<CartItem[]>('cart')\n *\n * session.clear() // Reset all state\n * ```\n */\nexport function createSessionState(): SessionState {\n const store = new Map<string, unknown>()\n\n return {\n get<T>(key: string): T | undefined {\n return store.get(key) as T | undefined\n },\n\n set<T>(key: string, value: T): void {\n store.set(key, value)\n },\n\n delete(key: string): void {\n store.delete(key)\n },\n\n clear(): void {\n store.clear()\n },\n\n keys(): string[] {\n return Array.from(store.keys())\n },\n\n has(key: string): boolean {\n return store.has(key)\n },\n\n size(): number {\n return store.size\n },\n }\n}\n","import type {\n DemoKitConfig,\n DemoInterceptor,\n FixtureMap,\n FixtureHandler,\n RequestContext,\n} from './types'\nimport { findMatchingPattern } from './matcher'\nimport { loadDemoState, saveDemoState, DEFAULT_STORAGE_KEY } from './storage'\nimport { createSessionState, type SessionState } from './session'\n\n/**\n * Parse request body based on content type\n */\nasync function parseRequestBody(\n body: BodyInit | null | undefined,\n headers: Headers\n): Promise<unknown> {\n if (!body) {\n return undefined\n }\n\n const contentType = headers.get('content-type') || ''\n\n try {\n if (typeof body === 'string') {\n if (contentType.includes('application/json')) {\n return JSON.parse(body)\n }\n return body\n }\n\n if (body instanceof FormData) {\n const obj: Record<string, unknown> = {}\n body.forEach((value, key) => {\n obj[key] = value\n })\n return obj\n }\n\n if (body instanceof URLSearchParams) {\n const obj: Record<string, string> = {}\n body.forEach((value, key) => {\n obj[key] = value\n })\n return obj\n }\n\n if (body instanceof Blob) {\n const text = await body.text()\n if (contentType.includes('application/json')) {\n return JSON.parse(text)\n }\n return text\n }\n\n if (body instanceof ArrayBuffer) {\n const text = new TextDecoder().decode(body)\n if (contentType.includes('application/json')) {\n return JSON.parse(text)\n }\n return text\n }\n } catch {\n // Return raw body if parsing fails\n }\n\n return body\n}\n\n/**\n * Create a mock Response from fixture data\n */\nfunction createMockResponse(data: unknown, status = 200): Response {\n const body = JSON.stringify(data)\n return new Response(body, {\n status,\n statusText: status === 200 ? 'OK' : 'Error',\n headers: {\n 'Content-Type': 'application/json',\n 'X-DemoKit-Mock': 'true',\n },\n })\n}\n\n/**\n * Extract pathname from URL, handling various input types\n */\nfunction extractPathname(input: RequestInfo | URL, baseUrl: string): string {\n try {\n if (typeof input === 'string') {\n // Handle relative URLs\n if (input.startsWith('/')) {\n return input.split('?')[0] || '/'\n }\n return new URL(input, baseUrl).pathname\n }\n if (input instanceof URL) {\n return input.pathname\n }\n if (input instanceof Request) {\n return new URL(input.url, baseUrl).pathname\n }\n } catch {\n // Fallback for malformed URLs\n }\n return '/'\n}\n\n/**\n * Extract full URL from input\n */\nfunction extractUrl(input: RequestInfo | URL, baseUrl: string): string {\n try {\n if (typeof input === 'string') {\n if (input.startsWith('/')) {\n return new URL(input, baseUrl).toString()\n }\n return input\n }\n if (input instanceof URL) {\n return input.toString()\n }\n if (input instanceof Request) {\n return input.url\n }\n } catch {\n // Fallback\n }\n return baseUrl\n}\n\n/**\n * Create a demo interceptor that patches fetch to return mock data\n *\n * @param config - Configuration including fixtures and options\n * @returns Demo interceptor instance with enable/disable controls\n *\n * @example\n * const demo = createDemoInterceptor({\n * fixtures: {\n * 'GET /api/users': () => [{ id: '1', name: 'Demo User' }],\n * 'GET /api/users/:id': ({ params }) => ({ id: params.id, name: 'Demo User' }),\n * 'POST /api/users': ({ body }) => ({ id: 'new', ...body }),\n * }\n * })\n *\n * demo.enable() // All matching fetches return mock data\n * demo.disable() // Back to real API\n */\nexport function createDemoInterceptor(config: DemoKitConfig): DemoInterceptor {\n const {\n fixtures: initialFixtures,\n storageKey = DEFAULT_STORAGE_KEY,\n onEnable,\n onDisable,\n initialEnabled,\n baseUrl = 'http://localhost',\n } = config\n\n // Track state\n let enabled = initialEnabled ?? loadDemoState(storageKey)\n let currentFixtures: FixtureMap = { ...initialFixtures }\n\n // Create session state (in-memory, resets on page refresh)\n let sessionState: SessionState = createSessionState()\n\n // Store original fetch\n let originalFetch: typeof fetch | null = null\n let isPatched = false\n\n /**\n * Patch global fetch to intercept requests\n */\n function patchFetch(): void {\n if (isPatched || typeof globalThis.fetch !== 'function') {\n return\n }\n\n originalFetch = globalThis.fetch\n\n globalThis.fetch = async function interceptedFetch(\n input: RequestInfo | URL,\n init?: RequestInit\n ): Promise<Response> {\n // If demo mode is disabled, pass through\n if (!enabled) {\n return originalFetch!(input, init)\n }\n\n const method = init?.method?.toUpperCase() || 'GET'\n const pathname = extractPathname(input, baseUrl)\n\n // Try to find a matching fixture\n const match = findMatchingPattern(currentFixtures, method, pathname)\n\n if (!match) {\n // No matching fixture - pass through to real API\n return originalFetch!(input, init)\n }\n\n const [pattern, matchResult] = match\n const handler = currentFixtures[pattern] as FixtureHandler\n\n // Build request context for the handler\n const url = extractUrl(input, baseUrl)\n const headers = new Headers(init?.headers)\n const body = await parseRequestBody(init?.body, headers)\n\n let searchParams: URLSearchParams\n try {\n searchParams = new URL(url, baseUrl).searchParams\n } catch {\n searchParams = new URLSearchParams()\n }\n\n const context: RequestContext = {\n url,\n method,\n params: matchResult.params,\n searchParams,\n body,\n headers,\n session: sessionState,\n }\n\n // Execute handler and get result\n let result: unknown\n try {\n if (typeof handler === 'function') {\n result = await handler(context)\n } else {\n result = handler\n }\n } catch (error) {\n // Return error response if handler throws\n console.error('[DemoKit] Fixture handler error:', error)\n return createMockResponse(\n { error: 'Fixture handler error', message: String(error) },\n 500\n )\n }\n\n return createMockResponse(result)\n }\n\n isPatched = true\n }\n\n /**\n * Restore original fetch\n */\n function restoreFetch(): void {\n if (!isPatched || !originalFetch) {\n return\n }\n\n globalThis.fetch = originalFetch\n originalFetch = null\n isPatched = false\n }\n\n // Patch fetch immediately\n patchFetch()\n\n return {\n enable(): void {\n if (enabled) return\n\n enabled = true\n saveDemoState(storageKey, true)\n onEnable?.()\n },\n\n disable(): void {\n if (!enabled) return\n\n enabled = false\n saveDemoState(storageKey, false)\n onDisable?.()\n },\n\n isEnabled(): boolean {\n return enabled\n },\n\n toggle(): boolean {\n if (enabled) {\n this.disable()\n } else {\n this.enable()\n }\n return enabled\n },\n\n setFixtures(fixtures: FixtureMap): void {\n currentFixtures = { ...fixtures }\n },\n\n addFixture(pattern: string, handler: FixtureHandler): void {\n currentFixtures[pattern] = handler\n },\n\n removeFixture(pattern: string): void {\n delete currentFixtures[pattern]\n },\n\n resetSession(): void {\n sessionState.clear()\n },\n\n getSession(): SessionState {\n return sessionState\n },\n\n destroy(): void {\n restoreFetch()\n enabled = false\n sessionState.clear()\n },\n }\n}\n","import type { FixtureMap } from './types'\n\n/**\n * Shared demo state interface that can be used across frameworks\n * This provides a consistent API for managing demo mode state\n */\nexport interface DemoState {\n /**\n * Whether demo mode is currently enabled\n */\n enabled: boolean\n\n /**\n * The currently active scenario name, if any\n * Scenarios allow grouping fixtures for different demo flows\n */\n scenario: string | null\n\n /**\n * The active fixture map for the current scenario\n */\n fixtures: FixtureMap\n\n /**\n * Timestamp when demo mode was last enabled\n * Useful for session timeout logic\n */\n enabledAt: number | null\n}\n\n/**\n * Create initial demo state\n *\n * @param initialEnabled - Whether demo mode should start enabled\n * @param fixtures - Initial fixture map\n * @returns A new DemoState object\n *\n * @example\n * const state = createDemoState(false, {\n * 'GET /api/users': () => [{ id: '1', name: 'Demo User' }],\n * })\n */\nexport function createDemoState(\n initialEnabled: boolean = false,\n fixtures: FixtureMap = {}\n): DemoState {\n return {\n enabled: initialEnabled,\n scenario: null,\n fixtures,\n enabledAt: initialEnabled ? Date.now() : null,\n }\n}\n\n/**\n * Options for creating a demo state store\n */\nexport interface DemoStateStoreOptions {\n /**\n * Initial enabled state\n * @default false\n */\n initialEnabled?: boolean\n\n /**\n * Initial fixtures\n * @default {}\n */\n fixtures?: FixtureMap\n\n /**\n * Available scenarios with their fixtures\n */\n scenarios?: Record<string, FixtureMap>\n\n /**\n * Callback when state changes\n */\n onChange?: (state: DemoState) => void\n}\n\n/**\n * A minimal state store for demo mode\n * Framework adapters can use this or integrate with their own state management\n */\nexport interface DemoStateStore {\n /**\n * Get the current state\n */\n getState(): DemoState\n\n /**\n * Enable demo mode\n */\n enable(): void\n\n /**\n * Disable demo mode\n */\n disable(): void\n\n /**\n * Toggle demo mode\n */\n toggle(): boolean\n\n /**\n * Set the active scenario\n * @param name - Scenario name (or null to clear)\n */\n setScenario(name: string | null): void\n\n /**\n * Update fixtures (merges with existing)\n */\n updateFixtures(fixtures: FixtureMap): void\n\n /**\n * Replace all fixtures\n */\n setFixtures(fixtures: FixtureMap): void\n\n /**\n * Subscribe to state changes\n * @returns Unsubscribe function\n */\n subscribe(listener: (state: DemoState) => void): () => void\n}\n\n/**\n * Create a demo state store\n *\n * This is a minimal, framework-agnostic state store that can be used\n * directly or wrapped by framework-specific adapters (React, Vue, etc.)\n *\n * @param options - Store configuration options\n * @returns A DemoStateStore instance\n *\n * @example\n * const store = createDemoStateStore({\n * initialEnabled: false,\n * fixtures: {\n * 'GET /api/users': () => [{ id: '1', name: 'Demo User' }],\n * },\n * scenarios: {\n * 'empty-state': { 'GET /api/users': () => [] },\n * 'error-state': { 'GET /api/users': () => { throw new Error('API Error') } },\n * },\n * })\n *\n * // Subscribe to changes\n * const unsubscribe = store.subscribe((state) => {\n * console.log('Demo mode:', state.enabled)\n * })\n *\n * // Enable demo mode\n * store.enable()\n *\n * // Switch scenario\n * store.setScenario('empty-state')\n */\nexport function createDemoStateStore(options: DemoStateStoreOptions = {}): DemoStateStore {\n const {\n initialEnabled = false,\n fixtures = {},\n scenarios = {},\n onChange,\n } = options\n\n let state = createDemoState(initialEnabled, fixtures)\n const listeners = new Set<(state: DemoState) => void>()\n\n function notify() {\n listeners.forEach((listener) => listener(state))\n onChange?.(state)\n }\n\n function setState(updates: Partial<DemoState>) {\n state = { ...state, ...updates }\n notify()\n }\n\n return {\n getState() {\n return state\n },\n\n enable() {\n if (!state.enabled) {\n setState({ enabled: true, enabledAt: Date.now() })\n }\n },\n\n disable() {\n if (state.enabled) {\n setState({ enabled: false, enabledAt: null })\n }\n },\n\n toggle() {\n const newEnabled = !state.enabled\n setState({\n enabled: newEnabled,\n enabledAt: newEnabled ? Date.now() : null,\n })\n return newEnabled\n },\n\n setScenario(name: string | null) {\n if (name === null) {\n setState({ scenario: null, fixtures })\n } else if (scenarios[name]) {\n setState({ scenario: name, fixtures: { ...fixtures, ...scenarios[name] } })\n } else {\n console.warn(`[DemoKit] Unknown scenario: ${name}`)\n }\n },\n\n updateFixtures(newFixtures: FixtureMap) {\n setState({ fixtures: { ...state.fixtures, ...newFixtures } })\n },\n\n setFixtures(newFixtures: FixtureMap) {\n setState({ fixtures: newFixtures })\n },\n\n subscribe(listener: (state: DemoState) => void) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n }\n}"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@demokit-ai/core",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Framework-agnostic demo mode SDK with fetch interception",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.cts",
|
|
17
|
+
"default": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsup",
|
|
26
|
+
"dev": "tsup --watch",
|
|
27
|
+
"test": "vitest",
|
|
28
|
+
"typecheck": "tsc --noEmit"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"tsup": "^8.3.5",
|
|
32
|
+
"typescript": "^5.7.2",
|
|
33
|
+
"vitest": "^2.1.8"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"demo",
|
|
37
|
+
"mock",
|
|
38
|
+
"api",
|
|
39
|
+
"fetch",
|
|
40
|
+
"interceptor",
|
|
41
|
+
"saas"
|
|
42
|
+
],
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "https://github.com/your-org/demokit",
|
|
47
|
+
"directory": "packages/core"
|
|
48
|
+
},
|
|
49
|
+
"sideEffects": false
|
|
50
|
+
}
|