Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

JS: kw, kwid and inter-event helpers

kw, kwid and inter-event helpers

A kw is the JSON payload that travels with every event. These helpers read it, write it, filter it and clone it.

Source code: src/helpers.js

Most helpers take gobj as their first parameter, and they use it only to name the gobj in a log message. kw_has_key(), kw_pop() and kw_match_simple() are the exceptions, and they take no gobj.


kw_flag_t

The flags of the typed readers. Combine them with the bit-or operator.

FlagEffect
KW_REQUIREDWrites a log error when the path does not exist, and in kw_get_dict() and kw_get_list() also when its value has a different type. kw_get_bool(), kw_get_int(), kw_get_real() and kw_get_str() log a value of a different type also without it, as in C. Example: kw_get_dict(gobj, {}, "cfg", null, kw_flag_t.KW_REQUIRED) gives null and logs “path not found: ‘cfg’”.
KW_CREATECreates the path with the default value when it does not exist.
KW_WILD_NUMBERIn kw_get_int(), kw_get_real() and kw_get_bool(): also reads a value of a simple type that is not the reader’s own (a boolean, a number, a string, null), and writes no log for it. A list or a dict gives 0 (false) and a log error.
KW_EXTRACTDeletes the key after the read, only when the reader answers with its value.
KW_BACKWARDSearches from the end in a list.
KW_VERBOSEWrites a log message when the operation fails.
KW_LOWERPuts the key in lower case.
KW_RECURSIVEGoes down into the sub-objects.

Read and write

kw_has_key(kw, key)

Tells if kw has the key as its own property. It takes a key, not a path, and it takes no gobj. Returns a boolean.

kw_find_path(gobj, kw, path, verbose)

Gives the value at a back-tick path. Returns undefined when the path does not exist, as the C kw_find_path() returns NULL. With verbose set to true the function writes a log error first.

When kw is not a dict or a list, or when a segment in the middle of the path holds a scalar, the function logs “kw must be list or dict” and returns undefined. A middle segment that is absent or null returns undefined, and logs only when verbose is true. So a typed reader (kw_get_int(), kw_get_str(), ...) that gets a bad kw returns its default (since gobj-js 7.25.6; before, a bad kw returned 0 and a null middle segment threw a TypeError):

kw_find_path(null, {a: [{b: 1}, {b: 2}]}, "a`1`b", false);  // 2
kw_find_path(null, {a: null}, "a`b", false);                // undefined, no log
kw_get_int(null, null, "x", 5, 0);   // 5, logged: kw must be list or dict: 'x'

In a list, a segment is an index: digits only, read as C’s atoi() reads them. Any other segment is not found (since gobj-js 7.26.2; before, length answered the size of the list). C answers item 0 for a segment that is not a number.

kw_find_path(null, {a: [1, 2]}, "a`1", false);       // 2
kw_find_path(null, {a: [1, 2]}, "a`length", false);  // undefined

kw_delete(gobj, kw, path)

Deletes the key at a back-tick path, as the C kw_delete(): it returns 0, or -1 with a logged error when the key or its parent is not there, or when the last segment would be an item of a list (only a key of a dict is deleted). Since gobj-js 7.26.6; before, a missing key answered 0 in silence and a length segment threw a TypeError.

kw_delete(gobj, {a: {b: 1}}, "a`b");     // 0, kw is {a: {}}
kw_delete(gobj, {a: [1, 2]}, "a`0");     // -1, logged: cannot delete an item of a list
kw_delete(gobj, {a: 1}, "b");            // -1, logged: path not found

kw_pop(kw1, kw2)

Deletes from kw1 every key that kw2 names. kw2 can be a string, an object or an array, and an array goes down into each of its elements. It takes no gobj, and it returns nothing.

kw_set_dict_value(gobj, kw, path, value)

Writes a value at a back-tick path, and creates the intermediate objects that the path needs. It returns 0, or -1 with a logged error when kw is not an object, when the path is empty (as C; since gobj-js 7.26.10 it no longer writes kw[""]), or when a middle segment is null or a scalar: nothing can be created inside one, so the kw is left as it was (C logs and stops there too). A typed reader with KW_CREATE over such a path answers its default, logged and NOT written -- kw_get_dict(), kw_get_list() and kw_get_dict_value() too. For what kw_get_dict() and kw_get_list() do with KW_CREATE over a value that IS there (a null, another type), see their entries below.

C differs here: since SDK 7.26.9 its json readers with KW_CREATE return NULL when they cannot answer what kw holds (a write that fails, a path of another type, no kw), because the C default is freed by then (see kw_get_dict() in C); in JS nothing is freed, and the default comes back.

const kw = {a: {}};
kw_set_dict_value(gobj, kw, "a`b`c", 1);       // 0, kw is {a: {b: {c: 1}}}
kw_set_dict_value(gobj, {a: null}, "a`b", 1);  // -1, logged
kw_set_dict_value(gobj, kw, "", 1);            // -1, logged: path empty
kw_get_int(gobj, {a: 5}, "a`b", 7, kw_flag_t.KW_CREATE); // 7, logged

An empty SEGMENT is refused too, by both writers (gobj-js 7.26.13, SDK 7.26.9): "a`", "`a" and "ab"`` name no key. Before, JS wrote the key "" ({a: {"": 1}}) and C dropped the segment and wrote over a.

kw_set_dict_value(gobj, {a: 5}, "a`", 1);      // -1, logged; a is still 5

A list on the path is walked, never grown: its segment is an index inside the list (digits, read as C’s atoi() reads them), and anything else is refused with -1 and a logged error (since gobj-js 7.26.4, the same rule as kw_find_path()):

const kw = {a: [1, 2]};
kw_set_dict_value(gobj, kw, "a`1", 9);         // 0, kw is {a: [1, 9]}
kw_set_dict_value(gobj, kw, "a`length", 0);    // -1, logged; the list is kept
kw_set_dict_value(gobj, kw, "a`5", 0);         // -1, logged: not in the list

kw_set_subdict_value(gobj, kw, path, key, value)

Writes key inside the object at path, and creates that object when it does not exist. Returns 0, or -1 with a log error when path cannot be written (not a string, a segment that is not an index of its list, a value that is not an object): nothing is written then (since gobj-js 7.26.8).

let kw = {a: [1, 2]};
kw_set_subdict_value(gobj, kw, "cfg`net", "port", 80);    // 0: kw.cfg.net.port === 80
kw_set_subdict_value(gobj, kw, "a`length", "port", 80);   // -1, logged; kw.a is kept

Typed readers

Every reader takes the same five parameters, and each one gives the type of its name. A value of a different type, or a key that is not there, gives the default value back, as the C readers do: as you gave it in kw_get_str(), kw_get_dict() and kw_get_list(), and converted to the reader’s type in kw_get_bool() (Boolean()), kw_get_int() (parseInt()) and kw_get_real() (Number()):

kw_get_str (gobj, {}, "name", null, 0);       // null, as given
kw_get_dict(gobj, {}, "cfg", null, 0);        // null, as given
kw_get_bool(gobj, {}, "on", 1, 0);            // true: Boolean(1)
kw_get_int (gobj, {}, "port", "8080", 0);     // 8080: parseInt("8080")
kw_get_real(gobj, {x: "1.5"}, "x", "2.5", 0); // 2.5, and logged: a string is not a number

With KW_REQUIRED a missing key or a value of a different type writes a log error; kw_get_bool(), kw_get_int(), kw_get_real() and kw_get_str() log a value of a different type also without it, as in C (kw_get_str() does not log a null value). In kw_get_bool(), kw_get_dict() and kw_get_list(), KW_CREATE writes only a default of the right type. In every typed reader, KW_EXTRACT deletes only a value that the reader gives back: a value of a different type stays in the kw. Before gobj-js 7.25.2, kw_get_list(), kw_get_dict() and kw_get_bool() did not keep this rule, before 7.25.4 kw_get_int(), kw_get_real() and kw_get_str() did not, and before 7.25.5 kw_get_str() logged a value of a different type only with KW_REQUIRED (see each one below).

kw_get_bool      (gobj, kw, path, default_value, flag)
kw_get_int       (gobj, kw, path, default_value, flag)
kw_get_real      (gobj, kw, path, default_value, flag)
kw_get_str       (gobj, kw, path, default_value, flag)
kw_get_dict      (gobj, kw, path, default_value, flag)
kw_get_list      (gobj, kw, path, default_value, flag)
kw_get_dict_value(gobj, kw, path, default_value, flag)
kw_get_pointer   (gobj, kw, path, default_value, flag)

kw_get_bool()

Reads a boolean. Only a boolean is read: any other value gives the default back, as a boolean, and writes a log error (“path MUST BE a json boolean”) with or without KW_REQUIRED, as in C. With KW_WILD_NUMBER the function also reads a number (0 is false), a string ("true" or "false" in any case, else its decimal integer, as C’s atoi() reads it: "0x1F" is 0, so false) and null (false), as in C; a list or a dict then gives false and a log error (“path MUST BE a simple json element”). Before gobj-js 7.25.2 the value went through Boolean(), so the string "false" was true. Before 7.25.3 a value of another type gave the default back with no log. Before 7.25.5 the string went through parseInt() with no base, which reads hex: "0x1F" was true.

kw_get_bool(gobj, {on: true}, "on", false, 0);                        // true
kw_get_bool(gobj, {}, "on", true, 0);                                 // true (the default)
kw_get_bool(gobj, {on: "false"}, "on", true, 0);                      // true, and logged: a string is not a boolean
kw_get_bool(gobj, {on: "false"}, "on", true, kw_flag_t.KW_WILD_NUMBER); // false
kw_get_bool(gobj, {on: 0}, "on", true, kw_flag_t.KW_WILD_NUMBER);       // false
kw_get_bool(gobj, {on: "12"}, "on", false, kw_flag_t.KW_WILD_NUMBER);   // true
kw_get_bool(gobj, {on: "0x1F"}, "on", true, kw_flag_t.KW_WILD_NUMBER);  // false: atoi("0x1F") is 0

kw_get_int()

Reads an integer. A number is read and truncated toward zero, as the C cast does. Any other value gives the default back, as an integer (parseInt()), and writes a log error (“path MUST BE a json integer”) with or without KW_REQUIRED, as in C. With KW_WILD_NUMBER the function also reads a boolean (1 or 0), a string (as C’s strtoll() with base 0: "0x1F" is 31, "017" is 15, "12abc" is 12, "abc" is 0) and null (0); a list or a dict then gives 0 and a log error (“path MUST BE a simple json element”). KW_EXTRACT deletes the key only when the function gives its value back.

Before gobj-js 7.25.4, KW_EXTRACT deleted the key before the type check (a value that was not a number was lost), a value of another type was logged only with KW_REQUIRED, KW_WILD_NUMBER had no effect, and a number went through parseInt(), which reads 1e-7 as 1.

kw_get_int(gobj, {port: 8080}, "port", 0, 0);                         // 8080
kw_get_int(gobj, {x: 3.9}, "x", 0, 0);                                // 3
kw_get_int(gobj, {}, "port", 80, 0);                                  // 80 (the default)
kw_get_int(gobj, {port: "8080"}, "port", 80, 0);                      // 80, and logged: a string is not a number
kw_get_int(gobj, {port: "8080"}, "port", 80, kw_flag_t.KW_WILD_NUMBER); // 8080
kw_get_int(gobj, {mask: "0x1F"}, "mask", 0, kw_flag_t.KW_WILD_NUMBER);  // 31

let kw = {port: "8080"};
kw_get_int(gobj, kw, "port", 80, kw_flag_t.KW_EXTRACT);  // 80, logged; kw is still {port: "8080"}
kw = {port: 8080};
kw_get_int(gobj, kw, "port", 80, kw_flag_t.KW_EXTRACT);  // 8080; kw is now {}

kw_get_real()

Reads a real number. A number is read as it is. Any other value gives the default back, as a number (Number()), and writes a log error (“path MUST BE a json real”) with or without KW_REQUIRED, as in C. With KW_WILD_NUMBER the function also reads a boolean (1 or 0), a string (as parseFloat() reads it; 0 when the string does not start with a number) and null (0); a list or a dict then gives 0 and a log error (“path MUST BE a simple json element”). KW_EXTRACT deletes the key only when the function gives its value back. Before gobj-js 7.25.4 it did not keep these rules, as kw_get_int().

kw_get_real(gobj, {t: 21.5}, "t", 0, 0);                         // 21.5
kw_get_real(gobj, {t: "21.5"}, "t", 0, 0);                       // 0, and logged: a string is not a number
kw_get_real(gobj, {t: "21.5"}, "t", 0, kw_flag_t.KW_WILD_NUMBER);  // 21.5
kw_get_real(gobj, {t: null}, "t", 1, kw_flag_t.KW_WILD_NUMBER);    // 0

kw_get_str()

Reads a string. If the key has a string, you get that string. If the key is not there, or its value is not a string, you get the default as you gave it, as in C. Before gobj-js 7.25.1 the function put the default through String(), so a default of 0 or null came back as "0" or "null", and an if() on the result was true.

What it logs is what C logs:

Before gobj-js 7.25.5 a value of another type was logged only with KW_REQUIRED, and then a null value was logged too.

With KW_CREATE, a string default is written into the kw. Any other default is written as null. With KW_EXTRACT only a string is deleted from the kw; before gobj-js 7.25.4 the key was deleted before the type check, so a value that was not a string was lost. (The C kw_get_str() does not extract at all: its answer would point into the freed json. A JS string does not have that problem.)

kw_get_str(gobj, {name: "x"}, "name", "", 0);     // "x"
kw_get_str(gobj, {}, "name", "", 0);              // ""
kw_get_str(gobj, {}, "name", null, 0);            // null (not "null")
kw_get_str(gobj, {name: 5}, "name", "none", 0);   // "none", and logged: 5 is not a string
kw_get_str(gobj, {name: null}, "name", "none", kw_flag_t.KW_REQUIRED);  // "none", not logged
kw_get_str(gobj, {}, "name", "none", kw_flag_t.KW_REQUIRED);            // "none", logged: path not found

let kw = {name: 5};
kw_get_str(gobj, kw, "name", "none", kw_flag_t.KW_EXTRACT);  // "none", logged; kw is still {name: 5}

kw_get_dict()

Reads an object. An object found is given back as it is (the same object, not a copy). Anything else gives the default back as you gave it, null included. A value of a different type is logged with KW_REQUIRED (“path MUST BE a json dict”) and, since gobj-js 7.26.14, with KW_CREATE (“…, cannot create”): the caller means to write inside what it gets, and the default it gets is held by nobody. With KW_CREATE a null at the path is “no value”: a dict default replaces it, as a missing key (since 7.26.14). A null with NO dict default is a missing key too (since 7.26.16): it is given back as the default, said only with KW_REQUIRED (“path not found”), never as “cannot create” -- the same as C.

Where C differs (SDK 7.26.9+): C answers NULL, not the default, for a value of another type with KW_CREATE (the C default is freed by then); C replaces a null with any non-NULL default, JS only with a dict default; and JS dumps the kw (trace_json_masked) with the “cannot create” error, C does not. Before gobj-js 7.25.2 the default went through Object(), so a null default came back as {}, and a 0 as a Number object.

kw_get_dict(gobj, {cfg: {a: 1}}, "cfg", {}, 0);    // {a: 1}, the object in the kw
kw_get_dict(gobj, {}, "cfg", null, 0);             // null (not {})
kw_get_dict(gobj, {cfg: [1]}, "cfg", null, 0);     // null: a list is not a dict
kw_get_dict(gobj, {cfg: [1]}, "cfg", null, kw_flag_t.KW_REQUIRED); // null, logged: path MUST BE a json dict
let kw = {};
kw_get_dict(gobj, kw, "a`b", {}, kw_flag_t.KW_CREATE); // {} -- and kw is now {a: {b: {}}}
kw_get_dict(gobj, {d: null}, "d", {}, kw_flag_t.KW_CREATE); // {}, and kw.d is it
kw_get_dict(gobj, {s: 5}, "s", {}, kw_flag_t.KW_CREATE);    // {} unwritten, logged
kw_get_dict(gobj, {d: null}, "d", null, kw_flag_t.KW_CREATE); // null, silent: a missing key
kw_get_dict(gobj, {d: null}, "d", null,
    kw_flag_t.KW_CREATE|kw_flag_t.KW_REQUIRED);              // null, logged: path not found

kw_get_dict_value()

Reads a value of any type from an object.

kw_get_list()

Reads an array. An array found is given back as it is. Anything else gives the default back as you gave it. A value of a different type is logged with KW_REQUIRED (“path MUST BE a json list”) and, since gobj-js 7.26.14, with KW_CREATE; with KW_CREATE a null at the path is replaced by a list default, and with no list default it is a missing key (since 7.26.16) -- as kw_get_dict(), where the differences with C are listed. Before gobj-js 7.25.2 the answer went through Array(), which wraps its argument: a list found came back inside another list, a default of [] as [[]], and a default of null as [null].

kw_get_list(gobj, {ids: [1, 2]}, "ids", [], 0);    // [1, 2] (was [[1, 2]])
kw_get_list(gobj, {}, "ids", [], 0);               // [] (was [[]])
kw_get_list(gobj, {}, "ids", null, 0);             // null (was [null])
kw_get_list(gobj, {ids: 5}, "ids", null, 0);       // null: 5 is not a list

kw_get_pointer()

Reads a value that is not JSON, such as a gobj or a DOM node.


Match and filter

kw_match_simple(kw, jn_filter)

Tells if kw matches a filter. It compares strings, numbers and booleans. An empty filter matches everything. It takes no gobj.

kw_match_simple({online: false, n: 0}, {online: false})   // true
kw_match_simple({n: 0}, {n: 0})                           // true
kw_match_simple({n: "abc"}, {n: 5})                       // false: "abc" reads 0
kw_match_simple({}, {n: 0})                               // false: no `n`

A false value (0, false, "") is a value to compare. Up to gobj-js 7.25.9 it was taken for “not there”, and a filter on it matched nothing.

kw_select(gobj, kw, jn_filter, match_fn)

Gives a new list with a deep copy of each row that matches the filter. Use it when the caller changes the rows. With match_fn empty the function uses kw_match_simple().

kw_collect(gobj, kw, jn_filter, match_fn)

Gives a new list with a reference to each row that matches the filter. It is kw_select() without the copy, so a change to a row changes the source.

kw_find_json_in_list(gobj, kw_list, item, flag)

Gives the index of a simple JSON item in a list. Returns -1 when the list does not hold it.

kw_clone_by_keys(gobj, kw, keys, verbose)

Gives a new object with the keys that keys names. keys can be a string, an array of strings or an object. It is not a deep copy. With empty keys the function gives kw back.

kw_clone_by_not_keys(gobj, kw, keys, verbose)

Gives a new object without the keys that keys names. It is the opposite of kw_clone_by_keys().


Local storage

These three helpers put a value in the local storage of the browser. The persistent attributes use them. See Persistence.

kw_get_local_storage_value(key, default_value, create)

Reads an attribute from the local storage. It returns the JSON value of the entry, or default_value when there is no value. With create set to true the function also writes the default value there.

These cases give the default value (7.26.26):

These answers are BREAKING in 7.26.26: an older version answered null for an absent entry or a JSON null, and undefined outside a browser, and it kept an entry that is not JSON.

kw_get_local_storage_value("theme", "light", true);   // "light", and written if absent
localStorage.setItem("theme", "dark");               // not JSON
kw_get_local_storage_value("theme", "light", false);  // "light", one WARNING, entry removed

kw_set_local_storage_value(key, value)

Writes an attribute to the local storage. Returns 0 on success, and -1 when the value did not reach the store. An older version returned nothing and only wrote to the console, so no caller saw the failure.

A value with no JSON text is refused with an ERROR and -1, as a typed attribute writer refuses it (7.26.27): a function, a symbol, a BigInt, a cycle, NaN or Infinity, a Date, a Map, an object with a toJSON(), and a function, undefined or a hole inside an array. Before 7.26.27, a function was stored as the text "undefined", and {w: NaN} as {"w":null}. A key whose value is undefined is an absent key, as in JSON.stringify().

kw_set_local_storage_value("rect", {x: 1, y: 2});     // 0
kw_set_local_storage_value("rect", {x: 1, y: NaN});   // -1, ERROR "a value with no json text"
kw_set_local_storage_value("rect", () => 1);          // -1, nothing stored

kw_remove_local_storage_value(key)

Deletes an attribute from the local storage. A store that refuses the removal gives a WARNING (7.26.27; before, a message on the console).


kwid record helpers

A kwid is a collection of records. It can be a list of strings, a list of objects, or an object of objects with the identifier as its key. These helpers read the three forms in the same way.

kwid_match_id(ids, id)

Tells if id is in the collection ids. An empty ids matches every identifier, because no filter lets everything through.

kwid_collect(gobj, kw, ids, jn_filter, match_fn)

Gives a new list with the records that match both ids and the filter. With match_fn empty the function uses kw_match_simple().

kwid_find_one_record(gobj, kw, ids, jn_filter, match_fn)

Gives the first record that matches. It takes the parameters of kwid_collect().

kwid_new_dict(gobj, kw, path)

Builds an object of objects from a list of records, with the field id of each record as the key. With a path that is not empty the function reads the list at that path first. The function gives an unchanged result for a kw that is an object already.

kwid_new_list(gobj, kw, path)

Builds a list of records from an object of records, and writes the KEY of each record into the record as its id. With a path that is not empty the function reads the object at that path first. The function gives the same list back for a kw that is a list already.

This is the normalizing half of the pair: whatever shape the data arrives in, what comes back is the shape a table indexed and sorted by id wants.

kwid_new_list(gobj, {a: {n: 1}, b: {n: 2}});
// [{id: "a", n: 1}, {id: "b", n: 2}]

kwid_get_ids(gobj, ids)

Gives the list of the identifiers of a collection. It accepts a string, a list of strings, a list of records or an object of records.


Inter-event metadata

The inter-event protocol carries its metadata inside the kw, in the key __md_iev__. Read and write it with these helpers and never by hand: the key name is an internal detail, and a message that goes to a remote yuno and comes back keeps only what these helpers wrote.

msg_iev_write_key(kw, key, value)

Writes a key in the metadata of the message, and creates the metadata object when it does not exist. It takes no gobj.

msg_iev_read_key(kw, key)

Reads a key of the metadata. Returns undefined when the message has no metadata. It takes no gobj.

msg_iev_push_stack(gobj, kw, stack, jn_data)

Puts jn_data on a stack with a name inside the metadata. The stack carries the data of one hop when a message goes through more than one yuno.

msg_iev_get_stack(gobj, kw, stack, verbose)

Reads the top of a stack with a name. It does not take the element out.

msg_iev_set_msg_type(gobj, kw, msg_type)

Writes the type of the message. An empty string deletes the key. Returns 0.

msg_iev_get_msg_type(gobj, kw)

Reads the type of the message. Returns an empty string when the message has none.


Metadata and private keys

is_metadata_key(key)

Tells if a key is a metadata key. A metadata key begins with two underscores.

is_private_key(key)

Tells if a key is a private key. A private key begins with one underscore.