@progus/connector 0.4.0 → 0.5.1
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 +3 -19
- package/dist/index.cjs +0 -225
- package/dist/index.d.cts +2 -70
- package/dist/index.d.ts +2 -70
- package/dist/index.js +0 -220
- package/package.json +3 -12
package/README.md
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# @progus/connector
|
|
2
2
|
|
|
3
|
-
Headless connector for Progus partner/affiliate tracking
|
|
4
|
-
and cross-sell offers.
|
|
3
|
+
Headless connector for Progus partner/affiliate tracking and partner ID assignment.
|
|
5
4
|
|
|
6
5
|
## Installation
|
|
7
6
|
|
|
@@ -51,19 +50,6 @@ if (result.success) {
|
|
|
51
50
|
}
|
|
52
51
|
```
|
|
53
52
|
|
|
54
|
-
## Cross-sell offers (remote catalog)
|
|
55
|
-
|
|
56
|
-
```ts
|
|
57
|
-
import { getCrossSellOffersFromApi } from "@progus/connector";
|
|
58
|
-
|
|
59
|
-
const offers = await getCrossSellOffersFromApi({
|
|
60
|
-
currentAppKey: "progus-store-locator",
|
|
61
|
-
installedAppKeys: ["progus_cod"],
|
|
62
|
-
appsCatalogUrl: "https://appsdata.progus.workers.dev/recommendations",
|
|
63
|
-
limit: 3,
|
|
64
|
-
});
|
|
65
|
-
```
|
|
66
|
-
|
|
67
53
|
## Optional signing helper
|
|
68
54
|
|
|
69
55
|
```ts
|
|
@@ -72,11 +58,9 @@ import { signPayload } from "@progus/connector";
|
|
|
72
58
|
const signature = signPayload(JSON.stringify({ test: true }), process.env.PARTNERS_SECRET_KEY!);
|
|
73
59
|
```
|
|
74
60
|
|
|
75
|
-
## UI components
|
|
61
|
+
## UI components and cross-sell
|
|
76
62
|
|
|
77
|
-
|
|
78
|
-
import { Recommendations, PartnerIdCard } from "@progus/connector";
|
|
79
|
-
```
|
|
63
|
+
Use `@progus/connector-ui` for browser-safe helpers and UI components.
|
|
80
64
|
|
|
81
65
|
## Environment variables
|
|
82
66
|
|
package/dist/index.cjs
CHANGED
|
@@ -20,12 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
-
PartnerIdCard: () => PartnerIdCard,
|
|
24
|
-
Recommendations: () => Recommendations,
|
|
25
23
|
createProgusConnector: () => createProgusConnector,
|
|
26
|
-
fetchAppsCatalog: () => fetchAppsCatalog,
|
|
27
|
-
getCrossSellOffers: () => getCrossSellOffers,
|
|
28
|
-
getCrossSellOffersFromApi: () => getCrossSellOffersFromApi,
|
|
29
24
|
normalizePartnerId: () => normalizePartnerId,
|
|
30
25
|
signPayload: () => signPayload
|
|
31
26
|
});
|
|
@@ -211,229 +206,9 @@ function createProgusConnector(config) {
|
|
|
211
206
|
checkPartnerId
|
|
212
207
|
};
|
|
213
208
|
}
|
|
214
|
-
|
|
215
|
-
// src/crossSell.ts
|
|
216
|
-
function getCrossSellOffers(options = {}) {
|
|
217
|
-
const appsCatalog = options.appsCatalog ?? [];
|
|
218
|
-
const installedKeys = new Set(
|
|
219
|
-
[options.currentAppKey, ...options.installedAppKeys ?? []].filter(
|
|
220
|
-
(key) => Boolean(key)
|
|
221
|
-
)
|
|
222
|
-
);
|
|
223
|
-
const locale = options.locale;
|
|
224
|
-
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));
|
|
225
|
-
}
|
|
226
|
-
var DEFAULT_APPS_CATALOG_URL = "https://appsdata.progus.workers.dev/recommendations";
|
|
227
|
-
async function fetchAppsCatalog(options = {}) {
|
|
228
|
-
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
229
|
-
const logger = options.logger ?? console;
|
|
230
|
-
const appName = options.appName ?? options.currentAppKey;
|
|
231
|
-
const baseUrl = stripTrailingSlash(options.appsCatalogUrl ?? DEFAULT_APPS_CATALOG_URL);
|
|
232
|
-
const limit = typeof options.limit === "number" ? options.limit : 3;
|
|
233
|
-
const params = new URLSearchParams();
|
|
234
|
-
if (appName) params.set("appName", appName);
|
|
235
|
-
if (limit > 0) params.set("limit", String(limit));
|
|
236
|
-
const url = params.toString() ? `${baseUrl}?${params.toString()}` : baseUrl;
|
|
237
|
-
if (!fetchImpl) {
|
|
238
|
-
throw new Error("Fetch implementation is required");
|
|
239
|
-
}
|
|
240
|
-
try {
|
|
241
|
-
const response = await fetchImpl(url);
|
|
242
|
-
const text = await response.text();
|
|
243
|
-
const parsed = safeJsonParse(text);
|
|
244
|
-
if (!response.ok || !parsed) {
|
|
245
|
-
logger?.error?.("Failed to fetch apps catalog", { status: response.status });
|
|
246
|
-
return [];
|
|
247
|
-
}
|
|
248
|
-
return parsed;
|
|
249
|
-
} catch (error) {
|
|
250
|
-
logger?.error?.("Failed to fetch apps catalog", {
|
|
251
|
-
error: error instanceof Error ? error.message : String(error)
|
|
252
|
-
});
|
|
253
|
-
return [];
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
async function getCrossSellOffersFromApi(options = {}) {
|
|
257
|
-
const appsCatalog = options.appsCatalog ?? await fetchAppsCatalog(options);
|
|
258
|
-
return getCrossSellOffers({ ...options, appsCatalog });
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// src/ui/PartnerIdCard.tsx
|
|
262
|
-
var import_polaris = require("@shopify/polaris");
|
|
263
|
-
var import_jsx_runtime = require("react/jsx-runtime");
|
|
264
|
-
function PartnerIdCard({
|
|
265
|
-
partnerId,
|
|
266
|
-
onPartnerIdChange,
|
|
267
|
-
onSave,
|
|
268
|
-
saveLabel,
|
|
269
|
-
loading,
|
|
270
|
-
saving,
|
|
271
|
-
locked,
|
|
272
|
-
error,
|
|
273
|
-
label,
|
|
274
|
-
placeholder,
|
|
275
|
-
helpText,
|
|
276
|
-
saveDisabled
|
|
277
|
-
}) {
|
|
278
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
279
|
-
import_polaris.LegacyCard,
|
|
280
|
-
{
|
|
281
|
-
sectioned: true,
|
|
282
|
-
primaryFooterAction: {
|
|
283
|
-
content: saveLabel,
|
|
284
|
-
loading: Boolean(saving),
|
|
285
|
-
onAction: onSave,
|
|
286
|
-
disabled: Boolean(saveDisabled)
|
|
287
|
-
},
|
|
288
|
-
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)(
|
|
289
|
-
import_polaris.TextField,
|
|
290
|
-
{
|
|
291
|
-
error,
|
|
292
|
-
label,
|
|
293
|
-
value: partnerId,
|
|
294
|
-
onChange: onPartnerIdChange,
|
|
295
|
-
placeholder,
|
|
296
|
-
helpText,
|
|
297
|
-
autoComplete: "off",
|
|
298
|
-
disabled: Boolean(locked || loading)
|
|
299
|
-
}
|
|
300
|
-
) })
|
|
301
|
-
}
|
|
302
|
-
);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// src/ui/Recommendations.tsx
|
|
306
|
-
var import_polaris2 = require("@shopify/polaris");
|
|
307
|
-
var import_polaris_icons = require("@shopify/polaris-icons");
|
|
308
|
-
var import_react = require("react");
|
|
309
|
-
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
310
|
-
function Recommendations({
|
|
311
|
-
recommendations,
|
|
312
|
-
loading,
|
|
313
|
-
title,
|
|
314
|
-
dismissLabel,
|
|
315
|
-
installLabel,
|
|
316
|
-
freePlanLabel,
|
|
317
|
-
newBadgeLabel,
|
|
318
|
-
onDismiss,
|
|
319
|
-
openUrl,
|
|
320
|
-
getDescription
|
|
321
|
-
}) {
|
|
322
|
-
const [menuActive, setMenuActive] = (0, import_react.useState)(false);
|
|
323
|
-
if (!recommendations || recommendations.length === 0) {
|
|
324
|
-
return null;
|
|
325
|
-
}
|
|
326
|
-
const handleOpenUrl = (url) => {
|
|
327
|
-
if (openUrl) return openUrl(url);
|
|
328
|
-
if (typeof window !== "undefined") {
|
|
329
|
-
window.open(url, "_blank", "noopener,noreferrer");
|
|
330
|
-
}
|
|
331
|
-
};
|
|
332
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
333
|
-
import_polaris2.LegacyCard,
|
|
334
|
-
{
|
|
335
|
-
sectioned: true,
|
|
336
|
-
title: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
|
|
337
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_polaris2.Text, { variant: "headingSm", as: "h3", children: title }),
|
|
338
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
339
|
-
import_polaris2.Popover,
|
|
340
|
-
{
|
|
341
|
-
active: menuActive,
|
|
342
|
-
activator: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
343
|
-
import_polaris2.Button,
|
|
344
|
-
{
|
|
345
|
-
onClick: () => setMenuActive(!menuActive),
|
|
346
|
-
icon: import_polaris_icons.MenuHorizontalIcon,
|
|
347
|
-
variant: "plain",
|
|
348
|
-
accessibilityLabel: "Menu"
|
|
349
|
-
}
|
|
350
|
-
),
|
|
351
|
-
onClose: () => setMenuActive(false),
|
|
352
|
-
preferredAlignment: "right",
|
|
353
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
354
|
-
import_polaris2.ActionList,
|
|
355
|
-
{
|
|
356
|
-
actionRole: "menuitem",
|
|
357
|
-
items: [
|
|
358
|
-
{
|
|
359
|
-
content: dismissLabel,
|
|
360
|
-
onAction: () => {
|
|
361
|
-
setMenuActive(false);
|
|
362
|
-
onDismiss();
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
]
|
|
366
|
-
}
|
|
367
|
-
)
|
|
368
|
-
}
|
|
369
|
-
) })
|
|
370
|
-
] }),
|
|
371
|
-
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: [
|
|
372
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "flex-start", gap: "16px", position: "relative" }, children: [
|
|
373
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
374
|
-
"div",
|
|
375
|
-
{
|
|
376
|
-
style: {
|
|
377
|
-
flexShrink: 0,
|
|
378
|
-
width: "48px",
|
|
379
|
-
height: "48px",
|
|
380
|
-
borderRadius: "12px",
|
|
381
|
-
overflow: "hidden",
|
|
382
|
-
backgroundColor: "#f6f6f7",
|
|
383
|
-
display: "flex",
|
|
384
|
-
alignItems: "center",
|
|
385
|
-
justifyContent: "center"
|
|
386
|
-
},
|
|
387
|
-
children: rec.icon ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
388
|
-
"img",
|
|
389
|
-
{
|
|
390
|
-
src: rec.icon,
|
|
391
|
-
alt: rec.title,
|
|
392
|
-
style: { width: "48px", height: "48px", objectFit: "cover" }
|
|
393
|
-
}
|
|
394
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
395
|
-
"div",
|
|
396
|
-
{
|
|
397
|
-
style: {
|
|
398
|
-
width: "48px",
|
|
399
|
-
height: "48px",
|
|
400
|
-
backgroundColor: "#e1e1e1",
|
|
401
|
-
borderRadius: "12px"
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
)
|
|
405
|
-
}
|
|
406
|
-
),
|
|
407
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { flex: 1, minWidth: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { position: "relative" }, children: [
|
|
408
|
-
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 }) }),
|
|
409
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_polaris2.Text, { variant: "headingSm", as: "h3", truncate: true, children: rec.title }),
|
|
410
|
-
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 }) })
|
|
411
|
-
] }) })
|
|
412
|
-
] }),
|
|
413
|
-
/* @__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 }) }),
|
|
414
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { marginTop: "12px" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
415
|
-
import_polaris2.Button,
|
|
416
|
-
{
|
|
417
|
-
disabled: loading,
|
|
418
|
-
size: "micro",
|
|
419
|
-
variant: "plain",
|
|
420
|
-
onClick: () => handleOpenUrl(rec.url),
|
|
421
|
-
icon: import_polaris_icons.ExternalIcon,
|
|
422
|
-
children: installLabel
|
|
423
|
-
}
|
|
424
|
-
) })
|
|
425
|
-
] }) }) }, `${rec.key}-${index}`)) })
|
|
426
|
-
}
|
|
427
|
-
);
|
|
428
|
-
}
|
|
429
209
|
// Annotate the CommonJS export names for ESM import in node:
|
|
430
210
|
0 && (module.exports = {
|
|
431
|
-
PartnerIdCard,
|
|
432
|
-
Recommendations,
|
|
433
211
|
createProgusConnector,
|
|
434
|
-
fetchAppsCatalog,
|
|
435
|
-
getCrossSellOffers,
|
|
436
|
-
getCrossSellOffersFromApi,
|
|
437
212
|
normalizePartnerId,
|
|
438
213
|
signPayload
|
|
439
214
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,45 +1,14 @@
|
|
|
1
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { TextFieldProps } from '@shopify/polaris';
|
|
3
|
-
|
|
4
|
-
type FetchLike = typeof fetch;
|
|
5
1
|
type Logger = {
|
|
6
2
|
info?: (message: string, meta?: Record<string, unknown>) => void;
|
|
7
3
|
warn?: (message: string, meta?: Record<string, unknown>) => void;
|
|
8
4
|
error?: (message: string, meta?: Record<string, unknown>) => void;
|
|
9
5
|
};
|
|
10
|
-
type
|
|
11
|
-
key: string;
|
|
12
|
-
type: string;
|
|
13
|
-
title: string;
|
|
14
|
-
company?: string;
|
|
15
|
-
companyUrl?: string;
|
|
16
|
-
desc?: string;
|
|
17
|
-
url: string;
|
|
18
|
-
icon?: string;
|
|
19
|
-
enabled?: boolean;
|
|
20
|
-
priority?: number;
|
|
21
|
-
locales?: string[];
|
|
22
|
-
};
|
|
23
|
-
type CrossSellOptions = {
|
|
24
|
-
currentAppKey?: string;
|
|
25
|
-
installedAppKeys?: string[];
|
|
26
|
-
locale?: string;
|
|
27
|
-
shopPlan?: string;
|
|
28
|
-
appsCatalog?: AppsCatalogEntry[];
|
|
29
|
-
};
|
|
30
|
-
type CrossSellFetchOptions = CrossSellOptions & {
|
|
31
|
-
appName?: string;
|
|
32
|
-
appsCatalogUrl?: string;
|
|
33
|
-
limit?: number;
|
|
34
|
-
fetch?: FetchLike;
|
|
35
|
-
logger?: Logger;
|
|
36
|
-
};
|
|
6
|
+
type FetchLike = typeof fetch;
|
|
37
7
|
type ConnectorConfig = {
|
|
38
8
|
appKey: string;
|
|
39
9
|
apiBaseUrl: string;
|
|
40
10
|
apiKey?: string;
|
|
41
11
|
signingSecret?: string;
|
|
42
|
-
appsCatalog?: AppsCatalogEntry[];
|
|
43
12
|
fetch?: FetchLike;
|
|
44
13
|
logger?: Logger;
|
|
45
14
|
enableIdempotency?: boolean;
|
|
@@ -106,45 +75,8 @@ type Connector = {
|
|
|
106
75
|
};
|
|
107
76
|
declare function createProgusConnector(config: ConnectorConfig): Connector;
|
|
108
77
|
|
|
109
|
-
declare function getCrossSellOffers(options?: CrossSellOptions): AppsCatalogEntry[];
|
|
110
|
-
declare function fetchAppsCatalog(options?: CrossSellFetchOptions): Promise<AppsCatalogEntry[]>;
|
|
111
|
-
declare function getCrossSellOffersFromApi(options?: CrossSellFetchOptions): Promise<AppsCatalogEntry[]>;
|
|
112
|
-
|
|
113
78
|
declare function signPayload(body: string, secret: string): string;
|
|
114
79
|
|
|
115
80
|
declare function normalizePartnerId(value?: string | null): string | null;
|
|
116
81
|
|
|
117
|
-
type
|
|
118
|
-
partnerId: string;
|
|
119
|
-
onPartnerIdChange: (value: string) => void;
|
|
120
|
-
onSave: () => void;
|
|
121
|
-
saveLabel: string;
|
|
122
|
-
loading?: boolean;
|
|
123
|
-
saving?: boolean;
|
|
124
|
-
locked?: boolean;
|
|
125
|
-
error?: string | TextFieldProps["error"];
|
|
126
|
-
label: string;
|
|
127
|
-
placeholder?: string;
|
|
128
|
-
helpText?: string;
|
|
129
|
-
saveDisabled?: boolean;
|
|
130
|
-
};
|
|
131
|
-
declare function PartnerIdCard({ partnerId, onPartnerIdChange, onSave, saveLabel, loading, saving, locked, error, label, placeholder, helpText, saveDisabled, }: PartnerIdCardProps): react_jsx_runtime.JSX.Element;
|
|
132
|
-
|
|
133
|
-
type RecommendationItem = AppsCatalogEntry & {
|
|
134
|
-
newBadge?: boolean;
|
|
135
|
-
};
|
|
136
|
-
type RecommendationsProps = {
|
|
137
|
-
recommendations: RecommendationItem[];
|
|
138
|
-
loading?: boolean;
|
|
139
|
-
title: string;
|
|
140
|
-
dismissLabel: string;
|
|
141
|
-
installLabel: string;
|
|
142
|
-
freePlanLabel?: string;
|
|
143
|
-
newBadgeLabel?: string;
|
|
144
|
-
onDismiss: () => void;
|
|
145
|
-
openUrl?: (url: string) => void;
|
|
146
|
-
getDescription?: (item: RecommendationItem) => string;
|
|
147
|
-
};
|
|
148
|
-
declare function Recommendations({ recommendations, loading, title, dismissLabel, installLabel, freePlanLabel, newBadgeLabel, onDismiss, openUrl, getDescription, }: RecommendationsProps): react_jsx_runtime.JSX.Element | null;
|
|
149
|
-
|
|
150
|
-
export { type AppsCatalogEntry, type AssignPartnerIdInput, type CheckPartnerIdResult, type ConnectorConfig, type CrossSellFetchOptions, type CrossSellOptions, type Logger, PartnerIdCard, type PartnerIdCardProps, type RecommendationItem, Recommendations, type RecommendationsProps, type SubscriptionEventData, type TrackEventName, type TrackEventParams, type TrackResult, createProgusConnector, fetchAppsCatalog, getCrossSellOffers, getCrossSellOffersFromApi, normalizePartnerId, signPayload };
|
|
82
|
+
export { type AssignPartnerIdInput, type CheckPartnerIdResult, type ConnectorConfig, type Logger, type SubscriptionEventData, type TrackEventName, type TrackEventParams, type TrackResult, createProgusConnector, normalizePartnerId, signPayload };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,45 +1,14 @@
|
|
|
1
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { TextFieldProps } from '@shopify/polaris';
|
|
3
|
-
|
|
4
|
-
type FetchLike = typeof fetch;
|
|
5
1
|
type Logger = {
|
|
6
2
|
info?: (message: string, meta?: Record<string, unknown>) => void;
|
|
7
3
|
warn?: (message: string, meta?: Record<string, unknown>) => void;
|
|
8
4
|
error?: (message: string, meta?: Record<string, unknown>) => void;
|
|
9
5
|
};
|
|
10
|
-
type
|
|
11
|
-
key: string;
|
|
12
|
-
type: string;
|
|
13
|
-
title: string;
|
|
14
|
-
company?: string;
|
|
15
|
-
companyUrl?: string;
|
|
16
|
-
desc?: string;
|
|
17
|
-
url: string;
|
|
18
|
-
icon?: string;
|
|
19
|
-
enabled?: boolean;
|
|
20
|
-
priority?: number;
|
|
21
|
-
locales?: string[];
|
|
22
|
-
};
|
|
23
|
-
type CrossSellOptions = {
|
|
24
|
-
currentAppKey?: string;
|
|
25
|
-
installedAppKeys?: string[];
|
|
26
|
-
locale?: string;
|
|
27
|
-
shopPlan?: string;
|
|
28
|
-
appsCatalog?: AppsCatalogEntry[];
|
|
29
|
-
};
|
|
30
|
-
type CrossSellFetchOptions = CrossSellOptions & {
|
|
31
|
-
appName?: string;
|
|
32
|
-
appsCatalogUrl?: string;
|
|
33
|
-
limit?: number;
|
|
34
|
-
fetch?: FetchLike;
|
|
35
|
-
logger?: Logger;
|
|
36
|
-
};
|
|
6
|
+
type FetchLike = typeof fetch;
|
|
37
7
|
type ConnectorConfig = {
|
|
38
8
|
appKey: string;
|
|
39
9
|
apiBaseUrl: string;
|
|
40
10
|
apiKey?: string;
|
|
41
11
|
signingSecret?: string;
|
|
42
|
-
appsCatalog?: AppsCatalogEntry[];
|
|
43
12
|
fetch?: FetchLike;
|
|
44
13
|
logger?: Logger;
|
|
45
14
|
enableIdempotency?: boolean;
|
|
@@ -106,45 +75,8 @@ type Connector = {
|
|
|
106
75
|
};
|
|
107
76
|
declare function createProgusConnector(config: ConnectorConfig): Connector;
|
|
108
77
|
|
|
109
|
-
declare function getCrossSellOffers(options?: CrossSellOptions): AppsCatalogEntry[];
|
|
110
|
-
declare function fetchAppsCatalog(options?: CrossSellFetchOptions): Promise<AppsCatalogEntry[]>;
|
|
111
|
-
declare function getCrossSellOffersFromApi(options?: CrossSellFetchOptions): Promise<AppsCatalogEntry[]>;
|
|
112
|
-
|
|
113
78
|
declare function signPayload(body: string, secret: string): string;
|
|
114
79
|
|
|
115
80
|
declare function normalizePartnerId(value?: string | null): string | null;
|
|
116
81
|
|
|
117
|
-
type
|
|
118
|
-
partnerId: string;
|
|
119
|
-
onPartnerIdChange: (value: string) => void;
|
|
120
|
-
onSave: () => void;
|
|
121
|
-
saveLabel: string;
|
|
122
|
-
loading?: boolean;
|
|
123
|
-
saving?: boolean;
|
|
124
|
-
locked?: boolean;
|
|
125
|
-
error?: string | TextFieldProps["error"];
|
|
126
|
-
label: string;
|
|
127
|
-
placeholder?: string;
|
|
128
|
-
helpText?: string;
|
|
129
|
-
saveDisabled?: boolean;
|
|
130
|
-
};
|
|
131
|
-
declare function PartnerIdCard({ partnerId, onPartnerIdChange, onSave, saveLabel, loading, saving, locked, error, label, placeholder, helpText, saveDisabled, }: PartnerIdCardProps): react_jsx_runtime.JSX.Element;
|
|
132
|
-
|
|
133
|
-
type RecommendationItem = AppsCatalogEntry & {
|
|
134
|
-
newBadge?: boolean;
|
|
135
|
-
};
|
|
136
|
-
type RecommendationsProps = {
|
|
137
|
-
recommendations: RecommendationItem[];
|
|
138
|
-
loading?: boolean;
|
|
139
|
-
title: string;
|
|
140
|
-
dismissLabel: string;
|
|
141
|
-
installLabel: string;
|
|
142
|
-
freePlanLabel?: string;
|
|
143
|
-
newBadgeLabel?: string;
|
|
144
|
-
onDismiss: () => void;
|
|
145
|
-
openUrl?: (url: string) => void;
|
|
146
|
-
getDescription?: (item: RecommendationItem) => string;
|
|
147
|
-
};
|
|
148
|
-
declare function Recommendations({ recommendations, loading, title, dismissLabel, installLabel, freePlanLabel, newBadgeLabel, onDismiss, openUrl, getDescription, }: RecommendationsProps): react_jsx_runtime.JSX.Element | null;
|
|
149
|
-
|
|
150
|
-
export { type AppsCatalogEntry, type AssignPartnerIdInput, type CheckPartnerIdResult, type ConnectorConfig, type CrossSellFetchOptions, type CrossSellOptions, type Logger, PartnerIdCard, type PartnerIdCardProps, type RecommendationItem, Recommendations, type RecommendationsProps, type SubscriptionEventData, type TrackEventName, type TrackEventParams, type TrackResult, createProgusConnector, fetchAppsCatalog, getCrossSellOffers, getCrossSellOffersFromApi, normalizePartnerId, signPayload };
|
|
82
|
+
export { type AssignPartnerIdInput, type CheckPartnerIdResult, type ConnectorConfig, type Logger, type SubscriptionEventData, type TrackEventName, type TrackEventParams, type TrackResult, createProgusConnector, normalizePartnerId, signPayload };
|
package/dist/index.js
CHANGED
|
@@ -178,228 +178,8 @@ function createProgusConnector(config) {
|
|
|
178
178
|
checkPartnerId
|
|
179
179
|
};
|
|
180
180
|
}
|
|
181
|
-
|
|
182
|
-
// src/crossSell.ts
|
|
183
|
-
function getCrossSellOffers(options = {}) {
|
|
184
|
-
const appsCatalog = options.appsCatalog ?? [];
|
|
185
|
-
const installedKeys = new Set(
|
|
186
|
-
[options.currentAppKey, ...options.installedAppKeys ?? []].filter(
|
|
187
|
-
(key) => Boolean(key)
|
|
188
|
-
)
|
|
189
|
-
);
|
|
190
|
-
const locale = options.locale;
|
|
191
|
-
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));
|
|
192
|
-
}
|
|
193
|
-
var DEFAULT_APPS_CATALOG_URL = "https://appsdata.progus.workers.dev/recommendations";
|
|
194
|
-
async function fetchAppsCatalog(options = {}) {
|
|
195
|
-
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
196
|
-
const logger = options.logger ?? console;
|
|
197
|
-
const appName = options.appName ?? options.currentAppKey;
|
|
198
|
-
const baseUrl = stripTrailingSlash(options.appsCatalogUrl ?? DEFAULT_APPS_CATALOG_URL);
|
|
199
|
-
const limit = typeof options.limit === "number" ? options.limit : 3;
|
|
200
|
-
const params = new URLSearchParams();
|
|
201
|
-
if (appName) params.set("appName", appName);
|
|
202
|
-
if (limit > 0) params.set("limit", String(limit));
|
|
203
|
-
const url = params.toString() ? `${baseUrl}?${params.toString()}` : baseUrl;
|
|
204
|
-
if (!fetchImpl) {
|
|
205
|
-
throw new Error("Fetch implementation is required");
|
|
206
|
-
}
|
|
207
|
-
try {
|
|
208
|
-
const response = await fetchImpl(url);
|
|
209
|
-
const text = await response.text();
|
|
210
|
-
const parsed = safeJsonParse(text);
|
|
211
|
-
if (!response.ok || !parsed) {
|
|
212
|
-
logger?.error?.("Failed to fetch apps catalog", { status: response.status });
|
|
213
|
-
return [];
|
|
214
|
-
}
|
|
215
|
-
return parsed;
|
|
216
|
-
} catch (error) {
|
|
217
|
-
logger?.error?.("Failed to fetch apps catalog", {
|
|
218
|
-
error: error instanceof Error ? error.message : String(error)
|
|
219
|
-
});
|
|
220
|
-
return [];
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
async function getCrossSellOffersFromApi(options = {}) {
|
|
224
|
-
const appsCatalog = options.appsCatalog ?? await fetchAppsCatalog(options);
|
|
225
|
-
return getCrossSellOffers({ ...options, appsCatalog });
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// src/ui/PartnerIdCard.tsx
|
|
229
|
-
import { FormLayout, LegacyCard, SkeletonBodyText, TextField } from "@shopify/polaris";
|
|
230
|
-
import { jsx } from "react/jsx-runtime";
|
|
231
|
-
function PartnerIdCard({
|
|
232
|
-
partnerId,
|
|
233
|
-
onPartnerIdChange,
|
|
234
|
-
onSave,
|
|
235
|
-
saveLabel,
|
|
236
|
-
loading,
|
|
237
|
-
saving,
|
|
238
|
-
locked,
|
|
239
|
-
error,
|
|
240
|
-
label,
|
|
241
|
-
placeholder,
|
|
242
|
-
helpText,
|
|
243
|
-
saveDisabled
|
|
244
|
-
}) {
|
|
245
|
-
return /* @__PURE__ */ jsx(
|
|
246
|
-
LegacyCard,
|
|
247
|
-
{
|
|
248
|
-
sectioned: true,
|
|
249
|
-
primaryFooterAction: {
|
|
250
|
-
content: saveLabel,
|
|
251
|
-
loading: Boolean(saving),
|
|
252
|
-
onAction: onSave,
|
|
253
|
-
disabled: Boolean(saveDisabled)
|
|
254
|
-
},
|
|
255
|
-
children: loading ? /* @__PURE__ */ jsx(SkeletonBodyText, { lines: 1 }) : /* @__PURE__ */ jsx(FormLayout, { children: /* @__PURE__ */ jsx(
|
|
256
|
-
TextField,
|
|
257
|
-
{
|
|
258
|
-
error,
|
|
259
|
-
label,
|
|
260
|
-
value: partnerId,
|
|
261
|
-
onChange: onPartnerIdChange,
|
|
262
|
-
placeholder,
|
|
263
|
-
helpText,
|
|
264
|
-
autoComplete: "off",
|
|
265
|
-
disabled: Boolean(locked || loading)
|
|
266
|
-
}
|
|
267
|
-
) })
|
|
268
|
-
}
|
|
269
|
-
);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// src/ui/Recommendations.tsx
|
|
273
|
-
import { ActionList, Badge, Button, Layout, LegacyCard as LegacyCard2, Popover, Text } from "@shopify/polaris";
|
|
274
|
-
import { ExternalIcon, MenuHorizontalIcon } from "@shopify/polaris-icons";
|
|
275
|
-
import { useState } from "react";
|
|
276
|
-
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
277
|
-
function Recommendations({
|
|
278
|
-
recommendations,
|
|
279
|
-
loading,
|
|
280
|
-
title,
|
|
281
|
-
dismissLabel,
|
|
282
|
-
installLabel,
|
|
283
|
-
freePlanLabel,
|
|
284
|
-
newBadgeLabel,
|
|
285
|
-
onDismiss,
|
|
286
|
-
openUrl,
|
|
287
|
-
getDescription
|
|
288
|
-
}) {
|
|
289
|
-
const [menuActive, setMenuActive] = useState(false);
|
|
290
|
-
if (!recommendations || recommendations.length === 0) {
|
|
291
|
-
return null;
|
|
292
|
-
}
|
|
293
|
-
const handleOpenUrl = (url) => {
|
|
294
|
-
if (openUrl) return openUrl(url);
|
|
295
|
-
if (typeof window !== "undefined") {
|
|
296
|
-
window.open(url, "_blank", "noopener,noreferrer");
|
|
297
|
-
}
|
|
298
|
-
};
|
|
299
|
-
return /* @__PURE__ */ jsx2(
|
|
300
|
-
LegacyCard2,
|
|
301
|
-
{
|
|
302
|
-
sectioned: true,
|
|
303
|
-
title: /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
|
|
304
|
-
/* @__PURE__ */ jsx2(Text, { variant: "headingSm", as: "h3", children: title }),
|
|
305
|
-
/* @__PURE__ */ jsx2("div", { children: /* @__PURE__ */ jsx2(
|
|
306
|
-
Popover,
|
|
307
|
-
{
|
|
308
|
-
active: menuActive,
|
|
309
|
-
activator: /* @__PURE__ */ jsx2(
|
|
310
|
-
Button,
|
|
311
|
-
{
|
|
312
|
-
onClick: () => setMenuActive(!menuActive),
|
|
313
|
-
icon: MenuHorizontalIcon,
|
|
314
|
-
variant: "plain",
|
|
315
|
-
accessibilityLabel: "Menu"
|
|
316
|
-
}
|
|
317
|
-
),
|
|
318
|
-
onClose: () => setMenuActive(false),
|
|
319
|
-
preferredAlignment: "right",
|
|
320
|
-
children: /* @__PURE__ */ jsx2(
|
|
321
|
-
ActionList,
|
|
322
|
-
{
|
|
323
|
-
actionRole: "menuitem",
|
|
324
|
-
items: [
|
|
325
|
-
{
|
|
326
|
-
content: dismissLabel,
|
|
327
|
-
onAction: () => {
|
|
328
|
-
setMenuActive(false);
|
|
329
|
-
onDismiss();
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
]
|
|
333
|
-
}
|
|
334
|
-
)
|
|
335
|
-
}
|
|
336
|
-
) })
|
|
337
|
-
] }),
|
|
338
|
-
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: [
|
|
339
|
-
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "flex-start", gap: "16px", position: "relative" }, children: [
|
|
340
|
-
/* @__PURE__ */ jsx2(
|
|
341
|
-
"div",
|
|
342
|
-
{
|
|
343
|
-
style: {
|
|
344
|
-
flexShrink: 0,
|
|
345
|
-
width: "48px",
|
|
346
|
-
height: "48px",
|
|
347
|
-
borderRadius: "12px",
|
|
348
|
-
overflow: "hidden",
|
|
349
|
-
backgroundColor: "#f6f6f7",
|
|
350
|
-
display: "flex",
|
|
351
|
-
alignItems: "center",
|
|
352
|
-
justifyContent: "center"
|
|
353
|
-
},
|
|
354
|
-
children: rec.icon ? /* @__PURE__ */ jsx2(
|
|
355
|
-
"img",
|
|
356
|
-
{
|
|
357
|
-
src: rec.icon,
|
|
358
|
-
alt: rec.title,
|
|
359
|
-
style: { width: "48px", height: "48px", objectFit: "cover" }
|
|
360
|
-
}
|
|
361
|
-
) : /* @__PURE__ */ jsx2(
|
|
362
|
-
"div",
|
|
363
|
-
{
|
|
364
|
-
style: {
|
|
365
|
-
width: "48px",
|
|
366
|
-
height: "48px",
|
|
367
|
-
backgroundColor: "#e1e1e1",
|
|
368
|
-
borderRadius: "12px"
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
)
|
|
372
|
-
}
|
|
373
|
-
),
|
|
374
|
-
/* @__PURE__ */ jsx2("div", { style: { flex: 1, minWidth: 0 }, children: /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
|
|
375
|
-
rec.newBadge && newBadgeLabel && /* @__PURE__ */ jsx2("div", { style: { position: "absolute", right: "-8px", top: "-8px" }, children: /* @__PURE__ */ jsx2(Badge, { tone: "info", children: newBadgeLabel }) }),
|
|
376
|
-
/* @__PURE__ */ jsx2(Text, { variant: "headingSm", as: "h3", truncate: true, children: rec.title }),
|
|
377
|
-
freePlanLabel && /* @__PURE__ */ jsx2("div", { style: { marginTop: "6px" }, children: /* @__PURE__ */ jsx2(Badge, { tone: "info", children: freePlanLabel }) })
|
|
378
|
-
] }) })
|
|
379
|
-
] }),
|
|
380
|
-
/* @__PURE__ */ jsx2("div", { style: { marginTop: "12px" }, children: /* @__PURE__ */ jsx2(Text, { variant: "bodySm", tone: "subdued", as: "p", children: getDescription ? getDescription(rec) : rec.desc }) }),
|
|
381
|
-
/* @__PURE__ */ jsx2("div", { style: { marginTop: "12px" }, children: /* @__PURE__ */ jsx2(
|
|
382
|
-
Button,
|
|
383
|
-
{
|
|
384
|
-
disabled: loading,
|
|
385
|
-
size: "micro",
|
|
386
|
-
variant: "plain",
|
|
387
|
-
onClick: () => handleOpenUrl(rec.url),
|
|
388
|
-
icon: ExternalIcon,
|
|
389
|
-
children: installLabel
|
|
390
|
-
}
|
|
391
|
-
) })
|
|
392
|
-
] }) }) }, `${rec.key}-${index}`)) })
|
|
393
|
-
}
|
|
394
|
-
);
|
|
395
|
-
}
|
|
396
181
|
export {
|
|
397
|
-
PartnerIdCard,
|
|
398
|
-
Recommendations,
|
|
399
182
|
createProgusConnector,
|
|
400
|
-
fetchAppsCatalog,
|
|
401
|
-
getCrossSellOffers,
|
|
402
|
-
getCrossSellOffersFromApi,
|
|
403
183
|
normalizePartnerId,
|
|
404
184
|
signPayload
|
|
405
185
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@progus/connector",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Progus partner/affiliate connector helpers",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -26,21 +26,12 @@
|
|
|
26
26
|
"access": "public"
|
|
27
27
|
},
|
|
28
28
|
"scripts": {
|
|
29
|
-
"build": "tsup src/index.ts --format esm,cjs --dts --clean --target node18
|
|
30
|
-
"dev": "tsup src/index.ts --format esm,cjs --dts --watch --target node18
|
|
31
|
-
"prepare": "npm run build",
|
|
29
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --clean --target node18",
|
|
30
|
+
"dev": "tsup src/index.ts --format esm,cjs --dts --watch --target node18",
|
|
32
31
|
"smoke": "tsx scripts/smoke.ts"
|
|
33
32
|
},
|
|
34
|
-
"peerDependencies": {
|
|
35
|
-
"@shopify/polaris": ">=13",
|
|
36
|
-
"@shopify/polaris-icons": ">=8",
|
|
37
|
-
"react": ">=18",
|
|
38
|
-
"react-dom": ">=18"
|
|
39
|
-
},
|
|
40
33
|
"devDependencies": {
|
|
41
34
|
"@types/node": "^20.11.30",
|
|
42
|
-
"@types/react": "^18.2.66",
|
|
43
|
-
"@types/react-dom": "^18.2.22",
|
|
44
35
|
"tsup": "^8.0.1",
|
|
45
36
|
"tsx": "^4.7.1",
|
|
46
37
|
"typescript": "^5.4.5"
|