@getmicdrop/svelte-components 5.3.13 → 5.3.14
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/constants/formOptions.d.ts +9 -4
- package/dist/constants/formOptions.d.ts.map +1 -1
- package/dist/constants/formOptions.js +48 -26
- package/dist/index.d.ts +0 -10
- package/dist/index.js +5 -19
- package/dist/stores/auth.js +36 -123
- package/dist/stores/auth.spec.js +139 -447
- package/dist/stores/createFormStore.d.ts +74 -0
- package/dist/stores/createFormStore.d.ts.map +1 -0
- package/dist/stores/createFormStore.js +386 -0
- package/dist/stores/createFormStore.spec.d.ts +2 -0
- package/dist/stores/createFormStore.spec.d.ts.map +1 -0
- package/dist/stores/createFormStore.spec.js +540 -0
- package/dist/telemetry.d.ts +21 -2
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +403 -358
- package/dist/utils/apiConfig.js +120 -50
- package/dist/utils/utils/utils.js +3 -323
- package/dist/utils/utils.js +644 -346
- package/package.json +16 -1
package/dist/stores/auth.spec.js
CHANGED
|
@@ -1,447 +1,139 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach
|
|
2
|
-
import { get } from 'svelte/store';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
describe('
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
expect(state.
|
|
66
|
-
expect(state.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
it('
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
expect(state.
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
initializeAuthState();
|
|
142
|
-
|
|
143
|
-
const state = get(auth);
|
|
144
|
-
expect(state.isAuthenticated).toBe(true);
|
|
145
|
-
expect(state.userDetails).toBe(null);
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
describe('store subscription', () => {
|
|
150
|
-
it('can subscribe to auth changes', () => {
|
|
151
|
-
const values = [];
|
|
152
|
-
const unsubscribe = auth.subscribe((value) => {
|
|
153
|
-
values.push(value.isAuthenticated);
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
setAuthState({ isAuthenticated: true, token: 'token', userDetails: null });
|
|
157
|
-
clearAuthState();
|
|
158
|
-
|
|
159
|
-
unsubscribe();
|
|
160
|
-
|
|
161
|
-
expect(values).toEqual([false, true, false]);
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
describe('updateTokens', () => {
|
|
166
|
-
it('updates access token and computes new expiry', () => {
|
|
167
|
-
setAuthState({
|
|
168
|
-
isAuthenticated: true,
|
|
169
|
-
token: 'old-token',
|
|
170
|
-
refreshToken: 'old-refresh',
|
|
171
|
-
userDetails: null,
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
updateTokens('new-access-token', 'new-refresh-token');
|
|
175
|
-
|
|
176
|
-
const state = get(auth);
|
|
177
|
-
expect(state.token).toBe('new-access-token');
|
|
178
|
-
expect(state.refreshToken).toBe('new-refresh-token');
|
|
179
|
-
expect(state.tokenExpiry).toBeDefined();
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it('preserves existing refresh token if not provided', () => {
|
|
183
|
-
setAuthState({
|
|
184
|
-
isAuthenticated: true,
|
|
185
|
-
token: 'old-token',
|
|
186
|
-
refreshToken: 'existing-refresh',
|
|
187
|
-
userDetails: null,
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
updateTokens('new-access-token');
|
|
191
|
-
|
|
192
|
-
const state = get(auth);
|
|
193
|
-
expect(state.token).toBe('new-access-token');
|
|
194
|
-
expect(state.refreshToken).toBe('existing-refresh');
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
it('preserves other state properties when updating tokens', () => {
|
|
198
|
-
setAuthState({
|
|
199
|
-
isAuthenticated: true,
|
|
200
|
-
token: 'old-token',
|
|
201
|
-
refreshToken: 'refresh',
|
|
202
|
-
userDetails: { email: 'test@test.com' },
|
|
203
|
-
permissions: { events: { read: true } },
|
|
204
|
-
context: { contextType: 'performer' },
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
updateTokens('new-token');
|
|
208
|
-
|
|
209
|
-
const state = get(auth);
|
|
210
|
-
expect(state.userDetails).toEqual({ email: 'test@test.com' });
|
|
211
|
-
expect(state.permissions).toEqual({ events: { read: true } });
|
|
212
|
-
expect(state.context).toEqual({ contextType: 'performer' });
|
|
213
|
-
});
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
describe('isTokenExpiringSoon', () => {
|
|
217
|
-
it('returns true when no token expiry is set', () => {
|
|
218
|
-
clearAuthState();
|
|
219
|
-
expect(isTokenExpiringSoon()).toBe(true);
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
it('returns true when token is expired', () => {
|
|
223
|
-
const pastTime = Date.now() - 10000; // 10 seconds ago
|
|
224
|
-
setAuthState({
|
|
225
|
-
isAuthenticated: true,
|
|
226
|
-
token: 'token',
|
|
227
|
-
tokenExpiry: pastTime,
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
expect(isTokenExpiringSoon()).toBe(true);
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
it('returns true when token expires within buffer time', () => {
|
|
234
|
-
const soonExpiry = Date.now() + (2 * 60 * 1000); // 2 minutes from now
|
|
235
|
-
setAuthState({
|
|
236
|
-
isAuthenticated: true,
|
|
237
|
-
token: 'token',
|
|
238
|
-
tokenExpiry: soonExpiry,
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
// Default buffer is 5 minutes, so 2 minutes is within buffer
|
|
242
|
-
expect(isTokenExpiringSoon()).toBe(true);
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
it('returns false when token has plenty of time left', () => {
|
|
246
|
-
const futureExpiry = Date.now() + (30 * 60 * 1000); // 30 minutes from now
|
|
247
|
-
setAuthState({
|
|
248
|
-
isAuthenticated: true,
|
|
249
|
-
token: 'token',
|
|
250
|
-
tokenExpiry: futureExpiry,
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
expect(isTokenExpiringSoon()).toBe(false);
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
it('accepts custom buffer time', () => {
|
|
257
|
-
const expiry = Date.now() + (10 * 60 * 1000); // 10 minutes from now
|
|
258
|
-
setAuthState({
|
|
259
|
-
isAuthenticated: true,
|
|
260
|
-
token: 'token',
|
|
261
|
-
tokenExpiry: expiry,
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
// With 5 minute buffer (default), should be fine
|
|
265
|
-
expect(isTokenExpiringSoon(5 * 60 * 1000)).toBe(false);
|
|
266
|
-
|
|
267
|
-
// With 15 minute buffer, should be expiring soon
|
|
268
|
-
expect(isTokenExpiringSoon(15 * 60 * 1000)).toBe(true);
|
|
269
|
-
});
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
describe('setPermissions and getPermissions', () => {
|
|
273
|
-
it('sets permissions on auth state', () => {
|
|
274
|
-
const permissions = {
|
|
275
|
-
events: { read: true, write: false },
|
|
276
|
-
shows: { read: true, write: true },
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
setPermissions(permissions);
|
|
280
|
-
|
|
281
|
-
const state = get(auth);
|
|
282
|
-
expect(state.permissions).toEqual(permissions);
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
it('getPermissions returns current permissions', () => {
|
|
286
|
-
const permissions = {
|
|
287
|
-
events: { read: true },
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
setPermissions(permissions);
|
|
291
|
-
|
|
292
|
-
expect(getPermissions()).toEqual(permissions);
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
it('getPermissions returns null when no permissions set', () => {
|
|
296
|
-
clearAuthState();
|
|
297
|
-
expect(getPermissions()).toBe(null);
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
it('preserves other state when setting permissions', () => {
|
|
301
|
-
setAuthState({
|
|
302
|
-
isAuthenticated: true,
|
|
303
|
-
token: 'test-token',
|
|
304
|
-
userDetails: { email: 'test@test.com' },
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
setPermissions({ events: { read: true } });
|
|
308
|
-
|
|
309
|
-
const state = get(auth);
|
|
310
|
-
expect(state.isAuthenticated).toBe(true);
|
|
311
|
-
expect(state.token).toBe('test-token');
|
|
312
|
-
expect(state.userDetails).toEqual({ email: 'test@test.com' });
|
|
313
|
-
});
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
describe('setContext', () => {
|
|
317
|
-
it('sets user context on auth state', () => {
|
|
318
|
-
const context = {
|
|
319
|
-
contextType: 'performer',
|
|
320
|
-
performerId: 'perf-123',
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
setContext(context);
|
|
324
|
-
|
|
325
|
-
const state = get(auth);
|
|
326
|
-
expect(state.context).toEqual(context);
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
it('preserves other state when setting context', () => {
|
|
330
|
-
setAuthState({
|
|
331
|
-
isAuthenticated: true,
|
|
332
|
-
token: 'test-token',
|
|
333
|
-
permissions: { events: { read: true } },
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
setContext({ contextType: 'performer' });
|
|
337
|
-
|
|
338
|
-
const state = get(auth);
|
|
339
|
-
expect(state.isAuthenticated).toBe(true);
|
|
340
|
-
expect(state.token).toBe('test-token');
|
|
341
|
-
expect(state.permissions).toEqual({ events: { read: true } });
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
it('can update existing context', () => {
|
|
345
|
-
setContext({ contextType: 'performer', performerId: 'old' });
|
|
346
|
-
setContext({ contextType: 'performer', performerId: 'new' });
|
|
347
|
-
|
|
348
|
-
const state = get(auth);
|
|
349
|
-
expect(state.context.performerId).toBe('new');
|
|
350
|
-
});
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
describe('hasPermission', () => {
|
|
354
|
-
it('returns false when no permissions are set', () => {
|
|
355
|
-
clearAuthState();
|
|
356
|
-
expect(hasPermission('events', 'read')).toBe(false);
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
it('returns true when permission exists and is true', () => {
|
|
360
|
-
setPermissions({
|
|
361
|
-
events: { read: true, write: false },
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
expect(hasPermission('events', 'read')).toBe(true);
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
it('returns false when permission exists and is false', () => {
|
|
368
|
-
setPermissions({
|
|
369
|
-
events: { read: true, write: false },
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
expect(hasPermission('events', 'write')).toBe(false);
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
it('returns false when category does not exist', () => {
|
|
376
|
-
setPermissions({
|
|
377
|
-
events: { read: true },
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
expect(hasPermission('shows', 'read')).toBe(false);
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
it('returns false when permission does not exist in category', () => {
|
|
384
|
-
setPermissions({
|
|
385
|
-
events: { read: true },
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
expect(hasPermission('events', 'delete')).toBe(false);
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
it('works with nested permission categories', () => {
|
|
392
|
-
setPermissions({
|
|
393
|
-
events: { read: true, write: true },
|
|
394
|
-
shows: { read: true, write: false, delete: false },
|
|
395
|
-
admin: { read: false },
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
expect(hasPermission('events', 'read')).toBe(true);
|
|
399
|
-
expect(hasPermission('events', 'write')).toBe(true);
|
|
400
|
-
expect(hasPermission('shows', 'read')).toBe(true);
|
|
401
|
-
expect(hasPermission('shows', 'write')).toBe(false);
|
|
402
|
-
expect(hasPermission('admin', 'read')).toBe(false);
|
|
403
|
-
});
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
describe('isPerformer derived store', () => {
|
|
407
|
-
it('returns true when context type is performer', () => {
|
|
408
|
-
setContext({ contextType: 'performer' });
|
|
409
|
-
|
|
410
|
-
expect(get(isPerformer)).toBe(true);
|
|
411
|
-
});
|
|
412
|
-
|
|
413
|
-
it('returns true when user is authenticated (fallback)', () => {
|
|
414
|
-
setAuthState({
|
|
415
|
-
isAuthenticated: true,
|
|
416
|
-
token: 'token',
|
|
417
|
-
userDetails: null,
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
expect(get(isPerformer)).toBe(true);
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
it('returns false when not authenticated and no performer context', () => {
|
|
424
|
-
clearAuthState();
|
|
425
|
-
|
|
426
|
-
expect(get(isPerformer)).toBe(false);
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
it('updates reactively when context changes', () => {
|
|
430
|
-
const values = [];
|
|
431
|
-
const unsubscribe = isPerformer.subscribe((value) => {
|
|
432
|
-
values.push(value);
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
clearAuthState();
|
|
436
|
-
setContext({ contextType: 'performer' });
|
|
437
|
-
setContext({ contextType: 'staff' });
|
|
438
|
-
setAuthState({ isAuthenticated: true, token: 'token' });
|
|
439
|
-
|
|
440
|
-
unsubscribe();
|
|
441
|
-
|
|
442
|
-
// false (initial), true (performer context), false (staff context, not authenticated), true (authenticated)
|
|
443
|
-
expect(values).toContain(false);
|
|
444
|
-
expect(values).toContain(true);
|
|
445
|
-
});
|
|
446
|
-
});
|
|
447
|
-
});
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { get } from 'svelte/store';
|
|
3
|
+
import { auth, setAuthState, clearAuthState, initializeAuthState } from './auth';
|
|
4
|
+
|
|
5
|
+
describe('auth store', () => {
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
// Reset auth state before each test
|
|
8
|
+
clearAuthState();
|
|
9
|
+
// Clear cookies
|
|
10
|
+
Object.defineProperty(document, 'cookie', {
|
|
11
|
+
writable: true,
|
|
12
|
+
value: '',
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe('initial state', () => {
|
|
17
|
+
it('starts with isAuthenticated as false', () => {
|
|
18
|
+
expect(get(auth).isAuthenticated).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('starts with token as null', () => {
|
|
22
|
+
expect(get(auth).token).toBe(null);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('starts with userDetails as null', () => {
|
|
26
|
+
expect(get(auth).userDetails).toBe(null);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('setAuthState', () => {
|
|
31
|
+
it('sets authentication state', () => {
|
|
32
|
+
setAuthState({
|
|
33
|
+
isAuthenticated: true,
|
|
34
|
+
token: 'test-token',
|
|
35
|
+
userDetails: { email: 'test@test.com' },
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const state = get(auth);
|
|
39
|
+
expect(state.isAuthenticated).toBe(true);
|
|
40
|
+
expect(state.token).toBe('test-token');
|
|
41
|
+
expect(state.userDetails).toEqual({ email: 'test@test.com' });
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('can update partial state', () => {
|
|
45
|
+
setAuthState({ isAuthenticated: true, token: 'token1', userDetails: null });
|
|
46
|
+
setAuthState({ isAuthenticated: true, token: 'token2', userDetails: { name: 'User' } });
|
|
47
|
+
|
|
48
|
+
const state = get(auth);
|
|
49
|
+
expect(state.token).toBe('token2');
|
|
50
|
+
expect(state.userDetails).toEqual({ name: 'User' });
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe('clearAuthState', () => {
|
|
55
|
+
it('clears authentication state', () => {
|
|
56
|
+
setAuthState({
|
|
57
|
+
isAuthenticated: true,
|
|
58
|
+
token: 'test-token',
|
|
59
|
+
userDetails: { email: 'test@test.com' },
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
clearAuthState();
|
|
63
|
+
|
|
64
|
+
const state = get(auth);
|
|
65
|
+
expect(state.isAuthenticated).toBe(false);
|
|
66
|
+
expect(state.token).toBe(null);
|
|
67
|
+
expect(state.userDetails).toBe(null);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('initializeAuthState', () => {
|
|
72
|
+
it('does nothing when no performer_token cookie exists', () => {
|
|
73
|
+
Object.defineProperty(document, 'cookie', {
|
|
74
|
+
writable: true,
|
|
75
|
+
value: '',
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
initializeAuthState();
|
|
79
|
+
|
|
80
|
+
const state = get(auth);
|
|
81
|
+
expect(state.isAuthenticated).toBe(false);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('sets auth state from performer_token cookie', () => {
|
|
85
|
+
Object.defineProperty(document, 'cookie', {
|
|
86
|
+
writable: true,
|
|
87
|
+
value: 'performer_token=my-token',
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
initializeAuthState();
|
|
91
|
+
|
|
92
|
+
const state = get(auth);
|
|
93
|
+
expect(state.isAuthenticated).toBe(true);
|
|
94
|
+
expect(state.token).toBe('my-token');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('parses userDetails cookie when present', () => {
|
|
98
|
+
const userDetails = JSON.stringify({ email: 'test@test.com', firstName: 'Test' });
|
|
99
|
+
Object.defineProperty(document, 'cookie', {
|
|
100
|
+
writable: true,
|
|
101
|
+
value: `performer_token=my-token; userDetails=${encodeURIComponent(userDetails)}`,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
initializeAuthState();
|
|
105
|
+
|
|
106
|
+
const state = get(auth);
|
|
107
|
+
expect(state.userDetails).toEqual({ email: 'test@test.com', firstName: 'Test' });
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('handles missing userDetails cookie', () => {
|
|
111
|
+
Object.defineProperty(document, 'cookie', {
|
|
112
|
+
writable: true,
|
|
113
|
+
value: 'performer_token=my-token',
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
initializeAuthState();
|
|
117
|
+
|
|
118
|
+
const state = get(auth);
|
|
119
|
+
expect(state.isAuthenticated).toBe(true);
|
|
120
|
+
expect(state.userDetails).toBe(null);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('store subscription', () => {
|
|
125
|
+
it('can subscribe to auth changes', () => {
|
|
126
|
+
const values = [];
|
|
127
|
+
const unsubscribe = auth.subscribe((value) => {
|
|
128
|
+
values.push(value.isAuthenticated);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
setAuthState({ isAuthenticated: true, token: 'token', userDetails: null });
|
|
132
|
+
clearAuthState();
|
|
133
|
+
|
|
134
|
+
unsubscribe();
|
|
135
|
+
|
|
136
|
+
expect(values).toEqual([false, true, false]);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} FormStoreOptions
|
|
3
|
+
* @property {Object} initialData - Initial form data
|
|
4
|
+
* @property {string} [endpoint] - API endpoint for saving
|
|
5
|
+
* @property {string} [successMessage] - Message to show on success
|
|
6
|
+
* @property {string} [errorMessage] - Message to show on error
|
|
7
|
+
* @property {Function} [validate] - Validation function returning { isValid, errors }
|
|
8
|
+
* @property {Function} [transformData] - Transform data before saving
|
|
9
|
+
* @property {Function} [onSuccess] - Callback after successful save
|
|
10
|
+
* @property {Function} [onError] - Callback after error
|
|
11
|
+
* @property {boolean} [showToasts=true] - Show toast notifications
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Creates a comprehensive form store with state management, dirty tracking,
|
|
15
|
+
* validation, and save handling.
|
|
16
|
+
*
|
|
17
|
+
* @param {FormStoreOptions} options - Configuration options
|
|
18
|
+
* @returns {Object} Form store with all state and handlers
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* const form = createFormStore({
|
|
22
|
+
* initialData: { name: '', email: '' },
|
|
23
|
+
* endpoint: '/api/profile',
|
|
24
|
+
* validate: (data) => {
|
|
25
|
+
* const errors = {};
|
|
26
|
+
* if (!data.name) errors.name = 'Name is required';
|
|
27
|
+
* return { isValid: Object.keys(errors).length === 0, errors };
|
|
28
|
+
* }
|
|
29
|
+
* });
|
|
30
|
+
*
|
|
31
|
+
* // In Svelte component:
|
|
32
|
+
* <input bind:value={$form.data.name} on:input={form.checkDirty} />
|
|
33
|
+
* <button on:click={form.submit} disabled={$form.isLoading}>Save</button>
|
|
34
|
+
*/
|
|
35
|
+
export function createFormStore(options?: FormStoreOptions): Object;
|
|
36
|
+
export type FormStoreOptions = {
|
|
37
|
+
/**
|
|
38
|
+
* - Initial form data
|
|
39
|
+
*/
|
|
40
|
+
initialData: Object;
|
|
41
|
+
/**
|
|
42
|
+
* - API endpoint for saving
|
|
43
|
+
*/
|
|
44
|
+
endpoint?: string | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* - Message to show on success
|
|
47
|
+
*/
|
|
48
|
+
successMessage?: string | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* - Message to show on error
|
|
51
|
+
*/
|
|
52
|
+
errorMessage?: string | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* - Validation function returning { isValid, errors }
|
|
55
|
+
*/
|
|
56
|
+
validate?: Function | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* - Transform data before saving
|
|
59
|
+
*/
|
|
60
|
+
transformData?: Function | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* - Callback after successful save
|
|
63
|
+
*/
|
|
64
|
+
onSuccess?: Function | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* - Callback after error
|
|
67
|
+
*/
|
|
68
|
+
onError?: Function | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* - Show toast notifications
|
|
71
|
+
*/
|
|
72
|
+
showToasts?: boolean | undefined;
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=createFormStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createFormStore.d.ts","sourceRoot":"","sources":["../../src/lib/stores/createFormStore.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;GAWG;AAEH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,0CAlBW,gBAAgB,GACd,MAAM,CA4WlB;;;;;iBA5Xa,MAAM"}
|