@hellotext/hellotext 2.3.5 → 2.3.7
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/dist/hellotext.js +1 -1
- package/index.d.ts +2 -1
- package/lib/api/acks.cjs +42 -0
- package/lib/api/acks.js +42 -0
- package/lib/api/index.cjs +6 -0
- package/lib/api/index.js +6 -0
- package/lib/hellotext.cjs +11 -8
- package/lib/hellotext.js +12 -7
- package/lib/models/fingerprint.cjs +89 -0
- package/lib/models/fingerprint.js +101 -0
- package/lib/models/index.cjs +7 -0
- package/lib/models/index.js +1 -0
- package/lib/models/session.cjs +11 -0
- package/lib/models/session.js +10 -0
- package/lib/models/user.cjs +10 -1
- package/lib/models/user.js +10 -1
- package/package.json +1 -1
- package/src/api/acks.js +25 -0
- package/src/api/index.js +5 -0
- package/src/hellotext.js +18 -9
- package/src/models/fingerprint.js +98 -0
- package/src/models/index.js +1 -0
- package/src/models/session.js +14 -0
- package/src/models/user.js +10 -1
package/index.d.ts
CHANGED
|
@@ -70,7 +70,8 @@ declare class Hellotext {
|
|
|
70
70
|
export declare class User {
|
|
71
71
|
static get id(): string | undefined
|
|
72
72
|
static get source(): string | undefined
|
|
73
|
-
static
|
|
73
|
+
static get fingerprint(): string | undefined
|
|
74
|
+
static remember(id: string, source?: string, fingerprint?: string): void
|
|
74
75
|
static forget(): void
|
|
75
76
|
static get identificationData(): IdentificationData | Record<string, never>
|
|
76
77
|
}
|
package/lib/api/acks.cjs
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _hellotext = _interopRequireDefault(require("../hellotext"));
|
|
8
|
+
var _core = require("../core");
|
|
9
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
10
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
11
|
+
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); } }
|
|
12
|
+
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
|
13
|
+
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
|
14
|
+
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); }
|
|
15
|
+
let AcksAPI = /*#__PURE__*/function () {
|
|
16
|
+
function AcksAPI() {
|
|
17
|
+
_classCallCheck(this, AcksAPI);
|
|
18
|
+
}
|
|
19
|
+
_createClass(AcksAPI, null, [{
|
|
20
|
+
key: "endpoint",
|
|
21
|
+
get: function () {
|
|
22
|
+
return _core.Configuration.endpoint('public/acks');
|
|
23
|
+
}
|
|
24
|
+
}, {
|
|
25
|
+
key: "send",
|
|
26
|
+
value: async function send() {
|
|
27
|
+
const payload = {
|
|
28
|
+
session: _hellotext.default.session,
|
|
29
|
+
at: new Date().toISOString()
|
|
30
|
+
};
|
|
31
|
+
fetch(this.endpoint, {
|
|
32
|
+
method: 'POST',
|
|
33
|
+
headers: _hellotext.default.headers,
|
|
34
|
+
body: JSON.stringify(payload),
|
|
35
|
+
keepalive: true
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}]);
|
|
39
|
+
return AcksAPI;
|
|
40
|
+
}();
|
|
41
|
+
var _default = AcksAPI;
|
|
42
|
+
exports.default = _default;
|
package/lib/api/acks.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
2
|
+
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
3
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
4
|
+
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); } }
|
|
5
|
+
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
|
6
|
+
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
|
7
|
+
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); }
|
|
8
|
+
import Hellotext from '../hellotext';
|
|
9
|
+
import { Configuration } from '../core';
|
|
10
|
+
var AcksAPI = /*#__PURE__*/function () {
|
|
11
|
+
function AcksAPI() {
|
|
12
|
+
_classCallCheck(this, AcksAPI);
|
|
13
|
+
}
|
|
14
|
+
_createClass(AcksAPI, null, [{
|
|
15
|
+
key: "endpoint",
|
|
16
|
+
get: function get() {
|
|
17
|
+
return Configuration.endpoint('public/acks');
|
|
18
|
+
}
|
|
19
|
+
}, {
|
|
20
|
+
key: "send",
|
|
21
|
+
value: function () {
|
|
22
|
+
var _send = _asyncToGenerator(function* () {
|
|
23
|
+
var payload = {
|
|
24
|
+
session: Hellotext.session,
|
|
25
|
+
at: new Date().toISOString()
|
|
26
|
+
};
|
|
27
|
+
fetch(this.endpoint, {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
headers: Hellotext.headers,
|
|
30
|
+
body: JSON.stringify(payload),
|
|
31
|
+
keepalive: true
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
function send() {
|
|
35
|
+
return _send.apply(this, arguments);
|
|
36
|
+
}
|
|
37
|
+
return send;
|
|
38
|
+
}()
|
|
39
|
+
}]);
|
|
40
|
+
return AcksAPI;
|
|
41
|
+
}();
|
|
42
|
+
export default AcksAPI;
|
package/lib/api/index.cjs
CHANGED
|
@@ -15,6 +15,7 @@ var _events = _interopRequireDefault(require("./events"));
|
|
|
15
15
|
var _forms = _interopRequireDefault(require("./forms"));
|
|
16
16
|
var _identifications = _interopRequireDefault(require("./identifications"));
|
|
17
17
|
var _webchats = _interopRequireDefault(require("./webchats"));
|
|
18
|
+
var _acks = _interopRequireDefault(require("./acks"));
|
|
18
19
|
var _response = require("./response");
|
|
19
20
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
20
21
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
@@ -51,6 +52,11 @@ let API = /*#__PURE__*/function () {
|
|
|
51
52
|
get: function () {
|
|
52
53
|
return _identifications.default;
|
|
53
54
|
}
|
|
55
|
+
}, {
|
|
56
|
+
key: "acks",
|
|
57
|
+
get: function () {
|
|
58
|
+
return _acks.default;
|
|
59
|
+
}
|
|
54
60
|
}]);
|
|
55
61
|
return API;
|
|
56
62
|
}();
|
package/lib/api/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import EventsAPI from './events';
|
|
|
8
8
|
import FormsAPI from './forms';
|
|
9
9
|
import IdentificationsAPI from './identifications';
|
|
10
10
|
import WebchatsAPI from './webchats';
|
|
11
|
+
import AcksAPI from './acks';
|
|
11
12
|
var API = /*#__PURE__*/function () {
|
|
12
13
|
function API() {
|
|
13
14
|
_classCallCheck(this, API);
|
|
@@ -37,6 +38,11 @@ var API = /*#__PURE__*/function () {
|
|
|
37
38
|
get: function get() {
|
|
38
39
|
return IdentificationsAPI;
|
|
39
40
|
}
|
|
41
|
+
}, {
|
|
42
|
+
key: "acks",
|
|
43
|
+
get: function get() {
|
|
44
|
+
return AcksAPI;
|
|
45
|
+
}
|
|
40
46
|
}]);
|
|
41
47
|
return API;
|
|
42
48
|
}();
|
package/lib/hellotext.cjs
CHANGED
|
@@ -28,10 +28,10 @@ let Hellotext = /*#__PURE__*/function () {
|
|
|
28
28
|
* @param { Configuration } config
|
|
29
29
|
*/
|
|
30
30
|
async function initialize(business, config) {
|
|
31
|
+
this.business = new _models.Business(business);
|
|
31
32
|
_core.Configuration.assign(config);
|
|
32
33
|
_models.Session.initialize();
|
|
33
34
|
this.page = new _models.Page();
|
|
34
|
-
this.business = new _models.Business(business);
|
|
35
35
|
this.forms = new _models.FormCollection();
|
|
36
36
|
this.query = new _models.Query();
|
|
37
37
|
if (_core.Configuration.webchat.id) {
|
|
@@ -85,7 +85,9 @@ let Hellotext = /*#__PURE__*/function () {
|
|
|
85
85
|
* @property { String } [name] - the name of the user
|
|
86
86
|
* @property { String } [source] - the platform specific identifier where this pixel is running on.
|
|
87
87
|
*
|
|
88
|
-
* Identifies a user and attaches the hello_session to the user ID
|
|
88
|
+
* Identifies a user and attaches the hello_session to the user ID.
|
|
89
|
+
* Repeated calls are skipped only when the last successful identify payload
|
|
90
|
+
* for the current session remains unchanged.
|
|
89
91
|
* @param { String } externalId - the user ID
|
|
90
92
|
* @param { IdentificationOptions } options - the options for the identification
|
|
91
93
|
* @returns {Promise<Response>}
|
|
@@ -93,11 +95,12 @@ let Hellotext = /*#__PURE__*/function () {
|
|
|
93
95
|
}, {
|
|
94
96
|
key: "identify",
|
|
95
97
|
value: async function identify(externalId, options = {}) {
|
|
96
|
-
|
|
98
|
+
const fingerprint = await _models.Fingerprint.generate(this.session, externalId, options);
|
|
99
|
+
if (_models.Fingerprint.matches(_models.User.fingerprint, fingerprint)) {
|
|
97
100
|
return new _api.Response(true, {
|
|
98
|
-
json: async () => {
|
|
99
|
-
already_identified: true
|
|
100
|
-
}
|
|
101
|
+
json: async () => ({
|
|
102
|
+
already_identified: true
|
|
103
|
+
})
|
|
101
104
|
});
|
|
102
105
|
}
|
|
103
106
|
const response = await _api.default.identifications.create({
|
|
@@ -105,7 +108,7 @@ let Hellotext = /*#__PURE__*/function () {
|
|
|
105
108
|
...options
|
|
106
109
|
});
|
|
107
110
|
if (response.succeeded) {
|
|
108
|
-
_models.User.remember(externalId, options.source);
|
|
111
|
+
_models.User.remember(externalId, options.source, fingerprint);
|
|
109
112
|
}
|
|
110
113
|
return response;
|
|
111
114
|
}
|
|
@@ -167,7 +170,7 @@ let Hellotext = /*#__PURE__*/function () {
|
|
|
167
170
|
}, {
|
|
168
171
|
key: "notInitialized",
|
|
169
172
|
get: function () {
|
|
170
|
-
return this.business.id === undefined;
|
|
173
|
+
return !this.business || this.business.id === undefined;
|
|
171
174
|
}
|
|
172
175
|
}, {
|
|
173
176
|
key: "headers",
|
package/lib/hellotext.js
CHANGED
|
@@ -10,7 +10,7 @@ function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typ
|
|
|
10
10
|
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); }
|
|
11
11
|
import { Configuration, Event } from './core';
|
|
12
12
|
import API, { Response } from './api';
|
|
13
|
-
import { Business, FormCollection, Page, Query, Session, User, Webchat } from './models';
|
|
13
|
+
import { Business, Fingerprint, FormCollection, Page, Query, Session, User, Webchat } from './models';
|
|
14
14
|
import { NotInitializedError } from './errors';
|
|
15
15
|
var Hellotext = /*#__PURE__*/function () {
|
|
16
16
|
function Hellotext() {
|
|
@@ -26,10 +26,10 @@ var Hellotext = /*#__PURE__*/function () {
|
|
|
26
26
|
*/
|
|
27
27
|
function () {
|
|
28
28
|
var _initialize = _asyncToGenerator(function* (business, config) {
|
|
29
|
+
this.business = new Business(business);
|
|
29
30
|
Configuration.assign(config);
|
|
30
31
|
Session.initialize();
|
|
31
32
|
this.page = new Page();
|
|
32
|
-
this.business = new Business(business);
|
|
33
33
|
this.forms = new FormCollection();
|
|
34
34
|
this.query = new Query();
|
|
35
35
|
if (Configuration.webchat.id) {
|
|
@@ -85,7 +85,9 @@ var Hellotext = /*#__PURE__*/function () {
|
|
|
85
85
|
* @property { String } [name] - the name of the user
|
|
86
86
|
* @property { String } [source] - the platform specific identifier where this pixel is running on.
|
|
87
87
|
*
|
|
88
|
-
* Identifies a user and attaches the hello_session to the user ID
|
|
88
|
+
* Identifies a user and attaches the hello_session to the user ID.
|
|
89
|
+
* Repeated calls are skipped only when the last successful identify payload
|
|
90
|
+
* for the current session remains unchanged.
|
|
89
91
|
* @param { String } externalId - the user ID
|
|
90
92
|
* @param { IdentificationOptions } options - the options for the identification
|
|
91
93
|
* @returns {Promise<Response>}
|
|
@@ -95,11 +97,14 @@ var Hellotext = /*#__PURE__*/function () {
|
|
|
95
97
|
value: function () {
|
|
96
98
|
var _identify = _asyncToGenerator(function* (externalId) {
|
|
97
99
|
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
98
|
-
|
|
100
|
+
var fingerprint = yield Fingerprint.generate(this.session, externalId, options);
|
|
101
|
+
if (Fingerprint.matches(User.fingerprint, fingerprint)) {
|
|
99
102
|
return new Response(true, {
|
|
100
103
|
json: function () {
|
|
101
104
|
var _json = _asyncToGenerator(function* () {
|
|
102
|
-
|
|
105
|
+
return {
|
|
106
|
+
already_identified: true
|
|
107
|
+
};
|
|
103
108
|
});
|
|
104
109
|
function json() {
|
|
105
110
|
return _json.apply(this, arguments);
|
|
@@ -112,7 +117,7 @@ var Hellotext = /*#__PURE__*/function () {
|
|
|
112
117
|
user_id: externalId
|
|
113
118
|
}, options));
|
|
114
119
|
if (response.succeeded) {
|
|
115
|
-
User.remember(externalId, options.source);
|
|
120
|
+
User.remember(externalId, options.source, fingerprint);
|
|
116
121
|
}
|
|
117
122
|
return response;
|
|
118
123
|
});
|
|
@@ -178,7 +183,7 @@ var Hellotext = /*#__PURE__*/function () {
|
|
|
178
183
|
}, {
|
|
179
184
|
key: "notInitialized",
|
|
180
185
|
get: function get() {
|
|
181
|
-
return this.business.id === undefined;
|
|
186
|
+
return !this.business || this.business.id === undefined;
|
|
182
187
|
}
|
|
183
188
|
}, {
|
|
184
189
|
key: "headers",
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.Fingerprint = void 0;
|
|
7
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
8
|
+
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); } }
|
|
9
|
+
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
|
10
|
+
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
|
11
|
+
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); }
|
|
12
|
+
function normalizeValue(value) {
|
|
13
|
+
// Collapse "missing" values so callers can add optional fields incrementally
|
|
14
|
+
// without changing the fingerprint when the effective payload is the same.
|
|
15
|
+
if (value === null || value === undefined) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
if (typeof value === 'string') {
|
|
19
|
+
const trimmedValue = value.trim();
|
|
20
|
+
|
|
21
|
+
// Treat blank strings as absent values so "" and an omitted field compare equally.
|
|
22
|
+
return trimmedValue === '' ? undefined : trimmedValue;
|
|
23
|
+
}
|
|
24
|
+
if (Array.isArray(value)) {
|
|
25
|
+
// Preserve array order because, unlike object keys, caller-provided sequence can be meaningful.
|
|
26
|
+
return value.map(item => normalizeValue(item)).filter(item => item !== undefined);
|
|
27
|
+
}
|
|
28
|
+
if (value instanceof Date) {
|
|
29
|
+
// Serialize dates into a stable primitive so equivalent timestamps fingerprint the same way.
|
|
30
|
+
return value.toISOString();
|
|
31
|
+
}
|
|
32
|
+
if (typeof value === 'object') {
|
|
33
|
+
// Canonicalize object shape by sorting keys recursively, so key placement never affects equality.
|
|
34
|
+
const normalizedObject = Object.keys(value).sort((leftKey, rightKey) => leftKey.localeCompare(rightKey)).reduce((result, key) => {
|
|
35
|
+
const normalizedChild = normalizeValue(value[key]);
|
|
36
|
+
if (normalizedChild !== undefined) {
|
|
37
|
+
result[key] = normalizedChild;
|
|
38
|
+
}
|
|
39
|
+
return result;
|
|
40
|
+
}, {});
|
|
41
|
+
return Object.keys(normalizedObject).length > 0 ? normalizedObject : undefined;
|
|
42
|
+
}
|
|
43
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
function serializePayload(session, userId, options = {}) {
|
|
49
|
+
const normalizedPayload = normalizeValue({
|
|
50
|
+
session,
|
|
51
|
+
user_id: userId,
|
|
52
|
+
...options
|
|
53
|
+
}) || {};
|
|
54
|
+
return JSON.stringify(normalizedPayload);
|
|
55
|
+
}
|
|
56
|
+
function fallbackHash(value) {
|
|
57
|
+
let hash = 5381;
|
|
58
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
59
|
+
hash = hash * 33 ^ value.charCodeAt(index);
|
|
60
|
+
}
|
|
61
|
+
return `v1:${(hash >>> 0).toString(16)}`;
|
|
62
|
+
}
|
|
63
|
+
async function sha256(value) {
|
|
64
|
+
var _globalThis$crypto;
|
|
65
|
+
if (!((_globalThis$crypto = globalThis.crypto) !== null && _globalThis$crypto !== void 0 && _globalThis$crypto.subtle) || typeof TextEncoder === 'undefined') {
|
|
66
|
+
return fallbackHash(value);
|
|
67
|
+
}
|
|
68
|
+
const digest = await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(value));
|
|
69
|
+
const hex = Array.from(new Uint8Array(digest)).map(byte => byte.toString(16).padStart(2, '0')).join('');
|
|
70
|
+
return `v1:${hex}`;
|
|
71
|
+
}
|
|
72
|
+
let Fingerprint = /*#__PURE__*/function () {
|
|
73
|
+
function Fingerprint() {
|
|
74
|
+
_classCallCheck(this, Fingerprint);
|
|
75
|
+
}
|
|
76
|
+
_createClass(Fingerprint, null, [{
|
|
77
|
+
key: "matches",
|
|
78
|
+
value: function matches(storedFingerprint, fingerprint) {
|
|
79
|
+
return !!storedFingerprint && storedFingerprint === fingerprint;
|
|
80
|
+
}
|
|
81
|
+
}, {
|
|
82
|
+
key: "generate",
|
|
83
|
+
value: async function generate(session, userId, options = {}) {
|
|
84
|
+
return await sha256(serializePayload(session, userId, options));
|
|
85
|
+
}
|
|
86
|
+
}]);
|
|
87
|
+
return Fingerprint;
|
|
88
|
+
}();
|
|
89
|
+
exports.Fingerprint = Fingerprint;
|
|
@@ -0,0 +1,101 @@
|
|
|
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 asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
5
|
+
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
6
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
7
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
8
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
9
|
+
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
|
10
|
+
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); }
|
|
11
|
+
function normalizeValue(value) {
|
|
12
|
+
// Collapse "missing" values so callers can add optional fields incrementally
|
|
13
|
+
// without changing the fingerprint when the effective payload is the same.
|
|
14
|
+
if (value === null || value === undefined) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
if (typeof value === 'string') {
|
|
18
|
+
var trimmedValue = value.trim();
|
|
19
|
+
|
|
20
|
+
// Treat blank strings as absent values so "" and an omitted field compare equally.
|
|
21
|
+
return trimmedValue === '' ? undefined : trimmedValue;
|
|
22
|
+
}
|
|
23
|
+
if (Array.isArray(value)) {
|
|
24
|
+
// Preserve array order because, unlike object keys, caller-provided sequence can be meaningful.
|
|
25
|
+
return value.map(item => normalizeValue(item)).filter(item => item !== undefined);
|
|
26
|
+
}
|
|
27
|
+
if (value instanceof Date) {
|
|
28
|
+
// Serialize dates into a stable primitive so equivalent timestamps fingerprint the same way.
|
|
29
|
+
return value.toISOString();
|
|
30
|
+
}
|
|
31
|
+
if (typeof value === 'object') {
|
|
32
|
+
// Canonicalize object shape by sorting keys recursively, so key placement never affects equality.
|
|
33
|
+
var normalizedObject = Object.keys(value).sort((leftKey, rightKey) => leftKey.localeCompare(rightKey)).reduce((result, key) => {
|
|
34
|
+
var normalizedChild = normalizeValue(value[key]);
|
|
35
|
+
if (normalizedChild !== undefined) {
|
|
36
|
+
result[key] = normalizedChild;
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}, {});
|
|
40
|
+
return Object.keys(normalizedObject).length > 0 ? normalizedObject : undefined;
|
|
41
|
+
}
|
|
42
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
function serializePayload(session, userId) {
|
|
48
|
+
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
49
|
+
var normalizedPayload = normalizeValue(_objectSpread({
|
|
50
|
+
session,
|
|
51
|
+
user_id: userId
|
|
52
|
+
}, options)) || {};
|
|
53
|
+
return JSON.stringify(normalizedPayload);
|
|
54
|
+
}
|
|
55
|
+
function fallbackHash(value) {
|
|
56
|
+
var hash = 5381;
|
|
57
|
+
for (var index = 0; index < value.length; index += 1) {
|
|
58
|
+
hash = hash * 33 ^ value.charCodeAt(index);
|
|
59
|
+
}
|
|
60
|
+
return "v1:".concat((hash >>> 0).toString(16));
|
|
61
|
+
}
|
|
62
|
+
function sha256(_x) {
|
|
63
|
+
return _sha.apply(this, arguments);
|
|
64
|
+
}
|
|
65
|
+
function _sha() {
|
|
66
|
+
_sha = _asyncToGenerator(function* (value) {
|
|
67
|
+
var _globalThis$crypto;
|
|
68
|
+
if (!((_globalThis$crypto = globalThis.crypto) !== null && _globalThis$crypto !== void 0 && _globalThis$crypto.subtle) || typeof TextEncoder === 'undefined') {
|
|
69
|
+
return fallbackHash(value);
|
|
70
|
+
}
|
|
71
|
+
var digest = yield globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(value));
|
|
72
|
+
var hex = Array.from(new Uint8Array(digest)).map(byte => byte.toString(16).padStart(2, '0')).join('');
|
|
73
|
+
return "v1:".concat(hex);
|
|
74
|
+
});
|
|
75
|
+
return _sha.apply(this, arguments);
|
|
76
|
+
}
|
|
77
|
+
var Fingerprint = /*#__PURE__*/function () {
|
|
78
|
+
function Fingerprint() {
|
|
79
|
+
_classCallCheck(this, Fingerprint);
|
|
80
|
+
}
|
|
81
|
+
_createClass(Fingerprint, null, [{
|
|
82
|
+
key: "matches",
|
|
83
|
+
value: function matches(storedFingerprint, fingerprint) {
|
|
84
|
+
return !!storedFingerprint && storedFingerprint === fingerprint;
|
|
85
|
+
}
|
|
86
|
+
}, {
|
|
87
|
+
key: "generate",
|
|
88
|
+
value: function () {
|
|
89
|
+
var _generate = _asyncToGenerator(function* (session, userId) {
|
|
90
|
+
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
91
|
+
return yield sha256(serializePayload(session, userId, options));
|
|
92
|
+
});
|
|
93
|
+
function generate(_x2, _x3) {
|
|
94
|
+
return _generate.apply(this, arguments);
|
|
95
|
+
}
|
|
96
|
+
return generate;
|
|
97
|
+
}()
|
|
98
|
+
}]);
|
|
99
|
+
return Fingerprint;
|
|
100
|
+
}();
|
|
101
|
+
export { Fingerprint };
|
package/lib/models/index.cjs
CHANGED
|
@@ -15,6 +15,12 @@ Object.defineProperty(exports, "Cookies", {
|
|
|
15
15
|
return _cookies.Cookies;
|
|
16
16
|
}
|
|
17
17
|
});
|
|
18
|
+
Object.defineProperty(exports, "Fingerprint", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () {
|
|
21
|
+
return _fingerprint.Fingerprint;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
18
24
|
Object.defineProperty(exports, "Form", {
|
|
19
25
|
enumerable: true,
|
|
20
26
|
get: function () {
|
|
@@ -65,6 +71,7 @@ Object.defineProperty(exports, "Webchat", {
|
|
|
65
71
|
});
|
|
66
72
|
var _business = require("./business");
|
|
67
73
|
var _cookies = require("./cookies");
|
|
74
|
+
var _fingerprint = require("./fingerprint");
|
|
68
75
|
var _form = require("./form");
|
|
69
76
|
var _form_collection = require("./form_collection");
|
|
70
77
|
var _page = require("./page");
|
package/lib/models/index.js
CHANGED
package/lib/models/session.cjs
CHANGED
|
@@ -7,6 +7,8 @@ exports.Session = void 0;
|
|
|
7
7
|
var _core = require("../core");
|
|
8
8
|
var _cookies = require("./cookies");
|
|
9
9
|
var _query2 = require("./query");
|
|
10
|
+
var _api = _interopRequireDefault(require("../api"));
|
|
11
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
10
12
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
11
13
|
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); } }
|
|
12
14
|
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
|
@@ -27,8 +29,17 @@ let Session = /*#__PURE__*/function () {
|
|
|
27
29
|
return _classPrivateFieldLooseBase(this, _session)[_session];
|
|
28
30
|
},
|
|
29
31
|
set: function (value) {
|
|
32
|
+
const oldSession = _cookies.Cookies.get('hello_session');
|
|
30
33
|
_classPrivateFieldLooseBase(this, _session)[_session] = value;
|
|
31
34
|
_cookies.Cookies.set('hello_session', value);
|
|
35
|
+
if (oldSession !== value) {
|
|
36
|
+
_cookies.Cookies.delete('hello_session_ack_at');
|
|
37
|
+
}
|
|
38
|
+
if (!_cookies.Cookies.get('hello_session_ack_at')) {
|
|
39
|
+
_api.default.acks.send();
|
|
40
|
+
_cookies.Cookies.set('hello_session_ack_at', new Date().toISOString());
|
|
41
|
+
}
|
|
42
|
+
return _classPrivateFieldLooseBase(this, _session)[_session];
|
|
32
43
|
}
|
|
33
44
|
}, {
|
|
34
45
|
key: "initialize",
|
package/lib/models/session.js
CHANGED
|
@@ -9,6 +9,7 @@ function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + n
|
|
|
9
9
|
import { Configuration } from '../core';
|
|
10
10
|
import { Cookies } from './cookies';
|
|
11
11
|
import { Query } from './query';
|
|
12
|
+
import API from '../api';
|
|
12
13
|
var _session = /*#__PURE__*/_classPrivateFieldLooseKey("session");
|
|
13
14
|
var _query = /*#__PURE__*/_classPrivateFieldLooseKey("query");
|
|
14
15
|
var Session = /*#__PURE__*/function () {
|
|
@@ -21,8 +22,17 @@ var Session = /*#__PURE__*/function () {
|
|
|
21
22
|
return _classPrivateFieldLooseBase(this, _session)[_session];
|
|
22
23
|
},
|
|
23
24
|
set: function set(value) {
|
|
25
|
+
var oldSession = Cookies.get('hello_session');
|
|
24
26
|
_classPrivateFieldLooseBase(this, _session)[_session] = value;
|
|
25
27
|
Cookies.set('hello_session', value);
|
|
28
|
+
if (oldSession !== value) {
|
|
29
|
+
Cookies.delete('hello_session_ack_at');
|
|
30
|
+
}
|
|
31
|
+
if (!Cookies.get('hello_session_ack_at')) {
|
|
32
|
+
API.acks.send();
|
|
33
|
+
Cookies.set('hello_session_ack_at', new Date().toISOString());
|
|
34
|
+
}
|
|
35
|
+
return _classPrivateFieldLooseBase(this, _session)[_session];
|
|
26
36
|
}
|
|
27
37
|
}, {
|
|
28
38
|
key: "initialize",
|
package/lib/models/user.cjs
CHANGED
|
@@ -24,12 +24,20 @@ let User = /*#__PURE__*/function () {
|
|
|
24
24
|
get: function () {
|
|
25
25
|
return _cookies.Cookies.get('hello_user_source');
|
|
26
26
|
}
|
|
27
|
+
}, {
|
|
28
|
+
key: "fingerprint",
|
|
29
|
+
get: function () {
|
|
30
|
+
return _cookies.Cookies.get('hello_user_identification_hash');
|
|
31
|
+
}
|
|
27
32
|
}, {
|
|
28
33
|
key: "remember",
|
|
29
|
-
value: function remember(id, source) {
|
|
34
|
+
value: function remember(id, source, fingerprint) {
|
|
30
35
|
if (source) {
|
|
31
36
|
_cookies.Cookies.set('hello_user_source', source);
|
|
32
37
|
}
|
|
38
|
+
if (fingerprint) {
|
|
39
|
+
_cookies.Cookies.set('hello_user_identification_hash', fingerprint);
|
|
40
|
+
}
|
|
33
41
|
_cookies.Cookies.set('hello_user_id', id);
|
|
34
42
|
}
|
|
35
43
|
}, {
|
|
@@ -37,6 +45,7 @@ let User = /*#__PURE__*/function () {
|
|
|
37
45
|
value: function forget() {
|
|
38
46
|
_cookies.Cookies.delete('hello_user_id');
|
|
39
47
|
_cookies.Cookies.delete('hello_user_source');
|
|
48
|
+
_cookies.Cookies.delete('hello_user_identification_hash');
|
|
40
49
|
}
|
|
41
50
|
}, {
|
|
42
51
|
key: "identificationData",
|
package/lib/models/user.js
CHANGED
|
@@ -18,12 +18,20 @@ var User = /*#__PURE__*/function () {
|
|
|
18
18
|
get: function get() {
|
|
19
19
|
return Cookies.get('hello_user_source');
|
|
20
20
|
}
|
|
21
|
+
}, {
|
|
22
|
+
key: "fingerprint",
|
|
23
|
+
get: function get() {
|
|
24
|
+
return Cookies.get('hello_user_identification_hash');
|
|
25
|
+
}
|
|
21
26
|
}, {
|
|
22
27
|
key: "remember",
|
|
23
|
-
value: function remember(id, source) {
|
|
28
|
+
value: function remember(id, source, fingerprint) {
|
|
24
29
|
if (source) {
|
|
25
30
|
Cookies.set('hello_user_source', source);
|
|
26
31
|
}
|
|
32
|
+
if (fingerprint) {
|
|
33
|
+
Cookies.set('hello_user_identification_hash', fingerprint);
|
|
34
|
+
}
|
|
27
35
|
Cookies.set('hello_user_id', id);
|
|
28
36
|
}
|
|
29
37
|
}, {
|
|
@@ -31,6 +39,7 @@ var User = /*#__PURE__*/function () {
|
|
|
31
39
|
value: function forget() {
|
|
32
40
|
Cookies.delete('hello_user_id');
|
|
33
41
|
Cookies.delete('hello_user_source');
|
|
42
|
+
Cookies.delete('hello_user_identification_hash');
|
|
34
43
|
}
|
|
35
44
|
}, {
|
|
36
45
|
key: "identificationData",
|
package/package.json
CHANGED
package/src/api/acks.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import Hellotext from '../hellotext'
|
|
2
|
+
|
|
3
|
+
import { Configuration } from '../core'
|
|
4
|
+
|
|
5
|
+
class AcksAPI {
|
|
6
|
+
static get endpoint() {
|
|
7
|
+
return Configuration.endpoint('public/acks')
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
static async send() {
|
|
11
|
+
const payload = {
|
|
12
|
+
session: Hellotext.session,
|
|
13
|
+
at: new Date().toISOString(),
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
fetch(this.endpoint, {
|
|
17
|
+
method: 'POST',
|
|
18
|
+
headers: Hellotext.headers,
|
|
19
|
+
body: JSON.stringify(payload),
|
|
20
|
+
keepalive: true,
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default AcksAPI
|
package/src/api/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import EventsAPI from './events'
|
|
|
3
3
|
import FormsAPI from './forms'
|
|
4
4
|
import IdentificationsAPI from './identifications'
|
|
5
5
|
import WebchatsAPI from './webchats'
|
|
6
|
+
import AcksAPI from './acks'
|
|
6
7
|
|
|
7
8
|
export default class API {
|
|
8
9
|
static get businesses() {
|
|
@@ -24,6 +25,10 @@ export default class API {
|
|
|
24
25
|
static get identifications() {
|
|
25
26
|
return IdentificationsAPI
|
|
26
27
|
}
|
|
28
|
+
|
|
29
|
+
static get acks() {
|
|
30
|
+
return AcksAPI
|
|
31
|
+
}
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
export { Response } from './response'
|