@svadmin/core 0.30.0 → 0.32.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svadmin/core",
3
- "version": "0.30.0",
3
+ "version": "0.32.0",
4
4
  "author": "zuohuadong",
5
5
  "repository": {
6
6
  "type": "git",
@@ -10,9 +10,9 @@
10
10
  "main": "src/index.ts",
11
11
  "devDependencies": {
12
12
  "@happy-dom/global-registrator": "^20.10.0",
13
- "@sveltejs/vite-plugin-svelte": "^7.1.2",
13
+ "@sveltejs/vite-plugin-svelte": "^7.2.0",
14
14
  "@testing-library/dom": "^10.4.1",
15
- "@testing-library/svelte": "^5.3.1",
15
+ "@testing-library/svelte": "^5.4.2",
16
16
  "@testing-library/user-event": "^14.6.1",
17
17
  "@types/bun": "^1.3.14",
18
18
  "@types/jsdom": "^28.0.3",
@@ -38,8 +38,8 @@
38
38
  "default": "./src/i18n.svelte.ts"
39
39
  },
40
40
  "./permissions": {
41
- "types": "./src/permissions.ts",
42
- "default": "./src/permissions.ts"
41
+ "types": "./src/permissions.svelte.ts",
42
+ "default": "./src/permissions.svelte.ts"
43
43
  },
44
44
  "./toast": {
45
45
  "types": "./src/toast.svelte.ts",
@@ -2,10 +2,10 @@
2
2
  // Each hook encapsulates the auth call + loading state + error handling + redirect
3
3
  // Uses module-level $state — no component init-time constraints.
4
4
 
5
- import { getAuthProvider } from './context.svelte';
6
- import { navigate } from './router';
5
+ import { captureAdminContext } from './context.svelte';
6
+ import type { AdminContextAccessor } from './context.svelte';
7
7
  import { notify } from './notification.svelte';
8
- import { t } from './i18n.svelte';
8
+ import { t, useTranslation } from './i18n.svelte';
9
9
  import type { AuthActionResult, CheckResult, Identity, AuthProvider } from './types';
10
10
  import { useQueryClient } from '@tanstack/svelte-query';
11
11
 
@@ -19,14 +19,16 @@ interface CreateAuthMutationOptions {
19
19
  method: keyof AuthProvider;
20
20
  successMessage?: string | null;
21
21
  errorMessage?: string | false;
22
- onSuccess?: (result: AuthActionResult) => void;
22
+ onSuccess?: (result: AuthActionResult, adminContext: AdminContextAccessor) => void | Promise<void>;
23
23
  }
24
24
 
25
25
  function createAuthMutation(options: CreateAuthMutationOptions) {
26
- const provider = getAuthProvider();
26
+ const adminContext = captureAdminContext();
27
+ const i18n = useTranslation();
27
28
  let isLoading = $state(false);
28
29
 
29
30
  async function mutate(params?: Record<string, unknown>): Promise<AuthActionResult> {
31
+ const provider = adminContext.authProvider;
30
32
  if (!provider) throw new Error('AuthProvider not configured');
31
33
  const fn = provider[options.method] as ((params?: Record<string, unknown>) => Promise<AuthActionResult>) | undefined;
32
34
  if (!fn) throw new Error(`AuthProvider.${options.method} not implemented`);
@@ -36,14 +38,14 @@ function createAuthMutation(options: CreateAuthMutationOptions) {
36
38
  const result = await fn.call(provider, params);
37
39
  if (result.success) {
38
40
  if (options.successMessage) notify({ type: 'success', message: options.successMessage });
39
- if (options.onSuccess) options.onSuccess(result);
41
+ if (options.onSuccess) await options.onSuccess(result, adminContext);
40
42
  } else {
41
- const msg = result.error?.message ?? (typeof options.errorMessage === 'string' ? options.errorMessage : t('common.operationFailed'));
43
+ const msg = result.error?.message ?? (typeof options.errorMessage === 'string' ? options.errorMessage : i18n.t('common.operationFailed'));
42
44
  if (options.errorMessage !== false) notify({ type: 'error', message: msg });
43
45
  }
44
46
  return result;
45
47
  } catch (err) {
46
- const msg = err instanceof Error ? err.message : (typeof options.errorMessage === 'string' ? options.errorMessage : t('common.operationFailed'));
48
+ const msg = err instanceof Error ? err.message : (typeof options.errorMessage === 'string' ? options.errorMessage : i18n.t('common.operationFailed'));
47
49
  if (options.errorMessage !== false) notify({ type: 'error', message: msg });
48
50
  return { success: false, error: { message: msg } };
49
51
  } finally {
@@ -64,23 +66,26 @@ export function useLogin(opts?: { errorMessage?: string | false }) {
64
66
  method: 'login',
65
67
  successMessage: t('common.operationSuccess'),
66
68
  errorMessage: opts?.errorMessage ?? t('common.loginFailed'),
67
- onSuccess: (result) => { navigate(result.redirectTo ?? '/'); }
69
+ onSuccess: async (result, adminContext) => { await adminContext.navigate(result.redirectTo ?? '/'); }
68
70
  });
69
71
  }
70
72
 
71
73
  // ─── useLogout ────────────────────────────────────────────────
72
74
 
73
75
  export function useLogout() {
76
+ let queryClient: ReturnType<typeof useQueryClient> | undefined;
77
+ try {
78
+ queryClient = useQueryClient();
79
+ } catch {
80
+ // Auth hooks remain usable without TanStack Query; cache clearing is best-effort.
81
+ }
74
82
  return createAuthMutation({
75
83
  method: 'logout',
76
84
  successMessage: null,
77
- onSuccess: (result) => {
85
+ onSuccess: async (result, adminContext) => {
78
86
  _logoutVersion++;
79
- try {
80
- const queryClient = useQueryClient();
81
- queryClient.clear();
82
- } catch { /* intentional */}
83
- navigate(result.redirectTo ?? '/login');
87
+ queryClient?.clear();
88
+ await adminContext.navigate(result.redirectTo ?? '/login');
84
89
  }
85
90
  });
86
91
  }
@@ -91,7 +96,9 @@ export function useRegister() {
91
96
  return createAuthMutation({
92
97
  method: 'register',
93
98
  successMessage: t('auth.registerSuccess'),
94
- onSuccess: (result) => { if (result.redirectTo) navigate(result.redirectTo); }
99
+ onSuccess: async (result, adminContext) => {
100
+ if (result.redirectTo) await adminContext.navigate(result.redirectTo);
101
+ }
95
102
  });
96
103
  }
97
104
 
@@ -110,19 +117,44 @@ export function useUpdatePassword() {
110
117
  return createAuthMutation({
111
118
  method: 'updatePassword',
112
119
  successMessage: t('common.operationSuccess'),
113
- onSuccess: (result) => { if (result.redirectTo) navigate(result.redirectTo); }
120
+ onSuccess: async (result, adminContext) => {
121
+ if (result.redirectTo) await adminContext.navigate(result.redirectTo);
122
+ }
123
+ });
124
+ }
125
+
126
+ // ─── useUpdateIdentity / useUpdateProfile ────────────────────
127
+
128
+ export function useUpdateIdentity() {
129
+ return createAuthMutation({
130
+ method: 'updateIdentity',
131
+ successMessage: t('common.operationSuccess'),
132
+ onSuccess: async (result, adminContext) => {
133
+ if (result.redirectTo) await adminContext.navigate(result.redirectTo);
134
+ }
135
+ });
136
+ }
137
+
138
+ export function useUpdateProfile() {
139
+ return createAuthMutation({
140
+ method: 'updateProfile',
141
+ successMessage: t('common.operationSuccess'),
142
+ onSuccess: async (result, adminContext) => {
143
+ if (result.redirectTo) await adminContext.navigate(result.redirectTo);
144
+ }
114
145
  });
115
146
  }
116
147
 
117
148
  // ─── useGetIdentity ──────────────────────────────────────────
118
149
 
119
150
  export function useGetIdentity() {
120
- const provider = getAuthProvider({ optional: true });
151
+ const adminContext = captureAdminContext();
121
152
  let data = $state<Identity | null>(null);
122
153
  let isLoading = $state(true);
123
154
  let error = $state<Error | null>(null);
124
155
 
125
156
  function fetch() {
157
+ const provider = adminContext.authProvider;
126
158
  if (!provider) { isLoading = false; return; }
127
159
  isLoading = true;
128
160
  provider.getIdentity().then(identity => {
@@ -154,12 +186,13 @@ export function useGetIdentity() {
154
186
  // ─── useIsAuthenticated ──────────────────────────────────────
155
187
 
156
188
  export function useIsAuthenticated() {
157
- const provider = getAuthProvider({ optional: true });
189
+ const adminContext = captureAdminContext();
158
190
  let isAuthenticated = $state(false);
159
191
  let isLoading = $state(true);
160
192
  let checkResult = $state<CheckResult | null>(null);
161
193
 
162
194
  function check() {
195
+ const provider = adminContext.authProvider;
163
196
  if (!provider) {
164
197
  isAuthenticated = true;
165
198
  isLoading = false;
@@ -209,9 +242,10 @@ export function useIsAuthenticated() {
209
242
  * If it returns { redirectTo }, navigates there.
210
243
  */
211
244
  export function useOnError() {
212
- const provider = getAuthProvider({ optional: true });
245
+ const adminContext = captureAdminContext();
213
246
 
214
247
  async function mutate(error: unknown) {
248
+ const provider = adminContext.authProvider;
215
249
  if (!provider?.onError) {
216
250
  console.warn('[svadmin] useOnError: authProvider.onError not implemented');
217
251
  return;
@@ -220,9 +254,9 @@ export function useOnError() {
220
254
  const result = await provider.onError(error);
221
255
  if (result.logout) {
222
256
  await provider.logout?.();
223
- navigate(result.redirectTo ?? '/login');
257
+ await adminContext.navigate(result.redirectTo ?? '/login');
224
258
  } else if (result.redirectTo) {
225
- navigate(result.redirectTo);
259
+ await adminContext.navigate(result.redirectTo);
226
260
  }
227
261
  } catch (err) {
228
262
  console.warn('[svadmin] useOnError failed:', err);
@@ -250,13 +284,14 @@ export function useOnError() {
250
284
  * ```
251
285
  */
252
286
  export function usePermissions<T = unknown>() {
253
- const provider = getAuthProvider();
287
+ const adminContext = captureAdminContext();
254
288
  let permissions = $state<T | null>(null);
255
289
  let isLoading = $state(true);
256
290
  let error = $state<Error | null>(null);
257
291
  let version = $state(0);
258
292
 
259
293
  function fetch() {
294
+ const provider = adminContext.authProvider;
260
295
  if (!provider?.getPermissions) { isLoading = false; return; }
261
296
  isLoading = true;
262
297
  error = null;
@@ -297,4 +332,3 @@ export function usePermissions<T = unknown>() {
297
332
  can: (resource: string, action: string): boolean => hasFn(`${resource}:${action}`),
298
333
  };
299
334
  }
300
-
@@ -1,8 +1,10 @@
1
- // Svelte 5 Context — module-level $state singletons (no setContext/getContext required)
2
- // This removes the constraint that hooks must be called at component init time.
1
+ // Svelte 5 Context — application trees use scoped context while the module-level
2
+ // setters remain as a compatibility facade for non-component consumers.
3
3
 
4
+ import { createContext } from 'svelte';
4
5
  import type { DataProvider, AuthProvider, ResourceDefinition, TaskProvider } from './types';
5
6
  import type { RouterProvider } from './router-provider';
7
+ import type { LiveProvider } from './live.svelte';
6
8
  import { resetNotificationProvider } from './notification.svelte';
7
9
  import { resetAuditLogProvider } from './audit';
8
10
  import { resetAccessControlProvider } from './permissions.svelte';
@@ -11,7 +13,15 @@ import { resetI18n } from './i18n.svelte';
11
13
  import { resetChatProvider } from './chatProvider.svelte';
12
14
  import { resetToast } from './toast.svelte';
13
15
  import { resetTheme } from './theme.svelte';
14
- import { resetRouter } from './router';
16
+ import {
17
+ currentPath as currentLegacyPath,
18
+ currentPathWithProvider,
19
+ formatLink as formatLegacyLink,
20
+ formatLinkWithProvider,
21
+ navigate as navigateLegacy,
22
+ navigateWithProvider,
23
+ resetRouter,
24
+ } from './router';
15
25
  import { resetUnsavedChanges } from './unsaved-changes.svelte';
16
26
  import { resetGlobalPath } from './useParsed.svelte';
17
27
  import { resetLogoutVersion } from './auth-hooks.svelte';
@@ -26,46 +36,176 @@ import { resetSidebarCollapsed } from './hooks.svelte';
26
36
  */
27
37
  export type DataProviderInput = DataProvider | Record<string, DataProvider>;
28
38
 
39
+ export interface AdminContextSource {
40
+ readonly dataProvider: DataProviderInput;
41
+ readonly authProvider?: AuthProvider | null;
42
+ readonly resources: ResourceDefinition[];
43
+ readonly routerProvider?: RouterProvider;
44
+ readonly liveProvider?: LiveProvider;
45
+ readonly taskProvider?: TaskProvider;
46
+ }
47
+
48
+ export interface AdminContextValue {
49
+ readonly providers: Record<string, DataProvider>;
50
+ readonly authProvider: AuthProvider | null;
51
+ readonly resources: ResourceDefinition[];
52
+ readonly routerProvider: RouterProvider | undefined;
53
+ readonly liveProvider: LiveProvider | undefined;
54
+ readonly taskProvider: TaskProvider | undefined;
55
+ }
56
+
57
+ /**
58
+ * Initialization-time snapshot of the owning AdminContext tree.
59
+ *
60
+ * The accessor keeps the scoped context object (whose fields are reactive
61
+ * getters) instead of calling Svelte's getContext from later DOM/query
62
+ * callbacks. When no scoped context exists, getters intentionally continue to
63
+ * read the module-level compatibility setters dynamically.
64
+ */
65
+ export interface AdminContextAccessor {
66
+ readonly providers: Record<string, DataProvider> | null;
67
+ readonly authProvider: AuthProvider | null;
68
+ readonly resources: ResourceDefinition[];
69
+ readonly routerProvider: RouterProvider | undefined;
70
+ readonly liveProvider: LiveProvider | undefined;
71
+ readonly taskProvider: TaskProvider | undefined;
72
+ getDataProvider(name?: string): DataProvider;
73
+ getDataProviderNames(): string[];
74
+ getDataProviderForResource(resourceName: string, overrideName?: string): DataProvider;
75
+ getResource(nameOrIdentifier: string): ResourceDefinition;
76
+ currentPath(): string;
77
+ formatLink(path: string): string;
78
+ navigate(path: string, options?: { replaceState?: boolean }): Promise<void>;
79
+ back(): void;
80
+ }
81
+
82
+ const [getRequiredAdminContext, setAdminContext] = createContext<AdminContextValue>();
83
+
84
+ function normalizeDataProviders(provider: DataProviderInput): Record<string, DataProvider> {
85
+ return isDataProvider(provider)
86
+ ? { default: provider }
87
+ : provider;
88
+ }
89
+
90
+ export function createAdminContext(source: AdminContextSource): AdminContextValue {
91
+ return {
92
+ get providers() { return normalizeDataProviders(source.dataProvider); },
93
+ get authProvider() { return source.authProvider ?? null; },
94
+ get resources() { return source.resources; },
95
+ get routerProvider() { return source.routerProvider; },
96
+ get liveProvider() { return source.liveProvider; },
97
+ get taskProvider() { return source.taskProvider; },
98
+ };
99
+ }
100
+
101
+ /** Provide request/tree-scoped admin state to all descendants. */
102
+ export function provideAdminContext(source: AdminContextSource): AdminContextValue {
103
+ const context = createAdminContext(source);
104
+ setAdminContext(context);
105
+ return context;
106
+ }
107
+
108
+ /** Return the current tree context when called during component initialization/render. */
109
+ export function getAdminContext(): AdminContextValue | undefined {
110
+ try {
111
+ return getRequiredAdminContext();
112
+ } catch {
113
+ return undefined;
114
+ }
115
+ }
116
+
29
117
  let providers = $state<Record<string, DataProvider> | null>(null);
30
118
 
119
+ /** Capture the current tree once so delayed callbacks never re-enter getContext. */
120
+ export function captureAdminContext(): AdminContextAccessor {
121
+ const scopedContext = getAdminContext();
122
+
123
+ const accessor: AdminContextAccessor = {
124
+ get providers() { return scopedContext ? scopedContext.providers : providers; },
125
+ get authProvider() { return scopedContext ? scopedContext.authProvider : authProvider; },
126
+ get resources() { return scopedContext ? scopedContext.resources : resources ?? []; },
127
+ get routerProvider() { return scopedContext ? scopedContext.routerProvider : routerProvider; },
128
+ get liveProvider() { return scopedContext ? scopedContext.liveProvider : liveProviderState; },
129
+ get taskProvider() { return scopedContext ? scopedContext.taskProvider : taskProviderState; },
130
+ getDataProvider(name) {
131
+ const activeProviders = accessor.providers;
132
+ if (!activeProviders) throw new Error('DataProvider not found. Did you call setDataProvider in App.svelte?');
133
+ const key = name ?? 'default';
134
+ const provider = activeProviders[key];
135
+ if (!provider) {
136
+ throw new Error(`DataProvider "${key}" not found. Available: ${Object.keys(activeProviders).join(', ')}`);
137
+ }
138
+ return provider;
139
+ },
140
+ getDataProviderNames() {
141
+ return accessor.providers ? Object.keys(accessor.providers) : [];
142
+ },
143
+ getDataProviderForResource(resourceName, overrideName) {
144
+ if (overrideName) {
145
+ try {
146
+ return accessor.getDataProvider(overrideName);
147
+ } catch {
148
+ // Preserve the historical fallback to the resource/default provider.
149
+ }
150
+ }
151
+ try {
152
+ const resource = accessor.getResource(resourceName);
153
+ const dataProviderName = resource.meta?.dataProviderName as string | undefined;
154
+ if (dataProviderName) return accessor.getDataProvider(dataProviderName);
155
+ } catch {
156
+ // Preserve the historical fallback when a resource is not registered.
157
+ }
158
+ return accessor.getDataProvider();
159
+ },
160
+ getResource(nameOrIdentifier) {
161
+ const resource = accessor.resources.find(
162
+ (candidate) => candidate.identifier === nameOrIdentifier || candidate.name === nameOrIdentifier,
163
+ );
164
+ if (!resource) throw new Error(`Resource "${nameOrIdentifier}" not found in resource definitions.`);
165
+ return resource;
166
+ },
167
+ currentPath() {
168
+ if (scopedContext) return currentPathWithProvider(scopedContext.routerProvider);
169
+ return currentLegacyPath();
170
+ },
171
+ formatLink(path) {
172
+ if (scopedContext) return formatLinkWithProvider(scopedContext.routerProvider, path);
173
+ return formatLegacyLink(path);
174
+ },
175
+ async navigate(path, options) {
176
+ if (scopedContext) {
177
+ await navigateWithProvider(scopedContext.routerProvider, path, options);
178
+ return;
179
+ }
180
+ await navigateLegacy(path, options);
181
+ },
182
+ back() {
183
+ const activeRouter = accessor.routerProvider;
184
+ if (activeRouter?.back) {
185
+ activeRouter.back();
186
+ } else if (typeof window !== 'undefined') {
187
+ window.history.back();
188
+ }
189
+ },
190
+ };
191
+
192
+ return accessor;
193
+ }
194
+
31
195
  export function setDataProvider(provider: DataProviderInput): void {
32
- if (isDataProvider(provider)) {
33
- providers = { default: provider } as Record<string, DataProvider>;
34
- } else {
35
- providers = provider as Record<string, DataProvider>;
36
- }
196
+ providers = normalizeDataProviders(provider);
37
197
  }
38
198
 
39
199
  export function getDataProvider(name?: string): DataProvider {
40
- if (!providers) throw new Error('DataProvider not found. Did you call setDataProvider in App.svelte?');
41
- const key = name ?? 'default';
42
- const provider = providers[key];
43
- if (!provider) {
44
- throw new Error(`DataProvider "${key}" not found. Available: ${Object.keys(providers).join(', ')}`);
45
- }
46
- return provider;
200
+ return captureAdminContext().getDataProvider(name);
47
201
  }
48
202
 
49
203
  export function getDataProviderNames(): string[] {
50
- return providers ? Object.keys(providers) : [];
204
+ return captureAdminContext().getDataProviderNames();
51
205
  }
52
206
 
53
207
  export function getDataProviderForResource(resourceName: string, overrideName?: string): DataProvider {
54
- if (overrideName) {
55
- try {
56
- return getDataProvider(overrideName);
57
- } catch {
58
- // ignore
59
- }
60
- }
61
- try {
62
- const resource = getResource(resourceName);
63
- const dpName = resource.meta?.dataProviderName as string | undefined;
64
- if (dpName) return getDataProvider(dpName);
65
- } catch {
66
- // ignore
67
- }
68
- return getDataProvider();
208
+ return captureAdminContext().getDataProviderForResource(resourceName, overrideName);
69
209
  }
70
210
 
71
211
  function isDataProvider(value: unknown): value is DataProvider {
@@ -88,8 +228,9 @@ export function setAuthProvider(provider: AuthProvider | null | undefined): void
88
228
  export function getAuthProvider(): AuthProvider;
89
229
  export function getAuthProvider(options: { optional: true }): AuthProvider | null;
90
230
  export function getAuthProvider(options?: { optional?: boolean }): AuthProvider | null {
91
- if (!authProvider && !options?.optional) throw new Error('AuthProvider not found. Did you call setAuthProvider in App.svelte?');
92
- return authProvider;
231
+ const activeAuthProvider = captureAdminContext().authProvider;
232
+ if (!activeAuthProvider && !options?.optional) throw new Error('AuthProvider not found. Did you call setAuthProvider in App.svelte?');
233
+ return activeAuthProvider;
93
234
  }
94
235
 
95
236
  // ─── Resources ──────────────────────────────────────────────────
@@ -101,39 +242,36 @@ export function setResources(newResources: ResourceDefinition[]): void {
101
242
  }
102
243
 
103
244
  export function getResources(): ResourceDefinition[] {
104
- return resources ?? [];
245
+ return captureAdminContext().resources;
105
246
  }
106
247
 
107
248
  export function getResource(nameOrIdentifier: string): ResourceDefinition {
108
- const rList = getResources();
109
- const resource = rList.find(r => r.identifier === nameOrIdentifier || r.name === nameOrIdentifier);
110
- if (!resource) throw new Error(`Resource "${nameOrIdentifier}" not found in resource definitions.`);
111
- return resource;
249
+ return captureAdminContext().getResource(nameOrIdentifier);
112
250
  }
113
251
 
114
252
  // ─── Router Provider ────────────────────────────────────────────
115
253
 
116
254
  let routerProvider = $state<RouterProvider | undefined>(undefined);
117
255
 
118
- export function setRouterProvider(provider: RouterProvider): void {
256
+ export function setRouterProvider(provider: RouterProvider | undefined): void {
119
257
  routerProvider = provider;
120
258
  }
121
259
 
122
260
  export function getRouterProvider(): RouterProvider | undefined {
123
- return routerProvider;
261
+ return captureAdminContext().routerProvider;
124
262
  }
125
263
 
126
264
 
127
265
  // ─── Live Provider ──────────────────────────────────────────────
128
266
 
129
- let liveProviderState = $state<import('./live.svelte').LiveProvider | undefined>(undefined);
267
+ let liveProviderState = $state<LiveProvider | undefined>(undefined);
130
268
 
131
- export function setLiveProvider(provider: import('./live.svelte').LiveProvider): void {
269
+ export function setLiveProvider(provider: LiveProvider): void {
132
270
  liveProviderState = provider;
133
271
  }
134
272
 
135
- export function getLiveProvider(): import('./live.svelte').LiveProvider | undefined {
136
- return liveProviderState;
273
+ export function getLiveProvider(): LiveProvider | undefined {
274
+ return captureAdminContext().liveProvider;
137
275
  }
138
276
 
139
277
  // ─── Task Provider ──────────────────────────────────────────────
@@ -147,10 +285,11 @@ export function setTaskProvider(provider: TaskProvider | undefined): void {
147
285
  export function getTaskProvider(): TaskProvider;
148
286
  export function getTaskProvider(options: { optional: true }): TaskProvider | undefined;
149
287
  export function getTaskProvider(options?: { optional?: boolean }): TaskProvider | undefined {
150
- if (!taskProviderState && !options?.optional) {
288
+ const activeTaskProvider = captureAdminContext().taskProvider;
289
+ if (!activeTaskProvider && !options?.optional) {
151
290
  throw new Error('TaskProvider not found. Did you call setTaskProvider in App.svelte?');
152
291
  }
153
- return taskProviderState;
292
+ return activeTaskProvider;
154
293
  }
155
294
 
156
295
  // ─── Reset — for testing / HMR ─────────────────────────────────