@weborigami/origami 0.2.12 → 0.3.1

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@weborigami/origami",
3
- "version": "0.2.12",
3
+ "version": "0.3.1",
4
4
  "description": "Web Origami language, CLI, framework, and server",
5
5
  "type": "module",
6
6
  "repository": {
@@ -17,10 +17,10 @@
17
17
  "typescript": "5.8.2"
18
18
  },
19
19
  "dependencies": {
20
- "@weborigami/async-tree": "0.2.12",
21
- "@weborigami/language": "0.2.12",
20
+ "@weborigami/async-tree": "0.3.1",
21
+ "@weborigami/language": "0.3.1",
22
22
  "@weborigami/json-feed-to-rss": "1.0.0",
23
- "@weborigami/types": "0.2.12",
23
+ "@weborigami/types": "0.3.1",
24
24
  "exif-parser": "0.1.12",
25
25
  "graphviz-wasm": "3.0.2",
26
26
  "highlight.js": "11.11.1",
@@ -302,8 +302,8 @@ tree:
302
302
  args: (tree)
303
303
  description: The tree's [key, value] pairs
304
304
  filter:
305
- args: (source, filter)
306
- description: Filter the source tree using the filter tree
305
+ args: (source, options)
306
+ description: Filter the source tree
307
307
  first:
308
308
  args: (tree)
309
309
  description: The first value in the tree
@@ -352,6 +352,9 @@ tree:
352
352
  mapReduce:
353
353
  args: (tree, valueFn, reduceFn)
354
354
  description: Map values and reduce them
355
+ mask:
356
+ args: (source, mask)
357
+ description: Return the source tree with only the keys in the mask
355
358
  match:
356
359
  args: (pattern, fn, [keys])
357
360
  description: Matches simple patterns or regular expressions
@@ -12,11 +12,16 @@ import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
12
12
  */
13
13
  export default async function csv(object) {
14
14
  assertTreeIsDefined(this, "origami:csv");
15
+ object = object ?? this;
16
+ if (object === undefined) {
17
+ return undefined;
18
+ }
15
19
  if (isUnpackable(object)) {
16
20
  object = await object.unpack();
17
21
  }
18
22
  const value = await toPlainValue(object);
19
- const text = formatCsv(value);
23
+ const array = Array.isArray(value) ? value : Object.values(value);
24
+ const text = formatCsv(array);
20
25
  return text;
21
26
  }
22
27
 
@@ -2,7 +2,7 @@ import { filter } from "@weborigami/async-tree";
2
2
  import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
3
3
 
4
4
  /**
5
- * Apply a filter to a tree.
5
+ * Apply a filter to a tree
6
6
  *
7
7
  * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
8
8
  * @typedef {import("@weborigami/async-tree").Treelike} Treelike
package/src/tree/map.js CHANGED
@@ -1,13 +1,10 @@
1
1
  import {
2
- cachedKeyFunctions,
3
- extensionKeyFunctions,
4
2
  isPlainObject,
5
3
  isUnpackable,
6
4
  map as mapTransform,
7
5
  } from "@weborigami/async-tree";
8
6
  import getTreeArgument from "../common/getTreeArgument.js";
9
7
  import { toFunction } from "../common/utilities.js";
10
- import parseExtensions from "./parseExtensions.js";
11
8
 
12
9
  /**
13
10
  * Map a hierarchical tree of keys and values to a new tree of keys and values.
@@ -81,12 +78,6 @@ function extendedOptions(context, operation) {
81
78
  let keyFn = options.key;
82
79
  let inverseKeyFn = options.inverseKey;
83
80
 
84
- if (extension && (keyFn || inverseKeyFn)) {
85
- throw new TypeError(
86
- `map: You can't specify extensions and also a key or inverseKey function`
87
- );
88
- }
89
-
90
81
  if (valueFn) {
91
82
  // @ts-ignore
92
83
  valueFn = toFunction(valueFn);
@@ -94,36 +85,23 @@ function extendedOptions(context, operation) {
94
85
  // Origami builtins can be used as value functions.
95
86
  // @ts-ignore
96
87
  const bound = valueFn.bind(context);
88
+ // Transfer sidecar functions
97
89
  // @ts-ignore
98
90
  Object.assign(bound, valueFn);
99
91
  valueFn = bound;
100
92
  }
101
93
 
102
- if (extension) {
103
- // Generate key/inverseKey functions from the extension
104
- let { resultExtension, sourceExtension } = parseExtensions(extension);
105
- const keyFns = extensionKeyFunctions(sourceExtension, resultExtension);
106
- keyFn = keyFns.key;
107
- inverseKeyFn = keyFns.inverseKey;
108
- } else if (keyFn) {
109
- // Extend the key function to include a value parameter
110
- keyFn = extendKeyFn(keyFn);
111
- } else {
112
- // Use sidecar key/inverseKey functions if the valueFn defines them
113
- keyFn = /** @type {any} */ (valueFn)?.key;
114
- inverseKeyFn = /** @type {any} */ (valueFn)?.inverseKey;
115
- }
116
-
117
- if (keyFn && !inverseKeyFn) {
118
- // Only keyFn was provided, so we need to generate the inverseKeyFn
119
- const keyFns = cachedKeyFunctions(keyFn, deep);
120
- keyFn = keyFns.key;
121
- inverseKeyFn = keyFns.inverseKey;
94
+ if (!extension) {
95
+ if (keyFn) {
96
+ // Extend the key function to include a value parameter
97
+ keyFn = extendKeyFn(keyFn);
98
+ }
122
99
  }
123
100
 
124
101
  return {
125
102
  deep,
126
103
  description,
104
+ extension,
127
105
  inverseKey: inverseKeyFn,
128
106
  key: keyFn,
129
107
  needsSourceValue,
@@ -0,0 +1,19 @@
1
+ import { mask } from "@weborigami/async-tree";
2
+ import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
3
+
4
+ /**
5
+ * Apply a mask to a tree
6
+ *
7
+ * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
8
+ * @typedef {import("@weborigami/async-tree").Treelike} Treelike
9
+ *
10
+ * @this {AsyncTree|null}
11
+ * @param {Treelike} sourceTreelike
12
+ * @param {Treelike} maskTreelike
13
+ */
14
+ export default async function maskBuiltin(sourceTreelike, maskTreelike) {
15
+ assertTreeIsDefined(this, "tree:mask");
16
+ const result = mask(sourceTreelike, maskTreelike);
17
+ result.parent = this;
18
+ return result;
19
+ }
package/src/tree/tree.js CHANGED
@@ -21,6 +21,7 @@ export { default as inners } from "./inners.js";
21
21
  export { default as keys } from "./keys.js";
22
22
  export { default as length } from "./length.js";
23
23
  export { default as map } from "./map.js";
24
+ export { default as mask } from "./mask.js";
24
25
  export { default as match } from "./match.js";
25
26
  export { default as merge } from "./merge.js";
26
27
  export { default as paginate } from "./paginate.js";
@@ -1,44 +0,0 @@
1
- /**
2
- * Given a string specifying an extension or a mapping of one extension to another,
3
- * return the source and result extensions.
4
- *
5
- * Syntax:
6
- * .foo source and result extension are the same
7
- * .foo→.bar Unicode Rightwards Arrow
8
- * .foo→ Unicode Rightwards Arrow, no result extension
9
- * →.bar Unicode Rightwards Arrow, no source extension
10
- * .foo->.bar hyphen and greater-than sign
11
- *
12
- * @param {string} specifier
13
- */
14
- export default function parseExtensions(specifier) {
15
- const lowercase = specifier?.toLowerCase() ?? "";
16
- const extensionRegex =
17
- /^((?<sourceExtension>\/|\.\S*)?\s*(→|->)\s*(?<resultExtension>\/|\.\S*)?)|(?<extension>\/|\.\S*)$/;
18
- const match = lowercase.match(extensionRegex);
19
- if (!match) {
20
- throw new Error(`Invalid file extension specifier "${specifier}".`);
21
- }
22
-
23
- // @ts-ignore
24
- let { extension, resultExtension, sourceExtension } = match.groups;
25
- if (extension) {
26
- // foo
27
- return {
28
- resultExtension: extension,
29
- sourceExtension: extension,
30
- };
31
- } else {
32
- // foo→bar
33
-
34
- if (resultExtension === undefined && sourceExtension === undefined) {
35
- throw new Error(
36
- `A file extension mapping must indicate a source or result extension: "${specifier}".`
37
- );
38
- }
39
-
40
- resultExtension ??= "";
41
- sourceExtension ??= "";
42
- return { resultExtension, sourceExtension };
43
- }
44
- }