@progus/connector 0.3.0 → 0.5.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/README.md CHANGED
@@ -51,14 +51,16 @@ if (result.success) {
51
51
  }
52
52
  ```
53
53
 
54
- ## Cross-sell offers
54
+ ## Cross-sell offers (remote catalog)
55
55
 
56
56
  ```ts
57
- import { getCrossSellOffers } from "@progus/connector";
57
+ import { getCrossSellOffersFromApi } from "@progus/connector/browser";
58
58
 
59
- const offers = getCrossSellOffers({
60
- currentAppKey: "progus-store-locator",
59
+ const offers = await getCrossSellOffersFromApi({
60
+ appName: "progus_store_locator",
61
61
  installedAppKeys: ["progus_cod"],
62
+ appsCatalogUrl: "https://appsdata.progus.workers.dev/recommendations",
63
+ limit: 3,
62
64
  });
63
65
  ```
64
66
 
@@ -70,6 +72,12 @@ import { signPayload } from "@progus/connector";
70
72
  const signature = signPayload(JSON.stringify({ test: true }), process.env.PARTNERS_SECRET_KEY!);
71
73
  ```
72
74
 
75
+ ## UI components (browser-safe)
76
+
77
+ ```tsx
78
+ import { Recommendations, PartnerIdCard } from "@progus/connector/ui";
79
+ ```
80
+
73
81
  ## Environment variables
74
82
 
75
83
  - `PARTNERS_API_URL` - base URL for partner API (e.g. `https://partners.example.com`)
@@ -0,0 +1,98 @@
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/browser/index.ts
21
+ var browser_exports = {};
22
+ __export(browser_exports, {
23
+ fetchAppsCatalog: () => fetchAppsCatalog,
24
+ getCrossSellOffers: () => getCrossSellOffers,
25
+ getCrossSellOffersFromApi: () => getCrossSellOffersFromApi,
26
+ normalizePartnerId: () => normalizePartnerId
27
+ });
28
+ module.exports = __toCommonJS(browser_exports);
29
+
30
+ // src/utils.ts
31
+ function normalizePartnerId(value) {
32
+ if (!value) return null;
33
+ const trimmed = value.trim();
34
+ return trimmed.length > 0 ? trimmed : null;
35
+ }
36
+ function stripTrailingSlash(value) {
37
+ return value.replace(/\/+$/, "");
38
+ }
39
+ function safeJsonParse(text) {
40
+ try {
41
+ return JSON.parse(text);
42
+ } catch {
43
+ return null;
44
+ }
45
+ }
46
+
47
+ // src/crossSell.ts
48
+ function getCrossSellOffers(options = {}) {
49
+ const appsCatalog = options.appsCatalog ?? [];
50
+ const installedKeys = new Set(
51
+ [options.appName, ...options.installedAppKeys ?? []].filter(
52
+ (key) => Boolean(key)
53
+ )
54
+ );
55
+ const locale = options.locale;
56
+ return appsCatalog.filter((app) => app.enabled !== false).filter((app) => locale ? !app.locales || app.locales.includes(locale) : true).filter((app) => !installedKeys.has(app.key)).sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
57
+ }
58
+ var DEFAULT_APPS_CATALOG_URL = "https://appsdata.progus.workers.dev/recommendations";
59
+ async function fetchAppsCatalog(options = {}) {
60
+ const fetchImpl = options.fetch ?? globalThis.fetch;
61
+ const logger = options.logger ?? console;
62
+ const appName = options.appName;
63
+ const baseUrl = stripTrailingSlash(options.appsCatalogUrl ?? DEFAULT_APPS_CATALOG_URL);
64
+ const limit = typeof options.limit === "number" ? options.limit : 3;
65
+ const params = new URLSearchParams();
66
+ if (appName) params.set("appName", appName);
67
+ if (limit > 0) params.set("limit", String(limit));
68
+ const url = params.toString() ? `${baseUrl}?${params.toString()}` : baseUrl;
69
+ if (!fetchImpl) {
70
+ throw new Error("Fetch implementation is required");
71
+ }
72
+ try {
73
+ const response = await fetchImpl(url);
74
+ const text = await response.text();
75
+ const parsed = safeJsonParse(text);
76
+ if (!response.ok || !parsed) {
77
+ logger?.error?.("Failed to fetch apps catalog", { status: response.status });
78
+ return [];
79
+ }
80
+ return parsed;
81
+ } catch (error) {
82
+ logger?.error?.("Failed to fetch apps catalog", {
83
+ error: error instanceof Error ? error.message : String(error)
84
+ });
85
+ return [];
86
+ }
87
+ }
88
+ async function getCrossSellOffersFromApi(options = {}) {
89
+ const appsCatalog = options.appsCatalog ?? await fetchAppsCatalog(options);
90
+ return getCrossSellOffers({ ...options, appsCatalog });
91
+ }
92
+ // Annotate the CommonJS export names for ESM import in node:
93
+ 0 && (module.exports = {
94
+ fetchAppsCatalog,
95
+ getCrossSellOffers,
96
+ getCrossSellOffersFromApi,
97
+ normalizePartnerId
98
+ });
@@ -0,0 +1,9 @@
1
+ import { d as CrossSellFetchOptions, c as AppsCatalogEntry, e as CrossSellOptions } from '../types-B6Duba-V.cjs';
2
+
3
+ declare function getCrossSellOffers(options?: CrossSellOptions): AppsCatalogEntry[];
4
+ declare function fetchAppsCatalog(options?: CrossSellFetchOptions): Promise<AppsCatalogEntry[]>;
5
+ declare function getCrossSellOffersFromApi(options?: CrossSellFetchOptions): Promise<AppsCatalogEntry[]>;
6
+
7
+ declare function normalizePartnerId(value?: string | null): string | null;
8
+
9
+ export { AppsCatalogEntry, CrossSellFetchOptions, CrossSellOptions, fetchAppsCatalog, getCrossSellOffers, getCrossSellOffersFromApi, normalizePartnerId };
@@ -0,0 +1,9 @@
1
+ import { d as CrossSellFetchOptions, c as AppsCatalogEntry, e as CrossSellOptions } from '../types-B6Duba-V.js';
2
+
3
+ declare function getCrossSellOffers(options?: CrossSellOptions): AppsCatalogEntry[];
4
+ declare function fetchAppsCatalog(options?: CrossSellFetchOptions): Promise<AppsCatalogEntry[]>;
5
+ declare function getCrossSellOffersFromApi(options?: CrossSellFetchOptions): Promise<AppsCatalogEntry[]>;
6
+
7
+ declare function normalizePartnerId(value?: string | null): string | null;
8
+
9
+ export { AppsCatalogEntry, CrossSellFetchOptions, CrossSellOptions, fetchAppsCatalog, getCrossSellOffers, getCrossSellOffersFromApi, normalizePartnerId };
@@ -0,0 +1,12 @@
1
+ import {
2
+ fetchAppsCatalog,
3
+ getCrossSellOffers,
4
+ getCrossSellOffersFromApi,
5
+ normalizePartnerId
6
+ } from "../chunk-ITDLIOZL.js";
7
+ export {
8
+ fetchAppsCatalog,
9
+ getCrossSellOffers,
10
+ getCrossSellOffersFromApi,
11
+ normalizePartnerId
12
+ };
@@ -0,0 +1,76 @@
1
+ // src/utils.ts
2
+ function normalizePartnerId(value) {
3
+ if (!value) return null;
4
+ const trimmed = value.trim();
5
+ return trimmed.length > 0 ? trimmed : null;
6
+ }
7
+ function buildEventId(shopDomain, eventName, externalId) {
8
+ if (!externalId) return void 0;
9
+ return `${shopDomain}:${eventName}:${externalId}`;
10
+ }
11
+ function stripTrailingSlash(value) {
12
+ return value.replace(/\/+$/, "");
13
+ }
14
+ function safeJsonParse(text) {
15
+ try {
16
+ return JSON.parse(text);
17
+ } catch {
18
+ return null;
19
+ }
20
+ }
21
+
22
+ // src/crossSell.ts
23
+ function getCrossSellOffers(options = {}) {
24
+ const appsCatalog = options.appsCatalog ?? [];
25
+ const installedKeys = new Set(
26
+ [options.appName, ...options.installedAppKeys ?? []].filter(
27
+ (key) => Boolean(key)
28
+ )
29
+ );
30
+ const locale = options.locale;
31
+ return appsCatalog.filter((app) => app.enabled !== false).filter((app) => locale ? !app.locales || app.locales.includes(locale) : true).filter((app) => !installedKeys.has(app.key)).sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
32
+ }
33
+ var DEFAULT_APPS_CATALOG_URL = "https://appsdata.progus.workers.dev/recommendations";
34
+ async function fetchAppsCatalog(options = {}) {
35
+ const fetchImpl = options.fetch ?? globalThis.fetch;
36
+ const logger = options.logger ?? console;
37
+ const appName = options.appName;
38
+ const baseUrl = stripTrailingSlash(options.appsCatalogUrl ?? DEFAULT_APPS_CATALOG_URL);
39
+ const limit = typeof options.limit === "number" ? options.limit : 3;
40
+ const params = new URLSearchParams();
41
+ if (appName) params.set("appName", appName);
42
+ if (limit > 0) params.set("limit", String(limit));
43
+ const url = params.toString() ? `${baseUrl}?${params.toString()}` : baseUrl;
44
+ if (!fetchImpl) {
45
+ throw new Error("Fetch implementation is required");
46
+ }
47
+ try {
48
+ const response = await fetchImpl(url);
49
+ const text = await response.text();
50
+ const parsed = safeJsonParse(text);
51
+ if (!response.ok || !parsed) {
52
+ logger?.error?.("Failed to fetch apps catalog", { status: response.status });
53
+ return [];
54
+ }
55
+ return parsed;
56
+ } catch (error) {
57
+ logger?.error?.("Failed to fetch apps catalog", {
58
+ error: error instanceof Error ? error.message : String(error)
59
+ });
60
+ return [];
61
+ }
62
+ }
63
+ async function getCrossSellOffersFromApi(options = {}) {
64
+ const appsCatalog = options.appsCatalog ?? await fetchAppsCatalog(options);
65
+ return getCrossSellOffers({ ...options, appsCatalog });
66
+ }
67
+
68
+ export {
69
+ normalizePartnerId,
70
+ buildEventId,
71
+ stripTrailingSlash,
72
+ safeJsonParse,
73
+ getCrossSellOffers,
74
+ fetchAppsCatalog,
75
+ getCrossSellOffersFromApi
76
+ };
package/dist/index.cjs CHANGED
@@ -20,9 +20,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- appsCatalog: () => appsCatalog,
24
23
  createProgusConnector: () => createProgusConnector,
24
+ fetchAppsCatalog: () => fetchAppsCatalog,
25
25
  getCrossSellOffers: () => getCrossSellOffers,
26
+ getCrossSellOffersFromApi: () => getCrossSellOffersFromApi,
26
27
  normalizePartnerId: () => normalizePartnerId,
27
28
  signPayload: () => signPayload
28
29
  });
@@ -209,52 +210,57 @@ function createProgusConnector(config) {
209
210
  };
210
211
  }
211
212
 
212
- // src/appsCatalog.json
213
- var appsCatalog_default = [
214
- {
215
- key: "progus_cod",
216
- type: "app",
217
- title: "Progus COD",
218
- company: "Progus",
219
- companyUrl: "https://progus.com",
220
- desc: "Automate COD Fees & Hide/Show Cash on Delivery by Rules",
221
- url: "https://apps.shopify.com/progus-cod",
222
- icon: "https://cdn.shopify.com/app-store/listing_images/bc537219cc3ed2bd4e7e3e683fe6b74a/icon/CMi_6dTEkIoDEAE=.png",
223
- priority: 100,
224
- enabled: true
225
- },
226
- {
227
- key: "progus_trust_badges",
228
- type: "app",
229
- title: "Progus Trust Badges",
230
- company: "Progus",
231
- companyUrl: "https://progus.com",
232
- desc: "Add Trust Badges to your store to build trust and credibility.",
233
- url: "https://apps.shopify.com/progus-trust-badges-1",
234
- icon: "https://cdn.shopify.com/app-store/listing_images/f9d0009e237f27d2db35b41ef99be858/icon/CJ3y1qDn1JEDEAE=.png",
235
- priority: 90,
236
- enabled: true
237
- }
238
- ];
239
-
240
213
  // src/crossSell.ts
241
- var catalog = appsCatalog_default;
242
214
  function getCrossSellOffers(options = {}) {
243
- const appsCatalog2 = options.appsCatalog ?? catalog;
215
+ const appsCatalog = options.appsCatalog ?? [];
244
216
  const installedKeys = new Set(
245
- [options.currentAppKey, ...options.installedAppKeys ?? []].filter(
217
+ [options.appName, ...options.installedAppKeys ?? []].filter(
246
218
  (key) => Boolean(key)
247
219
  )
248
220
  );
249
221
  const locale = options.locale;
250
- return appsCatalog2.filter((app) => app.enabled !== false).filter((app) => locale ? !app.locales || app.locales.includes(locale) : true).filter((app) => !installedKeys.has(app.key)).sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
222
+ return appsCatalog.filter((app) => app.enabled !== false).filter((app) => locale ? !app.locales || app.locales.includes(locale) : true).filter((app) => !installedKeys.has(app.key)).sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
223
+ }
224
+ var DEFAULT_APPS_CATALOG_URL = "https://appsdata.progus.workers.dev/recommendations";
225
+ async function fetchAppsCatalog(options = {}) {
226
+ const fetchImpl = options.fetch ?? globalThis.fetch;
227
+ const logger = options.logger ?? console;
228
+ const appName = options.appName;
229
+ const baseUrl = stripTrailingSlash(options.appsCatalogUrl ?? DEFAULT_APPS_CATALOG_URL);
230
+ const limit = typeof options.limit === "number" ? options.limit : 3;
231
+ const params = new URLSearchParams();
232
+ if (appName) params.set("appName", appName);
233
+ if (limit > 0) params.set("limit", String(limit));
234
+ const url = params.toString() ? `${baseUrl}?${params.toString()}` : baseUrl;
235
+ if (!fetchImpl) {
236
+ throw new Error("Fetch implementation is required");
237
+ }
238
+ try {
239
+ const response = await fetchImpl(url);
240
+ const text = await response.text();
241
+ const parsed = safeJsonParse(text);
242
+ if (!response.ok || !parsed) {
243
+ logger?.error?.("Failed to fetch apps catalog", { status: response.status });
244
+ return [];
245
+ }
246
+ return parsed;
247
+ } catch (error) {
248
+ logger?.error?.("Failed to fetch apps catalog", {
249
+ error: error instanceof Error ? error.message : String(error)
250
+ });
251
+ return [];
252
+ }
253
+ }
254
+ async function getCrossSellOffersFromApi(options = {}) {
255
+ const appsCatalog = options.appsCatalog ?? await fetchAppsCatalog(options);
256
+ return getCrossSellOffers({ ...options, appsCatalog });
251
257
  }
252
- var appsCatalog = catalog;
253
258
  // Annotate the CommonJS export names for ESM import in node:
254
259
  0 && (module.exports = {
255
- appsCatalog,
256
260
  createProgusConnector,
261
+ fetchAppsCatalog,
257
262
  getCrossSellOffers,
263
+ getCrossSellOffersFromApi,
258
264
  normalizePartnerId,
259
265
  signPayload
260
266
  });
package/dist/index.d.cts CHANGED
@@ -1,70 +1,6 @@
1
- type FetchLike = typeof fetch;
2
- type Logger = {
3
- info?: (message: string, meta?: Record<string, unknown>) => void;
4
- warn?: (message: string, meta?: Record<string, unknown>) => void;
5
- error?: (message: string, meta?: Record<string, unknown>) => void;
6
- };
7
- type AppsCatalogEntry = {
8
- key: string;
9
- type: string;
10
- title: string;
11
- company?: string;
12
- companyUrl?: string;
13
- desc?: string;
14
- url: string;
15
- icon?: string;
16
- enabled?: boolean;
17
- priority?: number;
18
- locales?: string[];
19
- };
20
- type CrossSellOptions = {
21
- currentAppKey?: string;
22
- installedAppKeys?: string[];
23
- locale?: string;
24
- shopPlan?: string;
25
- appsCatalog?: AppsCatalogEntry[];
26
- };
27
- type ConnectorConfig = {
28
- appKey: string;
29
- apiBaseUrl: string;
30
- apiKey?: string;
31
- signingSecret?: string;
32
- appsCatalog?: AppsCatalogEntry[];
33
- fetch?: FetchLike;
34
- logger?: Logger;
35
- enableIdempotency?: boolean;
36
- };
37
- type TrackEventName = "installation" | "uninstallation" | "subscription";
38
- type TrackEventParams<TData = Record<string, unknown>> = {
39
- eventName: TrackEventName | (string & {});
40
- shopDomain: string;
41
- partnerId?: string | null;
42
- data?: TData;
43
- externalId?: string;
44
- };
45
- type TrackResult<TData = Record<string, unknown>> = {
46
- success: boolean;
47
- message?: string;
48
- status?: number;
49
- data?: TData;
50
- };
51
- type CheckPartnerIdResult = {
52
- success: boolean;
53
- partnerId?: string;
54
- message?: string;
55
- status?: number;
56
- };
57
- type AssignPartnerIdInput = {
58
- shopDomain: string;
59
- partnerId: string;
60
- };
61
- type SubscriptionEventData = {
62
- subscriptionStatus?: string;
63
- subscriptionName?: string;
64
- subscriptionId?: string;
65
- subscriptionPrice?: number;
66
- subscriptionPeriod?: string;
67
- };
1
+ import { C as ConnectorConfig, T as TrackEventParams, a as TrackResult, S as SubscriptionEventData, A as AssignPartnerIdInput, b as CheckPartnerIdResult } from './types-B6Duba-V.cjs';
2
+ export { c as AppsCatalogEntry, d as CrossSellFetchOptions, e as CrossSellOptions, L as Logger, f as TrackEventName } from './types-B6Duba-V.cjs';
3
+ export { fetchAppsCatalog, getCrossSellOffers, getCrossSellOffersFromApi, normalizePartnerId } from './browser/index.cjs';
68
4
 
69
5
  type Connector = {
70
6
  track: (eventName: TrackEventParams["eventName"], payload: Omit<TrackEventParams, "eventName">) => Promise<TrackResult>;
@@ -96,11 +32,6 @@ type Connector = {
96
32
  };
97
33
  declare function createProgusConnector(config: ConnectorConfig): Connector;
98
34
 
99
- declare function getCrossSellOffers(options?: CrossSellOptions): AppsCatalogEntry[];
100
- declare const appsCatalog: AppsCatalogEntry[];
101
-
102
35
  declare function signPayload(body: string, secret: string): string;
103
36
 
104
- declare function normalizePartnerId(value?: string | null): string | null;
105
-
106
- export { type AppsCatalogEntry, type AssignPartnerIdInput, type CheckPartnerIdResult, type ConnectorConfig, type CrossSellOptions, type Logger, type SubscriptionEventData, type TrackEventName, type TrackEventParams, type TrackResult, appsCatalog, createProgusConnector, getCrossSellOffers, normalizePartnerId, signPayload };
37
+ export { AssignPartnerIdInput, CheckPartnerIdResult, ConnectorConfig, SubscriptionEventData, TrackEventParams, TrackResult, createProgusConnector, signPayload };
package/dist/index.d.ts CHANGED
@@ -1,70 +1,6 @@
1
- type FetchLike = typeof fetch;
2
- type Logger = {
3
- info?: (message: string, meta?: Record<string, unknown>) => void;
4
- warn?: (message: string, meta?: Record<string, unknown>) => void;
5
- error?: (message: string, meta?: Record<string, unknown>) => void;
6
- };
7
- type AppsCatalogEntry = {
8
- key: string;
9
- type: string;
10
- title: string;
11
- company?: string;
12
- companyUrl?: string;
13
- desc?: string;
14
- url: string;
15
- icon?: string;
16
- enabled?: boolean;
17
- priority?: number;
18
- locales?: string[];
19
- };
20
- type CrossSellOptions = {
21
- currentAppKey?: string;
22
- installedAppKeys?: string[];
23
- locale?: string;
24
- shopPlan?: string;
25
- appsCatalog?: AppsCatalogEntry[];
26
- };
27
- type ConnectorConfig = {
28
- appKey: string;
29
- apiBaseUrl: string;
30
- apiKey?: string;
31
- signingSecret?: string;
32
- appsCatalog?: AppsCatalogEntry[];
33
- fetch?: FetchLike;
34
- logger?: Logger;
35
- enableIdempotency?: boolean;
36
- };
37
- type TrackEventName = "installation" | "uninstallation" | "subscription";
38
- type TrackEventParams<TData = Record<string, unknown>> = {
39
- eventName: TrackEventName | (string & {});
40
- shopDomain: string;
41
- partnerId?: string | null;
42
- data?: TData;
43
- externalId?: string;
44
- };
45
- type TrackResult<TData = Record<string, unknown>> = {
46
- success: boolean;
47
- message?: string;
48
- status?: number;
49
- data?: TData;
50
- };
51
- type CheckPartnerIdResult = {
52
- success: boolean;
53
- partnerId?: string;
54
- message?: string;
55
- status?: number;
56
- };
57
- type AssignPartnerIdInput = {
58
- shopDomain: string;
59
- partnerId: string;
60
- };
61
- type SubscriptionEventData = {
62
- subscriptionStatus?: string;
63
- subscriptionName?: string;
64
- subscriptionId?: string;
65
- subscriptionPrice?: number;
66
- subscriptionPeriod?: string;
67
- };
1
+ import { C as ConnectorConfig, T as TrackEventParams, a as TrackResult, S as SubscriptionEventData, A as AssignPartnerIdInput, b as CheckPartnerIdResult } from './types-B6Duba-V.js';
2
+ export { c as AppsCatalogEntry, d as CrossSellFetchOptions, e as CrossSellOptions, L as Logger, f as TrackEventName } from './types-B6Duba-V.js';
3
+ export { fetchAppsCatalog, getCrossSellOffers, getCrossSellOffersFromApi, normalizePartnerId } from './browser/index.js';
68
4
 
69
5
  type Connector = {
70
6
  track: (eventName: TrackEventParams["eventName"], payload: Omit<TrackEventParams, "eventName">) => Promise<TrackResult>;
@@ -96,11 +32,6 @@ type Connector = {
96
32
  };
97
33
  declare function createProgusConnector(config: ConnectorConfig): Connector;
98
34
 
99
- declare function getCrossSellOffers(options?: CrossSellOptions): AppsCatalogEntry[];
100
- declare const appsCatalog: AppsCatalogEntry[];
101
-
102
35
  declare function signPayload(body: string, secret: string): string;
103
36
 
104
- declare function normalizePartnerId(value?: string | null): string | null;
105
-
106
- export { type AppsCatalogEntry, type AssignPartnerIdInput, type CheckPartnerIdResult, type ConnectorConfig, type CrossSellOptions, type Logger, type SubscriptionEventData, type TrackEventName, type TrackEventParams, type TrackResult, appsCatalog, createProgusConnector, getCrossSellOffers, normalizePartnerId, signPayload };
37
+ export { AssignPartnerIdInput, CheckPartnerIdResult, ConnectorConfig, SubscriptionEventData, TrackEventParams, TrackResult, createProgusConnector, signPayload };
package/dist/index.js CHANGED
@@ -1,30 +1,19 @@
1
+ import {
2
+ buildEventId,
3
+ fetchAppsCatalog,
4
+ getCrossSellOffers,
5
+ getCrossSellOffersFromApi,
6
+ normalizePartnerId,
7
+ safeJsonParse,
8
+ stripTrailingSlash
9
+ } from "./chunk-ITDLIOZL.js";
10
+
1
11
  // src/signing.ts
2
12
  import { createHmac } from "crypto";
3
13
  function signPayload(body, secret) {
4
14
  return createHmac("sha256", secret).update(body).digest("hex");
5
15
  }
6
16
 
7
- // src/utils.ts
8
- function normalizePartnerId(value) {
9
- if (!value) return null;
10
- const trimmed = value.trim();
11
- return trimmed.length > 0 ? trimmed : null;
12
- }
13
- function buildEventId(shopDomain, eventName, externalId) {
14
- if (!externalId) return void 0;
15
- return `${shopDomain}:${eventName}:${externalId}`;
16
- }
17
- function stripTrailingSlash(value) {
18
- return value.replace(/\/+$/, "");
19
- }
20
- function safeJsonParse(text) {
21
- try {
22
- return JSON.parse(text);
23
- } catch {
24
- return null;
25
- }
26
- }
27
-
28
17
  // src/connector.ts
29
18
  function createProgusConnector(config) {
30
19
  const apiBaseUrl = config.apiBaseUrl ? stripTrailingSlash(config.apiBaseUrl) : "";
@@ -178,52 +167,11 @@ function createProgusConnector(config) {
178
167
  checkPartnerId
179
168
  };
180
169
  }
181
-
182
- // src/appsCatalog.json
183
- var appsCatalog_default = [
184
- {
185
- key: "progus_cod",
186
- type: "app",
187
- title: "Progus COD",
188
- company: "Progus",
189
- companyUrl: "https://progus.com",
190
- desc: "Automate COD Fees & Hide/Show Cash on Delivery by Rules",
191
- url: "https://apps.shopify.com/progus-cod",
192
- icon: "https://cdn.shopify.com/app-store/listing_images/bc537219cc3ed2bd4e7e3e683fe6b74a/icon/CMi_6dTEkIoDEAE=.png",
193
- priority: 100,
194
- enabled: true
195
- },
196
- {
197
- key: "progus_trust_badges",
198
- type: "app",
199
- title: "Progus Trust Badges",
200
- company: "Progus",
201
- companyUrl: "https://progus.com",
202
- desc: "Add Trust Badges to your store to build trust and credibility.",
203
- url: "https://apps.shopify.com/progus-trust-badges-1",
204
- icon: "https://cdn.shopify.com/app-store/listing_images/f9d0009e237f27d2db35b41ef99be858/icon/CJ3y1qDn1JEDEAE=.png",
205
- priority: 90,
206
- enabled: true
207
- }
208
- ];
209
-
210
- // src/crossSell.ts
211
- var catalog = appsCatalog_default;
212
- function getCrossSellOffers(options = {}) {
213
- const appsCatalog2 = options.appsCatalog ?? catalog;
214
- const installedKeys = new Set(
215
- [options.currentAppKey, ...options.installedAppKeys ?? []].filter(
216
- (key) => Boolean(key)
217
- )
218
- );
219
- const locale = options.locale;
220
- return appsCatalog2.filter((app) => app.enabled !== false).filter((app) => locale ? !app.locales || app.locales.includes(locale) : true).filter((app) => !installedKeys.has(app.key)).sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
221
- }
222
- var appsCatalog = catalog;
223
170
  export {
224
- appsCatalog,
225
171
  createProgusConnector,
172
+ fetchAppsCatalog,
226
173
  getCrossSellOffers,
174
+ getCrossSellOffersFromApi,
227
175
  normalizePartnerId,
228
176
  signPayload
229
177
  };
@@ -0,0 +1,75 @@
1
+ type FetchLike = typeof fetch;
2
+ type Logger = {
3
+ info?: (message: string, meta?: Record<string, unknown>) => void;
4
+ warn?: (message: string, meta?: Record<string, unknown>) => void;
5
+ error?: (message: string, meta?: Record<string, unknown>) => void;
6
+ };
7
+ type AppsCatalogEntry = {
8
+ key: string;
9
+ type: string;
10
+ title: string;
11
+ company?: string;
12
+ companyUrl?: string;
13
+ desc?: string;
14
+ url: string;
15
+ icon?: string;
16
+ enabled?: boolean;
17
+ priority?: number;
18
+ locales?: string[];
19
+ };
20
+ type CrossSellOptions = {
21
+ appName?: string;
22
+ installedAppKeys?: string[];
23
+ locale?: string;
24
+ shopPlan?: string;
25
+ appsCatalog?: AppsCatalogEntry[];
26
+ };
27
+ type CrossSellFetchOptions = CrossSellOptions & {
28
+ appsCatalogUrl?: string;
29
+ limit?: number;
30
+ fetch?: FetchLike;
31
+ logger?: Logger;
32
+ };
33
+ type ConnectorConfig = {
34
+ appKey: string;
35
+ apiBaseUrl: string;
36
+ apiKey?: string;
37
+ signingSecret?: string;
38
+ appsCatalog?: AppsCatalogEntry[];
39
+ fetch?: FetchLike;
40
+ logger?: Logger;
41
+ enableIdempotency?: boolean;
42
+ };
43
+ type TrackEventName = "installation" | "uninstallation" | "subscription";
44
+ type TrackEventParams<TData = Record<string, unknown>> = {
45
+ eventName: TrackEventName | (string & {});
46
+ shopDomain: string;
47
+ partnerId?: string | null;
48
+ data?: TData;
49
+ externalId?: string;
50
+ };
51
+ type TrackResult<TData = Record<string, unknown>> = {
52
+ success: boolean;
53
+ message?: string;
54
+ status?: number;
55
+ data?: TData;
56
+ };
57
+ type CheckPartnerIdResult = {
58
+ success: boolean;
59
+ partnerId?: string;
60
+ message?: string;
61
+ status?: number;
62
+ };
63
+ type AssignPartnerIdInput = {
64
+ shopDomain: string;
65
+ partnerId: string;
66
+ };
67
+ type SubscriptionEventData = {
68
+ subscriptionStatus?: string;
69
+ subscriptionName?: string;
70
+ subscriptionId?: string;
71
+ subscriptionPrice?: number;
72
+ subscriptionPeriod?: string;
73
+ };
74
+
75
+ export type { AssignPartnerIdInput as A, ConnectorConfig as C, Logger as L, SubscriptionEventData as S, TrackEventParams as T, TrackResult as a, CheckPartnerIdResult as b, AppsCatalogEntry as c, CrossSellFetchOptions as d, CrossSellOptions as e, TrackEventName as f };
@@ -0,0 +1,75 @@
1
+ type FetchLike = typeof fetch;
2
+ type Logger = {
3
+ info?: (message: string, meta?: Record<string, unknown>) => void;
4
+ warn?: (message: string, meta?: Record<string, unknown>) => void;
5
+ error?: (message: string, meta?: Record<string, unknown>) => void;
6
+ };
7
+ type AppsCatalogEntry = {
8
+ key: string;
9
+ type: string;
10
+ title: string;
11
+ company?: string;
12
+ companyUrl?: string;
13
+ desc?: string;
14
+ url: string;
15
+ icon?: string;
16
+ enabled?: boolean;
17
+ priority?: number;
18
+ locales?: string[];
19
+ };
20
+ type CrossSellOptions = {
21
+ appName?: string;
22
+ installedAppKeys?: string[];
23
+ locale?: string;
24
+ shopPlan?: string;
25
+ appsCatalog?: AppsCatalogEntry[];
26
+ };
27
+ type CrossSellFetchOptions = CrossSellOptions & {
28
+ appsCatalogUrl?: string;
29
+ limit?: number;
30
+ fetch?: FetchLike;
31
+ logger?: Logger;
32
+ };
33
+ type ConnectorConfig = {
34
+ appKey: string;
35
+ apiBaseUrl: string;
36
+ apiKey?: string;
37
+ signingSecret?: string;
38
+ appsCatalog?: AppsCatalogEntry[];
39
+ fetch?: FetchLike;
40
+ logger?: Logger;
41
+ enableIdempotency?: boolean;
42
+ };
43
+ type TrackEventName = "installation" | "uninstallation" | "subscription";
44
+ type TrackEventParams<TData = Record<string, unknown>> = {
45
+ eventName: TrackEventName | (string & {});
46
+ shopDomain: string;
47
+ partnerId?: string | null;
48
+ data?: TData;
49
+ externalId?: string;
50
+ };
51
+ type TrackResult<TData = Record<string, unknown>> = {
52
+ success: boolean;
53
+ message?: string;
54
+ status?: number;
55
+ data?: TData;
56
+ };
57
+ type CheckPartnerIdResult = {
58
+ success: boolean;
59
+ partnerId?: string;
60
+ message?: string;
61
+ status?: number;
62
+ };
63
+ type AssignPartnerIdInput = {
64
+ shopDomain: string;
65
+ partnerId: string;
66
+ };
67
+ type SubscriptionEventData = {
68
+ subscriptionStatus?: string;
69
+ subscriptionName?: string;
70
+ subscriptionId?: string;
71
+ subscriptionPrice?: number;
72
+ subscriptionPeriod?: string;
73
+ };
74
+
75
+ export type { AssignPartnerIdInput as A, ConnectorConfig as C, Logger as L, SubscriptionEventData as S, TrackEventParams as T, TrackResult as a, CheckPartnerIdResult as b, AppsCatalogEntry as c, CrossSellFetchOptions as d, CrossSellOptions as e, TrackEventName as f };
@@ -0,0 +1,200 @@
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/ui/index.ts
21
+ var ui_exports = {};
22
+ __export(ui_exports, {
23
+ PartnerIdCard: () => PartnerIdCard,
24
+ Recommendations: () => Recommendations
25
+ });
26
+ module.exports = __toCommonJS(ui_exports);
27
+
28
+ // src/ui/PartnerIdCard.tsx
29
+ var import_polaris = require("@shopify/polaris");
30
+ var import_jsx_runtime = require("react/jsx-runtime");
31
+ function PartnerIdCard({
32
+ partnerId,
33
+ onPartnerIdChange,
34
+ onSave,
35
+ saveLabel,
36
+ loading,
37
+ saving,
38
+ locked,
39
+ error,
40
+ label,
41
+ placeholder,
42
+ helpText,
43
+ saveDisabled
44
+ }) {
45
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
46
+ import_polaris.LegacyCard,
47
+ {
48
+ sectioned: true,
49
+ primaryFooterAction: {
50
+ content: saveLabel,
51
+ loading: Boolean(saving),
52
+ onAction: onSave,
53
+ disabled: Boolean(saveDisabled)
54
+ },
55
+ children: loading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_polaris.SkeletonBodyText, { lines: 1 }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_polaris.FormLayout, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
56
+ import_polaris.TextField,
57
+ {
58
+ error,
59
+ label,
60
+ value: partnerId,
61
+ onChange: onPartnerIdChange,
62
+ placeholder,
63
+ helpText,
64
+ autoComplete: "off",
65
+ disabled: Boolean(locked || loading)
66
+ }
67
+ ) })
68
+ }
69
+ );
70
+ }
71
+
72
+ // src/ui/Recommendations.tsx
73
+ var import_polaris2 = require("@shopify/polaris");
74
+ var import_polaris_icons = require("@shopify/polaris-icons");
75
+ var import_react = require("react");
76
+ var import_jsx_runtime2 = require("react/jsx-runtime");
77
+ function Recommendations({
78
+ recommendations,
79
+ loading,
80
+ title,
81
+ dismissLabel,
82
+ installLabel,
83
+ freePlanLabel,
84
+ newBadgeLabel,
85
+ onDismiss,
86
+ openUrl,
87
+ getDescription
88
+ }) {
89
+ const [menuActive, setMenuActive] = (0, import_react.useState)(false);
90
+ if (!recommendations || recommendations.length === 0) {
91
+ return null;
92
+ }
93
+ const handleOpenUrl = (url) => {
94
+ if (openUrl) return openUrl(url);
95
+ if (typeof window !== "undefined") {
96
+ window.open(url, "_blank", "noopener,noreferrer");
97
+ }
98
+ };
99
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
100
+ import_polaris2.LegacyCard,
101
+ {
102
+ sectioned: true,
103
+ title: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
104
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_polaris2.Text, { variant: "headingSm", as: "h3", children: title }),
105
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
106
+ import_polaris2.Popover,
107
+ {
108
+ active: menuActive,
109
+ activator: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
110
+ import_polaris2.Button,
111
+ {
112
+ onClick: () => setMenuActive(!menuActive),
113
+ icon: import_polaris_icons.MenuHorizontalIcon,
114
+ variant: "plain",
115
+ accessibilityLabel: "Menu"
116
+ }
117
+ ),
118
+ onClose: () => setMenuActive(false),
119
+ preferredAlignment: "right",
120
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
121
+ import_polaris2.ActionList,
122
+ {
123
+ actionRole: "menuitem",
124
+ items: [
125
+ {
126
+ content: dismissLabel,
127
+ onAction: () => {
128
+ setMenuActive(false);
129
+ onDismiss();
130
+ }
131
+ }
132
+ ]
133
+ }
134
+ )
135
+ }
136
+ ) })
137
+ ] }),
138
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_polaris2.Layout, { children: recommendations.map((rec, index) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_polaris2.Layout.Section, { variant: "oneThird", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_polaris2.LegacyCard, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_polaris2.LegacyCard.Section, { children: [
139
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "flex-start", gap: "16px", position: "relative" }, children: [
140
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
141
+ "div",
142
+ {
143
+ style: {
144
+ flexShrink: 0,
145
+ width: "48px",
146
+ height: "48px",
147
+ borderRadius: "12px",
148
+ overflow: "hidden",
149
+ backgroundColor: "#f6f6f7",
150
+ display: "flex",
151
+ alignItems: "center",
152
+ justifyContent: "center"
153
+ },
154
+ children: rec.icon ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
155
+ "img",
156
+ {
157
+ src: rec.icon,
158
+ alt: rec.title,
159
+ style: { width: "48px", height: "48px", objectFit: "cover" }
160
+ }
161
+ ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
162
+ "div",
163
+ {
164
+ style: {
165
+ width: "48px",
166
+ height: "48px",
167
+ backgroundColor: "#e1e1e1",
168
+ borderRadius: "12px"
169
+ }
170
+ }
171
+ )
172
+ }
173
+ ),
174
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { flex: 1, minWidth: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { position: "relative" }, children: [
175
+ rec.newBadge && newBadgeLabel && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { position: "absolute", right: "-8px", top: "-8px" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_polaris2.Badge, { tone: "info", children: newBadgeLabel }) }),
176
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_polaris2.Text, { variant: "headingSm", as: "h3", truncate: true, children: rec.title }),
177
+ freePlanLabel && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { marginTop: "6px" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_polaris2.Badge, { tone: "info", children: freePlanLabel }) })
178
+ ] }) })
179
+ ] }),
180
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { marginTop: "12px" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_polaris2.Text, { variant: "bodySm", tone: "subdued", as: "p", children: getDescription ? getDescription(rec) : rec.desc }) }),
181
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { marginTop: "12px" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
182
+ import_polaris2.Button,
183
+ {
184
+ disabled: loading,
185
+ size: "micro",
186
+ variant: "plain",
187
+ onClick: () => handleOpenUrl(rec.url),
188
+ icon: import_polaris_icons.ExternalIcon,
189
+ children: installLabel
190
+ }
191
+ ) })
192
+ ] }) }) }, `${rec.key}-${index}`)) })
193
+ }
194
+ );
195
+ }
196
+ // Annotate the CommonJS export names for ESM import in node:
197
+ 0 && (module.exports = {
198
+ PartnerIdCard,
199
+ Recommendations
200
+ });
@@ -0,0 +1,38 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { TextFieldProps } from '@shopify/polaris';
3
+ import { c as AppsCatalogEntry } from '../types-B6Duba-V.cjs';
4
+
5
+ type PartnerIdCardProps = {
6
+ partnerId: string;
7
+ onPartnerIdChange: (value: string) => void;
8
+ onSave: () => void;
9
+ saveLabel: string;
10
+ loading?: boolean;
11
+ saving?: boolean;
12
+ locked?: boolean;
13
+ error?: string | TextFieldProps["error"];
14
+ label: string;
15
+ placeholder?: string;
16
+ helpText?: string;
17
+ saveDisabled?: boolean;
18
+ };
19
+ declare function PartnerIdCard({ partnerId, onPartnerIdChange, onSave, saveLabel, loading, saving, locked, error, label, placeholder, helpText, saveDisabled, }: PartnerIdCardProps): react_jsx_runtime.JSX.Element;
20
+
21
+ type RecommendationItem = AppsCatalogEntry & {
22
+ newBadge?: boolean;
23
+ };
24
+ type RecommendationsProps = {
25
+ recommendations: RecommendationItem[];
26
+ loading?: boolean;
27
+ title: string;
28
+ dismissLabel: string;
29
+ installLabel: string;
30
+ freePlanLabel?: string;
31
+ newBadgeLabel?: string;
32
+ onDismiss: () => void;
33
+ openUrl?: (url: string) => void;
34
+ getDescription?: (item: RecommendationItem) => string;
35
+ };
36
+ declare function Recommendations({ recommendations, loading, title, dismissLabel, installLabel, freePlanLabel, newBadgeLabel, onDismiss, openUrl, getDescription, }: RecommendationsProps): react_jsx_runtime.JSX.Element | null;
37
+
38
+ export { PartnerIdCard, type PartnerIdCardProps, type RecommendationItem, Recommendations, type RecommendationsProps };
@@ -0,0 +1,38 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { TextFieldProps } from '@shopify/polaris';
3
+ import { c as AppsCatalogEntry } from '../types-B6Duba-V.js';
4
+
5
+ type PartnerIdCardProps = {
6
+ partnerId: string;
7
+ onPartnerIdChange: (value: string) => void;
8
+ onSave: () => void;
9
+ saveLabel: string;
10
+ loading?: boolean;
11
+ saving?: boolean;
12
+ locked?: boolean;
13
+ error?: string | TextFieldProps["error"];
14
+ label: string;
15
+ placeholder?: string;
16
+ helpText?: string;
17
+ saveDisabled?: boolean;
18
+ };
19
+ declare function PartnerIdCard({ partnerId, onPartnerIdChange, onSave, saveLabel, loading, saving, locked, error, label, placeholder, helpText, saveDisabled, }: PartnerIdCardProps): react_jsx_runtime.JSX.Element;
20
+
21
+ type RecommendationItem = AppsCatalogEntry & {
22
+ newBadge?: boolean;
23
+ };
24
+ type RecommendationsProps = {
25
+ recommendations: RecommendationItem[];
26
+ loading?: boolean;
27
+ title: string;
28
+ dismissLabel: string;
29
+ installLabel: string;
30
+ freePlanLabel?: string;
31
+ newBadgeLabel?: string;
32
+ onDismiss: () => void;
33
+ openUrl?: (url: string) => void;
34
+ getDescription?: (item: RecommendationItem) => string;
35
+ };
36
+ declare function Recommendations({ recommendations, loading, title, dismissLabel, installLabel, freePlanLabel, newBadgeLabel, onDismiss, openUrl, getDescription, }: RecommendationsProps): react_jsx_runtime.JSX.Element | null;
37
+
38
+ export { PartnerIdCard, type PartnerIdCardProps, type RecommendationItem, Recommendations, type RecommendationsProps };
@@ -0,0 +1,172 @@
1
+ // src/ui/PartnerIdCard.tsx
2
+ import { FormLayout, LegacyCard, SkeletonBodyText, TextField } from "@shopify/polaris";
3
+ import { jsx } from "react/jsx-runtime";
4
+ function PartnerIdCard({
5
+ partnerId,
6
+ onPartnerIdChange,
7
+ onSave,
8
+ saveLabel,
9
+ loading,
10
+ saving,
11
+ locked,
12
+ error,
13
+ label,
14
+ placeholder,
15
+ helpText,
16
+ saveDisabled
17
+ }) {
18
+ return /* @__PURE__ */ jsx(
19
+ LegacyCard,
20
+ {
21
+ sectioned: true,
22
+ primaryFooterAction: {
23
+ content: saveLabel,
24
+ loading: Boolean(saving),
25
+ onAction: onSave,
26
+ disabled: Boolean(saveDisabled)
27
+ },
28
+ children: loading ? /* @__PURE__ */ jsx(SkeletonBodyText, { lines: 1 }) : /* @__PURE__ */ jsx(FormLayout, { children: /* @__PURE__ */ jsx(
29
+ TextField,
30
+ {
31
+ error,
32
+ label,
33
+ value: partnerId,
34
+ onChange: onPartnerIdChange,
35
+ placeholder,
36
+ helpText,
37
+ autoComplete: "off",
38
+ disabled: Boolean(locked || loading)
39
+ }
40
+ ) })
41
+ }
42
+ );
43
+ }
44
+
45
+ // src/ui/Recommendations.tsx
46
+ import { ActionList, Badge, Button, Layout, LegacyCard as LegacyCard2, Popover, Text } from "@shopify/polaris";
47
+ import { ExternalIcon, MenuHorizontalIcon } from "@shopify/polaris-icons";
48
+ import { useState } from "react";
49
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
50
+ function Recommendations({
51
+ recommendations,
52
+ loading,
53
+ title,
54
+ dismissLabel,
55
+ installLabel,
56
+ freePlanLabel,
57
+ newBadgeLabel,
58
+ onDismiss,
59
+ openUrl,
60
+ getDescription
61
+ }) {
62
+ const [menuActive, setMenuActive] = useState(false);
63
+ if (!recommendations || recommendations.length === 0) {
64
+ return null;
65
+ }
66
+ const handleOpenUrl = (url) => {
67
+ if (openUrl) return openUrl(url);
68
+ if (typeof window !== "undefined") {
69
+ window.open(url, "_blank", "noopener,noreferrer");
70
+ }
71
+ };
72
+ return /* @__PURE__ */ jsx2(
73
+ LegacyCard2,
74
+ {
75
+ sectioned: true,
76
+ title: /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
77
+ /* @__PURE__ */ jsx2(Text, { variant: "headingSm", as: "h3", children: title }),
78
+ /* @__PURE__ */ jsx2("div", { children: /* @__PURE__ */ jsx2(
79
+ Popover,
80
+ {
81
+ active: menuActive,
82
+ activator: /* @__PURE__ */ jsx2(
83
+ Button,
84
+ {
85
+ onClick: () => setMenuActive(!menuActive),
86
+ icon: MenuHorizontalIcon,
87
+ variant: "plain",
88
+ accessibilityLabel: "Menu"
89
+ }
90
+ ),
91
+ onClose: () => setMenuActive(false),
92
+ preferredAlignment: "right",
93
+ children: /* @__PURE__ */ jsx2(
94
+ ActionList,
95
+ {
96
+ actionRole: "menuitem",
97
+ items: [
98
+ {
99
+ content: dismissLabel,
100
+ onAction: () => {
101
+ setMenuActive(false);
102
+ onDismiss();
103
+ }
104
+ }
105
+ ]
106
+ }
107
+ )
108
+ }
109
+ ) })
110
+ ] }),
111
+ children: /* @__PURE__ */ jsx2(Layout, { children: recommendations.map((rec, index) => /* @__PURE__ */ jsx2(Layout.Section, { variant: "oneThird", children: /* @__PURE__ */ jsx2(LegacyCard2, { children: /* @__PURE__ */ jsxs(LegacyCard2.Section, { children: [
112
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "flex-start", gap: "16px", position: "relative" }, children: [
113
+ /* @__PURE__ */ jsx2(
114
+ "div",
115
+ {
116
+ style: {
117
+ flexShrink: 0,
118
+ width: "48px",
119
+ height: "48px",
120
+ borderRadius: "12px",
121
+ overflow: "hidden",
122
+ backgroundColor: "#f6f6f7",
123
+ display: "flex",
124
+ alignItems: "center",
125
+ justifyContent: "center"
126
+ },
127
+ children: rec.icon ? /* @__PURE__ */ jsx2(
128
+ "img",
129
+ {
130
+ src: rec.icon,
131
+ alt: rec.title,
132
+ style: { width: "48px", height: "48px", objectFit: "cover" }
133
+ }
134
+ ) : /* @__PURE__ */ jsx2(
135
+ "div",
136
+ {
137
+ style: {
138
+ width: "48px",
139
+ height: "48px",
140
+ backgroundColor: "#e1e1e1",
141
+ borderRadius: "12px"
142
+ }
143
+ }
144
+ )
145
+ }
146
+ ),
147
+ /* @__PURE__ */ jsx2("div", { style: { flex: 1, minWidth: 0 }, children: /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
148
+ rec.newBadge && newBadgeLabel && /* @__PURE__ */ jsx2("div", { style: { position: "absolute", right: "-8px", top: "-8px" }, children: /* @__PURE__ */ jsx2(Badge, { tone: "info", children: newBadgeLabel }) }),
149
+ /* @__PURE__ */ jsx2(Text, { variant: "headingSm", as: "h3", truncate: true, children: rec.title }),
150
+ freePlanLabel && /* @__PURE__ */ jsx2("div", { style: { marginTop: "6px" }, children: /* @__PURE__ */ jsx2(Badge, { tone: "info", children: freePlanLabel }) })
151
+ ] }) })
152
+ ] }),
153
+ /* @__PURE__ */ jsx2("div", { style: { marginTop: "12px" }, children: /* @__PURE__ */ jsx2(Text, { variant: "bodySm", tone: "subdued", as: "p", children: getDescription ? getDescription(rec) : rec.desc }) }),
154
+ /* @__PURE__ */ jsx2("div", { style: { marginTop: "12px" }, children: /* @__PURE__ */ jsx2(
155
+ Button,
156
+ {
157
+ disabled: loading,
158
+ size: "micro",
159
+ variant: "plain",
160
+ onClick: () => handleOpenUrl(rec.url),
161
+ icon: ExternalIcon,
162
+ children: installLabel
163
+ }
164
+ ) })
165
+ ] }) }) }, `${rec.key}-${index}`)) })
166
+ }
167
+ );
168
+ }
169
+ export {
170
+ PartnerIdCard,
171
+ Recommendations
172
+ };
package/package.json CHANGED
@@ -1,23 +1,32 @@
1
1
  {
2
2
  "name": "@progus/connector",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Progus partner/affiliate connector helpers",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "./dist/index.cjs",
8
- "module": "./dist/index.mjs",
8
+ "module": "./dist/index.js",
9
9
  "types": "./dist/index.d.ts",
10
10
  "exports": {
11
11
  ".": {
12
12
  "types": "./dist/index.d.ts",
13
- "import": "./dist/index.mjs",
13
+ "import": "./dist/index.js",
14
14
  "require": "./dist/index.cjs"
15
+ },
16
+ "./browser": {
17
+ "types": "./dist/browser/index.d.ts",
18
+ "import": "./dist/browser/index.js",
19
+ "require": "./dist/browser/index.cjs"
20
+ },
21
+ "./ui": {
22
+ "types": "./dist/ui/index.d.ts",
23
+ "import": "./dist/ui/index.js",
24
+ "require": "./dist/ui/index.cjs"
15
25
  }
16
26
  },
17
27
  "files": [
18
28
  "dist",
19
- "README.md",
20
- "src/appsCatalog.json"
29
+ "README.md"
21
30
  ],
22
31
  "sideEffects": false,
23
32
  "engines": {
@@ -27,12 +36,21 @@
27
36
  "access": "public"
28
37
  },
29
38
  "scripts": {
30
- "build": "tsup src/index.ts --format esm,cjs --dts --clean --target node18",
31
- "dev": "tsup src/index.ts --format esm,cjs --dts --watch --target node18",
39
+ "build": "tsup src/index.ts src/ui/index.ts src/browser/index.ts --format esm,cjs --dts --clean --target node18 --external react --external react-dom --external @shopify/polaris --external @shopify/polaris-icons",
40
+ "dev": "tsup src/index.ts src/ui/index.ts src/browser/index.ts --format esm,cjs --dts --watch --target node18 --external react --external react-dom --external @shopify/polaris --external @shopify/polaris-icons",
41
+ "prepare": "npm run build",
32
42
  "smoke": "tsx scripts/smoke.ts"
33
43
  },
44
+ "peerDependencies": {
45
+ "@shopify/polaris": ">=13",
46
+ "@shopify/polaris-icons": ">=8",
47
+ "react": ">=18",
48
+ "react-dom": ">=18"
49
+ },
34
50
  "devDependencies": {
35
51
  "@types/node": "^20.11.30",
52
+ "@types/react": "^18.2.66",
53
+ "@types/react-dom": "^18.2.22",
36
54
  "tsup": "^8.0.1",
37
55
  "tsx": "^4.7.1",
38
56
  "typescript": "^5.4.5"
@@ -1,26 +0,0 @@
1
- [
2
- {
3
- "key": "progus_cod",
4
- "type": "app",
5
- "title": "Progus COD",
6
- "company": "Progus",
7
- "companyUrl": "https://progus.com",
8
- "desc": "Automate COD Fees & Hide/Show Cash on Delivery by Rules",
9
- "url": "https://apps.shopify.com/progus-cod",
10
- "icon": "https://cdn.shopify.com/app-store/listing_images/bc537219cc3ed2bd4e7e3e683fe6b74a/icon/CMi_6dTEkIoDEAE=.png",
11
- "priority": 100,
12
- "enabled": true
13
- },
14
- {
15
- "key": "progus_trust_badges",
16
- "type": "app",
17
- "title": "Progus Trust Badges",
18
- "company": "Progus",
19
- "companyUrl": "https://progus.com",
20
- "desc": "Add Trust Badges to your store to build trust and credibility.",
21
- "url": "https://apps.shopify.com/progus-trust-badges-1",
22
- "icon": "https://cdn.shopify.com/app-store/listing_images/f9d0009e237f27d2db35b41ef99be858/icon/CJ3y1qDn1JEDEAE=.png",
23
- "priority": 90,
24
- "enabled": true
25
- }
26
- ]