@yuneta/gobj-js 7.25.8 → 7.26.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.
package/README.md CHANGED
@@ -159,8 +159,10 @@ To publish a new version of `@yuneta/gobj-js` to [npmjs.com](https://www.npmjs.c
159
159
  # 1. Configure your npm token (only once)
160
160
  echo "//registry.npmjs.org/:_authToken=<your-token>" > ~/.npmrc
161
161
 
162
- # 2. Update the version in package.json
163
- npm version patch # or minor / major
162
+ # 2. Update the version in package.json. The first two numbers are those
163
+ # of the SDK (YUNETA_VERSION) the package belongs to; only the third
164
+ # moves between SDK releases.
165
+ npm version patch
164
166
 
165
167
  # 3. Publish (build runs automatically via prepublishOnly)
166
168
  npm publish --access public
@@ -318,6 +320,23 @@ gobj_subscribe_event(pub, "EV_X", {__filter__: {v: 1}}, sub_c); // gets the
318
320
  gobj_publish_event(pub, "EV_X", {v: 1, secret: "s"}); // the kw stays {v: 1, secret: "s"}
319
321
  ```
320
322
 
323
+ **A renamed event** (since 7.25.9, the same rule as the C kernel):
324
+ `{__config__: {__rename_event_name__: "EV_A"}}` sends the subscriber `EV_A`
325
+ instead of the published event, with `__original_event_name__` in the kw, when
326
+ some gclass declares `EV_A` (a rename nobody declares is logged and ignored).
327
+ The rename is part of what the subscription IS: a renamed subscription over a
328
+ plain one is a second one, while a plain kw is a wildcard, so a plain
329
+ subscription over a renamed one replaces it and a plain unsubscribe removes
330
+ both.
331
+
332
+ ```javascript
333
+ gobj_subscribe_event(pub, "EV_ON_MESSAGE", {}, sub); // one
334
+ gobj_subscribe_event(pub, "EV_ON_MESSAGE",
335
+ {__config__: {__rename_event_name__: "EV_A"}}, sub); // two
336
+ gobj_publish_event(pub, "EV_ON_MESSAGE", {}); // sub gets EV_ON_MESSAGE, and EV_A
337
+ gobj_unsubscribe_event(pub, "EV_ON_MESSAGE", {}, sub); // none
338
+ ```
339
+
321
340
  ### GObject Tree (Yuno)
322
341
 
323
342
  GObjects form a parent-child tree. The root is the **Yuno**. Services live directly under the Yuno. Each GObject has exactly one parent (except the Yuno itself).
@@ -355,7 +374,7 @@ gclass_create(name, event_types, states, gmt, lmt, attrs_table,
355
374
  private_data, authz_table, command_table,
356
375
  s_user_trace_level, gclass_flag)
357
376
 
358
- gclass_find_by_name(name) // → GClass or null
377
+ gclass_find_by_name(name, verbose) // → GClass or null
359
378
  gclass_check_fsm(gclass) // validate FSM (returns error count)
360
379
  gclass_add_event_type(gclass, event_name, flag)
361
380
  gclass_add_state(gclass, state_name)
@@ -462,9 +481,9 @@ gobj_name(gobj)
462
481
  gobj_short_name(gobj)
463
482
  gobj_full_name(gobj)
464
483
  gobj_gclass_name(gobj)
465
- gobj_yuno_name(gobj)
466
- gobj_yuno_role(gobj)
467
- gobj_yuno_id(gobj)
484
+ gobj_yuno_name() // no argument: there is one yuno
485
+ gobj_yuno_role()
486
+ gobj_yuno_id()
468
487
 
469
488
  gobj_find_child(gobj, kw_filter)
470
489
  gobj_find_service(name, verbose)
@@ -588,6 +607,12 @@ kw_select (gobj, kw, jn_filter, match_fn) // filter list
588
607
  kw_collect(gobj, kw, jn_filter, match_fn) // extract subset
589
608
  kw_clone_by_keys (gobj, kw, keys, verbose)
590
609
  kw_clone_by_not_keys(gobj, kw, keys, verbose)
610
+ // A filter value is compared as a value: {online: false}, {channel: 0}
611
+ // and {name: ""} match (up to 7.25.9 a false value never did):
612
+ // kw_select(null, [{id: "a", online: false}], {online: false}) → [{id: "a", ...}]
613
+ // A key that names the prototype ("__proto__", "constructor",
614
+ // "prototype") is a key like any other, as in C: it is read only when the
615
+ // kw holds it, and written as the kw's own key, never into Object.prototype.
591
616
 
592
617
  // Flags: KW_REQUIRED, KW_CREATE, KW_EXTRACT, KW_RECURSIVE, KW_WILD_NUMBER
593
618
 
@@ -606,8 +631,8 @@ str_in_list(list, str, ci) // case-insensitive optional
606
631
  index_in_list(list, value)
607
632
  delete_from_list(list, value)
608
633
  jwtDecode(token) // → header/payload/signature
609
- jwt2json(token) // → payload JSON
610
- debounce(fn, delay)
634
+ jwt2json(token, what) // → payload JSON (what: optional part)
635
+ debounce(fn, wait, immediate)
611
636
  timeTracker()
612
637
 
613
638
  // Inter-event message stack
@@ -619,11 +644,53 @@ msg_iev_write_key(kw, key, value) // no gobj
619
644
  msg_iev_read_key (kw, key) // no gobj
620
645
  ```
621
646
 
647
+ ### A json as a table: `json2flat` / `flat2json`
648
+
649
+ The same grammar as the C kernel (`kwid.h`): one row per leaf, the id being
650
+ the path joined by `` ` ``, an array index written `[N]`, an empty container
651
+ a leaf of its own.
652
+
653
+ ```javascript
654
+ json2flat({a: {b: 1, c: [true]}, d: {}})
655
+ // → {"a`b": 1, "a`c`[0]": true, "d": {}}
656
+ flat2json({"a`b": 1, "a`c`[0]": true, "d": {}})
657
+ // → {a: {b: 1, c: [true]}, d: {}}
658
+
659
+ flat_diff({a: 1, b: 2}, {a: 1, b: 3, c: 4})
660
+ // → {added: {c: 4}, removed: {}, changed: {b: {from: 2, to: 3}}}
661
+ flat_apply(flat, diff) // → flat with that diff applied
662
+ ```
663
+
664
+ `flat2json()` throws on the first id that does not fit (a leaf and a
665
+ container at once) instead of guessing.
666
+
667
+ ### Secrets in traces and logs
668
+
669
+ ```javascript
670
+ is_secret_name("password") // → true ("token", "jwt", "apikey"...)
671
+ is_secret_name("token_endpoint") // → false (names something ABOUT one)
672
+ mask_secrets_inline("login user=a passw=x") // → "login user=a passw=********"
673
+ json_mask_secrets({user: "a", password: "x"}) // → {user: "a", password: "********"}
674
+ trace_json_masked(kw, "sent") // trace_json() of the masked copy
675
+ ```
676
+
677
+ As the C kernel's: linear and bounded (a text over 4 MB is not shown at all).
678
+
622
679
  ### Logging
623
680
 
624
- All log functions take a single `format` argument and use `printf`-style
625
- substitution (`%s`, `%d`, `%i`, `%f`, `%o`/`%O`). There is no `gobj` or
626
- error-code argument (that's the C API — the JS runtime is simpler).
681
+ All log functions take a `format` and its arguments, with `printf`-style
682
+ substitution (`%s`, `%d`, `%i`, `%f`, `%o`/`%O`); arguments left over are
683
+ appended. A message with no arguments (a template literal, the usual case)
684
+ is printed as it is, so a `%s` in its data is not touched. There is no
685
+ `gobj` or error-code argument (that's the C API — the JS runtime is simpler).
686
+
687
+ ```javascript
688
+ log_error("Command not found: %s", command); // → "Command not found: open"
689
+ log_error(`Command not found: ${command}`); // the same line
690
+ ```
691
+
692
+ (Up to 7.25.9 the arguments were dropped: the first line printed
693
+ `Command not found: undefined`.)
627
694
 
628
695
  ```javascript
629
696
  log_error(format, ...args) // red, prefixed "ERROR"
@@ -887,7 +954,8 @@ import {
887
954
  SDATA, SDATA_END,
888
955
  data_type_t, sdata_flag_t,
889
956
  gclass_create,
890
- gobj_subscribe_event, gobj_yuno,
957
+ gobj_subscribe_event, gobj_yuno, gobj_parent,
958
+ gobj_read_pointer_attr,
891
959
  trace_msg,
892
960
  } from "@yuneta/gobj-js";
893
961
 
@@ -895,8 +963,9 @@ const GCLASS_NAME = "C_MY_CLASS";
895
963
 
896
964
  // 1. Attribute schema
897
965
  const attrs_table = [
898
- SDATA(data_type_t.DTP_STRING, "url", sdata_flag_t.SDF_RD, "", "Remote URL"),
899
- SDATA(data_type_t.DTP_INTEGER, "retries", sdata_flag_t.SDF_RD, 3, "Max retries"),
966
+ SDATA(data_type_t.DTP_STRING, "url", sdata_flag_t.SDF_RD, "", "Remote URL"),
967
+ SDATA(data_type_t.DTP_INTEGER, "retries", sdata_flag_t.SDF_RD, 3, "Max retries"),
968
+ SDATA(data_type_t.DTP_POINTER, "subscriber", 0, null, "Subscriber of the output events"),
900
969
  SDATA_END()
901
970
  ];
902
971
 
@@ -907,10 +976,20 @@ const PRIVATE_DATA = {
907
976
 
908
977
  // 3. Lifecycle methods
909
978
  function mt_create(gobj) {
910
- // instance created, attrs not yet populated
979
+ // The attrs already hold their values: defaults + the kw of the create.
980
+
981
+ /*
982
+ * CHILD subscription model (a SERVICE uses the other one, see the
983
+ * skeletons of yuno-skeleton)
984
+ */
985
+ let subscriber = gobj_read_pointer_attr(gobj, "subscriber");
986
+ if(!subscriber) {
987
+ subscriber = gobj_parent(gobj);
988
+ }
989
+ gobj_subscribe_event(gobj, null, {}, subscriber);
911
990
  }
912
991
  function mt_start(gobj) {
913
- // subscribe to events from other gobjs here
992
+ // subscribe to events OF OTHER gobjs here
914
993
  gobj_subscribe_event(gobj_yuno(), "EV_TIMEOUT_PERIODIC", {}, gobj);
915
994
  }
916
995
  function mt_stop(gobj) { }
@@ -967,16 +1046,15 @@ kernel/js/gobj-js/
967
1046
  ├── package.json
968
1047
  ├── vite.config.js
969
1048
  ├── dist/ ← compiled outputs (ES, CJS, UMD, IIFE)
970
- ├── tests/
971
- │ └── kw_delete.test.js
1049
+ ├── tests/ ← vitest suite, one file per contract (npm test)
972
1050
  └── src/
973
1051
  ├── index.js ← public API entry point (barrel export)
974
- ├── gobj.js ← GObject/FSM runtime (~4 500 LOC)
975
- ├── helpers.js ← utilities, JSON, logging (~3 300 LOC)
976
- ├── c_ievent_cli.js ← remote service proxy (~1 350 LOC)
977
- ├── lib_treedb.js ← TreeDB helpers (~ 540 LOC)
978
- ├── c_timer.js ← Timer GClass (~ 330 LOC)
979
- ├── c_yuno.js ← Yuno GClass (~ 290 LOC)
1052
+ ├── gobj.js ← GObject/FSM runtime (~5 600 LOC)
1053
+ ├── helpers.js ← utilities, JSON, logging (~4 700 LOC)
1054
+ ├── c_ievent_cli.js ← remote service proxy (~1 600 LOC)
1055
+ ├── lib_treedb.js ← TreeDB helpers (~ 580 LOC)
1056
+ ├── c_timer.js ← Timer GClass (~ 390 LOC)
1057
+ ├── c_yuno.js ← Yuno GClass (~ 530 LOC)
980
1058
  ├── dbsimple.js ← localStorage persistence (~ 140 LOC)
981
1059
  ├── sprintf.js ← printf-style formatting (~ 210 LOC)
982
1060
  ├── command_parser.js ← command parsing