@whatalo/plugin-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 (55) hide show
  1. package/dist/adapters/express.cjs +87 -0
  2. package/dist/adapters/express.cjs.map +1 -0
  3. package/dist/adapters/express.d.cts +17 -0
  4. package/dist/adapters/express.d.ts +17 -0
  5. package/dist/adapters/express.mjs +60 -0
  6. package/dist/adapters/express.mjs.map +1 -0
  7. package/dist/adapters/hono.cjs +79 -0
  8. package/dist/adapters/hono.cjs.map +1 -0
  9. package/dist/adapters/hono.d.cts +15 -0
  10. package/dist/adapters/hono.d.ts +15 -0
  11. package/dist/adapters/hono.mjs +52 -0
  12. package/dist/adapters/hono.mjs.map +1 -0
  13. package/dist/adapters/nextjs.cjs +79 -0
  14. package/dist/adapters/nextjs.cjs.map +1 -0
  15. package/dist/adapters/nextjs.d.cts +7 -0
  16. package/dist/adapters/nextjs.d.ts +7 -0
  17. package/dist/adapters/nextjs.mjs +52 -0
  18. package/dist/adapters/nextjs.mjs.map +1 -0
  19. package/dist/bridge/index.cjs +290 -0
  20. package/dist/bridge/index.cjs.map +1 -0
  21. package/dist/bridge/index.d.cts +236 -0
  22. package/dist/bridge/index.d.ts +236 -0
  23. package/dist/bridge/index.mjs +260 -0
  24. package/dist/bridge/index.mjs.map +1 -0
  25. package/dist/client/index.cjs +423 -0
  26. package/dist/client/index.cjs.map +1 -0
  27. package/dist/client/index.d.cts +131 -0
  28. package/dist/client/index.d.ts +131 -0
  29. package/dist/client/index.mjs +396 -0
  30. package/dist/client/index.mjs.map +1 -0
  31. package/dist/index.cjs +843 -0
  32. package/dist/index.cjs.map +1 -0
  33. package/dist/index.d.cts +57 -0
  34. package/dist/index.d.ts +57 -0
  35. package/dist/index.mjs +801 -0
  36. package/dist/index.mjs.map +1 -0
  37. package/dist/manifest/index.cjs +145 -0
  38. package/dist/manifest/index.cjs.map +1 -0
  39. package/dist/manifest/index.d.cts +78 -0
  40. package/dist/manifest/index.d.ts +78 -0
  41. package/dist/manifest/index.mjs +117 -0
  42. package/dist/manifest/index.mjs.map +1 -0
  43. package/dist/types-D2Efg3EG.d.ts +19 -0
  44. package/dist/types-DZ659i6f.d.ts +68 -0
  45. package/dist/types-Db_BeRCj.d.cts +19 -0
  46. package/dist/types-DdqKKyqX.d.cts +68 -0
  47. package/dist/types-M1eLMz6w.d.cts +279 -0
  48. package/dist/types-M1eLMz6w.d.ts +279 -0
  49. package/dist/webhooks/index.cjs +50 -0
  50. package/dist/webhooks/index.cjs.map +1 -0
  51. package/dist/webhooks/index.d.cts +18 -0
  52. package/dist/webhooks/index.d.ts +18 -0
  53. package/dist/webhooks/index.mjs +23 -0
  54. package/dist/webhooks/index.mjs.map +1 -0
  55. package/package.json +94 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,843 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ AuthenticationError: () => AuthenticationError,
24
+ AuthorizationError: () => AuthorizationError,
25
+ BILLING_OPERATION: () => BILLING_OPERATION,
26
+ InternalError: () => InternalError,
27
+ ManifestValidationError: () => ManifestValidationError,
28
+ NotFoundError: () => NotFoundError,
29
+ RateLimitError: () => RateLimitError,
30
+ SDK_VERSION: () => SDK_VERSION,
31
+ ValidationError: () => ValidationError,
32
+ WhataloAPIError: () => WhataloAPIError,
33
+ WhataloClient: () => WhataloClient,
34
+ defineApp: () => defineApp,
35
+ useAppBridge: () => useAppBridge,
36
+ useWhataloAction: () => useWhataloAction,
37
+ useWhataloContext: () => useWhataloContext,
38
+ verifyWebhook: () => verifyWebhook
39
+ });
40
+ module.exports = __toCommonJS(src_exports);
41
+
42
+ // src/client/errors.ts
43
+ var WhataloAPIError = class extends Error {
44
+ statusCode;
45
+ code;
46
+ requestId;
47
+ constructor(message, statusCode, code, requestId) {
48
+ super(message);
49
+ this.name = "WhataloAPIError";
50
+ this.statusCode = statusCode;
51
+ this.code = code;
52
+ this.requestId = requestId;
53
+ }
54
+ };
55
+ var AuthenticationError = class extends WhataloAPIError {
56
+ constructor(message = "Invalid or missing API key", requestId) {
57
+ super(message, 401, "authentication_error", requestId);
58
+ this.name = "AuthenticationError";
59
+ }
60
+ };
61
+ var AuthorizationError = class extends WhataloAPIError {
62
+ requiredScope;
63
+ constructor(message, requiredScope, requestId) {
64
+ super(message, 403, "authorization_error", requestId);
65
+ this.name = "AuthorizationError";
66
+ this.requiredScope = requiredScope;
67
+ }
68
+ };
69
+ var InsufficientScopeError = class extends WhataloAPIError {
70
+ /** The exact scope string missing from the installation's granted_scopes */
71
+ requiredScope;
72
+ /** The scopes currently granted to this installation */
73
+ grantedScopes;
74
+ constructor(message, requiredScope, grantedScopes, requestId) {
75
+ super(message, 403, "insufficient_scope", requestId);
76
+ this.name = "InsufficientScopeError";
77
+ this.requiredScope = requiredScope;
78
+ this.grantedScopes = grantedScopes;
79
+ }
80
+ };
81
+ var NotFoundError = class extends WhataloAPIError {
82
+ resourceType;
83
+ resourceId;
84
+ constructor(resourceType, resourceId, requestId) {
85
+ super(
86
+ `${resourceType} '${resourceId}' not found`,
87
+ 404,
88
+ "not_found",
89
+ requestId
90
+ );
91
+ this.name = "NotFoundError";
92
+ this.resourceType = resourceType;
93
+ this.resourceId = resourceId;
94
+ }
95
+ };
96
+ var ValidationError = class extends WhataloAPIError {
97
+ fieldErrors;
98
+ constructor(fieldErrors, requestId) {
99
+ const message = `Validation failed: ${fieldErrors.map((e) => `${e.field} \u2014 ${e.message}`).join(", ")}`;
100
+ super(message, 422, "validation_error", requestId);
101
+ this.name = "ValidationError";
102
+ this.fieldErrors = fieldErrors;
103
+ }
104
+ };
105
+ var RateLimitError = class extends WhataloAPIError {
106
+ /** Seconds until the rate limit resets */
107
+ retryAfter;
108
+ constructor(retryAfter, requestId) {
109
+ super(
110
+ `Rate limit exceeded. Retry after ${retryAfter} seconds.`,
111
+ 429,
112
+ "rate_limit_exceeded",
113
+ requestId
114
+ );
115
+ this.name = "RateLimitError";
116
+ this.retryAfter = retryAfter;
117
+ }
118
+ };
119
+ var InternalError = class extends WhataloAPIError {
120
+ constructor(message = "An internal error occurred", requestId) {
121
+ super(message, 500, "internal_error", requestId);
122
+ this.name = "InternalError";
123
+ }
124
+ };
125
+
126
+ // src/client/whatalo-client.ts
127
+ var DEFAULT_BASE_URL = "https://api.whatalo.com/v1";
128
+ var DEFAULT_TIMEOUT = 3e4;
129
+ var WhataloClient = class {
130
+ apiKey;
131
+ baseUrl;
132
+ timeout;
133
+ maxRetries;
134
+ fetchFn;
135
+ onRequest;
136
+ onResponse;
137
+ /** Rate limit info from the most recent API response */
138
+ rateLimit = { limit: 0, remaining: 0, reset: 0 };
139
+ /** Product resource methods */
140
+ products;
141
+ /** Order resource methods */
142
+ orders;
143
+ /** Customer resource methods */
144
+ customers;
145
+ /** Category resource methods */
146
+ categories;
147
+ /** Discount resource methods */
148
+ discounts;
149
+ /** Store resource methods */
150
+ store;
151
+ /** Inventory resource methods */
152
+ inventory;
153
+ /** Webhook resource methods */
154
+ webhooks;
155
+ constructor(options) {
156
+ this.apiKey = options.apiKey;
157
+ this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
158
+ this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
159
+ this.maxRetries = Math.min(options.retries ?? 0, 3);
160
+ this.fetchFn = options.fetch ?? globalThis.fetch;
161
+ this.onRequest = options.onRequest;
162
+ this.onResponse = options.onResponse;
163
+ this.products = new ProductResource(this);
164
+ this.orders = new OrderResource(this);
165
+ this.customers = new CustomerResource(this);
166
+ this.categories = new CategoryResource(this);
167
+ this.discounts = new DiscountResource(this);
168
+ this.store = new StoreResource(this);
169
+ this.inventory = new InventoryResource(this);
170
+ this.webhooks = new WebhookResource(this);
171
+ }
172
+ /**
173
+ * Internal request method shared by all resource namespaces.
174
+ * Handles headers, timeout, error parsing, and rate limit extraction.
175
+ */
176
+ async request(method, path, options) {
177
+ let url = `${this.baseUrl}${path}`;
178
+ if (options?.params) {
179
+ const searchParams = new URLSearchParams();
180
+ for (const [key, value] of Object.entries(options.params)) {
181
+ if (value !== void 0) {
182
+ searchParams.set(key, String(value));
183
+ }
184
+ }
185
+ const qs = searchParams.toString();
186
+ if (qs) url += `?${qs}`;
187
+ }
188
+ const headers = {
189
+ "X-API-Key": this.apiKey,
190
+ "Content-Type": "application/json"
191
+ };
192
+ let lastError = null;
193
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
194
+ const controller = new AbortController();
195
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
196
+ const requestInit = {
197
+ method,
198
+ headers,
199
+ body: options?.body ? JSON.stringify(options.body) : void 0,
200
+ signal: controller.signal
201
+ };
202
+ const startedAt = Date.now();
203
+ this.onRequest?.(url, requestInit);
204
+ try {
205
+ const response = await this.fetchFn(url, requestInit);
206
+ clearTimeout(timeoutId);
207
+ this.onResponse?.(response, Date.now() - startedAt);
208
+ this.rateLimit = {
209
+ limit: Number(response.headers.get("X-RateLimit-Limit") ?? 0),
210
+ remaining: Number(
211
+ response.headers.get("X-RateLimit-Remaining") ?? 0
212
+ ),
213
+ reset: Number(response.headers.get("X-RateLimit-Reset") ?? 0)
214
+ };
215
+ const requestId = response.headers.get("X-Request-Id") ?? void 0;
216
+ if (response.ok) {
217
+ return await response.json();
218
+ }
219
+ const errorBody = await response.json().catch(() => ({
220
+ error: { code: "unknown", message: response.statusText }
221
+ }));
222
+ const errData = errorBody.error;
223
+ switch (response.status) {
224
+ case 401:
225
+ throw new AuthenticationError(errData?.message, requestId);
226
+ case 403:
227
+ if (errData?.code === "insufficient_scope") {
228
+ const details = errorBody;
229
+ throw new InsufficientScopeError(
230
+ errData.message ?? "Insufficient scope",
231
+ details.error?.required ?? "",
232
+ details.error?.granted ?? [],
233
+ requestId
234
+ );
235
+ }
236
+ throw new AuthorizationError(
237
+ errData?.message ?? "Forbidden",
238
+ errData?.code ?? "unknown_scope",
239
+ requestId
240
+ );
241
+ case 404:
242
+ throw new NotFoundError("Resource", path, requestId);
243
+ case 422:
244
+ throw new ValidationError(
245
+ errData?.details ?? [
246
+ { field: "unknown", message: errData?.message ?? "Validation failed" }
247
+ ],
248
+ requestId
249
+ );
250
+ case 429: {
251
+ const retryAfter = Number(
252
+ response.headers.get("Retry-After") ?? 60
253
+ );
254
+ throw new RateLimitError(retryAfter, requestId);
255
+ }
256
+ default:
257
+ if (response.status >= 500 && attempt < this.maxRetries) {
258
+ lastError = new InternalError(errData?.message, requestId);
259
+ await this.sleep(Math.pow(2, attempt) * 1e3);
260
+ continue;
261
+ }
262
+ throw new WhataloAPIError(
263
+ errData?.message ?? "API error",
264
+ response.status,
265
+ errData?.code ?? "unknown",
266
+ requestId
267
+ );
268
+ }
269
+ } catch (err) {
270
+ clearTimeout(timeoutId);
271
+ if (err instanceof WhataloAPIError) throw err;
272
+ if (err.name === "AbortError") {
273
+ throw new WhataloAPIError(
274
+ "Request timed out",
275
+ 408,
276
+ "timeout"
277
+ );
278
+ }
279
+ throw err;
280
+ }
281
+ }
282
+ throw lastError ?? new InternalError("Request failed after retries");
283
+ }
284
+ sleep(ms) {
285
+ return new Promise((resolve) => setTimeout(resolve, ms));
286
+ }
287
+ };
288
+ var ProductResource = class {
289
+ constructor(client) {
290
+ this.client = client;
291
+ }
292
+ async list(params) {
293
+ return this.client.request("GET", "/products", {
294
+ params
295
+ });
296
+ }
297
+ async get(id) {
298
+ return this.client.request("GET", `/products/${id}`);
299
+ }
300
+ async create(data) {
301
+ return this.client.request("POST", "/products", { body: data });
302
+ }
303
+ async update(id, data) {
304
+ return this.client.request("PATCH", `/products/${id}`, { body: data });
305
+ }
306
+ async delete(id) {
307
+ return this.client.request("DELETE", `/products/${id}`);
308
+ }
309
+ async count(status) {
310
+ return this.client.request("GET", "/products/count", {
311
+ params: status ? { status } : void 0
312
+ });
313
+ }
314
+ };
315
+ var OrderResource = class {
316
+ constructor(client) {
317
+ this.client = client;
318
+ }
319
+ async list(params) {
320
+ return this.client.request("GET", "/orders", {
321
+ params
322
+ });
323
+ }
324
+ async get(id) {
325
+ return this.client.request("GET", `/orders/${id}`);
326
+ }
327
+ async updateStatus(id, data) {
328
+ return this.client.request("PATCH", `/orders/${id}`, { body: data });
329
+ }
330
+ async count(status) {
331
+ return this.client.request("GET", "/orders/count", {
332
+ params: status ? { status } : void 0
333
+ });
334
+ }
335
+ };
336
+ var CustomerResource = class {
337
+ constructor(client) {
338
+ this.client = client;
339
+ }
340
+ async list(params) {
341
+ return this.client.request("GET", "/customers", {
342
+ params
343
+ });
344
+ }
345
+ async get(id) {
346
+ return this.client.request("GET", `/customers/${id}`);
347
+ }
348
+ };
349
+ var CategoryResource = class {
350
+ constructor(client) {
351
+ this.client = client;
352
+ }
353
+ async list(params) {
354
+ return this.client.request("GET", "/categories", {
355
+ params
356
+ });
357
+ }
358
+ async get(id) {
359
+ return this.client.request("GET", `/categories/${id}`);
360
+ }
361
+ async create(data) {
362
+ return this.client.request("POST", "/categories", { body: data });
363
+ }
364
+ async update(id, data) {
365
+ return this.client.request("PATCH", `/categories/${id}`, { body: data });
366
+ }
367
+ async delete(id) {
368
+ return this.client.request("DELETE", `/categories/${id}`);
369
+ }
370
+ async count() {
371
+ return this.client.request("GET", "/categories/count");
372
+ }
373
+ };
374
+ var DiscountResource = class {
375
+ constructor(client) {
376
+ this.client = client;
377
+ }
378
+ async list(params) {
379
+ return this.client.request("GET", "/discounts", {
380
+ params
381
+ });
382
+ }
383
+ async get(id) {
384
+ return this.client.request("GET", `/discounts/${id}`);
385
+ }
386
+ async create(data) {
387
+ return this.client.request("POST", "/discounts", { body: data });
388
+ }
389
+ async update(id, data) {
390
+ return this.client.request("PATCH", `/discounts/${id}`, { body: data });
391
+ }
392
+ async delete(id) {
393
+ return this.client.request("DELETE", `/discounts/${id}`);
394
+ }
395
+ };
396
+ var StoreResource = class {
397
+ constructor(client) {
398
+ this.client = client;
399
+ }
400
+ async get() {
401
+ return this.client.request("GET", "/store");
402
+ }
403
+ };
404
+ var InventoryResource = class {
405
+ constructor(client) {
406
+ this.client = client;
407
+ }
408
+ async get(productId) {
409
+ return this.client.request("GET", `/products/${productId}/inventory`);
410
+ }
411
+ async adjust(productId, data) {
412
+ return this.client.request("PATCH", `/products/${productId}/inventory`, {
413
+ body: data
414
+ });
415
+ }
416
+ };
417
+ var WebhookResource = class {
418
+ constructor(client) {
419
+ this.client = client;
420
+ }
421
+ async list() {
422
+ return this.client.request("GET", "/webhooks");
423
+ }
424
+ async create(data) {
425
+ return this.client.request("POST", "/webhooks", { body: data });
426
+ }
427
+ async update(id, data) {
428
+ return this.client.request("PATCH", `/webhooks/${id}`, { body: data });
429
+ }
430
+ async delete(id) {
431
+ return this.client.request("DELETE", `/webhooks/${id}`);
432
+ }
433
+ };
434
+
435
+ // src/webhooks/verify.ts
436
+ var import_node_crypto = require("crypto");
437
+ function verifyWebhook({
438
+ payload,
439
+ signature,
440
+ secret
441
+ }) {
442
+ if (!payload || !signature || !secret) return false;
443
+ const expected = (0, import_node_crypto.createHmac)("sha256", secret).update(payload, "utf8").digest("hex");
444
+ if (expected.length !== signature.length) return false;
445
+ try {
446
+ return (0, import_node_crypto.timingSafeEqual)(
447
+ Buffer.from(expected, "hex"),
448
+ Buffer.from(signature, "hex")
449
+ );
450
+ } catch {
451
+ return false;
452
+ }
453
+ }
454
+
455
+ // src/manifest/validate.ts
456
+ var ManifestValidationError = class extends Error {
457
+ errors;
458
+ constructor(errors) {
459
+ const message = `Invalid app manifest: ${errors.map((e) => `${e.field}: ${e.message}`).join("; ")}`;
460
+ super(message);
461
+ this.name = "ManifestValidationError";
462
+ this.errors = errors;
463
+ }
464
+ };
465
+ var ID_REGEX = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
466
+ var SEMVER_REGEX = /^\d+\.\d+\.\d+$/;
467
+ var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
468
+ var VALID_PERMISSIONS = /* @__PURE__ */ new Set([
469
+ "read:products",
470
+ "write:products",
471
+ "read:orders",
472
+ "write:orders",
473
+ "read:customers",
474
+ "write:customers",
475
+ "read:inventory",
476
+ "write:inventory",
477
+ "read:store",
478
+ "write:store",
479
+ "read:webhooks",
480
+ "write:webhooks",
481
+ "read:discounts",
482
+ "write:discounts",
483
+ "read:analytics"
484
+ ]);
485
+ var VALID_CATEGORIES = /* @__PURE__ */ new Set([
486
+ "analytics",
487
+ "marketing",
488
+ "shipping",
489
+ "payments",
490
+ "payment",
491
+ "inventory",
492
+ "communication",
493
+ "productivity",
494
+ "accounting",
495
+ "developer",
496
+ "other"
497
+ ]);
498
+ function defineApp(manifest) {
499
+ const errors = [];
500
+ if (!manifest.id || manifest.id.length < 3 || manifest.id.length > 50) {
501
+ errors.push({ field: "id", message: "Must be 3-50 characters" });
502
+ } else if (!ID_REGEX.test(manifest.id)) {
503
+ errors.push({
504
+ field: "id",
505
+ message: "Must be lowercase alphanumeric with hyphens"
506
+ });
507
+ }
508
+ if (!manifest.name || manifest.name.length < 3 || manifest.name.length > 50) {
509
+ errors.push({ field: "name", message: "Must be 3-50 characters" });
510
+ }
511
+ if (!manifest.description || manifest.description.length < 10 || manifest.description.length > 200) {
512
+ errors.push({
513
+ field: "description",
514
+ message: "Must be 10-200 characters"
515
+ });
516
+ }
517
+ if (!manifest.version || !SEMVER_REGEX.test(manifest.version)) {
518
+ errors.push({
519
+ field: "version",
520
+ message: "Must be valid semver (e.g., 1.0.0)"
521
+ });
522
+ }
523
+ if (!manifest.author?.name) {
524
+ errors.push({ field: "author.name", message: "Required" });
525
+ }
526
+ if (!manifest.author?.email || !EMAIL_REGEX.test(manifest.author.email)) {
527
+ errors.push({
528
+ field: "author.email",
529
+ message: "Must be a valid email address"
530
+ });
531
+ }
532
+ if (!manifest.permissions || manifest.permissions.length === 0) {
533
+ errors.push({
534
+ field: "permissions",
535
+ message: "At least one permission is required"
536
+ });
537
+ } else {
538
+ for (const perm of manifest.permissions) {
539
+ if (!VALID_PERMISSIONS.has(perm)) {
540
+ errors.push({
541
+ field: "permissions",
542
+ message: `Invalid permission: ${perm}`
543
+ });
544
+ }
545
+ }
546
+ }
547
+ if (!manifest.appUrl) {
548
+ errors.push({ field: "appUrl", message: "Required" });
549
+ }
550
+ if (manifest.pricing !== "free" && manifest.pricing !== "paid") {
551
+ errors.push({
552
+ field: "pricing",
553
+ message: 'Must be "free" or "paid"'
554
+ });
555
+ }
556
+ if (!VALID_CATEGORIES.has(manifest.category)) {
557
+ errors.push({
558
+ field: "category",
559
+ message: `Invalid category: ${manifest.category}`
560
+ });
561
+ }
562
+ if (errors.length > 0) {
563
+ throw new ManifestValidationError(errors);
564
+ }
565
+ return manifest;
566
+ }
567
+
568
+ // src/bridge/use-whatalo-context.ts
569
+ var import_react2 = require("react");
570
+
571
+ // src/bridge/use-whatalo-action.ts
572
+ var import_react = require("react");
573
+
574
+ // src/bridge/types.ts
575
+ var BILLING_OPERATION = {
576
+ GET_PLANS: "getPlans",
577
+ GET_SUBSCRIPTION: "getSubscription",
578
+ REQUEST_SUBSCRIPTION: "requestSubscription",
579
+ CANCEL_SUBSCRIPTION: "cancelSubscription",
580
+ REACTIVATE_SUBSCRIPTION: "reactivateSubscription",
581
+ SWITCH_PLAN: "switchPlan",
582
+ CREATE_USAGE_RECORD: "createUsageRecord"
583
+ };
584
+
585
+ // src/bridge/use-whatalo-action.ts
586
+ var ACK_TIMEOUT_MS = 5e3;
587
+ var parentOrigin = null;
588
+ var originResolvers = [];
589
+ function waitForParentOrigin() {
590
+ if (parentOrigin !== null) {
591
+ return Promise.resolve(parentOrigin);
592
+ }
593
+ return new Promise((resolve) => {
594
+ originResolvers.push(resolve);
595
+ });
596
+ }
597
+ var initListenerAttached = false;
598
+ function attachInitListener() {
599
+ if (initListenerAttached) return;
600
+ initListenerAttached = true;
601
+ window.addEventListener("message", (event) => {
602
+ if (event.source !== window.parent) return;
603
+ if (!event.data || typeof event.data !== "object") return;
604
+ if (event.data.type !== "whatalo:init") return;
605
+ const origin = event.data.origin;
606
+ if (typeof origin !== "string" || !origin) return;
607
+ parentOrigin = origin;
608
+ for (const resolve of originResolvers) {
609
+ resolve(origin);
610
+ }
611
+ originResolvers.length = 0;
612
+ });
613
+ }
614
+ function useWhataloAction() {
615
+ const pendingAcks = (0, import_react.useRef)(
616
+ /* @__PURE__ */ new Map()
617
+ );
618
+ (0, import_react.useEffect)(() => {
619
+ attachInitListener();
620
+ const handleMessage = (event) => {
621
+ if (!event.data || typeof event.data !== "object") return;
622
+ const msg = event.data;
623
+ if (msg.type !== "whatalo:ack") return;
624
+ const resolve = pendingAcks.current.get(msg.actionId);
625
+ if (resolve) {
626
+ resolve(msg);
627
+ pendingAcks.current.delete(msg.actionId);
628
+ }
629
+ };
630
+ window.addEventListener("message", handleMessage);
631
+ return () => window.removeEventListener("message", handleMessage);
632
+ }, []);
633
+ const sendAction = (0, import_react.useCallback)(
634
+ (action, payload) => {
635
+ return new Promise((resolve) => {
636
+ const actionId = crypto.randomUUID();
637
+ const timeout = setTimeout(() => {
638
+ pendingAcks.current.delete(actionId);
639
+ resolve({
640
+ type: "whatalo:ack",
641
+ actionId,
642
+ success: false,
643
+ error: "timeout"
644
+ });
645
+ }, ACK_TIMEOUT_MS);
646
+ pendingAcks.current.set(actionId, (ack) => {
647
+ clearTimeout(timeout);
648
+ resolve(ack);
649
+ });
650
+ waitForParentOrigin().then((origin) => {
651
+ window.parent.postMessage(
652
+ { type: "whatalo:action", actionId, action, payload },
653
+ origin
654
+ );
655
+ });
656
+ });
657
+ },
658
+ []
659
+ );
660
+ const showToast = (0, import_react.useCallback)(
661
+ (options) => sendAction("toast", options),
662
+ [sendAction]
663
+ );
664
+ const navigate = (0, import_react.useCallback)(
665
+ (path) => sendAction("navigate", { path }),
666
+ [sendAction]
667
+ );
668
+ const openModal = (0, import_react.useCallback)(
669
+ (options) => sendAction("modal", {
670
+ operation: "open",
671
+ ...options
672
+ }),
673
+ [sendAction]
674
+ );
675
+ const closeModal = (0, import_react.useCallback)(
676
+ () => sendAction("modal", { operation: "close" }),
677
+ [sendAction]
678
+ );
679
+ const resize = (0, import_react.useCallback)(
680
+ (height) => sendAction("resize", { height }),
681
+ [sendAction]
682
+ );
683
+ const sendBillingAction = (0, import_react.useCallback)(
684
+ (operation, extra) => sendAction("billing", {
685
+ operation,
686
+ ...extra
687
+ }),
688
+ [sendAction]
689
+ );
690
+ const billingGetPlans = (0, import_react.useCallback)(async () => {
691
+ const ack = await sendBillingAction(BILLING_OPERATION.GET_PLANS);
692
+ if (!ack.success) {
693
+ throw new Error(ack.error ?? "billing.getPlans failed");
694
+ }
695
+ return ack.data ?? [];
696
+ }, [sendBillingAction]);
697
+ const billingGetSubscription = (0, import_react.useCallback)(
698
+ async () => {
699
+ const ack = await sendBillingAction(BILLING_OPERATION.GET_SUBSCRIPTION);
700
+ if (!ack.success) {
701
+ throw new Error(ack.error ?? "billing.getSubscription failed");
702
+ }
703
+ return ack.data ?? null;
704
+ },
705
+ [sendBillingAction]
706
+ );
707
+ const billingRequestSubscription = (0, import_react.useCallback)(
708
+ async (planSlug) => {
709
+ const ack = await sendBillingAction(BILLING_OPERATION.REQUEST_SUBSCRIPTION, {
710
+ planSlug
711
+ });
712
+ if (!ack.success) {
713
+ throw new Error(ack.error ?? "billing.requestSubscription failed");
714
+ }
715
+ },
716
+ [sendBillingAction]
717
+ );
718
+ const billingCancelSubscription = (0, import_react.useCallback)(async () => {
719
+ const ack = await sendBillingAction(BILLING_OPERATION.CANCEL_SUBSCRIPTION);
720
+ if (!ack.success) {
721
+ throw new Error(ack.error ?? "billing.cancelSubscription failed");
722
+ }
723
+ }, [sendBillingAction]);
724
+ const billingReactivateSubscription = (0, import_react.useCallback)(async () => {
725
+ const ack = await sendBillingAction(BILLING_OPERATION.REACTIVATE_SUBSCRIPTION);
726
+ if (!ack.success) {
727
+ throw new Error(ack.error ?? "billing.reactivateSubscription failed");
728
+ }
729
+ }, [sendBillingAction]);
730
+ const billingSwitchPlan = (0, import_react.useCallback)(
731
+ async (newPlanSlug) => {
732
+ const ack = await sendBillingAction(BILLING_OPERATION.SWITCH_PLAN, {
733
+ planSlug: newPlanSlug
734
+ });
735
+ if (!ack.success) {
736
+ throw new Error(ack.error ?? "billing.switchPlan failed");
737
+ }
738
+ },
739
+ [sendBillingAction]
740
+ );
741
+ const billing = {
742
+ getPlans: billingGetPlans,
743
+ getSubscription: billingGetSubscription,
744
+ requestSubscription: billingRequestSubscription,
745
+ cancelSubscription: billingCancelSubscription,
746
+ reactivateSubscription: billingReactivateSubscription,
747
+ switchPlan: billingSwitchPlan
748
+ };
749
+ return { sendAction, showToast, navigate, openModal, closeModal, resize, billing };
750
+ }
751
+
752
+ // src/bridge/use-whatalo-context.ts
753
+ var DEFAULT_CONTEXT = {
754
+ storeId: "",
755
+ storeName: "",
756
+ user: { id: "", name: "", email: "", role: "owner" },
757
+ appId: "",
758
+ currentPage: "",
759
+ locale: "es",
760
+ theme: "light",
761
+ initialHeight: 200
762
+ };
763
+ function useWhataloContext() {
764
+ const [context, setContext] = (0, import_react2.useState)(DEFAULT_CONTEXT);
765
+ const [isReady, setIsReady] = (0, import_react2.useState)(false);
766
+ const handleMessage = (0, import_react2.useCallback)((event) => {
767
+ if (!event.data || typeof event.data !== "object") return;
768
+ const msg = event.data;
769
+ if (msg.type !== "whatalo:context") return;
770
+ const payload = msg.data ?? msg.context;
771
+ if (!payload || typeof payload.storeId !== "string") return;
772
+ setContext(payload);
773
+ setIsReady(true);
774
+ if (payload.theme) {
775
+ document.documentElement.setAttribute("data-theme", payload.theme);
776
+ }
777
+ }, []);
778
+ (0, import_react2.useEffect)(() => {
779
+ attachInitListener();
780
+ window.addEventListener("message", handleMessage);
781
+ waitForParentOrigin().then((origin) => {
782
+ window.parent.postMessage(
783
+ {
784
+ type: "whatalo:action",
785
+ actionId: crypto.randomUUID(),
786
+ action: "ready",
787
+ payload: {}
788
+ },
789
+ origin
790
+ );
791
+ });
792
+ return () => window.removeEventListener("message", handleMessage);
793
+ }, [handleMessage]);
794
+ return { ...context, isReady };
795
+ }
796
+
797
+ // src/bridge/use-app-bridge.ts
798
+ function useAppBridge() {
799
+ const ctx = useWhataloContext();
800
+ const actions = useWhataloAction();
801
+ return {
802
+ storeId: ctx.storeId,
803
+ storeName: ctx.storeName,
804
+ locale: ctx.locale,
805
+ theme: ctx.theme,
806
+ user: ctx.user,
807
+ appId: ctx.appId,
808
+ isReady: ctx.isReady,
809
+ toast: {
810
+ show: (title, options) => actions.showToast({ title, ...options })
811
+ },
812
+ navigate: actions.navigate,
813
+ modal: {
814
+ open: actions.openModal,
815
+ close: actions.closeModal
816
+ },
817
+ resize: actions.resize,
818
+ billing: actions.billing
819
+ };
820
+ }
821
+
822
+ // src/index.ts
823
+ var SDK_VERSION = "0.0.1";
824
+ // Annotate the CommonJS export names for ESM import in node:
825
+ 0 && (module.exports = {
826
+ AuthenticationError,
827
+ AuthorizationError,
828
+ BILLING_OPERATION,
829
+ InternalError,
830
+ ManifestValidationError,
831
+ NotFoundError,
832
+ RateLimitError,
833
+ SDK_VERSION,
834
+ ValidationError,
835
+ WhataloAPIError,
836
+ WhataloClient,
837
+ defineApp,
838
+ useAppBridge,
839
+ useWhataloAction,
840
+ useWhataloContext,
841
+ verifyWebhook
842
+ });
843
+ //# sourceMappingURL=index.cjs.map