@weborigami/async-tree 0.0.65 → 0.0.66-beta.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.
Files changed (45) hide show
  1. package/main.js +2 -0
  2. package/package.json +4 -4
  3. package/src/BrowserFileTree.js +28 -6
  4. package/src/DeepMapTree.js +2 -2
  5. package/src/DeepObjectTree.js +6 -9
  6. package/src/FileTree.js +14 -10
  7. package/src/MapTree.js +27 -4
  8. package/src/ObjectTree.js +53 -6
  9. package/src/OpenSiteTree.js +41 -0
  10. package/src/SetTree.js +0 -6
  11. package/src/SiteTree.js +24 -85
  12. package/src/Tree.d.ts +1 -2
  13. package/src/Tree.js +40 -52
  14. package/src/jsonKeys.js +4 -37
  15. package/src/operations/cache.js +0 -4
  16. package/src/operations/deepMerge.js +7 -10
  17. package/src/operations/merge.js +7 -10
  18. package/src/trailingSlash.js +54 -0
  19. package/src/transforms/cachedKeyFunctions.js +72 -34
  20. package/src/transforms/keyFunctionsForExtensions.js +24 -10
  21. package/src/transforms/mapFn.js +11 -17
  22. package/src/transforms/regExpKeys.js +17 -12
  23. package/src/utilities.js +34 -6
  24. package/test/BrowserFileTree.test.js +28 -5
  25. package/test/DeepMapTree.test.js +17 -0
  26. package/test/DeepObjectTree.test.js +17 -7
  27. package/test/FileTree.test.js +14 -7
  28. package/test/MapTree.test.js +21 -0
  29. package/test/ObjectTree.test.js +16 -12
  30. package/test/OpenSiteTree.test.js +113 -0
  31. package/test/SiteTree.test.js +14 -49
  32. package/test/Tree.test.js +19 -39
  33. package/test/browser/assert.js +9 -0
  34. package/test/browser/index.html +4 -4
  35. package/test/calendarTree.test.js +1 -1
  36. package/test/fixtures/markdown/subfolder/README.md +1 -0
  37. package/test/jsonKeys.test.js +0 -9
  38. package/test/operations/cache.test.js +1 -1
  39. package/test/operations/merge.test.js +20 -1
  40. package/test/trailingSlash.test.js +36 -0
  41. package/test/transforms/cachedKeyFunctions.test.js +90 -0
  42. package/test/transforms/keyFunctionsForExtensions.test.js +7 -3
  43. package/test/transforms/mapFn.test.js +29 -20
  44. package/test/utilities.test.js +6 -2
  45. package/test/transforms/cachedKeyMaps.test.js +0 -41
package/main.js CHANGED
@@ -8,6 +8,7 @@ export { default as MapTree } from "./src/MapTree.js";
8
8
  export { default as DeepMapTree } from "./src/DeepMapTree.js";
9
9
  export { DeepObjectTree, ObjectTree, Tree } from "./src/internal.js";
10
10
  export * as jsonKeys from "./src/jsonKeys.js";
11
+ export { default as OpenSiteTree } from "./src/OpenSiteTree.js";
11
12
  export { default as cache } from "./src/operations/cache.js";
12
13
  export { default as concat } from "./src/operations/concat.js";
13
14
  export { default as deepMerge } from "./src/operations/deepMerge.js";
@@ -25,6 +26,7 @@ export { default as take } from "./src/operations/take.js";
25
26
  export { default as SetTree } from "./src/SetTree.js";
26
27
  export { default as SiteTree } from "./src/SiteTree.js";
27
28
  export * as symbols from "./src/symbols.js";
29
+ export * as trailingSlash from "./src/trailingSlash.js";
28
30
  export { default as cachedKeyFunctions } from "./src/transforms/cachedKeyFunctions.js";
29
31
  export { default as deepReverse } from "./src/transforms/deepReverse.js";
30
32
  export { default as invokeFunctions } from "./src/transforms/invokeFunctions.js";
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@weborigami/async-tree",
3
- "version": "0.0.65",
3
+ "version": "0.0.66-beta.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
  "devDependencies": {
10
- "@types/node": "22.5.4",
11
- "typescript": "5.5.4"
10
+ "@types/node": "22.7.4",
11
+ "typescript": "5.6.2"
12
12
  },
13
13
  "dependencies": {
14
- "@weborigami/types": "0.0.65"
14
+ "@weborigami/types": "0.0.66-beta.2"
15
15
  },
16
16
  "scripts": {
17
17
  "test": "node --test --test-reporter=spec",
@@ -1,3 +1,4 @@
1
+ import * as trailingSlash from "../src/trailingSlash.js";
1
2
  import { Tree } from "./internal.js";
2
3
  import {
3
4
  hiddenFileNames,
@@ -40,10 +41,17 @@ export default class BrowserFileTree {
40
41
  `${this.constructor.name}: Cannot get a null or undefined key.`
41
42
  );
42
43
  }
44
+ if (key === "") {
45
+ // Can't have a file with no name
46
+ return undefined;
47
+ }
48
+
49
+ // Remove trailing slash if present
50
+ key = trailingSlash.remove(key);
43
51
 
44
52
  const directory = await this.getDirectory();
45
53
 
46
- // Try the key as a subfolder name.
54
+ // Try the key as a subfolder name
47
55
  try {
48
56
  const subfolderHandle = await directory.getDirectoryHandle(key);
49
57
  const value = Reflect.construct(this.constructor, [subfolderHandle]);
@@ -60,7 +68,7 @@ export default class BrowserFileTree {
60
68
  }
61
69
  }
62
70
 
63
- // Try the key as a file name.
71
+ // Try the key as a file name
64
72
  try {
65
73
  const fileHandle = await directory.getFileHandle(key);
66
74
  const file = await fileHandle.getFile();
@@ -76,6 +84,8 @@ export default class BrowserFileTree {
76
84
  return undefined;
77
85
  }
78
86
 
87
+ // Return the directory handle, creating it if necessary. We can't create the
88
+ // default value in the constructor because we need to await it.
79
89
  async getDirectory() {
80
90
  this.directory ??= await navigator.storage.getDirectory();
81
91
  return this.directory;
@@ -85,9 +95,18 @@ export default class BrowserFileTree {
85
95
  const directory = await this.getDirectory();
86
96
  let keys = [];
87
97
  // @ts-ignore
88
- for await (const key of directory.keys()) {
98
+ for await (const entryKey of directory.keys()) {
99
+ // Check if the entry is a subfolder
100
+ const baseKey = trailingSlash.remove(entryKey);
101
+ const subfolderHandle = await directory
102
+ .getDirectoryHandle(baseKey)
103
+ .catch(() => null);
104
+ const isSubfolder = subfolderHandle !== null;
105
+
106
+ const key = trailingSlash.toggle(entryKey, isSubfolder);
89
107
  keys.push(key);
90
108
  }
109
+
91
110
  // Filter out unhelpful file names.
92
111
  keys = keys.filter((key) => !hiddenFileNames.includes(key));
93
112
  keys.sort(naturalOrder);
@@ -96,12 +115,13 @@ export default class BrowserFileTree {
96
115
  }
97
116
 
98
117
  async set(key, value) {
118
+ const baseKey = trailingSlash.remove(key);
99
119
  const directory = await this.getDirectory();
100
120
 
101
121
  if (value === undefined) {
102
122
  // Delete file.
103
123
  try {
104
- await directory.removeEntry(key);
124
+ await directory.removeEntry(baseKey);
105
125
  } catch (error) {
106
126
  // If the file didn't exist, ignore the error.
107
127
  if (
@@ -133,13 +153,15 @@ export default class BrowserFileTree {
133
153
 
134
154
  if (isWriteable) {
135
155
  // Write file.
136
- const fileHandle = await directory.getFileHandle(key, { create: true });
156
+ const fileHandle = await directory.getFileHandle(baseKey, {
157
+ create: true,
158
+ });
137
159
  const writable = await fileHandle.createWritable();
138
160
  await writable.write(value);
139
161
  await writable.close();
140
162
  } else if (Tree.isTreelike(value)) {
141
163
  // Treat value as a tree and write it out as a subdirectory.
142
- const subdirectory = await directory.getDirectoryHandle(key, {
164
+ const subdirectory = await directory.getDirectoryHandle(baseKey, {
143
165
  create: true,
144
166
  });
145
167
  const destTree = Reflect.construct(this.constructor, [subdirectory]);
@@ -16,8 +16,8 @@ export default class DeepMapTree extends MapTree {
16
16
  return value;
17
17
  }
18
18
 
19
- async isKeyForSubtree(key) {
20
- const value = this.map.get[key];
19
+ /** @returns {boolean} */
20
+ isSubtree(value) {
21
21
  return value instanceof Map || Tree.isAsyncTree(value);
22
22
  }
23
23
  }
@@ -4,19 +4,16 @@ import { isPlainObject } from "./utilities.js";
4
4
  export default class DeepObjectTree extends ObjectTree {
5
5
  async get(key) {
6
6
  let value = await super.get(key);
7
-
8
- const isPlain =
9
- value instanceof Array ||
10
- (isPlainObject(value) && !Tree.isAsyncTree(value));
11
- if (isPlain) {
7
+ if (value instanceof Array || isPlainObject(value)) {
12
8
  value = Reflect.construct(this.constructor, [value]);
13
9
  }
14
-
15
10
  return value;
16
11
  }
17
12
 
18
- async isKeyForSubtree(key) {
19
- const value = await this.object[key];
20
- return isPlainObject(value) || Tree.isAsyncTree(value);
13
+ /** @returns {boolean} */
14
+ isSubtree(value) {
15
+ return (
16
+ value instanceof Array || isPlainObject(value) || Tree.isAsyncTree(value)
17
+ );
21
18
  }
22
19
  }
package/src/FileTree.js CHANGED
@@ -2,6 +2,7 @@ import * as fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { fileURLToPath, pathToFileURL } from "node:url";
4
4
  import { Tree } from "./internal.js";
5
+ import * as trailingSlash from "./trailingSlash.js";
5
6
  import {
6
7
  getRealmObjectPrototype,
7
8
  hiddenFileNames,
@@ -50,12 +51,18 @@ export default class FileTree {
50
51
 
51
52
  async get(key) {
52
53
  if (key == null) {
53
- // Reject nullish key.
54
+ // Reject nullish key
54
55
  throw new ReferenceError(
55
56
  `${this.constructor.name}: Cannot get a null or undefined key.`
56
57
  );
57
58
  }
59
+ if (key === "") {
60
+ // Can't have a file with no name
61
+ return undefined;
62
+ }
58
63
 
64
+ // Remove trailing slash if present
65
+ key = trailingSlash.remove(key);
59
66
  const filePath = path.resolve(this.dirname, key);
60
67
 
61
68
  let stats;
@@ -73,7 +80,7 @@ export default class FileTree {
73
80
  // Return subdirectory as a tree
74
81
  value = Reflect.construct(this.constructor, [filePath]);
75
82
  } else {
76
- // Return file contents as a standard Uint8Array.
83
+ // Return file contents as a standard Uint8Array
77
84
  const buffer = await fs.readFile(filePath);
78
85
  value = Uint8Array.from(buffer);
79
86
  }
@@ -82,12 +89,6 @@ export default class FileTree {
82
89
  return value;
83
90
  }
84
91
 
85
- async isKeyForSubtree(key) {
86
- const filePath = path.join(this.dirname, key);
87
- const stats = await stat(filePath);
88
- return stats ? stats.isDirectory() : false;
89
- }
90
-
91
92
  /**
92
93
  * Enumerate the names of the files/subdirectories in this directory.
93
94
  */
@@ -102,7 +103,9 @@ export default class FileTree {
102
103
  entries = [];
103
104
  }
104
105
 
105
- let names = entries.map((entry) => entry.name);
106
+ let names = entries.map((entry) =>
107
+ trailingSlash.toggle(entry.name, entry.isDirectory())
108
+ );
106
109
 
107
110
  // Filter out unhelpful file names.
108
111
  names = names.filter((name) => !hiddenFileNames.includes(name));
@@ -121,7 +124,8 @@ export default class FileTree {
121
124
  async set(key, value) {
122
125
  // Where are we going to write this value?
123
126
  const stringKey = key != null ? String(key) : "";
124
- const destPath = path.resolve(this.dirname, stringKey);
127
+ const baseKey = trailingSlash.remove(stringKey);
128
+ const destPath = path.resolve(this.dirname, baseKey);
125
129
 
126
130
  if (value === undefined) {
127
131
  // Delete the file or directory.
package/src/MapTree.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Tree } from "./internal.js";
2
+ import * as trailingSlash from "./trailingSlash.js";
2
3
  import { setParent } from "./utilities.js";
3
4
 
4
5
  /**
@@ -23,18 +24,40 @@ export default class MapTree {
23
24
  }
24
25
 
25
26
  async get(key) {
26
- const value = this.map.get(key);
27
+ // Try key as is
28
+ let value = this.map.get(key);
29
+ if (value === undefined) {
30
+ // Try the other variation of the key
31
+ const alternateKey = trailingSlash.toggle(key);
32
+ value = this.map.get(alternateKey);
33
+ if (value === undefined) {
34
+ // Key doesn't exist
35
+ return undefined;
36
+ }
37
+ }
38
+
39
+ value = await value;
40
+
41
+ if (value === undefined) {
42
+ // Key exists but value is undefined
43
+ return undefined;
44
+ }
45
+
27
46
  setParent(value, this);
28
47
  return value;
29
48
  }
30
49
 
31
- async isKeyForSubtree(key) {
32
- const value = this.map.get(key);
50
+ /** @returns {boolean} */
51
+ isSubtree(value) {
33
52
  return Tree.isAsyncTree(value);
34
53
  }
35
54
 
36
55
  async keys() {
37
- return this.map.keys();
56
+ const keys = [];
57
+ for (const [key, value] of this.map.entries()) {
58
+ keys.push(trailingSlash.toggle(key, this.isSubtree(value)));
59
+ }
60
+ return keys;
38
61
  }
39
62
 
40
63
  async set(key, value) {
package/src/ObjectTree.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Tree } from "./internal.js";
2
2
  import * as symbols from "./symbols.js";
3
+ import * as trailingSlash from "./trailingSlash.js";
3
4
  import { getRealmObjectPrototype, setParent } from "./utilities.js";
4
5
 
5
6
  /**
@@ -32,7 +33,20 @@ export default class ObjectTree {
32
33
  );
33
34
  }
34
35
 
35
- let value = await this.object[key];
36
+ // Does the object have the key with or without a trailing slash?
37
+ const existingKey = findExistingKey(this.object, key);
38
+ if (existingKey === null) {
39
+ // Key doesn't exist
40
+ return undefined;
41
+ }
42
+
43
+ let value = await this.object[existingKey];
44
+
45
+ if (value === undefined) {
46
+ // Key exists but value is undefined
47
+ return undefined;
48
+ }
49
+
36
50
  setParent(value, this);
37
51
 
38
52
  if (typeof value === "function" && !Object.hasOwn(this.object, key)) {
@@ -43,8 +57,8 @@ export default class ObjectTree {
43
57
  return value;
44
58
  }
45
59
 
46
- async isKeyForSubtree(key) {
47
- const value = await this.object[key];
60
+ /** @returns {boolean} */
61
+ isSubtree(value) {
48
62
  return Tree.isAsyncTree(value);
49
63
  }
50
64
 
@@ -67,7 +81,17 @@ export default class ObjectTree {
67
81
  (descriptor.enumerable ||
68
82
  (descriptor.get !== undefined && descriptor.set !== undefined))
69
83
  )
70
- .map(([name]) => name);
84
+ .map(([name, descriptor]) =>
85
+ trailingSlash.has(name)
86
+ ? // Preserve existing slash
87
+ name
88
+ : // Add a slash if the value is a plain property and a subtree
89
+ trailingSlash.toggle(
90
+ name,
91
+ descriptor.value !== undefined &&
92
+ this.isSubtree(descriptor.value)
93
+ )
94
+ );
71
95
  for (const name of propertyNames) {
72
96
  result.add(name);
73
97
  }
@@ -83,13 +107,36 @@ export default class ObjectTree {
83
107
  * @param {any} value
84
108
  */
85
109
  async set(key, value) {
110
+ const existingKey = findExistingKey(this.object, key);
111
+
86
112
  if (value === undefined) {
87
- // Delete the key.
88
- delete this.object[key];
113
+ // Delete the key if it exists.
114
+ if (existingKey !== null) {
115
+ delete this.object[existingKey];
116
+ }
89
117
  } else {
118
+ // If the key exists under a different form, delete the existing key.
119
+ if (existingKey !== null && existingKey !== key) {
120
+ delete this.object[existingKey];
121
+ }
122
+
90
123
  // Set the value for the key.
91
124
  this.object[key] = value;
92
125
  }
126
+
93
127
  return this;
94
128
  }
95
129
  }
130
+
131
+ function findExistingKey(object, key) {
132
+ // First try key as is
133
+ if (key in object) {
134
+ return key;
135
+ }
136
+ // Try alternate form
137
+ const alternateKey = trailingSlash.toggle(key);
138
+ if (alternateKey in object) {
139
+ return alternateKey;
140
+ }
141
+ return null;
142
+ }
@@ -0,0 +1,41 @@
1
+ import SiteTree from "./SiteTree.js";
2
+
3
+ export default class OpenSiteTree extends SiteTree {
4
+ constructor(...args) {
5
+ super(...args);
6
+ this.serverKeysPromise = undefined;
7
+ }
8
+
9
+ async getServerKeys() {
10
+ // We use a promise to ensure we only check for keys once.
11
+ const href = new URL(".keys.json", this.href).href;
12
+ this.serverKeysPromise ??= fetch(href)
13
+ .then((response) => (response.ok ? response.text() : null))
14
+ .then((text) => {
15
+ try {
16
+ return text ? JSON.parse(text) : null;
17
+ } catch (error) {
18
+ // Got a response, but it's not JSON. Most likely the site doesn't
19
+ // actually have a .keys.json file, and is returning a Not Found page,
20
+ // but hasn't set the correct 404 status code.
21
+ return null;
22
+ }
23
+ });
24
+ return this.serverKeysPromise;
25
+ }
26
+
27
+ async keys() {
28
+ const serverKeys = await this.getServerKeys();
29
+ return serverKeys ?? [];
30
+ }
31
+
32
+ processResponse(response) {
33
+ // If the response was redirected to a route that ends with a slash, and the
34
+ // site is an explorable site, we return a tree for the new route.
35
+ if (response.ok && response.redirected && response.url.endsWith("/")) {
36
+ return Reflect.construct(this.constructor, [response.url]);
37
+ }
38
+
39
+ return super.processResponse(response);
40
+ }
41
+ }
package/src/SetTree.js CHANGED
@@ -1,4 +1,3 @@
1
- import { Tree } from "./internal.js";
2
1
  import { setParent } from "./utilities.js";
3
2
 
4
3
  /**
@@ -29,11 +28,6 @@ export default class SetTree {
29
28
  return value;
30
29
  }
31
30
 
32
- async isKeyForSubtree(key) {
33
- const value = this.values[key];
34
- return Tree.isAsyncTree(value);
35
- }
36
-
37
31
  async keys() {
38
32
  return this.values.keys();
39
33
  }
package/src/SiteTree.js CHANGED
@@ -1,5 +1,4 @@
1
- import { Tree } from "./internal.js";
2
- import * as jsonKeys from "./jsonKeys.js";
1
+ import * as trailingSlash from "./trailingSlash.js";
3
2
  import { setParent } from "./utilities.js";
4
3
 
5
4
  /**
@@ -20,16 +19,14 @@ export default class SiteTree {
20
19
  href = new URL(href, window.location.href).href;
21
20
  }
22
21
 
23
- if (!href.endsWith("/")) {
24
- // Add trailing slash; the URL is expected to represent a directory.
25
- href += "/";
26
- }
22
+ // Add trailing slash if not present; URL should represent a directory.
23
+ href = trailingSlash.add(href);
27
24
 
28
25
  this.href = href;
29
- this.keysPromise = undefined;
30
26
  this.parent = null;
31
27
  }
32
28
 
29
+ /** @returns {Promise<any>} */
33
30
  async get(key) {
34
31
  if (key == null) {
35
32
  // Reject nullish key.
@@ -38,25 +35,16 @@ export default class SiteTree {
38
35
  );
39
36
  }
40
37
 
41
- // If there is only one key and it's the empty string, and the site is
42
- // explorable, we take the route as "index.html". With this and subsequent
43
- // checks, we try to avoid sniffing the site to see if it's explorable, as
44
- // that necessitates an extra network request per SiteTree instance. In many
45
- // cases, that can be avoided.
46
- if (key === "" && (await this.hasKeysJson())) {
47
- key = "index.html";
48
- }
49
-
50
- const href = new URL(key, this.href).href;
51
-
52
- // If the (possibly adjusted) route ends with a slash and the site is an
53
- // explorable site, we return a tree for the indicated route.
54
- if (href.endsWith("/") && (await this.hasKeysJson())) {
38
+ // If the keys ends with a slash, return a tree for the indicated route.
39
+ if (trailingSlash.has(key)) {
40
+ const href = new URL(key, this.href).href;
55
41
  const value = Reflect.construct(this.constructor, [href]);
56
42
  setParent(value, this);
57
43
  return value;
58
44
  }
59
45
 
46
+ const href = new URL(key, this.href).href;
47
+
60
48
  // Fetch the data at the given route.
61
49
  let response;
62
50
  try {
@@ -64,65 +52,12 @@ export default class SiteTree {
64
52
  } catch (error) {
65
53
  return undefined;
66
54
  }
67
- if (!response.ok) {
68
- return undefined;
69
- }
70
-
71
- if (response.redirected && response.url.endsWith("/")) {
72
- // If the response is redirected to a route that ends with a slash, and
73
- // the site is an explorable site, we return a tree for the new route.
74
- if (await this.hasKeysJson()) {
75
- return Reflect.construct(this.constructor, [response.url]);
76
- }
77
- }
78
-
79
- const mediaType = response.headers?.get("Content-Type");
80
- if (SiteTree.mediaTypeIsText(mediaType)) {
81
- return response.text();
82
- } else {
83
- const buffer = response.arrayBuffer();
84
- setParent(buffer, this);
85
- return buffer;
86
- }
87
- }
88
-
89
- async getKeyDictionary() {
90
- // We use a promise to ensure we only check for keys once.
91
- const href = new URL(".keys.json", this.href).href;
92
- this.keysPromise ??= fetch(href)
93
- .then((response) => (response.ok ? response.text() : null))
94
- .then((text) => {
95
- try {
96
- return text ? jsonKeys.parse(text) : null;
97
- } catch (error) {
98
- // Got a response, but it's not JSON. Most likely the site doesn't
99
- // actually have a .keys.json file, and is returning a Not Found page,
100
- // but hasn't set the correct 404 status code.
101
- return null;
102
- }
103
- });
104
- return this.keysPromise;
105
- }
106
55
 
107
- async hasKeysJson() {
108
- const keyDictionary = await this.getKeyDictionary();
109
- return keyDictionary !== null;
110
- }
111
-
112
- async isKeyForSubtree(key) {
113
- const keyDictionary = await this.getKeyDictionary();
114
- if (keyDictionary) {
115
- return keyDictionary[key];
116
- } else {
117
- // Expensive check, since this fetches the key's value.
118
- const value = await this.get(key);
119
- return Tree.isAsyncTree(value);
120
- }
56
+ return this.processResponse(response);
121
57
  }
122
58
 
123
59
  async keys() {
124
- const keyDictionary = await this.getKeyDictionary();
125
- return keyDictionary ? Object.keys(keyDictionary) : [];
60
+ return [];
126
61
  }
127
62
 
128
63
  // Return true if the given media type is a standard text type.
@@ -155,15 +90,19 @@ export default class SiteTree {
155
90
  return this.href;
156
91
  }
157
92
 
158
- /**
159
- * Returns a new `SiteTree` for the given relative route.
160
- *
161
- * @param {string} path
162
- * @returns {SiteTree}
163
- */
164
- resolve(path) {
165
- const href = new URL(path, this.href).href;
166
- return Reflect.construct(this.constructor, [href]);
93
+ processResponse(response) {
94
+ if (!response.ok) {
95
+ return undefined;
96
+ }
97
+
98
+ const mediaType = response.headers?.get("Content-Type");
99
+ if (SiteTree.mediaTypeIsText(mediaType)) {
100
+ return response.text();
101
+ } else {
102
+ const buffer = response.arrayBuffer();
103
+ setParent(buffer, this);
104
+ return buffer;
105
+ }
167
106
  }
168
107
 
169
108
  get url() {
package/src/Tree.d.ts CHANGED
@@ -5,11 +5,10 @@ export function assign(target: Treelike, source: Treelike): Promise<AsyncTree>;
5
5
  export function clear(AsyncTree: AsyncMutableTree): Promise<void>;
6
6
  export function entries(AsyncTree: AsyncTree): Promise<IterableIterator<any>>;
7
7
  export function forEach(AsyncTree: AsyncTree, callbackfn: (value: any, key: any) => Promise<void>): Promise<void>;
8
- export function from(obj: any, options?: { deep: boolean }): AsyncTree;
8
+ export function from(obj: any, options?: { deep?: boolean, parent?: AsyncTree|null }): AsyncTree;
9
9
  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
- export function isKeyForSubtree(tree: AsyncTree, obj: any): Promise<boolean>;
13
12
  export function isTraversable(obj: any): boolean;
14
13
  export function isTreelike(obj: any): obj is Treelike;
15
14
  export function map(tree: Treelike, valueFn: ValueKeyFn): AsyncTree;