@shopware-ag/app-server-sdk 1.1.2 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"context-resolver.d.ts","sourceRoot":"","sources":["../../src/context-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD;;;GAGG;AACH,qBAAa,eAAe,CAAC,IAAI,SAAS,aAAa,GAAG,aAAa;IAC1D,OAAO,CAAC,GAAG;gBAAH,GAAG,EAAE,SAAS;IAElC;;OAEG;IACU,OAAO,CAAC,OAAO,GAAG,OAAO,EACrC,GAAG,EAAE,OAAO,GACV,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAmClC;;;OAGG;IACU,WAAW,CAAC,OAAO,GAAG,OAAO,EACzC,GAAG,EAAE,OAAO,GACV,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;CA6BlC;AAED;;GAEG;AACH,qBAAa,OAAO,CACnB,IAAI,SAAS,aAAa,GAAG,aAAa,EAC1C,OAAO,GAAG,OAAO;IAGT,IAAI,EAAE,IAAI;IACV,OAAO,EAAE,OAAO;IAChB,UAAU,EAAE,UAAU;gBAFtB,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,UAAU;CAE9B"}
1
+ {"version":3,"file":"context-resolver.d.ts","sourceRoot":"","sources":["../../src/context-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD;;;GAGG;AACH,qBAAa,eAAe,CAAC,IAAI,SAAS,aAAa,GAAG,aAAa;IAC1D,OAAO,CAAC,GAAG;gBAAH,GAAG,EAAE,SAAS;IAElC;;OAEG;IACU,OAAO,CAAC,OAAO,GAAG,OAAO,EACrC,GAAG,EAAE,OAAO,GACV,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IA0ClC;;;OAGG;IACU,WAAW,CAAC,OAAO,GAAG,OAAO,EACzC,GAAG,EAAE,OAAO,GACV,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;CA6BlC;AAED;;GAEG;AACH,qBAAa,OAAO,CACnB,IAAI,SAAS,aAAa,GAAG,aAAa,EAC1C,OAAO,GAAG,OAAO;IAGT,IAAI,EAAE,IAAI;IACV,OAAO,EAAE,OAAO;IAChB,UAAU,EAAE,UAAU;gBAFtB,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,UAAU;CAE9B"}
@@ -17,6 +17,10 @@ class ContextResolver {
17
17
  async fromAPI(req) {
18
18
  const webHookContent = await req.text();
19
19
  const webHookBody = JSON.parse(webHookContent);
20
+ if (webHookBody.source === undefined ||
21
+ webHookBody.source.shopId === undefined) {
22
+ throw new Error("Invalid request");
23
+ }
20
24
  const shop = await this.app.repository.getShopById(webHookBody.source.shopId);
21
25
  if (shop === null) {
22
26
  throw new Error(`Cannot find shop by id ${webHookBody.source.shopId}`);
@@ -1 +1 @@
1
- {"version":3,"file":"context-resolver.js","sourceRoot":"","sources":["../../src/context-resolver.ts"],"names":[],"mappings":";;;AACA,qDAA8C;AAG9C;;;GAGG;AACH,MAAa,eAAe;IACP;IAApB,YAAoB,GAAc;QAAd,QAAG,GAAH,GAAG,CAAW;IAAG,CAAC;IAEtC;;OAEG;IACI,KAAK,CAAC,OAAO,CACnB,GAAY;QAEZ,MAAM,cAAc,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QACxC,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAE/C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,WAAW,CACjD,WAAW,CAAC,MAAM,CAAC,MAAM,CACzB,CAAC;QAEF,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,0BAA0B,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QACxE,CAAC;QAED,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;QAE7D,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAC3D,CAAC;QAED,IACC,CAAC,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAC7B,SAAS,EACT,cAAc,EACd,IAAI,CAAC,aAAa,EAAE,CACpB,CAAC,EACD,CAAC;YACF,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACtC,CAAC;QAED,OAAO,IAAI,OAAO,CACjB,IAAY,EACZ,WAAW,EACX,IAAI,2BAAU,CAAC,IAAI,CAAC,CACpB,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,WAAW,CACvB,GAAY;QAEZ,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAE7B,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAE/C,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACpD,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAE3D,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,0BAA0B,MAAM,EAAE,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;QAElE,MAAM,YAAY,GAA2B,EAAE,CAAC;QAEhD,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YACvC,YAAY,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAC3B,CAAC,CAAC,CAAC;QAEH,OAAO,IAAI,OAAO,CACjB,IAAY,EACZ,YAAuB,EACvB,IAAI,2BAAU,CAAC,IAAI,CAAC,CACpB,CAAC;IACH,CAAC;CACD;AA9ED,0CA8EC;AAED;;GAEG;AACH,MAAa,OAAO;IAKX;IACA;IACA;IAHR,YACQ,IAAU,EACV,OAAgB,EAChB,UAAsB;QAFtB,SAAI,GAAJ,IAAI,CAAM;QACV,YAAO,GAAP,OAAO,CAAS;QAChB,eAAU,GAAV,UAAU,CAAY;IAC3B,CAAC;CACJ;AATD,0BASC","sourcesContent":["import type { AppServer } from \"./app.js\";\nimport { HttpClient } from \"./http-client.js\";\nimport type { ShopInterface } from \"./repository.js\";\n\n/**\n * ContextResolver is a helper class to create a Context object from a request.\n * The context contains the shop, the payload and an instance of the HttpClient\n */\nexport class ContextResolver<Shop extends ShopInterface = ShopInterface> {\n\tconstructor(private app: AppServer) {}\n\n\t/**\n\t * Create a context from a request body\n\t */\n\tpublic async fromAPI<Payload = unknown>(\n\t\treq: Request,\n\t): Promise<Context<Shop, Payload>> {\n\t\tconst webHookContent = await req.text();\n\t\tconst webHookBody = JSON.parse(webHookContent);\n\n\t\tconst shop = await this.app.repository.getShopById(\n\t\t\twebHookBody.source.shopId,\n\t\t);\n\n\t\tif (shop === null) {\n\t\t\tthrow new Error(`Cannot find shop by id ${webHookBody.source.shopId}`);\n\t\t}\n\n\t\tconst signature = req.headers.get(\"shopware-shop-signature\");\n\n\t\tif (signature === null) {\n\t\t\tthrow new Error(\"Missing shopware-shop-signature header\");\n\t\t}\n\n\t\tif (\n\t\t\t!(await this.app.signer.verify(\n\t\t\t\tsignature,\n\t\t\t\twebHookContent,\n\t\t\t\tshop.getShopSecret(),\n\t\t\t))\n\t\t) {\n\t\t\tthrow new Error(\"Invalid signature\");\n\t\t}\n\n\t\treturn new Context<Shop, Payload>(\n\t\t\tshop as Shop,\n\t\t\twebHookBody,\n\t\t\tnew HttpClient(shop),\n\t\t);\n\t}\n\n\t/**\n\t * Create a context from a request query parameters\n\t * This is usually a module request from the shopware admin\n\t */\n\tpublic async fromBrowser<Payload = unknown>(\n\t\treq: Request,\n\t): Promise<Context<Shop, Payload>> {\n\t\tconst url = new URL(req.url);\n\n\t\tconst shopId = url.searchParams.get(\"shop-id\");\n\n\t\tif (shopId === null) {\n\t\t\tthrow new Error(\"Missing shop-id query parameter\");\n\t\t}\n\n\t\tconst shop = await this.app.repository.getShopById(shopId);\n\n\t\tif (shop === null) {\n\t\t\tthrow new Error(`Cannot find shop by id ${shopId}`);\n\t\t}\n\n\t\tawait this.app.signer.verifyGetRequest(req, shop.getShopSecret());\n\n\t\tconst paramsObject: Record<string, string> = {};\n\n\t\turl.searchParams.forEach((value, key) => {\n\t\t\tparamsObject[key] = value;\n\t\t});\n\n\t\treturn new Context<Shop, Payload>(\n\t\t\tshop as Shop,\n\t\t\tparamsObject as Payload,\n\t\t\tnew HttpClient(shop),\n\t\t);\n\t}\n}\n\n/**\n * Context is the parsed data from the request\n */\nexport class Context<\n\tShop extends ShopInterface = ShopInterface,\n\tPayload = unknown,\n> {\n\tconstructor(\n\t\tpublic shop: Shop,\n\t\tpublic payload: Payload,\n\t\tpublic httpClient: HttpClient,\n\t) {}\n}\n"]}
1
+ {"version":3,"file":"context-resolver.js","sourceRoot":"","sources":["../../src/context-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AppServer } from \"./app.js\";\nimport { HttpClient } from \"./http-client.js\";\nimport type { ShopInterface } from \"./repository.js\";\n\n/**\n * ContextResolver is a helper class to create a Context object from a request.\n * The context contains the shop, the payload and an instance of the HttpClient\n */\nexport class ContextResolver<Shop extends ShopInterface = ShopInterface> {\n\tconstructor(private app: AppServer) {}\n\n\t/**\n\t * Create a context from a request body\n\t */\n\tpublic async fromAPI<Payload = unknown>(\n\t\treq: Request,\n\t): Promise<Context<Shop, Payload>> {\n\t\tconst webHookContent = await req.text();\n\t\tconst webHookBody = JSON.parse(webHookContent);\n\n\t\tif (\n\t\t\twebHookBody.source === undefined ||\n\t\t\twebHookBody.source.shopId === undefined\n\t\t) {\n\t\t\tthrow new Error(\"Invalid request\");\n\t\t}\n\n\t\tconst shop = await this.app.repository.getShopById(\n\t\t\twebHookBody.source.shopId,\n\t\t);\n\n\t\tif (shop === null) {\n\t\t\tthrow new Error(`Cannot find shop by id ${webHookBody.source.shopId}`);\n\t\t}\n\n\t\tconst signature = req.headers.get(\"shopware-shop-signature\");\n\n\t\tif (signature === null) {\n\t\t\tthrow new Error(\"Missing shopware-shop-signature header\");\n\t\t}\n\n\t\tif (\n\t\t\t!(await this.app.signer.verify(\n\t\t\t\tsignature,\n\t\t\t\twebHookContent,\n\t\t\t\tshop.getShopSecret(),\n\t\t\t))\n\t\t) {\n\t\t\tthrow new Error(\"Invalid signature\");\n\t\t}\n\n\t\treturn new Context<Shop, Payload>(\n\t\t\tshop as Shop,\n\t\t\twebHookBody,\n\t\t\tnew HttpClient(shop),\n\t\t);\n\t}\n\n\t/**\n\t * Create a context from a request query parameters\n\t * This is usually a module request from the shopware admin\n\t */\n\tpublic async fromBrowser<Payload = unknown>(\n\t\treq: Request,\n\t): Promise<Context<Shop, Payload>> {\n\t\tconst url = new URL(req.url);\n\n\t\tconst shopId = url.searchParams.get(\"shop-id\");\n\n\t\tif (shopId === null) {\n\t\t\tthrow new Error(\"Missing shop-id query parameter\");\n\t\t}\n\n\t\tconst shop = await this.app.repository.getShopById(shopId);\n\n\t\tif (shop === null) {\n\t\t\tthrow new Error(`Cannot find shop by id ${shopId}`);\n\t\t}\n\n\t\tawait this.app.signer.verifyGetRequest(req, shop.getShopSecret());\n\n\t\tconst paramsObject: Record<string, string> = {};\n\n\t\turl.searchParams.forEach((value, key) => {\n\t\t\tparamsObject[key] = value;\n\t\t});\n\n\t\treturn new Context<Shop, Payload>(\n\t\t\tshop as Shop,\n\t\t\tparamsObject as Payload,\n\t\t\tnew HttpClient(shop),\n\t\t);\n\t}\n}\n\n/**\n * Context is the parsed data from the request\n */\nexport class Context<\n\tShop extends ShopInterface = ShopInterface,\n\tPayload = unknown,\n> {\n\tconstructor(\n\t\tpublic shop: Shop,\n\t\tpublic payload: Payload,\n\t\tpublic httpClient: HttpClient,\n\t) {}\n}\n"]}
@@ -13,7 +13,7 @@ export declare class HttpClient {
13
13
  /**
14
14
  * Permform a POST request
15
15
  */
16
- post<ResponseType>(url: string, json?: object, headers?: Record<string, string>): Promise<HttpClientResponse<ResponseType>>;
16
+ post<ResponseType>(url: string, json?: object | FormData | Blob, headers?: Record<string, string>): Promise<HttpClientResponse<ResponseType>>;
17
17
  /**
18
18
  * Permform a PUT request
19
19
  */
@@ -1 +1 @@
1
- {"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../../src/http-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD;;GAEG;AACH,qBAAa,UAAU;IAGV,OAAO,CAAC,IAAI;IAFxB,OAAO,CAAC,OAAO,CAAmD;gBAE9C,IAAI,EAAE,aAAa;IAOvC;;OAEG;IACG,GAAG,CAAC,YAAY,EACrB,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAClC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAI5C;;OAEG;IACG,IAAI,CAAC,YAAY,EACtB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,MAAW,EACjB,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAClC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAO5C;;OAEG;IACG,GAAG,CAAC,YAAY,EACrB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,MAAW,EACjB,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAClC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAO5C;;OAEG;IACG,KAAK,CAAC,YAAY,EACvB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,MAAW,EACjB,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAClC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAO5C;;OAEG;IACG,MAAM,CAAC,YAAY,EACxB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,MAAW,EACjB,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAClC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;YAO9B,OAAO;IAyCrB;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;CAuDjC;AAED;;GAEG;AACH,qBAAa,kBAAkB,CAAC,YAAY;IAEnC,UAAU,EAAE,MAAM;IAClB,IAAI,EAAE,YAAY;IAClB,OAAO,EAAE,OAAO;gBAFhB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE,OAAO;CAExB;AAED,KAAK,qBAAqB,GAAG;IAC5B,MAAM,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KACf,EAAE,CAAC;CACJ,CAAC;AAEF;;GAEG;AACH,qBAAa,6BAA8B,SAAQ,KAAK;IAG/C,QAAQ,EAAE,kBAAkB,CAAC,MAAM,CAAC;gBAD3C,MAAM,EAAE,MAAM,EACP,QAAQ,EAAE,kBAAkB,CAAC,MAAM,CAAC;CAM5C;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;IAGxC,QAAQ,EAAE,kBAAkB,CAAC,qBAAqB,CAAC;gBAD1D,MAAM,EAAE,MAAM,EACP,QAAQ,EAAE,kBAAkB,CAAC,qBAAqB,CAAC;CAM3D"}
1
+ {"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../../src/http-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD;;GAEG;AACH,qBAAa,UAAU;IAGV,OAAO,CAAC,IAAI;IAFxB,OAAO,CAAC,OAAO,CAAmD;gBAE9C,IAAI,EAAE,aAAa;IAOvC;;OAEG;IACG,GAAG,CAAC,YAAY,EACrB,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAClC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAI5C;;OAEG;IACG,IAAI,CAAC,YAAY,EACtB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,MAAM,GAAG,QAAQ,GAAG,IAAS,EACnC,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAClC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAkB5C;;OAEG;IACG,GAAG,CAAC,YAAY,EACrB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,MAAW,EACjB,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAClC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAO5C;;OAEG;IACG,KAAK,CAAC,YAAY,EACvB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,MAAW,EACjB,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAClC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAO5C;;OAEG;IACG,MAAM,CAAC,YAAY,EACxB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,MAAW,EACjB,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAClC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;YAO9B,OAAO;IAyCrB;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;CAuDjC;AAED;;GAEG;AACH,qBAAa,kBAAkB,CAAC,YAAY;IAEnC,UAAU,EAAE,MAAM;IAClB,IAAI,EAAE,YAAY;IAClB,OAAO,EAAE,OAAO;gBAFhB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE,OAAO;CAExB;AAED,KAAK,qBAAqB,GAAG;IAC5B,MAAM,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KACf,EAAE,CAAC;CACJ,CAAC;AAEF;;GAEG;AACH,qBAAa,6BAA8B,SAAQ,KAAK;IAG/C,QAAQ,EAAE,kBAAkB,CAAC,MAAM,CAAC;gBAD3C,MAAM,EAAE,MAAM,EACP,QAAQ,EAAE,kBAAkB,CAAC,MAAM,CAAC;CAM5C;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;IAGxC,QAAQ,EAAE,kBAAkB,CAAC,qBAAqB,CAAC;gBAD1D,MAAM,EAAE,MAAM,EACP,QAAQ,EAAE,kBAAkB,CAAC,qBAAqB,CAAC;CAM3D"}
@@ -24,9 +24,13 @@ class HttpClient {
24
24
  * Permform a POST request
25
25
  */
26
26
  async post(url, json = {}, headers = {}) {
27
- headers["content-type"] = "application/json";
27
+ let data = json;
28
+ if (!(json instanceof Blob) && !(json instanceof FormData)) {
29
+ headers["content-type"] = "application/json";
30
+ data = JSON.stringify(json);
31
+ }
28
32
  headers.accept = "application/json";
29
- return await this.request("POST", url, JSON.stringify(json), headers);
33
+ return await this.request("POST", url, data, headers);
30
34
  }
31
35
  /**
32
36
  * Permform a PUT request
@@ -1 +1 @@
1
- {"version":3,"file":"http-client.js","sourceRoot":"","sources":["../../src/http-client.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ShopInterface } from \"./repository.js\";\n\n/**\n * HttpClient is a simple wrapper around the fetch API, pre-configured with the shop's URL and access token\n */\nexport class HttpClient {\n\tprivate storage: { expiresIn: Date | null; token: string | null };\n\n\tconstructor(private shop: ShopInterface) {\n\t\tthis.storage = {\n\t\t\ttoken: null,\n\t\t\texpiresIn: null,\n\t\t};\n\t}\n\n\t/**\n\t * Permform a GET request\n\t */\n\tasync get<ResponseType>(\n\t\turl: string,\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\treturn await this.request(\"GET\", url, null, headers);\n\t}\n\n\t/**\n\t * Permform a POST request\n\t */\n\tasync post<ResponseType>(\n\t\turl: string,\n\t\tjson: object = {},\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\theaders[\"content-type\"] = \"application/json\";\n\t\theaders.accept = \"application/json\";\n\n\t\treturn await this.request(\"POST\", url, JSON.stringify(json), headers);\n\t}\n\n\t/**\n\t * Permform a PUT request\n\t */\n\tasync put<ResponseType>(\n\t\turl: string,\n\t\tjson: object = {},\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\theaders[\"content-type\"] = \"application/json\";\n\t\theaders.accept = \"application/json\";\n\n\t\treturn await this.request(\"PUT\", url, JSON.stringify(json), headers);\n\t}\n\n\t/**\n\t * Permform a PATCH request\n\t */\n\tasync patch<ResponseType>(\n\t\turl: string,\n\t\tjson: object = {},\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\theaders[\"content-type\"] = \"application/json\";\n\t\theaders.accept = \"application/json\";\n\n\t\treturn await this.request(\"PATCH\", url, JSON.stringify(json), headers);\n\t}\n\n\t/**\n\t * Permform a DELETE request\n\t */\n\tasync delete<ResponseType>(\n\t\turl: string,\n\t\tjson: object = {},\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\theaders[\"content-type\"] = \"application/json\";\n\t\theaders.accept = \"application/json\";\n\n\t\treturn await this.request(\"DELETE\", url, JSON.stringify(json), headers);\n\t}\n\n\tprivate async request<ResponseType>(\n\t\tmethod: string,\n\t\turl: string,\n\t\tbody: string | null = \"\",\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\tconst f = await globalThis.fetch(`${this.shop.getShopUrl()}/api${url}`, {\n\t\t\tbody,\n\t\t\theaders: Object.assign(\n\t\t\t\t{\n\t\t\t\t\tAuthorization: `Bearer ${await this.getToken()}`,\n\t\t\t\t},\n\t\t\t\theaders,\n\t\t\t),\n\t\t\tmethod,\n\t\t});\n\n\t\t// Obtain new token\n\t\tif (!f.ok && f.status === 401) {\n\t\t\tthis.storage.expiresIn = null;\n\n\t\t\treturn await this.request(method, url, body, headers);\n\t\t}\n\t\tif (!f.ok) {\n\t\t\tthrow new ApiClientRequestFailed(\n\t\t\t\tthis.shop.getShopId(),\n\t\t\t\tnew HttpClientResponse(f.status, await f.json(), f.headers),\n\t\t\t);\n\t\t}\n\n\t\tif (f.status === 204) {\n\t\t\treturn new HttpClientResponse<ResponseType>(\n\t\t\t\tf.status,\n\t\t\t\t{} as ResponseType,\n\t\t\t\tf.headers,\n\t\t\t);\n\t\t}\n\n\t\treturn new HttpClientResponse(f.status, await f.json(), f.headers);\n\t}\n\n\t/**\n\t * Obtain a valid bearer token\n\t */\n\tasync getToken(): Promise<string> {\n\t\tif (this.storage.expiresIn === null) {\n\t\t\tconst auth = await globalThis.fetch(\n\t\t\t\t`${this.shop.getShopUrl()}/api/oauth/token`,\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t\t\t},\n\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\tgrant_type: \"client_credentials\",\n\t\t\t\t\t\tclient_id: this.shop.getShopClientId(),\n\t\t\t\t\t\tclient_secret: this.shop.getShopClientSecret(),\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tif (!auth.ok) {\n\t\t\t\tconst contentType = auth.headers.get(\"content-type\") || \"text/plain\";\n\t\t\t\tlet body = \"\";\n\n\t\t\t\tif (contentType.indexOf(\"application/json\") !== -1) {\n\t\t\t\t\tbody = await auth.json();\n\t\t\t\t} else {\n\t\t\t\t\tbody = await auth.text();\n\t\t\t\t}\n\n\t\t\t\tthrow new ApiClientAuthenticationFailed(\n\t\t\t\t\tthis.shop.getShopId(),\n\t\t\t\t\tnew HttpClientResponse<string>(auth.status, body, auth.headers),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst expireDate = new Date();\n\t\t\tconst authBody = (await auth.json()) as {\n\t\t\t\taccess_token: string;\n\t\t\t\texpires_in: number;\n\t\t\t};\n\t\t\tthis.storage.token = authBody.access_token;\n\t\t\texpireDate.setSeconds(expireDate.getSeconds() + authBody.expires_in);\n\t\t\tthis.storage.expiresIn = expireDate;\n\n\t\t\treturn this.storage.token as string;\n\t\t}\n\n\t\tif (this.storage.expiresIn.getTime() < new Date().getTime()) {\n\t\t\t// Expired\n\n\t\t\tthis.storage.expiresIn = null;\n\n\t\t\treturn await this.getToken();\n\t\t}\n\n\t\treturn this.storage.token as string;\n\t}\n}\n\n/**\n * HttpClientResponse is the response object of the HttpClient\n */\nexport class HttpClientResponse<ResponseType> {\n\tconstructor(\n\t\tpublic statusCode: number,\n\t\tpublic body: ResponseType,\n\t\tpublic headers: Headers,\n\t) {}\n}\n\ntype ShopwareErrorResponse = {\n\terrors: {\n\t\tcode: string;\n\t\tstatus: string;\n\t\ttitle: string;\n\t\tdetail: string;\n\t}[];\n};\n\n/**\n * ApiClientAuthenticationFailed is thrown when the authentication to the shop's API fails\n */\nexport class ApiClientAuthenticationFailed extends Error {\n\tconstructor(\n\t\tshopId: string,\n\t\tpublic response: HttpClientResponse<string>,\n\t) {\n\t\tsuper(\n\t\t\t`The api client authentication to shop with id: ${shopId} with response: ${JSON.stringify(response.body)}`,\n\t\t);\n\t}\n}\n\n/**\n * ApiClientRequestFailed is thrown when the request to the shop's API fails\n */\nexport class ApiClientRequestFailed extends Error {\n\tconstructor(\n\t\tshopId: string,\n\t\tpublic response: HttpClientResponse<ShopwareErrorResponse>,\n\t) {\n\t\tconst message = response.body.errors.map((e) => e.detail).join(\", \");\n\n\t\tsuper(`Request failed with error: ${message} for shop with id: ${shopId}`);\n\t}\n}\n"]}
1
+ {"version":3,"file":"http-client.js","sourceRoot":"","sources":["../../src/http-client.ts"],"names":[],"mappings":";;;AAEA;;GAEG;AACH,MAAa,UAAU;IAGF;IAFZ,OAAO,CAAmD;IAElE,YAAoB,IAAmB;QAAnB,SAAI,GAAJ,IAAI,CAAe;QACtC,IAAI,CAAC,OAAO,GAAG;YACd,KAAK,EAAE,IAAI;YACX,SAAS,EAAE,IAAI;SACf,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,GAAG,CACR,GAAW,EACX,UAAkC,EAAE;QAEpC,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACtD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI,CACT,GAAW,EACX,OAAiC,EAAE,EACnC,UAAkC,EAAE;QAEpC,IAAI,IAAI,GAAsC,IAAI,CAAC;QAEnD,IAAI,CAAC,CAAC,IAAI,YAAY,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,YAAY,QAAQ,CAAC,EAAE,CAAC;YAC5D,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;YAC7C,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;QAED,OAAO,CAAC,MAAM,GAAG,kBAAkB,CAAC;QAEpC,OAAO,MAAM,IAAI,CAAC,OAAO,CACxB,MAAM,EACN,GAAG,EACH,IAAgC,EAChC,OAAO,CACP,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,GAAG,CACR,GAAW,EACX,OAAe,EAAE,EACjB,UAAkC,EAAE;QAEpC,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;QAC7C,OAAO,CAAC,MAAM,GAAG,kBAAkB,CAAC;QAEpC,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;IACtE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,KAAK,CACV,GAAW,EACX,OAAe,EAAE,EACjB,UAAkC,EAAE;QAEpC,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;QAC7C,OAAO,CAAC,MAAM,GAAG,kBAAkB,CAAC;QAEpC,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;IACxE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM,CACX,GAAW,EACX,OAAe,EAAE,EACjB,UAAkC,EAAE;QAEpC,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;QAC7C,OAAO,CAAC,MAAM,GAAG,kBAAkB,CAAC;QAEpC,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;IACzE,CAAC;IAEO,KAAK,CAAC,OAAO,CACpB,MAAc,EACd,GAAW,EACX,OAAwC,EAAE,EAC1C,UAAkC,EAAE;QAEpC,MAAM,CAAC,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,EAAE,EAAE;YACvE,IAAI;YACJ,OAAO,EAAE,MAAM,CAAC,MAAM,CACrB;gBACC,aAAa,EAAE,UAAU,MAAM,IAAI,CAAC,QAAQ,EAAE,EAAE;aAChD,EACD,OAAO,CACP;YACD,MAAM;SACN,CAAC,CAAC;QAEH,mBAAmB;QACnB,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC/B,IAAI,CAAC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;YAE9B,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QACvD,CAAC;QACD,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;YACX,MAAM,IAAI,sBAAsB,CAC/B,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EACrB,IAAI,kBAAkB,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,CAC3D,CAAC;QACH,CAAC;QAED,IAAI,CAAC,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACtB,OAAO,IAAI,kBAAkB,CAC5B,CAAC,CAAC,MAAM,EACR,EAAkB,EAClB,CAAC,CAAC,OAAO,CACT,CAAC;QACH,CAAC;QAED,OAAO,IAAI,kBAAkB,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;IACpE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAQ;QACb,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;YACrC,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,CAClC,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,kBAAkB,EAC3C;gBACC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACR,cAAc,EAAE,kBAAkB;iBAClC;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACpB,UAAU,EAAE,oBAAoB;oBAChC,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE;oBACtC,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE;iBAC9C,CAAC;aACF,CACD,CAAC;YAEF,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;gBACd,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,YAAY,CAAC;gBACrE,IAAI,IAAI,GAAG,EAAE,CAAC;gBAEd,IAAI,WAAW,CAAC,OAAO,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;oBACpD,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;gBAC1B,CAAC;qBAAM,CAAC;oBACP,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;gBAC1B,CAAC;gBAED,MAAM,IAAI,6BAA6B,CACtC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EACrB,IAAI,kBAAkB,CAAS,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAC/D,CAAC;YACH,CAAC;YAED,MAAM,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC;YAC9B,MAAM,QAAQ,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAGlC,CAAC;YACF,IAAI,CAAC,OAAO,CAAC,KAAK,GAAG,QAAQ,CAAC,YAAY,CAAC;YAC3C,UAAU,CAAC,UAAU,CAAC,UAAU,CAAC,UAAU,EAAE,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;YACrE,IAAI,CAAC,OAAO,CAAC,SAAS,GAAG,UAAU,CAAC;YAEpC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAe,CAAC;QACrC,CAAC;QAED,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC;YAC7D,UAAU;YAEV,IAAI,CAAC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;YAE9B,OAAO,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC9B,CAAC;QAED,OAAO,IAAI,CAAC,OAAO,CAAC,KAAe,CAAC;IACrC,CAAC;CACD;AA1LD,gCA0LC;AAED;;GAEG;AACH,MAAa,kBAAkB;IAEtB;IACA;IACA;IAHR,YACQ,UAAkB,EAClB,IAAkB,EAClB,OAAgB;QAFhB,eAAU,GAAV,UAAU,CAAQ;QAClB,SAAI,GAAJ,IAAI,CAAc;QAClB,YAAO,GAAP,OAAO,CAAS;IACrB,CAAC;CACJ;AAND,gDAMC;AAWD;;GAEG;AACH,MAAa,6BAA8B,SAAQ,KAAK;IAG/C;IAFR,YACC,MAAc,EACP,QAAoC;QAE3C,KAAK,CACJ,kDAAkD,MAAM,mBAAmB,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAC1G,CAAC;QAJK,aAAQ,GAAR,QAAQ,CAA4B;IAK5C,CAAC;CACD;AATD,sEASC;AAED;;GAEG;AACH,MAAa,sBAAuB,SAAQ,KAAK;IAGxC;IAFR,YACC,MAAc,EACP,QAAmD;QAE1D,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAErE,KAAK,CAAC,8BAA8B,OAAO,sBAAsB,MAAM,EAAE,CAAC,CAAC;QAJpE,aAAQ,GAAR,QAAQ,CAA2C;IAK3D,CAAC;CACD;AATD,wDASC","sourcesContent":["import type { ShopInterface } from \"./repository.js\";\n\n/**\n * HttpClient is a simple wrapper around the fetch API, pre-configured with the shop's URL and access token\n */\nexport class HttpClient {\n\tprivate storage: { expiresIn: Date | null; token: string | null };\n\n\tconstructor(private shop: ShopInterface) {\n\t\tthis.storage = {\n\t\t\ttoken: null,\n\t\t\texpiresIn: null,\n\t\t};\n\t}\n\n\t/**\n\t * Permform a GET request\n\t */\n\tasync get<ResponseType>(\n\t\turl: string,\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\treturn await this.request(\"GET\", url, null, headers);\n\t}\n\n\t/**\n\t * Permform a POST request\n\t */\n\tasync post<ResponseType>(\n\t\turl: string,\n\t\tjson: object | FormData | Blob = {},\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\tlet data: object | FormData | Blob | string = json;\n\n\t\tif (!(json instanceof Blob) && !(json instanceof FormData)) {\n\t\t\theaders[\"content-type\"] = \"application/json\";\n\t\t\tdata = JSON.stringify(json);\n\t\t}\n\n\t\theaders.accept = \"application/json\";\n\n\t\treturn await this.request(\n\t\t\t\"POST\",\n\t\t\turl,\n\t\t\tdata as FormData | Blob | string,\n\t\t\theaders,\n\t\t);\n\t}\n\n\t/**\n\t * Permform a PUT request\n\t */\n\tasync put<ResponseType>(\n\t\turl: string,\n\t\tjson: object = {},\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\theaders[\"content-type\"] = \"application/json\";\n\t\theaders.accept = \"application/json\";\n\n\t\treturn await this.request(\"PUT\", url, JSON.stringify(json), headers);\n\t}\n\n\t/**\n\t * Permform a PATCH request\n\t */\n\tasync patch<ResponseType>(\n\t\turl: string,\n\t\tjson: object = {},\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\theaders[\"content-type\"] = \"application/json\";\n\t\theaders.accept = \"application/json\";\n\n\t\treturn await this.request(\"PATCH\", url, JSON.stringify(json), headers);\n\t}\n\n\t/**\n\t * Permform a DELETE request\n\t */\n\tasync delete<ResponseType>(\n\t\turl: string,\n\t\tjson: object = {},\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\theaders[\"content-type\"] = \"application/json\";\n\t\theaders.accept = \"application/json\";\n\n\t\treturn await this.request(\"DELETE\", url, JSON.stringify(json), headers);\n\t}\n\n\tprivate async request<ResponseType>(\n\t\tmethod: string,\n\t\turl: string,\n\t\tbody: string | FormData | Blob | null = \"\",\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\tconst f = await globalThis.fetch(`${this.shop.getShopUrl()}/api${url}`, {\n\t\t\tbody,\n\t\t\theaders: Object.assign(\n\t\t\t\t{\n\t\t\t\t\tAuthorization: `Bearer ${await this.getToken()}`,\n\t\t\t\t},\n\t\t\t\theaders,\n\t\t\t),\n\t\t\tmethod,\n\t\t});\n\n\t\t// Obtain new token\n\t\tif (!f.ok && f.status === 401) {\n\t\t\tthis.storage.expiresIn = null;\n\n\t\t\treturn await this.request(method, url, body, headers);\n\t\t}\n\t\tif (!f.ok) {\n\t\t\tthrow new ApiClientRequestFailed(\n\t\t\t\tthis.shop.getShopId(),\n\t\t\t\tnew HttpClientResponse(f.status, await f.json(), f.headers),\n\t\t\t);\n\t\t}\n\n\t\tif (f.status === 204) {\n\t\t\treturn new HttpClientResponse<ResponseType>(\n\t\t\t\tf.status,\n\t\t\t\t{} as ResponseType,\n\t\t\t\tf.headers,\n\t\t\t);\n\t\t}\n\n\t\treturn new HttpClientResponse(f.status, await f.json(), f.headers);\n\t}\n\n\t/**\n\t * Obtain a valid bearer token\n\t */\n\tasync getToken(): Promise<string> {\n\t\tif (this.storage.expiresIn === null) {\n\t\t\tconst auth = await globalThis.fetch(\n\t\t\t\t`${this.shop.getShopUrl()}/api/oauth/token`,\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t\t\t},\n\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\tgrant_type: \"client_credentials\",\n\t\t\t\t\t\tclient_id: this.shop.getShopClientId(),\n\t\t\t\t\t\tclient_secret: this.shop.getShopClientSecret(),\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tif (!auth.ok) {\n\t\t\t\tconst contentType = auth.headers.get(\"content-type\") || \"text/plain\";\n\t\t\t\tlet body = \"\";\n\n\t\t\t\tif (contentType.indexOf(\"application/json\") !== -1) {\n\t\t\t\t\tbody = await auth.json();\n\t\t\t\t} else {\n\t\t\t\t\tbody = await auth.text();\n\t\t\t\t}\n\n\t\t\t\tthrow new ApiClientAuthenticationFailed(\n\t\t\t\t\tthis.shop.getShopId(),\n\t\t\t\t\tnew HttpClientResponse<string>(auth.status, body, auth.headers),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst expireDate = new Date();\n\t\t\tconst authBody = (await auth.json()) as {\n\t\t\t\taccess_token: string;\n\t\t\t\texpires_in: number;\n\t\t\t};\n\t\t\tthis.storage.token = authBody.access_token;\n\t\t\texpireDate.setSeconds(expireDate.getSeconds() + authBody.expires_in);\n\t\t\tthis.storage.expiresIn = expireDate;\n\n\t\t\treturn this.storage.token as string;\n\t\t}\n\n\t\tif (this.storage.expiresIn.getTime() < new Date().getTime()) {\n\t\t\t// Expired\n\n\t\t\tthis.storage.expiresIn = null;\n\n\t\t\treturn await this.getToken();\n\t\t}\n\n\t\treturn this.storage.token as string;\n\t}\n}\n\n/**\n * HttpClientResponse is the response object of the HttpClient\n */\nexport class HttpClientResponse<ResponseType> {\n\tconstructor(\n\t\tpublic statusCode: number,\n\t\tpublic body: ResponseType,\n\t\tpublic headers: Headers,\n\t) {}\n}\n\ntype ShopwareErrorResponse = {\n\terrors: {\n\t\tcode: string;\n\t\tstatus: string;\n\t\ttitle: string;\n\t\tdetail: string;\n\t}[];\n};\n\n/**\n * ApiClientAuthenticationFailed is thrown when the authentication to the shop's API fails\n */\nexport class ApiClientAuthenticationFailed extends Error {\n\tconstructor(\n\t\tshopId: string,\n\t\tpublic response: HttpClientResponse<string>,\n\t) {\n\t\tsuper(\n\t\t\t`The api client authentication to shop with id: ${shopId} with response: ${JSON.stringify(response.body)}`,\n\t\t);\n\t}\n}\n\n/**\n * ApiClientRequestFailed is thrown when the request to the shop's API fails\n */\nexport class ApiClientRequestFailed extends Error {\n\tconstructor(\n\t\tshopId: string,\n\t\tpublic response: HttpClientResponse<ShopwareErrorResponse>,\n\t) {\n\t\tconst message = response.body.errors.map((e) => e.detail).join(\", \");\n\n\t\tsuper(`Request failed with error: ${message} for shop with id: ${shopId}`);\n\t}\n}\n"]}
@@ -10,15 +10,75 @@ declare module "hono" {
10
10
  }
11
11
  }
12
12
  interface MiddlewareConfig {
13
+ /**
14
+ * The name of the app
15
+ */
13
16
  appName: string | ((c: HonoContext) => string);
17
+ /**
18
+ * The secret of the app. When the app is published in the Shopware Store, the Shopware Store provides this value.
19
+ */
14
20
  appSecret: string | ((c: HonoContext) => string);
21
+ /**
22
+ * The URL of the app. This is the base URL of the app. This will automatically determined by default
23
+ */
15
24
  appUrl?: string | null;
25
+ /**
26
+ * The relative url of the app registration endpoint
27
+ *
28
+ * @default "/app/register"
29
+ */
16
30
  registrationUrl?: string | null;
31
+ /**
32
+ * The relative url of the app registration confirmation endpoint
33
+ *
34
+ * @default "/app/register/confirm"
35
+ */
17
36
  registerConfirmationUrl?: string | null;
37
+ /**
38
+ * The relative url of the app activation lifecycle endpoint
39
+ *
40
+ * @default "/app/activate"
41
+ */
18
42
  appActivateUrl?: string | null;
43
+ /**
44
+ * The relative url of the app deactivation lifecycle endpoint
45
+ *
46
+ * @default "/app/deactivate"
47
+ */
19
48
  appDeactivateUrl?: string | null;
49
+ /**
50
+ * The relative url of the app deletion lifecycle endpoint
51
+ *
52
+ * @default "/app/delete"
53
+ */
20
54
  appDeleteUrl?: string | null;
55
+ /**
56
+ * The relative url of the app scope. All requests matching this will be the signature automatically validated and the response will be signed
57
+ *
58
+ * @default "/app/*"
59
+ */
21
60
  appPath?: string | null;
61
+ /**
62
+ * Enable the app iframe integration. This will automatically set a cookie to identifiy the shopware shop and validate the request from a client side application. See appIframeRedirects
63
+ */
64
+ appIframeEnable?: boolean;
65
+ /**
66
+ * The relative url of the app iframe scope. All requests matching this will require that the request has an cookie set with the shopware shop. This cookie will be automatically set by
67
+ *
68
+ * @default "/client-api/*"
69
+ */
70
+ appIframePath?: string | null;
71
+ /**
72
+ * A mapping of the app iframe paths to the actual paths. This route will set a cookie automatically before the redirect to the actual path. In that way the client side application can send requests to /app-iframe/* with the cookie set and the server will automatically validate the request and knows which shop the request is for.
73
+ *
74
+ * @default {
75
+ * "/app/module": "https://my-static-client-side-app.com"
76
+ * }
77
+ */
78
+ appIframeRedirects?: Record<string, string>;
79
+ /**
80
+ * The repository to fetch and store the shop data
81
+ */
22
82
  shopRepository: ShopRepositoryInterface | ((c: HonoContext) => ShopRepositoryInterface);
23
83
  }
24
84
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../../src/integration/hono.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAE/E,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,MAAM,CAAC;AAEzD,OAAO,QAAQ,MAAM,CAAC;IACrB,UAAU,kBAAkB;QAE3B,GAAG,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC;QAC9B,IAAI,EAAE,aAAa,CAAC;QAEpB,OAAO,EAAE,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;KACzC;CACD;AAED,UAAU,gBAAgB;IACzB,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,MAAM,CAAC,CAAC;IAC/C,SAAS,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,MAAM,CAAC,CAAC;IACjD,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,cAAc,EACX,uBAAuB,GACvB,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,uBAAuB,CAAC,CAAC;CACjD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,gBAAgB,QAqHnE"}
1
+ {"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../../src/integration/hono.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAE/E,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,MAAM,CAAC;AAEzD,OAAO,QAAQ,MAAM,CAAC;IACrB,UAAU,kBAAkB;QAE3B,GAAG,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC;QAC9B,IAAI,EAAE,aAAa,CAAC;QAEpB,OAAO,EAAE,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;KACzC;CACD;AAED,UAAU,gBAAgB;IACzB;;OAEG;IACH,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,MAAM,CAAC,CAAC;IAC/C;;OAEG;IACH,SAAS,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,MAAM,CAAC,CAAC;IAEjD;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEvB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhC;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAExC;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE/B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEjC;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE7B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAExB;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE9B;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE5C;;OAEG;IACH,cAAc,EACX,uBAAuB,GACvB,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,uBAAuB,CAAC,CAAC;CACjD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,gBAAgB,QA2KnE"}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.configureAppServer = configureAppServer;
4
+ const cookie_1 = require("hono/cookie");
4
5
  const app_js_1 = require("../app.js");
5
6
  /**
6
7
  * Configure the Hono server to handle the app registration and context resolution
@@ -14,6 +15,7 @@ function configureAppServer(hono, cfg) {
14
15
  cfg.appDeactivateUrl = cfg.appDeactivateUrl || "/app/deactivate";
15
16
  cfg.appDeleteUrl = cfg.appDeleteUrl || "/app/delete";
16
17
  cfg.appPath = cfg.appPath || "/app/*";
18
+ cfg.appIframePath = cfg.appIframePath || "/client-api/*";
17
19
  hono.use("*", async (ctx, next) => {
18
20
  if (app === null) {
19
21
  const appUrl = cfg.appUrl || buildBaseUrl(ctx.req.url);
@@ -89,6 +91,38 @@ function configureAppServer(hono, cfg) {
89
91
  const app = ctx.get("app");
90
92
  return await app.registration.delete(ctx.req.raw);
91
93
  });
94
+ if (cfg.appIframeEnable) {
95
+ hono.use(cfg.appIframePath, async (ctx, next) => {
96
+ const shopId = await (0, cookie_1.getSignedCookie)(ctx, ctx.get("app").cfg.appSecret, "shop");
97
+ if (!shopId) {
98
+ return ctx.json({ message: "Shop not found" }, { status: 400 });
99
+ }
100
+ const shop = await ctx.get("app").repository.getShopById(shopId);
101
+ if (!shop) {
102
+ return ctx.json({ message: "Shop not found" }, { status: 400 });
103
+ }
104
+ ctx.set("shop", shop);
105
+ await next();
106
+ });
107
+ for (let [path, redirect] of Object.entries(cfg.appIframeRedirects || {})) {
108
+ hono.get(path, async (ctx) => {
109
+ const url = new URL(ctx.req.url);
110
+ if (redirect.startsWith("/")) {
111
+ url.pathname = redirect;
112
+ redirect = url.toString();
113
+ }
114
+ else {
115
+ const newUrl = new URL(redirect);
116
+ for (const [key, value] of url.searchParams) {
117
+ newUrl.searchParams.set(key, value);
118
+ }
119
+ redirect = newUrl.toString();
120
+ }
121
+ await (0, cookie_1.setSignedCookie)(ctx, "shop", ctx.get("shop").getShopId(), ctx.get("app").cfg.appSecret);
122
+ return ctx.redirect(redirect);
123
+ });
124
+ }
125
+ }
92
126
  }
93
127
  function jsonResponse(body, status = 200) {
94
128
  return new Response(JSON.stringify(body), {
@@ -1 +1 @@
1
- {"version":3,"file":"hono.js","sourceRoot":"","sources":["../../../src/integration/hono.ts"],"names":[],"mappings":"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","sourcesContent":["import { AppServer } from \"../app.js\";\nimport type { Context } from \"../context-resolver.js\";\nimport type { ShopInterface, ShopRepositoryInterface } from \"../repository.js\";\n\nimport type { Hono, Context as HonoContext } from \"hono\";\n\ndeclare module \"hono\" {\n\tinterface ContextVariableMap {\n\t\t// @ts-ignore\n\t\tapp: AppServer<ShopInterface>;\n\t\tshop: ShopInterface;\n\t\t// @ts-ignore\n\t\tcontext: Context<ShopInterface, unknown>;\n\t}\n}\n\ninterface MiddlewareConfig {\n\tappName: string | ((c: HonoContext) => string);\n\tappSecret: string | ((c: HonoContext) => string);\n\tappUrl?: string | null;\n\tregistrationUrl?: string | null;\n\tregisterConfirmationUrl?: string | null;\n\tappActivateUrl?: string | null;\n\tappDeactivateUrl?: string | null;\n\tappDeleteUrl?: string | null;\n\tappPath?: string | null;\n\tshopRepository:\n\t\t| ShopRepositoryInterface\n\t\t| ((c: HonoContext) => ShopRepositoryInterface);\n}\n\n/**\n * Configure the Hono server to handle the app registration and context resolution\n */\nexport function configureAppServer(hono: Hono, cfg: MiddlewareConfig) {\n\tlet app: AppServer | null = null;\n\n\tcfg.registrationUrl = cfg.registrationUrl || \"/app/register\";\n\tcfg.registerConfirmationUrl =\n\t\tcfg.registerConfirmationUrl || \"/app/register/confirm\";\n\tcfg.appActivateUrl = cfg.appActivateUrl || \"/app/activate\";\n\tcfg.appDeactivateUrl = cfg.appDeactivateUrl || \"/app/deactivate\";\n\tcfg.appDeleteUrl = cfg.appDeleteUrl || \"/app/delete\";\n\tcfg.appPath = cfg.appPath || \"/app/*\";\n\n\thono.use(\"*\", async (ctx, next) => {\n\t\tif (app === null) {\n\t\t\tconst appUrl = cfg.appUrl || buildBaseUrl(ctx.req.url);\n\n\t\t\tif (typeof cfg.shopRepository === \"function\") {\n\t\t\t\tcfg.shopRepository = cfg.shopRepository(ctx);\n\t\t\t}\n\n\t\t\tif (typeof cfg.appName === \"function\") {\n\t\t\t\tcfg.appName = cfg.appName(ctx);\n\t\t\t}\n\n\t\t\tif (typeof cfg.appSecret === \"function\") {\n\t\t\t\tcfg.appSecret = cfg.appSecret(ctx);\n\t\t\t}\n\n\t\t\tapp = new AppServer(\n\t\t\t\t{\n\t\t\t\t\tappName: cfg.appName,\n\t\t\t\t\tappSecret: cfg.appSecret,\n\t\t\t\t\tauthorizeCallbackUrl: appUrl + cfg.registerConfirmationUrl,\n\t\t\t\t},\n\t\t\t\tcfg.shopRepository,\n\t\t\t);\n\t\t}\n\n\t\t// @ts-ignore\n\t\tctx.set(\"app\", app);\n\n\t\tawait next();\n\t});\n\n\thono.use(cfg.appPath, async (ctx, next) => {\n\t\t// @ts-ignore\n\t\tconst app = ctx.get(\"app\") as AppServer;\n\n\t\t// Don't validate signature for registration\n\t\tif (\n\t\t\tctx.req.path === cfg.registrationUrl ||\n\t\t\tctx.req.path === cfg.registerConfirmationUrl ||\n\t\t\tctx.req.path === cfg.appActivateUrl ||\n\t\t\tctx.req.path === cfg.appDeactivateUrl ||\n\t\t\tctx.req.path === cfg.appDeleteUrl\n\t\t) {\n\t\t\tawait next();\n\t\t\treturn;\n\t\t}\n\n\t\tlet context: Context<ShopInterface, unknown>;\n\t\ttry {\n\t\t\tcontext =\n\t\t\t\tctx.req.method === \"GET\"\n\t\t\t\t\t? await app.contextResolver.fromBrowser(ctx.req.raw)\n\t\t\t\t\t: await app.contextResolver.fromAPI(ctx.req.raw);\n\t\t} catch (_e) {\n\t\t\treturn jsonResponse({ message: \"Invalid request\" }, 400);\n\t\t}\n\n\t\t// @ts-ignore\n\t\tctx.set(\"shop\", context.shop);\n\t\t// @ts-ignore\n\t\tctx.set(\"context\", context);\n\n\t\tawait next();\n\n\t\tconst cloned = ctx.res.clone();\n\n\t\tawait ctx\n\t\t\t.get(\"app\")\n\t\t\t.signer.signResponse(cloned, ctx.get(\"shop\").getShopSecret());\n\n\t\tctx.header(\n\t\t\t\"shopware-app-signature\",\n\t\t\tcloned.headers.get(\"shopware-app-signature\") as string,\n\t\t);\n\t});\n\n\thono.get(cfg.registrationUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.authorize(ctx.req.raw);\n\t});\n\n\thono.post(cfg.registerConfirmationUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.authorizeCallback(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appActivateUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.activate(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appDeactivateUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.deactivate(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appDeleteUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.delete(ctx.req.raw);\n\t});\n}\n\nfunction jsonResponse(body: object, status = 200): Response {\n\treturn new Response(JSON.stringify(body), {\n\t\tstatus,\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t},\n\t});\n}\n\nfunction buildBaseUrl(url: string): string {\n\tconst u = new URL(url);\n\n\treturn `${u.protocol}//${u.host}`;\n}\n"]}
1
+ {"version":3,"file":"hono.js","sourceRoot":"","sources":["../../../src/integration/hono.ts"],"names":[],"mappings":"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","sourcesContent":["import { getSignedCookie, setSignedCookie } from \"hono/cookie\";\nimport { AppServer } from \"../app.js\";\nimport type { Context } from \"../context-resolver.js\";\nimport type { ShopInterface, ShopRepositoryInterface } from \"../repository.js\";\n\nimport type { Hono, Context as HonoContext } from \"hono\";\n\ndeclare module \"hono\" {\n\tinterface ContextVariableMap {\n\t\t// @ts-ignore\n\t\tapp: AppServer<ShopInterface>;\n\t\tshop: ShopInterface;\n\t\t// @ts-ignore\n\t\tcontext: Context<ShopInterface, unknown>;\n\t}\n}\n\ninterface MiddlewareConfig {\n\t/**\n\t * The name of the app\n\t */\n\tappName: string | ((c: HonoContext) => string);\n\t/**\n\t * The secret of the app. When the app is published in the Shopware Store, the Shopware Store provides this value.\n\t */\n\tappSecret: string | ((c: HonoContext) => string);\n\n\t/**\n\t * The URL of the app. This is the base URL of the app. This will automatically determined by default\n\t */\n\tappUrl?: string | null;\n\n\t/**\n\t * The relative url of the app registration endpoint\n\t *\n\t * @default \"/app/register\"\n\t */\n\tregistrationUrl?: string | null;\n\n\t/**\n\t * The relative url of the app registration confirmation endpoint\n\t *\n\t * @default \"/app/register/confirm\"\n\t */\n\tregisterConfirmationUrl?: string | null;\n\n\t/**\n\t * The relative url of the app activation lifecycle endpoint\n\t *\n\t * @default \"/app/activate\"\n\t */\n\tappActivateUrl?: string | null;\n\n\t/**\n\t * The relative url of the app deactivation lifecycle endpoint\n\t *\n\t * @default \"/app/deactivate\"\n\t */\n\tappDeactivateUrl?: string | null;\n\n\t/**\n\t * The relative url of the app deletion lifecycle endpoint\n\t *\n\t * @default \"/app/delete\"\n\t */\n\tappDeleteUrl?: string | null;\n\n\t/**\n\t * The relative url of the app scope. All requests matching this will be the signature automatically validated and the response will be signed\n\t *\n\t * @default \"/app/*\"\n\t */\n\tappPath?: string | null;\n\n\t/**\n\t * Enable the app iframe integration. This will automatically set a cookie to identifiy the shopware shop and validate the request from a client side application. See appIframeRedirects\n\t */\n\tappIframeEnable?: boolean;\n\n\t/**\n\t * The relative url of the app iframe scope. All requests matching this will require that the request has an cookie set with the shopware shop. This cookie will be automatically set by\n\t *\n\t * @default \"/client-api/*\"\n\t */\n\tappIframePath?: string | null;\n\n\t/**\n\t * A mapping of the app iframe paths to the actual paths. This route will set a cookie automatically before the redirect to the actual path. In that way the client side application can send requests to /app-iframe/* with the cookie set and the server will automatically validate the request and knows which shop the request is for.\n\t *\n\t * @default {\n\t * \"/app/module\": \"https://my-static-client-side-app.com\"\n\t * }\n\t */\n\tappIframeRedirects?: Record<string, string>;\n\n\t/**\n\t * The repository to fetch and store the shop data\n\t */\n\tshopRepository:\n\t\t| ShopRepositoryInterface\n\t\t| ((c: HonoContext) => ShopRepositoryInterface);\n}\n\n/**\n * Configure the Hono server to handle the app registration and context resolution\n */\nexport function configureAppServer(hono: Hono, cfg: MiddlewareConfig) {\n\tlet app: AppServer | null = null;\n\n\tcfg.registrationUrl = cfg.registrationUrl || \"/app/register\";\n\tcfg.registerConfirmationUrl =\n\t\tcfg.registerConfirmationUrl || \"/app/register/confirm\";\n\tcfg.appActivateUrl = cfg.appActivateUrl || \"/app/activate\";\n\tcfg.appDeactivateUrl = cfg.appDeactivateUrl || \"/app/deactivate\";\n\tcfg.appDeleteUrl = cfg.appDeleteUrl || \"/app/delete\";\n\tcfg.appPath = cfg.appPath || \"/app/*\";\n\n\tcfg.appIframePath = cfg.appIframePath || \"/client-api/*\";\n\n\thono.use(\"*\", async (ctx, next) => {\n\t\tif (app === null) {\n\t\t\tconst appUrl = cfg.appUrl || buildBaseUrl(ctx.req.url);\n\n\t\t\tif (typeof cfg.shopRepository === \"function\") {\n\t\t\t\tcfg.shopRepository = cfg.shopRepository(ctx);\n\t\t\t}\n\n\t\t\tif (typeof cfg.appName === \"function\") {\n\t\t\t\tcfg.appName = cfg.appName(ctx);\n\t\t\t}\n\n\t\t\tif (typeof cfg.appSecret === \"function\") {\n\t\t\t\tcfg.appSecret = cfg.appSecret(ctx);\n\t\t\t}\n\n\t\t\tapp = new AppServer(\n\t\t\t\t{\n\t\t\t\t\tappName: cfg.appName,\n\t\t\t\t\tappSecret: cfg.appSecret,\n\t\t\t\t\tauthorizeCallbackUrl: appUrl + cfg.registerConfirmationUrl,\n\t\t\t\t},\n\t\t\t\tcfg.shopRepository,\n\t\t\t);\n\t\t}\n\n\t\t// @ts-ignore\n\t\tctx.set(\"app\", app);\n\n\t\tawait next();\n\t});\n\n\thono.use(cfg.appPath, async (ctx, next) => {\n\t\t// @ts-ignore\n\t\tconst app = ctx.get(\"app\") as AppServer;\n\n\t\t// Don't validate signature for registration\n\t\tif (\n\t\t\tctx.req.path === cfg.registrationUrl ||\n\t\t\tctx.req.path === cfg.registerConfirmationUrl ||\n\t\t\tctx.req.path === cfg.appActivateUrl ||\n\t\t\tctx.req.path === cfg.appDeactivateUrl ||\n\t\t\tctx.req.path === cfg.appDeleteUrl\n\t\t) {\n\t\t\tawait next();\n\t\t\treturn;\n\t\t}\n\n\t\tlet context: Context<ShopInterface, unknown>;\n\t\ttry {\n\t\t\tcontext =\n\t\t\t\tctx.req.method === \"GET\"\n\t\t\t\t\t? await app.contextResolver.fromBrowser(ctx.req.raw)\n\t\t\t\t\t: await app.contextResolver.fromAPI(ctx.req.raw);\n\t\t} catch (_e) {\n\t\t\treturn jsonResponse({ message: \"Invalid request\" }, 400);\n\t\t}\n\n\t\t// @ts-ignore\n\t\tctx.set(\"shop\", context.shop);\n\t\t// @ts-ignore\n\t\tctx.set(\"context\", context);\n\n\t\tawait next();\n\n\t\tconst cloned = ctx.res.clone();\n\n\t\tawait ctx\n\t\t\t.get(\"app\")\n\t\t\t.signer.signResponse(cloned, ctx.get(\"shop\").getShopSecret());\n\n\t\tctx.header(\n\t\t\t\"shopware-app-signature\",\n\t\t\tcloned.headers.get(\"shopware-app-signature\") as string,\n\t\t);\n\t});\n\n\thono.get(cfg.registrationUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.authorize(ctx.req.raw);\n\t});\n\n\thono.post(cfg.registerConfirmationUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.authorizeCallback(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appActivateUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.activate(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appDeactivateUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.deactivate(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appDeleteUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.delete(ctx.req.raw);\n\t});\n\n\tif (cfg.appIframeEnable) {\n\t\thono.use(cfg.appIframePath, async (ctx, next) => {\n\t\t\tconst shopId = await getSignedCookie(\n\t\t\t\tctx,\n\t\t\t\tctx.get(\"app\").cfg.appSecret,\n\t\t\t\t\"shop\",\n\t\t\t);\n\n\t\t\tif (!shopId) {\n\t\t\t\treturn ctx.json({ message: \"Shop not found\" }, { status: 400 });\n\t\t\t}\n\n\t\t\tconst shop = await ctx.get(\"app\").repository.getShopById(shopId);\n\n\t\t\tif (!shop) {\n\t\t\t\treturn ctx.json({ message: \"Shop not found\" }, { status: 400 });\n\t\t\t}\n\n\t\t\tctx.set(\"shop\", shop);\n\n\t\t\tawait next();\n\t\t});\n\n\t\tfor (let [path, redirect] of Object.entries(cfg.appIframeRedirects || {})) {\n\t\t\thono.get(path, async (ctx) => {\n\t\t\t\tconst url = new URL(ctx.req.url);\n\n\t\t\t\tif (redirect.startsWith(\"/\")) {\n\t\t\t\t\turl.pathname = redirect;\n\t\t\t\t\tredirect = url.toString();\n\t\t\t\t} else {\n\t\t\t\t\tconst newUrl = new URL(redirect);\n\n\t\t\t\t\tfor (const [key, value] of url.searchParams) {\n\t\t\t\t\t\tnewUrl.searchParams.set(key, value);\n\t\t\t\t\t}\n\n\t\t\t\t\tredirect = newUrl.toString();\n\t\t\t\t}\n\n\t\t\t\tawait setSignedCookie(\n\t\t\t\t\tctx,\n\t\t\t\t\t\"shop\",\n\t\t\t\t\tctx.get(\"shop\").getShopId(),\n\t\t\t\t\tctx.get(\"app\").cfg.appSecret,\n\t\t\t\t);\n\n\t\t\t\treturn ctx.redirect(redirect);\n\t\t\t});\n\t\t}\n\t}\n}\n\nfunction jsonResponse(body: object, status = 200): Response {\n\treturn new Response(JSON.stringify(body), {\n\t\tstatus,\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t},\n\t});\n}\n\nfunction buildBaseUrl(url: string): string {\n\tconst u = new URL(url);\n\n\treturn `${u.protocol}//${u.host}`;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"context-resolver.d.ts","sourceRoot":"","sources":["../../src/context-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD;;;GAGG;AACH,qBAAa,eAAe,CAAC,IAAI,SAAS,aAAa,GAAG,aAAa;IAC1D,OAAO,CAAC,GAAG;gBAAH,GAAG,EAAE,SAAS;IAElC;;OAEG;IACU,OAAO,CAAC,OAAO,GAAG,OAAO,EACrC,GAAG,EAAE,OAAO,GACV,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAmClC;;;OAGG;IACU,WAAW,CAAC,OAAO,GAAG,OAAO,EACzC,GAAG,EAAE,OAAO,GACV,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;CA6BlC;AAED;;GAEG;AACH,qBAAa,OAAO,CACnB,IAAI,SAAS,aAAa,GAAG,aAAa,EAC1C,OAAO,GAAG,OAAO;IAGT,IAAI,EAAE,IAAI;IACV,OAAO,EAAE,OAAO;IAChB,UAAU,EAAE,UAAU;gBAFtB,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,UAAU;CAE9B"}
1
+ {"version":3,"file":"context-resolver.d.ts","sourceRoot":"","sources":["../../src/context-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD;;;GAGG;AACH,qBAAa,eAAe,CAAC,IAAI,SAAS,aAAa,GAAG,aAAa;IAC1D,OAAO,CAAC,GAAG;gBAAH,GAAG,EAAE,SAAS;IAElC;;OAEG;IACU,OAAO,CAAC,OAAO,GAAG,OAAO,EACrC,GAAG,EAAE,OAAO,GACV,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IA0ClC;;;OAGG;IACU,WAAW,CAAC,OAAO,GAAG,OAAO,EACzC,GAAG,EAAE,OAAO,GACV,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;CA6BlC;AAED;;GAEG;AACH,qBAAa,OAAO,CACnB,IAAI,SAAS,aAAa,GAAG,aAAa,EAC1C,OAAO,GAAG,OAAO;IAGT,IAAI,EAAE,IAAI;IACV,OAAO,EAAE,OAAO;IAChB,UAAU,EAAE,UAAU;gBAFtB,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,UAAU;CAE9B"}
@@ -14,6 +14,10 @@ export class ContextResolver {
14
14
  async fromAPI(req) {
15
15
  const webHookContent = await req.text();
16
16
  const webHookBody = JSON.parse(webHookContent);
17
+ if (webHookBody.source === undefined ||
18
+ webHookBody.source.shopId === undefined) {
19
+ throw new Error("Invalid request");
20
+ }
17
21
  const shop = await this.app.repository.getShopById(webHookBody.source.shopId);
18
22
  if (shop === null) {
19
23
  throw new Error(`Cannot find shop by id ${webHookBody.source.shopId}`);
@@ -1 +1 @@
1
- {"version":3,"file":"context-resolver.js","sourceRoot":"","sources":["../../src/context-resolver.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG9C;;;GAGG;AACH,MAAM,OAAO,eAAe;IACP;IAApB,YAAoB,GAAc;QAAd,QAAG,GAAH,GAAG,CAAW;IAAG,CAAC;IAEtC;;OAEG;IACI,KAAK,CAAC,OAAO,CACnB,GAAY;QAEZ,MAAM,cAAc,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QACxC,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAE/C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,WAAW,CACjD,WAAW,CAAC,MAAM,CAAC,MAAM,CACzB,CAAC;QAEF,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,0BAA0B,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QACxE,CAAC;QAED,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;QAE7D,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAC3D,CAAC;QAED,IACC,CAAC,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAC7B,SAAS,EACT,cAAc,EACd,IAAI,CAAC,aAAa,EAAE,CACpB,CAAC,EACD,CAAC;YACF,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACtC,CAAC;QAED,OAAO,IAAI,OAAO,CACjB,IAAY,EACZ,WAAW,EACX,IAAI,UAAU,CAAC,IAAI,CAAC,CACpB,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,WAAW,CACvB,GAAY;QAEZ,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAE7B,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAE/C,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACpD,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAE3D,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,0BAA0B,MAAM,EAAE,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;QAElE,MAAM,YAAY,GAA2B,EAAE,CAAC;QAEhD,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YACvC,YAAY,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAC3B,CAAC,CAAC,CAAC;QAEH,OAAO,IAAI,OAAO,CACjB,IAAY,EACZ,YAAuB,EACvB,IAAI,UAAU,CAAC,IAAI,CAAC,CACpB,CAAC;IACH,CAAC;CACD;AAED;;GAEG;AACH,MAAM,OAAO,OAAO;IAKX;IACA;IACA;IAHR,YACQ,IAAU,EACV,OAAgB,EAChB,UAAsB;QAFtB,SAAI,GAAJ,IAAI,CAAM;QACV,YAAO,GAAP,OAAO,CAAS;QAChB,eAAU,GAAV,UAAU,CAAY;IAC3B,CAAC;CACJ","sourcesContent":["import type { AppServer } from \"./app.js\";\nimport { HttpClient } from \"./http-client.js\";\nimport type { ShopInterface } from \"./repository.js\";\n\n/**\n * ContextResolver is a helper class to create a Context object from a request.\n * The context contains the shop, the payload and an instance of the HttpClient\n */\nexport class ContextResolver<Shop extends ShopInterface = ShopInterface> {\n\tconstructor(private app: AppServer) {}\n\n\t/**\n\t * Create a context from a request body\n\t */\n\tpublic async fromAPI<Payload = unknown>(\n\t\treq: Request,\n\t): Promise<Context<Shop, Payload>> {\n\t\tconst webHookContent = await req.text();\n\t\tconst webHookBody = JSON.parse(webHookContent);\n\n\t\tconst shop = await this.app.repository.getShopById(\n\t\t\twebHookBody.source.shopId,\n\t\t);\n\n\t\tif (shop === null) {\n\t\t\tthrow new Error(`Cannot find shop by id ${webHookBody.source.shopId}`);\n\t\t}\n\n\t\tconst signature = req.headers.get(\"shopware-shop-signature\");\n\n\t\tif (signature === null) {\n\t\t\tthrow new Error(\"Missing shopware-shop-signature header\");\n\t\t}\n\n\t\tif (\n\t\t\t!(await this.app.signer.verify(\n\t\t\t\tsignature,\n\t\t\t\twebHookContent,\n\t\t\t\tshop.getShopSecret(),\n\t\t\t))\n\t\t) {\n\t\t\tthrow new Error(\"Invalid signature\");\n\t\t}\n\n\t\treturn new Context<Shop, Payload>(\n\t\t\tshop as Shop,\n\t\t\twebHookBody,\n\t\t\tnew HttpClient(shop),\n\t\t);\n\t}\n\n\t/**\n\t * Create a context from a request query parameters\n\t * This is usually a module request from the shopware admin\n\t */\n\tpublic async fromBrowser<Payload = unknown>(\n\t\treq: Request,\n\t): Promise<Context<Shop, Payload>> {\n\t\tconst url = new URL(req.url);\n\n\t\tconst shopId = url.searchParams.get(\"shop-id\");\n\n\t\tif (shopId === null) {\n\t\t\tthrow new Error(\"Missing shop-id query parameter\");\n\t\t}\n\n\t\tconst shop = await this.app.repository.getShopById(shopId);\n\n\t\tif (shop === null) {\n\t\t\tthrow new Error(`Cannot find shop by id ${shopId}`);\n\t\t}\n\n\t\tawait this.app.signer.verifyGetRequest(req, shop.getShopSecret());\n\n\t\tconst paramsObject: Record<string, string> = {};\n\n\t\turl.searchParams.forEach((value, key) => {\n\t\t\tparamsObject[key] = value;\n\t\t});\n\n\t\treturn new Context<Shop, Payload>(\n\t\t\tshop as Shop,\n\t\t\tparamsObject as Payload,\n\t\t\tnew HttpClient(shop),\n\t\t);\n\t}\n}\n\n/**\n * Context is the parsed data from the request\n */\nexport class Context<\n\tShop extends ShopInterface = ShopInterface,\n\tPayload = unknown,\n> {\n\tconstructor(\n\t\tpublic shop: Shop,\n\t\tpublic payload: Payload,\n\t\tpublic httpClient: HttpClient,\n\t) {}\n}\n"]}
1
+ {"version":3,"file":"context-resolver.js","sourceRoot":"","sources":["../../src/context-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AppServer } from \"./app.js\";\nimport { HttpClient } from \"./http-client.js\";\nimport type { ShopInterface } from \"./repository.js\";\n\n/**\n * ContextResolver is a helper class to create a Context object from a request.\n * The context contains the shop, the payload and an instance of the HttpClient\n */\nexport class ContextResolver<Shop extends ShopInterface = ShopInterface> {\n\tconstructor(private app: AppServer) {}\n\n\t/**\n\t * Create a context from a request body\n\t */\n\tpublic async fromAPI<Payload = unknown>(\n\t\treq: Request,\n\t): Promise<Context<Shop, Payload>> {\n\t\tconst webHookContent = await req.text();\n\t\tconst webHookBody = JSON.parse(webHookContent);\n\n\t\tif (\n\t\t\twebHookBody.source === undefined ||\n\t\t\twebHookBody.source.shopId === undefined\n\t\t) {\n\t\t\tthrow new Error(\"Invalid request\");\n\t\t}\n\n\t\tconst shop = await this.app.repository.getShopById(\n\t\t\twebHookBody.source.shopId,\n\t\t);\n\n\t\tif (shop === null) {\n\t\t\tthrow new Error(`Cannot find shop by id ${webHookBody.source.shopId}`);\n\t\t}\n\n\t\tconst signature = req.headers.get(\"shopware-shop-signature\");\n\n\t\tif (signature === null) {\n\t\t\tthrow new Error(\"Missing shopware-shop-signature header\");\n\t\t}\n\n\t\tif (\n\t\t\t!(await this.app.signer.verify(\n\t\t\t\tsignature,\n\t\t\t\twebHookContent,\n\t\t\t\tshop.getShopSecret(),\n\t\t\t))\n\t\t) {\n\t\t\tthrow new Error(\"Invalid signature\");\n\t\t}\n\n\t\treturn new Context<Shop, Payload>(\n\t\t\tshop as Shop,\n\t\t\twebHookBody,\n\t\t\tnew HttpClient(shop),\n\t\t);\n\t}\n\n\t/**\n\t * Create a context from a request query parameters\n\t * This is usually a module request from the shopware admin\n\t */\n\tpublic async fromBrowser<Payload = unknown>(\n\t\treq: Request,\n\t): Promise<Context<Shop, Payload>> {\n\t\tconst url = new URL(req.url);\n\n\t\tconst shopId = url.searchParams.get(\"shop-id\");\n\n\t\tif (shopId === null) {\n\t\t\tthrow new Error(\"Missing shop-id query parameter\");\n\t\t}\n\n\t\tconst shop = await this.app.repository.getShopById(shopId);\n\n\t\tif (shop === null) {\n\t\t\tthrow new Error(`Cannot find shop by id ${shopId}`);\n\t\t}\n\n\t\tawait this.app.signer.verifyGetRequest(req, shop.getShopSecret());\n\n\t\tconst paramsObject: Record<string, string> = {};\n\n\t\turl.searchParams.forEach((value, key) => {\n\t\t\tparamsObject[key] = value;\n\t\t});\n\n\t\treturn new Context<Shop, Payload>(\n\t\t\tshop as Shop,\n\t\t\tparamsObject as Payload,\n\t\t\tnew HttpClient(shop),\n\t\t);\n\t}\n}\n\n/**\n * Context is the parsed data from the request\n */\nexport class Context<\n\tShop extends ShopInterface = ShopInterface,\n\tPayload = unknown,\n> {\n\tconstructor(\n\t\tpublic shop: Shop,\n\t\tpublic payload: Payload,\n\t\tpublic httpClient: HttpClient,\n\t) {}\n}\n"]}
@@ -13,7 +13,7 @@ export declare class HttpClient {
13
13
  /**
14
14
  * Permform a POST request
15
15
  */
16
- post<ResponseType>(url: string, json?: object, headers?: Record<string, string>): Promise<HttpClientResponse<ResponseType>>;
16
+ post<ResponseType>(url: string, json?: object | FormData | Blob, headers?: Record<string, string>): Promise<HttpClientResponse<ResponseType>>;
17
17
  /**
18
18
  * Permform a PUT request
19
19
  */
@@ -1 +1 @@
1
- {"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../../src/http-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD;;GAEG;AACH,qBAAa,UAAU;IAGV,OAAO,CAAC,IAAI;IAFxB,OAAO,CAAC,OAAO,CAAmD;gBAE9C,IAAI,EAAE,aAAa;IAOvC;;OAEG;IACG,GAAG,CAAC,YAAY,EACrB,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAClC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAI5C;;OAEG;IACG,IAAI,CAAC,YAAY,EACtB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,MAAW,EACjB,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAClC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAO5C;;OAEG;IACG,GAAG,CAAC,YAAY,EACrB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,MAAW,EACjB,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAClC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAO5C;;OAEG;IACG,KAAK,CAAC,YAAY,EACvB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,MAAW,EACjB,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAClC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAO5C;;OAEG;IACG,MAAM,CAAC,YAAY,EACxB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,MAAW,EACjB,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAClC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;YAO9B,OAAO;IAyCrB;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;CAuDjC;AAED;;GAEG;AACH,qBAAa,kBAAkB,CAAC,YAAY;IAEnC,UAAU,EAAE,MAAM;IAClB,IAAI,EAAE,YAAY;IAClB,OAAO,EAAE,OAAO;gBAFhB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE,OAAO;CAExB;AAED,KAAK,qBAAqB,GAAG;IAC5B,MAAM,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KACf,EAAE,CAAC;CACJ,CAAC;AAEF;;GAEG;AACH,qBAAa,6BAA8B,SAAQ,KAAK;IAG/C,QAAQ,EAAE,kBAAkB,CAAC,MAAM,CAAC;gBAD3C,MAAM,EAAE,MAAM,EACP,QAAQ,EAAE,kBAAkB,CAAC,MAAM,CAAC;CAM5C;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;IAGxC,QAAQ,EAAE,kBAAkB,CAAC,qBAAqB,CAAC;gBAD1D,MAAM,EAAE,MAAM,EACP,QAAQ,EAAE,kBAAkB,CAAC,qBAAqB,CAAC;CAM3D"}
1
+ {"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../../src/http-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD;;GAEG;AACH,qBAAa,UAAU;IAGV,OAAO,CAAC,IAAI;IAFxB,OAAO,CAAC,OAAO,CAAmD;gBAE9C,IAAI,EAAE,aAAa;IAOvC;;OAEG;IACG,GAAG,CAAC,YAAY,EACrB,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAClC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAI5C;;OAEG;IACG,IAAI,CAAC,YAAY,EACtB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,MAAM,GAAG,QAAQ,GAAG,IAAS,EACnC,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAClC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAkB5C;;OAEG;IACG,GAAG,CAAC,YAAY,EACrB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,MAAW,EACjB,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAClC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAO5C;;OAEG;IACG,KAAK,CAAC,YAAY,EACvB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,MAAW,EACjB,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAClC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAO5C;;OAEG;IACG,MAAM,CAAC,YAAY,EACxB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,MAAW,EACjB,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAClC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;YAO9B,OAAO;IAyCrB;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;CAuDjC;AAED;;GAEG;AACH,qBAAa,kBAAkB,CAAC,YAAY;IAEnC,UAAU,EAAE,MAAM;IAClB,IAAI,EAAE,YAAY;IAClB,OAAO,EAAE,OAAO;gBAFhB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE,OAAO;CAExB;AAED,KAAK,qBAAqB,GAAG;IAC5B,MAAM,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KACf,EAAE,CAAC;CACJ,CAAC;AAEF;;GAEG;AACH,qBAAa,6BAA8B,SAAQ,KAAK;IAG/C,QAAQ,EAAE,kBAAkB,CAAC,MAAM,CAAC;gBAD3C,MAAM,EAAE,MAAM,EACP,QAAQ,EAAE,kBAAkB,CAAC,MAAM,CAAC;CAM5C;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;IAGxC,QAAQ,EAAE,kBAAkB,CAAC,qBAAqB,CAAC;gBAD1D,MAAM,EAAE,MAAM,EACP,QAAQ,EAAE,kBAAkB,CAAC,qBAAqB,CAAC;CAM3D"}
@@ -21,9 +21,13 @@ export class HttpClient {
21
21
  * Permform a POST request
22
22
  */
23
23
  async post(url, json = {}, headers = {}) {
24
- headers["content-type"] = "application/json";
24
+ let data = json;
25
+ if (!(json instanceof Blob) && !(json instanceof FormData)) {
26
+ headers["content-type"] = "application/json";
27
+ data = JSON.stringify(json);
28
+ }
25
29
  headers.accept = "application/json";
26
- return await this.request("POST", url, JSON.stringify(json), headers);
30
+ return await this.request("POST", url, data, headers);
27
31
  }
28
32
  /**
29
33
  * Permform a PUT request
@@ -1 +1 @@
1
- {"version":3,"file":"http-client.js","sourceRoot":"","sources":["../../src/http-client.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ShopInterface } from \"./repository.js\";\n\n/**\n * HttpClient is a simple wrapper around the fetch API, pre-configured with the shop's URL and access token\n */\nexport class HttpClient {\n\tprivate storage: { expiresIn: Date | null; token: string | null };\n\n\tconstructor(private shop: ShopInterface) {\n\t\tthis.storage = {\n\t\t\ttoken: null,\n\t\t\texpiresIn: null,\n\t\t};\n\t}\n\n\t/**\n\t * Permform a GET request\n\t */\n\tasync get<ResponseType>(\n\t\turl: string,\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\treturn await this.request(\"GET\", url, null, headers);\n\t}\n\n\t/**\n\t * Permform a POST request\n\t */\n\tasync post<ResponseType>(\n\t\turl: string,\n\t\tjson: object = {},\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\theaders[\"content-type\"] = \"application/json\";\n\t\theaders.accept = \"application/json\";\n\n\t\treturn await this.request(\"POST\", url, JSON.stringify(json), headers);\n\t}\n\n\t/**\n\t * Permform a PUT request\n\t */\n\tasync put<ResponseType>(\n\t\turl: string,\n\t\tjson: object = {},\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\theaders[\"content-type\"] = \"application/json\";\n\t\theaders.accept = \"application/json\";\n\n\t\treturn await this.request(\"PUT\", url, JSON.stringify(json), headers);\n\t}\n\n\t/**\n\t * Permform a PATCH request\n\t */\n\tasync patch<ResponseType>(\n\t\turl: string,\n\t\tjson: object = {},\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\theaders[\"content-type\"] = \"application/json\";\n\t\theaders.accept = \"application/json\";\n\n\t\treturn await this.request(\"PATCH\", url, JSON.stringify(json), headers);\n\t}\n\n\t/**\n\t * Permform a DELETE request\n\t */\n\tasync delete<ResponseType>(\n\t\turl: string,\n\t\tjson: object = {},\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\theaders[\"content-type\"] = \"application/json\";\n\t\theaders.accept = \"application/json\";\n\n\t\treturn await this.request(\"DELETE\", url, JSON.stringify(json), headers);\n\t}\n\n\tprivate async request<ResponseType>(\n\t\tmethod: string,\n\t\turl: string,\n\t\tbody: string | null = \"\",\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\tconst f = await globalThis.fetch(`${this.shop.getShopUrl()}/api${url}`, {\n\t\t\tbody,\n\t\t\theaders: Object.assign(\n\t\t\t\t{\n\t\t\t\t\tAuthorization: `Bearer ${await this.getToken()}`,\n\t\t\t\t},\n\t\t\t\theaders,\n\t\t\t),\n\t\t\tmethod,\n\t\t});\n\n\t\t// Obtain new token\n\t\tif (!f.ok && f.status === 401) {\n\t\t\tthis.storage.expiresIn = null;\n\n\t\t\treturn await this.request(method, url, body, headers);\n\t\t}\n\t\tif (!f.ok) {\n\t\t\tthrow new ApiClientRequestFailed(\n\t\t\t\tthis.shop.getShopId(),\n\t\t\t\tnew HttpClientResponse(f.status, await f.json(), f.headers),\n\t\t\t);\n\t\t}\n\n\t\tif (f.status === 204) {\n\t\t\treturn new HttpClientResponse<ResponseType>(\n\t\t\t\tf.status,\n\t\t\t\t{} as ResponseType,\n\t\t\t\tf.headers,\n\t\t\t);\n\t\t}\n\n\t\treturn new HttpClientResponse(f.status, await f.json(), f.headers);\n\t}\n\n\t/**\n\t * Obtain a valid bearer token\n\t */\n\tasync getToken(): Promise<string> {\n\t\tif (this.storage.expiresIn === null) {\n\t\t\tconst auth = await globalThis.fetch(\n\t\t\t\t`${this.shop.getShopUrl()}/api/oauth/token`,\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t\t\t},\n\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\tgrant_type: \"client_credentials\",\n\t\t\t\t\t\tclient_id: this.shop.getShopClientId(),\n\t\t\t\t\t\tclient_secret: this.shop.getShopClientSecret(),\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tif (!auth.ok) {\n\t\t\t\tconst contentType = auth.headers.get(\"content-type\") || \"text/plain\";\n\t\t\t\tlet body = \"\";\n\n\t\t\t\tif (contentType.indexOf(\"application/json\") !== -1) {\n\t\t\t\t\tbody = await auth.json();\n\t\t\t\t} else {\n\t\t\t\t\tbody = await auth.text();\n\t\t\t\t}\n\n\t\t\t\tthrow new ApiClientAuthenticationFailed(\n\t\t\t\t\tthis.shop.getShopId(),\n\t\t\t\t\tnew HttpClientResponse<string>(auth.status, body, auth.headers),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst expireDate = new Date();\n\t\t\tconst authBody = (await auth.json()) as {\n\t\t\t\taccess_token: string;\n\t\t\t\texpires_in: number;\n\t\t\t};\n\t\t\tthis.storage.token = authBody.access_token;\n\t\t\texpireDate.setSeconds(expireDate.getSeconds() + authBody.expires_in);\n\t\t\tthis.storage.expiresIn = expireDate;\n\n\t\t\treturn this.storage.token as string;\n\t\t}\n\n\t\tif (this.storage.expiresIn.getTime() < new Date().getTime()) {\n\t\t\t// Expired\n\n\t\t\tthis.storage.expiresIn = null;\n\n\t\t\treturn await this.getToken();\n\t\t}\n\n\t\treturn this.storage.token as string;\n\t}\n}\n\n/**\n * HttpClientResponse is the response object of the HttpClient\n */\nexport class HttpClientResponse<ResponseType> {\n\tconstructor(\n\t\tpublic statusCode: number,\n\t\tpublic body: ResponseType,\n\t\tpublic headers: Headers,\n\t) {}\n}\n\ntype ShopwareErrorResponse = {\n\terrors: {\n\t\tcode: string;\n\t\tstatus: string;\n\t\ttitle: string;\n\t\tdetail: string;\n\t}[];\n};\n\n/**\n * ApiClientAuthenticationFailed is thrown when the authentication to the shop's API fails\n */\nexport class ApiClientAuthenticationFailed extends Error {\n\tconstructor(\n\t\tshopId: string,\n\t\tpublic response: HttpClientResponse<string>,\n\t) {\n\t\tsuper(\n\t\t\t`The api client authentication to shop with id: ${shopId} with response: ${JSON.stringify(response.body)}`,\n\t\t);\n\t}\n}\n\n/**\n * ApiClientRequestFailed is thrown when the request to the shop's API fails\n */\nexport class ApiClientRequestFailed extends Error {\n\tconstructor(\n\t\tshopId: string,\n\t\tpublic response: HttpClientResponse<ShopwareErrorResponse>,\n\t) {\n\t\tconst message = response.body.errors.map((e) => e.detail).join(\", \");\n\n\t\tsuper(`Request failed with error: ${message} for shop with id: ${shopId}`);\n\t}\n}\n"]}
1
+ {"version":3,"file":"http-client.js","sourceRoot":"","sources":["../../src/http-client.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,OAAO,UAAU;IAGF;IAFZ,OAAO,CAAmD;IAElE,YAAoB,IAAmB;QAAnB,SAAI,GAAJ,IAAI,CAAe;QACtC,IAAI,CAAC,OAAO,GAAG;YACd,KAAK,EAAE,IAAI;YACX,SAAS,EAAE,IAAI;SACf,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,GAAG,CACR,GAAW,EACX,UAAkC,EAAE;QAEpC,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACtD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI,CACT,GAAW,EACX,OAAiC,EAAE,EACnC,UAAkC,EAAE;QAEpC,IAAI,IAAI,GAAsC,IAAI,CAAC;QAEnD,IAAI,CAAC,CAAC,IAAI,YAAY,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,YAAY,QAAQ,CAAC,EAAE,CAAC;YAC5D,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;YAC7C,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;QAED,OAAO,CAAC,MAAM,GAAG,kBAAkB,CAAC;QAEpC,OAAO,MAAM,IAAI,CAAC,OAAO,CACxB,MAAM,EACN,GAAG,EACH,IAAgC,EAChC,OAAO,CACP,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,GAAG,CACR,GAAW,EACX,OAAe,EAAE,EACjB,UAAkC,EAAE;QAEpC,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;QAC7C,OAAO,CAAC,MAAM,GAAG,kBAAkB,CAAC;QAEpC,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;IACtE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,KAAK,CACV,GAAW,EACX,OAAe,EAAE,EACjB,UAAkC,EAAE;QAEpC,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;QAC7C,OAAO,CAAC,MAAM,GAAG,kBAAkB,CAAC;QAEpC,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;IACxE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM,CACX,GAAW,EACX,OAAe,EAAE,EACjB,UAAkC,EAAE;QAEpC,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;QAC7C,OAAO,CAAC,MAAM,GAAG,kBAAkB,CAAC;QAEpC,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;IACzE,CAAC;IAEO,KAAK,CAAC,OAAO,CACpB,MAAc,EACd,GAAW,EACX,OAAwC,EAAE,EAC1C,UAAkC,EAAE;QAEpC,MAAM,CAAC,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,EAAE,EAAE;YACvE,IAAI;YACJ,OAAO,EAAE,MAAM,CAAC,MAAM,CACrB;gBACC,aAAa,EAAE,UAAU,MAAM,IAAI,CAAC,QAAQ,EAAE,EAAE;aAChD,EACD,OAAO,CACP;YACD,MAAM;SACN,CAAC,CAAC;QAEH,mBAAmB;QACnB,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC/B,IAAI,CAAC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;YAE9B,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QACvD,CAAC;QACD,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;YACX,MAAM,IAAI,sBAAsB,CAC/B,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EACrB,IAAI,kBAAkB,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,CAC3D,CAAC;QACH,CAAC;QAED,IAAI,CAAC,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACtB,OAAO,IAAI,kBAAkB,CAC5B,CAAC,CAAC,MAAM,EACR,EAAkB,EAClB,CAAC,CAAC,OAAO,CACT,CAAC;QACH,CAAC;QAED,OAAO,IAAI,kBAAkB,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;IACpE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAQ;QACb,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;YACrC,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,CAClC,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,kBAAkB,EAC3C;gBACC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACR,cAAc,EAAE,kBAAkB;iBAClC;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACpB,UAAU,EAAE,oBAAoB;oBAChC,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE;oBACtC,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE;iBAC9C,CAAC;aACF,CACD,CAAC;YAEF,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;gBACd,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,YAAY,CAAC;gBACrE,IAAI,IAAI,GAAG,EAAE,CAAC;gBAEd,IAAI,WAAW,CAAC,OAAO,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;oBACpD,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;gBAC1B,CAAC;qBAAM,CAAC;oBACP,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;gBAC1B,CAAC;gBAED,MAAM,IAAI,6BAA6B,CACtC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EACrB,IAAI,kBAAkB,CAAS,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAC/D,CAAC;YACH,CAAC;YAED,MAAM,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC;YAC9B,MAAM,QAAQ,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAGlC,CAAC;YACF,IAAI,CAAC,OAAO,CAAC,KAAK,GAAG,QAAQ,CAAC,YAAY,CAAC;YAC3C,UAAU,CAAC,UAAU,CAAC,UAAU,CAAC,UAAU,EAAE,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;YACrE,IAAI,CAAC,OAAO,CAAC,SAAS,GAAG,UAAU,CAAC;YAEpC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAe,CAAC;QACrC,CAAC;QAED,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC;YAC7D,UAAU;YAEV,IAAI,CAAC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;YAE9B,OAAO,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC9B,CAAC;QAED,OAAO,IAAI,CAAC,OAAO,CAAC,KAAe,CAAC;IACrC,CAAC;CACD;AAED;;GAEG;AACH,MAAM,OAAO,kBAAkB;IAEtB;IACA;IACA;IAHR,YACQ,UAAkB,EAClB,IAAkB,EAClB,OAAgB;QAFhB,eAAU,GAAV,UAAU,CAAQ;QAClB,SAAI,GAAJ,IAAI,CAAc;QAClB,YAAO,GAAP,OAAO,CAAS;IACrB,CAAC;CACJ;AAWD;;GAEG;AACH,MAAM,OAAO,6BAA8B,SAAQ,KAAK;IAG/C;IAFR,YACC,MAAc,EACP,QAAoC;QAE3C,KAAK,CACJ,kDAAkD,MAAM,mBAAmB,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAC1G,CAAC;QAJK,aAAQ,GAAR,QAAQ,CAA4B;IAK5C,CAAC;CACD;AAED;;GAEG;AACH,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAGxC;IAFR,YACC,MAAc,EACP,QAAmD;QAE1D,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAErE,KAAK,CAAC,8BAA8B,OAAO,sBAAsB,MAAM,EAAE,CAAC,CAAC;QAJpE,aAAQ,GAAR,QAAQ,CAA2C;IAK3D,CAAC;CACD","sourcesContent":["import type { ShopInterface } from \"./repository.js\";\n\n/**\n * HttpClient is a simple wrapper around the fetch API, pre-configured with the shop's URL and access token\n */\nexport class HttpClient {\n\tprivate storage: { expiresIn: Date | null; token: string | null };\n\n\tconstructor(private shop: ShopInterface) {\n\t\tthis.storage = {\n\t\t\ttoken: null,\n\t\t\texpiresIn: null,\n\t\t};\n\t}\n\n\t/**\n\t * Permform a GET request\n\t */\n\tasync get<ResponseType>(\n\t\turl: string,\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\treturn await this.request(\"GET\", url, null, headers);\n\t}\n\n\t/**\n\t * Permform a POST request\n\t */\n\tasync post<ResponseType>(\n\t\turl: string,\n\t\tjson: object | FormData | Blob = {},\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\tlet data: object | FormData | Blob | string = json;\n\n\t\tif (!(json instanceof Blob) && !(json instanceof FormData)) {\n\t\t\theaders[\"content-type\"] = \"application/json\";\n\t\t\tdata = JSON.stringify(json);\n\t\t}\n\n\t\theaders.accept = \"application/json\";\n\n\t\treturn await this.request(\n\t\t\t\"POST\",\n\t\t\turl,\n\t\t\tdata as FormData | Blob | string,\n\t\t\theaders,\n\t\t);\n\t}\n\n\t/**\n\t * Permform a PUT request\n\t */\n\tasync put<ResponseType>(\n\t\turl: string,\n\t\tjson: object = {},\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\theaders[\"content-type\"] = \"application/json\";\n\t\theaders.accept = \"application/json\";\n\n\t\treturn await this.request(\"PUT\", url, JSON.stringify(json), headers);\n\t}\n\n\t/**\n\t * Permform a PATCH request\n\t */\n\tasync patch<ResponseType>(\n\t\turl: string,\n\t\tjson: object = {},\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\theaders[\"content-type\"] = \"application/json\";\n\t\theaders.accept = \"application/json\";\n\n\t\treturn await this.request(\"PATCH\", url, JSON.stringify(json), headers);\n\t}\n\n\t/**\n\t * Permform a DELETE request\n\t */\n\tasync delete<ResponseType>(\n\t\turl: string,\n\t\tjson: object = {},\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\theaders[\"content-type\"] = \"application/json\";\n\t\theaders.accept = \"application/json\";\n\n\t\treturn await this.request(\"DELETE\", url, JSON.stringify(json), headers);\n\t}\n\n\tprivate async request<ResponseType>(\n\t\tmethod: string,\n\t\turl: string,\n\t\tbody: string | FormData | Blob | null = \"\",\n\t\theaders: Record<string, string> = {},\n\t): Promise<HttpClientResponse<ResponseType>> {\n\t\tconst f = await globalThis.fetch(`${this.shop.getShopUrl()}/api${url}`, {\n\t\t\tbody,\n\t\t\theaders: Object.assign(\n\t\t\t\t{\n\t\t\t\t\tAuthorization: `Bearer ${await this.getToken()}`,\n\t\t\t\t},\n\t\t\t\theaders,\n\t\t\t),\n\t\t\tmethod,\n\t\t});\n\n\t\t// Obtain new token\n\t\tif (!f.ok && f.status === 401) {\n\t\t\tthis.storage.expiresIn = null;\n\n\t\t\treturn await this.request(method, url, body, headers);\n\t\t}\n\t\tif (!f.ok) {\n\t\t\tthrow new ApiClientRequestFailed(\n\t\t\t\tthis.shop.getShopId(),\n\t\t\t\tnew HttpClientResponse(f.status, await f.json(), f.headers),\n\t\t\t);\n\t\t}\n\n\t\tif (f.status === 204) {\n\t\t\treturn new HttpClientResponse<ResponseType>(\n\t\t\t\tf.status,\n\t\t\t\t{} as ResponseType,\n\t\t\t\tf.headers,\n\t\t\t);\n\t\t}\n\n\t\treturn new HttpClientResponse(f.status, await f.json(), f.headers);\n\t}\n\n\t/**\n\t * Obtain a valid bearer token\n\t */\n\tasync getToken(): Promise<string> {\n\t\tif (this.storage.expiresIn === null) {\n\t\t\tconst auth = await globalThis.fetch(\n\t\t\t\t`${this.shop.getShopUrl()}/api/oauth/token`,\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t\t\t},\n\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\tgrant_type: \"client_credentials\",\n\t\t\t\t\t\tclient_id: this.shop.getShopClientId(),\n\t\t\t\t\t\tclient_secret: this.shop.getShopClientSecret(),\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tif (!auth.ok) {\n\t\t\t\tconst contentType = auth.headers.get(\"content-type\") || \"text/plain\";\n\t\t\t\tlet body = \"\";\n\n\t\t\t\tif (contentType.indexOf(\"application/json\") !== -1) {\n\t\t\t\t\tbody = await auth.json();\n\t\t\t\t} else {\n\t\t\t\t\tbody = await auth.text();\n\t\t\t\t}\n\n\t\t\t\tthrow new ApiClientAuthenticationFailed(\n\t\t\t\t\tthis.shop.getShopId(),\n\t\t\t\t\tnew HttpClientResponse<string>(auth.status, body, auth.headers),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst expireDate = new Date();\n\t\t\tconst authBody = (await auth.json()) as {\n\t\t\t\taccess_token: string;\n\t\t\t\texpires_in: number;\n\t\t\t};\n\t\t\tthis.storage.token = authBody.access_token;\n\t\t\texpireDate.setSeconds(expireDate.getSeconds() + authBody.expires_in);\n\t\t\tthis.storage.expiresIn = expireDate;\n\n\t\t\treturn this.storage.token as string;\n\t\t}\n\n\t\tif (this.storage.expiresIn.getTime() < new Date().getTime()) {\n\t\t\t// Expired\n\n\t\t\tthis.storage.expiresIn = null;\n\n\t\t\treturn await this.getToken();\n\t\t}\n\n\t\treturn this.storage.token as string;\n\t}\n}\n\n/**\n * HttpClientResponse is the response object of the HttpClient\n */\nexport class HttpClientResponse<ResponseType> {\n\tconstructor(\n\t\tpublic statusCode: number,\n\t\tpublic body: ResponseType,\n\t\tpublic headers: Headers,\n\t) {}\n}\n\ntype ShopwareErrorResponse = {\n\terrors: {\n\t\tcode: string;\n\t\tstatus: string;\n\t\ttitle: string;\n\t\tdetail: string;\n\t}[];\n};\n\n/**\n * ApiClientAuthenticationFailed is thrown when the authentication to the shop's API fails\n */\nexport class ApiClientAuthenticationFailed extends Error {\n\tconstructor(\n\t\tshopId: string,\n\t\tpublic response: HttpClientResponse<string>,\n\t) {\n\t\tsuper(\n\t\t\t`The api client authentication to shop with id: ${shopId} with response: ${JSON.stringify(response.body)}`,\n\t\t);\n\t}\n}\n\n/**\n * ApiClientRequestFailed is thrown when the request to the shop's API fails\n */\nexport class ApiClientRequestFailed extends Error {\n\tconstructor(\n\t\tshopId: string,\n\t\tpublic response: HttpClientResponse<ShopwareErrorResponse>,\n\t) {\n\t\tconst message = response.body.errors.map((e) => e.detail).join(\", \");\n\n\t\tsuper(`Request failed with error: ${message} for shop with id: ${shopId}`);\n\t}\n}\n"]}
@@ -10,15 +10,75 @@ declare module "hono" {
10
10
  }
11
11
  }
12
12
  interface MiddlewareConfig {
13
+ /**
14
+ * The name of the app
15
+ */
13
16
  appName: string | ((c: HonoContext) => string);
17
+ /**
18
+ * The secret of the app. When the app is published in the Shopware Store, the Shopware Store provides this value.
19
+ */
14
20
  appSecret: string | ((c: HonoContext) => string);
21
+ /**
22
+ * The URL of the app. This is the base URL of the app. This will automatically determined by default
23
+ */
15
24
  appUrl?: string | null;
25
+ /**
26
+ * The relative url of the app registration endpoint
27
+ *
28
+ * @default "/app/register"
29
+ */
16
30
  registrationUrl?: string | null;
31
+ /**
32
+ * The relative url of the app registration confirmation endpoint
33
+ *
34
+ * @default "/app/register/confirm"
35
+ */
17
36
  registerConfirmationUrl?: string | null;
37
+ /**
38
+ * The relative url of the app activation lifecycle endpoint
39
+ *
40
+ * @default "/app/activate"
41
+ */
18
42
  appActivateUrl?: string | null;
43
+ /**
44
+ * The relative url of the app deactivation lifecycle endpoint
45
+ *
46
+ * @default "/app/deactivate"
47
+ */
19
48
  appDeactivateUrl?: string | null;
49
+ /**
50
+ * The relative url of the app deletion lifecycle endpoint
51
+ *
52
+ * @default "/app/delete"
53
+ */
20
54
  appDeleteUrl?: string | null;
55
+ /**
56
+ * The relative url of the app scope. All requests matching this will be the signature automatically validated and the response will be signed
57
+ *
58
+ * @default "/app/*"
59
+ */
21
60
  appPath?: string | null;
61
+ /**
62
+ * Enable the app iframe integration. This will automatically set a cookie to identifiy the shopware shop and validate the request from a client side application. See appIframeRedirects
63
+ */
64
+ appIframeEnable?: boolean;
65
+ /**
66
+ * The relative url of the app iframe scope. All requests matching this will require that the request has an cookie set with the shopware shop. This cookie will be automatically set by
67
+ *
68
+ * @default "/client-api/*"
69
+ */
70
+ appIframePath?: string | null;
71
+ /**
72
+ * A mapping of the app iframe paths to the actual paths. This route will set a cookie automatically before the redirect to the actual path. In that way the client side application can send requests to /app-iframe/* with the cookie set and the server will automatically validate the request and knows which shop the request is for.
73
+ *
74
+ * @default {
75
+ * "/app/module": "https://my-static-client-side-app.com"
76
+ * }
77
+ */
78
+ appIframeRedirects?: Record<string, string>;
79
+ /**
80
+ * The repository to fetch and store the shop data
81
+ */
22
82
  shopRepository: ShopRepositoryInterface | ((c: HonoContext) => ShopRepositoryInterface);
23
83
  }
24
84
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../../src/integration/hono.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAE/E,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,MAAM,CAAC;AAEzD,OAAO,QAAQ,MAAM,CAAC;IACrB,UAAU,kBAAkB;QAE3B,GAAG,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC;QAC9B,IAAI,EAAE,aAAa,CAAC;QAEpB,OAAO,EAAE,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;KACzC;CACD;AAED,UAAU,gBAAgB;IACzB,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,MAAM,CAAC,CAAC;IAC/C,SAAS,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,MAAM,CAAC,CAAC;IACjD,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,cAAc,EACX,uBAAuB,GACvB,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,uBAAuB,CAAC,CAAC;CACjD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,gBAAgB,QAqHnE"}
1
+ {"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../../src/integration/hono.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAE/E,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,MAAM,CAAC;AAEzD,OAAO,QAAQ,MAAM,CAAC;IACrB,UAAU,kBAAkB;QAE3B,GAAG,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC;QAC9B,IAAI,EAAE,aAAa,CAAC;QAEpB,OAAO,EAAE,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;KACzC;CACD;AAED,UAAU,gBAAgB;IACzB;;OAEG;IACH,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,MAAM,CAAC,CAAC;IAC/C;;OAEG;IACH,SAAS,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,MAAM,CAAC,CAAC;IAEjD;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEvB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhC;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAExC;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE/B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEjC;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE7B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAExB;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE9B;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE5C;;OAEG;IACH,cAAc,EACX,uBAAuB,GACvB,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,uBAAuB,CAAC,CAAC;CACjD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,gBAAgB,QA2KnE"}
@@ -1,3 +1,4 @@
1
+ import { getSignedCookie, setSignedCookie } from "hono/cookie";
1
2
  import { AppServer } from "../app.js";
2
3
  /**
3
4
  * Configure the Hono server to handle the app registration and context resolution
@@ -11,6 +12,7 @@ export function configureAppServer(hono, cfg) {
11
12
  cfg.appDeactivateUrl = cfg.appDeactivateUrl || "/app/deactivate";
12
13
  cfg.appDeleteUrl = cfg.appDeleteUrl || "/app/delete";
13
14
  cfg.appPath = cfg.appPath || "/app/*";
15
+ cfg.appIframePath = cfg.appIframePath || "/client-api/*";
14
16
  hono.use("*", async (ctx, next) => {
15
17
  if (app === null) {
16
18
  const appUrl = cfg.appUrl || buildBaseUrl(ctx.req.url);
@@ -86,6 +88,38 @@ export function configureAppServer(hono, cfg) {
86
88
  const app = ctx.get("app");
87
89
  return await app.registration.delete(ctx.req.raw);
88
90
  });
91
+ if (cfg.appIframeEnable) {
92
+ hono.use(cfg.appIframePath, async (ctx, next) => {
93
+ const shopId = await getSignedCookie(ctx, ctx.get("app").cfg.appSecret, "shop");
94
+ if (!shopId) {
95
+ return ctx.json({ message: "Shop not found" }, { status: 400 });
96
+ }
97
+ const shop = await ctx.get("app").repository.getShopById(shopId);
98
+ if (!shop) {
99
+ return ctx.json({ message: "Shop not found" }, { status: 400 });
100
+ }
101
+ ctx.set("shop", shop);
102
+ await next();
103
+ });
104
+ for (let [path, redirect] of Object.entries(cfg.appIframeRedirects || {})) {
105
+ hono.get(path, async (ctx) => {
106
+ const url = new URL(ctx.req.url);
107
+ if (redirect.startsWith("/")) {
108
+ url.pathname = redirect;
109
+ redirect = url.toString();
110
+ }
111
+ else {
112
+ const newUrl = new URL(redirect);
113
+ for (const [key, value] of url.searchParams) {
114
+ newUrl.searchParams.set(key, value);
115
+ }
116
+ redirect = newUrl.toString();
117
+ }
118
+ await setSignedCookie(ctx, "shop", ctx.get("shop").getShopId(), ctx.get("app").cfg.appSecret);
119
+ return ctx.redirect(redirect);
120
+ });
121
+ }
122
+ }
89
123
  }
90
124
  function jsonResponse(body, status = 200) {
91
125
  return new Response(JSON.stringify(body), {
@@ -1 +1 @@
1
- {"version":3,"file":"hono.js","sourceRoot":"","sources":["../../../src/integration/hono.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AA+BtC;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAU,EAAE,GAAqB;IACnE,IAAI,GAAG,GAAqB,IAAI,CAAC;IAEjC,GAAG,CAAC,eAAe,GAAG,GAAG,CAAC,eAAe,IAAI,eAAe,CAAC;IAC7D,GAAG,CAAC,uBAAuB;QAC1B,GAAG,CAAC,uBAAuB,IAAI,uBAAuB,CAAC;IACxD,GAAG,CAAC,cAAc,GAAG,GAAG,CAAC,cAAc,IAAI,eAAe,CAAC;IAC3D,GAAG,CAAC,gBAAgB,GAAG,GAAG,CAAC,gBAAgB,IAAI,iBAAiB,CAAC;IACjE,GAAG,CAAC,YAAY,GAAG,GAAG,CAAC,YAAY,IAAI,aAAa,CAAC;IACrD,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,QAAQ,CAAC;IAEtC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACjC,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YAClB,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAEvD,IAAI,OAAO,GAAG,CAAC,cAAc,KAAK,UAAU,EAAE,CAAC;gBAC9C,GAAG,CAAC,cAAc,GAAG,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;YAC9C,CAAC;YAED,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;gBACvC,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAChC,CAAC;YAED,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;gBACzC,GAAG,CAAC,SAAS,GAAG,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACpC,CAAC;YAED,GAAG,GAAG,IAAI,SAAS,CAClB;gBACC,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,SAAS,EAAE,GAAG,CAAC,SAAS;gBACxB,oBAAoB,EAAE,MAAM,GAAG,GAAG,CAAC,uBAAuB;aAC1D,EACD,GAAG,CAAC,cAAc,CAClB,CAAC;QACH,CAAC;QAED,aAAa;QACb,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAEpB,MAAM,IAAI,EAAE,CAAC;IACd,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACzC,aAAa;QACb,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,CAAc,CAAC;QAExC,4CAA4C;QAC5C,IACC,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,eAAe;YACpC,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,uBAAuB;YAC5C,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,cAAc;YACnC,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,gBAAgB;YACrC,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,YAAY,EAChC,CAAC;YACF,MAAM,IAAI,EAAE,CAAC;YACb,OAAO;QACR,CAAC;QAED,IAAI,OAAwC,CAAC;QAC7C,IAAI,CAAC;YACJ,OAAO;gBACN,GAAG,CAAC,GAAG,CAAC,MAAM,KAAK,KAAK;oBACvB,CAAC,CAAC,MAAM,GAAG,CAAC,eAAe,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC;oBACpD,CAAC,CAAC,MAAM,GAAG,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACpD,CAAC;QAAC,OAAO,EAAE,EAAE,CAAC;YACb,OAAO,YAAY,CAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE,EAAE,GAAG,CAAC,CAAC;QAC1D,CAAC;QAED,aAAa;QACb,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAC9B,aAAa;QACb,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAE5B,MAAM,IAAI,EAAE,CAAC;QAEb,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;QAE/B,MAAM,GAAG;aACP,GAAG,CAAC,KAAK,CAAC;aACV,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC;QAE/D,GAAG,CAAC,MAAM,CACT,wBAAwB,EACxB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAW,CACtD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAC3C,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAE3B,OAAO,MAAM,GAAG,CAAC,YAAY,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,uBAAuB,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACpD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAE3B,OAAO,MAAM,GAAG,CAAC,YAAY,CAAC,iBAAiB,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAC3C,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAE3B,OAAO,MAAM,GAAG,CAAC,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAC7C,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAE3B,OAAO,MAAM,GAAG,CAAC,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACzC,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAE3B,OAAO,MAAM,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,IAAY,EAAE,MAAM,GAAG,GAAG;IAC/C,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE;QACzC,MAAM;QACN,OAAO,EAAE;YACR,cAAc,EAAE,kBAAkB;SAClC;KACD,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,GAAW;IAChC,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IAEvB,OAAO,GAAG,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;AACnC,CAAC","sourcesContent":["import { AppServer } from \"../app.js\";\nimport type { Context } from \"../context-resolver.js\";\nimport type { ShopInterface, ShopRepositoryInterface } from \"../repository.js\";\n\nimport type { Hono, Context as HonoContext } from \"hono\";\n\ndeclare module \"hono\" {\n\tinterface ContextVariableMap {\n\t\t// @ts-ignore\n\t\tapp: AppServer<ShopInterface>;\n\t\tshop: ShopInterface;\n\t\t// @ts-ignore\n\t\tcontext: Context<ShopInterface, unknown>;\n\t}\n}\n\ninterface MiddlewareConfig {\n\tappName: string | ((c: HonoContext) => string);\n\tappSecret: string | ((c: HonoContext) => string);\n\tappUrl?: string | null;\n\tregistrationUrl?: string | null;\n\tregisterConfirmationUrl?: string | null;\n\tappActivateUrl?: string | null;\n\tappDeactivateUrl?: string | null;\n\tappDeleteUrl?: string | null;\n\tappPath?: string | null;\n\tshopRepository:\n\t\t| ShopRepositoryInterface\n\t\t| ((c: HonoContext) => ShopRepositoryInterface);\n}\n\n/**\n * Configure the Hono server to handle the app registration and context resolution\n */\nexport function configureAppServer(hono: Hono, cfg: MiddlewareConfig) {\n\tlet app: AppServer | null = null;\n\n\tcfg.registrationUrl = cfg.registrationUrl || \"/app/register\";\n\tcfg.registerConfirmationUrl =\n\t\tcfg.registerConfirmationUrl || \"/app/register/confirm\";\n\tcfg.appActivateUrl = cfg.appActivateUrl || \"/app/activate\";\n\tcfg.appDeactivateUrl = cfg.appDeactivateUrl || \"/app/deactivate\";\n\tcfg.appDeleteUrl = cfg.appDeleteUrl || \"/app/delete\";\n\tcfg.appPath = cfg.appPath || \"/app/*\";\n\n\thono.use(\"*\", async (ctx, next) => {\n\t\tif (app === null) {\n\t\t\tconst appUrl = cfg.appUrl || buildBaseUrl(ctx.req.url);\n\n\t\t\tif (typeof cfg.shopRepository === \"function\") {\n\t\t\t\tcfg.shopRepository = cfg.shopRepository(ctx);\n\t\t\t}\n\n\t\t\tif (typeof cfg.appName === \"function\") {\n\t\t\t\tcfg.appName = cfg.appName(ctx);\n\t\t\t}\n\n\t\t\tif (typeof cfg.appSecret === \"function\") {\n\t\t\t\tcfg.appSecret = cfg.appSecret(ctx);\n\t\t\t}\n\n\t\t\tapp = new AppServer(\n\t\t\t\t{\n\t\t\t\t\tappName: cfg.appName,\n\t\t\t\t\tappSecret: cfg.appSecret,\n\t\t\t\t\tauthorizeCallbackUrl: appUrl + cfg.registerConfirmationUrl,\n\t\t\t\t},\n\t\t\t\tcfg.shopRepository,\n\t\t\t);\n\t\t}\n\n\t\t// @ts-ignore\n\t\tctx.set(\"app\", app);\n\n\t\tawait next();\n\t});\n\n\thono.use(cfg.appPath, async (ctx, next) => {\n\t\t// @ts-ignore\n\t\tconst app = ctx.get(\"app\") as AppServer;\n\n\t\t// Don't validate signature for registration\n\t\tif (\n\t\t\tctx.req.path === cfg.registrationUrl ||\n\t\t\tctx.req.path === cfg.registerConfirmationUrl ||\n\t\t\tctx.req.path === cfg.appActivateUrl ||\n\t\t\tctx.req.path === cfg.appDeactivateUrl ||\n\t\t\tctx.req.path === cfg.appDeleteUrl\n\t\t) {\n\t\t\tawait next();\n\t\t\treturn;\n\t\t}\n\n\t\tlet context: Context<ShopInterface, unknown>;\n\t\ttry {\n\t\t\tcontext =\n\t\t\t\tctx.req.method === \"GET\"\n\t\t\t\t\t? await app.contextResolver.fromBrowser(ctx.req.raw)\n\t\t\t\t\t: await app.contextResolver.fromAPI(ctx.req.raw);\n\t\t} catch (_e) {\n\t\t\treturn jsonResponse({ message: \"Invalid request\" }, 400);\n\t\t}\n\n\t\t// @ts-ignore\n\t\tctx.set(\"shop\", context.shop);\n\t\t// @ts-ignore\n\t\tctx.set(\"context\", context);\n\n\t\tawait next();\n\n\t\tconst cloned = ctx.res.clone();\n\n\t\tawait ctx\n\t\t\t.get(\"app\")\n\t\t\t.signer.signResponse(cloned, ctx.get(\"shop\").getShopSecret());\n\n\t\tctx.header(\n\t\t\t\"shopware-app-signature\",\n\t\t\tcloned.headers.get(\"shopware-app-signature\") as string,\n\t\t);\n\t});\n\n\thono.get(cfg.registrationUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.authorize(ctx.req.raw);\n\t});\n\n\thono.post(cfg.registerConfirmationUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.authorizeCallback(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appActivateUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.activate(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appDeactivateUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.deactivate(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appDeleteUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.delete(ctx.req.raw);\n\t});\n}\n\nfunction jsonResponse(body: object, status = 200): Response {\n\treturn new Response(JSON.stringify(body), {\n\t\tstatus,\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t},\n\t});\n}\n\nfunction buildBaseUrl(url: string): string {\n\tconst u = new URL(url);\n\n\treturn `${u.protocol}//${u.host}`;\n}\n"]}
1
+ {"version":3,"file":"hono.js","sourceRoot":"","sources":["../../../src/integration/hono.ts"],"names":[],"mappings":"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","sourcesContent":["import { getSignedCookie, setSignedCookie } from \"hono/cookie\";\nimport { AppServer } from \"../app.js\";\nimport type { Context } from \"../context-resolver.js\";\nimport type { ShopInterface, ShopRepositoryInterface } from \"../repository.js\";\n\nimport type { Hono, Context as HonoContext } from \"hono\";\n\ndeclare module \"hono\" {\n\tinterface ContextVariableMap {\n\t\t// @ts-ignore\n\t\tapp: AppServer<ShopInterface>;\n\t\tshop: ShopInterface;\n\t\t// @ts-ignore\n\t\tcontext: Context<ShopInterface, unknown>;\n\t}\n}\n\ninterface MiddlewareConfig {\n\t/**\n\t * The name of the app\n\t */\n\tappName: string | ((c: HonoContext) => string);\n\t/**\n\t * The secret of the app. When the app is published in the Shopware Store, the Shopware Store provides this value.\n\t */\n\tappSecret: string | ((c: HonoContext) => string);\n\n\t/**\n\t * The URL of the app. This is the base URL of the app. This will automatically determined by default\n\t */\n\tappUrl?: string | null;\n\n\t/**\n\t * The relative url of the app registration endpoint\n\t *\n\t * @default \"/app/register\"\n\t */\n\tregistrationUrl?: string | null;\n\n\t/**\n\t * The relative url of the app registration confirmation endpoint\n\t *\n\t * @default \"/app/register/confirm\"\n\t */\n\tregisterConfirmationUrl?: string | null;\n\n\t/**\n\t * The relative url of the app activation lifecycle endpoint\n\t *\n\t * @default \"/app/activate\"\n\t */\n\tappActivateUrl?: string | null;\n\n\t/**\n\t * The relative url of the app deactivation lifecycle endpoint\n\t *\n\t * @default \"/app/deactivate\"\n\t */\n\tappDeactivateUrl?: string | null;\n\n\t/**\n\t * The relative url of the app deletion lifecycle endpoint\n\t *\n\t * @default \"/app/delete\"\n\t */\n\tappDeleteUrl?: string | null;\n\n\t/**\n\t * The relative url of the app scope. All requests matching this will be the signature automatically validated and the response will be signed\n\t *\n\t * @default \"/app/*\"\n\t */\n\tappPath?: string | null;\n\n\t/**\n\t * Enable the app iframe integration. This will automatically set a cookie to identifiy the shopware shop and validate the request from a client side application. See appIframeRedirects\n\t */\n\tappIframeEnable?: boolean;\n\n\t/**\n\t * The relative url of the app iframe scope. All requests matching this will require that the request has an cookie set with the shopware shop. This cookie will be automatically set by\n\t *\n\t * @default \"/client-api/*\"\n\t */\n\tappIframePath?: string | null;\n\n\t/**\n\t * A mapping of the app iframe paths to the actual paths. This route will set a cookie automatically before the redirect to the actual path. In that way the client side application can send requests to /app-iframe/* with the cookie set and the server will automatically validate the request and knows which shop the request is for.\n\t *\n\t * @default {\n\t * \"/app/module\": \"https://my-static-client-side-app.com\"\n\t * }\n\t */\n\tappIframeRedirects?: Record<string, string>;\n\n\t/**\n\t * The repository to fetch and store the shop data\n\t */\n\tshopRepository:\n\t\t| ShopRepositoryInterface\n\t\t| ((c: HonoContext) => ShopRepositoryInterface);\n}\n\n/**\n * Configure the Hono server to handle the app registration and context resolution\n */\nexport function configureAppServer(hono: Hono, cfg: MiddlewareConfig) {\n\tlet app: AppServer | null = null;\n\n\tcfg.registrationUrl = cfg.registrationUrl || \"/app/register\";\n\tcfg.registerConfirmationUrl =\n\t\tcfg.registerConfirmationUrl || \"/app/register/confirm\";\n\tcfg.appActivateUrl = cfg.appActivateUrl || \"/app/activate\";\n\tcfg.appDeactivateUrl = cfg.appDeactivateUrl || \"/app/deactivate\";\n\tcfg.appDeleteUrl = cfg.appDeleteUrl || \"/app/delete\";\n\tcfg.appPath = cfg.appPath || \"/app/*\";\n\n\tcfg.appIframePath = cfg.appIframePath || \"/client-api/*\";\n\n\thono.use(\"*\", async (ctx, next) => {\n\t\tif (app === null) {\n\t\t\tconst appUrl = cfg.appUrl || buildBaseUrl(ctx.req.url);\n\n\t\t\tif (typeof cfg.shopRepository === \"function\") {\n\t\t\t\tcfg.shopRepository = cfg.shopRepository(ctx);\n\t\t\t}\n\n\t\t\tif (typeof cfg.appName === \"function\") {\n\t\t\t\tcfg.appName = cfg.appName(ctx);\n\t\t\t}\n\n\t\t\tif (typeof cfg.appSecret === \"function\") {\n\t\t\t\tcfg.appSecret = cfg.appSecret(ctx);\n\t\t\t}\n\n\t\t\tapp = new AppServer(\n\t\t\t\t{\n\t\t\t\t\tappName: cfg.appName,\n\t\t\t\t\tappSecret: cfg.appSecret,\n\t\t\t\t\tauthorizeCallbackUrl: appUrl + cfg.registerConfirmationUrl,\n\t\t\t\t},\n\t\t\t\tcfg.shopRepository,\n\t\t\t);\n\t\t}\n\n\t\t// @ts-ignore\n\t\tctx.set(\"app\", app);\n\n\t\tawait next();\n\t});\n\n\thono.use(cfg.appPath, async (ctx, next) => {\n\t\t// @ts-ignore\n\t\tconst app = ctx.get(\"app\") as AppServer;\n\n\t\t// Don't validate signature for registration\n\t\tif (\n\t\t\tctx.req.path === cfg.registrationUrl ||\n\t\t\tctx.req.path === cfg.registerConfirmationUrl ||\n\t\t\tctx.req.path === cfg.appActivateUrl ||\n\t\t\tctx.req.path === cfg.appDeactivateUrl ||\n\t\t\tctx.req.path === cfg.appDeleteUrl\n\t\t) {\n\t\t\tawait next();\n\t\t\treturn;\n\t\t}\n\n\t\tlet context: Context<ShopInterface, unknown>;\n\t\ttry {\n\t\t\tcontext =\n\t\t\t\tctx.req.method === \"GET\"\n\t\t\t\t\t? await app.contextResolver.fromBrowser(ctx.req.raw)\n\t\t\t\t\t: await app.contextResolver.fromAPI(ctx.req.raw);\n\t\t} catch (_e) {\n\t\t\treturn jsonResponse({ message: \"Invalid request\" }, 400);\n\t\t}\n\n\t\t// @ts-ignore\n\t\tctx.set(\"shop\", context.shop);\n\t\t// @ts-ignore\n\t\tctx.set(\"context\", context);\n\n\t\tawait next();\n\n\t\tconst cloned = ctx.res.clone();\n\n\t\tawait ctx\n\t\t\t.get(\"app\")\n\t\t\t.signer.signResponse(cloned, ctx.get(\"shop\").getShopSecret());\n\n\t\tctx.header(\n\t\t\t\"shopware-app-signature\",\n\t\t\tcloned.headers.get(\"shopware-app-signature\") as string,\n\t\t);\n\t});\n\n\thono.get(cfg.registrationUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.authorize(ctx.req.raw);\n\t});\n\n\thono.post(cfg.registerConfirmationUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.authorizeCallback(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appActivateUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.activate(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appDeactivateUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.deactivate(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appDeleteUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.delete(ctx.req.raw);\n\t});\n\n\tif (cfg.appIframeEnable) {\n\t\thono.use(cfg.appIframePath, async (ctx, next) => {\n\t\t\tconst shopId = await getSignedCookie(\n\t\t\t\tctx,\n\t\t\t\tctx.get(\"app\").cfg.appSecret,\n\t\t\t\t\"shop\",\n\t\t\t);\n\n\t\t\tif (!shopId) {\n\t\t\t\treturn ctx.json({ message: \"Shop not found\" }, { status: 400 });\n\t\t\t}\n\n\t\t\tconst shop = await ctx.get(\"app\").repository.getShopById(shopId);\n\n\t\t\tif (!shop) {\n\t\t\t\treturn ctx.json({ message: \"Shop not found\" }, { status: 400 });\n\t\t\t}\n\n\t\t\tctx.set(\"shop\", shop);\n\n\t\t\tawait next();\n\t\t});\n\n\t\tfor (let [path, redirect] of Object.entries(cfg.appIframeRedirects || {})) {\n\t\t\thono.get(path, async (ctx) => {\n\t\t\t\tconst url = new URL(ctx.req.url);\n\n\t\t\t\tif (redirect.startsWith(\"/\")) {\n\t\t\t\t\turl.pathname = redirect;\n\t\t\t\t\tredirect = url.toString();\n\t\t\t\t} else {\n\t\t\t\t\tconst newUrl = new URL(redirect);\n\n\t\t\t\t\tfor (const [key, value] of url.searchParams) {\n\t\t\t\t\t\tnewUrl.searchParams.set(key, value);\n\t\t\t\t\t}\n\n\t\t\t\t\tredirect = newUrl.toString();\n\t\t\t\t}\n\n\t\t\t\tawait setSignedCookie(\n\t\t\t\t\tctx,\n\t\t\t\t\t\"shop\",\n\t\t\t\t\tctx.get(\"shop\").getShopId(),\n\t\t\t\t\tctx.get(\"app\").cfg.appSecret,\n\t\t\t\t);\n\n\t\t\t\treturn ctx.redirect(redirect);\n\t\t\t});\n\t\t}\n\t}\n}\n\nfunction jsonResponse(body: object, status = 200): Response {\n\treturn new Response(JSON.stringify(body), {\n\t\tstatus,\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t},\n\t});\n}\n\nfunction buildBaseUrl(url: string): string {\n\tconst u = new URL(url);\n\n\treturn `${u.protocol}//${u.host}`;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shopware-ag/app-server-sdk",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "App Server SDK for JavaScript",
5
5
  "type": "module",
6
6
  "license": "MIT",