@yuneta/gobj-js 7.1.4 → 7.2.1

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.
Files changed (2) hide show
  1. package/README.md +739 -38
  2. package/package.json +3 -3
package/README.md CHANGED
@@ -1,71 +1,772 @@
1
- # README
1
+ # gobj-js
2
2
 
3
- ## Install
3
+ JavaScript/ES6 implementation of the [Yuneta](https://yuneta.io) GObject runtime (v7).
4
4
 
5
- This project uses [`vite`](https://vite.dev/) as build tool.
5
+ This package gives you:
6
6
 
7
- Install the latest `node`:
7
+ - **GObjects** — lightweight, event-driven components organized in a parent-child tree.
8
+ - **Finite State Machines** — every component has an FSM that maps (state, event) pairs to action functions, enforcing clear lifecycle and communication rules.
9
+ - **Typed attributes** — schema-declared properties (`SDATA`) with access flags, defaults, and optional persistence to `localStorage`.
10
+ - **Pub/Sub event system** — components communicate exclusively through JSON events: direct sends, subscriptions, and publications. No direct method calls between components.
11
+ - **Inter-event client** (`C_IEVENT_CLI`) — a built-in WebSocket proxy that lets browser components talk to C-based Yuneta backend services as if they were local GObjects.
8
12
 
9
- nvm install --lts
13
+ Yuneta is a **function-oriented** framework: components (GClasses) are defined by wiring together plain functions and data tables — not class hierarchies — making the architecture portable across languages. The reference implementation is in [C](https://github.com/artgins/yunetas) (~12 000 LOC); this package mirrors the same API and patterns for browser and Node.js environments.
10
14
 
11
- When writing this readme the LTS version was:
15
+ Published as [`@yuneta/gobj-js`](https://www.npmjs.com/package/@yuneta/gobj-js).
12
16
 
13
- node --version
14
- v22.14.0
17
+ ## License
15
18
 
16
- npm install -g vite
19
+ Licensed under the [MIT License](http://www.opensource.org/licenses/mit-license).
17
20
 
18
- Install dependencies:
21
+ ---
19
22
 
20
- npm install
23
+ ## Table of Contents
21
24
 
22
- To start Vite dev server:
25
+ - [Function-Oriented & Language-Portable](#function-oriented--language-portable)
26
+ - [Architecture Overview](#architecture-overview)
27
+ - [Installation](#installation)
28
+ - [Build & Develop](#build--develop)
29
+ - [Publish](#publish)
30
+ - [Quick Start](#quick-start)
31
+ - [Core Concepts](#core-concepts)
32
+ - [Public API](#public-api)
33
+ - [Built-in GClasses](#built-in-gclasses)
34
+ - [TreeDB Helpers](#treedb-helpers)
35
+ - [Writing a Custom GClass](#writing-a-custom-gclass)
36
+ - [Source Layout](#source-layout)
23
37
 
24
- vite
38
+ ---
25
39
 
26
- To build:
40
+ ## Function-Oriented & Language-Portable
27
41
 
28
- vite build
42
+ Yuneta is designed around a **function-oriented paradigm**. Every component (GClass) is defined by wiring together plain functions and data tables:
29
43
 
30
- To publish a new version of @yuneta/gobj-js to [npmjs.com](https://www.npmjs.com/package/@yuneta/gobj-js):
44
+ - **Action functions** handle events: `function ac_connect(gobj, event, kw, src)`
45
+ - **Lifecycle methods** manage creation and teardown: `mt_create`, `mt_start`, `mt_stop`, `mt_destroy`
46
+ - **FSM tables** map (state, event) pairs to action functions and next-state transitions
47
+ - **Attribute schemas** (`SDATA`) declare typed, flagged fields as data — not getter/setter methods
31
48
 
32
- # 1. Configure your npm token (only once)
33
- echo "//registry.npmjs.org/:_authToken=<your-token>" > ~/.npmrc
49
+ There is no class inheritance, no method overriding, and no language-specific constructs at the core. A GClass is a bag of functions plus a data description, registered at startup.
34
50
 
35
- # 2. Update the version in package.json
36
- npm version patch # or minor / major
51
+ ### Why this matters
37
52
 
38
- # 3. Publish (build runs automatically via prepublishOnly)
39
- npm publish --access public
53
+ This design makes Yuneta **portable to any programming language** that supports functions, arrays, and structured data — which is virtually all of them. The reference implementation is in **C** (~12,000 LOC in `gobj.c`), and this JavaScript package mirrors the same API and patterns:
40
54
 
41
- To create an npm token, go to [npmjs.com](https://www.npmjs.com) → Account → Access Tokens.
55
+ | Concept | C | JavaScript |
56
+ |---------|---|------------|
57
+ | Create an instance | `gobj_create(name, gclass, kw, parent)` | `gobj_create(name, gclass_name, attrs, parent)` |
58
+ | Send an event | `gobj_send_event(gobj, event, kw, src)` | `gobj_send_event(gobj, event, kw, src)` |
59
+ | Subscribe | `gobj_subscribe_event(pub, event, kw, sub)` | `gobj_subscribe_event(pub, event, kw, sub)` |
60
+ | Attribute schema | `SDATA(DTP_STRING, "url", SDF_RD, "", "desc")` | `SDATA(DTP_STRING, "url", SDF_RD, "", "desc")` |
61
+ | FSM action row | `{EV_CONNECT, ac_connect, "ST_CONNECTED"}` | `["EV_CONNECT", ac_connect, "ST_CONNECTED"]` |
62
+
63
+ The same GClass structure translates directly between languages. Different implementations can also **interoperate over the network** via the inter-event protocol: the built-in `C_IEVENT_CLI` GClass proxies a remote Yuneta service over WebSocket, so a JavaScript frontend can communicate with a C backend as if it were a local GObject.
64
+
65
+ ---
66
+
67
+ ## Architecture Overview
68
+
69
+ ```
70
+ ┌──────────────────────────────────────────────────────────┐
71
+ │ Yuno │
72
+ │ (application root GObject) │
73
+ │ │
74
+ │ ┌──────────────┐ ┌──────────────┐ │
75
+ │ │ Service A │ │ Service B │ │
76
+ │ │ (GObject) │ │ (GObject) │ │
77
+ │ │ │ │ │ │
78
+ │ │ ┌────────┐ │ │ ┌────────┐ │ │
79
+ │ │ │Child 1 │ │ │ │Child 2 │ │ │
80
+ │ │ └────────┘ │ │ └────────┘ │ │
81
+ │ └──────────────┘ └──────────────┘ │
82
+ └──────────────────────────────────────────────────────────┘
83
+ │ events (JSON kw) ↕ pub/sub
84
+ ```
85
+
86
+ Every component is a **GObject** — an instance of a **GClass**. GClasses define:
87
+ - Typed **attributes** (schema via `SDATA`)
88
+ - A **Finite State Machine** (states + event-action table)
89
+ - **Lifecycle methods** (`mt_create`, `mt_start`, `mt_stop`, `mt_destroy`, …)
90
+
91
+ GObjects communicate exclusively via **events** carrying JSON key-value payloads (`kw`). There is no direct method calling between components — everything goes through the FSM event dispatcher.
92
+
93
+ ---
94
+
95
+ ## Installation
96
+
97
+ ```bash
98
+ # From npm (published package)
99
+ npm install @yuneta/gobj-js
100
+
101
+ # From source (local)
102
+ npm install /path/to/kernel/js/gobj-js
103
+ ```
42
104
 
43
- To test:
105
+ Import in your project:
44
106
 
45
- npm test
107
+ ```javascript
108
+ import { gobj_start_up, gobj_create_yuno, register_c_yuno } from "@yuneta/gobj-js";
109
+ ```
46
110
 
47
- or
111
+ ---
48
112
 
49
- npx vitest
113
+ ## Build & Develop
50
114
 
51
- or
115
+ Requires Node.js LTS (v22+):
52
116
 
53
- npx vitest --watch # automatically re-run tests when files change,
117
+ ```bash
118
+ nvm install --lts
119
+ npm install -g vite
120
+ ```
54
121
 
55
- or
122
+ ```bash
123
+ cd gobj-js/
124
+ npm install # install dependencies
56
125
 
57
- npm run test:coverage
126
+ vite # dev server
127
+ vite build # build all output formats to dist/
128
+ npm test # run tests (vitest)
129
+ npm run test:coverage
130
+ npx vitest --watch # watch mode
131
+ ```
58
132
 
59
- ## Update
133
+ To update dependencies:
60
134
 
61
- ONLY one time: to update all js packages, install the module::
135
+ ```bash
136
+ npm install -g npm-check-updates
137
+ ncu -u && npm install
138
+ ```
62
139
 
63
- npm install -g npm-check-updates
140
+ ### Output formats
141
+
142
+ `vite build` produces files in `dist/`:
143
+
144
+ | File | Format | Use |
145
+ |------|--------|-----|
146
+ | `gobj-js.es.js` | ES modules | bundlers, modern browsers |
147
+ | `gobj-js.cjs.js` | CommonJS | Node.js |
148
+ | `gobj-js.umd.js` | UMD | legacy bundlers |
149
+ | `gobj-js.iife.js` | IIFE | `<script>` tag |
150
+ | `*.min.js` | minified variants | production |
151
+
152
+ ---
153
+
154
+ ## Publish
155
+
156
+ To publish a new version of `@yuneta/gobj-js` to [npmjs.com](https://www.npmjs.com/package/@yuneta/gobj-js):
157
+
158
+ ```bash
159
+ # 1. Configure your npm token (only once)
160
+ echo "//registry.npmjs.org/:_authToken=<your-token>" > ~/.npmrc
161
+
162
+ # 2. Update the version in package.json
163
+ npm version patch # or minor / major
164
+
165
+ # 3. Publish (build runs automatically via prepublishOnly)
166
+ npm publish --access public
167
+ ```
168
+
169
+ To create an npm token, go to [npmjs.com](https://www.npmjs.com) → Account → Access Tokens.
64
170
 
65
- To download new releases::
171
+ ---
172
+
173
+ ## Quick Start
174
+
175
+ ```javascript
176
+ import {
177
+ gobj_start_up,
178
+ gobj_create_yuno,
179
+ gobj_create_service,
180
+ gobj_start,
181
+ gobj_play,
182
+ gobj_yuno,
183
+ db_load_persistent_attrs,
184
+ db_save_persistent_attrs,
185
+ db_remove_persistent_attrs,
186
+ db_list_persistent_attrs,
187
+ register_c_yuno,
188
+ register_c_timer,
189
+ register_c_ievent_cli,
190
+ } from "@yuneta/gobj-js";
191
+
192
+ // 1. Register GClasses
193
+ register_c_yuno();
194
+ register_c_timer();
195
+ register_c_ievent_cli();
196
+
197
+ // 2. Initialize framework
198
+ gobj_start_up(
199
+ null, // jn_global_settings (JSON)
200
+ db_load_persistent_attrs, // load persistent attrs
201
+ db_save_persistent_attrs, // save persistent attrs
202
+ db_remove_persistent_attrs, // remove persistent attrs
203
+ db_list_persistent_attrs, // list persistent attrs
204
+ null, // global command parser fn
205
+ null // global stats parser fn
206
+ );
66
207
 
67
- ncu -u
208
+ // 3. Create the Yuno (application root)
209
+ let yuno = gobj_create_yuno("yuno", "C_YUNO", {
210
+ yuno_name: "my_app",
211
+ yuno_role: "my_role",
212
+ yuno_version: "1.0.0",
213
+ });
214
+
215
+ // 4. Create services under the yuno
216
+ gobj_create_service("main", "C_MY_SERVICE", {}, gobj_yuno());
217
+
218
+ // 5. Start everything
219
+ gobj_start(yuno);
220
+ gobj_play(yuno); // triggers mt_play on the default service
221
+ ```
68
222
 
69
- And to install the new versions::
223
+ ---
70
224
 
71
- npm install
225
+ ## Core Concepts
226
+
227
+ ### GClass & GObject
228
+
229
+ A **GClass** is a class definition — registered once at startup. A **GObject** is an instance of a GClass.
230
+
231
+ ```javascript
232
+ // Register a GClass
233
+ gclass_create(name, event_types, states, gmt, lmt,
234
+ attrs_table, private_data, authz_table,
235
+ command_table, s_user_trace_level, gclass_flag);
236
+
237
+ // Create an instance
238
+ let gobj = gobj_create(name, gclass_name, attributes, parent);
239
+ ```
240
+
241
+ ### Finite State Machines
242
+
243
+ Each GClass defines:
244
+ - A list of **states** (strings, e.g. `"ST_IDLE"`, `"ST_CONNECTED"`)
245
+ - Per-state **event-action tables**: `[event_name, action_fn, next_state]`
246
+
247
+ When an event is sent to a GObject, the FSM looks up the current state → finds the matching event → calls the action function. `next_state` (or `null` to stay) controls state transitions.
248
+
249
+ ```javascript
250
+ const st_idle = [
251
+ ["EV_CONNECT", ac_connect, "ST_CONNECTED"],
252
+ ["EV_TIMEOUT", ac_timeout, null], // stay in ST_IDLE
253
+ ];
254
+ const st_connected = [
255
+ ["EV_DISCONNECT", ac_disconnect, "ST_IDLE"],
256
+ ["EV_MESSAGE", ac_message, null],
257
+ ];
258
+
259
+ const states = [
260
+ ["ST_IDLE", st_idle],
261
+ ["ST_CONNECTED", st_connected],
262
+ ];
263
+ ```
264
+
265
+ ### Attributes (SData)
266
+
267
+ Attributes are declared in a schema table using `SDATA()` macros. Each attribute has a type, name, flags, default value, and description.
268
+
269
+ ```javascript
270
+ import { SDATA, SDATA_END, data_type_t, sdata_flag_t } from "@yuneta/gobj-js";
271
+
272
+ const attrs_table = [
273
+ SDATA(data_type_t.DTP_STRING, "url", sdata_flag_t.SDF_RD, "", "Server URL"),
274
+ SDATA(data_type_t.DTP_INTEGER, "timeout", sdata_flag_t.SDF_RD, 0, "Timeout ms"),
275
+ SDATA(data_type_t.DTP_BOOLEAN, "connected", sdata_flag_t.SDF_RD, false, "Connection state"),
276
+ SDATA(data_type_t.DTP_STRING, "saved_key", sdata_flag_t.SDF_PERSIST, "", "Persisted value"),
277
+ SDATA_END()
278
+ ];
279
+ ```
280
+
281
+ **Data types (`data_type_t`):** `DTP_STRING`, `DTP_BOOLEAN`, `DTP_INTEGER`, `DTP_REAL`, `DTP_LIST`, `DTP_DICT`, `DTP_JSON`, `DTP_POINTER`
282
+
283
+ **Flags (`sdata_flag_t`):** `SDF_RD`, `SDF_WR`, `SDF_PERSIST`, `SDF_STATS`, `SDF_AUTHZ_R`, `SDF_AUTHZ_W`, `SDF_REQUIRED`, `SDF_VOLATIL`
284
+
285
+ ### Events & Pub-Sub
286
+
287
+ GObjects communicate via events with JSON payloads:
288
+
289
+ ```javascript
290
+ // Send directly to a specific GObject
291
+ gobj_send_event(target_gobj, "EV_CONNECT", { url: "ws://..." }, src_gobj);
292
+
293
+ // Publish to all subscribers
294
+ gobj_publish_event(gobj, "EV_DATA_READY", { data: [] });
295
+
296
+ // Subscribe to events from a source
297
+ gobj_subscribe_event(source_gobj, "EV_DATA_READY", {}, subscriber_gobj);
298
+
299
+ // Unsubscribe
300
+ gobj_unsubscribe_event(source_gobj, "EV_DATA_READY", {}, subscriber_gobj);
301
+ ```
302
+
303
+ ### GObject Tree (Yuno)
304
+
305
+ 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).
306
+
307
+ ```
308
+ Yuno
309
+ ├── Service "auth" (C_IEVENT_CLI)
310
+ ├── Service "main" (C_MY_SERVICE)
311
+ │ └── Child "sub" (C_SOME_GCLASS)
312
+ └── Service "timer" (C_TIMER)
313
+ ```
314
+
315
+ ---
316
+
317
+ ## Public API
318
+
319
+ ### Framework Bootstrap
320
+
321
+ ```javascript
322
+ gobj_start_up(
323
+ jn_global_settings, // JSON object or null
324
+ load_persistent_attrs_fn, // fn(gobj, keys)
325
+ save_persistent_attrs_fn, // fn(gobj, keys)
326
+ remove_persistent_attrs_fn, // fn(gobj, keys)
327
+ list_persistent_attrs_fn, // fn(gobj, keys)
328
+ global_command_parser_fn, // fn or null
329
+ global_stats_parser_fn // fn or null
330
+ )
331
+ ```
332
+
333
+ ### GClass Registration
334
+
335
+ ```javascript
336
+ gclass_create(name, event_types, states, gmt, lmt, attrs_table,
337
+ private_data, authz_table, command_table,
338
+ s_user_trace_level, gclass_flag)
339
+
340
+ gclass_find_by_name(name) // → GClass or null
341
+ gclass_check_fsm(gclass) // validate FSM (returns error count)
342
+ gclass_add_event_type(gclass, event_name, flag)
343
+ gclass_add_state(gclass, state_name)
344
+ gclass_add_ev_action(gclass, state_name, event_name, action_fn, next_state)
345
+ ```
346
+
347
+ ### GObject Lifecycle
348
+
349
+ ```javascript
350
+ // Creation
351
+ gobj_create(name, gclass_name, attrs, parent) // generic child
352
+ gobj_create_yuno(name, gclass_name, attrs) // application root
353
+ gobj_create_service(name, gclass_name, attrs, yuno) // named service
354
+ gobj_create_default_service(name, gclass_name, attrs, yuno)
355
+ gobj_create_volatil(name, gclass_name, attrs, parent)
356
+ gobj_create_pure_child(name, gclass_name, attrs, parent)
357
+
358
+ // Start / Stop
359
+ gobj_start(gobj) // calls mt_start
360
+ gobj_stop(gobj) // calls mt_stop
361
+ gobj_start_children(gobj)
362
+ gobj_stop_children(gobj)
363
+ gobj_start_tree(gobj)
364
+ gobj_stop_tree(gobj)
365
+
366
+ // Play / Pause (applied to default service)
367
+ gobj_play(gobj)
368
+ gobj_pause(gobj)
369
+
370
+ // Destroy
371
+ gobj_destroy(gobj)
372
+
373
+ // Status
374
+ gobj_is_running(gobj) // → boolean
375
+ gobj_is_playing(gobj) // → boolean
376
+ gobj_is_destroying(gobj) // → boolean
377
+ gobj_is_volatil(gobj) // → boolean
378
+ gobj_is_pure_child(gobj) // → boolean
379
+ ```
380
+
381
+ ### State Machine
382
+
383
+ ```javascript
384
+ gobj_current_state(gobj) // → state name string
385
+ gobj_change_state(gobj, new_state) // trigger FSM transition
386
+ gobj_has_event(gobj, event, event_flag) // → boolean; pass 0 to ignore flag
387
+ gobj_has_output_event(gobj, event, event_flag) // same; checks EVF_OUTPUT_EVENT
388
+ ```
389
+
390
+ ### Attribute Access
391
+
392
+ ```javascript
393
+ // Generic read/write
394
+ // `path` accepts back-tick navigation to walk into child gobjs: "child`subattr"
395
+ gobj_read_attr (gobj, name, src)
396
+ gobj_write_attr(gobj, path, value, src)
397
+ gobj_read_attrs (gobj, include_flag, src) // → JSON object of matching attrs
398
+ gobj_write_attrs(gobj, kw, include_flag, src) // include_flag filters which are written
399
+ gobj_has_attr(gobj, name) // → boolean
400
+
401
+ // Typed reads — name only (no src)
402
+ gobj_read_bool_attr (gobj, name)
403
+ gobj_read_integer_attr(gobj, name)
404
+ gobj_read_str_attr (gobj, name)
405
+ gobj_read_pointer_attr(gobj, name)
406
+
407
+ // Typed writes — (name, value), no src
408
+ gobj_write_bool_attr (gobj, name, value)
409
+ gobj_write_integer_attr(gobj, name, value)
410
+ gobj_write_str_attr (gobj, name, value)
411
+ ```
412
+
413
+ ### Event System
414
+
415
+ ```javascript
416
+ // Sending
417
+ gobj_send_event(gobj, event, kw, src) // direct send
418
+ gobj_publish_event(gobj, event, kw) // publish to subscribers
419
+ gobj_post_event(gobj, event, kw, src) // post (deferred)
420
+
421
+ // Subscriptions
422
+ gobj_subscribe_event (publisher, event, kw, subscriber)
423
+ gobj_unsubscribe_event(publisher, event, kw, subscriber)
424
+ gobj_unsubscribe_list (publisher, dl_subs, force) // force=true also removes hard subs
425
+ gobj_find_subscriptions(publisher, event, kw, subscriber)
426
+ gobj_list_subscriptions(gobj)
427
+ gobj_find_subscribings (subscriber, event, kw, publisher)
428
+
429
+ // Commands & Stats
430
+ gobj_command(gobj, command, kw, src) // → response JSON
431
+ gobj_stats(gobj, stats, kw, src) // → response JSON
432
+ build_command_response(gobj, result, comment, schema, data)
433
+ build_stats_response(gobj, result, comment, schema, data)
434
+ ```
435
+
436
+ ### Hierarchy & Navigation
437
+
438
+ ```javascript
439
+ gobj_parent(gobj)
440
+ gobj_yuno() // → the Yuno root
441
+ gobj_default_service() // → default service under yuno
442
+
443
+ gobj_name(gobj)
444
+ gobj_short_name(gobj)
445
+ gobj_full_name(gobj)
446
+ gobj_gclass_name(gobj)
447
+ gobj_yuno_name(gobj)
448
+ gobj_yuno_role(gobj)
449
+ gobj_yuno_id(gobj)
450
+
451
+ gobj_find_child(gobj, kw_filter)
452
+ gobj_find_service(name, verbose)
453
+ gobj_find_gobj(gobj, path)
454
+ gobj_search_path(gobj, path)
455
+
456
+ gobj_walk_gobj_children(gobj, walk_type, cb_walking, user_data, user_data2)
457
+ gobj_walk_gobj_children_tree(gobj, walk_type, cb_walking, user_data, user_data2)
458
+ ```
459
+
460
+ ### Persistence
461
+
462
+ Backed by `localStorage` (browser) via `dbsimple.js`:
463
+
464
+ ```javascript
465
+ // Low-level (dbsimple)
466
+ db_load_persistent_attrs(gobj, keys)
467
+ db_save_persistent_attrs(gobj, keys)
468
+ db_remove_persistent_attrs(gobj, keys)
469
+ db_list_persistent_attrs(gobj, keys)
470
+
471
+ // Via gobj (delegates to registered persistence fns)
472
+ gobj_load_persistent_attrs(gobj, keys)
473
+ gobj_save_persistent_attrs(gobj, keys)
474
+ gobj_remove_persistent_attrs(gobj, keys)
475
+ gobj_list_persistent_attrs(gobj, keys)
476
+ ```
477
+
478
+ Attributes marked `SDF_PERSIST` are automatically saved/loaded.
479
+
480
+ ### Helpers & Utilities
481
+
482
+ ```javascript
483
+ // JSON / Object operations
484
+ json_deep_copy(obj)
485
+ json_is_identical(a, b)
486
+ json_object_update(dst, src) // merge src into dst
487
+ json_object_update_existing(dst, src) // only existing keys
488
+ json_object_update_missing(dst, src) // only missing keys
489
+ json_object_get(obj, key)
490
+ json_object_set(obj, key, value)
491
+ json_object_del(obj, key)
492
+ json_array_append(arr, value)
493
+ json_array_remove(arr, index)
494
+ json_array_extend(dst, src)
495
+ json_object_size(obj)
496
+ json_array_size(arr)
497
+
498
+ // Type checking
499
+ is_object(v), is_array(v), is_string(v), is_number(v),
500
+ is_boolean(v), is_null(v), is_date(v), is_function(v), is_gobj(v)
501
+
502
+ // Keyword (kw) operations
503
+ //
504
+ // Most kw_* helpers take `gobj` as first argument (for error logging)
505
+ // and a dot/back-tick path instead of a single key. Exceptions that do
506
+ // NOT take gobj: kw_has_key, kw_pop, kw_match_simple.
507
+
508
+ kw_has_key(kw, key) // → boolean
509
+ kw_pop(kw1, kw2) // delete from kw1 the keys listed in kw2
510
+ kw_delete(gobj, kw, path)
511
+ kw_find_path(gobj, kw, path, verbose) // back-tick path: "a`b`c"
512
+
513
+ kw_get_bool(gobj, kw, path, default_value, flag)
514
+ kw_get_int (gobj, kw, path, default_value, flag)
515
+ kw_get_real(gobj, kw, path, default_value, flag)
516
+ kw_get_str (gobj, kw, path, default_value, flag)
517
+ kw_get_dict(gobj, kw, path, default_value, flag)
518
+ kw_get_list(gobj, kw, path, default_value, flag)
519
+ kw_get_dict_value(gobj, kw, path, default_value, flag)
520
+
521
+ kw_set_dict_value(gobj, kw, path, value)
522
+ kw_set_subdict_value(gobj, kw, path, key, value)
523
+
524
+ kw_match_simple(kw, filter) // → boolean
525
+ kw_select (gobj, kw, jn_filter, match_fn) // filter list
526
+ kw_collect(gobj, kw, jn_filter, match_fn) // extract subset
527
+ kw_clone_by_keys (gobj, kw, keys, verbose)
528
+ kw_clone_by_not_keys(gobj, kw, keys, verbose)
529
+
530
+ // Flags: KW_REQUIRED, KW_CREATE, KW_EXTRACT, KW_RECURSIVE, KW_WILD_NUMBER
531
+
532
+ // Local storage helpers (browser localStorage, no "store" parameter)
533
+ kw_get_local_storage_value(key, default_value, create) // create=false
534
+ kw_set_local_storage_value(key, value)
535
+ kw_remove_local_storage_value(key)
536
+
537
+ // Misc utilities
538
+ current_timestamp() // ISO string
539
+ get_now() // Date object
540
+ node_uuid() // generate UUID
541
+ parseBoolean(v) // coerce to boolean
542
+ empty_string(s) // true if null/undefined/""
543
+ str_in_list(list, str, ci) // case-insensitive optional
544
+ index_in_list(list, value)
545
+ delete_from_list(list, value)
546
+ jwtDecode(token) // → header/payload/signature
547
+ jwt2json(token) // → payload JSON
548
+ debounce(fn, delay)
549
+ timeTracker()
550
+
551
+ // Inter-event message stack
552
+ msg_iev_push_stack (gobj, kw, stack, jn_data) // push jn_data onto named stack
553
+ msg_iev_get_stack (gobj, kw, stack, verbose) // peek top of named stack
554
+ msg_iev_set_msg_type(gobj, kw, msg_type) // "" to delete
555
+ msg_iev_get_msg_type(gobj, kw)
556
+ msg_iev_write_key(kw, key, value) // no gobj
557
+ msg_iev_read_key (kw, key) // no gobj
558
+ ```
559
+
560
+ ### Logging
561
+
562
+ All log functions take a single `format` argument and use `printf`-style
563
+ substitution (`%s`, `%d`, `%i`, `%f`, `%o`/`%O`). There is no `gobj` or
564
+ error-code argument (that's the C API — the JS runtime is simpler).
565
+
566
+ ```javascript
567
+ log_error(format, ...args) // red, prefixed "ERROR"
568
+ log_warning(format, ...args) // yellow, prefixed "WARNING"
569
+ log_info(format, ...args) // cyan, prefixed "INFO"
570
+ log_debug(format, ...args) // silver, prefixed "DEBUG"
571
+ trace_msg(format, ...args) // cyan, prefixed "MSG"
572
+ trace_json(json, msg) // dir-dump a JSON object
573
+
574
+ // Redirect error/warning output to a single remote handler
575
+ // (info/debug always go to the browser console)
576
+ set_remote_log_functions(remote_log_fn) // fn(message) — single function
577
+ ```
578
+
579
+ ### String Formatting
580
+
581
+ ```javascript
582
+ sprintf(format, ...args) // printf-style
583
+ vsprintf(fmt, argv) // variadic version
584
+ ```
585
+
586
+ Format specifiers: `%s` `%d` `%i` `%f` `%e` `%g` `%o` `%x` `%X` `%b` `%c` `%j` (JSON) `%t` (boolean) `%T` (type) `%v` (value) `%u` (unsigned)
587
+
588
+ ---
589
+
590
+ ## Built-in GClasses
591
+
592
+ ### C_YUNO
593
+
594
+ The application root. Always the first GClass registered.
595
+
596
+ ```javascript
597
+ import { register_c_yuno } from "@yuneta/gobj-js";
598
+ register_c_yuno();
599
+ ```
600
+
601
+ Key attributes: `yuno_name`, `yuno_role`, `yuno_id`, `yuno_version`, `yuno_release`, `yuneta_version`, `required_services`, `tracing`, `start_date`, `node_uuid`, `__username__`
602
+
603
+ ### C_TIMER
604
+
605
+ Manages timeouts and periodic timers.
606
+
607
+ ```javascript
608
+ import { register_c_timer, set_timeout, set_timeout_periodic, clear_timeout } from "@yuneta/gobj-js";
609
+ register_c_timer();
610
+
611
+ // One-shot timeout (ms)
612
+ set_timeout(timer_gobj, 5000);
613
+
614
+ // Periodic timeout
615
+ set_timeout_periodic(timer_gobj, 1000);
616
+
617
+ // Cancel
618
+ clear_timeout(timer_gobj);
619
+ ```
620
+
621
+ Events published: `EV_TIMEOUT`, `EV_TIMEOUT_PERIODIC`
622
+
623
+ Attributes: `subscriber`, `periodic`, `msec`
624
+
625
+ ### C_IEVENT_CLI
626
+
627
+ Inter-event client — proxies a remote Yuneta service over WebSocket so it looks like a local GObject. Used to communicate with backend yunos.
628
+
629
+ ```javascript
630
+ import { register_c_ievent_cli } from "@yuneta/gobj-js";
631
+ register_c_ievent_cli();
632
+
633
+ let remote = gobj_create_service("backend", "C_IEVENT_CLI", {
634
+ url: "ws://localhost:1991",
635
+ wanted_yuno_role: "agent",
636
+ wanted_yuno_service: "agent",
637
+ jwt: "...",
638
+ }, gobj_yuno());
639
+ ```
640
+
641
+ Key attributes: `url`, `jwt`, `wanted_yuno_role`, `wanted_yuno_name`, `wanted_yuno_service`, `remote_yuno_role`, `remote_yuno_name`, `remote_yuno_service`
642
+
643
+ ---
644
+
645
+ ## TreeDB Helpers
646
+
647
+ Utilities for interacting with the Yuneta TreeDB (schema-driven graph database):
648
+
649
+ ```javascript
650
+ import {
651
+ treedb_hook_data_size,
652
+ treedb_decoder_fkey,
653
+ treedb_encoder_fkey,
654
+ treedb_decoder_hook,
655
+ treedb_get_field_desc,
656
+ template_get_field_desc,
657
+ create_template_record,
658
+ } from "@yuneta/gobj-js";
659
+
660
+ treedb_hook_data_size(value) // count with caching
661
+ treedb_decoder_fkey(col, fkey) // parse foreign key reference
662
+ treedb_encoder_fkey(col, fkey) // build fkey string "topic^id^hook"
663
+ treedb_decoder_hook(col, hook) // parse hook reference
664
+ treedb_get_field_desc(col) // build field descriptor from column
665
+ template_get_field_desc(key, value) // build field descriptor from template
666
+ create_template_record(template, kw) // instantiate from template
667
+ ```
668
+
669
+ ---
670
+
671
+ ## Writing a Custom GClass
672
+
673
+ ```javascript
674
+ import {
675
+ SDATA, SDATA_END,
676
+ data_type_t, sdata_flag_t,
677
+ gclass_create,
678
+ gobj_subscribe_event, gobj_yuno,
679
+ trace_msg,
680
+ } from "@yuneta/gobj-js";
681
+
682
+ const GCLASS_NAME = "C_MY_CLASS";
683
+
684
+ // 1. Attribute schema
685
+ const attrs_table = [
686
+ SDATA(data_type_t.DTP_STRING, "url", sdata_flag_t.SDF_RD, "", "Remote URL"),
687
+ SDATA(data_type_t.DTP_INTEGER, "retries", sdata_flag_t.SDF_RD, 3, "Max retries"),
688
+ SDATA_END()
689
+ ];
690
+
691
+ // 2. Private data (per-instance, copied from this template)
692
+ const PRIVATE_DATA = {
693
+ retry_count: 0,
694
+ };
695
+
696
+ // 3. Lifecycle methods
697
+ function mt_create(gobj) {
698
+ // instance created, attrs not yet populated
699
+ }
700
+ function mt_start(gobj) {
701
+ // subscribe to events from other gobjs here
702
+ gobj_subscribe_event(gobj_yuno(), "EV_TIMEOUT_PERIODIC", {}, gobj);
703
+ }
704
+ function mt_stop(gobj) { }
705
+ function mt_destroy(gobj) { }
706
+
707
+ // 4. Action functions
708
+ function ac_timeout(gobj, event, kw, src) {
709
+ trace_msg(`Tick! retry_count=${gobj.priv.retry_count}`);
710
+ gobj.priv.retry_count++;
711
+ return 0; // kw consumed
712
+ }
713
+
714
+ // 5. FSM
715
+ const st_idle = [
716
+ ["EV_TIMEOUT_PERIODIC", ac_timeout, null],
717
+ ];
718
+ const states = [
719
+ ["ST_IDLE", st_idle],
720
+ ];
721
+ const event_types = [
722
+ ["EV_TIMEOUT_PERIODIC", 0],
723
+ ];
724
+
725
+ // 6. Methods table
726
+ const gmt = { mt_create, mt_start, mt_stop, mt_destroy };
727
+
728
+ // 7. Register
729
+ function register_c_my_class() {
730
+ return gclass_create(
731
+ GCLASS_NAME,
732
+ event_types,
733
+ states,
734
+ gmt,
735
+ 0, // lmt (low-level methods)
736
+ attrs_table,
737
+ PRIVATE_DATA,
738
+ 0, // authz_table
739
+ 0, // command_table
740
+ 0, // s_user_trace_level
741
+ 0 // gclass_flag
742
+ );
743
+ }
744
+
745
+ export { register_c_my_class };
746
+ ```
747
+
748
+ ---
749
+
750
+ ## Source Layout
751
+
752
+ ```
753
+ kernel/js/gobj-js/
754
+ ├── README.md ← this file
755
+ ├── package.json
756
+ ├── vite.config.js
757
+ ├── dist/ ← compiled outputs (ES, CJS, UMD, IIFE)
758
+ ├── tests/
759
+ │ └── kw_delete.test.js
760
+ └── src/
761
+ ├── index.js ← public API entry point (barrel export)
762
+ ├── gobj.js ← GObject/FSM runtime (~4 500 LOC)
763
+ ├── helpers.js ← utilities, JSON, logging (~3 300 LOC)
764
+ ├── c_ievent_cli.js ← remote service proxy (~1 350 LOC)
765
+ ├── lib_treedb.js ← TreeDB helpers (~ 540 LOC)
766
+ ├── c_timer.js ← Timer GClass (~ 330 LOC)
767
+ ├── c_yuno.js ← Yuno GClass (~ 290 LOC)
768
+ ├── dbsimple.js ← localStorage persistence (~ 140 LOC)
769
+ ├── sprintf.js ← printf-style formatting (~ 210 LOC)
770
+ ├── command_parser.js ← command parsing
771
+ └── stats_parser.js ← stats parsing
772
+ ```
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-js",
3
- "version": "7.1.4",
4
- "description": "Yuneta Simplified v7 in javascript version",
5
- "author": "Niyamaka",
3
+ "version": "7.2.1",
4
+ "description": "GObject + FSM runtime for JavaScript — event-driven components with typed attributes, pub/sub, and WebSocket inter-event client",
5
+ "author": "ArtGins",
6
6
  "license": "MIT",
7
7
  "type": "module",
8
8
  "main": "dist/gobj-js.cjs.js",