@weborigami/async-tree 0.0.62 → 0.0.63
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 +1 -1
- package/package.json +2 -2
- package/src/ObjectTree.js +1 -1
- package/src/Tree.d.ts +2 -1
- package/src/Tree.js +44 -17
- package/src/operations/concat.js +1 -1
- package/src/operations/scope.js +2 -2
- package/src/transforms/sortFn.js +3 -5
- package/src/utilities.d.ts +4 -4
- package/src/utilities.js +21 -22
- package/test/Tree.test.js +19 -0
- package/test/utilities.test.js +1 -1
package/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weborigami/async-tree",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.63",
|
|
4
4
|
"description": "Asynchronous tree drivers based on standard JavaScript classes",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./main.js",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"typescript": "5.5.3"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@weborigami/types": "0.0.
|
|
14
|
+
"@weborigami/types": "0.0.63"
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
17
|
"test": "node --test --test-reporter=spec",
|
package/src/ObjectTree.js
CHANGED
package/src/Tree.d.ts
CHANGED
|
@@ -13,7 +13,8 @@ export function isKeyForSubtree(tree: AsyncTree, obj: any): Promise<boolean>;
|
|
|
13
13
|
export function isTraversable(obj: any): boolean;
|
|
14
14
|
export function isTreelike(obj: any): obj is Treelike;
|
|
15
15
|
export function map(tree: Treelike, valueFn: ValueKeyFn): AsyncTree;
|
|
16
|
-
export function mapReduce(tree: Treelike, mapFn: ValueKeyFn|null, reduceFn: ReduceFn): Promise<any>;
|
|
16
|
+
export function mapReduce(tree: Treelike, mapFn: ValueKeyFn | null, reduceFn: ReduceFn): Promise<any>;
|
|
17
|
+
export function paths(tree: Treelike, base: string): string[];
|
|
17
18
|
export function plain(tree: Treelike): Promise<PlainObject>;
|
|
18
19
|
export function remove(AsyncTree: AsyncMutableTree, key: any): Promise<boolean>;
|
|
19
20
|
export function toFunction(tree: Treelike): Function;
|
package/src/Tree.js
CHANGED
|
@@ -134,6 +134,14 @@ export function from(object, options = {}) {
|
|
|
134
134
|
} else if (object && typeof object === "object") {
|
|
135
135
|
// An instance of some class.
|
|
136
136
|
return new ObjectTree(object);
|
|
137
|
+
} else if (
|
|
138
|
+
typeof object === "string" ||
|
|
139
|
+
typeof object === "number" ||
|
|
140
|
+
typeof object === "boolean"
|
|
141
|
+
) {
|
|
142
|
+
// A primitive value; box it into an object and construct a tree.
|
|
143
|
+
const boxed = utilities.box(object);
|
|
144
|
+
return new ObjectTree(boxed);
|
|
137
145
|
}
|
|
138
146
|
|
|
139
147
|
throw new TypeError("Couldn't convert argument to an async tree");
|
|
@@ -154,25 +162,23 @@ export async function has(tree, key) {
|
|
|
154
162
|
/**
|
|
155
163
|
* Return true if the indicated object is an async tree.
|
|
156
164
|
*
|
|
157
|
-
* @param {any}
|
|
165
|
+
* @param {any} obj
|
|
158
166
|
* @returns {obj is AsyncTree}
|
|
159
167
|
*/
|
|
160
|
-
export function isAsyncTree(
|
|
161
|
-
return
|
|
162
|
-
object &&
|
|
163
|
-
typeof object.get === "function" &&
|
|
164
|
-
typeof object.keys === "function"
|
|
165
|
-
);
|
|
168
|
+
export function isAsyncTree(obj) {
|
|
169
|
+
return obj && typeof obj.get === "function" && typeof obj.keys === "function";
|
|
166
170
|
}
|
|
167
171
|
|
|
168
172
|
/**
|
|
169
173
|
* Return true if the indicated object is an async mutable tree.
|
|
170
174
|
*
|
|
171
|
-
* @param {any}
|
|
175
|
+
* @param {any} obj
|
|
172
176
|
* @returns {obj is AsyncMutableTree}
|
|
173
177
|
*/
|
|
174
|
-
export function isAsyncMutableTree(
|
|
175
|
-
return
|
|
178
|
+
export function isAsyncMutableTree(obj) {
|
|
179
|
+
return (
|
|
180
|
+
isAsyncTree(obj) && typeof (/** @type {any} */ (obj).set) === "function"
|
|
181
|
+
);
|
|
176
182
|
}
|
|
177
183
|
|
|
178
184
|
/**
|
|
@@ -219,16 +225,16 @@ export function isTraversable(object) {
|
|
|
219
225
|
* Note: the `from()` method accepts any JavaScript object, but `isTreelike`
|
|
220
226
|
* returns `false` for an object that isn't one of the above types.
|
|
221
227
|
*
|
|
222
|
-
* @param {any}
|
|
228
|
+
* @param {any} obj
|
|
223
229
|
* @returns {obj is Treelike}
|
|
224
230
|
*/
|
|
225
|
-
export function isTreelike(
|
|
231
|
+
export function isTreelike(obj) {
|
|
226
232
|
return (
|
|
227
|
-
isAsyncTree(
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
isPlainObject(
|
|
233
|
+
isAsyncTree(obj) ||
|
|
234
|
+
obj instanceof Function ||
|
|
235
|
+
obj instanceof Array ||
|
|
236
|
+
obj instanceof Set ||
|
|
237
|
+
isPlainObject(obj)
|
|
232
238
|
);
|
|
233
239
|
}
|
|
234
240
|
|
|
@@ -283,6 +289,27 @@ export async function mapReduce(treelike, valueFn, reduceFn) {
|
|
|
283
289
|
return reduceFn(values, keys);
|
|
284
290
|
}
|
|
285
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Returns slash-separated paths for all values in the tree.
|
|
294
|
+
*
|
|
295
|
+
* @param {Treelike} treelike
|
|
296
|
+
* @param {string} base
|
|
297
|
+
*/
|
|
298
|
+
export async function paths(treelike, base = "") {
|
|
299
|
+
const tree = from(treelike);
|
|
300
|
+
const result = [];
|
|
301
|
+
for (const key of await tree.keys()) {
|
|
302
|
+
const valuePath = base ? `${base}/${key}` : key;
|
|
303
|
+
const value = await tree.get(key);
|
|
304
|
+
if (await isAsyncTree(value)) {
|
|
305
|
+
const subPaths = await paths(value, valuePath);
|
|
306
|
+
result.push(...subPaths);
|
|
307
|
+
} else {
|
|
308
|
+
result.push(valuePath);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return result;
|
|
312
|
+
}
|
|
286
313
|
/**
|
|
287
314
|
* Converts an asynchronous tree into a synchronous plain JavaScript object.
|
|
288
315
|
*
|
package/src/operations/concat.js
CHANGED
|
@@ -7,7 +7,7 @@ import deepValuesIterator from "./deepValuesIterator.js";
|
|
|
7
7
|
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
8
8
|
*
|
|
9
9
|
* @this {AsyncTree|null}
|
|
10
|
-
* @param {import("
|
|
10
|
+
* @param {import("../../index.ts").Treelike} treelike
|
|
11
11
|
*/
|
|
12
12
|
export default async function concatTreeValues(treelike) {
|
|
13
13
|
const strings = [];
|
package/src/operations/scope.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { Tree } from "
|
|
1
|
+
import { Tree } from "../internal.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* A tree's "scope" is the collection of everything in that tree and all of its
|
|
5
5
|
* ancestors.
|
|
6
6
|
*
|
|
7
7
|
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
8
|
-
* @typedef {import("
|
|
8
|
+
* @typedef {import("../../index.ts").Treelike} Treelike
|
|
9
9
|
*
|
|
10
10
|
* @param {Treelike} treelike
|
|
11
11
|
* @returns {AsyncTree & {trees: AsyncTree[]}}
|
package/src/transforms/sortFn.js
CHANGED
|
@@ -50,16 +50,14 @@ export default function sortFn(options) {
|
|
|
50
50
|
const defaultCompare = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
|
|
51
51
|
const originalCompare = compare ?? defaultCompare;
|
|
52
52
|
// Sort by the sort key.
|
|
53
|
-
|
|
54
|
-
originalCompare(a.sort, b.sort)
|
|
55
|
-
);
|
|
53
|
+
tuples.sort((a, b) => originalCompare(a.sort, b.sort));
|
|
56
54
|
// Map back to the original keys.
|
|
57
|
-
const sorted =
|
|
55
|
+
const sorted = tuples.map((pair) => pair.key);
|
|
58
56
|
return sorted;
|
|
59
57
|
} else {
|
|
60
58
|
// Use original keys as sort keys.
|
|
61
59
|
// If compare is undefined, this uses default sort order.
|
|
62
|
-
return keys.
|
|
60
|
+
return keys.slice().sort(compare);
|
|
63
61
|
}
|
|
64
62
|
};
|
|
65
63
|
return transformed;
|
package/src/utilities.d.ts
CHANGED
|
@@ -5,10 +5,10 @@ export function box(value: any): any;
|
|
|
5
5
|
export function castArrayLike(object: any): any;
|
|
6
6
|
export function getRealmObjectPrototype(object: any): any;
|
|
7
7
|
export const hiddenFileNames: string[];
|
|
8
|
-
export function isPacked(
|
|
9
|
-
export function isPlainObject(
|
|
10
|
-
export function
|
|
11
|
-
export function
|
|
8
|
+
export function isPacked(obj: any): obj is Packed;
|
|
9
|
+
export function isPlainObject(obj: any): obj is PlainObject;
|
|
10
|
+
export function isStringLike(obj: any): obj is StringLike;
|
|
11
|
+
export function isUnpackable(obj): obj is { unpack: () => any };
|
|
12
12
|
export function keysFromPath(path: string): string[];
|
|
13
13
|
export const naturalOrder: (a: string, b: string) => number;
|
|
14
14
|
export function pipeline(start: any, ...functions: Function[]): Promise<any>;
|
package/src/utilities.js
CHANGED
|
@@ -73,16 +73,16 @@ export const hiddenFileNames = [".DS_Store"];
|
|
|
73
73
|
* Return true if the object is in a packed form (or can be readily packed into
|
|
74
74
|
* a form) that can be given to fs.writeFile or response.write().
|
|
75
75
|
*
|
|
76
|
-
* @param {any}
|
|
77
|
-
* @returns {
|
|
76
|
+
* @param {any} obj
|
|
77
|
+
* @returns {obj is import("../index.ts").Packed}
|
|
78
78
|
*/
|
|
79
|
-
export function isPacked(
|
|
79
|
+
export function isPacked(obj) {
|
|
80
80
|
return (
|
|
81
|
-
typeof
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
81
|
+
typeof obj === "string" ||
|
|
82
|
+
obj instanceof ArrayBuffer ||
|
|
83
|
+
obj instanceof ReadableStream ||
|
|
84
|
+
obj instanceof String ||
|
|
85
|
+
obj instanceof TypedArray
|
|
86
86
|
);
|
|
87
87
|
}
|
|
88
88
|
|
|
@@ -93,23 +93,23 @@ export function isPacked(object) {
|
|
|
93
93
|
* This function also considers object-like things with no prototype (like a
|
|
94
94
|
* `Module`) as plain objects.
|
|
95
95
|
*
|
|
96
|
-
* @param {any}
|
|
97
|
-
* @returns {
|
|
96
|
+
* @param {any} obj
|
|
97
|
+
* @returns {obj is import("../index.ts").PlainObject}
|
|
98
98
|
*/
|
|
99
|
-
export function isPlainObject(
|
|
99
|
+
export function isPlainObject(obj) {
|
|
100
100
|
// From https://stackoverflow.com/q/51722354/76472
|
|
101
|
-
if (typeof
|
|
101
|
+
if (typeof obj !== "object" || obj === null) {
|
|
102
102
|
return false;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
// We treat object-like things with no prototype (like a Module) as plain
|
|
106
106
|
// objects.
|
|
107
|
-
if (Object.getPrototypeOf(
|
|
107
|
+
if (Object.getPrototypeOf(obj) === null) {
|
|
108
108
|
return true;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
// Do we inherit directly from Object in this realm?
|
|
112
|
-
return Object.getPrototypeOf(
|
|
112
|
+
return Object.getPrototypeOf(obj) === getRealmObjectPrototype(obj);
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
/**
|
|
@@ -130,15 +130,15 @@ export function isPrimitive(value) {
|
|
|
130
130
|
* Return true if the object is a string or object with a non-trival `toString`
|
|
131
131
|
* method.
|
|
132
132
|
*
|
|
133
|
-
* @param {any}
|
|
133
|
+
* @param {any} obj
|
|
134
134
|
* @returns {obj is import("../index.ts").StringLike}
|
|
135
135
|
*/
|
|
136
|
-
export function isStringLike(
|
|
137
|
-
if (typeof
|
|
136
|
+
export function isStringLike(obj) {
|
|
137
|
+
if (typeof obj === "string") {
|
|
138
138
|
return true;
|
|
139
|
-
} else if (
|
|
139
|
+
} else if (obj?.toString === undefined) {
|
|
140
140
|
return false;
|
|
141
|
-
} else if (
|
|
141
|
+
} else if (obj.toString === getRealmObjectPrototype(obj)?.toString) {
|
|
142
142
|
// The stupid Object.prototype.toString implementation always returns
|
|
143
143
|
// "[object Object]", so if that's the only toString method the object has,
|
|
144
144
|
// we return false.
|
|
@@ -148,10 +148,9 @@ export function isStringLike(object) {
|
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
export function isUnpackable(
|
|
151
|
+
export function isUnpackable(obj) {
|
|
152
152
|
return (
|
|
153
|
-
isPacked(
|
|
154
|
-
typeof (/** @type {any} */ (object).unpack) === "function"
|
|
153
|
+
isPacked(obj) && typeof (/** @type {any} */ (obj).unpack) === "function"
|
|
155
154
|
);
|
|
156
155
|
}
|
|
157
156
|
|
package/test/Tree.test.js
CHANGED
|
@@ -118,6 +118,13 @@ describe("Tree", () => {
|
|
|
118
118
|
});
|
|
119
119
|
});
|
|
120
120
|
|
|
121
|
+
test("from() autoboxes primitive values", async () => {
|
|
122
|
+
const tree = Tree.from("Hello, world.");
|
|
123
|
+
const slice = await tree.get("slice");
|
|
124
|
+
const result = await slice(0, 5);
|
|
125
|
+
assert.equal(result, "Hello");
|
|
126
|
+
});
|
|
127
|
+
|
|
121
128
|
test("has returns true if the key exists", async () => {
|
|
122
129
|
const fixture = createFixture();
|
|
123
130
|
assert.equal(await Tree.has(fixture, "Alice.md"), true);
|
|
@@ -208,6 +215,18 @@ describe("Tree", () => {
|
|
|
208
215
|
assert.deepEqual(reduced, "1234");
|
|
209
216
|
});
|
|
210
217
|
|
|
218
|
+
test("paths returns an array of paths to the values in the tree", async () => {
|
|
219
|
+
const tree = new DeepObjectTree({
|
|
220
|
+
a: 1,
|
|
221
|
+
b: 2,
|
|
222
|
+
c: {
|
|
223
|
+
d: 3,
|
|
224
|
+
e: 4,
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
assert.deepEqual(await Tree.paths(tree), ["a", "b", "c/d", "c/e"]);
|
|
228
|
+
});
|
|
229
|
+
|
|
211
230
|
test("plain() produces a plain object version of a tree", async () => {
|
|
212
231
|
const original = {
|
|
213
232
|
a: 1,
|
package/test/utilities.test.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import { symbols } from "../main.js";
|
|
4
3
|
import { ObjectTree } from "../src/internal.js";
|
|
4
|
+
import * as symbols from "../src/symbols.js";
|
|
5
5
|
import * as utilities from "../src/utilities.js";
|
|
6
6
|
|
|
7
7
|
describe("utilities", () => {
|