bestraw-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.
package/dist/index.js ADDED
@@ -0,0 +1,508 @@
1
+ // src/base.ts
2
+ var BestrawError = class extends Error {
3
+ constructor(status, message, details) {
4
+ super(message);
5
+ this.name = "BestrawError";
6
+ this.status = status;
7
+ this.details = details;
8
+ const err = details?.error;
9
+ if (err?.code) {
10
+ this.code = err.code;
11
+ }
12
+ if (err?.meta) {
13
+ this.meta = err.meta;
14
+ }
15
+ }
16
+ };
17
+ var BaseClient = class {
18
+ constructor(config) {
19
+ this.baseUrl = config.baseUrl.replace(/\/$/, "");
20
+ this.apiToken = config.apiToken;
21
+ this.defaultLocale = config.defaultLocale;
22
+ this.getAuthToken = config.getAuthToken;
23
+ }
24
+ async headers() {
25
+ const headers = {
26
+ "Content-Type": "application/json"
27
+ };
28
+ if (this.apiToken) {
29
+ headers["Authorization"] = `Bearer ${this.apiToken}`;
30
+ } else if (this.getAuthToken) {
31
+ const token = await this.getAuthToken();
32
+ if (token) {
33
+ headers["Authorization"] = `Bearer ${token}`;
34
+ }
35
+ }
36
+ return headers;
37
+ }
38
+ appendLocale(path, locale) {
39
+ const effectiveLocale = locale || this.defaultLocale;
40
+ if (!effectiveLocale) return path;
41
+ const separator = path.includes("?") ? "&" : "?";
42
+ return `${path}${separator}locale=${effectiveLocale}`;
43
+ }
44
+ async request(path, options = {}) {
45
+ const { locale, ...fetchOptions } = options;
46
+ const url = `${this.baseUrl}${this.appendLocale(path, locale)}`;
47
+ const headers = await this.headers();
48
+ if (fetchOptions.body instanceof FormData) {
49
+ delete headers["Content-Type"];
50
+ }
51
+ const response = await fetch(url, {
52
+ ...fetchOptions,
53
+ headers: {
54
+ ...headers,
55
+ ...fetchOptions.headers
56
+ }
57
+ });
58
+ if (!response.ok) {
59
+ const error = await response.json().catch(() => ({
60
+ message: response.statusText
61
+ }));
62
+ throw new BestrawError(
63
+ response.status,
64
+ error.error?.message ?? error.message ?? "Request failed",
65
+ error
66
+ );
67
+ }
68
+ if (response.status === 204) {
69
+ return void 0;
70
+ }
71
+ const json = await response.json();
72
+ return json.data !== void 0 ? json.data : json;
73
+ }
74
+ };
75
+
76
+ // src/namespaces/menu.ts
77
+ var MenuNamespace = class {
78
+ constructor(client) {
79
+ this.client = client;
80
+ }
81
+ // --- Menus ---
82
+ async getMenus(locale) {
83
+ return this.client.request("/api/menu/menus", { locale });
84
+ }
85
+ async getMenu(documentId, options) {
86
+ const query = options?.populate ? `?populate=${options.populate}` : "";
87
+ return this.client.request(`/api/menu/menus/${documentId}${query}`, { locale: options?.locale });
88
+ }
89
+ async createMenu(data, locale) {
90
+ return this.client.request("/api/menu/menus", {
91
+ method: "POST",
92
+ body: JSON.stringify({ data }),
93
+ locale
94
+ });
95
+ }
96
+ async updateMenu(documentId, data, locale) {
97
+ return this.client.request(`/api/menu/menus/${documentId}`, {
98
+ method: "PUT",
99
+ body: JSON.stringify({ data }),
100
+ locale
101
+ });
102
+ }
103
+ async deleteMenu(documentId) {
104
+ await this.client.request(`/api/menu/menus/${documentId}`, { method: "DELETE" });
105
+ }
106
+ // --- Variants ---
107
+ async getVariants(menuDocumentId, locale) {
108
+ return this.client.request(`/api/menu/menus/${menuDocumentId}/variants`, { locale });
109
+ }
110
+ async getActiveVariant(menuDocumentId, locale) {
111
+ return this.client.request(`/api/menu/menus/${menuDocumentId}/active-variant`, { locale });
112
+ }
113
+ async setActiveVariant(menuDocumentId, variantDocumentId) {
114
+ await this.client.request(`/api/menu/menus/${menuDocumentId}/active-variant`, {
115
+ method: "PUT",
116
+ body: JSON.stringify({ variantId: variantDocumentId })
117
+ });
118
+ }
119
+ async createVariant(data, locale) {
120
+ return this.client.request("/api/menu/variants", {
121
+ method: "POST",
122
+ body: JSON.stringify({ data }),
123
+ locale
124
+ });
125
+ }
126
+ async updateVariant(documentId, data, locale) {
127
+ return this.client.request(`/api/menu/variants/${documentId}`, {
128
+ method: "PUT",
129
+ body: JSON.stringify({ data }),
130
+ locale
131
+ });
132
+ }
133
+ async deleteVariant(documentId) {
134
+ await this.client.request(`/api/menu/variants/${documentId}`, { method: "DELETE" });
135
+ }
136
+ // --- Categories ---
137
+ async createCategory(data, locale) {
138
+ return this.client.request("/api/menu/categories", {
139
+ method: "POST",
140
+ body: JSON.stringify({ data }),
141
+ locale
142
+ });
143
+ }
144
+ async updateCategory(documentId, data, locale) {
145
+ return this.client.request(`/api/menu/categories/${documentId}`, {
146
+ method: "PUT",
147
+ body: JSON.stringify({ data }),
148
+ locale
149
+ });
150
+ }
151
+ async deleteCategory(documentId) {
152
+ await this.client.request(`/api/menu/categories/${documentId}`, { method: "DELETE" });
153
+ }
154
+ async sortCategories(variantDocumentId, sortedCategoryIds) {
155
+ await this.client.request(`/api/menu/variants/${variantDocumentId}/sort-categories`, {
156
+ method: "PUT",
157
+ body: JSON.stringify({ sortedCategoryIds })
158
+ });
159
+ }
160
+ // --- Meals ---
161
+ async createMeal(data, locale) {
162
+ return this.client.request("/api/menu/meals", {
163
+ method: "POST",
164
+ body: JSON.stringify({ data }),
165
+ locale
166
+ });
167
+ }
168
+ async updateMeal(documentId, data, locale) {
169
+ return this.client.request(`/api/menu/meals/${documentId}`, {
170
+ method: "PUT",
171
+ body: JSON.stringify({ data }),
172
+ locale
173
+ });
174
+ }
175
+ async deleteMeal(documentId) {
176
+ await this.client.request(`/api/menu/meals/${documentId}`, { method: "DELETE" });
177
+ }
178
+ async sortMeals(categoryDocumentId, sortedMealIds) {
179
+ await this.client.request(`/api/menu/categories/${categoryDocumentId}/sort-meals`, {
180
+ method: "PUT",
181
+ body: JSON.stringify({ sortedMealIds })
182
+ });
183
+ }
184
+ // --- Pictures (Strapi Upload API - core, not plugin) ---
185
+ async uploadPicture(file, ref, refId, field) {
186
+ const formData = new FormData();
187
+ formData.append("files", file);
188
+ formData.append("ref", ref);
189
+ formData.append("refId", refId);
190
+ formData.append("field", field);
191
+ return this.client.request("/api/upload", {
192
+ method: "POST",
193
+ body: formData
194
+ });
195
+ }
196
+ async deletePicture(pictureId) {
197
+ await this.client.request(`/api/upload/files/${pictureId}`, { method: "DELETE" });
198
+ }
199
+ };
200
+
201
+ // src/namespaces/restaurant.ts
202
+ var RestaurantNamespace = class {
203
+ constructor(client) {
204
+ this.client = client;
205
+ }
206
+ async getInfo(locale) {
207
+ return this.client.request("/api/restaurant/restaurant-info", { locale });
208
+ }
209
+ async updateInfo(data, locale) {
210
+ return this.client.request("/api/restaurant/restaurant-info", {
211
+ method: "PUT",
212
+ body: JSON.stringify({ data }),
213
+ locale
214
+ });
215
+ }
216
+ async getHours() {
217
+ return this.client.request("/api/restaurant/opening-hours");
218
+ }
219
+ async getStatus() {
220
+ return this.client.request("/api/restaurant/opening-hours/status");
221
+ }
222
+ async updateHours(data) {
223
+ return this.client.request("/api/restaurant/opening-hours", {
224
+ method: "PUT",
225
+ body: JSON.stringify({ data })
226
+ });
227
+ }
228
+ async getTheme() {
229
+ return this.client.request("/api/restaurant/theme");
230
+ }
231
+ async updateTheme(data) {
232
+ return this.client.request("/api/restaurant/theme", {
233
+ method: "PUT",
234
+ body: JSON.stringify({ data })
235
+ });
236
+ }
237
+ };
238
+
239
+ // src/namespaces/customer.ts
240
+ var CustomerNamespace = class {
241
+ constructor(client) {
242
+ this.client = client;
243
+ }
244
+ async requestOtp(phone) {
245
+ return this.client.request("/api/customer/auth/request-otp", {
246
+ method: "POST",
247
+ body: JSON.stringify({ phone })
248
+ });
249
+ }
250
+ async verifyOtp(phone, code) {
251
+ return this.client.request("/api/customer/auth/verify-otp", {
252
+ method: "POST",
253
+ body: JSON.stringify({ phone, code })
254
+ });
255
+ }
256
+ async requestEmailOtp(email) {
257
+ return this.client.request("/api/customer/auth/request-email-otp", {
258
+ method: "POST",
259
+ body: JSON.stringify({ email })
260
+ });
261
+ }
262
+ async verifyEmailOtp(email, code) {
263
+ return this.client.request("/api/customer/auth/verify-email-otp", {
264
+ method: "POST",
265
+ body: JSON.stringify({ email, code })
266
+ });
267
+ }
268
+ async getMe() {
269
+ return this.client.request("/api/customer/customers/me");
270
+ }
271
+ async updateMe(data) {
272
+ return this.client.request("/api/customer/customers/me", {
273
+ method: "PUT",
274
+ body: JSON.stringify({ data })
275
+ });
276
+ }
277
+ };
278
+
279
+ // src/utils.ts
280
+ function stringifyQuery(obj) {
281
+ const parts = [];
282
+ function flatten(value, prefix) {
283
+ if (value === null || value === void 0) return;
284
+ if (Array.isArray(value)) {
285
+ value.forEach((item, i) => flatten(item, `${prefix}[${i}]`));
286
+ } else if (typeof value === "object") {
287
+ for (const [key, val] of Object.entries(value)) {
288
+ flatten(val, prefix ? `${prefix}[${key}]` : key);
289
+ }
290
+ } else {
291
+ parts.push(`${encodeURIComponent(prefix)}=${encodeURIComponent(String(value))}`);
292
+ }
293
+ }
294
+ flatten(obj, "");
295
+ return parts.join("&");
296
+ }
297
+
298
+ // src/namespaces/ordering.ts
299
+ var OrderingNamespace = class {
300
+ constructor(client) {
301
+ this.client = client;
302
+ }
303
+ // --- Customer routes ---
304
+ async createOrder(data) {
305
+ return this.client.request("/api/ordering/orders", {
306
+ method: "POST",
307
+ body: JSON.stringify(data)
308
+ });
309
+ }
310
+ async getMyOrders() {
311
+ return this.client.request("/api/ordering/orders/mine");
312
+ }
313
+ async getMyOrder(documentId) {
314
+ return this.client.request(`/api/ordering/orders/mine/${documentId}`);
315
+ }
316
+ async cancelOrder(documentId, reason) {
317
+ return this.client.request(`/api/ordering/orders/${documentId}/cancel`, {
318
+ method: "PUT",
319
+ body: JSON.stringify({ reason })
320
+ });
321
+ }
322
+ // --- Admin / KDS routes ---
323
+ async getOrders(query) {
324
+ const qs = query ? "?" + stringifyQuery(query) : "";
325
+ return this.client.request(`/api/ordering/orders${qs}`);
326
+ }
327
+ async getOrder(documentId) {
328
+ return this.client.request(`/api/ordering/orders/${documentId}`);
329
+ }
330
+ async updateOrderStatus(documentId, status) {
331
+ return this.client.request(`/api/ordering/orders/${documentId}/status`, {
332
+ method: "PUT",
333
+ body: JSON.stringify({ status })
334
+ });
335
+ }
336
+ async getActiveOrders() {
337
+ return this.client.request("/api/ordering/orders/active");
338
+ }
339
+ async getPublicOrderSettings() {
340
+ return this.client.request("/api/ordering/order-settings/public");
341
+ }
342
+ async getOrderSettings() {
343
+ return this.client.request("/api/ordering/order-settings");
344
+ }
345
+ async updateOrderSettings(data) {
346
+ return this.client.request("/api/ordering/order-settings", {
347
+ method: "PUT",
348
+ body: JSON.stringify({ data })
349
+ });
350
+ }
351
+ };
352
+
353
+ // src/namespaces/payment.ts
354
+ var PaymentNamespace = class {
355
+ constructor(client) {
356
+ this.client = client;
357
+ }
358
+ async createPaymentIntent(orderDocumentId) {
359
+ return this.client.request("/api/payment/payments/create-intent", {
360
+ method: "POST",
361
+ body: JSON.stringify({ orderDocumentId })
362
+ });
363
+ }
364
+ async getPayment(documentId) {
365
+ return this.client.request(`/api/payment/payments/${documentId}`);
366
+ }
367
+ async refundPayment(documentId, amount) {
368
+ return this.client.request(`/api/payment/payments/${documentId}/refund`, {
369
+ method: "POST",
370
+ body: JSON.stringify({ amount })
371
+ });
372
+ }
373
+ };
374
+
375
+ // src/namespaces/loyalty.ts
376
+ var LoyaltyNamespace = class {
377
+ constructor(client) {
378
+ this.client = client;
379
+ }
380
+ // --- Customer routes ---
381
+ async getMyAccount() {
382
+ return this.client.request("/api/loyalty/loyalty/me");
383
+ }
384
+ async getMyTransactions() {
385
+ return this.client.request("/api/loyalty/loyalty/me/transactions");
386
+ }
387
+ async redeemReward(rewardIndex) {
388
+ return this.client.request("/api/loyalty/loyalty/redeem", {
389
+ method: "POST",
390
+ body: JSON.stringify({ rewardIndex })
391
+ });
392
+ }
393
+ // --- Public ---
394
+ async getProgram() {
395
+ return this.client.request("/api/loyalty/loyalty/program");
396
+ }
397
+ // --- Admin ---
398
+ async getAccounts() {
399
+ return this.client.request("/api/loyalty/loyalty/accounts");
400
+ }
401
+ async adjustPoints(accountDocumentId, points, description) {
402
+ return this.client.request(`/api/loyalty/loyalty/accounts/${accountDocumentId}/adjust`, {
403
+ method: "POST",
404
+ body: JSON.stringify({ points, description })
405
+ });
406
+ }
407
+ };
408
+
409
+ // src/namespaces/blog.ts
410
+ var BlogNamespace = class {
411
+ constructor(client) {
412
+ this.client = client;
413
+ }
414
+ async getArticles(options) {
415
+ const params = new URLSearchParams();
416
+ if (options?.category) params.set("category", options.category);
417
+ if (options?.tag) params.set("tag", options.tag);
418
+ if (options?.featured) params.set("featured", "true");
419
+ if (options?.page) params.set("pagination[page]", String(options.page));
420
+ if (options?.pageSize) params.set("pagination[pageSize]", String(options.pageSize));
421
+ if (options?.locale) params.set("locale", options.locale);
422
+ const qs = params.toString();
423
+ return this.client.request(`/api/blog/articles${qs ? `?${qs}` : ""}`);
424
+ }
425
+ async getArticle(slug, locale) {
426
+ const params = locale ? `?locale=${locale}` : "";
427
+ return this.client.request(`/api/blog/articles/${slug}${params}`);
428
+ }
429
+ async getCategories(locale) {
430
+ const params = locale ? `?locale=${locale}` : "";
431
+ return this.client.request(`/api/blog/categories${params}`);
432
+ }
433
+ };
434
+
435
+ // src/client.ts
436
+ var BestrawClient = class extends BaseClient {
437
+ constructor(config) {
438
+ super(config);
439
+ this.menu = new MenuNamespace(this);
440
+ this.restaurant = new RestaurantNamespace(this);
441
+ this.customer = new CustomerNamespace(this);
442
+ this.ordering = new OrderingNamespace(this);
443
+ this.payment = new PaymentNamespace(this);
444
+ this.loyalty = new LoyaltyNamespace(this);
445
+ this.blog = new BlogNamespace(this);
446
+ }
447
+ };
448
+
449
+ // src/errors.ts
450
+ var ErrorCode = {
451
+ // Generic
452
+ UNKNOWN: "UNKNOWN",
453
+ NETWORK_ERROR: "NETWORK_ERROR",
454
+ // Auth / Customer
455
+ AUTH_REQUIRED: "AUTH_REQUIRED",
456
+ INVALID_PHONE: "INVALID_PHONE",
457
+ INVALID_EMAIL: "INVALID_EMAIL",
458
+ NO_ACTIVE_SESSION: "NO_ACTIVE_SESSION",
459
+ OTP_EXPIRED: "OTP_EXPIRED",
460
+ OTP_TOO_MANY_ATTEMPTS: "OTP_TOO_MANY_ATTEMPTS",
461
+ OTP_INVALID: "OTP_INVALID",
462
+ INVALID_TOKEN: "INVALID_TOKEN",
463
+ // Ordering
464
+ ORDER_DISABLED: "ORDER_DISABLED",
465
+ TAKEAWAY_DISABLED: "TAKEAWAY_DISABLED",
466
+ DINEIN_DISABLED: "DINEIN_DISABLED",
467
+ MAX_ORDERS_REACHED: "MAX_ORDERS_REACHED",
468
+ EMPTY_ORDER: "EMPTY_ORDER",
469
+ MEAL_NOT_FOUND: "MEAL_NOT_FOUND",
470
+ MEAL_UNAVAILABLE: "MEAL_UNAVAILABLE",
471
+ ORDER_NOT_FOUND: "ORDER_NOT_FOUND",
472
+ INVALID_STATUS_TRANSITION: "INVALID_STATUS_TRANSITION",
473
+ CANNOT_CANCEL_ORDER: "CANNOT_CANCEL_ORDER",
474
+ // Payment
475
+ PAYMENT_ORDER_NOT_FOUND: "PAYMENT_ORDER_NOT_FOUND",
476
+ PAYMENT_INVALID_ORDER_STATUS: "PAYMENT_INVALID_ORDER_STATUS",
477
+ PAYMENT_NOT_FOUND: "PAYMENT_NOT_FOUND",
478
+ PAYMENT_CANNOT_REFUND: "PAYMENT_CANNOT_REFUND",
479
+ PAYMENT_FAILED: "PAYMENT_FAILED",
480
+ // Loyalty
481
+ LOYALTY_ACCOUNT_NOT_FOUND: "LOYALTY_ACCOUNT_NOT_FOUND",
482
+ REWARD_NOT_FOUND: "REWARD_NOT_FOUND",
483
+ REWARD_INACTIVE: "REWARD_INACTIVE",
484
+ NOT_ENOUGH_POINTS: "NOT_ENOUGH_POINTS"
485
+ };
486
+ var AppError = class extends Error {
487
+ constructor(code, statusCode, message, meta) {
488
+ super(message);
489
+ this.name = "AppError";
490
+ this.code = code;
491
+ this.statusCode = statusCode;
492
+ this.meta = meta;
493
+ }
494
+ };
495
+ export {
496
+ AppError,
497
+ BaseClient,
498
+ BestrawClient,
499
+ BestrawError,
500
+ BlogNamespace,
501
+ CustomerNamespace,
502
+ ErrorCode,
503
+ LoyaltyNamespace,
504
+ MenuNamespace,
505
+ OrderingNamespace,
506
+ PaymentNamespace,
507
+ RestaurantNamespace
508
+ };
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "bestraw-sdk",
3
+ "version": "1.0.0",
4
+ "description": "SDK for BeStraw restaurant platform",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist/"
19
+ ],
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "devDependencies": {
24
+ "tsup": "^8.0.0",
25
+ "typescript": "^5.0.0"
26
+ },
27
+ "scripts": {
28
+ "build": "tsup src/index.ts --format cjs,esm --dts",
29
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
30
+ "clean": "rm -rf dist"
31
+ }
32
+ }