@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,119 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {
4
+ createServer,
5
+ IncomingMessage,
6
+ RequestListener,
7
+ ServerResponse,
8
+ } from "http";
9
+ import { AddressInfo } from "net";
10
+ import * as path from "path";
11
+ import { info } from "../../log.js";
12
+ import { findIndex } from "./apps.js";
13
+ import { fileResponse } from "./response.js";
14
+ import { sitemap } from "./sitemap.js";
15
+ import { staticFileServer } from "./static.js";
16
+ import { tsFileServer } from "./typescript.js";
17
+
18
+ export interface StaticResponse {
19
+ status: 200 | 404 | 500;
20
+ content: Buffer;
21
+ contentType: string;
22
+ contentLength?: number;
23
+ }
24
+
25
+ export interface ServerConfig {
26
+ root: string;
27
+ scopes?: Record<`@${string}`, string>;
28
+ }
29
+
30
+ export interface MiddlewareFactory {
31
+ (config: ServerConfig): Promise<StaticMiddleware>;
32
+ }
33
+
34
+ export interface StaticMiddleware {
35
+ (req: IncomingMessage): Promise<undefined | (() => Promise<StaticResponse>)>;
36
+ }
37
+
38
+ const notFound: MiddlewareFactory =
39
+ async ({ root }) =>
40
+ async () =>
41
+ fileResponse(
42
+ // path.join(path.dirname(FLAGS.argv0), "404.html"),
43
+ path.join(root, "404.html"),
44
+ undefined,
45
+ 404
46
+ );
47
+
48
+ const BASE_MIDDLEWARES: MiddlewareFactory[] = [
49
+ sitemap,
50
+ tsFileServer,
51
+ staticFileServer,
52
+ findIndex,
53
+ notFound,
54
+ ];
55
+
56
+ const error = (res: ServerResponse, message: string) => {
57
+ console.error(message);
58
+ res.statusCode = 500;
59
+ res.write(message);
60
+ res.end();
61
+ return true;
62
+ };
63
+
64
+ const sendContent = async (
65
+ res: ServerResponse,
66
+ { content, contentType, contentLength }: StaticResponse
67
+ ) => {
68
+ res.setHeader("content-length", `${contentLength}`);
69
+ res.setHeader("content-type", contentType);
70
+ await res.write(content);
71
+ res.end();
72
+ return true;
73
+ };
74
+
75
+ const log = (req: IncomingMessage) => {
76
+ const when = new Date().toISOString();
77
+ const who = req.socket.remoteAddress;
78
+ const what = req.url;
79
+ const how = `${req.method} ${what}`;
80
+ info("Request", { when, who, how });
81
+ };
82
+
83
+ export const makeServer = async (
84
+ config: ServerConfig,
85
+ middlewares: MiddlewareFactory[] = []
86
+ ) => {
87
+ const handlers = await Promise.all(
88
+ [...middlewares, ...BASE_MIDDLEWARES].map(async (m) => m(config))
89
+ );
90
+ const middlewareHandler: RequestListener = async (req, res) => {
91
+ log(req);
92
+ let handler: undefined | (() => Promise<StaticResponse>);
93
+ try {
94
+ for (const middleware of handlers) {
95
+ handler = await middleware(req);
96
+ if (handler !== undefined) {
97
+ break;
98
+ }
99
+ }
100
+ if (handler) {
101
+ sendContent(res, await handler());
102
+ } else {
103
+ res.end();
104
+ }
105
+ } catch (e) {
106
+ error(res, (e as Error).message + "\n" + (e as Error).stack);
107
+ }
108
+ };
109
+
110
+ // TODO(https)
111
+ const server = createServer(middlewareHandler);
112
+
113
+ server.on("listening", () => {
114
+ const { address, port } = server.address() as AddressInfo;
115
+ info("Server listening", { address: `http://${address}:${port}` });
116
+ });
117
+
118
+ return server;
119
+ };
@@ -0,0 +1,47 @@
1
+ import { Stats } from "fs";
2
+ import * as fs from "fs/promises";
3
+ import { StaticMiddleware, StaticResponse } from ".";
4
+
5
+ const MIME_TYPES: Record<string, string> = {
6
+ js: "text/javascript",
7
+ json: "text/javascript",
8
+ css: "text/css",
9
+ html: "text/html",
10
+ png: "image/png",
11
+ jpg: "image/jpeg",
12
+ jpeg: "image/jpeg",
13
+ svg: "image/svg+xml",
14
+ eot: "application/vnd.ms-fontobject",
15
+ ttf: "application/font-ttf",
16
+ woff: "application/font-woff",
17
+ woff2: "application/font-woff2",
18
+ };
19
+
20
+ const mime = (basename: string) => {
21
+ const extension = basename.substr(basename.lastIndexOf(".") + 1);
22
+ return MIME_TYPES[extension] ?? "application/octet-stream";
23
+ };
24
+
25
+ export const fileResponse =
26
+ (filename: string, stat?: Stats, status: 200 | 404 | 500 = 200) =>
27
+ async (): Promise<StaticResponse> => {
28
+ if (!stat) {
29
+ stat = await fs.stat(filename);
30
+ }
31
+ const content = await fs.readFile(filename);
32
+ const contentType = mime(filename);
33
+ const contentLength = stat.size;
34
+ return { status, contentType, contentLength, content };
35
+ };
36
+
37
+ export const contentResponse =
38
+ (content: string, contentType: string, status: 200 | 404 | 500 = 200) =>
39
+ async (): Promise<StaticResponse> => {
40
+ const contentBuffer = Buffer.from(content, "utf-8");
41
+ return {
42
+ content: contentBuffer,
43
+ contentType,
44
+ status,
45
+ contentLength: contentBuffer.length,
46
+ };
47
+ };
@@ -0,0 +1,48 @@
1
+ import * as fs from "fs/promises";
2
+ import * as path from "path";
3
+ import { info } from "../../log.js";
4
+ import { MiddlewareFactory, StaticMiddleware } from "./index.js";
5
+ import { contentResponse } from "./response.js";
6
+
7
+ const findSiteMap = async (root: string, prefix = root) => {
8
+ if (root.startsWith("node_modules")) {
9
+ return [];
10
+ }
11
+ const children = (await fs.readdir(root, { withFileTypes: true })).map(
12
+ async (entry): Promise<string[]> => {
13
+ const next = path
14
+ .join(root, entry.name)
15
+ // Normalize separators for web
16
+ .replaceAll(path.sep, "/");
17
+ if (entry.isFile()) {
18
+ if (entry.name === "index.html") {
19
+ let index = next.replace(prefix, "");
20
+ info(`Adding to sitemap`, { index });
21
+ return [index];
22
+ }
23
+ } else if (entry.isDirectory()) {
24
+ if (entry.name.startsWith(".")) {
25
+ return [];
26
+ }
27
+ const flattened = (
28
+ await Promise.all(await findSiteMap(next, prefix))
29
+ ).flat();
30
+ return flattened;
31
+ }
32
+ return [];
33
+ }
34
+ );
35
+ return children;
36
+ };
37
+
38
+ export const sitemap: MiddlewareFactory = async ({ root }) => {
39
+ const apps = await (await Promise.all(await findSiteMap(root)))
40
+ .flat()
41
+ .filter((a) => a !== undefined);
42
+ return async (req) => {
43
+ if ((req.url ?? "").endsWith("sitemap.json")) {
44
+ return contentResponse(JSON.stringify(apps), "application/json");
45
+ }
46
+ return undefined;
47
+ };
48
+ };
@@ -0,0 +1,27 @@
1
+ import * as path from "path";
2
+ import * as fs from "fs/promises";
3
+ import { fileResponse } from "./response.js";
4
+ import type { MiddlewareFactory } from "./index.js";
5
+
6
+ export const staticFileServer: MiddlewareFactory =
7
+ async ({ root, scopes = {} }) =>
8
+ async (req) => {
9
+ const scope = Object.entries(scopes).find(([s]) =>
10
+ req.url?.startsWith(`/${s}`)
11
+ );
12
+ const url = new URL(req.url ?? "", `http://localhost`);
13
+ const pathname = scope
14
+ ? url.pathname.replace(scope[0], scope[1])
15
+ : url.pathname;
16
+ const filename = path.join(root, pathname);
17
+ // Expand url with found scope
18
+ console.log(scope, req.url, filename);
19
+
20
+ try {
21
+ const stat = await fs.stat(filename);
22
+ return stat.isDirectory() ? undefined : fileResponse(filename, stat);
23
+ } catch (e) {
24
+ console.error(e);
25
+ return undefined;
26
+ }
27
+ };
@@ -0,0 +1,46 @@
1
+ import * as fs from "fs/promises";
2
+ import * as path from "path";
3
+ import { contentResponse } from "./response.js";
4
+ import { transpile } from "../../transpile.mjs";
5
+ import { MiddlewareFactory } from "./index.js";
6
+
7
+ function render(source: string) {
8
+ // Replace `from "@scope` with `from "/@scope`, for browsers
9
+ source = source
10
+ .replaceAll(`from "@`, 'from "/@')
11
+ .replaceAll(`import("@`, 'import("/@');
12
+ return contentResponse(source, "application/javascript");
13
+ }
14
+
15
+ /**
16
+ * Serves .js files statically. Finds .ts files and transpiles them to JS.
17
+ */
18
+ export const tsFileServer: MiddlewareFactory =
19
+ async ({ root, scopes = {} }) =>
20
+ async (req) => {
21
+ if (req.url?.endsWith(".js")) {
22
+ let scope = Object.entries(scopes).find(([s]) =>
23
+ req.url?.startsWith(`/${s}`)
24
+ );
25
+ // Expand url with found scope
26
+ let url = scope ? req.url.replace(scope[0], scope[1]) : req.url;
27
+ let filename = path.join(root, url);
28
+ try {
29
+ const stat = await fs.stat(filename);
30
+ if (stat.isFile()) {
31
+ const js = (await fs.readFile(filename)).toString("utf-8");
32
+ return render(js);
33
+ }
34
+ } catch {}
35
+
36
+ filename = filename.replace(/\.js$/, ".ts");
37
+ try {
38
+ const stat = await fs.stat(filename);
39
+ if (stat.isFile()) {
40
+ const js = await transpile(filename, () => fs.readFile(filename));
41
+ return render(js);
42
+ }
43
+ } catch {}
44
+ }
45
+ return undefined;
46
+ };
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node --experimental-loader ../loader.mjs
2
+
3
+ import { info } from "../log.js";
4
+ info("Starting server", { cwd: process.cwd() });
5
+
6
+ import { parse } from "../flags.js";
7
+ const FLAGS = parse(process.argv);
8
+
9
+ import { makeServer } from "./http/index.js";
10
+ import * as path from "node:path";
11
+
12
+ const server = await makeServer({ root: path.join(process.cwd(), "src") });
13
+ server.listen(FLAGS.asNumber("port", 8080), FLAGS.asString("host", "0.0.0.0"));
package/src/test.mjs ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { parse } from "./flags.js";
4
+ import { execute } from "./scope/execute.js";
5
+ import { asXML } from "./scope/display/junit.js";
6
+ import { onConsole } from "./scope/display/console.js";
7
+
8
+ await import("./test_all.js");
9
+
10
+ (async function () {
11
+ const results = await execute();
12
+
13
+ const FLAGS = parse(process.argv);
14
+
15
+ switch (FLAGS.asString("mode", "console")) {
16
+ case "junit":
17
+ const xml = asXML(results);
18
+ console.log(xml);
19
+ break;
20
+ case "console":
21
+ default:
22
+ onConsole(results);
23
+ break;
24
+ }
25
+
26
+ if (results.failed > 0) {
27
+ process.exit(1);
28
+ }
29
+ })();
@@ -0,0 +1,22 @@
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.js";
4
+
5
+ await Promise.all([
6
+ import("./context.test.js"),
7
+ import("./equal.test.js"),
8
+ import("./flags.test.js"),
9
+ import("./generator.test.js"),
10
+ import("./lock.test.js"),
11
+ import("./result.test.js"),
12
+ ]);
13
+
14
+ describe("Test executor", () => {
15
+ it("matches equality", () => {
16
+ expect(1).toBe(1);
17
+ });
18
+
19
+ it("fails on inequality", () => {
20
+ expect(() => expect(1).toBe(2)).toThrow();
21
+ });
22
+ });
@@ -0,0 +1,29 @@
1
+ // @ts-ignore
2
+ import ts from "typescript";
3
+
4
+ const compilerOptions = {
5
+ target: ts.ScriptTarget.ESNext,
6
+ module: ts.ModuleKind.ESNext,
7
+ inlineSourceMap: true,
8
+ inlineSources: true,
9
+ };
10
+
11
+ const tsmap = new Map();
12
+
13
+ export async function transpile(
14
+ /** @type string */ url,
15
+ /** @type {() => Promise<{toString(): string}>} */ get
16
+ ) {
17
+ if (!tsmap.has(url) || true) {
18
+ const source = ts.transpile(
19
+ (await get()).toString(),
20
+ compilerOptions,
21
+ url,
22
+ undefined,
23
+ url
24
+ );
25
+ tsmap.set(url, source);
26
+ }
27
+
28
+ return tsmap.get(url);
29
+ }