@svadmin/core 0.37.0 → 0.39.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.37.0",
3
+ "version": "0.39.0",
4
4
  "author": "zuohuadong",
5
5
  "repository": {
6
6
  "type": "git",
@@ -14,7 +14,7 @@
14
14
  "@testing-library/dom": "^10.4.1",
15
15
  "@testing-library/svelte": "^5.4.2",
16
16
  "@testing-library/user-event": "^14.6.4",
17
- "@types/bun": "^1.3.14",
17
+ "@types/bun": "^1.4.0",
18
18
  "@types/jsdom": "^30.0.0",
19
19
  "happy-dom": "^20.11.2",
20
20
  "jsdom": "^30.0.1",
package/src/audit.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  // Full AuditLogProvider interface
3
3
 
4
4
  export interface AuditEntry {
5
+ id?: string | number;
5
6
  timestamp: string;
6
7
  action: 'create' | 'update' | 'delete' | 'login' | 'logout';
7
8
  resource?: string;
@@ -11,6 +12,13 @@ export interface AuditEntry {
11
12
  data?: Record<string, unknown>;
12
13
  previousData?: Record<string, unknown>;
13
14
  meta?: Record<string, unknown>;
15
+ outcome?: 'success' | 'failure';
16
+ tenantId?: string | number;
17
+ requestId?: string;
18
+ traceId?: string;
19
+ ipAddress?: string;
20
+ userAgent?: string;
21
+ error?: { message: string; code?: string };
14
22
  }
15
23
 
16
24
  export type AuditHandler = (entry: AuditEntry) => void | Promise<void>;
@@ -22,9 +30,20 @@ export type AuditHandler = (entry: AuditEntry) => void | Promise<void>;
22
30
  * - update: update an existing entry (e.g. mark as reviewed)
23
31
  */
24
32
  export interface AuditLogProvider {
25
- create: (params: { resource: string; action: string; data?: Record<string, unknown>; previousData?: Record<string, unknown>; meta?: Record<string, unknown> }) => Promise<AuditEntry>;
33
+ create: (params: {
34
+ resource: string;
35
+ action: string;
36
+ timestamp?: string;
37
+ recordId?: string | number;
38
+ userId?: string;
39
+ outcome?: AuditEntry['outcome'];
40
+ data?: Record<string, unknown>;
41
+ previousData?: Record<string, unknown>;
42
+ meta?: Record<string, unknown>;
43
+ }) => Promise<AuditEntry>;
26
44
  get: (params: { resource: string; action?: string; meta?: Record<string, unknown>; author?: Record<string, unknown> }) => Promise<AuditEntry[]>;
27
- update: (params: { id: string | number; name: string; meta?: Record<string, unknown> }) => Promise<AuditEntry>;
45
+ /** @deprecated 合规审计应保持追加写入;仅为旧实现兼容保留。 */
46
+ update?: (params: { id: string | number; name: string; meta?: Record<string, unknown> }) => Promise<AuditEntry>;
28
47
  }
29
48
 
30
49
  let handler: AuditHandler = (entry) => {
@@ -68,6 +87,10 @@ export function auditWithProvider(
68
87
  provider.create({
69
88
  resource: entry.resource ?? '',
70
89
  action: entry.action,
90
+ timestamp: fullEntry.timestamp,
91
+ recordId: entry.recordId,
92
+ userId: entry.userId,
93
+ outcome: entry.outcome,
71
94
  data: entry.data ?? entry.details,
72
95
  previousData: entry.previousData,
73
96
  meta: entry.meta,
@@ -78,3 +101,38 @@ export function auditWithProvider(
78
101
  export function audit(entry: Omit<AuditEntry, 'timestamp'>): void {
79
102
  auditWithProvider(entry, auditLogProvider);
80
103
  }
104
+
105
+ /**
106
+ * 严格写入审计记录。与兼容的 best-effort `audit` 不同,此函数会等待所有
107
+ * 写入并传播失败,适合权限、凭据、策略等必须和业务结果一起验证的流程。
108
+ */
109
+ export async function writeAuditEntry(
110
+ entry: Omit<AuditEntry, 'timestamp'>,
111
+ provider: AuditLogProvider | null | undefined = auditLogProvider,
112
+ ): Promise<AuditEntry> {
113
+ const fullEntry: AuditEntry = { ...entry, timestamp: new Date().toISOString() };
114
+ if (!provider) {
115
+ throw new Error('Strict audit logging requires an AuditLogProvider.');
116
+ }
117
+ await handler(fullEntry);
118
+ return provider.create({
119
+ resource: entry.resource ?? '',
120
+ action: entry.action,
121
+ timestamp: fullEntry.timestamp,
122
+ recordId: entry.recordId,
123
+ userId: entry.userId,
124
+ outcome: entry.outcome,
125
+ data: entry.data ?? entry.details,
126
+ previousData: entry.previousData,
127
+ meta: {
128
+ ...(entry.meta ?? {}),
129
+ ...(entry.outcome === undefined ? {} : { outcome: entry.outcome }),
130
+ ...(entry.tenantId === undefined ? {} : { tenantId: entry.tenantId }),
131
+ ...(entry.requestId === undefined ? {} : { requestId: entry.requestId }),
132
+ ...(entry.traceId === undefined ? {} : { traceId: entry.traceId }),
133
+ ...(entry.ipAddress === undefined ? {} : { ipAddress: entry.ipAddress }),
134
+ ...(entry.userAgent === undefined ? {} : { userAgent: entry.userAgent }),
135
+ ...(entry.error === undefined ? {} : { error: entry.error }),
136
+ },
137
+ });
138
+ }
@@ -10,6 +10,7 @@ import type { AuthActionResult, CheckResult, Identity, AuthProvider } from './ty
10
10
  import { useQueryClient } from '@tanstack/svelte-query';
11
11
 
12
12
  let _logoutVersion = $state(0);
13
+ let nextAuthMutationInstanceId = 0;
13
14
  export function getLogoutVersion() { return _logoutVersion; }
14
15
  export function resetLogoutVersion() { _logoutVersion = 0; }
15
16
 
@@ -22,7 +23,21 @@ interface CreateAuthMutationOptions {
22
23
  onSuccess?: (result: AuthActionResult, adminContext: AdminContextAccessor) => void | Promise<void>;
23
24
  }
24
25
 
26
+ export interface AuthNotificationOptions {
27
+ /** Set to false when the page owns the success state. */
28
+ successNotification?: string | false;
29
+ errorNotification?: string | false;
30
+ }
31
+
32
+ function resolveSuccessMessage(
33
+ configured: string | false | undefined,
34
+ defaultMessage: string,
35
+ ): string | null {
36
+ return configured === false ? null : configured ?? defaultMessage;
37
+ }
38
+
25
39
  function createAuthMutation(options: CreateAuthMutationOptions) {
40
+ const mutationInstanceId = nextAuthMutationInstanceId++;
26
41
  const adminContext = captureAdminContext();
27
42
  const i18n = useTranslation();
28
43
  let isLoading = $state(false);
@@ -57,18 +72,31 @@ function createAuthMutation(options: CreateAuthMutationOptions) {
57
72
  try {
58
73
  const result = await fn.call(provider, params);
59
74
  if (!isActiveMutation(epoch, provider, tenantIdentity)) return result;
75
+ const eventKey = `auth:${mutationInstanceId}:${String(options.method)}:${epoch}`;
60
76
  if (result.success) {
61
- if (options.successMessage) sendNotification({ type: 'success', message: options.successMessage });
77
+ if (options.successMessage) {
78
+ sendNotification({
79
+ type: 'success',
80
+ message: options.successMessage,
81
+ key: `${eventKey}:success`,
82
+ });
83
+ }
62
84
  if (options.onSuccess) await options.onSuccess(result, adminContext);
63
85
  } else {
64
86
  const msg = result.error?.message ?? (typeof options.errorMessage === 'string' ? options.errorMessage : i18n.t('common.operationFailed'));
65
- if (options.errorMessage !== false) sendNotification({ type: 'error', message: msg });
87
+ if (options.errorMessage !== false) {
88
+ sendNotification({ type: 'error', message: msg, key: `${eventKey}:error` });
89
+ }
66
90
  }
67
91
  return result;
68
92
  } catch (err) {
69
93
  const msg = err instanceof Error ? err.message : (typeof options.errorMessage === 'string' ? options.errorMessage : i18n.t('common.operationFailed'));
70
94
  if (isActiveMutation(epoch, provider, tenantIdentity) && options.errorMessage !== false) {
71
- sendNotification({ type: 'error', message: msg });
95
+ sendNotification({
96
+ type: 'error',
97
+ message: msg,
98
+ key: `auth:${mutationInstanceId}:${String(options.method)}:${epoch}:error`,
99
+ });
72
100
  }
73
101
  return { success: false, error: { message: msg } };
74
102
  } finally {
@@ -100,11 +128,11 @@ function createAuthMutation(options: CreateAuthMutationOptions) {
100
128
 
101
129
  // ─── useLogin ─────────────────────────────────────────────────
102
130
 
103
- export function useLogin(opts?: { errorMessage?: string | false }) {
131
+ export function useLogin(opts?: AuthNotificationOptions & { errorMessage?: string | false }) {
104
132
  return createAuthMutation({
105
133
  method: 'login',
106
- successMessage: t('common.operationSuccess'),
107
- errorMessage: opts?.errorMessage ?? t('common.loginFailed'),
134
+ successMessage: resolveSuccessMessage(opts?.successNotification, t('common.operationSuccess')),
135
+ errorMessage: opts?.errorNotification ?? opts?.errorMessage ?? t('common.loginFailed'),
108
136
  onSuccess: async (result, adminContext) => { await adminContext.navigate(result.redirectTo ?? '/'); }
109
137
  });
110
138
  }
@@ -131,10 +159,11 @@ export function useLogout() {
131
159
 
132
160
  // ─── useRegister ──────────────────────────────────────────────
133
161
 
134
- export function useRegister() {
162
+ export function useRegister(opts?: AuthNotificationOptions) {
135
163
  return createAuthMutation({
136
164
  method: 'register',
137
- successMessage: t('auth.registerSuccess'),
165
+ successMessage: resolveSuccessMessage(opts?.successNotification, t('auth.registerSuccess')),
166
+ errorMessage: opts?.errorNotification,
138
167
  onSuccess: async (result, adminContext) => {
139
168
  if (result.redirectTo) await adminContext.navigate(result.redirectTo);
140
169
  }
@@ -143,19 +172,21 @@ export function useRegister() {
143
172
 
144
173
  // ─── useForgotPassword ───────────────────────────────────────
145
174
 
146
- export function useForgotPassword() {
175
+ export function useForgotPassword(opts?: AuthNotificationOptions) {
147
176
  return createAuthMutation({
148
177
  method: 'forgotPassword',
149
- successMessage: t('auth.resetLinkSent')
178
+ successMessage: resolveSuccessMessage(opts?.successNotification, t('auth.resetLinkSent')),
179
+ errorMessage: opts?.errorNotification,
150
180
  });
151
181
  }
152
182
 
153
183
  // ─── useUpdatePassword ───────────────────────────────────────
154
184
 
155
- export function useUpdatePassword() {
185
+ export function useUpdatePassword(opts?: AuthNotificationOptions) {
156
186
  return createAuthMutation({
157
187
  method: 'updatePassword',
158
- successMessage: t('common.operationSuccess'),
188
+ successMessage: resolveSuccessMessage(opts?.successNotification, t('common.operationSuccess')),
189
+ errorMessage: opts?.errorNotification,
159
190
  onSuccess: async (result, adminContext) => {
160
191
  if (result.redirectTo) await adminContext.navigate(result.redirectTo);
161
192
  }
@@ -164,20 +195,22 @@ export function useUpdatePassword() {
164
195
 
165
196
  // ─── useUpdateIdentity / useUpdateProfile ────────────────────
166
197
 
167
- export function useUpdateIdentity() {
198
+ export function useUpdateIdentity(opts?: AuthNotificationOptions) {
168
199
  return createAuthMutation({
169
200
  method: 'updateIdentity',
170
- successMessage: t('common.operationSuccess'),
201
+ successMessage: resolveSuccessMessage(opts?.successNotification, t('common.operationSuccess')),
202
+ errorMessage: opts?.errorNotification,
171
203
  onSuccess: async (result, adminContext) => {
172
204
  if (result.redirectTo) await adminContext.navigate(result.redirectTo);
173
205
  }
174
206
  });
175
207
  }
176
208
 
177
- export function useUpdateProfile() {
209
+ export function useUpdateProfile(opts?: AuthNotificationOptions) {
178
210
  return createAuthMutation({
179
211
  method: 'updateProfile',
180
- successMessage: t('common.operationSuccess'),
212
+ successMessage: resolveSuccessMessage(opts?.successNotification, t('common.operationSuccess')),
213
+ errorMessage: opts?.errorNotification,
181
214
  onSuccess: async (result, adminContext) => {
182
215
  if (result.redirectTo) await adminContext.navigate(result.redirectTo);
183
216
  }
@@ -8,6 +8,7 @@ import type { LiveProvider } from './live.svelte';
8
8
  import type { AccessControlProvider } from './permissions.svelte';
9
9
  import type { AuditLogProvider } from './audit';
10
10
  import type { AgentProvider, ChatProvider } from './chatProvider.svelte';
11
+ import type { EnterpriseRequestContext } from './enterprise';
11
12
  import {
12
13
  createTenantCacheKey,
13
14
  resolveTenantProviderMeta,
@@ -77,6 +78,10 @@ export interface AdminContextValue {
77
78
  readonly notificationProvider: ProviderBundle['notificationProvider'];
78
79
  readonly chatProvider: ChatProvider | null;
79
80
  readonly agentProvider: AgentProvider | null;
81
+ readonly organizationProvider: ProviderBundle['organizationProvider'];
82
+ readonly identityGovernanceProvider: ProviderBundle['identityGovernanceProvider'];
83
+ readonly sessionProvider: ProviderBundle['sessionProvider'];
84
+ readonly credentialProvider: ProviderBundle['credentialProvider'];
80
85
  readonly tenant: TenantContext | undefined;
81
86
  readonly tenantAdapter: TenantAdapter | undefined;
82
87
  }
@@ -102,9 +107,14 @@ export interface AdminContextAccessor {
102
107
  readonly notificationProvider: ProviderBundle['notificationProvider'];
103
108
  readonly chatProvider: ChatProvider | null;
104
109
  readonly agentProvider: AgentProvider | null;
110
+ readonly organizationProvider: ProviderBundle['organizationProvider'];
111
+ readonly identityGovernanceProvider: ProviderBundle['identityGovernanceProvider'];
112
+ readonly sessionProvider: ProviderBundle['sessionProvider'];
113
+ readonly credentialProvider: ProviderBundle['credentialProvider'];
105
114
  readonly tenant: TenantContext | undefined;
106
115
  readonly tenantAdapter: TenantAdapter | undefined;
107
116
  readonly tenantCacheKey: TenantCacheKey | undefined;
117
+ readonly enterpriseRequestContext: EnterpriseRequestContext;
108
118
  getDataProvider(name?: string): DataProvider;
109
119
  getDataProviderNames(): string[];
110
120
  resolveDataProviderName(resourceName: string, overrideName?: string): string;
@@ -166,6 +176,18 @@ function resolveProviderBundle(source: AdminContextSource): ProviderBundle {
166
176
  taskProvider: source.taskProvider ?? bundled?.taskProvider,
167
177
  routerProvider: source.routerProvider ?? bundled?.routerProvider,
168
178
  tenantAdapter: source.tenantAdapter ?? bundled?.tenantAdapter,
179
+ organizationProvider: source.organizationProvider !== undefined
180
+ ? source.organizationProvider
181
+ : bundled?.organizationProvider,
182
+ identityGovernanceProvider: source.identityGovernanceProvider !== undefined
183
+ ? source.identityGovernanceProvider
184
+ : bundled?.identityGovernanceProvider,
185
+ sessionProvider: source.sessionProvider !== undefined
186
+ ? source.sessionProvider
187
+ : bundled?.sessionProvider,
188
+ credentialProvider: source.credentialProvider !== undefined
189
+ ? source.credentialProvider
190
+ : bundled?.credentialProvider,
169
191
  };
170
192
  }
171
193
 
@@ -184,6 +206,10 @@ export function createAdminContext(source: AdminContextSource): AdminContextValu
184
206
  get notificationProvider() { return resolveProviderBundle(source).notificationProvider ?? null; },
185
207
  get chatProvider() { return resolveProviderBundle(source).chatProvider ?? null; },
186
208
  get agentProvider() { return resolveProviderBundle(source).agentProvider ?? null; },
209
+ get organizationProvider() { return resolveProviderBundle(source).organizationProvider ?? null; },
210
+ get identityGovernanceProvider() { return resolveProviderBundle(source).identityGovernanceProvider ?? null; },
211
+ get sessionProvider() { return resolveProviderBundle(source).sessionProvider ?? null; },
212
+ get credentialProvider() { return resolveProviderBundle(source).credentialProvider ?? null; },
187
213
  get tenant() { return source.tenant ?? inheritedTenant; },
188
214
  get tenantAdapter() { return resolveProviderBundle(source).tenantAdapter; },
189
215
  };
@@ -254,11 +280,24 @@ export function captureAdminContext(): AdminContextAccessor {
254
280
  },
255
281
  get chatProvider() { return scopedContext ? scopedContext.chatProvider : getLegacyChatProvider(); },
256
282
  get agentProvider() { return scopedContext ? scopedContext.agentProvider : getLegacyAgentProvider(); },
283
+ get organizationProvider() { return scopedContext?.organizationProvider ?? null; },
284
+ get identityGovernanceProvider() { return scopedContext?.identityGovernanceProvider ?? null; },
285
+ get sessionProvider() { return scopedContext?.sessionProvider ?? null; },
286
+ get credentialProvider() { return scopedContext?.credentialProvider ?? null; },
257
287
  get tenant() { return scopedContext?.tenant; },
258
288
  get tenantAdapter() { return scopedContext?.tenantAdapter; },
259
289
  get tenantCacheKey() {
260
290
  return accessor.tenant ? createTenantCacheKey(accessor.tenant, accessor.tenantAdapter) : undefined;
261
291
  },
292
+ get enterpriseRequestContext() {
293
+ const tenant = accessor.tenant;
294
+ return {
295
+ tenantId: tenant?.tenantId,
296
+ requestId: typeof tenant?.meta?.requestId === 'string' ? tenant.meta.requestId : undefined,
297
+ traceId: typeof tenant?.meta?.traceId === 'string' ? tenant.meta.traceId : undefined,
298
+ meta: tenant?.meta,
299
+ };
300
+ },
262
301
  getDataProvider(name) {
263
302
  const activeProviders = accessor.providers;
264
303
  if (!activeProviders) throw new Error('DataProvider not found. Did you call setDataProvider in App.svelte?');
@@ -0,0 +1,146 @@
1
+ /** 企业级管理能力的正式 Provider 契约。 */
2
+
3
+ export interface EnterpriseActionError {
4
+ message: string;
5
+ code?: string;
6
+ }
7
+
8
+ export interface EnterpriseActionResult {
9
+ success: boolean;
10
+ error?: EnterpriseActionError;
11
+ }
12
+
13
+ /** 每次企业能力调用都必须显式携带当前请求树的租户与追踪上下文。 */
14
+ export interface EnterpriseRequestContext {
15
+ tenantId?: string | number;
16
+ requestId?: string;
17
+ traceId?: string;
18
+ meta?: Readonly<Record<string, unknown>>;
19
+ }
20
+
21
+ export interface Organization {
22
+ id: string;
23
+ name: string;
24
+ slug?: string;
25
+ domain?: string;
26
+ metadata?: Record<string, unknown>;
27
+ }
28
+
29
+ export interface OrganizationProvider {
30
+ getCurrentOrganization: (context: EnterpriseRequestContext) => Promise<Organization | null>;
31
+ updateCurrentOrganization?: (
32
+ input: Partial<Omit<Organization, 'id'>>,
33
+ context: EnterpriseRequestContext,
34
+ ) => Promise<Organization>;
35
+ }
36
+
37
+ export interface EnterpriseSecurityPolicy {
38
+ sessionTimeoutMinutes: number;
39
+ auditRetentionDays: number;
40
+ auditLoggingEnabled: boolean;
41
+ requireSso: boolean;
42
+ }
43
+
44
+ export type IdentityProviderProtocol = 'oidc' | 'saml' | (string & {});
45
+ export type IdentityProviderStatus = 'connected' | 'disabled' | 'error' | (string & {});
46
+
47
+ export interface IdentityProviderSummary {
48
+ id: string;
49
+ name: string;
50
+ protocol: IdentityProviderProtocol;
51
+ status: IdentityProviderStatus;
52
+ metadataUrl?: string;
53
+ domain?: string;
54
+ }
55
+
56
+ export interface EnterpriseSecurityEvent {
57
+ id: string;
58
+ event: string;
59
+ actor?: string;
60
+ location?: string;
61
+ createdAt: string | Date;
62
+ severity?: 'info' | 'warning' | 'critical';
63
+ tenantId?: string | number;
64
+ requestId?: string;
65
+ traceId?: string;
66
+ metadata?: Record<string, unknown>;
67
+ }
68
+
69
+ export interface IdentityGovernanceProvider {
70
+ getSecurityPolicy: (context: EnterpriseRequestContext) => Promise<EnterpriseSecurityPolicy>;
71
+ updateSecurityPolicy: (
72
+ policy: EnterpriseSecurityPolicy,
73
+ context: EnterpriseRequestContext,
74
+ ) => Promise<EnterpriseSecurityPolicy>;
75
+ listIdentityProviders: (context: EnterpriseRequestContext) => Promise<IdentityProviderSummary[]>;
76
+ testIdentityProvider: (params: {
77
+ id?: string;
78
+ protocol?: IdentityProviderProtocol;
79
+ metadataUrl?: string;
80
+ }, context: EnterpriseRequestContext) => Promise<EnterpriseActionResult>;
81
+ listSecurityEvents?: (params: {
82
+ page?: number;
83
+ pageSize?: number;
84
+ } | undefined, context: EnterpriseRequestContext) => Promise<{ data: EnterpriseSecurityEvent[]; total: number }>;
85
+ }
86
+
87
+ export interface SessionInfo {
88
+ id: string;
89
+ os?: string;
90
+ browser?: string;
91
+ ipAddress?: string;
92
+ lastActiveAt?: string | Date;
93
+ current: boolean;
94
+ }
95
+
96
+ export interface MfaState {
97
+ enabled: boolean;
98
+ methods?: string[];
99
+ }
100
+
101
+ export interface SessionProvider {
102
+ listSessions: (context: EnterpriseRequestContext) => Promise<SessionInfo[]>;
103
+ revokeSession: (sessionId: string, context: EnterpriseRequestContext) => Promise<EnterpriseActionResult>;
104
+ revokeOtherSessions: (context: EnterpriseRequestContext) => Promise<EnterpriseActionResult>;
105
+ getMfaState?: (context: EnterpriseRequestContext) => Promise<MfaState>;
106
+ setMfaEnabled?: (enabled: boolean, context: EnterpriseRequestContext) => Promise<MfaState>;
107
+ }
108
+
109
+ export interface ApiCredentialSummary {
110
+ id: string;
111
+ name: string;
112
+ prefix: string;
113
+ createdAt: string | Date;
114
+ lastUsedAt?: string | Date;
115
+ permissions: string[];
116
+ }
117
+
118
+ export interface CreatedApiCredential {
119
+ credential: ApiCredentialSummary;
120
+ /** 只在创建时返回一次;列表接口不得返回此字段。 */
121
+ secret: string;
122
+ }
123
+
124
+ export interface WebhookSummary {
125
+ id: string;
126
+ name: string;
127
+ url: string;
128
+ eventType: string;
129
+ enabled?: boolean;
130
+ }
131
+
132
+ export interface CredentialProvider {
133
+ listApiCredentials: (context: EnterpriseRequestContext) => Promise<ApiCredentialSummary[]>;
134
+ createApiCredential: (params: {
135
+ name: string;
136
+ permissions: string[];
137
+ }, context: EnterpriseRequestContext) => Promise<CreatedApiCredential>;
138
+ revokeApiCredential: (credentialId: string, context: EnterpriseRequestContext) => Promise<EnterpriseActionResult>;
139
+ listWebhooks: (context: EnterpriseRequestContext) => Promise<WebhookSummary[]>;
140
+ createWebhook: (params: {
141
+ name: string;
142
+ url: string;
143
+ eventType: string;
144
+ }, context: EnterpriseRequestContext) => Promise<WebhookSummary>;
145
+ deleteWebhook: (webhookId: string, context: EnterpriseRequestContext) => Promise<EnterpriseActionResult>;
146
+ }
@@ -128,7 +128,12 @@ export function createLiveSubscription(paramsFn: () => LiveSubscriptionParams):
128
128
  export type NotificationConfig =
129
129
  | string
130
130
  | false
131
- | ((data?: unknown, values?: unknown, resource?: string) => { message: string; description?: string; type?: 'success' | 'error' })
131
+ | ((data?: unknown, values?: unknown, resource?: string) => {
132
+ message: string;
133
+ description?: string;
134
+ type?: 'success' | 'error';
135
+ key?: string;
136
+ })
132
137
  | undefined;
133
138
 
134
139
  export interface SuccessNotificationRequest {
@@ -154,7 +159,12 @@ export function fireSuccessNotification(request: SuccessNotificationRequest): vo
154
159
  if (!config && !defaultMessage) return;
155
160
  if (typeof config === 'function') {
156
161
  const result = config(data, values, resource);
157
- notifyWithProvider({ type: 'success', message: result.message, description: result.description }, provider);
162
+ notifyWithProvider({
163
+ type: 'success',
164
+ message: result.message,
165
+ description: result.description,
166
+ key: result.key,
167
+ }, provider);
158
168
  return;
159
169
  }
160
170
  notifyWithProvider({ type: 'success', message: config || defaultMessage }, provider);
@@ -166,7 +176,12 @@ export function fireErrorNotification(request: ErrorNotificationRequest): void {
166
176
  if (!config && !defaultMessage) return;
167
177
  if (typeof config === 'function') {
168
178
  const result = config(error, undefined, resource);
169
- notifyWithProvider({ type: 'error', message: result.message, description: result.description }, provider);
179
+ notifyWithProvider({
180
+ type: 'error',
181
+ message: result.message,
182
+ description: result.description,
183
+ key: result.key,
184
+ }, provider);
170
185
  return;
171
186
  }
172
187
  const errMsg = error instanceof Error ? error.message : String(error ?? '');
@@ -90,6 +90,7 @@ const baseLocales: Record<string, Record<string, string>> = {
90
90
  'common.toggleSidebar': '切换侧边栏',
91
91
  'common.configRequired': '需要配置',
92
92
  'common.removeImage': '移除图片',
93
+ 'common.skipToMainContent': '跳到主要内容',
93
94
  // Field
94
95
  'field.enterValue': '输入{label}',
95
96
  'field.selectPlaceholder': '请选择',
@@ -125,6 +126,8 @@ const baseLocales: Record<string, Record<string, string>> = {
125
126
  'auth.forgotPasswordDescription': '输入您的邮箱地址,我们将发送重置密码的链接。',
126
127
  'auth.resetPasswordDescription': '设置一个新的登录密码。',
127
128
  'auth.resetLinkSent': '重置链接已发送,请查看您的邮箱。',
129
+ 'auth.resetLinkSentTitle': '重置链接已发送',
130
+ 'auth.resetLinkSentDescription': '如果该账号存在,请按照邮件中的步骤重置密码。',
128
131
  'auth.passwordMismatch': '两次输入的密码不一致',
129
132
  'auth.emailRequired': '请输入邮箱',
130
133
  'auth.passwordRequired': '请输入密码',
@@ -719,6 +722,7 @@ const baseLocales: Record<string, Record<string, string>> = {
719
722
  'common.toggleSidebar': 'Toggle sidebar',
720
723
  'common.configRequired': 'Configuration Required',
721
724
  'common.removeImage': 'Remove image',
725
+ 'common.skipToMainContent': 'Skip to main content',
722
726
  // Field
723
727
  'field.enterValue': 'Enter {label}',
724
728
  'field.selectPlaceholder': 'Select...',
@@ -754,6 +758,8 @@ const baseLocales: Record<string, Record<string, string>> = {
754
758
  'auth.forgotPasswordDescription': 'Enter your email address and we will send you a reset link.',
755
759
  'auth.resetPasswordDescription': 'Set a new sign-in password.',
756
760
  'auth.resetLinkSent': 'Reset link sent! Check your email.',
761
+ 'auth.resetLinkSentTitle': 'Reset link sent',
762
+ 'auth.resetLinkSentDescription': 'If the account exists, follow the instructions in the email to reset the password.',
757
763
  'auth.passwordMismatch': 'Passwords do not match',
758
764
  'auth.emailRequired': 'Email is required',
759
765
  'auth.passwordRequired': 'Password is required',
package/src/index.ts CHANGED
@@ -84,8 +84,28 @@ export {
84
84
  export type { NotificationParams } from './notification.svelte';
85
85
  export { t, setLocale, getLocale, getAvailableLocales, addTranslations, useTranslation, setI18nProvider, getI18nProvider, createI18nScope, provideI18nScope, getI18nScope, resetI18n } from './i18n.svelte';
86
86
  export type { I18nProvider, I18nScope, I18nScopeOptions } from './i18n.svelte';
87
- export { audit, auditWithProvider, setAuditHandler, setAuditLogProvider, getAuditLogProvider } from './audit';
87
+ export { audit, auditWithProvider, writeAuditEntry, setAuditHandler, setAuditLogProvider, getAuditLogProvider } from './audit';
88
88
  export type { AuditLogProvider } from './audit';
89
+ export type {
90
+ ApiCredentialSummary,
91
+ CreatedApiCredential,
92
+ CredentialProvider,
93
+ EnterpriseActionError,
94
+ EnterpriseActionResult,
95
+ EnterpriseRequestContext,
96
+ EnterpriseSecurityEvent,
97
+ EnterpriseSecurityPolicy,
98
+ IdentityGovernanceProvider,
99
+ IdentityProviderProtocol,
100
+ IdentityProviderStatus,
101
+ IdentityProviderSummary,
102
+ MfaState,
103
+ Organization,
104
+ OrganizationProvider,
105
+ SessionInfo,
106
+ SessionProvider,
107
+ WebhookSummary,
108
+ } from './enterprise';
89
109
  export { setChatProvider, getChatProvider, setChatContext, getChatContext, setAgentProvider, getAgentProvider, registerApproval, resolveApproval, hasPendingApprovals, resetChatProvider } from './chatProvider.svelte';
90
110
  export type { ChatProvider, ChatMessage, ChatContext, ChatAction, AgentProvider, AgentEvent, AgentOptions, AdminTool, AdminToolParameter, ToolResult } from './chatProvider.svelte';
91
111
  export { getTheme, setTheme, toggleTheme, getResolvedTheme, getColorTheme, setColorTheme, getColorThemes, configureTheme, getThemeConfig, clearCssOverrides, builtinPresets, registerColorPreset, getColorPresets, registerThemeOwner, updateThemeOwner, unregisterThemeOwner, resetTheme } from './theme.svelte';
@@ -136,6 +156,7 @@ export {
136
156
  useOnError, usePermissions,
137
157
  getLogoutVersion, resetLogoutVersion,
138
158
  } from './auth-hooks.svelte';
159
+ export type { AuthNotificationOptions } from './auth-hooks.svelte';
139
160
  export {
140
161
  useSubmitTask,
141
162
  useTask,
@@ -6,6 +6,12 @@ import type { AuditLogProvider } from './audit';
6
6
  import type { AccessControlProvider } from './permissions.svelte';
7
7
  import type { LiveProvider } from './live.svelte';
8
8
  import type { RouterProvider } from './router-provider';
9
+ import type {
10
+ CredentialProvider,
11
+ IdentityGovernanceProvider,
12
+ OrganizationProvider,
13
+ SessionProvider,
14
+ } from './enterprise';
9
15
  import type {
10
16
  AuthProvider,
11
17
  BaseRecord,
@@ -52,6 +58,10 @@ export interface ProviderBundle {
52
58
  readonly taskProvider?: TaskProvider;
53
59
  readonly routerProvider?: RouterProvider;
54
60
  readonly tenantAdapter?: TenantAdapter;
61
+ readonly organizationProvider?: OrganizationProvider | null;
62
+ readonly identityGovernanceProvider?: IdentityGovernanceProvider | null;
63
+ readonly sessionProvider?: SessionProvider | null;
64
+ readonly credentialProvider?: CredentialProvider | null;
55
65
  }
56
66
 
57
67
  /** 保留传入对象的精确类型,同时执行 ProviderBundle 的编译期校验。 */