@azure-net/kit 3.9.1 → 3.9.2

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.
Files changed (41) hide show
  1. package/dist/core/delivery/index.d.ts +1 -4
  2. package/dist/core/delivery/index.js +1 -4
  3. package/dist/core/delivery/injectableDependencies/AsyncHelpers.d.ts +28 -36
  4. package/dist/core/delivery/injectableDependencies/AsyncHelpers.js +79 -137
  5. package/dist/core/delivery/injectableDependencies/ErrorHandler.d.ts +3 -21
  6. package/dist/core/delivery/injectableDependencies/ErrorHandler.js +11 -42
  7. package/dist/core/delivery/schema/Schema.d.ts +8 -1
  8. package/dist/core/delivery/schema/Schema.js +11 -2
  9. package/dist/core/delivery.d.ts +5 -0
  10. package/dist/core/delivery.js +5 -0
  11. package/dist/core/errors.d.ts +4 -0
  12. package/dist/core/errors.js +4 -0
  13. package/dist/core/index.d.ts +7 -8
  14. package/dist/core/index.js +7 -8
  15. package/dist/core/infra/httpService/HttpServiceInstance.d.ts +16 -8
  16. package/dist/core/infra/httpService/HttpServiceInstance.js +47 -49
  17. package/dist/core/infra/query/QueryBuilder.js +2 -1
  18. package/dist/core/infra/response/BaseResponse.js +3 -2
  19. package/dist/core/infra.d.ts +6 -0
  20. package/dist/core/infra.js +6 -0
  21. package/dist/core/shared/appError/AppError.d.ts +30 -0
  22. package/dist/core/shared/appError/AppError.js +66 -0
  23. package/dist/core/shared/appEventBus/EventBus.js +1 -1
  24. package/dist/core/shared/boundaryProvider/Provider.js +4 -3
  25. package/dist/core/shared/classMirror/ClassMirror.d.ts +7 -2
  26. package/dist/core/shared/classMirror/ClassMirror.js +34 -15
  27. package/dist/core/shared/cookie/UniversalCookie.js +8 -10
  28. package/dist/core/svelte/ActiveForm/ActiveForm.svelte.d.ts +6 -5
  29. package/dist/core/svelte/ActiveForm/ActiveForm.svelte.js +31 -26
  30. package/dist/core/svelte/AsyncSignal/AsyncSignal.svelte.d.ts +0 -1
  31. package/dist/core/svelte/AsyncSignal/AsyncSignal.svelte.js +21 -30
  32. package/dist/core/svelte/Query/Query.svelte.d.ts +11 -12
  33. package/dist/core/svelte/Query/Query.svelte.js +42 -54
  34. package/dist/core/ui/scrollTo/index.js +2 -2
  35. package/dist/edges/index.d.ts +1 -1
  36. package/dist/edges/index.js +1 -1
  37. package/package.json +10 -6
  38. package/dist/core/delivery/serverAction/CreateServerAction.d.ts +0 -15
  39. package/dist/core/delivery/serverAction/CreateServerAction.js +0 -22
  40. package/dist/core/delivery/serverAction/index.d.ts +0 -1
  41. package/dist/core/delivery/serverAction/index.js +0 -1
@@ -26,22 +26,26 @@ export class HttpServiceResponse {
26
26
  this.raw = raw;
27
27
  }
28
28
  }
29
- export class HttpServiceError {
30
- headers;
31
- status;
32
- success;
29
+ export var HttpErrorTypes;
30
+ (function (HttpErrorTypes) {
31
+ HttpErrorTypes["Internal"] = "internal";
32
+ HttpErrorTypes["External"] = "external";
33
+ })(HttpErrorTypes || (HttpErrorTypes = {}));
34
+ export class HttpServiceError extends Error {
33
35
  data;
36
+ status;
34
37
  message;
38
+ type;
35
39
  original;
36
- raw;
37
- constructor({ headers, status, success, data, message, original, raw }) {
38
- this.headers = headers;
40
+ response;
41
+ constructor({ status = 500, data, message = 'unknown error', original, type = HttpErrorTypes.Internal, response }) {
42
+ super(message);
39
43
  this.status = status;
40
- this.success = success;
41
44
  this.data = data;
42
45
  this.message = message;
43
46
  this.original = original;
44
- this.raw = raw;
47
+ this.response = response;
48
+ this.type = type;
45
49
  }
46
50
  }
47
51
  const stringifyJsonFunc = (data) => {
@@ -188,16 +192,39 @@ const withTimeout = (signal, timeout) => {
188
192
  };
189
193
  };
190
194
  const shouldRetryError = (error) => {
191
- if (error instanceof HttpServiceError) {
192
- return [408, 425, 429, 500, 502, 503, 504].includes(error.status);
193
- }
194
- return !(error instanceof Error && error.name === 'AbortError');
195
+ return error instanceof HttpServiceError && error.type === HttpErrorTypes.External;
195
196
  };
196
197
  const normalizeUnknownError = (error) => {
197
198
  if (error instanceof Error)
198
199
  return error;
199
200
  return new Error(String(error));
200
201
  };
202
+ const parseResponse = async (response, request) => {
203
+ const raw = request.includeRaw && typeof response?.clone === 'function' ? response?.clone() : undefined;
204
+ let data;
205
+ let parseError;
206
+ try {
207
+ data = await parseBodyByFormat(response, request.responseFormat);
208
+ }
209
+ catch (error) {
210
+ parseError = normalizeUnknownError(error);
211
+ data = undefined;
212
+ }
213
+ if (!response.ok || parseError) {
214
+ const message = !response.ok
215
+ ? `Request failed with status code ${response.status}. ${response.statusText}`
216
+ : (parseError?.message ?? 'Internal error');
217
+ throw new HttpServiceError({
218
+ status: response.status,
219
+ data,
220
+ message,
221
+ type: !response.ok ? HttpErrorTypes.External : HttpErrorTypes.Internal,
222
+ original: parseError,
223
+ response: raw ?? response
224
+ });
225
+ }
226
+ return { data, headers: Object.fromEntries(response.headers.entries()), status: response.status, raw };
227
+ };
201
228
  const defaultDoFetch = async (request) => {
202
229
  const maxAttempts = Math.max(1, Math.floor(request.retry) + 1);
203
230
  let lastError;
@@ -211,30 +238,14 @@ const defaultDoFetch = async (request) => {
211
238
  body: request.body,
212
239
  signal: timeoutState.signal ?? undefined
213
240
  });
214
- const rawResponse = request.includeRaw ? response.clone() : undefined;
215
- const data = await parseBodyByFormat(response, request.responseFormat);
216
- if (!response.ok) {
217
- const httpError = new HttpServiceError({
218
- headers: Object.fromEntries(response.headers.entries()),
219
- status: response.status,
220
- success: false,
221
- data,
222
- message: `Request failed with status code ${response.status}`,
223
- raw: rawResponse
224
- });
225
- lastError = httpError;
226
- if (attempt >= maxAttempts || !shouldRetryError(httpError)) {
227
- throw httpError;
228
- }
229
- continue;
230
- }
241
+ const { data, headers, status, raw } = await parseResponse(response, request);
231
242
  return new HttpServiceResponse({
232
- headers: Object.fromEntries(response.headers.entries()),
233
- status: response.status,
243
+ headers,
244
+ status,
234
245
  success: true,
235
246
  data,
236
247
  message: 'Request completed',
237
- raw: rawResponse
248
+ raw
238
249
  });
239
250
  }
240
251
  catch (error) {
@@ -303,28 +314,15 @@ const makeMethod = (method, defaults) => {
303
314
  const normalizedError = error instanceof HttpServiceError
304
315
  ? error
305
316
  : new HttpServiceError({
306
- headers: {},
307
- status: 500,
308
- success: false,
309
317
  data: undefined,
310
- message: normalizeUnknownError(error).message,
318
+ message: normalizeUnknownError(error).message ?? 'Unknown error',
311
319
  original: normalizeUnknownError(error)
312
320
  });
313
321
  const onError = options.onError ?? defaults.onError;
314
322
  if (!onError)
315
323
  throw normalizedError;
316
- const handledError = await onError(normalizedError, normalizedRequest);
317
- if (!(handledError instanceof HttpServiceError)) {
318
- throw new HttpServiceError({
319
- headers: normalizedError.headers,
320
- status: normalizedError.status,
321
- success: false,
322
- data: normalizedError.data,
323
- message: 'onError hook must return HttpServiceError',
324
- original: new Error(normalizedError.message)
325
- });
326
- }
327
- throw handledError;
324
+ await onError(normalizedError, normalizedRequest);
325
+ throw normalizedError;
328
326
  }
329
327
  };
330
328
  };
@@ -1,3 +1,4 @@
1
+ import { AzureNetKitInternalError } from '../../shared/appError/AppError.js';
1
2
  const normalizeSettings = (base, override) => {
2
3
  return {
3
4
  delimiter: override?.delimiter ?? base?.delimiter ?? true,
@@ -19,7 +20,7 @@ const encodeArrayEntries = (key, values, arrayFormat) => {
19
20
  case 'json':
20
21
  return [[key, JSON.stringify(cleanValues)]];
21
22
  default:
22
- throw new Error(`Unsupported array format: ${arrayFormat}`);
23
+ throw new AzureNetKitInternalError(`[QueryInstance] Unsupported array format: ${arrayFormat}`);
23
24
  }
24
25
  };
25
26
  const serializeEntries = (obj, arrayFormat, objectFormat, prefix = '') => {
@@ -1,3 +1,4 @@
1
+ import { AzureNetKitInternalError } from '../../shared/appError/AppError.js';
1
2
  export class ResponseBuilder {
2
3
  response;
3
4
  state;
@@ -22,7 +23,7 @@ export class ResponseBuilder {
22
23
  }
23
24
  mapCollectionUsing(ResourceClass) {
24
25
  if (!Array.isArray(this.state.data)) {
25
- throw new Error('toCollection can only be used when data is an array');
26
+ throw new AzureNetKitInternalError('[ResponseBuilder] Method "mapCollectionUsing" can only be used when data is an array');
26
27
  }
27
28
  const collection = this.state.data.map((dataElement) => new ResourceClass(dataElement).toPlainObject());
28
29
  const newResponse = new this.constructor(this.response);
@@ -40,7 +41,7 @@ export class ResponseBuilder {
40
41
  result = result[key];
41
42
  }
42
43
  else {
43
- throw new Error(`Path "${path}" not found in response data`);
44
+ throw new AzureNetKitInternalError(`[ResponseBuilder] Failed to extract: path "${path}" not found in response data`);
44
45
  }
45
46
  }
46
47
  const newResponse = new this.constructor(this.response);
@@ -0,0 +1,6 @@
1
+ export { createHttpServiceInstance, HttpInstanceFetchMethods, HttpErrorTypes, HttpServiceResponse, type IHttpServiceError, type IHttpServiceResponse, type IHttpServiceInstance, type IHttpInstanceOptions, type HttpInstanceNormalizedRequest, type HttpInstanceOnRequest, type HttpInstanceOnError, type HttpInstanceDoFetch, type HttpInstanceRequestMethod } from './infra/httpService/HttpServiceInstance.js';
2
+ export { HttpStatusCode } from './infra/httpService/Statuses.js';
3
+ export { BaseHttpDatasource, type CreateRequestCallbackType } from './infra/datasource/BaseDatasource.js';
4
+ export { ResponseBuilder } from './infra/response/BaseResponse.js';
5
+ export { createQueryInstance, type IQueryBuilderInstance, type ArrayFormat, type ObjectFormat, type IQueryBuilderSettings } from './infra/query/QueryBuilder.js';
6
+ export { DTOMapper, type IDTOMapper } from './infra/resource/BaseResource.js';
@@ -0,0 +1,6 @@
1
+ export { createHttpServiceInstance, HttpInstanceFetchMethods, HttpErrorTypes, HttpServiceResponse } from './infra/httpService/HttpServiceInstance.js';
2
+ export { HttpStatusCode } from './infra/httpService/Statuses.js';
3
+ export { BaseHttpDatasource } from './infra/datasource/BaseDatasource.js';
4
+ export { ResponseBuilder } from './infra/response/BaseResponse.js';
5
+ export { createQueryInstance } from './infra/query/QueryBuilder.js';
6
+ export { DTOMapper } from './infra/resource/BaseResource.js';
@@ -0,0 +1,30 @@
1
+ import { HttpServiceError } from '../../infra/httpService/HttpServiceInstance.js';
2
+ import { type RequestErrors } from '../../delivery/schema/Schema.js';
3
+ export declare enum ErrorTypes {
4
+ Http = "Http",
5
+ Schema = "Schema",
6
+ KitInternalError = "AzureNetKitInternal",
7
+ AsyncHelperError = "AsyncHelperError",
8
+ Unknown = "Unknown"
9
+ }
10
+ export declare class AzureNetKitInternalError extends Error {
11
+ }
12
+ export interface IAppError<T = unknown> {
13
+ type: ErrorTypes;
14
+ message: string;
15
+ validation?: RequestErrors<T>;
16
+ external: boolean;
17
+ appErrorConvert?: boolean;
18
+ }
19
+ export declare class AppError<T = unknown> extends Error {
20
+ error: Error;
21
+ type: ErrorTypes;
22
+ message: string;
23
+ external: boolean;
24
+ validation?: RequestErrors<T>;
25
+ constructor(error: Error);
26
+ tryToGetExternalError<HttpServiceDataType = unknown>(): HttpServiceError<HttpServiceDataType> | undefined;
27
+ redeclareValidation<D = T>(validation: D): void;
28
+ toPlainObject<CustomData extends object>(custom?: CustomData): IAppError<T> & CustomData;
29
+ private parseError;
30
+ }
@@ -0,0 +1,66 @@
1
+ import { HttpErrorTypes, HttpServiceError } from '../../infra/httpService/HttpServiceInstance.js';
2
+ import { SchemaFail } from '../../delivery/schema/Schema.js';
3
+ import { AsyncHelperError } from '../../delivery/injectableDependencies/AsyncHelpers.js';
4
+ export var ErrorTypes;
5
+ (function (ErrorTypes) {
6
+ ErrorTypes["Http"] = "Http";
7
+ ErrorTypes["Schema"] = "Schema";
8
+ ErrorTypes["KitInternalError"] = "AzureNetKitInternal";
9
+ ErrorTypes["AsyncHelperError"] = "AsyncHelperError";
10
+ ErrorTypes["Unknown"] = "Unknown";
11
+ })(ErrorTypes || (ErrorTypes = {}));
12
+ export class AzureNetKitInternalError extends Error {
13
+ }
14
+ export class AppError extends Error {
15
+ error;
16
+ type = ErrorTypes.Unknown;
17
+ message;
18
+ external = false;
19
+ validation;
20
+ constructor(error) {
21
+ super(error.message);
22
+ this.error = error;
23
+ this.message = error.message ?? 'Unknown error';
24
+ this.parseError();
25
+ }
26
+ tryToGetExternalError() {
27
+ if (this.external && this.error instanceof HttpServiceError) {
28
+ return this.error;
29
+ }
30
+ return undefined;
31
+ }
32
+ redeclareValidation(validation) {
33
+ this.validation = validation;
34
+ }
35
+ toPlainObject(custom) {
36
+ const customData = custom ?? {};
37
+ return {
38
+ message: this.message,
39
+ type: this.type,
40
+ external: this.external,
41
+ validation: this.validation,
42
+ appErrorConvert: true,
43
+ ...customData
44
+ };
45
+ }
46
+ parseError() {
47
+ switch (true) {
48
+ case this.error instanceof HttpServiceError:
49
+ this.type = ErrorTypes.Http;
50
+ this.external = this.error.type === HttpErrorTypes.External;
51
+ break;
52
+ case this.error instanceof SchemaFail:
53
+ this.type = ErrorTypes.Schema;
54
+ this.validation = this.error.getSelfTypedData();
55
+ break;
56
+ case this.error instanceof AzureNetKitInternalError:
57
+ this.type = ErrorTypes.KitInternalError;
58
+ break;
59
+ case this.error instanceof AsyncHelperError:
60
+ this.type = ErrorTypes.AsyncHelperError;
61
+ break;
62
+ default:
63
+ this.type = ErrorTypes.Unknown;
64
+ }
65
+ }
66
+ }
@@ -1,7 +1,7 @@
1
1
  import { EventBus } from '@azure-net/tools';
2
2
  import { createPresenter } from '@azure-net/edges';
3
3
  export const AppEvents = createPresenter(() => {
4
- const appEventBus = new EventBus({ OnAsyncHelperError: [] });
4
+ const appEventBus = new EventBus({});
5
5
  return {
6
6
  bus: appEventBus
7
7
  };
@@ -1,5 +1,6 @@
1
1
  import { RequestContext } from '../../../edges/context/index.js';
2
2
  import { BROWSER } from '@azure-net/tools/environment';
3
+ import { AzureNetKitInternalError } from '../appError/AppError.js';
3
4
  const clientCache = new Map();
4
5
  const factoriesCache = new WeakMap();
5
6
  const providerProxyCache = new Map();
@@ -94,15 +95,15 @@ export const createBoundaryProvider = (name, settings) => {
94
95
  }
95
96
  const factories = getFactories();
96
97
  if (!(key in factories)) {
97
- throw new Error(`Service '${key}' not found in provider '${name}'`);
98
+ throw new AzureNetKitInternalError(`[BoundaryProvider] Service '${key}' not found in provider '${name}'`);
98
99
  }
99
100
  const factory = factories[key];
100
101
  if (typeof factory !== 'function') {
101
- throw Error('Factory must be a function');
102
+ throw new AzureNetKitInternalError('[BoundaryProvider] Factory must be a function');
102
103
  }
103
104
  const instance = factory();
104
105
  if (instance && typeof instance?.then === 'function') {
105
- throw new Error(`Service '${key}' in provider '${name}' returned Promise.`);
106
+ throw new AzureNetKitInternalError(`[BoundaryProvider] Service '${key}' in provider '${name}' returned Promise.`);
106
107
  }
107
108
  cache.set(key, instance);
108
109
  return instance;
@@ -1,3 +1,8 @@
1
- export declare class ClassMirror<Target extends object> {
2
- constructor(target: Target);
1
+ declare const MIRROR_TARGET: unique symbol;
2
+ declare const MIRROR_CACHE: unique symbol;
3
+ export declare class ClassMirror {
4
+ private [MIRROR_TARGET];
5
+ private [MIRROR_CACHE];
6
+ constructor(target: object);
3
7
  }
8
+ export {};
@@ -1,19 +1,38 @@
1
+ const MIRROR_TARGET = Symbol('class-mirror-target');
2
+ const MIRROR_CACHE = Symbol('class-mirror-cache');
1
3
  export class ClassMirror {
4
+ [MIRROR_TARGET];
5
+ [MIRROR_CACHE];
2
6
  constructor(target) {
3
- const proto = Object.getPrototypeOf(target);
4
- for (const name of Object.getOwnPropertyNames(proto)) {
5
- if (name === 'constructor')
6
- continue;
7
- const descriptor = Object.getOwnPropertyDescriptor(proto, name);
8
- if (!descriptor || typeof descriptor.value !== 'function')
9
- continue;
10
- if (name in this)
11
- continue;
12
- Object.defineProperty(this, name, {
13
- value: descriptor.value.bind(target),
14
- configurable: true,
15
- writable: true
16
- });
17
- }
7
+ this[MIRROR_TARGET] = target;
8
+ this[MIRROR_CACHE] = new Map();
9
+ return new Proxy(this, {
10
+ get(proxyTarget, prop, receiver) {
11
+ if (Reflect.has(proxyTarget, prop)) {
12
+ return Reflect.get(proxyTarget, prop, receiver);
13
+ }
14
+ const mirrorTarget = proxyTarget[MIRROR_TARGET];
15
+ const mirroredValue = Reflect.get(mirrorTarget, prop, mirrorTarget);
16
+ if (typeof mirroredValue !== 'function') {
17
+ return mirroredValue;
18
+ }
19
+ const cache = proxyTarget[MIRROR_CACHE];
20
+ if (cache.has(prop)) {
21
+ return cache.get(prop);
22
+ }
23
+ const bound = mirroredValue.bind(mirrorTarget);
24
+ cache.set(prop, bound);
25
+ return bound;
26
+ },
27
+ has(proxyTarget, prop) {
28
+ return Reflect.has(proxyTarget, prop) || Reflect.has(proxyTarget[MIRROR_TARGET], prop);
29
+ },
30
+ ownKeys(proxyTarget) {
31
+ return [...new Set([...Reflect.ownKeys(proxyTarget), ...Reflect.ownKeys(proxyTarget[MIRROR_TARGET])])];
32
+ },
33
+ getOwnPropertyDescriptor(proxyTarget, prop) {
34
+ return Reflect.getOwnPropertyDescriptor(proxyTarget, prop) ?? Reflect.getOwnPropertyDescriptor(proxyTarget[MIRROR_TARGET], prop);
35
+ }
36
+ });
18
37
  }
19
38
  }
@@ -1,4 +1,5 @@
1
- import { Cookies, EnvironmentUtil } from '@azure-net/tools';
1
+ import { Cookies } from '@azure-net/tools';
2
+ import { BROWSER } from '@azure-net/tools/environment';
2
3
  import { RequestContext } from '@azure-net/edges/context';
3
4
  /**
4
5
  * Utility class for managing cookies in svelte ssr (browser and server).
@@ -17,11 +18,11 @@ export class UniversalCookie {
17
18
  * @returns {void}
18
19
  */
19
20
  static set(name, value, options) {
20
- if (EnvironmentUtil.isBrowser) {
21
+ if (BROWSER) {
21
22
  Cookies.set(name, value, options);
22
23
  return;
23
24
  }
24
- if (EnvironmentUtil.isServer) {
25
+ else {
25
26
  const event = RequestContext.current().event;
26
27
  if (event) {
27
28
  const encodedKey = encodeURIComponent(name);
@@ -53,7 +54,6 @@ export class UniversalCookie {
53
54
  return;
54
55
  }
55
56
  }
56
- throw new Error('Could not detect current environment');
57
57
  }
58
58
  /**
59
59
  * Retrieves the value of a cookie by key.
@@ -64,10 +64,10 @@ export class UniversalCookie {
64
64
  * @returns {T | undefined} The cookie value, parsed as type T or null if not found.
65
65
  */
66
66
  static get(name) {
67
- if (EnvironmentUtil.isBrowser) {
67
+ if (BROWSER) {
68
68
  return Cookies.get(name) ?? undefined;
69
69
  }
70
- if (EnvironmentUtil.isServer) {
70
+ else {
71
71
  const event = RequestContext.current().event;
72
72
  if (event) {
73
73
  const encodedKey = encodeURIComponent(name);
@@ -85,7 +85,6 @@ export class UniversalCookie {
85
85
  }
86
86
  return undefined;
87
87
  }
88
- return undefined;
89
88
  }
90
89
  /**
91
90
  * Retrieves all cookies as a key-value record.
@@ -95,10 +94,10 @@ export class UniversalCookie {
95
94
  * @returns {T | undefined} An object with all cookie keys and their values.
96
95
  */
97
96
  static getAll() {
98
- if (EnvironmentUtil.isBrowser) {
97
+ if (BROWSER) {
99
98
  return Cookies.getAll();
100
99
  }
101
- if (EnvironmentUtil.isServer) {
100
+ else {
102
101
  const event = RequestContext.current().event;
103
102
  const result = {};
104
103
  if (event) {
@@ -114,7 +113,6 @@ export class UniversalCookie {
114
113
  }
115
114
  return result;
116
115
  }
117
- return undefined;
118
116
  }
119
117
  /**
120
118
  * Checks if a cookie with the given key exists.
@@ -1,6 +1,6 @@
1
1
  import type { RequestErrors } from '../../delivery/schema/index.js';
2
- import type { AsyncActionResponse } from '../../index.js';
3
- type InitialData<FormData, Initial extends Partial<FormData>> = Initial | Promise<Initial> | (() => Initial | Promise<Initial>);
2
+ import type { AsyncActionResponse } from '../../delivery/injectableDependencies/AsyncHelpers.js';
3
+ type InitialData<FormData, Initial extends Partial<FormData>> = () => Initial | Promise<Initial>;
4
4
  type PathRequiredShape<T, P extends string> = P extends `${infer Head}.${infer Tail}` ? Head extends keyof T ? {
5
5
  [K in Head]-?: PathRequiredShape<NonNullable<T[K]>, Tail>;
6
6
  } : unknown : P extends keyof T ? {
@@ -8,6 +8,7 @@ type PathRequiredShape<T, P extends string> = P extends `${infer Head}.${infer T
8
8
  } : unknown;
9
9
  type UnionToIntersection<U> = (U extends unknown ? (arg: U) => void : never) extends (arg: infer I) => void ? I : never;
10
10
  type RequiredByPaths<T, P extends string> = [P] extends [never] ? object : UnionToIntersection<PathRequiredShape<T, P>>;
11
+ type ResetBehaviors = 'clear' | 'initial' | 'reloadInitial' | 'default';
11
12
  export interface FormConfig<FormData, Response, Initial extends Partial<FormData> = Partial<FormData>, RequiredPath extends string = never> {
12
13
  initialData?: InitialData<FormData, Initial>;
13
14
  required?: readonly RequiredPath[];
@@ -17,13 +18,13 @@ export interface FormConfig<FormData, Response, Initial extends Partial<FormData
17
18
  form: ActiveFormController<FormData, RequiredPath>;
18
19
  abort: () => void;
19
20
  }) => Promise<void> | void;
20
- waitForInitialData?: boolean;
21
+ successBehavior?: ResetBehaviors;
21
22
  }
22
23
  export interface ActiveForm<FormData, Response, Custom, RequiredPath extends string = never> {
23
24
  data: Partial<FormData> & RequiredByPaths<FormData, RequiredPath>;
24
25
  errors: RequestErrors<FormData>;
25
26
  submit: () => Promise<AsyncActionResponse<Response, FormData, Custom>>;
26
- reset: (toInitial?: boolean) => void;
27
+ reset: (behavior?: ResetBehaviors) => Promise<void>;
27
28
  pending: boolean;
28
29
  dirty: boolean;
29
30
  ready: Promise<Partial<FormData>>;
@@ -31,7 +32,7 @@ export interface ActiveForm<FormData, Response, Custom, RequiredPath extends str
31
32
  export interface ActiveFormController<FormData, RequiredPath extends string = never> {
32
33
  data: Partial<FormData> & RequiredByPaths<FormData, RequiredPath>;
33
34
  errors: RequestErrors<FormData>;
34
- reset: (toInitial?: boolean) => void;
35
+ reset: (behavior?: ResetBehaviors) => Promise<void>;
35
36
  }
36
37
  type ExtractResponse<T> = T extends AsyncActionResponse<infer R, unknown, unknown> ? R : never;
37
38
  type ExtractFormData<T> = T extends AsyncActionResponse<unknown, infer D, unknown> ? D : never;
@@ -3,32 +3,27 @@ export const createActiveForm = (onSubmit, config) => {
3
3
  const isPromise = (value) => {
4
4
  return typeof value?.then === 'function';
5
5
  };
6
- const resolveInitialSource = () => {
7
- const source = config?.initialData;
8
- if (!source) {
9
- return { sync: true, value: {} };
10
- }
11
- const result = typeof source === 'function' ? source() : source;
12
- if (isPromise(result)) {
13
- return { sync: false, promise: result };
14
- }
15
- return { sync: true, value: (result ?? {}) };
16
- };
17
- const initialSource = resolveInitialSource();
18
- let initial = initialSource.sync ? initialSource.value : {};
6
+ let initial = {};
19
7
  let formData = $state(ObjectUtil.deepClone(initial));
20
8
  let formErrors = $state({});
21
9
  let pending = $state(false);
22
10
  const dirty = $derived(!ObjectUtil.equals(formData, initial));
23
- const ready = (async () => {
24
- if (!initialSource.sync) {
25
- initial = (await initialSource.promise) ?? {};
26
- if (config?.waitForInitialData !== false) {
27
- formData = ObjectUtil.deepClone(initial);
28
- }
11
+ const loadInitialData = async () => {
12
+ const source = config?.initialData;
13
+ if (!source) {
14
+ initial = {};
15
+ formData = ObjectUtil.deepClone(initial);
16
+ formErrors = {};
17
+ return initial;
29
18
  }
19
+ const value = source();
20
+ const nextInitial = isPromise(value) ? await value : value;
21
+ initial = (nextInitial ?? {});
22
+ formData = ObjectUtil.deepClone(initial);
23
+ formErrors = {};
30
24
  return initial;
31
- })();
25
+ };
26
+ const ready = loadInitialData();
32
27
  const submit = async () => {
33
28
  pending = true;
34
29
  try {
@@ -49,11 +44,11 @@ export const createActiveForm = (onSubmit, config) => {
49
44
  const result = await onSubmit($state.snapshot(formData));
50
45
  if (result.success) {
51
46
  await config?.onSuccess?.(result.response);
52
- formErrors = {};
47
+ await reset(config?.successBehavior ?? 'default');
53
48
  }
54
49
  else {
55
- if (result.error?.fields) {
56
- formErrors = result.error.fields;
50
+ if (result.error?.validation) {
51
+ formErrors = result.error.validation;
57
52
  }
58
53
  await config?.onError?.();
59
54
  }
@@ -63,8 +58,18 @@ export const createActiveForm = (onSubmit, config) => {
63
58
  pending = false;
64
59
  }
65
60
  };
66
- const reset = (toInitial = false) => {
67
- formData = toInitial ? ObjectUtil.deepClone(initial) : {};
61
+ const reset = async (behavior = 'clear') => {
62
+ switch (behavior) {
63
+ case 'clear':
64
+ formData = {};
65
+ break;
66
+ case 'initial':
67
+ formData = ObjectUtil.deepClone(initial);
68
+ break;
69
+ case 'reloadInitial':
70
+ await loadInitialData();
71
+ break;
72
+ }
68
73
  formErrors = {};
69
74
  };
70
75
  const formApi = {
@@ -101,8 +106,8 @@ export const createActiveForm = (onSubmit, config) => {
101
106
  get pending() {
102
107
  return pending;
103
108
  },
104
- ready,
105
109
  submit,
110
+ ready,
106
111
  reset
107
112
  };
108
113
  };
@@ -3,7 +3,6 @@ export type AsyncSignalSource = 'auto' | 'manual' | 'global';
3
3
  export interface AsyncSignalOptions<TData, TError = Error> {
4
4
  server?: boolean;
5
5
  immediate?: boolean;
6
- watch?: (() => unknown)[];
7
6
  initialData?: TData | (() => TData);
8
7
  beforeSend?: (meta: {
9
8
  initial: boolean;