@fuul/sdk 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 +99 -0
- package/lib/cjs/constants.js +8 -0
- package/lib/cjs/index.js +185 -0
- package/lib/cjs/types/constants.d.ts +6 -0
- package/lib/cjs/types/constants.d.ts.map +1 -0
- package/lib/cjs/types/index.d.ts +37 -0
- package/lib/cjs/types/index.d.ts.map +1 -0
- package/lib/cjs/types/types/types.d.ts +20 -0
- package/lib/cjs/types/types/types.d.ts.map +1 -0
- package/lib/cjs/types/types.js +2 -0
- package/lib/cjs/types/utils/date.d.ts +2 -0
- package/lib/cjs/types/utils/date.d.ts.map +1 -0
- package/lib/cjs/types/utils/localStorage.d.ts +5 -0
- package/lib/cjs/types/utils/localStorage.d.ts.map +1 -0
- package/lib/cjs/utils/date.js +7 -0
- package/lib/cjs/utils/localStorage.js +12 -0
- package/lib/esm/constants.js +5 -0
- package/lib/esm/index.mjs +155 -0
- package/lib/esm/types/constants.d.ts +6 -0
- package/lib/esm/types/constants.d.ts.map +1 -0
- package/lib/esm/types/index.d.ts +37 -0
- package/lib/esm/types/index.d.ts.map +1 -0
- package/lib/esm/types/types/types.d.ts +20 -0
- package/lib/esm/types/types/types.d.ts.map +1 -0
- package/lib/esm/types/types.js +1 -0
- package/lib/esm/types/utils/date.d.ts +2 -0
- package/lib/esm/types/utils/date.d.ts.map +1 -0
- package/lib/esm/types/utils/localStorage.d.ts +5 -0
- package/lib/esm/types/utils/localStorage.d.ts.map +1 -0
- package/lib/esm/utils/date.js +3 -0
- package/lib/esm/utils/localStorage.js +5 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Getting started with Fuul SDK
|
|
2
|
+
|
|
3
|
+
**Setting up the Fuul SDK**
|
|
4
|
+
|
|
5
|
+
## **1. Install the Fuul SDK**
|
|
6
|
+
|
|
7
|
+
Run one of the following commands to add Fuul SDK to your project:
|
|
8
|
+
|
|
9
|
+
### npm
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install fuul-sdk
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### yarn
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
yarn add fuul-sdk
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 2**. Set up the Fuul SDK**
|
|
22
|
+
|
|
23
|
+
In order to authenticate to full with your project, you must execute the following in the root file of your app.
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
// App.tsx
|
|
27
|
+
|
|
28
|
+
// Settings config object
|
|
29
|
+
const settings = {
|
|
30
|
+
projectId: "myprojectid", // Replace with your Fuul Project Id.
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const fuul = new Fuul(settings);
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
By this way, you’ll be available to use Fuul as a global object in any of your files, so you don’t have to create a new instance every time.
|
|
37
|
+
|
|
38
|
+
- Please note that `projectId` is not required when you initialize Fuul sdk, but it is mandatory in order to send a `“connect_wallet”` event. But don’t worry, you can send it later as an argument using the `Fuul.sendEvent()` method if needed (please refer to section 3 of this guide)
|
|
39
|
+
|
|
40
|
+
## 3**. Verifying the connection to Fuul SDK**
|
|
41
|
+
|
|
42
|
+
You can verify that everything was set up successfully with the following method
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
Fuul.verifyConnection()
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
If everything is ok, you will see a prompt as below:
|
|
49
|
+
|
|
50
|
+

|
|
51
|
+
|
|
52
|
+
## 4**. Sending events**
|
|
53
|
+
|
|
54
|
+
In order to send events such as `pageview` or `connect_wallet` you must do the following
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
// Sending a pageview event does not need any parameters, just the event name
|
|
58
|
+
const handlePageView = async () => {
|
|
59
|
+
await Fuul.sendEvent("pageview");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// You can also send additional event arguments as follows:
|
|
63
|
+
const handlePageView = async () => {
|
|
64
|
+
await Fuul.sendEvent("pageview", {
|
|
65
|
+
foo,
|
|
66
|
+
bar,
|
|
67
|
+
...myWonderfulArguments
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// For connect_wallet events, you must send the address that is being connected as an argument
|
|
72
|
+
const handlePageView = async () => {
|
|
73
|
+
await Fuul.sendEvent("connect_wallet", {
|
|
74
|
+
address: "0x3Ec0590BC79c74B0145f94C0bE1C5d63E491569f"
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Sending projectId as an event argument (please note that you must send it as project_id)
|
|
79
|
+
const handlePageView = async () => {
|
|
80
|
+
await Fuul.sendEvent("pageview", {
|
|
81
|
+
project_id: "my-wonderful-project-id"
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## 5**. Generating tracking links**
|
|
87
|
+
|
|
88
|
+
You can also generate the tracking link for a given wallet `address` and `campaign id`
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
// Let's assume you are testing in localhost:3000
|
|
92
|
+
|
|
93
|
+
const myWonderfulReferrerAddress: string = "0xE8BF39dCd16CF20d39006ba3C722A02e701bf0eE"
|
|
94
|
+
const campaignId: string = "79e72760-c730-4422-9e7b-3b730e8800dc"
|
|
95
|
+
|
|
96
|
+
const myTrackingId: string = Fuul.generateTrackingLink(myWonderfulReferrerAddress, campaignId);
|
|
97
|
+
|
|
98
|
+
console.log(myTrackingId) // http://localhost:3000?c=79e72760-c730-4422-9e7b-3b730e8800dc&origin=fuul&r=0xE8BF39dCd16CF20d39006ba3C722A02e701bf0eE
|
|
99
|
+
```
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SENT_EVENT_ID_KEY = exports.REFERRER_ID_KEY = exports.CAMPAIGN_ID_KEY = exports.TRACKING_ID_KEY = exports.SESSION_ID_KEY = void 0;
|
|
4
|
+
exports.SESSION_ID_KEY = "fuul.session_id";
|
|
5
|
+
exports.TRACKING_ID_KEY = "fuul.tracking_id";
|
|
6
|
+
exports.CAMPAIGN_ID_KEY = "fuul.campaign_id";
|
|
7
|
+
exports.REFERRER_ID_KEY = "fuul.referrer_id";
|
|
8
|
+
exports.SENT_EVENT_ID_KEY = "fuul.sent";
|
package/lib/cjs/index.js
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
35
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
|
+
};
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.Fuul = void 0;
|
|
39
|
+
const axios_1 = __importDefault(require("axios"));
|
|
40
|
+
const date_1 = require("./utils/date");
|
|
41
|
+
const localStorage_1 = require("./utils/localStorage");
|
|
42
|
+
const constants_1 = require("./constants");
|
|
43
|
+
const saveSentEvent = (eventName, params) => {
|
|
44
|
+
const timestamp = Date.now();
|
|
45
|
+
const SENT_EVENT_KEY = `${constants_1.SENT_EVENT_ID_KEY}_${eventName}`;
|
|
46
|
+
const eventParams = Object.assign(Object.assign({}, params), { timestamp });
|
|
47
|
+
localStorage.setItem(SENT_EVENT_KEY, JSON.stringify(eventParams));
|
|
48
|
+
};
|
|
49
|
+
const isEventAlreadySentAndInValidTimestamp = (eventName, params) => {
|
|
50
|
+
const SENT_EVENT_KEY = `${constants_1.SENT_EVENT_ID_KEY}_${eventName}`;
|
|
51
|
+
const storedEvent = localStorage.getItem(SENT_EVENT_KEY);
|
|
52
|
+
if (!storedEvent)
|
|
53
|
+
return false;
|
|
54
|
+
const parsedEvent = JSON.parse(storedEvent);
|
|
55
|
+
const isSameDay = (0, date_1.datesAreOnSameDay)(new Date(Date.now()), new Date(parsedEvent.timestamp));
|
|
56
|
+
if (eventName === "connect_wallet") {
|
|
57
|
+
return parsedEvent["tracking_id"] === params.tracking_id && isSameDay;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
return (parsedEvent["tracking_id"] === params.tracking_id &&
|
|
61
|
+
parsedEvent["campaign_id"] === params.campaign_id &&
|
|
62
|
+
parsedEvent["referrer_id"] === params.referrer_id &&
|
|
63
|
+
isSameDay);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const generateRandomId = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
67
|
+
const nanoid = yield Promise.resolve().then(() => __importStar(require("nanoid"))).then((m) => m.nanoid);
|
|
68
|
+
return nanoid();
|
|
69
|
+
});
|
|
70
|
+
const saveSessionId = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
71
|
+
if (typeof window === "undefined")
|
|
72
|
+
return;
|
|
73
|
+
localStorage.setItem(constants_1.SESSION_ID_KEY, yield generateRandomId());
|
|
74
|
+
});
|
|
75
|
+
const saveTrackingId = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
76
|
+
var _a, _b;
|
|
77
|
+
if (typeof window === "undefined" || typeof document === "undefined")
|
|
78
|
+
return;
|
|
79
|
+
const queryParams = new URLSearchParams(window.location.search);
|
|
80
|
+
if (!queryParams.has("c") ||
|
|
81
|
+
!queryParams.has("origin") ||
|
|
82
|
+
!queryParams.has("r"))
|
|
83
|
+
return;
|
|
84
|
+
const isFuulOrigin = queryParams.get("origin") === "fuul";
|
|
85
|
+
if (!isFuulOrigin)
|
|
86
|
+
return;
|
|
87
|
+
if (!(0, localStorage_1.getTrackingId)()) {
|
|
88
|
+
localStorage.setItem(constants_1.TRACKING_ID_KEY, yield generateRandomId());
|
|
89
|
+
}
|
|
90
|
+
localStorage.setItem(constants_1.CAMPAIGN_ID_KEY, (_a = queryParams.get("c")) !== null && _a !== void 0 ? _a : "");
|
|
91
|
+
localStorage.setItem(constants_1.REFERRER_ID_KEY, (_b = queryParams.get("r")) !== null && _b !== void 0 ? _b : "");
|
|
92
|
+
});
|
|
93
|
+
const buildTrackingLinkQueryParams = (r, c) => {
|
|
94
|
+
return `c=${c}&origin=fuul&r=${r}`;
|
|
95
|
+
};
|
|
96
|
+
class Fuul {
|
|
97
|
+
constructor(projectId) {
|
|
98
|
+
this.BASE_API_URL = "https://api.fuul.xyz/api/v1";
|
|
99
|
+
this.projectId = projectId;
|
|
100
|
+
saveSessionId();
|
|
101
|
+
saveTrackingId();
|
|
102
|
+
globalThis.Fuul = this;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* @param {EventType} name Event name.
|
|
106
|
+
* @param {EventArgsType} args Event additional arguments.
|
|
107
|
+
* ```js
|
|
108
|
+
* import { Fuul } from 'fuul-sdk'
|
|
109
|
+
*
|
|
110
|
+
* // Initialize Fuul in your app root file
|
|
111
|
+
* new Fuul('your-project-id')
|
|
112
|
+
*
|
|
113
|
+
* // Then you can send an event as follows:
|
|
114
|
+
* fuul.sendEvent('connect_wallet', {
|
|
115
|
+
* address,
|
|
116
|
+
* ...args
|
|
117
|
+
* })
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
sendEvent(name, args) {
|
|
121
|
+
var _a;
|
|
122
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
123
|
+
const session_id = (0, localStorage_1.getSessionId)();
|
|
124
|
+
const tracking_id = (0, localStorage_1.getTrackingId)();
|
|
125
|
+
const campaign_id = (0, localStorage_1.getCampaignId)();
|
|
126
|
+
const referrer_id = (0, localStorage_1.getReferrerId)();
|
|
127
|
+
if (!tracking_id)
|
|
128
|
+
return;
|
|
129
|
+
let params = {
|
|
130
|
+
tracking_id,
|
|
131
|
+
};
|
|
132
|
+
let reqBody = {};
|
|
133
|
+
if (name === "connect_wallet") {
|
|
134
|
+
reqBody = {
|
|
135
|
+
name,
|
|
136
|
+
session_id,
|
|
137
|
+
event_args: Object.assign(Object.assign({}, args), { tracking_id }),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
if (!campaign_id || !referrer_id)
|
|
142
|
+
return;
|
|
143
|
+
params = Object.assign(Object.assign({}, params), { referrer_id,
|
|
144
|
+
campaign_id });
|
|
145
|
+
reqBody = {
|
|
146
|
+
name,
|
|
147
|
+
session_id,
|
|
148
|
+
project_id: (_a = this.projectId) !== null && _a !== void 0 ? _a : args === null || args === void 0 ? void 0 : args.project_id,
|
|
149
|
+
event_args: Object.assign(Object.assign({}, args), { referrer: referrer_id, campaign_id,
|
|
150
|
+
tracking_id }),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (isEventAlreadySentAndInValidTimestamp(name, params))
|
|
154
|
+
return;
|
|
155
|
+
const url = `${this.BASE_API_URL}/events`;
|
|
156
|
+
try {
|
|
157
|
+
const response = yield axios_1.default.post(url, reqBody);
|
|
158
|
+
saveSentEvent(name, params);
|
|
159
|
+
return response.data;
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
return error;
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
verifyConnection() {
|
|
167
|
+
if (window !== undefined && globalThis.Fuul) {
|
|
168
|
+
window.alert("You are successfully connected to Fuul SDK! ✅");
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Generates a tracking link for a referrer
|
|
173
|
+
* @param {string} address referrer address
|
|
174
|
+
* @param {string} cid campaign id you want to refer the user
|
|
175
|
+
* @param {string} baseUrl base url of your app
|
|
176
|
+
* @returns {string} tracking link
|
|
177
|
+
*/
|
|
178
|
+
generateTrackingLink({ address, cid, baseUrl, }) {
|
|
179
|
+
return `${baseUrl !== null && baseUrl !== void 0 ? baseUrl : window.location.href}?${buildTrackingLinkQueryParams(address, cid)}`;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
exports.Fuul = Fuul;
|
|
183
|
+
exports.default = {
|
|
184
|
+
Fuul,
|
|
185
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const SESSION_ID_KEY = "fuul.session_id";
|
|
2
|
+
export declare const TRACKING_ID_KEY = "fuul.tracking_id";
|
|
3
|
+
export declare const CAMPAIGN_ID_KEY = "fuul.campaign_id";
|
|
4
|
+
export declare const REFERRER_ID_KEY = "fuul.referrer_id";
|
|
5
|
+
export declare const SENT_EVENT_ID_KEY = "fuul.sent";
|
|
6
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,oBAAoB,CAAC;AAChD,eAAO,MAAM,eAAe,qBAAqB,CAAC;AAClD,eAAO,MAAM,eAAe,qBAAqB,CAAC;AAClD,eAAO,MAAM,eAAe,qBAAqB,CAAC;AAClD,eAAO,MAAM,iBAAiB,cAAc,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { EventArgsType, EventType, IGenerateTrackingLink } from "./types/types";
|
|
2
|
+
export declare class Fuul {
|
|
3
|
+
private projectId?;
|
|
4
|
+
private BASE_API_URL;
|
|
5
|
+
constructor(projectId?: string);
|
|
6
|
+
/**
|
|
7
|
+
* @param {EventType} name Event name.
|
|
8
|
+
* @param {EventArgsType} args Event additional arguments.
|
|
9
|
+
* ```js
|
|
10
|
+
* import { Fuul } from 'fuul-sdk'
|
|
11
|
+
*
|
|
12
|
+
* // Initialize Fuul in your app root file
|
|
13
|
+
* new Fuul('your-project-id')
|
|
14
|
+
*
|
|
15
|
+
* // Then you can send an event as follows:
|
|
16
|
+
* fuul.sendEvent('connect_wallet', {
|
|
17
|
+
* address,
|
|
18
|
+
* ...args
|
|
19
|
+
* })
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
sendEvent(name: EventType, args?: EventArgsType): Promise<any>;
|
|
23
|
+
verifyConnection(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Generates a tracking link for a referrer
|
|
26
|
+
* @param {string} address referrer address
|
|
27
|
+
* @param {string} cid campaign id you want to refer the user
|
|
28
|
+
* @param {string} baseUrl base url of your app
|
|
29
|
+
* @returns {string} tracking link
|
|
30
|
+
*/
|
|
31
|
+
generateTrackingLink({ address, cid, baseUrl, }: IGenerateTrackingLink): string;
|
|
32
|
+
}
|
|
33
|
+
declare const _default: {
|
|
34
|
+
Fuul: typeof Fuul;
|
|
35
|
+
};
|
|
36
|
+
export default _default;
|
|
37
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAkBA,OAAO,EACL,aAAa,EACb,SAAS,EACT,qBAAqB,EAEtB,MAAM,eAAe,CAAC;AA+EvB,qBAAa,IAAI;IACf,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,YAAY,CAAyC;gBAEjD,SAAS,CAAC,EAAE,MAAM;IAS9B;;;;;;;;;;;;;;;OAeG;IACG,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC;IA4DpE,gBAAgB,IAAI,IAAI;IAMxB;;;;;;OAMG;IACH,oBAAoB,CAAC,EACnB,OAAO,EACP,GAAG,EACH,OAAO,GACR,EAAE,qBAAqB,GAAG,MAAM;CAMlC;;;;AAMD,wBAEE"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface SendEventParams {
|
|
2
|
+
name: EventType;
|
|
3
|
+
args?: EventArgsType;
|
|
4
|
+
projectId?: string;
|
|
5
|
+
}
|
|
6
|
+
export type EventArgsType = {
|
|
7
|
+
[key: string]: string;
|
|
8
|
+
};
|
|
9
|
+
export type EventType = "connect_wallet" | "pageview";
|
|
10
|
+
export interface SentEventParams {
|
|
11
|
+
tracking_id: string;
|
|
12
|
+
campaign_id?: string;
|
|
13
|
+
referrer_id?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface IGenerateTrackingLink {
|
|
16
|
+
address: string;
|
|
17
|
+
cid: string;
|
|
18
|
+
baseUrl?: string;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/types/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,gBAAgB,GAAG,UAAU,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../../../../src/utils/date.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,UAAW,IAAI,UAAU,IAAI,YAGrB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const getSessionId: () => string | null;
|
|
2
|
+
export declare const getTrackingId: () => string | null;
|
|
3
|
+
export declare const getCampaignId: () => string | null;
|
|
4
|
+
export declare const getReferrerId: () => string | null;
|
|
5
|
+
//# sourceMappingURL=localStorage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localStorage.d.ts","sourceRoot":"","sources":["../../../../src/utils/localStorage.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,YAAY,qBAA6C,CAAC;AACvE,eAAO,MAAM,aAAa,qBAA8C,CAAC;AACzE,eAAO,MAAM,aAAa,qBAA8C,CAAC;AACzE,eAAO,MAAM,aAAa,qBAA8C,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.datesAreOnSameDay = void 0;
|
|
4
|
+
const datesAreOnSameDay = (first, second) => first.getFullYear() === second.getFullYear() &&
|
|
5
|
+
first.getMonth() === second.getMonth() &&
|
|
6
|
+
first.getDate() === second.getDate();
|
|
7
|
+
exports.datesAreOnSameDay = datesAreOnSameDay;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getReferrerId = exports.getCampaignId = exports.getTrackingId = exports.getSessionId = void 0;
|
|
4
|
+
const constants_1 = require("../constants");
|
|
5
|
+
const getSessionId = () => localStorage.getItem(constants_1.SESSION_ID_KEY);
|
|
6
|
+
exports.getSessionId = getSessionId;
|
|
7
|
+
const getTrackingId = () => localStorage.getItem(constants_1.TRACKING_ID_KEY);
|
|
8
|
+
exports.getTrackingId = getTrackingId;
|
|
9
|
+
const getCampaignId = () => localStorage.getItem(constants_1.CAMPAIGN_ID_KEY);
|
|
10
|
+
exports.getCampaignId = getCampaignId;
|
|
11
|
+
const getReferrerId = () => localStorage.getItem(constants_1.REFERRER_ID_KEY);
|
|
12
|
+
exports.getReferrerId = getReferrerId;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import axios from "axios";
|
|
11
|
+
import { datesAreOnSameDay } from "./utils/date";
|
|
12
|
+
import { getCampaignId, getReferrerId, getSessionId, getTrackingId, } from "./utils/localStorage";
|
|
13
|
+
import { CAMPAIGN_ID_KEY, REFERRER_ID_KEY, SENT_EVENT_ID_KEY, SESSION_ID_KEY, TRACKING_ID_KEY, } from "./constants";
|
|
14
|
+
const saveSentEvent = (eventName, params) => {
|
|
15
|
+
const timestamp = Date.now();
|
|
16
|
+
const SENT_EVENT_KEY = `${SENT_EVENT_ID_KEY}_${eventName}`;
|
|
17
|
+
const eventParams = Object.assign(Object.assign({}, params), { timestamp });
|
|
18
|
+
localStorage.setItem(SENT_EVENT_KEY, JSON.stringify(eventParams));
|
|
19
|
+
};
|
|
20
|
+
const isEventAlreadySentAndInValidTimestamp = (eventName, params) => {
|
|
21
|
+
const SENT_EVENT_KEY = `${SENT_EVENT_ID_KEY}_${eventName}`;
|
|
22
|
+
const storedEvent = localStorage.getItem(SENT_EVENT_KEY);
|
|
23
|
+
if (!storedEvent)
|
|
24
|
+
return false;
|
|
25
|
+
const parsedEvent = JSON.parse(storedEvent);
|
|
26
|
+
const isSameDay = datesAreOnSameDay(new Date(Date.now()), new Date(parsedEvent.timestamp));
|
|
27
|
+
if (eventName === "connect_wallet") {
|
|
28
|
+
return parsedEvent["tracking_id"] === params.tracking_id && isSameDay;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
return (parsedEvent["tracking_id"] === params.tracking_id &&
|
|
32
|
+
parsedEvent["campaign_id"] === params.campaign_id &&
|
|
33
|
+
parsedEvent["referrer_id"] === params.referrer_id &&
|
|
34
|
+
isSameDay);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const generateRandomId = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
38
|
+
const nanoid = yield import("nanoid").then((m) => m.nanoid);
|
|
39
|
+
return nanoid();
|
|
40
|
+
});
|
|
41
|
+
const saveSessionId = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
42
|
+
if (typeof window === "undefined")
|
|
43
|
+
return;
|
|
44
|
+
localStorage.setItem(SESSION_ID_KEY, yield generateRandomId());
|
|
45
|
+
});
|
|
46
|
+
const saveTrackingId = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
47
|
+
var _a, _b;
|
|
48
|
+
if (typeof window === "undefined" || typeof document === "undefined")
|
|
49
|
+
return;
|
|
50
|
+
const queryParams = new URLSearchParams(window.location.search);
|
|
51
|
+
if (!queryParams.has("c") ||
|
|
52
|
+
!queryParams.has("origin") ||
|
|
53
|
+
!queryParams.has("r"))
|
|
54
|
+
return;
|
|
55
|
+
const isFuulOrigin = queryParams.get("origin") === "fuul";
|
|
56
|
+
if (!isFuulOrigin)
|
|
57
|
+
return;
|
|
58
|
+
if (!getTrackingId()) {
|
|
59
|
+
localStorage.setItem(TRACKING_ID_KEY, yield generateRandomId());
|
|
60
|
+
}
|
|
61
|
+
localStorage.setItem(CAMPAIGN_ID_KEY, (_a = queryParams.get("c")) !== null && _a !== void 0 ? _a : "");
|
|
62
|
+
localStorage.setItem(REFERRER_ID_KEY, (_b = queryParams.get("r")) !== null && _b !== void 0 ? _b : "");
|
|
63
|
+
});
|
|
64
|
+
const buildTrackingLinkQueryParams = (r, c) => {
|
|
65
|
+
return `c=${c}&origin=fuul&r=${r}`;
|
|
66
|
+
};
|
|
67
|
+
export class Fuul {
|
|
68
|
+
constructor(projectId) {
|
|
69
|
+
this.BASE_API_URL = "https://api.fuul.xyz/api/v1";
|
|
70
|
+
this.projectId = projectId;
|
|
71
|
+
saveSessionId();
|
|
72
|
+
saveTrackingId();
|
|
73
|
+
globalThis.Fuul = this;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* @param {EventType} name Event name.
|
|
77
|
+
* @param {EventArgsType} args Event additional arguments.
|
|
78
|
+
* ```js
|
|
79
|
+
* import { Fuul } from 'fuul-sdk'
|
|
80
|
+
*
|
|
81
|
+
* // Initialize Fuul in your app root file
|
|
82
|
+
* new Fuul('your-project-id')
|
|
83
|
+
*
|
|
84
|
+
* // Then you can send an event as follows:
|
|
85
|
+
* fuul.sendEvent('connect_wallet', {
|
|
86
|
+
* address,
|
|
87
|
+
* ...args
|
|
88
|
+
* })
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
sendEvent(name, args) {
|
|
92
|
+
var _a;
|
|
93
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
94
|
+
const session_id = getSessionId();
|
|
95
|
+
const tracking_id = getTrackingId();
|
|
96
|
+
const campaign_id = getCampaignId();
|
|
97
|
+
const referrer_id = getReferrerId();
|
|
98
|
+
if (!tracking_id)
|
|
99
|
+
return;
|
|
100
|
+
let params = {
|
|
101
|
+
tracking_id,
|
|
102
|
+
};
|
|
103
|
+
let reqBody = {};
|
|
104
|
+
if (name === "connect_wallet") {
|
|
105
|
+
reqBody = {
|
|
106
|
+
name,
|
|
107
|
+
session_id,
|
|
108
|
+
event_args: Object.assign(Object.assign({}, args), { tracking_id }),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
if (!campaign_id || !referrer_id)
|
|
113
|
+
return;
|
|
114
|
+
params = Object.assign(Object.assign({}, params), { referrer_id,
|
|
115
|
+
campaign_id });
|
|
116
|
+
reqBody = {
|
|
117
|
+
name,
|
|
118
|
+
session_id,
|
|
119
|
+
project_id: (_a = this.projectId) !== null && _a !== void 0 ? _a : args === null || args === void 0 ? void 0 : args.project_id,
|
|
120
|
+
event_args: Object.assign(Object.assign({}, args), { referrer: referrer_id, campaign_id,
|
|
121
|
+
tracking_id }),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
if (isEventAlreadySentAndInValidTimestamp(name, params))
|
|
125
|
+
return;
|
|
126
|
+
const url = `${this.BASE_API_URL}/events`;
|
|
127
|
+
try {
|
|
128
|
+
const response = yield axios.post(url, reqBody);
|
|
129
|
+
saveSentEvent(name, params);
|
|
130
|
+
return response.data;
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
return error;
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
verifyConnection() {
|
|
138
|
+
if (window !== undefined && globalThis.Fuul) {
|
|
139
|
+
window.alert("You are successfully connected to Fuul SDK! ✅");
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Generates a tracking link for a referrer
|
|
144
|
+
* @param {string} address referrer address
|
|
145
|
+
* @param {string} cid campaign id you want to refer the user
|
|
146
|
+
* @param {string} baseUrl base url of your app
|
|
147
|
+
* @returns {string} tracking link
|
|
148
|
+
*/
|
|
149
|
+
generateTrackingLink({ address, cid, baseUrl, }) {
|
|
150
|
+
return `${baseUrl !== null && baseUrl !== void 0 ? baseUrl : window.location.href}?${buildTrackingLinkQueryParams(address, cid)}`;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
export default {
|
|
154
|
+
Fuul,
|
|
155
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const SESSION_ID_KEY = "fuul.session_id";
|
|
2
|
+
export declare const TRACKING_ID_KEY = "fuul.tracking_id";
|
|
3
|
+
export declare const CAMPAIGN_ID_KEY = "fuul.campaign_id";
|
|
4
|
+
export declare const REFERRER_ID_KEY = "fuul.referrer_id";
|
|
5
|
+
export declare const SENT_EVENT_ID_KEY = "fuul.sent";
|
|
6
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,oBAAoB,CAAC;AAChD,eAAO,MAAM,eAAe,qBAAqB,CAAC;AAClD,eAAO,MAAM,eAAe,qBAAqB,CAAC;AAClD,eAAO,MAAM,eAAe,qBAAqB,CAAC;AAClD,eAAO,MAAM,iBAAiB,cAAc,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { EventArgsType, EventType, IGenerateTrackingLink } from "./types/types";
|
|
2
|
+
export declare class Fuul {
|
|
3
|
+
private projectId?;
|
|
4
|
+
private BASE_API_URL;
|
|
5
|
+
constructor(projectId?: string);
|
|
6
|
+
/**
|
|
7
|
+
* @param {EventType} name Event name.
|
|
8
|
+
* @param {EventArgsType} args Event additional arguments.
|
|
9
|
+
* ```js
|
|
10
|
+
* import { Fuul } from 'fuul-sdk'
|
|
11
|
+
*
|
|
12
|
+
* // Initialize Fuul in your app root file
|
|
13
|
+
* new Fuul('your-project-id')
|
|
14
|
+
*
|
|
15
|
+
* // Then you can send an event as follows:
|
|
16
|
+
* fuul.sendEvent('connect_wallet', {
|
|
17
|
+
* address,
|
|
18
|
+
* ...args
|
|
19
|
+
* })
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
sendEvent(name: EventType, args?: EventArgsType): Promise<any>;
|
|
23
|
+
verifyConnection(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Generates a tracking link for a referrer
|
|
26
|
+
* @param {string} address referrer address
|
|
27
|
+
* @param {string} cid campaign id you want to refer the user
|
|
28
|
+
* @param {string} baseUrl base url of your app
|
|
29
|
+
* @returns {string} tracking link
|
|
30
|
+
*/
|
|
31
|
+
generateTrackingLink({ address, cid, baseUrl, }: IGenerateTrackingLink): string;
|
|
32
|
+
}
|
|
33
|
+
declare const _default: {
|
|
34
|
+
Fuul: typeof Fuul;
|
|
35
|
+
};
|
|
36
|
+
export default _default;
|
|
37
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAkBA,OAAO,EACL,aAAa,EACb,SAAS,EACT,qBAAqB,EAEtB,MAAM,eAAe,CAAC;AA+EvB,qBAAa,IAAI;IACf,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,YAAY,CAAyC;gBAEjD,SAAS,CAAC,EAAE,MAAM;IAS9B;;;;;;;;;;;;;;;OAeG;IACG,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC;IA4DpE,gBAAgB,IAAI,IAAI;IAMxB;;;;;;OAMG;IACH,oBAAoB,CAAC,EACnB,OAAO,EACP,GAAG,EACH,OAAO,GACR,EAAE,qBAAqB,GAAG,MAAM;CAMlC;;;;AAMD,wBAEE"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface SendEventParams {
|
|
2
|
+
name: EventType;
|
|
3
|
+
args?: EventArgsType;
|
|
4
|
+
projectId?: string;
|
|
5
|
+
}
|
|
6
|
+
export type EventArgsType = {
|
|
7
|
+
[key: string]: string;
|
|
8
|
+
};
|
|
9
|
+
export type EventType = "connect_wallet" | "pageview";
|
|
10
|
+
export interface SentEventParams {
|
|
11
|
+
tracking_id: string;
|
|
12
|
+
campaign_id?: string;
|
|
13
|
+
referrer_id?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface IGenerateTrackingLink {
|
|
16
|
+
address: string;
|
|
17
|
+
cid: string;
|
|
18
|
+
baseUrl?: string;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/types/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,gBAAgB,GAAG,UAAU,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../../../../src/utils/date.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,UAAW,IAAI,UAAU,IAAI,YAGrB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const getSessionId: () => string | null;
|
|
2
|
+
export declare const getTrackingId: () => string | null;
|
|
3
|
+
export declare const getCampaignId: () => string | null;
|
|
4
|
+
export declare const getReferrerId: () => string | null;
|
|
5
|
+
//# sourceMappingURL=localStorage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localStorage.d.ts","sourceRoot":"","sources":["../../../../src/utils/localStorage.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,YAAY,qBAA6C,CAAC;AACvE,eAAO,MAAM,aAAa,qBAA8C,CAAC;AACzE,eAAO,MAAM,aAAa,qBAA8C,CAAC;AACzE,eAAO,MAAM,aAAa,qBAA8C,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { CAMPAIGN_ID_KEY, REFERRER_ID_KEY, SESSION_ID_KEY, TRACKING_ID_KEY, } from "../constants";
|
|
2
|
+
export const getSessionId = () => localStorage.getItem(SESSION_ID_KEY);
|
|
3
|
+
export const getTrackingId = () => localStorage.getItem(TRACKING_ID_KEY);
|
|
4
|
+
export const getCampaignId = () => localStorage.getItem(CAMPAIGN_ID_KEY);
|
|
5
|
+
export const getReferrerId = () => localStorage.getItem(REFERRER_ID_KEY);
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fuul/sdk",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Fuul SDK",
|
|
5
|
+
"types": "./lib/esm/types/index.d.ts",
|
|
6
|
+
"main": "./lib/esm/index.mjs",
|
|
7
|
+
"files": [
|
|
8
|
+
"lib/**/*"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"compile": "node compile.js",
|
|
12
|
+
"clean": "rm -rf ./lib",
|
|
13
|
+
"dev": "ts-node ./src/index.ts",
|
|
14
|
+
"build": "npm run clean && npm run build:esm && npm run build:cjs",
|
|
15
|
+
"build:esm": "tsc -p ./configs/tsconfig.esm.json && mv lib/esm/index.js lib/esm/index.mjs",
|
|
16
|
+
"build:cjs": "tsc -p ./configs/tsconfig.cjs.json",
|
|
17
|
+
"semantic-release": "semantic-release"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [],
|
|
20
|
+
"author": "",
|
|
21
|
+
"license": "ISC",
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"axios": "^1.2.2",
|
|
24
|
+
"nanoid": "^4.0.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@semantic-release/git": "^10.0.1",
|
|
28
|
+
"@semantic-release/npm": "^9.0.2",
|
|
29
|
+
"@types/node": "^18.11.18",
|
|
30
|
+
"semantic-release": "^20.1.1",
|
|
31
|
+
"ts-node": "^10.9.1",
|
|
32
|
+
"typescript": "^4.7.4"
|
|
33
|
+
},
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/fuul-app/fuul-sdk.git"
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"tag": "latest",
|
|
40
|
+
"access": "public"
|
|
41
|
+
}
|
|
42
|
+
}
|