@yuneta/gobj-js 7.13.7 → 7.16.0

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.
@@ -2297,7 +2297,9 @@ function gobj_change_state(gobj, state_name) {
2297
2297
  gobj.current_state = new_state;
2298
2298
  let tracea = is_machine_tracing(gobj, "EV_STATE_CHANGED");
2299
2299
  let tracea_states = __trace_gobj_states__(gobj) ? true : false;
2300
- if (tracea || tracea_states) trace_machine(sprintf("🔀🔀 mach(%s%s^%s), new st(%s), prev st(%s)", !gobj.running ? "!!" : "", gobj_gclass_name(gobj), gobj_name(gobj), gobj_current_state(gobj), gobj.last_state.state_name));
2300
+ if (tracea || tracea_states) {
2301
+ if (__trace_machine_format__ === 1) {} else trace_machine(sprintf("🔀🔀 mach(%s%s^%s), new st(%s), prev st(%s)", !gobj.running ? "!!" : "", gobj_gclass_name(gobj), gobj_name(gobj), gobj_current_state(gobj), gobj.last_state.state_name));
2302
+ }
2301
2303
  return true;
2302
2304
  }
2303
2305
  /***************************************************************************
@@ -2490,7 +2492,8 @@ function gobj_send_event(dst, event, kw, src) {
2490
2492
  if (dst.gclass.gmt.mt_inject_event) {
2491
2493
  __inside__--;
2492
2494
  if (tracea) {
2493
- trace_machine(sprintf("🔃 mach(%s%s), st: %s, ev: %s, from(%s%s)", !dst.running ? "!!" : "", gobj_short_name(dst), state.state_name, event ? event : "", src && !src.running ? "!!" : "", gobj_short_name(src)));
2495
+ if (__trace_machine_format__ === 1) trace_machine(sprintf("🔜 %s %s%s %s", event ? event : "", !dst.running ? "!!" : "", gobj_short_name(dst), state.state_name));
2496
+ else trace_machine(sprintf("🔃 mach(%s%s), st: %s, ev: %s, from(%s%s)", !dst.running ? "!!" : "", gobj_short_name(dst), state.state_name, event ? event : "", src && !src.running ? "!!" : "", gobj_short_name(src)));
2494
2497
  if (kw) {
2495
2498
  if (tracea > 1) {
2496
2499
  if (json_object_size(kw)) trace_json(kw);
@@ -2871,8 +2874,9 @@ function gobj_publish_event(publisher, event, kw) {
2871
2874
  }
2872
2875
  let tracea = __yuno__ && gobj_read_bool_attr(__yuno__, "trace_subscriptions");
2873
2876
  if (tracea) {
2874
- trace_machine(sprintf("🔝🔝 mach(%s%s^%s), st: %s, ev: %s%s%s", !publisher.running ? "!!" : "", gobj_gclass_name(publisher), gobj_name(publisher), publisher.current_state.state_name, "", event ? event : "", ""));
2875
- trace_json(kw);
2877
+ if (__trace_machine_format__ === 1) trace_machine(sprintf("🔝🔝 %s %s%s %s", event ? event : "", !publisher.running ? "!!" : "", gobj_short_name(publisher), publisher.current_state.state_name));
2878
+ else trace_machine(sprintf("🔝🔝 mach(%s%s^%s), st: %s, ev: %s%s%s", !publisher.running ? "!!" : "", gobj_gclass_name(publisher), gobj_name(publisher), publisher.current_state.state_name, "", event ? event : "", ""));
2879
+ if (json_object_size(kw)) trace_json(kw);
2876
2880
  }
2877
2881
  if (publisher.gclass.gmt.mt_publish_event) {
2878
2882
  let topublish = publisher.gclass.gmt.mt_publish_event(publisher, event, kw);
@@ -2919,8 +2923,9 @@ function gobj_publish_event(publisher, event, kw) {
2919
2923
  if (__config__) {}
2920
2924
  if (__global__) json_object_update(kw2publish, __global__);
2921
2925
  if (tracea) {
2922
- trace_machine(sprintf("🔝🔄 mach(%s%s), st: %s, ev: %s, from(%s%s)", !subscriber.running ? "!!" : "", gobj_short_name(subscriber), gobj_current_state(subscriber), event ? event : "", publisher && !publisher.running ? "!!" : "", gobj_short_name(publisher)));
2923
- trace_json(kw2publish);
2926
+ if (__trace_machine_format__ === 1) trace_machine(sprintf("🔝🔄 %s (%s) %s%s", event ? event : "", event ? event : "", !subscriber.running ? "!!" : "", gobj_short_name(subscriber)));
2927
+ else trace_machine(sprintf("🔝🔄 mach(%s%s), st: %s, ev: %s, from(%s%s)", !subscriber.running ? "!!" : "", gobj_short_name(subscriber), gobj_current_state(subscriber), event ? event : "", publisher && !publisher.running ? "!!" : "", gobj_short_name(publisher)));
2928
+ if (json_object_size(kw2publish)) trace_json(kw2publish);
2924
2929
  }
2925
2930
  let ret_ = gobj_send_event(subscriber, event, kw2publish, publisher);
2926
2931
  if (ret_ < 0 && subs_flag & subs_flag_t.__own_event__) {
@@ -5095,6 +5100,234 @@ function build_path(...segments) {
5095
5100
  }
5096
5101
  return result;
5097
5102
  }
5103
+ /***************************************
5104
+ * Flat json (id -> value)
5105
+ ***************************************/
5106
+ /************************************************************
5107
+ * THE FLAT FORM, AND WHY IT IS SHAPED LIKE THIS
5108
+ *
5109
+ * A json seen as a table: one row per LEAF, the id being the
5110
+ * PATH of the item and the value its value. Easier to store,
5111
+ * to compare and to diff than the nested form, and the only
5112
+ * one a person can read when two configurations disagree.
5113
+ *
5114
+ * {a: {b: 1}, c: [10, 20]}
5115
+ * -> {"a`b": 1, "c`[0]": 10, "c`[1]": 20}
5116
+ *
5117
+ * Same grammar as the C side (kwid.c), because the point is
5118
+ * that a flat json written by either can be read by the other:
5119
+ *
5120
+ * - segments joined by '`', the SDK's own path delimiter;
5121
+ * - a literal '`' in a key is DOUBLED, so no key is
5122
+ * forbidden -- a dict keyed by "1630" is a dict, not an
5123
+ * array of 1631 elements, which is what the first C
5124
+ * implementation answered;
5125
+ * - an array index is '[N]', canonical, no leading zeros;
5126
+ * - a dict key starting with '[' doubles it ('[[0]'), so it
5127
+ * can never be read as an index;
5128
+ * - AN EMPTY CONTAINER IS A LEAF: {} and [] hold no leaves,
5129
+ * and 'properties': {} is everywhere in our configs.
5130
+ *
5131
+ * flat2json() THROWS instead of guessing: an id used as leaf and
5132
+ * as container would depend on the order the ids are read in.
5133
+ ************************************************************/
5134
+ var FLAT_MAX_DEPTH = 256;
5135
+ var FLAT_MAX_INDEX = 1e5;
5136
+ /************************************************************
5137
+ * Is this raw segment an array index, '[N]'?
5138
+ * Returns the index, or null.
5139
+ ************************************************************/
5140
+ function flat_seg_index(seg) {
5141
+ if (typeof seg !== "string" || seg.length < 3 || seg[0] !== "[") return null;
5142
+ if (seg[seg.length - 1] !== "]") return null;
5143
+ const body = seg.slice(1, -1);
5144
+ if (!/^[0-9]+$/.test(body)) return null;
5145
+ if (body.length > 1 && body[0] === "0") return null;
5146
+ return parseInt(body, 10);
5147
+ }
5148
+ /************************************************************
5149
+ * Escape one dict key.
5150
+ ************************************************************/
5151
+ function flat_escape_key(key) {
5152
+ const s = String(key).replace(/`/g, "``");
5153
+ return s[0] === "[" ? "[" + s : s;
5154
+ }
5155
+ /************************************************************
5156
+ * Compose a flat id from its segments.
5157
+ *
5158
+ * A STRING segment is a dict key and gets escaped; a NUMBER is
5159
+ * an array index and is written '[N]'. They are two types and
5160
+ * not two spellings: as strings, the key "[0]" and the index 0
5161
+ * would both be "[0]", which is the ambiguity '[N]' removes.
5162
+ ************************************************************/
5163
+ function flat_key_join(segments) {
5164
+ if (!Array.isArray(segments) || segments.length === 0) return "";
5165
+ return segments.map((seg) => typeof seg === "number" ? `[${seg}]` : flat_escape_key(seg)).join("`");
5166
+ }
5167
+ /************************************************************
5168
+ * Split a flat id into its segments, undoing the escapes.
5169
+ *
5170
+ * An index comes back as a NUMBER and a dict key as a STRING.
5171
+ ************************************************************/
5172
+ function flat_key_split(key) {
5173
+ if (typeof key !== "string") return null;
5174
+ let raw = [];
5175
+ let cur = "";
5176
+ for (let i = 0; i < key.length; i++) {
5177
+ if (key[i] === "`") {
5178
+ if (key[i + 1] === "`") {
5179
+ cur += "`";
5180
+ i++;
5181
+ continue;
5182
+ }
5183
+ raw.push(cur);
5184
+ cur = "";
5185
+ continue;
5186
+ }
5187
+ cur += key[i];
5188
+ }
5189
+ raw.push(cur);
5190
+ return raw.map((seg) => {
5191
+ const idx = flat_seg_index(seg);
5192
+ if (idx !== null) return idx;
5193
+ if (seg[0] === "[" && seg[1] === "[") return seg.slice(1);
5194
+ return seg;
5195
+ });
5196
+ }
5197
+ /************************************************************
5198
+ * Nested -> flat.
5199
+ ************************************************************/
5200
+ function json2flat(jn) {
5201
+ const flat = {};
5202
+ const walk = (value, path, depth) => {
5203
+ if (depth > FLAT_MAX_DEPTH) throw new Error(`json2flat: deeper than ${FLAT_MAX_DEPTH}`);
5204
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
5205
+ const keys = Object.keys(value);
5206
+ if (keys.length > 0) {
5207
+ for (const key of keys) {
5208
+ const seg = flat_escape_key(key);
5209
+ walk(value[key], depth === 0 ? seg : path + "`" + seg, depth + 1);
5210
+ }
5211
+ return;
5212
+ }
5213
+ } else if (Array.isArray(value) && value.length > 0) {
5214
+ value.forEach((child, index) => {
5215
+ const seg = `[${index}]`;
5216
+ walk(child, depth === 0 ? seg : path + "`" + seg, depth + 1);
5217
+ });
5218
+ return;
5219
+ }
5220
+ if (depth === 0) return;
5221
+ flat[path] = value;
5222
+ };
5223
+ if (jn === void 0 || jn === null) return flat;
5224
+ walk(jn, "", 0);
5225
+ return flat;
5226
+ }
5227
+ /************************************************************
5228
+ * Flat -> nested. Throws on the FIRST id that does not fit:
5229
+ * rebuilding "most of it" is how a configuration comes back
5230
+ * subtly different from the one that was saved.
5231
+ ************************************************************/
5232
+ function flat2json(flat) {
5233
+ if (flat === null || typeof flat !== "object" || Array.isArray(flat)) throw new Error("flat2json: a flat json is an object of id -> value");
5234
+ let root = null;
5235
+ for (const key of Object.keys(flat)) {
5236
+ const segments = flat_key_split(key);
5237
+ if (!segments || segments.length === 0) throw new Error(`flat2json: malformed flat id: '${key}'`);
5238
+ if (segments.length > FLAT_MAX_DEPTH) throw new Error(`flat2json: '${key}' deeper than ${FLAT_MAX_DEPTH}`);
5239
+ const first_is_index = typeof segments[0] === "number";
5240
+ if (root === null) root = first_is_index ? [] : {};
5241
+ else if (first_is_index !== Array.isArray(root)) throw new Error(`flat2json: '${key}' does not fit a ${Array.isArray(root) ? "array" : "object"} root`);
5242
+ let current = root;
5243
+ for (let i = 0; i < segments.length; i++) {
5244
+ const seg = segments[i];
5245
+ const is_index = typeof seg === "number";
5246
+ const is_last = i === segments.length - 1;
5247
+ const next_is_index = is_last ? false : typeof segments[i + 1] === "number";
5248
+ if (is_index && seg > FLAT_MAX_INDEX) throw new Error(`flat2json: array index ${seg} over the limit (${FLAT_MAX_INDEX}) in '${key}'`);
5249
+ if (is_index !== Array.isArray(current)) throw new Error(`flat2json: '${key}' uses a ${is_index ? "array index" : "key"} where the tree already has a ${Array.isArray(current) ? "array" : "object"}`);
5250
+ if (is_last) {
5251
+ if (Array.isArray(current)) {
5252
+ while (current.length <= seg) current.push(null);
5253
+ current[seg] = flat[key];
5254
+ } else {
5255
+ if (Object.prototype.hasOwnProperty.call(current, seg)) throw new Error(`flat2json: '${key}' is written twice, or once as a value and once as a container`);
5256
+ current[seg] = flat[key];
5257
+ }
5258
+ break;
5259
+ }
5260
+ let child;
5261
+ if (Array.isArray(current)) {
5262
+ while (current.length <= seg) current.push(null);
5263
+ child = current[seg];
5264
+ if (child === null || child === void 0) {
5265
+ child = next_is_index ? [] : {};
5266
+ current[seg] = child;
5267
+ }
5268
+ } else {
5269
+ child = current[seg];
5270
+ if (child === void 0) {
5271
+ child = next_is_index ? [] : {};
5272
+ current[seg] = child;
5273
+ }
5274
+ }
5275
+ if (child === null || typeof child !== "object") throw new Error(`flat2json: '${key}' walks through a value: the same id is a leaf and a container at once`);
5276
+ if (next_is_index !== Array.isArray(child)) throw new Error(`flat2json: '${key}' needs a ${next_is_index ? "array" : "object"} where the tree already has a ${Array.isArray(child) ? "array" : "object"}`);
5277
+ current = child;
5278
+ }
5279
+ }
5280
+ return root === null ? {} : root;
5281
+ }
5282
+ /************************************************************
5283
+ * Deep equality, for comparing two leaf values.
5284
+ ************************************************************/
5285
+ function flat_value_equal(a, b) {
5286
+ if (a === b) return true;
5287
+ if (a === null || b === null || typeof a !== "object" || typeof b !== "object") return false;
5288
+ return JSON.stringify(a) === JSON.stringify(b);
5289
+ }
5290
+ /************************************************************
5291
+ * What changed between two flat dicts:
5292
+ *
5293
+ * {added: {id: value}, removed: {id: value},
5294
+ * changed: {id: {from, to}}}
5295
+ ************************************************************/
5296
+ function flat_diff(flat1, flat2) {
5297
+ const a = flat1 && typeof flat1 === "object" ? flat1 : {};
5298
+ const b = flat2 && typeof flat2 === "object" ? flat2 : {};
5299
+ const diff = {
5300
+ added: {},
5301
+ removed: {},
5302
+ changed: {}
5303
+ };
5304
+ for (const key of Object.keys(a)) if (!Object.prototype.hasOwnProperty.call(b, key)) diff.removed[key] = a[key];
5305
+ else if (!flat_value_equal(a[key], b[key])) diff.changed[key] = {
5306
+ from: a[key],
5307
+ to: b[key]
5308
+ };
5309
+ for (const key of Object.keys(b)) if (!Object.prototype.hasOwnProperty.call(a, key)) diff.added[key] = b[key];
5310
+ return diff;
5311
+ }
5312
+ /************************************************************
5313
+ * Apply that diff to a flat dict, which is MUTATED.
5314
+ *
5315
+ * On the flat form on purpose: there an id addresses one value,
5316
+ * so applying is setting and deleting, with nothing to walk and
5317
+ * nothing to guess. Nested is json2flat -> flat_apply -> flat2json.
5318
+ ************************************************************/
5319
+ function flat_apply(flat, diff) {
5320
+ if (flat === null || typeof flat !== "object" || Array.isArray(flat)) throw new Error("flat_apply: a flat json is an object of id -> value");
5321
+ if (diff === null || typeof diff !== "object") throw new Error("flat_apply: a diff is the object flat_diff() answers");
5322
+ for (const key of Object.keys(diff.removed || {})) delete flat[key];
5323
+ for (const key of Object.keys(diff.added || {})) flat[key] = diff.added[key];
5324
+ for (const key of Object.keys(diff.changed || {})) {
5325
+ const change = diff.changed[key];
5326
+ if (!change || !Object.prototype.hasOwnProperty.call(change, "to")) throw new Error(`flat_apply: changed '${key}' without a 'to'`);
5327
+ flat[key] = change.to;
5328
+ }
5329
+ return flat;
5330
+ }
5098
5331
  //#endregion
5099
5332
  //#region src/dbsimple.js
5100
5333
  /*********************************************************************************
@@ -6677,6 +6910,11 @@ exports.duplicate_objects = duplicate_objects;
6677
6910
  exports.empty_string = empty_string;
6678
6911
  exports.escapeHtml = escapeHtml;
6679
6912
  exports.event_flag_t = event_flag_t;
6913
+ exports.flat2json = flat2json;
6914
+ exports.flat_apply = flat_apply;
6915
+ exports.flat_diff = flat_diff;
6916
+ exports.flat_key_join = flat_key_join;
6917
+ exports.flat_key_split = flat_key_split;
6680
6918
  exports.gclass_add_ev_action = gclass_add_ev_action;
6681
6919
  exports.gclass_add_event_type = gclass_add_event_type;
6682
6920
  exports.gclass_add_state = gclass_add_state;
@@ -6804,6 +7042,7 @@ exports.jdb_get_by_idx = jdb_get_by_idx;
6804
7042
  exports.jdb_get_topic = jdb_get_topic;
6805
7043
  exports.jdb_init = jdb_init;
6806
7044
  exports.jdb_update = jdb_update;
7045
+ exports.json2flat = json2flat;
6807
7046
  exports.json_array_append = json_array_append;
6808
7047
  exports.json_array_append_new = json_array_append_new;
6809
7048
  exports.json_array_extend = json_array_extend;