@cimplify/sdk 0.6.7 → 0.6.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -142,6 +142,25 @@ if (isOnSale(product)) {
142
142
  }
143
143
  ```
144
144
 
145
+ ### Type-Safe Money
146
+
147
+ All monetary values use the branded `Money` type: string at runtime, distinct from
148
+ plain `string` at compile time.
149
+
150
+ ```typescript
151
+ import { money, type Money } from "@cimplify/sdk";
152
+
153
+ const price = money("29.99");
154
+ const zero = money("0.00");
155
+
156
+ parseFloat(price); // 29.99
157
+ console.log(price); // "29.99"
158
+
159
+ // Compile-time safety:
160
+ // const bad: Money = "29.99"; // Error
161
+ // const bad2: Money = "coffee"; // Error
162
+ ```
163
+
145
164
  ## Error Handling
146
165
 
147
166
  ```typescript
@@ -1,5 +1,5 @@
1
- export { A as AuthService, B as BusinessService, i as CartOperations, b as CatalogueQueries, j as CheckoutOperations, j as CheckoutService, v as CimplifyElement, u as CimplifyElements, x as ELEMENT_TYPES, E as EVENT_TYPES, H as ElementEventType, z as ElementOptions, D as ElementType, y as ElementsOptions, F as FetchQuoteInput, r as FxService, G as GetProductsOptions, I as InventoryService, L as LinkService, q as LiteService, M as MESSAGE_TYPES, O as OrderQueries, P as PriceQuote, d as QuoteBundleSelectionInput, Q as QuoteCompositeSelectionInput, f as QuoteDynamicBuckets, e as QuoteStatus, g as QuoteUiMessage, R as RefreshQuoteInput, h as RefreshQuoteResult, p as SchedulingService, S as SearchOptions, w as createElements, k as generateIdempotencyKey } from './client-B4etj3AD.mjs';
2
- import './payment-pjpfIKX8.mjs';
1
+ export { A as AuthService, B as BusinessService, i as CartOperations, b as CatalogueQueries, j as CheckoutOperations, j as CheckoutService, v as CimplifyElement, u as CimplifyElements, x as ELEMENT_TYPES, E as EVENT_TYPES, H as ElementEventType, z as ElementOptions, D as ElementType, y as ElementsOptions, F as FetchQuoteInput, r as FxService, G as GetProductsOptions, I as InventoryService, L as LinkService, q as LiteService, M as MESSAGE_TYPES, O as OrderQueries, P as PriceQuote, d as QuoteBundleSelectionInput, Q as QuoteCompositeSelectionInput, f as QuoteDynamicBuckets, e as QuoteStatus, g as QuoteUiMessage, R as RefreshQuoteInput, h as RefreshQuoteResult, p as SchedulingService, S as SearchOptions, w as createElements, k as generateIdempotencyKey } from './client-vmXPt1j0.mjs';
2
+ import './payment-Cu75tmUc.mjs';
3
3
 
4
4
  type Operator = "==" | "!=" | ">" | "<" | ">=" | "<=" | "contains" | "startsWith";
5
5
  type SortOrder = "asc" | "desc";
@@ -1,5 +1,5 @@
1
- export { A as AuthService, B as BusinessService, i as CartOperations, b as CatalogueQueries, j as CheckoutOperations, j as CheckoutService, v as CimplifyElement, u as CimplifyElements, x as ELEMENT_TYPES, E as EVENT_TYPES, H as ElementEventType, z as ElementOptions, D as ElementType, y as ElementsOptions, F as FetchQuoteInput, r as FxService, G as GetProductsOptions, I as InventoryService, L as LinkService, q as LiteService, M as MESSAGE_TYPES, O as OrderQueries, P as PriceQuote, d as QuoteBundleSelectionInput, Q as QuoteCompositeSelectionInput, f as QuoteDynamicBuckets, e as QuoteStatus, g as QuoteUiMessage, R as RefreshQuoteInput, h as RefreshQuoteResult, p as SchedulingService, S as SearchOptions, w as createElements, k as generateIdempotencyKey } from './client-CYVVuP5J.js';
2
- import './payment-pjpfIKX8.js';
1
+ export { A as AuthService, B as BusinessService, i as CartOperations, b as CatalogueQueries, j as CheckoutOperations, j as CheckoutService, v as CimplifyElement, u as CimplifyElements, x as ELEMENT_TYPES, E as EVENT_TYPES, H as ElementEventType, z as ElementOptions, D as ElementType, y as ElementsOptions, F as FetchQuoteInput, r as FxService, G as GetProductsOptions, I as InventoryService, L as LinkService, q as LiteService, M as MESSAGE_TYPES, O as OrderQueries, P as PriceQuote, d as QuoteBundleSelectionInput, Q as QuoteCompositeSelectionInput, f as QuoteDynamicBuckets, e as QuoteStatus, g as QuoteUiMessage, R as RefreshQuoteInput, h as RefreshQuoteResult, p as SchedulingService, S as SearchOptions, w as createElements, k as generateIdempotencyKey } from './client-COpV6Yuu.js';
2
+ import './payment-Cu75tmUc.js';
3
3
 
4
4
  type Operator = "==" | "!=" | ">" | "<" | ">=" | "<=" | "contains" | "startsWith";
5
5
  type SortOrder = "asc" | "desc";
package/dist/advanced.js CHANGED
@@ -9,6 +9,15 @@ function err(error) {
9
9
  }
10
10
 
11
11
  // src/types/common.ts
12
+ function money(value) {
13
+ return value;
14
+ }
15
+ function moneyFromNumber(value) {
16
+ return value.toFixed(2);
17
+ }
18
+ function currencyCode(value) {
19
+ return value;
20
+ }
12
21
  var ErrorCode = {
13
22
  // General
14
23
  UNKNOWN_ERROR: "UNKNOWN_ERROR"};
@@ -108,6 +117,74 @@ function enrichError(error, options = {}) {
108
117
  return error;
109
118
  }
110
119
 
120
+ // src/query/builder.ts
121
+ function escapeQueryValue(value) {
122
+ return value.replace(/'/g, "\\'");
123
+ }
124
+ var QueryBuilder = class {
125
+ constructor(entity) {
126
+ this.filters = [];
127
+ this.modifiers = [];
128
+ this.pathSegments = [];
129
+ this.entity = entity;
130
+ }
131
+ path(segment) {
132
+ this.pathSegments.push(segment);
133
+ return this;
134
+ }
135
+ where(field, op, value) {
136
+ const v = typeof value === "string" ? `'${escapeQueryValue(value)}'` : value;
137
+ if (op === "contains" || op === "startsWith") {
138
+ this.filters.push(`@.${field} ${op} ${v}`);
139
+ } else {
140
+ this.filters.push(`@.${field}${op}${v}`);
141
+ }
142
+ return this;
143
+ }
144
+ and(field, op, value) {
145
+ return this.where(field, op, value);
146
+ }
147
+ sort(field, order = "asc") {
148
+ this.modifiers.push(`sort(${field},${order})`);
149
+ return this;
150
+ }
151
+ limit(n) {
152
+ this.modifiers.push(`limit(${n})`);
153
+ return this;
154
+ }
155
+ offset(n) {
156
+ this.modifiers.push(`offset(${n})`);
157
+ return this;
158
+ }
159
+ count() {
160
+ this.modifiers.push("count");
161
+ return this;
162
+ }
163
+ enriched() {
164
+ this.modifiers.push("enriched");
165
+ return this;
166
+ }
167
+ build() {
168
+ let query2 = this.entity;
169
+ if (this.pathSegments.length > 0) {
170
+ query2 += "." + this.pathSegments.join(".");
171
+ }
172
+ if (this.filters.length > 0) {
173
+ query2 += `[?(${this.filters.join(" && ")})]`;
174
+ }
175
+ for (const mod of this.modifiers) {
176
+ query2 += `#${mod}`;
177
+ }
178
+ return query2;
179
+ }
180
+ toString() {
181
+ return this.build();
182
+ }
183
+ };
184
+ function query(entity) {
185
+ return new QueryBuilder(entity);
186
+ }
187
+
111
188
  // src/catalogue.ts
112
189
  function toCimplifyError(error) {
113
190
  if (error instanceof CimplifyError) return enrichError(error);
@@ -188,7 +265,7 @@ var CatalogueQueries = class {
188
265
  let query2 = "products";
189
266
  const filters = [];
190
267
  if (options?.category) {
191
- filters.push(`@.category_id=='${options.category}'`);
268
+ filters.push(`@.category_id=='${escapeQueryValue(options.category)}'`);
192
269
  }
193
270
  if (options?.featured !== void 0) {
194
271
  filters.push(`@.featured==${options.featured}`);
@@ -197,7 +274,7 @@ var CatalogueQueries = class {
197
274
  filters.push(`@.in_stock==${options.in_stock}`);
198
275
  }
199
276
  if (options?.search) {
200
- filters.push(`@.name contains '${options.search}'`);
277
+ filters.push(`@.name contains '${escapeQueryValue(options.search)}'`);
201
278
  }
202
279
  if (options?.min_price !== void 0) {
203
280
  filters.push(`@.price>=${options.min_price}`);
@@ -228,7 +305,9 @@ var CatalogueQueries = class {
228
305
  }
229
306
  async getProductBySlug(slug) {
230
307
  const filteredResult = await safe(
231
- this.client.query(`products[?(@.slug=='${slug}')]`)
308
+ this.client.query(
309
+ `products[?(@.slug=='${escapeQueryValue(slug)}')]`
310
+ )
232
311
  );
233
312
  if (!filteredResult.ok) return filteredResult;
234
313
  const exactMatch = findProductBySlug(filteredResult.value, slug);
@@ -280,7 +359,7 @@ var CatalogueQueries = class {
280
359
  }
281
360
  async getCategoryBySlug(slug) {
282
361
  const result = await safe(
283
- this.client.query(`categories[?(@.slug=='${slug}')]`)
362
+ this.client.query(`categories[?(@.slug=='${escapeQueryValue(slug)}')]`)
284
363
  );
285
364
  if (!result.ok) return result;
286
365
  if (!result.value.length) {
@@ -289,7 +368,11 @@ var CatalogueQueries = class {
289
368
  return ok(result.value[0]);
290
369
  }
291
370
  async getCategoryProducts(categoryId) {
292
- return safe(this.client.query(`products[?(@.category_id=='${categoryId}')]`));
371
+ return safe(
372
+ this.client.query(
373
+ `products[?(@.category_id=='${escapeQueryValue(categoryId)}')]`
374
+ )
375
+ );
293
376
  }
294
377
  async getCollections() {
295
378
  return safe(this.client.query("collections"));
@@ -299,7 +382,9 @@ var CatalogueQueries = class {
299
382
  }
300
383
  async getCollectionBySlug(slug) {
301
384
  const result = await safe(
302
- this.client.query(`collections[?(@.slug=='${slug}')]`)
385
+ this.client.query(
386
+ `collections[?(@.slug=='${escapeQueryValue(slug)}')]`
387
+ )
303
388
  );
304
389
  if (!result.ok) return result;
305
390
  if (!result.value.length) {
@@ -312,7 +397,9 @@ var CatalogueQueries = class {
312
397
  }
313
398
  async searchCollections(query2, limit = 20) {
314
399
  return safe(
315
- this.client.query(`collections[?(@.name contains '${query2}')]#limit(${limit})`)
400
+ this.client.query(
401
+ `collections[?(@.name contains '${escapeQueryValue(query2)}')]#limit(${limit})`
402
+ )
316
403
  );
317
404
  }
318
405
  async getBundles() {
@@ -323,7 +410,9 @@ var CatalogueQueries = class {
323
410
  }
324
411
  async getBundleBySlug(slug) {
325
412
  const result = await safe(
326
- this.client.query(`bundles[?(@.slug=='${slug}')]`)
413
+ this.client.query(
414
+ `bundles[?(@.slug=='${escapeQueryValue(slug)}')]`
415
+ )
327
416
  );
328
417
  if (!result.ok) return result;
329
418
  if (!result.value.length) {
@@ -333,7 +422,9 @@ var CatalogueQueries = class {
333
422
  }
334
423
  async searchBundles(query2, limit = 20) {
335
424
  return safe(
336
- this.client.query(`bundles[?(@.name contains '${query2}')]#limit(${limit})`)
425
+ this.client.query(
426
+ `bundles[?(@.name contains '${escapeQueryValue(query2)}')]#limit(${limit})`
427
+ )
337
428
  );
338
429
  }
339
430
  async getComposites(options) {
@@ -373,9 +464,9 @@ var CatalogueQueries = class {
373
464
  }
374
465
  async search(query2, options) {
375
466
  const limit = options?.limit ?? 20;
376
- let searchQuery = `products[?(@.name contains '${query2}')]`;
467
+ let searchQuery = `products[?(@.name contains '${escapeQueryValue(query2)}')]`;
377
468
  if (options?.category) {
378
- searchQuery = `products[?(@.name contains '${query2}' && @.category_id=='${options.category}')]`;
469
+ searchQuery = `products[?(@.name contains '${escapeQueryValue(query2)}' && @.category_id=='${escapeQueryValue(options.category)}')]`;
379
470
  }
380
471
  searchQuery += `#limit(${limit})`;
381
472
  return safe(this.client.query(searchQuery));
@@ -392,7 +483,7 @@ var CatalogueQueries = class {
392
483
  async getMenu(options) {
393
484
  let query2 = "menu";
394
485
  if (options?.category) {
395
- query2 = `menu[?(@.category=='${options.category}')]`;
486
+ query2 = `menu[?(@.category=='${escapeQueryValue(options.category)}')]`;
396
487
  }
397
488
  if (options?.limit) {
398
489
  query2 += `#limit(${options.limit})`;
@@ -539,9 +630,6 @@ var CartOperations = class {
539
630
 
540
631
  // src/constants.ts
541
632
  var LINK_QUERY = {
542
- DATA: "link.data",
543
- ADDRESSES: "link.addresses",
544
- MOBILE_MONEY: "link.mobile_money",
545
633
  PREFERENCES: "link.preferences"};
546
634
  var LINK_MUTATION = {
547
635
  CHECK_STATUS: "link.check_status",
@@ -550,12 +638,8 @@ var LINK_MUTATION = {
550
638
  UPDATE_PREFERENCES: "link.update_preferences",
551
639
  CREATE_ADDRESS: "link.create_address",
552
640
  UPDATE_ADDRESS: "link.update_address",
553
- DELETE_ADDRESS: "link.delete_address",
554
- SET_DEFAULT_ADDRESS: "link.set_default_address",
555
641
  TRACK_ADDRESS_USAGE: "link.track_address_usage",
556
642
  CREATE_MOBILE_MONEY: "link.create_mobile_money",
557
- DELETE_MOBILE_MONEY: "link.delete_mobile_money",
558
- SET_DEFAULT_MOBILE_MONEY: "link.set_default_mobile_money",
559
643
  TRACK_MOBILE_MONEY_USAGE: "link.track_mobile_money_usage",
560
644
  VERIFY_MOBILE_MONEY: "link.verify_mobile_money"};
561
645
  var AUTH_MUTATION = {
@@ -673,6 +757,8 @@ function normalizeStatusResponse(response) {
673
757
  }
674
758
  const res = response;
675
759
  const normalizedStatus = normalizePaymentStatusValue(res.status ?? void 0);
760
+ const normalizedAmount = typeof res.amount === "string" ? money(res.amount) : typeof res.amount === "number" && Number.isFinite(res.amount) ? moneyFromNumber(res.amount) : void 0;
761
+ const normalizedCurrency = typeof res.currency === "string" && res.currency.trim().length > 0 ? currencyCode(res.currency) : void 0;
676
762
  const paidValue = res.paid === true;
677
763
  const derivedPaid = paidValue || [
678
764
  "success",
@@ -685,8 +771,8 @@ function normalizeStatusResponse(response) {
685
771
  return {
686
772
  status: normalizedStatus,
687
773
  paid: derivedPaid,
688
- amount: res.amount,
689
- currency: res.currency,
774
+ amount: normalizedAmount,
775
+ currency: normalizedCurrency,
690
776
  reference: res.reference,
691
777
  message: res.message || ""
692
778
  };
@@ -1378,14 +1464,17 @@ var CheckoutService = class {
1378
1464
  pay_currency: data.pay_currency,
1379
1465
  fx_quote_id: data.fx_quote_id
1380
1466
  };
1381
- const baseCurrency = (cart.pricing.currency || checkoutData.pay_currency || "GHS").toUpperCase();
1467
+ const baseCurrency = currencyCode(
1468
+ (cart.pricing.currency || checkoutData.pay_currency || "GHS").toUpperCase()
1469
+ );
1382
1470
  const payCurrency = data.pay_currency?.trim().toUpperCase();
1471
+ const payCurrencyCode = payCurrency ? currencyCode(payCurrency) : void 0;
1383
1472
  const cartTotalAmount = Number.parseFloat(cart.pricing.total_price || "0");
1384
- if (payCurrency && payCurrency !== baseCurrency && !checkoutData.fx_quote_id && Number.isFinite(cartTotalAmount) && cartTotalAmount > 0) {
1473
+ if (payCurrencyCode && payCurrencyCode !== baseCurrency && !checkoutData.fx_quote_id && Number.isFinite(cartTotalAmount) && cartTotalAmount > 0) {
1385
1474
  const fxQuoteResult = await this.client.fx.lockQuote({
1386
1475
  from: baseCurrency,
1387
- to: payCurrency,
1388
- amount: cartTotalAmount
1476
+ to: payCurrencyCode,
1477
+ amount: cart.pricing.total_price
1389
1478
  });
1390
1479
  if (!fxQuoteResult.ok) {
1391
1480
  return ok(
@@ -1396,7 +1485,7 @@ var CheckoutService = class {
1396
1485
  )
1397
1486
  );
1398
1487
  }
1399
- checkoutData.pay_currency = payCurrency;
1488
+ checkoutData.pay_currency = payCurrencyCode;
1400
1489
  checkoutData.fx_quote_id = fxQuoteResult.value.id;
1401
1490
  }
1402
1491
  data.on_status_change?.("processing", {});
@@ -1501,6 +1590,9 @@ var LinkService = class {
1501
1590
  constructor(client) {
1502
1591
  this.client = client;
1503
1592
  }
1593
+ async getProfile() {
1594
+ return safe5(this.client.linkGet("/v1/link/profile"));
1595
+ }
1504
1596
  async requestOtp(input) {
1505
1597
  return safe5(this.client.linkPost("/v1/link/auth/request-otp", input));
1506
1598
  }
@@ -1528,13 +1620,13 @@ var LinkService = class {
1528
1620
  );
1529
1621
  }
1530
1622
  async getLinkData() {
1531
- return safe5(this.client.query(LINK_QUERY.DATA));
1623
+ return safe5(this.client.linkGet("/v1/link/data"));
1532
1624
  }
1533
1625
  async getAddresses() {
1534
- return safe5(this.client.query(LINK_QUERY.ADDRESSES));
1626
+ return safe5(this.client.linkGet("/v1/link/addresses"));
1535
1627
  }
1536
1628
  async getMobileMoney() {
1537
- return safe5(this.client.query(LINK_QUERY.MOBILE_MONEY));
1629
+ return safe5(this.client.linkGet("/v1/link/mobile-money"));
1538
1630
  }
1539
1631
  async getPreferences() {
1540
1632
  return safe5(this.client.query(LINK_QUERY.PREFERENCES));
@@ -1557,10 +1649,10 @@ var LinkService = class {
1557
1649
  return safe5(this.client.call(LINK_MUTATION.UPDATE_ADDRESS, input));
1558
1650
  }
1559
1651
  async deleteAddress(addressId) {
1560
- return safe5(this.client.call(LINK_MUTATION.DELETE_ADDRESS, addressId));
1652
+ return safe5(this.client.linkDelete(`/v1/link/addresses/${addressId}`));
1561
1653
  }
1562
1654
  async setDefaultAddress(addressId) {
1563
- return safe5(this.client.call(LINK_MUTATION.SET_DEFAULT_ADDRESS, addressId));
1655
+ return safe5(this.client.linkPost(`/v1/link/addresses/${addressId}/default`));
1564
1656
  }
1565
1657
  async trackAddressUsage(addressId) {
1566
1658
  return safe5(
@@ -1573,11 +1665,13 @@ var LinkService = class {
1573
1665
  return safe5(this.client.call(LINK_MUTATION.CREATE_MOBILE_MONEY, input));
1574
1666
  }
1575
1667
  async deleteMobileMoney(mobileMoneyId) {
1576
- return safe5(this.client.call(LINK_MUTATION.DELETE_MOBILE_MONEY, mobileMoneyId));
1668
+ return safe5(
1669
+ this.client.linkDelete(`/v1/link/mobile-money/${mobileMoneyId}`)
1670
+ );
1577
1671
  }
1578
1672
  async setDefaultMobileMoney(mobileMoneyId) {
1579
1673
  return safe5(
1580
- this.client.call(LINK_MUTATION.SET_DEFAULT_MOBILE_MONEY, mobileMoneyId)
1674
+ this.client.linkPost(`/v1/link/mobile-money/${mobileMoneyId}/default`)
1581
1675
  );
1582
1676
  }
1583
1677
  async trackMobileMoneyUsage(mobileMoneyId) {
@@ -2647,71 +2741,6 @@ function createElements(client, businessId, options) {
2647
2741
  return new CimplifyElements(client, businessId, options);
2648
2742
  }
2649
2743
 
2650
- // src/query/builder.ts
2651
- var QueryBuilder = class {
2652
- constructor(entity) {
2653
- this.filters = [];
2654
- this.modifiers = [];
2655
- this.pathSegments = [];
2656
- this.entity = entity;
2657
- }
2658
- path(segment) {
2659
- this.pathSegments.push(segment);
2660
- return this;
2661
- }
2662
- where(field, op, value) {
2663
- const v = typeof value === "string" ? `'${value}'` : value;
2664
- if (op === "contains" || op === "startsWith") {
2665
- this.filters.push(`@.${field} ${op} ${v}`);
2666
- } else {
2667
- this.filters.push(`@.${field}${op}${v}`);
2668
- }
2669
- return this;
2670
- }
2671
- and(field, op, value) {
2672
- return this.where(field, op, value);
2673
- }
2674
- sort(field, order = "asc") {
2675
- this.modifiers.push(`sort(${field},${order})`);
2676
- return this;
2677
- }
2678
- limit(n) {
2679
- this.modifiers.push(`limit(${n})`);
2680
- return this;
2681
- }
2682
- offset(n) {
2683
- this.modifiers.push(`offset(${n})`);
2684
- return this;
2685
- }
2686
- count() {
2687
- this.modifiers.push("count");
2688
- return this;
2689
- }
2690
- enriched() {
2691
- this.modifiers.push("enriched");
2692
- return this;
2693
- }
2694
- build() {
2695
- let query2 = this.entity;
2696
- if (this.pathSegments.length > 0) {
2697
- query2 += "." + this.pathSegments.join(".");
2698
- }
2699
- if (this.filters.length > 0) {
2700
- query2 += `[?(${this.filters.join(" && ")})]`;
2701
- }
2702
- for (const mod of this.modifiers) {
2703
- query2 += `#${mod}`;
2704
- }
2705
- return query2;
2706
- }
2707
- toString() {
2708
- return this.build();
2709
- }
2710
- };
2711
- function query(entity) {
2712
- return new QueryBuilder(entity);
2713
- }
2714
-
2715
2744
  exports.AuthService = AuthService;
2716
2745
  exports.BusinessService = BusinessService;
2717
2746
  exports.CartOperations = CartOperations;