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.
@@ -84,6 +84,218 @@ interface ShopwaveOAuthClient {
84
84
  refreshToken(token: ShopwaveToken | string): Promise<ShopwaveToken>;
85
85
  }
86
86
  //#endregion
87
+ //#region src/core/entities.d.ts
88
+ /**
89
+ * How each Shopwave entity is addressed, shared by the client functions below
90
+ * and the server route handlers in `react-shopwave-connect/next`, so the two
91
+ * halves of the contract can't drift apart.
92
+ */
93
+ interface EntityDefinition {
94
+ /** App route segment: `/api/<route>` and `/api/<route>/<id>`. */
95
+ route: string;
96
+ /** Key of the entity map in requests and responses (`{ products: { … } }`). */
97
+ collection: string;
98
+ /** Shopwave API path (`GET/POST/DELETE {apiUrl}/<upstream>`). */
99
+ upstream: string;
100
+ /** Header that filters reads by id (`productIds: 1,2`). */
101
+ idsHeader: string;
102
+ /** Header that names the record to delete (`productId: 1`). */
103
+ idHeader: string;
104
+ /** False for read-only entities (the API has no POST for them). */
105
+ writable: boolean;
106
+ /**
107
+ * How a delete is done upstream:
108
+ * - `"delete"`: `DELETE {apiUrl}/<upstream>` with the `idHeader` (205, empty body)
109
+ * - `"retire"`: the API has no DELETE; the record is retired by POSTing
110
+ * `{ id, [retireField]: <now> }` (employees: `exitDate`, promotions: `endDate`)
111
+ * - `"none"`: can't be deleted
112
+ */
113
+ deleteMode: "delete" | "retire" | "none";
114
+ /** Date field set to "now" when `deleteMode` is `"retire"`. */
115
+ retireField?: string;
116
+ }
117
+ type EntityKind = "product" | "category" | "store" | "promotion" | "employee" | "consumer";
118
+ /**
119
+ * Addressing per entity, following the Shopwave API reference
120
+ * (https://developer.merchantstack.com/api-reference.html) and checked against
121
+ * the live API where noted:
122
+ * - category/product/store: GET filtered by `<x>Ids`, POST upsert, DELETE with `<x>Id` → 205.
123
+ * - promotion: no DELETE; "deleting" a promotion ends it by setting `endDate` to now.
124
+ * - employee: POST updates `roleId`, `joinedDate`, `exitDate` (names can't be
125
+ * changed); no DELETE (live API: "Cannot DELETE /employee") → retired via `exitDate`.
126
+ * - consumer: read-only (`GET` with `ids`, comma-separated); live POST → 404.
127
+ */
128
+ declare const SHOPWAVE_ENTITIES: Readonly<Record<EntityKind, EntityDefinition>>;
129
+ //#endregion
130
+ //#region src/core/resources.d.ts
131
+ /**
132
+ * Singleton resources (one per logged-in user) and the uploader.
133
+ *
134
+ * Like `SHOPWAVE_ENTITIES`, this table is shared by the client functions
135
+ * (`fetchMerchant`, `fetchUser`, `uploadImage`, …) and the server route
136
+ * handlers in `react-shopwave-connect/next`, so the two halves of the contract
137
+ * can't drift apart. These aren't in `SHOPWAVE_ENTITIES` because they aren't
138
+ * keyed collections: `GET /merchant` answers `{ merchant: {…} }`, not a map by id.
139
+ */
140
+ interface ResourceDefinition {
141
+ /** App route: `/api/<route>`. */
142
+ route: string;
143
+ /** Key of the object in requests and responses (`{ merchant: {…} }`). */
144
+ key: string;
145
+ /** Shopwave API path. */
146
+ upstream: string;
147
+ /** False for read-only resources. */
148
+ writable: boolean;
149
+ }
150
+ type ResourceKind = "merchant" | "user";
151
+ /**
152
+ * - merchant: `GET /merchant`; `POST /merchant` with `{ merchant: { id, … } }`
153
+ * updates it (without an `id` the API would create a second merchant, so the
154
+ * route requires one).
155
+ * - user: `GET /user` (read-only here; the API's `POST /user` invites users and
156
+ * needs the admin scope).
157
+ */
158
+ declare const SHOPWAVE_RESOURCES: Readonly<Record<ResourceKind, ResourceDefinition>>;
159
+ /**
160
+ * What an upload is for. Sent to Shopwave as the `contentType` header of
161
+ * `PUT /uploader`. `merchant` is for the receipt and square logos (stored
162
+ * under `…/merchant/<id>/logo/`), `merchantFeature` for the featured image.
163
+ * The API spells two of its values `applicaionLogo` / `applicaionImages`; use
164
+ * the correctly spelt names here and the server maps them.
165
+ */
166
+ type UploadKind = "merchant" | "merchantFeature" | "product" | "user" | "applicationLogo" | "applicationImages";
167
+ /** `UploadKind` → the API's `contentType` value. */
168
+ declare const UPLOAD_KINDS: Readonly<Record<UploadKind, string>>;
169
+ //#endregion
170
+ //#region src/server/api.d.ts
171
+ /**
172
+ * The OAuth token a caller sent with the request, without its scheme, or `null`.
173
+ *
174
+ * Reads, in order: `Authorization: OAuth|Bearer <token>` (what the SDK sends
175
+ * from 0.3), the `token` header (SDK ≤ 0.2 writes), and `extras.token`
176
+ * (SDK ≤ 0.2 reads).
177
+ */
178
+ declare function readRequestToken(request: Request): string | null;
179
+ interface ShopwaveApiConfig {
180
+ /** Shopwave API base URL, e.g. `process.env.SHOPWAVE_API_SERVER_URL`. */
181
+ apiUrl: string;
182
+ /**
183
+ * `Authorization` header value for the logged-in user (`"OAuth <token>"`),
184
+ * or `null` when logged out. Called again with `forceRefresh: true` when the
185
+ * API reports the token expired.
186
+ */
187
+ getAuthorization?: (options?: {
188
+ forceRefresh?: boolean;
189
+ }) => Promise<string | null>;
190
+ /**
191
+ * Accept a token sent by the caller (`Authorization: OAuth <token>`, or the
192
+ * legacy `token` header / `extras.token`) instead of the session. The token
193
+ * is only forwarded to the Shopwave API, which validates it. Defaults to `true`
194
+ * (SDK integration tests, scripts and server-to-server calls rely on it).
195
+ */
196
+ allowRequestToken?: boolean;
197
+ /** `x-accept-version` sent upstream. Defaults to `"2.0"`. */
198
+ apiVersion?: string;
199
+ /** Custom fetch for the upstream call. */
200
+ fetch?: typeof fetch;
201
+ /** Override how an entity is addressed (e.g. a different id header). */
202
+ entities?: Partial<Record<EntityKind, Partial<EntityDefinition>>>;
203
+ /** Extra `extras` keys to drop, on top of {@link BLOCKED_EXTRAS}. */
204
+ blockedExtras?: string[];
205
+ /**
206
+ * Called when the upstream call throws (network error). Defaults to a
207
+ * `console.error` with the method and path only — never headers or tokens.
208
+ */
209
+ onError?: (error: unknown, context: {
210
+ method: string;
211
+ path: string;
212
+ }) => void;
213
+ }
214
+ interface ForwardInit {
215
+ /** HTTP method sent upstream. Defaults to `GET`. */
216
+ method?: string;
217
+ /** Shopwave API path, e.g. `"product"`. */
218
+ path: string;
219
+ /** Extra upstream headers; they win over `extras`. */
220
+ headers?: Record<string, string>;
221
+ /** Sent as the form field `postBody=<JSON>`. */
222
+ postBody?: unknown;
223
+ /** Sent as a multipart body (e.g. uploads). Ignored when `postBody` is set. */
224
+ formData?: FormData;
225
+ /** Forward the caller's `extras` header as upstream headers. Defaults to `true`. */
226
+ forwardExtras?: boolean;
227
+ }
228
+ /** Second argument Next.js (and similar) pass to dynamic route handlers. */
229
+ interface RouteContext {
230
+ params?: Promise<Record<string, string | string[] | undefined>> | Record<string, string | string[] | undefined>;
231
+ }
232
+ type RouteHandler = (request: Request, context?: RouteContext) => Promise<Response>;
233
+ interface CollectionHandlers {
234
+ /** List/filter via `extras` (e.g. `productIds`, `deleted`). */
235
+ GET: RouteHandler;
236
+ /** Create or update (Shopwave upserts: an `id` means update). */
237
+ POST: RouteHandler;
238
+ /** Same as POST (kept for older clients). */
239
+ PUT: RouteHandler;
240
+ }
241
+ interface ItemHandlers {
242
+ /** Read one record by the `[id]` route param. */
243
+ GET: RouteHandler;
244
+ /** Update the record at `[id]` (the id in the URL wins). */
245
+ PUT: RouteHandler;
246
+ /** Delete the record at `[id]`. Shopwave answers 205 with an empty body. */
247
+ DELETE: RouteHandler;
248
+ }
249
+ interface ResourceHandlers {
250
+ /** Read the resource (`GET {apiUrl}/<upstream>`). */
251
+ GET: RouteHandler;
252
+ /**
253
+ * Update it: body `{ <key>: { id, … } }` or the bare object. The `id` is
254
+ * required (without one Shopwave would create a new record). 405 for
255
+ * read-only resources.
256
+ */
257
+ PUT: RouteHandler;
258
+ /** Same as PUT. */
259
+ POST: RouteHandler;
260
+ }
261
+ interface UploadOptions {
262
+ /** Largest accepted file, in bytes. Defaults to 10 MB. */
263
+ maxBytes?: number;
264
+ /**
265
+ * Accepted MIME types. An entry ending in `/` is a prefix (`"image/"`).
266
+ * Defaults to `["image/"]`.
267
+ */
268
+ accept?: string[];
269
+ }
270
+ interface UploadHandlers {
271
+ /**
272
+ * Multipart `file` + `kind` (see `UploadKind`) → `PUT {apiUrl}/uploader`.
273
+ * Answers with Shopwave's reply (201 `{ fileName, path, api }`); 400 for a
274
+ * missing file or unknown kind, 413 when too large, 415 for other types.
275
+ */
276
+ POST: RouteHandler;
277
+ }
278
+ interface ShopwaveApiHandlers {
279
+ /** Calls the Shopwave API for this request (token, extras, refresh-retry) and returns its answer. */
280
+ forward(request: Request, init: ForwardInit): Promise<Response>;
281
+ /** Handlers for `app/api/<route>/route.ts`. */
282
+ collection(kind: EntityKind): CollectionHandlers;
283
+ /** Handlers for `app/api/<route>/[id]/route.ts`. */
284
+ item(kind: EntityKind, options?: {
285
+ param?: string;
286
+ }): ItemHandlers;
287
+ /** GET-only proxy for a Shopwave path (e.g. `"report"`). */
288
+ passthrough(path: string): {
289
+ GET: RouteHandler;
290
+ };
291
+ /** Handlers for a singleton resource: `app/api/merchant/route.ts`, `app/api/user/route.ts`. */
292
+ resource(kind: ResourceKind): ResourceHandlers;
293
+ /** Handler for `app/api/upload/route.ts`. */
294
+ upload(options?: UploadOptions): UploadHandlers;
295
+ /** The entity table in use (defaults merged with `config.entities`). */
296
+ entity(kind: EntityKind): EntityDefinition;
297
+ }
298
+ //#endregion
87
299
  //#region src/core/session.d.ts
88
300
  /**
89
301
  * Login status returned by the app's `/api/session` route. It deliberately
@@ -192,6 +404,13 @@ interface ProtectOptions {
192
404
  publicPaths?: string[];
193
405
  /** Paths that get a 401 JSON response instead of a login redirect. Defaults to `["/api"]`. */
194
406
  apiPaths?: string[];
407
+ /**
408
+ * Let API requests that carry their own token (`Authorization: OAuth <token>`,
409
+ * or the legacy `token` header / `extras.token`) through without a session
410
+ * cookie. The route handlers from {@link createShopwaveApi} forward that token
411
+ * to the Shopwave API, which validates it. Defaults to `false`.
412
+ */
413
+ allowRequestToken?: boolean;
195
414
  }
196
415
  interface ShopwaveAuth {
197
416
  /** The underlying framework-agnostic OAuth client. */
@@ -250,6 +469,35 @@ interface ShopwaveAuth {
250
469
  * a missing env var doesn't fail `next build`.
251
470
  */
252
471
  declare function createShopwaveAuth(config: ShopwaveAuthConfig): ShopwaveAuth;
472
+ interface ShopwaveNextApiConfig extends Omit<ShopwaveApiConfig, "getAuthorization"> {
473
+ /** The app's auth instance: supplies (and refreshes) the logged-in user's token. */
474
+ auth: Pick<ShopwaveAuth, "getAuthorizationHeader">;
475
+ }
476
+ /**
477
+ * Ready-made Next.js route handlers for the SDK's `/api/*` contract, using the
478
+ * session from `createShopwaveAuth`. Create once (e.g. `lib/shopwave.ts`):
479
+ *
480
+ * ```ts
481
+ * export const shopwave = createShopwaveApi({ auth, apiUrl: process.env.SHOPWAVE_API_SERVER_URL! });
482
+ * ```
483
+ *
484
+ * then each route file is one line:
485
+ *
486
+ * ```ts
487
+ * // app/api/products/route.ts
488
+ * export const { GET, POST, PUT } = shopwave.collection("product");
489
+ * // app/api/products/[id]/route.ts
490
+ * export const { GET, PUT, DELETE } = shopwave.item("product");
491
+ * // app/api/report/route.ts
492
+ * export const { GET } = shopwave.passthrough("report");
493
+ * // app/api/merchant/route.ts, app/api/user/route.ts
494
+ * export const { GET, PUT } = shopwave.resource("merchant");
495
+ * export const { GET } = shopwave.resource("user");
496
+ * // app/api/upload/route.ts
497
+ * export const { POST } = shopwave.upload();
498
+ * ```
499
+ */
500
+ declare function createShopwaveApi(config: ShopwaveNextApiConfig): ShopwaveApiHandlers;
253
501
  //#endregion
254
- export { AuthContext, ProtectOptions, type SessionStatus, ShopwaveAuth, ShopwaveAuthConfig, ShopwaveAuthError, ShopwaveSessionConfig, ShopwaveSessionData, type ShopwaveToken, authorizationHeader, createShopwaveAuth, isExpiredTokenResponse };
502
+ export { AuthContext, type CollectionHandlers, type EntityDefinition, type EntityKind, type ForwardInit, type ItemHandlers, ProtectOptions, type ResourceHandlers, type ResourceKind, type RouteContext, type RouteHandler, SHOPWAVE_ENTITIES, SHOPWAVE_RESOURCES, type SessionStatus, type ShopwaveApiHandlers, ShopwaveAuth, ShopwaveAuthConfig, ShopwaveAuthError, ShopwaveNextApiConfig, ShopwaveSessionConfig, ShopwaveSessionData, type ShopwaveToken, UPLOAD_KINDS, type UploadHandlers, type UploadKind, type UploadOptions, authorizationHeader, createShopwaveApi, createShopwaveAuth, isExpiredTokenResponse, readRequestToken };
255
503
  //# sourceMappingURL=index.d.cts.map
@@ -84,6 +84,218 @@ interface ShopwaveOAuthClient {
84
84
  refreshToken(token: ShopwaveToken | string): Promise<ShopwaveToken>;
85
85
  }
86
86
  //#endregion
87
+ //#region src/core/entities.d.ts
88
+ /**
89
+ * How each Shopwave entity is addressed, shared by the client functions below
90
+ * and the server route handlers in `react-shopwave-connect/next`, so the two
91
+ * halves of the contract can't drift apart.
92
+ */
93
+ interface EntityDefinition {
94
+ /** App route segment: `/api/<route>` and `/api/<route>/<id>`. */
95
+ route: string;
96
+ /** Key of the entity map in requests and responses (`{ products: { … } }`). */
97
+ collection: string;
98
+ /** Shopwave API path (`GET/POST/DELETE {apiUrl}/<upstream>`). */
99
+ upstream: string;
100
+ /** Header that filters reads by id (`productIds: 1,2`). */
101
+ idsHeader: string;
102
+ /** Header that names the record to delete (`productId: 1`). */
103
+ idHeader: string;
104
+ /** False for read-only entities (the API has no POST for them). */
105
+ writable: boolean;
106
+ /**
107
+ * How a delete is done upstream:
108
+ * - `"delete"`: `DELETE {apiUrl}/<upstream>` with the `idHeader` (205, empty body)
109
+ * - `"retire"`: the API has no DELETE; the record is retired by POSTing
110
+ * `{ id, [retireField]: <now> }` (employees: `exitDate`, promotions: `endDate`)
111
+ * - `"none"`: can't be deleted
112
+ */
113
+ deleteMode: "delete" | "retire" | "none";
114
+ /** Date field set to "now" when `deleteMode` is `"retire"`. */
115
+ retireField?: string;
116
+ }
117
+ type EntityKind = "product" | "category" | "store" | "promotion" | "employee" | "consumer";
118
+ /**
119
+ * Addressing per entity, following the Shopwave API reference
120
+ * (https://developer.merchantstack.com/api-reference.html) and checked against
121
+ * the live API where noted:
122
+ * - category/product/store: GET filtered by `<x>Ids`, POST upsert, DELETE with `<x>Id` → 205.
123
+ * - promotion: no DELETE; "deleting" a promotion ends it by setting `endDate` to now.
124
+ * - employee: POST updates `roleId`, `joinedDate`, `exitDate` (names can't be
125
+ * changed); no DELETE (live API: "Cannot DELETE /employee") → retired via `exitDate`.
126
+ * - consumer: read-only (`GET` with `ids`, comma-separated); live POST → 404.
127
+ */
128
+ declare const SHOPWAVE_ENTITIES: Readonly<Record<EntityKind, EntityDefinition>>;
129
+ //#endregion
130
+ //#region src/core/resources.d.ts
131
+ /**
132
+ * Singleton resources (one per logged-in user) and the uploader.
133
+ *
134
+ * Like `SHOPWAVE_ENTITIES`, this table is shared by the client functions
135
+ * (`fetchMerchant`, `fetchUser`, `uploadImage`, …) and the server route
136
+ * handlers in `react-shopwave-connect/next`, so the two halves of the contract
137
+ * can't drift apart. These aren't in `SHOPWAVE_ENTITIES` because they aren't
138
+ * keyed collections: `GET /merchant` answers `{ merchant: {…} }`, not a map by id.
139
+ */
140
+ interface ResourceDefinition {
141
+ /** App route: `/api/<route>`. */
142
+ route: string;
143
+ /** Key of the object in requests and responses (`{ merchant: {…} }`). */
144
+ key: string;
145
+ /** Shopwave API path. */
146
+ upstream: string;
147
+ /** False for read-only resources. */
148
+ writable: boolean;
149
+ }
150
+ type ResourceKind = "merchant" | "user";
151
+ /**
152
+ * - merchant: `GET /merchant`; `POST /merchant` with `{ merchant: { id, … } }`
153
+ * updates it (without an `id` the API would create a second merchant, so the
154
+ * route requires one).
155
+ * - user: `GET /user` (read-only here; the API's `POST /user` invites users and
156
+ * needs the admin scope).
157
+ */
158
+ declare const SHOPWAVE_RESOURCES: Readonly<Record<ResourceKind, ResourceDefinition>>;
159
+ /**
160
+ * What an upload is for. Sent to Shopwave as the `contentType` header of
161
+ * `PUT /uploader`. `merchant` is for the receipt and square logos (stored
162
+ * under `…/merchant/<id>/logo/`), `merchantFeature` for the featured image.
163
+ * The API spells two of its values `applicaionLogo` / `applicaionImages`; use
164
+ * the correctly spelt names here and the server maps them.
165
+ */
166
+ type UploadKind = "merchant" | "merchantFeature" | "product" | "user" | "applicationLogo" | "applicationImages";
167
+ /** `UploadKind` → the API's `contentType` value. */
168
+ declare const UPLOAD_KINDS: Readonly<Record<UploadKind, string>>;
169
+ //#endregion
170
+ //#region src/server/api.d.ts
171
+ /**
172
+ * The OAuth token a caller sent with the request, without its scheme, or `null`.
173
+ *
174
+ * Reads, in order: `Authorization: OAuth|Bearer <token>` (what the SDK sends
175
+ * from 0.3), the `token` header (SDK ≤ 0.2 writes), and `extras.token`
176
+ * (SDK ≤ 0.2 reads).
177
+ */
178
+ declare function readRequestToken(request: Request): string | null;
179
+ interface ShopwaveApiConfig {
180
+ /** Shopwave API base URL, e.g. `process.env.SHOPWAVE_API_SERVER_URL`. */
181
+ apiUrl: string;
182
+ /**
183
+ * `Authorization` header value for the logged-in user (`"OAuth <token>"`),
184
+ * or `null` when logged out. Called again with `forceRefresh: true` when the
185
+ * API reports the token expired.
186
+ */
187
+ getAuthorization?: (options?: {
188
+ forceRefresh?: boolean;
189
+ }) => Promise<string | null>;
190
+ /**
191
+ * Accept a token sent by the caller (`Authorization: OAuth <token>`, or the
192
+ * legacy `token` header / `extras.token`) instead of the session. The token
193
+ * is only forwarded to the Shopwave API, which validates it. Defaults to `true`
194
+ * (SDK integration tests, scripts and server-to-server calls rely on it).
195
+ */
196
+ allowRequestToken?: boolean;
197
+ /** `x-accept-version` sent upstream. Defaults to `"2.0"`. */
198
+ apiVersion?: string;
199
+ /** Custom fetch for the upstream call. */
200
+ fetch?: typeof fetch;
201
+ /** Override how an entity is addressed (e.g. a different id header). */
202
+ entities?: Partial<Record<EntityKind, Partial<EntityDefinition>>>;
203
+ /** Extra `extras` keys to drop, on top of {@link BLOCKED_EXTRAS}. */
204
+ blockedExtras?: string[];
205
+ /**
206
+ * Called when the upstream call throws (network error). Defaults to a
207
+ * `console.error` with the method and path only — never headers or tokens.
208
+ */
209
+ onError?: (error: unknown, context: {
210
+ method: string;
211
+ path: string;
212
+ }) => void;
213
+ }
214
+ interface ForwardInit {
215
+ /** HTTP method sent upstream. Defaults to `GET`. */
216
+ method?: string;
217
+ /** Shopwave API path, e.g. `"product"`. */
218
+ path: string;
219
+ /** Extra upstream headers; they win over `extras`. */
220
+ headers?: Record<string, string>;
221
+ /** Sent as the form field `postBody=<JSON>`. */
222
+ postBody?: unknown;
223
+ /** Sent as a multipart body (e.g. uploads). Ignored when `postBody` is set. */
224
+ formData?: FormData;
225
+ /** Forward the caller's `extras` header as upstream headers. Defaults to `true`. */
226
+ forwardExtras?: boolean;
227
+ }
228
+ /** Second argument Next.js (and similar) pass to dynamic route handlers. */
229
+ interface RouteContext {
230
+ params?: Promise<Record<string, string | string[] | undefined>> | Record<string, string | string[] | undefined>;
231
+ }
232
+ type RouteHandler = (request: Request, context?: RouteContext) => Promise<Response>;
233
+ interface CollectionHandlers {
234
+ /** List/filter via `extras` (e.g. `productIds`, `deleted`). */
235
+ GET: RouteHandler;
236
+ /** Create or update (Shopwave upserts: an `id` means update). */
237
+ POST: RouteHandler;
238
+ /** Same as POST (kept for older clients). */
239
+ PUT: RouteHandler;
240
+ }
241
+ interface ItemHandlers {
242
+ /** Read one record by the `[id]` route param. */
243
+ GET: RouteHandler;
244
+ /** Update the record at `[id]` (the id in the URL wins). */
245
+ PUT: RouteHandler;
246
+ /** Delete the record at `[id]`. Shopwave answers 205 with an empty body. */
247
+ DELETE: RouteHandler;
248
+ }
249
+ interface ResourceHandlers {
250
+ /** Read the resource (`GET {apiUrl}/<upstream>`). */
251
+ GET: RouteHandler;
252
+ /**
253
+ * Update it: body `{ <key>: { id, … } }` or the bare object. The `id` is
254
+ * required (without one Shopwave would create a new record). 405 for
255
+ * read-only resources.
256
+ */
257
+ PUT: RouteHandler;
258
+ /** Same as PUT. */
259
+ POST: RouteHandler;
260
+ }
261
+ interface UploadOptions {
262
+ /** Largest accepted file, in bytes. Defaults to 10 MB. */
263
+ maxBytes?: number;
264
+ /**
265
+ * Accepted MIME types. An entry ending in `/` is a prefix (`"image/"`).
266
+ * Defaults to `["image/"]`.
267
+ */
268
+ accept?: string[];
269
+ }
270
+ interface UploadHandlers {
271
+ /**
272
+ * Multipart `file` + `kind` (see `UploadKind`) → `PUT {apiUrl}/uploader`.
273
+ * Answers with Shopwave's reply (201 `{ fileName, path, api }`); 400 for a
274
+ * missing file or unknown kind, 413 when too large, 415 for other types.
275
+ */
276
+ POST: RouteHandler;
277
+ }
278
+ interface ShopwaveApiHandlers {
279
+ /** Calls the Shopwave API for this request (token, extras, refresh-retry) and returns its answer. */
280
+ forward(request: Request, init: ForwardInit): Promise<Response>;
281
+ /** Handlers for `app/api/<route>/route.ts`. */
282
+ collection(kind: EntityKind): CollectionHandlers;
283
+ /** Handlers for `app/api/<route>/[id]/route.ts`. */
284
+ item(kind: EntityKind, options?: {
285
+ param?: string;
286
+ }): ItemHandlers;
287
+ /** GET-only proxy for a Shopwave path (e.g. `"report"`). */
288
+ passthrough(path: string): {
289
+ GET: RouteHandler;
290
+ };
291
+ /** Handlers for a singleton resource: `app/api/merchant/route.ts`, `app/api/user/route.ts`. */
292
+ resource(kind: ResourceKind): ResourceHandlers;
293
+ /** Handler for `app/api/upload/route.ts`. */
294
+ upload(options?: UploadOptions): UploadHandlers;
295
+ /** The entity table in use (defaults merged with `config.entities`). */
296
+ entity(kind: EntityKind): EntityDefinition;
297
+ }
298
+ //#endregion
87
299
  //#region src/core/session.d.ts
88
300
  /**
89
301
  * Login status returned by the app's `/api/session` route. It deliberately
@@ -192,6 +404,13 @@ interface ProtectOptions {
192
404
  publicPaths?: string[];
193
405
  /** Paths that get a 401 JSON response instead of a login redirect. Defaults to `["/api"]`. */
194
406
  apiPaths?: string[];
407
+ /**
408
+ * Let API requests that carry their own token (`Authorization: OAuth <token>`,
409
+ * or the legacy `token` header / `extras.token`) through without a session
410
+ * cookie. The route handlers from {@link createShopwaveApi} forward that token
411
+ * to the Shopwave API, which validates it. Defaults to `false`.
412
+ */
413
+ allowRequestToken?: boolean;
195
414
  }
196
415
  interface ShopwaveAuth {
197
416
  /** The underlying framework-agnostic OAuth client. */
@@ -250,6 +469,35 @@ interface ShopwaveAuth {
250
469
  * a missing env var doesn't fail `next build`.
251
470
  */
252
471
  declare function createShopwaveAuth(config: ShopwaveAuthConfig): ShopwaveAuth;
472
+ interface ShopwaveNextApiConfig extends Omit<ShopwaveApiConfig, "getAuthorization"> {
473
+ /** The app's auth instance: supplies (and refreshes) the logged-in user's token. */
474
+ auth: Pick<ShopwaveAuth, "getAuthorizationHeader">;
475
+ }
476
+ /**
477
+ * Ready-made Next.js route handlers for the SDK's `/api/*` contract, using the
478
+ * session from `createShopwaveAuth`. Create once (e.g. `lib/shopwave.ts`):
479
+ *
480
+ * ```ts
481
+ * export const shopwave = createShopwaveApi({ auth, apiUrl: process.env.SHOPWAVE_API_SERVER_URL! });
482
+ * ```
483
+ *
484
+ * then each route file is one line:
485
+ *
486
+ * ```ts
487
+ * // app/api/products/route.ts
488
+ * export const { GET, POST, PUT } = shopwave.collection("product");
489
+ * // app/api/products/[id]/route.ts
490
+ * export const { GET, PUT, DELETE } = shopwave.item("product");
491
+ * // app/api/report/route.ts
492
+ * export const { GET } = shopwave.passthrough("report");
493
+ * // app/api/merchant/route.ts, app/api/user/route.ts
494
+ * export const { GET, PUT } = shopwave.resource("merchant");
495
+ * export const { GET } = shopwave.resource("user");
496
+ * // app/api/upload/route.ts
497
+ * export const { POST } = shopwave.upload();
498
+ * ```
499
+ */
500
+ declare function createShopwaveApi(config: ShopwaveNextApiConfig): ShopwaveApiHandlers;
253
501
  //#endregion
254
- export { AuthContext, ProtectOptions, type SessionStatus, ShopwaveAuth, ShopwaveAuthConfig, ShopwaveAuthError, ShopwaveSessionConfig, ShopwaveSessionData, type ShopwaveToken, authorizationHeader, createShopwaveAuth, isExpiredTokenResponse };
502
+ export { AuthContext, type CollectionHandlers, type EntityDefinition, type EntityKind, type ForwardInit, type ItemHandlers, ProtectOptions, type ResourceHandlers, type ResourceKind, type RouteContext, type RouteHandler, SHOPWAVE_ENTITIES, SHOPWAVE_RESOURCES, type SessionStatus, type ShopwaveApiHandlers, ShopwaveAuth, ShopwaveAuthConfig, ShopwaveAuthError, ShopwaveNextApiConfig, ShopwaveSessionConfig, ShopwaveSessionData, type ShopwaveToken, UPLOAD_KINDS, type UploadHandlers, type UploadKind, type UploadOptions, authorizationHeader, createShopwaveApi, createShopwaveAuth, isExpiredTokenResponse, readRequestToken };
255
503
  //# sourceMappingURL=index.d.ts.map