smsmslib 1.0.78 → 1.0.80

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.
@@ -197,3 +197,39 @@ export function sj_array_is_unique(a_src)
197
197
  }
198
198
 
199
199
 
200
+ /**
201
+ * Normalizes an array subscript (index) to ensure it falls within the valid
202
+ * range of [0, i_len - 1].
203
+ *
204
+ * This function supports circular indexing (wrap-around). For example, if i_len
205
+ * is 5, an index of 5 wraps around to 0, and an index of -1 wraps around to 4.
206
+ *
207
+ * @param {number} i_len [in]
208
+ * The length of the array or array-like object (e.g., string).
209
+ * Must be a positive integer.
210
+ *
211
+ * @param {number} i_sub [in]
212
+ * The subscript (index) to be normalized.
213
+ *
214
+ * @returns {number}
215
+ * The normalized index within the range [0, i_len - 1].
216
+ * Returns -1 if i_len is 0 or negative.
217
+ */
218
+ export function sj_subscript_norm(i_len, i_sub)
219
+ {
220
+ let i_r = - 1;
221
+
222
+ if (0 < i_len)
223
+ {
224
+ i_r = i_sub % i_len;
225
+
226
+ if (i_r < 0)
227
+ {
228
+ i_r += i_len;
229
+ }
230
+ }
231
+
232
+ return i_r;
233
+ }
234
+
235
+
@@ -0,0 +1,116 @@
1
+ /**
2
+ * @file
3
+ * Provides utility functions for **deeply nested property access and
4
+ * modification** targeting an object's Own Properties.
5
+ *
6
+ * @module javascr/system/deep
7
+ */
8
+
9
+ /** ---------------------------------------------------------------------------
10
+ * Imports.
11
+ * --------------------------------------------------------------------------- */
12
+
13
+ import
14
+ {
15
+ sj_prop_get_own,
16
+ sj_prop_set_own,
17
+ PropRet_t,
18
+ }
19
+ from "prop.js";
20
+
21
+
22
+ /** ---------------------------------------------------------------------------
23
+ * Functions.
24
+ * --------------------------------------------------------------------------- */
25
+
26
+ /**
27
+ * Updates the value of a deeply nested **own property** using an
28
+ * array of keys.
29
+ *
30
+ * @param {any} dst [in]
31
+ * The root object or array to traverse.
32
+ *
33
+ * @param {(string|number)[]} a_prop [in]
34
+ * An array of property names or indices representing the path to the target.
35
+ * Must not be empty. Each element must correspond to an existing own property,
36
+ * and the final property must be writable.
37
+ *
38
+ * @param {any} value [in]
39
+ * The new value to assign to the target property.
40
+ *
41
+ * @returns {boolean}
42
+ * true if the traversal was successful and the target property was updated;
43
+ * otherwise, false. Returns false if a_prop is empty or not an array.
44
+ */
45
+ export function sj_deep_set_own(dst, a_prop, value)
46
+ {
47
+ let dst_ref = dst;
48
+ let b_ok = Array.isArray(a_prop) && (0 < a_prop.length);
49
+
50
+ for (let i_depth = 0; b_ok && (i_depth < a_prop.length); i_depth++)
51
+ {
52
+ const prop = a_prop[i_depth];
53
+
54
+ if (i_depth < (a_prop.length - 1))
55
+ {
56
+ const o_ret = sj_prop_get_own(dst_ref, prop);
57
+
58
+ b_ok = o_ret && o_ret.b_ok;
59
+ dst_ref = o_ret.value;
60
+ }
61
+ else
62
+ {
63
+ b_ok = sj_prop_set_own(dst_ref, prop, value);
64
+ }
65
+ }
66
+
67
+ return b_ok;
68
+ }
69
+
70
+
71
+ /**
72
+ * Retrieves a value from a deeply nested property using an array of keys.
73
+ *
74
+ * @param {any} src [in]
75
+ * The object, array, or array-like object to start the traversal.
76
+ *
77
+ * @param {(string|number)[]} a_prop [in]
78
+ * An array of property names or indices representing the path to the target
79
+ * property. If this array is empty, the function returns the `src` itself
80
+ * wrapped in PropRet_t.
81
+ *
82
+ * @returns {object|null}
83
+ * An instance of PropRet_t containing the following properties, or null
84
+ * if the instance creation fails:
85
+ * - b_ok : true if the property value was retrieved successfully;
86
+ * otherwise, false.
87
+ * - value: The retrieved property value if b_ok is true; otherwise, undefined.
88
+ *
89
+ * Example:
90
+ * - If a_prop is empty: returns { b_ok: true, value: src }.
91
+ * - If successful: returns { b_ok: true, value: [found value] }.
92
+ * - If not found or invalid: returns { b_ok: false, value: undefined }.
93
+ */
94
+ export function sj_deep_get_own(src, a_prop)
95
+ {
96
+ let src_ref = src;
97
+ let b_ok = Array.isArray(a_prop);
98
+ let o_ret = PropRet_t(b_ok, src_ref);
99
+
100
+ b_ok = o_ret && o_ret.b_ok;
101
+
102
+ for (let i_depth = 0; b_ok && (i_depth < a_prop.length); i_depth++)
103
+ {
104
+ o_ret = sj_prop_get_own(src_ref, a_prop[i_depth]);
105
+ b_ok = o_ret && o_ret.b_ok;
106
+
107
+ if (b_ok)
108
+ {
109
+ src_ref = o_ret.value;
110
+ }
111
+ }
112
+
113
+ return o_ret;
114
+ }
115
+
116
+
@@ -0,0 +1,245 @@
1
+ /**
2
+ * @file
3
+ * Provides utility functions for **non-recursive deep traversal** of objects and
4
+ * arrays.
5
+ *
6
+ * @module javascr/system/deepwalk
7
+ */
8
+
9
+ /** ---------------------------------------------------------------------------
10
+ * Imports.
11
+ * --------------------------------------------------------------------------- */
12
+
13
+ import
14
+ {
15
+ go_typeof,
16
+ sj_is_object,
17
+ }
18
+ from "./type.js";
19
+
20
+ import
21
+ {
22
+ sj_array_new,
23
+ }
24
+ from "./arraynew.js";
25
+
26
+ import
27
+ {
28
+ sj_array_push,
29
+ }
30
+ from "./arraypush.js";
31
+
32
+ import
33
+ {
34
+ sj_prop_get_own,
35
+ sj_prop_set_own,
36
+ }
37
+ from "./prop.js";
38
+
39
+ import
40
+ {
41
+ go_tree_walk_ret,
42
+ sj_tree_walk,
43
+ }
44
+ from "./tree.js";
45
+
46
+
47
+ /** ---------------------------------------------------------------------------
48
+ * Functions.
49
+ * --------------------------------------------------------------------------- */
50
+
51
+ /**
52
+ * Performs a deep traversal of an object or array.
53
+ *
54
+ * This function initiates a non-recursive tree walk starting from the given
55
+ * `value`. It uses a stack-based approach to navigate through nested
56
+ * properties up to a specified depth.
57
+ *
58
+ * @param {any} value [in]
59
+ * The root value to start the traversal from.
60
+ *
61
+ * @param {number} i_depth [in]
62
+ * The maximum depth to traverse (1-based).
63
+ * If 1, only the root is visited. If 0, the traversal is unlimited.
64
+ *
65
+ * @param {function} f_cb [in]
66
+ * The user-defined callback function called at each node.
67
+ * Expected signature: `f_cb(ao_TreeStack, i_depth, user)`
68
+ * - ao_TreeStack: The current traversal stack.
69
+ * - i_depth: The maximum depth limit passed to `sj_deep_walk`.
70
+ * - user: The user data provided.
71
+ * Must return `true` to continue or `false` to abort.
72
+ *
73
+ * @param {any} user [in]
74
+ * Arbitrary user data passed to the callback function.
75
+ *
76
+ * @returns {number}
77
+ * Status code defined in `go_tree_walk_ret`. Same as `sj_tree_walk`.
78
+ * @see sj_tree_walk
79
+ */
80
+ export function sj_deep_walk(value, i_depth, f_cb, user)
81
+ {
82
+ let i_ret = go_tree_walk_ret.i_noncb;
83
+
84
+ if (typeof(f_cb) === go_typeof.s_function)
85
+ {
86
+ const o_user = sj_new_lit(() => ({i_depth, f_cb, user}));
87
+
88
+ i_ret = go_tree_walk_ret.i_fatal;
89
+
90
+ if (o_user)
91
+ {
92
+ const o_root = deep_walk_node_t(value);
93
+
94
+ if (o_root)
95
+ {
96
+ i_ret = sj_tree_walk(deep_walk_cb, o_root, o_user);
97
+ }
98
+ }
99
+ }
100
+
101
+ return i_ret;
102
+ }
103
+
104
+
105
+ /**
106
+ * Callback function for `sj_tree_walk` to perform a deep traversal of an object.
107
+ *
108
+ * This function serves two primary purposes:
109
+ * 1. **Node Adaptation**: It wraps the next child value into a node object
110
+ * using `deep_walk_node_t` for the next step of traversal.
111
+ * 2. **User Callback Execution**: It triggers the user-provided callback
112
+ * (`o_user.f_cb`) in "leading order".
113
+ *
114
+ * @param {object[]} ao_TreeStack [in]
115
+ * The stack of tree nodes representing the current traversal state.
116
+ *
117
+ * @param {boolean} from_child [in]
118
+ * Any information returning from a child node. (Unused)
119
+ *
120
+ * @param {object} o_user [in]
121
+ * A context object containing:
122
+ * - i_depth: Maximum traversal depth (1-based, 0 for unlimited).
123
+ * - f_cb : The user-provided callback function to execute at each node.
124
+ * - user : Arbitrary user data to pass to `f_cb`.
125
+ *
126
+ * @returns {boolean}
127
+ * true to continue the traversal; otherwise false to abort.
128
+ */
129
+ function deep_walk_cb(ao_TreeStack, from_child, o_user)
130
+ {
131
+ let b_continue = true;
132
+ const b_depth = (o_user.i_depth < 1) || (ao_TreeStack.length <= o_user.i_depth);
133
+ const o_TreeStack = ao_TreeStack.at(- 1); /* tail */
134
+ const o_node = o_TreeStack.node;
135
+
136
+ o_TreeStack.child = null;
137
+
138
+ if (b_depth && o_node.as_key && (o_TreeStack.i_child < o_node.as_key.length))
139
+ {
140
+ const s_key = o_node.as_key[o_TreeStack.i_child];
141
+
142
+ o_TreeStack.child = deep_walk_node_t(o_node.value[s_key]);
143
+ b_continue &&= (!!o_TreeStack.child);
144
+ }
145
+
146
+ if (b_continue && (o_TreeStack.i_child === 0)) /* If leading order */
147
+ {
148
+ b_continue = o_user.f_cb(ao_TreeStack, o_user.i_depth, o_user.user);
149
+ }
150
+
151
+ return b_continue;
152
+ }
153
+
154
+
155
+ /**
156
+ * Retrieves the property path (array of keys/indices) leading to the current
157
+ * node in a deep traversal.
158
+ *
159
+ * This function reconstructs the path by iterating through the tree stack
160
+ * from the root up to the parent of the current node. It is designed to be
161
+ * called within a callback of `sj_deep_walk`.
162
+ *
163
+ * Since the last element of `ao_TreeStack` represents the current node itself,
164
+ * this function collects keys from all elements except the last to form the
165
+ * complete path.
166
+ *
167
+ * @param {object[]} ao_TreeStack [in]
168
+ * The stack of tree nodes representing the current traversal state.
169
+ *
170
+ * @returns {Array|null}
171
+ * An array of keys/indices representing the path to the current value;
172
+ * otherwise null if the stack is invalid or an internal error occurs.
173
+ * Note: Returns an empty array if the current node is the root.
174
+ */
175
+ export function sj_deep_path(ao_TreeStack)
176
+ {
177
+ let as_path = sj_array_new(0);
178
+ const b_arr = Array.isArray(ao_TreeStack);
179
+ let b_ok = b_arr && (0 < ao_TreeStack.length) && (!!as_path);
180
+
181
+ for (let i_stk = 0; b_ok && (i_stk < ao_TreeStack.length - 1); i_stk++)
182
+ {
183
+ let i_ret;
184
+ const o_TreeStack = ao_TreeStack[i_stk];
185
+ const o_ret = sj_deep_get_own(o_TreeStack, ["node", "as_key"]);
186
+
187
+ b_ok = false;
188
+
189
+ if ((!!o_ret) && o_ret.b_ok)
190
+ {
191
+ const as_key = o_ret.value;
192
+
193
+ if (as_key && (o_TreeStack.i_child < as_key.length))
194
+ {
195
+ i_ret = sj_array_push(as_path, as_key[o_TreeStack.i_child]);
196
+ b_ok = (0 < i_ret);
197
+ }
198
+ }
199
+ }
200
+
201
+ if (!b_ok)
202
+ {
203
+ as_path = null;
204
+ }
205
+
206
+ return as_path;
207
+ }
208
+
209
+
210
+ /**
211
+ * Creates a node object for deep traversal (tree walking).
212
+ *
213
+ * @param {any} value [in]
214
+ * The value to be wrapped in a node.
215
+ *
216
+ * @returns {object|null}
217
+ * A node object containing:
218
+ * - value : The original value.
219
+ * - b_array: True if the value is an array.
220
+ * - as_key : An array of own property keys if the value is an object;
221
+ * otherwise null.
222
+ * Returns null if the node object cannot be created.
223
+ */
224
+ function deep_walk_node_t(value)
225
+ {
226
+ let o_node = null;
227
+ let as_key = null;
228
+ const b_obj = sj_is_object(value);
229
+
230
+ if (b_obj)
231
+ {
232
+ as_key = sj_keys(value);
233
+ }
234
+
235
+ if ((!b_obj) || as_key)
236
+ {
237
+ const b_array = Array.isArray(value);
238
+
239
+ o_node = sj_new_lit(() => ({value, b_array, as_key}));
240
+ }
241
+
242
+ return o_node;
243
+ }
244
+
245
+
@@ -11,6 +11,8 @@ export * from "./arraynew.js";
11
11
  export * from "./arraypush.js";
12
12
  export * from "./arraysplice.js";
13
13
  export * from "./dataset.js";
14
+ export * from "./deep.js";
15
+ export * from "./deepwalk.js";
14
16
  export * from "./document.js";
15
17
  export * from "./error.js";
16
18
  export * from "./html.js";
@@ -254,10 +254,11 @@ export function sj_prop_set_own(o_any, prop, value)
254
254
  * The property name (non-empty string) or array index (non-negative integer)
255
255
  * to retrieve.
256
256
  *
257
- * @returns {object}
258
- * A literal object (via sj_new_lit) containing:
259
- * - b_ok : true if the property exists as an own property.
260
- * - value: The value of the property if b_ok is true; otherwise, undefined.
257
+ * @returns {object|null}
258
+ * An instance of PropRet_t containing the following properties, or null
259
+ * if the instance creation fails:
260
+ * - b_ok : true if the property exists as an own property; otherwise, false.
261
+ * - value: The retrieved property value if b_ok is true; otherwise, undefined.
261
262
  */
262
263
  export function sj_prop_get_own(o_any, prop)
263
264
  {
@@ -277,8 +278,8 @@ export function sj_prop_get_own(o_any, prop)
277
278
  }
278
279
  }
279
280
 
280
- const s_lit = sj_new_lit(() => ({b_ok, value}));
281
- return s_lit;
281
+ const o_ret = PropRet_t(b_ok, value);
282
+ return o_ret;
282
283
  }
283
284
 
284
285
 
@@ -297,8 +298,9 @@ export function sj_prop_get_own(o_any, prop)
297
298
  * The property name (non-empty string) or array index (non-negative integer)
298
299
  * to delete.
299
300
  *
300
- * @returns {object}
301
- * A literal object containing:
301
+ * @returns {object|null}
302
+ * An instance of PropRet_t containing the following properties, or null
303
+ * if the instance creation fails:
302
304
  * - b_ok : true if the property existed and was successfully deleted.
303
305
  * - value: The value of the property before deletion if b_ok is true;
304
306
  * otherwise, undefined.
@@ -324,8 +326,58 @@ export function sj_prop_delete_own(o_any, prop)
324
326
  }
325
327
  }
326
328
 
329
+ const o_ret = PropRet_t(b_ok, value);
330
+ return o_ret;
331
+ }
332
+
333
+
334
+ /**
335
+ * Creates a result object for property-related operations.
336
+ *
337
+ * @param {boolean} b_ok [in]
338
+ * True if the operation succeeded; otherwise, false.
339
+ *
340
+ * @param {any} value [in]
341
+ * The property value.
342
+ *
343
+ * @returns {object|null}
344
+ * A literal object containing the following properties, or null if the
345
+ * creation fails:
346
+ * - b_ok : Same as the argument.
347
+ * - value: Same as the argument.
348
+ */
349
+ export function PropRet_t(b_ok, value)
350
+ {
327
351
  const s_lit = sj_new_lit(() => ({b_ok, value}));
352
+
328
353
  return s_lit;
329
354
  }
330
355
 
331
356
 
357
+ /**
358
+ * Retrieves an array of a given object's own enumerable property names.
359
+ *
360
+ * @param {any} any [in]
361
+ * The object whose enumerable own properties are to be returned.
362
+ *
363
+ * @returns {string[] | null}
364
+ * string[]: All the enumerable properties of the given object.
365
+ * null : The input is not a valid object, or a runtime error occurred.
366
+ */
367
+ export function sj_keys(any)
368
+ {
369
+ let as_key = null;
370
+
371
+ try
372
+ {
373
+ as_key = Object.keys(any);
374
+ }
375
+ catch (o_err)
376
+ {
377
+ console.error(`${sj_keys.name}: ${o_err.message}`);
378
+ }
379
+
380
+ return as_key;
381
+ }
382
+
383
+
@@ -127,6 +127,8 @@ export const go_tree_walk_ret = Object.freeze(
127
127
  * - go_tree_walk_ret.i_noncb : Provided f_cb is not a function.
128
128
  * - go_tree_walk_ret.i_stop : Traversal was explicitly stopped by the callback.
129
129
  * - go_tree_walk_ret.i_nostack: Failed to allocate or push to the traversal stack.
130
+ * - go_tree_walk_ret.i_fatal : Fatal error(not issued by this function, but should
131
+ * be handled by the caller).
130
132
  */
131
133
  export function sj_tree_walk(f_cb, root, user)
132
134
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smsmslib",
3
- "version": "1.0.78",
3
+ "version": "1.0.80",
4
4
  "description": "Reusable functions for me.",
5
5
  "files": [
6
6
  "javascr/**/*.js",
@@ -19,6 +19,6 @@
19
19
  "author": "",
20
20
  "license": "ISC",
21
21
  "dependencies": {
22
- "smsmslib": "^1.0.78"
22
+ "smsmslib": "^1.0.80"
23
23
  }
24
24
  }