@svadmin/core 0.25.1 → 0.25.3
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/package.json +6 -2
- package/src/index.ts +2 -0
- package/src/types.ts +21 -13
- package/src/README.md +0 -18
- package/src/auth-hooks.test.ts +0 -205
- package/src/chatProvider.test.ts +0 -388
- package/src/data-transfer.test.ts +0 -118
- package/src/form-hooks.test.svelte.ts +0 -83
- package/src/helpers.test.ts +0 -177
- package/src/http-error.test.ts +0 -85
- package/src/i18n.test.ts +0 -90
- package/src/inferencer-openapi.test.ts +0 -192
- package/src/inferencer.test.ts +0 -189
- package/src/live.test.ts +0 -134
- package/src/mutation-hooks.test.svelte.ts +0 -114
- package/src/permissions.test.ts +0 -136
- package/src/query-hooks.test.svelte.ts +0 -65
- package/src/reactive-hooks.test.svelte.ts +0 -26
- package/src/resource-types.test.ts +0 -79
- package/src/router-provider.test.ts +0 -82
- package/src/routing-hooks.test.svelte.ts +0 -55
- package/src/table-hooks.test.svelte.ts +0 -57
- package/src/task-hooks.test.svelte.ts +0 -129
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svadmin/core",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.3",
|
|
4
4
|
"author": "zuohuadong",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -63,7 +63,11 @@
|
|
|
63
63
|
},
|
|
64
64
|
"description": "Core SDK — hooks, types, context, i18n, permissions, router",
|
|
65
65
|
"files": [
|
|
66
|
-
"src
|
|
66
|
+
"src/**/*.ts",
|
|
67
|
+
"!src/**/*.test.*",
|
|
68
|
+
"!src/**/*.spec.*",
|
|
69
|
+
"!src/**/vitest.config.*",
|
|
70
|
+
"!src/**/setupTest.*"
|
|
67
71
|
],
|
|
68
72
|
"homepage": "https://github.com/zuohuadong/svadmin#readme",
|
|
69
73
|
"license": "MIT",
|
package/src/index.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -194,6 +194,9 @@ export interface DataProvider {
|
|
|
194
194
|
|
|
195
195
|
// ─── TaskProvider ─────────────────────────────────────────────
|
|
196
196
|
|
|
197
|
+
export type TaskDateValue = string | Date | null;
|
|
198
|
+
export type TaskMessageValue = string | null;
|
|
199
|
+
|
|
197
200
|
export interface TaskRecord extends BaseRecord {
|
|
198
201
|
id: string;
|
|
199
202
|
name?: string;
|
|
@@ -203,7 +206,7 @@ export interface TaskRecord extends BaseRecord {
|
|
|
203
206
|
priority?: number;
|
|
204
207
|
queueName?: string;
|
|
205
208
|
queue_name?: string;
|
|
206
|
-
message?:
|
|
209
|
+
message?: TaskMessageValue;
|
|
207
210
|
payload?: unknown;
|
|
208
211
|
/**
|
|
209
212
|
* Task output payload. `result` is the canonical camelCase field used by
|
|
@@ -213,16 +216,21 @@ export interface TaskRecord extends BaseRecord {
|
|
|
213
216
|
*/
|
|
214
217
|
result?: unknown;
|
|
215
218
|
result_data?: unknown;
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
219
|
+
/**
|
|
220
|
+
* Task lifecycle timestamps. Supabase/Postgres mirrors often expose nullable
|
|
221
|
+
* columns for lifecycle phases that have not happened yet, and svadmin UI
|
|
222
|
+
* helpers intentionally skip nullish values with `??` fallback ordering.
|
|
223
|
+
*/
|
|
224
|
+
createdAt?: TaskDateValue;
|
|
225
|
+
created_at?: TaskDateValue;
|
|
226
|
+
updatedAt?: TaskDateValue;
|
|
227
|
+
updated_at?: TaskDateValue;
|
|
228
|
+
startedAt?: TaskDateValue;
|
|
229
|
+
started_at?: TaskDateValue;
|
|
230
|
+
finishedAt?: TaskDateValue;
|
|
231
|
+
finished_at?: TaskDateValue;
|
|
232
|
+
cancelledAt?: TaskDateValue;
|
|
233
|
+
cancelled_at?: TaskDateValue;
|
|
226
234
|
/**
|
|
227
235
|
* Task failure payload/message. `error` carries structured error details
|
|
228
236
|
* when available; `errorMessage` and `error_message` are string fallbacks for
|
|
@@ -230,8 +238,8 @@ export interface TaskRecord extends BaseRecord {
|
|
|
230
238
|
* `errorMessage`, then `error_message`, matching `resolveTaskError`.
|
|
231
239
|
*/
|
|
232
240
|
error?: unknown;
|
|
233
|
-
errorMessage?:
|
|
234
|
-
error_message?:
|
|
241
|
+
errorMessage?: TaskMessageValue;
|
|
242
|
+
error_message?: TaskMessageValue;
|
|
235
243
|
}
|
|
236
244
|
|
|
237
245
|
export interface SubmitTaskOptions {
|
package/src/README.md
DELETED
package/src/auth-hooks.test.ts
DELETED
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Auth Hooks — Unit Tests
|
|
3
|
-
*
|
|
4
|
-
* These test the auth hook *logic* by mocking the Svelte context module
|
|
5
|
-
* so `getAuthProvider()` returns a mock provider without needing a real component tree.
|
|
6
|
-
*/
|
|
7
|
-
import { describe, test, expect, mock, beforeEach } from 'bun:test';
|
|
8
|
-
import type { AuthProvider, AuthActionResult, CheckResult, Identity } from './types';
|
|
9
|
-
|
|
10
|
-
// Build a mock AuthProvider factory
|
|
11
|
-
function createMockAuthProvider(overrides: Partial<AuthProvider> = {}): AuthProvider {
|
|
12
|
-
return {
|
|
13
|
-
login: mock(async () => ({ success: true, redirectTo: '/' } as AuthActionResult)),
|
|
14
|
-
logout: mock(async () => ({ success: true, redirectTo: '/login' } as AuthActionResult)),
|
|
15
|
-
check: mock(async () => ({ authenticated: true } as CheckResult)),
|
|
16
|
-
getIdentity: mock(async () => ({ id: '1', name: 'Admin', email: 'admin@test.com' } as Identity)),
|
|
17
|
-
register: mock(async () => ({ success: true } as AuthActionResult)),
|
|
18
|
-
forgotPassword: mock(async () => ({ success: true } as AuthActionResult)),
|
|
19
|
-
updatePassword: mock(async () => ({ success: true } as AuthActionResult)),
|
|
20
|
-
onError: mock(async () => ({ logout: false })),
|
|
21
|
-
getPermissions: mock(async () => ['admin', 'editor']),
|
|
22
|
-
...overrides,
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// ─── Tests for AuthProvider mock interface ───────────────────
|
|
27
|
-
|
|
28
|
-
describe('AuthProvider mock interface', () => {
|
|
29
|
-
test('login returns success', async () => {
|
|
30
|
-
const provider = createMockAuthProvider();
|
|
31
|
-
const result = await provider.login({ email: 'test@test.com', password: '123' });
|
|
32
|
-
expect(result.success).toBe(true);
|
|
33
|
-
expect(result.redirectTo).toBe('/');
|
|
34
|
-
expect(provider.login).toHaveBeenCalledTimes(1);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
test('login returns failure', async () => {
|
|
38
|
-
const provider = createMockAuthProvider({
|
|
39
|
-
login: mock(async () => ({ success: false, error: { message: 'Invalid credentials' } })),
|
|
40
|
-
});
|
|
41
|
-
const result = await provider.login({ email: 'bad', password: 'bad' });
|
|
42
|
-
expect(result.success).toBe(false);
|
|
43
|
-
expect(result.error?.message).toBe('Invalid credentials');
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test('logout returns success with redirect', async () => {
|
|
47
|
-
const provider = createMockAuthProvider();
|
|
48
|
-
const result = await provider.logout!();
|
|
49
|
-
expect(result.success).toBe(true);
|
|
50
|
-
expect(result.redirectTo).toBe('/login');
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
test('check returns authenticated', async () => {
|
|
54
|
-
const provider = createMockAuthProvider();
|
|
55
|
-
const result = await provider.check();
|
|
56
|
-
expect(result.authenticated).toBe(true);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test('check returns not authenticated', async () => {
|
|
60
|
-
const provider = createMockAuthProvider({
|
|
61
|
-
check: mock(async () => ({ authenticated: false, redirectTo: '/login' })),
|
|
62
|
-
});
|
|
63
|
-
const result = await provider.check();
|
|
64
|
-
expect(result.authenticated).toBe(false);
|
|
65
|
-
expect(result.redirectTo).toBe('/login');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('getIdentity returns user info', async () => {
|
|
69
|
-
const provider = createMockAuthProvider();
|
|
70
|
-
const identity = await provider.getIdentity();
|
|
71
|
-
expect(identity).not.toBeNull();
|
|
72
|
-
expect(identity!.name).toBe('Admin');
|
|
73
|
-
expect(identity!.email).toBe('admin@test.com');
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test('getIdentity returns null when not logged in', async () => {
|
|
77
|
-
const provider = createMockAuthProvider({
|
|
78
|
-
getIdentity: mock(async () => null),
|
|
79
|
-
});
|
|
80
|
-
const identity = await provider.getIdentity();
|
|
81
|
-
expect(identity).toBeNull();
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
test('register returns success', async () => {
|
|
85
|
-
const provider = createMockAuthProvider();
|
|
86
|
-
const result = await provider.register!({ email: 'new@test.com', password: '123' });
|
|
87
|
-
expect(result.success).toBe(true);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
test('forgotPassword returns success', async () => {
|
|
91
|
-
const provider = createMockAuthProvider();
|
|
92
|
-
const result = await provider.forgotPassword!({ email: 'test@test.com' });
|
|
93
|
-
expect(result.success).toBe(true);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test('updatePassword returns success', async () => {
|
|
97
|
-
const provider = createMockAuthProvider();
|
|
98
|
-
const result = await provider.updatePassword!({ password: 'newpass', confirmPassword: 'newpass' });
|
|
99
|
-
expect(result.success).toBe(true);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test('onError with logout flag', async () => {
|
|
103
|
-
const provider = createMockAuthProvider({
|
|
104
|
-
onError: mock(async () => ({ logout: true, redirectTo: '/login' })),
|
|
105
|
-
});
|
|
106
|
-
const result = await provider.onError!(new Error('401'));
|
|
107
|
-
expect(result.logout).toBe(true);
|
|
108
|
-
expect(result.redirectTo).toBe('/login');
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
test('onError without logout', async () => {
|
|
112
|
-
const provider = createMockAuthProvider({
|
|
113
|
-
onError: mock(async () => ({ logout: false, redirectTo: '/error' })),
|
|
114
|
-
});
|
|
115
|
-
const result = await provider.onError!(new Error('500'));
|
|
116
|
-
expect(result.logout).toBe(false);
|
|
117
|
-
expect(result.redirectTo).toBe('/error');
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
test('getPermissions returns permissions array', async () => {
|
|
121
|
-
const provider = createMockAuthProvider();
|
|
122
|
-
const permissions = await provider.getPermissions!();
|
|
123
|
-
expect(permissions).toEqual(['admin', 'editor']);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
test('getPermissions can return custom structure', async () => {
|
|
127
|
-
const provider = createMockAuthProvider({
|
|
128
|
-
getPermissions: mock(async () => ({ role: 'admin', canDelete: true })),
|
|
129
|
-
});
|
|
130
|
-
const permissions = await provider.getPermissions!() as { role: string; canDelete: boolean };
|
|
131
|
-
expect(permissions.role).toBe('admin');
|
|
132
|
-
expect(permissions.canDelete).toBe(true);
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
// ─── Tests for auth logic patterns ──────────────────────────
|
|
137
|
-
|
|
138
|
-
describe('auth hook patterns', () => {
|
|
139
|
-
test('login flow: success triggers redirect', async () => {
|
|
140
|
-
const provider = createMockAuthProvider();
|
|
141
|
-
const result = await provider.login({ email: 'admin@test.com', password: 'pass' });
|
|
142
|
-
expect(result.success).toBe(true);
|
|
143
|
-
// In real hook: navigate(result.redirectTo)
|
|
144
|
-
expect(result.redirectTo).toBeDefined();
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
test('login flow: failure shows error', async () => {
|
|
148
|
-
const provider = createMockAuthProvider({
|
|
149
|
-
login: mock(async () => ({ success: false, error: { message: 'Bad password' } })),
|
|
150
|
-
});
|
|
151
|
-
const result = await provider.login({ email: 'admin@test.com', password: 'wrong' });
|
|
152
|
-
expect(result.success).toBe(false);
|
|
153
|
-
// In real hook: toast.error(result.error.message)
|
|
154
|
-
expect(result.error?.message).toBe('Bad password');
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
test('login flow: exception produces error result', async () => {
|
|
158
|
-
const provider = createMockAuthProvider({
|
|
159
|
-
login: mock(async () => { throw new Error('Network Error'); }),
|
|
160
|
-
});
|
|
161
|
-
try {
|
|
162
|
-
await provider.login({ email: 'test', password: 'test' });
|
|
163
|
-
expect(true).toBe(false); // should not reach
|
|
164
|
-
} catch (err) {
|
|
165
|
-
expect((err as Error).message).toBe('Network Error');
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
test('onError flow: 401 triggers logout + redirect', async () => {
|
|
170
|
-
const provider = createMockAuthProvider({
|
|
171
|
-
onError: mock(async (err: unknown) => {
|
|
172
|
-
if (err instanceof Error && err.message.includes('401')) {
|
|
173
|
-
return { logout: true, redirectTo: '/login' };
|
|
174
|
-
}
|
|
175
|
-
return { logout: false };
|
|
176
|
-
}),
|
|
177
|
-
});
|
|
178
|
-
const result = await provider.onError!(new Error('401 Unauthorized'));
|
|
179
|
-
expect(result.logout).toBe(true);
|
|
180
|
-
// In real hook: provider.logout() then navigate(result.redirectTo)
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
test('onError flow: 500 does not trigger logout', async () => {
|
|
184
|
-
const provider = createMockAuthProvider({
|
|
185
|
-
onError: mock(async (err: unknown) => {
|
|
186
|
-
if (err instanceof Error && err.message.includes('401')) {
|
|
187
|
-
return { logout: true, redirectTo: '/login' };
|
|
188
|
-
}
|
|
189
|
-
return { logout: false };
|
|
190
|
-
}),
|
|
191
|
-
});
|
|
192
|
-
const result = await provider.onError!(new Error('500 Internal Server Error'));
|
|
193
|
-
expect(result.logout).toBe(false);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
test('check → getIdentity flow', async () => {
|
|
197
|
-
const provider = createMockAuthProvider();
|
|
198
|
-
const checkResult = await provider.check();
|
|
199
|
-
expect(checkResult.authenticated).toBe(true);
|
|
200
|
-
if (checkResult.authenticated) {
|
|
201
|
-
const identity = await provider.getIdentity();
|
|
202
|
-
expect(identity!.name).toBe('Admin');
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
});
|