@ambarltd/core 0.1.17

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 (46) hide show
  1. package/README.md +10 -0
  2. package/dist/callable.d.ts +1 -0
  3. package/dist/callable.js +6 -0
  4. package/dist/future.d.ts +102 -0
  5. package/dist/future.js +164 -0
  6. package/dist/helpers/object.d.ts +9 -0
  7. package/dist/helpers/object.js +31 -0
  8. package/dist/json/decoder.d.ts +99 -0
  9. package/dist/json/decoder.js +213 -0
  10. package/dist/json/encoder.d.ts +50 -0
  11. package/dist/json/encoder.js +115 -0
  12. package/dist/json/schema.d.ts +78 -0
  13. package/dist/json/schema.js +168 -0
  14. package/dist/json/types.d.ts +6 -0
  15. package/dist/json/types.js +1 -0
  16. package/dist/list.d.ts +35 -0
  17. package/dist/list.js +130 -0
  18. package/dist/maybe.d.ts +104 -0
  19. package/dist/maybe.js +106 -0
  20. package/dist/remote-data.d.ts +117 -0
  21. package/dist/remote-data.js +148 -0
  22. package/dist/result.d.ts +75 -0
  23. package/dist/result.js +126 -0
  24. package/dist/router.d.ts +117 -0
  25. package/dist/router.js +106 -0
  26. package/dist/test.d.ts +63 -0
  27. package/dist/test.js +470 -0
  28. package/dist/time.d.ts +118 -0
  29. package/dist/time.js +387 -0
  30. package/dist/tracing/opentelemetry.d.ts +27 -0
  31. package/dist/tracing/opentelemetry.js +215 -0
  32. package/dist/tracing/proxy.d.ts +20 -0
  33. package/dist/tracing/proxy.js +103 -0
  34. package/dist/tracing/simple.d.ts +10 -0
  35. package/dist/tracing/simple.js +224 -0
  36. package/dist/tracing.d.ts +29 -0
  37. package/dist/tracing.js +55 -0
  38. package/dist/trampoline.d.ts +24 -0
  39. package/dist/trampoline.js +46 -0
  40. package/dist/tree-map.d.ts +73 -0
  41. package/dist/tree-map.js +169 -0
  42. package/dist/tree-set.d.ts +63 -0
  43. package/dist/tree-set.js +114 -0
  44. package/dist/types.d.ts +21 -0
  45. package/dist/types.js +1 -0
  46. package/package.json +49 -0
@@ -0,0 +1,117 @@
1
+ import * as express from "express";
2
+ import { Future } from "./future";
3
+ type Json = null | string | number | boolean | JsonArray | JsonObject;
4
+ type JsonObject = {
5
+ [x: string]: Json;
6
+ };
7
+ type JsonArray = Array<Json>;
8
+ /**
9
+ * A library for making express.js routes pure.
10
+ *
11
+ * It's a simple idea. A route handler is a function that takes a request
12
+ * and a request environment and returns a response.
13
+ *
14
+ * ```ts
15
+ * function myHandler(req: Request, env: T): Promise<Response>
16
+ * ```
17
+ *
18
+ * As you can see, the environment can be any type specified by the handler.
19
+ * This will allow for type-safe middlewares as the compiler will ensure that
20
+ * we do not use handlers with invalid environments.
21
+ * Things like session information should live in the environment.
22
+ *
23
+ * Middlewares are functions that take a request and an environment and
24
+ * return a different environment.
25
+ *
26
+ * ```ts
27
+ * function myMiddleware(req: Request, env: T1): Promise<MiddlewareResponse<T2>>
28
+ * ```
29
+ *
30
+ * This is how you 'modify' an environment. There is no real modification because
31
+ * everything should be immutable.
32
+ *
33
+ * A route is a request handler that returns a success response
34
+ * or a failure response.
35
+ */
36
+ type Route<T> = (req: express.Request, env: T) => Future<Response, Response>;
37
+ /**
38
+ * We use express.js objects for the request, but we will not mutate them.
39
+ * Instead we will add things to the environment, which is passed alongside
40
+ * the request.
41
+ */
42
+ type Request = express.Request;
43
+ type Headers = Record<string, string>;
44
+ declare class JSON {
45
+ values: {
46
+ status: number;
47
+ headers: Headers;
48
+ content: Json;
49
+ };
50
+ constructor(values: {
51
+ status: number;
52
+ headers: Headers;
53
+ content: Json;
54
+ });
55
+ }
56
+ declare class Redirect {
57
+ values: {
58
+ path: string;
59
+ };
60
+ constructor(values: {
61
+ path: string;
62
+ });
63
+ }
64
+ declare class Render {
65
+ values: {
66
+ status: number;
67
+ headers: Headers;
68
+ content: string | Json;
69
+ };
70
+ constructor(values: {
71
+ status: number;
72
+ headers: Headers;
73
+ content: string | Json;
74
+ });
75
+ }
76
+ /**
77
+ * Server-Sent Events response for real-time streaming to clients.
78
+ * Keeps connection alive and allows server to push updates continuously.
79
+ */
80
+ declare class SSE {
81
+ values: {
82
+ headers: Headers;
83
+ stream: (emit: SendMessage, onError: OnError) => Future<Error, null>;
84
+ };
85
+ constructor(values: {
86
+ headers: Headers;
87
+ stream: (emit: SendMessage, onError: OnError) => Future<Error, null>;
88
+ });
89
+ }
90
+ type SendMessage = (json: Json) => boolean;
91
+ type OnError = (handler: (err: Error) => void) => void;
92
+ type Response = Render | JSON | Redirect | SSE;
93
+ declare const json: ({ status, headers, content, }: {
94
+ status?: number;
95
+ headers?: Headers;
96
+ content: Json;
97
+ }) => Response;
98
+ declare const redirect: (path: string) => Response;
99
+ declare const render: ({ status, headers, content, }: {
100
+ status?: number;
101
+ headers?: Headers;
102
+ content: string | Json;
103
+ }) => Response;
104
+ declare const sse: ({ headers, stream, }: {
105
+ headers?: Headers;
106
+ stream: (emit: SendMessage, onError: OnError) => Future<Error, null>;
107
+ }) => Response;
108
+ /** A middleware is something that transforms the environment. */
109
+ type Middleware<A, B> = (req: express.Request, env: A) => Future<Response, B>;
110
+ declare function middleware<A, B>(fun: Middleware<A, B>, route: Route<B>): Route<A>;
111
+ /**
112
+ * Create an express route handler from a Route.
113
+ * It expects the route environment to be null because the
114
+ * environment will ultimately be enriched through middlewares.
115
+ */
116
+ declare function route(routeHandler: Route<{}>): express.Handler;
117
+ export { type Response, type Request, route, middleware, redirect, render, json, sse, type JSON, type SSE, type SendMessage, type OnError, };
package/dist/router.js ADDED
@@ -0,0 +1,106 @@
1
+ class JSON {
2
+ values;
3
+ constructor(values) {
4
+ this.values = values;
5
+ }
6
+ }
7
+ class Redirect {
8
+ values;
9
+ constructor(values) {
10
+ this.values = values;
11
+ }
12
+ }
13
+ class Render {
14
+ values;
15
+ constructor(values) {
16
+ this.values = values;
17
+ }
18
+ }
19
+ /**
20
+ * Server-Sent Events response for real-time streaming to clients.
21
+ * Keeps connection alive and allows server to push updates continuously.
22
+ */
23
+ class SSE {
24
+ values;
25
+ constructor(values) {
26
+ this.values = values;
27
+ }
28
+ }
29
+ // Convenience constructors for responses.
30
+ const json = ({ status = 200, headers = {}, content, }) => new JSON({ status, headers, content });
31
+ const redirect = (path) => new Redirect({ path });
32
+ const render = ({ status = 200, headers = {}, content, }) => new Render({ status, headers, content });
33
+ const sse = ({ headers = {}, stream, }) => new SSE({ headers, stream });
34
+ function middleware(fun, route) {
35
+ return (req, env) => fun(req, env).chain(res => route(req, res));
36
+ }
37
+ function send(response, res) {
38
+ switch (true) {
39
+ case response instanceof JSON:
40
+ res.status(response.values.status);
41
+ res.set(response.values.headers);
42
+ res.json(response.values.content);
43
+ return;
44
+ case response instanceof Render:
45
+ res.status(response.values.status);
46
+ res.set(response.values.headers);
47
+ res.send(response.values.content);
48
+ return;
49
+ case response instanceof Redirect:
50
+ return res.redirect(response.values.path);
51
+ case response instanceof SSE: {
52
+ res.set({
53
+ ...response.values.headers,
54
+ "Content-Type": "text/event-stream",
55
+ "Cache-Control": "no-cache",
56
+ Connection: "keep-alive",
57
+ });
58
+ res.flushHeaders();
59
+ streamSSEResponse(res, response.values.stream);
60
+ return;
61
+ }
62
+ }
63
+ }
64
+ function streamSSEResponse(res, stream) {
65
+ let connectionClosed = false;
66
+ let endStream = () => { };
67
+ const closeConnection = () => {
68
+ if (connectionClosed)
69
+ return;
70
+ connectionClosed = true;
71
+ endStream();
72
+ res.end();
73
+ };
74
+ const errorHandlers = [];
75
+ const handleError = (err) => {
76
+ if (connectionClosed)
77
+ return;
78
+ errorHandlers.forEach(handler => handler(err));
79
+ closeConnection();
80
+ };
81
+ res.on("close", closeConnection);
82
+ res.on("error", handleError);
83
+ // start streaming
84
+ endStream = stream(function emit(payload) {
85
+ if (connectionClosed)
86
+ return false;
87
+ res.write(`data: ${globalThis.JSON.stringify(payload)}\n\n`);
88
+ return true;
89
+ }, function onError(handler) {
90
+ errorHandlers.push(handler);
91
+ }).fork(handleError, closeConnection);
92
+ }
93
+ function sendResponse(routeHandler, req, res) {
94
+ routeHandler(req, {}).fork(r => send(r, res), r => send(r, res));
95
+ }
96
+ /**
97
+ * Create an express route handler from a Route.
98
+ * It expects the route environment to be null because the
99
+ * environment will ultimately be enriched through middlewares.
100
+ */
101
+ function route(routeHandler) {
102
+ return (req, res) => sendResponse(routeHandler, req, res);
103
+ }
104
+ export { route, middleware,
105
+ // responses
106
+ redirect, render, json, sse, };
package/dist/test.d.ts ADDED
@@ -0,0 +1,63 @@
1
+ export { run, expect, group, test, parseArgs, type RunOptions };
2
+ declare class Test {
3
+ readonly name: string;
4
+ readonly fun: () => void | Promise<void>;
5
+ readonly id: string;
6
+ constructor(name: string, fun: () => void | Promise<void>);
7
+ }
8
+ declare class Group {
9
+ readonly name: string;
10
+ readonly entries: Array<Group | Test>;
11
+ constructor(name: string, entries: Array<Group | Test>);
12
+ }
13
+ declare const test: (x: string, y: () => void | Promise<void>) => Test;
14
+ declare const group: (x: string, y: Array<Group | Test>) => Group;
15
+ type RunOptions = {
16
+ filters: ReadonlyArray<RegExp>;
17
+ };
18
+ /**
19
+ * Run a test suite. This should be the entry point of a test program.
20
+ *
21
+ * A sane, simple testing framework. Instead of a complicated test setup which
22
+ * finds and compiles files, we do the simplest obvious thing: a function that
23
+ * takes a list of tests. To run the tests execute a Node.js program that calls
24
+ * this `run` function.
25
+ *
26
+ * ```ts
27
+ * import { run, test, group, expect, parseArgs } from "@ambarltd/core/test";
28
+ *
29
+ * run(parseArgs(), [
30
+ * group("trivial tests", [
31
+ * test("referential equality", () => expect.equals(1, 3)),
32
+ * test("structural equality", () => expect.json_equals({}, {})),
33
+ * test("async test", async () => {
34
+ * const n = await fetchNumberFromTheInternet();
35
+ * expect.equals(1, n);
36
+ * }),
37
+ * ]),
38
+ * ]);
39
+ * ```
40
+ *
41
+ * If you want tests in different files, just import them like you would
42
+ * in a normal program.
43
+ *
44
+ * ```ts
45
+ * import { run } from "@ambarltd/core/test";
46
+ * import * as unit from "@test/unitTests";
47
+ * import * as integration from "@test/integrationTests";
48
+ *
49
+ * run(parseArgs(), [unit.tests, integration.tests]);
50
+ * ```
51
+ */
52
+ declare function run(options: RunOptions, groups: Array<Group>): Promise<void>;
53
+ declare const expect: {
54
+ fail: (reason: string) => void;
55
+ equals: <T>(a: T, b: T, label?: string) => void;
56
+ deep_equals: <T>(ra: T, rb: T) => void;
57
+ greater_than: <T>(a: T, b: T) => void;
58
+ not_equals: <T>(a: T, b: T) => void;
59
+ json_equals: <T>(a: T, b: T) => void;
60
+ contains: (needle: string, haystack: string) => void;
61
+ throws: (f: () => unknown, g: (e: Error) => void) => void;
62
+ };
63
+ declare function parseArgs(argv?: Array<string>): RunOptions;