run npm install to generate a package lock

This commit is contained in:
sashinexists
2024-12-07 13:18:31 +11:00
parent e7d08a91b5
commit 23437d228e
2501 changed files with 290663 additions and 0 deletions

21
node_modules/@weborigami/origami/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Jan Miksovsky and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

3
node_modules/@weborigami/origami/ReadMe.md generated vendored Normal file
View File

@@ -0,0 +1,3 @@
# Origami package
This folder contains the files published as the high-level `@weborigami/origami` package. It contains common types of useful trees, the `ori` CLI, a templating system for creating text output, and more.

22
node_modules/@weborigami/origami/index.ts generated vendored Normal file
View File

@@ -0,0 +1,22 @@
/**
* Tree Origami is a JavaScript project, but we use TypeScript as an internal
* tool to confirm our code is type safe.
*/
import { Treelike, Unpackable } from "@weborigami/async-tree";
/**
* A class constructor is an object with a `new` method that returns an
* instance of the indicated type.
*/
export type Constructor<T> = new (...args: any[]) => T;
export type Invocable = Function | Unpackable<Function|Treelike> | Treelike;
export interface JsonObject {
[key: string]: JsonValue;
}
export type JsonValue = boolean | number | string | Date | JsonObject | JsonValue[] | null;
export type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array | BigInt64Array | BigUint64Array;

12
node_modules/@weborigami/origami/main.js generated vendored Normal file
View File

@@ -0,0 +1,12 @@
export * from "./src/calc/calc.js";
export { default as documentObject } from "./src/common/documentObject.js";
export { toString } from "./src/common/utilities.js";
export * from "./src/dev/dev.js";
export * from "./src/handlers/handlerExports.js";
export * from "./src/handlers/handlers.js";
export * from "./src/image/image.js";
export { builtinsTree } from "./src/internal.js";
export * from "./src/origami/origami.js";
export * from "./src/site/site.js";
export * from "./src/text/text.js";
export * from "./src/tree/tree.js";

37
node_modules/@weborigami/origami/package.json generated vendored Normal file
View File

@@ -0,0 +1,37 @@
{
"name": "@weborigami/origami",
"version": "0.2.1",
"description": "Web Origami language, CLI, framework, and server",
"type": "module",
"repository": {
"type": "git",
"url": "git://github.com/WebOrigami/origami.git"
},
"bin": {
"ori": "src/cli/cli.js"
},
"main": "./main.js",
"types": "./index.ts",
"devDependencies": {
"@types/node": "22.7.4",
"typescript": "5.6.2"
},
"dependencies": {
"@weborigami/async-tree": "0.2.1",
"@weborigami/language": "0.2.1",
"@weborigami/types": "0.2.1",
"exif-parser": "0.1.12",
"graphviz-wasm": "3.0.2",
"highlight.js": "11.10.0",
"marked": "14.1.2",
"marked-gfm-heading-id": "4.1.0",
"marked-highlight": "2.1.4",
"marked-smartypants": "1.1.8",
"sharp": "0.33.5",
"yaml": "2.5.1"
},
"scripts": {
"test": "node --test --test-reporter=spec",
"typecheck": "node node_modules/typescript/bin/tsc"
}
}

62
node_modules/@weborigami/origami/src/builtins.js generated vendored Normal file
View File

@@ -0,0 +1,62 @@
import * as calc from "./calc/calc.js";
import deprecated from "./deprecated.js";
import * as dev from "./dev/dev.js";
import * as handlers from "./handlers/handlers.js";
import help from "./help/help.js";
import * as image from "./image/image.js";
import js from "./js.js";
import node from "./node.js";
import * as origami from "./origami/origami.js";
import explore from "./protocols/explore.js";
import files from "./protocols/files.js";
import http from "./protocols/http.js";
import https from "./protocols/https.js";
import httpstree from "./protocols/httpstree.js";
import httptree from "./protocols/httptree.js";
import inherited from "./protocols/inherited.js";
import instantiate from "./protocols/new.js";
import packageNamespace from "./protocols/package.js";
import scope from "./protocols/scope.js";
import * as site from "./site/site.js";
import * as text from "./text/text.js";
import * as tree from "./tree/tree.js";
/** @type {any} */
export default {
"calc:": adjustReservedWords(calc),
"dev:": dev,
"explore:": explore,
"files:": files,
"help:": help,
"http:": http,
"https:": https,
"httpstree:": httpstree,
"httptree:": httptree,
"image:": image,
"inherited:": inherited,
"js:": js,
"new:": instantiate,
"node:": node,
"origami:": origami,
"package:": packageNamespace,
"scope:": scope,
"site:": adjustReservedWords(site),
"text:": text,
"tree:": tree,
// Some builtins need to be exposed at top level
...handlers.default,
// Deprecated builtins
...deprecated,
};
// Handle cases where a builtin name conflicts with a JS reserved word
function adjustReservedWords(obj) {
const result = {};
for (const [key, value] of Object.entries(obj)) {
const name = value.key ?? key;
result[name] = value;
}
return result;
}

36
node_modules/@weborigami/origami/src/builtinsTree.js generated vendored Normal file
View File

@@ -0,0 +1,36 @@
import { trailingSlash } from "@weborigami/async-tree";
import builtins from "./builtins.js";
const expanded = { ...builtins };
// For all builtins like `tree:keys`, add a shorthand `keys`.
for (const [key, value] of Object.entries(expanded)) {
const isNamespace = key.endsWith(":");
if (isNamespace) {
for (const [subKey, subValue] of Object.entries(value)) {
// HACK: Skip description keys until we can make them all non-enumerable.
if (subKey === "description") {
continue;
}
if (subKey in expanded) {
throw new Error(`Internal Origami error: Duplicate key: ${subKey}`);
}
expanded[subKey] = subValue;
}
}
}
// We create our own tree instead of using ObjectTree, since that binds the
// functions would be bound to the object. We want to leave them unbound.
class BuiltinsTree {
async get(key) {
const normalizedKey = trailingSlash.remove(key);
return expanded[normalizedKey];
}
async keys() {
return Object.keys(expanded);
}
}
export default new BuiltinsTree();

81
node_modules/@weborigami/origami/src/calc/calc.js generated vendored Normal file
View File

@@ -0,0 +1,81 @@
import { Tree } from "@weborigami/async-tree";
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
export function add(...args) {
console.warn(`Warning: "add" is deprecated. Use the "+" operator instead.`);
const numbers = args.map((arg) => Number(arg));
return numbers.reduce((acc, val) => acc + val, 0);
}
export function and(...args) {
console.warn(`Warning: "and" is deprecated. Use the "&&" operator instead.`);
return args.every((arg) => arg);
}
export function divide(a, b) {
console.warn(
`Warning: "divide" is deprecated. Use the "/" operator instead.`
);
return Number(a) / Number(b);
}
export function equals(a, b) {
console.warn(
`Warning: "equals" is deprecated. Use the "===" operator instead.`
);
return a === b;
}
/**
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
*
* @this {AsyncTree|null}
* @param {any} value
* @param {any} trueResult
* @param {any} [falseResult]
*/
export async function ifBuiltin(value, trueResult, falseResult) {
console.warn(
`Warning: "if" is deprecated. Use the conditional "a ? b : c" operator instead.`
);
assertTreeIsDefined(this, "calc:if");
let condition = await value;
if (Tree.isAsyncTree(condition)) {
const keys = Array.from(await condition.keys());
condition = keys.length > 0;
}
// 0 is true, null/undefined/false is false
let result = condition || condition === 0 ? trueResult : falseResult;
if (typeof result === "function") {
result = await result.call(this);
}
return result;
}
ifBuiltin.key = "if";
export function multiply(...args) {
console.warn(
`Warning: "multiply" is deprecated. Use the "*" operator instead.`
);
const numbers = args.map((arg) => Number(arg));
return numbers.reduce((acc, val) => acc * val, 1);
}
export function not(value) {
console.warn(`Warning: "not" is deprecated. Use the "!" operator instead.`);
return !value;
}
export function or(...args) {
console.warn(`Warning: "or" is deprecated. Use the "||" operator instead.`);
return args.find((arg) => arg);
}
export function subtract(a, b) {
console.warn(
`Warning: "subtract" is deprecated. Use the "-" operator instead.`
);
return Number(a) - Number(b);
}

69
node_modules/@weborigami/origami/src/cli/cli.js generated vendored Executable file
View File

@@ -0,0 +1,69 @@
#!/usr/bin/env node
import { Tree } from "@weborigami/async-tree";
import { formatError } from "@weborigami/language";
import path from "node:path";
import process, { stdout } from "node:process";
import help from "../help/help.js";
import ori from "../origami/ori.js";
import project from "../origami/project.js";
const TypedArray = Object.getPrototypeOf(Uint8Array);
async function main(...args) {
const expression = args.join(" ");
// Find the project root.
const projectTree = await project.call(null);
// If no arguments were passed, show usage.
if (!expression) {
// HACK: the config is the parent of the project tree.
const config = projectTree.parent;
const usage = await help.call(config);
console.log(usage);
return;
}
// Traverse from the project root to the current directory.
const currentDirectory = process.cwd();
const relative = path.relative(projectTree.path, currentDirectory);
const tree = await Tree.traversePath(projectTree, relative);
const result = await ori.call(tree, expression);
if (result !== undefined) {
const output =
result instanceof ArrayBuffer
? new Uint8Array(result)
: typeof result === "string" || result instanceof TypedArray
? result
: String(result);
await stdout.write(output);
// If stdout points to the console, and the result didn't end in a newline,
// then output a newline.
if (stdout.isTTY) {
const lastChar = output[output.length - 1];
const isNewLine = lastChar === "\n" || lastChar === 10;
if (!isNewLine) {
await stdout.write("\n");
}
}
}
}
// Process command line arguments
const args = process.argv;
args.shift(); // "node"
args.shift(); // name of this script file
// Not sure why we end up with blank arguments; skip them.
while (args[0] === "") {
args.shift();
}
try {
await main(...args);
} catch (/** @type {any} */ error) {
console.error(formatError(error));
process.exitCode = 1;
}

View File

@@ -0,0 +1,16 @@
import path from "node:path";
import process from "node:process";
import { pathToFileURL } from "node:url";
export default async function defaultModuleExport(...keys) {
// On Windows, absolute paths must be valid file:// URLs.
const modulePath = path.resolve(process.cwd(), ...keys);
const url = pathToFileURL(modulePath);
const module = await import(url.href);
const result = module.default;
if (!result) {
console.error(`${modulePath} does not define a default export.`);
return;
}
return result;
}

View File

@@ -0,0 +1,18 @@
/**
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
* @implements {AsyncTree}
*/
export default class ConstantTree {
constructor(value) {
this.value = value;
this.parent = null;
}
async get(key) {
return this.value;
}
async keys() {
return [];
}
}

View File

@@ -0,0 +1 @@
export default function assertTreeIsDefined(tree: any, methodName: string): asserts tree is object

View File

@@ -0,0 +1,10 @@
import { Tree } from "@weborigami/async-tree";
export default function assertTreeIsDefined(tree, methodName) {
const isValid = tree === null || Tree.isAsyncTree(tree);
if (!isValid) {
throw new Error(
`${methodName} must be called with a tree target. If you don't want to pass a tree, invoke with: ${methodName}.call(null)`
);
}
}

View File

@@ -0,0 +1,20 @@
import { pathFromKeys } from "@weborigami/async-tree";
/**
* Given a protocol, a host, and a list of keys, construct an href.
*
* @param {string} protocol
* @param {string} host
* @param {string[]} keys
*/
export default function constructHref(protocol, host, ...keys) {
const path = pathFromKeys(keys);
let href = [host, path].join("/");
if (!href.startsWith(protocol)) {
if (!href.startsWith("//")) {
href = `//${href}`;
}
href = `${protocol}${href}`;
}
return href;
}

View File

@@ -0,0 +1,34 @@
import { trailingSlash } from "@weborigami/async-tree";
import { HandleExtensionsTransform } from "@weborigami/language";
import constructHref from "./constructHref.js";
/**
* Given a protocol, a host, and a list of keys, construct an href.
*
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
*
* @param {string} protocol
* @param {import("../../index.ts").Constructor<AsyncTree>} treeClass
* @param {AsyncTree|null} parent
* @param {string} host
* @param {string[]} keys
*/
export default function constructSiteTree(
protocol,
treeClass,
parent,
host,
...keys
) {
// If the last key doesn't end in a slash, remove it for now.
let lastKey;
if (keys.length > 0 && keys.at(-1) && !trailingSlash.has(keys.at(-1))) {
lastKey = keys.pop();
}
const href = constructHref(protocol, host, ...keys);
let result = new (HandleExtensionsTransform(treeClass))(href);
result.parent = parent;
return lastKey ? result.get(lastKey) : result;
}

View File

@@ -0,0 +1,42 @@
import { isPlainObject, isUnpackable, toString } from "@weborigami/async-tree";
// import txtHandler from "../builtins/txt.handler.js";
/**
* In Origami, a text document object is any object with a `@text` property and
* a pack() method that formats that object as text with YAML front matter. This
* function is a helper for constructing such text document objects.
*
* @typedef {import("@weborigami/async-tree").StringLike} StringLike
* @typedef {import("@weborigami/async-tree").PlainObject} PlainObject
*
* @param {StringLike|PlainObject} input
* @param {any} [data]
*/
export default async function documentObject(input, data) {
let text;
let inputData;
if (isUnpackable(input)) {
// Unpack the input first, might already be a document object.
input = await input.unpack();
}
if (isPlainObject(input)) {
text = input["@text"];
inputData = input;
} else {
text = toString(input);
inputData = null;
}
// TODO: Either restore this code, or move responsibility for packing a
// document to HandleExtensionsTransform set().
// const base = {
// pack() {
// return txtHandler.pack(this);
// },
// };
// const result = Object.create(base);
const result = {};
Object.assign(result, inputData, data, { "@text": text });
return result;
}

View File

@@ -0,0 +1,26 @@
import { handleExtension } from "@weborigami/language";
/**
* Fetch the resource at the given href.
*
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
*
* @this {AsyncTree|null}
* @param {string} href
*/
export default async function fetchAndHandleExtension(href) {
const response = await fetch(href);
if (!response.ok) {
return undefined;
}
let buffer = await response.arrayBuffer();
// Attach any loader defined for the file type.
const url = new URL(href);
const filename = url.pathname.split("/").pop();
if (this && filename) {
buffer = await handleExtension(this, buffer, filename);
}
return buffer;
}

View File

@@ -0,0 +1,67 @@
import { Tree, isUnpackable } from "@weborigami/async-tree";
import assertTreeIsDefined from "./assertTreeIsDefined.js";
/**
* Many Origami built-in functions accept an optional treelike object as their
* first argument. If no tree is supplied, then the current context for the
* Origami command is used as the tree.
*
* So the argument is optional -- but if supplied, it must be defined. The
* caller should pass its `arguments` object to this function so that the actual
* number of supplied arguments can be checked.
*
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
*
* @param {AsyncTree|null} parent
* @param {IArguments} args
* @param {Treelike|undefined} treelike
* @param {string} methodName
* @param {boolean} [deep]
* @returns {Promise<AsyncTree>}
*/
export default async function getTreeArgument(
parent,
args,
treelike,
methodName,
deep
) {
assertTreeIsDefined(parent, methodName);
if (treelike !== undefined) {
if (isUnpackable(treelike)) {
treelike = await treelike.unpack();
}
if (Tree.isTreelike(treelike)) {
const options = deep !== undefined ? { deep } : undefined;
let tree = Tree.from(treelike, options);
// If the tree was created from a treelike object and does not yet have a
// parent, make the current tree its parent.
if (!tree.parent && parent !== undefined) {
if (parent !== null && !Tree.isAsyncTree(parent)) {
throw new Error(
`The parent argument passed to ${methodName} must be a tree.`
);
}
tree.parent = parent;
}
return tree;
}
throw new Error(
`The first argument to ${methodName} must be a tree, like an array, object, or files.`
);
}
if (args.length === 0) {
if (!parent) {
// Should never happen because assertTreeIsDefined throws an exception.
throw new Error(
`${methodName} was called with no tree argument and no parent.`
);
}
return parent;
}
throw new Error(`The first argument to ${methodName} was undefined.`);
}

View File

@@ -0,0 +1,38 @@
import { symbols, Tree } from "@weborigami/async-tree";
import { builtinsTree } from "../internal.js";
/**
* Perform any necessary post-processing on the unpacked content of a file. This
* lets treat the contents of various file types consistently.
*
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
*
* @param {any} content
* @param {AsyncTree|null} parent
* @returns
*/
export default function processUnpackedContent(content, parent) {
if (typeof content === "function") {
// Bind the function to the parent as the `this` context.
const target = parent ?? builtinsTree;
const result = content.bind(target);
if (content.code) {
result.code = content.code;
}
return result;
} else if (Tree.isAsyncTree(content) && !content.parent) {
const result = Object.create(content);
result.parent = parent;
return result;
} else if (Object.isExtensible(content) && !content[symbols.parent]) {
Object.defineProperty(content, symbols.parent, {
configurable: true,
enumerable: false,
value: parent,
writable: true,
});
return content;
} else {
return content;
}
}

View File

@@ -0,0 +1,7 @@
import type { AsyncTree } from "@weborigami/types";
import type { JsonValue } from "../../index.ts";
export function evaluateYaml(text: string, parent?: AsyncTree|null): Promise<JsonValue>;
export function parseYaml(text: string): JsonValue|AsyncTree;
export function toJson(obj: JsonValue | AsyncTree): Promise<string>;
export function toYaml(obj: JsonValue | AsyncTree): Promise<string>;

View File

@@ -0,0 +1,58 @@
/**
* @typedef {import("../../index.ts").JsonValue} JsonValue
* @typedef {import("@weborigami/async-tree").PlainObject} PlainObject
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
*/
import { Tree, toPlainValue } from "@weborigami/async-tree";
import * as YAMLModule from "yaml";
// The "yaml" package doesn't seem to provide a default export that the browser can
// recognize, so we have to handle two ways to accommodate Node and the browser.
// @ts-ignore
const YAML = YAMLModule.default ?? YAMLModule.YAML;
/**
*
* @param {string} text
* @param {AsyncTree|null} [parent]
*/
export async function evaluateYaml(text, parent) {
const data = parseYaml(String(text));
if (Tree.isAsyncTree(data)) {
data.parent = parent;
return Tree.plain(data);
} else {
return data;
}
}
/**
* @param {string} text
* @returns {JsonValue|AsyncTree}
*/
export function parseYaml(text) {
return YAML.parse(text);
}
/**
* Serializes an object as a JSON string.
*
* @param {any} obj
*/
export async function toJson(obj) {
const serializable = await toPlainValue(obj);
return JSON.stringify(serializable, null, 2);
}
/**
* Serializes an object as a JSON string.
*
* @param {any} obj
* @returns {Promise<string>}
*/
export async function toYaml(obj) {
const serializable = await toPlainValue(obj);
return YAML.stringify(serializable);
}

View File

@@ -0,0 +1,7 @@
export function getDescriptor(object: any): string;
export function hasNonPrintableCharacters(text: string): boolean;
export function isTransformApplied(Transform: Function, object: any): boolean;
export function toFunction(object: any): Function;
export function toString(object: any): string|null;
export function transformObject(Transform: Function, object: any): any;

View File

@@ -0,0 +1,144 @@
import {
Tree,
toString as asyncTreeToString,
isPlainObject,
isUnpackable,
trailingSlash,
} from "@weborigami/async-tree";
import { symbols } from "@weborigami/language";
import { basename } from "node:path";
// For a given tree, return some user-friendly descriptor
export function getDescriptor(tree) {
if ("path" in tree) {
return trailingSlash.add(basename(tree.path));
}
const source = tree[symbols.sourceSymbol];
if (source) {
// If the source looks like an identifier, use that.
// TODO: Use real identifier parsing.
const identifierRegex = /^[A-Za-z0-9_\-\.]+\/?$/;
if (identifierRegex.test(source)) {
return trailingSlash.add(source);
}
}
return null;
}
// Return true if the text appears to contain non-printable binary characters;
// used to infer whether a file is binary or text.
export function hasNonPrintableCharacters(text) {
// https://stackoverflow.com/a/1677660/76472
return /[\x00-\x08\x0E-\x1F]/.test(text);
}
export function isTransformApplied(Transform, obj) {
let transformName = Transform.name;
if (!transformName) {
throw `isTransformApplied was called on an unnamed transform function, but a name is required.`;
}
if (transformName.endsWith("Transform")) {
transformName = transformName.slice(0, -9);
}
// Walk up prototype chain looking for a constructor with the same name as the
// transform. This is not a great test.
for (let proto = obj; proto; proto = Object.getPrototypeOf(proto)) {
if (proto.constructor.name === transformName) {
return true;
}
}
return false;
}
/**
* Convert the given object to a function.
*
* @typedef {import("../../index.ts").Invocable} Invocable
* @param {any} obj
* @returns {Function|null}
*/
export function toFunction(obj) {
if (typeof obj === "function") {
// Return a function as is.
return obj;
} else if (isUnpackable(obj)) {
// Extract the contents of the object and convert that to a function.
let fnPromise;
/** @this {any} */
return async function (...args) {
if (!fnPromise) {
// unpack() may return a function or a promise for a function; normalize
// to a promise for a function
const unpackPromise = Promise.resolve(obj.unpack());
fnPromise = unpackPromise.then((content) => toFunction(content));
}
const fn = await fnPromise;
return fn.call(this, ...args);
};
} else if (Tree.isTreelike(obj)) {
// Return a function that invokes the tree's getter.
return Tree.toFunction(obj);
} else {
// Not a function
return null;
}
}
/**
* Extend the async-tree toString method: objects that have a `@text` property
* will return the value of that property as a string.
*
* @param {any} object
* @returns {string|null}
*/
export function toString(object) {
if (isPlainObject(object) && "@text" in object) {
object = object["@text"];
}
return asyncTreeToString(object);
}
/**
* Apply a functional class mixin to an individual object instance.
*
* This works by create an intermediate class, creating an instance of that, and
* then setting the intermediate class's prototype to the given individual
* object. The resulting, extended object is then returned.
*
* This manipulation of the prototype chain is generally sound in JavaScript,
* with some caveats. In particular, the original object class cannot make
* direct use of private members; JavaScript will complain if the extended
* object does anything that requires access to those private members.
*
* @param {Function} Transform
* @param {any} obj
*/
export function transformObject(Transform, obj) {
// Apply the mixin to Object and instantiate that. The Object base class here
// is going to be cut out of the prototype chain in a moment; we just use
// Object as a convenience because its constructor takes no arguments.
const mixed = new (Transform(Object))();
// Find the highest prototype in the chain that was added by the class mixin.
// The mixin may have added multiple prototypes to the chain. Walk up the
// prototype chain until we hit Object.
let mixinProto = Object.getPrototypeOf(mixed);
while (Object.getPrototypeOf(mixinProto) !== Object.prototype) {
mixinProto = Object.getPrototypeOf(mixinProto);
}
// Redirect the prototype chain above the mixin to point to the original
// object. The mixed object now extends the original object with the mixin.
Object.setPrototypeOf(mixinProto, obj);
// Create a new constructor for this mixed object that reflects its prototype
// chain. Because we've already got the instance we want, we won't use this
// constructor now, but this can be used later to instantiate other objects
// that look like the mixed one.
mixed.constructor = Transform(obj.constructor);
// Return the mixed object.
return mixed;
}

140
node_modules/@weborigami/origami/src/deprecated.js generated vendored Normal file
View File

@@ -0,0 +1,140 @@
import { Tree } from "@weborigami/async-tree";
import * as calc from "./calc/calc.js";
import * as dev from "./dev/dev.js";
import * as image from "./image/image.js";
import js from "./js.js";
import node from "./node.js";
import * as origami from "./origami/origami.js";
import files from "./protocols/files.js";
import * as site from "./site/site.js";
import * as text from "./text/text.js";
import * as tree from "./tree/tree.js";
const warningsDisplayedForKeys = new Set();
export function command(namespace, newKey, oldKey, fn) {
const wrappedFn = function (...args) {
const keys = newKey
? `"${namespace}${newKey}" or just "${newKey}"`
: `"${namespace}"`;
if (!warningsDisplayedForKeys.has(oldKey)) {
console.warn(
`ori: Warning: "${oldKey}" is deprecated. Use ${keys} instead.`
);
warningsDisplayedForKeys.add(oldKey);
}
return fn instanceof Function
? // @ts-ignore
fn.call(this, ...args)
: Tree.traverseOrThrow(fn, ...args);
};
if (fn.key) {
wrappedFn.key = fn.key;
}
if (fn.inverseKey) {
wrappedFn.inverseKey = fn.inverseKey;
}
return wrappedFn;
}
export function commands(namespace, object) {
const deprecatedEntries = Object.entries(object).map(([key, fn]) => [
`@${fn.key ?? key}`,
command(namespace, fn.key ?? key, `@${fn.key ?? key}`, fn),
]);
return Object.fromEntries(deprecatedEntries);
}
export default {
...commands("calc:", calc),
...commands("dev:", dev),
"@false": command("js:", "false", "@false", js.false),
"@fetch": command("js:", "fetch", "@fetch", js.fetch),
"@files": command("files:", null, "@files/", files),
"@image": command("image:", null, "@image/", image),
"@js": command("js:", null, "@js/", js),
"@math": command("calc:", null, "@math/", calc),
"@mdHtml": command("text:", "mdHtml", "@mdHtml", text.mdHtml),
"@node": command("node:", null, "@node/", node),
...commands("origami:", origami),
...commands("site:", site),
...commands("text:", text),
...commands("tree:", tree),
"@tree": command("tree:", null, "@tree/", Tree),
"@true": command("js:", "true", "@true", js.true),
// Renamed commands
"@clean": command("tree:", "clear", "@clean", tree.clear),
// Deprecated commands
"@deepTakeFn": command(
"tree:",
"deepTake",
"@deepTakeFn",
(options) =>
/** @this {any} */
function (treelike) {
return tree.deepTake.call(this, treelike, options);
}
),
"@deepMapFn": command(
"tree:",
"deepMap",
"@deepMapFn",
(options) =>
/** @this {any} */
function (treelike) {
return tree.deepMap.call(this, treelike, options);
}
),
"@groupFn": command(
"tree:",
"group",
"@groupFn",
(options) =>
/** @this {any} */
function (treelike) {
return tree.group.call(this, treelike, options);
}
),
"@mapFn": command(
"tree:",
"map",
"@mapFn",
(options) =>
/** @this {any} */
function (treelike) {
return tree.map.call(this, treelike, options);
}
),
"@paginateFn": command(
"tree:",
"paginate",
"@paginateFn",
(options) =>
/** @this {any} */
function (treelike) {
return tree.paginate.call(this, treelike, options);
}
),
"@sortFn": command(
"tree:",
"sort",
"@sortFn",
(options) =>
/** @this {any} */
function (treelike) {
return tree.sort.call(this, treelike, options);
}
),
"@takeFn": command(
"tree:",
"take",
"@takeFn",
(options) =>
/** @this {any} */
function (treelike) {
return tree.take.call(this, treelike, options);
}
),
};

View File

@@ -0,0 +1,5 @@
import { Mixin } from "@weborigami/language";
declare const ExplorableSiteTransform: Mixin<{}>;
export default ExplorableSiteTransform;

View File

@@ -0,0 +1,78 @@
import { Tree, jsonKeys } from "@weborigami/async-tree";
import { isTransformApplied, transformObject } from "../common/utilities.js";
import index from "../site/index.js";
/**
* Wraps a tree (typically a SiteTree) to turn a standard site into an
* explorable site.
*
* An explorable site follows three conventions:
* 1. if route /foo has any resources beneath it (/foo/bar.jpg), then /foo
* redirects to /foo/
* 2. /foo/ is a synonym for foo/index.html
* 3. /foo/.keys.json returns the public keys below foo/
*
* The first convention is handled by the Tree Origami server. This transform
* handles the second and third conventions.
*
* As a convenience, this transform also provides a default index.html page if
* the tree doesn't define one.
*
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
* @typedef {import("../../index.ts").Constructor<AsyncTree>} AsyncTreeConstructor
* @param {AsyncTreeConstructor} Base
*/
export default function ExplorableSiteTransform(Base) {
return class ExplorableSite extends Base {
async get(key) {
// Ask the tree if it has the key.
let value = await super.get(key);
if (value === undefined) {
// The tree doesn't have the key; try the defaults.
if (key === "index.html") {
// This tree is both the function call target and the parameter.
value = await index.call(this, this);
} else if (key === ".keys.json") {
value = await jsonKeys.stringify(this);
}
}
if (Tree.isAsyncTree(value)) {
// Ensure this transform is applied to any tree result so the user
// browse into data and trees of classes other than the current class.
if (!isTransformApplied(ExplorableSiteTransform, value)) {
value = transformObject(ExplorableSiteTransform, value);
}
} else if (value?.unpack) {
// If the value isn't a tree, but has a tree attached via an `unpack`
// method, wrap the unpack method to add this transform.
const original = value.unpack.bind(value);
const parent = this;
value.unpack = async () => {
const content = await original();
// See function notes at @debug
if (!Tree.isTraversable(content) || typeof content === "function") {
return content;
}
/** @type {any} */
let tree = Tree.from(content);
if (!tree.parent) {
tree.parent = parent;
}
if (!isTransformApplied(ExplorableSiteTransform, tree)) {
tree = transformObject(ExplorableSiteTransform, tree);
}
return tree;
};
}
return value;
}
// If this value is given to the server, the server will call this pack()
// method. We respond with the index page.
async pack() {
return this.get("index.html");
}
};
}

View File

@@ -0,0 +1,5 @@
import { Mixin } from "@weborigami/language";
declare const OriCommandTransform: Mixin<{}>;
export default OriCommandTransform;

View File

@@ -0,0 +1,34 @@
/** @typedef {import("@weborigami/types").AsyncTree} AsyncTree */
import ori from "../origami/ori.js";
/**
* Add support for commands prefixed with `!`.
*
* E.g., asking this tree for `!yaml` will invoke the yaml() builtin function
* in the context of this tree.
*
* @typedef {import("../../index.ts").Constructor<AsyncTree>} AsyncTreeConstructor
* @param {AsyncTreeConstructor} Base
*/
export default function OriCommandTransform(Base) {
return class OriCommand extends Base {
async get(key) {
let value = await super.get(key);
if (value === undefined) {
if (
key === undefined ||
typeof key !== "string" ||
!key.startsWith?.("!")
) {
return undefined;
}
// Key is an Origami command; invoke it.
const source = key.slice(1).trim();
value = await ori.call(this, source, { formatResult: false });
}
return value;
}
};
}

12
node_modules/@weborigami/origami/src/dev/breakpoint.js generated vendored Normal file
View File

@@ -0,0 +1,12 @@
/**
* Break into the JavaScript debugger.
*
* This can be used to pause execution of the JavaScript code and inspect the
* function argument and function target (`this`).
*
* @param {*} arg
*/
export default function breakpoint(arg) {
debugger;
return arg;
}

64
node_modules/@weborigami/origami/src/dev/changes.js generated vendored Normal file
View File

@@ -0,0 +1,64 @@
import { trailingSlash, Tree } from "@weborigami/async-tree";
/**
* Given an old tree and a new tree, return a tree of changes indicated
* by the values: "added", "changed", or "deleted".
*
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
*
* @this {import("@weborigami/types").AsyncTree|null}
* @param {Treelike} oldTreelike
* @param {Treelike} newTreelike
*/
export default async function changes(oldTreelike, newTreelike) {
const oldTree = Tree.from(oldTreelike, { deep: true, parent: this });
const newTree = Tree.from(newTreelike, { deep: true, parent: this });
const oldKeys = Array.from(await oldTree.keys());
const newKeys = Array.from(await newTree.keys());
const oldKeysNormalized = oldKeys.map(trailingSlash.remove);
const newKeysNormalized = newKeys.map(trailingSlash.remove);
let result;
for (const oldKey of oldKeys) {
const oldNormalized = trailingSlash.remove(oldKey);
if (!newKeysNormalized.includes(oldNormalized)) {
result ??= {};
result[oldKey] = "deleted";
continue;
}
const oldValue = await oldTree.get(oldKey);
const newValue = await newTree.get(oldKey);
if (Tree.isAsyncTree(oldValue) && Tree.isAsyncTree(newValue)) {
const treeChanges = await changes.call(this, oldValue, newValue);
if (treeChanges && Object.keys(treeChanges).length > 0) {
result ??= {};
result[oldKey] = treeChanges;
}
} else if (oldValue?.toString && newValue?.toString) {
const oldText = oldValue.toString();
const newText = newValue.toString();
if (oldText !== newText) {
result ??= {};
result[oldKey] = "changed";
}
} else {
result ??= {};
result[oldKey] = "changed";
}
}
for (const newKey of newKeys) {
const newNormalized = trailingSlash.remove(newKey);
if (!oldKeysNormalized.includes(newNormalized)) {
result ??= {};
result[newKey] = "added";
}
}
return result;
}

37
node_modules/@weborigami/origami/src/dev/code.js generated vendored Normal file
View File

@@ -0,0 +1,37 @@
import { symbols } from "@weborigami/language";
import getTreeArgument from "../common/getTreeArgument.js";
/**
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
*
* @this {AsyncTree|null}
* @param {any} value
*/
export default async function code(value) {
if (value === undefined) {
value = await getTreeArgument(this, arguments, value, "dev:code");
}
if (value === undefined) {
return undefined;
}
const code = value.code ?? value[symbols.codeSymbol];
return code ? functionNames(code) : undefined;
}
function functionNames(code) {
if (!Array.isArray(code)) {
return code;
}
let [head, ...tail] = code;
if (typeof head === "function") {
const text = head.toString();
if (text.startsWith("«ops.")) {
head = text;
} else {
head = head.name;
}
} else {
head = functionNames(head);
}
return [head, ...tail.map(functionNames)];
}

71
node_modules/@weborigami/origami/src/dev/debug.js generated vendored Normal file
View File

@@ -0,0 +1,71 @@
import { Tree } from "@weborigami/async-tree";
import getTreeArgument from "../common/getTreeArgument.js";
import { isTransformApplied, transformObject } from "../common/utilities.js";
import ExplorableSiteTransform from "./ExplorableSiteTransform.js";
import OriCommandTransform from "./OriCommandTransform.js";
/**
* Add debugging features to the indicated tree.
*
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
*
* @this {AsyncTree|null}
* @param {Treelike} [treelike]
*/
export default async function debug(treelike) {
// The debug command leaves the tree's existing scope intact; it does not
// apply its own scope to the tree.
let tree = await getTreeArgument(this, arguments, treelike, "dev:debug");
if (!isTransformApplied(DebugTransform, tree)) {
tree = transformObject(DebugTransform, tree);
}
if (!isTransformApplied(ExplorableSiteTransform, tree)) {
tree = transformObject(ExplorableSiteTransform, tree);
}
return tree;
}
/**
* @typedef {import("../../index.ts").Constructor<AsyncTree>} AsyncTreeConstructor
* @param {AsyncTreeConstructor} Base
*/
function DebugTransform(Base) {
return class Debug extends OriCommandTransform(Base) {
async get(key) {
let value = await super.get(key);
const parent = this;
// Since this transform is for diagnostic purposes, cast any treelike
// result to a tree so we can debug the result too. (Don't do this for
// functions, as that can be undesirable, e.g., when writing functions
// that handle POST requests.)
if (Tree.isTreelike(value) && typeof value !== "function") {
value = Tree.from(value, { parent });
if (!isTransformApplied(DebugTransform, value)) {
value = transformObject(DebugTransform, value);
}
} else if (value?.unpack) {
// If the value isn't a tree, but has a tree attached via an `unpack`
// method, wrap the unpack method to provide debug support for it.
const original = value.unpack.bind(value);
value.unpack = async () => {
let content = await original();
if (!Tree.isTraversable(content)) {
return content;
}
/** @type {any} */
let tree = Tree.from(content, { parent });
if (!isTransformApplied(DebugTransform, tree)) {
tree = transformObject(DebugTransform, tree);
}
return tree;
};
}
return value;
}
};
}

9
node_modules/@weborigami/origami/src/dev/dev.js generated vendored Normal file
View File

@@ -0,0 +1,9 @@
export { default as breakpoint } from "./breakpoint.js";
export { default as changes } from "./changes.js";
export { default as code } from "./code.js";
export { default as debug } from "./debug.js";
export { default as explore } from "./explore.js";
export { default as log } from "./log.js";
export { default as serve } from "./serve.js";
export { default as svg } from "./svg.js";
export { default as watch } from "./watch.js";

88
node_modules/@weborigami/origami/src/dev/explore.css generated vendored Normal file
View File

@@ -0,0 +1,88 @@
* {
box-sizing: border-box;
}
html {
height: 100%;
}
body {
background: #333;
color: #eee;
display: grid;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
grid-template-columns: 200px 1fr;
grid-template-rows: minMax(0, 1fr);
height: 100%;
margin: 0;
overflow: hidden;
}
nav {
display: grid;
gap: 1em;
grid-auto-rows: min-content;
grid-template-columns: minmax(0, 1fr);
overflow: auto;
padding: 1em 0.5em;
}
#label {
font-weight: bold;
}
#scopeToolbar {
display: grid;
grid-template-columns: repeat(4, auto);
}
button {
background: transparent;
border: solid 1px #555;
color: inherit;
font-size: smaller;
font-family: inherit;
font-weight: inherit;
padding: 0.25em;
}
button:hover {
border-color: #999;
}
button:active {
border-color: #eee;
}
button[aria-pressed="true"] {
background: #555;
}
ul {
list-style: none;
margin: 0;
padding: 0;
}
h2 {
color: #999;
font-size: inherit;
margin: 0.25em 0;
padding-left: 0.25em;
}
li {
padding: 0.25em;
padding-left: 1em;
text-indent: -0.75em;
}
a {
color: inherit;
text-decoration: none;
}
iframe {
background: white;
border: none;
height: 100%;
width: 100%;
}

80
node_modules/@weborigami/origami/src/dev/explore.js generated vendored Normal file
View File

@@ -0,0 +1,80 @@
/** @typedef {import("@weborigami/types").AsyncTree} AsyncTree */
import { Tree, scope } from "@weborigami/async-tree";
import { OrigamiFiles } from "@weborigami/language";
import path from "node:path";
import { fileURLToPath } from "node:url";
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
import { getDescriptor } from "../common/utilities.js";
import { builtinsTree } from "../internal.js";
import debug from "./debug.js";
let templatePromise;
/**
* @this {AsyncTree|null}
*/
export default async function explore(...keys) {
assertTreeIsDefined(this, "origami:explore");
if (!this) {
return undefined;
}
const tree = Tree.from(this);
/** @type {any} */
let result;
if (keys.length > 0) {
// Traverse the scope using the given keys.
const debugTree = await debug.call(tree, this);
if (!debugTree) {
return undefined;
}
const debugScope = scope(debugTree);
// HACK: reproduce logic of ExplorableSiteTransform that turns a trailing
// slash into index.html. Calling `debug` applies that transform and the
// transform should handle that logic, but unfortunately the `traverse`
// operation has special casing to treat a trailing slash, and never gives
// ExplorableSiteTransform a chance.
if (keys.at(-1) === "") {
keys[keys.length - 1] = "index.html";
}
result = await Tree.traverse(debugScope, ...keys);
} else {
// Return the Explore page for the current scope.
const data = await getScopeData(scope(tree));
templatePromise ??= loadTemplate();
const template = await templatePromise;
const text = await template(data);
result = new String(text);
result.unpack = () => debug.call(tree, tree);
}
return result;
}
async function getScopeData(scope) {
const trees = scope.trees ?? [scope];
const data = [];
for (const tree of trees) {
if (tree.parent === undefined) {
// Skip builtins.
continue;
}
const name = getDescriptor(tree);
const treeKeys = Array.from(await tree.keys());
// Skip system-ish files that start with a period.
const keys = treeKeys.filter((key) => !key.startsWith?.("."));
data.push({ name, keys });
}
return data;
}
async function loadTemplate() {
const folderPath = path.resolve(fileURLToPath(import.meta.url), "..");
const folder = new OrigamiFiles(folderPath);
folder.parent = builtinsTree;
const templateFile = await folder.get("explore.ori");
const template = await templateFile.unpack();
return template;
}

View File

@@ -0,0 +1,119 @@
let defaultPath;
let frame;
const modes = {
Content: "",
Index: "!index",
YAML: "!yaml",
SVG: "!svg",
};
// Extract the path from the URL hash.
function getPathFromHash() {
return window.location.hash.slice(1); // Remove `#`
}
function getModeFromLocation() {
const href = document.location.href;
const match = /[\/](?<command>\!(?:index|yaml|svg))$/.exec(href);
const command = match?.groups?.command ?? "";
const mode =
Object.keys(modes).find((key) => modes[key] === command) ?? "Content";
return mode;
}
function removeDocumentPath(path) {
const documentPath = document.location.pathname;
if (path.startsWith(documentPath)) {
// Remove the document path prefix.
path = path.slice(documentPath.length);
}
if (path.startsWith("/")) {
// Remove the leading slash.
path = path.slice(1);
}
return path;
}
function selectMode(newMode) {
const currentMode = getModeFromLocation();
if (newMode !== currentMode) {
let newPath = removeDocumentPath(frame.contentDocument.location.pathname);
const currentExtension = modes[currentMode];
if (currentExtension && newPath.endsWith(currentExtension)) {
// Remove the current extension.
newPath = newPath.slice(0, -currentExtension.length);
}
const newExtension = modes[newMode];
const separator = newPath.endsWith("/") ? "" : "/";
const newFullPath = `${newPath}${separator}${newExtension}`;
setPath(newFullPath);
}
}
function setPath(path) {
// Show the indicated page in the frame.
const abbreviatedPath = `/${path}`;
const fullPath = `${document.location.pathname}/${path}`;
const framePathname = frame.contentDocument.location.pathname;
if (framePathname !== abbreviatedPath && framePathname !== fullPath) {
// Use `replace` to avoid affecting browser history.
frame.contentWindow.location.replace(fullPath);
}
// If the path ends with a file name corresponding to a mode, select
// the corresponding mode button.
const mode = getModeFromLocation();
const selectedButtonId = `button${mode}`;
scopeToolbar.querySelectorAll("button").forEach((button) => {
const pressed = button.id === selectedButtonId ? "true" : "false";
button.setAttribute("aria-pressed", pressed);
});
}
// When hash changes, load the indicated page.
window.addEventListener("hashchange", () => {
const hashPath = getPathFromHash();
const newPath = hashPath !== undefined ? hashPath : defaultPath;
if (newPath) {
setPath(newPath);
}
});
// Initialize
window.addEventListener("load", () => {
// Refresh title on page load.
frame = document.getElementById("frame");
frame.addEventListener("load", () => {
if (frame.contentDocument.location.href !== "about:blank") {
document.title = frame.contentDocument.title;
const newPath = removeDocumentPath(
frame.contentDocument.location.pathname
);
const hash = `#${newPath}`;
if (window.location.hash !== hash) {
// Use `replace` to avoid affecting browser history.
window.location.replace(hash);
}
}
});
buttonContent.addEventListener("click", () => {
selectMode("Content");
});
buttonIndex.addEventListener("click", () => {
selectMode("Index");
});
buttonYAML.addEventListener("click", () => {
selectMode("YAML");
});
buttonSVG.addEventListener("click", () => {
selectMode("SVG");
});
// Navigate to any path already in the hash.
defaultPath = getPathFromHash();
if (defaultPath) {
setPath(defaultPath);
}
});

33
node_modules/@weborigami/origami/src/dev/explore.ori generated vendored Normal file
View File

@@ -0,0 +1,33 @@
(scope) => `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Web Origami Explorer</title>
<style>${ explore.css }</style>
<script>${ explore.js.inline }</script>
</head>
<body>
<nav>
<div id="label">Web Origami Explorer</div>
<div id="scopeToolbar">
<button id="buttonContent">Content</button>
<button id="buttonIndex">Index</button>
<button id="buttonSVG">SVG</button>
<button id="buttonYAML">YAML</button>
</div>
${ map(scope, (scopeTree) => `
<ul>
<h2>${ scopeTree/name ?? "" }</h2>
${ map(scopeTree/keys, (key) => `
<li>
<a href="./!explore/${ key }" target="frame">${ key }</a>
</li>
`) }
</ul>
`) }
</nav>
<iframe id="frame" name="frame"></iframe>
</body>
</html>
`

16
node_modules/@weborigami/origami/src/dev/log.js generated vendored Normal file
View File

@@ -0,0 +1,16 @@
import yaml from "../origami/yaml.js";
/**
* Log the first argument to the console as a side effect and return the second
* argument. If no second argument is provided, return the first argument.
*
* @this {import("@weborigami/types").AsyncTree|null}
* @param {any} object
* @param {any} [result]
*/
export default async function log(result, object = result) {
let text = object !== undefined ? await yaml.call(this, object) : "undefined";
text = text?.trim();
console.log(text);
return result;
}

68
node_modules/@weborigami/origami/src/dev/serve.js generated vendored Normal file
View File

@@ -0,0 +1,68 @@
import { Tree } from "@weborigami/async-tree";
import http from "node:http";
import { createServer } from "node:net";
import process from "node:process";
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
import { isTransformApplied, transformObject } from "../common/utilities.js";
import { requestListener } from "../server/server.js";
import debug from "./debug.js";
import ExplorableSiteTransform from "./ExplorableSiteTransform.js";
import watch from "./watch.js";
const defaultPort = 5000;
/**
* Start a local web server for the indicated tree.
*
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
*
* @param {Treelike} treelike
* @param {number} [port]
* @this {AsyncTree|null}
*/
export default async function serve(treelike, port) {
assertTreeIsDefined(this, "dev:serve");
let tree;
if (treelike) {
tree = Tree.from(treelike, { parent: this });
if (!isTransformApplied(ExplorableSiteTransform, tree)) {
tree = transformObject(ExplorableSiteTransform, tree);
}
} else {
// By default, watch the default tree and add default pages.
const withDefaults = await debug.call(this);
tree = await watch.call(this, withDefaults);
}
if (port === undefined) {
if (process.env.PORT) {
// Use the port specified in the environment.
port = parseInt(process.env.PORT);
} else {
// Find an open port.
port = await findOpenPort(defaultPort);
}
}
// @ts-ignore
http.createServer(requestListener(tree)).listen(port, undefined, () => {
console.log(
`Server running at http://localhost:${port}. Press Ctrl+C to stop.`
);
});
}
// Return the first open port number on or after the given port number.
// From https://gist.github.com/mikeal/1840641?permalink_comment_id=2896667#gistcomment-2896667
function findOpenPort(port) {
const server = createServer();
return new Promise((resolve, reject) =>
server
.on("error", (/** @type {any} */ error) =>
error.code === "EADDRINUSE" ? server.listen(++port) : reject(error)
)
.on("listening", () => server.close(() => resolve(port)))
.listen(port)
);
}

40
node_modules/@weborigami/origami/src/dev/svg.js generated vendored Normal file
View File

@@ -0,0 +1,40 @@
import graphviz from "graphviz-wasm";
import getTreeArgument from "../common/getTreeArgument.js";
import dot from "./treeDot.js";
let graphvizLoaded = false;
/**
* Render a tree visually in SVG format.
*
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
* @typedef {import("@weborigami/async-tree").PlainObject} PlainObject
*
* @this {AsyncTree|null}
* @param {Treelike} [treelike]
* @param {PlainObject} [options]
*/
export default async function svg(treelike, options = {}) {
if (!graphvizLoaded) {
await graphviz.loadWASM();
graphvizLoaded = true;
}
const tree = await getTreeArgument(
this,
arguments,
treelike,
"dev:svg",
true
);
const dotText = await dot.call(this, tree, options);
if (dotText === undefined) {
return undefined;
}
const svgText = await graphviz.layout(dotText, "svg");
/** @type {any} */
const result = new String(svgText);
result.mediaType = "image/svg+xml";
result.unpack = () => tree;
return result;
}

193
node_modules/@weborigami/origami/src/dev/treeDot.js generated vendored Normal file
View File

@@ -0,0 +1,193 @@
import {
Tree,
isPlainObject,
isStringLike,
toString,
trailingSlash,
} from "@weborigami/async-tree";
import * as serialize from "../common/serialize.js";
import { getDescriptor } from "../common/utilities.js";
/**
* Render a tree in DOT format.
*
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
* @typedef {import("@weborigami/async-tree").PlainObject} PlainObject
*
* @this {AsyncTree|null}
* @param {Treelike} [treelike]
* @param {PlainObject} [options]
*/
export default async function dot(treelike, options = {}) {
const tree = Tree.from(treelike, { deep: true });
const rootLabel = getDescriptor(tree) ?? "";
const treeArcs = await statements(tree, "", rootLabel, options);
return `digraph g {
bgcolor="transparent";
nodesep=1;
rankdir=LR;
ranksep=1.5;
node [color=gray70; fillcolor="white"; fontname="Helvetica"; fontsize="10"; nojustify=true; style="filled"; shape=box];
edge [arrowhead=vee; arrowsize=0.75; color=gray60; fontname="Helvetica"; fontsize="10"; labeldistance=5];
${treeArcs.join("\n")}
}`;
}
async function statements(tree, nodePath, nodeLabel, options) {
let result = [];
const createLinks = options.createLinks ?? true;
// Add a node for the root of this (sub)tree.
const rootUrl = nodePath || ".";
const url = createLinks ? `; URL="${rootUrl}"` : "";
const rootLabel = nodeLabel ? `; xlabel="${nodeLabel}"` : "";
result.push(
` "${nodePath}" [shape=circle${rootLabel}; label=""; color=gray40; width=0.15${url}];`
);
// Draw edges and collect labels for the nodes they lead to.
let nodes = new Map();
for (const key of await tree.keys()) {
const destPath = nodePath ? `${trailingSlash.add(nodePath)}${key}` : key;
const arc = ` "${nodePath}" -> "${destPath}" [label="${key}"];`;
result.push(arc);
let isError = false;
let value;
try {
value = await tree.get(key);
} catch (/** @type {any} */ error) {
isError = true;
value =
error.name && error.message
? `${error.name}: ${error.message}`
: error.name ?? error.message ?? error;
}
const expandable =
value instanceof Array || isPlainObject(value) || Tree.isAsyncTree(value);
if (expandable) {
const subtree = Tree.from(value);
const subStatements = await statements(subtree, destPath, null, options);
result = result.concat(subStatements);
} else {
const label = isStringLike(value)
? toString(value)
: value !== undefined
? await serialize.toYaml(value)
: "";
if (value === undefined) {
isError = true;
}
const data = { label };
if (isError) {
data.isError = true;
}
nodes.set(key, data);
}
}
// If we have more than one label, we'll focus on the labels' differences.
// We'll use the first label as a representative baseline for all labels but
// the first (which will use the second label as a baseline).
const values = [...nodes.values()];
const showLabelDiffs = values.length > 1;
const label1 = showLabelDiffs ? String(values[0].label) : undefined;
const label2 = showLabelDiffs ? String(values[1].label) : undefined;
// Trim labels.
let i = 0;
for (const data of nodes.values()) {
let label = data.label;
if (label === null) {
data.label = "[binary data]";
} else if (label) {
let clippedStart = false;
let clippedEnd = false;
if (showLabelDiffs) {
const baseline = i === 0 ? label2 : label1;
const diff = stringDiff(baseline, label);
if (diff !== label) {
label = diff;
clippedStart = true;
}
}
label = label.trim();
if (label.length > 40) {
// Long text, just use the beginning
label = label.slice(0, 40);
clippedEnd = true;
}
// Left justify node label using weird Dot escape character
// See https://stackoverflow.com/a/13104953/76472
const endsWithNewline = label.endsWith("\n");
label = label.replace(/\n/g, "\\l");
label = label.replace(/"/g, '\\"'); // Escape quotes
label = label.replace(/[\ \t]+/g, " "); // Collapse spaces and tabs
// Add ellipses if we clipped the label. We'd prefer to end with a real
// ellipsis, but GraphViz warns about "non-ASCII character 226" if we do.
// (That's not even the ellipsis character!) We could use a real ellipsis
// for the start, but then they might look different.
if (clippedStart) {
label = "..." + label;
}
if (clippedEnd) {
label += "...";
}
if (!endsWithNewline) {
// See note above
label += "\\l";
}
data.label = label;
}
i++;
}
// Draw labels.
for (const [key, node] of nodes.entries()) {
const icon = node.isError ? "⚠️ " : "";
// GraphViz has trouble rendering DOT nodes whose labels contain ellipsis
// characters, so we map those to three periods. GraphViz appears to turn
// those back into ellipsis characters when rendering the graph.
const text = node.label.replace(/…/g, "...");
const label = `label="${icon}${text}"`;
const color = node.isError ? `; color="red"` : "";
const fill = node.isError ? `; fillcolor="#FFF4F4"` : "";
const destPath = nodePath ? `${trailingSlash.add(nodePath)}${key}` : key;
const url = createLinks ? `; URL="${destPath}"` : "";
result.push(` "${destPath}" [${label}${color}${fill}${url}];`);
}
return result;
}
// Return the second string, removing the initial portion it shares with the
// first string. The returned string will start with the first non-whitespace
// character of the first line that differs from the first string.
function stringDiff(first, second) {
let i = 0;
// Find point of first difference.
while (i < first.length && i < second.length && first[i] === second[i]) {
i++;
}
// Back up to start of that line.
while (i > 0 && second[i - 1] !== "\n") {
i--;
}
// Move forward to first non-whitespace character.
while (i < second.length && /\s/.test(second[i])) {
i++;
}
return second.slice(i);
}

89
node_modules/@weborigami/origami/src/dev/watch.js generated vendored Normal file
View File

@@ -0,0 +1,89 @@
import { Tree } from "@weborigami/async-tree";
import { formatError } from "@weborigami/language";
import ConstantTree from "../common/ConstantTree.js";
import getTreeArgument from "../common/getTreeArgument.js";
/**
* Let a tree (e.g., of files) respond to changes.
*
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
* @typedef {import("../../index.ts").Invocable} Invocable
*
* @this {AsyncTree|null}
* @param {Treelike} [treelike]
* @param {Invocable} [fn]
*/
export default async function watch(treelike, fn) {
/** @type {any} */
const container = await getTreeArgument(
this,
arguments,
treelike,
"dev:watch"
);
// Watch the indicated tree.
await /** @type {any} */ (container).watch?.();
if (fn === undefined) {
return container;
}
// The caller supplied a function to reevaluate whenever the tree changes.
let tree = await evaluateTree(container, fn);
// We want to return a stable reference to the tree, so we'll use a prototype
// chain that will always point to the latest tree. We'll extend the tree's
// prototype chain with an empty object, and use that as a handle (pointer to
// a pointer) to the tree. This is what we'll return to the caller.
const handle = Object.create(tree);
// Reevaluate the function whenever the tree changes.
container.addEventListener?.("change", async () => {
const tree = await evaluateTree(container, fn);
updateIndirectPointer(handle, tree);
});
return handle;
}
async function evaluateTree(parent, fn) {
let tree;
let message;
let result;
try {
result = await fn();
} catch (/** @type {any} */ error) {
message = formatError(error);
}
tree = result ? Tree.from(result, { parent }) : undefined;
if (tree) {
return tree;
}
if (!message) {
message = `warning: watch expression did not return a tree`;
}
console.warn(message);
tree = new ConstantTree(message);
tree.parent = parent;
return tree;
}
// Update an indirect pointer to a target.
function updateIndirectPointer(indirect, target) {
// Clean the pointer of any named properties or symbols that have been set
// directly on it.
try {
for (const key of Object.getOwnPropertyNames(indirect)) {
delete indirect[key];
}
for (const key of Object.getOwnPropertySymbols(indirect)) {
delete indirect[key];
}
} catch {
// Ignore errors.
}
Object.setPrototypeOf(indirect, target);
}

View File

@@ -0,0 +1,7 @@
// .css files use the .txt loader
import fileTypeText from "./txt.handler.js";
export default {
...fileTypeText,
mediaType: "text/css",
};

View File

@@ -0,0 +1,16 @@
export { default as cssHandler } from "./css.handler.js";
export { default as htmHandler } from "./htm.handler.js";
export { default as htmlHandler } from "./html.handler.js";
export { default as jpegHandler } from "./jpeg.handler.js";
export { default as jpgHandler } from "./jpg.handler.js";
export { default as jsHandler } from "./js.handler.js";
export { default as jsonHandler } from "./json.handler.js";
export { default as mdHandler } from "./md.handler.js";
export { default as mjsHandler } from "./mjs.handler.js";
export { default as oriHandler } from "./ori.handler.js";
export { default as oridocumentHandler } from "./oridocument.handler.js";
export { default as txtHandler } from "./txt.handler.js";
export { default as wasmHandler } from "./wasm.handler.js";
export { default as xhtmlHandler } from "./xhtml.handler.js";
export { default as yamlHandler } from "./yaml.handler.js";
export { default as ymlHandler } from "./yml.handler.js";

View File

@@ -0,0 +1,37 @@
import {
jsHandler,
oriHandler,
oridocumentHandler,
wasmHandler,
yamlHandler,
} from "../internal.js";
import cssHandler from "./css.handler.js";
import htmHandler from "./htm.handler.js";
import htmlHandler from "./html.handler.js";
import jpegHandler from "./jpeg.handler.js";
import jpgHandler from "./jpg.handler.js";
import jsonHandler from "./json.handler.js";
import mdHandler from "./md.handler.js";
import mjsHandler from "./mjs.handler.js";
import txtHandler from "./txt.handler.js";
import xhtmlHandler from "./xhtml.handler.js";
import ymlHandler from "./yml.handler.js";
export default {
"css.handler": cssHandler,
"htm.handler": htmHandler,
"html.handler": htmlHandler,
"jpeg.handler": jpegHandler,
"jpg.handler": jpgHandler,
"js.handler": jsHandler,
"json.handler": jsonHandler,
"md.handler": mdHandler,
"mjs.handler": mjsHandler,
"ori.handler": oriHandler,
"oridocument.handler": oridocumentHandler,
"txt.handler": txtHandler,
"wasm.handler": wasmHandler,
"xhtml.handler": xhtmlHandler,
"yaml.handler": yamlHandler,
"yml.handler": ymlHandler,
};

View File

@@ -0,0 +1,2 @@
// .htm is a synonynm for .html
export { default } from "./html.handler.js";

View File

@@ -0,0 +1,7 @@
// .html files use the .txt loader
import fileTypeText from "./txt.handler.js";
export default {
...fileTypeText,
mediaType: "text/html",
};

View File

@@ -0,0 +1,62 @@
import exifParser from "exif-parser";
const exifDateTags = [
"ModifyDate",
"MDPrepDate",
"DateTimeOriginal",
"CreateDate",
"PreviewDateTime",
"GPSDateStamp",
];
/**
* A JPEG file with possible Exif metadata
*/
export default {
mediaType: "image/jpeg",
/** @type {import("@weborigami/language").UnpackFunction} */
async unpack(packed, options) {
if (packed instanceof Uint8Array) {
// Downgrade to old Node Buffer for exif-parser.
packed = Buffer.from(packed);
}
const parser = exifParser.create(packed);
parser.enableTagNames(true);
parser.enableSimpleValues(true);
const parsed = await parser.parse();
// The exif-parser `enableSimpleValues` option should convert dates to
// JavaScript Date objects, but that doesn't seem to work. Ensure dates are
// Date objects.
const exif = parsed.tags;
for (const tag of exifDateTags) {
if (typeof exif[tag] === "number") {
exif[tag] = new Date(exif[tag] * 1000);
}
}
const result = {
height: parsed.imageSize.height,
width: parsed.imageSize.width,
exif,
};
// Promote some Exif properties to the top level.
const tagsToPromote = {
ImageDescription: "caption",
ModifyDate: "modified",
Orientation: "orientation",
};
for (const [tag, key] of Object.entries(tagsToPromote)) {
if (exif[tag] !== undefined) {
result[key] = exif[tag];
}
}
// Add aspect ratio for use with `aspect-ratio` CSS.
result.aspectRatio = result.width / result.height;
return result;
},
};

View File

@@ -0,0 +1,2 @@
// .jpg is a synonym for .jpeg
export { default } from "./jpeg.handler.js";

View File

@@ -0,0 +1,20 @@
import { processUnpackedContent } from "../internal.js";
/**
* A JavaScript file
*
* Unpacking a JavaScript file returns its default export, or its set of exports
* if there is more than one.
*/
export default {
mediaType: "application/javascript",
/** @type {import("@weborigami/language").UnpackFunction} */
async unpack(packed, options = {}) {
const { key, parent } = options;
if (parent && "import" in parent) {
const content = await /** @type {any} */ (parent).import?.(key);
return processUnpackedContent(content, parent);
}
},
};

View File

@@ -0,0 +1,27 @@
import { symbols } from "@weborigami/async-tree";
import * as utilities from "../common/utilities.js";
/**
* A JSON file
*
* Unpacking a JSON file returns the parsed data.
*/
export default {
mediaType: "application/json",
/** @type {import("@weborigami/language").UnpackFunction} */
unpack(packed) {
const json = utilities.toString(packed);
if (!json) {
throw new Error("Tried to parse something as JSON but it wasn't text.");
}
const data = JSON.parse(json);
if (data && typeof data === "object" && Object.isExtensible(data)) {
Object.defineProperty(data, symbols.deep, {
enumerable: false,
value: true,
});
}
return data;
},
};

View File

@@ -0,0 +1,7 @@
// .md files use the .txt loader
import fileTypeText from "./txt.handler.js";
export default {
...fileTypeText,
mediaType: "text/markdown",
};

View File

@@ -0,0 +1,2 @@
// .mjs is a synonynm for .js
export { jsHandler as default } from "../internal.js";

View File

@@ -0,0 +1,46 @@
import { symbols } from "@weborigami/async-tree";
import { compile } from "@weborigami/language";
import * as utilities from "../common/utilities.js";
import { builtinsTree, processUnpackedContent } from "../internal.js";
/**
* An Origami expression file
*
* Unpacking an Origami file returns the result of evaluating the expression.
*/
export default {
mediaType: "text/plain",
/** @type {import("@weborigami/language").UnpackFunction} */
async unpack(packed, options = {}) {
const parent =
options.parent ??
/** @type {any} */ (packed).parent ??
/** @type {any} */ (packed)[symbols.parent];
// Construct an object to represent the source code.
const sourceName = options.key;
let url;
if (sourceName && parent?.url) {
let parentHref = parent.url.href;
if (!parentHref.endsWith("/")) {
parentHref += "/";
}
url = new URL(sourceName, parentHref);
}
const source = {
text: utilities.toString(packed),
name: options.key,
url,
};
// Compile the source code as an Origami program and evaluate it.
const compiler = options.compiler ?? compile.program;
const fn = compiler(source);
const target = parent ?? builtinsTree;
let content = await fn.call(target);
return processUnpackedContent(content, parent);
},
};

View File

@@ -0,0 +1,43 @@
import { symbols } from "@weborigami/async-tree";
import { compile } from "@weborigami/language";
import * as utilities from "../common/utilities.js";
import { processUnpackedContent } from "../internal.js";
/**
* An Origami template document: a plain text file that contains Origami
* expressions.
*/
export default {
mediaType: "text/plain",
/** @type {import("@weborigami/language").UnpackFunction} */
async unpack(packed, options = {}) {
const parent =
options.parent ??
/** @type {any} */ (packed).parent ??
/** @type {any} */ (packed)[symbols.parent];
// Construct an object to represent the source code.
const sourceName = options.key;
let url;
if (sourceName && parent?.url) {
let parentHref = parent.url.href;
if (!parentHref.endsWith("/")) {
parentHref += "/";
}
url = new URL(sourceName, parentHref);
}
const source = {
text: utilities.toString(packed),
name: options.key,
url,
};
// Compile the text as an Origami template document.
const templateDefineFn = compile.templateDocument(source);
const templateFn = await templateDefineFn.call(parent);
return processUnpackedContent(templateFn, parent);
},
};

View File

@@ -0,0 +1,78 @@
import { isPacked, symbols } from "@weborigami/async-tree";
import { parseYaml, toYaml } from "../common/serialize.js";
import * as utilities from "../common/utilities.js";
/**
* A text file with possible front matter
*
* The unpacking process will parse out any YAML or JSON front matter and attach
* it to the document as data. The first line of the text must be "---",
* followed by a block of JSON or YAML, followed by another line of "---". Any
* lines following will be treated as the document text.
*
* If there is no front matter, the document will be treated as plain text and
* returned as a String object.
*
* If there is front matter, any Origami expressions in the front matter will be
* evaluated. The result will be a plain JavaScript object with the evaluated
* data and a `@text` property containing the document text.
*/
export default {
mediaType: "text/plain",
/**
* If the input is already in some packed format, it will be returned as is.
*
* Otherwise, the properties of the object will be formatted as YAML. If the
* object has a `@text` property, that will be used as the body of the text
* document; otherwise, an empty string will be used.
*
* @param {any} object
* @returns {Promise<import("@weborigami/async-tree").Packed>}
*/
async pack(object) {
if (isPacked(object)) {
return object;
} else if (!object || typeof object !== "object") {
throw new TypeError("The input to pack must be a JavaScript object.");
}
const text = object["@text"] ?? "";
/** @type {any} */
const dataWithoutText = Object.assign({}, object);
delete dataWithoutText["@text"];
if (Object.keys(dataWithoutText).length > 0) {
const frontMatter = (await toYaml(dataWithoutText)).trimEnd();
return `---\n${frontMatter}\n---\n${text}`;
} else {
return text;
}
},
/** @type {import("@weborigami/language").UnpackFunction} */
unpack(packed, options = {}) {
const parent = options.parent ?? null;
const text = utilities.toString(packed);
if (text === null) {
throw new Error("Tried to treat something as text but it wasn't text.");
}
const regex =
/^(---\r?\n(?<frontText>[\s\S]*?\r?\n?)---\r?\n)(?<body>[\s\S]*$)/;
const match = regex.exec(text);
let unpacked;
if (match) {
// Document object with front matter
const { body, frontText } = /** @type {any} */ (match.groups);
const frontData = parseYaml(frontText);
unpacked = Object.assign({}, frontData, { "@text": body });
} else {
// Plain text
unpacked = new String(text);
}
unpacked[symbols.parent] = parent;
return unpacked;
},
};

View File

@@ -0,0 +1,17 @@
import { processUnpackedContent } from "../internal.js";
/**
* A WebAssembly module
*
* Unpacking a WebAssembly module returns its exports.
*/
export default {
mediaType: "application/wasm",
/** @type {import("@weborigami/language").UnpackFunction} */
async unpack(packed, options = {}) {
const wasmModule = await WebAssembly.instantiate(packed);
// @ts-ignore TypeScript thinks wasmModule is already an Instance.
return processUnpackedContent(wasmModule.instance.exports, options.parent);
},
};

View File

@@ -0,0 +1,2 @@
// .xhtml is a synonynm for .html
export { default } from "./html.handler.js";

View File

@@ -0,0 +1,36 @@
import { symbols } from "@weborigami/async-tree";
import * as YAMLModule from "yaml";
import { parseYaml } from "../common/serialize.js";
import * as utilities from "../common/utilities.js";
import { processUnpackedContent } from "../internal.js";
// See notes at serialize.js
// @ts-ignore
const YAML = YAMLModule.default ?? YAMLModule.YAML;
/**
* A YAML file
*
* Unpacking a YAML file returns the parsed data.
*
*/
export default {
mediaType: "application/yaml",
/** @type {import("@weborigami/language").UnpackFunction} */
unpack(packed, options = {}) {
const parent = options.parent ?? null;
const yaml = utilities.toString(packed);
if (!yaml) {
throw new Error("Tried to parse something as YAML but it wasn't text.");
}
const data = parseYaml(yaml);
if (data && typeof data === "object" && Object.isExtensible(data)) {
Object.defineProperty(data, symbols.deep, {
enumerable: false,
value: true,
});
}
return processUnpackedContent(data, parent);
},
};

View File

@@ -0,0 +1,2 @@
// .yml is a synonym for .yaml
export { yamlHandler as default } from "../internal.js";

103
node_modules/@weborigami/origami/src/help/help.js generated vendored Normal file
View File

@@ -0,0 +1,103 @@
import fs from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "url";
import YAML from "yaml";
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
import version from "../origami/version.js";
/**
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
*
* @this {AsyncTree|null}
* @param {string} [key]
*/
export default async function help(key) {
assertTreeIsDefined(this, "help:");
const helpFilename = path.resolve(
fileURLToPath(import.meta.url),
"../help.yaml"
);
const helpYaml = await fs.readFile(helpFilename);
const helpData = YAML.parse(String(helpYaml));
if (key === undefined) {
// Show all namespace descriptions
return namespaceDescriptions(helpData);
}
// Try treating key as a namespace.
const namespace = helpData[key];
if (namespace) {
return namespaceCommands(namespace, key);
}
// Try treating key as a builtin command.
for (const [namespace, { commands }] of Object.entries(helpData)) {
if (commands && Object.hasOwn(commands, key)) {
return commandDescription(commands[key], namespace, key);
}
}
return `help: "${key}" not found`;
}
async function commandDescription(commandHelp, namespace, command) {
const url = commandHelp.collection
? `${namespace}.html`
: `${namespace}/${command}.html`;
const text = [
"",
formatCommandDescription(commandHelp, namespace, command),
"",
`For more information: https://weborigami.org/builtins/${url}`,
];
return text.join("\n");
}
function formatCommandDescription(commandHelp, namespace, command) {
const { args, description } = commandHelp;
return ` ${namespace}:${command}${args ?? ""} - ${description}`;
}
async function namespaceCommands(namespaceHelp, namespace) {
const text = [];
const commands = namespaceHelp.commands;
if (commands === undefined) {
text.push(`"${namespace}" works like a protocol at the start of a path.`);
} else {
if (namespaceHelp.description) {
const description = namespaceHelp.description;
const lowercase = description[0].toLowerCase() + description.slice(1);
text.push(
`The "${namespace}" namespace contains commands to ${lowercase}.`
);
}
text.push("");
for (const [command, commandHelp] of Object.entries(commands)) {
text.push(formatCommandDescription(commandHelp, namespace, command));
}
text.push("");
}
const url = namespaceHelp.collection ? `${namespace}.html` : `${namespace}/`;
text.push(`For more information: https://weborigami.org/builtins/${url}`);
return text.join("\n");
}
async function namespaceDescriptions(helpData) {
const text = [
`Origami ${version} has commands grouped into the following namespaces:\n`,
];
for (const [key, value] of Object.entries(helpData)) {
const description = value.description;
if (description) {
text.push(` ${key}: ${description}`);
}
}
text.push(
`\nType "ori help:<namespace>" for more or visit https://weborigami.org/builtins`
);
return text.join("\n");
}

399
node_modules/@weborigami/origami/src/help/help.yaml generated vendored Normal file
View File

@@ -0,0 +1,399 @@
dev:
description: Develop and debug Origami projects
commands:
breakpoint:
args: (a)
description: Break into the JavaScript debugger, then return a
changes:
args: (old, new)
description: Return a tree of changes
debug:
args: (tree)
description: Add debug features to the tree
explore:
args: ()
description: Explore the current scope [when run in browser]
log:
args: (a, message)
description: Log message to the console and return a
serve:
args: (tree, port)
description: Start a web server for the tree
svg:
args: (tree, options)
description: Render a tree visually in SVG format
watch:
args: (tree, fn)
description: Reevaluate fn when tree changes
explore:
description: URL protocol to treat a website with JSON keys as a tree
files:
description: URL protocol for file system folders and files
help:
description: Get help on builtin namespaces and commands
http:
description: URL protocol for web resources via HTTP
https:
description: URL protocol for web resources via HTTPS
httpstree:
description: URL protocol for a website tree via HTTPS
httptree:
description: URL protocol for a website tree via HTTP
image:
description: Format and resize images
commands:
format:
args: (image, format, options)
description: Return the image in a different format
resize:
args: (image, options)
description: Resize the image
inherited:
description: URL protocol to get an inherited value instead of a local one
js:
description: JavaScript classes and functions
collection: true
commands:
Array:
description: JavaScript Array class
BigInt:
description: JavaScript BigInt class
Boolean:
description: JavaScript Boolean class
Date:
description: JavaScript Date class
Error:
description: JavaScript Error class
Infinity:
description: JavaScript Infinity constant
Intl:
description: JavaScript Intl object
JSON:
description: JavaScript JSON object
Map:
description: JavaScript Map class
Math:
description: JavaScript Math object
NaN:
description: JavaScript NaN constant
Number:
description: JavaScript Number class
Object:
description: JavaScript Object class
RegExp:
description: JavaScript RegExp class
Set:
description: JavaScript Set class
String:
description: JavaScript String class
Symbol:
description: JavaScript Symbol class
decodeURI:
description: JavaScript decodeURI function
decodeURIComponent:
description: JavaScript decodeURIComponent function
encodeURI:
description: JavaScript encodeURI function
encodeURIComponent:
description: JavaScript encodeURIComponent function
"false":
description: JavaScript false constant
fetch:
description: JavaScript fetch function
isFinite:
description: JavaScript isFinite function
isNaN:
description: JavaScript isNaN function
"null":
description: JavaScript null constant
parseFloat:
description: JavaScript parseFloat function
parseInt:
description: JavaScript parseInt function
"true":
description: JavaScript true constant
undefined:
description: JavaScript undefined constant
new:
description: Create instances of JavaScript classes
node:
description: Node.js classes and modules
collection: true
commands:
Buffer:
description: Node.js Buffer class
path:
description: Node.js path module
process:
description: Node.js process object
url:
description: Node.js URL module
origami:
description: Perform general Origami language functions
commands:
basename:
args: (key)
description: Removes an extension from the key if present
builtins:
description: The set of installed builtin functions
config:
description: The current project's configuration
extension:
description: Helpers for working with file extensions
json:
args: (obj)
description: Render the object in JSON format
jsonParse:
args: (text)
description: Parse text as JSON
naturalOrder:
description: A comparison function for natural sort order
once:
args: (fn)
description: Run the function only once, return the same result
ori:
args: (text)
description: Evaluate the text as an Origami expression
post:
args: (url, data)
description: POST the given data to the URL
project:
description: The root folder for the current Origami project
regexMatch:
args: (text, regex)
description: Return matches of the regex in the text
repeat:
args: (n, obj)
description: An array of n copies of the object
shell:
args: (text)
description: Run the text as a shell command, return the output
slash:
description: Helpers for working with trailing slashes
stdin:
description: Returns the content of the standard input stream
string:
args: (obj)
description: Coerce a buffer or document to a string
toFunction:
args: (obj)
description: Coerce a tree or packed function definition to a function
unpack:
args: (buffer)
description: Unpack the buffer into a usable form
version:
args: ()
description: Return the version number of the Origami language
yaml:
args: (obj)
description: Render the object in YAML format
yamlParse:
args: (text)
description: Parse text as YAML
package:
description: URL protocol for packages installed in node_modules
scope:
description: URL protocol to explicitly reference a key in scope
site:
description: Add common website features
commands:
audit:
args: (tree)
description: Identify broken internal links and references
crawl:
args: (tree, base)
description: A tree of a site's discoverable resources
index:
args: (tree)
description: A default index.html page for the tree
jsonKeys:
args: (tree)
description: Add .keys.json files to a tree
redirect:
args: (url, options)
description: Redirect to the given URL
rss:
args: (feed)
description: Transforms a JSON Feed tree to RSS XML
sitemap:
args: (tree)
description: Generate a sitemap for the tree
slug:
args: (text)
description: A version of the text suitable for use in URLs
static:
args: (tree)
description: Define common static files for the tree
text:
description: Manipulate text
commands:
document:
args: (text, [data])
description: Create a document object with the text and data
indent:
description: Tagged template literal for normalizing indentation
inline:
args: (text)
description: Inline Origami expressions found in the text
mdHtml:
args: (markdown)
description: Render the markdown as HTML
tree:
description: Work with trees
commands:
addNextPrevious:
args: (tree)
description: Add next/previous fields to the tree's values
assign:
args: (target, source)
description: Apply key/values from source to target
cache:
args: (tree, [cache], [filter])
description: Caches values from the tree
clear:
args: (tree)
description: Remove all values from the tree
concat:
args: (...objs)
description: Concatenate text and/or trees of text
copy:
args: (source, target)
description: Copy the source tree to the target
deepMap:
args: (tree, options)
description: Map the keys and values of a deep tree
deepMerge:
args: (...trees)
description: Return a deeply-merged tree
deepReverse:
args: (tree)
description: Reverse order of keys at all levels of the tree
deepTake:
args: (tree, n)
description: The first n values from the deep tree
deepValues:
args: (tree)
description: The in-order leaf values of the tree
defineds:
args: (tree)
description: Only the defined values of the tree
entries:
args: (tree)
description: The tree's [key, value] pairs
first:
args: (tree)
description: The first value in the tree
forEach:
args: (tree, fn)
description: Apply fn to each (value, key)
from:
args: (object, options)
description: Create a tree from an object
fromFn:
args: (fn, [keys])
description: A tree defined by a value function
globs:
args: (patterns)
description: A tree whose keys can include wildcard patterns
group:
args: (tree, fn)
description: A new tree with values grouped by the function
has:
args: (tree, key)
description: True if key exists in tree
inners:
args: (tree)
description: The tree's interior nodes
isAsyncMutableTree:
args: (object)
description: True if object is an async mutable tree
isAsyncTree:
args: (object)
description: True if object is an async tree
isTraversable:
args: (object)
description: True if object is traversable
isTreelike:
args: (object)
description: True if object can be coerced to a tree
keys:
args: (tree)
description: The keys of the tree
length:
args: (tree)
description: The tree's size (number of keys)
map:
args: (tree, options)
description: Create a new tree by mapping keys and/or values
mapReduce:
args: (tree, valueFn, reduceFn)
description: Map values and reduce them
match:
args: (pattern, fn, [keys])
description: Matches simple patterns or regular expressions
merge:
args: (...trees)
description: Return a new tree merging the given trees
paginate:
args: (tree, [n])
description: Group the tree's values into fixed-size sets
parent:
args: (tree)
description: The parent of the given tree node
paths:
args: (tree)
description: Slash-separated paths for the tree's values
plain:
args: (tree)
description: Render the tree as a plain JavaScript object
remove:
args: (tree, key)
description: Remove the value for the key from tree
reverse:
args: (tree)
description: Reverse the order of the tree's keys
root:
args: (tree)
description: The root node of the given tree
setDeep:
args: (target, source)
description: Applies the source tree to the target
shuffle:
args: (tree)
description: Shuffle the keys of the tree
sort:
args: (tree, options)
description: A new tree with its keys sorted
take:
args: (tree, n)
description: The first n values in the tree
traverse:
args: (tree, ...keys)
description: Return the value at the path of keys
traverseOrThrow:
args: (tree, ...keys)
description: Return the value at the path of keys or throw
traversePath:
args: (tree, path)
description: Traverse a slash-separated path
values:
args: (tree)
description: The tree's values

18
node_modules/@weborigami/origami/src/image/format.js generated vendored Normal file
View File

@@ -0,0 +1,18 @@
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
import imageFormatFn from "./formatFn.js";
/**
* Return the image in a different format.
*
* @this {import("@weborigami/types").AsyncTree|null}
*
* @this {import("@weborigami/types").AsyncTree|null}
* @param {import("@weborigami/async-tree").Packed} input
* @param {keyof import("sharp").FormatEnum|import("sharp").AvailableFormatInfo}
* format
* @param {any} options
*/
export default async function imageFormat(input, format, options) {
assertTreeIsDefined(this, "image:format");
return imageFormatFn.call(this, format, options)(input);
}

18
node_modules/@weborigami/origami/src/image/formatFn.js generated vendored Normal file
View File

@@ -0,0 +1,18 @@
import sharp from "sharp";
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
/**
* Return a function that transforms an input image to a different format.
*
* @this {import("@weborigami/types").AsyncTree|null}
* @param {keyof import("sharp").FormatEnum|import("sharp").AvailableFormatInfo}
* format
* @param {any} options
*/
export default function imageFormatFn(format, options) {
assertTreeIsDefined(this, "image/formatFn");
return (buffer) =>
buffer instanceof Uint8Array || buffer instanceof ArrayBuffer
? sharp(buffer).toFormat(format, options).toBuffer()
: undefined;
}

2
node_modules/@weborigami/origami/src/image/image.js generated vendored Normal file
View File

@@ -0,0 +1,2 @@
export { default as format } from "./format.js";
export { default as resize } from "./resize.js";

14
node_modules/@weborigami/origami/src/image/resize.js generated vendored Normal file
View File

@@ -0,0 +1,14 @@
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
import imageResizeFn from "./resizeFn.js";
/**
* Resize an image.
*
* @this {import("@weborigami/types").AsyncTree|null}
* @param {import("@weborigami/async-tree").Packed} input
* @param {import("sharp").ResizeOptions} options
*/
export default async function resize(input, options) {
assertTreeIsDefined(this, "image:resize");
return imageResizeFn.call(this, options)(input);
}

17
node_modules/@weborigami/origami/src/image/resizeFn.js generated vendored Normal file
View File

@@ -0,0 +1,17 @@
import sharp from "sharp";
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
/**
* Return a function that resizes an image.
*
* @this {import("@weborigami/types").AsyncTree|null}
* @param {import("sharp").ResizeOptions} options
*/
export default function imageResizeFn(options) {
assertTreeIsDefined(this, "image/resizeFn");
// Include `rotate()` to auto-rotate according to EXIF data.
return (buffer) =>
buffer instanceof Uint8Array || buffer instanceof ArrayBuffer
? sharp(buffer).rotate().resize(options).toBuffer()
: undefined;
}

24
node_modules/@weborigami/origami/src/internal.js generated vendored Normal file
View File

@@ -0,0 +1,24 @@
//
// This library includes a number of modules with circular dependencies. This
// module exists to explicitly set the loading order for those modules. To
// enforce use of this loading order, other modules should only load the modules
// below via this module.
//
// About this pattern:
// https://medium.com/visual-development/how-to-fix-nasty-circular-dependency-issues-once-and-for-all-in-javascript-typescript-a04c987cf0de
//
// Note: to avoid having VS Code auto-sort the imports, keep lines between them.
export { default as jsHandler } from "./handlers/js.handler.js";
export { default as oriHandler } from "./handlers/ori.handler.js";
export { default as oridocumentHandler } from "./handlers/oridocument.handler.js";
export { default as processUnpackedContent } from "./common/processUnpackedContent.js";
export { default as wasmHandler } from "./handlers/wasm.handler.js";
export { default as yamlHandler } from "./handlers/yaml.handler.js";
export { default as builtinsTree } from "./builtinsTree.js";

37
node_modules/@weborigami/origami/src/js.js generated vendored Normal file
View File

@@ -0,0 +1,37 @@
async function fetchWrapper(resource, options) {
const response = await fetch(resource, options);
return response.ok ? await response.arrayBuffer() : undefined;
}
export default {
Array,
BigInt,
Boolean,
Date,
Error,
Infinity,
Intl,
JSON,
Map,
Math,
NaN,
Number,
Object,
RegExp,
Set,
String,
Symbol,
decodeURI,
decodeURIComponent,
encodeURI,
encodeURIComponent,
false: false,
fetch: fetchWrapper,
isFinite,
isNaN,
null: null,
parseFloat,
parseInt,
true: true,
undefined: undefined,
};

22
node_modules/@weborigami/origami/src/node.js generated vendored Normal file
View File

@@ -0,0 +1,22 @@
import path from "node:path";
import process from "node:process";
import url from "node:url";
// Patch process.env to be a plain object. Among other things, this lets us dump
// the complete environment to the terminal with `ori @node/process/env`.
const patchedProcess = Object.create(null, {
...Object.getOwnPropertyDescriptors(process),
env: {
enumerable: true,
get: function () {
return Object.assign({}, process.env);
},
},
});
export default {
Buffer,
path,
process: patchedProcess,
url,
};

View File

@@ -0,0 +1,6 @@
import { extension } from "@weborigami/async-tree";
export default function basename(key) {
const ext = extension.extname(key);
return ext ? key.slice(0, -ext.length) : key;
}

20
node_modules/@weborigami/origami/src/origami/config.js generated vendored Normal file
View File

@@ -0,0 +1,20 @@
import project from "./project.js";
/**
* Return the configuration for the current project.
*
* The configuration is the project's config.ori file (if defined in the project
* root) plus the Origami builtins.
*
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
*
* @this {AsyncTree|null}
* @param {any} [key]
*/
export default async function config(key) {
const projectTree = await project.call(this);
// HACK: We use specific knowledge of how @project returns a tree to get the
// config. The config is always the parent of the project folder.
const parent = projectTree.parent;
return key === undefined ? parent : parent.get(key);
}

28
node_modules/@weborigami/origami/src/origami/json.js generated vendored Normal file
View File

@@ -0,0 +1,28 @@
/** @typedef {import("@weborigami/types").AsyncTree} AsyncTree */
import { isUnpackable, toPlainValue } from "@weborigami/async-tree";
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
/**
* Render the given object in JSON format.
*
* @this {AsyncTree|null}
* @param {any} [obj]
*/
export default async function json(obj) {
assertTreeIsDefined(this, "origami:json");
// A fragment of the logic from getTreeArgument.js
if (arguments.length > 0 && obj === undefined) {
throw new Error(
"An Origami function was called with an initial argument, but its value is undefined."
);
}
obj = obj ?? this;
if (obj === undefined) {
return undefined;
}
if (isUnpackable(obj)) {
obj = await obj.unpack();
}
const value = await toPlainValue(obj);
return JSON.stringify(value, null, 2);
}

View File

@@ -0,0 +1,6 @@
import { toString } from "../common/utilities.js";
export default async function jsonParse(input) {
const text = toString(input);
return text ? JSON.parse(text) : undefined;
}

View File

@@ -0,0 +1 @@
export { naturalOrder as default } from "@weborigami/async-tree";

18
node_modules/@weborigami/origami/src/origami/once.js generated vendored Normal file
View File

@@ -0,0 +1,18 @@
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
const fnPromiseMap = new WeakMap();
/**
* Evaluate the given function only once and cache the result.
*
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
* @this {AsyncTree|null}
* @param {Function} fn
*/
export default async function once(fn) {
assertTreeIsDefined(this, "origami:once");
if (!fnPromiseMap.has(fn)) {
fnPromiseMap.set(fn, fn.call(this));
}
return fnPromiseMap.get(fn);
}

93
node_modules/@weborigami/origami/src/origami/ori.js generated vendored Executable file
View File

@@ -0,0 +1,93 @@
import {
Tree,
getRealmObjectPrototype,
toString,
} from "@weborigami/async-tree";
import { compile } from "@weborigami/language";
import builtinsTree from "../builtinsTree.js";
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
import { toYaml } from "../common/serialize.js";
const TypedArray = Object.getPrototypeOf(Uint8Array);
/**
* Parse an Origami expression, evaluate it in the context of a tree (provided
* by `this`), and return the result as text.
*
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
*
* @this {AsyncTree|null}
* @param {string} expression
*/
export default async function ori(
expression,
options = { formatResult: true }
) {
assertTreeIsDefined(this, "origami:ori");
// In case expression has come from a file, cast it to a string.
expression = toString(expression);
// Run in the context of `this` if defined, otherwise use the builtins.
const tree = this ?? builtinsTree;
// Compile the expression. Avoid caching scope references so that, e.g.,
// passing a function to the `watch` builtin will always look the current
// value of things in scope.
const fn = compile.expression(expression, { scopeCaching: false });
// Execute
let result = await fn.call(tree);
// If result was a function, execute it.
if (typeof result === "function") {
result = await result.call(tree);
}
return options.formatResult ? await formatResult(result) : result;
}
async function formatResult(result) {
if (
typeof result === "string" ||
result instanceof ArrayBuffer ||
result instanceof TypedArray
) {
// Use as is
return result;
}
/** @type {string|String|undefined} */
let text;
// Does the result have a meaningful toString() method (and not the dumb
// Object.toString)? Exception: if the result is an array, we'll use YAML
// instead.
if (!result) {
// Return falsy values as is.
text = result;
} else if (
!(result instanceof Array) &&
(typeof result !== "object" ||
result.toString !== getRealmObjectPrototype(result)?.toString)
) {
text = result.toString();
} else if (typeof result === "object") {
// Render YAML
text = await toYaml(result);
} else {
// Use result itself.
text = result;
}
// If the result is treelike, attach it to the text output.
if (Tree.isTreelike(result)) {
if (typeof text === "string") {
// @ts-ignore
text = new String(text);
}
/** @type {any} */ (text).unpack = () => result;
}
return text;
}

View File

@@ -0,0 +1,29 @@
// Use a dynamic import to avoid circular dependencies
export const builtins = import("../internal.js").then(
(internal) => internal.builtinsTree
);
export { extension } from "@weborigami/async-tree";
export { toFunction } from "../common/utilities.js";
export { default as help } from "../help/help.js"; // Alias
export { default as basename } from "./basename.js";
export { default as config } from "./config.js";
export { default as json } from "./json.js";
export { default as jsonParse } from "./jsonParse.js";
export { default as naturalOrder } from "./naturalOrder.js";
export { default as once } from "./once.js";
export { default as ori } from "./ori.js";
export { default as pack } from "./pack.js";
export { default as post } from "./post.js";
export { default as project } from "./project.js";
export { default as regexMatch } from "./regexMatch.js";
export { default as repeat } from "./repeat.js";
export { default as shell } from "./shell.js";
export { default as slash } from "./slash.js";
export { default as stdin } from "./stdin.js";
export { default as string } from "./string.js";
export { default as unpack } from "./unpack.js";
export { default as version } from "./version.js";
export { default as yaml } from "./yaml.js";
export { default as yamlParse } from "./yamlParse.js";

13
node_modules/@weborigami/origami/src/origami/pack.js generated vendored Normal file
View File

@@ -0,0 +1,13 @@
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
/**
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
*
* @this {AsyncTree|null}
* @param {any} obj
* @returns
*/
export default function pack(obj) {
assertTreeIsDefined(this, "origami:pack");
return obj?.pack?.();
}

45
node_modules/@weborigami/origami/src/origami/post.js generated vendored Normal file
View File

@@ -0,0 +1,45 @@
import {
isStringLike,
isUnpackable,
toPlainValue,
toString,
Tree,
} from "@weborigami/async-tree";
/**
* @this {import("@weborigami/types").AsyncTree|null}
* @param {string} url
* @param {any} data
*/
export default async function post(url, data) {
let body;
let headers;
if (isUnpackable(data)) {
data = await data.unpack();
}
if (Tree.isTreelike(data)) {
const value = await toPlainValue(data);
body = JSON.stringify(value);
headers = {
"Content-Type": "application/json",
};
} else if (isStringLike(data)) {
body = toString(data);
headers = {
"Content-Type": "text/plain",
};
} else {
body = data;
}
const response = await fetch(url, {
method: "POST",
body,
headers,
});
if (!response.ok) {
throw new Error(
`Failed to POST to ${url}. Error ${response.status}: ${response.statusText}`
);
}
return response.arrayBuffer();
}

View File

@@ -0,0 +1,99 @@
/** @typedef {import("@weborigami/types").AsyncTree} AsyncTree */
import { Tree } from "@weborigami/async-tree";
import { OrigamiFiles } from "@weborigami/language";
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
import { builtinsTree, oriHandler } from "../internal.js";
const configFileName = "config.ori";
/**
* Return the tree for the current project's root folder.
*
* This searches the current directory and its ancestors for an Origami file
* called `config.ori`. If an Origami configuration file is found, the
* containing folder is considered to be the project root. This returns a tree
* for that folder, with the exported configuration as the context for that
* folder — that is, the tree exported by the configuration will be the scope.
*
* If no Origami configuration file is found, the current folder will be
* returned as a tree, with the builtins as its parent.
*
* @this {AsyncTree|null}
* @param {any} [key]
*/
export default async function project(key) {
assertTreeIsDefined(this, "origami:project");
const dirname = process.cwd();
const currentTree = new OrigamiFiles(dirname);
currentTree.parent = builtinsTree;
// Search up the tree for the configuration file or package.json to determine
// the project root.
const configContainer =
(await findAncestorFile(currentTree, configFileName)) ??
(await findAncestorFile(currentTree, "package.json"));
let projectRoot;
if (!configContainer) {
// No configuration file or package.json found; use the current directory.
projectRoot = currentTree;
} else {
// Load the configuration file if one exists.
const buffer = await configContainer.get(configFileName);
if (!buffer) {
// Project root defined by package.json
projectRoot = configContainer;
} else {
// Load Origami configuration file
const config = await oriHandler.unpack(buffer, {
key: configFileName,
parent: configContainer,
});
if (!config) {
const configPath = /** @type {any} */ (configContainer).path;
throw new Error(
`Couldn't load the Origami configuration in ${configPath}/${configFileName}`
);
}
// The config tree may refer to the container tree *and vice versa*. To
// support this, we put the container in the tree twice. The chain will
// be: projectRoot -> configTree -> configContainer -> builtins, where
// the projectRoot and configContainer are the same folder.
const configTree = Tree.from(config);
projectRoot = new OrigamiFiles(configContainer.path);
projectRoot.parent = configTree;
configTree.parent = configContainer;
configContainer.parent = builtinsTree;
}
}
return key === undefined ? projectRoot : projectRoot.get(key);
}
// Return the first ancestor of the given tree that contains a file with the
// given name.
async function findAncestorFile(start, fileName) {
let current = start;
while (current) {
const value = await current.get(fileName);
if (value) {
// Found the desired file; its container is the project root. Set the
// parent to the builtins; in the context of this project, there's nothing
// higher up.
current.parent = builtinsTree;
return current;
}
// Not found; try the parent.
const parent = await current.get("..");
if (
!parent ||
(parent.path && current.path && parent.path === current.path)
) {
break;
}
current = parent;
}
return undefined;
}

View File

@@ -0,0 +1,5 @@
import regexMatchFn from "./regexMatchFn.js";
export default function regexMatch(text, regex) {
return regexMatchFn(regex)(text);
}

View File

@@ -0,0 +1,9 @@
const parsers = {};
export default function regexParseFn(text) {
if (!parsers[text]) {
const regexp = new RegExp(text);
parsers[text] = (input) => input.match(regexp)?.groups;
}
return parsers[text];
}

View File

@@ -0,0 +1,5 @@
export default async function repeat(count, content) {
const array = new Array(count);
array.fill(content);
return array;
}

13
node_modules/@weborigami/origami/src/origami/shell.js generated vendored Normal file
View File

@@ -0,0 +1,13 @@
import { exec as callbackExec } from "node:child_process";
import util from "node:util";
const exec = util.promisify(callbackExec);
export default async function shell(command) {
try {
const { stdout } = await exec(command);
return stdout;
} catch (err) {
console.error(err);
return undefined;
}
}

View File

@@ -0,0 +1 @@
export { trailingSlash as default } from "@weborigami/async-tree";

29
node_modules/@weborigami/origami/src/origami/stdin.js generated vendored Normal file
View File

@@ -0,0 +1,29 @@
import process from "node:process";
export default async function stdin() {
return readAll(process.stdin);
}
function readAll(readable) {
return new Promise((resolve) => {
const chunks = [];
readable.on("readable", () => {
let chunk;
while (null !== (chunk = readable.read())) {
chunks.push(chunk);
}
});
readable.on("end", () => {
const size = chunks.reduce((acc, chunk) => acc + chunk.length, 0);
const buffer = new Uint8Array(size);
let offset = 0;
for (const chunk of chunks) {
buffer.set(chunk, offset);
offset += chunk.length;
}
resolve(buffer);
});
});
}

14
node_modules/@weborigami/origami/src/origami/string.js generated vendored Normal file
View File

@@ -0,0 +1,14 @@
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
import { toString } from "../common/utilities.js";
/**
* Convert an object to a string.
*
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
* @this {AsyncTree|null}
* @param {any} object
*/
export default function stringBuiltin(object) {
assertTreeIsDefined(this, "origami:string");
return toString(object);
}

14
node_modules/@weborigami/origami/src/origami/unpack.js generated vendored Normal file
View File

@@ -0,0 +1,14 @@
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
/**
* Unpack a packed format like a Uint8Array or ArrayBuffer to a usable form like
* text or a plain JavaScript object.
*
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
* @this {AsyncTree|null}
* @param {any} obj
*/
export default function unpack(obj) {
assertTreeIsDefined(this, "origami:unpack");
return obj?.unpack?.() ?? obj;
}

View File

@@ -0,0 +1,13 @@
// When this is no longer experimental in Node:
// import packageJson from "../../package.json" with { type: "json" };
import fs from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
const dirname = path.dirname(fileURLToPath(import.meta.url));
const packageJsonPath = path.resolve(dirname, "../../package.json");
const buffer = await fs.readFile(packageJsonPath);
const data = JSON.parse(String(buffer));
export default data.version;

29
node_modules/@weborigami/origami/src/origami/yaml.js generated vendored Normal file
View File

@@ -0,0 +1,29 @@
/** @typedef {import("@weborigami/types").AsyncTree} AsyncTree */
import { isUnpackable, toPlainValue } from "@weborigami/async-tree";
import YAML from "yaml";
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
/**
* Render the object as text in YAML format.
*
* @this {AsyncTree|null}
* @param {any} [obj]
*/
export default async function toYaml(obj) {
assertTreeIsDefined(this, "origami:yaml");
// A fragment of the logic from getTreeArgument.js
if (arguments.length > 0 && obj === undefined) {
throw new Error(
"An Origami function was called with an initial argument, but its value is undefined."
);
}
obj = obj ?? this;
if (obj === undefined) {
return undefined;
}
if (isUnpackable(obj)) {
obj = await obj.unpack();
}
const value = await toPlainValue(obj);
return YAML.stringify(value);
}

View File

@@ -0,0 +1,7 @@
import * as serialize from "../common/serialize.js";
import { toString } from "../common/utilities.js";
export default async function yamlParse(input) {
const text = toString(input);
return text ? serialize.parseYaml(text) : undefined;
}

View File

@@ -0,0 +1,19 @@
import { ExplorableSiteTree } from "@weborigami/async-tree";
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
import constructSiteTree from "../common/constructSiteTree.js";
/**
* A site tree with JSON Keys via HTTPS.
*
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
* @typedef {import("../../index.ts").Invocable} Invocable
*
* @this {AsyncTree|null}
* @param {string} host
* @param {...string} keys
*/
export default function explore(host, ...keys) {
assertTreeIsDefined(this, "explore:");
return constructSiteTree("https:", ExplorableSiteTree, this, host, ...keys);
}

View File

@@ -0,0 +1,30 @@
import { OrigamiFiles } from "@weborigami/language";
import os from "node:os";
import path from "node:path";
import process from "node:process";
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
/**
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
*
* @this {AsyncTree|null}
* @param {string[]} keys
*/
export default async function files(...keys) {
assertTreeIsDefined(this, "files:");
// If path begins with `~`, treat it relative to the home directory.
// Otherwise, treat it relative to the current working directory.
let relativePath = keys.join(path.sep);
let basePath;
if (relativePath.startsWith("~")) {
basePath = os.homedir();
relativePath = relativePath.slice(2);
} else {
basePath = process.cwd();
}
const resolved = path.resolve(basePath, relativePath);
const result = new OrigamiFiles(resolved);
return result;
}

18
node_modules/@weborigami/origami/src/protocols/http.js generated vendored Normal file
View File

@@ -0,0 +1,18 @@
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
import constructHref from "../common/constructHref.js";
import fetchAndHandleExtension from "../common/fetchAndHandleExtension.js";
/**
* Retrieve the indicated web resource via HTTP.
*
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
*
* @this {AsyncTree|null}
* @param {string} host
* @param {...string} keys
*/
export default async function http(host, ...keys) {
assertTreeIsDefined(this, "http:");
const href = constructHref("http:", host, ...keys);
return fetchAndHandleExtension.call(this, href);
}

View File

@@ -0,0 +1,18 @@
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
import constructHref from "../common/constructHref.js";
import fetchAndHandleExtension from "../common/fetchAndHandleExtension.js";
/**
* Retrieve the indicated web resource via HTTPS.
*
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
*
* @this {AsyncTree|null}
* @param {string} host
* @param {...string} keys
*/
export default async function https(host, ...keys) {
assertTreeIsDefined(this, "https:");
const href = constructHref("https:", host, ...keys);
return fetchAndHandleExtension.call(this, href);
}

View File

@@ -0,0 +1,19 @@
import { SiteTree } from "@weborigami/async-tree";
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
import constructSiteTree from "../common/constructSiteTree.js";
/**
* Return a website tree via HTTPS.
*
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
* @typedef {import("../../index.ts").Invocable} Invocable
*
* @this {AsyncTree|null}
* @param {string} host
* @param {...string} keys
*/
export default function httpstree(host, ...keys) {
assertTreeIsDefined(this, "treehttps:");
return constructSiteTree("https:", SiteTree, this, host, ...keys);
}

View File

@@ -0,0 +1,19 @@
import { SiteTree } from "@weborigami/async-tree";
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
import constructSiteTree from "../common/constructSiteTree.js";
/**
* Return a website tree via HTTP.
*
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
* @typedef {import("../../index.ts").Invocable} Invocable
*
* @this {AsyncTree|null}
* @param {string} host
* @param {...string} keys
*/
export default function httptree(host, ...keys) {
assertTreeIsDefined(this, "httptree:");
return constructSiteTree("http:", SiteTree, this, host, ...keys);
}

View File

@@ -0,0 +1,18 @@
import { Tree } from "@weborigami/async-tree";
import { ops } from "@weborigami/language";
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
/**
* Return the inherited value (if any) for the indicated key.
*
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
*
* @this {AsyncTree|null}
* @param {string[]} keys
*/
export default async function inherited(...keys) {
assertTreeIsDefined(this, "inherited:");
const key = keys.shift();
const value = await ops.inherited.call(this, key);
return keys.length > 0 ? await Tree.traverse(value, ...keys) : value;
}

42
node_modules/@weborigami/origami/src/protocols/new.js generated vendored Normal file
View File

@@ -0,0 +1,42 @@
import { isUnpackable, scope as scopeFn, Tree } from "@weborigami/async-tree";
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
/**
* Find the indicated class constructor in scope, then return a function which
* invokes it with `new`.
*
* This can also take a single argument that is a class.
*
* @this {import("@weborigami/types").AsyncTree|null}
* @param {...any} keys
*/
export default async function instantiate(...keys) {
assertTreeIsDefined(this, "new:");
let constructor;
const scope = this ? scopeFn(this) : null;
if (
keys.length === 1 &&
(typeof keys[0] === "object" || typeof keys[0] === "function")
) {
constructor = keys[0];
} else if (scope) {
constructor = await Tree.traverseOrThrow(scope, ...keys);
} else {
throw new TypeError(`new: The scope isn't defined.`);
}
if (isUnpackable(constructor)) {
constructor = await constructor.unpack();
}
// Origami may pass `undefined` as the first argument to the constructor. We
// don't pass that along, because constructors like `Date` don't like it.
return (...args) => {
const object =
args.length === 1 && args[0] === undefined
? new constructor()
: new constructor(...args);
if (Tree.isAsyncTree(object)) {
object.parent = scope;
}
return object;
};
}

Some files were not shown because too many files have changed in this diff Show More