@hellotext/hellotext 1.0.10 → 1.1.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/.babelrc +3 -0
- package/.github/workflows/ci.yml +24 -0
- package/README.md +64 -7
- package/__tests__/event_emitter_test.js +36 -0
- package/__tests__/event_test.js +21 -0
- package/__tests__/hellotext_test.js +156 -0
- package/lib/errors/invalidEvent.js +8 -0
- package/lib/event.js +15 -0
- package/lib/eventEmitter.js +28 -0
- package/lib/hellotext.js +79 -38
- package/lib/response.js +21 -0
- package/package.json +9 -2
package/.babelrc
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches:
|
|
6
|
+
- '*'
|
|
7
|
+
push:
|
|
8
|
+
branches:
|
|
9
|
+
- main
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
strategy:
|
|
15
|
+
matrix:
|
|
16
|
+
node: ['14', '16']
|
|
17
|
+
name: Node ${{ matrix.node }}
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v2
|
|
20
|
+
- uses: actions/setup-node@v2
|
|
21
|
+
with:
|
|
22
|
+
node-version: ${{ matrix.node }}
|
|
23
|
+
- run: npm install
|
|
24
|
+
- run: npm run test
|
package/README.md
CHANGED
|
@@ -43,6 +43,7 @@ Hellotext.track("page.viewed");
|
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
In the example above only the name of the action is required.
|
|
46
|
+
|
|
46
47
|
The library takes care of handling the `url` parameter with the current URL automatically and is not required to specify it explicitly.
|
|
47
48
|
If you want to provide another url, you can pass a `url` key in the params object when tracking an event.
|
|
48
49
|
|
|
@@ -56,7 +57,19 @@ The `track` method returns a Promise that can be `await`ed using the async/await
|
|
|
56
57
|
|
|
57
58
|
```javascript
|
|
58
59
|
const response = await Hellotext.track("page.viewed");
|
|
59
|
-
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The return of the `Hellotext.track` method is an instance of a `Response` object that ships with the package. You can check the status of the response via methods, like
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
if(response.failed) {
|
|
66
|
+
console.log("failed because", response.data)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if(response.succeeded) {
|
|
70
|
+
console.log("success")
|
|
71
|
+
console.log(response.data) // { status: "received" }
|
|
72
|
+
}
|
|
60
73
|
```
|
|
61
74
|
|
|
62
75
|
The parameters passed to the action must be a valid set of parameters as described in
|
|
@@ -66,6 +79,13 @@ Failing to provide valid set of parameters will result in an error object being
|
|
|
66
79
|
|
|
67
80
|
```javascript
|
|
68
81
|
const response = await Hellotext.track("app.installed", { app_attributes: { name: "My App" }})
|
|
82
|
+
|
|
83
|
+
console.log(response.data)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
yields
|
|
87
|
+
|
|
88
|
+
```javascript
|
|
69
89
|
{
|
|
70
90
|
errors: [
|
|
71
91
|
{
|
|
@@ -73,15 +93,15 @@ const response = await Hellotext.track("app.installed", { app_attributes: { name
|
|
|
73
93
|
parameter: 'name',
|
|
74
94
|
description: 'The value must be unique and it is already present in another object of the same type.'
|
|
75
95
|
},
|
|
76
|
-
]
|
|
77
|
-
success: false
|
|
96
|
+
]
|
|
78
97
|
}
|
|
79
98
|
```
|
|
80
99
|
|
|
81
|
-
You can use `success` key present in the response object to run code conditionally.
|
|
82
100
|
For a complete list of errors types. See [Error Types](https://www.hellotext.com/api#errors)
|
|
83
101
|
|
|
84
102
|
Generally, most actions also require an associated object. These can be of type [`app`](https://www.hellotext.com/api#apps), [`coupon`](https://www.hellotext.com/api#coupons), [`form`](https://www.hellotext.com/api#forms), [`order`](https://www.hellotext.com/api#orders), [`product`](https://www.hellotext.com/api#products) and [`refund`](https://www.hellotext.com/api#refunds).
|
|
103
|
+
Aside from [Custom Actions](https://www.hellotext.com/api#create_an_action), which don't require the trackable to be present.
|
|
104
|
+
|
|
85
105
|
|
|
86
106
|
You can create the associated object directly by defining its attributes in a hash:
|
|
87
107
|
|
|
@@ -157,6 +177,27 @@ Hellotext.track("product.purchased", {
|
|
|
157
177
|
| **tracked_at** | Original date when the event happened. This is useful if you want to record an event that happened in the past. If no value is provided its value will be the same from `created_at`. | epoch | `null`
|
|
158
178
|
|
|
159
179
|
|
|
180
|
+
## Events
|
|
181
|
+
|
|
182
|
+
This library emits events that you can listen to and perform specific action when the event happens.
|
|
183
|
+
Think of it like `addEventListener` for HTML elements. You can listen for events, and remove events as well.
|
|
184
|
+
|
|
185
|
+
To listen to an event, you can call the `on` method, like so
|
|
186
|
+
|
|
187
|
+
```javascript
|
|
188
|
+
Hellotext.on(eventName, callback)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
To disconnect an event listener, you can call `disconnect`
|
|
192
|
+
|
|
193
|
+
```javascript
|
|
194
|
+
Hellotext.disconnect(eventName, callback)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### List of events
|
|
198
|
+
|
|
199
|
+
- `session-set`: This event is fired when the session value for `Hellotext.session` is set. Either through an API request, or if the session was found in the cookie.
|
|
200
|
+
|
|
160
201
|
## Understanding Sessions
|
|
161
202
|
|
|
162
203
|
The library looks for a session identifier present on the `hellotext_session` query parameter. If the session is not present as a cookie neither it will create a new random session identifier.
|
|
@@ -172,8 +213,24 @@ It is possible to obtain the current session by simply calling `Hellotext.sessio
|
|
|
172
213
|
await Hellotext.session
|
|
173
214
|
// Returns bBJn9vR15yPaYkWmR2QK0jopMeNxrA6l
|
|
174
215
|
```
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
216
|
+
|
|
217
|
+
If the session has not been set yet, the result returned will be `undefined`.
|
|
218
|
+
You can check whether the session has been set or not by calling `Hellotext.isInitialized`.
|
|
219
|
+
|
|
220
|
+
```javascript
|
|
221
|
+
if(Hellotext.isInitialized) {
|
|
222
|
+
console.log("session is present")
|
|
223
|
+
} else {
|
|
224
|
+
console.log("session has not been set")
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Moreover, you can hook in and listen for the session being set, such that when it's set, you're notified about the change, like so
|
|
229
|
+
|
|
230
|
+
```javascript
|
|
231
|
+
Hellotext.on("session-set", (session) => {
|
|
232
|
+
console.log("session is: ", session)
|
|
233
|
+
})
|
|
234
|
+
```
|
|
178
235
|
|
|
179
236
|
You may want to store the session on your backend when customers are unidentified so you can later [attach it to a profile](https://www.hellotext.com/api#attach_session) when it becomes known.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import EventEmitter from "../lib/eventEmitter"
|
|
2
|
+
|
|
3
|
+
describe("#addSubscriber", function () {
|
|
4
|
+
const instance = new EventEmitter()
|
|
5
|
+
|
|
6
|
+
it("adds the callback to the list of subscribers for an event", () => {
|
|
7
|
+
const callback = (session) => {}
|
|
8
|
+
instance.addSubscriber("session-set", callback)
|
|
9
|
+
expect(instance.subscribers["session-set"].length).toEqual(1)
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe("#removeSubscriber", function () {
|
|
14
|
+
const instance = new EventEmitter()
|
|
15
|
+
|
|
16
|
+
it("removes the callback from the list of subscribers for an event", () => {
|
|
17
|
+
const callback = (session) => {}
|
|
18
|
+
instance.addSubscriber("session-set", callback)
|
|
19
|
+
instance.removeSubscriber("session-set", callback)
|
|
20
|
+
|
|
21
|
+
expect(instance.subscribers["session-set"].length).toEqual(0)
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe("#emit", () => {
|
|
26
|
+
const instance = new EventEmitter()
|
|
27
|
+
|
|
28
|
+
it("notifies the listeners for an event", () => {
|
|
29
|
+
const callback = jest.fn()
|
|
30
|
+
|
|
31
|
+
instance.addSubscriber("session-set", callback)
|
|
32
|
+
instance.emit("session-set", "session_payload")
|
|
33
|
+
|
|
34
|
+
expect(callback).toHaveBeenCalledTimes(1)
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import Event from "../lib/event"
|
|
2
|
+
|
|
3
|
+
describe(".valid", function () {
|
|
4
|
+
it("is true when event name is a valid defined name", () => {
|
|
5
|
+
expect(Event.valid("session-set")).toEqual(true)
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it("is false when event name is not defined", () => {
|
|
9
|
+
expect(Event.valid("undefined-event")).toEqual(false)
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe(".invalid", () => {
|
|
14
|
+
it("is true when event name is not defined", () => {
|
|
15
|
+
expect(Event.invalid("undefined-event")).toEqual(true)
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("is false when event name is a valid defined name", () => {
|
|
19
|
+
expect(Event.invalid("session-set")).toEqual(false)
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import Hellotext from "../lib/hellotext";
|
|
6
|
+
|
|
7
|
+
const getCookieValue = name => document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]+)')?.pop()
|
|
8
|
+
|
|
9
|
+
describe("when trying to call methods before initializing the class", () => {
|
|
10
|
+
it("raises an error when Hellotext.session is called", () => {
|
|
11
|
+
expect(() => Hellotext.session).toThrowError()
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("raises an error when Hellotext.track is called", () => {
|
|
15
|
+
expect(Hellotext.track("page.viewed")).rejects.toThrowError()
|
|
16
|
+
});
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
describe("when the class is initialized successfully", () => {
|
|
20
|
+
const business_id = "xy76ks"
|
|
21
|
+
|
|
22
|
+
describe("when hello_session is present in the query params", () => {
|
|
23
|
+
beforeAll(() => {
|
|
24
|
+
const windowMock = {
|
|
25
|
+
location: { search: "?hello_session=session" },
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
jest.spyOn(global, 'window', 'get').mockImplementation(() => windowMock)
|
|
29
|
+
Hellotext.initialize(business_id)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it("sets the cookie as value of the query parameter", () => {
|
|
33
|
+
expect(getCookieValue("hello_session")).toEqual("session")
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("returns the value when Hellotext.session is called", () => {
|
|
37
|
+
expect(Hellotext.session).toEqual("session")
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe("when tracking events", () => {
|
|
41
|
+
it("success attribute is true when response from the server is received successfully", async () => {
|
|
42
|
+
global.fetch = jest.fn().mockResolvedValue({
|
|
43
|
+
json: jest.fn().mockResolvedValue({received: "success"}),
|
|
44
|
+
status: 200
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
const response = await Hellotext.track("page.viewed")
|
|
48
|
+
|
|
49
|
+
expect(response.succeeded).toEqual(true)
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("success attribute is false when response from the server is rejected", async () => {
|
|
53
|
+
global.fetch = jest.fn().mockResolvedValue({
|
|
54
|
+
json: jest.fn().mockResolvedValue({}),
|
|
55
|
+
status: 422
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
const response = await Hellotext.track("page.viewed")
|
|
59
|
+
|
|
60
|
+
expect(response.failed).toEqual(true)
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe("when hello_session is not present in the query params", () => {
|
|
66
|
+
const business_id = "xy76ks"
|
|
67
|
+
|
|
68
|
+
beforeAll(() => {
|
|
69
|
+
const windowMock = {location: { search: "" },}
|
|
70
|
+
jest.spyOn(global, 'window', 'get').mockImplementation(() => windowMock)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it("mints a new session token and sets the cookie as well", () => {
|
|
74
|
+
global.fetch = jest.fn().mockResolvedValue({
|
|
75
|
+
json: jest.fn().mockResolvedValue({id: "generated_token"}),
|
|
76
|
+
status: 200
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
Hellotext.initialize(business_id)
|
|
80
|
+
|
|
81
|
+
setTimeout(() => {
|
|
82
|
+
expect(getCookieValue("hello_session")).toEqual("generated_token")
|
|
83
|
+
expect(Hellotext.session).toEqual("generated_token")
|
|
84
|
+
}, 1000)
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe(".isInitialized", () => {
|
|
90
|
+
describe("when session is set", () => {
|
|
91
|
+
beforeAll(() => {
|
|
92
|
+
const windowMock = {location: { search: "?hello_session=session" }}
|
|
93
|
+
jest.spyOn(global, 'window', 'get').mockImplementation(() => windowMock)
|
|
94
|
+
Hellotext.initialize("123")
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it("is true", () => {
|
|
98
|
+
expect(Hellotext.isInitialized).toEqual(true)
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe(".on", () => {
|
|
104
|
+
const business_id = "xy76ks"
|
|
105
|
+
|
|
106
|
+
beforeAll(() => {
|
|
107
|
+
const windowMock = {location: { search: "" },}
|
|
108
|
+
jest.spyOn(global, 'window', 'get').mockImplementation(() => windowMock)
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it("registers a callback that is called when the session is set", function () {
|
|
112
|
+
global.fetch = jest.fn().mockResolvedValue({
|
|
113
|
+
json: jest.fn().mockResolvedValue({id: "generated_token"}),
|
|
114
|
+
status: 200
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
const callback = jest.fn()
|
|
118
|
+
|
|
119
|
+
Hellotext.on("session-set", callback)
|
|
120
|
+
Hellotext.initialize(business_id)
|
|
121
|
+
|
|
122
|
+
expect(callback).toHaveBeenCalledTimes(1)
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("throws an error when event is invalid", () => {
|
|
126
|
+
expect(
|
|
127
|
+
() => Hellotext.on("undefined-event", () => {})
|
|
128
|
+
).toThrowError()
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe(".disconnect", () => {
|
|
133
|
+
const business_id = "xy76ks"
|
|
134
|
+
|
|
135
|
+
beforeAll(() => {
|
|
136
|
+
const windowMock = {location: { search: "?hello_session=123" },}
|
|
137
|
+
jest.spyOn(global, 'window', 'get').mockImplementation(() => windowMock)
|
|
138
|
+
|
|
139
|
+
Hellotext.initialize(123)
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it("throws an error when event is invalid", () => {
|
|
143
|
+
expect(
|
|
144
|
+
() => Hellotext.disconnect("undefined-event", () => {})
|
|
145
|
+
).toThrowError()
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("removes the callback from the subscribers and will not be notified again", () => {
|
|
149
|
+
const callback = jest.fn()
|
|
150
|
+
|
|
151
|
+
Hellotext.on("session-set", callback)
|
|
152
|
+
Hellotext.disconnect("session-set", callback)
|
|
153
|
+
|
|
154
|
+
expect(callback).toHaveBeenCalledTimes(0)
|
|
155
|
+
});
|
|
156
|
+
})
|
package/lib/event.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default class Event {
|
|
2
|
+
static events = ["session-set"]
|
|
3
|
+
|
|
4
|
+
static valid(name) {
|
|
5
|
+
return Event.exists(name)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
static invalid(name) {
|
|
9
|
+
return !this.valid(name)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
static exists(name) {
|
|
13
|
+
return this.events.find((eventName) => eventName === name) !== undefined
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export default class EventEmitter {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.subscribers = {}
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
addSubscriber(eventName, callback) {
|
|
7
|
+
this.subscribers = {
|
|
8
|
+
...this.subscribers,
|
|
9
|
+
[eventName]: this.subscribers[eventName] ? [...this.subscribers[eventName], callback] : [callback]
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
removeSubscriber(eventName, callback) {
|
|
14
|
+
if(this.subscribers[eventName]) {
|
|
15
|
+
this.subscribers[eventName] = this.subscribers[eventName].filter((cb) => cb !== callback)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
emit(eventName, data) {
|
|
20
|
+
this.subscribers[eventName].forEach((subscriber) => {
|
|
21
|
+
subscriber(data)
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get listeners() {
|
|
26
|
+
return Object.keys(this.subscribers).length !== 0
|
|
27
|
+
}
|
|
28
|
+
}
|
package/lib/hellotext.js
CHANGED
|
@@ -1,104 +1,145 @@
|
|
|
1
|
+
import Event from "./event"
|
|
2
|
+
import EventEmitter from "./eventEmitter"
|
|
3
|
+
import Response from "./response";
|
|
4
|
+
|
|
1
5
|
import { NotInitializedError } from './errors/notInitializedError'
|
|
6
|
+
import { InvalidEvent } from "./errors/invalidEvent"
|
|
2
7
|
|
|
3
8
|
const apiUrl = 'https://api.hellotext.com/v1/'
|
|
4
9
|
|
|
5
10
|
class Hellotext {
|
|
6
|
-
static #
|
|
7
|
-
static #
|
|
11
|
+
static #session
|
|
12
|
+
static #business
|
|
13
|
+
static #eventEmitter = new EventEmitter()
|
|
14
|
+
|
|
8
15
|
/**
|
|
9
16
|
* initialize the module.
|
|
17
|
+
* @param business public business id
|
|
10
18
|
*/
|
|
11
|
-
static initialize(
|
|
12
|
-
this.#
|
|
19
|
+
static initialize(business) {
|
|
20
|
+
this.#business = business
|
|
13
21
|
|
|
14
22
|
const urlSearchParams = new URLSearchParams(window.location.search)
|
|
15
|
-
const session = urlSearchParams.get('hello_session') ||
|
|
16
|
-
|
|
17
|
-
if (session) {
|
|
18
|
-
this.#
|
|
19
|
-
this
|
|
23
|
+
const session = urlSearchParams.get('hello_session') || this.#cookie
|
|
24
|
+
|
|
25
|
+
if (session && session !== "undefined" && session !== "null") {
|
|
26
|
+
this.#session = session
|
|
27
|
+
this.#setSessionCookie()
|
|
28
|
+
} else {
|
|
29
|
+
this.#mintAnonymousSession()
|
|
30
|
+
.then(response => {
|
|
31
|
+
this.#session = response.id
|
|
32
|
+
this.#setSessionCookie()
|
|
33
|
+
})
|
|
20
34
|
}
|
|
21
35
|
}
|
|
22
36
|
|
|
23
37
|
/**
|
|
38
|
+
* Tracks an action that has happened on the page
|
|
24
39
|
*
|
|
25
40
|
* @param { String } action a valid action name
|
|
26
41
|
* @param { Object } params
|
|
27
42
|
* @returns {Promise}
|
|
28
43
|
*/
|
|
29
|
-
static async track(action, params) {
|
|
30
|
-
if (this
|
|
44
|
+
static async track(action, params = {}) {
|
|
45
|
+
if (this.#notInitialized) { throw new NotInitializedError() }
|
|
31
46
|
|
|
32
47
|
const response = await fetch(apiUrl + 'track/events', {
|
|
33
|
-
headers: this
|
|
48
|
+
headers: this.#headers,
|
|
34
49
|
method: 'post',
|
|
35
50
|
body: JSON.stringify({
|
|
36
|
-
session:
|
|
51
|
+
session: this.session,
|
|
37
52
|
action,
|
|
38
53
|
...params,
|
|
39
54
|
url: (params && params.url) || window.location.href
|
|
40
55
|
}),
|
|
41
56
|
})
|
|
42
57
|
|
|
43
|
-
|
|
44
|
-
|
|
58
|
+
return new Response(response.status === 200, await response.json)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Registers an event listener
|
|
63
|
+
* @param event the name of the event to listen to
|
|
64
|
+
* @param callback the callback. This method will be called with the payload
|
|
65
|
+
*/
|
|
66
|
+
static on(event, callback) {
|
|
67
|
+
if(Event.invalid(event)) { throw new InvalidEvent(event) }
|
|
68
|
+
|
|
69
|
+
this.#eventEmitter.addSubscriber(event, callback)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Removes an event listener
|
|
74
|
+
* @param event the name of the event to remove
|
|
75
|
+
* @param callback the callback to remove
|
|
76
|
+
*/
|
|
77
|
+
static disconnect(event, callback) {
|
|
78
|
+
if(Event.invalid(event)) { throw new InvalidEvent(event) }
|
|
45
79
|
|
|
46
|
-
|
|
80
|
+
this.#eventEmitter.removeSubscriber(event, callback)
|
|
47
81
|
}
|
|
48
82
|
|
|
49
83
|
/**
|
|
50
84
|
*
|
|
51
|
-
* @returns {
|
|
85
|
+
* @returns {String}
|
|
52
86
|
*/
|
|
53
87
|
static get session() {
|
|
54
|
-
if (this
|
|
88
|
+
if (this.#notInitialized) { throw new NotInitializedError() }
|
|
55
89
|
|
|
56
|
-
|
|
90
|
+
return this.#session
|
|
91
|
+
}
|
|
57
92
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Determines if the session is set or not
|
|
95
|
+
* @returns {boolean}
|
|
96
|
+
*/
|
|
97
|
+
static get isInitialized() {
|
|
98
|
+
return this.#session !== undefined
|
|
64
99
|
}
|
|
65
100
|
|
|
66
101
|
// private
|
|
67
102
|
|
|
68
|
-
static
|
|
69
|
-
|
|
103
|
+
static get #notInitialized() {
|
|
104
|
+
return this.#business === undefined
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
static async #mintAnonymousSession() {
|
|
108
|
+
if (this.#notInitialized) { throw new NotInitializedError() }
|
|
70
109
|
|
|
71
110
|
const trackingUrl = apiUrl + 'track/sessions'
|
|
72
111
|
|
|
73
112
|
this.mintingPromise = await fetch(trackingUrl, {
|
|
74
113
|
method: 'post',
|
|
75
|
-
headers: { Authorization: `Bearer ${this.#
|
|
114
|
+
headers: { Authorization: `Bearer ${this.#business}` },
|
|
76
115
|
})
|
|
77
116
|
|
|
78
117
|
return this.mintingPromise.json()
|
|
79
118
|
}
|
|
80
119
|
|
|
81
|
-
static get headers() {
|
|
82
|
-
if (this
|
|
120
|
+
static get #headers() {
|
|
121
|
+
if (this.#notInitialized) { throw new NotInitializedError() }
|
|
83
122
|
|
|
84
123
|
return {
|
|
85
|
-
Authorization: `Bearer ${this.#
|
|
124
|
+
Authorization: `Bearer ${this.#business}`,
|
|
86
125
|
Accept: 'application.json',
|
|
87
126
|
'Content-Type': 'application/json',
|
|
88
127
|
}
|
|
89
128
|
}
|
|
90
129
|
|
|
91
|
-
static setSessionCookie(
|
|
92
|
-
if (this
|
|
130
|
+
static #setSessionCookie() {
|
|
131
|
+
if (this.#notInitialized) { throw new NotInitializedError() }
|
|
93
132
|
|
|
94
|
-
|
|
133
|
+
if(this.#eventEmitter.listeners) {
|
|
134
|
+
this.#eventEmitter.emit("session-set", this.#session)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
document.cookie = `hello_session=${this.#session}`
|
|
95
138
|
}
|
|
96
139
|
|
|
97
|
-
static
|
|
98
|
-
return
|
|
140
|
+
static #cookie() {
|
|
141
|
+
return document.cookie.match('(^|;)\\s*' + 'hello_session' + '\\s*=\\s*([^;]+)')?.pop()
|
|
99
142
|
}
|
|
100
143
|
}
|
|
101
144
|
|
|
102
|
-
const getCookieValue = name => document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]+)')?.pop()
|
|
103
|
-
|
|
104
145
|
export default Hellotext
|
package/lib/response.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export default class Response {
|
|
2
|
+
#success
|
|
3
|
+
#response
|
|
4
|
+
|
|
5
|
+
constructor(success, response) {
|
|
6
|
+
this.#success = success
|
|
7
|
+
this.#response = response
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
data() {
|
|
11
|
+
return this.#response
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
get failed() {
|
|
15
|
+
return this.#success === false
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get succeeded() {
|
|
19
|
+
return this.#success === true
|
|
20
|
+
}
|
|
21
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hellotext/hellotext",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Hellotext
|
|
3
|
+
"version": "1.1.1",
|
|
4
|
+
"description": "Hellotext JavaScript Client",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"author": "Hellotext",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"homepage": "https://github.com/hellotext/hellotext.js",
|
|
9
9
|
"devDependencies": {
|
|
10
|
+
"@babel/preset-env": "^7.20.2",
|
|
11
|
+
"@testing-library/jest-dom": "^5.16.5",
|
|
12
|
+
"jest": "^29.3.1",
|
|
13
|
+
"jest-environment-jsdom": "^29.3.1",
|
|
10
14
|
"prettier": "2.8.2"
|
|
11
15
|
},
|
|
12
16
|
"directories": {
|
|
@@ -20,6 +24,9 @@
|
|
|
20
24
|
"hellotext",
|
|
21
25
|
"javascript"
|
|
22
26
|
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"test": "NODE_ENV=test jest"
|
|
29
|
+
},
|
|
23
30
|
"bugs": {
|
|
24
31
|
"url": "https://github.com/hellotext/hellotext.js/issues"
|
|
25
32
|
}
|