@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 CHANGED
@@ -306,15 +306,35 @@ thrown (7.26.24). So is a value that is not json ALL THE WAY DOWN given to a
306
306
  string, a `DTP_DICT` or a `DTP_LIST` (7.26.25): a cycle anywhere inside, a
307
307
  `BigInt` inside (`[1n]`), a `Map`, a `Set`, a `Date`, a typed array, an instance
308
308
  of a class. They were written, and burst (or turned into `"{}"`) when the value
309
- was serialized, far from the write. A key whose value is `undefined` or a
310
- function is dropped, as `JSON.stringify()` drops it:
309
+ was serialized, far from the write. And so is a value whose text would say
310
+ another value (7.26.26): an object with a `toJSON()`, `NaN` or `Infinity` inside
311
+ (the text says `null`; jansson holds no such real), a function, `undefined`, a
312
+ symbol or a hole inside an array (`[1, , 3]`: the text says `null`), and a value
313
+ nested deeper than 2048 levels (a scalar is a level too), which C would not read
314
+ back and which bursts `JSON.stringify()` a little further on. Every version
315
+ before 7.26.26 wrote them, so this is a BREAKING change for a caller that wrote
316
+ one. A getter or a `Proxy` trap that
317
+ throws is a refusal too, never an exception (7.26.26). A key whose value is
318
+ `undefined`, a function or a symbol is dropped, as `JSON.stringify()` drops it:
319
+ an absent key. A plain object of another realm (a `vm` context, an iframe) is a
320
+ plain object. A `Proxy` is read through its traps, as `JSON.stringify()` reads
321
+ it: JS cannot tell one from its target.
311
322
 
312
323
  ```javascript
313
324
  gobj_write_attr(gobj, "settings", {a: 1, b: undefined}); // 0: b goes when serialized
314
325
  gobj_write_attr(gobj, "settings", new Map([["a", 1]])); // -1, ERROR "a value with no json text"
315
326
  gobj_write_str_attr(gobj, "name", new Map()); // -1: it was the text "{}"
327
+ gobj_write_attr(gobj, "settings", {ratio: NaN}); // -1: it was the text {"ratio":null}
328
+ gobj_write_attr(gobj, "items", [1, undefined]); // -1: it was the text [1,null]
329
+ gobj_write_attr(gobj, "items", [1, , 3]); // -1: a hole, it was the text [1,null,3]
330
+ gobj_write_str_attr(gobj, "name", NaN); // -1: it was the text "null"
316
331
  ```
317
332
 
333
+ The check walks the whole value on every write of a string, a dict or a list:
334
+ it costs about what a `JSON.stringify()` of the value costs (some 20-30 ms for a
335
+ dict of 50 000 records), once per write. It is not cached: an object found json
336
+ once can be changed into one that is not before its next write.
337
+
318
338
  A `DTP_JSON` attribute is not checked: it takes any value, as before.
319
339
 
320
340
  A json TEXT is read as the C parser reads it: one that C
@@ -322,11 +342,14 @@ refuses is refused in JS too -- an escaped NUL (`"\u0000"`), a lone surrogate,
322
342
  an integer out of int64, a real that overflows (`1e309`; to a `DTP_JSON` it is
323
343
  the text `"1e309"`, not `Infinity`), a text nested deeper than 2048 levels (a
324
344
  scalar is a level too; 7.26.25). A number beyond int64 is "a real" to a dict,
325
- as in C (`1e20`), and `9223372036854775807` is taken by an integer (JSON.parse()
326
- makes it 2^63, kept as such). Two differences remain, because a JS number
327
- cannot carry them: an integer beyond 2^53 is rounded, and `2.0` (and `-0.0`) is
328
- the integer (see `tests/fixtures/attr_types_parity.json`, which the C test
329
- `tests/c/attr_types` of yunetas reads too).
345
+ as in C (`1e20`). Three differences remain, because a JS number cannot carry
346
+ them: an integer beyond 2^53 is rounded; `2.0` (and `-0.0`) is the integer; and
347
+ the last 512 integers of int64 round up to 2^63, which is OUT of int64, so an
348
+ integer attr refuses them, as text or as a number (`"9223372036854775807"`,
349
+ the int64 maximum C takes, is "a number out of its range" in JS: its JS number
350
+ is 2^63, whose text C refuses as "too big integer"; 7.26.26, 7.26.25 took it).
351
+ See `tests/fixtures/attr_types_parity.json`, which the C test
352
+ `tests/c/attr_types` of yunetas reads too.
330
353
 
331
354
  ### Events & Pub-Sub
332
355
 
@@ -703,8 +726,20 @@ kw_clone_by_not_keys(gobj, kw, keys, verbose)
703
726
 
704
727
  // Local storage helpers (browser localStorage, no "store" parameter)
705
728
  kw_get_local_storage_value(key, default_value, create) // create=false
706
- kw_set_local_storage_value(key, value)
729
+ kw_set_local_storage_value(key, value) // 0, or -1 logged
707
730
  kw_remove_local_storage_value(key)
731
+ // BREAKING in 7.26.26: kw_get_local_storage_value() answers default_value
732
+ // where it answered null (an absent entry, a json null) or undefined (no
733
+ // browser), and an entry that is not json is REMOVED, with one WARNING
734
+ // that never shows its text. A store the browser blocks (cookies blocked,
735
+ // a sandboxed iframe: reading window.localStorage throws) answers the
736
+ // default, warned once per key, and a write to it is -1 (7.26.27: before,
737
+ // every helper and node_uuid() threw). A value with no json text is
738
+ // refused by the writer, -1 with an ERROR, as a typed writer refuses it
739
+ // (7.26.27; a function was stored as the text "undefined"):
740
+ // kw_set_local_storage_value("k", () => 1) → -1
741
+ // kw_set_local_storage_value("k", {w: NaN}) → -1 (it was {"w":null})
742
+ // kw_set_local_storage_value("k", [1, , 3]) → -1 (a hole: [1,null,3])
708
743
 
709
744
  // Misc utilities
710
745
  current_timestamp() // ISO string
@@ -1040,7 +1075,9 @@ const event_types = [
1040
1075
  ["EV_MT_COMMAND_ANSWER", event_flag_t.EVF_PUBLIC_EVENT],
1041
1076
  ["EV_REALTIME_ALARM", event_flag_t.EVF_PUBLIC_EVENT],
1042
1077
  ];
1043
- ``` What the server gets wrong is a WARNING, never an ERROR. Before the session only `EV_IDENTITY_CARD_ACK` is taken; a `dst_role` or `dst_yuno` that is not this yuno's closes the channel too (an empty one is legal: anybody's), as the C transport does; an event that the named service does not take as public is dropped (7.26.24). A channel closed for a fault of the server is closed with **4002**, not 1000 (a browser may send only 1000 or 3000-4999, so 1002 cannot be used), so `EV_ON_CLOSE` says the session was cut (7.26.25).
1078
+ ```
1079
+
1080
+ What the server gets wrong is a WARNING, never an ERROR. Before the session only `EV_IDENTITY_CARD_ACK` is taken; a `dst_role` or `dst_yuno` that is not this yuno's closes the channel too (an empty one is legal: anybody's), as the C transport does; an event that the named service does not take as public is dropped (7.26.24). A channel closed for a fault of the server is closed with **4002**, not 1000 (a browser may send only 1000 or 3000-4999, so 1002 cannot be used), so `EV_ON_CLOSE` says the session was cut (7.26.25).
1044
1081
 
1045
1082
  `EV_ON_CLOSE` carries `code`, `reason` (masked) and `was_clean`, and so does `EV_ON_OPEN_ERROR` (7.26.24). A session that ends cleanly (a stop, a logout, the server closing with 1000 or 1001) is logged by nobody; a session CUT -- not clean, or any other code (1006, 1008, 1011) -- is one WARNING with the code and the reason masked:
1046
1083
 
@@ -1066,8 +1103,25 @@ that is just coming back. The backoff resets when a session is actually reached,
1066
1103
  and on `mt_start` — a deliberate reconnect never inherits a previous run's
1067
1104
  penalty. A session the transport closes for a fault of the server (4002) does not
1068
1105
  count as reached: the backoff goes on from where it was (7.26.25), so a server
1069
- that breaks every session is not reconnected at the first delay for ever. For the old fixed-interval behaviour set `timeout_retry_max` equal to
1070
- `timeout_retry`.
1106
+ that breaks every session is not reconnected at the first delay for ever -- unless
1107
+ the session outlived `timeout_retry_max`: a session that long was served, and its
1108
+ fault starts the backoff over, as a clean end does (7.26.26). For the old fixed-interval behaviour set `timeout_retry_max` equal to
1109
+ `timeout_retry`. The first delay is **at least 1000 ms** (7.26.27): a lower
1110
+ `timeout_retry` is raised to 1000 with one WARNING, because 0 never doubled
1111
+ away from 0 and a server that closes before the ack was reconnected in a loop
1112
+ with no wait, hundreds of times a second.
1113
+
1114
+ **A refused identity card** (`result < 0` in its ack) publishes `EV_ON_ID_NAK`
1115
+ and closes the channel with 1000; no session was reached, so the close is then
1116
+ `EV_ON_OPEN_ERROR` (one WARNING), and the link retries -- as the C transport.
1117
+ A subscriber that shows the refusal on `EV_ON_ID_NAK` should not show a second
1118
+ error on the `EV_ON_OPEN_ERROR` that follows it.
1119
+
1120
+ **Stats and commands the server asks of this client** (`EV_MT_STATS`,
1121
+ `EV_MT_COMMAND`) are not served: the C client runs them on the service named and
1122
+ answers, a browser client does not run what its server asks. Each is one WARNING
1123
+ naming what was asked (masked), and the session goes on (7.26.26; up to 7.26.25
1124
+ they were dropped without a trace).
1071
1125
 
1072
1126
  ---
1073
1127