@weborigami/async-tree 0.0.46 → 0.0.47
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/index.ts +17 -0
- package/main.js +2 -3
- package/package.json +4 -4
- package/src/BrowserFileTree.js +1 -1
- package/src/DeepMapTree.js +1 -1
- package/src/DeepObjectTree.js +1 -2
- package/src/DeferredTree.js +22 -9
- package/src/FileTree.js +18 -17
- package/src/FunctionTree.js +0 -4
- package/src/MapTree.js +1 -1
- package/src/ObjectTree.js +1 -1
- package/src/SetTree.js +1 -1
- package/src/SiteTree.js +1 -6
- package/src/Tree.d.ts +1 -0
- package/src/Tree.js +48 -37
- package/src/internal.js +15 -0
- package/src/keysJson.js +1 -1
- package/src/operations/cache.js +2 -2
- package/src/operations/mergeDeep.js +1 -1
- package/src/symbols.js +1 -0
- package/src/transforms/cachedKeyFunctions.js +1 -1
- package/src/transforms/groupBy.js +2 -3
- package/src/transforms/map.js +1 -1
- package/src/transforms/regExpKeys.js +1 -1
- package/src/transforms/sort.js +1 -1
- package/src/transforms/sortBy.js +1 -1
- package/src/utilities.d.ts +3 -1
- package/src/utilities.js +37 -9
- package/test/BrowserFileTree.test.js +1 -1
- package/test/DeepObjectTree.test.js +1 -2
- package/test/DeferredTree.test.js +1 -2
- package/test/FileTree.test.js +1 -1
- package/test/FunctionTree.test.js +0 -6
- package/test/ObjectTree.test.js +1 -2
- package/test/SetTree.test.js +1 -1
- package/test/SiteTree.test.js +1 -1
- package/test/Tree.test.js +12 -22
- package/test/operations/cache.test.js +1 -2
- package/test/operations/merge.test.js +1 -1
- package/test/operations/mergeDeep.test.js +1 -2
- package/test/transforms/cachedKeyMaps.test.js +1 -1
- package/test/transforms/groupBy.test.js +1 -1
- package/test/transforms/keyMapsForExtensions.test.js +1 -2
- package/test/transforms/map.test.js +1 -3
- package/test/transforms/regExpKeys.test.js +1 -2
- package/test/transforms/sort.test.js +1 -1
- package/test/transforms/sortBy.test.js +1 -1
- package/test/transforms/sortNatural.test.js +1 -1
package/index.ts
CHANGED
|
@@ -15,6 +15,12 @@ export type HasString = {
|
|
|
15
15
|
toString(): string;
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* A packed value is one that can be written to a file via fs.writeFile or into
|
|
20
|
+
* an HTTP response via response.write, or readily converted to such a form.
|
|
21
|
+
*/
|
|
22
|
+
export type Packed = ArrayBuffer | Buffer | ReadableStream | string | String | TypedArray;
|
|
23
|
+
|
|
18
24
|
export type PlainObject = {
|
|
19
25
|
[key: string]: any;
|
|
20
26
|
};
|
|
@@ -37,6 +43,17 @@ export type Treelike =
|
|
|
37
43
|
|
|
38
44
|
export type TreeTransform = (treelike: Treelike) => AsyncTree;
|
|
39
45
|
|
|
46
|
+
export type TypedArray =
|
|
47
|
+
Float32Array |
|
|
48
|
+
Float64Array |
|
|
49
|
+
Int8Array |
|
|
50
|
+
Int16Array |
|
|
51
|
+
Int32Array |
|
|
52
|
+
Uint8Array |
|
|
53
|
+
Uint8ClampedArray |
|
|
54
|
+
Uint16Array |
|
|
55
|
+
Uint32Array;
|
|
56
|
+
|
|
40
57
|
export type Unpackable<T> = {
|
|
41
58
|
unpack(): Promise<T>
|
|
42
59
|
};
|
package/main.js
CHANGED
|
@@ -4,17 +4,16 @@ export { default as DeferredTree } from "./src/DeferredTree.js";
|
|
|
4
4
|
export { default as FileTree } from "./src/FileTree.js";
|
|
5
5
|
export { default as FunctionTree } from "./src/FunctionTree.js";
|
|
6
6
|
export { default as MapTree } from "./src/MapTree.js";
|
|
7
|
-
export { default as ObjectTree } from "./src/ObjectTree.js";
|
|
8
7
|
// Skip BrowserFileTree.js, which is browser-only.
|
|
9
8
|
export { default as DeepMapTree } from "./src/DeepMapTree.js";
|
|
10
|
-
export { default as DeepObjectTree } from "./src/DeepObjectTree.js";
|
|
11
9
|
export { default as SetTree } from "./src/SetTree.js";
|
|
12
10
|
export { default as SiteTree } from "./src/SiteTree.js";
|
|
13
|
-
export
|
|
11
|
+
export { DeepObjectTree, ObjectTree, Tree } from "./src/internal.js";
|
|
14
12
|
export * as keysJson from "./src/keysJson.js";
|
|
15
13
|
export { default as cache } from "./src/operations/cache.js";
|
|
16
14
|
export { default as merge } from "./src/operations/merge.js";
|
|
17
15
|
export { default as mergeDeep } from "./src/operations/mergeDeep.js";
|
|
16
|
+
export * as symbols from "./src/symbols.js";
|
|
18
17
|
export { default as cachedKeyFunctions } from "./src/transforms/cachedKeyFunctions.js";
|
|
19
18
|
export { default as groupBy } from "./src/transforms/groupBy.js";
|
|
20
19
|
export { default as keyFunctionsForExtensions } from "./src/transforms/keyFunctionsForExtensions.js";
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weborigami/async-tree",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.47",
|
|
4
4
|
"description": "Asynchronous tree drivers based on standard JavaScript classes",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./main.js",
|
|
7
7
|
"browser": "./browser.js",
|
|
8
8
|
"types": "./index.ts",
|
|
9
9
|
"devDependencies": {
|
|
10
|
-
"@types/node": "20.11.
|
|
11
|
-
"typescript": "5.
|
|
10
|
+
"@types/node": "20.11.27",
|
|
11
|
+
"typescript": "5.4.2"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@weborigami/types": "0.0.
|
|
14
|
+
"@weborigami/types": "0.0.47"
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
17
|
"test": "node --test --test-reporter=spec",
|
package/src/BrowserFileTree.js
CHANGED
package/src/DeepMapTree.js
CHANGED
package/src/DeepObjectTree.js
CHANGED
package/src/DeferredTree.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Tree } from "./internal.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* A tree that is loaded lazily.
|
|
@@ -19,7 +19,8 @@ export default class DeferredTree {
|
|
|
19
19
|
this.loader = loader;
|
|
20
20
|
this.treePromise = null;
|
|
21
21
|
this._tree = null;
|
|
22
|
-
this.
|
|
22
|
+
this._parentUntilLoaded = null;
|
|
23
|
+
this._scopeUntilLoaded = null;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
async get(key) {
|
|
@@ -39,14 +40,17 @@ export default class DeferredTree {
|
|
|
39
40
|
return tree.keys();
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
// A deferred tree's parent generally comes from the loaded tree. However, if
|
|
44
|
+
// someone tries to get or set the parent before the tree is loaded, we store
|
|
45
|
+
// that parent reference and apply it once the tree is loaded.
|
|
42
46
|
get parent() {
|
|
43
|
-
return this._tree?.parent ?? this.
|
|
47
|
+
return this._tree?.parent ?? this._parentUntilLoaded;
|
|
44
48
|
}
|
|
45
49
|
set parent(parent) {
|
|
46
50
|
if (this._tree && !this._tree.parent) {
|
|
47
51
|
this._tree.parent = parent;
|
|
48
52
|
} else {
|
|
49
|
-
this.
|
|
53
|
+
this._parentUntilLoaded = parent;
|
|
50
54
|
}
|
|
51
55
|
}
|
|
52
56
|
|
|
@@ -61,9 +65,11 @@ export default class DeferredTree {
|
|
|
61
65
|
return /** @type {any} */ (this._tree)?.scope;
|
|
62
66
|
}
|
|
63
67
|
set scope(scope) {
|
|
64
|
-
//
|
|
65
|
-
if (this._tree) {
|
|
68
|
+
// As with `parent`, we can defer setting of scope.
|
|
69
|
+
if (this._tree && !(/** @type {any} */ (this._tree).scope)) {
|
|
66
70
|
/** @type {any} */ (this._tree).scope = scope;
|
|
71
|
+
} else {
|
|
72
|
+
this._scopeUntilLoaded = scope;
|
|
67
73
|
}
|
|
68
74
|
}
|
|
69
75
|
|
|
@@ -75,11 +81,18 @@ export default class DeferredTree {
|
|
|
75
81
|
// Use a promise to ensure the treelike is only converted to a tree once.
|
|
76
82
|
this.treePromise ??= this.loadResult().then((treelike) => {
|
|
77
83
|
this._tree = Tree.from(treelike);
|
|
78
|
-
if (this.
|
|
84
|
+
if (this._parentUntilLoaded) {
|
|
85
|
+
// Now that the tree has been loaded, we can set its parent.
|
|
79
86
|
if (!this._tree.parent) {
|
|
80
|
-
this._tree.parent = this.
|
|
87
|
+
this._tree.parent = this._parentUntilLoaded;
|
|
88
|
+
}
|
|
89
|
+
this._parentUntilLoaded = null;
|
|
90
|
+
}
|
|
91
|
+
if (this._scopeUntilLoaded) {
|
|
92
|
+
if (!(/** @type {any} */ (this._tree).scope)) {
|
|
93
|
+
/** @type {any} */ (this._tree).scope = this._scopeUntilLoaded;
|
|
81
94
|
}
|
|
82
|
-
this.
|
|
95
|
+
this._scopeUntilLoaded = null;
|
|
83
96
|
}
|
|
84
97
|
return this._tree;
|
|
85
98
|
});
|
package/src/FileTree.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
4
|
-
import
|
|
4
|
+
import { Tree } from "./internal.js";
|
|
5
5
|
import {
|
|
6
6
|
getRealmObjectPrototype,
|
|
7
7
|
hiddenFileNames,
|
|
8
|
+
isPacked,
|
|
8
9
|
sortNatural,
|
|
9
10
|
} from "./utilities.js";
|
|
10
11
|
|
|
11
|
-
const TypedArray = Object.getPrototypeOf(Uint8Array);
|
|
12
|
-
|
|
13
12
|
/**
|
|
14
13
|
* A file system tree via the Node file system API.
|
|
15
14
|
*
|
|
@@ -133,27 +132,29 @@ export default class FileTree {
|
|
|
133
132
|
value = await value();
|
|
134
133
|
}
|
|
135
134
|
|
|
135
|
+
let packed = false;
|
|
136
136
|
if (value === null) {
|
|
137
137
|
// Treat null value as empty string; will create an empty file.
|
|
138
138
|
value = "";
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
value instanceof DataView ||
|
|
148
|
-
(globalThis.ReadableStream && value instanceof ReadableStream);
|
|
149
|
-
|
|
150
|
-
if (!isWriteable && isStringLike(value)) {
|
|
139
|
+
packed = true;
|
|
140
|
+
} else if (isPacked(value)) {
|
|
141
|
+
packed = true;
|
|
142
|
+
} else if (typeof value.pack === "function") {
|
|
143
|
+
// Pack the value for writing.
|
|
144
|
+
value = await value.pack();
|
|
145
|
+
packed = true;
|
|
146
|
+
} else if (isStringLike(value)) {
|
|
151
147
|
// Value has a meaningful `toString` method, use that.
|
|
152
148
|
value = String(value);
|
|
153
|
-
|
|
149
|
+
packed = true;
|
|
154
150
|
}
|
|
155
151
|
|
|
156
|
-
if (
|
|
152
|
+
if (packed) {
|
|
153
|
+
// Single writeable value.
|
|
154
|
+
if (value instanceof ArrayBuffer) {
|
|
155
|
+
// Convert ArrayBuffer to Uint8Array, which Node.js can write directly.
|
|
156
|
+
value = new Uint8Array(value);
|
|
157
|
+
}
|
|
157
158
|
// Ensure this directory exists.
|
|
158
159
|
await fs.mkdir(this.dirname, { recursive: true });
|
|
159
160
|
// Write out the value as the contents of a file.
|
package/src/FunctionTree.js
CHANGED
package/src/MapTree.js
CHANGED
package/src/ObjectTree.js
CHANGED
package/src/SetTree.js
CHANGED
package/src/SiteTree.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Tree } from "./internal.js";
|
|
2
2
|
import * as keysJson from "./keysJson.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -152,11 +152,6 @@ export default class SiteTree {
|
|
|
152
152
|
return Reflect.construct(this.constructor, [href]);
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
async unpack() {
|
|
156
|
-
const response = await fetch(this.href);
|
|
157
|
-
return response.ok ? response.arrayBuffer() : undefined;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
155
|
get url() {
|
|
161
156
|
return new URL(this.href);
|
|
162
157
|
}
|
package/src/Tree.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export function has(AsyncTree: AsyncTree, key: any): Promise<boolean>;
|
|
|
10
10
|
export function isAsyncMutableTree(obj: any): obj is AsyncMutableTree;
|
|
11
11
|
export function isAsyncTree(obj: any): obj is AsyncTree;
|
|
12
12
|
export function isKeyForSubtree(tree: AsyncTree, obj: any): Promise<boolean>;
|
|
13
|
+
export function isTraversable(obj: any): boolean;
|
|
13
14
|
export function isTreelike(obj: any): obj is Treelike;
|
|
14
15
|
export function map(tree: Treelike, valueFn: ValueKeyFn): AsyncTree;
|
|
15
16
|
export function mapReduce(tree: Treelike, mapFn: ValueKeyFn|null, reduceFn: ReduceFn): Promise<any>;
|
package/src/Tree.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import DeferredTree from "./DeferredTree.js";
|
|
2
2
|
import FunctionTree from "./FunctionTree.js";
|
|
3
3
|
import MapTree from "./MapTree.js";
|
|
4
|
-
import ObjectTree from "./ObjectTree.js";
|
|
5
4
|
import SetTree from "./SetTree.js";
|
|
5
|
+
import { DeepObjectTree, ObjectTree } from "./internal.js";
|
|
6
6
|
import mapTransform from "./transforms/map.js";
|
|
7
7
|
import * as utilities from "./utilities.js";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
castArrayLike,
|
|
10
|
+
isPacked,
|
|
11
|
+
isPlainObject,
|
|
12
|
+
isUnpackable,
|
|
13
|
+
} from "./utilities.js";
|
|
9
14
|
|
|
10
15
|
/**
|
|
11
16
|
* Helper functions for working with async trees
|
|
@@ -112,7 +117,9 @@ export function from(obj) {
|
|
|
112
117
|
return new MapTree(obj);
|
|
113
118
|
} else if (obj instanceof Set) {
|
|
114
119
|
return new SetTree(obj);
|
|
115
|
-
} else if (obj
|
|
120
|
+
} else if (isPlainObject(obj)) {
|
|
121
|
+
return new DeepObjectTree(obj);
|
|
122
|
+
} else if (isUnpackable(obj)) {
|
|
116
123
|
async function AsyncFunction() {} // Sample async function
|
|
117
124
|
return obj.unpack instanceof AsyncFunction.constructor
|
|
118
125
|
? // Async unpack: return a deferred tree.
|
|
@@ -163,23 +170,52 @@ export function isAsyncMutableTree(object) {
|
|
|
163
170
|
return isAsyncTree(object) && typeof object.set === "function";
|
|
164
171
|
}
|
|
165
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Return true if the indicated key produces or is expected to produce an
|
|
175
|
+
* async tree.
|
|
176
|
+
*
|
|
177
|
+
* This defers to the tree's own isKeyForSubtree method. If not found, this
|
|
178
|
+
* gets the value of that key and returns true if the value is an async
|
|
179
|
+
* tree.
|
|
180
|
+
*/
|
|
181
|
+
export async function isKeyForSubtree(tree, key) {
|
|
182
|
+
if (tree.isKeyForSubtree) {
|
|
183
|
+
return tree.isKeyForSubtree(key);
|
|
184
|
+
}
|
|
185
|
+
const value = await tree.get(key);
|
|
186
|
+
return isAsyncTree(value);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Return true if the object can be traversed via the `traverse()` method. The
|
|
191
|
+
* object must be either treelike or a packed object with an `unpack()` method.
|
|
192
|
+
*
|
|
193
|
+
* @param {any} object
|
|
194
|
+
*/
|
|
195
|
+
export function isTraversable(object) {
|
|
196
|
+
return (
|
|
197
|
+
isTreelike(object) ||
|
|
198
|
+
(isPacked(object) && /** @type {any} */ (object).unpack instanceof Function)
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
166
202
|
/**
|
|
167
203
|
* Returns true if the indicated object can be directly treated as an
|
|
168
204
|
* asynchronous tree. This includes:
|
|
169
205
|
*
|
|
170
206
|
* - An object that implements the AsyncTree interface (including
|
|
171
207
|
* AsyncTree instances)
|
|
172
|
-
* - An object that implements the `unpack()` method
|
|
173
208
|
* - A function
|
|
174
209
|
* - An `Array` instance
|
|
175
210
|
* - A `Map` instance
|
|
176
211
|
* - A `Set` instance
|
|
177
212
|
* - A plain object
|
|
178
213
|
*
|
|
179
|
-
* Note: the `from()` method accepts any JavaScript object, but `
|
|
214
|
+
* Note: the `from()` method accepts any JavaScript object, but `isTreelike`
|
|
180
215
|
* returns `false` for an object that isn't one of the above types.
|
|
181
216
|
*
|
|
182
217
|
* @param {any} object
|
|
218
|
+
* @returns {obj is Treelike}
|
|
183
219
|
*/
|
|
184
220
|
export function isTreelike(object) {
|
|
185
221
|
return (
|
|
@@ -187,27 +223,10 @@ export function isTreelike(object) {
|
|
|
187
223
|
object instanceof Function ||
|
|
188
224
|
object instanceof Array ||
|
|
189
225
|
object instanceof Set ||
|
|
190
|
-
object?.unpack instanceof Function ||
|
|
191
226
|
isPlainObject(object)
|
|
192
227
|
);
|
|
193
228
|
}
|
|
194
229
|
|
|
195
|
-
/**
|
|
196
|
-
* Return true if the indicated key produces or is expected to produce an
|
|
197
|
-
* async tree.
|
|
198
|
-
*
|
|
199
|
-
* This defers to the tree's own isKeyForSubtree method. If not found, this
|
|
200
|
-
* gets the value of that key and returns true if the value is an async
|
|
201
|
-
* tree.
|
|
202
|
-
*/
|
|
203
|
-
export async function isKeyForSubtree(tree, key) {
|
|
204
|
-
if (tree.isKeyForSubtree) {
|
|
205
|
-
return tree.isKeyForSubtree(key);
|
|
206
|
-
}
|
|
207
|
-
const value = await tree.get(key);
|
|
208
|
-
return isAsyncTree(value);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
230
|
/**
|
|
212
231
|
* Return a new tree with deeply-mapped values of the original tree.
|
|
213
232
|
*
|
|
@@ -364,24 +383,16 @@ export async function traverseOrThrow(treelike, ...keys) {
|
|
|
364
383
|
throw new TraverseError(message, treelike, keys);
|
|
365
384
|
}
|
|
366
385
|
|
|
367
|
-
//
|
|
368
|
-
if (
|
|
369
|
-
// Unpack the value if it defines an `unpack` function, otherwise return
|
|
370
|
-
// the value itself.
|
|
371
|
-
return typeof value.unpack === "function" ? await value.unpack() : value;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// If the value is not a function or async tree already, but can be
|
|
375
|
-
// unpacked, unpack it.
|
|
376
|
-
if (
|
|
377
|
-
!(value instanceof Function) &&
|
|
378
|
-
!isAsyncTree(value) &&
|
|
379
|
-
value.unpack instanceof Function
|
|
380
|
-
) {
|
|
386
|
+
// If the value is packed and can be unpacked, unpack it.
|
|
387
|
+
if (isUnpackable(value)) {
|
|
381
388
|
value = await value.unpack();
|
|
382
389
|
}
|
|
383
390
|
|
|
384
|
-
if
|
|
391
|
+
// Peek ahead: if there's only one key left and it's an empty string, return
|
|
392
|
+
// the value itself.
|
|
393
|
+
if (remainingKeys.length === 1 && remainingKeys[0] === "") {
|
|
394
|
+
return value;
|
|
395
|
+
} else if (value instanceof Function) {
|
|
385
396
|
// Value is a function: call it with the remaining keys.
|
|
386
397
|
const fn = value;
|
|
387
398
|
// We'll take as many keys as the function's length, but at least one.
|
package/src/internal.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//
|
|
2
|
+
// This library includes a number of modules with circular dependencies. This
|
|
3
|
+
// module exists to explicitly set the loading order for those modules. To
|
|
4
|
+
// enforce use of this loading order, other modules should only load the modules
|
|
5
|
+
// below via this module.
|
|
6
|
+
//
|
|
7
|
+
// About this pattern:
|
|
8
|
+
// https://medium.com/visual-development/how-to-fix-nasty-circular-dependency-issues-once-and-for-all-in-javascript-typescript-a04c987cf0de
|
|
9
|
+
//
|
|
10
|
+
|
|
11
|
+
export * as Tree from "./Tree.js";
|
|
12
|
+
|
|
13
|
+
export { default as ObjectTree } from "./ObjectTree.js";
|
|
14
|
+
|
|
15
|
+
export { default as DeepObjectTree } from "./DeepObjectTree.js";
|
package/src/keysJson.js
CHANGED
package/src/operations/cache.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DeepObjectTree, Tree } from "../internal.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Caches values from a source tree in a second cache tree. If no second tree is
|
|
@@ -21,7 +21,7 @@ export default function treeCache(sourceTreelike, cacheTree, filterTreelike) {
|
|
|
21
21
|
const filter = filterTreelike ? Tree.from(filterTreelike) : undefined;
|
|
22
22
|
|
|
23
23
|
/** @type {AsyncMutableTree} */
|
|
24
|
-
const cache = cacheTree ?? new
|
|
24
|
+
const cache = cacheTree ?? new DeepObjectTree({});
|
|
25
25
|
return {
|
|
26
26
|
description: "cache",
|
|
27
27
|
|
package/src/symbols.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const parent = Symbol("parent");
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as Tree from "../Tree.js";
|
|
1
|
+
import { DeepObjectTree, Tree } from "../internal.js";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Given a function that returns a grouping key for a value, returns a transform
|
|
@@ -37,6 +36,6 @@ export default function createGroupByTransform(groupKeyFn) {
|
|
|
37
36
|
}
|
|
38
37
|
}
|
|
39
38
|
|
|
40
|
-
return new
|
|
39
|
+
return new DeepObjectTree(result);
|
|
41
40
|
};
|
|
42
41
|
}
|
package/src/transforms/map.js
CHANGED
package/src/transforms/sort.js
CHANGED
package/src/transforms/sortBy.js
CHANGED
package/src/utilities.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { PlainObject, StringLike } from "../index.ts";
|
|
1
|
+
import { Packed, PlainObject, StringLike } from "../index.ts";
|
|
2
2
|
|
|
3
3
|
export function castArrayLike(object: any): any;
|
|
4
4
|
export function getRealmObjectPrototype(object: any): any;
|
|
5
5
|
export const hiddenFileNames: string[];
|
|
6
|
+
export function isPacked(object: any): object is Packed;
|
|
6
7
|
export function isPlainObject(object: any): object is PlainObject;
|
|
8
|
+
export function isUnpackable(object): object is { unpack: () => any };
|
|
7
9
|
export function isStringLike(obj: any): obj is StringLike;
|
|
8
10
|
export function keysFromPath(path: string): string[];
|
|
9
11
|
export const naturalSortCompareFn: (a: string, b: string) => number;
|
package/src/utilities.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const TypedArray = Object.getPrototypeOf(Uint8Array);
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* If the given plain object has only sequential integer keys, return it as an
|
|
3
5
|
* array. Otherwise return it as is.
|
|
@@ -19,9 +21,6 @@ export function castArrayLike(object) {
|
|
|
19
21
|
return hasKeys ? Object.values(object) : object;
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
// Names of OS-generated files that should not be enumerated
|
|
23
|
-
export const hiddenFileNames = [".DS_Store"];
|
|
24
|
-
|
|
25
24
|
/**
|
|
26
25
|
* Return the Object prototype at the root of the object's prototype chain.
|
|
27
26
|
*
|
|
@@ -38,6 +37,27 @@ export function getRealmObjectPrototype(object) {
|
|
|
38
37
|
return proto;
|
|
39
38
|
}
|
|
40
39
|
|
|
40
|
+
// Names of OS-generated files that should not be enumerated
|
|
41
|
+
export const hiddenFileNames = [".DS_Store"];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Return true if the object is in a packed form (or can be readily packed into
|
|
45
|
+
* a form) that can be given to fs.writeFile or response.write().
|
|
46
|
+
*
|
|
47
|
+
* @param {any} object
|
|
48
|
+
* @returns {object is import("../index.ts").Packed}
|
|
49
|
+
*/
|
|
50
|
+
export function isPacked(object) {
|
|
51
|
+
return (
|
|
52
|
+
typeof object === "string" ||
|
|
53
|
+
object instanceof ArrayBuffer ||
|
|
54
|
+
object instanceof Buffer ||
|
|
55
|
+
object instanceof ReadableStream ||
|
|
56
|
+
object instanceof String ||
|
|
57
|
+
object instanceof TypedArray
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
41
61
|
/**
|
|
42
62
|
* Return true if the object is a plain JavaScript object created by `{}`,
|
|
43
63
|
* `new Object()`, or `Object.create(null)`.
|
|
@@ -46,6 +66,7 @@ export function getRealmObjectPrototype(object) {
|
|
|
46
66
|
* `Module`) as plain objects.
|
|
47
67
|
*
|
|
48
68
|
* @param {any} object
|
|
69
|
+
* @returns {object is import("../index.ts").PlainObject}
|
|
49
70
|
*/
|
|
50
71
|
export function isPlainObject(object) {
|
|
51
72
|
// From https://stackoverflow.com/q/51722354/76472
|
|
@@ -67,15 +88,15 @@ export function isPlainObject(object) {
|
|
|
67
88
|
* Return true if the object is a string or object with a non-trival `toString`
|
|
68
89
|
* method.
|
|
69
90
|
*
|
|
70
|
-
* @param {any}
|
|
71
|
-
* @returns {obj is import("
|
|
91
|
+
* @param {any} object
|
|
92
|
+
* @returns {obj is import("../index.ts").StringLike}
|
|
72
93
|
*/
|
|
73
|
-
export function isStringLike(
|
|
74
|
-
if (typeof
|
|
94
|
+
export function isStringLike(object) {
|
|
95
|
+
if (typeof object === "string") {
|
|
75
96
|
return true;
|
|
76
|
-
} else if (
|
|
97
|
+
} else if (object?.toString === undefined) {
|
|
77
98
|
return false;
|
|
78
|
-
} else if (
|
|
99
|
+
} else if (object.toString === getRealmObjectPrototype(object).toString) {
|
|
79
100
|
// The stupid Object.prototype.toString implementation always returns
|
|
80
101
|
// "[object Object]", so if that's the only toString method the object has,
|
|
81
102
|
// we return false.
|
|
@@ -85,6 +106,13 @@ export function isStringLike(obj) {
|
|
|
85
106
|
}
|
|
86
107
|
}
|
|
87
108
|
|
|
109
|
+
export function isUnpackable(object) {
|
|
110
|
+
return (
|
|
111
|
+
isPacked(object) &&
|
|
112
|
+
typeof (/** @type {any} */ (object).unpack) === "function"
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
88
116
|
/**
|
|
89
117
|
* Given a path like "/foo/bar/baz", return an array of keys like ["foo", "bar",
|
|
90
118
|
* "baz"].
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
3
|
import BrowserFileTree from "../src/BrowserFileTree.js";
|
|
4
|
-
import
|
|
4
|
+
import { Tree } from "../src/internal.js";
|
|
5
5
|
|
|
6
6
|
// Skip these tests if we're not in a browser.
|
|
7
7
|
const isBrowser = typeof window !== "undefined";
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import DeepObjectTree from "../src/
|
|
4
|
-
import * as Tree from "../src/Tree.js";
|
|
3
|
+
import { DeepObjectTree, Tree } from "../src/internal.js";
|
|
5
4
|
|
|
6
5
|
describe("DeepObjectTree", () => {
|
|
7
6
|
test("returns an ObjectTree for value that's a plain sub-object or sub-array", async () => {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
3
|
import DeferredTree from "../src/DeferredTree.js";
|
|
4
|
-
import ObjectTree from "../src/
|
|
5
|
-
import * as Tree from "../src/Tree.js";
|
|
4
|
+
import { ObjectTree, Tree } from "../src/internal.js";
|
|
6
5
|
|
|
7
6
|
describe("DeferredTree", () => {
|
|
8
7
|
test("lazy-loads a treelike object", async () => {
|
package/test/FileTree.test.js
CHANGED
|
@@ -4,7 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { describe, test } from "node:test";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import FileTree from "../src/FileTree.js";
|
|
7
|
-
import
|
|
7
|
+
import { Tree } from "../src/internal.js";
|
|
8
8
|
|
|
9
9
|
const dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
10
|
const tempDirectory = path.join(dirname, "fixtures/temp");
|
|
@@ -18,12 +18,6 @@ describe("FunctionTree", async () => {
|
|
|
18
18
|
assert.equal(alice, "Hello, **Alice**.");
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
test("unpacking the tree returns the function itself", async () => {
|
|
22
|
-
const fn = () => true;
|
|
23
|
-
const fixture = new FunctionTree(fn);
|
|
24
|
-
assert.equal(await fixture.unpack(), fn);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
21
|
test("getting a value from function with multiple arguments curries the function", async () => {
|
|
28
22
|
const fixture = new FunctionTree((a, b, c) => a + b + c);
|
|
29
23
|
const fnA = await fixture.get(1);
|
package/test/ObjectTree.test.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import ObjectTree from "../src/
|
|
4
|
-
import * as Tree from "../src/Tree.js";
|
|
3
|
+
import { ObjectTree, Tree } from "../src/internal.js";
|
|
5
4
|
|
|
6
5
|
describe("ObjectTree", () => {
|
|
7
6
|
test("can get the keys of the tree", async () => {
|
package/test/SetTree.test.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import ObjectTree from "../src/ObjectTree.js";
|
|
4
3
|
import SetTree from "../src/SetTree.js";
|
|
4
|
+
import { ObjectTree } from "../src/internal.js";
|
|
5
5
|
|
|
6
6
|
describe("SetTree", () => {
|
|
7
7
|
test("can get the keys of the tree", async () => {
|
package/test/SiteTree.test.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { beforeEach, describe, mock, test } from "node:test";
|
|
3
3
|
import SiteTree from "../src/SiteTree.js";
|
|
4
|
-
import
|
|
4
|
+
import { Tree } from "../src/internal.js";
|
|
5
5
|
|
|
6
6
|
const mockHost = "https://mock";
|
|
7
7
|
|
package/test/Tree.test.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import { DeepObjectTree } from "../main.js";
|
|
4
3
|
import MapTree from "../src/MapTree.js";
|
|
5
|
-
import ObjectTree from "../src/
|
|
6
|
-
import * as Tree from "../src/Tree.js";
|
|
4
|
+
import { DeepObjectTree, ObjectTree, Tree } from "../src/internal.js";
|
|
7
5
|
|
|
8
6
|
describe("Tree", () => {
|
|
9
7
|
test("assign applies one tree to another", async () => {
|
|
@@ -89,13 +87,10 @@ describe("Tree", () => {
|
|
|
89
87
|
});
|
|
90
88
|
|
|
91
89
|
test("from() uses an object's unpack() method if defined", async () => {
|
|
92
|
-
const obj =
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
};
|
|
97
|
-
},
|
|
98
|
-
};
|
|
90
|
+
const obj = new String();
|
|
91
|
+
/** @type {any} */ (obj).unpack = () => ({
|
|
92
|
+
a: "Hello, a.",
|
|
93
|
+
});
|
|
99
94
|
const tree = Tree.from(obj);
|
|
100
95
|
assert.deepEqual(await Tree.plain(tree), {
|
|
101
96
|
a: "Hello, a.",
|
|
@@ -103,13 +98,10 @@ describe("Tree", () => {
|
|
|
103
98
|
});
|
|
104
99
|
|
|
105
100
|
test("from() creates a deferred tree if unpack() returns a promise", async () => {
|
|
106
|
-
const obj =
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
};
|
|
111
|
-
},
|
|
112
|
-
};
|
|
101
|
+
const obj = new String();
|
|
102
|
+
/** @type {any} */ (obj).unpack = async () => ({
|
|
103
|
+
a: "Hello, a.",
|
|
104
|
+
});
|
|
113
105
|
const tree = Tree.from(obj);
|
|
114
106
|
assert.deepEqual(await Tree.plain(tree), {
|
|
115
107
|
a: "Hello, a.",
|
|
@@ -299,12 +291,10 @@ describe("Tree", () => {
|
|
|
299
291
|
});
|
|
300
292
|
|
|
301
293
|
test("traversing a final empty string can unpack the last value", async () => {
|
|
294
|
+
const unpackable = new String();
|
|
295
|
+
/** @type {any} */ (unpackable).unpack = () => "Content";
|
|
302
296
|
const tree = {
|
|
303
|
-
unpackable
|
|
304
|
-
unpack() {
|
|
305
|
-
return "Content";
|
|
306
|
-
},
|
|
307
|
-
},
|
|
297
|
+
unpackable,
|
|
308
298
|
};
|
|
309
299
|
const result = await Tree.traverse(tree, "unpackable", "");
|
|
310
300
|
assert.equal(result, "Content");
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import ObjectTree from "../../src/
|
|
4
|
-
import * as Tree from "../../src/Tree.js";
|
|
3
|
+
import { ObjectTree, Tree } from "../../src/internal.js";
|
|
5
4
|
import cache from "../../src/operations/cache.js";
|
|
6
5
|
|
|
7
6
|
describe("cache", () => {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import DeepObjectTree from "../../src/
|
|
4
|
-
import * as Tree from "../../src/Tree.js";
|
|
3
|
+
import { DeepObjectTree, Tree } from "../../src/internal.js";
|
|
5
4
|
import mergeDeep from "../../src/operations/mergeDeep.js";
|
|
6
5
|
|
|
7
6
|
describe("mergeDeep", () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import ObjectTree from "../../src/
|
|
3
|
+
import { ObjectTree } from "../../src/internal.js";
|
|
4
4
|
import cachedKeyFunctions from "../../src/transforms/cachedKeyFunctions.js";
|
|
5
5
|
|
|
6
6
|
describe("cachedKeyFunctions", () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import
|
|
3
|
+
import { Tree } from "../../src/internal.js";
|
|
4
4
|
import groupBy from "../../src/transforms/groupBy.js";
|
|
5
5
|
|
|
6
6
|
describe("groupBy transform", () => {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import ObjectTree from "../../src/
|
|
4
|
-
import * as Tree from "../../src/Tree.js";
|
|
3
|
+
import { ObjectTree, Tree } from "../../src/internal.js";
|
|
5
4
|
import keyFunctionsForExtensions from "../../src/transforms/keyFunctionsForExtensions.js";
|
|
6
5
|
import map from "../../src/transforms/map.js";
|
|
7
6
|
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import { ObjectTree } from "../../main.js";
|
|
4
|
-
import DeepObjectTree from "../../src/DeepObjectTree.js";
|
|
5
3
|
import FunctionTree from "../../src/FunctionTree.js";
|
|
6
|
-
import
|
|
4
|
+
import { DeepObjectTree, ObjectTree, Tree } from "../../src/internal.js";
|
|
7
5
|
import map from "../../src/transforms/map.js";
|
|
8
6
|
|
|
9
7
|
describe("map", () => {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import DeepObjectTree from "../../src/
|
|
4
|
-
import * as Tree from "../../src/Tree.js";
|
|
3
|
+
import { DeepObjectTree, Tree } from "../../src/internal.js";
|
|
5
4
|
import regExpKeys from "../../src/transforms/regExpKeys.js";
|
|
6
5
|
|
|
7
6
|
describe("regExpKeys", () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import
|
|
3
|
+
import { Tree } from "../../src/internal.js";
|
|
4
4
|
import sortBy from "../../src/transforms/sortBy.js";
|
|
5
5
|
|
|
6
6
|
describe("sortBy transform", () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import
|
|
3
|
+
import { Tree } from "../../src/internal.js";
|
|
4
4
|
import sortNatural from "../../src/transforms/sortNatural.js";
|
|
5
5
|
|
|
6
6
|
describe("sortNatural transform", () => {
|