@hellotext/hellotext 2.2.0 → 2.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 +1 -44
- package/dist/hellotext.js +1 -1
- package/index.d.ts +28 -0
- package/lib/api/identifications.cjs +42 -0
- package/lib/api/identifications.js +45 -0
- package/lib/api/index.cjs +6 -0
- package/lib/api/index.js +6 -0
- package/lib/hellotext.cjs +38 -14
- package/lib/hellotext.js +44 -15
- package/lib/models/cookies.cjs +23 -1
- package/lib/models/cookies.js +23 -1
- package/lib/models/index.cjs +7 -0
- package/lib/models/index.js +1 -0
- package/lib/models/page.cjs +79 -1
- package/lib/models/page.js +80 -1
- package/lib/models/user.cjs +53 -0
- package/lib/models/user.js +47 -0
- package/package.json +1 -1
- package/src/api/identifications.js +25 -0
- package/src/api/index.js +5 -0
- package/src/hellotext.js +37 -5
- package/src/models/cookies.js +23 -1
- package/src/models/index.js +1 -0
- package/src/models/page.js +83 -1
- package/src/models/user.js +35 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.User = void 0;
|
|
7
|
+
var _cookies = require("./cookies");
|
|
8
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
9
|
+
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
|
|
10
|
+
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
|
11
|
+
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
|
12
|
+
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
|
13
|
+
let User = /*#__PURE__*/function () {
|
|
14
|
+
function User() {
|
|
15
|
+
_classCallCheck(this, User);
|
|
16
|
+
}
|
|
17
|
+
_createClass(User, null, [{
|
|
18
|
+
key: "id",
|
|
19
|
+
get: function () {
|
|
20
|
+
return _cookies.Cookies.get('hello_user_id');
|
|
21
|
+
}
|
|
22
|
+
}, {
|
|
23
|
+
key: "source",
|
|
24
|
+
get: function () {
|
|
25
|
+
return _cookies.Cookies.get('hello_user_source');
|
|
26
|
+
}
|
|
27
|
+
}, {
|
|
28
|
+
key: "remember",
|
|
29
|
+
value: function remember(id, source) {
|
|
30
|
+
if (source) {
|
|
31
|
+
_cookies.Cookies.set('hello_user_source', source);
|
|
32
|
+
}
|
|
33
|
+
_cookies.Cookies.set('hello_user_id', id);
|
|
34
|
+
}
|
|
35
|
+
}, {
|
|
36
|
+
key: "forget",
|
|
37
|
+
value: function forget() {
|
|
38
|
+
_cookies.Cookies.delete('hello_user_id');
|
|
39
|
+
_cookies.Cookies.delete('hello_user_source');
|
|
40
|
+
}
|
|
41
|
+
}, {
|
|
42
|
+
key: "identificationData",
|
|
43
|
+
get: function () {
|
|
44
|
+
if (!this.id) return {};
|
|
45
|
+
return {
|
|
46
|
+
id: this.id,
|
|
47
|
+
source: this.source
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}]);
|
|
51
|
+
return User;
|
|
52
|
+
}();
|
|
53
|
+
exports.User = User;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
2
|
+
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
|
|
3
|
+
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
|
4
|
+
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
|
5
|
+
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
|
6
|
+
import { Cookies } from './cookies';
|
|
7
|
+
var User = /*#__PURE__*/function () {
|
|
8
|
+
function User() {
|
|
9
|
+
_classCallCheck(this, User);
|
|
10
|
+
}
|
|
11
|
+
_createClass(User, null, [{
|
|
12
|
+
key: "id",
|
|
13
|
+
get: function get() {
|
|
14
|
+
return Cookies.get('hello_user_id');
|
|
15
|
+
}
|
|
16
|
+
}, {
|
|
17
|
+
key: "source",
|
|
18
|
+
get: function get() {
|
|
19
|
+
return Cookies.get('hello_user_source');
|
|
20
|
+
}
|
|
21
|
+
}, {
|
|
22
|
+
key: "remember",
|
|
23
|
+
value: function remember(id, source) {
|
|
24
|
+
if (source) {
|
|
25
|
+
Cookies.set('hello_user_source', source);
|
|
26
|
+
}
|
|
27
|
+
Cookies.set('hello_user_id', id);
|
|
28
|
+
}
|
|
29
|
+
}, {
|
|
30
|
+
key: "forget",
|
|
31
|
+
value: function forget() {
|
|
32
|
+
Cookies.delete('hello_user_id');
|
|
33
|
+
Cookies.delete('hello_user_source');
|
|
34
|
+
}
|
|
35
|
+
}, {
|
|
36
|
+
key: "identificationData",
|
|
37
|
+
get: function get() {
|
|
38
|
+
if (!this.id) return {};
|
|
39
|
+
return {
|
|
40
|
+
id: this.id,
|
|
41
|
+
source: this.source
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}]);
|
|
45
|
+
return User;
|
|
46
|
+
}();
|
|
47
|
+
export { User };
|
package/package.json
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import Hellotext from '../hellotext'
|
|
2
|
+
|
|
3
|
+
import { Configuration } from '../core'
|
|
4
|
+
import { Response } from './response'
|
|
5
|
+
|
|
6
|
+
class IdentificationsAPI {
|
|
7
|
+
static get endpoint() {
|
|
8
|
+
return Configuration.endpoint('public/identifications')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
static async create(data = {}) {
|
|
12
|
+
const response = await fetch(this.endpoint, {
|
|
13
|
+
method: 'POST',
|
|
14
|
+
headers: Hellotext.headers,
|
|
15
|
+
body: JSON.stringify({
|
|
16
|
+
session: Hellotext.session,
|
|
17
|
+
...data,
|
|
18
|
+
}),
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
return new Response(response.ok, response)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default IdentificationsAPI
|
package/src/api/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import BusinessesAPI from './businesses'
|
|
2
2
|
import EventsAPI from './events'
|
|
3
3
|
import FormsAPI from './forms'
|
|
4
|
+
import IdentificationsAPI from './identifications'
|
|
4
5
|
import WebchatsAPI from './webchats'
|
|
5
6
|
|
|
6
7
|
export default class API {
|
|
@@ -19,6 +20,10 @@ export default class API {
|
|
|
19
20
|
static get webchats() {
|
|
20
21
|
return WebchatsAPI
|
|
21
22
|
}
|
|
23
|
+
|
|
24
|
+
static get identifications() {
|
|
25
|
+
return IdentificationsAPI
|
|
26
|
+
}
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
export { Response } from './response'
|
package/src/hellotext.js
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { Configuration, Event } from './core'
|
|
2
2
|
|
|
3
3
|
import API, { Response } from './api'
|
|
4
|
-
import { Business, FormCollection, Page, Query, Session, Webchat } from './models'
|
|
4
|
+
import { Business, FormCollection, Page, Query, Session, User, Webchat } from './models'
|
|
5
5
|
|
|
6
6
|
import { NotInitializedError } from './errors'
|
|
7
7
|
|
|
8
8
|
class Hellotext {
|
|
9
|
-
static #session
|
|
10
|
-
static #query
|
|
11
|
-
|
|
12
9
|
static eventEmitter = new Event()
|
|
13
10
|
static forms
|
|
14
11
|
static business
|
|
@@ -27,7 +24,7 @@ class Hellotext {
|
|
|
27
24
|
this.business = new Business(business)
|
|
28
25
|
this.forms = new FormCollection()
|
|
29
26
|
|
|
30
|
-
this
|
|
27
|
+
this.query = new Query()
|
|
31
28
|
|
|
32
29
|
if (Configuration.webchat.id) {
|
|
33
30
|
this.webchat = await Webchat.load(Configuration.webchat.id)
|
|
@@ -59,6 +56,7 @@ class Hellotext {
|
|
|
59
56
|
|
|
60
57
|
const body = {
|
|
61
58
|
session: this.session,
|
|
59
|
+
user: User.identificationData,
|
|
62
60
|
action,
|
|
63
61
|
...params,
|
|
64
62
|
...pageInstance.trackingData,
|
|
@@ -72,6 +70,40 @@ class Hellotext {
|
|
|
72
70
|
})
|
|
73
71
|
}
|
|
74
72
|
|
|
73
|
+
/**
|
|
74
|
+
* @typedef { Object } IdentificationOptions
|
|
75
|
+
* @property { String } [email] - the email of the user
|
|
76
|
+
* @property { String } [phone] - the phone number of the user
|
|
77
|
+
* @property { String } [name] - the name of the user
|
|
78
|
+
* @property { String } [source] - the platform specific identifier where this pixel is running on.
|
|
79
|
+
*
|
|
80
|
+
* Identifies a user and attaches the hello_session to the user ID
|
|
81
|
+
* @param { String } externalId - the user ID
|
|
82
|
+
* @param { IdentificationOptions } options - the options for the identification
|
|
83
|
+
* @returns {Promise<Response>}
|
|
84
|
+
*/
|
|
85
|
+
static async identify(externalId, options = {}) {
|
|
86
|
+
const response = await API.identifications.create({
|
|
87
|
+
user_id: externalId,
|
|
88
|
+
...options,
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
if (response.succeeded) {
|
|
92
|
+
User.remember(externalId, options.source)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return response
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Clears the user session, use when the user logs out to clear the hello cookies
|
|
100
|
+
*
|
|
101
|
+
* @returns {void}
|
|
102
|
+
*/
|
|
103
|
+
static forget() {
|
|
104
|
+
User.forget()
|
|
105
|
+
}
|
|
106
|
+
|
|
75
107
|
/**
|
|
76
108
|
* Registers an event listener
|
|
77
109
|
* @param event the name of the event to listen to
|
package/src/models/cookies.js
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import Hellotext from '../hellotext'
|
|
2
|
+
import { Page } from './page'
|
|
2
3
|
|
|
3
4
|
class Cookies {
|
|
4
5
|
static set(name, value) {
|
|
5
6
|
if (typeof document !== 'undefined') {
|
|
6
|
-
|
|
7
|
+
const secure = window.location.protocol === 'https:' ? '; Secure' : ''
|
|
8
|
+
const domain = Page.getRootDomain()
|
|
9
|
+
const maxAge = 10 * 365 * 24 * 60 * 60 // 10 years in seconds
|
|
10
|
+
|
|
11
|
+
if (domain) {
|
|
12
|
+
document.cookie = `${name}=${value}; path=/${secure}; domain=${domain}; max-age=${maxAge}; SameSite=Lax`
|
|
13
|
+
} else {
|
|
14
|
+
document.cookie = `${name}=${value}; path=/${secure}; max-age=${maxAge}; SameSite=Lax`
|
|
15
|
+
}
|
|
7
16
|
}
|
|
8
17
|
|
|
9
18
|
if (name === 'hello_session') {
|
|
@@ -24,6 +33,19 @@ class Cookies {
|
|
|
24
33
|
return undefined
|
|
25
34
|
}
|
|
26
35
|
}
|
|
36
|
+
|
|
37
|
+
static delete(name) {
|
|
38
|
+
if (typeof document !== 'undefined') {
|
|
39
|
+
const domain = Page.getRootDomain()
|
|
40
|
+
const secure = window.location.protocol === 'https:' ? '; Secure' : ''
|
|
41
|
+
|
|
42
|
+
if (domain) {
|
|
43
|
+
document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT${secure}; domain=${domain}; SameSite=Lax`
|
|
44
|
+
} else {
|
|
45
|
+
document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT${secure}; SameSite=Lax`
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
27
49
|
}
|
|
28
50
|
|
|
29
51
|
export { Cookies }
|
package/src/models/index.js
CHANGED
package/src/models/page.js
CHANGED
|
@@ -11,7 +11,7 @@ class Page {
|
|
|
11
11
|
* @returns {string} The page URL
|
|
12
12
|
*/
|
|
13
13
|
get url() {
|
|
14
|
-
return this._url
|
|
14
|
+
return this._url !== null && this._url !== undefined ? this._url : window.location.href
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
/**
|
|
@@ -60,6 +60,88 @@ class Page {
|
|
|
60
60
|
utm_params: this.utmParams,
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get the current page domain (root domain for cookie sharing)
|
|
66
|
+
* @returns {string} The root domain with leading dot, or null if no valid URL
|
|
67
|
+
*/
|
|
68
|
+
get domain() {
|
|
69
|
+
try {
|
|
70
|
+
const url = this.url
|
|
71
|
+
if (!url) {
|
|
72
|
+
return null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const hostname = new URL(url).hostname
|
|
76
|
+
return Page.getRootDomain(hostname)
|
|
77
|
+
} catch (e) {
|
|
78
|
+
return null
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get the root domain from a hostname (static method, no instance needed)
|
|
84
|
+
* @param {string} hostname - The hostname to parse
|
|
85
|
+
* @returns {string} The root domain with leading dot, or null if invalid
|
|
86
|
+
*/
|
|
87
|
+
static getRootDomain(hostname = null) {
|
|
88
|
+
try {
|
|
89
|
+
if (!hostname) {
|
|
90
|
+
if (typeof window === 'undefined' || !window.location?.hostname) {
|
|
91
|
+
return null
|
|
92
|
+
}
|
|
93
|
+
hostname = window.location.hostname
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const parts = hostname.split('.')
|
|
97
|
+
|
|
98
|
+
// Handle localhost or single-part domains
|
|
99
|
+
if (parts.length <= 1) {
|
|
100
|
+
return hostname
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Handle e-commerce platform domains where store identifier must be preserved
|
|
104
|
+
// Examples:
|
|
105
|
+
// storename.myshopify.com -> .storename.myshopify.com
|
|
106
|
+
// storename.vtexcommercestable.com.br -> .storename.vtexcommercestable.com.br
|
|
107
|
+
// storename.wixsite.com -> .storename.wixsite.com
|
|
108
|
+
const platformSuffixes = [
|
|
109
|
+
'myshopify.com',
|
|
110
|
+
'vtexcommercestable.com.br',
|
|
111
|
+
'myvtex.com',
|
|
112
|
+
'wixsite.com',
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
for (const suffix of platformSuffixes) {
|
|
116
|
+
const suffixParts = suffix.split('.')
|
|
117
|
+
// Get the last N parts of the hostname (e.g., last 2 parts for 'myshopify.com')
|
|
118
|
+
const domainTail = parts.slice(-suffixParts.length).join('.')
|
|
119
|
+
|
|
120
|
+
// Check if the tail exactly matches the platform suffix
|
|
121
|
+
// and there are additional parts for the store identifier
|
|
122
|
+
if (domainTail === suffix && parts.length > suffixParts.length) {
|
|
123
|
+
// Include store identifier + platform suffix
|
|
124
|
+
// e.g., ['secure', 'mystore', 'myshopify', 'com'] -> '.mystore.myshopify.com'
|
|
125
|
+
return `.${parts.slice(-(suffixParts.length + 1)).join('.')}`
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Simple heuristic: if TLD is 2 chars and second-level is short (<=3 chars),
|
|
130
|
+
// it's likely a multi-part TLD like .com.br, .co.uk
|
|
131
|
+
const tld = parts[parts.length - 1]
|
|
132
|
+
const secondLevel = parts[parts.length - 2]
|
|
133
|
+
|
|
134
|
+
if (parts.length > 2 && tld.length === 2 && secondLevel.length <= 3) {
|
|
135
|
+
// Multi-part TLD: take last 3 parts (e.g., store.com.br)
|
|
136
|
+
return `.${parts.slice(-3).join('.')}`
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Regular TLD: take last 2 parts (e.g., example.com)
|
|
140
|
+
return `.${parts.slice(-2).join('.')}`
|
|
141
|
+
} catch (e) {
|
|
142
|
+
return null
|
|
143
|
+
}
|
|
144
|
+
}
|
|
63
145
|
}
|
|
64
146
|
|
|
65
147
|
export { Page }
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Cookies } from './cookies'
|
|
2
|
+
|
|
3
|
+
class User {
|
|
4
|
+
static get id() {
|
|
5
|
+
return Cookies.get('hello_user_id')
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
static get source() {
|
|
9
|
+
return Cookies.get('hello_user_source')
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
static remember(id, source) {
|
|
13
|
+
if (source) {
|
|
14
|
+
Cookies.set('hello_user_source', source)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
Cookies.set('hello_user_id', id)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static forget() {
|
|
21
|
+
Cookies.delete('hello_user_id')
|
|
22
|
+
Cookies.delete('hello_user_source')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static get identificationData() {
|
|
26
|
+
if (!this.id) return {}
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
id: this.id,
|
|
30
|
+
source: this.source,
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { User }
|