@jackens/nnn 2026.2.6 → 2026.2.11
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/nnn.d.ts +20 -12
- package/nnn.js +24 -15
- package/package.json +1 -1
- package/readme.md +136 -25
package/nnn.d.ts
CHANGED
|
@@ -312,16 +312,16 @@ export declare const js_on_parse: (handlers: Record<PropertyKey, Function>, text
|
|
|
312
312
|
* Different languages have different plural rules. The `Intl.PluralRules` API provides
|
|
313
313
|
* locale-aware plural category selection. Possible categories are:
|
|
314
314
|
*
|
|
315
|
-
* - `zero
|
|
316
|
-
* - `one
|
|
317
|
-
* - `two
|
|
318
|
-
* - `few
|
|
319
|
-
* - `many
|
|
320
|
-
* - `other
|
|
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)
|
|
321
321
|
*
|
|
322
322
|
* @param locale
|
|
323
323
|
*
|
|
324
|
-
* A BCP 47 language tag (e.g.,
|
|
324
|
+
* A BCP 47 language tag (e.g., `pl`, `en`).
|
|
325
325
|
*
|
|
326
326
|
* @param forms
|
|
327
327
|
*
|
|
@@ -440,12 +440,20 @@ export declare const uuid_v1: (date?: Date, node?: string) => string;
|
|
|
440
440
|
/**
|
|
441
441
|
* A Proxy-based helper for auto-vivification of nested object structures.
|
|
442
442
|
*
|
|
443
|
-
* Accessing any property on the returned proxy automatically creates
|
|
444
|
-
* (or
|
|
445
|
-
* allowing deep assignments without explicit null checks.
|
|
443
|
+
* Accessing, assigning, or deleting any nested property on the returned proxy automatically creates intermediate objects
|
|
444
|
+
* (or arrays for numeric-string keys matching `^(0|[1-9]\d*)$`) as needed, allowing deep operations without explicit null checks.
|
|
446
445
|
*
|
|
447
|
-
*
|
|
448
|
-
*
|
|
446
|
+
* Intermediates of the last level in a get-only property chain are NOT auto-created.
|
|
447
|
+
* For example, `vivify(ref).one.two` will create `ref.one` as `{}`, but will NOT create `ref.one.two`.
|
|
448
|
+
* Only when a deeper access, assignment, or deletion occurs
|
|
449
|
+
* (e.g. `vivify(ref).one.two[3]` or `vivify(ref).one.two.three = 42`) will `ref.one.two` be materialized.
|
|
450
|
+
*
|
|
451
|
+
* When traversal reaches a primitive value, no auto-creation happens;
|
|
452
|
+
* the primitive’s own property is returned instead
|
|
453
|
+
* (e.g. accessing `.toString.name` on a number yields `'toString'` without modifying the underlying structure).
|
|
454
|
+
*
|
|
455
|
+
* Deletion on a non-existing intermediate path will auto-create intermediates up to the parent of the deleted key
|
|
456
|
+
* (e.g. `delete vivify(ref).a.b.c` will create `ref.a.b` as `{}` if it does not exist).
|
|
449
457
|
*
|
|
450
458
|
* @param ref
|
|
451
459
|
*
|
package/nnn.js
CHANGED
|
@@ -7,21 +7,32 @@ var is_string = (arg) => typeof arg === "string";
|
|
|
7
7
|
|
|
8
8
|
// src/nnn/vivify.ts
|
|
9
9
|
var ARRAY_INDEX_REGEXP = /^(0|[1-9]\d*)$/;
|
|
10
|
-
var
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
var get_target = (parent, parent_key, child_key) => {
|
|
11
|
+
if (parent_key == null) {
|
|
12
|
+
return parent;
|
|
13
|
+
}
|
|
14
|
+
if (!(parent_key in parent)) {
|
|
15
|
+
parent[parent_key] = is_string(child_key) && ARRAY_INDEX_REGEXP.test(child_key) ? [] : {};
|
|
16
|
+
}
|
|
17
|
+
return parent[parent_key];
|
|
18
|
+
};
|
|
19
|
+
var _vivify = (parent, parent_key) => new Proxy({}, {
|
|
20
|
+
get(_, key) {
|
|
21
|
+
const target = get_target(parent, parent_key, key);
|
|
22
|
+
if (target != null && typeof target === "object") {
|
|
23
|
+
return _vivify(target, key);
|
|
15
24
|
}
|
|
16
|
-
target[key]
|
|
17
|
-
return _vivify(target[key], target, key);
|
|
25
|
+
return target?.[key];
|
|
18
26
|
},
|
|
19
|
-
set(
|
|
20
|
-
|
|
21
|
-
target = parent[parentKey] = [];
|
|
22
|
-
}
|
|
27
|
+
set(_, key, value) {
|
|
28
|
+
const target = get_target(parent, parent_key, key);
|
|
23
29
|
target[key] = value;
|
|
24
30
|
return true;
|
|
31
|
+
},
|
|
32
|
+
deleteProperty(_, key) {
|
|
33
|
+
const target = get_target(parent, parent_key, key);
|
|
34
|
+
delete target[key];
|
|
35
|
+
return true;
|
|
25
36
|
}
|
|
26
37
|
});
|
|
27
38
|
var vivify = (ref) => _vivify(ref);
|
|
@@ -224,10 +235,8 @@ var js_on_parse = (handlers, text) => JSON.parse(text, (key, value) => {
|
|
|
224
235
|
return value;
|
|
225
236
|
});
|
|
226
237
|
// src/nnn/new_noun_form.ts
|
|
227
|
-
var
|
|
228
|
-
|
|
229
|
-
return (value) => forms[plural_rules.select(value)] ?? forms.other ?? "";
|
|
230
|
-
};
|
|
238
|
+
var PLURAL_RULES = {};
|
|
239
|
+
var new_noun_form = (locale, forms) => (value) => forms[(PLURAL_RULES[locale] ??= new Intl.PluralRules(locale)).select(value)] ?? forms.other ?? "";
|
|
231
240
|
// src/nnn/omit_pick.ts
|
|
232
241
|
var pick = (ref, keys) => Object.fromEntries(Object.entries(ref).filter(([key]) => keys.includes(key)));
|
|
233
242
|
var omit = (ref, keys) => Object.fromEntries(Object.entries(ref).filter(([key]) => !keys.includes(key)));
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -918,16 +918,16 @@ Creates a function that returns the appropriate noun form based on a numeric val
|
|
|
918
918
|
Different languages have different plural rules. The `Intl.PluralRules` API provides
|
|
919
919
|
locale-aware plural category selection. Possible categories are:
|
|
920
920
|
|
|
921
|
-
- `zero
|
|
922
|
-
- `one
|
|
923
|
-
- `two
|
|
924
|
-
- `few
|
|
925
|
-
- `many
|
|
926
|
-
- `other
|
|
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)
|
|
927
927
|
|
|
928
928
|
#### locale
|
|
929
929
|
|
|
930
|
-
A BCP 47 language tag (e.g.,
|
|
930
|
+
A BCP 47 language tag (e.g., `pl`, `en`).
|
|
931
931
|
|
|
932
932
|
#### forms
|
|
933
933
|
|
|
@@ -1252,12 +1252,20 @@ const vivify: (ref: unknown) => any;
|
|
|
1252
1252
|
|
|
1253
1253
|
A Proxy-based helper for auto-vivification of nested object structures.
|
|
1254
1254
|
|
|
1255
|
-
Accessing any property on the returned proxy automatically creates
|
|
1256
|
-
(or
|
|
1257
|
-
allowing deep assignments without explicit null checks.
|
|
1255
|
+
Accessing, assigning, or deleting any nested property on the returned proxy automatically creates intermediate objects
|
|
1256
|
+
(or arrays for numeric-string keys matching `^(0|[1-9]\d*)$`) as needed, allowing deep operations without explicit null checks.
|
|
1258
1257
|
|
|
1259
|
-
|
|
1260
|
-
|
|
1258
|
+
Intermediates of the last level in a get-only property chain are NOT auto-created.
|
|
1259
|
+
For example, `vivify(ref).one.two` will create `ref.one` as `{}`, but will NOT create `ref.one.two`.
|
|
1260
|
+
Only when a deeper access, assignment, or deletion occurs
|
|
1261
|
+
(e.g. `vivify(ref).one.two[3]` or `vivify(ref).one.two.three = 42`) will `ref.one.two` be materialized.
|
|
1262
|
+
|
|
1263
|
+
When traversal reaches a primitive value, no auto-creation happens;
|
|
1264
|
+
the primitive’s own property is returned instead
|
|
1265
|
+
(e.g. accessing `.toString.name` on a number yields `'toString'` without modifying the underlying structure).
|
|
1266
|
+
|
|
1267
|
+
Deletion on a non-existing intermediate path will auto-create intermediates up to the parent of the deleted key
|
|
1268
|
+
(e.g. `delete vivify(ref).a.b.c` will create `ref.a.b` as `{}` if it does not exist).
|
|
1261
1269
|
|
|
1262
1270
|
#### ref
|
|
1263
1271
|
|
|
@@ -1272,37 +1280,140 @@ A proxy that auto-creates nested objects/arrays on property access.
|
|
|
1272
1280
|
```ts
|
|
1273
1281
|
const ref: any = {}
|
|
1274
1282
|
|
|
1275
|
-
vivify(ref).one.two[
|
|
1283
|
+
vivify(ref).one.two[1][2] = 42
|
|
1284
|
+
|
|
1285
|
+
expect(ref).to.deep.equal({
|
|
1286
|
+
one: {
|
|
1287
|
+
two: [
|
|
1288
|
+
undefined,
|
|
1289
|
+
[undefined, undefined, 42]
|
|
1290
|
+
]
|
|
1291
|
+
}
|
|
1292
|
+
})
|
|
1293
|
+
|
|
1294
|
+
vivify(ref).one.two[1][3] = 42
|
|
1276
1295
|
|
|
1277
|
-
expect(ref).to.deep.equal({
|
|
1296
|
+
expect(ref).to.deep.equal({
|
|
1297
|
+
one: {
|
|
1298
|
+
two: [
|
|
1299
|
+
undefined,
|
|
1300
|
+
[undefined, undefined, 42, 42]
|
|
1301
|
+
]
|
|
1302
|
+
}
|
|
1303
|
+
})
|
|
1304
|
+
|
|
1305
|
+
vivify(ref).one.two[1] = 42
|
|
1306
|
+
|
|
1307
|
+
expect(ref).to.deep.equal({
|
|
1308
|
+
one: {
|
|
1309
|
+
two: [
|
|
1310
|
+
undefined,
|
|
1311
|
+
42
|
|
1312
|
+
]
|
|
1313
|
+
}
|
|
1314
|
+
})
|
|
1278
1315
|
|
|
1279
|
-
vivify(ref).one.two[3][
|
|
1316
|
+
vivify(ref).one.two[3][0] = 42
|
|
1280
1317
|
|
|
1281
|
-
expect(ref).to.deep.equal({
|
|
1318
|
+
expect(ref).to.deep.equal({
|
|
1319
|
+
one: {
|
|
1320
|
+
two: [
|
|
1321
|
+
undefined,
|
|
1322
|
+
42,
|
|
1323
|
+
undefined,
|
|
1324
|
+
[42]
|
|
1325
|
+
]
|
|
1326
|
+
}
|
|
1327
|
+
})
|
|
1282
1328
|
|
|
1283
|
-
vivify(ref).one.two[
|
|
1329
|
+
vivify(ref).one.two[3].length = 2
|
|
1284
1330
|
|
|
1285
|
-
expect(ref).to.deep.equal({
|
|
1331
|
+
expect(ref).to.deep.equal({
|
|
1332
|
+
one: {
|
|
1333
|
+
two: [
|
|
1334
|
+
undefined,
|
|
1335
|
+
42,
|
|
1336
|
+
undefined,
|
|
1337
|
+
[42, undefined]
|
|
1338
|
+
]
|
|
1339
|
+
}
|
|
1340
|
+
})
|
|
1286
1341
|
|
|
1287
|
-
vivify(ref).one.two =
|
|
1342
|
+
vivify(ref).one.two = 12
|
|
1288
1343
|
|
|
1289
|
-
expect(ref).to.deep.equal({
|
|
1344
|
+
expect(ref).to.deep.equal({
|
|
1345
|
+
one: {
|
|
1346
|
+
two: 12
|
|
1347
|
+
}
|
|
1348
|
+
})
|
|
1290
1349
|
|
|
1291
1350
|
vivify(ref).one.two = undefined
|
|
1292
1351
|
|
|
1293
|
-
expect(ref).to.deep.equal({
|
|
1352
|
+
expect(ref).to.deep.equal({
|
|
1353
|
+
one: {
|
|
1354
|
+
two: undefined
|
|
1355
|
+
}
|
|
1356
|
+
})
|
|
1294
1357
|
|
|
1295
1358
|
delete vivify(ref).one.two
|
|
1296
1359
|
|
|
1297
|
-
expect(ref).to.deep.equal({
|
|
1360
|
+
expect(ref).to.deep.equal({
|
|
1361
|
+
one: {}
|
|
1362
|
+
})
|
|
1363
|
+
|
|
1364
|
+
delete vivify(ref).one.two.three
|
|
1365
|
+
|
|
1366
|
+
expect(ref).to.deep.equal({
|
|
1367
|
+
one: {
|
|
1368
|
+
two: {}
|
|
1369
|
+
}
|
|
1370
|
+
})
|
|
1298
1371
|
|
|
1299
1372
|
vivify(ref).one.two.three.four
|
|
1300
1373
|
|
|
1301
|
-
expect(ref).to.deep.equal({
|
|
1374
|
+
expect(ref).to.deep.equal({
|
|
1375
|
+
one: {
|
|
1376
|
+
two: {
|
|
1377
|
+
three: {}
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
})
|
|
1381
|
+
|
|
1382
|
+
vivify(ref).one.two[3]
|
|
1383
|
+
|
|
1384
|
+
expect(ref).to.deep.equal({
|
|
1385
|
+
one: {
|
|
1386
|
+
two: {
|
|
1387
|
+
three: {}
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
})
|
|
1391
|
+
|
|
1392
|
+
vivify(ref).one.two.three.four = 42
|
|
1393
|
+
|
|
1394
|
+
expect(ref).to.deep.equal({
|
|
1395
|
+
one: {
|
|
1396
|
+
two: {
|
|
1397
|
+
three: {
|
|
1398
|
+
four: 42
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
})
|
|
1302
1403
|
|
|
1303
|
-
vivify(ref).one.two.three.four
|
|
1404
|
+
const name = vivify(ref).one.two.three.four.toString.name
|
|
1304
1405
|
|
|
1305
|
-
expect(
|
|
1406
|
+
expect(name).to.deep.equal('toString')
|
|
1407
|
+
|
|
1408
|
+
expect(ref).to.deep.equal({
|
|
1409
|
+
one: {
|
|
1410
|
+
two: {
|
|
1411
|
+
three: {
|
|
1412
|
+
four: 42
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
})
|
|
1306
1417
|
```
|
|
1307
1418
|
|
|
1308
1419
|
## License
|