@authrim/sveltekit 0.1.0
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/LICENSE +191 -0
- package/README.md +531 -0
- package/dist/__tests__/client-events.test.d.ts +2 -0
- package/dist/__tests__/client-events.test.d.ts.map +1 -0
- package/dist/__tests__/client-events.test.js +225 -0
- package/dist/__tests__/providers.test.d.ts +2 -0
- package/dist/__tests__/providers.test.d.ts.map +1 -0
- package/dist/__tests__/providers.test.js +68 -0
- package/dist/__tests__/response.test.d.ts +2 -0
- package/dist/__tests__/response.test.d.ts.map +1 -0
- package/dist/__tests__/response.test.js +99 -0
- package/dist/__tests__/stores.test.d.ts +2 -0
- package/dist/__tests__/stores.test.d.ts.map +1 -0
- package/dist/__tests__/stores.test.js +91 -0
- package/dist/client.d.ts +25 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +411 -0
- package/dist/components/AuthProvider.svelte +56 -0
- package/dist/components/AuthProvider.svelte.d.ts +34 -0
- package/dist/components/AuthProvider.svelte.d.ts.map +1 -0
- package/dist/components/ProtectedRoute.svelte +71 -0
- package/dist/components/ProtectedRoute.svelte.d.ts +38 -0
- package/dist/components/ProtectedRoute.svelte.d.ts.map +1 -0
- package/dist/components/SignInButton.svelte +93 -0
- package/dist/components/SignInButton.svelte.d.ts +43 -0
- package/dist/components/SignInButton.svelte.d.ts.map +1 -0
- package/dist/components/SignOutButton.svelte +72 -0
- package/dist/components/SignOutButton.svelte.d.ts +40 -0
- package/dist/components/SignOutButton.svelte.d.ts.map +1 -0
- package/dist/components/UserProfile.svelte +71 -0
- package/dist/components/UserProfile.svelte.d.ts +51 -0
- package/dist/components/UserProfile.svelte.d.ts.map +1 -0
- package/dist/components/index.d.ts +6 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +5 -0
- package/dist/direct-auth/ciba.d.ts +47 -0
- package/dist/direct-auth/ciba.d.ts.map +1 -0
- package/dist/direct-auth/ciba.js +77 -0
- package/dist/direct-auth/consent.d.ts +85 -0
- package/dist/direct-auth/consent.d.ts.map +1 -0
- package/dist/direct-auth/consent.js +57 -0
- package/dist/direct-auth/device-flow.d.ts +40 -0
- package/dist/direct-auth/device-flow.d.ts.map +1 -0
- package/dist/direct-auth/device-flow.js +45 -0
- package/dist/direct-auth/email-code.d.ts +48 -0
- package/dist/direct-auth/email-code.d.ts.map +1 -0
- package/dist/direct-auth/email-code.js +265 -0
- package/dist/direct-auth/index.d.ts +9 -0
- package/dist/direct-auth/index.d.ts.map +1 -0
- package/dist/direct-auth/index.js +8 -0
- package/dist/direct-auth/login-challenge.d.ts +41 -0
- package/dist/direct-auth/login-challenge.d.ts.map +1 -0
- package/dist/direct-auth/login-challenge.js +34 -0
- package/dist/direct-auth/passkey.d.ts +30 -0
- package/dist/direct-auth/passkey.d.ts.map +1 -0
- package/dist/direct-auth/passkey.js +392 -0
- package/dist/direct-auth/session.d.ts +48 -0
- package/dist/direct-auth/session.d.ts.map +1 -0
- package/dist/direct-auth/session.js +219 -0
- package/dist/direct-auth/social.d.ts +56 -0
- package/dist/direct-auth/social.d.ts.map +1 -0
- package/dist/direct-auth/social.js +484 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/providers/crypto.d.ts +13 -0
- package/dist/providers/crypto.d.ts.map +1 -0
- package/dist/providers/crypto.js +27 -0
- package/dist/providers/http.d.ts +30 -0
- package/dist/providers/http.d.ts.map +1 -0
- package/dist/providers/http.js +65 -0
- package/dist/providers/index.d.ts +4 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +3 -0
- package/dist/providers/storage.d.ts +21 -0
- package/dist/providers/storage.d.ts.map +1 -0
- package/dist/providers/storage.js +83 -0
- package/dist/server/handle.d.ts +46 -0
- package/dist/server/handle.d.ts.map +1 -0
- package/dist/server/handle.js +60 -0
- package/dist/server/index.d.ts +4 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +3 -0
- package/dist/server/load.d.ts +83 -0
- package/dist/server/load.d.ts.map +1 -0
- package/dist/server/load.js +86 -0
- package/dist/server/session.d.ts +44 -0
- package/dist/server/session.d.ts.map +1 -0
- package/dist/server/session.js +50 -0
- package/dist/stores/auth.d.ts +56 -0
- package/dist/stores/auth.d.ts.map +1 -0
- package/dist/stores/auth.js +64 -0
- package/dist/stores/index.d.ts +2 -0
- package/dist/stores/index.d.ts.map +1 -0
- package/dist/stores/index.js +1 -0
- package/dist/types.d.ts +164 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/ui/account/LinkAccountButton.svelte +133 -0
- package/dist/ui/account/LinkAccountButton.svelte.d.ts +37 -0
- package/dist/ui/account/LinkAccountButton.svelte.d.ts.map +1 -0
- package/dist/ui/account/LinkedAccountsList.svelte +233 -0
- package/dist/ui/account/LinkedAccountsList.svelte.d.ts +32 -0
- package/dist/ui/account/LinkedAccountsList.svelte.d.ts.map +1 -0
- package/dist/ui/account/UnlinkAccountButton.svelte +179 -0
- package/dist/ui/account/UnlinkAccountButton.svelte.d.ts +28 -0
- package/dist/ui/account/UnlinkAccountButton.svelte.d.ts.map +1 -0
- package/dist/ui/account/index.d.ts +7 -0
- package/dist/ui/account/index.d.ts.map +1 -0
- package/dist/ui/account/index.js +6 -0
- package/dist/ui/context.d.ts +17 -0
- package/dist/ui/context.d.ts.map +1 -0
- package/dist/ui/context.js +71 -0
- package/dist/ui/forms/CIBARequestCard.svelte +315 -0
- package/dist/ui/forms/CIBARequestCard.svelte.d.ts +50 -0
- package/dist/ui/forms/CIBARequestCard.svelte.d.ts.map +1 -0
- package/dist/ui/forms/ClientInfo.svelte +232 -0
- package/dist/ui/forms/ClientInfo.svelte.d.ts +35 -0
- package/dist/ui/forms/ClientInfo.svelte.d.ts.map +1 -0
- package/dist/ui/forms/ConsentScopesList.svelte +109 -0
- package/dist/ui/forms/ConsentScopesList.svelte.d.ts +30 -0
- package/dist/ui/forms/ConsentScopesList.svelte.d.ts.map +1 -0
- package/dist/ui/forms/EmailCodeForm.svelte +224 -0
- package/dist/ui/forms/EmailCodeForm.svelte.d.ts +39 -0
- package/dist/ui/forms/EmailCodeForm.svelte.d.ts.map +1 -0
- package/dist/ui/forms/OrgSelector.svelte +95 -0
- package/dist/ui/forms/OrgSelector.svelte.d.ts +37 -0
- package/dist/ui/forms/OrgSelector.svelte.d.ts.map +1 -0
- package/dist/ui/forms/PasskeyConditionalInput.svelte +173 -0
- package/dist/ui/forms/PasskeyConditionalInput.svelte.d.ts +36 -0
- package/dist/ui/forms/PasskeyConditionalInput.svelte.d.ts.map +1 -0
- package/dist/ui/forms/QRCodeDisplay.svelte +122 -0
- package/dist/ui/forms/QRCodeDisplay.svelte.d.ts +27 -0
- package/dist/ui/forms/QRCodeDisplay.svelte.d.ts.map +1 -0
- package/dist/ui/forms/SocialLoginButtons.svelte +209 -0
- package/dist/ui/forms/SocialLoginButtons.svelte.d.ts +33 -0
- package/dist/ui/forms/SocialLoginButtons.svelte.d.ts.map +1 -0
- package/dist/ui/forms/UserCodeInput.svelte +183 -0
- package/dist/ui/forms/UserCodeInput.svelte.d.ts +34 -0
- package/dist/ui/forms/UserCodeInput.svelte.d.ts.map +1 -0
- package/dist/ui/forms/index.d.ts +13 -0
- package/dist/ui/forms/index.d.ts.map +1 -0
- package/dist/ui/forms/index.js +12 -0
- package/dist/ui/helpers/AuthError.svelte +124 -0
- package/dist/ui/helpers/AuthError.svelte.d.ts +26 -0
- package/dist/ui/helpers/AuthError.svelte.d.ts.map +1 -0
- package/dist/ui/helpers/AuthLoading.svelte +83 -0
- package/dist/ui/helpers/AuthLoading.svelte.d.ts +25 -0
- package/dist/ui/helpers/AuthLoading.svelte.d.ts.map +1 -0
- package/dist/ui/helpers/OTPInput.svelte +214 -0
- package/dist/ui/helpers/OTPInput.svelte.d.ts +34 -0
- package/dist/ui/helpers/OTPInput.svelte.d.ts.map +1 -0
- package/dist/ui/helpers/ResendCodeButton.svelte +140 -0
- package/dist/ui/helpers/ResendCodeButton.svelte.d.ts +28 -0
- package/dist/ui/helpers/ResendCodeButton.svelte.d.ts.map +1 -0
- package/dist/ui/helpers/index.d.ts +8 -0
- package/dist/ui/helpers/index.d.ts.map +1 -0
- package/dist/ui/helpers/index.js +7 -0
- package/dist/ui/index.d.ts +43 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +48 -0
- package/dist/ui/passkey/PasskeyDeleteButton.svelte +177 -0
- package/dist/ui/passkey/PasskeyDeleteButton.svelte.d.ts +26 -0
- package/dist/ui/passkey/PasskeyDeleteButton.svelte.d.ts.map +1 -0
- package/dist/ui/passkey/PasskeyList.svelte +225 -0
- package/dist/ui/passkey/PasskeyList.svelte.d.ts +30 -0
- package/dist/ui/passkey/PasskeyList.svelte.d.ts.map +1 -0
- package/dist/ui/passkey/PasskeyRegisterButton.svelte +52 -0
- package/dist/ui/passkey/PasskeyRegisterButton.svelte.d.ts +38 -0
- package/dist/ui/passkey/PasskeyRegisterButton.svelte.d.ts.map +1 -0
- package/dist/ui/passkey/index.d.ts +7 -0
- package/dist/ui/passkey/index.d.ts.map +1 -0
- package/dist/ui/passkey/index.js +6 -0
- package/dist/ui/session/SessionExpiryIndicator.svelte +109 -0
- package/dist/ui/session/SessionExpiryIndicator.svelte.d.ts +23 -0
- package/dist/ui/session/SessionExpiryIndicator.svelte.d.ts.map +1 -0
- package/dist/ui/session/SessionList.svelte +231 -0
- package/dist/ui/session/SessionList.svelte.d.ts +31 -0
- package/dist/ui/session/SessionList.svelte.d.ts.map +1 -0
- package/dist/ui/session/SessionRevokeButton.svelte +72 -0
- package/dist/ui/session/SessionRevokeButton.svelte.d.ts +26 -0
- package/dist/ui/session/SessionRevokeButton.svelte.d.ts.map +1 -0
- package/dist/ui/session/index.d.ts +7 -0
- package/dist/ui/session/index.d.ts.map +1 -0
- package/dist/ui/session/index.js +6 -0
- package/dist/ui/shared/Alert.svelte +246 -0
- package/dist/ui/shared/Alert.svelte.d.ts +36 -0
- package/dist/ui/shared/Alert.svelte.d.ts.map +1 -0
- package/dist/ui/shared/Badge.svelte +100 -0
- package/dist/ui/shared/Badge.svelte.d.ts +35 -0
- package/dist/ui/shared/Badge.svelte.d.ts.map +1 -0
- package/dist/ui/shared/Button.svelte +213 -0
- package/dist/ui/shared/Button.svelte.d.ts +42 -0
- package/dist/ui/shared/Button.svelte.d.ts.map +1 -0
- package/dist/ui/shared/Card.svelte +85 -0
- package/dist/ui/shared/Card.svelte.d.ts +39 -0
- package/dist/ui/shared/Card.svelte.d.ts.map +1 -0
- package/dist/ui/shared/CountdownTimer.svelte +150 -0
- package/dist/ui/shared/CountdownTimer.svelte.d.ts +30 -0
- package/dist/ui/shared/CountdownTimer.svelte.d.ts.map +1 -0
- package/dist/ui/shared/Dialog.svelte +240 -0
- package/dist/ui/shared/Dialog.svelte.d.ts +39 -0
- package/dist/ui/shared/Dialog.svelte.d.ts.map +1 -0
- package/dist/ui/shared/Input.svelte +192 -0
- package/dist/ui/shared/Input.svelte.d.ts +42 -0
- package/dist/ui/shared/Input.svelte.d.ts.map +1 -0
- package/dist/ui/shared/LanguageSwitcher.svelte +99 -0
- package/dist/ui/shared/LanguageSwitcher.svelte.d.ts +31 -0
- package/dist/ui/shared/LanguageSwitcher.svelte.d.ts.map +1 -0
- package/dist/ui/shared/Spinner.svelte +75 -0
- package/dist/ui/shared/Spinner.svelte.d.ts +24 -0
- package/dist/ui/shared/Spinner.svelte.d.ts.map +1 -0
- package/dist/ui/shared/index.d.ts +13 -0
- package/dist/ui/shared/index.d.ts.map +1 -0
- package/dist/ui/shared/index.js +12 -0
- package/dist/ui/styles/base.css +168 -0
- package/dist/ui/styles/theme.css +279 -0
- package/dist/ui/templates/AccountSettingsTemplate.svelte +205 -0
- package/dist/ui/templates/AccountSettingsTemplate.svelte.d.ts +49 -0
- package/dist/ui/templates/AccountSettingsTemplate.svelte.d.ts.map +1 -0
- package/dist/ui/templates/CIBATemplate.svelte +227 -0
- package/dist/ui/templates/CIBATemplate.svelte.d.ts +45 -0
- package/dist/ui/templates/CIBATemplate.svelte.d.ts.map +1 -0
- package/dist/ui/templates/ConsentTemplate.svelte +549 -0
- package/dist/ui/templates/ConsentTemplate.svelte.d.ts +76 -0
- package/dist/ui/templates/ConsentTemplate.svelte.d.ts.map +1 -0
- package/dist/ui/templates/DeviceFlowTemplate.svelte +228 -0
- package/dist/ui/templates/DeviceFlowTemplate.svelte.d.ts +47 -0
- package/dist/ui/templates/DeviceFlowTemplate.svelte.d.ts.map +1 -0
- package/dist/ui/templates/LoginTemplate.svelte +234 -0
- package/dist/ui/templates/LoginTemplate.svelte.d.ts +49 -0
- package/dist/ui/templates/LoginTemplate.svelte.d.ts.map +1 -0
- package/dist/ui/templates/ReauthTemplate.svelte +269 -0
- package/dist/ui/templates/ReauthTemplate.svelte.d.ts +54 -0
- package/dist/ui/templates/ReauthTemplate.svelte.d.ts.map +1 -0
- package/dist/ui/templates/SignUpTemplate.svelte +345 -0
- package/dist/ui/templates/SignUpTemplate.svelte.d.ts +53 -0
- package/dist/ui/templates/SignUpTemplate.svelte.d.ts.map +1 -0
- package/dist/ui/templates/index.d.ts +14 -0
- package/dist/ui/templates/index.d.ts.map +1 -0
- package/dist/ui/templates/index.js +13 -0
- package/dist/ui/types.d.ts +151 -0
- package/dist/ui/types.d.ts.map +1 -0
- package/dist/ui/types.js +4 -0
- package/dist/utils/context.d.ts +12 -0
- package/dist/utils/context.d.ts.map +1 -0
- package/dist/utils/context.js +26 -0
- package/dist/utils/error-mapping.d.ts +29 -0
- package/dist/utils/error-mapping.d.ts.map +1 -0
- package/dist/utils/error-mapping.js +38 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +6 -0
- package/dist/utils/response.d.ts +21 -0
- package/dist/utils/response.d.ts.map +1 -0
- package/dist/utils/response.js +84 -0
- package/dist/utils/sensitive-data.d.ts +9 -0
- package/dist/utils/sensitive-data.d.ts.map +1 -0
- package/dist/utils/sensitive-data.js +56 -0
- package/dist/utils/ssr.d.ts +38 -0
- package/dist/utils/ssr.d.ts.map +1 -0
- package/dist/utils/ssr.js +73 -0
- package/dist/utils/webauthn-converters.d.ts +9 -0
- package/dist/utils/webauthn-converters.d.ts.map +1 -0
- package/dist/utils/webauthn-converters.js +75 -0
- package/package.json +111 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for client's event→store projection
|
|
3
|
+
*
|
|
4
|
+
* Design principle: "Events are source of truth, stores are projections"
|
|
5
|
+
* These tests verify that events properly update the stores.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
8
|
+
import { get } from 'svelte/store';
|
|
9
|
+
// Mock @authrim/core
|
|
10
|
+
vi.mock('@authrim/core', () => ({
|
|
11
|
+
PKCEHelper: vi.fn().mockImplementation(() => ({
|
|
12
|
+
generatePKCE: vi.fn().mockResolvedValue({
|
|
13
|
+
codeVerifier: 'mock-verifier',
|
|
14
|
+
codeChallenge: 'mock-challenge',
|
|
15
|
+
}),
|
|
16
|
+
})),
|
|
17
|
+
AuthrimError: class AuthrimError extends Error {
|
|
18
|
+
code;
|
|
19
|
+
meta;
|
|
20
|
+
constructor(code, message) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.code = code;
|
|
23
|
+
this.meta = { retryable: false, severity: 'error' };
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
}));
|
|
27
|
+
// Mock browser APIs
|
|
28
|
+
const mockLocalStorage = (() => {
|
|
29
|
+
let store = {};
|
|
30
|
+
return {
|
|
31
|
+
getItem: vi.fn((key) => store[key] || null),
|
|
32
|
+
setItem: vi.fn((key, value) => {
|
|
33
|
+
store[key] = value;
|
|
34
|
+
}),
|
|
35
|
+
removeItem: vi.fn((key) => {
|
|
36
|
+
delete store[key];
|
|
37
|
+
}),
|
|
38
|
+
clear: vi.fn(() => {
|
|
39
|
+
store = {};
|
|
40
|
+
}),
|
|
41
|
+
get length() {
|
|
42
|
+
return Object.keys(store).length;
|
|
43
|
+
},
|
|
44
|
+
key: vi.fn((index) => Object.keys(store)[index] || null),
|
|
45
|
+
};
|
|
46
|
+
})();
|
|
47
|
+
// Setup globals before importing modules
|
|
48
|
+
vi.stubGlobal('localStorage', mockLocalStorage);
|
|
49
|
+
vi.stubGlobal('sessionStorage', mockLocalStorage);
|
|
50
|
+
vi.stubGlobal('crypto', {
|
|
51
|
+
getRandomValues: (arr) => {
|
|
52
|
+
for (let i = 0; i < arr.length; i++) {
|
|
53
|
+
arr[i] = Math.floor(Math.random() * 256);
|
|
54
|
+
}
|
|
55
|
+
return arr;
|
|
56
|
+
},
|
|
57
|
+
subtle: {
|
|
58
|
+
digest: vi.fn().mockResolvedValue(new ArrayBuffer(32)),
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
// Import after mocking
|
|
62
|
+
import { createAuthStores, toAuthError } from '../stores/auth.js';
|
|
63
|
+
describe('Event→Store Projection', () => {
|
|
64
|
+
const mockSession = {
|
|
65
|
+
id: 'session-1',
|
|
66
|
+
userId: 'user-1',
|
|
67
|
+
createdAt: new Date().toISOString(),
|
|
68
|
+
expiresAt: new Date(Date.now() + 3600000).toISOString(),
|
|
69
|
+
};
|
|
70
|
+
const mockUser = {
|
|
71
|
+
id: 'user-1',
|
|
72
|
+
email: 'test@example.com',
|
|
73
|
+
name: 'Test User',
|
|
74
|
+
};
|
|
75
|
+
const mockError = {
|
|
76
|
+
code: 'AR001001',
|
|
77
|
+
error: 'network_error',
|
|
78
|
+
message: 'Network error occurred',
|
|
79
|
+
retryable: true,
|
|
80
|
+
severity: 'error',
|
|
81
|
+
};
|
|
82
|
+
beforeEach(() => {
|
|
83
|
+
mockLocalStorage.clear();
|
|
84
|
+
});
|
|
85
|
+
describe('auth:login event', () => {
|
|
86
|
+
it('should update session and user stores on login', () => {
|
|
87
|
+
const stores = createAuthStores();
|
|
88
|
+
// Simulate login event projection
|
|
89
|
+
stores._session.set(mockSession);
|
|
90
|
+
stores._user.set(mockUser);
|
|
91
|
+
stores._loadingState.set('idle');
|
|
92
|
+
stores._error.set(null);
|
|
93
|
+
expect(get(stores.public.session)).toEqual(mockSession);
|
|
94
|
+
expect(get(stores.public.user)).toEqual(mockUser);
|
|
95
|
+
expect(get(stores.public.isAuthenticated)).toBe(true);
|
|
96
|
+
expect(get(stores.public.loadingState)).toBe('idle');
|
|
97
|
+
expect(get(stores.public.error)).toBeNull();
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
describe('auth:logout event', () => {
|
|
101
|
+
it('should clear session and user stores on logout', () => {
|
|
102
|
+
const stores = createAuthStores();
|
|
103
|
+
// Setup: simulate logged in state
|
|
104
|
+
stores._session.set(mockSession);
|
|
105
|
+
stores._user.set(mockUser);
|
|
106
|
+
expect(get(stores.public.isAuthenticated)).toBe(true);
|
|
107
|
+
// Simulate logout event projection
|
|
108
|
+
stores._session.set(null);
|
|
109
|
+
stores._user.set(null);
|
|
110
|
+
stores._loadingState.set('idle');
|
|
111
|
+
stores._error.set(null);
|
|
112
|
+
expect(get(stores.public.session)).toBeNull();
|
|
113
|
+
expect(get(stores.public.user)).toBeNull();
|
|
114
|
+
expect(get(stores.public.isAuthenticated)).toBe(false);
|
|
115
|
+
expect(get(stores.public.loadingState)).toBe('idle');
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
describe('auth:error event', () => {
|
|
119
|
+
it('should set error store and return to idle', () => {
|
|
120
|
+
const stores = createAuthStores();
|
|
121
|
+
// Setup: simulate authenticating state
|
|
122
|
+
stores._loadingState.set('authenticating');
|
|
123
|
+
// Simulate error event projection
|
|
124
|
+
const authError = toAuthError(mockError);
|
|
125
|
+
stores._error.set(authError);
|
|
126
|
+
stores._loadingState.set('idle');
|
|
127
|
+
expect(get(stores.public.error)).toEqual(authError);
|
|
128
|
+
expect(get(stores.public.loadingState)).toBe('idle');
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
describe('session:changed event', () => {
|
|
132
|
+
it('should update session and user stores', () => {
|
|
133
|
+
const stores = createAuthStores();
|
|
134
|
+
// Simulate session changed event projection
|
|
135
|
+
stores._session.set(mockSession);
|
|
136
|
+
stores._user.set(mockUser);
|
|
137
|
+
expect(get(stores.public.session)).toEqual(mockSession);
|
|
138
|
+
expect(get(stores.public.user)).toEqual(mockUser);
|
|
139
|
+
});
|
|
140
|
+
it('should handle session cleared', () => {
|
|
141
|
+
const stores = createAuthStores();
|
|
142
|
+
// Setup: logged in
|
|
143
|
+
stores._session.set(mockSession);
|
|
144
|
+
stores._user.set(mockUser);
|
|
145
|
+
// Simulate session cleared
|
|
146
|
+
stores._session.set(null);
|
|
147
|
+
stores._user.set(null);
|
|
148
|
+
expect(get(stores.public.session)).toBeNull();
|
|
149
|
+
expect(get(stores.public.user)).toBeNull();
|
|
150
|
+
expect(get(stores.public.isAuthenticated)).toBe(false);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
describe('loadingState transitions', () => {
|
|
154
|
+
it('should transition: idle → authenticating → idle (success)', () => {
|
|
155
|
+
const stores = createAuthStores();
|
|
156
|
+
// Initial state
|
|
157
|
+
expect(get(stores.public.loadingState)).toBe('idle');
|
|
158
|
+
// Start authentication
|
|
159
|
+
stores._loadingState.set('authenticating');
|
|
160
|
+
expect(get(stores.public.loadingState)).toBe('authenticating');
|
|
161
|
+
// Complete (success)
|
|
162
|
+
stores._session.set(mockSession);
|
|
163
|
+
stores._user.set(mockUser);
|
|
164
|
+
stores._loadingState.set('idle');
|
|
165
|
+
expect(get(stores.public.loadingState)).toBe('idle');
|
|
166
|
+
expect(get(stores.public.isAuthenticated)).toBe(true);
|
|
167
|
+
});
|
|
168
|
+
it('should transition: idle → authenticating → idle (error)', () => {
|
|
169
|
+
const stores = createAuthStores();
|
|
170
|
+
// Initial state
|
|
171
|
+
expect(get(stores.public.loadingState)).toBe('idle');
|
|
172
|
+
// Start authentication
|
|
173
|
+
stores._loadingState.set('authenticating');
|
|
174
|
+
expect(get(stores.public.loadingState)).toBe('authenticating');
|
|
175
|
+
// Complete (error)
|
|
176
|
+
const authError = toAuthError(mockError);
|
|
177
|
+
stores._error.set(authError);
|
|
178
|
+
stores._loadingState.set('idle');
|
|
179
|
+
expect(get(stores.public.loadingState)).toBe('idle');
|
|
180
|
+
expect(get(stores.public.error)).toEqual(authError);
|
|
181
|
+
expect(get(stores.public.isAuthenticated)).toBe(false);
|
|
182
|
+
});
|
|
183
|
+
it('should transition: idle → signing_out → idle', () => {
|
|
184
|
+
const stores = createAuthStores();
|
|
185
|
+
// Setup: logged in
|
|
186
|
+
stores._session.set(mockSession);
|
|
187
|
+
stores._user.set(mockUser);
|
|
188
|
+
// Start sign out
|
|
189
|
+
stores._loadingState.set('signing_out');
|
|
190
|
+
expect(get(stores.public.loadingState)).toBe('signing_out');
|
|
191
|
+
// Complete sign out
|
|
192
|
+
stores._session.set(null);
|
|
193
|
+
stores._user.set(null);
|
|
194
|
+
stores._loadingState.set('idle');
|
|
195
|
+
expect(get(stores.public.loadingState)).toBe('idle');
|
|
196
|
+
expect(get(stores.public.isAuthenticated)).toBe(false);
|
|
197
|
+
});
|
|
198
|
+
it('should transition: idle → refreshing → idle', () => {
|
|
199
|
+
const stores = createAuthStores();
|
|
200
|
+
// Setup: logged in
|
|
201
|
+
stores._session.set(mockSession);
|
|
202
|
+
// Start refresh
|
|
203
|
+
stores._loadingState.set('refreshing');
|
|
204
|
+
expect(get(stores.public.loadingState)).toBe('refreshing');
|
|
205
|
+
// Complete refresh
|
|
206
|
+
stores._loadingState.set('idle');
|
|
207
|
+
expect(get(stores.public.loadingState)).toBe('idle');
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
describe('error clearing', () => {
|
|
211
|
+
it('should clear error on successful login', () => {
|
|
212
|
+
const stores = createAuthStores();
|
|
213
|
+
// Setup: error state
|
|
214
|
+
const authError = toAuthError(mockError);
|
|
215
|
+
stores._error.set(authError);
|
|
216
|
+
expect(get(stores.public.error)).not.toBeNull();
|
|
217
|
+
// Successful login clears error
|
|
218
|
+
stores._session.set(mockSession);
|
|
219
|
+
stores._user.set(mockUser);
|
|
220
|
+
stores._error.set(null);
|
|
221
|
+
stores._loadingState.set('idle');
|
|
222
|
+
expect(get(stores.public.error)).toBeNull();
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers.test.d.ts","sourceRoot":"","sources":["../../src/lib/__tests__/providers.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { BrowserCryptoProvider } from '../providers/crypto.js';
|
|
3
|
+
import { createBrowserStorage } from '../providers/storage.js';
|
|
4
|
+
describe('BrowserCryptoProvider', () => {
|
|
5
|
+
const crypto = new BrowserCryptoProvider();
|
|
6
|
+
it('should generate random bytes', async () => {
|
|
7
|
+
const bytes = await crypto.randomBytes(32);
|
|
8
|
+
expect(bytes).toBeInstanceOf(Uint8Array);
|
|
9
|
+
expect(bytes.length).toBe(32);
|
|
10
|
+
});
|
|
11
|
+
it('should compute SHA-256 hash', async () => {
|
|
12
|
+
const hash = await crypto.sha256('hello');
|
|
13
|
+
expect(hash).toBeInstanceOf(Uint8Array);
|
|
14
|
+
expect(hash.length).toBe(32);
|
|
15
|
+
});
|
|
16
|
+
it('should generate code verifier', async () => {
|
|
17
|
+
const verifier = await crypto.generateCodeVerifier();
|
|
18
|
+
expect(typeof verifier).toBe('string');
|
|
19
|
+
expect(verifier.length).toBeGreaterThanOrEqual(43);
|
|
20
|
+
});
|
|
21
|
+
it('should generate code challenge from verifier', async () => {
|
|
22
|
+
const verifier = await crypto.generateCodeVerifier();
|
|
23
|
+
const challenge = await crypto.generateCodeChallenge(verifier);
|
|
24
|
+
expect(typeof challenge).toBe('string');
|
|
25
|
+
expect(challenge.length).toBeGreaterThan(0);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
describe('createBrowserStorage', () => {
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
// Clear storage before each test
|
|
31
|
+
if (typeof sessionStorage !== 'undefined') {
|
|
32
|
+
sessionStorage.clear();
|
|
33
|
+
}
|
|
34
|
+
if (typeof localStorage !== 'undefined') {
|
|
35
|
+
localStorage.clear();
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
describe('memory storage', () => {
|
|
39
|
+
it('should store and retrieve values', async () => {
|
|
40
|
+
const storage = createBrowserStorage({ storage: 'memory' });
|
|
41
|
+
await storage.set('key', 'value');
|
|
42
|
+
const result = await storage.get('key');
|
|
43
|
+
expect(result).toBe('value');
|
|
44
|
+
});
|
|
45
|
+
it('should remove values', async () => {
|
|
46
|
+
const storage = createBrowserStorage({ storage: 'memory' });
|
|
47
|
+
await storage.set('key', 'value');
|
|
48
|
+
await storage.remove('key');
|
|
49
|
+
const result = await storage.get('key');
|
|
50
|
+
expect(result).toBeNull();
|
|
51
|
+
});
|
|
52
|
+
it('should get all values', async () => {
|
|
53
|
+
const storage = createBrowserStorage({ storage: 'memory' });
|
|
54
|
+
await storage.set('key1', 'value1');
|
|
55
|
+
await storage.set('key2', 'value2');
|
|
56
|
+
const all = await storage.getAll();
|
|
57
|
+
expect(all).toEqual({ key1: 'value1', key2: 'value2' });
|
|
58
|
+
});
|
|
59
|
+
it('should clear all values', async () => {
|
|
60
|
+
const storage = createBrowserStorage({ storage: 'memory' });
|
|
61
|
+
await storage.set('key1', 'value1');
|
|
62
|
+
await storage.set('key2', 'value2');
|
|
63
|
+
await storage.clear();
|
|
64
|
+
const all = await storage.getAll();
|
|
65
|
+
expect(all).toEqual({});
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response.test.d.ts","sourceRoot":"","sources":["../../src/lib/__tests__/response.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { success, failure, failureFromParams, toAuthError, authResultToResponse, } from '../utils/response.js';
|
|
3
|
+
describe('response utilities', () => {
|
|
4
|
+
describe('success', () => {
|
|
5
|
+
it('should create success response', () => {
|
|
6
|
+
const data = { id: 1, name: 'Test' };
|
|
7
|
+
const result = success(data);
|
|
8
|
+
expect(result.data).toEqual(data);
|
|
9
|
+
expect(result.error).toBeNull();
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
describe('failure', () => {
|
|
13
|
+
it('should create failure response', () => {
|
|
14
|
+
const error = {
|
|
15
|
+
code: 'AR001001',
|
|
16
|
+
error: 'network_error',
|
|
17
|
+
message: 'Network error',
|
|
18
|
+
retryable: true,
|
|
19
|
+
severity: 'error',
|
|
20
|
+
};
|
|
21
|
+
const result = failure(error);
|
|
22
|
+
expect(result.data).toBeNull();
|
|
23
|
+
expect(result.error).toEqual(error);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
describe('failureFromParams', () => {
|
|
27
|
+
it('should create failure response from params', () => {
|
|
28
|
+
const result = failureFromParams({
|
|
29
|
+
code: 'AR001001',
|
|
30
|
+
error: 'network_error',
|
|
31
|
+
message: 'Network error',
|
|
32
|
+
retryable: true,
|
|
33
|
+
severity: 'error',
|
|
34
|
+
});
|
|
35
|
+
expect(result.data).toBeNull();
|
|
36
|
+
expect(result.error?.code).toBe('AR001001');
|
|
37
|
+
expect(result.error?.message).toBe('Network error');
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
describe('toAuthError', () => {
|
|
41
|
+
it('should convert DirectAuthError', () => {
|
|
42
|
+
const directAuthError = {
|
|
43
|
+
code: 'AR003001',
|
|
44
|
+
error: 'passkey_not_found',
|
|
45
|
+
error_description: 'No passkey found',
|
|
46
|
+
meta: {
|
|
47
|
+
retryable: false,
|
|
48
|
+
severity: 'warn',
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
const authError = toAuthError(directAuthError);
|
|
52
|
+
expect(authError.code).toBe('AR003001');
|
|
53
|
+
expect(authError.error).toBe('passkey_not_found');
|
|
54
|
+
expect(authError.message).toBe('No passkey found');
|
|
55
|
+
expect(authError.retryable).toBe(false);
|
|
56
|
+
expect(authError.severity).toBe('warn');
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe('authResultToResponse', () => {
|
|
60
|
+
it('should convert successful AuthResult', () => {
|
|
61
|
+
const result = {
|
|
62
|
+
success: true,
|
|
63
|
+
session: {
|
|
64
|
+
id: 'session-1',
|
|
65
|
+
userId: 'user-1',
|
|
66
|
+
createdAt: new Date().toISOString(),
|
|
67
|
+
expiresAt: new Date(Date.now() + 3600000).toISOString(),
|
|
68
|
+
},
|
|
69
|
+
user: {
|
|
70
|
+
id: 'user-1',
|
|
71
|
+
email: 'test@example.com',
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
const response = authResultToResponse(result);
|
|
75
|
+
expect(response.data).toBeDefined();
|
|
76
|
+
expect(response.data?.session).toEqual(result.session);
|
|
77
|
+
expect(response.data?.user).toEqual(result.user);
|
|
78
|
+
expect(response.error).toBeNull();
|
|
79
|
+
});
|
|
80
|
+
it('should convert failed AuthResult', () => {
|
|
81
|
+
const result = {
|
|
82
|
+
success: false,
|
|
83
|
+
error: {
|
|
84
|
+
code: 'AR003001',
|
|
85
|
+
error: 'passkey_not_found',
|
|
86
|
+
error_description: 'No passkey found',
|
|
87
|
+
meta: {
|
|
88
|
+
retryable: false,
|
|
89
|
+
severity: 'warn',
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
const response = authResultToResponse(result);
|
|
94
|
+
expect(response.data).toBeNull();
|
|
95
|
+
expect(response.error).toBeDefined();
|
|
96
|
+
expect(response.error?.code).toBe('AR003001');
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stores.test.d.ts","sourceRoot":"","sources":["../../src/lib/__tests__/stores.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { get } from 'svelte/store';
|
|
3
|
+
import { createAuthStores, toAuthError } from '../stores/auth.js';
|
|
4
|
+
describe('createAuthStores', () => {
|
|
5
|
+
it('should create stores with initial values', () => {
|
|
6
|
+
const stores = createAuthStores();
|
|
7
|
+
expect(get(stores.public.session)).toBeNull();
|
|
8
|
+
expect(get(stores.public.user)).toBeNull();
|
|
9
|
+
expect(get(stores.public.isAuthenticated)).toBe(false);
|
|
10
|
+
expect(get(stores.public.loadingState)).toBe('idle');
|
|
11
|
+
expect(get(stores.public.error)).toBeNull();
|
|
12
|
+
});
|
|
13
|
+
it('should update session store', () => {
|
|
14
|
+
const stores = createAuthStores();
|
|
15
|
+
const mockSession = {
|
|
16
|
+
id: 'session-1',
|
|
17
|
+
userId: 'user-1',
|
|
18
|
+
createdAt: new Date().toISOString(),
|
|
19
|
+
expiresAt: new Date(Date.now() + 3600000).toISOString(),
|
|
20
|
+
};
|
|
21
|
+
stores._session.set(mockSession);
|
|
22
|
+
expect(get(stores.public.session)).toEqual(mockSession);
|
|
23
|
+
expect(get(stores.public.isAuthenticated)).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
it('should update user store', () => {
|
|
26
|
+
const stores = createAuthStores();
|
|
27
|
+
const mockUser = {
|
|
28
|
+
id: 'user-1',
|
|
29
|
+
email: 'test@example.com',
|
|
30
|
+
name: 'Test User',
|
|
31
|
+
};
|
|
32
|
+
stores._user.set(mockUser);
|
|
33
|
+
expect(get(stores.public.user)).toEqual(mockUser);
|
|
34
|
+
});
|
|
35
|
+
it('should update loading state', () => {
|
|
36
|
+
const stores = createAuthStores();
|
|
37
|
+
stores._loadingState.set('authenticating');
|
|
38
|
+
expect(get(stores.public.loadingState)).toBe('authenticating');
|
|
39
|
+
stores._loadingState.set('idle');
|
|
40
|
+
expect(get(stores.public.loadingState)).toBe('idle');
|
|
41
|
+
});
|
|
42
|
+
it('should update error store', () => {
|
|
43
|
+
const stores = createAuthStores();
|
|
44
|
+
const error = {
|
|
45
|
+
code: 'AR001001',
|
|
46
|
+
message: 'Network error',
|
|
47
|
+
};
|
|
48
|
+
stores._error.set(error);
|
|
49
|
+
expect(get(stores.public.error)).toEqual(error);
|
|
50
|
+
});
|
|
51
|
+
it('should derive isAuthenticated from session', () => {
|
|
52
|
+
const stores = createAuthStores();
|
|
53
|
+
expect(get(stores.public.isAuthenticated)).toBe(false);
|
|
54
|
+
stores._session.set({
|
|
55
|
+
id: 'session-1',
|
|
56
|
+
userId: 'user-1',
|
|
57
|
+
createdAt: new Date().toISOString(),
|
|
58
|
+
expiresAt: new Date(Date.now() + 3600000).toISOString(),
|
|
59
|
+
});
|
|
60
|
+
expect(get(stores.public.isAuthenticated)).toBe(true);
|
|
61
|
+
stores._session.set(null);
|
|
62
|
+
expect(get(stores.public.isAuthenticated)).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
describe('toAuthError', () => {
|
|
66
|
+
it('should convert DirectAuthError to AuthError', () => {
|
|
67
|
+
const directAuthError = {
|
|
68
|
+
code: 'AR001001',
|
|
69
|
+
error: 'network_error',
|
|
70
|
+
error_description: 'Network request failed',
|
|
71
|
+
meta: {
|
|
72
|
+
retryable: true,
|
|
73
|
+
severity: 'error',
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
const authError = toAuthError(directAuthError);
|
|
77
|
+
expect(authError.code).toBe('AR001001');
|
|
78
|
+
expect(authError.message).toBe('Network request failed');
|
|
79
|
+
});
|
|
80
|
+
it('should handle Error instances', () => {
|
|
81
|
+
const error = new Error('Something went wrong');
|
|
82
|
+
const authError = toAuthError(error);
|
|
83
|
+
expect(authError.code).toBe('UNKNOWN_ERROR');
|
|
84
|
+
expect(authError.message).toBe('Something went wrong');
|
|
85
|
+
});
|
|
86
|
+
it('should handle unknown errors', () => {
|
|
87
|
+
const authError = toAuthError('Unknown error string');
|
|
88
|
+
expect(authError.code).toBe('UNKNOWN_ERROR');
|
|
89
|
+
expect(authError.message).toBe('Unknown error string');
|
|
90
|
+
});
|
|
91
|
+
});
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authrim Svelte SDK Main Entry Point
|
|
3
|
+
*/
|
|
4
|
+
import type { AuthrimConfig, AuthrimClient } from "./types.js";
|
|
5
|
+
/**
|
|
6
|
+
* Create Authrim client for Svelte
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { createAuthrim } from '@authrim/sveltekit';
|
|
11
|
+
*
|
|
12
|
+
* const auth = await createAuthrim({
|
|
13
|
+
* issuer: 'https://auth.example.com',
|
|
14
|
+
* clientId: 'your-client-id',
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* // Passkey login
|
|
18
|
+
* const { data, error } = await auth.passkey.login();
|
|
19
|
+
*
|
|
20
|
+
* // Reactive stores
|
|
21
|
+
* const { session, user, isAuthenticated } = auth.stores;
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare function createAuthrim(config: AuthrimConfig): Promise<AuthrimClient>;
|
|
25
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/lib/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAYH,OAAO,KAAK,EACV,aAAa,EACb,aAAa,EAgBd,MAAM,YAAY,CAAC;AAmEpB;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,aAAa,CAAC,CAkbxB"}
|