@davidsouther/jiffies 2026.4.0 → 2026.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/README.md +0 -3
  2. package/package.json +13 -6
  3. package/src/404.html +1 -1
  4. package/src/components/accordion.ts +25 -0
  5. package/src/components/alert.ts +47 -0
  6. package/src/components/card.ts +54 -0
  7. package/src/components/children.ts +11 -0
  8. package/src/components/form.ts +25 -0
  9. package/src/components/index.ts +22 -0
  10. package/src/components/link.ts +22 -0
  11. package/src/components/modal.ts +15 -0
  12. package/src/components/nav.ts +42 -0
  13. package/src/components/property.ts +32 -0
  14. package/src/components/tabs.ts +82 -0
  15. package/src/components/virtual_scroll.ts +1 -1
  16. package/src/dom/README.md +8 -3
  17. package/src/dom/SKILL.md +201 -0
  18. package/src/dom/dom.ts +192 -41
  19. package/src/dom/fc.ts +7 -3
  20. package/src/dom/form/form.app.ts +35 -41
  21. package/src/dom/form/form.ts +79 -10
  22. package/src/dom/form/index.html +2 -2
  23. package/src/dom/html.ts +1 -1
  24. package/src/dom/hydrate.ts +206 -0
  25. package/src/dom/navigation/index.ts +349 -0
  26. package/src/dom/render.ts +41 -0
  27. package/src/dom/router/router.ts +1 -1
  28. package/src/dom/svg.ts +6 -2
  29. package/src/fs_node.ts +2 -2
  30. package/src/log.ts +154 -2
  31. package/src/server/http/response.ts +6 -3
  32. package/src/server/http/sitemap.ts +10 -34
  33. package/src/server/http/static.ts +0 -2
  34. package/src/server/live-reload.ts +208 -0
  35. package/src/server/main.ts +14 -7
  36. package/src/server/ws/frame.ts +36 -0
  37. package/src/server/ws/handshake.ts +42 -0
  38. package/src/server/ws/index.ts +100 -0
  39. package/src/ssg/bundle.ts +85 -0
  40. package/src/ssg/copy-public.ts +44 -0
  41. package/src/ssg/discover.ts +143 -0
  42. package/src/ssg/main.ts +168 -0
  43. package/src/ssg/rewrite.ts +18 -0
  44. package/src/ssg/ssg.ts +134 -0
  45. package/src/components/test.ts +0 -5
  46. package/src/components/virtual_scroll.test.ts +0 -30
  47. package/src/context.test.ts +0 -58
  48. package/src/context.ts +0 -67
  49. package/src/diff.test.ts +0 -48
  50. package/src/dom/fc.test.ts +0 -43
  51. package/src/dom/form/form.test.ts +0 -0
  52. package/src/dom/html.test.ts +0 -74
  53. package/src/dom/observable.test.ts +0 -43
  54. package/src/dom/test.ts +0 -11
  55. package/src/equal.test.ts +0 -23
  56. package/src/flags.test.ts +0 -43
  57. package/src/flags.ts +0 -53
  58. package/src/fs.test.ts +0 -106
  59. package/src/fs_win.test.ts +0 -11
  60. package/src/generator.test.ts +0 -27
  61. package/src/index.html +0 -82
  62. package/src/is_browser.js +0 -1
  63. package/src/lock.test.ts +0 -17
  64. package/src/observable/observable.test.ts +0 -73
  65. package/src/pico/_variables.scss +0 -66
  66. package/src/pico/components/_accordion.scss +0 -112
  67. package/src/pico/components/_button-group.scss +0 -51
  68. package/src/pico/components/_card.scss +0 -47
  69. package/src/pico/components/_dropdown.scss +0 -203
  70. package/src/pico/components/_modal.scss +0 -181
  71. package/src/pico/components/_nav.scss +0 -79
  72. package/src/pico/components/_progress.scss +0 -70
  73. package/src/pico/components/_property.scss +0 -34
  74. package/src/pico/content/_button.scss +0 -152
  75. package/src/pico/content/_code.scss +0 -63
  76. package/src/pico/content/_embedded.scss +0 -0
  77. package/src/pico/content/_form-alt.scss +0 -276
  78. package/src/pico/content/_form.scss +0 -259
  79. package/src/pico/content/_misc.scss +0 -0
  80. package/src/pico/content/_table.scss +0 -28
  81. package/src/pico/content/_toggle.scss +0 -132
  82. package/src/pico/content/_typography.scss +0 -232
  83. package/src/pico/layout/_container.scss +0 -40
  84. package/src/pico/layout/_document.scss +0 -0
  85. package/src/pico/layout/_flex.scss +0 -46
  86. package/src/pico/layout/_grid.scss +0 -24
  87. package/src/pico/layout/_scroller.scss +0 -16
  88. package/src/pico/layout/_section.scss +0 -8
  89. package/src/pico/layout/_sectioning.scss +0 -55
  90. package/src/pico/pico.scss +0 -60
  91. package/src/pico/reset/_accessibility.scss +0 -34
  92. package/src/pico/reset/_button.scss +0 -17
  93. package/src/pico/reset/_code.scss +0 -15
  94. package/src/pico/reset/_document.scss +0 -48
  95. package/src/pico/reset/_embedded.scss +0 -39
  96. package/src/pico/reset/_form.scss +0 -97
  97. package/src/pico/reset/_misc.scss +0 -23
  98. package/src/pico/reset/_nav.scss +0 -5
  99. package/src/pico/reset/_progress.scss +0 -4
  100. package/src/pico/reset/_table.scss +0 -8
  101. package/src/pico/reset/_typography.scss +0 -25
  102. package/src/pico/themes/default/_colors.scss +0 -65
  103. package/src/pico/themes/default/_dark.scss +0 -148
  104. package/src/pico/themes/default/_light.scss +0 -149
  105. package/src/pico/themes/default/_styles.scss +0 -272
  106. package/src/pico/themes/default.scss +0 -34
  107. package/src/pico/utilities/_accessibility.scss +0 -3
  108. package/src/pico/utilities/_loading.scss +0 -52
  109. package/src/pico/utilities/_reduce-motion.scss +0 -27
  110. package/src/pico/utilities/_tooltip.scss +0 -101
  111. package/src/result.test.ts +0 -101
  112. package/src/scope/describe.ts +0 -81
  113. package/src/scope/display/console.ts +0 -26
  114. package/src/scope/display/dom.ts +0 -36
  115. package/src/scope/display/junit.ts +0 -64
  116. package/src/scope/execute.ts +0 -110
  117. package/src/scope/expect.ts +0 -169
  118. package/src/scope/fix.ts +0 -30
  119. package/src/scope/index.ts +0 -11
  120. package/src/scope/scope.ts +0 -21
  121. package/src/scope/state.ts +0 -13
  122. package/src/test.mjs +0 -33
  123. package/src/test_all.ts +0 -35
@@ -1,36 +0,0 @@
1
- import { isHTMLLogger, makeHTMLLogger } from "../../components/logger.ts";
2
- import type { DOMElement } from "../../dom/dom.ts";
3
- import { DEFAULT_LOGGER, LEVEL } from "../../log.ts";
4
- import { getTotalCases } from "../describe.ts";
5
- import { flattenResults } from "../execute.ts";
6
- import type { TestResult } from "../scope.ts";
7
-
8
- export function displayStatistics(
9
- results: TestResult,
10
- root: DOMElement = document.body as DOMElement,
11
- ) {
12
- const { executed, failed } = results;
13
- const logger = (() => {
14
- try {
15
- return makeHTMLLogger(
16
- `Executed ${executed} of ${getTotalCases()}; ${failed} failed.`,
17
- );
18
- } catch (_e) {
19
- return DEFAULT_LOGGER;
20
- }
21
- })();
22
-
23
- logger.level = LEVEL.DEBUG;
24
-
25
- const flat = flattenResults(results);
26
- for (const { test, stack } of flat) {
27
- if (stack) {
28
- logger.info(test);
29
- logger.debug(`${stack}`);
30
- }
31
- }
32
-
33
- if (isHTMLLogger(logger)) {
34
- root.appendChild(logger.root);
35
- }
36
- }
@@ -1,64 +0,0 @@
1
- import { xml } from "../../dom/xml.ts";
2
- import { type FlatResult, flattenResults } from "../execute.ts";
3
- import type { TestResult } from "../scope.ts";
4
-
5
- const cases = (results: TestResult) =>
6
- Object.entries(results).filter(
7
- ([key]) => !["executed", "passed", "failed"].includes(key),
8
- );
9
-
10
- export function asXML(results: TestResult) {
11
- return `<?xml version="1.0" encoding="UTF-8" ?>${xml(
12
- "testsuites",
13
- { tests: results.executed, failures: results.failed },
14
- cases(results).map(([title, children]) =>
15
- testsuite(
16
- title,
17
- (children as TestResult).executed,
18
- (children as TestResult).failed,
19
- flattenResults(children as TestResult),
20
- ),
21
- ),
22
- )}`;
23
- }
24
-
25
- function testsuite(
26
- name: string,
27
- tests: number,
28
- failures: number,
29
- cases: FlatResult[],
30
- ) {
31
- const id = name.replace("s+", "_");
32
- return xml(
33
- "testsuite",
34
- { id, name, tests, failures },
35
- cases.map(({ test, stack }) =>
36
- testcase({ name: `${name} ${test}` }, stack ? [stack as string] : []),
37
- ),
38
- );
39
- }
40
-
41
- function testcase(
42
- {
43
- name,
44
- id = name.replace(/\s+/g, "_"),
45
- time = "0.00",
46
- }: { name: string; id?: string; time?: string },
47
- failures: string[],
48
- ) {
49
- return xml(
50
- "testcase",
51
- { id, name, time },
52
- failures.map((stack) => failure({ text: stack })),
53
- );
54
- }
55
-
56
- function failure({
57
- text,
58
- message = text.split("\n")[0],
59
- }: {
60
- text: string;
61
- message?: string;
62
- }) {
63
- return xml("failure", { message }, [`<![CDATA[${text}]]>`]);
64
- }
@@ -1,110 +0,0 @@
1
- import {
2
- afterall,
3
- aftereach,
4
- beforeall,
5
- beforeeach,
6
- rootCases,
7
- } from "./describe.ts";
8
- import type {
9
- TestFailed,
10
- TestPassed,
11
- TestResult,
12
- TestSummary,
13
- } from "./scope.ts";
14
-
15
- export async function execute(cases = rootCases()): Promise<TestResult> {
16
- const beforeallfn = cases[beforeall] ?? (() => {});
17
- const beforeeachfn = cases[beforeeach] ?? (() => {});
18
- const afterallfn = cases[afterall] ?? (() => {});
19
- const aftereachfn = cases[aftereach] ?? (() => {});
20
-
21
- const result: TestResult = { executed: 0, passed: 0, failed: 0, total: 0 };
22
-
23
- try {
24
- await beforeallfn();
25
- } catch (e) {
26
- result._beforeAll = { error: e };
27
- return result;
28
- }
29
-
30
- for (const [title, block] of Object.entries(cases)) {
31
- if (typeof title === "symbol") {
32
- continue;
33
- }
34
- if (block instanceof Function) {
35
- try {
36
- result.executed += 1;
37
- await beforeeachfn();
38
- await block();
39
- await aftereachfn();
40
- result.passed += 1;
41
- result[title] = { passed: true };
42
- } catch (e) {
43
- result.failed += 1;
44
- result[title] = { error: /** @type Error */ e };
45
- }
46
- } else if (block) {
47
- const run = await execute(block);
48
- result.executed += run.executed;
49
- result.passed += run.passed;
50
- result.failed += run.failed;
51
- result[title] = run;
52
- }
53
- }
54
-
55
- try {
56
- await afterallfn();
57
- } catch (e) {
58
- result._afterAll = { error: e };
59
- }
60
-
61
- return result;
62
- }
63
-
64
- export function getError({ error }: TestResult) {
65
- if (typeof error === "string") {
66
- return error;
67
- }
68
- if ((error as TestResult).message) {
69
- return (error as TestResult).stack;
70
- }
71
- return "unknown error";
72
- }
73
-
74
- export interface FlatResult {
75
- test: string;
76
- stack?: string | number | TestResult | TestSummary;
77
- stats: { executed: number; failed: number };
78
- }
79
-
80
- function makeResult(
81
- test: string,
82
- result: TestResult | TestSummary,
83
- ): FlatResult[] {
84
- if ((result as TestFailed).error)
85
- return [
86
- {
87
- test,
88
- stack: getError(result as TestResult),
89
- stats: { executed: 1, failed: 1 },
90
- },
91
- ];
92
- if ((result as TestPassed).passed === true) {
93
- return [{ test, stats: { executed: 1, failed: 0 } }];
94
- }
95
- return flattenResults(result as TestResult, test);
96
- }
97
-
98
- export function flattenResults(results: TestResult, prefix = ""): FlatResult[] {
99
- const arrow = prefix === "" ? "" : " -> ";
100
- let errorList: FlatResult[] = [];
101
- for (const [title, result] of Object.entries(results).filter(
102
- ([key]) => !["executed", "passed", "failed"].includes(key),
103
- )) {
104
- const test = `${prefix}${arrow}${title}`;
105
- if (typeof result === "number") continue;
106
- const flatResult = makeResult(test, result);
107
- errorList = errorList.concat(flatResult);
108
- }
109
- return errorList;
110
- }
@@ -1,169 +0,0 @@
1
- import { assert } from "../assert.ts";
2
- import { display } from "../display.ts";
3
- import { equals } from "../equal.ts";
4
-
5
- export class Matcher<T> {
6
- actual: T;
7
- constructor(actual: T) {
8
- this.actual = actual;
9
- }
10
-
11
- get not(): Matcher<T> {
12
- return new NotMatcher(this.actual);
13
- }
14
-
15
- toBe(expected: T) {
16
- assert(this.actual === expected, () => `${this.actual} !== ${expected}`);
17
- }
18
-
19
- toEqual(expected: T, partial = false) {
20
- assert(
21
- equals(this.actual, expected, partial),
22
- () =>
23
- `Objects are not equivalent: ${display(this.actual)}, ${display(
24
- expected,
25
- )}`,
26
- );
27
- }
28
-
29
- toMatch(expected: RegExp | string) {
30
- assert(
31
- typeof this.actual === "string",
32
- () => "Must have string for regexp match",
33
- );
34
- // @ts-expect-error
35
- const actual: string = this.actual;
36
- if (typeof expected === "string") {
37
- assert(
38
- actual.includes(expected),
39
- () => `${actual} does not include ${expected}`,
40
- );
41
- } else {
42
- assert(
43
- expected.test(actual),
44
- () => `${actual} does not match ${expected}`,
45
- );
46
- }
47
- }
48
-
49
- toMatchObject(expected: Partial<T>) {
50
- for (const [k, v] of Object.entries(expected)) {
51
- // @ts-expect-error
52
- const actual: Partial<T> = this.actual[k];
53
- assert(
54
- equals(actual, v, true),
55
- () =>
56
- `Comparing ${k}, properties not equal: ${display(actual)}, ${display(
57
- v,
58
- )}`,
59
- );
60
- }
61
- }
62
-
63
- toBeNull() {
64
- assert(
65
- this.actual === null,
66
- () => `Expected null, got ${JSON.stringify(this.actual)}`,
67
- );
68
- }
69
-
70
- toThrow(message = "") {
71
- let didThrow = false;
72
-
73
- let result: unknown;
74
- try {
75
- // @ts-expect-error
76
- result = this.actual();
77
- } catch (e) {
78
- assert(
79
- ((e as { message?: string }).message ?? "").match(message) !== null,
80
- () => `Expected thrown message to match ${message}, got ${e}`,
81
- );
82
- didThrow = true;
83
- }
84
-
85
- assert(didThrow, () => `Expected throw but got ${JSON.stringify(result)}`);
86
- }
87
- }
88
-
89
- export class NotMatcher<T> {
90
- actual: T;
91
- constructor(actual: T) {
92
- this.actual = actual;
93
- }
94
-
95
- get not(): Matcher<T> {
96
- return new Matcher(this.actual);
97
- }
98
-
99
- toBe(expected: T) {
100
- assert(this.actual !== expected, () => `${this.actual} === ${expected}`);
101
- }
102
-
103
- toEqual(expected: T) {
104
- assert(
105
- !equals(this.actual, expected),
106
- () =>
107
- `Objects are equivalent: ${JSON.stringify(
108
- this.actual,
109
- )}, ${JSON.stringify(expected)}`,
110
- );
111
- }
112
-
113
- toMatch(expected: RegExp | string) {
114
- assert(
115
- typeof this.actual === "string",
116
- () => "Must have string for regexp match",
117
- );
118
- // @ts-expect-error
119
- const actual: string = this.actual;
120
- if (typeof expected === "string") {
121
- assert(
122
- !actual.includes(expected),
123
- () => `${actual} includes ${expected}`,
124
- );
125
- } else {
126
- assert(!expected.test(actual), () => `${actual} matches ${expected}`);
127
- }
128
- }
129
-
130
- toMatchObject(expected: Partial<T>) {
131
- for (const [k, v] of Object.entries(expected)) {
132
- // @ts-expect-error
133
- const actual = this.actual[k];
134
- assert(
135
- !equals(actual, v),
136
- () =>
137
- `Comparing ${k}, properties equal: ${JSON.stringify(
138
- actual,
139
- )}, ${JSON.stringify(v)}`,
140
- );
141
- }
142
- }
143
-
144
- toBeNull() {
145
- assert(this.actual !== null, () => "Expected not null");
146
- }
147
-
148
- toThrow(message = "") {
149
- let didThrow = false;
150
-
151
- let result: unknown;
152
- try {
153
- // @ts-expect-error
154
- result = this.actual();
155
- } catch (e) {
156
- assert(
157
- ((e as { message?: string }).message ?? "").match(message) !== null,
158
- () => `Expected thrown message to match ${message}, got ${e}`,
159
- );
160
- didThrow = true;
161
- }
162
-
163
- assert(!didThrow, () => `Expected throw but got ${JSON.stringify(result)}`);
164
- }
165
- }
166
-
167
- export function expect<T>(t: T): Matcher<T> {
168
- return new Matcher(t);
169
- }
package/src/scope/fix.ts DELETED
@@ -1,30 +0,0 @@
1
- /**
2
- * Given a value with numbers, attempt to fix all numbers to 1 decimal point.
3
- */
4
- export function fix<T>(n: T): T {
5
- if (typeof n === "number") {
6
- return +n.toFixed(1) as T;
7
- }
8
- if (n !== Object(n)) {
9
- // A primitive
10
- return n;
11
- }
12
- if (Array.isArray(n)) {
13
- return n.map(fix) as T;
14
- }
15
- // @ts-expect-error
16
- return mapreduce<T>(fix, n as Record<string, T>);
17
- }
18
-
19
- function mapreduce<T, U>(
20
- fn: (t: T) => U,
21
- iter: Record<string, T>,
22
- ): Record<string, U> {
23
- return Object.entries(iter).reduce(
24
- (acc, [k, v]) => {
25
- acc[k] = fn(v);
26
- return acc;
27
- },
28
- {} as Record<string, U>,
29
- );
30
- }
@@ -1,11 +0,0 @@
1
- export {
2
- afterAll,
3
- afterEach,
4
- beforeAll,
5
- beforeEach,
6
- cleanState,
7
- describe,
8
- it,
9
- } from "./describe.ts";
10
- export { expect } from "./expect.ts";
11
- export { fix } from "./fix.ts";
@@ -1,21 +0,0 @@
1
- export interface TestCase {
2
- [k: string]: CallableFunction | TestCase;
3
- [k: symbol]: CallableFunction;
4
- }
5
-
6
- export interface TestResult {
7
- executed: number;
8
- passed: number;
9
- failed: number;
10
- [k: string]: TestResult | TestSummary | number;
11
- }
12
-
13
- export type TestSummary = TestFailed | TestPassed;
14
-
15
- export interface TestFailed {
16
- error: unknown;
17
- }
18
-
19
- export interface TestPassed {
20
- passed: true;
21
- }
@@ -1,13 +0,0 @@
1
- import type { Awaitable } from "../awaitable.ts";
2
-
3
- export function cleanState<State extends {}>(
4
- init: () => Awaitable<State>,
5
- runner: (action: () => Promise<void>) => void,
6
- ): State {
7
- const state = {};
8
- runner(async () => {
9
- const freshState = await init();
10
- Object.assign(state, freshState);
11
- });
12
- return state as State;
13
- }
package/src/test.mjs DELETED
@@ -1,33 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { parse } from "./flags.ts";
4
- import { onConsole } from "./scope/display/console.ts";
5
- import { asXML } from "./scope/display/junit.ts";
6
- import { execute } from "./scope/execute.ts";
7
-
8
- async function main() {
9
- await import("./test_all.ts");
10
-
11
- (async () => {
12
- const results = await execute();
13
-
14
- const FLAGS = parse(process.argv);
15
-
16
- switch (FLAGS.asString("mode", "console")) {
17
- case "junit": {
18
- const xml = asXML(results);
19
- console.log(xml);
20
- break;
21
- }
22
- default:
23
- onConsole(results);
24
- break;
25
- }
26
-
27
- if (results.failed > 0) {
28
- process.exit(1);
29
- }
30
- })();
31
- }
32
-
33
- main();
package/src/test_all.ts DELETED
@@ -1,35 +0,0 @@
1
- // This file must be .js for imports to run. Unused imports in .ts files are
2
- // discarded during transpilation.
3
- import { describe, expect, it } from "./scope/index.ts";
4
-
5
- describe("Test executor", () => {
6
- it("matches equality", () => {
7
- expect(1).toBe(1);
8
- });
9
-
10
- it("fails on inequality", () => {
11
- expect(() => expect(1).toBe(2)).toThrow();
12
- });
13
- });
14
-
15
- import "./context.test.ts";
16
- import "./diff.test.ts";
17
- import "./equal.test.ts";
18
- import "./flags.test.ts";
19
- import "./fs.test.ts";
20
- import "./generator.test.ts";
21
- import "./lock.test.ts";
22
- import "./result.test.ts";
23
- import "./observable/observable.test.ts";
24
-
25
- if (
26
- typeof process !== "undefined" &&
27
- process.env.CI?.toLowerCase() !== "true"
28
- ) {
29
- (async () => {
30
- const components = await import("./components/test.ts");
31
- const dom = await import("./dom/test.ts");
32
- await components.loadTests();
33
- await dom.loadTests();
34
- })();
35
- }