From 012dfcdafd38d036cf1cde189c755369afe733ec Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Wed, 15 Nov 2023 10:23:14 -0600 Subject: [PATCH] frontside.com joints the revolution Upgrade to the middleware framework we've been using for all our sites which has now been extracted into a separate library. This consolidates a lot of code, makes twind more accessible and standard via `twing.config.ts` and also improves the caching and proxy behavior to load sites faster. For example, we were not properly forwarding `if-none-match` headers and so none of our proxy responses were being cached. --- deno.json | 12 +- deno.lock | 637 +++++++++++--- html.ts | 5 - ...ance-backstage-plugin-development.html.tsx | 792 ----------------- html/app.html.tsx | 146 ---- html/env.ts | 9 - html/index.html.tsx | 28 - lib/deps.ts | 5 - lib/html.ts | 42 - lib/route-recognizer.ts | 21 - lib/router.ts | 1 - lib/server.ts | 115 --- lib/types.ts | 13 - lib/view.ts | 33 - lib/watch.ts | 24 +- main.ts | 17 +- main.tsx | 62 ++ plugins/current-request.ts | 37 + plugins/etag.ts | 40 + plugins/twind.ts | 54 ++ ...ced-backstage-plugin-development-route.tsx | 822 ++++++++++++++++++ routes/app.html.tsx | 162 ++++ routes/proxy-route.ts | 35 + sitemap/proxy.ts | 31 - sitemap/sitemap.ts | 35 - src/components/navbar/project-select.tsx | 111 --- lib/twind.ts => twind.config.ts | 67 +- 27 files changed, 1782 insertions(+), 1574 deletions(-) delete mode 100644 html.ts delete mode 100644 html/advance-backstage-plugin-development.html.tsx delete mode 100644 html/app.html.tsx delete mode 100644 html/env.ts delete mode 100644 html/index.html.tsx delete mode 100644 lib/deps.ts delete mode 100644 lib/html.ts delete mode 100644 lib/route-recognizer.ts delete mode 100644 lib/router.ts delete mode 100644 lib/server.ts delete mode 100644 lib/types.ts delete mode 100644 lib/view.ts create mode 100644 main.tsx create mode 100644 plugins/current-request.ts create mode 100644 plugins/etag.ts create mode 100644 plugins/twind.ts create mode 100644 routes/advanced-backstage-plugin-development-route.tsx create mode 100644 routes/app.html.tsx create mode 100644 routes/proxy-route.ts delete mode 100644 sitemap/proxy.ts delete mode 100644 sitemap/sitemap.ts delete mode 100644 src/components/navbar/project-select.tsx rename lib/twind.ts => twind.config.ts (73%) diff --git a/deno.json b/deno.json index 6244e07e..bec9ce7d 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,6 @@ { "tasks": { - "dev": "deno run -A lib/watch.ts html components lib sitemap assets src -- deno run --lock-write -A main.ts" + "dev": "deno run -A lib/watch.ts components lib assets routes plugins -- deno run --lock-write -A main.tsx" }, "lint": { "rules": { @@ -12,13 +12,13 @@ "exclude": ["legacy"] }, "compilerOptions": { + "lib": ["deno.ns", "dom", "dom.iterable"], "jsx": "react-jsx", - "jsxImportSource": "hastx" + "jsxImportSource": "revolution" }, "imports": { - "effection": "https://deno.land/x/effection@3.0.0-alpha.9/mod.ts", - "freejack/": "./lib/", - "hastx/": "https://deno.land/x/hastx@v0.0.6/", - "hastx/jsx-runtime": "https://deno.land/x/hastx@v0.0.6/jsx-runtime.ts" + "effection": "https://deno.land/x/effection@3.0.0-beta.2/mod.ts", + "revolution": "https://deno.land/x/revolution@0.3.0/mod.ts", + "revolution/jsx-runtime": "https://deno.land/x/revolution@0.3.0/jsx-runtime.ts" } } diff --git a/deno.lock b/deno.lock index bef98554..889cbc17 100644 --- a/deno.lock +++ b/deno.lock @@ -1,112 +1,547 @@ { "version": "3", + "packages": { + "specifiers": { + "npm:@twind/core@1.1.3": "npm:@twind/core@1.1.3", + "npm:@twind/preset-tailwind@1.1.4": "npm:@twind/preset-tailwind@1.1.4_@twind+core@1.1.3", + "npm:@twind/preset-typography@1.0.7": "npm:@twind/preset-typography@1.0.7_@twind+core@1.1.3", + "npm:@types/hast@^3.0.0": "npm:@types/hast@3.0.3", + "npm:hast-util-to-html@9.0.0": "npm:hast-util-to-html@9.0.0" + }, + "npm": { + "@twind/core@1.1.3": { + "integrity": "sha512-/B/aNFerMb2IeyjSJy3SJxqVxhrT77gBDknLMiZqXIRr4vNJqiuhx7KqUSRzDCwUmyGuogkamz+aOLzN6MeSLw==", + "dependencies": { + "csstype": "csstype@3.1.2" + } + }, + "@twind/preset-tailwind@1.1.4_@twind+core@1.1.3": { + "integrity": "sha512-zv85wrP/DW4AxgWrLfH7kyGn/KJF3K04FMLVl2AjoxZGYdCaoZDkL8ma3hzaKQ+WGgBFRubuB/Ku2Rtv/wjzVw==", + "dependencies": { + "@twind/core": "@twind/core@1.1.3" + } + }, + "@twind/preset-typography@1.0.7_@twind+core@1.1.3": { + "integrity": "sha512-Xqh3dSFUnqWCVmVpsq4Xq7mAYpjVhd5ynq+uorTc5wEr/m/M+Bgz1QiC4bm/0F+NPq92K96uWz3t0clpHkTz5A==", + "dependencies": { + "@twind/core": "@twind/core@1.1.3" + } + }, + "@types/hast@3.0.3": { + "integrity": "sha512-2fYGlaDy/qyLlhidX42wAH0KBi2TCjKMH8CHmBXgRlJ3Y+OXTiqsPQ6IWarZKwF1JoUcAJdPogv1d4b0COTpmQ==", + "dependencies": { + "@types/unist": "@types/unist@3.0.2" + } + }, + "@types/mdast@4.0.3": { + "integrity": "sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==", + "dependencies": { + "@types/unist": "@types/unist@3.0.2" + } + }, + "@types/unist@3.0.2": { + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==", + "dependencies": {} + }, + "@ungap/structured-clone@1.2.0": { + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dependencies": {} + }, + "ccount@2.0.1": { + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dependencies": {} + }, + "character-entities-html4@2.1.0": { + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dependencies": {} + }, + "character-entities-legacy@3.0.0": { + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dependencies": {} + }, + "comma-separated-tokens@2.0.3": { + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "dependencies": {} + }, + "csstype@3.1.2": { + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", + "dependencies": {} + }, + "dequal@2.0.3": { + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dependencies": {} + }, + "devlop@1.1.0": { + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dependencies": { + "dequal": "dequal@2.0.3" + } + }, + "entities@4.5.0": { + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dependencies": {} + }, + "hast-util-from-parse5@8.0.1": { + "integrity": "sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==", + "dependencies": { + "@types/hast": "@types/hast@3.0.3", + "@types/unist": "@types/unist@3.0.2", + "devlop": "devlop@1.1.0", + "hastscript": "hastscript@8.0.0", + "property-information": "property-information@6.4.0", + "vfile": "vfile@6.0.1", + "vfile-location": "vfile-location@5.0.2", + "web-namespaces": "web-namespaces@2.0.1" + } + }, + "hast-util-parse-selector@4.0.0": { + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "dependencies": { + "@types/hast": "@types/hast@3.0.3" + } + }, + "hast-util-raw@9.0.1": { + "integrity": "sha512-5m1gmba658Q+lO5uqL5YNGQWeh1MYWZbZmWrM5lncdcuiXuo5E2HT/CIOp0rLF8ksfSwiCVJ3twlgVRyTGThGA==", + "dependencies": { + "@types/hast": "@types/hast@3.0.3", + "@types/unist": "@types/unist@3.0.2", + "@ungap/structured-clone": "@ungap/structured-clone@1.2.0", + "hast-util-from-parse5": "hast-util-from-parse5@8.0.1", + "hast-util-to-parse5": "hast-util-to-parse5@8.0.0", + "html-void-elements": "html-void-elements@3.0.0", + "mdast-util-to-hast": "mdast-util-to-hast@13.0.2", + "parse5": "parse5@7.1.2", + "unist-util-position": "unist-util-position@5.0.0", + "unist-util-visit": "unist-util-visit@5.0.0", + "vfile": "vfile@6.0.1", + "web-namespaces": "web-namespaces@2.0.1", + "zwitch": "zwitch@2.0.4" + } + }, + "hast-util-to-html@9.0.0": { + "integrity": "sha512-IVGhNgg7vANuUA2XKrT6sOIIPgaYZnmLx3l/CCOAK0PtgfoHrZwX7jCSYyFxHTrGmC6S9q8aQQekjp4JPZF+cw==", + "dependencies": { + "@types/hast": "@types/hast@3.0.3", + "@types/unist": "@types/unist@3.0.2", + "ccount": "ccount@2.0.1", + "comma-separated-tokens": "comma-separated-tokens@2.0.3", + "hast-util-raw": "hast-util-raw@9.0.1", + "hast-util-whitespace": "hast-util-whitespace@3.0.0", + "html-void-elements": "html-void-elements@3.0.0", + "mdast-util-to-hast": "mdast-util-to-hast@13.0.2", + "property-information": "property-information@6.4.0", + "space-separated-tokens": "space-separated-tokens@2.0.2", + "stringify-entities": "stringify-entities@4.0.3", + "zwitch": "zwitch@2.0.4" + } + }, + "hast-util-to-parse5@8.0.0": { + "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", + "dependencies": { + "@types/hast": "@types/hast@3.0.3", + "comma-separated-tokens": "comma-separated-tokens@2.0.3", + "devlop": "devlop@1.1.0", + "property-information": "property-information@6.4.0", + "space-separated-tokens": "space-separated-tokens@2.0.2", + "web-namespaces": "web-namespaces@2.0.1", + "zwitch": "zwitch@2.0.4" + } + }, + "hast-util-whitespace@3.0.0": { + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dependencies": { + "@types/hast": "@types/hast@3.0.3" + } + }, + "hastscript@8.0.0": { + "integrity": "sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==", + "dependencies": { + "@types/hast": "@types/hast@3.0.3", + "comma-separated-tokens": "comma-separated-tokens@2.0.3", + "hast-util-parse-selector": "hast-util-parse-selector@4.0.0", + "property-information": "property-information@6.4.0", + "space-separated-tokens": "space-separated-tokens@2.0.2" + } + }, + "html-void-elements@3.0.0": { + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "dependencies": {} + }, + "mdast-util-to-hast@13.0.2": { + "integrity": "sha512-U5I+500EOOw9e3ZrclN3Is3fRpw8c19SMyNZlZ2IS+7vLsNzb2Om11VpIVOR+/0137GhZsFEF6YiKD5+0Hr2Og==", + "dependencies": { + "@types/hast": "@types/hast@3.0.3", + "@types/mdast": "@types/mdast@4.0.3", + "@ungap/structured-clone": "@ungap/structured-clone@1.2.0", + "devlop": "devlop@1.1.0", + "micromark-util-sanitize-uri": "micromark-util-sanitize-uri@2.0.0", + "trim-lines": "trim-lines@3.0.1", + "unist-util-position": "unist-util-position@5.0.0", + "unist-util-visit": "unist-util-visit@5.0.0" + } + }, + "micromark-util-character@2.0.1": { + "integrity": "sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw==", + "dependencies": { + "micromark-util-symbol": "micromark-util-symbol@2.0.0", + "micromark-util-types": "micromark-util-types@2.0.0" + } + }, + "micromark-util-encode@2.0.0": { + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "dependencies": {} + }, + "micromark-util-sanitize-uri@2.0.0": { + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "dependencies": { + "micromark-util-character": "micromark-util-character@2.0.1", + "micromark-util-encode": "micromark-util-encode@2.0.0", + "micromark-util-symbol": "micromark-util-symbol@2.0.0" + } + }, + "micromark-util-symbol@2.0.0": { + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "dependencies": {} + }, + "micromark-util-types@2.0.0": { + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "dependencies": {} + }, + "parse5@7.1.2": { + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dependencies": { + "entities": "entities@4.5.0" + } + }, + "property-information@6.4.0": { + "integrity": "sha512-9t5qARVofg2xQqKtytzt+lZ4d1Qvj8t5B8fEwXK6qOfgRLgH/b13QlgEyDh033NOS31nXeFbYv7CLUDG1CeifQ==", + "dependencies": {} + }, + "space-separated-tokens@2.0.2": { + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dependencies": {} + }, + "stringify-entities@4.0.3": { + "integrity": "sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==", + "dependencies": { + "character-entities-html4": "character-entities-html4@2.1.0", + "character-entities-legacy": "character-entities-legacy@3.0.0" + } + }, + "trim-lines@3.0.1": { + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dependencies": {} + }, + "unist-util-is@6.0.0": { + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dependencies": { + "@types/unist": "@types/unist@3.0.2" + } + }, + "unist-util-position@5.0.0": { + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dependencies": { + "@types/unist": "@types/unist@3.0.2" + } + }, + "unist-util-stringify-position@4.0.0": { + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dependencies": { + "@types/unist": "@types/unist@3.0.2" + } + }, + "unist-util-visit-parents@6.0.1": { + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dependencies": { + "@types/unist": "@types/unist@3.0.2", + "unist-util-is": "unist-util-is@6.0.0" + } + }, + "unist-util-visit@5.0.0": { + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dependencies": { + "@types/unist": "@types/unist@3.0.2", + "unist-util-is": "unist-util-is@6.0.0", + "unist-util-visit-parents": "unist-util-visit-parents@6.0.1" + } + }, + "vfile-location@5.0.2": { + "integrity": "sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg==", + "dependencies": { + "@types/unist": "@types/unist@3.0.2", + "vfile": "vfile@6.0.1" + } + }, + "vfile-message@4.0.2": { + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dependencies": { + "@types/unist": "@types/unist@3.0.2", + "unist-util-stringify-position": "unist-util-stringify-position@4.0.0" + } + }, + "vfile@6.0.1": { + "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==", + "dependencies": { + "@types/unist": "@types/unist@3.0.2", + "unist-util-stringify-position": "unist-util-stringify-position@4.0.0", + "vfile-message": "vfile-message@4.0.2" + } + }, + "web-namespaces@2.0.1": { + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "dependencies": {} + }, + "zwitch@2.0.4": { + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dependencies": {} + } + } + }, "remote": { - "https://deno.land/std@0.148.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", + "https://deno.land/std@0.140.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", + "https://deno.land/std@0.140.0/_util/os.ts": "3b4c6e27febd119d36a416d7a97bd3b0251b77c88942c8f16ee5953ea13e2e49", + "https://deno.land/std@0.140.0/bytes/bytes_list.ts": "67eb118e0b7891d2f389dad4add35856f4ad5faab46318ff99653456c23b025d", + "https://deno.land/std@0.140.0/bytes/equals.ts": "fc16dff2090cced02497f16483de123dfa91e591029f985029193dfaa9d894c9", + "https://deno.land/std@0.140.0/bytes/mod.ts": "763f97d33051cc3f28af1a688dfe2830841192a9fea0cbaa55f927b49d49d0bf", + "https://deno.land/std@0.140.0/fmt/colors.ts": "30455035d6d728394781c10755351742dd731e3db6771b1843f9b9e490104d37", + "https://deno.land/std@0.140.0/fs/_util.ts": "0fb24eb4bfebc2c194fb1afdb42b9c3dda12e368f43e8f2321f84fc77d42cb0f", + "https://deno.land/std@0.140.0/fs/ensure_dir.ts": "9dc109c27df4098b9fc12d949612ae5c9c7169507660dcf9ad90631833209d9d", + "https://deno.land/std@0.140.0/hash/sha256.ts": "803846c7a5a8a5a97f31defeb37d72f519086c880837129934f5d6f72102a8e8", + "https://deno.land/std@0.140.0/io/buffer.ts": "bd0c4bf53db4b4be916ca5963e454bddfd3fcd45039041ea161dbf826817822b", + "https://deno.land/std@0.140.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", + "https://deno.land/std@0.140.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", + "https://deno.land/std@0.140.0/path/_util.ts": "c1e9686d0164e29f7d880b2158971d805b6e0efc3110d0b3e24e4b8af2190d2b", + "https://deno.land/std@0.140.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", + "https://deno.land/std@0.140.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee", + "https://deno.land/std@0.140.0/path/mod.ts": "d3e68d0abb393fb0bf94a6d07c46ec31dc755b544b13144dee931d8d5f06a52d", + "https://deno.land/std@0.140.0/path/posix.ts": "293cdaec3ecccec0a9cc2b534302dfe308adb6f10861fa183275d6695faace44", + "https://deno.land/std@0.140.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", + "https://deno.land/std@0.140.0/path/win32.ts": "31811536855e19ba37a999cd8d1b62078235548d67902ece4aa6b814596dd757", + "https://deno.land/std@0.140.0/streams/conversion.ts": "712585bfa0172a97fb68dd46e784ae8ad59d11b88079d6a4ab098ff42e697d21", "https://deno.land/std@0.158.0/fmt/colors.ts": "ff7dc9c9f33a72bd48bc24b21bbc1b4545d8494a431f17894dbc5fe92a938fc4", "https://deno.land/std@0.158.0/testing/_diff.ts": "a23e7fc2b4d8daa3e158fa06856bedf5334ce2a2831e8bf9e509717f455adb2c", "https://deno.land/std@0.158.0/testing/_format.ts": "cd11136e1797791045e639e9f0f4640d5b4166148796cad37e6ef75f7d7f3832", "https://deno.land/std@0.158.0/testing/asserts.ts": "8696c488bc98d8d175e74dc652a0ffbc7fca93858da01edc57ed33c1148345da", - "https://deno.land/std@0.188.0/async/abortable.ts": "fd682fa46f3b7b16b4606a5ab52a7ce309434b76f820d3221bdfb862719a15d7", - "https://deno.land/std@0.188.0/async/deadline.ts": "58f72a3cc0fcb731b2cc055ba046f4b5be3349ff6bf98f2e793c3b969354aab2", - "https://deno.land/std@0.188.0/async/debounce.ts": "adab11d04ca38d699444ac8a9d9856b4155e8dda2afd07ce78276c01ea5a4332", - "https://deno.land/std@0.188.0/async/deferred.ts": "42790112f36a75a57db4a96d33974a936deb7b04d25c6084a9fa8a49f135def8", - "https://deno.land/std@0.188.0/async/delay.ts": "73aa04cec034c84fc748c7be49bb15cac3dd43a57174bfdb7a4aec22c248f0dd", - "https://deno.land/std@0.188.0/async/mod.ts": "f04344fa21738e5ad6bea37a6bfffd57c617c2d372bb9f9dcfd118a1b622e576", - "https://deno.land/std@0.188.0/async/mux_async_iterator.ts": "70c7f2ee4e9466161350473ad61cac0b9f115cff4c552eaa7ef9d50c4cbb4cc9", - "https://deno.land/std@0.188.0/async/pool.ts": "f1b8d3df4d7fd3c73f8cbc91cc2e8b8e950910f1eab94230b443944d7584c657", - "https://deno.land/std@0.188.0/async/retry.ts": "dd19d93033d8eaddbfcb7654c0366e9d3b0a21448bdb06eba4a7d8a8cf936a92", - "https://deno.land/std@0.188.0/async/tee.ts": "47e42d35f622650b02234d43803d0383a89eb4387e1b83b5a40106d18ae36757", - "https://deno.land/std@0.188.0/http/server.ts": "1b23463b5b36e4eebc495417f6af47a6f7d52e3294827a1226d2a1aab23d9d20", - "https://deno.land/std@0.190.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", - "https://deno.land/std@0.190.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", - "https://deno.land/std@0.190.0/async/abortable.ts": "fd682fa46f3b7b16b4606a5ab52a7ce309434b76f820d3221bdfb862719a15d7", - "https://deno.land/std@0.190.0/async/deadline.ts": "58f72a3cc0fcb731b2cc055ba046f4b5be3349ff6bf98f2e793c3b969354aab2", - "https://deno.land/std@0.190.0/async/debounce.ts": "adab11d04ca38d699444ac8a9d9856b4155e8dda2afd07ce78276c01ea5a4332", - "https://deno.land/std@0.190.0/async/deferred.ts": "42790112f36a75a57db4a96d33974a936deb7b04d25c6084a9fa8a49f135def8", - "https://deno.land/std@0.190.0/async/delay.ts": "73aa04cec034c84fc748c7be49bb15cac3dd43a57174bfdb7a4aec22c248f0dd", - "https://deno.land/std@0.190.0/async/mod.ts": "f04344fa21738e5ad6bea37a6bfffd57c617c2d372bb9f9dcfd118a1b622e576", - "https://deno.land/std@0.190.0/async/mux_async_iterator.ts": "70c7f2ee4e9466161350473ad61cac0b9f115cff4c552eaa7ef9d50c4cbb4cc9", - "https://deno.land/std@0.190.0/async/pool.ts": "f1b8d3df4d7fd3c73f8cbc91cc2e8b8e950910f1eab94230b443944d7584c657", - "https://deno.land/std@0.190.0/async/retry.ts": "c9248325ec08cc2cceb7618472e77589a51a25cee460e07b6096be34966c2ead", - "https://deno.land/std@0.190.0/async/tee.ts": "47e42d35f622650b02234d43803d0383a89eb4387e1b83b5a40106d18ae36757", - "https://deno.land/std@0.190.0/collections/_utils.ts": "5114abc026ddef71207a79609b984614e66a63a4bda17d819d56b0e72c51527e", - "https://deno.land/std@0.190.0/collections/deep_merge.ts": "5a8ed29030f4471a5272785c57c3455fa79697b9a8f306013a8feae12bafc99a", - "https://deno.land/std@0.190.0/encoding/base64.ts": "144ae6234c1fbe5b68666c711dc15b1e9ee2aef6d42b3b4345bf9a6c91d70d0d", - "https://deno.land/std@0.190.0/flags/mod.ts": "17f444ddbee43c5487568de0c6a076c7729cfe90d96d2ffcd2b8f8adadafb6e8", - "https://deno.land/std@0.190.0/fmt/bytes.ts": "f29cf69e0791d375f9f5d94ae1f0641e5a03b975f32ddf86d70f70fdf37e7b6a", - "https://deno.land/std@0.190.0/fmt/colors.ts": "d67e3cd9f472535241a8e410d33423980bec45047e343577554d3356e1f0ef4e", - "https://deno.land/std@0.190.0/http/etag.ts": "6ad8abbbb1045aabf2307959a2c5565054a8bf01c9824ddee836b1ff22706a58", - "https://deno.land/std@0.190.0/http/file_server.ts": "fd99a7a0e1ac6f80873b1f812aa30ac64f96663971d061ced3a4d10b65048efe", - "https://deno.land/std@0.190.0/http/http_status.ts": "8a7bcfe3ac025199ad804075385e57f63d055b2aed539d943ccc277616d6f932", - "https://deno.land/std@0.190.0/http/server.ts": "1b23463b5b36e4eebc495417f6af47a6f7d52e3294827a1226d2a1aab23d9d20", - "https://deno.land/std@0.190.0/http/util.ts": "57bf151c8d4388bbd56ed01ece1720934252adc6f859f782392927f0f78964c0", - "https://deno.land/std@0.190.0/media_types/_db.ts": "7606d83e31f23ce1a7968cbaee852810c2cf477903a095696cdc62eaab7ce570", - "https://deno.land/std@0.190.0/media_types/_util.ts": "916efbd30b6148a716f110e67a4db29d6949bf4048997b754415dd7e42c52378", - "https://deno.land/std@0.190.0/media_types/content_type.ts": "ad98a5aa2d95f5965b2796072284258710a25e520952376ed432b0937ce743bc", - "https://deno.land/std@0.190.0/media_types/format_media_type.ts": "f5e1073c05526a6f5a516ac5c5587a1abd043bf1039c71cde1166aa4328c8baf", - "https://deno.land/std@0.190.0/media_types/get_charset.ts": "18b88274796fda5d353806bf409eb1d2ddb3f004eb4bd311662c4cdd8ac173db", - "https://deno.land/std@0.190.0/media_types/parse_media_type.ts": "835c4112e1357e95b4f10d7cdea5ae1801967e444f48673ff8f1cb4d32af9920", - "https://deno.land/std@0.190.0/media_types/type_by_extension.ts": "daa801eb0f11cdf199445d0f1b656cf116d47dcf9e5b85cc1e6b4469f5ee0432", - "https://deno.land/std@0.190.0/media_types/vendor/mime-db.v1.52.0.ts": "6925bbcae81ca37241e3f55908d0505724358cda3384eaea707773b2c7e99586", - "https://deno.land/std@0.190.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", - "https://deno.land/std@0.190.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", - "https://deno.land/std@0.190.0/path/_util.ts": "d7abb1e0dea065f427b89156e28cdeb32b045870acdf865833ba808a73b576d0", - "https://deno.land/std@0.190.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", - "https://deno.land/std@0.190.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1", - "https://deno.land/std@0.190.0/path/mod.ts": "ee161baec5ded6510ee1d1fb6a75a0f5e4b41f3f3301c92c716ecbdf7dae910d", - "https://deno.land/std@0.190.0/path/posix.ts": "8b7c67ac338714b30c816079303d0285dd24af6b284f7ad63da5b27372a2c94d", - "https://deno.land/std@0.190.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1", - "https://deno.land/std@0.190.0/path/win32.ts": "d186344e5583bcbf8b18af416d13d82b35a317116e6460a5a3953508c3de5bba", - "https://deno.land/std@0.190.0/streams/byte_slice_stream.ts": "225d57263a34325d7c96cb3dafeb478eec0e6fd05cd0458d678752eadd132bb4", - "https://deno.land/std@0.190.0/version.ts": "c3fdbeb886289441c29bb900612bb35815659717391f631be2c14a34fca40c52", + "https://deno.land/std@0.201.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", + "https://deno.land/std@0.201.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", + "https://deno.land/std@0.201.0/encoding/base32.ts": "c329447451560ec692b9eb4d1badb6437f1d419ddbb21c1f994b0fe0b6b66cc8", + "https://deno.land/std@0.201.0/fs/_util.ts": "fbf57dcdc9f7bc8128d60301eece608246971a7836a3bb1e78da75314f08b978", + "https://deno.land/std@0.201.0/fs/copy.ts": "23cc1c465babe5ca4d69778821e2f8addc44593e30a5ca0b902b3784eed75bb6", + "https://deno.land/std@0.201.0/fs/empty_dir.ts": "2e52cd4674d18e2e007175c80449fc3d263786a1361e858d9dfa9360a6581b47", + "https://deno.land/std@0.201.0/fs/ensure_dir.ts": "dc64c4c75c64721d4e3fb681f1382f803ff3d2868f08563ff923fdd20d071c40", + "https://deno.land/std@0.201.0/fs/ensure_file.ts": "39ac83cc283a20ec2735e956adf5de3e8a3334e0b6820547b5772f71c49ae083", + "https://deno.land/std@0.201.0/fs/ensure_link.ts": "c15e69c48556d78aae31b83e0c0ece04b7b8bc0951412f5b759aceb6fde7f0ac", + "https://deno.land/std@0.201.0/fs/ensure_symlink.ts": "b389c8568f0656d145ac7ece472afe710815cccbb2ebfd19da7978379ae143fe", + "https://deno.land/std@0.201.0/fs/eol.ts": "f1f2eb348a750c34500741987b21d65607f352cf7205f48f4319d417fff42842", + "https://deno.land/std@0.201.0/fs/exists.ts": "cb59a853d84871d87acab0e7936a4dac11282957f8e195102c5a7acb42546bb8", + "https://deno.land/std@0.201.0/fs/expand_glob.ts": "52b8b6f5b1fa585c348250da1c80ce5d820746cb4a75d874b3599646f677d3a7", + "https://deno.land/std@0.201.0/fs/mod.ts": "bc3d0acd488cc7b42627044caf47d72019846d459279544e1934418955ba4898", + "https://deno.land/std@0.201.0/fs/move.ts": "b4f8f46730b40c32ea3c0bc8eb0fd0e8139249a698883c7b3756424cf19785c9", + "https://deno.land/std@0.201.0/fs/walk.ts": "a16146724a6aaf9efdb92023a74e9805195c3469900744ce5de4113b07b29779", + "https://deno.land/std@0.201.0/jsonc/mod.ts": "b88dce28eb3645667caa856538ae2fe87af51410822544a0b45a4177ef3bd7dd", + "https://deno.land/std@0.201.0/jsonc/parse.ts": "c1096e2b7ffb4996d7ed841dfdb29a4fccc78edcc55299beaa20d6fe5facf7b6", + "https://deno.land/std@0.201.0/path/_basename.ts": "057d420c9049821f983f784fd87fa73ac471901fb628920b67972b0f44319343", + "https://deno.land/std@0.201.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", + "https://deno.land/std@0.201.0/path/_dirname.ts": "355e297236b2218600aee7a5301b937204c62e12da9db4b0b044993d9e658395", + "https://deno.land/std@0.201.0/path/_extname.ts": "eaaa5aae1acf1f03254d681bd6a8ce42a9cb5b7ff2213a9d4740e8ab31283664", + "https://deno.land/std@0.201.0/path/_format.ts": "4a99270d6810f082e614309164fad75d6f1a483b68eed97c830a506cc589f8b4", + "https://deno.land/std@0.201.0/path/_from_file_url.ts": "6eadfae2e6f63ad9ee46b26db4a1b16583055c0392acedfb50ed2fc694b6f581", + "https://deno.land/std@0.201.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", + "https://deno.land/std@0.201.0/path/_is_absolute.ts": "05dac10b5e93c63198b92e3687baa2be178df5321c527dc555266c0f4f51558c", + "https://deno.land/std@0.201.0/path/_join.ts": "815f5e85b042285175b1492dd5781240ce126c23bd97bad6b8211fe7129c538e", + "https://deno.land/std@0.201.0/path/_normalize.ts": "a19ec8706b2707f9dd974662a5cd89fad438e62ab1857e08b314a8eb49a34d81", + "https://deno.land/std@0.201.0/path/_os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", + "https://deno.land/std@0.201.0/path/_parse.ts": "0f9b0ff43682dd9964eb1c4398610c4e165d8db9d3ac9d594220217adf480cfa", + "https://deno.land/std@0.201.0/path/_relative.ts": "27bdeffb5311a47d85be26d37ad1969979359f7636c5cd9fcf05dcd0d5099dc5", + "https://deno.land/std@0.201.0/path/_resolve.ts": "7a3616f1093735ed327e758313b79c3c04ea921808ca5f19ddf240cb68d0adf6", + "https://deno.land/std@0.201.0/path/_to_file_url.ts": "a141e4a525303e1a3a0c0571fd024552b5f3553a2af7d75d1ff3a503dcbb66d8", + "https://deno.land/std@0.201.0/path/_to_namespaced_path.ts": "0d5f4caa2ed98ef7a8786286df6af804b50e38859ae897b5b5b4c8c5930a75c8", + "https://deno.land/std@0.201.0/path/_util.ts": "4e191b1bac6b3bf0c31aab42e5ca2e01a86ab5a0d2e08b75acf8585047a86221", + "https://deno.land/std@0.201.0/path/basename.ts": "bdfa5a624c6a45564dc6758ef2077f2822978a6dbe77b0a3514f7d1f81362930", + "https://deno.land/std@0.201.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", + "https://deno.land/std@0.201.0/path/dirname.ts": "b6533f4ee4174a526dec50c279534df5345836dfdc15318400b08c62a62a39dd", + "https://deno.land/std@0.201.0/path/extname.ts": "62c4b376300795342fe1e4746c0de518b4dc9c4b0b4617bfee62a2973a9555cf", + "https://deno.land/std@0.201.0/path/format.ts": "110270b238514dd68455a4c54956215a1aff7e37e22e4427b7771cefe1920aa5", + "https://deno.land/std@0.201.0/path/from_file_url.ts": "9f5cb58d58be14c775ec2e57fc70029ac8b17ed3bd7fe93e475b07280adde0ac", + "https://deno.land/std@0.201.0/path/glob.ts": "593e2c3573883225c25c5a21aaa8e9382a696b8e175ea20a3b6a1471ad17aaed", + "https://deno.land/std@0.201.0/path/is_absolute.ts": "0b92eb35a0a8780e9f16f16bb23655b67dace6a8e0d92d42039e518ee38103c1", + "https://deno.land/std@0.201.0/path/join.ts": "31c5419f23d91655b08ec7aec403f4e4cd1a63d39e28f6e42642ea207c2734f8", + "https://deno.land/std@0.201.0/path/mod.ts": "6e1efb0b13121463aedb53ea51dabf5639a3172ab58c89900bbb72b486872532", + "https://deno.land/std@0.201.0/path/normalize.ts": "6ea523e0040979dd7ae2f1be5bf2083941881a252554c0f32566a18b03021955", + "https://deno.land/std@0.201.0/path/parse.ts": "be8de342bb9e1924d78dc4d93c45215c152db7bf738ec32475560424b119b394", + "https://deno.land/std@0.201.0/path/posix.ts": "0a1c1952d132323a88736d03e92bd236f3ed5f9f079e5823fae07c8d978ee61b", + "https://deno.land/std@0.201.0/path/relative.ts": "8bedac226afd360afc45d451a6c29fabceaf32978526bcb38e0c852661f66c61", + "https://deno.land/std@0.201.0/path/resolve.ts": "133161e4949fc97f9ca67988d51376b0f5eef8968a6372325ab84d39d30b80dc", + "https://deno.land/std@0.201.0/path/separator.ts": "40a3e9a4ad10bef23bc2cd6c610291b6c502a06237c2c4cd034a15ca78dedc1f", + "https://deno.land/std@0.201.0/path/to_file_url.ts": "00e6322373dd51ad109956b775e4e72e5f9fa68ce2c6b04e4af2a6eed3825d31", + "https://deno.land/std@0.201.0/path/to_namespaced_path.ts": "1b1db3055c343ab389901adfbda34e82b7386bcd1c744d54f9c1496ee0fd0c3d", + "https://deno.land/std@0.201.0/path/win32.ts": "8b3f80ef7a462511d5e8020ff490edcaa0a0d118f1b1e9da50e2916bdd73f9dd", + "https://deno.land/std@0.203.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", + "https://deno.land/std@0.203.0/path/_normalize.ts": "a19ec8706b2707f9dd974662a5cd89fad438e62ab1857e08b314a8eb49a34d81", + "https://deno.land/std@0.203.0/path/_util.ts": "4e191b1bac6b3bf0c31aab42e5ca2e01a86ab5a0d2e08b75acf8585047a86221", + "https://deno.land/std@0.205.0/path/_common/assert_path.ts": "061e4d093d4ba5aebceb2c4da3318bfe3289e868570e9d3a8e327d91c2958946", + "https://deno.land/std@0.205.0/path/_common/constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", + "https://deno.land/std@0.205.0/path/_common/normalize.ts": "2ba7fb4cc9fafb0f38028f434179579ce61d4d9e51296fad22b701c3d3cd7397", + "https://deno.land/std@0.205.0/path/_common/normalize_string.ts": "88c472f28ae49525f9fe82de8c8816d93442d46a30d6bb5063b07ff8a89ff589", + "https://deno.land/std@0.205.0/path/posix/_util.ts": "ecf49560fedd7dd376c6156cc5565cad97c1abe9824f4417adebc7acc36c93e5", + "https://deno.land/std@0.205.0/path/posix/join.ts": "0c0d84bdc344876930126640011ec1b888e6facf74153ffad9ef26813aa2a076", + "https://deno.land/std@0.205.0/path/posix/normalize.ts": "11de90a94ab7148cc46e5a288f7d732aade1d616bc8c862f5560fa18ff987b4b", + "https://deno.land/std@0.206.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", + "https://deno.land/std@0.206.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", + "https://deno.land/std@0.206.0/collections/_utils.ts": "5114abc026ddef71207a79609b984614e66a63a4bda17d819d56b0e72c51527e", + "https://deno.land/std@0.206.0/collections/deep_merge.ts": "9db788ba56cb05b65c77166b789e58e125dff159b7f41bf4d19dc1cba19ecb8b", + "https://deno.land/std@0.206.0/encoding/_util.ts": "f368920189c4fe6592ab2e93bd7ded8f3065b84f95cd3e036a4a10a75649dcba", + "https://deno.land/std@0.206.0/encoding/base64.ts": "cc03110d6518170aeaa68ec97f89c6d6e2276294b30807e7332591d7ce2e4b72", + "https://deno.land/std@0.206.0/flags/mod.ts": "0948466fc437f017f00c0b972a422b3dc3317a790bcf326429d23182977eaf9f", + "https://deno.land/std@0.206.0/fmt/bytes.ts": "f29cf69e0791d375f9f5d94ae1f0641e5a03b975f32ddf86d70f70fdf37e7b6a", + "https://deno.land/std@0.206.0/fmt/colors.ts": "c51c4642678eb690dcf5ffee5918b675bf01a33fba82acf303701ae1a4f8c8d9", + "https://deno.land/std@0.206.0/http/etag.ts": "259abf65316c728660e34a100dcb07a1303c37ab6ca3d4ee068503c18b4358b0", + "https://deno.land/std@0.206.0/http/file_server.ts": "7632f763996c74cc7ea8f38bdbf76fb378fc848edc06576e1a13e7340e927b26", + "https://deno.land/std@0.206.0/http/status.ts": "1353e82e27996ef123dc625e5fcc9d66b94d92e5175879fa5e9f0dc39330206a", + "https://deno.land/std@0.206.0/media_types/_db.ts": "7606d83e31f23ce1a7968cbaee852810c2cf477903a095696cdc62eaab7ce570", + "https://deno.land/std@0.206.0/media_types/_util.ts": "0879b04cc810ff18d3dcd97d361e03c9dfb29f67d7fc4a9c6c9d387282ef5fe8", + "https://deno.land/std@0.206.0/media_types/content_type.ts": "a7a3cb6a2b6e81101637afcaa9884d655b4568ded84fa7e6169bb690a87ee2aa", + "https://deno.land/std@0.206.0/media_types/format_media_type.ts": "f5e1073c05526a6f5a516ac5c5587a1abd043bf1039c71cde1166aa4328c8baf", + "https://deno.land/std@0.206.0/media_types/get_charset.ts": "18b88274796fda5d353806bf409eb1d2ddb3f004eb4bd311662c4cdd8ac173db", + "https://deno.land/std@0.206.0/media_types/parse_media_type.ts": "31ccf2388ffab31b49500bb89fa0f5de189c8897e2ee6c9954f207637d488211", + "https://deno.land/std@0.206.0/media_types/vendor/mime-db.v1.52.0.ts": "6925bbcae81ca37241e3f55908d0505724358cda3384eaea707773b2c7e99586", + "https://deno.land/std@0.206.0/path/_common/assert_path.ts": "061e4d093d4ba5aebceb2c4da3318bfe3289e868570e9d3a8e327d91c2958946", + "https://deno.land/std@0.206.0/path/_common/constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", + "https://deno.land/std@0.206.0/path/_common/normalize.ts": "2ba7fb4cc9fafb0f38028f434179579ce61d4d9e51296fad22b701c3d3cd7397", + "https://deno.land/std@0.206.0/path/_common/normalize_string.ts": "88c472f28ae49525f9fe82de8c8816d93442d46a30d6bb5063b07ff8a89ff589", + "https://deno.land/std@0.206.0/path/_common/relative.ts": "1af19d787a2a84b8c534cc487424fe101f614982ae4851382c978ab2216186b4", + "https://deno.land/std@0.206.0/path/_os.ts": "30b0c2875f360c9296dbe6b7f2d528f0f9c741cecad2e97f803f5219e91b40a2", + "https://deno.land/std@0.206.0/path/extname.ts": "2da4e2490f3b48b7121d19fb4c91681a5e11bd6bd99df4f6f47d7a71bb6ecdf2", + "https://deno.land/std@0.206.0/path/join.ts": "98d3d76c819af4a11a81d5ba2dbb319f1ce9d63fc2b615597d4bcfddd4a89a09", + "https://deno.land/std@0.206.0/path/posix/_util.ts": "ecf49560fedd7dd376c6156cc5565cad97c1abe9824f4417adebc7acc36c93e5", + "https://deno.land/std@0.206.0/path/posix/extname.ts": "ee7f6571a9c0a37f9218fbf510c440d1685a7c13082c348d701396cc795e0be0", + "https://deno.land/std@0.206.0/path/posix/join.ts": "0c0d84bdc344876930126640011ec1b888e6facf74153ffad9ef26813aa2a076", + "https://deno.land/std@0.206.0/path/posix/normalize.ts": "11de90a94ab7148cc46e5a288f7d732aade1d616bc8c862f5560fa18ff987b4b", + "https://deno.land/std@0.206.0/path/posix/relative.ts": "e2f230608b0f083e6deaa06e063943e5accb3320c28aef8d87528fbb7fe6504c", + "https://deno.land/std@0.206.0/path/posix/resolve.ts": "51579d83159d5c719518c9ae50812a63959bbcb7561d79acbdb2c3682236e285", + "https://deno.land/std@0.206.0/path/relative.ts": "23d45ede8b7ac464a8299663a43488aad6b561414e7cbbe4790775590db6349c", + "https://deno.land/std@0.206.0/path/resolve.ts": "5b184efc87155a0af9fa305ff68a109e28de9aee81fc3e77cd01380f19daf867", + "https://deno.land/std@0.206.0/path/separator.ts": "40a3e9a4ad10bef23bc2cd6c610291b6c502a06237c2c4cd034a15ca78dedc1f", + "https://deno.land/std@0.206.0/path/windows/_util.ts": "f32b9444554c8863b9b4814025c700492a2b57ff2369d015360970a1b1099d54", + "https://deno.land/std@0.206.0/path/windows/join.ts": "e6600bf88edeeef4e2276e155b8de1d5dec0435fd526ba2dc4d37986b2882f16", + "https://deno.land/std@0.206.0/path/windows/normalize.ts": "9deebbf40c81ef540b7b945d4ccd7a6a2c5a5992f791e6d3377043031e164e69", + "https://deno.land/std@0.206.0/path/windows/relative.ts": "026855cd2c36c8f28f1df3c6fbd8f2449a2aa21f48797a74700c5d872b86d649", + "https://deno.land/std@0.206.0/path/windows/resolve.ts": "5ff441ab18a2346abadf778121128ee71bda4d0898513d4639a6ca04edca366b", + "https://deno.land/std@0.206.0/streams/byte_slice_stream.ts": "c46d7c74836fc8c1a9acd9fe211cbe1bbaaee1b36087c834fb03af4991135c3a", + "https://deno.land/std@0.206.0/version.ts": "0f88dc210ada63987aec52fc935b8db072905d83bf696cf4ec77148c415111d2", "https://deno.land/x/continuation@0.1.5/mod.ts": "690def2735046367b3e1b4bc6e51b5912f2ed09c41c7df7a55c060f23720ad33", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/abort-signal.ts": "31224b5f61f1ef58a2acf72bfc1cb9c9bf589f15c263b6555eb06f1b49f77b54", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/async.ts": "3e007ef245abb240de07029f523c7ef74b9bc383db5716f89d261a5150295777", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/call.ts": "69c465573031e6315e375c17e01e820239e01a93107866dd9f5ef584b79d13dd", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/channel.ts": "405659d3453dcc0e3ac4190c38bf9a1140f0a4ef4981985ce3564c7aadba67e9", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/context.ts": "7d17fcb2fe432eb6da2eeabc425deeac0855d9efd7f2856bdda0a24d62a97968", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/deps.ts": "91062b4b97089a8cf36550d4f9605d325a0fd19bebc72d15524481a3b56ea669", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/events.ts": "787b666c5f9fcb8b175a9bcbfdabbfb7b1283da35163fd1f75caf11dd96c2fc0", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/filter.ts": "ad3ba0ce59923306620da410393c8f248fb9885a55c8cbd1f3670521cd96cc68", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/first.ts": "6dfacc67b5c6b11bf1df1cf272491ed24a88431ea2eb8a36216b848fbfbe2e8e", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/future.ts": "7800fde82168c9be4644ec9d2fe532b35a8300baf34a8e1673c6fe6067365bfc", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/instructions.ts": "c2f99e6a85d9c67896d2980293fb3183aa101bb86eea057322077dd6db0906d6", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/lazy.ts": "92ea526c5ad7d88290f2a87168e038d482f97421379508d85cf2e049ee60639b", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/log.ts": "312b6fb4226be5554b945bc9eb7b05ed7b2dd53dd139ca86971bef256eb78997", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/main.ts": "da8ad0b03e6b49a6fd05da8ad1ce5a2d477a9ee75e2ff3f0a37f19b264797289", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/map.ts": "26184b51211c87e37396f336c7a8212ea46d9fdea210df208127a6394eee69f9", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/mod.ts": "5fe0f3ac29c64e13f3692c78b0c1a7b2565d67051c7ca87d8f9d6a652a218841", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/op.ts": "483465a66fb9fa7529eb8cafbaf94c085b1a5e72876e5d34e44cd4f875b2141b", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/pipe.ts": "4a28fa93a1ba53661bafb84265f3fcb5614920bbecc0db1c261e1093da3b2cdf", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/result.ts": "41414d8f376f73c95483982fe81fe9c256a6d836f9b2e4ae31d1a6fc09dec69d", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/run.ts": "5c1eb89e19246ee4d47ec55c1b7b88842366151c1caabd5f3144b3d59e9d30db", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/run/block.ts": "41b5e6ba7b80de347589b886e2a414a720b041853e625c6a35ff5d74bb5476f7", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/run/create.ts": "be9139af2fbe15908256d2d159dec8dca079f94cf02d488074c94fa26fc651fa", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/run/event-stream.ts": "8c6d40bf315652535a85aa02a61256678dc4e537a4fb436fbe5fecdad794c076", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/run/frame.ts": "5f3cdd85833e32d94d24da76a2ca584af0a9d60abbe43300beb7cda71250cfda", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/run/scope.ts": "5c406a4230bd4f4613fdccfa58c69c61b500222a83d92829596b3cc67990427d", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/sleep.ts": "d7000c05ace8143efd80f1e35243f5521316bc334e4051afe692e1a0c7268304", - "https://deno.land/x/effection@3.0.0-alpha.9/lib/types.ts": "3eab26c41d8d49cf7a60c433805601e889e814e8c7e0cd1db26a8df48611146f", - "https://deno.land/x/effection@3.0.0-alpha.9/mod.ts": "ffae461c16d4a1bf24c2179582ab8d5c81ad0df61e4ae2fba51ef5e5bdf90345", - "https://deno.land/x/hastx@v0.0.6/jsx-runtime.ts": "5e1db59ceea4834b8d248a018a5adfc73a09922d8dcdaa3a3bb228c884da0f6c", + "https://deno.land/x/deno_cache@0.4.1/auth_tokens.ts": "5fee7e9155e78cedf3f6ff3efacffdb76ac1a76c86978658d9066d4fb0f7326e", + "https://deno.land/x/deno_cache@0.4.1/cache.ts": "51f72f4299411193d780faac8c09d4e8cbee951f541121ef75fcc0e94e64c195", + "https://deno.land/x/deno_cache@0.4.1/deno_dir.ts": "f2a9044ce8c7fe1109004cda6be96bf98b08f478ce77e7a07f866eff1bdd933f", + "https://deno.land/x/deno_cache@0.4.1/deps.ts": "8974097d6c17e65d9a82d39377ae8af7d94d74c25c0cbb5855d2920e063f2343", + "https://deno.land/x/deno_cache@0.4.1/dirs.ts": "d2fa473ef490a74f2dcb5abb4b9ab92a48d2b5b6320875df2dee64851fa64aa9", + "https://deno.land/x/deno_cache@0.4.1/disk_cache.ts": "1f3f5232cba4c56412d93bdb324c624e95d5dd179d0578d2121e3ccdf55539f9", + "https://deno.land/x/deno_cache@0.4.1/file_fetcher.ts": "07a6c5f8fd94bf50a116278cc6012b4921c70d2251d98ce1c9f3c352135c39f7", + "https://deno.land/x/deno_cache@0.4.1/http_cache.ts": "f632e0d6ec4a5d61ae3987737a72caf5fcdb93670d21032ddb78df41131360cd", + "https://deno.land/x/deno_cache@0.4.1/mod.ts": "ef1cda9235a93b89cb175fe648372fc0f785add2a43aa29126567a05e3e36195", + "https://deno.land/x/deno_cache@0.4.1/util.ts": "8cb686526f4be5205b92c819ca2ce82220aa0a8dd3613ef0913f6dc269dbbcfe", + "https://deno.land/x/denoflate@1.2.1/mod.ts": "f5628e44b80b3d80ed525afa2ba0f12408e3849db817d47a883b801f9ce69dd6", + "https://deno.land/x/denoflate@1.2.1/pkg/denoflate.js": "b9f9ad9457d3f12f28b1fb35c555f57443427f74decb403113d67364e4f2caf4", + "https://deno.land/x/denoflate@1.2.1/pkg/denoflate_bg.wasm.js": "d581956245407a2115a3d7e8d85a9641c032940a8e810acbd59ca86afd34d44d", + "https://deno.land/x/effection@3.0.0-beta.1/lib/abort-signal.ts": "8be1b331b2bc417d70fe4c07e0b806e89972b8eab519ce58beed7ec632ae9048", + "https://deno.land/x/effection@3.0.0-beta.1/lib/all.ts": "acadab8258228e290192f587c8c532428f9093337a9b7688ae55cbc2cacd5caf", + "https://deno.land/x/effection@3.0.0-beta.1/lib/async.ts": "c369af9c29c16a5e4188fba1e22fd4fac8eb882f5e1b11d491cd6a99faa61a44", + "https://deno.land/x/effection@3.0.0-beta.1/lib/call.ts": "1ab6649c2944b72ffd27a495712562311abf414c548ada9cc1c8edea96c46d37", + "https://deno.land/x/effection@3.0.0-beta.1/lib/channel.ts": "5f7e34e616a63802ada3b7b019dc794a06959d1e2769c8fa27fcbe61400685fd", + "https://deno.land/x/effection@3.0.0-beta.1/lib/context.ts": "108989ac839d6756e30f6c0afc458bfa3975dd0f970d5173b6b8f8473ce4c335", + "https://deno.land/x/effection@3.0.0-beta.1/lib/deps.ts": "91062b4b97089a8cf36550d4f9605d325a0fd19bebc72d15524481a3b56ea669", + "https://deno.land/x/effection@3.0.0-beta.1/lib/each.ts": "1f7411fb5406f1106fbbed70363ed948d0d20b4fb86bb11e301b7e52da8b374a", + "https://deno.land/x/effection@3.0.0-beta.1/lib/ensure.ts": "c3640cc12c1bc747a8a4086af476840db026d04ea22f45a697d53617b2b1cc66", + "https://deno.land/x/effection@3.0.0-beta.1/lib/events.ts": "d962e7403d62948642f5a3161f611f4375932aa8702050575f0d538aab7c3467", + "https://deno.land/x/effection@3.0.0-beta.1/lib/filter.ts": "39f349ee921ba718cf3259e05003255eeeafbb5ca6e437d2d269b1805da2236e", + "https://deno.land/x/effection@3.0.0-beta.1/lib/first.ts": "5bc321069d2e2b87b6623f626a929d5d5ba32bca32ee03b37bdc1a64722eebb9", + "https://deno.land/x/effection@3.0.0-beta.1/lib/instructions.ts": "5fd8638e385068adc6c1a896bba02b736d7c2c26e5124d3d063fdbcaf140abec", + "https://deno.land/x/effection@3.0.0-beta.1/lib/lazy.ts": "92ea526c5ad7d88290f2a87168e038d482f97421379508d85cf2e049ee60639b", + "https://deno.land/x/effection@3.0.0-beta.1/lib/lift.ts": "0c622bf0359f92235547b57efa66139b265a7b259428e6883469de0b3af32f5d", + "https://deno.land/x/effection@3.0.0-beta.1/lib/main.ts": "a0deaf1d1d958ef7a5821d8ac3dfbd190a47608d603798a5fc3b0c2309a724da", + "https://deno.land/x/effection@3.0.0-beta.1/lib/map.ts": "1a0c369dad53affc4b798a04142de637a75f981385acafcafd26bdc569675bc2", + "https://deno.land/x/effection@3.0.0-beta.1/lib/mod.ts": "f7189b02d008baba1166d33779379b12f7104e0b6d373194270ac126a73ba82d", + "https://deno.land/x/effection@3.0.0-beta.1/lib/pause.ts": "a690b0d67cf970c34f528df8c61d69eb43deda9817362776f6359f506dc0da45", + "https://deno.land/x/effection@3.0.0-beta.1/lib/pipe.ts": "4a28fa93a1ba53661bafb84265f3fcb5614920bbecc0db1c261e1093da3b2cdf", + "https://deno.land/x/effection@3.0.0-beta.1/lib/queue.ts": "80c6234cb6eaba9fd1abdae077e73f51897b099ea54f852b9a744e8eba51302f", + "https://deno.land/x/effection@3.0.0-beta.1/lib/race.ts": "0c43f24ce5006768f5cbac8d6f5dc07848bafa625cc0bc6c24fb6a2f2a8808f2", + "https://deno.land/x/effection@3.0.0-beta.1/lib/result.ts": "44e4bdadad155beb9bbfe41948819bbcb9e27a772283e52e89981bd6636a8687", + "https://deno.land/x/effection@3.0.0-beta.1/lib/run.ts": "b85043bc8b30c0eb0d04654cdd07004b21145f2e3f59f52e39df76558e324ca4", + "https://deno.land/x/effection@3.0.0-beta.1/lib/run/create.ts": "be9139af2fbe15908256d2d159dec8dca079f94cf02d488074c94fa26fc651fa", + "https://deno.land/x/effection@3.0.0-beta.1/lib/run/frame.ts": "b9b85112e3168c0fc91e01b1df60f2e911ee1a44314678944d9cbfa71b0641de", + "https://deno.land/x/effection@3.0.0-beta.1/lib/run/scope.ts": "0e164df8b9825ac1aef3ff1e35a85cf6c82ac48318ba9942d76bf477337895ca", + "https://deno.land/x/effection@3.0.0-beta.1/lib/run/task.ts": "b4b019d6e32d4c22c83ea9d1cfd64a3d587587080d459aec00aa9e6ba9d49b2a", + "https://deno.land/x/effection@3.0.0-beta.1/lib/run/value.ts": "d57428b45dfeecc9df1e68dadf8697dbc33cd412e6ffcab9d0ba4368e8c1fbd6", + "https://deno.land/x/effection@3.0.0-beta.1/lib/shift-sync.ts": "74ecefa9cb2e145a3c52f363319f8d6296b804600852044b7d14bd53bc10b512", + "https://deno.land/x/effection@3.0.0-beta.1/lib/signal.ts": "acae6b2f348a9c8c6bc43a9a5bd53e0187c8048619d7e0c6d6cf70e3a46bb8bf", + "https://deno.land/x/effection@3.0.0-beta.1/lib/sleep.ts": "44e3a80248dad7a47066a99a7daec9b318e87d5d211adf27776145544d455689", + "https://deno.land/x/effection@3.0.0-beta.1/lib/types.ts": "4595c09ccfaae87c5a1d12006c23e5f4083fcd5658c322350f27801a9a9cb348", + "https://deno.land/x/effection@3.0.0-beta.1/mod.ts": "ffae461c16d4a1bf24c2179582ab8d5c81ad0df61e4ae2fba51ef5e5bdf90345", + "https://deno.land/x/effection@3.0.0-beta.2/lib/abort-signal.ts": "8be1b331b2bc417d70fe4c07e0b806e89972b8eab519ce58beed7ec632ae9048", + "https://deno.land/x/effection@3.0.0-beta.2/lib/all.ts": "acadab8258228e290192f587c8c532428f9093337a9b7688ae55cbc2cacd5caf", + "https://deno.land/x/effection@3.0.0-beta.2/lib/async.ts": "086b27b253be944c47c633d105f1657e243cd8c0d35b9a0dc5383528d7235dde", + "https://deno.land/x/effection@3.0.0-beta.2/lib/call.ts": "1ab6649c2944b72ffd27a495712562311abf414c548ada9cc1c8edea96c46d37", + "https://deno.land/x/effection@3.0.0-beta.2/lib/channel.ts": "f86b36666463f8f86fc1ac1726a94f0f08dc05559ba710b8eb93581b2b8588e6", + "https://deno.land/x/effection@3.0.0-beta.2/lib/context.ts": "108989ac839d6756e30f6c0afc458bfa3975dd0f970d5173b6b8f8473ce4c335", + "https://deno.land/x/effection@3.0.0-beta.2/lib/deps.ts": "91062b4b97089a8cf36550d4f9605d325a0fd19bebc72d15524481a3b56ea669", + "https://deno.land/x/effection@3.0.0-beta.2/lib/each.ts": "9689346d1db3fedcd87d48c70be5515ad3e18fa4b894755fa53910fb8ad356f3", + "https://deno.land/x/effection@3.0.0-beta.2/lib/ensure.ts": "c3640cc12c1bc747a8a4086af476840db026d04ea22f45a697d53617b2b1cc66", + "https://deno.land/x/effection@3.0.0-beta.2/lib/events.ts": "d962e7403d62948642f5a3161f611f4375932aa8702050575f0d538aab7c3467", + "https://deno.land/x/effection@3.0.0-beta.2/lib/filter.ts": "39f349ee921ba718cf3259e05003255eeeafbb5ca6e437d2d269b1805da2236e", + "https://deno.land/x/effection@3.0.0-beta.2/lib/first.ts": "5bc321069d2e2b87b6623f626a929d5d5ba32bca32ee03b37bdc1a64722eebb9", + "https://deno.land/x/effection@3.0.0-beta.2/lib/instructions.ts": "5fd8638e385068adc6c1a896bba02b736d7c2c26e5124d3d063fdbcaf140abec", + "https://deno.land/x/effection@3.0.0-beta.2/lib/lazy.ts": "92ea526c5ad7d88290f2a87168e038d482f97421379508d85cf2e049ee60639b", + "https://deno.land/x/effection@3.0.0-beta.2/lib/lift.ts": "0c622bf0359f92235547b57efa66139b265a7b259428e6883469de0b3af32f5d", + "https://deno.land/x/effection@3.0.0-beta.2/lib/main.ts": "a0deaf1d1d958ef7a5821d8ac3dfbd190a47608d603798a5fc3b0c2309a724da", + "https://deno.land/x/effection@3.0.0-beta.2/lib/map.ts": "1a0c369dad53affc4b798a04142de637a75f981385acafcafd26bdc569675bc2", + "https://deno.land/x/effection@3.0.0-beta.2/lib/mod.ts": "f7189b02d008baba1166d33779379b12f7104e0b6d373194270ac126a73ba82d", + "https://deno.land/x/effection@3.0.0-beta.2/lib/pause.ts": "a690b0d67cf970c34f528df8c61d69eb43deda9817362776f6359f506dc0da45", + "https://deno.land/x/effection@3.0.0-beta.2/lib/pipe.ts": "4a28fa93a1ba53661bafb84265f3fcb5614920bbecc0db1c261e1093da3b2cdf", + "https://deno.land/x/effection@3.0.0-beta.2/lib/queue.ts": "80c6234cb6eaba9fd1abdae077e73f51897b099ea54f852b9a744e8eba51302f", + "https://deno.land/x/effection@3.0.0-beta.2/lib/race.ts": "0c43f24ce5006768f5cbac8d6f5dc07848bafa625cc0bc6c24fb6a2f2a8808f2", + "https://deno.land/x/effection@3.0.0-beta.2/lib/result.ts": "44e4bdadad155beb9bbfe41948819bbcb9e27a772283e52e89981bd6636a8687", + "https://deno.land/x/effection@3.0.0-beta.2/lib/run.ts": "b85043bc8b30c0eb0d04654cdd07004b21145f2e3f59f52e39df76558e324ca4", + "https://deno.land/x/effection@3.0.0-beta.2/lib/run/create.ts": "be9139af2fbe15908256d2d159dec8dca079f94cf02d488074c94fa26fc651fa", + "https://deno.land/x/effection@3.0.0-beta.2/lib/run/frame.ts": "b9b85112e3168c0fc91e01b1df60f2e911ee1a44314678944d9cbfa71b0641de", + "https://deno.land/x/effection@3.0.0-beta.2/lib/run/scope.ts": "0e164df8b9825ac1aef3ff1e35a85cf6c82ac48318ba9942d76bf477337895ca", + "https://deno.land/x/effection@3.0.0-beta.2/lib/run/task.ts": "b4b019d6e32d4c22c83ea9d1cfd64a3d587587080d459aec00aa9e6ba9d49b2a", + "https://deno.land/x/effection@3.0.0-beta.2/lib/run/value.ts": "d57428b45dfeecc9df1e68dadf8697dbc33cd412e6ffcab9d0ba4368e8c1fbd6", + "https://deno.land/x/effection@3.0.0-beta.2/lib/shift-sync.ts": "74ecefa9cb2e145a3c52f363319f8d6296b804600852044b7d14bd53bc10b512", + "https://deno.land/x/effection@3.0.0-beta.2/lib/signal.ts": "da723b43b6bd61ea86dab991e9a6c6249a61d3b1c3c98ef473b160c9383e7d07", + "https://deno.land/x/effection@3.0.0-beta.2/lib/sleep.ts": "44e3a80248dad7a47066a99a7daec9b318e87d5d211adf27776145544d455689", + "https://deno.land/x/effection@3.0.0-beta.2/lib/types.ts": "4595c09ccfaae87c5a1d12006c23e5f4083fcd5658c322350f27801a9a9cb348", + "https://deno.land/x/effection@3.0.0-beta.2/mod.ts": "ffae461c16d4a1bf24c2179582ab8d5c81ad0df61e4ae2fba51ef5e5bdf90345", + "https://deno.land/x/esbuild@v0.19.5/mod.js": "cbc57c89f925d7b6e752594832450f2af57d5f5c3bd4e488ceba76fad233db6c", + "https://deno.land/x/esbuild_deno_loader@0.8.2/deps.ts": "c1aa4747e43d3ae09da96e54aac798ed9bb967634cff72f21b7fab6e5435c293", + "https://deno.land/x/esbuild_deno_loader@0.8.2/mod.ts": "28524460bef46d487221b01ade6ed913d2e127de7eeee025ab75b34b491283da", + "https://deno.land/x/esbuild_deno_loader@0.8.2/src/deno.ts": "b0af3e430c068f18c6fa48c2083a1b4354b6c303e16fb37855e02fcafb95f36d", + "https://deno.land/x/esbuild_deno_loader@0.8.2/src/loader_native.ts": "3ffab59d0ed26c9329b2b84e0a775be5a910b7fed403a46edf4d2c3c8feb8b5a", + "https://deno.land/x/esbuild_deno_loader@0.8.2/src/loader_portable.ts": "d999f452ef3d8ec2dd3c8443f542adf57efc8a2cd59b29cc41f5b3d7dff512e5", + "https://deno.land/x/esbuild_deno_loader@0.8.2/src/plugin_deno_loader.ts": "166356133ee63d80e5559a10c18e10b625da96e39a4518b8c7adfef718bb4e32", + "https://deno.land/x/esbuild_deno_loader@0.8.2/src/plugin_deno_resolver.ts": "0449ed23ae93db1ec74d015a46934aefd7ba7a8f719f7a4980b616cb3f5bbee4", + "https://deno.land/x/esbuild_deno_loader@0.8.2/src/shared.ts": "33052684aeb542ebd24da372816bbbf885cd090a7ab0fde7770801f7f5b49572", + "https://deno.land/x/hastx@v0.0.10/html.ts": "54f86c5378dd7282142c9847efaf20b7ee244743f16af20a62900e912d1ae810", + "https://deno.land/x/importmap@0.2.1/_util.ts": "ada9a9618b537e6c0316c048a898352396c882b9f2de38aba18fd3f2950ede89", + "https://deno.land/x/importmap@0.2.1/mod.ts": "ae3d1cd7eabd18c01a4960d57db471126b020f23b37ef14e1359bbb949227ade", "https://deno.land/x/path_to_regexp@v6.2.1/index.ts": "894060567837bae8fc9c5cbd4d0a05e9024672083d5883b525c031eea940e556", - "https://esm.sh/@twind/core@1.1.3": "bf3e64c13de95b061a721831bf2aed322f6e7335848b06d805168c3212686c1d", - "https://esm.sh/@twind/preset-tailwind@1.1.4": "29f5e4774c344ff08d2636e3d86382060a8b40d6bce1c158b803df1823c2804c", - "https://esm.sh/@twind/preset-typography@1.0.7": "aa7359859b24662f2487a2f2476ba2a96addc57173edd3ebe3ded49874c01fc2", - "https://esm.sh/hast-util-to-html@8.0.4": "bc84fe34110790ac3819562a3fcc56671527390708abc21ee6e4756b76e2a508", - "https://esm.sh/v132/@twind/core@1.1.3/denonext/core.mjs": "c2618087a5d5cc406c7dc1079015f4d7cc874bee167f74e9945694896d907b6d", - "https://esm.sh/v132/@twind/preset-tailwind@1.1.4/denonext/preset-tailwind.mjs": "3cb9f5cde89e11cd2adad54ff264f62f5000ccb1694cd88874b1856eb2d8d7f7", - "https://esm.sh/v132/@twind/preset-typography@1.0.7/denonext/preset-typography.mjs": "3b4ae7a60b95548570a9388b1c4196a2c5afd16b1a7b486804d6914c1c615abf", - "https://esm.sh/v132/ccount@2.0.1/denonext/ccount.mjs": "7b32092651a866fcc992c028982ce5e911356da7653baa3febb1a8ccb93e30f8", - "https://esm.sh/v132/character-entities-html4@2.1.0/denonext/character-entities-html4.mjs": "0b4e64d1b0152acbeec6d854eadce6ceb2de05b0f459ad47485afa206f745f10", - "https://esm.sh/v132/character-entities-legacy@3.0.0/denonext/character-entities-legacy.mjs": "5da76ada1554e4956dc6b702ba92b56a3faf158b24bf45279c522e85f5d9cd21", - "https://esm.sh/v132/comma-separated-tokens@2.0.3/denonext/comma-separated-tokens.mjs": "ad5df8a36487e0a63d15bbbb6bab8b153e08583d0d5eb6d0058cd0fc619252e0", - "https://esm.sh/v132/hast-util-to-html@8.0.4/denonext/hast-util-to-html.mjs": "0ea8ca07f7f492dcfd7a4b29c42ed3623eac6e5be4346238036795e611e2d1b6", - "https://esm.sh/v132/hast-util-whitespace@2.0.1/denonext/hast-util-whitespace.mjs": "0d45cf1a1b1d7ba26d5a3f2fda8c929b02ef701786e656ade327d8c3f7b390bb", - "https://esm.sh/v132/html-void-elements@2.0.1/denonext/html-void-elements.mjs": "10f57d4f9d680bf2de32538d6aaf879f20d4ee02dd57879aa232319d6c984f54", - "https://esm.sh/v132/property-information@6.3.0/denonext/property-information.mjs": "892dd4dcba102c3cf2becccefb9bcc89b208a133e0547d8d8929016ffd4be0ca", - "https://esm.sh/v132/space-separated-tokens@2.0.2/denonext/space-separated-tokens.mjs": "f30773a9959cacfe7511c250e5d125a9f88ee00d3aef6e87b4d17fe49806b276", - "https://esm.sh/v132/stringify-entities@4.0.3/denonext/stringify-entities.mjs": "0faee16147724b4df7c88ecfbc6bf5fec40027b8c3a364f8f5aacdf656e44750", - "https://esm.sh/v132/zwitch@2.0.4/denonext/zwitch.mjs": "c0e8c246a1f38b425335ea78cc366a7801d3ef89701229a35f85a51310e6e49f" + "https://deno.land/x/revolution@0.3.0/jsx-runtime.ts": "b0d18239c9202c8881750902a8d19b07d84361ca4ab178edb1fd46b3542fd9a6", + "https://deno.land/x/revolution@0.3.0/lib/assertions.ts": "e7dc5c32b5609aaac5c1458796e4fdc2e858fcf538c64a95d80f1e3cf594a969", + "https://deno.land/x/revolution@0.3.0/lib/builder.ts": "e84a387070f569c724eabf270dc3850196fca671f44bc3521bfcf936015b3661", + "https://deno.land/x/revolution@0.3.0/lib/deps/effection.ts": "8c8157bd38f8cc2dbd4c8a9887b3f82c7fc7c2224cabe06f450b6733a65a0eaa", + "https://deno.land/x/revolution@0.3.0/lib/deps/hast.ts": "c18e53e92fadfc87f4e27d7568a3d764e8059d38ce54a4f02dc75451c0f716e6", + "https://deno.land/x/revolution@0.3.0/lib/island.ts": "7bfebfdc877648aadead95db7acab1ce249f43fb512bf11d6bb78727350012da", + "https://deno.land/x/revolution@0.3.0/lib/middleware.ts": "7e48ef017613fdd55488e4334b6027a4c41ec2a020c46df8090899f9497a32cb", + "https://deno.land/x/revolution@0.3.0/lib/middleware/concat.ts": "84cc5adcecd10614e5baa7080db343bfc313e4d451f29a9e4a12c229c6dd8da5", + "https://deno.land/x/revolution@0.3.0/lib/middleware/dispatch.ts": "1f461c54b7c326de7891694f80c465597c41fffe3b3eca8519210a1d5397d249", + "https://deno.land/x/revolution@0.3.0/lib/middleware/http-responses.ts": "cce163033268305d3176a077176bba3a2df5da11c491c297cf20b8cc4dcb6bac", + "https://deno.land/x/revolution@0.3.0/lib/middleware/island-middleware.ts": "91c482145220521adb5d39a2b68a8c5fb3c695d7bb8b02de8bb1b7635743d5a3", + "https://deno.land/x/revolution@0.3.0/lib/middleware/serialize-html.ts": "a3d1efd86d3d2abceb20244b274780b97a6f5343135e323cc6ab923bf3cdd5ae", + "https://deno.land/x/revolution@0.3.0/lib/middleware/serve-dir.ts": "7a26395f45ed40d697c90271e392da7e13db2cc1bb9faa559c05d8421b480669", + "https://deno.land/x/revolution@0.3.0/lib/mod.ts": "9d5f0463acb94b47b94aee1557a089546504451cfa23bac6d53739e4ea133d28", + "https://deno.land/x/revolution@0.3.0/lib/revolution.ts": "163125b36b601467f7e7fe10937d5d726887a874ad71161b2520933388115f77", + "https://deno.land/x/revolution@0.3.0/lib/route.ts": "d3b9cd8ab2c817d60f8d4bb2b5a35f3a6a6d1d895f92886930708b4c3ff7848a", + "https://deno.land/x/revolution@0.3.0/lib/server.ts": "7a261358c16aa799c8dcebc08933bdda8b7db8540f3e3120c36fcd512051d74d", + "https://deno.land/x/revolution@0.3.0/lib/types.ts": "2f32cbc673d496b5a37e5b9ff829577866d66a19b993ed59840b92700861d237", + "https://deno.land/x/revolution@0.3.0/mod.ts": "ffae461c16d4a1bf24c2179582ab8d5c81ad0df61e4ae2fba51ef5e5bdf90345" } } diff --git a/html.ts b/html.ts deleted file mode 100644 index ebbecba5..00000000 --- a/html.ts +++ /dev/null @@ -1,5 +0,0 @@ -import AppHtml from "./html/app.html.tsx"; -import IndexHtml from "./html/index.html.tsx"; -export * from "./html/advance-backstage-plugin-development.html.tsx"; - -export { AppHtml, IndexHtml }; diff --git a/html/advance-backstage-plugin-development.html.tsx b/html/advance-backstage-plugin-development.html.tsx deleted file mode 100644 index 02e38e38..00000000 --- a/html/advance-backstage-plugin-development.html.tsx +++ /dev/null @@ -1,792 +0,0 @@ -import type { Operation } from "effection"; -import { url } from "freejack/view.ts"; - -export function* AdvancedBackstagePluginDevelopmentHtml(): Operation< - JSX.Element -> { - let logoHumanitec = yield* url("../assets/client-logos/logo-humanitec.svg"); - let logoHP = yield* url("../assets/client-logos/logo-HP-black.svg"); - let logoIndeed = yield* url("../assets/client-logos/logo-indeed.svg"); - let logoResideo = yield* url("../assets/client-logos/logo-resideo.svg"); - let logoEricsson = yield* url("../assets/client-logos/logo-ericsson.svg"); - let headerImage = yield* url("../assets/pluginWorkshopHeaderImagev2.png"); - let benefitsImage1 = yield* url("../assets/benefitsImage1.svg"); - let benefitsImage2 = yield* url("../assets/benefitsImage2.svg"); - let benefitsImage3 = yield* url("../assets/benefitsImage3.svg"); - - return ( -
-
-
- -

- Discover the{" "} - Power of Backstage{" "} - Plugins! -

-

- Learn from a hands-on workshop derived from our experience with - large enterprises. This isn't theory—it's actionable expertise. Join - a workshop loaded with best practices, insights, and advanced - engineering. -

- - Signup for Waitlist Today - - -
- Image of Connected Nodes and Plugins -
-
-
-
-
- HP Logo -
-
- Ericsson Logo -
-
- Resideo Logo -
-
- Indeed Logo -
-
- Humanitec Logo -
-
-
-
-
-
-

- Everything{" "} - in Backstage is a Plugin! -

-

- The architecture of Backstage is ingeniously designed around - plugins. Everything from the Service Catalog, Templates, Scaffolder, - Search and Tech Docs are all plugins! This workshop will not only - equip you with the skills to create and manage plugins - but start - to build an understanding of a core component of grand architecture - underpinning Backstage. Dive into our workshop, and empower yourself - to shape and command Backstage Plugins! -

-
-
-
-

- Mastering Plugins:
{" "} - Improve Your Internal Developer Portal Without Limitations -

-

- To fully harness and customize Backstage tailored to your company's - unique needs, a deep understanding of plugin development is - indispensable. Without mastering plugins, you're confined to a limited - realm of possibilities in Backstage. Break through these constraints - and unlock a world of expansive features. Embrace the power of plugins - and propel your capabilities to new horizons. Say yes to boundless - innovation! -

-
-
-
-

- Confidence to Innovate -

-

- Grasping the intricacies of plugins doesn't just give you a - technical advantage—it fuels your creativity. With this newfound - knowledge, you'll confidently design and implement complex features - and functionalities, turning your innovative ideas into powerful - features. -

-
- -
-
-
-

- Deeper understanding of Backstage Architecture -

-

- Go beyond mere plugin creation, understand plugins and decode the - core of Backstage's architecture. These are not just plugins; - they're the fundamental building blocks that shape Backstage. As you - master plugins, watch your understanding of the entire ecosystem - come alive. -

-
- -
-
-
-

- Maximize ROI, Engagement, & Adoption -

-

- Mastering plugin development isn't just about technical prowess—it's - an investment that brings exponential returns. By customizing - Backstage to your unique needs, systems and users, you open doors to - unparalleled solutions. Don't just adapt; lead. Witness your - investments bear fruit as you craft amazing solutions and - experiences for your users, and organizations. -

-
- -
-
-

Who is this workshop for?

- -

- Experienced Backstage Developer:{" "} - If you've already dabbled in Backstage and built a foundational - understanding, but feel there's more to explore—especially in plugin - development—this workshop is your next step. We're here to refine your - skills and elevate your mastery. -

-

- Developers Seeking Direction:{" "} - Perhaps you haven't ventured into creating a Backstage plugin yet, If - the term 'plugin' sounds daunting or if you’re unsure where to begin, - fret not! We’ve tailored segments of our workshop especially for you. - Think of this as a guided tour, ensuring you never feel lost, and - always feel empowered to ask, learn, and grow. -

-

- Teams with Aspirations:{" "} - For teams eager to amplify their collective proficiency, this workshop - is a goldmine. Equip your developer suite with the confidence and - competence to craft sophisticated plugins, fostering a harmonized and - empowered team environment. -

-

- Everyone Eager to Learn:{" "} - Whether you’re a seasoned developer, a newbie, or someone in between, - if you have the zeal to learn, we have the insights to share. Join us, - and let’s embark on this enlightening journey together. -

-
-
-
-

What you will build

-
-

- Pipeline Monitor Plugin -

-

- In this workshop, we’ll build a Pipeline Monitor plugin to pull - information from external services and aggregate that information - in a database. The plugin will allow your users to customize it - via YAML. The functionality of the YAML syntax will be - configurable with custom functions in the backend. We’ll create a - backend plugin engine that will use configuration from the YAML - file to execute pipeline monitoring by polling each state of the - pipeline, retrieving the status, and storing the status in the - database. We’ll implement a mechanism for generating and storing - information summaries in the database. The raw data about the - pipeline and the status will be accessible via the backend REST - API. We’ll write a front-end plugin that will show information - from the database. -

-
-
-
-
-
-
- - - -
-
-

Design

-

- Dive into custom frontend components tailored for Backstage's - APIs. Learn the boundaries between React and Backstage APIs, - ensuring components are efficient and interactive. Understand the - vital link between frontend design and the plugin backend. -

-
-
-
-
- - - -
-
-

Build

-

- Navigate the creation of a robust backend. Design and deploy a - specialized database for your plugin, ensuring optimal data flow - between frontend and backend. Master the intricacies of seamless - data storage and retrieval. -

-
-
-
-
- - - -
-
-

Test

-

- Embrace the importance of rigorous plugin testing. Uncover best - practices to identify and rectify vulnerabilities, ensuring your - plugin meets the highest quality benchmarks. -

-
-
-
-
- - - - - - - -
-
-

Ship

-

- Learn to bundle, build, and ship your plugin. Ensure your creation - is packaged for easy integration, sharing your innovation - internally or with the broader Backstage community. -

-
-
-
-
-
-

What you will learn

-
-

Frontend

-
    -
  • Create frontend components that work as a page or a card.
  • -
  • Create an API client for your backend API
  • -
  • Make the API client available via an API Ref.
  • -
  • Use your custom API ref in your components.
  • -
  • Configure your API ref using Backstage API configuration.
  • -
  • Customize the frontend components of a new custom kind.
  • -
-
-
-

Catalog

-
    -
  • Define a new custom kind
  • -
  • - Create a new processor that will validate your kind using Zod -
  • -
  • Emit custom relationships using processors
  • -
-
-
-

Backend

-
    -
  • - Create a new backend plugin using a new backend extension - system. -
  • -
  • Create a REST API backend using OpenAPI specification.
  • -
  • - Generate types from OpenAPI specification. -
  • -
  • Store and Share types between frontend and backend.
  • -
  • Write database migrations to create a database.
  • -
  • - Write database migrations to change the database structure. -
  • -
  • Connect the database to the REST API
  • -
  • - Write a backend engine that will execute pipeline monitoring. -
  • -
-
-
-
-

Workshop Instructors

-
-
-
- - - - -
-
-

- Official Backstage Professional Services Partners -

-

- Frontside is recognized as a Professional Services Partner with - Backstage and have helped companies of all sizes develop a wide - range of solutions leveraging Plugins. -

-
-
-
-
- - - -
-
-

- Enterprise Level Backstage Experience -

-

- Frontside has worked with some of the largest Enterprise companies - with extremely complex, data models, and requirements for their - plugins and overall system of plugins. We build plugins that can - can to Enterprise. -

-
-
-
-
- - - -
-
-

- Early-Adopters, Highly Experienced Engineers -

-

- Frontside has been using Backstage before the 1.0-release. Our - engineers have a wealth of experience in Backstage and Backstage - plugins, and their underlying technologies such as React and - Typescript. -

-
-
-
-
- - - -
-
-

- Pioneering the Backstage Ecosystem -

-

- Frontside both independently and in collaboration with our trusted - partners has been at the forefront of Backstage innovation. -

-
-
-
-
-
- - - -
-
-

- Core Contributors -

-

- Frontside has been diligently studying the core mechanics, and - where applicable have contribution code to both the core and by - creating plugins to enhance core and overall functionality. -

-
-
-
-
-
- - - - - - - - - - - - - - - -
-
-

- Engaged and Invested Community Members -

-

- Proactive members of various Special Interest Groups, actively - engage on the community Discord, and are regular attendees of - Community and Contributor Backstage Sessions. Frontside’s - commitment isn't just superficial; they are genuinely invested in - uplifting and supporting fellow community members. -

-
-
-
-
-

Trusted By

-
-
-
- Ericsson Logo -
-
- HP Logo -
-
- Resideo Logo -
-
- Indeed Logo -
-
- Humanitec Logo -
-
-
-
-
-

What our clients say

-
-
-
- - - - -
-
-

- "I owe so much to [Frontside]...who were like professors to me — - and [ when needed ] told me 'no'. And they needed to tell me - 'no' [sometimes]. Thank so you very much, guys, for that.” -

-
- - Guilherme G., Senior Software Engineer - -
-
-
- - - - -
-
-

- "Huge, thank you, huge...for investing in me and helping me move - forward with my knowledge of the latest technologies. It was a - daunting task for me to learn this tech stack, and you guys - [invested] a lot in me. I really appreciate that. I really - [have] been able to move forward and become confident in this - technology set because of your guys' [investment] in me!" -

-
- - North K., Software Engineer - -
-
-
- - - - -
-
-

- "The Frontside developers have an incredibly high level of - technical expertise. They've built complex plug-ins from - scratch...I consider them educators as much as individual - contributors, because they can communicate so clearly about - their ideas, and they have deep skills to share." -

-
- - Benji S., Staff Software Engineer - -
-
-
-
-

- Join us to elevate your Backstage expertise and transform into a - proficient plugin developer. Discover, learn, and kick-start your path - to master the art of crafting sophisticated plugins for Backstage in - this comprehensive, hands-on workshop. -

- - Signup for the Waitlist Today - -
-
- ); -} - -function H2( - { children, class: classes = " " }: { - children: string | JSX.Element | (string | JSX.Element)[]; - class?: string; - }, -): JSX.Element { - return ( -

- {children} -

- ); -} diff --git a/html/app.html.tsx b/html/app.html.tsx deleted file mode 100644 index 1452672a..00000000 --- a/html/app.html.tsx +++ /dev/null @@ -1,146 +0,0 @@ -import type { Operation } from "effection"; -import { outlet, url } from "freejack/view.ts"; - -import { ProjectSelect } from "../components/project-select.tsx"; - -import { PAGE_SENSE_SCRIPT_SRC } from "./env.ts"; - -export interface Options { - title: string; - description: string; - ogImage?: string; - twitterXImage?: string; - author?: string; -} - -export default function* AppHtml(options: Options): Operation { - let { title, description, ogImage, twitterXImage, author } = options; - let siteURL = yield* url(); - let ogImageMeta = yield* url(ogImage); - let twitterXImageMeta = yield* url(twitterXImage); - let logoNoText = yield* url("assets/fs-logo-no-text.svg"); - let logoURL = yield* url("assets/fs-logo.svg"); - - let PageSenseScriptTag = PAGE_SENSE_SCRIPT_SRC - ? - : <>; - - return ( - - - - {title} - - - - - - - - - - - - - - - - - {PageSenseScriptTag} - - -
- -
-
- {yield* outlet} -
- - - - ); -} diff --git a/html/env.ts b/html/env.ts deleted file mode 100644 index f4216ad9..00000000 --- a/html/env.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const PAGE_SENSE_SCRIPT_SRC = getEnv("PAGE_SENSE_SCRIPT_SRC"); - -interface Env { - PAGE_SENSE_SCRIPT_SRC: string; -} - -function getEnv(name: keyof Env): string | undefined { - return Deno.env.get(name); -} diff --git a/html/index.html.tsx b/html/index.html.tsx deleted file mode 100644 index 05255f87..00000000 --- a/html/index.html.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import type { Operation } from "effection"; - -export default function* IndexHTML(): Operation { - return ( -
-
-

Developer Index

-

- This is just a place holder for the `/` path. However, You won't see - this page anywhere except development because this root url is - currently inhabited by the Gatsby-based website. -

-

- Have a look at these sub-sites which are visible: -

-

-
-
- ); -} diff --git a/lib/deps.ts b/lib/deps.ts deleted file mode 100644 index 8c5db026..00000000 --- a/lib/deps.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { - match, - type MatchFunction, - type MatchResult, -} from "https://deno.land/x/path_to_regexp@v6.2.1/index.ts"; diff --git a/lib/html.ts b/lib/html.ts deleted file mode 100644 index 46626b4b..00000000 --- a/lib/html.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { HandlerOptions, ServeHandler } from "./types.ts"; -import type { Operation } from "effection"; - -import { toHtml } from "https://esm.sh/hast-util-to-html@8.0.4"; -import { assert } from "https://deno.land/std@0.148.0/_util/assert.ts"; - -import { twind } from "freejack/twind.ts"; -import { URLContext } from "freejack/view.ts"; - -export interface HtmlHandler { - (options: HandlerOptions): Operation; -} - -export const html = { - get(handler: HtmlHandler): ServeHandler { - return function* ({ request, params }) { - if (request.method.toUpperCase() !== "GET") { - return new Response("Not Found", { - status: 404, - statusText: "Not Found", - }); - } - - yield* URLContext.set(new URL(request.url)); - - let top = yield* handler({ params, request }); - - assert(top.type === "element"); - twind(top); - - let text = `${toHtml(top)}`; - - return new Response(text, { - status: 200, - statusText: "OK", - headers: { - "Content-Type": "text/html", - }, - }); - }; - }, -}; diff --git a/lib/route-recognizer.ts b/lib/route-recognizer.ts deleted file mode 100644 index 10b481e0..00000000 --- a/lib/route-recognizer.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { default as _RouteRecognizer } from "https://esm.sh/route-recognizer@0.3.4"; - -export interface RouteRecognizer { - add(segments: Pattern[]): void; - recognize(uri: string): Segment[]; -} - -export interface Pattern { - path: string; - handler: unknown; -} - -export interface Segment { - handler: unknown; - params: Record; -} - -export function createRouteRecognizer(): RouteRecognizer { - //@ts-expect-error this library is completely untyped; - return new _RouteRecognizer(); -} diff --git a/lib/router.ts b/lib/router.ts deleted file mode 100644 index a088c7de..00000000 --- a/lib/router.ts +++ /dev/null @@ -1 +0,0 @@ -export const main = Symbol.for("main"); diff --git a/lib/server.ts b/lib/server.ts deleted file mode 100644 index efaadae3..00000000 --- a/lib/server.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { - type Handler, - serve as $serve, -} from "https://deno.land/std@0.188.0/http/server.ts"; -import { serveDir } from "https://deno.land/std@0.190.0/http/file_server.ts"; -import { action, expect, type Operation, resource, useScope } from "effection"; -import { match, type MatchFunction } from "./deps.ts"; -import type { ServeHandler } from "./types.ts"; - -export interface FreejackServerOptions { - serve(): Operation; - port: number; - dir: string; -} - -export interface FreejackServer { - hostname: string; - port: number; -} - -export function useServer( - options: FreejackServerOptions, -): Operation { - return resource(function* (provide) { - let requestHandler = yield* options.serve(); - - let handler: Handler = async (request, info) => { - let pathname = new URL(request.url).pathname; - if (pathname.startsWith("/assets")) { - return serveDir(request, { fsRoot: options.dir }); - } else { - return await requestHandler(request, info); - } - }; - - let controller = new AbortController(); - let { signal } = controller; - - let [done, server] = yield* action<[Promise, FreejackServer]>( - function* (resolve) { - let promise = Promise.resolve(); - promise = $serve(handler, { - port: options.port, - signal, - onListen(s) { - resolve([promise, s]); - }, - }); - }, - ); - - try { - yield* provide(server); - } finally { - controller.abort(); - yield* expect(done); - } - }); -} - -export function serve(group: Record): Operation { - return resource(function* (provide) { - let scope = yield* useScope(); - - let matchers: [MatchFunction, ServeHandler, string][] = []; - - for (let [path, handler] of Object.entries(group)) { - matchers.push([match(path), handler, path]); - } - - yield* provide(function Handler(request) { - return scope.run(function* () { - try { - let url = new URL(request.url); - - for (let [matcher, handler] of matchers) { - let match = matcher(url.pathname); - if (match) { - let { params } = match; - return yield* handler({ params, request }); - } - } - } catch (error) { - return new Response(error.stack, { - status: 500, - statusText: "Internal Error", - }); - } - return new Response("Not Found", { - status: 404, - statusText: "Not Found", - }); - }); - }); - }); -} - -type Next = { - type: "continue"; -} | { - type: "response"; - response: Response; -}; - -import { createContext } from "effection"; -const NextContext = createContext<(next: Next) => void>("next"); - -export function next(): Operation { - return { - *[Symbol.iterator]() { - let escape = yield* NextContext; - escape({ type: "continue" }); - }, - }; -} diff --git a/lib/types.ts b/lib/types.ts deleted file mode 100644 index 45312136..00000000 --- a/lib/types.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { Operation } from "effection"; -import type { MatchResult } from "./deps.ts"; - -export type Params

= MatchResult

["params"]; - -export interface HandlerOptions

{ - params: Params

; - request: Request; -} - -export interface ServeHandler { - (options: HandlerOptions): Operation; -} diff --git a/lib/view.ts b/lib/view.ts deleted file mode 100644 index 0f543c39..00000000 --- a/lib/view.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { createContext, type Operation } from "effection"; - -export const URLContext = createContext("url"); - -export function* useURL(): Operation { - return yield* URLContext; -} - -const Outlet = createContext("outlet"); - -export const outlet: Operation = { - *[Symbol.iterator]() { - return yield* Outlet; - }, -}; - -export function* url(path?: string): Operation { - let base = yield* URLContext; - let pathname = path ?? base.pathname; - return new URL(pathname, base.origin).toString(); -} - -export function* render( - ...templates: Operation[] -): Operation { - // won't be necessary when we migrate to HAST - let content = null as unknown as JSX.Element; - for (let template of templates.reverse()) { - yield* Outlet.set(content); - content = yield* template; - } - return content; -} diff --git a/lib/watch.ts b/lib/watch.ts index 6a030193..6a6a7dcb 100644 --- a/lib/watch.ts +++ b/lib/watch.ts @@ -39,7 +39,7 @@ await run(function* () { ignores.some((ignore) => path.includes(ignore)) ); }), - ); + ).subscribe(); yield* useCommand(cmd, { args }); @@ -59,13 +59,17 @@ await run(function* () { }); function useFsWatch(paths: string | string[]): Stream { - return resource(function* (provide) { - let watcher = Deno.watchFs(paths); - try { - let subscription = yield* stream(watcher); - yield* provide(subscription as Subscription); - } finally { - watcher.close(); - } - }); + return { + subscribe() { + return resource(function* (provide) { + let watcher = Deno.watchFs(paths); + try { + let subscription = yield* stream(watcher).subscribe(); + yield* provide(subscription as Subscription); + } finally { + watcher.close(); + } + }); + }, + }; } diff --git a/main.ts b/main.ts index 1675d65c..56e1a959 100644 --- a/main.ts +++ b/main.ts @@ -1,16 +1 @@ -import { main, suspend } from "effection"; - -import { useServer } from "freejack/server.ts"; -import serve from "./sitemap/sitemap.ts"; - -await main(function* () { - let server = yield* useServer({ - serve, - port: 8000, - dir: new URL(".", import.meta.url).pathname, - }); - - console.log(`www -> http://${server.hostname}:${server.port}`); - - yield* suspend(); -}); +import "./main.tsx"; diff --git a/main.tsx b/main.tsx new file mode 100644 index 00000000..e0ac4150 --- /dev/null +++ b/main.tsx @@ -0,0 +1,62 @@ +import { main, suspend } from "effection"; + +import { createRevolution, route, serveDirMiddleware } from "revolution"; + +import { proxyRoute } from "./routes/proxy-route.ts"; +import { pluginWorkshopRoute } from "./routes/advanced-backstage-plugin-development-route.tsx"; + +import { etagPlugin } from "./plugins/etag.ts"; +import { currentRequestPlugin } from "./plugins/current-request.ts"; +import { twindPlugin } from "./plugins/twind.ts"; +import { config } from "./twind.config.ts"; + +await main(function* () { + let proxies = proxySites(); + + let revolution = createRevolution({ + app: [ + route( + "/workshops/advanced-backstage-plugin-development", + pluginWorkshopRoute(), + ), + route("/effection(.*)", proxyRoute(proxies.effection)), + route("/graphgen(.*)", proxyRoute(proxies.graphgen)), + route( + "/assets(.*)", + serveDirMiddleware({ + fsRoot: new URL(import.meta.resolve("./assets")).pathname, + urlRoot: "assets", + }), + ), + proxyRoute(proxies.legacy), + ], + + plugins: [ + etagPlugin(), + currentRequestPlugin(), + twindPlugin({ config }), + ], + }); + + let server = yield* revolution.start(); + console.log(`www -> http://${server.hostname}:${server.port}`); + + yield* suspend(); +}); + +function proxySites() { + return { + effection: { + prefix: "effection", + website: Deno.env.get("EFFECTION_URL") ?? "https://effection.deno.dev", + }, + graphgen: { + prefix: "graphgen", + website: Deno.env.get("GRAPHGEN_URL") ?? "https://graphgen.deno.dev", + }, + legacy: { + prefix: "", + website: Deno.env.get("FS_LEGACY_URL") ?? "https://frontside.netlify.app", + }, + } as const; +} diff --git a/plugins/current-request.ts b/plugins/current-request.ts new file mode 100644 index 00000000..158d7baa --- /dev/null +++ b/plugins/current-request.ts @@ -0,0 +1,37 @@ +import type { Operation } from "effection"; +import type { RevolutionPlugin } from "revolution"; + +import { createContext } from "effection"; +import { posixNormalize } from "https://deno.land/std@0.203.0/path/_normalize.ts"; + +const CurrentRequest = createContext("Request"); + +export function currentRequestPlugin(): RevolutionPlugin { + return { + *http(request, next) { + yield* CurrentRequest.set(request); + return yield* next(request); + }, + }; +} + +export function* useCurrentRequest() { + return yield* CurrentRequest; +} + +/** + * Convert a non fully qualified url into a fully qualified url, complete + * with protocol. + */ +export function* useAbsoluteUrl(path: string): Operation { + let normalizedPath = posixNormalize(path); + let request = yield* useCurrentRequest(); + + if (normalizedPath.startsWith("/")) { + let url = new URL(request.url); + url.pathname = normalizedPath; + return url.toString(); + } else { + return new URL(normalizedPath, request.url).toString(); + } +} diff --git a/plugins/etag.ts b/plugins/etag.ts new file mode 100644 index 00000000..a3c4578f --- /dev/null +++ b/plugins/etag.ts @@ -0,0 +1,40 @@ +import { RevolutionPlugin } from "revolution"; +import { encodeBase64 } from "https://deno.land/std@0.206.0/encoding/base64.ts"; + +const DEPLOYMENT_ID = + // The same deployment will be shared by the many isolates that serve it + // but because pages do not change, we can use this id as the ETAG + Deno.env.get("DENO_DEPLOYMENT_ID") || + // For local development, just create a new id every time the module is + // reloaded i.e. whenever the dev server restarts. + crypto.randomUUID(); + +const DEPLOYMENT_ID_HASH = await crypto.subtle.digest( + "SHA-1", + new TextEncoder().encode(DEPLOYMENT_ID), +); + +const ETAG = `"${encodeBase64(DEPLOYMENT_ID_HASH)}"`; +const WEAK_ETAG = `W/"${encodeBase64(DEPLOYMENT_ID_HASH)}"`; + +export function etagPlugin(): RevolutionPlugin { + return { + *http(request, next) { + let ifNoneMatch = request.headers.get("if-none-match"); + if (ifNoneMatch === ETAG || ifNoneMatch === WEAK_ETAG) { + return new Response(null, { + status: 304, + statusText: "Not Modified", + }); + } else { + let response = yield* next(request); + if (!response.headers.get("etag")) { + let tagged = new Response(response.body, response); + tagged.headers.set("etag", ETAG); + return tagged; + } + return response; + } + }, + }; +} diff --git a/plugins/twind.ts b/plugins/twind.ts new file mode 100644 index 00000000..04641f13 --- /dev/null +++ b/plugins/twind.ts @@ -0,0 +1,54 @@ +import type { HASTElement, RevolutionPlugin } from "revolution"; +import { + setup, + stringify, + tw, + type TwindConfig, + virtual, +} from "npm:@twind/core@1.1.3"; + +export interface TwindRevolution { + config: TwindConfig; +} + +export function twindPlugin(options: TwindRevolution): RevolutionPlugin { + let { config } = options; + + return { + *html(request, next) { + let html = yield* next(request); + let sheet = virtual(); + + setup(config, sheet); + + visit(html); + + let css = stringify(sheet.target); + + let head = html.children.find((child) => + child.type === "element" && child.tagName === "head" + ) as HASTElement | undefined; + + head?.children.push({ + type: "element", + tagName: "style", + properties: { type: "text/css" }, + children: [{ type: "text", value: css }], + }); + + return html; + }, + }; +} + +function visit(element: HASTElement): void { + let { properties: { className: classnames } = {}, children } = element; + if (classnames) { + tw(String(classnames)); + } + for (let child of children) { + if (!!child && child.type === "element") { + visit(child); + } + } +} diff --git a/routes/advanced-backstage-plugin-development-route.tsx b/routes/advanced-backstage-plugin-development-route.tsx new file mode 100644 index 00000000..b88e5e5a --- /dev/null +++ b/routes/advanced-backstage-plugin-development-route.tsx @@ -0,0 +1,822 @@ +import type { Operation } from "effection"; +import type { JSXHandler } from "revolution"; + +import { useAppHtml } from "./app.html.tsx"; + +export function pluginWorkshopRoute(): JSXHandler { + return function* () { + let logoHumanitec = "../assets/client-logos/logo-humanitec.svg"; + let logoHP = "../assets/client-logos/logo-HP-black.svg"; + let logoIndeed = "../assets/client-logos/logo-indeed.svg"; + let logoResideo = "../assets/client-logos/logo-resideo.svg"; + let logoEricsson = "../assets/client-logos/logo-ericsson.svg"; + let headerImage = "../assets/pluginWorkshopHeaderImagev2.png"; + let benefitsImage1 = "../assets/benefitsImage1.svg"; + let benefitsImage2 = "../assets/benefitsImage2.svg"; + let benefitsImage3 = "../assets/benefitsImage3.svg"; + + let AppHtml = yield* useAppHtml({ + title: "Frontside: Advanced Backstage Plugin Development", + description: + "Harness Backstage's Potential: Become an Advanced Plugin Developer", + ogImage: "/assets/pluginWorkshopHeaderImagev2.png", + twitterXImage: "/assets/pluginWorkshopHeaderImagev2.png", + author: "Frontside", + }); + + return ( + +

+
+
+ +

+ Discover the{" "} + Power of Backstage{" "} + Plugins! +

+

+ Learn from a hands-on workshop derived from our experience with + large enterprises. This isn't theory—it's actionable expertise. + Join a workshop loaded with best practices, insights, and + advanced engineering. +

+ + Signup for Waitlist Today + + +
+ Image of Connected Nodes and Plugins +
+
+
+
+
+ HP Logo +
+
+ Ericsson Logo +
+
+ Resideo Logo +
+
+ Indeed Logo +
+
+ Humanitec Logo +
+
+
+
+
+
+

+ Everything{" "} + in Backstage is a Plugin! +

+

+ The architecture of Backstage is ingeniously designed around + plugins. Everything from the Service Catalog, Templates, + Scaffolder, Search and Tech Docs are all plugins! This workshop + will not only equip you with the skills to create and manage + plugins - but start to build an understanding of a core + component of grand architecture underpinning Backstage. Dive + into our workshop, and empower yourself to shape and command + Backstage Plugins! +

+
+
+
+

+ Mastering Plugins:
{" "} + Improve Your Internal Developer Portal Without Limitations +

+

+ To fully harness and customize Backstage tailored to your + company's unique needs, a deep understanding of plugin development + is indispensable. Without mastering plugins, you're confined to a + limited realm of possibilities in Backstage. Break through these + constraints and unlock a world of expansive features. Embrace the + power of plugins and propel your capabilities to new horizons. Say + yes to boundless innovation! +

+
+
+
+

+ Confidence to Innovate +

+

+ Grasping the intricacies of plugins doesn't just give you a + technical advantage—it fuels your creativity. With this newfound + knowledge, you'll confidently design and implement complex + features and functionalities, turning your innovative ideas into + powerful features. +

+
+ +
+
+
+

+ Deeper understanding of Backstage Architecture +

+

+ Go beyond mere plugin creation, understand plugins and decode + the core of Backstage's architecture. These are not just + plugins; they're the fundamental building blocks that shape + Backstage. As you master plugins, watch your understanding of + the entire ecosystem come alive. +

+
+ +
+
+
+

+ Maximize ROI, Engagement, & Adoption +

+

+ Mastering plugin development isn't just about technical + prowess—it's an investment that brings exponential returns. By + customizing Backstage to your unique needs, systems and users, + you open doors to unparalleled solutions. Don't just adapt; + lead. Witness your investments bear fruit as you craft amazing + solutions and experiences for your users, and organizations. +

+
+ +
+
+

Who is this workshop for?

+ +

+ Experienced Backstage Developer:{" "} + If you've already dabbled in Backstage and built a foundational + understanding, but feel there's more to explore—especially in + plugin development—this workshop is your next step. We're here to + refine your skills and elevate your mastery. +

+

+ Developers Seeking Direction:{" "} + Perhaps you haven't ventured into creating a Backstage plugin yet, + If the term 'plugin' sounds daunting or if you’re unsure where to + begin, fret not! We’ve tailored segments of our workshop + especially for you. Think of this as a guided tour, ensuring you + never feel lost, and always feel empowered to ask, learn, and + grow. +

+

+ Teams with Aspirations:{" "} + For teams eager to amplify their collective proficiency, this + workshop is a goldmine. Equip your developer suite with the + confidence and competence to craft sophisticated plugins, + fostering a harmonized and empowered team environment. +

+

+ Everyone Eager to Learn:{" "} + Whether you’re a seasoned developer, a newbie, or someone in + between, if you have the zeal to learn, we have the insights to + share. Join us, and let’s embark on this enlightening journey + together. +

+
+
+
+

What you will build

+
+

+ Pipeline Monitor Plugin +

+

+ In this workshop, we’ll build a Pipeline Monitor plugin to + pull information from external services and aggregate that + information in a database. The plugin will allow your users to + customize it via YAML. The functionality of the YAML syntax + will be configurable with custom functions in the backend. + We’ll create a backend plugin engine that will use + configuration from the YAML file to execute pipeline + monitoring by polling each state of the pipeline, retrieving + the status, and storing the status in the database. We’ll + implement a mechanism for generating and storing information + summaries in the database. The raw data about the pipeline and + the status will be accessible via the backend REST API. We’ll + write a front-end plugin that will show information from the + database. +

+
+
+
+
+
+
+ + + +
+
+

Design

+

+ Dive into custom frontend components tailored for Backstage's + APIs. Learn the boundaries between React and Backstage APIs, + ensuring components are efficient and interactive. Understand + the vital link between frontend design and the plugin backend. +

+
+
+
+
+ + + +
+
+

Build

+

+ Navigate the creation of a robust backend. Design and deploy a + specialized database for your plugin, ensuring optimal data + flow between frontend and backend. Master the intricacies of + seamless data storage and retrieval. +

+
+
+
+
+ + + +
+
+

Test

+

+ Embrace the importance of rigorous plugin testing. Uncover + best practices to identify and rectify vulnerabilities, + ensuring your plugin meets the highest quality benchmarks. +

+
+
+
+
+ + + + + + + +
+
+

Ship

+

+ Learn to bundle, build, and ship your plugin. Ensure your + creation is packaged for easy integration, sharing your + innovation internally or with the broader Backstage community. +

+
+
+
+
+
+

What you will learn

+
+

Frontend

+
    +
  • + Create frontend components that work as a page or a card. +
  • +
  • Create an API client for your backend API
  • +
  • Make the API client available via an API Ref.
  • +
  • Use your custom API ref in your components.
  • +
  • + Configure your API ref using Backstage API configuration. +
  • +
  • + Customize the frontend components of a new custom kind. +
  • +
+
+
+

Catalog

+
    +
  • Define a new custom kind
  • +
  • + Create a new processor that will validate your kind using + Zod +
  • +
  • Emit custom relationships using processors
  • +
+
+
+

Backend

+
    +
  • + Create a new backend plugin using a new backend extension + system. +
  • +
  • + Create a REST API backend using OpenAPI specification. +
  • +
  • + Generate types from OpenAPI specification. +
  • +
  • Store and Share types between frontend and backend.
  • +
  • Write database migrations to create a database.
  • +
  • + Write database migrations to change the database structure. +
  • +
  • Connect the database to the REST API
  • +
  • + Write a backend engine that will execute pipeline + monitoring. +
  • +
+
+
+
+

Workshop Instructors

+
+
+
+ + + + +
+
+

+ Official Backstage Professional Services Partners +

+

+ Frontside is recognized as a Professional Services Partner + with Backstage and have helped companies of all sizes develop + a wide range of solutions leveraging Plugins. +

+
+
+
+
+ + + +
+
+

+ Enterprise Level Backstage Experience +

+

+ Frontside has worked with some of the largest Enterprise + companies with extremely complex, data models, and + requirements for their plugins and overall system of plugins. + We build plugins that can can to Enterprise. +

+
+
+
+
+ + + +
+
+

+ Early-Adopters, Highly Experienced Engineers +

+

+ Frontside has been using Backstage before the 1.0-release. Our + engineers have a wealth of experience in Backstage and + Backstage plugins, and their underlying technologies such as + React and Typescript. +

+
+
+
+
+ + + +
+
+

+ Pioneering the Backstage Ecosystem +

+

+ Frontside both independently and in collaboration with our + trusted partners has been at the forefront of Backstage + innovation. +

+
+
+
+
+
+ + + +
+
+

+ Core Contributors +

+

+ Frontside has been diligently studying the core mechanics, + and where applicable have contribution code to both the core + and by creating plugins to enhance core and overall + functionality. +

+
+
+
+
+
+ + + + + + + + + + + + + + + +
+
+

+ Engaged and Invested Community Members +

+

+ Proactive members of various Special Interest Groups, actively + engage on the community Discord, and are regular attendees of + Community and Contributor Backstage Sessions. Frontside’s + commitment isn't just superficial; they are genuinely invested + in uplifting and supporting fellow community members. +

+
+
+
+
+

Trusted By

+
+
+
+ Ericsson Logo +
+
+ HP Logo +
+
+ Resideo Logo +
+
+ Indeed Logo +
+
+ Humanitec Logo +
+
+
+
+
+

What our clients say

+
+
+
+ + + + +
+
+

+ "I owe so much to [Frontside]...who were like professors to + me — and [ when needed ] told me 'no'. And they needed to + tell me 'no' [sometimes]. Thank so you very much, guys, for + that.” +

+
+ + Guilherme G., Senior Software Engineer + +
+
+
+ + + + +
+
+

+ "Huge, thank you, huge...for investing in me and helping me + move forward with my knowledge of the latest technologies. + It was a daunting task for me to learn this tech stack, and + you guys [invested] a lot in me. I really appreciate that. I + really [have] been able to move forward and become confident + in this technology set because of your guys' [investment] in + me!" +

+
+ + North K., Software Engineer + +
+
+
+ + + + +
+
+

+ "The Frontside developers have an incredibly high level of + technical expertise. They've built complex plug-ins from + scratch...I consider them educators as much as individual + contributors, because they can communicate so clearly about + their ideas, and they have deep skills to share." +

+
+ + Benji S., Staff Software Engineer + +
+
+
+
+

+ Join us to elevate your Backstage expertise and transform into a + proficient plugin developer. Discover, learn, and kick-start your + path to master the art of crafting sophisticated plugins for + Backstage in this comprehensive, hands-on workshop. +

+ + Signup for the Waitlist Today + +
+
+ + ); + }; +} +function H2( + { children, class: classes = " " }: { + children: string | JSX.Element | (string | JSX.Element)[]; + class?: string; + }, +): JSX.Element { + return ( +

+ {children} +

+ ); +} diff --git a/routes/app.html.tsx b/routes/app.html.tsx new file mode 100644 index 00000000..04c34ff6 --- /dev/null +++ b/routes/app.html.tsx @@ -0,0 +1,162 @@ +import type { Operation } from "effection"; +import type { JSXChild, JSXElement } from "revolution"; + +import { useAbsoluteUrl } from "../plugins/current-request.ts"; +import { ProjectSelect } from "../components/project-select.tsx"; + +const PAGE_SENSE_SCRIPT_SRC = getEnv("PAGE_SENSE_SCRIPT_SRC"); + +export interface Options { + title: string; + description: string; + ogImage: string; + twitterXImage: string; + author: string; +} + +export function* useAppHtml( + options: Options, +): Operation<({ children }: { children: JSXChild }) => JSXElement> { + let { title, description, author } = options; + let siteURL = yield* useAbsoluteUrl("/"); + let ogImageMeta = yield* useAbsoluteUrl(options.ogImage); + let twitterXImageMeta = yield* useAbsoluteUrl(options.twitterXImage); + let logoNoText = "/assets/fs-logo-no-text.svg"; + let logoURL = "/assets/fs-logo.svg"; + + return function AppHtml({ children }): JSX.Element { + let PageSenseScriptTag = PAGE_SENSE_SCRIPT_SRC + ? + : <>; + + return ( + + + + {title} + + + + + + + + + + + + + + + + + {PageSenseScriptTag} + + +
+ +
+
+ {children} +
+ + + + ); + }; +} + +interface Env { + PAGE_SENSE_SCRIPT_SRC: string; +} + +function getEnv(name: keyof Env): string | undefined { + return Deno.env.get(name); +} diff --git a/routes/proxy-route.ts b/routes/proxy-route.ts new file mode 100644 index 00000000..89fd23ca --- /dev/null +++ b/routes/proxy-route.ts @@ -0,0 +1,35 @@ +import type { HTTPMiddleware } from "revolution"; +import { call } from "effection"; + +export interface ProxyRouteOptions { + website: string; + prefix: string; +} + +export function proxyRoute(options: ProxyRouteOptions): HTTPMiddleware { + return function* proxy(request) { + let website = new URL(options.website); + let target = new URL(request.url); + let prefix = new RegExp(`^\/${options.prefix}\/?`); + target.pathname = target.pathname.replace(prefix, "/"); + target.hostname = website.hostname; + target.port = website.port; + target.protocol = website.protocol; + + let base = new URL(`/${options.prefix}`, request.url); + + let headers: Record = { + "X-Base-Url": base.toString(), + }; + for (let [key, value] of request.headers.entries()) { + headers[key] = value; + } + + let response = yield* call(fetch(target, { + redirect: "manual", + headers, + })); + + return response; + }; +} diff --git a/sitemap/proxy.ts b/sitemap/proxy.ts deleted file mode 100644 index c0ba1f89..00000000 --- a/sitemap/proxy.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { ServeHandler } from "../lib/types.ts"; -import { expect } from "effection"; - -export interface ProxyOptions { - website: string; - prefix: string; -} - -export function proxy(options: ProxyOptions): ServeHandler { - return function* handler({ request }) { - let website = new URL(options.website); - let target = new URL(request.url); - let prefix = new RegExp(`^\/${options.prefix}\/?`) - target.pathname = target.pathname.replace(prefix, "/"); - target.hostname = website.hostname; - target.port = website.port; - target.protocol = website.protocol; - - let base = new URL(`/${options.prefix}`, request.url); - - let response = yield* expect(fetch(target, { - redirect: "manual", - headers: { - ...request.headers, - "X-Base-Url": base.toString(), - } - })); - - return response; - } -} diff --git a/sitemap/sitemap.ts b/sitemap/sitemap.ts deleted file mode 100644 index 33ab4b9c..00000000 --- a/sitemap/sitemap.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { serve } from "freejack/server.ts"; -import { html } from "freejack/html.ts"; -import { render } from "freejack/view.ts"; - -import { GatsbyWebsite } from "../legacy/gatsby-website.ts"; - -import { AdvancedBackstagePluginDevelopmentHtml, AppHtml } from "../html.ts"; - -import { proxy } from "./proxy.ts"; - -export default () => - serve({ - "/workshops/advanced-backstage-plugin-development": html.get(() => - render( - AppHtml({ - title: "Frontside: Advanced Backstage Plugin Development", - description: - "Harness Backstage's Potential: Become an Advanced Plugin Developer", - ogImage: "/assets/pluginWorkshopHeaderImagev2.png", - twitterXImage: "/assets/pluginWorkshopHeaderImagev2.png", - author: "Frontside", - }), - AdvancedBackstagePluginDevelopmentHtml(), - ) - ), - "/effection(.*)": proxy({ - prefix: "effection", - website: "https://effection.deno.dev", - }), - "/graphgen(.*)": proxy({ - prefix: "graphgen", - website: "https://graphgen.deno.dev", - }), - "(.*)": GatsbyWebsite, - }); diff --git a/src/components/navbar/project-select.tsx b/src/components/navbar/project-select.tsx deleted file mode 100644 index 33495f37..00000000 --- a/src/components/navbar/project-select.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import React from "react"; -import { useSelect } from "downshift"; -import { - arrowDropdownButton, - projectDescription, - projectItem, - projectItemHighlighted, - projectItemText, - projectLink, - projectSelectLabel, - projectSelectWrap, - projectsList, - projectsListTitle, - projectTitle, - projectVersion, -} from "./navbar.css"; - -import iconInteractors from "../../img/icon-interactors.svg"; -import iconEffection from "../../img/icon-effection.svg"; -import iconBigtest from "../../img/icon-bigtest.svg"; -import iconSimulacrum from "../../img/icon-simulacrum.svg"; - -import DropdownArrow from "./dropdown-arrow"; - -const items = [ - { - title: "Interactors", - description: "Page Objects for components libraries", - url: "https://frontside.com/interactors", - version: "v1", - img: iconInteractors, - }, - { - title: "Effection", - description: "Structured Concurrency for JavaScript", - url: "https://frontside.com/effection", - version: "v3", - img: iconEffection, - }, - { - title: "Auth0 Simulator", - description: "Enabling testing and local development", - url: "https://github.com/thefrontside/simulacrum/tree/v0/packages/auth0", - version: "v0", - img: iconSimulacrum, - }, - { - title: "Bigtest", - description: "Universal test runner, GraphQL driven", - url: "https://frontside.com/bigtest", - version: "v0", - img: iconBigtest, - }, -]; - -function ProjectSelect() { - let { - isOpen, - getToggleButtonProps, - getLabelProps, - getMenuProps, - highlightedIndex, - getItemProps, - } = useSelect({ items, itemToString: (item) => item.title }); - return ( -
- - - -
- ); -} -export default ProjectSelect; diff --git a/lib/twind.ts b/twind.config.ts similarity index 73% rename from lib/twind.ts rename to twind.config.ts index 8140e5c2..ab61359a 100644 --- a/lib/twind.ts +++ b/twind.config.ts @@ -1,14 +1,17 @@ -import { - defineConfig, - setup, - stringify, - tw, - virtual, -} from "https://esm.sh/@twind/core@1.1.3"; +import { defineConfig } from "npm:@twind/core@1.1.3"; +import presetTailwind from "npm:@twind/preset-tailwind@1.1.4"; +import presetTypography from "npm:@twind/preset-typography@1.0.7"; -import type { Element } from "hastx/jsx-runtime"; -import presetTailwind from "https://esm.sh/@twind/preset-tailwind@1.1.4"; -import presetTypography from "https://esm.sh/@twind/preset-typography@1.0.7"; +export const config = defineConfig({ + //@ts-expect-error the tailwind preset types are wiggity whack. + presets: [presetTailwind(), presetTypography(), presetFrontside()], + theme: { + fontFamily: { + sans: ["Proxima Nova", "proxima-nova", "sans-serif"], + inter: ["Inter", "inter", "san-serif"], + }, + }, +}); function presetFrontside() { return { @@ -35,47 +38,3 @@ function presetFrontside() { }, }; } - -const config = defineConfig({ - //@ts-expect-error the tailwind preset types are wiggity whack. - presets: [presetTailwind(), presetTypography(), presetFrontside()], - theme: { - fontFamily: { - sans: ["Proxima Nova", "proxima-nova", "sans-serif"], - inter: ["Inter", "inter", "san-serif"], - }, - }, -}); - -export function twind(document: Element): void { - let sheet = virtual(); - - setup(config, sheet); - - visit(document); - - let css = stringify(sheet.target); - - let head = document.children.find((child) => - child.type === "element" && child.tagName === "head" - ) as Element; - - head?.children.push({ - type: "element", - tagName: "style", - properties: { type: "text/css" }, - children: [{ type: "text", value: css }], - }); -} - -function visit(element: Element): void { - let { properties: { className: classnames } = {}, children } = element; - if (classnames) { - tw(String(classnames)); - } - for (let child of children) { - if (!!child && child.type === "element") { - visit(child); - } - } -}