@blueprint-ts/core 4.1.0-beta.3 → 4.1.0-beta.5

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.
@@ -9,6 +9,7 @@ import { type RequestDriverContract } from './contracts/RequestDriverContract';
9
9
  import { type RequestLoaderFactoryContract } from './contracts/RequestLoaderFactoryContract';
10
10
  import { type BaseRequestContract, type EventHandlerCallback } from './contracts/BaseRequestContract';
11
11
  import { type HeadersContract } from './contracts/HeadersContract';
12
+ import { type ResponseHandlerContract } from './drivers/contracts/ResponseHandlerContract';
12
13
  import { type ResponseContract } from './contracts/ResponseContract';
13
14
  import { type RequestConcurrencyOptions } from './types/RequestConcurrencyOptions';
14
15
  export declare abstract class BaseRequest<RequestLoaderLoadingType, ResponseErrorBody, ResponseBodyInterface = undefined, ResponseClass extends ResponseContract<ResponseBodyInterface> = BaseResponse<ResponseBodyInterface>, RequestBodyInterface = undefined, RequestParamsInterface extends object = object> implements BaseRequestContract<RequestLoaderLoadingType, RequestBodyInterface, ResponseClass, RequestParamsInterface> {
@@ -18,6 +19,7 @@ export declare abstract class BaseRequest<RequestLoaderLoadingType, ResponseErro
18
19
  protected requestLoader: RequestLoaderContract<RequestLoaderLoadingType> | undefined;
19
20
  protected abortSignal: AbortSignal | undefined;
20
21
  protected concurrencyOptions: RequestConcurrencyOptions | undefined;
22
+ protected additionalHeaders: HeadersContract;
21
23
  protected events: {
22
24
  [key in RequestEvents]?: EventHandlerCallback[];
23
25
  };
@@ -40,12 +42,19 @@ export declare abstract class BaseRequest<RequestLoaderLoadingType, ResponseErro
40
42
  withParams(params: RequestParamsInterface): this;
41
43
  getParams(): RequestParamsInterface | undefined;
42
44
  setBody(requestBody: RequestBodyInterface): this;
45
+ setHeaders(headers: HeadersContract): this;
43
46
  getBody(): RequestBodyInterface | undefined;
44
47
  requestHeaders(): HeadersContract;
45
48
  buildUrl(): URL;
46
49
  on<T>(event: RequestEvents, handler: EventHandlerCallback<T>): this;
47
50
  protected dispatch<T>(event: RequestEvents, value: T): void;
48
51
  send(): Promise<ResponseClass>;
52
+ send(options: {
53
+ resolveBody?: true;
54
+ }): Promise<ResponseClass>;
55
+ send(options: {
56
+ resolveBody: false;
57
+ }): Promise<ResponseHandlerContract>;
49
58
  isLoading(): RequestLoaderLoadingType;
50
59
  abstract getResponse(): ResponseClass;
51
60
  getRequestBodyFactory(): BodyFactoryContract<RequestBodyInterface | undefined> | undefined;
@@ -23,6 +23,7 @@ export class BaseRequest {
23
23
  this.requestLoader = undefined;
24
24
  this.abortSignal = undefined;
25
25
  this.concurrencyOptions = undefined;
26
+ this.additionalHeaders = {};
26
27
  /* @ts-expect-error Ignore generics */
27
28
  this.events = {};
28
29
  if (BaseRequest.requestLoaderFactory !== undefined) {
@@ -64,6 +65,10 @@ export class BaseRequest {
64
65
  this.requestBody = requestBody;
65
66
  return this;
66
67
  }
68
+ setHeaders(headers) {
69
+ this.additionalHeaders = Object.assign(Object.assign({}, this.additionalHeaders), headers);
70
+ return this;
71
+ }
67
72
  getBody() {
68
73
  return this.requestBody;
69
74
  }
@@ -90,8 +95,10 @@ export class BaseRequest {
90
95
  this.events[event].forEach((handler) => handler(value));
91
96
  }
92
97
  send() {
93
- return __awaiter(this, void 0, void 0, function* () {
98
+ return __awaiter(this, arguments, void 0, function* (options = {}) {
94
99
  var _a, _b, _c, _d, _e, _f;
100
+ const responseSkeleton = this.getResponse();
101
+ const acceptHeader = responseSkeleton.getAcceptHeader();
95
102
  const concurrencyMode = (_b = (_a = this.concurrencyOptions) === null || _a === void 0 ? void 0 : _a.mode) !== null && _b !== void 0 ? _b : RequestConcurrencyMode.ALLOW;
96
103
  const concurrencyKey = (_d = (_c = this.concurrencyOptions) === null || _c === void 0 ? void 0 : _c.key) !== null && _d !== void 0 ? _d : this.requestId;
97
104
  const useReplace = concurrencyMode === RequestConcurrencyMode.REPLACE || concurrencyMode === RequestConcurrencyMode.REPLACE_LATEST;
@@ -109,17 +116,20 @@ export class BaseRequest {
109
116
  }
110
117
  this.dispatch(RequestEvents.LOADING, true);
111
118
  (_e = this.requestLoader) === null || _e === void 0 ? void 0 : _e.setLoading(true);
112
- const responseSkeleton = this.getResponse();
113
119
  const requestBody = this.requestBody === undefined ? undefined : (_f = this.getRequestBodyFactory()) === null || _f === void 0 ? void 0 : _f.make(this.requestBody);
114
120
  const requestConfig = this.buildRequestConfig(requestBody, concurrencyKey, sequence, useLatest);
115
- return this.resolveRequestDriver()
116
- .send(this.buildUrl(), this.method(), Object.assign({ Accept: responseSkeleton.getAcceptHeader() }, this.requestHeaders()), requestBody, requestConfig)
117
- .then((responseHandler) => __awaiter(this, void 0, void 0, function* () {
121
+ const responseHandler = yield this.resolveRequestDriver()
122
+ .send(this.buildUrl(), this.method(), Object.assign(Object.assign({ Accept: acceptHeader }, this.requestHeaders()), this.additionalHeaders), requestBody, requestConfig)
123
+ .then((driverResponseHandler) => __awaiter(this, void 0, void 0, function* () {
124
+ var _a;
118
125
  if (useLatest && !this.isLatestSequence(concurrencyKey, sequence)) {
119
126
  throw new StaleResponseException();
120
127
  }
121
- yield responseSkeleton.setResponse(responseHandler);
122
- return responseSkeleton;
128
+ if (((_a = driverResponseHandler.getStatusCode()) !== null && _a !== void 0 ? _a : 0) >= 400) {
129
+ const handler = new ErrorHandler(driverResponseHandler);
130
+ yield handler.handle();
131
+ }
132
+ return driverResponseHandler;
123
133
  }))
124
134
  .catch((error) => __awaiter(this, void 0, void 0, function* () {
125
135
  if (useLatest && !this.isLatestSequence(concurrencyKey, sequence)) {
@@ -144,6 +154,11 @@ export class BaseRequest {
144
154
  }
145
155
  this.decrementConcurrencyInFlight(concurrencyKey);
146
156
  });
157
+ if (options.resolveBody === false) {
158
+ return responseHandler;
159
+ }
160
+ yield responseSkeleton.setResponse(responseHandler);
161
+ return responseSkeleton;
147
162
  });
148
163
  }
149
164
  isLoading() {
@@ -3,7 +3,11 @@ import { RequestEvents } from '../RequestEvents.enum';
3
3
  import { type BodyFactoryContract } from './BodyFactoryContract';
4
4
  import { type HeadersContract } from './HeadersContract';
5
5
  import { type RequestConcurrencyOptions } from '../types/RequestConcurrencyOptions';
6
+ import { type ResponseHandlerContract } from '../drivers/contracts/ResponseHandlerContract';
6
7
  export type EventHandlerCallback<T> = (value: T) => void;
8
+ export interface SendRequestOptions {
9
+ resolveBody?: boolean;
10
+ }
7
11
  export interface BaseRequestContract<RequestLoaderLoadingType, RequestBodyInterface, ResponseClass, RequestParamsInterface extends object> {
8
12
  method(): RequestMethodEnum;
9
13
  url(): URL | string;
@@ -15,6 +19,12 @@ export interface BaseRequestContract<RequestLoaderLoadingType, RequestBodyInterf
15
19
  buildUrl(): URL;
16
20
  on<T>(event: RequestEvents, handler: EventHandlerCallback<T>): this;
17
21
  send(): Promise<ResponseClass>;
22
+ send(options: {
23
+ resolveBody?: true;
24
+ }): Promise<ResponseClass>;
25
+ send(options: {
26
+ resolveBody: false;
27
+ }): Promise<ResponseHandlerContract>;
18
28
  isLoading(): RequestLoaderLoadingType;
19
29
  getRequestBodyFactory(): BodyFactoryContract<RequestBodyInterface> | undefined;
20
30
  getResponse(): ResponseClass;
@@ -1,20 +1,27 @@
1
- import { type WritableComputedRef } from 'vue';
2
1
  import { type PersistenceDriver } from '../../persistenceDrivers/types/PersistenceDriver';
3
- import { PropertyAwareArray } from './PropertyAwareArray';
4
- import { type ValidationRules } from './validation';
2
+ import { PropertyAwareArray, type PropertyAwareField } from './PropertyAwareArray';
3
+ import { PropertyAwareObject } from './PropertyAwareObject';
4
+ import { type PersistenceDebugEvent, type PersistenceRestorePolicy } from './persistence/types';
5
+ import { type ValidationGroups, type ValidationRules } from './validation';
5
6
  type ErrorMessages = string[];
6
7
  interface ErrorObject {
7
8
  [key: string]: FieldErrors;
8
9
  }
9
10
  type ErrorArray = Array<ErrorObject>;
10
11
  type FieldErrors = ErrorMessages | ErrorObject | ErrorArray;
11
- type FieldProperty<T> = {
12
- model: WritableComputedRef<T>;
13
- errors: ErrorMessages;
14
- dirty: boolean;
15
- touched: boolean;
12
+ type FieldProperty<T> = PropertyAwareField<T>;
13
+ type NestedPropertyAwareValue<T> = T extends PropertyAwareArray<infer Item> ? ArrayProperty<Item> : T extends PropertyAwareObject<infer Shape> ? ObjectProperty<Shape> : FieldProperty<T>;
14
+ type ArrayProperty<T> = Array<T extends object ? {
15
+ [P in keyof T]: NestedPropertyAwareValue<T[P]>;
16
+ } : {
17
+ value: FieldProperty<T>;
18
+ }>;
19
+ type ObjectProperty<T extends object> = {
20
+ [K in keyof T]: NestedPropertyAwareValue<T[K]>;
16
21
  };
17
- type PropertyAwareToRaw<T> = T extends Array<infer U> ? Array<PropertyAwareToRaw<U>> : T extends {
22
+ type PropertyAwareToRaw<T> = T extends Array<infer U> ? Array<PropertyAwareToRaw<U>> : T extends PropertyAwareObject<infer Shape> ? {
23
+ [K in keyof Shape]: PropertyAwareToRaw<Shape[K]>;
24
+ } : T extends {
18
25
  model: {
19
26
  value: infer V;
20
27
  };
@@ -23,12 +30,18 @@ type PropertyAwareToRaw<T> = T extends Array<infer U> ? Array<PropertyAwareToRaw
23
30
  } : T;
24
31
  type ArrayItem<T> = T extends Array<infer Item> ? Item : never;
25
32
  type FormProperties<FormBody extends object> = {
26
- [K in keyof FormBody]: FormBody[K] extends PropertyAwareArray<infer Item> ? Array<Item extends object ? {
27
- [P in keyof Item]: FieldProperty<Item[P]>;
28
- } : {
29
- value: FieldProperty<Item>;
30
- }> : FieldProperty<FormBody[K]>;
33
+ [K in keyof FormBody]: FormBody[K] extends PropertyAwareArray<infer Item> ? ArrayProperty<Item> : FormBody[K] extends PropertyAwareObject<infer Shape> ? ObjectProperty<Shape> : FieldProperty<FormBody[K]>;
31
34
  };
35
+ interface ValidationContext {
36
+ isDirty?: boolean;
37
+ isSubmitting?: boolean;
38
+ isDependentChange?: boolean;
39
+ isTouched?: boolean;
40
+ }
41
+ interface AsyncValidationContext extends ValidationContext {
42
+ skipSyncValidation?: boolean;
43
+ skipAsyncValidation?: boolean;
44
+ }
32
45
  export declare function propertyAwareToRaw<T>(propertyAwareObject: T): PropertyAwareToRaw<T>;
33
46
  /**
34
47
  * A generic base class for forms.
@@ -49,6 +62,7 @@ export declare abstract class BaseForm<RequestBody extends object, FormBody exte
49
62
  private readonly original;
50
63
  private readonly _model;
51
64
  private _errors;
65
+ private _asyncErrors;
52
66
  private _hasErrors;
53
67
  protected append: string[];
54
68
  protected ignore: string[];
@@ -56,13 +70,22 @@ export declare abstract class BaseForm<RequestBody extends object, FormBody exte
56
70
  [serverKey: string]: string | string[];
57
71
  };
58
72
  protected rules: ValidationRules<FormBody>;
73
+ protected validationGroups: ValidationGroups<FormBody>;
59
74
  private fieldDependencies;
75
+ private readonly arrayWrapperCache;
76
+ private readonly arrayItemWrapperCache;
77
+ private readonly asyncValidationDebouncers;
78
+ private readonly pendingAsyncValidationContexts;
79
+ private readonly asyncValidationTokens;
60
80
  /**
61
81
  * Returns the persistence driver to use.
62
82
  * The default is a NonPersistentDriver.
63
83
  * Child classes can override this method to return a different driver.
64
84
  */
65
85
  protected getPersistenceDriver(_suffix: string | undefined): PersistenceDriver;
86
+ protected getPersistenceRestorePolicy(): PersistenceRestorePolicy<FormBody>;
87
+ protected shouldLogPersistenceDebug(): boolean;
88
+ protected logPersistenceDebug(event: PersistenceDebugEvent<FormBody>): void;
66
89
  /**
67
90
  * Helper: recursively computes the dirty state for a value based on the original.
68
91
  * For plain arrays we compare the entire array (a single flag), not each element.
@@ -87,7 +110,29 @@ export declare abstract class BaseForm<RequestBody extends object, FormBody exte
87
110
  persistSuffix?: string;
88
111
  } | undefined);
89
112
  protected defineRules(): ValidationRules<FormBody>;
113
+ protected defineValidationGroups(): ValidationGroups<FormBody>;
114
+ private clearErrorBag;
115
+ private clearSyncErrors;
116
+ private clearAsyncErrors;
90
117
  private clearErrors;
118
+ private hasAsyncRules;
119
+ private bumpAsyncValidationToken;
120
+ private cancelPendingAsyncValidations;
121
+ private scheduleAsyncValidation;
122
+ private executeScheduledAsyncValidation;
123
+ private flattenErrorValue;
124
+ private flattenErrorsFromBag;
125
+ private mergeErrorMessages;
126
+ private flattenErrors;
127
+ private applyErrors;
128
+ private getValidationGroupPaths;
129
+ private matchesValidationGroupPath;
130
+ private errorKeyBelongsToGroup;
131
+ private getValidationGroupFields;
132
+ private clearGroupErrors;
133
+ private collectSiblingNestedErrors;
134
+ private validateFieldPreservingNestedErrors;
135
+ private clearErrorBagPaths;
91
136
  private getOrCreateErrorArray;
92
137
  private getOrCreateErrorObject;
93
138
  private getFieldErrors;
@@ -95,6 +140,9 @@ export declare abstract class BaseForm<RequestBody extends object, FormBody exte
95
140
  private getArrayItemErrors;
96
141
  private getArrayItemFieldErrors;
97
142
  private getArrayItemErrorMessages;
143
+ private getObjectFieldErrors;
144
+ private getArrayItemErrorsFromBag;
145
+ private getArrayItemErrorMessagesFromBag;
98
146
  private setArrayDirty;
99
147
  /**
100
148
  * Collapse nested array dirty states into a single boolean/object for array entries.
@@ -102,6 +150,16 @@ export declare abstract class BaseForm<RequestBody extends object, FormBody exte
102
150
  private normalizeItemDirtyState;
103
151
  private getArrayItemDirty;
104
152
  private getArrayItemDirtyValue;
153
+ private getNestedErrorMessagesFromValue;
154
+ private setNestedError;
155
+ private getNestedDirtyValue;
156
+ private createFieldProperty;
157
+ private resolveArrayItemIndex;
158
+ private getArrayItemValueByPath;
159
+ private setArrayItemValueByPath;
160
+ private createObjectWrapperFromShape;
161
+ private getOrCreateArrayItemWrapper;
162
+ private getOrCreateArrayWrappers;
105
163
  /**
106
164
  * Map server-side errors (including dot-notation paths) into the form error bag.
107
165
  */
@@ -118,13 +176,18 @@ export declare abstract class BaseForm<RequestBody extends object, FormBody exte
118
176
  * @returns boolean indicating if the field has been touched
119
177
  */
120
178
  isTouched(field: keyof FormBody): boolean;
121
- protected validateField(field: keyof FormBody, context?: {
122
- isDirty?: boolean;
123
- isSubmitting?: boolean;
124
- isDependentChange?: boolean;
125
- isTouched?: boolean;
126
- }): void;
127
- validate(isSubmitting?: boolean): boolean;
179
+ protected validateField(field: keyof FormBody, context?: AsyncValidationContext): void;
180
+ validate(isSubmitting?: boolean, options?: {
181
+ skipAsyncValidation?: boolean;
182
+ }): boolean;
183
+ validateGroup(group: string, isSubmitting?: boolean, options?: {
184
+ skipAsyncValidation?: boolean;
185
+ }): boolean;
186
+ private runFieldAsyncValidation;
187
+ validateFieldAsync(field: keyof FormBody, context?: AsyncValidationContext): Promise<boolean>;
188
+ validateAsync(isSubmitting?: boolean): Promise<boolean>;
189
+ validateGroupAsync(group: string, isSubmitting?: boolean): Promise<boolean>;
190
+ touchGroup(group: string): void;
128
191
  fillState(data: Partial<FormBody>): void;
129
192
  private getValueGetter;
130
193
  private getNoArgGetter;
@@ -149,6 +212,9 @@ export declare abstract class BaseForm<RequestBody extends object, FormBody exte
149
212
  * @returns boolean indicating if the form has errors
150
213
  */
151
214
  hasErrors(): boolean;
215
+ getErrors(): Record<string, ErrorMessages>;
216
+ hasErrorsInGroup(group: string): boolean;
217
+ getErrorsInGroup(group: string): Record<string, ErrorMessages>;
152
218
  /**
153
219
  * Updates both the state and original value for a given property,
154
220
  * keeping the field in a clean (not dirty) state.