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'

kw_delete(gobj, kw, path)

Deletes the key at a back-tick path. Returns 0.

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 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.

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_get_int(gobj, {a: 5}, "a`b", 7, kw_flag_t.KW_CREATE); // 7, logged

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.


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. As in C, a value of a different type is logged only with KW_REQUIRED (“path MUST BE a json dict”). 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_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; as in C, a value of a different type is logged only with KW_REQUIRED (“path MUST BE a json list”). 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 and numbers only. An empty filter matches everything. It takes no gobj.

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. With create set to true the function writes the default value when the key does not exist.

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.

kw_remove_local_storage_value(key)

Deletes an attribute from the local storage.


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.