From f884cfdf66b50774f8da0ad2a7af2b3885a7b0df Mon Sep 17 00:00:00 2001 From: rohanharikr Date: Wed, 4 Dec 2024 14:17:27 +0000 Subject: [PATCH] Svelte 5 (#87) * remove scripts * init svelte 5 with tailwind * migrations * fix: dont try to send event if dev * update readme * svelte 5 * cleanup * header component * refactor * svelte 5 api * start of rewrite * update * updates * updates * fixes * comment * fixes * updates * updates * fix * updates * updates * updates * updates * reorganize * updates * updates * updates * updates * fixes * updates * fix * updates * format * updates * fixes * updates * updates * shiki * updates * fixes * comments * fix * updates * updates * remove fn * updates * fixes * fixes * updates * reorganize * use alias * format * fix * upgrade deps * fixes * fixes * updates * fix: transition duration * fixes * updates * updates * fix * fixes * fixes * fixes * fixes * updates * updates * fixes * updates * updates * updates * updates * fix * ui fix * updates * fixes * ui fixes * updates * updates * updates * ui updates * format app.css * updates * fix * updates * reorder * format * updates * updates * updates * bump version * fix * updates * add prettier * eslint * fixes * update redirect_uri * format * fixes * add test script --- .eslintignore | 7 - .eslintrc.cjs | 15 - .gitignore | 105 +- .npmrc | 2 - .prettierignore | 6 - .prettierrc | 17 +- README.md | 4 +- docs/.gitignore | 4 - eslint.config.js | 63 + index.html | 43 +- jsconfig.json | 19 +- package-lock.json | 8650 ++++++----------- package.json | 51 +- postcss.config.js | 2 +- public/CNAME | 1 - public/favicon.ico | Bin 1150 -> 0 bytes scripts/pr.sh | 34 - scripts/pre-requisties.sh | 14 - src/App.svelte | 2142 +--- src/{index.css => app.css} | 40 +- src/assets/svelte.png | Bin 5185 -> 0 bytes .../components/AuthorizationRequest.svelte | 74 + .../components/AuthorizationResponse.svelte | 41 + src/lib/components/ChevronY.svelte | 15 + src/lib/components/CopyButton.svelte | 41 + src/lib/components/CopyText.svelte | 16 + src/lib/components/FileIssue.svelte | 14 + src/lib/components/Header.svelte | 101 + src/lib/components/Icons/CloseIcon.svelte | 10 + .../components/Icons/ExternalLinkIcon.svelte | 14 + src/lib/components/Icons/MenuIcon.svelte | 14 + .../components/Inputs/DomainHintInput.svelte | 21 + .../Inputs/ProviderHintInput.svelte | 43 + src/lib/components/Notification.svelte | 24 + .../Request/AuthorizationServer.svelte | 71 + .../components/Request/HelloModeToggle.svelte | 32 + src/lib/components/Request/HelloParams.svelte | 83 + .../components/Request/InviteRequest.svelte | 51 + .../components/Request/ProtocolParams.svelte | 108 + src/lib/components/Request/RequestUrl.svelte | 58 + src/lib/components/Request/ScopeParam.svelte | 110 + .../components/Response/JsonResponse.svelte | 16 + .../components/Response/UrlResponse.svelte | 17 + src/lib/components/Tooltip.svelte | 20 + src/lib/constants.js | 176 + src/lib/request.js | 76 + src/lib/shiki.js | 30 + src/lib/utils.js | 89 + src/lib/validate.js | 80 + src/main.js | 5 +- src/utils/pkce.js | 39 - svelte.config.js | 7 + tailwind.config.js | 5 +- vite.config.js | 11 +- 54 files changed, 4999 insertions(+), 7732 deletions(-) delete mode 100644 .eslintignore delete mode 100644 .eslintrc.cjs delete mode 100644 .npmrc delete mode 100644 .prettierignore delete mode 100644 docs/.gitignore create mode 100644 eslint.config.js delete mode 100644 public/CNAME delete mode 100644 public/favicon.ico delete mode 100755 scripts/pr.sh delete mode 100755 scripts/pre-requisties.sh rename src/{index.css => app.css} (66%) delete mode 100644 src/assets/svelte.png create mode 100644 src/lib/components/AuthorizationRequest.svelte create mode 100644 src/lib/components/AuthorizationResponse.svelte create mode 100644 src/lib/components/ChevronY.svelte create mode 100644 src/lib/components/CopyButton.svelte create mode 100644 src/lib/components/CopyText.svelte create mode 100644 src/lib/components/FileIssue.svelte create mode 100644 src/lib/components/Header.svelte create mode 100644 src/lib/components/Icons/CloseIcon.svelte create mode 100644 src/lib/components/Icons/ExternalLinkIcon.svelte create mode 100644 src/lib/components/Icons/MenuIcon.svelte create mode 100644 src/lib/components/Inputs/DomainHintInput.svelte create mode 100644 src/lib/components/Inputs/ProviderHintInput.svelte create mode 100644 src/lib/components/Notification.svelte create mode 100644 src/lib/components/Request/AuthorizationServer.svelte create mode 100644 src/lib/components/Request/HelloModeToggle.svelte create mode 100644 src/lib/components/Request/HelloParams.svelte create mode 100644 src/lib/components/Request/InviteRequest.svelte create mode 100644 src/lib/components/Request/ProtocolParams.svelte create mode 100644 src/lib/components/Request/RequestUrl.svelte create mode 100644 src/lib/components/Request/ScopeParam.svelte create mode 100644 src/lib/components/Response/JsonResponse.svelte create mode 100644 src/lib/components/Response/UrlResponse.svelte create mode 100644 src/lib/components/Tooltip.svelte create mode 100644 src/lib/constants.js create mode 100644 src/lib/request.js create mode 100644 src/lib/shiki.js create mode 100644 src/lib/utils.js create mode 100644 src/lib/validate.js delete mode 100644 src/utils/pkce.js create mode 100644 svelte.config.js diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index f359359..0000000 --- a/.eslintignore +++ /dev/null @@ -1,7 +0,0 @@ -dist -node_modules -S3 -cypress -rumTracker.js -assets -playwright-report \ No newline at end of file diff --git a/.eslintrc.cjs b/.eslintrc.cjs deleted file mode 100644 index fab32bf..0000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = { - root: true, - extends: ['eslint:recommended', 'prettier'], - plugins: ['svelte3'], - overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }], - parserOptions: { - sourceType: 'module', - ecmaVersion: 2020 - }, - env: { - browser: true, - es2017: true, - node: true - } -}; diff --git a/.gitignore b/.gitignore index 07e491f..a547bf3 100644 --- a/.gitignore +++ b/.gitignore @@ -4,107 +4,16 @@ logs npm-debug.log* yarn-debug.log* yarn-error.log* +pnpm-debug.log* lerna-debug.log* -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env -.env.test - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# Next.js build output -.next - -# Nuxt.js build / generate output -.nuxt +node_modules dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and *not* Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - +dist-ssr *.local -.vscode + +# Editor directories and files +.vscode/* !.vscode/extensions.json .idea .DS_Store @@ -113,5 +22,3 @@ dist *.njsproj *.sln *.sw? - -S3 \ No newline at end of file diff --git a/.npmrc b/.npmrc deleted file mode 100644 index e84e4e8..0000000 --- a/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -# .npmrc -engine-strict=true \ No newline at end of file diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 344428e..0000000 --- a/.prettierignore +++ /dev/null @@ -1,6 +0,0 @@ -dist -node_modules -S3 -cypress -rumTracker.js -playwright-report \ No newline at end of file diff --git a/.prettierrc b/.prettierrc index ff2677e..4dd7e46 100644 --- a/.prettierrc +++ b/.prettierrc @@ -2,5 +2,20 @@ "useTabs": true, "singleQuote": true, "trailingComma": "none", - "printWidth": 100 + "printWidth": 100, + "overrides": [ + { + "files": "*.svelte", + "options": { + "parser": "svelte", + "plugins": ["prettier-plugin-svelte"] + } + }, + { + "files": "content/**/*.{css,html,js,svelte}", + "options": { + "requirePragma": true + } + } + ] } diff --git a/README.md b/README.md index 2fc7320..8eb4d4a 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,8 @@ This repo contains the source code powering [https://playground.hello.dev/](http ### Running docs locally 1. `npm run dev` to start the hot-reloading development server (powered by [Vite](https://vitejs.dev/)) -1. `open http://localhost:3000` to open the site in your favorite browser +1. `open http://localhost:5173` to open the site in your favorite browser ## License -[MIT](LICENSE) \ No newline at end of file +[MIT](LICENSE) diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index 86d0cb2..0000000 --- a/docs/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Ignore everything in this directory -* -# Except this file -!.gitignore \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..62483ef --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,63 @@ +import js from '@eslint/js'; +import svelte from 'eslint-plugin-svelte'; +import prettier from 'eslint-config-prettier'; +import globals from 'globals'; + +/** @type {import('eslint').Linter.Config[]} */ +export default [ + js.configs.recommended, + ...svelte.configs['flat/recommended'], + prettier, + ...svelte.configs['flat/prettier'], + { + languageOptions: { + globals: { + ...globals.browser, + ...globals.node, + __NAME__: 'readonly', + __VERSION__: 'readonly', + __GITHUBURL__: 'readonly', + __SVELTEVERSION__: 'readonly', + __VITEVERSION__: 'readonly', + __TAILWINDCSSVERSION__: 'readonly' + } + } + }, + { + files: ['**/*.svelte'], + languageOptions: { + parserOptions: { + svelteFeatures: { + experimentalGenerics: true + } + } + } + }, + { + ignores: [ + '.vercel/', + 'src/routes/utils/highlight/', + 'build/', + '.svelte-kit/', + 'dist/', + 'test-results/', + '*.md', + 'src/**/*/Setup.svelte' + ] + }, + { + rules: { + // Note: you must disable the base rule as it can report incorrect errors + // "no-unused-vars": "off", + 'svelte/no-at-html-tags': 'off', + 'no-unused-vars': [ + 'error', + { + varsIgnorePattern: '^_$', // Ignore variables named '_' + argsIgnorePattern: '^_$', // Ignore function arguments named '_' + caughtErrors: 'none' // Ignore unused variables in catch clauses + } + ] + } + } +]; diff --git a/index.html b/index.html index 06cf42d..a10fac2 100644 --- a/index.html +++ b/index.html @@ -1,4 +1,4 @@ - + @@ -12,8 +12,7 @@ rel="stylesheet" href="https://unpkg.com/prism-themes@1.6.0/themes/prism-duotone-dark.css" /> - - + + +{/if} diff --git a/src/index.css b/src/app.css similarity index 66% rename from src/index.css rename to src/app.css index a100278..ed2f66c 100644 --- a/src/index.css +++ b/src/app.css @@ -29,7 +29,8 @@ --shiki-token-link: #ffab70; } } -/* hello.dev nextra default colors */ + +/* End of Nextra default colors */ pre[class*='language-'] { @apply m-0; @@ -43,29 +44,14 @@ input[type='url'] { @apply text-charcoal dark:text-[#d4d4d4]; } -input[type='radio'], -input[type='checkbox'] { - @apply hover:border-2 border-charcoal dark:border-[#808080]; -} - @tailwind components; -.form-input, -.form-checkbox { - @apply rounded-sm bg-transparent; -} - -.form-radio { - @apply bg-transparent; -} - .btn { - @apply ring-1 border-none bg-transparent ring-charcoal dark:ring-[#808080] rounded-sm; - @apply hover:ring-2; + @apply ring-1 border-none bg-transparent ring-charcoal dark:ring-[#808080] rounded-sm hover:ring-2; } .form-input { - @apply ring-1 border-none hover:ring-2; + @apply rounded-sm bg-transparent ring-1 border-none hover:ring-2; @apply ring-charcoal !important; @apply dark:ring-[#808080] !important; } @@ -75,4 +61,22 @@ input[type='checkbox'] { @apply text-charcoal dark:text-gray opacity-50; } +pre { + @apply whitespace-pre-wrap; +} + +.hello-btn-black-and-static { + @apply h-11 font-medium; +} + +input[type='checkbox'] { + @apply form-checkbox w-[14px] h-[14px] text-charcoal dark:text-[#808080] border-charcoal dark:border-[#808080] rounded-sm bg-transparent; + @apply hover:border-2 focus:ring-0 focus:outline-none focus:ring-offset-0; +} + +input[type='radio'] { + @apply form-radio w-[14px] h-[14px] text-charcoal dark:text-[#808080] border-charcoal dark:border-[#808080] bg-transparent; + @apply hover:border-2 focus:ring-0 focus:outline-none focus:ring-offset-0; +} + @tailwind utilities; diff --git a/src/assets/svelte.png b/src/assets/svelte.png deleted file mode 100644 index e673c91c7bcb0e3a1be737745af56990613a641e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5185 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 diff --git a/src/lib/components/AuthorizationRequest.svelte b/src/lib/components/AuthorizationRequest.svelte new file mode 100644 index 0000000..5d64b64 --- /dev/null +++ b/src/lib/components/AuthorizationRequest.svelte @@ -0,0 +1,74 @@ + + +
+ Authorization Request + {#if localStorage.plausible_ignore} + + {/if} + +
+ + + + + + + + + +
+
diff --git a/src/lib/components/AuthorizationResponse.svelte b/src/lib/components/AuthorizationResponse.svelte new file mode 100644 index 0000000..24ed2d1 --- /dev/null +++ b/src/lib/components/AuthorizationResponse.svelte @@ -0,0 +1,41 @@ + + +
+ Authorization Response + + {#if authzResponse.url} +
+ + + {#if authzResponse.token} + + {/if} + + + {#if authzResponse.parsed} + + {:else if authzResponse.introspect} + + {/if} + + {#if authzResponse.userinfo} + + {/if} +
+ {:else} +

Nothing to see here yet

+ {/if} +
diff --git a/src/lib/components/ChevronY.svelte b/src/lib/components/ChevronY.svelte new file mode 100644 index 0000000..1899469 --- /dev/null +++ b/src/lib/components/ChevronY.svelte @@ -0,0 +1,15 @@ + + + + + diff --git a/src/lib/components/CopyButton.svelte b/src/lib/components/CopyButton.svelte new file mode 100644 index 0000000..99d48f4 --- /dev/null +++ b/src/lib/components/CopyButton.svelte @@ -0,0 +1,41 @@ + + + diff --git a/src/lib/components/CopyText.svelte b/src/lib/components/CopyText.svelte new file mode 100644 index 0000000..4ae0f12 --- /dev/null +++ b/src/lib/components/CopyText.svelte @@ -0,0 +1,16 @@ + + + diff --git a/src/lib/components/FileIssue.svelte b/src/lib/components/FileIssue.svelte new file mode 100644 index 0000000..affc79a --- /dev/null +++ b/src/lib/components/FileIssue.svelte @@ -0,0 +1,14 @@ + + +
+ + File an issue on GitHub + + +
diff --git a/src/lib/components/Header.svelte b/src/lib/components/Header.svelte new file mode 100644 index 0000000..f346c2f --- /dev/null +++ b/src/lib/components/Header.svelte @@ -0,0 +1,101 @@ + + +
+
+ + +
+ + Hellō Playground + +
+ +
+ + {#if mobileMenu} +
+ +
+ +
(mobileMenu = false)} + onkeydown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + mobileMenu = false; + } + }} + class="md:hidden fixed top-12 left-0 z-40 bg-black bg-opacity-60 w-full h-full" + >
+ {/if} +
diff --git a/src/lib/components/Icons/CloseIcon.svelte b/src/lib/components/Icons/CloseIcon.svelte new file mode 100644 index 0000000..981a073 --- /dev/null +++ b/src/lib/components/Icons/CloseIcon.svelte @@ -0,0 +1,10 @@ + + + diff --git a/src/lib/components/Icons/ExternalLinkIcon.svelte b/src/lib/components/Icons/ExternalLinkIcon.svelte new file mode 100644 index 0000000..f8382db --- /dev/null +++ b/src/lib/components/Icons/ExternalLinkIcon.svelte @@ -0,0 +1,14 @@ + + + diff --git a/src/lib/components/Icons/MenuIcon.svelte b/src/lib/components/Icons/MenuIcon.svelte new file mode 100644 index 0000000..81bfeeb --- /dev/null +++ b/src/lib/components/Icons/MenuIcon.svelte @@ -0,0 +1,14 @@ + + + diff --git a/src/lib/components/Inputs/DomainHintInput.svelte b/src/lib/components/Inputs/DomainHintInput.svelte new file mode 100644 index 0000000..4e1a677 --- /dev/null +++ b/src/lib/components/Inputs/DomainHintInput.svelte @@ -0,0 +1,21 @@ + + + + +{#if invalidSlugs?.length} +

+ {invalidSlugs} + {invalidSlugs.length > 1 ? 'are unsupported values' : 'is an unsupported value'} +

+{/if} diff --git a/src/lib/components/Inputs/ProviderHintInput.svelte b/src/lib/components/Inputs/ProviderHintInput.svelte new file mode 100644 index 0000000..3a3068b --- /dev/null +++ b/src/lib/components/Inputs/ProviderHintInput.svelte @@ -0,0 +1,43 @@ + + + + +{#if invalidSlugs?.length} +

+ {invalidSlugs} + {invalidSlugs.length > 1 ? 'are unsupported values' : 'is an unsupported value'} +

+{/if} diff --git a/src/lib/components/Notification.svelte b/src/lib/components/Notification.svelte new file mode 100644 index 0000000..3e68ce0 --- /dev/null +++ b/src/lib/components/Notification.svelte @@ -0,0 +1,24 @@ + + +
+ Authorization Error - Please see Response section below + +
diff --git a/src/lib/components/Request/AuthorizationServer.svelte b/src/lib/components/Request/AuthorizationServer.svelte new file mode 100644 index 0000000..0e58a9e --- /dev/null +++ b/src/lib/components/Request/AuthorizationServer.svelte @@ -0,0 +1,71 @@ + + +
+ + {#if dropdowns.server} + {@const ALL_AUTHZ_SERVERS = [ + ...AUTHZ_SERVERS.SERVERS, + ...(isHelloMode ? AUTHZ_SERVERS.HELLO_EXTEND_SERVERS : []) + ]} +
    + {#each ALL_AUTHZ_SERVERS as server} +
  • + + {#await validate(server)} + Checking ... + {:then validateOk} + + + {/await} +
  • + {/each} +
  • + + (selectedAuthzServer = 'custom-authz-server')} + bind:value={customAuthzServer} + /> +
  • +
+ {/if} +
diff --git a/src/lib/components/Request/HelloModeToggle.svelte b/src/lib/components/Request/HelloModeToggle.svelte new file mode 100644 index 0000000..49d357f --- /dev/null +++ b/src/lib/components/Request/HelloModeToggle.svelte @@ -0,0 +1,32 @@ + + +
+
+ + +
+
+ + +
+
+ + diff --git a/src/lib/components/Request/HelloParams.svelte b/src/lib/components/Request/HelloParams.svelte new file mode 100644 index 0000000..0b9f758 --- /dev/null +++ b/src/lib/components/Request/HelloParams.svelte @@ -0,0 +1,83 @@ + + +
+ + {#if dropdowns.hello} + {@const ALL_HELLO_PARAMS = [ + ...PARAMS.HELLO_PARAM.PARAMS, + ...(isHelloMode ? PARAMS.HELLO_PARAM.HELLO_EXTEND_PARAMS : []) + ]} +
    + {#each ALL_HELLO_PARAMS as param} + {@const required = PARAMS.HELLO_PARAM.REQUIRED.includes(param.NAME)} + {@const selected = selectedHelloParams.includes(param.NAME)} + {@const validateOk = validate({ + param, + protocolParams: selectedProtocolParams, + helloParams: selectedHelloParams, + helloParamsValues: selectedHelloParamsValues + })} +
  • +
    + + +
    +
    + {#if param.NAME === 'provider_hint'} + + {:else if param.NAME === 'domain_hint'} + + {:else} + + {/if} + {#if param.HINT} +

    + {param.HINT} +

    + {/if} +
    +
  • + {/each} +
+ {/if} +
diff --git a/src/lib/components/Request/InviteRequest.svelte b/src/lib/components/Request/InviteRequest.svelte new file mode 100644 index 0000000..0696256 --- /dev/null +++ b/src/lib/components/Request/InviteRequest.svelte @@ -0,0 +1,51 @@ + + +
+ Invite Request +
+ {#if canInvite} +
+ + + +

Invite URL

+ + {@html highlight('http', lineBreakUrl(inviteUrl))} + +
+ {/if} + + +

+ {#if canInvite} + Use this to test sending invitations + {:else} + To invite others, your must provide the
`name` and `email` claims + {/if} +

+
+
diff --git a/src/lib/components/Request/ProtocolParams.svelte b/src/lib/components/Request/ProtocolParams.svelte new file mode 100644 index 0000000..42f602e --- /dev/null +++ b/src/lib/components/Request/ProtocolParams.svelte @@ -0,0 +1,108 @@ + + +
+ + {#if dropdowns.protocol} +
    + {#each PARAMS.PROTOCOL_PARAM.PARAMS as param} + {@const required = PARAMS.PROTOCOL_PARAM.REQUIRED.includes(param.NAME)} + {@const selected = selectedProtocolParams.includes(param.NAME)} + {@const hasValue = !!selectedProtocolParamsValues[param.NAME]} + {@const requiredOk = !required || (selected && hasValue)} + {@const needsOk = validate({ + param: param, + protocolParams: selectedProtocolParams, + protocolParamsValues: selectedProtocolParamsValues, + helloParams: selectedHelloParams, + helloParamsValues: selectedHelloParamsValues + })} + {@const error = !requiredOk || !needsOk} +
  • + + + + {#if Array.isArray(param.POSSIBLE_VALUE)} +
      + {#each param.POSSIBLE_VALUE as value} +
    • + {#if param.ONLY_ONE} + + {:else} + + {/if} + +
    • + {/each} +
    + {:else} + + {/if} +
  • + {/each} +
+ {/if} +
diff --git a/src/lib/components/Request/RequestUrl.svelte b/src/lib/components/Request/RequestUrl.svelte new file mode 100644 index 0000000..b82a41c --- /dev/null +++ b/src/lib/components/Request/RequestUrl.svelte @@ -0,0 +1,58 @@ + + +
+
+ +
+ {#if dropdowns.request} +
+
+ + + + + {@html highlight('http', lineBreakUrl(authzUrl))} + +
+
+ {/if} + + + +
diff --git a/src/lib/components/Request/ScopeParam.svelte b/src/lib/components/Request/ScopeParam.svelte new file mode 100644 index 0000000..1f665d9 --- /dev/null +++ b/src/lib/components/Request/ScopeParam.svelte @@ -0,0 +1,110 @@ + + +
+ + + {#if isOverwritten} +

+ Overwritten with 'scope' in Protocol Parameters section +

+ {/if} + + {#if dropdowns.scope} + {@const ALL_STANDARD_SCOEPS = [ + ...PARAMS.SCOPE_PARAM.STANDARD, + ...(isHelloMode ? PARAMS.SCOPE_PARAM.HELLO_EXTEND_STANDARD : []) + ]} + {@const ALL_NON_STANDARD_SCOPES = [ + ...PARAMS.SCOPE_PARAM.NON_STANDARD, + ...(isHelloMode ? PARAMS.SCOPE_PARAM.HELLO_EXTEND_NON_STANDARD : []) + ]} +
+
    + {#each ALL_STANDARD_SCOEPS as stdScope} + {@const required = PARAMS.SCOPE_PARAM.REQUIRED.includes(stdScope)} + {@const selected = selectedScopes.includes(stdScope)} + {@const requiredOk = !required || selected} + {@const validateOk = validate(stdScope, selectedScopes)} +
  • + + +
  • + {/each} +
+ +
    + {#each ALL_NON_STANDARD_SCOPES as nonStdScope} +
  • + + +
  • + {/each} + +
  • + + +
  • +
+
+ {/if} +
diff --git a/src/lib/components/Response/JsonResponse.svelte b/src/lib/components/Response/JsonResponse.svelte new file mode 100644 index 0000000..a062596 --- /dev/null +++ b/src/lib/components/Response/JsonResponse.svelte @@ -0,0 +1,16 @@ + + +
+
+ {label} + +
+

+ {@html highlight('json', JSON.stringify(json, null, 2))} +

+
diff --git a/src/lib/components/Response/UrlResponse.svelte b/src/lib/components/Response/UrlResponse.svelte new file mode 100644 index 0000000..b513b85 --- /dev/null +++ b/src/lib/components/Response/UrlResponse.svelte @@ -0,0 +1,17 @@ + + +
+
+ {label} + +
+

+ {@html highlight('http', lineBreakUrl(url))} +

+
diff --git a/src/lib/components/Tooltip.svelte b/src/lib/components/Tooltip.svelte new file mode 100644 index 0000000..1dabbb6 --- /dev/null +++ b/src/lib/components/Tooltip.svelte @@ -0,0 +1,20 @@ + + + + + + + diff --git a/src/lib/constants.js b/src/lib/constants.js new file mode 100644 index 0000000..0ee815d --- /dev/null +++ b/src/lib/constants.js @@ -0,0 +1,176 @@ +const PROFILE_CLAIMS = ['name', 'email', 'picture']; + +const SCOPE_PARAM = { + STANDARD: [ + 'openid', + 'profile', + ...PROFILE_CLAIMS, + 'nickname', + 'given_name', + 'family_name', + 'phone' + ], + NON_STANDARD: ['ethereum', 'discord', 'twitter', 'github', 'gitlab'], + + // Hellō Dev Mode + HELLO_EXTEND_STANDARD: ['preferred_username'], + HELLO_EXTEND_NON_STANDARD: ['recovery', 'verified_name', 'existing_name', 'existing_username'], + + REQUIRED: ['openid'], + DEFAULT_SELECTED: ['openid', 'profile'] +}; + +const PROTOCOL_PARAM = { + PARAMS: [ + { + NAME: 'client_id', + POSSIBLE_VALUE: '' + }, + { + NAME: 'redirect_uri', + POSSIBLE_VALUE: '' + }, + { + NAME: 'nonce', + POSSIBLE_VALUE: '' + }, + { + NAME: 'response_type', + POSSIBLE_VALUE: ['code', 'id_token'], + ONLY_ONE: true + }, + { + NAME: 'code_challenge', + POSSIBLE_VALUE: '' + }, + { + NAME: 'code_verifier', + POSSIBLE_VALUE: '', + CHECKBOX_HIDDEN: true + }, + { + NAME: 'response_mode', + POSSIBLE_VALUE: ['fragment', 'query'], + ONLY_ONE: true + }, + { + NAME: 'state', + POSSIBLE_VALUE: '' + }, + { + NAME: 'prompt', + POSSIBLE_VALUE: ['consent', 'login'], + ONLY_ONE: false + }, + { + NAME: 'login_hint', + POSSIBLE_VALUE: '', + PLACEHOLDER: 'name@example.com' + }, + { + NAME: 'scope', + POSSIBLE_VALUE: '', + PLACEHOLDER: 'space separated' + } + ], + REQUIRED: ['client_id', 'nonce', 'redirect_uri', 'response_type'], + DEFAULT_SELECTED: [ + 'client_id', + 'nonce', + 'redirect_uri', + 'response_type', + 'code_challenge', + 'response_mode' + ], + DEFAULT_VALUES: { + client_id: 'app_HelloDeveloperPlayground_Iq2', + redirect_uri: 'https://playground.hello.dev/', + response_type: 'code', + response_mode: 'fragment', + prompt: 'consent' + } +}; + +const PROVIDER_HINTS = [ + 'apple', + 'google', + 'discord', + 'facebook', + 'github', + 'gitlab', + 'twitch', + 'twitter', + 'tumblr', + 'mastodon', + 'microsoft', + 'line', + 'wordpress', + 'yahoo', + 'zoho', + 'email', + 'ethereum', + 'qrcode', + 'passkey' +].sort(); + +const HELLO_PARAM = { + PARAMS: [ + { + NAME: 'provider_hint', + POSSIBLE_VALUE: PROVIDER_HINTS, + HINT: [...PROVIDER_HINTS, 'apple--', 'microsoft--', 'google-', 'email--', 'passkey--'].join( + ' ' + ), + ONLY_ONE: false + }, + { + NAME: 'domain_hint', + POSSIBLE_VALUE: '', + HINT: 'personal | managed | domain.example', + ONLY_ONE: true + }, + { + NAME: 'custom', + POSSIBLE_VALUE: '', + PLACEHOLDER: 'foo=bar&hello=world' + } + ], + + // Hellō Dev Mode + HELLO_EXTEND_PARAMS: [ + { + NAME: 'passkeys', + POSSIBLE_VALUE: '' + } + ], + + REQUIRED: [], + DEFAULT_SELECTED: [], + DEFAULT_VALUES: { + passkeys: 'global' + } +}; + +const PARAMS = { + SCOPE_PARAM, + PROTOCOL_PARAM, + HELLO_PARAM +}; + +const BETA_SERVER = 'https://wallet.hello-beta.net/authorize'; + +const SERVERS = ['https://wallet.hello.coop/authorize', BETA_SERVER]; + +const AUTHZ_SERVERS = { + SERVERS, + DEFAULT_SELECTED: SERVERS[0], + + // Hellō Dev Mode + HELLO_EXTEND_SERVERS: [ + 'https://wallet.hello-staging.net/authorize', + 'https://wallet.hello-local.net/authorize', + 'https://wallet.hello-dev.net/authorize' + ] +}; + +export { PARAMS, AUTHZ_SERVERS, BETA_SERVER, PROFILE_CLAIMS }; diff --git a/src/lib/request.js b/src/lib/request.js new file mode 100644 index 0000000..d12a35b --- /dev/null +++ b/src/lib/request.js @@ -0,0 +1,76 @@ +function makeAuthzUrl({ + authzServer, + customAuthzServer = null, + scopes, + customScope = null, + protocolParams, + protocolParamsValues, + helloParams, + helloParamsValues +}) { + try { + const server = authzServer === 'custom-authz-server' ? customAuthzServer : authzServer; + const url = new URL(server); + + // scope is not overridden in protocol params section + if (scopes.length && !protocolParams.includes('scope')) { + let scopesStr = scopes.join(' '); + // replace 'custom-scope' key w/ custom scope value + scopesStr = scopesStr.replace('custom-scope', customScope); + // trim whitespace at ends so there is no trailing '+' + scopesStr = scopesStr.trim(); + url.searchParams.set('scope', scopesStr); + } + + for (const key in protocolParamsValues) { + // value exists + if (!protocolParamsValues[key].length) continue; + // param not selected + if (!protocolParams.includes(key)) continue; + + const value = protocolParamsValues[key]; + if (Array.isArray(value)) { + // value of prompt is array [consent,login] + url.searchParams.set(key, protocolParamsValues[key].join(' ')); + } else { + url.searchParams.set(key, protocolParamsValues[key].trim()); + } + } + + for (const key in helloParamsValues) { + // value exists + if (!helloParamsValues[key].length) continue; + // param not selected + if (!helloParams.includes(key)) continue; + + if (key === 'custom') { + // interpret 'custom' value as param + value + // i.e. custom='foo=bar&bar=foo' => '&foo=bar&bar=foo' + const custom = new URLSearchParams(helloParamsValues[key]); + for (const [key, value] of custom) { + url.searchParams.set(key, value.trim()); + } + continue; + } else { + url.searchParams.set(key, helloParamsValues[key].trim()); + } + } + + return url.href; + } catch (_) { + // do nothing with err since common flow + // (user can enter custom authz server url) + return 'Invalid URL'; + } +} + +function makeInviteUrl({ authzServer, claims, protocolParamsValues }) { + const url = new URL('/invite', authzServer); + url.searchParams.set('inviter', claims.sub); + url.searchParams.set('client_id', protocolParamsValues.client_id); + url.searchParams.set('initiate_login_uri', window.location.origin); + url.searchParams.set('return_uri', window.location.origin); + return url.href; +} + +export { makeAuthzUrl, makeInviteUrl }; diff --git a/src/lib/shiki.js b/src/lib/shiki.js new file mode 100644 index 0000000..c811597 --- /dev/null +++ b/src/lib/shiki.js @@ -0,0 +1,30 @@ +import { createCssVariablesTheme } from 'shiki/core'; +import { createHighlighterCore } from 'shiki'; +import getWasm from 'shiki/wasm'; +import json from 'shiki/langs/json.mjs'; +import http from 'shiki/langs/http.mjs'; + +let shiki; +async function init() { + const theme = createCssVariablesTheme({ + name: 'css-vars', + variablePrefix: '--shiki-', + variableDefaults: {}, + fontStyle: true + }); + shiki = await createHighlighterCore({ + loadWasm: getWasm, + themes: [theme], + langs: [json, http] + }); +} + +function highlight(lang, content) { + const html = shiki.codeToHtml(content, { + lang, + theme: 'css-vars' + }); + return html; +} + +export { init, highlight }; diff --git a/src/lib/utils.js b/src/lib/utils.js new file mode 100644 index 0000000..a4ea9e9 --- /dev/null +++ b/src/lib/utils.js @@ -0,0 +1,89 @@ +import { createAuthRequest } from '@hellocoop/helper-browser'; + +function cleanUrl() { + if (window.location.search) { + window.history.replaceState({}, document.title, '/'); + } else { + window.location.replace('#'); + // slice off the remaining '#' in HTML5: + if (typeof window.history.replaceState == 'function') { + history.replaceState({}, '', window.location.href.slice(0, -1)); + } + } +} + +function removeLoader() { + document.getElementById('load-spinner')?.remove(); //remove spinner +} + +function lineBreakUrl(url) { + return url.replace(/&/g, '\n&').replace(/\?/g, '\n?'); +} + +function reset() { + localStorage.removeItem('states'); + window.location.reload(); +} + +function handleLegacyState() { + // move old state to new var just in case we need it later + localStorage.setItem('_legacy_states', localStorage.getItem('states')); + // remove old state and reload + return reset(); +} + +async function generatePkce() { + try { + const { url, nonce, code_verifier } = await createAuthRequest({ + // we just need nonce & code_verifier + client_id: 'x', + redirect_uri: 'x' + }); + // because helper-browser only returns code_verifier in url + const code_challenge = new URL(url).searchParams.get('code_challenge'); + return { + nonce, + code_verifier, + code_challenge + }; + } catch (err) { + console.error(err); + } +} + +const plausibleIgnore = + localStorage.getItem('plausible_ignore') == 'true' || + window.location.origin !== 'https://playground.hello.dev'; + +async function sendPlausibleEvent() { + if (plausibleIgnore) { + console.info('Ignoring Event: localStorage flag'); + return; + } + const _body = { + w: window.innerWidth, + d: 'playground.hello.dev', + n: 'pageview', + r: document.referrer || null, + u: new URL('https://playground.hello.dev/') + }; + try { + await fetch('/api/event', { + method: 'POST', + body: JSON.stringify(_body) + }); + console.info(`Event sent: ${_body.u} (${_body.n})`); + } catch (err) { + console.error(err); + } +} + +export { + cleanUrl, + removeLoader, + lineBreakUrl, + reset, + handleLegacyState, + generatePkce, + sendPlausibleEvent +}; diff --git a/src/lib/validate.js b/src/lib/validate.js new file mode 100644 index 0000000..050269f --- /dev/null +++ b/src/lib/validate.js @@ -0,0 +1,80 @@ +import { PROFILE_CLAIMS } from './constants.js'; + +function validateScopes(scope, selectedScopes) { + if (scope === 'profile') { + if (PROFILE_CLAIMS.every((scope) => selectedScopes.includes(scope))) return false; + } else if (PROFILE_CLAIMS.includes(scope)) { + if (selectedScopes.includes('profile')) return false; + } + return true; +} + +function validateProtocolParams({ + param, + protocolParams, + protocolParamsValues, + helloParams, + helloParamsValues +}) { + const { NAME } = param; + if (NAME === 'code_challenge') { + // skip if response_type=id_token and code_challenge unselected + const responseType = protocolParamsValues.response_type; + if (responseType === 'id_token') { + const codeChallengeSelected = protocolParams.includes('code_challenge'); + if (!codeChallengeSelected) return true; + } + // invalidate if no response_type=code + const codeChallenge = protocolParams.includes('code_challenge'); + if (!codeChallenge) return false; + const responseTypeSelected = protocolParams.includes('response_type'); + if (!responseTypeSelected) return false; + if (responseType !== 'code') return false; + } else if (NAME === 'response_mode') { + // invalidate if response_type=id_token and response_mode=query + const responseTypeSelected = protocolParams.includes('response_type'); + const responseModeSelected = protocolParams.includes('response_mode'); + if (!responseTypeSelected || !responseModeSelected) return true; + const responseType = protocolParamsValues.response_type; + const responseMode = protocolParamsValues.response_mode; + if (responseType === 'id_token' && responseMode === 'query') return false; + } else if (NAME === 'login_hint') { + // invalidate if domain_hint is custom domain (not personal or managed) + const loginHintSelected = protocolParams.includes('login_hint'); + const domainHintSelected = helloParams.includes('domain_hint'); + if (!loginHintSelected || !domainHintSelected) return true; + const domainHint = helloParamsValues.domain_hint; + if (!['personal', 'managed'].includes(domainHint.trim())) return false; + } + return true; +} + +function validateHelloParams({ param, protocolParams, helloParams, helloParamsValues }) { + const { NAME } = param; + if (NAME === 'domain_hint') { + // invalidate if domain_hint is custom domain (not personal or managed) + const loginHintSelected = protocolParams.includes('login_hint'); + const domainHintSelected = helloParams.includes('domain_hint'); + if (!loginHintSelected || !domainHintSelected) return true; + const domainHint = helloParamsValues.domain_hint; + if (!['personal', 'managed'].includes(domainHint.trim())) return false; + } + return true; +} + +async function validateAuthzServer(_) { + // TBD CORS -- no way for browser to know for sure that a URL exists + // try { + // const res = await fetch(url, { + // method: 'HEAD', // Only fetch headers, no body + // cache: 'no-cache' // To avoid using cached responses + // }); + // return res.status === 200 + // } catch (error) { + // console.error('Failed to validate', url) + // return false; + // } + return true; +} + +export { validateScopes, validateProtocolParams, validateHelloParams, validateAuthzServer }; diff --git a/src/main.js b/src/main.js index 4e8966a..d0ab9ef 100644 --- a/src/main.js +++ b/src/main.js @@ -1,7 +1,8 @@ +import { mount } from 'svelte'; +import './app.css'; import App from './App.svelte'; -import './index.css'; -const app = new App({ +const app = mount(App, { target: document.getElementById('app') }); diff --git a/src/utils/pkce.js b/src/utils/pkce.js deleted file mode 100644 index b2a25aa..0000000 --- a/src/utils/pkce.js +++ /dev/null @@ -1,39 +0,0 @@ -const makePKCE = async () => { - const code_verifier = generateRandomString(); - const code_challenge = await pkceChallengeFromVerifier(code_verifier); - return { - code_verifier, - code_challenge - }; -}; - -const pkceChallengeFromVerifier = async (v) => { - let hashed = await sha256(v); - return base64urlencode(hashed); -}; - -const generateRandomString = () => { - const array = new Uint32Array(28); - window.crypto.getRandomValues(array); - return Array.from(array, (dec) => ('0' + dec.toString(16)).substr(-2)).join(''); -}; - -const sha256 = (plain) => { - const encoder = new TextEncoder(); - const data = encoder.encode(plain); - return window.crypto.subtle.digest('SHA-256', data); -}; - -// Base64-urlencodes the input string -const base64urlencode = (str) => { - // Convert the ArrayBuffer to string using Uint8 array to conver to what btoa accepts. - // btoa accepts chars only within ascii 0-255 and base64 encodes them. - // Then convert the base64 encoded to base64url encoded - // (replace + with -, replace / with _, trim trailing =) - return btoa(String.fromCharCode.apply(null, new Uint8Array(str))) - .replace(/\+/g, '-') - .replace(/\//g, '_') - .replace(/=+$/, ''); -}; - -export default makePKCE; diff --git a/svelte.config.js b/svelte.config.js new file mode 100644 index 0000000..c6e1140 --- /dev/null +++ b/svelte.config.js @@ -0,0 +1,7 @@ +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; + +export default { + // Consult https://svelte.dev/docs#compile-time-svelte-preprocess + // for more information about preprocessors + preprocess: vitePreprocess() +}; diff --git a/tailwind.config.js b/tailwind.config.js index dae1a23..f5a49fe 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,6 +1,7 @@ -module.exports = { +/** @type {import('tailwindcss').Config} */ +export default { darkMode: 'media', - content: ['./index.html', './src/**/*.{svelte,js,ts,html}'], + content: ['./index.html', './src/**/*.{svelte,js,ts,jsx,tsx}'], theme: { extend: { spacing: { diff --git a/vite.config.js b/vite.config.js index 1f62422..ad8856d 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,11 +1,14 @@ import { defineConfig } from 'vite'; import { svelte } from '@sveltejs/vite-plugin-svelte'; +import path from 'path'; -// https://vitejs.dev/config/ +// https://vite.dev/config/ export default defineConfig({ plugins: [svelte()], - build: { - outDir: 'S3', - emptyOutDir: true + resolve: { + alias: { + $lib: path.resolve('./src/lib'), + $components: path.resolve('./src/lib/components') + } } });