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