@avstantso/utils-names-tree 1.2.1 → 1.3.2

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
@@ -14,6 +14,7 @@ Type-safe tree structure utilities for generating hierarchical names, paths, and
14
14
  - **Preset configurations** - Built-in presets for common use cases (Names, URLs, i18n keys)
15
15
  - **Customizable kinds** - Define custom formatting methods for different output types
16
16
  - **Tree merging** - Combine multiple data sources into a single tree structure
17
+ - **Symbol-based root alias access** - Access `rootAlias` value via `NamesTree.symbolRootAlias` symbol, avoiding property name conflicts
17
18
  - **Prefix support** - Add prefixes to all generated values
18
19
  - Zero dependencies beyond the monorepo packages
19
20
 
@@ -40,9 +41,9 @@ import '@avstantso/utils-names-tree';
40
41
 
41
42
  // Use utilities from the global namespace
42
43
  const formTree = AVStantso.NamesTree.Names({
43
- form: '',
44
- label: '',
45
- placeholder: ''
44
+ form: 1,
45
+ label: 1,
46
+ placeholder: 1
46
47
  });
47
48
  ```
48
49
 
@@ -55,9 +56,9 @@ import { NamesTree } from '@avstantso/utils-names-tree';
55
56
 
56
57
  // Use imported NamesTree namespace
57
58
  const formTree = NamesTree.Names({
58
- form: '',
59
- label: '',
60
- placeholder: ''
59
+ form: 1,
60
+ label: 1,
61
+ placeholder: 1
61
62
  });
62
63
 
63
64
  console.log(`${formTree.label}`); // 'label'
@@ -65,6 +66,7 @@ console.log(`${formTree.label}`); // 'label'
65
66
 
66
67
  **Exported Members:**
67
68
  - `NamesTree` - Complete NamesTree utilities namespace (same as `AVStantso.NamesTree`)
69
+ - `NamesTree.symbolRootAlias` - Symbol for accessing the `rootAlias` value from tree instances
68
70
 
69
71
  ## API Reference
70
72
 
@@ -94,11 +96,11 @@ import { NamesTree } from '@avstantso/utils-names-tree';
94
96
 
95
97
  const data = {
96
98
  user: {
97
- profile: '',
98
- settings: ''
99
+ profile: 1,
100
+ settings: 1
99
101
  },
100
102
  admin: {
101
- dashboard: ''
103
+ dashboard: 1
102
104
  }
103
105
  };
104
106
 
@@ -116,37 +118,45 @@ console.log(tree.user.profile._path); // 'user/profile'
116
118
 
117
119
  ## Built-in Kinds
118
120
 
119
- NamesTree comes with several predefined kind methods that determine how tree paths are formatted.
120
-
121
- ### `name`
122
-
123
- Returns the last segment of the path (leaf node name).
124
-
125
- **Example:** `['user', 'profile']` becomes `'profile'`
126
-
127
- ### `path`
128
-
129
- Returns the full path with segments joined by `/`.
130
-
131
- **Example:** `['user', 'profile']` becomes `'user/profile'`
132
-
133
- ### `url`
134
-
135
- Returns the full path as a URL with leading `/`.
136
-
137
- **Example:** `['user', 'profile']` becomes `'/user/profile'`
138
-
139
- ### `i18n`
140
-
141
- Returns the full path with segments joined by `.` (dot notation).
142
-
143
- **Example:** `['schemas', 'login', 'required']` becomes `'schemas.login.required'`
144
-
145
- ### `domain`
146
-
147
- Returns the full path with segments joined by `.` (same as i18n).
148
-
149
- **Example:** `['api', 'v1', 'users']` becomes `'api.v1.users'`
121
+ NamesTree comes with 20+ predefined kind methods that determine how tree paths are formatted.
122
+
123
+ ### Basic Kinds
124
+
125
+ | Kind | Separator | Example Input | Example Output |
126
+ |------|-----------|---------------|----------------|
127
+ | `name` | - | `['user', 'profile']` | `'profile'` |
128
+ | `slash` / `path` | `/` | `['user', 'profile']` | `'user/profile'` |
129
+ | `backslash` | `\` | `['user', 'profile']` | `'user\profile'` |
130
+ | `dot` / `i18n` / `domain` | `.` | `['schemas', 'login']` | `'schemas.login'` |
131
+ | `dash` | `-` | `['btn', 'primary']` | `'btn-primary'` |
132
+ | `underscore` / `snake` | `_` | `['user', 'name']` | `'user_name'` |
133
+ | `doubleColon` / `namespace` | `::` | `['std', 'vector']` | `'std::vector'` |
134
+ | `colon` | `:` | `['a', 'b']` | `'a:b'` |
135
+ | `arrow` | `->` | `['a', 'b']` | `'a->b'` |
136
+ | `amp` | `&` | `['a', 'b']` | `'a&b'` |
137
+ | `hash` | `#` | `['a', 'b']` | `'a#b'` |
138
+ | `comma` / `csv` | `,` | `['a', 'b']` | `'a,b'` |
139
+ | `semicolon` | `;` | `['a', 'b']` | `'a;b'` |
140
+ | `space` / `title` | ` ` | `['hello', 'world']` | `'hello world'` |
141
+ | `pipe` | `\|` | `['a', 'b']` | `'a\|b'` |
142
+
143
+ ### Derived Kinds (with prefixes/transformations)
144
+
145
+ | Kind | Description | Example Input | Example Output |
146
+ |------|-------------|---------------|----------------|
147
+ | `url` | URL with leading `/` | `['user', 'profile']` | `'/user/profile'` |
148
+ | `query` | Query string | `['a', 'b']` | `'?a&b'` |
149
+ | `longArg` | CLI long argument | `['dry', 'run']` | `'--dry-run'` |
150
+ | `shortArg` | CLI short argument | `['v']` | `'-v'` |
151
+ | `envVar` | Environment variable | `['db', 'host']` | `'DB_HOST'` |
152
+ | `cssClass` | CSS class selector | `['btn', 'primary']` | `'.btn-primary'` |
153
+ | `cssId` | CSS ID selector | `['main', 'content']` | `'#main-content'` |
154
+ | `anchor` | HTML anchor | `['section']` | `'#section'` |
155
+ | `scoped` | Scoped package | `['org', 'pkg']` | `'@org/pkg'` |
156
+ | `param` | Route parameter (last) | `['users', 'id']` | `':id'` |
157
+ | `params` | Route parameters (all) | `['users', 'id']` | `':users/:id'` |
158
+ | `arg` | Template arg (last) | `['users', 'id']` | `'{id}'` |
159
+ | `args` | Template args (all) | `['users', 'id']` | `'{users}/{id}'` |
150
160
 
151
161
  ---
152
162
 
@@ -176,9 +186,9 @@ NamesTree.Names<T>(
176
186
  import { NamesTree } from '@avstantso/utils-names-tree';
177
187
 
178
188
  const form = {
179
- form: '',
180
- label: '',
181
- placeholder: ''
189
+ form: 1,
190
+ label: 1,
191
+ placeholder: 1
182
192
  };
183
193
 
184
194
  const formTree = NamesTree.Names(form);
@@ -197,12 +207,12 @@ console.log(formDict); // { form: 'form', label: 'label', placeholder: 'placehol
197
207
  import { NamesTree } from '@avstantso/utils-names-tree';
198
208
 
199
209
  const form = {
200
- form: '',
201
- label: '',
202
- placeholder: ''
210
+ form: 1,
211
+ label: 1,
212
+ placeholder: 1
203
213
  };
204
214
 
205
- const formTree = NamesTree.Names(form, { prefix: 'xxx-' });
215
+ const formTree = NamesTree.Names(form, { prefix: 'xxx-' } as const);
206
216
  const formDict = formTree.Name;
207
217
 
208
218
  console.log(formDict);
@@ -272,12 +282,12 @@ import { NamesTree } from '@avstantso/utils-names-tree';
272
282
  const validationKeys = NamesTree.I18ns({
273
283
  user: {
274
284
  email: {
275
- required: '',
276
- invalid: ''
285
+ required: 1,
286
+ invalid: 1
277
287
  },
278
288
  password: {
279
- required: '',
280
- tooShort: ''
289
+ required: 1,
290
+ tooShort: 1
281
291
  }
282
292
  }
283
293
  });
@@ -318,15 +328,15 @@ import { NamesTree } from '@avstantso/utils-names-tree';
318
328
 
319
329
  const Urls = {
320
330
  profile: {
321
- login: '',
322
- logout: '',
323
- settings: '',
324
- registration: '',
325
- reset: ''
331
+ login: 1,
332
+ logout: 1,
333
+ settings: 1,
334
+ registration: 1,
335
+ reset: 1
326
336
  },
327
337
  admin: {
328
- roles: '',
329
- users: ''
338
+ roles: 1,
339
+ users: 1
330
340
  }
331
341
  };
332
342
 
@@ -373,7 +383,7 @@ const Urls = { profile: { settings: '' } };
373
383
  const UrlsTree = NamesTree.Urls(Urls);
374
384
 
375
385
  // Get array of trees, one per kind
376
- const [NAMES, PATHS, URLS] = UrlsTree.splitted;
386
+ const [NAMES, PATHS, URLS] = UrlsTree.$splitted;
377
387
 
378
388
  console.log(`${NAMES.profile.settings}`); // 'settings'
379
389
  console.log(NAMES.profile.settings); // 'settings'
@@ -385,17 +395,73 @@ console.log(`${URLS.profile.settings}`); // '/profile/settings'
385
395
  console.log(URLS.profile.settings); // '/profile/settings'
386
396
  ```
387
397
 
398
+ **Using Root Alias:**
399
+ ```typescript
400
+ import { NamesTree } from '@avstantso/utils-names-tree';
401
+
402
+ const Urls = { profile: { settings: '' } };
403
+ const UrlsTree = NamesTree.Urls(Urls, { rootAlias: 'Home' } as const);
404
+
405
+ // Access root via alias
406
+ console.log(`${UrlsTree.Home}`); // ''
407
+ console.log(`${UrlsTree.Url.Home}`); // '/'
408
+
409
+ // Splitted trees also have the alias
410
+ const [NAMES, PATHS, URLS] = UrlsTree.$splitted;
411
+ console.log(`${URLS.Home}`); // '/'
412
+
413
+ // Access rootAlias value via Symbol (avoids property name conflicts)
414
+ console.log(UrlsTree[NamesTree.symbolRootAlias]); // 'Home'
415
+ ```
416
+
417
+ **Using Default Kind:**
418
+ ```typescript
419
+ import { NamesTree } from '@avstantso/utils-names-tree';
420
+
421
+ const data = { profile: { settings: '' } };
422
+
423
+ // Default toString() uses first kind ('name')
424
+ const tree1 = NamesTree(data, 'name', 'path', 'url');
425
+ console.log(`${tree1.profile.settings}`); // 'settings'
426
+
427
+ // Override default toString() to use 'url' kind
428
+ const tree2 = NamesTree(data, { defaultKind: 'url' } as const, 'name', 'path', 'url');
429
+ console.log(`${tree2.profile.settings}`); // '/profile/settings'
430
+ ```
431
+
432
+ ---
433
+
434
+ ### NamesTree.symbolRootAlias
435
+
436
+ A Symbol property for safely accessing the `rootAlias` value from a tree instance. Using a Symbol avoids conflicts with tree data properties.
437
+
438
+ **Type:** `typeof symbolRootAlias` (unique symbol)
439
+
440
+ **Example:**
441
+ ```typescript
442
+ import { NamesTree } from '@avstantso/utils-names-tree';
443
+
444
+ const Urls = { profile: { settings: '' } };
445
+ const UrlsTree = NamesTree.Urls(Urls, { rootAlias: 'Home' } as const);
446
+
447
+ // Access rootAlias value via symbol
448
+ console.log(UrlsTree[NamesTree.symbolRootAlias]); // 'Home'
449
+
450
+ // The symbol is NOT accessible as a regular property
451
+ console.log((UrlsTree as any).symbolRootAlias); // undefined
452
+ ```
453
+
388
454
  ---
389
455
 
390
456
  ## Advanced Features
391
457
 
392
458
  ### Tree Merging
393
459
 
394
- Combine multiple data sources into a single tree, useful for adding route handlers to URL structures.
460
+ Combine multiple data sources into a single tree. Can be used for extending basic trees or adding route handlers to URL structures.
395
461
 
396
462
  **Method:**
397
463
  ```typescript
398
- tree.merge<M>(data: M): NamesTree<T & M, Kinds>
464
+ tree.$merge<M>(data: M): NamesTree<T & M, Kinds>
399
465
  ```
400
466
 
401
467
  **Parameters:**
@@ -409,8 +475,8 @@ import { NamesTree } from '@avstantso/utils-names-tree';
409
475
 
410
476
  const Urls = {
411
477
  profile: {
412
- login: '',
413
- settings: ''
478
+ login: 1,
479
+ settings: 1
414
480
  }
415
481
  };
416
482
 
@@ -421,7 +487,7 @@ function profile() {
421
487
  return 123;
422
488
  }
423
489
 
424
- const merged = UrlsTree.merge({ profile });
490
+ const merged = UrlsTree.$merge({ profile });
425
491
 
426
492
  // Call the function
427
493
  console.log(merged.profile()); // 123
@@ -439,9 +505,9 @@ import express from 'express';
439
505
  const routes = {
440
506
  api: {
441
507
  users: {
442
- list: '',
443
- create: '',
444
- detail: ''
508
+ list: 1,
509
+ create: 1,
510
+ detail: 1
445
511
  }
446
512
  }
447
513
  };
@@ -456,7 +522,7 @@ const handlers = {
456
522
  }
457
523
  };
458
524
 
459
- const RoutesTree = NamesTree.Urls(routes).merge(handlers);
525
+ const RoutesTree = NamesTree.Urls(routes).$merge(handlers);
460
526
 
461
527
  const app = express();
462
528
 
@@ -476,7 +542,7 @@ Extract individual trees for each kind from a multi-kind tree.
476
542
 
477
543
  **Property:**
478
544
  ```typescript
479
- tree.splitted: Array<NamesTree<T, [Kind]>>
545
+ tree.$splitted: Array<NamesTree<T, [Kind]>>
480
546
  ```
481
547
 
482
548
  **Returns:** Array of single-kind trees in the order kinds were specified
@@ -485,10 +551,10 @@ tree.splitted: Array<NamesTree<T, [Kind]>>
485
551
  ```typescript
486
552
  import { NamesTree } from '@avstantso/utils-names-tree';
487
553
 
488
- const data = { user: { profile: '' } };
554
+ const data = { user: { profile: 1 } };
489
555
  const tree = NamesTree.Urls(data);
490
556
 
491
- const [names, paths, urls] = tree.splitted;
557
+ const [names, paths, urls] = tree.$splitted;
492
558
 
493
559
  console.log(`${names.user.profile}`); // 'profile'
494
560
  console.log(`${paths.user.profile}`); // 'user/profile'
@@ -514,34 +580,50 @@ NamesTree._RegKinds(kinds: Record<string, Kinds.Method>): void
514
580
  type Method = (path: string[], options?: Options) => string
515
581
  ```
516
582
 
583
+ **TypeScript Support:**
584
+
585
+ For proper type inference with custom kinds, extend the `KindsMeta` interface:
586
+
587
+ ```typescript
588
+ declare global {
589
+ namespace AVStantso {
590
+ namespace NamesTree {
591
+ interface KindsMeta {
592
+ bem: '__'; // separator or KindMeta object
593
+ }
594
+ }
595
+ }
596
+ }
597
+ ```
598
+
517
599
  **Example:**
518
600
  ```typescript
519
601
  import { NamesTree } from '@avstantso/utils-names-tree';
520
602
 
521
- // Register custom kind for CSS class names
603
+ // Register custom kind for BEM-style CSS (block__element)
522
604
  NamesTree._RegKinds({
523
- cssClass: (path, options) => {
605
+ bem: (path, options) => {
524
606
  const { prefix = '' } = options || {};
525
- return `${prefix}${path.join('__')}`; // BEM-style
607
+ return `${prefix}${path.join('__')}`;
526
608
  }
527
609
  });
528
610
 
529
611
  // Use custom kind
530
612
  const components = {
531
613
  button: {
532
- primary: '',
533
- secondary: ''
614
+ icon: 1,
615
+ label: 1
534
616
  }
535
617
  };
536
618
 
537
- const cssTree = NamesTree(components, 'cssClass');
619
+ const bemTree = NamesTree(components, 'bem');
538
620
 
539
- console.log(`${cssTree.button.primary}`); // 'button__primary'
540
- console.log(`${cssTree.button.secondary}`); // 'button__secondary'
621
+ console.log(`${bemTree.button.icon}`); // 'button__icon'
622
+ console.log(`${bemTree.button.label}`); // 'button__label'
541
623
 
542
624
  // With prefix
543
- const prefixedTree = NamesTree(components, { prefix: 'app-' }, 'cssClass');
544
- console.log(`${prefixedTree.button.primary}`); // 'app-button__primary'
625
+ const prefixedTree = NamesTree(components, { prefix: 'app-' } as const, 'bem');
626
+ console.log(`${prefixedTree.button.icon}`); // 'app-button__icon'
545
627
  ```
546
628
 
547
629
  **Example: GraphQL Field Paths**
@@ -556,9 +638,9 @@ NamesTree._RegKinds({
556
638
 
557
639
  const schema = {
558
640
  user: {
559
- firstName: '',
560
- lastName: '',
561
- emailAddress: ''
641
+ firstName: 1,
642
+ lastName: 1,
643
+ emailAddress: 1
562
644
  }
563
645
  };
564
646
 
@@ -578,14 +660,37 @@ Union type for valid tree source data.
578
660
 
579
661
  **Type Definition:**
580
662
  ```typescript
581
- type Source = Source.Root | string
663
+ type Source = TS.Type.Builtin // string | number | boolean | null | undefined | symbol | bigint
582
664
 
583
665
  namespace Source {
584
666
  type Root = object | Function
585
667
  }
586
668
  ```
587
669
 
588
- **Description:** Tree sources can be nested objects, functions, or string values at leaf nodes.
670
+ **Description:**
671
+ - `Source` — any built-in primitive type for leaf nodes (values are ignored)
672
+ - `Source.Root` — nested objects or functions for tree structure
673
+
674
+ Leaf node values are ignored — only the object structure (keys) matters for generating paths.
675
+
676
+ **Flexible Leaf Values:**
677
+ ```typescript
678
+ // All of these are equivalent - leaf values are ignored
679
+ const data1 = { user: { name: '' } };
680
+ const data2 = { user: { name: 1 } };
681
+ const data3 = { user: { name: true } };
682
+ const data4 = { user: { name: null } };
683
+
684
+ // Using numbers is shorter to type
685
+ const form = {
686
+ form: 1,
687
+ label: 1,
688
+ placeholder: 1
689
+ };
690
+
691
+ const tree = NamesTree.Names(form);
692
+ console.log(tree.Name); // { form: 'form', label: 'label', placeholder: 'placeholder' }
693
+ ```
589
694
 
590
695
  ---
591
696
 
@@ -610,11 +715,15 @@ Configuration options for tree creation.
610
715
  ```typescript
611
716
  interface Options {
612
717
  prefix?: string;
718
+ rootAlias?: string;
719
+ defaultKind?: Kind;
613
720
  }
614
721
  ```
615
722
 
616
723
  **Properties:**
617
724
  - `prefix` - Optional string to prepend to all generated values (default: `''`)
725
+ - `rootAlias` - Optional alias for tree root as additional property (e.g., `'Home'` for URL trees). The alias is `enumerable: true`, so it appears in `Object.keys()` and iterations
726
+ - `defaultKind` - Optional kind to use for default `toString()` output (defaults to first kind in tree)
618
727
 
619
728
  ---
620
729
 
@@ -626,17 +735,21 @@ Type-safe tree node structure.
626
735
  ```typescript
627
736
  type Node<
628
737
  T extends Source = Source,
738
+ O extends Options = Options,
629
739
  KDs extends readonly Kind[] = Kinds.All,
630
- S extends boolean = false
631
- > = NodeKinds<KDs> & Node.Data<T, KDs, S>
740
+ S extends boolean = false,
741
+ P extends TS.Keys = []
742
+ > = (S extends true ? unknown : _NodeKinds<O, KDs, P>) & Node.Data<T, O, KDs, S, P>
632
743
  ```
633
744
 
634
745
  **Type Parameters:**
635
746
  - `T` - Source data type
747
+ - `O` - Options type (for prefix inference)
636
748
  - `KDs` - Tuple of kinds available on this node
637
- - `S` - If true, allows string tail nodes (for splitted trees)
749
+ - `S` - If true, allows resolved string types for leaf nodes (for splitted trees)
750
+ - `P` - Path stack for compile-time kind resolution
638
751
 
639
- **Description:** Represents a tree node with properties for data access and kind-specific formatters.
752
+ **Description:** Represents a tree node with properties for data access and kind-specific formatters. The `P` parameter enables compile-time path tracking for precise type inference of kind accessors.
640
753
 
641
754
  ---
642
755
 
@@ -671,28 +784,56 @@ const urls = NamesTree.Urls(routeData);
671
784
  const urls = NamesTree(routeData, 'name', 'path', 'url');
672
785
  ```
673
786
 
674
- ### 2. Leverage Type Inference
787
+ ### 2. Use Short Leaf Values
675
788
 
676
- Let TypeScript infer types from your data structure for full type safety.
789
+ Leaf node values are ignored use `1` or any short value instead of empty strings for brevity.
790
+
791
+ ```typescript
792
+ // Verbose
793
+ const form1 = { name: '', email: '', password: '' };
794
+
795
+ // Concise - same result
796
+ const form2 = { name: 1, email: 1, password: 1 };
797
+
798
+ const tree = NamesTree.Names(form2);
799
+ console.log(tree.Name); // { name: 'name', email: 'email', password: 'password' }
800
+ ```
801
+
802
+ ### 3. Leverage Type Inference
803
+
804
+ TypeScript automatically infers key names as literal types — no `as const` needed for data structures.
677
805
 
678
806
  ```typescript
679
807
  const routes = {
680
- admin: { users: '', roles: '' }
681
- } as const; // Use 'as const' for literal types
808
+ admin: { users: 1, roles: 1 }
809
+ };
682
810
 
683
811
  const tree = NamesTree.Urls(routes);
684
812
 
685
813
  // TypeScript knows these properties exist
686
814
  tree.admin.users; // OK
687
815
  tree.admin.posts; // Error: Property 'posts' does not exist
816
+
817
+ // Kind accessors have precise literal types
818
+ tree.admin.users._url; // Type: '/admin/users'
688
819
  ```
689
820
 
690
- ### 3. Use String Coercion for Output
821
+ **Note:** Use `as const` for options when you need literal types:
822
+ ```typescript
823
+ // Options need 'as const' for precise type inference
824
+ const tree = NamesTree.Urls(data, { rootAlias: 'Home' } as const);
825
+ tree.Home; // Property 'Home' exists
826
+
827
+ const form = NamesTree.Names(data, { prefix: 'app-' } as const);
828
+ form.Name.user; // Type: 'app-user'
829
+ ```
830
+
831
+ ### 4. Use String Coercion for Output
691
832
 
692
833
  Trees automatically convert to strings via `toString()` method.
693
834
 
694
835
  ```typescript
695
- const tree = NamesTree.Names({ user: '' });
836
+ const tree = NamesTree.Names({ user: 1 });
696
837
 
697
838
  // Explicit string coercion
698
839
  console.log(`${tree.user}`); // 'user'
@@ -701,7 +842,7 @@ console.log(`${tree.user}`); // 'user'
701
842
  const url = `/api/${tree.user}`; // '/api/user'
702
843
  ```
703
844
 
704
- ### 4. Centralize Route Definitions
845
+ ### 5. Centralize Route Definitions
705
846
 
706
847
  Define routes once and derive all variations from a single source.
707
848
 
@@ -709,15 +850,15 @@ Define routes once and derive all variations from a single source.
709
850
  // routes.ts
710
851
  export const RouteStructure = {
711
852
  auth: {
712
- login: '',
713
- logout: '',
714
- register: ''
853
+ login: 1,
854
+ logout: 1,
855
+ register: 1
715
856
  },
716
857
  dashboard: {
717
- home: '',
718
- analytics: ''
858
+ home: 1,
859
+ analytics: 1
719
860
  }
720
- } as const;
861
+ };
721
862
 
722
863
  export const Routes = NamesTree.Urls(RouteStructure);
723
864
 
@@ -734,17 +875,17 @@ router.get(`${Routes.Url.auth.login}`, loginHandler);
734
875
  fetch(`${Routes.Path.dashboard.home}`);
735
876
  ```
736
877
 
737
- ### 5. Combine with Enums for Constants
878
+ ### 6. Combine with Enums for Constants
738
879
 
739
880
  Use trees for structured constants that need multiple representations.
740
881
 
741
882
  ```typescript
742
883
  const StatusStructure = {
743
- pending: '',
744
- active: '',
745
- completed: '',
746
- archived: ''
747
- } as const;
884
+ pending: 1,
885
+ active: 1,
886
+ completed: 1,
887
+ archived: 1
888
+ };
748
889
 
749
890
  const StatusTree = NamesTree.Names(StatusStructure);
750
891
 
@@ -768,17 +909,17 @@ import { NamesTree } from '@avstantso/utils-names-tree';
768
909
 
769
910
  const AppRoutes = NamesTree.Urls({
770
911
  public: {
771
- home: '',
772
- about: '',
773
- contact: ''
912
+ home: 1,
913
+ about: 1,
914
+ contact: 1
774
915
  },
775
916
  auth: {
776
- login: '',
777
- signup: ''
917
+ login: 1,
918
+ signup: 1
778
919
  },
779
920
  dashboard: {
780
- overview: '',
781
- settings: ''
921
+ overview: 1,
922
+ settings: 1
782
923
  }
783
924
  });
784
925
 
@@ -798,16 +939,16 @@ import { NamesTree } from '@avstantso/utils-names-tree';
798
939
  const i18nKeys = NamesTree.I18ns({
799
940
  common: {
800
941
  buttons: {
801
- save: '',
802
- cancel: '',
803
- delete: ''
942
+ save: 1,
943
+ cancel: 1,
944
+ delete: 1
804
945
  }
805
946
  },
806
947
  errors: {
807
948
  validation: {
808
- required: '',
809
- email: '',
810
- minLength: ''
949
+ required: 1,
950
+ email: 1,
951
+ minLength: 1
811
952
  }
812
953
  }
813
954
  });
@@ -823,11 +964,11 @@ t(`${i18nKeys.errors.validation.required}`); // 'errors.validation.required'
823
964
  import { NamesTree } from '@avstantso/utils-names-tree';
824
965
 
825
966
  const styles = NamesTree.Names({
826
- button: '',
827
- input: '',
828
- label: '',
829
- container: ''
830
- }, { prefix: 'app-' });
967
+ button: 1,
968
+ input: 1,
969
+ label: 1,
970
+ container: 1
971
+ }, { prefix: 'app-' } as const);
831
972
 
832
973
  // Use in JSX
833
974
  <button className={styles.Name.button}>Click Me</button>
@@ -844,13 +985,13 @@ import { NamesTree } from '@avstantso/utils-names-tree';
844
985
 
845
986
  const FormFields = NamesTree.Names({
846
987
  user: {
847
- firstName: '',
848
- lastName: '',
849
- email: '',
988
+ firstName: 1,
989
+ lastName: 1,
990
+ email: 1,
850
991
  address: {
851
- street: '',
852
- city: '',
853
- zipCode: ''
992
+ street: 1,
993
+ city: 1,
994
+ zipCode: 1
854
995
  }
855
996
  }
856
997
  });
@@ -861,6 +1002,68 @@ const FormFields = NamesTree.Names({
861
1002
  <input name={FormFields.Name.user.address.city} />
862
1003
  ```
863
1004
 
1005
+ ### Environment Variables
1006
+
1007
+ ```typescript
1008
+ import { NamesTree } from '@avstantso/utils-names-tree';
1009
+
1010
+ const envVars = NamesTree({
1011
+ db: {
1012
+ host: 1,
1013
+ port: 1,
1014
+ name: 1
1015
+ },
1016
+ api: {
1017
+ key: 1,
1018
+ secret: 1
1019
+ }
1020
+ }, 'envVar');
1021
+
1022
+ console.log(envVars.db.host._envVar); // 'DB_HOST'
1023
+ console.log(envVars.api.key._envVar); // 'API_KEY'
1024
+ ```
1025
+
1026
+ ### CLI Arguments
1027
+
1028
+ ```typescript
1029
+ import { NamesTree } from '@avstantso/utils-names-tree';
1030
+
1031
+ const cliArgs = NamesTree({
1032
+ verbose: 1,
1033
+ dry: { run: 1 },
1034
+ output: { file: 1 }
1035
+ }, 'longArg', 'shortArg');
1036
+
1037
+ console.log(cliArgs.verbose._longArg); // '--verbose'
1038
+ console.log(cliArgs.verbose._shortArg); // '-verbose'
1039
+ console.log(cliArgs.dry.run._longArg); // '--dry-run'
1040
+ ```
1041
+
1042
+ ### Route Parameters
1043
+
1044
+ ```typescript
1045
+ import { NamesTree } from '@avstantso/utils-names-tree';
1046
+
1047
+ const apiRoutes = NamesTree({
1048
+ users: {
1049
+ id: {
1050
+ posts: {
1051
+ postId: 1
1052
+ }
1053
+ }
1054
+ }
1055
+ }, 'url', 'params', 'args');
1056
+
1057
+ // Express-style route
1058
+ console.log(apiRoutes.users.id._params); // ':users/:id'
1059
+
1060
+ // OpenAPI-style route
1061
+ console.log(apiRoutes.users.id.posts.postId._args); // '{users}/{id}/{posts}/{postId}'
1062
+
1063
+ // Combined with url
1064
+ console.log(apiRoutes.users.id._url); // '/users/id'
1065
+ ```
1066
+
864
1067
  ---
865
1068
 
866
1069
  ## Requirements