@yuneta/gobj-js 7.26.25 → 7.26.27
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 +65 -11
- package/dist/gobj-js.cjs.js +277 -77
- package/dist/gobj-js.cjs.js.map +1 -1
- package/dist/gobj-js.cjs.min.js +1685 -1563
- package/dist/gobj-js.cjs.min.js.map +1 -1
- package/dist/gobj-js.es.js +277 -78
- package/dist/gobj-js.es.js.map +1 -1
- package/dist/gobj-js.es.min.js +1716 -1594
- package/dist/gobj-js.es.min.js.map +1 -1
- package/dist/gobj-js.iife.js +277 -77
- package/dist/gobj-js.iife.js.map +1 -1
- package/dist/gobj-js.iife.min.js +1685 -1563
- package/dist/gobj-js.iife.min.js.map +1 -1
- package/dist/gobj-js.umd.js +277 -77
- package/dist/gobj-js.umd.js.map +1 -1
- package/dist/gobj-js.umd.min.js +1685 -1563
- package/dist/gobj-js.umd.min.js.map +1 -1
- package/package.json +1 -1
package/dist/gobj-js.iife.js
CHANGED
|
@@ -980,11 +980,14 @@ var gobjJs = (function(exports) {
|
|
|
980
980
|
if (DTP_IS_NUMBER(type)) return typeof value === "number" || typeof value === "boolean";
|
|
981
981
|
return true;
|
|
982
982
|
}
|
|
983
|
+
function is_int64_number(value) {
|
|
984
|
+
return value >= -0x8000000000000000 && value < 0x8000000000000000;
|
|
985
|
+
}
|
|
983
986
|
function shape_of_value(value) {
|
|
984
987
|
if (is_array(value)) return "an array";
|
|
985
988
|
if (is_object(value)) return "an object";
|
|
986
989
|
if (is_string(value)) return REFUSED_TEXT;
|
|
987
|
-
if (typeof value === "number") return Number.isInteger(value) &&
|
|
990
|
+
if (typeof value === "number") return Number.isInteger(value) && is_int64_number(value) ? "an integer" : "a real";
|
|
988
991
|
if (typeof value === "boolean") return "a boolean";
|
|
989
992
|
return "null";
|
|
990
993
|
}
|
|
@@ -1005,7 +1008,7 @@ var gobjJs = (function(exports) {
|
|
|
1005
1008
|
value: value ? 1 : 0,
|
|
1006
1009
|
refused: null
|
|
1007
1010
|
};
|
|
1008
|
-
if (!(value
|
|
1011
|
+
if (!is_int64_number(value)) return {
|
|
1009
1012
|
value: void 0,
|
|
1010
1013
|
refused: "a number out of its range"
|
|
1011
1014
|
};
|
|
@@ -1070,8 +1073,13 @@ var gobjJs = (function(exports) {
|
|
|
1070
1073
|
value: n !== 0n,
|
|
1071
1074
|
refused: null
|
|
1072
1075
|
};
|
|
1076
|
+
const v = Number(n);
|
|
1077
|
+
if (!is_int64_number(v)) return {
|
|
1078
|
+
value: void 0,
|
|
1079
|
+
refused: "a number out of its range"
|
|
1080
|
+
};
|
|
1073
1081
|
return {
|
|
1074
|
-
value:
|
|
1082
|
+
value: v,
|
|
1075
1083
|
refused: null
|
|
1076
1084
|
};
|
|
1077
1085
|
}
|
|
@@ -1130,6 +1138,16 @@ var gobjJs = (function(exports) {
|
|
|
1130
1138
|
* undefined, or an empty text given to a number).
|
|
1131
1139
|
***************************************************************************/
|
|
1132
1140
|
function command_parameter_from_json(type, value) {
|
|
1141
|
+
try {
|
|
1142
|
+
return parameter_from_json(type, value);
|
|
1143
|
+
} catch (e) {
|
|
1144
|
+
return {
|
|
1145
|
+
value: void 0,
|
|
1146
|
+
refused: NO_JSON_TEXT
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
function parameter_from_json(type, value) {
|
|
1133
1151
|
if (value === void 0 || value === null) {
|
|
1134
1152
|
if (DTP_IS_JSON(type) && value === null) return {
|
|
1135
1153
|
value: null,
|
|
@@ -1188,31 +1206,63 @@ var gobjJs = (function(exports) {
|
|
|
1188
1206
|
data_type_t.DTP_LIST,
|
|
1189
1207
|
data_type_t.DTP_DICT
|
|
1190
1208
|
];
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1209
|
+
function is_plain_object$1(v) {
|
|
1210
|
+
const proto = Object.getPrototypeOf(v);
|
|
1211
|
+
if (proto === Object.prototype || proto === null) return true;
|
|
1212
|
+
if (Object.getPrototypeOf(proto) !== null || !Object.prototype.hasOwnProperty.call(proto, "constructor")) return false;
|
|
1213
|
+
const ctor = proto.constructor;
|
|
1214
|
+
return typeof ctor === "function" && ctor.name === "Object";
|
|
1215
|
+
}
|
|
1216
|
+
function json_scalar_or_container(v) {
|
|
1217
|
+
switch (typeof v) {
|
|
1218
|
+
case "string":
|
|
1219
|
+
case "boolean": return true;
|
|
1220
|
+
case "number": return Number.isFinite(v);
|
|
1221
|
+
case "object": return v === null ? true : null;
|
|
1222
|
+
default: return false;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
function is_json_node(value) {
|
|
1226
|
+
const top = json_scalar_or_container(value);
|
|
1227
|
+
if (top !== null) return top;
|
|
1228
|
+
const values = [value];
|
|
1229
|
+
const levels = [1];
|
|
1230
|
+
while (values.length) {
|
|
1231
|
+
const v = values.pop();
|
|
1232
|
+
const level = levels.pop() + 1;
|
|
1233
|
+
let children;
|
|
1234
|
+
if (Array.isArray(v)) {
|
|
1235
|
+
if (typeof v.toJSON === "function") return false;
|
|
1236
|
+
children = v;
|
|
1237
|
+
} else {
|
|
1238
|
+
if (!is_plain_object$1(v) || typeof v.toJSON === "function") return false;
|
|
1239
|
+
children = [];
|
|
1240
|
+
for (const key of Object.keys(v)) {
|
|
1241
|
+
const x = v[key];
|
|
1242
|
+
if (x === void 0 || typeof x === "function" || typeof x === "symbol") continue;
|
|
1243
|
+
children.push(x);
|
|
1244
|
+
}
|
|
1200
1245
|
}
|
|
1201
|
-
if (
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1246
|
+
if (children.length > 0 && level > JSON_MAX_DEPTH) return false;
|
|
1247
|
+
for (let i = 0; i < children.length; i++) {
|
|
1248
|
+
const x = children[i];
|
|
1249
|
+
const r = json_scalar_or_container(x);
|
|
1250
|
+
if (r === false) return false;
|
|
1251
|
+
if (r === null) {
|
|
1252
|
+
values.push(x);
|
|
1253
|
+
levels.push(level);
|
|
1254
|
+
}
|
|
1208
1255
|
}
|
|
1209
|
-
on_path.add(v);
|
|
1210
|
-
stack.push(v, LEAVE_NODE);
|
|
1211
|
-
if (array) for (let i = 0; i < v.length; i++) stack.push(v[i]);
|
|
1212
|
-
else for (const key of Object.keys(v)) stack.push(v[key]);
|
|
1213
1256
|
}
|
|
1214
1257
|
return true;
|
|
1215
1258
|
}
|
|
1259
|
+
function is_json_value(value) {
|
|
1260
|
+
try {
|
|
1261
|
+
return is_json_node(value);
|
|
1262
|
+
} catch (e) {
|
|
1263
|
+
return false;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1216
1266
|
var NO_JSON_TEXT = "a value with no json text";
|
|
1217
1267
|
function json_text_of(value) {
|
|
1218
1268
|
if (!is_json_value(value)) return;
|
|
@@ -2343,11 +2393,13 @@ var gobjJs = (function(exports) {
|
|
|
2343
2393
|
let child;
|
|
2344
2394
|
if (walk_type & walk_type_t.WALK_LAST2FIRST) for (let i = iter.length; i > 0; i--) {
|
|
2345
2395
|
child = iter[i - 1];
|
|
2396
|
+
if (gobj_is_destroying(child)) continue;
|
|
2346
2397
|
let ret = walk_ret(cb_walking(child, user_data, user_data2));
|
|
2347
2398
|
if (ret < 0) return ret;
|
|
2348
2399
|
}
|
|
2349
2400
|
else for (let i = 0; i < iter.length; i++) {
|
|
2350
2401
|
child = iter[i];
|
|
2402
|
+
if (gobj_is_destroying(child)) continue;
|
|
2351
2403
|
let ret = walk_ret(cb_walking(child, user_data, user_data2));
|
|
2352
2404
|
if (ret < 0) return ret;
|
|
2353
2405
|
}
|
|
@@ -2362,18 +2414,21 @@ var gobjJs = (function(exports) {
|
|
|
2362
2414
|
* or
|
|
2363
2415
|
* WALK_LAST2FIRST
|
|
2364
2416
|
***************************************************************/
|
|
2365
|
-
function rc_walk_by_level(
|
|
2417
|
+
function rc_walk_by_level(iter_, walk_type, cb_walking, user_data, user_data2) {
|
|
2418
|
+
let iter = [...iter_];
|
|
2366
2419
|
let ret = rc_walk_by_list(iter, walk_type, cb_walking, user_data, user_data2);
|
|
2367
2420
|
if (ret < 0) return ret;
|
|
2368
2421
|
let child;
|
|
2369
2422
|
if (walk_type & walk_type_t.WALK_LAST2FIRST) for (let i = iter.length; i > 0; i--) {
|
|
2370
2423
|
child = iter[i - 1];
|
|
2371
|
-
|
|
2424
|
+
if (gobj_is_destroying(child)) continue;
|
|
2425
|
+
ret = rc_walk_by_level(child.dl_children, walk_type, cb_walking, user_data, user_data2);
|
|
2372
2426
|
if (ret < 0) return ret;
|
|
2373
2427
|
}
|
|
2374
2428
|
else for (let i = 0; i < iter.length; i++) {
|
|
2375
2429
|
child = iter[i];
|
|
2376
|
-
|
|
2430
|
+
if (gobj_is_destroying(child)) continue;
|
|
2431
|
+
ret = rc_walk_by_level(child.dl_children, walk_type, cb_walking, user_data, user_data2);
|
|
2377
2432
|
if (ret < 0) return ret;
|
|
2378
2433
|
}
|
|
2379
2434
|
return ret;
|
|
@@ -2393,23 +2448,27 @@ var gobjJs = (function(exports) {
|
|
|
2393
2448
|
* or
|
|
2394
2449
|
* WALK_LAST2FIRST
|
|
2395
2450
|
***************************************************************/
|
|
2396
|
-
function rc_walk_by_tree(
|
|
2397
|
-
if (walk_type & walk_type_t.WALK_BYLEVEL) return rc_walk_by_level(
|
|
2451
|
+
function rc_walk_by_tree(iter_, walk_type, cb_walking, user_data, user_data2) {
|
|
2452
|
+
if (walk_type & walk_type_t.WALK_BYLEVEL) return rc_walk_by_level(iter_, walk_type, cb_walking, user_data, user_data2);
|
|
2453
|
+
let iter = [...iter_];
|
|
2398
2454
|
let ret;
|
|
2399
2455
|
let child;
|
|
2400
2456
|
if (walk_type & walk_type_t.WALK_BOTTOM2TOP) for (let i = iter.length; i > 0; i--) {
|
|
2401
2457
|
child = iter[i - 1];
|
|
2402
|
-
|
|
2458
|
+
if (gobj_is_destroying(child)) continue;
|
|
2459
|
+
ret = rc_walk_by_tree(child.dl_children, walk_type, cb_walking, user_data, user_data2);
|
|
2403
2460
|
if (ret < 0) return ret;
|
|
2461
|
+
if (gobj_is_destroying(child)) continue;
|
|
2404
2462
|
ret = walk_ret(cb_walking(child, user_data, user_data2));
|
|
2405
2463
|
if (ret < 0) return ret;
|
|
2406
2464
|
}
|
|
2407
2465
|
else for (let i = 0; i < iter.length; i++) {
|
|
2408
2466
|
child = iter[i];
|
|
2467
|
+
if (gobj_is_destroying(child)) continue;
|
|
2409
2468
|
ret = walk_ret(cb_walking(child, user_data, user_data2));
|
|
2410
2469
|
if (ret < 0) return ret;
|
|
2411
|
-
else if (ret === 0) {
|
|
2412
|
-
ret = rc_walk_by_tree(
|
|
2470
|
+
else if (ret === 0 && !gobj_is_destroying(child)) {
|
|
2471
|
+
ret = rc_walk_by_tree(child.dl_children, walk_type, cb_walking, user_data, user_data2);
|
|
2413
2472
|
if (ret < 0) return ret;
|
|
2414
2473
|
}
|
|
2415
2474
|
}
|
|
@@ -4726,6 +4785,7 @@ var gobjJs = (function(exports) {
|
|
|
4726
4785
|
mark_labels(str, marks);
|
|
4727
4786
|
}
|
|
4728
4787
|
var MASK_MAX_DECODINGS = 2;
|
|
4788
|
+
var VIEW_CHUNK = 8192;
|
|
4729
4789
|
function encoded_ascii_at(str, p) {
|
|
4730
4790
|
if (p + 3 > str.length || str.charCodeAt(p) !== 37) return -1;
|
|
4731
4791
|
const hi = hex_digit_value(str.charCodeAt(p + 1));
|
|
@@ -4736,28 +4796,30 @@ var gobjJs = (function(exports) {
|
|
|
4736
4796
|
}
|
|
4737
4797
|
function mark_decoded_view(str, marks, decodings) {
|
|
4738
4798
|
if (decodings >= MASK_MAX_DECODINGS) return;
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4799
|
+
let first = str.indexOf("%");
|
|
4800
|
+
while (first >= 0 && encoded_ascii_at(str, first) < 0) first = str.indexOf("%", first + 1);
|
|
4801
|
+
if (first < 0) return;
|
|
4802
|
+
const codes = new Uint16Array(str.length - first);
|
|
4803
|
+
let n = 0;
|
|
4804
|
+
for (let p = first; p < str.length; p++) {
|
|
4805
|
+
const code = str.charCodeAt(p);
|
|
4806
|
+
const c = code === 37 ? encoded_ascii_at(str, p) : -1;
|
|
4744
4807
|
if (c >= 0) {
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
const view = pieces.join("");
|
|
4808
|
+
codes[n++] = c;
|
|
4809
|
+
p += 2;
|
|
4810
|
+
} else codes[n++] = code;
|
|
4811
|
+
}
|
|
4812
|
+
const chunks = [str.substring(0, first)];
|
|
4813
|
+
for (let k = 0; k < n; k += VIEW_CHUNK) chunks.push(String.fromCharCode.apply(null, codes.subarray(k, Math.min(n, k + VIEW_CHUNK))));
|
|
4814
|
+
const view = chunks.join("");
|
|
4753
4815
|
const view_marks = new Uint8Array(view.length);
|
|
4754
4816
|
mark_inline_rules_of_now(view, view_marks);
|
|
4755
4817
|
mark_decoded_view(view, view_marks, decodings + 1);
|
|
4756
4818
|
let q = 0;
|
|
4757
4819
|
for (let i = 0; i < view.length; i++) {
|
|
4758
|
-
const
|
|
4759
|
-
if (view_marks[i]) mark_range(marks, q, q +
|
|
4760
|
-
q +=
|
|
4820
|
+
const n_from = encoded_ascii_at(str, q) >= 0 ? 3 : 1;
|
|
4821
|
+
if (view_marks[i]) mark_range(marks, q, q + n_from);
|
|
4822
|
+
q += n_from;
|
|
4761
4823
|
}
|
|
4762
4824
|
}
|
|
4763
4825
|
function mark_inline_secrets(str, marks) {
|
|
@@ -5991,21 +6053,88 @@ var gobjJs = (function(exports) {
|
|
|
5991
6053
|
return kw_clone;
|
|
5992
6054
|
}
|
|
5993
6055
|
/********************************************
|
|
6056
|
+
* The browser's localStorage, or null when there is none (outside a
|
|
6057
|
+
* browser).
|
|
6058
|
+
*
|
|
6059
|
+
* With the site's data blocked (Chrome with cookies blocked, a sandboxed
|
|
6060
|
+
* iframe), READING the window.localStorage property already throws a
|
|
6061
|
+
* SecurityError, not only its getItem(): every app reads its locale at
|
|
6062
|
+
* start, so the app did not start. Such a store is answered as one whose
|
|
6063
|
+
* every call throws that error, so each helper fails as it fails for any
|
|
6064
|
+
* store that refuses it: a read gives the default, a write -1.
|
|
6065
|
+
********************************************/
|
|
6066
|
+
function local_store() {
|
|
6067
|
+
if (typeof window === "undefined") return null;
|
|
6068
|
+
let store;
|
|
6069
|
+
try {
|
|
6070
|
+
store = window.localStorage;
|
|
6071
|
+
} catch (e) {
|
|
6072
|
+
const refuse = () => {
|
|
6073
|
+
throw e;
|
|
6074
|
+
};
|
|
6075
|
+
return {
|
|
6076
|
+
getItem: refuse,
|
|
6077
|
+
setItem: refuse,
|
|
6078
|
+
removeItem: refuse
|
|
6079
|
+
};
|
|
6080
|
+
}
|
|
6081
|
+
return store || null;
|
|
6082
|
+
}
|
|
6083
|
+
/********************************************
|
|
6084
|
+
* A failure of the store that would repeat on every read is warned
|
|
6085
|
+
* once per key, until a read of the key goes well again.
|
|
6086
|
+
********************************************/
|
|
6087
|
+
var local_storage_warned = /* @__PURE__ */ new Set();
|
|
6088
|
+
function warn_local_storage_once(key, msg) {
|
|
6089
|
+
if (local_storage_warned.has(key)) return;
|
|
6090
|
+
local_storage_warned.add(key);
|
|
6091
|
+
log_warning(msg);
|
|
6092
|
+
}
|
|
6093
|
+
/********************************************
|
|
5994
6094
|
* Get a local attribute
|
|
6095
|
+
*
|
|
6096
|
+
* The json the entry holds, or `default_value` when there is none
|
|
6097
|
+
* (written there too with `create`). A json null is no value, as
|
|
6098
|
+
* json_absent() in C: the default. An entry that is not json (a
|
|
6099
|
+
* corrupt or foreign write) is a WARNING and is REMOVED, so the
|
|
6100
|
+
* next read finds no entry: it used to console.warn on every read,
|
|
6101
|
+
* for ever; a new corrupt entry is a new WARNING. Its text is never
|
|
6102
|
+
* shown (JSON.parse() quotes it in its error, and an entry can hold a
|
|
6103
|
+
* token). A store that cannot be read (blocked) is warned once per
|
|
6104
|
+
* key, and the default is NOT written there. Outside a browser: the
|
|
6105
|
+
* default.
|
|
5995
6106
|
********************************************/
|
|
5996
6107
|
function kw_get_local_storage_value(key, default_value, create = false) {
|
|
5997
|
-
|
|
6108
|
+
const store = local_store();
|
|
6109
|
+
if (!store) return default_value;
|
|
6110
|
+
const where = `kw_get_local_storage_value(): localStorage key "${key}"`;
|
|
6111
|
+
let text;
|
|
5998
6112
|
try {
|
|
5999
|
-
|
|
6000
|
-
if (value === null) {
|
|
6001
|
-
if (create) kw_set_local_storage_value(key, default_value);
|
|
6002
|
-
return default_value;
|
|
6003
|
-
}
|
|
6004
|
-
return JSON.parse(value);
|
|
6113
|
+
text = store.getItem(key);
|
|
6005
6114
|
} catch (e) {
|
|
6006
|
-
|
|
6115
|
+
warn_local_storage_once(key, `${where} cannot be read (storage blocked?): the default is used`);
|
|
6007
6116
|
return default_value;
|
|
6008
6117
|
}
|
|
6118
|
+
let value = null;
|
|
6119
|
+
if (text !== null) try {
|
|
6120
|
+
value = JSON.parse(text);
|
|
6121
|
+
} catch (e) {
|
|
6122
|
+
value = null;
|
|
6123
|
+
try {
|
|
6124
|
+
store.removeItem(key);
|
|
6125
|
+
log_warning(`${where} does not hold json: removed, the default is used`);
|
|
6126
|
+
local_storage_warned.delete(key);
|
|
6127
|
+
} catch (e2) {
|
|
6128
|
+
warn_local_storage_once(key, `${where} does not hold json and cannot be removed: the default is used`);
|
|
6129
|
+
}
|
|
6130
|
+
text = void 0;
|
|
6131
|
+
}
|
|
6132
|
+
if (text !== void 0) local_storage_warned.delete(key);
|
|
6133
|
+
if (value === null) {
|
|
6134
|
+
if (create) kw_set_local_storage_value(key, default_value);
|
|
6135
|
+
return default_value;
|
|
6136
|
+
}
|
|
6137
|
+
return value;
|
|
6009
6138
|
}
|
|
6010
6139
|
/********************************************
|
|
6011
6140
|
* Save a local attribute
|
|
@@ -6016,15 +6145,26 @@ var gobjJs = (function(exports) {
|
|
|
6016
6145
|
* reported success on a store that had rejected the write: a full or blocked
|
|
6017
6146
|
* localStorage (quota, private mode) silently discarded the change while the
|
|
6018
6147
|
* UI showed it as saved.
|
|
6148
|
+
*
|
|
6149
|
+
* A value with no json text is refused with an ERROR, as a typed writer
|
|
6150
|
+
* refuses it (json_text_of()): a function was stored as the text
|
|
6151
|
+
* "undefined", and a NaN, a Date or a Map as a text that reads back as
|
|
6152
|
+
* another value. Checked in Node.js too, where there is no store.
|
|
6019
6153
|
********************************************/
|
|
6020
6154
|
function kw_set_local_storage_value(key, value) {
|
|
6021
|
-
if (
|
|
6022
|
-
|
|
6023
|
-
log_error(`kw_set_local_storage_value(): invalid key or value: key=${key}`);
|
|
6155
|
+
if (!key) {
|
|
6156
|
+
log_error(`kw_set_local_storage_value(): invalid key: key=${key}`);
|
|
6024
6157
|
return -1;
|
|
6025
6158
|
}
|
|
6159
|
+
const text = json_text_of(value);
|
|
6160
|
+
if (text === void 0) {
|
|
6161
|
+
log_error(`kw_set_local_storage_value(): cannot save "${key}": a value with no json text`);
|
|
6162
|
+
return -1;
|
|
6163
|
+
}
|
|
6164
|
+
const store = local_store();
|
|
6165
|
+
if (!store) return 0;
|
|
6026
6166
|
try {
|
|
6027
|
-
|
|
6167
|
+
store.setItem(key, text);
|
|
6028
6168
|
return 0;
|
|
6029
6169
|
} catch (e) {
|
|
6030
6170
|
log_error(`kw_set_local_storage_value(): cannot save "${key}" (storage full or blocked?): ${e && e.message ? e.message : e}`);
|
|
@@ -6035,15 +6175,16 @@ var gobjJs = (function(exports) {
|
|
|
6035
6175
|
* Remove local attribute
|
|
6036
6176
|
********************************************/
|
|
6037
6177
|
function kw_remove_local_storage_value(key) {
|
|
6038
|
-
|
|
6178
|
+
const store = local_store();
|
|
6179
|
+
if (!store) return;
|
|
6039
6180
|
if (!key) {
|
|
6040
|
-
|
|
6181
|
+
log_error(`kw_remove_local_storage_value(): invalid key: key=${key}`);
|
|
6041
6182
|
return;
|
|
6042
6183
|
}
|
|
6043
6184
|
try {
|
|
6044
|
-
|
|
6185
|
+
store.removeItem(key);
|
|
6045
6186
|
} catch (e) {
|
|
6046
|
-
|
|
6187
|
+
log_warning(`kw_remove_local_storage_value(): cannot remove "${key}" (storage blocked?): ${e && e.message ? e.message : e}`);
|
|
6047
6188
|
}
|
|
6048
6189
|
}
|
|
6049
6190
|
/*************************************************************
|
|
@@ -7236,11 +7377,21 @@ var gobjJs = (function(exports) {
|
|
|
7236
7377
|
************************************************************/
|
|
7237
7378
|
function node_uuid() {
|
|
7238
7379
|
let uuid = "";
|
|
7239
|
-
|
|
7240
|
-
|
|
7380
|
+
const store = local_store();
|
|
7381
|
+
if (store) {
|
|
7382
|
+
try {
|
|
7383
|
+
uuid = store.getItem("host_uuid");
|
|
7384
|
+
} catch (e) {
|
|
7385
|
+
uuid = null;
|
|
7386
|
+
warn_local_storage_once("host_uuid", "node_uuid(): localStorage cannot be read (storage blocked?): a uuid of this page only");
|
|
7387
|
+
}
|
|
7241
7388
|
if (!uuid && window.crypto?.randomUUID) {
|
|
7242
7389
|
uuid = window.crypto.randomUUID();
|
|
7243
|
-
|
|
7390
|
+
try {
|
|
7391
|
+
store.setItem("host_uuid", uuid);
|
|
7392
|
+
} catch (e) {
|
|
7393
|
+
warn_local_storage_once("host_uuid", "node_uuid(): localStorage cannot be written (storage full or blocked?): a uuid of this page only");
|
|
7394
|
+
}
|
|
7244
7395
|
}
|
|
7245
7396
|
} else if (typeof globalThis !== "undefined" && globalThis.crypto?.randomUUID) uuid = globalThis.crypto.randomUUID();
|
|
7246
7397
|
else uuid = "00000000-0000-0000-0000-000000000000";
|
|
@@ -8529,7 +8680,7 @@ var gobjJs = (function(exports) {
|
|
|
8529
8680
|
SDATA(data_type_t.DTP_STRING, "cert_pem", sdata_flag_t.SDF_PERSIST, "", "SSL server certification, PEM str format"),
|
|
8530
8681
|
SDATA(data_type_t.DTP_JSON, "extra_info", sdata_flag_t.SDF_RD, "{}", "dict data set by user, added to the identity card msg."),
|
|
8531
8682
|
SDATA(data_type_t.DTP_LIST, "required_services", sdata_flag_t.SDF_RD, "[]", "Services this LINK requires (identity card). Empty = the yuno's `required_services`, as before. A yuno with several links to DIFFERENT backends must set it per link: the yuno-wide list is the union, so every backend is told the service names of all the others."),
|
|
8532
|
-
SDATA(data_type_t.DTP_INTEGER, "timeout_retry", sdata_flag_t.SDF_RD, "5000", "First reconnect delay (ms). It BACKS OFF from here, doubling up to timeout_retry_max, and resets when a session is reached"),
|
|
8683
|
+
SDATA(data_type_t.DTP_INTEGER, "timeout_retry", sdata_flag_t.SDF_RD, "5000", "First reconnect delay (ms), at least 1000. It BACKS OFF from here, doubling up to timeout_retry_max, and resets when a session is reached"),
|
|
8533
8684
|
SDATA(data_type_t.DTP_INTEGER, "timeout_retry_max", sdata_flag_t.SDF_RD, "60000", "Cap of the reconnect backoff (ms). Set it equal to timeout_retry for the old fixed-interval behaviour"),
|
|
8534
8685
|
SDATA(data_type_t.DTP_INTEGER, "timeout_idack", sdata_flag_t.SDF_RD, "5000", "timeout waiting idAck"),
|
|
8535
8686
|
SDATA(data_type_t.DTP_POINTER, "subscriber", 0, 0, "subscriber of output-events. If null then subscriber is the parent"),
|
|
@@ -8553,7 +8704,9 @@ var gobjJs = (function(exports) {
|
|
|
8553
8704
|
websocket: null,
|
|
8554
8705
|
inside_on_open: false,
|
|
8555
8706
|
retry_ms: 0,
|
|
8556
|
-
retry_ms_before_session: 0
|
|
8707
|
+
retry_ms_before_session: 0,
|
|
8708
|
+
session_since: 0,
|
|
8709
|
+
retry_floor_warned: false
|
|
8557
8710
|
};
|
|
8558
8711
|
var __gclass__ = null;
|
|
8559
8712
|
/******************************
|
|
@@ -8812,15 +8965,22 @@ var gobjJs = (function(exports) {
|
|
|
8812
8965
|
* protocol error, throws there), so it is 4002 -- and the server
|
|
8813
8966
|
* echoes it, so EV_ON_CLOSE carries it.
|
|
8814
8967
|
*
|
|
8815
|
-
* And a session the server broke was not a session reached:
|
|
8816
|
-
* backoff the ack reset is put back, or a server that breaks every
|
|
8968
|
+
* And a session the server broke at once was not a session reached:
|
|
8969
|
+
* the backoff the ack reset is put back, or a server that breaks every
|
|
8817
8970
|
* session is reconnected at the first delay for ever, with an
|
|
8818
|
-
* EV_ON_OPEN and an EV_ON_CLOSE in the app per round.
|
|
8971
|
+
* EV_ON_OPEN and an EV_ON_CLOSE in the app per round. A session that
|
|
8972
|
+
* outlived the longest delay of the backoff (`timeout_retry_max`) was
|
|
8973
|
+
* served: its fault starts the backoff over, as a clean end does, or
|
|
8974
|
+
* one foreign message hours later is waited for with a delay escalated
|
|
8975
|
+
* by failures long gone.
|
|
8819
8976
|
***************************************************************/
|
|
8820
8977
|
var CLOSE_PEER_FAULT = 4002;
|
|
8821
8978
|
function close_for_peer_fault(gobj, reason) {
|
|
8822
8979
|
let priv = gobj.priv;
|
|
8823
|
-
if (gobj_current_state(gobj) === "ST_SESSION")
|
|
8980
|
+
if (gobj_current_state(gobj) === "ST_SESSION") {
|
|
8981
|
+
if (performance.now() - priv.session_since < retry_bounds(gobj).max) priv.retry_ms = priv.retry_ms_before_session;
|
|
8982
|
+
priv.retry_ms_before_session = 0;
|
|
8983
|
+
}
|
|
8824
8984
|
close_websocket(gobj, CLOSE_PEER_FAULT, reason);
|
|
8825
8985
|
}
|
|
8826
8986
|
/***************************************************************
|
|
@@ -9090,6 +9250,32 @@ var gobjJs = (function(exports) {
|
|
|
9090
9250
|
return 0;
|
|
9091
9251
|
}
|
|
9092
9252
|
/***************************************************************
|
|
9253
|
+
* The first delay of the backoff and its cap.
|
|
9254
|
+
*
|
|
9255
|
+
* A first delay below RETRY_FLOOR_MS is raised to it, with a WARNING
|
|
9256
|
+
* once: `timeout_retry` 0 never left 0 (0 doubled is 0), so a server
|
|
9257
|
+
* that closed before the ack was reconnected in a loop with no wait,
|
|
9258
|
+
* hundreds of connections a second.
|
|
9259
|
+
***************************************************************/
|
|
9260
|
+
var RETRY_FLOOR_MS = 1e3;
|
|
9261
|
+
function retry_bounds(gobj) {
|
|
9262
|
+
let priv = gobj.priv;
|
|
9263
|
+
let base = gobj_read_integer_attr(gobj, "timeout_retry");
|
|
9264
|
+
let max = gobj_read_integer_attr(gobj, "timeout_retry_max");
|
|
9265
|
+
if (!(base >= RETRY_FLOOR_MS)) {
|
|
9266
|
+
if (!priv.retry_floor_warned) {
|
|
9267
|
+
priv.retry_floor_warned = true;
|
|
9268
|
+
log_warning(`${gobj_short_name(gobj)}: timeout_retry ${base} ms is below ${RETRY_FLOOR_MS} ms: ${RETRY_FLOOR_MS} ms is used`);
|
|
9269
|
+
}
|
|
9270
|
+
base = RETRY_FLOOR_MS;
|
|
9271
|
+
}
|
|
9272
|
+
if (!(max >= base)) max = base;
|
|
9273
|
+
return {
|
|
9274
|
+
base,
|
|
9275
|
+
max
|
|
9276
|
+
};
|
|
9277
|
+
}
|
|
9278
|
+
/***************************************************************
|
|
9093
9279
|
* How long to wait before the next reconnect attempt.
|
|
9094
9280
|
*
|
|
9095
9281
|
* The delay used to be a constant (`timeout_retry`, 5s), for ever. A
|
|
@@ -9110,9 +9296,7 @@ var gobjJs = (function(exports) {
|
|
|
9110
9296
|
***************************************************************/
|
|
9111
9297
|
function next_retry(gobj) {
|
|
9112
9298
|
let priv = gobj.priv;
|
|
9113
|
-
|
|
9114
|
-
let max = gobj_read_integer_attr(gobj, "timeout_retry_max");
|
|
9115
|
-
if (max < base) max = base;
|
|
9299
|
+
const { base, max } = retry_bounds(gobj);
|
|
9116
9300
|
priv.retry_ms = priv.retry_ms ? Math.min(priv.retry_ms * 2, max) : base;
|
|
9117
9301
|
let jitter = priv.retry_ms * .2;
|
|
9118
9302
|
let ms = priv.retry_ms - jitter + Math.random() * 2 * jitter;
|
|
@@ -9158,6 +9342,7 @@ var gobjJs = (function(exports) {
|
|
|
9158
9342
|
priv.inside_on_open = true;
|
|
9159
9343
|
priv.retry_ms_before_session = priv.retry_ms;
|
|
9160
9344
|
priv.retry_ms = 0;
|
|
9345
|
+
priv.session_since = performance.now();
|
|
9161
9346
|
if (!priv.inform_on_close) {
|
|
9162
9347
|
priv.inform_on_close = true;
|
|
9163
9348
|
gobj_publish_event(gobj, "EV_ON_OPEN", {
|
|
@@ -9255,16 +9440,30 @@ var gobjJs = (function(exports) {
|
|
|
9255
9440
|
return 0;
|
|
9256
9441
|
}
|
|
9257
9442
|
/***************************************************************************
|
|
9443
|
+
* The server asking this client for stats or a command. The C client
|
|
9444
|
+
* serves them (gobj_stats() / gobj_command() of the service named, and
|
|
9445
|
+
* an answer); a browser client does not run what its server asks, so it
|
|
9446
|
+
* is refused: a WARNING naming what was asked, masked (a command line
|
|
9447
|
+
* can carry a secret), and nothing else -- the protocol allows the ask,
|
|
9448
|
+
* so the session goes on. Up to 7.26.25 it was dropped without a trace.
|
|
9449
|
+
***************************************************************************/
|
|
9450
|
+
function refuse_asked_by_server(gobj, event, kw, key) {
|
|
9451
|
+
const asked = kw && typeof kw[key] === "string" ? kw[key] : "";
|
|
9452
|
+
const service = kw && typeof kw.service === "string" ? kw.service : "";
|
|
9453
|
+
log_warning(`${gobj_short_name(gobj)}: ${event} not served by this client, DROPPED: ${key.replace(/_/g, "")} '${peer_text_shown(asked, PEER_NAME_SHOWN)}', service '${peer_text_shown(service, PEER_NAME_SHOWN)}'`);
|
|
9454
|
+
return -1;
|
|
9455
|
+
}
|
|
9456
|
+
/***************************************************************************
|
|
9258
9457
|
* remote asking for stats
|
|
9259
9458
|
***************************************************************************/
|
|
9260
9459
|
function ac_mt_stats(gobj, event, kw, src) {
|
|
9261
|
-
return
|
|
9460
|
+
return refuse_asked_by_server(gobj, event, kw, "__stats__");
|
|
9262
9461
|
}
|
|
9263
9462
|
/***************************************************************************
|
|
9264
9463
|
* remote asking for command
|
|
9265
9464
|
***************************************************************************/
|
|
9266
9465
|
function ac_mt_command(gobj, event, kw, src) {
|
|
9267
|
-
return
|
|
9466
|
+
return refuse_asked_by_server(gobj, event, kw, "__command__");
|
|
9268
9467
|
}
|
|
9269
9468
|
/***************************
|
|
9270
9469
|
* FSM
|
|
@@ -9595,6 +9794,7 @@ var gobjJs = (function(exports) {
|
|
|
9595
9794
|
exports.json_object_update_existing = json_object_update_existing;
|
|
9596
9795
|
exports.json_object_update_missing = json_object_update_missing;
|
|
9597
9796
|
exports.json_size = json_size;
|
|
9797
|
+
exports.json_text_of = json_text_of;
|
|
9598
9798
|
exports.jwt2json = jwt2json;
|
|
9599
9799
|
exports.jwtDecode = jwtDecode;
|
|
9600
9800
|
exports.kw_clone_by_keys = kw_clone_by_keys;
|