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

View File

@@ -0,0 +1,153 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import BrowserFileTree from "../../src/drivers/BrowserFileTree.js";
import { Tree } from "../../src/internal.js";
// Skip these tests if we're not in a browser.
const isBrowser = typeof window !== "undefined";
if (isBrowser) {
describe("BrowserFileTree", async () => {
test("can get the keys of the tree", async () => {
const fixture = await createFixture();
assert.deepEqual(Array.from(await fixture.keys()), [
"Alice.md",
"Bob.md",
"Carol.md",
"subfolder/",
]);
});
test("can get the value for a key", async () => {
const fixture = await createFixture();
const buffer = await fixture.get("Alice.md");
assert.equal(text(buffer), "Hello, **Alice**.");
});
test("getting an unsupported key returns undefined", async () => {
const fixture = await createFixture();
assert.equal(await fixture.get("xyz"), undefined);
});
test("getting empty key returns undefined", async () => {
const fixture = await createFixture();
assert.equal(await fixture.get(""), undefined);
});
test("getting a null/undefined key throws an exception", async () => {
const fixture = await createFixture();
await assert.rejects(async () => {
await fixture.get(null);
});
await assert.rejects(async () => {
await fixture.get(undefined);
});
});
test("sets parent on subtrees", async () => {
const fixture = await createFixture();
const subfolder = await fixture.get("subfolder");
assert.equal(subfolder.parent, fixture);
});
test("can retrieve values with optional trailing slash", async () => {
const fixture = await createFixture();
assert(await fixture.get("Alice.md"));
assert(await fixture.get("Alice.md/"));
assert(await fixture.get("subfolder"));
assert(await fixture.get("subfolder/"));
});
test("can set a value", async () => {
const fixture = await createFixture();
// Update existing key.
await fixture.set("Alice.md", "Goodbye, **Alice**.");
// New key.
await fixture.set("David.md", "Hello, **David**.");
// Delete key.
await fixture.set("Bob.md", undefined);
// Delete non-existent key.
await fixture.set("xyz", undefined);
assert.deepEqual(await strings(fixture), {
"Alice.md": "Goodbye, **Alice**.",
"Carol.md": "Hello, **Carol**.",
"David.md": "Hello, **David**.",
subfolder: {},
});
});
test("can create a subfolder via set", async () => {
const fixture = await createFixture();
const tree = {
async get(key) {
const name = key.replace(/\.md$/, "");
return `Hello, **${name}**.`;
},
async keys() {
return ["Ellen.md"];
},
};
await fixture.set("more", tree);
assert.deepEqual(await strings(fixture), {
"Alice.md": "Hello, **Alice**.",
"Bob.md": "Hello, **Bob**.",
"Carol.md": "Hello, **Carol**.",
more: {
"Ellen.md": "Hello, **Ellen**.",
},
subfolder: {},
});
});
});
}
async function createFile(directory, name, contents) {
const file = await directory.getFileHandle(name, { create: true });
const writable = await file.createWritable();
await writable.write(contents);
await writable.close();
}
let count = 0;
async function createFixture() {
const root = await navigator.storage.getDirectory();
const directory = await root.getDirectoryHandle("async-tree", {
create: true,
});
// Create a new subdirectory for each test.
const subdirectoryName = `test${count++}`;
// Delete any pre-existing subdirectory with that name.
try {
await directory.removeEntry(subdirectoryName, { recursive: true });
} catch (e) {
// Ignore errors.
}
const subdirectory = await directory.getDirectoryHandle(subdirectoryName, {
create: true,
});
await createFile(subdirectory, "Alice.md", "Hello, **Alice**.");
await createFile(subdirectory, "Bob.md", "Hello, **Bob**.");
await createFile(subdirectory, "Carol.md", "Hello, **Carol**.");
await subdirectory.getDirectoryHandle("subfolder", {
create: true,
});
return new BrowserFileTree(subdirectory);
}
async function strings(tree) {
return Tree.plain(Tree.map(tree, (value) => text(value)));
}
function text(arrayBuffer) {
return new TextDecoder().decode(arrayBuffer);
}

View File

@@ -0,0 +1,17 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import DeepMapTree from "../../src/drivers/DeepMapTree.js";
import { Tree } from "../../src/internal.js";
describe("DeepMapTree", () => {
test("returns a DeepMapTree for value that's a Map", async () => {
const tree = new DeepMapTree([
["a", 1],
["map", new Map([["b", 2]])],
]);
const map = await tree.get("map");
assert.equal(map instanceof DeepMapTree, true);
assert.deepEqual(await Tree.plain(map), { b: 2 });
assert.equal(map.parent, tree);
});
});

View File

@@ -0,0 +1,35 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import { DeepObjectTree, Tree } from "../../src/internal.js";
describe("DeepObjectTree", () => {
test("returns an ObjectTree for value that's a plain sub-object or sub-array", async () => {
const tree = createFixture();
const object = await tree.get("object");
assert.equal(object instanceof DeepObjectTree, true);
assert.deepEqual(await Tree.plain(object), { b: 2 });
assert.equal(object.parent, tree);
const array = await tree.get("array");
assert.equal(array instanceof DeepObjectTree, true);
assert.deepEqual(await Tree.plain(array), [3]);
assert.equal(array.parent, tree);
});
test("adds trailing slashes to keys for subtrees including plain objects or arrays", async () => {
const tree = createFixture();
const keys = Array.from(await tree.keys());
assert.deepEqual(keys, ["a", "object/", "array/"]);
});
});
function createFixture() {
return new DeepObjectTree({
a: 1,
object: {
b: 2,
},
array: [3],
});
}

View File

@@ -0,0 +1,22 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import DeferredTree from "../../src/drivers/DeferredTree.js";
import { ObjectTree, Tree } from "../../src/internal.js";
describe("DeferredTree", () => {
test("lazy-loads a treelike object", async () => {
const tree = new DeferredTree(async () => ({ a: 1, b: 2, c: 3 }));
assert.deepEqual(await Tree.plain(tree), { a: 1, b: 2, c: 3 });
});
test("sets parent on subtrees", async () => {
const object = {
a: 1,
};
const parent = new ObjectTree({});
const fixture = new DeferredTree(() => object);
fixture.parent = parent;
const tree = await fixture.tree();
assert.equal(tree.parent, parent);
});
});

View File

@@ -0,0 +1,116 @@
import assert from "node:assert";
import { beforeEach, describe, mock, test } from "node:test";
import ExplorableSiteTree from "../../src/drivers/ExplorableSiteTree.js";
import { Tree } from "../../src/internal.js";
const textDecoder = new TextDecoder();
const textEncoder = new TextEncoder();
const mockHost = "https://mock";
const mockResponses = {
"/.keys.json": {
data: JSON.stringify(["about/", "index.html"]),
},
"/about": {
redirected: true,
status: 301,
url: "https://mock/about/",
},
"/about/.keys.json": {
data: JSON.stringify(["Alice.html", "Bob.html", "Carol.html"]),
},
"/about/Alice.html": {
data: "Hello, Alice!",
},
"/about/Bob.html": {
data: "Hello, Bob!",
},
"/about/Carol.html": {
data: "Hello, Carol!",
},
"/index.html": {
data: "Home page",
},
};
describe("ExplorableSiteTree", () => {
beforeEach(() => {
mock.method(global, "fetch", mockFetch);
});
test("can get the keys of a tree", async () => {
const fixture = new ExplorableSiteTree(mockHost);
const keys = await fixture.keys();
assert.deepEqual(Array.from(keys), ["about/", "index.html"]);
});
test("can get a plain value for a key", async () => {
const fixture = new ExplorableSiteTree(mockHost);
const arrayBuffer = await fixture.get("index.html");
const text = textDecoder.decode(arrayBuffer);
assert.equal(text, "Home page");
});
test("getting an unsupported key returns undefined", async () => {
const fixture = new ExplorableSiteTree(mockHost);
assert.equal(await fixture.get("xyz"), undefined);
});
test("getting a null/undefined key throws an exception", async () => {
const fixture = new ExplorableSiteTree(mockHost);
await assert.rejects(async () => {
await fixture.get(null);
});
await assert.rejects(async () => {
await fixture.get(undefined);
});
});
test("can return a new tree for a key that redirects", async () => {
const fixture = new ExplorableSiteTree(mockHost);
const about = await fixture.get("about");
assert(about instanceof ExplorableSiteTree);
assert.equal(about.href, "https://mock/about/");
});
test("can convert a site to a plain object", async () => {
const fixture = new ExplorableSiteTree(mockHost);
// Convert buffers to strings.
const strings = Tree.map(fixture, {
deep: true,
value: (value) => textDecoder.decode(value),
});
assert.deepEqual(await Tree.plain(strings), {
about: {
"Alice.html": "Hello, Alice!",
"Bob.html": "Hello, Bob!",
"Carol.html": "Hello, Carol!",
},
"index.html": "Home page",
});
});
});
async function mockFetch(href) {
if (!href.startsWith(mockHost)) {
return { status: 404 };
}
const path = href.slice(mockHost.length);
const mockedResponse = mockResponses[path];
if (mockedResponse) {
return Object.assign(
{
arrayBuffer: () => textEncoder.encode(mockedResponse.data).buffer,
ok: true,
status: 200,
text: () => mockedResponse.data,
},
mockedResponse
);
}
return {
ok: false,
status: 404,
};
}

View File

@@ -0,0 +1,192 @@
import assert from "node:assert";
import * as fs from "node:fs/promises";
import path from "node:path";
import { describe, test } from "node:test";
import { fileURLToPath } from "node:url";
import FileTree from "../../src/drivers/FileTree.js";
import { ObjectTree, Tree } from "../../src/internal.js";
const dirname = path.dirname(fileURLToPath(import.meta.url));
const tempDirectory = path.join(dirname, "fixtures/temp");
const textDecoder = new TextDecoder();
describe("FileTree", async () => {
test("can get the keys of the tree", async () => {
const fixture = createFixture("fixtures/markdown");
assert.deepEqual(Array.from(await fixture.keys()), [
"Alice.md",
"Bob.md",
"Carol.md",
"subfolder/",
]);
});
test("can get the value for a key", async () => {
const fixture = createFixture("fixtures/markdown");
const buffer = await fixture.get("Alice.md");
const text = textDecoder.decode(buffer);
assert.equal(text, "Hello, **Alice**.");
});
test("getting an unsupported key returns undefined", async () => {
const fixture = createFixture("fixtures/markdown");
assert.equal(await fixture.get("xyz"), undefined);
});
test("getting empty key returns undefined", async () => {
const fixture = createFixture("fixtures/markdown");
assert.equal(await fixture.get(""), undefined);
});
test("getting a null/undefined key throws an exception", async () => {
const fixture = createFixture("fixtures/markdown");
await assert.rejects(async () => {
await fixture.get(null);
});
await assert.rejects(async () => {
await fixture.get(undefined);
});
});
test("can retrieve values with optional trailing slash", async () => {
const fixture = createFixture("fixtures/markdown");
assert(await fixture.get("Alice.md"));
assert(await fixture.get("Alice.md/"));
assert(await fixture.get("subfolder"));
assert(await fixture.get("subfolder/"));
});
test("sets parent on subtrees", async () => {
const fixture = createFixture("fixtures");
const markdown = await fixture.get("markdown");
assert.equal(markdown.parent, fixture);
});
test("can write out a file via set()", async () => {
await createTempDirectory();
// Write out a file.
const fileName = "file1";
const fileText = "This is the first file.";
const tempFiles = new FileTree(tempDirectory);
await tempFiles.set(fileName, fileText);
// Read it back in.
const filePath = path.join(tempDirectory, fileName);
const actualText = String(await fs.readFile(filePath));
assert.equal(fileText, actualText);
await removeTempDirectory();
});
test("create subfolder via set() with empty object value", async () => {
await createTempDirectory();
// Write out new, empty folder called "empty".
const tempFiles = new FileTree(tempDirectory);
await tempFiles.set("empty", {});
// Verify folder exists and has no contents.
const folderPath = path.join(tempDirectory, "empty");
const stats = await fs.stat(folderPath);
assert(stats.isDirectory());
const files = await fs.readdir(folderPath);
assert.deepEqual(files, []);
await removeTempDirectory();
});
test("create subfolder via set() with empty tree value", async () => {
await createTempDirectory();
// Write out new, empty folder called "empty".
const tempFiles = new FileTree(tempDirectory);
await tempFiles.set("empty", new ObjectTree({}));
// Verify folder exists and has no contents.
const folderPath = path.join(tempDirectory, "empty");
const stats = await fs.stat(folderPath);
assert(stats.isDirectory());
const files = await fs.readdir(folderPath);
assert.deepEqual(files, []);
await removeTempDirectory();
});
test("can write out subfolder via set()", async () => {
await createTempDirectory();
// Create a tiny set of "files".
const obj = {
file1: "This is the first file.",
subfolder: {
file2: "This is the second file.",
},
};
// Write out files as a new folder called "folder".
const tempFiles = new FileTree(tempDirectory);
await tempFiles.set("folder", obj);
// Read them back in.
const actualFiles = await tempFiles.get("folder");
const strings = Tree.map(actualFiles, {
deep: true,
value: (buffer) => textDecoder.decode(buffer),
});
const plain = await Tree.plain(strings);
assert.deepEqual(plain, obj);
await removeTempDirectory();
});
test("can delete a file via set()", async () => {
await createTempDirectory();
const tempFile = path.join(tempDirectory, "file");
await fs.writeFile(tempFile, "");
const tempFiles = new FileTree(tempDirectory);
await tempFiles.set("file", undefined);
let stats;
try {
stats = await fs.stat(tempFile);
} catch (/** @type {any} */ error) {
if (error.code !== "ENOENT") {
throw error;
}
}
assert(stats === undefined);
await removeTempDirectory();
});
test("can delete a folder via set()", async () => {
await createTempDirectory();
const folder = path.join(tempDirectory, "folder");
await fs.mkdir(folder);
const tempFiles = new FileTree(tempDirectory);
await tempFiles.set("folder", undefined);
let stats;
try {
stats = await fs.stat(folder);
} catch (/** @type {any} */ error) {
if (error.code !== "ENOENT") {
throw error;
}
}
assert(stats === undefined);
await removeTempDirectory();
});
});
function createFixture(fixturePath) {
return new FileTree(path.join(dirname, fixturePath));
}
async function createTempDirectory() {
await fs.mkdir(tempDirectory, { recursive: true });
}
async function removeTempDirectory() {
await fs.rm(tempDirectory, { recursive: true });
}

View File

@@ -0,0 +1,46 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import FunctionTree from "../../src/drivers/FunctionTree.js";
describe("FunctionTree", async () => {
test("can get the keys of the tree", async () => {
const fixture = createFixture();
assert.deepEqual(Array.from(await fixture.keys()), [
"Alice.md",
"Bob.md",
"Carol.md",
]);
});
test("can get the value for a key", async () => {
const fixture = createFixture();
const alice = await fixture.get("Alice.md");
assert.equal(alice, "Hello, **Alice**.");
});
test("getting a value from function with multiple arguments curries the function", async () => {
const fixture = new FunctionTree((a, b, c) => a + b + c);
const fnA = await fixture.get(1);
const fnAB = await fnA.get(2);
const result = await fnAB.get(3);
assert.equal(result, 6);
});
test("getting an unsupported key returns undefined", async () => {
const fixture = createFixture();
assert.equal(await fixture.get("xyz"), undefined);
});
});
function createFixture() {
return new FunctionTree(
(key) => {
if (key?.endsWith?.(".md")) {
const name = key.slice(0, -3);
return `Hello, **${name}**.`;
}
return undefined;
},
["Alice.md", "Bob.md", "Carol.md"]
);
}

View File

@@ -0,0 +1,59 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import MapTree from "../../src/drivers/MapTree.js";
import * as symbols from "../../src/symbols.js";
describe("MapTree", () => {
test("can get the keys of the tree", async () => {
const fixture = createFixture();
assert.deepEqual(Array.from(await fixture.keys()), ["a", "b", "c"]);
});
test("can get the value for a key", async () => {
const fixture = createFixture();
const a = await fixture.get("a");
assert.equal(a, 1);
});
test("sets parent on subtrees", async () => {
const map = new Map([["more", new Map([["a", 1]])]]);
const fixture = new MapTree(map);
const more = await fixture.get("more");
assert.equal(more[symbols.parent], fixture);
});
test("adds trailing slashes to keys for subtrees", async () => {
const tree = new MapTree([
["a", 1],
["subtree", new MapTree([["b", 2]])],
]);
const keys = Array.from(await tree.keys());
assert.deepEqual(keys, ["a", "subtree/"]);
});
test("can retrieve values with optional trailing slash", async () => {
const subtree = new MapTree([["b", 2]]);
const tree = new MapTree([
["a", 1],
["subtree", subtree],
]);
assert.equal(await tree.get("a"), 1);
assert.equal(await tree.get("a/"), 1);
assert.equal(await tree.get("subtree"), subtree);
assert.equal(await tree.get("subtree/"), subtree);
});
test("getting an unsupported key returns undefined", async () => {
const fixture = createFixture();
assert.equal(await fixture.get("d"), undefined);
});
});
function createFixture() {
const map = new Map([
["a", 1],
["b", 2],
["c", 3],
]);
return new MapTree(map);
}

View File

@@ -0,0 +1,156 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import { ObjectTree, Tree } from "../../src/internal.js";
import * as symbols from "../../src/symbols.js";
describe("ObjectTree", () => {
test("can get the keys of the tree", async () => {
const fixture = createFixture();
assert.deepEqual(Array.from(await fixture.keys()), [
"Alice.md",
"Bob.md",
"Carol.md",
]);
});
test("can get the value for a key", async () => {
const fixture = createFixture();
const alice = await fixture.get("Alice.md");
assert.equal(alice, "Hello, **Alice**.");
});
test("getting an unsupported key returns undefined", async () => {
const fixture = createFixture();
assert.equal(await fixture.get("xyz"), undefined);
});
test("getting a null/undefined key throws an exception", async () => {
const fixture = createFixture();
await assert.rejects(async () => {
await fixture.get(null);
});
await assert.rejects(async () => {
await fixture.get(undefined);
});
});
test("can set a value", async () => {
const tree = new ObjectTree({
a: 1,
b: 2,
c: 3,
});
// Update existing key
await tree.set("a", 4);
// Delete key
await tree.set("b", undefined);
// Overwrite key with trailing slash
await tree.set("c/", {});
// New key
await tree.set("d", 5);
assert.deepEqual(await Tree.entries(tree), [
["a", 4],
["c/", {}],
["d", 5],
]);
});
test("can wrap a class instance", async () => {
class Foo {
constructor() {
this.a = 1;
}
get prop() {
return this._prop;
}
set prop(prop) {
this._prop = prop;
}
}
class Bar extends Foo {
method() {}
}
const bar = new Bar();
/** @type {any} */ (bar).extra = "Hello";
const fixture = new ObjectTree(bar);
assert.deepEqual(await Tree.entries(fixture), [
["a", 1],
["extra", "Hello"],
["prop", undefined],
]);
assert.equal(await fixture.get("a"), 1);
await fixture.set("prop", "Goodbye");
assert.equal(bar.prop, "Goodbye");
assert.equal(await fixture.get("prop"), "Goodbye");
});
test("sets parent symbol on subobjects", async () => {
const fixture = new ObjectTree({
sub: {},
});
const sub = await fixture.get("sub");
assert.equal(sub[symbols.parent], fixture);
});
test("sets parent on subtrees", async () => {
const fixture = new ObjectTree({
a: 1,
more: new ObjectTree({
b: 2,
}),
});
const more = await fixture.get("more");
assert.equal(more.parent, fixture);
});
test("adds trailing slashes to keys for subtrees", async () => {
const tree = new ObjectTree({
a1: 1,
a2: new ObjectTree({
b1: 2,
}),
a3: 3,
a4: new ObjectTree({
b2: 4,
}),
});
const keys = Array.from(await tree.keys());
assert.deepEqual(keys, ["a1", "a2/", "a3", "a4/"]);
});
test("can retrieve values with optional trailing slash", async () => {
const subtree = {
async get(key) {},
async keys() {},
};
const tree = new ObjectTree({
a: 1,
subtree,
});
assert.equal(await tree.get("a"), 1);
assert.equal(await tree.get("a/"), 1);
assert.equal(await tree.get("subtree"), subtree);
assert.equal(await tree.get("subtree/"), subtree);
});
test("method on an object is bound to the object", async () => {
const n = new Number(123);
const tree = new ObjectTree(n);
const method = await tree.get("toString");
assert.equal(method(), "123");
});
});
function createFixture() {
return new ObjectTree({
"Alice.md": "Hello, **Alice**.",
"Bob.md": "Hello, **Bob**.",
"Carol.md": "Hello, **Carol**.",
});
}

View File

@@ -0,0 +1,44 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import SetTree from "../../src/drivers/SetTree.js";
import { ObjectTree } from "../../src/internal.js";
describe("SetTree", () => {
test("can get the keys of the tree", async () => {
const set = new Set(["a", "b", "c"]);
const fixture = new SetTree(set);
assert.deepEqual(Array.from(await fixture.keys()), [0, 1, 2]);
});
test("can get the value for a key", async () => {
const set = new Set(["a", "b", "c"]);
const fixture = new SetTree(set);
const a = await fixture.get(0);
assert.equal(a, "a");
});
test("getting an unsupported key returns undefined", async () => {
const set = new Set(["a", "b", "c"]);
const fixture = new SetTree(set);
assert.equal(await fixture.get(3), undefined);
});
test("getting a null/undefined key throws an exception", async () => {
const set = new Set(["a", "b", "c"]);
const fixture = new SetTree(set);
await assert.rejects(async () => {
await fixture.get(null);
});
await assert.rejects(async () => {
await fixture.get(undefined);
});
});
test("sets parent on subtrees", async () => {
const set = new Set();
set.add(new ObjectTree({}));
const fixture = new SetTree(set);
const subtree = await fixture.get(0);
assert.equal(subtree.parent, fixture);
});
});

View File

@@ -0,0 +1,92 @@
import assert from "node:assert";
import { beforeEach, describe, mock, test } from "node:test";
import SiteTree from "../../src/drivers/SiteTree.js";
const textDecoder = new TextDecoder();
const textEncoder = new TextEncoder();
const mockHost = "https://mock";
const mockResponses = {
"/about": {
redirected: true,
status: 301,
url: "https://mock/about/",
},
"/about/Alice.html": {
data: "Hello, Alice!",
},
"/about/Bob.html": {
data: "Hello, Bob!",
},
"/about/Carol.html": {
data: "Hello, Carol!",
},
"/index.html": {
data: "Home page",
},
};
describe("SiteTree", () => {
beforeEach(() => {
mock.method(global, "fetch", mockFetch);
});
test("returns an empty array as the keys of a tree", async () => {
const fixture = new SiteTree(mockHost);
const keys = await fixture.keys();
assert.deepEqual(Array.from(keys), []);
});
test("can get a plain value for a key", async () => {
const fixture = new SiteTree(mockHost);
const arrayBuffer = await fixture.get("index.html");
const text = textDecoder.decode(arrayBuffer);
assert.equal(text, "Home page");
});
test("immediately return a new tree for a key with a trailing slash", async () => {
const fixture = new SiteTree(mockHost);
const about = await fixture.get("about/");
assert(about instanceof SiteTree);
assert.equal(about.href, "https://mock/about/");
});
test("getting an unsupported key returns undefined", async () => {
const fixture = new SiteTree(mockHost);
assert.equal(await fixture.get("xyz"), undefined);
});
test("getting a null/undefined key throws an exception", async () => {
const fixture = new SiteTree(mockHost);
await assert.rejects(async () => {
await fixture.get(null);
});
await assert.rejects(async () => {
await fixture.get(undefined);
});
});
});
async function mockFetch(href) {
if (!href.startsWith(mockHost)) {
return { status: 404 };
}
const path = href.slice(mockHost.length);
const mockedResponse = mockResponses[path];
if (mockedResponse) {
return Object.assign(
{
arrayBuffer: () => textEncoder.encode(mockedResponse.data).buffer,
ok: true,
status: 200,
text: () => mockedResponse.data,
},
mockedResponse
);
}
return {
ok: false,
status: 404,
};
}

View File

@@ -0,0 +1,121 @@
import assert from "node:assert";
import { describe, test } from "node:test";
import calendar from "../../src/drivers/calendarTree.js";
import { toPlainValue } from "../../src/utilities.js";
describe("calendarTree", () => {
test("without a start or end, returns a tree for today", async () => {
const tree = calendar({
value: (year, month, day) => `${year}-${month}-${day}`,
});
const plain = await toPlainValue(tree);
const today = new Date();
const year = today.getFullYear();
const month = (today.getMonth() + 1).toString().padStart(2, "0");
const day = today.getDate().toString().padStart(2, "0");
assert.deepEqual(plain, {
[year]: {
[month]: {
[day]: `${year}-${month}-${day}`,
},
},
});
});
test("returns a tree for a month range", async () => {
const tree = calendar({
start: "2025-01",
end: "2025-02",
value: (year, month, day) => `${year}-${month}-${day}`,
});
const plain = await toPlainValue(tree);
assert.deepEqual(plain, {
2025: {
"01": {
"01": "2025-01-01",
"02": "2025-01-02",
"03": "2025-01-03",
"04": "2025-01-04",
"05": "2025-01-05",
"06": "2025-01-06",
"07": "2025-01-07",
"08": "2025-01-08",
"09": "2025-01-09",
10: "2025-01-10",
11: "2025-01-11",
12: "2025-01-12",
13: "2025-01-13",
14: "2025-01-14",
15: "2025-01-15",
16: "2025-01-16",
17: "2025-01-17",
18: "2025-01-18",
19: "2025-01-19",
20: "2025-01-20",
21: "2025-01-21",
22: "2025-01-22",
23: "2025-01-23",
24: "2025-01-24",
25: "2025-01-25",
26: "2025-01-26",
27: "2025-01-27",
28: "2025-01-28",
29: "2025-01-29",
30: "2025-01-30",
31: "2025-01-31",
},
"02": {
"01": "2025-02-01",
"02": "2025-02-02",
"03": "2025-02-03",
"04": "2025-02-04",
"05": "2025-02-05",
"06": "2025-02-06",
"07": "2025-02-07",
"08": "2025-02-08",
"09": "2025-02-09",
10: "2025-02-10",
11: "2025-02-11",
12: "2025-02-12",
13: "2025-02-13",
14: "2025-02-14",
15: "2025-02-15",
16: "2025-02-16",
17: "2025-02-17",
18: "2025-02-18",
19: "2025-02-19",
20: "2025-02-20",
21: "2025-02-21",
22: "2025-02-22",
23: "2025-02-23",
24: "2025-02-24",
25: "2025-02-25",
26: "2025-02-26",
27: "2025-02-27",
28: "2025-02-28",
},
},
});
});
test("returns a tree for a day range", async () => {
const tree = calendar({
start: "2025-02-27",
end: "2025-03-02",
value: (year, month, day) => `${year}-${month}-${day}`,
});
const plain = await toPlainValue(tree);
assert.deepEqual(plain, {
2025: {
"02": {
27: "2025-02-27",
28: "2025-02-28",
},
"03": {
"01": "2025-03-01",
"02": "2025-03-02",
},
},
});
});
});

View File

@@ -0,0 +1 @@
Hello, **Alice**.

View File

@@ -0,0 +1 @@
Hello, **Bob**.

View File

@@ -0,0 +1 @@
Hello, **Carol**.

View File

@@ -0,0 +1 @@
This file exists to force the creation of the parent folder.