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

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,26 @@
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 ValidationGroups, type ValidationRules } from './validation';
5
5
  type ErrorMessages = string[];
6
6
  interface ErrorObject {
7
7
  [key: string]: FieldErrors;
8
8
  }
9
9
  type ErrorArray = Array<ErrorObject>;
10
10
  type FieldErrors = ErrorMessages | ErrorObject | ErrorArray;
11
- type FieldProperty<T> = {
12
- model: WritableComputedRef<T>;
13
- errors: ErrorMessages;
14
- dirty: boolean;
15
- touched: boolean;
11
+ type FieldProperty<T> = PropertyAwareField<T>;
12
+ type NestedPropertyAwareValue<T> = T extends PropertyAwareArray<infer Item> ? ArrayProperty<Item> : T extends PropertyAwareObject<infer Shape> ? ObjectProperty<Shape> : FieldProperty<T>;
13
+ type ArrayProperty<T> = Array<T extends object ? {
14
+ [P in keyof T]: NestedPropertyAwareValue<T[P]>;
15
+ } : {
16
+ value: FieldProperty<T>;
17
+ }>;
18
+ type ObjectProperty<T extends object> = {
19
+ [K in keyof T]: NestedPropertyAwareValue<T[K]>;
16
20
  };
17
- type PropertyAwareToRaw<T> = T extends Array<infer U> ? Array<PropertyAwareToRaw<U>> : T extends {
21
+ type PropertyAwareToRaw<T> = T extends Array<infer U> ? Array<PropertyAwareToRaw<U>> : T extends PropertyAwareObject<infer Shape> ? {
22
+ [K in keyof Shape]: PropertyAwareToRaw<Shape[K]>;
23
+ } : T extends {
18
24
  model: {
19
25
  value: infer V;
20
26
  };
@@ -23,12 +29,18 @@ type PropertyAwareToRaw<T> = T extends Array<infer U> ? Array<PropertyAwareToRaw
23
29
  } : T;
24
30
  type ArrayItem<T> = T extends Array<infer Item> ? Item : never;
25
31
  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]>;
32
+ [K in keyof FormBody]: FormBody[K] extends PropertyAwareArray<infer Item> ? ArrayProperty<Item> : FormBody[K] extends PropertyAwareObject<infer Shape> ? ObjectProperty<Shape> : FieldProperty<FormBody[K]>;
31
33
  };
34
+ interface ValidationContext {
35
+ isDirty?: boolean;
36
+ isSubmitting?: boolean;
37
+ isDependentChange?: boolean;
38
+ isTouched?: boolean;
39
+ }
40
+ interface AsyncValidationContext extends ValidationContext {
41
+ skipSyncValidation?: boolean;
42
+ skipAsyncValidation?: boolean;
43
+ }
32
44
  export declare function propertyAwareToRaw<T>(propertyAwareObject: T): PropertyAwareToRaw<T>;
33
45
  /**
34
46
  * A generic base class for forms.
@@ -49,6 +61,7 @@ export declare abstract class BaseForm<RequestBody extends object, FormBody exte
49
61
  private readonly original;
50
62
  private readonly _model;
51
63
  private _errors;
64
+ private _asyncErrors;
52
65
  private _hasErrors;
53
66
  protected append: string[];
54
67
  protected ignore: string[];
@@ -56,7 +69,13 @@ export declare abstract class BaseForm<RequestBody extends object, FormBody exte
56
69
  [serverKey: string]: string | string[];
57
70
  };
58
71
  protected rules: ValidationRules<FormBody>;
72
+ protected validationGroups: ValidationGroups<FormBody>;
59
73
  private fieldDependencies;
74
+ private readonly arrayWrapperCache;
75
+ private readonly arrayItemWrapperCache;
76
+ private readonly asyncValidationDebouncers;
77
+ private readonly pendingAsyncValidationContexts;
78
+ private readonly asyncValidationTokens;
60
79
  /**
61
80
  * Returns the persistence driver to use.
62
81
  * The default is a NonPersistentDriver.
@@ -87,7 +106,29 @@ export declare abstract class BaseForm<RequestBody extends object, FormBody exte
87
106
  persistSuffix?: string;
88
107
  } | undefined);
89
108
  protected defineRules(): ValidationRules<FormBody>;
109
+ protected defineValidationGroups(): ValidationGroups<FormBody>;
110
+ private clearErrorBag;
111
+ private clearSyncErrors;
112
+ private clearAsyncErrors;
90
113
  private clearErrors;
114
+ private hasAsyncRules;
115
+ private bumpAsyncValidationToken;
116
+ private cancelPendingAsyncValidations;
117
+ private scheduleAsyncValidation;
118
+ private executeScheduledAsyncValidation;
119
+ private flattenErrorValue;
120
+ private flattenErrorsFromBag;
121
+ private mergeErrorMessages;
122
+ private flattenErrors;
123
+ private applyErrors;
124
+ private getValidationGroupPaths;
125
+ private matchesValidationGroupPath;
126
+ private errorKeyBelongsToGroup;
127
+ private getValidationGroupFields;
128
+ private clearGroupErrors;
129
+ private collectSiblingNestedErrors;
130
+ private validateFieldPreservingNestedErrors;
131
+ private clearErrorBagPaths;
91
132
  private getOrCreateErrorArray;
92
133
  private getOrCreateErrorObject;
93
134
  private getFieldErrors;
@@ -95,6 +136,9 @@ export declare abstract class BaseForm<RequestBody extends object, FormBody exte
95
136
  private getArrayItemErrors;
96
137
  private getArrayItemFieldErrors;
97
138
  private getArrayItemErrorMessages;
139
+ private getObjectFieldErrors;
140
+ private getArrayItemErrorsFromBag;
141
+ private getArrayItemErrorMessagesFromBag;
98
142
  private setArrayDirty;
99
143
  /**
100
144
  * Collapse nested array dirty states into a single boolean/object for array entries.
@@ -102,6 +146,16 @@ export declare abstract class BaseForm<RequestBody extends object, FormBody exte
102
146
  private normalizeItemDirtyState;
103
147
  private getArrayItemDirty;
104
148
  private getArrayItemDirtyValue;
149
+ private getNestedErrorMessagesFromValue;
150
+ private setNestedError;
151
+ private getNestedDirtyValue;
152
+ private createFieldProperty;
153
+ private resolveArrayItemIndex;
154
+ private getArrayItemValueByPath;
155
+ private setArrayItemValueByPath;
156
+ private createObjectWrapperFromShape;
157
+ private getOrCreateArrayItemWrapper;
158
+ private getOrCreateArrayWrappers;
105
159
  /**
106
160
  * Map server-side errors (including dot-notation paths) into the form error bag.
107
161
  */
@@ -118,13 +172,18 @@ export declare abstract class BaseForm<RequestBody extends object, FormBody exte
118
172
  * @returns boolean indicating if the field has been touched
119
173
  */
120
174
  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;
175
+ protected validateField(field: keyof FormBody, context?: AsyncValidationContext): void;
176
+ validate(isSubmitting?: boolean, options?: {
177
+ skipAsyncValidation?: boolean;
178
+ }): boolean;
179
+ validateGroup(group: string, isSubmitting?: boolean, options?: {
180
+ skipAsyncValidation?: boolean;
181
+ }): boolean;
182
+ private runFieldAsyncValidation;
183
+ validateFieldAsync(field: keyof FormBody, context?: AsyncValidationContext): Promise<boolean>;
184
+ validateAsync(isSubmitting?: boolean): Promise<boolean>;
185
+ validateGroupAsync(group: string, isSubmitting?: boolean): Promise<boolean>;
186
+ touchGroup(group: string): void;
128
187
  fillState(data: Partial<FormBody>): void;
129
188
  private getValueGetter;
130
189
  private getNoArgGetter;
@@ -149,6 +208,9 @@ export declare abstract class BaseForm<RequestBody extends object, FormBody exte
149
208
  * @returns boolean indicating if the form has errors
150
209
  */
151
210
  hasErrors(): boolean;
211
+ getErrors(): Record<string, ErrorMessages>;
212
+ hasErrorsInGroup(group: string): boolean;
213
+ getErrorsInGroup(group: string): Record<string, ErrorMessages>;
152
214
  /**
153
215
  * Updates both the state and original value for a given property,
154
216
  * keeping the field in a clean (not dirty) state.