@weborigami/origami 0.0.40 → 0.0.42

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 (90) hide show
  1. package/exports/exports.js +33 -34
  2. package/package.json +4 -4
  3. package/src/builtins/@arrows.js +2 -11
  4. package/src/builtins/@builtins.js +2 -5
  5. package/src/builtins/@cache.js +1 -1
  6. package/src/builtins/{@tree/concat.js → @concat.js} +3 -3
  7. package/src/builtins/@copy.js +2 -2
  8. package/src/builtins/{@tree/count.js → @count.js} +3 -9
  9. package/src/builtins/@crawl.js +1 -1
  10. package/src/builtins/@debug.js +2 -8
  11. package/src/builtins/{@tree/defineds.js → @defineds.js} +3 -7
  12. package/src/builtins/@document.js +1 -1
  13. package/src/builtins/{@tree/exceptions.js → @exceptions.js} +7 -8
  14. package/src/builtins/@explore.js +2 -5
  15. package/src/builtins/@files.js +1 -1
  16. package/src/builtins/@filter.js +1 -1
  17. package/src/builtins/{@tree/first.js → @first.js} +3 -9
  18. package/src/builtins/{@tree/fn.js → @fnTree.js} +13 -7
  19. package/src/builtins/@globs.js +1 -1
  20. package/src/builtins/{@tree/groupBy.js → @groupBy.js} +7 -9
  21. package/src/builtins/@help.js +1 -1
  22. package/src/builtins/@http.js +1 -1
  23. package/src/builtins/@https.js +1 -1
  24. package/src/builtins/@if.js +1 -1
  25. package/src/builtins/@image/format.js +36 -2
  26. package/src/builtins/@image/resize.js +30 -2
  27. package/src/builtins/@index.js +2 -8
  28. package/src/builtins/@inherited.js +1 -1
  29. package/src/builtins/@inline.js +1 -1
  30. package/src/builtins/{@tree/inners.js → @inners.js} +3 -8
  31. package/src/builtins/@invoke.js +8 -0
  32. package/src/builtins/{@tree/isAsyncTree.js → @isAsyncTree.js} +1 -1
  33. package/src/builtins/@json.js +7 -1
  34. package/src/builtins/{@tree/keys.js → @keys.js} +3 -9
  35. package/src/builtins/{@tree/keysJson.js → @keysJson.js} +3 -8
  36. package/src/builtins/@loaders/ori.js +18 -18
  37. package/src/builtins/@map.js +17 -19
  38. package/src/builtins/@match.js +1 -1
  39. package/src/builtins/{@tree/merge.js → @merge.js} +2 -2
  40. package/src/builtins/{@tree/mergeDeep.js → @mergeDeep.js} +3 -3
  41. package/src/builtins/@once.js +1 -1
  42. package/src/builtins/@ori.js +1 -1
  43. package/src/builtins/@pack.js +1 -1
  44. package/src/builtins/{@tree/parent.js → @parent.js} +3 -9
  45. package/src/builtins/{@tree/paths.js → @paths.js} +3 -8
  46. package/src/builtins/@perf.js +18 -0
  47. package/src/builtins/{@tree/plain.js → @plain.js} +4 -8
  48. package/src/builtins/@project.js +1 -1
  49. package/src/builtins/@redirect.js +8 -0
  50. package/src/builtins/{@tree/reverse.js → @reverse.js} +3 -8
  51. package/src/builtins/@rss.js +1 -1
  52. package/src/builtins/@scope/extend.js +6 -6
  53. package/src/builtins/@scope/get.js +1 -1
  54. package/src/builtins/@scope/set.js +4 -4
  55. package/src/builtins/@serve.js +1 -1
  56. package/src/builtins/{@tree/setDeep.js → @setDeep.js} +1 -1
  57. package/src/builtins/{@tree/shuffle.js → @shuffle.js} +5 -11
  58. package/src/builtins/{@tree/sitemap.js → @sitemap.js} +5 -8
  59. package/src/builtins/{@tree/sort.js → @sort.js} +5 -7
  60. package/src/builtins/{@tree/sortBy.js → @sortBy.js} +7 -9
  61. package/src/builtins/{@tree/static.js → @static.js} +5 -10
  62. package/src/builtins/@svg.js +3 -9
  63. package/src/builtins/{@tree/table.js → @table.js} +3 -8
  64. package/src/builtins/{@tree/take.js → @take.js} +3 -9
  65. package/src/builtins/@tree.js +19 -0
  66. package/src/builtins/@treeHttp.js +1 -1
  67. package/src/builtins/@treeHttps.js +1 -1
  68. package/src/builtins/@unpack.js +1 -1
  69. package/src/builtins/{@tree/values.js → @values.js} +3 -8
  70. package/src/builtins/{@tree/valuesDeep.js → @valuesDeep.js} +4 -8
  71. package/src/builtins/@watch.js +3 -8
  72. package/src/builtins/@with.js +1 -1
  73. package/src/builtins/@yaml.js +7 -1
  74. package/src/builtins/{@tree/map.d.ts → map.d.ts} +1 -3
  75. package/src/cli/cli.js +2 -15
  76. package/src/common/ExplorableSiteTransform.js +6 -4
  77. package/src/common/addValueKeyToScope.js +3 -11
  78. package/src/common/processUnpackedContent.js +2 -7
  79. package/src/misc/assertScopeIsDefined.js +2 -2
  80. package/src/misc/getTreeArgument.js +51 -0
  81. package/src/{builtins/@tree/dot.js → misc/treeDot.js} +2 -11
  82. package/src/server/constructResponse.js +126 -0
  83. package/src/server/mediaTypes.js +0 -16
  84. package/src/server/server.js +62 -131
  85. package/src/builtins/@tree/flowSvg.js +0 -55
  86. package/src/builtins/@tree/from.js +0 -27
  87. package/src/builtins/@tree/fromJson.js +0 -6
  88. package/src/builtins/@tree/fromYaml.js +0 -24
  89. package/src/builtins/@tree/nextKey.js +0 -29
  90. package/src/builtins/@tree/previousKey.js +0 -29
@@ -1,5 +1,33 @@
1
1
  import sharp from "sharp";
2
2
 
3
- export default function resize(buffer, options) {
4
- return sharp(buffer).resize(options).toBuffer();
3
+ /**
4
+ * Resize an image.
5
+ *
6
+ * @this {import("@weborigami/types").AsyncTree|null}
7
+ *
8
+ * @typedef {import("sharp").ResizeOptions} ResizeOptions
9
+ *
10
+ * @overload
11
+ * @param {ResizeOptions} param1
12
+ * @returns {(buffer: Buffer) => Promise<Buffer>}
13
+ *
14
+ * @overload
15
+ * @param {Buffer} param1
16
+ * @param {ResizeOptions} param2
17
+ * @returns {Promise<Buffer>}
18
+ */
19
+ export default function resize(param1, param2) {
20
+ // Identify which overload was used.
21
+ let buffer;
22
+ let options;
23
+ if (param2 === undefined) {
24
+ options = param1;
25
+ } else {
26
+ buffer = param1;
27
+ options = param2;
28
+ }
29
+
30
+ const transform = (buffer) => sharp(buffer).resize(options).toBuffer();
31
+
32
+ return buffer ? transform(buffer) : transform;
5
33
  }
@@ -1,6 +1,5 @@
1
- import { Tree } from "@weborigami/async-tree";
2
1
  import { keySymbol } from "../common/utilities.js";
3
- import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
2
+ import getTreeArgument from "../misc/getTreeArgument.js";
4
3
 
5
4
  /**
6
5
  * Return a default index.html page for the current tree.
@@ -11,12 +10,7 @@ import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
11
10
  * @param {Treelike} [treelike]
12
11
  */
13
12
  export default async function index(treelike) {
14
- assertScopeIsDefined(this);
15
- treelike = treelike ?? (await this?.get("@current"));
16
- if (treelike === undefined) {
17
- return undefined;
18
- }
19
- const tree = Tree.from(treelike);
13
+ const tree = await getTreeArgument(this, arguments, treelike, "@index");
20
14
  const keys = Array.from(await tree.keys());
21
15
 
22
16
  // Skip system-ish files that start with a period. Also skip `index.html`.
@@ -9,7 +9,7 @@ import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
9
9
  * @this {AsyncTree|null}
10
10
  */
11
11
  export default async function inherited(key) {
12
- assertScopeIsDefined(this);
12
+ assertScopeIsDefined(this, "inherited");
13
13
  return ops.inherited.call(this, key);
14
14
  }
15
15
 
@@ -14,7 +14,7 @@ import unpackOrigamiExpression from "./@loaders/ori.js";
14
14
  * @param {StringLike} input
15
15
  */
16
16
  export default async function inline(input) {
17
- assertScopeIsDefined(this);
17
+ assertScopeIsDefined(this, "inline");
18
18
 
19
19
  // Get the input text and any attached front matter.
20
20
  let inputDocument;
@@ -1,6 +1,6 @@
1
1
  import { Tree } from "@weborigami/async-tree";
2
2
  import { Scope } from "@weborigami/language";
3
- import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
3
+ import getTreeArgument from "../misc/getTreeArgument.js";
4
4
 
5
5
  /**
6
6
  * Return the source nodes of the tree: the nodes with children.
@@ -11,12 +11,7 @@ import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
11
11
  * @param {Treelike} [treelike]
12
12
  */
13
13
  export default async function inners(treelike) {
14
- assertScopeIsDefined(this);
15
- treelike = treelike ?? (await this?.get("@current"));
16
- if (treelike === undefined) {
17
- return undefined;
18
- }
19
- const tree = Tree.from(treelike);
14
+ const tree = await getTreeArgument(this, arguments, treelike, "@inners");
20
15
 
21
16
  /** @type {AsyncTree} */
22
17
  let result = {
@@ -40,5 +35,5 @@ export default async function inners(treelike) {
40
35
  return result;
41
36
  }
42
37
 
43
- inners.usage = `inners <tree>\tThe source nodes of the tree`;
38
+ inners.usage = `@inners <tree>\tThe source nodes of the tree`;
44
39
  inners.documentation = "https://weborigami.org/cli/builtins.html#inners";
@@ -1,3 +1,4 @@
1
+ import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
1
2
  import builtins from "./@builtins.js";
2
3
 
3
4
  /**
@@ -22,6 +23,13 @@ import builtins from "./@builtins.js";
22
23
  * @this {import("@weborigami/types").AsyncTree|null}
23
24
  */
24
25
  export default async function invoke(fn) {
26
+ assertScopeIsDefined(this, "invoke");
27
+ // A fragment of the logic from getTreeArgument.js
28
+ if (arguments.length > 0 && fn === undefined) {
29
+ throw new Error(
30
+ "An Origami function was called with an initial argument, but its value is undefined."
31
+ );
32
+ }
25
33
  if (typeof fn !== "function" && fn.unpack) {
26
34
  fn = await fn.unpack();
27
35
  }
@@ -12,6 +12,6 @@ export default function isAsyncTree(value) {
12
12
  return Tree.isAsyncTree(value);
13
13
  }
14
14
 
15
- isAsyncTree.usage = `@tree/isAsyncTree <value>\tReturn true for an async tree`;
15
+ isAsyncTree.usage = `@isAsyncTree <value>\tReturn true for an async tree`;
16
16
  isAsyncTree.documentation =
17
17
  "https://weborigami.org/cli/builtins.html#isAsyncTree";
@@ -9,7 +9,13 @@ import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
9
9
  * @param {any} [obj]
10
10
  */
11
11
  export default async function json(obj) {
12
- assertScopeIsDefined(this);
12
+ assertScopeIsDefined(this, "json");
13
+ // A fragment of the logic from getTreeArgument.js
14
+ if (arguments.length > 0 && obj === undefined) {
15
+ throw new Error(
16
+ "An Origami function was called with an initial argument, but its value is undefined."
17
+ );
18
+ }
13
19
  obj = obj ?? (await this?.get("@current"));
14
20
  if (obj === undefined) {
15
21
  return undefined;
@@ -1,5 +1,4 @@
1
- import { Tree } from "@weborigami/async-tree";
2
- import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
1
+ import getTreeArgument from "../misc/getTreeArgument.js";
3
2
 
4
3
  /**
5
4
  * Return the top-level keys in the tree as an array.
@@ -10,15 +9,10 @@ import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
10
9
  * @param {Treelike} [treelike]
11
10
  */
12
11
  export default async function keys(treelike) {
13
- assertScopeIsDefined(this);
14
- treelike = treelike ?? (await this?.get("@current"));
15
- if (treelike === undefined) {
16
- return undefined;
17
- }
18
- const tree = Tree.from(treelike);
12
+ const tree = await getTreeArgument(this, arguments, treelike, "@keys");
19
13
  const keys = await tree.keys();
20
14
  return Array.from(keys);
21
15
  }
22
16
 
23
- keys.usage = `keys <tree>\tThe top-level keys in the tree`;
17
+ keys.usage = `@keys <tree>\tThe top-level keys in the tree`;
24
18
  keys.documentation = "https://weborigami.org/cli/builtins.html#keys";
@@ -1,7 +1,7 @@
1
1
  import { Tree, keysJson } from "@weborigami/async-tree";
2
2
  import { Scope } from "@weborigami/language";
3
- import { transformObject } from "../../common/utilities.js";
4
- import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
3
+ import { transformObject } from "../common/utilities.js";
4
+ import getTreeArgument from "../misc/getTreeArgument.js";
5
5
 
6
6
  /**
7
7
  * Expose .keys.json for a tree.
@@ -12,12 +12,7 @@ import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
12
12
  * @param {Treelike} treelike
13
13
  */
14
14
  export default async function treeKeysJson(treelike) {
15
- assertScopeIsDefined(this);
16
- treelike = treelike ?? (await this?.get("@current"));
17
- if (treelike === undefined) {
18
- return undefined;
19
- }
20
- const tree = Tree.from(treelike);
15
+ const tree = await getTreeArgument(this, arguments, treelike, "@keysJson");
21
16
  let result = transformObject(KeysJsonTransform, tree);
22
17
  result = Scope.treeWithScope(result, this);
23
18
  return result;
@@ -17,27 +17,27 @@ export default async function unpackOrigamiExpression(
17
17
  options.parent ??
18
18
  /** @type {any} */ (inputDocument).parent ??
19
19
  /** @type {any} */ (inputDocument)[utilities.parentSymbol];
20
- const compiler = options.compiler ?? compile.expression;
21
20
 
22
- // Compile the body text as an Origami expression and evaluate it.
23
- const inputText = utilities.toString(inputDocument);
24
- let fn;
25
- try {
26
- fn = compiler(inputText);
27
- } catch (/** @type {any} */ error) {
28
- let location = "";
29
- if (options.key) {
30
- location += `${options.key}`;
31
- }
32
- if (error.location) {
33
- const { start } = error.location;
34
- location += `, line ${start.line}, column ${start.column}`;
35
- }
36
- if (location) {
37
- error.message += ` (${location})`;
21
+ // Construct an object to represent the source code.
22
+ const sourceName = options.key;
23
+ let url;
24
+ if (sourceName && parent?.url) {
25
+ let parentHref = parent.url.href;
26
+ if (!parentHref.endsWith("/")) {
27
+ parentHref += "/";
38
28
  }
39
- throw error;
29
+ url = new URL(sourceName, parentHref);
40
30
  }
31
+
32
+ const source = {
33
+ text: utilities.toString(inputDocument),
34
+ name: options.key,
35
+ url,
36
+ };
37
+
38
+ // Compile the source code as an Origami expression and evaluate it.
39
+ const compiler = options.compiler ?? compile.expression;
40
+ const fn = compiler(source);
41
41
  const parentScope = parent ? Scope.getScope(parent) : builtins;
42
42
  let content = await fn.call(parentScope);
43
43
 
@@ -19,8 +19,8 @@ import { toFunction } from "../common/utilities.js";
19
19
  * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
20
20
  *
21
21
  * @typedef {{ deep?: boolean, description?: string, extensions?: string,
22
- * inverseKeyMap?: KeyFn, keyMap?: ValueKeyFn, keyName?: string, valueMap?:
23
- * ValueKeyFn, valueName?: string }} TreeMapOptions
22
+ * inverseKeyMap?: KeyFn, keyMap?: ValueKeyFn, valueMap?: ValueKeyFn }}
23
+ * TreeMapOptions
24
24
  *
25
25
  * @this {import("@weborigami/types").AsyncTree|null}
26
26
  *
@@ -47,8 +47,16 @@ export default function treeMap(param1, param2) {
47
47
  // or the second.
48
48
  let source;
49
49
  let options;
50
- if (param2 === undefined) {
50
+ if (arguments.length === 0) {
51
+ throw new TypeError(
52
+ `@map: You must give @map a function or a dictionary of options.`
53
+ );
54
+ } else if (!param1) {
55
+ throw new TypeError(`@map: The first argument was undefined.`);
56
+ } else if (arguments.length === 1) {
51
57
  options = param1;
58
+ } else if (!param2) {
59
+ throw new TypeError(`@map: The second argument was undefined.`);
52
60
  } else {
53
61
  source = param1;
54
62
  options = param2;
@@ -63,6 +71,10 @@ export default function treeMap(param1, param2) {
63
71
  ) {
64
72
  valueMap = options;
65
73
  options = {};
74
+ } else if (!options) {
75
+ throw new TypeError(
76
+ `@map: You must specify a valueMap function or options dictionary.`
77
+ );
66
78
  } else {
67
79
  valueMap = options.valueMap;
68
80
  }
@@ -73,9 +85,7 @@ export default function treeMap(param1, param2) {
73
85
  extensions,
74
86
  inverseKeyMap,
75
87
  keyMap,
76
- keyName,
77
88
  needsSourceValue,
78
- valueName,
79
89
  } = options;
80
90
 
81
91
  description ??= `@map ${extensions ?? ""}`;
@@ -93,13 +103,7 @@ export default function treeMap(param1, param2) {
93
103
  if (valueMap) {
94
104
  const resolvedValueFn = toFunction(valueMap);
95
105
  extendedValueFn = function (sourceValue, sourceKey, tree) {
96
- const scope = addValueKeyToScope(
97
- baseScope,
98
- sourceValue,
99
- sourceKey,
100
- valueName,
101
- keyName
102
- );
106
+ const scope = addValueKeyToScope(baseScope, sourceValue, sourceKey);
103
107
  return resolvedValueFn.call(scope, sourceValue, sourceKey, tree);
104
108
  };
105
109
  }
@@ -119,13 +123,7 @@ export default function treeMap(param1, param2) {
119
123
  const resolvedKeyFn = toFunction(keyMap);
120
124
  async function scopedKeyFn(sourceKey, tree) {
121
125
  const sourceValue = await tree.get(sourceKey);
122
- const scope = addValueKeyToScope(
123
- baseScope,
124
- sourceValue,
125
- sourceKey,
126
- valueName,
127
- keyName
128
- );
126
+ const scope = addValueKeyToScope(baseScope, sourceValue, sourceKey);
129
127
  const resultKey = await resolvedKeyFn.call(
130
128
  scope,
131
129
  sourceValue,
@@ -26,7 +26,7 @@ import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
26
26
  * @this {AsyncTree|null}
27
27
  */
28
28
  export default function match(pattern, resultFn, keys = []) {
29
- assertScopeIsDefined(this);
29
+ assertScopeIsDefined(this, "match");
30
30
  let regex;
31
31
  if (typeof pattern === "string") {
32
32
  // Convert the simple pattern format into a regular expression.
@@ -1,6 +1,6 @@
1
1
  import { merge } from "@weborigami/async-tree";
2
2
  import { Scope } from "@weborigami/language";
3
- import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
3
+ import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
4
4
 
5
5
  /**
6
6
  * Create a tree that's the result of merging the given trees.
@@ -12,7 +12,7 @@ import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
12
12
  * @param {(Treelike|null)[]} trees
13
13
  */
14
14
  export default async function treeMerge(...trees) {
15
- assertScopeIsDefined(this);
15
+ assertScopeIsDefined(this, "merge");
16
16
 
17
17
  // Filter out null or undefined trees.
18
18
  /** @type {Treelike[]}
@@ -1,6 +1,6 @@
1
1
  import { mergeDeep } from "@weborigami/async-tree";
2
2
  import { Scope } from "@weborigami/language";
3
- import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
3
+ import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
4
4
 
5
5
  /**
6
6
  * Create a tree that's the result of deep merging the given trees.
@@ -11,7 +11,7 @@ import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
11
11
  * @param {Treelike[]} trees
12
12
  */
13
13
  export default async function treeMergeDeep(...trees) {
14
- assertScopeIsDefined(this);
14
+ assertScopeIsDefined(this, "mergeDeep");
15
15
  // Filter out null or undefined trees.
16
16
  const filtered = trees.filter((tree) => tree);
17
17
 
@@ -39,6 +39,6 @@ export default async function treeMergeDeep(...trees) {
39
39
  return result;
40
40
  }
41
41
 
42
- treeMergeDeep.usage = `mergeDeep <...trees>\tMerge the given trees deeply`;
42
+ treeMergeDeep.usage = `@mergeDeep <...trees>\tMerge the given trees deeply`;
43
43
  treeMergeDeep.documentation =
44
44
  "https://weborigami.org/cli/builtins.html#mergeDeep";
@@ -10,7 +10,7 @@ const fnPromiseMap = new WeakMap();
10
10
  * @param {Function} fn
11
11
  */
12
12
  export default async function once(fn) {
13
- assertScopeIsDefined(this);
13
+ assertScopeIsDefined(this, "once");
14
14
  if (!fnPromiseMap.has(fn)) {
15
15
  fnPromiseMap.set(fn, fn.call(this));
16
16
  }
@@ -19,7 +19,7 @@ export default async function ori(
19
19
  expression,
20
20
  options = { formatResult: true }
21
21
  ) {
22
- assertScopeIsDefined(this);
22
+ assertScopeIsDefined(this, "ori");
23
23
  // In case expression is a Buffer, cast it to a string.
24
24
  expression = String(expression);
25
25
 
@@ -8,6 +8,6 @@ import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
8
8
  * @returns
9
9
  */
10
10
  export default function pack(obj) {
11
- assertScopeIsDefined(this);
11
+ assertScopeIsDefined(this, "pack");
12
12
  return obj?.pack?.();
13
13
  }
@@ -1,5 +1,4 @@
1
- import { Tree } from "@weborigami/async-tree";
2
- import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
1
+ import getTreeArgument from "../misc/getTreeArgument.js";
3
2
 
4
3
  /**
5
4
  * Returns the parent of the current tree.
@@ -11,14 +10,9 @@ import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
11
10
  * @param {Treelike} [treelike]
12
11
  */
13
12
  export default async function parent(treelike) {
14
- assertScopeIsDefined(this);
15
- treelike = treelike ?? (await this?.get("@current"));
16
- if (treelike === undefined) {
17
- return undefined;
18
- }
19
- const tree = Tree.from(treelike);
13
+ const tree = await getTreeArgument(this, arguments, treelike, "@parent");
20
14
  return tree.parent;
21
15
  }
22
16
 
23
- parent.usage = `parent\tThe parent of the current tree`;
17
+ parent.usage = `@parent\tThe parent of the current tree`;
24
18
  parent.documentation = "https://weborigami.org/cli/builtins.html#parent";
@@ -1,5 +1,5 @@
1
1
  import { Tree } from "@weborigami/async-tree";
2
- import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
2
+ import getTreeArgument from "../misc/getTreeArgument.js";
3
3
 
4
4
  /**
5
5
  * Return an array of paths to the values in the tree.
@@ -12,13 +12,8 @@ import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
12
12
  * @param {string} [prefix]
13
13
  */
14
14
  export default async function paths(treelike, prefix = "") {
15
- assertScopeIsDefined(this);
16
- treelike = treelike ?? (await this?.get("@current"));
17
- if (treelike === undefined) {
18
- return undefined;
19
- }
15
+ const tree = await getTreeArgument(this, arguments, treelike, "@paths");
20
16
  const result = [];
21
- const tree = Tree.from(treelike);
22
17
  for (const key of await tree.keys()) {
23
18
  const valuePath = prefix ? `${prefix}/${key}` : key;
24
19
  const value = await tree.get(key);
@@ -32,4 +27,4 @@ export default async function paths(treelike, prefix = "") {
32
27
  return result;
33
28
  }
34
29
 
35
- paths.usage = `paths(tree)\tReturn an array of paths to the values in the tree`;
30
+ paths.usage = `@paths(tree)\tReturn an array of paths to the values in the tree`;
@@ -0,0 +1,18 @@
1
+ import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
2
+
3
+ /**
4
+ * Return the number of milliseconds required to execute the given function the
5
+ * specified number of times.
6
+ *
7
+ * @this {import("@weborigami/types").AsyncTree|null}
8
+ * @param {Function} fn
9
+ */
10
+ export default async function perf(fn, count = 10000) {
11
+ assertScopeIsDefined(this, "perf");
12
+ const start = performance.now();
13
+ for (let i = 0; i < count; i++) {
14
+ await fn.call(this);
15
+ }
16
+ const end = performance.now();
17
+ return end - start;
18
+ }
@@ -1,5 +1,5 @@
1
1
  import { Tree } from "@weborigami/async-tree";
2
- import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
2
+ import getTreeArgument from "../misc/getTreeArgument.js";
3
3
 
4
4
  /**
5
5
  * Return the interior nodes of the tree.
@@ -10,13 +10,9 @@ import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
10
10
  * @param {Treelike} [treelike]
11
11
  */
12
12
  export default async function plain(treelike) {
13
- assertScopeIsDefined(this);
14
- treelike = treelike ?? (await this?.get("@current"));
15
- if (treelike === undefined) {
16
- return undefined;
17
- }
18
- return Tree.plain(treelike);
13
+ const tree = await getTreeArgument(this, arguments, treelike, "@plain");
14
+ return Tree.plain(tree);
19
15
  }
20
16
 
21
- plain.usage = `plain <tree>\tA plain JavaScript object representation of the tree`;
17
+ plain.usage = `@plain <tree>\tA plain JavaScript object representation of the tree`;
22
18
  plain.documentation = "https://weborigami.org/cli/builtins.html#plain";
@@ -21,7 +21,7 @@ const configFileName = "ori.config.js";
21
21
  * @param {any} [key]
22
22
  */
23
23
  export default async function project(key) {
24
- assertScopeIsDefined(this);
24
+ assertScopeIsDefined(this, "project");
25
25
 
26
26
  const dirname = process.cwd();
27
27
  const currentTree = new OrigamiFiles(dirname);
@@ -0,0 +1,8 @@
1
+ export default function redirect(url, options = { permanent: false }) {
2
+ return new Response("ok", {
3
+ headers: {
4
+ Location: url,
5
+ },
6
+ status: options.permanent ? 301 : 307,
7
+ });
8
+ }
@@ -1,6 +1,6 @@
1
1
  import { Tree } from "@weborigami/async-tree";
2
2
  import { Scope } from "@weborigami/language";
3
- import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
3
+ import getTreeArgument from "../misc/getTreeArgument.js";
4
4
 
5
5
  /**
6
6
  * Reverse the order of the top-level keys in the tree.
@@ -14,13 +14,8 @@ import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
14
14
  * @param {PlainObject} [options]
15
15
  */
16
16
  export default async function reverse(treelike, options = {}) {
17
- assertScopeIsDefined(this);
18
- treelike = treelike ?? (await this?.get("@current"));
19
- if (treelike === undefined) {
20
- return undefined;
21
- }
17
+ const tree = await getTreeArgument(this, arguments, treelike, "@reverse");
22
18
  const scope = this;
23
- const tree = Tree.from(treelike);
24
19
  const deep = options.deep ?? false;
25
20
 
26
21
  /** @type {AsyncTree} */
@@ -47,5 +42,5 @@ export default async function reverse(treelike, options = {}) {
47
42
  return reversed;
48
43
  }
49
44
 
50
- reverse.usage = `reverse <tree>\tReverses the order of the tree's top-level keys`;
45
+ reverse.usage = `@reverse <tree>\tReverses the order of the tree's top-level keys`;
51
46
  reverse.documentation = "https://weborigami.org/cli/builtins.html#reverse";
@@ -8,7 +8,7 @@ import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
8
8
  * @param {Treelike} jsonFeedTree
9
9
  */
10
10
  export default async function rss(jsonFeedTree) {
11
- assertScopeIsDefined(this);
11
+ assertScopeIsDefined(this, "rss");
12
12
  const jsonFeed = await Tree.plain(jsonFeedTree);
13
13
  const { description, home_page_url, items, feed_url, title } = jsonFeed;
14
14
 
@@ -1,5 +1,5 @@
1
1
  import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
2
- import setScope from "./set.js";
2
+ import scopeSet from "./set.js";
3
3
 
4
4
  /**
5
5
  * Return a copy of the given tree whose scope includes the given trees *and*
@@ -12,11 +12,11 @@ import setScope from "./set.js";
12
12
  * @param {...Treelike} scopeTrees
13
13
  * @this {AsyncTree|null}
14
14
  */
15
- export default function extendScope(treelike, ...scopeTrees) {
16
- assertScopeIsDefined(this);
15
+ export default function scopeExtend(treelike, ...scopeTrees) {
16
+ assertScopeIsDefined(this, "scopeExtend");
17
17
  const scope = this;
18
- return setScope.call(scope, treelike, ...scopeTrees, scope);
18
+ return scopeSet.call(scope, treelike, ...scopeTrees, scope);
19
19
  }
20
20
 
21
- extendScope.usage = `@scope/extend <tree>, <...trees>\tExtends tree's scope with the given trees`;
22
- extendScope.documentation = "https://weborigami.org/cli/builtins.html#@scope";
21
+ scopeExtend.usage = `@scope/extend <tree>, <...trees>\tExtends tree's scope with the given trees`;
22
+ scopeExtend.documentation = "https://weborigami.org/cli/builtins.html#@scope";
@@ -11,7 +11,7 @@ import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
11
11
  * @param {any} [obj]
12
12
  */
13
13
  export default async function getScope(obj) {
14
- assertScopeIsDefined(this);
14
+ assertScopeIsDefined(this, "getScope");
15
15
  if (obj) {
16
16
  /** @type {any} */
17
17
  const tree = Tree.from(obj);
@@ -12,8 +12,8 @@ import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
12
12
  * @param {...(Treelike|null)} scopeTrees
13
13
  * @this {AsyncTree|null}
14
14
  */
15
- export default function setScope(treelike, ...scopeTrees) {
16
- assertScopeIsDefined(this);
15
+ export default function scopeSet(treelike, ...scopeTrees) {
16
+ assertScopeIsDefined(this, "scopeSet");
17
17
  const tree = Tree.from(treelike);
18
18
  const scope = scopeTrees.length === 0 ? this : new Scope(...scopeTrees);
19
19
  const result = Scope.treeWithScope(tree, scope);
@@ -21,5 +21,5 @@ export default function setScope(treelike, ...scopeTrees) {
21
21
  return result;
22
22
  }
23
23
 
24
- setScope.usage = `@scope/set <tree>, <...trees>\tReturns a tree copy with the given scope`;
25
- setScope.documentation = "https://weborigami.org/cli/builtins.html#@scope";
24
+ scopeSet.usage = `@scope/set <tree>, <...trees>\tReturns a tree copy with the given scope`;
25
+ scopeSet.documentation = "https://weborigami.org/cli/builtins.html#@scope";
@@ -22,7 +22,7 @@ const defaultPort = 5000;
22
22
  * @this {AsyncTree|null}
23
23
  */
24
24
  export default async function serve(treelike, port) {
25
- assertScopeIsDefined(this);
25
+ assertScopeIsDefined(this, "serve");
26
26
  let tree;
27
27
  if (treelike) {
28
28
  tree = Tree.from(treelike);
@@ -41,5 +41,5 @@ async function applyUpdateForKey(source, target, key) {
41
41
  await target.set(key, sourceValue);
42
42
  }
43
43
 
44
- setDeep.usage = `setDeep <target>, <source>\tApplies the source tree to the target`;
44
+ setDeep.usage = `@setDeep <target>, <source>\tApplies the source tree to the target`;
45
45
  setDeep.documentation = "https://weborigami.org/cli/builtins.html#setDeep";