@weborigami/origami 0.0.58 → 0.0.60

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 (86) hide show
  1. package/exports/buildExports.js +6 -2
  2. package/exports/exports.js +2 -6
  3. package/package.json +12 -12
  4. package/src/builtins/@arrowsMap.js +4 -5
  5. package/src/builtins/@breakpoint.js +12 -0
  6. package/src/builtins/@cache.js +3 -6
  7. package/src/builtins/@concat.js +3 -4
  8. package/src/builtins/@config.js +3 -12
  9. package/src/builtins/@constructor.js +3 -7
  10. package/src/builtins/@copy.js +2 -2
  11. package/src/builtins/@crawl.js +4 -6
  12. package/src/builtins/@debug.js +5 -6
  13. package/src/builtins/@deepMap.js +2 -2
  14. package/src/builtins/@deepMapFn.js +2 -2
  15. package/src/builtins/@deepMerge.js +3 -18
  16. package/src/builtins/@deepReverse.js +1 -3
  17. package/src/builtins/@deepTakeFn.js +5 -6
  18. package/src/builtins/@defineds.js +4 -7
  19. package/src/builtins/@document.js +2 -2
  20. package/src/builtins/@exceptions.js +1 -5
  21. package/src/builtins/@explore.js +15 -14
  22. package/src/builtins/@files.js +4 -6
  23. package/src/builtins/@filter.js +3 -6
  24. package/src/builtins/@fnTree.js +8 -22
  25. package/src/builtins/@globs.js +3 -6
  26. package/src/builtins/@groupFn.js +7 -8
  27. package/src/builtins/@help.js +2 -2
  28. package/src/builtins/@http.js +2 -2
  29. package/src/builtins/@https.js +2 -2
  30. package/src/builtins/@if.js +2 -2
  31. package/src/builtins/@image/format.js +5 -5
  32. package/src/builtins/@image/formatFn.js +6 -3
  33. package/src/builtins/@image/resize.js +5 -5
  34. package/src/builtins/@image/resizeFn.js +6 -3
  35. package/src/builtins/@inherited.js +2 -2
  36. package/src/builtins/@inline.js +5 -9
  37. package/src/builtins/@inners.js +0 -1
  38. package/src/builtins/@invoke.js +4 -7
  39. package/src/builtins/@json.js +5 -6
  40. package/src/builtins/@map.js +2 -2
  41. package/src/builtins/@mapFn.js +9 -10
  42. package/src/builtins/@match.js +17 -16
  43. package/src/builtins/@mdHtml.js +4 -1
  44. package/src/builtins/@merge.js +3 -18
  45. package/src/builtins/@once.js +2 -2
  46. package/src/builtins/@ori.js +8 -7
  47. package/src/builtins/@pack.js +2 -2
  48. package/src/builtins/@package.js +4 -8
  49. package/src/builtins/@paginateFn.js +1 -6
  50. package/src/builtins/@perf.js +4 -3
  51. package/src/builtins/@project.js +31 -22
  52. package/src/builtins/@reverse.js +1 -3
  53. package/src/builtins/@rss.js +2 -2
  54. package/src/builtins/@serve.js +2 -2
  55. package/src/builtins/@sitemap.js +4 -1
  56. package/src/builtins/@sortFn.js +6 -7
  57. package/src/builtins/@stdin.js +7 -1
  58. package/src/builtins/@string.js +2 -2
  59. package/src/builtins/@takeFn.js +5 -6
  60. package/src/builtins/@treeHttp.js +2 -2
  61. package/src/builtins/@treeHttps.js +2 -2
  62. package/src/builtins/@unpack.js +3 -3
  63. package/src/builtins/@watch.js +5 -12
  64. package/src/builtins/@yaml.js +5 -6
  65. package/src/builtins/jpeg_handler.js +4 -0
  66. package/src/builtins/ori_handler.js +3 -3
  67. package/src/builtins/~.js +3 -3
  68. package/src/cli/cli.js +6 -19
  69. package/src/common/ConstantTree.js +1 -0
  70. package/src/common/ExplorableSiteTransform.js +9 -12
  71. package/src/common/documentObject.js +2 -2
  72. package/src/common/processUnpackedContent.js +12 -18
  73. package/src/common/serialize.d.ts +0 -2
  74. package/src/common/serialize.js +3 -90
  75. package/src/misc/OriCommandTransform.js +1 -9
  76. package/src/misc/assertTreeIsDefined.d.ts +1 -0
  77. package/src/misc/assertTreeIsDefined.js +7 -0
  78. package/src/misc/getTreeArgument.js +11 -12
  79. package/src/server/constructResponse.js +4 -4
  80. package/src/server/server.js +3 -10
  81. package/src/builtins/@scope/extend.js +0 -22
  82. package/src/builtins/@scope/get.js +0 -25
  83. package/src/builtins/@scope/invoke.js +0 -22
  84. package/src/builtins/@scope/set.js +0 -25
  85. package/src/common/addValueKeyToScope.js +0 -23
  86. package/src/misc/assertScopeIsDefined.js +0 -7
@@ -1,4 +1,4 @@
1
- import { isPlainObject, isUnpackable } from "@weborigami/async-tree";
1
+ import { isPlainObject, isUnpackable, toString } from "@weborigami/async-tree";
2
2
  // import txtHandler from "../builtins/txt_handler.js";
3
3
 
4
4
  /**
@@ -25,7 +25,7 @@ export default async function documentObject(input, data) {
25
25
  text = input["@text"];
26
26
  inputData = input;
27
27
  } else {
28
- text = String(input);
28
+ text = toString(input);
29
29
  inputData = null;
30
30
  }
31
31
  // TODO: Either restore this code, or move responsibility for packing a
@@ -1,5 +1,4 @@
1
1
  import { Tree } from "@weborigami/async-tree";
2
- import { Scope } from "@weborigami/language";
3
2
  import builtins from "../builtins/@builtins.js";
4
3
 
5
4
  /**
@@ -15,23 +14,18 @@ import builtins from "../builtins/@builtins.js";
15
14
  */
16
15
  export default function processUnpackedContent(content, parent, attachedData) {
17
16
  if (typeof content === "function") {
18
- // Wrap the function such to add ambients to the scope.
19
- const fn = content;
20
-
21
- // Use the parent's scope, adding any attached data.
22
- const parentScope = parent ? Scope.getScope(parent) : builtins;
23
-
24
- // If there's attached data, include it in the scope.
25
- const extendedScope = attachedData
26
- ? new Scope(attachedData, parentScope)
27
- : parentScope;
28
-
29
- const boundFn = fn.bind(extendedScope);
30
- return boundFn;
31
- } else if (
32
- Tree.isAsyncTree(content) &&
33
- !(/** @type {any} */ (content).scope)
34
- ) {
17
+ // Bind the function to a target that's the attached data (if it exists) or
18
+ // the parent.
19
+ const base = parent ?? builtins;
20
+ let target;
21
+ if (attachedData) {
22
+ target = Tree.from(attachedData);
23
+ target.parent = base;
24
+ } else {
25
+ target = base;
26
+ }
27
+ return content.bind(target);
28
+ } else if (Tree.isAsyncTree(content) && !content.parent) {
35
29
  const result = Object.create(content);
36
30
  result.parent = parent;
37
31
  return result;
@@ -4,6 +4,4 @@ import type { JsonValue } from "../../index.ts";
4
4
  export function evaluateYaml(text: string, parent?: AsyncTree|null): Promise<JsonValue>;
5
5
  export function parseYaml(text: string): JsonValue|AsyncTree;
6
6
  export function toJson(obj: JsonValue | AsyncTree): Promise<string>;
7
- export function toJsonValue(obj: any): Promise<JsonValue>;
8
- export function toValue(obj: any, jsonValuesOnly?: boolean): Promise<JsonValue>;
9
7
  export function toYaml(obj: JsonValue | AsyncTree): Promise<string>;
@@ -5,12 +5,9 @@
5
5
  * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
6
6
  */
7
7
 
8
- import { Tree, isStringLike } from "@weborigami/async-tree";
8
+ import { Tree, toPlainValue } from "@weborigami/async-tree";
9
9
  import * as YAMLModule from "yaml";
10
10
 
11
- const textDecoder = new TextDecoder();
12
- const TypedArray = Object.getPrototypeOf(Uint8Array);
13
-
14
11
  // The "yaml" package doesn't seem to provide a default export that the browser can
15
12
  // recognize, so we have to handle two ways to accommodate Node and the browser.
16
13
  // @ts-ignore
@@ -31,21 +28,6 @@ export async function evaluateYaml(text, parent) {
31
28
  }
32
29
  }
33
30
 
34
- /**
35
- * @param {any} obj
36
- * @returns {obj is JsonValue}
37
- */
38
- function isJsonValue(obj) {
39
- const t = typeof obj;
40
- return (
41
- t === "boolean" ||
42
- t === "number" ||
43
- t === "string" ||
44
- obj instanceof Date ||
45
- obj === null
46
- );
47
- }
48
-
49
31
  /**
50
32
  * @param {string} text
51
33
  * @returns {JsonValue|AsyncTree}
@@ -60,79 +42,10 @@ export function parseYaml(text) {
60
42
  * @param {any} obj
61
43
  */
62
44
  export async function toJson(obj) {
63
- const serializable = await toJsonValue(obj);
45
+ const serializable = await toPlainValue(obj);
64
46
  return JSON.stringify(serializable, null, 2);
65
47
  }
66
48
 
67
- /**
68
- * Convert the given object to a corresponding JSON value that can be
69
- * represented as JSON or YAML.
70
- *
71
- * @param {any} object
72
- * @returns {Promise<JsonValue>}
73
- */
74
- export async function toJsonValue(object) {
75
- return toValue(object, true);
76
- }
77
-
78
- /**
79
- * Convert the given input to the plainest possible JavaScript value. This
80
- * helper is intended for functions that want to accept an argument from the ori
81
- * CLI, which could a string, a file buffer, an ArrayBuffer from a URL, or some
82
- * other kind of JavaScript object.
83
- *
84
- * If the input implements the `unpack()` method, the input will be unpacked and
85
- * before processing.
86
- *
87
- * If the input is treelike, it will be converted to a plain JavaScript object,
88
- * recursively traversing the tree and converting all values to plain types.
89
- *
90
- * If the input is stringlike, its text will be returned.
91
- *
92
- * If the input is a Buffer or ArrayBuffer, it will be interpreted as UTF-8
93
- * text.
94
- *
95
- * If the input has a custom class instance, its public properties will be
96
- * returned as a plain object.
97
- *
98
- * The `jsonValuesOnly` parameter can be set to `true` to ensure that the
99
- * returned value can be represented as JSON. If the input can't be represented
100
- * as JSON, an error is thrown.
101
- *
102
- * @param {any} input
103
- * @param {boolean} [jsonValuesOnly]
104
- * @returns {Promise<any>}
105
- */
106
- export async function toValue(input, jsonValuesOnly = false) {
107
- if (input instanceof Promise) {
108
- // Resolve promise before processing.
109
- return toValue(await input, jsonValuesOnly);
110
- } else if (isJsonValue(input)) {
111
- return input;
112
- } else if (typeof input !== "object") {
113
- if (jsonValuesOnly) {
114
- throw new TypeError(`Couldn't serialize value to JSON: ${input}`);
115
- } else {
116
- return input;
117
- }
118
- } else if (isStringLike(input) && !(input instanceof Array)) {
119
- return String(input);
120
- } else if (Tree.isTreelike(input)) {
121
- const mapped = await Tree.map(input, (value) => toValue(value));
122
- return Tree.plain(mapped);
123
- } else if (input instanceof ArrayBuffer || input instanceof TypedArray) {
124
- // Interpret input as UTF-8 text.
125
- return textDecoder.decode(input);
126
- } else {
127
- // Some other kind of class instance; return its public properties.
128
- const plain = {};
129
- for (const [key, value] of Object.entries(input)) {
130
- plain[key] = await toValue(value);
131
- }
132
- return plain;
133
- }
134
- }
135
-
136
49
  /**
137
50
  * Serializes an object as a JSON string.
138
51
  *
@@ -140,6 +53,6 @@ export async function toValue(input, jsonValuesOnly = false) {
140
53
  * @returns {Promise<string>}
141
54
  */
142
55
  export async function toYaml(obj) {
143
- const serializable = await toJsonValue(obj);
56
+ const serializable = await toPlainValue(obj);
144
57
  return YAML.stringify(serializable);
145
58
  }
@@ -1,8 +1,5 @@
1
1
  /** @typedef {import("@weborigami/types").AsyncTree} AsyncTree */
2
- import { ObjectTree } from "@weborigami/async-tree";
3
- import { Scope } from "@weborigami/language";
4
2
  import ori from "../builtins/@ori.js";
5
- import { keySymbol } from "../common/utilities.js";
6
3
 
7
4
  /**
8
5
  * Add support for commands prefixed with `!`.
@@ -27,13 +24,8 @@ export default function OriCommandTransform(Base) {
27
24
  return undefined;
28
25
  }
29
26
  // Key is an Origami command; invoke it.
30
- const ambientsTree = new ObjectTree({
31
- "@current": this,
32
- });
33
- ambientsTree[keySymbol] = "ori command";
34
- const extendedScope = new Scope(ambientsTree, Scope.getScope(this));
35
27
  const source = key.slice(1).trim();
36
- value = await ori.call(extendedScope, source, { formatResult: false });
28
+ value = await ori.call(this, source, { formatResult: false });
37
29
  }
38
30
 
39
31
  return value;
@@ -0,0 +1 @@
1
+ export default function assertTreeIsDefined(tree: any, methodName: string): asserts tree is object
@@ -0,0 +1,7 @@
1
+ export default function assertTreeIsDefined(tree, methodName) {
2
+ if (tree === undefined) {
3
+ throw new Error(
4
+ `${methodName} must be called with a tree target. If you don't want to pass a tree, invoke with: ${methodName}.call(null)`
5
+ );
6
+ }
7
+ }
@@ -1,7 +1,6 @@
1
1
  import { Tree, isUnpackable } from "@weborigami/async-tree";
2
2
  import { isTreelike } from "@weborigami/async-tree/src/Tree.js";
3
- import { Scope } from "@weborigami/language";
4
- import assertScopeIsDefined from "./assertScopeIsDefined.js";
3
+ import assertTreeIsDefined from "./assertTreeIsDefined.js";
5
4
 
6
5
  /**
7
6
  * Many Origami built-in functions accept an optional treelike object as their
@@ -15,19 +14,19 @@ import assertScopeIsDefined from "./assertScopeIsDefined.js";
15
14
  * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
16
15
  * @typedef {import("@weborigami/async-tree").Treelike} Treelike
17
16
  *
18
- * @param {AsyncTree|null} scope
17
+ * @param {AsyncTree|null} parent
19
18
  * @param {IArguments} args
20
19
  * @param {Treelike|undefined} treelike
21
20
  * @param {string} methodName
22
21
  * @returns {Promise<AsyncTree>}
23
22
  */
24
23
  export default async function getTreeArgument(
25
- scope,
24
+ parent,
26
25
  args,
27
26
  treelike,
28
27
  methodName
29
28
  ) {
30
- assertScopeIsDefined(scope);
29
+ assertTreeIsDefined(parent, methodName);
31
30
 
32
31
  if (treelike !== undefined) {
33
32
  if (isUnpackable(treelike)) {
@@ -36,9 +35,9 @@ export default async function getTreeArgument(
36
35
  if (isTreelike(treelike)) {
37
36
  let tree = Tree.from(treelike);
38
37
  // If the tree was created from a treelike object and does not yet have a
39
- // parent or scope, put it in the current scope.
40
- if (!tree.parent && !(/** @type {any} */ (tree).scope)) {
41
- tree = Scope.treeWithScope(tree, scope);
38
+ // parent, make the current tree its parent.
39
+ if (!tree.parent) {
40
+ tree.parent = parent;
42
41
  }
43
42
  return tree;
44
43
  }
@@ -48,13 +47,13 @@ export default async function getTreeArgument(
48
47
  }
49
48
 
50
49
  if (args.length === 0) {
51
- if (!scope) {
52
- // Should never happen because assertScopeIsDefined throws an exception.
50
+ if (!parent) {
51
+ // Should never happen because assertTreeIsDefined throws an exception.
53
52
  throw new Error(
54
- `${methodName} was called with no tree argument and no scope.`
53
+ `${methodName} was called with no tree argument and no parent.`
55
54
  );
56
55
  }
57
- return scope.get("@current");
56
+ return parent;
58
57
  }
59
58
 
60
59
  throw new Error(`${methodName}: The first argument was undefined.`);
@@ -62,7 +62,7 @@ export default async function constructResponse(request, resource) {
62
62
  !isStringLike(resource)
63
63
  ) {
64
64
  // The request is for a JSON or YAML result, and the resource we got isn't
65
- // yet a string or Buffer: convert the resource to JSON or YAML now.
65
+ // yet a string: convert the resource to JSON or YAML now.
66
66
  const tree = Tree.from(resource);
67
67
  resource =
68
68
  mediaType === "text/yaml"
@@ -103,7 +103,7 @@ export default async function constructResponse(request, resource) {
103
103
  if (!validResponse) {
104
104
  const typeName = body?.constructor?.name ?? typeof body;
105
105
  console.error(
106
- `A served tree must return a string or a TypedArray (such as a Buffer) but returned an instance of ${typeName}.`
106
+ `A served tree must return a string or a TypedArray but returned an instance of ${typeName}.`
107
107
  );
108
108
  return null;
109
109
  }
@@ -123,8 +123,8 @@ export default async function constructResponse(request, resource) {
123
123
  */
124
124
  function textOrObject(object) {
125
125
  if (object instanceof ArrayBuffer) {
126
- // Convert to Buffer.
127
- return Buffer.from(object);
126
+ // Convert to Uint8Array so we can write it to the Response.
127
+ return new Uint8Array(object);
128
128
  } else if (object instanceof TypedArray) {
129
129
  // Return typed arrays as is.
130
130
  return object;
@@ -1,5 +1,5 @@
1
1
  import { DeepObjectTree, Tree, keysFromPath } from "@weborigami/async-tree";
2
- import { Scope, formatError } from "@weborigami/language";
2
+ import { formatError } from "@weborigami/language";
3
3
  import { ServerResponse } from "node:http";
4
4
  import constructResponse from "./constructResponse.js";
5
5
 
@@ -53,15 +53,8 @@ function extendTreeScopeWithParams(tree, url) {
53
53
  const paramTree = new DeepObjectTree({
54
54
  "@params": params,
55
55
  });
56
-
57
- // Create a new scope that includes search parameter tree.
58
- const scope = Scope.getScope(tree);
59
- const extendedScope = new Scope(paramTree, scope);
60
-
61
- // Create a new tree that extends the prototype chain of the supplied tree.
62
- const extendedTree = Scope.treeWithScope(tree, extendedScope);
63
-
64
- return extendedTree;
56
+ paramTree.parent = tree;
57
+ return paramTree;
65
58
  }
66
59
 
67
60
  /**
@@ -1,22 +0,0 @@
1
- import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
2
- import scopeSet from "./set.js";
3
-
4
- /**
5
- * Return a copy of the given tree whose scope includes the given trees *and*
6
- * the current scope.
7
- *
8
- * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
9
- * @typedef {import("@weborigami/async-tree").Treelike} Treelike
10
- * @this {AsyncTree|null}
11
- * @param {Treelike} treelike
12
- * @param {...Treelike} scopeTrees
13
- * @this {AsyncTree|null}
14
- */
15
- export default function scopeExtend(treelike, ...scopeTrees) {
16
- assertScopeIsDefined(this, "scopeExtend");
17
- const scope = this;
18
- return scopeSet.call(scope, treelike, ...scopeTrees, scope);
19
- }
20
-
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";
@@ -1,25 +0,0 @@
1
- import { Tree } from "@weborigami/async-tree";
2
- import { Scope } from "@weborigami/language";
3
- import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
4
-
5
- /**
6
- * Returns the scope of the indicated tree or the current scope.
7
- *
8
- * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
9
- * @typedef {import("@weborigami/async-tree").Treelike} Treelike
10
- * @this {AsyncTree|null}
11
- * @param {any} [obj]
12
- */
13
- export default async function getScope(obj) {
14
- assertScopeIsDefined(this, "getScope");
15
- if (obj) {
16
- /** @type {any} */
17
- const tree = Tree.from(obj);
18
- return Scope.getScope(tree);
19
- } else {
20
- return this;
21
- }
22
- }
23
-
24
- getScope.usage = `@scope/get [<tree>]\tReturns the scope of the tree or the current scope`;
25
- getScope.documentation = "https://weborigami.org/cli/builtins.html#@scope";
@@ -1,22 +0,0 @@
1
- import { Scope } from "@weborigami/language";
2
- import { toFunction } from "../../common/utilities.js";
3
-
4
- /**
5
- * Invokes the given function in the context of the current scope.
6
- *
7
- * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
8
- * @typedef {import("../../../index.ts").Invocable} Invocable
9
- *
10
- * @this {AsyncTree|null}
11
- * @param {AsyncTree} context
12
- * @param {Invocable} invocable
13
- */
14
- export default async function invoke(context, invocable, ...args) {
15
- const scope = Scope.getScope(context);
16
- const fn = toFunction(invocable);
17
- const result = await fn.call(scope, ...args);
18
- return result;
19
- }
20
-
21
- invoke.usage = `@scope/invoke fn, <...args>\tInvoke the function in the current scope`;
22
- invoke.documentation = "https://weborigami.org/cli/builtins.html#@scope";
@@ -1,25 +0,0 @@
1
- import { Tree } from "@weborigami/async-tree";
2
- import { Scope } from "@weborigami/language";
3
- import { keySymbol } from "../../common/utilities.js";
4
- import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
5
-
6
- /**
7
- * Return a copy of the given tree that has the indicated trees as its scope.
8
- *
9
- * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
10
- * @typedef {import("@weborigami/async-tree").Treelike} Treelike
11
- * @param {Treelike} treelike
12
- * @param {...(Treelike|null)} scopeTrees
13
- * @this {AsyncTree|null}
14
- */
15
- export default function scopeSet(treelike, ...scopeTrees) {
16
- assertScopeIsDefined(this, "scopeSet");
17
- const tree = Tree.from(treelike);
18
- const scope = scopeTrees.length === 0 ? this : new Scope(...scopeTrees);
19
- const result = Scope.treeWithScope(tree, scope);
20
- result[keySymbol] = tree[keySymbol];
21
- return result;
22
- }
23
-
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";
@@ -1,23 +0,0 @@
1
- import { ObjectTree } from "@weborigami/async-tree";
2
- import { Scope } from "@weborigami/language";
3
-
4
- /**
5
- * A number of transforms accept functions that can accept a single value. This
6
- * helper adds the value and key to the scope as ambients.
7
- *
8
- *
9
- * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
10
- * @typedef {import("../../index.js").Invocable} Invocable
11
- *
12
- * @param {AsyncTree|null} scope
13
- * @param {any} value
14
- * @param {any} key
15
- */
16
- export default function addValueKeyToScope(scope, value, key) {
17
- // Add the key and value to the scope as ambients.
18
- const ambients = new ObjectTree({
19
- "@key": key,
20
- _: value,
21
- });
22
- return new Scope(ambients, scope);
23
- }
@@ -1,7 +0,0 @@
1
- export default function assertScopeIsDefined(scope, methodName) {
2
- if (scope === undefined) {
3
- throw new Error(
4
- `${methodName} must be called with a scope. If you don't want to pass a scope, invoke with: ${methodName}.call(null)`
5
- );
6
- }
7
- }