@weborigami/origami 0.6.17 → 0.7.0-beta.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 +13 -13
- package/src/cli/cli.js +10 -5
- package/src/common/serialize.js +3 -0
- package/src/dev/OriCommandTransform.js +3 -3
- package/src/dev/changes.js +15 -52
- package/src/dev/debug2/debug2.js +0 -34
- package/src/dev/debug2/debugChild.js +73 -43
- package/src/dev/debug2/debugCommands.js +1 -0
- package/src/dev/debug2/debugParent.js +30 -42
- package/src/dev/debug2/debugTransform.js +32 -20
- package/src/dev/debug2/expressionTree.js +10 -6
- package/src/dev/debug2/oriEval.js +2 -2
- package/src/dev/dev.js +1 -1
- package/src/dev/explore.js +1 -0
- package/src/dev/help.yaml +17 -11
- package/src/dev/syscache.js +56 -0
- package/src/handlers/xml_handler.js +1 -1
- package/src/origami/{domNodeToObject.js → domObject.js} +16 -4
- package/src/origami/fetch.js +1 -22
- package/src/origami/htmlDom.js +21 -0
- package/src/origami/htmlParse.js +6 -13
- package/src/origami/once.js +4 -1
- package/src/origami/ori.js +10 -11
- package/src/origami/origami.js +4 -1
- package/src/origami/volatile.js +1 -0
- package/src/origami/xmlDom.js +32 -0
- package/src/origami/xmlParse.js +6 -24
- package/src/server/constructResponse.js +47 -12
- package/src/server/server.js +69 -42
- package/src/dev/changes2.js +0 -38
- package/src/origami/project.js +0 -6
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { trailingSlash, Tree } from "@weborigami/async-tree";
|
|
2
|
-
import { evaluate,
|
|
2
|
+
import { evaluate, getGlobalsForTree } from "@weborigami/language";
|
|
3
3
|
import debugTransform from "./debugTransform.js";
|
|
4
4
|
|
|
5
5
|
const mapParentToResult = new WeakMap();
|
|
@@ -16,7 +16,7 @@ const mapParentToResult = new WeakMap();
|
|
|
16
16
|
* counter to force reevaluation of the same expression.
|
|
17
17
|
*/
|
|
18
18
|
export default async function oriEval(parent) {
|
|
19
|
-
const globals =
|
|
19
|
+
const globals = getGlobalsForTree(parent);
|
|
20
20
|
return async (key) => {
|
|
21
21
|
const normalizedKey = trailingSlash.remove(key);
|
|
22
22
|
let result = mapParentToResult.get(parent);
|
package/src/dev/dev.js
CHANGED
|
@@ -6,7 +6,6 @@ export { default as indexPage } from "../origami/indexPage.js";
|
|
|
6
6
|
export { default as yaml } from "../origami/yaml.js";
|
|
7
7
|
export { default as breakpoint } from "./breakpoint.js";
|
|
8
8
|
export { default as changes } from "./changes.js";
|
|
9
|
-
export { default as changes2 } from "./changes2.js";
|
|
10
9
|
export { default as code } from "./code.js";
|
|
11
10
|
export { default as copy } from "./copy.js";
|
|
12
11
|
export { default as audit } from "./crawler/audit.js";
|
|
@@ -19,5 +18,6 @@ export { default as log } from "./log.js";
|
|
|
19
18
|
export { default as serve } from "./serve.js";
|
|
20
19
|
export { default as stdin } from "./stdin.js";
|
|
21
20
|
export { default as svg } from "./svg.js";
|
|
21
|
+
export { default as syscache } from "./syscache.js";
|
|
22
22
|
export { default as version } from "./version.js";
|
|
23
23
|
export { default as watch } from "./watch.js";
|
package/src/dev/explore.js
CHANGED
|
@@ -44,6 +44,7 @@ async function getScopeData(scope) {
|
|
|
44
44
|
async function loadTemplate() {
|
|
45
45
|
const folderPath = path.resolve(fileURLToPath(import.meta.url), "..");
|
|
46
46
|
const folder = new OrigamiFileMap(folderPath);
|
|
47
|
+
await folder.initializeGlobals();
|
|
47
48
|
const templateFile = await folder.get("explore.ori");
|
|
48
49
|
const template = await Handlers.ori_handler.unpack(templateFile, {
|
|
49
50
|
parent: folder,
|
package/src/dev/help.yaml
CHANGED
|
@@ -13,12 +13,12 @@ Dev:
|
|
|
13
13
|
clear:
|
|
14
14
|
args: (tree)
|
|
15
15
|
description: Remove all values from the tree (alias of Tree.clear)
|
|
16
|
-
crawl:
|
|
17
|
-
args: (tree, base)
|
|
18
|
-
description: A tree of a site's discoverable resources
|
|
19
16
|
copy:
|
|
20
17
|
args: (source, target)
|
|
21
18
|
description: Copy the source tree to the target
|
|
19
|
+
crawl:
|
|
20
|
+
args: (tree, base)
|
|
21
|
+
description: A tree of a site's discoverable resources
|
|
22
22
|
debug:
|
|
23
23
|
args: (tree)
|
|
24
24
|
description: Add debug features to the tree
|
|
@@ -63,6 +63,9 @@ Origami:
|
|
|
63
63
|
document:
|
|
64
64
|
args: (text, [data])
|
|
65
65
|
description: Create a document object with the text and data
|
|
66
|
+
domObject:
|
|
67
|
+
args: (dom)
|
|
68
|
+
description: Convert a DOM tree to a plain JavaScript object
|
|
66
69
|
extension:
|
|
67
70
|
description: Helpers for working with file extensions
|
|
68
71
|
fetch:
|
|
@@ -71,12 +74,12 @@ Origami:
|
|
|
71
74
|
hash:
|
|
72
75
|
args: (data)
|
|
73
76
|
description: A hex string hash of the data
|
|
77
|
+
htmlDom:
|
|
78
|
+
args: (html)
|
|
79
|
+
description: Parse HTML into a DOM tree
|
|
74
80
|
htmlEscape:
|
|
75
81
|
args: (text)
|
|
76
82
|
description: Escape HTML entities in the text
|
|
77
|
-
htmlParse:
|
|
78
|
-
args: (html)
|
|
79
|
-
description: Parse HTML into a plain JavaScript object
|
|
80
83
|
image:
|
|
81
84
|
description: Collection of functions for working with images
|
|
82
85
|
indexPage:
|
|
@@ -102,9 +105,9 @@ Origami:
|
|
|
102
105
|
description: The outline structure of the markdown document
|
|
103
106
|
naturalOrder:
|
|
104
107
|
description: A comparison function for natural sort order
|
|
105
|
-
once:
|
|
106
|
-
|
|
107
|
-
|
|
108
|
+
# once:
|
|
109
|
+
# args: (fn)
|
|
110
|
+
# description: Run the function only once, return the same result
|
|
108
111
|
ori:
|
|
109
112
|
args: (text)
|
|
110
113
|
description: Evaluate the text as an Origami expression
|
|
@@ -148,12 +151,15 @@ Origami:
|
|
|
148
151
|
toFunction:
|
|
149
152
|
args: (obj)
|
|
150
153
|
description: Coerce a tree or packed function definition to a function
|
|
154
|
+
tsv:
|
|
155
|
+
args: (tree)
|
|
156
|
+
description: Render the tree as a TSV file
|
|
151
157
|
unpack:
|
|
152
158
|
args: (buffer)
|
|
153
159
|
description: Unpack the buffer into a usable form
|
|
154
|
-
|
|
160
|
+
xmlDom:
|
|
155
161
|
args: (xml)
|
|
156
|
-
description: Parse XML into a
|
|
162
|
+
description: Parse XML into a DOM tree
|
|
157
163
|
yaml:
|
|
158
164
|
args: (obj)
|
|
159
165
|
description: Render the object in YAML format
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { activeProjectRoot, systemCache, volatile } from "@weborigami/language";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import * as YAMLModule from "yaml";
|
|
4
|
+
|
|
5
|
+
// The "yaml" package doesn't seem to provide a default export that the browser can
|
|
6
|
+
// recognize, so we have to handle two ways to accommodate Node and the browser.
|
|
7
|
+
// @ts-ignore
|
|
8
|
+
const YAML = YAMLModule.default ?? YAMLModule.YAML;
|
|
9
|
+
|
|
10
|
+
export default function syscache() {
|
|
11
|
+
const projectRoot = activeProjectRoot.get();
|
|
12
|
+
|
|
13
|
+
/** @type {any} */
|
|
14
|
+
const entries = [...systemCache.entries()].map(([path, entry]) => {
|
|
15
|
+
const result = {};
|
|
16
|
+
if (entry.downstreams) {
|
|
17
|
+
result.downstreams = preferRelativePaths(projectRoot, entry.downstreams);
|
|
18
|
+
}
|
|
19
|
+
if (entry.upstreams) {
|
|
20
|
+
result.upstreams = preferRelativePaths(projectRoot, entry.upstreams);
|
|
21
|
+
}
|
|
22
|
+
return [preferRelativePath(projectRoot, path), result];
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Sort the entries by key
|
|
26
|
+
entries.sort(([keyA], [keyB]) => keyA.localeCompare(keyB));
|
|
27
|
+
|
|
28
|
+
const result = volatile(new Map(entries));
|
|
29
|
+
|
|
30
|
+
// When served, render as YAML and preserve trailing slashes
|
|
31
|
+
Object.defineProperty(result, "pack", {
|
|
32
|
+
configurable: true,
|
|
33
|
+
enumerable: false,
|
|
34
|
+
get() {
|
|
35
|
+
return () => volatile(YAML.stringify(result));
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function preferRelativePath(projectRoot, inputPath) {
|
|
43
|
+
if (!path.isAbsolute(inputPath)) {
|
|
44
|
+
return inputPath;
|
|
45
|
+
}
|
|
46
|
+
const prefix = `${projectRoot.path}${path.sep}`;
|
|
47
|
+
if (!inputPath.startsWith(prefix)) {
|
|
48
|
+
return inputPath;
|
|
49
|
+
}
|
|
50
|
+
const relativePath = inputPath.slice(prefix.length);
|
|
51
|
+
return relativePath;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function preferRelativePaths(projectRoot, paths) {
|
|
55
|
+
return Array.from(paths).map((path) => preferRelativePath(projectRoot, path));
|
|
56
|
+
}
|
|
@@ -1,17 +1,29 @@
|
|
|
1
|
+
import { isUnpackable } from "@weborigami/async-tree";
|
|
2
|
+
|
|
1
3
|
const ELEMENT_NODE = 1;
|
|
2
4
|
const TEXT_NODE = 3;
|
|
3
5
|
const CDATA_SECTION_NODE = 4;
|
|
4
6
|
const DOCUMENT_NODE = 9;
|
|
5
7
|
const DOCUMENT_FRAGMENT_NODE = 11;
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Given a DOM node, return a plain object representation of it.
|
|
11
|
+
*/
|
|
12
|
+
export default async function domObject(input) {
|
|
13
|
+
if (isUnpackable(input)) {
|
|
14
|
+
input = await input.unpack();
|
|
15
|
+
}
|
|
16
|
+
return nodeObject(input);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function nodeObject(node) {
|
|
8
20
|
switch (node.nodeType) {
|
|
9
21
|
case DOCUMENT_NODE:
|
|
10
22
|
return {
|
|
11
23
|
name: "#document",
|
|
12
24
|
children: [...node.childNodes]
|
|
13
25
|
.filter((child) => !isWhitespaceOnly(child))
|
|
14
|
-
.map(
|
|
26
|
+
.map(nodeObject),
|
|
15
27
|
};
|
|
16
28
|
|
|
17
29
|
case DOCUMENT_FRAGMENT_NODE:
|
|
@@ -19,7 +31,7 @@ export default function domNodeToObject(node) {
|
|
|
19
31
|
name: "#document-fragment",
|
|
20
32
|
children: [...node.childNodes]
|
|
21
33
|
.filter((child) => !isWhitespaceOnly(child))
|
|
22
|
-
.map(
|
|
34
|
+
.map(nodeObject),
|
|
23
35
|
};
|
|
24
36
|
|
|
25
37
|
case ELEMENT_NODE: {
|
|
@@ -55,7 +67,7 @@ export default function domNodeToObject(node) {
|
|
|
55
67
|
result.text = text;
|
|
56
68
|
}
|
|
57
69
|
} else if (relevantChildren.length > 0) {
|
|
58
|
-
result.children = relevantChildren.map(
|
|
70
|
+
result.children = relevantChildren.map(nodeObject);
|
|
59
71
|
}
|
|
60
72
|
|
|
61
73
|
return result;
|
package/src/origami/fetch.js
CHANGED
|
@@ -1,22 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { handleExtension } from "@weborigami/language";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Extend the JavaScript `fetch` function to implicity return an ArrayBuffer
|
|
6
|
-
* with an unpack() method if the resource has a known file extension.
|
|
7
|
-
*/
|
|
8
|
-
export default async function fetchBuiltin(resource, options, state) {
|
|
9
|
-
resource = args.string(resource, "Origami.fetch");
|
|
10
|
-
const response = await fetch(resource, options);
|
|
11
|
-
if (!response.ok) {
|
|
12
|
-
return undefined;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const value = await response.arrayBuffer();
|
|
16
|
-
|
|
17
|
-
const url = new URL(resource);
|
|
18
|
-
const key = url.pathname;
|
|
19
|
-
|
|
20
|
-
return handleExtension(value, key, state?.parent);
|
|
21
|
-
}
|
|
22
|
-
fetchBuiltin.needsState = true;
|
|
1
|
+
export { fetchAndHandleExtension as default } from "@weborigami/language";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { args } from "@weborigami/async-tree";
|
|
2
|
+
import loadJsDom from "../common/loadJsDom.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Return the DOM structure for the given HTML.
|
|
6
|
+
*
|
|
7
|
+
* @param {import("@weborigami/async-tree").Stringlike} html
|
|
8
|
+
*/
|
|
9
|
+
export default async function htmlDom(html) {
|
|
10
|
+
html = args.stringlike(html, "Origami.htmlDom");
|
|
11
|
+
const { JSDOM } = await loadJsDom();
|
|
12
|
+
let dom = JSDOM.fragment(html);
|
|
13
|
+
if (
|
|
14
|
+
(dom.nodeType === 9 || dom.nodeType === 11) &&
|
|
15
|
+
dom.children.length === 1
|
|
16
|
+
) {
|
|
17
|
+
// Document or DocumentFragment with a single child: return the child
|
|
18
|
+
dom = dom.children[0];
|
|
19
|
+
}
|
|
20
|
+
return dom;
|
|
21
|
+
}
|
package/src/origami/htmlParse.js
CHANGED
|
@@ -1,22 +1,15 @@
|
|
|
1
1
|
import { args } from "@weborigami/async-tree";
|
|
2
|
-
import
|
|
3
|
-
import domNodeToObject from "./domNodeToObject.js";
|
|
2
|
+
import htmlDom from "./htmlDom.js";
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
|
-
* Return the DOM structure for the given HTML
|
|
5
|
+
* Return the DOM structure for the given HTML.
|
|
7
6
|
*
|
|
8
7
|
* @param {import("@weborigami/async-tree").Stringlike} html
|
|
9
8
|
*/
|
|
10
9
|
export default async function htmlParse(html) {
|
|
10
|
+
console.warn("Origami.htmlParse is deprecated. Use Origami.htmlDom instead.");
|
|
11
|
+
|
|
11
12
|
html = args.stringlike(html, "Origami.htmlParse");
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
let object = domNodeToObject(dom);
|
|
15
|
-
if (
|
|
16
|
-
(object.name === "#document" || object.name === "#document-fragment") &&
|
|
17
|
-
object.children.length === 1
|
|
18
|
-
) {
|
|
19
|
-
object = object.children[0];
|
|
20
|
-
}
|
|
21
|
-
return object;
|
|
13
|
+
const dom = await htmlDom(html);
|
|
14
|
+
return dom;
|
|
22
15
|
}
|
package/src/origami/once.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { args } from "@weborigami/async-tree";
|
|
1
|
+
import { args, interop } from "@weborigami/async-tree";
|
|
2
2
|
|
|
3
3
|
const fnPromiseMap = new Map();
|
|
4
4
|
const codePromiseMap = new Map();
|
|
@@ -9,6 +9,9 @@ const codePromiseMap = new Map();
|
|
|
9
9
|
* @param {Function} fn
|
|
10
10
|
*/
|
|
11
11
|
export default async function once(fn) {
|
|
12
|
+
interop.warn(
|
|
13
|
+
`Now that Origami caches most results, Origami.once is no longer needed and will be removed in a future release. If you believe you have a need for it, please mention your use case in the Origami chat.`,
|
|
14
|
+
);
|
|
12
15
|
fn = args.fn(fn, "Origami.once");
|
|
13
16
|
const code = /** @type {any} */ (fn).code;
|
|
14
17
|
if (code) {
|
package/src/origami/ori.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
args,
|
|
3
|
+
assignPropertyDescriptors,
|
|
4
|
+
getRealmObjectPrototype,
|
|
5
|
+
Tree,
|
|
6
|
+
} from "@weborigami/async-tree";
|
|
7
|
+
import { compile, getGlobalsForTree } from "@weborigami/language";
|
|
3
8
|
import { toYaml } from "../common/serialize.js";
|
|
4
9
|
import * as dev from "../dev/dev.js";
|
|
5
10
|
|
|
@@ -17,18 +22,12 @@ export default async function ori(expression, options = {}) {
|
|
|
17
22
|
|
|
18
23
|
expression = args.stringlike(expression, "Origami.ori");
|
|
19
24
|
|
|
20
|
-
// Add Dev builtins as top-level globals
|
|
21
|
-
const globals = {
|
|
22
|
-
...(await projectGlobals(parent)),
|
|
23
|
-
...dev,
|
|
24
|
-
};
|
|
25
|
+
// Add Dev builtins as top-level globals; avoid invoking getters
|
|
26
|
+
const globals = assignPropertyDescriptors({}, getGlobalsForTree(parent), dev);
|
|
25
27
|
|
|
26
|
-
// Compile the expression
|
|
27
|
-
// passing a function to the `watch` builtin will always look the current
|
|
28
|
-
// value of things in scope.
|
|
28
|
+
// Compile the expression
|
|
29
29
|
const fn = compile.expression(expression, {
|
|
30
30
|
globals,
|
|
31
|
-
enableCaching: false,
|
|
32
31
|
mode: "shell",
|
|
33
32
|
parent,
|
|
34
33
|
});
|
package/src/origami/origami.js
CHANGED
|
@@ -3,8 +3,10 @@ export { default as help } from "../dev/help.js"; // Alias
|
|
|
3
3
|
export { default as basename } from "./basename.js";
|
|
4
4
|
export { default as csv } from "./csv.js";
|
|
5
5
|
export { default as document } from "./document.js";
|
|
6
|
+
export { default as domObject } from "./domObject.js";
|
|
6
7
|
export { default as fetch } from "./fetch.js";
|
|
7
8
|
export { default as hash } from "./hash.js";
|
|
9
|
+
export { default as htmlDom } from "./htmlDom.js";
|
|
8
10
|
export { default as htmlEscape } from "./htmlEscape.js";
|
|
9
11
|
export { default as htmlParse } from "./htmlParse.js";
|
|
10
12
|
export { default as format } from "./image/format.js";
|
|
@@ -22,7 +24,6 @@ export { default as once } from "./once.js";
|
|
|
22
24
|
export { default as ori } from "./ori.js";
|
|
23
25
|
export { default as pack } from "./pack.js";
|
|
24
26
|
export { default as post } from "./post.js";
|
|
25
|
-
export { default as project } from "./project.js";
|
|
26
27
|
export { default as projectRoot } from "./projectRoot.js";
|
|
27
28
|
export { default as randomFrom } from "./randomFrom.js";
|
|
28
29
|
export { default as randomsFrom } from "./randomsFrom.js";
|
|
@@ -37,6 +38,8 @@ export { default as static } from "./static.js";
|
|
|
37
38
|
export { default as string } from "./string.js";
|
|
38
39
|
export { default as tsv } from "./tsv.js";
|
|
39
40
|
export { default as unpack } from "./unpack.js";
|
|
41
|
+
export { default as volatile } from "./volatile.js";
|
|
42
|
+
export { default as xmlDom } from "./xmlDom.js";
|
|
40
43
|
export { default as xmlParse } from "./xmlParse.js";
|
|
41
44
|
export { default as yaml } from "./yaml.js";
|
|
42
45
|
export { default as yamlParse } from "./yamlParse.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { volatile as default } from "@weborigami/language";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { args } from "@weborigami/async-tree";
|
|
2
|
+
import loadJsDom from "../common/loadJsDom.js";
|
|
3
|
+
|
|
4
|
+
let parser;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Return the DOM for the given XML.
|
|
8
|
+
*
|
|
9
|
+
* @param {import("@weborigami/async-tree").Stringlike} xml
|
|
10
|
+
*/
|
|
11
|
+
export default async function xmlDom(xml) {
|
|
12
|
+
xml = args.stringlike(xml, "Origami.xmlDom");
|
|
13
|
+
const parser = await getParser();
|
|
14
|
+
let dom = parser.parseFromString(xml, "application/xml");
|
|
15
|
+
if (
|
|
16
|
+
(dom.nodeType === 9 || dom.nodeType === 11) &&
|
|
17
|
+
dom.children.length === 1
|
|
18
|
+
) {
|
|
19
|
+
// Document or DocumentFragment with a single child: return the child
|
|
20
|
+
dom = dom.children[0];
|
|
21
|
+
}
|
|
22
|
+
return dom;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function getParser() {
|
|
26
|
+
if (!parser) {
|
|
27
|
+
const { JSDOM } = await loadJsDom();
|
|
28
|
+
const dom = new JSDOM();
|
|
29
|
+
parser = new dom.window.DOMParser();
|
|
30
|
+
}
|
|
31
|
+
return parser;
|
|
32
|
+
}
|
package/src/origami/xmlParse.js
CHANGED
|
@@ -1,33 +1,15 @@
|
|
|
1
1
|
import { args } from "@weborigami/async-tree";
|
|
2
|
-
import
|
|
3
|
-
import domNodeToObject from "./domNodeToObject.js";
|
|
4
|
-
|
|
5
|
-
let parser;
|
|
2
|
+
import xmlDom from "./xmlDom.js";
|
|
6
3
|
|
|
7
4
|
/**
|
|
8
|
-
* Return the DOM for the given XML
|
|
5
|
+
* Return the DOM structure for the given XML.
|
|
9
6
|
*
|
|
10
7
|
* @param {import("@weborigami/async-tree").Stringlike} xml
|
|
11
8
|
*/
|
|
12
9
|
export default async function xmlParse(xml) {
|
|
13
|
-
|
|
14
|
-
const parser = await getParser();
|
|
15
|
-
const dom = parser.parseFromString(xml, "application/xml");
|
|
16
|
-
let object = domNodeToObject(dom);
|
|
17
|
-
if (
|
|
18
|
-
(object.name === "#document" || object.name === "#document-fragment") &&
|
|
19
|
-
object.children.length === 1
|
|
20
|
-
) {
|
|
21
|
-
object = object.children[0];
|
|
22
|
-
}
|
|
23
|
-
return object;
|
|
24
|
-
}
|
|
10
|
+
console.warn("Origami.xmlParse is deprecated. Use Origami.xmlDom instead.");
|
|
25
11
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const dom = new JSDOM();
|
|
30
|
-
parser = new dom.window.DOMParser();
|
|
31
|
-
}
|
|
32
|
-
return parser;
|
|
12
|
+
xml = args.stringlike(xml, "Origami.xmlParse");
|
|
13
|
+
const dom = await xmlDom(xml);
|
|
14
|
+
return dom;
|
|
33
15
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { extension, isPacked, toString, Tree } from "@weborigami/async-tree";
|
|
2
|
+
import { symbols } from "@weborigami/language";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
2
4
|
import { computedMIMEType } from "whatwg-mimetype";
|
|
3
5
|
import { mediaTypeForExtension } from "./mediaTypes.js";
|
|
4
6
|
|
|
@@ -19,6 +21,16 @@ export default async function constructResponse(request, resource) {
|
|
|
19
21
|
// Determine media type, what data we'll send, and encoding.
|
|
20
22
|
const url = new URL(request?.url ?? "", `https://${request?.headers.host}`);
|
|
21
23
|
|
|
24
|
+
if (!isPacked(resource) && typeof resource.pack === "function") {
|
|
25
|
+
resource = await resource.pack();
|
|
26
|
+
if (typeof resource === "function") {
|
|
27
|
+
resource = await resource();
|
|
28
|
+
}
|
|
29
|
+
if (resource instanceof Response) {
|
|
30
|
+
return resource;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
22
34
|
if (!url.pathname.endsWith("/") && Tree.isMaplike(resource)) {
|
|
23
35
|
// Maplike resource: redirect to its index page.
|
|
24
36
|
const Location = `${url.pathname}/`;
|
|
@@ -30,16 +42,6 @@ export default async function constructResponse(request, resource) {
|
|
|
30
42
|
});
|
|
31
43
|
}
|
|
32
44
|
|
|
33
|
-
if (!isPacked(resource) && typeof resource.pack === "function") {
|
|
34
|
-
resource = await resource.pack();
|
|
35
|
-
if (typeof resource === "function") {
|
|
36
|
-
resource = await resource();
|
|
37
|
-
}
|
|
38
|
-
if (resource instanceof Response) {
|
|
39
|
-
return resource;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
45
|
let body = resource;
|
|
44
46
|
if (!isPacked(resource)) {
|
|
45
47
|
// Can we treat it as text?
|
|
@@ -50,6 +52,7 @@ export default async function constructResponse(request, resource) {
|
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
// Determine MIME type
|
|
55
|
+
/** @type {string | undefined} */
|
|
53
56
|
let mediaType;
|
|
54
57
|
if (resource.mediaType) {
|
|
55
58
|
// Resource indicates its own media type.
|
|
@@ -77,6 +80,10 @@ export default async function constructResponse(request, resource) {
|
|
|
77
80
|
} else {
|
|
78
81
|
mediaType = sniffedType.toString();
|
|
79
82
|
}
|
|
83
|
+
if (mediaType === "text/plain") {
|
|
84
|
+
// Prefer UTF-8 encoding for text/plain
|
|
85
|
+
mediaType += "; charset=utf-8";
|
|
86
|
+
}
|
|
80
87
|
}
|
|
81
88
|
}
|
|
82
89
|
}
|
|
@@ -91,7 +98,35 @@ export default async function constructResponse(request, resource) {
|
|
|
91
98
|
);
|
|
92
99
|
}
|
|
93
100
|
|
|
94
|
-
|
|
95
|
-
|
|
101
|
+
// Compute ETag from the body content.
|
|
102
|
+
let etag;
|
|
103
|
+
if (!resource[symbols.volatileSymbol]) {
|
|
104
|
+
const hash = createHash("sha1");
|
|
105
|
+
if (typeof body === "string" || body instanceof String) {
|
|
106
|
+
hash.update(String(body), "utf8");
|
|
107
|
+
} else {
|
|
108
|
+
hash.update(body);
|
|
109
|
+
}
|
|
110
|
+
const digest = hash.digest("hex");
|
|
111
|
+
// Store ETag with quotes in cache to match If-None-Match header
|
|
112
|
+
etag = `"${digest}"`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** @type {Record<string, string>} */
|
|
116
|
+
const headers = {};
|
|
117
|
+
if (mediaType) {
|
|
118
|
+
headers["Content-Type"] = mediaType;
|
|
119
|
+
}
|
|
120
|
+
if (etag) {
|
|
121
|
+
headers["Cache-Control"] = "no-cache";
|
|
122
|
+
headers.ETag = etag;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const response = new Response(body, { headers });
|
|
126
|
+
|
|
127
|
+
if (resource[symbols.volatileSymbol]) {
|
|
128
|
+
response[symbols.volatileSymbol] = true;
|
|
129
|
+
}
|
|
130
|
+
|
|
96
131
|
return response;
|
|
97
132
|
}
|