@weborigami/async-tree 0.5.1 → 0.5.2

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
@@ -20,6 +20,7 @@ export type HasString = {
20
20
  * an HTTP response via response.write, or readily converted to such a form.
21
21
  */
22
22
  export type Packed = (ArrayBuffer | Buffer | ReadableStream | string | String | TypedArray) & {
23
+ parent?: AsyncTree|null;
23
24
  unpack?(): Promise<any>;
24
25
  };
25
26
 
@@ -70,4 +71,9 @@ export type Unpackable<T> = {
70
71
  unpack(): Promise<T>
71
72
  };
72
73
 
74
+ /**
75
+ * A function that converts a value from a persistent form into a live value.
76
+ */
77
+ export type UnpackFunction = (input: Packed, options?: any) => any;
78
+
73
79
  export type ValueKeyFn = (value: any, key: any, innerTree: AsyncTree) => any;
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@weborigami/async-tree",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
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
  "dependencies": {
10
- "@weborigami/types": "0.5.1"
10
+ "@weborigami/types": "0.5.2"
11
11
  },
12
12
  "devDependencies": {
13
13
  "@types/node": "24.3.0",
package/src/Tree.d.ts CHANGED
@@ -13,7 +13,7 @@ export function isTraversable(obj: any): boolean;
13
13
  export function isTreelike(obj: any): obj is Treelike;
14
14
  export function map(tree: Treelike, options: TreeMapOptions|ValueKeyFn): AsyncTree;
15
15
  export function mapReduce(tree: Treelike, mapFn: ValueKeyFn | null, reduceFn: ReduceFn): Promise<any>;
16
- export function paths(tree: Treelike, base?: string): string[];
16
+ export function paths(tree: Treelike, options?: { assumeSlashes?: boolean, base?: string }): string[];
17
17
  export function plain(tree: Treelike): Promise<PlainObject>;
18
18
  export function root(tree: Treelike): AsyncTree;
19
19
  export function remove(AsyncTree: AsyncMutableTree, key: any): Promise<boolean>;
package/src/Tree.js CHANGED
@@ -291,18 +291,41 @@ export async function mapReduce(treelike, valueFn, reduceFn) {
291
291
  /**
292
292
  * Returns slash-separated paths for all values in the tree.
293
293
  *
294
+ * The `base` argument is prepended to all paths.
295
+ *
296
+ * If `assumeSlashes` is true, then keys are assumed to have trailing slashes to
297
+ * indicate subtrees. The default value of this option is false.
298
+ *
294
299
  * @param {Treelike} treelike
295
- * @param {string?} base
300
+ * @param {{ assumeSlashes?: boolean, base?: string }} options
296
301
  */
297
- export async function paths(treelike, base = "") {
302
+ export async function paths(treelike, options = {}) {
298
303
  const tree = from(treelike);
304
+ const base = options.base ?? "";
305
+ const assumeSlashes = options.assumeSlashes ?? false;
299
306
  const result = [];
300
307
  for (const key of await tree.keys()) {
301
308
  const separator = trailingSlash.has(base) ? "" : "/";
302
309
  const valuePath = base ? `${base}${separator}${key}` : key;
303
- const value = await tree.get(key);
304
- if (await isAsyncTree(value)) {
305
- const subPaths = await paths(value, valuePath);
310
+ let isSubtree;
311
+ let value;
312
+ if (assumeSlashes) {
313
+ // Subtree needs to have a trailing slash
314
+ isSubtree = trailingSlash.has(key);
315
+ if (isSubtree) {
316
+ // We'll need the value to recurse
317
+ value = await tree.get(key);
318
+ }
319
+ } else {
320
+ // Get value and check
321
+ value = await tree.get(key);
322
+ }
323
+ if (value) {
324
+ // If we got the value we can check if it's a subtree
325
+ isSubtree = isAsyncTree(value);
326
+ }
327
+ if (isSubtree) {
328
+ const subPaths = await paths(value, { assumeSlashes, base: valuePath });
306
329
  result.push(...subPaths);
307
330
  } else {
308
331
  result.push(valuePath);
@@ -4,6 +4,7 @@ import { Packed, PlainObject, StringLike } from "../index.ts";
4
4
  export function assertIsTreelike(object: any, operation: string, position?: number): void;
5
5
  export function box(value: any): any;
6
6
  export function castArrayLike(keys: any[], values: any[]): any;
7
+ export function getParent(object: any, options?: any): AsyncTree|null;
7
8
  export function getRealmObjectPrototype(object: any): any;
8
9
  export const hiddenFileNames: string[];
9
10
  export function isPacked(obj: any): obj is Packed;
package/src/utilities.js CHANGED
@@ -87,6 +87,36 @@ export function castArrayLike(keys, values) {
87
87
  : Object.fromEntries(keys.map((key, i) => [key, values[i]]));
88
88
  }
89
89
 
90
+ /**
91
+ * Return a suitable parent for the packed file.
92
+ *
93
+ * This is intended to be called by unpack functions.
94
+ *
95
+ * @param {any} packed
96
+ * @param {any} [options]
97
+ * @returns {AsyncTree|null}
98
+ */
99
+ export function getParent(packed, options = {}) {
100
+ // Prefer parent set on options
101
+ if (options?.parent) {
102
+ return options.parent;
103
+ }
104
+
105
+ // If the packed object has a `parent` property, use that. Exception: Node
106
+ // Buffer objects have a `parent` property that we ignore.
107
+ if (packed.parent && !(packed instanceof Buffer)) {
108
+ return packed.parent;
109
+ }
110
+
111
+ // If the packed object has a parent symbol, use that.
112
+ if (packed[symbols.parent]) {
113
+ return packed[symbols.parent];
114
+ }
115
+
116
+ // Otherwise, return null.
117
+ return null;
118
+ }
119
+
90
120
  /**
91
121
  * Return the Object prototype at the root of the object's prototype chain.
92
122
  *
package/test/Tree.test.js CHANGED
@@ -232,6 +232,27 @@ describe("Tree", () => {
232
232
  assert.deepEqual(await Tree.paths(tree), ["a", "b", "c/d", "c/e"]);
233
233
  });
234
234
 
235
+ test("paths can focus just on keys with trailing slashes", async () => {
236
+ const tree = new ObjectTree({
237
+ a: 1,
238
+ b: 2,
239
+ // This is a shallow ObjectTree, so `c` won't have a trailing slash
240
+ c: {
241
+ d: 3,
242
+ },
243
+ // Explicitly include a trailing slash to signal a subtree
244
+ "d/": new ObjectTree({
245
+ e: 4,
246
+ }),
247
+ });
248
+ assert.deepEqual(await Tree.paths(tree, { assumeSlashes: true }), [
249
+ "a",
250
+ "b",
251
+ "c",
252
+ "d/e",
253
+ ]);
254
+ });
255
+
235
256
  test("plain() produces a plain object version of a tree", async () => {
236
257
  const tree = new ObjectTree({
237
258
  a: 1,