@inertiajs/core 2.3.16 → 3.0.0-beta.1

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/types/types.d.ts CHANGED
@@ -1,11 +1,38 @@
1
- import { AxiosProgressEvent, AxiosResponse } from 'axios';
2
1
  import { NamedInputEvent, ValidationConfig, Validator } from 'laravel-precognition';
2
+ import type { HttpCancelledError, HttpNetworkError, HttpResponseError } from './httpErrors';
3
3
  import { Response } from './response';
4
- declare module 'axios' {
5
- interface AxiosProgressEvent {
6
- percentage: number | undefined;
7
- }
4
+ export type HttpRequestHeaders = Record<string, unknown>;
5
+ export type HttpResponseHeaders = Record<string, string>;
6
+ export interface HttpProgressEvent {
7
+ progress: number | undefined;
8
+ loaded: number;
9
+ total: number | undefined;
10
+ percentage?: number;
11
+ }
12
+ export interface HttpRequestConfig {
13
+ method: Method;
14
+ url: string;
15
+ data?: unknown;
16
+ params?: Record<string, unknown>;
17
+ headers?: HttpRequestHeaders;
18
+ signal?: AbortSignal;
19
+ onUploadProgress?: (event: HttpProgressEvent) => void;
20
+ }
21
+ export interface HttpResponse {
22
+ status: number;
23
+ data: string;
24
+ headers: HttpResponseHeaders;
25
+ }
26
+ export interface HttpClient {
27
+ request(config: HttpRequestConfig): Promise<HttpResponse>;
28
+ }
29
+ export interface HttpClientOptions {
30
+ xsrfCookieName?: string;
31
+ xsrfHeaderName?: string;
8
32
  }
33
+ export type HttpRequestHandler = (config: HttpRequestConfig) => HttpRequestConfig | Promise<HttpRequestConfig>;
34
+ export type HttpResponseHandler = (response: HttpResponse) => HttpResponse | Promise<HttpResponse>;
35
+ export type HttpErrorHandler = (error: HttpResponseError | HttpNetworkError | HttpCancelledError) => void | Promise<void>;
9
36
  export interface PageFlashData {
10
37
  [key: string]: unknown;
11
38
  }
@@ -93,14 +120,16 @@ export interface Page<SharedProps extends PageProps = PageProps> {
93
120
  };
94
121
  url: string;
95
122
  version: string | null;
96
- clearHistory: boolean;
97
- encryptHistory: boolean;
123
+ clearHistory?: boolean;
124
+ preserveFragment?: boolean;
125
+ encryptHistory?: boolean;
98
126
  deferredProps?: Record<string, NonNullable<VisitOptions['only']>>;
99
127
  initialDeferredProps?: Record<string, NonNullable<VisitOptions['only']>>;
100
128
  mergeProps?: string[];
101
129
  prependProps?: string[];
102
130
  deepMergeProps?: string[];
103
131
  matchPropsOn?: string[];
132
+ sharedProps?: string[];
104
133
  scrollProps?: Record<keyof PageProps, ScrollProp>;
105
134
  flash: FlashData;
106
135
  onceProps?: Record<string, {
@@ -109,6 +138,8 @@ export interface Page<SharedProps extends PageProps = PageProps> {
109
138
  }>;
110
139
  /** @internal */
111
140
  rememberedState: Record<string, unknown>;
141
+ /** @internal */
142
+ optimisticUpdatedAt?: Record<string, number>;
112
143
  }
113
144
  export type ScrollRegion = {
114
145
  top: number;
@@ -130,7 +161,7 @@ export interface ClientSideVisitOptions<TProps = Page['props']> {
130
161
  onFlash?: (flash: FlashData) => void;
131
162
  onSuccess?: (page: Page) => void;
132
163
  }
133
- export type PageResolver = (name: string) => Component;
164
+ export type PageResolver = (name: string, page?: Page) => Component;
134
165
  export type PageHandler<ComponentType = Component> = ({ component, page, preserveState, }: {
135
166
  component: ComponentType;
136
167
  page: Page;
@@ -138,7 +169,7 @@ export type PageHandler<ComponentType = Component> = ({ component, page, preserv
138
169
  }) => Promise<unknown>;
139
170
  export type PreserveStateOption = boolean | 'errors' | ((page: Page) => boolean);
140
171
  export type QueryStringArrayFormatOption = 'indices' | 'brackets';
141
- export type Progress = AxiosProgressEvent;
172
+ export type Progress = HttpProgressEvent;
142
173
  export type LocationVisit = {
143
174
  preserveScroll: boolean;
144
175
  };
@@ -146,6 +177,7 @@ export type CancelToken = {
146
177
  cancel: VoidFunction;
147
178
  };
148
179
  export type CancelTokenCallback = (cancelToken: CancelToken) => void;
180
+ export type OptimisticCallback<TProps = Page['props']> = (props: TProps) => Partial<TProps> | void;
149
181
  export type Visit<T extends RequestPayload = RequestPayload> = {
150
182
  method: Method;
151
183
  data: T;
@@ -164,8 +196,12 @@ export type Visit<T extends RequestPayload = RequestPayload> = {
164
196
  fresh: boolean;
165
197
  reset: string[];
166
198
  preserveUrl: boolean;
199
+ preserveErrors: boolean;
167
200
  invalidateCacheTags: string | string[];
168
201
  viewTransition: boolean | ((viewTransition: ViewTransition) => void);
202
+ optimistic?: OptimisticCallback;
203
+ component: string | null;
204
+ pageProps: Record<string, unknown> | ((currentProps: PageProps, sharedProps: Partial<PageProps>) => Record<string, unknown>) | null;
169
205
  };
170
206
  export type GlobalEventsMap<T extends RequestPayload = RequestPayload> = {
171
207
  before: {
@@ -229,14 +265,14 @@ export type GlobalEventsMap<T extends RequestPayload = RequestPayload> = {
229
265
  };
230
266
  result: void;
231
267
  };
232
- invalid: {
233
- parameters: [AxiosResponse];
268
+ httpException: {
269
+ parameters: [HttpResponse];
234
270
  details: {
235
- response: AxiosResponse;
271
+ response: HttpResponse;
236
272
  };
237
273
  result: boolean | void;
238
274
  };
239
- exception: {
275
+ networkError: {
240
276
  parameters: [Error];
241
277
  details: {
242
278
  exception: Error;
@@ -244,9 +280,9 @@ export type GlobalEventsMap<T extends RequestPayload = RequestPayload> = {
244
280
  result: boolean | void;
245
281
  };
246
282
  prefetched: {
247
- parameters: [AxiosResponse, ActiveVisit<T>];
283
+ parameters: [HttpResponse, ActiveVisit<T>];
248
284
  details: {
249
- response: AxiosResponse;
285
+ response: HttpResponse;
250
286
  fetchedAt: number;
251
287
  visit: ActiveVisit<T>;
252
288
  };
@@ -286,6 +322,8 @@ export type VisitCallbacks<T extends RequestPayload = RequestPayload> = {
286
322
  onCancel: GlobalEventCallback<'cancel', T>;
287
323
  onSuccess: GlobalEventCallback<'success', T>;
288
324
  onError: GlobalEventCallback<'error', T>;
325
+ onHttpException: GlobalEventCallback<'httpException', T>;
326
+ onNetworkError: GlobalEventCallback<'networkError', T>;
289
327
  onFlash: GlobalEventCallback<'flash', T>;
290
328
  onPrefetched: GlobalEventCallback<'prefetched', T>;
291
329
  onPrefetching: GlobalEventCallback<'prefetching', T>;
@@ -310,7 +348,7 @@ export type PendingVisitOptions = {
310
348
  interrupted: boolean;
311
349
  };
312
350
  export type PendingVisit<T extends RequestPayload = RequestPayload> = Visit<T> & PendingVisitOptions;
313
- export type ActiveVisit<T extends RequestPayload = RequestPayload> = PendingVisit<T> & Required<VisitOptions<T>>;
351
+ export type ActiveVisit<T extends RequestPayload = RequestPayload> = PendingVisit<T> & Required<Omit<VisitOptions<T>, 'optimistic'>>;
314
352
  export type InternalActiveVisit = ActiveVisit & {
315
353
  onPrefetchResponse?: (response: Response) => void;
316
354
  onPrefetchError?: (error: Error) => void;
@@ -323,24 +361,35 @@ type FirstLevelOptional<T> = {
323
361
  [P in keyof T[K]]?: T[K][P];
324
362
  } : T[K];
325
363
  };
326
- interface CreateInertiaAppOptions<TComponentResolver, TSetupOptions, TSetupReturn, TAdditionalInertiaAppConfig> {
364
+ type PagesOption = string | {
365
+ path: string;
366
+ extension?: string | string[];
367
+ transform?: (name: string) => string;
368
+ };
369
+ export type ProgressOptions = {
370
+ delay?: number;
371
+ color?: string;
372
+ includeCSS?: boolean;
373
+ showSpinner?: boolean;
374
+ popover?: boolean | null;
375
+ };
376
+ interface BaseCreateInertiaAppOptions<TComponentResolver, TSetupOptions, TSetupReturn, TAdditionalInertiaAppConfig> {
327
377
  resolve: TComponentResolver;
378
+ pages?: PagesOption;
379
+ layout?: (name: string, page: Page) => unknown;
328
380
  setup: (options: TSetupOptions) => TSetupReturn;
329
381
  title?: HeadManagerTitleCallback;
330
382
  defaults?: FirstLevelOptional<InertiaAppConfig & TAdditionalInertiaAppConfig>;
383
+ /** HTTP client or options to use for requests. Defaults to XhrHttpClient. */
384
+ http?: HttpClient | HttpClientOptions;
331
385
  }
332
- export interface CreateInertiaAppOptionsForCSR<SharedProps extends PageProps, TComponentResolver, TSetupOptions, TSetupReturn, TAdditionalInertiaAppConfig> extends CreateInertiaAppOptions<TComponentResolver, TSetupOptions, TSetupReturn, TAdditionalInertiaAppConfig> {
386
+ export interface CreateInertiaAppOptionsForCSR<SharedProps extends PageProps, TComponentResolver, TSetupOptions, TSetupReturn, TAdditionalInertiaAppConfig> extends BaseCreateInertiaAppOptions<TComponentResolver, TSetupOptions, TSetupReturn, TAdditionalInertiaAppConfig> {
333
387
  id?: string;
334
388
  page?: Page<SharedProps>;
335
- progress?: false | {
336
- delay?: number;
337
- color?: string;
338
- includeCSS?: boolean;
339
- showSpinner?: boolean;
340
- };
389
+ progress?: ProgressOptions | false;
341
390
  render?: undefined;
342
391
  }
343
- export interface CreateInertiaAppOptionsForSSR<SharedProps extends PageProps, TComponentResolver, TSetupOptions, TSetupReturn, TAdditionalInertiaAppConfig> extends CreateInertiaAppOptions<TComponentResolver, TSetupOptions, TSetupReturn, TAdditionalInertiaAppConfig> {
392
+ export interface CreateInertiaAppOptionsForSSR<SharedProps extends PageProps, TComponentResolver, TSetupOptions, TSetupReturn, TAdditionalInertiaAppConfig> extends BaseCreateInertiaAppOptions<TComponentResolver, TSetupOptions, TSetupReturn, TAdditionalInertiaAppConfig> {
344
393
  id?: undefined;
345
394
  page: Page<SharedProps>;
346
395
  progress?: undefined;
@@ -352,11 +401,22 @@ export type InertiaAppSSRResponse = {
352
401
  };
353
402
  export type InertiaAppResponse = Promise<InertiaAppSSRResponse | void>;
354
403
  export type HeadManagerTitleCallback = (title: string) => string;
404
+ export interface CreateInertiaAppOptions<TComponentResolver, TSetupOptions, TSetupReturn, TAdditionalInertiaAppConfig> {
405
+ id?: string;
406
+ resolve?: TComponentResolver;
407
+ pages?: PagesOption;
408
+ layout?: (name: string, page: Page) => unknown;
409
+ setup?: (options: TSetupOptions) => TSetupReturn;
410
+ title?: HeadManagerTitleCallback;
411
+ progress?: ProgressOptions | false;
412
+ defaults?: FirstLevelOptional<InertiaAppConfig & TAdditionalInertiaAppConfig>;
413
+ /** HTTP client or options to use for requests. Defaults to XhrHttpClient. */
414
+ http?: HttpClient | HttpClientOptions;
415
+ }
355
416
  export type HeadManagerOnUpdateCallback = (elements: string[]) => void;
356
417
  export type HeadManager = {
357
418
  forceUpdate: () => void;
358
419
  createProvider: () => {
359
- preferredAttribute: () => 'data-inertia' | 'inertia';
360
420
  reconnect: () => void;
361
421
  update: HeadManagerOnUpdateCallback;
362
422
  disconnect: () => void;
@@ -375,20 +435,16 @@ export type InertiaAppConfig = {
375
435
  forceIndicesArrayFormatInFormData: boolean;
376
436
  withAllErrors: boolean;
377
437
  };
378
- future: {
379
- preserveEqualProps: boolean;
380
- useDataInertiaHeadAttribute: boolean;
381
- useDialogForErrorModal: boolean;
382
- useScriptElementForInitialPage: boolean;
383
- };
384
438
  prefetch: {
385
439
  cacheFor: CacheForOption | CacheForOption[];
386
440
  hoverDelay: number;
387
441
  };
388
442
  visitOptions?: (href: string, options: VisitOptions) => VisitOptions;
389
443
  };
390
- export interface LinkComponentBaseProps extends Partial<Pick<Visit<RequestPayload>, 'data' | 'method' | 'replace' | 'preserveScroll' | 'preserveState' | 'preserveUrl' | 'only' | 'except' | 'headers' | 'queryStringArrayFormat' | 'async' | 'viewTransition'> & VisitCallbacks & {
444
+ export interface LinkComponentBaseProps extends Partial<Pick<Visit<RequestPayload>, 'component' | 'data' | 'method' | 'replace' | 'preserveScroll' | 'preserveState' | 'preserveUrl' | 'only' | 'except' | 'headers' | 'queryStringArrayFormat' | 'async' | 'viewTransition'> & VisitCallbacks & {
391
445
  href: string | UrlMethodPair;
446
+ instant: boolean;
447
+ pageProps: Record<string, unknown> | ((currentProps: PageProps, sharedProps: Partial<PageProps>) => Record<string, unknown>) | null;
392
448
  prefetch: boolean | LinkPrefetchOption | LinkPrefetchOption[];
393
449
  cacheFor: CacheForOption | CacheForOption[];
394
450
  cacheTags: string | string[];
@@ -421,7 +477,6 @@ export type PrefetchRemovalTimer = {
421
477
  export type ProgressSettings = {
422
478
  minimum: number;
423
479
  easing: string;
424
- positionUsing: 'translate3d' | 'translate' | 'margin';
425
480
  speed: number;
426
481
  trickle: boolean;
427
482
  trickleSpeed: number;
@@ -432,10 +487,12 @@ export type ProgressSettings = {
432
487
  template: string;
433
488
  includeCSS: boolean;
434
489
  color: string;
490
+ popover: boolean | null;
435
491
  };
436
492
  export type UrlMethodPair = {
437
493
  url: string;
438
494
  method: Method;
495
+ component?: string | string[];
439
496
  };
440
497
  export type UseFormTransformCallback<TForm> = (data: TForm) => object;
441
498
  export type UseFormWithPrecognitionArguments = [Method | (() => Method), string | (() => string)] | [UrlMethodPair | (() => UrlMethodPair)];
@@ -444,13 +501,18 @@ type UseFormPrecognitionArguments<TForm> = [urlMethodPair: UrlMethodPair | (() =
444
501
  export type UseFormArguments<TForm> = UseFormInertiaArguments<TForm> | UseFormPrecognitionArguments<TForm>;
445
502
  export type UseFormSubmitOptions = Omit<VisitOptions, 'data'>;
446
503
  export type UseFormSubmitArguments = [Method, string, UseFormSubmitOptions?] | [UrlMethodPair, UseFormSubmitOptions?] | [UseFormSubmitOptions?];
504
+ export type UseHttpSubmitArguments<TResponse = unknown, TForm = unknown> = [Method, string, UseHttpSubmitOptions<TResponse, TForm>?] | [UrlMethodPair, UseHttpSubmitOptions<TResponse, TForm>?] | [UseHttpSubmitOptions<TResponse, TForm>?];
447
505
  export type FormComponentOptions = Pick<VisitOptions, 'preserveScroll' | 'preserveState' | 'preserveUrl' | 'replace' | 'only' | 'except' | 'reset' | 'viewTransition'>;
506
+ export type FormComponentOptimisticCallback<TProps = Page['props']> = (props: TProps, formData: Record<string, FormDataConvertible>) => Partial<TProps> | void;
448
507
  export type FormComponentProps = Partial<Pick<Visit, 'headers' | 'queryStringArrayFormat' | 'errorBag' | 'showProgress' | 'invalidateCacheTags'> & Omit<VisitCallbacks, 'onPrefetched' | 'onPrefetching'>> & {
449
508
  method?: Method | Uppercase<Method>;
450
509
  action?: string | UrlMethodPair;
510
+ component?: string;
511
+ instant?: boolean;
451
512
  transform?: (data: Record<string, FormDataConvertible>) => Record<string, FormDataConvertible>;
513
+ optimistic?: FormComponentOptimisticCallback;
452
514
  options?: FormComponentOptions;
453
- onSubmitComplete?: (props: FormComponentonSubmitCompleteArguments) => void;
515
+ onSubmitComplete?: (props: FormComponentOnSubmitCompleteArguments) => void;
454
516
  disableWhileProcessing?: boolean;
455
517
  resetOnSuccess?: boolean | string[];
456
518
  resetOnError?: boolean | string[];
@@ -459,28 +521,28 @@ export type FormComponentProps = Partial<Pick<Visit, 'headers' | 'queryStringArr
459
521
  validationTimeout?: number;
460
522
  withAllErrors?: boolean | null;
461
523
  };
462
- export type FormComponentMethods = {
463
- clearErrors: (...fields: string[]) => void;
464
- resetAndClearErrors: (...fields: string[]) => void;
524
+ export type FormComponentMethods<TForm extends object = Record<string, any>> = {
525
+ clearErrors: <K extends FormDataKeys<TForm>>(...fields: K[]) => void;
526
+ resetAndClearErrors: <K extends FormDataKeys<TForm>>(...fields: K[]) => void;
465
527
  setError: {
466
- (field: string, value: ErrorValue): void;
467
- (errors: Record<string, ErrorValue>): void;
528
+ <K extends FormDataKeys<TForm>>(field: K, value: ErrorValue): void;
529
+ (errors: FormDataErrors<TForm>): void;
468
530
  };
469
- reset: (...fields: string[]) => void;
531
+ reset: <K extends FormDataKeys<TForm>>(...fields: K[]) => void;
470
532
  submit: () => void;
471
533
  defaults: () => void;
472
- getData: () => Record<string, FormDataConvertible>;
534
+ getData: () => TForm;
473
535
  getFormData: () => FormData;
474
- valid: (field: string) => boolean;
475
- invalid: (field: string) => boolean;
476
- validate: (field?: string | NamedInputEvent | ValidationConfig, config?: ValidationConfig) => void;
477
- touch: (...fields: string[]) => void;
478
- touched: (field?: string) => boolean;
536
+ valid: <K extends FormDataKeys<TForm>>(field: K) => boolean;
537
+ invalid: <K extends FormDataKeys<TForm>>(field: K) => boolean;
538
+ validate: <K extends FormDataKeys<TForm>>(field?: K | NamedInputEvent | ValidationConfig, config?: ValidationConfig) => void;
539
+ touch: <K extends FormDataKeys<TForm>>(...fields: K[]) => void;
540
+ touched: <K extends FormDataKeys<TForm>>(field?: K) => boolean;
479
541
  validator: () => Validator;
480
542
  };
481
- export type FormComponentonSubmitCompleteArguments = Pick<FormComponentMethods, 'reset' | 'defaults'>;
482
- export type FormComponentState = {
483
- errors: Record<string, ErrorValue>;
543
+ export type FormComponentOnSubmitCompleteArguments<TForm extends object = Record<string, any>> = Pick<FormComponentMethods<TForm>, 'reset' | 'defaults'>;
544
+ export type FormComponentState<TForm extends object = Record<string, any>> = {
545
+ errors: FormDataErrors<TForm>;
484
546
  hasErrors: boolean;
485
547
  processing: boolean;
486
548
  progress: Progress | null;
@@ -489,14 +551,15 @@ export type FormComponentState = {
489
551
  isDirty: boolean;
490
552
  validating: boolean;
491
553
  };
492
- export type FormComponentSlotProps = FormComponentMethods & FormComponentState;
493
- export type FormComponentRef = FormComponentSlotProps;
554
+ export type FormComponentSlotProps<TForm extends object = Record<string, any>> = FormComponentMethods<TForm> & FormComponentState<TForm>;
555
+ export type FormComponentRef<TForm extends object = Record<string, any>> = FormComponentSlotProps<TForm>;
494
556
  export interface UseInfiniteScrollOptions {
495
557
  getPropName: () => string;
496
558
  inReverseMode: () => boolean;
497
559
  shouldFetchNext: () => boolean;
498
560
  shouldFetchPrevious: () => boolean;
499
561
  shouldPreserveUrl: () => boolean;
562
+ getReloadOptions?: () => ReloadOptions;
500
563
  getTriggerMargin: () => number;
501
564
  getStartElement: () => HTMLElement;
502
565
  getEndElement: () => HTMLElement;
@@ -566,6 +629,20 @@ export interface InfiniteScrollComponentBaseProps {
566
629
  onlyNext?: boolean;
567
630
  onlyPrevious?: boolean;
568
631
  }
632
+ export type UseHttpOptions<TResponse = unknown> = {
633
+ onBefore?: () => boolean | void;
634
+ onStart?: () => void;
635
+ onProgress?: (progress: HttpProgressEvent) => void;
636
+ onSuccess?: (response: TResponse) => void;
637
+ onError?: (errors: Errors) => void;
638
+ onFinish?: () => void;
639
+ onCancel?: () => void;
640
+ onCancelToken?: (cancelToken: CancelToken) => void;
641
+ };
642
+ export type UseHttpSubmitOptions<TResponse = unknown, TForm = unknown> = UseHttpOptions<TResponse> & {
643
+ headers?: HttpRequestHeaders;
644
+ optimistic?: (currentData: TForm) => Partial<TForm>;
645
+ };
569
646
  declare global {
570
647
  interface DocumentEventMap {
571
648
  'inertia:before': GlobalEvent<'before'>;
@@ -573,8 +650,8 @@ declare global {
573
650
  'inertia:progress': GlobalEvent<'progress'>;
574
651
  'inertia:success': GlobalEvent<'success'>;
575
652
  'inertia:error': GlobalEvent<'error'>;
576
- 'inertia:invalid': GlobalEvent<'invalid'>;
577
- 'inertia:exception': GlobalEvent<'exception'>;
653
+ 'inertia:httpException': GlobalEvent<'httpException'>;
654
+ 'inertia:networkError': GlobalEvent<'networkError'>;
578
655
  'inertia:finish': GlobalEvent<'finish'>;
579
656
  'inertia:beforeUpdate': GlobalEvent<'beforeUpdate'>;
580
657
  'inertia:navigate': GlobalEvent<'navigate'>;
package/types/url.d.ts CHANGED
@@ -8,6 +8,7 @@ export declare const setHashIfSameUrl: (originUrl: URL | Location, destinationUr
8
8
  export declare const isSameUrlWithoutHash: (url1: URL | Location, url2: URL | Location) => boolean;
9
9
  export declare const isSameUrlWithoutQueryOrHash: (url1: URL | Location, url2: URL | Location) => boolean;
10
10
  export declare function isUrlMethodPair(href: unknown): href is UrlMethodPair;
11
+ export declare function resolveUrlMethodPairComponent(pair: UrlMethodPair): string | null;
11
12
  export declare function urlHasProtocol(url: string): boolean;
12
13
  export declare function urlToString(url: URL | string, absolute: boolean): string;
13
14
  export {};
@@ -0,0 +1,12 @@
1
+ import { HttpClient, HttpClientOptions, HttpRequestConfig, HttpResponse } from './types';
2
+ /**
3
+ * Inertia's built-in HTTP client using XMLHttpRequest
4
+ */
5
+ export declare class XhrHttpClient implements HttpClient {
6
+ protected xsrfCookieName: string;
7
+ protected xsrfHeaderName: string;
8
+ constructor(options?: HttpClientOptions);
9
+ request(config: HttpRequestConfig): Promise<HttpResponse>;
10
+ protected doRequest(config: HttpRequestConfig): Promise<HttpResponse>;
11
+ }
12
+ export declare const xhrHttpClient: XhrHttpClient;