react-shopwave-connect 0.2.0 → 0.4.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/README.md +215 -24
- package/dist/core/index.cjs +903 -88
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +604 -23
- package/dist/core/index.d.ts +604 -23
- package/dist/core/index.js +845 -89
- package/dist/core/index.js.map +1 -1
- package/dist/hooks/index.cjs +735 -109
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.cts +263 -12
- package/dist/hooks/index.d.ts +263 -12
- package/dist/hooks/index.js +732 -111
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.cjs +1087 -107
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +670 -24
- package/dist/index.d.ts +670 -24
- package/dist/index.js +1025 -109
- package/dist/index.js.map +1 -1
- package/dist/next/index.cjs +560 -2
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +249 -1
- package/dist/next/index.d.ts +249 -1
- package/dist/next/index.js +556 -3
- package/dist/next/index.js.map +1 -1
- package/dist/server/index.cjs +531 -1
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +258 -1
- package/dist/server/index.d.ts +258 -1
- package/dist/server/index.js +521 -2
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -34,19 +34,24 @@ interface apiResponse {
|
|
|
34
34
|
/**
|
|
35
35
|
* Per-call configuration shared by every core API function.
|
|
36
36
|
*
|
|
37
|
-
* Nothing here is React-specific.
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* `token
|
|
37
|
+
* Nothing here is React-specific. In the browser/Next leave `baseUrl` empty to
|
|
38
|
+
* call the app's own `/api/...` routes (which hold the session). From Node, a
|
|
39
|
+
* CLI or tests, pass an absolute `baseUrl` pointing at such an app plus a
|
|
40
|
+
* `token`.
|
|
41
41
|
*/
|
|
42
42
|
interface RequestOptions {
|
|
43
43
|
/**
|
|
44
44
|
* Prefixed in front of every request path. Leave undefined in the browser to
|
|
45
|
-
* use relative URLs (e.g. `/api/products`).
|
|
46
|
-
* `
|
|
45
|
+
* use relative URLs (e.g. `/api/products`). From Node pass the app origin,
|
|
46
|
+
* e.g. `http://localhost:3000`.
|
|
47
47
|
*/
|
|
48
48
|
baseUrl?: string;
|
|
49
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* Shopwave OAuth access token. Sent as `Authorization: OAuth <token>` on every
|
|
51
|
+
* request (GET, POST, PUT and DELETE alike). A value that already starts with
|
|
52
|
+
* a scheme (`OAuth …` / `Bearer …`) is sent as-is. Leave unset in the browser:
|
|
53
|
+
* the app's routes use the session cookie.
|
|
54
|
+
*/
|
|
50
55
|
token?: string;
|
|
51
56
|
/**
|
|
52
57
|
* Custom fetch implementation. Defaults to the global `fetch`. Useful for
|
|
@@ -58,24 +63,79 @@ interface RequestOptions {
|
|
|
58
63
|
}
|
|
59
64
|
/** The Shopwave API ships request metadata inside a single `extras` header. */
|
|
60
65
|
type Extras = Record<string, unknown>;
|
|
66
|
+
/** Options with `token` filled in from a legacy per-call `params.token`, unless `options.token` is set. */
|
|
67
|
+
declare function withToken(options: RequestOptions, token: string | undefined): RequestOptions;
|
|
68
|
+
/** `Authorization` header value for a token (`OAuth <token>` unless it already has a scheme). */
|
|
69
|
+
declare function authorizationFor(token: string): string;
|
|
70
|
+
/**
|
|
71
|
+
* Low-level request: resolves `{ status, body }` for 2xx responses and throws
|
|
72
|
+
* {@link ShopwaveApiError} otherwise. `init.headers` are merged over the
|
|
73
|
+
* standard ones (Accept + Authorization).
|
|
74
|
+
*/
|
|
75
|
+
declare function apiRequest(path: string, init: RequestInit, options?: RequestOptions): Promise<{
|
|
76
|
+
status: number;
|
|
77
|
+
body: unknown;
|
|
78
|
+
}>;
|
|
61
79
|
/**
|
|
62
80
|
* Returns a JSON string of API errors if the response carries any, otherwise
|
|
63
|
-
* `null`.
|
|
81
|
+
* `null`. Kept for backwards compatibility; prefer `ShopwaveApiError`.
|
|
64
82
|
*/
|
|
65
83
|
declare function getApiErrors(api?: apiResponse): string | null;
|
|
66
84
|
/**
|
|
67
|
-
* GET helper. Sends request metadata via the `extras` header
|
|
68
|
-
*
|
|
85
|
+
* GET helper. Sends request metadata via the `extras` header, parses JSON and
|
|
86
|
+
* returns the payload. Throws {@link ShopwaveApiError} for non-2xx responses.
|
|
69
87
|
*/
|
|
70
88
|
declare function apiGet<T>(path: string, extras: Extras, options?: RequestOptions): Promise<T>;
|
|
71
89
|
/**
|
|
72
|
-
* JSON body helper for POST/PUT mutations.
|
|
90
|
+
* JSON body helper for POST/PUT mutations. Returns the parsed body (`null` for
|
|
91
|
+
* an empty one). Throws {@link ShopwaveApiError} for non-2xx responses.
|
|
73
92
|
*/
|
|
74
93
|
declare function apiSend<T>(path: string, method: "POST" | "PUT", payload: unknown, options?: RequestOptions): Promise<T>;
|
|
75
94
|
/**
|
|
76
|
-
* DELETE helper.
|
|
95
|
+
* DELETE helper. Shopwave answers deletes with 205 and an empty body; if a body
|
|
96
|
+
* does come back and carries API errors, they are thrown. Returns the parsed
|
|
97
|
+
* body (`null` when empty).
|
|
77
98
|
*/
|
|
78
|
-
declare function apiDelete(path: string, options?: RequestOptions): Promise<
|
|
99
|
+
declare function apiDelete(path: string, options?: RequestOptions): Promise<unknown>;
|
|
100
|
+
//#endregion
|
|
101
|
+
//#region src/core/errors.d.ts
|
|
102
|
+
/** Map of Shopwave API errors as found in `api.message.errors`, keyed by error code. */
|
|
103
|
+
type ShopwaveErrorMap = Record<string, Partial<ErrorMessage>>;
|
|
104
|
+
/**
|
|
105
|
+
* Thrown by every `core` function when a request fails, either at the HTTP
|
|
106
|
+
* level (`status` is the HTTP status) or because the Shopwave response carried
|
|
107
|
+
* `api.message.errors` (then `status` is the HTTP status of that response,
|
|
108
|
+
* usually 200/201, and `errors` holds the API errors).
|
|
109
|
+
*
|
|
110
|
+
* Branch on `status` (e.g. `isUnauthorized` → send the user to log in) rather
|
|
111
|
+
* than parsing `message`.
|
|
112
|
+
*/
|
|
113
|
+
declare class ShopwaveApiError extends Error {
|
|
114
|
+
/** HTTP status of the response (0 when the request never got a response). */
|
|
115
|
+
readonly status: number;
|
|
116
|
+
/** Shopwave API errors, when the response carried any. */
|
|
117
|
+
readonly errors: ShopwaveErrorMap | null;
|
|
118
|
+
/** Parsed response body, when there was one. */
|
|
119
|
+
readonly body?: unknown;
|
|
120
|
+
constructor(status: number, message: string, details?: {
|
|
121
|
+
errors?: ShopwaveErrorMap | null;
|
|
122
|
+
body?: unknown;
|
|
123
|
+
cause?: unknown;
|
|
124
|
+
});
|
|
125
|
+
/** 401, or the Shopwave "token expired / invalid" error 908. */
|
|
126
|
+
get isUnauthorized(): boolean;
|
|
127
|
+
get isNotFound(): boolean;
|
|
128
|
+
}
|
|
129
|
+
/** The `api.message.errors` (or `api.message.error`) map of a Shopwave response, or `null` when there are none. */
|
|
130
|
+
declare function getApiErrorMap(body: unknown): ShopwaveErrorMap | null;
|
|
131
|
+
/**
|
|
132
|
+
* Builds a {@link ShopwaveApiError} for a response. Understands the Shopwave
|
|
133
|
+
* envelope (`api.message.errors`) and the `{ error, message }` bodies returned
|
|
134
|
+
* by the `react-shopwave-connect/next` routes.
|
|
135
|
+
*/
|
|
136
|
+
declare function toShopwaveApiError(status: number, body: unknown, fallback?: string): ShopwaveApiError;
|
|
137
|
+
/** Throws a {@link ShopwaveApiError} when the response body carries API errors. */
|
|
138
|
+
declare function assertNoApiErrors(body: unknown, status: number): void;
|
|
79
139
|
//#endregion
|
|
80
140
|
//#region src/core/category.d.ts
|
|
81
141
|
interface Category {
|
|
@@ -96,7 +156,7 @@ interface FetchCategoriesParams {
|
|
|
96
156
|
categoryIds?: Array<number>;
|
|
97
157
|
/** Whether to include soft-deleted records. Defaults to `false`. */
|
|
98
158
|
deleted?: boolean;
|
|
99
|
-
/**
|
|
159
|
+
/** @deprecated Pass `options.token` instead. Sent the same way (Authorization header). */
|
|
100
160
|
token?: string;
|
|
101
161
|
}
|
|
102
162
|
/**
|
|
@@ -159,6 +219,8 @@ interface FetchEmployeesParams {
|
|
|
159
219
|
employeeIds?: Array<number>;
|
|
160
220
|
/** Whether to include soft-deleted records. Defaults to `false`. */
|
|
161
221
|
deleted?: boolean;
|
|
222
|
+
/** @deprecated Pass `options.token` instead. Sent the same way (Authorization header). */
|
|
223
|
+
token?: string;
|
|
162
224
|
}
|
|
163
225
|
/**
|
|
164
226
|
* Fetches employees. Returns the flattened `Employee[]`.
|
|
@@ -203,7 +265,7 @@ interface FetchProductsParams {
|
|
|
203
265
|
storeId?: number;
|
|
204
266
|
/** Whether to include soft-deleted records. Defaults to `false`. */
|
|
205
267
|
deleted?: boolean;
|
|
206
|
-
/**
|
|
268
|
+
/** @deprecated Pass `options.token` instead. Sent the same way (Authorization header). */
|
|
207
269
|
token?: string;
|
|
208
270
|
}
|
|
209
271
|
/**
|
|
@@ -265,7 +327,7 @@ interface FetchPromotionsParams {
|
|
|
265
327
|
active?: boolean;
|
|
266
328
|
/** Filter for expired promotions. */
|
|
267
329
|
expired?: boolean;
|
|
268
|
-
/**
|
|
330
|
+
/** @deprecated Pass `options.token` instead. Sent the same way (Authorization header). */
|
|
269
331
|
token?: string;
|
|
270
332
|
}
|
|
271
333
|
/**
|
|
@@ -297,8 +359,12 @@ interface StoreResponse {
|
|
|
297
359
|
api: apiResponse;
|
|
298
360
|
}
|
|
299
361
|
interface FetchStoresParams {
|
|
362
|
+
/** Optional id filter. */
|
|
363
|
+
storeIds?: Array<number>;
|
|
300
364
|
/** Whether to include soft-deleted records. Defaults to `false`. */
|
|
301
365
|
deleted?: boolean;
|
|
366
|
+
/** @deprecated Pass `options.token` instead. Sent the same way (Authorization header). */
|
|
367
|
+
token?: string;
|
|
302
368
|
}
|
|
303
369
|
/**
|
|
304
370
|
* Fetches stores. Returns the flattened `Store[]`.
|
|
@@ -376,10 +442,12 @@ declare function logout(options?: RequestOptions): Promise<void>;
|
|
|
376
442
|
//#endregion
|
|
377
443
|
//#region src/core/entity.d.ts
|
|
378
444
|
/**
|
|
379
|
-
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
*
|
|
445
|
+
* Low-level, untyped mutations: the caller supplies the route and payload.
|
|
446
|
+
*
|
|
447
|
+
* Prefer the typed functions in `./entities` (`saveProduct`, `deleteCategory`,
|
|
448
|
+
* …): they know each route, build the request envelope, check the response and
|
|
449
|
+
* return the saved entity with its id. Keep these for routes that have no typed
|
|
450
|
+
* function yet (e.g. `/api/merchant`).
|
|
383
451
|
*/
|
|
384
452
|
/**
|
|
385
453
|
* Deletes `/api/{resourcePath}/{entityId}`.
|
|
@@ -398,8 +466,9 @@ interface SubmitEntityResponse<R = any> {
|
|
|
398
466
|
[key: string]: unknown;
|
|
399
467
|
}
|
|
400
468
|
/**
|
|
401
|
-
* Sends a create/update mutation
|
|
402
|
-
* (
|
|
469
|
+
* Sends a create/update mutation. Returns the response's `result` field when it
|
|
470
|
+
* has one (older app routes wrapped their answer that way), otherwise the whole
|
|
471
|
+
* parsed response body. Throws `ShopwaveApiError` on HTTP or API errors.
|
|
403
472
|
*/
|
|
404
473
|
declare function submitEntity<R = any>({
|
|
405
474
|
endpoint,
|
|
@@ -407,6 +476,128 @@ declare function submitEntity<R = any>({
|
|
|
407
476
|
payload
|
|
408
477
|
}: SubmitEntityArgs, options?: RequestOptions): Promise<R | undefined>;
|
|
409
478
|
//#endregion
|
|
479
|
+
//#region src/core/entities.d.ts
|
|
480
|
+
/**
|
|
481
|
+
* How each Shopwave entity is addressed, shared by the client functions below
|
|
482
|
+
* and the server route handlers in `react-shopwave-connect/next`, so the two
|
|
483
|
+
* halves of the contract can't drift apart.
|
|
484
|
+
*/
|
|
485
|
+
interface EntityDefinition {
|
|
486
|
+
/** App route segment: `/api/<route>` and `/api/<route>/<id>`. */
|
|
487
|
+
route: string;
|
|
488
|
+
/** Key of the entity map in requests and responses (`{ products: { … } }`). */
|
|
489
|
+
collection: string;
|
|
490
|
+
/** Shopwave API path (`GET/POST/DELETE {apiUrl}/<upstream>`). */
|
|
491
|
+
upstream: string;
|
|
492
|
+
/** Header that filters reads by id (`productIds: 1,2`). */
|
|
493
|
+
idsHeader: string;
|
|
494
|
+
/** Header that names the record to delete (`productId: 1`). */
|
|
495
|
+
idHeader: string;
|
|
496
|
+
/** False for read-only entities (the API has no POST for them). */
|
|
497
|
+
writable: boolean;
|
|
498
|
+
/**
|
|
499
|
+
* How a delete is done upstream:
|
|
500
|
+
* - `"delete"`: `DELETE {apiUrl}/<upstream>` with the `idHeader` (205, empty body)
|
|
501
|
+
* - `"retire"`: the API has no DELETE; the record is retired by POSTing
|
|
502
|
+
* `{ id, [retireField]: <now> }` (employees: `exitDate`, promotions: `endDate`)
|
|
503
|
+
* - `"none"`: can't be deleted
|
|
504
|
+
*/
|
|
505
|
+
deleteMode: "delete" | "retire" | "none";
|
|
506
|
+
/** Date field set to "now" when `deleteMode` is `"retire"`. */
|
|
507
|
+
retireField?: string;
|
|
508
|
+
}
|
|
509
|
+
type EntityKind = "product" | "category" | "store" | "promotion" | "employee" | "consumer";
|
|
510
|
+
/** Entities that can be created/updated through the API. */
|
|
511
|
+
type WritableEntityKind = Exclude<EntityKind, "consumer">;
|
|
512
|
+
/** Entities that can be deleted (or retired) through the API. */
|
|
513
|
+
type DeletableEntityKind = Exclude<EntityKind, "consumer">;
|
|
514
|
+
/**
|
|
515
|
+
* Addressing per entity, following the Shopwave API reference
|
|
516
|
+
* (https://developer.merchantstack.com/api-reference.html) and checked against
|
|
517
|
+
* the live API where noted:
|
|
518
|
+
* - category/product/store: GET filtered by `<x>Ids`, POST upsert, DELETE with `<x>Id` → 205.
|
|
519
|
+
* - promotion: no DELETE; "deleting" a promotion ends it by setting `endDate` to now.
|
|
520
|
+
* - employee: POST updates `roleId`, `joinedDate`, `exitDate` (names can't be
|
|
521
|
+
* changed); no DELETE (live API: "Cannot DELETE /employee") → retired via `exitDate`.
|
|
522
|
+
* - consumer: read-only (`GET` with `ids`, comma-separated); live POST → 404.
|
|
523
|
+
*/
|
|
524
|
+
declare const SHOPWAVE_ENTITIES: Readonly<Record<EntityKind, EntityDefinition>>;
|
|
525
|
+
interface EntityTypes {
|
|
526
|
+
product: Product;
|
|
527
|
+
category: Category;
|
|
528
|
+
store: Store;
|
|
529
|
+
promotion: Promotion;
|
|
530
|
+
employee: Employee;
|
|
531
|
+
consumer: Consumer;
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* What a save accepts: any subset of the entity's fields (plus extra API
|
|
535
|
+
* fields the type doesn't list). Include `id` to update; leave it out to create.
|
|
536
|
+
*/
|
|
537
|
+
type EntityInput<T> = Partial<T> & {
|
|
538
|
+
[field: string]: unknown;
|
|
539
|
+
};
|
|
540
|
+
/** A saved entity: always has its `id`. */
|
|
541
|
+
type Saved<T> = T & {
|
|
542
|
+
id: number;
|
|
543
|
+
};
|
|
544
|
+
type EntityId = number | string;
|
|
545
|
+
/**
|
|
546
|
+
* Creates or updates several entities of one kind in a single request and
|
|
547
|
+
* returns them as saved (with their ids), in the order given.
|
|
548
|
+
*
|
|
549
|
+
* The request is `{ <collection>: { "0": item0, "1": item1, … } }`. Shopwave
|
|
550
|
+
* answers 201 and echoes every entity under the ref it was sent with, so each
|
|
551
|
+
* result is matched back by ref. Throws {@link ShopwaveApiError} when the
|
|
552
|
+
* response carries `api.message.errors` or any ref is missing from it.
|
|
553
|
+
*
|
|
554
|
+
* The API's partial-success semantics for multi-entity saves haven't been
|
|
555
|
+
* confirmed yet; single saves (`saveProduct` etc.) are the tested path.
|
|
556
|
+
*/
|
|
557
|
+
declare function saveEntities<K extends WritableEntityKind>(kind: K, items: ReadonlyArray<EntityInput<EntityTypes[K]>>, options?: RequestOptions): Promise<Array<Saved<EntityTypes[K]>>>;
|
|
558
|
+
/** Creates (no `id`) or updates (with `id`) one entity and returns it with its id. */
|
|
559
|
+
declare function saveEntity<K extends WritableEntityKind>(kind: K, item: EntityInput<EntityTypes[K]>, options?: RequestOptions): Promise<Saved<EntityTypes[K]>>;
|
|
560
|
+
/**
|
|
561
|
+
* Deletes one entity (a soft delete: it stays readable with `deleted: true`).
|
|
562
|
+
* Employees and promotions can't be deleted in Shopwave; for them the route
|
|
563
|
+
* sets `exitDate` / `endDate` to now instead (see `SHOPWAVE_ENTITIES`).
|
|
564
|
+
*
|
|
565
|
+
* Shopwave answers 205 with an empty body — also for ids that don't exist — so
|
|
566
|
+
* a resolved promise means "the API accepted the request", not "a record was
|
|
567
|
+
* deleted".
|
|
568
|
+
*/
|
|
569
|
+
declare function deleteEntityById(kind: DeletableEntityKind, id: EntityId, options?: RequestOptions): Promise<void>;
|
|
570
|
+
interface FetchByIdParams {
|
|
571
|
+
/** Also return the record when it has been deleted. Defaults to `false`. */
|
|
572
|
+
deleted?: boolean;
|
|
573
|
+
}
|
|
574
|
+
/** Reads one entity by id, or `null` when there's no such (non-deleted) record. */
|
|
575
|
+
declare function fetchEntityById<K extends EntityKind>(kind: K, id: EntityId, params?: FetchByIdParams, options?: RequestOptions): Promise<EntityTypes[K] | null>;
|
|
576
|
+
declare const saveProduct: (product: EntityInput<Product>, options?: RequestOptions) => Promise<Saved<Product>>;
|
|
577
|
+
declare const deleteProduct: (id: EntityId, options?: RequestOptions) => Promise<void>;
|
|
578
|
+
declare const fetchProduct: (id: EntityId, params?: FetchByIdParams, options?: RequestOptions) => Promise<Product | null>;
|
|
579
|
+
declare const saveCategory: (category: EntityInput<Category>, options?: RequestOptions) => Promise<Saved<Category>>;
|
|
580
|
+
declare const deleteCategory: (id: EntityId, options?: RequestOptions) => Promise<void>;
|
|
581
|
+
declare const fetchCategory: (id: EntityId, params?: FetchByIdParams, options?: RequestOptions) => Promise<Category | null>;
|
|
582
|
+
declare const saveStore: (store: EntityInput<Store>, options?: RequestOptions) => Promise<Saved<Store>>;
|
|
583
|
+
declare const deleteStore: (id: EntityId, options?: RequestOptions) => Promise<void>;
|
|
584
|
+
declare const fetchStore: (id: EntityId, params?: FetchByIdParams, options?: RequestOptions) => Promise<Store | null>;
|
|
585
|
+
declare const savePromotion: (promotion: EntityInput<Promotion>, options?: RequestOptions) => Promise<Saved<Promotion>>;
|
|
586
|
+
/** Ends a promotion: Shopwave has no promotion DELETE, so this sets `endDate` to now. */
|
|
587
|
+
declare const deletePromotion: (id: EntityId, options?: RequestOptions) => Promise<void>;
|
|
588
|
+
declare const fetchPromotion: (id: EntityId, params?: FetchByIdParams, options?: RequestOptions) => Promise<Promotion | null>;
|
|
589
|
+
/**
|
|
590
|
+
* Creates an employee, or updates one (with `id`). On update Shopwave only
|
|
591
|
+
* applies `roleId`, `joinedDate` and `exitDate` (and store roles); names and
|
|
592
|
+
* email can't be changed, and the echo only carries the updated fields — re-read
|
|
593
|
+
* with `fetchEmployee` if you need the stored record.
|
|
594
|
+
*/
|
|
595
|
+
declare const saveEmployee: (employee: EntityInput<Employee>, options?: RequestOptions) => Promise<Saved<Employee>>;
|
|
596
|
+
/** Retires an employee: Shopwave has no employee DELETE, so this sets `exitDate` to now. */
|
|
597
|
+
declare const deleteEmployee: (id: EntityId, options?: RequestOptions) => Promise<void>;
|
|
598
|
+
declare const fetchEmployee: (id: EntityId, params?: FetchByIdParams, options?: RequestOptions) => Promise<Employee | null>;
|
|
599
|
+
declare const fetchConsumer: (id: EntityId, params?: FetchByIdParams, options?: RequestOptions) => Promise<Consumer | null>;
|
|
600
|
+
//#endregion
|
|
410
601
|
//#region src/core/basket.d.ts
|
|
411
602
|
interface BasketItem {
|
|
412
603
|
productId: number;
|
|
@@ -505,15 +696,415 @@ declare function buildConsumerMap(consumers: Consumer[] | null | undefined): {
|
|
|
505
696
|
[id: number]: Consumer;
|
|
506
697
|
};
|
|
507
698
|
//#endregion
|
|
699
|
+
//#region src/core/resources.d.ts
|
|
700
|
+
/**
|
|
701
|
+
* Singleton resources (one per logged-in user) and the uploader.
|
|
702
|
+
*
|
|
703
|
+
* Like `SHOPWAVE_ENTITIES`, this table is shared by the client functions
|
|
704
|
+
* (`fetchMerchant`, `fetchUser`, `uploadImage`, …) and the server route
|
|
705
|
+
* handlers in `react-shopwave-connect/next`, so the two halves of the contract
|
|
706
|
+
* can't drift apart. These aren't in `SHOPWAVE_ENTITIES` because they aren't
|
|
707
|
+
* keyed collections: `GET /merchant` answers `{ merchant: {…} }`, not a map by id.
|
|
708
|
+
*/
|
|
709
|
+
interface ResourceDefinition {
|
|
710
|
+
/** App route: `/api/<route>`. */
|
|
711
|
+
route: string;
|
|
712
|
+
/** Key of the object in requests and responses (`{ merchant: {…} }`). */
|
|
713
|
+
key: string;
|
|
714
|
+
/** Shopwave API path. */
|
|
715
|
+
upstream: string;
|
|
716
|
+
/** False for read-only resources. */
|
|
717
|
+
writable: boolean;
|
|
718
|
+
}
|
|
719
|
+
type ResourceKind = "merchant" | "user";
|
|
720
|
+
/**
|
|
721
|
+
* - merchant: `GET /merchant`; `POST /merchant` with `{ merchant: { id, … } }`
|
|
722
|
+
* updates it (without an `id` the API would create a second merchant, so the
|
|
723
|
+
* route requires one).
|
|
724
|
+
* - user: `GET /user` (read-only here; the API's `POST /user` invites users and
|
|
725
|
+
* needs the admin scope).
|
|
726
|
+
*/
|
|
727
|
+
declare const SHOPWAVE_RESOURCES: Readonly<Record<ResourceKind, ResourceDefinition>>;
|
|
728
|
+
/**
|
|
729
|
+
* What an upload is for. Sent to Shopwave as the `contentType` header of
|
|
730
|
+
* `PUT /uploader`. `merchant` is for the receipt and square logos (stored
|
|
731
|
+
* under `…/merchant/<id>/logo/`), `merchantFeature` for the featured image.
|
|
732
|
+
* The API spells two of its values `applicaionLogo` / `applicaionImages`; use
|
|
733
|
+
* the correctly spelt names here and the server maps them.
|
|
734
|
+
*/
|
|
735
|
+
type UploadKind = "merchant" | "merchantFeature" | "product" | "user" | "applicationLogo" | "applicationImages";
|
|
736
|
+
/** `UploadKind` → the API's `contentType` value. */
|
|
737
|
+
declare const UPLOAD_KINDS: Readonly<Record<UploadKind, string>>;
|
|
738
|
+
/** How uploads are addressed (app route and Shopwave uploader). */
|
|
739
|
+
declare const SHOPWAVE_UPLOAD: Readonly<{
|
|
740
|
+
/** App route: `POST /api/<route>` (multipart `file` + `kind`). */
|
|
741
|
+
route: "upload";
|
|
742
|
+
/** Shopwave path: `PUT {apiUrl}/uploader` (multipart `file`). */
|
|
743
|
+
upstream: "uploader";
|
|
744
|
+
/** Header that carries the upload kind upstream. */
|
|
745
|
+
kindHeader: "contentType";
|
|
746
|
+
/** Multipart field name for the file, on both sides. */
|
|
747
|
+
fileField: "file";
|
|
748
|
+
/** Multipart field name for the kind in the app request. */
|
|
749
|
+
kindField: "kind";
|
|
750
|
+
}>;
|
|
751
|
+
declare function isUploadKind(value: unknown): value is UploadKind;
|
|
752
|
+
//#endregion
|
|
753
|
+
//#region src/core/imageFit.d.ts
|
|
754
|
+
/**
|
|
755
|
+
* Browser helpers for getting an image to an exact pixel size before upload.
|
|
756
|
+
*
|
|
757
|
+
* Shopwave doesn't resize uploads, and the merchant image slots have fixed
|
|
758
|
+
* sizes (adminV1 refuses any other size), so the SDK scales and centre-crops
|
|
759
|
+
* the image in the browser instead.
|
|
760
|
+
*/
|
|
761
|
+
/** Source rectangle to draw so the image covers `dstWidth × dstHeight` (centre crop, no stretching). */
|
|
762
|
+
interface CropRect {
|
|
763
|
+
sx: number;
|
|
764
|
+
sy: number;
|
|
765
|
+
sw: number;
|
|
766
|
+
sh: number;
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* The centred part of a `srcWidth × srcHeight` image with the aspect ratio of
|
|
770
|
+
* `dstWidth × dstHeight` (like CSS `object-fit: cover`).
|
|
771
|
+
*/
|
|
772
|
+
declare function coverCrop(srcWidth: number, srcHeight: number, dstWidth: number, dstHeight: number): CropRect;
|
|
773
|
+
interface ImageSize {
|
|
774
|
+
width: number;
|
|
775
|
+
height: number;
|
|
776
|
+
}
|
|
777
|
+
/** Reads an image file's pixel size (browser only). */
|
|
778
|
+
declare function readImageSize(file: Blob): Promise<ImageSize>;
|
|
779
|
+
interface FitImageOptions {
|
|
780
|
+
/** Output type. Default `"image/png"`. */
|
|
781
|
+
type?: "image/png" | "image/jpeg" | "image/webp";
|
|
782
|
+
/** 0–1, for JPEG/WebP. */
|
|
783
|
+
quality?: number;
|
|
784
|
+
/** Return the file untouched when it's already the right size and type. Default true. */
|
|
785
|
+
keepIfExact?: boolean;
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* Returns the image at exactly `width × height`: scaled and centre-cropped
|
|
789
|
+
* (never stretched). Browser only. The result is a `File` named like the
|
|
790
|
+
* input, with the output type's extension.
|
|
791
|
+
*/
|
|
792
|
+
declare function fitImageToSize(file: Blob, width: number, height: number, options?: FitImageOptions): Promise<File>;
|
|
793
|
+
//#endregion
|
|
794
|
+
//#region src/core/upload.d.ts
|
|
795
|
+
interface UploadImageParams {
|
|
796
|
+
/** What the image is for (sent to Shopwave as the uploader's `contentType`). */
|
|
797
|
+
kind: UploadKind;
|
|
798
|
+
/** File name to send when `file` is a Blob without one. Defaults to `File.name`, else `"upload"`. */
|
|
799
|
+
fileName?: string;
|
|
800
|
+
}
|
|
801
|
+
interface UploadedImage {
|
|
802
|
+
/** The generated file name (products store it in `images`). */
|
|
803
|
+
id: string;
|
|
804
|
+
/** Public HTTPS URL of the file (see {@link getImageUrl}). */
|
|
805
|
+
url: string;
|
|
806
|
+
/** The URL exactly as the API returned it (`http://static.merchantstack.com/images/…`). */
|
|
807
|
+
path: string;
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* HTTPS host that serves the Shopwave CDN's images. The API returns
|
|
811
|
+
* `http://static.merchantstack.com/images/…`, which has no HTTPS endpoint
|
|
812
|
+
* (checked Sep 2026), so pages served over HTTPS can't show it.
|
|
813
|
+
*/
|
|
814
|
+
declare const SHOPWAVE_IMAGE_BASE_URL = "https://s3-eu-west-1.amazonaws.com/static.merchantstack.com/images";
|
|
815
|
+
/**
|
|
816
|
+
* Turns an image path from the API into a URL a page can load over HTTPS:
|
|
817
|
+
* `http://static.merchantstack.com/images/x.png` → `<base>/x.png`, and a
|
|
818
|
+
* relative path (`merchant/5644/logo/x.png`) → `<base>/merchant/5644/logo/x.png`.
|
|
819
|
+
* Other absolute URLs are returned unchanged; empty input gives `""`.
|
|
820
|
+
*/
|
|
821
|
+
declare function getImageUrl(path: string | null | undefined, base?: string): string;
|
|
822
|
+
/**
|
|
823
|
+
* Uploads one image to the Shopwave CDN through the app's `/api/upload` route
|
|
824
|
+
* (`createShopwaveApi(...).upload()`), and returns its id and URL.
|
|
825
|
+
*
|
|
826
|
+
* The file only lands on the CDN (under `…/images/<kind>/<merchantId>/…`); a
|
|
827
|
+
* record has to point at it to use it. Live API (Sep 2026): the upload answers
|
|
828
|
+
* 201 `{ fileName, path }`.
|
|
829
|
+
*/
|
|
830
|
+
declare function uploadImage(file: Blob, params: UploadImageParams, options?: RequestOptions): Promise<UploadedImage>;
|
|
831
|
+
//#endregion
|
|
832
|
+
//#region src/core/merchantImages.d.ts
|
|
833
|
+
/**
|
|
834
|
+
* Merchant images, as adminV1 saves them and adminV2 reads them:
|
|
835
|
+
*
|
|
836
|
+
* ```
|
|
837
|
+
* { logo: { receipt: "<file>", square: "<file>" }, feature: ["<file>"] }
|
|
838
|
+
* ```
|
|
839
|
+
*
|
|
840
|
+
* - **Writes** take the uploader's `fileName` (`<hash>.png`), not a URL.
|
|
841
|
+
* - **Reads** return full URLs (`http://static.merchantstack.com/images/…`),
|
|
842
|
+
* so a value read back must be turned into a file name before it's saved
|
|
843
|
+
* again ({@link toStoredImageIds}; `updateMerchant` does this for you).
|
|
844
|
+
* - Sending `imageIds` replaces the whole stored object (`{}` clears it);
|
|
845
|
+
* leaving it out keeps it.
|
|
846
|
+
* - The API builds each URL from the key, not from where the file is:
|
|
847
|
+
* `logo.*` → `…/merchant/<id>/logo/<file>`, `feature` → `…/feature/<file>`.
|
|
848
|
+
* So a featured image must be uploaded with kind `merchantFeature` (which
|
|
849
|
+
* stores it under `feature/`); a `merchant` upload lands under `logo/`.
|
|
850
|
+
*
|
|
851
|
+
* Confirmed on the live API, merchant 5644, 30 Sep 2026.
|
|
852
|
+
*/
|
|
853
|
+
interface MerchantImageIds {
|
|
854
|
+
logo?: {
|
|
855
|
+
receipt?: string | null;
|
|
856
|
+
square?: string | null;
|
|
857
|
+
[key: string]: string | null | undefined;
|
|
858
|
+
} | null;
|
|
859
|
+
feature?: string[] | null;
|
|
860
|
+
[key: string]: unknown;
|
|
861
|
+
}
|
|
862
|
+
/** The three image slots on the merchant settings page. */
|
|
863
|
+
declare const MERCHANT_IMAGE_SLOT_KEYS: readonly ["receipt", "square", "featured"];
|
|
864
|
+
type MerchantImageSlot = (typeof MERCHANT_IMAGE_SLOT_KEYS)[number];
|
|
865
|
+
interface MerchantImageSlotDefinition {
|
|
866
|
+
/** Where it's stored: `logo.receipt`, `logo.square` or `feature[0]`. */
|
|
867
|
+
field: "logo.receipt" | "logo.square" | "feature[0]";
|
|
868
|
+
/** Uploader `contentType` (the featured image has its own). */
|
|
869
|
+
uploadKind: UploadKind;
|
|
870
|
+
/** Exact size in pixels. adminV1 refuses any other size; the API doesn't resize. */
|
|
871
|
+
width: number;
|
|
872
|
+
height: number;
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Where each slot lives, its upload kind and its exact size (from adminV1's
|
|
876
|
+
* merchant settings page).
|
|
877
|
+
*/
|
|
878
|
+
declare const MERCHANT_IMAGE_SLOTS: Readonly<Record<MerchantImageSlot, MerchantImageSlotDefinition>>;
|
|
879
|
+
/**
|
|
880
|
+
* The file name in an image value: `"http://…/logo/abc.png"` → `"abc.png"`,
|
|
881
|
+
* `"abc.png"` → `"abc.png"`. Query strings and fragments are ignored.
|
|
882
|
+
* Returns `null` for empty or non-string values.
|
|
883
|
+
*/
|
|
884
|
+
declare function imageFileName(value: unknown): string | null;
|
|
885
|
+
/** The stored value for a slot (a URL when read from the API; a file name right after an upload). */
|
|
886
|
+
declare function getMerchantImage(imageIds: MerchantImageIds | null | undefined, slot: MerchantImageSlot): string | null;
|
|
887
|
+
/**
|
|
888
|
+
* An HTTPS URL for a slot's stored image, or `""` when the slot is empty or
|
|
889
|
+
* holds only a file name (a fresh upload not yet saved; use the upload's `url`).
|
|
890
|
+
*/
|
|
891
|
+
declare function getMerchantImageUrl(imageIds: MerchantImageIds | null | undefined, slot: MerchantImageSlot, base?: string): string;
|
|
892
|
+
/**
|
|
893
|
+
* Returns a copy of `imageIds` with one slot set to `fileName` (or cleared
|
|
894
|
+
* with `null`). Other slots and unknown keys are kept; an empty `logo` or
|
|
895
|
+
* `feature` is removed.
|
|
896
|
+
*/
|
|
897
|
+
declare function setMerchantImage(imageIds: MerchantImageIds | null | undefined, slot: MerchantImageSlot, fileName: string | null): MerchantImageIds;
|
|
898
|
+
/**
|
|
899
|
+
* The `imageIds` to send on a save: every URL is turned back into its file
|
|
900
|
+
* name (what adminV1 does with images you didn't change) and empty values
|
|
901
|
+
* are dropped. No images gives `{}`.
|
|
902
|
+
*/
|
|
903
|
+
declare function toStoredImageIds(imageIds: unknown): MerchantImageIds;
|
|
904
|
+
interface UploadMerchantImageOptions extends RequestOptions {
|
|
905
|
+
/**
|
|
906
|
+
* Scale and centre-crop the image to the slot's exact size before upload
|
|
907
|
+
* (browser only). Default true. With `false` the file is sent as-is.
|
|
908
|
+
*/
|
|
909
|
+
fit?: boolean;
|
|
910
|
+
/** Output type and quality when fitting. Default PNG. */
|
|
911
|
+
fitOptions?: FitImageOptions;
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Uploads an image for a merchant slot: fits it to the slot's size, uploads
|
|
915
|
+
* it with the slot's kind, and returns `{ id, url, path }`. Put `id` into
|
|
916
|
+
* `imageIds` with {@link setMerchantImage} and save with `updateMerchant`.
|
|
917
|
+
*/
|
|
918
|
+
declare function uploadMerchantImage(file: Blob, slot: MerchantImageSlot, options?: UploadMerchantImageOptions): Promise<UploadedImage>;
|
|
919
|
+
//#endregion
|
|
920
|
+
//#region src/core/merchant.d.ts
|
|
921
|
+
/** A colour set: hex strings (`"#1a2b3c"`). */
|
|
922
|
+
interface MerchantColourSet {
|
|
923
|
+
main?: string | null;
|
|
924
|
+
highlight?: string | null;
|
|
925
|
+
contrast?: string | null;
|
|
926
|
+
[key: string]: string | null | undefined;
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* Brand colours, as adminV1 saves them and adminV2 reads them
|
|
930
|
+
* (`colours.primary.main` is the logo background): `{ primary: { main,
|
|
931
|
+
* highlight, contrast } }`. `{}` when unset. Confirmed live, 30 Sep 2026.
|
|
932
|
+
*/
|
|
933
|
+
interface MerchantColours {
|
|
934
|
+
primary?: MerchantColourSet | null;
|
|
935
|
+
[key: string]: MerchantColourSet | null | undefined;
|
|
936
|
+
}
|
|
937
|
+
/** Colour keys inside `colours.primary`, in display order. */
|
|
938
|
+
declare const MERCHANT_COLOUR_KEYS: readonly ["main", "highlight", "contrast"];
|
|
939
|
+
type MerchantColourKey = (typeof MERCHANT_COLOUR_KEYS)[number];
|
|
940
|
+
/**
|
|
941
|
+
* Web and social links, as adminV1 saves them:
|
|
942
|
+
* `{ website: { home }, social: { twitter, facebook, instagram } }`.
|
|
943
|
+
* `{}` when empty (confirmed live, 30 Sep 2026). Use {@link normalizeMerchantLinks} to read older shapes and
|
|
944
|
+
* {@link merchantLinksToForm} / {@link merchantLinksFromForm} for flat forms.
|
|
945
|
+
*/
|
|
946
|
+
interface MerchantLinks {
|
|
947
|
+
website?: {
|
|
948
|
+
home?: string | null;
|
|
949
|
+
[key: string]: string | null | undefined;
|
|
950
|
+
} | null;
|
|
951
|
+
social?: {
|
|
952
|
+
twitter?: string | null;
|
|
953
|
+
facebook?: string | null;
|
|
954
|
+
instagram?: string | null;
|
|
955
|
+
[key: string]: string | null | undefined;
|
|
956
|
+
} | null;
|
|
957
|
+
[key: string]: unknown;
|
|
958
|
+
}
|
|
959
|
+
/** Flat link fields a form edits, in display order. */
|
|
960
|
+
declare const MERCHANT_LINK_KEYS: readonly ["website", "twitter", "facebook", "instagram"];
|
|
961
|
+
type MerchantLinkKey = (typeof MERCHANT_LINK_KEYS)[number];
|
|
962
|
+
/** Where each flat link field lives in `merchant.links`. */
|
|
963
|
+
declare const MERCHANT_LINK_PATHS: Readonly<Record<MerchantLinkKey, readonly [string, string]>>;
|
|
964
|
+
/**
|
|
965
|
+
* Reads `merchant.links` as flat fields (`{ website, twitter, facebook,
|
|
966
|
+
* instagram }`), whatever shape it was saved in:
|
|
967
|
+
* - the nested shape adminV1 saves (`{ website: { home }, social: { … } }`);
|
|
968
|
+
* - flat keys (`{ website: "…" }`, what SDK 0.4.0 pre-releases sent);
|
|
969
|
+
* - an array in {@link MERCHANT_LINK_KEYS} order (older AdminUI versions).
|
|
970
|
+
* Empty values are dropped.
|
|
971
|
+
*/
|
|
972
|
+
declare function merchantLinksToForm(links: unknown): Partial<Record<MerchantLinkKey, string>>;
|
|
973
|
+
/**
|
|
974
|
+
* Builds the stored `links` object from flat fields. Empty values are left
|
|
975
|
+
* out, and so is a group with nothing in it; no links gives `{}`.
|
|
976
|
+
*/
|
|
977
|
+
declare function merchantLinksFromForm(form: Partial<Record<MerchantLinkKey, string | null | undefined>>): MerchantLinks;
|
|
978
|
+
/** Reads any stored or older `links` value into the nested shape adminV1 saves. */
|
|
979
|
+
declare function normalizeMerchantLinks(links: unknown): MerchantLinks;
|
|
980
|
+
interface Merchant {
|
|
981
|
+
id: number;
|
|
982
|
+
name: string;
|
|
983
|
+
description?: string | null;
|
|
984
|
+
/** Company registration number. */
|
|
985
|
+
companyNumber?: string | null;
|
|
986
|
+
vatNumber?: string | null;
|
|
987
|
+
categoryId?: number | null;
|
|
988
|
+
estAnnualRevenue?: number | string | null;
|
|
989
|
+
note?: string | null;
|
|
990
|
+
links?: MerchantLinks | null;
|
|
991
|
+
colours?: MerchantColours | null;
|
|
992
|
+
imageIds?: MerchantImageIds | null;
|
|
993
|
+
createdDate?: string;
|
|
994
|
+
modifiedDate?: string;
|
|
995
|
+
/** Other fields the API returns are kept as-is. */
|
|
996
|
+
[field: string]: unknown;
|
|
997
|
+
}
|
|
998
|
+
interface MerchantResponse {
|
|
999
|
+
merchant: Merchant;
|
|
1000
|
+
api: apiResponse;
|
|
1001
|
+
}
|
|
1002
|
+
/** What `updateMerchant` accepts: any subset of the merchant's fields. */
|
|
1003
|
+
type MerchantPatch = Partial<Merchant> & {
|
|
1004
|
+
[field: string]: unknown;
|
|
1005
|
+
};
|
|
1006
|
+
/**
|
|
1007
|
+
* Object fields of the merchant. The API keeps one you leave out, and
|
|
1008
|
+
* replaces it as a whole when you send it.
|
|
1009
|
+
*/
|
|
1010
|
+
declare const MERCHANT_OBJECT_FIELDS: ReadonlyArray<string>;
|
|
1011
|
+
/** Fields the API sets itself; `updateMerchant` never sends them back. */
|
|
1012
|
+
declare const MERCHANT_READ_ONLY_FIELDS: ReadonlyArray<string>;
|
|
1013
|
+
/**
|
|
1014
|
+
* Longest values the API stores. Longer values are cut off silently
|
|
1015
|
+
* (live API, Sep 2026), so `updateMerchant` rejects them instead.
|
|
1016
|
+
* `vatNumber` fits a UK VAT number without the `GB` prefix.
|
|
1017
|
+
*/
|
|
1018
|
+
declare const MERCHANT_FIELD_MAX_LENGTH: Readonly<Record<string, number>>;
|
|
1019
|
+
/** Returns `"<field> is longer than <n> characters"` messages for over-long fields. */
|
|
1020
|
+
declare function validateMerchant(merchant: Record<string, unknown>): string[];
|
|
1021
|
+
/**
|
|
1022
|
+
* Reads the logged-in user's merchant (a user has exactly one), or `null`
|
|
1023
|
+
* when the API returns none.
|
|
1024
|
+
*/
|
|
1025
|
+
declare function fetchMerchant(options?: RequestOptions): Promise<Merchant | null>;
|
|
1026
|
+
interface UpdateMerchantOptions extends RequestOptions {
|
|
1027
|
+
/**
|
|
1028
|
+
* Read the merchant first and send the patch merged over it (the default).
|
|
1029
|
+
* Scalar fields are merged over the stored ones. Object fields
|
|
1030
|
+
* (`colours`, `links`, `imageIds`) are sent only when the patch has them,
|
|
1031
|
+
* and then replace the stored one as a whole.
|
|
1032
|
+
*
|
|
1033
|
+
* Set to `false` to send only the patch (it must then include `id`). Only
|
|
1034
|
+
* do that if you know the API keeps the fields you leave out.
|
|
1035
|
+
*/
|
|
1036
|
+
merge?: boolean;
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* Updates the merchant and returns it as stored.
|
|
1040
|
+
*
|
|
1041
|
+
* Shopwave's `POST /merchant` replaces the whole record: a scalar field you
|
|
1042
|
+
* leave out (`companyNumber`, `vatNumber`, `note`, …) is set to null, while
|
|
1043
|
+
* an object field you leave out (`colours`, `links`, `imageIds`) is kept and
|
|
1044
|
+
* one you send replaces the stored one; sending `{}` clears it (live API,
|
|
1045
|
+
* Sep 2026). So by default the
|
|
1046
|
+
* current merchant is read first and the patch is merged over it.
|
|
1047
|
+
*
|
|
1048
|
+
* `imageIds` is always sent as file names ({@link toStoredImageIds}), so you
|
|
1049
|
+
* can pass back what you read. See `MerchantImageIds` for the shape.
|
|
1050
|
+
*
|
|
1051
|
+
* The API answers 205 with an empty body, so the merchant is read again after
|
|
1052
|
+
* the save and that is what's returned (it shows exactly what was kept).
|
|
1053
|
+
* Throws before sending when a value is longer than the API stores
|
|
1054
|
+
* ({@link MERCHANT_FIELD_MAX_LENGTH}).
|
|
1055
|
+
*/
|
|
1056
|
+
declare function updateMerchant(patch: MerchantPatch, options?: UpdateMerchantOptions): Promise<Merchant>;
|
|
1057
|
+
//#endregion
|
|
1058
|
+
//#region src/core/user.d.ts
|
|
1059
|
+
/** The logged-in user's employee record (`GET /user` → `user.employee`). */
|
|
1060
|
+
interface UserEmployee {
|
|
1061
|
+
merchantId?: number;
|
|
1062
|
+
/** See `rolesById`. */
|
|
1063
|
+
roleId?: number;
|
|
1064
|
+
/** Store roles, keyed by store id. */
|
|
1065
|
+
stores?: Record<string, unknown>;
|
|
1066
|
+
[field: string]: unknown;
|
|
1067
|
+
}
|
|
1068
|
+
/** The logged-in user (`GET /user`). */
|
|
1069
|
+
interface User {
|
|
1070
|
+
id: number;
|
|
1071
|
+
firstName: string;
|
|
1072
|
+
lastName: string;
|
|
1073
|
+
email: string;
|
|
1074
|
+
employee?: UserEmployee | null;
|
|
1075
|
+
/** Documented in the API reference but not returned by the live API (Sep 2026). */
|
|
1076
|
+
createdDate?: string;
|
|
1077
|
+
/** Not returned by the live API (Sep 2026); use `fetchMerchant()`. */
|
|
1078
|
+
merchant?: Merchant | null;
|
|
1079
|
+
/** Other fields the API returns are kept as-is. */
|
|
1080
|
+
[field: string]: unknown;
|
|
1081
|
+
}
|
|
1082
|
+
interface UserResponse {
|
|
1083
|
+
user: User;
|
|
1084
|
+
api: apiResponse;
|
|
1085
|
+
}
|
|
1086
|
+
/** Reads the logged-in user, or `null` when the API returns none. */
|
|
1087
|
+
declare function fetchUser(options?: RequestOptions): Promise<User | null>;
|
|
1088
|
+
//#endregion
|
|
508
1089
|
//#region src/hooks/shared.d.ts
|
|
509
1090
|
/**
|
|
510
1091
|
* Consistent return shape for every auto-fetching ("query") hook.
|
|
511
1092
|
*/
|
|
512
1093
|
interface QueryState<T> {
|
|
1094
|
+
/**
|
|
1095
|
+
* The latest data for the current params. Kept while a `refetch()` is in
|
|
1096
|
+
* flight (and after a failed refetch), so lists don't blank out and flash a
|
|
1097
|
+
* skeleton after every save. Reset to `null` when the params change.
|
|
1098
|
+
*/
|
|
513
1099
|
data: T | null;
|
|
1100
|
+
/** True while there's no data to show yet (first load, or params changed). */
|
|
514
1101
|
loading: boolean;
|
|
1102
|
+
/** True whenever a request is in flight, including background refetches. */
|
|
1103
|
+
fetching: boolean;
|
|
515
1104
|
error: string | null;
|
|
516
|
-
/**
|
|
1105
|
+
/** HTTP status of the last error (0 = no response), or `null`. */
|
|
1106
|
+
errorStatus: number | null;
|
|
1107
|
+
/** Re-runs the underlying fetch, keeping the current data visible meanwhile. */
|
|
517
1108
|
refetch: () => void;
|
|
518
1109
|
}
|
|
519
1110
|
/**
|
|
@@ -525,6 +1116,8 @@ interface MutationState<TData, TArgs extends any[]> {
|
|
|
525
1116
|
data: TData | null;
|
|
526
1117
|
loading: boolean;
|
|
527
1118
|
error: string | null;
|
|
1119
|
+
/** HTTP status of the last error (0 = no response), or `null`. */
|
|
1120
|
+
errorStatus: number | null;
|
|
528
1121
|
}
|
|
529
1122
|
//#endregion
|
|
530
1123
|
//#region src/hooks/useCategory.d.ts
|
|
@@ -633,6 +1226,59 @@ declare function useLogout(options?: RequestOptions): MutationState<void, []>;
|
|
|
633
1226
|
*/
|
|
634
1227
|
declare function useSession(options?: RequestOptions): QueryState<SessionStatus>;
|
|
635
1228
|
//#endregion
|
|
1229
|
+
//#region src/hooks/useMerchant.d.ts
|
|
1230
|
+
/**
|
|
1231
|
+
* The logged-in user's merchant, fetched on mount. `data` is `null` while
|
|
1232
|
+
* loading or when the API has no merchant.
|
|
1233
|
+
*
|
|
1234
|
+
* @example
|
|
1235
|
+
* const { data: merchant, loading, refetch } = useMerchant();
|
|
1236
|
+
*/
|
|
1237
|
+
declare function useMerchant(options?: RequestOptions): QueryState<Merchant | null>;
|
|
1238
|
+
/**
|
|
1239
|
+
* Manually-triggered merchant update (see `updateMerchant`: the patch is merged
|
|
1240
|
+
* over the current merchant by default). Resolves to the saved merchant, or
|
|
1241
|
+
* `null` on error (then `error` / `errorStatus` are set).
|
|
1242
|
+
*
|
|
1243
|
+
* @example
|
|
1244
|
+
* const { mutate: saveMerchant, loading: saving } = useUpdateMerchant();
|
|
1245
|
+
* const saved = await saveMerchant({ vatNumber: "GB123" });
|
|
1246
|
+
*/
|
|
1247
|
+
declare function useUpdateMerchant(options?: UpdateMerchantOptions): MutationState<Merchant, [MerchantPatch]>;
|
|
1248
|
+
//#endregion
|
|
1249
|
+
//#region src/hooks/useUser.d.ts
|
|
1250
|
+
/**
|
|
1251
|
+
* The logged-in user, fetched on mount. `errorStatus` is 401 when logged out.
|
|
1252
|
+
*
|
|
1253
|
+
* @example
|
|
1254
|
+
* const { data: user } = useUser();
|
|
1255
|
+
*/
|
|
1256
|
+
declare function useUser(options?: RequestOptions & {
|
|
1257
|
+
enabled?: boolean;
|
|
1258
|
+
}): QueryState<User | null>;
|
|
1259
|
+
//#endregion
|
|
1260
|
+
//#region src/hooks/useUploadImage.d.ts
|
|
1261
|
+
/**
|
|
1262
|
+
* Manually-triggered image upload. Resolves to `{ id, url }`, or `null` on
|
|
1263
|
+
* error (then `error` / `errorStatus` are set).
|
|
1264
|
+
*
|
|
1265
|
+
* @example
|
|
1266
|
+
* const { mutate: upload, loading: uploading } = useUploadImage();
|
|
1267
|
+
* const image = await upload(file, { kind: "merchant" });
|
|
1268
|
+
*/
|
|
1269
|
+
declare function useUploadImage(options?: RequestOptions): MutationState<UploadedImage, [Blob, UploadImageParams]>;
|
|
1270
|
+
/**
|
|
1271
|
+
* Manually-triggered upload for a merchant image slot (`receipt`, `square`,
|
|
1272
|
+
* `featured`): fits the image to the slot's exact size, uploads it with the
|
|
1273
|
+
* slot's kind, and resolves to `{ id, url }`. Put `id` into `imageIds` with
|
|
1274
|
+
* `setMerchantImage`.
|
|
1275
|
+
*
|
|
1276
|
+
* @example
|
|
1277
|
+
* const { mutate: upload } = useUploadMerchantImage();
|
|
1278
|
+
* const image = await upload(file, "square");
|
|
1279
|
+
*/
|
|
1280
|
+
declare function useUploadMerchantImage(options?: UploadMerchantImageOptions): MutationState<UploadedImage, [Blob, MerchantImageSlot]>;
|
|
1281
|
+
//#endregion
|
|
636
1282
|
//#region src/hooks/useBasketReport.d.ts
|
|
637
1283
|
interface UseBasketReportReturn {
|
|
638
1284
|
rows: BasketReportRow[];
|
|
@@ -653,5 +1299,5 @@ interface UseBasketReportReturn {
|
|
|
653
1299
|
*/
|
|
654
1300
|
declare function useBasketReport(options?: RequestOptions): UseBasketReportReturn;
|
|
655
1301
|
//#endregion
|
|
656
|
-
export { Basket, BasketFilters, BasketItem, BasketPagination, BasketReportRow, BasketSummary, Category, CategoryResponse, CombineBasketRowsParams, Consumer, ConsumerResponse, DEFAULT_BASKET_FILTERS, DEFAULT_PAGINATION, Employee, EmployeeResponse, ErrorMessage, Extras, FetchCategoriesParams, FetchEmployeesParams, FetchProductsParams, FetchPromotionsParams, FetchStoresParams, MessageInfo, MutationState, ParsedBasket, Product, ProductInstance, ProductResponse, Promotion, PromotionResponse, PromotionRuleSet, QueryState, ReportData, ReportKey, ReportQuery, ReportQueryMap, ReportResponse, RequestOptions, SessionStatus, Store, StoreResponse, SubmitEntityArgs, SubmitEntityResponse, SuccessMessage, UseBasketReportReturn, apiDelete, apiGet, apiResponse, apiSend, buildBasketReportQuery, buildConsumerMap, collectConsumerIds, collectProductIds, combineBasketRows, computeBasketSummary, deleteEntity, fetchCategories, fetchConsumers, fetchEmployees, fetchProducts, fetchProductsMap, fetchPromotions, fetchReport, fetchSession, fetchStores, formatDateForQuery, getApiErrors, loginPath, logout, logoutPath, parseBasketReportData, rolesById, submitEntity, useBasketReport, useCategory, useConsumer, useDelete, useEmployee, useLogout, useProduct, usePromotion, useReport, useSession, useStore, useSubmit };
|
|
1302
|
+
export { Basket, BasketFilters, BasketItem, BasketPagination, BasketReportRow, BasketSummary, Category, CategoryResponse, CombineBasketRowsParams, Consumer, ConsumerResponse, CropRect, DEFAULT_BASKET_FILTERS, DEFAULT_PAGINATION, DeletableEntityKind, Employee, EmployeeResponse, EntityDefinition, EntityId, EntityInput, EntityKind, EntityTypes, ErrorMessage, Extras, FetchByIdParams, FetchCategoriesParams, FetchEmployeesParams, FetchProductsParams, FetchPromotionsParams, FetchStoresParams, FitImageOptions, ImageSize, MERCHANT_COLOUR_KEYS, MERCHANT_FIELD_MAX_LENGTH, MERCHANT_IMAGE_SLOTS, MERCHANT_IMAGE_SLOT_KEYS, MERCHANT_LINK_KEYS, MERCHANT_LINK_PATHS, MERCHANT_OBJECT_FIELDS, MERCHANT_READ_ONLY_FIELDS, Merchant, MerchantColourKey, MerchantColourSet, MerchantColours, MerchantImageIds, MerchantImageSlot, MerchantImageSlotDefinition, MerchantLinkKey, MerchantLinks, MerchantPatch, MerchantResponse, MessageInfo, MutationState, ParsedBasket, Product, ProductInstance, ProductResponse, Promotion, PromotionResponse, PromotionRuleSet, QueryState, ReportData, ReportKey, ReportQuery, ReportQueryMap, ReportResponse, RequestOptions, ResourceDefinition, ResourceKind, SHOPWAVE_ENTITIES, SHOPWAVE_IMAGE_BASE_URL, SHOPWAVE_RESOURCES, SHOPWAVE_UPLOAD, Saved, SessionStatus, ShopwaveApiError, ShopwaveErrorMap, Store, StoreResponse, SubmitEntityArgs, SubmitEntityResponse, SuccessMessage, UPLOAD_KINDS, UpdateMerchantOptions, UploadImageParams, UploadKind, UploadMerchantImageOptions, UploadedImage, UseBasketReportReturn, User, UserEmployee, UserResponse, WritableEntityKind, apiDelete, apiGet, apiRequest, apiResponse, apiSend, assertNoApiErrors, authorizationFor, buildBasketReportQuery, buildConsumerMap, collectConsumerIds, collectProductIds, combineBasketRows, computeBasketSummary, coverCrop, deleteCategory, deleteEmployee, deleteEntity, deleteEntityById, deleteProduct, deletePromotion, deleteStore, fetchCategories, fetchCategory, fetchConsumer, fetchConsumers, fetchEmployee, fetchEmployees, fetchEntityById, fetchMerchant, fetchProduct, fetchProducts, fetchProductsMap, fetchPromotion, fetchPromotions, fetchReport, fetchSession, fetchStore, fetchStores, fetchUser, fitImageToSize, formatDateForQuery, getApiErrorMap, getApiErrors, getImageUrl, getMerchantImage, getMerchantImageUrl, imageFileName, isUploadKind, loginPath, logout, logoutPath, merchantLinksFromForm, merchantLinksToForm, normalizeMerchantLinks, parseBasketReportData, readImageSize, rolesById, saveCategory, saveEmployee, saveEntities, saveEntity, saveProduct, savePromotion, saveStore, setMerchantImage, submitEntity, toShopwaveApiError, toStoredImageIds, updateMerchant, uploadImage, uploadMerchantImage, useBasketReport, useCategory, useConsumer, useDelete, useEmployee, useLogout, useMerchant, useProduct, usePromotion, useReport, useSession, useStore, useSubmit, useUpdateMerchant, useUploadImage, useUploadMerchantImage, useUser, validateMerchant, withToken };
|
|
657
1303
|
//# sourceMappingURL=index.d.cts.map
|