@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 CHANGED
@@ -29,7 +29,7 @@ export type ReduceFn = (values: any[], keys: any[]) => Promise<any>;
29
29
 
30
30
  export type StringLike = string | HasString;
31
31
 
32
- type NativeTreelike =
32
+ export type NativeTreelike =
33
33
  any[] |
34
34
  AsyncTree |
35
35
  Function |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weborigami/async-tree",
3
- "version": "0.0.62",
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.62"
14
+ "@weborigami/types": "0.0.63"
15
15
  },
16
16
  "scripts": {
17
17
  "test": "node --test --test-reporter=spec",
package/src/ObjectTree.js CHANGED
@@ -48,7 +48,7 @@ export default class ObjectTree {
48
48
  }
49
49
 
50
50
  async isKeyForSubtree(key) {
51
- const value = this.object[key];
51
+ const value = await this.object[key];
52
52
  return Tree.isAsyncTree(value);
53
53
  }
54
54
 
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} object
165
+ * @param {any} obj
158
166
  * @returns {obj is AsyncTree}
159
167
  */
160
- export function isAsyncTree(object) {
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} object
175
+ * @param {any} obj
172
176
  * @returns {obj is AsyncMutableTree}
173
177
  */
174
- export function isAsyncMutableTree(object) {
175
- return isAsyncTree(object) && typeof object.set === "function";
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} object
228
+ * @param {any} obj
223
229
  * @returns {obj is Treelike}
224
230
  */
225
- export function isTreelike(object) {
231
+ export function isTreelike(obj) {
226
232
  return (
227
- isAsyncTree(object) ||
228
- object instanceof Function ||
229
- object instanceof Array ||
230
- object instanceof Set ||
231
- isPlainObject(object)
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
  *
@@ -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("@weborigami/async-tree").Treelike} treelike
10
+ * @param {import("../../index.ts").Treelike} treelike
11
11
  */
12
12
  export default async function concatTreeValues(treelike) {
13
13
  const strings = [];
@@ -1,11 +1,11 @@
1
- import { Tree } from "@weborigami/async-tree";
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("@weborigami/async-tree").Treelike} Treelike
8
+ * @typedef {import("../../index.ts").Treelike} Treelike
9
9
  *
10
10
  * @param {Treelike} treelike
11
11
  * @returns {AsyncTree & {trees: AsyncTree[]}}
@@ -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
- const sortedTuples = tuples.toSorted((a, b) =>
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 = sortedTuples.map((pair) => pair.key);
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.toSorted(compare);
60
+ return keys.slice().sort(compare);
63
61
  }
64
62
  };
65
63
  return transformed;
@@ -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(object: any): object is Packed;
9
- export function isPlainObject(object: any): object is PlainObject;
10
- export function isUnpackable(object): object is { unpack: () => any };
11
- export function isStringLike(object: any): object is StringLike;
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} object
77
- * @returns {object is import("../index.ts").Packed}
76
+ * @param {any} obj
77
+ * @returns {obj is import("../index.ts").Packed}
78
78
  */
79
- export function isPacked(object) {
79
+ export function isPacked(obj) {
80
80
  return (
81
- typeof object === "string" ||
82
- object instanceof ArrayBuffer ||
83
- object instanceof ReadableStream ||
84
- object instanceof String ||
85
- object instanceof TypedArray
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} object
97
- * @returns {object is import("../index.ts").PlainObject}
96
+ * @param {any} obj
97
+ * @returns {obj is import("../index.ts").PlainObject}
98
98
  */
99
- export function isPlainObject(object) {
99
+ export function isPlainObject(obj) {
100
100
  // From https://stackoverflow.com/q/51722354/76472
101
- if (typeof object !== "object" || object === null) {
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(object) === null) {
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(object) === getRealmObjectPrototype(object);
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} object
133
+ * @param {any} obj
134
134
  * @returns {obj is import("../index.ts").StringLike}
135
135
  */
136
- export function isStringLike(object) {
137
- if (typeof object === "string") {
136
+ export function isStringLike(obj) {
137
+ if (typeof obj === "string") {
138
138
  return true;
139
- } else if (object?.toString === undefined) {
139
+ } else if (obj?.toString === undefined) {
140
140
  return false;
141
- } else if (object.toString === getRealmObjectPrototype(object)?.toString) {
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(object) {
151
+ export function isUnpackable(obj) {
152
152
  return (
153
- isPacked(object) &&
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,
@@ -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", () => {