@subiz/widget-v4 4.1.212 → 4.1.214
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/api.md +57 -0
- package/compiled_src/lib.js +52 -30
- package/compiled_src/store/api.js +3 -0
- package/compiled_src/store/store.js +16 -0
- package/dist/.HASH +1 -0
- package/dist/{1a0f6f3ff1a.27.js → 1a12a372880.27.js} +1 -1
- package/dist/{1a0f6f3ff1a.339.js → 1a12a372880.339.js} +1 -1
- package/dist/{1a0f6f3ff1a.459.js → 1a12a372880.459.js} +1 -1
- package/dist/{1a0f6f3ff1a.app.js → 1a12a372880.app.js} +1 -1
- package/dist/{1a0f6f3ff1a.chat.html → 1a12a372880.chat.html} +1 -1
- package/dist/{1a0f6f3ff1a.chat.js → 1a12a372880.chat.js} +1 -1
- package/dist/{1a0f6f3ff1a.form.html → 1a12a372880.form.html} +1 -1
- package/dist/1a12a372880.form.js +1 -0
- package/dist/{1a0f6f3ff1a.index.html → 1a12a372880.index.html} +1 -1
- package/dist/{1a0f6f3ff1a.library.html → 1a12a372880.library.html} +1 -1
- package/dist/{1a0f6f3ff1a.library.js → 1a12a372880.library.js} +1 -1
- package/dist/{1a0f6f3ff1a.preview.html → 1a12a372880.preview.html} +1 -1
- package/dist/{1a0f6f3ff1a.preview.js → 1a12a372880.preview.js} +1 -1
- package/dist/{1a0f6f3ff1a.webview.js → 1a12a372880.webview.js} +1 -1
- package/dist/27.js +1 -1
- package/dist/459.js +1 -1
- package/dist/screenshoot.js +70 -29
- package/package.json +3 -3
- package/src/lib.js +54 -24
- package/src/store/api.js +9 -0
- package/src/store/store.js +18 -0
- package/dist/1a0f6f3ff1a.form.js +0 -1
- package/dist/HASH +0 -1
- /package/dist/{1a0f6f3ff1a.127.js → 1a12a372880.127.js} +0 -0
- /package/dist/{1a0f6f3ff1a.132.js → 1a12a372880.132.js} +0 -0
- /package/dist/{1a0f6f3ff1a.185.js → 1a12a372880.185.js} +0 -0
- /package/dist/{1a0f6f3ff1a.278.js → 1a12a372880.278.js} +0 -0
- /package/dist/{1a0f6f3ff1a.490.js → 1a12a372880.490.js} +0 -0
package/dist/screenshoot.js
CHANGED
|
@@ -4586,6 +4586,9 @@ let trackResponseFn = (param, done) => {
|
|
|
4586
4586
|
_defineProperty(this, "getConversation", cid => this.apireq.get(`/accounts/${this.accid}/conversations/${cid}`));
|
|
4587
4587
|
_defineProperty(this, "getLocaleContent", locale => this.apireq.get(`/accounts/${this.accid}/languages/${locale}`));
|
|
4588
4588
|
_defineProperty(this, "sendEvent", data => this.apireq.addQuery('shorten', true).addQuery('x-referer', util/* default */.A.get(location, 'origin', '')).post(`/accounts/${this.accid}/users/${this.uid}/events`, data));
|
|
4589
|
+
_defineProperty(this, "websiteFormRequest", () => this.apireq.setBaseUrl(this.ApiURL.replace(/\/4\.[01]\/?$/, '/4.1')));
|
|
4590
|
+
_defineProperty(this, "submitWebsiteForm", (formid, payload) => this.websiteFormRequest().post(`/accounts/${this.accid}/external-forms/${encodeURIComponent(formid)}/submissions`, payload));
|
|
4591
|
+
_defineProperty(this, "getWebsiteFormStatus", formid => this.websiteFormRequest().get(`/accounts/${this.accid}/external-forms/${encodeURIComponent(formid)}/status`));
|
|
4589
4592
|
_defineProperty(this, "submitForm", (form_id, page_url, device, data, user) => this.apireq.addQuery('x-referer', util/* default */.A.get(location, 'origin', '')).post(`/accounts/${this.accid}/users/${this.uid}/events`, {
|
|
4590
4593
|
by: {
|
|
4591
4594
|
id: this.uid,
|
|
@@ -5721,6 +5724,9 @@ cookie.defaults.expires = 3650;
|
|
|
5721
5724
|
let store_memlocalStorage = (inmem_localstorage_namespaceFn());
|
|
5722
5725
|
const MAX_CONVO = 20;
|
|
5723
5726
|
const page_loaded = util/* default */.A.Date.now();
|
|
5727
|
+
const CHAT_STATE_RESET_AFTER_MS = 24 * 60 * 60 * 1000;
|
|
5728
|
+
const CHAT_STATE_CYCLE_STARTED_AT = 'chat_state_cycle_started_at';
|
|
5729
|
+
const RESETTABLE_CHAT_STATE_KEYS = ['lastview', 'light_convos', 'bot_run_logs', 'close_preview_at'];
|
|
5724
5730
|
class Store {
|
|
5725
5731
|
constructor(ApiURL, FileURL, RealtimeURL, inmem, fakeapi) {
|
|
5726
5732
|
store_defineProperty(this, "inmem", false);
|
|
@@ -6394,6 +6400,18 @@ class Store {
|
|
|
6394
6400
|
}
|
|
6395
6401
|
this.kv.put('lastview', lastView);
|
|
6396
6402
|
}
|
|
6403
|
+
resetChatStateIfExpired() {
|
|
6404
|
+
const now = this.now();
|
|
6405
|
+
const cycleStartedAt = this.kv.match(CHAT_STATE_CYCLE_STARTED_AT);
|
|
6406
|
+
if (!Number.isFinite(cycleStartedAt) || cycleStartedAt <= 0 || cycleStartedAt > now) {
|
|
6407
|
+
this.kv.put(CHAT_STATE_CYCLE_STARTED_AT, now);
|
|
6408
|
+
return false;
|
|
6409
|
+
}
|
|
6410
|
+
if (now - cycleStartedAt < CHAT_STATE_RESET_AFTER_MS) return false;
|
|
6411
|
+
util/* default */.A.map(RESETTABLE_CHAT_STATE_KEYS, key => this.kv.del(key));
|
|
6412
|
+
this.kv.put(CHAT_STATE_CYCLE_STARTED_AT, now);
|
|
6413
|
+
return true;
|
|
6414
|
+
}
|
|
6397
6415
|
async _initResource(accid, auth, realtimep) {
|
|
6398
6416
|
this.g_sync = {};
|
|
6399
6417
|
let user = await this.api.setAuth(auth);
|
|
@@ -6404,6 +6422,7 @@ class Store {
|
|
|
6404
6422
|
}
|
|
6405
6423
|
this.readyGate.close();
|
|
6406
6424
|
accid = (accid || '').trim();
|
|
6425
|
+
this.resetChatStateIfExpired();
|
|
6407
6426
|
let lightConvos = this.kv.match('light_convos') || {};
|
|
6408
6427
|
this.lightConvos = {};
|
|
6409
6428
|
util/* default */.A.map(lightConvos, convo => {
|
|
@@ -14133,8 +14152,9 @@ class lib_Widget {
|
|
|
14133
14152
|
this.impressionTokens[formid] = impressiontoken;
|
|
14134
14153
|
return impressiontoken;
|
|
14135
14154
|
}
|
|
14136
|
-
async submitForm(formid, values) {
|
|
14137
|
-
if (!formid || !values)
|
|
14155
|
+
async submitForm(formid, values, options = {}) {
|
|
14156
|
+
if (!formid || !values) throw new TypeError('submitForm requires a form ID and fields');
|
|
14157
|
+
await this.store.readyGate.entry();
|
|
14138
14158
|
if (util/* default */.A.startsWith(formid, 'cp')) {
|
|
14139
14159
|
let storeuser = util/* default */.A.cloneDeep(this.store.matchUser());
|
|
14140
14160
|
let user = {
|
|
@@ -14165,33 +14185,13 @@ class lib_Widget {
|
|
|
14165
14185
|
this.store.setUserDiff(user);
|
|
14166
14186
|
return this.store.sendConversion(formid, values, impressiontoken);
|
|
14167
14187
|
}
|
|
14168
|
-
|
|
14169
|
-
|
|
14170
|
-
|
|
14171
|
-
|
|
14172
|
-
|
|
14173
|
-
|
|
14174
|
-
|
|
14175
|
-
return this.store.startConvo({
|
|
14176
|
-
subject: 'Khách điền form',
|
|
14177
|
-
touchpoint: {
|
|
14178
|
-
channel: 'form',
|
|
14179
|
-
source: window.location.hostname,
|
|
14180
|
-
id: Date.now() + '_' + util/* default */.A.randomString()
|
|
14181
|
-
},
|
|
14182
|
-
form_submission: {
|
|
14183
|
-
id: formid,
|
|
14184
|
-
values: values
|
|
14185
|
-
},
|
|
14186
|
-
message_sent: {
|
|
14187
|
-
data: {
|
|
14188
|
-
message: {
|
|
14189
|
-
format: 'plaintext',
|
|
14190
|
-
text: text
|
|
14191
|
-
}
|
|
14192
|
-
}
|
|
14193
|
-
}
|
|
14194
|
-
});
|
|
14188
|
+
const payload = formRequest(formid, values, options);
|
|
14189
|
+
return formResult(await this.store.api.submitWebsiteForm(formid, payload), 'submit');
|
|
14190
|
+
}
|
|
14191
|
+
async getFormStatus(formid) {
|
|
14192
|
+
validateWebsiteFormId(formid);
|
|
14193
|
+
await this.store.readyGate.entry();
|
|
14194
|
+
return formResult(await this.store.api.getWebsiteFormStatus(formid), 'status');
|
|
14195
14195
|
}
|
|
14196
14196
|
startConvo(req) {
|
|
14197
14197
|
req = util/* default */.A.cloneDeep(req) || {};
|
|
@@ -14304,6 +14304,47 @@ class lib_Widget {
|
|
|
14304
14304
|
}
|
|
14305
14305
|
window.__sbz_WIDGET = lib_Widget;
|
|
14306
14306
|
/* harmony default export */ const lib = (lib_Widget);
|
|
14307
|
+
function validateWebsiteFormId(formid) {
|
|
14308
|
+
if (typeof formid !== 'string' || !/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/.test(formid)) {
|
|
14309
|
+
throw new TypeError('Form ID must contain 1-64 letters, digits, underscores or hyphens');
|
|
14310
|
+
}
|
|
14311
|
+
if (formid.startsWith('cp')) throw new TypeError('Form IDs starting with cp are reserved for Subiz campaigns');
|
|
14312
|
+
}
|
|
14313
|
+
function formRequest(formid, fields, options = {}) {
|
|
14314
|
+
validateWebsiteFormId(formid);
|
|
14315
|
+
if (!Array.isArray(fields) || !fields.length) throw new TypeError('Form fields must be a nonempty array');
|
|
14316
|
+
if (!options || typeof options !== 'object' || Array.isArray(options)) throw new TypeError('Form options must be an object');
|
|
14317
|
+
return {
|
|
14318
|
+
form_name: options.name === undefined ? formid : options.name,
|
|
14319
|
+
page_url: options.page_url === undefined ? window.location.href : options.page_url,
|
|
14320
|
+
submission_id: options.submission_id === undefined ? util/* default */.A.randomString(36) : options.submission_id,
|
|
14321
|
+
values: fields.map(field => {
|
|
14322
|
+
if (!field || typeof field.key !== 'string') throw new TypeError('Each field requires a key');
|
|
14323
|
+
const value = field.value;
|
|
14324
|
+
if (value != null && !['string', 'number', 'boolean'].includes(typeof value)) {
|
|
14325
|
+
throw new TypeError('Form field values must be strings, numbers or booleans');
|
|
14326
|
+
}
|
|
14327
|
+
return {
|
|
14328
|
+
key: field.key,
|
|
14329
|
+
label: field.label === undefined ? field.key : field.label,
|
|
14330
|
+
value: value == null ? '' : String(value)
|
|
14331
|
+
};
|
|
14332
|
+
})
|
|
14333
|
+
};
|
|
14334
|
+
}
|
|
14335
|
+
function formResult(response, operation) {
|
|
14336
|
+
const body = response && response.body;
|
|
14337
|
+
const detail = response && response.error || body && body.error;
|
|
14338
|
+
const status = response && response.code;
|
|
14339
|
+
const valid = operation === 'submit' ? body && typeof body.conversation_id === 'string' && body.conversation_id && typeof body.submission_id === 'string' && body.submission_id : body && typeof body.submitted === 'boolean' && typeof body.status === 'string';
|
|
14340
|
+
if (detail || !(status >= 200 && status < 300) || !valid) {
|
|
14341
|
+
const error = new Error('Subiz form request failed');
|
|
14342
|
+
error.code = detail && detail.code || 'form_request_failed';
|
|
14343
|
+
error.status = status;
|
|
14344
|
+
throw error;
|
|
14345
|
+
}
|
|
14346
|
+
return body;
|
|
14347
|
+
}
|
|
14307
14348
|
;// ./screenshoot.js
|
|
14308
14349
|
|
|
14309
14350
|
let accid = 'acqsulrowbxiugvginhw';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@subiz/widget-v4",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.214",
|
|
4
4
|
"main": "./compiled_src/lib.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "npx webpack serve --config webpack.dev.js",
|
|
@@ -32,10 +32,10 @@
|
|
|
32
32
|
"@babel/preset-env": "^7.29.7",
|
|
33
33
|
"babel-loader": "^10.1.1",
|
|
34
34
|
"babel-plugin-syntax-jsx": "^6.18.0",
|
|
35
|
-
"eslint": "^10.
|
|
35
|
+
"eslint": "^10.12.0",
|
|
36
36
|
"globals": "^17.13.0",
|
|
37
37
|
"html-webpack-plugin": "^5.6.8",
|
|
38
|
-
"jsdom": "^30.1.
|
|
38
|
+
"jsdom": "^30.1.2",
|
|
39
39
|
"puppeteer": "^25.12.0",
|
|
40
40
|
"tape": "^5.10.2",
|
|
41
41
|
"webpack": "^5.111.1",
|
package/src/lib.js
CHANGED
|
@@ -208,8 +208,9 @@ class Widget {
|
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
// cpxdf, [{key:'fullname', type:'text', value: 'thanhpk@liec.om'}]
|
|
211
|
-
async submitForm(formid, values) {
|
|
212
|
-
if (!formid || !values)
|
|
211
|
+
async submitForm(formid, values, options = {}) {
|
|
212
|
+
if (!formid || !values) throw new TypeError('submitForm requires a form ID and fields')
|
|
213
|
+
await this.store.readyGate.entry()
|
|
213
214
|
if (sb.startsWith(formid, 'cp')) {
|
|
214
215
|
let storeuser = sb.cloneDeep(this.store.matchUser())
|
|
215
216
|
let user = {id: storeuser.id, account_id: storeuser.account_id}
|
|
@@ -233,28 +234,14 @@ class Widget {
|
|
|
233
234
|
return this.store.sendConversion(formid, values, impressiontoken)
|
|
234
235
|
}
|
|
235
236
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
return this.store.startConvo({
|
|
245
|
-
subject: 'Khách điền form',
|
|
246
|
-
touchpoint: {
|
|
247
|
-
channel: 'form',
|
|
248
|
-
source: window.location.hostname,
|
|
249
|
-
id: Date.now() + '_' + sb.randomString(),
|
|
250
|
-
},
|
|
251
|
-
form_submission: {id: formid, values: values},
|
|
252
|
-
message_sent: {
|
|
253
|
-
data: {
|
|
254
|
-
message: {format: 'plaintext', text: text},
|
|
255
|
-
},
|
|
256
|
-
},
|
|
257
|
-
})
|
|
237
|
+
const payload = formRequest(formid, values, options)
|
|
238
|
+
return formResult(await this.store.api.submitWebsiteForm(formid, payload), 'submit')
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async getFormStatus(formid) {
|
|
242
|
+
validateWebsiteFormId(formid)
|
|
243
|
+
await this.store.readyGate.entry()
|
|
244
|
+
return formResult(await this.store.api.getWebsiteFormStatus(formid), 'status')
|
|
258
245
|
}
|
|
259
246
|
|
|
260
247
|
// req.bot_id != "" -> start conversation with bot id
|
|
@@ -404,3 +391,46 @@ window.__sbz_WIDGET = Widget
|
|
|
404
391
|
export default Widget
|
|
405
392
|
|
|
406
393
|
// lib.submitForm('cprkztryyobndhbvwzjab', [{key: 'fullname', value: 'Pham Kieu Thanh 2' }, {key: 'phones', value: "0366621895"}]
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
function validateWebsiteFormId(formid) {
|
|
397
|
+
if (typeof formid !== 'string' || !/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/.test(formid)) {
|
|
398
|
+
throw new TypeError('Form ID must contain 1-64 letters, digits, underscores or hyphens')
|
|
399
|
+
}
|
|
400
|
+
if (formid.startsWith('cp')) throw new TypeError('Form IDs starting with cp are reserved for Subiz campaigns')
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function formRequest(formid, fields, options = {}) {
|
|
404
|
+
validateWebsiteFormId(formid)
|
|
405
|
+
if (!Array.isArray(fields) || !fields.length) throw new TypeError('Form fields must be a nonempty array')
|
|
406
|
+
if (!options || typeof options !== 'object' || Array.isArray(options)) throw new TypeError('Form options must be an object')
|
|
407
|
+
return {
|
|
408
|
+
form_name: options.name === undefined ? formid : options.name,
|
|
409
|
+
page_url: options.page_url === undefined ? window.location.href : options.page_url,
|
|
410
|
+
submission_id: options.submission_id === undefined ? sb.randomString(36) : options.submission_id,
|
|
411
|
+
values: fields.map((field) => {
|
|
412
|
+
if (!field || typeof field.key !== 'string') throw new TypeError('Each field requires a key')
|
|
413
|
+
const value = field.value
|
|
414
|
+
if (value != null && !['string', 'number', 'boolean'].includes(typeof value)) {
|
|
415
|
+
throw new TypeError('Form field values must be strings, numbers or booleans')
|
|
416
|
+
}
|
|
417
|
+
return {key: field.key, label: field.label === undefined ? field.key : field.label, value: value == null ? '' : String(value)}
|
|
418
|
+
}),
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function formResult(response, operation) {
|
|
423
|
+
const body = response && response.body
|
|
424
|
+
const detail = (response && response.error) || (body && body.error)
|
|
425
|
+
const status = response && response.code
|
|
426
|
+
const valid = operation === 'submit'
|
|
427
|
+
? body && typeof body.conversation_id === 'string' && body.conversation_id && typeof body.submission_id === 'string' && body.submission_id
|
|
428
|
+
: body && typeof body.submitted === 'boolean' && typeof body.status === 'string'
|
|
429
|
+
if (detail || !(status >= 200 && status < 300) || !valid) {
|
|
430
|
+
const error = new Error('Subiz form request failed')
|
|
431
|
+
error.code = (detail && detail.code) || 'form_request_failed'
|
|
432
|
+
error.status = status
|
|
433
|
+
throw error
|
|
434
|
+
}
|
|
435
|
+
return body
|
|
436
|
+
}
|
package/src/store/api.js
CHANGED
|
@@ -271,6 +271,15 @@ export default class {
|
|
|
271
271
|
.addQuery('x-referer', sb.get(location, 'origin', ''))
|
|
272
272
|
.post(`/accounts/${this.accid}/users/${this.uid}/events`, data)
|
|
273
273
|
|
|
274
|
+
// Keep the configured API host and all widget authentication hooks.
|
|
275
|
+
websiteFormRequest = () => this.apireq.setBaseUrl(this.ApiURL.replace(/\/4\.[01]\/?$/, '/4.1'))
|
|
276
|
+
|
|
277
|
+
submitWebsiteForm = (formid, payload) => this.websiteFormRequest()
|
|
278
|
+
.post(`/accounts/${this.accid}/external-forms/${encodeURIComponent(formid)}/submissions`, payload)
|
|
279
|
+
|
|
280
|
+
getWebsiteFormStatus = (formid) => this.websiteFormRequest()
|
|
281
|
+
.get(`/accounts/${this.accid}/external-forms/${encodeURIComponent(formid)}/status`)
|
|
282
|
+
|
|
274
283
|
submitForm = (form_id, page_url, device, data, user) =>
|
|
275
284
|
this.apireq
|
|
276
285
|
.addQuery('x-referer', sb.get(location, 'origin', ''))
|
package/src/store/store.js
CHANGED
|
@@ -15,6 +15,9 @@ cookie.defaults.expires = 3650 // 10 year
|
|
|
15
15
|
let memlocalStorage = require('./inmem_localstorage.js')
|
|
16
16
|
const MAX_CONVO = 20
|
|
17
17
|
const page_loaded = sb.Date.now()
|
|
18
|
+
const CHAT_STATE_RESET_AFTER_MS = 24 * 60 * 60 * 1000
|
|
19
|
+
const CHAT_STATE_CYCLE_STARTED_AT = 'chat_state_cycle_started_at'
|
|
20
|
+
const RESETTABLE_CHAT_STATE_KEYS = ['lastview', 'light_convos', 'bot_run_logs', 'close_preview_at']
|
|
18
21
|
|
|
19
22
|
// available localstorage key mask, user, hasManuallyClosed, isopen, close_preview_at, lastview, bot_run_logs, plugin_run_logs
|
|
20
23
|
export default class Store {
|
|
@@ -740,6 +743,20 @@ export default class Store {
|
|
|
740
743
|
this.kv.put('lastview', lastView)
|
|
741
744
|
}
|
|
742
745
|
|
|
746
|
+
resetChatStateIfExpired() {
|
|
747
|
+
const now = this.now()
|
|
748
|
+
const cycleStartedAt = this.kv.match(CHAT_STATE_CYCLE_STARTED_AT)
|
|
749
|
+
if (!Number.isFinite(cycleStartedAt) || cycleStartedAt <= 0 || cycleStartedAt > now) {
|
|
750
|
+
this.kv.put(CHAT_STATE_CYCLE_STARTED_AT, now)
|
|
751
|
+
return false
|
|
752
|
+
}
|
|
753
|
+
if (now - cycleStartedAt < CHAT_STATE_RESET_AFTER_MS) return false
|
|
754
|
+
|
|
755
|
+
sb.map(RESETTABLE_CHAT_STATE_KEYS, (key) => this.kv.del(key))
|
|
756
|
+
this.kv.put(CHAT_STATE_CYCLE_STARTED_AT, now)
|
|
757
|
+
return true
|
|
758
|
+
}
|
|
759
|
+
|
|
743
760
|
async _initResource(accid, auth, realtimep) {
|
|
744
761
|
this.g_sync = {}
|
|
745
762
|
|
|
@@ -752,6 +769,7 @@ export default class Store {
|
|
|
752
769
|
|
|
753
770
|
this.readyGate.close()
|
|
754
771
|
accid = (accid || '').trim()
|
|
772
|
+
this.resetChatStateIfExpired()
|
|
755
773
|
let lightConvos = this.kv.match('light_convos') || {}
|
|
756
774
|
this.lightConvos = {}
|
|
757
775
|
sb.map(lightConvos, (convo) => {
|