@weborigami/async-tree 0.5.5 → 0.5.7

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/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@weborigami/async-tree",
3
- "version": "0.5.5",
3
+ "version": "0.5.7",
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.5"
10
+ "@weborigami/types": "0.5.7"
11
11
  },
12
12
  "devDependencies": {
13
13
  "@types/node": "24.3.0",
package/shared.js CHANGED
@@ -12,6 +12,7 @@ export { default as ObjectTree } from "./src/drivers/ObjectTree.js";
12
12
  export { default as SetTree } from "./src/drivers/SetTree.js";
13
13
  export { default as SiteTree } from "./src/drivers/SiteTree.js";
14
14
  export * as jsonKeys from "./src/jsonKeys.js";
15
+ export { default as scope } from "./src/operations/scope.js";
15
16
  export * as symbols from "./src/symbols.js";
16
17
  export * as trailingSlash from "./src/trailingSlash.js";
17
18
  export { default as TraverseError } from "./src/TraverseError.js";
package/src/Tree.js CHANGED
@@ -8,7 +8,6 @@ export { default as addNextPrevious } from "./operations/addNextPrevious.js";
8
8
  export { default as assign } from "./operations/assign.js";
9
9
  export { default as cache } from "./operations/cache.js";
10
10
  export { default as clear } from "./operations/clear.js";
11
- export { default as concat } from "./operations/concat.js";
12
11
  export { default as deepMap } from "./operations/deepMap.js";
13
12
  export { default as deepMerge } from "./operations/deepMerge.js";
14
13
  export { default as deepReverse } from "./operations/deepReverse.js";
@@ -16,16 +15,15 @@ export { default as deepTake } from "./operations/deepTake.js";
16
15
  export { default as deepText } from "./operations/deepText.js";
17
16
  export { default as deepValues } from "./operations/deepValues.js";
18
17
  export { default as deepValuesIterator } from "./operations/deepValuesIterator.js";
19
- export { default as defineds } from "./operations/defineds.js";
20
18
  export { default as delete } from "./operations/delete.js";
21
19
  export { default as entries } from "./operations/entries.js";
22
20
  export { default as filter } from "./operations/filter.js";
23
21
  export { default as first } from "./operations/first.js";
24
22
  export { default as forEach } from "./operations/forEach.js";
25
23
  export { default as from } from "./operations/from.js";
26
- export { default as fromFn } from "./operations/fromFn.js";
27
24
  export { default as globKeys } from "./operations/globKeys.js";
28
25
  export { default as group } from "./operations/group.js";
26
+ export { default as groupBy } from "./operations/groupBy.js";
29
27
  export { default as has } from "./operations/has.js";
30
28
  export { default as indent } from "./operations/indent.js";
31
29
  export { default as inners } from "./operations/inners.js";
@@ -51,7 +49,6 @@ export { default as regExpKeys } from "./operations/regExpKeys.js";
51
49
  export { default as reverse } from "./operations/reverse.js";
52
50
  export { default as root } from "./operations/root.js";
53
51
  export { default as scope } from "./operations/scope.js";
54
- export { default as setDeep } from "./operations/setDeep.js";
55
52
  export { default as shuffle } from "./operations/shuffle.js";
56
53
  export { default as sort } from "./operations/sort.js";
57
54
  export { default as take } from "./operations/take.js";
@@ -27,11 +27,11 @@ export default class FunctionTree {
27
27
  this.fn.length <= 1
28
28
  ? // Function takes no arguments, one argument, or a variable number of
29
29
  // arguments: invoke it.
30
- await this.fn.call(this.parent, key)
30
+ await this.fn(key)
31
31
  : // Bind the key to the first parameter. Subsequent get calls will
32
32
  // eventually bind all parameters until only one remains. At that point,
33
33
  // the above condition will apply and the function will be invoked.
34
- Reflect.construct(this.constructor, [this.fn.bind(this.parent, key)]);
34
+ Reflect.construct(this.constructor, [this.fn.bind(null, key)]);
35
35
  setParent(value, this);
36
36
  return value;
37
37
  }
@@ -57,12 +57,9 @@ export default class ObjectTree {
57
57
 
58
58
  setParent(value, this);
59
59
 
60
- // Is value an instance method? The first clause sees if the object is a
61
- // constructor, in which case the method is likely a static method.
60
+ // Is value an instance method?
62
61
  const isInstanceMethod =
63
- !(this.object instanceof Function) &&
64
- value instanceof Function &&
65
- !Object.hasOwn(this.object, key);
62
+ value instanceof Function && !Object.hasOwn(this.object, key);
66
63
  if (isInstanceMethod) {
67
64
  // Bind it to the object
68
65
  value = value.bind(this.object);
@@ -1,51 +1,6 @@
1
- import ObjectTree from "../drivers/ObjectTree.js";
2
- import getTreeArgument from "../utilities/getTreeArgument.js";
3
- import from from "./from.js";
4
- import isTreelike from "./isTreelike.js";
5
- import values from "./values.js";
1
+ import groupBy from "./groupBy.js";
6
2
 
7
- /**
8
- * Given a function that returns a grouping key for a value, returns a transform
9
- * that applies that grouping function to a tree.
10
- *
11
- * @param {import("../../index.ts").Treelike} treelike
12
- * @param {import("../../index.ts").ValueKeyFn} groupKeyFn
13
- */
14
3
  export default async function group(treelike, groupKeyFn) {
15
- const tree = await getTreeArgument(treelike, "group");
16
-
17
- const keys = Array.from(await tree.keys());
18
-
19
- // Are all the keys integers?
20
- const isArray = keys.every((key) => !Number.isNaN(parseInt(key)));
21
-
22
- const result = {};
23
- for (const key of await tree.keys()) {
24
- const value = await tree.get(key);
25
-
26
- // Get the groups for this value.
27
- let groups = await groupKeyFn(value, key, tree);
28
- if (!groups) {
29
- continue;
30
- }
31
-
32
- if (!isTreelike(groups)) {
33
- // A single value was returned
34
- groups = [groups];
35
- }
36
- groups = from(groups);
37
-
38
- // Add the value to each group.
39
- for (const group of await values(groups)) {
40
- if (isArray) {
41
- result[group] ??= [];
42
- result[group].push(value);
43
- } else {
44
- result[group] ??= {};
45
- result[group][key] = value;
46
- }
47
- }
48
- }
49
-
50
- return new ObjectTree(result);
4
+ console.warn("Tree.group() is deprecated. Use Tree.groupBy() instead.");
5
+ return groupBy(treelike, groupKeyFn);
51
6
  }
@@ -0,0 +1,51 @@
1
+ import ObjectTree from "../drivers/ObjectTree.js";
2
+ import getTreeArgument from "../utilities/getTreeArgument.js";
3
+ import from from "./from.js";
4
+ import isTreelike from "./isTreelike.js";
5
+ import values from "./values.js";
6
+
7
+ /**
8
+ * Given a function that returns a grouping key for a value, returns a transform
9
+ * that applies that grouping function to a tree.
10
+ *
11
+ * @param {import("../../index.ts").Treelike} treelike
12
+ * @param {import("../../index.ts").ValueKeyFn} groupKeyFn
13
+ */
14
+ export default async function groupBy(treelike, groupKeyFn) {
15
+ const tree = await getTreeArgument(treelike, "groupBy");
16
+
17
+ const keys = Array.from(await tree.keys());
18
+
19
+ // Are all the keys integers?
20
+ const isArray = keys.every((key) => !Number.isNaN(parseInt(key)));
21
+
22
+ const result = {};
23
+ for (const key of await tree.keys()) {
24
+ const value = await tree.get(key);
25
+
26
+ // Get the groups for this value.
27
+ let groups = await groupKeyFn(value, key, tree);
28
+ if (!groups) {
29
+ continue;
30
+ }
31
+
32
+ if (!isTreelike(groups)) {
33
+ // A single value was returned
34
+ groups = [groups];
35
+ }
36
+ groups = from(groups);
37
+
38
+ // Add the value to each group.
39
+ for (const group of await values(groups)) {
40
+ if (isArray) {
41
+ result[group] ??= [];
42
+ result[group].push(value);
43
+ } else {
44
+ result[group] ??= {};
45
+ result[group][key] = value;
46
+ }
47
+ }
48
+ }
49
+
50
+ return new ObjectTree(result);
51
+ }
@@ -186,6 +186,11 @@ function validateOptions(options) {
186
186
  throw error;
187
187
  }
188
188
 
189
+ if (extension && !options._noExtensionWarning) {
190
+ console.warn(
191
+ `map: The 'extension' option for Tree.map() is deprecated and will be removed in a future release. Use Tree.mapExtension() instead.`
192
+ );
193
+ }
189
194
  if (extension && (keyFn || inverseKeyFn)) {
190
195
  throw new TypeError(
191
196
  `map: You can't specify extensions and also a key or inverseKey function`
@@ -1,27 +1,82 @@
1
- import getTreeArgument from "../utilities/getTreeArgument.js";
2
1
  import isPlainObject from "../utilities/isPlainObject.js";
2
+ import isUnpackable from "../utilities/isUnpackable.js";
3
3
  import map from "./map.js";
4
4
 
5
5
  /**
6
- * Shorthand for calling `map` with the `deep: true` option.
7
- *
8
6
  * @typedef {import("../../index.ts").TreeMapExtensionOptions} TreeMapExtensionOptions
9
7
  * @typedef {import("../../index.ts").Treelike} Treelike
10
8
  * @typedef {import("../../index.ts").ValueKeyFn} ValueKeyFn
11
9
  * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
12
- *
10
+ */
11
+
12
+ /**
13
+ * @overload
14
+ * @param {Treelike} treelike
15
+ * @param {string} extension
16
+ */
17
+
18
+ /**
19
+ * @overload
20
+ * @param {Treelike} treelike
21
+ * @param {TreeMapExtensionOptions} options
22
+ */
23
+
24
+ /**
25
+ * @overload
26
+ * @param {Treelike} treelike
27
+ * @param {string} extension
28
+ * @param {ValueKeyFn} fn
29
+ */
30
+
31
+ /**
32
+ * @overload
13
33
  * @param {Treelike} treelike
14
34
  * @param {string} extension
15
- * @param {ValueKeyFn|TreeMapExtensionOptions} options
35
+ * @param {TreeMapExtensionOptions} options
36
+ */
37
+
38
+ /**
39
+ * Shorthand for calling `map` with the `deep: true` option.
40
+ *
41
+ * @param {Treelike} treelike
42
+ * @param {string|TreeMapExtensionOptions} arg2
43
+ * @param {ValueKeyFn|TreeMapExtensionOptions} [arg3]
16
44
  * @returns {Promise<AsyncTree>}
17
45
  */
18
- export default async function mapExtension(treelike, extension, options) {
19
- const tree = await getTreeArgument(treelike, "mapExtension");
20
- const withExtension = isPlainObject(options)
21
- ? // Dictionary
22
- { ...options, extension }
23
- : // Function
24
- { extension, value: options };
46
+ export default async function mapExtension(treelike, arg2, arg3) {
47
+ /** @type {TreeMapExtensionOptions} */
48
+ // @ts-ignore
49
+ let options = { _noExtensionWarning: true };
50
+ if (arg3 === undefined) {
51
+ if (typeof arg2 === "string") {
52
+ options.extension = arg2;
53
+ } else if (isPlainObject(arg2)) {
54
+ Object.assign(options, arg2);
55
+ } else {
56
+ throw new TypeError(
57
+ "mapExtension: Expected a string or options object for the second argument."
58
+ );
59
+ }
60
+ } else {
61
+ if (typeof arg2 !== "string") {
62
+ throw new TypeError(
63
+ "mapExtension: Expected a string for the second argument."
64
+ );
65
+ }
66
+ options.extension = arg2;
67
+ if (isUnpackable(arg3)) {
68
+ arg3 = await arg3.unpack();
69
+ }
70
+ if (typeof arg3 === "function") {
71
+ options.value = arg3;
72
+ } else if (isPlainObject(arg3)) {
73
+ Object.assign(options, arg3);
74
+ } else {
75
+ throw new TypeError(
76
+ "mapExtension: Expected a function or options object for the third argument."
77
+ );
78
+ }
79
+ }
25
80
 
26
- return map(tree, withExtension);
81
+ return map(treelike, options);
27
82
  }
@@ -15,7 +15,7 @@ import isAsyncTree from "./isAsyncTree.js";
15
15
  *
16
16
  * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
17
17
  * @typedef {import("../../index.ts").Treelike} Treelike
18
- * @typedef {import("@weborigami/async-tree").Invocable} Invocable
18
+ * @typedef {import("../../index.ts").Invocable} Invocable
19
19
  *
20
20
  * @param {string|RegExp} pattern
21
21
  * @param {Invocable} resultFn
@@ -6,7 +6,6 @@ import traverseOrThrow from "./traverseOrThrow.js";
6
6
  *
7
7
  * @typedef {import("../../index.ts").Treelike} Treelike
8
8
  *
9
- * @this {any}
10
9
  * @param {Treelike} treelike
11
10
  * @param {...any} keys
12
11
  */
@@ -14,7 +13,7 @@ export default async function traverse(treelike, ...keys) {
14
13
  try {
15
14
  // Await the result here so that, if the path doesn't exist, the catch
16
15
  // block below will catch the exception.
17
- return await traverseOrThrow.call(this, treelike, ...keys);
16
+ return await traverseOrThrow(treelike, ...keys);
18
17
  } catch (/** @type {any} */ error) {
19
18
  if (error instanceof TraverseError) {
20
19
  return undefined;
@@ -9,7 +9,6 @@ import from from "./from.js";
9
9
  * @typedef {import("../../index.ts").Treelike} Treelike
10
10
  * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
11
11
  *
12
- * @this {any}
13
12
  * @param {Treelike} treelike
14
13
  * @param {...any} keys
15
14
  */
@@ -19,10 +18,6 @@ export default async function traverseOrThrow(treelike, ...keys) {
19
18
  let value = treelike;
20
19
  let position = 0;
21
20
 
22
- // If traversal operation was called with a `this` context, use that as the
23
- // target for function calls.
24
- const target = this;
25
-
26
21
  // Process all the keys.
27
22
  const remainingKeys = keys.slice();
28
23
  let key;
@@ -47,7 +42,7 @@ export default async function traverseOrThrow(treelike, ...keys) {
47
42
  let fnKeyCount = Math.max(fn.length, 1);
48
43
  const args = remainingKeys.splice(0, fnKeyCount);
49
44
  key = null;
50
- value = await fn.call(target, ...args);
45
+ value = await fn(...args);
51
46
  } else {
52
47
  // Cast value to a tree.
53
48
  const tree = from(value);
@@ -17,7 +17,6 @@ export default function toFunction(obj) {
17
17
  } else if (isUnpackable(obj)) {
18
18
  // Extract the contents of the object and convert that to a function.
19
19
  let fnPromise;
20
- /** @this {any} */
21
20
  return async function (...args) {
22
21
  if (!fnPromise) {
23
22
  // unpack() may return a function or a promise for a function; normalize
@@ -28,7 +27,7 @@ export default function toFunction(obj) {
28
27
  fnPromise = unpackPromise.then((content) => toFunction(content));
29
28
  }
30
29
  const fn = await fnPromise;
31
- return fn.call(this, ...args);
30
+ return fn(...args);
32
31
  };
33
32
  } else if (isTreelike(obj)) {
34
33
  // Return a function that invokes the tree's getter.
@@ -12,7 +12,7 @@ describe("deepReverse", () => {
12
12
  d: 3,
13
13
  },
14
14
  };
15
- const reversed = await deepReverse.call(null, tree);
15
+ const reversed = await deepReverse(tree);
16
16
  assert.deepEqual(await Tree.plain(reversed), {
17
17
  b: {
18
18
  d: 3,
@@ -1,9 +1,9 @@
1
1
  import assert from "node:assert";
2
2
  import { describe, test } from "node:test";
3
3
  import { Tree } from "../../src/internal.js";
4
- import group from "../../src/operations/group.js";
4
+ import groupBy from "../../src/operations/groupBy.js";
5
5
 
6
- describe("group transform", () => {
6
+ describe("groupBy transform", () => {
7
7
  test("groups an array using a group key function", async () => {
8
8
  const fonts = [
9
9
  { name: "Aboreto", tags: ["Sans Serif"] },
@@ -12,7 +12,7 @@ describe("group transform", () => {
12
12
  { name: "Work Sans", tags: ["Grotesque", "Sans Serif"] },
13
13
  ];
14
14
  const tree = Tree.from(fonts);
15
- const grouped = await group(tree, (value, key, tree) => value.tags);
15
+ const grouped = await groupBy(tree, (value, key, tree) => value.tags);
16
16
  assert.deepEqual(await Tree.plain(grouped), {
17
17
  Geometric: [{ name: "Albert Sans", tags: ["Geometric", "Sans Serif"] }],
18
18
  Grotesque: [{ name: "Work Sans", tags: ["Grotesque", "Sans Serif"] }],
@@ -33,7 +33,7 @@ describe("group transform", () => {
33
33
  "Work Sans": { tags: ["Grotesque", "Sans Serif"] },
34
34
  };
35
35
  const tree = Tree.from(fonts);
36
- const grouped = await group(tree, (value, key, tree) => value.tags);
36
+ const grouped = await groupBy(tree, (value, key, tree) => value.tags);
37
37
  assert.deepEqual(await Tree.plain(grouped), {
38
38
  Geometric: {
39
39
  "Albert Sans": { tags: ["Geometric", "Sans Serif"] },
@@ -208,56 +208,6 @@ describe("map", () => {
208
208
  C: "letter c",
209
209
  });
210
210
  });
211
-
212
- test("can add an extension to a key", async () => {
213
- const treelike = {
214
- "file0.txt": 1,
215
- file1: 2,
216
- file2: 3,
217
- };
218
- const fixture = await map(treelike, {
219
- extension: "->.data",
220
- });
221
- assert.deepEqual(await Tree.plain(fixture), {
222
- "file0.txt.data": 1,
223
- "file1.data": 2,
224
- "file2.data": 3,
225
- });
226
- });
227
-
228
- test("can change a key's extension", async () => {
229
- const treelike = {
230
- "file1.lower": "will be mapped",
231
- file2: "won't be mapped",
232
- "file3.foo": "won't be mapped",
233
- };
234
- const fixture = await map(treelike, {
235
- extension: ".lower->.upper",
236
- value: (sourceValue) => sourceValue.toUpperCase(),
237
- });
238
- assert.deepEqual(await Tree.plain(fixture), {
239
- "file1.upper": "WILL BE MAPPED",
240
- });
241
- });
242
-
243
- test("can manipulate extensions deeply", async () => {
244
- const treelike = {
245
- "file1.txt": 1,
246
- more: {
247
- "file2.txt": 2,
248
- },
249
- };
250
- const fixture = await map(treelike, {
251
- deep: true,
252
- extension: ".txt->",
253
- });
254
- assert.deepEqual(await Tree.plain(fixture), {
255
- file1: 1,
256
- more: {
257
- file2: 2,
258
- },
259
- });
260
- });
261
211
  });
262
212
 
263
213
  function addUnderscore(value, key) {
@@ -0,0 +1,53 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import mapExtension from "../../src/operations/mapExtension.js";
4
+ import plain from "../../src/operations/plain.js";
5
+
6
+ describe("mapExtension", () => {
7
+ test("can add an extension to a key", async () => {
8
+ const treelike = {
9
+ "file0.txt": 1,
10
+ file1: 2,
11
+ file2: 3,
12
+ };
13
+ const fixture = await mapExtension(treelike, "->.data");
14
+ assert.deepEqual(await plain(fixture), {
15
+ "file0.txt.data": 1,
16
+ "file1.data": 2,
17
+ "file2.data": 3,
18
+ });
19
+ });
20
+
21
+ test("can change a key's extension", async () => {
22
+ const treelike = {
23
+ "file1.lower": "will be mapped",
24
+ file2: "won't be mapped",
25
+ "file3.foo": "won't be mapped",
26
+ };
27
+ const fixture = await mapExtension(treelike, {
28
+ extension: ".lower->.upper",
29
+ value: (sourceValue) => sourceValue.toUpperCase(),
30
+ });
31
+ assert.deepEqual(await plain(fixture), {
32
+ "file1.upper": "WILL BE MAPPED",
33
+ });
34
+ });
35
+
36
+ test("can manipulate extensions deeply", async () => {
37
+ const treelike = {
38
+ "file1.txt": 1,
39
+ more: {
40
+ "file2.txt": 2,
41
+ },
42
+ };
43
+ const fixture = await mapExtension(treelike, ".txt->", {
44
+ deep: true,
45
+ });
46
+ assert.deepEqual(await plain(fixture), {
47
+ file1: 1,
48
+ more: {
49
+ file2: 2,
50
+ },
51
+ });
52
+ });
53
+ });
@@ -12,7 +12,7 @@ describe("paginate", () => {
12
12
  d: 4,
13
13
  e: 5,
14
14
  };
15
- const paginated = await paginate.call(null, treelike, 2);
15
+ const paginated = await paginate(treelike, 2);
16
16
  const plain = await Tree.plain(paginated);
17
17
  assert.deepEqual(await plain, {
18
18
  1: {
@@ -10,7 +10,7 @@ describe("reverse", () => {
10
10
  b: "B",
11
11
  c: "C",
12
12
  };
13
- const reversed = await reverse.call(null, tree);
13
+ const reversed = await reverse(tree);
14
14
  // @ts-ignore
15
15
  assert.deepEqual(Array.from(await reversed.keys()), ["c", "b", "a"]);
16
16
  // @ts-ignore
@@ -1,17 +0,0 @@
1
- import deepText from "./deepText.js";
2
- import from from "./from.js";
3
-
4
- /**
5
- * Concatenate the text content of objects or trees.
6
- *
7
- * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
8
- *
9
- * @param {any[]} args
10
- */
11
- export default async function concat(...args) {
12
- console.warn(
13
- "Warning: the Tree.concat function is deprecated, use Tree.deepText instead."
14
- );
15
- const tree = from(args);
16
- return deepText(tree);
17
- }
@@ -1,32 +0,0 @@
1
- import getTreeArgument from "../utilities/getTreeArgument.js";
2
- import mapReduce from "./mapReduce.js";
3
-
4
- /**
5
- * Return only the defined (not `undefined`) values in the deep tree.
6
- *
7
- * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
8
- * @typedef {import("../../index.ts").Treelike} Treelike
9
- *
10
- * @param {Treelike} treelike
11
- */
12
- export default async function defineds(treelike) {
13
- console.warn(
14
- "Warning: Tree.defineds is deprecated. If you have a use for it, please let us know."
15
- );
16
- const tree = await getTreeArgument(treelike, "defineds", { deep: true });
17
-
18
- const result = await mapReduce(tree, null, async (values, keys) => {
19
- const object = {};
20
- let someValuesExist = false;
21
- for (let i = 0; i < keys.length; i++) {
22
- const value = values[i];
23
- if (value != null) {
24
- someValuesExist = true;
25
- object[keys[i]] = values[i];
26
- }
27
- }
28
- return someValuesExist ? object : null;
29
- });
30
-
31
- return result;
32
- }
@@ -1,26 +0,0 @@
1
- import FunctionTree from "../drivers/FunctionTree.js";
2
- import isUnpackable from "../utilities/isUnpackable.js";
3
- import toFunction from "../utilities/toFunction.js";
4
-
5
- /**
6
- * Create a tree from a function and a set of keys.
7
- *
8
- * @typedef {import("@weborigami/async-tree").Invocable} Invocable
9
- *
10
- * @param {Invocable} invocable
11
- */
12
- export default async function fromFn(invocable, keys = []) {
13
- console.warn("Tree.fromFn is deprecated, use Tree.withKeys instead.");
14
- if (invocable === undefined) {
15
- throw new Error(
16
- "Tree.fromFn: the first argument must be a function or a tree."
17
- );
18
- }
19
- const fn = toFunction(invocable);
20
- if (isUnpackable(keys)) {
21
- keys = await keys.unpack();
22
- }
23
- // @ts-ignore
24
- const tree = new FunctionTree(fn, keys);
25
- return tree;
26
- }
@@ -1,14 +0,0 @@
1
- import { default as del } from "./delete.js";
2
-
3
- /**
4
- * Removes the value for the given key from the specific node of the tree.
5
- *
6
- * @typedef {import("@weborigami/types").AsyncMutableTree} AsyncMutableTree
7
- *
8
- * @param {AsyncMutableTree} tree
9
- * @param {any} key
10
- */
11
- export default async function remove(tree, key) {
12
- console.warn("`Tree.remove` is deprecated. Use `Tree.delete` instead.");
13
- return del(tree, key);
14
- }
@@ -1,50 +0,0 @@
1
- import from from "./from.js";
2
- import isAsyncTree from "./isAsyncTree.js";
3
-
4
- /**
5
- * @typedef {import("@weborigami/async-tree").Treelike} Treelike
6
- *
7
- * @param {Treelike} target
8
- * @param {Treelike} source
9
- */
10
- export default async function setDeep(target, source) {
11
- console.warn("Tree.setDeep is deprecated, use Tree.assign instead.");
12
- const targetTree = from(target);
13
- const sourceTree = from(source);
14
- await applyUpdates(sourceTree, targetTree);
15
- }
16
-
17
- // Apply all updates from the source to the target.
18
- async function applyUpdates(source, target) {
19
- // Fire off requests to update all keys, then wait for all of them to finish.
20
- const promises = [];
21
- for (const key of await source.keys()) {
22
- const updateKeyPromise = applyUpdateForKey(source, target, key);
23
- promises.push(updateKeyPromise);
24
- }
25
- await Promise.all(promises);
26
-
27
- // HACK: Transforms like KeysTransform that maintain caches will need to
28
- // recalculate things now that updates have been applied. This should be an
29
- // automatic part of calling set() -- but triggering those changes inside
30
- // set() produces cases where set() and get() calls can be interleaved. The
31
- // atomicity of set() needs to be reconsidered. For now, we work around the
32
- // problem by triggering `onChange` after the updates have been applied.
33
- target.onChange?.();
34
- }
35
-
36
- // Copy the value for the given key from the source to the target.
37
- async function applyUpdateForKey(source, target, key) {
38
- const sourceValue = await source.get(key);
39
- if (isAsyncTree(sourceValue)) {
40
- const targetValue = await target.get(key);
41
- if (isAsyncTree(targetValue)) {
42
- // Both source and target are async dictionaries; recurse.
43
- await applyUpdates(sourceValue, targetValue);
44
- return;
45
- }
46
- }
47
-
48
- // Copy the value from the source to the target.
49
- await target.set(key, sourceValue);
50
- }
@@ -1,29 +0,0 @@
1
- import assert from "node:assert";
2
- import { describe, test } from "node:test";
3
- import { DeepObjectTree, Tree } from "../../src/internal.js";
4
- import deepMap from "../../src/operations/deepMap.js";
5
-
6
- describe("deepMap", () => {
7
- test("can map extensions deeply", async () => {
8
- const treelike = new DeepObjectTree({
9
- "file1.txt": "will be mapped",
10
- file2: "won't be mapped",
11
- "file3.foo": "won't be mapped",
12
- more: {
13
- "file4.txt": "will be mapped",
14
- "file5.bar": "won't be mapped",
15
- },
16
- });
17
- const fixture = await deepMap.call(null, treelike, {
18
- deep: true,
19
- extension: ".txt->.upper",
20
- value: (sourceValue, sourceKey, tree) => sourceValue.toUpperCase(),
21
- });
22
- assert.deepEqual(await Tree.plain(fixture), {
23
- "file1.upper": "WILL BE MAPPED",
24
- more: {
25
- "file4.upper": "WILL BE MAPPED",
26
- },
27
- });
28
- });
29
- });
@@ -1,25 +0,0 @@
1
- import assert from "node:assert";
2
- import { describe, test } from "node:test";
3
- import { Tree } from "../../src/internal.js";
4
- import defineds from "../../src/operations/defineds.js";
5
-
6
- describe("defineds", () => {
7
- test("returns only defined values in a tree", async () => {
8
- const obj = {
9
- a: 1,
10
- b: undefined,
11
- c: null,
12
- d: {
13
- e: 2,
14
- f: undefined,
15
- g: {
16
- h: 3,
17
- i: undefined,
18
- },
19
- },
20
- };
21
- const result = await defineds(obj);
22
- const plain = await Tree.plain(result);
23
- assert.deepEqual(plain, { a: 1, d: { e: 2, g: { h: 3 } } });
24
- });
25
- });
@@ -1,53 +0,0 @@
1
- import assert from "node:assert";
2
- import { describe, test } from "node:test";
3
- import { DeepObjectTree, ObjectTree, Tree } from "../../src/internal.js";
4
- import setDeep from "../../src/operations/setDeep.js";
5
-
6
- describe("tree/setDeep", () => {
7
- test("can apply updates with a single argument to set", async () => {
8
- const tree = new DeepObjectTree({
9
- a: 1,
10
- b: 2,
11
- more: {
12
- d: 3,
13
- },
14
- });
15
-
16
- // Apply changes.
17
- await setDeep.call(
18
- null,
19
- tree,
20
- new DeepObjectTree({
21
- a: 4, // Overwrite existing value
22
- b: undefined, // Delete
23
- c: 5, // Add
24
- more: {
25
- // Should leave existing `more` keys alone.
26
- e: 6, // Add
27
- },
28
- // Add new subtree
29
- extra: {
30
- f: 7,
31
- },
32
- })
33
- );
34
-
35
- assert.deepEqual(await Tree.plain(tree), {
36
- a: 4,
37
- c: 5,
38
- more: {
39
- d: 3,
40
- e: 6,
41
- },
42
- extra: {
43
- f: 7,
44
- },
45
- });
46
- });
47
-
48
- test("can apply updates to an array", async () => {
49
- const tree = new ObjectTree(["a", "b", "c"]);
50
- await setDeep.call(null, tree, ["d", "e"]);
51
- assert.deepEqual(await Tree.plain(tree), ["d", "e", "c"]);
52
- });
53
- });