@mission_sciences/provider-sdk 0.2.0 → 0.3.0-dev.6e896da

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 (48) hide show
  1. package/README.md +1 -40
  2. package/dist/adapters/react/index.d.ts +3 -0
  3. package/dist/adapters/react/index.d.ts.map +1 -0
  4. package/dist/adapters/react/index.js +75 -0
  5. package/dist/adapters/react/index.js.map +1 -0
  6. package/dist/adapters/react/useMarketplaceSession.d.ts +30 -0
  7. package/dist/adapters/react/useMarketplaceSession.d.ts.map +1 -0
  8. package/dist/adapters/vue/index.d.ts +3 -0
  9. package/dist/adapters/vue/index.d.ts.map +1 -0
  10. package/dist/adapters/vue/index.js +56 -0
  11. package/dist/adapters/vue/index.js.map +1 -0
  12. package/dist/adapters/vue/useMarketplaceSession.d.ts +78 -0
  13. package/dist/adapters/vue/useMarketplaceSession.d.ts.map +1 -0
  14. package/dist/core/HeartbeatManager.d.ts +43 -0
  15. package/dist/core/HeartbeatManager.d.ts.map +1 -0
  16. package/dist/core/JWKSValidator.d.ts +26 -0
  17. package/dist/core/JWKSValidator.d.ts.map +1 -0
  18. package/dist/core/JWTParser.d.ts +46 -0
  19. package/dist/core/JWTParser.d.ts.map +1 -0
  20. package/dist/core/MarketplaceSDK.d.ts +117 -0
  21. package/dist/core/MarketplaceSDK.d.ts.map +1 -0
  22. package/dist/core/PurchaseStateManager.d.ts +28 -0
  23. package/dist/core/PurchaseStateManager.d.ts.map +1 -0
  24. package/dist/core/TabSyncManager.d.ts +49 -0
  25. package/dist/core/TabSyncManager.d.ts.map +1 -0
  26. package/dist/core/TimerManager.d.ts +55 -0
  27. package/dist/core/TimerManager.d.ts.map +1 -0
  28. package/dist/index.d.ts +23 -769
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/marketplace-sdk.es.js +307 -4
  31. package/dist/marketplace-sdk.es.js.map +1 -1
  32. package/dist/marketplace-sdk.umd.js +1 -1
  33. package/dist/marketplace-sdk.umd.js.map +1 -1
  34. package/dist/styles/theme.d.ts +89 -0
  35. package/dist/styles/theme.d.ts.map +1 -0
  36. package/dist/types/index.d.ts +287 -0
  37. package/dist/types/index.d.ts.map +1 -0
  38. package/dist/ui/PurchaseModal.d.ts +30 -0
  39. package/dist/ui/PurchaseModal.d.ts.map +1 -0
  40. package/dist/ui/SessionHeader.d.ts +46 -0
  41. package/dist/ui/SessionHeader.d.ts.map +1 -0
  42. package/dist/ui/WarningModal.d.ts +39 -0
  43. package/dist/ui/WarningModal.d.ts.map +1 -0
  44. package/dist/utils/logger.d.ts +25 -0
  45. package/dist/utils/logger.d.ts.map +1 -0
  46. package/dist/utils/url.d.ts +12 -0
  47. package/dist/utils/url.d.ts.map +1 -0
  48. package/package.json +17 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAGvD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG5D,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACvF,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGxF,YAAY,EACV,SAAS,EACT,WAAW,EACX,SAAS,EACT,WAAW,EACX,SAAS,EACT,SAAS,EACT,SAAS,EACT,OAAO,EACP,YAAY,EACZ,qBAAqB,EACrB,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClD,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAGxC,OAAO,EAAE,cAAc,IAAI,OAAO,EAAE,MAAM,uBAAuB,CAAC"}
@@ -20,6 +20,19 @@ class SDKError extends Error {
20
20
  }
21
21
  }
22
22
  }
23
+ class PurchaseError extends SDKError {
24
+ constructor(message2, code, itemId, statusCode) {
25
+ super(message2, code, statusCode);
26
+ this.itemId = itemId;
27
+ this.name = "PurchaseError";
28
+ }
29
+ }
30
+ var PurchaseState = /* @__PURE__ */ ((PurchaseState2) => {
31
+ PurchaseState2["AVAILABLE"] = "AVAILABLE";
32
+ PurchaseState2["PRIVY_REQUIRED"] = "PRIVY_REQUIRED";
33
+ PurchaseState2["INSUFFICIENT_FUNDS"] = "INSUFFICIENT_FUNDS";
34
+ return PurchaseState2;
35
+ })(PurchaseState || {});
23
36
  class JWTParser {
24
37
  /**
25
38
  * Decode JWT payload without verification
@@ -2017,6 +2030,55 @@ class TabSyncManager {
2017
2030
  this.logger.log("Tab sync destroyed");
2018
2031
  }
2019
2032
  }
2033
+ class PurchaseStateManager {
2034
+ constructor(config) {
2035
+ this.config = {
2036
+ timeout: 5e3,
2037
+ ...config
2038
+ };
2039
+ }
2040
+ /**
2041
+ * Check purchase state for a specific item
2042
+ * Filters raw API response to expose only safe data to publisher apps
2043
+ */
2044
+ async checkItemPurchaseState(itemId) {
2045
+ try {
2046
+ const response = await fetch(`${this.config.apiEndpoint}/items/${itemId}/purchase-state`, {
2047
+ method: "GET",
2048
+ headers: {
2049
+ "Content-Type": "application/json"
2050
+ },
2051
+ signal: AbortSignal.timeout(this.config.timeout)
2052
+ });
2053
+ if (!response.ok) {
2054
+ throw new Error(`API request failed: ${response.status} ${response.statusText}`);
2055
+ }
2056
+ const rawData = await response.json();
2057
+ return this.filterPurchaseStateResponse(rawData);
2058
+ } catch (error) {
2059
+ if (error instanceof Error) {
2060
+ throw error;
2061
+ }
2062
+ throw new Error("Unknown error occurred while checking purchase state");
2063
+ }
2064
+ }
2065
+ /**
2066
+ * Filters raw API response to expose only safe data to publisher apps
2067
+ * CRITICAL: Raw privyEligibility map must NEVER be exposed to apps
2068
+ */
2069
+ filterPurchaseStateResponse(rawResponse) {
2070
+ const filteredResponse = {
2071
+ state: rawResponse.state
2072
+ };
2073
+ if (rawResponse.state === PurchaseState.PRIVY_REQUIRED && rawResponse.privyEligibility) {
2074
+ const requiredLevels = Object.values(rawResponse.privyEligibility).filter((details) => details.requiresUpgrade).map((details) => details.requiredLevel);
2075
+ if (requiredLevels.length > 0) {
2076
+ filteredResponse.requiredLevel = Math.max(...requiredLevels);
2077
+ }
2078
+ }
2079
+ return filteredResponse;
2080
+ }
2081
+ }
2020
2082
  const lightTheme = {
2021
2083
  colors: {
2022
2084
  // Background
@@ -2503,6 +2565,166 @@ function extractTokenFromURL(paramName = "gwSession", url) {
2503
2565
  function isBrowser() {
2504
2566
  return typeof window !== "undefined" && typeof window.document !== "undefined";
2505
2567
  }
2568
+ class PurchaseModal {
2569
+ constructor(themeMode = "light", customStyles) {
2570
+ this.modal = null;
2571
+ this.legacyStyles = null;
2572
+ const prefersDark = themeMode === "dark" || themeMode === "auto" && this.detectDarkMode();
2573
+ this.theme = getTheme(prefersDark);
2574
+ if (customStyles) {
2575
+ this.legacyStyles = {
2576
+ backgroundColor: customStyles.backgroundColor || "#ffffff",
2577
+ textColor: customStyles.textColor || "#333333",
2578
+ primaryColor: customStyles.primaryColor || "#007bff",
2579
+ borderRadius: customStyles.borderRadius || "8px",
2580
+ fontFamily: customStyles.fontFamily || '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
2581
+ };
2582
+ }
2583
+ }
2584
+ detectDarkMode() {
2585
+ if (typeof window !== "undefined" && window.matchMedia) {
2586
+ return window.matchMedia("(prefers-color-scheme: dark)").matches;
2587
+ }
2588
+ return false;
2589
+ }
2590
+ escapeHtml(text) {
2591
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
2592
+ }
2593
+ /**
2594
+ * Show purchase confirmation modal
2595
+ */
2596
+ show(options) {
2597
+ this.hide();
2598
+ this.modal = document.createElement("div");
2599
+ this.modal.id = "gw-purchase-modal";
2600
+ this.modal.style.cssText = `
2601
+ position: fixed;
2602
+ top: 0;
2603
+ left: 0;
2604
+ width: 100%;
2605
+ height: 100%;
2606
+ background-color: rgba(0, 0, 0, 0.5);
2607
+ display: flex;
2608
+ align-items: center;
2609
+ justify-content: center;
2610
+ z-index: 99999;
2611
+ font-family: ${this.legacyStyles?.fontFamily || this.theme.typography.fontFamily};
2612
+ `;
2613
+ const content = document.createElement("div");
2614
+ const bgColor = this.legacyStyles?.backgroundColor || this.theme.colors.card;
2615
+ const textColor = this.legacyStyles?.textColor || this.theme.colors.cardForeground;
2616
+ const borderRadius = this.legacyStyles?.borderRadius || this.theme.spacing.borderRadius.lg;
2617
+ content.style.cssText = `
2618
+ background-color: ${bgColor};
2619
+ color: ${textColor};
2620
+ border-radius: ${borderRadius};
2621
+ padding: ${this.theme.spacing.padding.lg};
2622
+ max-width: 400px;
2623
+ width: 90%;
2624
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
2625
+ border: 1px solid ${this.theme.colors.border};
2626
+ `;
2627
+ content.innerHTML = `
2628
+ <h2 style="margin: 0 0 ${this.theme.spacing.padding.md} 0; font-size: ${this.theme.typography.fontSize.xl}; font-weight: ${this.theme.typography.fontWeight.semibold}; color: ${textColor};">
2629
+ 🛒 Confirm Purchase
2630
+ </h2>
2631
+ <p style="margin: 0 0 ${this.theme.spacing.padding.lg} 0; font-size: ${this.theme.typography.fontSize.base}; line-height: ${this.theme.typography.lineHeight.normal}; color: ${this.theme.colors.mutedForeground};">
2632
+ Are you sure you want to purchase item <strong style="color: ${textColor};">${this.escapeHtml(options.itemId)}</strong>?
2633
+ </p>
2634
+ <div style="display: flex; gap: ${this.theme.spacing.gap.md}; justify-content: flex-end;">
2635
+ <button id="gw-cancel-btn" style="
2636
+ padding: 10px 20px;
2637
+ background-color: ${this.theme.colors.secondary};
2638
+ color: ${this.theme.colors.secondaryForeground};
2639
+ border: 1px solid ${this.theme.colors.border};
2640
+ border-radius: ${this.theme.spacing.borderRadius.sm};
2641
+ font-size: ${this.theme.typography.fontSize.sm};
2642
+ font-weight: ${this.theme.typography.fontWeight.medium};
2643
+ cursor: pointer;
2644
+ transition: all 0.2s;
2645
+ font-family: ${this.theme.typography.fontFamily};
2646
+ ">
2647
+ Cancel
2648
+ </button>
2649
+ <button id="gw-confirm-btn" style="
2650
+ padding: 10px 20px;
2651
+ background-color: ${this.theme.colors.primary};
2652
+ color: ${this.theme.colors.primaryForeground};
2653
+ border: none;
2654
+ border-radius: ${this.theme.spacing.borderRadius.sm};
2655
+ font-size: ${this.theme.typography.fontSize.sm};
2656
+ font-weight: ${this.theme.typography.fontWeight.medium};
2657
+ cursor: pointer;
2658
+ transition: all 0.2s;
2659
+ font-family: ${this.theme.typography.fontFamily};
2660
+ ">
2661
+ Confirm Purchase
2662
+ </button>
2663
+ </div>
2664
+ `;
2665
+ this.modal.appendChild(content);
2666
+ document.body.appendChild(this.modal);
2667
+ const confirmBtn = document.getElementById("gw-confirm-btn");
2668
+ if (confirmBtn && options.onConfirm) {
2669
+ confirmBtn.addEventListener("click", () => {
2670
+ options.onConfirm?.();
2671
+ this.hide();
2672
+ });
2673
+ }
2674
+ const cancelBtn = document.getElementById("gw-cancel-btn");
2675
+ if (cancelBtn) {
2676
+ cancelBtn.addEventListener("click", () => {
2677
+ options.onCancel?.();
2678
+ this.hide();
2679
+ });
2680
+ }
2681
+ const buttons = content.querySelectorAll("button");
2682
+ buttons.forEach((button) => {
2683
+ button.addEventListener("mouseenter", () => {
2684
+ button.style.opacity = "0.9";
2685
+ });
2686
+ button.addEventListener("mouseleave", () => {
2687
+ button.style.opacity = "1";
2688
+ });
2689
+ button.addEventListener("focus", () => {
2690
+ button.style.outline = `2px solid ${this.theme.colors.ring}`;
2691
+ button.style.outlineOffset = "2px";
2692
+ });
2693
+ button.addEventListener("blur", () => {
2694
+ button.style.outline = "none";
2695
+ });
2696
+ });
2697
+ const handleKeyDown = (e) => {
2698
+ if (e.key === "Escape") {
2699
+ options.onCancel?.();
2700
+ this.hide();
2701
+ document.removeEventListener("keydown", handleKeyDown);
2702
+ }
2703
+ };
2704
+ document.addEventListener("keydown", handleKeyDown);
2705
+ this.modal.addEventListener("click", (e) => {
2706
+ if (e.target === this.modal) {
2707
+ options.onCancel?.();
2708
+ this.hide();
2709
+ }
2710
+ });
2711
+ }
2712
+ /**
2713
+ * Hide and remove modal
2714
+ */
2715
+ hide() {
2716
+ if (this.modal && this.modal.parentNode) {
2717
+ this.modal.parentNode.removeChild(this.modal);
2718
+ this.modal = null;
2719
+ }
2720
+ }
2721
+ /**
2722
+ * Check if modal is currently shown
2723
+ */
2724
+ isShown() {
2725
+ return this.modal !== null;
2726
+ }
2727
+ }
2506
2728
  class MarketplaceSDK {
2507
2729
  constructor(config) {
2508
2730
  this.timer = null;
@@ -2513,17 +2735,19 @@ class MarketplaceSDK {
2513
2735
  this.sessionData = null;
2514
2736
  this.jwtToken = null;
2515
2737
  this.endReason = "manual";
2738
+ this.purchaseModal = null;
2516
2739
  this.config = {
2517
- jwksUri: config.jwksUri || "https://api.generalwisdom.com/.well-known/jwks.json",
2740
+ jwksUri: config.jwksUri || "https://api.platform.generalwisdom.com/.well-known/jwks.json",
2518
2741
  jwtParamName: config.jwtParamName || "gwSession",
2519
- apiEndpoint: config.apiEndpoint || "http://localhost:3000",
2742
+ apiEndpoint: config.apiEndpoint || "https://api.platform.generalwisdom.com",
2743
+ jwtIssuer: config.jwtIssuer || "generalwisdom.com",
2520
2744
  debug: config.debug ?? false,
2521
2745
  autoStart: config.autoStart ?? true,
2522
2746
  warningThresholdSeconds: config.warningThresholdSeconds ?? 300,
2523
2747
  customStyles: config.customStyles ?? {},
2524
2748
  themeMode: config.themeMode ?? "light",
2525
2749
  applicationId: config.applicationId ?? "",
2526
- marketplaceUrl: config.marketplaceUrl ?? "https://d3p2yqofgy75sz.cloudfront.net/",
2750
+ marketplaceUrl: config.marketplaceUrl ?? "https://platform.generalwisdom.com/",
2527
2751
  // Phase 2 options
2528
2752
  enableHeartbeat: config.enableHeartbeat ?? false,
2529
2753
  heartbeatIntervalSeconds: config.heartbeatIntervalSeconds ?? 30,
@@ -2536,6 +2760,9 @@ class MarketplaceSDK {
2536
2760
  };
2537
2761
  this.validator = new JWKSValidator(this.config.jwksUri, this.config.debug);
2538
2762
  this.logger = new Logger(this.config.debug, "[MarketplaceSDK]");
2763
+ this.purchaseStateManager = new PurchaseStateManager({
2764
+ apiEndpoint: this.config.apiEndpoint
2765
+ });
2539
2766
  this.logger.info("SDK initialized with config:", {
2540
2767
  jwksUri: this.config.jwksUri,
2541
2768
  jwtParamName: this.config.jwtParamName,
@@ -2624,7 +2851,7 @@ class MarketplaceSDK {
2624
2851
  this.logger.log("Using JWKS validation");
2625
2852
  verifiedClaims = await this.validator.verify(
2626
2853
  this.jwtToken,
2627
- "generalwisdom.com",
2854
+ this.config.jwtIssuer,
2628
2855
  this.config.applicationId || void 0
2629
2856
  );
2630
2857
  }
@@ -3045,6 +3272,79 @@ class MarketplaceSDK {
3045
3272
  isTimerRunning() {
3046
3273
  return this.timer?.isRunning() ?? false;
3047
3274
  }
3275
+ /**
3276
+ * Request purchase of an item — opens PurchaseModal directly
3277
+ * Bypasses the add-ons panel; works for any active item (visible or hidden)
3278
+ */
3279
+ requestPurchase(itemId) {
3280
+ if (!this.sessionData || !this.jwtToken) {
3281
+ throw new SDKError("No active session", "NO_SESSION");
3282
+ }
3283
+ this.logger.info("Requesting purchase for item:", itemId);
3284
+ this.events.onPurchaseStart?.({ itemId, quantity: 1 });
3285
+ if (!this.purchaseModal) {
3286
+ this.purchaseModal = new PurchaseModal(
3287
+ this.config.themeMode || "light",
3288
+ this.config.customStyles
3289
+ );
3290
+ }
3291
+ this.purchaseModal.show({
3292
+ itemId,
3293
+ onConfirm: async () => {
3294
+ try {
3295
+ const response = await fetch(
3296
+ `${this.config.apiEndpoint}/items/${itemId}/purchase`,
3297
+ {
3298
+ method: "POST",
3299
+ headers: {
3300
+ "Authorization": `Bearer ${this.jwtToken}`,
3301
+ "Content-Type": "application/json"
3302
+ },
3303
+ body: JSON.stringify({ item_id: itemId, quantity: 1 })
3304
+ }
3305
+ );
3306
+ if (!response.ok) {
3307
+ throw new PurchaseError(
3308
+ "Purchase failed",
3309
+ "PURCHASE_FAILED",
3310
+ itemId,
3311
+ response.status
3312
+ );
3313
+ }
3314
+ const data = await response.json();
3315
+ const result = {
3316
+ itemId,
3317
+ transactionId: data.transactionId,
3318
+ amount: data.amount
3319
+ };
3320
+ this.events.onPurchaseSuccess?.(result);
3321
+ this.events.onPurchaseComplete?.(itemId);
3322
+ if (data.newBalance !== void 0) {
3323
+ this.events.onBalanceUpdate?.(data.newBalance);
3324
+ }
3325
+ this.logger.info("Purchase successful:", result);
3326
+ } catch (error) {
3327
+ const purchaseError = error instanceof PurchaseError ? error : new PurchaseError(
3328
+ error instanceof Error ? error.message : "Purchase failed",
3329
+ "PURCHASE_ERROR",
3330
+ itemId
3331
+ );
3332
+ this.events.onPurchaseError?.(purchaseError);
3333
+ this.logger.error("Purchase failed:", purchaseError);
3334
+ }
3335
+ },
3336
+ onCancel: () => {
3337
+ this.events.onPurchaseCancelled?.(itemId);
3338
+ this.logger.info("Purchase cancelled for item:", itemId);
3339
+ }
3340
+ });
3341
+ }
3342
+ /**
3343
+ * Get purchase state manager for item purchase state checking
3344
+ */
3345
+ getPurchaseStateManager() {
3346
+ return this.purchaseStateManager;
3347
+ }
3048
3348
  /**
3049
3349
  * Cleanup and destroy SDK instance
3050
3350
  */
@@ -3054,6 +3354,7 @@ class MarketplaceSDK {
3054
3354
  this.heartbeat?.stop();
3055
3355
  this.tabSync?.destroy();
3056
3356
  this.modal?.hide();
3357
+ this.purchaseModal?.hide();
3057
3358
  if (typeof sessionStorage !== "undefined") {
3058
3359
  sessionStorage.removeItem("gw_marketplace_jwt");
3059
3360
  this.logger.log("JWT token cleared from storage");
@@ -3264,6 +3565,8 @@ export {
3264
3565
  JWTParser,
3265
3566
  Logger,
3266
3567
  MarketplaceSDK,
3568
+ PurchaseError,
3569
+ PurchaseModal,
3267
3570
  SDKError,
3268
3571
  SessionHeader,
3269
3572
  TabSyncManager,