@shopware-ag/app-server-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/README.md +43 -0
  2. package/dist/commonjs/app.d.ts +31 -0
  3. package/dist/commonjs/app.d.ts.map +1 -0
  4. package/dist/commonjs/app.js +25 -0
  5. package/dist/commonjs/app.js.map +1 -0
  6. package/dist/commonjs/context-resolver.d.ts +30 -0
  7. package/dist/commonjs/context-resolver.d.ts.map +1 -0
  8. package/dist/commonjs/context-resolver.js +70 -0
  9. package/dist/commonjs/context-resolver.js.map +1 -0
  10. package/dist/commonjs/framework/hono.d.ts +36 -0
  11. package/dist/commonjs/framework/hono.d.ts.map +1 -0
  12. package/dist/commonjs/framework/hono.js +84 -0
  13. package/dist/commonjs/framework/hono.js.map +1 -0
  14. package/dist/commonjs/helper/app-actions.d.ts +13 -0
  15. package/dist/commonjs/helper/app-actions.d.ts.map +1 -0
  16. package/dist/commonjs/helper/app-actions.js +54 -0
  17. package/dist/commonjs/helper/app-actions.js.map +1 -0
  18. package/dist/commonjs/http-client.d.ts +67 -0
  19. package/dist/commonjs/http-client.d.ts.map +1 -0
  20. package/dist/commonjs/http-client.js +155 -0
  21. package/dist/commonjs/http-client.js.map +1 -0
  22. package/dist/commonjs/mod.d.ts +6 -0
  23. package/dist/commonjs/mod.d.ts.map +1 -0
  24. package/dist/commonjs/mod.js +16 -0
  25. package/dist/commonjs/mod.js.map +1 -0
  26. package/dist/commonjs/package.json +3 -0
  27. package/dist/commonjs/registration.d.ts +18 -0
  28. package/dist/commonjs/registration.d.ts.map +1 -0
  29. package/dist/commonjs/registration.js +89 -0
  30. package/dist/commonjs/registration.js.map +1 -0
  31. package/dist/commonjs/repository.d.ts +51 -0
  32. package/dist/commonjs/repository.d.ts.map +1 -0
  33. package/dist/commonjs/repository.js +68 -0
  34. package/dist/commonjs/repository.js.map +1 -0
  35. package/dist/commonjs/service/cloudflare.d.ts +43 -0
  36. package/dist/commonjs/service/cloudflare.d.ts.map +1 -0
  37. package/dist/commonjs/service/cloudflare.js +45 -0
  38. package/dist/commonjs/service/cloudflare.js.map +1 -0
  39. package/dist/commonjs/service/deno.d.ts +18 -0
  40. package/dist/commonjs/service/deno.d.ts.map +1 -0
  41. package/dist/commonjs/service/deno.js +48 -0
  42. package/dist/commonjs/service/deno.js.map +1 -0
  43. package/dist/commonjs/signer.d.ts +12 -0
  44. package/dist/commonjs/signer.d.ts.map +1 -0
  45. package/dist/commonjs/signer.js +58 -0
  46. package/dist/commonjs/signer.js.map +1 -0
  47. package/dist/commonjs/types.d.ts +28 -0
  48. package/dist/commonjs/types.d.ts.map +1 -0
  49. package/dist/commonjs/types.js +3 -0
  50. package/dist/commonjs/types.js.map +1 -0
  51. package/dist/deno/app.d.ts +31 -0
  52. package/dist/deno/app.d.ts.map +1 -0
  53. package/dist/deno/app.js +21 -0
  54. package/dist/deno/app.js.map +1 -0
  55. package/dist/deno/context-resolver.d.ts +30 -0
  56. package/dist/deno/context-resolver.d.ts.map +1 -0
  57. package/dist/deno/context-resolver.js +65 -0
  58. package/dist/deno/context-resolver.js.map +1 -0
  59. package/dist/deno/framework/hono.d.ts +36 -0
  60. package/dist/deno/framework/hono.d.ts.map +1 -0
  61. package/dist/deno/framework/hono.js +81 -0
  62. package/dist/deno/framework/hono.js.map +1 -0
  63. package/dist/deno/helper/app-actions.d.ts +13 -0
  64. package/dist/deno/helper/app-actions.d.ts.map +1 -0
  65. package/dist/deno/helper/app-actions.js +49 -0
  66. package/dist/deno/helper/app-actions.js.map +1 -0
  67. package/dist/deno/http-client.d.ts +67 -0
  68. package/dist/deno/http-client.d.ts.map +1 -0
  69. package/dist/deno/http-client.js +148 -0
  70. package/dist/deno/http-client.js.map +1 -0
  71. package/dist/deno/mod.d.ts +6 -0
  72. package/dist/deno/mod.d.ts.map +1 -0
  73. package/dist/deno/mod.js +5 -0
  74. package/dist/deno/mod.js.map +1 -0
  75. package/dist/deno/package.json +3 -0
  76. package/dist/deno/registration.d.ts +18 -0
  77. package/dist/deno/registration.d.ts.map +1 -0
  78. package/dist/deno/registration.js +84 -0
  79. package/dist/deno/registration.js.map +1 -0
  80. package/dist/deno/repository.d.ts +51 -0
  81. package/dist/deno/repository.d.ts.map +1 -0
  82. package/dist/deno/repository.js +63 -0
  83. package/dist/deno/repository.js.map +1 -0
  84. package/dist/deno/service/cloudflare.d.ts +43 -0
  85. package/dist/deno/service/cloudflare.d.ts.map +1 -0
  86. package/dist/deno/service/cloudflare.js +41 -0
  87. package/dist/deno/service/cloudflare.js.map +1 -0
  88. package/dist/deno/service/deno.d.ts +18 -0
  89. package/dist/deno/service/deno.d.ts.map +1 -0
  90. package/dist/deno/service/deno.js +44 -0
  91. package/dist/deno/service/deno.js.map +1 -0
  92. package/dist/deno/signer.d.ts +12 -0
  93. package/dist/deno/signer.d.ts.map +1 -0
  94. package/dist/deno/signer.js +54 -0
  95. package/dist/deno/signer.js.map +1 -0
  96. package/dist/deno/types.d.ts +28 -0
  97. package/dist/deno/types.d.ts.map +1 -0
  98. package/dist/deno/types.js +2 -0
  99. package/dist/deno/types.js.map +1 -0
  100. package/dist/esm/app.d.ts +31 -0
  101. package/dist/esm/app.d.ts.map +1 -0
  102. package/dist/esm/app.js +21 -0
  103. package/dist/esm/app.js.map +1 -0
  104. package/dist/esm/context-resolver.d.ts +30 -0
  105. package/dist/esm/context-resolver.d.ts.map +1 -0
  106. package/dist/esm/context-resolver.js +65 -0
  107. package/dist/esm/context-resolver.js.map +1 -0
  108. package/dist/esm/framework/hono.d.ts +36 -0
  109. package/dist/esm/framework/hono.d.ts.map +1 -0
  110. package/dist/esm/framework/hono.js +81 -0
  111. package/dist/esm/framework/hono.js.map +1 -0
  112. package/dist/esm/helper/app-actions.d.ts +13 -0
  113. package/dist/esm/helper/app-actions.d.ts.map +1 -0
  114. package/dist/esm/helper/app-actions.js +49 -0
  115. package/dist/esm/helper/app-actions.js.map +1 -0
  116. package/dist/esm/http-client.d.ts +67 -0
  117. package/dist/esm/http-client.d.ts.map +1 -0
  118. package/dist/esm/http-client.js +148 -0
  119. package/dist/esm/http-client.js.map +1 -0
  120. package/dist/esm/mod.d.ts +6 -0
  121. package/dist/esm/mod.d.ts.map +1 -0
  122. package/dist/esm/mod.js +5 -0
  123. package/dist/esm/mod.js.map +1 -0
  124. package/dist/esm/package.json +3 -0
  125. package/dist/esm/registration.d.ts +18 -0
  126. package/dist/esm/registration.d.ts.map +1 -0
  127. package/dist/esm/registration.js +84 -0
  128. package/dist/esm/registration.js.map +1 -0
  129. package/dist/esm/repository.d.ts +51 -0
  130. package/dist/esm/repository.d.ts.map +1 -0
  131. package/dist/esm/repository.js +63 -0
  132. package/dist/esm/repository.js.map +1 -0
  133. package/dist/esm/service/cloudflare.d.ts +43 -0
  134. package/dist/esm/service/cloudflare.d.ts.map +1 -0
  135. package/dist/esm/service/cloudflare.js +41 -0
  136. package/dist/esm/service/cloudflare.js.map +1 -0
  137. package/dist/esm/service/deno.d.ts +18 -0
  138. package/dist/esm/service/deno.d.ts.map +1 -0
  139. package/dist/esm/service/deno.js +44 -0
  140. package/dist/esm/service/deno.js.map +1 -0
  141. package/dist/esm/signer.d.ts +12 -0
  142. package/dist/esm/signer.d.ts.map +1 -0
  143. package/dist/esm/signer.js +54 -0
  144. package/dist/esm/signer.js.map +1 -0
  145. package/dist/esm/types.d.ts +28 -0
  146. package/dist/esm/types.d.ts.map +1 -0
  147. package/dist/esm/types.js +2 -0
  148. package/dist/esm/types.js.map +1 -0
  149. package/package.json +72 -0
@@ -0,0 +1,67 @@
1
+ import type { ShopInterface } from "./repository.js";
2
+ /**
3
+ * HttpClient is a simple wrapper around the fetch API, pre-configured with the shop's URL and access token
4
+ */
5
+ export declare class HttpClient {
6
+ private shop;
7
+ private storage;
8
+ constructor(shop: ShopInterface);
9
+ /**
10
+ * Permform a GET request
11
+ */
12
+ get<ResponseType>(url: string, headers?: Record<string, string>): Promise<HttpClientResponse<ResponseType>>;
13
+ /**
14
+ * Permform a POST request
15
+ */
16
+ post<ResponseType>(url: string, json?: object, headers?: Record<string, string>): Promise<HttpClientResponse<ResponseType>>;
17
+ /**
18
+ * Permform a PUT request
19
+ */
20
+ put<ResponseType>(url: string, json?: object, headers?: Record<string, string>): Promise<HttpClientResponse<ResponseType>>;
21
+ /**
22
+ * Permform a PATCH request
23
+ */
24
+ patch<ResponseType>(url: string, json?: object, headers?: Record<string, string>): Promise<HttpClientResponse<ResponseType>>;
25
+ /**
26
+ * Permform a DELETE request
27
+ */
28
+ delete<ResponseType>(url: string, json?: object, headers?: Record<string, string>): Promise<HttpClientResponse<ResponseType>>;
29
+ private request;
30
+ /**
31
+ * Obtain a valid bearer token
32
+ */
33
+ getToken(): Promise<string>;
34
+ }
35
+ /**
36
+ * HttpClientResponse is the response object of the HttpClient
37
+ */
38
+ export declare class HttpClientResponse<ResponseType> {
39
+ statusCode: number;
40
+ body: ResponseType;
41
+ headers: Headers;
42
+ constructor(statusCode: number, body: ResponseType, headers: Headers);
43
+ }
44
+ type ShopwareErrorResponse = {
45
+ errors: {
46
+ code: string;
47
+ status: string;
48
+ title: string;
49
+ detail: string;
50
+ }[];
51
+ };
52
+ /**
53
+ * ApiClientAuthenticationFailed is thrown when the authentication to the shop's API fails
54
+ */
55
+ export declare class ApiClientAuthenticationFailed extends Error {
56
+ response: HttpClientResponse<string>;
57
+ constructor(shopId: string, response: HttpClientResponse<string>);
58
+ }
59
+ /**
60
+ * ApiClientRequestFailed is thrown when the request to the shop's API fails
61
+ */
62
+ export declare class ApiClientRequestFailed extends Error {
63
+ response: HttpClientResponse<ShopwareErrorResponse>;
64
+ constructor(shopId: string, response: HttpClientResponse<ShopwareErrorResponse>);
65
+ }
66
+ export {};
67
+ //# sourceMappingURL=http-client.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,148 @@
1
+ /**
2
+ * HttpClient is a simple wrapper around the fetch API, pre-configured with the shop's URL and access token
3
+ */
4
+ export class HttpClient {
5
+ shop;
6
+ storage;
7
+ constructor(shop) {
8
+ this.shop = shop;
9
+ this.storage = {
10
+ token: null,
11
+ expiresIn: null,
12
+ };
13
+ }
14
+ /**
15
+ * Permform a GET request
16
+ */
17
+ async get(url, headers = {}) {
18
+ return await this.request("GET", url, null, headers);
19
+ }
20
+ /**
21
+ * Permform a POST request
22
+ */
23
+ async post(url, json = {}, headers = {}) {
24
+ headers["content-type"] = "application/json";
25
+ headers.accept = "application/json";
26
+ return await this.request("POST", url, JSON.stringify(json), headers);
27
+ }
28
+ /**
29
+ * Permform a PUT request
30
+ */
31
+ async put(url, json = {}, headers = {}) {
32
+ headers["content-type"] = "application/json";
33
+ headers.accept = "application/json";
34
+ return await this.request("PUT", url, JSON.stringify(json), headers);
35
+ }
36
+ /**
37
+ * Permform a PATCH request
38
+ */
39
+ async patch(url, json = {}, headers = {}) {
40
+ headers["content-type"] = "application/json";
41
+ headers.accept = "application/json";
42
+ return await this.request("PATCH", url, JSON.stringify(json), headers);
43
+ }
44
+ /**
45
+ * Permform a DELETE request
46
+ */
47
+ async delete(url, json = {}, headers = {}) {
48
+ headers["content-type"] = "application/json";
49
+ headers.accept = "application/json";
50
+ return await this.request("DELETE", url, JSON.stringify(json), headers);
51
+ }
52
+ async request(method, url, body = "", headers = {}) {
53
+ const f = await globalThis.fetch(`${this.shop.getShopUrl()}/api${url}`, {
54
+ body,
55
+ headers: Object.assign({
56
+ Authorization: `Bearer ${await this.getToken()}`,
57
+ }, headers),
58
+ method,
59
+ });
60
+ // Obtain new token
61
+ if (!f.ok && f.status === 401) {
62
+ this.storage.expiresIn = null;
63
+ return await this.request(method, url, body, headers);
64
+ }
65
+ if (!f.ok) {
66
+ throw new ApiClientRequestFailed(this.shop.getShopId(), new HttpClientResponse(f.status, await f.json(), f.headers));
67
+ }
68
+ if (f.status === 204) {
69
+ return new HttpClientResponse(f.status, {}, f.headers);
70
+ }
71
+ return new HttpClientResponse(f.status, await f.json(), f.headers);
72
+ }
73
+ /**
74
+ * Obtain a valid bearer token
75
+ */
76
+ async getToken() {
77
+ if (this.storage.expiresIn === null) {
78
+ const auth = await globalThis.fetch(`${this.shop.getShopUrl()}/api/oauth/token`, {
79
+ method: "POST",
80
+ headers: {
81
+ "content-type": "application/json",
82
+ },
83
+ body: JSON.stringify({
84
+ grant_type: "client_credentials",
85
+ client_id: this.shop.getShopClientId(),
86
+ client_secret: this.shop.getShopClientSecret(),
87
+ }),
88
+ });
89
+ if (!auth.ok) {
90
+ const contentType = auth.headers.get("content-type") || "text/plain";
91
+ let body = "";
92
+ if (contentType.indexOf("application/json") !== -1) {
93
+ body = await auth.json();
94
+ }
95
+ else {
96
+ body = await auth.text();
97
+ }
98
+ throw new ApiClientAuthenticationFailed(this.shop.getShopId(), new HttpClientResponse(auth.status, body, auth.headers));
99
+ }
100
+ const expireDate = new Date();
101
+ const authBody = (await auth.json());
102
+ this.storage.token = authBody.access_token;
103
+ expireDate.setSeconds(expireDate.getSeconds() + authBody.expires_in);
104
+ this.storage.expiresIn = expireDate;
105
+ return this.storage.token;
106
+ }
107
+ if (this.storage.expiresIn.getTime() < new Date().getTime()) {
108
+ // Expired
109
+ this.storage.expiresIn = null;
110
+ return await this.getToken();
111
+ }
112
+ return this.storage.token;
113
+ }
114
+ }
115
+ /**
116
+ * HttpClientResponse is the response object of the HttpClient
117
+ */
118
+ export class HttpClientResponse {
119
+ statusCode;
120
+ body;
121
+ headers;
122
+ constructor(statusCode, body, headers) {
123
+ this.statusCode = statusCode;
124
+ this.body = body;
125
+ this.headers = headers;
126
+ }
127
+ }
128
+ /**
129
+ * ApiClientAuthenticationFailed is thrown when the authentication to the shop's API fails
130
+ */
131
+ export class ApiClientAuthenticationFailed extends Error {
132
+ response;
133
+ constructor(shopId, response) {
134
+ super(`The api client authentication to shop with id: ${shopId} with response: ${JSON.stringify(response.body)}`);
135
+ this.response = response;
136
+ }
137
+ }
138
+ /**
139
+ * ApiClientRequestFailed is thrown when the request to the shop's API fails
140
+ */
141
+ export class ApiClientRequestFailed extends Error {
142
+ response;
143
+ constructor(shopId, response) {
144
+ super(`The api request failed with status code: ${response.statusCode} for shop with id: ${shopId}`);
145
+ this.response = response;
146
+ }
147
+ }
148
+ //# sourceMappingURL=http-client.js.map
@@ -0,0 +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\tsuper(\n\t\t\t`The api request failed with status code: ${response.statusCode} for shop with id: ${shopId}`,\n\t\t);\n\t}\n}\n"]}
@@ -0,0 +1,6 @@
1
+ export { AppServer } from "./app.js";
2
+ export { InMemoryShopRepository, SimpleShop } from "./repository.js";
3
+ export type { ShopInterface, ShopRepositoryInterface } from "./repository.js";
4
+ export { HttpClient, HttpClientResponse, ApiClientAuthenticationFailed, ApiClientRequestFailed, } from "./http-client.js";
5
+ export { Context } from "./context-resolver.js";
6
+ //# sourceMappingURL=mod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../src/mod.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACrE,YAAY,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC9E,OAAO,EACN,UAAU,EACV,kBAAkB,EAClB,6BAA6B,EAC7B,sBAAsB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { AppServer } from "./app.js";
2
+ export { InMemoryShopRepository, SimpleShop } from "./repository.js";
3
+ export { HttpClient, HttpClientResponse, ApiClientAuthenticationFailed, ApiClientRequestFailed, } from "./http-client.js";
4
+ export { Context } from "./context-resolver.js";
5
+ //# sourceMappingURL=mod.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.js","sourceRoot":"","sources":["../../src/mod.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAErE,OAAO,EACN,UAAU,EACV,kBAAkB,EAClB,6BAA6B,EAC7B,sBAAsB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC","sourcesContent":["export { AppServer } from \"./app.js\";\nexport { InMemoryShopRepository, SimpleShop } from \"./repository.js\";\nexport type { ShopInterface, ShopRepositoryInterface } from \"./repository.js\";\nexport {\n\tHttpClient,\n\tHttpClientResponse,\n\tApiClientAuthenticationFailed,\n\tApiClientRequestFailed,\n} from \"./http-client.js\";\nexport { Context } from \"./context-resolver.js\";\n"]}
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -0,0 +1,18 @@
1
+ import type { AppServer } from "./app.js";
2
+ export declare class Registration {
3
+ private app;
4
+ constructor(app: AppServer);
5
+ /**
6
+ * This method checks the request for the handshake with the Shopware Shop.
7
+ * if it's valid a Shop will be created, and a proof will be responded with a confirmation url.
8
+ * then the Shop will call the confirmation url, and this should be handled by the authorizeCallback method to finish the handshake.
9
+ */
10
+ authorize(req: Request): Promise<Response>;
11
+ /**
12
+ * This method is called by the Shopware Shop to confirm the handshake.
13
+ * It will update the shop with the given oauth2 credentials.
14
+ */
15
+ authorizeCallback(req: Request): Promise<Response>;
16
+ }
17
+ export declare function randomString(length?: number): string;
18
+ //# sourceMappingURL=registration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registration.d.ts","sourceRoot":"","sources":["../../src/registration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,qBAAa,YAAY;IACZ,OAAO,CAAC,GAAG;gBAAH,GAAG,EAAE,SAAS;IAElC;;;;OAIG;IACU,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;IA6CvD;;;OAGG;IACU,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;CAsC/D;AAED,wBAAgB,YAAY,CAAC,MAAM,SAAM,UAUxC"}
@@ -0,0 +1,84 @@
1
+ export class Registration {
2
+ app;
3
+ constructor(app) {
4
+ this.app = app;
5
+ }
6
+ /**
7
+ * This method checks the request for the handshake with the Shopware Shop.
8
+ * if it's valid a Shop will be created, and a proof will be responded with a confirmation url.
9
+ * then the Shop will call the confirmation url, and this should be handled by the authorizeCallback method to finish the handshake.
10
+ */
11
+ async authorize(req) {
12
+ const url = new URL(req.url);
13
+ if (!url.searchParams.has("shop-url") ||
14
+ !req.headers.has("shopware-app-signature") ||
15
+ !url.searchParams.has("shop-id") ||
16
+ !url.searchParams.has("timestamp")) {
17
+ return new InvalidRequestResponse("Invalid Request", 400);
18
+ }
19
+ const v = await this.app.signer.verify(req.headers.get("shopware-app-signature"), `shop-id=${url.searchParams.get("shop-id")}&shop-url=${url.searchParams.get("shop-url")}&timestamp=${url.searchParams.get("timestamp")}`, this.app.cfg.appSecret);
20
+ if (!v) {
21
+ return new InvalidRequestResponse("Cannot validate app signature");
22
+ }
23
+ const shopId = url.searchParams.get("shop-id");
24
+ const shopUrl = url.searchParams.get("shop-url");
25
+ const shopSecret = randomString();
26
+ await this.app.repository.createShop(shopId, shopUrl, shopSecret);
27
+ return new Response(JSON.stringify({
28
+ proof: await this.app.signer.sign(shopId + shopUrl + this.app.cfg.appName, this.app.cfg.appSecret),
29
+ secret: shopSecret,
30
+ confirmation_url: this.app.cfg.authorizeCallbackUrl,
31
+ }), {
32
+ headers: {
33
+ "content-type": "application/json",
34
+ },
35
+ });
36
+ }
37
+ /**
38
+ * This method is called by the Shopware Shop to confirm the handshake.
39
+ * It will update the shop with the given oauth2 credentials.
40
+ */
41
+ async authorizeCallback(req) {
42
+ const bodyContent = await req.text();
43
+ const body = JSON.parse(bodyContent);
44
+ if (typeof body.shopId !== "string" ||
45
+ typeof body.apiKey !== "string" ||
46
+ typeof body.secretKey !== "string" ||
47
+ !req.headers.has("shopware-shop-signature")) {
48
+ return new InvalidRequestResponse("Invalid Request", 400);
49
+ }
50
+ const shop = await this.app.repository.getShopById(body.shopId);
51
+ if (shop === null) {
52
+ return new InvalidRequestResponse("Invalid shop given");
53
+ }
54
+ const v = await this.app.signer.verify(req.headers.get("shopware-shop-signature"), bodyContent, shop.getShopSecret());
55
+ if (!v) {
56
+ // Shop has failed the verify. Delete it from our DB
57
+ await this.app.repository.deleteShop(shop.getShopId());
58
+ return new InvalidRequestResponse("Cannot validate app signature");
59
+ }
60
+ shop.setShopCredentials(body.apiKey, body.secretKey);
61
+ await this.app.repository.updateShop(shop);
62
+ return new Response(null, { status: 204 });
63
+ }
64
+ }
65
+ export function randomString(length = 120) {
66
+ let result = "";
67
+ const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
68
+ const charactersLength = characters.length;
69
+ for (let i = 0; i < length; i++) {
70
+ result += characters.charAt(Math.floor(Math.random() * charactersLength));
71
+ }
72
+ return result;
73
+ }
74
+ class InvalidRequestResponse extends Response {
75
+ constructor(message, status = 401) {
76
+ super(JSON.stringify({ message }), {
77
+ status,
78
+ headers: {
79
+ "content-type": "application/json",
80
+ },
81
+ });
82
+ }
83
+ }
84
+ //# sourceMappingURL=registration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registration.js","sourceRoot":"","sources":["../../src/registration.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AppServer } from \"./app.js\";\n\nexport class Registration {\n\tconstructor(private app: AppServer) {}\n\n\t/**\n\t * This method checks the request for the handshake with the Shopware Shop.\n\t * if it's valid a Shop will be created, and a proof will be responded with a confirmation url.\n\t * then the Shop will call the confirmation url, and this should be handled by the authorizeCallback method to finish the handshake.\n\t */\n\tpublic async authorize(req: Request): Promise<Response> {\n\t\tconst url = new URL(req.url);\n\n\t\tif (\n\t\t\t!url.searchParams.has(\"shop-url\") ||\n\t\t\t!req.headers.has(\"shopware-app-signature\") ||\n\t\t\t!url.searchParams.has(\"shop-id\") ||\n\t\t\t!url.searchParams.has(\"timestamp\")\n\t\t) {\n\t\t\treturn new InvalidRequestResponse(\"Invalid Request\", 400);\n\t\t}\n\n\t\tconst v = await this.app.signer.verify(\n\t\t\treq.headers.get(\"shopware-app-signature\") as string,\n\t\t\t`shop-id=${url.searchParams.get(\"shop-id\")}&shop-url=${url.searchParams.get(\"shop-url\")}&timestamp=${url.searchParams.get(\"timestamp\")}`,\n\t\t\tthis.app.cfg.appSecret,\n\t\t);\n\n\t\tif (!v) {\n\t\t\treturn new InvalidRequestResponse(\"Cannot validate app signature\");\n\t\t}\n\n\t\tconst shopId = url.searchParams.get(\"shop-id\") as string;\n\t\tconst shopUrl = url.searchParams.get(\"shop-url\") as string;\n\t\tconst shopSecret = randomString();\n\n\t\tawait this.app.repository.createShop(shopId, shopUrl, shopSecret);\n\n\t\treturn new Response(\n\t\t\tJSON.stringify({\n\t\t\t\tproof: await this.app.signer.sign(\n\t\t\t\t\tshopId + shopUrl + this.app.cfg.appName,\n\t\t\t\t\tthis.app.cfg.appSecret,\n\t\t\t\t),\n\t\t\t\tsecret: shopSecret,\n\t\t\t\tconfirmation_url: this.app.cfg.authorizeCallbackUrl,\n\t\t\t}),\n\t\t\t{\n\t\t\t\theaders: {\n\t\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t}\n\n\t/**\n\t * This method is called by the Shopware Shop to confirm the handshake.\n\t * It will update the shop with the given oauth2 credentials.\n\t */\n\tpublic async authorizeCallback(req: Request): Promise<Response> {\n\t\tconst bodyContent = await req.text();\n\n\t\tconst body = JSON.parse(bodyContent);\n\n\t\tif (\n\t\t\ttypeof body.shopId !== \"string\" ||\n\t\t\ttypeof body.apiKey !== \"string\" ||\n\t\t\ttypeof body.secretKey !== \"string\" ||\n\t\t\t!req.headers.has(\"shopware-shop-signature\")\n\t\t) {\n\t\t\treturn new InvalidRequestResponse(\"Invalid Request\", 400);\n\t\t}\n\n\t\tconst shop = await this.app.repository.getShopById(body.shopId as string);\n\n\t\tif (shop === null) {\n\t\t\treturn new InvalidRequestResponse(\"Invalid shop given\");\n\t\t}\n\n\t\tconst v = await this.app.signer.verify(\n\t\t\treq.headers.get(\"shopware-shop-signature\") as string,\n\t\t\tbodyContent,\n\t\t\tshop.getShopSecret(),\n\t\t);\n\t\tif (!v) {\n\t\t\t// Shop has failed the verify. Delete it from our DB\n\t\t\tawait this.app.repository.deleteShop(shop.getShopId());\n\n\t\t\treturn new InvalidRequestResponse(\"Cannot validate app signature\");\n\t\t}\n\n\t\tshop.setShopCredentials(body.apiKey, body.secretKey);\n\n\t\tawait this.app.repository.updateShop(shop);\n\n\t\treturn new Response(null, { status: 204 });\n\t}\n}\n\nexport function randomString(length = 120) {\n\tlet result = \"\";\n\tconst characters =\n\t\t\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n\tconst charactersLength = characters.length;\n\tfor (let i = 0; i < length; i++) {\n\t\tresult += characters.charAt(Math.floor(Math.random() * charactersLength));\n\t}\n\n\treturn result;\n}\n\nclass InvalidRequestResponse extends Response {\n\tconstructor(message: string, status = 401) {\n\t\tsuper(JSON.stringify({ message }), {\n\t\t\tstatus,\n\t\t\theaders: {\n\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t},\n\t\t});\n\t}\n}\n"]}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * ShopInterface defines the object that given back from the ShopRepository, it should methods to get the shop data and set them
3
+ */
4
+ export interface ShopInterface {
5
+ getShopId(): string;
6
+ getShopUrl(): string;
7
+ getShopSecret(): string;
8
+ getShopClientId(): string | null;
9
+ getShopClientSecret(): string | null;
10
+ setShopCredentials(clientId: string, clientSecret: string): void;
11
+ }
12
+ /**
13
+ * ShopRepositoryInterface is the storage interface for the shops, you should implement this to save the shop data to your database
14
+ * For testing cases the InMemoryShopRepository can be used
15
+ */
16
+ export interface ShopRepositoryInterface<Shop = ShopInterface> {
17
+ createShop(id: string, url: string, secret: string): Promise<void>;
18
+ getShopById(id: string): Promise<Shop | null>;
19
+ updateShop(shop: Shop): Promise<void>;
20
+ deleteShop(id: string): Promise<void>;
21
+ }
22
+ /**
23
+ * SimpleShop is a simple implementation of the ShopInterface, it stores the shop data in memory
24
+ */
25
+ export declare class SimpleShop implements ShopInterface {
26
+ private shopId;
27
+ private shopUrl;
28
+ private shopSecret;
29
+ private shopClientId;
30
+ private shopClientSecret;
31
+ yes(): void;
32
+ constructor(shopId: string, shopUrl: string, shopSecret: string);
33
+ getShopId(): string;
34
+ getShopUrl(): string;
35
+ getShopSecret(): string;
36
+ getShopClientId(): string | null;
37
+ getShopClientSecret(): string | null;
38
+ setShopCredentials(clientId: string, clientSecret: string): void;
39
+ }
40
+ /**
41
+ * InMemoryShopRepository is a simple implementation of the ShopRepositoryInterface, it stores the shop data in memory
42
+ */
43
+ export declare class InMemoryShopRepository implements ShopRepositoryInterface<SimpleShop> {
44
+ private storage;
45
+ constructor();
46
+ createShop(id: string, secret: string, url: string): Promise<void>;
47
+ getShopById(id: string): Promise<SimpleShop | null>;
48
+ updateShop(shop: SimpleShop): Promise<void>;
49
+ deleteShop(id: string): Promise<void>;
50
+ }
51
+ //# sourceMappingURL=repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/repository.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,SAAS,IAAI,MAAM,CAAC;IACpB,UAAU,IAAI,MAAM,CAAC;IACrB,aAAa,IAAI,MAAM,CAAC;IACxB,eAAe,IAAI,MAAM,GAAG,IAAI,CAAC;IACjC,mBAAmB,IAAI,MAAM,GAAG,IAAI,CAAC;IACrC,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CACjE;AAED;;;GAGG;AACH,MAAM,WAAW,uBAAuB,CAAC,IAAI,GAAG,aAAa;IAC5D,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnE,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAE9C,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtC,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;AAED;;GAEG;AACH,qBAAa,UAAW,YAAW,aAAa;IAC/C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,gBAAgB,CAAgB;IAExC,GAAG;gBAES,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM;IAQ/D,SAAS,IAAI,MAAM;IAGnB,UAAU,IAAI,MAAM;IAGpB,aAAa,IAAI,MAAM;IAGvB,eAAe,IAAI,MAAM,GAAG,IAAI;IAGhC,mBAAmB,IAAI,MAAM,GAAG,IAAI;IAGpC,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;CAIhE;AAED;;GAEG;AACH,qBAAa,sBACZ,YAAW,uBAAuB,CAAC,UAAU,CAAC;IAE9C,OAAO,CAAC,OAAO,CAA0B;;IAMnC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM;IAIlD,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAUnD,UAAU,CAAC,IAAI,EAAE,UAAU;IAI3B,UAAU,CAAC,EAAE,EAAE,MAAM;CAG3B"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * SimpleShop is a simple implementation of the ShopInterface, it stores the shop data in memory
3
+ */
4
+ export class SimpleShop {
5
+ shopId;
6
+ shopUrl;
7
+ shopSecret;
8
+ shopClientId;
9
+ shopClientSecret;
10
+ yes() { }
11
+ constructor(shopId, shopUrl, shopSecret) {
12
+ this.shopId = shopId;
13
+ this.shopUrl = shopUrl;
14
+ this.shopSecret = shopSecret;
15
+ this.shopClientId = null;
16
+ this.shopClientSecret = null;
17
+ }
18
+ getShopId() {
19
+ return this.shopId;
20
+ }
21
+ getShopUrl() {
22
+ return this.shopUrl;
23
+ }
24
+ getShopSecret() {
25
+ return this.shopSecret;
26
+ }
27
+ getShopClientId() {
28
+ return this.shopClientId;
29
+ }
30
+ getShopClientSecret() {
31
+ return this.shopClientSecret;
32
+ }
33
+ setShopCredentials(clientId, clientSecret) {
34
+ this.shopClientId = clientId;
35
+ this.shopClientSecret = clientSecret;
36
+ }
37
+ }
38
+ /**
39
+ * InMemoryShopRepository is a simple implementation of the ShopRepositoryInterface, it stores the shop data in memory
40
+ */
41
+ export class InMemoryShopRepository {
42
+ storage;
43
+ constructor() {
44
+ this.storage = new Map();
45
+ }
46
+ async createShop(id, secret, url) {
47
+ this.storage.set(id, new SimpleShop(id, url, secret));
48
+ }
49
+ async getShopById(id) {
50
+ const shop = this.storage.get(id);
51
+ if (shop === undefined) {
52
+ return null;
53
+ }
54
+ return shop;
55
+ }
56
+ async updateShop(shop) {
57
+ this.storage.set(shop.getShopId(), shop);
58
+ }
59
+ async deleteShop(id) {
60
+ this.storage.delete(id);
61
+ }
62
+ }
63
+ //# sourceMappingURL=repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository.js","sourceRoot":"","sources":["../../src/repository.ts"],"names":[],"mappings":"AA0BA;;GAEG;AACH,MAAM,OAAO,UAAU;IACd,MAAM,CAAS;IACf,OAAO,CAAS;IAChB,UAAU,CAAS;IACnB,YAAY,CAAgB;IAC5B,gBAAgB,CAAgB;IAExC,GAAG,KAAI,CAAC;IAER,YAAY,MAAc,EAAE,OAAe,EAAE,UAAkB;QAC9D,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAC9B,CAAC;IAED,SAAS;QACR,OAAO,IAAI,CAAC,MAAM,CAAC;IACpB,CAAC;IACD,UAAU;QACT,OAAO,IAAI,CAAC,OAAO,CAAC;IACrB,CAAC;IACD,aAAa;QACZ,OAAO,IAAI,CAAC,UAAU,CAAC;IACxB,CAAC;IACD,eAAe;QACd,OAAO,IAAI,CAAC,YAAY,CAAC;IAC1B,CAAC;IACD,mBAAmB;QAClB,OAAO,IAAI,CAAC,gBAAgB,CAAC;IAC9B,CAAC;IACD,kBAAkB,CAAC,QAAgB,EAAE,YAAoB;QACxD,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAC;QAC7B,IAAI,CAAC,gBAAgB,GAAG,YAAY,CAAC;IACtC,CAAC;CACD;AAED;;GAEG;AACH,MAAM,OAAO,sBAAsB;IAG1B,OAAO,CAA0B;IAEzC;QACC,IAAI,CAAC,OAAO,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,EAAU,EAAE,MAAc,EAAE,GAAW;QACvD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,UAAU,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;IACvD,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,EAAU;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAElC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC;QACb,CAAC;QAED,OAAO,IAAI,CAAC;IACb,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAgB;QAChC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,EAAU;QAC1B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC;CACD","sourcesContent":["/**\n * ShopInterface defines the object that given back from the ShopRepository, it should methods to get the shop data and set them\n */\nexport interface ShopInterface {\n\tgetShopId(): string;\n\tgetShopUrl(): string;\n\tgetShopSecret(): string;\n\tgetShopClientId(): string | null;\n\tgetShopClientSecret(): string | null;\n\tsetShopCredentials(clientId: string, clientSecret: string): void;\n}\n\n/**\n * ShopRepositoryInterface is the storage interface for the shops, you should implement this to save the shop data to your database\n * For testing cases the InMemoryShopRepository can be used\n */\nexport interface ShopRepositoryInterface<Shop = ShopInterface> {\n\tcreateShop(id: string, url: string, secret: string): Promise<void>;\n\n\tgetShopById(id: string): Promise<Shop | null>;\n\n\tupdateShop(shop: Shop): Promise<void>;\n\n\tdeleteShop(id: string): Promise<void>;\n}\n\n/**\n * SimpleShop is a simple implementation of the ShopInterface, it stores the shop data in memory\n */\nexport class SimpleShop implements ShopInterface {\n\tprivate shopId: string;\n\tprivate shopUrl: string;\n\tprivate shopSecret: string;\n\tprivate shopClientId: string | null;\n\tprivate shopClientSecret: string | null;\n\n\tyes() {}\n\n\tconstructor(shopId: string, shopUrl: string, shopSecret: string) {\n\t\tthis.shopId = shopId;\n\t\tthis.shopUrl = shopUrl;\n\t\tthis.shopSecret = shopSecret;\n\t\tthis.shopClientId = null;\n\t\tthis.shopClientSecret = null;\n\t}\n\n\tgetShopId(): string {\n\t\treturn this.shopId;\n\t}\n\tgetShopUrl(): string {\n\t\treturn this.shopUrl;\n\t}\n\tgetShopSecret(): string {\n\t\treturn this.shopSecret;\n\t}\n\tgetShopClientId(): string | null {\n\t\treturn this.shopClientId;\n\t}\n\tgetShopClientSecret(): string | null {\n\t\treturn this.shopClientSecret;\n\t}\n\tsetShopCredentials(clientId: string, clientSecret: string): void {\n\t\tthis.shopClientId = clientId;\n\t\tthis.shopClientSecret = clientSecret;\n\t}\n}\n\n/**\n * InMemoryShopRepository is a simple implementation of the ShopRepositoryInterface, it stores the shop data in memory\n */\nexport class InMemoryShopRepository\n\timplements ShopRepositoryInterface<SimpleShop>\n{\n\tprivate storage: Map<string, SimpleShop>;\n\n\tconstructor() {\n\t\tthis.storage = new Map<string, SimpleShop>();\n\t}\n\n\tasync createShop(id: string, secret: string, url: string) {\n\t\tthis.storage.set(id, new SimpleShop(id, url, secret));\n\t}\n\n\tasync getShopById(id: string): Promise<SimpleShop | null> {\n\t\tconst shop = this.storage.get(id);\n\n\t\tif (shop === undefined) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn shop;\n\t}\n\n\tasync updateShop(shop: SimpleShop) {\n\t\tthis.storage.set(shop.getShopId(), shop);\n\t}\n\n\tasync deleteShop(id: string) {\n\t\tthis.storage.delete(id);\n\t}\n}\n"]}
@@ -0,0 +1,43 @@
1
+ import { SimpleShop } from "../repository.js";
2
+ import type { ShopRepositoryInterface } from "../repository.js";
3
+ /**
4
+ * Cloudflare KV integration
5
+ * @module
6
+ */
7
+ /**
8
+ * Cloudflare KV implementation of the ShopRepositoryInterface
9
+ */
10
+ export declare class CloudflareShopRepository implements ShopRepositoryInterface<SimpleShop> {
11
+ private storage;
12
+ constructor(storage: KVNamespace);
13
+ createShop(id: string, url: string, secret: string): Promise<void>;
14
+ deleteShop(id: string): Promise<void>;
15
+ getShopById(id: string): Promise<SimpleShop | null>;
16
+ updateShop(shop: SimpleShop): Promise<void>;
17
+ protected serializeShop(shop: SimpleShop): string;
18
+ protected deserializeShop(data: string): SimpleShop;
19
+ }
20
+ /**
21
+ * Cloudflare KV
22
+ */
23
+ export declare interface KVNamespace<Key extends string = string> {
24
+ get(key: Key, options?: Partial<KVNamespaceGetOptions<undefined>>): Promise<string | null>;
25
+ put(key: Key, value: string | ArrayBuffer | ArrayBufferView | ReadableStream, options?: KVNamespacePutOptions): Promise<void>;
26
+ delete(key: Key): Promise<void>;
27
+ }
28
+ /**
29
+ * Cloudflare KV get options
30
+ */
31
+ export declare interface KVNamespaceGetOptions<Type> {
32
+ type: Type;
33
+ cacheTtl?: number;
34
+ }
35
+ /**
36
+ * Cloudflare KV put options
37
+ */
38
+ export declare interface KVNamespacePutOptions {
39
+ expiration?: number;
40
+ expirationTtl?: number;
41
+ metadata?: any | null;
42
+ }
43
+ //# sourceMappingURL=cloudflare.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudflare.d.ts","sourceRoot":"","sources":["../../../src/service/cloudflare.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAiB,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAE/E;;;GAGG;AAEH;;GAEG;AACH,qBAAa,wBACZ,YAAW,uBAAuB,CAAC,UAAU,CAAC;IAElC,OAAO,CAAC,OAAO;gBAAP,OAAO,EAAE,WAAW;IAIlC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOlE,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrC,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAUnD,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjD,SAAS,CAAC,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM;IAIjD,SAAS,CAAC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU;CAYnD;AAED;;GAEG;AACH,MAAM,CAAC,OAAO,WAAW,WAAW,CAAC,GAAG,SAAS,MAAM,GAAG,MAAM;IAC/D,GAAG,CACF,GAAG,EAAE,GAAG,EACR,OAAO,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC,GACjD,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1B,GAAG,CACF,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,eAAe,GAAG,cAAc,EAC9D,OAAO,CAAC,EAAE,qBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,MAAM,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,CAAC,OAAO,WAAW,qBAAqB,CAAC,IAAI;IAClD,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,CAAC,OAAO,WAAW,qBAAqB;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;CACtB"}
@@ -0,0 +1,41 @@
1
+ import { SimpleShop } from "../repository.js";
2
+ /**
3
+ * Cloudflare KV integration
4
+ * @module
5
+ */
6
+ /**
7
+ * Cloudflare KV implementation of the ShopRepositoryInterface
8
+ */
9
+ export class CloudflareShopRepository {
10
+ storage;
11
+ constructor(storage) {
12
+ this.storage = storage;
13
+ this.storage = storage;
14
+ }
15
+ async createShop(id, url, secret) {
16
+ await this.storage.put(id, this.serializeShop(new SimpleShop(id, url, secret)));
17
+ }
18
+ async deleteShop(id) {
19
+ await this.storage.delete(id);
20
+ }
21
+ async getShopById(id) {
22
+ const kvObj = await this.storage.get(id);
23
+ if (kvObj === null) {
24
+ return null;
25
+ }
26
+ return this.deserializeShop(kvObj);
27
+ }
28
+ async updateShop(shop) {
29
+ await this.storage.put(shop.getShopId(), this.serializeShop(shop));
30
+ }
31
+ serializeShop(shop) {
32
+ return JSON.stringify(shop);
33
+ }
34
+ deserializeShop(data) {
35
+ const obj = JSON.parse(data);
36
+ const shop = new SimpleShop(obj.shopId || "", obj.shopUrl || "", obj.shopSecret || "");
37
+ shop.setShopCredentials(obj.shopClientId || "", obj.shopClientSecret || "");
38
+ return shop;
39
+ }
40
+ }
41
+ //# sourceMappingURL=cloudflare.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../../../src/service/cloudflare.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG9C;;;GAGG;AAEH;;GAEG;AACH,MAAM,OAAO,wBAAwB;IAGhB;IAApB,YAAoB,OAAoB;QAApB,YAAO,GAAP,OAAO,CAAa;QACvC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,EAAU,EAAE,GAAW,EAAE,MAAc;QACvD,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CACrB,EAAE,EACF,IAAI,CAAC,aAAa,CAAC,IAAI,UAAU,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC,CACnD,CAAC;IACH,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,EAAU;QAC1B,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,EAAU;QAC3B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEzC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACpB,OAAO,IAAI,CAAC;QACb,CAAC;QAED,OAAO,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAgB;QAChC,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;IACpE,CAAC;IAES,aAAa,CAAC,IAAgB;QACvC,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAES,eAAe,CAAC,IAAY;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAE7B,MAAM,IAAI,GAAG,IAAI,UAAU,CAC1B,GAAG,CAAC,MAAM,IAAI,EAAE,EAChB,GAAG,CAAC,OAAO,IAAI,EAAE,EACjB,GAAG,CAAC,UAAU,IAAI,EAAE,CACpB,CAAC;QAEF,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,EAAE,GAAG,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC;QAC5E,OAAO,IAAI,CAAC;IACb,CAAC;CACD","sourcesContent":["import { SimpleShop } from \"../repository.js\";\nimport type { ShopInterface, ShopRepositoryInterface } from \"../repository.js\";\n\n/**\n * Cloudflare KV integration\n * @module\n */\n\n/**\n * Cloudflare KV implementation of the ShopRepositoryInterface\n */\nexport class CloudflareShopRepository\n\timplements ShopRepositoryInterface<SimpleShop>\n{\n\tconstructor(private storage: KVNamespace) {\n\t\tthis.storage = storage;\n\t}\n\n\tasync createShop(id: string, url: string, secret: string): Promise<void> {\n\t\tawait this.storage.put(\n\t\t\tid,\n\t\t\tthis.serializeShop(new SimpleShop(id, url, secret)),\n\t\t);\n\t}\n\n\tasync deleteShop(id: string): Promise<void> {\n\t\tawait this.storage.delete(id);\n\t}\n\n\tasync getShopById(id: string): Promise<SimpleShop | null> {\n\t\tconst kvObj = await this.storage.get(id);\n\n\t\tif (kvObj === null) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn this.deserializeShop(kvObj);\n\t}\n\n\tasync updateShop(shop: SimpleShop): Promise<void> {\n\t\tawait this.storage.put(shop.getShopId(), this.serializeShop(shop));\n\t}\n\n\tprotected serializeShop(shop: SimpleShop): string {\n\t\treturn JSON.stringify(shop);\n\t}\n\n\tprotected deserializeShop(data: string): SimpleShop {\n\t\tconst obj = JSON.parse(data);\n\n\t\tconst shop = new SimpleShop(\n\t\t\tobj.shopId || \"\",\n\t\t\tobj.shopUrl || \"\",\n\t\t\tobj.shopSecret || \"\",\n\t\t);\n\n\t\tshop.setShopCredentials(obj.shopClientId || \"\", obj.shopClientSecret || \"\");\n\t\treturn shop;\n\t}\n}\n\n/**\n * Cloudflare KV\n */\nexport declare interface KVNamespace<Key extends string = string> {\n\tget(\n\t\tkey: Key,\n\t\toptions?: Partial<KVNamespaceGetOptions<undefined>>,\n\t): Promise<string | null>;\n\tput(\n\t\tkey: Key,\n\t\tvalue: string | ArrayBuffer | ArrayBufferView | ReadableStream,\n\t\toptions?: KVNamespacePutOptions,\n\t): Promise<void>;\n\tdelete(key: Key): Promise<void>;\n}\n\n/**\n * Cloudflare KV get options\n */\nexport declare interface KVNamespaceGetOptions<Type> {\n\ttype: Type;\n\tcacheTtl?: number;\n}\n\n/**\n * Cloudflare KV put options\n */\nexport declare interface KVNamespacePutOptions {\n\texpiration?: number;\n\texpirationTtl?: number;\n\tmetadata?: any | null;\n}\n"]}