react-shopwave-connect 0.3.0 → 0.4.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/README.md +73 -4
- package/dist/core/index.cjs +612 -7
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +459 -2
- package/dist/core/index.d.ts +459 -2
- package/dist/core/index.js +579 -8
- package/dist/core/index.js.map +1 -1
- package/dist/hooks/index.cjs +456 -0
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.cts +229 -1
- package/dist/hooks/index.d.ts +229 -1
- package/dist/hooks/index.js +452 -1
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.cjs +696 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +512 -2
- package/dist/index.d.ts +512 -2
- package/dist/index.js +658 -8
- package/dist/index.js.map +1 -1
- package/dist/next/index.cjs +126 -2
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +82 -2
- package/dist/next/index.d.ts +82 -2
- package/dist/next/index.js +125 -3
- package/dist/next/index.js.map +1 -1
- package/dist/server/index.cjs +122 -2
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +90 -2
- package/dist/server/index.d.ts +90 -2
- package/dist/server/index.js +120 -3
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
package/dist/next/index.cjs
CHANGED
|
@@ -345,6 +345,50 @@ const SHOPWAVE_ENTITIES = Object.freeze({
|
|
|
345
345
|
}
|
|
346
346
|
});
|
|
347
347
|
|
|
348
|
+
//#endregion
|
|
349
|
+
//#region src/core/resources.ts
|
|
350
|
+
/**
|
|
351
|
+
* - merchant: `GET /merchant`; `POST /merchant` with `{ merchant: { id, … } }`
|
|
352
|
+
* updates it (without an `id` the API would create a second merchant, so the
|
|
353
|
+
* route requires one).
|
|
354
|
+
* - user: `GET /user` (read-only here; the API's `POST /user` invites users and
|
|
355
|
+
* needs the admin scope).
|
|
356
|
+
*/
|
|
357
|
+
const SHOPWAVE_RESOURCES = Object.freeze({
|
|
358
|
+
merchant: {
|
|
359
|
+
route: "merchant",
|
|
360
|
+
key: "merchant",
|
|
361
|
+
upstream: "merchant",
|
|
362
|
+
writable: true
|
|
363
|
+
},
|
|
364
|
+
user: {
|
|
365
|
+
route: "user",
|
|
366
|
+
key: "user",
|
|
367
|
+
upstream: "user",
|
|
368
|
+
writable: false
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
/** `UploadKind` → the API's `contentType` value. */
|
|
372
|
+
const UPLOAD_KINDS = Object.freeze({
|
|
373
|
+
merchant: "merchant",
|
|
374
|
+
merchantFeature: "merchantFeature",
|
|
375
|
+
product: "product",
|
|
376
|
+
user: "user",
|
|
377
|
+
applicationLogo: "applicaionLogo",
|
|
378
|
+
applicationImages: "applicaionImages"
|
|
379
|
+
});
|
|
380
|
+
/** How uploads are addressed (app route and Shopwave uploader). */
|
|
381
|
+
const SHOPWAVE_UPLOAD = Object.freeze({
|
|
382
|
+
route: "upload",
|
|
383
|
+
upstream: "uploader",
|
|
384
|
+
kindHeader: "contentType",
|
|
385
|
+
fileField: "file",
|
|
386
|
+
kindField: "kind"
|
|
387
|
+
});
|
|
388
|
+
function isUploadKind(value) {
|
|
389
|
+
return typeof value === "string" && Object.prototype.hasOwnProperty.call(UPLOAD_KINDS, value);
|
|
390
|
+
}
|
|
391
|
+
|
|
348
392
|
//#endregion
|
|
349
393
|
//#region src/server/api.ts
|
|
350
394
|
/**
|
|
@@ -543,13 +587,14 @@ function createShopwaveApiHandlers(config) {
|
|
|
543
587
|
return jsonError(502, "auth_unavailable", "Could not refresh the Shopwave session");
|
|
544
588
|
}
|
|
545
589
|
if (!authorization) return jsonError(401, "unauthorized", "Not logged in");
|
|
546
|
-
const body = init.postBody
|
|
590
|
+
const body = init.postBody !== void 0 ? new URLSearchParams({ postBody: JSON.stringify(init.postBody) }) : init.formData;
|
|
547
591
|
const call = (authz) => {
|
|
548
592
|
const headers = new Headers(extras);
|
|
549
593
|
for (const [k, v] of Object.entries(init.headers ?? {})) headers.set(k, v);
|
|
550
594
|
headers.set("Authorization", authz);
|
|
551
595
|
headers.set("x-accept-version", apiVersion);
|
|
552
|
-
if (body) headers.set("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
|
|
596
|
+
if (body instanceof URLSearchParams) headers.set("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
|
|
597
|
+
else if (body) headers.delete("Content-Type");
|
|
553
598
|
return (config.fetch ?? fetch)(`${apiBase()}/${path}`, {
|
|
554
599
|
method,
|
|
555
600
|
headers,
|
|
@@ -612,6 +657,66 @@ function createShopwaveApiHandlers(config) {
|
|
|
612
657
|
postBody
|
|
613
658
|
});
|
|
614
659
|
}
|
|
660
|
+
function resourceDef(kind) {
|
|
661
|
+
const def = SHOPWAVE_RESOURCES[kind];
|
|
662
|
+
if (!def) throw new Error(`Unknown Shopwave resource "${kind}"`);
|
|
663
|
+
return def;
|
|
664
|
+
}
|
|
665
|
+
async function writeResource(request, def) {
|
|
666
|
+
if (!def.writable) return jsonError(405, "method_not_allowed", `The Shopwave API doesn't support writes for ${def.key}`);
|
|
667
|
+
let record;
|
|
668
|
+
try {
|
|
669
|
+
const body = await readJsonBody(request);
|
|
670
|
+
if (!isPlainObject(body)) throw new RequestBodyError("Request body must be a JSON object");
|
|
671
|
+
const inner = body[def.key];
|
|
672
|
+
if (inner !== void 0 && !isPlainObject(inner)) throw new RequestBodyError(`"${def.key}" must be an object`);
|
|
673
|
+
record = { ...inner ?? body };
|
|
674
|
+
const id = record.id;
|
|
675
|
+
if (id == null || id === "" || !ID_PATTERN.test(String(id))) throw new RequestBodyError(`Missing or invalid ${def.key} id`);
|
|
676
|
+
record.id = /^\d+$/.test(String(id)) ? Number(id) : id;
|
|
677
|
+
} catch (error) {
|
|
678
|
+
if (error instanceof RequestBodyError) return jsonError(400, "invalid_body", error.message);
|
|
679
|
+
throw error;
|
|
680
|
+
}
|
|
681
|
+
return forward(request, {
|
|
682
|
+
method: "POST",
|
|
683
|
+
path: def.upstream,
|
|
684
|
+
postBody: { [def.key]: record }
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
function acceptsType(type, accept) {
|
|
688
|
+
const t = type.toLowerCase().split(";")[0].trim();
|
|
689
|
+
return accept.some((a) => {
|
|
690
|
+
const rule = a.toLowerCase();
|
|
691
|
+
return rule.endsWith("/") ? t.startsWith(rule) : t === rule;
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
async function uploadFile(request, options) {
|
|
695
|
+
const maxBytes = options.maxBytes ?? 10 * 1024 * 1024;
|
|
696
|
+
const accept = options.accept ?? ["image/"];
|
|
697
|
+
if (Number(request.headers.get("content-length") ?? 0) > maxBytes + 64 * 1024) return jsonError(413, "too_large", `The file is larger than ${maxBytes} bytes`);
|
|
698
|
+
let form;
|
|
699
|
+
try {
|
|
700
|
+
form = await request.formData();
|
|
701
|
+
} catch {
|
|
702
|
+
return jsonError(400, "invalid_body", "Expected a multipart/form-data body");
|
|
703
|
+
}
|
|
704
|
+
const file = form.get(SHOPWAVE_UPLOAD.fileField);
|
|
705
|
+
if (!file || typeof file === "string" || file.size === 0) return jsonError(400, "invalid_body", `Missing "${SHOPWAVE_UPLOAD.fileField}"`);
|
|
706
|
+
const kind = form.get(SHOPWAVE_UPLOAD.kindField);
|
|
707
|
+
if (!isUploadKind(kind)) return jsonError(400, "invalid_body", `"${SHOPWAVE_UPLOAD.kindField}" must be one of ${Object.keys(UPLOAD_KINDS).join(", ")}`);
|
|
708
|
+
if (file.size > maxBytes) return jsonError(413, "too_large", `The file is larger than ${maxBytes} bytes`);
|
|
709
|
+
if (!acceptsType(file.type || "", accept)) return jsonError(415, "unsupported_type", `Files of type "${file.type || "unknown"}" aren't accepted`);
|
|
710
|
+
const upstreamForm = new FormData();
|
|
711
|
+
upstreamForm.append(SHOPWAVE_UPLOAD.fileField, file, file.name || "upload");
|
|
712
|
+
return forward(request, {
|
|
713
|
+
method: "PUT",
|
|
714
|
+
path: SHOPWAVE_UPLOAD.upstream,
|
|
715
|
+
headers: { [SHOPWAVE_UPLOAD.kindHeader]: UPLOAD_KINDS[kind] },
|
|
716
|
+
formData: upstreamForm,
|
|
717
|
+
forwardExtras: false
|
|
718
|
+
});
|
|
719
|
+
}
|
|
615
720
|
function badId() {
|
|
616
721
|
return jsonError(400, "invalid_id", "Missing or invalid id");
|
|
617
722
|
}
|
|
@@ -671,6 +776,18 @@ function createShopwaveApiHandlers(config) {
|
|
|
671
776
|
},
|
|
672
777
|
passthrough(path) {
|
|
673
778
|
return { GET: (request) => forward(request, { path }) };
|
|
779
|
+
},
|
|
780
|
+
resource(kind) {
|
|
781
|
+
const def = resourceDef(kind);
|
|
782
|
+
const save = (request) => writeResource(request, def);
|
|
783
|
+
return {
|
|
784
|
+
GET: (request) => forward(request, { path: def.upstream }),
|
|
785
|
+
PUT: save,
|
|
786
|
+
POST: save
|
|
787
|
+
};
|
|
788
|
+
},
|
|
789
|
+
upload(options = {}) {
|
|
790
|
+
return { POST: (request) => uploadFile(request, options) };
|
|
674
791
|
}
|
|
675
792
|
};
|
|
676
793
|
}
|
|
@@ -997,6 +1114,11 @@ function createShopwaveAuth(config) {
|
|
|
997
1114
|
* export const { GET, PUT, DELETE } = shopwave.item("product");
|
|
998
1115
|
* // app/api/report/route.ts
|
|
999
1116
|
* export const { GET } = shopwave.passthrough("report");
|
|
1117
|
+
* // app/api/merchant/route.ts, app/api/user/route.ts
|
|
1118
|
+
* export const { GET, PUT } = shopwave.resource("merchant");
|
|
1119
|
+
* export const { GET } = shopwave.resource("user");
|
|
1120
|
+
* // app/api/upload/route.ts
|
|
1121
|
+
* export const { POST } = shopwave.upload();
|
|
1000
1122
|
* ```
|
|
1001
1123
|
*/
|
|
1002
1124
|
function createShopwaveApi(config) {
|
|
@@ -1010,7 +1132,9 @@ function createShopwaveApi(config) {
|
|
|
1010
1132
|
|
|
1011
1133
|
//#endregion
|
|
1012
1134
|
exports.SHOPWAVE_ENTITIES = SHOPWAVE_ENTITIES;
|
|
1135
|
+
exports.SHOPWAVE_RESOURCES = SHOPWAVE_RESOURCES;
|
|
1013
1136
|
exports.ShopwaveAuthError = ShopwaveAuthError;
|
|
1137
|
+
exports.UPLOAD_KINDS = UPLOAD_KINDS;
|
|
1014
1138
|
exports.authorizationHeader = authorizationHeader;
|
|
1015
1139
|
exports.createShopwaveApi = createShopwaveApi;
|
|
1016
1140
|
exports.createShopwaveAuth = createShopwaveAuth;
|