@fruga/sdk 1.0.2 → 1.3.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 +8 -1
- package/dist/connect.d.mts +16 -1
- package/dist/connect.d.ts +16 -1
- package/dist/connect.js +1 -1
- package/dist/connect.mjs +1 -1
- package/dist/index.d.mts +324 -21
- package/dist/index.d.ts +324 -21
- package/dist/index.js +356 -216
- package/dist/index.mjs +337 -210
- package/dist/loader-react.d.mts +140 -25
- package/dist/loader-react.d.ts +140 -25
- package/dist/loader-react.js +1 -1
- package/dist/loader-react.mjs +1 -1
- package/dist/loader.d.mts +168 -6
- package/dist/loader.d.ts +168 -6
- package/dist/loader.global.js +3 -42
- package/dist/loader.js +1 -1
- package/dist/loader.mjs +1 -1
- package/dist/relay.css +1 -1
- package/dist/relay.d.mts +20 -2
- package/dist/relay.d.ts +20 -2
- package/dist/relay.js +1 -1
- package/dist/relay.mjs +1 -1
- package/package.json +15 -3
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
9
|
var __export = (target, all) => {
|
|
9
10
|
for (var name in all)
|
|
10
11
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -26,6 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
27
|
mod
|
|
27
28
|
));
|
|
28
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
29
31
|
|
|
30
32
|
// packages/core/src/index.ts
|
|
31
33
|
var src_exports = {};
|
|
@@ -33,23 +35,38 @@ __export(src_exports, {
|
|
|
33
35
|
DEFAULT_API_URL: () => DEFAULT_API_URL,
|
|
34
36
|
DEFAULT_THEME: () => DEFAULT_THEME,
|
|
35
37
|
DefaultTheme: () => DEFAULT_THEME,
|
|
38
|
+
HOST_MOBILE_BREAKPOINT: () => HOST_MOBILE_BREAKPOINT,
|
|
36
39
|
HttpActivityService: () => HttpActivityService,
|
|
40
|
+
HttpError: () => HttpError,
|
|
37
41
|
HttpOffersService: () => HttpOffersService,
|
|
38
42
|
HttpPayoutService: () => HttpPayoutService,
|
|
39
43
|
HttpWalletService: () => HttpWalletService,
|
|
40
|
-
|
|
44
|
+
LEGACY_BOOTSTRAP_STORAGE_KEY: () => LEGACY_BOOTSTRAP_STORAGE_KEY,
|
|
45
|
+
MessageTransportContext: () => MessageTransportContext,
|
|
46
|
+
MessageTransportProvider: () => MessageTransportProvider,
|
|
47
|
+
PROTOCOL_VERSION: () => PROTOCOL_VERSION,
|
|
48
|
+
PostMessageTransport: () => PostMessageTransport,
|
|
49
|
+
SDK_VERSION: () => SDK_VERSION,
|
|
41
50
|
StorageService: () => StorageService,
|
|
42
|
-
|
|
51
|
+
UnauthorizedError: () => UnauthorizedError,
|
|
43
52
|
WidgetContainer: () => WidgetContainer,
|
|
44
|
-
WidgetHeader: () => WidgetHeader,
|
|
45
53
|
WidgetProvider: () => WidgetProvider,
|
|
54
|
+
createHostTransport: () => createHostTransport,
|
|
46
55
|
formatDate: () => formatDate,
|
|
47
56
|
getBaseUrl: () => getBaseUrl,
|
|
48
57
|
getDaysRemaining: () => getDaysRemaining,
|
|
49
58
|
getResolvedTheme: () => getResolvedTheme,
|
|
59
|
+
isFrugaMessage: () => isFrugaMessage,
|
|
60
|
+
isFrugaMessageType: () => isFrugaMessageType,
|
|
61
|
+
isTestMode: () => isTestMode,
|
|
50
62
|
isWithinDays: () => isWithinDays,
|
|
63
|
+
protocolMajor: () => protocolMajor,
|
|
64
|
+
resolvePayoutMode: () => resolvePayoutMode,
|
|
65
|
+
storageKey: () => storageKey,
|
|
51
66
|
truncateText: () => truncateText,
|
|
52
67
|
useDebounce: () => useDebounce,
|
|
68
|
+
useFrugaMessage: () => useFrugaMessage,
|
|
69
|
+
useMessageTransport: () => useMessageTransport,
|
|
53
70
|
useWidget: () => useWidget,
|
|
54
71
|
useWidgetStore: () => useWidgetStore
|
|
55
72
|
});
|
|
@@ -108,50 +125,51 @@ function getResolvedTheme(theme) {
|
|
|
108
125
|
};
|
|
109
126
|
}
|
|
110
127
|
|
|
111
|
-
// packages/core/src/
|
|
112
|
-
var
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
128
|
+
// packages/core/src/domain/entities/Messages.ts
|
|
129
|
+
var PROTOCOL_VERSION = "1.0";
|
|
130
|
+
var MESSAGE_TYPES = [
|
|
131
|
+
"NEED_TOKEN",
|
|
132
|
+
"FRUGA_TOKEN_UPDATE",
|
|
133
|
+
"FRUGA_GET_BALANCE",
|
|
134
|
+
"FRUGA_BALANCE_RESPONSE",
|
|
135
|
+
"FRUGA_RESIZE",
|
|
136
|
+
"FRUGA_HOST_RESIZE",
|
|
137
|
+
"FRUGA_READY",
|
|
138
|
+
"FRUGA_INIT"
|
|
139
|
+
];
|
|
140
|
+
var isFrugaMessageType = (value) => typeof value === "string" && MESSAGE_TYPES.includes(value);
|
|
141
|
+
var NEED_TOKEN_REASONS = ["ttl", "unauthorized"];
|
|
142
|
+
var isFrugaMessage = (data) => {
|
|
143
|
+
if (typeof data !== "object" || data === null) return false;
|
|
144
|
+
const candidate = data;
|
|
145
|
+
if (!isFrugaMessageType(candidate.type)) return false;
|
|
146
|
+
if (candidate.version !== void 0 && typeof candidate.version !== "string") return false;
|
|
147
|
+
if (candidate.type === "NEED_TOKEN") {
|
|
148
|
+
const { reason } = data;
|
|
149
|
+
return typeof reason === "string" && NEED_TOKEN_REASONS.includes(reason);
|
|
116
150
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
pending: data.pending ?? 0,
|
|
134
|
-
totalBalance: (data.available || 0) + (data.pending || 0),
|
|
135
|
-
lifetimeEarned: data.lifetimeEarned ?? 0,
|
|
136
|
-
lifetimeRedeemed: data.lifetimeRedeemed ?? 0,
|
|
137
|
-
updatedAt: data.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
138
|
-
requiredActions: data.requiredActions ?? null
|
|
139
|
-
};
|
|
140
|
-
} catch (error) {
|
|
141
|
-
console.warn(`[FrugaSDK] Network error fetching wallet. Returning zeroed wallet.`, error);
|
|
142
|
-
return this.getEmptyWallet();
|
|
143
|
-
}
|
|
151
|
+
return true;
|
|
152
|
+
};
|
|
153
|
+
var protocolMajor = (v) => Number.parseInt(v.split(".")[0] ?? "", 10);
|
|
154
|
+
|
|
155
|
+
// packages/core/src/domain/entities/Constants.ts
|
|
156
|
+
var HOST_MOBILE_BREAKPOINT = 768;
|
|
157
|
+
|
|
158
|
+
// packages/core/src/domain/entities/index.ts
|
|
159
|
+
var resolvePayoutMode = (value) => value === "PARTNER" || value === "USER" ? value : null;
|
|
160
|
+
|
|
161
|
+
// packages/core/src/domain/errors/HttpError.ts
|
|
162
|
+
var HttpError = class extends Error {
|
|
163
|
+
constructor(status, message) {
|
|
164
|
+
super(message);
|
|
165
|
+
this.status = status;
|
|
166
|
+
this.name = "HttpError";
|
|
144
167
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
lifetimeEarned: 0,
|
|
151
|
-
lifetimeRedeemed: 0,
|
|
152
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
153
|
-
requiredActions: null
|
|
154
|
-
};
|
|
168
|
+
};
|
|
169
|
+
var UnauthorizedError = class extends HttpError {
|
|
170
|
+
constructor(message = "Unauthorized") {
|
|
171
|
+
super(401, message);
|
|
172
|
+
this.name = "UnauthorizedError";
|
|
155
173
|
}
|
|
156
174
|
};
|
|
157
175
|
|
|
@@ -181,6 +199,41 @@ var BaseHttpService = class {
|
|
|
181
199
|
});
|
|
182
200
|
}
|
|
183
201
|
};
|
|
202
|
+
var assertOk = (response, context) => {
|
|
203
|
+
if (response.ok) return;
|
|
204
|
+
if (response.status === 401) {
|
|
205
|
+
throw new UnauthorizedError(`${context}: unauthorized`);
|
|
206
|
+
}
|
|
207
|
+
throw new HttpError(response.status, `${context}: ${response.status}`);
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// packages/core/src/infrastructure/adapters/HttpWalletService.ts
|
|
211
|
+
var HttpWalletService = class {
|
|
212
|
+
constructor(baseUrl, token) {
|
|
213
|
+
this.baseUrl = baseUrl;
|
|
214
|
+
this.token = token;
|
|
215
|
+
}
|
|
216
|
+
async getWallet() {
|
|
217
|
+
const response = await fetch(`${this.baseUrl}/api/wallet`, {
|
|
218
|
+
method: "GET",
|
|
219
|
+
headers: {
|
|
220
|
+
"Accept": "application/json",
|
|
221
|
+
"Authorization": `Bearer ${this.token}`
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
assertOk(response, "Failed to fetch wallet");
|
|
225
|
+
const data = await response.json();
|
|
226
|
+
return {
|
|
227
|
+
available: data.available ?? 0,
|
|
228
|
+
pending: data.pending ?? 0,
|
|
229
|
+
totalBalance: (data.available || 0) + (data.pending || 0),
|
|
230
|
+
lifetimeEarned: data.lifetimeEarned ?? 0,
|
|
231
|
+
lifetimeRedeemed: data.lifetimeRedeemed ?? 0,
|
|
232
|
+
updatedAt: data.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
233
|
+
requiredActions: data.requiredActions ?? null
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
};
|
|
184
237
|
|
|
185
238
|
// packages/core/src/infrastructure/adapters/HttpActivityService.ts
|
|
186
239
|
var HttpActivityService = class extends BaseHttpService {
|
|
@@ -188,27 +241,12 @@ var HttpActivityService = class extends BaseHttpService {
|
|
|
188
241
|
super(baseUrl, token);
|
|
189
242
|
}
|
|
190
243
|
async getActivity(pageNumber, pageSize) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
console.warn(`[FrugaSDK] Failed to fetch activity: ${response.status}. Returning empty activity.`);
|
|
195
|
-
return this.getEmptyActivity();
|
|
196
|
-
}
|
|
197
|
-
const data = await response.json();
|
|
198
|
-
return {
|
|
199
|
-
...data,
|
|
200
|
-
transactions: data.transactions || []
|
|
201
|
-
};
|
|
202
|
-
} catch (error) {
|
|
203
|
-
console.warn(`[FrugaSDK] Network error fetching activity. Returning empty activity.`, error);
|
|
204
|
-
return this.getEmptyActivity();
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
getEmptyActivity() {
|
|
244
|
+
const response = await this.fetchQuery("/api/activity", { pageNumber, pageSize });
|
|
245
|
+
assertOk(response, "Failed to fetch activity");
|
|
246
|
+
const data = await response.json();
|
|
208
247
|
return {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
transactions: []
|
|
248
|
+
...data,
|
|
249
|
+
transactions: data.transactions || []
|
|
212
250
|
};
|
|
213
251
|
}
|
|
214
252
|
};
|
|
@@ -219,53 +257,25 @@ var HttpOffersService = class extends BaseHttpService {
|
|
|
219
257
|
super(baseUrl, token);
|
|
220
258
|
}
|
|
221
259
|
async getCategories() {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
return [];
|
|
227
|
-
}
|
|
228
|
-
const data = await response.json();
|
|
229
|
-
const categories = data.categories || [];
|
|
230
|
-
return categories;
|
|
231
|
-
} catch (error) {
|
|
232
|
-
console.warn(`[FrugaSDK] Network error fetching offer categories. Returning empty list.`, error);
|
|
233
|
-
return [];
|
|
234
|
-
}
|
|
260
|
+
const response = await this.fetchQuery("/api/offers/categories");
|
|
261
|
+
assertOk(response, "Failed to fetch offer categories");
|
|
262
|
+
const data = await response.json();
|
|
263
|
+
return data.categories || [];
|
|
235
264
|
}
|
|
236
265
|
async getOffers(query) {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
console.warn(`[FrugaSDK] Failed to fetch offers: ${response.status}. Returning empty response.`);
|
|
248
|
-
return this.getEmptyPaginatedResponse();
|
|
249
|
-
}
|
|
250
|
-
const data = await response.json();
|
|
251
|
-
return {
|
|
252
|
-
...data,
|
|
253
|
-
content: data.content || []
|
|
254
|
-
};
|
|
255
|
-
} catch (error) {
|
|
256
|
-
console.warn(`[FrugaSDK] Network error fetching offers. Returning empty response.`, error);
|
|
257
|
-
return this.getEmptyPaginatedResponse();
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
getEmptyPaginatedResponse() {
|
|
266
|
+
const response = await this.fetchQuery("/api/offers", {
|
|
267
|
+
pageNumber: query.pageNumber ?? 0,
|
|
268
|
+
pageSize: query.pageSize ?? 10,
|
|
269
|
+
type: query.type,
|
|
270
|
+
query: query.query,
|
|
271
|
+
featured: query.featured,
|
|
272
|
+
categories: query.categories
|
|
273
|
+
});
|
|
274
|
+
assertOk(response, "Failed to fetch offers");
|
|
275
|
+
const data = await response.json();
|
|
261
276
|
return {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
totalPages: 0,
|
|
265
|
-
number: 0,
|
|
266
|
-
size: 0,
|
|
267
|
-
numberOfElements: 0,
|
|
268
|
-
numberOfPages: 0
|
|
277
|
+
...data,
|
|
278
|
+
content: data.content || []
|
|
269
279
|
};
|
|
270
280
|
}
|
|
271
281
|
};
|
|
@@ -277,23 +287,16 @@ var HttpPayoutService = class {
|
|
|
277
287
|
this.token = token;
|
|
278
288
|
}
|
|
279
289
|
async listMethods() {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
"Authorization": `Bearer ${this.token}`
|
|
286
|
-
}
|
|
287
|
-
});
|
|
288
|
-
if (!response.ok) {
|
|
289
|
-
throw new Error(`Failed to list payout methods: ${response.status}`);
|
|
290
|
+
const response = await fetch(`${this.baseUrl}/api/payout-methods`, {
|
|
291
|
+
method: "GET",
|
|
292
|
+
headers: {
|
|
293
|
+
"Accept": "application/json",
|
|
294
|
+
"Authorization": `Bearer ${this.token}`
|
|
290
295
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
return [];
|
|
296
|
-
}
|
|
296
|
+
});
|
|
297
|
+
assertOk(response, "Failed to list payout methods");
|
|
298
|
+
const data = await response.json();
|
|
299
|
+
return data.items || [];
|
|
297
300
|
}
|
|
298
301
|
async addBankMethod(payload) {
|
|
299
302
|
const response = await fetch(`${this.baseUrl}/api/payout-methods/bank`, {
|
|
@@ -305,9 +308,7 @@ var HttpPayoutService = class {
|
|
|
305
308
|
},
|
|
306
309
|
body: JSON.stringify(payload)
|
|
307
310
|
});
|
|
308
|
-
|
|
309
|
-
throw new Error(`Failed to add bank payout method: ${response.status}`);
|
|
310
|
-
}
|
|
311
|
+
assertOk(response, "Failed to add bank payout method");
|
|
311
312
|
return response.json();
|
|
312
313
|
}
|
|
313
314
|
async deleteMethod(id) {
|
|
@@ -318,53 +319,124 @@ var HttpPayoutService = class {
|
|
|
318
319
|
"Authorization": `Bearer ${this.token}`
|
|
319
320
|
}
|
|
320
321
|
});
|
|
321
|
-
|
|
322
|
-
|
|
322
|
+
assertOk(response, "Failed to delete payout method");
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
// packages/core/src/infrastructure/adapters/PostMessageTransport.ts
|
|
327
|
+
var WILDCARD_ORIGIN = "*";
|
|
328
|
+
var PostMessageTransport = class {
|
|
329
|
+
constructor(options) {
|
|
330
|
+
__publicField(this, "target");
|
|
331
|
+
__publicField(this, "acceptSource");
|
|
332
|
+
__publicField(this, "listenOn");
|
|
333
|
+
__publicField(this, "handlers", /* @__PURE__ */ new Set());
|
|
334
|
+
__publicField(this, "pinOriginFrom");
|
|
335
|
+
__publicField(this, "targetOrigin");
|
|
336
|
+
__publicField(this, "acceptOrigin");
|
|
337
|
+
__publicField(this, "pinned", null);
|
|
338
|
+
__publicField(this, "disposed", false);
|
|
339
|
+
__publicField(this, "onMessage", (event) => {
|
|
340
|
+
if (this.disposed) return;
|
|
341
|
+
if (this.acceptSource && event.source !== this.acceptSource) return;
|
|
342
|
+
if (this.acceptOrigin && event.origin !== this.acceptOrigin) return;
|
|
343
|
+
if (!isFrugaMessage(event.data)) return;
|
|
344
|
+
if (!this.admitWhileUnpinned(event.data.type, event.origin)) return;
|
|
345
|
+
const message = typeof event.data.version === "string" && event.data.version.length > 0 ? event.data : { ...event.data, version: PROTOCOL_VERSION };
|
|
346
|
+
for (const handler of [...this.handlers]) {
|
|
347
|
+
handler(message);
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
const listenOn = options.listenOn ?? (typeof window !== "undefined" ? window : void 0);
|
|
351
|
+
if (!listenOn) {
|
|
352
|
+
throw new Error("PostMessageTransport: no window to listen on; pass options.listenOn");
|
|
353
|
+
}
|
|
354
|
+
this.target = options.target;
|
|
355
|
+
this.targetOrigin = options.targetOrigin;
|
|
356
|
+
this.acceptSource = options.acceptSource;
|
|
357
|
+
this.acceptOrigin = options.acceptOrigin;
|
|
358
|
+
this.pinOriginFrom = options.pinOriginFrom;
|
|
359
|
+
this.listenOn = listenOn;
|
|
360
|
+
this.listenOn.addEventListener("message", this.onMessage);
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Origin the transport has locked onto, `'*'` when it pinned to the wildcard
|
|
364
|
+
* after an opaque-origin handshake, or `null` while still unpinned.
|
|
365
|
+
*/
|
|
366
|
+
get pinnedOrigin() {
|
|
367
|
+
return this.pinned;
|
|
368
|
+
}
|
|
369
|
+
/** Lock both the outbound and inbound origin once the peer origin is known. */
|
|
370
|
+
pinOrigin(origin) {
|
|
371
|
+
this.targetOrigin = origin;
|
|
372
|
+
this.acceptOrigin = origin;
|
|
373
|
+
this.pinned = origin;
|
|
374
|
+
}
|
|
375
|
+
send(message) {
|
|
376
|
+
if (this.disposed) return;
|
|
377
|
+
const payload = typeof message.version === "string" && message.version.length > 0 ? message : { ...message, version: PROTOCOL_VERSION };
|
|
378
|
+
this.target.postMessage(payload, this.targetOrigin);
|
|
379
|
+
}
|
|
380
|
+
subscribe(handler) {
|
|
381
|
+
if (this.disposed) {
|
|
382
|
+
throw new Error("PostMessageTransport: cannot subscribe after dispose()");
|
|
383
|
+
}
|
|
384
|
+
this.handlers.add(handler);
|
|
385
|
+
return () => {
|
|
386
|
+
this.handlers.delete(handler);
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
dispose() {
|
|
390
|
+
if (this.disposed) return;
|
|
391
|
+
this.disposed = true;
|
|
392
|
+
this.handlers.clear();
|
|
393
|
+
this.listenOn.removeEventListener("message", this.onMessage);
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Gate applied only while `pinOriginFrom` is set and the transport is still
|
|
397
|
+
* unpinned: nothing but the pinning message type gets through, and the first
|
|
398
|
+
* one that does completes the handshake for everything after it.
|
|
399
|
+
*/
|
|
400
|
+
admitWhileUnpinned(type, origin) {
|
|
401
|
+
if (!this.pinOriginFrom || this.pinned !== null) return true;
|
|
402
|
+
if (type !== this.pinOriginFrom) return false;
|
|
403
|
+
if (origin && origin !== "null") {
|
|
404
|
+
this.pinOrigin(origin);
|
|
405
|
+
return true;
|
|
323
406
|
}
|
|
407
|
+
this.pinned = WILDCARD_ORIGIN;
|
|
408
|
+
return true;
|
|
324
409
|
}
|
|
325
410
|
};
|
|
326
411
|
|
|
327
|
-
// packages/core/src/
|
|
328
|
-
var
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
transition: "opacity 0.2s",
|
|
342
|
-
backgroundColor: variant === "primary" ? "#007bff" : "#6c757d",
|
|
343
|
-
color: "white",
|
|
344
|
-
...style
|
|
345
|
-
};
|
|
346
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { style: baseStyle, ...props, children });
|
|
412
|
+
// packages/core/src/infrastructure/adapters/createHostTransport.ts
|
|
413
|
+
var createHostTransport = (options) => {
|
|
414
|
+
const win = options?.listenOn ?? (typeof window !== "undefined" ? window : void 0);
|
|
415
|
+
if (!win) {
|
|
416
|
+
throw new Error("createHostTransport: no window available; pass options.listenOn");
|
|
417
|
+
}
|
|
418
|
+
const framed = win.parent !== win;
|
|
419
|
+
return new PostMessageTransport({
|
|
420
|
+
target: win.parent,
|
|
421
|
+
targetOrigin: "*",
|
|
422
|
+
pinOriginFrom: "FRUGA_INIT",
|
|
423
|
+
listenOn: win,
|
|
424
|
+
...framed ? { acceptSource: win.parent } : {}
|
|
425
|
+
});
|
|
347
426
|
};
|
|
348
|
-
var WidgetHeader = ({ title }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("header", { style: {
|
|
349
|
-
padding: "16px",
|
|
350
|
-
borderBottom: "1px solid #eee",
|
|
351
|
-
display: "flex",
|
|
352
|
-
alignItems: "center",
|
|
353
|
-
justifyContent: "space-between"
|
|
354
|
-
}, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { style: { margin: 0, fontSize: "1.1rem" }, children: title }) });
|
|
355
427
|
|
|
356
428
|
// packages/core/src/presentation/components/WidgetContainer.tsx
|
|
357
|
-
var
|
|
429
|
+
var import_react5 = require("react");
|
|
358
430
|
|
|
359
431
|
// packages/core/src/presentation/components/ui/LauncherIcon.tsx
|
|
360
432
|
var import_lucide_react = require("lucide-react");
|
|
361
|
-
var
|
|
433
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
362
434
|
function LauncherIcon({ theme }) {
|
|
363
435
|
if (theme.launcherIcon === "none") return null;
|
|
364
|
-
if (theme.launcherIcon === "sparkles") return /* @__PURE__ */ (0,
|
|
365
|
-
if (theme.launcherIcon === "credit") return /* @__PURE__ */ (0,
|
|
366
|
-
if (theme.launcherIcon === "arrow") return /* @__PURE__ */ (0,
|
|
367
|
-
return /* @__PURE__ */ (0,
|
|
436
|
+
if (theme.launcherIcon === "sparkles") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Sparkles, { size: 20 });
|
|
437
|
+
if (theme.launcherIcon === "credit") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.CreditCard, { size: 20 });
|
|
438
|
+
if (theme.launcherIcon === "arrow") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.MoveUpRight, { size: 20 });
|
|
439
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Gift, { size: 20 });
|
|
368
440
|
}
|
|
369
441
|
|
|
370
442
|
// packages/core/src/presentation/hooks/useActivityDetector.ts
|
|
@@ -424,6 +496,8 @@ var useWidgetStore = create((set) => ({
|
|
|
424
496
|
pending: 0,
|
|
425
497
|
mode: "light",
|
|
426
498
|
token: null,
|
|
499
|
+
tokenEpoch: 0,
|
|
500
|
+
authStatus: "pending",
|
|
427
501
|
partnerPayout: null,
|
|
428
502
|
isAddingPayoutFromAlert: false,
|
|
429
503
|
showPayoutSuccess: false,
|
|
@@ -433,8 +507,12 @@ var useWidgetStore = create((set) => ({
|
|
|
433
507
|
setIdle: (idle) => set({ isIdle: idle }),
|
|
434
508
|
setBalances: (available, pending) => set({ available, pending }),
|
|
435
509
|
setMode: (mode) => set({ mode }),
|
|
436
|
-
setToken: (token) => set(
|
|
437
|
-
|
|
510
|
+
setToken: (token) => set((state) => ({
|
|
511
|
+
token,
|
|
512
|
+
tokenEpoch: state.token === token ? state.tokenEpoch : state.tokenEpoch + 1,
|
|
513
|
+
authStatus: token ? "authenticated" : "pending"
|
|
514
|
+
})),
|
|
515
|
+
setPartnerPayout: (payout) => set({ partnerPayout: resolvePayoutMode(payout) }),
|
|
438
516
|
setIsAddingPayoutFromAlert: (val) => set({ isAddingPayoutFromAlert: val }),
|
|
439
517
|
setShowPayoutSuccess: (val) => set({ showPayoutSuccess: val })
|
|
440
518
|
}));
|
|
@@ -482,6 +560,42 @@ function useActivityDetector() {
|
|
|
482
560
|
}, [setFocused, setIdle]);
|
|
483
561
|
}
|
|
484
562
|
|
|
563
|
+
// packages/core/src/presentation/hooks/useMessageTransport.ts
|
|
564
|
+
var import_react4 = require("react");
|
|
565
|
+
|
|
566
|
+
// packages/core/src/presentation/context/MessageTransportContext.tsx
|
|
567
|
+
var import_react3 = require("react");
|
|
568
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
569
|
+
var MessageTransportContext = (0, import_react3.createContext)(null);
|
|
570
|
+
var MessageTransportProvider = ({
|
|
571
|
+
transport,
|
|
572
|
+
children
|
|
573
|
+
}) => {
|
|
574
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MessageTransportContext.Provider, { value: transport, children });
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
// packages/core/src/presentation/hooks/useMessageTransport.ts
|
|
578
|
+
var useMessageTransport = () => {
|
|
579
|
+
const transport = (0, import_react4.useContext)(MessageTransportContext);
|
|
580
|
+
if (!transport) {
|
|
581
|
+
throw new Error(
|
|
582
|
+
"useMessageTransport: no transport in context. Wrap the widget tree in <MessageTransportProvider transport={...}>."
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
return transport;
|
|
586
|
+
};
|
|
587
|
+
var useFrugaMessage = (type, handler) => {
|
|
588
|
+
const transport = useMessageTransport();
|
|
589
|
+
const handlerRef = (0, import_react4.useRef)(handler);
|
|
590
|
+
handlerRef.current = handler;
|
|
591
|
+
(0, import_react4.useEffect)(() => {
|
|
592
|
+
return transport.subscribe((message) => {
|
|
593
|
+
if (message.type !== type) return;
|
|
594
|
+
handlerRef.current(message);
|
|
595
|
+
});
|
|
596
|
+
}, [transport, type]);
|
|
597
|
+
};
|
|
598
|
+
|
|
485
599
|
// packages/core/src/presentation/components/WidgetContainer.tsx
|
|
486
600
|
var import_lucide_react2 = require("lucide-react");
|
|
487
601
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
@@ -494,37 +608,47 @@ var WidgetContainer = ({
|
|
|
494
608
|
useActivityDetector();
|
|
495
609
|
const { isOpen, toggle, mode: storeMode } = useWidgetStore();
|
|
496
610
|
const theme = themeProp || DEFAULT_THEME;
|
|
497
|
-
const [hostHeight, setHostHeight] = (0,
|
|
498
|
-
const [isMobile, setIsMobile] = (0,
|
|
499
|
-
const
|
|
611
|
+
const [hostHeight, setHostHeight] = (0, import_react5.useState)(initialHostHeight);
|
|
612
|
+
const [isMobile, setIsMobile] = (0, import_react5.useState)(initialIsMobile);
|
|
613
|
+
const [hostThemeMode, setHostThemeMode] = (0, import_react5.useState)(void 0);
|
|
614
|
+
const effectiveTheme = theme.allowUserThemeControl ? { ...theme, mode: storeMode } : { ...theme, mode: hostThemeMode ?? theme.mode ?? "light" };
|
|
500
615
|
const resolved = getResolvedTheme(effectiveTheme);
|
|
501
|
-
const containerRef = (0,
|
|
502
|
-
const buttonRef = (0,
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
616
|
+
const containerRef = (0, import_react5.useRef)(null);
|
|
617
|
+
const buttonRef = (0, import_react5.useRef)(null);
|
|
618
|
+
const transport = useMessageTransport();
|
|
619
|
+
useFrugaMessage("FRUGA_HOST_RESIZE", (message) => {
|
|
620
|
+
setHostHeight(message.hostHeight);
|
|
621
|
+
setIsMobile(message.isHostMobile);
|
|
622
|
+
});
|
|
623
|
+
useFrugaMessage("FRUGA_TOKEN_UPDATE", (message) => {
|
|
624
|
+
useWidgetStore.getState().setToken(message.token);
|
|
625
|
+
});
|
|
626
|
+
useFrugaMessage("FRUGA_GET_BALANCE", (message) => {
|
|
627
|
+
const { available, pending } = useWidgetStore.getState();
|
|
628
|
+
transport.send({
|
|
629
|
+
type: "FRUGA_BALANCE_RESPONSE",
|
|
630
|
+
version: PROTOCOL_VERSION,
|
|
631
|
+
requestId: message.requestId,
|
|
632
|
+
available,
|
|
633
|
+
pending
|
|
634
|
+
});
|
|
635
|
+
});
|
|
636
|
+
useFrugaMessage("FRUGA_INIT", (message) => {
|
|
637
|
+
setHostHeight(message.host.height);
|
|
638
|
+
setIsMobile(message.host.isMobile);
|
|
639
|
+
if (message.token) {
|
|
640
|
+
useWidgetStore.getState().setToken(message.token);
|
|
641
|
+
}
|
|
642
|
+
if (message.overrides?.panelOpen) {
|
|
643
|
+
useWidgetStore.getState().setOpen(true);
|
|
644
|
+
}
|
|
645
|
+
const overrideThemeMode = message.overrides?.theme;
|
|
646
|
+
if (overrideThemeMode) {
|
|
647
|
+
setHostThemeMode(overrideThemeMode);
|
|
648
|
+
useWidgetStore.getState().setMode(overrideThemeMode);
|
|
649
|
+
}
|
|
650
|
+
});
|
|
651
|
+
(0, import_react5.useEffect)(() => {
|
|
528
652
|
if (!containerRef.current) return;
|
|
529
653
|
const sendResize = () => {
|
|
530
654
|
if (!containerRef.current) return;
|
|
@@ -537,18 +661,19 @@ var WidgetContainer = ({
|
|
|
537
661
|
expectedWidth = window.innerWidth;
|
|
538
662
|
expectedHeight = hostHeight;
|
|
539
663
|
}
|
|
540
|
-
|
|
664
|
+
transport.send({
|
|
541
665
|
type: "FRUGA_RESIZE",
|
|
666
|
+
version: PROTOCOL_VERSION,
|
|
542
667
|
height: expectedHeight,
|
|
543
668
|
width: expectedWidth,
|
|
544
669
|
isFullscreen: isMobile && isOpen
|
|
545
|
-
}
|
|
670
|
+
});
|
|
546
671
|
};
|
|
547
672
|
const observer = new ResizeObserver(() => sendResize());
|
|
548
673
|
observer.observe(containerRef.current);
|
|
549
674
|
sendResize();
|
|
550
675
|
return () => observer.disconnect();
|
|
551
|
-
}, [isOpen, hostHeight, isMobile]);
|
|
676
|
+
}, [isOpen, hostHeight, isMobile, transport]);
|
|
552
677
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
553
678
|
"div",
|
|
554
679
|
{
|
|
@@ -647,10 +772,10 @@ var useWidget = () => {
|
|
|
647
772
|
};
|
|
648
773
|
|
|
649
774
|
// packages/core/src/presentation/hooks/useDebounce.ts
|
|
650
|
-
var
|
|
775
|
+
var import_react6 = require("react");
|
|
651
776
|
var useDebounce = (value, delay) => {
|
|
652
|
-
const [debouncedValue, setDebouncedValue] = (0,
|
|
653
|
-
(0,
|
|
777
|
+
const [debouncedValue, setDebouncedValue] = (0, import_react6.useState)(value);
|
|
778
|
+
(0, import_react6.useEffect)(() => {
|
|
654
779
|
const handleTimeout = setTimeout(() => {
|
|
655
780
|
setDebouncedValue(value);
|
|
656
781
|
}, delay);
|
|
@@ -662,10 +787,8 @@ var useDebounce = (value, delay) => {
|
|
|
662
787
|
};
|
|
663
788
|
|
|
664
789
|
// packages/core/src/infrastructure/storage/StorageService.ts
|
|
665
|
-
var
|
|
666
|
-
|
|
667
|
-
BOOTSTRAP_CONFIG: "fruga_active_config"
|
|
668
|
-
};
|
|
790
|
+
var LEGACY_BOOTSTRAP_STORAGE_KEY = "fruga_bootstrap";
|
|
791
|
+
var storageKey = (partnerKey, name) => `fruga:${partnerKey}:${name}`;
|
|
669
792
|
var StorageService = class {
|
|
670
793
|
static set(key, value) {
|
|
671
794
|
if (typeof window === "undefined") return;
|
|
@@ -687,20 +810,22 @@ var StorageService = class {
|
|
|
687
810
|
}
|
|
688
811
|
static remove(key) {
|
|
689
812
|
if (typeof window === "undefined") return;
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
813
|
+
try {
|
|
814
|
+
window.localStorage.removeItem(key);
|
|
815
|
+
} catch (error) {
|
|
816
|
+
console.warn(`[FrugaStorage] Failed to remove key "${key}":`, error);
|
|
817
|
+
}
|
|
695
818
|
}
|
|
696
819
|
};
|
|
697
820
|
|
|
698
821
|
// packages/core/src/infrastructure/config.ts
|
|
699
|
-
var DEFAULT_API_URL =
|
|
822
|
+
var DEFAULT_API_URL = "https://api.fruga.co.uk";
|
|
700
823
|
var getBaseUrl = (override) => {
|
|
701
824
|
if (override) return override;
|
|
702
|
-
return DEFAULT_API_URL;
|
|
825
|
+
return "" ? "" : DEFAULT_API_URL;
|
|
703
826
|
};
|
|
827
|
+
var isTestMode = () => false;
|
|
828
|
+
var SDK_VERSION = "1.3.0" ? "1.3.0" : "0.0.0-dev";
|
|
704
829
|
|
|
705
830
|
// packages/core/src/infrastructure/helpers/date.ts
|
|
706
831
|
var formatDate = (dateStr) => {
|
|
@@ -713,7 +838,7 @@ var formatDate = (dateStr) => {
|
|
|
713
838
|
const month = (date.getMonth() + 1).toString().padStart(2, "0");
|
|
714
839
|
const year = date.getFullYear();
|
|
715
840
|
return `${day}-${month}-${year}`;
|
|
716
|
-
} catch
|
|
841
|
+
} catch {
|
|
717
842
|
return dateStr;
|
|
718
843
|
}
|
|
719
844
|
};
|
|
@@ -760,23 +885,38 @@ var truncateText = (str, maxLength = 40) => {
|
|
|
760
885
|
DEFAULT_API_URL,
|
|
761
886
|
DEFAULT_THEME,
|
|
762
887
|
DefaultTheme,
|
|
888
|
+
HOST_MOBILE_BREAKPOINT,
|
|
763
889
|
HttpActivityService,
|
|
890
|
+
HttpError,
|
|
764
891
|
HttpOffersService,
|
|
765
892
|
HttpPayoutService,
|
|
766
893
|
HttpWalletService,
|
|
767
|
-
|
|
894
|
+
LEGACY_BOOTSTRAP_STORAGE_KEY,
|
|
895
|
+
MessageTransportContext,
|
|
896
|
+
MessageTransportProvider,
|
|
897
|
+
PROTOCOL_VERSION,
|
|
898
|
+
PostMessageTransport,
|
|
899
|
+
SDK_VERSION,
|
|
768
900
|
StorageService,
|
|
769
|
-
|
|
901
|
+
UnauthorizedError,
|
|
770
902
|
WidgetContainer,
|
|
771
|
-
WidgetHeader,
|
|
772
903
|
WidgetProvider,
|
|
904
|
+
createHostTransport,
|
|
773
905
|
formatDate,
|
|
774
906
|
getBaseUrl,
|
|
775
907
|
getDaysRemaining,
|
|
776
908
|
getResolvedTheme,
|
|
909
|
+
isFrugaMessage,
|
|
910
|
+
isFrugaMessageType,
|
|
911
|
+
isTestMode,
|
|
777
912
|
isWithinDays,
|
|
913
|
+
protocolMajor,
|
|
914
|
+
resolvePayoutMode,
|
|
915
|
+
storageKey,
|
|
778
916
|
truncateText,
|
|
779
917
|
useDebounce,
|
|
918
|
+
useFrugaMessage,
|
|
919
|
+
useMessageTransport,
|
|
780
920
|
useWidget,
|
|
781
921
|
useWidgetStore
|
|
782
922
|
});
|