@jackens/nnn 2025.12.29 → 2026.2.7

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 (4) hide show
  1. package/nnn.d.ts +35 -29
  2. package/nnn.js +26 -15
  3. package/package.json +4 -2
  4. package/readme.md +122 -86
package/nnn.d.ts CHANGED
@@ -126,7 +126,7 @@ export declare const new_escape: (escape_map: EscapeMap) => (template: TemplateS
126
126
  * The root DOM node to process. All descendant text nodes are corrected recursively,
127
127
  * except those inside `IFRAME`, `NOSCRIPT`, `PRE`, `SCRIPT`, `STYLE`, or `TEXTAREA` elements.
128
128
  */
129
- export declare const fix_typography: (node: Node) => void;
129
+ export declare const fix_pl_typography: (node: Node) => void;
130
130
  /**
131
131
  * Single argument type for the {@link h} and {@link s} helpers.
132
132
  */
@@ -307,30 +307,32 @@ export declare const is_string: (arg: unknown) => arg is string;
307
307
  */
308
308
  export declare const js_on_parse: (handlers: Record<PropertyKey, Function>, text: string) => any;
309
309
  /**
310
- * Creates a function that returns the appropriate Polish noun form based on a numeric value.
310
+ * Creates a function that returns the appropriate noun form based on a numeric value using `Intl.PluralRules`.
311
311
  *
312
- * Polish has three plural forms depending on the number:
313
- * - Singular: used for exactly 1.
314
- * - “Plural 2-4”: used for 2, 3, 4, 22, 23, 24, etc. (but not 12, 13, 14).
315
- * - “Plural 5+”: used for 0, 5–21, 25–31, etc.
312
+ * Different languages have different plural rules. The `Intl.PluralRules` API provides
313
+ * locale-aware plural category selection. Possible categories are:
316
314
  *
317
- * @param singular
315
+ * - `zero`: for zero items (used in some languages like Arabic, Latvian)
316
+ * - `one`: for singular (e.g., 1 item)
317
+ * - `two`: for dual (used in some languages like Arabic, Hebrew)
318
+ * - `few`: for small plurals (e.g., 2-4 in Polish)
319
+ * - `many`: for larger plurals (e.g., 5-21 in Polish)
320
+ * - `other`: fallback category (used by all languages)
318
321
  *
319
- * The singular form (e.g., “auto” for “car”).
322
+ * @param locale
320
323
  *
321
- * @param plural_2
324
+ * A BCP 47 language tag (e.g., `pl`, `en`).
322
325
  *
323
- * The form for 2, 3, 4 (e.g., “auta”).
326
+ * @param forms
324
327
  *
325
- * @param plural_5
326
- *
327
- * The form for 5+ (e.g., “aut”).
328
+ * An object mapping plural categories to noun forms. Not all categories need to be provided;
329
+ * if a category is missing, the function falls back to `other`, then to an empty string.
328
330
  *
329
331
  * @returns
330
332
  *
331
333
  * A function that takes a numeric value and returns the appropriate noun form.
332
334
  */
333
- export declare const new_noun_form: (singular: string, plural_2: string, plural_5: string) => (value: number) => string;
335
+ export declare const new_noun_form: (locale: string, forms: Partial<Record<Intl.LDMLPluralRule, string>>) => (value: number) => string;
334
336
  /**
335
337
  * Creates a new object containing only the specified keys from the source object.
336
338
  *
@@ -367,21 +369,6 @@ export declare const pick: <T, K extends keyof T>(ref: T, keys: K[]) => Pick<T,
367
369
  * A new object without the specified keys.
368
370
  */
369
371
  export declare const omit: <T, K extends keyof T>(ref: T, keys: unknown[]) => Omit<T, K>;
370
- /**
371
- * A Proxy-based helper for auto-vivification of nested object structures.
372
- *
373
- * Accessing any property on the returned proxy automatically creates an empty object if the property does not exist,
374
- * allowing deep assignments without explicit null checks.
375
- *
376
- * @param ref
377
- *
378
- * The root object to wrap.
379
- *
380
- * @returns
381
- *
382
- * A proxy that auto-creates nested objects on property access.
383
- */
384
- export declare const pro: (ref: unknown) => any;
385
372
  /**
386
373
  * A helper for building simple tokenizers (see also {@link nanolight_ts}).
387
374
  *
@@ -450,3 +437,22 @@ export declare const monokai: CRoot;
450
437
  * A UUID v1 `string` in the standard format `xxxxxxxx-xxxx-1xxx-xxxx-xxxxxxxxxxxx`.
451
438
  */
452
439
  export declare const uuid_v1: (date?: Date, node?: string) => string;
440
+ /**
441
+ * A Proxy-based helper for auto-vivification of nested object structures.
442
+ *
443
+ * Accessing any property on the returned proxy automatically creates an empty object
444
+ * (or array for numeric keys) if the property does not exist,
445
+ * allowing deep assignments without explicit null checks.
446
+ *
447
+ * When a numeric key (like `[42]`) is accessed, the parent structure is
448
+ * automatically converted to an array if it isn’t one already.
449
+ *
450
+ * @param ref
451
+ *
452
+ * The root object to wrap.
453
+ *
454
+ * @returns
455
+ *
456
+ * A proxy that auto-creates nested objects/arrays on property access.
457
+ */
458
+ export declare const vivify: (ref: unknown) => any;
package/nnn.js CHANGED
@@ -5,12 +5,26 @@ var is_number = (arg) => typeof arg === "number";
5
5
  var is_record = (arg) => typeof arg === "object" && arg != null && !is_array(arg);
6
6
  var is_string = (arg) => typeof arg === "string";
7
7
 
8
- // src/nnn/pro.ts
9
- var pro = (ref) => new Proxy(ref, {
8
+ // src/nnn/vivify.ts
9
+ var ARRAY_INDEX_REGEXP = /^(0|[1-9]\d*)$/;
10
+ var should_convert_target_to_array = (target, key, parent) => parent != null && typeof key === "string" && ARRAY_INDEX_REGEXP.test(key) && !is_array(target);
11
+ var _vivify = (ref, parent, parentKey) => new Proxy(ref, {
10
12
  get(target, key) {
11
- return pro(target[key] ??= {});
13
+ if (should_convert_target_to_array(target, key, parent)) {
14
+ target = parent[parentKey] = [];
15
+ }
16
+ target[key] ??= {};
17
+ return _vivify(target[key], target, key);
18
+ },
19
+ set(target, key, value) {
20
+ if (should_convert_target_to_array(target, key, parent)) {
21
+ target = parent[parentKey] = [];
22
+ }
23
+ target[key] = value;
24
+ return true;
12
25
  }
13
26
  });
27
+ var vivify = (ref) => _vivify(ref);
14
28
 
15
29
  // src/nnn/c.ts
16
30
  var _c = (node, prefix, result, splitter) => {
@@ -65,7 +79,7 @@ var c = (root, splitter = "$$") => {
65
79
  return chunks.join("");
66
80
  };
67
81
  var rwd = (root, selector, cell_width_px, cell_height_px, ...specs) => {
68
- const main = pro(root)[selector];
82
+ const main = vivify(root)[selector];
69
83
  main.boxSizing = "border-box";
70
84
  main.display = "block";
71
85
  main.float = "left";
@@ -73,7 +87,7 @@ var rwd = (root, selector, cell_width_px, cell_height_px, ...specs) => {
73
87
  main.height = `${cell_height_px}px`;
74
88
  specs.sort(([a], [b]) => a - b);
75
89
  for (let [max_width, width, height] of specs) {
76
- const node = max_width === 1 ? main : pro(root)[`@media(min-width:${cell_width_px * max_width}px)`][selector];
90
+ const node = max_width === 1 ? main : vivify(root)[`@media(min-width:${cell_width_px * max_width}px)`][selector];
77
91
  width ??= 1;
78
92
  height ??= 1;
79
93
  let gcd = 100 * width;
@@ -158,9 +172,9 @@ var h = /* @__PURE__ */ _h();
158
172
  var s = /* @__PURE__ */ _h("http://www.w3.org/2000/svg");
159
173
  var svg_use = (id, ...args) => s("svg", ["use", { "xlink:href": "#" + id }], ...args);
160
174
 
161
- // src/nnn/fix_typography.ts
175
+ // src/nnn/fix_pl_typography.ts
162
176
  var TAGS_TO_SKIP = ["IFRAME", "NOSCRIPT", "PRE", "SCRIPT", "STYLE", "TEXTAREA"];
163
- var fix_typography = (node) => {
177
+ var fix_pl_typography = (node) => {
164
178
  const queue = [node];
165
179
  while (queue.length > 0) {
166
180
  const node_0 = queue.shift();
@@ -209,12 +223,9 @@ var js_on_parse = (handlers, text) => JSON.parse(text, (key, value) => {
209
223
  }
210
224
  return value;
211
225
  });
212
- // src/nnn/noun_form.ts
213
- var new_noun_form = (singular, plural_2, plural_5) => (value) => {
214
- const abs_value = Math.abs(value);
215
- const abs_value_mod_10 = abs_value % 10;
216
- return value === 1 ? singular : (abs_value_mod_10 === 2 || abs_value_mod_10 === 3 || abs_value_mod_10 === 4) && abs_value !== 12 && abs_value !== 13 && abs_value !== 14 ? plural_2 : plural_5;
217
- };
226
+ // src/nnn/new_noun_form.ts
227
+ var PLURAL_RULES = {};
228
+ var new_noun_form = (locale, forms) => (value) => forms[(PLURAL_RULES[locale] ??= new Intl.PluralRules(locale)).select(value)] ?? forms.other ?? "";
218
229
  // src/nnn/omit_pick.ts
219
230
  var pick = (ref, keys) => Object.fromEntries(Object.entries(ref).filter(([key]) => keys.includes(key)));
220
231
  var omit = (ref, keys) => Object.fromEntries(Object.entries(ref).filter(([key]) => !keys.includes(key)));
@@ -319,11 +330,11 @@ var uuid_v1 = (date = new Date, node = Math.random().toString(16).slice(2)) => {
319
330
  return time.slice(-8).concat("-", time.slice(-12, -8), -1, time.slice(-15, -12), "-", (8 | counter >> 12).toString(16), (ZEROS + (counter & 4095).toString(16)).slice(-3), "-", (ZEROS + node).slice(-12));
320
331
  };
321
332
  export {
333
+ vivify,
322
334
  uuid_v1,
323
335
  svg_use,
324
336
  s,
325
337
  rwd,
326
- pro,
327
338
  pick,
328
339
  omit,
329
340
  new_tokenizer,
@@ -339,7 +350,7 @@ export {
339
350
  is_array,
340
351
  has_own,
341
352
  h,
342
- fix_typography,
353
+ fix_pl_typography,
343
354
  escape_values,
344
355
  csv_parse,
345
356
  c
package/package.json CHANGED
@@ -2,6 +2,7 @@
2
2
  "author": "Jackens",
3
3
  "description": "Jackens’ JavaScript helpers.",
4
4
  "keywords": [
5
+ "autovivify",
5
6
  "c",
6
7
  "CSS-in-JS",
7
8
  "CSV parse",
@@ -34,12 +35,13 @@
34
35
  "tokenizer",
35
36
  "typography",
36
37
  "uuid",
37
- "uuidv1"
38
+ "uuidv1",
39
+ "vivify"
38
40
  ],
39
41
  "license": "MIT",
40
42
  "main": "nnn.js",
41
43
  "name": "@jackens/nnn",
42
44
  "type": "module",
43
45
  "types": "nnn.d.ts",
44
- "version": "2025.12.29"
46
+ "version": "2026.2.7"
45
47
  }
package/readme.md CHANGED
@@ -5,25 +5,43 @@ A collection of Jackens’ JavaScript helper utilities.
5
5
  ## Installation
6
6
 
7
7
  ```sh
8
- bun i @jackens/nnn
9
- ```
10
-
11
- or
12
-
13
- ```sh
14
- npm i @jackens/nnn
8
+ bun i @jackens/nnn # npm i @jackens/nnn
15
9
  ```
16
10
 
17
11
  ## Usage
18
12
 
19
13
  ```js
20
- import { «something» } from '@jackens/nnn'
21
- ```
22
-
23
- or
24
-
25
- ```js
26
- import { «something» } from './node_modules/@jackens/nnn/nnn.js'
14
+ import {
15
+ CNode,
16
+ CRoot,
17
+ EscapeMap,
18
+ HArgs,
19
+ HArgs1,
20
+ c,
21
+ csv_parse,
22
+ escape_values,
23
+ fix_pl_typography,
24
+ h,
25
+ has_own,
26
+ is_array,
27
+ is_finite_number,
28
+ is_number,
29
+ is_record,
30
+ is_string,
31
+ js_on_parse,
32
+ monokai,
33
+ nanolight_ts,
34
+ new_escape,
35
+ new_noun_form,
36
+ new_tokenizer,
37
+ omit,
38
+ pick,
39
+ rwd,
40
+ s,
41
+ svg_use,
42
+ uuid_v1,
43
+ vivify
44
+ } from '@jackens/nnn' // './node_modules/@jackens/nnn/nnn.js'
27
45
  ```
28
46
 
29
47
  ## Exports
@@ -36,7 +54,7 @@ import { «something» } from './node_modules/@jackens/nnn/nnn.js'
36
54
  - [`c`](#c): A minimal CSS-in-JS helper that converts a JavaScript object hierarchy into a CSS string.
37
55
  - [`csv_parse`](#csv_parse): Parses a CSV string into a two-dimensional array of strings.
38
56
  - [`escape_values`](#escape_values): Escapes an array of values using the provided escape map.
39
- - [`fix_typography`](#fix_typography): Applies Polish-specific typographic corrections to a DOM subtree.
57
+ - [`fix_pl_typography`](#fix_pl_typography): Applies Polish-specific typographic corrections to a DOM subtree.
40
58
  - [`h`](#h): A lightweight [HyperScript](https://github.com/hyperhype/hyperscript)-style helper
41
59
  for creating and modifying `HTMLElement`s (see also [`s`](#s)).
42
60
  - [`has_own`](#has_own): Checks whether an object has the specified key as its own property.
@@ -49,16 +67,16 @@ import { «something» } from './node_modules/@jackens/nnn/nnn.js'
49
67
  - [`monokai`](#monokai): A Monokai-inspired color scheme for use with the [`c`](#c) helper and [`nanolight_ts`](#nanolight_ts) tokenizer.
50
68
  - [`nanolight_ts`](#nanolight_ts): A TypeScript/JavaScript syntax highlighting tokenizer built using [`new_tokenizer`](#new_tokenizer).
51
69
  - [`new_escape`](#new_escape): Creates a tag function for escaping interpolated values in template literals.
52
- - [`new_noun_form`](#new_noun_form): Creates a function that returns the appropriate Polish noun form based on a numeric value.
70
+ - [`new_noun_form`](#new_noun_form): Creates a function that returns the appropriate noun form based on a numeric value using `Intl.PluralRules`.
53
71
  - [`new_tokenizer`](#new_tokenizer): A helper for building simple tokenizers (see also [`nanolight_ts`](#nanolight_ts)).
54
72
  - [`omit`](#omit): Creates a new object excluding the specified keys from the source object.
55
73
  - [`pick`](#pick): Creates a new object containing only the specified keys from the source object.
56
- - [`pro`](#pro): A Proxy-based helper for auto-vivification of nested object structures.
57
74
  - [`rwd`](#rwd): A responsive web design helper that generates CSS rules for a grid-like layout.
58
75
  - [`s`](#s): A lightweight [HyperScript](https://github.com/hyperhype/hyperscript)-style helper
59
76
  for creating and modifying `SVGElement`s (see also [`h`](#h)).
60
77
  - [`svg_use`](#svg_use): Shorthand for creating an SVG element with a `<use>` child referencing an icon by ID.
61
78
  - [`uuid_v1`](#uuid_v1): Generates a UUID v1 (time-based) identifier.
79
+ - [`vivify`](#vivify): A Proxy-based helper for auto-vivification of nested object structures.
62
80
 
63
81
  ### CNode
64
82
 
@@ -391,10 +409,10 @@ The array of values to escape.
391
409
 
392
410
  An array of escaped strings.
393
411
 
394
- ### fix_typography
412
+ ### fix_pl_typography
395
413
 
396
414
  ```ts
397
- const fix_typography: (node: Node) => void;
415
+ const fix_pl_typography: (node: Node) => void;
398
416
  ```
399
417
 
400
418
  Applies Polish-specific typographic corrections to a DOM subtree.
@@ -413,7 +431,7 @@ except those inside `IFRAME`, `NOSCRIPT`, `PRE`, `SCRIPT`, `STYLE`, or `TEXTAREA
413
431
  ```ts
414
432
  const p = h('p', 'Pchnąć w tę łódź jeża lub ośm skrzyń fig (zob. https://pl.wikipedia.org/wiki/Pangram).')
415
433
 
416
- fix_typography(p)
434
+ fix_pl_typography(p)
417
435
 
418
436
  expect(p.innerHTML).to.deep.equal(
419
437
  'Pchnąć <span style="white-space:nowrap">w </span>tę łódź jeża lub ośm skrzyń fig ' +
@@ -879,12 +897,12 @@ const sql = new_escape(escape_map)
879
897
  const actual = sql`
880
898
  SELECT *
881
899
  FROM table_name
882
- WHERE column_name IN (${[true, null, undefined, 42, '42', "4'2", /42/, new Date(323325000000)]})`
900
+ WHERE column_name IN (${[true, null, undefined, 42, '42', "4'2", new Date(323325000000)]})`
883
901
 
884
902
  const expected = `
885
903
  SELECT *
886
904
  FROM table_name
887
- WHERE column_name IN (b'1', NULL, NULL, 42, '42', '4''2', NULL, '1980-03-31 04:30:00')`
905
+ WHERE column_name IN (b'1', NULL, NULL, 42, '42', '4''2', '1980-03-31 04:30:00')`
888
906
 
889
907
  expect(actual).to.deep.equal(expected)
890
908
  ```
@@ -892,27 +910,29 @@ expect(actual).to.deep.equal(expected)
892
910
  ### new_noun_form
893
911
 
894
912
  ```ts
895
- const new_noun_form: (singular: string, plural_2: string, plural_5: string) => (value: number) => string;
913
+ const new_noun_form: (locale: string, forms: Partial<Record<Intl.LDMLPluralRule, string>>) => (value: number) => string;
896
914
  ```
897
915
 
898
- Creates a function that returns the appropriate Polish noun form based on a numeric value.
899
-
900
- Polish has three plural forms depending on the number:
901
- - Singular: used for exactly 1.
902
- - “Plural 2-4”: used for 2, 3, 4, 22, 23, 24, etc. (but not 12, 13, 14).
903
- - “Plural 5+”: used for 0, 5–21, 25–31, etc.
916
+ Creates a function that returns the appropriate noun form based on a numeric value using `Intl.PluralRules`.
904
917
 
905
- #### singular
918
+ Different languages have different plural rules. The `Intl.PluralRules` API provides
919
+ locale-aware plural category selection. Possible categories are:
906
920
 
907
- The singular form (e.g., “auto” for “car”).
921
+ - `zero`: for zero items (used in some languages like Arabic, Latvian)
922
+ - `one`: for singular (e.g., 1 item)
923
+ - `two`: for dual (used in some languages like Arabic, Hebrew)
924
+ - `few`: for small plurals (e.g., 2-4 in Polish)
925
+ - `many`: for larger plurals (e.g., 5-21 in Polish)
926
+ - `other`: fallback category (used by all languages)
908
927
 
909
- #### plural_2
928
+ #### locale
910
929
 
911
- The form for 2, 3, 4 (e.g., “auta”).
930
+ A BCP 47 language tag (e.g., `pl`, `en`).
912
931
 
913
- #### plural_5
932
+ #### forms
914
933
 
915
- The form for 5+ (e.g., “aut”).
934
+ An object mapping plural categories to noun forms. Not all categories need to be provided;
935
+ if a category is missing, the function falls back to `other`, then to an empty string.
916
936
 
917
937
  #### Returns
918
938
 
@@ -921,14 +941,14 @@ A function that takes a numeric value and returns the appropriate noun form.
921
941
  #### Usage Examples
922
942
 
923
943
  ```ts
924
- const auto = new_noun_form('auto', 'auta', 'aut')
944
+ const auto = new_noun_form('pl', { one: 'auto', few: 'auta', other: 'aut' })
925
945
 
926
946
  expect(auto(0)).to.deep.equal('aut')
927
947
  expect(auto(1)).to.deep.equal('auto')
928
948
  expect(auto(17)).to.deep.equal('aut')
929
949
  expect(auto(42)).to.deep.equal('auta')
930
950
 
931
- const car = new_noun_form('car', 'cars', 'cars')
951
+ const car = new_noun_form('en', { one: 'car', other: 'cars' })
932
952
 
933
953
  expect(car(0)).to.deep.equal('cars')
934
954
  expect(car(1)).to.deep.equal('car')
@@ -1026,55 +1046,6 @@ const obj = { a: 42, b: '42', c: 17 }
1026
1046
  expect(pick(obj, ['a', 'b'])).to.deep.equal({ a: 42, b: '42' })
1027
1047
  ```
1028
1048
 
1029
- ### pro
1030
-
1031
- ```ts
1032
- const pro: (ref: unknown) => any;
1033
- ```
1034
-
1035
- A Proxy-based helper for auto-vivification of nested object structures.
1036
-
1037
- Accessing any property on the returned proxy automatically creates an empty object if the property does not exist,
1038
- allowing deep assignments without explicit null checks.
1039
-
1040
- #### ref
1041
-
1042
- The root object to wrap.
1043
-
1044
- #### Returns
1045
-
1046
- A proxy that auto-creates nested objects on property access.
1047
-
1048
- #### Usage Examples
1049
-
1050
- ```ts
1051
- const ref = {}
1052
-
1053
- pro(ref).one.two[3][4] = 1234
1054
-
1055
- expect(ref).to.deep.equal({ one: { two: { 3: { 4: 1234 } } } })
1056
-
1057
- pro(ref).one.two.tree = 123
1058
-
1059
- expect(ref).to.deep.equal({ one: { two: { 3: { 4: 1234 }, tree: 123 } } })
1060
-
1061
- pro(ref).one.two = undefined
1062
-
1063
- expect(ref).to.deep.equal({ one: { two: undefined } })
1064
-
1065
- delete pro(ref).one.two
1066
-
1067
- expect(ref).to.deep.equal({ one: {} })
1068
-
1069
- pro(ref).one.two.three.four
1070
-
1071
- expect(ref).to.deep.equal({ one: { two: { three: { four: {} } } } })
1072
-
1073
- pro(ref).one.two.three.four = 1234
1074
-
1075
- expect(ref).to.deep.equal({ one: { two: { three: { four: 1234 } } } })
1076
- ```
1077
-
1078
1049
  ### rwd
1079
1050
 
1080
1051
  ```ts
@@ -1273,6 +1244,71 @@ expect(uuid_v1(new Date(), '123456789').split('-')[4]).to.deep.equal('0001234567
1273
1244
  expect(uuid_v1(new Date(323325000000)).startsWith('c1399400-9a71-11bd')).to.be.true
1274
1245
  ```
1275
1246
 
1247
+ ### vivify
1248
+
1249
+ ```ts
1250
+ const vivify: (ref: unknown) => any;
1251
+ ```
1252
+
1253
+ A Proxy-based helper for auto-vivification of nested object structures.
1254
+
1255
+ Accessing any property on the returned proxy automatically creates an empty object
1256
+ (or array for numeric keys) if the property does not exist,
1257
+ allowing deep assignments without explicit null checks.
1258
+
1259
+ When a numeric key (like `[42]`) is accessed, the parent structure is
1260
+ automatically converted to an array if it isn’t one already.
1261
+
1262
+ #### ref
1263
+
1264
+ The root object to wrap.
1265
+
1266
+ #### Returns
1267
+
1268
+ A proxy that auto-creates nested objects/arrays on property access.
1269
+
1270
+ #### Usage Examples
1271
+
1272
+ ```ts
1273
+ const ref: any = {}
1274
+
1275
+ vivify(ref).one.two[3][4] = 1234
1276
+
1277
+ expect(ref).to.deep.equal({ one: { two: [, , , [, , , , 1234]] } })
1278
+
1279
+ vivify(ref).one.two[3][5] = 1235
1280
+
1281
+ expect(ref).to.deep.equal({ one: { two: [, , , [, , , , 1234, 1235]] } })
1282
+
1283
+ vivify(ref).one.two[1] = 121
1284
+
1285
+ expect(ref).to.deep.equal({ one: { two: [, 121, , [, , , , 1234, 1235]] } })
1286
+
1287
+ vivify(ref).one.two = 12
1288
+
1289
+ expect(ref).to.deep.equal({ one: { two: 12 } })
1290
+
1291
+ vivify(ref).one.two = undefined
1292
+
1293
+ expect(ref).to.deep.equal({ one: { two: undefined } })
1294
+
1295
+ delete vivify(ref).one.two
1296
+
1297
+ expect(ref).to.deep.equal({ one: {} })
1298
+
1299
+ delete vivify(ref).one.two.three
1300
+
1301
+ expect(ref).to.deep.equal({ one: { two: {} } })
1302
+
1303
+ vivify(ref).one.two.three.four
1304
+
1305
+ expect(ref).to.deep.equal({ one: { two: { three: { four: {} } } } })
1306
+
1307
+ vivify(ref).one.two.three.four = 1234
1308
+
1309
+ expect(ref).to.deep.equal({ one: { two: { three: { four: 1234 } } } })
1310
+ ```
1311
+
1276
1312
  ## License
1277
1313
 
1278
1314
  The MIT License (MIT)