ac-storage 0.11.0 → 0.13.0
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 +32 -0
- package/dist/bundle.cjs +153 -31
- package/dist/bundle.cjs.map +1 -1
- package/dist/bundle.mjs +153 -31
- package/dist/bundle.mjs.map +1 -1
- package/dist/index.d.ts +2 -1
- package/package.json +2 -2
- package/dist/features/StorageAccess/StorageAccess.d.ts +0 -14
- package/dist/features/StorageAccess/index.d.ts +0 -3
- package/dist/features/StorageAccess/types.d.ts +0 -20
- package/dist/features/StorageAccessControl/StorageAccessControl.d.ts +0 -22
- package/dist/features/StorageAccessControl/errors.d.ts +0 -13
- package/dist/features/StorageAccessControl/index.d.ts +0 -4
- package/dist/features/StorageAccessControl/types.d.ts +0 -15
- package/dist/features/TreeExplorer/TreeExplorer.d.ts +0 -14
- package/dist/features/TreeExplorer/TreeExplorer.test.d.ts +0 -1
- package/dist/features/TreeExplorer/index.d.ts +0 -2
- package/dist/features/accessors/BinaryAccessor.d.ts +0 -13
- package/dist/features/accessors/JSONAccessor/JSONAccessor.d.ts +0 -30
- package/dist/features/accessors/JSONAccessor/JSONAccessor.test.d.ts +0 -1
- package/dist/features/accessors/JSONAccessor/MemJSONAccessor.d.ts +0 -9
- package/dist/features/accessors/JSONAccessor/index.d.ts +0 -2
- package/dist/features/accessors/MemBinaryAccessor.d.ts +0 -12
- package/dist/features/accessors/MemTextAccessor.d.ts +0 -11
- package/dist/features/accessors/TextAccessor.d.ts +0 -12
- package/dist/features/accessors/errors.d.ts +0 -3
- package/dist/features/accessors/index.d.ts +0 -7
- package/dist/features/accessors/types.d.ts +0 -25
- package/dist/features/storage/FSStorage.d.ts +0 -28
- package/dist/features/storage/IStorage.d.ts +0 -20
- package/dist/features/storage/MemStorage.d.ts +0 -7
- package/dist/features/storage/errors.d.ts +0 -3
- package/dist/features/storage/index.d.ts +0 -4
- package/dist/test/entry.test.d.ts +0 -1
- package/dist/test/storage-access-control.test.d.ts +0 -1
- package/dist/test/storage-accessor.test.d.ts +0 -1
- package/dist/test/storage-fs.test.d.ts +0 -1
- package/dist/test/storage.test.d.ts +0 -1
- package/dist/test-utils/index.d.ts +0 -1
- package/dist/types/json.d.ts +0 -13
- package/dist/types/storage-access.d.ts +0 -20
package/README.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# AC-Storage
|
|
2
|
+
|
|
3
|
+
## Install
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install ac-storage
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Example
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { ACStorage, StorageAccess } from 'ac-storage';
|
|
13
|
+
|
|
14
|
+
const storage = new ACStorage('./store');
|
|
15
|
+
storage.register({
|
|
16
|
+
'auth' : {
|
|
17
|
+
'default.json' : StorageAccess.JSON(),
|
|
18
|
+
},
|
|
19
|
+
'cache' : {
|
|
20
|
+
'last_access.txt' : StorageAccess.Text(),
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const authAC = await storage.accessAsJSON('auth:default.json');
|
|
25
|
+
authAC.setOne('id', 'user');
|
|
26
|
+
|
|
27
|
+
const lastAccessAC = await storage.accessAsText('cache:last_access.txt');
|
|
28
|
+
lastAccessAC.write('20250607');
|
|
29
|
+
|
|
30
|
+
await storage.commit();
|
|
31
|
+
```
|
|
32
|
+
|
package/dist/bundle.cjs
CHANGED
|
@@ -27,7 +27,7 @@ var fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs$1);
|
|
|
27
27
|
var fs__namespace$1 = /*#__PURE__*/_interopNamespaceDefault(fs$2);
|
|
28
28
|
var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
|
|
29
29
|
|
|
30
|
-
let e$1 = class e extends Error{constructor(e){super(e),this.name="TreeNavigateError";}};const t$1="--tree-leaf";var i$1;let r$1 = class r{#e={};#t=".";#i=false;#r=true;constructor(){}static from(e,t={}){const r=new i$1;return r.#e=e,r.#t=t.delimiter??".",r.#i=t.allowWildcard??false,r.#r=t.allowRecursiveWildcard??true,r}subtree(t){const r=t.split(this.#t),a=this.#a(r,this.#e);if(!a)throw new e$1(`Path '${t}' does not exist`);if(i$1.#l(a.value)||i$1.#s(a.value))throw new e$1(`Path '${t}' is not a subtree`);const l=a.path.reduce((
|
|
30
|
+
let e$1 = class e extends Error{constructor(e){super(e),this.name="TreeNavigateError";}};const t$1="--tree-leaf";var i$1;let r$1 = class r{#e={};#t=".";#i=false;#r=true;constructor(){}static from(e,t={}){const r=new i$1;return r.#e=e,r.#t=t.delimiter??".",r.#i=t.allowWildcard??false,r.#r=t.allowRecursiveWildcard??true,r}subtree(t){const r=t.split(this.#t),a=this.#a(r,this.#e);if(!a)throw new e$1(`Path '${t}' does not exist`);if(i$1.#l(a.value)||i$1.#s(a.value))throw new e$1(`Path '${t}' is not a subtree`);const l=a.path.reduce((e,t)=>e[t],this.#e),s=new i$1;return s.#e=l,s.#t=this.#t,s.#i=this.#i,s}get(e,t){return this.walk(e,t)?.value??null}walk(e,t={}){if(""===e)return t.allowIntermediate?{value:this.#e,path:[]}:null;const r=e.split(this.#t),a=this.#a(r,this.#e);if(!a)return null;const l=a.value;return i$1.#l(l)?a:i$1.#s(l)?{value:l.value,path:a.path}:t.allowIntermediate?a:null}trace(e){const t=e.split(this.#t),r={treePath:[]},a=this.#a(t,this.#e,r);let l,s;if(a){const e=a.value;return i$1.#l(e)?(l=true,s=e):i$1.#s(e)?(l=true,s=e.value):(l=false,s=e),{find:true,isLeaf:l,value:s,nodePath:a?.path??[],tracePath:r.treePath,untracePath:t}}return {find:false,isLeaf:false,value:void 0,nodePath:[],tracePath:r.treePath,untracePath:t}}#a(e,t,r={treePath:[]}){const{treePath:a}=r,l=e.shift();if(null==l)return {value:t,path:[]};if("object"!=typeof t||i$1.#s(t))return e.unshift(l),null;if(a.push(l),l in t){const i=this.#a(e,t[l],r);if(i)return {value:i.value,path:[l,...i.path]}}else if(this.#i){if("*"in t){const i=this.#a(e,t["*"],r);if(i)return {value:i.value,path:["*",...i.path]}}if(this.#r&&"**/*"in t)return {value:t["**/*"],path:["**/*"]}}return null}static#l(e){return null==e||"object"!=typeof e}static#s(e){return 1==e[t$1]}};i$1=r$1;
|
|
31
31
|
|
|
32
32
|
class JSONAccessorError extends Error {
|
|
33
33
|
constructor(message) {
|
|
@@ -35,6 +35,18 @@ class JSONAccessorError extends Error {
|
|
|
35
35
|
this.name = 'AccessorError';
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
+
class UnserializableTypeError extends JSONAccessorError {
|
|
39
|
+
constructor(key, value) {
|
|
40
|
+
super(`Unserializable data: '${value}' for field '${key}'`);
|
|
41
|
+
this.name = 'UnserializableTypeError';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
class IncompatibleTypeError extends JSONAccessorError {
|
|
45
|
+
constructor(message) {
|
|
46
|
+
super(message);
|
|
47
|
+
this.name = 'IncompatibleTypeError';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
38
50
|
|
|
39
51
|
const JSON_TYPE_FLAG = '--json-type';
|
|
40
52
|
|
|
@@ -188,10 +200,18 @@ function isObject(value) {
|
|
|
188
200
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
189
201
|
}
|
|
190
202
|
|
|
191
|
-
|
|
192
|
-
|
|
203
|
+
/**
|
|
204
|
+
* 경로 문자열을 연결하여 반환
|
|
205
|
+
*/
|
|
206
|
+
function dotJoin(prefix, key) {
|
|
207
|
+
return (prefix != null && prefix.length > 0) ? `${prefix}.${key}` : key;
|
|
193
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* 값이 json-accessor 호환 가능 타입인지 확인하고 타입명 반환, 호환 불가능할 경우 null 반환
|
|
211
|
+
*/
|
|
194
212
|
function getJSONTypeName(value) {
|
|
213
|
+
if (value == null)
|
|
214
|
+
return 'null';
|
|
195
215
|
if (Array.isArray(value))
|
|
196
216
|
return 'array';
|
|
197
217
|
const typeName = typeof value;
|
|
@@ -216,10 +236,17 @@ class Flattener {
|
|
|
216
236
|
this.typeChecker = typeChecker;
|
|
217
237
|
}
|
|
218
238
|
/**
|
|
219
|
-
* key-value
|
|
239
|
+
* key-value 쌍에 대한 경로 유효성 검사후 `Array<[key, value]>` 형태로 반환
|
|
240
|
+
*
|
|
241
|
+
* @param key - dot(.)으로 구분된 경로 문자열
|
|
242
|
+
* @param value - 유효성 검사를 위한 값
|
|
243
|
+
* @return 인자를 [[key, value]] 형태로 반환
|
|
220
244
|
*/
|
|
221
245
|
transform(key, value) {
|
|
246
|
+
// [[key, value]] 를 그대로 반환하는건 flat()과의 호환을 위함
|
|
247
|
+
// 추후 구현에서 한 key에서 여러 결과를 리턴하는 등의 확장 가능성이 있음
|
|
222
248
|
if (key === '') {
|
|
249
|
+
// 루트 경로는 비허용
|
|
223
250
|
throw new JSONAccessorError(`Invalid path: ${key}`);
|
|
224
251
|
}
|
|
225
252
|
if (this.navigate == null) {
|
|
@@ -230,7 +257,7 @@ class Flattener {
|
|
|
230
257
|
if (traceResult.isLeaf) {
|
|
231
258
|
const node = traceResult.value;
|
|
232
259
|
this.typeChecker.check(key, value, node);
|
|
233
|
-
if (node.type === 'struct') {
|
|
260
|
+
if (value != null && node.type === 'struct') {
|
|
234
261
|
// struct 형식인 value에 대한 구조 검사
|
|
235
262
|
this.flatStruct({
|
|
236
263
|
target: value,
|
|
@@ -241,6 +268,8 @@ class Flattener {
|
|
|
241
268
|
return [[key, value]];
|
|
242
269
|
}
|
|
243
270
|
else {
|
|
271
|
+
// 경로는 존재하지만 노드의 중간인 경우
|
|
272
|
+
// value가 object 형태라면 flat()을 통해 유효성 검사를 진행
|
|
244
273
|
if (!isObject(value)) {
|
|
245
274
|
throw new JSONAccessorError(`Field '${key}' is not allowed`);
|
|
246
275
|
}
|
|
@@ -250,34 +279,41 @@ class Flattener {
|
|
|
250
279
|
});
|
|
251
280
|
}
|
|
252
281
|
}
|
|
282
|
+
// 경로상 존재하지 않는 경우
|
|
283
|
+
else if (traceResult.tracePath.length === 0) {
|
|
284
|
+
throw new JSONAccessorError(`Field '${key}' is not allowed`);
|
|
285
|
+
}
|
|
286
|
+
// 중간 경로까지는 존재하는 경우
|
|
253
287
|
else {
|
|
254
|
-
// 경로상 존재하지 않는 경우
|
|
255
|
-
// 닿을 수 있는 마지막 노드의 타입이 struct, any 라면 허용됨
|
|
256
288
|
const { tracePath } = traceResult;
|
|
257
289
|
const reached = tracePath.join(DELIMITER);
|
|
258
290
|
const node = this.navigate.get(reached);
|
|
291
|
+
// 닿을 수 있는 마지막 노드의 타입이 struct, any 라면 허용됨
|
|
259
292
|
if (!node || (node.type !== 'struct' && node.type !== 'any')) {
|
|
260
293
|
throw new JSONAccessorError(`Field '${key}' is not allowed`);
|
|
261
294
|
}
|
|
262
295
|
return [[key, value]];
|
|
263
296
|
}
|
|
264
297
|
}
|
|
265
|
-
|
|
298
|
+
/**
|
|
299
|
+
* 객체를 평탄화하여 `Array<[key, value]>`로 반환
|
|
300
|
+
*
|
|
301
|
+
* 유효성 검증을 포함함
|
|
302
|
+
*/
|
|
303
|
+
flat({ target, prefix }) {
|
|
266
304
|
if (!this.navigate) {
|
|
267
305
|
return this.flatWithoutNavigate({ target, prefix });
|
|
268
306
|
}
|
|
269
307
|
const navigate = this.navigate;
|
|
270
|
-
return Object.entries(target)
|
|
271
|
-
|
|
308
|
+
return Object.entries(target)
|
|
309
|
+
.flatMap(([key, value]) => {
|
|
310
|
+
const newKey = dotJoin(prefix, key);
|
|
272
311
|
const node = navigate.get(newKey, { allowIntermediate: true });
|
|
273
312
|
if (node == null) {
|
|
274
313
|
throw new JSONAccessorError(`Field '${newKey}' is not allowed`);
|
|
275
314
|
}
|
|
276
315
|
else if (isJSONTypeData(node)) {
|
|
277
316
|
this.typeChecker.check(newKey, value, node);
|
|
278
|
-
// @TODO : 배열 타입에 대한 세부 처리 필요
|
|
279
|
-
// 현재는 배열 경로까지만 flat이 이루어지므로
|
|
280
|
-
// get/set 시 배열 전체를 가져오거나 덮어쓰게됨
|
|
281
317
|
if (node.type === 'any') {
|
|
282
318
|
return this.flatWithoutNavigate({
|
|
283
319
|
target: value,
|
|
@@ -291,6 +327,12 @@ class Flattener {
|
|
|
291
327
|
structData: node,
|
|
292
328
|
});
|
|
293
329
|
}
|
|
330
|
+
else if (node.type === 'array') {
|
|
331
|
+
// @TODO : 배열 타입에 대한 세부 처리 필요
|
|
332
|
+
// 현재는 배열 경로까지만 flat이 이루어지므로
|
|
333
|
+
// get/set 시 배열 전체를 가져오거나 덮어쓰게됨
|
|
334
|
+
return [[newKey, value]];
|
|
335
|
+
}
|
|
294
336
|
else {
|
|
295
337
|
return [[newKey, value]];
|
|
296
338
|
}
|
|
@@ -302,7 +344,11 @@ class Flattener {
|
|
|
302
344
|
});
|
|
303
345
|
}
|
|
304
346
|
else {
|
|
305
|
-
|
|
347
|
+
// 가져온 값이 JSONTypeData(탐색한 노드의 끝) 도 아니고
|
|
348
|
+
// 일반 object(중간 노드)도 아닌 다른 타입인 경우
|
|
349
|
+
//
|
|
350
|
+
// TreeNavigate<JSONTypeData> 에서 가져온 결과에선 나올 수 없으므로 데이터 오염이라고 판단
|
|
351
|
+
throw new JSONAccessorError(`Data corrupted: unexpected node type encountered. key: '${newKey}', value: ${value} (${typeof value})`);
|
|
306
352
|
}
|
|
307
353
|
});
|
|
308
354
|
}
|
|
@@ -311,8 +357,9 @@ class Flattener {
|
|
|
311
357
|
*/
|
|
312
358
|
flatStruct({ target, prefix, structData }) {
|
|
313
359
|
// struct는 내부 구조에 대한 구조 검증이 없으므로 유효성 검증 없이 단순 평탄화
|
|
314
|
-
return Object.entries(target)
|
|
315
|
-
|
|
360
|
+
return Object.entries(target)
|
|
361
|
+
.flatMap(([key, value]) => {
|
|
362
|
+
const newKey = dotJoin(prefix, key);
|
|
316
363
|
if (isObject(value)) {
|
|
317
364
|
return this.flatWithoutNavigate({
|
|
318
365
|
target: value,
|
|
@@ -325,11 +372,12 @@ class Flattener {
|
|
|
325
372
|
});
|
|
326
373
|
}
|
|
327
374
|
/**
|
|
328
|
-
* 유효성 검증 없이
|
|
375
|
+
* 유효성 검증 없이 평탄화 수행
|
|
329
376
|
*/
|
|
330
377
|
flatWithoutNavigate({ target, prefix }) {
|
|
331
|
-
return Object.entries(target)
|
|
332
|
-
|
|
378
|
+
return Object.entries(target)
|
|
379
|
+
.flatMap(([key, value]) => {
|
|
380
|
+
const newKey = dotJoin(prefix, key);
|
|
333
381
|
if (isObject(value)) {
|
|
334
382
|
return this.flatWithoutNavigate({
|
|
335
383
|
target: value,
|
|
@@ -350,20 +398,26 @@ class CompatibilityChecker {
|
|
|
350
398
|
if (!this.isCompatible(value, typeData)) {
|
|
351
399
|
const typeName = getJSONTypeName(value);
|
|
352
400
|
if (typeName == null) {
|
|
353
|
-
throw new
|
|
401
|
+
throw new UnserializableTypeError(key, value);
|
|
354
402
|
}
|
|
355
403
|
else if (typeName === 'null') {
|
|
356
|
-
throw new
|
|
404
|
+
throw new IncompatibleTypeError(`Incompatible type for field '${key}': expected '${typeData.type}' and not nullable, received null`);
|
|
357
405
|
}
|
|
358
406
|
else if (typeName === 'array' && typeData.type === 'array') {
|
|
359
|
-
throw new
|
|
407
|
+
throw new IncompatibleTypeError(`Incompatible array structure for field '${key}'`);
|
|
408
|
+
}
|
|
409
|
+
else if (typeData.type === 'union') {
|
|
410
|
+
const expected = this.getUnionTypeNames(typeData);
|
|
411
|
+
throw new IncompatibleTypeError(`Incompatible type for field '${key}': expected one of (${expected.join(' | ')}), received ${value} ('${typeName}')`);
|
|
360
412
|
}
|
|
361
413
|
else {
|
|
362
|
-
throw new
|
|
414
|
+
throw new IncompatibleTypeError(`Incompatible type for field '${key}': expected '${typeData.type}', received '${typeName}'`);
|
|
363
415
|
}
|
|
364
416
|
}
|
|
365
417
|
}
|
|
366
418
|
isCompatible(target, jsonTypeData) {
|
|
419
|
+
// jsonTypeData 타입이 primitive 인 경우
|
|
420
|
+
// union 타입 검사 시 isCompatible() 가 다시 호출된 경우 발생
|
|
367
421
|
if (typeof jsonTypeData !== 'object') {
|
|
368
422
|
return target === jsonTypeData;
|
|
369
423
|
}
|
|
@@ -372,12 +426,12 @@ class CompatibilityChecker {
|
|
|
372
426
|
if (targetType === null) {
|
|
373
427
|
return false;
|
|
374
428
|
}
|
|
375
|
-
else if (targetType === 'null') {
|
|
376
|
-
return jsonTypeData.nullable;
|
|
377
|
-
}
|
|
378
429
|
else if (jsonTypeData.type === 'any') {
|
|
379
430
|
return true;
|
|
380
431
|
}
|
|
432
|
+
else if (targetType === 'null') {
|
|
433
|
+
return jsonTypeData.nullable;
|
|
434
|
+
}
|
|
381
435
|
else if (jsonTypeData.type === 'union') {
|
|
382
436
|
for (const candidate of jsonTypeData.candidates) {
|
|
383
437
|
if (this.isCompatible(target, candidate)) {
|
|
@@ -402,9 +456,7 @@ class CompatibilityChecker {
|
|
|
402
456
|
}
|
|
403
457
|
}
|
|
404
458
|
isArrayCompatible(array, arrayTypeData) {
|
|
405
|
-
if (!arrayTypeData.strict)
|
|
406
|
-
return true;
|
|
407
|
-
if (!arrayTypeData.element)
|
|
459
|
+
if (!arrayTypeData.strict || !arrayTypeData.element)
|
|
408
460
|
return true;
|
|
409
461
|
for (const ele of array) {
|
|
410
462
|
if (!this.isCompatible(ele, arrayTypeData.element)) {
|
|
@@ -428,6 +480,16 @@ class CompatibilityChecker {
|
|
|
428
480
|
return false;
|
|
429
481
|
}
|
|
430
482
|
}
|
|
483
|
+
getUnionTypeNames(union) {
|
|
484
|
+
return union.candidates.map((c) => {
|
|
485
|
+
if (typeof c === 'object') {
|
|
486
|
+
return c.type;
|
|
487
|
+
}
|
|
488
|
+
else {
|
|
489
|
+
return c.toString();
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
}
|
|
431
493
|
}
|
|
432
494
|
|
|
433
495
|
class SchemaFlattener {
|
|
@@ -498,6 +560,56 @@ class MockJSONFS {
|
|
|
498
560
|
}
|
|
499
561
|
}
|
|
500
562
|
|
|
563
|
+
class DefaultValueProvider {
|
|
564
|
+
#navigate;
|
|
565
|
+
constructor(navigate = null) {
|
|
566
|
+
this.#navigate = navigate;
|
|
567
|
+
}
|
|
568
|
+
get(key) {
|
|
569
|
+
if (this.#navigate == null) {
|
|
570
|
+
return undefined;
|
|
571
|
+
}
|
|
572
|
+
const result = this.#navigate.walk(key, { allowIntermediate: true });
|
|
573
|
+
if (result == null) {
|
|
574
|
+
return undefined;
|
|
575
|
+
}
|
|
576
|
+
else if (isJSONTypeData(result.value)) {
|
|
577
|
+
const defaultValue = result.value.default_value;
|
|
578
|
+
return ((defaultValue != null)
|
|
579
|
+
? defaultValue
|
|
580
|
+
: undefined);
|
|
581
|
+
}
|
|
582
|
+
else {
|
|
583
|
+
// for (const [key, value] of Object.entries(result.value)) {
|
|
584
|
+
// console.log(key, value);
|
|
585
|
+
// }
|
|
586
|
+
return this.#getDefaultData(result.value);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
#getDefaultData(typeData) {
|
|
590
|
+
if (t$1 in typeData) {
|
|
591
|
+
if (typeData.value.default_value != null) {
|
|
592
|
+
return typeData.value.default_value;
|
|
593
|
+
}
|
|
594
|
+
return undefined;
|
|
595
|
+
}
|
|
596
|
+
else {
|
|
597
|
+
let hasKey = false;
|
|
598
|
+
const result = {};
|
|
599
|
+
for (const [key, value] of Object.entries(typeData)) {
|
|
600
|
+
const defaultValue = this.#getDefaultData(value);
|
|
601
|
+
if (defaultValue !== undefined) {
|
|
602
|
+
result[key] = defaultValue;
|
|
603
|
+
hasKey = true;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
return (hasKey
|
|
607
|
+
? result
|
|
608
|
+
: undefined);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
501
613
|
class JSONAccessor {
|
|
502
614
|
static anyJSONType = JSONType.Any().nullable().value;
|
|
503
615
|
filePath;
|
|
@@ -506,6 +618,7 @@ class JSONAccessor {
|
|
|
506
618
|
jsonFS = new JSONFS();
|
|
507
619
|
#tree = null;
|
|
508
620
|
#flatter;
|
|
621
|
+
#defaultValueProvider;
|
|
509
622
|
#isDropped = false;
|
|
510
623
|
#changed = true;
|
|
511
624
|
constructor(filePath, tree = null) {
|
|
@@ -515,9 +628,11 @@ class JSONAccessor {
|
|
|
515
628
|
this.#tree = tree;
|
|
516
629
|
this.explorer = r$1.from(tree, { delimiter: '.', allowWildcard: true, allowRecursiveWildcard: false });
|
|
517
630
|
this.#flatter = new SchemaFlattener(this.explorer);
|
|
631
|
+
this.#defaultValueProvider = new DefaultValueProvider(this.explorer);
|
|
518
632
|
}
|
|
519
633
|
else {
|
|
520
634
|
this.#flatter = new SchemaFlattener();
|
|
635
|
+
this.#defaultValueProvider = new DefaultValueProvider();
|
|
521
636
|
}
|
|
522
637
|
}
|
|
523
638
|
get tree() {
|
|
@@ -569,13 +684,20 @@ class JSONAccessor {
|
|
|
569
684
|
}
|
|
570
685
|
getOne(key) {
|
|
571
686
|
this.#ensureNotDropped();
|
|
572
|
-
|
|
687
|
+
let value = this.#getData(key);
|
|
688
|
+
if (value == null) {
|
|
689
|
+
value = this.#defaultValueProvider.get(key);
|
|
690
|
+
}
|
|
691
|
+
return value;
|
|
573
692
|
}
|
|
574
693
|
get(...keys) {
|
|
575
694
|
this.#ensureNotDropped();
|
|
576
695
|
const result = {};
|
|
577
696
|
for (const key of keys) {
|
|
578
|
-
|
|
697
|
+
let value = this.#getData(key);
|
|
698
|
+
if (value == null) {
|
|
699
|
+
value = this.#defaultValueProvider.get(key);
|
|
700
|
+
}
|
|
579
701
|
const resolved = resolveNestedRef(result, key, true);
|
|
580
702
|
resolved.parent[resolved.key] = value;
|
|
581
703
|
}
|