@davidsouther/jiffies 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/README.md +60 -0
  2. package/build/assert.d.ts +23 -0
  3. package/build/assert.js +33 -0
  4. package/build/case.d.ts +1 -0
  5. package/build/case.js +5 -0
  6. package/build/components/button_bar.d.ts +8 -0
  7. package/build/components/button_bar.js +16 -0
  8. package/build/components/index.d.ts +1 -0
  9. package/build/components/index.js +1 -0
  10. package/build/components/inline_edit.d.ts +12 -0
  11. package/build/components/inline_edit.js +48 -0
  12. package/build/components/logger.d.ts +7 -0
  13. package/build/components/logger.js +22 -0
  14. package/build/components/select.d.ts +13 -0
  15. package/build/components/select.js +3 -0
  16. package/build/components/test.d.ts +1 -0
  17. package/build/components/test.js +2 -0
  18. package/build/components/virtual_scroll.d.ts +41 -0
  19. package/build/components/virtual_scroll.js +94 -0
  20. package/build/components/virtual_scroll.test.d.ts +1 -0
  21. package/build/components/virtual_scroll.test.js +21 -0
  22. package/build/context.d.ts +15 -0
  23. package/build/context.js +43 -0
  24. package/build/context.test.d.ts +1 -0
  25. package/build/context.test.js +46 -0
  26. package/build/debounce.d.ts +1 -0
  27. package/build/debounce.js +7 -0
  28. package/build/display.d.ts +5 -0
  29. package/build/display.js +3 -0
  30. package/build/dom/css/border.d.ts +11 -0
  31. package/build/dom/css/border.js +27 -0
  32. package/build/dom/css/constants.d.ts +31 -0
  33. package/build/dom/css/constants.js +28 -0
  34. package/build/dom/css/core.d.ts +5 -0
  35. package/build/dom/css/core.js +24 -0
  36. package/build/dom/css/fstyle.d.ts +5 -0
  37. package/build/dom/css/fstyle.js +32 -0
  38. package/build/dom/css/sizing.d.ts +5 -0
  39. package/build/dom/css/sizing.js +10 -0
  40. package/build/dom/dom.d.ts +27 -0
  41. package/build/dom/dom.js +94 -0
  42. package/build/dom/fc.d.ts +14 -0
  43. package/build/dom/fc.js +35 -0
  44. package/build/dom/fc.test.d.ts +1 -0
  45. package/build/dom/fc.test.js +21 -0
  46. package/build/dom/form/form.app.d.ts +1 -0
  47. package/build/dom/form/form.app.js +23 -0
  48. package/build/dom/form/form.d.ts +25 -0
  49. package/build/dom/form/form.js +25 -0
  50. package/build/dom/form/form.test.d.ts +0 -0
  51. package/build/dom/form/form.test.js +1 -0
  52. package/build/dom/html.d.ts +117 -0
  53. package/build/dom/html.js +114 -0
  54. package/build/dom/html.test.d.ts +1 -0
  55. package/build/dom/html.test.js +58 -0
  56. package/build/dom/router/link.d.ts +6 -0
  57. package/build/dom/router/link.js +3 -0
  58. package/build/dom/router/router.d.ts +12 -0
  59. package/build/dom/router/router.js +49 -0
  60. package/build/dom/svg.d.ts +64 -0
  61. package/build/dom/svg.js +65 -0
  62. package/build/dom/test.d.ts +1 -0
  63. package/build/dom/test.js +2 -0
  64. package/build/dom/types/css.d.ts +6612 -0
  65. package/build/dom/types/css.js +23 -0
  66. package/build/dom/types/dom.d.ts +0 -0
  67. package/build/dom/types/dom.js +1 -0
  68. package/build/dom/types/html.d.ts +616 -0
  69. package/build/dom/types/html.js +1 -0
  70. package/build/dom/xml.d.ts +1 -0
  71. package/build/dom/xml.js +5 -0
  72. package/build/equal.d.ts +4 -0
  73. package/build/equal.js +22 -0
  74. package/build/equal.test.d.ts +1 -0
  75. package/build/equal.test.js +20 -0
  76. package/build/flags.d.ts +7 -0
  77. package/build/flags.js +48 -0
  78. package/build/flags.test.d.ts +1 -0
  79. package/build/flags.test.js +35 -0
  80. package/build/generator.d.ts +1 -0
  81. package/build/generator.js +10 -0
  82. package/build/generator.test.d.ts +1 -0
  83. package/build/generator.test.js +24 -0
  84. package/build/index.d.ts +13 -0
  85. package/build/index.js +13 -0
  86. package/build/is_browser.d.ts +1 -0
  87. package/build/is_browser.js +1 -0
  88. package/build/loader.d.mts +22 -0
  89. package/build/loader.mjs +35 -0
  90. package/build/lock.d.ts +1 -0
  91. package/build/lock.js +23 -0
  92. package/build/lock.test.d.ts +1 -0
  93. package/build/lock.test.js +16 -0
  94. package/build/log.d.ts +26 -0
  95. package/build/log.js +34 -0
  96. package/build/parcel_resolver.d.ts +3 -0
  97. package/build/parcel_resolver.js +19 -0
  98. package/build/range.d.ts +1 -0
  99. package/build/range.js +7 -0
  100. package/build/result.d.ts +31 -0
  101. package/build/result.js +65 -0
  102. package/build/result.test.d.ts +1 -0
  103. package/build/result.test.js +71 -0
  104. package/build/safe.d.ts +1 -0
  105. package/build/safe.js +10 -0
  106. package/build/scope/describe.d.ts +14 -0
  107. package/build/scope/describe.js +52 -0
  108. package/build/scope/display/console.d.ts +2 -0
  109. package/build/scope/display/console.js +21 -0
  110. package/build/scope/display/dom.d.ts +3 -0
  111. package/build/scope/display/dom.js +26 -0
  112. package/build/scope/display/junit.d.ts +2 -0
  113. package/build/scope/display/junit.js +17 -0
  114. package/build/scope/execute.d.ts +12 -0
  115. package/build/scope/execute.js +85 -0
  116. package/build/scope/expect.d.ts +23 -0
  117. package/build/scope/expect.js +107 -0
  118. package/build/scope/fix.d.ts +4 -0
  119. package/build/scope/fix.js +22 -0
  120. package/build/scope/index.d.ts +3 -0
  121. package/build/scope/index.js +3 -0
  122. package/build/scope/scope.d.ts +17 -0
  123. package/build/scope/scope.js +1 -0
  124. package/build/server/http/apps.d.ts +5 -0
  125. package/build/server/http/apps.js +23 -0
  126. package/build/server/http/index.d.ts +21 -0
  127. package/build/server/http/index.js +71 -0
  128. package/build/server/http/response.d.ts +4 -0
  129. package/build/server/http/response.js +37 -0
  130. package/build/server/http/sitemap.d.ts +2 -0
  131. package/build/server/http/sitemap.js +42 -0
  132. package/build/server/http/static.d.ts +2 -0
  133. package/build/server/http/static.js +21 -0
  134. package/build/server/http/typescript.d.ts +5 -0
  135. package/build/server/http/typescript.js +40 -0
  136. package/build/server/main.d.ts +2 -0
  137. package/build/server/main.js +9 -0
  138. package/build/test.d.mts +2 -0
  139. package/build/test.mjs +23 -0
  140. package/build/test_all.d.ts +1 -0
  141. package/build/test_all.js +19 -0
  142. package/build/transpile.d.mts +3 -0
  143. package/build/transpile.mjs +18 -0
  144. package/package.json +36 -0
  145. package/src/404.html +14 -0
  146. package/src/assert.ts +50 -0
  147. package/src/case.ts +5 -0
  148. package/src/components/_notes +33 -0
  149. package/src/components/button_bar.ts +38 -0
  150. package/src/components/inline_edit.ts +77 -0
  151. package/src/components/logger.ts +36 -0
  152. package/src/components/select.ts +22 -0
  153. package/src/components/test.js +2 -0
  154. package/src/components/virtual_scroll.test.ts +27 -0
  155. package/src/components/virtual_scroll.ts +194 -0
  156. package/src/context.test.ts +58 -0
  157. package/src/context.ts +62 -0
  158. package/src/debounce.ts +7 -0
  159. package/src/display.ts +12 -0
  160. package/src/dom/README.md +102 -0
  161. package/src/dom/css/border.ts +47 -0
  162. package/src/dom/css/constants.ts +34 -0
  163. package/src/dom/css/core.ts +28 -0
  164. package/src/dom/css/fstyle.ts +42 -0
  165. package/src/dom/css/sizing.ts +11 -0
  166. package/src/dom/dom.ts +153 -0
  167. package/src/dom/fc.test.ts +43 -0
  168. package/src/dom/fc.ts +79 -0
  169. package/src/dom/form/form.app.ts +50 -0
  170. package/src/dom/form/form.test.ts +0 -0
  171. package/src/dom/form/form.ts +53 -0
  172. package/src/dom/form/index.html +14 -0
  173. package/src/dom/html.test.ts +72 -0
  174. package/src/dom/html.ts +129 -0
  175. package/src/dom/router/link.ts +14 -0
  176. package/src/dom/router/router.ts +69 -0
  177. package/src/dom/svg.ts +77 -0
  178. package/src/dom/test.ts +2 -0
  179. package/src/dom/types/css.ts +10106 -0
  180. package/src/dom/types/dom.ts +0 -0
  181. package/src/dom/types/html.ts +631 -0
  182. package/src/dom/xml.ts +12 -0
  183. package/src/equal.test.ts +23 -0
  184. package/src/equal.ts +32 -0
  185. package/src/favicon.ico +0 -0
  186. package/src/flags.test.ts +43 -0
  187. package/src/flags.ts +53 -0
  188. package/src/generator.test.ts +26 -0
  189. package/src/generator.ts +12 -0
  190. package/src/hooks/_notes +3 -0
  191. package/src/index.html +79 -0
  192. package/src/is_browser.js +1 -0
  193. package/src/loader.mjs +45 -0
  194. package/src/lock.test.ts +17 -0
  195. package/src/lock.ts +22 -0
  196. package/src/log.ts +61 -0
  197. package/src/observable/_notes +13 -0
  198. package/src/observable/observable._js +175 -0
  199. package/src/range.ts +7 -0
  200. package/src/result.test.ts +98 -0
  201. package/src/result.ts +107 -0
  202. package/src/safe.ts +12 -0
  203. package/src/scope/describe.ts +70 -0
  204. package/src/scope/display/console.ts +26 -0
  205. package/src/scope/display/dom.ts +36 -0
  206. package/src/scope/display/junit.ts +67 -0
  207. package/src/scope/execute.ts +108 -0
  208. package/src/scope/expect.ts +170 -0
  209. package/src/scope/fix.ts +29 -0
  210. package/src/scope/index.ts +11 -0
  211. package/src/scope/scope.ts +21 -0
  212. package/src/server/http/apps.ts +26 -0
  213. package/src/server/http/index.ts +119 -0
  214. package/src/server/http/response.ts +47 -0
  215. package/src/server/http/sitemap.ts +48 -0
  216. package/src/server/http/static.ts +27 -0
  217. package/src/server/http/typescript.ts +46 -0
  218. package/src/server/main.ts +13 -0
  219. package/src/test.mjs +29 -0
  220. package/src/test_all.ts +22 -0
  221. package/src/transpile.mjs +29 -0
@@ -0,0 +1,43 @@
1
+ import { describe, it, expect } from "./scope/index.js";
2
+ import { parse } from "./flags.js";
3
+
4
+ const TEST_CASES = {
5
+ SIMPLE: "program",
6
+ NODE: "/usr/local/bin/node program",
7
+ ONE_FLAG: "program --fun",
8
+ ONE_PARAM: "program --fun=100",
9
+ ONE_ARG: "program cat",
10
+ ONE_NO_FLAG: "program --no-fun",
11
+ MIXED: "program --fun=dog cat --port=8080 mouse --hamster --no-bird",
12
+ };
13
+
14
+ const makeFlags = (commandLine: string) => {
15
+ return parse(commandLine.split(" "));
16
+ };
17
+
18
+ describe("Flags", () => {
19
+ it("stores the program name", () => {
20
+ const flags = makeFlags(TEST_CASES.SIMPLE);
21
+
22
+ expect(flags.argv0).toEqual("program");
23
+ expect(flags.args).toEqual([]);
24
+ });
25
+
26
+ it("stores the program name ignoring node", () => {
27
+ const flags = makeFlags(TEST_CASES.NODE);
28
+
29
+ expect(flags.argv0).toEqual("program");
30
+ expect(flags.args).toEqual([]);
31
+ });
32
+
33
+ it("parses a flag", () => {
34
+ const flags = makeFlags(TEST_CASES.ONE_FLAG);
35
+ expect(flags.get("fun")).toBe(true);
36
+ });
37
+
38
+ it("parses a flag with a default value", () => {
39
+ const flags = makeFlags(TEST_CASES.SIMPLE);
40
+ expect(flags.get("missing")).toBe(false);
41
+ expect(flags.get("missing", true)).toBe(true);
42
+ });
43
+ });
package/src/flags.ts ADDED
@@ -0,0 +1,53 @@
1
+ export const parse = (parseArgs: string[]) => {
2
+ const fromNode = parseArgs[0].endsWith("node");
3
+ const argv = parseArgs[fromNode ? 1 : 0];
4
+ const params = new Map<string, string>();
5
+ const flags = new Map<string, boolean>();
6
+ const args: string[] = [];
7
+
8
+ let index = fromNode ? 2 : 1;
9
+ const hasNext = () => index < parseArgs.length;
10
+ const peek = () => parseArgs[index];
11
+ const advance = () => parseArgs[index++];
12
+
13
+ const parseLong = (arg: string) => {
14
+ if (arg.substr(0, 3) === "no-") {
15
+ flags.set(arg.substr(3), false);
16
+ } else if (!arg.includes("=")) {
17
+ flags.set(arg, true);
18
+ } else {
19
+ const [param, ...value] = arg.split("=");
20
+ params.set(param, value.join("="));
21
+ }
22
+ };
23
+
24
+ while (hasNext()) {
25
+ if (peek().substr(0, 2) == "--") {
26
+ parseLong(advance().substr(2));
27
+ } else {
28
+ args.push(advance());
29
+ }
30
+ }
31
+
32
+ return {
33
+ get argv0() {
34
+ return argv;
35
+ },
36
+
37
+ get args(): ReadonlyArray<string> {
38
+ return args;
39
+ },
40
+
41
+ get(flag: string, def = false) {
42
+ return flags.get(flag) ?? def;
43
+ },
44
+
45
+ asNumber(param: string, def = 0): number {
46
+ return Number.parseFloat(params.get(param) ?? `${def}`);
47
+ },
48
+
49
+ asString(param: string, def = ""): string {
50
+ return params.get(param) ?? def;
51
+ },
52
+ };
53
+ };
@@ -0,0 +1,26 @@
1
+ import { takeWhile } from "./generator.js";
2
+ import { describe, it, expect } from "./scope/index.js";
3
+
4
+ describe("Generator", () => {
5
+ it("takes from a generator until a predicate", () => {
6
+ const generator = function* () {
7
+ let i = 1;
8
+ while (true) {
9
+ yield (i = i * 2);
10
+ }
11
+ };
12
+ const filter = () => {
13
+ let previousValue = 0;
14
+ return (n: number) => {
15
+ if (previousValue < 100) {
16
+ previousValue = n;
17
+ return true;
18
+ }
19
+ return false;
20
+ };
21
+ };
22
+
23
+ const values = [...takeWhile(filter(), generator())];
24
+ expect(values).toEqual([2, 4, 8, 16, 32, 64, 128]);
25
+ });
26
+ });
@@ -0,0 +1,12 @@
1
+ export function* takeWhile<T>(
2
+ predicate: (t: T) => boolean,
3
+ iterator: Iterable<T>
4
+ ) {
5
+ for (const x of iterator) {
6
+ if (predicate(x)) {
7
+ yield x;
8
+ } else {
9
+ return;
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,3 @@
1
+ useEffect
2
+ useState
3
+ useContext
package/src/index.html ADDED
@@ -0,0 +1,79 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Jiffies Tests</title>
5
+ <base href="/" />
6
+ <link rel="stylesheet" href="/pico/pico-1.4.1.css" />
7
+ </head>
8
+ <body>
9
+ <header id="menu"></header>
10
+
11
+ <main class="container">
12
+ <article id="test_output"></article>
13
+ </main>
14
+
15
+ <div style="display: none" id="status"></div>
16
+ <div style="display: none" id="json"></div>
17
+ <div style="display: none" id="xml"></div>
18
+ <script type="module" src="./test_all.js"></script>
19
+ <script type="module" src="./dom/test.js"></script>
20
+ <script type="module" src="./components/test.js"></script>
21
+
22
+ <script type="module">
23
+ import { execute } from "./scope/execute.js";
24
+ import { displayStatistics } from "./scope/display/dom.js";
25
+ import { onConsole } from "./scope/display/console.js";
26
+ import { asXML } from "./scope/display/junit.js";
27
+
28
+ function write(id, value) {
29
+ const start = `--- START_${id.toUpperCase()} ---`;
30
+ const end = `--- END ---`;
31
+ document.getElementById(
32
+ id
33
+ ).innerHTML = `\n${start}\n${value}\n${end}\n`;
34
+ }
35
+
36
+ (async () => {
37
+ const results = await execute();
38
+ displayStatistics(results, document.getElementById("test_output"));
39
+ onConsole(results);
40
+ write("status", results.failed);
41
+ write("json", JSON.stringify(results));
42
+ write("xml", asXML(results));
43
+ })();
44
+ </script>
45
+ <script type="module">
46
+ import { nav, ul, li, strong } from "./dom/html.js";
47
+ import { link } from "./dom/router/link.js";
48
+
49
+ const getUrls = async () => {
50
+ const sitemap = await fetch("/sitemap.json");
51
+ if (sitemap.ok) {
52
+ return await sitemap.json();
53
+ } else {
54
+ return [
55
+ "/index.html",
56
+ "/apps/computron5k/index.html",
57
+ "/apps/chess/index.html",
58
+ ];
59
+ }
60
+ };
61
+
62
+ const urls = (await getUrls()).map((url) => ({
63
+ href: url.replace("index.html", ""),
64
+ link:
65
+ url.match(/\/(?<link>[^\/]+)(\/index\.html)?$/)?.groups["link"] ??
66
+ url.replace("index.html", ""),
67
+ }));
68
+
69
+ document
70
+ .getElementById("menu")
71
+ .appendChild(
72
+ nav(
73
+ ul(li(strong("Jiffy Apps"))),
74
+ ul(...urls.map((url) => li(link(url))))
75
+ )
76
+ );
77
+ </script>
78
+ </body>
79
+ </html>
@@ -0,0 +1 @@
1
+ export const IsBrowser = typeof window !== "undefined";
package/src/loader.mjs ADDED
@@ -0,0 +1,45 @@
1
+ import { stat } from "node:fs/promises";
2
+ import { cwd } from "node:process";
3
+ import { fileURLToPath, pathToFileURL } from "node:url";
4
+ import { transpile } from "./transpile.mjs";
5
+
6
+ const baseURL = pathToFileURL(`${cwd()}/`).href;
7
+
8
+ /** @returns {Promise<{url: string}>} */
9
+ export async function resolve(
10
+ /** @type string */ specifier,
11
+ /** @type {{parentURL?: string}} */ context,
12
+ /** @type {(specifier: string, context: {parentURL?: string}, defaultResolve: Function) => Promise<{url: string}>} */ defaultResolve
13
+ ) {
14
+ const tsURL = new URL(
15
+ specifier.replace(/js$/, "ts"),
16
+ context.parentURL ?? baseURL
17
+ );
18
+ const tsSpecifier = tsURL.href;
19
+ try {
20
+ const stats = await stat(fileURLToPath(tsURL));
21
+ if (stats.isFile()) {
22
+ return { url: tsSpecifier };
23
+ }
24
+ } catch (e) {
25
+ // Do nothing
26
+ }
27
+ return defaultResolve(specifier, context, defaultResolve);
28
+ }
29
+
30
+ /** @returns {Promise<{format: string, source: string}>} */
31
+ export async function load(
32
+ /** @type string */ url,
33
+ /** @type {{format?: string}} */ context,
34
+ /** @type {(url: string, context: {format?: string}, defaultLoad?: Function) => Promise<{format: string, source: string}>} */ defaultLoad
35
+ ) {
36
+ return url.endsWith("ts")
37
+ ? {
38
+ format: "module",
39
+ source: await transpile(
40
+ url,
41
+ async () => (await defaultLoad(url, { format: "module" })).source
42
+ ),
43
+ }
44
+ : defaultLoad(url, context, defaultLoad);
45
+ }
@@ -0,0 +1,17 @@
1
+ import { describe, it, expect } from "./scope/index.js";
2
+ import { lock } from "./lock.js";
3
+
4
+ describe("Lock", () => {
5
+ it("prevents reentry", function () {
6
+ let count = 0;
7
+ const inc = lock(function () {
8
+ if (count > 4) {
9
+ return;
10
+ }
11
+ inc();
12
+ count++;
13
+ });
14
+ inc();
15
+ expect(count).toBe(1);
16
+ });
17
+ });
package/src/lock.ts ADDED
@@ -0,0 +1,22 @@
1
+ const locks = new WeakSet<Function>();
2
+
3
+ export function lock(fn: Function): Function {
4
+ return function (...args: unknown[]) {
5
+ let ret = null;
6
+ let ex = null;
7
+ if (!locks.has(fn)) {
8
+ locks.add(fn);
9
+ try {
10
+ ret = fn(...args);
11
+ } catch (e) {
12
+ ex = e;
13
+ }
14
+ }
15
+ locks.delete(fn);
16
+ if (ex !== null) {
17
+ throw ex;
18
+ } else {
19
+ return ret;
20
+ }
21
+ };
22
+ }
package/src/log.ts ADDED
@@ -0,0 +1,61 @@
1
+ import { display, Display } from "./display.js";
2
+
3
+ export interface Log {
4
+ (message: Display, data?: {}): void;
5
+ }
6
+
7
+ export interface Logger {
8
+ level: Number;
9
+ debug: Log;
10
+ info: Log;
11
+ warn: Log;
12
+ error: Log;
13
+ }
14
+
15
+ export const LEVEL = {
16
+ UNKNOWN: 0,
17
+ SILENT: 0,
18
+ DEBUG: 1,
19
+ VERBOSE: 1,
20
+ INFO: 2,
21
+ WARN: 3,
22
+ ERROR: 4,
23
+ };
24
+
25
+ export function getLogger(name: string): Logger {
26
+ const logger: Partial<Logger> = { level: LEVEL.INFO };
27
+ const logAt =
28
+ (
29
+ level: number,
30
+ fn: (message: Display, data?: {}) => void
31
+ ): ((message: Display) => void) =>
32
+ (message: Display, data?: {}) =>
33
+ level >= (logger.level ?? LEVEL.SILENT)
34
+ ? fn(display(message), data)
35
+ : undefined;
36
+
37
+ logger.debug = logAt(LEVEL.VERBOSE, console.debug.bind(console));
38
+ logger.info = logAt(LEVEL.INFO, console.info.bind(console));
39
+ logger.warn = logAt(LEVEL.WARN, console.warn.bind(console));
40
+ logger.error = logAt(LEVEL.ERROR, console.error.bind(console));
41
+
42
+ return logger as Logger;
43
+ }
44
+
45
+ export const DEFAULT_LOGGER = getLogger("default");
46
+
47
+ export function debug(message: Display, data?: {}) {
48
+ DEFAULT_LOGGER.debug(message, data);
49
+ }
50
+
51
+ export function info(message: Display, data?: {}) {
52
+ DEFAULT_LOGGER.info(message, data);
53
+ }
54
+
55
+ export function warn(message: Display, data?: {}) {
56
+ DEFAULT_LOGGER.warn(message, data);
57
+ }
58
+
59
+ export function error(message: Display, data?: {}) {
60
+ DEFAULT_LOGGER.error(message, data);
61
+ }
@@ -0,0 +1,13 @@
1
+ Subject
2
+ BehaviorSubject
3
+ ReplaySubject
4
+ Observable
5
+ of
6
+ from
7
+ fromEvent
8
+ pipe
9
+ operator
10
+ map
11
+ filter
12
+ reduce
13
+ tap
@@ -0,0 +1,175 @@
1
+ // import { Observable, ReplaySubject } from "rxjs";
2
+ // import { tap } from "rxjs/operators";
3
+ import { display } from "../display.js";
4
+ import { DEFAULT_LOGGER, Logger } from "../log.js";
5
+
6
+ /**
7
+ * @template T
8
+ * @typedef Next
9
+ * @property {T} value
10
+ * @property {false} completed
11
+ * @property {false} failed
12
+ */
13
+
14
+ /**
15
+ * @template E
16
+ * @typedef Error
17
+ * @property {E} error
18
+ * @property {false} completed
19
+ * @property {true} failed
20
+ */
21
+
22
+ /**
23
+ * @typedef Completed
24
+ * @property {true} completed
25
+ * @property {false} failed
26
+ */
27
+
28
+ /**
29
+ * @typedef Failed
30
+ * @property {true} completed
31
+ * @property {true} failed
32
+ */
33
+
34
+ /**
35
+ * @template T
36
+ * @template E
37
+ * @typedef {Next<T> | Error<E> | Completed | Failed} Event
38
+ */
39
+
40
+ /**
41
+ * @template T
42
+ * @param {T} value
43
+ * @returns {Next<T>}
44
+ */
45
+ export const next = (value) => ({ value, completed: false, failed: false });
46
+
47
+ /**
48
+ * @template E
49
+ * @param {E} e
50
+ * @returns {Error<E>}
51
+ */
52
+ export const error = (e) => ({ error: e, completed: false, failed: true });
53
+
54
+ /** @returns {Completed} */
55
+ export const completed = () => ({ completed: true, failed: false });
56
+
57
+ /** @returns {Failed} */
58
+ export const failed = () => ({ completed: true, failed: true });
59
+
60
+ /**
61
+ * @template T
62
+ * @param {Event<T, unknown>} event
63
+ * @returns {event is Next<T>}
64
+ */
65
+ export const isNext = (event) =>
66
+ !event.completed && !event.failed && event.value !== undefined;
67
+
68
+ /**
69
+ * @template E
70
+ * @param {Event<unknown, E>} event
71
+ * @returns {event is Error<E>}
72
+ */
73
+ export const isError = (event) => event.failed && !event.completed;
74
+
75
+ /**
76
+ * @param {Event<unknown, unknown>} event
77
+ * @returns {event is Completed}
78
+ */
79
+ export const isCompleted = (event) => event.completed && !event.failed;
80
+
81
+ /**
82
+ * @param {Event<unknown, unknown>} event
83
+ * @returns {event is Failed}
84
+ */
85
+ export const isFailed = (event) => event.completed && event.failed;
86
+
87
+ /**
88
+ * @template T
89
+ * @param {T|Event<T, unknown>} t
90
+ * @returns {t is Event<T, unknown>}
91
+ */
92
+ export const isEvent = (t) => {
93
+ /* @type unknown */
94
+ const b = t;
95
+ return isNext(b) || isError(b) || isCompleted(b);
96
+ };
97
+
98
+ /**
99
+ * @template T
100
+ * @param {(T|Event<T, unknown>)[]} a
101
+ * @returns {(Event<T, unknown>)[]}
102
+ */
103
+ export const asEvents = (a) => a.map((e) => (isEvent(e) ? e : next(e)));
104
+
105
+ /**
106
+ * @template T
107
+ * @param {Event<T, unknown>} event
108
+ * @returns {string}
109
+ */
110
+ const marble = (event) =>
111
+ isError(event)
112
+ ? "X"
113
+ : isFailed(event)
114
+ ? "!"
115
+ : isCompleted(event)
116
+ ? "|"
117
+ : `(${display(event.value)})`;
118
+
119
+ /**
120
+ * @template T
121
+ * @param {(Event<T, unknown>)[]} events
122
+ * @returns string
123
+ */
124
+ export const marbles = (events) => `:${events.map(marble).join("-")}`;
125
+
126
+ /**
127
+ * @template T
128
+ * @template E
129
+ * @param {Observable<T>} input$
130
+ * @returns {(Event<T, E>)[]}
131
+ */
132
+ export const collect = (input$) => {
133
+ /** @type {(Event<T, E>)[]} */
134
+ const collected = [];
135
+
136
+ const subscription = input$.subscribe({
137
+ next: (/* @type T */ x) => collected.push(next(x)),
138
+ error: (/* @type E */ e) => collected.push(error(e)),
139
+ complete: () => collected.push(completed()),
140
+ });
141
+
142
+ subscription.unsubscribe();
143
+
144
+ return collected;
145
+ };
146
+
147
+ /**
148
+ * @template T
149
+ * @param {Logger} logger
150
+ * @returns {(o: Observable<T>) => Observable<T>}
151
+ */
152
+ export const watch =
153
+ (logger = DEFAULT_LOGGER) =>
154
+ /**
155
+ * @template E
156
+ * @param {Observable<T>} observable
157
+ * @returns {Observable<T>}
158
+ */
159
+ (observable) => {
160
+ observable.pipe(
161
+ tap({
162
+ next(/* @type T */ t) {
163
+ logger.info(t);
164
+ },
165
+ complete() {
166
+ logger.info("Observable completed");
167
+ },
168
+ error(/* @type E */ e) {
169
+ logger.warn(e);
170
+ },
171
+ })
172
+ );
173
+
174
+ return observable;
175
+ };
package/src/range.ts ADDED
@@ -0,0 +1,7 @@
1
+ export function range(start: number, end: number, stride = 1): number[] {
2
+ const range = [];
3
+ for (let i = start; i < end; i += stride) {
4
+ range.push(i);
5
+ }
6
+ return range;
7
+ }
@@ -0,0 +1,98 @@
1
+ import {
2
+ Err,
3
+ None,
4
+ Ok,
5
+ Result,
6
+ Some,
7
+ unwrap,
8
+ unwrapOr,
9
+ unwrapOrElse,
10
+ } from "./result.js";
11
+ import { describe, it } from "./scope/describe.js";
12
+ import { expect } from "./scope/expect.js";
13
+
14
+ describe("Result", () => {
15
+ it("converts Nones", () => {
16
+ const a = None<string>();
17
+ expect(a).toBeNull();
18
+
19
+ const b = None<string>();
20
+ expect(b).toBeNull();
21
+
22
+ const c = Some(a);
23
+ expect(c).toBeNull();
24
+
25
+ const d = Some(b);
26
+ expect(d).toBeNull();
27
+ });
28
+
29
+ it("converts Somes", () => {
30
+ const a = Some("a");
31
+ expect(a).toBe("a");
32
+
33
+ const b = Some(a);
34
+ expect(b).toBe("a");
35
+ });
36
+
37
+ it("converts Errs", () => {
38
+ const a = Err(new Error("a error"));
39
+ const b = Err(a);
40
+ expect(b).toMatchObject({ message: "a error" });
41
+
42
+ // Assign Err to Result
43
+ const c: Result<string> = a;
44
+ });
45
+
46
+ it("converts Oks", () => {
47
+ const a = Ok("a ok");
48
+ const b = Ok(a);
49
+ expect(b).toBe("a ok");
50
+
51
+ // Assign ok to Result
52
+ const c: Result<string> = a;
53
+ });
54
+
55
+ it("unwraps", () => {
56
+ const a = Some("some");
57
+ const b = None<string>();
58
+ const c = Ok("ok");
59
+ const d = Err(new Error("err"));
60
+ const e: string = "else";
61
+
62
+ expect(unwrap(a)).toBe("some");
63
+ expect(unwrap<string, Error>(c)).toBe("ok");
64
+ expect(() => unwrap(b)).toThrow("Attempted to unwrap None");
65
+ expect(() => unwrap(d)).toThrow("err");
66
+ expect(unwrap(e)).toBe("else");
67
+ });
68
+
69
+ it("unwrapsOrs", () => {
70
+ const a = Some<string>("some");
71
+ const b = None<string>();
72
+ const c = Ok<string>("ok");
73
+ const d = Err(new Error("err"));
74
+ const e: string = "else";
75
+
76
+ expect(unwrapOr(a, "z")).toBe("some");
77
+ expect(unwrapOr(c, "z")).toBe("ok");
78
+ expect(unwrapOr(b, "z")).toBe("z");
79
+ expect(unwrapOr(d, "z")).toBe("z");
80
+ expect(unwrapOr(e, "z")).toBe("else");
81
+ });
82
+
83
+ it("unwrapsOrElse", () => {
84
+ const a = Some<string>("some");
85
+ const b = None();
86
+ const c = Ok("ok");
87
+ const d = Err(new Error("err"));
88
+ const e = "else";
89
+
90
+ expect(unwrapOrElse(a, () => "z")).toBe("some");
91
+ expect(unwrapOrElse(c, () => "z")).toBe("ok");
92
+ expect(unwrapOrElse(b, () => "z")).toBe("z");
93
+ expect(unwrapOrElse(d, () => "z")).toBe("z");
94
+ expect(unwrapOrElse(e, () => "z")).toBe("else");
95
+ });
96
+ });
97
+
98
+ export {};