├── .changeset └── config.json ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yaml │ ├── config.yaml │ └── feature_request.yaml ├── pull_request_template.md └── workflows │ ├── changesets.yaml │ ├── deploy-permissions.md │ ├── deploy-pp-docs-prod.yaml │ ├── deploy-pp-docs-staging.yaml │ ├── deploy-sk-prod.yaml │ ├── deploy-sk-staging.yaml │ └── validate.yaml ├── .gitignore ├── .npmrc ├── .prettierignore ├── DockerCompose.samplekit.dev.yaml ├── DockerCompose.samplekit.preview.yaml ├── Dockerfile.pp-docs.gh ├── Dockerfile.samplekit.gh ├── Dockerfile.samplekit.preview ├── Dockerfile.samplekit.preview.dockerignore ├── LICENSE ├── README.md ├── caprover-apps ├── cron │ ├── Dockerfile │ ├── bin │ │ └── reset_db.sh.example │ ├── captain-definition │ ├── deploy.sh │ └── readme.md ├── log-stream │ ├── deploy.sh │ └── readme.md ├── nginx-static │ └── deploy.sh ├── pg │ ├── deploy.sh │ └── readme.md ├── redis │ ├── deploy.sh │ └── readme.md └── sveltekit │ └── deploy.sh ├── eslint.config.js ├── package.json ├── packages ├── auth │ ├── LICENSE │ ├── package.json │ ├── prettier.config.js │ ├── src │ │ ├── client │ │ │ ├── auth.ts │ │ │ ├── index.ts │ │ │ └── passkey │ │ │ │ └── handlers.ts │ │ ├── common │ │ │ ├── index.ts │ │ │ └── schema.ts │ │ ├── server │ │ │ ├── auth.ts │ │ │ ├── config.ts │ │ │ ├── index.ts │ │ │ ├── provider │ │ │ │ ├── index.ts │ │ │ │ ├── oauth │ │ │ │ │ ├── github.ts │ │ │ │ │ ├── google.ts │ │ │ │ │ └── utils.ts │ │ │ │ └── pass │ │ │ │ │ ├── email.ts │ │ │ │ │ └── mfa │ │ │ │ │ ├── common.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── passkey.ts │ │ │ ├── session.ts │ │ │ ├── sessionHandler.ts │ │ │ ├── token │ │ │ │ ├── consts.ts │ │ │ │ ├── getOrCreate.ts │ │ │ │ ├── index.ts │ │ │ │ └── validate.ts │ │ │ └── user.ts │ │ ├── types │ │ │ ├── client.ts │ │ │ ├── common.ts │ │ │ └── server │ │ │ │ ├── adapters.ts │ │ │ │ ├── auth.ts │ │ │ │ ├── config.ts │ │ │ │ ├── cookie.ts │ │ │ │ ├── errors.ts │ │ │ │ ├── index.ts │ │ │ │ └── repository │ │ │ │ ├── index.ts │ │ │ │ ├── provider.ts │ │ │ │ ├── session.ts │ │ │ │ ├── sessionHandler.ts │ │ │ │ ├── token.ts │ │ │ │ └── user.ts │ │ └── utils │ │ │ └── common │ │ │ └── index.ts │ └── tsconfig.json ├── preprocess-katex-vscode │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── snippets │ │ └── katex.pp-svelte.code-snippets │ ├── static │ │ ├── demo-comment-katex-before.png │ │ ├── demo-comment-katex.png │ │ ├── demo-template-katex-before.png │ │ ├── demo-template-katex.png │ │ ├── sk-katex.png │ │ └── sk-katex.svg │ └── syntaxes │ │ ├── katex.pp-svelte.tmLanguage.json │ │ └── katex.template-literal.tmLanguage.json ├── preprocess-katex │ ├── CHANGELOG.md │ ├── LICENSE │ ├── package.json │ ├── prettier.config.js │ ├── readme.md │ ├── src │ │ ├── client.ts │ │ ├── index.ts │ │ ├── katex.css │ │ ├── katex.ts │ │ ├── logger.ts │ │ └── preprocess-katex.ts │ └── tsconfig.json ├── preprocess-markdown-vscode │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── snippets │ │ └── md.pp-svelte.code-snippets │ ├── static │ │ ├── demo-comment-md-before.png │ │ ├── demo-comment-md.png │ │ ├── sk-md.png │ │ └── sk-md.svg │ └── syntaxes │ │ └── md.pp-svelte.tmLanguage.json ├── preprocess-markdown │ ├── CHANGELOG.md │ ├── LICENSE │ ├── package.json │ ├── prettier.config.js │ ├── readme.md │ ├── src │ │ ├── index.ts │ │ ├── logger.ts │ │ └── preprocess-md.ts │ └── tsconfig.json ├── preprocess-shiki-vscode │ ├── .vscodeignore │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── scripts │ │ └── generate │ │ │ ├── shiki-block.js │ │ │ └── shiki-line.js │ ├── snippets │ │ └── shiki.pp-svelte.code-snippets │ ├── static │ │ ├── demo-comment-code-before.png │ │ ├── demo-comment-code.png │ │ ├── sk-shiki.png │ │ └── sk-shiki.svg │ └── syntaxes │ │ └── generated │ │ ├── shiki-block.pp-svelte.tmLanguage.json │ │ └── shiki-line.pp-svelte.tmLanguage.json ├── preprocess-shiki │ ├── CHANGELOG.md │ ├── LICENSE │ ├── package.json │ ├── prettier.config.js │ ├── readme.md │ ├── src │ │ ├── defaultOpts.ts │ │ ├── highlight.ts │ │ ├── index.ts │ │ ├── logger.ts │ │ ├── preprocessors.ts │ │ ├── strip-options │ │ │ ├── findAllSubstrWindows.ts │ │ │ ├── index.ts │ │ │ ├── option-type │ │ │ │ ├── range.test.ts │ │ │ │ ├── range.ts │ │ │ │ ├── rustyString.test.ts │ │ │ │ ├── rustyString.ts │ │ │ │ └── utils.ts │ │ │ ├── parseGlobalOptGroup.test.ts │ │ │ ├── parseGlobalOptGroup.ts │ │ │ ├── parseLineOptGroup.ts │ │ │ ├── splitCodeFence.test.ts │ │ │ └── splitCodeFence.ts │ │ ├── themes │ │ │ └── darker.ts │ │ └── types.ts │ ├── tsconfig.json │ └── vitest.config.ts └── svelte-crop-window │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── postcss.config.js │ ├── prettier.config.js │ ├── src │ ├── app.css │ ├── app.d.ts │ ├── app.html │ ├── lib │ │ ├── LICENSE_melt-ui │ │ ├── LICENSE_svelte-crop-window │ │ ├── actions │ │ │ ├── handlers.ts │ │ │ ├── mouseEvents.ts │ │ │ └── touchScalePanRotate.ts │ │ ├── cropWindow.svelte.ts │ │ ├── index.ts │ │ ├── styleHelpers.ts │ │ └── utils │ │ │ ├── css.ts │ │ │ ├── defaults.ts │ │ │ ├── geometry.ts │ │ │ ├── id.ts │ │ │ ├── point.ts │ │ │ └── types.ts │ └── routes │ │ ├── +layout.svelte │ │ ├── +layout.ts │ │ └── +page.svelte │ ├── static │ └── favicon.png │ ├── svelte.config.js │ ├── tailwind.config.ts │ ├── tsconfig.json │ └── vite.config.ts ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── prettier.config.js ├── scripts ├── .githooks │ └── pre-commit ├── check-dependencies.js └── release.sh ├── sites ├── preprocessor-docs │ ├── .npmrc │ ├── LICENSE │ ├── README.md │ ├── cspell.json │ ├── nginx-default.conf │ ├── package.json │ ├── postcss.config.js │ ├── prettier.config.js │ ├── scripts │ │ └── generate-toc.ts │ ├── src │ │ ├── app.css │ │ ├── app.d.ts │ │ ├── app.html │ │ ├── hooks.server.ts │ │ ├── lib │ │ │ ├── actions │ │ │ │ ├── clickoutside.ts │ │ │ │ ├── index.ts │ │ │ │ ├── keyboard.ts │ │ │ │ └── windowEscape.ts │ │ │ ├── components │ │ │ │ ├── Admonition.svelte │ │ │ │ ├── HAnchor.svelte │ │ │ │ ├── LogoIcon.svelte │ │ │ │ └── index.ts │ │ │ ├── controllers │ │ │ │ ├── dropdown.svelte.ts │ │ │ │ ├── index.ts │ │ │ │ └── keyboard.ts │ │ │ ├── icons │ │ │ │ ├── GitHub.svelte │ │ │ │ ├── _Icon.svelte │ │ │ │ └── index.ts │ │ │ ├── math │ │ │ │ ├── components │ │ │ │ │ ├── Display.svelte │ │ │ │ │ └── Inline.svelte │ │ │ │ └── index.ts │ │ │ ├── nav │ │ │ │ ├── consts.ts │ │ │ │ ├── index.ts │ │ │ │ ├── sidebar │ │ │ │ │ ├── SidebarContent.svelte │ │ │ │ │ ├── SidebarState.svelte.ts │ │ │ │ │ ├── TOCActive.svelte │ │ │ │ │ ├── TOCInactive.svelte │ │ │ │ │ ├── generated │ │ │ │ │ │ └── toc.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── observeToc.svelte.ts │ │ │ │ │ ├── sidebarStorage.ts │ │ │ │ │ └── types.ts │ │ │ │ └── topbar │ │ │ │ │ ├── TopbarContent.svelte │ │ │ │ │ ├── index.ts │ │ │ │ │ └── topbarController.svelte.ts │ │ │ ├── shiki │ │ │ │ └── index.js │ │ │ ├── styles │ │ │ │ ├── PaletteMenu.svelte │ │ │ │ ├── css │ │ │ │ │ ├── code.css │ │ │ │ │ ├── themes.css │ │ │ │ │ └── tokens.css │ │ │ │ ├── index.ts │ │ │ │ ├── postcss │ │ │ │ │ ├── components │ │ │ │ │ │ ├── btn.css │ │ │ │ │ │ ├── features.css │ │ │ │ │ │ ├── link.css │ │ │ │ │ │ └── steps.css │ │ │ │ │ ├── cssAsPlugin.ts │ │ │ │ │ └── utilities │ │ │ │ │ │ ├── underline.css │ │ │ │ │ │ └── utils.css │ │ │ │ ├── tailwindConfig.ts │ │ │ │ ├── themeController.svelte.ts │ │ │ │ └── themeUtils.ts │ │ │ └── utils │ │ │ │ ├── client │ │ │ │ ├── defineCtx.ts │ │ │ │ ├── index.ts │ │ │ │ └── storage.ts │ │ │ │ └── common │ │ │ │ └── index.ts │ │ └── routes │ │ │ ├── 404 │ │ │ └── +page.svelte │ │ │ ├── +error.svelte │ │ │ ├── +layout.svelte │ │ │ ├── +layout.ts │ │ │ ├── docs │ │ │ ├── +layout.svelte │ │ │ ├── code-decoration │ │ │ │ ├── +page.server.ts │ │ │ │ └── +page.svelte │ │ │ ├── codeTheme.ctx.svelte.ts │ │ │ ├── markdown │ │ │ │ └── +page.svelte │ │ │ ├── math │ │ │ │ ├── +page.server.ts │ │ │ │ └── +page.svelte │ │ │ ├── sidebar.ctx.svelte.ts │ │ │ └── topbar.ctx.svelte.ts │ │ │ └── themeController.ctx.ts │ ├── static │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon.png │ │ ├── browserconfig.xml │ │ ├── cookies.js │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── fonts │ │ │ └── UbuntuMonoLigaturized │ │ │ │ ├── LICENCE.txt │ │ │ │ ├── UbuntuMonoLigaturized-Regular.ttf │ │ │ │ └── copyright.txt │ │ ├── initSidebar.js │ │ ├── initTheme.js │ │ ├── mstile-144x144.png │ │ ├── mstile-150x150.png │ │ ├── mstile-310x150.png │ │ ├── mstile-310x310.png │ │ ├── mstile-70x70.png │ │ ├── overview-photo-1420w.webp │ │ ├── safari-pinned-tab.svg │ │ ├── samplekit-logo-128x128.png │ │ ├── samplekit-logo.svg │ │ └── site.webmanifest │ ├── svelte.config.js │ ├── tailwind.config.ts │ ├── tsconfig.json │ └── vite.config.ts └── samplekit.dev │ ├── .env.example │ ├── .graphqlrc.ts │ ├── LICENSE │ ├── README.md │ ├── cspell.json │ ├── drizzle.gen.ts │ ├── drizzle.studio.ts │ ├── generated │ ├── db-migrations │ │ ├── 0000_keen_wonder_man.sql │ │ ├── 0001_tricky_rick_jones.sql │ │ ├── 0002_sturdy_young_avengers.sql │ │ ├── 0003_heavy_wraith.sql │ │ ├── 0004_new_bug.sql │ │ ├── 0005_bumpy_patch.sql │ │ └── meta │ │ │ ├── 0000_snapshot.json │ │ │ ├── 0001_snapshot.json │ │ │ ├── 0002_snapshot.json │ │ │ ├── 0003_snapshot.json │ │ │ ├── 0004_snapshot.json │ │ │ ├── 0005_snapshot.json │ │ │ └── _journal.json │ └── shopify-graphql-types │ │ ├── storefront-2024-10.schema.json │ │ ├── storefront.generated.d.ts │ │ └── storefront.types.d.ts │ ├── package.json │ ├── postcss.config.js │ ├── prettier.config.js │ ├── scripts │ ├── applyDbMigrations.js │ ├── email │ │ ├── templates │ │ │ └── auth-email.html │ │ └── updateTemplates.js │ └── generate-theme-utils.sh │ ├── src │ ├── app.css │ ├── app.d.ts │ ├── app.html │ ├── hooks.client.ts │ ├── hooks.server.ts │ ├── lib │ │ ├── articles │ │ │ ├── components │ │ │ │ ├── Changelog.svelte │ │ │ │ ├── DateLine.svelte │ │ │ │ ├── HAnchor.svelte │ │ │ │ ├── PrevNext.svelte │ │ │ │ ├── Series.svelte │ │ │ │ ├── TOC.svelte │ │ │ │ ├── TOCBase.svelte │ │ │ │ ├── TOCMelt.svelte │ │ │ │ ├── Vaul.svelte │ │ │ │ ├── card │ │ │ │ │ ├── CardList.svelte │ │ │ │ │ ├── FeatureCard.svelte │ │ │ │ │ ├── LinksAndDate.svelte │ │ │ │ │ ├── Tags.svelte │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ └── tab-panels │ │ │ │ │ ├── CodeTopper.svelte │ │ │ │ │ ├── CodeTopper45.svelte │ │ │ │ │ ├── Copy.svelte │ │ │ │ │ ├── DiagonalLines.svelte │ │ │ │ │ ├── PatternWrapper.svelte │ │ │ │ │ ├── TabPanelItem.svelte │ │ │ │ │ ├── TabPanels.svelte │ │ │ │ │ ├── collapsed.ctx.svelte.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── svelte45.ctx.svelte.ts │ │ │ ├── load │ │ │ │ ├── data │ │ │ │ │ └── common.ts │ │ │ │ ├── demos │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── common.ts │ │ │ │ │ ├── server.ts │ │ │ │ │ └── types.ts │ │ │ │ └── index.ts │ │ │ └── schemas.ts │ │ ├── auth │ │ │ ├── client │ │ │ │ └── index.ts │ │ │ ├── common │ │ │ │ └── index.ts │ │ │ └── server │ │ │ │ ├── config.ts │ │ │ │ ├── createAuth.ts │ │ │ │ ├── index.ts │ │ │ │ └── middleware.ts │ │ ├── bot-protection │ │ │ └── turnstile │ │ │ │ ├── client │ │ │ │ ├── TurnstileEl.svelte │ │ │ │ ├── index.ts │ │ │ │ ├── turnstile.svelte.ts │ │ │ │ └── turnstileLoaded.ctx.svelte.ts │ │ │ │ ├── common.ts │ │ │ │ └── server │ │ │ │ └── index.ts │ │ ├── components │ │ │ ├── Admonition.svelte │ │ │ ├── CodeInput.svelte │ │ │ ├── LoadingDots.svelte │ │ │ ├── LogoIcon.svelte │ │ │ ├── LogoLink.svelte │ │ │ ├── Portal.svelte │ │ │ ├── SEO.svelte │ │ │ ├── ScrollIndicator.svelte │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── FileInput.svelte │ │ │ │ ├── InputMessage.svelte │ │ │ │ ├── Select.svelte │ │ │ │ ├── Switch.svelte │ │ │ │ └── index.ts │ │ │ ├── layout │ │ │ │ ├── AccountLayout.svelte │ │ │ │ ├── Header.svelte │ │ │ │ ├── Notice.svelte │ │ │ │ ├── index.ts │ │ │ │ └── nav │ │ │ │ │ ├── DesktopNavItem.svelte │ │ │ │ │ ├── MobileNav.svelte │ │ │ │ │ ├── MobileNavToggler.svelte │ │ │ │ │ ├── MobileRoute.svelte │ │ │ │ │ ├── SidebarRoute.svelte │ │ │ │ │ ├── mobileNav.ctx.svelte.ts │ │ │ │ │ ├── mobileNavController.svelte.ts │ │ │ │ │ └── routes.ts │ │ │ └── user │ │ │ │ ├── Avatar.svelte │ │ │ │ └── index.ts │ │ ├── consts.ts │ │ ├── db │ │ │ └── server │ │ │ │ ├── connect.ts │ │ │ │ ├── index.ts │ │ │ │ ├── repository │ │ │ │ ├── authAdapter.ts │ │ │ │ ├── index.ts │ │ │ │ ├── meta.ts │ │ │ │ ├── presigned.ts │ │ │ │ ├── types.ts │ │ │ │ └── user.ts │ │ │ │ ├── schema │ │ │ │ ├── DB.d.ts │ │ │ │ ├── index.ts │ │ │ │ ├── presignedUrls.ts │ │ │ │ └── user-and-auth │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── provider.ts │ │ │ │ │ ├── session.ts │ │ │ │ │ ├── tokens.ts │ │ │ │ │ ├── user.ts │ │ │ │ │ └── utils.ts │ │ │ │ └── singleton.ts │ │ ├── device-info │ │ │ ├── index.ts │ │ │ ├── platform.ts │ │ │ └── types.ts │ │ ├── http │ │ │ ├── client.svelte.ts │ │ │ ├── common.ts │ │ │ └── server │ │ │ │ ├── checkedRedirect.ts │ │ │ │ ├── index.ts │ │ │ │ ├── json.ts │ │ │ │ └── sanitize.ts │ │ ├── icons │ │ │ ├── GitHub.svelte │ │ │ ├── Google.svelte │ │ │ ├── Icon.svelte │ │ │ ├── Svelte.svelte │ │ │ └── index.ts │ │ ├── image │ │ │ ├── client │ │ │ │ ├── index.ts │ │ │ │ └── utils.ts │ │ │ ├── common │ │ │ │ ├── index.ts │ │ │ │ └── schemas.ts │ │ │ └── components │ │ │ │ ├── ImageCardBtns.svelte │ │ │ │ ├── ImageCardOverlays.svelte │ │ │ │ ├── ImageCrop.svelte │ │ │ │ ├── UploadProgress.svelte │ │ │ │ └── index.ts │ │ ├── initClient.ts │ │ ├── initServer.ts │ │ ├── kv │ │ │ └── server │ │ │ │ ├── index.ts │ │ │ │ └── redis.ts │ │ ├── logging │ │ │ ├── client │ │ │ │ ├── index.ts │ │ │ │ ├── logflare.ts │ │ │ │ ├── logger.ts │ │ │ │ └── sentry.ts │ │ │ ├── common │ │ │ │ ├── logflare.ts │ │ │ │ ├── logger.ts │ │ │ │ ├── pretty.ts │ │ │ │ └── types.ts │ │ │ └── server │ │ │ │ ├── index.ts │ │ │ │ ├── logflare.ts │ │ │ │ ├── logger.ts │ │ │ │ ├── pretty.ts │ │ │ │ └── sentry.ts │ │ ├── object-storage │ │ │ ├── client │ │ │ │ ├── cropImgUploadController.svelte.ts │ │ │ │ ├── index.ts │ │ │ │ ├── repository.ts │ │ │ │ ├── types.ts │ │ │ │ └── uploadToCloudStorage.ts │ │ │ └── server │ │ │ │ ├── cloudfront.ts │ │ │ │ ├── index.ts │ │ │ │ ├── rekognition.ts │ │ │ │ ├── repository.ts │ │ │ │ ├── s3.ts │ │ │ │ ├── s3CloudfrontKeyController.ts │ │ │ │ ├── types.ts │ │ │ │ └── unsavedUploadsCleaner.ts │ │ ├── rate-limit │ │ │ └── server.ts │ │ ├── shiki │ │ │ └── index.js │ │ ├── shop │ │ │ ├── api │ │ │ │ ├── connectedOrExit.server.ts │ │ │ │ ├── index.ts │ │ │ │ ├── shopify │ │ │ │ │ ├── connectedOrExit.server.ts │ │ │ │ │ ├── gql │ │ │ │ │ │ ├── cart │ │ │ │ │ │ │ ├── fragments.ts │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── mutations.ts │ │ │ │ │ │ │ └── queries.ts │ │ │ │ │ │ ├── collection │ │ │ │ │ │ │ ├── fragments.ts │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── queries.ts │ │ │ │ │ │ ├── fragments.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── menu │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── queries.ts │ │ │ │ │ │ ├── page │ │ │ │ │ │ │ ├── fragments.ts │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── queries.ts │ │ │ │ │ │ ├── product │ │ │ │ │ │ │ ├── fragments.ts │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── queries.ts │ │ │ │ │ │ └── shop │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── queries.ts │ │ │ │ │ ├── handlers │ │ │ │ │ │ ├── cart.ts │ │ │ │ │ │ ├── collection.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── menu.ts │ │ │ │ │ │ ├── page.ts │ │ │ │ │ │ └── product.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── repository.ts │ │ │ │ │ └── storefront.ts │ │ │ │ └── types │ │ │ │ │ ├── handlers.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── models.ts │ │ │ │ │ └── repository.ts │ │ │ ├── index.ts │ │ │ ├── searchAndFilter.ts │ │ │ └── utils.ts │ │ ├── styles │ │ │ ├── components │ │ │ │ ├── ThemeDemo.svelte │ │ │ │ ├── ThemePicker.svelte │ │ │ │ ├── ThemeSwitchDayNight.svelte │ │ │ │ ├── ThemeSwitchDayNightSystem.svelte │ │ │ │ ├── ThemeToggler.svelte │ │ │ │ └── index.ts │ │ │ ├── css │ │ │ │ ├── code.css │ │ │ │ ├── themes.css │ │ │ │ └── tokens.css │ │ │ ├── index.ts │ │ │ ├── postcss │ │ │ │ ├── components │ │ │ │ │ ├── btn.css │ │ │ │ │ ├── input.css │ │ │ │ │ ├── link.css │ │ │ │ │ ├── modal.css │ │ │ │ │ ├── radio.css │ │ │ │ │ ├── swap-flip.css │ │ │ │ │ └── typography.css │ │ │ │ ├── cssAsPlugin.ts │ │ │ │ └── utilities │ │ │ │ │ ├── underline.css │ │ │ │ │ └── utils.css │ │ │ ├── storeTheme.ts │ │ │ ├── tailwindConfig.ts │ │ │ ├── theme.ctx.svelte.ts │ │ │ ├── themeController.svelte.ts │ │ │ └── themeUtils.ts │ │ ├── superforms │ │ │ ├── client.ts │ │ │ ├── common.ts │ │ │ └── server.ts │ │ ├── svelte-actions │ │ │ ├── clickoutside.ts │ │ │ ├── index.ts │ │ │ ├── keyboard.ts │ │ │ ├── minMaxVal.ts │ │ │ ├── trapFocus.ts │ │ │ └── windowEscape.ts │ │ ├── svelte-runes │ │ │ ├── primitives.svelte.ts │ │ │ └── storage.svelte.ts │ │ ├── svelte-stores │ │ │ ├── accordionSlider.svelte.ts │ │ │ ├── activeIndex.svelte.ts │ │ │ ├── index.ts │ │ │ ├── params │ │ │ │ ├── index.ts │ │ │ │ ├── params.ts │ │ │ │ ├── paramsGeneric.ts │ │ │ │ └── searchParams.ts │ │ │ ├── pausableTweened.svelte.ts │ │ │ └── tempValue.svelte.ts │ │ ├── transport │ │ │ └── server │ │ │ │ ├── consts.ts │ │ │ │ ├── email │ │ │ │ ├── console.ts │ │ │ │ ├── index.ts │ │ │ │ ├── ses.ts │ │ │ │ └── types.ts │ │ │ │ ├── index.ts │ │ │ │ ├── repository │ │ │ │ ├── email.ts │ │ │ │ ├── index.ts │ │ │ │ ├── sms.ts │ │ │ │ └── types.ts │ │ │ │ └── sms │ │ │ │ ├── console.ts │ │ │ │ ├── index.ts │ │ │ │ ├── twilio.ts │ │ │ │ └── types.ts │ │ └── utils │ │ │ ├── client │ │ │ ├── defineCtx.ts │ │ │ └── index.ts │ │ │ ├── common │ │ │ ├── index.ts │ │ │ ├── language.ts │ │ │ └── types │ │ │ │ ├── index.ts │ │ │ │ └── result.ts │ │ │ └── server │ │ │ └── index.ts │ ├── params │ │ ├── mfaKinds.ts │ │ └── oauthProviders.ts │ └── routes │ │ ├── (api) │ │ ├── .well-known │ │ │ └── security.txt │ │ │ │ └── +server.ts │ │ ├── robots.txt │ │ │ └── +server.ts │ │ ├── security-policy │ │ │ └── +page.svelte │ │ └── sitemap.xml │ │ │ └── +server.ts │ │ ├── (auth) │ │ ├── (login-signup) │ │ │ ├── +layout.svelte │ │ │ ├── +layout.ts │ │ │ ├── components │ │ │ │ ├── DotPattern.svelte │ │ │ │ ├── GoogleFormButton.svelte │ │ │ │ ├── Or.svelte │ │ │ │ └── index.ts │ │ │ ├── login │ │ │ │ ├── +page.server.ts │ │ │ │ ├── +page.svelte │ │ │ │ ├── oauth-disabled │ │ │ │ │ └── [[provider=oauthProviders]] │ │ │ │ │ │ ├── +page.server.ts │ │ │ │ │ │ └── +page@.svelte │ │ │ │ └── verify-mfa │ │ │ │ │ ├── +page.server.ts │ │ │ │ │ ├── +page@.svelte │ │ │ │ │ └── passkey.json │ │ │ │ │ ├── +server.ts │ │ │ │ │ ├── client.ts │ │ │ │ │ └── common.ts │ │ │ ├── oauth │ │ │ │ └── google │ │ │ │ │ ├── callback.json │ │ │ │ │ └── +server.ts │ │ │ │ │ ├── consts.ts │ │ │ │ │ └── send-to-oauth.json │ │ │ │ │ └── +server.ts │ │ │ ├── signin │ │ │ │ └── +page.server.ts │ │ │ ├── signup │ │ │ │ ├── +page.server.ts │ │ │ │ ├── +page.svelte │ │ │ │ └── email-verification │ │ │ │ │ ├── +page.server.ts │ │ │ │ │ ├── +page@.svelte │ │ │ │ │ └── [token].json │ │ │ │ │ └── +server.ts │ │ │ └── turnstile.ctx.svelte.ts │ │ ├── +layout.server.ts │ │ ├── account │ │ │ ├── delete │ │ │ │ ├── +page.server.ts │ │ │ │ └── +page.svelte │ │ │ ├── security │ │ │ │ ├── auth │ │ │ │ │ ├── +layout.svelte │ │ │ │ │ ├── +page.server.ts │ │ │ │ │ ├── +page.svelte │ │ │ │ │ └── UpdatePassForm.svelte │ │ │ │ └── devices │ │ │ │ │ ├── +layout.svelte │ │ │ │ │ ├── +page.server.ts │ │ │ │ │ └── +page.svelte │ │ │ └── verify │ │ │ │ ├── +page.server.ts │ │ │ │ ├── dtos.common.ts │ │ │ │ ├── email │ │ │ │ ├── +page.server.ts │ │ │ │ └── [token].json │ │ │ │ │ └── +server.ts │ │ │ │ ├── handlers.client.ts │ │ │ │ ├── handlers.server.ts │ │ │ │ └── passkey.json │ │ │ │ └── +server.ts │ │ ├── actionsMap.ts │ │ ├── api │ │ │ ├── expire-tokens.json │ │ │ │ └── +server.ts │ │ │ ├── guard.ts │ │ │ └── reset-db.json │ │ │ │ └── +server.ts │ │ ├── change-to-google │ │ │ ├── +page.server.ts │ │ │ ├── +page.svelte │ │ │ ├── callback.json │ │ │ │ └── +server.ts │ │ │ └── consts.ts │ │ ├── change-to-password │ │ │ ├── +page.server.ts │ │ │ └── +page.svelte │ │ ├── components │ │ │ ├── LoginAsDiffUserForm.svelte │ │ │ ├── MFASelector.svelte │ │ │ ├── PassInput.svelte │ │ │ ├── PasskeyChallengeScript.svelte │ │ │ ├── PhoneInput.svelte │ │ │ ├── Robot.svelte │ │ │ ├── SendSMSTokenForm.svelte │ │ │ ├── Verifier.svelte │ │ │ ├── VerifyCodeForm.svelte │ │ │ ├── VerifyEmailForm.svelte │ │ │ ├── VerifyMFAForm.svelte │ │ │ ├── VerifyPWForm.svelte │ │ │ └── index.ts │ │ ├── hooks.server.ts │ │ ├── index.ts │ │ ├── invalid-token │ │ │ └── +page.svelte │ │ ├── logout │ │ │ └── +page.server.ts │ │ ├── mfa │ │ │ ├── passkey │ │ │ │ └── get-auth-options.json │ │ │ │ │ ├── +server.ts │ │ │ │ │ ├── client.ts │ │ │ │ │ └── common.ts │ │ │ ├── sms │ │ │ │ └── +page.server.ts │ │ │ └── update │ │ │ │ ├── +layout.server.ts │ │ │ │ ├── +layout.svelte │ │ │ │ ├── +page.svelte │ │ │ │ ├── register │ │ │ │ ├── +layout.ts │ │ │ │ ├── authenticator │ │ │ │ │ ├── +page.server.ts │ │ │ │ │ └── +page.svelte │ │ │ │ ├── passkeys.json │ │ │ │ │ ├── +server.ts │ │ │ │ │ ├── client.ts │ │ │ │ │ └── common.ts │ │ │ │ ├── passkeys │ │ │ │ │ ├── +page.server.ts │ │ │ │ │ └── +page.svelte │ │ │ │ └── sms │ │ │ │ │ ├── +page.server.ts │ │ │ │ │ └── +page.svelte │ │ │ │ ├── remove │ │ │ │ └── [mfaKind=mfaKinds] │ │ │ │ │ ├── +page.server.ts │ │ │ │ │ └── +page.svelte │ │ │ │ └── utils.ts │ │ ├── password-update │ │ │ └── [token] │ │ │ │ ├── +page.server.ts │ │ │ │ └── +page.svelte │ │ └── schemas.ts │ │ ├── +error.svelte │ │ ├── +layout.server.ts │ │ ├── +layout.svelte │ │ ├── +layout.ts │ │ ├── +page.svelte │ │ ├── +page.ts │ │ ├── account │ │ ├── +layout.server.ts │ │ ├── +layout.svelte │ │ ├── hooks.server.ts │ │ └── profile │ │ │ ├── +page.server.ts │ │ │ ├── +page.svelte │ │ │ ├── AvatarEditor.svelte │ │ │ ├── ConfirmDelAvatarModal.svelte │ │ │ ├── EditableAvatar.svelte │ │ │ ├── EditableName.svelte │ │ │ ├── ProfileCard.svelte │ │ │ └── avatar │ │ │ ├── crop.json │ │ │ ├── +server.ts │ │ │ ├── client.ts │ │ │ └── common.ts │ │ │ └── upload.json │ │ │ ├── +server.ts │ │ │ ├── client.ts │ │ │ └── common.ts │ │ ├── appearance │ │ ├── +layout.svelte │ │ └── +page.svelte │ │ ├── articles │ │ ├── +layout.server.ts │ │ ├── +layout.svelte │ │ ├── +layout.ts │ │ ├── generic-url-state-controller │ │ │ ├── +page.server.ts │ │ │ ├── +page.svelte │ │ │ ├── FeatureCard.svelte │ │ │ ├── assets │ │ │ │ ├── generic-url-state-controller-thumbnail-1200w.webp │ │ │ │ └── generic-url-state-controller-thumbnail.png │ │ │ ├── demos │ │ │ │ └── main │ │ │ │ │ ├── Demo.svelte │ │ │ │ │ ├── Search.svelte │ │ │ │ │ └── meta.preview.ts │ │ │ └── generated │ │ │ │ └── metadata.ts │ │ ├── image-uploader │ │ │ ├── +page.server.ts │ │ │ ├── +page.svelte │ │ │ ├── FeatureCard.svelte │ │ │ ├── assets │ │ │ │ ├── 2024-08-05_21-17-07_1410x1440_30fps.mp4 │ │ │ │ ├── 2024-08-05_21-17-07_465x474_24fps.mp4 │ │ │ │ ├── image-uploader-flow-q30.webp │ │ │ │ ├── image-uploader-flow.jpg │ │ │ │ ├── image-uploader-thumbnail-1200w.webp │ │ │ │ ├── image-uploader-thumbnail.png │ │ │ │ ├── owl-400w.jpg │ │ │ │ ├── owl-400w.webp │ │ │ │ ├── owl.jpg │ │ │ │ ├── smoky-400w.jpg │ │ │ │ ├── smoky-400w.webp │ │ │ │ └── smoky.jpg │ │ │ ├── demos │ │ │ │ └── main │ │ │ │ │ ├── Demo.svelte │ │ │ │ │ ├── Mock.svelte │ │ │ │ │ └── meta.preview.ts │ │ │ └── generated │ │ │ │ └── metadata.ts │ │ ├── preprocessors │ │ │ ├── +page.server.ts │ │ │ ├── +page.svelte │ │ │ ├── FeatureCard.svelte │ │ │ ├── assets │ │ │ │ ├── 2024-08-05_22-41-28_1334x941.mp4 │ │ │ │ ├── 2024-08-05_22-41-28_1778x1254.mp4 │ │ │ │ ├── 2024-08-05_22-41-28_889x627.mp4 │ │ │ │ ├── preprocessors-thumbnail-1200w.webp │ │ │ │ └── preprocessors-thumbnail.png │ │ │ ├── demos │ │ │ │ └── main │ │ │ │ │ ├── Code.svelte │ │ │ │ │ ├── Layout.svelte │ │ │ │ │ ├── Markdown.svelte │ │ │ │ │ ├── Math.svelte │ │ │ │ │ ├── exampleSvelteConfig.txt │ │ │ │ │ └── meta.preview.ts │ │ │ └── generated │ │ │ │ └── metadata.ts │ │ ├── simple-url-state-controller │ │ │ ├── +page.svelte │ │ │ ├── FeatureCard.svelte │ │ │ ├── assets │ │ │ │ ├── simple-url-state-controller-q30.webp │ │ │ │ ├── simple-url-state-controller-thumbnail-1200w.webp │ │ │ │ ├── simple-url-state-controller-thumbnail.png │ │ │ │ └── simple-url-state-controller.png │ │ │ ├── demos │ │ │ │ └── main │ │ │ │ │ ├── Demo.svelte │ │ │ │ │ ├── FilterInputs.svelte │ │ │ │ │ ├── Posts.svelte │ │ │ │ │ ├── demoData.ts │ │ │ │ │ └── meta.preview.ts │ │ │ └── generated │ │ │ │ └── metadata.ts │ │ ├── theme-controller │ │ │ ├── +page.svelte │ │ │ ├── FeatureCard.svelte │ │ │ ├── assets │ │ │ │ ├── 2024-08-05_19-56-30_1132x912.mp4 │ │ │ │ ├── 2024-08-05_19-56-30_2264x1824.mp4 │ │ │ │ ├── 2024-08-05_19-56-30_800x645.mp4 │ │ │ │ ├── theme-controller-q30.webp │ │ │ │ ├── theme-controller-thumbnail-1200w.webp │ │ │ │ ├── theme-controller-thumbnail.png │ │ │ │ └── theme-controller.png │ │ │ ├── demos │ │ │ │ └── main │ │ │ │ │ └── meta.preview.ts │ │ │ └── generated │ │ │ │ └── metadata.ts │ │ ├── typesafe-fetch-handler │ │ │ ├── +page.server.ts │ │ │ ├── +page.svelte │ │ │ ├── FeatureCard.svelte │ │ │ ├── assets │ │ │ │ ├── typesafe-fetch-handler-q30.webp │ │ │ │ ├── typesafe-fetch-handler-thumbnail-1200w.webp │ │ │ │ ├── typesafe-fetch-handler-thumbnail.png │ │ │ │ └── typesafe-fetch-handler.png │ │ │ ├── demos │ │ │ │ └── main │ │ │ │ │ ├── FetchBasic.svelte │ │ │ │ │ ├── FetchDetailed.svelte │ │ │ │ │ ├── LangSelect.svelte │ │ │ │ │ ├── demo.json │ │ │ │ │ ├── +server.ts │ │ │ │ │ ├── client.ts │ │ │ │ │ └── common.ts │ │ │ │ │ ├── lang.service.common.ts │ │ │ │ │ ├── lang.service.server.ts │ │ │ │ │ └── meta.preview.ts │ │ │ └── generated │ │ │ │ └── metadata.ts │ │ └── update-loaded-front-matter.json │ │ │ ├── +server.ts │ │ │ ├── client.ts │ │ │ └── common.ts │ │ ├── deployment-access │ │ ├── +page.server.ts │ │ ├── +page.svelte │ │ ├── actionsMap.ts │ │ ├── hooks.server.ts │ │ ├── repository.ts │ │ └── types.ts │ │ ├── home │ │ ├── Slider.svelte │ │ ├── assets │ │ │ ├── auth-1420w.jpeg │ │ │ ├── auth-1420w.webp │ │ │ ├── auth-850w.jpeg │ │ │ ├── auth-850w.webp │ │ │ ├── auth.png │ │ │ ├── aws-1420w.jpeg │ │ │ ├── aws-1420w.webp │ │ │ ├── aws-850w.jpeg │ │ │ ├── aws-850w.webp │ │ │ ├── aws.png │ │ │ ├── e-commerce-1420w.jpeg │ │ │ ├── e-commerce-1420w.webp │ │ │ ├── e-commerce-850w.jpeg │ │ │ ├── e-commerce-850w.webp │ │ │ ├── e-commerce.png │ │ │ ├── self-hosting-1420w.jpeg │ │ │ ├── self-hosting-1420w.webp │ │ │ ├── self-hosting-850w.jpeg │ │ │ ├── self-hosting-850w.webp │ │ │ └── self-hosting.png │ │ └── pp-demo │ │ │ ├── After.pp.svelte │ │ │ ├── Before.svelte │ │ │ ├── MockupPhone.svelte │ │ │ ├── MockupVSCode.svelte │ │ │ └── PreprocessorDemo.pp.svelte │ │ └── shop │ │ ├── +error.svelte │ │ ├── +layout.svelte │ │ ├── +layout.ts │ │ ├── +page.svelte │ │ ├── +page.ts │ │ ├── cart.ctx.svelte.ts │ │ ├── cart.json │ │ ├── +server.ts │ │ ├── client.ts │ │ └── common.ts │ │ ├── collections │ │ ├── +page.svelte │ │ └── [collectionHandle] │ │ │ ├── +layout.svelte │ │ │ ├── +page.svelte │ │ │ └── +page.ts │ │ ├── components │ │ ├── cart │ │ │ ├── AddToCartBtn.svelte │ │ │ ├── Cart.svelte │ │ │ ├── EditItemQuantityBtn.svelte │ │ │ ├── OpenCartBtn.svelte │ │ │ └── RemoveFromCartBtn.svelte │ │ ├── home │ │ │ ├── Carousel.svelte │ │ │ ├── ThreeItemGrid.svelte │ │ │ └── ThreeItemGridItem.svelte │ │ ├── index.ts │ │ ├── layout │ │ │ ├── DesktopNav.svelte │ │ │ ├── MobileNav.svelte │ │ │ └── Sidebar.svelte │ │ ├── product │ │ │ ├── Gallery.svelte │ │ │ ├── ProductDescription.svelte │ │ │ ├── ProductVariant.svelte │ │ │ ├── RelatedProducts.svelte │ │ │ └── VariantSelector.svelte │ │ ├── search-and-filter │ │ │ ├── Available.svelte │ │ │ ├── NoResults.svelte │ │ │ ├── Price.svelte │ │ │ ├── SearchBar.svelte │ │ │ └── SortBy.svelte │ │ └── shared │ │ │ ├── GridTileImage.svelte │ │ │ └── Label.svelte │ │ ├── navAndDrawer.ctx.ts │ │ ├── product │ │ └── [handle] │ │ │ ├── +page.svelte │ │ │ └── +page.ts │ │ └── searchAndFilter.ctx.ts │ ├── static │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── browserconfig.xml │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── fonts │ │ ├── Alegreya │ │ │ ├── Alegreya-Italic-VariableFont_wght.ttf │ │ │ ├── Alegreya-VariableFont_wght.ttf │ │ │ ├── OFL.txt │ │ │ ├── README.txt │ │ │ └── static │ │ │ │ ├── Alegreya-Black.ttf │ │ │ │ ├── Alegreya-BlackItalic.ttf │ │ │ │ ├── Alegreya-Bold.ttf │ │ │ │ ├── Alegreya-BoldItalic.ttf │ │ │ │ ├── Alegreya-ExtraBold.ttf │ │ │ │ ├── Alegreya-ExtraBoldItalic.ttf │ │ │ │ ├── Alegreya-Italic.ttf │ │ │ │ ├── Alegreya-Medium.ttf │ │ │ │ ├── Alegreya-MediumItalic.ttf │ │ │ │ ├── Alegreya-Regular.ttf │ │ │ │ ├── Alegreya-SemiBold.ttf │ │ │ │ └── Alegreya-SemiBoldItalic.ttf │ │ ├── Nunito │ │ │ ├── Nunito-Italic-VariableFont_wght.ttf │ │ │ ├── Nunito-VariableFont_wght.ttf │ │ │ ├── OFL.txt │ │ │ └── README.txt │ │ └── UbuntuMonoLigaturized │ │ │ └── UbuntuMonoLigaturized-Regular.ttf │ ├── mstile-144x144.png │ ├── mstile-150x150.png │ ├── mstile-310x150.png │ ├── mstile-310x310.png │ ├── mstile-70x70.png │ ├── safari-pinned-tab.svg │ ├── samplekit-logo-128x128.png │ ├── samplekit-logo.svg │ ├── site.webmanifest │ └── themeUtils.js │ ├── svelte.config.js │ ├── tailwind.config.ts │ ├── tsconfig.json │ └── vite.config.ts └── tsconfig.base.json /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@3.0.2/schema.json", 3 | "changelog": ["@svitejs/changesets-changelog-github-compact", { "repo": "timothycohen/samplekit" }], 4 | "commit": false, 5 | "access": "public", 6 | "bumpVersionsWithWorkspaceProtocolOnly": true, 7 | "baseBranch": "main", 8 | "updateInternalDependencies": "patch", 9 | "fixed": [], 10 | "linked": [], 11 | "privatePackages": { 12 | "version": true, 13 | "tag": true 14 | }, 15 | "changedFilePatterns": ["**"], 16 | "snapshot": {}, 17 | "ignore": [] 18 | } 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yaml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## What problem does this PR solve? 2 | 3 | ## PR Checklist: 4 | 5 | - [ ] The PR body illustrates what problems are being solved. 6 | 7 | - [ ] `pnpm validate` has been run inside the changed workspace project(s). 8 | (You will need to run `pnpm build:dependencies && pnpm sync` first for sites.) 9 | 10 | - [ ] No NPM packages / VS Code extensions have changed, or if they have, a changeset has been created. 11 | (Create changesets by running `pnpm changeset` and following the instructions. If unsure, please make a note in the PR description.) 12 | 13 | - [ ] 'Allow edits from maintainers.' is checked or the PR branch is not a fork. 14 | -------------------------------------------------------------------------------- /.github/workflows/validate.yaml: -------------------------------------------------------------------------------- 1 | name: Validate 2 | 3 | on: 4 | pull_request: 5 | branches: [main] 6 | 7 | jobs: 8 | validate: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout Repo 12 | uses: actions/checkout@v4 13 | 14 | - name: Install pnpm 15 | uses: pnpm/action-setup@v4.0.0 16 | 17 | - name: Setup Node.js 18 | uses: actions/setup-node@v4 19 | with: 20 | node-version: '20.14' 21 | cache: pnpm 22 | 23 | - name: Install Dependencies 24 | run: pnpm install --frozen-lockfile 25 | 26 | - name: Build 27 | run: pnpm build:packages && grep -o '^[^#]\+=' sites/samplekit.dev/.env.example > sites/samplekit.dev/.env && pnpm sync 28 | 29 | - name: Validate 30 | run: pnpm validate 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .env 3 | .env.* 4 | !.env.example 5 | node_modules 6 | package-lock.json 7 | yarn.lock 8 | build 9 | dist 10 | .svelte-kit 11 | .vercel 12 | .cloudflare 13 | .netlify 14 | vite.config.js.timestamp-* 15 | vite.config.ts.timestamp-* 16 | logs 17 | wip 18 | .sentryclirc 19 | *.secret 20 | *.vsix 21 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | resolution-mode=highest 3 | link-workspace-packages=true 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .env 2 | .env.* 3 | !.env.example 4 | pnpm-lock.yaml 5 | node_modules 6 | dist 7 | build 8 | .svelte-kit 9 | generated 10 | static 11 | vite.config.js.timestamp-* 12 | vite.config.ts.timestamp-* 13 | -------------------------------------------------------------------------------- /Dockerfile.pp-docs.gh: -------------------------------------------------------------------------------- 1 | FROM node:20.14-alpine AS build 2 | WORKDIR /usr/src/app 3 | 4 | COPY . /usr/src/app 5 | 6 | RUN corepack enable pnpm 7 | RUN pnpm install 8 | RUN pnpm --filter preprocessor-docs build 9 | 10 | FROM nginx:alpine 11 | RUN rm -rf /usr/share/nginx/html/* 12 | COPY --from=build /usr/src/app/sites/preprocessor-docs/build /usr/share/nginx/html 13 | RUN chown -R nginx:nginx /usr/share/nginx/html && chmod -R 755 /usr/share/nginx/html 14 | COPY --from=build /usr/src/app/sites/preprocessor-docs/nginx-default.conf /etc/nginx/conf.d/default.conf 15 | -------------------------------------------------------------------------------- /Dockerfile.samplekit.preview.dockerignore: -------------------------------------------------------------------------------- 1 | **node_modules 2 | .env* 3 | !.env.preview 4 | -------------------------------------------------------------------------------- /caprover-apps/cron/Dockerfile: -------------------------------------------------------------------------------- 1 | ##################### 🚧 WARNING: ENV VARS ARE BAKED INTO THE IMAGE 🚧 ##################### 2 | # Do not make resulting images public. 3 | # 4 | # The env vars are encoded directly into the bin scripts inside the image. Deploy directly on server. 5 | # 6 | # Do not make resulting images public. 7 | ##################### 🚧 WARNING: ENV VARS ARE BAKED INTO THE IMAGE 🚧 ##################### 8 | 9 | FROM alpine:latest 10 | ARG TZ=America/New_York 11 | RUN apk add --no-cache tzdata curl && \ 12 | cp /usr/share/zoneinfo/$TZ /etc/localtime && \ 13 | echo $TZ > /etc/timezone 14 | 15 | COPY bin/reset_db.sh.secret /usr/local/bin/reset_db.sh 16 | RUN chmod +x /usr/local/bin/reset_db.sh 17 | 18 | RUN echo "0 2 * * * /usr/local/bin/reset_db.sh /proc/1/fd/1 2>/proc/1/fd/2" >> /var/spool/cron/crontabs/root 19 | 20 | CMD ["crond", "-f"] 21 | -------------------------------------------------------------------------------- /caprover-apps/cron/bin/reset_db.sh.example: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | /usr/bin/env curl -X POST \ 4 | -H "Content-Type: application/json" \ 5 | -H "x-real-ip: $(hostname -i)" \ 6 | -H "x-deployment-access: $X_DEPLOYMENT_ACCESS" \ 7 | -d "{\"cron_api_key\": \"$RESET_DB_DELETE_EXPIRED_TOKENS_KEY\", \"expected_db_name\": \"$RESET_DB_EXPECTED_DB_NAME\"}" \ 8 | "http://srv-captain--$RESET_DB_APP_NAME:3000/api/reset-db.json" 9 | -------------------------------------------------------------------------------- /caprover-apps/cron/captain-definition: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "dockerfilePath": "./Dockerfile" 4 | } 5 | -------------------------------------------------------------------------------- /caprover-apps/cron/readme.md: -------------------------------------------------------------------------------- 1 | ### This app runs cron jobs. 2 | 3 | #### Cron jobs 4 | 5 | - Curl api/reset-db.json at 2am every day 6 | -------------------------------------------------------------------------------- /caprover-apps/log-stream/readme.md: -------------------------------------------------------------------------------- 1 | ### This app provides a UI to monitor the logs of containers on the docker network. 2 | 3 | It is also available through the CapRover GUI as a one-click app (Dozzle). 4 | -------------------------------------------------------------------------------- /caprover-apps/pg/readme.md: -------------------------------------------------------------------------------- 1 | ### This app provides a postgres DB available on the docker network. 2 | 3 | It is also available through the CapRover GUI as a one-click app (PostgreSQL). 4 | -------------------------------------------------------------------------------- /caprover-apps/redis/readme.md: -------------------------------------------------------------------------------- 1 | ### This app provides a redis server available on the docker network. 2 | 3 | It is also available through the CapRover GUI as a one-click app (Redis). 4 | -------------------------------------------------------------------------------- /packages/auth/prettier.config.js: -------------------------------------------------------------------------------- 1 | import baseConfig from '../../prettier.config.js'; 2 | 3 | /** 4 | * @type {import("prettier").Config} 5 | */ 6 | const config = { 7 | ...baseConfig, 8 | }; 9 | 10 | export default config; 11 | -------------------------------------------------------------------------------- /packages/auth/src/client/auth.ts: -------------------------------------------------------------------------------- 1 | import { passkey } from './passkey/handlers.js'; 2 | import type { ClientAuth } from '../types/client.js'; 3 | 4 | export const auth: ClientAuth = { 5 | passkey, 6 | }; 7 | -------------------------------------------------------------------------------- /packages/auth/src/client/index.ts: -------------------------------------------------------------------------------- 1 | export { auth } from './auth.js'; 2 | export type { ClientAuth } from '../types/client.js'; 3 | -------------------------------------------------------------------------------- /packages/auth/src/common/index.ts: -------------------------------------------------------------------------------- 1 | export type { 2 | AuthenticationResponseJSON, 3 | PublicKeyCredentialCreationOptionsJSON, 4 | PublicKeyCredentialRequestOptionsJSON, 5 | RegistrationResponseJSON, 6 | } from '../types/common.js'; 7 | 8 | export { authenticationResponseJSONSchema, registrationResponseJSONSchema } from './schema.js'; 9 | -------------------------------------------------------------------------------- /packages/auth/src/server/index.ts: -------------------------------------------------------------------------------- 1 | export { createAuth } from './auth.js'; 2 | export { createConfig } from './config.js'; 3 | 4 | export type { 5 | DbAdapter, 6 | DbAdapterProvider, 7 | DbAdapterSession, 8 | DbAdapterToken, 9 | DbAdapterUser, 10 | DbAdapterUserAndSession, 11 | ProviderFromAuth, 12 | ProviderToAuth, 13 | SessionFromAuth, 14 | SessionToAuth, 15 | UpdateExpires, 16 | UserFromAuth, 17 | UserToAuth, 18 | } from '../types/server/adapters.js'; 19 | export type { Auth, AuthenticatorDevice } from '../types/server/auth.js'; 20 | export type { Config, DefaultConfig, RequiredConfig } from '../types/server/config.js'; 21 | export type { Cookies } from '../types/server/cookie.js'; 22 | export type { TokenErr } from '../types/server/errors.js'; 23 | export type { MiddlewareContext, ISessionHandler, ServerAuth } from '../types/server/repository/index.js'; 24 | -------------------------------------------------------------------------------- /packages/auth/src/server/token/consts.ts: -------------------------------------------------------------------------------- 1 | export const tokenKinds = [ 2 | 'passkey_challenge', 3 | 'setup_authenticator', 4 | 'email_veri', 5 | 'pw_reset', 6 | 'sms_veri', 7 | 'setup_sms_veri', 8 | ] as const; 9 | -------------------------------------------------------------------------------- /packages/auth/src/types/client.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | AuthenticationResponseJSON, 3 | PublicKeyCredentialCreationOptionsJSON, 4 | PublicKeyCredentialRequestOptionsJSON, 5 | RegistrationResponseJSON, 6 | } from './common.js'; 7 | 8 | type StartPasskeyReg = ( 9 | optionsJSON: PublicKeyCredentialCreationOptionsJSON, 10 | ) => Promise<{ data: RegistrationResponseJSON; error?: never } | { data?: never; error: Error }>; 11 | 12 | type StartPasskeyAuth = ( 13 | optionsJSON: PublicKeyCredentialRequestOptionsJSON, 14 | ) => Promise<{ data: AuthenticationResponseJSON; error?: never } | { data?: never; error: Error }>; 15 | 16 | export interface ClientAuth { 17 | passkey: { 18 | startReg: StartPasskeyReg; 19 | startAuth: StartPasskeyAuth; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /packages/auth/src/types/common.ts: -------------------------------------------------------------------------------- 1 | export type { 2 | AuthenticationResponseJSON, 3 | PublicKeyCredentialCreationOptionsJSON, 4 | PublicKeyCredentialRequestOptionsJSON, 5 | RegistrationResponseJSON, 6 | } from '@simplewebauthn/types'; 7 | -------------------------------------------------------------------------------- /packages/auth/src/types/server/errors.ts: -------------------------------------------------------------------------------- 1 | export declare namespace TokenErr { 2 | export type Val = 'invalid_token' | 'expired_token'; 3 | export type LimAtt = 'max_attempts'; 4 | export type LimSend = 'send_max' | 'send_timeout'; 5 | export type All = Val | LimAtt | LimSend; 6 | } 7 | -------------------------------------------------------------------------------- /packages/auth/src/types/server/index.ts: -------------------------------------------------------------------------------- 1 | export type * from './adapters.js'; 2 | export type * from './auth.js'; 3 | export type * from './config.js'; 4 | export type * from './cookie.js'; 5 | export type * from './errors.js'; 6 | export type * from './repository/index.js'; 7 | -------------------------------------------------------------------------------- /packages/auth/src/types/server/repository/sessionHandler.ts: -------------------------------------------------------------------------------- 1 | import type { Auth } from '../auth.js'; 2 | 3 | export type MiddlewareContext = { 4 | requestCookie: string | null; 5 | cookie: { 6 | setPersistent: (a: { sessionId: string; expires: Date }) => void; 7 | setSession: (a: { sessionId: string }) => void; 8 | delete: () => void; 9 | }; 10 | }; 11 | 12 | export type ISessionHandler = { 13 | getSessionUser: (a?: { skipCache?: true }) => Promise<{ user: U; session: S } | null>; 14 | set: (a: { session: Auth.Session | null }) => void; 15 | invalidateCache: () => void; 16 | }; 17 | 18 | export type ServerAuthCreateSessionHandler = (middlewareContext: MiddlewareContext) => ISessionHandler; 19 | -------------------------------------------------------------------------------- /packages/auth/src/utils/common/index.ts: -------------------------------------------------------------------------------- 1 | export const assertUnreachable = (_: never): never => { 2 | throw new Error('Unchecked'); 3 | }; 4 | 5 | export type Result = NonNullable | Result.Ok>; 6 | export namespace Result { 7 | export type Error = { status: number; message: string; code: string }; 8 | export type Success = { message: 'Success' }; 9 | export type Ok = { data: T; error?: never }; 10 | export type Err = { data?: never; error: E }; 11 | } 12 | -------------------------------------------------------------------------------- /packages/auth/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../tsconfig.base.json"], 3 | "include": ["src"], 4 | "compilerOptions": { 5 | "target": "ES2021", 6 | "lib": ["ES2021", "DOM", "DOM.Iterable"], 7 | "module": "Node16", 8 | "moduleResolution": "Node16", 9 | "declaration": true, 10 | "declarationMap": true, 11 | "preserveSymlinks": true, 12 | "outDir": "dist" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/preprocess-katex-vscode/snippets/katex.pp-svelte.code-snippets: -------------------------------------------------------------------------------- 1 | { 2 | "New KaTeX inline comment": { 3 | "scope": "text.svelte", 4 | "prefix": "katinline", 5 | "description": "Creates a new inline KaTeX block.", 6 | "body": [" $0"], 7 | }, 8 | "New KaTeX display comment": { 9 | "scope": "text.svelte", 10 | "prefix": "katdisplay", 11 | "description": "Creates a new display KaTeX block.", 12 | "body": ["", "$0"], 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /packages/preprocess-katex-vscode/static/demo-comment-katex-before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/packages/preprocess-katex-vscode/static/demo-comment-katex-before.png -------------------------------------------------------------------------------- /packages/preprocess-katex-vscode/static/demo-comment-katex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/packages/preprocess-katex-vscode/static/demo-comment-katex.png -------------------------------------------------------------------------------- /packages/preprocess-katex-vscode/static/demo-template-katex-before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/packages/preprocess-katex-vscode/static/demo-template-katex-before.png -------------------------------------------------------------------------------- /packages/preprocess-katex-vscode/static/demo-template-katex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/packages/preprocess-katex-vscode/static/demo-template-katex.png -------------------------------------------------------------------------------- /packages/preprocess-katex-vscode/static/sk-katex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/packages/preprocess-katex-vscode/static/sk-katex.png -------------------------------------------------------------------------------- /packages/preprocess-katex/prettier.config.js: -------------------------------------------------------------------------------- 1 | import baseConfig from '../../prettier.config.js'; 2 | 3 | /** 4 | * @type {import("prettier").Config} 5 | */ 6 | const config = { 7 | ...baseConfig, 8 | }; 9 | 10 | export default config; 11 | -------------------------------------------------------------------------------- /packages/preprocess-katex/src/client.ts: -------------------------------------------------------------------------------- 1 | export const LaTeX = String.raw as ( 2 | template: { raw: readonly string[] | ArrayLike }, 3 | ...substitutions: unknown[] 4 | ) => string; 5 | -------------------------------------------------------------------------------- /packages/preprocess-katex/src/index.ts: -------------------------------------------------------------------------------- 1 | export { processKatex } from './preprocess-katex.js'; 2 | 3 | export { createKatexLogger, type Logger } from './logger.js'; 4 | -------------------------------------------------------------------------------- /packages/preprocess-katex/src/katex.css: -------------------------------------------------------------------------------- 1 | @import 'katex/dist/katex.css'; 2 | -------------------------------------------------------------------------------- /packages/preprocess-katex/src/katex.ts: -------------------------------------------------------------------------------- 1 | import katex from 'katex'; 2 | 3 | export { katex }; 4 | -------------------------------------------------------------------------------- /packages/preprocess-katex/src/logger.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | export type Logger = { 4 | error?: (e: Error, filename: string) => void; 5 | warn?: (e: Error, filename: string) => void; 6 | info?: (detail: { count: number }, filename: string) => void; 7 | }; 8 | 9 | export const createKatexLogger = ( 10 | formatFilename: (filename: string) => void = (filename: string) => filename, 11 | ): Logger => { 12 | return { 13 | error: (e, filename) => console.error(`[PREPROCESS] | Math | Error | ${formatFilename(filename)} | ${e.message}`), 14 | warn: (e: Error, filename) => 15 | console.warn(`[PREPROCESS] | Math | Warn | ${formatFilename(filename)} | ${e.message}`), 16 | info: (detail, filename) => 17 | console.info(`[PREPROCESS] | Math | Info | ${formatFilename(filename)} | count: ${detail.count}`), 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /packages/preprocess-katex/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "target": "ES2022", 5 | "lib": ["ES2022"], 6 | "module": "Node16", 7 | "moduleResolution": "Node16", 8 | "declaration": true, 9 | "declarationMap": true, 10 | "preserveSymlinks": true, 11 | "outDir": "dist" 12 | }, 13 | "include": ["src"] 14 | } 15 | -------------------------------------------------------------------------------- /packages/preprocess-markdown-vscode/snippets/md.pp-svelte.code-snippets: -------------------------------------------------------------------------------- 1 | { 2 | "New Markdown block": { 3 | "scope": "text.svelte", 4 | "prefix": "md-start", 5 | "description": "Creates a new markdown block.", 6 | "body": ["", "$0"], 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /packages/preprocess-markdown-vscode/static/demo-comment-md-before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/packages/preprocess-markdown-vscode/static/demo-comment-md-before.png -------------------------------------------------------------------------------- /packages/preprocess-markdown-vscode/static/demo-comment-md.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/packages/preprocess-markdown-vscode/static/demo-comment-md.png -------------------------------------------------------------------------------- /packages/preprocess-markdown-vscode/static/sk-md.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/packages/preprocess-markdown-vscode/static/sk-md.png -------------------------------------------------------------------------------- /packages/preprocess-markdown-vscode/syntaxes/md.pp-svelte.tmLanguage.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Svelte Component Markdown Injection", 3 | "scopeName": "md.pp-svelte", 4 | "injectionSelector": "L:source.svelte", 5 | "fileTypes": [], 6 | "patterns": [ 7 | { 8 | "begin": "", 10 | "name": "source.pp-svelte", 11 | "patterns": [ 12 | { 13 | "begin": "md-start", 14 | "beginCaptures": { "0": { "name": "keyword.control.pp-svelte" } }, 15 | "end": ".*?(md-end)", 16 | "endCaptures": { "1": { "name": "keyword.control.pp-svelte" } }, 17 | "name": "markdown.pp-svelte", 18 | "contentName": "text.html.markdown", 19 | "patterns": [{ "include": "text.html.markdown" }] 20 | } 21 | ] 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /packages/preprocess-markdown/prettier.config.js: -------------------------------------------------------------------------------- 1 | import baseConfig from '../../prettier.config.js'; 2 | 3 | /** 4 | * @type {import("prettier").Config} 5 | */ 6 | const config = { 7 | ...baseConfig, 8 | }; 9 | 10 | export default config; 11 | -------------------------------------------------------------------------------- /packages/preprocess-markdown/src/index.ts: -------------------------------------------------------------------------------- 1 | export { processMarkdown } from './preprocess-md.js'; 2 | export { type Logger, createMdLogger } from './logger.js'; 3 | -------------------------------------------------------------------------------- /packages/preprocess-markdown/src/logger.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | export type Logger = { 4 | error?: (e: Error, filename: string) => void; 5 | info?: (a: { count: number }, filename: string) => void; 6 | }; 7 | 8 | export const createMdLogger = (formatFilename: (filename: string) => void = (filename: string) => filename): Logger => { 9 | return { 10 | error: (e, filename) => console.error(`[PREPROCESS] | Mark | Error | ${formatFilename(filename)} | ${e.message}`), 11 | info: (detail, filename) => 12 | console.info(`[PREPROCESS] | Mark | Info | ${formatFilename(filename)} | count: ${detail.count}`), 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /packages/preprocess-markdown/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "target": "ES2022", 5 | "lib": ["ES2022"], 6 | "module": "Node16", 7 | "moduleResolution": "Node16", 8 | "declaration": true, 9 | "declarationMap": true, 10 | "preserveSymlinks": true, 11 | "outDir": "dist" 12 | }, 13 | "include": ["src"] 14 | } 15 | -------------------------------------------------------------------------------- /packages/preprocess-shiki-vscode/.vscodeignore: -------------------------------------------------------------------------------- 1 | scripts/ 2 | -------------------------------------------------------------------------------- /packages/preprocess-shiki-vscode/static/demo-comment-code-before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/packages/preprocess-shiki-vscode/static/demo-comment-code-before.png -------------------------------------------------------------------------------- /packages/preprocess-shiki-vscode/static/demo-comment-code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/packages/preprocess-shiki-vscode/static/demo-comment-code.png -------------------------------------------------------------------------------- /packages/preprocess-shiki-vscode/static/sk-shiki.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/packages/preprocess-shiki-vscode/static/sk-shiki.png -------------------------------------------------------------------------------- /packages/preprocess-shiki/prettier.config.js: -------------------------------------------------------------------------------- 1 | import baseConfig from '../../prettier.config.js'; 2 | 3 | /** 4 | * @type {import("prettier").Config} 5 | */ 6 | const config = { 7 | ...baseConfig, 8 | }; 9 | 10 | export default config; 11 | -------------------------------------------------------------------------------- /packages/preprocess-shiki/src/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | defaultBundledLangNames, 3 | defaultCssVarToThemeName, 4 | defaultcssVarPrefix, 5 | defaultCommonDelimiter, 6 | defaultTransformMap, 7 | defaultEscapePreprocessor, 8 | defaultEscapeSvelte, 9 | getOrLoadOpts, 10 | updateOpts, 11 | } from './defaultOpts.js'; 12 | export { codeToDecoratedHtmlSync, codeToDecoratedHtml } from './highlight.js'; 13 | export { createShikiLogger } from './logger.js'; 14 | export { processCodeblockSync, processCodeblock } from './preprocessors.js'; 15 | export type { HighlightOpts, PreprocessOpts, Logger, CreateOptsArgs } from './types.js'; 16 | -------------------------------------------------------------------------------- /packages/preprocess-shiki/src/logger.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import type { Logger } from './types.js'; 4 | 5 | export const createShikiLogger = ( 6 | formatFilename: (filename: string) => void = (filename: string) => filename, 7 | ): Logger => { 8 | return { 9 | error: (e, filename) => console.error(`[PREPROCESS] | Code | Error | ${formatFilename(filename)} | ${e.message}`), 10 | warn: (e, filename) => console.warn(`[PREPROCESS] | Code | Warn | ${formatFilename(filename)} | ${e.message}`), 11 | info: (detail, filename) => 12 | console.info(`[PREPROCESS] | Code | Info | ${formatFilename(filename)} | count: ${detail.count}`), 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /packages/preprocess-shiki/src/strip-options/findAllSubstrWindows.ts: -------------------------------------------------------------------------------- 1 | import type { Range } from '../types.js'; 2 | 3 | export function findAllSubstrWindows(str: string, substr: string): Range[] { 4 | const indexes: Range[] = []; 5 | let i = -1; 6 | while ((i = str.indexOf(substr, i + 1)) !== -1) indexes.push([i, i + substr.length]); 7 | return indexes; 8 | } 9 | -------------------------------------------------------------------------------- /packages/preprocess-shiki/src/strip-options/option-type/rustyString.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | import { takeRustyString } from './rustyString.js'; 3 | 4 | test('takeRustyString', () => { 5 | expect(takeRustyString('#"foo"#')).toEqual({ match: 'foo', rest: '' }); 6 | expect(takeRustyString('##"foo"##baz')).toEqual({ match: 'foo', rest: 'baz' }); 7 | expect(takeRustyString('##"foo"##"##baz')).toEqual({ match: 'foo', rest: '"##baz' }); 8 | 9 | expect(takeRustyString('')).toBe(null); 10 | expect(takeRustyString('')).toBe(null); 11 | expect(takeRustyString('""')).toBe(null); 12 | expect(takeRustyString('#""#f')).toBe(null); 13 | expect(takeRustyString('##foo"##baz')).toBe(null); 14 | expect(takeRustyString('##"foo##baz')).toBe(null); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/preprocess-shiki/src/strip-options/option-type/utils.ts: -------------------------------------------------------------------------------- 1 | export const escapeOptGroup = (optGroupStr: string) => 2 | optGroupStr.replace(/\\\\n/g, '\n').replace(/\\\\r/g, '\r').replace(/\\\\t/g, '\t').replace(/\\\\/g, '\\'); 3 | -------------------------------------------------------------------------------- /packages/preprocess-shiki/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "target": "ES2022", 5 | "lib": ["ES2022"], 6 | "module": "Node16", 7 | "moduleResolution": "Node16", 8 | "declaration": true, 9 | "declarationMap": true, 10 | "preserveSymlinks": true, 11 | "outDir": "dist" 12 | }, 13 | "include": ["src"] 14 | } 15 | -------------------------------------------------------------------------------- /packages/preprocess-shiki/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, coverageConfigDefaults } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | coverage: { 6 | provider: 'v8', 7 | include: ['**'], 8 | exclude: [...coverageConfigDefaults.exclude, 'src/themes'], 9 | reporter: ['text'], 10 | }, 11 | }, 12 | }); 13 | -------------------------------------------------------------------------------- /packages/svelte-crop-window/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/packages/svelte-crop-window/README.md -------------------------------------------------------------------------------- /packages/svelte-crop-window/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /packages/svelte-crop-window/prettier.config.js: -------------------------------------------------------------------------------- 1 | import baseConfig from '../../prettier.config.js'; 2 | 3 | /** 4 | * @type {import("prettier").Config} 5 | */ 6 | const config = { 7 | ...baseConfig, 8 | plugins: ['prettier-plugin-svelte', 'prettier-plugin-tailwindcss'], 9 | overrides: [{ files: '*.svelte', options: { parser: 'svelte' } }], 10 | tailwindConfig: 'tailwind.config.ts', 11 | }; 12 | 13 | export default config; 14 | -------------------------------------------------------------------------------- /packages/svelte-crop-window/src/app.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /packages/svelte-crop-window/src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://kit.svelte.dev/docs/types#app 2 | // for information about these interfaces 3 | declare global { 4 | namespace App { 5 | // interface Error {} 6 | // interface Locals {} 7 | // interface PageData {} 8 | // interface PageState {} 9 | // interface Platform {} 10 | } 11 | } 12 | 13 | export {}; 14 | -------------------------------------------------------------------------------- /packages/svelte-crop-window/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %sveltekit.head% 8 | 9 | 10 |
%sveltekit.body%
11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/svelte-crop-window/src/lib/actions/handlers.ts: -------------------------------------------------------------------------------- 1 | export function preventDefault(event: Event) { 2 | event.preventDefault(); 3 | return false; 4 | } 5 | -------------------------------------------------------------------------------- /packages/svelte-crop-window/src/lib/index.ts: -------------------------------------------------------------------------------- 1 | export { CropWindow } from './cropWindow.svelte.js'; 2 | export { cropValueToStyles, outerStyles, mediaStyles } from './styleHelpers.js'; 3 | export type { CropShape, CropValue, CropWindowOptions } from './utils/types.js'; 4 | export { defaultCropValue, defaultCropWindowOptions } from './utils/defaults.js'; 5 | -------------------------------------------------------------------------------- /packages/svelte-crop-window/src/lib/utils/css.ts: -------------------------------------------------------------------------------- 1 | import type { PropertiesHyphen as StyleObject } from 'csstype'; 2 | 3 | // https://github.com/melt-ui/runes/blob/develop/packages/helpers/src/lib/style.ts 4 | 5 | /** 6 | * A utility function that converts a style object to a string. 7 | * 8 | * @param style - The style object to convert 9 | * @returns The style object as a string 10 | */ 11 | export function styleToString(style: StyleObject): string { 12 | return Object.keys(style).reduce((str, key) => { 13 | const value = style[key as keyof StyleObject]; 14 | if (value === undefined) { 15 | return str; 16 | } 17 | return `${str}${key}:${value};`; 18 | }, ''); 19 | } 20 | -------------------------------------------------------------------------------- /packages/svelte-crop-window/src/lib/utils/defaults.ts: -------------------------------------------------------------------------------- 1 | import type { CropValue, CropWindowOptions } from './types.js'; 2 | 3 | export const defaultCropWindowOptions: CropWindowOptions = { 4 | marginPercent: 0, 5 | fixDelayMs: 500, 6 | fixDurationMs: 500, 7 | }; 8 | 9 | export const defaultCropValue: CropValue = { 10 | shape: 'round', 11 | position: { x: 0, y: 0 }, 12 | aspect: 1.0, 13 | rotation: 0, 14 | scale: 1, 15 | }; 16 | -------------------------------------------------------------------------------- /packages/svelte-crop-window/src/lib/utils/id.ts: -------------------------------------------------------------------------------- 1 | /** Creates an id with length 10 */ 2 | export function generateId(): string { 3 | return Math.random().toString(36).substr(2, 10); 4 | } 5 | -------------------------------------------------------------------------------- /packages/svelte-crop-window/src/lib/utils/types.ts: -------------------------------------------------------------------------------- 1 | export type Point = { 2 | x: number; 3 | y: number; 4 | }; 5 | 6 | export type Size = { 7 | width: number; 8 | height: number; 9 | }; 10 | 11 | export type CropShape = 'rect' | 'round'; 12 | 13 | /** 14 | * When scaling or rotating, `position` must be updated to keep the same center point. 15 | */ 16 | export type CropValue = { 17 | shape: CropShape; 18 | position: Point; 19 | aspect: number; 20 | rotation: number; 21 | scale: number; 22 | }; 23 | 24 | export type TouchScalePanRotate = { 25 | focalPoint: Point; 26 | pan: Point; 27 | rotation: number; 28 | scale: number; 29 | }; 30 | 31 | export type CropWindowOptions = { 32 | marginPercent: number; 33 | fixDelayMs: number; 34 | fixDurationMs: number; 35 | }; 36 | -------------------------------------------------------------------------------- /packages/svelte-crop-window/src/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | {@render children()} 8 | -------------------------------------------------------------------------------- /packages/svelte-crop-window/src/routes/+layout.ts: -------------------------------------------------------------------------------- 1 | export const prerender = true; 2 | 3 | const colors = { 4 | iris1: '#13131e', 5 | iris2: '#171625', 6 | iris3: '#202248', 7 | iris4: '#262a65', 8 | iris5: '#303374', 9 | iris6: '#3d3e82', 10 | iris7: '#4a4a95', 11 | iris8: '#5958b1', 12 | iris9: '#5b5bd6', 13 | iris10: '#6e6ade', 14 | iris11: '#b1a9ff', 15 | iris12: '#e0dffe', 16 | mauve11: '#b5b2bc', 17 | mauve12: '#eeeef0', 18 | }; 19 | 20 | export const load = async () => { 21 | return { colors }; 22 | }; 23 | -------------------------------------------------------------------------------- /packages/svelte-crop-window/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/packages/svelte-crop-window/static/favicon.png -------------------------------------------------------------------------------- /packages/svelte-crop-window/svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-node'; 2 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; 3 | 4 | /** @type {import('@sveltejs/kit').Config} */ 5 | const config = { 6 | preprocess: vitePreprocess(), 7 | 8 | kit: { 9 | adapter: adapter(), 10 | }, 11 | }; 12 | 13 | export default config; 14 | -------------------------------------------------------------------------------- /packages/svelte-crop-window/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ['./src/**/*.{html,js,svelte,ts}'], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /packages/svelte-crop-window/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["./.svelte-kit/tsconfig.json", "../../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "module": "NodeNext", 5 | "moduleResolution": "NodeNext", 6 | "lib": ["ES2022", "DOM", "DOM.Iterable"], 7 | "target": "ES2022" 8 | }, 9 | "include": [ 10 | "*", 11 | "src/**/*", 12 | ".svelte-kit/ambient.d.ts", 13 | ".svelte-kit/non-ambient.d.ts", 14 | ".svelte-kit/./types/**/$types.d.ts" 15 | ], 16 | "exclude": ["static/**", "vite.config.ts.timestamp-*", "vite.config.js.timestamp-*"] 17 | } 18 | -------------------------------------------------------------------------------- /packages/svelte-crop-window/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite'; 2 | import { defineConfig } from 'vite'; 3 | 4 | export default defineConfig({ 5 | plugins: [sveltekit()], 6 | }); 7 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'packages/*' 3 | - 'sites/*' 4 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import("prettier").Config} 3 | */ 4 | const baseConfig = { 5 | useTabs: true, 6 | singleQuote: true, 7 | printWidth: 120, 8 | }; 9 | 10 | export default baseConfig; 11 | -------------------------------------------------------------------------------- /scripts/release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # this creates the tags for all packages, but only publishes the npm packages 4 | pnpm changeset publish 5 | 6 | # publish vs code extensions too 7 | pnpm build:extensions 8 | 9 | for vsix_path in $(find packages -name "*.vsix"); do 10 | vsce publish --packagePath $vsix_path 2>/dev/null || echo "warn: $vsix_path is not being published because this version is already published on the VS Code Marketplace" 11 | done 12 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "words": [ 3 | "amssymb", 4 | "bazinga", 5 | "catppuccin", 6 | "coolbeans", 7 | "datas", 8 | "defaultCss", 9 | "drac", 10 | "katex", 11 | "klass", 12 | "mdsvex", 13 | "postprocessor", 14 | "rehype", 15 | "rosé", 16 | "samplekit", 17 | "shiki", 18 | "sveltiful", 19 | "timothycohen", 20 | "topbar", 21 | "transpiles", 22 | "vitesse" 23 | ], 24 | "dictionaries": [] 25 | } 26 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/nginx-default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name localhost; 4 | 5 | root /usr/share/nginx/html; 6 | index index.html; 7 | 8 | error_page 404 /404/index.html; 9 | 10 | location / { 11 | try_files $uri $uri/ @custom404; 12 | } 13 | 14 | location = / { 15 | return 302 $scheme://$http_host/docs/code-decoration/; 16 | } 17 | 18 | location = /docs/ { 19 | return 302 $scheme://$http_host/docs/code-decoration/; 20 | } 21 | 22 | location @custom404 { 23 | if ($uri ~ ^/docs) { 24 | return 302 $scheme://$http_host/docs/code-decoration/; 25 | } 26 | return 404; 27 | } 28 | 29 | location = /404/index.html { 30 | internal; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/prettier.config.js: -------------------------------------------------------------------------------- 1 | import baseConfig from '../../prettier.config.js'; 2 | 3 | /** 4 | * @type {import("prettier").Config} 5 | */ 6 | const config = { 7 | ...baseConfig, 8 | plugins: ['prettier-plugin-svelte', 'prettier-plugin-tailwindcss'], 9 | overrides: [{ files: '*.svelte', options: { parser: 'svelte' } }], 10 | tailwindConfig: 'tailwind.config.ts', 11 | }; 12 | 13 | export default config; 14 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://kit.svelte.dev/docs/types#app 2 | // for information about these interfaces 3 | declare global { 4 | namespace App { 5 | // interface Error {} 6 | // interface Locals {} 7 | // interface PageData {} 8 | // interface PageState {} 9 | // interface Platform {} 10 | } 11 | } 12 | 13 | export {}; 14 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/hooks.server.ts: -------------------------------------------------------------------------------- 1 | // Note: this file will not be used in the adapter-static build 2 | // configure redirects in nginx-default.conf 3 | 4 | import { redirect, type Handle } from '@sveltejs/kit'; 5 | import { sequence } from '@sveltejs/kit/hooks'; 6 | 7 | const redirects: Handle = async ({ event, resolve }) => { 8 | const pathname = event.url.pathname; 9 | const res = await resolve(event); 10 | if (res.status === 404) { 11 | if (pathname === '/' || pathname.startsWith('/docs')) { 12 | return redirect(302, '/docs/code-decoration/'); 13 | } 14 | } 15 | return res; 16 | }; 17 | 18 | export const handle = sequence(redirects); 19 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/actions/index.ts: -------------------------------------------------------------------------------- 1 | export * from './clickoutside'; 2 | export * from './keyboard'; 3 | export * from './windowEscape'; 4 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/actions/keyboard.ts: -------------------------------------------------------------------------------- 1 | export type Callback = (e: KeyboardEvent) => void; 2 | export type KeyFnMap = Record; 3 | 4 | export const keyboard = (listenerNode: HTMLElement, keyCallbackMap: KeyFnMap) => { 5 | const onKeydown = (e: KeyboardEvent) => { 6 | for (const keyCode of Object.keys(keyCallbackMap)) { 7 | if (e.code === keyCode) { 8 | keyCallbackMap[keyCode]?.forEach((cb) => cb(e)); 9 | } 10 | } 11 | }; 12 | 13 | listenerNode.addEventListener('keydown', onKeydown); 14 | 15 | return { 16 | destroy() { 17 | listenerNode.removeEventListener('keydown', onKeydown); 18 | }, 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/actions/windowEscape.ts: -------------------------------------------------------------------------------- 1 | export const windowEscape = (_node: HTMLElement, cb?: (e: KeyboardEvent) => void) => { 2 | if (!cb) return; 3 | 4 | const handleKeydown = (event: KeyboardEvent) => { 5 | if (event.key === 'Escape') cb?.(event); 6 | }; 7 | 8 | document.addEventListener('keydown', handleKeydown, true); 9 | 10 | return { 11 | destroy() { 12 | document.removeEventListener('keydown', handleKeydown, true); 13 | }, 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/components/HAnchor.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 16 | {title} 17 | 21 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as HAnchor } from './HAnchor.svelte'; 2 | export { default as Admonition } from './Admonition.svelte'; 3 | export { default as LogoIcon } from './LogoIcon.svelte'; 4 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/controllers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './dropdown.svelte'; 2 | export * from './keyboard'; 3 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/icons/GitHub.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/math/components/Display.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 |
{@html katex.renderToString(eq, { displayMode: true })}
9 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/math/components/Inline.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | {@html katex.renderToString(eq)} 9 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/math/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Display } from './components/Display.svelte'; 2 | export { default as Inline } from './components/Inline.svelte'; 3 | export { LaTeX } from '@samplekit/preprocess-katex/client'; 4 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/nav/index.ts: -------------------------------------------------------------------------------- 1 | export * from './sidebar'; 2 | export * from './topbar'; 3 | export * from './consts'; 4 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/nav/sidebar/SidebarState.svelte.ts: -------------------------------------------------------------------------------- 1 | import { storeOnClient } from './sidebarStorage'; 2 | 3 | export class SidebarState { 4 | #open = $state() as boolean; 5 | 6 | constructor(initialState: boolean) { 7 | this.#open = initialState; 8 | } 9 | 10 | get open() { 11 | return this.#open; 12 | } 13 | 14 | set open(value: boolean) { 15 | this.#open = value; 16 | storeOnClient(value); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/nav/sidebar/TOCInactive.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | {#snippet Toc(tocTree: Array, level: number)} 13 | {#if tocTree.length} 14 |
    15 | {#each tocTree as { href, title, children }, i (i)} 16 |
  • 17 | 18 | {title} 19 | 20 | {#if children?.length} 21 | {@render Toc(children, level + 1)} 22 | {/if} 23 |
  • 24 | {/each} 25 |
26 | {/if} 27 | {/snippet} 28 | 29 | {@render Toc(tocTree, level)} 30 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/nav/sidebar/index.ts: -------------------------------------------------------------------------------- 1 | export { default as SidebarContent } from './SidebarContent.svelte'; 2 | export type { TocItem } from './types'; 3 | export { toc } from './generated/toc'; 4 | export { getStoredSidebarOnClient } from './sidebarStorage'; 5 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/nav/sidebar/sidebarStorage.ts: -------------------------------------------------------------------------------- 1 | import { browser } from '$app/environment'; 2 | import { getBrowserCookie, setBrowserCookie } from '$lib/utils/client'; 3 | 4 | export const STORAGE_KEY_SIDEBAR_STATE = 'sidebar_state'; 5 | 6 | export const getStoredSidebarOnClient = () => { 7 | if (!browser) return false; 8 | const desired = getBrowserCookie('sidebar_state'); 9 | if (desired === 'true') return true; 10 | if (desired === 'false') return false; 11 | return window.matchMedia('(min-width: 1024px)').matches; 12 | }; 13 | 14 | export const storeOnClient = (state: boolean) => { 15 | setBrowserCookie(STORAGE_KEY_SIDEBAR_STATE, state.toString()); 16 | }; 17 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/nav/sidebar/types.ts: -------------------------------------------------------------------------------- 1 | export type TocItem = { title: string; href: string; children?: TocItem[] }; 2 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/nav/topbar/index.ts: -------------------------------------------------------------------------------- 1 | export { default as TopbarContent } from './TopbarContent.svelte'; 2 | export { TopbarController } from './topbarController.svelte'; 3 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/styles/index.ts: -------------------------------------------------------------------------------- 1 | export { getStoredThemeOnClient, THEMES, type Theme } from './themeUtils'; 2 | export { ThemeController } from './themeController.svelte'; 3 | export { default as PaletteMenu } from './PaletteMenu.svelte'; 4 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/styles/postcss/components/features.css: -------------------------------------------------------------------------------- 1 | @layer components { 2 | .feature { 3 | @apply relative list-none; 4 | } 5 | .feature-icon { 6 | @apply absolute -left-8 top-1 h-5 w-5 stroke-success-9; 7 | } 8 | .feature-title { 9 | @apply text-xl text-gray-12; 10 | } 11 | .feature-content { 12 | @apply my-5 block text-base lg:my-6; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/styles/postcss/components/steps.css: -------------------------------------------------------------------------------- 1 | @layer components { 2 | .steps { 3 | counter-reset: 0; 4 | } 5 | .step { 6 | @apply relative list-none before:absolute before:-left-6 before:top-0.5 before:grid before:h-6 before:w-6 before:place-content-center before:rounded-badge before:border before:border-accent-5 before:bg-accent-12 before:text-xs before:font-bold before:text-accent-1 before:content-[counter(step)]; 7 | counter-increment: step; 8 | } 9 | .step-title { 10 | @apply text-xl text-gray-12; 11 | } 12 | .step-content { 13 | @apply my-5 block text-base lg:my-6; 14 | } 15 | .step:not(:last-of-type) { 16 | @apply after:absolute after:-left-[13px] after:bottom-1 after:top-8 after:w-0.5 after:bg-accent-11/10; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/styles/postcss/utilities/utils.css: -------------------------------------------------------------------------------- 1 | @layer utilities { 2 | .respect-reduced-motion { 3 | @apply motion-reduce:!delay-0 motion-reduce:!duration-0 motion-reduce:!animation-delay-0 motion-reduce:!animation-duration-0; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/utils/client/defineCtx.ts: -------------------------------------------------------------------------------- 1 | // Credit: https://www.reddit.com/r/sveltejs/comments/szc481/comment/i8hik7c/?utm_source=share&utm_medium=web2x&context=3 2 | 3 | import { setContext, getContext } from 'svelte'; 4 | 5 | function set(key: string | symbol, service: T): T { 6 | setContext(key, service); 7 | return service; 8 | } 9 | 10 | function get(key: string | symbol): () => T { 11 | return () => getContext(key) as T; 12 | } 13 | 14 | export function defineCtx(key: string | symbol = Symbol()): [() => T, (service: T) => T] { 15 | return [ 16 | get(key), 17 | (service: T) => { 18 | set(key, service); 19 | return service; 20 | }, 21 | ]; 22 | } 23 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/utils/client/index.ts: -------------------------------------------------------------------------------- 1 | export * from './defineCtx'; 2 | export * from './storage'; 3 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/lib/utils/common/index.ts: -------------------------------------------------------------------------------- 1 | export const assertUnreachable = (e: never): never => { 2 | throw new Error(`Unreachable code reached: ${e}`); 3 | }; 4 | 5 | export const cap = (word: T): Capitalize => 6 | ((word[0]?.toUpperCase() ?? '') + word.substring(1)) as Capitalize; 7 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/routes/+layout.ts: -------------------------------------------------------------------------------- 1 | export const prerender = true; 2 | export const trailingSlash = 'always'; 3 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/routes/404/+page.svelte: -------------------------------------------------------------------------------- 1 |
2 |
3 |

404

4 |
Not Found
5 |
6 | 7 |
Documentation:
8 | 9 |
10 | Code Decoration 11 | Math 12 | Markdown 13 |
14 |
15 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/routes/docs/code-decoration/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { codeToDecoratedHtmlSync } from '@samplekit/preprocess-shiki'; 2 | import { opts } from '$lib/shiki'; 3 | 4 | const processedCodeblockExampleInner = codeToDecoratedHtmlSync({ 5 | lang: 'ts', 6 | opts, 7 | transformName: 'block', 8 | code: `console.log('hello world');\nconst highlighted = true;`, 9 | lineToProperties: new Map([[1, { datas: ['highlight'] }]]), 10 | }).data!; 11 | 12 | const processedCodeblockExample = codeToDecoratedHtmlSync({ 13 | lang: 'html', 14 | preProperties: { classes: ['no-lines'] }, 15 | transformName: 'block', 16 | opts, 17 | code: processedCodeblockExampleInner, 18 | }).data!; 19 | 20 | export const load = async () => { 21 | return { processedCodeblockExample }; 22 | }; 23 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/routes/docs/math/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { codeToDecoratedHtmlSync } from '@samplekit/preprocess-shiki'; 2 | import { opts } from '$lib/shiki'; 3 | 4 | const jsTemplateLiteralExampleInner = codeToDecoratedHtmlSync({ 5 | transformName: 'no-code', 6 | lang: 'latex', 7 | opts, 8 | code: ` x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}`, 9 | }).data!; 10 | 11 | const jsTemplateLiteralExample = codeToDecoratedHtmlSync({ 12 | transformName: 'block', 13 | lang: 'ts', 14 | opts, 15 | code: `const eq1 = LaTeX\` 16 | REPLACEME 17 | \`;`, 18 | }).data!.replace(/^(]*>]*>.*\n).*(.*)/, `$1${jsTemplateLiteralExampleInner}$2`)!; 19 | 20 | export const load = async () => { 21 | return { jsTemplateLiteralExample }; 22 | }; 23 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/routes/docs/sidebar.ctx.svelte.ts: -------------------------------------------------------------------------------- 1 | import { defineCtx } from '$lib/utils/client'; 2 | import { SidebarState } from '../../lib/nav/sidebar/SidebarState.svelte'; 3 | 4 | const [get, set] = defineCtx(); 5 | 6 | const createSidebarCtx = (initialState: boolean) => set(new SidebarState(initialState)); 7 | 8 | /** 9 | * The source of truth for the sidebar state is the `#sidebar-toggler` checkbox, ensuring changes work without JS. 10 | * This class is kept in sync by the checkbox so it can be used elsewhere via JS for progressive enhancement. 11 | * 12 | * For example, 13 | * 1) setting a negative tabindex on the sidebar links when closed 14 | * 2) forcing the topbar to stay visible when sidebar is open 15 | * 3) syncing with localStorage for persistence 16 | */ 17 | const useSidebarCtx = get; 18 | 19 | export { createSidebarCtx, useSidebarCtx }; 20 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/src/routes/themeController.ctx.ts: -------------------------------------------------------------------------------- 1 | import { ThemeController, type Theme } from '$lib/styles'; 2 | import { defineCtx } from '$lib/utils/client'; 3 | 4 | const [get, set] = defineCtx(); 5 | 6 | const createThemeControllerCtx = (initialTheme: Theme) => { 7 | set(new ThemeController(initialTheme)); 8 | }; 9 | 10 | /** 11 | * Initializes from storage and propagates changes to storage and the DOM. 12 | */ 13 | const useThemeControllerCtx = get; 14 | 15 | export { createThemeControllerCtx, useThemeControllerCtx }; 16 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/static/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/preprocessor-docs/static/android-chrome-192x192.png -------------------------------------------------------------------------------- /sites/preprocessor-docs/static/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/preprocessor-docs/static/android-chrome-512x512.png -------------------------------------------------------------------------------- /sites/preprocessor-docs/static/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/preprocessor-docs/static/apple-touch-icon.png -------------------------------------------------------------------------------- /sites/preprocessor-docs/static/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/static/cookies.js: -------------------------------------------------------------------------------- 1 | function getBrowserCookie(name) { 2 | const nameEQ = `${name}=`; 3 | const cookies = document.cookie.split(';'); 4 | for (let i = 0; i < cookies.length; i++) { 5 | let cookie = cookies[i]; 6 | while (cookie?.charAt(0) === ' ') { 7 | cookie = cookie.substring(1, cookie.length); 8 | } 9 | if (cookie?.indexOf(nameEQ) === 0) { 10 | return cookie.substring(nameEQ.length, cookie.length); 11 | } 12 | } 13 | return null; 14 | } -------------------------------------------------------------------------------- /sites/preprocessor-docs/static/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/preprocessor-docs/static/favicon-16x16.png -------------------------------------------------------------------------------- /sites/preprocessor-docs/static/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/preprocessor-docs/static/favicon-32x32.png -------------------------------------------------------------------------------- /sites/preprocessor-docs/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/preprocessor-docs/static/favicon.ico -------------------------------------------------------------------------------- /sites/preprocessor-docs/static/fonts/UbuntuMonoLigaturized/UbuntuMonoLigaturized-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/preprocessor-docs/static/fonts/UbuntuMonoLigaturized/UbuntuMonoLigaturized-Regular.ttf -------------------------------------------------------------------------------- /sites/preprocessor-docs/static/fonts/UbuntuMonoLigaturized/copyright.txt: -------------------------------------------------------------------------------- 1 | Copyright 2010,2011 Canonical Ltd. 2 | 3 | This Font Software is licensed under the Ubuntu Font Licence, Version 4 | 1.0. https://launchpad.net/ubuntu-font-licence 5 | 6 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/static/initSidebar.js: -------------------------------------------------------------------------------- 1 | const sidebarOpen = (() => { 2 | const desired = getBrowserCookie('sidebar_state') 3 | if (desired === 'true') return true 4 | if (desired === 'false') return false 5 | return window.matchMedia('(min-width: 1024px)').matches 6 | })() 7 | 8 | document.documentElement.dataset.preHydrationSidebar = sidebarOpen 9 | 10 | let scrollY = 0; 11 | try { 12 | scrollY = Object.entries(JSON.parse(sessionStorage.getItem('sveltekit:scroll'))).at(-1)[1].y 13 | } catch {} 14 | 15 | document.documentElement.style.setProperty('--topbar-border', `var(--${(sidebarOpen && scrollY) || scrollY ? 'visible' : 'hidden'}-topbar-border)`); 16 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/static/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/preprocessor-docs/static/mstile-144x144.png -------------------------------------------------------------------------------- /sites/preprocessor-docs/static/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/preprocessor-docs/static/mstile-150x150.png -------------------------------------------------------------------------------- /sites/preprocessor-docs/static/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/preprocessor-docs/static/mstile-310x150.png -------------------------------------------------------------------------------- /sites/preprocessor-docs/static/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/preprocessor-docs/static/mstile-310x310.png -------------------------------------------------------------------------------- /sites/preprocessor-docs/static/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/preprocessor-docs/static/mstile-70x70.png -------------------------------------------------------------------------------- /sites/preprocessor-docs/static/overview-photo-1420w.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/preprocessor-docs/static/overview-photo-1420w.webp -------------------------------------------------------------------------------- /sites/preprocessor-docs/static/samplekit-logo-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/preprocessor-docs/static/samplekit-logo-128x128.png -------------------------------------------------------------------------------- /sites/preprocessor-docs/static/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import { config } from './src/lib/styles/tailwindConfig'; 2 | 3 | export default config; 4 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["./.svelte-kit/tsconfig.json", "../../tsconfig.base.json"], 3 | "compilerOptions": { 4 | "module": "ES2022", 5 | "moduleResolution": "Bundler", 6 | "lib": ["ES2022", "DOM", "DOM.Iterable"], 7 | "noEmit": true, 8 | "target": "ES2022" 9 | }, 10 | "include": [ 11 | "postcss.config.js", 12 | "prettier.config.js", 13 | "svelte.config.js", 14 | "vite.config.ts", 15 | "src/**/*", 16 | ".svelte-kit/ambient.d.ts", 17 | ".svelte-kit/non-ambient.d.ts", 18 | ".svelte-kit/./types/**/$types.d.ts" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /sites/preprocessor-docs/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite'; 2 | import { defineConfig } from 'vite'; 3 | 4 | export default defineConfig({ 5 | plugins: [sveltekit()], 6 | build: { 7 | target: 'ES2022', 8 | }, 9 | optimizeDeps: { 10 | esbuildOptions: { 11 | target: 'ES2022', 12 | }, 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /sites/samplekit.dev/.graphqlrc.ts: -------------------------------------------------------------------------------- 1 | import { ApiType, shopifyApiProject } from '@shopify/api-codegen-preset'; 2 | 3 | export default { 4 | // For syntax highlighting / auto-complete when writing operations 5 | schema: 'https://shopify.dev/admin-graphql-direct-proxy/2024-10', 6 | documents: ['./lib/**/*.{js,ts,jsx,tsx}'], 7 | projects: { 8 | // To produce variable / return types for Shopify Storefront API operations 9 | default: shopifyApiProject({ 10 | apiType: ApiType.Storefront, 11 | apiVersion: '2024-10', 12 | documents: ['./src/lib/**/*.{js,ts,jsx,tsx}'], 13 | outputDir: './generated/shopify-graphql-types', 14 | }), 15 | }, 16 | ignoreNoDocuments: true, 17 | }; 18 | -------------------------------------------------------------------------------- /sites/samplekit.dev/cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "words": [ 3 | "behaviour", 4 | "brackbang", 5 | "btns", 6 | "closehtmlcomment", 7 | "endregion", 8 | "fouc", 9 | "idxs", 10 | "imagekit", 11 | "katex", 12 | "ltscript", 13 | "lucide", 14 | "Neuburg", 15 | "mdsvex", 16 | "nogo", 17 | "postcss", 18 | "rehype", 19 | "replacestate", 20 | "samplekit", 21 | "sesh", 22 | "shiki", 23 | "supabase", 24 | "superforms", 25 | "timothycohen", 26 | "tripgrave", 27 | "tripslash", 28 | "triptilde", 29 | "ttlcache", 30 | "typesafe", 31 | "unsub", 32 | "uswds", 33 | "vals", 34 | "vaul", 35 | "vite", 36 | "webp", 37 | "websockets" 38 | ], 39 | "dictionaries": ["html", "typescript", "svelte", "sql", "node"] 40 | } 41 | -------------------------------------------------------------------------------- /sites/samplekit.dev/drizzle.gen.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'drizzle-kit'; 2 | 3 | export default { 4 | schema: ['./src/lib/db/server/schema'], 5 | out: 'generated/db-migrations/', 6 | dialect: 'postgresql', 7 | } satisfies Config; 8 | -------------------------------------------------------------------------------- /sites/samplekit.dev/drizzle.studio.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'drizzle-kit'; 2 | 3 | const PG_CONNECTION_STRING = process.env['PG_CONNECTION_STRING']; 4 | 5 | if (!PG_CONNECTION_STRING) { 6 | throw new Error('PG_CONNECTION_STRING environment variable is not set (are you running with env-cmd?)'); 7 | } 8 | 9 | export default { 10 | schema: ['./src/lib/db/server/schema'], 11 | dialect: 'postgresql', 12 | dbCredentials: { url: PG_CONNECTION_STRING }, 13 | } satisfies Config; 14 | -------------------------------------------------------------------------------- /sites/samplekit.dev/generated/db-migrations/0000_keen_wonder_man.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS "user_account" ( 2 | "id" varchar PRIMARY KEY NOT NULL 3 | ); 4 | -------------------------------------------------------------------------------- /sites/samplekit.dev/generated/db-migrations/0002_sturdy_young_avengers.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS "presigned_url" ( 2 | "user_id" varchar NOT NULL, 3 | "url" varchar PRIMARY KEY NOT NULL, 4 | "created" timestamp NOT NULL, 5 | CONSTRAINT "presigned_url_user_id_unique" UNIQUE("user_id") 6 | ); 7 | --> statement-breakpoint 8 | DO $$ BEGIN 9 | ALTER TABLE "presigned_url" ADD CONSTRAINT "presigned_url_user_id_user_account_id_fk" FOREIGN KEY ("user_id") REFERENCES "user_account"("id") ON DELETE cascade ON UPDATE no action; 10 | EXCEPTION 11 | WHEN duplicate_object THEN null; 12 | END $$; 13 | -------------------------------------------------------------------------------- /sites/samplekit.dev/generated/db-migrations/0003_heavy_wraith.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Unfortunately in current drizzle-kit version we can't automatically get name for primary key. 3 | We are working on making it available! 4 | 5 | Meanwhile you can: 6 | 1. Check pk name in your database, by running 7 | SELECT constraint_name FROM information_schema.table_constraints 8 | WHERE table_schema = 'public' 9 | AND table_name = 'presigned_url' 10 | AND constraint_type = 'PRIMARY KEY'; 11 | 2. Uncomment code below and paste pk name manually 12 | 13 | Hope to release this update as soon as possible 14 | */ 15 | 16 | -- ALTER TABLE "presigned_url" DROP CONSTRAINT "";--> statement-breakpoint 17 | ALTER TABLE "presigned_url" ADD COLUMN "key" varchar NOT NULL; -------------------------------------------------------------------------------- /sites/samplekit.dev/generated/db-migrations/0004_new_bug.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "presigned_url" ALTER COLUMN "created" SET DEFAULT now(); -------------------------------------------------------------------------------- /sites/samplekit.dev/generated/db-migrations/0005_bumpy_patch.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "session" RENAME COLUMN "lastSeen" TO "last_seen";--> statement-breakpoint 2 | ALTER TABLE "session" RENAME COLUMN "send_time" TO "temp_confirmation_expires"; -------------------------------------------------------------------------------- /sites/samplekit.dev/generated/db-migrations/meta/0000_snapshot.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "7", 3 | "dialect": "postgresql", 4 | "tables": { 5 | "public.user_account": { 6 | "name": "user_account", 7 | "schema": "", 8 | "columns": { 9 | "id": { 10 | "name": "id", 11 | "type": "varchar", 12 | "primaryKey": true, 13 | "notNull": true 14 | } 15 | }, 16 | "indexes": {}, 17 | "foreignKeys": {}, 18 | "compositePrimaryKeys": {}, 19 | "uniqueConstraints": {} 20 | } 21 | }, 22 | "enums": {}, 23 | "schemas": {}, 24 | "_meta": { 25 | "schemas": {}, 26 | "tables": {}, 27 | "columns": {} 28 | }, 29 | "id": "20852d21-2bb5-45fc-a9b4-063c04692075", 30 | "prevId": "00000000-0000-0000-0000-000000000000" 31 | } -------------------------------------------------------------------------------- /sites/samplekit.dev/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | 'tailwindcss/nesting': {}, 4 | tailwindcss: {}, 5 | autoprefixer: {}, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /sites/samplekit.dev/prettier.config.js: -------------------------------------------------------------------------------- 1 | import baseConfig from '../../prettier.config.js'; 2 | 3 | /** 4 | * @type {import("prettier").Config} 5 | */ 6 | const config = { 7 | ...baseConfig, 8 | plugins: ['prettier-plugin-svelte', 'prettier-plugin-tailwindcss'], 9 | overrides: [{ files: '*.svelte', options: { parser: 'svelte' } }], 10 | tailwindConfig: 'tailwind.config.ts', 11 | }; 12 | 13 | export default config; 14 | -------------------------------------------------------------------------------- /sites/samplekit.dev/scripts/generate-theme-utils.sh: -------------------------------------------------------------------------------- 1 | cd src/lib/styles && 2 | cp themeUtils.ts themeUtils.tmp.ts && 3 | sed -i '' 's/export //g' themeUtils.tmp.ts && 4 | npx tsc themeUtils.tmp.ts && 5 | mv themeUtils.tmp.js ../../../static/themeUtils.js && 6 | rm themeUtils.tmp.ts 7 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/hooks.client.ts: -------------------------------------------------------------------------------- 1 | import '$lib/initClient'; 2 | 3 | import { handleErrorWithSentry } from '$lib/logging/client'; 4 | 5 | export const handleError = handleErrorWithSentry(); 6 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/articles/components/Changelog.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | {#if updates && updates.length} 12 |
13 |

Changelog

14 | {#each updates as update} 15 |

16 | 17 |

18 |
    19 | {#each update.descriptions as description} 20 |
  • {description}
  • 21 | {/each} 22 |
23 | {/each} 24 |
25 | {/if} 26 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/articles/components/PrevNext.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | {#if prev} 14 |
15 |

Previous Article

16 | 17 |
18 | {/if} 19 | 20 | {#if next} 21 |
22 |

Next Article

23 | 24 |
25 | {/if} 26 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/articles/components/card/Tags.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | {#if tags?.length} 8 |
    9 | {#each tags as tag} 10 |
  • 11 | {tag} 12 |
  • 13 | {/each} 14 |
15 | {/if} 16 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/articles/components/card/index.ts: -------------------------------------------------------------------------------- 1 | export { default as CardList } from './CardList.svelte'; 2 | export { default as FeatureCard } from './FeatureCard.svelte'; 3 | export { default as LinksAndDate } from './LinksAndDate.svelte'; 4 | export { default as Tags } from './Tags.svelte'; 5 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/articles/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Changelog } from './Changelog.svelte'; 2 | export { default as DateLine } from './DateLine.svelte'; 3 | export { default as HAnchor } from './HAnchor.svelte'; 4 | export { default as PrevNext } from './PrevNext.svelte'; 5 | export { default as Series } from './Series.svelte'; 6 | export { default as TOC } from './TOC.svelte'; 7 | export { default as Vaul } from './Vaul.svelte'; 8 | 9 | export * from './card'; 10 | export * from './tab-panels'; 11 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/articles/components/tab-panels/TabPanelItem.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | {#if panel.component} 16 | {@const Component = panel.component} 17 | {#if Component instanceof Promise}{#await Component then C}{/await} 18 | {:else}{/if} 19 | {:else if panel.rawHTML} 20 | {@const rawHTML = panel.rawHTML} 21 | {#if rawHTML instanceof Promise}{#await rawHTML then html}{@html html}{/await} 22 | {:else}{@html panel.rawHTML}{/if} 23 | {/if} 24 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/articles/components/tab-panels/collapsed.ctx.svelte.ts: -------------------------------------------------------------------------------- 1 | import { BoxedBool } from '$lib/svelte-runes/primitives.svelte'; 2 | import { defineCtx } from '$lib/utils/client'; 3 | 4 | const [getCtx, setCtx] = defineCtx(); 5 | 6 | const createCollapsedCtx = (initial: boolean) => { 7 | return setCtx(new BoxedBool(initial)); 8 | }; 9 | 10 | export { createCollapsedCtx, getCtx as useCollapsedCtx }; 11 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/articles/components/tab-panels/index.ts: -------------------------------------------------------------------------------- 1 | export { default as TabPanelItem } from './TabPanelItem.svelte'; 2 | export { default as TabPanels } from './TabPanels.svelte'; 3 | export { default as CodeTopper } from './CodeTopper.svelte'; 4 | export { createCollapsedCtx, useCollapsedCtx } from './collapsed.ctx.svelte'; 5 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/articles/load/demos/client.ts: -------------------------------------------------------------------------------- 1 | import { splitAndProcess } from './common'; 2 | import type { ComponentProcessedLazy, DemoComponent, ModuleDefinitions } from './types'; 3 | 4 | export const processComponents = (moduleDefinitions: ModuleDefinitions) => { 5 | return moduleDefinitions.reduce>((acc, curr, index) => { 6 | if (!curr.loadComponent) return acc; 7 | 8 | const { title, loadComponent, wrapperProps, icon } = curr; 9 | return [ 10 | ...acc, 11 | { 12 | title, 13 | index, 14 | component: (loadComponent() as Promise<{ default: DemoComponent }>).then((c) => c.default), 15 | wrapperProps, 16 | icon, 17 | }, 18 | ]; 19 | }, []); 20 | }; 21 | 22 | export const processedComponentsMap = splitAndProcess(processComponents); 23 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/auth/client/index.ts: -------------------------------------------------------------------------------- 1 | export * from '@samplekit/auth/client'; 2 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/auth/common/index.ts: -------------------------------------------------------------------------------- 1 | export const mfaKinds = ['authenticator', 'passkeys', 'sms'] as const; 2 | export const mfaLabels: Record = { 3 | sms: 'SMS', 4 | passkeys: 'Passkey / Biometric', 5 | authenticator: 'Authenticator App', 6 | }; 7 | 8 | export * from '@samplekit/auth/common'; 9 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/auth/server/config.ts: -------------------------------------------------------------------------------- 1 | import { createConfig } from '@samplekit/auth/server'; 2 | import { dev } from '$app/environment'; 3 | import { PUBLIC_EFFECTIVE_DOMAIN, PUBLIC_ORIGIN } from '$env/static/public'; 4 | 5 | export const config = createConfig({ 6 | env: { 7 | secureCookie: !dev, 8 | PUBLIC_ORIGIN, 9 | }, 10 | sessionExpiresIn: { 11 | activePeriod: 1000 * 60 * 60 * 24 * 1, 12 | idlePeriod: 1000 * 60 * 60 * 24 * 13, 13 | }, 14 | lastSeen: { 15 | updateEvery: 1000 * 60 * 5, 16 | }, 17 | authenticatorName: 'SampleKit', 18 | passkey: { 19 | rpName: 'SampleKit', // https://simplewebauthn.dev/docs/packages/server 20 | rpID: PUBLIC_EFFECTIVE_DOMAIN, // https://www.w3.org/TR/webauthn-2/#rp-id 21 | }, 22 | }); 23 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/auth/server/index.ts: -------------------------------------------------------------------------------- 1 | export { auth } from './createAuth'; 2 | export { createAuthMiddleware } from './middleware'; 3 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/bot-protection/turnstile/client/index.ts: -------------------------------------------------------------------------------- 1 | export { default as TurnstileEl } from './TurnstileEl.svelte'; 2 | export { Turnstile, type TurnstileForm, type TurnstileRenderOpts } from './turnstile.svelte'; 3 | export { createTurnstileLoadedFlagCtx, useTurnstileLoadedFlagCtx } from './turnstileLoaded.ctx.svelte'; 4 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/bot-protection/turnstile/client/turnstileLoaded.ctx.svelte.ts: -------------------------------------------------------------------------------- 1 | import { defineCtx } from '$lib/utils/client'; 2 | 3 | const [getCtx, setCtx] = defineCtx<{ flag: boolean }>(); 4 | 5 | const createTurnstileLoadedFlagCtx = () => { 6 | const flag = $state({ flag: typeof window === 'undefined' ? false : 'turnstile' in window }); 7 | setCtx(flag); 8 | }; 9 | 10 | export { createTurnstileLoadedFlagCtx, getCtx as useTurnstileLoadedFlagCtx }; 11 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/bot-protection/turnstile/common.ts: -------------------------------------------------------------------------------- 1 | export const turnstileFormInputName = 'cf-turnstile-response'; 2 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/components/LoadingDots.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/components/LogoLink.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 19 | 20 | Sample.Kit 21 | 22 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/components/ScrollIndicator.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as LogoIcon } from './LogoIcon.svelte'; 2 | export { default as LogoLink } from './LogoLink.svelte'; 3 | export { default as CodeInput } from './CodeInput.svelte'; 4 | export { default as LoadingDots } from './LoadingDots.svelte'; 5 | export { default as SEO } from './SEO.svelte'; 6 | export { default as Admonition } from './Admonition.svelte'; 7 | export { default as Portal } from './Portal.svelte'; 8 | export { default as ScrollIndicator } from './ScrollIndicator.svelte'; 9 | 10 | export * from './input'; 11 | export * from './layout'; 12 | export * from './user'; 13 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/components/input/index.ts: -------------------------------------------------------------------------------- 1 | export { default as FileInput } from './FileInput.svelte'; 2 | export { default as InputMessage } from './InputMessage.svelte'; 3 | export { default as Switch } from './Switch.svelte'; 4 | export { default as Select } from './Select.svelte'; 5 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/components/layout/AccountLayout.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 |
13 | 22 | 23 |
24 |
25 | {@render children()} 26 |
27 |
28 |
29 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/components/layout/index.ts: -------------------------------------------------------------------------------- 1 | export { default as AccountLayout } from './AccountLayout.svelte'; 2 | export { default as Header } from './Header.svelte'; 3 | export { default as Notice } from './Notice.svelte'; 4 | export * from './nav/mobileNav.ctx.svelte'; 5 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/components/layout/nav/DesktopNavItem.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 |
  • 11 | 18 | {entry.title} 19 | 20 |
  • 21 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/components/user/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Avatar } from './Avatar.svelte'; 2 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/consts.ts: -------------------------------------------------------------------------------- 1 | export const SECURITY_EMAIL = 'security@samplekit.dev'; 2 | export const GH_ROOT = `https://github.com/timothycohen/samplekit`; 3 | export const GH_TREE = `${GH_ROOT}/tree/main`; 4 | export const GH_BLOB = `${GH_ROOT}/blob/main`; 5 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/db/server/index.ts: -------------------------------------------------------------------------------- 1 | export { db } from './repository'; 2 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/db/server/repository/index.ts: -------------------------------------------------------------------------------- 1 | import { meta } from './meta'; 2 | import { presigned } from './presigned'; 3 | import { user } from './user'; 4 | import type { DBRepository } from './types'; 5 | 6 | export const db: DBRepository = { 7 | presigned, 8 | meta, 9 | user, 10 | }; 11 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/db/server/repository/meta.ts: -------------------------------------------------------------------------------- 1 | import { sql } from 'drizzle-orm'; 2 | import { db } from '../singleton'; 3 | import type { DBRepository } from './types'; 4 | 5 | const resetSql = /*sql*/ ` 6 | DO $$ 7 | DECLARE 8 | v_table_name TEXT; 9 | BEGIN 10 | FOR v_table_name IN 11 | SELECT table_name 12 | FROM information_schema.tables 13 | WHERE table_schema = 'public' 14 | AND table_type = 'BASE TABLE' 15 | LOOP 16 | EXECUTE format('TRUNCATE TABLE %I RESTART IDENTITY CASCADE;', v_table_name); 17 | RAISE NOTICE 'Table % truncated successfully.', v_table_name; 18 | END LOOP; 19 | END $$; 20 | `; 21 | 22 | export const meta: DBRepository['meta'] = { 23 | resetDb: async () => { 24 | await db.execute(sql.raw(resetSql)); 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/db/server/repository/presigned.ts: -------------------------------------------------------------------------------- 1 | import { eq } from 'drizzle-orm'; 2 | import { presignedUrls } from '../schema/presignedUrls'; 3 | import { db } from '../singleton'; 4 | import type { DBRepository } from './types'; 5 | 6 | export const presigned: DBRepository['presigned'] = { 7 | insertOrOverwrite: async ({ userId, url, key, created }) => { 8 | await db 9 | .insert(presignedUrls) 10 | .values({ userId, url, key, created }) 11 | .onConflictDoUpdate({ target: presignedUrls.userId, set: { created, url, key } }); 12 | }, 13 | get: async ({ userId }) => { 14 | return await db 15 | .select() 16 | .from(presignedUrls) 17 | .where(eq(presignedUrls.userId, userId)) 18 | .limit(1) 19 | .then((r) => r[0]); 20 | }, 21 | delete: async ({ userId }) => { 22 | await db.delete(presignedUrls).where(eq(presignedUrls.userId, userId)); 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/db/server/repository/types.ts: -------------------------------------------------------------------------------- 1 | import type { DbAdapter } from '@samplekit/auth/server'; 2 | 3 | export type DBRepository = { 4 | presigned: { 5 | insertOrOverwrite: (a: DB.PresignedUrl.Set) => Promise; 6 | get: (a: { userId: string }) => Promise; 7 | delete: (a: { userId: string }) => Promise; 8 | }; 9 | meta: { 10 | resetDb: () => Promise; 11 | }; 12 | user: { 13 | get: (a: { userId: string }) => Promise; 14 | update: (a: { userId: string; values: Partial }) => Promise; 15 | }; 16 | }; 17 | 18 | export type AuthAdapter = DbAdapter< 19 | DB.User, 20 | DB.Provider, 21 | Omit, 22 | DB.Session, 23 | void, 24 | void, 25 | { os: string | null; browser: string | null; ip: string | null } 26 | >; 27 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/db/server/repository/user.ts: -------------------------------------------------------------------------------- 1 | import { eq } from 'drizzle-orm'; 2 | import { users } from '../schema'; 3 | import { db } from '../singleton'; 4 | import type { DBRepository } from './types'; 5 | 6 | export const user: DBRepository['user'] = { 7 | get: async ({ userId }) => { 8 | return (await db.select().from(users).where(eq(users.id, userId)).limit(1))[0]; 9 | }, 10 | update: async ({ userId, values }) => { 11 | await db.update(users).set(values).where(eq(users.id, userId)); 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/db/server/schema/index.ts: -------------------------------------------------------------------------------- 1 | export * from './user-and-auth'; 2 | export * from './presignedUrls'; 3 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/db/server/schema/presignedUrls.ts: -------------------------------------------------------------------------------- 1 | import { pgTable, timestamp, varchar } from 'drizzle-orm/pg-core'; 2 | import { uniqueUserReference } from './user-and-auth/utils'; 3 | 4 | export const presignedUrls = pgTable('presigned_url', { 5 | ...uniqueUserReference({ onDelete: 'cascade' }), 6 | url: varchar('url').notNull(), 7 | key: varchar('key').primaryKey(), 8 | created: timestamp('created', { mode: 'date' }).notNull().defaultNow(), 9 | }); 10 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/db/server/schema/user-and-auth/index.ts: -------------------------------------------------------------------------------- 1 | export * from './provider'; 2 | export * from './session'; 3 | export * from './tokens'; 4 | export * from './user'; 5 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/db/server/schema/user-and-auth/user.ts: -------------------------------------------------------------------------------- 1 | import { jsonb, pgTable, timestamp, varchar } from 'drizzle-orm/pg-core'; 2 | import { createSelectSchema } from 'drizzle-zod'; 3 | import type { CroppedImg } from '$lib/image/common'; 4 | 5 | // user is a reserved keyword in postgres 6 | export const users = pgTable('user_account', { 7 | id: varchar('id').primaryKey(), 8 | email: varchar('email').notNull(), 9 | givenName: varchar('given_name').notNull(), 10 | familyName: varchar('family_name').notNull(), 11 | joinedOn: timestamp('joined_on', { mode: 'date' }).notNull(), 12 | avatar: jsonb('avatar').$type(), 13 | }); 14 | export const userSchema = createSelectSchema(users); 15 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/db/server/schema/user-and-auth/utils.ts: -------------------------------------------------------------------------------- 1 | import { varchar, type UpdateDeleteAction } from 'drizzle-orm/pg-core'; 2 | import { users } from './user'; 3 | 4 | export const userReference = (actions?: { onDelete: UpdateDeleteAction }) => ({ 5 | userId: varchar('user_id') 6 | .references(() => users.id, actions) 7 | .notNull(), 8 | }); 9 | 10 | export const uniqueUserReference = (actions?: { onDelete: UpdateDeleteAction }) => ({ 11 | userId: varchar('user_id') 12 | .references(() => users.id, actions) 13 | .notNull() 14 | .unique(), 15 | }); 16 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/db/server/singleton.ts: -------------------------------------------------------------------------------- 1 | import { getDb } from './connect'; 2 | 3 | export const { db } = getDb(); 4 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/device-info/index.ts: -------------------------------------------------------------------------------- 1 | export { getDeviceInfo } from './platform'; 2 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/device-info/platform.ts: -------------------------------------------------------------------------------- 1 | import platform from 'platform'; 2 | import type { GetDeviceInfo } from './types'; 3 | 4 | export const getDeviceInfo: GetDeviceInfo = ({ headers, getClientAddress }) => { 5 | const pf = platform.parse(headers.get('user-agent') ?? undefined); 6 | return { os: pf.os?.family ?? null, browser: pf.name ?? null, ip: getClientAddress() }; 7 | }; 8 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/device-info/types.ts: -------------------------------------------------------------------------------- 1 | export type GetDeviceInfo = (a: { headers: Headers; getClientAddress: () => string }) => { 2 | os: string | null; 3 | browser: string | null; 4 | ip: string; 5 | }; 6 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/http/common.ts: -------------------------------------------------------------------------------- 1 | export const httpCodeMap: Record = { 2 | 200: 'Success', 3 | 202: 'Accepted', 4 | 400: 'Bad Request', 5 | 401: 'Unauthorized', 6 | 403: 'Forbidden', 7 | 404: 'Not Found', 8 | 429: 'Too Many Requests', 9 | 500: 'Internal Server Error', 10 | }; 11 | 12 | export type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; 13 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/http/server/index.ts: -------------------------------------------------------------------------------- 1 | export * from './json'; 2 | export * from './checkedRedirect'; 3 | export * from './sanitize'; 4 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/http/server/sanitize.ts: -------------------------------------------------------------------------------- 1 | import { PUBLIC_ORIGIN } from '$env/static/public'; 2 | 3 | export const sanitizeRedirectUrl = (strOrForm: string | FormData | null | undefined): string | null => { 4 | if (!strOrForm) return null; 5 | const redirect_url = typeof strOrForm === 'string' ? strOrForm : strOrForm.get('redirect_url'); 6 | if (typeof redirect_url === 'string' && (redirect_url.startsWith('/') || redirect_url.startsWith(PUBLIC_ORIGIN))) { 7 | return redirect_url; 8 | } 9 | return null; 10 | }; 11 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/icons/GitHub.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/image/client/index.ts: -------------------------------------------------------------------------------- 1 | export * from './utils'; 2 | export { 3 | CropWindow, 4 | defaultCropValue, 5 | defaultCropWindowOptions, 6 | outerStyles, 7 | mediaStyles, 8 | cropValueToStyles, 9 | } from '@samplekit/svelte-crop-window'; 10 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/image/client/utils.ts: -------------------------------------------------------------------------------- 1 | export const fileToDataUrl = ( 2 | blob: Blob, 3 | ): Promise<{ uri: string; error?: never } | { uri?: never; error: Error | DOMException }> => { 4 | return new Promise((resolve) => { 5 | const reader = new FileReader(); 6 | reader.onload = (_e) => resolve({ uri: reader.result as string }); 7 | reader.onerror = (_e) => resolve({ error: reader.error ?? new Error('Unknown error') }); 8 | reader.onabort = (_e) => resolve({ error: new Error('Read aborted') }); 9 | reader.readAsDataURL(blob); 10 | }); 11 | }; 12 | 13 | export const humanReadableFileSize = (size: number) => { 14 | if (size < 1024) return `${size}B`; 15 | if (size < 1024 * 1024) return `${(size / 1024).toPrecision(2)}kB`; 16 | return `${(size / (1024 * 1024)).toPrecision(2)}MB`; 17 | }; 18 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/image/common/index.ts: -------------------------------------------------------------------------------- 1 | export * from './schemas'; 2 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/image/common/schemas.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const cropSchema = z 4 | .object({ 5 | position: z.object({ x: z.number(), y: z.number() }), 6 | aspect: z.number(), 7 | rotation: z.number(), 8 | scale: z.number(), 9 | }) 10 | .default({ position: { x: 0, y: 0 }, aspect: 1, rotation: 0, scale: 1 }); 11 | 12 | export type CropValue = z.infer; 13 | 14 | export const croppedImgSchema = z.object({ 15 | url: z.string().url(), 16 | crop: cropSchema, 17 | }); 18 | 19 | export type CroppedImg = z.infer; 20 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/image/components/UploadProgress.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 |
    13 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/image/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ImageCardBtns } from './ImageCardBtns.svelte'; 2 | export { default as ImageCardOverlays } from './ImageCardOverlays.svelte'; 3 | export { default as ImageCrop } from './ImageCrop.svelte'; 4 | export { default as UploadProgress } from './UploadProgress.svelte'; 5 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/initClient.ts: -------------------------------------------------------------------------------- 1 | import { initSentry } from '$lib/logging/client'; 2 | 3 | initSentry(); 4 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/kv/server/index.ts: -------------------------------------------------------------------------------- 1 | export * from './redis'; 2 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/logging/client/index.ts: -------------------------------------------------------------------------------- 1 | export { logger } from './logger'; 2 | export { getBrowserLogflare } from './logflare'; 3 | export { handleErrorWithSentry, initSentry } from './sentry'; 4 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/logging/client/logflare.ts: -------------------------------------------------------------------------------- 1 | import { PUBLIC_LOGFLARE_ACCESS_TOKEN_BROWSER, PUBLIC_LOGFLARE_SOURCE_ID_BROWSER } from '$env/static/public'; 2 | import { createLogflareHttpClient, type LogflareClient } from '../common/logflare'; 3 | 4 | let logflareClient: null | LogflareClient = null; 5 | let disabled = false; 6 | 7 | export const getBrowserLogflare = (): LogflareClient | null => { 8 | if (logflareClient) return logflareClient; 9 | if (disabled) return null; 10 | 11 | try { 12 | logflareClient = createLogflareHttpClient({ 13 | accessToken: PUBLIC_LOGFLARE_ACCESS_TOKEN_BROWSER, 14 | sourceId: PUBLIC_LOGFLARE_SOURCE_ID_BROWSER, 15 | }); 16 | return logflareClient; 17 | } catch { 18 | disabled = true; 19 | } 20 | return null; 21 | }; 22 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/logging/client/sentry.ts: -------------------------------------------------------------------------------- 1 | import * as sentry from '@sentry/sveltekit'; 2 | import { PUBLIC_SENTRY_ENABLED, PUBLIC_SENTRY_DSN } from '$env/static/public'; 3 | 4 | export const initSentry = () => 5 | sentry.init({ 6 | enabled: PUBLIC_SENTRY_ENABLED === 'true', 7 | dsn: PUBLIC_SENTRY_DSN, 8 | tracesSampleRate: 1.0, 9 | replaysSessionSampleRate: 0.1, 10 | replaysOnErrorSampleRate: 1.0, 11 | environment: import.meta.env.MODE, 12 | integrations: [sentry.replayIntegration()], 13 | }); 14 | 15 | export const handleErrorWithSentry = sentry.handleErrorWithSentry; 16 | export const captureExceptionWithSentry = sentry.captureException; 17 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/logging/server/index.ts: -------------------------------------------------------------------------------- 1 | export { logger, setupLogger } from './logger'; 2 | export { getServerLogflare } from './logflare'; 3 | export { initSentry, handleErrorWithSentry, sentryHandle } from './sentry'; 4 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/logging/server/pretty.ts: -------------------------------------------------------------------------------- 1 | import { createPinoPretty } from '../common/pretty'; 2 | 3 | export const pretty = createPinoPretty({ ignore: 'context,status' }); 4 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/object-storage/client/index.ts: -------------------------------------------------------------------------------- 1 | export * from './cropImgUploadController.svelte'; 2 | export { objectStorage } from './repository'; 3 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/object-storage/client/repository.ts: -------------------------------------------------------------------------------- 1 | import { uploadToCloudStorage } from './uploadToCloudStorage'; 2 | import type { ObjectStorageClient } from './types'; 3 | 4 | export const objectStorage: ObjectStorageClient = { 5 | upload: uploadToCloudStorage, 6 | }; 7 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/object-storage/client/types.ts: -------------------------------------------------------------------------------- 1 | import type { Result } from '$lib/utils/common'; 2 | import type { Tweened } from 'svelte/motion'; 3 | 4 | export type UploaderArgs = { 5 | url: string; 6 | formData: FormData; 7 | uploadProgress?: { scale?: number | undefined; tweened?: Tweened | undefined } | undefined; 8 | }; 9 | export type UploaderRes = { promise: Promise>; abort: () => void }; 10 | 11 | export type Uploader = (a: UploaderArgs) => UploaderRes; 12 | 13 | export type ObjectStorageClient = { 14 | upload: Uploader; 15 | }; 16 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/object-storage/server/index.ts: -------------------------------------------------------------------------------- 1 | export { objectStorage } from './repository'; 2 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/object-storage/server/repository.ts: -------------------------------------------------------------------------------- 1 | import { invalidateCloudfront } from './cloudfront'; 2 | import { detectModerationLabels } from './rekognition'; 3 | import { clearBucket, deleteS3Object, generateS3UploadPost } from './s3'; 4 | import { s3CloudfrontKeyController } from './s3CloudfrontKeyController'; 5 | import { createUnsavedUploadCleaner } from './unsavedUploadsCleaner'; 6 | import type { ObjectStorage } from './types'; 7 | 8 | export const objectStorage: ObjectStorage = { 9 | delete: deleteS3Object, 10 | deleteAll: clearBucket, 11 | detectModerationLabels: detectModerationLabels, 12 | invalidateCDN: invalidateCloudfront, 13 | generateUploadFormDataFields: generateS3UploadPost, 14 | createUnsavedUploadCleaner: createUnsavedUploadCleaner, 15 | keyController: s3CloudfrontKeyController, 16 | }; 17 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/api/connectedOrExit.server.ts: -------------------------------------------------------------------------------- 1 | export * from './shopify/connectedOrExit.server'; 2 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/api/index.ts: -------------------------------------------------------------------------------- 1 | export * from './types'; 2 | export { shop } from './shopify/repository'; 3 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/api/shopify/connectedOrExit.server.ts: -------------------------------------------------------------------------------- 1 | import { setupLogger } from '$lib/logging/server'; 2 | import { getShopNameQuery } from './gql'; 3 | import { requestStorefront } from './storefront'; 4 | 5 | export async function shopConnectedOrExit(): Promise { 6 | const res = await requestStorefront({ operation: getShopNameQuery, fetch }); 7 | if (res.errors) { 8 | setupLogger.fatal( 9 | 'Storefront connection error. Please check the following env vars: PUBLIC_SHOPIFY_API_VERSION, PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN, PUBLIC_SHOPIFY_STORE_DOMAIN.', 10 | ); 11 | process.exit(1); 12 | } else { 13 | setupLogger.info('Storefront connected successfully.'); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/api/shopify/gql/cart/index.ts: -------------------------------------------------------------------------------- 1 | export * from './fragments'; 2 | export * from './mutations'; 3 | export * from './queries'; 4 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/api/shopify/gql/cart/queries.ts: -------------------------------------------------------------------------------- 1 | import { cartFragment } from './fragments'; 2 | 3 | export const getCartQuery = /* GraphQL */ ` 4 | query getCart($cartId: ID!) { 5 | cart(id: $cartId) { 6 | ...cart 7 | } 8 | } 9 | ${cartFragment} 10 | `; 11 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/api/shopify/gql/collection/fragments.ts: -------------------------------------------------------------------------------- 1 | import { imageFragment, seoFragment } from '../fragments'; 2 | 3 | export const collectionFragment = /* GraphQL */ ` 4 | fragment collection on Collection { 5 | description 6 | handle 7 | image { 8 | ...image 9 | } 10 | seo { 11 | ...seo 12 | } 13 | title 14 | updatedAt 15 | 16 | products(first: 1) { 17 | edges { 18 | node { 19 | images(first: 1) { 20 | edges { 21 | node { 22 | ...image 23 | } 24 | } 25 | } 26 | } 27 | } 28 | } 29 | } 30 | ${seoFragment} 31 | ${imageFragment} 32 | `; 33 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/api/shopify/gql/collection/index.ts: -------------------------------------------------------------------------------- 1 | export * from './fragments'; 2 | export * from './queries'; 3 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/api/shopify/gql/collection/queries.ts: -------------------------------------------------------------------------------- 1 | import { collectionFragment } from './fragments'; 2 | 3 | export const getCollectionQuery = /* GraphQL */ ` 4 | query getCollection($handle: String!) { 5 | collection(handle: $handle) { 6 | ...collection 7 | } 8 | } 9 | ${collectionFragment} 10 | `; 11 | 12 | export const getCollectionsQuery = /* GraphQL */ ` 13 | query getCollections { 14 | collections(first: 100, sortKey: TITLE) { 15 | edges { 16 | node { 17 | ...collection 18 | } 19 | } 20 | } 21 | } 22 | ${collectionFragment} 23 | `; 24 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/api/shopify/gql/fragments.ts: -------------------------------------------------------------------------------- 1 | export const seoFragment = /* GraphQL */ ` 2 | fragment seo on SEO { 3 | description 4 | title 5 | } 6 | `; 7 | 8 | export const imageFragment = /* GraphQL */ ` 9 | fragment image on Image { 10 | altText 11 | height 12 | url 13 | width 14 | } 15 | `; 16 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/api/shopify/gql/index.ts: -------------------------------------------------------------------------------- 1 | export * from './cart'; 2 | export * from './collection'; 3 | export * from './menu'; 4 | export * from './page'; 5 | export * from './product'; 6 | export * from './shop'; 7 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/api/shopify/gql/menu/index.ts: -------------------------------------------------------------------------------- 1 | export * from './queries'; 2 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/api/shopify/gql/menu/queries.ts: -------------------------------------------------------------------------------- 1 | export const getMenuQuery = /* GraphQL */ ` 2 | query getMenu($handle: String!) { 3 | menu(handle: $handle) { 4 | items { 5 | title 6 | url 7 | } 8 | } 9 | } 10 | `; 11 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/api/shopify/gql/page/fragments.ts: -------------------------------------------------------------------------------- 1 | import { seoFragment } from '../fragments'; 2 | 3 | export const pageFragment = /* GraphQL */ ` 4 | fragment page on Page { 5 | ... on Page { 6 | id 7 | title 8 | handle 9 | body 10 | bodySummary 11 | seo { 12 | ...seo 13 | } 14 | createdAt 15 | updatedAt 16 | } 17 | } 18 | ${seoFragment} 19 | `; 20 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/api/shopify/gql/page/index.ts: -------------------------------------------------------------------------------- 1 | export * from './fragments'; 2 | export * from './queries'; 3 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/api/shopify/gql/page/queries.ts: -------------------------------------------------------------------------------- 1 | import { pageFragment } from './fragments'; 2 | 3 | export const getPageQuery = /* GraphQL */ ` 4 | query getPage($handle: String!) { 5 | page(handle: $handle) { 6 | ...page 7 | } 8 | } 9 | ${pageFragment} 10 | `; 11 | 12 | export const getPagesQuery = /* GraphQL */ ` 13 | query getPages { 14 | pages(first: 100) { 15 | edges { 16 | node { 17 | ...page 18 | } 19 | } 20 | } 21 | } 22 | ${pageFragment} 23 | `; 24 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/api/shopify/gql/product/index.ts: -------------------------------------------------------------------------------- 1 | export * from './fragments'; 2 | export * from './queries'; 3 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/api/shopify/gql/shop/index.ts: -------------------------------------------------------------------------------- 1 | export * from './queries'; 2 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/api/shopify/gql/shop/queries.ts: -------------------------------------------------------------------------------- 1 | export const getShopNameQuery = /* GraphQL */ ` 2 | query getShopName { 3 | shop { 4 | name 5 | } 6 | } 7 | `; 8 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/api/shopify/handlers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './cart'; 2 | export * from './collection'; 3 | export * from './menu'; 4 | export * from './page'; 5 | export * from './product'; 6 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/api/shopify/handlers/menu.ts: -------------------------------------------------------------------------------- 1 | import { getMenuQuery } from '../gql'; 2 | import { requestStorefront } from '../storefront'; 3 | import type { GetMenu } from '../../types'; 4 | 5 | export const getMenu: GetMenu = async ({ handle, fetch }) => { 6 | const res = await requestStorefront({ operation: getMenuQuery, variables: { handle }, fetch }); 7 | return res.data?.menu?.items ?? []; 8 | }; 9 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/api/shopify/handlers/page.ts: -------------------------------------------------------------------------------- 1 | import { getPageQuery, getPagesQuery } from '../gql'; 2 | import { requestStorefront } from '../storefront'; 3 | import type { GetPage, GetPages } from '../../types'; 4 | 5 | /** @throws Error */ 6 | export const getPage: GetPage = async ({ handle, fetch }) => { 7 | const res = await requestStorefront({ operation: getPageQuery, variables: { handle }, fetch }); 8 | if (!res.data?.page) throw new Error('getPage'); 9 | return res.data.page; 10 | }; 11 | 12 | /** @throws Error */ 13 | export const getPages: GetPages = async ({ fetch }) => { 14 | const res = await requestStorefront({ operation: getPagesQuery, fetch }); 15 | if (!res.data?.pages) throw new Error('getPages'); 16 | return res.data.pages.edges.map((e) => e.node); 17 | }; 18 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/api/shopify/index.ts: -------------------------------------------------------------------------------- 1 | export { shop } from './repository'; 2 | 3 | // this shop is created from the default data when you create a partner dev store 4 | // https://shopify.dev/docs/apps/tools/development-stores/generated-data 5 | 6 | // and also extra sample data (this repo uses the clothes theme) 7 | // https://admin.shopify.com/store/quickstart-61f60346/apps/simple-sample-data 8 | 9 | // https://shopify.dev/docs/api/storefront 10 | // https://admin.shopify.com/ 11 | // https://partners.shopify.com/ 12 | // https://demo.vercel.store 13 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/api/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from './handlers'; 2 | export * from './models'; 3 | export * from './repository'; 4 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/index.ts: -------------------------------------------------------------------------------- 1 | export * from './api'; 2 | export * from './searchAndFilter'; 3 | export * from './utils'; 4 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/shop/utils.ts: -------------------------------------------------------------------------------- 1 | export const handleToPath = ({ handle, kind }: { handle: string; kind: 'collection' | 'product' }) => { 2 | switch (kind) { 3 | case 'collection': 4 | return `/shop/collections/${handle}`; 5 | case 'product': 6 | return `/shop/product/${handle}`; 7 | } 8 | }; 9 | 10 | export const formatPrice = ({ amount, currencyCode }: { amount: string; currencyCode: string }) => { 11 | return new Intl.NumberFormat(undefined, { 12 | style: 'currency', 13 | currency: currencyCode, 14 | currencyDisplay: 'narrowSymbol', 15 | }).format(parseFloat(amount)); 16 | }; 17 | 18 | export const DEFAULT_SELECTED_PRODUCT_VARIANT = 'Default Title'; 19 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/styles/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ThemePicker } from './ThemePicker.svelte'; 2 | export { default as ThemeSwitchDayNight } from './ThemeSwitchDayNight.svelte'; 3 | export { default as ThemeSwitchDayNightSystem } from './ThemeSwitchDayNightSystem.svelte'; 4 | export { default as ThemeToggler } from './ThemeToggler.svelte'; 5 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/styles/index.ts: -------------------------------------------------------------------------------- 1 | export * from './components'; 2 | 3 | export { createThemeControllerCtx, useThemeControllerCtx } from './theme.ctx.svelte'; 4 | export { THEMES, type Theme } from './themeUtils'; 5 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/styles/postcss/components/input.css: -------------------------------------------------------------------------------- 1 | @layer components { 2 | .input-label { 3 | @apply mb-2 inline-block text-sm; 4 | } 5 | .input-text { 6 | @apply block h-10 w-full rounded-btn border border-gray-6 px-3 text-sm transition-colors hover:border-gray-7 focus:border-gray-8; 7 | @apply disabled:cursor-not-allowed disabled:bg-gray-1 disabled:text-gray-9 disabled:opacity-70; 8 | } 9 | .input-text-has-btn { 10 | @apply pr-20; 11 | } 12 | .input-text-btn-wrapper { 13 | @apply absolute inset-y-0 right-0 flex items-center; 14 | } 15 | .input-text-btn { 16 | @apply h-full w-full rounded-r-btn px-5 text-gray-11 -outline-offset-2 hover:bg-accent-5 hover:text-gray-12; 17 | } 18 | .input-invalid { 19 | @apply border-error-9 hover:border-error-9 focus:border-error-9; 20 | } 21 | .input-subtext { 22 | @apply min-h-[1.25rem] text-sm; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/styles/postcss/components/radio.css: -------------------------------------------------------------------------------- 1 | @layer components { 2 | .radio { 3 | appearance: none; 4 | width: 1.15rem; 5 | height: 1.15rem; 6 | border: 1px solid hsl(var(--gray-7)); 7 | border-radius: 50%; 8 | display: grid; 9 | place-content: center; 10 | } 11 | 12 | .radio:hover { 13 | border-color: hsl(var(--gray-8)); 14 | } 15 | 16 | .radio:checked { 17 | border-color: hsl(var(--accent-9)); 18 | } 19 | 20 | .radio:focus { 21 | outline: 2px solid hsl(var(--accent-9)); 22 | outline-offset: -2px; 23 | } 24 | 25 | .radio::before { 26 | content: ''; 27 | width: 0.575rem; 28 | height: 0.575rem; 29 | border-radius: 50%; 30 | transition: 150ms transform ease-in-out; 31 | box-shadow: inset 1em 1em hsl(var(--accent-9)); 32 | transform: scale(0); 33 | } 34 | 35 | .radio:checked::before { 36 | transform: scale(1); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/styles/postcss/utilities/utils.css: -------------------------------------------------------------------------------- 1 | @layer utilities { 2 | .respect-reduced-motion { 3 | @apply motion-reduce:!delay-0 motion-reduce:!duration-0 motion-reduce:!animation-delay-0 motion-reduce:!animation-duration-0; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/styles/theme.ctx.svelte.ts: -------------------------------------------------------------------------------- 1 | import { defineCtx } from '$lib/utils/client'; 2 | import { ThemeController, type InitialTheme } from './themeController.svelte'; 3 | import { getSystemScheme } from './themeUtils'; 4 | 5 | const [getCtx, setCtx] = defineCtx(); 6 | 7 | const createThemeControllerCtx = (initialTheme: Omit) => { 8 | setCtx(new ThemeController({ systemScheme: getSystemScheme(), ...initialTheme })); 9 | }; 10 | 11 | /** 12 | * Initializes from storage and propagates changes to storage and the DOM. 13 | */ 14 | const useThemeControllerCtx = getCtx; 15 | 16 | export { createThemeControllerCtx, useThemeControllerCtx }; 17 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/superforms/client.ts: -------------------------------------------------------------------------------- 1 | export { zodClient } from 'sveltekit-superforms/adapters'; 2 | export { superForm } from 'sveltekit-superforms/client'; 3 | 4 | export * from './common'; 5 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/superforms/common.ts: -------------------------------------------------------------------------------- 1 | import type { SuperForm, Schema, SuperValidated as SV, Infer } from 'sveltekit-superforms/client'; 2 | 3 | export type { SuperForm, Schema }; 4 | export type SuperValidated = SV>; 5 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/superforms/server.ts: -------------------------------------------------------------------------------- 1 | export { zod } from 'sveltekit-superforms/adapters'; 2 | export { message, superValidate } from 'sveltekit-superforms/server'; 3 | 4 | export * from './common'; 5 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/svelte-actions/index.ts: -------------------------------------------------------------------------------- 1 | export * from './clickoutside'; 2 | export * from './keyboard'; 3 | export * from './trapFocus'; 4 | export * from './windowEscape'; 5 | export * from './minMaxVal'; 6 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/svelte-actions/windowEscape.ts: -------------------------------------------------------------------------------- 1 | export const windowEscape = (_node: HTMLElement, cb?: (e: KeyboardEvent) => void) => { 2 | if (!cb) return; 3 | 4 | const handleKeydown = (event: KeyboardEvent) => { 5 | if (event.key === 'Escape') cb?.(event); 6 | }; 7 | 8 | document.addEventListener('keydown', handleKeydown, true); 9 | 10 | return { 11 | destroy() { 12 | document.removeEventListener('keydown', handleKeydown, true); 13 | }, 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/svelte-runes/primitives.svelte.ts: -------------------------------------------------------------------------------- 1 | export class BoxedBool { 2 | #true = $state() as { true: boolean }; 3 | 4 | constructor(initial: boolean) { 5 | this.#true = { true: initial }; 6 | } 7 | 8 | get true() { 9 | return this.#true.true; 10 | } 11 | get false() { 12 | return !this.#true.true; 13 | } 14 | set true(value) { 15 | this.#true.true = value; 16 | } 17 | toggle() { 18 | this.true = !this.true; 19 | return this.true; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/svelte-stores/index.ts: -------------------------------------------------------------------------------- 1 | export { TempValue } from './tempValue.svelte'; 2 | export { AccordionSlider, type AccordionSliderProps, accordionSliderPropsDefaults } from './accordionSlider.svelte'; 3 | export { PausableTweened, type PausableTweenedProps, pausableTweenedPropsDefaults } from './pausableTweened.svelte'; 4 | export { ActiveIndex } from './activeIndex.svelte'; 5 | 6 | export * from './params'; 7 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/svelte-stores/params/index.ts: -------------------------------------------------------------------------------- 1 | export * from './params'; 2 | export * from './paramsGeneric'; 3 | export * from './searchParams'; 4 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/svelte-stores/tempValue.svelte.ts: -------------------------------------------------------------------------------- 1 | export class TempValue { 2 | #value: StoreVal | null = $state(null); 3 | beforeFinishCb?: (val: StoreVal) => BeforeFinishCBReturn; 4 | duration: number; 5 | #timeout: null | ReturnType = null; 6 | 7 | constructor(a: { duration: number; beforeFinishCb?: (val: StoreVal) => BeforeFinishCBReturn }) { 8 | this.duration = a.duration; 9 | this.beforeFinishCb = a.beforeFinishCb; 10 | } 11 | 12 | set value(value: StoreVal | null) { 13 | if (this.#timeout) clearInterval(this.#timeout); 14 | this.#value = value; 15 | if (value !== null) { 16 | this.#timeout = setTimeout(() => { 17 | this.beforeFinishCb?.(value); 18 | this.#value = null; 19 | }, this.duration); 20 | } 21 | } 22 | 23 | get value() { 24 | return this.#value; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/transport/server/consts.ts: -------------------------------------------------------------------------------- 1 | import { PUBLIC_INTERCEPT_TRANSPORTS } from '$env/static/public'; 2 | 3 | export const INTERCEPT_TRANSPORTS = PUBLIC_INTERCEPT_TRANSPORTS !== 'false'; 4 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/transport/server/email/console.ts: -------------------------------------------------------------------------------- 1 | import { EMAIL_DEFAULT_SENDER } from '$env/static/private'; 2 | import type { SendEmail } from './types'; 3 | 4 | export const consoleLogEmail: SendEmail = async ({ dynamicTemplateData, to, from }) => { 5 | // eslint-disable-next-line no-console 6 | console.info(` 7 | From: SampleKit <${from ?? EMAIL_DEFAULT_SENDER}> 8 | To: ${to} 9 | Subject: ${dynamicTemplateData.subject} 10 | Header: ${dynamicTemplateData.header} 11 | Body: ${dynamicTemplateData.body} 12 | Button Text: ${dynamicTemplateData.button_text} 13 | Button Link: ${dynamicTemplateData.href} 14 | Template ID: ${dynamicTemplateData.templateId} 15 | `); 16 | 17 | return { transportErr: false }; 18 | }; 19 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/transport/server/email/index.ts: -------------------------------------------------------------------------------- 1 | import { INTERCEPT_TRANSPORTS } from '../consts'; 2 | import { consoleLogEmail } from './console'; 3 | import { sendSESEmail } from './ses'; 4 | import type { SendEmail } from './types'; 5 | 6 | export const sendEmail: SendEmail = async (props) => { 7 | if (INTERCEPT_TRANSPORTS) return consoleLogEmail(props); 8 | return await sendSESEmail(props); 9 | }; 10 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/transport/server/email/types.ts: -------------------------------------------------------------------------------- 1 | export type Props = { 2 | from?: string; 3 | to: string; 4 | dynamicTemplateData: { 5 | subject: string; 6 | header: string; 7 | body: string; 8 | button_text: string; 9 | href: string; 10 | templateId: 'authEmails'; 11 | PUBLIC_ORIGIN?: string; 12 | }; 13 | }; 14 | 15 | export type SendEmail = ({ dynamicTemplateData, to, from }: Props) => Promise<{ 16 | transportErr: boolean; 17 | }>; 18 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/transport/server/index.ts: -------------------------------------------------------------------------------- 1 | export { transports } from './repository'; 2 | export { INTERCEPT_TRANSPORTS } from './consts'; 3 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/transport/server/repository/index.ts: -------------------------------------------------------------------------------- 1 | import { email } from './email'; 2 | import { sms } from './sms'; 3 | import type { Transports } from './types'; 4 | 5 | export const transports: Transports = { 6 | email, 7 | sms, 8 | }; 9 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/transport/server/repository/sms.ts: -------------------------------------------------------------------------------- 1 | import { lookupPhoneNumber, sendSMS } from '../sms'; 2 | import type { Transports } from './types'; 3 | 4 | export const sms: Transports['sms'] = { 5 | send: { 6 | Otp: async ({ phoneNumber, otp }: { phoneNumber: string; otp: string }) => 7 | sendSMS({ 8 | phoneNumber: phoneNumber, 9 | body: `Your SampleKit verification code is ${otp}. It will expire in 10 minutes.`, 10 | }), 11 | }, 12 | lookupPhoneNumber, 13 | }; 14 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/transport/server/sms/console.ts: -------------------------------------------------------------------------------- 1 | import type { SendSMS } from './types'; 2 | 3 | export const consoleLogSMS: SendSMS = async ({ phoneNumber, body }) => { 4 | // eslint-disable-next-line no-console 5 | console.info(` 6 | From: Console 7 | To: ${phoneNumber} 8 | Body: ${body} 9 | `); 10 | return { transportErr: false }; 11 | }; 12 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/transport/server/sms/index.ts: -------------------------------------------------------------------------------- 1 | import { INTERCEPT_TRANSPORTS } from '../consts'; 2 | import { consoleLogSMS } from './console'; 3 | import { sendTwilioSMS, lookupPhoneNumber } from './twilio'; 4 | import type { SendSMS } from './types'; 5 | 6 | export const sendSMS: SendSMS = async (props) => { 7 | if (INTERCEPT_TRANSPORTS) return consoleLogSMS(props); 8 | return await sendTwilioSMS(props); 9 | }; 10 | 11 | export { lookupPhoneNumber }; 12 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/transport/server/sms/types.ts: -------------------------------------------------------------------------------- 1 | export type Props = { phoneNumber: string; body: string }; 2 | 3 | export type SendSMS = ({ phoneNumber, body }: Props) => Promise<{ 4 | transportErr: boolean; 5 | }>; 6 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/utils/client/defineCtx.ts: -------------------------------------------------------------------------------- 1 | // Credit: https://www.reddit.com/r/sveltejs/comments/szc481/comment/i8hik7c/?utm_source=share&utm_medium=web2x&context=3 2 | 3 | import { setContext, getContext } from 'svelte'; 4 | 5 | function set(key: string | symbol, service: T): T { 6 | setContext(key, service); 7 | return service; 8 | } 9 | 10 | function get(key: string | symbol): () => T { 11 | return () => getContext(key) as T; 12 | } 13 | 14 | export function defineCtx(key: string | symbol = Symbol()): [() => T, (service: T) => T] { 15 | return [ 16 | get(key), 17 | (service: T) => { 18 | set(key, service); 19 | return service; 20 | }, 21 | ]; 22 | } 23 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/utils/client/index.ts: -------------------------------------------------------------------------------- 1 | export { defineCtx } from './defineCtx'; 2 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/utils/common/index.ts: -------------------------------------------------------------------------------- 1 | export * from './types'; 2 | export * from './language'; 3 | 4 | export const assertUnreachable = (e: never): never => { 5 | throw new Error(`Unreachable code reached: ${e}`); 6 | }; 7 | 8 | export function debounce(func: (...args: A) => R, timeout: number) { 9 | let timer: NodeJS.Timeout; 10 | 11 | return (...args: A) => { 12 | clearTimeout(timer); 13 | timer = setTimeout(() => func(...args), timeout); 14 | }; 15 | } 16 | 17 | type DateStyle = Intl.DateTimeFormatOptions['dateStyle']; 18 | 19 | export function formatDate(date: string | Date, dateStyle: DateStyle = 'medium', locales = 'en') { 20 | const formatter = new Intl.DateTimeFormat(locales, { dateStyle }); 21 | return formatter.format(new Date(date)); 22 | } 23 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/utils/common/language.ts: -------------------------------------------------------------------------------- 1 | export const cap = (word: T): Capitalize => 2 | ((word[0]?.toUpperCase() ?? '') + word.substring(1)) as Capitalize; 3 | 4 | export const pluralize = (word: string, count: number): string => (count === 1 ? word : `${word}s`); 5 | 6 | export const indefiniteArticle = (word: string): 'a' | 'an' => (word[0]?.match(/[aeiou]/i) ? 'an' : 'a'); 7 | 8 | export const toHumanReadableTime = (seconds: number) => { 9 | if (seconds < 60) return `${seconds} seconds`; 10 | if (seconds < 3600) return `${Math.floor(seconds / 60)} minutes`; 11 | return `${Math.floor(seconds / 3600)} hours`; 12 | }; 13 | 14 | export const kebabToTitleCase = (str: string) => 15 | str 16 | .split('-') 17 | .map((s) => s.charAt(0).toUpperCase() + s.slice(1)) 18 | .join(' '); 19 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/utils/common/types/index.ts: -------------------------------------------------------------------------------- 1 | import type { Component } from 'svelte'; 2 | 3 | export * from './result'; 4 | 5 | export type NoPropComponent = Component; 6 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/utils/common/types/result.ts: -------------------------------------------------------------------------------- 1 | export type Result = NonNullable | Result.Ok>; 2 | export namespace Result { 3 | export type Success = { message: 'Success' }; 4 | export type Ok = { data: T; error?: never }; 5 | export type Err = { data?: never; error: E }; 6 | } 7 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/lib/utils/server/index.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import { join } from 'path'; 3 | 4 | let rootPath = new URL(import.meta.url).pathname; 5 | 6 | const maxDepth = 20; 7 | 8 | for (let i = 0; i < maxDepth; i++) { 9 | if (fs.existsSync(join(rootPath, 'package.json'))) { 10 | break; 11 | } 12 | rootPath = join(rootPath, '..'); 13 | } 14 | 15 | /** 16 | * Joins from the website root directory. 17 | * Use to reference non-bundled code. 18 | */ 19 | export const joinRoot = (...path: string[]) => join(rootPath, ...path); 20 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/params/mfaKinds.ts: -------------------------------------------------------------------------------- 1 | import { mfaKinds } from '$lib/auth/common'; 2 | import type { ParamMatcher } from '@sveltejs/kit'; 3 | 4 | export const match: ParamMatcher = (param: string): param is (typeof mfaKinds)[number] => 5 | mfaKinds.includes(param as (typeof mfaKinds)[number]); 6 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/params/oauthProviders.ts: -------------------------------------------------------------------------------- 1 | import type { ParamMatcher } from '@sveltejs/kit'; 2 | 3 | export const match: ParamMatcher = (param: string): param is 'google' | 'github' => 4 | ['google', 'github'].includes(param); 5 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(api)/.well-known/security.txt/+server.ts: -------------------------------------------------------------------------------- 1 | import { PUBLIC_ORIGIN } from '$env/static/public'; 2 | import { SECURITY_EMAIL } from '$lib/consts'; 3 | import type { RequestHandler } from '@sveltejs/kit'; 4 | 5 | const security: RequestHandler = () => { 6 | const endOfYear = new Date(new Date().getFullYear(), 11, 31, 23, 59, 59, 999).toISOString(); 7 | const canonicalLink = `${PUBLIC_ORIGIN}/.well-known/security.txt`; 8 | const policyLink = `${PUBLIC_ORIGIN}/security-policy`; 9 | 10 | return new Response( 11 | `Contact: mailto:${SECURITY_EMAIL} 12 | Expires: ${endOfYear} 13 | Canonical: ${canonicalLink} 14 | Policy: ${policyLink}`, 15 | { 16 | headers: { 17 | 'Cache-Control': 'max-age=0, s-maxage=3600', 18 | 'Content-Type': 'text/plain', 19 | }, 20 | }, 21 | ); 22 | }; 23 | 24 | export const GET = security; 25 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(api)/robots.txt/+server.ts: -------------------------------------------------------------------------------- 1 | import { PUBLIC_ORIGIN } from '$env/static/public'; 2 | import type { RequestHandler } from '@sveltejs/kit'; 3 | 4 | const robots: RequestHandler = () => { 5 | return new Response( 6 | `User-agent: * 7 | Sitemap: ${PUBLIC_ORIGIN}/sitemap.xml`, 8 | { 9 | headers: { 10 | 'Cache-Control': 'public, max-age=86400', 11 | 'Content-Type': 'text/plain', 12 | }, 13 | }, 14 | ); 15 | }; 16 | 17 | export const GET = robots; 18 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/(login-signup)/+layout.ts: -------------------------------------------------------------------------------- 1 | export const load = () => { 2 | return { layout: { showFooter: false, showHeader: false } }; 3 | }; 4 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/(login-signup)/components/DotPattern.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/(login-signup)/components/GoogleFormButton.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 |
    (submitGoogle = true)}> 16 | 17 | 25 |
    26 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/(login-signup)/components/Or.svelte: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    or
    4 |
    5 |
    6 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/(login-signup)/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as DotPattern } from './DotPattern.svelte'; 2 | export { default as GoogleFormButton } from './GoogleFormButton.svelte'; 3 | export { default as Or } from './Or.svelte'; 4 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/(login-signup)/login/oauth-disabled/[[provider=oauthProviders]]/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { checkedRedirect } from '$lib/http/server'; 2 | import { cap } from '$lib/utils/common'; 3 | 4 | export const load = async ({ locals, params }) => { 5 | const seshUser = await locals.seshHandler.getSessionUser(); 6 | if (seshUser) return checkedRedirect('/account/profile'); 7 | 8 | return { provider: params.provider ? cap(params.provider) : null }; 9 | }; 10 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/(login-signup)/login/verify-mfa/passkey.json/client.ts: -------------------------------------------------------------------------------- 1 | import { ClientFetcher } from '$lib/http/client.svelte'; 2 | import type { RouteId } from './$types'; 3 | import type { LoginWithPasskeyRes, LoginWithPasskeyReq } from './common'; 4 | 5 | export const loginWithPasskey = new ClientFetcher( 6 | 'POST', 7 | '/(auth)/(login-signup)/login/verify-mfa/passkey.json', 8 | ); 9 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/(login-signup)/login/verify-mfa/passkey.json/common.ts: -------------------------------------------------------------------------------- 1 | import { authenticationResponseJSONSchema } from '$lib/auth/common'; 2 | import type { Result } from '$lib/utils/common'; 3 | import type { z } from 'zod'; 4 | 5 | export const loginWithPasskeyReqSchema = authenticationResponseJSONSchema; 6 | export type LoginWithPasskeyReq = z.infer; 7 | export type LoginWithPasskeyRes = Result.Success; 8 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/(login-signup)/oauth/google/consts.ts: -------------------------------------------------------------------------------- 1 | export const PUBLIC_GOOGLE_OAUTH_LOGIN_PATHNAME = 'oauth/google/callback.json'; 2 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/(login-signup)/signin/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { checkedRedirect } from '$lib/http/server'; 2 | 3 | export const load = async () => { 4 | return checkedRedirect('/login', 301); 5 | }; 6 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/(login-signup)/turnstile.ctx.svelte.ts: -------------------------------------------------------------------------------- 1 | import { Turnstile } from '$lib/bot-protection/turnstile/client'; 2 | import { defineCtx } from '$lib/utils/client'; 3 | 4 | const [getCtx, setCtx] = defineCtx(); 5 | export const createTurnstileCtx = () => setCtx(new Turnstile()); 6 | export const useTurnstileCtx = getCtx; 7 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/+layout.server.ts: -------------------------------------------------------------------------------- 1 | // This file is so SvelteKit will populate type LayoutRouteId 2 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/account/security/auth/+layout.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | {@render children()} 8 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/account/security/devices/+layout.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | {@render children()} 8 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/account/verify/dtos.common.ts: -------------------------------------------------------------------------------- 1 | import { authenticationResponseJSONSchema } from '@samplekit/auth/common'; 2 | import type { Result } from '$lib/utils/common'; 3 | import type { z } from 'zod'; 4 | 5 | export const seshConfFromPasskeyReqSchema = authenticationResponseJSONSchema; 6 | export type SeshConfFromPasskeyReq = z.infer; 7 | export type SeshConfFromPasskeyRes = Result.Success; 8 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/account/verify/email/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { sendSeshConfToken } from '../handlers.server'; 2 | 3 | export const actions = { sendSeshConfToken }; 4 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/account/verify/email/[token].json/+server.ts: -------------------------------------------------------------------------------- 1 | import { seshConfFromEmailVeri } from '../../handlers.server'; 2 | 3 | export const GET = seshConfFromEmailVeri; 4 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/account/verify/handlers.client.ts: -------------------------------------------------------------------------------- 1 | import { ClientFetcher } from '$lib/http/client.svelte'; 2 | import type { SeshConfFromPasskeyReq, SeshConfFromPasskeyRes } from './dtos.common'; 3 | import type { RouteId } from './passkey.json/$types'; 4 | 5 | export const seshConfFromPasskey = new ClientFetcher( 6 | 'POST', 7 | '/(auth)/account/verify/passkey.json', 8 | ); 9 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/account/verify/passkey.json/+server.ts: -------------------------------------------------------------------------------- 1 | import { seshConfFromPasskey } from '../handlers.server'; 2 | 3 | export const POST = seshConfFromPasskey; 4 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/change-to-google/consts.ts: -------------------------------------------------------------------------------- 1 | export type ChangeToGoogleError = 'auth-failed' | 'email-mismatch'; 2 | export const PUBLIC_GOOGLE_OAUTH_LINK_PATHNAME = 'change-to-google/callback.json'; 3 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/components/LoginAsDiffUserForm.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
    6 | 9 |
    10 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as LoginAsDiffUserForm } from './LoginAsDiffUserForm.svelte'; 2 | export { default as MFASelector } from './MFASelector.svelte'; 3 | export { default as PhoneInput } from './PhoneInput.svelte'; 4 | export { default as SendSMSTokenForm } from './SendSMSTokenForm.svelte'; 5 | export { default as Verifier, type VerifierProps } from './Verifier.svelte'; 6 | export { default as VerifyEmailForm } from './VerifyEmailForm.svelte'; 7 | export { default as VerifyMFAForm } from './VerifyMFAForm.svelte'; 8 | export { default as VerifyPWForm } from './VerifyPWForm.svelte'; 9 | export { default as VerifyCodeForm } from './VerifyCodeForm.svelte'; 10 | export { default as PassInput } from './PassInput.svelte'; 11 | export { default as Robot } from './Robot.svelte'; 12 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/index.ts: -------------------------------------------------------------------------------- 1 | export * from './schemas'; 2 | export * from './actionsMap'; 3 | export * from './components'; 4 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/invalid-token/+page.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
    6 |
    7 |
    8 |
    9 |

    Oops!

    10 |

    That verification code is no long valid.

    11 |
    12 |
    13 |
    14 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/mfa/passkey/get-auth-options.json/client.ts: -------------------------------------------------------------------------------- 1 | import { ClientFetcher } from '$lib/http/client.svelte'; 2 | import type { RouteId } from './$types'; 3 | import type { GetPasskeyAuthOptsRes } from './common'; 4 | 5 | export const getPasskeyAuthOpts = new ClientFetcher( 6 | 'GET', 7 | '/(auth)/mfa/passkey/get-auth-options.json', 8 | ); 9 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/mfa/passkey/get-auth-options.json/common.ts: -------------------------------------------------------------------------------- 1 | import type { PublicKeyCredentialRequestOptionsJSON } from '$lib/auth/common'; 2 | 3 | export type GetPasskeyAuthOptsRes = { opts: PublicKeyCredentialRequestOptionsJSON }; 4 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/mfa/update/+layout.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 |
    9 |

    {data.action} {mfaLabels[data.desiredMFA]} MFA

    10 | 11 | 12 | 13 | {@render children()} 14 | 15 |
    16 |
    17 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/mfa/update/register/+layout.ts: -------------------------------------------------------------------------------- 1 | export const load = async () => { 2 | const meta: App.PageData['meta'] = { title: 'Register MFA | SampleKit' }; 3 | 4 | return { meta }; 5 | }; 6 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/mfa/update/register/passkeys.json/client.ts: -------------------------------------------------------------------------------- 1 | import { ClientFetcher } from '$lib/http/client.svelte'; 2 | import type { RouteId } from './$types'; 3 | import type { 4 | RegisterMFA_Passkey_WithSeshConfAndPasskeyReq, 5 | RegisterMFA_Passkey_WithSeshConfAndPasskeyRes, 6 | } from './common'; 7 | 8 | export const registerMFA_Passkey_WithSeshConfAndPasskey = new ClientFetcher< 9 | RouteId, 10 | RegisterMFA_Passkey_WithSeshConfAndPasskeyRes, 11 | RegisterMFA_Passkey_WithSeshConfAndPasskeyReq 12 | >('POST', '/(auth)/mfa/update/register/passkeys.json'); 13 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/mfa/update/register/passkeys.json/common.ts: -------------------------------------------------------------------------------- 1 | import { registrationResponseJSONSchema } from '$lib/auth/common'; 2 | import type { Result } from '$lib/utils/common'; 3 | import type { z } from 'zod'; 4 | 5 | export const registerMFA_Passkey_WithSeshConfAndPasskeyReqSchema = registrationResponseJSONSchema; 6 | export type RegisterMFA_Passkey_WithSeshConfAndPasskeyReq = z.infer< 7 | typeof registerMFA_Passkey_WithSeshConfAndPasskeyReqSchema 8 | >; 9 | export type RegisterMFA_Passkey_WithSeshConfAndPasskeyRes = Result.Success; 10 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/mfa/update/register/passkeys/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { auth } from '$lib/auth/server'; 2 | 3 | export const load = async ({ locals }) => { 4 | const { user } = await locals.seshHandler.userOrRedirect(); 5 | const { email, givenName, id: userId } = user; 6 | 7 | const savedPasskeys = await auth.provider.pass.MFA.passkey.getSaved(user.id); 8 | const opts = await auth.provider.pass.MFA.passkey.createRegOpts({ email, savedPasskeys, givenName }); 9 | await auth.token.passkeyChallenge.createOrReplace({ userId, challenge: opts.challenge }); 10 | 11 | return { 12 | passkey: { opts }, 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/(auth)/mfa/update/remove/[mfaKind=mfaKinds]/+page.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 |
    9 |

    Step 2: Confirm {mfaLabels[data.desiredMFA]} MFA Deletion

    10 |
    11 | 12 |
    13 |
    14 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/+error.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 |
    8 |

    {$page.status}

    9 | {#if $page.error?.message} 10 |

    {$page.error.message}

    11 | {:else if $page.status === 500} 12 |

    Internal server error

    13 | {/if} 14 | 15 | {#if $page.status === 404} 16 |

    Perhaps these might interest you?

    17 | c.metadata.featured)} /> 18 | {/if} 19 |
    20 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/+layout.server.ts: -------------------------------------------------------------------------------- 1 | import { getThemeOnServer } from '$lib/styles/storeTheme'; 2 | 3 | export const load = async ({ locals, cookies }) => { 4 | return { 5 | user: await locals.seshHandler.getVerifiedUser(), 6 | layout: { showHeader: true, showFooter: false }, 7 | initialTheme: getThemeOnServer(cookies), 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/+layout.ts: -------------------------------------------------------------------------------- 1 | import { kebabToTitleCase } from '$lib/utils/common'; 2 | 3 | export const load = async ({ data, url }) => { 4 | let page = url.pathname.split('/').pop(); 5 | if (page) page = kebabToTitleCase(page); 6 | const siteTitle = url.pathname.startsWith('/shop') ? 'SampleKit Shop' : 'SampleKit'; 7 | const title = page ? `${page} | ${siteTitle}` : siteTitle; 8 | const meta: App.PageData['meta'] = { title }; 9 | 10 | return { ...data, meta }; 11 | }; 12 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/+page.ts: -------------------------------------------------------------------------------- 1 | import { allPostData, featureCards } from '$lib/articles/load/data/common'; 2 | 3 | export const load = async () => { 4 | return { allPostData, featureCards, layout: { showHeader: true, showFooter: true } }; 5 | }; 6 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/account/+layout.server.ts: -------------------------------------------------------------------------------- 1 | export const load = async ({ locals }) => { 2 | const { user } = await locals.seshHandler.userOrRedirect(); 3 | return { user }; 4 | }; 5 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/account/+layout.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | {@render children()} 8 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/account/hooks.server.ts: -------------------------------------------------------------------------------- 1 | import type { RouteId } from './$types'; 2 | import type { Handle } from '@sveltejs/kit'; 3 | 4 | export const handleAccountRedirects: Handle = async ({ event, resolve }) => { 5 | const layoutId: RouteId = '/account'; 6 | if (event.url.pathname.startsWith(layoutId)) await event.locals.seshHandler.userOrRedirect(); 7 | return await resolve(event); 8 | }; 9 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/account/profile/+page.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 |
    8 |

    Profile

    9 | 10 |
    11 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/account/profile/avatar/crop.json/client.ts: -------------------------------------------------------------------------------- 1 | import { ClientFetcher } from '$lib/http/client.svelte'; 2 | import type { RouteId } from './$types'; 3 | import type { UpdateAvatarCropRes, UpdateAvatarCropReq } from './common'; 4 | 5 | export const updateAvatarCrop = new ClientFetcher( 6 | 'PUT', 7 | '/account/profile/avatar/crop.json', 8 | ); 9 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/account/profile/avatar/crop.json/common.ts: -------------------------------------------------------------------------------- 1 | import { croppedImgSchema, type CroppedImg } from '$lib/image/common'; 2 | import type { z } from 'zod'; 3 | 4 | export type UpdateAvatarCropReq = Omit, 'url'>; 5 | export type UpdateAvatarCropRes = { savedImg: CroppedImg }; 6 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/account/profile/avatar/upload.json/client.ts: -------------------------------------------------------------------------------- 1 | import { ClientFetcher } from '$lib/http/client.svelte'; 2 | import type { RouteId } from './$types'; 3 | import type { 4 | GetSignedAvatarUploadUrlRes, 5 | CheckAndSaveUploadedAvatarRes, 6 | CheckAndSaveUploadedAvatarReq, 7 | DeleteAvatarRes, 8 | } from './common'; 9 | 10 | const routeId: RouteId = '/account/profile/avatar/upload.json'; 11 | 12 | export const getSignedAvatarUploadUrl = new ClientFetcher('GET', routeId); 13 | 14 | export const checkAndSaveUploadedAvatar = new ClientFetcher< 15 | RouteId, 16 | CheckAndSaveUploadedAvatarRes, 17 | CheckAndSaveUploadedAvatarReq 18 | >('PUT', routeId); 19 | 20 | export const deleteAvatar = new ClientFetcher('DELETE', routeId); 21 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/account/profile/avatar/upload.json/common.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | import { cropSchema, type CroppedImg } from '$lib/image/common'; 3 | import type { Result } from '$lib/utils/common'; 4 | 5 | export const MAX_UPLOAD_SIZE = 1024 * 1024 * 3; // 3MB 6 | 7 | export type GetSignedAvatarUploadUrlRes = { url: string; objectKey: string; formDataFields: Record }; 8 | 9 | export const checkAndSaveUploadedAvatarReqSchema = z.object({ crop: cropSchema }); 10 | export type CheckAndSaveUploadedAvatarReq = z.infer; 11 | export type CheckAndSaveUploadedAvatarRes = { savedImg: CroppedImg | null }; 12 | 13 | export type DeleteAvatarRes = Result.Success; 14 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/appearance/+layout.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | {#if $page.data['user']} 9 | {@render children()} 10 | {:else} 11 |
    12 | {@render children()} 13 |
    14 | {/if} 15 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/+layout.server.ts: -------------------------------------------------------------------------------- 1 | import { error } from '@sveltejs/kit'; 2 | import { allPostData } from '$lib/articles/load'; 3 | import { processedCodeMap, loadMainOnce } from '$lib/articles/load/demos/server'; 4 | import { type ServerFrontMatter } from '$lib/articles/schemas'; 5 | import type { LayoutRouteId } from './$types'; 6 | 7 | export const load = async ({ route }) => { 8 | const articlePath = route.id as Exclude; 9 | 10 | const frontMatter = allPostData.find((p) => p.articlePath === articlePath); 11 | if (!frontMatter) return error(404, `Article not found`); 12 | 13 | const article: ServerFrontMatter = frontMatter; 14 | const demos = processedCodeMap[articlePath]; 15 | if (demos) { 16 | await loadMainOnce(demos); 17 | article.demos = demos; 18 | } 19 | return { article }; 20 | }; 21 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/generic-url-state-controller/FeatureCard.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/generic-url-state-controller/assets/generic-url-state-controller-thumbnail-1200w.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/generic-url-state-controller/assets/generic-url-state-controller-thumbnail-1200w.webp -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/generic-url-state-controller/assets/generic-url-state-controller-thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/generic-url-state-controller/assets/generic-url-state-controller-thumbnail.png -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/image-uploader/FeatureCard.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/image-uploader/assets/2024-08-05_21-17-07_1410x1440_30fps.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/image-uploader/assets/2024-08-05_21-17-07_1410x1440_30fps.mp4 -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/image-uploader/assets/2024-08-05_21-17-07_465x474_24fps.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/image-uploader/assets/2024-08-05_21-17-07_465x474_24fps.mp4 -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/image-uploader/assets/image-uploader-flow-q30.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/image-uploader/assets/image-uploader-flow-q30.webp -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/image-uploader/assets/image-uploader-flow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/image-uploader/assets/image-uploader-flow.jpg -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/image-uploader/assets/image-uploader-thumbnail-1200w.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/image-uploader/assets/image-uploader-thumbnail-1200w.webp -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/image-uploader/assets/image-uploader-thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/image-uploader/assets/image-uploader-thumbnail.png -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/image-uploader/assets/owl-400w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/image-uploader/assets/owl-400w.jpg -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/image-uploader/assets/owl-400w.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/image-uploader/assets/owl-400w.webp -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/image-uploader/assets/owl.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/image-uploader/assets/owl.jpg -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/image-uploader/assets/smoky-400w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/image-uploader/assets/smoky-400w.jpg -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/image-uploader/assets/smoky-400w.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/image-uploader/assets/smoky-400w.webp -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/image-uploader/assets/smoky.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/image-uploader/assets/smoky.jpg -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/preprocessors/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { codeToDecoratedHtmlSync } from '@samplekit/preprocess-shiki'; 2 | import { opts } from '$lib/shiki'; 3 | 4 | const codeBlockCss = import('/src/lib/styles/css/code.css?raw') 5 | .then(({ default: rawCode }) => 6 | codeToDecoratedHtmlSync({ 7 | lang: 'css', 8 | code: rawCode, 9 | opts, 10 | transformName: 'block', 11 | }), 12 | ) 13 | .then((res) => { 14 | const data = res.data; 15 | if (!data) throw new Error('Failed to parse code block'); 16 | return data; 17 | }); 18 | 19 | export const load = async () => { 20 | return { codeBlockCss }; 21 | }; 22 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/preprocessors/FeatureCard.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/preprocessors/assets/2024-08-05_22-41-28_1334x941.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/preprocessors/assets/2024-08-05_22-41-28_1334x941.mp4 -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/preprocessors/assets/2024-08-05_22-41-28_1778x1254.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/preprocessors/assets/2024-08-05_22-41-28_1778x1254.mp4 -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/preprocessors/assets/2024-08-05_22-41-28_889x627.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/preprocessors/assets/2024-08-05_22-41-28_889x627.mp4 -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/preprocessors/assets/preprocessors-thumbnail-1200w.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/preprocessors/assets/preprocessors-thumbnail-1200w.webp -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/preprocessors/assets/preprocessors-thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/preprocessors/assets/preprocessors-thumbnail.png -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/preprocessors/demos/main/meta.preview.ts: -------------------------------------------------------------------------------- 1 | import type { ModuleDefinitions } from '$lib/articles/load'; 2 | 3 | export default [ 4 | { title: 'Code Decoration', loadComponent: () => import('./Code.svelte'), wrapperProps: { bg: true } }, 5 | { title: 'Markdown', loadComponent: () => import('./Markdown.svelte'), wrapperProps: { bg: true } }, 6 | { title: 'Math', loadComponent: () => import('./Math.svelte'), wrapperProps: { bg: true } }, 7 | { title: 'svelte.config.js', loadRaw: () => import('./exampleSvelteConfig.txt?raw') }, 8 | ] satisfies ModuleDefinitions; 9 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/simple-url-state-controller/FeatureCard.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/simple-url-state-controller/assets/simple-url-state-controller-q30.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/simple-url-state-controller/assets/simple-url-state-controller-q30.webp -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/simple-url-state-controller/assets/simple-url-state-controller-thumbnail-1200w.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/simple-url-state-controller/assets/simple-url-state-controller-thumbnail-1200w.webp -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/simple-url-state-controller/assets/simple-url-state-controller-thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/simple-url-state-controller/assets/simple-url-state-controller-thumbnail.png -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/simple-url-state-controller/assets/simple-url-state-controller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/simple-url-state-controller/assets/simple-url-state-controller.png -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/simple-url-state-controller/demos/main/meta.preview.ts: -------------------------------------------------------------------------------- 1 | import type { ModuleDefinitions } from '$lib/articles/load'; 2 | 3 | export default [ 4 | { title: 'Demo', loadComponent: () => import('./Demo.svelte'), icon: 'svelte' }, 5 | { title: 'Demo.svelte', loadRaw: () => import('./Demo.svelte?raw') }, 6 | { title: 'FilterInputs.svelte', loadRaw: () => import('./FilterInputs.svelte?raw') }, 7 | { title: 'Posts.svelte', loadRaw: () => import('./Posts.svelte?raw') }, 8 | { title: 'searchParams.ts', loadRaw: () => import('/src/lib/svelte-stores/params/searchParams.ts?raw') }, 9 | { title: 'demoData.ts', loadRaw: () => import('./demoData.ts?raw') }, 10 | ] satisfies ModuleDefinitions; 11 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/theme-controller/FeatureCard.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/theme-controller/assets/2024-08-05_19-56-30_1132x912.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/theme-controller/assets/2024-08-05_19-56-30_1132x912.mp4 -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/theme-controller/assets/2024-08-05_19-56-30_2264x1824.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/theme-controller/assets/2024-08-05_19-56-30_2264x1824.mp4 -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/theme-controller/assets/2024-08-05_19-56-30_800x645.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/theme-controller/assets/2024-08-05_19-56-30_800x645.mp4 -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/theme-controller/assets/theme-controller-q30.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/theme-controller/assets/theme-controller-q30.webp -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/theme-controller/assets/theme-controller-thumbnail-1200w.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/theme-controller/assets/theme-controller-thumbnail-1200w.webp -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/theme-controller/assets/theme-controller-thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/theme-controller/assets/theme-controller-thumbnail.png -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/theme-controller/assets/theme-controller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/theme-controller/assets/theme-controller.png -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/theme-controller/demos/main/meta.preview.ts: -------------------------------------------------------------------------------- 1 | import type { ModuleDefinitions } from '$lib/articles/load'; 2 | 3 | export default [ 4 | { 5 | title: 'Appearance', 6 | loadComponent: () => import('../../../../appearance/+page.svelte'), 7 | icon: 'svelte', 8 | }, 9 | { title: 'Appearance.svelte', loadRaw: () => import('/src/routes/appearance/+page.svelte?raw') }, 10 | { title: 'ThemePicker.svelte', loadRaw: () => import('/src/lib/styles/components/ThemePicker.svelte?raw') }, 11 | { 12 | title: 'themeController.svelte.ts', 13 | loadRaw: () => import('/src/lib/styles/themeController.svelte.ts?raw'), 14 | }, 15 | { title: 'storeTheme.ts', loadRaw: () => import('/src/lib/styles/storeTheme.ts?raw') }, 16 | { title: 'themeUtils.ts', loadRaw: () => import('/src/lib/styles/themeUtils.ts?raw') }, 17 | ] satisfies ModuleDefinitions; 18 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/typesafe-fetch-handler/FeatureCard.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/typesafe-fetch-handler/assets/typesafe-fetch-handler-q30.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/typesafe-fetch-handler/assets/typesafe-fetch-handler-q30.webp -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/typesafe-fetch-handler/assets/typesafe-fetch-handler-thumbnail-1200w.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/typesafe-fetch-handler/assets/typesafe-fetch-handler-thumbnail-1200w.webp -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/typesafe-fetch-handler/assets/typesafe-fetch-handler-thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/typesafe-fetch-handler/assets/typesafe-fetch-handler-thumbnail.png -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/typesafe-fetch-handler/assets/typesafe-fetch-handler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/articles/typesafe-fetch-handler/assets/typesafe-fetch-handler.png -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/typesafe-fetch-handler/demos/main/demo.json/client.ts: -------------------------------------------------------------------------------- 1 | import { ClientFetcher } from '$lib/http/client.svelte'; 2 | import type { RouteId } from './$types'; 3 | import type { GetRandomColorRes, GetRandomColorReq } from './common'; 4 | 5 | export const getRandomColor = new ClientFetcher( 6 | 'POST', 7 | '/articles/typesafe-fetch-handler/demos/main/demo.json', 8 | ); 9 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/typesafe-fetch-handler/demos/main/demo.json/common.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | import { langs } from '../lang.service.common'; 3 | 4 | export const getRandomColorReqSchema = z.object({ 5 | lang: z.enum(langs), 6 | excludeColor: z.string(), 7 | simulateDelay: z.boolean().optional(), 8 | }); 9 | export type GetRandomColorReq = z.infer; 10 | export type GetRandomColorRes = { color: string }; 11 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/typesafe-fetch-handler/demos/main/lang.service.common.ts: -------------------------------------------------------------------------------- 1 | export type Lang = 'EN' | 'DE' | 'KO'; 2 | export const langs = ['EN', 'DE', 'KO'] as const satisfies readonly Lang[]; 3 | 4 | export const langOptions = { 5 | language: [ 6 | { value: { lang: 'EN' }, label: 'English' }, 7 | { value: { lang: 'DE' }, label: 'Deutsch' }, 8 | { value: { lang: 'KO' }, label: '한국어' }, 9 | ], 10 | } as const satisfies { language: Array<{ value: { lang: Lang }; label: string }> }; 11 | 12 | export const defaultLang = langOptions.language[0]; 13 | 14 | export const isSupportedLang = (lang: unknown): lang is Lang => { 15 | if (typeof lang !== 'string') return false; 16 | return langs.includes(lang as Lang); 17 | }; 18 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/typesafe-fetch-handler/demos/main/lang.service.server.ts: -------------------------------------------------------------------------------- 1 | import { type Lang } from './lang.service.common'; 2 | 3 | export const EN = ['Yellow', 'Blue', 'Red', 'Green', 'Black', 'Brown', 'Pink', 'Purple', 'White']; 4 | export const KO = ['노란색 ', '파란색 ', '빨간색 ', '초록색 ', '검정색 ', '갈색 ', '분홍색 ', '보라색 ', '흰색 ']; 5 | export const DE = ['Gelb', 'Blau', 'Rot', 'Grün', 'Schwarz', 'Braun', 'Rosa', 'Lila', 'Weiß']; 6 | export const colors: Record = { DE, EN, KO }; 7 | 8 | export const getNewColor = ({ excludeColor, lang }: { lang: Lang; excludeColor: string }) => { 9 | const langColors = colors[lang]; 10 | 11 | let newColor = excludeColor; 12 | while (newColor === excludeColor) { 13 | newColor = langColors[Math.floor(Math.random() * langColors.length)]!; 14 | } 15 | 16 | return newColor; 17 | }; 18 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/update-loaded-front-matter.json/client.ts: -------------------------------------------------------------------------------- 1 | import { ClientFetcher } from '$lib/http/client.svelte'; 2 | import type { RouteId } from './$types'; 3 | import type { UpdateLoadedFrontMatterRes, UpdateLoadedFrontMatterReq } from './common'; 4 | 5 | export const updateLoadedFrontMatter = new ClientFetcher< 6 | RouteId, 7 | UpdateLoadedFrontMatterRes, 8 | UpdateLoadedFrontMatterReq 9 | >('PUT', '/articles/update-loaded-front-matter.json'); 10 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/articles/update-loaded-front-matter.json/common.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | import { articlePathSchema } from '$lib/articles/schemas'; 3 | import type { Result } from '$lib/utils/common'; 4 | 5 | export const updateLoadedFrontMatterReqSchema = z.object({ wordCount: z.number(), articlePath: articlePathSchema }); 6 | export type UpdateLoadedFrontMatterReq = z.infer; 7 | export type UpdateLoadedFrontMatterRes = Result.Success; 8 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/deployment-access/actionsMap.ts: -------------------------------------------------------------------------------- 1 | export type Action = '/deployment-access?/signin' | '/deployment-access?/signout' | '/deployment-access?/signoutAll'; 2 | 3 | export const actionsMap = { 4 | signin: '/deployment-access?/signin', 5 | signout: '/deployment-access?/signout', 6 | signoutAll: '/deployment-access?/signoutAll', 7 | } as const satisfies Record; 8 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/deployment-access/types.ts: -------------------------------------------------------------------------------- 1 | import type { Cookies } from '@sveltejs/kit'; 2 | 3 | export type DeploymentAccessRepository = { 4 | isAuthenticated: ( 5 | a: 6 | | { cookies: Cookies; headers?: undefined } 7 | | { cookies?: undefined; headers: Headers } 8 | | { cookies: Cookies; headers: Headers }, 9 | ) => Promise; 10 | createSession: (a: { accessToken: string; cookies: Cookies }) => Promise<{ success: boolean }>; 11 | deleteSession: (a: { cookies: Cookies }) => Promise; 12 | deleteAllSessions: (a: { cookies: Cookies }) => Promise; 13 | countAuthenticatedSessions: (a: { 14 | cookies: Cookies; 15 | }) => Promise<{ authenticated: false } | { authenticated: true; sessionCount: number }>; 16 | }; 17 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/home/assets/auth-1420w.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/home/assets/auth-1420w.jpeg -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/home/assets/auth-1420w.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/home/assets/auth-1420w.webp -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/home/assets/auth-850w.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/home/assets/auth-850w.jpeg -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/home/assets/auth-850w.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/home/assets/auth-850w.webp -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/home/assets/auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/home/assets/auth.png -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/home/assets/aws-1420w.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/home/assets/aws-1420w.jpeg -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/home/assets/aws-1420w.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/home/assets/aws-1420w.webp -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/home/assets/aws-850w.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/home/assets/aws-850w.jpeg -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/home/assets/aws-850w.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/home/assets/aws-850w.webp -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/home/assets/aws.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/home/assets/aws.png -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/home/assets/e-commerce-1420w.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/home/assets/e-commerce-1420w.jpeg -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/home/assets/e-commerce-1420w.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/home/assets/e-commerce-1420w.webp -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/home/assets/e-commerce-850w.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/home/assets/e-commerce-850w.jpeg -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/home/assets/e-commerce-850w.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/home/assets/e-commerce-850w.webp -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/home/assets/e-commerce.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/home/assets/e-commerce.png -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/home/assets/self-hosting-1420w.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/home/assets/self-hosting-1420w.jpeg -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/home/assets/self-hosting-1420w.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/home/assets/self-hosting-1420w.webp -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/home/assets/self-hosting-850w.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/home/assets/self-hosting-850w.jpeg -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/home/assets/self-hosting-850w.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/home/assets/self-hosting-850w.webp -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/home/assets/self-hosting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycohen/samplekit/8da4062b343ca7db71f350a176e6462f9dc91cea/sites/samplekit.dev/src/routes/home/assets/self-hosting.png -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/home/pp-demo/MockupPhone.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 |
    8 |
    9 | {@render children()} 10 |
    11 |
    12 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/shop/+error.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
    6 |

    {$page.status}

    7 | {#if $page.error?.message} 8 |

    {$page.error.message}

    9 | {:else if $page.status === 500} 10 |

    Internal server error

    11 | {/if} 12 | 13 | {#if $page.status === 404 && $page.data['homepageProducts']?.length} 14 |

    Perhaps these might interest you?

    15 | 16 |
    17 | {#each $page.data['homepageProducts'] as product} 18 | {product} 19 | {/each} 20 |
    21 | {/if} 22 |
    23 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/shop/+layout.ts: -------------------------------------------------------------------------------- 1 | import { PUBLIC_SHOPIFY_STORE_DOMAIN } from '$env/static/public'; 2 | import { shop, handleToPath, type Collection } from '$lib/shop'; 3 | 4 | export const load = async ({ fetch }) => { 5 | const menu: { title: string; path: string }[] = (await shop.menu.get({ handle: 'main-menu', fetch })).map((item) => ({ 6 | title: item.title, 7 | path: (item.url ?? '/shop').replace(PUBLIC_SHOPIFY_STORE_DOMAIN, '/shop'), 8 | })); 9 | 10 | const collections: Array = (await shop.collection.getAll({ fetch })).map((c) => ({ 11 | ...c, 12 | path: handleToPath({ handle: c.handle, kind: 'collection' }), 13 | })); 14 | 15 | return { menu, collections, layout: { showFooter: false, showHeader: false } }; 16 | }; 17 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/shop/+page.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 |
    14 | 15 |
    16 | 17 | 18 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/shop/+page.ts: -------------------------------------------------------------------------------- 1 | import { shop } from '$lib/shop'; 2 | 3 | export const load = async ({ fetch }) => { 4 | const [homepageProducts, marqueeProducts] = await Promise.all([ 5 | shop.product.getByCollection({ collectionHandle: 'frontpage', filters: {}, fetch }), 6 | shop.product.getAll({ filters: {}, fetch }), 7 | ]); 8 | 9 | return { homepageProducts, marqueeProducts }; 10 | }; 11 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/shop/cart.json/client.ts: -------------------------------------------------------------------------------- 1 | import { ClientFetcher } from '$lib/http/client.svelte'; 2 | import type { RouteId } from './$types'; 3 | import type { 4 | AddCartItemRes, 5 | AddCartItemReq, 6 | RemoveCartItemRes, 7 | RemoveCartItemReq, 8 | UpdateCartItemQtyRes, 9 | UpdateCartItemQtyReq, 10 | GetCartItemsRes, 11 | } from './common'; 12 | 13 | export const addCartItem = new ClientFetcher('POST', '/shop/cart.json'); 14 | export const removeCartItem = new ClientFetcher( 15 | 'DELETE', 16 | '/shop/cart.json', 17 | ); 18 | export const updateCartItemQty = new ClientFetcher( 19 | 'PUT', 20 | '/shop/cart.json', 21 | ); 22 | export const getCartItems = new ClientFetcher('GET', '/shop/cart.json'); 23 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/shop/components/cart/OpenCartBtn.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 24 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/shop/components/home/ThreeItemGrid.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | {#if firstProduct && secondProduct && thirdProduct} 15 |
    16 | 17 | 18 | 19 |
    20 | {/if} 21 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/shop/components/search-and-filter/Available.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 |
    11 | 12 | 13 |
    14 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/shop/components/search-and-filter/NoResults.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | {#if $query} 11 |

    No results for {$query}

    12 |

    13 | 14 |

    15 | {:else} 16 |

    17 | Nothing here! 18 | 19 |

    20 | {/if} 21 | -------------------------------------------------------------------------------- /sites/samplekit.dev/src/routes/shop/components/search-and-filter/SortBy.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |